All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/3] support of v4l2 encoder for STMicroelectronics SOC
@ 2016-07-11 15:14 Jean-Christophe Trotin
  2016-07-11 15:14 ` [PATCH v2 1/3] Documentation: DT: add bindings for STI HVA Jean-Christophe Trotin
                   ` (3 more replies)
  0 siblings, 4 replies; 18+ messages in thread
From: Jean-Christophe Trotin @ 2016-07-11 15:14 UTC (permalink / raw)
  To: linux-media, Hans Verkuil
  Cc: kernel, Benjamin Gaignard, Yannick Fertre, Hugues Fruchet,
	Jean-Christophe Trotin

version 2:
- List of pixel formats supported by the encoder reduced to NV12 and NV21
- x86_64 compilation warnings corrected
- V4L2 compliance successfully passed with this version (see report below)
- All remarks about version 1 of hva-v4l2.c taken into account:
        - V4L2 mem2mem framework used 
	- V4L2 control framework used
	- allocator context initialized in the probe and cleaned up in the remove
	- start_streaming and stop_streaming ops added
	- colorspace, bytesperline and sizeimage fields initialized in TRY_FMT
	- better estimation of sizeimage for compressed formats
	- checks and debugging logs already covered by vb2 removed
	- some dev_err changed in dev_dbg
	- typos corrected

version 1:
- Initial submission.

Only one feature supported and tested:
- encode (NV12, NV21) to H.264 video format

The driver is mainly implemented across three files:
- hva-v4l2.c
- hva-h264.c
- hva-hw.c
hva-v4l2.c manages the V4L2 interface with the userland.
It calls the HW services that are implemented in hva-hw.c.
hva-h264.c manages specific part of H.264 codec.

Below is the v4l2-compliance report for the version 2 of the sti hva driver:


root@sti-next:/home/video_test# v4l2-compliance -d /dev/video0
Driver Info:
	Driver name   : 8c85000.hva
	Card type     : 8c85000.hva
	Bus info      : platform:hva
	Driver version: 4.7.0
	Capabilities  : 0x84208000
		Video Memory-to-Memory
		Streaming
		Extended Pix Format
		Device Capabilities
	Device Caps   : 0x04208000
		Video Memory-to-Memory
		Streaming
		Extended Pix Format

Compliance test for device /dev/video0 (not using libv4l2):

Required ioctls:
	test VIDIOC_QUERYCAP: OK

Allow for multiple opens:
	test second video open: OK
	test VIDIOC_QUERYCAP: OK
	test VIDIOC_G/S_PRIORITY: 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
		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: 16 Private Controls: 0

	Format ioctls:
		test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
		test VIDIOC_G/S_PARM: OK
		test VIDIOC_G_FBUF: OK (Not Supported)
		test VIDIOC_G_FMT: OK
		warn: /local/home/frq08988/views/opensdk-2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(716): TRY_FMT cannot handle an invalid pixelformat.
		warn: /local/home/frq08988/views/opensdk-2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(717): This may or may not be a problem. For more information see:
		warn: /local/home/frq08988/views/opensdk-2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(718): http://www.mail-archive.com/linux-media@vger.kernel.org/msg56550.html
		warn: /local/home/frq08988/views/opensdk-2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(716): TRY_FMT cannot handle an invalid pixelformat.
		warn: /local/home/frq08988/views/opensdk-2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(717): This may or may not be a problem. For more information see:
		warn: /local/home/frq08988/views/opensdk-2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(718): http://www.mail-archive.com/linux-media@vger.kernel.org/msg56550.html
		test VIDIOC_TRY_FMT: OK
		warn: /local/home/frq08988/views/opensdk-2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(937): S_FMT cannot handle an invalid pixelformat.
		warn: /local/home/frq08988/views/opensdk-2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(938): This may or may not be a problem. For more information see:
		warn: /local/home/frq08988/views/opensdk-2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(939): http://www.mail-archive.com/linux-media@vger.kernel.org/msg56550.html
		warn: /local/home/frq08988/views/opensdk-2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(937): S_FMT cannot handle an invalid pixelformat.
		warn: /local/home/frq08988/views/opensdk-2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(938): This may or may not be a problem. For more information see:
		warn: /local/home/frq08988/views/opensdk-2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(939): http://www.mail-archive.com/linux-media@vger.kernel.org/msg56550.html
		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:
		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 input 0:


Total: 42, Succeeded: 42, Failed: 0, Warnings: 12

root@sti-next:/home/video_test# v4l2-compliance -f -d /dev/video0
Driver Info:
	Driver name   : 8c85000.hva
	Card type     : 8c85000.hva
	Bus info      : platform:hva
	Driver version: 4.7.0
	Capabilities  : 0x84208000
		Video Memory-to-Memory
		Streaming
		Extended Pix Format
		Device Capabilities
	Device Caps   : 0x04208000
		Video Memory-to-Memory
		Streaming
		Extended Pix Format

Compliance test for device /dev/video0 (not using libv4l2):

Required ioctls:
	test VIDIOC_QUERYCAP: OK

Allow for multiple opens:
	test second video open: OK
	test VIDIOC_QUERYCAP: OK
	test VIDIOC_G/S_PRIORITY: 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
		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: 16 Private Controls: 0

	Format ioctls:
		test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
		test VIDIOC_G/S_PARM: OK
		test VIDIOC_G_FBUF: OK (Not Supported)
		test VIDIOC_G_FMT: OK
		warn: /local/home/frq08988/views/opensdk-2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(716): TRY_FMT cannot handle an invalid pixelformat.
		warn: /local/home/frq08988/views/opensdk-2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(717): This may or may not be a problem. For more information see:
		warn: /local/home/frq08988/views/opensdk-2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(718): http://www.mail-archive.com/linux-media@vger.kernel.org/msg56550.html
		warn: /local/home/frq08988/views/opensdk-2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(716): TRY_FMT cannot handle an invalid pixelformat.
		warn: /local/home/frq08988/views/opensdk-2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(717): This may or may not be a problem. For more information see:
		warn: /local/home/frq08988/views/opensdk-2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(718): http://www.mail-archive.com/linux-media@vger.kernel.org/msg56550.html
		test VIDIOC_TRY_FMT: OK
		warn: /local/home/frq08988/views/opensdk-2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(937): S_FMT cannot handle an invalid pixelformat.
		warn: /local/home/frq08988/views/opensdk-2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(938): This may or may not be a problem. For more information see:
		warn: /local/home/frq08988/views/opensdk-2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(939): http://www.mail-archive.com/linux-media@vger.kernel.org/msg56550.html
		warn: /local/home/frq08988/views/opensdk-2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(937): S_FMT cannot handle an invalid pixelformat.
		warn: /local/home/frq08988/views/opensdk-2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(938): This may or may not be a problem. For more information see:
		warn: /local/home/frq08988/views/opensdk-2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(939): http://www.mail-archive.com/linux-media@vger.kernel.org/msg56550.html
		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:
		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 input 0:

Stream using all formats:
	Not supported for M2M devices

Total: 42, Succeeded: 42, Failed: 0, Warnings: 12

root@sti-next:/home/video_test# v4l2-compliance -a -d /dev/video0
Driver Info:
	Driver name   : 8c85000.hva
	Card type     : 8c85000.hva
	Bus info      : platform:hva
	Driver version: 4.7.0
	Capabilities  : 0x84208000
		Video Memory-to-Memory
		Streaming
		Extended Pix Format
		Device Capabilities
	Device Caps   : 0x04208000
		Video Memory-to-Memory
		Streaming
		Extended Pix Format

Compliance test for device /dev/video0 (not using libv4l2):

Required ioctls:
	test VIDIOC_QUERYCAP: OK

Allow for multiple opens:
	test second video open: OK
	test VIDIOC_QUERYCAP: OK
	test VIDIOC_G/S_PRIORITY: 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
		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: 16 Private Controls: 0

	Format ioctls:
		test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
		test VIDIOC_G/S_PARM: OK
		test VIDIOC_G_FBUF: OK (Not Supported)
		test VIDIOC_G_FMT: OK
		warn: /local/home/frq08988/views/opensdk-2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(716): TRY_FMT cannot handle an invalid pixelformat.
		warn: /local/home/frq08988/views/opensdk-2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(717): This may or may not be a problem. For more information see:
		warn: /local/home/frq08988/views/opensdk-2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(718): http://www.mail-archive.com/linux-media@vger.kernel.org/msg56550.html
		warn: /local/home/frq08988/views/opensdk-2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(716): TRY_FMT cannot handle an invalid pixelformat.
		warn: /local/home/frq08988/views/opensdk-2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(717): This may or may not be a problem. For more information see:
		warn: /local/home/frq08988/views/opensdk-2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(718): http://www.mail-archive.com/linux-media@vger.kernel.org/msg56550.html
		test VIDIOC_TRY_FMT: OK
		warn: /local/home/frq08988/views/opensdk-2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(937): S_FMT cannot handle an invalid pixelformat.
		warn: /local/home/frq08988/views/opensdk-2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(938): This may or may not be a problem. For more information see:
		warn: /local/home/frq08988/views/opensdk-2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(939): http://www.mail-archive.com/linux-media@vger.kernel.org/msg56550.html
		warn: /local/home/frq08988/views/opensdk-2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(937): S_FMT cannot handle an invalid pixelformat.
		warn: /local/home/frq08988/views/opensdk-2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(938): This may or may not be a problem. For more information see:
		warn: /local/home/frq08988/views/opensdk-2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(939): http://www.mail-archive.com/linux-media@vger.kernel.org/msg56550.html
		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:
		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 input 0:


Total: 42, Succeeded: 42, Failed: 0, Warnings: 12

root@sti-next:/home/video_test# v4l2-compliance -s -d /dev/video0
Driver Info:
	Driver name   : 8c85000.hva
	Card type     : 8c85000.hva
	Bus info      : platform:hva
	Driver version: 4.7.0
	Capabilities  : 0x84208000
		Video Memory-to-Memory
		Streaming
		Extended Pix Format
		Device Capabilities
	Device Caps   : 0x04208000
		Video Memory-to-Memory
		Streaming
		Extended Pix Format

Compliance test for device /dev/video0 (not using libv4l2):

Required ioctls:
	test VIDIOC_QUERYCAP: OK

Allow for multiple opens:
	test second video open: OK
	test VIDIOC_QUERYCAP: OK
	test VIDIOC_G/S_PRIORITY: 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
		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: 16 Private Controls: 0

	Format ioctls:
		test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
		test VIDIOC_G/S_PARM: OK
		test VIDIOC_G_FBUF: OK (Not Supported)
		test VIDIOC_G_FMT: OK
		warn: /local/home/frq08988/views/opensdk-2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(716): TRY_FMT cannot handle an invalid pixelformat.
		warn: /local/home/frq08988/views/opensdk-2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(717): This may or may not be a problem. For more information see:
		warn: /local/home/frq08988/views/opensdk-2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(718): http://www.mail-archive.com/linux-media@vger.kernel.org/msg56550.html
		warn: /local/home/frq08988/views/opensdk-2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(716): TRY_FMT cannot handle an invalid pixelformat.
		warn: /local/home/frq08988/views/opensdk-2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(717): This may or may not be a problem. For more information see:
		warn: /local/home/frq08988/views/opensdk-2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(718): http://www.mail-archive.com/linux-media@vger.kernel.org/msg56550.html
		test VIDIOC_TRY_FMT: OK
		warn: /local/home/frq08988/views/opensdk-2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(937): S_FMT cannot handle an invalid pixelformat.
		warn: /local/home/frq08988/views/opensdk-2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(938): This may or may not be a problem. For more information see:
		warn: /local/home/frq08988/views/opensdk-2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(939): http://www.mail-archive.com/linux-media@vger.kernel.org/msg56550.html
		warn: /local/home/frq08988/views/opensdk-2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(937): S_FMT cannot handle an invalid pixelformat.
		warn: /local/home/frq08988/views/opensdk-2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(938): This may or may not be a problem. For more information see:
		warn: /local/home/frq08988/views/opensdk-2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(939): http://www.mail-archive.com/linux-media@vger.kernel.org/msg56550.html
		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:
		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 input 0:

Streaming ioctls:
	test read/write: OK (Not Supported)
	test MMAP: OK                                     
	test USERPTR: OK (Not Supported)
	test DMABUF: Cannot test, specify --expbuf-device


Total: 45, Succeeded: 45, Failed: 0, Warnings: 12


Jean-Christophe Trotin (3):
  Documentation: DT: add bindings for STI HVA
  [media] hva: multi-format video encoder V4L2 driver
  [media] hva: add H.264 video encoding support

 .../devicetree/bindings/media/st,sti-hva.txt       |   24 +
 drivers/media/platform/Kconfig                     |   14 +
 drivers/media/platform/Makefile                    |    1 +
 drivers/media/platform/sti/hva/Makefile            |    2 +
 drivers/media/platform/sti/hva/hva-h264.c          | 1053 +++++++++++++++
 drivers/media/platform/sti/hva/hva-hw.c            |  534 ++++++++
 drivers/media/platform/sti/hva/hva-hw.h            |   42 +
 drivers/media/platform/sti/hva/hva-mem.c           |   60 +
 drivers/media/platform/sti/hva/hva-mem.h           |   36 +
 drivers/media/platform/sti/hva/hva-v4l2.c          | 1404 ++++++++++++++++++++
 drivers/media/platform/sti/hva/hva.h               |  389 ++++++
 11 files changed, 3559 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/st,sti-hva.txt
 create mode 100644 drivers/media/platform/sti/hva/Makefile
 create mode 100644 drivers/media/platform/sti/hva/hva-h264.c
 create mode 100644 drivers/media/platform/sti/hva/hva-hw.c
 create mode 100644 drivers/media/platform/sti/hva/hva-hw.h
 create mode 100644 drivers/media/platform/sti/hva/hva-mem.c
 create mode 100644 drivers/media/platform/sti/hva/hva-mem.h
 create mode 100644 drivers/media/platform/sti/hva/hva-v4l2.c
 create mode 100644 drivers/media/platform/sti/hva/hva.h

-- 
1.9.1


^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH v2 1/3] Documentation: DT: add bindings for STI HVA
  2016-07-11 15:14 [PATCH v2 0/3] support of v4l2 encoder for STMicroelectronics SOC Jean-Christophe Trotin
@ 2016-07-11 15:14 ` Jean-Christophe Trotin
  2016-07-11 15:14 ` [PATCH v2 2/3] [media] hva: multi-format video encoder V4L2 driver Jean-Christophe Trotin
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 18+ messages in thread
From: Jean-Christophe Trotin @ 2016-07-11 15:14 UTC (permalink / raw)
  To: linux-media, Hans Verkuil
  Cc: kernel, Benjamin Gaignard, Yannick Fertre, Hugues Fruchet,
	Jean-Christophe Trotin

This patch adds DT binding documentation for STMicroelectronics hva
driver.

Signed-off-by: Yannick Fertre <yannick.fertre@st.com>
Signed-off-by: Jean-Christophe Trotin <jean-christophe.trotin@st.com>
---
 .../devicetree/bindings/media/st,sti-hva.txt       | 24 ++++++++++++++++++++++
 1 file changed, 24 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/st,sti-hva.txt

diff --git a/Documentation/devicetree/bindings/media/st,sti-hva.txt b/Documentation/devicetree/bindings/media/st,sti-hva.txt
new file mode 100644
index 0000000..d1030bb
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/st,sti-hva.txt
@@ -0,0 +1,24 @@
+hva: multi-format video encoder for STMicroelectronics SoC.
+
+Required properties:
+- compatible: should be "st,sti-hva".
+- reg: HVA physical address location and length, esram address location and
+  length.
+- reg-names: names of the registers listed in registers property in the same
+  order.
+- interrupts: HVA interrupt number.
+- clocks: from common clock binding: handle hardware IP needed clocks, the
+  number of clocks may depend on the SoC type.
+  See ../clock/clock-bindings.txt for details.
+- clock-names: names of the clocks listed in clocks property in the same order.
+
+Example:
+	hva@8c85000{
+		compatible = "st,sti-hva";
+		reg = <0x8c85000 0x400>, <0x6000000 0x40000>;
+		reg-names = "hva_registers", "hva_esram";
+		interrupts = <GIC_SPI 58 IRQ_TYPE_NONE>,
+			     <GIC_SPI 59 IRQ_TYPE_NONE>;
+		clock-names = "clk_hva";
+		clocks = <&clk_s_c0_flexgen CLK_HVA>;
+	};
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v2 2/3] [media] hva: multi-format video encoder V4L2 driver
  2016-07-11 15:14 [PATCH v2 0/3] support of v4l2 encoder for STMicroelectronics SOC Jean-Christophe Trotin
  2016-07-11 15:14 ` [PATCH v2 1/3] Documentation: DT: add bindings for STI HVA Jean-Christophe Trotin
@ 2016-07-11 15:14 ` Jean-Christophe Trotin
  2016-07-11 18:00   ` Nicolas Dufresne
  2016-07-18 11:45   ` Hans Verkuil
  2016-07-11 15:14 ` [PATCH v2 3/3] [media] hva: add H.264 video encoding support Jean-Christophe Trotin
  2016-07-11 17:48 ` [PATCH v2 0/3] support of v4l2 encoder for STMicroelectronics SOC Nicolas Dufresne
  3 siblings, 2 replies; 18+ messages in thread
From: Jean-Christophe Trotin @ 2016-07-11 15:14 UTC (permalink / raw)
  To: linux-media, Hans Verkuil
  Cc: kernel, Benjamin Gaignard, Yannick Fertre, Hugues Fruchet,
	Jean-Christophe Trotin

This patch adds V4L2 HVA (Hardware Video Accelerator) video encoder
driver for STMicroelectronics SoC. It uses the V4L2 mem2mem framework.

This patch only contains the core parts of the driver:
- the V4L2 interface with the userland (hva-v4l2.c)
- the hardware services (hva-hw.c)
- the memory management utilities (hva-mem.c)

This patch doesn't include the support of specific codec (e.g. H.264)
video encoding: this support is part of subsequent patches.

Signed-off-by: Yannick Fertre <yannick.fertre@st.com>
Signed-off-by: Jean-Christophe Trotin <jean-christophe.trotin@st.com>
---
 drivers/media/platform/Kconfig            |   14 +
 drivers/media/platform/Makefile           |    1 +
 drivers/media/platform/sti/hva/Makefile   |    2 +
 drivers/media/platform/sti/hva/hva-hw.c   |  534 ++++++++++++
 drivers/media/platform/sti/hva/hva-hw.h   |   42 +
 drivers/media/platform/sti/hva/hva-mem.c  |   60 ++
 drivers/media/platform/sti/hva/hva-mem.h  |   36 +
 drivers/media/platform/sti/hva/hva-v4l2.c | 1299 +++++++++++++++++++++++++++++
 drivers/media/platform/sti/hva/hva.h      |  284 +++++++
 9 files changed, 2272 insertions(+)
 create mode 100644 drivers/media/platform/sti/hva/Makefile
 create mode 100644 drivers/media/platform/sti/hva/hva-hw.c
 create mode 100644 drivers/media/platform/sti/hva/hva-hw.h
 create mode 100644 drivers/media/platform/sti/hva/hva-mem.c
 create mode 100644 drivers/media/platform/sti/hva/hva-mem.h
 create mode 100644 drivers/media/platform/sti/hva/hva-v4l2.c
 create mode 100644 drivers/media/platform/sti/hva/hva.h

diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index 382f393..182d63f 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -227,6 +227,20 @@ config VIDEO_STI_BDISP
 	help
 	  This v4l2 mem2mem driver is a 2D blitter for STMicroelectronics SoC.
 
+config VIDEO_STI_HVA
+	tristate "STMicroelectronics STiH41x HVA multi-format video encoder V4L2 driver"
+	depends on VIDEO_DEV && VIDEO_V4L2
+	depends on ARCH_STI || COMPILE_TEST
+	select VIDEOBUF2_DMA_CONTIG
+	select V4L2_MEM2MEM_DEV
+	help
+	  This V4L2 driver enables HVA multi-format video encoder of
+	  STMicroelectronics SoC STiH41x series, allowing hardware encoding of raw
+	  uncompressed formats in various compressed video bitstreams format.
+
+	  To compile this driver as a module, choose M here:
+	  the module will be called hva.
+
 config VIDEO_SH_VEU
 	tristate "SuperH VEU mem2mem video processing driver"
 	depends on VIDEO_DEV && VIDEO_V4L2 && HAS_DMA
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index 99cf315..784dcd4 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -36,6 +36,7 @@ obj-$(CONFIG_VIDEO_SAMSUNG_S5P_G2D)	+= s5p-g2d/
 obj-$(CONFIG_VIDEO_SAMSUNG_EXYNOS_GSC)	+= exynos-gsc/
 
 obj-$(CONFIG_VIDEO_STI_BDISP)		+= sti/bdisp/
+obj-$(CONFIG_VIDEO_STI_HVA)		+= sti/hva/
 obj-$(CONFIG_DVB_C8SECTPFE)		+= sti/c8sectpfe/
 
 obj-$(CONFIG_BLACKFIN)                  += blackfin/
diff --git a/drivers/media/platform/sti/hva/Makefile b/drivers/media/platform/sti/hva/Makefile
new file mode 100644
index 0000000..7022a33
--- /dev/null
+++ b/drivers/media/platform/sti/hva/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_VIDEO_STI_HVA) := hva.o
+hva-y := hva-v4l2.o hva-hw.o hva-mem.o
diff --git a/drivers/media/platform/sti/hva/hva-hw.c b/drivers/media/platform/sti/hva/hva-hw.c
new file mode 100644
index 0000000..fa293c7
--- /dev/null
+++ b/drivers/media/platform/sti/hva/hva-hw.c
@@ -0,0 +1,534 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2015
+ * Authors: Yannick Fertre <yannick.fertre@st.com>
+ *          Hugues Fruchet <hugues.fruchet@st.com>
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+
+#include "hva.h"
+#include "hva-hw.h"
+
+/* HVA register offsets */
+#define HVA_HIF_REG_RST                 0x0100U
+#define HVA_HIF_REG_RST_ACK             0x0104U
+#define HVA_HIF_REG_MIF_CFG             0x0108U
+#define HVA_HIF_REG_HEC_MIF_CFG         0x010CU
+#define HVA_HIF_REG_CFL                 0x0110U
+#define HVA_HIF_FIFO_CMD                0x0114U
+#define HVA_HIF_FIFO_STS                0x0118U
+#define HVA_HIF_REG_SFL                 0x011CU
+#define HVA_HIF_REG_IT_ACK              0x0120U
+#define HVA_HIF_REG_ERR_IT_ACK          0x0124U
+#define HVA_HIF_REG_LMI_ERR             0x0128U
+#define HVA_HIF_REG_EMI_ERR             0x012CU
+#define HVA_HIF_REG_HEC_MIF_ERR         0x0130U
+#define HVA_HIF_REG_HEC_STS             0x0134U
+#define HVA_HIF_REG_HVC_STS             0x0138U
+#define HVA_HIF_REG_HJE_STS             0x013CU
+#define HVA_HIF_REG_CNT                 0x0140U
+#define HVA_HIF_REG_HEC_CHKSYN_DIS      0x0144U
+#define HVA_HIF_REG_CLK_GATING          0x0148U
+#define HVA_HIF_REG_VERSION             0x014CU
+#define HVA_HIF_REG_BSM                 0x0150U
+
+/* define value for version id register (HVA_HIF_REG_VERSION) */
+#define VERSION_ID_MASK	0x0000FFFF
+
+/* define values for BSM register (HVA_HIF_REG_BSM) */
+#define BSM_CFG_VAL1	0x0003F000
+#define BSM_CFG_VAL2	0x003F0000
+
+/* define values for memory interface register (HVA_HIF_REG_MIF_CFG) */
+#define MIF_CFG_VAL1	0x04460446
+#define MIF_CFG_VAL2	0x04460806
+#define MIF_CFG_VAL3	0x00000000
+
+/* define value for HEC memory interface register (HVA_HIF_REG_MIF_CFG) */
+#define HEC_MIF_CFG_VAL	0x000000C4
+
+/*  Bits definition for clock gating register (HVA_HIF_REG_CLK_GATING) */
+#define CLK_GATING_HVC	BIT(0)
+#define CLK_GATING_HEC	BIT(1)
+#define CLK_GATING_HJE	BIT(2)
+
+/* fix hva clock rate */
+#define CLK_RATE		300000000
+
+/* fix delay for pmruntime */
+#define AUTOSUSPEND_DELAY_MS	3
+
+/**
+ * hw encode error values
+ * NO_ERROR: Success, Task OK
+ * H264_BITSTREAM_OVERSIZE: VECH264 Bitstream size > bitstream buffer
+ * H264_FRAME_SKIPPED: VECH264 Frame skipped (refers to CPB Buffer Size)
+ * H264_SLICE_LIMIT_SIZE: VECH264 MB > slice limit size
+ * H264_MAX_SLICE_NUMBER: VECH264 max slice number reached
+ * H264_SLICE_READY: VECH264 Slice ready
+ * TASK_LIST_FULL: HVA/FPC task list full
+		   (discard latest transform command)
+ * UNKNOWN_COMMAND: Transform command not known by HVA/FPC
+ * WRONG_CODEC_OR_RESOLUTION: Wrong Codec or Resolution Selection
+ * NO_INT_COMPLETION: Time-out on interrupt completion
+ * LMI_ERR: Local Memory Interface Error
+ * EMI_ERR: External Memory Interface Error
+ * HECMI_ERR: HEC Memory Interface Error
+ */
+enum hva_hw_error {
+	NO_ERROR = 0x0,
+	H264_BITSTREAM_OVERSIZE = 0x2,
+	H264_FRAME_SKIPPED = 0x4,
+	H264_SLICE_LIMIT_SIZE = 0x5,
+	H264_MAX_SLICE_NUMBER = 0x7,
+	H264_SLICE_READY = 0x8,
+	TASK_LIST_FULL = 0xF0,
+	UNKNOWN_COMMAND = 0xF1,
+	WRONG_CODEC_OR_RESOLUTION = 0xF4,
+	NO_INT_COMPLETION = 0x100,
+	LMI_ERR = 0x101,
+	EMI_ERR = 0x102,
+	HECMI_ERR = 0x103,
+};
+
+static irqreturn_t hva_hw_its_interrupt(int irq, void *data)
+{
+	struct hva_dev *hva = data;
+
+	/* read status registers */
+	hva->sts_reg = readl_relaxed(hva->regs + HVA_HIF_FIFO_STS);
+	hva->sfl_reg = readl_relaxed(hva->regs + HVA_HIF_REG_SFL);
+
+	/* acknowledge interruption */
+	writel_relaxed(0x1, hva->regs + HVA_HIF_REG_IT_ACK);
+
+	return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t hva_hw_its_irq_thread(int irq, void *arg)
+{
+	struct hva_dev *hva = arg;
+	struct device *dev = hva_to_dev(hva);
+	u32 status = hva->sts_reg & 0xFF;
+	u8 ctx_id = 0;
+	struct hva_ctx *ctx = NULL;
+
+	dev_dbg(dev, "%s     %s: status: 0x%02x fifo level: 0x%02x\n",
+		HVA_PREFIX, __func__, hva->sts_reg & 0xFF, hva->sfl_reg & 0xF);
+
+	/*
+	 * status: task_id[31:16] client_id[15:8] status[7:0]
+	 * the context identifier is retrieved from the client identifier
+	 */
+	ctx_id = (hva->sts_reg & 0xFF00) >> 8;
+	if (ctx_id >= HVA_MAX_INSTANCES) {
+		dev_err(dev, "%s     %s: bad context identifier: %d\n",
+			ctx->name, __func__, ctx_id);
+		ctx->hw_err = true;
+		goto out;
+	}
+
+	ctx = hva->instances[ctx_id];
+
+	switch (status) {
+	case NO_ERROR:
+		dev_dbg(dev, "%s     %s: no error\n",
+			ctx->name, __func__);
+		ctx->hw_err = false;
+		break;
+	case H264_SLICE_READY:
+		dev_dbg(dev, "%s     %s: h264 slice ready\n",
+			ctx->name, __func__);
+		ctx->hw_err = false;
+		break;
+	case H264_FRAME_SKIPPED:
+		dev_dbg(dev, "%s     %s: h264 frame skipped\n",
+			ctx->name, __func__);
+		ctx->hw_err = false;
+		break;
+	case H264_BITSTREAM_OVERSIZE:
+		dev_err(dev, "%s     %s:h264 bitstream oversize\n",
+			ctx->name, __func__);
+		ctx->hw_err = true;
+		break;
+	case H264_SLICE_LIMIT_SIZE:
+		dev_err(dev, "%s     %s: h264 slice limit size is reached\n",
+			ctx->name, __func__);
+		ctx->hw_err = true;
+		break;
+	case H264_MAX_SLICE_NUMBER:
+		dev_err(dev, "%s     %s: h264 max slice number is reached\n",
+			ctx->name, __func__);
+		ctx->hw_err = true;
+		break;
+	case TASK_LIST_FULL:
+		dev_err(dev, "%s     %s:task list full\n",
+			ctx->name, __func__);
+		ctx->hw_err = true;
+		break;
+	case UNKNOWN_COMMAND:
+		dev_err(dev, "%s     %s: command not known\n",
+			ctx->name, __func__);
+		ctx->hw_err = true;
+		break;
+	case WRONG_CODEC_OR_RESOLUTION:
+		dev_err(dev, "%s     %s: wrong codec or resolution\n",
+			ctx->name, __func__);
+		ctx->hw_err = true;
+		break;
+	default:
+		dev_err(dev, "%s     %s: status not recognized\n",
+			ctx->name, __func__);
+		ctx->hw_err = true;
+		break;
+	}
+out:
+	complete(&hva->interrupt);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t hva_hw_err_interrupt(int irq, void *data)
+{
+	struct hva_dev *hva = data;
+
+	/* read status registers */
+	hva->sts_reg = readl_relaxed(hva->regs + HVA_HIF_FIFO_STS);
+	hva->sfl_reg = readl_relaxed(hva->regs + HVA_HIF_REG_SFL);
+
+	/* read error registers */
+	hva->lmi_err_reg = readl_relaxed(hva->regs + HVA_HIF_REG_LMI_ERR);
+	hva->emi_err_reg = readl_relaxed(hva->regs + HVA_HIF_REG_EMI_ERR);
+	hva->hec_mif_err_reg = readl_relaxed(hva->regs +
+					     HVA_HIF_REG_HEC_MIF_ERR);
+
+	/* acknowledge interruption */
+	writel_relaxed(0x1, hva->regs + HVA_HIF_REG_IT_ACK);
+
+	return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t hva_hw_err_irq_thread(int irq, void *arg)
+{
+	struct hva_dev *hva = arg;
+	struct device *dev = hva_to_dev(hva);
+	u8 ctx_id = 0;
+	struct hva_ctx *ctx;
+
+	dev_dbg(dev, "%s     status: 0x%02x fifo level: 0x%02x\n",
+		HVA_PREFIX, hva->sts_reg & 0xFF, hva->sfl_reg & 0xF);
+
+	/*
+	 * status: task_id[31:16] client_id[15:8] status[7:0]
+	 * the context identifier is retrieved from the client identifier
+	 */
+	ctx_id = (hva->sts_reg & 0xFF00) >> 8;
+	if (ctx_id >= HVA_MAX_INSTANCES) {
+		dev_err(dev, "%s     bad context identifier: %d\n", HVA_PREFIX,
+			ctx_id);
+		goto out;
+	}
+
+	ctx = hva->instances[ctx_id];
+
+	if (hva->lmi_err_reg) {
+		dev_err(dev, "%s     local memory interface error: 0x%08x\n",
+			ctx->name, hva->lmi_err_reg);
+		ctx->hw_err = true;
+	}
+
+	if (hva->lmi_err_reg) {
+		dev_err(dev, "%s     external memory interface error: 0x%08x\n",
+			ctx->name, hva->emi_err_reg);
+		ctx->hw_err = true;
+	}
+
+	if (hva->hec_mif_err_reg) {
+		dev_err(dev, "%s     hec memory interface error: 0x%08x\n",
+			ctx->name, hva->hec_mif_err_reg);
+		ctx->hw_err = true;
+	}
+out:
+	complete(&hva->interrupt);
+
+	return IRQ_HANDLED;
+}
+
+static unsigned long int hva_hw_get_ip_version(struct hva_dev *hva)
+{
+	struct device *dev = hva_to_dev(hva);
+	unsigned long int version;
+
+	if (pm_runtime_get_sync(dev) < 0) {
+		dev_err(dev, "%s     failed to get pm_runtime\n", HVA_PREFIX);
+		mutex_unlock(&hva->protect_mutex);
+		return -EFAULT;
+	}
+
+	version = readl_relaxed(hva->regs + HVA_HIF_REG_VERSION) &
+				VERSION_ID_MASK;
+
+	pm_runtime_put_autosuspend(dev);
+
+	switch (version) {
+	case HVA_VERSION_V400:
+		dev_dbg(dev, "%s     IP hardware version 0x%lx\n",
+			HVA_PREFIX, version);
+		break;
+	default:
+		dev_err(dev, "%s     unknown IP hardware version 0x%lx\n",
+			HVA_PREFIX, version);
+		version = HVA_VERSION_UNKNOWN;
+		break;
+	}
+
+	return version;
+}
+
+int hva_hw_probe(struct platform_device *pdev, struct hva_dev *hva)
+{
+	struct device *dev = &pdev->dev;
+	struct resource *regs;
+	struct resource *esram;
+	int ret;
+
+	WARN_ON(!hva);
+
+	/* get memory for registers */
+	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	hva->regs = devm_ioremap_resource(dev, regs);
+	if (IS_ERR_OR_NULL(hva->regs)) {
+		dev_err(dev, "%s     failed to get regs\n", HVA_PREFIX);
+		return PTR_ERR(hva->regs);
+	}
+
+	/* get memory for esram */
+	esram = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (IS_ERR_OR_NULL(esram)) {
+		dev_err(dev, "%s     failed to get esram\n", HVA_PREFIX);
+		return PTR_ERR(esram);
+	}
+	hva->esram_addr = esram->start;
+	hva->esram_size = esram->end - esram->start + 1;
+
+	dev_info(dev, "%s     esram reserved for address: 0x%x size:%d\n",
+		 HVA_PREFIX, hva->esram_addr, hva->esram_size);
+
+	/* get clock resource */
+	hva->clk = devm_clk_get(dev, "clk_hva");
+	if (IS_ERR(hva->clk)) {
+		dev_err(dev, "%s     failed to get clock\n", HVA_PREFIX);
+		return PTR_ERR(hva->clk);
+	}
+
+	ret = clk_prepare(hva->clk);
+	if (ret < 0) {
+		dev_err(dev, "%s     failed to prepare clock\n", HVA_PREFIX);
+		hva->clk = ERR_PTR(-EINVAL);
+		return ret;
+	}
+
+	/* get status interruption resource */
+	ret  = platform_get_irq(pdev, 0);
+	if (ret < 0) {
+		dev_err(dev, "%s     failed to get status IRQ\n", HVA_PREFIX);
+		goto err_clk;
+	}
+	hva->irq_its = ret;
+
+	ret = devm_request_threaded_irq(dev, hva->irq_its, hva_hw_its_interrupt,
+					hva_hw_its_irq_thread,
+					IRQF_ONESHOT,
+					"hva_its_irq", hva);
+	if (ret) {
+		dev_err(dev, "%s     failed to install status IRQ 0x%x\n",
+			HVA_PREFIX, hva->irq_its);
+		goto err_clk;
+	}
+	disable_irq(hva->irq_its);
+
+	/* get error interruption resource */
+	ret = platform_get_irq(pdev, 1);
+	if (ret < 0) {
+		dev_err(dev, "%s     failed to get error IRQ\n", HVA_PREFIX);
+		goto err_clk;
+	}
+	hva->irq_err = ret;
+
+	ret = devm_request_threaded_irq(dev, hva->irq_err, hva_hw_err_interrupt,
+					hva_hw_err_irq_thread,
+					IRQF_ONESHOT,
+					"hva_err_irq", hva);
+	if (ret) {
+		dev_err(dev, "%s     failed to install error IRQ 0x%x\n",
+			HVA_PREFIX, hva->irq_err);
+		goto err_clk;
+	}
+	disable_irq(hva->irq_err);
+
+	/* initialise protection mutex */
+	mutex_init(&hva->protect_mutex);
+
+	/* initialise completion signal */
+	init_completion(&hva->interrupt);
+
+	/* initialise runtime power management */
+	pm_runtime_set_autosuspend_delay(dev, AUTOSUSPEND_DELAY_MS);
+	pm_runtime_use_autosuspend(dev);
+	pm_runtime_set_suspended(dev);
+	pm_runtime_enable(dev);
+
+	ret = pm_runtime_get_sync(dev);
+	if (ret < 0) {
+		dev_err(dev, "%s     failed to set PM\n", HVA_PREFIX);
+		goto err_clk;
+	}
+
+	/* check IP hardware version */
+	hva->ip_version = hva_hw_get_ip_version(hva);
+
+	if (hva->ip_version == HVA_VERSION_UNKNOWN) {
+		ret = -EINVAL;
+		goto err_pm;
+	}
+
+	dev_info(dev, "%s     found hva device (version 0x%lx)\n", HVA_PREFIX,
+		 hva->ip_version);
+
+	return 0;
+
+err_pm:
+	pm_runtime_put(dev);
+err_clk:
+	if (hva->clk)
+		clk_unprepare(hva->clk);
+
+	return ret;
+}
+
+void hva_hw_remove(struct hva_dev *hva)
+{
+	struct device *dev = hva_to_dev(hva);
+
+	disable_irq(hva->irq_its);
+	disable_irq(hva->irq_err);
+
+	pm_runtime_put_autosuspend(dev);
+	pm_runtime_disable(dev);
+}
+
+int hva_hw_runtime_suspend(struct device *dev)
+{
+	struct hva_dev *hva = dev_get_drvdata(dev);
+
+	clk_disable_unprepare(hva->clk);
+
+	return 0;
+}
+
+int hva_hw_runtime_resume(struct device *dev)
+{
+	struct hva_dev *hva = dev_get_drvdata(dev);
+
+	if (clk_prepare_enable(hva->clk)) {
+		dev_err(hva->dev, "%s     failed to prepare hva clk\n",
+			HVA_PREFIX);
+		return -EINVAL;
+	}
+
+	if (clk_set_rate(hva->clk, CLK_RATE)) {
+		dev_err(dev, "%s     failed to set clock frequency\n",
+			HVA_PREFIX);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+int hva_hw_execute_task(struct hva_ctx *ctx, enum hva_hw_cmd_type cmd,
+			struct hva_buffer *task)
+{
+	struct hva_dev *hva = ctx_to_hdev(ctx);
+	struct device *dev = hva_to_dev(hva);
+	u8 client_id = ctx->id;
+	int ret;
+	u32 reg = 0;
+
+	mutex_lock(&hva->protect_mutex);
+
+	/* enable irqs */
+	enable_irq(hva->irq_its);
+	enable_irq(hva->irq_err);
+
+	if (pm_runtime_get_sync(dev) < 0) {
+		dev_err(dev, "%s     failed to get pm_runtime\n", ctx->name);
+		ret = -EFAULT;
+		goto out;
+	}
+
+	reg = readl_relaxed(hva->regs + HVA_HIF_REG_CLK_GATING);
+	switch (cmd) {
+	case H264_ENC:
+		reg |= CLK_GATING_HVC;
+		break;
+	default:
+		dev_dbg(dev, "%s     unknown command 0x%x\n", ctx->name, cmd);
+		ret = -EFAULT;
+		goto out;
+	}
+	writel_relaxed(reg, hva->regs + HVA_HIF_REG_CLK_GATING);
+
+	dev_dbg(dev, "%s     %s: write configuration registers\n", ctx->name,
+		__func__);
+
+	/* byte swap config */
+	writel_relaxed(BSM_CFG_VAL1, hva->regs + HVA_HIF_REG_BSM);
+
+	/* define Max Opcode Size and Max Message Size for LMI and EMI */
+	writel_relaxed(MIF_CFG_VAL3, hva->regs + HVA_HIF_REG_MIF_CFG);
+	writel_relaxed(HEC_MIF_CFG_VAL, hva->regs + HVA_HIF_REG_HEC_MIF_CFG);
+
+	/*
+	 * command FIFO: task_id[31:16] client_id[15:8] command_type[7:0]
+	 * the context identifier is provided as client identifier to the
+	 * hardware, and is retrieved in the interrupt functions from the
+	 * status register
+	 */
+	dev_dbg(dev, "%s     %s: send task (cmd: %d, task_desc: %pad)\n",
+		ctx->name, __func__, cmd + (client_id << 8), &task->paddr);
+	writel_relaxed(cmd + (client_id << 8), hva->regs + HVA_HIF_FIFO_CMD);
+	writel_relaxed(task->paddr, hva->regs + HVA_HIF_FIFO_CMD);
+
+	if (!wait_for_completion_timeout(&hva->interrupt,
+					 msecs_to_jiffies(2000))) {
+		dev_err(dev, "%s     %s: time out on completion\n", ctx->name,
+			__func__);
+		ret = -EFAULT;
+		goto out;
+	}
+
+	/* get encoding status */
+	ret = ctx->hw_err ? -EFAULT : 0;
+
+out:
+	disable_irq(hva->irq_its);
+	disable_irq(hva->irq_err);
+
+	switch (cmd) {
+	case H264_ENC:
+		reg &= ~CLK_GATING_HVC;
+		writel_relaxed(reg, hva->regs + HVA_HIF_REG_CLK_GATING);
+		break;
+	default:
+		dev_dbg(dev, "%s     unknown command 0x%x\n", ctx->name, cmd);
+	}
+
+	pm_runtime_put_autosuspend(dev);
+	mutex_unlock(&hva->protect_mutex);
+
+	return ret;
+}
diff --git a/drivers/media/platform/sti/hva/hva-hw.h b/drivers/media/platform/sti/hva/hva-hw.h
new file mode 100644
index 0000000..efb45b9
--- /dev/null
+++ b/drivers/media/platform/sti/hva/hva-hw.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2015
+ * Authors: Yannick Fertre <yannick.fertre@st.com>
+ *          Hugues Fruchet <hugues.fruchet@st.com>
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#ifndef HVA_HW_H
+#define HVA_HW_H
+
+#include "hva-mem.h"
+
+/* HVA Versions */
+#define HVA_VERSION_UNKNOWN    0x000
+#define HVA_VERSION_V400       0x400
+
+/* HVA command types */
+enum hva_hw_cmd_type {
+	/* RESERVED = 0x00 */
+	/* RESERVED = 0x01 */
+	H264_ENC = 0x02,
+	/* RESERVED = 0x03 */
+	/* RESERVED = 0x04 */
+	/* RESERVED = 0x05 */
+	/* RESERVED = 0x06 */
+	/* RESERVED = 0x07 */
+	REMOVE_CLIENT = 0x08,
+	FREEZE_CLIENT = 0x09,
+	START_CLIENT = 0x0A,
+	FREEZE_ALL = 0x0B,
+	START_ALL = 0x0C,
+	REMOVE_ALL = 0x0D
+};
+
+int hva_hw_probe(struct platform_device *pdev, struct hva_dev *hva);
+void hva_hw_remove(struct hva_dev *hva);
+int hva_hw_runtime_suspend(struct device *dev);
+int hva_hw_runtime_resume(struct device *dev);
+int hva_hw_execute_task(struct hva_ctx *ctx, enum hva_hw_cmd_type cmd,
+			struct hva_buffer *task);
+
+#endif /* HVA_HW_H */
diff --git a/drivers/media/platform/sti/hva/hva-mem.c b/drivers/media/platform/sti/hva/hva-mem.c
new file mode 100644
index 0000000..759c873
--- /dev/null
+++ b/drivers/media/platform/sti/hva/hva-mem.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2015
+ * Authors: Yannick Fertre <yannick.fertre@st.com>
+ *          Hugues Fruchet <hugues.fruchet@st.com>
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#include "hva.h"
+#include "hva-mem.h"
+
+int hva_mem_alloc(struct hva_ctx *ctx, u32 size, const char *name,
+		  struct hva_buffer **buf)
+{
+	struct device *dev = ctx_to_dev(ctx);
+	struct hva_buffer *b;
+	dma_addr_t paddr;
+	void *base;
+	DEFINE_DMA_ATTRS(attrs);
+
+	b = devm_kzalloc(dev, sizeof(*b), GFP_KERNEL);
+	if (!b)
+		return -ENOMEM;
+
+	dma_set_attr(DMA_ATTR_WRITE_COMBINE, &attrs);
+	base = dma_alloc_attrs(dev, size, &paddr, GFP_KERNEL | GFP_DMA, &attrs);
+	if (!base) {
+		dev_err(dev, "%s %s : dma_alloc_attrs failed for %s (size=%d)\n",
+			ctx->name, __func__, name, size);
+		devm_kfree(dev, b);
+		return -ENOMEM;
+	}
+
+	b->size = size;
+	b->paddr = paddr;
+	b->vaddr = base;
+	b->attrs = attrs;
+	b->name = name;
+
+	dev_dbg(dev,
+		"%s allocate %d bytes of HW memory @(virt=%p, phy=%pad): %s\n",
+		ctx->name, size, b->vaddr, &b->paddr, b->name);
+
+	/* return  hva buffer to user */
+	*buf = b;
+
+	return 0;
+}
+
+void hva_mem_free(struct hva_ctx *ctx, struct hva_buffer *buf)
+{
+	struct device *dev = ctx_to_dev(ctx);
+
+	dev_dbg(dev,
+		"%s free %d bytes of HW memory @(virt=%p, phy=%pad): %s\n",
+		ctx->name, buf->size, buf->vaddr, &buf->paddr, buf->name);
+
+	dma_free_attrs(dev, buf->size, buf->vaddr, buf->paddr, &buf->attrs);
+
+	devm_kfree(dev, buf);
+}
diff --git a/drivers/media/platform/sti/hva/hva-mem.h b/drivers/media/platform/sti/hva/hva-mem.h
new file mode 100644
index 0000000..e8a3f7e
--- /dev/null
+++ b/drivers/media/platform/sti/hva/hva-mem.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2015
+ * Authors: Yannick Fertre <yannick.fertre@st.com>
+ *          Hugues Fruchet <hugues.fruchet@st.com>
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#ifndef HVA_MEM_H
+#define HVA_MEM_H
+
+/**
+ * struct hva_buffer - hva buffer
+ *
+ * @name:  name of requester
+ * @attrs: dma attributes
+ * @paddr: physical address (for hardware)
+ * @vaddr: virtual address (kernel can read/write)
+ * @size:  size of buffer
+ */
+struct hva_buffer {
+	const char		*name;
+	struct dma_attrs	attrs;
+	dma_addr_t		paddr;
+	void			*vaddr;
+	u32			size;
+};
+
+int hva_mem_alloc(struct hva_ctx *ctx,
+		  __u32 size,
+		  const char *name,
+		  struct hva_buffer **buf);
+
+void hva_mem_free(struct hva_ctx *ctx,
+		  struct hva_buffer *buf);
+
+#endif /* HVA_MEM_H */
diff --git a/drivers/media/platform/sti/hva/hva-v4l2.c b/drivers/media/platform/sti/hva/hva-v4l2.c
new file mode 100644
index 0000000..bacc9ff
--- /dev/null
+++ b/drivers/media/platform/sti/hva/hva-v4l2.c
@@ -0,0 +1,1299 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2015
+ * Authors: Yannick Fertre <yannick.fertre@st.com>
+ *          Hugues Fruchet <hugues.fruchet@st.com>
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "hva.h"
+#include "hva-hw.h"
+
+#define HVA_NAME "hva"
+
+#define MIN_FRAMES	1
+#define MIN_STREAMS	1
+
+#define HVA_MIN_WIDTH	32
+#define HVA_MAX_WIDTH	1920
+#define HVA_MIN_HEIGHT	32
+#define HVA_MAX_HEIGHT	1920
+
+/* HVA requires a 16x16 pixels alignment for frames */
+#define HVA_WIDTH_ALIGNMENT	16
+#define HVA_HEIGHT_ALIGNMENT	16
+
+#define DEFAULT_WIDTH		HVA_MIN_WIDTH
+#define	DEFAULT_HEIGHT		HVA_MIN_HEIGHT
+#define DEFAULT_FRAME_NUM	1
+#define DEFAULT_FRAME_DEN	30
+
+#define to_type_str(type) (type == V4L2_BUF_TYPE_VIDEO_OUTPUT ? \
+			   "frame" : "stream")
+
+#define fh_to_ctx(f)    (container_of(f, struct hva_ctx, fh))
+
+/* registry of available encoders */
+const struct hva_enc *hva_encoders[] = {
+};
+
+static inline int frame_size(u32 w, u32 h, u32 fmt)
+{
+	switch (fmt) {
+	case V4L2_PIX_FMT_NV12:
+	case V4L2_PIX_FMT_NV21:
+		return (w * h * 3) / 2;
+	default:
+		return 0;
+	}
+}
+
+static inline int frame_stride(u32 w, u32 fmt)
+{
+	switch (fmt) {
+	case V4L2_PIX_FMT_NV12:
+	case V4L2_PIX_FMT_NV21:
+		return w;
+	default:
+		return 0;
+	}
+}
+
+static inline int frame_alignment(u32 fmt)
+{
+	switch (fmt) {
+	case V4L2_PIX_FMT_NV12:
+	case V4L2_PIX_FMT_NV21:
+		/* multiple of 2 */
+		return 2;
+	default:
+		return 1;
+	}
+}
+
+static inline int estimated_stream_size(u32 w, u32 h)
+{
+	/*
+	 * HVA only encodes in YUV420 format, whatever the frame format.
+	 * A compression ratio of 2 is assumed: thus, the maximum size
+	 * of a stream is estimated to ((width x height x 3 / 2) / 2)
+	 */
+	return (w * h * 3) / 4;
+}
+
+static void set_default_params(struct hva_ctx *ctx)
+{
+	struct hva_frameinfo *frameinfo = &ctx->frameinfo;
+	struct hva_streaminfo *streaminfo = &ctx->streaminfo;
+
+	frameinfo->pixelformat = V4L2_PIX_FMT_NV12;
+	frameinfo->width = DEFAULT_WIDTH;
+	frameinfo->height = DEFAULT_HEIGHT;
+	frameinfo->aligned_width = DEFAULT_WIDTH;
+	frameinfo->aligned_height = DEFAULT_HEIGHT;
+	frameinfo->size = frame_size(frameinfo->aligned_width,
+				     frameinfo->aligned_height,
+				     frameinfo->pixelformat);
+
+	streaminfo->streamformat = V4L2_PIX_FMT_H264;
+	streaminfo->width = DEFAULT_WIDTH;
+	streaminfo->height = DEFAULT_HEIGHT;
+
+	ctx->max_stream_size = estimated_stream_size(streaminfo->width,
+						     streaminfo->height);
+}
+
+static const struct hva_enc *hva_find_encoder(struct hva_ctx *ctx,
+					      u32 pixelformat,
+					      u32 streamformat)
+{
+	struct hva_dev *hva = ctx_to_hdev(ctx);
+	const struct hva_enc *enc;
+	unsigned int i;
+
+	for (i = 0; i < hva->nb_of_encoders; i++) {
+		enc = hva->encoders[i];
+		if ((enc->pixelformat == pixelformat) &&
+		    (enc->streamformat == streamformat))
+			return enc;
+	}
+
+	return NULL;
+}
+
+static void register_format(u32 format, u32 formats[], u32 *nb_of_formats)
+{
+	u32 i;
+	bool found = false;
+
+	for (i = 0; i < *nb_of_formats; i++) {
+		if (format == formats[i]) {
+			found = true;
+			break;
+		}
+	}
+
+	if (!found)
+		formats[(*nb_of_formats)++] = format;
+}
+
+static void register_formats(struct hva_dev *hva)
+{
+	unsigned int i;
+
+	for (i = 0; i < hva->nb_of_encoders; i++) {
+		register_format(hva->encoders[i]->pixelformat,
+				hva->pixelformats,
+				&hva->nb_of_pixelformats);
+
+		register_format(hva->encoders[i]->streamformat,
+				hva->streamformats,
+				&hva->nb_of_streamformats);
+	}
+}
+
+static void register_encoders(struct hva_dev *hva)
+{
+	struct device *dev = hva_to_dev(hva);
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(hva_encoders); i++) {
+		if (hva->nb_of_encoders >= HVA_MAX_ENCODERS) {
+			dev_dbg(dev,
+				"%s failed to register encoder (%d maximum reached)\n",
+				hva_encoders[i]->name, HVA_MAX_ENCODERS);
+			return;
+		}
+
+		hva->encoders[hva->nb_of_encoders++] = hva_encoders[i];
+		dev_info(dev, "%s encoder registered\n", hva_encoders[i]->name);
+	}
+}
+
+static int hva_open_encoder(struct hva_ctx *ctx, u32 streamformat,
+			    u32 pixelformat, struct hva_enc **penc)
+{
+	struct hva_dev *hva = ctx_to_hdev(ctx);
+	struct device *dev = ctx_to_dev(ctx);
+	struct hva_enc *enc;
+	unsigned int i;
+	int ret;
+	bool found = false;
+
+	/* find an encoder which can deal with these formats */
+	for (i = 0; i < hva->nb_of_encoders; i++) {
+		enc = (struct hva_enc *)hva->encoders[i];
+		if ((enc->streamformat == streamformat) &&
+		    (enc->pixelformat == pixelformat)) {
+			found = true;
+			break;
+		}
+	}
+
+	if (!found) {
+		dev_err(dev, "%s no encoder found matching %4.4s => %4.4s\n",
+			ctx->name, (char *)&pixelformat, (char *)&streamformat);
+		return -EINVAL;
+	}
+
+	dev_dbg(dev, "%s one encoder matching %4.4s => %4.4s\n",
+		ctx->name, (char *)&pixelformat, (char *)&streamformat);
+
+	/* update instance name */
+	snprintf(ctx->name, sizeof(ctx->name), "[%3d:%4.4s]",
+		 hva->instance_id, (char *)&streamformat);
+
+	/* open encoder instance */
+	ret = enc->open(ctx);
+	if (ret) {
+		dev_err(dev, "%s failed to open encoder instance (%d)\n",
+			ctx->name, ret);
+		return ret;
+	}
+
+	dev_dbg(dev, "%s %s encoder opened\n", ctx->name, enc->name);
+
+	*penc = enc;
+
+	return ret;
+}
+
+/*
+ * V4L2 ioctl operations
+ */
+
+static int hva_querycap(struct file *file, void *priv,
+			struct v4l2_capability *cap)
+{
+	struct hva_ctx *ctx = fh_to_ctx(file->private_data);
+	struct hva_dev *hva = ctx_to_hdev(ctx);
+
+	strlcpy(cap->driver, hva->pdev->name, sizeof(cap->driver));
+	strlcpy(cap->card, hva->pdev->name, sizeof(cap->card));
+	snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
+		 HVA_NAME);
+
+	cap->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M;
+	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+
+	return 0;
+}
+
+static int hva_enum_fmt_stream(struct file *file, void *priv,
+			       struct v4l2_fmtdesc *f)
+{
+	struct hva_ctx *ctx = fh_to_ctx(file->private_data);
+	struct hva_dev *hva = ctx_to_hdev(ctx);
+
+	if (unlikely(f->index >= hva->nb_of_streamformats))
+		return -EINVAL;
+
+	f->pixelformat = hva->streamformats[f->index];
+	snprintf(f->description, sizeof(f->description), "%4.4s",
+		 (char *)&f->pixelformat);
+	f->flags = V4L2_FMT_FLAG_COMPRESSED;
+
+	return 0;
+}
+
+static int hva_enum_fmt_frame(struct file *file, void *priv,
+			      struct v4l2_fmtdesc *f)
+{
+	struct hva_ctx *ctx = fh_to_ctx(file->private_data);
+	struct hva_dev *hva = ctx_to_hdev(ctx);
+
+	if (unlikely(f->index >= hva->nb_of_pixelformats))
+		return -EINVAL;
+
+	f->pixelformat = hva->pixelformats[f->index];
+	snprintf(f->description, sizeof(f->description), "%4.4s",
+		 (char *)&f->pixelformat);
+
+	return 0;
+}
+
+static int hva_g_fmt_stream(struct file *file, void *fh, struct v4l2_format *f)
+{
+	struct hva_ctx *ctx = fh_to_ctx(file->private_data);
+	struct device *dev = ctx_to_dev(ctx);
+	struct hva_streaminfo *streaminfo = &ctx->streaminfo;
+
+	f->fmt.pix.width = streaminfo->width;
+	f->fmt.pix.height = streaminfo->height;
+	f->fmt.pix.field = V4L2_FIELD_NONE;
+	f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+	f->fmt.pix.pixelformat = streaminfo->streamformat;
+	f->fmt.pix.bytesperline = 0;
+	f->fmt.pix.sizeimage = ctx->max_stream_size;
+
+	dev_dbg(dev, "%s V4L2 G_FMT (CAPTURE): %dx%d fmt:%.4s size:%d\n",
+		ctx->name, f->fmt.pix.width, f->fmt.pix.height,
+		(u8 *)&f->fmt.pix.pixelformat, f->fmt.pix.sizeimage);
+	return 0;
+}
+
+static int hva_g_fmt_frame(struct file *file, void *fh, struct v4l2_format *f)
+{
+	struct hva_ctx *ctx = fh_to_ctx(file->private_data);
+	struct device *dev = ctx_to_dev(ctx);
+	struct hva_frameinfo *frameinfo = &ctx->frameinfo;
+
+	f->fmt.pix.width = frameinfo->width;
+	f->fmt.pix.height = frameinfo->height;
+	f->fmt.pix.field = V4L2_FIELD_NONE;
+	f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+	f->fmt.pix.pixelformat = frameinfo->pixelformat;
+	f->fmt.pix.bytesperline = frame_stride(frameinfo->aligned_width,
+					       frameinfo->pixelformat);
+	f->fmt.pix.sizeimage = frameinfo->size;
+
+	dev_dbg(dev, "%s V4L2 G_FMT (OUTPUT): %dx%d fmt:%.4s size:%d\n",
+		ctx->name, f->fmt.pix.width, f->fmt.pix.height,
+		(u8 *)&f->fmt.pix.pixelformat, f->fmt.pix.sizeimage);
+
+	return 0;
+}
+
+static int hva_try_fmt_stream(struct file *file, void *priv,
+			      struct v4l2_format *f)
+{
+	struct hva_ctx *ctx = fh_to_ctx(file->private_data);
+	struct device *dev = ctx_to_dev(ctx);
+	struct v4l2_pix_format *pix = &f->fmt.pix;
+	u32 streamformat = pix->pixelformat;
+	const struct hva_enc *enc;
+	u32 width, height;
+	u32 stream_size;
+
+	enc = hva_find_encoder(ctx, ctx->frameinfo.pixelformat, streamformat);
+	if (!enc) {
+		dev_dbg(dev,
+			"%s V4L2 TRY_FMT (CAPTURE): unsupported format %.4s\n",
+			ctx->name, (char *)&pix->pixelformat);
+		return -EINVAL;
+	}
+
+	width = pix->width;
+	height = pix->height;
+	if (ctx->flags & HVA_FLAG_FRAMEINFO) {
+		/*
+		 * if the frame resolution is already fixed, only allow the
+		 * same stream resolution
+		 */
+		pix->width = ctx->frameinfo.width;
+		pix->height = ctx->frameinfo.height;
+		if ((pix->width != width) || (pix->height != height))
+			dev_dbg(dev,
+				"%s V4L2 TRY_FMT (CAPTURE): resolution updated %dx%d -> %dx%d to fit frame resolution\n",
+				ctx->name, width, height,
+				pix->width, pix->height);
+	} else {
+		/* adjust width & height */
+		v4l_bound_align_image(&pix->width,
+				      HVA_MIN_WIDTH, enc->max_width,
+				      0,
+				      &pix->height,
+				      HVA_MIN_HEIGHT, enc->max_height,
+				      0,
+				      0);
+
+		if ((pix->width != width) || (pix->height != height))
+			dev_dbg(dev,
+				"%s V4L2 TRY_FMT (CAPTURE): resolution updated %dx%d -> %dx%d to fit min/max/alignment\n",
+				ctx->name, width, height,
+				pix->width, pix->height);
+	}
+
+	stream_size = estimated_stream_size(pix->width, pix->height);
+	if (pix->sizeimage < stream_size)
+		pix->sizeimage = stream_size;
+
+	pix->bytesperline = 0;
+	pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
+	pix->field = V4L2_FIELD_NONE;
+
+	return 0;
+}
+
+static int hva_try_fmt_frame(struct file *file, void *priv,
+			     struct v4l2_format *f)
+{
+	struct hva_ctx *ctx = fh_to_ctx(file->private_data);
+	struct device *dev = ctx_to_dev(ctx);
+	struct v4l2_pix_format *pix = &f->fmt.pix;
+	u32 pixelformat = pix->pixelformat;
+	const struct hva_enc *enc;
+	u32 width, height;
+
+	enc = hva_find_encoder(ctx, pixelformat, ctx->streaminfo.streamformat);
+	if (!enc) {
+		dev_dbg(dev,
+			"%s V4L2 TRY_FMT (OUTPUT): unsupported format %.4s\n",
+			ctx->name, (char *)&pixelformat);
+		return -EINVAL;
+	}
+
+	/* adjust width & height */
+	width = pix->width;
+	height = pix->height;
+	v4l_bound_align_image(&pix->width,
+			      HVA_MIN_WIDTH, HVA_MAX_WIDTH,
+			      frame_alignment(pixelformat) - 1,
+			      &pix->height,
+			      HVA_MIN_HEIGHT, HVA_MAX_HEIGHT,
+			      frame_alignment(pixelformat) - 1,
+			      0);
+
+	if ((pix->width != width) || (pix->height != height))
+		dev_dbg(dev,
+			"%s V4L2 TRY_FMT (OUTPUT): resolution updated %dx%d -> %dx%d to fit min/max/alignment\n",
+			ctx->name, width, height, pix->width, pix->height);
+
+	width = ALIGN(pix->width, HVA_WIDTH_ALIGNMENT);
+	height = ALIGN(pix->height, HVA_HEIGHT_ALIGNMENT);
+
+	pix->bytesperline = frame_stride(width, pixelformat);
+	pix->sizeimage = frame_size(width, height, pixelformat);
+	pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
+	pix->field = V4L2_FIELD_NONE;
+
+	return 0;
+}
+
+static int hva_s_fmt_stream(struct file *file, void *fh, struct v4l2_format *f)
+{
+	struct hva_ctx *ctx = fh_to_ctx(file->private_data);
+	struct device *dev = ctx_to_dev(ctx);
+	struct vb2_queue *vq;
+	int ret;
+
+	dev_dbg(dev, "%s V4L2 S_FMT (CAPTURE): %dx%d fmt:%.4s size:%d\n",
+		ctx->name, f->fmt.pix.width, f->fmt.pix.height,
+		(u8 *)&f->fmt.pix.pixelformat, f->fmt.pix.sizeimage);
+
+	ret = hva_try_fmt_stream(file, fh, f);
+	if (ret) {
+		dev_dbg(dev, "%s V4L2 S_FMT (CAPTURE): unsupported format %.4s\n",
+			ctx->name, (char *)&f->fmt.pix.pixelformat);
+		return ret;
+	}
+
+	vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
+	if (vb2_is_streaming(vq)) {
+		dev_dbg(dev, "%s V4L2 S_FMT (CAPTURE): queue busy\n",
+			ctx->name);
+		return -EBUSY;
+	}
+
+	ctx->max_stream_size = f->fmt.pix.sizeimage;
+	ctx->streaminfo.width = f->fmt.pix.width;
+	ctx->streaminfo.height = f->fmt.pix.height;
+	ctx->streaminfo.streamformat = f->fmt.pix.pixelformat;
+	ctx->flags |= HVA_FLAG_STREAMINFO;
+
+	return 0;
+}
+
+static int hva_s_fmt_frame(struct file *file, void *fh, struct v4l2_format *f)
+{
+	struct hva_ctx *ctx = fh_to_ctx(file->private_data);
+	struct device *dev = ctx_to_dev(ctx);
+	struct v4l2_pix_format *pix = &f->fmt.pix;
+	struct vb2_queue *vq;
+	int ret;
+
+	dev_dbg(dev, "%s V4L2 S_FMT (OUTPUT): %dx%d fmt %.4s size %d\n",
+		ctx->name, f->fmt.pix.width, f->fmt.pix.height,
+		(u8 *)&f->fmt.pix.pixelformat, f->fmt.pix.sizeimage);
+
+	ret = hva_try_fmt_frame(file, fh, f);
+	if (ret) {
+		dev_dbg(dev, "%s V4L2 S_FMT (OUTPUT): unsupported format %.4s\n",
+			ctx->name, (char *)&f->fmt.pix.pixelformat);
+		return ret;
+	}
+
+	vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
+	if (vb2_is_streaming(vq)) {
+		dev_dbg(dev, "%s V4L2 S_FMT (OUTPUT): queue busy\n", ctx->name);
+		return -EBUSY;
+	}
+
+	ctx->frameinfo.aligned_width = ALIGN(pix->width, HVA_WIDTH_ALIGNMENT);
+	ctx->frameinfo.aligned_height = ALIGN(pix->height,
+					      HVA_HEIGHT_ALIGNMENT);
+
+	ctx->frameinfo.size = pix->sizeimage;
+	ctx->frameinfo.pixelformat = pix->pixelformat;
+	ctx->frameinfo.width = pix->width;
+	ctx->frameinfo.height = pix->height;
+	ctx->flags |= HVA_FLAG_FRAMEINFO;
+
+	return 0;
+}
+
+static int hva_s_parm(struct file *file, void *fh, struct v4l2_streamparm *sp)
+{
+	struct hva_ctx *ctx = fh_to_ctx(file->private_data);
+	struct device *dev = ctx_to_dev(ctx);
+	struct v4l2_fract *time_per_frame = &ctx->ctrls.time_per_frame;
+
+	time_per_frame->numerator = sp->parm.capture.timeperframe.numerator;
+	time_per_frame->denominator =
+		sp->parm.capture.timeperframe.denominator;
+
+	dev_dbg(dev, "%s set parameters %d/%d\n", ctx->name,
+		time_per_frame->numerator, time_per_frame->denominator);
+
+	return 0;
+}
+
+static int hva_g_parm(struct file *file, void *fh, struct v4l2_streamparm *sp)
+{
+	struct hva_ctx *ctx = fh_to_ctx(file->private_data);
+	struct device *dev = ctx_to_dev(ctx);
+	struct v4l2_fract *time_per_frame = &ctx->ctrls.time_per_frame;
+
+	sp->parm.capture.timeperframe.numerator = time_per_frame->numerator;
+	sp->parm.capture.timeperframe.denominator =
+		time_per_frame->denominator;
+
+	dev_dbg(dev, "%s get parameters %d/%d\n", ctx->name,
+		time_per_frame->numerator, time_per_frame->denominator);
+
+	return 0;
+}
+
+static int hva_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+	struct hva_ctx *ctx = fh_to_ctx(file->private_data);
+	struct device *dev = ctx_to_dev(ctx);
+
+	if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+		/*
+		 * depending on the targeted compressed video format, the
+		 * capture buffer might contain headers (e.g. H.264 SPS/PPS)
+		 * filled in by the driver client; the size of these data is
+		 * copied from the bytesused field of the V4L2 buffer in the
+		 * payload field of the hva stream buffer
+		 */
+		struct vb2_queue *vq;
+		struct hva_stream *stream;
+
+		vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, buf->type);
+
+		if (buf->index >= vq->num_buffers) {
+			dev_dbg(dev, "%s buffer index %d out of range (%d)\n",
+				ctx->name, buf->index, vq->num_buffers);
+			return -EINVAL;
+		}
+
+		stream = (struct hva_stream *)vq->bufs[buf->index];
+		stream->bytesused = buf->bytesused;
+	}
+
+	return v4l2_m2m_qbuf(file, ctx->fh.m2m_ctx, buf);
+}
+
+/* V4L2 ioctl ops */
+static const struct v4l2_ioctl_ops hva_ioctl_ops = {
+	.vidioc_querycap		= hva_querycap,
+	.vidioc_enum_fmt_vid_cap	= hva_enum_fmt_stream,
+	.vidioc_enum_fmt_vid_out	= hva_enum_fmt_frame,
+	.vidioc_g_fmt_vid_cap		= hva_g_fmt_stream,
+	.vidioc_g_fmt_vid_out		= hva_g_fmt_frame,
+	.vidioc_try_fmt_vid_cap		= hva_try_fmt_stream,
+	.vidioc_try_fmt_vid_out		= hva_try_fmt_frame,
+	.vidioc_s_fmt_vid_cap		= hva_s_fmt_stream,
+	.vidioc_s_fmt_vid_out		= hva_s_fmt_frame,
+	.vidioc_g_parm			= hva_g_parm,
+	.vidioc_s_parm			= hva_s_parm,
+	.vidioc_reqbufs			= v4l2_m2m_ioctl_reqbufs,
+	.vidioc_create_bufs             = v4l2_m2m_ioctl_create_bufs,
+	.vidioc_querybuf		= v4l2_m2m_ioctl_querybuf,
+	.vidioc_expbuf			= v4l2_m2m_ioctl_expbuf,
+	.vidioc_qbuf			= hva_qbuf,
+	.vidioc_dqbuf			= v4l2_m2m_ioctl_dqbuf,
+	.vidioc_streamon		= v4l2_m2m_ioctl_streamon,
+	.vidioc_streamoff		= v4l2_m2m_ioctl_streamoff,
+	.vidioc_subscribe_event		= v4l2_ctrl_subscribe_event,
+	.vidioc_unsubscribe_event	= v4l2_event_unsubscribe,
+};
+
+/*
+ * V4L2 control operations
+ */
+
+static int hva_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct hva_ctx *ctx = container_of(ctrl->handler, struct hva_ctx,
+					   ctrl_handler);
+	struct device *dev = ctx_to_dev(ctx);
+
+	dev_dbg(dev, "%s S_CTRL: id = %d, val = %d\n", ctx->name,
+		ctrl->id, ctrl->val);
+
+	switch (ctrl->id) {
+	case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+		ctx->ctrls.bitrate_mode = ctrl->val;
+		break;
+	case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
+		ctx->ctrls.gop_size = ctrl->val;
+		break;
+	case V4L2_CID_MPEG_VIDEO_BITRATE:
+		ctx->ctrls.bitrate = ctrl->val;
+		break;
+	case V4L2_CID_MPEG_VIDEO_ASPECT:
+		ctx->ctrls.aspect = ctrl->val;
+		break;
+	default:
+		dev_dbg(dev, "%s S_CTRL: invalid control (id = %d)\n",
+			ctx->name, ctrl->id);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/* V4L2 control ops */
+static const struct v4l2_ctrl_ops hva_ctrl_ops = {
+	.s_ctrl = hva_s_ctrl,
+};
+
+static int hva_ctrls_setup(struct hva_ctx *ctx)
+{
+	struct device *dev = ctx_to_dev(ctx);
+	u64 mask;
+
+	v4l2_ctrl_handler_init(&ctx->ctrl_handler, 4);
+
+	v4l2_ctrl_new_std_menu(&ctx->ctrl_handler, &hva_ctrl_ops,
+			       V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
+			       V4L2_MPEG_VIDEO_BITRATE_MODE_CBR,
+			       0,
+			       V4L2_MPEG_VIDEO_BITRATE_MODE_CBR);
+
+	v4l2_ctrl_new_std(&ctx->ctrl_handler, &hva_ctrl_ops,
+			  V4L2_CID_MPEG_VIDEO_GOP_SIZE,
+			  1, 60, 1, 16);
+
+	v4l2_ctrl_new_std(&ctx->ctrl_handler, &hva_ctrl_ops,
+			  V4L2_CID_MPEG_VIDEO_BITRATE,
+			  1, 50000, 1, 20000);
+
+	mask = ~(1 << V4L2_MPEG_VIDEO_ASPECT_1x1);
+	v4l2_ctrl_new_std_menu(&ctx->ctrl_handler, &hva_ctrl_ops,
+			       V4L2_CID_MPEG_VIDEO_ASPECT,
+			       V4L2_MPEG_VIDEO_ASPECT_1x1,
+			       mask,
+			       V4L2_MPEG_VIDEO_ASPECT_1x1);
+
+	if (ctx->ctrl_handler.error) {
+		int err = ctx->ctrl_handler.error;
+
+		dev_dbg(dev, "%s controls setup failed (%d)\n",
+			ctx->name, err);
+		v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+		return err;
+	}
+
+	v4l2_ctrl_handler_setup(&ctx->ctrl_handler);
+
+	/* set default time per frame */
+	ctx->ctrls.time_per_frame.numerator = DEFAULT_FRAME_NUM;
+	ctx->ctrls.time_per_frame.denominator = DEFAULT_FRAME_DEN;
+
+	return 0;
+}
+
+/*
+ * mem-to-mem operations
+ */
+
+static void hva_run_work(struct work_struct *work)
+{
+	struct hva_ctx *ctx = container_of(work, struct hva_ctx, run_work);
+	struct vb2_v4l2_buffer *src_buf, *dst_buf;
+	const struct hva_enc *enc = ctx->enc;
+	struct hva_frame *frame;
+	struct hva_stream *stream;
+	int ret;
+
+	/* protect instance against reentrancy */
+	mutex_lock(&ctx->lock);
+
+	src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
+	dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
+
+	frame = to_hva_frame(src_buf);
+	stream = to_hva_stream(dst_buf);
+	frame->vbuf.sequence = ctx->frame_num++;
+
+	ret = enc->encode(ctx, frame, stream);
+
+	if (ret) {
+		v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR);
+		v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR);
+	} else {
+		/* propagate frame timestamp */
+		dst_buf->vb2_buf.timestamp = src_buf->vb2_buf.timestamp;
+		dst_buf->field = V4L2_FIELD_NONE;
+		dst_buf->sequence = ctx->stream_num - 1;
+
+		v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE);
+		v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE);
+	}
+
+	mutex_unlock(&ctx->lock);
+
+	v4l2_m2m_job_finish(ctx->hva_dev->m2m_dev, ctx->fh.m2m_ctx);
+}
+
+static void hva_device_run(void *priv)
+{
+	struct hva_ctx *ctx = priv;
+	struct hva_dev *hva = ctx_to_hdev(ctx);
+
+	queue_work(hva->work_queue, &ctx->run_work);
+}
+
+static void hva_job_abort(void *priv)
+{
+	struct hva_ctx *ctx = priv;
+	struct device *dev = ctx_to_dev(ctx);
+
+	dev_dbg(dev, "%s aborting job\n", ctx->name);
+
+	ctx->aborting = true;
+}
+
+static int hva_job_ready(void *priv)
+{
+	struct hva_ctx *ctx = priv;
+	struct device *dev = ctx_to_dev(ctx);
+
+	if (!v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx)) {
+		dev_dbg(dev, "%s job not ready: no frame buffers\n",
+			ctx->name);
+		return 0;
+	}
+
+	if (!v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx)) {
+		dev_dbg(dev, "%s job not ready: no stream buffers\n",
+			ctx->name);
+		return 0;
+	}
+
+	if (ctx->aborting) {
+		dev_dbg(dev, "%s job not ready: aborting\n", ctx->name);
+		return 0;
+	}
+
+	return 1;
+}
+
+/* mem-to-mem ops */
+static const struct v4l2_m2m_ops hva_m2m_ops = {
+	.device_run	= hva_device_run,
+	.job_abort	= hva_job_abort,
+	.job_ready	= hva_job_ready,
+};
+
+/*
+ * VB2 queue operations
+ */
+
+static int hva_queue_setup(struct vb2_queue *vq,
+			   unsigned int *num_buffers, unsigned int *num_planes,
+			   unsigned int sizes[], void *alloc_ctxs[])
+{
+	struct hva_ctx *ctx = vb2_get_drv_priv(vq);
+	struct device *dev = ctx_to_dev(ctx);
+	unsigned int size;
+
+	dev_dbg(dev, "%s %s queue setup: num_buffers %d\n", ctx->name,
+		to_type_str(vq->type), *num_buffers);
+
+	size = vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT ?
+		ctx->frameinfo.size : ctx->max_stream_size;
+
+	alloc_ctxs[0] = ctx->hva_dev->alloc_ctx;
+
+	if (*num_planes)
+		return sizes[0] < size ? -EINVAL : 0;
+
+	/* only one plane supported */
+	*num_planes = 1;
+	sizes[0] = size;
+
+	return 0;
+}
+
+static int hva_buf_prepare(struct vb2_buffer *vb)
+{
+	struct hva_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+	struct device *dev = ctx_to_dev(ctx);
+	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+
+	if (vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+		struct hva_frame *frame = to_hva_frame(vbuf);
+
+		if (vbuf->field == V4L2_FIELD_ANY)
+			vbuf->field = V4L2_FIELD_NONE;
+		if (vbuf->field != V4L2_FIELD_NONE) {
+			dev_dbg(dev,
+				"%s frame[%d] prepare: %d field not supported\n",
+				ctx->name, vb->index, vbuf->field);
+			return -EINVAL;
+		}
+
+		if (!frame->prepared) {
+			/* get memory addresses */
+			frame->vaddr = vb2_plane_vaddr(&vbuf->vb2_buf, 0);
+			frame->paddr = vb2_dma_contig_plane_dma_addr(
+					&vbuf->vb2_buf, 0);
+			frame->info = ctx->frameinfo;
+			frame->prepared = true;
+
+			dev_dbg(dev,
+				"%s frame[%d] prepared; virt=%p, phy=%pad\n",
+				ctx->name, vb->index,
+				frame->vaddr, &frame->paddr);
+		}
+	} else {
+		struct hva_stream *stream = to_hva_stream(vbuf);
+
+		if (!stream->prepared) {
+			/* get memory addresses */
+			stream->vaddr = vb2_plane_vaddr(&vbuf->vb2_buf, 0);
+			stream->paddr = vb2_dma_contig_plane_dma_addr(
+					&vbuf->vb2_buf, 0);
+			stream->size = vb2_plane_size(&vbuf->vb2_buf, 0);
+			stream->prepared = true;
+
+			dev_dbg(dev,
+				"%s stream[%d] prepared; virt=%p, phy=%pad\n",
+				ctx->name, vb->index,
+				stream->vaddr, &stream->paddr);
+		}
+	}
+
+	return 0;
+}
+
+static void hva_buf_queue(struct vb2_buffer *vb)
+{
+	struct hva_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+
+	if (ctx->fh.m2m_ctx)
+		v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
+}
+
+static int hva_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+	struct hva_ctx *ctx = vb2_get_drv_priv(vq);
+	struct device *dev = ctx_to_dev(ctx);
+	int ret = 0;
+
+	dev_dbg(dev, "%s %s start streaming\n", ctx->name,
+		to_type_str(vq->type));
+
+	/* open encoder when both start_streaming have been called */
+	if (V4L2_TYPE_IS_OUTPUT(vq->type)) {
+		if (!vb2_start_streaming_called(&ctx->fh.m2m_ctx->cap_q_ctx.q))
+			return 0;
+	} else {
+		if (!vb2_start_streaming_called(&ctx->fh.m2m_ctx->out_q_ctx.q))
+			return 0;
+	}
+
+	if (!ctx->enc)
+		ret = hva_open_encoder(ctx,
+				       ctx->streaminfo.streamformat,
+				       ctx->frameinfo.pixelformat,
+				       &ctx->enc);
+
+	return ret;
+}
+
+static void hva_stop_streaming(struct vb2_queue *vq)
+{
+	struct hva_ctx *ctx = vb2_get_drv_priv(vq);
+	struct device *dev = ctx_to_dev(ctx);
+	const struct hva_enc *enc = ctx->enc;
+	struct vb2_v4l2_buffer *vbuf;
+
+	dev_dbg(dev, "%s %s stop streaming\n", ctx->name,
+		to_type_str(vq->type));
+
+	if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+		/* return of all pending buffers to vb2 (in error state) */
+		ctx->frame_num = 0;
+		while ((vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx)))
+			v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
+	} else {
+		/* return of all pending buffers to vb2 (in error state) */
+		ctx->stream_num = 0;
+		while ((vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx)))
+			v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
+	}
+
+	if ((V4L2_TYPE_IS_OUTPUT(vq->type) &&
+	     vb2_is_streaming(&ctx->fh.m2m_ctx->cap_q_ctx.q)) ||
+	    (!V4L2_TYPE_IS_OUTPUT(vq->type) &&
+	     vb2_is_streaming(&ctx->fh.m2m_ctx->out_q_ctx.q))) {
+		dev_dbg(dev, "%s %s out=%d cap=%d\n",
+			ctx->name, to_type_str(vq->type),
+			vb2_is_streaming(&ctx->fh.m2m_ctx->out_q_ctx.q),
+			vb2_is_streaming(&ctx->fh.m2m_ctx->cap_q_ctx.q));
+		return;
+	}
+
+	/* close encoder when both stop_streaming have been called */
+	if (enc) {
+		dev_dbg(dev, "%s %s encoder closed\n", ctx->name, enc->name);
+		enc->close(ctx);
+		ctx->enc = NULL;
+	}
+
+	ctx->aborting = false;
+}
+
+/* VB2 queue ops */
+static const struct vb2_ops hva_qops = {
+	.queue_setup		= hva_queue_setup,
+	.buf_prepare		= hva_buf_prepare,
+	.buf_queue		= hva_buf_queue,
+	.start_streaming	= hva_start_streaming,
+	.stop_streaming		= hva_stop_streaming,
+	.wait_prepare		= vb2_ops_wait_prepare,
+	.wait_finish		= vb2_ops_wait_finish,
+};
+
+/*
+ * V4L2 file operations
+ */
+
+static int queue_init(struct hva_ctx *ctx, struct vb2_queue *vq)
+{
+	vq->io_modes = VB2_MMAP | VB2_DMABUF;
+	vq->drv_priv = ctx;
+	vq->ops = &hva_qops;
+	vq->mem_ops = &vb2_dma_contig_memops;
+	vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+	vq->lock = &ctx->hva_dev->lock;
+
+	return vb2_queue_init(vq);
+}
+
+static int hva_queue_init(void *priv, struct vb2_queue *src_vq,
+			  struct vb2_queue *dst_vq)
+{
+	struct hva_ctx *ctx = priv;
+	int ret;
+
+	src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+	src_vq->buf_struct_size = sizeof(struct hva_frame);
+	src_vq->min_buffers_needed = MIN_FRAMES;
+
+	ret = queue_init(ctx, src_vq);
+	if (ret)
+		return ret;
+
+	dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	dst_vq->buf_struct_size = sizeof(struct hva_stream);
+	dst_vq->min_buffers_needed = MIN_STREAMS;
+
+	return queue_init(ctx, dst_vq);
+}
+
+static int hva_open(struct file *file)
+{
+	struct hva_dev *hva = video_drvdata(file);
+	struct device *dev = hva_to_dev(hva);
+	struct hva_ctx *ctx;
+	int ret;
+	unsigned int i;
+	bool found = false;
+
+	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+	if (!ctx) {
+		ret = -ENOMEM;
+		goto out;
+	}
+	ctx->hva_dev = hva;
+
+	mutex_lock(&hva->lock);
+
+	/* store the instance context in the instances array */
+	for (i = 0; i < HVA_MAX_INSTANCES; i++) {
+		if (!hva->instances[i]) {
+			hva->instances[i] = ctx;
+			/* save the context identifier in the context */
+			ctx->id = i;
+			found = true;
+			break;
+		}
+	}
+
+	if (!found) {
+		dev_err(dev, "%s [x:x] maximum instances reached\n",
+			HVA_PREFIX);
+		ret = -ENOMEM;
+		goto mem_ctx;
+	}
+
+	INIT_WORK(&ctx->run_work, hva_run_work);
+	v4l2_fh_init(&ctx->fh, video_devdata(file));
+	file->private_data = &ctx->fh;
+	v4l2_fh_add(&ctx->fh);
+
+	ret = hva_ctrls_setup(ctx);
+	if (ret) {
+		dev_err(dev, "%s [x:x] failed to setup controls\n",
+			HVA_PREFIX);
+		goto err_fh;
+	}
+	ctx->fh.ctrl_handler = &ctx->ctrl_handler;
+
+	mutex_init(&ctx->lock);
+
+	ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(hva->m2m_dev, ctx,
+					    &hva_queue_init);
+	if (IS_ERR(ctx->fh.m2m_ctx)) {
+		ret = PTR_ERR(ctx->fh.m2m_ctx);
+		dev_err(dev, "%s [x:x] failed to initialize m2m context (%d)\n",
+			HVA_PREFIX, ret);
+		goto err_ctrls;
+	}
+
+	/* set the instance name */
+	hva->instance_id++;
+	snprintf(ctx->name, sizeof(ctx->name), "[%3d:----]",
+		 hva->instance_id);
+
+	hva->nb_of_instances++;
+
+	mutex_unlock(&hva->lock);
+
+	/* default parameters for frame and stream */
+	set_default_params(ctx);
+
+	dev_info(dev, "%s encoder instance created (id %d)\n",
+		 ctx->name, ctx->id);
+
+	return 0;
+
+err_ctrls:
+	v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+err_fh:
+	v4l2_fh_del(&ctx->fh);
+	v4l2_fh_exit(&ctx->fh);
+	hva->instances[ctx->id] = NULL;
+mem_ctx:
+	kfree(ctx);
+	mutex_unlock(&hva->lock);
+out:
+	return ret;
+}
+
+static int hva_release(struct file *file)
+{
+	struct hva_dev *hva = video_drvdata(file);
+	struct hva_ctx *ctx = fh_to_ctx(file->private_data);
+	struct device *dev = ctx_to_dev(ctx);
+
+	v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
+
+	v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+
+	v4l2_fh_del(&ctx->fh);
+	v4l2_fh_exit(&ctx->fh);
+
+	mutex_lock(&hva->lock);
+
+	/* clear instance context in instances array */
+	hva->instances[ctx->id] = NULL;
+
+	hva->nb_of_instances--;
+
+	mutex_unlock(&hva->lock);
+
+	dev_info(dev, "%s encoder instance released (id %d)\n",
+		 ctx->name, ctx->id);
+
+	kfree(ctx);
+
+	return 0;
+}
+
+/* V4L2 file ops */
+static const struct v4l2_file_operations hva_fops = {
+	.owner			= THIS_MODULE,
+	.open			= hva_open,
+	.release		= hva_release,
+	.unlocked_ioctl		= video_ioctl2,
+	.mmap			= v4l2_m2m_fop_mmap,
+	.poll			= v4l2_m2m_fop_poll,
+};
+
+/*
+ * Platform device operations
+ */
+
+static int hva_register_device(struct hva_dev *hva)
+{
+	int ret;
+	struct video_device *vdev;
+	struct device *dev;
+
+	if (!hva)
+		return -ENODEV;
+	dev = hva_to_dev(hva);
+
+	hva->m2m_dev = v4l2_m2m_init(&hva_m2m_ops);
+	if (IS_ERR(hva->m2m_dev)) {
+		dev_err(dev, "%s %s failed to initialize v4l2-m2m device\n",
+			HVA_PREFIX, HVA_NAME);
+		ret = PTR_ERR(hva->m2m_dev);
+		goto err;
+	}
+
+	vdev = video_device_alloc();
+	if (!vdev) {
+		dev_err(dev, "%s %s failed to allocate video device\n",
+			HVA_PREFIX, HVA_NAME);
+		ret = -ENOMEM;
+		goto err_m2m_release;
+	}
+
+	vdev->fops = &hva_fops;
+	vdev->ioctl_ops = &hva_ioctl_ops;
+	vdev->release = video_device_release;
+	vdev->lock = &hva->lock;
+	vdev->vfl_dir = VFL_DIR_M2M;
+	vdev->v4l2_dev = &hva->v4l2_dev;
+	snprintf(vdev->name, sizeof(vdev->name), "%s", HVA_NAME);
+
+	ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+	if (ret) {
+		dev_err(dev, "%s %s failed to register video device\n",
+			HVA_PREFIX, HVA_NAME);
+		goto err_vdev_release;
+	}
+
+	hva->vdev = vdev;
+	video_set_drvdata(vdev, hva);
+	return 0;
+
+err_vdev_release:
+	video_device_release(vdev);
+err_m2m_release:
+	v4l2_m2m_release(hva->m2m_dev);
+err:
+	return ret;
+}
+
+static void hva_unregister_device(struct hva_dev *hva)
+{
+	if (!hva)
+		return;
+
+	if (hva->m2m_dev)
+		v4l2_m2m_release(hva->m2m_dev);
+
+	video_unregister_device(hva->vdev);
+}
+
+static int hva_probe(struct platform_device *pdev)
+{
+	struct hva_dev *hva;
+	struct device *dev = &pdev->dev;
+	int ret;
+
+	hva = devm_kzalloc(dev, sizeof(*hva), GFP_KERNEL);
+	if (!hva) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	hva->dev = dev;
+	hva->pdev = pdev;
+	platform_set_drvdata(pdev, hva);
+
+	mutex_init(&hva->lock);
+
+	/* probe hardware */
+	ret = hva_hw_probe(pdev, hva);
+	if (ret)
+		goto err;
+
+	/* register all available encoders */
+	register_encoders(hva);
+
+	/* register all supported formats */
+	register_formats(hva);
+
+	/* register on V4L2 */
+	ret = v4l2_device_register(dev, &hva->v4l2_dev);
+	if (ret) {
+		dev_err(dev, "%s %s failed to register V4L2 device\n",
+			HVA_PREFIX, HVA_NAME);
+		goto err_hw;
+	}
+
+	/* continuous memory allocator */
+	hva->alloc_ctx = vb2_dma_contig_init_ctx(dev);
+	if (IS_ERR(hva->alloc_ctx)) {
+		ret = PTR_ERR(hva->alloc_ctx);
+		goto err_v4l2;
+	}
+
+	hva->work_queue = create_workqueue(HVA_NAME);
+	if (!hva->work_queue) {
+		dev_err(dev, "%s %s failed to allocate work queue\n",
+			HVA_PREFIX, HVA_NAME);
+		ret = -ENOMEM;
+		goto err_vb2_dma;
+	}
+
+	/* register device */
+	ret = hva_register_device(hva);
+	if (ret)
+		goto err_work_queue;
+
+	dev_info(dev, "%s %s registered as /dev/video%d\n", HVA_PREFIX,
+		 HVA_NAME, hva->vdev->num);
+
+	return 0;
+
+err_work_queue:
+	destroy_workqueue(hva->work_queue);
+err_vb2_dma:
+	vb2_dma_contig_cleanup_ctx(hva->alloc_ctx);
+err_v4l2:
+	v4l2_device_unregister(&hva->v4l2_dev);
+err_hw:
+	hva_hw_remove(hva);
+err:
+	return ret;
+}
+
+static int hva_remove(struct platform_device *pdev)
+{
+	struct hva_dev *hva = platform_get_drvdata(pdev);
+	struct device *dev = hva_to_dev(hva);
+
+	hva_unregister_device(hva);
+
+	destroy_workqueue(hva->work_queue);
+
+	vb2_dma_contig_cleanup_ctx(hva->alloc_ctx);
+
+	hva_hw_remove(hva);
+
+	v4l2_device_unregister(&hva->v4l2_dev);
+
+	dev_info(dev, "%s %s removed\n", HVA_PREFIX, pdev->name);
+
+	return 0;
+}
+
+/* PM ops */
+static const struct dev_pm_ops hva_pm_ops = {
+	.runtime_suspend	= hva_hw_runtime_suspend,
+	.runtime_resume		= hva_hw_runtime_resume,
+};
+
+static const struct of_device_id hva_match_types[] = {
+	{
+	 .compatible = "st,sti-hva",
+	},
+	{ /* end node */ }
+};
+
+MODULE_DEVICE_TABLE(of, hva_match_types);
+
+struct platform_driver hva_driver = {
+	.probe  = hva_probe,
+	.remove = hva_remove,
+	.driver = {
+		.name           = HVA_NAME,
+		.owner          = THIS_MODULE,
+		.of_match_table = hva_match_types,
+		.pm             = &hva_pm_ops,
+		},
+};
+
+module_platform_driver(hva_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>");
+MODULE_DESCRIPTION("HVA video encoder V4L2 driver");
diff --git a/drivers/media/platform/sti/hva/hva.h b/drivers/media/platform/sti/hva/hva.h
new file mode 100644
index 0000000..9a1b503b
--- /dev/null
+++ b/drivers/media/platform/sti/hva/hva.h
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2015
+ * Authors: Yannick Fertre <yannick.fertre@st.com>
+ *          Hugues Fruchet <hugues.fruchet@st.com>
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#ifndef HVA_H
+#define HVA_H
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/videobuf2-v4l2.h>
+#include <media/v4l2-mem2mem.h>
+
+#define fh_to_ctx(f)    (container_of(f, struct hva_ctx, fh))
+
+#define hva_to_dev(h)   (h->dev)
+
+#define ctx_to_dev(c)   (c->hva_dev->dev)
+
+#define ctx_to_hdev(c)  (c->hva_dev)
+
+#define HVA_PREFIX "[---:----]"
+
+/**
+ * struct hva_frameinfo - information about hva frame
+ *
+ * @pixelformat:    fourcc code for uncompressed video format
+ * @width:          width of frame
+ * @height:         height of frame
+ * @aligned_width:  width of frame (with encoder alignment constraint)
+ * @aligned_height: height of frame (with encoder alignment constraint)
+ * @size:           maximum size in bytes required for data
+*/
+struct hva_frameinfo {
+	u32	pixelformat;
+	u32	width;
+	u32	height;
+	u32	aligned_width;
+	u32	aligned_height;
+	u32	size;
+};
+
+/**
+ * struct hva_streaminfo - information about hva stream
+ *
+ * @streamformat: fourcc code of compressed video format (H.264...)
+ * @width:        width of stream
+ * @height:       height of stream
+ * @profile:      profile string
+ * @level:        level string
+ */
+struct hva_streaminfo {
+	u32	streamformat;
+	u32	width;
+	u32	height;
+	u8	profile[32];
+	u8	level[32];
+};
+
+/**
+ * struct hva_controls - hva controls set
+ *
+ * @time_per_frame: time per frame in seconds
+ * @bitrate_mode:   bitrate mode (constant bitrate or variable bitrate)
+ * @gop_size:       groupe of picture size
+ * @bitrate:        bitrate (in kbps)
+ * @aspect:         video aspect
+ */
+struct hva_controls {
+	struct v4l2_fract			time_per_frame;
+	enum v4l2_mpeg_video_bitrate_mode	bitrate_mode;
+	u32					gop_size;
+	u32					bitrate;
+	enum v4l2_mpeg_video_aspect		aspect;
+};
+
+/**
+ * struct hva_frame - hva frame buffer (output)
+ *
+ * @vbuf:     video buffer information for V4L2
+ * @list:     V4L2 m2m list that the frame belongs to
+ * @info:     frame information (width, height, format, alignment...)
+ * @paddr:    physical address (for hardware)
+ * @vaddr:    virtual address (kernel can read/write)
+ * @prepared: true if vaddr/paddr are resolved
+ */
+struct hva_frame {
+	struct vb2_v4l2_buffer	vbuf;
+	struct list_head	list;
+	struct hva_frameinfo	info;
+	dma_addr_t		paddr;
+	void			*vaddr;
+	bool			prepared;
+};
+
+/*
+ * to_hva_frame() - cast struct vb2_v4l2_buffer * to struct hva_frame *
+ */
+#define to_hva_frame(vb) \
+	container_of(vb, struct hva_frame, vbuf)
+
+/**
+ * struct hva_stream - hva stream buffer (capture)
+ *
+ * @v4l2:       video buffer information for V4L2
+ * @list:       V4L2 m2m list that the frame belongs to
+ * @paddr:      physical address (for hardware)
+ * @vaddr:      virtual address (kernel can read/write)
+ * @prepared:   true if vaddr/paddr are resolved
+ * @size:       size of the buffer in bytes
+ * @bytesused:  number of bytes occupied by data in the buffer
+ */
+struct hva_stream {
+	struct vb2_v4l2_buffer	vbuf;
+	struct list_head	list;
+	dma_addr_t		paddr;
+	void			*vaddr;
+	int			prepared;
+	unsigned int		size;
+	unsigned int		bytesused;
+};
+
+/*
+ * to_hva_stream() - cast struct vb2_v4l2_buffer * to struct hva_stream *
+ */
+#define to_hva_stream(vb) \
+	container_of(vb, struct hva_stream, vbuf)
+
+struct hva_dev;
+struct hva_enc;
+
+/**
+ * struct hva_ctx - context of hva instance
+ *
+ * @hva_dev:         the device that this instance is associated with
+ * @fh:              V4L2 file handle
+ * @ctrl_handler:    V4L2 controls handler
+ * @ctrls:           hva controls set
+ * @id:              instance identifier
+ * @aborting:        true if current job aborted
+ * @name:            instance name (debug purpose)
+ * @run_work:        encode work
+ * @lock:            mutex used to lock access of this context
+ * @flags:           validity of streaminfo and frameinfo fields
+ * @frame_num:       frame number
+ * @stream_num:      stream number
+ * @max_stream_size: maximum size in bytes required for stream data
+ * @streaminfo:      stream properties
+ * @frameinfo:       frame properties
+ * @enc:             current encoder
+ * @priv:            private codec data for this instance, allocated
+ *                   by encoder @open time
+ * @hw_err:          true if hardware error detected
+ */
+struct hva_ctx {
+	struct hva_dev		        *hva_dev;
+	struct v4l2_fh			fh;
+	struct v4l2_ctrl_handler	ctrl_handler;
+	struct hva_controls		ctrls;
+	u8				id;
+	bool				aborting;
+	char				name[100];
+	struct work_struct		run_work;
+	/* mutex protecting this data structure */
+	struct mutex			lock;
+	u32				flags;
+	u32				frame_num;
+	u32				stream_num;
+	u32				max_stream_size;
+	struct hva_streaminfo		streaminfo;
+	struct hva_frameinfo		frameinfo;
+	struct hva_enc			*enc;
+	void				*priv;
+	bool				hw_err;
+};
+
+#define HVA_FLAG_STREAMINFO	0x0001
+#define HVA_FLAG_FRAMEINFO	0x0002
+
+#define HVA_MAX_INSTANCES	16
+#define HVA_MAX_ENCODERS	10
+#define HVA_MAX_FORMATS		HVA_MAX_ENCODERS
+
+/**
+ * struct hva_dev - abstraction for hva entity
+ *
+ * @v4l2_dev:            V4L2 device
+ * @vdev:                video device
+ * @pdev:                platform device
+ * @dev:                 device
+ * @lock:                mutex used for critical sections & V4L2 ops
+ *                       serialization
+ * @m2m_dev:             memory-to-memory V4L2 device informatio
+ * @alloc_ctx:           videobuf2 memory allocator context
+ * @instances:           opened instances
+ * @nb_of_instances:     number of opened instances
+ * @instance_id:         rolling counter identifying an instance (debug purpose)
+ * @regs:                register io memory access
+ * @esram_addr:          esram address
+ * @esram_size:          esram size
+ * @clk:                 hva clock
+ * @irq_its:             status interruption
+ * @irq_err:             error interruption
+ * @work_queue:          work queue to handle the encode jobs
+ * @protect_mutex:       mutex used to lock access of hardware
+ * @interrupt:           completion interrupt
+ * @ip_version:          IP hardware version
+ * @encoders:            registered encoders
+ * @nb_of_encoders:      number of registered encoders
+ * @pixelformats:        supported uncompressed video formats
+ * @nb_of_pixelformats:  number of supported umcompressed video formats
+ * @streamformats:       supported compressed video formats
+ * @nb_of_streamformats: number of supported compressed video formats
+ * @sfl_reg:             status fifo level register value
+ * @sts_reg:             status register value
+ * @lmi_err_reg:         local memory interface error register value
+ * @emi_err_reg:         external memory interface error register value
+ * @hec_mif_err_reg:     HEC memory interface error register value
+ */
+struct hva_dev {
+	struct v4l2_device	v4l2_dev;
+	struct video_device	*vdev;
+	struct platform_device	*pdev;
+	struct device		*dev;
+	/* mutex protecting vb2_queue structure */
+	struct mutex		lock;
+	struct v4l2_m2m_dev	*m2m_dev;
+	struct vb2_alloc_ctx	*alloc_ctx;
+	struct hva_ctx		*instances[HVA_MAX_INSTANCES];
+	unsigned int		nb_of_instances;
+	unsigned int		instance_id;
+	void __iomem		*regs;
+	u32			esram_addr;
+	u32			esram_size;
+	struct clk		*clk;
+	int			irq_its;
+	int			irq_err;
+	struct workqueue_struct *work_queue;
+	/* mutex protecting hardware access */
+	struct mutex		protect_mutex;
+	struct completion	interrupt;
+	unsigned long int	ip_version;
+	const struct hva_enc	*encoders[HVA_MAX_ENCODERS];
+	u32			nb_of_encoders;
+	u32			pixelformats[HVA_MAX_FORMATS];
+	u32			nb_of_pixelformats;
+	u32			streamformats[HVA_MAX_FORMATS];
+	u32			nb_of_streamformats;
+	u32			sfl_reg;
+	u32			sts_reg;
+	u32			lmi_err_reg;
+	u32			emi_err_reg;
+	u32			hec_mif_err_reg;
+};
+
+/**
+ * struct hva_enc - hva encoder
+ *
+ * @name:         encoder name
+ * @streamformat: fourcc code for compressed video format (H.264...)
+ * @pixelformat:  fourcc code for uncompressed video format
+ * @max_width:    maximum width of frame for this encoder
+ * @max_height:   maximum height of frame for this encoder
+ * @open:         open encoder
+ * @close:        close encoder
+ * @encode:       encode a frame (struct hva_frame) in a stream
+ *                (struct hva_stream)
+ */
+
+struct hva_enc {
+	const char	*name;
+	u32		streamformat;
+	u32		pixelformat;
+	u32		max_width;
+	u32		max_height;
+	int		(*open)(struct hva_ctx *ctx);
+	int		(*close)(struct hva_ctx *ctx);
+	int		(*encode)(struct hva_ctx *ctx, struct hva_frame *frame,
+				  struct hva_stream *stream);
+};
+
+#endif /* HVA_H */
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v2 3/3] [media] hva: add H.264 video encoding support
  2016-07-11 15:14 [PATCH v2 0/3] support of v4l2 encoder for STMicroelectronics SOC Jean-Christophe Trotin
  2016-07-11 15:14 ` [PATCH v2 1/3] Documentation: DT: add bindings for STI HVA Jean-Christophe Trotin
  2016-07-11 15:14 ` [PATCH v2 2/3] [media] hva: multi-format video encoder V4L2 driver Jean-Christophe Trotin
@ 2016-07-11 15:14 ` Jean-Christophe Trotin
  2016-07-18 11:55   ` Hans Verkuil
  2016-07-11 17:48 ` [PATCH v2 0/3] support of v4l2 encoder for STMicroelectronics SOC Nicolas Dufresne
  3 siblings, 1 reply; 18+ messages in thread
From: Jean-Christophe Trotin @ 2016-07-11 15:14 UTC (permalink / raw)
  To: linux-media, Hans Verkuil
  Cc: kernel, Benjamin Gaignard, Yannick Fertre, Hugues Fruchet,
	Jean-Christophe Trotin

This patch adds the H.264 video encoding capability in the V4L2 HVA
video encoder driver for STMicroelectronics SoC (hva-h264.c).

The main supported features are:
- profile: baseline, main, high, stereo high
- level: up to 4.2
- bitrate mode: CBR, VBR
- entropy mode: CABAC, CAVLC
- video aspect: 1x1 only

Signed-off-by: Yannick Fertre <yannick.fertre@st.com>
Signed-off-by: Jean-Christophe Trotin <jean-christophe.trotin@st.com>
---
 drivers/media/platform/sti/hva/Makefile   |    2 +-
 drivers/media/platform/sti/hva/hva-h264.c | 1053 +++++++++++++++++++++++++++++
 drivers/media/platform/sti/hva/hva-v4l2.c |  107 ++-
 drivers/media/platform/sti/hva/hva.h      |  115 +++-
 4 files changed, 1270 insertions(+), 7 deletions(-)
 create mode 100644 drivers/media/platform/sti/hva/hva-h264.c

diff --git a/drivers/media/platform/sti/hva/Makefile b/drivers/media/platform/sti/hva/Makefile
index 7022a33..495894b 100644
--- a/drivers/media/platform/sti/hva/Makefile
+++ b/drivers/media/platform/sti/hva/Makefile
@@ -1,2 +1,2 @@
 obj-$(CONFIG_VIDEO_STI_HVA) := hva.o
-hva-y := hva-v4l2.o hva-hw.o hva-mem.o
+hva-y := hva-v4l2.o hva-hw.o hva-mem.o hva-h264.o
diff --git a/drivers/media/platform/sti/hva/hva-h264.c b/drivers/media/platform/sti/hva/hva-h264.c
new file mode 100644
index 0000000..f7cdaa1
--- /dev/null
+++ b/drivers/media/platform/sti/hva/hva-h264.c
@@ -0,0 +1,1053 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2015
+ * Authors: Yannick Fertre <yannick.fertre@st.com>
+ *          Hugues Fruchet <hugues.fruchet@st.com>
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#include "hva.h"
+#include "hva-hw.h"
+
+#define MAX_SPS_PPS_SIZE 128
+
+#define BITSTREAM_OFFSET_MASK 0x7F
+
+/* video max size*/
+#define H264_MAX_SIZE_W 1920
+#define H264_MAX_SIZE_H 1920
+
+/* macroBlocs number (width & height) */
+#define MB_W(w) ((w + 0xF)  / 0x10)
+#define MB_H(h) ((h + 0xF)  / 0x10)
+
+/* formula to get temporal or spatial data size */
+#define DATA_SIZE(w, h) (MB_W(w) * MB_H(h) * 16)
+
+#define SEARCH_WINDOW_BUFFER_MAX_SIZE(w) ((4 * MB_W(w) + 42) * 256 * 3 / 2)
+#define CABAC_CONTEXT_BUFFER_MAX_SIZE(w) (MB_W(w) * 16)
+#define CTX_MB_BUFFER_MAX_SIZE(w) (MB_W(w) * 16 * 8)
+#define SLICE_HEADER_SIZE (4 * 16)
+#define BRC_DATA_SIZE (5 * 16)
+
+/* source buffer copy in YUV 420 MB-tiled format with size=16*256*3/2 */
+#define CURRENT_WINDOW_BUFFER_MAX_SIZE (16 * 256 * 3 / 2)
+
+/*
+ * 4 lines of pixels (in Luma, Chroma blue and Chroma red) of top MB
+ * for deblocking with size=4*16*MBx*2
+ */
+#define LOCAL_RECONSTRUCTED_BUFFER_MAX_SIZE(w) (4 * 16 * MB_W(w) * 2)
+
+/* factor for bitrate and cpb buffer size max values if profile >= high */
+#define H264_FACTOR_HIGH 1200
+
+/* factor for bitrate and cpb buffer size max values if profile < high */
+#define H264_FACTOR_BASELINE 1000
+
+/* number of bytes for NALU_TYPE_FILLER_DATA header and footer */
+#define H264_FILLER_DATA_SIZE 6
+
+struct h264_profile {
+	enum v4l2_mpeg_video_h264_level level;
+	u32 max_mb_per_seconds;
+	u32 max_frame_size;
+	u32 max_bitrate;
+	u32 max_cpb_size;
+	u32 min_comp_ratio;
+};
+
+static const struct h264_profile h264_infos_list[] = {
+	{V4L2_MPEG_VIDEO_H264_LEVEL_1_0, 1485, 99, 64, 175, 2},
+	{V4L2_MPEG_VIDEO_H264_LEVEL_1B, 1485, 99, 128, 350, 2},
+	{V4L2_MPEG_VIDEO_H264_LEVEL_1_1, 3000, 396, 192, 500, 2},
+	{V4L2_MPEG_VIDEO_H264_LEVEL_1_2, 6000, 396, 384, 1000, 2},
+	{V4L2_MPEG_VIDEO_H264_LEVEL_1_3, 11880, 396, 768, 2000, 2},
+	{V4L2_MPEG_VIDEO_H264_LEVEL_2_0, 11880, 396, 2000, 2000, 2},
+	{V4L2_MPEG_VIDEO_H264_LEVEL_2_1, 19800, 792, 4000, 4000, 2},
+	{V4L2_MPEG_VIDEO_H264_LEVEL_2_2, 20250, 1620, 4000, 4000, 2},
+	{V4L2_MPEG_VIDEO_H264_LEVEL_3_0, 40500, 1620, 10000, 10000, 2},
+	{V4L2_MPEG_VIDEO_H264_LEVEL_3_1, 108000, 3600, 14000, 14000, 4},
+	{V4L2_MPEG_VIDEO_H264_LEVEL_3_2, 216000, 5120, 20000, 20000, 4},
+	{V4L2_MPEG_VIDEO_H264_LEVEL_4_0, 245760, 8192, 20000, 25000, 4},
+	{V4L2_MPEG_VIDEO_H264_LEVEL_4_1, 245760, 8192, 50000, 62500, 2},
+	{V4L2_MPEG_VIDEO_H264_LEVEL_4_2, 522240, 8704, 50000, 62500, 2},
+	{V4L2_MPEG_VIDEO_H264_LEVEL_5_0, 589824, 22080, 135000, 135000, 2},
+	{V4L2_MPEG_VIDEO_H264_LEVEL_5_1, 983040, 36864, 240000, 240000, 2}
+};
+
+enum hva_brc_type {
+	BRC_TYPE_NONE = 0,
+	BRC_TYPE_CBR = 1,
+	BRC_TYPE_VBR = 2,
+	BRC_TYPE_VBR_LOW_DELAY = 3
+};
+
+enum hva_entropy_coding_mode {
+	CAVLC = 0,
+	CABAC = 1
+};
+
+enum hva_picture_coding_type {
+	PICTURE_CODING_TYPE_I = 0,
+	PICTURE_CODING_TYPE_P = 1,
+	PICTURE_CODING_TYPE_B = 2
+};
+
+enum hva_h264_sampling_mode {
+	SAMPLING_MODE_NV12 = 0,
+	SAMPLING_MODE_UYVY = 1,
+	SAMPLING_MODE_RGB3 = 3,
+	SAMPLING_MODE_XRGB4 = 4,
+	SAMPLING_MODE_NV21 = 8,
+	SAMPLING_MODE_VYUY = 9,
+	SAMPLING_MODE_BGR3 = 11,
+	SAMPLING_MODE_XBGR4 = 12,
+	SAMPLING_MODE_RGBX4 = 20,
+	SAMPLING_MODE_BGRX4 = 28
+};
+
+enum hva_h264_nalu_type {
+	NALU_TYPE_UNKNOWN = 0,
+	NALU_TYPE_SLICE = 1,
+	NALU_TYPE_SLICE_DPA = 2,
+	NALU_TYPE_SLICE_DPB = 3,
+	NALU_TYPE_SLICE_DPC = 4,
+	NALU_TYPE_SLICE_IDR = 5,
+	NALU_TYPE_SEI = 6,
+	NALU_TYPE_SPS = 7,
+	NALU_TYPE_PPS = 8,
+	NALU_TYPE_AU_DELIMITER = 9,
+	NALU_TYPE_SEQ_END = 10,
+	NALU_TYPE_STREAM_END = 11,
+	NALU_TYPE_FILLER_DATA = 12,
+	NALU_TYPE_SPS_EXT = 13,
+	NALU_TYPE_PREFIX_UNIT = 14,
+	NALU_TYPE_SUBSET_SPS = 15,
+	NALU_TYPE_SLICE_AUX = 19,
+	NALU_TYPE_SLICE_EXT = 20
+};
+
+enum hva_h264_sei_payload_type {
+	SEI_BUFFERING_PERIOD = 0,
+	SEI_PICTURE_TIMING = 1,
+	SEI_STEREO_VIDEO_INFO = 21,
+	SEI_FRAME_PACKING_ARRANGEMENT = 45
+};
+
+/**
+ * stereo Video Info struct
+ */
+struct hva_h264_stereo_video_sei {
+	u8 field_views_flag;
+	u8 top_field_is_left_view_flag;
+	u8 current_frame_is_left_view_flag;
+	u8 next_frame_is_second_view_flag;
+	u8 left_view_self_contained_flag;
+	u8 right_view_self_contained_flag;
+};
+
+/**
+ * @frame_width: width in pixels of the buffer containing the input frame
+ * @frame_height: height in pixels of the buffer containing the input frame
+ * @frame_num: the parameter to be written in the slice header
+ * @picture_coding_type: type I, P or B
+ * @pic_order_cnt_type: POC mode, as defined in H264 std : can be 0,1,2
+ * @first_picture_in_sequence: flag telling to encoder that this is the
+ *			       first picture in a video sequence.
+ *			       Used for VBR
+ * @slice_size_type: 0 = no constraint to close the slice
+ *		     1= a slice is closed as soon as the slice_mb_size limit
+ *			is reached
+ *		     2= a slice is closed as soon as the slice_byte_size limit
+ *			is reached
+ *		     3= a slice is closed as soon as either the slice_byte_size
+ *			limit or the slice_mb_size limit is reached
+ * @slice_mb_size: defines the slice size in number of macroblocks
+ *		   (used when slice_size_type=1 or slice_size_type=3)
+ * @ir_param_option: defines the number of macroblocks per frame to be
+ *		     refreshed by AIR algorithm OR the refresh period
+ *		     by CIR algorithm
+ * @intra_refresh_type: enables the adaptive intra refresh algorithm.
+ *			Disable=0 / Adaptative=1 and Cycle=2 as intra refresh
+ * @use_constrained_intra_flag: constrained_intra_pred_flag from PPS
+ * @transform_mode: controls the use of 4x4/8x8 transform mode
+ * @disable_deblocking_filter_idc:
+ *		     0: specifies that all luma and chroma block edges of
+ *			the slice are filtered.
+ *		     1: specifies that deblocking is disabled for all block
+ *			edges of the slice.
+ *		     2: specifies that all luma and chroma block edges of
+ *			the slice are filtered with exception of the block edges
+ *			that coincide with slice boundaries
+ * @slice_alpha_c0_offset_div2: to be written in slice header,
+ *				controls deblocking
+ * @slice_beta_offset_div2: to be written in slice header,
+ *			    controls deblocking
+ * @encoder_complexity: encoder complexity control (IME).
+ *		     0 = I_16x16, P_16x16, Full ME Complexity
+ *		     1 = I_16x16, I_NxN, P_16x16, Full ME Complexity
+ *		     2 = I_16x16, I_NXN, P_16x16, P_WxH, Full ME Complexity
+ *		     4 = I_16x16, P_16x16, Reduced ME Complexity
+ *		     5 = I_16x16, I_NxN, P_16x16, Reduced ME Complexity
+ *		     6 = I_16x16, I_NXN, P_16x16, P_WxH, Reduced ME Complexity
+ *  @chroma_qp_index_offset: coming from picture parameter set
+ *			     (PPS see [H.264 STD] 7.4.2.2)
+ *  @entropy_coding_mode: entropy coding mode.
+ *			  0 = CAVLC
+ *			  1 = CABAC
+ * @brc_type: selects the bit-rate control algorithm
+ *		     0 = constant Qp, (no BRC)
+ *		     1 = CBR
+ *		     2 = VBR
+ * @quant: Quantization param used in case of fix QP encoding (no BRC)
+ * @non_VCL_NALU_Size: size of non-VCL NALUs (SPS, PPS, filler),
+ *		       used by BRC
+ * @cpb_buffer_size: size of Coded Picture Buffer, used by BRC
+ * @bit_rate: target bitrate, for BRC
+ * @qp_min: min QP threshold
+ * @qp_max: max QP threshold
+ * @framerate_num: target framerate numerator , used by BRC
+ * @framerate_den: target framerate denomurator , used by BRC
+ * @delay: End-to-End Initial Delay
+ * @strict_HRD_compliancy: flag for HDR compliancy (1)
+ *			   May impact quality encoding
+ * @addr_source_buffer: address of input frame buffer for current frame
+ * @addr_fwd_Ref_Buffer: address of reference frame buffer
+ * @addr_rec_buffer: address of reconstructed frame buffer
+ * @addr_output_bitstream_start: output bitstream start address
+ * @addr_output_bitstream_end: output bitstream end address
+ * @addr_external_sw : address of external search window
+ * @addr_lctx : address of context picture buffer
+ * @addr_local_rec_buffer: address of local reconstructed buffer
+ * @addr_spatial_context: address of spatial context buffer
+ * @bitstream_offset: offset in bits between aligned bitstream start
+ *		      address and first bit to be written by HVA.
+ *		      Range value is [0..63]
+ * @sampling_mode: Input picture format .
+ *		     0: YUV420 semi_planar Interleaved
+ *		     1: YUV422 raster Interleaved
+ * @addr_param_out: address of output parameters structure
+ * @addr_scaling_matrix: address to the coefficient of
+ *			 the inverse scaling matrix
+ * @addr_scaling_matrix_dir: address to the coefficient of
+ *			     the direct scaling matrix
+ * @addr_cabac_context_buffer: address of cabac context buffer
+ * @GmvX: Input information about the horizontal global displacement of
+ *	  the encoded frame versus the previous one
+ * @GmvY: Input information about the vertical global displacement of
+ *	  the encoded frame versus the previous one
+ * @window_width: width in pixels of the window to be encoded inside
+ *		  the input frame
+ * @window_height: width in pixels of the window to be encoded inside
+ *		   the input frame
+ * @window_horizontal_offset: horizontal offset in pels for input window
+ *			      within input frame
+ * @window_vertical_offset: vertical offset in pels for input window
+ *			    within input frame
+ * @addr_roi: Map of QP offset for the Region of Interest algorithm and
+ *	      also used for Error map.
+ *	      Bit 0-6 used for qp offset (value -64 to 63).
+ *	      Bit 7 used to force intra
+ * @addr_slice_header: address to slice header
+ * @slice_header_size_in_bits: size in bits of the Slice header
+ * @slice_header_offset0: Slice header offset where to insert
+ *			  first_Mb_in_slice
+ * @slice_header_offset1: Slice header offset where to insert
+ *			  slice_qp_delta
+ * @slice_header_offset2: Slice header offset where to insert
+ *			  num_MBs_in_slice
+ * @slice_synchro_enable: enable "slice ready" interrupt after each slice
+ * @max_slice_number: Maximum number of slice in a frame
+ *		      (0 is strictly forbidden)
+ * @rgb2_yuv_y_coeff: Four coefficients (C0C1C2C3) to convert from RGB to
+ *		      YUV for the Y component.
+ *		      Y = C0*R + C1*G + C2*B + C3 (C0 is on byte 0)
+ * @rgb2_yuv_u_coeff: four coefficients (C0C1C2C3) to convert from RGB to
+ *		      YUV for the Y component.
+ *		      Y = C0*R + C1*G + C2*B + C3 (C0 is on byte 0)
+ * @rgb2_yuv_v_coeff: Four coefficients (C0C1C2C3) to convert from RGB to
+ *		      YUV for the U (Cb) component.
+ *		      U = C0*R + C1*G + C2*B + C3 (C0 is on byte 0)
+ * @slice_byte_size: maximum slice size in bytes
+ *		     (used when slice_size_type=2 or slice_size_type=3)
+ * @max_air_intra_mb_nb: Maximum number of intra macroblock in a frame
+ *			 for the AIR algorithm
+ * @brc_no_skip: Disable skipping in the Bitrate Controller
+ * @addr_brc_in_out_parameter: address of static buffer for BRC parameters
+ */
+struct hva_h264_td {
+	u16 frame_width;
+	u16 frame_height;
+	u32 frame_num;
+	u16 picture_coding_type;
+	u16 reserved1;
+	u16 pic_order_cnt_type;
+	u16 first_picture_in_sequence;
+	u16 slice_size_type;
+	u16 reserved2;
+	u32 slice_mb_size;
+	u16 ir_param_option;
+	u16 intra_refresh_type;
+	u16 use_constrained_intra_flag;
+	u16 transform_mode;
+	u16 disable_deblocking_filter_idc;
+	s16 slice_alpha_c0_offset_div2;
+	s16 slice_beta_offset_div2;
+	u16 encoder_complexity;
+	s16 chroma_qp_index_offset;
+	u16 entropy_coding_mode;
+	u16 brc_type;
+	u16 quant;
+	u32 non_vcl_nalu_size;
+	u32 cpb_buffer_size;
+	u32 bit_rate;
+	u16 qp_min;
+	u16 qp_max;
+	u16 framerate_num;
+	u16 framerate_den;
+	u16 delay;
+	u16 strict_hrd_compliancy;
+	u32 addr_source_buffer;
+	u32 addr_fwd_ref_buffer;
+	u32 addr_rec_buffer;
+	u32 addr_output_bitstream_start;
+	u32 addr_output_bitstream_end;
+	u32 addr_external_sw;
+	u32 addr_lctx;
+	u32 addr_local_rec_buffer;
+	u32 addr_spatial_context;
+	u16 bitstream_offset;
+	u16 sampling_mode;
+	u32 addr_param_out;
+	u32 addr_scaling_matrix;
+	u32 addr_scaling_matrix_dir;
+	u32 addr_cabac_context_buffer;
+	u32 reserved3;
+	u32 reserved4;
+	s16 gmv_x;
+	s16 gmv_y;
+	u16 window_width;
+	u16 window_height;
+	u16 window_horizontal_offset;
+	u16 window_vertical_offset;
+	u32 addr_roi;
+	u32 addr_slice_header;
+	u16 slice_header_size_in_bits;
+	u16 slice_header_offset0;
+	u16 slice_header_offset1;
+	u16 slice_header_offset2;
+	u32 reserved5;
+	u32 reserved6;
+	u16 reserved7;
+	u16 reserved8;
+	u16 slice_synchro_enable;
+	u16 max_slice_number;
+	u32 rgb2_yuv_y_coeff;
+	u32 rgb2_yuv_u_coeff;
+	u32 rgb2_yuv_v_coeff;
+	u32 slice_byte_size;
+	u16 max_air_intra_mb_nb;
+	u16 brc_no_skip;
+	u32 addr_temporal_context;
+	u32 addr_brc_in_out_parameter;
+};
+
+/**
+ * @ slice_size: slice size
+ * @ slice_start_time: start time
+ * @ slice_stop_time: stop time
+ * @ slice_num: slice number
+ */
+struct hva_h264_slice_po {
+	u32 slice_size;
+	u32 slice_start_time;
+	u32 slice_end_time;
+	u32 slice_num;
+};
+
+/**
+ * @ bitstream_size: bitstream size
+ * @ dct_bitstream_size: dtc bitstream size
+ * @ stuffing_bits: number of stuffing bits inserted by the encoder
+ * @ removal_time: removal time of current frame (nb of ticks 1/framerate)
+ * @ hvc_start_time: hvc start time
+ * @ hvc_stop_time: hvc stop time
+ * @ slice_count: slice count
+ */
+struct hva_h264_po {
+	u32 bitstream_size;
+	u32 dct_bitstream_size;
+	u32 stuffing_bits;
+	u32 removal_time;
+	u32 hvc_start_time;
+	u32 hvc_stop_time;
+	u32 slice_count;
+	u32 reserved0;
+	struct hva_h264_slice_po slice_params[16];
+};
+
+struct hva_h264_task {
+	struct hva_h264_td td;
+	struct hva_h264_po po;
+};
+
+/**
+ * @seq_info:  sequence information buffer
+ * @ref_frame: reference frame buffer
+ * @rec_frame: reconstructed frame buffer
+ * @task:      task descriptor
+ */
+struct hva_h264_ctx {
+	struct hva_buffer *seq_info;
+	struct hva_buffer *ref_frame;
+	struct hva_buffer *rec_frame;
+	struct hva_buffer *task;
+};
+
+static int hva_h264_fill_slice_header(struct hva_ctx *pctx,
+				      u8 *slice_header_addr,
+				      struct hva_controls *ctrls,
+				      int frame_num,
+				      u16 *header_size,
+				      u16 *header_offset0,
+				      u16 *header_offset1,
+				      u16 *header_offset2)
+{
+	/*
+	 * with this HVA hardware version, part of the slice header is computed
+	 * on host and part by hardware.
+	 * The part of host is precomputed and available through this array.
+	 */
+	struct device *dev = ctx_to_dev(pctx);
+	int  cabac = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC;
+	const unsigned char slice_header[] = { 0x00, 0x00, 0x00, 0x01,
+					       0x41, 0x34, 0x07, 0x00};
+	int idr_pic_id = frame_num % 2;
+	enum hva_picture_coding_type type;
+	u32 frame_order = frame_num % ctrls->gop_size;
+
+	if (!(frame_num % ctrls->gop_size))
+		type = PICTURE_CODING_TYPE_I;
+	else
+		type = PICTURE_CODING_TYPE_P;
+
+	memcpy(slice_header_addr, slice_header, sizeof(slice_header));
+
+	*header_size = 56;
+	*header_offset0 = 40;
+	*header_offset1 = 13;
+	*header_offset2 = 0;
+
+	if (type == PICTURE_CODING_TYPE_I) {
+		slice_header_addr[4] = 0x65;
+		slice_header_addr[5] = 0x11;
+
+		/* toggle the I frame */
+		if ((frame_num / ctrls->gop_size) % 2) {
+			*header_size += 4;
+			*header_offset1 += 4;
+			slice_header_addr[6] = 0x04;
+			slice_header_addr[7] = 0x70;
+
+		} else {
+			*header_size += 2;
+			*header_offset1 += 2;
+			slice_header_addr[6] = 0x09;
+			slice_header_addr[7] = 0xC0;
+		}
+	} else {
+		if (ctrls->entropy_mode == cabac) {
+			*header_size += 1;
+			*header_offset1 += 1;
+			slice_header_addr[7] = 0x80;
+		}
+		/*
+		 * update slice header with P frame order
+		 * frame order is limited to 16 (coded on 4bits only)
+		 */
+		slice_header_addr[5] += ((frame_order & 0x0C) >> 2);
+		slice_header_addr[6] += ((frame_order & 0x03) << 6);
+	}
+
+	dev_dbg(dev,
+		"%s   %s slice header order %d idrPicId %d header size %d\n",
+		pctx->name, __func__, frame_order, idr_pic_id, *header_size);
+	return 0;
+}
+
+static int hva_h264_fill_data_nal(struct hva_ctx *pctx,
+				  unsigned int stuffing_bytes, u8 *addr,
+				  unsigned int stream_size, unsigned int *size)
+{
+	struct device *dev = ctx_to_dev(pctx);
+	const u8 start[] = { 0x00, 0x00, 0x00, 0x01 };
+
+	dev_dbg(dev, "%s   %s stuffing bytes %d\n", pctx->name, __func__,
+		stuffing_bytes);
+
+	if ((*size + stuffing_bytes + H264_FILLER_DATA_SIZE) > stream_size) {
+		dev_dbg(dev, "%s   %s too many stuffing bytes %d\n",
+			pctx->name, __func__, stuffing_bytes);
+		return 0;
+	}
+
+	/* start code */
+	memcpy(addr + *size, start, sizeof(start));
+	*size += sizeof(start);
+
+	/* nal_unit_type */
+	addr[*size] = NALU_TYPE_FILLER_DATA;
+	*size += 1;
+
+	memset(addr + *size, 0xff, stuffing_bytes);
+	*size += stuffing_bytes;
+
+	addr[*size] = 0x80;
+	*size += 1;
+
+	return 0;
+}
+
+static int hva_h264_fill_sei_nal(struct hva_ctx *pctx,
+				 enum hva_h264_sei_payload_type type,
+				 u8 *addr, u32 *size)
+{
+	struct device *dev = ctx_to_dev(pctx);
+	const u8 start[] = { 0x00, 0x00, 0x00, 0x01 };
+	struct hva_h264_stereo_video_sei info;
+	u8 offset = 7;
+	u8 msg = 0;
+
+	/* start code */
+	memcpy(addr + *size, start, sizeof(start));
+	*size += sizeof(start);
+
+	/* nal_unit_type */
+	addr[*size] = NALU_TYPE_SEI;
+	*size += 1;
+
+	/* payload type */
+	addr[*size] = type;
+	*size += 1;
+
+	switch (type) {
+	case SEI_STEREO_VIDEO_INFO:
+		memset(&info, 0, sizeof(info));
+
+		/* set to top/bottom frame packing arrangement */
+		info.field_views_flag = 1;
+		info.top_field_is_left_view_flag = 1;
+
+		/* payload size */
+		addr[*size] = 1;
+		*size += 1;
+
+		/* payload */
+		msg = info.field_views_flag << offset--;
+
+		if (info.field_views_flag) {
+			msg |= info.top_field_is_left_view_flag <<
+			       offset--;
+		} else {
+			msg |= info.current_frame_is_left_view_flag <<
+			       offset--;
+			msg |= info.next_frame_is_second_view_flag <<
+			       offset--;
+		}
+		msg |= info.left_view_self_contained_flag << offset--;
+		msg |= info.right_view_self_contained_flag << offset--;
+
+		addr[*size] = msg;
+		*size += 1;
+
+		addr[*size] = 0x80;
+		*size += 1;
+
+		return 0;
+	case SEI_BUFFERING_PERIOD:
+	case SEI_PICTURE_TIMING:
+	case SEI_FRAME_PACKING_ARRANGEMENT:
+	default:
+		dev_err(dev, "%s   sei nal type not supported %d\n",
+			pctx->name, type);
+		return -EINVAL;
+	}
+}
+
+static int hva_h264_prepare_task(struct hva_ctx *pctx,
+				 struct hva_h264_task *task,
+				 struct hva_frame *frame,
+				 struct hva_stream *stream)
+{
+	struct hva_dev *hva = ctx_to_hdev(pctx);
+	struct device *dev = ctx_to_dev(pctx);
+	struct hva_h264_ctx *ctx = (struct hva_h264_ctx *)pctx->priv;
+	struct hva_buffer *seq_info = ctx->seq_info;
+	struct hva_buffer *fwd_ref_frame = ctx->ref_frame;
+	struct hva_buffer *loc_rec_frame = ctx->rec_frame;
+	struct hva_h264_td *td = &task->td;
+	struct hva_controls *ctrls = &pctx->ctrls;
+	struct v4l2_fract *time_per_frame = &pctx->ctrls.time_per_frame;
+	int cavlc =  V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC;
+	u32 frame_num = pctx->stream_num;
+	u32 addr_esram = hva->esram_addr;
+	enum v4l2_mpeg_video_h264_level level;
+	dma_addr_t paddr = 0;
+	u8 *slice_header_vaddr;
+	u32 frame_width = frame->info.aligned_width;
+	u32 frame_height = frame->info.aligned_height;
+	u32 max_cpb_buffer_size;
+	unsigned int payload = stream->bytesused;
+	u32 max_bitrate;
+
+	/* check width and height parameters */
+	if ((frame_width > max(H264_MAX_SIZE_W, H264_MAX_SIZE_H)) ||
+	    (frame_height > max(H264_MAX_SIZE_W, H264_MAX_SIZE_H))) {
+		dev_err(dev,
+			"%s   width(%d) or height(%d) exceeds limits (%dx%d)\n",
+			pctx->name, frame_width, frame_height,
+			H264_MAX_SIZE_W, H264_MAX_SIZE_H);
+		return -EINVAL;
+	}
+
+	level = ctrls->level;
+
+	memset(td, 0, sizeof(struct hva_h264_td));
+
+	td->frame_width = frame_width;
+	td->frame_height = frame_height;
+
+	/* set frame alignement */
+	td->window_width =  frame_width;
+	td->window_height = frame_height;
+	td->window_horizontal_offset = 0;
+	td->window_vertical_offset = 0;
+
+	td->first_picture_in_sequence = (!frame_num) ? 1 : 0;
+
+	/* pic_order_cnt_type hard coded to '2' as only I & P frames */
+	td->pic_order_cnt_type = 2;
+
+	/* useConstrainedIntraFlag set to false for better coding efficiency */
+	td->use_constrained_intra_flag = false;
+	td->brc_type = (ctrls->bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR)
+			? BRC_TYPE_CBR : BRC_TYPE_VBR;
+
+	td->entropy_coding_mode = (ctrls->entropy_mode == cavlc) ? CAVLC :
+				  CABAC;
+
+	/* convert bitrate in bits per seconds */
+	td->bit_rate = ctrls->bitrate * 1000;
+
+	/* set framerate, framerate = 1 n/ time per frame */
+	if (time_per_frame->numerator >= 536) {
+		/*
+		 * due to a hardware bug, framerate denominator can't exceed
+		 * 536 (BRC overflow). Compute nearest framerate
+		 */
+		td->framerate_den = 1;
+		td->framerate_num = (time_per_frame->denominator +
+				    (time_per_frame->numerator >> 1) - 1) /
+				    time_per_frame->numerator;
+
+		/*
+		 * update bitrate to introduce a correction due to
+		 * the new framerate
+		 * new bitrate = (old bitrate * new framerate) / old framerate
+		 */
+		td->bit_rate /= time_per_frame->numerator;
+		td->bit_rate *= time_per_frame->denominator;
+		td->bit_rate /= td->framerate_num;
+	} else {
+		td->framerate_den = time_per_frame->numerator;
+		td->framerate_num = time_per_frame->denominator;
+	}
+
+	/* compute maximum bitrate depending on profile */
+	if (ctrls->profile >= V4L2_MPEG_VIDEO_H264_PROFILE_HIGH)
+		max_bitrate = h264_infos_list[level].max_bitrate *
+			      H264_FACTOR_HIGH;
+	else
+		max_bitrate = h264_infos_list[level].max_bitrate *
+			      H264_FACTOR_BASELINE;
+
+	/* check if bitrate doesn't exceed max size */
+	if (td->bit_rate > max_bitrate) {
+		dev_dbg(dev,
+			"%s   bitrate (%d) larger than level and profile allow, clip to %d\n",
+			pctx->name, td->bit_rate, max_bitrate);
+		td->bit_rate = max_bitrate;
+	}
+
+	/* convert cpb_buffer_size in bits */
+	td->cpb_buffer_size = ctrls->cpb_size * 1000;
+
+	/* compute maximum cpb buffer size depending on profile */
+	if (ctrls->profile >= V4L2_MPEG_VIDEO_H264_PROFILE_HIGH)
+		max_cpb_buffer_size =
+		    h264_infos_list[level].max_cpb_size * H264_FACTOR_HIGH;
+	else
+		max_cpb_buffer_size =
+		    h264_infos_list[level].max_cpb_size * H264_FACTOR_BASELINE;
+
+	/* check if cpb buffer size doesn't exceed max size */
+	if (td->cpb_buffer_size > max_cpb_buffer_size) {
+		dev_dbg(dev,
+			"%s   cpb size larger than level %d allows, clip to %d\n",
+			pctx->name, td->cpb_buffer_size, max_cpb_buffer_size);
+		td->cpb_buffer_size = max_cpb_buffer_size;
+	}
+
+	/* enable skipping in the Bitrate Controller */
+	td->brc_no_skip = 0;
+
+	/* initial delay */
+	if ((ctrls->bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR) &&
+	    td->bit_rate)
+		td->delay = 1000 * (td->cpb_buffer_size / td->bit_rate);
+	else
+		td->delay = 0;
+
+	switch (frame->info.pixelformat) {
+	case V4L2_PIX_FMT_NV12:
+		td->sampling_mode = SAMPLING_MODE_NV12;
+		break;
+	case V4L2_PIX_FMT_NV21:
+		td->sampling_mode = SAMPLING_MODE_NV21;
+		break;
+	default:
+		dev_err(dev, "%s   invalid source pixel format\n",
+			pctx->name);
+		return -EINVAL;
+	}
+
+	/*
+	 * fill matrix color converter (RGB to YUV)
+	 * Y = 0,299 R + 0,587 G + 0,114 B
+	 * Cb = -0,1687 R -0,3313 G + 0,5 B + 128
+	 * Cr = 0,5 R - 0,4187 G - 0,0813 B + 128
+	 */
+	td->rgb2_yuv_y_coeff = 0x12031008;
+	td->rgb2_yuv_u_coeff = 0x800EF7FB;
+	td->rgb2_yuv_v_coeff = 0x80FEF40E;
+
+	/* enable/disable transform mode */
+	td->transform_mode = ctrls->dct8x8;
+
+	/* encoder complexity fix to 2, ENCODE_I_16x16_I_NxN_P_16x16_P_WxH */
+	td->encoder_complexity = 2;
+
+	/* quant fix to 28, default VBR value */
+	td->quant = 28;
+
+	if (td->framerate_den == 0) {
+		dev_err(dev, "%s   invalid framerate\n", pctx->name);
+		return -EINVAL;
+	}
+
+	/* if automatic framerate, deactivate bitrate controller */
+	if (td->framerate_num == 0)
+		td->brc_type = 0;
+
+	/* compliancy fix to true */
+	td->strict_hrd_compliancy = 1;
+
+	/* set minimum & maximum quantizers */
+	td->qp_min = clamp_val(ctrls->qpmin, 0, 51);
+	td->qp_max = clamp_val(ctrls->qpmax, 0, 51);
+
+	td->addr_source_buffer = frame->paddr;
+	td->addr_fwd_ref_buffer = fwd_ref_frame->paddr;
+	td->addr_rec_buffer = loc_rec_frame->paddr;
+
+	td->addr_output_bitstream_end = (u32)stream->paddr + stream->size;
+
+	td->addr_output_bitstream_start = (u32)stream->paddr;
+	td->bitstream_offset = (((u32)stream->paddr & 0xF) << 3) &
+			       BITSTREAM_OFFSET_MASK;
+
+	td->addr_param_out = (u32)ctx->task->paddr +
+			     offsetof(struct hva_h264_task, po);
+
+	/* swap spatial and temporal context */
+	if (frame_num % 2) {
+		paddr = seq_info->paddr;
+		td->addr_spatial_context =  ALIGN(paddr, 0x100);
+		paddr = seq_info->paddr + DATA_SIZE(frame_width,
+							frame_height);
+		td->addr_temporal_context = ALIGN(paddr, 0x100);
+	} else {
+		paddr = seq_info->paddr;
+		td->addr_temporal_context = ALIGN(paddr, 0x100);
+		paddr = seq_info->paddr + DATA_SIZE(frame_width,
+							frame_height);
+		td->addr_spatial_context =  ALIGN(paddr, 0x100);
+	}
+
+	paddr = seq_info->paddr + 2 * DATA_SIZE(frame_width, frame_height);
+
+	td->addr_brc_in_out_parameter =  ALIGN(paddr, 0x100);
+
+	paddr = td->addr_brc_in_out_parameter + BRC_DATA_SIZE;
+	td->addr_slice_header =  ALIGN(paddr, 0x100);
+	td->addr_external_sw =  ALIGN(addr_esram, 0x100);
+
+	addr_esram += SEARCH_WINDOW_BUFFER_MAX_SIZE(frame_width);
+	td->addr_local_rec_buffer = ALIGN(addr_esram, 0x100);
+
+	addr_esram += LOCAL_RECONSTRUCTED_BUFFER_MAX_SIZE(frame_width);
+	td->addr_lctx = ALIGN(addr_esram, 0x100);
+
+	addr_esram += CTX_MB_BUFFER_MAX_SIZE(max(frame_width, frame_height));
+	td->addr_cabac_context_buffer = ALIGN(addr_esram, 0x100);
+
+	if (!(frame_num % ctrls->gop_size)) {
+		td->picture_coding_type = PICTURE_CODING_TYPE_I;
+		stream->vbuf.flags |= V4L2_BUF_FLAG_KEYFRAME;
+	} else {
+		td->picture_coding_type = PICTURE_CODING_TYPE_P;
+		stream->vbuf.flags &= ~V4L2_BUF_FLAG_KEYFRAME;
+	}
+
+	/* fill the slice header part */
+	slice_header_vaddr = seq_info->vaddr + (td->addr_slice_header -
+			     seq_info->paddr);
+
+	hva_h264_fill_slice_header(pctx, slice_header_vaddr, ctrls, frame_num,
+				   &td->slice_header_size_in_bits,
+				   &td->slice_header_offset0,
+				   &td->slice_header_offset1,
+				   &td->slice_header_offset2);
+
+	td->chroma_qp_index_offset = 2;
+	td->slice_synchro_enable = 0;
+	td->max_slice_number = 1;
+
+	/*
+	 * check the sps/pps header size for key frame only
+	 * sps/pps header was previously fill by libv4l
+	 * during qbuf of stream buffer
+	 */
+	if ((stream->vbuf.flags == V4L2_BUF_FLAG_KEYFRAME) &&
+	    (payload > MAX_SPS_PPS_SIZE)) {
+		dev_err(dev, "%s   invalid sps/pps size %d\n", pctx->name,
+			payload);
+		return -EINVAL;
+	}
+
+	if (stream->vbuf.flags != V4L2_BUF_FLAG_KEYFRAME)
+		payload = 0;
+
+	/* add SEI nal (video stereo info) */
+	if (ctrls->sei_fp && hva_h264_fill_sei_nal(pctx, SEI_STEREO_VIDEO_INFO,
+						   (u8 *)stream->vaddr,
+						   &payload)) {
+		dev_err(dev, "%s   fail to get SEI nal\n", pctx->name);
+		return -EINVAL;
+	}
+
+	/* fill size of non-VCL NAL units (SPS, PPS, filler and SEI) */
+	td->non_vcl_nalu_size = payload * 8;
+
+	/* compute bitstream offset & new start address of bitstream */
+	td->addr_output_bitstream_start += ((payload >> 4) << 4);
+	td->bitstream_offset += (payload - ((payload >> 4) << 4)) * 8;
+
+	stream->bytesused = payload;
+
+	return 0;
+}
+
+static unsigned int hva_h264_get_stream_size(struct hva_h264_task *task)
+{
+	struct hva_h264_po *po = &task->po;
+
+	return po->bitstream_size;
+}
+
+static u32 hva_h264_get_stuffing_bytes(struct hva_h264_task *task)
+{
+	struct hva_h264_po *po = &task->po;
+
+	return po->stuffing_bits >> 3;
+}
+
+static int hva_h264_open(struct hva_ctx *pctx)
+{
+	struct device *dev = ctx_to_dev(pctx);
+	struct hva_h264_ctx *ctx;
+	struct hva_dev *hva = ctx_to_hdev(pctx);
+	u32 frame_width = pctx->frameinfo.aligned_width;
+	u32 frame_height = pctx->frameinfo.aligned_height;
+	u32 size;
+	int ret;
+
+	/* check esram size necessary to encode a frame */
+	size = SEARCH_WINDOW_BUFFER_MAX_SIZE(frame_width) +
+	       LOCAL_RECONSTRUCTED_BUFFER_MAX_SIZE(frame_width) +
+	       CTX_MB_BUFFER_MAX_SIZE(max(frame_width, frame_height)) +
+	       CABAC_CONTEXT_BUFFER_MAX_SIZE(frame_width);
+
+	if (hva->esram_size < size) {
+		dev_err(dev, "%s   not enough esram (max:%d request:%d)\n",
+			pctx->name, hva->esram_size, size);
+		ret = -EINVAL;
+		goto err;
+	}
+
+	/* allocate context for codec */
+	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+	if (!ctx) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	/* allocate sequence info buffer */
+	ret = hva_mem_alloc(pctx,
+			    2 * DATA_SIZE(frame_width, frame_height) +
+			    SLICE_HEADER_SIZE +
+			    BRC_DATA_SIZE,
+			    "hva sequence info",
+			    &ctx->seq_info);
+	if (ret) {
+		dev_err(dev,
+			"%s   failed to allocate sequence info buffer\n",
+			pctx->name);
+		goto err_ctx;
+	}
+
+	/* allocate reference frame buffer */
+	ret = hva_mem_alloc(pctx,
+			    frame_width * frame_height * 3 / 2,
+			    "hva reference frame",
+			    &ctx->ref_frame);
+	if (ret) {
+		dev_err(dev, "%s   failed to allocate reference frame buffer\n",
+			pctx->name);
+		goto err_seq_info;
+	}
+
+	/* allocate reconstructed frame buffer */
+	ret = hva_mem_alloc(pctx,
+			    frame_width * frame_height * 3 / 2,
+			    "hva reconstructed frame",
+			    &ctx->rec_frame);
+	if (ret) {
+		dev_err(dev,
+			"%s   failed to allocate reconstructed frame buffer\n",
+			pctx->name);
+		goto err_ref_frame;
+	}
+
+	/* allocate task descriptor */
+	ret = hva_mem_alloc(pctx,
+			    sizeof(struct hva_h264_task),
+			    "hva task descriptor",
+			    &ctx->task);
+	if (ret) {
+		dev_err(dev,
+			"%s   failed to allocate task descriptor\n",
+			pctx->name);
+		goto err_rec_frame;
+	}
+
+	pctx->priv = (void *)ctx;
+
+	return 0;
+
+err_rec_frame:
+	hva_mem_free(pctx, ctx->rec_frame);
+err_ref_frame:
+	hva_mem_free(pctx, ctx->ref_frame);
+err_seq_info:
+	hva_mem_free(pctx, ctx->seq_info);
+err_ctx:
+	devm_kfree(dev, ctx);
+err:
+	return ret;
+}
+
+static int hva_h264_close(struct hva_ctx *pctx)
+{
+	struct hva_h264_ctx *ctx = (struct hva_h264_ctx *)pctx->priv;
+	struct device *dev = ctx_to_dev(pctx);
+
+	if (ctx->seq_info)
+		hva_mem_free(pctx, ctx->seq_info);
+
+	if (ctx->ref_frame)
+		hva_mem_free(pctx, ctx->ref_frame);
+
+	if (ctx->rec_frame)
+		hva_mem_free(pctx, ctx->rec_frame);
+
+	if (ctx->task)
+		hva_mem_free(pctx, ctx->task);
+
+	devm_kfree(dev, ctx);
+
+	return 0;
+}
+
+static int hva_h264_encode(struct hva_ctx *pctx, struct hva_frame *frame,
+			   struct hva_stream *stream)
+{
+	struct hva_h264_ctx *ctx = (struct hva_h264_ctx *)pctx->priv;
+	struct hva_h264_task *task = (struct hva_h264_task *)ctx->task->vaddr;
+	struct hva_buffer *tmp_frame;
+	u32 stuffing_bytes = 0;
+	int ret = 0;
+
+	ret = hva_h264_prepare_task(pctx, task, frame, stream);
+	if (ret)
+		goto err;
+
+	ret = hva_hw_execute_task(pctx, H264_ENC, ctx->task);
+	if (ret)
+		goto err;
+
+	pctx->stream_num++;
+	stream->bytesused += hva_h264_get_stream_size(task);
+
+	stuffing_bytes = hva_h264_get_stuffing_bytes(task);
+
+	if (stuffing_bytes)
+		hva_h264_fill_data_nal(pctx, stuffing_bytes,
+				       (u8 *)stream->vaddr,
+				       stream->size,
+				       &stream->bytesused);
+
+	vb2_set_plane_payload(&stream->vbuf.vb2_buf, 0, stream->bytesused);
+
+	/* switch reference & reconstructed frame */
+	tmp_frame = ctx->ref_frame;
+	ctx->ref_frame = ctx->rec_frame;
+	ctx->rec_frame = tmp_frame;
+
+	return 0;
+err:
+	vb2_set_plane_payload(&stream->vbuf.vb2_buf, 0, 0);
+	return ret;
+}
+
+const struct hva_enc nv12h264enc = {
+	.name = "H264(NV12)",
+	.pixelformat = V4L2_PIX_FMT_NV12,
+	.streamformat = V4L2_PIX_FMT_H264,
+	.max_width = H264_MAX_SIZE_W,
+	.max_height = H264_MAX_SIZE_H,
+	.open = hva_h264_open,
+	.close = hva_h264_close,
+	.encode = hva_h264_encode,
+};
+
+const struct hva_enc nv21h264enc = {
+	.name = "H264(NV21)",
+	.pixelformat = V4L2_PIX_FMT_NV21,
+	.streamformat = V4L2_PIX_FMT_H264,
+	.max_width = H264_MAX_SIZE_W,
+	.max_height = H264_MAX_SIZE_H,
+	.open = hva_h264_open,
+	.close = hva_h264_close,
+	.encode = hva_h264_encode,
+};
diff --git a/drivers/media/platform/sti/hva/hva-v4l2.c b/drivers/media/platform/sti/hva/hva-v4l2.c
index bacc9ff..a589268 100644
--- a/drivers/media/platform/sti/hva/hva-v4l2.c
+++ b/drivers/media/platform/sti/hva/hva-v4l2.c
@@ -41,6 +41,8 @@
 
 /* registry of available encoders */
 const struct hva_enc *hva_encoders[] = {
+	&nv12h264enc,
+	&nv21h264enc,
 };
 
 static inline int frame_size(u32 w, u32 h, u32 fmt)
@@ -612,6 +614,47 @@ static int hva_s_ctrl(struct v4l2_ctrl *ctrl)
 	case V4L2_CID_MPEG_VIDEO_ASPECT:
 		ctx->ctrls.aspect = ctrl->val;
 		break;
+	case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
+		ctx->ctrls.profile = ctrl->val;
+		if (ctx->flags & HVA_FLAG_STREAMINFO)
+			snprintf(ctx->streaminfo.profile,
+				 sizeof(ctx->streaminfo.profile),
+				 "%s", profile_str(ctx->ctrls.profile));
+		break;
+	case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
+		ctx->ctrls.level = ctrl->val;
+		if (ctx->flags & HVA_FLAG_STREAMINFO)
+			snprintf(ctx->streaminfo.level,
+				 sizeof(ctx->streaminfo.level),
+				 "%s", level_str(ctx->ctrls.level));
+		break;
+	case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE:
+		ctx->ctrls.entropy_mode = ctrl->val;
+		break;
+	case V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE:
+		ctx->ctrls.cpb_size = ctrl->val;
+		break;
+	case V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM:
+		ctx->ctrls.dct8x8 = ctrl->val;
+		break;
+	case V4L2_CID_MPEG_VIDEO_H264_MIN_QP:
+		ctx->ctrls.qpmin = ctrl->val;
+		break;
+	case V4L2_CID_MPEG_VIDEO_H264_MAX_QP:
+		ctx->ctrls.qpmax = ctrl->val;
+		break;
+	case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE:
+		ctx->ctrls.vui_sar = ctrl->val;
+		break;
+	case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC:
+		ctx->ctrls.vui_sar_idc = ctrl->val;
+		break;
+	case V4L2_CID_MPEG_VIDEO_H264_SEI_FRAME_PACKING:
+		ctx->ctrls.sei_fp = ctrl->val;
+		break;
+	case V4L2_CID_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE:
+		ctx->ctrls.sei_fp_type = ctrl->val;
+		break;
 	default:
 		dev_dbg(dev, "%s S_CTRL: invalid control (id = %d)\n",
 			ctx->name, ctrl->id);
@@ -630,8 +673,10 @@ static int hva_ctrls_setup(struct hva_ctx *ctx)
 {
 	struct device *dev = ctx_to_dev(ctx);
 	u64 mask;
+	enum v4l2_mpeg_video_h264_sei_fp_arrangement_type sei_fp_type =
+		V4L2_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE_TOP_BOTTOM;
 
-	v4l2_ctrl_handler_init(&ctx->ctrl_handler, 4);
+	v4l2_ctrl_handler_init(&ctx->ctrl_handler, 15);
 
 	v4l2_ctrl_new_std_menu(&ctx->ctrl_handler, &hva_ctrl_ops,
 			       V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
@@ -654,6 +699,66 @@ static int hva_ctrls_setup(struct hva_ctx *ctx)
 			       mask,
 			       V4L2_MPEG_VIDEO_ASPECT_1x1);
 
+	mask = ~((1 << V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE) |
+		 (1 << V4L2_MPEG_VIDEO_H264_PROFILE_MAIN) |
+		 (1 << V4L2_MPEG_VIDEO_H264_PROFILE_HIGH) |
+		 (1 << V4L2_MPEG_VIDEO_H264_PROFILE_STEREO_HIGH));
+	v4l2_ctrl_new_std_menu(&ctx->ctrl_handler, &hva_ctrl_ops,
+			       V4L2_CID_MPEG_VIDEO_H264_PROFILE,
+			       V4L2_MPEG_VIDEO_H264_PROFILE_STEREO_HIGH,
+			       mask,
+			       V4L2_MPEG_VIDEO_H264_PROFILE_HIGH);
+
+	v4l2_ctrl_new_std_menu(&ctx->ctrl_handler, &hva_ctrl_ops,
+			       V4L2_CID_MPEG_VIDEO_H264_LEVEL,
+			       V4L2_MPEG_VIDEO_H264_LEVEL_4_2,
+			       0,
+			       V4L2_MPEG_VIDEO_H264_LEVEL_4_0);
+
+	v4l2_ctrl_new_std_menu(&ctx->ctrl_handler, &hva_ctrl_ops,
+			       V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE,
+			       V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC,
+			       0,
+			       V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC);
+
+	v4l2_ctrl_new_std(&ctx->ctrl_handler, &hva_ctrl_ops,
+			  V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE,
+			  1, 62500, 1, 25000);
+
+	v4l2_ctrl_new_std(&ctx->ctrl_handler, &hva_ctrl_ops,
+			  V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM,
+			  0, 1, 1, 0);
+
+	v4l2_ctrl_new_std(&ctx->ctrl_handler, &hva_ctrl_ops,
+			  V4L2_CID_MPEG_VIDEO_H264_MIN_QP,
+			  0, 51, 1, 5);
+
+	v4l2_ctrl_new_std(&ctx->ctrl_handler, &hva_ctrl_ops,
+			  V4L2_CID_MPEG_VIDEO_H264_MAX_QP,
+			  0, 51, 1, 51);
+
+	v4l2_ctrl_new_std(&ctx->ctrl_handler, &hva_ctrl_ops,
+			  V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE,
+			  0, 1, 1, 1);
+
+	mask = ~(1 << V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_1x1);
+	v4l2_ctrl_new_std_menu(&ctx->ctrl_handler, &hva_ctrl_ops,
+			       V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC,
+			       V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_1x1,
+			       mask,
+			       V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_1x1);
+
+	v4l2_ctrl_new_std(&ctx->ctrl_handler, &hva_ctrl_ops,
+			  V4L2_CID_MPEG_VIDEO_H264_SEI_FRAME_PACKING,
+			  0, 1, 1, 0);
+
+	mask = ~(1 << sei_fp_type);
+	v4l2_ctrl_new_std_menu(&ctx->ctrl_handler, &hva_ctrl_ops,
+			       V4L2_CID_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE,
+			       sei_fp_type,
+			       mask,
+			       sei_fp_type);
+
 	if (ctx->ctrl_handler.error) {
 		int err = ctx->ctrl_handler.error;
 
diff --git a/drivers/media/platform/sti/hva/hva.h b/drivers/media/platform/sti/hva/hva.h
index 9a1b503b..a81f313 100644
--- a/drivers/media/platform/sti/hva/hva.h
+++ b/drivers/media/platform/sti/hva/hva.h
@@ -23,6 +23,9 @@
 
 #define HVA_PREFIX "[---:----]"
 
+extern const struct hva_enc nv12h264enc;
+extern const struct hva_enc nv21h264enc;
+
 /**
  * struct hva_frameinfo - information about hva frame
  *
@@ -67,13 +70,35 @@ struct hva_streaminfo {
  * @gop_size:       groupe of picture size
  * @bitrate:        bitrate (in kbps)
  * @aspect:         video aspect
+ * @profile:        H.264 profile
+ * @level:          H.264 level
+ * @entropy_mode:   H.264 entropy mode (CABAC or CVLC)
+ * @cpb_size:       coded picture buffer size (in kbps)
+ * @dct8x8:         transform mode 8x8 enable
+ * @qpmin:          minimum quantizer
+ * @qpmax:          maximum quantizer
+ * @vui_sar:        pixel aspect ratio enable
+ * @vui_sar_idc:    pixel aspect ratio identifier
+ * @sei_fp:         sei frame packing arrangement enable
+ * @sei_fp_type:    sei frame packing arrangement type
  */
 struct hva_controls {
-	struct v4l2_fract			time_per_frame;
-	enum v4l2_mpeg_video_bitrate_mode	bitrate_mode;
-	u32					gop_size;
-	u32					bitrate;
-	enum v4l2_mpeg_video_aspect		aspect;
+	struct v4l2_fract					time_per_frame;
+	enum v4l2_mpeg_video_bitrate_mode			bitrate_mode;
+	u32							gop_size;
+	u32							bitrate;
+	enum v4l2_mpeg_video_aspect				aspect;
+	enum v4l2_mpeg_video_h264_profile			profile;
+	enum v4l2_mpeg_video_h264_level				level;
+	enum v4l2_mpeg_video_h264_entropy_mode			entropy_mode;
+	u32							cpb_size;
+	bool							dct8x8;
+	u32							qpmin;
+	u32							qpmax;
+	bool							vui_sar;
+	enum v4l2_mpeg_video_h264_vui_sar_idc			vui_sar_idc;
+	bool							sei_fp;
+	enum v4l2_mpeg_video_h264_sei_fp_arrangement_type	sei_fp_type;
 };
 
 /**
@@ -281,4 +306,84 @@ struct hva_enc {
 				  struct hva_stream *stream);
 };
 
+static inline const char *profile_str(unsigned int p)
+{
+	switch (p) {
+	case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE:
+		return "baseline profile";
+	case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN:
+		return "main profile";
+	case V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED:
+		return "extended profile";
+	case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH:
+		return "high profile";
+	case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_10:
+		return "high 10 profile";
+	case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422:
+		return "high 422 profile";
+	case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_PREDICTIVE:
+		return "high 444 predictive profile";
+	case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_10_INTRA:
+		return "high 10 intra profile";
+	case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422_INTRA:
+		return "high 422 intra profile";
+	case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_INTRA:
+		return "high 444 intra profile";
+	case V4L2_MPEG_VIDEO_H264_PROFILE_CAVLC_444_INTRA:
+		return "calvc 444 intra profile";
+	case V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_BASELINE:
+		return "scalable baseline profile";
+	case V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_HIGH:
+		return "scalable high profile";
+	case V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_HIGH_INTRA:
+		return "scalable high intra profile";
+	case V4L2_MPEG_VIDEO_H264_PROFILE_STEREO_HIGH:
+		return "stereo high profile";
+	case V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH:
+		return "multiview high profile";
+	default:
+		return "unknown profile";
+	}
+}
+
+static inline const char *level_str(enum v4l2_mpeg_video_h264_level l)
+{
+	switch (l) {
+	case V4L2_MPEG_VIDEO_H264_LEVEL_1_0:
+		return "level 1.0";
+	case V4L2_MPEG_VIDEO_H264_LEVEL_1B:
+		return "level 1b";
+	case V4L2_MPEG_VIDEO_H264_LEVEL_1_1:
+		return "level 1.1";
+	case V4L2_MPEG_VIDEO_H264_LEVEL_1_2:
+		return "level 1.2";
+	case V4L2_MPEG_VIDEO_H264_LEVEL_1_3:
+		return "level 1.3";
+	case V4L2_MPEG_VIDEO_H264_LEVEL_2_0:
+		return "level 2.0";
+	case V4L2_MPEG_VIDEO_H264_LEVEL_2_1:
+		return "level 2.1";
+	case V4L2_MPEG_VIDEO_H264_LEVEL_2_2:
+		return "level 2.2";
+	case V4L2_MPEG_VIDEO_H264_LEVEL_3_0:
+		return "level 3.0";
+	case V4L2_MPEG_VIDEO_H264_LEVEL_3_1:
+		return "level 3.1";
+	case V4L2_MPEG_VIDEO_H264_LEVEL_3_2:
+		return "level 3.2";
+	case V4L2_MPEG_VIDEO_H264_LEVEL_4_0:
+		return "level 4.0";
+	case V4L2_MPEG_VIDEO_H264_LEVEL_4_1:
+		return "level 4.1";
+	case V4L2_MPEG_VIDEO_H264_LEVEL_4_2:
+		return "level 4.2";
+	case V4L2_MPEG_VIDEO_H264_LEVEL_5_0:
+		return "level 5.0";
+	case V4L2_MPEG_VIDEO_H264_LEVEL_5_1:
+		return "level 5.1";
+	default:
+		return "unknown level";
+	}
+}
+
 #endif /* HVA_H */
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* Re: [PATCH v2 0/3] support of v4l2 encoder for STMicroelectronics SOC
  2016-07-11 15:14 [PATCH v2 0/3] support of v4l2 encoder for STMicroelectronics SOC Jean-Christophe Trotin
                   ` (2 preceding siblings ...)
  2016-07-11 15:14 ` [PATCH v2 3/3] [media] hva: add H.264 video encoding support Jean-Christophe Trotin
@ 2016-07-11 17:48 ` Nicolas Dufresne
  2016-07-11 18:57   ` Javier Martinez Canillas
  3 siblings, 1 reply; 18+ messages in thread
From: Nicolas Dufresne @ 2016-07-11 17:48 UTC (permalink / raw)
  To: Jean-Christophe Trotin, linux-media, Hans Verkuil
  Cc: kernel, Benjamin Gaignard, Yannick Fertre, Hugues Fruchet

Le lundi 11 juillet 2016 à 17:14 +0200, Jean-Christophe Trotin a
écrit :
> version 2:
> - List of pixel formats supported by the encoder reduced to NV12 and
> NV21
> - x86_64 compilation warnings corrected
> - V4L2 compliance successfully passed with this version (see report
> below)
> - All remarks about version 1 of hva-v4l2.c taken into account:
>         - V4L2 mem2mem framework used 
> 	- V4L2 control framework used
> 	- allocator context initialized in the probe and cleaned up in
> the remove
> 	- start_streaming and stop_streaming ops added
> 	- colorspace, bytesperline and sizeimage fields initialized in
> TRY_FMT
> 	- better estimation of sizeimage for compressed formats
> 	- checks and debugging logs already covered by vb2 removed
> 	- some dev_err changed in dev_dbg
> 	- typos corrected
> 
> version 1:
> - Initial submission.
> 
> Only one feature supported and tested:
> - encode (NV12, NV21) to H.264 video format
> 
> The driver is mainly implemented across three files:
> - hva-v4l2.c
> - hva-h264.c
> - hva-hw.c
> hva-v4l2.c manages the V4L2 interface with the userland.
> It calls the HW services that are implemented in hva-hw.c.
> hva-h264.c manages specific part of H.264 codec.
> 
> Below is the v4l2-compliance report for the version 2 of the sti hva
> driver:
> 
> 
> root@sti-next:/home/video_test# v4l2-compliance -d /dev/video0
> Driver Info:
> 	Driver name   : 8c85000.hva

I think it would be nice to set a driver name that means something.

> 	Card type     : 8c85000.hva
> 	Bus info      : platform:hva
> 	Driver version: 4.7.0
> 	Capabilities  : 0x84208000
> 		Video Memory-to-Memory
> 		Streaming
> 		Extended Pix Format
> 		Device Capabilities
> 	Device Caps   : 0x04208000
> 		Video Memory-to-Memory
> 		Streaming
> 		Extended Pix Format
> 
> Compliance test for device /dev/video0 (not using libv4l2):
> 
> Required ioctls:
> 	test VIDIOC_QUERYCAP: OK
> 
> Allow for multiple opens:
> 	test second video open: OK
> 	test VIDIOC_QUERYCAP: OK
> 	test VIDIOC_G/S_PRIORITY: 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
> 		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: 16 Private Controls: 0
> 
> 	Format ioctls:
> 		test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
> 		test VIDIOC_G/S_PARM: OK
> 		test VIDIOC_G_FBUF: OK (Not Supported)
> 		test VIDIOC_G_FMT: OK
> 		warn: /local/home/frq08988/views/opensdk-
> 2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-
> formats.cpp(716): TRY_FMT cannot handle an invalid pixelformat.
> 		warn: /local/home/frq08988/views/opensdk-
> 2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-
> formats.cpp(717): This may or may not be a problem. For more
> information see:
> 		warn: /local/home/frq08988/views/opensdk-
> 2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-
> formats.cpp(718): http://www.mail-archive.com/linux-media@vger.kernel
> .org/msg56550.html
> 		warn: /local/home/frq08988/views/opensdk-
> 2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-
> formats.cpp(716): TRY_FMT cannot handle an invalid pixelformat.
> 		warn: /local/home/frq08988/views/opensdk-
> 2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-
> formats.cpp(717): This may or may not be a problem. For more
> information see:
> 		warn: /local/home/frq08988/views/opensdk-
> 2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-
> formats.cpp(718): http://www.mail-archive.com/linux-media@vger.kernel
> .org/msg56550.html
> 		test VIDIOC_TRY_FMT: OK
> 		warn: /local/home/frq08988/views/opensdk-
> 2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-
> formats.cpp(937): S_FMT cannot handle an invalid pixelformat.
> 		warn: /local/home/frq08988/views/opensdk-
> 2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-
> formats.cpp(938): This may or may not be a problem. For more
> information see:
> 		warn: /local/home/frq08988/views/opensdk-
> 2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-
> formats.cpp(939): http://www.mail-archive.com/linux-media@vger.kernel
> .org/msg56550.html
> 		warn: /local/home/frq08988/views/opensdk-
> 2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-
> formats.cpp(937): S_FMT cannot handle an invalid pixelformat.
> 		warn: /local/home/frq08988/views/opensdk-
> 2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-
> formats.cpp(938): This may or may not be a problem. For more
> information see:
> 		warn: /local/home/frq08988/views/opensdk-
> 2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-
> formats.cpp(939): http://www.mail-archive.com/linux-media@vger.kernel
> .org/msg56550.html
> 		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:
> 		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 input 0:
> 
> 
> Total: 42, Succeeded: 42, Failed: 0, Warnings: 12
> 
> root@sti-next:/home/video_test# v4l2-compliance -f -d /dev/video0
> Driver Info:
> 	Driver name   : 8c85000.hva
> 	Card type     : 8c85000.hva
> 	Bus info      : platform:hva
> 	Driver version: 4.7.0
> 	Capabilities  : 0x84208000
> 		Video Memory-to-Memory
> 		Streaming
> 		Extended Pix Format
> 		Device Capabilities
> 	Device Caps   : 0x04208000
> 		Video Memory-to-Memory
> 		Streaming
> 		Extended Pix Format
> 
> Compliance test for device /dev/video0 (not using libv4l2):
> 
> Required ioctls:
> 	test VIDIOC_QUERYCAP: OK
> 
> Allow for multiple opens:
> 	test second video open: OK
> 	test VIDIOC_QUERYCAP: OK
> 	test VIDIOC_G/S_PRIORITY: 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
> 		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: 16 Private Controls: 0
> 
> 	Format ioctls:
> 		test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
> 		test VIDIOC_G/S_PARM: OK
> 		test VIDIOC_G_FBUF: OK (Not Supported)
> 		test VIDIOC_G_FMT: OK
> 		warn: /local/home/frq08988/views/opensdk-
> 2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-
> formats.cpp(716): TRY_FMT cannot handle an invalid pixelformat.
> 		warn: /local/home/frq08988/views/opensdk-
> 2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-
> formats.cpp(717): This may or may not be a problem. For more
> information see:
> 		warn: /local/home/frq08988/views/opensdk-
> 2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-
> formats.cpp(718): http://www.mail-archive.com/linux-media@vger.kernel
> .org/msg56550.html
> 		warn: /local/home/frq08988/views/opensdk-
> 2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-
> formats.cpp(716): TRY_FMT cannot handle an invalid pixelformat.
> 		warn: /local/home/frq08988/views/opensdk-
> 2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-
> formats.cpp(717): This may or may not be a problem. For more
> information see:
> 		warn: /local/home/frq08988/views/opensdk-
> 2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-
> formats.cpp(718): http://www.mail-archive.com/linux-media@vger.kernel
> .org/msg56550.html
> 		test VIDIOC_TRY_FMT: OK
> 		warn: /local/home/frq08988/views/opensdk-
> 2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-
> formats.cpp(937): S_FMT cannot handle an invalid pixelformat.
> 		warn: /local/home/frq08988/views/opensdk-
> 2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-
> formats.cpp(938): This may or may not be a problem. For more
> information see:
> 		warn: /local/home/frq08988/views/opensdk-
> 2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-
> formats.cpp(939): http://www.mail-archive.com/linux-media@vger.kernel
> .org/msg56550.html
> 		warn: /local/home/frq08988/views/opensdk-
> 2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-
> formats.cpp(937): S_FMT cannot handle an invalid pixelformat.
> 		warn: /local/home/frq08988/views/opensdk-
> 2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-
> formats.cpp(938): This may or may not be a problem. For more
> information see:
> 		warn: /local/home/frq08988/views/opensdk-
> 2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-
> formats.cpp(939): http://www.mail-archive.com/linux-media@vger.kernel
> .org/msg56550.html
> 		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:
> 		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 input 0:
> 
> Stream using all formats:
> 	Not supported for M2M devices
> 
> Total: 42, Succeeded: 42, Failed: 0, Warnings: 12
> 
> root@sti-next:/home/video_test# v4l2-compliance -a -d /dev/video0
> Driver Info:
> 	Driver name   : 8c85000.hva
> 	Card type     : 8c85000.hva
> 	Bus info      : platform:hva
> 	Driver version: 4.7.0
> 	Capabilities  : 0x84208000
> 		Video Memory-to-Memory
> 		Streaming
> 		Extended Pix Format
> 		Device Capabilities
> 	Device Caps   : 0x04208000
> 		Video Memory-to-Memory
> 		Streaming
> 		Extended Pix Format
> 
> Compliance test for device /dev/video0 (not using libv4l2):
> 
> Required ioctls:
> 	test VIDIOC_QUERYCAP: OK
> 
> Allow for multiple opens:
> 	test second video open: OK
> 	test VIDIOC_QUERYCAP: OK
> 	test VIDIOC_G/S_PRIORITY: 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
> 		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: 16 Private Controls: 0
> 
> 	Format ioctls:
> 		test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
> 		test VIDIOC_G/S_PARM: OK
> 		test VIDIOC_G_FBUF: OK (Not Supported)
> 		test VIDIOC_G_FMT: OK
> 		warn: /local/home/frq08988/views/opensdk-
> 2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-
> formats.cpp(716): TRY_FMT cannot handle an invalid pixelformat.
> 		warn: /local/home/frq08988/views/opensdk-
> 2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-
> formats.cpp(717): This may or may not be a problem. For more
> information see:
> 		warn: /local/home/frq08988/views/opensdk-
> 2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-
> formats.cpp(718): http://www.mail-archive.com/linux-media@vger.kernel
> .org/msg56550.html
> 		warn: /local/home/frq08988/views/opensdk-
> 2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-
> formats.cpp(716): TRY_FMT cannot handle an invalid pixelformat.
> 		warn: /local/home/frq08988/views/opensdk-
> 2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-
> formats.cpp(717): This may or may not be a problem. For more
> information see:
> 		warn: /local/home/frq08988/views/opensdk-
> 2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-
> formats.cpp(718): http://www.mail-archive.com/linux-media@vger.kernel
> .org/msg56550.html
> 		test VIDIOC_TRY_FMT: OK
> 		warn: /local/home/frq08988/views/opensdk-
> 2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-
> formats.cpp(937): S_FMT cannot handle an invalid pixelformat.
> 		warn: /local/home/frq08988/views/opensdk-
> 2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-
> formats.cpp(938): This may or may not be a problem. For more
> information see:
> 		warn: /local/home/frq08988/views/opensdk-
> 2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-
> formats.cpp(939): http://www.mail-archive.com/linux-media@vger.kernel
> .org/msg56550.html
> 		warn: /local/home/frq08988/views/opensdk-
> 2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-
> formats.cpp(937): S_FMT cannot handle an invalid pixelformat.
> 		warn: /local/home/frq08988/views/opensdk-
> 2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-
> formats.cpp(938): This may or may not be a problem. For more
> information see:
> 		warn: /local/home/frq08988/views/opensdk-
> 2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-
> formats.cpp(939): http://www.mail-archive.com/linux-media@vger.kernel
> .org/msg56550.html
> 		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:
> 		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 input 0:
> 
> 
> Total: 42, Succeeded: 42, Failed: 0, Warnings: 12
> 
> root@sti-next:/home/video_test# v4l2-compliance -s -d /dev/video0
> Driver Info:
> 	Driver name   : 8c85000.hva
> 	Card type     : 8c85000.hva
> 	Bus info      : platform:hva
> 	Driver version: 4.7.0
> 	Capabilities  : 0x84208000
> 		Video Memory-to-Memory
> 		Streaming
> 		Extended Pix Format
> 		Device Capabilities
> 	Device Caps   : 0x04208000
> 		Video Memory-to-Memory
> 		Streaming
> 		Extended Pix Format
> 
> Compliance test for device /dev/video0 (not using libv4l2):
> 
> Required ioctls:
> 	test VIDIOC_QUERYCAP: OK
> 
> Allow for multiple opens:
> 	test second video open: OK
> 	test VIDIOC_QUERYCAP: OK
> 	test VIDIOC_G/S_PRIORITY: 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
> 		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: 16 Private Controls: 0
> 
> 	Format ioctls:
> 		test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
> 		test VIDIOC_G/S_PARM: OK
> 		test VIDIOC_G_FBUF: OK (Not Supported)
> 		test VIDIOC_G_FMT: OK
> 		warn: /local/home/frq08988/views/opensdk-
> 2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-
> formats.cpp(716): TRY_FMT cannot handle an invalid pixelformat.
> 		warn: /local/home/frq08988/views/opensdk-
> 2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-
> formats.cpp(717): This may or may not be a problem. For more
> information see:
> 		warn: /local/home/frq08988/views/opensdk-
> 2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-
> formats.cpp(718): http://www.mail-archive.com/linux-media@vger.kernel
> .org/msg56550.html
> 		warn: /local/home/frq08988/views/opensdk-
> 2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-
> formats.cpp(716): TRY_FMT cannot handle an invalid pixelformat.
> 		warn: /local/home/frq08988/views/opensdk-
> 2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-
> formats.cpp(717): This may or may not be a problem. For more
> information see:
> 		warn: /local/home/frq08988/views/opensdk-
> 2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-
> formats.cpp(718): http://www.mail-archive.com/linux-media@vger.kernel
> .org/msg56550.html
> 		test VIDIOC_TRY_FMT: OK
> 		warn: /local/home/frq08988/views/opensdk-
> 2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-
> formats.cpp(937): S_FMT cannot handle an invalid pixelformat.
> 		warn: /local/home/frq08988/views/opensdk-
> 2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-
> formats.cpp(938): This may or may not be a problem. For more
> information see:
> 		warn: /local/home/frq08988/views/opensdk-
> 2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-
> formats.cpp(939): http://www.mail-archive.com/linux-media@vger.kernel
> .org/msg56550.html
> 		warn: /local/home/frq08988/views/opensdk-
> 2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-
> formats.cpp(937): S_FMT cannot handle an invalid pixelformat.
> 		warn: /local/home/frq08988/views/opensdk-
> 2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-
> formats.cpp(938): This may or may not be a problem. For more
> information see:
> 		warn: /local/home/frq08988/views/opensdk-
> 2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-
> formats.cpp(939): http://www.mail-archive.com/linux-media@vger.kernel
> .org/msg56550.html
> 		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:
> 		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 input 0:
> 
> Streaming ioctls:
> 	test read/write: OK (Not Supported)
> 	test MMAP: OK                                     
> 	test USERPTR: OK (Not Supported)
> 	test DMABUF: Cannot test, specify --expbuf-device
> 
> 
> Total: 45, Succeeded: 45, Failed: 0, Warnings: 12
> 
> 
> Jean-Christophe Trotin (3):
>   Documentation: DT: add bindings for STI HVA
>   [media] hva: multi-format video encoder V4L2 driver
>   [media] hva: add H.264 video encoding support
> 
>  .../devicetree/bindings/media/st,sti-hva.txt       |   24 +
>  drivers/media/platform/Kconfig                     |   14 +
>  drivers/media/platform/Makefile                    |    1 +
>  drivers/media/platform/sti/hva/Makefile            |    2 +
>  drivers/media/platform/sti/hva/hva-h264.c          | 1053
> +++++++++++++++
>  drivers/media/platform/sti/hva/hva-hw.c            |  534 ++++++++
>  drivers/media/platform/sti/hva/hva-hw.h            |   42 +
>  drivers/media/platform/sti/hva/hva-mem.c           |   60 +
>  drivers/media/platform/sti/hva/hva-mem.h           |   36 +
>  drivers/media/platform/sti/hva/hva-v4l2.c          | 1404
> ++++++++++++++++++++
>  drivers/media/platform/sti/hva/hva.h               |  389 ++++++
>  11 files changed, 3559 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/media/st,sti-
> hva.txt
>  create mode 100644 drivers/media/platform/sti/hva/Makefile
>  create mode 100644 drivers/media/platform/sti/hva/hva-h264.c
>  create mode 100644 drivers/media/platform/sti/hva/hva-hw.c
>  create mode 100644 drivers/media/platform/sti/hva/hva-hw.h
>  create mode 100644 drivers/media/platform/sti/hva/hva-mem.c
>  create mode 100644 drivers/media/platform/sti/hva/hva-mem.h
>  create mode 100644 drivers/media/platform/sti/hva/hva-v4l2.c
>  create mode 100644 drivers/media/platform/sti/hva/hva.h
> 

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v2 2/3] [media] hva: multi-format video encoder V4L2 driver
  2016-07-11 15:14 ` [PATCH v2 2/3] [media] hva: multi-format video encoder V4L2 driver Jean-Christophe Trotin
@ 2016-07-11 18:00   ` Nicolas Dufresne
  2016-07-13 14:11     ` Jean Christophe TROTIN
  2016-07-18 11:45   ` Hans Verkuil
  1 sibling, 1 reply; 18+ messages in thread
From: Nicolas Dufresne @ 2016-07-11 18:00 UTC (permalink / raw)
  To: Jean-Christophe Trotin, linux-media, Hans Verkuil
  Cc: kernel, Benjamin Gaignard, Yannick Fertre, Hugues Fruchet

[-- Attachment #1: Type: text/plain, Size: 74728 bytes --]

Le lundi 11 juillet 2016 à 17:14 +0200, Jean-Christophe Trotin a écrit :
> This patch adds V4L2 HVA (Hardware Video Accelerator) video encoder
> driver for STMicroelectronics SoC. It uses the V4L2 mem2mem framework.
> 
> This patch only contains the core parts of the driver:
> - the V4L2 interface with the userland (hva-v4l2.c)
> - the hardware services (hva-hw.c)
> - the memory management utilities (hva-mem.c)
> 
> This patch doesn't include the support of specific codec (e.g. H.264)
> video encoding: this support is part of subsequent patches.
> 
> Signed-off-by: Yannick Fertre <yannick.fertre@st.com>
> Signed-off-by: Jean-Christophe Trotin <jean-christophe.trotin@st.com>
> ---
>  drivers/media/platform/Kconfig            |   14 +
>  drivers/media/platform/Makefile           |    1 +
>  drivers/media/platform/sti/hva/Makefile   |    2 +
>  drivers/media/platform/sti/hva/hva-hw.c   |  534 ++++++++++++
>  drivers/media/platform/sti/hva/hva-hw.h   |   42 +
>  drivers/media/platform/sti/hva/hva-mem.c  |   60 ++
>  drivers/media/platform/sti/hva/hva-mem.h  |   36 +
>  drivers/media/platform/sti/hva/hva-v4l2.c | 1299 +++++++++++++++++++++++++++++
>  drivers/media/platform/sti/hva/hva.h      |  284 +++++++
>  9 files changed, 2272 insertions(+)
>  create mode 100644 drivers/media/platform/sti/hva/Makefile
>  create mode 100644 drivers/media/platform/sti/hva/hva-hw.c
>  create mode 100644 drivers/media/platform/sti/hva/hva-hw.h
>  create mode 100644 drivers/media/platform/sti/hva/hva-mem.c
>  create mode 100644 drivers/media/platform/sti/hva/hva-mem.h
>  create mode 100644 drivers/media/platform/sti/hva/hva-v4l2.c
>  create mode 100644 drivers/media/platform/sti/hva/hva.h
> 
> diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
> index 382f393..182d63f 100644
> --- a/drivers/media/platform/Kconfig
> +++ b/drivers/media/platform/Kconfig
> @@ -227,6 +227,20 @@ config VIDEO_STI_BDISP
>  	help
>  	  This v4l2 mem2mem driver is a 2D blitter for STMicroelectronics SoC.
>  
> +config VIDEO_STI_HVA
> +	tristate "STMicroelectronics STiH41x HVA multi-format video encoder V4L2 driver"
> +	depends on VIDEO_DEV && VIDEO_V4L2
> +	depends on ARCH_STI || COMPILE_TEST
> +	select VIDEOBUF2_DMA_CONTIG
> +	select V4L2_MEM2MEM_DEV
> +	help
> +	  This V4L2 driver enables HVA multi-format video encoder of
> +	  STMicroelectronics SoC STiH41x series, allowing hardware encoding of raw
> +	  uncompressed formats in various compressed video bitstreams format.
> +
> +	  To compile this driver as a module, choose M here:
> +	  the module will be called hva.
> +
>  config VIDEO_SH_VEU
>  	tristate "SuperH VEU mem2mem video processing driver"
>  	depends on VIDEO_DEV && VIDEO_V4L2 && HAS_DMA
> diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
> index 99cf315..784dcd4 100644
> --- a/drivers/media/platform/Makefile
> +++ b/drivers/media/platform/Makefile
> @@ -36,6 +36,7 @@ obj-$(CONFIG_VIDEO_SAMSUNG_S5P_G2D)	+= s5p-g2d/
>  obj-$(CONFIG_VIDEO_SAMSUNG_EXYNOS_GSC)	+= exynos-gsc/
>  
>  obj-$(CONFIG_VIDEO_STI_BDISP)		+= sti/bdisp/
> +obj-$(CONFIG_VIDEO_STI_HVA)		+= sti/hva/
>  obj-$(CONFIG_DVB_C8SECTPFE)		+= sti/c8sectpfe/
>  
>  obj-$(CONFIG_BLACKFIN)                  += blackfin/
> diff --git a/drivers/media/platform/sti/hva/Makefile b/drivers/media/platform/sti/hva/Makefile
> new file mode 100644
> index 0000000..7022a33
> --- /dev/null
> +++ b/drivers/media/platform/sti/hva/Makefile
> @@ -0,0 +1,2 @@
> +obj-$(CONFIG_VIDEO_STI_HVA) := hva.o
> +hva-y := hva-v4l2.o hva-hw.o hva-mem.o
> diff --git a/drivers/media/platform/sti/hva/hva-hw.c b/drivers/media/platform/sti/hva/hva-hw.c
> new file mode 100644
> index 0000000..fa293c7
> --- /dev/null
> +++ b/drivers/media/platform/sti/hva/hva-hw.c
> @@ -0,0 +1,534 @@
> +/*
> + * Copyright (C) STMicroelectronics SA 2015
> + * Authors: Yannick Fertre <yannick.fertre@st.com>
> + *          Hugues Fruchet <hugues.fruchet@st.com>
> + * License terms:  GNU General Public License (GPL), version 2
> + */
> +
> +#include 
> +#include 
> +#include 
> +#include 
> +
> +#include "hva.h"
> +#include "hva-hw.h"
> +
> +/* HVA register offsets */
> +#define HVA_HIF_REG_RST                 0x0100U
> +#define HVA_HIF_REG_RST_ACK             0x0104U
> +#define HVA_HIF_REG_MIF_CFG             0x0108U
> +#define HVA_HIF_REG_HEC_MIF_CFG         0x010CU
> +#define HVA_HIF_REG_CFL                 0x0110U
> +#define HVA_HIF_FIFO_CMD                0x0114U
> +#define HVA_HIF_FIFO_STS                0x0118U
> +#define HVA_HIF_REG_SFL                 0x011CU
> +#define HVA_HIF_REG_IT_ACK              0x0120U
> +#define HVA_HIF_REG_ERR_IT_ACK          0x0124U
> +#define HVA_HIF_REG_LMI_ERR             0x0128U
> +#define HVA_HIF_REG_EMI_ERR             0x012CU
> +#define HVA_HIF_REG_HEC_MIF_ERR         0x0130U
> +#define HVA_HIF_REG_HEC_STS             0x0134U
> +#define HVA_HIF_REG_HVC_STS             0x0138U
> +#define HVA_HIF_REG_HJE_STS             0x013CU
> +#define HVA_HIF_REG_CNT                 0x0140U
> +#define HVA_HIF_REG_HEC_CHKSYN_DIS      0x0144U
> +#define HVA_HIF_REG_CLK_GATING          0x0148U
> +#define HVA_HIF_REG_VERSION             0x014CU
> +#define HVA_HIF_REG_BSM                 0x0150U
> +
> +/* define value for version id register (HVA_HIF_REG_VERSION) */
> +#define VERSION_ID_MASK	0x0000FFFF
> +
> +/* define values for BSM register (HVA_HIF_REG_BSM) */
> +#define BSM_CFG_VAL1	0x0003F000
> +#define BSM_CFG_VAL2	0x003F0000
> +
> +/* define values for memory interface register (HVA_HIF_REG_MIF_CFG) */
> +#define MIF_CFG_VAL1	0x04460446
> +#define MIF_CFG_VAL2	0x04460806
> +#define MIF_CFG_VAL3	0x00000000
> +
> +/* define value for HEC memory interface register (HVA_HIF_REG_MIF_CFG) */
> +#define HEC_MIF_CFG_VAL	0x000000C4
> +
> +/*  Bits definition for clock gating register (HVA_HIF_REG_CLK_GATING) */
> +#define CLK_GATING_HVC	BIT(0)
> +#define CLK_GATING_HEC	BIT(1)
> +#define CLK_GATING_HJE	BIT(2)
> +
> +/* fix hva clock rate */
> +#define CLK_RATE		300000000
> +
> +/* fix delay for pmruntime */
> +#define AUTOSUSPEND_DELAY_MS	3
> +
> +/**
> + * hw encode error values
> + * NO_ERROR: Success, Task OK
> + * H264_BITSTREAM_OVERSIZE: VECH264 Bitstream size > bitstream buffer
> + * H264_FRAME_SKIPPED: VECH264 Frame skipped (refers to CPB Buffer Size)
> + * H264_SLICE_LIMIT_SIZE: VECH264 MB > slice limit size
> + * H264_MAX_SLICE_NUMBER: VECH264 max slice number reached
> + * H264_SLICE_READY: VECH264 Slice ready
> + * TASK_LIST_FULL: HVA/FPC task list full
> +		   (discard latest transform command)
> + * UNKNOWN_COMMAND: Transform command not known by HVA/FPC
> + * WRONG_CODEC_OR_RESOLUTION: Wrong Codec or Resolution Selection
> + * NO_INT_COMPLETION: Time-out on interrupt completion
> + * LMI_ERR: Local Memory Interface Error
> + * EMI_ERR: External Memory Interface Error
> + * HECMI_ERR: HEC Memory Interface Error
> + */
> +enum hva_hw_error {
> +	NO_ERROR = 0x0,
> +	H264_BITSTREAM_OVERSIZE = 0x2,
> +	H264_FRAME_SKIPPED = 0x4,
> +	H264_SLICE_LIMIT_SIZE = 0x5,
> +	H264_MAX_SLICE_NUMBER = 0x7,
> +	H264_SLICE_READY = 0x8,
> +	TASK_LIST_FULL = 0xF0,
> +	UNKNOWN_COMMAND = 0xF1,
> +	WRONG_CODEC_OR_RESOLUTION = 0xF4,
> +	NO_INT_COMPLETION = 0x100,
> +	LMI_ERR = 0x101,
> +	EMI_ERR = 0x102,
> +	HECMI_ERR = 0x103,
> +};
> +
> +static irqreturn_t hva_hw_its_interrupt(int irq, void *data)
> +{
> +	struct hva_dev *hva = data;
> +
> +	/* read status registers */
> +	hva->sts_reg = readl_relaxed(hva->regs + HVA_HIF_FIFO_STS);
> +	hva->sfl_reg = readl_relaxed(hva->regs + HVA_HIF_REG_SFL);
> +
> +	/* acknowledge interruption */
> +	writel_relaxed(0x1, hva->regs + HVA_HIF_REG_IT_ACK);
> +
> +	return IRQ_WAKE_THREAD;
> +}
> +
> +static irqreturn_t hva_hw_its_irq_thread(int irq, void *arg)
> +{
> +	struct hva_dev *hva = arg;
> +	struct device *dev = hva_to_dev(hva);
> +	u32 status = hva->sts_reg & 0xFF;
> +	u8 ctx_id = 0;
> +	struct hva_ctx *ctx = NULL;
> +
> +	dev_dbg(dev, "%s     %s: status: 0x%02x fifo level: 0x%02x\n",
> +		HVA_PREFIX, __func__, hva->sts_reg & 0xFF, hva->sfl_reg & 0xF);
> +
> +	/*
> +	 * status: task_id[31:16] client_id[15:8] status[7:0]
> +	 * the context identifier is retrieved from the client identifier
> +	 */
> +	ctx_id = (hva->sts_reg & 0xFF00) >> 8;
> +	if (ctx_id >= HVA_MAX_INSTANCES) {
> +		dev_err(dev, "%s     %s: bad context identifier: %d\n",
> +			ctx->name, __func__, ctx_id);
> +		ctx->hw_err = true;
> +		goto out;
> +	}
> +
> +	ctx = hva->instances[ctx_id];
> +
> +	switch (status) {
> +	case NO_ERROR:
> +		dev_dbg(dev, "%s     %s: no error\n",
> +			ctx->name, __func__);
> +		ctx->hw_err = false;
> +		break;
> +	case H264_SLICE_READY:
> +		dev_dbg(dev, "%s     %s: h264 slice ready\n",
> +			ctx->name, __func__);
> +		ctx->hw_err = false;
> +		break;
> +	case H264_FRAME_SKIPPED:
> +		dev_dbg(dev, "%s     %s: h264 frame skipped\n",
> +			ctx->name, __func__);
> +		ctx->hw_err = false;
> +		break;
> +	case H264_BITSTREAM_OVERSIZE:
> +		dev_err(dev, "%s     %s:h264 bitstream oversize\n",
> +			ctx->name, __func__);
> +		ctx->hw_err = true;
> +		break;
> +	case H264_SLICE_LIMIT_SIZE:
> +		dev_err(dev, "%s     %s: h264 slice limit size is reached\n",
> +			ctx->name, __func__);
> +		ctx->hw_err = true;
> +		break;
> +	case H264_MAX_SLICE_NUMBER:
> +		dev_err(dev, "%s     %s: h264 max slice number is reached\n",
> +			ctx->name, __func__);
> +		ctx->hw_err = true;
> +		break;
> +	case TASK_LIST_FULL:
> +		dev_err(dev, "%s     %s:task list full\n",
> +			ctx->name, __func__);
> +		ctx->hw_err = true;
> +		break;
> +	case UNKNOWN_COMMAND:
> +		dev_err(dev, "%s     %s: command not known\n",
> +			ctx->name, __func__);
> +		ctx->hw_err = true;
> +		break;
> +	case WRONG_CODEC_OR_RESOLUTION:
> +		dev_err(dev, "%s     %s: wrong codec or resolution\n",
> +			ctx->name, __func__);
> +		ctx->hw_err = true;
> +		break;
> +	default:
> +		dev_err(dev, "%s     %s: status not recognized\n",
> +			ctx->name, __func__);
> +		ctx->hw_err = true;
> +		break;
> +	}
> +out:
> +	complete(&hva->interrupt);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t hva_hw_err_interrupt(int irq, void *data)
> +{
> +	struct hva_dev *hva = data;
> +
> +	/* read status registers */
> +	hva->sts_reg = readl_relaxed(hva->regs + HVA_HIF_FIFO_STS);
> +	hva->sfl_reg = readl_relaxed(hva->regs + HVA_HIF_REG_SFL);
> +
> +	/* read error registers */
> +	hva->lmi_err_reg = readl_relaxed(hva->regs + HVA_HIF_REG_LMI_ERR);
> +	hva->emi_err_reg = readl_relaxed(hva->regs + HVA_HIF_REG_EMI_ERR);
> +	hva->hec_mif_err_reg = readl_relaxed(hva->regs +
> +					     HVA_HIF_REG_HEC_MIF_ERR);
> +
> +	/* acknowledge interruption */
> +	writel_relaxed(0x1, hva->regs + HVA_HIF_REG_IT_ACK);
> +
> +	return IRQ_WAKE_THREAD;
> +}
> +
> +static irqreturn_t hva_hw_err_irq_thread(int irq, void *arg)
> +{
> +	struct hva_dev *hva = arg;
> +	struct device *dev = hva_to_dev(hva);
> +	u8 ctx_id = 0;
> +	struct hva_ctx *ctx;
> +
> +	dev_dbg(dev, "%s     status: 0x%02x fifo level: 0x%02x\n",
> +		HVA_PREFIX, hva->sts_reg & 0xFF, hva->sfl_reg & 0xF);
> +
> +	/*
> +	 * status: task_id[31:16] client_id[15:8] status[7:0]
> +	 * the context identifier is retrieved from the client identifier
> +	 */
> +	ctx_id = (hva->sts_reg & 0xFF00) >> 8;
> +	if (ctx_id >= HVA_MAX_INSTANCES) {
> +		dev_err(dev, "%s     bad context identifier: %d\n", HVA_PREFIX,
> +			ctx_id);
> +		goto out;
> +	}
> +
> +	ctx = hva->instances[ctx_id];
> +
> +	if (hva->lmi_err_reg) {
> +		dev_err(dev, "%s     local memory interface error: 0x%08x\n",
> +			ctx->name, hva->lmi_err_reg);
> +		ctx->hw_err = true;
> +	}
> +
> +	if (hva->lmi_err_reg) {
> +		dev_err(dev, "%s     external memory interface error: 0x%08x\n",
> +			ctx->name, hva->emi_err_reg);
> +		ctx->hw_err = true;
> +	}
> +
> +	if (hva->hec_mif_err_reg) {
> +		dev_err(dev, "%s     hec memory interface error: 0x%08x\n",
> +			ctx->name, hva->hec_mif_err_reg);
> +		ctx->hw_err = true;
> +	}
> +out:
> +	complete(&hva->interrupt);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static unsigned long int hva_hw_get_ip_version(struct hva_dev *hva)
> +{
> +	struct device *dev = hva_to_dev(hva);
> +	unsigned long int version;
> +
> +	if (pm_runtime_get_sync(dev) < 0) {
> +		dev_err(dev, "%s     failed to get pm_runtime\n", HVA_PREFIX);
> +		mutex_unlock(&hva->protect_mutex);
> +		return -EFAULT;
> +	}
> +
> +	version = readl_relaxed(hva->regs + HVA_HIF_REG_VERSION) &
> +				VERSION_ID_MASK;
> +
> +	pm_runtime_put_autosuspend(dev);
> +
> +	switch (version) {
> +	case HVA_VERSION_V400:
> +		dev_dbg(dev, "%s     IP hardware version 0x%lx\n",
> +			HVA_PREFIX, version);
> +		break;
> +	default:
> +		dev_err(dev, "%s     unknown IP hardware version 0x%lx\n",
> +			HVA_PREFIX, version);
> +		version = HVA_VERSION_UNKNOWN;
> +		break;
> +	}
> +
> +	return version;
> +}
> +
> +int hva_hw_probe(struct platform_device *pdev, struct hva_dev *hva)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct resource *regs;
> +	struct resource *esram;
> +	int ret;
> +
> +	WARN_ON(!hva);
> +
> +	/* get memory for registers */
> +	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	hva->regs = devm_ioremap_resource(dev, regs);
> +	if (IS_ERR_OR_NULL(hva->regs)) {
> +		dev_err(dev, "%s     failed to get regs\n", HVA_PREFIX);
> +		return PTR_ERR(hva->regs);
> +	}
> +
> +	/* get memory for esram */
> +	esram = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> +	if (IS_ERR_OR_NULL(esram)) {
> +		dev_err(dev, "%s     failed to get esram\n", HVA_PREFIX);
> +		return PTR_ERR(esram);
> +	}
> +	hva->esram_addr = esram->start;
> +	hva->esram_size = esram->end - esram->start + 1;
> +
> +	dev_info(dev, "%s     esram reserved for address: 0x%x size:%d\n",
> +		 HVA_PREFIX, hva->esram_addr, hva->esram_size);
> +
> +	/* get clock resource */
> +	hva->clk = devm_clk_get(dev, "clk_hva");
> +	if (IS_ERR(hva->clk)) {
> +		dev_err(dev, "%s     failed to get clock\n", HVA_PREFIX);
> +		return PTR_ERR(hva->clk);
> +	}
> +
> +	ret = clk_prepare(hva->clk);
> +	if (ret < 0) {
> +		dev_err(dev, "%s     failed to prepare clock\n", HVA_PREFIX);
> +		hva->clk = ERR_PTR(-EINVAL);
> +		return ret;
> +	}
> +
> +	/* get status interruption resource */
> +	ret  = platform_get_irq(pdev, 0);
> +	if (ret < 0) {
> +		dev_err(dev, "%s     failed to get status IRQ\n", HVA_PREFIX);
> +		goto err_clk;
> +	}
> +	hva->irq_its = ret;
> +
> +	ret = devm_request_threaded_irq(dev, hva->irq_its, hva_hw_its_interrupt,
> +					hva_hw_its_irq_thread,
> +					IRQF_ONESHOT,
> +					"hva_its_irq", hva);
> +	if (ret) {
> +		dev_err(dev, "%s     failed to install status IRQ 0x%x\n",
> +			HVA_PREFIX, hva->irq_its);
> +		goto err_clk;
> +	}
> +	disable_irq(hva->irq_its);
> +
> +	/* get error interruption resource */
> +	ret = platform_get_irq(pdev, 1);
> +	if (ret < 0) {
> +		dev_err(dev, "%s     failed to get error IRQ\n", HVA_PREFIX);
> +		goto err_clk;
> +	}
> +	hva->irq_err = ret;
> +
> +	ret = devm_request_threaded_irq(dev, hva->irq_err, hva_hw_err_interrupt,
> +					hva_hw_err_irq_thread,
> +					IRQF_ONESHOT,
> +					"hva_err_irq", hva);
> +	if (ret) {
> +		dev_err(dev, "%s     failed to install error IRQ 0x%x\n",
> +			HVA_PREFIX, hva->irq_err);
> +		goto err_clk;
> +	}
> +	disable_irq(hva->irq_err);
> +
> +	/* initialise protection mutex */
> +	mutex_init(&hva->protect_mutex);
> +
> +	/* initialise completion signal */
> +	init_completion(&hva->interrupt);
> +
> +	/* initialise runtime power management */
> +	pm_runtime_set_autosuspend_delay(dev, AUTOSUSPEND_DELAY_MS);
> +	pm_runtime_use_autosuspend(dev);
> +	pm_runtime_set_suspended(dev);
> +	pm_runtime_enable(dev);
> +
> +	ret = pm_runtime_get_sync(dev);
> +	if (ret < 0) {
> +		dev_err(dev, "%s     failed to set PM\n", HVA_PREFIX);
> +		goto err_clk;
> +	}
> +
> +	/* check IP hardware version */
> +	hva->ip_version = hva_hw_get_ip_version(hva);
> +
> +	if (hva->ip_version == HVA_VERSION_UNKNOWN) {
> +		ret = -EINVAL;
> +		goto err_pm;
> +	}
> +
> +	dev_info(dev, "%s     found hva device (version 0x%lx)\n", HVA_PREFIX,
> +		 hva->ip_version);
> +
> +	return 0;
> +
> +err_pm:
> +	pm_runtime_put(dev);
> +err_clk:
> +	if (hva->clk)
> +		clk_unprepare(hva->clk);
> +
> +	return ret;
> +}
> +
> +void hva_hw_remove(struct hva_dev *hva)
> +{
> +	struct device *dev = hva_to_dev(hva);
> +
> +	disable_irq(hva->irq_its);
> +	disable_irq(hva->irq_err);
> +
> +	pm_runtime_put_autosuspend(dev);
> +	pm_runtime_disable(dev);
> +}
> +
> +int hva_hw_runtime_suspend(struct device *dev)
> +{
> +	struct hva_dev *hva = dev_get_drvdata(dev);
> +
> +	clk_disable_unprepare(hva->clk);
> +
> +	return 0;
> +}
> +
> +int hva_hw_runtime_resume(struct device *dev)
> +{
> +	struct hva_dev *hva = dev_get_drvdata(dev);
> +
> +	if (clk_prepare_enable(hva->clk)) {
> +		dev_err(hva->dev, "%s     failed to prepare hva clk\n",
> +			HVA_PREFIX);
> +		return -EINVAL;
> +	}
> +
> +	if (clk_set_rate(hva->clk, CLK_RATE)) {
> +		dev_err(dev, "%s     failed to set clock frequency\n",
> +			HVA_PREFIX);
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +int hva_hw_execute_task(struct hva_ctx *ctx, enum hva_hw_cmd_type cmd,
> +			struct hva_buffer *task)
> +{
> +	struct hva_dev *hva = ctx_to_hdev(ctx);
> +	struct device *dev = hva_to_dev(hva);
> +	u8 client_id = ctx->id;
> +	int ret;
> +	u32 reg = 0;
> +
> +	mutex_lock(&hva->protect_mutex);
> +
> +	/* enable irqs */
> +	enable_irq(hva->irq_its);
> +	enable_irq(hva->irq_err);
> +
> +	if (pm_runtime_get_sync(dev) < 0) {
> +		dev_err(dev, "%s     failed to get pm_runtime\n", ctx->name);
> +		ret = -EFAULT;
> +		goto out;
> +	}
> +
> +	reg = readl_relaxed(hva->regs + HVA_HIF_REG_CLK_GATING);
> +	switch (cmd) {
> +	case H264_ENC:
> +		reg |= CLK_GATING_HVC;
> +		break;
> +	default:
> +		dev_dbg(dev, "%s     unknown command 0x%x\n", ctx->name, cmd);
> +		ret = -EFAULT;
> +		goto out;
> +	}
> +	writel_relaxed(reg, hva->regs + HVA_HIF_REG_CLK_GATING);
> +
> +	dev_dbg(dev, "%s     %s: write configuration registers\n", ctx->name,
> +		__func__);
> +
> +	/* byte swap config */
> +	writel_relaxed(BSM_CFG_VAL1, hva->regs + HVA_HIF_REG_BSM);
> +
> +	/* define Max Opcode Size and Max Message Size for LMI and EMI */
> +	writel_relaxed(MIF_CFG_VAL3, hva->regs + HVA_HIF_REG_MIF_CFG);
> +	writel_relaxed(HEC_MIF_CFG_VAL, hva->regs + HVA_HIF_REG_HEC_MIF_CFG);
> +
> +	/*
> +	 * command FIFO: task_id[31:16] client_id[15:8] command_type[7:0]
> +	 * the context identifier is provided as client identifier to the
> +	 * hardware, and is retrieved in the interrupt functions from the
> +	 * status register
> +	 */
> +	dev_dbg(dev, "%s     %s: send task (cmd: %d, task_desc: %pad)\n",
> +		ctx->name, __func__, cmd + (client_id << 8), &task->paddr);
> +	writel_relaxed(cmd + (client_id << 8), hva->regs + HVA_HIF_FIFO_CMD);
> +	writel_relaxed(task->paddr, hva->regs + HVA_HIF_FIFO_CMD);
> +
> +	if (!wait_for_completion_timeout(&hva->interrupt,
> +					 msecs_to_jiffies(2000))) {
> +		dev_err(dev, "%s     %s: time out on completion\n", ctx->name,
> +			__func__);
> +		ret = -EFAULT;
> +		goto out;
> +	}
> +
> +	/* get encoding status */
> +	ret = ctx->hw_err ? -EFAULT : 0;
> +
> +out:
> +	disable_irq(hva->irq_its);
> +	disable_irq(hva->irq_err);
> +
> +	switch (cmd) {
> +	case H264_ENC:
> +		reg &= ~CLK_GATING_HVC;
> +		writel_relaxed(reg, hva->regs + HVA_HIF_REG_CLK_GATING);
> +		break;
> +	default:
> +		dev_dbg(dev, "%s     unknown command 0x%x\n", ctx->name, cmd);
> +	}
> +
> +	pm_runtime_put_autosuspend(dev);
> +	mutex_unlock(&hva->protect_mutex);
> +
> +	return ret;
> +}
> diff --git a/drivers/media/platform/sti/hva/hva-hw.h b/drivers/media/platform/sti/hva/hva-hw.h
> new file mode 100644
> index 0000000..efb45b9
> --- /dev/null
> +++ b/drivers/media/platform/sti/hva/hva-hw.h
> @@ -0,0 +1,42 @@
> +/*
> + * Copyright (C) STMicroelectronics SA 2015
> + * Authors: Yannick Fertre <yannick.fertre@st.com>
> + *          Hugues Fruchet <hugues.fruchet@st.com>
> + * License terms:  GNU General Public License (GPL), version 2
> + */
> +
> +#ifndef HVA_HW_H
> +#define HVA_HW_H
> +
> +#include "hva-mem.h"
> +
> +/* HVA Versions */
> +#define HVA_VERSION_UNKNOWN    0x000
> +#define HVA_VERSION_V400       0x400
> +
> +/* HVA command types */
> +enum hva_hw_cmd_type {
> +	/* RESERVED = 0x00 */
> +	/* RESERVED = 0x01 */
> +	H264_ENC = 0x02,
> +	/* RESERVED = 0x03 */
> +	/* RESERVED = 0x04 */
> +	/* RESERVED = 0x05 */
> +	/* RESERVED = 0x06 */
> +	/* RESERVED = 0x07 */
> +	REMOVE_CLIENT = 0x08,
> +	FREEZE_CLIENT = 0x09,
> +	START_CLIENT = 0x0A,
> +	FREEZE_ALL = 0x0B,
> +	START_ALL = 0x0C,
> +	REMOVE_ALL = 0x0D
> +};
> +
> +int hva_hw_probe(struct platform_device *pdev, struct hva_dev *hva);
> +void hva_hw_remove(struct hva_dev *hva);
> +int hva_hw_runtime_suspend(struct device *dev);
> +int hva_hw_runtime_resume(struct device *dev);
> +int hva_hw_execute_task(struct hva_ctx *ctx, enum hva_hw_cmd_type cmd,
> +			struct hva_buffer *task);
> +
> +#endif /* HVA_HW_H */
> diff --git a/drivers/media/platform/sti/hva/hva-mem.c b/drivers/media/platform/sti/hva/hva-mem.c
> new file mode 100644
> index 0000000..759c873
> --- /dev/null
> +++ b/drivers/media/platform/sti/hva/hva-mem.c
> @@ -0,0 +1,60 @@
> +/*
> + * Copyright (C) STMicroelectronics SA 2015
> + * Authors: Yannick Fertre <yannick.fertre@st.com>
> + *          Hugues Fruchet <hugues.fruchet@st.com>
> + * License terms:  GNU General Public License (GPL), version 2
> + */
> +
> +#include "hva.h"
> +#include "hva-mem.h"
> +
> +int hva_mem_alloc(struct hva_ctx *ctx, u32 size, const char *name,
> +		  struct hva_buffer **buf)
> +{
> +	struct device *dev = ctx_to_dev(ctx);
> +	struct hva_buffer *b;
> +	dma_addr_t paddr;
> +	void *base;
> +	DEFINE_DMA_ATTRS(attrs);
> +
> +	b = devm_kzalloc(dev, sizeof(*b), GFP_KERNEL);
> +	if (!b)
> +		return -ENOMEM;
> +
> +	dma_set_attr(DMA_ATTR_WRITE_COMBINE, &attrs);
> +	base = dma_alloc_attrs(dev, size, &paddr, GFP_KERNEL | GFP_DMA, &attrs);
> +	if (!base) {
> +		dev_err(dev, "%s %s : dma_alloc_attrs failed for %s (size=%d)\n",
> +			ctx->name, __func__, name, size);
> +		devm_kfree(dev, b);
> +		return -ENOMEM;
> +	}
> +
> +	b->size = size;
> +	b->paddr = paddr;
> +	b->vaddr = base;
> +	b->attrs = attrs;
> +	b->name = name;
> +
> +	dev_dbg(dev,
> +		"%s allocate %d bytes of HW memory @(virt=%p, phy=%pad): %s\n",
> +		ctx->name, size, b->vaddr, &b->paddr, b->name);
> +
> +	/* return  hva buffer to user */
> +	*buf = b;
> +
> +	return 0;
> +}
> +
> +void hva_mem_free(struct hva_ctx *ctx, struct hva_buffer *buf)
> +{
> +	struct device *dev = ctx_to_dev(ctx);
> +
> +	dev_dbg(dev,
> +		"%s free %d bytes of HW memory @(virt=%p, phy=%pad): %s\n",
> +		ctx->name, buf->size, buf->vaddr, &buf->paddr, buf->name);
> +
> +	dma_free_attrs(dev, buf->size, buf->vaddr, buf->paddr, &buf->attrs);
> +
> +	devm_kfree(dev, buf);
> +}
> diff --git a/drivers/media/platform/sti/hva/hva-mem.h b/drivers/media/platform/sti/hva/hva-mem.h
> new file mode 100644
> index 0000000..e8a3f7e
> --- /dev/null
> +++ b/drivers/media/platform/sti/hva/hva-mem.h
> @@ -0,0 +1,36 @@
> +/*
> + * Copyright (C) STMicroelectronics SA 2015
> + * Authors: Yannick Fertre <yannick.fertre@st.com>
> + *          Hugues Fruchet <hugues.fruchet@st.com>
> + * License terms:  GNU General Public License (GPL), version 2
> + */
> +
> +#ifndef HVA_MEM_H
> +#define HVA_MEM_H
> +
> +/**
> + * struct hva_buffer - hva buffer
> + *
> + * @name:  name of requester
> + * @attrs: dma attributes
> + * @paddr: physical address (for hardware)
> + * @vaddr: virtual address (kernel can read/write)
> + * @size:  size of buffer
> + */
> +struct hva_buffer {
> +	const char		*name;
> +	struct dma_attrs	attrs;
> +	dma_addr_t		paddr;
> +	void			*vaddr;
> +	u32			size;
> +};
> +
> +int hva_mem_alloc(struct hva_ctx *ctx,
> +		  __u32 size,
> +		  const char *name,
> +		  struct hva_buffer **buf);
> +
> +void hva_mem_free(struct hva_ctx *ctx,
> +		  struct hva_buffer *buf);
> +
> +#endif /* HVA_MEM_H */
> diff --git a/drivers/media/platform/sti/hva/hva-v4l2.c b/drivers/media/platform/sti/hva/hva-v4l2.c
> new file mode 100644
> index 0000000..bacc9ff
> --- /dev/null
> +++ b/drivers/media/platform/sti/hva/hva-v4l2.c
> @@ -0,0 +1,1299 @@
> +/*
> + * Copyright (C) STMicroelectronics SA 2015
> + * Authors: Yannick Fertre <yannick.fertre@st.com>
> + *          Hugues Fruchet <hugues.fruchet@st.com>
> + * License terms:  GNU General Public License (GPL), version 2
> + */
> +
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +
> +#include "hva.h"
> +#include "hva-hw.h"
> +
> +#define HVA_NAME "hva"
> +
> +#define MIN_FRAMES	1
> +#define MIN_STREAMS	1
> +
> +#define HVA_MIN_WIDTH	32
> +#define HVA_MAX_WIDTH	1920
> +#define HVA_MIN_HEIGHT	32
> +#define HVA_MAX_HEIGHT	1920
> +
> +/* HVA requires a 16x16 pixels alignment for frames */
> +#define HVA_WIDTH_ALIGNMENT	16
> +#define HVA_HEIGHT_ALIGNMENT	16
> +
> +#define DEFAULT_WIDTH		HVA_MIN_WIDTH
> +#define	DEFAULT_HEIGHT		HVA_MIN_HEIGHT
> +#define DEFAULT_FRAME_NUM	1
> +#define DEFAULT_FRAME_DEN	30
> +
> +#define to_type_str(type) (type == V4L2_BUF_TYPE_VIDEO_OUTPUT ? \
> +			   "frame" : "stream")
> +
> +#define fh_to_ctx(f)    (container_of(f, struct hva_ctx, fh))
> +
> +/* registry of available encoders */
> +const struct hva_enc *hva_encoders[] = {
> +};
> +
> +static inline int frame_size(u32 w, u32 h, u32 fmt)
> +{
> +	switch (fmt) {
> +	case V4L2_PIX_FMT_NV12:
> +	case V4L2_PIX_FMT_NV21:
> +		return (w * h * 3) / 2;
> +	default:
> +		return 0;
> +	}
> +}
> +
> +static inline int frame_stride(u32 w, u32 fmt)
> +{
> +	switch (fmt) {
> +	case V4L2_PIX_FMT_NV12:
> +	case V4L2_PIX_FMT_NV21:
> +		return w;
> +	default:
> +		return 0;
> +	}
> +}
> +
> +static inline int frame_alignment(u32 fmt)
> +{
> +	switch (fmt) {
> +	case V4L2_PIX_FMT_NV12:
> +	case V4L2_PIX_FMT_NV21:
> +		/* multiple of 2 */
> +		return 2;
> +	default:
> +		return 1;
> +	}
> +}
> +
> +static inline int estimated_stream_size(u32 w, u32 h)
> +{
> +	/*
> +	 * HVA only encodes in YUV420 format, whatever the frame format.
> +	 * A compression ratio of 2 is assumed: thus, the maximum size
> +	 * of a stream is estimated to ((width x height x 3 / 2) / 2)
> +	 */
> +	return (w * h * 3) / 4;
> +}
> +
> +static void set_default_params(struct hva_ctx *ctx)
> +{
> +	struct hva_frameinfo *frameinfo = &ctx->frameinfo;
> +	struct hva_streaminfo *streaminfo = &ctx->streaminfo;
> +
> +	frameinfo->pixelformat = V4L2_PIX_FMT_NV12;
> +	frameinfo->width = DEFAULT_WIDTH;
> +	frameinfo->height = DEFAULT_HEIGHT;
> +	frameinfo->aligned_width = DEFAULT_WIDTH;
> +	frameinfo->aligned_height = DEFAULT_HEIGHT;
> +	frameinfo->size = frame_size(frameinfo->aligned_width,
> +				     frameinfo->aligned_height,
> +				     frameinfo->pixelformat);
> +
> +	streaminfo->streamformat = V4L2_PIX_FMT_H264;
> +	streaminfo->width = DEFAULT_WIDTH;
> +	streaminfo->height = DEFAULT_HEIGHT;
> +
> +	ctx->max_stream_size = estimated_stream_size(streaminfo->width,
> +						     streaminfo->height);
> +}
> +
> +static const struct hva_enc *hva_find_encoder(struct hva_ctx *ctx,
> +					      u32 pixelformat,
> +					      u32 streamformat)
> +{
> +	struct hva_dev *hva = ctx_to_hdev(ctx);
> +	const struct hva_enc *enc;
> +	unsigned int i;
> +
> +	for (i = 0; i < hva->nb_of_encoders; i++) {
> +		enc = hva->encoders[i];
> +		if ((enc->pixelformat == pixelformat) &&
> +		    (enc->streamformat == streamformat))
> +			return enc;
> +	}
> +
> +	return NULL;
> +}
> +
> +static void register_format(u32 format, u32 formats[], u32 *nb_of_formats)
> +{
> +	u32 i;
> +	bool found = false;
> +
> +	for (i = 0; i < *nb_of_formats; i++) {
> +		if (format == formats[i]) {
> +			found = true;
> +			break;
> +		}
> +	}
> +
> +	if (!found)
> +		formats[(*nb_of_formats)++] = format;
> +}
> +
> +static void register_formats(struct hva_dev *hva)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < hva->nb_of_encoders; i++) {
> +		register_format(hva->encoders[i]->pixelformat,
> +				hva->pixelformats,
> +				&hva->nb_of_pixelformats);
> +
> +		register_format(hva->encoders[i]->streamformat,
> +				hva->streamformats,
> +				&hva->nb_of_streamformats);
> +	}
> +}
> +
> +static void register_encoders(struct hva_dev *hva)
> +{
> +	struct device *dev = hva_to_dev(hva);
> +	unsigned int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(hva_encoders); i++) {
> +		if (hva->nb_of_encoders >= HVA_MAX_ENCODERS) {
> +			dev_dbg(dev,
> +				"%s failed to register encoder (%d maximum reached)\n",
> +				hva_encoders[i]->name, HVA_MAX_ENCODERS);
> +			return;
> +		}
> +
> +		hva->encoders[hva->nb_of_encoders++] = hva_encoders[i];
> +		dev_info(dev, "%s encoder registered\n", hva_encoders[i]->name);
> +	}
> +}
> +
> +static int hva_open_encoder(struct hva_ctx *ctx, u32 streamformat,
> +			    u32 pixelformat, struct hva_enc **penc)
> +{
> +	struct hva_dev *hva = ctx_to_hdev(ctx);
> +	struct device *dev = ctx_to_dev(ctx);
> +	struct hva_enc *enc;
> +	unsigned int i;
> +	int ret;
> +	bool found = false;
> +
> +	/* find an encoder which can deal with these formats */
> +	for (i = 0; i < hva->nb_of_encoders; i++) {
> +		enc = (struct hva_enc *)hva->encoders[i];
> +		if ((enc->streamformat == streamformat) &&
> +		    (enc->pixelformat == pixelformat)) {
> +			found = true;
> +			break;
> +		}
> +	}
> +
> +	if (!found) {
> +		dev_err(dev, "%s no encoder found matching %4.4s => %4.4s\n",
> +			ctx->name, (char *)&pixelformat, (char *)&streamformat);
> +		return -EINVAL;
> +	}
> +
> +	dev_dbg(dev, "%s one encoder matching %4.4s => %4.4s\n",
> +		ctx->name, (char *)&pixelformat, (char *)&streamformat);
> +
> +	/* update instance name */
> +	snprintf(ctx->name, sizeof(ctx->name), "[%3d:%4.4s]",
> +		 hva->instance_id, (char *)&streamformat);
> +
> +	/* open encoder instance */
> +	ret = enc->open(ctx);
> +	if (ret) {
> +		dev_err(dev, "%s failed to open encoder instance (%d)\n",
> +			ctx->name, ret);
> +		return ret;
> +	}
> +
> +	dev_dbg(dev, "%s %s encoder opened\n", ctx->name, enc->name);
> +
> +	*penc = enc;
> +
> +	return ret;
> +}
> +
> +/*
> + * V4L2 ioctl operations
> + */
> +
> +static int hva_querycap(struct file *file, void *priv,
> +			struct v4l2_capability *cap)
> +{
> +	struct hva_ctx *ctx = fh_to_ctx(file->private_data);
> +	struct hva_dev *hva = ctx_to_hdev(ctx);
> +
> +	strlcpy(cap->driver, hva->pdev->name, sizeof(cap->driver));
> +	strlcpy(cap->card, hva->pdev->name, sizeof(cap->card));
> +	snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
> +		 HVA_NAME);
> +
> +	cap->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M;
> +	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
> +
> +	return 0;
> +}
> +
> +static int hva_enum_fmt_stream(struct file *file, void *priv,
> +			       struct v4l2_fmtdesc *f)
> +{
> +	struct hva_ctx *ctx = fh_to_ctx(file->private_data);
> +	struct hva_dev *hva = ctx_to_hdev(ctx);
> +
> +	if (unlikely(f->index >= hva->nb_of_streamformats))
> +		return -EINVAL;
> +
> +	f->pixelformat = hva->streamformats[f->index];
> +	snprintf(f->description, sizeof(f->description), "%4.4s",
> +		 (char *)&f->pixelformat);
> +	f->flags = V4L2_FMT_FLAG_COMPRESSED;
> +
> +	return 0;
> +}
> +
> +static int hva_enum_fmt_frame(struct file *file, void *priv,
> +			      struct v4l2_fmtdesc *f)
> +{
> +	struct hva_ctx *ctx = fh_to_ctx(file->private_data);
> +	struct hva_dev *hva = ctx_to_hdev(ctx);
> +
> +	if (unlikely(f->index >= hva->nb_of_pixelformats))
> +		return -EINVAL;
> +
> +	f->pixelformat = hva->pixelformats[f->index];
> +	snprintf(f->description, sizeof(f->description), "%4.4s",
> +		 (char *)&f->pixelformat);
> +
> +	return 0;
> +}
> +
> +static int hva_g_fmt_stream(struct file *file, void *fh, struct v4l2_format *f)
> +{
> +	struct hva_ctx *ctx = fh_to_ctx(file->private_data);
> +	struct device *dev = ctx_to_dev(ctx);
> +	struct hva_streaminfo *streaminfo = &ctx->streaminfo;
> +
> +	f->fmt.pix.width = streaminfo->width;
> +	f->fmt.pix.height = streaminfo->height;
> +	f->fmt.pix.field = V4L2_FIELD_NONE;
> +	f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;

Hard coding this is not great.Ideally the colorimetry (if not modified) should be copied from OUTPUT to CAPTURE, you may also set this to V4L2_COLORSPACE_DEFAULT.

> +	f->fmt.pix.pixelformat = streaminfo->streamformat;
> +	f->fmt.pix.bytesperline = 0;
> +	f->fmt.pix.sizeimage = ctx->max_stream_size;
> +
> +	dev_dbg(dev, "%s V4L2 G_FMT (CAPTURE): %dx%d fmt:%.4s size:%d\n",
> +		ctx->name, f->fmt.pix.width, f->fmt.pix.height,
> +		(u8 *)&f->fmt.pix.pixelformat, f->fmt.pix.sizeimage);
> +	return 0;
> +}
> +
> +static int hva_g_fmt_frame(struct file *file, void *fh, struct v4l2_format *f)
> +{
> +	struct hva_ctx *ctx = fh_to_ctx(file->private_data);
> +	struct device *dev = ctx_to_dev(ctx);
> +	struct hva_frameinfo *frameinfo = &ctx->frameinfo;
> +
> +	f->fmt.pix.width = frameinfo->width;
> +	f->fmt.pix.height = frameinfo->height;
> +	f->fmt.pix.field = V4L2_FIELD_NONE;
> +	f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
> +	f->fmt.pix.pixelformat = frameinfo->pixelformat;
> +	f->fmt.pix.bytesperline = frame_stride(frameinfo->aligned_width,
> +					       frameinfo->pixelformat);
> +	f->fmt.pix.sizeimage = frameinfo->size;
> +
> +	dev_dbg(dev, "%s V4L2 G_FMT (OUTPUT): %dx%d fmt:%.4s size:%d\n",
> +		ctx->name, f->fmt.pix.width, f->fmt.pix.height,
> +		(u8 *)&f->fmt.pix.pixelformat, f->fmt.pix.sizeimage);
> +
> +	return 0;
> +}
> +
> +static int hva_try_fmt_stream(struct file *file, void *priv,
> +			      struct v4l2_format *f)
> +{
> +	struct hva_ctx *ctx = fh_to_ctx(file->private_data);
> +	struct device *dev = ctx_to_dev(ctx);
> +	struct v4l2_pix_format *pix = &f->fmt.pix;
> +	u32 streamformat = pix->pixelformat;
> +	const struct hva_enc *enc;
> +	u32 width, height;
> +	u32 stream_size;
> +
> +	enc = hva_find_encoder(ctx, ctx->frameinfo.pixelformat, streamformat);
> +	if (!enc) {
> +		dev_dbg(dev,
> +			"%s V4L2 TRY_FMT (CAPTURE): unsupported format %.4s\n",
> +			ctx->name, (char *)&pix->pixelformat);
> +		return -EINVAL;
> +	}
> +
> +	width = pix->width;
> +	height = pix->height;
> +	if (ctx->flags & HVA_FLAG_FRAMEINFO) {
> +		/*
> +		 * if the frame resolution is already fixed, only allow the
> +		 * same stream resolution
> +		 */
> +		pix->width = ctx->frameinfo.width;
> +		pix->height = ctx->frameinfo.height;
> +		if ((pix->width != width) || (pix->height != height))
> +			dev_dbg(dev,
> +				"%s V4L2 TRY_FMT (CAPTURE): resolution updated %dx%d -> %dx%d to fit frame resolution\n",
> +				ctx->name, width, height,
> +				pix->width, pix->height);
> +	} else {
> +		/* adjust width & height */
> +		v4l_bound_align_image(&pix->width,
> +				      HVA_MIN_WIDTH, enc->max_width,
> +				      0,
> +				      &pix->height,
> +				      HVA_MIN_HEIGHT, enc->max_height,
> +				      0,
> +				      0);
> +
> +		if ((pix->width != width) || (pix->height != height))
> +			dev_dbg(dev,
> +				"%s V4L2 TRY_FMT (CAPTURE): resolution updated %dx%d -> %dx%d to fit min/max/alignment\n",
> +				ctx->name, width, height,
> +				pix->width, pix->height);
> +	}
> +
> +	stream_size = estimated_stream_size(pix->width, pix->height);
> +	if (pix->sizeimage < stream_size)
> +		pix->sizeimage = stream_size;
> +
> +	pix->bytesperline = 0;
> +	pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
> +	pix->field = V4L2_FIELD_NONE;
> +
> +	return 0;
> +}
> +
> +static int hva_try_fmt_frame(struct file *file, void *priv,
> +			     struct v4l2_format *f)
> +{
> +	struct hva_ctx *ctx = fh_to_ctx(file->private_data);
> +	struct device *dev = ctx_to_dev(ctx);
> +	struct v4l2_pix_format *pix = &f->fmt.pix;
> +	u32 pixelformat = pix->pixelformat;
> +	const struct hva_enc *enc;
> +	u32 width, height;
> +
> +	enc = hva_find_encoder(ctx, pixelformat, ctx->streaminfo.streamformat);
> +	if (!enc) {
> +		dev_dbg(dev,
> +			"%s V4L2 TRY_FMT (OUTPUT): unsupported format %.4s\n",
> +			ctx->name, (char *)&pixelformat);
> +		return -EINVAL;
> +	}
> +
> +	/* adjust width & height */
> +	width = pix->width;
> +	height = pix->height;
> +	v4l_bound_align_image(&pix->width,
> +			      HVA_MIN_WIDTH, HVA_MAX_WIDTH,
> +			      frame_alignment(pixelformat) - 1,
> +			      &pix->height,
> +			      HVA_MIN_HEIGHT, HVA_MAX_HEIGHT,
> +			      frame_alignment(pixelformat) - 1,
> +			      0);
> +
> +	if ((pix->width != width) || (pix->height != height))
> +		dev_dbg(dev,
> +			"%s V4L2 TRY_FMT (OUTPUT): resolution updated %dx%d -> %dx%d to fit min/max/alignment\n",
> +			ctx->name, width, height, pix->width, pix->height);
> +
> +	width = ALIGN(pix->width, HVA_WIDTH_ALIGNMENT);
> +	height = ALIGN(pix->height, HVA_HEIGHT_ALIGNMENT);
> +
> +	pix->bytesperline = frame_stride(width, pixelformat);
> +	pix->sizeimage = frame_size(width, height, pixelformat);
> +	pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
> +	pix->field = V4L2_FIELD_NONE;
> +
> +	return 0;
> +}
> +
> +static int hva_s_fmt_stream(struct file *file, void *fh, struct v4l2_format *f)
> +{
> +	struct hva_ctx *ctx = fh_to_ctx(file->private_data);
> +	struct device *dev = ctx_to_dev(ctx);
> +	struct vb2_queue *vq;
> +	int ret;
> +
> +	dev_dbg(dev, "%s V4L2 S_FMT (CAPTURE): %dx%d fmt:%.4s size:%d\n",
> +		ctx->name, f->fmt.pix.width, f->fmt.pix.height,
> +		(u8 *)&f->fmt.pix.pixelformat, f->fmt.pix.sizeimage);
> +
> +	ret = hva_try_fmt_stream(file, fh, f);
> +	if (ret) {
> +		dev_dbg(dev, "%s V4L2 S_FMT (CAPTURE): unsupported format %.4s\n",
> +			ctx->name, (char *)&f->fmt.pix.pixelformat);
> +		return ret;
> +	}
> +
> +	vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
> +	if (vb2_is_streaming(vq)) {
> +		dev_dbg(dev, "%s V4L2 S_FMT (CAPTURE): queue busy\n",
> +			ctx->name);
> +		return -EBUSY;
> +	}
> +
> +	ctx->max_stream_size = f->fmt.pix.sizeimage;
> +	ctx->streaminfo.width = f->fmt.pix.width;
> +	ctx->streaminfo.height = f->fmt.pix.height;
> +	ctx->streaminfo.streamformat = f->fmt.pix.pixelformat;
> +	ctx->flags |= HVA_FLAG_STREAMINFO;
> +
> +	return 0;
> +}
> +
> +static int hva_s_fmt_frame(struct file *file, void *fh, struct v4l2_format *f)
> +{
> +	struct hva_ctx *ctx = fh_to_ctx(file->private_data);
> +	struct device *dev = ctx_to_dev(ctx);
> +	struct v4l2_pix_format *pix = &f->fmt.pix;
> +	struct vb2_queue *vq;
> +	int ret;
> +
> +	dev_dbg(dev, "%s V4L2 S_FMT (OUTPUT): %dx%d fmt %.4s size %d\n",
> +		ctx->name, f->fmt.pix.width, f->fmt.pix.height,
> +		(u8 *)&f->fmt.pix.pixelformat, f->fmt.pix.sizeimage);
> +
> +	ret = hva_try_fmt_frame(file, fh, f);
> +	if (ret) {
> +		dev_dbg(dev, "%s V4L2 S_FMT (OUTPUT): unsupported format %.4s\n",
> +			ctx->name, (char *)&f->fmt.pix.pixelformat);
> +		return ret;
> +	}
> +
> +	vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
> +	if (vb2_is_streaming(vq)) {
> +		dev_dbg(dev, "%s V4L2 S_FMT (OUTPUT): queue busy\n", ctx->name);
> +		return -EBUSY;
> +	}
> +
> +	ctx->frameinfo.aligned_width = ALIGN(pix->width, HVA_WIDTH_ALIGNMENT);
> +	ctx->frameinfo.aligned_height = ALIGN(pix->height,
> +					      HVA_HEIGHT_ALIGNMENT);
> +
> +	ctx->frameinfo.size = pix->sizeimage;
> +	ctx->frameinfo.pixelformat = pix->pixelformat;
> +	ctx->frameinfo.width = pix->width;
> +	ctx->frameinfo.height = pix->height;
> +	ctx->flags |= HVA_FLAG_FRAMEINFO;
> +
> +	return 0;
> +}
> +
> +static int hva_s_parm(struct file *file, void *fh, struct v4l2_streamparm *sp)
> +{
> +	struct hva_ctx *ctx = fh_to_ctx(file->private_data);
> +	struct device *dev = ctx_to_dev(ctx);
> +	struct v4l2_fract *time_per_frame = &ctx->ctrls.time_per_frame;
> +
> +	time_per_frame->numerator = sp->parm.capture.timeperframe.numerator;
> +	time_per_frame->denominator =
> +		sp->parm.capture.timeperframe.denominator;
> +
> +	dev_dbg(dev, "%s set parameters %d/%d\n", ctx->name,
> +		time_per_frame->numerator, time_per_frame->denominator);
> +
> +	return 0;
> +}
> +
> +static int hva_g_parm(struct file *file, void *fh, struct v4l2_streamparm *sp)
> +{
> +	struct hva_ctx *ctx = fh_to_ctx(file->private_data);
> +	struct device *dev = ctx_to_dev(ctx);
> +	struct v4l2_fract *time_per_frame = &ctx->ctrls.time_per_frame;
> +
> +	sp->parm.capture.timeperframe.numerator = time_per_frame->numerator;
> +	sp->parm.capture.timeperframe.denominator =
> +		time_per_frame->denominator;
> +
> +	dev_dbg(dev, "%s get parameters %d/%d\n", ctx->name,
> +		time_per_frame->numerator, time_per_frame->denominator);
> +
> +	return 0;
> +}
> +
> +static int hva_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
> +{
> +	struct hva_ctx *ctx = fh_to_ctx(file->private_data);
> +	struct device *dev = ctx_to_dev(ctx);
> +
> +	if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
> +		/*
> +		 * depending on the targeted compressed video format, the
> +		 * capture buffer might contain headers (e.g. H.264 SPS/PPS)
> +		 * filled in by the driver client; the size of these data is
> +		 * copied from the bytesused field of the V4L2 buffer in the
> +		 * payload field of the hva stream buffer
> +		 */
> +		struct vb2_queue *vq;
> +		struct hva_stream *stream;
> +
> +		vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, buf->type);
> +
> +		if (buf->index >= vq->num_buffers) {
> +			dev_dbg(dev, "%s buffer index %d out of range (%d)\n",
> +				ctx->name, buf->index, vq->num_buffers);
> +			return -EINVAL;
> +		}
> +
> +		stream = (struct hva_stream *)vq->bufs[buf->index];
> +		stream->bytesused = buf->bytesused;
> +	}
> +
> +	return v4l2_m2m_qbuf(file, ctx->fh.m2m_ctx, buf);
> +}
> +
> +/* V4L2 ioctl ops */
> +static const struct v4l2_ioctl_ops hva_ioctl_ops = {
> +	.vidioc_querycap		= hva_querycap,
> +	.vidioc_enum_fmt_vid_cap	= hva_enum_fmt_stream,
> +	.vidioc_enum_fmt_vid_out	= hva_enum_fmt_frame,
> +	.vidioc_g_fmt_vid_cap		= hva_g_fmt_stream,
> +	.vidioc_g_fmt_vid_out		= hva_g_fmt_frame,
> +	.vidioc_try_fmt_vid_cap		= hva_try_fmt_stream,
> +	.vidioc_try_fmt_vid_out		= hva_try_fmt_frame,
> +	.vidioc_s_fmt_vid_cap		= hva_s_fmt_stream,
> +	.vidioc_s_fmt_vid_out		= hva_s_fmt_frame,
> +	.vidioc_g_parm			= hva_g_parm,
> +	.vidioc_s_parm			= hva_s_parm,
> +	.vidioc_reqbufs			= v4l2_m2m_ioctl_reqbufs,
> +	.vidioc_create_bufs             = v4l2_m2m_ioctl_create_bufs,
> +	.vidioc_querybuf		= v4l2_m2m_ioctl_querybuf,
> +	.vidioc_expbuf			= v4l2_m2m_ioctl_expbuf,
> +	.vidioc_qbuf			= hva_qbuf,
> +	.vidioc_dqbuf			= v4l2_m2m_ioctl_dqbuf,
> +	.vidioc_streamon		= v4l2_m2m_ioctl_streamon,
> +	.vidioc_streamoff		= v4l2_m2m_ioctl_streamoff,
> +	.vidioc_subscribe_event		= v4l2_ctrl_subscribe_event,
> +	.vidioc_unsubscribe_event	= v4l2_event_unsubscribe,
> +};
> +
> +/*
> + * V4L2 control operations
> + */
> +
> +static int hva_s_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	struct hva_ctx *ctx = container_of(ctrl->handler, struct hva_ctx,
> +					   ctrl_handler);
> +	struct device *dev = ctx_to_dev(ctx);
> +
> +	dev_dbg(dev, "%s S_CTRL: id = %d, val = %d\n", ctx->name,
> +		ctrl->id, ctrl->val);
> +
> +	switch (ctrl->id) {
> +	case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
> +		ctx->ctrls.bitrate_mode = ctrl->val;
> +		break;
> +	case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
> +		ctx->ctrls.gop_size = ctrl->val;
> +		break;
> +	case V4L2_CID_MPEG_VIDEO_BITRATE:
> +		ctx->ctrls.bitrate = ctrl->val;
> +		break;
> +	case V4L2_CID_MPEG_VIDEO_ASPECT:
> +		ctx->ctrls.aspect = ctrl->val;
> +		break;
> +	default:
> +		dev_dbg(dev, "%s S_CTRL: invalid control (id = %d)\n",
> +			ctx->name, ctrl->id);
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +/* V4L2 control ops */
> +static const struct v4l2_ctrl_ops hva_ctrl_ops = {
> +	.s_ctrl = hva_s_ctrl,
> +};
> +
> +static int hva_ctrls_setup(struct hva_ctx *ctx)
> +{
> +	struct device *dev = ctx_to_dev(ctx);
> +	u64 mask;
> +
> +	v4l2_ctrl_handler_init(&ctx->ctrl_handler, 4);
> +
> +	v4l2_ctrl_new_std_menu(&ctx->ctrl_handler, &hva_ctrl_ops,
> +			       V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
> +			       V4L2_MPEG_VIDEO_BITRATE_MODE_CBR,
> +			       0,
> +			       V4L2_MPEG_VIDEO_BITRATE_MODE_CBR);
> +
> +	v4l2_ctrl_new_std(&ctx->ctrl_handler, &hva_ctrl_ops,
> +			  V4L2_CID_MPEG_VIDEO_GOP_SIZE,
> +			  1, 60, 1, 16);
> +
> +	v4l2_ctrl_new_std(&ctx->ctrl_handler, &hva_ctrl_ops,
> +			  V4L2_CID_MPEG_VIDEO_BITRATE,
> +			  1, 50000, 1, 20000);
> +
> +	mask = ~(1 << V4L2_MPEG_VIDEO_ASPECT_1x1);
> +	v4l2_ctrl_new_std_menu(&ctx->ctrl_handler, &hva_ctrl_ops,
> +			       V4L2_CID_MPEG_VIDEO_ASPECT,
> +			       V4L2_MPEG_VIDEO_ASPECT_1x1,
> +			       mask,
> +			       V4L2_MPEG_VIDEO_ASPECT_1x1);
> +
> +	if (ctx->ctrl_handler.error) {
> +		int err = ctx->ctrl_handler.error;
> +
> +		dev_dbg(dev, "%s controls setup failed (%d)\n",
> +			ctx->name, err);
> +		v4l2_ctrl_handler_free(&ctx->ctrl_handler);
> +		return err;
> +	}
> +
> +	v4l2_ctrl_handler_setup(&ctx->ctrl_handler);
> +
> +	/* set default time per frame */
> +	ctx->ctrls.time_per_frame.numerator = DEFAULT_FRAME_NUM;
> +	ctx->ctrls.time_per_frame.denominator = DEFAULT_FRAME_DEN;
> +
> +	return 0;
> +}
> +
> +/*
> + * mem-to-mem operations
> + */
> +
> +static void hva_run_work(struct work_struct *work)
> +{
> +	struct hva_ctx *ctx = container_of(work, struct hva_ctx, run_work);
> +	struct vb2_v4l2_buffer *src_buf, *dst_buf;
> +	const struct hva_enc *enc = ctx->enc;
> +	struct hva_frame *frame;
> +	struct hva_stream *stream;
> +	int ret;
> +
> +	/* protect instance against reentrancy */
> +	mutex_lock(&ctx->lock);
> +
> +	src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
> +	dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
> +
> +	frame = to_hva_frame(src_buf);
> +	stream = to_hva_stream(dst_buf);
> +	frame->vbuf.sequence = ctx->frame_num++;
> +
> +	ret = enc->encode(ctx, frame, stream);
> +
> +	if (ret) {
> +		v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR);
> +		v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR);
> +	} else {
> +		/* propagate frame timestamp */
> +		dst_buf->vb2_buf.timestamp = src_buf->vb2_buf.timestamp;
> +		dst_buf->field = V4L2_FIELD_NONE;
> +		dst_buf->sequence = ctx->stream_num - 1;
> +
> +		v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE);
> +		v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE);
> +	}
> +
> +	mutex_unlock(&ctx->lock);
> +
> +	v4l2_m2m_job_finish(ctx->hva_dev->m2m_dev, ctx->fh.m2m_ctx);
> +}
> +
> +static void hva_device_run(void *priv)
> +{
> +	struct hva_ctx *ctx = priv;
> +	struct hva_dev *hva = ctx_to_hdev(ctx);
> +
> +	queue_work(hva->work_queue, &ctx->run_work);
> +}
> +
> +static void hva_job_abort(void *priv)
> +{
> +	struct hva_ctx *ctx = priv;
> +	struct device *dev = ctx_to_dev(ctx);
> +
> +	dev_dbg(dev, "%s aborting job\n", ctx->name);
> +
> +	ctx->aborting = true;
> +}
> +
> +static int hva_job_ready(void *priv)
> +{
> +	struct hva_ctx *ctx = priv;
> +	struct device *dev = ctx_to_dev(ctx);
> +
> +	if (!v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx)) {
> +		dev_dbg(dev, "%s job not ready: no frame buffers\n",
> +			ctx->name);
> +		return 0;
> +	}
> +
> +	if (!v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx)) {
> +		dev_dbg(dev, "%s job not ready: no stream buffers\n",
> +			ctx->name);
> +		return 0;
> +	}
> +
> +	if (ctx->aborting) {
> +		dev_dbg(dev, "%s job not ready: aborting\n", ctx->name);
> +		return 0;
> +	}
> +
> +	return 1;
> +}
> +
> +/* mem-to-mem ops */
> +static const struct v4l2_m2m_ops hva_m2m_ops = {
> +	.device_run	= hva_device_run,
> +	.job_abort	= hva_job_abort,
> +	.job_ready	= hva_job_ready,
> +};
> +
> +/*
> + * VB2 queue operations
> + */
> +
> +static int hva_queue_setup(struct vb2_queue *vq,
> +			   unsigned int *num_buffers, unsigned int *num_planes,
> +			   unsigned int sizes[], void *alloc_ctxs[])
> +{
> +	struct hva_ctx *ctx = vb2_get_drv_priv(vq);
> +	struct device *dev = ctx_to_dev(ctx);
> +	unsigned int size;
> +
> +	dev_dbg(dev, "%s %s queue setup: num_buffers %d\n", ctx->name,
> +		to_type_str(vq->type), *num_buffers);
> +
> +	size = vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT ?
> +		ctx->frameinfo.size : ctx->max_stream_size;
> +
> +	alloc_ctxs[0] = ctx->hva_dev->alloc_ctx;
> +
> +	if (*num_planes)
> +		return sizes[0] < size ? -EINVAL : 0;
> +
> +	/* only one plane supported */
> +	*num_planes = 1;
> +	sizes[0] = size;
> +
> +	return 0;
> +}
> +
> +static int hva_buf_prepare(struct vb2_buffer *vb)
> +{
> +	struct hva_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
> +	struct device *dev = ctx_to_dev(ctx);
> +	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
> +
> +	if (vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
> +		struct hva_frame *frame = to_hva_frame(vbuf);
> +
> +		if (vbuf->field == V4L2_FIELD_ANY)
> +			vbuf->field = V4L2_FIELD_NONE;
> +		if (vbuf->field != V4L2_FIELD_NONE) {
> +			dev_dbg(dev,
> +				"%s frame[%d] prepare: %d field not supported\n",
> +				ctx->name, vb->index, vbuf->field);
> +			return -EINVAL;
> +		}
> +
> +		if (!frame->prepared) {
> +			/* get memory addresses */
> +			frame->vaddr = vb2_plane_vaddr(&vbuf->vb2_buf, 0);
> +			frame->paddr = vb2_dma_contig_plane_dma_addr(
> +					&vbuf->vb2_buf, 0);
> +			frame->info = ctx->frameinfo;
> +			frame->prepared = true;
> +
> +			dev_dbg(dev,
> +				"%s frame[%d] prepared; virt=%p, phy=%pad\n",
> +				ctx->name, vb->index,
> +				frame->vaddr, &frame->paddr);
> +		}
> +	} else {
> +		struct hva_stream *stream = to_hva_stream(vbuf);
> +
> +		if (!stream->prepared) {
> +			/* get memory addresses */
> +			stream->vaddr = vb2_plane_vaddr(&vbuf->vb2_buf, 0);
> +			stream->paddr = vb2_dma_contig_plane_dma_addr(
> +					&vbuf->vb2_buf, 0);
> +			stream->size = vb2_plane_size(&vbuf->vb2_buf, 0);
> +			stream->prepared = true;
> +
> +			dev_dbg(dev,
> +				"%s stream[%d] prepared; virt=%p, phy=%pad\n",
> +				ctx->name, vb->index,
> +				stream->vaddr, &stream->paddr);
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static void hva_buf_queue(struct vb2_buffer *vb)
> +{
> +	struct hva_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
> +	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
> +
> +	if (ctx->fh.m2m_ctx)
> +		v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
> +}
> +
> +static int hva_start_streaming(struct vb2_queue *vq, unsigned int count)
> +{
> +	struct hva_ctx *ctx = vb2_get_drv_priv(vq);
> +	struct device *dev = ctx_to_dev(ctx);
> +	int ret = 0;
> +
> +	dev_dbg(dev, "%s %s start streaming\n", ctx->name,
> +		to_type_str(vq->type));
> +
> +	/* open encoder when both start_streaming have been called */
> +	if (V4L2_TYPE_IS_OUTPUT(vq->type)) {
> +		if (!vb2_start_streaming_called(&ctx->fh.m2m_ctx->cap_q_ctx.q))
> +			return 0;
> +	} else {
> +		if (!vb2_start_streaming_called(&ctx->fh.m2m_ctx->out_q_ctx.q))
> +			return 0;
> +	}
> +
> +	if (!ctx->enc)
> +		ret = hva_open_encoder(ctx,
> +				       ctx->streaminfo.streamformat,
> +				       ctx->frameinfo.pixelformat,
> +				       &ctx->enc);
> +
> +	return ret;
> +}
> +
> +static void hva_stop_streaming(struct vb2_queue *vq)
> +{
> +	struct hva_ctx *ctx = vb2_get_drv_priv(vq);
> +	struct device *dev = ctx_to_dev(ctx);
> +	const struct hva_enc *enc = ctx->enc;
> +	struct vb2_v4l2_buffer *vbuf;
> +
> +	dev_dbg(dev, "%s %s stop streaming\n", ctx->name,
> +		to_type_str(vq->type));
> +
> +	if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
> +		/* return of all pending buffers to vb2 (in error state) */
> +		ctx->frame_num = 0;
> +		while ((vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx)))
> +			v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
> +	} else {
> +		/* return of all pending buffers to vb2 (in error state) */
> +		ctx->stream_num = 0;
> +		while ((vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx)))
> +			v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
> +	}
> +
> +	if ((V4L2_TYPE_IS_OUTPUT(vq->type) &&
> +	     vb2_is_streaming(&ctx->fh.m2m_ctx->cap_q_ctx.q)) ||
> +	    (!V4L2_TYPE_IS_OUTPUT(vq->type) &&
> +	     vb2_is_streaming(&ctx->fh.m2m_ctx->out_q_ctx.q))) {
> +		dev_dbg(dev, "%s %s out=%d cap=%d\n",
> +			ctx->name, to_type_str(vq->type),
> +			vb2_is_streaming(&ctx->fh.m2m_ctx->out_q_ctx.q),
> +			vb2_is_streaming(&ctx->fh.m2m_ctx->cap_q_ctx.q));
> +		return;
> +	}
> +
> +	/* close encoder when both stop_streaming have been called */
> +	if (enc) {
> +		dev_dbg(dev, "%s %s encoder closed\n", ctx->name, enc->name);
> +		enc->close(ctx);
> +		ctx->enc = NULL;
> +	}
> +
> +	ctx->aborting = false;
> +}
> +
> +/* VB2 queue ops */
> +static const struct vb2_ops hva_qops = {
> +	.queue_setup		= hva_queue_setup,
> +	.buf_prepare		= hva_buf_prepare,
> +	.buf_queue		= hva_buf_queue,
> +	.start_streaming	= hva_start_streaming,
> +	.stop_streaming		= hva_stop_streaming,
> +	.wait_prepare		= vb2_ops_wait_prepare,
> +	.wait_finish		= vb2_ops_wait_finish,
> +};
> +
> +/*
> + * V4L2 file operations
> + */
> +
> +static int queue_init(struct hva_ctx *ctx, struct vb2_queue *vq)
> +{
> +	vq->io_modes = VB2_MMAP | VB2_DMABUF;
> +	vq->drv_priv = ctx;
> +	vq->ops = &hva_qops;
> +	vq->mem_ops = &vb2_dma_contig_memops;
> +	vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
> +	vq->lock = &ctx->hva_dev->lock;
> +
> +	return vb2_queue_init(vq);
> +}
> +
> +static int hva_queue_init(void *priv, struct vb2_queue *src_vq,
> +			  struct vb2_queue *dst_vq)
> +{
> +	struct hva_ctx *ctx = priv;
> +	int ret;
> +
> +	src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
> +	src_vq->buf_struct_size = sizeof(struct hva_frame);
> +	src_vq->min_buffers_needed = MIN_FRAMES;
> +
> +	ret = queue_init(ctx, src_vq);
> +	if (ret)
> +		return ret;
> +
> +	dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> +	dst_vq->buf_struct_size = sizeof(struct hva_stream);
> +	dst_vq->min_buffers_needed = MIN_STREAMS;
> +
> +	return queue_init(ctx, dst_vq);
> +}
> +
> +static int hva_open(struct file *file)
> +{
> +	struct hva_dev *hva = video_drvdata(file);
> +	struct device *dev = hva_to_dev(hva);
> +	struct hva_ctx *ctx;
> +	int ret;
> +	unsigned int i;
> +	bool found = false;
> +
> +	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
> +	if (!ctx) {
> +		ret = -ENOMEM;
> +		goto out;
> +	}
> +	ctx->hva_dev = hva;
> +
> +	mutex_lock(&hva->lock);
> +
> +	/* store the instance context in the instances array */
> +	for (i = 0; i < HVA_MAX_INSTANCES; i++) {
> +		if (!hva->instances[i]) {
> +			hva->instances[i] = ctx;
> +			/* save the context identifier in the context */
> +			ctx->id = i;
> +			found = true;
> +			break;
> +		}
> +	}
> +
> +	if (!found) {
> +		dev_err(dev, "%s [x:x] maximum instances reached\n",
> +			HVA_PREFIX);
> +		ret = -ENOMEM;
> +		goto mem_ctx;
> +	}
> +
> +	INIT_WORK(&ctx->run_work, hva_run_work);
> +	v4l2_fh_init(&ctx->fh, video_devdata(file));
> +	file->private_data = &ctx->fh;
> +	v4l2_fh_add(&ctx->fh);
> +
> +	ret = hva_ctrls_setup(ctx);
> +	if (ret) {
> +		dev_err(dev, "%s [x:x] failed to setup controls\n",
> +			HVA_PREFIX);
> +		goto err_fh;
> +	}
> +	ctx->fh.ctrl_handler = &ctx->ctrl_handler;
> +
> +	mutex_init(&ctx->lock);
> +
> +	ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(hva->m2m_dev, ctx,
> +					    &hva_queue_init);
> +	if (IS_ERR(ctx->fh.m2m_ctx)) {
> +		ret = PTR_ERR(ctx->fh.m2m_ctx);
> +		dev_err(dev, "%s [x:x] failed to initialize m2m context (%d)\n",
> +			HVA_PREFIX, ret);
> +		goto err_ctrls;
> +	}
> +
> +	/* set the instance name */
> +	hva->instance_id++;
> +	snprintf(ctx->name, sizeof(ctx->name), "[%3d:----]",
> +		 hva->instance_id);
> +
> +	hva->nb_of_instances++;
> +
> +	mutex_unlock(&hva->lock);
> +
> +	/* default parameters for frame and stream */
> +	set_default_params(ctx);
> +
> +	dev_info(dev, "%s encoder instance created (id %d)\n",
> +		 ctx->name, ctx->id);
> +
> +	return 0;
> +
> +err_ctrls:
> +	v4l2_ctrl_handler_free(&ctx->ctrl_handler);
> +err_fh:
> +	v4l2_fh_del(&ctx->fh);
> +	v4l2_fh_exit(&ctx->fh);
> +	hva->instances[ctx->id] = NULL;
> +mem_ctx:
> +	kfree(ctx);
> +	mutex_unlock(&hva->lock);
> +out:
> +	return ret;
> +}
> +
> +static int hva_release(struct file *file)
> +{
> +	struct hva_dev *hva = video_drvdata(file);
> +	struct hva_ctx *ctx = fh_to_ctx(file->private_data);
> +	struct device *dev = ctx_to_dev(ctx);
> +
> +	v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
> +
> +	v4l2_ctrl_handler_free(&ctx->ctrl_handler);
> +
> +	v4l2_fh_del(&ctx->fh);
> +	v4l2_fh_exit(&ctx->fh);
> +
> +	mutex_lock(&hva->lock);
> +
> +	/* clear instance context in instances array */
> +	hva->instances[ctx->id] = NULL;
> +
> +	hva->nb_of_instances--;
> +
> +	mutex_unlock(&hva->lock);
> +
> +	dev_info(dev, "%s encoder instance released (id %d)\n",
> +		 ctx->name, ctx->id);
> +
> +	kfree(ctx);
> +
> +	return 0;
> +}
> +
> +/* V4L2 file ops */
> +static const struct v4l2_file_operations hva_fops = {
> +	.owner			= THIS_MODULE,
> +	.open			= hva_open,
> +	.release		= hva_release,
> +	.unlocked_ioctl		= video_ioctl2,
> +	.mmap			= v4l2_m2m_fop_mmap,
> +	.poll			= v4l2_m2m_fop_poll,
> +};
> +
> +/*
> + * Platform device operations
> + */
> +
> +static int hva_register_device(struct hva_dev *hva)
> +{
> +	int ret;
> +	struct video_device *vdev;
> +	struct device *dev;
> +
> +	if (!hva)
> +		return -ENODEV;
> +	dev = hva_to_dev(hva);
> +
> +	hva->m2m_dev = v4l2_m2m_init(&hva_m2m_ops);
> +	if (IS_ERR(hva->m2m_dev)) {
> +		dev_err(dev, "%s %s failed to initialize v4l2-m2m device\n",
> +			HVA_PREFIX, HVA_NAME);
> +		ret = PTR_ERR(hva->m2m_dev);
> +		goto err;
> +	}
> +
> +	vdev = video_device_alloc();
> +	if (!vdev) {
> +		dev_err(dev, "%s %s failed to allocate video device\n",
> +			HVA_PREFIX, HVA_NAME);
> +		ret = -ENOMEM;
> +		goto err_m2m_release;
> +	}
> +
> +	vdev->fops = &hva_fops;
> +	vdev->ioctl_ops = &hva_ioctl_ops;
> +	vdev->release = video_device_release;
> +	vdev->lock = &hva->lock;
> +	vdev->vfl_dir = VFL_DIR_M2M;
> +	vdev->v4l2_dev = &hva->v4l2_dev;
> +	snprintf(vdev->name, sizeof(vdev->name), "%s", HVA_NAME);
> +
> +	ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
> +	if (ret) {
> +		dev_err(dev, "%s %s failed to register video device\n",
> +			HVA_PREFIX, HVA_NAME);
> +		goto err_vdev_release;
> +	}
> +
> +	hva->vdev = vdev;
> +	video_set_drvdata(vdev, hva);
> +	return 0;
> +
> +err_vdev_release:
> +	video_device_release(vdev);
> +err_m2m_release:
> +	v4l2_m2m_release(hva->m2m_dev);
> +err:
> +	return ret;
> +}
> +
> +static void hva_unregister_device(struct hva_dev *hva)
> +{
> +	if (!hva)
> +		return;
> +
> +	if (hva->m2m_dev)
> +		v4l2_m2m_release(hva->m2m_dev);
> +
> +	video_unregister_device(hva->vdev);
> +}
> +
> +static int hva_probe(struct platform_device *pdev)
> +{
> +	struct hva_dev *hva;
> +	struct device *dev = &pdev->dev;
> +	int ret;
> +
> +	hva = devm_kzalloc(dev, sizeof(*hva), GFP_KERNEL);
> +	if (!hva) {
> +		ret = -ENOMEM;
> +		goto err;
> +	}
> +
> +	hva->dev = dev;
> +	hva->pdev = pdev;
> +	platform_set_drvdata(pdev, hva);
> +
> +	mutex_init(&hva->lock);
> +
> +	/* probe hardware */
> +	ret = hva_hw_probe(pdev, hva);
> +	if (ret)
> +		goto err;
> +
> +	/* register all available encoders */
> +	register_encoders(hva);
> +
> +	/* register all supported formats */
> +	register_formats(hva);
> +
> +	/* register on V4L2 */
> +	ret = v4l2_device_register(dev, &hva->v4l2_dev);
> +	if (ret) {
> +		dev_err(dev, "%s %s failed to register V4L2 device\n",
> +			HVA_PREFIX, HVA_NAME);
> +		goto err_hw;
> +	}
> +
> +	/* continuous memory allocator */
> +	hva->alloc_ctx = vb2_dma_contig_init_ctx(dev);
> +	if (IS_ERR(hva->alloc_ctx)) {
> +		ret = PTR_ERR(hva->alloc_ctx);
> +		goto err_v4l2;
> +	}
> +
> +	hva->work_queue = create_workqueue(HVA_NAME);
> +	if (!hva->work_queue) {
> +		dev_err(dev, "%s %s failed to allocate work queue\n",
> +			HVA_PREFIX, HVA_NAME);
> +		ret = -ENOMEM;
> +		goto err_vb2_dma;
> +	}
> +
> +	/* register device */
> +	ret = hva_register_device(hva);
> +	if (ret)
> +		goto err_work_queue;
> +
> +	dev_info(dev, "%s %s registered as /dev/video%d\n", HVA_PREFIX,
> +		 HVA_NAME, hva->vdev->num);
> +
> +	return 0;
> +
> +err_work_queue:
> +	destroy_workqueue(hva->work_queue);
> +err_vb2_dma:
> +	vb2_dma_contig_cleanup_ctx(hva->alloc_ctx);
> +err_v4l2:
> +	v4l2_device_unregister(&hva->v4l2_dev);
> +err_hw:
> +	hva_hw_remove(hva);
> +err:
> +	return ret;
> +}
> +
> +static int hva_remove(struct platform_device *pdev)
> +{
> +	struct hva_dev *hva = platform_get_drvdata(pdev);
> +	struct device *dev = hva_to_dev(hva);
> +
> +	hva_unregister_device(hva);
> +
> +	destroy_workqueue(hva->work_queue);
> +
> +	vb2_dma_contig_cleanup_ctx(hva->alloc_ctx);
> +
> +	hva_hw_remove(hva);
> +
> +	v4l2_device_unregister(&hva->v4l2_dev);
> +
> +	dev_info(dev, "%s %s removed\n", HVA_PREFIX, pdev->name);
> +
> +	return 0;
> +}
> +
> +/* PM ops */
> +static const struct dev_pm_ops hva_pm_ops = {
> +	.runtime_suspend	= hva_hw_runtime_suspend,
> +	.runtime_resume		= hva_hw_runtime_resume,
> +};
> +
> +static const struct of_device_id hva_match_types[] = {
> +	{
> +	 .compatible = "st,sti-hva",
> +	},
> +	{ /* end node */ }
> +};
> +
> +MODULE_DEVICE_TABLE(of, hva_match_types);
> +
> +struct platform_driver hva_driver = {
> +	.probe  = hva_probe,
> +	.remove = hva_remove,
> +	.driver = {
> +		.name           = HVA_NAME,
> +		.owner          = THIS_MODULE,
> +		.of_match_table = hva_match_types,
> +		.pm             = &hva_pm_ops,
> +		},
> +};
> +
> +module_platform_driver(hva_driver);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>");
> +MODULE_DESCRIPTION("HVA video encoder V4L2 driver");
> diff --git a/drivers/media/platform/sti/hva/hva.h b/drivers/media/platform/sti/hva/hva.h
> new file mode 100644
> index 0000000..9a1b503b
> --- /dev/null
> +++ b/drivers/media/platform/sti/hva/hva.h
> @@ -0,0 +1,284 @@
> +/*
> + * Copyright (C) STMicroelectronics SA 2015
> + * Authors: Yannick Fertre <yannick.fertre@st.com>
> + *          Hugues Fruchet <hugues.fruchet@st.com>
> + * License terms:  GNU General Public License (GPL), version 2
> + */
> +
> +#ifndef HVA_H
> +#define HVA_H
> +
> +#include 
> +#include 
> +#include 
> +#include 
> +
> +#define fh_to_ctx(f)    (container_of(f, struct hva_ctx, fh))
> +
> +#define hva_to_dev(h)   (h->dev)
> +
> +#define ctx_to_dev(c)   (c->hva_dev->dev)
> +
> +#define ctx_to_hdev(c)  (c->hva_dev)
> +
> +#define HVA_PREFIX "[---:----]"
> +
> +/**
> + * struct hva_frameinfo - information about hva frame
> + *
> + * @pixelformat:    fourcc code for uncompressed video format
> + * @width:          width of frame
> + * @height:         height of frame
> + * @aligned_width:  width of frame (with encoder alignment constraint)
> + * @aligned_height: height of frame (with encoder alignment constraint)
> + * @size:           maximum size in bytes required for data
> +*/
> +struct hva_frameinfo {
> +	u32	pixelformat;
> +	u32	width;
> +	u32	height;
> +	u32	aligned_width;
> +	u32	aligned_height;
> +	u32	size;
> +};
> +
> +/**
> + * struct hva_streaminfo - information about hva stream
> + *
> + * @streamformat: fourcc code of compressed video format (H.264...)
> + * @width:        width of stream
> + * @height:       height of stream
> + * @profile:      profile string
> + * @level:        level string
> + */
> +struct hva_streaminfo {
> +	u32	streamformat;
> +	u32	width;
> +	u32	height;
> +	u8	profile[32];
> +	u8	level[32];
> +};
> +
> +/**
> + * struct hva_controls - hva controls set
> + *
> + * @time_per_frame: time per frame in seconds
> + * @bitrate_mode:   bitrate mode (constant bitrate or variable bitrate)
> + * @gop_size:       groupe of picture size
> + * @bitrate:        bitrate (in kbps)
> + * @aspect:         video aspect
> + */
> +struct hva_controls {
> +	struct v4l2_fract			time_per_frame;
> +	enum v4l2_mpeg_video_bitrate_mode	bitrate_mode;
> +	u32					gop_size;
> +	u32					bitrate;
> +	enum v4l2_mpeg_video_aspect		aspect;
> +};
> +
> +/**
> + * struct hva_frame - hva frame buffer (output)
> + *
> + * @vbuf:     video buffer information for V4L2
> + * @list:     V4L2 m2m list that the frame belongs to
> + * @info:     frame information (width, height, format, alignment...)
> + * @paddr:    physical address (for hardware)
> + * @vaddr:    virtual address (kernel can read/write)
> + * @prepared: true if vaddr/paddr are resolved
> + */
> +struct hva_frame {
> +	struct vb2_v4l2_buffer	vbuf;
> +	struct list_head	list;
> +	struct hva_frameinfo	info;
> +	dma_addr_t		paddr;
> +	void			*vaddr;
> +	bool			prepared;
> +};
> +
> +/*
> + * to_hva_frame() - cast struct vb2_v4l2_buffer * to struct hva_frame *
> + */
> +#define to_hva_frame(vb) \
> +	container_of(vb, struct hva_frame, vbuf)
> +
> +/**
> + * struct hva_stream - hva stream buffer (capture)
> + *
> + * @v4l2:       video buffer information for V4L2
> + * @list:       V4L2 m2m list that the frame belongs to
> + * @paddr:      physical address (for hardware)
> + * @vaddr:      virtual address (kernel can read/write)
> + * @prepared:   true if vaddr/paddr are resolved
> + * @size:       size of the buffer in bytes
> + * @bytesused:  number of bytes occupied by data in the buffer
> + */
> +struct hva_stream {
> +	struct vb2_v4l2_buffer	vbuf;
> +	struct list_head	list;
> +	dma_addr_t		paddr;
> +	void			*vaddr;
> +	int			prepared;
> +	unsigned int		size;
> +	unsigned int		bytesused;
> +};
> +
> +/*
> + * to_hva_stream() - cast struct vb2_v4l2_buffer * to struct hva_stream *
> + */
> +#define to_hva_stream(vb) \
> +	container_of(vb, struct hva_stream, vbuf)
> +
> +struct hva_dev;
> +struct hva_enc;
> +
> +/**
> + * struct hva_ctx - context of hva instance
> + *
> + * @hva_dev:         the device that this instance is associated with
> + * @fh:              V4L2 file handle
> + * @ctrl_handler:    V4L2 controls handler
> + * @ctrls:           hva controls set
> + * @id:              instance identifier
> + * @aborting:        true if current job aborted
> + * @name:            instance name (debug purpose)
> + * @run_work:        encode work
> + * @lock:            mutex used to lock access of this context
> + * @flags:           validity of streaminfo and frameinfo fields
> + * @frame_num:       frame number
> + * @stream_num:      stream number
> + * @max_stream_size: maximum size in bytes required for stream data
> + * @streaminfo:      stream properties
> + * @frameinfo:       frame properties
> + * @enc:             current encoder
> + * @priv:            private codec data for this instance, allocated
> + *                   by encoder @open time
> + * @hw_err:          true if hardware error detected
> + */
> +struct hva_ctx {
> +	struct hva_dev		        *hva_dev;
> +	struct v4l2_fh			fh;
> +	struct v4l2_ctrl_handler	ctrl_handler;
> +	struct hva_controls		ctrls;
> +	u8				id;
> +	bool				aborting;
> +	char				name[100];
> +	struct work_struct		run_work;
> +	/* mutex protecting this data structure */
> +	struct mutex			lock;
> +	u32				flags;
> +	u32				frame_num;
> +	u32				stream_num;
> +	u32				max_stream_size;
> +	struct hva_streaminfo		streaminfo;
> +	struct hva_frameinfo		frameinfo;
> +	struct hva_enc			*enc;
> +	void				*priv;
> +	bool				hw_err;
> +};
> +
> +#define HVA_FLAG_STREAMINFO	0x0001
> +#define HVA_FLAG_FRAMEINFO	0x0002
> +
> +#define HVA_MAX_INSTANCES	16
> +#define HVA_MAX_ENCODERS	10
> +#define HVA_MAX_FORMATS		HVA_MAX_ENCODERS
> +
> +/**
> + * struct hva_dev - abstraction for hva entity
> + *
> + * @v4l2_dev:            V4L2 device
> + * @vdev:                video device
> + * @pdev:                platform device
> + * @dev:                 device
> + * @lock:                mutex used for critical sections & V4L2 ops
> + *                       serialization
> + * @m2m_dev:             memory-to-memory V4L2 device informatio
> + * @alloc_ctx:           videobuf2 memory allocator context
> + * @instances:           opened instances
> + * @nb_of_instances:     number of opened instances
> + * @instance_id:         rolling counter identifying an instance (debug purpose)
> + * @regs:                register io memory access
> + * @esram_addr:          esram address
> + * @esram_size:          esram size
> + * @clk:                 hva clock
> + * @irq_its:             status interruption
> + * @irq_err:             error interruption
> + * @work_queue:          work queue to handle the encode jobs
> + * @protect_mutex:       mutex used to lock access of hardware
> + * @interrupt:           completion interrupt
> + * @ip_version:          IP hardware version
> + * @encoders:            registered encoders
> + * @nb_of_encoders:      number of registered encoders
> + * @pixelformats:        supported uncompressed video formats
> + * @nb_of_pixelformats:  number of supported umcompressed video formats
> + * @streamformats:       supported compressed video formats
> + * @nb_of_streamformats: number of supported compressed video formats
> + * @sfl_reg:             status fifo level register value
> + * @sts_reg:             status register value
> + * @lmi_err_reg:         local memory interface error register value
> + * @emi_err_reg:         external memory interface error register value
> + * @hec_mif_err_reg:     HEC memory interface error register value
> + */
> +struct hva_dev {
> +	struct v4l2_device	v4l2_dev;
> +	struct video_device	*vdev;
> +	struct platform_device	*pdev;
> +	struct device		*dev;
> +	/* mutex protecting vb2_queue structure */
> +	struct mutex		lock;
> +	struct v4l2_m2m_dev	*m2m_dev;
> +	struct vb2_alloc_ctx	*alloc_ctx;
> +	struct hva_ctx		*instances[HVA_MAX_INSTANCES];
> +	unsigned int		nb_of_instances;
> +	unsigned int		instance_id;
> +	void __iomem		*regs;
> +	u32			esram_addr;
> +	u32			esram_size;
> +	struct clk		*clk;
> +	int			irq_its;
> +	int			irq_err;
> +	struct workqueue_struct *work_queue;
> +	/* mutex protecting hardware access */
> +	struct mutex		protect_mutex;
> +	struct completion	interrupt;
> +	unsigned long int	ip_version;
> +	const struct hva_enc	*encoders[HVA_MAX_ENCODERS];
> +	u32			nb_of_encoders;
> +	u32			pixelformats[HVA_MAX_FORMATS];
> +	u32			nb_of_pixelformats;
> +	u32			streamformats[HVA_MAX_FORMATS];
> +	u32			nb_of_streamformats;
> +	u32			sfl_reg;
> +	u32			sts_reg;
> +	u32			lmi_err_reg;
> +	u32			emi_err_reg;
> +	u32			hec_mif_err_reg;
> +};
> +
> +/**
> + * struct hva_enc - hva encoder
> + *
> + * @name:         encoder name
> + * @streamformat: fourcc code for compressed video format (H.264...)
> + * @pixelformat:  fourcc code for uncompressed video format
> + * @max_width:    maximum width of frame for this encoder
> + * @max_height:   maximum height of frame for this encoder
> + * @open:         open encoder
> + * @close:        close encoder
> + * @encode:       encode a frame (struct hva_frame) in a stream
> + *                (struct hva_stream)
> + */
> +
> +struct hva_enc {
> +	const char	*name;
> +	u32		streamformat;
> +	u32		pixelformat;
> +	u32		max_width;
> +	u32		max_height;
> +	int		(*open)(struct hva_ctx *ctx);
> +	int		(*close)(struct hva_ctx *ctx);
> +	int		(*encode)(struct hva_ctx *ctx, struct hva_frame *frame,
> +				  struct hva_stream *stream);
> +};
> +
> +#endif /* HVA_H */

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 181 bytes --]

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v2 0/3] support of v4l2 encoder for STMicroelectronics SOC
  2016-07-11 17:48 ` [PATCH v2 0/3] support of v4l2 encoder for STMicroelectronics SOC Nicolas Dufresne
@ 2016-07-11 18:57   ` Javier Martinez Canillas
  2016-07-13 13:49     ` Jean Christophe TROTIN
  0 siblings, 1 reply; 18+ messages in thread
From: Javier Martinez Canillas @ 2016-07-11 18:57 UTC (permalink / raw)
  To: Jean-Christophe Trotin
  Cc: nicolas, Linux Media Mailing List, Hans Verkuil, kernel,
	Benjamin Gaignard, Yannick Fertre, Hugues Fruchet

On Mon, Jul 11, 2016 at 1:48 PM, Nicolas Dufresne
<nicolas.dufresne@gmail.com> wrote:
> Le lundi 11 juillet 2016 à 17:14 +0200, Jean-Christophe Trotin a
> écrit :

[snip]

>>
>> Below is the v4l2-compliance report for the version 2 of the sti hva
>> driver:
>>
>>
>> root@sti-next:/home/video_test# v4l2-compliance -d /dev/video0
>> Driver Info:
>>       Driver name   : 8c85000.hva
>
> I think it would be nice to set a driver name that means something.
>
>>       Card type     : 8c85000.hva

Agreed, same for Card type. The VIDIOC_QUERYCAP ioctl documentation
explains what information these fields should contain:

https://linuxtv.org/downloads/v4l-dvb-apis/vidioc-querycap.html

For example https://git.linuxtv.org/media_tree.git/commit/?id=e0d80c8acca0f221b9dedb2eab7a5184848b99b7

Best regards,
Javier

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v2 0/3] support of v4l2 encoder for STMicroelectronics SOC
  2016-07-11 18:57   ` Javier Martinez Canillas
@ 2016-07-13 13:49     ` Jean Christophe TROTIN
  2016-07-13 14:00       ` Javier Martinez Canillas
  0 siblings, 1 reply; 18+ messages in thread
From: Jean Christophe TROTIN @ 2016-07-13 13:49 UTC (permalink / raw)
  To: Javier Martinez Canillas
  Cc: nicolas, Linux Media Mailing List, Hans Verkuil, kernel,
	Benjamin Gaignard, Yannick FERTRE, Hugues FRUCHET



On 07/11/2016 08:57 PM, Javier Martinez Canillas wrote:
> On Mon, Jul 11, 2016 at 1:48 PM, Nicolas Dufresne
> <nicolas.dufresne@gmail.com> wrote:
>> Le lundi 11 juillet 2016 à 17:14 +0200, Jean-Christophe Trotin a
>> écrit :
>
> [snip]
>
>>>
>>> Below is the v4l2-compliance report for the version 2 of the sti hva
>>> driver:
>>>
>>>
>>> root@sti-next:/home/video_test# v4l2-compliance -d /dev/video0
>>> Driver Info:
>>>        Driver name   : 8c85000.hva
>>
>> I think it would be nice to set a driver name that means something.
>>
>>>        Card type     : 8c85000.hva
>
> Agreed, same for Card type. The VIDIOC_QUERYCAP ioctl documentation
> explains what information these fields should contain:
>
> https://linuxtv.org/downloads/v4l-dvb-apis/vidioc-querycap.html
>
> For example https://git.linuxtv.org/media_tree.git/commit/?id=e0d80c8acca0f221b9dedb2eab7a5184848b99b7
>
> Best regards,
> Javier
>

Nicolas and Javier,

Thank you for the remarks.
I will modify the code in version 3 so that "driver" contains the name of the 
encoder ("hva"), "card" identifies the hardware version ("hva<hw_ip_version>" 
with <hw_ip_version> equal to 400 here, which leads to "hva400"), and "bus_info" 
indicates the location of the device ("platform:8c85000.hva").

Before the modification:
Driver Info:
        Driver name   : 8c85000.hva
        Card type     : 8c85000.hva
        Bus info      : platform:hva
        Driver version: 4.7.0

After the modification:
Driver Info:
	Driver name   : hva
	Card type     : hva400
	Bus info      : platform:8c85000.hva
	Driver version: 4.7.0

Best regards,
Jean-Christophe.

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v2 0/3] support of v4l2 encoder for STMicroelectronics SOC
  2016-07-13 13:49     ` Jean Christophe TROTIN
@ 2016-07-13 14:00       ` Javier Martinez Canillas
  0 siblings, 0 replies; 18+ messages in thread
From: Javier Martinez Canillas @ 2016-07-13 14:00 UTC (permalink / raw)
  To: Jean Christophe TROTIN
  Cc: nicolas, Linux Media Mailing List, Hans Verkuil, kernel,
	Benjamin Gaignard, Yannick FERTRE, Hugues FRUCHET

Hello Jean Christophe,

On Wed, Jul 13, 2016 at 9:49 AM, Jean Christophe TROTIN
<jean-christophe.trotin@st.com> wrote:
>
>
> On 07/11/2016 08:57 PM, Javier Martinez Canillas wrote:
>> On Mon, Jul 11, 2016 at 1:48 PM, Nicolas Dufresne
>> <nicolas.dufresne@gmail.com> wrote:
>>> Le lundi 11 juillet 2016 à 17:14 +0200, Jean-Christophe Trotin a
>>> écrit :
>>
>> [snip]
>>
>>>>
>>>> Below is the v4l2-compliance report for the version 2 of the sti hva
>>>> driver:
>>>>
>>>>
>>>> root@sti-next:/home/video_test# v4l2-compliance -d /dev/video0
>>>> Driver Info:
>>>>        Driver name   : 8c85000.hva
>>>
>>> I think it would be nice to set a driver name that means something.
>>>
>>>>        Card type     : 8c85000.hva
>>
>> Agreed, same for Card type. The VIDIOC_QUERYCAP ioctl documentation
>> explains what information these fields should contain:
>>
>> https://linuxtv.org/downloads/v4l-dvb-apis/vidioc-querycap.html
>>
>> For example https://git.linuxtv.org/media_tree.git/commit/?id=e0d80c8acca0f221b9dedb2eab7a5184848b99b7
>>
>> Best regards,
>> Javier
>>
>
> Nicolas and Javier,
>
> Thank you for the remarks.
> I will modify the code in version 3 so that "driver" contains the name of the
> encoder ("hva"), "card" identifies the hardware version ("hva<hw_ip_version>"
> with <hw_ip_version> equal to 400 here, which leads to "hva400"), and "bus_info"
> indicates the location of the device ("platform:8c85000.hva").
>
> Before the modification:
> Driver Info:
>         Driver name   : 8c85000.hva
>         Card type     : 8c85000.hva
>         Bus info      : platform:hva
>         Driver version: 4.7.0
>
> After the modification:
> Driver Info:
>         Driver name   : hva
>         Card type     : hva400
>         Bus info      : platform:8c85000.hva
>         Driver version: 4.7.0
>

Thanks a lot for doing this, it looks correct to me now.

> Best regards,
> Jean-Christophe.

Best regards,
Javier

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v2 2/3] [media] hva: multi-format video encoder V4L2 driver
  2016-07-11 18:00   ` Nicolas Dufresne
@ 2016-07-13 14:11     ` Jean Christophe TROTIN
  0 siblings, 0 replies; 18+ messages in thread
From: Jean Christophe TROTIN @ 2016-07-13 14:11 UTC (permalink / raw)
  To: nicolas, linux-media, Hans Verkuil
  Cc: kernel, Benjamin Gaignard, Yannick FERTRE, Hugues FRUCHET



On 07/11/2016 08:00 PM, Nicolas Dufresne wrote:
> Le lundi 11 juillet 2016 à 17:14 +0200, Jean-Christophe Trotin a écrit :

[snip]

>> +static int hva_g_fmt_stream(struct file *file, void *fh, struct v4l2_format *f)
>> +{
>> +	struct hva_ctx *ctx = fh_to_ctx(file->private_data);
>> +	struct device *dev = ctx_to_dev(ctx);
>> +	struct hva_streaminfo *streaminfo = &ctx->streaminfo;
>> +
>> +	f->fmt.pix.width = streaminfo->width;
>> +	f->fmt.pix.height = streaminfo->height;
>> +	f->fmt.pix.field = V4L2_FIELD_NONE;
>> +	f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
>
> Hard coding this is not great.Ideally the colorimetry (if not modified) should be copied from OUTPUT to CAPTURE, you may also set this to V4L2_COLORSPACE_DEFAULT.
>

Nicolas,

Thank you for the remark.
Colorspace was hard-coded because only V4L2_COLORSPACE_SMPTE170M is supported. 
However, I understand that hard-coding is not great: I will align the code in 
version 3 on the colorspace management made in the coda driver.

Best regards,
Jean-Christophe.

>> +	f->fmt.pix.pixelformat = streaminfo->streamformat;
>> +	f->fmt.pix.bytesperline = 0;
>> +	f->fmt.pix.sizeimage = ctx->max_stream_size;
>> +
>> +	dev_dbg(dev, "%s V4L2 G_FMT (CAPTURE): %dx%d fmt:%.4s size:%d\n",
>> +		ctx->name, f->fmt.pix.width, f->fmt.pix.height,
>> +		(u8 *)&f->fmt.pix.pixelformat, f->fmt.pix.sizeimage);
>> +	return 0;
>> +}

[snip]

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v2 2/3] [media] hva: multi-format video encoder V4L2 driver
  2016-07-11 15:14 ` [PATCH v2 2/3] [media] hva: multi-format video encoder V4L2 driver Jean-Christophe Trotin
  2016-07-11 18:00   ` Nicolas Dufresne
@ 2016-07-18 11:45   ` Hans Verkuil
  2016-07-19 15:55     ` Jean Christophe TROTIN
  2016-07-21  7:30     ` Jean Christophe TROTIN
  1 sibling, 2 replies; 18+ messages in thread
From: Hans Verkuil @ 2016-07-18 11:45 UTC (permalink / raw)
  To: Jean-Christophe Trotin, linux-media
  Cc: kernel, Benjamin Gaignard, Yannick Fertre, Hugues Fruchet

Hi Jean-Christophe,

See my review comments below. Nothing really major, but I do need to know more
about the g/s_parm and the restriction on the number of open()s has to be lifted.
That's not allowed.

On 07/11/2016 05:14 PM, Jean-Christophe Trotin wrote:
> This patch adds V4L2 HVA (Hardware Video Accelerator) video encoder
> driver for STMicroelectronics SoC. It uses the V4L2 mem2mem framework.
> 
> This patch only contains the core parts of the driver:
> - the V4L2 interface with the userland (hva-v4l2.c)
> - the hardware services (hva-hw.c)
> - the memory management utilities (hva-mem.c)
> 
> This patch doesn't include the support of specific codec (e.g. H.264)
> video encoding: this support is part of subsequent patches.
> 
> Signed-off-by: Yannick Fertre <yannick.fertre@st.com>
> Signed-off-by: Jean-Christophe Trotin <jean-christophe.trotin@st.com>
> ---
>  drivers/media/platform/Kconfig            |   14 +
>  drivers/media/platform/Makefile           |    1 +
>  drivers/media/platform/sti/hva/Makefile   |    2 +
>  drivers/media/platform/sti/hva/hva-hw.c   |  534 ++++++++++++
>  drivers/media/platform/sti/hva/hva-hw.h   |   42 +
>  drivers/media/platform/sti/hva/hva-mem.c  |   60 ++
>  drivers/media/platform/sti/hva/hva-mem.h  |   36 +
>  drivers/media/platform/sti/hva/hva-v4l2.c | 1299 +++++++++++++++++++++++++++++
>  drivers/media/platform/sti/hva/hva.h      |  284 +++++++
>  9 files changed, 2272 insertions(+)
>  create mode 100644 drivers/media/platform/sti/hva/Makefile
>  create mode 100644 drivers/media/platform/sti/hva/hva-hw.c
>  create mode 100644 drivers/media/platform/sti/hva/hva-hw.h
>  create mode 100644 drivers/media/platform/sti/hva/hva-mem.c
>  create mode 100644 drivers/media/platform/sti/hva/hva-mem.h
>  create mode 100644 drivers/media/platform/sti/hva/hva-v4l2.c
>  create mode 100644 drivers/media/platform/sti/hva/hva.h
> 
> diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
> index 382f393..182d63f 100644
> --- a/drivers/media/platform/Kconfig
> +++ b/drivers/media/platform/Kconfig
> @@ -227,6 +227,20 @@ config VIDEO_STI_BDISP
>  	help
>  	  This v4l2 mem2mem driver is a 2D blitter for STMicroelectronics SoC.
>  
> +config VIDEO_STI_HVA
> +	tristate "STMicroelectronics STiH41x HVA multi-format video encoder V4L2 driver"
> +	depends on VIDEO_DEV && VIDEO_V4L2
> +	depends on ARCH_STI || COMPILE_TEST
> +	select VIDEOBUF2_DMA_CONTIG
> +	select V4L2_MEM2MEM_DEV
> +	help
> +	  This V4L2 driver enables HVA multi-format video encoder of

Please mention here what HVA stands for.

> +	  STMicroelectronics SoC STiH41x series, allowing hardware encoding of raw
> +	  uncompressed formats in various compressed video bitstreams format.
> +
> +	  To compile this driver as a module, choose M here:
> +	  the module will be called hva.

How about sti-hva as the module name? 'hva' is a bit too generic.

> +
>  config VIDEO_SH_VEU
>  	tristate "SuperH VEU mem2mem video processing driver"
>  	depends on VIDEO_DEV && VIDEO_V4L2 && HAS_DMA

<snip>

> diff --git a/drivers/media/platform/sti/hva/hva-v4l2.c b/drivers/media/platform/sti/hva/hva-v4l2.c
> new file mode 100644
> index 0000000..bacc9ff
> --- /dev/null
> +++ b/drivers/media/platform/sti/hva/hva-v4l2.c
> @@ -0,0 +1,1299 @@
> +/*
> + * Copyright (C) STMicroelectronics SA 2015
> + * Authors: Yannick Fertre <yannick.fertre@st.com>
> + *          Hugues Fruchet <hugues.fruchet@st.com>
> + * License terms:  GNU General Public License (GPL), version 2
> + */
> +
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <media/v4l2-event.h>
> +#include <media/v4l2-ioctl.h>
> +#include <media/videobuf2-dma-contig.h>
> +
> +#include "hva.h"
> +#include "hva-hw.h"
> +
> +#define HVA_NAME "hva"
> +
> +#define MIN_FRAMES	1
> +#define MIN_STREAMS	1
> +
> +#define HVA_MIN_WIDTH	32
> +#define HVA_MAX_WIDTH	1920
> +#define HVA_MIN_HEIGHT	32
> +#define HVA_MAX_HEIGHT	1920
> +
> +/* HVA requires a 16x16 pixels alignment for frames */
> +#define HVA_WIDTH_ALIGNMENT	16
> +#define HVA_HEIGHT_ALIGNMENT	16
> +
> +#define DEFAULT_WIDTH		HVA_MIN_WIDTH
> +#define	DEFAULT_HEIGHT		HVA_MIN_HEIGHT
> +#define DEFAULT_FRAME_NUM	1
> +#define DEFAULT_FRAME_DEN	30
> +
> +#define to_type_str(type) (type == V4L2_BUF_TYPE_VIDEO_OUTPUT ? \
> +			   "frame" : "stream")
> +
> +#define fh_to_ctx(f)    (container_of(f, struct hva_ctx, fh))
> +
> +/* registry of available encoders */
> +const struct hva_enc *hva_encoders[] = {
> +};
> +
> +static inline int frame_size(u32 w, u32 h, u32 fmt)
> +{
> +	switch (fmt) {
> +	case V4L2_PIX_FMT_NV12:
> +	case V4L2_PIX_FMT_NV21:
> +		return (w * h * 3) / 2;
> +	default:
> +		return 0;
> +	}
> +}
> +
> +static inline int frame_stride(u32 w, u32 fmt)
> +{
> +	switch (fmt) {
> +	case V4L2_PIX_FMT_NV12:
> +	case V4L2_PIX_FMT_NV21:
> +		return w;
> +	default:
> +		return 0;
> +	}
> +}
> +
> +static inline int frame_alignment(u32 fmt)
> +{
> +	switch (fmt) {
> +	case V4L2_PIX_FMT_NV12:
> +	case V4L2_PIX_FMT_NV21:
> +		/* multiple of 2 */
> +		return 2;
> +	default:
> +		return 1;
> +	}
> +}
> +
> +static inline int estimated_stream_size(u32 w, u32 h)
> +{
> +	/*
> +	 * HVA only encodes in YUV420 format, whatever the frame format.
> +	 * A compression ratio of 2 is assumed: thus, the maximum size
> +	 * of a stream is estimated to ((width x height x 3 / 2) / 2)
> +	 */
> +	return (w * h * 3) / 4;
> +}
> +
> +static void set_default_params(struct hva_ctx *ctx)
> +{
> +	struct hva_frameinfo *frameinfo = &ctx->frameinfo;
> +	struct hva_streaminfo *streaminfo = &ctx->streaminfo;
> +
> +	frameinfo->pixelformat = V4L2_PIX_FMT_NV12;
> +	frameinfo->width = DEFAULT_WIDTH;
> +	frameinfo->height = DEFAULT_HEIGHT;
> +	frameinfo->aligned_width = DEFAULT_WIDTH;
> +	frameinfo->aligned_height = DEFAULT_HEIGHT;
> +	frameinfo->size = frame_size(frameinfo->aligned_width,
> +				     frameinfo->aligned_height,
> +				     frameinfo->pixelformat);
> +
> +	streaminfo->streamformat = V4L2_PIX_FMT_H264;
> +	streaminfo->width = DEFAULT_WIDTH;
> +	streaminfo->height = DEFAULT_HEIGHT;
> +
> +	ctx->max_stream_size = estimated_stream_size(streaminfo->width,
> +						     streaminfo->height);
> +}
> +
> +static const struct hva_enc *hva_find_encoder(struct hva_ctx *ctx,
> +					      u32 pixelformat,
> +					      u32 streamformat)
> +{
> +	struct hva_dev *hva = ctx_to_hdev(ctx);
> +	const struct hva_enc *enc;
> +	unsigned int i;
> +
> +	for (i = 0; i < hva->nb_of_encoders; i++) {
> +		enc = hva->encoders[i];
> +		if ((enc->pixelformat == pixelformat) &&
> +		    (enc->streamformat == streamformat))
> +			return enc;
> +	}
> +
> +	return NULL;
> +}
> +
> +static void register_format(u32 format, u32 formats[], u32 *nb_of_formats)
> +{
> +	u32 i;
> +	bool found = false;
> +
> +	for (i = 0; i < *nb_of_formats; i++) {
> +		if (format == formats[i]) {
> +			found = true;
> +			break;
> +		}
> +	}
> +
> +	if (!found)
> +		formats[(*nb_of_formats)++] = format;
> +}
> +
> +static void register_formats(struct hva_dev *hva)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < hva->nb_of_encoders; i++) {
> +		register_format(hva->encoders[i]->pixelformat,
> +				hva->pixelformats,
> +				&hva->nb_of_pixelformats);
> +
> +		register_format(hva->encoders[i]->streamformat,
> +				hva->streamformats,
> +				&hva->nb_of_streamformats);
> +	}
> +}
> +
> +static void register_encoders(struct hva_dev *hva)
> +{
> +	struct device *dev = hva_to_dev(hva);
> +	unsigned int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(hva_encoders); i++) {
> +		if (hva->nb_of_encoders >= HVA_MAX_ENCODERS) {
> +			dev_dbg(dev,
> +				"%s failed to register encoder (%d maximum reached)\n",
> +				hva_encoders[i]->name, HVA_MAX_ENCODERS);
> +			return;
> +		}
> +
> +		hva->encoders[hva->nb_of_encoders++] = hva_encoders[i];
> +		dev_info(dev, "%s encoder registered\n", hva_encoders[i]->name);
> +	}
> +}
> +
> +static int hva_open_encoder(struct hva_ctx *ctx, u32 streamformat,
> +			    u32 pixelformat, struct hva_enc **penc)
> +{
> +	struct hva_dev *hva = ctx_to_hdev(ctx);
> +	struct device *dev = ctx_to_dev(ctx);
> +	struct hva_enc *enc;
> +	unsigned int i;
> +	int ret;
> +	bool found = false;
> +
> +	/* find an encoder which can deal with these formats */
> +	for (i = 0; i < hva->nb_of_encoders; i++) {
> +		enc = (struct hva_enc *)hva->encoders[i];
> +		if ((enc->streamformat == streamformat) &&
> +		    (enc->pixelformat == pixelformat)) {
> +			found = true;
> +			break;
> +		}
> +	}
> +
> +	if (!found) {
> +		dev_err(dev, "%s no encoder found matching %4.4s => %4.4s\n",
> +			ctx->name, (char *)&pixelformat, (char *)&streamformat);
> +		return -EINVAL;
> +	}
> +
> +	dev_dbg(dev, "%s one encoder matching %4.4s => %4.4s\n",
> +		ctx->name, (char *)&pixelformat, (char *)&streamformat);
> +
> +	/* update instance name */
> +	snprintf(ctx->name, sizeof(ctx->name), "[%3d:%4.4s]",
> +		 hva->instance_id, (char *)&streamformat);
> +
> +	/* open encoder instance */
> +	ret = enc->open(ctx);
> +	if (ret) {
> +		dev_err(dev, "%s failed to open encoder instance (%d)\n",
> +			ctx->name, ret);
> +		return ret;
> +	}
> +
> +	dev_dbg(dev, "%s %s encoder opened\n", ctx->name, enc->name);
> +
> +	*penc = enc;
> +
> +	return ret;
> +}
> +
> +/*
> + * V4L2 ioctl operations
> + */
> +
> +static int hva_querycap(struct file *file, void *priv,
> +			struct v4l2_capability *cap)
> +{
> +	struct hva_ctx *ctx = fh_to_ctx(file->private_data);
> +	struct hva_dev *hva = ctx_to_hdev(ctx);
> +
> +	strlcpy(cap->driver, hva->pdev->name, sizeof(cap->driver));
> +	strlcpy(cap->card, hva->pdev->name, sizeof(cap->card));
> +	snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
> +		 HVA_NAME);
> +
> +	cap->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M;
> +	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;

Set the new device_caps field of struct video_device to
V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M and drop these two lines.

The v4l2 core will set device_caps and capabilities for you based on the
video_device struct device_caps field. New drivers should use this.

The advantage is that the v4l2 core now knows the caps of the video node.

> +
> +	return 0;
> +}
> +
> +static int hva_enum_fmt_stream(struct file *file, void *priv,
> +			       struct v4l2_fmtdesc *f)
> +{
> +	struct hva_ctx *ctx = fh_to_ctx(file->private_data);
> +	struct hva_dev *hva = ctx_to_hdev(ctx);
> +
> +	if (unlikely(f->index >= hva->nb_of_streamformats))
> +		return -EINVAL;
> +
> +	f->pixelformat = hva->streamformats[f->index];
> +	snprintf(f->description, sizeof(f->description), "%4.4s",
> +		 (char *)&f->pixelformat);
> +	f->flags = V4L2_FMT_FLAG_COMPRESSED;

Drop these two lines. The v4l2 code fills in the description and flags for
you.

> +
> +	return 0;
> +}
> +
> +static int hva_enum_fmt_frame(struct file *file, void *priv,
> +			      struct v4l2_fmtdesc *f)
> +{
> +	struct hva_ctx *ctx = fh_to_ctx(file->private_data);
> +	struct hva_dev *hva = ctx_to_hdev(ctx);
> +
> +	if (unlikely(f->index >= hva->nb_of_pixelformats))
> +		return -EINVAL;
> +
> +	f->pixelformat = hva->pixelformats[f->index];
> +	snprintf(f->description, sizeof(f->description), "%4.4s",
> +		 (char *)&f->pixelformat);

Ditto.

> +
> +	return 0;
> +}
> +
> +static int hva_g_fmt_stream(struct file *file, void *fh, struct v4l2_format *f)
> +{
> +	struct hva_ctx *ctx = fh_to_ctx(file->private_data);
> +	struct device *dev = ctx_to_dev(ctx);
> +	struct hva_streaminfo *streaminfo = &ctx->streaminfo;
> +
> +	f->fmt.pix.width = streaminfo->width;
> +	f->fmt.pix.height = streaminfo->height;
> +	f->fmt.pix.field = V4L2_FIELD_NONE;
> +	f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;

This is a mem2mem device, so the colorspace comes from the colorspace that
the application specified for the video output format. That's copied to the
video capture format. See e.g. the vim2m.c example driver.

The default colorspace should be REC709 as well, rather than SMPTE170M (that's
for SDTV).

I've added checks to v4l2-compliance to improve testing for this.

See also the vim2m patch I just posted where I patch that m2m driver so it
passes the compliance test.

> +	f->fmt.pix.pixelformat = streaminfo->streamformat;
> +	f->fmt.pix.bytesperline = 0;
> +	f->fmt.pix.sizeimage = ctx->max_stream_size;
> +
> +	dev_dbg(dev, "%s V4L2 G_FMT (CAPTURE): %dx%d fmt:%.4s size:%d\n",
> +		ctx->name, f->fmt.pix.width, f->fmt.pix.height,
> +		(u8 *)&f->fmt.pix.pixelformat, f->fmt.pix.sizeimage);

No need for these debug messages. You can always debug the ioctls by:

echo 2 >/sys/class/video4linux/video0/dev_debug.

> +	return 0;
> +}

<snip>

> +
> +static int hva_s_parm(struct file *file, void *fh, struct v4l2_streamparm *sp)
> +{
> +	struct hva_ctx *ctx = fh_to_ctx(file->private_data);
> +	struct device *dev = ctx_to_dev(ctx);
> +	struct v4l2_fract *time_per_frame = &ctx->ctrls.time_per_frame;
> +
> +	time_per_frame->numerator = sp->parm.capture.timeperframe.numerator;
> +	time_per_frame->denominator =
> +		sp->parm.capture.timeperframe.denominator;
> +
> +	dev_dbg(dev, "%s set parameters %d/%d\n", ctx->name,
> +		time_per_frame->numerator, time_per_frame->denominator);
> +
> +	return 0;
> +}
> +
> +static int hva_g_parm(struct file *file, void *fh, struct v4l2_streamparm *sp)
> +{
> +	struct hva_ctx *ctx = fh_to_ctx(file->private_data);
> +	struct device *dev = ctx_to_dev(ctx);
> +	struct v4l2_fract *time_per_frame = &ctx->ctrls.time_per_frame;
> +
> +	sp->parm.capture.timeperframe.numerator = time_per_frame->numerator;
> +	sp->parm.capture.timeperframe.denominator =
> +		time_per_frame->denominator;
> +
> +	dev_dbg(dev, "%s get parameters %d/%d\n", ctx->name,
> +		time_per_frame->numerator, time_per_frame->denominator);
> +
> +	return 0;
> +}

This is strange. Normally codecs don't need this. You give them a buffer and
it will be encoded/decoded and then you give it the next one if it is available.
There is normally no frame rate involved.

How does this work in this SoC? I need to know a bit more about this to be
certain there isn't a misunderstanding somewhere.

> +
> +static int hva_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
> +{
> +	struct hva_ctx *ctx = fh_to_ctx(file->private_data);
> +	struct device *dev = ctx_to_dev(ctx);
> +
> +	if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
> +		/*
> +		 * depending on the targeted compressed video format, the
> +		 * capture buffer might contain headers (e.g. H.264 SPS/PPS)
> +		 * filled in by the driver client; the size of these data is
> +		 * copied from the bytesused field of the V4L2 buffer in the
> +		 * payload field of the hva stream buffer
> +		 */
> +		struct vb2_queue *vq;
> +		struct hva_stream *stream;
> +
> +		vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, buf->type);
> +
> +		if (buf->index >= vq->num_buffers) {
> +			dev_dbg(dev, "%s buffer index %d out of range (%d)\n",
> +				ctx->name, buf->index, vq->num_buffers);
> +			return -EINVAL;
> +		}
> +
> +		stream = (struct hva_stream *)vq->bufs[buf->index];
> +		stream->bytesused = buf->bytesused;
> +	}
> +
> +	return v4l2_m2m_qbuf(file, ctx->fh.m2m_ctx, buf);
> +}
> +
> +/* V4L2 ioctl ops */
> +static const struct v4l2_ioctl_ops hva_ioctl_ops = {
> +	.vidioc_querycap		= hva_querycap,
> +	.vidioc_enum_fmt_vid_cap	= hva_enum_fmt_stream,
> +	.vidioc_enum_fmt_vid_out	= hva_enum_fmt_frame,
> +	.vidioc_g_fmt_vid_cap		= hva_g_fmt_stream,
> +	.vidioc_g_fmt_vid_out		= hva_g_fmt_frame,
> +	.vidioc_try_fmt_vid_cap		= hva_try_fmt_stream,
> +	.vidioc_try_fmt_vid_out		= hva_try_fmt_frame,
> +	.vidioc_s_fmt_vid_cap		= hva_s_fmt_stream,
> +	.vidioc_s_fmt_vid_out		= hva_s_fmt_frame,
> +	.vidioc_g_parm			= hva_g_parm,
> +	.vidioc_s_parm			= hva_s_parm,
> +	.vidioc_reqbufs			= v4l2_m2m_ioctl_reqbufs,
> +	.vidioc_create_bufs             = v4l2_m2m_ioctl_create_bufs,
> +	.vidioc_querybuf		= v4l2_m2m_ioctl_querybuf,
> +	.vidioc_expbuf			= v4l2_m2m_ioctl_expbuf,
> +	.vidioc_qbuf			= hva_qbuf,
> +	.vidioc_dqbuf			= v4l2_m2m_ioctl_dqbuf,
> +	.vidioc_streamon		= v4l2_m2m_ioctl_streamon,
> +	.vidioc_streamoff		= v4l2_m2m_ioctl_streamoff,
> +	.vidioc_subscribe_event		= v4l2_ctrl_subscribe_event,
> +	.vidioc_unsubscribe_event	= v4l2_event_unsubscribe,
> +};
> +
> +/*
> + * V4L2 control operations
> + */
> +
> +static int hva_s_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	struct hva_ctx *ctx = container_of(ctrl->handler, struct hva_ctx,
> +					   ctrl_handler);
> +	struct device *dev = ctx_to_dev(ctx);
> +
> +	dev_dbg(dev, "%s S_CTRL: id = %d, val = %d\n", ctx->name,
> +		ctrl->id, ctrl->val);
> +
> +	switch (ctrl->id) {
> +	case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
> +		ctx->ctrls.bitrate_mode = ctrl->val;
> +		break;
> +	case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
> +		ctx->ctrls.gop_size = ctrl->val;
> +		break;
> +	case V4L2_CID_MPEG_VIDEO_BITRATE:
> +		ctx->ctrls.bitrate = ctrl->val;
> +		break;
> +	case V4L2_CID_MPEG_VIDEO_ASPECT:
> +		ctx->ctrls.aspect = ctrl->val;
> +		break;
> +	default:
> +		dev_dbg(dev, "%s S_CTRL: invalid control (id = %d)\n",
> +			ctx->name, ctrl->id);
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +/* V4L2 control ops */
> +static const struct v4l2_ctrl_ops hva_ctrl_ops = {
> +	.s_ctrl = hva_s_ctrl,
> +};
> +
> +static int hva_ctrls_setup(struct hva_ctx *ctx)
> +{
> +	struct device *dev = ctx_to_dev(ctx);
> +	u64 mask;
> +
> +	v4l2_ctrl_handler_init(&ctx->ctrl_handler, 4);
> +
> +	v4l2_ctrl_new_std_menu(&ctx->ctrl_handler, &hva_ctrl_ops,
> +			       V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
> +			       V4L2_MPEG_VIDEO_BITRATE_MODE_CBR,
> +			       0,
> +			       V4L2_MPEG_VIDEO_BITRATE_MODE_CBR);
> +
> +	v4l2_ctrl_new_std(&ctx->ctrl_handler, &hva_ctrl_ops,
> +			  V4L2_CID_MPEG_VIDEO_GOP_SIZE,
> +			  1, 60, 1, 16);
> +
> +	v4l2_ctrl_new_std(&ctx->ctrl_handler, &hva_ctrl_ops,
> +			  V4L2_CID_MPEG_VIDEO_BITRATE,
> +			  1, 50000, 1, 20000);
> +
> +	mask = ~(1 << V4L2_MPEG_VIDEO_ASPECT_1x1);
> +	v4l2_ctrl_new_std_menu(&ctx->ctrl_handler, &hva_ctrl_ops,
> +			       V4L2_CID_MPEG_VIDEO_ASPECT,
> +			       V4L2_MPEG_VIDEO_ASPECT_1x1,
> +			       mask,
> +			       V4L2_MPEG_VIDEO_ASPECT_1x1);
> +
> +	if (ctx->ctrl_handler.error) {
> +		int err = ctx->ctrl_handler.error;
> +
> +		dev_dbg(dev, "%s controls setup failed (%d)\n",
> +			ctx->name, err);
> +		v4l2_ctrl_handler_free(&ctx->ctrl_handler);
> +		return err;
> +	}
> +
> +	v4l2_ctrl_handler_setup(&ctx->ctrl_handler);
> +
> +	/* set default time per frame */
> +	ctx->ctrls.time_per_frame.numerator = DEFAULT_FRAME_NUM;
> +	ctx->ctrls.time_per_frame.denominator = DEFAULT_FRAME_DEN;
> +
> +	return 0;
> +}
> +
> +/*
> + * mem-to-mem operations
> + */
> +
> +static void hva_run_work(struct work_struct *work)
> +{
> +	struct hva_ctx *ctx = container_of(work, struct hva_ctx, run_work);
> +	struct vb2_v4l2_buffer *src_buf, *dst_buf;
> +	const struct hva_enc *enc = ctx->enc;
> +	struct hva_frame *frame;
> +	struct hva_stream *stream;
> +	int ret;
> +
> +	/* protect instance against reentrancy */
> +	mutex_lock(&ctx->lock);
> +
> +	src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
> +	dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
> +
> +	frame = to_hva_frame(src_buf);
> +	stream = to_hva_stream(dst_buf);
> +	frame->vbuf.sequence = ctx->frame_num++;
> +
> +	ret = enc->encode(ctx, frame, stream);
> +
> +	if (ret) {
> +		v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR);
> +		v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR);
> +	} else {
> +		/* propagate frame timestamp */
> +		dst_buf->vb2_buf.timestamp = src_buf->vb2_buf.timestamp;
> +		dst_buf->field = V4L2_FIELD_NONE;
> +		dst_buf->sequence = ctx->stream_num - 1;
> +
> +		v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE);
> +		v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE);
> +	}
> +
> +	mutex_unlock(&ctx->lock);
> +
> +	v4l2_m2m_job_finish(ctx->hva_dev->m2m_dev, ctx->fh.m2m_ctx);
> +}
> +
> +static void hva_device_run(void *priv)
> +{
> +	struct hva_ctx *ctx = priv;
> +	struct hva_dev *hva = ctx_to_hdev(ctx);
> +
> +	queue_work(hva->work_queue, &ctx->run_work);
> +}
> +
> +static void hva_job_abort(void *priv)
> +{
> +	struct hva_ctx *ctx = priv;
> +	struct device *dev = ctx_to_dev(ctx);
> +
> +	dev_dbg(dev, "%s aborting job\n", ctx->name);
> +
> +	ctx->aborting = true;
> +}
> +
> +static int hva_job_ready(void *priv)
> +{
> +	struct hva_ctx *ctx = priv;
> +	struct device *dev = ctx_to_dev(ctx);
> +
> +	if (!v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx)) {
> +		dev_dbg(dev, "%s job not ready: no frame buffers\n",
> +			ctx->name);
> +		return 0;
> +	}
> +
> +	if (!v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx)) {
> +		dev_dbg(dev, "%s job not ready: no stream buffers\n",
> +			ctx->name);
> +		return 0;
> +	}
> +
> +	if (ctx->aborting) {
> +		dev_dbg(dev, "%s job not ready: aborting\n", ctx->name);
> +		return 0;
> +	}
> +
> +	return 1;
> +}
> +
> +/* mem-to-mem ops */
> +static const struct v4l2_m2m_ops hva_m2m_ops = {
> +	.device_run	= hva_device_run,
> +	.job_abort	= hva_job_abort,
> +	.job_ready	= hva_job_ready,
> +};
> +
> +/*
> + * VB2 queue operations
> + */
> +
> +static int hva_queue_setup(struct vb2_queue *vq,
> +			   unsigned int *num_buffers, unsigned int *num_planes,
> +			   unsigned int sizes[], void *alloc_ctxs[])

This patch needs to be rebased: the way allocation contexts are set up has
changed. You now set the new 'dev' field in vb2_queue to the struct device,
and there is no longer any need to fill in alloc_ctxs here or init/free the
allocation context. The prototype of this queue_setup function has
changed as well.

> +{
> +	struct hva_ctx *ctx = vb2_get_drv_priv(vq);
> +	struct device *dev = ctx_to_dev(ctx);
> +	unsigned int size;
> +
> +	dev_dbg(dev, "%s %s queue setup: num_buffers %d\n", ctx->name,
> +		to_type_str(vq->type), *num_buffers);
> +
> +	size = vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT ?
> +		ctx->frameinfo.size : ctx->max_stream_size;
> +
> +	alloc_ctxs[0] = ctx->hva_dev->alloc_ctx;
> +
> +	if (*num_planes)
> +		return sizes[0] < size ? -EINVAL : 0;
> +
> +	/* only one plane supported */
> +	*num_planes = 1;
> +	sizes[0] = size;
> +
> +	return 0;
> +}
> +
> +static int hva_buf_prepare(struct vb2_buffer *vb)
> +{
> +	struct hva_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
> +	struct device *dev = ctx_to_dev(ctx);
> +	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
> +
> +	if (vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
> +		struct hva_frame *frame = to_hva_frame(vbuf);
> +
> +		if (vbuf->field == V4L2_FIELD_ANY)
> +			vbuf->field = V4L2_FIELD_NONE;

Anything other than FIELD_NONE should result in an error since no interlaced is supported.
FIELD_ANY is an incorrect value as well.

> +		if (vbuf->field != V4L2_FIELD_NONE) {
> +			dev_dbg(dev,
> +				"%s frame[%d] prepare: %d field not supported\n",
> +				ctx->name, vb->index, vbuf->field);
> +			return -EINVAL;
> +		}
> +
> +		if (!frame->prepared) {
> +			/* get memory addresses */
> +			frame->vaddr = vb2_plane_vaddr(&vbuf->vb2_buf, 0);
> +			frame->paddr = vb2_dma_contig_plane_dma_addr(
> +					&vbuf->vb2_buf, 0);
> +			frame->info = ctx->frameinfo;
> +			frame->prepared = true;
> +
> +			dev_dbg(dev,
> +				"%s frame[%d] prepared; virt=%p, phy=%pad\n",
> +				ctx->name, vb->index,
> +				frame->vaddr, &frame->paddr);
> +		}
> +	} else {
> +		struct hva_stream *stream = to_hva_stream(vbuf);
> +
> +		if (!stream->prepared) {
> +			/* get memory addresses */
> +			stream->vaddr = vb2_plane_vaddr(&vbuf->vb2_buf, 0);
> +			stream->paddr = vb2_dma_contig_plane_dma_addr(
> +					&vbuf->vb2_buf, 0);
> +			stream->size = vb2_plane_size(&vbuf->vb2_buf, 0);
> +			stream->prepared = true;
> +
> +			dev_dbg(dev,
> +				"%s stream[%d] prepared; virt=%p, phy=%pad\n",
> +				ctx->name, vb->index,
> +				stream->vaddr, &stream->paddr);
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static void hva_buf_queue(struct vb2_buffer *vb)
> +{
> +	struct hva_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
> +	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
> +
> +	if (ctx->fh.m2m_ctx)
> +		v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
> +}
> +
> +static int hva_start_streaming(struct vb2_queue *vq, unsigned int count)
> +{
> +	struct hva_ctx *ctx = vb2_get_drv_priv(vq);
> +	struct device *dev = ctx_to_dev(ctx);
> +	int ret = 0;
> +
> +	dev_dbg(dev, "%s %s start streaming\n", ctx->name,
> +		to_type_str(vq->type));
> +
> +	/* open encoder when both start_streaming have been called */
> +	if (V4L2_TYPE_IS_OUTPUT(vq->type)) {
> +		if (!vb2_start_streaming_called(&ctx->fh.m2m_ctx->cap_q_ctx.q))
> +			return 0;
> +	} else {
> +		if (!vb2_start_streaming_called(&ctx->fh.m2m_ctx->out_q_ctx.q))
> +			return 0;
> +	}
> +
> +	if (!ctx->enc)
> +		ret = hva_open_encoder(ctx,
> +				       ctx->streaminfo.streamformat,
> +				       ctx->frameinfo.pixelformat,
> +				       &ctx->enc);

On error all pending buffers for queue vq should be returned to vb2 in the QUEUED state.
Similar to what happens in stop_streaming, but with state QUEUED instead of state ERROR.

> +
> +	return ret;
> +}
> +
> +static void hva_stop_streaming(struct vb2_queue *vq)
> +{
> +	struct hva_ctx *ctx = vb2_get_drv_priv(vq);
> +	struct device *dev = ctx_to_dev(ctx);
> +	const struct hva_enc *enc = ctx->enc;
> +	struct vb2_v4l2_buffer *vbuf;
> +
> +	dev_dbg(dev, "%s %s stop streaming\n", ctx->name,
> +		to_type_str(vq->type));
> +
> +	if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
> +		/* return of all pending buffers to vb2 (in error state) */
> +		ctx->frame_num = 0;
> +		while ((vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx)))
> +			v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
> +	} else {
> +		/* return of all pending buffers to vb2 (in error state) */
> +		ctx->stream_num = 0;
> +		while ((vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx)))
> +			v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
> +	}
> +
> +	if ((V4L2_TYPE_IS_OUTPUT(vq->type) &&
> +	     vb2_is_streaming(&ctx->fh.m2m_ctx->cap_q_ctx.q)) ||
> +	    (!V4L2_TYPE_IS_OUTPUT(vq->type) &&
> +	     vb2_is_streaming(&ctx->fh.m2m_ctx->out_q_ctx.q))) {
> +		dev_dbg(dev, "%s %s out=%d cap=%d\n",
> +			ctx->name, to_type_str(vq->type),
> +			vb2_is_streaming(&ctx->fh.m2m_ctx->out_q_ctx.q),
> +			vb2_is_streaming(&ctx->fh.m2m_ctx->cap_q_ctx.q));
> +		return;
> +	}
> +
> +	/* close encoder when both stop_streaming have been called */
> +	if (enc) {
> +		dev_dbg(dev, "%s %s encoder closed\n", ctx->name, enc->name);
> +		enc->close(ctx);
> +		ctx->enc = NULL;
> +	}
> +
> +	ctx->aborting = false;
> +}
> +
> +/* VB2 queue ops */
> +static const struct vb2_ops hva_qops = {
> +	.queue_setup		= hva_queue_setup,
> +	.buf_prepare		= hva_buf_prepare,
> +	.buf_queue		= hva_buf_queue,
> +	.start_streaming	= hva_start_streaming,
> +	.stop_streaming		= hva_stop_streaming,
> +	.wait_prepare		= vb2_ops_wait_prepare,
> +	.wait_finish		= vb2_ops_wait_finish,
> +};
> +
> +/*
> + * V4L2 file operations
> + */
> +
> +static int queue_init(struct hva_ctx *ctx, struct vb2_queue *vq)
> +{
> +	vq->io_modes = VB2_MMAP | VB2_DMABUF;
> +	vq->drv_priv = ctx;
> +	vq->ops = &hva_qops;
> +	vq->mem_ops = &vb2_dma_contig_memops;
> +	vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
> +	vq->lock = &ctx->hva_dev->lock;
> +
> +	return vb2_queue_init(vq);
> +}
> +
> +static int hva_queue_init(void *priv, struct vb2_queue *src_vq,
> +			  struct vb2_queue *dst_vq)
> +{
> +	struct hva_ctx *ctx = priv;
> +	int ret;
> +
> +	src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
> +	src_vq->buf_struct_size = sizeof(struct hva_frame);
> +	src_vq->min_buffers_needed = MIN_FRAMES;
> +
> +	ret = queue_init(ctx, src_vq);
> +	if (ret)
> +		return ret;
> +
> +	dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> +	dst_vq->buf_struct_size = sizeof(struct hva_stream);
> +	dst_vq->min_buffers_needed = MIN_STREAMS;
> +
> +	return queue_init(ctx, dst_vq);
> +}
> +
> +static int hva_open(struct file *file)
> +{
> +	struct hva_dev *hva = video_drvdata(file);
> +	struct device *dev = hva_to_dev(hva);
> +	struct hva_ctx *ctx;
> +	int ret;
> +	unsigned int i;
> +	bool found = false;
> +
> +	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
> +	if (!ctx) {
> +		ret = -ENOMEM;
> +		goto out;
> +	}
> +	ctx->hva_dev = hva;
> +
> +	mutex_lock(&hva->lock);
> +
> +	/* store the instance context in the instances array */
> +	for (i = 0; i < HVA_MAX_INSTANCES; i++) {
> +		if (!hva->instances[i]) {
> +			hva->instances[i] = ctx;
> +			/* save the context identifier in the context */
> +			ctx->id = i;
> +			found = true;
> +			break;
> +		}
> +	}
> +
> +	if (!found) {
> +		dev_err(dev, "%s [x:x] maximum instances reached\n",
> +			HVA_PREFIX);
> +		ret = -ENOMEM;
> +		goto mem_ctx;
> +	}

This is wrong. It should always be possible to open the device node and
e.g. query the format or control settings, or whatever.

In this case there is apparently a hardware restriction with regards to
the number of codec instances. It *is* a hardware restriction, right?
If it is a driver restriction, then that's certainly wrong since there
is normally no reason for that.

Assuming it is a HW restriction, then this restriction is normally
checked in start_streaming or in queue_setup. I.e. at the point where
the HW resource reservation actually takes place.

> +
> +	INIT_WORK(&ctx->run_work, hva_run_work);
> +	v4l2_fh_init(&ctx->fh, video_devdata(file));
> +	file->private_data = &ctx->fh;
> +	v4l2_fh_add(&ctx->fh);
> +
> +	ret = hva_ctrls_setup(ctx);
> +	if (ret) {
> +		dev_err(dev, "%s [x:x] failed to setup controls\n",
> +			HVA_PREFIX);
> +		goto err_fh;
> +	}
> +	ctx->fh.ctrl_handler = &ctx->ctrl_handler;
> +
> +	mutex_init(&ctx->lock);
> +
> +	ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(hva->m2m_dev, ctx,
> +					    &hva_queue_init);
> +	if (IS_ERR(ctx->fh.m2m_ctx)) {
> +		ret = PTR_ERR(ctx->fh.m2m_ctx);
> +		dev_err(dev, "%s [x:x] failed to initialize m2m context (%d)\n",
> +			HVA_PREFIX, ret);
> +		goto err_ctrls;
> +	}
> +
> +	/* set the instance name */
> +	hva->instance_id++;
> +	snprintf(ctx->name, sizeof(ctx->name), "[%3d:----]",
> +		 hva->instance_id);
> +
> +	hva->nb_of_instances++;
> +
> +	mutex_unlock(&hva->lock);
> +
> +	/* default parameters for frame and stream */
> +	set_default_params(ctx);
> +
> +	dev_info(dev, "%s encoder instance created (id %d)\n",
> +		 ctx->name, ctx->id);
> +
> +	return 0;
> +
> +err_ctrls:
> +	v4l2_ctrl_handler_free(&ctx->ctrl_handler);
> +err_fh:
> +	v4l2_fh_del(&ctx->fh);
> +	v4l2_fh_exit(&ctx->fh);
> +	hva->instances[ctx->id] = NULL;
> +mem_ctx:
> +	kfree(ctx);
> +	mutex_unlock(&hva->lock);
> +out:
> +	return ret;
> +}
> +
> +static int hva_release(struct file *file)
> +{
> +	struct hva_dev *hva = video_drvdata(file);
> +	struct hva_ctx *ctx = fh_to_ctx(file->private_data);
> +	struct device *dev = ctx_to_dev(ctx);
> +
> +	v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
> +
> +	v4l2_ctrl_handler_free(&ctx->ctrl_handler);
> +
> +	v4l2_fh_del(&ctx->fh);
> +	v4l2_fh_exit(&ctx->fh);
> +
> +	mutex_lock(&hva->lock);
> +
> +	/* clear instance context in instances array */
> +	hva->instances[ctx->id] = NULL;
> +
> +	hva->nb_of_instances--;
> +
> +	mutex_unlock(&hva->lock);
> +
> +	dev_info(dev, "%s encoder instance released (id %d)\n",
> +		 ctx->name, ctx->id);
> +
> +	kfree(ctx);
> +
> +	return 0;
> +}
> +
> +/* V4L2 file ops */
> +static const struct v4l2_file_operations hva_fops = {
> +	.owner			= THIS_MODULE,
> +	.open			= hva_open,
> +	.release		= hva_release,
> +	.unlocked_ioctl		= video_ioctl2,
> +	.mmap			= v4l2_m2m_fop_mmap,
> +	.poll			= v4l2_m2m_fop_poll,
> +};
> +
> +/*
> + * Platform device operations
> + */
> +
> +static int hva_register_device(struct hva_dev *hva)
> +{
> +	int ret;
> +	struct video_device *vdev;
> +	struct device *dev;
> +
> +	if (!hva)
> +		return -ENODEV;
> +	dev = hva_to_dev(hva);
> +
> +	hva->m2m_dev = v4l2_m2m_init(&hva_m2m_ops);
> +	if (IS_ERR(hva->m2m_dev)) {
> +		dev_err(dev, "%s %s failed to initialize v4l2-m2m device\n",
> +			HVA_PREFIX, HVA_NAME);
> +		ret = PTR_ERR(hva->m2m_dev);
> +		goto err;
> +	}
> +
> +	vdev = video_device_alloc();
> +	if (!vdev) {
> +		dev_err(dev, "%s %s failed to allocate video device\n",
> +			HVA_PREFIX, HVA_NAME);
> +		ret = -ENOMEM;
> +		goto err_m2m_release;
> +	}
> +
> +	vdev->fops = &hva_fops;
> +	vdev->ioctl_ops = &hva_ioctl_ops;
> +	vdev->release = video_device_release;
> +	vdev->lock = &hva->lock;
> +	vdev->vfl_dir = VFL_DIR_M2M;
> +	vdev->v4l2_dev = &hva->v4l2_dev;
> +	snprintf(vdev->name, sizeof(vdev->name), "%s", HVA_NAME);
> +
> +	ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
> +	if (ret) {
> +		dev_err(dev, "%s %s failed to register video device\n",
> +			HVA_PREFIX, HVA_NAME);
> +		goto err_vdev_release;
> +	}
> +
> +	hva->vdev = vdev;
> +	video_set_drvdata(vdev, hva);
> +	return 0;
> +
> +err_vdev_release:
> +	video_device_release(vdev);
> +err_m2m_release:
> +	v4l2_m2m_release(hva->m2m_dev);
> +err:
> +	return ret;
> +}
> +
> +static void hva_unregister_device(struct hva_dev *hva)
> +{
> +	if (!hva)
> +		return;
> +
> +	if (hva->m2m_dev)
> +		v4l2_m2m_release(hva->m2m_dev);
> +
> +	video_unregister_device(hva->vdev);
> +}
> +
> +static int hva_probe(struct platform_device *pdev)
> +{
> +	struct hva_dev *hva;
> +	struct device *dev = &pdev->dev;
> +	int ret;
> +
> +	hva = devm_kzalloc(dev, sizeof(*hva), GFP_KERNEL);
> +	if (!hva) {
> +		ret = -ENOMEM;
> +		goto err;
> +	}
> +
> +	hva->dev = dev;
> +	hva->pdev = pdev;
> +	platform_set_drvdata(pdev, hva);
> +
> +	mutex_init(&hva->lock);
> +
> +	/* probe hardware */
> +	ret = hva_hw_probe(pdev, hva);
> +	if (ret)
> +		goto err;
> +
> +	/* register all available encoders */
> +	register_encoders(hva);
> +
> +	/* register all supported formats */
> +	register_formats(hva);
> +
> +	/* register on V4L2 */
> +	ret = v4l2_device_register(dev, &hva->v4l2_dev);
> +	if (ret) {
> +		dev_err(dev, "%s %s failed to register V4L2 device\n",
> +			HVA_PREFIX, HVA_NAME);
> +		goto err_hw;
> +	}
> +
> +	/* continuous memory allocator */
> +	hva->alloc_ctx = vb2_dma_contig_init_ctx(dev);
> +	if (IS_ERR(hva->alloc_ctx)) {
> +		ret = PTR_ERR(hva->alloc_ctx);
> +		goto err_v4l2;
> +	}
> +
> +	hva->work_queue = create_workqueue(HVA_NAME);
> +	if (!hva->work_queue) {
> +		dev_err(dev, "%s %s failed to allocate work queue\n",
> +			HVA_PREFIX, HVA_NAME);
> +		ret = -ENOMEM;
> +		goto err_vb2_dma;
> +	}
> +
> +	/* register device */
> +	ret = hva_register_device(hva);
> +	if (ret)
> +		goto err_work_queue;
> +
> +	dev_info(dev, "%s %s registered as /dev/video%d\n", HVA_PREFIX,
> +		 HVA_NAME, hva->vdev->num);
> +
> +	return 0;
> +
> +err_work_queue:
> +	destroy_workqueue(hva->work_queue);
> +err_vb2_dma:
> +	vb2_dma_contig_cleanup_ctx(hva->alloc_ctx);
> +err_v4l2:
> +	v4l2_device_unregister(&hva->v4l2_dev);
> +err_hw:
> +	hva_hw_remove(hva);
> +err:
> +	return ret;
> +}
> +
> +static int hva_remove(struct platform_device *pdev)
> +{
> +	struct hva_dev *hva = platform_get_drvdata(pdev);
> +	struct device *dev = hva_to_dev(hva);
> +
> +	hva_unregister_device(hva);
> +
> +	destroy_workqueue(hva->work_queue);
> +
> +	vb2_dma_contig_cleanup_ctx(hva->alloc_ctx);
> +
> +	hva_hw_remove(hva);
> +
> +	v4l2_device_unregister(&hva->v4l2_dev);
> +
> +	dev_info(dev, "%s %s removed\n", HVA_PREFIX, pdev->name);
> +
> +	return 0;
> +}
> +
> +/* PM ops */
> +static const struct dev_pm_ops hva_pm_ops = {
> +	.runtime_suspend	= hva_hw_runtime_suspend,
> +	.runtime_resume		= hva_hw_runtime_resume,
> +};
> +
> +static const struct of_device_id hva_match_types[] = {
> +	{
> +	 .compatible = "st,sti-hva",
> +	},
> +	{ /* end node */ }
> +};
> +
> +MODULE_DEVICE_TABLE(of, hva_match_types);
> +
> +struct platform_driver hva_driver = {
> +	.probe  = hva_probe,
> +	.remove = hva_remove,
> +	.driver = {
> +		.name           = HVA_NAME,
> +		.owner          = THIS_MODULE,
> +		.of_match_table = hva_match_types,
> +		.pm             = &hva_pm_ops,
> +		},

Wrong indentation?

> +};
> +
> +module_platform_driver(hva_driver);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>");
> +MODULE_DESCRIPTION("HVA video encoder V4L2 driver");

Regards,

	Hans

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v2 3/3] [media] hva: add H.264 video encoding support
  2016-07-11 15:14 ` [PATCH v2 3/3] [media] hva: add H.264 video encoding support Jean-Christophe Trotin
@ 2016-07-18 11:55   ` Hans Verkuil
  2016-07-21  7:30     ` Jean Christophe TROTIN
  0 siblings, 1 reply; 18+ messages in thread
From: Hans Verkuil @ 2016-07-18 11:55 UTC (permalink / raw)
  To: Jean-Christophe Trotin, linux-media
  Cc: kernel, Benjamin Gaignard, Yannick Fertre, Hugues Fruchet

On 07/11/2016 05:14 PM, Jean-Christophe Trotin wrote:
> This patch adds the H.264 video encoding capability in the V4L2 HVA
> video encoder driver for STMicroelectronics SoC (hva-h264.c).
> 
> The main supported features are:
> - profile: baseline, main, high, stereo high
> - level: up to 4.2
> - bitrate mode: CBR, VBR
> - entropy mode: CABAC, CAVLC
> - video aspect: 1x1 only
> 
> Signed-off-by: Yannick Fertre <yannick.fertre@st.com>
> Signed-off-by: Jean-Christophe Trotin <jean-christophe.trotin@st.com>
> ---
>  drivers/media/platform/sti/hva/Makefile   |    2 +-
>  drivers/media/platform/sti/hva/hva-h264.c | 1053 +++++++++++++++++++++++++++++
>  drivers/media/platform/sti/hva/hva-v4l2.c |  107 ++-
>  drivers/media/platform/sti/hva/hva.h      |  115 +++-
>  4 files changed, 1270 insertions(+), 7 deletions(-)
>  create mode 100644 drivers/media/platform/sti/hva/hva-h264.c
> 

<snip>

> diff --git a/drivers/media/platform/sti/hva/hva.h b/drivers/media/platform/sti/hva/hva.h
> index 9a1b503b..a81f313 100644
> --- a/drivers/media/platform/sti/hva/hva.h
> +++ b/drivers/media/platform/sti/hva/hva.h
> @@ -23,6 +23,9 @@
>  
>  #define HVA_PREFIX "[---:----]"
>  
> +extern const struct hva_enc nv12h264enc;
> +extern const struct hva_enc nv21h264enc;
> +
>  /**
>   * struct hva_frameinfo - information about hva frame
>   *
> @@ -67,13 +70,35 @@ struct hva_streaminfo {
>   * @gop_size:       groupe of picture size
>   * @bitrate:        bitrate (in kbps)
>   * @aspect:         video aspect
> + * @profile:        H.264 profile
> + * @level:          H.264 level
> + * @entropy_mode:   H.264 entropy mode (CABAC or CVLC)
> + * @cpb_size:       coded picture buffer size (in kbps)
> + * @dct8x8:         transform mode 8x8 enable
> + * @qpmin:          minimum quantizer
> + * @qpmax:          maximum quantizer
> + * @vui_sar:        pixel aspect ratio enable
> + * @vui_sar_idc:    pixel aspect ratio identifier
> + * @sei_fp:         sei frame packing arrangement enable
> + * @sei_fp_type:    sei frame packing arrangement type
>   */
>  struct hva_controls {
> -	struct v4l2_fract			time_per_frame;
> -	enum v4l2_mpeg_video_bitrate_mode	bitrate_mode;
> -	u32					gop_size;
> -	u32					bitrate;
> -	enum v4l2_mpeg_video_aspect		aspect;
> +	struct v4l2_fract					time_per_frame;
> +	enum v4l2_mpeg_video_bitrate_mode			bitrate_mode;
> +	u32							gop_size;
> +	u32							bitrate;
> +	enum v4l2_mpeg_video_aspect				aspect;
> +	enum v4l2_mpeg_video_h264_profile			profile;
> +	enum v4l2_mpeg_video_h264_level				level;
> +	enum v4l2_mpeg_video_h264_entropy_mode			entropy_mode;
> +	u32							cpb_size;
> +	bool							dct8x8;
> +	u32							qpmin;
> +	u32							qpmax;
> +	bool							vui_sar;
> +	enum v4l2_mpeg_video_h264_vui_sar_idc			vui_sar_idc;
> +	bool							sei_fp;
> +	enum v4l2_mpeg_video_h264_sei_fp_arrangement_type	sei_fp_type;
>  };
>  
>  /**
> @@ -281,4 +306,84 @@ struct hva_enc {
>  				  struct hva_stream *stream);
>  };
>  
> +static inline const char *profile_str(unsigned int p)
> +{
> +	switch (p) {
> +	case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE:
> +		return "baseline profile";
> +	case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN:
> +		return "main profile";
> +	case V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED:
> +		return "extended profile";
> +	case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH:
> +		return "high profile";
> +	case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_10:
> +		return "high 10 profile";
> +	case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422:
> +		return "high 422 profile";
> +	case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_PREDICTIVE:
> +		return "high 444 predictive profile";
> +	case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_10_INTRA:
> +		return "high 10 intra profile";
> +	case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422_INTRA:
> +		return "high 422 intra profile";
> +	case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_INTRA:
> +		return "high 444 intra profile";
> +	case V4L2_MPEG_VIDEO_H264_PROFILE_CAVLC_444_INTRA:
> +		return "calvc 444 intra profile";
> +	case V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_BASELINE:
> +		return "scalable baseline profile";
> +	case V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_HIGH:
> +		return "scalable high profile";
> +	case V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_HIGH_INTRA:
> +		return "scalable high intra profile";
> +	case V4L2_MPEG_VIDEO_H264_PROFILE_STEREO_HIGH:
> +		return "stereo high profile";
> +	case V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH:
> +		return "multiview high profile";
> +	default:
> +		return "unknown profile";
> +	}
> +}
> +
> +static inline const char *level_str(enum v4l2_mpeg_video_h264_level l)
> +{
> +	switch (l) {
> +	case V4L2_MPEG_VIDEO_H264_LEVEL_1_0:
> +		return "level 1.0";
> +	case V4L2_MPEG_VIDEO_H264_LEVEL_1B:
> +		return "level 1b";
> +	case V4L2_MPEG_VIDEO_H264_LEVEL_1_1:
> +		return "level 1.1";
> +	case V4L2_MPEG_VIDEO_H264_LEVEL_1_2:
> +		return "level 1.2";
> +	case V4L2_MPEG_VIDEO_H264_LEVEL_1_3:
> +		return "level 1.3";
> +	case V4L2_MPEG_VIDEO_H264_LEVEL_2_0:
> +		return "level 2.0";
> +	case V4L2_MPEG_VIDEO_H264_LEVEL_2_1:
> +		return "level 2.1";
> +	case V4L2_MPEG_VIDEO_H264_LEVEL_2_2:
> +		return "level 2.2";
> +	case V4L2_MPEG_VIDEO_H264_LEVEL_3_0:
> +		return "level 3.0";
> +	case V4L2_MPEG_VIDEO_H264_LEVEL_3_1:
> +		return "level 3.1";
> +	case V4L2_MPEG_VIDEO_H264_LEVEL_3_2:
> +		return "level 3.2";
> +	case V4L2_MPEG_VIDEO_H264_LEVEL_4_0:
> +		return "level 4.0";
> +	case V4L2_MPEG_VIDEO_H264_LEVEL_4_1:
> +		return "level 4.1";
> +	case V4L2_MPEG_VIDEO_H264_LEVEL_4_2:
> +		return "level 4.2";
> +	case V4L2_MPEG_VIDEO_H264_LEVEL_5_0:
> +		return "level 5.0";
> +	case V4L2_MPEG_VIDEO_H264_LEVEL_5_1:
> +		return "level 5.1";
> +	default:
> +		return "unknown level";
> +	}
> +}

These two static inlines should be replaced. You can get the menu strings directly
with v4l2_ctrl_get_menu(). No need to duplicate these strings here.

Regards,

	Hans

> +
>  #endif /* HVA_H */
> 

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v2 2/3] [media] hva: multi-format video encoder V4L2 driver
  2016-07-18 11:45   ` Hans Verkuil
@ 2016-07-19 15:55     ` Jean Christophe TROTIN
  2016-07-19 16:45       ` Hans Verkuil
  2016-07-21  7:30     ` Jean Christophe TROTIN
  1 sibling, 1 reply; 18+ messages in thread
From: Jean Christophe TROTIN @ 2016-07-19 15:55 UTC (permalink / raw)
  To: Hans Verkuil, linux-media
  Cc: kernel, Benjamin Gaignard, Yannick FERTRE, Hugues FRUCHET

Hi Hans,

Thank you for your comments.
I've started to take them into account.
I've got a question about V4L2_FIELD_ANY in buf_prepare (please see below).

[snip]

 >> +static int hva_buf_prepare(struct vb2_buffer *vb)
 >> +{
 >> +     struct hva_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
 >> +     struct device *dev = ctx_to_dev(ctx);
 >> +     struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
 >> +
 >> +     if (vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
 >> +             struct hva_frame *frame = to_hva_frame(vbuf);
 >> +
 >> +             if (vbuf->field == V4L2_FIELD_ANY)
 >> +                     vbuf->field = V4L2_FIELD_NONE;
 >
 > Anything other than FIELD_NONE should result in an error since no interlaced 
is supported.
 > FIELD_ANY is an incorrect value as well.
 >

In videodev2.h, V4L2_FIELD_ANY is commented as "driver can choose from none, 
top, bottom, interlaced depending on whatever it thinks is approximate ...": I 
understand this comment as if vbuf->field is equal to V4L2_FIELD_ANY, then the 
driver can choose to force it to V4L2_FIELD_NONE. Furthermore, it's coded in the 
same way in vim2m.c (vim2m_buf_prepare).
Finally, if I remove these 2 lines, I've got the following error with the 
v4l2-compliance:
Streaming ioctls:
		VIDIOC_G_INPUT returned -1 (Inappropriate ioctl for device)
		VIDIOC_ENUMINPUT returned -1 (Inappropriate ioctl for device)
	test read/write: OK (Not Supported)
		VIDIOC_QUERYCAP returned 0 (Success)
		[snip]
		VIDIOC_QUERYBUF returned 0 (Success)
		VIDIOC_QBUF returned -1 (Invalid argument)
		fail: 
/local/home/frq08988/views/opensdk-2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-buffers.cpp(773): 
buf.qbuf(node)
		fail: 
/local/home/frq08988/views/opensdk-2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-buffers.cpp(971): 
setupM2M(node, m2m_q)
	test MMAP: FAIL

Don't you think that I could keep these two lines?

 >> +             if (vbuf->field != V4L2_FIELD_NONE) {
 >> +                     dev_dbg(dev,
 >> +                             "%s frame[%d] prepare: %d field not supported\n",
 >> +                             ctx->name, vb->index, vbuf->field);
 >> +                     return -EINVAL;
 >> +             }
 >> +
 >> +             if (!frame->prepared) {
 >> +                     /* get memory addresses */
 >> +                     frame->vaddr = vb2_plane_vaddr(&vbuf->vb2_buf, 0);
 >> +                     frame->paddr = vb2_dma_contig_plane_dma_addr(
 >> +                                     &vbuf->vb2_buf, 0);
 >> +                     frame->info = ctx->frameinfo;
 >> +                     frame->prepared = true;
 >> +
 >> +                     dev_dbg(dev,
 >> +                             "%s frame[%d] prepared; virt=%p, phy=%pad\n",
 >> +                             ctx->name, vb->index,
 >> +                             frame->vaddr, &frame->paddr);
 >> +             }
 >> +     } else {
 >> +             struct hva_stream *stream = to_hva_stream(vbuf);
 >> +
 >> +             if (!stream->prepared) {
 >> +                     /* get memory addresses */
 >> +                     stream->vaddr = vb2_plane_vaddr(&vbuf->vb2_buf, 0);
 >> +                     stream->paddr = vb2_dma_contig_plane_dma_addr(
 >> +                                     &vbuf->vb2_buf, 0);
 >> +                     stream->size = vb2_plane_size(&vbuf->vb2_buf, 0);
 >> +                     stream->prepared = true;
 >> +
 >> +                     dev_dbg(dev,
 >> +                             "%s stream[%d] prepared; virt=%p, phy=%pad\n",
 >> +                             ctx->name, vb->index,
 >> +                             stream->vaddr, &stream->paddr);
 >> +             }
 >> +     }
 >> +
 >> +     return 0;
 >> +}
 >> +

[snip]

Regards,
Jean-Christophe.

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v2 2/3] [media] hva: multi-format video encoder V4L2 driver
  2016-07-19 15:55     ` Jean Christophe TROTIN
@ 2016-07-19 16:45       ` Hans Verkuil
  0 siblings, 0 replies; 18+ messages in thread
From: Hans Verkuil @ 2016-07-19 16:45 UTC (permalink / raw)
  To: Jean Christophe TROTIN, linux-media
  Cc: kernel, Benjamin Gaignard, Yannick FERTRE, Hugues FRUCHET

On 07/19/2016 05:55 PM, Jean Christophe TROTIN wrote:
> Hi Hans,
> 
> Thank you for your comments.
> I've started to take them into account.
> I've got a question about V4L2_FIELD_ANY in buf_prepare (please see below).
> 
> [snip]
> 
>  >> +static int hva_buf_prepare(struct vb2_buffer *vb)
>  >> +{
>  >> +     struct hva_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
>  >> +     struct device *dev = ctx_to_dev(ctx);
>  >> +     struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
>  >> +
>  >> +     if (vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
>  >> +             struct hva_frame *frame = to_hva_frame(vbuf);
>  >> +
>  >> +             if (vbuf->field == V4L2_FIELD_ANY)
>  >> +                     vbuf->field = V4L2_FIELD_NONE;
>  >
>  > Anything other than FIELD_NONE should result in an error since no interlaced 
> is supported.
>  > FIELD_ANY is an incorrect value as well.
>  >
> 
> In videodev2.h, V4L2_FIELD_ANY is commented as "driver can choose from none, 
> top, bottom, interlaced depending on whatever it thinks is approximate ...": I 
> understand this comment as if vbuf->field is equal to V4L2_FIELD_ANY, then the 
> driver can choose to force it to V4L2_FIELD_NONE. Furthermore, it's coded in the 
> same way in vim2m.c (vim2m_buf_prepare).
> Finally, if I remove these 2 lines, I've got the following error with the 
> v4l2-compliance:
> Streaming ioctls:
> 		VIDIOC_G_INPUT returned -1 (Inappropriate ioctl for device)
> 		VIDIOC_ENUMINPUT returned -1 (Inappropriate ioctl for device)
> 	test read/write: OK (Not Supported)
> 		VIDIOC_QUERYCAP returned 0 (Success)
> 		[snip]
> 		VIDIOC_QUERYBUF returned 0 (Success)
> 		VIDIOC_QBUF returned -1 (Invalid argument)
> 		fail: 
> /local/home/frq08988/views/opensdk-2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-buffers.cpp(773): 
> buf.qbuf(node)
> 		fail: 
> /local/home/frq08988/views/opensdk-2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-buffers.cpp(971): 
> setupM2M(node, m2m_q)
> 	test MMAP: FAIL
> 
> Don't you think that I could keep these two lines?

Keep it for now, I dug into this a bit further and it is really a workaround for
poorly written applications that can't be bothered to set the field value to a
proper value. I think the documentation needs to be updated for this.

I might change my mind, though :-)

Regards,

	Hans

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v2 2/3] [media] hva: multi-format video encoder V4L2 driver
  2016-07-18 11:45   ` Hans Verkuil
  2016-07-19 15:55     ` Jean Christophe TROTIN
@ 2016-07-21  7:30     ` Jean Christophe TROTIN
  2016-07-21  9:49       ` Hans Verkuil
  1 sibling, 1 reply; 18+ messages in thread
From: Jean Christophe TROTIN @ 2016-07-21  7:30 UTC (permalink / raw)
  To: Hans Verkuil, linux-media
  Cc: kernel, Benjamin Gaignard, Yannick FERTRE, Hugues FRUCHET


On 07/18/2016 01:45 PM, Hans Verkuil wrote:
> Hi Jean-Christophe,
>
> See my review comments below. Nothing really major, but I do need to know more
> about the g/s_parm and the restriction on the number of open()s has to be lifted.
> That's not allowed.
>

Hi Hans,

Thank you for your comments.
I've explained below why I would like to keep 'hva' as driver's name and why the
frame rate is needed (g/s_parm).
I've followed your advice for managing the hardware restriction with regards to
the number of codec instances (see also below).
Finally, I've taken into account all the other comments.
All these modifications will be reflected in the version 3.

Best regards,
Jean-Christophe.

> On 07/11/2016 05:14 PM, Jean-Christophe Trotin wrote:
>> This patch adds V4L2 HVA (Hardware Video Accelerator) video encoder
>> driver for STMicroelectronics SoC. It uses the V4L2 mem2mem framework.
>>
>> This patch only contains the core parts of the driver:
>> - the V4L2 interface with the userland (hva-v4l2.c)
>> - the hardware services (hva-hw.c)
>> - the memory management utilities (hva-mem.c)
>>
>> This patch doesn't include the support of specific codec (e.g. H.264)
>> video encoding: this support is part of subsequent patches.
>>
>> Signed-off-by: Yannick Fertre <yannick.fertre@st.com>
>> Signed-off-by: Jean-Christophe Trotin <jean-christophe.trotin@st.com>
>> ---
>>   drivers/media/platform/Kconfig            |   14 +
>>   drivers/media/platform/Makefile           |    1 +
>>   drivers/media/platform/sti/hva/Makefile   |    2 +
>>   drivers/media/platform/sti/hva/hva-hw.c   |  534 ++++++++++++
>>   drivers/media/platform/sti/hva/hva-hw.h   |   42 +
>>   drivers/media/platform/sti/hva/hva-mem.c  |   60 ++
>>   drivers/media/platform/sti/hva/hva-mem.h  |   36 +
>>   drivers/media/platform/sti/hva/hva-v4l2.c | 1299 +++++++++++++++++++++++++++++
>>   drivers/media/platform/sti/hva/hva.h      |  284 +++++++
>>   9 files changed, 2272 insertions(+)
>>   create mode 100644 drivers/media/platform/sti/hva/Makefile
>>   create mode 100644 drivers/media/platform/sti/hva/hva-hw.c
>>   create mode 100644 drivers/media/platform/sti/hva/hva-hw.h
>>   create mode 100644 drivers/media/platform/sti/hva/hva-mem.c
>>   create mode 100644 drivers/media/platform/sti/hva/hva-mem.h
>>   create mode 100644 drivers/media/platform/sti/hva/hva-v4l2.c
>>   create mode 100644 drivers/media/platform/sti/hva/hva.h
>>
>> diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
>> index 382f393..182d63f 100644
>> --- a/drivers/media/platform/Kconfig
>> +++ b/drivers/media/platform/Kconfig
>> @@ -227,6 +227,20 @@ config VIDEO_STI_BDISP
>>        help
>>          This v4l2 mem2mem driver is a 2D blitter for STMicroelectronics SoC.
>>
>> +config VIDEO_STI_HVA
>> +     tristate "STMicroelectronics STiH41x HVA multi-format video encoder V4L2 driver"
>> +     depends on VIDEO_DEV && VIDEO_V4L2
>> +     depends on ARCH_STI || COMPILE_TEST
>> +     select VIDEOBUF2_DMA_CONTIG
>> +     select V4L2_MEM2MEM_DEV
>> +     help
>> +       This V4L2 driver enables HVA multi-format video encoder of
>
> Please mention here what HVA stands for.
>

Done in version 3.
HVA stands for "Hardware Video Accelerator".

>> +       STMicroelectronics SoC STiH41x series, allowing hardware encoding of raw
>> +       uncompressed formats in various compressed video bitstreams format.
>> +
>> +       To compile this driver as a module, choose M here:
>> +       the module will be called hva.
>
> How about sti-hva as the module name? 'hva' is a bit too generic.
>

'hva' is a generic IP which could be used on different STMicroelectronics SoCs.
That's the reason why I would like to keep this name. It's not specific to  the
STiH41x series: thus, I've reworked the Kconfig's comment.

>> +
>>   config VIDEO_SH_VEU
>>        tristate "SuperH VEU mem2mem video processing driver"
>>        depends on VIDEO_DEV && VIDEO_V4L2 && HAS_DMA
>
> <snip>
>
>> diff --git a/drivers/media/platform/sti/hva/hva-v4l2.c b/drivers/media/platform/sti/hva/hva-v4l2.c
>> new file mode 100644
>> index 0000000..bacc9ff
>> --- /dev/null
>> +++ b/drivers/media/platform/sti/hva/hva-v4l2.c
>> @@ -0,0 +1,1299 @@
>> +/*
>> + * Copyright (C) STMicroelectronics SA 2015
>> + * Authors: Yannick Fertre <yannick.fertre@st.com>
>> + *          Hugues Fruchet <hugues.fruchet@st.com>
>> + * License terms:  GNU General Public License (GPL), version 2
>> + */
>> +
>> +#include <linux/module.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/slab.h>
>> +#include <media/v4l2-event.h>
>> +#include <media/v4l2-ioctl.h>
>> +#include <media/videobuf2-dma-contig.h>
>> +
>> +#include "hva.h"
>> +#include "hva-hw.h"
>> +
>> +#define HVA_NAME "hva"
>> +
>> +#define MIN_FRAMES   1
>> +#define MIN_STREAMS  1
>> +
>> +#define HVA_MIN_WIDTH        32
>> +#define HVA_MAX_WIDTH        1920
>> +#define HVA_MIN_HEIGHT       32
>> +#define HVA_MAX_HEIGHT       1920
>> +
>> +/* HVA requires a 16x16 pixels alignment for frames */
>> +#define HVA_WIDTH_ALIGNMENT  16
>> +#define HVA_HEIGHT_ALIGNMENT 16
>> +
>> +#define DEFAULT_WIDTH                HVA_MIN_WIDTH
>> +#define      DEFAULT_HEIGHT          HVA_MIN_HEIGHT
>> +#define DEFAULT_FRAME_NUM    1
>> +#define DEFAULT_FRAME_DEN    30
>> +
>> +#define to_type_str(type) (type == V4L2_BUF_TYPE_VIDEO_OUTPUT ? \
>> +                        "frame" : "stream")
>> +
>> +#define fh_to_ctx(f)    (container_of(f, struct hva_ctx, fh))
>> +
>> +/* registry of available encoders */
>> +const struct hva_enc *hva_encoders[] = {
>> +};
>> +
>> +static inline int frame_size(u32 w, u32 h, u32 fmt)
>> +{
>> +     switch (fmt) {
>> +     case V4L2_PIX_FMT_NV12:
>> +     case V4L2_PIX_FMT_NV21:
>> +             return (w * h * 3) / 2;
>> +     default:
>> +             return 0;
>> +     }
>> +}
>> +
>> +static inline int frame_stride(u32 w, u32 fmt)
>> +{
>> +     switch (fmt) {
>> +     case V4L2_PIX_FMT_NV12:
>> +     case V4L2_PIX_FMT_NV21:
>> +             return w;
>> +     default:
>> +             return 0;
>> +     }
>> +}
>> +
>> +static inline int frame_alignment(u32 fmt)
>> +{
>> +     switch (fmt) {
>> +     case V4L2_PIX_FMT_NV12:
>> +     case V4L2_PIX_FMT_NV21:
>> +             /* multiple of 2 */
>> +             return 2;
>> +     default:
>> +             return 1;
>> +     }
>> +}
>> +
>> +static inline int estimated_stream_size(u32 w, u32 h)
>> +{
>> +     /*
>> +      * HVA only encodes in YUV420 format, whatever the frame format.
>> +      * A compression ratio of 2 is assumed: thus, the maximum size
>> +      * of a stream is estimated to ((width x height x 3 / 2) / 2)
>> +      */
>> +     return (w * h * 3) / 4;
>> +}
>> +
>> +static void set_default_params(struct hva_ctx *ctx)
>> +{
>> +     struct hva_frameinfo *frameinfo = &ctx->frameinfo;
>> +     struct hva_streaminfo *streaminfo = &ctx->streaminfo;
>> +
>> +     frameinfo->pixelformat = V4L2_PIX_FMT_NV12;
>> +     frameinfo->width = DEFAULT_WIDTH;
>> +     frameinfo->height = DEFAULT_HEIGHT;
>> +     frameinfo->aligned_width = DEFAULT_WIDTH;
>> +     frameinfo->aligned_height = DEFAULT_HEIGHT;
>> +     frameinfo->size = frame_size(frameinfo->aligned_width,
>> +                                  frameinfo->aligned_height,
>> +                                  frameinfo->pixelformat);
>> +
>> +     streaminfo->streamformat = V4L2_PIX_FMT_H264;
>> +     streaminfo->width = DEFAULT_WIDTH;
>> +     streaminfo->height = DEFAULT_HEIGHT;
>> +
>> +     ctx->max_stream_size = estimated_stream_size(streaminfo->width,
>> +                                                  streaminfo->height);
>> +}
>> +
>> +static const struct hva_enc *hva_find_encoder(struct hva_ctx *ctx,
>> +                                           u32 pixelformat,
>> +                                           u32 streamformat)
>> +{
>> +     struct hva_dev *hva = ctx_to_hdev(ctx);
>> +     const struct hva_enc *enc;
>> +     unsigned int i;
>> +
>> +     for (i = 0; i < hva->nb_of_encoders; i++) {
>> +             enc = hva->encoders[i];
>> +             if ((enc->pixelformat == pixelformat) &&
>> +                 (enc->streamformat == streamformat))
>> +                     return enc;
>> +     }
>> +
>> +     return NULL;
>> +}
>> +
>> +static void register_format(u32 format, u32 formats[], u32 *nb_of_formats)
>> +{
>> +     u32 i;
>> +     bool found = false;
>> +
>> +     for (i = 0; i < *nb_of_formats; i++) {
>> +             if (format == formats[i]) {
>> +                     found = true;
>> +                     break;
>> +             }
>> +     }
>> +
>> +     if (!found)
>> +             formats[(*nb_of_formats)++] = format;
>> +}
>> +
>> +static void register_formats(struct hva_dev *hva)
>> +{
>> +     unsigned int i;
>> +
>> +     for (i = 0; i < hva->nb_of_encoders; i++) {
>> +             register_format(hva->encoders[i]->pixelformat,
>> +                             hva->pixelformats,
>> +                             &hva->nb_of_pixelformats);
>> +
>> +             register_format(hva->encoders[i]->streamformat,
>> +                             hva->streamformats,
>> +                             &hva->nb_of_streamformats);
>> +     }
>> +}
>> +
>> +static void register_encoders(struct hva_dev *hva)
>> +{
>> +     struct device *dev = hva_to_dev(hva);
>> +     unsigned int i;
>> +
>> +     for (i = 0; i < ARRAY_SIZE(hva_encoders); i++) {
>> +             if (hva->nb_of_encoders >= HVA_MAX_ENCODERS) {
>> +                     dev_dbg(dev,
>> +                             "%s failed to register encoder (%d maximum reached)\n",
>> +                             hva_encoders[i]->name, HVA_MAX_ENCODERS);
>> +                     return;
>> +             }
>> +
>> +             hva->encoders[hva->nb_of_encoders++] = hva_encoders[i];
>> +             dev_info(dev, "%s encoder registered\n", hva_encoders[i]->name);
>> +     }
>> +}
>> +
>> +static int hva_open_encoder(struct hva_ctx *ctx, u32 streamformat,
>> +                         u32 pixelformat, struct hva_enc **penc)
>> +{
>> +     struct hva_dev *hva = ctx_to_hdev(ctx);
>> +     struct device *dev = ctx_to_dev(ctx);
>> +     struct hva_enc *enc;
>> +     unsigned int i;
>> +     int ret;
>> +     bool found = false;
>> +
>> +     /* find an encoder which can deal with these formats */
>> +     for (i = 0; i < hva->nb_of_encoders; i++) {
>> +             enc = (struct hva_enc *)hva->encoders[i];
>> +             if ((enc->streamformat == streamformat) &&
>> +                 (enc->pixelformat == pixelformat)) {
>> +                     found = true;
>> +                     break;
>> +             }
>> +     }
>> +
>> +     if (!found) {
>> +             dev_err(dev, "%s no encoder found matching %4.4s => %4.4s\n",
>> +                     ctx->name, (char *)&pixelformat, (char *)&streamformat);
>> +             return -EINVAL;
>> +     }
>> +
>> +     dev_dbg(dev, "%s one encoder matching %4.4s => %4.4s\n",
>> +             ctx->name, (char *)&pixelformat, (char *)&streamformat);
>> +
>> +     /* update instance name */
>> +     snprintf(ctx->name, sizeof(ctx->name), "[%3d:%4.4s]",
>> +              hva->instance_id, (char *)&streamformat);
>> +
>> +     /* open encoder instance */
>> +     ret = enc->open(ctx);
>> +     if (ret) {
>> +             dev_err(dev, "%s failed to open encoder instance (%d)\n",
>> +                     ctx->name, ret);
>> +             return ret;
>> +     }
>> +
>> +     dev_dbg(dev, "%s %s encoder opened\n", ctx->name, enc->name);
>> +
>> +     *penc = enc;
>> +
>> +     return ret;
>> +}
>> +
>> +/*
>> + * V4L2 ioctl operations
>> + */
>> +
>> +static int hva_querycap(struct file *file, void *priv,
>> +                     struct v4l2_capability *cap)
>> +{
>> +     struct hva_ctx *ctx = fh_to_ctx(file->private_data);
>> +     struct hva_dev *hva = ctx_to_hdev(ctx);
>> +
>> +     strlcpy(cap->driver, hva->pdev->name, sizeof(cap->driver));
>> +     strlcpy(cap->card, hva->pdev->name, sizeof(cap->card));
>> +     snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
>> +              HVA_NAME);
>> +
>> +     cap->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M;
>> +     cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
>
> Set the new device_caps field of struct video_device to
> V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M and drop these two lines.
>
> The v4l2 core will set device_caps and capabilities for you based on the
> video_device struct device_caps field. New drivers should use this.
>
> The advantage is that the v4l2 core now knows the caps of the video node.
>

Done in version 3.

>> +
>> +     return 0;
>> +}
>> +
>> +static int hva_enum_fmt_stream(struct file *file, void *priv,
>> +                            struct v4l2_fmtdesc *f)
>> +{
>> +     struct hva_ctx *ctx = fh_to_ctx(file->private_data);
>> +     struct hva_dev *hva = ctx_to_hdev(ctx);
>> +
>> +     if (unlikely(f->index >= hva->nb_of_streamformats))
>> +             return -EINVAL;
>> +
>> +     f->pixelformat = hva->streamformats[f->index];
>> +     snprintf(f->description, sizeof(f->description), "%4.4s",
>> +              (char *)&f->pixelformat);
>> +     f->flags = V4L2_FMT_FLAG_COMPRESSED;
>
> Drop these two lines. The v4l2 code fills in the description and flags for
> you.
>

Done in version 3.

>> +
>> +     return 0;
>> +}
>> +
>> +static int hva_enum_fmt_frame(struct file *file, void *priv,
>> +                           struct v4l2_fmtdesc *f)
>> +{
>> +     struct hva_ctx *ctx = fh_to_ctx(file->private_data);
>> +     struct hva_dev *hva = ctx_to_hdev(ctx);
>> +
>> +     if (unlikely(f->index >= hva->nb_of_pixelformats))
>> +             return -EINVAL;
>> +
>> +     f->pixelformat = hva->pixelformats[f->index];
>> +     snprintf(f->description, sizeof(f->description), "%4.4s",
>> +              (char *)&f->pixelformat);
>
> Ditto.
>

Done in version 3.

>> +
>> +     return 0;
>> +}
>> +
>> +static int hva_g_fmt_stream(struct file *file, void *fh, struct v4l2_format *f)
>> +{
>> +     struct hva_ctx *ctx = fh_to_ctx(file->private_data);
>> +     struct device *dev = ctx_to_dev(ctx);
>> +     struct hva_streaminfo *streaminfo = &ctx->streaminfo;
>> +
>> +     f->fmt.pix.width = streaminfo->width;
>> +     f->fmt.pix.height = streaminfo->height;
>> +     f->fmt.pix.field = V4L2_FIELD_NONE;
>> +     f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
>
> This is a mem2mem device, so the colorspace comes from the colorspace that
> the application specified for the video output format. That's copied to the
> video capture format. See e.g. the vim2m.c example driver.
>
> The default colorspace should be REC709 as well, rather than SMPTE170M (that's
> for SDTV).
>
> I've added checks to v4l2-compliance to improve testing for this.
>
> See also the vim2m patch I just posted where I patch that m2m driver so it
> passes the compliance test.
>

Done in version 3.
I've aligned the code in version 3 on the colorspace management made in the
vim2m and mtk-vcodec drivers.

>> +     f->fmt.pix.pixelformat = streaminfo->streamformat;
>> +     f->fmt.pix.bytesperline = 0;
>> +     f->fmt.pix.sizeimage = ctx->max_stream_size;
>> +
>> +     dev_dbg(dev, "%s V4L2 G_FMT (CAPTURE): %dx%d fmt:%.4s size:%d\n",
>> +             ctx->name, f->fmt.pix.width, f->fmt.pix.height,
>> +             (u8 *)&f->fmt.pix.pixelformat, f->fmt.pix.sizeimage);
>
> No need for these debug messages. You can always debug the ioctls by:
>
> echo 2 >/sys/class/video4linux/video0/dev_debug.
>

Done in version 3 in hva_g_fmt_stream, hva_g_fmt_frame, hva_s_fmt_stream,
hva_s_fmt_frame, hva_s_parm and hva_g_parm functions.

>> +     return 0;
>> +}
>
> <snip>
>
>> +
>> +static int hva_s_parm(struct file *file, void *fh, struct v4l2_streamparm *sp)
>> +{
>> +     struct hva_ctx *ctx = fh_to_ctx(file->private_data);
>> +     struct device *dev = ctx_to_dev(ctx);
>> +     struct v4l2_fract *time_per_frame = &ctx->ctrls.time_per_frame;
>> +
>> +     time_per_frame->numerator = sp->parm.capture.timeperframe.numerator;
>> +     time_per_frame->denominator =
>> +             sp->parm.capture.timeperframe.denominator;
>> +
>> +     dev_dbg(dev, "%s set parameters %d/%d\n", ctx->name,
>> +             time_per_frame->numerator, time_per_frame->denominator);
>> +
>> +     return 0;
>> +}
>> +
>> +static int hva_g_parm(struct file *file, void *fh, struct v4l2_streamparm *sp)
>> +{
>> +     struct hva_ctx *ctx = fh_to_ctx(file->private_data);
>> +     struct device *dev = ctx_to_dev(ctx);
>> +     struct v4l2_fract *time_per_frame = &ctx->ctrls.time_per_frame;
>> +
>> +     sp->parm.capture.timeperframe.numerator = time_per_frame->numerator;
>> +     sp->parm.capture.timeperframe.denominator =
>> +             time_per_frame->denominator;
>> +
>> +     dev_dbg(dev, "%s get parameters %d/%d\n", ctx->name,
>> +             time_per_frame->numerator, time_per_frame->denominator);
>> +
>> +     return 0;
>> +}
>
> This is strange. Normally codecs don't need this. You give them a buffer and
> it will be encoded/decoded and then you give it the next one if it is available.
> There is normally no frame rate involved.
>
> How does this work in this SoC? I need to know a bit more about this to be
> certain there isn't a misunderstanding somewhere.
>

Among the parameters dimensioning its buffer model, the 'hva' HW IP needs to
calculate the depletion that is the quantity of bits at the output of the
encoder in each time slot, basically bitrate/framerate. That's the reason for
these 2 functions.
Furthermore, I've seen that mtk-vcodec and coda encoders also get the frame rate
to configure their HW IPs (vidioc_venc_s_parm & coda_s_parm).

>> +
>> +static int hva_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
>> +{
>> +     struct hva_ctx *ctx = fh_to_ctx(file->private_data);
>> +     struct device *dev = ctx_to_dev(ctx);
>> +
>> +     if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
>> +             /*
>> +              * depending on the targeted compressed video format, the
>> +              * capture buffer might contain headers (e.g. H.264 SPS/PPS)
>> +              * filled in by the driver client; the size of these data is
>> +              * copied from the bytesused field of the V4L2 buffer in the
>> +              * payload field of the hva stream buffer
>> +              */
>> +             struct vb2_queue *vq;
>> +             struct hva_stream *stream;
>> +
>> +             vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, buf->type);
>> +
>> +             if (buf->index >= vq->num_buffers) {
>> +                     dev_dbg(dev, "%s buffer index %d out of range (%d)\n",
>> +                             ctx->name, buf->index, vq->num_buffers);
>> +                     return -EINVAL;
>> +             }
>> +
>> +             stream = (struct hva_stream *)vq->bufs[buf->index];
>> +             stream->bytesused = buf->bytesused;
>> +     }
>> +
>> +     return v4l2_m2m_qbuf(file, ctx->fh.m2m_ctx, buf);
>> +}
>> +
>> +/* V4L2 ioctl ops */
>> +static const struct v4l2_ioctl_ops hva_ioctl_ops = {
>> +     .vidioc_querycap                = hva_querycap,
>> +     .vidioc_enum_fmt_vid_cap        = hva_enum_fmt_stream,
>> +     .vidioc_enum_fmt_vid_out        = hva_enum_fmt_frame,
>> +     .vidioc_g_fmt_vid_cap           = hva_g_fmt_stream,
>> +     .vidioc_g_fmt_vid_out           = hva_g_fmt_frame,
>> +     .vidioc_try_fmt_vid_cap         = hva_try_fmt_stream,
>> +     .vidioc_try_fmt_vid_out         = hva_try_fmt_frame,
>> +     .vidioc_s_fmt_vid_cap           = hva_s_fmt_stream,
>> +     .vidioc_s_fmt_vid_out           = hva_s_fmt_frame,
>> +     .vidioc_g_parm                  = hva_g_parm,
>> +     .vidioc_s_parm                  = hva_s_parm,
>> +     .vidioc_reqbufs                 = v4l2_m2m_ioctl_reqbufs,
>> +     .vidioc_create_bufs             = v4l2_m2m_ioctl_create_bufs,
>> +     .vidioc_querybuf                = v4l2_m2m_ioctl_querybuf,
>> +     .vidioc_expbuf                  = v4l2_m2m_ioctl_expbuf,
>> +     .vidioc_qbuf                    = hva_qbuf,
>> +     .vidioc_dqbuf                   = v4l2_m2m_ioctl_dqbuf,
>> +     .vidioc_streamon                = v4l2_m2m_ioctl_streamon,
>> +     .vidioc_streamoff               = v4l2_m2m_ioctl_streamoff,
>> +     .vidioc_subscribe_event         = v4l2_ctrl_subscribe_event,
>> +     .vidioc_unsubscribe_event       = v4l2_event_unsubscribe,
>> +};
>> +
>> +/*
>> + * V4L2 control operations
>> + */
>> +
>> +static int hva_s_ctrl(struct v4l2_ctrl *ctrl)
>> +{
>> +     struct hva_ctx *ctx = container_of(ctrl->handler, struct hva_ctx,
>> +                                        ctrl_handler);
>> +     struct device *dev = ctx_to_dev(ctx);
>> +
>> +     dev_dbg(dev, "%s S_CTRL: id = %d, val = %d\n", ctx->name,
>> +             ctrl->id, ctrl->val);
>> +
>> +     switch (ctrl->id) {
>> +     case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
>> +             ctx->ctrls.bitrate_mode = ctrl->val;
>> +             break;
>> +     case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
>> +             ctx->ctrls.gop_size = ctrl->val;
>> +             break;
>> +     case V4L2_CID_MPEG_VIDEO_BITRATE:
>> +             ctx->ctrls.bitrate = ctrl->val;
>> +             break;
>> +     case V4L2_CID_MPEG_VIDEO_ASPECT:
>> +             ctx->ctrls.aspect = ctrl->val;
>> +             break;
>> +     default:
>> +             dev_dbg(dev, "%s S_CTRL: invalid control (id = %d)\n",
>> +                     ctx->name, ctrl->id);
>> +             return -EINVAL;
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +/* V4L2 control ops */
>> +static const struct v4l2_ctrl_ops hva_ctrl_ops = {
>> +     .s_ctrl = hva_s_ctrl,
>> +};
>> +
>> +static int hva_ctrls_setup(struct hva_ctx *ctx)
>> +{
>> +     struct device *dev = ctx_to_dev(ctx);
>> +     u64 mask;
>> +
>> +     v4l2_ctrl_handler_init(&ctx->ctrl_handler, 4);
>> +
>> +     v4l2_ctrl_new_std_menu(&ctx->ctrl_handler, &hva_ctrl_ops,
>> +                            V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
>> +                            V4L2_MPEG_VIDEO_BITRATE_MODE_CBR,
>> +                            0,
>> +                            V4L2_MPEG_VIDEO_BITRATE_MODE_CBR);
>> +
>> +     v4l2_ctrl_new_std(&ctx->ctrl_handler, &hva_ctrl_ops,
>> +                       V4L2_CID_MPEG_VIDEO_GOP_SIZE,
>> +                       1, 60, 1, 16);
>> +
>> +     v4l2_ctrl_new_std(&ctx->ctrl_handler, &hva_ctrl_ops,
>> +                       V4L2_CID_MPEG_VIDEO_BITRATE,
>> +                       1, 50000, 1, 20000);
>> +
>> +     mask = ~(1 << V4L2_MPEG_VIDEO_ASPECT_1x1);
>> +     v4l2_ctrl_new_std_menu(&ctx->ctrl_handler, &hva_ctrl_ops,
>> +                            V4L2_CID_MPEG_VIDEO_ASPECT,
>> +                            V4L2_MPEG_VIDEO_ASPECT_1x1,
>> +                            mask,
>> +                            V4L2_MPEG_VIDEO_ASPECT_1x1);
>> +
>> +     if (ctx->ctrl_handler.error) {
>> +             int err = ctx->ctrl_handler.error;
>> +
>> +             dev_dbg(dev, "%s controls setup failed (%d)\n",
>> +                     ctx->name, err);
>> +             v4l2_ctrl_handler_free(&ctx->ctrl_handler);
>> +             return err;
>> +     }
>> +
>> +     v4l2_ctrl_handler_setup(&ctx->ctrl_handler);
>> +
>> +     /* set default time per frame */
>> +     ctx->ctrls.time_per_frame.numerator = DEFAULT_FRAME_NUM;
>> +     ctx->ctrls.time_per_frame.denominator = DEFAULT_FRAME_DEN;
>> +
>> +     return 0;
>> +}
>> +
>> +/*
>> + * mem-to-mem operations
>> + */
>> +
>> +static void hva_run_work(struct work_struct *work)
>> +{
>> +     struct hva_ctx *ctx = container_of(work, struct hva_ctx, run_work);
>> +     struct vb2_v4l2_buffer *src_buf, *dst_buf;
>> +     const struct hva_enc *enc = ctx->enc;
>> +     struct hva_frame *frame;
>> +     struct hva_stream *stream;
>> +     int ret;
>> +
>> +     /* protect instance against reentrancy */
>> +     mutex_lock(&ctx->lock);
>> +
>> +     src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
>> +     dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
>> +
>> +     frame = to_hva_frame(src_buf);
>> +     stream = to_hva_stream(dst_buf);
>> +     frame->vbuf.sequence = ctx->frame_num++;
>> +
>> +     ret = enc->encode(ctx, frame, stream);
>> +
>> +     if (ret) {
>> +             v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR);
>> +             v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR);
>> +     } else {
>> +             /* propagate frame timestamp */
>> +             dst_buf->vb2_buf.timestamp = src_buf->vb2_buf.timestamp;
>> +             dst_buf->field = V4L2_FIELD_NONE;
>> +             dst_buf->sequence = ctx->stream_num - 1;
>> +
>> +             v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE);
>> +             v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE);
>> +     }
>> +
>> +     mutex_unlock(&ctx->lock);
>> +
>> +     v4l2_m2m_job_finish(ctx->hva_dev->m2m_dev, ctx->fh.m2m_ctx);
>> +}
>> +
>> +static void hva_device_run(void *priv)
>> +{
>> +     struct hva_ctx *ctx = priv;
>> +     struct hva_dev *hva = ctx_to_hdev(ctx);
>> +
>> +     queue_work(hva->work_queue, &ctx->run_work);
>> +}
>> +
>> +static void hva_job_abort(void *priv)
>> +{
>> +     struct hva_ctx *ctx = priv;
>> +     struct device *dev = ctx_to_dev(ctx);
>> +
>> +     dev_dbg(dev, "%s aborting job\n", ctx->name);
>> +
>> +     ctx->aborting = true;
>> +}
>> +
>> +static int hva_job_ready(void *priv)
>> +{
>> +     struct hva_ctx *ctx = priv;
>> +     struct device *dev = ctx_to_dev(ctx);
>> +
>> +     if (!v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx)) {
>> +             dev_dbg(dev, "%s job not ready: no frame buffers\n",
>> +                     ctx->name);
>> +             return 0;
>> +     }
>> +
>> +     if (!v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx)) {
>> +             dev_dbg(dev, "%s job not ready: no stream buffers\n",
>> +                     ctx->name);
>> +             return 0;
>> +     }
>> +
>> +     if (ctx->aborting) {
>> +             dev_dbg(dev, "%s job not ready: aborting\n", ctx->name);
>> +             return 0;
>> +     }
>> +
>> +     return 1;
>> +}
>> +
>> +/* mem-to-mem ops */
>> +static const struct v4l2_m2m_ops hva_m2m_ops = {
>> +     .device_run     = hva_device_run,
>> +     .job_abort      = hva_job_abort,
>> +     .job_ready      = hva_job_ready,
>> +};
>> +
>> +/*
>> + * VB2 queue operations
>> + */
>> +
>> +static int hva_queue_setup(struct vb2_queue *vq,
>> +                        unsigned int *num_buffers, unsigned int *num_planes,
>> +                        unsigned int sizes[], void *alloc_ctxs[])
>
> This patch needs to be rebased: the way allocation contexts are set up has
> changed. You now set the new 'dev' field in vb2_queue to the struct device,
> and there is no longer any need to fill in alloc_ctxs here or init/free the
> allocation context. The prototype of this queue_setup function has
> changed as well.
>

I apologize for forgetting this rebase.
Done in version 3.

>> +{
>> +     struct hva_ctx *ctx = vb2_get_drv_priv(vq);
>> +     struct device *dev = ctx_to_dev(ctx);
>> +     unsigned int size;
>> +
>> +     dev_dbg(dev, "%s %s queue setup: num_buffers %d\n", ctx->name,
>> +             to_type_str(vq->type), *num_buffers);
>> +
>> +     size = vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT ?
>> +             ctx->frameinfo.size : ctx->max_stream_size;
>> +
>> +     alloc_ctxs[0] = ctx->hva_dev->alloc_ctx;
>> +
>> +     if (*num_planes)
>> +             return sizes[0] < size ? -EINVAL : 0;
>> +
>> +     /* only one plane supported */
>> +     *num_planes = 1;
>> +     sizes[0] = size;
>> +
>> +     return 0;
>> +}
>> +
>> +static int hva_buf_prepare(struct vb2_buffer *vb)
>> +{
>> +     struct hva_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
>> +     struct device *dev = ctx_to_dev(ctx);
>> +     struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
>> +
>> +     if (vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
>> +             struct hva_frame *frame = to_hva_frame(vbuf);
>> +
>> +             if (vbuf->field == V4L2_FIELD_ANY)
>> +                     vbuf->field = V4L2_FIELD_NONE;
>
> Anything other than FIELD_NONE should result in an error since no interlaced is supported.
> FIELD_ANY is an incorrect value as well.
>

These 2 lines are kept as discussed in a previous email.

>> +             if (vbuf->field != V4L2_FIELD_NONE) {
>> +                     dev_dbg(dev,
>> +                             "%s frame[%d] prepare: %d field not supported\n",
>> +                             ctx->name, vb->index, vbuf->field);
>> +                     return -EINVAL;
>> +             }
>> +
>> +             if (!frame->prepared) {
>> +                     /* get memory addresses */
>> +                     frame->vaddr = vb2_plane_vaddr(&vbuf->vb2_buf, 0);
>> +                     frame->paddr = vb2_dma_contig_plane_dma_addr(
>> +                                     &vbuf->vb2_buf, 0);
>> +                     frame->info = ctx->frameinfo;
>> +                     frame->prepared = true;
>> +
>> +                     dev_dbg(dev,
>> +                             "%s frame[%d] prepared; virt=%p, phy=%pad\n",
>> +                             ctx->name, vb->index,
>> +                             frame->vaddr, &frame->paddr);
>> +             }
>> +     } else {
>> +             struct hva_stream *stream = to_hva_stream(vbuf);
>> +
>> +             if (!stream->prepared) {
>> +                     /* get memory addresses */
>> +                     stream->vaddr = vb2_plane_vaddr(&vbuf->vb2_buf, 0);
>> +                     stream->paddr = vb2_dma_contig_plane_dma_addr(
>> +                                     &vbuf->vb2_buf, 0);
>> +                     stream->size = vb2_plane_size(&vbuf->vb2_buf, 0);
>> +                     stream->prepared = true;
>> +
>> +                     dev_dbg(dev,
>> +                             "%s stream[%d] prepared; virt=%p, phy=%pad\n",
>> +                             ctx->name, vb->index,
>> +                             stream->vaddr, &stream->paddr);
>> +             }
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +static void hva_buf_queue(struct vb2_buffer *vb)
>> +{
>> +     struct hva_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
>> +     struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
>> +
>> +     if (ctx->fh.m2m_ctx)
>> +             v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
>> +}
>> +
>> +static int hva_start_streaming(struct vb2_queue *vq, unsigned int count)
>> +{
>> +     struct hva_ctx *ctx = vb2_get_drv_priv(vq);
>> +     struct device *dev = ctx_to_dev(ctx);
>> +     int ret = 0;
>> +
>> +     dev_dbg(dev, "%s %s start streaming\n", ctx->name,
>> +             to_type_str(vq->type));
>> +
>> +     /* open encoder when both start_streaming have been called */
>> +     if (V4L2_TYPE_IS_OUTPUT(vq->type)) {
>> +             if (!vb2_start_streaming_called(&ctx->fh.m2m_ctx->cap_q_ctx.q))
>> +                     return 0;
>> +     } else {
>> +             if (!vb2_start_streaming_called(&ctx->fh.m2m_ctx->out_q_ctx.q))
>> +                     return 0;
>> +     }
>> +
>> +     if (!ctx->enc)
>> +             ret = hva_open_encoder(ctx,
>> +                                    ctx->streaminfo.streamformat,
>> +                                    ctx->frameinfo.pixelformat,
>> +                                    &ctx->enc);
>
> On error all pending buffers for queue vq should be returned to vb2 in the QUEUED state.
> Similar to what happens in stop_streaming, but with state QUEUED instead of state ERROR.
>

Done in version 3.

>> +
>> +     return ret;
>> +}
>> +
>> +static void hva_stop_streaming(struct vb2_queue *vq)
>> +{
>> +     struct hva_ctx *ctx = vb2_get_drv_priv(vq);
>> +     struct device *dev = ctx_to_dev(ctx);
>> +     const struct hva_enc *enc = ctx->enc;
>> +     struct vb2_v4l2_buffer *vbuf;
>> +
>> +     dev_dbg(dev, "%s %s stop streaming\n", ctx->name,
>> +             to_type_str(vq->type));
>> +
>> +     if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
>> +             /* return of all pending buffers to vb2 (in error state) */
>> +             ctx->frame_num = 0;
>> +             while ((vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx)))
>> +                     v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
>> +     } else {
>> +             /* return of all pending buffers to vb2 (in error state) */
>> +             ctx->stream_num = 0;
>> +             while ((vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx)))
>> +                     v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
>> +     }
>> +
>> +     if ((V4L2_TYPE_IS_OUTPUT(vq->type) &&
>> +          vb2_is_streaming(&ctx->fh.m2m_ctx->cap_q_ctx.q)) ||
>> +         (!V4L2_TYPE_IS_OUTPUT(vq->type) &&
>> +          vb2_is_streaming(&ctx->fh.m2m_ctx->out_q_ctx.q))) {
>> +             dev_dbg(dev, "%s %s out=%d cap=%d\n",
>> +                     ctx->name, to_type_str(vq->type),
>> +                     vb2_is_streaming(&ctx->fh.m2m_ctx->out_q_ctx.q),
>> +                     vb2_is_streaming(&ctx->fh.m2m_ctx->cap_q_ctx.q));
>> +             return;
>> +     }
>> +
>> +     /* close encoder when both stop_streaming have been called */
>> +     if (enc) {
>> +             dev_dbg(dev, "%s %s encoder closed\n", ctx->name, enc->name);
>> +             enc->close(ctx);
>> +             ctx->enc = NULL;
>> +     }
>> +
>> +     ctx->aborting = false;
>> +}
>> +
>> +/* VB2 queue ops */
>> +static const struct vb2_ops hva_qops = {
>> +     .queue_setup            = hva_queue_setup,
>> +     .buf_prepare            = hva_buf_prepare,
>> +     .buf_queue              = hva_buf_queue,
>> +     .start_streaming        = hva_start_streaming,
>> +     .stop_streaming         = hva_stop_streaming,
>> +     .wait_prepare           = vb2_ops_wait_prepare,
>> +     .wait_finish            = vb2_ops_wait_finish,
>> +};
>> +
>> +/*
>> + * V4L2 file operations
>> + */
>> +
>> +static int queue_init(struct hva_ctx *ctx, struct vb2_queue *vq)
>> +{
>> +     vq->io_modes = VB2_MMAP | VB2_DMABUF;
>> +     vq->drv_priv = ctx;
>> +     vq->ops = &hva_qops;
>> +     vq->mem_ops = &vb2_dma_contig_memops;
>> +     vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
>> +     vq->lock = &ctx->hva_dev->lock;
>> +
>> +     return vb2_queue_init(vq);
>> +}
>> +
>> +static int hva_queue_init(void *priv, struct vb2_queue *src_vq,
>> +                       struct vb2_queue *dst_vq)
>> +{
>> +     struct hva_ctx *ctx = priv;
>> +     int ret;
>> +
>> +     src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
>> +     src_vq->buf_struct_size = sizeof(struct hva_frame);
>> +     src_vq->min_buffers_needed = MIN_FRAMES;
>> +
>> +     ret = queue_init(ctx, src_vq);
>> +     if (ret)
>> +             return ret;
>> +
>> +     dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
>> +     dst_vq->buf_struct_size = sizeof(struct hva_stream);
>> +     dst_vq->min_buffers_needed = MIN_STREAMS;
>> +
>> +     return queue_init(ctx, dst_vq);
>> +}
>> +
>> +static int hva_open(struct file *file)
>> +{
>> +     struct hva_dev *hva = video_drvdata(file);
>> +     struct device *dev = hva_to_dev(hva);
>> +     struct hva_ctx *ctx;
>> +     int ret;
>> +     unsigned int i;
>> +     bool found = false;
>> +
>> +     ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
>> +     if (!ctx) {
>> +             ret = -ENOMEM;
>> +             goto out;
>> +     }
>> +     ctx->hva_dev = hva;
>> +
>> +     mutex_lock(&hva->lock);
>> +
>> +     /* store the instance context in the instances array */
>> +     for (i = 0; i < HVA_MAX_INSTANCES; i++) {
>> +             if (!hva->instances[i]) {
>> +                     hva->instances[i] = ctx;
>> +                     /* save the context identifier in the context */
>> +                     ctx->id = i;
>> +                     found = true;
>> +                     break;
>> +             }
>> +     }
>> +
>> +     if (!found) {
>> +             dev_err(dev, "%s [x:x] maximum instances reached\n",
>> +                     HVA_PREFIX);
>> +             ret = -ENOMEM;
>> +             goto mem_ctx;
>> +     }
>
> This is wrong. It should always be possible to open the device node and
> e.g. query the format or control settings, or whatever.
>
> In this case there is apparently a hardware restriction with regards to
> the number of codec instances. It *is* a hardware restriction, right?
> If it is a driver restriction, then that's certainly wrong since there
> is normally no reason for that.
>
> Assuming it is a HW restriction, then this restriction is normally
> checked in start_streaming or in queue_setup. I.e. at the point where
> the HW resource reservation actually takes place.
>

I confirm that there's a hardware restriction with regards to the number of
codec instances. I agree with you that it shall be checked in start_streaming.
Done in version 3.

>> +
>> +     INIT_WORK(&ctx->run_work, hva_run_work);
>> +     v4l2_fh_init(&ctx->fh, video_devdata(file));
>> +     file->private_data = &ctx->fh;
>> +     v4l2_fh_add(&ctx->fh);
>> +
>> +     ret = hva_ctrls_setup(ctx);
>> +     if (ret) {
>> +             dev_err(dev, "%s [x:x] failed to setup controls\n",
>> +                     HVA_PREFIX);
>> +             goto err_fh;
>> +     }
>> +     ctx->fh.ctrl_handler = &ctx->ctrl_handler;
>> +
>> +     mutex_init(&ctx->lock);
>> +
>> +     ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(hva->m2m_dev, ctx,
>> +                                         &hva_queue_init);
>> +     if (IS_ERR(ctx->fh.m2m_ctx)) {
>> +             ret = PTR_ERR(ctx->fh.m2m_ctx);
>> +             dev_err(dev, "%s [x:x] failed to initialize m2m context (%d)\n",
>> +                     HVA_PREFIX, ret);
>> +             goto err_ctrls;
>> +     }
>> +
>> +     /* set the instance name */
>> +     hva->instance_id++;
>> +     snprintf(ctx->name, sizeof(ctx->name), "[%3d:----]",
>> +              hva->instance_id);
>> +
>> +     hva->nb_of_instances++;
>> +
>> +     mutex_unlock(&hva->lock);
>> +
>> +     /* default parameters for frame and stream */
>> +     set_default_params(ctx);
>> +
>> +     dev_info(dev, "%s encoder instance created (id %d)\n",
>> +              ctx->name, ctx->id);
>> +
>> +     return 0;
>> +
>> +err_ctrls:
>> +     v4l2_ctrl_handler_free(&ctx->ctrl_handler);
>> +err_fh:
>> +     v4l2_fh_del(&ctx->fh);
>> +     v4l2_fh_exit(&ctx->fh);
>> +     hva->instances[ctx->id] = NULL;
>> +mem_ctx:
>> +     kfree(ctx);
>> +     mutex_unlock(&hva->lock);
>> +out:
>> +     return ret;
>> +}
>> +
>> +static int hva_release(struct file *file)
>> +{
>> +     struct hva_dev *hva = video_drvdata(file);
>> +     struct hva_ctx *ctx = fh_to_ctx(file->private_data);
>> +     struct device *dev = ctx_to_dev(ctx);
>> +
>> +     v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
>> +
>> +     v4l2_ctrl_handler_free(&ctx->ctrl_handler);
>> +
>> +     v4l2_fh_del(&ctx->fh);
>> +     v4l2_fh_exit(&ctx->fh);
>> +
>> +     mutex_lock(&hva->lock);
>> +
>> +     /* clear instance context in instances array */
>> +     hva->instances[ctx->id] = NULL;
>> +
>> +     hva->nb_of_instances--;
>> +
>> +     mutex_unlock(&hva->lock);
>> +
>> +     dev_info(dev, "%s encoder instance released (id %d)\n",
>> +              ctx->name, ctx->id);
>> +
>> +     kfree(ctx);
>> +
>> +     return 0;
>> +}
>> +
>> +/* V4L2 file ops */
>> +static const struct v4l2_file_operations hva_fops = {
>> +     .owner                  = THIS_MODULE,
>> +     .open                   = hva_open,
>> +     .release                = hva_release,
>> +     .unlocked_ioctl         = video_ioctl2,
>> +     .mmap                   = v4l2_m2m_fop_mmap,
>> +     .poll                   = v4l2_m2m_fop_poll,
>> +};
>> +
>> +/*
>> + * Platform device operations
>> + */
>> +
>> +static int hva_register_device(struct hva_dev *hva)
>> +{
>> +     int ret;
>> +     struct video_device *vdev;
>> +     struct device *dev;
>> +
>> +     if (!hva)
>> +             return -ENODEV;
>> +     dev = hva_to_dev(hva);
>> +
>> +     hva->m2m_dev = v4l2_m2m_init(&hva_m2m_ops);
>> +     if (IS_ERR(hva->m2m_dev)) {
>> +             dev_err(dev, "%s %s failed to initialize v4l2-m2m device\n",
>> +                     HVA_PREFIX, HVA_NAME);
>> +             ret = PTR_ERR(hva->m2m_dev);
>> +             goto err;
>> +     }
>> +
>> +     vdev = video_device_alloc();
>> +     if (!vdev) {
>> +             dev_err(dev, "%s %s failed to allocate video device\n",
>> +                     HVA_PREFIX, HVA_NAME);
>> +             ret = -ENOMEM;
>> +             goto err_m2m_release;
>> +     }
>> +
>> +     vdev->fops = &hva_fops;
>> +     vdev->ioctl_ops = &hva_ioctl_ops;
>> +     vdev->release = video_device_release;
>> +     vdev->lock = &hva->lock;
>> +     vdev->vfl_dir = VFL_DIR_M2M;
>> +     vdev->v4l2_dev = &hva->v4l2_dev;
>> +     snprintf(vdev->name, sizeof(vdev->name), "%s", HVA_NAME);
>> +
>> +     ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
>> +     if (ret) {
>> +             dev_err(dev, "%s %s failed to register video device\n",
>> +                     HVA_PREFIX, HVA_NAME);
>> +             goto err_vdev_release;
>> +     }
>> +
>> +     hva->vdev = vdev;
>> +     video_set_drvdata(vdev, hva);
>> +     return 0;
>> +
>> +err_vdev_release:
>> +     video_device_release(vdev);
>> +err_m2m_release:
>> +     v4l2_m2m_release(hva->m2m_dev);
>> +err:
>> +     return ret;
>> +}
>> +
>> +static void hva_unregister_device(struct hva_dev *hva)
>> +{
>> +     if (!hva)
>> +             return;
>> +
>> +     if (hva->m2m_dev)
>> +             v4l2_m2m_release(hva->m2m_dev);
>> +
>> +     video_unregister_device(hva->vdev);
>> +}
>> +
>> +static int hva_probe(struct platform_device *pdev)
>> +{
>> +     struct hva_dev *hva;
>> +     struct device *dev = &pdev->dev;
>> +     int ret;
>> +
>> +     hva = devm_kzalloc(dev, sizeof(*hva), GFP_KERNEL);
>> +     if (!hva) {
>> +             ret = -ENOMEM;
>> +             goto err;
>> +     }
>> +
>> +     hva->dev = dev;
>> +     hva->pdev = pdev;
>> +     platform_set_drvdata(pdev, hva);
>> +
>> +     mutex_init(&hva->lock);
>> +
>> +     /* probe hardware */
>> +     ret = hva_hw_probe(pdev, hva);
>> +     if (ret)
>> +             goto err;
>> +
>> +     /* register all available encoders */
>> +     register_encoders(hva);
>> +
>> +     /* register all supported formats */
>> +     register_formats(hva);
>> +
>> +     /* register on V4L2 */
>> +     ret = v4l2_device_register(dev, &hva->v4l2_dev);
>> +     if (ret) {
>> +             dev_err(dev, "%s %s failed to register V4L2 device\n",
>> +                     HVA_PREFIX, HVA_NAME);
>> +             goto err_hw;
>> +     }
>> +
>> +     /* continuous memory allocator */
>> +     hva->alloc_ctx = vb2_dma_contig_init_ctx(dev);
>> +     if (IS_ERR(hva->alloc_ctx)) {
>> +             ret = PTR_ERR(hva->alloc_ctx);
>> +             goto err_v4l2;
>> +     }
>> +
>> +     hva->work_queue = create_workqueue(HVA_NAME);
>> +     if (!hva->work_queue) {
>> +             dev_err(dev, "%s %s failed to allocate work queue\n",
>> +                     HVA_PREFIX, HVA_NAME);
>> +             ret = -ENOMEM;
>> +             goto err_vb2_dma;
>> +     }
>> +
>> +     /* register device */
>> +     ret = hva_register_device(hva);
>> +     if (ret)
>> +             goto err_work_queue;
>> +
>> +     dev_info(dev, "%s %s registered as /dev/video%d\n", HVA_PREFIX,
>> +              HVA_NAME, hva->vdev->num);
>> +
>> +     return 0;
>> +
>> +err_work_queue:
>> +     destroy_workqueue(hva->work_queue);
>> +err_vb2_dma:
>> +     vb2_dma_contig_cleanup_ctx(hva->alloc_ctx);
>> +err_v4l2:
>> +     v4l2_device_unregister(&hva->v4l2_dev);
>> +err_hw:
>> +     hva_hw_remove(hva);
>> +err:
>> +     return ret;
>> +}
>> +
>> +static int hva_remove(struct platform_device *pdev)
>> +{
>> +     struct hva_dev *hva = platform_get_drvdata(pdev);
>> +     struct device *dev = hva_to_dev(hva);
>> +
>> +     hva_unregister_device(hva);
>> +
>> +     destroy_workqueue(hva->work_queue);
>> +
>> +     vb2_dma_contig_cleanup_ctx(hva->alloc_ctx);
>> +
>> +     hva_hw_remove(hva);
>> +
>> +     v4l2_device_unregister(&hva->v4l2_dev);
>> +
>> +     dev_info(dev, "%s %s removed\n", HVA_PREFIX, pdev->name);
>> +
>> +     return 0;
>> +}
>> +
>> +/* PM ops */
>> +static const struct dev_pm_ops hva_pm_ops = {
>> +     .runtime_suspend        = hva_hw_runtime_suspend,
>> +     .runtime_resume         = hva_hw_runtime_resume,
>> +};
>> +
>> +static const struct of_device_id hva_match_types[] = {
>> +     {
>> +      .compatible = "st,sti-hva",
>> +     },
>> +     { /* end node */ }
>> +};
>> +
>> +MODULE_DEVICE_TABLE(of, hva_match_types);
>> +
>> +struct platform_driver hva_driver = {
>> +     .probe  = hva_probe,
>> +     .remove = hva_remove,
>> +     .driver = {
>> +             .name           = HVA_NAME,
>> +             .owner          = THIS_MODULE,
>> +             .of_match_table = hva_match_types,
>> +             .pm             = &hva_pm_ops,
>> +             },
>
> Wrong indentation?
>

Done in version 3.

>> +};
>> +
>> +module_platform_driver(hva_driver);
>> +
>> +MODULE_LICENSE("GPL");
>> +MODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>");
>> +MODULE_DESCRIPTION("HVA video encoder V4L2 driver");
>
> Regards,
>
>          Hans
>

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v2 3/3] [media] hva: add H.264 video encoding support
  2016-07-18 11:55   ` Hans Verkuil
@ 2016-07-21  7:30     ` Jean Christophe TROTIN
  0 siblings, 0 replies; 18+ messages in thread
From: Jean Christophe TROTIN @ 2016-07-21  7:30 UTC (permalink / raw)
  To: Hans Verkuil, linux-media
  Cc: kernel, Benjamin Gaignard, Yannick FERTRE, Hugues FRUCHET



On 07/18/2016 01:55 PM, Hans Verkuil wrote:
> On 07/11/2016 05:14 PM, Jean-Christophe Trotin wrote:
>> This patch adds the H.264 video encoding capability in the V4L2 HVA
>> video encoder driver for STMicroelectronics SoC (hva-h264.c).
>>
>> The main supported features are:
>> - profile: baseline, main, high, stereo high
>> - level: up to 4.2
>> - bitrate mode: CBR, VBR
>> - entropy mode: CABAC, CAVLC
>> - video aspect: 1x1 only
>>
>> Signed-off-by: Yannick Fertre <yannick.fertre@st.com>
>> Signed-off-by: Jean-Christophe Trotin <jean-christophe.trotin@st.com>
>> ---
>>   drivers/media/platform/sti/hva/Makefile   |    2 +-
>>   drivers/media/platform/sti/hva/hva-h264.c | 1053 +++++++++++++++++++++++++++++
>>   drivers/media/platform/sti/hva/hva-v4l2.c |  107 ++-
>>   drivers/media/platform/sti/hva/hva.h      |  115 +++-
>>   4 files changed, 1270 insertions(+), 7 deletions(-)
>>   create mode 100644 drivers/media/platform/sti/hva/hva-h264.c
>>
>
> <snip>
>
>> diff --git a/drivers/media/platform/sti/hva/hva.h b/drivers/media/platform/sti/hva/hva.h
>> index 9a1b503b..a81f313 100644
>> --- a/drivers/media/platform/sti/hva/hva.h
>> +++ b/drivers/media/platform/sti/hva/hva.h
>> @@ -23,6 +23,9 @@
>>
>>   #define HVA_PREFIX "[---:----]"
>>
>> +extern const struct hva_enc nv12h264enc;
>> +extern const struct hva_enc nv21h264enc;
>> +
>>   /**
>>    * struct hva_frameinfo - information about hva frame
>>    *
>> @@ -67,13 +70,35 @@ struct hva_streaminfo {
>>    * @gop_size:       groupe of picture size
>>    * @bitrate:        bitrate (in kbps)
>>    * @aspect:         video aspect
>> + * @profile:        H.264 profile
>> + * @level:          H.264 level
>> + * @entropy_mode:   H.264 entropy mode (CABAC or CVLC)
>> + * @cpb_size:       coded picture buffer size (in kbps)
>> + * @dct8x8:         transform mode 8x8 enable
>> + * @qpmin:          minimum quantizer
>> + * @qpmax:          maximum quantizer
>> + * @vui_sar:        pixel aspect ratio enable
>> + * @vui_sar_idc:    pixel aspect ratio identifier
>> + * @sei_fp:         sei frame packing arrangement enable
>> + * @sei_fp_type:    sei frame packing arrangement type
>>    */
>>   struct hva_controls {
>> -	struct v4l2_fract			time_per_frame;
>> -	enum v4l2_mpeg_video_bitrate_mode	bitrate_mode;
>> -	u32					gop_size;
>> -	u32					bitrate;
>> -	enum v4l2_mpeg_video_aspect		aspect;
>> +	struct v4l2_fract					time_per_frame;
>> +	enum v4l2_mpeg_video_bitrate_mode			bitrate_mode;
>> +	u32							gop_size;
>> +	u32							bitrate;
>> +	enum v4l2_mpeg_video_aspect				aspect;
>> +	enum v4l2_mpeg_video_h264_profile			profile;
>> +	enum v4l2_mpeg_video_h264_level				level;
>> +	enum v4l2_mpeg_video_h264_entropy_mode			entropy_mode;
>> +	u32							cpb_size;
>> +	bool							dct8x8;
>> +	u32							qpmin;
>> +	u32							qpmax;
>> +	bool							vui_sar;
>> +	enum v4l2_mpeg_video_h264_vui_sar_idc			vui_sar_idc;
>> +	bool							sei_fp;
>> +	enum v4l2_mpeg_video_h264_sei_fp_arrangement_type	sei_fp_type;
>>   };
>>
>>   /**
>> @@ -281,4 +306,84 @@ struct hva_enc {
>>   				  struct hva_stream *stream);
>>   };
>>
>> +static inline const char *profile_str(unsigned int p)
>> +{
>> +	switch (p) {
>> +	case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE:
>> +		return "baseline profile";
>> +	case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN:
>> +		return "main profile";
>> +	case V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED:
>> +		return "extended profile";
>> +	case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH:
>> +		return "high profile";
>> +	case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_10:
>> +		return "high 10 profile";
>> +	case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422:
>> +		return "high 422 profile";
>> +	case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_PREDICTIVE:
>> +		return "high 444 predictive profile";
>> +	case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_10_INTRA:
>> +		return "high 10 intra profile";
>> +	case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422_INTRA:
>> +		return "high 422 intra profile";
>> +	case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_INTRA:
>> +		return "high 444 intra profile";
>> +	case V4L2_MPEG_VIDEO_H264_PROFILE_CAVLC_444_INTRA:
>> +		return "calvc 444 intra profile";
>> +	case V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_BASELINE:
>> +		return "scalable baseline profile";
>> +	case V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_HIGH:
>> +		return "scalable high profile";
>> +	case V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_HIGH_INTRA:
>> +		return "scalable high intra profile";
>> +	case V4L2_MPEG_VIDEO_H264_PROFILE_STEREO_HIGH:
>> +		return "stereo high profile";
>> +	case V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH:
>> +		return "multiview high profile";
>> +	default:
>> +		return "unknown profile";
>> +	}
>> +}
>> +
>> +static inline const char *level_str(enum v4l2_mpeg_video_h264_level l)
>> +{
>> +	switch (l) {
>> +	case V4L2_MPEG_VIDEO_H264_LEVEL_1_0:
>> +		return "level 1.0";
>> +	case V4L2_MPEG_VIDEO_H264_LEVEL_1B:
>> +		return "level 1b";
>> +	case V4L2_MPEG_VIDEO_H264_LEVEL_1_1:
>> +		return "level 1.1";
>> +	case V4L2_MPEG_VIDEO_H264_LEVEL_1_2:
>> +		return "level 1.2";
>> +	case V4L2_MPEG_VIDEO_H264_LEVEL_1_3:
>> +		return "level 1.3";
>> +	case V4L2_MPEG_VIDEO_H264_LEVEL_2_0:
>> +		return "level 2.0";
>> +	case V4L2_MPEG_VIDEO_H264_LEVEL_2_1:
>> +		return "level 2.1";
>> +	case V4L2_MPEG_VIDEO_H264_LEVEL_2_2:
>> +		return "level 2.2";
>> +	case V4L2_MPEG_VIDEO_H264_LEVEL_3_0:
>> +		return "level 3.0";
>> +	case V4L2_MPEG_VIDEO_H264_LEVEL_3_1:
>> +		return "level 3.1";
>> +	case V4L2_MPEG_VIDEO_H264_LEVEL_3_2:
>> +		return "level 3.2";
>> +	case V4L2_MPEG_VIDEO_H264_LEVEL_4_0:
>> +		return "level 4.0";
>> +	case V4L2_MPEG_VIDEO_H264_LEVEL_4_1:
>> +		return "level 4.1";
>> +	case V4L2_MPEG_VIDEO_H264_LEVEL_4_2:
>> +		return "level 4.2";
>> +	case V4L2_MPEG_VIDEO_H264_LEVEL_5_0:
>> +		return "level 5.0";
>> +	case V4L2_MPEG_VIDEO_H264_LEVEL_5_1:
>> +		return "level 5.1";
>> +	default:
>> +		return "unknown level";
>> +	}
>> +}
>
> These two static inlines should be replaced. You can get the menu strings directly
> with v4l2_ctrl_get_menu(). No need to duplicate these strings here.
>
> Regards,
>
> 	Hans
>

Hi Hans,

Thank you for your comments.
In version 3, v4l2_ctrl_get_menu() will be used instead of the 2 static inlines.

Best regards,
Jean-Christophe.

>> +
>>   #endif /* HVA_H */
>>

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v2 2/3] [media] hva: multi-format video encoder V4L2 driver
  2016-07-21  7:30     ` Jean Christophe TROTIN
@ 2016-07-21  9:49       ` Hans Verkuil
  2016-07-25 14:09         ` Jean Christophe TROTIN
  0 siblings, 1 reply; 18+ messages in thread
From: Hans Verkuil @ 2016-07-21  9:49 UTC (permalink / raw)
  To: Jean Christophe TROTIN, linux-media
  Cc: kernel, Benjamin Gaignard, Yannick FERTRE, Hugues FRUCHET



On 07/21/2016 09:30 AM, Jean Christophe TROTIN wrote:
> 
> On 07/18/2016 01:45 PM, Hans Verkuil wrote:
>> Hi Jean-Christophe,
>>
>> See my review comments below. Nothing really major, but I do need to know more
>> about the g/s_parm and the restriction on the number of open()s has to be lifted.
>> That's not allowed.
>>
> 
> Hi Hans,
> 
> Thank you for your comments.
> I've explained below why I would like to keep 'hva' as driver's name and why the
> frame rate is needed (g/s_parm).
> I've followed your advice for managing the hardware restriction with regards to
> the number of codec instances (see also below).
> Finally, I've taken into account all the other comments.
> All these modifications will be reflected in the version 3.
> 
> Best regards,
> Jean-Christophe.
> 
>> On 07/11/2016 05:14 PM, Jean-Christophe Trotin wrote:
>>> This patch adds V4L2 HVA (Hardware Video Accelerator) video encoder
>>> driver for STMicroelectronics SoC. It uses the V4L2 mem2mem framework.
>>>
>>> This patch only contains the core parts of the driver:
>>> - the V4L2 interface with the userland (hva-v4l2.c)
>>> - the hardware services (hva-hw.c)
>>> - the memory management utilities (hva-mem.c)
>>>
>>> This patch doesn't include the support of specific codec (e.g. H.264)
>>> video encoding: this support is part of subsequent patches.
>>>
>>> Signed-off-by: Yannick Fertre <yannick.fertre@st.com>
>>> Signed-off-by: Jean-Christophe Trotin <jean-christophe.trotin@st.com>
>>> ---
>>>   drivers/media/platform/Kconfig            |   14 +
>>>   drivers/media/platform/Makefile           |    1 +
>>>   drivers/media/platform/sti/hva/Makefile   |    2 +
>>>   drivers/media/platform/sti/hva/hva-hw.c   |  534 ++++++++++++
>>>   drivers/media/platform/sti/hva/hva-hw.h   |   42 +
>>>   drivers/media/platform/sti/hva/hva-mem.c  |   60 ++
>>>   drivers/media/platform/sti/hva/hva-mem.h  |   36 +
>>>   drivers/media/platform/sti/hva/hva-v4l2.c | 1299 +++++++++++++++++++++++++++++
>>>   drivers/media/platform/sti/hva/hva.h      |  284 +++++++
>>>   9 files changed, 2272 insertions(+)
>>>   create mode 100644 drivers/media/platform/sti/hva/Makefile
>>>   create mode 100644 drivers/media/platform/sti/hva/hva-hw.c
>>>   create mode 100644 drivers/media/platform/sti/hva/hva-hw.h
>>>   create mode 100644 drivers/media/platform/sti/hva/hva-mem.c
>>>   create mode 100644 drivers/media/platform/sti/hva/hva-mem.h
>>>   create mode 100644 drivers/media/platform/sti/hva/hva-v4l2.c
>>>   create mode 100644 drivers/media/platform/sti/hva/hva.h
>>>
>>> diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
>>> index 382f393..182d63f 100644
>>> --- a/drivers/media/platform/Kconfig
>>> +++ b/drivers/media/platform/Kconfig
>>> @@ -227,6 +227,20 @@ config VIDEO_STI_BDISP
>>>        help
>>>          This v4l2 mem2mem driver is a 2D blitter for STMicroelectronics SoC.
>>>
>>> +config VIDEO_STI_HVA
>>> +     tristate "STMicroelectronics STiH41x HVA multi-format video encoder V4L2 driver"
>>> +     depends on VIDEO_DEV && VIDEO_V4L2
>>> +     depends on ARCH_STI || COMPILE_TEST
>>> +     select VIDEOBUF2_DMA_CONTIG
>>> +     select V4L2_MEM2MEM_DEV
>>> +     help
>>> +       This V4L2 driver enables HVA multi-format video encoder of
>>
>> Please mention here what HVA stands for.
>>
> 
> Done in version 3.
> HVA stands for "Hardware Video Accelerator".
> 
>>> +       STMicroelectronics SoC STiH41x series, allowing hardware encoding of raw
>>> +       uncompressed formats in various compressed video bitstreams format.
>>> +
>>> +       To compile this driver as a module, choose M here:
>>> +       the module will be called hva.
>>
>> How about sti-hva as the module name? 'hva' is a bit too generic.
>>
> 
> 'hva' is a generic IP which could be used on different STMicroelectronics SoCs.
> That's the reason why I would like to keep this name. It's not specific to  the
> STiH41x series: thus, I've reworked the Kconfig's comment.

How about st-hva? I really like it to be a bit more specific.

>>> +static int hva_s_parm(struct file *file, void *fh, struct v4l2_streamparm *sp)
>>> +{
>>> +     struct hva_ctx *ctx = fh_to_ctx(file->private_data);
>>> +     struct device *dev = ctx_to_dev(ctx);
>>> +     struct v4l2_fract *time_per_frame = &ctx->ctrls.time_per_frame;
>>> +
>>> +     time_per_frame->numerator = sp->parm.capture.timeperframe.numerator;
>>> +     time_per_frame->denominator =
>>> +             sp->parm.capture.timeperframe.denominator;
>>> +
>>> +     dev_dbg(dev, "%s set parameters %d/%d\n", ctx->name,
>>> +             time_per_frame->numerator, time_per_frame->denominator);
>>> +
>>> +     return 0;
>>> +}
>>> +
>>> +static int hva_g_parm(struct file *file, void *fh, struct v4l2_streamparm *sp)
>>> +{
>>> +     struct hva_ctx *ctx = fh_to_ctx(file->private_data);
>>> +     struct device *dev = ctx_to_dev(ctx);
>>> +     struct v4l2_fract *time_per_frame = &ctx->ctrls.time_per_frame;
>>> +
>>> +     sp->parm.capture.timeperframe.numerator = time_per_frame->numerator;
>>> +     sp->parm.capture.timeperframe.denominator =
>>> +             time_per_frame->denominator;
>>> +
>>> +     dev_dbg(dev, "%s get parameters %d/%d\n", ctx->name,
>>> +             time_per_frame->numerator, time_per_frame->denominator);
>>> +
>>> +     return 0;
>>> +}
>>
>> This is strange. Normally codecs don't need this. You give them a buffer and
>> it will be encoded/decoded and then you give it the next one if it is available.
>> There is normally no frame rate involved.
>>
>> How does this work in this SoC? I need to know a bit more about this to be
>> certain there isn't a misunderstanding somewhere.
>>
> 
> Among the parameters dimensioning its buffer model, the 'hva' HW IP needs to
> calculate the depletion that is the quantity of bits at the output of the
> encoder in each time slot, basically bitrate/framerate. That's the reason for
> these 2 functions.
> Furthermore, I've seen that mtk-vcodec and coda encoders also get the frame rate
> to configure their HW IPs (vidioc_venc_s_parm & coda_s_parm).

Ah, OK. That makes sense. So this is *only* used in calculating the bitrate(s),
not in actual scheduling of threads or something like that, right?

Regards,

	Hans

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v2 2/3] [media] hva: multi-format video encoder V4L2 driver
  2016-07-21  9:49       ` Hans Verkuil
@ 2016-07-25 14:09         ` Jean Christophe TROTIN
  0 siblings, 0 replies; 18+ messages in thread
From: Jean Christophe TROTIN @ 2016-07-25 14:09 UTC (permalink / raw)
  To: Hans Verkuil, linux-media
  Cc: kernel, Benjamin Gaignard, Yannick FERTRE, Hugues FRUCHET



On 07/21/2016 11:49 AM, Hans Verkuil wrote:
>
>
> On 07/21/2016 09:30 AM, Jean Christophe TROTIN wrote:
>>
>> On 07/18/2016 01:45 PM, Hans Verkuil wrote:
>>> Hi Jean-Christophe,
>>>
>>> See my review comments below. Nothing really major, but I do need to know more
>>> about the g/s_parm and the restriction on the number of open()s has to be lifted.
>>> That's not allowed.
>>>
>>
>> Hi Hans,
>>
>> Thank you for your comments.
>> I've explained below why I would like to keep 'hva' as driver's name and why the
>> frame rate is needed (g/s_parm).
>> I've followed your advice for managing the hardware restriction with regards to
>> the number of codec instances (see also below).
>> Finally, I've taken into account all the other comments.
>> All these modifications will be reflected in the version 3.
>>
>> Best regards,
>> Jean-Christophe.
>>
>>> On 07/11/2016 05:14 PM, Jean-Christophe Trotin wrote:
>>>> This patch adds V4L2 HVA (Hardware Video Accelerator) video encoder
>>>> driver for STMicroelectronics SoC. It uses the V4L2 mem2mem framework.
>>>>
>>>> This patch only contains the core parts of the driver:
>>>> - the V4L2 interface with the userland (hva-v4l2.c)
>>>> - the hardware services (hva-hw.c)
>>>> - the memory management utilities (hva-mem.c)
>>>>
>>>> This patch doesn't include the support of specific codec (e.g. H.264)
>>>> video encoding: this support is part of subsequent patches.
>>>>
>>>> Signed-off-by: Yannick Fertre <yannick.fertre@st.com>
>>>> Signed-off-by: Jean-Christophe Trotin <jean-christophe.trotin@st.com>
>>>> ---
>>>>   drivers/media/platform/Kconfig            |   14 +
>>>>   drivers/media/platform/Makefile           |    1 +
>>>>   drivers/media/platform/sti/hva/Makefile   |    2 +
>>>>   drivers/media/platform/sti/hva/hva-hw.c   |  534 ++++++++++++
>>>>   drivers/media/platform/sti/hva/hva-hw.h   |   42 +
>>>>   drivers/media/platform/sti/hva/hva-mem.c  |   60 ++
>>>>   drivers/media/platform/sti/hva/hva-mem.h  |   36 +
>>>>   drivers/media/platform/sti/hva/hva-v4l2.c | 1299 +++++++++++++++++++++++++++++
>>>>   drivers/media/platform/sti/hva/hva.h      |  284 +++++++
>>>>   9 files changed, 2272 insertions(+)
>>>>   create mode 100644 drivers/media/platform/sti/hva/Makefile
>>>>   create mode 100644 drivers/media/platform/sti/hva/hva-hw.c
>>>>   create mode 100644 drivers/media/platform/sti/hva/hva-hw.h
>>>>   create mode 100644 drivers/media/platform/sti/hva/hva-mem.c
>>>>   create mode 100644 drivers/media/platform/sti/hva/hva-mem.h
>>>>   create mode 100644 drivers/media/platform/sti/hva/hva-v4l2.c
>>>>   create mode 100644 drivers/media/platform/sti/hva/hva.h
>>>>
>>>> diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
>>>> index 382f393..182d63f 100644
>>>> --- a/drivers/media/platform/Kconfig
>>>> +++ b/drivers/media/platform/Kconfig
>>>> @@ -227,6 +227,20 @@ config VIDEO_STI_BDISP
>>>>        help
>>>>          This v4l2 mem2mem driver is a 2D blitter for STMicroelectronics SoC.
>>>>
>>>> +config VIDEO_STI_HVA
>>>> +     tristate "STMicroelectronics STiH41x HVA multi-format video encoder V4L2 driver"
>>>> +     depends on VIDEO_DEV && VIDEO_V4L2
>>>> +     depends on ARCH_STI || COMPILE_TEST
>>>> +     select VIDEOBUF2_DMA_CONTIG
>>>> +     select V4L2_MEM2MEM_DEV
>>>> +     help
>>>> +       This V4L2 driver enables HVA multi-format video encoder of
>>>
>>> Please mention here what HVA stands for.
>>>
>>
>> Done in version 3.
>> HVA stands for "Hardware Video Accelerator".
>>
>>>> +       STMicroelectronics SoC STiH41x series, allowing hardware encoding of raw
>>>> +       uncompressed formats in various compressed video bitstreams format.
>>>> +
>>>> +       To compile this driver as a module, choose M here:
>>>> +       the module will be called hva.
>>>
>>> How about sti-hva as the module name? 'hva' is a bit too generic.
>>>
>>
>> 'hva' is a generic IP which could be used on different STMicroelectronics SoCs.
>> That's the reason why I would like to keep this name. It's not specific to  the
>> STiH41x series: thus, I've reworked the Kconfig's comment.
>
> How about st-hva? I really like it to be a bit more specific.
>

Hi Hans,

Thank you for your comments.
As discussed through IRC with you and Benjamin Gaignard last week, I will rename 
the module "st-hva" in the version 4.

>>>> +static int hva_s_parm(struct file *file, void *fh, struct v4l2_streamparm *sp)
>>>> +{
>>>> +     struct hva_ctx *ctx = fh_to_ctx(file->private_data);
>>>> +     struct device *dev = ctx_to_dev(ctx);
>>>> +     struct v4l2_fract *time_per_frame = &ctx->ctrls.time_per_frame;
>>>> +
>>>> +     time_per_frame->numerator = sp->parm.capture.timeperframe.numerator;
>>>> +     time_per_frame->denominator =
>>>> +             sp->parm.capture.timeperframe.denominator;
>>>> +
>>>> +     dev_dbg(dev, "%s set parameters %d/%d\n", ctx->name,
>>>> +             time_per_frame->numerator, time_per_frame->denominator);
>>>> +
>>>> +     return 0;
>>>> +}
>>>> +
>>>> +static int hva_g_parm(struct file *file, void *fh, struct v4l2_streamparm *sp)
>>>> +{
>>>> +     struct hva_ctx *ctx = fh_to_ctx(file->private_data);
>>>> +     struct device *dev = ctx_to_dev(ctx);
>>>> +     struct v4l2_fract *time_per_frame = &ctx->ctrls.time_per_frame;
>>>> +
>>>> +     sp->parm.capture.timeperframe.numerator = time_per_frame->numerator;
>>>> +     sp->parm.capture.timeperframe.denominator =
>>>> +             time_per_frame->denominator;
>>>> +
>>>> +     dev_dbg(dev, "%s get parameters %d/%d\n", ctx->name,
>>>> +             time_per_frame->numerator, time_per_frame->denominator);
>>>> +
>>>> +     return 0;
>>>> +}
>>>
>>> This is strange. Normally codecs don't need this. You give them a buffer and
>>> it will be encoded/decoded and then you give it the next one if it is available.
>>> There is normally no frame rate involved.
>>>
>>> How does this work in this SoC? I need to know a bit more about this to be
>>> certain there isn't a misunderstanding somewhere.
>>>
>>
>> Among the parameters dimensioning its buffer model, the 'hva' HW IP needs to
>> calculate the depletion that is the quantity of bits at the output of the
>> encoder in each time slot, basically bitrate/framerate. That's the reason for
>> these 2 functions.
>> Furthermore, I've seen that mtk-vcodec and coda encoders also get the frame rate
>> to configure their HW IPs (vidioc_venc_s_parm & coda_s_parm).
>
> Ah, OK. That makes sense. So this is *only* used in calculating the bitrate(s),
> not in actual scheduling of threads or something like that, right?
>
> Regards,
>
> 	Hans
>

About the frame rate (G/S-PARM), I confirm that it's not used for any scheduling 
of threads or anything like that: it's only for bitrate(s) calculation.

Best regards,
Jean-Christophe.

^ permalink raw reply	[flat|nested] 18+ messages in thread

end of thread, other threads:[~2016-07-25 14:09 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-07-11 15:14 [PATCH v2 0/3] support of v4l2 encoder for STMicroelectronics SOC Jean-Christophe Trotin
2016-07-11 15:14 ` [PATCH v2 1/3] Documentation: DT: add bindings for STI HVA Jean-Christophe Trotin
2016-07-11 15:14 ` [PATCH v2 2/3] [media] hva: multi-format video encoder V4L2 driver Jean-Christophe Trotin
2016-07-11 18:00   ` Nicolas Dufresne
2016-07-13 14:11     ` Jean Christophe TROTIN
2016-07-18 11:45   ` Hans Verkuil
2016-07-19 15:55     ` Jean Christophe TROTIN
2016-07-19 16:45       ` Hans Verkuil
2016-07-21  7:30     ` Jean Christophe TROTIN
2016-07-21  9:49       ` Hans Verkuil
2016-07-25 14:09         ` Jean Christophe TROTIN
2016-07-11 15:14 ` [PATCH v2 3/3] [media] hva: add H.264 video encoding support Jean-Christophe Trotin
2016-07-18 11:55   ` Hans Verkuil
2016-07-21  7:30     ` Jean Christophe TROTIN
2016-07-11 17:48 ` [PATCH v2 0/3] support of v4l2 encoder for STMicroelectronics SOC Nicolas Dufresne
2016-07-11 18:57   ` Javier Martinez Canillas
2016-07-13 13:49     ` Jean Christophe TROTIN
2016-07-13 14:00       ` Javier Martinez Canillas

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.