* [PATCH 0/3] support of v4l2 encoder for STMicroelectronics SOC.
@ 2015-12-18 10:45 Yannick Fertre
2015-12-18 10:45 ` [PATCH 1/3] Documentation: devicetree: add STI HVA binding Yannick Fertre
` (2 more replies)
0 siblings, 3 replies; 6+ messages in thread
From: Yannick Fertre @ 2015-12-18 10:45 UTC (permalink / raw)
To: linux-media; +Cc: Benjamin Gaignard, hugues.fruchet, kernel
version 1:
- Initial submission.
Only one feature supported and tested:
- encode (RGB32, RGB24, RGB16, NV12, NV21, UYVY, VYUY) to h264 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 h264 codec.
Below is the v4l2-compliance report for the sti hva driver.
Note: using patched v4l2-compliance:
"v4l2-compliance: test SELECTION only for the supported buf_type"
root@st-next:/# ./v4l2-compliance
Driver Info:
Driver name : 8c85000.hva
Card type : 8c85000.hva
Bus info : platform:hva
Driver version: 4.4.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 (Not Supported)
test VIDIOC_QUERYCTRL: OK (Not Supported)
test VIDIOC_G/S_CTRL: OK
test VIDIOC_G/S/TRY_EXT_CTRLS: OK (Not Supported)
test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK (Not Supported)
test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
Standard Controls: 0 Private Controls: 0
Format ioctls:
test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
test VIDIOC_G/S_PARM: OK
test VIDIOC_G_FBUF: OK (Not Supported)
test VIDIOC_G_FMT: OK
warn: /local/frq07647/views/opensdk-1.0.4/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(707): TRY_FMT cannot handle an invalid pixelformat
.
warn: /local/frq07647/views/opensdk-1.0.4/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(708): This may or may not be a problem. For more i
nformation see:
warn: /local/frq07647/views/opensdk-1.0.4/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(709): http://www.mail-archive.com/linux-media@vger
.kernel.org/msg56550.html
warn: /local/frq07647/views/opensdk-1.0.4/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(707): TRY_FMT cannot handle an invalid pixelformat
.
warn: /local/frq07647/views/opensdk-1.0.4/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(708): This may or may not be a problem. For more i
nformation see:
warn: /local/frq07647/views/opensdk-1.0.4/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(709): http://www.mail-archive.com/linux-media@vger
.kernel.org/msg56550.html
test VIDIOC_TRY_FMT: OK
warn: /local/frq07647/views/opensdk-1.0.4/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(923): S_FMT cannot handle an invalid pixelformat.
warn: /local/frq07647/views/opensdk-1.0.4/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(924): This may or may not be a problem. For more i
nformation see:
warn: /local/frq07647/views/opensdk-1.0.4/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(925): http://www.mail-archive.com/linux-media@vger
.kernel.org/msg56550.html
warn: /local/frq07647/views/opensdk-1.0.4/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(923): S_FMT cannot handle an invalid pixelformat.
warn: /local/frq07647/views/opensdk-1.0.4/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(924): This may or may not be a problem. For more i
nformation see:
warn: /local/frq07647/views/opensdk-1.0.4/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(925): 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
test Composing: OK (Not Supported)
fail: /local/frq07647/views/opensdk-1.0.4/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(1435): doioctl(node, VIDIOC_S_FMT, &fmt)
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@st-next:/# ./v4l2-compliance -f
Driver Info:
Driver name : 8c85000.hva
Card type : 8c85000.hva
Bus info : platform:hva
Driver version: 4.4.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 (Not Supported)
test VIDIOC_QUERYCTRL: OK (Not Supported)
test VIDIOC_G/S_CTRL: OK
test VIDIOC_G/S/TRY_EXT_CTRLS: OK (Not Supported)
test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK (Not Supported)
test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
Standard Controls: 0 Private Controls: 0
Format ioctls:
test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
test VIDIOC_G/S_PARM: OK
test VIDIOC_G_FBUF: OK (Not Supported)
test VIDIOC_G_FMT: OK
warn: /local/frq07647/views/opensdk-1.0.4/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(707): TRY_FMT cannot handle an invalid pixelformat
.
warn: /local/frq07647/views/opensdk-1.0.4/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(708): This may or may not be a problem. For more i
nformation see:
warn: /local/frq07647/views/opensdk-1.0.4/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(709): http://www.mail-archive.com/linux-media@vger
.kernel.org/msg56550.html
warn: /local/frq07647/views/opensdk-1.0.4/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(707): TRY_FMT cannot handle an invalid pixelformat
.
warn: /local/frq07647/views/opensdk-1.0.4/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(708): This may or may not be a problem. For more i
nformation see:
warn: /local/frq07647/views/opensdk-1.0.4/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(709): http://www.mail-archive.com/linux-media@vger
.kernel.org/msg56550.html
test VIDIOC_TRY_FMT: OK
warn: /local/frq07647/views/opensdk-1.0.4/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(923): S_FMT cannot handle an invalid pixelformat.
warn: /local/frq07647/views/opensdk-1.0.4/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(924): This may or may not be a problem. For more i
nformation see:
warn: /local/frq07647/views/opensdk-1.0.4/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(925): http://www.mail-archive.com/linux-media@vger
.kernel.org/msg56550.html
warn: /local/frq07647/views/opensdk-1.0.4/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(923): S_FMT cannot handle an invalid pixelformat.
warn: /local/frq07647/views/opensdk-1.0.4/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(924): This may or may not be a problem. For more i
nformation see:
warn: /local/frq07647/views/opensdk-1.0.4/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(925): 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
test Composing: OK (Not Supported)
fail: /local/frq07647/views/opensdk-1.0.4/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(1435): doioctl(node, VIDIOC_S_FMT, &fmt)
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@st-next:/# ./v4l2-compliance -s
Driver Info:
Driver name : 8c85000.hva
Card type : 8c85000.hva
Bus info : platform:hva
Driver version: 4.4.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 (Not Supported)
test VIDIOC_QUERYCTRL: OK (Not Supported)
test VIDIOC_G/S_CTRL: OK
test VIDIOC_G/S/TRY_EXT_CTRLS: OK (Not Supported)
test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK (Not Supported)
test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
Standard Controls: 0 Private Controls: 0
Format ioctls:
test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
test VIDIOC_G/S_PARM: OK
test VIDIOC_G_FBUF: OK (Not Supported)
test VIDIOC_G_FMT: OK
warn: /local/frq07647/views/opensdk-1.0.4/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(707): TRY_FMT cannot handle an invalid pixelformat
.
warn: /local/frq07647/views/opensdk-1.0.4/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(708): This may or may not be a problem. For more i
nformation see:
warn: /local/frq07647/views/opensdk-1.0.4/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(709): http://www.mail-archive.com/linux-media@vger
.kernel.org/msg56550.html
warn: /local/frq07647/views/opensdk-1.0.4/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(707): TRY_FMT cannot handle an invalid pixelformat
.
warn: /local/frq07647/views/opensdk-1.0.4/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(708): This may or may not be a problem. For more i
nformation see:
warn: /local/frq07647/views/opensdk-1.0.4/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(709): http://www.mail-archive.com/linux-media@vger
.kernel.org/msg56550.html
test VIDIOC_TRY_FMT: OK
warn: /local/frq07647/views/opensdk-1.0.4/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(923): S_FMT cannot handle an invalid pixelformat.
warn: /local/frq07647/views/opensdk-1.0.4/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(924): This may or may not be a problem. For more i
nformation see:
warn: /local/frq07647/views/opensdk-1.0.4/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(925): http://www.mail-archive.com/linux-media@vger
.kernel.org/msg56550.html
warn: /local/frq07647/views/opensdk-1.0.4/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(923): S_FMT cannot handle an invalid pixelformat.
warn: /local/frq07647/views/opensdk-1.0.4/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(924): This may or may not be a problem. For more i
nformation see:
warn: /local/frq07647/views/opensdk-1.0.4/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(925): 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
test Composing: OK (Not Supported)
fail: /local/frq07647/views/opensdk-1.0.4/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-formats.cpp(1435): doioctl(node, VIDIOC_S_FMT, &fmt)
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)
fail: /local/frq07647/views/opensdk-1.0.4/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-buffers.cpp(948): ret != EINVAL
test MMAP: FAIL
test USERPTR: OK (Not Supported)
test DMABUF: Cannot test, specify --expbuf-device
Total: 45, Succeeded: 44, Failed: 1, Warnings: 12
Yannick Fertre (3):
Documentation: devicetree: add STI HVA binding
[media] hva: STiH41x multi-format video encoder V4L2 driver
[media] hva: add h264 support
.../devicetree/bindings/media/st,sti-hva.txt | 26 +
drivers/media/platform/Kconfig | 13 +
drivers/media/platform/Makefile | 1 +
drivers/media/platform/sti/hva/Makefile | 2 +
drivers/media/platform/sti/hva/hva-h264.c | 1225 ++++++++++++++++
drivers/media/platform/sti/hva/hva-hw.c | 561 +++++++
drivers/media/platform/sti/hva/hva-hw.h | 76 +
drivers/media/platform/sti/hva/hva-mem.c | 63 +
drivers/media/platform/sti/hva/hva-mem.h | 20 +
drivers/media/platform/sti/hva/hva-v4l2.c | 1530 ++++++++++++++++++++
drivers/media/platform/sti/hva/hva.h | 499 +++++++
11 files changed, 4016 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] 6+ messages in thread
* [PATCH 1/3] Documentation: devicetree: add STI HVA binding
2015-12-18 10:45 [PATCH 0/3] support of v4l2 encoder for STMicroelectronics SOC Yannick Fertre
@ 2015-12-18 10:45 ` Yannick Fertre
2015-12-18 10:45 ` [PATCH 2/3] [media] hva: STiH41x multi-format video encoder V4L2 driver Yannick Fertre
2015-12-18 10:45 ` [PATCH 3/3] [media] hva: add h264 support Yannick Fertre
2 siblings, 0 replies; 6+ messages in thread
From: Yannick Fertre @ 2015-12-18 10:45 UTC (permalink / raw)
To: linux-media; +Cc: Benjamin Gaignard, hugues.fruchet, kernel
This patch documents DT compatible string "st,st-hva".
Signed-off-by: Yannick Fertre <yannick.fertre@st.com>
---
.../devicetree/bindings/media/st,sti-hva.txt | 26 ++++++++++++++++++++++
1 file changed, 26 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..3dc431d
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/st,sti-hva.txt
@@ -0,0 +1,26 @@
+==============================================================================
+ hva: hardware video encoder accelerator
+==============================================================================
+
+Required properties:
+- compatible: should be "st,stih407-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,stih407-hva";
+ reg= <0x8c85000 0x400>, <0x6000000 0x40000>;
+ reg-names = "hva_registers", "hva_esram";
+ interrupts = <0 58 0>, <0 59 0>;
+ clock-names = "clk_hva";
+ clocks = <&CLK_S_C0_FLEXGEN 3>;
+};
--
1.9.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 2/3] [media] hva: STiH41x multi-format video encoder V4L2 driver
2015-12-18 10:45 [PATCH 0/3] support of v4l2 encoder for STMicroelectronics SOC Yannick Fertre
2015-12-18 10:45 ` [PATCH 1/3] Documentation: devicetree: add STI HVA binding Yannick Fertre
@ 2015-12-18 10:45 ` Yannick Fertre
2015-12-18 11:42 ` kbuild test robot
2016-01-11 10:10 ` Hans Verkuil
2015-12-18 10:45 ` [PATCH 3/3] [media] hva: add h264 support Yannick Fertre
2 siblings, 2 replies; 6+ messages in thread
From: Yannick Fertre @ 2015-12-18 10:45 UTC (permalink / raw)
To: linux-media; +Cc: Benjamin Gaignard, hugues.fruchet, kernel
This patch adds HVA (Hardware Video Accelerator) support for STI platform.
Signed-off-by: Yannick Fertre <yannick.fertre@st.com>
---
drivers/media/platform/Kconfig | 13 +
drivers/media/platform/Makefile | 1 +
drivers/media/platform/sti/hva/Makefile | 2 +
drivers/media/platform/sti/hva/hva-hw.c | 561 ++++++++++++
drivers/media/platform/sti/hva/hva-hw.h | 76 ++
drivers/media/platform/sti/hva/hva-mem.c | 63 ++
drivers/media/platform/sti/hva/hva-mem.h | 20 +
drivers/media/platform/sti/hva/hva-v4l2.c | 1404 +++++++++++++++++++++++++++++
drivers/media/platform/sti/hva/hva.h | 499 ++++++++++
9 files changed, 2639 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 5263594..50b5f83 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -221,6 +221,19 @@ 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
+ 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 & jpeg 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 efa0295..d7740d8 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -35,6 +35,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..00b915b
--- /dev/null
+++ b/drivers/media/platform/sti/hva/hva-hw.c
@@ -0,0 +1,561 @@
+/*
+ * 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/completion.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/time.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
+
+static irqreturn_t hva_hw_its_interrupt(int irq, void *data)
+{
+ struct hva_device *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_device *hva = arg;
+ struct device *dev = hva_to_dev(hva);
+ u32 status = hva->sts_reg & 0xFF;
+ u8 client_id = (hva->sts_reg & 0xFF00) >> 8;
+ 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);
+
+ /* check client ID */
+ if (client_id >= MAX_CONTEXT) {
+ dev_err(dev, "%s %s: bad client identifier: %d\n",
+ ctx->name, __func__, client_id);
+ ctx->encode_errors++;
+ ctx->hw_err = true;
+ goto out;
+ }
+
+ ctx = hva->contexts_list[client_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 JPEG_BITSTREAM_OVERSIZE:
+ dev_err(dev, "%s %s:jpeg bitstream oversize\n",
+ ctx->name, __func__);
+ ctx->encode_errors++;
+ ctx->hw_err = true;
+ break;
+ case H264_BITSTREAM_OVERSIZE:
+ dev_err(dev, "%s %s:h264 bitstream oversize\n",
+ ctx->name, __func__);
+ ctx->encode_errors++;
+ 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->encode_errors++;
+ 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->encode_errors++;
+ ctx->hw_err = true;
+ break;
+ case TASK_LIST_FULL:
+ dev_err(dev, "%s %s:task list full\n",
+ ctx->name, __func__);
+ ctx->encode_errors++;
+ ctx->hw_err = true;
+ break;
+ case UNKNOWN_COMMAND:
+ dev_err(dev, "%s %s:command not known\n",
+ ctx->name, __func__);
+ ctx->encode_errors++;
+ ctx->hw_err = true;
+ break;
+ case WRONG_CODEC_OR_RESOLUTION:
+ dev_err(dev, "%s %s:wrong codec or resolution\n",
+ ctx->name, __func__);
+ ctx->encode_errors++;
+ ctx->hw_err = true;
+ break;
+ default:
+ dev_err(dev, "%s %s:status not recognized\n",
+ ctx->name, __func__);
+ ctx->encode_errors++;
+ 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_device *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_device *hva = arg;
+ struct device *dev = hva_to_dev(hva);
+ u8 client_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);
+
+ /* check client ID */
+ client_id = (hva->sts_reg & 0xFF00) >> 8;
+ if (client_id >= MAX_CONTEXT) {
+ dev_err(dev, "%s bad client identifier: %d\n", HVA_PREFIX,
+ client_id);
+ goto out;
+ }
+
+ ctx = hva->contexts_list[client_id];
+
+ if (hva->lmi_err_reg) {
+ dev_err(dev, "%s local memory interface error :0x%08x\n",
+ ctx->name, hva->lmi_err_reg);
+ ctx->encode_errors++;
+ ctx->hw_err = true;
+ }
+
+ if (hva->lmi_err_reg) {
+ dev_err(dev, "%s external memory iterface error :0x%08x\n",
+ ctx->name, hva->emi_err_reg);
+ ctx->encode_errors++;
+ 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->encode_errors++;
+ ctx->hw_err = true;
+ }
+out:
+ complete(&hva->interrupt);
+
+ return IRQ_HANDLED;
+}
+
+static unsigned long int hva_hw_get_chipset_id(struct hva_device *hva)
+{
+ struct device *dev = hva_to_dev(hva);
+ unsigned long int id;
+
+ mutex_lock(&hva->protect_mutex);
+
+ if (pm_runtime_get_sync(dev) < 0) {
+ dev_err(dev, "%s get pm_runtime failed\n", HVA_PREFIX);
+ mutex_unlock(&hva->protect_mutex);
+ return -EFAULT;
+ }
+
+ id = readl_relaxed(hva->regs + HVA_HIF_REG_VERSION) &
+ VERSION_ID_MASK;
+
+ pm_runtime_put_autosuspend(dev);
+
+ mutex_unlock(&hva->protect_mutex);
+
+ switch (id) {
+ case HVA_VERSION_V400:
+ case HVA_VERSION_V397:
+ dev_info(dev, "%s chipset identifier 0x%lx\n",
+ HVA_PREFIX, id);
+ break;
+ default:
+ dev_err(dev, "%s unknown chipset identifier 0x%lx\n",
+ HVA_PREFIX, id);
+ id = HVA_VERSION_UNKNOWN;
+ break;
+ }
+
+ return id;
+}
+
+int hva_hw_probe(struct platform_device *pdev, struct hva_device *hva)
+{
+ struct device *dev = &pdev->dev;
+ struct resource *regs;
+ struct resource *esram;
+ int irq = 0;
+ int ret = 0;
+
+ WARN_ON(!hva);
+ hva->pdev = pdev;
+ hva->dev = dev;
+
+ /* get a memory region for mmio */
+ 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 a memory region for esram from device tree */
+ esram = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (IS_ERR_OR_NULL(esram)) {
+ dev_err(dev, "%s failed to get esram region\n", HVA_PREFIX);
+ return PTR_ERR(esram);
+ }
+
+ hva->esram_addr = esram->start;
+ hva->esram_size = esram->end - esram->start + 1;
+
+ /* 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;
+ }
+
+ /* retrieve irq number from board resources */
+ hva->irq_its = platform_get_irq(pdev, 0);
+ if (!hva->irq_its) {
+ dev_err(dev, "%s failed to get IRQ resource\n", HVA_PREFIX);
+ goto err_clk;
+ }
+
+ /* request irq */
+ ret = devm_request_threaded_irq(dev, hva->irq_its, hva_hw_its_interrupt,
+ hva_hw_its_irq_thread,
+ IRQF_ONESHOT,
+ "hva_its_irq", (void *)hva);
+ if (ret) {
+ dev_err(dev, "%s failed to register its IRQ 0x%x\n",
+ HVA_PREFIX, irq);
+ goto err_clk;
+ }
+ disable_irq(hva->irq_its);
+
+ /* retrieve irq number from board resources */
+ hva->irq_err = platform_get_irq(pdev, 1);
+ if (!hva->irq_err) {
+ dev_err(dev, "%s failed to get IRQ resource\n", HVA_PREFIX);
+ goto err_clk;
+ }
+
+ /* request irq */
+ ret = devm_request_threaded_irq(dev, hva->irq_err, hva_hw_err_interrupt,
+ hva_hw_err_irq_thread,
+ IRQF_ONESHOT,
+ "hva_err_irq", (void *)hva);
+ if (ret) {
+ dev_err(dev, "%s failed to register err IRQ 0x%x\n",
+ HVA_PREFIX, irq);
+ goto err_clk;
+ }
+ disable_irq(hva->irq_err);
+
+ /* initialisation of the protection mutex */
+ mutex_init(&hva->protect_mutex);
+
+ /* initialisation of completion signal */
+ init_completion(&hva->interrupt);
+
+ /* init pm_runtime used for 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_pm;
+ }
+
+ /* check hardware ID */
+ hva->chip_id = hva_hw_get_chipset_id(hva);
+
+ if (hva->chip_id == HVA_VERSION_UNKNOWN) {
+ ret = -EINVAL;
+ goto err_pm;
+ }
+
+ dev_info(dev, "%s found hva device (id=%lx)\n", HVA_PREFIX,
+ hva->chip_id);
+
+ 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_device *hva)
+{
+ struct device *dev = hva_to_dev(hva);
+
+ /* disable interrupt */
+ 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_device *hva = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(hva->clk);
+
+ return 0;
+}
+
+int hva_hw_runtime_resume(struct device *dev)
+{
+ struct hva_device *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_device *hva = ctx_to_hdev(ctx);
+ struct device *dev = hva_to_dev(hva);
+ unsigned long int version = 0;
+ u8 client_id = ctx->client_id;
+ 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 get pm_runtime failed\n", ctx->name);
+ ctx->sys_errors++;
+ goto out;
+ }
+
+ version = readl_relaxed(hva->regs + HVA_HIF_REG_VERSION) &
+ VERSION_ID_MASK;
+
+ reg = readl_relaxed(hva->regs + HVA_HIF_REG_CLK_GATING);
+ switch (cmd) {
+ case JPEG_ENC:
+ reg |= CLK_GATING_HJE;
+ break;
+ case H264_ENC:
+ case VP8_ENC:
+ reg |= CLK_GATING_HVC;
+ break;
+ default:
+ dev_warn(dev, "%s unknown command 0x%x\n", ctx->name, cmd);
+ 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 */
+ switch (version) {
+ case HVA_VERSION_V397:
+ case HVA_VERSION_V400:
+ writel_relaxed(BSM_CFG_VAL1,
+ hva->regs + HVA_HIF_REG_BSM);
+ break;
+ default:
+ dev_err(dev, "%s unknown chipset identifier 0x%lx\n",
+ ctx->name, version);
+ ctx->sys_errors++;
+ goto out;
+ }
+
+ /*
+ * define Max Opcode Size and Max Message Size
+ * for LMI and EMI
+ */
+ switch (version) {
+ case HVA_VERSION_V397:
+ case HVA_VERSION_V400:
+ 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);
+ break;
+ default:
+ /* do nothing */
+ break;
+ }
+
+ /* command FIFO: task_id[31:16] client_id[15:8] command_type[7:0] */
+ dev_dbg(dev, "%s %s: Send task ( cmd:%d, task_desc:0x%x)\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__);
+ ctx->encode_errors++;
+ ctx->hw_err = true;
+ }
+out:
+ disable_irq(hva->irq_its);
+ disable_irq(hva->irq_err);
+
+ switch (cmd) {
+ case JPEG_ENC:
+ reg &= ~CLK_GATING_HJE;
+ writel_relaxed(reg, hva->regs + HVA_HIF_REG_CLK_GATING);
+ break;
+ case H264_ENC:
+ case VP8_ENC:
+ reg &= ~CLK_GATING_HVC;
+ writel_relaxed(reg, hva->regs + HVA_HIF_REG_CLK_GATING);
+
+ break;
+ default:
+ dev_warn(dev, "%s unknown command 0x%x\n", ctx->name, cmd);
+ }
+
+ pm_runtime_put_autosuspend(dev);
+ mutex_unlock(&hva->protect_mutex);
+
+ if (ctx->hw_err)
+ return -EFAULT;
+
+ return 0;
+}
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..b27465a
--- /dev/null
+++ b/drivers/media/platform/sti/hva/hva-hw.h
@@ -0,0 +1,76 @@
+/*
+ * 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
+
+/* HVA Versions */
+#define HVA_VERSION_UNKNOWN 0x000
+#define HVA_VERSION_V397 0x397
+#define HVA_VERSION_V400 0x400
+
+enum hva_hw_cmd_type {
+ /* RESERVED = 0x00 */
+ /* RESERVED = 0x01 */
+ H264_ENC = 0x02,
+ JPEG_ENC = 0x03,
+ /* SW synchro task (reserved in HW) */
+ /* RESERVED = 0x04 */
+ /* RESERVED = 0x05 */
+ VP8_ENC = 0x06,
+ /* RESERVED = 0x07 */
+ REMOVE_CLIENT = 0x08,
+ FREEZE_CLIENT = 0x09,
+ START_CLIENT = 0x0A,
+ FREEZE_ALL = 0x0B,
+ START_ALL = 0x0C,
+ REMOVE_ALL = 0x0D
+};
+
+/**
+ * hw encode error values
+ * NO_ERROR: Success, Task OK
+ * JPEG_BITSTREAM_OVERSIZE: VECJPEG Picture size > Max bitstream size
+ * 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,
+ JPEG_BITSTREAM_OVERSIZE = 0x1,
+ 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,
+};
+
+int hva_hw_probe(struct platform_device *pdev, struct hva_device *hva);
+void hva_hw_remove(struct hva_device *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..01da742
--- /dev/null
+++ b/drivers/media/platform/sti/hva/hva-mem.c
@@ -0,0 +1,63 @@
+/*
+ * 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) {
+ ctx->sys_errors++;
+ 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);
+ ctx->sys_errors++;
+ 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=0x%x): %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=0x%x): %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..f181115
--- /dev/null
+++ b/drivers/media/platform/sti/hva/hva-mem.h
@@ -0,0 +1,20 @@
+/*
+ * 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
+
+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..051d2ea
--- /dev/null
+++ b/drivers/media/platform/sti/hva/hva-v4l2.c
@@ -0,0 +1,1404 @@
+/*
+ * 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/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#include <linux/of.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-dma-contig.h>
+#include "hva.h"
+#include "hva-hw.h"
+
+#define HVA_NAME "hva"
+
+/*
+ * 1 frame at least for user
+ * limit number of frames to 16
+ */
+#define MAX_FRAMES 16
+#define MIN_FRAMES 1
+
+#define HVA_MIN_WIDTH 32
+#define HVA_MAX_WIDTH 1920
+#define HVA_MIN_HEIGHT 32
+#define HVA_MAX_HEIGHT 1080
+
+#define DFT_CFG_WIDTH HVA_MIN_WIDTH
+#define DFT_CFG_HEIGHT HVA_MIN_HEIGHT
+#define DFT_CFG_BITRATE_MODE V4L2_MPEG_VIDEO_BITRATE_MODE_CBR
+#define DFT_CFG_GOP_SIZE 16
+#define DFT_CFG_INTRA_REFRESH true
+#define DFT_CFG_FRAME_NUM 1
+#define DFT_CFG_FRAME_DEN 30
+#define DFT_CFG_QPMIN 5
+#define DFT_CFG_QPMAX 51
+#define DFT_CFG_DCT8X8 false
+#define DFT_CFG_COMP_QUALITY 85
+#define DFT_CFG_SAR_ENABLE 1
+#define DFT_CFG_BITRATE (20000 * 1024)
+#define DFT_CFG_CPB_SIZE (25000 * 1024)
+
+static const struct hva_frameinfo frame_dflt_fmt = {
+ .fmt = {
+ .pixelformat = V4L2_PIX_FMT_NV12,
+ .nb_planes = 2,
+ .bpp = 12,
+ .bpp_plane0 = 8,
+ .w_align = 2,
+ .h_align = 2
+ },
+ .width = DFT_CFG_WIDTH,
+ .height = DFT_CFG_HEIGHT,
+ .crop = {0, 0, DFT_CFG_WIDTH, DFT_CFG_HEIGHT},
+ .frame_width = DFT_CFG_WIDTH,
+ .frame_height = DFT_CFG_HEIGHT
+};
+
+static const struct hva_streaminfo stream_dflt_fmt = {
+ .width = DFT_CFG_WIDTH,
+ .height = DFT_CFG_HEIGHT
+};
+
+/* list of stream formats supported by hva hardware */
+const u32 stream_fmt[] = {
+};
+
+/* list of pixel formats supported by hva hardware */
+static const struct hva_frame_fmt frame_fmts[] = {
+ /* NV12. YUV420SP - 1 plane for Y + 1 plane for (CbCr) */
+ {
+ .pixelformat = V4L2_PIX_FMT_NV12,
+ .nb_planes = 2,
+ .bpp = 12,
+ .bpp_plane0 = 8,
+ .w_align = 2,
+ .h_align = 2
+ },
+ /* NV21. YUV420SP - 1 plane for Y + 1 plane for (CbCr) */
+ {
+ .pixelformat = V4L2_PIX_FMT_NV21,
+ .nb_planes = 2,
+ .bpp = 12,
+ .bpp_plane0 = 8,
+ .w_align = 2,
+ .h_align = 2
+ },
+};
+
+/* offset to differentiate OUTPUT/CAPTURE @mmap */
+#define MMAP_FRAME_OFFSET (UL(0x100000000) / 2)
+
+/* registry of available encoders */
+const struct hva_encoder *hva_encoders[] = {
+};
+
+static const struct hva_frame_fmt *hva_find_frame_fmt(u32 pixelformat)
+{
+ const struct hva_frame_fmt *fmt;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(frame_fmts); i++) {
+ fmt = &frame_fmts[i];
+ if (fmt->pixelformat == pixelformat)
+ return fmt;
+ }
+
+ return NULL;
+}
+
+static void register_encoder(struct hva_device *hva,
+ const struct hva_encoder *enc)
+{
+ if (hva->nb_of_encoders >= HVA_MAX_ENCODERS) {
+ dev_warn(hva->dev,
+ "%s can' t register encoder (max nb (%d) is reached!)\n",
+ enc->name, HVA_MAX_ENCODERS);
+ return;
+ }
+
+ /* those encoder ops are mandatory */
+ WARN_ON(!enc->open);
+ WARN_ON(!enc->close);
+ WARN_ON(!enc->encode);
+
+ hva->encoders[hva->nb_of_encoders] = enc;
+ hva->nb_of_encoders++;
+ dev_info(hva->dev, "%s encoder registered\n", enc->name);
+}
+
+static void register_all(struct hva_device *hva)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(hva_encoders); i++)
+ register_encoder(hva, hva_encoders[i]);
+}
+
+static int hva_open_encoder(struct hva_ctx *ctx, u32 streamformat,
+ u32 pixelformat, struct hva_encoder **penc)
+{
+ struct hva_device *hva = ctx_to_hdev(ctx);
+ struct device *dev = ctx_to_dev(ctx);
+ struct hva_encoder *enc;
+ unsigned int i;
+ int ret;
+
+ /* find an encoder which can deal with these formats */
+ for (i = 0; i < hva->nb_of_encoders; i++) {
+ enc = (struct hva_encoder *)hva->encoders[i];
+ if ((enc->streamformat == streamformat) &&
+ (enc->pixelformat == pixelformat))
+ break; /* found */
+ }
+
+ if (i == hva->nb_of_encoders) {
+ 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 name instance */
+ 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(hva->dev, "%s enc->open failed (%d)\n",
+ ctx->name, ret);
+ return ret;
+ }
+
+ *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_device *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_frame(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ /* index don't have to exceed number of format supported */
+ if (f->index >= ARRAY_SIZE(frame_fmts))
+ return -EINVAL;
+
+ /* pixel format */
+ f->pixelformat = frame_fmts[f->index].pixelformat;
+
+ return 0;
+}
+
+static int hva_enum_fmt_stream(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ /* index don't have to exceed number of stream format supported */
+ if (f->index >= ARRAY_SIZE(stream_fmt))
+ return -EINVAL;
+
+ /* pixel format */
+ f->pixelformat = stream_fmt[f->index];
+
+ /* compressed */
+ f->flags = V4L2_FMT_FLAG_COMPRESSED;
+
+ 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 hva_device *hva = ctx_to_hdev(ctx);
+ struct device *dev = ctx_to_dev(ctx);
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ const struct hva_encoder *enc = NULL;
+ unsigned int i;
+
+ if ((f->fmt.pix.width == 0) || (f->fmt.pix.height == 0))
+ goto out;
+
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ f->fmt.pix.priv = 0;
+
+ for (i = 0; i < hva->nb_of_encoders; i++) {
+ enc = hva->encoders[i];
+ if (enc->streamformat == pix->pixelformat)
+ if ((f->fmt.pix.height * f->fmt.pix.width) <=
+ (enc->max_width * enc->max_height))
+ return 0;
+ }
+out:
+ dev_dbg(dev, "%s stream format or resolution %dx%d not supported\n",
+ ctx->name, f->fmt.pix.width, f->fmt.pix.height);
+ return -EINVAL;
+}
+
+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;
+ const struct hva_frame_fmt *format;
+ u32 in_w, in_h;
+
+ format = hva_find_frame_fmt(pix->pixelformat);
+ if (!format) {
+ dev_dbg(dev, "%s Unknown format 0x%x\n", ctx->name,
+ pix->pixelformat);
+ return -EINVAL;
+ }
+
+ /* adjust width & height */
+ in_w = pix->width;
+ in_h = pix->height;
+ v4l_bound_align_image(&pix->width,
+ HVA_MIN_WIDTH, HVA_MAX_WIDTH,
+ ffs(format->w_align) - 1,
+ &pix->height,
+ HVA_MIN_HEIGHT, HVA_MAX_HEIGHT,
+ ffs(format->h_align) - 1,
+ 0);
+
+ if ((pix->width != in_w) || (pix->height != in_h))
+ dev_dbg(dev, "%s size updated: %dx%d -> %dx%d\n", ctx->name,
+ in_w, in_h, pix->width, pix->height);
+
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ f->fmt.pix.priv = 0;
+
+ 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);
+ int ret;
+
+ dev_dbg(dev, "%s %s %dx%d fmt:%.4s size:%d\n",
+ ctx->name, __func__, 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_err(dev,
+ "%s %s %.4s format or %dx%d resolution not supported\n",
+ ctx->name, __func__, (char *)&f->fmt.pix.pixelformat,
+ f->fmt.pix.width, f->fmt.pix.height);
+ return ret;
+ }
+
+ /* update context */
+ ctx->streaminfo.width = f->fmt.pix.width;
+ ctx->streaminfo.height = f->fmt.pix.height;
+ ctx->streaminfo.streamformat = f->fmt.pix.pixelformat;
+ ctx->streaminfo.dpb = 1;
+ ctx->flags |= HVA_FLAG_STREAMINFO;
+
+ if ((!ctx->encoder) && (ctx->flags & HVA_FLAG_FRAMEINFO)) {
+ ret = hva_open_encoder(ctx,
+ ctx->streaminfo.streamformat,
+ ctx->frameinfo.fmt.pixelformat,
+ &ctx->encoder);
+ if (ret)
+ return ret;
+ }
+
+ 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;
+ const struct hva_frame_fmt *fmt;
+ int ret = 0;
+
+ dev_dbg(dev, "%s %s %dx%d fmt:%.4s size:%d\n",
+ ctx->name, __func__, 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_err(dev,
+ "%s %s %.4s format or %dx%d resolution not supported\n",
+ ctx->name, __func__, (char *)&f->fmt.pix.pixelformat,
+ f->fmt.pix.width, f->fmt.pix.height);
+ return ret;
+ }
+
+ fmt = hva_find_frame_fmt(pix->pixelformat);
+ if (!fmt) {
+ dev_dbg(dev, "%s %s unknown format 0x%x\n", ctx->name,
+ ctx->name, pix->pixelformat);
+ return -EINVAL;
+ }
+
+ memcpy(&ctx->frameinfo.fmt, fmt, sizeof(struct hva_frame_fmt));
+ ctx->frameinfo.frame_width = ALIGN(pix->width, 16);
+ ctx->frameinfo.frame_height = ALIGN(pix->height, 16);
+ ctx->frameinfo.width = pix->width;
+ ctx->frameinfo.height = pix->height;
+ ctx->frameinfo.crop.width = pix->width;
+ ctx->frameinfo.crop.height = pix->height;
+ ctx->frameinfo.crop.left = 0;
+ ctx->frameinfo.crop.top = 0;
+
+ ctx->flags |= HVA_FLAG_FRAMEINFO;
+
+ if ((!ctx->encoder) && (ctx->flags & HVA_FLAG_STREAMINFO))
+ ret = hva_open_encoder(ctx,
+ ctx->streaminfo.streamformat,
+ ctx->frameinfo.fmt.pixelformat,
+ &ctx->encoder);
+
+ return ret;
+}
+
+static int hva_s_ext_ctrls(struct file *file, void *fh,
+ struct v4l2_ext_controls *ctrls)
+{
+ struct hva_ctx *ctx = fh_to_ctx(file->private_data);
+ struct device *dev = ctx_to_dev(ctx);
+ unsigned int i;
+
+ dev_dbg(dev, "%s %s count controls %d\n", ctx->name, __func__,
+ ctrls->count);
+
+ for (i = 0; i < ctrls->count; i++) {
+ switch (ctrls->controls[i].id) {
+ case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
+ ctx->ctrls.gop_size = ctrls->controls[i].value;
+ dev_dbg(dev, "%s V4L2_CID_MPEG_VIDEO_GOP_SIZE %d\n",
+ ctx->name, ctrls->controls[i].value);
+ break;
+ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+ ctx->ctrls.bitrate_mode = ctrls->controls[i].value;
+ dev_dbg(dev, "%s V4L2_CID_MPEG_VIDEO_BITRATE_MODE %d\n",
+ ctx->name, ctrls->controls[i].value);
+ break;
+ case V4L2_CID_MPEG_VIDEO_BITRATE:
+ ctx->ctrls.bitrate = ctrls->controls[i].value;
+ dev_dbg(dev, "%s V4L2_CID_MPEG_VIDEO_BITRATE %d\n",
+ ctx->name, ctrls->controls[i].value);
+ break;
+ case V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB:
+ ctx->ctrls.intra_refresh = ctrls->controls[i].value;
+ dev_dbg(dev,
+ "%s V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB %d\n",
+ ctx->name, ctrls->controls[i].value);
+ break;
+ case V4L2_CID_MPEG_VIDEO_ASPECT:
+ /* only one video aspect ratio supported (1/1) */
+ switch (ctrls->controls[i].value) {
+ case V4L2_MPEG_VIDEO_ASPECT_1x1:
+ dev_dbg(dev,
+ "%s V4L2_CID_MPEG_VIDEO_ASPECT 1x1\n",
+ ctx->name);
+ break;
+ case V4L2_MPEG_VIDEO_ASPECT_4x3:
+ case V4L2_MPEG_VIDEO_ASPECT_16x9:
+ case V4L2_MPEG_VIDEO_ASPECT_221x100:
+ default:
+ dev_err(dev,
+ "%s V4L2_CID_MPEG_VIDEO_ASPECT: Unsupported aspect ratio %d\n",
+ ctx->name, ctrls->controls[i].value);
+ return -EINVAL;
+ }
+ break;
+ default:
+ dev_err(dev,
+ "%s VIDIOC_S_EXT_CTRLS(): Unsupported control id %d\n",
+ ctx->name, ctrls->controls[i].id);
+ return -EINVAL;
+ }
+ }
+
+ 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);
+
+ ctx->time_per_frame.numerator = sp->parm.capture.timeperframe.numerator;
+ ctx->time_per_frame.denominator =
+ sp->parm.capture.timeperframe.denominator;
+
+ dev_dbg(dev, "%s set parameters %d/%d\n",
+ ctx->name, ctx->time_per_frame.numerator,
+ ctx->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);
+
+ sp->parm.capture.timeperframe.numerator = ctx->time_per_frame.numerator;
+ sp->parm.capture.timeperframe.denominator =
+ ctx->time_per_frame.denominator;
+
+ dev_dbg(dev, "%s get parameters %d/%d\n",
+ ctx->name, ctx->time_per_frame.numerator,
+ ctx->time_per_frame.denominator);
+
+ 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);
+
+ /* get stream format */
+ f->fmt.pix.width = ctx->streaminfo.width;
+ f->fmt.pix.height = ctx->streaminfo.height;
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ /* 32 bytes alignment */
+ f->fmt.pix.bytesperline = ALIGN(f->fmt.pix.width, 32);
+ f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height;
+ f->fmt.pix.pixelformat = ctx->streaminfo.streamformat;
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+
+ dev_dbg(dev, "%s %s %dx%d fmt:%.4s size:%d\n",
+ ctx->name, __func__, 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_frame_fmt *fmt = &ctx->frameinfo.fmt;
+ int width = ctx->frameinfo.frame_width;
+ int height = ctx->frameinfo.frame_height;
+
+ /* get source format */
+ f->fmt.pix.pixelformat = fmt->pixelformat;
+ f->fmt.pix.width = ctx->frameinfo.width;
+ f->fmt.pix.height = ctx->frameinfo.height;
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+ f->fmt.pix.bytesperline = (width * fmt->bpp_plane0) / 8;
+ f->fmt.pix.sizeimage = (width * height * fmt->bpp) / 8;
+
+ dev_dbg(dev, "%s %s %dx%d fmt:%.4s size:%d\n",
+ ctx->name, __func__, f->fmt.pix.width, f->fmt.pix.height,
+ (u8 *)&f->fmt.pix.pixelformat, f->fmt.pix.sizeimage);
+
+ return 0;
+}
+
+static int hva_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *reqbufs)
+{
+ struct hva_ctx *ctx = fh_to_ctx(file->private_data);
+ struct device *dev = ctx_to_dev(ctx);
+ int ret = 0;
+
+ dev_dbg(dev, "%s %s %s\n", ctx->name, __func__,
+ to_type_str(reqbufs->type));
+
+ ret = vb2_reqbufs(get_queue(ctx, reqbufs->type), reqbufs);
+ if (ret) {
+ dev_err(dev, "%s vb2_reqbufs failed (%d)\n", ctx->name, ret);
+ return ret;
+ }
+
+ if (reqbufs->count == 0) {
+ /*
+ * buffers have been freed in vb2 __reqbufs()
+ * now cleanup "allocation context" ...
+ */
+ if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ vb2_dma_contig_cleanup_ctx(ctx->q_frame.alloc_ctx[0]);
+ ctx->q_frame.alloc_ctx[0] = NULL;
+ } else if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ vb2_dma_contig_cleanup_ctx(ctx->q_stream.alloc_ctx[0]);
+ ctx->q_stream.alloc_ctx[0] = NULL;
+ }
+ }
+
+ return 0;
+}
+
+static int hva_create_bufs(struct file *file, void *priv,
+ struct v4l2_create_buffers *create)
+{
+ struct hva_ctx *ctx = fh_to_ctx(file->private_data);
+ struct vb2_queue *q = get_queue(ctx, create->format.type);
+
+ return vb2_create_bufs(q, create);
+}
+
+static int hva_querybuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct hva_ctx *ctx = fh_to_ctx(file->private_data);
+ struct device *dev = ctx_to_dev(ctx);
+ int ret = 0;
+
+ dev_dbg(dev, "%s %s %s[%d]\n", ctx->name, __func__,
+ to_type_str(b->type), b->index);
+
+ /* vb2 call */
+ ret = vb2_querybuf(get_queue(ctx, b->type), b);
+ if (ret) {
+ dev_err(dev, "%s vb2_querybuf failed (%d)\n", ctx->name, ret);
+ return ret;
+ }
+
+ /* add an offset to differentiate OUTPUT/CAPTURE @mmap time */
+ if ((b->memory == V4L2_MEMORY_MMAP) &&
+ (b->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)) {
+ b->m.offset += MMAP_FRAME_OFFSET;
+ }
+
+ return 0;
+}
+
+static int hva_expbuf(struct file *file, void *fh, struct v4l2_exportbuffer *b)
+{
+ struct hva_ctx *ctx = fh_to_ctx(file->private_data);
+ struct device *dev = ctx_to_dev(ctx);
+ int ret = 0;
+
+ /* request validation */
+ if ((b->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) &&
+ (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)) {
+ dev_err(dev,
+ "%s V4L2 EXPBUF: only type output/cature are supported\n",
+ ctx->name);
+ return -EINVAL;
+ }
+
+ /* vb2 call */
+ ret = vb2_expbuf(get_queue(ctx, b->type), b);
+ if (ret) {
+ dev_err(dev, "%s vb2_expbuf failed (%d)\n", ctx->name, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int hva_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct hva_ctx *ctx = fh_to_ctx(file->private_data);
+ struct device *dev = ctx_to_dev(ctx);
+ struct vb2_queue *q = get_queue(ctx, b->type);
+ int ret = 0;
+
+ /* copy bytesused field from v4l2 buffer to vb2 buffer */
+ if ((b->index < MAX_FRAMES) &&
+ (b->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)) {
+ struct hva_stream *s = (struct hva_stream *)q->bufs[b->index];
+
+ s->payload = b->bytesused;
+ }
+
+ ret = vb2_qbuf(q, b);
+ if (ret) {
+ dev_err(dev, "%s vb2_qbuf failed (%d)\n", ctx->name, ret);
+ return ret;
+ }
+ return 0;
+}
+
+static int hva_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct hva_ctx *ctx = fh_to_ctx(file->private_data);
+ struct device *dev = ctx_to_dev(ctx);
+ struct vb2_queue *q = get_queue(ctx, b->type);
+ int ret = 0;
+
+ /* vb2 call */
+ ret = vb2_dqbuf(q, b, file->f_flags & O_NONBLOCK);
+ if (ret) {
+ dev_err(dev, "%s vb2_dqbuf failed (%d)\n", ctx->name, ret);
+ return ret;
+ }
+
+ dev_dbg(dev, "%s %s %s[%d]\n", ctx->name, __func__,
+ to_type_str(b->type), b->index);
+
+ return 0;
+}
+
+static int hva_streamon(struct file *file, void *priv, enum v4l2_buf_type type)
+{
+ struct hva_ctx *ctx = fh_to_ctx(file->private_data);
+ struct device *dev = ctx_to_dev(ctx);
+ int ret = 0;
+
+ /* reset frame number */
+ if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ ctx->frame_num = 0;
+
+ /* vb2 call */
+ ret = vb2_streamon(get_queue(ctx, type), type);
+ if (ret) {
+ dev_err(dev, "%s vb2_streamon failed (%d)\n", ctx->name, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int hva_streamoff(struct file *file, void *priv, enum v4l2_buf_type type)
+{
+ struct hva_ctx *ctx = fh_to_ctx(file->private_data);
+ struct device *dev = ctx_to_dev(ctx);
+ struct hva_stream *sr, *node;
+ int ret = 0;
+
+ /* release all active buffers */
+ if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ list_for_each_entry_safe(sr, node, &ctx->list_stream, list) {
+ list_del_init(&sr->list);
+ vb2_buffer_done(&sr->v4l2.vb2_buf, VB2_BUF_STATE_ERROR);
+ }
+ }
+
+ /* vb2 call */
+ ret = vb2_streamoff(get_queue(ctx, type), type);
+ if (ret) {
+ dev_err(dev, "%s vb2_streamoff failed (%d)\n", ctx->name, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int is_rect_enclosed(struct v4l2_rect *a, struct v4l2_rect *b)
+{
+ /* return 1 if a is enclosed in b, or 0 otherwise. */
+ if (a->left < b->left || a->top < b->top)
+ return 0;
+
+ if (a->left + a->width > b->left + b->width)
+ return 0;
+
+ if (a->top + a->height > b->top + b->height)
+ return 0;
+
+ return 1;
+}
+
+static int hva_g_selection(struct file *file, void *fh,
+ struct v4l2_selection *s)
+{
+ struct hva_ctx *ctx = fh_to_ctx(file->private_data);
+ struct device *dev = ctx_to_dev(ctx);
+
+ if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ dev_err(dev, "%s %s: G_SELECTION failed, invalid type (%d)\n",
+ ctx->name, __func__, s->type);
+ return -EINVAL;
+ }
+
+ switch (s->target) {
+ case V4L2_SEL_TGT_CROP:
+ /* cropped frame */
+ s->r = ctx->frameinfo.crop;
+ break;
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ /* complete frame */
+ s->r.left = 0;
+ s->r.top = 0;
+ s->r.width = ctx->frameinfo.width;
+ s->r.height = ctx->frameinfo.height;
+ break;
+ default:
+ dev_err(dev, "%s %s: G_SELECTION failed, invalid target (%d)\n",
+ ctx->name, __func__, s->target);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int hva_s_selection(struct file *file, void *fh,
+ struct v4l2_selection *s)
+{
+ struct hva_ctx *ctx = fh_to_ctx(file->private_data);
+ struct device *dev = ctx_to_dev(ctx);
+ struct v4l2_rect *in, out;
+
+ if ((s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) ||
+ (s->target != V4L2_SEL_TGT_CROP)) {
+ dev_err(dev, "%s %s: S_SELECTION failed, invalid type (%d)\n",
+ ctx->name, __func__, s->type);
+ return -EINVAL;
+ }
+
+ in = &s->r;
+ out = *in;
+
+ /* align and check origin */
+ out.left = ALIGN(in->left, ctx->frameinfo.fmt.w_align);
+ out.top = ALIGN(in->top, ctx->frameinfo.fmt.h_align);
+
+ if (((out.left + out.width) > ctx->frameinfo.width) ||
+ ((out.top + out.height) > ctx->frameinfo.height)) {
+ dev_err(dev,
+ "%s %s: S_SELECTION failed, invalid crop %dx%d@(%d,%d)\n",
+ ctx->name, __func__, out.width, out.height,
+ out.left, out.top);
+ return -EINVAL;
+ }
+
+ /* checks adjust constraints flags */
+ if (s->flags & V4L2_SEL_FLAG_LE && !is_rect_enclosed(&out, in))
+ return -ERANGE;
+
+ if (s->flags & V4L2_SEL_FLAG_GE && !is_rect_enclosed(in, &out))
+ return -ERANGE;
+
+ if ((out.left != in->left) || (out.top != in->top) ||
+ (out.width != in->width) || (out.height != in->height))
+ *in = out;
+
+ ctx->frameinfo.crop = s->r;
+
+ return 0;
+}
+
+/* vb2 ioctls operations */
+
+static int hva_vb2_frame_queue_setup(struct vb2_queue *q,
+ const void *parg,
+ unsigned int *num_buffers,
+ unsigned int *num_planes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ struct hva_ctx *ctx = fh_to_ctx(q->drv_priv);
+ struct device *dev = ctx_to_dev(ctx);
+ int width = ctx->frameinfo.frame_width;
+ int height = ctx->frameinfo.frame_height;
+
+ dev_dbg(dev, "%s %s *num_buffers=%d\n", ctx->name, __func__,
+ *num_buffers);
+
+ /* only one plane supported */
+ *num_planes = 1;
+
+ /* setup nb of input buffers needed =
+ * user need (*num_buffer given, usually for grab pipeline) +
+ * encoder internal need
+ */
+ if (*num_buffers < MIN_FRAMES) {
+ dev_warn(dev,
+ "%s num_buffers too low (%d), increasing to %d\n",
+ ctx->name, *num_buffers, MIN_FRAMES);
+ *num_buffers = MIN_FRAMES;
+ }
+
+ if (*num_buffers > MAX_FRAMES) {
+ dev_warn(dev,
+ "%s input frame count too high (%d), cut to %d\n",
+ ctx->name, *num_buffers, MAX_FRAMES);
+ *num_buffers = MAX_FRAMES;
+ }
+
+ if (sizes[0])
+ dev_warn(dev, "%s psize[0] already set to %d\n", ctx->name,
+ sizes[0]);
+
+ if (alloc_ctxs[0])
+ dev_warn(dev, "%s allocators[0] already set\n", ctx->name);
+
+ if (!(ctx->flags & HVA_FLAG_FRAMEINFO)) {
+ dev_err(dev, "%s %s frame format not set, using default format\n",
+ ctx->name, __func__);
+ }
+
+ sizes[0] = (width * height * ctx->frameinfo.fmt.bpp) / 8;
+ alloc_ctxs[0] = vb2_dma_contig_init_ctx(dev);
+ /* alloc_ctxs[0] will be freed @ reqbufs(0) or @ release */
+
+ return 0;
+}
+
+static int hva_vb2_frame_prepare(struct vb2_buffer *vb)
+{
+ struct vb2_queue *q = vb->vb2_queue;
+ struct hva_ctx *ctx = fh_to_ctx(q->drv_priv);
+ struct device *dev = ctx_to_dev(ctx);
+ struct hva_frame *fm = (struct hva_frame *)vb;
+
+ if (!fm->prepared) {
+ /* get memory addresses */
+ fm->vaddr = vb2_plane_vaddr(&fm->v4l2.vb2_buf, 0);
+ fm->paddr =
+ vb2_dma_contig_plane_dma_addr(&fm->v4l2.vb2_buf, 0);
+ fm->prepared = true;
+
+ ctx->num_frames++;
+
+ dev_dbg(dev, "%s frame[%d] prepared; virt=%p, phy=0x%x\n",
+ ctx->name, vb->index, fm->vaddr,
+ fm->paddr);
+ }
+
+ return 0;
+}
+
+static void hva_vb2_frame_queue(struct vb2_buffer *vb)
+{
+ struct vb2_queue *q = vb->vb2_queue;
+ struct hva_ctx *ctx = fh_to_ctx(q->drv_priv);
+ struct device *dev = ctx_to_dev(ctx);
+ const struct hva_encoder *enc = ctx_to_enc(ctx);
+ struct hva_frame *fm = NULL;
+ struct hva_stream *sr = NULL;
+ int ret = 0;
+
+ fm = (struct hva_frame *)vb;
+
+ if (!vb2_is_streaming(q)) {
+ vb2_buffer_done(&fm->v4l2.vb2_buf, VB2_BUF_STATE_ERROR);
+ return;
+ }
+
+ /* get a free destination buffer */
+ if (list_empty(&ctx->list_stream)) {
+ dev_err(dev, "%s no free buffer for destination stream!\n",
+ ctx->name);
+ ctx->sys_errors++;
+ goto err;
+ }
+ sr = list_first_entry(&ctx->list_stream, struct hva_stream, list);
+
+ if (!sr)
+ goto err;
+
+ list_del(&sr->list);
+
+ /* encode the frame & get stream unit */
+ ret = enc->encode(ctx, fm, sr);
+ if (ret)
+ goto err;
+
+ /* propagate frame timestamp */
+ sr->v4l2.timestamp = fm->v4l2.timestamp;
+
+ ctx->encoded_frames++;
+
+ vb2_buffer_done(&sr->v4l2.vb2_buf, VB2_BUF_STATE_DONE);
+ vb2_buffer_done(&fm->v4l2.vb2_buf, VB2_BUF_STATE_DONE);
+
+ return;
+err:
+ if (sr)
+ vb2_buffer_done(&sr->v4l2.vb2_buf, VB2_BUF_STATE_ERROR);
+
+ vb2_buffer_done(&fm->v4l2.vb2_buf, VB2_BUF_STATE_ERROR);
+}
+
+static int hva_vb2_stream_queue_setup(struct vb2_queue *q,
+ const void *parg,
+ unsigned int *num_buffers,
+ unsigned int *num_planes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ struct hva_ctx *ctx = fh_to_ctx(q->drv_priv);
+ struct device *dev = ctx_to_dev(ctx);
+ int max_buf_size = 0;
+ u32 pixelformat;
+ int width;
+ int height;
+
+ dev_dbg(dev, "%s %s *num_buffers=%d\n", ctx->name, __func__,
+ *num_buffers);
+
+ /* only one plane supported */
+ *num_planes = 1;
+
+ /* number of buffers must be at least 1 */
+ if (*num_buffers < 1)
+ *num_buffers = 1;
+
+ if (sizes[0])
+ dev_warn(dev, "%s psize[0] already set to %d\n", ctx->name,
+ sizes[0]);
+
+ if (alloc_ctxs[0])
+ dev_warn(dev, "%s allocators[0] already set\n", ctx->name);
+
+ if (!(ctx->flags & HVA_FLAG_STREAMINFO)) {
+ dev_err(dev, "%s %s stream format not set, using dflt format\n",
+ ctx->name, __func__);
+ }
+
+ pixelformat = ctx->streaminfo.streamformat;
+ width = ctx->streaminfo.width;
+ height = ctx->streaminfo.height;
+
+ switch (pixelformat) {
+ default:
+ dev_err(dev, "%s %s Unknown stream format\n", ctx->name,
+ __func__);
+ }
+
+ sizes[0] = max_buf_size;
+ alloc_ctxs[0] = vb2_dma_contig_init_ctx(dev); /* free @ release */
+
+ return 0;
+}
+
+static int hva_vb2_stream_prepare(struct vb2_buffer *vb)
+{
+ struct vb2_queue *q = vb->vb2_queue;
+ struct hva_ctx *ctx = fh_to_ctx(q->drv_priv);
+ struct device *dev = ctx_to_dev(ctx);
+ struct hva_stream *sr = (struct hva_stream *)vb;
+
+ if (!sr->prepared) {
+ /* get memory addresses */
+ sr->vaddr = vb2_plane_vaddr(&sr->v4l2.vb2_buf, 0);
+ sr->paddr = vb2_dma_contig_plane_dma_addr(&sr->v4l2.vb2_buf, 0);
+ sr->prepared = true;
+
+ dev_dbg(dev, "%s stream[%d] prepared; virt=%p, phy=0x%x\n",
+ ctx->name, vb->index, sr->vaddr, sr->paddr);
+ }
+
+ return 0;
+}
+
+static void hva_vb2_stream_queue(struct vb2_buffer *vb)
+{
+ struct vb2_queue *q = vb->vb2_queue;
+ struct hva_ctx *ctx = fh_to_ctx(q->drv_priv);
+ struct hva_stream *sr = (struct hva_stream *)vb;
+
+ /* check validity of video stream */
+ if (vb) {
+ /* enqueue to a list destination stream */
+ list_add(&sr->list, &ctx->list_stream);
+ }
+}
+
+static struct vb2_ops hva_vb2_frame_ops = {
+ .queue_setup = hva_vb2_frame_queue_setup,
+ .buf_prepare = hva_vb2_frame_prepare,
+ .buf_queue = hva_vb2_frame_queue,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+};
+
+static struct vb2_ops hva_vb2_stream_ops = {
+ .queue_setup = hva_vb2_stream_queue_setup,
+ .buf_prepare = hva_vb2_stream_prepare,
+ .buf_queue = hva_vb2_stream_queue,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+};
+
+/* file basics operations */
+
+static int hva_open(struct file *file)
+{
+ struct hva_device *hva = video_drvdata(file);
+ struct vb2_queue *q;
+ struct device *dev;
+ struct hva_ctx *ctx;
+ int ret = 0;
+ unsigned int i;
+
+ WARN_ON(!hva);
+ dev = hva->dev;
+
+ mutex_lock(&hva->lock);
+
+ ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx) {
+ mutex_unlock(&hva->lock);
+ return -ENOMEM;
+ }
+
+ /* store the context address in the contexts list */
+ for (i = 0; i < MAX_CONTEXT; i++) {
+ if (!hva->contexts_list[i]) {
+ hva->contexts_list[i] = ctx;
+ /* save client id in context */
+ ctx->client_id = i;
+ break;
+ }
+ }
+
+ v4l2_fh_init(&ctx->fh, video_devdata(file));
+ file->private_data = &ctx->fh;
+ v4l2_fh_add(&ctx->fh);
+
+ /* recopy device handlers */
+ ctx->dev = hva->dev;
+ ctx->hdev = hva;
+
+ /* setup vb2 queue for frame input */
+ q = &ctx->q_frame;
+ q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; /* to say input, weird! */
+ q->io_modes = VB2_MMAP | VB2_DMABUF;
+
+ /* save file handle to private data field of the queue */
+ q->drv_priv = &ctx->fh;
+
+ /* overload vb2 buffer size with private struct */
+ q->buf_struct_size = sizeof(struct hva_frame);
+
+ q->ops = &hva_vb2_frame_ops;
+ q->mem_ops = (struct vb2_mem_ops *)&vb2_dma_contig_memops;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ q->lock = &hva->lock;
+
+ ret = vb2_queue_init(q);
+ if (ret) {
+ dev_err(dev, "%s [x:x] vb2_queue_init(frame) failed (%d)\n",
+ HVA_PREFIX, ret);
+ ctx->sys_errors++;
+ goto err_fh_del;
+ }
+
+ /* setup vb2 queue of the destination */
+ q = &ctx->q_stream;
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ q->io_modes = VB2_MMAP | VB2_DMABUF;
+
+ /* save file handle to private data field of the queue */
+ q->drv_priv = &ctx->fh;
+
+ /* overload vb2 buffer size with private struct */
+ q->buf_struct_size = sizeof(struct hva_stream);
+
+ q->ops = &hva_vb2_stream_ops;
+ q->mem_ops = (struct vb2_mem_ops *)&vb2_dma_contig_memops;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ q->lock = &hva->lock;
+
+ ret = vb2_queue_init(q);
+ if (ret) {
+ dev_err(dev, "%s [x:x] vb2_queue_init(stream) failed (%d)\n",
+ HVA_PREFIX, ret);
+ ctx->sys_errors++;
+ goto err_queue_del_frame;
+ }
+
+ /* initialize the list of stream buffers */
+ INIT_LIST_HEAD(&ctx->list_stream);
+
+ /* name this instance */
+ hva->instance_id++; /* rolling id to identify this instance */
+ snprintf(ctx->name, sizeof(ctx->name), "[%3d:----]", hva->instance_id);
+
+ /* initialize controls */
+ ctx->ctrls.bitrate_mode = DFT_CFG_BITRATE_MODE;
+ ctx->ctrls.bitrate = DFT_CFG_BITRATE;
+ ctx->ctrls.cpb_size = DFT_CFG_CPB_SIZE;
+ ctx->ctrls.gop_size = DFT_CFG_GOP_SIZE;
+ ctx->ctrls.intra_refresh = DFT_CFG_INTRA_REFRESH;
+ ctx->ctrls.dct8x8 = DFT_CFG_DCT8X8;
+ ctx->ctrls.qpmin = DFT_CFG_QPMIN;
+ ctx->ctrls.qpmax = DFT_CFG_QPMAX;
+ ctx->ctrls.jpeg_comp_quality = DFT_CFG_COMP_QUALITY;
+ ctx->ctrls.vui_sar = DFT_CFG_SAR_ENABLE;
+
+ /* set by default time per frame */
+ ctx->time_per_frame.numerator = DFT_CFG_FRAME_NUM;
+ ctx->time_per_frame.denominator = DFT_CFG_FRAME_DEN;
+
+ /* default format */
+ ctx->streaminfo = stream_dflt_fmt;
+ ctx->frameinfo = frame_dflt_fmt;
+
+ hva->nb_of_instances++;
+
+ mutex_unlock(&hva->lock);
+
+ return 0;
+
+err_queue_del_frame:
+ vb2_queue_release(&ctx->q_frame);
+err_fh_del:
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
+ hva->contexts_list[ctx->client_id] = NULL;
+ devm_kfree(dev, ctx);
+
+ mutex_unlock(&hva->lock);
+
+ return ret;
+}
+
+static int hva_release(struct file *file)
+{
+ struct hva_device *hva = video_drvdata(file);
+ struct hva_ctx *ctx = fh_to_ctx(file->private_data);
+ struct device *dev = ctx_to_dev(ctx);
+ const struct hva_encoder *enc = ctx_to_enc(ctx);
+
+ mutex_lock(&hva->lock);
+
+ /* free queues: source & destination */
+ vb2_queue_release(&ctx->q_frame);
+ vb2_queue_release(&ctx->q_stream);
+
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
+
+ /* will free dma memory of each frame in queue */
+ vb2_queue_release(&ctx->q_frame);
+ if (ctx->q_frame.alloc_ctx[0])
+ vb2_dma_contig_cleanup_ctx(ctx->q_frame.alloc_ctx[0]);
+
+ /* will free dma memory of each aus in queue */
+ vb2_queue_release(&ctx->q_stream);
+ if (ctx->q_stream.alloc_ctx[0])
+ vb2_dma_contig_cleanup_ctx(ctx->q_stream.alloc_ctx[0]);
+
+ /* clear context in contexts list */
+ if ((ctx->client_id >= MAX_CONTEXT) ||
+ (hva->contexts_list[ctx->client_id] != ctx)) {
+ dev_err(dev, "%s can't clear context in contexts list!\n",
+ ctx->name);
+ ctx->sys_errors++;
+ }
+ hva->contexts_list[ctx->client_id] = NULL;
+
+ /* close encoder */
+ if (enc)
+ enc->close(ctx);
+
+ devm_kfree(dev, ctx);
+
+ hva->nb_of_instances--;
+
+ mutex_unlock(&hva->lock);
+
+ return 0;
+}
+
+static int hva_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct hva_device *hva = video_drvdata(file);
+ struct hva_ctx *ctx = fh_to_ctx(file->private_data);
+ struct device *dev = ctx->dev;
+ unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+ enum v4l2_buf_type type;
+ int ret;
+
+ mutex_lock(&hva->lock);
+
+ /* offset used to differentiate OUTPUT/CAPTURE */
+ if (offset < MMAP_FRAME_OFFSET) {
+ type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ } else {
+ type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ vma->vm_pgoff -= (MMAP_FRAME_OFFSET >> PAGE_SHIFT);
+ }
+
+ /* vb2 call */
+ ret = vb2_mmap(get_queue(ctx, type), vma);
+ if (ret) {
+ dev_err(dev, "%s vb2_mmap failed (%d)\n", ctx->name, ret);
+ ctx->sys_errors++;
+ mutex_unlock(&hva->lock);
+ return ret;
+ }
+
+ mutex_unlock(&hva->lock);
+
+ return 0;
+}
+
+/* v4l2 ops */
+static const struct v4l2_file_operations hva_fops = {
+ .owner = THIS_MODULE,
+ .open = hva_open,
+ .release = hva_release,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = hva_mmap,
+};
+
+/* v4l2 ioctl ops */
+static const struct v4l2_ioctl_ops hva_ioctl_ops = {
+ .vidioc_querycap = hva_querycap,
+ /* formats ioctl */
+ .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_s_ext_ctrls = hva_s_ext_ctrls,
+ .vidioc_g_parm = hva_g_parm,
+ .vidioc_s_parm = hva_s_parm,
+ /* buffers ioctls */
+ .vidioc_reqbufs = hva_reqbufs,
+ .vidioc_create_bufs = hva_create_bufs,
+ .vidioc_querybuf = hva_querybuf,
+ .vidioc_expbuf = hva_expbuf,
+ .vidioc_qbuf = hva_qbuf,
+ .vidioc_dqbuf = hva_dqbuf,
+ /* stream ioctls */
+ .vidioc_streamon = hva_streamon,
+ .vidioc_streamoff = hva_streamoff,
+ .vidioc_g_selection = hva_g_selection,
+ .vidioc_s_selection = hva_s_selection,
+};
+
+static int hva_probe(struct platform_device *pdev)
+{
+ struct hva_device *hva;
+ struct device *dev = &pdev->dev;
+ struct video_device *vdev;
+ 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_all(hva);
+
+ /* register on V4L2 */
+ ret = v4l2_device_register(dev, &hva->v4l2_dev);
+ if (ret) {
+ dev_err(dev, "%s %s could not register v4l2 device\n",
+ HVA_PREFIX, HVA_NAME);
+ goto err_hw_remove;
+ }
+
+ vdev = video_device_alloc();
+ vdev->fops = &hva_fops;
+ vdev->ioctl_ops = &hva_ioctl_ops;
+ vdev->release = video_device_release;
+ vdev->lock = &hva->lock;
+ vdev->v4l2_dev = &hva->v4l2_dev;
+ snprintf(vdev->name, sizeof(vdev->name), "%s", HVA_NAME);
+ vdev->vfl_dir = VFL_DIR_M2M;
+ ret = video_register_device(vdev, VFL_TYPE_GRABBER, 0);
+ 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);
+
+ dev_info(dev, "%s %s registered as /dev/video%d\n", HVA_PREFIX,
+ HVA_NAME, vdev->num);
+
+ dev_info(dev, "%s %s esram reserved for address: %p size:%d\n",
+ HVA_PREFIX, HVA_NAME, (void *)hva->esram_addr,
+ hva->esram_size);
+
+ return 0;
+
+err_vdev_release:
+ video_device_release(vdev);
+
+err_hw_remove:
+ hva_hw_remove(hva);
+
+err:
+ return ret;
+}
+
+static int hva_remove(struct platform_device *pdev)
+{
+ struct hva_device *hva = platform_get_drvdata(pdev);
+ struct device *dev = hva_to_dev(hva);
+
+ dev_info(dev, "%s removing %s\n", HVA_PREFIX, pdev->name);
+
+ hva_hw_remove(hva);
+
+ video_unregister_device(hva->vdev);
+ v4l2_device_unregister(&hva->v4l2_dev);
+
+ return 0;
+}
+
+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,stih407-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..e646718
--- /dev/null
+++ b/drivers/media/platform/sti/hva/hva.h
@@ -0,0 +1,499 @@
+/*
+ * 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-common.h>
+#include <media/v4l2-device.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-v4l2.h>
+
+#define get_queue(c, t) (t == V4L2_BUF_TYPE_VIDEO_OUTPUT ? \
+ &c->q_frame : &c->q_stream)
+
+#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))
+
+#define hva_to_dev(h) (h->dev)
+
+#define ctx_to_dev(c) (c->dev)
+
+#define ctx_to_hdev(c) (c->hdev)
+
+#define ctx_to_enc(c) (c->encoder)
+
+#define HVA_PREFIX "[---:----]"
+
+#define MAX_CONTEXT 16
+
+extern const struct hva_encoder nv12h264enc;
+extern const struct hva_encoder nv21h264enc;
+extern const struct hva_encoder uyvyh264enc;
+extern const struct hva_encoder vyuyh264enc;
+extern const struct hva_encoder xrgb32h264enc;
+extern const struct hva_encoder xbgr32h264enc;
+extern const struct hva_encoder rgbx32h264enc;
+extern const struct hva_encoder bgrx32h264enc;
+extern const struct hva_encoder rgb24h264enc;
+extern const struct hva_encoder bgr24h264enc;
+
+/**
+ * struct hva_frame_fmt - driver's internal color format data
+ * @pixelformat:fourcc code for this format
+ * @nb_planes: number of planes (ex: [0]=RGB/Y - [1]=Cb/Cr, ...)
+ * @bpp: bits per pixel (general)
+ * @bpp_plane0: byte per pixel for the 1st plane
+ * @w_align: width alignment in pixel (multiple of)
+ * @h_align: height alignment in pixel (multiple of)
+ */
+struct hva_frame_fmt {
+ u32 pixelformat;
+ u8 nb_planes;
+ u8 bpp;
+ u8 bpp_plane0;
+ u8 w_align;
+ u8 h_align;
+};
+
+/**
+ * struct hva_frameinfo - information of frame
+ *
+ * @flags flags of input frame
+ * @fmt: format of input frame
+ * @width: width of input frame
+ * @height: height of input frame
+ * @crop: cropping window due to encoder alignment constraints
+ * (1920x1080@0,0 inside 1920x1088 encoded frame for ex.)
+ * @pixelaspect: pixel aspect ratio of video (4/3, 5/4)
+ * @frame_width: width of output frame (encoder alignment constraint)
+ * @frame_height: height ""
+*/
+struct hva_frameinfo {
+ u32 flags;
+ struct hva_frame_fmt fmt;
+ u32 width;
+ u32 height;
+ struct v4l2_rect crop;
+ struct v4l2_fract pixelaspect;
+ u32 frame_width;
+ u32 frame_height;
+};
+
+/**
+ * struct hva_streaminfo - information of stream
+ *
+ * @flags flags of video stream
+ * @width: width of video stream
+ * @height: height ""
+ * @streamformat: fourcc compressed format of video (H264, JPEG, ...)
+ * @dpb: number of frames needed to encode a single frame
+ * (h264 dpb, up to 16 in standard)
+ * @profile: profile string
+ * @level: level string
+ * @other: other string information from codec
+ */
+struct hva_streaminfo {
+ u32 flags;
+ u32 streamformat;
+ u32 width;
+ u32 height;
+ u32 dpb;
+ u8 profile[32];
+ u8 level[32];
+ u8 other[32];
+};
+
+#define HVA_FRAMEINFO_FLAG_CROP 0x0001
+#define HVA_FRAMEINFO_FLAG_PIXELASPECT 0x0002
+
+#define HVA_STREAMINFO_FLAG_OTHER 0x0001
+#define HVA_STREAMINFO_FLAG_JPEG 0x0002
+#define HVA_STREAMINFO_FLAG_H264 0x0004
+#define HVA_STREAMINFO_FLAG_VP8 0x0008
+
+/**
+ * struct hva_controls
+ *
+ * @level: level enumerate
+ * @profile: video profile
+ * @entropy_mode: entropy mode (CABAC or CVLC)
+ * @bitrate_mode: bitrate mode (constant bitrate or variable bitrate)
+ * @gop_size: groupe of picture size
+ * @bitrate: bitrate
+ * @cpb_size: coded picture buffer size
+ * @intra_refresh: activate intra refresh
+ * @dct8x8: enable transform mode 8x8
+ * @qpmin: defines the minimum quantizer
+ * @qpmax: defines the maximum quantizer
+ * @jpeg_comp_quality: jpeg compression quality
+ * @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 {
+ enum v4l2_mpeg_video_h264_level level;
+ enum v4l2_mpeg_video_h264_profile profile;
+ enum v4l2_mpeg_video_h264_entropy_mode entropy_mode;
+ enum v4l2_mpeg_video_bitrate_mode bitrate_mode;
+ u32 gop_size;
+ u32 bitrate;
+ u32 cpb_size;
+ bool intra_refresh;
+ bool dct8x8;
+ u32 qpmin;
+ u32 qpmax;
+ u32 jpeg_comp_quality;
+ 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;
+};
+
+/**
+ * struct hva_frame - structure.
+ *
+ * @v4l2: video buffer information for v4l2.
+ * To be kept first and not to be wrote by driver.
+ * Allows to get the hva_frame fields by just casting a vb2_buffer
+ * with hva_frame struct. This is allowed through the use of
+ * vb2 custom buffer mechanism, cf @buf_struct_size of
+ * struct vb2_queue in include/media/videobuf2-core.h
+ * @paddr: physical address (for hardware)
+ * @vaddr: virtual address (kernel can read/write)
+ * @prepared: boolean, if set vaddr/paddr are resolved
+ */
+struct hva_frame {
+ struct vb2_v4l2_buffer v4l2; /* !keep first! */
+ dma_addr_t paddr;
+ void *vaddr;
+ int prepared;
+};
+
+/**
+ * struct hva_stream - structure.
+ *
+ * @v4l2: video buffer information for v4l2.
+ * To be kept first and not to be wrote by driver.
+ * Allows to get the hva_stream fields by just casting a vb2_buffer
+ * with hva_stream struct. This is allowed through the use of
+ * vb2 custom buffer mechanism, cf @buf_struct_size of
+ * struct vb2_queue in include/media/videobuf2-core.h
+ * @list: list element
+ * @paddr: physical address (for hardware)
+ * @vaddr: virtual address (kernel can read/write)
+ * @prepared: boolean, if set vaddr/paddr are resolved
+ * @payload: number of bytes occupied by data in the buffer
+ */
+struct hva_stream {
+ struct vb2_v4l2_buffer v4l2; /* !keep first! */
+ struct list_head list;
+ dma_addr_t paddr;
+ void *vaddr;
+ int prepared;
+ unsigned int payload;
+};
+
+/**
+ * struct hva_buffer - structure.
+ *
+ * @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;
+};
+
+struct hva_device;
+struct hva_encoder;
+
+#define HVA_MAX_ENCODERS 30
+
+#define HVA_FLAG_STREAMINFO 0x0001
+#define HVA_FLAG_FRAMEINFO 0x0002
+
+/**
+ * struct hva_ctx - context structure.
+ *
+ * @flags: validity of fields (streaminfo,frameinfo)
+ * @fh: keep track of V4L2 file handle
+ * @dev: keep track of device context
+ * @client_id: Client Identifier
+ * @q_frame: V4L2 vb2 queue for access units, allocated by driver
+ * but managed by vb2 framework.
+ * @q_stream: V4L2 vb2 queue for frames, allocated by driver
+ * but managed by vb2 framework.
+ * @name: string naming this instance (debug purpose)
+ * @list_stream: list of stream queued for destination only
+ * @frame_num frame number
+ * @frames: set of src frames (input, reconstructed & reference)
+ * @priv: private codec context for this instance, allocated
+ * by encoder @open time.
+ * @sys_errors: number of system errors ( memory, resource, pm, ..)
+ * @encode_errors: number of encoding errors ( hw/driver errors)
+ * @frames_errors: number of frames errors ( format, size, header ..)
+ * @hw_err: hardware error detected
+ */
+struct hva_ctx {
+ u32 flags;
+
+ struct v4l2_fh fh;
+ struct hva_device *hdev;
+ struct device *dev;
+
+ u8 client_id;
+
+ /* vb2 queues */
+ struct vb2_queue q_frame;
+ struct vb2_queue q_stream;
+
+ char name[100];
+
+ struct list_head list_stream;
+
+ u32 frame_num;
+
+ struct hva_controls ctrls;
+ struct v4l2_fract time_per_frame;
+ u32 num_frames;
+
+ /* stream */
+ struct hva_streaminfo streaminfo;
+
+ /* frame */
+ struct hva_frameinfo frameinfo;
+
+ /* current encoder */
+ struct hva_encoder *encoder;
+
+ /* stats */
+ u32 encoded_frames;
+
+ /* private data */
+ void *priv;
+
+ /* errors */
+ u32 sys_errors;
+ u32 encode_errors;
+ u32 frame_skipped;
+ u32 frame_errors;
+ bool hw_err;
+
+ /* hardware task descriptor*/
+ struct hva_buffer *task;
+};
+
+/**
+ * struct hva_device - device struct, 1 per probe (so single one for
+ * all platform life)
+ *
+ * @v4l2_dev: v4l2 device
+ * @vdev: v4l2 video device
+ * @pdev: platform device
+ * @dev: device
+ * @lock: device lock for critical section &
+ * V4L2 ops serialization
+ * @instance_id: instance identifier
+ * @contexts_list: contexts list
+ * @regs: register io memory access
+ * @reg_size: register size
+ * @irq_its: its interruption
+ * @irq_err: error interruption
+ * @chip_id: chipset identifier
+ * @protect_mutex: mutex use to lock access of hardware
+ * @interrupt: completion interrupt
+ * @clk: hva clock
+ * @esram_addr: esram address
+ * @esram_size: esram size
+ * @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
+ * @encoders: list of all encoders registered
+ * @nb_of_encoders: number of encoders registered
+ * @nb_of_instances: number of instance
+ */
+struct hva_device {
+ /* device */
+ struct v4l2_device v4l2_dev;
+ struct video_device *vdev;
+ struct platform_device *pdev;
+ struct device *dev;
+ struct mutex lock; /* device lock for critical section & V4L2 ops */
+ int instance_id;
+ struct hva_ctx *contexts_list[MAX_CONTEXT];
+
+ /* hardware */
+ void __iomem *regs;
+ int regs_size;
+ int irq_its;
+ int irq_err;
+ unsigned long int chip_id;
+ struct mutex protect_mutex; /* mutex use to lock access of hardware */
+ struct completion interrupt;
+ struct clk *clk;
+ u32 esram_addr;
+ u32 esram_size;
+
+ /* registers */
+ u32 sfl_reg;
+ u32 sts_reg;
+ u32 lmi_err_reg;
+ u32 emi_err_reg;
+ u32 hec_mif_err_reg;
+
+ /* encoders */
+ const struct hva_encoder *encoders[HVA_MAX_ENCODERS];
+ u32 nb_of_encoders;
+ u32 nb_of_instances;
+};
+
+struct hva_encoder {
+ struct list_head list;
+ const char *name;
+ u32 streamformat;
+ u32 pixelformat;
+ u32 max_width;
+ u32 max_height;
+
+ /**
+ * Encoder ops
+ */
+ int (*open)(struct hva_ctx *ctx);
+ int (*close)(struct hva_ctx *ctx);
+
+ /**
+ * encode() - encode a single access unit
+ * @ctx: (in) instance
+ * @frame: (in/out) access unit
+ * @frame.size (in) size of frame to encode
+ * @frame.vaddr (in) virtual address (kernel can read/write)
+ * @frame.paddr (in) physical address (for hardware)
+ * @frame.flags (out) frame type (V4L2_BUF_FLAG_KEYFRAME/
+ * PFRAME/BFRAME)
+ * @stream: (out) frame with encoded data:
+ * @stream.index (out) identifier of frame
+ * @stream.vaddr (out) virtual address (kernel can read/write)
+ * @stream.paddr (out) physical address (for hardware)
+ * @stream.pix (out) width/height/format/stride/...
+ * @stream.flags (out) stream type (V4L2_BUF_FLAG_KEYFRAME/
+ * PFRAME/BFRAME)
+ *
+ * Encode the access unit given. Encode is synchronous;
+ * access unit memory is no more needed after this call.
+ * After this call, none, one or several frames could
+ * have been encoded, which can be retrieved using
+ * get_stream().
+ */
+ int (*encode)(struct hva_ctx *ctx, struct hva_frame *frame,
+ 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";
+ }
+}
+
+static inline const char *bitrate_mode_str(enum v4l2_mpeg_video_bitrate_mode m)
+{
+ switch (m) {
+ case V4L2_MPEG_VIDEO_BITRATE_MODE_VBR:
+ return "variable bitrate";
+ case V4L2_MPEG_VIDEO_BITRATE_MODE_CBR:
+ return "constant bitrate";
+ default:
+ return "unknown bitrate mode";
+ }
+}
+
+#endif /* HVA_H */
--
1.9.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 3/3] [media] hva: add h264 support
2015-12-18 10:45 [PATCH 0/3] support of v4l2 encoder for STMicroelectronics SOC Yannick Fertre
2015-12-18 10:45 ` [PATCH 1/3] Documentation: devicetree: add STI HVA binding Yannick Fertre
2015-12-18 10:45 ` [PATCH 2/3] [media] hva: STiH41x multi-format video encoder V4L2 driver Yannick Fertre
@ 2015-12-18 10:45 ` Yannick Fertre
2 siblings, 0 replies; 6+ messages in thread
From: Yannick Fertre @ 2015-12-18 10:45 UTC (permalink / raw)
To: linux-media; +Cc: Benjamin Gaignard, hugues.fruchet, kernel
Signed-off-by: Yannick Fertre <yannick.fertre@st.com>
---
drivers/media/platform/sti/hva/Makefile | 2 +-
drivers/media/platform/sti/hva/hva-h264.c | 1225 +++++++++++++++++++++++++++++
drivers/media/platform/sti/hva/hva-v4l2.c | 126 +++
3 files changed, 1352 insertions(+), 1 deletion(-)
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..ae57f4b
--- /dev/null
+++ b/drivers/media/platform/sti/hva/hva-h264.c
@@ -0,0 +1,1225 @@
+/*
+ * 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/dma-mapping.h>
+#include "hva.h"
+#include "hva-hw.h"
+#include "hva-mem.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 1088
+
+/* 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
+
+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_v397 {
+ 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_v397 {
+ 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];
+};
+
+union hva_h264_td {
+ struct hva_h264_td_v397 v397;
+};
+
+union hva_h264_po {
+ struct hva_h264_po_v397 v397;
+};
+
+struct hva_h264_task {
+ union hva_h264_td td;
+ union hva_h264_po po;
+};
+
+struct hva_h264_ctx {
+ struct hva_buffer *seq_info_buf;
+ struct hva_buffer *ref_frame;
+ struct hva_buffer *rec_frame;
+};
+
+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.
+ * in order to avoid introducing heavy bit-parsing code
+ * (about 500 lines) - that we don't want on kernel side -,
+ * 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 *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);
+
+ /* 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 inline int hva_h264_prepare_task_v397(struct hva_ctx *pctx,
+ struct hva_buffer *task,
+ struct hva_h264_td_v397 *td,
+ struct hva_frame *frame,
+ struct hva_stream *stream)
+{
+ struct hva_device *hva = ctx_to_hdev(pctx);
+ struct hva_h264_ctx *ctx = (struct hva_h264_ctx *)pctx->priv;
+ struct device *dev = ctx_to_dev(pctx);
+ struct hva_buffer *seq_info_buf = ctx->seq_info_buf;
+ struct hva_buffer *fwd_ref_frame = ctx->ref_frame;
+ struct hva_buffer *loc_rec_frame = ctx->rec_frame;
+ struct hva_controls *ctrls = &pctx->ctrls;
+ struct v4l2_fract *time_per_frame = &pctx->time_per_frame;
+ int cavlc = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC;
+ u32 frame_num = pctx->frame_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 = pctx->frameinfo.frame_width;
+ u32 frame_height = pctx->frameinfo.frame_height;
+ u32 max_cpb_buffer_size;
+ unsigned int payload = stream->payload;
+ 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);
+ pctx->frame_errors++;
+ return -EINVAL;
+ }
+
+ level = ctrls->level;
+
+ memset(td, 0, sizeof(struct hva_h264_td_v397));
+
+ td->frame_width = frame_width;
+ td->frame_height = frame_height;
+
+ /* set frame alignement */
+ td->window_width = ALIGN(pctx->frameinfo.width, 16);
+ td->window_height = ALIGN(pctx->frameinfo.height, 16);
+ 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;
+
+ /* enable/disable intra refresh type */
+ td->intra_refresh_type = ctrls->intra_refresh ? 1 : 0;
+
+ 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_warn(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_warn(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 (pctx->frameinfo.fmt.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;
+ case V4L2_PIX_FMT_UYVY:
+ td->sampling_mode = SAMPLING_MODE_UYVY;
+ break;
+ case V4L2_PIX_FMT_VYUY:
+ td->sampling_mode = SAMPLING_MODE_VYUY;
+ break;
+ case V4L2_PIX_FMT_RGB24:
+ td->sampling_mode = SAMPLING_MODE_RGB3;
+ break;
+ case V4L2_PIX_FMT_BGR24:
+ td->sampling_mode = SAMPLING_MODE_BGR3;
+ break;
+ case V4L2_PIX_FMT_RGB32:
+ td->sampling_mode = SAMPLING_MODE_RGBX4;
+ break;
+ case V4L2_PIX_FMT_BGR32:
+ td->sampling_mode = SAMPLING_MODE_BGRX4;
+ break;
+ case V4L2_PIX_FMT_XRGB32:
+ td->sampling_mode = SAMPLING_MODE_XRGB4;
+ break;
+ case V4L2_PIX_FMT_XBGR32:
+ td->sampling_mode = SAMPLING_MODE_XBGR4;
+ break;
+ default:
+ dev_err(dev, "%s invalid source pixel format\n",
+ pctx->name);
+ pctx->frame_errors++;
+ 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);
+ pctx->frame_errors++;
+ 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 +
+ vb2_plane_size(&stream->v4l2.vb2_buf,
+ 0);
+
+ td->addr_output_bitstream_start = (u32)stream->paddr;
+ td->bitstream_offset = (((u32)stream->paddr & 0xF) << 3) &
+ BITSTREAM_OFFSET_MASK;
+
+ td->addr_param_out = (u32)task->paddr +
+ offsetof(struct hva_h264_task, po);
+
+ /* swap spatial and temporal context */
+ if (frame_num % 2) {
+ paddr = seq_info_buf->paddr;
+ td->addr_spatial_context = ALIGN(paddr, 0x100);
+ paddr = seq_info_buf->paddr + DATA_SIZE(frame_width,
+ frame_height);
+ td->addr_temporal_context = ALIGN(paddr, 0x100);
+ } else {
+ paddr = seq_info_buf->paddr;
+ td->addr_temporal_context = ALIGN(paddr, 0x100);
+ paddr = seq_info_buf->paddr + DATA_SIZE(frame_width,
+ frame_height);
+ td->addr_spatial_context = ALIGN(paddr, 0x100);
+ }
+
+ paddr = seq_info_buf->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->v4l2.flags |= V4L2_BUF_FLAG_KEYFRAME;
+ } else {
+ td->picture_coding_type = PICTURE_CODING_TYPE_P;
+ stream->v4l2.flags &= ~V4L2_BUF_FLAG_KEYFRAME;
+ }
+
+ /* fill the slice header part */
+ slice_header_vaddr = seq_info_buf->vaddr + (td->addr_slice_header -
+ seq_info_buf->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->v4l2.flags == V4L2_BUF_FLAG_KEYFRAME) &&
+ (payload > MAX_SPS_PPS_SIZE)) {
+ dev_err(dev, "%s invalid sps/pps size %d\n", pctx->name,
+ payload);
+ pctx->frame_errors++;
+ return -EINVAL;
+ }
+
+ if (stream->v4l2.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);
+ pctx->frame_errors++;
+ 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->payload = payload;
+
+ return 0;
+}
+
+static int hva_h264_prepare_task(struct hva_ctx *pctx,
+ struct hva_buffer *task,
+ struct hva_frame *frame,
+ struct hva_stream *stream)
+{
+ struct hva_device *hva = ctx_to_hdev(pctx);
+ struct hva_h264_task *video_task = task->vaddr;
+ union hva_h264_td *td = &video_task->td;
+ int ret = 0;
+
+ mutex_lock(&hva->protect_mutex);
+
+ /* task descriptors on V397 & V400 are similar */
+ if ((hva->chip_id == HVA_VERSION_V397) ||
+ (hva->chip_id == HVA_VERSION_V400)) {
+ ret = hva_h264_prepare_task_v397(pctx,
+ task,
+ &td->v397,
+ frame,
+ stream);
+ }
+
+ mutex_unlock(&hva->protect_mutex);
+ return ret;
+}
+
+static unsigned int hva_h264_get_au_size(struct hva_ctx *pctx,
+ struct hva_buffer *task)
+{
+ struct hva_device *hva = ctx_to_hdev(pctx);
+ struct device *dev = ctx_to_dev(pctx);
+ struct hva_h264_task *video_task = task->vaddr;
+ union hva_h264_po *po = &video_task->po;
+
+ /* task descriptors on V397 & V400 are similar */
+ if ((hva->chip_id == HVA_VERSION_V397) ||
+ (hva->chip_id == HVA_VERSION_V400))
+ return po->v397.bitstream_size;
+
+ dev_err(dev, "unknown chipset identifier 0x%lx\n", hva->chip_id);
+
+ return 0;
+}
+
+static u32 hva_h264_get_stuffing_bytes(struct hva_ctx *pctx,
+ struct hva_buffer *task)
+{
+ struct hva_device *hva = ctx_to_hdev(pctx);
+ struct device *dev = ctx_to_dev(pctx);
+ struct hva_h264_task *video_task = task->vaddr;
+ union hva_h264_po *po = &video_task->po;
+
+ /* task descriptors on V397 & V400 are similar */
+ if ((hva->chip_id == HVA_VERSION_V397) ||
+ (hva->chip_id == HVA_VERSION_V400))
+ return po->v397.stuffing_bits >> 3;
+
+ dev_err(dev, "unknown chipset identifier 0x%lx\n", hva->chip_id);
+
+ return 0;
+}
+
+/*
+ * codec ioctls operations
+ */
+
+static int hva_h264_open(struct hva_ctx *pctx)
+{
+ struct device *dev = ctx_to_dev(pctx);
+ struct hva_h264_ctx *ctx = NULL;
+ struct hva_device *hva = ctx_to_hdev(pctx);
+ u32 frame_width = pctx->frameinfo.frame_width;
+ u32 frame_height = pctx->frameinfo.frame_height;
+ u32 size;
+ int ret = 0;
+
+ /* 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);
+ pctx->sys_errors++;
+ return -EINVAL;
+ }
+
+ /* allocate context for codec */
+ ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ ret = -ENOMEM;
+
+ pctx->priv = (void *)ctx;
+
+ /* set info flags for debugfs */
+ pctx->streaminfo.flags |= HVA_STREAMINFO_FLAG_H264;
+
+ 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_buf)
+ hva_mem_free(pctx, ctx->seq_info_buf);
+
+ if (ctx->rec_frame)
+ hva_mem_free(pctx, ctx->rec_frame);
+
+ if (ctx->ref_frame)
+ hva_mem_free(pctx, ctx->ref_frame);
+
+ if (pctx->task)
+ hva_mem_free(pctx, pctx->task);
+
+ devm_kfree(dev, ctx);
+
+ return 0;
+}
+
+static int hva_h264_encode(struct hva_ctx *pctx, struct hva_frame *frame,
+ struct hva_stream *au)
+{
+ struct device *dev = ctx_to_dev(pctx);
+ struct hva_h264_ctx *ctx = (struct hva_h264_ctx *)pctx->priv;
+ struct hva_buffer *tmp_frame;
+ struct hva_buffer *task;
+ u32 width = pctx->frameinfo.frame_width;
+ u32 height = pctx->frameinfo.frame_height;
+ u32 stuffing_bytes = 0;
+ int ret = 0;
+
+ /* allocate sequence info buffer & local frames if needed */
+ if (!ctx->seq_info_buf) {
+ ret = hva_mem_alloc(pctx,
+ 2 * DATA_SIZE(width, height) +
+ SLICE_HEADER_SIZE +
+ BRC_DATA_SIZE,
+ "hva sequence info buffer",
+ &ctx->seq_info_buf);
+ if (ret || !ctx->seq_info_buf) {
+ dev_err(dev,
+ "%s no more memory for sequence info buffer\n",
+ pctx->name);
+ pctx->sys_errors++;
+ return -ENOMEM;
+ }
+ }
+
+ if (!ctx->ref_frame) {
+ ret = hva_mem_alloc(pctx,
+ width * height * 3 / 2,
+ "hva reference frame", &ctx->ref_frame);
+ if (ret || !ctx->ref_frame) {
+ dev_err(dev, "%s more memory for reference frame\n",
+ pctx->name);
+ pctx->sys_errors++;
+ return -ENOMEM;
+ }
+ }
+
+ if (!ctx->rec_frame) {
+ ret = hva_mem_alloc(pctx,
+ width * height * 3 / 2,
+ "hva reconstructed frame", &ctx->rec_frame);
+ if (ret || !ctx->rec_frame) {
+ dev_err(dev,
+ "%s no more memory for reconstructed frame\n",
+ pctx->name);
+ pctx->sys_errors++;
+ return -ENOMEM;
+ }
+ }
+
+ /* allocate task descriptor if not already done */
+ if (!pctx->task) {
+ ret = hva_mem_alloc(pctx,
+ sizeof(struct hva_h264_task),
+ "hva task descriptor", &pctx->task);
+ if (ret || !pctx->task) {
+ dev_err(dev,
+ "%s no more memory for task descriptor\n",
+ pctx->name);
+ return -ENOMEM;
+ }
+ }
+
+ /* only one task allocated & used */
+ task = pctx->task;
+
+ ret = hva_h264_prepare_task(pctx, task, frame, au);
+ if (ret)
+ goto err;
+
+ ret = hva_hw_execute_task(pctx, H264_ENC, task);
+ if (ret)
+ goto err;
+
+ pctx->frame_num++;
+ au->payload += hva_h264_get_au_size(pctx, task);
+
+ stuffing_bytes = hva_h264_get_stuffing_bytes(pctx, task);
+
+ if (stuffing_bytes)
+ hva_h264_fill_data_nal(pctx, stuffing_bytes,
+ (u8 *)au->vaddr,
+ &au->payload);
+
+ vb2_set_plane_payload(&au->v4l2.vb2_buf, 0, au->payload);
+
+ /* 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(&au->v4l2.vb2_buf, 0, 0);
+ return ret;
+}
+
+const struct hva_encoder 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_encoder 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,
+};
+
+const struct hva_encoder uyvyh264enc = {
+ .name = "H264(UYVY)",
+ .pixelformat = V4L2_PIX_FMT_UYVY,
+ .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_encoder vyuyh264enc = {
+ .name = "H264(VYUY)",
+ .pixelformat = V4L2_PIX_FMT_VYUY,
+ .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_encoder xrgb32h264enc = {
+ .name = "H264(XRGB32)",
+ .pixelformat = V4L2_PIX_FMT_XRGB32,
+ .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_encoder xbgr32h264enc = {
+ .name = "H264(XBGR32)",
+ .pixelformat = V4L2_PIX_FMT_XBGR32,
+ .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_encoder rgbx32h264enc = {
+ .name = "H264(RGBX32)",
+ .pixelformat = V4L2_PIX_FMT_RGB32,
+ .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_encoder bgrx32h264enc = {
+ .name = "H264(BGRX32)",
+ .pixelformat = V4L2_PIX_FMT_BGR32,
+ .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_encoder rgb24h264enc = {
+ .name = "H264(RGB3)",
+ .pixelformat = V4L2_PIX_FMT_RGB24,
+ .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_encoder bgr24h264enc = {
+ .name = "H264(BGR3)",
+ .pixelformat = V4L2_PIX_FMT_BGR24,
+ .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 051d2ea..ece4a2a 100644
--- a/drivers/media/platform/sti/hva/hva-v4l2.c
+++ b/drivers/media/platform/sti/hva/hva-v4l2.c
@@ -33,7 +33,10 @@
#define DFT_CFG_WIDTH HVA_MIN_WIDTH
#define DFT_CFG_HEIGHT HVA_MIN_HEIGHT
+#define DFT_CFG_LEVEL V4L2_MPEG_VIDEO_H264_LEVEL_4_0
+#define DFT_CFG_PROFILE V4L2_MPEG_VIDEO_H264_PROFILE_HIGH
#define DFT_CFG_BITRATE_MODE V4L2_MPEG_VIDEO_BITRATE_MODE_CBR
+#define DFT_CFG_ENTROPY_MODE V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC
#define DFT_CFG_GOP_SIZE 16
#define DFT_CFG_INTRA_REFRESH true
#define DFT_CFG_FRAME_NUM 1
@@ -43,6 +46,7 @@
#define DFT_CFG_DCT8X8 false
#define DFT_CFG_COMP_QUALITY 85
#define DFT_CFG_SAR_ENABLE 1
+#define DFT_CFG_SAR_IDC V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_1x1
#define DFT_CFG_BITRATE (20000 * 1024)
#define DFT_CFG_CPB_SIZE (25000 * 1024)
@@ -63,12 +67,14 @@ static const struct hva_frameinfo frame_dflt_fmt = {
};
static const struct hva_streaminfo stream_dflt_fmt = {
+ .streamformat = V4L2_PIX_FMT_H264,
.width = DFT_CFG_WIDTH,
.height = DFT_CFG_HEIGHT
};
/* list of stream formats supported by hva hardware */
const u32 stream_fmt[] = {
+ V4L2_PIX_FMT_H264,
};
/* list of pixel formats supported by hva hardware */
@@ -98,6 +104,16 @@ static const struct hva_frame_fmt frame_fmts[] = {
/* registry of available encoders */
const struct hva_encoder *hva_encoders[] = {
+ &nv12h264enc,
+ &nv21h264enc,
+ &uyvyh264enc,
+ &vyuyh264enc,
+ &rgb24h264enc,
+ &bgr24h264enc,
+ &xrgb32h264enc,
+ &xbgr32h264enc,
+ &rgbx32h264enc,
+ &bgrx32h264enc,
};
static const struct hva_frame_fmt *hva_find_frame_fmt(u32 pixelformat)
@@ -391,6 +407,7 @@ static int hva_s_ext_ctrls(struct file *file, void *fh,
{
struct hva_ctx *ctx = fh_to_ctx(file->private_data);
struct device *dev = ctx_to_dev(ctx);
+ int top_bot = V4L2_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE_TOP_BOTTOM;
unsigned int i;
dev_dbg(dev, "%s %s count controls %d\n", ctx->name, __func__,
@@ -398,6 +415,24 @@ static int hva_s_ext_ctrls(struct file *file, void *fh,
for (i = 0; i < ctrls->count; i++) {
switch (ctrls->controls[i].id) {
+ case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
+ ctx->ctrls.level = ctrls->controls[i].value;
+ dev_dbg(dev, "%s V4L2_CID_MPEG_VIDEO_H264_LEVEL %d\n",
+ ctx->name, ctrls->controls[i].value);
+ 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_PROFILE:
+ ctx->ctrls.profile = ctrls->controls[i].value;
+ dev_dbg(dev, "%s V4L2_CID_MPEG_VIDEO_H264_PROFILE %d\n",
+ ctx->name, ctrls->controls[i].value);
+ 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_GOP_SIZE:
ctx->ctrls.gop_size = ctrls->controls[i].value;
dev_dbg(dev, "%s V4L2_CID_MPEG_VIDEO_GOP_SIZE %d\n",
@@ -408,17 +443,51 @@ static int hva_s_ext_ctrls(struct file *file, void *fh,
dev_dbg(dev, "%s V4L2_CID_MPEG_VIDEO_BITRATE_MODE %d\n",
ctx->name, ctrls->controls[i].value);
break;
+ case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE:
+ ctx->ctrls.entropy_mode = ctrls->controls[i].value;
+ dev_dbg(dev,
+ "%s V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE %d\n",
+ ctx->name, ctrls->controls[i].value);
+ break;
case V4L2_CID_MPEG_VIDEO_BITRATE:
ctx->ctrls.bitrate = ctrls->controls[i].value;
dev_dbg(dev, "%s V4L2_CID_MPEG_VIDEO_BITRATE %d\n",
ctx->name, ctrls->controls[i].value);
break;
+ case V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE:
+ ctx->ctrls.cpb_size = ctrls->controls[i].value;
+ dev_dbg(dev,
+ "%s V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE %d\n",
+ ctx->name, ctrls->controls[i].value);
+ break;
case V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB:
ctx->ctrls.intra_refresh = ctrls->controls[i].value;
dev_dbg(dev,
"%s V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB %d\n",
ctx->name, ctrls->controls[i].value);
break;
+ case V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM:
+ ctx->ctrls.dct8x8 = ctrls->controls[i].value;
+ dev_dbg(dev,
+ "%s V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM %d\n",
+ ctx->name, ctrls->controls[i].value);
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_MIN_QP:
+ ctx->ctrls.qpmin = ctrls->controls[i].value;
+ dev_dbg(dev, "%s V4L2_CID_MPEG_VIDEO_H264_MIN_QP %d\n",
+ ctx->name, ctrls->controls[i].value);
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_MAX_QP:
+ ctx->ctrls.qpmax = ctrls->controls[i].value;
+ dev_dbg(dev, "%s V4L2_CID_MPEG_VIDEO_H264_MAX_QP %d\n",
+ ctx->name, ctrls->controls[i].value);
+ break;
+ case V4L2_CID_JPEG_COMPRESSION_QUALITY:
+ ctx->ctrls.jpeg_comp_quality = ctrls->controls[i].value;
+ dev_dbg(dev,
+ "%s V4L2_CID_JPEG_COMPRESSION_QUALITY %d\n",
+ ctx->name, ctrls->controls[i].value);
+ break;
case V4L2_CID_MPEG_VIDEO_ASPECT:
/* only one video aspect ratio supported (1/1) */
switch (ctrls->controls[i].value) {
@@ -437,6 +506,56 @@ static int hva_s_ext_ctrls(struct file *file, void *fh,
return -EINVAL;
}
break;
+ case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE:
+ ctx->ctrls.vui_sar = ctrls->controls[i].value;
+ dev_dbg(dev,
+ "%s V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE %d\n",
+ ctx->name, ctrls->controls[i].value);
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC:
+ switch (ctrls->controls[i].value) {
+ /* only one pixel aspect ratio supported (1/1) */
+ case V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_1x1:
+ ctx->ctrls.vui_sar_idc =
+ ctrls->controls[i].value;
+ dev_dbg(dev,
+ "%s V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC %d\n",
+ ctx->name, ctrls->controls[i].value);
+ break;
+ default:
+ dev_err(dev,
+ "%s V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC: Unsupported aspect ratio %d\n",
+ ctx->name, ctrls->controls[i].value);
+ return -EINVAL;
+ }
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_WIDTH:
+ case V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_HEIGHT:
+ dev_err(dev,
+ "%s VIDIOC_S_EXT_CTRLS(): Unsupported control id %d for Extended Aspect Ratio\n",
+ ctx->name, ctrls->controls[i].id);
+ return -EINVAL;
+ case V4L2_CID_MPEG_VIDEO_H264_SEI_FRAME_PACKING:
+ ctx->ctrls.sei_fp = ctrls->controls[i].value;
+ dev_dbg(dev,
+ "%s V4L2_CID_MPEG_VIDEO_H264_SEI_FRAME_PACKING %d\n",
+ ctx->name, ctrls->controls[i].value);
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE:
+ /* only top/bottom frame packing supported */
+ if (ctrls->controls[i].value == top_bot) {
+ ctx->ctrls.sei_fp_type =
+ ctrls->controls[i].value;
+ dev_dbg(dev,
+ "%s V4L2_CID_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE %d\n",
+ ctx->name, ctrls->controls[i].value);
+ } else {
+ dev_err(dev,
+ "%s V4L2_CID_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE: Unsupported frame packing arrangement %d\n",
+ ctx->name, ctrls->controls[i].value);
+ return -EINVAL;
+ }
+ break;
default:
dev_err(dev,
"%s VIDIOC_S_EXT_CTRLS(): Unsupported control id %d\n",
@@ -972,6 +1091,9 @@ static int hva_vb2_stream_queue_setup(struct vb2_queue *q,
height = ctx->streaminfo.height;
switch (pixelformat) {
+ case V4L2_PIX_FMT_H264:
+ max_buf_size = width * height;
+ break;
default:
dev_err(dev, "%s %s Unknown stream format\n", ctx->name,
__func__);
@@ -1128,16 +1250,20 @@ static int hva_open(struct file *file)
snprintf(ctx->name, sizeof(ctx->name), "[%3d:----]", hva->instance_id);
/* initialize controls */
+ ctx->ctrls.level = DFT_CFG_LEVEL;
+ ctx->ctrls.profile = DFT_CFG_PROFILE;
ctx->ctrls.bitrate_mode = DFT_CFG_BITRATE_MODE;
ctx->ctrls.bitrate = DFT_CFG_BITRATE;
ctx->ctrls.cpb_size = DFT_CFG_CPB_SIZE;
ctx->ctrls.gop_size = DFT_CFG_GOP_SIZE;
ctx->ctrls.intra_refresh = DFT_CFG_INTRA_REFRESH;
+ ctx->ctrls.entropy_mode = DFT_CFG_ENTROPY_MODE;
ctx->ctrls.dct8x8 = DFT_CFG_DCT8X8;
ctx->ctrls.qpmin = DFT_CFG_QPMIN;
ctx->ctrls.qpmax = DFT_CFG_QPMAX;
ctx->ctrls.jpeg_comp_quality = DFT_CFG_COMP_QUALITY;
ctx->ctrls.vui_sar = DFT_CFG_SAR_ENABLE;
+ ctx->ctrls.vui_sar_idc = DFT_CFG_SAR_IDC;
/* set by default time per frame */
ctx->time_per_frame.numerator = DFT_CFG_FRAME_NUM;
--
1.9.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH 2/3] [media] hva: STiH41x multi-format video encoder V4L2 driver
2015-12-18 10:45 ` [PATCH 2/3] [media] hva: STiH41x multi-format video encoder V4L2 driver Yannick Fertre
@ 2015-12-18 11:42 ` kbuild test robot
2016-01-11 10:10 ` Hans Verkuil
1 sibling, 0 replies; 6+ messages in thread
From: kbuild test robot @ 2015-12-18 11:42 UTC (permalink / raw)
To: Yannick Fertre
Cc: kbuild-all, linux-media, Benjamin Gaignard, hugues.fruchet, kernel
[-- Attachment #1: Type: text/plain, Size: 12223 bytes --]
Hi Yannick,
[auto build test ERROR on linuxtv-media/master]
[also build test ERROR on v4.4-rc5 next-20151217]
url: https://github.com/0day-ci/linux/commits/Yannick-Fertre/Documentation-devicetree-add-STI-HVA-binding/20151218-184834
base: git://linuxtv.org/media_tree.git master
config: x86_64-allmodconfig (attached as .config)
reproduce:
# save the attached .config to linux build tree
make ARCH=x86_64
All error/warnings (new ones prefixed by >>):
drivers/media/platform/sti/hva/hva-v4l2.c: In function 'hva_open_encoder':
>> drivers/media/platform/sti/hva/hva-v4l2.c:164:15: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
ctx->name, (char *)pixelformat, (char *)streamformat);
^
drivers/media/platform/sti/hva/hva-v4l2.c:164:36: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
ctx->name, (char *)pixelformat, (char *)streamformat);
^
drivers/media/platform/sti/hva/hva-v4l2.c: In function 'hva_querybuf':
>> drivers/media/platform/sti/hva/hva-v4l2.c:97:28: error: implicit declaration of function 'UL' [-Werror=implicit-function-declaration]
#define MMAP_FRAME_OFFSET (UL(0x100000000) / 2)
^
>> drivers/media/platform/sti/hva/hva-v4l2.c:589:18: note: in expansion of macro 'MMAP_FRAME_OFFSET'
b->m.offset += MMAP_FRAME_OFFSET;
^
In file included from include/linux/printk.h:277:0,
from include/linux/kernel.h:13,
from include/linux/list.h:8,
from include/linux/kobject.h:20,
from include/linux/device.h:17,
from drivers/media/platform/sti/hva/hva-v4l2.c:8:
drivers/media/platform/sti/hva/hva-v4l2.c: In function 'hva_vb2_frame_prepare':
>> drivers/media/platform/sti/hva/hva-v4l2.c:875:16: warning: format '%x' expects argument of type 'unsigned int', but argument 7 has type 'dma_addr_t {aka long long unsigned int}' [-Wformat=]
dev_dbg(dev, "%s frame[%d] prepared; virt=%p, phy=0x%x\n",
^
include/linux/dynamic_debug.h:86:39: note: in definition of macro 'dynamic_dev_dbg'
__dynamic_dev_dbg(&descriptor, dev, fmt, \
^
>> drivers/media/platform/sti/hva/hva-v4l2.c:875:3: note: in expansion of macro 'dev_dbg'
dev_dbg(dev, "%s frame[%d] prepared; virt=%p, phy=0x%x\n",
^
drivers/media/platform/sti/hva/hva-v4l2.c: In function 'hva_vb2_stream_prepare':
drivers/media/platform/sti/hva/hva-v4l2.c:999:16: warning: format '%x' expects argument of type 'unsigned int', but argument 7 has type 'dma_addr_t {aka long long unsigned int}' [-Wformat=]
dev_dbg(dev, "%s stream[%d] prepared; virt=%p, phy=0x%x\n",
^
include/linux/dynamic_debug.h:86:39: note: in definition of macro 'dynamic_dev_dbg'
__dynamic_dev_dbg(&descriptor, dev, fmt, \
^
drivers/media/platform/sti/hva/hva-v4l2.c:999:3: note: in expansion of macro 'dev_dbg'
dev_dbg(dev, "%s stream[%d] prepared; virt=%p, phy=0x%x\n",
^
In file included from drivers/media/platform/sti/hva/hva-v4l2.c:8:0:
drivers/media/platform/sti/hva/hva-v4l2.c: In function 'hva_probe':
drivers/media/platform/sti/hva/hva-v4l2.c:1345:26: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
HVA_PREFIX, HVA_NAME, (void *)hva->esram_addr,
^
include/linux/device.h:1174:58: note: in definition of macro 'dev_info'
#define dev_info(dev, fmt, arg...) _dev_info(dev, fmt, ##arg)
^
cc1: some warnings being treated as errors
--
In file included from include/linux/printk.h:277:0,
from include/linux/kernel.h:13,
from include/linux/clk.h:16,
from drivers/media/platform/sti/hva/hva-hw.c:8:
drivers/media/platform/sti/hva/hva-hw.c: In function 'hva_hw_execute_task':
>> drivers/media/platform/sti/hva/hva-hw.c:523:15: warning: format '%x' expects argument of type 'unsigned int', but argument 7 has type 'dma_addr_t {aka long long unsigned int}' [-Wformat=]
dev_dbg(dev, "%s %s: Send task ( cmd:%d, task_desc:0x%x)\n",
^
include/linux/dynamic_debug.h:86:39: note: in definition of macro 'dynamic_dev_dbg'
__dynamic_dev_dbg(&descriptor, dev, fmt, \
^
>> drivers/media/platform/sti/hva/hva-hw.c:523:2: note: in expansion of macro 'dev_dbg'
dev_dbg(dev, "%s %s: Send task ( cmd:%d, task_desc:0x%x)\n",
^
--
In file included from include/linux/printk.h:277:0,
from include/linux/kernel.h:13,
from include/linux/list.h:8,
from include/linux/preempt.h:10,
from include/linux/spinlock.h:50,
from include/linux/seqlock.h:35,
from include/linux/time.h:5,
from include/linux/ktime.h:24,
from include/linux/poll.h:6,
from include/media/v4l2-dev.h:12,
from include/media/v4l2-common.h:29,
from drivers/media/platform/sti/hva/hva.h:11,
from drivers/media/platform/sti/hva/hva-mem.c:8:
drivers/media/platform/sti/hva/hva-mem.c: In function 'hva_mem_alloc':
>> drivers/media/platform/sti/hva/hva-mem.c:43:3: warning: format '%x' expects argument of type 'unsigned int', but argument 7 has type 'dma_addr_t {aka long long unsigned int}' [-Wformat=]
"%s allocate %d bytes of HW memory @(virt=%p, phy=0x%x): %s\n",
^
include/linux/dynamic_debug.h:86:39: note: in definition of macro 'dynamic_dev_dbg'
__dynamic_dev_dbg(&descriptor, dev, fmt, \
^
>> drivers/media/platform/sti/hva/hva-mem.c:42:2: note: in expansion of macro 'dev_dbg'
dev_dbg(dev,
^
drivers/media/platform/sti/hva/hva-mem.c: In function 'hva_mem_free':
drivers/media/platform/sti/hva/hva-mem.c:57:3: warning: format '%x' expects argument of type 'unsigned int', but argument 7 has type 'dma_addr_t {aka long long unsigned int}' [-Wformat=]
"%s free %d bytes of HW memory @(virt=%p, phy=0x%x): %s\n",
^
include/linux/dynamic_debug.h:86:39: note: in definition of macro 'dynamic_dev_dbg'
__dynamic_dev_dbg(&descriptor, dev, fmt, \
^
drivers/media/platform/sti/hva/hva-mem.c:56:2: note: in expansion of macro 'dev_dbg'
dev_dbg(dev,
^
vim +/UL +97 drivers/media/platform/sti/hva/hva-v4l2.c
2 * Copyright (C) STMicroelectronics SA 2015
3 * Authors: Yannick Fertre <yannick.fertre@st.com>
4 * Hugues Fruchet <hugues.fruchet@st.com>
5 * License terms: GNU General Public License (GPL), version 2
6 */
7
> 8 #include <linux/device.h>
9 #include <linux/dma-mapping.h>
10 #include <linux/module.h>
11 #include <linux/platform_device.h>
12 #include <linux/slab.h>
13 #include <linux/version.h>
14 #include <linux/of.h>
15 #include <media/v4l2-ioctl.h>
16 #include <media/videobuf2-dma-contig.h>
17 #include "hva.h"
18 #include "hva-hw.h"
19
20 #define HVA_NAME "hva"
21
22 /*
23 * 1 frame at least for user
24 * limit number of frames to 16
25 */
26 #define MAX_FRAMES 16
27 #define MIN_FRAMES 1
28
29 #define HVA_MIN_WIDTH 32
30 #define HVA_MAX_WIDTH 1920
31 #define HVA_MIN_HEIGHT 32
32 #define HVA_MAX_HEIGHT 1080
33
34 #define DFT_CFG_WIDTH HVA_MIN_WIDTH
35 #define DFT_CFG_HEIGHT HVA_MIN_HEIGHT
36 #define DFT_CFG_BITRATE_MODE V4L2_MPEG_VIDEO_BITRATE_MODE_CBR
37 #define DFT_CFG_GOP_SIZE 16
38 #define DFT_CFG_INTRA_REFRESH true
39 #define DFT_CFG_FRAME_NUM 1
40 #define DFT_CFG_FRAME_DEN 30
41 #define DFT_CFG_QPMIN 5
42 #define DFT_CFG_QPMAX 51
43 #define DFT_CFG_DCT8X8 false
44 #define DFT_CFG_COMP_QUALITY 85
45 #define DFT_CFG_SAR_ENABLE 1
46 #define DFT_CFG_BITRATE (20000 * 1024)
47 #define DFT_CFG_CPB_SIZE (25000 * 1024)
48
49 static const struct hva_frameinfo frame_dflt_fmt = {
50 .fmt = {
51 .pixelformat = V4L2_PIX_FMT_NV12,
52 .nb_planes = 2,
53 .bpp = 12,
54 .bpp_plane0 = 8,
55 .w_align = 2,
56 .h_align = 2
57 },
58 .width = DFT_CFG_WIDTH,
59 .height = DFT_CFG_HEIGHT,
60 .crop = {0, 0, DFT_CFG_WIDTH, DFT_CFG_HEIGHT},
61 .frame_width = DFT_CFG_WIDTH,
62 .frame_height = DFT_CFG_HEIGHT
63 };
64
65 static const struct hva_streaminfo stream_dflt_fmt = {
66 .width = DFT_CFG_WIDTH,
67 .height = DFT_CFG_HEIGHT
68 };
69
70 /* list of stream formats supported by hva hardware */
71 const u32 stream_fmt[] = {
72 };
73
74 /* list of pixel formats supported by hva hardware */
75 static const struct hva_frame_fmt frame_fmts[] = {
76 /* NV12. YUV420SP - 1 plane for Y + 1 plane for (CbCr) */
77 {
78 .pixelformat = V4L2_PIX_FMT_NV12,
79 .nb_planes = 2,
80 .bpp = 12,
81 .bpp_plane0 = 8,
82 .w_align = 2,
83 .h_align = 2
84 },
85 /* NV21. YUV420SP - 1 plane for Y + 1 plane for (CbCr) */
86 {
87 .pixelformat = V4L2_PIX_FMT_NV21,
88 .nb_planes = 2,
89 .bpp = 12,
90 .bpp_plane0 = 8,
91 .w_align = 2,
92 .h_align = 2
93 },
94 };
95
96 /* offset to differentiate OUTPUT/CAPTURE @mmap */
> 97 #define MMAP_FRAME_OFFSET (UL(0x100000000) / 2)
98
99 /* registry of available encoders */
100 const struct hva_encoder *hva_encoders[] = {
101 };
102
103 static const struct hva_frame_fmt *hva_find_frame_fmt(u32 pixelformat)
104 {
105 const struct hva_frame_fmt *fmt;
106 unsigned int i;
107
108 for (i = 0; i < ARRAY_SIZE(frame_fmts); i++) {
109 fmt = &frame_fmts[i];
110 if (fmt->pixelformat == pixelformat)
111 return fmt;
112 }
113
114 return NULL;
115 }
116
117 static void register_encoder(struct hva_device *hva,
118 const struct hva_encoder *enc)
119 {
120 if (hva->nb_of_encoders >= HVA_MAX_ENCODERS) {
121 dev_warn(hva->dev,
122 "%s can' t register encoder (max nb (%d) is reached!)\n",
123 enc->name, HVA_MAX_ENCODERS);
124 return;
125 }
126
127 /* those encoder ops are mandatory */
128 WARN_ON(!enc->open);
129 WARN_ON(!enc->close);
130 WARN_ON(!enc->encode);
131
132 hva->encoders[hva->nb_of_encoders] = enc;
133 hva->nb_of_encoders++;
134 dev_info(hva->dev, "%s encoder registered\n", enc->name);
135 }
136
137 static void register_all(struct hva_device *hva)
138 {
139 unsigned int i;
140
141 for (i = 0; i < ARRAY_SIZE(hva_encoders); i++)
142 register_encoder(hva, hva_encoders[i]);
143 }
144
145 static int hva_open_encoder(struct hva_ctx *ctx, u32 streamformat,
146 u32 pixelformat, struct hva_encoder **penc)
147 {
148 struct hva_device *hva = ctx_to_hdev(ctx);
149 struct device *dev = ctx_to_dev(ctx);
150 struct hva_encoder *enc;
151 unsigned int i;
152 int ret;
153
154 /* find an encoder which can deal with these formats */
155 for (i = 0; i < hva->nb_of_encoders; i++) {
156 enc = (struct hva_encoder *)hva->encoders[i];
157 if ((enc->streamformat == streamformat) &&
158 (enc->pixelformat == pixelformat))
159 break; /* found */
160 }
161
162 if (i == hva->nb_of_encoders) {
163 dev_err(dev, "%s no encoder found matching %4.4s => %4.4s\n",
> 164 ctx->name, (char *)pixelformat, (char *)streamformat);
165 return -EINVAL;
166 }
167
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
[-- Attachment #2: .config.gz --]
[-- Type: application/octet-stream, Size: 51054 bytes --]
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 2/3] [media] hva: STiH41x multi-format video encoder V4L2 driver
2015-12-18 10:45 ` [PATCH 2/3] [media] hva: STiH41x multi-format video encoder V4L2 driver Yannick Fertre
2015-12-18 11:42 ` kbuild test robot
@ 2016-01-11 10:10 ` Hans Verkuil
1 sibling, 0 replies; 6+ messages in thread
From: Hans Verkuil @ 2016-01-11 10:10 UTC (permalink / raw)
To: Yannick Fertre, linux-media; +Cc: Benjamin Gaignard, hugues.fruchet, kernel
Hi Yannick,
I apologize for the delay in reviewing this, but I was on vacation.
But here it is:
On 12/18/2015 11:45 AM, Yannick Fertre wrote:
> This patch adds HVA (Hardware Video Accelerator) support for STI platform.
>
> Signed-off-by: Yannick Fertre <yannick.fertre@st.com>
> ---
> drivers/media/platform/Kconfig | 13 +
> drivers/media/platform/Makefile | 1 +
> drivers/media/platform/sti/hva/Makefile | 2 +
> drivers/media/platform/sti/hva/hva-hw.c | 561 ++++++++++++
> drivers/media/platform/sti/hva/hva-hw.h | 76 ++
> drivers/media/platform/sti/hva/hva-mem.c | 63 ++
> drivers/media/platform/sti/hva/hva-mem.h | 20 +
> drivers/media/platform/sti/hva/hva-v4l2.c | 1404 +++++++++++++++++++++++++++++
> drivers/media/platform/sti/hva/hva.h | 499 ++++++++++
> 9 files changed, 2639 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
>
<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..051d2ea
> --- /dev/null
> +++ b/drivers/media/platform/sti/hva/hva-v4l2.c
> @@ -0,0 +1,1404 @@
> +/*
> + * 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/device.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <linux/version.h>
> +#include <linux/of.h>
> +#include <media/v4l2-ioctl.h>
> +#include <media/videobuf2-dma-contig.h>
> +#include "hva.h"
> +#include "hva-hw.h"
> +
> +#define HVA_NAME "hva"
> +
> +/*
> + * 1 frame at least for user
> + * limit number of frames to 16
> + */
> +#define MAX_FRAMES 16
> +#define MIN_FRAMES 1
> +
> +#define HVA_MIN_WIDTH 32
> +#define HVA_MAX_WIDTH 1920
> +#define HVA_MIN_HEIGHT 32
> +#define HVA_MAX_HEIGHT 1080
> +
> +#define DFT_CFG_WIDTH HVA_MIN_WIDTH
> +#define DFT_CFG_HEIGHT HVA_MIN_HEIGHT
> +#define DFT_CFG_BITRATE_MODE V4L2_MPEG_VIDEO_BITRATE_MODE_CBR
> +#define DFT_CFG_GOP_SIZE 16
> +#define DFT_CFG_INTRA_REFRESH true
> +#define DFT_CFG_FRAME_NUM 1
> +#define DFT_CFG_FRAME_DEN 30
> +#define DFT_CFG_QPMIN 5
> +#define DFT_CFG_QPMAX 51
> +#define DFT_CFG_DCT8X8 false
> +#define DFT_CFG_COMP_QUALITY 85
> +#define DFT_CFG_SAR_ENABLE 1
> +#define DFT_CFG_BITRATE (20000 * 1024)
> +#define DFT_CFG_CPB_SIZE (25000 * 1024)
> +
> +static const struct hva_frameinfo frame_dflt_fmt = {
> + .fmt = {
> + .pixelformat = V4L2_PIX_FMT_NV12,
> + .nb_planes = 2,
> + .bpp = 12,
> + .bpp_plane0 = 8,
> + .w_align = 2,
> + .h_align = 2
> + },
> + .width = DFT_CFG_WIDTH,
> + .height = DFT_CFG_HEIGHT,
> + .crop = {0, 0, DFT_CFG_WIDTH, DFT_CFG_HEIGHT},
> + .frame_width = DFT_CFG_WIDTH,
> + .frame_height = DFT_CFG_HEIGHT
> +};
> +
> +static const struct hva_streaminfo stream_dflt_fmt = {
> + .width = DFT_CFG_WIDTH,
> + .height = DFT_CFG_HEIGHT
> +};
> +
> +/* list of stream formats supported by hva hardware */
> +const u32 stream_fmt[] = {
> +};
> +
> +/* list of pixel formats supported by hva hardware */
> +static const struct hva_frame_fmt frame_fmts[] = {
> + /* NV12. YUV420SP - 1 plane for Y + 1 plane for (CbCr) */
> + {
> + .pixelformat = V4L2_PIX_FMT_NV12,
> + .nb_planes = 2,
> + .bpp = 12,
> + .bpp_plane0 = 8,
> + .w_align = 2,
> + .h_align = 2
> + },
> + /* NV21. YUV420SP - 1 plane for Y + 1 plane for (CbCr) */
> + {
> + .pixelformat = V4L2_PIX_FMT_NV21,
> + .nb_planes = 2,
> + .bpp = 12,
> + .bpp_plane0 = 8,
> + .w_align = 2,
> + .h_align = 2
> + },
> +};
> +
> +/* offset to differentiate OUTPUT/CAPTURE @mmap */
> +#define MMAP_FRAME_OFFSET (UL(0x100000000) / 2)
> +
> +/* registry of available encoders */
> +const struct hva_encoder *hva_encoders[] = {
> +};
> +
> +static const struct hva_frame_fmt *hva_find_frame_fmt(u32 pixelformat)
> +{
> + const struct hva_frame_fmt *fmt;
> + unsigned int i;
> +
> + for (i = 0; i < ARRAY_SIZE(frame_fmts); i++) {
> + fmt = &frame_fmts[i];
> + if (fmt->pixelformat == pixelformat)
> + return fmt;
> + }
> +
> + return NULL;
> +}
> +
> +static void register_encoder(struct hva_device *hva,
> + const struct hva_encoder *enc)
> +{
> + if (hva->nb_of_encoders >= HVA_MAX_ENCODERS) {
> + dev_warn(hva->dev,
> + "%s can' t register encoder (max nb (%d) is reached!)\n",
> + enc->name, HVA_MAX_ENCODERS);
> + return;
> + }
> +
> + /* those encoder ops are mandatory */
> + WARN_ON(!enc->open);
> + WARN_ON(!enc->close);
> + WARN_ON(!enc->encode);
> +
> + hva->encoders[hva->nb_of_encoders] = enc;
> + hva->nb_of_encoders++;
> + dev_info(hva->dev, "%s encoder registered\n", enc->name);
> +}
> +
> +static void register_all(struct hva_device *hva)
> +{
> + unsigned int i;
> +
> + for (i = 0; i < ARRAY_SIZE(hva_encoders); i++)
> + register_encoder(hva, hva_encoders[i]);
> +}
> +
> +static int hva_open_encoder(struct hva_ctx *ctx, u32 streamformat,
> + u32 pixelformat, struct hva_encoder **penc)
> +{
> + struct hva_device *hva = ctx_to_hdev(ctx);
> + struct device *dev = ctx_to_dev(ctx);
> + struct hva_encoder *enc;
> + unsigned int i;
> + int ret;
> +
> + /* find an encoder which can deal with these formats */
> + for (i = 0; i < hva->nb_of_encoders; i++) {
> + enc = (struct hva_encoder *)hva->encoders[i];
> + if ((enc->streamformat == streamformat) &&
> + (enc->pixelformat == pixelformat))
> + break; /* found */
> + }
> +
> + if (i == hva->nb_of_encoders) {
> + 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 name instance */
> + 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(hva->dev, "%s enc->open failed (%d)\n",
> + ctx->name, ret);
> + return ret;
> + }
> +
> + *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_device *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_frame(struct file *file, void *priv,
> + struct v4l2_fmtdesc *f)
> +{
> + /* index don't have to exceed number of format supported */
> + if (f->index >= ARRAY_SIZE(frame_fmts))
Two spaces instead of one after '>='.
> + return -EINVAL;
> +
> + /* pixel format */
> + f->pixelformat = frame_fmts[f->index].pixelformat;
> +
> + return 0;
> +}
> +
> +static int hva_enum_fmt_stream(struct file *file, void *priv,
> + struct v4l2_fmtdesc *f)
> +{
> + /* index don't have to exceed number of stream format supported */
> + if (f->index >= ARRAY_SIZE(stream_fmt))
> + return -EINVAL;
> +
> + /* pixel format */
> + f->pixelformat = stream_fmt[f->index];
> +
> + /* compressed */
> + f->flags = V4L2_FMT_FLAG_COMPRESSED;
> +
> + 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 hva_device *hva = ctx_to_hdev(ctx);
> + struct device *dev = ctx_to_dev(ctx);
> + struct v4l2_pix_format *pix = &f->fmt.pix;
> + const struct hva_encoder *enc = NULL;
> + unsigned int i;
> +
> + if ((f->fmt.pix.width == 0) || (f->fmt.pix.height == 0))
> + goto out;
This is wrong (and I don't understand why v4l2-compliance doesn't complain about
it, possibly because m2m support in v4l2-compliance for codecs is limited). The
only reason try_fmt can ever return an error is if the pixelformat is not
supported. If a wrong width/height is passed in, then that should be replace by
a valid default width/height.
> +
> + f->fmt.pix.field = V4L2_FIELD_NONE;
> + f->fmt.pix.priv = 0;
No need to set priv to 0 anymore, just drop this line everywhere you use it.
Again, I don't understand how this can pass v4l2-compliance: it should complain
about uninitialized fields: colorspace, bytesperline (must be 0 for compressed
formats) and sizeimage (worst-case buffer size).
> +
> + for (i = 0; i < hva->nb_of_encoders; i++) {
> + enc = hva->encoders[i];
> + if (enc->streamformat == pix->pixelformat)
> + if ((f->fmt.pix.height * f->fmt.pix.width) <=
> + (enc->max_width * enc->max_height))
> + return 0;
> + }
> +out:
> + dev_dbg(dev, "%s stream format or resolution %dx%d not supported\n",
> + ctx->name, f->fmt.pix.width, f->fmt.pix.height);
> + return -EINVAL;
> +}
> +
> +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;
> + const struct hva_frame_fmt *format;
> + u32 in_w, in_h;
> +
> + format = hva_find_frame_fmt(pix->pixelformat);
> + if (!format) {
> + dev_dbg(dev, "%s Unknown format 0x%x\n", ctx->name,
> + pix->pixelformat);
> + return -EINVAL;
> + }
> +
> + /* adjust width & height */
> + in_w = pix->width;
> + in_h = pix->height;
> + v4l_bound_align_image(&pix->width,
> + HVA_MIN_WIDTH, HVA_MAX_WIDTH,
> + ffs(format->w_align) - 1,
> + &pix->height,
> + HVA_MIN_HEIGHT, HVA_MAX_HEIGHT,
> + ffs(format->h_align) - 1,
> + 0);
> +
> + if ((pix->width != in_w) || (pix->height != in_h))
> + dev_dbg(dev, "%s size updated: %dx%d -> %dx%d\n", ctx->name,
> + in_w, in_h, pix->width, pix->height);
> +
> + f->fmt.pix.field = V4L2_FIELD_NONE;
> + f->fmt.pix.priv = 0;
Missing initializations for colorspace, bytesperline and sizeimage.
> +
> + 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);
> + int ret;
> +
> + dev_dbg(dev, "%s %s %dx%d fmt:%.4s size:%d\n",
> + ctx->name, __func__, 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_err(dev,
> + "%s %s %.4s format or %dx%d resolution not supported\n",
> + ctx->name, __func__, (char *)&f->fmt.pix.pixelformat,
> + f->fmt.pix.width, f->fmt.pix.height);
> + return ret;
> + }
> +
> + /* update context */
> + ctx->streaminfo.width = f->fmt.pix.width;
> + ctx->streaminfo.height = f->fmt.pix.height;
> + ctx->streaminfo.streamformat = f->fmt.pix.pixelformat;
> + ctx->streaminfo.dpb = 1;
> + ctx->flags |= HVA_FLAG_STREAMINFO;
> +
> + if ((!ctx->encoder) && (ctx->flags & HVA_FLAG_FRAMEINFO)) {
> + ret = hva_open_encoder(ctx,
> + ctx->streaminfo.streamformat,
> + ctx->frameinfo.fmt.pixelformat,
> + &ctx->encoder);
> + if (ret)
> + return ret;
I'm not sure this is the right place: I would expect this in the start_streaming
op, because that's when the encoder really starts. I would also expect to see a check
here whether streaming is in progress: usually you can't change the format once
streaming is active.
> + }
> +
> + 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;
> + const struct hva_frame_fmt *fmt;
> + int ret = 0;
> +
> + dev_dbg(dev, "%s %s %dx%d fmt:%.4s size:%d\n",
> + ctx->name, __func__, 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_err(dev,
> + "%s %s %.4s format or %dx%d resolution not supported\n",
> + ctx->name, __func__, (char *)&f->fmt.pix.pixelformat,
> + f->fmt.pix.width, f->fmt.pix.height);
> + return ret;
> + }
> +
> + fmt = hva_find_frame_fmt(pix->pixelformat);
> + if (!fmt) {
> + dev_dbg(dev, "%s %s unknown format 0x%x\n", ctx->name,
> + ctx->name, pix->pixelformat);
> + return -EINVAL;
> + }
> +
> + memcpy(&ctx->frameinfo.fmt, fmt, sizeof(struct hva_frame_fmt));
> + ctx->frameinfo.frame_width = ALIGN(pix->width, 16);
> + ctx->frameinfo.frame_height = ALIGN(pix->height, 16);
> + ctx->frameinfo.width = pix->width;
> + ctx->frameinfo.height = pix->height;
> + ctx->frameinfo.crop.width = pix->width;
> + ctx->frameinfo.crop.height = pix->height;
> + ctx->frameinfo.crop.left = 0;
> + ctx->frameinfo.crop.top = 0;
> +
> + ctx->flags |= HVA_FLAG_FRAMEINFO;
> +
> + if ((!ctx->encoder) && (ctx->flags & HVA_FLAG_STREAMINFO))
> + ret = hva_open_encoder(ctx,
> + ctx->streaminfo.streamformat,
> + ctx->frameinfo.fmt.pixelformat,
> + &ctx->encoder);
Same comments as for the previous function.
> +
> + return ret;
> +}
> +
> +static int hva_s_ext_ctrls(struct file *file, void *fh,
> + struct v4l2_ext_controls *ctrls)
Huh? You must use the control framework instead of implementing this directly.
Just look in other drivers how that's done and Documentation/video4linux/v4l2-controls.txt.
> +{
> + struct hva_ctx *ctx = fh_to_ctx(file->private_data);
> + struct device *dev = ctx_to_dev(ctx);
> + unsigned int i;
> +
> + dev_dbg(dev, "%s %s count controls %d\n", ctx->name, __func__,
> + ctrls->count);
> +
> + for (i = 0; i < ctrls->count; i++) {
> + switch (ctrls->controls[i].id) {
> + case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
> + ctx->ctrls.gop_size = ctrls->controls[i].value;
> + dev_dbg(dev, "%s V4L2_CID_MPEG_VIDEO_GOP_SIZE %d\n",
> + ctx->name, ctrls->controls[i].value);
> + break;
> + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
> + ctx->ctrls.bitrate_mode = ctrls->controls[i].value;
> + dev_dbg(dev, "%s V4L2_CID_MPEG_VIDEO_BITRATE_MODE %d\n",
> + ctx->name, ctrls->controls[i].value);
> + break;
> + case V4L2_CID_MPEG_VIDEO_BITRATE:
> + ctx->ctrls.bitrate = ctrls->controls[i].value;
> + dev_dbg(dev, "%s V4L2_CID_MPEG_VIDEO_BITRATE %d\n",
> + ctx->name, ctrls->controls[i].value);
> + break;
> + case V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB:
> + ctx->ctrls.intra_refresh = ctrls->controls[i].value;
> + dev_dbg(dev,
> + "%s V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB %d\n",
> + ctx->name, ctrls->controls[i].value);
> + break;
> + case V4L2_CID_MPEG_VIDEO_ASPECT:
> + /* only one video aspect ratio supported (1/1) */
> + switch (ctrls->controls[i].value) {
> + case V4L2_MPEG_VIDEO_ASPECT_1x1:
> + dev_dbg(dev,
> + "%s V4L2_CID_MPEG_VIDEO_ASPECT 1x1\n",
> + ctx->name);
> + break;
> + case V4L2_MPEG_VIDEO_ASPECT_4x3:
> + case V4L2_MPEG_VIDEO_ASPECT_16x9:
> + case V4L2_MPEG_VIDEO_ASPECT_221x100:
> + default:
> + dev_err(dev,
> + "%s V4L2_CID_MPEG_VIDEO_ASPECT: Unsupported aspect ratio %d\n",
> + ctx->name, ctrls->controls[i].value);
> + return -EINVAL;
> + }
> + break;
> + default:
> + dev_err(dev,
> + "%s VIDIOC_S_EXT_CTRLS(): Unsupported control id %d\n",
> + ctx->name, ctrls->controls[i].id);
> + return -EINVAL;
> + }
> + }
> +
> + 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);
> +
> + ctx->time_per_frame.numerator = sp->parm.capture.timeperframe.numerator;
> + ctx->time_per_frame.denominator =
> + sp->parm.capture.timeperframe.denominator;
> +
> + dev_dbg(dev, "%s set parameters %d/%d\n",
> + ctx->name, ctx->time_per_frame.numerator,
> + ctx->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);
> +
> + sp->parm.capture.timeperframe.numerator = ctx->time_per_frame.numerator;
> + sp->parm.capture.timeperframe.denominator =
> + ctx->time_per_frame.denominator;
> +
> + dev_dbg(dev, "%s get parameters %d/%d\n",
> + ctx->name, ctx->time_per_frame.numerator,
> + ctx->time_per_frame.denominator);
> +
> + 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);
> +
> + /* get stream format */
> + f->fmt.pix.width = ctx->streaminfo.width;
> + f->fmt.pix.height = ctx->streaminfo.height;
> + f->fmt.pix.field = V4L2_FIELD_NONE;
> + /* 32 bytes alignment */
> + f->fmt.pix.bytesperline = ALIGN(f->fmt.pix.width, 32);
Bytesperline makes no sense for compressed formats, so set this to 0.
> + f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height;
FYI: for compressed formats the sizeimage field should return the worst-case buffer
size that is needed. Which you set to width * height. You know best whether that
is correct, but it feels a bit high.
> + f->fmt.pix.pixelformat = ctx->streaminfo.streamformat;
> + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
> +
> + dev_dbg(dev, "%s %s %dx%d fmt:%.4s size:%d\n",
> + ctx->name, __func__, 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_frame_fmt *fmt = &ctx->frameinfo.fmt;
> + int width = ctx->frameinfo.frame_width;
> + int height = ctx->frameinfo.frame_height;
> +
> + /* get source format */
> + f->fmt.pix.pixelformat = fmt->pixelformat;
> + f->fmt.pix.width = ctx->frameinfo.width;
> + f->fmt.pix.height = ctx->frameinfo.height;
> + f->fmt.pix.field = V4L2_FIELD_NONE;
> + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
> + f->fmt.pix.bytesperline = (width * fmt->bpp_plane0) / 8;
> + f->fmt.pix.sizeimage = (width * height * fmt->bpp) / 8;
> +
> + dev_dbg(dev, "%s %s %dx%d fmt:%.4s size:%d\n",
> + ctx->name, __func__, f->fmt.pix.width, f->fmt.pix.height,
> + (u8 *)&f->fmt.pix.pixelformat, f->fmt.pix.sizeimage);
> +
> + return 0;
> +}
> +
> +static int hva_reqbufs(struct file *file, void *priv,
> + struct v4l2_requestbuffers *reqbufs)
> +{
> + struct hva_ctx *ctx = fh_to_ctx(file->private_data);
> + struct device *dev = ctx_to_dev(ctx);
> + int ret = 0;
> +
> + dev_dbg(dev, "%s %s %s\n", ctx->name, __func__,
> + to_type_str(reqbufs->type));
> +
> + ret = vb2_reqbufs(get_queue(ctx, reqbufs->type), reqbufs);
> + if (ret) {
> + dev_err(dev, "%s vb2_reqbufs failed (%d)\n", ctx->name, ret);
Drop this, vb2 already has all the debugging you need.
> + return ret;
> + }
> +
> + if (reqbufs->count == 0) {
> + /*
> + * buffers have been freed in vb2 __reqbufs()
> + * now cleanup "allocation context" ...
> + */
> + if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
> + vb2_dma_contig_cleanup_ctx(ctx->q_frame.alloc_ctx[0]);
> + ctx->q_frame.alloc_ctx[0] = NULL;
Nack: you clean up the context when the driver is removed, not if the
buffer allocation fails.
> + } else if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
> + vb2_dma_contig_cleanup_ctx(ctx->q_stream.alloc_ctx[0]);
> + ctx->q_stream.alloc_ctx[0] = NULL;
> + }
> + }
> +
> + return 0;
> +}
A general question: any reason why you can't use the v4l2-mem2mem.h framework for
this driver? It would likely simplify the driver code.
> +
> +static int hva_create_bufs(struct file *file, void *priv,
> + struct v4l2_create_buffers *create)
> +{
> + struct hva_ctx *ctx = fh_to_ctx(file->private_data);
> + struct vb2_queue *q = get_queue(ctx, create->format.type);
> +
> + return vb2_create_bufs(q, create);
> +}
> +
> +static int hva_querybuf(struct file *file, void *priv, struct v4l2_buffer *b)
> +{
> + struct hva_ctx *ctx = fh_to_ctx(file->private_data);
> + struct device *dev = ctx_to_dev(ctx);
> + int ret = 0;
> +
> + dev_dbg(dev, "%s %s %s[%d]\n", ctx->name, __func__,
> + to_type_str(b->type), b->index);
> +
> + /* vb2 call */
> + ret = vb2_querybuf(get_queue(ctx, b->type), b);
> + if (ret) {
> + dev_err(dev, "%s vb2_querybuf failed (%d)\n", ctx->name, ret);
Drop this, vb2 already has all the debugging you need.
> + return ret;
> + }
> +
> + /* add an offset to differentiate OUTPUT/CAPTURE @mmap time */
> + if ((b->memory == V4L2_MEMORY_MMAP) &&
> + (b->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)) {
> + b->m.offset += MMAP_FRAME_OFFSET;
> + }
> +
> + return 0;
> +}
> +
> +static int hva_expbuf(struct file *file, void *fh, struct v4l2_exportbuffer *b)
> +{
> + struct hva_ctx *ctx = fh_to_ctx(file->private_data);
> + struct device *dev = ctx_to_dev(ctx);
> + int ret = 0;
> +
> + /* request validation */
> + if ((b->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) &&
> + (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)) {
> + dev_err(dev,
> + "%s V4L2 EXPBUF: only type output/cature are supported\n",
cature -> capture
But vb2_expbuf already checks for wrong buffer types, so you can drop this check.
> + ctx->name);
> + return -EINVAL;
> + }
> +
> + /* vb2 call */
> + ret = vb2_expbuf(get_queue(ctx, b->type), b);
> + if (ret) {
> + dev_err(dev, "%s vb2_expbuf failed (%d)\n", ctx->name, ret);
Drop this, vb2 already has all the debugging you need.
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int hva_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
> +{
> + struct hva_ctx *ctx = fh_to_ctx(file->private_data);
> + struct device *dev = ctx_to_dev(ctx);
> + struct vb2_queue *q = get_queue(ctx, b->type);
> + int ret = 0;
> +
> + /* copy bytesused field from v4l2 buffer to vb2 buffer */
> + if ((b->index < MAX_FRAMES) &&
> + (b->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)) {
> + struct hva_stream *s = (struct hva_stream *)q->bufs[b->index];
> +
> + s->payload = b->bytesused;
> + }
> +
> + ret = vb2_qbuf(q, b);
> + if (ret) {
> + dev_err(dev, "%s vb2_qbuf failed (%d)\n", ctx->name, ret);
Drop this, vb2 already has all the debugging you need.
> + return ret;
> + }
> + return 0;
> +}
> +
> +static int hva_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
> +{
> + struct hva_ctx *ctx = fh_to_ctx(file->private_data);
> + struct device *dev = ctx_to_dev(ctx);
> + struct vb2_queue *q = get_queue(ctx, b->type);
> + int ret = 0;
> +
> + /* vb2 call */
> + ret = vb2_dqbuf(q, b, file->f_flags & O_NONBLOCK);
> + if (ret) {
> + dev_err(dev, "%s vb2_dqbuf failed (%d)\n", ctx->name, ret);
Drop this, vb2 already has all the debugging you need.
> + return ret;
> + }
> +
> + dev_dbg(dev, "%s %s %s[%d]\n", ctx->name, __func__,
> + to_type_str(b->type), b->index);
Drop this, vb2 already has all the debugging you need.
> +
> + return 0;
> +}
> +
> +static int hva_streamon(struct file *file, void *priv, enum v4l2_buf_type type)
> +{
> + struct hva_ctx *ctx = fh_to_ctx(file->private_data);
> + struct device *dev = ctx_to_dev(ctx);
> + int ret = 0;
> +
> + /* reset frame number */
> + if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
> + ctx->frame_num = 0;
> +
> + /* vb2 call */
> + ret = vb2_streamon(get_queue(ctx, type), type);
> + if (ret) {
> + dev_err(dev, "%s vb2_streamon failed (%d)\n", ctx->name, ret);
Drop this, vb2 already has all the debugging you need.
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int hva_streamoff(struct file *file, void *priv, enum v4l2_buf_type type)
> +{
> + struct hva_ctx *ctx = fh_to_ctx(file->private_data);
> + struct device *dev = ctx_to_dev(ctx);
> + struct hva_stream *sr, *node;
> + int ret = 0;
> +
> + /* release all active buffers */
> + if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
> + list_for_each_entry_safe(sr, node, &ctx->list_stream, list) {
> + list_del_init(&sr->list);
> + vb2_buffer_done(&sr->v4l2.vb2_buf, VB2_BUF_STATE_ERROR);
This belongs in stop_streaming.
> + }
> + }
> +
> + /* vb2 call */
> + ret = vb2_streamoff(get_queue(ctx, type), type);
> + if (ret) {
> + dev_err(dev, "%s vb2_streamoff failed (%d)\n", ctx->name, ret);
Drop this, vb2 already has all the debugging you need.
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int is_rect_enclosed(struct v4l2_rect *a, struct v4l2_rect *b)
> +{
> + /* return 1 if a is enclosed in b, or 0 otherwise. */
> + if (a->left < b->left || a->top < b->top)
> + return 0;
> +
> + if (a->left + a->width > b->left + b->width)
> + return 0;
> +
> + if (a->top + a->height > b->top + b->height)
> + return 0;
> +
> + return 1;
> +}
> +
> +static int hva_g_selection(struct file *file, void *fh,
> + struct v4l2_selection *s)
> +{
> + struct hva_ctx *ctx = fh_to_ctx(file->private_data);
> + struct device *dev = ctx_to_dev(ctx);
> +
> + if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
> + dev_err(dev, "%s %s: G_SELECTION failed, invalid type (%d)\n",
> + ctx->name, __func__, s->type);
> + return -EINVAL;
> + }
> +
> + switch (s->target) {
> + case V4L2_SEL_TGT_CROP:
> + /* cropped frame */
> + s->r = ctx->frameinfo.crop;
> + break;
> + case V4L2_SEL_TGT_CROP_DEFAULT:
> + case V4L2_SEL_TGT_CROP_BOUNDS:
> + /* complete frame */
> + s->r.left = 0;
> + s->r.top = 0;
> + s->r.width = ctx->frameinfo.width;
> + s->r.height = ctx->frameinfo.height;
> + break;
> + default:
> + dev_err(dev, "%s %s: G_SELECTION failed, invalid target (%d)\n",
> + ctx->name, __func__, s->target);
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int hva_s_selection(struct file *file, void *fh,
> + struct v4l2_selection *s)
> +{
> + struct hva_ctx *ctx = fh_to_ctx(file->private_data);
> + struct device *dev = ctx_to_dev(ctx);
> + struct v4l2_rect *in, out;
> +
> + if ((s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) ||
> + (s->target != V4L2_SEL_TGT_CROP)) {
> + dev_err(dev, "%s %s: S_SELECTION failed, invalid type (%d)\n",
> + ctx->name, __func__, s->type);
Drop this or make it dev_dbg. Invalid user input should not cause kernel log spamming.
> + return -EINVAL;
> + }
> +
> + in = &s->r;
> + out = *in;
> +
> + /* align and check origin */
> + out.left = ALIGN(in->left, ctx->frameinfo.fmt.w_align);
> + out.top = ALIGN(in->top, ctx->frameinfo.fmt.h_align);
> +
> + if (((out.left + out.width) > ctx->frameinfo.width) ||
> + ((out.top + out.height) > ctx->frameinfo.height)) {
> + dev_err(dev,
> + "%s %s: S_SELECTION failed, invalid crop %dx%d@(%d,%d)\n",
> + ctx->name, __func__, out.width, out.height,
> + out.left, out.top);
Ditto.
> + return -EINVAL;
> + }
> +
> + /* checks adjust constraints flags */
> + if (s->flags & V4L2_SEL_FLAG_LE && !is_rect_enclosed(&out, in))
> + return -ERANGE;
> +
> + if (s->flags & V4L2_SEL_FLAG_GE && !is_rect_enclosed(in, &out))
> + return -ERANGE;
> +
> + if ((out.left != in->left) || (out.top != in->top) ||
> + (out.width != in->width) || (out.height != in->height))
> + *in = out;
> +
> + ctx->frameinfo.crop = s->r;
> +
> + return 0;
> +}
> +
> +/* vb2 ioctls operations */
> +
> +static int hva_vb2_frame_queue_setup(struct vb2_queue *q,
> + const void *parg,
> + unsigned int *num_buffers,
> + unsigned int *num_planes,
> + unsigned int sizes[], void *alloc_ctxs[])
> +{
> + struct hva_ctx *ctx = fh_to_ctx(q->drv_priv);
> + struct device *dev = ctx_to_dev(ctx);
> + int width = ctx->frameinfo.frame_width;
> + int height = ctx->frameinfo.frame_height;
> +
> + dev_dbg(dev, "%s %s *num_buffers=%d\n", ctx->name, __func__,
> + *num_buffers);
> +
> + /* only one plane supported */
> + *num_planes = 1;
> +
> + /* setup nb of input buffers needed =
> + * user need (*num_buffer given, usually for grab pipeline) +
> + * encoder internal need
> + */
> + if (*num_buffers < MIN_FRAMES) {
> + dev_warn(dev,
> + "%s num_buffers too low (%d), increasing to %d\n",
> + ctx->name, *num_buffers, MIN_FRAMES);
dev_dbg, same for the remainder of this function.
Also, just set min_buffers_needed in vb2_queue to the minimum required buffers
and vb2 will take care of this for you.
> + *num_buffers = MIN_FRAMES;
> + }
> +
> + if (*num_buffers > MAX_FRAMES) {
> + dev_warn(dev,
> + "%s input frame count too high (%d), cut to %d\n",
> + ctx->name, *num_buffers, MAX_FRAMES);
> + *num_buffers = MAX_FRAMES;
> + }
> +
> + if (sizes[0])
> + dev_warn(dev, "%s psize[0] already set to %d\n", ctx->name,
> + sizes[0]);
> +
> + if (alloc_ctxs[0])
> + dev_warn(dev, "%s allocators[0] already set\n", ctx->name);
This is a meaningless check, just drop it.
> +
> + if (!(ctx->flags & HVA_FLAG_FRAMEINFO)) {
> + dev_err(dev, "%s %s frame format not set, using default format\n",
> + ctx->name, __func__);
> + }
> +
> + sizes[0] = (width * height * ctx->frameinfo.fmt.bpp) / 8;
> + alloc_ctxs[0] = vb2_dma_contig_init_ctx(dev);
The probe should call this and store the result in the top-level struct.
When the driver is removed the alloc context should be cleaned up. It's a one-time
thing.
> + /* alloc_ctxs[0] will be freed @ reqbufs(0) or @ release */
> +
> + return 0;
> +}
> +
> +static int hva_vb2_frame_prepare(struct vb2_buffer *vb)
> +{
> + struct vb2_queue *q = vb->vb2_queue;
> + struct hva_ctx *ctx = fh_to_ctx(q->drv_priv);
> + struct device *dev = ctx_to_dev(ctx);
> + struct hva_frame *fm = (struct hva_frame *)vb;
> +
> + if (!fm->prepared) {
> + /* get memory addresses */
> + fm->vaddr = vb2_plane_vaddr(&fm->v4l2.vb2_buf, 0);
> + fm->paddr =
> + vb2_dma_contig_plane_dma_addr(&fm->v4l2.vb2_buf, 0);
> + fm->prepared = true;
> +
> + ctx->num_frames++;
> +
> + dev_dbg(dev, "%s frame[%d] prepared; virt=%p, phy=0x%x\n",
> + ctx->name, vb->index, fm->vaddr,
> + fm->paddr);
> + }
> +
> + return 0;
> +}
> +
> +static void hva_vb2_frame_queue(struct vb2_buffer *vb)
> +{
> + struct vb2_queue *q = vb->vb2_queue;
> + struct hva_ctx *ctx = fh_to_ctx(q->drv_priv);
> + struct device *dev = ctx_to_dev(ctx);
> + const struct hva_encoder *enc = ctx_to_enc(ctx);
> + struct hva_frame *fm = NULL;
> + struct hva_stream *sr = NULL;
> + int ret = 0;
> +
> + fm = (struct hva_frame *)vb;
> +
> + if (!vb2_is_streaming(q)) {
> + vb2_buffer_done(&fm->v4l2.vb2_buf, VB2_BUF_STATE_ERROR);
> + return;
> + }
> +
> + /* get a free destination buffer */
> + if (list_empty(&ctx->list_stream)) {
> + dev_err(dev, "%s no free buffer for destination stream!\n",
> + ctx->name);
> + ctx->sys_errors++;
> + goto err;
> + }
> + sr = list_first_entry(&ctx->list_stream, struct hva_stream, list);
> +
> + if (!sr)
> + goto err;
> +
> + list_del(&sr->list);
> +
> + /* encode the frame & get stream unit */
> + ret = enc->encode(ctx, fm, sr);
> + if (ret)
> + goto err;
> +
> + /* propagate frame timestamp */
> + sr->v4l2.timestamp = fm->v4l2.timestamp;
> +
> + ctx->encoded_frames++;
> +
> + vb2_buffer_done(&sr->v4l2.vb2_buf, VB2_BUF_STATE_DONE);
> + vb2_buffer_done(&fm->v4l2.vb2_buf, VB2_BUF_STATE_DONE);
> +
> + return;
> +err:
> + if (sr)
> + vb2_buffer_done(&sr->v4l2.vb2_buf, VB2_BUF_STATE_ERROR);
> +
> + vb2_buffer_done(&fm->v4l2.vb2_buf, VB2_BUF_STATE_ERROR);
> +}
> +
> +static int hva_vb2_stream_queue_setup(struct vb2_queue *q,
> + const void *parg,
> + unsigned int *num_buffers,
> + unsigned int *num_planes,
> + unsigned int sizes[], void *alloc_ctxs[])
> +{
> + struct hva_ctx *ctx = fh_to_ctx(q->drv_priv);
> + struct device *dev = ctx_to_dev(ctx);
> + int max_buf_size = 0;
> + u32 pixelformat;
> + int width;
> + int height;
> +
> + dev_dbg(dev, "%s %s *num_buffers=%d\n", ctx->name, __func__,
> + *num_buffers);
> +
> + /* only one plane supported */
> + *num_planes = 1;
> +
> + /* number of buffers must be at least 1 */
> + if (*num_buffers < 1)
> + *num_buffers = 1;
> +
> + if (sizes[0])
> + dev_warn(dev, "%s psize[0] already set to %d\n", ctx->name,
> + sizes[0]);
> +
> + if (alloc_ctxs[0])
> + dev_warn(dev, "%s allocators[0] already set\n", ctx->name);
> +
> + if (!(ctx->flags & HVA_FLAG_STREAMINFO)) {
> + dev_err(dev, "%s %s stream format not set, using dflt format\n",
> + ctx->name, __func__);
> + }
> +
> + pixelformat = ctx->streaminfo.streamformat;
> + width = ctx->streaminfo.width;
> + height = ctx->streaminfo.height;
> +
> + switch (pixelformat) {
> + default:
> + dev_err(dev, "%s %s Unknown stream format\n", ctx->name,
> + __func__);
> + }
> +
> + sizes[0] = max_buf_size;
> + alloc_ctxs[0] = vb2_dma_contig_init_ctx(dev); /* free @ release */
> +
> + return 0;
> +}
> +
> +static int hva_vb2_stream_prepare(struct vb2_buffer *vb)
> +{
> + struct vb2_queue *q = vb->vb2_queue;
> + struct hva_ctx *ctx = fh_to_ctx(q->drv_priv);
> + struct device *dev = ctx_to_dev(ctx);
> + struct hva_stream *sr = (struct hva_stream *)vb;
> +
> + if (!sr->prepared) {
> + /* get memory addresses */
> + sr->vaddr = vb2_plane_vaddr(&sr->v4l2.vb2_buf, 0);
> + sr->paddr = vb2_dma_contig_plane_dma_addr(&sr->v4l2.vb2_buf, 0);
> + sr->prepared = true;
> +
> + dev_dbg(dev, "%s stream[%d] prepared; virt=%p, phy=0x%x\n",
> + ctx->name, vb->index, sr->vaddr, sr->paddr);
> + }
> +
> + return 0;
> +}
> +
> +static void hva_vb2_stream_queue(struct vb2_buffer *vb)
> +{
> + struct vb2_queue *q = vb->vb2_queue;
> + struct hva_ctx *ctx = fh_to_ctx(q->drv_priv);
> + struct hva_stream *sr = (struct hva_stream *)vb;
> +
> + /* check validity of video stream */
> + if (vb) {
> + /* enqueue to a list destination stream */
> + list_add(&sr->list, &ctx->list_stream);
> + }
> +}
> +
> +static struct vb2_ops hva_vb2_frame_ops = {
> + .queue_setup = hva_vb2_frame_queue_setup,
> + .buf_prepare = hva_vb2_frame_prepare,
> + .buf_queue = hva_vb2_frame_queue,
> + .wait_prepare = vb2_ops_wait_prepare,
> + .wait_finish = vb2_ops_wait_finish,
> +};
> +
> +static struct vb2_ops hva_vb2_stream_ops = {
> + .queue_setup = hva_vb2_stream_queue_setup,
> + .buf_prepare = hva_vb2_stream_prepare,
> + .buf_queue = hva_vb2_stream_queue,
> + .wait_prepare = vb2_ops_wait_prepare,
> + .wait_finish = vb2_ops_wait_finish,
You really need to add start/stop_streaming ops to setup/release the encoder.
That's the right place to do that.
> +};
> +
> +/* file basics operations */
> +
> +static int hva_open(struct file *file)
> +{
> + struct hva_device *hva = video_drvdata(file);
> + struct vb2_queue *q;
> + struct device *dev;
> + struct hva_ctx *ctx;
> + int ret = 0;
> + unsigned int i;
> +
> + WARN_ON(!hva);
> + dev = hva->dev;
> +
> + mutex_lock(&hva->lock);
> +
> + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
> + if (!ctx) {
> + mutex_unlock(&hva->lock);
> + return -ENOMEM;
> + }
> +
> + /* store the context address in the contexts list */
> + for (i = 0; i < MAX_CONTEXT; i++) {
> + if (!hva->contexts_list[i]) {
> + hva->contexts_list[i] = ctx;
> + /* save client id in context */
> + ctx->client_id = i;
> + break;
> + }
> + }
> +
> + v4l2_fh_init(&ctx->fh, video_devdata(file));
> + file->private_data = &ctx->fh;
> + v4l2_fh_add(&ctx->fh);
> +
> + /* recopy device handlers */
> + ctx->dev = hva->dev;
> + ctx->hdev = hva;
> +
> + /* setup vb2 queue for frame input */
> + q = &ctx->q_frame;
> + q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; /* to say input, weird! */
> + q->io_modes = VB2_MMAP | VB2_DMABUF;
> +
> + /* save file handle to private data field of the queue */
> + q->drv_priv = &ctx->fh;
> +
> + /* overload vb2 buffer size with private struct */
> + q->buf_struct_size = sizeof(struct hva_frame);
> +
> + q->ops = &hva_vb2_frame_ops;
> + q->mem_ops = (struct vb2_mem_ops *)&vb2_dma_contig_memops;
> + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
> + q->lock = &hva->lock;
> +
> + ret = vb2_queue_init(q);
> + if (ret) {
> + dev_err(dev, "%s [x:x] vb2_queue_init(frame) failed (%d)\n",
> + HVA_PREFIX, ret);
> + ctx->sys_errors++;
> + goto err_fh_del;
> + }
> +
> + /* setup vb2 queue of the destination */
> + q = &ctx->q_stream;
> + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> + q->io_modes = VB2_MMAP | VB2_DMABUF;
> +
> + /* save file handle to private data field of the queue */
> + q->drv_priv = &ctx->fh;
> +
> + /* overload vb2 buffer size with private struct */
> + q->buf_struct_size = sizeof(struct hva_stream);
> +
> + q->ops = &hva_vb2_stream_ops;
> + q->mem_ops = (struct vb2_mem_ops *)&vb2_dma_contig_memops;
> + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
> + q->lock = &hva->lock;
> +
> + ret = vb2_queue_init(q);
> + if (ret) {
> + dev_err(dev, "%s [x:x] vb2_queue_init(stream) failed (%d)\n",
> + HVA_PREFIX, ret);
> + ctx->sys_errors++;
> + goto err_queue_del_frame;
> + }
> +
> + /* initialize the list of stream buffers */
> + INIT_LIST_HEAD(&ctx->list_stream);
> +
> + /* name this instance */
> + hva->instance_id++; /* rolling id to identify this instance */
> + snprintf(ctx->name, sizeof(ctx->name), "[%3d:----]", hva->instance_id);
> +
> + /* initialize controls */
> + ctx->ctrls.bitrate_mode = DFT_CFG_BITRATE_MODE;
> + ctx->ctrls.bitrate = DFT_CFG_BITRATE;
> + ctx->ctrls.cpb_size = DFT_CFG_CPB_SIZE;
> + ctx->ctrls.gop_size = DFT_CFG_GOP_SIZE;
> + ctx->ctrls.intra_refresh = DFT_CFG_INTRA_REFRESH;
> + ctx->ctrls.dct8x8 = DFT_CFG_DCT8X8;
> + ctx->ctrls.qpmin = DFT_CFG_QPMIN;
> + ctx->ctrls.qpmax = DFT_CFG_QPMAX;
> + ctx->ctrls.jpeg_comp_quality = DFT_CFG_COMP_QUALITY;
> + ctx->ctrls.vui_sar = DFT_CFG_SAR_ENABLE;
> +
> + /* set by default time per frame */
> + ctx->time_per_frame.numerator = DFT_CFG_FRAME_NUM;
> + ctx->time_per_frame.denominator = DFT_CFG_FRAME_DEN;
> +
> + /* default format */
> + ctx->streaminfo = stream_dflt_fmt;
> + ctx->frameinfo = frame_dflt_fmt;
> +
> + hva->nb_of_instances++;
> +
> + mutex_unlock(&hva->lock);
> +
> + return 0;
> +
> +err_queue_del_frame:
> + vb2_queue_release(&ctx->q_frame);
> +err_fh_del:
> + v4l2_fh_del(&ctx->fh);
> + v4l2_fh_exit(&ctx->fh);
> + hva->contexts_list[ctx->client_id] = NULL;
> + devm_kfree(dev, ctx);
> +
> + mutex_unlock(&hva->lock);
> +
> + return ret;
> +}
> +
> +static int hva_release(struct file *file)
> +{
> + struct hva_device *hva = video_drvdata(file);
> + struct hva_ctx *ctx = fh_to_ctx(file->private_data);
> + struct device *dev = ctx_to_dev(ctx);
> + const struct hva_encoder *enc = ctx_to_enc(ctx);
> +
> + mutex_lock(&hva->lock);
> +
> + /* free queues: source & destination */
> + vb2_queue_release(&ctx->q_frame);
> + vb2_queue_release(&ctx->q_stream);
> +
> + v4l2_fh_del(&ctx->fh);
> + v4l2_fh_exit(&ctx->fh);
> +
> + /* will free dma memory of each frame in queue */
> + vb2_queue_release(&ctx->q_frame);
> + if (ctx->q_frame.alloc_ctx[0])
> + vb2_dma_contig_cleanup_ctx(ctx->q_frame.alloc_ctx[0]);
> +
> + /* will free dma memory of each aus in queue */
> + vb2_queue_release(&ctx->q_stream);
> + if (ctx->q_stream.alloc_ctx[0])
> + vb2_dma_contig_cleanup_ctx(ctx->q_stream.alloc_ctx[0]);
> +
> + /* clear context in contexts list */
> + if ((ctx->client_id >= MAX_CONTEXT) ||
> + (hva->contexts_list[ctx->client_id] != ctx)) {
> + dev_err(dev, "%s can't clear context in contexts list!\n",
> + ctx->name);
> + ctx->sys_errors++;
> + }
> + hva->contexts_list[ctx->client_id] = NULL;
> +
> + /* close encoder */
> + if (enc)
> + enc->close(ctx);
> +
> + devm_kfree(dev, ctx);
> +
> + hva->nb_of_instances--;
> +
> + mutex_unlock(&hva->lock);
> +
> + return 0;
> +}
> +
> +static int hva_mmap(struct file *file, struct vm_area_struct *vma)
> +{
> + struct hva_device *hva = video_drvdata(file);
> + struct hva_ctx *ctx = fh_to_ctx(file->private_data);
> + struct device *dev = ctx->dev;
> + unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
> + enum v4l2_buf_type type;
> + int ret;
> +
> + mutex_lock(&hva->lock);
> +
> + /* offset used to differentiate OUTPUT/CAPTURE */
> + if (offset < MMAP_FRAME_OFFSET) {
> + type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
> + } else {
> + type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> + vma->vm_pgoff -= (MMAP_FRAME_OFFSET >> PAGE_SHIFT);
> + }
> +
> + /* vb2 call */
> + ret = vb2_mmap(get_queue(ctx, type), vma);
> + if (ret) {
> + dev_err(dev, "%s vb2_mmap failed (%d)\n", ctx->name, ret);
> + ctx->sys_errors++;
> + mutex_unlock(&hva->lock);
> + return ret;
> + }
> +
> + mutex_unlock(&hva->lock);
> +
> + return 0;
> +}
> +
> +/* v4l2 ops */
> +static const struct v4l2_file_operations hva_fops = {
> + .owner = THIS_MODULE,
> + .open = hva_open,
> + .release = hva_release,
> + .unlocked_ioctl = video_ioctl2,
> + .mmap = hva_mmap,
> +};
> +
> +/* v4l2 ioctl ops */
> +static const struct v4l2_ioctl_ops hva_ioctl_ops = {
> + .vidioc_querycap = hva_querycap,
> + /* formats ioctl */
> + .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_s_ext_ctrls = hva_s_ext_ctrls,
> + .vidioc_g_parm = hva_g_parm,
> + .vidioc_s_parm = hva_s_parm,
> + /* buffers ioctls */
> + .vidioc_reqbufs = hva_reqbufs,
> + .vidioc_create_bufs = hva_create_bufs,
> + .vidioc_querybuf = hva_querybuf,
> + .vidioc_expbuf = hva_expbuf,
> + .vidioc_qbuf = hva_qbuf,
> + .vidioc_dqbuf = hva_dqbuf,
> + /* stream ioctls */
> + .vidioc_streamon = hva_streamon,
> + .vidioc_streamoff = hva_streamoff,
> + .vidioc_g_selection = hva_g_selection,
> + .vidioc_s_selection = hva_s_selection,
> +};
> +
> +static int hva_probe(struct platform_device *pdev)
> +{
> + struct hva_device *hva;
> + struct device *dev = &pdev->dev;
> + struct video_device *vdev;
> + 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_all(hva);
> +
> + /* register on V4L2 */
> + ret = v4l2_device_register(dev, &hva->v4l2_dev);
> + if (ret) {
> + dev_err(dev, "%s %s could not register v4l2 device\n",
> + HVA_PREFIX, HVA_NAME);
> + goto err_hw_remove;
> + }
> +
> + vdev = video_device_alloc();
> + vdev->fops = &hva_fops;
> + vdev->ioctl_ops = &hva_ioctl_ops;
> + vdev->release = video_device_release;
> + vdev->lock = &hva->lock;
> + vdev->v4l2_dev = &hva->v4l2_dev;
> + snprintf(vdev->name, sizeof(vdev->name), "%s", HVA_NAME);
> + vdev->vfl_dir = VFL_DIR_M2M;
> + ret = video_register_device(vdev, VFL_TYPE_GRABBER, 0);
> + 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);
> +
> + dev_info(dev, "%s %s registered as /dev/video%d\n", HVA_PREFIX,
> + HVA_NAME, vdev->num);
> +
> + dev_info(dev, "%s %s esram reserved for address: %p size:%d\n",
> + HVA_PREFIX, HVA_NAME, (void *)hva->esram_addr,
> + hva->esram_size);
> +
> + return 0;
> +
> +err_vdev_release:
> + video_device_release(vdev);
> +
> +err_hw_remove:
> + hva_hw_remove(hva);
> +
> +err:
> + return ret;
> +}
> +
> +static int hva_remove(struct platform_device *pdev)
> +{
> + struct hva_device *hva = platform_get_drvdata(pdev);
> + struct device *dev = hva_to_dev(hva);
> +
> + dev_info(dev, "%s removing %s\n", HVA_PREFIX, pdev->name);
> +
> + hva_hw_remove(hva);
> +
> + video_unregister_device(hva->vdev);
> + v4l2_device_unregister(&hva->v4l2_dev);
> +
> + return 0;
> +}
> +
> +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,stih407-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");
<snip>
Regards,
Hans
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2016-01-11 10:10 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-12-18 10:45 [PATCH 0/3] support of v4l2 encoder for STMicroelectronics SOC Yannick Fertre
2015-12-18 10:45 ` [PATCH 1/3] Documentation: devicetree: add STI HVA binding Yannick Fertre
2015-12-18 10:45 ` [PATCH 2/3] [media] hva: STiH41x multi-format video encoder V4L2 driver Yannick Fertre
2015-12-18 11:42 ` kbuild test robot
2016-01-11 10:10 ` Hans Verkuil
2015-12-18 10:45 ` [PATCH 3/3] [media] hva: add h264 support Yannick Fertre
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).