2021-05-20 12:19:24

by Dillon Min

[permalink] [raw]
Subject: [PATCH 0/7] Add support for DMA2D of STMicroelectronics STM32 SoC series

From: Dillon Min <[email protected]>

This patchset introduces a basic support for DMA2D Interface
of STMicroelectronics STM32 SoC series.

This first basic support implements R2M, M2M, M2M_PFC
M2M_BLEND support will be added later on.

This has been tested on STM32469-DISCO board.

The commit based on kernel(master): c3d0e3fd41b7f0f5d5d5b6022ab7e813f04ea727

Note for v4l2-compliance tool on nu-mmu platform:
I add two change based on v4l-utils since commit:
f0c7e3d71eaf4182bae7eb3ee0e43b4eeb047ea9

- change fork() to vfork() in v4l2-test-controls.cpp
since no-mmu platform don't include fork().

- bypass VIDIOC_QUERYCAP nullptr check
I'm not sure if this is the bug from my cross compile tool
which created by buildroot. user area's nullptr can't be
detected by kernel side, i was try to add access_ok()
in v4l2-ioctl.c, but no help

If this case must be fixed, i will continue to debug it.
the error log:
...
fail: v4l2-compliance.cpp(623): doioctl(node, VIDIOC_QUERYCAP, nullptr) !=
EFAULT
..

My changes on v4l2-compliance:

diff --git a/utils/v4l2-compliance/v4l2-compliance.cpp
b/utils/v4l2-compliance/v4l2-compliance.cpp
index 90a5036..a25fe4f 100644
--- a/utils/v4l2-compliance/v4l2-compliance.cpp
+++ b/utils/v4l2-compliance/v4l2-compliance.cpp
@@ -620,7 +620,7 @@ static int testCap(struct node *node)

memset(&vcap, 0xff, sizeof(vcap));
// Must always be there
- fail_on_test(doioctl(node, VIDIOC_QUERYCAP, nullptr) != EFAULT);
+ //fail_on_test(doioctl(node, VIDIOC_QUERYCAP, nullptr) != EFAULT);
fail_on_test(doioctl(node, VIDIOC_QUERYCAP, &vcap));
fail_on_test(check_ustring(vcap.driver, sizeof(vcap.driver)));
fail_on_test(check_ustring(vcap.card, sizeof(vcap.card)));

with v4l2-compliance test log (with above modification):
since the stm32f469-disco ram limitation, there are 25 failed on
dma_alloc_coherent()

Really appreciate if someone can help to test this patch on the STM32429I-EVAL
evaluation board (https://www.st.com/en/evaluation-tools/stm32429i-eval.html)
8M x 32-bit SDRAM, 1M x 16-bit SRAM and 8M x 16-bit NOR Flash

/ # free
total used free shared buffers cached
Mem: 15604 6104 9500 0 0 2488
-/+ buffers/cache: 3616 11988




/ # v4l2-compliance -s -f -d /dev/video0 &
[1] 45 v4l2-compliance -s -f -d /dev/video0
/ # v4l2-compliance 1.21.0-4782, 32 bits, 32-bit time_t
v4l2-compliance SHA: f0c7e3d71eaf 2021-05-06 10:57:09

Compliance test for stm-dma2d device /dev/video0:

Driver Info:
Driver name : stm-dma2d
Card type : stm-dma2d
Bus info : platform:stm-dma2d
Driver version : 5.13.0
Capabilities : 0x84208000
Video Memory-to-Memory
Streaming
Extended Pix Format
Device Capabilities
Device Caps : 0x04208000
Video Memory-to-Memory
Streaming
Extended Pix Format

Required ioctls:
test VIDIOC_QUERYCAP: OK
test invalid ioctls: OK

Allow for multiple opens:
test second /dev/video0 open: OK
test VIDIOC_QUERYCAP: OK
test VIDIOC_G/S_PRIORITY: OK
test for unlimited opens: OK

Debug ioctls:
test VIDIOC_DBG_G/S_REGISTER: OK (Not Supported)
test VIDIOC_LOG_STATUS: OK (Not Supported)

Input ioctls:
test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK (Not Supported)
test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported)
test VIDIOC_ENUMAUDIO: OK (Not Supported)
test VIDIOC_G/S/ENUMINPUT: OK (Not Supported)
test VIDIOC_G/S_AUDIO: OK (Not Supported)
Inputs: 0 Audio Inputs: 0 Tuners: 0

Output ioctls:
test VIDIOC_G/S_MODULATOR: OK (Not Supported)
test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
test VIDIOC_ENUMAUDOUT: OK (Not Supported)
test VIDIOC_G/S/ENUMOUTPUT: OK (Not Supported)
test VIDIOC_G/S_AUDOUT: OK (Not Supported)
Outputs: 0 Audio Outputs: 0 Modulators: 0

Input/Output configuration ioctls:
test VIDIOC_ENUM/G/S/QUERY_STD: OK (Not Supported)
test VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: OK (Not Supported)
test VIDIOC_DV_TIMINGS_CAP: OK (Not Supported)
test VIDIOC_G/S_EDID: OK (Not Supported)

Control ioctls:
test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK
test VIDIOC_QUERYCTRL: OK
test VIDIOC_G/S_CTRL: OK
test VIDIOC_G/S/TRY_EXT_CTRLS: OK
test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK
test VIDIOC[G/S_JP 143.242702] stm-dma2d 4002b000.dma2d: Format not supported: , use the default.
EGCOMP: OK (Not Supported)
St[ndard Controls: 2 Private Contro s: 2

Fo m1t ioctls:
test VIDIOC_4NUM_FMT/FRAMESIZES/FRAMEINTERV3LS: O.0m
test VIDIOC_G5S_PARM: 3K (Not 555] stm-dma2d 4002b000.dma2d: Format not supported: , use the default.
Supported)
test VIDIOC_G_FBUF: OK (Not Supported)
143.280786] stm-dma2d 4002b000.dma2d: Format not supported: , use the default.
test VIDIOC_TRY_FMT: OK
[ 143.293759] stm-dma2d 4002b000.dma2d: Format not supported: , use the default.
test VIDIOC_S_FMT: OK
test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
test Cropping: OK (Not Supported)
test Composing: OK (Not Supported)
test Scaling: OK

Codec ioctls:
test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported)
test VIDIOC_G_ENC_INDEX: OK (Not Supported)
test VIDIOC_(TRY_)DECODER_CMD: OK (Not Supported)

Buffer ioctls:
test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK
test VIDIOC_EXPBUF: OK
test Requests: OK (Not Supported)

Test input 0:

Streaming ioctls:
test read/write: OK (Not Supported)
test blocking wait: OK
Video Capture: Captured 58 buffers
test MMAP (no poll): OK
Video Capture: Captured 58 buffers
test MMAP (select): OK
Video Capture: Captured 58 buffers
test MMAP (epoll): OK
test USERPTR (no poll): OK (Not Supported)
test USERPTR (select): OK (Not Supported)
test DMABUF: Cannot test, specify --expbuf-device

Stream using all formats:
Video Capture: Captured 8 buffers
BA24 (32-bit ARGB 8-8-8-8) 1x1 -> BA24 (32-bit ARGB 8-8-8-8) 1x1: OK
Video Capture: Captured 8 buffers
BA24 (32-bit ARGB 8-8-8-8) 1x1 -> RGB3 (24-bit RGB 8-8-8) 1x1: OK
Video Capture: Captured 8 buffers
BA24 (32-bit ARGB 8-8-8-8) 1x1 -> RGBP (16-bit RGB 5-6-5) 1x1: OK
Video Capture: Captured 8 buffers
BA24 (32-bit ARGB 8-8-8-8) 1x1 -> AR15 (16-bit ARGB 1-5-5-5) 1x1: OK
Video Capture: Captured 8 buffers
BA24 (32-bit ARGB 8-8-8-8) 1x1 -[ AR12 (16-bit ARGB 4-4-4-4) 1x1: OK
146.963829] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size -323584 failed
fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
BA24 (32-bit ARGB 8-8-8-8) 16383x6[535 -> BA 4 (32-bit ARGB 8-8-8 8) 11383x65545: FAIL
6.985202] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size -323584 failed
fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
BA24 (32[bit ARGB 8-8-8-8) 16383x65 35 1> RGB3 (24-bit RGB48-8-8) 16383x65535: FAI7
.008713] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size -323584 failed
fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
BA24 (32-bit ARG[ 8-8-8-8) 163 3x6 535 -> RGBP (16-bit RGB 5-6-5) 16383x65535: FAIL
147.033896] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size -323584 failed
fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
BA24 (32-[it ARGB 8- -8-8) 16383x65535 -> AR15 (16-bit ARGB -5-515) 16383x65535: FAIL
47.058256] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size -323584 failed
fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
BA24 (32-bit ARGB 8-8-8-8) 16383x65535 -> AR12 (16-bit ARGB 4-4-4-4) 16383x65535: FAIL
Video Capture: Captured 8 buffers
BA24 (32-bit ARGB 8-8-8-8) 240x320 -> BA24 (32-bit ARGB 8-8-8-8) 240x320: OK
Video Capture: Captured 8 buffers
BA24 (32-bit ARGB 8-8-8-8) 240x320 -> RGB3 (24-bit RGB 8-8-8) 240x320: OK
Video Capture: Captured 8 buffers
BA24 (32-bit ARGB 8-8-8-8) 240x320 -> RGBP (16-bit RGB 5-6-5) 240x320: OK
Video Capture: Captured 8 buffers
BA24 (32-bit ARGB 8-8-8-8) 240x320 -> AR15 (16-bit ARGB 1-5-5-5) 240x320: OK
Video Capture: Captured 8 buffers
BA24 (32-bit ARGB 8-8-8-8) 240x320 -> AR12 (16-bit ARGB 4-4-4-4) 240x320: OK
Video Capture: Captured 8 buffers
RGB3 (24-bit RGB 8-8-8) 1x1 -> BA24 (32-bit ARGB 8-8-8-8) 1x1: OK
Video Capture: Captured 8 buffers
RGB3 (24-bit RGB 8-8-8) 1x1 -> RGB3 (24-bit RGB 8-8-8) 1x1: OK
Video Capture: Captured 8 buffers
RGB3 (24-bit RGB 8-8-8) 1x1 -> RGBP (16-bit RGB 5-6-5) 1x1: OK
Video Capture: Captured 8 buffers
RGB3 (24-bit RGB 8-8-8) 1x1 -> AR15 (16-bit ARGB 1-5-5-5) 1x1: OK
Video Capture: Captured 8 buffers
RGB3 (24-bit RGB 8-8-8) 1x1 -> AR12 (16-bit ARGB 4-4-4-4) 1[1: K
148.406686] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size -1073983488 failed
fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
RGB3 (24-bit RGB 8-8-8) 1[383x 5 35 -1 BA24 (32-b4t ARGB 888-8-8) 16383x65.35: FAIL
28566] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size -1073983488 failed
fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
RGB3 (24-bit RGB 8-8-8) 16383x65535[-> RGB3 (24 bit RGB 8-8-8) 16383 65535: FAIL
148.453973] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size -1073983488 failed
fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
RGB3 (24-bit RGB 8-8-8) 16383x65535 [> RGBP (16-bit RGB 5-6-5) 16383x65535: FAIL
148.477828] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size -1073983488 failed
fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
RGB3 (24-bit RGB 8[8-8) 16383x 5535 -> AR 5 (16-bit ARGB 1-514-58 16383x65535: FAIL
.503495] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size -1073983488 failed
fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
RGB3 (24-bit RGB 8-8-8) 16383x65535 -> AR12 (16-bit ARGB 4-4-4-4) 16383x65535: FAIL
Video Capture: Captured 8 buffers
RGB3 (24-bit RGB 8-8-8) 240x320 -> BA24 (32-bit ARGB 8-8-8-8) 240x320: OK
Video Capture: Captured 8 buffers
RGB3 (24-bit RGB 8-8-8) 240x320 -> RGB3 (24-bit RGB 8-8-8) 240x320: OK
Video Capture: Captured 8 buffers
RGB3 (24-bit RGB 8-8-8) 240x320 -> RGBP (16-bit RGB 5-6-5) 240x320: OK
Video Capture: Captured 8 buffers
RGB3 (24-bit RGB 8-8-8) 240x320 -> AR15 (16-bit ARGB 1-5-5-5) 240x320: OK
Video Capture: Captured 8 buffers
RGB3 (24-bit RGB 8-8-8) 240x320 -> AR12 (16-bit ARGB 4-4-4-4) 240x320: OK
Video Capture: Captured 8 buffers
RGBP (16-bit RGB 5-6-5) 1x1 -> BA24 (32-bit ARGB 8-8-8-8) 1x1: OK
Video Capture: Captured 8 buffers
RGBP (16-bit RGB 5-6-5) 1x1 -> RGB3 (24-bit RGB 8-8-8) 1x1: OK
Video Capture: Captured 8 buffers
RGBP (16-bit RGB 5-6-5) 1x1 -> RGBP (16-bit RGB 5-6-5) 1x1: OK
Video Capture: Captured 8 buffers
RGBP (16-bit RGB 5-6-5) 1x1 -> AR15 (16-bit ARGB 1-5-5-5) 1x1: OK
Video Capture: Captured 8 buffers
149.725823] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
RGBP (16-bit RGB 5-6-5) 1[383x65535 -> BA24 (32-bi ARGB 8 8-8-8) 16383x65535: FAIL
49.746860] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
RGBP (16-bit RGB 5-[-5) 16383x65535 -> RGB3 (24-b t RGB 8-8-8) 16383x65535: FAIL
49.772555] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
RGBP (16-bit R[B 5-6-5 1638 x65535 -> RGBP1(16-bit 4GB 596-5. 16383x67535: FAIL
330] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
RGBP (16-bit RGB 5-6-5) 16383x65535 ->[AR15 (1 -bit AR B 1-5-5-5) 11383x65535: 9IL
.821301] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
RGBP (16-bit RGB 5-6-5) 16383x65535 -> AR12 (16-bit ARGB 4-4-4-4) 16383x65535: FAIL
Video Capture: Captured 8 buffers
RGBP (16-bit RGB 5-6-5) 240x320 -> BA24 (32-bit ARGB 8-8-8-8) 240x320: OK
Video Capture: Captured 8 buffers
RGBP (16-bit RGB 5-6-5) 240x320 -> RGB3 (24-bit RGB 8-8-8) 240x320: OK
Video Capture: Captured 8 buffers
RGBP (16-bit RGB 5-6-5) 240x320 -> RGBP (16-bit RGB 5-6-5) 240x320: OK
Video Capture: Captured 8 buffers
RGBP (16-bit RGB 5-6-5) 240x320 -> AR15 (16-bit ARGB 1-5-5-5) 240x320: OK
Video Capture: Captured 8 buffers
RGBP (16-bit RGB 5-6-5) 240x320 -> AR12 (16-bit ARGB 4-4-4-4) 240x320: OK
Video Capture: Captured 8 buffers
AR15 (16-bit ARGB 1-5-5-5) 1x1 -> BA24 (32-bit ARGB 8-8-8-8) 1x1: OK
Video Capture: Captured 8 buffers
AR15 (16-bit ARGB 1-5-5-5) 1x1 -> RGB3 (24-bit RGB 8-8-8) 1x1: OK
Video Capture: Captured 8 buffers
AR15 (16-bit ARGB 1-5-5-5) 1x1 -> RGBP (16-bit RGB 5-6-5) 1x1: OK
Video Capture: Captured 8 buffers
AR15 (16-bit ARGB 1-5-5-5) 1x1 -> AR15 (16-bit ARGB 1-5-5-5) 1x1: OK
Video Capture: Captured 8 buffers
AR15 (16-bit ARGB 1-5-5-5) 1x1 -[ AR1 (16-bit ARGB 4-4-4-4) 1x15 O0
.956666] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
AR15 (16-bit ARGB 1-[-5-5) 16 83x65535 -> BA24 (32-bit ARGB 8 8-8-8) 16383x15535: FAIL
50.977546] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
AR15 (16-bit AR[B 1-5-5-5) 16383x65535 -> R B3 ( 4-b1t RGB 8-8-8) 16383x65535:5FAIL
1.003061] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
AR15 (16-bit ARGB 1-5-5[5) 16383x65535 -> RGBP (16-bit RGB 5-6-5) 16383x65535: FAIL
151.027900] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
AR15 (16-bit ARGB 1-5[5-5) 16383x65535 -> AR15 (16- it ARGB 1-5-5-5) 16383x65 15: F5IL
1.053781] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
AR15 (16-bit ARGB 1-5-5-5) 16383x65535 -> AR12 (16-bit ARGB 4-4-4-4) 16383x65535: FAIL
Video Capture: Captured 8 buffers
AR15 (16-bit ARGB 1-5-5-5) 240x320 -> BA24 (32-bit ARGB 8-8-8-8) 240x320: OK
Video Capture: Captured 8 buffers
AR15 (16-bit ARGB 1-5-5-5) 240x320 -> RGB3 (24-bit RGB 8-8-8) 240x320: OK
Video Capture: Captured 8 buffers
AR15 (16-bit ARGB 1-5-5-5) 240x320 -> RGBP (16-bit RGB 5-6-5) 240x320: OK
Video Capture: Captured 8 buffers
AR15 (16-bit ARGB 1-5-5-5) 240x320 -> AR15 (16-bit ARGB 1-5-5-5) 240x320: OK
Video Capture: Captured 8 buffers
AR15 (16-bit ARGB 1-5-5-5) 240x320 -> AR12 (16-bit ARGB 4-4-4-4) 240x320: OK
Video Capture: Captured 8 buffers
AR12 (16-bit ARGB 4-4-4-4) 1x1 -> BA24 (32-bit ARGB 8-8-8-8) 1x1: OK
Video Capture: Captured 8 buffers
AR12 (16-bit ARGB 4-4-4-4) 1x1 -> RGB3 (24-bit RGB 8-8-8) 1x1: OK
Video Capture: Captured 8 buffers
AR12 (16-bit ARGB 4-4-4-4) 1x1 -> RGBP (16-bit RGB 5-6-5) 1x1: OK
Video Capture: Captured 8 buffers
AR12 (16-bit ARGB 4-4-4-4) 1x1 -> AR15 (16-bit ARGB 1-5-5-5) 1x1: OK
Video Capture: Captured 8 buffers
AR12 (16-bit ARGB 4-4-4-4) 1x1 -> AR12 (16-bit A[GB 4-4-4 4) 1x1: OK
152.187325] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
AR12 (16-[ t A GB 4-4-4-4) 16383x65135 -> BA24 (32-bit ARGB 8-8-8-8) 16383x6553552.21;31mFAIL0[0m
8867] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
AR12 (16-bit ARGB 4-4[4-4 16383x6553 -> RGB3 (24-bit RGB18-8-8) 16383x65535: FAIL
52.234355] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
AR12 (16-bit ARGB 4-4-4-4) 16383x65535 -> RGBP (16-bit RGB 5-6-5) 16383x65535: [ 1; 1mFAIL
152.258077] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
AR12 (16-bit ARGB 4-4-4[4) 16383x65535 -> R15 (16-bit ARGB 1-5-5-5) 16383x65535: FAIL
152.284054] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
AR12 (16-bit ARGB 4-4-4-4) 16383x65535 -> AR12 (16-bit ARGB 4-4-4-4) 16383x65535: FAIL
Video Capture: Captured 8 buffers
AR12 (16-bit ARGB 4-4-4-4) 240x320 -> BA24 (32-bit ARGB 8-8-8-8) 240x320: OK
Video Capture: Captured 8 buffers
AR12 (16-bit ARGB 4-4-4-4) 240x320 -> RGB3 (24-bit RGB 8-8-8) 240x320: OK
Video Capture: Captured 8 buffers
AR12 (16-bit ARGB 4-4-4-4) 240x320 -> RGBP (16-bit RGB 5-6-5) 240x320: OK
Video Capture: Captured 8 buffers
AR12 (16-bit ARGB 4-4-4-4) 240x320 -> AR15 (16-bit ARGB 1-5-5-5) 240x320: OK
Video Capture: Captured 8 buffers
AR12 (16-bit ARGB 4-4-4-4) 240x320 -> AR12 (16-bit ARGB 4-4-4-4) 240x320: OK
Total for stm-dma2d device /dev/video0: 127, Succeeded: 102, Failed: 25, Warnings: 0

[1] Done v4l2-compliance -s -f -d /dev/video0
*** BLURB HERE ***

Dillon Min (7):
media: admin-guide: add stm32-dma2d description
media: dt-bindings: media: add document for STM32 DMA2d bindings
clk: stm32: Fix ltdc's clock turn off by clk_disable_unused() after
kernel startup
ARM: dts: stm32: Enable DMA2D support on STM32F429 MCU
ARM: dts: stm32: Enable DMA2D on STM32F469-DISCO board
media: v4l2-mem2mem: add v4l2_m2m_get_unmapped_area for no-mmu
platform
media: stm32-dma2d: STM32 DMA2D driver

.../admin-guide/media/platform-cardlist.rst | 1 +
.../devicetree/bindings/media/st,stm32-dma2d.yaml | 71 ++
arch/arm/boot/dts/stm32f429.dtsi | 10 +
arch/arm/boot/dts/stm32f469-disco.dts | 4 +
drivers/clk/clk-stm32f4.c | 7 +-
drivers/media/platform/Kconfig | 9 +
drivers/media/platform/Makefile | 1 +
drivers/media/platform/stm32/Makefile | 2 +
drivers/media/platform/stm32/dma2d/dma2d-hw.c | 143 ++++
drivers/media/platform/stm32/dma2d/dma2d-regs.h | 114 +++
drivers/media/platform/stm32/dma2d/dma2d.c | 791 +++++++++++++++++++++
drivers/media/platform/stm32/dma2d/dma2d.h | 132 ++++
drivers/media/v4l2-core/v4l2-mem2mem.c | 20 +
include/media/v4l2-mem2mem.h | 4 +
14 files changed, 1305 insertions(+), 4 deletions(-)
create mode 100644 Documentation/devicetree/bindings/media/st,stm32-dma2d.yaml
create mode 100644 drivers/media/platform/stm32/dma2d/dma2d-hw.c
create mode 100644 drivers/media/platform/stm32/dma2d/dma2d-regs.h
create mode 100644 drivers/media/platform/stm32/dma2d/dma2d.c
create mode 100644 drivers/media/platform/stm32/dma2d/dma2d.h

--
2.7.4


2021-05-20 12:21:20

by Dillon Min

[permalink] [raw]
Subject: [PATCH 7/7] media: stm32-dma2d: STM32 DMA2D driver

From: Dillon Min <[email protected]>

This V4L2 subdev m2m driver enables Chrom-Art Accelerator unit
of STMicroelectronics STM32 SoC series.

Currently support r2m, m2m, m2m_pfc.

Signed-off-by: Dillon Min <[email protected]>
---
drivers/media/platform/Kconfig | 9 +
drivers/media/platform/Makefile | 1 +
drivers/media/platform/stm32/Makefile | 2 +
drivers/media/platform/stm32/dma2d/dma2d-hw.c | 143 +++++
drivers/media/platform/stm32/dma2d/dma2d-regs.h | 114 ++++
drivers/media/platform/stm32/dma2d/dma2d.c | 791 ++++++++++++++++++++++++
drivers/media/platform/stm32/dma2d/dma2d.h | 132 ++++
7 files changed, 1192 insertions(+)
create mode 100644 drivers/media/platform/stm32/dma2d/dma2d-hw.c
create mode 100644 drivers/media/platform/stm32/dma2d/dma2d-regs.h
create mode 100644 drivers/media/platform/stm32/dma2d/dma2d.c
create mode 100644 drivers/media/platform/stm32/dma2d/dma2d.h

diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index 157c924686e4..128f4420623f 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -244,6 +244,15 @@ config VIDEO_CODA
config VIDEO_IMX_VDOA
def_tristate VIDEO_CODA if SOC_IMX6Q || COMPILE_TEST

+config VIDEO_STM32_DMA2D
+ tristate "STM32 Chrom-Art Accelerator (DMA2D)"
+ depends on VIDEO_DEV && VIDEO_V4L2 && ARCH_STM32
+ select VIDEOBUF2_DMA_CONTIG
+ select V4L2_MEM2MEM_DEV
+ help
+ The STM32 DMA2D is a memory-to-memory engine for pixel conversion,
+ specialized DMA dedicated to image manipulation
+
config VIDEO_IMX_PXP
tristate "i.MX Pixel Pipeline (PXP)"
depends on VIDEO_DEV && VIDEO_V4L2 && (ARCH_MXC || COMPILE_TEST)
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index eedc14aafb32..93b2b319a2b2 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -69,6 +69,7 @@ obj-$(CONFIG_VIDEO_ATMEL_ISC) += atmel/
obj-$(CONFIG_VIDEO_ATMEL_ISI) += atmel/

obj-$(CONFIG_VIDEO_STM32_DCMI) += stm32/
+obj-$(CONFIG_VIDEO_STM32_DMA2D) += stm32/

obj-$(CONFIG_VIDEO_MEDIATEK_VPU) += mtk-vpu/

diff --git a/drivers/media/platform/stm32/Makefile b/drivers/media/platform/stm32/Makefile
index 48b36db2c2e2..896ef98a73ab 100644
--- a/drivers/media/platform/stm32/Makefile
+++ b/drivers/media/platform/stm32/Makefile
@@ -1,2 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_VIDEO_STM32_DCMI) += stm32-dcmi.o
+stm32-dma2d-objs := dma2d/dma2d.o dma2d/dma2d-hw.o
+obj-$(CONFIG_VIDEO_STM32_DMA2D) += stm32-dma2d.o
diff --git a/drivers/media/platform/stm32/dma2d/dma2d-hw.c b/drivers/media/platform/stm32/dma2d/dma2d-hw.c
new file mode 100644
index 000000000000..6fdbf5a21151
--- /dev/null
+++ b/drivers/media/platform/stm32/dma2d/dma2d-hw.c
@@ -0,0 +1,143 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * ST stm32 Chrom-Art - 2D Graphics Accelerator Driver
+ *
+ * Copyright (c) 2021 Dillon Min
+ * Dillon Min, <[email protected]>
+ *
+ * based on s5p-g2d
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * Kamil Debski, <[email protected]>
+ */
+
+#include <linux/io.h>
+
+#include "dma2d.h"
+#include "dma2d-regs.h"
+
+static inline u32 reg_read(void __iomem *base, u32 reg)
+{
+ return readl_relaxed(base + reg);
+}
+
+static inline void reg_write(void __iomem *base, u32 reg, u32 val)
+{
+ writel_relaxed(val, base + reg);
+}
+
+static inline void reg_set(void __iomem *base, u32 reg, u32 mask)
+{
+ reg_write(base, reg, reg_read(base, reg) | mask);
+}
+
+static inline void reg_clear(void __iomem *base, u32 reg, u32 mask)
+{
+ reg_write(base, reg, reg_read(base, reg) & ~mask);
+}
+
+static inline void reg_update_bits(void __iomem *base, u32 reg, u32 mask,
+ u32 val)
+{
+ reg_write(base, reg, (reg_read(base, reg) & ~mask) | val);
+}
+
+void dma2d_start(struct dma2d_dev *d)
+{
+ reg_update_bits(d->regs, DMA2D_CR_REG, CR_START, CR_START);
+}
+
+u32 dma2d_get_int(struct dma2d_dev *d)
+{
+ return reg_read(d->regs, DMA2D_ISR_REG);
+}
+
+void dma2d_clear_int(struct dma2d_dev *d)
+{
+ u32 isr_val = reg_read(d->regs, DMA2D_ISR_REG);
+
+ reg_write(d->regs, DMA2D_IFCR_REG, isr_val & 0x003f);
+}
+
+void dma2d_config_common(struct dma2d_dev *d, enum dma2d_op_mode op_mode,
+ u16 width, u16 height)
+{
+ reg_update_bits(d->regs, DMA2D_CR_REG, CR_MODE_MASK,
+ op_mode << CR_MODE_SHIFT);
+
+ reg_write(d->regs, DMA2D_NLR_REG, (width << 16) | height);
+}
+
+void dma2d_config_out(struct dma2d_dev *d, struct dma2d_frame *frm,
+ dma_addr_t o_addr)
+{
+ reg_update_bits(d->regs, DMA2D_CR_REG, CR_CEIE, CR_CEIE);
+ reg_update_bits(d->regs, DMA2D_CR_REG, CR_CTCIE, CR_CTCIE);
+ reg_update_bits(d->regs, DMA2D_CR_REG, CR_CAEIE, CR_CAEIE);
+ reg_update_bits(d->regs, DMA2D_CR_REG, CR_TCIE, CR_TCIE);
+ reg_update_bits(d->regs, DMA2D_CR_REG, CR_TEIE, CR_TEIE);
+
+ if (frm->fmt->cmode >= CM_MODE_ARGB8888 &&
+ frm->fmt->cmode <= CM_MODE_ARGB4444)
+ reg_update_bits(d->regs, DMA2D_OPFCCR_REG, OPFCCR_CM_MASK,
+ frm->fmt->cmode);
+
+ reg_write(d->regs, DMA2D_OMAR_REG, o_addr);
+
+ reg_write(d->regs, DMA2D_OCOLR_REG,
+ (frm->a_rgb[3] << 24) |
+ (frm->a_rgb[2] << 16) |
+ (frm->a_rgb[1] << 8) |
+ frm->a_rgb[0]);
+
+ reg_update_bits(d->regs, DMA2D_OOR_REG, OOR_LO_MASK,
+ frm->line_ofs & 0x3fff);
+}
+
+void dma2d_config_fg(struct dma2d_dev *d, struct dma2d_frame *frm,
+ dma_addr_t f_addr)
+{
+ reg_write(d->regs, DMA2D_FGMAR_REG, f_addr);
+ reg_update_bits(d->regs, DMA2D_FGOR_REG, FGOR_LO_MASK,
+ frm->line_ofs);
+
+ if (frm->fmt->cmode >= CM_MODE_ARGB8888 &&
+ frm->fmt->cmode <= CM_MODE_A4)
+ reg_update_bits(d->regs, DMA2D_FGPFCCR_REG, FGPFCCR_CM_MASK,
+ frm->fmt->cmode);
+
+ reg_update_bits(d->regs, DMA2D_FGPFCCR_REG, FGPFCCR_AM_MASK,
+ (frm->a_mode << 16) & 0x03);
+
+ reg_update_bits(d->regs, DMA2D_FGPFCCR_REG, FGPFCCR_ALPHA_MASK,
+ frm->a_rgb[3] << 24);
+
+ reg_write(d->regs, DMA2D_FGCOLR_REG,
+ (frm->a_rgb[2] << 16) |
+ (frm->a_rgb[1] << 8) |
+ frm->a_rgb[0]);
+}
+
+void dma2d_config_bg(struct dma2d_dev *d, struct dma2d_frame *frm,
+ dma_addr_t b_addr)
+{
+ reg_write(d->regs, DMA2D_BGMAR_REG, b_addr);
+ reg_update_bits(d->regs, DMA2D_BGOR_REG, BGOR_LO_MASK,
+ frm->line_ofs);
+
+ if (frm->fmt->cmode >= CM_MODE_ARGB8888 &&
+ frm->fmt->cmode <= CM_MODE_A4)
+ reg_update_bits(d->regs, DMA2D_BGPFCCR_REG, BGPFCCR_CM_MASK,
+ frm->fmt->cmode);
+
+ reg_update_bits(d->regs, DMA2D_BGPFCCR_REG, BGPFCCR_AM_MASK,
+ (frm->a_mode << 16) & 0x03);
+
+ reg_update_bits(d->regs, DMA2D_BGPFCCR_REG, BGPFCCR_ALPHA_MASK,
+ frm->a_rgb[3] << 24);
+
+ reg_write(d->regs, DMA2D_BGCOLR_REG,
+ (frm->a_rgb[2] << 16) |
+ (frm->a_rgb[1] << 8) |
+ frm->a_rgb[0]);
+}
diff --git a/drivers/media/platform/stm32/dma2d/dma2d-regs.h b/drivers/media/platform/stm32/dma2d/dma2d-regs.h
new file mode 100644
index 000000000000..6e375bcb1281
--- /dev/null
+++ b/drivers/media/platform/stm32/dma2d/dma2d-regs.h
@@ -0,0 +1,114 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * ST stm32 Chrom-Art - 2D Graphics Accelerator Driver
+ *
+ * Copyright (c) 2021 Dillon Min
+ * Dillon Min, <[email protected]>
+ *
+ * based on s5p-g2d
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * Kamil Debski, <[email protected]>
+ */
+
+#ifndef __DMA2D_REGS_H__
+#define __DMA2D_REGS_H__
+
+#define DMA2D_CR_REG 0x0000
+#define CR_MODE_MASK GENMASK(17, 16)
+#define CR_MODE_SHIFT 16
+#define CR_M2M 0x0000
+#define CR_M2M_PFC BIT(16)
+#define CR_M2M_BLEND BIT(17)
+#define CR_R2M (BIT(17) | BIT(16))
+#define CR_CEIE BIT(13)
+#define CR_CTCIE BIT(12)
+#define CR_CAEIE BIT(11)
+#define CR_TWIE BIT(10)
+#define CR_TCIE BIT(9)
+#define CR_TEIE BIT(8)
+#define CR_ABORT BIT(2)
+#define CR_SUSP BIT(1)
+#define CR_START BIT(0)
+
+#define DMA2D_ISR_REG 0x0004
+#define ISR_CEIF BIT(5)
+#define ISR_CTCIF BIT(4)
+#define ISR_CAEIF BIT(3)
+#define ISR_TWIF BIT(2)
+#define ISR_TCIF BIT(1)
+#define ISR_TEIF BIT(0)
+
+#define DMA2D_IFCR_REG 0x0008
+#define IFCR_CCEIF BIT(5)
+#define IFCR_CCTCIF BIT(4)
+#define IFCR_CAECIF BIT(3)
+#define IFCR_CTWIF BIT(2)
+#define IFCR_CTCIF BIT(1)
+#define IFCR_CTEIF BIT(0)
+
+#define DMA2D_FGMAR_REG 0x000c
+#define DMA2D_FGOR_REG 0x0010
+#define FGOR_LO_MASK GENMASK(13, 0)
+
+#define DMA2D_BGMAR_REG 0x0014
+#define DMA2D_BGOR_REG 0x0018
+#define BGOR_LO_MASK GENMASK(13, 0)
+
+#define DMA2D_FGPFCCR_REG 0x001c
+#define FGPFCCR_ALPHA_MASK GENMASK(31, 24)
+#define FGPFCCR_AM_MASK GENMASK(17, 16)
+#define FGPFCCR_CS_MASK GENMASK(15, 8)
+#define FGPFCCR_START BIT(5)
+#define FGPFCCR_CCM_RGB888 BIT(4)
+#define FGPFCCR_CM_MASK GENMASK(3, 0)
+
+#define DMA2D_FGCOLR_REG 0x0020
+#define FGCOLR_REG_MASK GENMASK(23, 16)
+#define FGCOLR_GREEN_MASK GENMASK(15, 8)
+#define FGCOLR_BLUE_MASK GENMASK(7, 0)
+
+#define DMA2D_BGPFCCR_REG 0x0024
+#define BGPFCCR_ALPHA_MASK GENMASK(31, 24)
+#define BGPFCCR_AM_MASK GENMASK(17, 16)
+#define BGPFCCR_CS_MASK GENMASK(15, 8)
+#define BGPFCCR_START BIT(5)
+#define BGPFCCR_CCM_RGB888 BIT(4)
+#define BGPFCCR_CM_MASK GENMASK(3, 0)
+
+#define DMA2D_BGCOLR_REG 0x0028
+#define BGCOLR_REG_MASK GENMASK(23, 16)
+#define BGCOLR_GREEN_MASK GENMASK(15, 8)
+#define BGCOLR_BLUE_MASK GENMASK(7, 0)
+
+#define DMA2D_OPFCCR_REG 0x0034
+#define OPFCCR_CM_MASK GENMASK(2, 0)
+
+#define DMA2D_OCOLR_REG 0x0038
+#define OCOLR_ALPHA_MASK GENMASK(31, 24)
+#define OCOLR_RED_MASK GENMASK(23, 16)
+#define OCOLR_GREEN_MASK GENMASK(15, 8)
+#define OCOLR_BLUE_MASK GENMASK(7, 0)
+
+#define DMA2D_OMAR_REG 0x003c
+
+#define DMA2D_OOR_REG 0x0040
+#define OOR_LO_MASK GENMASK(13, 0)
+
+#define DMA2D_NLR_REG 0x0044
+#define NLR_PL_MASK GENMASK(29, 16)
+#define NLR_NL_MASK GENMASK(15, 0)
+
+
+/* Hardware limits */
+#define MAX_WIDTH 0x3fff
+#define MAX_HEIGHT 0xffff
+
+#define DEFAULT_WIDTH 240
+#define DEFAULT_HEIGHT 320
+#define DEFAULT_SIZE 307200
+
+#define CM_MODE_ARGB8888 0x00
+#define CM_MODE_ARGB4444 0x04
+#define CM_MODE_A4 0x0a
+#endif /* __DMA2D_REGS_H__ */
diff --git a/drivers/media/platform/stm32/dma2d/dma2d.c b/drivers/media/platform/stm32/dma2d/dma2d.c
new file mode 100644
index 000000000000..88f8333a174a
--- /dev/null
+++ b/drivers/media/platform/stm32/dma2d/dma2d.c
@@ -0,0 +1,791 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * STM32 DMA2D - 2D Graphics Accelerator Driver
+ *
+ * Copyright (c) 2021 Dillon Min
+ * Dillon Min, <[email protected]>
+ *
+ * based on s5p-g2d
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * Kamil Debski, <[email protected]>
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/timer.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/of.h>
+
+#include <linux/platform_device.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-event.h>
+#include <media/videobuf2-v4l2.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "dma2d.h"
+#include "dma2d-regs.h"
+
+#define fh2ctx(__fh) container_of(__fh, struct dma2d_ctx, fh)
+
+static struct dma2d_fmt formats[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_ARGB32,
+ .cmode = DMA2D_CMODE_ARGB8888,
+ .depth = 32,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_RGB24,
+ .cmode = DMA2D_CMODE_RGB888,
+ .depth = 24,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .cmode = DMA2D_CMODE_RGB565,
+ .depth = 16,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_ARGB555,
+ .cmode = DMA2D_CMODE_ARGB1555,
+ .depth = 16,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_ARGB444,
+ .cmode = DMA2D_CMODE_ARGB4444,
+ .depth = 16,
+ },
+};
+#define NUM_FORMATS ARRAY_SIZE(formats)
+
+static struct dma2d_frame def_frame = {
+ .width = DEFAULT_WIDTH,
+ .height = DEFAULT_HEIGHT,
+ .line_ofs = 0,
+ .a_rgb = {0x00, 0x00, 0x00, 0xff},
+ .a_mode = DMA2D_ALPHA_MODE_NO_MODIF,
+ .fmt = &formats[0],
+ .size = DEFAULT_SIZE,
+};
+
+static struct dma2d_fmt *find_fmt(int pixelformat)
+{
+ unsigned int i;
+
+ for (i = 0; i < NUM_FORMATS; i++) {
+ if (formats[i].fourcc == pixelformat)
+ return &formats[i];
+ }
+
+ return NULL;
+}
+
+static struct dma2d_frame *get_frame(struct dma2d_ctx *ctx,
+ enum v4l2_buf_type type)
+{
+ switch (type) {
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ return &ctx->fg;
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ return &ctx->out;
+ default:
+ return ERR_PTR(-EINVAL);
+ }
+}
+
+static int dma2d_queue_setup(struct vb2_queue *vq,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], struct device *alloc_devs[])
+{
+ struct dma2d_ctx *ctx = vb2_get_drv_priv(vq);
+ struct dma2d_frame *f = get_frame(ctx, vq->type);
+
+ if (IS_ERR(f))
+ return PTR_ERR(f);
+
+ if (*nplanes)
+ return sizes[0] < f->size ? -EINVAL : 0;
+
+ sizes[0] = f->size;
+ *nplanes = 1;
+
+ if (*nbuffers == 0)
+ *nbuffers = 1;
+
+ return 0;
+}
+
+static int dma2d_buf_out_validate(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+
+ if (vbuf->field == V4L2_FIELD_ANY)
+ vbuf->field = V4L2_FIELD_NONE;
+ if (vbuf->field != V4L2_FIELD_NONE)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int dma2d_buf_prepare(struct vb2_buffer *vb)
+{
+ struct dma2d_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct dma2d_frame *f = get_frame(ctx, vb->vb2_queue->type);
+
+ if (IS_ERR(f))
+ return PTR_ERR(f);
+
+ if (vb2_plane_size(vb, 0) < f->size)
+ return -EINVAL;
+
+ vb2_set_plane_payload(vb, 0, f->size);
+
+ return 0;
+}
+
+static void dma2d_buf_queue(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct dma2d_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+
+ v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
+}
+
+static int dma2d_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ struct dma2d_ctx *ctx = vb2_get_drv_priv(q);
+ struct dma2d_frame *f = get_frame(ctx, q->type);
+
+ if (IS_ERR(f))
+ return -EINVAL;
+
+ f->sequence = 0;
+ return 0;
+}
+
+static void dma2d_stop_streaming(struct vb2_queue *q)
+{
+ struct dma2d_ctx *ctx = vb2_get_drv_priv(q);
+ struct vb2_v4l2_buffer *vbuf;
+
+ for (;;) {
+ if (V4L2_TYPE_IS_OUTPUT(q->type))
+ vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
+ else
+ vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
+ if (!vbuf)
+ return;
+ v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
+ }
+}
+
+static const struct vb2_ops dma2d_qops = {
+ .queue_setup = dma2d_queue_setup,
+ .buf_out_validate = dma2d_buf_out_validate,
+ .buf_prepare = dma2d_buf_prepare,
+ .buf_queue = dma2d_buf_queue,
+ .start_streaming = dma2d_start_streaming,
+ .stop_streaming = dma2d_stop_streaming,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+};
+
+static int queue_init(void *priv, struct vb2_queue *src_vq,
+ struct vb2_queue *dst_vq)
+{
+ struct dma2d_ctx *ctx = priv;
+ int ret;
+
+ src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+ src_vq->drv_priv = ctx;
+ src_vq->ops = &dma2d_qops;
+ src_vq->mem_ops = &vb2_dma_contig_memops;
+ src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+ src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ src_vq->lock = &ctx->dev->mutex;
+ src_vq->dev = ctx->dev->v4l2_dev.dev;
+
+ ret = vb2_queue_init(src_vq);
+ if (ret)
+ return ret;
+
+ dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+ dst_vq->drv_priv = ctx;
+ dst_vq->ops = &dma2d_qops;
+ dst_vq->mem_ops = &vb2_dma_contig_memops;
+ dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+ dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ dst_vq->lock = &ctx->dev->mutex;
+ dst_vq->dev = ctx->dev->v4l2_dev.dev;
+
+ return vb2_queue_init(dst_vq);
+}
+
+#define V4L2_CID_DMA2D_R2M_COLOR (V4L2_CID_USER_BASE | 0x1200)
+#define V4L2_CID_DMA2D_R2M_MODE (V4L2_CID_USER_BASE | 0x1201)
+static int dma2d_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct dma2d_frame *frm;
+ struct dma2d_ctx *ctx = container_of(ctrl->handler, struct dma2d_ctx,
+ ctrl_handler);
+ unsigned long flags;
+
+ spin_lock_irqsave(&ctx->dev->ctrl_lock, flags);
+ switch (ctrl->id) {
+ case V4L2_CID_ALPHA_COMPONENT:
+ /* set the background alpha value*/
+ ctx->alpha_component = (u8) ctrl->val;
+ break;
+ case V4L2_CID_DMA2D_R2M_COLOR:
+ frm = get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ frm->a_rgb[3] = (ctrl->val >> 24) & 0xff;
+ frm->a_rgb[2] = (ctrl->val >> 16) & 0xff;
+ frm->a_rgb[1] = (ctrl->val >> 8) & 0xff;
+ frm->a_rgb[0] = ctrl->val & 0xff;
+ break;
+ case V4L2_CID_DMA2D_R2M_MODE:
+ if (ctrl->val)
+ ctx->op_mode = DMA2D_MODE_R2M;
+ break;
+ default:
+ v4l2_err(&ctx->dev->v4l2_dev, "Invalid control\n");
+ spin_unlock_irqrestore(&ctx->dev->ctrl_lock, flags);
+ return -EINVAL;
+
+ }
+ spin_unlock_irqrestore(&ctx->dev->ctrl_lock, flags);
+
+ return 0;
+}
+
+static const struct v4l2_ctrl_ops dma2d_ctrl_ops = {
+ .s_ctrl = dma2d_s_ctrl,
+};
+
+static const struct v4l2_ctrl_config dma2d_r2m_control[] = {
+ {
+ .ops = &dma2d_ctrl_ops,
+ .id = V4L2_CID_DMA2D_R2M_COLOR,
+ .name = "R2M Alpha/Color Value",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .min = 0xffffffff80000000ULL,
+ .max = 0x7fffffff,
+ .def = 0,
+ .step = 1,
+ },
+ {
+ .ops = &dma2d_ctrl_ops,
+ .id = V4L2_CID_DMA2D_R2M_MODE,
+ .name = "Set to r2m mode",
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .min = 0,
+ .max = 1,
+ .def = 0,
+ .step = 1,
+ }
+};
+
+static int dma2d_setup_ctrls(struct dma2d_ctx *ctx)
+{
+ v4l2_ctrl_handler_init(&ctx->ctrl_handler, 3);
+
+ v4l2_ctrl_new_std(&ctx->ctrl_handler, &dma2d_ctrl_ops,
+ V4L2_CID_ALPHA_COMPONENT, 0, 255, 1, 255);
+ v4l2_ctrl_new_custom(&ctx->ctrl_handler, &dma2d_r2m_control[0], NULL);
+ v4l2_ctrl_new_custom(&ctx->ctrl_handler, &dma2d_r2m_control[1], NULL);
+
+ return 0;
+}
+
+static int dma2d_open(struct file *file)
+{
+ struct dma2d_dev *dev = video_drvdata(file);
+ struct dma2d_ctx *ctx = NULL;
+ int ret = 0;
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+ ctx->dev = dev;
+ /* Set default formats */
+ ctx->fg = def_frame;
+ ctx->bg = def_frame;
+ ctx->out = def_frame;
+ ctx->op_mode = DMA2D_MODE_M2M_FPC;
+ ctx->colorspace = V4L2_COLORSPACE_REC709;
+ ctx->alpha_component = 0x00;
+ if (mutex_lock_interruptible(&dev->mutex)) {
+ kfree(ctx);
+ return -ERESTARTSYS;
+ }
+
+ ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init);
+ if (IS_ERR(ctx->fh.m2m_ctx)) {
+ ret = PTR_ERR(ctx->fh.m2m_ctx);
+ mutex_unlock(&dev->mutex);
+ kfree(ctx);
+ return ret;
+ }
+
+ v4l2_fh_init(&ctx->fh, video_devdata(file));
+ file->private_data = &ctx->fh;
+ v4l2_fh_add(&ctx->fh);
+
+ dma2d_setup_ctrls(ctx);
+
+ /* Write the default values to the ctx struct */
+ v4l2_ctrl_handler_setup(&ctx->ctrl_handler);
+
+ ctx->fh.ctrl_handler = &ctx->ctrl_handler;
+ mutex_unlock(&dev->mutex);
+
+ return 0;
+}
+
+static int dma2d_release(struct file *file)
+{
+ struct dma2d_dev *dev = video_drvdata(file);
+ struct dma2d_ctx *ctx = fh2ctx(file->private_data);
+
+ v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
+ mutex_lock(&dev->mutex);
+ v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
+ mutex_unlock(&dev->mutex);
+ kfree(ctx);
+
+ return 0;
+}
+
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ strscpy(cap->driver, DMA2D_NAME, sizeof(cap->driver));
+ strscpy(cap->card, DMA2D_NAME, sizeof(cap->card));
+ strscpy(cap->bus_info, BUS_INFO, sizeof(cap->bus_info));
+
+ return 0;
+}
+
+static int vidioc_enum_fmt(struct file *file, void *prv, struct v4l2_fmtdesc *f)
+{
+ if (f->index >= NUM_FORMATS)
+ return -EINVAL;
+
+ f->pixelformat = formats[f->index].fourcc;
+ return 0;
+}
+
+static int vidioc_g_fmt(struct file *file, void *prv, struct v4l2_format *f)
+{
+ struct dma2d_ctx *ctx = prv;
+ struct vb2_queue *vq;
+ struct dma2d_frame *frm;
+
+ vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
+ if (!vq)
+ return -EINVAL;
+
+ frm = get_frame(ctx, f->type);
+ if (IS_ERR(frm))
+ return PTR_ERR(frm);
+
+ f->fmt.pix.width = frm->width;
+ f->fmt.pix.height = frm->height;
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ f->fmt.pix.pixelformat = frm->fmt->fourcc;
+ f->fmt.pix.bytesperline = (frm->width * frm->fmt->depth) >> 3;
+ f->fmt.pix.sizeimage = frm->size;
+ f->fmt.pix.colorspace = ctx->colorspace;
+ f->fmt.pix.xfer_func = ctx->xfer_func;
+ f->fmt.pix.ycbcr_enc = ctx->ycbcr_enc;
+ f->fmt.pix.quantization = ctx->quant;
+
+ return 0;
+}
+
+static int vidioc_try_fmt(struct file *file, void *prv, struct v4l2_format *f)
+{
+ struct dma2d_ctx *ctx = prv;
+ struct dma2d_dev *dev = ctx->dev;
+ struct dma2d_fmt *fmt;
+ enum v4l2_field *field;
+ u32 fourcc = f->fmt.pix.pixelformat;
+
+ fmt = find_fmt(fourcc);
+ if (!fmt) {
+ v4l2_warn(&dev->v4l2_dev,
+ "Format not supported: %c%c%c%c, use the default.\n",
+ (fourcc & 0xff), (fourcc >> 8) & 0xff,
+ (fourcc >> 16) & 0xff, (fourcc >> 24) & 0xff);
+ f->fmt.pix.pixelformat = formats[0].fourcc;
+ fmt = find_fmt(f->fmt.pix.pixelformat);
+ }
+
+ field = &f->fmt.pix.field;
+ if (*field == V4L2_FIELD_ANY)
+ *field = V4L2_FIELD_NONE;
+ else if (*field != V4L2_FIELD_NONE)
+ return -EINVAL;
+
+ if (f->fmt.pix.width > MAX_WIDTH)
+ f->fmt.pix.width = MAX_WIDTH;
+ if (f->fmt.pix.height > MAX_HEIGHT)
+ f->fmt.pix.height = MAX_HEIGHT;
+
+ if (f->fmt.pix.width < 1)
+ f->fmt.pix.width = 1;
+ if (f->fmt.pix.height < 1)
+ f->fmt.pix.height = 1;
+
+ if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT && !f->fmt.pix.colorspace)
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_REC709;
+ else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ f->fmt.pix.colorspace = ctx->colorspace;
+ f->fmt.pix.xfer_func = ctx->xfer_func;
+ f->fmt.pix.ycbcr_enc = ctx->ycbcr_enc;
+ f->fmt.pix.quantization = ctx->quant;
+ }
+ f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3;
+ f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
+
+ return 0;
+}
+
+static int vidioc_s_fmt(struct file *file, void *prv, struct v4l2_format *f)
+{
+ struct dma2d_ctx *ctx = prv;
+ struct dma2d_dev *dev = ctx->dev;
+ struct vb2_queue *vq;
+ struct dma2d_frame *frm;
+ struct dma2d_fmt *fmt;
+ int ret = 0;
+
+ /* Adjust all values accordingly to the hardware capabilities
+ * and chosen format.
+ */
+ ret = vidioc_try_fmt(file, prv, f);
+ if (ret)
+ return ret;
+
+ vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
+ if (vb2_is_busy(vq)) {
+ v4l2_err(&dev->v4l2_dev, "queue (%d) bust\n", f->type);
+ return -EBUSY;
+ }
+
+ frm = get_frame(ctx, f->type);
+ if (IS_ERR(frm))
+ return PTR_ERR(frm);
+
+ fmt = find_fmt(f->fmt.pix.pixelformat);
+ if (!fmt)
+ return -EINVAL;
+
+ if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ ctx->colorspace = f->fmt.pix.colorspace;
+ ctx->xfer_func = f->fmt.pix.xfer_func;
+ ctx->ycbcr_enc = f->fmt.pix.ycbcr_enc;
+ ctx->quant = f->fmt.pix.quantization;
+ }
+
+ frm->width = f->fmt.pix.width;
+ frm->height = f->fmt.pix.height;
+ frm->size = f->fmt.pix.sizeimage;
+ /* Reset crop settings */
+ frm->o_width = 0;
+ frm->o_height = 0;
+ frm->c_width = frm->width;
+ frm->c_height = frm->height;
+ frm->right = frm->width;
+ frm->bottom = frm->height;
+ frm->fmt = fmt;
+ frm->line_ofs = 0;
+ if (f->fmt.win.global_alpha != 0) {
+ frm->a_rgb[3] = f->fmt.win.global_alpha;
+ frm->a_mode = DMA2D_ALPHA_MODE_REPLACE;
+ }
+
+ return 0;
+}
+
+static void device_run(void *prv)
+{
+ struct dma2d_ctx *ctx = prv;
+ struct dma2d_dev *dev = ctx->dev;
+ struct dma2d_frame *frm_out, *frm_cap;
+ struct vb2_v4l2_buffer *src, *dst;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->ctrl_lock, flags);
+ dev->curr = ctx;
+
+ src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
+ dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
+ if (dst == NULL || src == NULL)
+ goto end;
+
+ frm_cap = get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ frm_out = get_frame(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ if (!frm_cap || !frm_out)
+ goto end;
+
+ src->sequence = frm_out->sequence++;
+ dst->sequence = frm_cap->sequence++;
+ v4l2_m2m_buf_copy_metadata(src, dst, true);
+
+ clk_enable(dev->gate);
+
+ dma2d_config_fg(dev, frm_out,
+ vb2_dma_contig_plane_dma_addr(&src->vb2_buf, 0));
+
+ /* TODO: add M2M_BLEND handler here */
+
+ if (ctx->op_mode != DMA2D_MODE_R2M) {
+ if (frm_out->fmt->fourcc == frm_cap->fmt->fourcc)
+ ctx->op_mode = DMA2D_MODE_M2M;
+ else
+ ctx->op_mode = DMA2D_MODE_M2M_FPC;
+ }
+
+ dma2d_config_out(dev, frm_cap,
+ vb2_dma_contig_plane_dma_addr(&dst->vb2_buf, 0));
+ dma2d_config_common(dev, ctx->op_mode, frm_cap->width, frm_cap->height);
+
+ dma2d_start(dev);
+end:
+ spin_unlock_irqrestore(&dev->ctrl_lock, flags);
+}
+
+static irqreturn_t dma2d_isr(int irq, void *prv)
+{
+ struct dma2d_dev *dev = prv;
+ struct dma2d_ctx *ctx = dev->curr;
+ struct vb2_v4l2_buffer *src, *dst;
+ uint32_t s = dma2d_get_int(dev);
+
+ if (s & ISR_TCIF || s == 0) {
+ dma2d_clear_int(dev);
+ clk_disable(dev->gate);
+
+ WARN_ON(ctx == NULL);
+
+ src = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
+ dst = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
+
+ WARN_ON(dst == NULL);
+ WARN_ON(src == NULL);
+
+ v4l2_m2m_buf_done(src, VB2_BUF_STATE_DONE);
+ v4l2_m2m_buf_done(dst, VB2_BUF_STATE_DONE);
+ v4l2_m2m_job_finish(dev->m2m_dev, ctx->fh.m2m_ctx);
+
+ dev->curr = NULL;
+ } else {
+ dma2d_clear_int(dev);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static const struct v4l2_file_operations dma2d_fops = {
+ .owner = THIS_MODULE,
+ .open = dma2d_open,
+ .release = dma2d_release,
+ .poll = v4l2_m2m_fop_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = v4l2_m2m_fop_mmap,
+#ifndef CONFIG_MMU
+ .get_unmapped_area = v4l2_m2m_get_unmapped_area,
+#endif
+};
+
+static const struct v4l2_ioctl_ops dma2d_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt,
+
+ .vidioc_enum_fmt_vid_out = vidioc_enum_fmt,
+ .vidioc_g_fmt_vid_out = vidioc_g_fmt,
+ .vidioc_try_fmt_vid_out = vidioc_try_fmt,
+ .vidioc_s_fmt_vid_out = vidioc_s_fmt,
+
+ .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
+ .vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
+ .vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
+ .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
+ .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,
+ .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
+ .vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
+
+ .vidioc_streamon = v4l2_m2m_ioctl_streamon,
+ .vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
+
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static const struct video_device dma2d_videodev = {
+ .name = DMA2D_NAME,
+ .fops = &dma2d_fops,
+ .ioctl_ops = &dma2d_ioctl_ops,
+ .minor = -1,
+ .release = video_device_release,
+ .vfl_dir = VFL_DIR_M2M,
+};
+
+static const struct v4l2_m2m_ops dma2d_m2m_ops = {
+ .device_run = device_run,
+};
+
+static const struct of_device_id stm32_dma2d_match[];
+
+static int dma2d_probe(struct platform_device *pdev)
+{
+ struct dma2d_dev *dev;
+ struct video_device *vfd;
+ struct resource *res;
+ int ret = 0;
+
+ dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ spin_lock_init(&dev->ctrl_lock);
+ mutex_init(&dev->mutex);
+ atomic_set(&dev->num_inst, 0);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ dev->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(dev->regs))
+ return PTR_ERR(dev->regs);
+
+ dev->gate = clk_get(&pdev->dev, "dma2d");
+ if (IS_ERR(dev->gate)) {
+ dev_err(&pdev->dev, "failed to get dma2d clock gate\n");
+ ret = -ENXIO;
+ return ret;
+ }
+
+ ret = clk_prepare(dev->gate);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to prepare dma2d clock gate\n");
+ goto put_clk_gate;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "failed to find IRQ\n");
+ ret = -ENXIO;
+ goto unprep_clk_gate;
+ }
+
+ dev->irq = res->start;
+
+ ret = devm_request_irq(&pdev->dev, dev->irq, dma2d_isr,
+ 0, pdev->name, dev);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to install IRQ\n");
+ goto unprep_clk_gate;
+ }
+
+ ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
+ if (ret)
+ goto unprep_clk_gate;
+
+ vfd = video_device_alloc();
+ if (!vfd) {
+ v4l2_err(&dev->v4l2_dev, "Failed to allocate video device\n");
+ ret = -ENOMEM;
+ goto unreg_v4l2_dev;
+ }
+
+ *vfd = dma2d_videodev;
+ set_bit(V4L2_FL_QUIRK_INVERTED_CROP, &vfd->flags);
+ vfd->lock = &dev->mutex;
+ vfd->v4l2_dev = &dev->v4l2_dev;
+ vfd->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING;
+ ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0);
+ if (ret) {
+ v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
+ goto rel_vdev;
+ }
+
+ video_set_drvdata(vfd, dev);
+ dev->vfd = vfd;
+ v4l2_info(&dev->v4l2_dev, "device registered as /dev/video%d\n",
+ vfd->num);
+ platform_set_drvdata(pdev, dev);
+ dev->m2m_dev = v4l2_m2m_init(&dma2d_m2m_ops);
+ if (IS_ERR(dev->m2m_dev)) {
+ v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n");
+ ret = PTR_ERR(dev->m2m_dev);
+ goto unreg_video_dev;
+ }
+
+ v4l2_info(&dev->v4l2_dev, "stm32 dma2d initialized\n");
+ return 0;
+
+unreg_video_dev:
+ video_unregister_device(dev->vfd);
+rel_vdev:
+ video_device_release(vfd);
+unreg_v4l2_dev:
+ v4l2_device_unregister(&dev->v4l2_dev);
+unprep_clk_gate:
+ clk_unprepare(dev->gate);
+put_clk_gate:
+ clk_put(dev->gate);
+
+ return ret;
+}
+
+static int dma2d_remove(struct platform_device *pdev)
+{
+ struct dma2d_dev *dev = platform_get_drvdata(pdev);
+
+ v4l2_info(&dev->v4l2_dev, "Removing " DMA2D_NAME);
+ v4l2_m2m_release(dev->m2m_dev);
+ video_unregister_device(dev->vfd);
+ v4l2_device_unregister(&dev->v4l2_dev);
+ vb2_dma_contig_clear_max_seg_size(&pdev->dev);
+ clk_unprepare(dev->gate);
+ clk_put(dev->gate);
+
+ return 0;
+}
+
+static const struct of_device_id stm32_dma2d_match[] = {
+ {
+ .compatible = "st,stm32-dma2d",
+ .data = NULL,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, stm32_dma2d_match);
+
+static struct platform_driver dma2d_pdrv = {
+ .probe = dma2d_probe,
+ .remove = dma2d_remove,
+ .driver = {
+ .name = DMA2D_NAME,
+ .of_match_table = stm32_dma2d_match,
+ },
+};
+
+module_platform_driver(dma2d_pdrv);
+
+MODULE_AUTHOR("Dillon Min <[email protected]>");
+MODULE_DESCRIPTION("STM32 Chrom-Art Accelerator DMA2D driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/stm32/dma2d/dma2d.h b/drivers/media/platform/stm32/dma2d/dma2d.h
new file mode 100644
index 000000000000..1c64cd2f7587
--- /dev/null
+++ b/drivers/media/platform/stm32/dma2d/dma2d.h
@@ -0,0 +1,132 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * ST stm32 DMA2D - 2D Graphics Accelerator Driver
+ *
+ * Copyright (c) 2021 Dillon Min
+ * Dillon Min, <[email protected]>
+ *
+ * based on s5p-g2d
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * Kamil Debski, <[email protected]>
+ */
+
+#ifndef __DMA2D_H__
+#define __DMA2D_H__
+
+#include <linux/platform_device.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
+
+#define DMA2D_NAME "stm-dma2d"
+#define BUS_INFO "platform:stm-dma2d"
+enum dma2d_op_mode {
+ DMA2D_MODE_M2M,
+ DMA2D_MODE_M2M_FPC,
+ DMA2D_MODE_M2M_BLEND,
+ DMA2D_MODE_R2M
+};
+
+enum dma2d_cmode {
+ /* output pfc cmode from ARGB888 to ARGB4444 */
+ DMA2D_CMODE_ARGB8888,
+ DMA2D_CMODE_RGB888,
+ DMA2D_CMODE_RGB565,
+ DMA2D_CMODE_ARGB1555,
+ DMA2D_CMODE_ARGB4444,
+ /* bg or fg pfc cmode from L8 to A4 */
+ DMA2D_CMODE_L8,
+ DMA2D_CMODE_AL44,
+ DMA2D_CMODE_AL88,
+ DMA2D_CMODE_L4,
+ DMA2D_CMODE_A8,
+ DMA2D_CMODE_A4
+};
+
+enum dma2d_alpha_mode {
+ DMA2D_ALPHA_MODE_NO_MODIF,
+ DMA2D_ALPHA_MODE_REPLACE,
+ DMA2D_ALPHA_MODE_COMBINE
+};
+
+struct dma2d_fmt {
+ u32 fourcc;
+ int depth;
+ enum dma2d_cmode cmode;
+};
+
+struct dma2d_frame {
+ /* Original dimensions */
+ u32 width;
+ u32 height;
+ /* Crop size */
+ u32 c_width;
+ u32 c_height;
+ /* Offset */
+ u32 o_width;
+ u32 o_height;
+ u32 bottom;
+ u32 right;
+ u16 line_ofs;
+ /* Image format */
+ struct dma2d_fmt *fmt;
+ /* [0]: blue
+ * [1]: green
+ * [2]: red
+ * [3]: alpha
+ */
+ u8 a_rgb[4];
+ /*
+ * AM[1:0] of DMA2D_FGPFCCR
+ */
+ enum dma2d_alpha_mode a_mode;
+ u32 size;
+ unsigned int sequence;
+};
+
+struct dma2d_ctx {
+ struct v4l2_fh fh;
+ struct dma2d_dev *dev;
+ struct dma2d_frame fg;
+ struct dma2d_frame out;
+ struct dma2d_frame bg;
+ /* fb_buf always point to bg address */
+ struct v4l2_framebuffer fb_buf;
+ /*
+ * MODE[17:16] of DMA2D_CR
+ */
+ enum dma2d_op_mode op_mode;
+ u8 alpha_component;
+ struct v4l2_ctrl_handler ctrl_handler;
+ enum v4l2_colorspace colorspace;
+ enum v4l2_ycbcr_encoding ycbcr_enc;
+ enum v4l2_xfer_func xfer_func;
+ enum v4l2_quantization quant;
+};
+
+struct dma2d_dev {
+ struct v4l2_device v4l2_dev;
+ struct v4l2_m2m_dev *m2m_dev;
+ struct video_device *vfd;
+ struct mutex mutex;
+ spinlock_t ctrl_lock;
+ atomic_t num_inst;
+ void __iomem *regs;
+ struct clk *gate;
+ struct dma2d_ctx *curr;
+ int irq;
+};
+
+void dma2d_start(struct dma2d_dev *d);
+u32 dma2d_get_int(struct dma2d_dev *d);
+void dma2d_clear_int(struct dma2d_dev *d);
+void dma2d_config_out(struct dma2d_dev *d, struct dma2d_frame *frm,
+ dma_addr_t o_addr);
+void dma2d_config_fg(struct dma2d_dev *d, struct dma2d_frame *frm,
+ dma_addr_t f_addr);
+void dma2d_config_bg(struct dma2d_dev *d, struct dma2d_frame *frm,
+ dma_addr_t b_addr);
+void dma2d_config_common(struct dma2d_dev *d, enum dma2d_op_mode op_mode,
+ u16 width, u16 height);
+
+#endif /* __DMA2D_H__ */
--
2.7.4

2021-05-20 12:22:26

by Dillon Min

[permalink] [raw]
Subject: [PATCH 5/7] ARM: dts: stm32: Enable DMA2D on STM32F469-DISCO board

From: Dillon Min <[email protected]>

Enable DMA2D on STM32F469-DISCO board.

Signed-off-by: Dillon Min <[email protected]>
---
arch/arm/boot/dts/stm32f469-disco.dts | 4 ++++
1 file changed, 4 insertions(+)

diff --git a/arch/arm/boot/dts/stm32f469-disco.dts b/arch/arm/boot/dts/stm32f469-disco.dts
index 2e1b3bbbe4b5..9cafe456d0d5 100644
--- a/arch/arm/boot/dts/stm32f469-disco.dts
+++ b/arch/arm/boot/dts/stm32f469-disco.dts
@@ -134,6 +134,10 @@
clock-frequency = <8000000>;
};

+&dma2d {
+ status = "okay";
+};
+
&dsi {
#address-cells = <1>;
#size-cells = <0>;
--
2.7.4

2021-05-21 02:03:37

by Dillon Min

[permalink] [raw]
Subject: [PATCH 2/7] media: dt-bindings: media: add document for STM32 DMA2D bindings

From: Dillon Min <[email protected]>

This adds documentation of device tree bindings for the STM32 DMA2D

Signed-off-by: Dillon Min <[email protected]>
---
.../devicetree/bindings/media/st,stm32-dma2d.yaml | 71 ++++++++++++++++++++++
1 file changed, 71 insertions(+)
create mode 100644 Documentation/devicetree/bindings/media/st,stm32-dma2d.yaml

diff --git a/Documentation/devicetree/bindings/media/st,stm32-dma2d.yaml b/Documentation/devicetree/bindings/media/st,stm32-dma2d.yaml
new file mode 100644
index 000000000000..f97b4a246605
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/st,stm32-dma2d.yaml
@@ -0,0 +1,71 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/st,stm32-dma2d.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: STMicroelectronics STM32 Chrom-Art Accelerator DMA2D binding
+
+description:
+ Chrom-ART Accelerator(DMA2D), graphical hardware accelerator
+ enabling enhanced graphical user interface with minimum CPU load
+
+ It can perform the following operations.
+
+ - Filling a part or the whole of a destination image with a specific color.
+ - Copying a part or the whole of a source image into a part or the whole of
+ a destination image.
+ - Copying a part or the whole of a source image into a part or the whole of
+ a destination image with a pixel format conversion.
+ - Blending a part and/or two complete source images with different pixel
+ format and copy the result into a part or the whole of a destination image
+ with a different color format. (TODO)
+
+
+maintainers:
+ - Dillon Min <[email protected]>
+
+properties:
+ compatible:
+ const: st,stm32-dma2d
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ clocks:
+ maxItems: 1
+
+ clock-names:
+ items:
+ - const: dma2d
+
+ resets:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - clocks
+ - clock-names
+ - resets
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/stm32fx-clock.h>
+ #include <dt-bindings/mfd/stm32f4-rcc.h>
+ dma2d: dma2d@4002b000 {
+ compatible = "st,stm32-dma2d";
+ reg = <0x4002b000 0xc00>;
+ interrupts = <90>;
+ resets = <&rcc STM32F4_AHB1_RESET(DMA2D)>;
+ clocks = <&rcc 0 STM32F4_AHB1_CLOCK(DMA2D)>;
+ clock-names = "dma2d";
+ };
+
+...
--
2.7.4

2021-05-21 02:24:48

by Dillon Min

[permalink] [raw]
Subject: [PATCH 3/7] clk: stm32: Fix ltdc's clock turn off by clk_disable_unused() after kernel startup

From: Dillon Min <[email protected]>

stm32's clk driver register two ltdc gate clk to clk core by
clk_hw_register_gate() and clk_hw_register_composite()

first: 'stm32f429_gates[]', clk name is 'ltdc', which no user to use.
second: 'stm32f429_aux_clk[]', clk name is 'lcd-tft', used by ltdc driver

both of them point to the same offset of stm32's RCC register. after
kernel enter console, clk core turn off ltdc's clk as 'stm32f429_gates[]'
is no one to use. but, actually 'stm32f429_aux_clk[]' is in use.

Fixes: daf2d117cbca ("clk: stm32f4: Add lcd-tft clock")
Signed-off-by: Dillon Min <[email protected]>
Acked-by: Stephen Boyd <[email protected]>
Link: https://lore.kernel.org/linux-arm-kernel/[email protected]/
---

This patch was submitted in
https://lore.kernel.org/lkml/[email protected]/

drivers/clk/clk-stm32f4.c | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/drivers/clk/clk-stm32f4.c b/drivers/clk/clk-stm32f4.c
index 18117ce5ff85..b6ab8c3a7994 100644
--- a/drivers/clk/clk-stm32f4.c
+++ b/drivers/clk/clk-stm32f4.c
@@ -211,7 +211,6 @@ static const struct stm32f4_gate_data stm32f469_gates[] __initconst = {
{ STM32F4_RCC_APB2ENR, 20, "spi5", "apb2_div" },
{ STM32F4_RCC_APB2ENR, 21, "spi6", "apb2_div" },
{ STM32F4_RCC_APB2ENR, 22, "sai1", "apb2_div" },
- { STM32F4_RCC_APB2ENR, 26, "ltdc", "apb2_div" },
};

static const struct stm32f4_gate_data stm32f746_gates[] __initconst = {
@@ -557,13 +556,13 @@ static const struct clk_div_table post_divr_table[] = {

#define MAX_POST_DIV 3
static const struct stm32f4_pll_post_div_data post_div_data[MAX_POST_DIV] = {
- { CLK_I2SQ_PDIV, PLL_I2S, "plli2s-q-div", "plli2s-q",
+ { CLK_I2SQ_PDIV, PLL_VCO_I2S, "plli2s-q-div", "plli2s-q",
CLK_SET_RATE_PARENT, STM32F4_RCC_DCKCFGR, 0, 5, 0, NULL},

- { CLK_SAIQ_PDIV, PLL_SAI, "pllsai-q-div", "pllsai-q",
+ { CLK_SAIQ_PDIV, PLL_VCO_SAI, "pllsai-q-div", "pllsai-q",
CLK_SET_RATE_PARENT, STM32F4_RCC_DCKCFGR, 8, 5, 0, NULL },

- { NO_IDX, PLL_SAI, "pllsai-r-div", "pllsai-r", CLK_SET_RATE_PARENT,
+ { NO_IDX, PLL_VCO_SAI, "pllsai-r-div", "pllsai-r", CLK_SET_RATE_PARENT,
STM32F4_RCC_DCKCFGR, 16, 2, 0, post_divr_table },
};

--
2.7.4

2021-05-21 02:25:05

by Dillon Min

[permalink] [raw]
Subject: [PATCH 4/7] ARM: dts: stm32: Enable DMA2D support on STM32F429 MCU

From: Dillon Min <[email protected]>

Enable DMA2D on STM32F429 MCU.

Signed-off-by: Dillon Min <[email protected]>
---
arch/arm/boot/dts/stm32f429.dtsi | 10 ++++++++++
1 file changed, 10 insertions(+)

diff --git a/arch/arm/boot/dts/stm32f429.dtsi b/arch/arm/boot/dts/stm32f429.dtsi
index f6530d724d00..2cd1506027b3 100644
--- a/arch/arm/boot/dts/stm32f429.dtsi
+++ b/arch/arm/boot/dts/stm32f429.dtsi
@@ -799,6 +799,16 @@
status = "disabled";
};

+ dma2d: dma2d@4002b000 {
+ compatible = "st,stm32-dma2d";
+ reg = <0x4002b000 0xc00>;
+ interrupts = <90>;
+ resets = <&rcc STM32F4_AHB1_RESET(DMA2D)>;
+ clocks = <&rcc 0 STM32F4_AHB1_CLOCK(DMA2D)>;
+ clock-names = "dma2d";
+ status = "disabled";
+ };
+
rng: rng@50060800 {
compatible = "st,stm32-rng";
reg = <0x50060800 0x400>;
--
2.7.4

2021-05-21 02:25:06

by Dillon Min

[permalink] [raw]
Subject: [PATCH 6/7] media: v4l2-mem2mem: add v4l2_m2m_get_unmapped_area for no-mmu platform

From: Dillon Min <[email protected]>

For platforms without MMU the m2m provides a helper method
v4l2_m2m_get_unmapped_area(), The mmap() routines will call
this to get a proposed address for the mapping.

More detailed information about get_unmapped_area can be found in
Documentation/nommu-mmap.txt

Signed-off-by: Dillon Min <[email protected]>
---
drivers/media/v4l2-core/v4l2-mem2mem.c | 20 ++++++++++++++++++++
include/media/v4l2-mem2mem.h | 4 ++++
2 files changed, 24 insertions(+)

diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c
index e7f4bf5bc8dd..f82a18ecab2f 100644
--- a/drivers/media/v4l2-core/v4l2-mem2mem.c
+++ b/drivers/media/v4l2-core/v4l2-mem2mem.c
@@ -966,6 +966,26 @@ int v4l2_m2m_mmap(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
}
EXPORT_SYMBOL(v4l2_m2m_mmap);

+#ifndef CONFIG_MMU
+unsigned long v4l2_m2m_get_unmapped_area(struct file *file, unsigned long addr,
+ unsigned long len, unsigned long pgoff, unsigned long flags)
+{
+ struct v4l2_fh *fh = file->private_data;
+ unsigned long offset = pgoff << PAGE_SHIFT;
+ struct vb2_queue *vq;
+
+ if (offset < DST_QUEUE_OFF_BASE) {
+ vq = v4l2_m2m_get_src_vq(fh->m2m_ctx);
+ } else {
+ vq = v4l2_m2m_get_dst_vq(fh->m2m_ctx);
+ pgoff -= (DST_QUEUE_OFF_BASE >> PAGE_SHIFT);
+ }
+
+ return vb2_get_unmapped_area(vq, addr, len, pgoff, flags);
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_get_unmapped_area);
+#endif
+
#if defined(CONFIG_MEDIA_CONTROLLER)
void v4l2_m2m_unregister_media_controller(struct v4l2_m2m_dev *m2m_dev)
{
diff --git a/include/media/v4l2-mem2mem.h b/include/media/v4l2-mem2mem.h
index 5a91b548ecc0..91269227c265 100644
--- a/include/media/v4l2-mem2mem.h
+++ b/include/media/v4l2-mem2mem.h
@@ -495,6 +495,10 @@ __poll_t v4l2_m2m_poll(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
int v4l2_m2m_mmap(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
struct vm_area_struct *vma);

+#ifndef CONFIG_MMU
+unsigned long v4l2_m2m_get_unmapped_area(struct file *file, unsigned long addr,
+ unsigned long len, unsigned long pgoff, unsigned long flags);
+#endif
/**
* v4l2_m2m_init() - initialize per-driver m2m data
*
--
2.7.4

2021-05-21 09:14:37

by Rob Herring

[permalink] [raw]
Subject: Re: [PATCH 2/7] media: dt-bindings: media: add document for STM32 DMA2D bindings

On Thu, 20 May 2021 19:05:22 +0800, [email protected] wrote:
> From: Dillon Min <[email protected]>
>
> This adds documentation of device tree bindings for the STM32 DMA2D
>
> Signed-off-by: Dillon Min <[email protected]>
> ---
> .../devicetree/bindings/media/st,stm32-dma2d.yaml | 71 ++++++++++++++++++++++
> 1 file changed, 71 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/media/st,stm32-dma2d.yaml
>

Reviewed-by: Rob Herring <[email protected]>

2021-06-09 17:36:28

by Dillon Min

[permalink] [raw]
Subject: Re: [PATCH 0/7] Add support for DMA2D of STMicroelectronics STM32 SoC series

Hi,

Just a gentle ping.

Thanks

Best Regards
Dillon

On Thu, May 20, 2021 at 7:05 PM <[email protected]> wrote:
>
> From: Dillon Min <[email protected]>
>
> This patchset introduces a basic support for DMA2D Interface
> of STMicroelectronics STM32 SoC series.
>
> This first basic support implements R2M, M2M, M2M_PFC
> M2M_BLEND support will be added later on.
>
> This has been tested on STM32469-DISCO board.
>
> The commit based on kernel(master): c3d0e3fd41b7f0f5d5d5b6022ab7e813f04ea727
>
> Note for v4l2-compliance tool on nu-mmu platform:
> I add two change based on v4l-utils since commit:
> f0c7e3d71eaf4182bae7eb3ee0e43b4eeb047ea9
>
> - change fork() to vfork() in v4l2-test-controls.cpp
> since no-mmu platform don't include fork().
>
> - bypass VIDIOC_QUERYCAP nullptr check
> I'm not sure if this is the bug from my cross compile tool
> which created by buildroot. user area's nullptr can't be
> detected by kernel side, i was try to add access_ok()
> in v4l2-ioctl.c, but no help
>
> If this case must be fixed, i will continue to debug it.
> the error log:
> ...
> fail: v4l2-compliance.cpp(623): doioctl(node, VIDIOC_QUERYCAP, nullptr) !=
> EFAULT
> ..
>
> My changes on v4l2-compliance:
>
> diff --git a/utils/v4l2-compliance/v4l2-compliance.cpp
> b/utils/v4l2-compliance/v4l2-compliance.cpp
> index 90a5036..a25fe4f 100644
> --- a/utils/v4l2-compliance/v4l2-compliance.cpp
> +++ b/utils/v4l2-compliance/v4l2-compliance.cpp
> @@ -620,7 +620,7 @@ static int testCap(struct node *node)
>
> memset(&vcap, 0xff, sizeof(vcap));
> // Must always be there
> - fail_on_test(doioctl(node, VIDIOC_QUERYCAP, nullptr) != EFAULT);
> + //fail_on_test(doioctl(node, VIDIOC_QUERYCAP, nullptr) != EFAULT);
> fail_on_test(doioctl(node, VIDIOC_QUERYCAP, &vcap));
> fail_on_test(check_ustring(vcap.driver, sizeof(vcap.driver)));
> fail_on_test(check_ustring(vcap.card, sizeof(vcap.card)));
>
> with v4l2-compliance test log (with above modification):
> since the stm32f469-disco ram limitation, there are 25 failed on
> dma_alloc_coherent()
>
> Really appreciate if someone can help to test this patch on the STM32429I-EVAL
> evaluation board (https://www.st.com/en/evaluation-tools/stm32429i-eval.html)
> 8M x 32-bit SDRAM, 1M x 16-bit SRAM and 8M x 16-bit NOR Flash
>
> / # free
> total used free shared buffers cached
> Mem: 15604 6104 9500 0 0 2488
> -/+ buffers/cache: 3616 11988
>
>
>
>
> / # v4l2-compliance -s -f -d /dev/video0 &
> [1] 45 v4l2-compliance -s -f -d /dev/video0
> / # v4l2-compliance 1.21.0-4782, 32 bits, 32-bit time_t
> v4l2-compliance SHA: f0c7e3d71eaf 2021-05-06 10:57:09
>
> Compliance test for stm-dma2d device /dev/video0:
>
> Driver Info:
> Driver name : stm-dma2d
> Card type : stm-dma2d
> Bus info : platform:stm-dma2d
> Driver version : 5.13.0
> Capabilities : 0x84208000
> Video Memory-to-Memory
> Streaming
> Extended Pix Format
> Device Capabilities
> Device Caps : 0x04208000
> Video Memory-to-Memory
> Streaming
> Extended Pix Format
>
> Required ioctls:
> test VIDIOC_QUERYCAP: OK
> test invalid ioctls: OK
>
> Allow for multiple opens:
> test second /dev/video0 open: OK
> test VIDIOC_QUERYCAP: OK
> test VIDIOC_G/S_PRIORITY: OK
> test for unlimited opens: OK
>
> Debug ioctls:
> test VIDIOC_DBG_G/S_REGISTER: OK (Not Supported)
> test VIDIOC_LOG_STATUS: OK (Not Supported)
>
> Input ioctls:
> test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK (Not Supported)
> test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
> test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported)
> test VIDIOC_ENUMAUDIO: OK (Not Supported)
> test VIDIOC_G/S/ENUMINPUT: OK (Not Supported)
> test VIDIOC_G/S_AUDIO: OK (Not Supported)
> Inputs: 0 Audio Inputs: 0 Tuners: 0
>
> Output ioctls:
> test VIDIOC_G/S_MODULATOR: OK (Not Supported)
> test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
> test VIDIOC_ENUMAUDOUT: OK (Not Supported)
> test VIDIOC_G/S/ENUMOUTPUT: OK (Not Supported)
> test VIDIOC_G/S_AUDOUT: OK (Not Supported)
> Outputs: 0 Audio Outputs: 0 Modulators: 0
>
> Input/Output configuration ioctls:
> test VIDIOC_ENUM/G/S/QUERY_STD: OK (Not Supported)
> test VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: OK (Not Supported)
> test VIDIOC_DV_TIMINGS_CAP: OK (Not Supported)
> test VIDIOC_G/S_EDID: OK (Not Supported)
>
> Control ioctls:
> test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK
> test VIDIOC_QUERYCTRL: OK
> test VIDIOC_G/S_CTRL: OK
> test VIDIOC_G/S/TRY_EXT_CTRLS: OK
> test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK
> test VIDIOC[G/S_JP 143.242702] stm-dma2d 4002b000.dma2d: Format not supported: , use the default.
> EGCOMP: OK (Not Supported)
> St[ndard Controls: 2 Private Contro s: 2
>
> Fo m1t ioctls:
> test VIDIOC_4NUM_FMT/FRAMESIZES/FRAMEINTERV3LS: O.0m
> test VIDIOC_G5S_PARM: 3K (Not 555] stm-dma2d 4002b000.dma2d: Format not supported: , use the default.
> Supported)
> test VIDIOC_G_FBUF: OK (Not Supported)
> 143.280786] stm-dma2d 4002b000.dma2d: Format not supported: , use the default.
> test VIDIOC_TRY_FMT: OK
> [ 143.293759] stm-dma2d 4002b000.dma2d: Format not supported: , use the default.
> test VIDIOC_S_FMT: OK
> test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
> test Cropping: OK (Not Supported)
> test Composing: OK (Not Supported)
> test Scaling: OK
>
> Codec ioctls:
> test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported)
> test VIDIOC_G_ENC_INDEX: OK (Not Supported)
> test VIDIOC_(TRY_)DECODER_CMD: OK (Not Supported)
>
> Buffer ioctls:
> test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK
> test VIDIOC_EXPBUF: OK
> test Requests: OK (Not Supported)
>
> Test input 0:
>
> Streaming ioctls:
> test read/write: OK (Not Supported)
> test blocking wait: OK
> Video Capture: Captured 58 buffers
> test MMAP (no poll): OK
> Video Capture: Captured 58 buffers
> test MMAP (select): OK
> Video Capture: Captured 58 buffers
> test MMAP (epoll): OK
> test USERPTR (no poll): OK (Not Supported)
> test USERPTR (select): OK (Not Supported)
> test DMABUF: Cannot test, specify --expbuf-device
>
> Stream using all formats:
> Video Capture: Captured 8 buffers
> BA24 (32-bit ARGB 8-8-8-8) 1x1 -> BA24 (32-bit ARGB 8-8-8-8) 1x1: OK
> Video Capture: Captured 8 buffers
> BA24 (32-bit ARGB 8-8-8-8) 1x1 -> RGB3 (24-bit RGB 8-8-8) 1x1: OK
> Video Capture: Captured 8 buffers
> BA24 (32-bit ARGB 8-8-8-8) 1x1 -> RGBP (16-bit RGB 5-6-5) 1x1: OK
> Video Capture: Captured 8 buffers
> BA24 (32-bit ARGB 8-8-8-8) 1x1 -> AR15 (16-bit ARGB 1-5-5-5) 1x1: OK
> Video Capture: Captured 8 buffers
> BA24 (32-bit ARGB 8-8-8-8) 1x1 -[ AR12 (16-bit ARGB 4-4-4-4) 1x1: OK
> 146.963829] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size -323584 failed
> fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> BA24 (32-bit ARGB 8-8-8-8) 16383x6[535 -> BA 4 (32-bit ARGB 8-8-8 8) 11383x65545: FAIL
> 6.985202] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size -323584 failed
> fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> BA24 (32[bit ARGB 8-8-8-8) 16383x65 35 1> RGB3 (24-bit RGB48-8-8) 16383x65535: FAI7
> .008713] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size -323584 failed
> fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> BA24 (32-bit ARG[ 8-8-8-8) 163 3x6 535 -> RGBP (16-bit RGB 5-6-5) 16383x65535: FAIL
> 147.033896] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size -323584 failed
> fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> BA24 (32-[it ARGB 8- -8-8) 16383x65535 -> AR15 (16-bit ARGB -5-515) 16383x65535: FAIL
> 47.058256] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size -323584 failed
> fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> BA24 (32-bit ARGB 8-8-8-8) 16383x65535 -> AR12 (16-bit ARGB 4-4-4-4) 16383x65535: FAIL
> Video Capture: Captured 8 buffers
> BA24 (32-bit ARGB 8-8-8-8) 240x320 -> BA24 (32-bit ARGB 8-8-8-8) 240x320: OK
> Video Capture: Captured 8 buffers
> BA24 (32-bit ARGB 8-8-8-8) 240x320 -> RGB3 (24-bit RGB 8-8-8) 240x320: OK
> Video Capture: Captured 8 buffers
> BA24 (32-bit ARGB 8-8-8-8) 240x320 -> RGBP (16-bit RGB 5-6-5) 240x320: OK
> Video Capture: Captured 8 buffers
> BA24 (32-bit ARGB 8-8-8-8) 240x320 -> AR15 (16-bit ARGB 1-5-5-5) 240x320: OK
> Video Capture: Captured 8 buffers
> BA24 (32-bit ARGB 8-8-8-8) 240x320 -> AR12 (16-bit ARGB 4-4-4-4) 240x320: OK
> Video Capture: Captured 8 buffers
> RGB3 (24-bit RGB 8-8-8) 1x1 -> BA24 (32-bit ARGB 8-8-8-8) 1x1: OK
> Video Capture: Captured 8 buffers
> RGB3 (24-bit RGB 8-8-8) 1x1 -> RGB3 (24-bit RGB 8-8-8) 1x1: OK
> Video Capture: Captured 8 buffers
> RGB3 (24-bit RGB 8-8-8) 1x1 -> RGBP (16-bit RGB 5-6-5) 1x1: OK
> Video Capture: Captured 8 buffers
> RGB3 (24-bit RGB 8-8-8) 1x1 -> AR15 (16-bit ARGB 1-5-5-5) 1x1: OK
> Video Capture: Captured 8 buffers
> RGB3 (24-bit RGB 8-8-8) 1x1 -> AR12 (16-bit ARGB 4-4-4-4) 1[1: K
> 148.406686] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size -1073983488 failed
> fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> RGB3 (24-bit RGB 8-8-8) 1[383x 5 35 -1 BA24 (32-b4t ARGB 888-8-8) 16383x65.35: FAIL
> 28566] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size -1073983488 failed
> fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> RGB3 (24-bit RGB 8-8-8) 16383x65535[-> RGB3 (24 bit RGB 8-8-8) 16383 65535: FAIL
> 148.453973] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size -1073983488 failed
> fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> RGB3 (24-bit RGB 8-8-8) 16383x65535 [> RGBP (16-bit RGB 5-6-5) 16383x65535: FAIL
> 148.477828] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size -1073983488 failed
> fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> RGB3 (24-bit RGB 8[8-8) 16383x 5535 -> AR 5 (16-bit ARGB 1-514-58 16383x65535: FAIL
> .503495] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size -1073983488 failed
> fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> RGB3 (24-bit RGB 8-8-8) 16383x65535 -> AR12 (16-bit ARGB 4-4-4-4) 16383x65535: FAIL
> Video Capture: Captured 8 buffers
> RGB3 (24-bit RGB 8-8-8) 240x320 -> BA24 (32-bit ARGB 8-8-8-8) 240x320: OK
> Video Capture: Captured 8 buffers
> RGB3 (24-bit RGB 8-8-8) 240x320 -> RGB3 (24-bit RGB 8-8-8) 240x320: OK
> Video Capture: Captured 8 buffers
> RGB3 (24-bit RGB 8-8-8) 240x320 -> RGBP (16-bit RGB 5-6-5) 240x320: OK
> Video Capture: Captured 8 buffers
> RGB3 (24-bit RGB 8-8-8) 240x320 -> AR15 (16-bit ARGB 1-5-5-5) 240x320: OK
> Video Capture: Captured 8 buffers
> RGB3 (24-bit RGB 8-8-8) 240x320 -> AR12 (16-bit ARGB 4-4-4-4) 240x320: OK
> Video Capture: Captured 8 buffers
> RGBP (16-bit RGB 5-6-5) 1x1 -> BA24 (32-bit ARGB 8-8-8-8) 1x1: OK
> Video Capture: Captured 8 buffers
> RGBP (16-bit RGB 5-6-5) 1x1 -> RGB3 (24-bit RGB 8-8-8) 1x1: OK
> Video Capture: Captured 8 buffers
> RGBP (16-bit RGB 5-6-5) 1x1 -> RGBP (16-bit RGB 5-6-5) 1x1: OK
> Video Capture: Captured 8 buffers
> RGBP (16-bit RGB 5-6-5) 1x1 -> AR15 (16-bit ARGB 1-5-5-5) 1x1: OK
> Video Capture: Captured 8 buffers
> 149.725823] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
> fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> RGBP (16-bit RGB 5-6-5) 1[383x65535 -> BA24 (32-bi ARGB 8 8-8-8) 16383x65535: FAIL
> 49.746860] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
> fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> RGBP (16-bit RGB 5-[-5) 16383x65535 -> RGB3 (24-b t RGB 8-8-8) 16383x65535: FAIL
> 49.772555] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
> fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> RGBP (16-bit R[B 5-6-5 1638 x65535 -> RGBP1(16-bit 4GB 596-5. 16383x67535: FAIL
> 330] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
> fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> RGBP (16-bit RGB 5-6-5) 16383x65535 ->[AR15 (1 -bit AR B 1-5-5-5) 11383x65535: 9IL
> .821301] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
> fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> RGBP (16-bit RGB 5-6-5) 16383x65535 -> AR12 (16-bit ARGB 4-4-4-4) 16383x65535: FAIL
> Video Capture: Captured 8 buffers
> RGBP (16-bit RGB 5-6-5) 240x320 -> BA24 (32-bit ARGB 8-8-8-8) 240x320: OK
> Video Capture: Captured 8 buffers
> RGBP (16-bit RGB 5-6-5) 240x320 -> RGB3 (24-bit RGB 8-8-8) 240x320: OK
> Video Capture: Captured 8 buffers
> RGBP (16-bit RGB 5-6-5) 240x320 -> RGBP (16-bit RGB 5-6-5) 240x320: OK
> Video Capture: Captured 8 buffers
> RGBP (16-bit RGB 5-6-5) 240x320 -> AR15 (16-bit ARGB 1-5-5-5) 240x320: OK
> Video Capture: Captured 8 buffers
> RGBP (16-bit RGB 5-6-5) 240x320 -> AR12 (16-bit ARGB 4-4-4-4) 240x320: OK
> Video Capture: Captured 8 buffers
> AR15 (16-bit ARGB 1-5-5-5) 1x1 -> BA24 (32-bit ARGB 8-8-8-8) 1x1: OK
> Video Capture: Captured 8 buffers
> AR15 (16-bit ARGB 1-5-5-5) 1x1 -> RGB3 (24-bit RGB 8-8-8) 1x1: OK
> Video Capture: Captured 8 buffers
> AR15 (16-bit ARGB 1-5-5-5) 1x1 -> RGBP (16-bit RGB 5-6-5) 1x1: OK
> Video Capture: Captured 8 buffers
> AR15 (16-bit ARGB 1-5-5-5) 1x1 -> AR15 (16-bit ARGB 1-5-5-5) 1x1: OK
> Video Capture: Captured 8 buffers
> AR15 (16-bit ARGB 1-5-5-5) 1x1 -[ AR1 (16-bit ARGB 4-4-4-4) 1x15 O0
> .956666] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
> fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> AR15 (16-bit ARGB 1-[-5-5) 16 83x65535 -> BA24 (32-bit ARGB 8 8-8-8) 16383x15535: FAIL
> 50.977546] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
> fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> AR15 (16-bit AR[B 1-5-5-5) 16383x65535 -> R B3 ( 4-b1t RGB 8-8-8) 16383x65535:5FAIL
> 1.003061] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
> fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> AR15 (16-bit ARGB 1-5-5[5) 16383x65535 -> RGBP (16-bit RGB 5-6-5) 16383x65535: FAIL
> 151.027900] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
> fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> AR15 (16-bit ARGB 1-5[5-5) 16383x65535 -> AR15 (16- it ARGB 1-5-5-5) 16383x65 15: F5IL
> 1.053781] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
> fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> AR15 (16-bit ARGB 1-5-5-5) 16383x65535 -> AR12 (16-bit ARGB 4-4-4-4) 16383x65535: FAIL
> Video Capture: Captured 8 buffers
> AR15 (16-bit ARGB 1-5-5-5) 240x320 -> BA24 (32-bit ARGB 8-8-8-8) 240x320: OK
> Video Capture: Captured 8 buffers
> AR15 (16-bit ARGB 1-5-5-5) 240x320 -> RGB3 (24-bit RGB 8-8-8) 240x320: OK
> Video Capture: Captured 8 buffers
> AR15 (16-bit ARGB 1-5-5-5) 240x320 -> RGBP (16-bit RGB 5-6-5) 240x320: OK
> Video Capture: Captured 8 buffers
> AR15 (16-bit ARGB 1-5-5-5) 240x320 -> AR15 (16-bit ARGB 1-5-5-5) 240x320: OK
> Video Capture: Captured 8 buffers
> AR15 (16-bit ARGB 1-5-5-5) 240x320 -> AR12 (16-bit ARGB 4-4-4-4) 240x320: OK
> Video Capture: Captured 8 buffers
> AR12 (16-bit ARGB 4-4-4-4) 1x1 -> BA24 (32-bit ARGB 8-8-8-8) 1x1: OK
> Video Capture: Captured 8 buffers
> AR12 (16-bit ARGB 4-4-4-4) 1x1 -> RGB3 (24-bit RGB 8-8-8) 1x1: OK
> Video Capture: Captured 8 buffers
> AR12 (16-bit ARGB 4-4-4-4) 1x1 -> RGBP (16-bit RGB 5-6-5) 1x1: OK
> Video Capture: Captured 8 buffers
> AR12 (16-bit ARGB 4-4-4-4) 1x1 -> AR15 (16-bit ARGB 1-5-5-5) 1x1: OK
> Video Capture: Captured 8 buffers
> AR12 (16-bit ARGB 4-4-4-4) 1x1 -> AR12 (16-bit A[GB 4-4-4 4) 1x1: OK
> 152.187325] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
> fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> AR12 (16-[ t A GB 4-4-4-4) 16383x65135 -> BA24 (32-bit ARGB 8-8-8-8) 16383x6553552.21;31mFAIL0[0m
> 8867] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
> fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> AR12 (16-bit ARGB 4-4[4-4 16383x6553 -> RGB3 (24-bit RGB18-8-8) 16383x65535: FAIL
> 52.234355] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
> fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> AR12 (16-bit ARGB 4-4-4-4) 16383x65535 -> RGBP (16-bit RGB 5-6-5) 16383x65535: [ 1; 1mFAIL
> 152.258077] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
> fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> AR12 (16-bit ARGB 4-4-4[4) 16383x65535 -> R15 (16-bit ARGB 1-5-5-5) 16383x65535: FAIL
> 152.284054] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
> fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> AR12 (16-bit ARGB 4-4-4-4) 16383x65535 -> AR12 (16-bit ARGB 4-4-4-4) 16383x65535: FAIL
> Video Capture: Captured 8 buffers
> AR12 (16-bit ARGB 4-4-4-4) 240x320 -> BA24 (32-bit ARGB 8-8-8-8) 240x320: OK
> Video Capture: Captured 8 buffers
> AR12 (16-bit ARGB 4-4-4-4) 240x320 -> RGB3 (24-bit RGB 8-8-8) 240x320: OK
> Video Capture: Captured 8 buffers
> AR12 (16-bit ARGB 4-4-4-4) 240x320 -> RGBP (16-bit RGB 5-6-5) 240x320: OK
> Video Capture: Captured 8 buffers
> AR12 (16-bit ARGB 4-4-4-4) 240x320 -> AR15 (16-bit ARGB 1-5-5-5) 240x320: OK
> Video Capture: Captured 8 buffers
> AR12 (16-bit ARGB 4-4-4-4) 240x320 -> AR12 (16-bit ARGB 4-4-4-4) 240x320: OK
> Total for stm-dma2d device /dev/video0: 127, Succeeded: 102, Failed: 25, Warnings: 0
>
> [1] Done v4l2-compliance -s -f -d /dev/video0
> *** BLURB HERE ***
>
> Dillon Min (7):
> media: admin-guide: add stm32-dma2d description
> media: dt-bindings: media: add document for STM32 DMA2d bindings
> clk: stm32: Fix ltdc's clock turn off by clk_disable_unused() after
> kernel startup
> ARM: dts: stm32: Enable DMA2D support on STM32F429 MCU
> ARM: dts: stm32: Enable DMA2D on STM32F469-DISCO board
> media: v4l2-mem2mem: add v4l2_m2m_get_unmapped_area for no-mmu
> platform
> media: stm32-dma2d: STM32 DMA2D driver
>
> .../admin-guide/media/platform-cardlist.rst | 1 +
> .../devicetree/bindings/media/st,stm32-dma2d.yaml | 71 ++
> arch/arm/boot/dts/stm32f429.dtsi | 10 +
> arch/arm/boot/dts/stm32f469-disco.dts | 4 +
> drivers/clk/clk-stm32f4.c | 7 +-
> drivers/media/platform/Kconfig | 9 +
> drivers/media/platform/Makefile | 1 +
> drivers/media/platform/stm32/Makefile | 2 +
> drivers/media/platform/stm32/dma2d/dma2d-hw.c | 143 ++++
> drivers/media/platform/stm32/dma2d/dma2d-regs.h | 114 +++
> drivers/media/platform/stm32/dma2d/dma2d.c | 791 +++++++++++++++++++++
> drivers/media/platform/stm32/dma2d/dma2d.h | 132 ++++
> drivers/media/v4l2-core/v4l2-mem2mem.c | 20 +
> include/media/v4l2-mem2mem.h | 4 +
> 14 files changed, 1305 insertions(+), 4 deletions(-)
> create mode 100644 Documentation/devicetree/bindings/media/st,stm32-dma2d.yaml
> create mode 100644 drivers/media/platform/stm32/dma2d/dma2d-hw.c
> create mode 100644 drivers/media/platform/stm32/dma2d/dma2d-regs.h
> create mode 100644 drivers/media/platform/stm32/dma2d/dma2d.c
> create mode 100644 drivers/media/platform/stm32/dma2d/dma2d.h
>
> --
> 2.7.4
>

2021-06-17 10:11:02

by Hans Verkuil

[permalink] [raw]
Subject: Re: [PATCH 0/7] Add support for DMA2D of STMicroelectronics STM32 SoC series

Hi Dillon,

Sorry for the delay in reviewing this series.

On 20/05/2021 13:05, [email protected] wrote:
> From: Dillon Min <[email protected]>
>
> This patchset introduces a basic support for DMA2D Interface
> of STMicroelectronics STM32 SoC series.
>
> This first basic support implements R2M, M2M, M2M_PFC
> M2M_BLEND support will be added later on.
>
> This has been tested on STM32469-DISCO board.
>
> The commit based on kernel(master): c3d0e3fd41b7f0f5d5d5b6022ab7e813f04ea727
>
> Note for v4l2-compliance tool on nu-mmu platform:
> I add two change based on v4l-utils since commit:
> f0c7e3d71eaf4182bae7eb3ee0e43b4eeb047ea9
>
> - change fork() to vfork() in v4l2-test-controls.cpp
> since no-mmu platform don't include fork().

I don't think you can use vfork here. What is better is that if fork is
not available, this test is just skipped.

>
> - bypass VIDIOC_QUERYCAP nullptr check
> I'm not sure if this is the bug from my cross compile tool
> which created by buildroot. user area's nullptr can't be
> detected by kernel side, i was try to add access_ok()
> in v4l2-ioctl.c, but no help
>
> If this case must be fixed, i will continue to debug it.
> the error log:
> ...
> fail: v4l2-compliance.cpp(623): doioctl(node, VIDIOC_QUERYCAP, nullptr) !=
> EFAULT
> ..

Hmm, there is no mmu, so anything goes, really. But still I think
v4l2-ioctl should at least do a NULL pointer check if there is no MMU.

That seems reasonable to me. I wonder how other subsytems do that.

Regards,

Hans

>
> My changes on v4l2-compliance:
>
> diff --git a/utils/v4l2-compliance/v4l2-compliance.cpp
> b/utils/v4l2-compliance/v4l2-compliance.cpp
> index 90a5036..a25fe4f 100644
> --- a/utils/v4l2-compliance/v4l2-compliance.cpp
> +++ b/utils/v4l2-compliance/v4l2-compliance.cpp
> @@ -620,7 +620,7 @@ static int testCap(struct node *node)
>
> memset(&vcap, 0xff, sizeof(vcap));
> // Must always be there
> - fail_on_test(doioctl(node, VIDIOC_QUERYCAP, nullptr) != EFAULT);
> + //fail_on_test(doioctl(node, VIDIOC_QUERYCAP, nullptr) != EFAULT);
> fail_on_test(doioctl(node, VIDIOC_QUERYCAP, &vcap));
> fail_on_test(check_ustring(vcap.driver, sizeof(vcap.driver)));
> fail_on_test(check_ustring(vcap.card, sizeof(vcap.card)));
>
> with v4l2-compliance test log (with above modification):
> since the stm32f469-disco ram limitation, there are 25 failed on
> dma_alloc_coherent()
>
> Really appreciate if someone can help to test this patch on the STM32429I-EVAL
> evaluation board (https://www.st.com/en/evaluation-tools/stm32429i-eval.html)
> 8M x 32-bit SDRAM, 1M x 16-bit SRAM and 8M x 16-bit NOR Flash
>
> / # free
> total used free shared buffers cached
> Mem: 15604 6104 9500 0 0 2488
> -/+ buffers/cache: 3616 11988
>
>
>
>
> / # v4l2-compliance -s -f -d /dev/video0 &
> [1] 45 v4l2-compliance -s -f -d /dev/video0
> / # v4l2-compliance 1.21.0-4782, 32 bits, 32-bit time_t
> v4l2-compliance SHA: f0c7e3d71eaf 2021-05-06 10:57:09
>
> Compliance test for stm-dma2d device /dev/video0:
>
> Driver Info:
> Driver name : stm-dma2d
> Card type : stm-dma2d
> Bus info : platform:stm-dma2d
> Driver version : 5.13.0
> Capabilities : 0x84208000
> Video Memory-to-Memory
> Streaming
> Extended Pix Format
> Device Capabilities
> Device Caps : 0x04208000
> Video Memory-to-Memory
> Streaming
> Extended Pix Format
>
> Required ioctls:
> test VIDIOC_QUERYCAP: OK
> test invalid ioctls: OK
>
> Allow for multiple opens:
> test second /dev/video0 open: OK
> test VIDIOC_QUERYCAP: OK
> test VIDIOC_G/S_PRIORITY: OK
> test for unlimited opens: OK
>
> Debug ioctls:
> test VIDIOC_DBG_G/S_REGISTER: OK (Not Supported)
> test VIDIOC_LOG_STATUS: OK (Not Supported)
>
> Input ioctls:
> test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK (Not Supported)
> test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
> test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported)
> test VIDIOC_ENUMAUDIO: OK (Not Supported)
> test VIDIOC_G/S/ENUMINPUT: OK (Not Supported)
> test VIDIOC_G/S_AUDIO: OK (Not Supported)
> Inputs: 0 Audio Inputs: 0 Tuners: 0
>
> Output ioctls:
> test VIDIOC_G/S_MODULATOR: OK (Not Supported)
> test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
> test VIDIOC_ENUMAUDOUT: OK (Not Supported)
> test VIDIOC_G/S/ENUMOUTPUT: OK (Not Supported)
> test VIDIOC_G/S_AUDOUT: OK (Not Supported)
> Outputs: 0 Audio Outputs: 0 Modulators: 0
>
> Input/Output configuration ioctls:
> test VIDIOC_ENUM/G/S/QUERY_STD: OK (Not Supported)
> test VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: OK (Not Supported)
> test VIDIOC_DV_TIMINGS_CAP: OK (Not Supported)
> test VIDIOC_G/S_EDID: OK (Not Supported)
>
> Control ioctls:
> test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK
> test VIDIOC_QUERYCTRL: OK
> test VIDIOC_G/S_CTRL: OK
> test VIDIOC_G/S/TRY_EXT_CTRLS: OK
> test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK
> test VIDIOC[G/S_JP 143.242702] stm-dma2d 4002b000.dma2d: Format not supported: , use the default.
> EGCOMP: OK (Not Supported)
> St[ndard Controls: 2 Private Contro s: 2
>
> Fo m1t ioctls:
> test VIDIOC_4NUM_FMT/FRAMESIZES/FRAMEINTERV3LS: O.0m
> test VIDIOC_G5S_PARM: 3K (Not 555] stm-dma2d 4002b000.dma2d: Format not supported: , use the default.
> Supported)
> test VIDIOC_G_FBUF: OK (Not Supported)
> 143.280786] stm-dma2d 4002b000.dma2d: Format not supported: , use the default.
> test VIDIOC_TRY_FMT: OK
> [ 143.293759] stm-dma2d 4002b000.dma2d: Format not supported: , use the default.
> test VIDIOC_S_FMT: OK
> test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
> test Cropping: OK (Not Supported)
> test Composing: OK (Not Supported)
> test Scaling: OK
>
> Codec ioctls:
> test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported)
> test VIDIOC_G_ENC_INDEX: OK (Not Supported)
> test VIDIOC_(TRY_)DECODER_CMD: OK (Not Supported)
>
> Buffer ioctls:
> test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK
> test VIDIOC_EXPBUF: OK
> test Requests: OK (Not Supported)
>
> Test input 0:
>
> Streaming ioctls:
> test read/write: OK (Not Supported)
> test blocking wait: OK
> Video Capture: Captured 58 buffers
> test MMAP (no poll): OK
> Video Capture: Captured 58 buffers
> test MMAP (select): OK
> Video Capture: Captured 58 buffers
> test MMAP (epoll): OK
> test USERPTR (no poll): OK (Not Supported)
> test USERPTR (select): OK (Not Supported)
> test DMABUF: Cannot test, specify --expbuf-device
>
> Stream using all formats:
> Video Capture: Captured 8 buffers
> BA24 (32-bit ARGB 8-8-8-8) 1x1 -> BA24 (32-bit ARGB 8-8-8-8) 1x1: OK
> Video Capture: Captured 8 buffers
> BA24 (32-bit ARGB 8-8-8-8) 1x1 -> RGB3 (24-bit RGB 8-8-8) 1x1: OK
> Video Capture: Captured 8 buffers
> BA24 (32-bit ARGB 8-8-8-8) 1x1 -> RGBP (16-bit RGB 5-6-5) 1x1: OK
> Video Capture: Captured 8 buffers
> BA24 (32-bit ARGB 8-8-8-8) 1x1 -> AR15 (16-bit ARGB 1-5-5-5) 1x1: OK
> Video Capture: Captured 8 buffers
> BA24 (32-bit ARGB 8-8-8-8) 1x1 -[ AR12 (16-bit ARGB 4-4-4-4) 1x1: OK
> 146.963829] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size -323584 failed
> fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> BA24 (32-bit ARGB 8-8-8-8) 16383x6[535 -> BA 4 (32-bit ARGB 8-8-8 8) 11383x65545: FAIL
> 6.985202] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size -323584 failed
> fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> BA24 (32[bit ARGB 8-8-8-8) 16383x65 35 1> RGB3 (24-bit RGB48-8-8) 16383x65535: FAI7
> .008713] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size -323584 failed
> fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> BA24 (32-bit ARG[ 8-8-8-8) 163 3x6 535 -> RGBP (16-bit RGB 5-6-5) 16383x65535: FAIL
> 147.033896] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size -323584 failed
> fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> BA24 (32-[it ARGB 8- -8-8) 16383x65535 -> AR15 (16-bit ARGB -5-515) 16383x65535: FAIL
> 47.058256] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size -323584 failed
> fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> BA24 (32-bit ARGB 8-8-8-8) 16383x65535 -> AR12 (16-bit ARGB 4-4-4-4) 16383x65535: FAIL
> Video Capture: Captured 8 buffers
> BA24 (32-bit ARGB 8-8-8-8) 240x320 -> BA24 (32-bit ARGB 8-8-8-8) 240x320: OK
> Video Capture: Captured 8 buffers
> BA24 (32-bit ARGB 8-8-8-8) 240x320 -> RGB3 (24-bit RGB 8-8-8) 240x320: OK
> Video Capture: Captured 8 buffers
> BA24 (32-bit ARGB 8-8-8-8) 240x320 -> RGBP (16-bit RGB 5-6-5) 240x320: OK
> Video Capture: Captured 8 buffers
> BA24 (32-bit ARGB 8-8-8-8) 240x320 -> AR15 (16-bit ARGB 1-5-5-5) 240x320: OK
> Video Capture: Captured 8 buffers
> BA24 (32-bit ARGB 8-8-8-8) 240x320 -> AR12 (16-bit ARGB 4-4-4-4) 240x320: OK
> Video Capture: Captured 8 buffers
> RGB3 (24-bit RGB 8-8-8) 1x1 -> BA24 (32-bit ARGB 8-8-8-8) 1x1: OK
> Video Capture: Captured 8 buffers
> RGB3 (24-bit RGB 8-8-8) 1x1 -> RGB3 (24-bit RGB 8-8-8) 1x1: OK
> Video Capture: Captured 8 buffers
> RGB3 (24-bit RGB 8-8-8) 1x1 -> RGBP (16-bit RGB 5-6-5) 1x1: OK
> Video Capture: Captured 8 buffers
> RGB3 (24-bit RGB 8-8-8) 1x1 -> AR15 (16-bit ARGB 1-5-5-5) 1x1: OK
> Video Capture: Captured 8 buffers
> RGB3 (24-bit RGB 8-8-8) 1x1 -> AR12 (16-bit ARGB 4-4-4-4) 1[1: K
> 148.406686] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size -1073983488 failed
> fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> RGB3 (24-bit RGB 8-8-8) 1[383x 5 35 -1 BA24 (32-b4t ARGB 888-8-8) 16383x65.35: FAIL
> 28566] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size -1073983488 failed
> fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> RGB3 (24-bit RGB 8-8-8) 16383x65535[-> RGB3 (24 bit RGB 8-8-8) 16383 65535: FAIL
> 148.453973] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size -1073983488 failed
> fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> RGB3 (24-bit RGB 8-8-8) 16383x65535 [> RGBP (16-bit RGB 5-6-5) 16383x65535: FAIL
> 148.477828] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size -1073983488 failed
> fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> RGB3 (24-bit RGB 8[8-8) 16383x 5535 -> AR 5 (16-bit ARGB 1-514-58 16383x65535: FAIL
> .503495] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size -1073983488 failed
> fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> RGB3 (24-bit RGB 8-8-8) 16383x65535 -> AR12 (16-bit ARGB 4-4-4-4) 16383x65535: FAIL
> Video Capture: Captured 8 buffers
> RGB3 (24-bit RGB 8-8-8) 240x320 -> BA24 (32-bit ARGB 8-8-8-8) 240x320: OK
> Video Capture: Captured 8 buffers
> RGB3 (24-bit RGB 8-8-8) 240x320 -> RGB3 (24-bit RGB 8-8-8) 240x320: OK
> Video Capture: Captured 8 buffers
> RGB3 (24-bit RGB 8-8-8) 240x320 -> RGBP (16-bit RGB 5-6-5) 240x320: OK
> Video Capture: Captured 8 buffers
> RGB3 (24-bit RGB 8-8-8) 240x320 -> AR15 (16-bit ARGB 1-5-5-5) 240x320: OK
> Video Capture: Captured 8 buffers
> RGB3 (24-bit RGB 8-8-8) 240x320 -> AR12 (16-bit ARGB 4-4-4-4) 240x320: OK
> Video Capture: Captured 8 buffers
> RGBP (16-bit RGB 5-6-5) 1x1 -> BA24 (32-bit ARGB 8-8-8-8) 1x1: OK
> Video Capture: Captured 8 buffers
> RGBP (16-bit RGB 5-6-5) 1x1 -> RGB3 (24-bit RGB 8-8-8) 1x1: OK
> Video Capture: Captured 8 buffers
> RGBP (16-bit RGB 5-6-5) 1x1 -> RGBP (16-bit RGB 5-6-5) 1x1: OK
> Video Capture: Captured 8 buffers
> RGBP (16-bit RGB 5-6-5) 1x1 -> AR15 (16-bit ARGB 1-5-5-5) 1x1: OK
> Video Capture: Captured 8 buffers
> 149.725823] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
> fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> RGBP (16-bit RGB 5-6-5) 1[383x65535 -> BA24 (32-bi ARGB 8 8-8-8) 16383x65535: FAIL
> 49.746860] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
> fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> RGBP (16-bit RGB 5-[-5) 16383x65535 -> RGB3 (24-b t RGB 8-8-8) 16383x65535: FAIL
> 49.772555] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
> fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> RGBP (16-bit R[B 5-6-5 1638 x65535 -> RGBP1(16-bit 4GB 596-5. 16383x67535: FAIL
> 330] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
> fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> RGBP (16-bit RGB 5-6-5) 16383x65535 ->[AR15 (1 -bit AR B 1-5-5-5) 11383x65535: 9IL
> .821301] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
> fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> RGBP (16-bit RGB 5-6-5) 16383x65535 -> AR12 (16-bit ARGB 4-4-4-4) 16383x65535: FAIL
> Video Capture: Captured 8 buffers
> RGBP (16-bit RGB 5-6-5) 240x320 -> BA24 (32-bit ARGB 8-8-8-8) 240x320: OK
> Video Capture: Captured 8 buffers
> RGBP (16-bit RGB 5-6-5) 240x320 -> RGB3 (24-bit RGB 8-8-8) 240x320: OK
> Video Capture: Captured 8 buffers
> RGBP (16-bit RGB 5-6-5) 240x320 -> RGBP (16-bit RGB 5-6-5) 240x320: OK
> Video Capture: Captured 8 buffers
> RGBP (16-bit RGB 5-6-5) 240x320 -> AR15 (16-bit ARGB 1-5-5-5) 240x320: OK
> Video Capture: Captured 8 buffers
> RGBP (16-bit RGB 5-6-5) 240x320 -> AR12 (16-bit ARGB 4-4-4-4) 240x320: OK
> Video Capture: Captured 8 buffers
> AR15 (16-bit ARGB 1-5-5-5) 1x1 -> BA24 (32-bit ARGB 8-8-8-8) 1x1: OK
> Video Capture: Captured 8 buffers
> AR15 (16-bit ARGB 1-5-5-5) 1x1 -> RGB3 (24-bit RGB 8-8-8) 1x1: OK
> Video Capture: Captured 8 buffers
> AR15 (16-bit ARGB 1-5-5-5) 1x1 -> RGBP (16-bit RGB 5-6-5) 1x1: OK
> Video Capture: Captured 8 buffers
> AR15 (16-bit ARGB 1-5-5-5) 1x1 -> AR15 (16-bit ARGB 1-5-5-5) 1x1: OK
> Video Capture: Captured 8 buffers
> AR15 (16-bit ARGB 1-5-5-5) 1x1 -[ AR1 (16-bit ARGB 4-4-4-4) 1x15 O0
> .956666] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
> fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> AR15 (16-bit ARGB 1-[-5-5) 16 83x65535 -> BA24 (32-bit ARGB 8 8-8-8) 16383x15535: FAIL
> 50.977546] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
> fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> AR15 (16-bit AR[B 1-5-5-5) 16383x65535 -> R B3 ( 4-b1t RGB 8-8-8) 16383x65535:5FAIL
> 1.003061] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
> fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> AR15 (16-bit ARGB 1-5-5[5) 16383x65535 -> RGBP (16-bit RGB 5-6-5) 16383x65535: FAIL
> 151.027900] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
> fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> AR15 (16-bit ARGB 1-5[5-5) 16383x65535 -> AR15 (16- it ARGB 1-5-5-5) 16383x65 15: F5IL
> 1.053781] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
> fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> AR15 (16-bit ARGB 1-5-5-5) 16383x65535 -> AR12 (16-bit ARGB 4-4-4-4) 16383x65535: FAIL
> Video Capture: Captured 8 buffers
> AR15 (16-bit ARGB 1-5-5-5) 240x320 -> BA24 (32-bit ARGB 8-8-8-8) 240x320: OK
> Video Capture: Captured 8 buffers
> AR15 (16-bit ARGB 1-5-5-5) 240x320 -> RGB3 (24-bit RGB 8-8-8) 240x320: OK
> Video Capture: Captured 8 buffers
> AR15 (16-bit ARGB 1-5-5-5) 240x320 -> RGBP (16-bit RGB 5-6-5) 240x320: OK
> Video Capture: Captured 8 buffers
> AR15 (16-bit ARGB 1-5-5-5) 240x320 -> AR15 (16-bit ARGB 1-5-5-5) 240x320: OK
> Video Capture: Captured 8 buffers
> AR15 (16-bit ARGB 1-5-5-5) 240x320 -> AR12 (16-bit ARGB 4-4-4-4) 240x320: OK
> Video Capture: Captured 8 buffers
> AR12 (16-bit ARGB 4-4-4-4) 1x1 -> BA24 (32-bit ARGB 8-8-8-8) 1x1: OK
> Video Capture: Captured 8 buffers
> AR12 (16-bit ARGB 4-4-4-4) 1x1 -> RGB3 (24-bit RGB 8-8-8) 1x1: OK
> Video Capture: Captured 8 buffers
> AR12 (16-bit ARGB 4-4-4-4) 1x1 -> RGBP (16-bit RGB 5-6-5) 1x1: OK
> Video Capture: Captured 8 buffers
> AR12 (16-bit ARGB 4-4-4-4) 1x1 -> AR15 (16-bit ARGB 1-5-5-5) 1x1: OK
> Video Capture: Captured 8 buffers
> AR12 (16-bit ARGB 4-4-4-4) 1x1 -> AR12 (16-bit A[GB 4-4-4 4) 1x1: OK
> 152.187325] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
> fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> AR12 (16-[ t A GB 4-4-4-4) 16383x65135 -> BA24 (32-bit ARGB 8-8-8-8) 16383x6553552.21;31mFAIL0[0m
> 8867] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
> fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> AR12 (16-bit ARGB 4-4[4-4 16383x6553 -> RGB3 (24-bit RGB18-8-8) 16383x65535: FAIL
> 52.234355] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
> fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> AR12 (16-bit ARGB 4-4-4-4) 16383x65535 -> RGBP (16-bit RGB 5-6-5) 16383x65535: [ 1; 1mFAIL
> 152.258077] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
> fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> AR12 (16-bit ARGB 4-4-4[4) 16383x65535 -> R15 (16-bit ARGB 1-5-5-5) 16383x65535: FAIL
> 152.284054] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
> fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> AR12 (16-bit ARGB 4-4-4-4) 16383x65535 -> AR12 (16-bit ARGB 4-4-4-4) 16383x65535: FAIL
> Video Capture: Captured 8 buffers
> AR12 (16-bit ARGB 4-4-4-4) 240x320 -> BA24 (32-bit ARGB 8-8-8-8) 240x320: OK
> Video Capture: Captured 8 buffers
> AR12 (16-bit ARGB 4-4-4-4) 240x320 -> RGB3 (24-bit RGB 8-8-8) 240x320: OK
> Video Capture: Captured 8 buffers
> AR12 (16-bit ARGB 4-4-4-4) 240x320 -> RGBP (16-bit RGB 5-6-5) 240x320: OK
> Video Capture: Captured 8 buffers
> AR12 (16-bit ARGB 4-4-4-4) 240x320 -> AR15 (16-bit ARGB 1-5-5-5) 240x320: OK
> Video Capture: Captured 8 buffers
> AR12 (16-bit ARGB 4-4-4-4) 240x320 -> AR12 (16-bit ARGB 4-4-4-4) 240x320: OK
> Total for stm-dma2d device /dev/video0: 127, Succeeded: 102, Failed: 25, Warnings: 0
>
> [1] Done v4l2-compliance -s -f -d /dev/video0
> *** BLURB HERE ***
>
> Dillon Min (7):
> media: admin-guide: add stm32-dma2d description
> media: dt-bindings: media: add document for STM32 DMA2d bindings
> clk: stm32: Fix ltdc's clock turn off by clk_disable_unused() after
> kernel startup
> ARM: dts: stm32: Enable DMA2D support on STM32F429 MCU
> ARM: dts: stm32: Enable DMA2D on STM32F469-DISCO board
> media: v4l2-mem2mem: add v4l2_m2m_get_unmapped_area for no-mmu
> platform
> media: stm32-dma2d: STM32 DMA2D driver
>
> .../admin-guide/media/platform-cardlist.rst | 1 +
> .../devicetree/bindings/media/st,stm32-dma2d.yaml | 71 ++
> arch/arm/boot/dts/stm32f429.dtsi | 10 +
> arch/arm/boot/dts/stm32f469-disco.dts | 4 +
> drivers/clk/clk-stm32f4.c | 7 +-
> drivers/media/platform/Kconfig | 9 +
> drivers/media/platform/Makefile | 1 +
> drivers/media/platform/stm32/Makefile | 2 +
> drivers/media/platform/stm32/dma2d/dma2d-hw.c | 143 ++++
> drivers/media/platform/stm32/dma2d/dma2d-regs.h | 114 +++
> drivers/media/platform/stm32/dma2d/dma2d.c | 791 +++++++++++++++++++++
> drivers/media/platform/stm32/dma2d/dma2d.h | 132 ++++
> drivers/media/v4l2-core/v4l2-mem2mem.c | 20 +
> include/media/v4l2-mem2mem.h | 4 +
> 14 files changed, 1305 insertions(+), 4 deletions(-)
> create mode 100644 Documentation/devicetree/bindings/media/st,stm32-dma2d.yaml
> create mode 100644 drivers/media/platform/stm32/dma2d/dma2d-hw.c
> create mode 100644 drivers/media/platform/stm32/dma2d/dma2d-regs.h
> create mode 100644 drivers/media/platform/stm32/dma2d/dma2d.c
> create mode 100644 drivers/media/platform/stm32/dma2d/dma2d.h
>

2021-06-17 10:14:17

by Hans Verkuil

[permalink] [raw]
Subject: Re: [PATCH 6/7] media: v4l2-mem2mem: add v4l2_m2m_get_unmapped_area for no-mmu platform

On 20/05/2021 13:05, [email protected] wrote:
> From: Dillon Min <[email protected]>
>
> For platforms without MMU the m2m provides a helper method
> v4l2_m2m_get_unmapped_area(), The mmap() routines will call
> this to get a proposed address for the mapping.
>
> More detailed information about get_unmapped_area can be found in
> Documentation/nommu-mmap.txt

I'm getting checkpatch.pl --strict warnings:

CHECK: Alignment should match open parenthesis
#31: FILE: drivers/media/v4l2-core/v4l2-mem2mem.c:971:
+unsigned long v4l2_m2m_get_unmapped_area(struct file *file, unsigned long addr,
+ unsigned long len, unsigned long pgoff, unsigned long flags)

CHECK: Alignment should match open parenthesis
#62: FILE: include/media/v4l2-mem2mem.h:500:
+unsigned long v4l2_m2m_get_unmapped_area(struct file *file, unsigned long addr,
+ unsigned long len, unsigned long pgoff, unsigned long flags);

Regards,

Hans

>
> Signed-off-by: Dillon Min <[email protected]>
> ---
> drivers/media/v4l2-core/v4l2-mem2mem.c | 20 ++++++++++++++++++++
> include/media/v4l2-mem2mem.h | 4 ++++
> 2 files changed, 24 insertions(+)
>
> diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c
> index e7f4bf5bc8dd..f82a18ecab2f 100644
> --- a/drivers/media/v4l2-core/v4l2-mem2mem.c
> +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c
> @@ -966,6 +966,26 @@ int v4l2_m2m_mmap(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
> }
> EXPORT_SYMBOL(v4l2_m2m_mmap);
>
> +#ifndef CONFIG_MMU
> +unsigned long v4l2_m2m_get_unmapped_area(struct file *file, unsigned long addr,
> + unsigned long len, unsigned long pgoff, unsigned long flags)
> +{
> + struct v4l2_fh *fh = file->private_data;
> + unsigned long offset = pgoff << PAGE_SHIFT;
> + struct vb2_queue *vq;
> +
> + if (offset < DST_QUEUE_OFF_BASE) {
> + vq = v4l2_m2m_get_src_vq(fh->m2m_ctx);
> + } else {
> + vq = v4l2_m2m_get_dst_vq(fh->m2m_ctx);
> + pgoff -= (DST_QUEUE_OFF_BASE >> PAGE_SHIFT);
> + }
> +
> + return vb2_get_unmapped_area(vq, addr, len, pgoff, flags);
> +}
> +EXPORT_SYMBOL_GPL(v4l2_m2m_get_unmapped_area);
> +#endif
> +
> #if defined(CONFIG_MEDIA_CONTROLLER)
> void v4l2_m2m_unregister_media_controller(struct v4l2_m2m_dev *m2m_dev)
> {
> diff --git a/include/media/v4l2-mem2mem.h b/include/media/v4l2-mem2mem.h
> index 5a91b548ecc0..91269227c265 100644
> --- a/include/media/v4l2-mem2mem.h
> +++ b/include/media/v4l2-mem2mem.h
> @@ -495,6 +495,10 @@ __poll_t v4l2_m2m_poll(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
> int v4l2_m2m_mmap(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
> struct vm_area_struct *vma);
>
> +#ifndef CONFIG_MMU
> +unsigned long v4l2_m2m_get_unmapped_area(struct file *file, unsigned long addr,
> + unsigned long len, unsigned long pgoff, unsigned long flags);
> +#endif
> /**
> * v4l2_m2m_init() - initialize per-driver m2m data
> *
>

2021-06-17 10:24:57

by Hans Verkuil

[permalink] [raw]
Subject: Re: [PATCH 7/7] media: stm32-dma2d: STM32 DMA2D driver

Hi Dillon,

Please run checkpatch.pl --strict over this patch: I get too many 'CHECK' warnings.

On 20/05/2021 13:05, [email protected] wrote:
> From: Dillon Min <[email protected]>
>
> This V4L2 subdev m2m driver enables Chrom-Art Accelerator unit
> of STMicroelectronics STM32 SoC series.
>
> Currently support r2m, m2m, m2m_pfc.

What does this mean?

>
> Signed-off-by: Dillon Min <[email protected]>
> ---
> drivers/media/platform/Kconfig | 9 +
> drivers/media/platform/Makefile | 1 +
> drivers/media/platform/stm32/Makefile | 2 +
> drivers/media/platform/stm32/dma2d/dma2d-hw.c | 143 +++++
> drivers/media/platform/stm32/dma2d/dma2d-regs.h | 114 ++++
> drivers/media/platform/stm32/dma2d/dma2d.c | 791 ++++++++++++++++++++++++
> drivers/media/platform/stm32/dma2d/dma2d.h | 132 ++++
> 7 files changed, 1192 insertions(+)
> create mode 100644 drivers/media/platform/stm32/dma2d/dma2d-hw.c
> create mode 100644 drivers/media/platform/stm32/dma2d/dma2d-regs.h
> create mode 100644 drivers/media/platform/stm32/dma2d/dma2d.c
> create mode 100644 drivers/media/platform/stm32/dma2d/dma2d.h
>
> diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
> index 157c924686e4..128f4420623f 100644
> --- a/drivers/media/platform/Kconfig
> +++ b/drivers/media/platform/Kconfig
> @@ -244,6 +244,15 @@ config VIDEO_CODA
> config VIDEO_IMX_VDOA
> def_tristate VIDEO_CODA if SOC_IMX6Q || COMPILE_TEST
>
> +config VIDEO_STM32_DMA2D
> + tristate "STM32 Chrom-Art Accelerator (DMA2D)"
> + depends on VIDEO_DEV && VIDEO_V4L2 && ARCH_STM32

This needs to depend on COMPILE_TEST as well.

> + select VIDEOBUF2_DMA_CONTIG
> + select V4L2_MEM2MEM_DEV
> + help
> + The STM32 DMA2D is a memory-to-memory engine for pixel conversion,
> + specialized DMA dedicated to image manipulation

Missing period at the end. And the , should probably be replaced with 'and'.

> +
> config VIDEO_IMX_PXP
> tristate "i.MX Pixel Pipeline (PXP)"
> depends on VIDEO_DEV && VIDEO_V4L2 && (ARCH_MXC || COMPILE_TEST)
> diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
> index eedc14aafb32..93b2b319a2b2 100644
> --- a/drivers/media/platform/Makefile
> +++ b/drivers/media/platform/Makefile
> @@ -69,6 +69,7 @@ obj-$(CONFIG_VIDEO_ATMEL_ISC) += atmel/
> obj-$(CONFIG_VIDEO_ATMEL_ISI) += atmel/
>
> obj-$(CONFIG_VIDEO_STM32_DCMI) += stm32/
> +obj-$(CONFIG_VIDEO_STM32_DMA2D) += stm32/
>
> obj-$(CONFIG_VIDEO_MEDIATEK_VPU) += mtk-vpu/
>
> diff --git a/drivers/media/platform/stm32/Makefile b/drivers/media/platform/stm32/Makefile
> index 48b36db2c2e2..896ef98a73ab 100644
> --- a/drivers/media/platform/stm32/Makefile
> +++ b/drivers/media/platform/stm32/Makefile
> @@ -1,2 +1,4 @@
> # SPDX-License-Identifier: GPL-2.0-only
> obj-$(CONFIG_VIDEO_STM32_DCMI) += stm32-dcmi.o
> +stm32-dma2d-objs := dma2d/dma2d.o dma2d/dma2d-hw.o
> +obj-$(CONFIG_VIDEO_STM32_DMA2D) += stm32-dma2d.o
> diff --git a/drivers/media/platform/stm32/dma2d/dma2d-hw.c b/drivers/media/platform/stm32/dma2d/dma2d-hw.c
> new file mode 100644
> index 000000000000..6fdbf5a21151
> --- /dev/null
> +++ b/drivers/media/platform/stm32/dma2d/dma2d-hw.c
> @@ -0,0 +1,143 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * ST stm32 Chrom-Art - 2D Graphics Accelerator Driver
> + *
> + * Copyright (c) 2021 Dillon Min
> + * Dillon Min, <[email protected]>
> + *
> + * based on s5p-g2d
> + *
> + * Copyright (c) 2011 Samsung Electronics Co., Ltd.
> + * Kamil Debski, <[email protected]>
> + */
> +
> +#include <linux/io.h>
> +
> +#include "dma2d.h"
> +#include "dma2d-regs.h"
> +
> +static inline u32 reg_read(void __iomem *base, u32 reg)
> +{
> + return readl_relaxed(base + reg);
> +}
> +
> +static inline void reg_write(void __iomem *base, u32 reg, u32 val)
> +{
> + writel_relaxed(val, base + reg);
> +}
> +
> +static inline void reg_set(void __iomem *base, u32 reg, u32 mask)
> +{
> + reg_write(base, reg, reg_read(base, reg) | mask);
> +}
> +
> +static inline void reg_clear(void __iomem *base, u32 reg, u32 mask)
> +{
> + reg_write(base, reg, reg_read(base, reg) & ~mask);
> +}
> +
> +static inline void reg_update_bits(void __iomem *base, u32 reg, u32 mask,
> + u32 val)
> +{
> + reg_write(base, reg, (reg_read(base, reg) & ~mask) | val);
> +}
> +
> +void dma2d_start(struct dma2d_dev *d)
> +{
> + reg_update_bits(d->regs, DMA2D_CR_REG, CR_START, CR_START);
> +}
> +
> +u32 dma2d_get_int(struct dma2d_dev *d)
> +{
> + return reg_read(d->regs, DMA2D_ISR_REG);
> +}
> +
> +void dma2d_clear_int(struct dma2d_dev *d)
> +{
> + u32 isr_val = reg_read(d->regs, DMA2D_ISR_REG);
> +
> + reg_write(d->regs, DMA2D_IFCR_REG, isr_val & 0x003f);
> +}
> +
> +void dma2d_config_common(struct dma2d_dev *d, enum dma2d_op_mode op_mode,
> + u16 width, u16 height)
> +{
> + reg_update_bits(d->regs, DMA2D_CR_REG, CR_MODE_MASK,
> + op_mode << CR_MODE_SHIFT);
> +
> + reg_write(d->regs, DMA2D_NLR_REG, (width << 16) | height);
> +}
> +
> +void dma2d_config_out(struct dma2d_dev *d, struct dma2d_frame *frm,
> + dma_addr_t o_addr)
> +{
> + reg_update_bits(d->regs, DMA2D_CR_REG, CR_CEIE, CR_CEIE);
> + reg_update_bits(d->regs, DMA2D_CR_REG, CR_CTCIE, CR_CTCIE);
> + reg_update_bits(d->regs, DMA2D_CR_REG, CR_CAEIE, CR_CAEIE);
> + reg_update_bits(d->regs, DMA2D_CR_REG, CR_TCIE, CR_TCIE);
> + reg_update_bits(d->regs, DMA2D_CR_REG, CR_TEIE, CR_TEIE);
> +
> + if (frm->fmt->cmode >= CM_MODE_ARGB8888 &&
> + frm->fmt->cmode <= CM_MODE_ARGB4444)
> + reg_update_bits(d->regs, DMA2D_OPFCCR_REG, OPFCCR_CM_MASK,
> + frm->fmt->cmode);
> +
> + reg_write(d->regs, DMA2D_OMAR_REG, o_addr);
> +
> + reg_write(d->regs, DMA2D_OCOLR_REG,
> + (frm->a_rgb[3] << 24) |
> + (frm->a_rgb[2] << 16) |
> + (frm->a_rgb[1] << 8) |
> + frm->a_rgb[0]);
> +
> + reg_update_bits(d->regs, DMA2D_OOR_REG, OOR_LO_MASK,
> + frm->line_ofs & 0x3fff);
> +}
> +
> +void dma2d_config_fg(struct dma2d_dev *d, struct dma2d_frame *frm,
> + dma_addr_t f_addr)
> +{
> + reg_write(d->regs, DMA2D_FGMAR_REG, f_addr);
> + reg_update_bits(d->regs, DMA2D_FGOR_REG, FGOR_LO_MASK,
> + frm->line_ofs);
> +
> + if (frm->fmt->cmode >= CM_MODE_ARGB8888 &&
> + frm->fmt->cmode <= CM_MODE_A4)
> + reg_update_bits(d->regs, DMA2D_FGPFCCR_REG, FGPFCCR_CM_MASK,
> + frm->fmt->cmode);
> +
> + reg_update_bits(d->regs, DMA2D_FGPFCCR_REG, FGPFCCR_AM_MASK,
> + (frm->a_mode << 16) & 0x03);
> +
> + reg_update_bits(d->regs, DMA2D_FGPFCCR_REG, FGPFCCR_ALPHA_MASK,
> + frm->a_rgb[3] << 24);
> +
> + reg_write(d->regs, DMA2D_FGCOLR_REG,
> + (frm->a_rgb[2] << 16) |
> + (frm->a_rgb[1] << 8) |
> + frm->a_rgb[0]);
> +}
> +
> +void dma2d_config_bg(struct dma2d_dev *d, struct dma2d_frame *frm,
> + dma_addr_t b_addr)
> +{
> + reg_write(d->regs, DMA2D_BGMAR_REG, b_addr);
> + reg_update_bits(d->regs, DMA2D_BGOR_REG, BGOR_LO_MASK,
> + frm->line_ofs);
> +
> + if (frm->fmt->cmode >= CM_MODE_ARGB8888 &&
> + frm->fmt->cmode <= CM_MODE_A4)
> + reg_update_bits(d->regs, DMA2D_BGPFCCR_REG, BGPFCCR_CM_MASK,
> + frm->fmt->cmode);
> +
> + reg_update_bits(d->regs, DMA2D_BGPFCCR_REG, BGPFCCR_AM_MASK,
> + (frm->a_mode << 16) & 0x03);
> +
> + reg_update_bits(d->regs, DMA2D_BGPFCCR_REG, BGPFCCR_ALPHA_MASK,
> + frm->a_rgb[3] << 24);
> +
> + reg_write(d->regs, DMA2D_BGCOLR_REG,
> + (frm->a_rgb[2] << 16) |
> + (frm->a_rgb[1] << 8) |
> + frm->a_rgb[0]);
> +}
> diff --git a/drivers/media/platform/stm32/dma2d/dma2d-regs.h b/drivers/media/platform/stm32/dma2d/dma2d-regs.h
> new file mode 100644
> index 000000000000..6e375bcb1281
> --- /dev/null
> +++ b/drivers/media/platform/stm32/dma2d/dma2d-regs.h
> @@ -0,0 +1,114 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * ST stm32 Chrom-Art - 2D Graphics Accelerator Driver
> + *
> + * Copyright (c) 2021 Dillon Min
> + * Dillon Min, <[email protected]>
> + *
> + * based on s5p-g2d
> + *
> + * Copyright (c) 2011 Samsung Electronics Co., Ltd.
> + * Kamil Debski, <[email protected]>
> + */
> +
> +#ifndef __DMA2D_REGS_H__
> +#define __DMA2D_REGS_H__
> +
> +#define DMA2D_CR_REG 0x0000
> +#define CR_MODE_MASK GENMASK(17, 16)
> +#define CR_MODE_SHIFT 16
> +#define CR_M2M 0x0000
> +#define CR_M2M_PFC BIT(16)
> +#define CR_M2M_BLEND BIT(17)
> +#define CR_R2M (BIT(17) | BIT(16))
> +#define CR_CEIE BIT(13)
> +#define CR_CTCIE BIT(12)
> +#define CR_CAEIE BIT(11)
> +#define CR_TWIE BIT(10)
> +#define CR_TCIE BIT(9)
> +#define CR_TEIE BIT(8)
> +#define CR_ABORT BIT(2)
> +#define CR_SUSP BIT(1)
> +#define CR_START BIT(0)
> +
> +#define DMA2D_ISR_REG 0x0004
> +#define ISR_CEIF BIT(5)
> +#define ISR_CTCIF BIT(4)
> +#define ISR_CAEIF BIT(3)
> +#define ISR_TWIF BIT(2)
> +#define ISR_TCIF BIT(1)
> +#define ISR_TEIF BIT(0)
> +
> +#define DMA2D_IFCR_REG 0x0008
> +#define IFCR_CCEIF BIT(5)
> +#define IFCR_CCTCIF BIT(4)
> +#define IFCR_CAECIF BIT(3)
> +#define IFCR_CTWIF BIT(2)
> +#define IFCR_CTCIF BIT(1)
> +#define IFCR_CTEIF BIT(0)
> +
> +#define DMA2D_FGMAR_REG 0x000c
> +#define DMA2D_FGOR_REG 0x0010
> +#define FGOR_LO_MASK GENMASK(13, 0)
> +
> +#define DMA2D_BGMAR_REG 0x0014
> +#define DMA2D_BGOR_REG 0x0018
> +#define BGOR_LO_MASK GENMASK(13, 0)
> +
> +#define DMA2D_FGPFCCR_REG 0x001c
> +#define FGPFCCR_ALPHA_MASK GENMASK(31, 24)
> +#define FGPFCCR_AM_MASK GENMASK(17, 16)
> +#define FGPFCCR_CS_MASK GENMASK(15, 8)
> +#define FGPFCCR_START BIT(5)
> +#define FGPFCCR_CCM_RGB888 BIT(4)
> +#define FGPFCCR_CM_MASK GENMASK(3, 0)
> +
> +#define DMA2D_FGCOLR_REG 0x0020
> +#define FGCOLR_REG_MASK GENMASK(23, 16)
> +#define FGCOLR_GREEN_MASK GENMASK(15, 8)
> +#define FGCOLR_BLUE_MASK GENMASK(7, 0)
> +
> +#define DMA2D_BGPFCCR_REG 0x0024
> +#define BGPFCCR_ALPHA_MASK GENMASK(31, 24)
> +#define BGPFCCR_AM_MASK GENMASK(17, 16)
> +#define BGPFCCR_CS_MASK GENMASK(15, 8)
> +#define BGPFCCR_START BIT(5)
> +#define BGPFCCR_CCM_RGB888 BIT(4)
> +#define BGPFCCR_CM_MASK GENMASK(3, 0)
> +
> +#define DMA2D_BGCOLR_REG 0x0028
> +#define BGCOLR_REG_MASK GENMASK(23, 16)
> +#define BGCOLR_GREEN_MASK GENMASK(15, 8)
> +#define BGCOLR_BLUE_MASK GENMASK(7, 0)
> +
> +#define DMA2D_OPFCCR_REG 0x0034
> +#define OPFCCR_CM_MASK GENMASK(2, 0)
> +
> +#define DMA2D_OCOLR_REG 0x0038
> +#define OCOLR_ALPHA_MASK GENMASK(31, 24)
> +#define OCOLR_RED_MASK GENMASK(23, 16)
> +#define OCOLR_GREEN_MASK GENMASK(15, 8)
> +#define OCOLR_BLUE_MASK GENMASK(7, 0)
> +
> +#define DMA2D_OMAR_REG 0x003c
> +
> +#define DMA2D_OOR_REG 0x0040
> +#define OOR_LO_MASK GENMASK(13, 0)
> +
> +#define DMA2D_NLR_REG 0x0044
> +#define NLR_PL_MASK GENMASK(29, 16)
> +#define NLR_NL_MASK GENMASK(15, 0)
> +
> +
> +/* Hardware limits */
> +#define MAX_WIDTH 0x3fff
> +#define MAX_HEIGHT 0xffff
> +
> +#define DEFAULT_WIDTH 240
> +#define DEFAULT_HEIGHT 320
> +#define DEFAULT_SIZE 307200
> +
> +#define CM_MODE_ARGB8888 0x00
> +#define CM_MODE_ARGB4444 0x04
> +#define CM_MODE_A4 0x0a
> +#endif /* __DMA2D_REGS_H__ */
> diff --git a/drivers/media/platform/stm32/dma2d/dma2d.c b/drivers/media/platform/stm32/dma2d/dma2d.c
> new file mode 100644
> index 000000000000..88f8333a174a
> --- /dev/null
> +++ b/drivers/media/platform/stm32/dma2d/dma2d.c
> @@ -0,0 +1,791 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * STM32 DMA2D - 2D Graphics Accelerator Driver
> + *
> + * Copyright (c) 2021 Dillon Min
> + * Dillon Min, <[email protected]>
> + *
> + * based on s5p-g2d
> + *
> + * Copyright (c) 2011 Samsung Electronics Co., Ltd.
> + * Kamil Debski, <[email protected]>
> + */
> +
> +#include <linux/module.h>
> +#include <linux/fs.h>
> +#include <linux/timer.h>
> +#include <linux/sched.h>
> +#include <linux/slab.h>
> +#include <linux/clk.h>
> +#include <linux/interrupt.h>
> +#include <linux/of.h>
> +
> +#include <linux/platform_device.h>
> +#include <media/v4l2-mem2mem.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-ioctl.h>
> +#include <media/v4l2-event.h>
> +#include <media/videobuf2-v4l2.h>
> +#include <media/videobuf2-dma-contig.h>
> +
> +#include "dma2d.h"
> +#include "dma2d-regs.h"
> +
> +#define fh2ctx(__fh) container_of(__fh, struct dma2d_ctx, fh)
> +
> +static struct dma2d_fmt formats[] = {

Can this be const?

> + {
> + .fourcc = V4L2_PIX_FMT_ARGB32,
> + .cmode = DMA2D_CMODE_ARGB8888,
> + .depth = 32,
> + },
> + {
> + .fourcc = V4L2_PIX_FMT_RGB24,
> + .cmode = DMA2D_CMODE_RGB888,
> + .depth = 24,
> + },
> + {
> + .fourcc = V4L2_PIX_FMT_RGB565,
> + .cmode = DMA2D_CMODE_RGB565,
> + .depth = 16,
> + },
> + {
> + .fourcc = V4L2_PIX_FMT_ARGB555,
> + .cmode = DMA2D_CMODE_ARGB1555,
> + .depth = 16,
> + },
> + {
> + .fourcc = V4L2_PIX_FMT_ARGB444,
> + .cmode = DMA2D_CMODE_ARGB4444,
> + .depth = 16,
> + },
> +};
> +#define NUM_FORMATS ARRAY_SIZE(formats)
> +
> +static struct dma2d_frame def_frame = {

Ditto.

> + .width = DEFAULT_WIDTH,
> + .height = DEFAULT_HEIGHT,
> + .line_ofs = 0,

Does 'ofs' stand for 'offset'? If so, then I prefer the full 'offset' over 'ofs'.
'ofs' is a bit obscure.

> + .a_rgb = {0x00, 0x00, 0x00, 0xff},
> + .a_mode = DMA2D_ALPHA_MODE_NO_MODIF,
> + .fmt = &formats[0],
> + .size = DEFAULT_SIZE,
> +};
> +
> +static struct dma2d_fmt *find_fmt(int pixelformat)
> +{
> + unsigned int i;
> +
> + for (i = 0; i < NUM_FORMATS; i++) {
> + if (formats[i].fourcc == pixelformat)
> + return &formats[i];
> + }
> +
> + return NULL;
> +}
> +
> +static struct dma2d_frame *get_frame(struct dma2d_ctx *ctx,
> + enum v4l2_buf_type type)
> +{
> + switch (type) {
> + case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> + return &ctx->fg;
> + case V4L2_BUF_TYPE_VIDEO_CAPTURE:
> + return &ctx->out;

Hmm, capture returns ctx->out, and output returns ctx->fg. Not exactly what
you would expect.

> + default:
> + return ERR_PTR(-EINVAL);

Using error pointers here is a bit overkill IMHO. I think returning NULL is
typical. With a WARN_ON perhaps since this really shouldn't happen.

Alternatively just do this:

return V4L2_TYPE_IS_OUTPUT(type) ? &ctx->fg : &ctx->out;

It avoids the whole problem.

> + }
> +}
> +
> +static int dma2d_queue_setup(struct vb2_queue *vq,
> + unsigned int *nbuffers, unsigned int *nplanes,
> + unsigned int sizes[], struct device *alloc_devs[])
> +{
> + struct dma2d_ctx *ctx = vb2_get_drv_priv(vq);
> + struct dma2d_frame *f = get_frame(ctx, vq->type);
> +
> + if (IS_ERR(f))
> + return PTR_ERR(f);
> +
> + if (*nplanes)
> + return sizes[0] < f->size ? -EINVAL : 0;
> +
> + sizes[0] = f->size;
> + *nplanes = 1;
> +
> + if (*nbuffers == 0)
> + *nbuffers = 1;
> +
> + return 0;
> +}
> +
> +static int dma2d_buf_out_validate(struct vb2_buffer *vb)
> +{
> + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
> +
> + if (vbuf->field == V4L2_FIELD_ANY)
> + vbuf->field = V4L2_FIELD_NONE;
> + if (vbuf->field != V4L2_FIELD_NONE)
> + return -EINVAL;
> +
> + return 0;
> +}
> +
> +static int dma2d_buf_prepare(struct vb2_buffer *vb)
> +{
> + struct dma2d_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
> + struct dma2d_frame *f = get_frame(ctx, vb->vb2_queue->type);
> +
> + if (IS_ERR(f))
> + return PTR_ERR(f);
> +
> + if (vb2_plane_size(vb, 0) < f->size)
> + return -EINVAL;
> +
> + vb2_set_plane_payload(vb, 0, f->size);
> +
> + return 0;
> +}
> +
> +static void dma2d_buf_queue(struct vb2_buffer *vb)
> +{
> + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
> + struct dma2d_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
> +
> + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
> +}
> +
> +static int dma2d_start_streaming(struct vb2_queue *q, unsigned int count)
> +{
> + struct dma2d_ctx *ctx = vb2_get_drv_priv(q);
> + struct dma2d_frame *f = get_frame(ctx, q->type);
> +
> + if (IS_ERR(f))
> + return -EINVAL;
> +
> + f->sequence = 0;
> + return 0;
> +}
> +
> +static void dma2d_stop_streaming(struct vb2_queue *q)
> +{
> + struct dma2d_ctx *ctx = vb2_get_drv_priv(q);
> + struct vb2_v4l2_buffer *vbuf;
> +
> + for (;;) {
> + if (V4L2_TYPE_IS_OUTPUT(q->type))
> + vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
> + else
> + vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
> + if (!vbuf)
> + return;
> + v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
> + }
> +}
> +
> +static const struct vb2_ops dma2d_qops = {
> + .queue_setup = dma2d_queue_setup,
> + .buf_out_validate = dma2d_buf_out_validate,
> + .buf_prepare = dma2d_buf_prepare,
> + .buf_queue = dma2d_buf_queue,
> + .start_streaming = dma2d_start_streaming,
> + .stop_streaming = dma2d_stop_streaming,
> + .wait_prepare = vb2_ops_wait_prepare,
> + .wait_finish = vb2_ops_wait_finish,
> +};
> +
> +static int queue_init(void *priv, struct vb2_queue *src_vq,
> + struct vb2_queue *dst_vq)
> +{
> + struct dma2d_ctx *ctx = priv;
> + int ret;
> +
> + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
> + src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
> + src_vq->drv_priv = ctx;
> + src_vq->ops = &dma2d_qops;
> + src_vq->mem_ops = &vb2_dma_contig_memops;
> + src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
> + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
> + src_vq->lock = &ctx->dev->mutex;
> + src_vq->dev = ctx->dev->v4l2_dev.dev;
> +
> + ret = vb2_queue_init(src_vq);
> + if (ret)
> + return ret;
> +
> + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> + dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
> + dst_vq->drv_priv = ctx;
> + dst_vq->ops = &dma2d_qops;
> + dst_vq->mem_ops = &vb2_dma_contig_memops;
> + dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
> + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
> + dst_vq->lock = &ctx->dev->mutex;
> + dst_vq->dev = ctx->dev->v4l2_dev.dev;
> +
> + return vb2_queue_init(dst_vq);
> +}
> +
> +#define V4L2_CID_DMA2D_R2M_COLOR (V4L2_CID_USER_BASE | 0x1200)
> +#define V4L2_CID_DMA2D_R2M_MODE (V4L2_CID_USER_BASE | 0x1201)

You need to reserve a range for driver-specific controls in include/uapi/linux/v4l2-controls.h.
Search for V4L2_CID_USER_CCS_BASE to see how it is done.

> +static int dma2d_s_ctrl(struct v4l2_ctrl *ctrl)
> +{
> + struct dma2d_frame *frm;
> + struct dma2d_ctx *ctx = container_of(ctrl->handler, struct dma2d_ctx,
> + ctrl_handler);
> + unsigned long flags;
> +
> + spin_lock_irqsave(&ctx->dev->ctrl_lock, flags);
> + switch (ctrl->id) {
> + case V4L2_CID_ALPHA_COMPONENT:
> + /* set the background alpha value*/
> + ctx->alpha_component = (u8) ctrl->val;
> + break;
> + case V4L2_CID_DMA2D_R2M_COLOR:
> + frm = get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
> + frm->a_rgb[3] = (ctrl->val >> 24) & 0xff;
> + frm->a_rgb[2] = (ctrl->val >> 16) & 0xff;
> + frm->a_rgb[1] = (ctrl->val >> 8) & 0xff;
> + frm->a_rgb[0] = ctrl->val & 0xff;
> + break;
> + case V4L2_CID_DMA2D_R2M_MODE:
> + if (ctrl->val)
> + ctx->op_mode = DMA2D_MODE_R2M;
> + break;
> + default:
> + v4l2_err(&ctx->dev->v4l2_dev, "Invalid control\n");
> + spin_unlock_irqrestore(&ctx->dev->ctrl_lock, flags);
> + return -EINVAL;
> +
> + }
> + spin_unlock_irqrestore(&ctx->dev->ctrl_lock, flags);
> +
> + return 0;
> +}
> +
> +static const struct v4l2_ctrl_ops dma2d_ctrl_ops = {
> + .s_ctrl = dma2d_s_ctrl,
> +};
> +
> +static const struct v4l2_ctrl_config dma2d_r2m_control[] = {
> + {
> + .ops = &dma2d_ctrl_ops,
> + .id = V4L2_CID_DMA2D_R2M_COLOR,
> + .name = "R2M Alpha/Color Value",
> + .type = V4L2_CTRL_TYPE_INTEGER,
> + .min = 0xffffffff80000000ULL,
> + .max = 0x7fffffff,
> + .def = 0,
> + .step = 1,
> + },
> + {
> + .ops = &dma2d_ctrl_ops,
> + .id = V4L2_CID_DMA2D_R2M_MODE,
> + .name = "Set to r2m mode",

"Set R2M Mode"

These controls need to be documented.

> + .type = V4L2_CTRL_TYPE_BOOLEAN,
> + .min = 0,
> + .max = 1,
> + .def = 0,
> + .step = 1,
> + }
> +};
> +
> +static int dma2d_setup_ctrls(struct dma2d_ctx *ctx)
> +{
> + v4l2_ctrl_handler_init(&ctx->ctrl_handler, 3);
> +
> + v4l2_ctrl_new_std(&ctx->ctrl_handler, &dma2d_ctrl_ops,
> + V4L2_CID_ALPHA_COMPONENT, 0, 255, 1, 255);
> + v4l2_ctrl_new_custom(&ctx->ctrl_handler, &dma2d_r2m_control[0], NULL);
> + v4l2_ctrl_new_custom(&ctx->ctrl_handler, &dma2d_r2m_control[1], NULL);
> +
> + return 0;
> +}
> +
> +static int dma2d_open(struct file *file)
> +{
> + struct dma2d_dev *dev = video_drvdata(file);
> + struct dma2d_ctx *ctx = NULL;
> + int ret = 0;
> +
> + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
> + if (!ctx)
> + return -ENOMEM;
> + ctx->dev = dev;
> + /* Set default formats */
> + ctx->fg = def_frame;
> + ctx->bg = def_frame;
> + ctx->out = def_frame;
> + ctx->op_mode = DMA2D_MODE_M2M_FPC;
> + ctx->colorspace = V4L2_COLORSPACE_REC709;
> + ctx->alpha_component = 0x00;
> + if (mutex_lock_interruptible(&dev->mutex)) {
> + kfree(ctx);
> + return -ERESTARTSYS;
> + }
> +
> + ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init);
> + if (IS_ERR(ctx->fh.m2m_ctx)) {
> + ret = PTR_ERR(ctx->fh.m2m_ctx);
> + mutex_unlock(&dev->mutex);
> + kfree(ctx);
> + return ret;
> + }
> +
> + v4l2_fh_init(&ctx->fh, video_devdata(file));
> + file->private_data = &ctx->fh;
> + v4l2_fh_add(&ctx->fh);
> +
> + dma2d_setup_ctrls(ctx);
> +
> + /* Write the default values to the ctx struct */
> + v4l2_ctrl_handler_setup(&ctx->ctrl_handler);
> +
> + ctx->fh.ctrl_handler = &ctx->ctrl_handler;
> + mutex_unlock(&dev->mutex);
> +
> + return 0;
> +}
> +
> +static int dma2d_release(struct file *file)
> +{
> + struct dma2d_dev *dev = video_drvdata(file);
> + struct dma2d_ctx *ctx = fh2ctx(file->private_data);
> +
> + v4l2_ctrl_handler_free(&ctx->ctrl_handler);
> + v4l2_fh_del(&ctx->fh);
> + v4l2_fh_exit(&ctx->fh);
> + mutex_lock(&dev->mutex);
> + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
> + mutex_unlock(&dev->mutex);
> + kfree(ctx);
> +
> + return 0;
> +}
> +
> +static int vidioc_querycap(struct file *file, void *priv,
> + struct v4l2_capability *cap)
> +{
> + strscpy(cap->driver, DMA2D_NAME, sizeof(cap->driver));
> + strscpy(cap->card, DMA2D_NAME, sizeof(cap->card));
> + strscpy(cap->bus_info, BUS_INFO, sizeof(cap->bus_info));
> +
> + return 0;
> +}
> +
> +static int vidioc_enum_fmt(struct file *file, void *prv, struct v4l2_fmtdesc *f)
> +{
> + if (f->index >= NUM_FORMATS)
> + return -EINVAL;
> +
> + f->pixelformat = formats[f->index].fourcc;
> + return 0;
> +}
> +
> +static int vidioc_g_fmt(struct file *file, void *prv, struct v4l2_format *f)
> +{
> + struct dma2d_ctx *ctx = prv;
> + struct vb2_queue *vq;
> + struct dma2d_frame *frm;
> +
> + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
> + if (!vq)
> + return -EINVAL;
> +
> + frm = get_frame(ctx, f->type);
> + if (IS_ERR(frm))
> + return PTR_ERR(frm);
> +
> + f->fmt.pix.width = frm->width;
> + f->fmt.pix.height = frm->height;
> + f->fmt.pix.field = V4L2_FIELD_NONE;
> + f->fmt.pix.pixelformat = frm->fmt->fourcc;
> + f->fmt.pix.bytesperline = (frm->width * frm->fmt->depth) >> 3;
> + f->fmt.pix.sizeimage = frm->size;
> + f->fmt.pix.colorspace = ctx->colorspace;
> + f->fmt.pix.xfer_func = ctx->xfer_func;
> + f->fmt.pix.ycbcr_enc = ctx->ycbcr_enc;
> + f->fmt.pix.quantization = ctx->quant;
> +
> + return 0;
> +}
> +
> +static int vidioc_try_fmt(struct file *file, void *prv, struct v4l2_format *f)
> +{
> + struct dma2d_ctx *ctx = prv;
> + struct dma2d_dev *dev = ctx->dev;
> + struct dma2d_fmt *fmt;
> + enum v4l2_field *field;
> + u32 fourcc = f->fmt.pix.pixelformat;
> +
> + fmt = find_fmt(fourcc);
> + if (!fmt) {
> + v4l2_warn(&dev->v4l2_dev,
> + "Format not supported: %c%c%c%c, use the default.\n",
> + (fourcc & 0xff), (fourcc >> 8) & 0xff,
> + (fourcc >> 16) & 0xff, (fourcc >> 24) & 0xff);
> + f->fmt.pix.pixelformat = formats[0].fourcc;
> + fmt = find_fmt(f->fmt.pix.pixelformat);
> + }
> +
> + field = &f->fmt.pix.field;
> + if (*field == V4L2_FIELD_ANY)
> + *field = V4L2_FIELD_NONE;
> + else if (*field != V4L2_FIELD_NONE)
> + return -EINVAL;
> +
> + if (f->fmt.pix.width > MAX_WIDTH)
> + f->fmt.pix.width = MAX_WIDTH;
> + if (f->fmt.pix.height > MAX_HEIGHT)
> + f->fmt.pix.height = MAX_HEIGHT;
> +
> + if (f->fmt.pix.width < 1)
> + f->fmt.pix.width = 1;
> + if (f->fmt.pix.height < 1)
> + f->fmt.pix.height = 1;
> +
> + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT && !f->fmt.pix.colorspace)
> + f->fmt.pix.colorspace = V4L2_COLORSPACE_REC709;
> + else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
> + f->fmt.pix.colorspace = ctx->colorspace;
> + f->fmt.pix.xfer_func = ctx->xfer_func;
> + f->fmt.pix.ycbcr_enc = ctx->ycbcr_enc;
> + f->fmt.pix.quantization = ctx->quant;
> + }
> + f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3;
> + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
> +
> + return 0;
> +}
> +
> +static int vidioc_s_fmt(struct file *file, void *prv, struct v4l2_format *f)
> +{
> + struct dma2d_ctx *ctx = prv;
> + struct dma2d_dev *dev = ctx->dev;
> + struct vb2_queue *vq;
> + struct dma2d_frame *frm;
> + struct dma2d_fmt *fmt;
> + int ret = 0;
> +
> + /* Adjust all values accordingly to the hardware capabilities
> + * and chosen format.
> + */
> + ret = vidioc_try_fmt(file, prv, f);
> + if (ret)
> + return ret;
> +
> + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
> + if (vb2_is_busy(vq)) {
> + v4l2_err(&dev->v4l2_dev, "queue (%d) bust\n", f->type);
> + return -EBUSY;
> + }
> +
> + frm = get_frame(ctx, f->type);
> + if (IS_ERR(frm))
> + return PTR_ERR(frm);
> +
> + fmt = find_fmt(f->fmt.pix.pixelformat);
> + if (!fmt)
> + return -EINVAL;
> +
> + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
> + ctx->colorspace = f->fmt.pix.colorspace;
> + ctx->xfer_func = f->fmt.pix.xfer_func;
> + ctx->ycbcr_enc = f->fmt.pix.ycbcr_enc;
> + ctx->quant = f->fmt.pix.quantization;
> + }
> +
> + frm->width = f->fmt.pix.width;
> + frm->height = f->fmt.pix.height;
> + frm->size = f->fmt.pix.sizeimage;
> + /* Reset crop settings */
> + frm->o_width = 0;
> + frm->o_height = 0;
> + frm->c_width = frm->width;
> + frm->c_height = frm->height;
> + frm->right = frm->width;
> + frm->bottom = frm->height;
> + frm->fmt = fmt;
> + frm->line_ofs = 0;
> + if (f->fmt.win.global_alpha != 0) {
> + frm->a_rgb[3] = f->fmt.win.global_alpha;
> + frm->a_mode = DMA2D_ALPHA_MODE_REPLACE;
> + }
> +
> + return 0;
> +}
> +
> +static void device_run(void *prv)
> +{
> + struct dma2d_ctx *ctx = prv;
> + struct dma2d_dev *dev = ctx->dev;
> + struct dma2d_frame *frm_out, *frm_cap;
> + struct vb2_v4l2_buffer *src, *dst;
> + unsigned long flags;
> +
> + spin_lock_irqsave(&dev->ctrl_lock, flags);
> + dev->curr = ctx;
> +
> + src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
> + dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
> + if (dst == NULL || src == NULL)
> + goto end;
> +
> + frm_cap = get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
> + frm_out = get_frame(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
> + if (!frm_cap || !frm_out)
> + goto end;
> +
> + src->sequence = frm_out->sequence++;
> + dst->sequence = frm_cap->sequence++;
> + v4l2_m2m_buf_copy_metadata(src, dst, true);
> +
> + clk_enable(dev->gate);
> +
> + dma2d_config_fg(dev, frm_out,
> + vb2_dma_contig_plane_dma_addr(&src->vb2_buf, 0));
> +
> + /* TODO: add M2M_BLEND handler here */
> +
> + if (ctx->op_mode != DMA2D_MODE_R2M) {
> + if (frm_out->fmt->fourcc == frm_cap->fmt->fourcc)
> + ctx->op_mode = DMA2D_MODE_M2M;
> + else
> + ctx->op_mode = DMA2D_MODE_M2M_FPC;
> + }
> +
> + dma2d_config_out(dev, frm_cap,
> + vb2_dma_contig_plane_dma_addr(&dst->vb2_buf, 0));
> + dma2d_config_common(dev, ctx->op_mode, frm_cap->width, frm_cap->height);
> +
> + dma2d_start(dev);
> +end:
> + spin_unlock_irqrestore(&dev->ctrl_lock, flags);
> +}
> +
> +static irqreturn_t dma2d_isr(int irq, void *prv)
> +{
> + struct dma2d_dev *dev = prv;
> + struct dma2d_ctx *ctx = dev->curr;
> + struct vb2_v4l2_buffer *src, *dst;
> + uint32_t s = dma2d_get_int(dev);
> +
> + if (s & ISR_TCIF || s == 0) {
> + dma2d_clear_int(dev);
> + clk_disable(dev->gate);
> +
> + WARN_ON(ctx == NULL);
> +
> + src = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
> + dst = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
> +
> + WARN_ON(dst == NULL);
> + WARN_ON(src == NULL);
> +
> + v4l2_m2m_buf_done(src, VB2_BUF_STATE_DONE);
> + v4l2_m2m_buf_done(dst, VB2_BUF_STATE_DONE);
> + v4l2_m2m_job_finish(dev->m2m_dev, ctx->fh.m2m_ctx);
> +
> + dev->curr = NULL;
> + } else {
> + dma2d_clear_int(dev);
> + }
> +
> + return IRQ_HANDLED;
> +}
> +
> +static const struct v4l2_file_operations dma2d_fops = {
> + .owner = THIS_MODULE,
> + .open = dma2d_open,
> + .release = dma2d_release,
> + .poll = v4l2_m2m_fop_poll,
> + .unlocked_ioctl = video_ioctl2,
> + .mmap = v4l2_m2m_fop_mmap,
> +#ifndef CONFIG_MMU
> + .get_unmapped_area = v4l2_m2m_get_unmapped_area,
> +#endif
> +};
> +
> +static const struct v4l2_ioctl_ops dma2d_ioctl_ops = {
> + .vidioc_querycap = vidioc_querycap,
> +
> + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt,
> + .vidioc_g_fmt_vid_cap = vidioc_g_fmt,
> + .vidioc_try_fmt_vid_cap = vidioc_try_fmt,
> + .vidioc_s_fmt_vid_cap = vidioc_s_fmt,
> +
> + .vidioc_enum_fmt_vid_out = vidioc_enum_fmt,
> + .vidioc_g_fmt_vid_out = vidioc_g_fmt,
> + .vidioc_try_fmt_vid_out = vidioc_try_fmt,
> + .vidioc_s_fmt_vid_out = vidioc_s_fmt,
> +
> + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
> + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
> + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
> + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
> + .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,
> + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
> + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
> +
> + .vidioc_streamon = v4l2_m2m_ioctl_streamon,
> + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
> +
> + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
> + .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
> +};
> +
> +static const struct video_device dma2d_videodev = {
> + .name = DMA2D_NAME,
> + .fops = &dma2d_fops,
> + .ioctl_ops = &dma2d_ioctl_ops,
> + .minor = -1,
> + .release = video_device_release,
> + .vfl_dir = VFL_DIR_M2M,
> +};
> +
> +static const struct v4l2_m2m_ops dma2d_m2m_ops = {
> + .device_run = device_run,
> +};
> +
> +static const struct of_device_id stm32_dma2d_match[];
> +
> +static int dma2d_probe(struct platform_device *pdev)
> +{
> + struct dma2d_dev *dev;
> + struct video_device *vfd;
> + struct resource *res;
> + int ret = 0;
> +
> + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
> + if (!dev)
> + return -ENOMEM;
> +
> + spin_lock_init(&dev->ctrl_lock);
> + mutex_init(&dev->mutex);
> + atomic_set(&dev->num_inst, 0);
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +
> + dev->regs = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(dev->regs))
> + return PTR_ERR(dev->regs);
> +
> + dev->gate = clk_get(&pdev->dev, "dma2d");
> + if (IS_ERR(dev->gate)) {
> + dev_err(&pdev->dev, "failed to get dma2d clock gate\n");
> + ret = -ENXIO;
> + return ret;
> + }
> +
> + ret = clk_prepare(dev->gate);
> + if (ret) {
> + dev_err(&pdev->dev, "failed to prepare dma2d clock gate\n");
> + goto put_clk_gate;
> + }
> +
> + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
> + if (!res) {
> + dev_err(&pdev->dev, "failed to find IRQ\n");
> + ret = -ENXIO;
> + goto unprep_clk_gate;
> + }
> +
> + dev->irq = res->start;
> +
> + ret = devm_request_irq(&pdev->dev, dev->irq, dma2d_isr,
> + 0, pdev->name, dev);
> + if (ret) {
> + dev_err(&pdev->dev, "failed to install IRQ\n");
> + goto unprep_clk_gate;
> + }
> +
> + ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
> + if (ret)
> + goto unprep_clk_gate;
> +
> + vfd = video_device_alloc();
> + if (!vfd) {
> + v4l2_err(&dev->v4l2_dev, "Failed to allocate video device\n");
> + ret = -ENOMEM;
> + goto unreg_v4l2_dev;
> + }
> +
> + *vfd = dma2d_videodev;
> + set_bit(V4L2_FL_QUIRK_INVERTED_CROP, &vfd->flags);
> + vfd->lock = &dev->mutex;
> + vfd->v4l2_dev = &dev->v4l2_dev;
> + vfd->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING;
> + ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0);
> + if (ret) {
> + v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
> + goto rel_vdev;
> + }
> +
> + video_set_drvdata(vfd, dev);
> + dev->vfd = vfd;
> + v4l2_info(&dev->v4l2_dev, "device registered as /dev/video%d\n",
> + vfd->num);
> + platform_set_drvdata(pdev, dev);
> + dev->m2m_dev = v4l2_m2m_init(&dma2d_m2m_ops);
> + if (IS_ERR(dev->m2m_dev)) {
> + v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n");
> + ret = PTR_ERR(dev->m2m_dev);
> + goto unreg_video_dev;
> + }
> +
> + v4l2_info(&dev->v4l2_dev, "stm32 dma2d initialized\n");
> + return 0;
> +
> +unreg_video_dev:
> + video_unregister_device(dev->vfd);
> +rel_vdev:
> + video_device_release(vfd);
> +unreg_v4l2_dev:
> + v4l2_device_unregister(&dev->v4l2_dev);
> +unprep_clk_gate:
> + clk_unprepare(dev->gate);
> +put_clk_gate:
> + clk_put(dev->gate);
> +
> + return ret;
> +}
> +
> +static int dma2d_remove(struct platform_device *pdev)
> +{
> + struct dma2d_dev *dev = platform_get_drvdata(pdev);
> +
> + v4l2_info(&dev->v4l2_dev, "Removing " DMA2D_NAME);
> + v4l2_m2m_release(dev->m2m_dev);
> + video_unregister_device(dev->vfd);
> + v4l2_device_unregister(&dev->v4l2_dev);
> + vb2_dma_contig_clear_max_seg_size(&pdev->dev);
> + clk_unprepare(dev->gate);
> + clk_put(dev->gate);
> +
> + return 0;
> +}
> +
> +static const struct of_device_id stm32_dma2d_match[] = {
> + {
> + .compatible = "st,stm32-dma2d",
> + .data = NULL,
> + },
> + {},
> +};
> +MODULE_DEVICE_TABLE(of, stm32_dma2d_match);
> +
> +static struct platform_driver dma2d_pdrv = {
> + .probe = dma2d_probe,
> + .remove = dma2d_remove,
> + .driver = {
> + .name = DMA2D_NAME,
> + .of_match_table = stm32_dma2d_match,
> + },
> +};
> +
> +module_platform_driver(dma2d_pdrv);
> +
> +MODULE_AUTHOR("Dillon Min <[email protected]>");
> +MODULE_DESCRIPTION("STM32 Chrom-Art Accelerator DMA2D driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/media/platform/stm32/dma2d/dma2d.h b/drivers/media/platform/stm32/dma2d/dma2d.h
> new file mode 100644
> index 000000000000..1c64cd2f7587
> --- /dev/null
> +++ b/drivers/media/platform/stm32/dma2d/dma2d.h
> @@ -0,0 +1,132 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * ST stm32 DMA2D - 2D Graphics Accelerator Driver
> + *
> + * Copyright (c) 2021 Dillon Min
> + * Dillon Min, <[email protected]>
> + *
> + * based on s5p-g2d
> + *
> + * Copyright (c) 2011 Samsung Electronics Co., Ltd.
> + * Kamil Debski, <[email protected]>
> + */
> +
> +#ifndef __DMA2D_H__
> +#define __DMA2D_H__
> +
> +#include <linux/platform_device.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-ctrls.h>
> +
> +#define DMA2D_NAME "stm-dma2d"
> +#define BUS_INFO "platform:stm-dma2d"
> +enum dma2d_op_mode {
> + DMA2D_MODE_M2M,
> + DMA2D_MODE_M2M_FPC,
> + DMA2D_MODE_M2M_BLEND,
> + DMA2D_MODE_R2M
> +};
> +
> +enum dma2d_cmode {
> + /* output pfc cmode from ARGB888 to ARGB4444 */
> + DMA2D_CMODE_ARGB8888,
> + DMA2D_CMODE_RGB888,
> + DMA2D_CMODE_RGB565,
> + DMA2D_CMODE_ARGB1555,
> + DMA2D_CMODE_ARGB4444,
> + /* bg or fg pfc cmode from L8 to A4 */
> + DMA2D_CMODE_L8,
> + DMA2D_CMODE_AL44,
> + DMA2D_CMODE_AL88,
> + DMA2D_CMODE_L4,
> + DMA2D_CMODE_A8,
> + DMA2D_CMODE_A4
> +};
> +
> +enum dma2d_alpha_mode {
> + DMA2D_ALPHA_MODE_NO_MODIF,
> + DMA2D_ALPHA_MODE_REPLACE,
> + DMA2D_ALPHA_MODE_COMBINE
> +};
> +
> +struct dma2d_fmt {
> + u32 fourcc;
> + int depth;
> + enum dma2d_cmode cmode;
> +};
> +
> +struct dma2d_frame {
> + /* Original dimensions */
> + u32 width;
> + u32 height;
> + /* Crop size */
> + u32 c_width;
> + u32 c_height;
> + /* Offset */
> + u32 o_width;
> + u32 o_height;
> + u32 bottom;
> + u32 right;
> + u16 line_ofs;
> + /* Image format */
> + struct dma2d_fmt *fmt;
> + /* [0]: blue
> + * [1]: green
> + * [2]: red
> + * [3]: alpha
> + */
> + u8 a_rgb[4];
> + /*
> + * AM[1:0] of DMA2D_FGPFCCR
> + */
> + enum dma2d_alpha_mode a_mode;
> + u32 size;
> + unsigned int sequence;
> +};
> +
> +struct dma2d_ctx {
> + struct v4l2_fh fh;
> + struct dma2d_dev *dev;
> + struct dma2d_frame fg;
> + struct dma2d_frame out;
> + struct dma2d_frame bg;
> + /* fb_buf always point to bg address */
> + struct v4l2_framebuffer fb_buf;
> + /*
> + * MODE[17:16] of DMA2D_CR
> + */
> + enum dma2d_op_mode op_mode;
> + u8 alpha_component;
> + struct v4l2_ctrl_handler ctrl_handler;
> + enum v4l2_colorspace colorspace;
> + enum v4l2_ycbcr_encoding ycbcr_enc;
> + enum v4l2_xfer_func xfer_func;
> + enum v4l2_quantization quant;
> +};
> +
> +struct dma2d_dev {
> + struct v4l2_device v4l2_dev;
> + struct v4l2_m2m_dev *m2m_dev;
> + struct video_device *vfd;
> + struct mutex mutex;
> + spinlock_t ctrl_lock;
> + atomic_t num_inst;
> + void __iomem *regs;
> + struct clk *gate;
> + struct dma2d_ctx *curr;
> + int irq;
> +};
> +
> +void dma2d_start(struct dma2d_dev *d);
> +u32 dma2d_get_int(struct dma2d_dev *d);
> +void dma2d_clear_int(struct dma2d_dev *d);
> +void dma2d_config_out(struct dma2d_dev *d, struct dma2d_frame *frm,
> + dma_addr_t o_addr);
> +void dma2d_config_fg(struct dma2d_dev *d, struct dma2d_frame *frm,
> + dma_addr_t f_addr);
> +void dma2d_config_bg(struct dma2d_dev *d, struct dma2d_frame *frm,
> + dma_addr_t b_addr);
> +void dma2d_config_common(struct dma2d_dev *d, enum dma2d_op_mode op_mode,
> + u16 width, u16 height);
> +
> +#endif /* __DMA2D_H__ */
>

Regards,

Hans

2021-06-17 10:25:25

by Dillon Min

[permalink] [raw]
Subject: Re: [PATCH 3/7] clk: stm32: Fix ltdc's clock turn off by clk_disable_unused() after kernel startup

Hi Hans,

On Thu, Jun 17, 2021 at 5:57 PM Hans Verkuil <[email protected]> wrote:
>
> On 20/05/2021 13:05, [email protected] wrote:
> > From: Dillon Min <[email protected]>
> >
> > stm32's clk driver register two ltdc gate clk to clk core by
> > clk_hw_register_gate() and clk_hw_register_composite()
> >
> > first: 'stm32f429_gates[]', clk name is 'ltdc', which no user to use.
> > second: 'stm32f429_aux_clk[]', clk name is 'lcd-tft', used by ltdc driver
> >
> > both of them point to the same offset of stm32's RCC register. after
> > kernel enter console, clk core turn off ltdc's clk as 'stm32f429_gates[]'
> > is no one to use. but, actually 'stm32f429_aux_clk[]' is in use.
> >
> > Fixes: daf2d117cbca ("clk: stm32f4: Add lcd-tft clock")
> > Signed-off-by: Dillon Min <[email protected]>
> > Acked-by: Stephen Boyd <[email protected]>
> > Link: https://lore.kernel.org/linux-arm-kernel/[email protected]/
>
> For my understanding: this patch is going/has already gone in via a different
> subsystem, right? And I should skip it when adding this driver to the media subsystem?

Yes, Just ignore this patch please.

It's only to make st's engineer easier to verify the dma2d driver,
since the board's lcd panel depends on this patch to work

stm32f469-disco board :
https://www.st.com/content/st_com/en/products/evaluation-tools/product-evaluation-tools/mcu-mpu-eval-tools/stm32-mcu-mpu-eval-tools/stm32-discovery-kits/32f469idiscovery.html

Thanks
Best Regards

Dillon

>
> Regards,
>
> Hans
>
> > ---
> >
> > This patch was submitted in
> > https://lore.kernel.org/lkml/[email protected]/
> >
> > drivers/clk/clk-stm32f4.c | 7 +++----
> > 1 file changed, 3 insertions(+), 4 deletions(-)
> >
> > diff --git a/drivers/clk/clk-stm32f4.c b/drivers/clk/clk-stm32f4.c
> > index 18117ce5ff85..b6ab8c3a7994 100644
> > --- a/drivers/clk/clk-stm32f4.c
> > +++ b/drivers/clk/clk-stm32f4.c
> > @@ -211,7 +211,6 @@ static const struct stm32f4_gate_data stm32f469_gates[] __initconst = {
> > { STM32F4_RCC_APB2ENR, 20, "spi5", "apb2_div" },
> > { STM32F4_RCC_APB2ENR, 21, "spi6", "apb2_div" },
> > { STM32F4_RCC_APB2ENR, 22, "sai1", "apb2_div" },
> > - { STM32F4_RCC_APB2ENR, 26, "ltdc", "apb2_div" },
> > };
> >
> > static const struct stm32f4_gate_data stm32f746_gates[] __initconst = {
> > @@ -557,13 +556,13 @@ static const struct clk_div_table post_divr_table[] = {
> >
> > #define MAX_POST_DIV 3
> > static const struct stm32f4_pll_post_div_data post_div_data[MAX_POST_DIV] = {
> > - { CLK_I2SQ_PDIV, PLL_I2S, "plli2s-q-div", "plli2s-q",
> > + { CLK_I2SQ_PDIV, PLL_VCO_I2S, "plli2s-q-div", "plli2s-q",
> > CLK_SET_RATE_PARENT, STM32F4_RCC_DCKCFGR, 0, 5, 0, NULL},
> >
> > - { CLK_SAIQ_PDIV, PLL_SAI, "pllsai-q-div", "pllsai-q",
> > + { CLK_SAIQ_PDIV, PLL_VCO_SAI, "pllsai-q-div", "pllsai-q",
> > CLK_SET_RATE_PARENT, STM32F4_RCC_DCKCFGR, 8, 5, 0, NULL },
> >
> > - { NO_IDX, PLL_SAI, "pllsai-r-div", "pllsai-r", CLK_SET_RATE_PARENT,
> > + { NO_IDX, PLL_VCO_SAI, "pllsai-r-div", "pllsai-r", CLK_SET_RATE_PARENT,
> > STM32F4_RCC_DCKCFGR, 16, 2, 0, post_divr_table },
> > };
> >
> >
>

2021-06-17 10:45:03

by Dillon Min

[permalink] [raw]
Subject: Re: [PATCH 6/7] media: v4l2-mem2mem: add v4l2_m2m_get_unmapped_area for no-mmu platform

Hi Hans,

On Thu, Jun 17, 2021 at 5:58 PM Hans Verkuil <[email protected]> wrote:
>
> On 20/05/2021 13:05, [email protected] wrote:
> > From: Dillon Min <[email protected]>
> >
> > For platforms without MMU the m2m provides a helper method
> > v4l2_m2m_get_unmapped_area(), The mmap() routines will call
> > this to get a proposed address for the mapping.
> >
> > More detailed information about get_unmapped_area can be found in
> > Documentation/nommu-mmap.txt
>
> I'm getting checkpatch.pl --strict warnings:
>
> CHECK: Alignment should match open parenthesis
> #31: FILE: drivers/media/v4l2-core/v4l2-mem2mem.c:971:
> +unsigned long v4l2_m2m_get_unmapped_area(struct file *file, unsigned long addr,
> + unsigned long len, unsigned long pgoff, unsigned long flags)
>
> CHECK: Alignment should match open parenthesis
> #62: FILE: include/media/v4l2-mem2mem.h:500:
> +unsigned long v4l2_m2m_get_unmapped_area(struct file *file, unsigned long addr,
> + unsigned long len, unsigned long pgoff, unsigned long flags);

Okay, I will add the "--strict" check to my regular patch submission.
Thanks for the reminder.

Best Regards
Dillon

>
> Regards,
>
> Hans
>
> >
> > Signed-off-by: Dillon Min <[email protected]>
> > ---
> > drivers/media/v4l2-core/v4l2-mem2mem.c | 20 ++++++++++++++++++++
> > include/media/v4l2-mem2mem.h | 4 ++++
> > 2 files changed, 24 insertions(+)
> >
> > diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c
> > index e7f4bf5bc8dd..f82a18ecab2f 100644
> > --- a/drivers/media/v4l2-core/v4l2-mem2mem.c
> > +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c
> > @@ -966,6 +966,26 @@ int v4l2_m2m_mmap(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
> > }
> > EXPORT_SYMBOL(v4l2_m2m_mmap);
> >
> > +#ifndef CONFIG_MMU
> > +unsigned long v4l2_m2m_get_unmapped_area(struct file *file, unsigned long addr,
> > + unsigned long len, unsigned long pgoff, unsigned long flags)
> > +{
> > + struct v4l2_fh *fh = file->private_data;
> > + unsigned long offset = pgoff << PAGE_SHIFT;
> > + struct vb2_queue *vq;
> > +
> > + if (offset < DST_QUEUE_OFF_BASE) {
> > + vq = v4l2_m2m_get_src_vq(fh->m2m_ctx);
> > + } else {
> > + vq = v4l2_m2m_get_dst_vq(fh->m2m_ctx);
> > + pgoff -= (DST_QUEUE_OFF_BASE >> PAGE_SHIFT);
> > + }
> > +
> > + return vb2_get_unmapped_area(vq, addr, len, pgoff, flags);
> > +}
> > +EXPORT_SYMBOL_GPL(v4l2_m2m_get_unmapped_area);
> > +#endif
> > +
> > #if defined(CONFIG_MEDIA_CONTROLLER)
> > void v4l2_m2m_unregister_media_controller(struct v4l2_m2m_dev *m2m_dev)
> > {
> > diff --git a/include/media/v4l2-mem2mem.h b/include/media/v4l2-mem2mem.h
> > index 5a91b548ecc0..91269227c265 100644
> > --- a/include/media/v4l2-mem2mem.h
> > +++ b/include/media/v4l2-mem2mem.h
> > @@ -495,6 +495,10 @@ __poll_t v4l2_m2m_poll(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
> > int v4l2_m2m_mmap(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
> > struct vm_area_struct *vma);
> >
> > +#ifndef CONFIG_MMU
> > +unsigned long v4l2_m2m_get_unmapped_area(struct file *file, unsigned long addr,
> > + unsigned long len, unsigned long pgoff, unsigned long flags);
> > +#endif
> > /**
> > * v4l2_m2m_init() - initialize per-driver m2m data
> > *
> >
>

2021-06-17 11:08:43

by Hans Verkuil

[permalink] [raw]
Subject: Re: [PATCH 3/7] clk: stm32: Fix ltdc's clock turn off by clk_disable_unused() after kernel startup

On 20/05/2021 13:05, [email protected] wrote:
> From: Dillon Min <[email protected]>
>
> stm32's clk driver register two ltdc gate clk to clk core by
> clk_hw_register_gate() and clk_hw_register_composite()
>
> first: 'stm32f429_gates[]', clk name is 'ltdc', which no user to use.
> second: 'stm32f429_aux_clk[]', clk name is 'lcd-tft', used by ltdc driver
>
> both of them point to the same offset of stm32's RCC register. after
> kernel enter console, clk core turn off ltdc's clk as 'stm32f429_gates[]'
> is no one to use. but, actually 'stm32f429_aux_clk[]' is in use.
>
> Fixes: daf2d117cbca ("clk: stm32f4: Add lcd-tft clock")
> Signed-off-by: Dillon Min <[email protected]>
> Acked-by: Stephen Boyd <[email protected]>
> Link: https://lore.kernel.org/linux-arm-kernel/[email protected]/

For my understanding: this patch is going/has already gone in via a different
subsystem, right? And I should skip it when adding this driver to the media subsystem?

Regards,

Hans

> ---
>
> This patch was submitted in
> https://lore.kernel.org/lkml/[email protected]/
>
> drivers/clk/clk-stm32f4.c | 7 +++----
> 1 file changed, 3 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/clk/clk-stm32f4.c b/drivers/clk/clk-stm32f4.c
> index 18117ce5ff85..b6ab8c3a7994 100644
> --- a/drivers/clk/clk-stm32f4.c
> +++ b/drivers/clk/clk-stm32f4.c
> @@ -211,7 +211,6 @@ static const struct stm32f4_gate_data stm32f469_gates[] __initconst = {
> { STM32F4_RCC_APB2ENR, 20, "spi5", "apb2_div" },
> { STM32F4_RCC_APB2ENR, 21, "spi6", "apb2_div" },
> { STM32F4_RCC_APB2ENR, 22, "sai1", "apb2_div" },
> - { STM32F4_RCC_APB2ENR, 26, "ltdc", "apb2_div" },
> };
>
> static const struct stm32f4_gate_data stm32f746_gates[] __initconst = {
> @@ -557,13 +556,13 @@ static const struct clk_div_table post_divr_table[] = {
>
> #define MAX_POST_DIV 3
> static const struct stm32f4_pll_post_div_data post_div_data[MAX_POST_DIV] = {
> - { CLK_I2SQ_PDIV, PLL_I2S, "plli2s-q-div", "plli2s-q",
> + { CLK_I2SQ_PDIV, PLL_VCO_I2S, "plli2s-q-div", "plli2s-q",
> CLK_SET_RATE_PARENT, STM32F4_RCC_DCKCFGR, 0, 5, 0, NULL},
>
> - { CLK_SAIQ_PDIV, PLL_SAI, "pllsai-q-div", "pllsai-q",
> + { CLK_SAIQ_PDIV, PLL_VCO_SAI, "pllsai-q-div", "pllsai-q",
> CLK_SET_RATE_PARENT, STM32F4_RCC_DCKCFGR, 8, 5, 0, NULL },
>
> - { NO_IDX, PLL_SAI, "pllsai-r-div", "pllsai-r", CLK_SET_RATE_PARENT,
> + { NO_IDX, PLL_VCO_SAI, "pllsai-r-div", "pllsai-r", CLK_SET_RATE_PARENT,
> STM32F4_RCC_DCKCFGR, 16, 2, 0, post_divr_table },
> };
>
>

2021-06-17 11:23:01

by Dillon Min

[permalink] [raw]
Subject: Re: [PATCH 7/7] media: stm32-dma2d: STM32 DMA2D driver

Hi Hans

On Thu, Jun 17, 2021 at 6:18 PM Hans Verkuil <[email protected]> wrote:
>
> Hi Dillon,
>
> Please run checkpatch.pl --strict over this patch: I get too many 'CHECK' warnings.

Okay, thanks.

>
> On 20/05/2021 13:05, [email protected] wrote:
> > From: Dillon Min <[email protected]>
> >
> > This V4L2 subdev m2m driver enables Chrom-Art Accelerator unit
> > of STMicroelectronics STM32 SoC series.
> >
> > Currently support r2m, m2m, m2m_pfc.
>
> What does this mean?

This is means graphic data flow support following features:
- Filling a part or the whole of a destination image with a specific color
- Copying a part or the whole of a source image into a part or the
whole of a destination
image
- Copying a part or the whole of a source image into a part or the
whole of a destination
image with a pixel format conversion
- Blending a part and/or two complete source images with different
pixel format and copy
the result into a part or the whole of a destination image with a
different color format
(not support in this version)

I will make the commit message to be more readable in v2.

>
> >
> > Signed-off-by: Dillon Min <[email protected]>
> > ---
> > drivers/media/platform/Kconfig | 9 +
> > drivers/media/platform/Makefile | 1 +
> > drivers/media/platform/stm32/Makefile | 2 +
> > drivers/media/platform/stm32/dma2d/dma2d-hw.c | 143 +++++
> > drivers/media/platform/stm32/dma2d/dma2d-regs.h | 114 ++++
> > drivers/media/platform/stm32/dma2d/dma2d.c | 791 ++++++++++++++++++++++++
> > drivers/media/platform/stm32/dma2d/dma2d.h | 132 ++++
> > 7 files changed, 1192 insertions(+)
> > create mode 100644 drivers/media/platform/stm32/dma2d/dma2d-hw.c
> > create mode 100644 drivers/media/platform/stm32/dma2d/dma2d-regs.h
> > create mode 100644 drivers/media/platform/stm32/dma2d/dma2d.c
> > create mode 100644 drivers/media/platform/stm32/dma2d/dma2d.h
> >
> > diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
> > index 157c924686e4..128f4420623f 100644
> > --- a/drivers/media/platform/Kconfig
> > +++ b/drivers/media/platform/Kconfig
> > @@ -244,6 +244,15 @@ config VIDEO_CODA
> > config VIDEO_IMX_VDOA
> > def_tristate VIDEO_CODA if SOC_IMX6Q || COMPILE_TEST
> >
> > +config VIDEO_STM32_DMA2D
> > + tristate "STM32 Chrom-Art Accelerator (DMA2D)"
> > + depends on VIDEO_DEV && VIDEO_V4L2 && ARCH_STM32
>
> This needs to depend on COMPILE_TEST as well.

Agree, thanks

>
> > + select VIDEOBUF2_DMA_CONTIG
> > + select V4L2_MEM2MEM_DEV
> > + help
> > + The STM32 DMA2D is a memory-to-memory engine for pixel conversion,
> > + specialized DMA dedicated to image manipulation
>
> Missing period at the end. And the , should probably be replaced with 'and'.

Agree, thanks.

>
> > +
> > config VIDEO_IMX_PXP
> > tristate "i.MX Pixel Pipeline (PXP)"
> > depends on VIDEO_DEV && VIDEO_V4L2 && (ARCH_MXC || COMPILE_TEST)
> > diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
> > index eedc14aafb32..93b2b319a2b2 100644
> > --- a/drivers/media/platform/Makefile
> > +++ b/drivers/media/platform/Makefile
> > @@ -69,6 +69,7 @@ obj-$(CONFIG_VIDEO_ATMEL_ISC) += atmel/
> > obj-$(CONFIG_VIDEO_ATMEL_ISI) += atmel/
> >
> > obj-$(CONFIG_VIDEO_STM32_DCMI) += stm32/
> > +obj-$(CONFIG_VIDEO_STM32_DMA2D) += stm32/
> >
> > obj-$(CONFIG_VIDEO_MEDIATEK_VPU) += mtk-vpu/
> >
> > diff --git a/drivers/media/platform/stm32/Makefile b/drivers/media/platform/stm32/Makefile
> > index 48b36db2c2e2..896ef98a73ab 100644
> > --- a/drivers/media/platform/stm32/Makefile
> > +++ b/drivers/media/platform/stm32/Makefile
> > @@ -1,2 +1,4 @@
> > # SPDX-License-Identifier: GPL-2.0-only
> > obj-$(CONFIG_VIDEO_STM32_DCMI) += stm32-dcmi.o
> > +stm32-dma2d-objs := dma2d/dma2d.o dma2d/dma2d-hw.o
> > +obj-$(CONFIG_VIDEO_STM32_DMA2D) += stm32-dma2d.o
> > diff --git a/drivers/media/platform/stm32/dma2d/dma2d-hw.c b/drivers/media/platform/stm32/dma2d/dma2d-hw.c
> > new file mode 100644
> > index 000000000000..6fdbf5a21151
> > --- /dev/null
> > +++ b/drivers/media/platform/stm32/dma2d/dma2d-hw.c
> > @@ -0,0 +1,143 @@
> > +// SPDX-License-Identifier: GPL-2.0-or-later
> > +/*
> > + * ST stm32 Chrom-Art - 2D Graphics Accelerator Driver
> > + *
> > + * Copyright (c) 2021 Dillon Min
> > + * Dillon Min, <[email protected]>
> > + *
> > + * based on s5p-g2d
> > + *
> > + * Copyright (c) 2011 Samsung Electronics Co., Ltd.
> > + * Kamil Debski, <[email protected]>
> > + */
> > +
> > +#include <linux/io.h>
> > +
> > +#include "dma2d.h"
> > +#include "dma2d-regs.h"
> > +
> > +static inline u32 reg_read(void __iomem *base, u32 reg)
> > +{
> > + return readl_relaxed(base + reg);
> > +}
> > +
> > +static inline void reg_write(void __iomem *base, u32 reg, u32 val)
> > +{
> > + writel_relaxed(val, base + reg);
> > +}
> > +
> > +static inline void reg_set(void __iomem *base, u32 reg, u32 mask)
> > +{
> > + reg_write(base, reg, reg_read(base, reg) | mask);
> > +}
> > +
> > +static inline void reg_clear(void __iomem *base, u32 reg, u32 mask)
> > +{
> > + reg_write(base, reg, reg_read(base, reg) & ~mask);
> > +}
> > +
> > +static inline void reg_update_bits(void __iomem *base, u32 reg, u32 mask,
> > + u32 val)
> > +{
> > + reg_write(base, reg, (reg_read(base, reg) & ~mask) | val);
> > +}
> > +
> > +void dma2d_start(struct dma2d_dev *d)
> > +{
> > + reg_update_bits(d->regs, DMA2D_CR_REG, CR_START, CR_START);
> > +}
> > +
> > +u32 dma2d_get_int(struct dma2d_dev *d)
> > +{
> > + return reg_read(d->regs, DMA2D_ISR_REG);
> > +}
> > +
> > +void dma2d_clear_int(struct dma2d_dev *d)
> > +{
> > + u32 isr_val = reg_read(d->regs, DMA2D_ISR_REG);
> > +
> > + reg_write(d->regs, DMA2D_IFCR_REG, isr_val & 0x003f);
> > +}
> > +
> > +void dma2d_config_common(struct dma2d_dev *d, enum dma2d_op_mode op_mode,
> > + u16 width, u16 height)
> > +{
> > + reg_update_bits(d->regs, DMA2D_CR_REG, CR_MODE_MASK,
> > + op_mode << CR_MODE_SHIFT);
> > +
> > + reg_write(d->regs, DMA2D_NLR_REG, (width << 16) | height);
> > +}
> > +
> > +void dma2d_config_out(struct dma2d_dev *d, struct dma2d_frame *frm,
> > + dma_addr_t o_addr)
> > +{
> > + reg_update_bits(d->regs, DMA2D_CR_REG, CR_CEIE, CR_CEIE);
> > + reg_update_bits(d->regs, DMA2D_CR_REG, CR_CTCIE, CR_CTCIE);
> > + reg_update_bits(d->regs, DMA2D_CR_REG, CR_CAEIE, CR_CAEIE);
> > + reg_update_bits(d->regs, DMA2D_CR_REG, CR_TCIE, CR_TCIE);
> > + reg_update_bits(d->regs, DMA2D_CR_REG, CR_TEIE, CR_TEIE);
> > +
> > + if (frm->fmt->cmode >= CM_MODE_ARGB8888 &&
> > + frm->fmt->cmode <= CM_MODE_ARGB4444)
> > + reg_update_bits(d->regs, DMA2D_OPFCCR_REG, OPFCCR_CM_MASK,
> > + frm->fmt->cmode);
> > +
> > + reg_write(d->regs, DMA2D_OMAR_REG, o_addr);
> > +
> > + reg_write(d->regs, DMA2D_OCOLR_REG,
> > + (frm->a_rgb[3] << 24) |
> > + (frm->a_rgb[2] << 16) |
> > + (frm->a_rgb[1] << 8) |
> > + frm->a_rgb[0]);
> > +
> > + reg_update_bits(d->regs, DMA2D_OOR_REG, OOR_LO_MASK,
> > + frm->line_ofs & 0x3fff);
> > +}
> > +
> > +void dma2d_config_fg(struct dma2d_dev *d, struct dma2d_frame *frm,
> > + dma_addr_t f_addr)
> > +{
> > + reg_write(d->regs, DMA2D_FGMAR_REG, f_addr);
> > + reg_update_bits(d->regs, DMA2D_FGOR_REG, FGOR_LO_MASK,
> > + frm->line_ofs);
> > +
> > + if (frm->fmt->cmode >= CM_MODE_ARGB8888 &&
> > + frm->fmt->cmode <= CM_MODE_A4)
> > + reg_update_bits(d->regs, DMA2D_FGPFCCR_REG, FGPFCCR_CM_MASK,
> > + frm->fmt->cmode);
> > +
> > + reg_update_bits(d->regs, DMA2D_FGPFCCR_REG, FGPFCCR_AM_MASK,
> > + (frm->a_mode << 16) & 0x03);
> > +
> > + reg_update_bits(d->regs, DMA2D_FGPFCCR_REG, FGPFCCR_ALPHA_MASK,
> > + frm->a_rgb[3] << 24);
> > +
> > + reg_write(d->regs, DMA2D_FGCOLR_REG,
> > + (frm->a_rgb[2] << 16) |
> > + (frm->a_rgb[1] << 8) |
> > + frm->a_rgb[0]);
> > +}
> > +
> > +void dma2d_config_bg(struct dma2d_dev *d, struct dma2d_frame *frm,
> > + dma_addr_t b_addr)
> > +{
> > + reg_write(d->regs, DMA2D_BGMAR_REG, b_addr);
> > + reg_update_bits(d->regs, DMA2D_BGOR_REG, BGOR_LO_MASK,
> > + frm->line_ofs);
> > +
> > + if (frm->fmt->cmode >= CM_MODE_ARGB8888 &&
> > + frm->fmt->cmode <= CM_MODE_A4)
> > + reg_update_bits(d->regs, DMA2D_BGPFCCR_REG, BGPFCCR_CM_MASK,
> > + frm->fmt->cmode);
> > +
> > + reg_update_bits(d->regs, DMA2D_BGPFCCR_REG, BGPFCCR_AM_MASK,
> > + (frm->a_mode << 16) & 0x03);
> > +
> > + reg_update_bits(d->regs, DMA2D_BGPFCCR_REG, BGPFCCR_ALPHA_MASK,
> > + frm->a_rgb[3] << 24);
> > +
> > + reg_write(d->regs, DMA2D_BGCOLR_REG,
> > + (frm->a_rgb[2] << 16) |
> > + (frm->a_rgb[1] << 8) |
> > + frm->a_rgb[0]);
> > +}
> > diff --git a/drivers/media/platform/stm32/dma2d/dma2d-regs.h b/drivers/media/platform/stm32/dma2d/dma2d-regs.h
> > new file mode 100644
> > index 000000000000..6e375bcb1281
> > --- /dev/null
> > +++ b/drivers/media/platform/stm32/dma2d/dma2d-regs.h
> > @@ -0,0 +1,114 @@
> > +/* SPDX-License-Identifier: GPL-2.0-or-later */
> > +/*
> > + * ST stm32 Chrom-Art - 2D Graphics Accelerator Driver
> > + *
> > + * Copyright (c) 2021 Dillon Min
> > + * Dillon Min, <[email protected]>
> > + *
> > + * based on s5p-g2d
> > + *
> > + * Copyright (c) 2011 Samsung Electronics Co., Ltd.
> > + * Kamil Debski, <[email protected]>
> > + */
> > +
> > +#ifndef __DMA2D_REGS_H__
> > +#define __DMA2D_REGS_H__
> > +
> > +#define DMA2D_CR_REG 0x0000
> > +#define CR_MODE_MASK GENMASK(17, 16)
> > +#define CR_MODE_SHIFT 16
> > +#define CR_M2M 0x0000
> > +#define CR_M2M_PFC BIT(16)
> > +#define CR_M2M_BLEND BIT(17)
> > +#define CR_R2M (BIT(17) | BIT(16))
> > +#define CR_CEIE BIT(13)
> > +#define CR_CTCIE BIT(12)
> > +#define CR_CAEIE BIT(11)
> > +#define CR_TWIE BIT(10)
> > +#define CR_TCIE BIT(9)
> > +#define CR_TEIE BIT(8)
> > +#define CR_ABORT BIT(2)
> > +#define CR_SUSP BIT(1)
> > +#define CR_START BIT(0)
> > +
> > +#define DMA2D_ISR_REG 0x0004
> > +#define ISR_CEIF BIT(5)
> > +#define ISR_CTCIF BIT(4)
> > +#define ISR_CAEIF BIT(3)
> > +#define ISR_TWIF BIT(2)
> > +#define ISR_TCIF BIT(1)
> > +#define ISR_TEIF BIT(0)
> > +
> > +#define DMA2D_IFCR_REG 0x0008
> > +#define IFCR_CCEIF BIT(5)
> > +#define IFCR_CCTCIF BIT(4)
> > +#define IFCR_CAECIF BIT(3)
> > +#define IFCR_CTWIF BIT(2)
> > +#define IFCR_CTCIF BIT(1)
> > +#define IFCR_CTEIF BIT(0)
> > +
> > +#define DMA2D_FGMAR_REG 0x000c
> > +#define DMA2D_FGOR_REG 0x0010
> > +#define FGOR_LO_MASK GENMASK(13, 0)
> > +
> > +#define DMA2D_BGMAR_REG 0x0014
> > +#define DMA2D_BGOR_REG 0x0018
> > +#define BGOR_LO_MASK GENMASK(13, 0)
> > +
> > +#define DMA2D_FGPFCCR_REG 0x001c
> > +#define FGPFCCR_ALPHA_MASK GENMASK(31, 24)
> > +#define FGPFCCR_AM_MASK GENMASK(17, 16)
> > +#define FGPFCCR_CS_MASK GENMASK(15, 8)
> > +#define FGPFCCR_START BIT(5)
> > +#define FGPFCCR_CCM_RGB888 BIT(4)
> > +#define FGPFCCR_CM_MASK GENMASK(3, 0)
> > +
> > +#define DMA2D_FGCOLR_REG 0x0020
> > +#define FGCOLR_REG_MASK GENMASK(23, 16)
> > +#define FGCOLR_GREEN_MASK GENMASK(15, 8)
> > +#define FGCOLR_BLUE_MASK GENMASK(7, 0)
> > +
> > +#define DMA2D_BGPFCCR_REG 0x0024
> > +#define BGPFCCR_ALPHA_MASK GENMASK(31, 24)
> > +#define BGPFCCR_AM_MASK GENMASK(17, 16)
> > +#define BGPFCCR_CS_MASK GENMASK(15, 8)
> > +#define BGPFCCR_START BIT(5)
> > +#define BGPFCCR_CCM_RGB888 BIT(4)
> > +#define BGPFCCR_CM_MASK GENMASK(3, 0)
> > +
> > +#define DMA2D_BGCOLR_REG 0x0028
> > +#define BGCOLR_REG_MASK GENMASK(23, 16)
> > +#define BGCOLR_GREEN_MASK GENMASK(15, 8)
> > +#define BGCOLR_BLUE_MASK GENMASK(7, 0)
> > +
> > +#define DMA2D_OPFCCR_REG 0x0034
> > +#define OPFCCR_CM_MASK GENMASK(2, 0)
> > +
> > +#define DMA2D_OCOLR_REG 0x0038
> > +#define OCOLR_ALPHA_MASK GENMASK(31, 24)
> > +#define OCOLR_RED_MASK GENMASK(23, 16)
> > +#define OCOLR_GREEN_MASK GENMASK(15, 8)
> > +#define OCOLR_BLUE_MASK GENMASK(7, 0)
> > +
> > +#define DMA2D_OMAR_REG 0x003c
> > +
> > +#define DMA2D_OOR_REG 0x0040
> > +#define OOR_LO_MASK GENMASK(13, 0)
> > +
> > +#define DMA2D_NLR_REG 0x0044
> > +#define NLR_PL_MASK GENMASK(29, 16)
> > +#define NLR_NL_MASK GENMASK(15, 0)
> > +
> > +
> > +/* Hardware limits */
> > +#define MAX_WIDTH 0x3fff
> > +#define MAX_HEIGHT 0xffff
> > +
> > +#define DEFAULT_WIDTH 240
> > +#define DEFAULT_HEIGHT 320
> > +#define DEFAULT_SIZE 307200
> > +
> > +#define CM_MODE_ARGB8888 0x00
> > +#define CM_MODE_ARGB4444 0x04
> > +#define CM_MODE_A4 0x0a
> > +#endif /* __DMA2D_REGS_H__ */
> > diff --git a/drivers/media/platform/stm32/dma2d/dma2d.c b/drivers/media/platform/stm32/dma2d/dma2d.c
> > new file mode 100644
> > index 000000000000..88f8333a174a
> > --- /dev/null
> > +++ b/drivers/media/platform/stm32/dma2d/dma2d.c
> > @@ -0,0 +1,791 @@
> > +// SPDX-License-Identifier: GPL-2.0-or-later
> > +/*
> > + * STM32 DMA2D - 2D Graphics Accelerator Driver
> > + *
> > + * Copyright (c) 2021 Dillon Min
> > + * Dillon Min, <[email protected]>
> > + *
> > + * based on s5p-g2d
> > + *
> > + * Copyright (c) 2011 Samsung Electronics Co., Ltd.
> > + * Kamil Debski, <[email protected]>
> > + */
> > +
> > +#include <linux/module.h>
> > +#include <linux/fs.h>
> > +#include <linux/timer.h>
> > +#include <linux/sched.h>
> > +#include <linux/slab.h>
> > +#include <linux/clk.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/of.h>
> > +
> > +#include <linux/platform_device.h>
> > +#include <media/v4l2-mem2mem.h>
> > +#include <media/v4l2-device.h>
> > +#include <media/v4l2-ioctl.h>
> > +#include <media/v4l2-event.h>
> > +#include <media/videobuf2-v4l2.h>
> > +#include <media/videobuf2-dma-contig.h>
> > +
> > +#include "dma2d.h"
> > +#include "dma2d-regs.h"
> > +
> > +#define fh2ctx(__fh) container_of(__fh, struct dma2d_ctx, fh)
> > +
> > +static struct dma2d_fmt formats[] = {
>
> Can this be const?

Agree

>
> > + {
> > + .fourcc = V4L2_PIX_FMT_ARGB32,
> > + .cmode = DMA2D_CMODE_ARGB8888,
> > + .depth = 32,
> > + },
> > + {
> > + .fourcc = V4L2_PIX_FMT_RGB24,
> > + .cmode = DMA2D_CMODE_RGB888,
> > + .depth = 24,
> > + },
> > + {
> > + .fourcc = V4L2_PIX_FMT_RGB565,
> > + .cmode = DMA2D_CMODE_RGB565,
> > + .depth = 16,
> > + },
> > + {
> > + .fourcc = V4L2_PIX_FMT_ARGB555,
> > + .cmode = DMA2D_CMODE_ARGB1555,
> > + .depth = 16,
> > + },
> > + {
> > + .fourcc = V4L2_PIX_FMT_ARGB444,
> > + .cmode = DMA2D_CMODE_ARGB4444,
> > + .depth = 16,
> > + },
> > +};
> > +#define NUM_FORMATS ARRAY_SIZE(formats)
> > +
> > +static struct dma2d_frame def_frame = {
>
> Ditto.

Agree

>
> > + .width = DEFAULT_WIDTH,
> > + .height = DEFAULT_HEIGHT,
> > + .line_ofs = 0,
>
> Does 'ofs' stand for 'offset'? If so, then I prefer the full 'offset' over 'ofs'.
> 'ofs' is a bit obscure.

Agree

>
> > + .a_rgb = {0x00, 0x00, 0x00, 0xff},
> > + .a_mode = DMA2D_ALPHA_MODE_NO_MODIF,
> > + .fmt = &formats[0],
> > + .size = DEFAULT_SIZE,
> > +};
> > +
> > +static struct dma2d_fmt *find_fmt(int pixelformat)
> > +{
> > + unsigned int i;
> > +
> > + for (i = 0; i < NUM_FORMATS; i++) {
> > + if (formats[i].fourcc == pixelformat)
> > + return &formats[i];
> > + }
> > +
> > + return NULL;
> > +}
> > +
> > +static struct dma2d_frame *get_frame(struct dma2d_ctx *ctx,
> > + enum v4l2_buf_type type)
> > +{
> > + switch (type) {
> > + case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> > + return &ctx->fg;
> > + case V4L2_BUF_TYPE_VIDEO_CAPTURE:
> > + return &ctx->out;
>
> Hmm, capture returns ctx->out, and output returns ctx->fg. Not exactly what
> you would expect.

fg means foreground layer of dma2d controller, the name is indeed confusing.
I'll change it to cap/out, is it reasonable?

>
> > + default:
> > + return ERR_PTR(-EINVAL);
>
> Using error pointers here is a bit overkill IMHO. I think returning NULL is
> typical. With a WARN_ON perhaps since this really shouldn't happen.
>
> Alternatively just do this:
>
> return V4L2_TYPE_IS_OUTPUT(type) ? &ctx->fg : &ctx->out;
>
> It avoids the whole problem.

Agree, will add this suggestion to [v2], thanks.

>
> > + }
> > +}
> > +
> > +static int dma2d_queue_setup(struct vb2_queue *vq,
> > + unsigned int *nbuffers, unsigned int *nplanes,
> > + unsigned int sizes[], struct device *alloc_devs[])
> > +{
> > + struct dma2d_ctx *ctx = vb2_get_drv_priv(vq);
> > + struct dma2d_frame *f = get_frame(ctx, vq->type);
> > +
> > + if (IS_ERR(f))
> > + return PTR_ERR(f);
> > +
> > + if (*nplanes)
> > + return sizes[0] < f->size ? -EINVAL : 0;
> > +
> > + sizes[0] = f->size;
> > + *nplanes = 1;
> > +
> > + if (*nbuffers == 0)
> > + *nbuffers = 1;
> > +
> > + return 0;
> > +}
> > +
> > +static int dma2d_buf_out_validate(struct vb2_buffer *vb)
> > +{
> > + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
> > +
> > + if (vbuf->field == V4L2_FIELD_ANY)
> > + vbuf->field = V4L2_FIELD_NONE;
> > + if (vbuf->field != V4L2_FIELD_NONE)
> > + return -EINVAL;
> > +
> > + return 0;
> > +}
> > +
> > +static int dma2d_buf_prepare(struct vb2_buffer *vb)
> > +{
> > + struct dma2d_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
> > + struct dma2d_frame *f = get_frame(ctx, vb->vb2_queue->type);
> > +
> > + if (IS_ERR(f))
> > + return PTR_ERR(f);
> > +
> > + if (vb2_plane_size(vb, 0) < f->size)
> > + return -EINVAL;
> > +
> > + vb2_set_plane_payload(vb, 0, f->size);
> > +
> > + return 0;
> > +}
> > +
> > +static void dma2d_buf_queue(struct vb2_buffer *vb)
> > +{
> > + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
> > + struct dma2d_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
> > +
> > + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
> > +}
> > +
> > +static int dma2d_start_streaming(struct vb2_queue *q, unsigned int count)
> > +{
> > + struct dma2d_ctx *ctx = vb2_get_drv_priv(q);
> > + struct dma2d_frame *f = get_frame(ctx, q->type);
> > +
> > + if (IS_ERR(f))
> > + return -EINVAL;
> > +
> > + f->sequence = 0;
> > + return 0;
> > +}
> > +
> > +static void dma2d_stop_streaming(struct vb2_queue *q)
> > +{
> > + struct dma2d_ctx *ctx = vb2_get_drv_priv(q);
> > + struct vb2_v4l2_buffer *vbuf;
> > +
> > + for (;;) {
> > + if (V4L2_TYPE_IS_OUTPUT(q->type))
> > + vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
> > + else
> > + vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
> > + if (!vbuf)
> > + return;
> > + v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
> > + }
> > +}
> > +
> > +static const struct vb2_ops dma2d_qops = {
> > + .queue_setup = dma2d_queue_setup,
> > + .buf_out_validate = dma2d_buf_out_validate,
> > + .buf_prepare = dma2d_buf_prepare,
> > + .buf_queue = dma2d_buf_queue,
> > + .start_streaming = dma2d_start_streaming,
> > + .stop_streaming = dma2d_stop_streaming,
> > + .wait_prepare = vb2_ops_wait_prepare,
> > + .wait_finish = vb2_ops_wait_finish,
> > +};
> > +
> > +static int queue_init(void *priv, struct vb2_queue *src_vq,
> > + struct vb2_queue *dst_vq)
> > +{
> > + struct dma2d_ctx *ctx = priv;
> > + int ret;
> > +
> > + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
> > + src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
> > + src_vq->drv_priv = ctx;
> > + src_vq->ops = &dma2d_qops;
> > + src_vq->mem_ops = &vb2_dma_contig_memops;
> > + src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
> > + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
> > + src_vq->lock = &ctx->dev->mutex;
> > + src_vq->dev = ctx->dev->v4l2_dev.dev;
> > +
> > + ret = vb2_queue_init(src_vq);
> > + if (ret)
> > + return ret;
> > +
> > + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> > + dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
> > + dst_vq->drv_priv = ctx;
> > + dst_vq->ops = &dma2d_qops;
> > + dst_vq->mem_ops = &vb2_dma_contig_memops;
> > + dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
> > + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
> > + dst_vq->lock = &ctx->dev->mutex;
> > + dst_vq->dev = ctx->dev->v4l2_dev.dev;
> > +
> > + return vb2_queue_init(dst_vq);
> > +}
> > +
> > +#define V4L2_CID_DMA2D_R2M_COLOR (V4L2_CID_USER_BASE | 0x1200)
> > +#define V4L2_CID_DMA2D_R2M_MODE (V4L2_CID_USER_BASE | 0x1201)
>
> You need to reserve a range for driver-specific controls in include/uapi/linux/v4l2-controls.h.
> Search for V4L2_CID_USER_CCS_BASE to see how it is done.

Okay, I will optimize this part in [v2]. thanks.

>
> > +static int dma2d_s_ctrl(struct v4l2_ctrl *ctrl)
> > +{
> > + struct dma2d_frame *frm;
> > + struct dma2d_ctx *ctx = container_of(ctrl->handler, struct dma2d_ctx,
> > + ctrl_handler);
> > + unsigned long flags;
> > +
> > + spin_lock_irqsave(&ctx->dev->ctrl_lock, flags);
> > + switch (ctrl->id) {
> > + case V4L2_CID_ALPHA_COMPONENT:
> > + /* set the background alpha value*/
> > + ctx->alpha_component = (u8) ctrl->val;
> > + break;
> > + case V4L2_CID_DMA2D_R2M_COLOR:
> > + frm = get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
> > + frm->a_rgb[3] = (ctrl->val >> 24) & 0xff;
> > + frm->a_rgb[2] = (ctrl->val >> 16) & 0xff;
> > + frm->a_rgb[1] = (ctrl->val >> 8) & 0xff;
> > + frm->a_rgb[0] = ctrl->val & 0xff;
> > + break;
> > + case V4L2_CID_DMA2D_R2M_MODE:
> > + if (ctrl->val)
> > + ctx->op_mode = DMA2D_MODE_R2M;
> > + break;
> > + default:
> > + v4l2_err(&ctx->dev->v4l2_dev, "Invalid control\n");
> > + spin_unlock_irqrestore(&ctx->dev->ctrl_lock, flags);
> > + return -EINVAL;
> > +
> > + }
> > + spin_unlock_irqrestore(&ctx->dev->ctrl_lock, flags);
> > +
> > + return 0;
> > +}
> > +
> > +static const struct v4l2_ctrl_ops dma2d_ctrl_ops = {
> > + .s_ctrl = dma2d_s_ctrl,
> > +};
> > +
> > +static const struct v4l2_ctrl_config dma2d_r2m_control[] = {
> > + {
> > + .ops = &dma2d_ctrl_ops,
> > + .id = V4L2_CID_DMA2D_R2M_COLOR,
> > + .name = "R2M Alpha/Color Value",
> > + .type = V4L2_CTRL_TYPE_INTEGER,
> > + .min = 0xffffffff80000000ULL,
> > + .max = 0x7fffffff,
> > + .def = 0,
> > + .step = 1,
> > + },
> > + {
> > + .ops = &dma2d_ctrl_ops,
> > + .id = V4L2_CID_DMA2D_R2M_MODE,
> > + .name = "Set to r2m mode",
>
> "Set R2M Mode"

Agree

>
> These controls need to be documented.

Okay, I will add a doc patch in v2.

>
> > + .type = V4L2_CTRL_TYPE_BOOLEAN,
> > + .min = 0,
> > + .max = 1,
> > + .def = 0,
> > + .step = 1,
> > + }
> > +};
> > +
> > +static int dma2d_setup_ctrls(struct dma2d_ctx *ctx)
> > +{
> > + v4l2_ctrl_handler_init(&ctx->ctrl_handler, 3);
> > +
> > + v4l2_ctrl_new_std(&ctx->ctrl_handler, &dma2d_ctrl_ops,
> > + V4L2_CID_ALPHA_COMPONENT, 0, 255, 1, 255);
> > + v4l2_ctrl_new_custom(&ctx->ctrl_handler, &dma2d_r2m_control[0], NULL);
> > + v4l2_ctrl_new_custom(&ctx->ctrl_handler, &dma2d_r2m_control[1], NULL);
> > +
> > + return 0;
> > +}
> > +
> > +static int dma2d_open(struct file *file)
> > +{
> > + struct dma2d_dev *dev = video_drvdata(file);
> > + struct dma2d_ctx *ctx = NULL;
> > + int ret = 0;
> > +
> > + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
> > + if (!ctx)
> > + return -ENOMEM;
> > + ctx->dev = dev;
> > + /* Set default formats */
> > + ctx->fg = def_frame;
> > + ctx->bg = def_frame;
> > + ctx->out = def_frame;
> > + ctx->op_mode = DMA2D_MODE_M2M_FPC;
> > + ctx->colorspace = V4L2_COLORSPACE_REC709;
> > + ctx->alpha_component = 0x00;
> > + if (mutex_lock_interruptible(&dev->mutex)) {
> > + kfree(ctx);
> > + return -ERESTARTSYS;
> > + }
> > +
> > + ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init);
> > + if (IS_ERR(ctx->fh.m2m_ctx)) {
> > + ret = PTR_ERR(ctx->fh.m2m_ctx);
> > + mutex_unlock(&dev->mutex);
> > + kfree(ctx);
> > + return ret;
> > + }
> > +
> > + v4l2_fh_init(&ctx->fh, video_devdata(file));
> > + file->private_data = &ctx->fh;
> > + v4l2_fh_add(&ctx->fh);
> > +
> > + dma2d_setup_ctrls(ctx);
> > +
> > + /* Write the default values to the ctx struct */
> > + v4l2_ctrl_handler_setup(&ctx->ctrl_handler);
> > +
> > + ctx->fh.ctrl_handler = &ctx->ctrl_handler;
> > + mutex_unlock(&dev->mutex);
> > +
> > + return 0;
> > +}
> > +
> > +static int dma2d_release(struct file *file)
> > +{
> > + struct dma2d_dev *dev = video_drvdata(file);
> > + struct dma2d_ctx *ctx = fh2ctx(file->private_data);
> > +
> > + v4l2_ctrl_handler_free(&ctx->ctrl_handler);
> > + v4l2_fh_del(&ctx->fh);
> > + v4l2_fh_exit(&ctx->fh);
> > + mutex_lock(&dev->mutex);
> > + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
> > + mutex_unlock(&dev->mutex);
> > + kfree(ctx);
> > +
> > + return 0;
> > +}
> > +
> > +static int vidioc_querycap(struct file *file, void *priv,
> > + struct v4l2_capability *cap)
> > +{
> > + strscpy(cap->driver, DMA2D_NAME, sizeof(cap->driver));
> > + strscpy(cap->card, DMA2D_NAME, sizeof(cap->card));
> > + strscpy(cap->bus_info, BUS_INFO, sizeof(cap->bus_info));
> > +
> > + return 0;
> > +}
> > +
> > +static int vidioc_enum_fmt(struct file *file, void *prv, struct v4l2_fmtdesc *f)
> > +{
> > + if (f->index >= NUM_FORMATS)
> > + return -EINVAL;
> > +
> > + f->pixelformat = formats[f->index].fourcc;
> > + return 0;
> > +}
> > +
> > +static int vidioc_g_fmt(struct file *file, void *prv, struct v4l2_format *f)
> > +{
> > + struct dma2d_ctx *ctx = prv;
> > + struct vb2_queue *vq;
> > + struct dma2d_frame *frm;
> > +
> > + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
> > + if (!vq)
> > + return -EINVAL;
> > +
> > + frm = get_frame(ctx, f->type);
> > + if (IS_ERR(frm))
> > + return PTR_ERR(frm);
> > +
> > + f->fmt.pix.width = frm->width;
> > + f->fmt.pix.height = frm->height;
> > + f->fmt.pix.field = V4L2_FIELD_NONE;
> > + f->fmt.pix.pixelformat = frm->fmt->fourcc;
> > + f->fmt.pix.bytesperline = (frm->width * frm->fmt->depth) >> 3;
> > + f->fmt.pix.sizeimage = frm->size;
> > + f->fmt.pix.colorspace = ctx->colorspace;
> > + f->fmt.pix.xfer_func = ctx->xfer_func;
> > + f->fmt.pix.ycbcr_enc = ctx->ycbcr_enc;
> > + f->fmt.pix.quantization = ctx->quant;
> > +
> > + return 0;
> > +}
> > +
> > +static int vidioc_try_fmt(struct file *file, void *prv, struct v4l2_format *f)
> > +{
> > + struct dma2d_ctx *ctx = prv;
> > + struct dma2d_dev *dev = ctx->dev;
> > + struct dma2d_fmt *fmt;
> > + enum v4l2_field *field;
> > + u32 fourcc = f->fmt.pix.pixelformat;
> > +
> > + fmt = find_fmt(fourcc);
> > + if (!fmt) {
> > + v4l2_warn(&dev->v4l2_dev,
> > + "Format not supported: %c%c%c%c, use the default.\n",
> > + (fourcc & 0xff), (fourcc >> 8) & 0xff,
> > + (fourcc >> 16) & 0xff, (fourcc >> 24) & 0xff);
> > + f->fmt.pix.pixelformat = formats[0].fourcc;
> > + fmt = find_fmt(f->fmt.pix.pixelformat);
> > + }
> > +
> > + field = &f->fmt.pix.field;
> > + if (*field == V4L2_FIELD_ANY)
> > + *field = V4L2_FIELD_NONE;
> > + else if (*field != V4L2_FIELD_NONE)
> > + return -EINVAL;
> > +
> > + if (f->fmt.pix.width > MAX_WIDTH)
> > + f->fmt.pix.width = MAX_WIDTH;
> > + if (f->fmt.pix.height > MAX_HEIGHT)
> > + f->fmt.pix.height = MAX_HEIGHT;
> > +
> > + if (f->fmt.pix.width < 1)
> > + f->fmt.pix.width = 1;
> > + if (f->fmt.pix.height < 1)
> > + f->fmt.pix.height = 1;
> > +
> > + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT && !f->fmt.pix.colorspace)
> > + f->fmt.pix.colorspace = V4L2_COLORSPACE_REC709;
> > + else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
> > + f->fmt.pix.colorspace = ctx->colorspace;
> > + f->fmt.pix.xfer_func = ctx->xfer_func;
> > + f->fmt.pix.ycbcr_enc = ctx->ycbcr_enc;
> > + f->fmt.pix.quantization = ctx->quant;
> > + }
> > + f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3;
> > + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
> > +
> > + return 0;
> > +}
> > +
> > +static int vidioc_s_fmt(struct file *file, void *prv, struct v4l2_format *f)
> > +{
> > + struct dma2d_ctx *ctx = prv;
> > + struct dma2d_dev *dev = ctx->dev;
> > + struct vb2_queue *vq;
> > + struct dma2d_frame *frm;
> > + struct dma2d_fmt *fmt;
> > + int ret = 0;
> > +
> > + /* Adjust all values accordingly to the hardware capabilities
> > + * and chosen format.
> > + */
> > + ret = vidioc_try_fmt(file, prv, f);
> > + if (ret)
> > + return ret;
> > +
> > + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
> > + if (vb2_is_busy(vq)) {
> > + v4l2_err(&dev->v4l2_dev, "queue (%d) bust\n", f->type);
> > + return -EBUSY;
> > + }
> > +
> > + frm = get_frame(ctx, f->type);
> > + if (IS_ERR(frm))
> > + return PTR_ERR(frm);
> > +
> > + fmt = find_fmt(f->fmt.pix.pixelformat);
> > + if (!fmt)
> > + return -EINVAL;
> > +
> > + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
> > + ctx->colorspace = f->fmt.pix.colorspace;
> > + ctx->xfer_func = f->fmt.pix.xfer_func;
> > + ctx->ycbcr_enc = f->fmt.pix.ycbcr_enc;
> > + ctx->quant = f->fmt.pix.quantization;
> > + }
> > +
> > + frm->width = f->fmt.pix.width;
> > + frm->height = f->fmt.pix.height;
> > + frm->size = f->fmt.pix.sizeimage;
> > + /* Reset crop settings */
> > + frm->o_width = 0;
> > + frm->o_height = 0;
> > + frm->c_width = frm->width;
> > + frm->c_height = frm->height;
> > + frm->right = frm->width;
> > + frm->bottom = frm->height;
> > + frm->fmt = fmt;
> > + frm->line_ofs = 0;
> > + if (f->fmt.win.global_alpha != 0) {
> > + frm->a_rgb[3] = f->fmt.win.global_alpha;
> > + frm->a_mode = DMA2D_ALPHA_MODE_REPLACE;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static void device_run(void *prv)
> > +{
> > + struct dma2d_ctx *ctx = prv;
> > + struct dma2d_dev *dev = ctx->dev;
> > + struct dma2d_frame *frm_out, *frm_cap;
> > + struct vb2_v4l2_buffer *src, *dst;
> > + unsigned long flags;
> > +
> > + spin_lock_irqsave(&dev->ctrl_lock, flags);
> > + dev->curr = ctx;
> > +
> > + src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
> > + dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
> > + if (dst == NULL || src == NULL)
> > + goto end;
> > +
> > + frm_cap = get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
> > + frm_out = get_frame(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
> > + if (!frm_cap || !frm_out)
> > + goto end;
> > +
> > + src->sequence = frm_out->sequence++;
> > + dst->sequence = frm_cap->sequence++;
> > + v4l2_m2m_buf_copy_metadata(src, dst, true);
> > +
> > + clk_enable(dev->gate);
> > +
> > + dma2d_config_fg(dev, frm_out,
> > + vb2_dma_contig_plane_dma_addr(&src->vb2_buf, 0));
> > +
> > + /* TODO: add M2M_BLEND handler here */
> > +
> > + if (ctx->op_mode != DMA2D_MODE_R2M) {
> > + if (frm_out->fmt->fourcc == frm_cap->fmt->fourcc)
> > + ctx->op_mode = DMA2D_MODE_M2M;
> > + else
> > + ctx->op_mode = DMA2D_MODE_M2M_FPC;
> > + }
> > +
> > + dma2d_config_out(dev, frm_cap,
> > + vb2_dma_contig_plane_dma_addr(&dst->vb2_buf, 0));
> > + dma2d_config_common(dev, ctx->op_mode, frm_cap->width, frm_cap->height);
> > +
> > + dma2d_start(dev);
> > +end:
> > + spin_unlock_irqrestore(&dev->ctrl_lock, flags);
> > +}
> > +
> > +static irqreturn_t dma2d_isr(int irq, void *prv)
> > +{
> > + struct dma2d_dev *dev = prv;
> > + struct dma2d_ctx *ctx = dev->curr;
> > + struct vb2_v4l2_buffer *src, *dst;
> > + uint32_t s = dma2d_get_int(dev);
> > +
> > + if (s & ISR_TCIF || s == 0) {
> > + dma2d_clear_int(dev);
> > + clk_disable(dev->gate);
> > +
> > + WARN_ON(ctx == NULL);
> > +
> > + src = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
> > + dst = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
> > +
> > + WARN_ON(dst == NULL);
> > + WARN_ON(src == NULL);
> > +
> > + v4l2_m2m_buf_done(src, VB2_BUF_STATE_DONE);
> > + v4l2_m2m_buf_done(dst, VB2_BUF_STATE_DONE);
> > + v4l2_m2m_job_finish(dev->m2m_dev, ctx->fh.m2m_ctx);
> > +
> > + dev->curr = NULL;
> > + } else {
> > + dma2d_clear_int(dev);
> > + }
> > +
> > + return IRQ_HANDLED;
> > +}
> > +
> > +static const struct v4l2_file_operations dma2d_fops = {
> > + .owner = THIS_MODULE,
> > + .open = dma2d_open,
> > + .release = dma2d_release,
> > + .poll = v4l2_m2m_fop_poll,
> > + .unlocked_ioctl = video_ioctl2,
> > + .mmap = v4l2_m2m_fop_mmap,
> > +#ifndef CONFIG_MMU
> > + .get_unmapped_area = v4l2_m2m_get_unmapped_area,
> > +#endif
> > +};
> > +
> > +static const struct v4l2_ioctl_ops dma2d_ioctl_ops = {
> > + .vidioc_querycap = vidioc_querycap,
> > +
> > + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt,
> > + .vidioc_g_fmt_vid_cap = vidioc_g_fmt,
> > + .vidioc_try_fmt_vid_cap = vidioc_try_fmt,
> > + .vidioc_s_fmt_vid_cap = vidioc_s_fmt,
> > +
> > + .vidioc_enum_fmt_vid_out = vidioc_enum_fmt,
> > + .vidioc_g_fmt_vid_out = vidioc_g_fmt,
> > + .vidioc_try_fmt_vid_out = vidioc_try_fmt,
> > + .vidioc_s_fmt_vid_out = vidioc_s_fmt,
> > +
> > + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
> > + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
> > + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
> > + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
> > + .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,
> > + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
> > + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
> > +
> > + .vidioc_streamon = v4l2_m2m_ioctl_streamon,
> > + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
> > +
> > + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
> > + .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
> > +};
> > +
> > +static const struct video_device dma2d_videodev = {
> > + .name = DMA2D_NAME,
> > + .fops = &dma2d_fops,
> > + .ioctl_ops = &dma2d_ioctl_ops,
> > + .minor = -1,
> > + .release = video_device_release,
> > + .vfl_dir = VFL_DIR_M2M,
> > +};
> > +
> > +static const struct v4l2_m2m_ops dma2d_m2m_ops = {
> > + .device_run = device_run,
> > +};
> > +
> > +static const struct of_device_id stm32_dma2d_match[];
> > +
> > +static int dma2d_probe(struct platform_device *pdev)
> > +{
> > + struct dma2d_dev *dev;
> > + struct video_device *vfd;
> > + struct resource *res;
> > + int ret = 0;
> > +
> > + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
> > + if (!dev)
> > + return -ENOMEM;
> > +
> > + spin_lock_init(&dev->ctrl_lock);
> > + mutex_init(&dev->mutex);
> > + atomic_set(&dev->num_inst, 0);
> > +
> > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > +
> > + dev->regs = devm_ioremap_resource(&pdev->dev, res);
> > + if (IS_ERR(dev->regs))
> > + return PTR_ERR(dev->regs);
> > +
> > + dev->gate = clk_get(&pdev->dev, "dma2d");
> > + if (IS_ERR(dev->gate)) {
> > + dev_err(&pdev->dev, "failed to get dma2d clock gate\n");
> > + ret = -ENXIO;
> > + return ret;
> > + }
> > +
> > + ret = clk_prepare(dev->gate);
> > + if (ret) {
> > + dev_err(&pdev->dev, "failed to prepare dma2d clock gate\n");
> > + goto put_clk_gate;
> > + }
> > +
> > + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
> > + if (!res) {
> > + dev_err(&pdev->dev, "failed to find IRQ\n");
> > + ret = -ENXIO;
> > + goto unprep_clk_gate;
> > + }
> > +
> > + dev->irq = res->start;
> > +
> > + ret = devm_request_irq(&pdev->dev, dev->irq, dma2d_isr,
> > + 0, pdev->name, dev);
> > + if (ret) {
> > + dev_err(&pdev->dev, "failed to install IRQ\n");
> > + goto unprep_clk_gate;
> > + }
> > +
> > + ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
> > + if (ret)
> > + goto unprep_clk_gate;
> > +
> > + vfd = video_device_alloc();
> > + if (!vfd) {
> > + v4l2_err(&dev->v4l2_dev, "Failed to allocate video device\n");
> > + ret = -ENOMEM;
> > + goto unreg_v4l2_dev;
> > + }
> > +
> > + *vfd = dma2d_videodev;
> > + set_bit(V4L2_FL_QUIRK_INVERTED_CROP, &vfd->flags);
> > + vfd->lock = &dev->mutex;
> > + vfd->v4l2_dev = &dev->v4l2_dev;
> > + vfd->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING;
> > + ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0);
> > + if (ret) {
> > + v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
> > + goto rel_vdev;
> > + }
> > +
> > + video_set_drvdata(vfd, dev);
> > + dev->vfd = vfd;
> > + v4l2_info(&dev->v4l2_dev, "device registered as /dev/video%d\n",
> > + vfd->num);
> > + platform_set_drvdata(pdev, dev);
> > + dev->m2m_dev = v4l2_m2m_init(&dma2d_m2m_ops);
> > + if (IS_ERR(dev->m2m_dev)) {
> > + v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n");
> > + ret = PTR_ERR(dev->m2m_dev);
> > + goto unreg_video_dev;
> > + }
> > +
> > + v4l2_info(&dev->v4l2_dev, "stm32 dma2d initialized\n");
> > + return 0;
> > +
> > +unreg_video_dev:
> > + video_unregister_device(dev->vfd);
> > +rel_vdev:
> > + video_device_release(vfd);
> > +unreg_v4l2_dev:
> > + v4l2_device_unregister(&dev->v4l2_dev);
> > +unprep_clk_gate:
> > + clk_unprepare(dev->gate);
> > +put_clk_gate:
> > + clk_put(dev->gate);
> > +
> > + return ret;
> > +}
> > +
> > +static int dma2d_remove(struct platform_device *pdev)
> > +{
> > + struct dma2d_dev *dev = platform_get_drvdata(pdev);
> > +
> > + v4l2_info(&dev->v4l2_dev, "Removing " DMA2D_NAME);
> > + v4l2_m2m_release(dev->m2m_dev);
> > + video_unregister_device(dev->vfd);
> > + v4l2_device_unregister(&dev->v4l2_dev);
> > + vb2_dma_contig_clear_max_seg_size(&pdev->dev);
> > + clk_unprepare(dev->gate);
> > + clk_put(dev->gate);
> > +
> > + return 0;
> > +}
> > +
> > +static const struct of_device_id stm32_dma2d_match[] = {
> > + {
> > + .compatible = "st,stm32-dma2d",
> > + .data = NULL,
> > + },
> > + {},
> > +};
> > +MODULE_DEVICE_TABLE(of, stm32_dma2d_match);
> > +
> > +static struct platform_driver dma2d_pdrv = {
> > + .probe = dma2d_probe,
> > + .remove = dma2d_remove,
> > + .driver = {
> > + .name = DMA2D_NAME,
> > + .of_match_table = stm32_dma2d_match,
> > + },
> > +};
> > +
> > +module_platform_driver(dma2d_pdrv);
> > +
> > +MODULE_AUTHOR("Dillon Min <[email protected]>");
> > +MODULE_DESCRIPTION("STM32 Chrom-Art Accelerator DMA2D driver");
> > +MODULE_LICENSE("GPL");
> > diff --git a/drivers/media/platform/stm32/dma2d/dma2d.h b/drivers/media/platform/stm32/dma2d/dma2d.h
> > new file mode 100644
> > index 000000000000..1c64cd2f7587
> > --- /dev/null
> > +++ b/drivers/media/platform/stm32/dma2d/dma2d.h
> > @@ -0,0 +1,132 @@
> > +/* SPDX-License-Identifier: GPL-2.0-or-later */
> > +/*
> > + * ST stm32 DMA2D - 2D Graphics Accelerator Driver
> > + *
> > + * Copyright (c) 2021 Dillon Min
> > + * Dillon Min, <[email protected]>
> > + *
> > + * based on s5p-g2d
> > + *
> > + * Copyright (c) 2011 Samsung Electronics Co., Ltd.
> > + * Kamil Debski, <[email protected]>
> > + */
> > +
> > +#ifndef __DMA2D_H__
> > +#define __DMA2D_H__
> > +
> > +#include <linux/platform_device.h>
> > +#include <media/v4l2-device.h>
> > +#include <media/v4l2-ctrls.h>
> > +
> > +#define DMA2D_NAME "stm-dma2d"
> > +#define BUS_INFO "platform:stm-dma2d"
> > +enum dma2d_op_mode {
> > + DMA2D_MODE_M2M,
> > + DMA2D_MODE_M2M_FPC,
> > + DMA2D_MODE_M2M_BLEND,
> > + DMA2D_MODE_R2M
> > +};
> > +
> > +enum dma2d_cmode {
> > + /* output pfc cmode from ARGB888 to ARGB4444 */
> > + DMA2D_CMODE_ARGB8888,
> > + DMA2D_CMODE_RGB888,
> > + DMA2D_CMODE_RGB565,
> > + DMA2D_CMODE_ARGB1555,
> > + DMA2D_CMODE_ARGB4444,
> > + /* bg or fg pfc cmode from L8 to A4 */
> > + DMA2D_CMODE_L8,
> > + DMA2D_CMODE_AL44,
> > + DMA2D_CMODE_AL88,
> > + DMA2D_CMODE_L4,
> > + DMA2D_CMODE_A8,
> > + DMA2D_CMODE_A4
> > +};
> > +
> > +enum dma2d_alpha_mode {
> > + DMA2D_ALPHA_MODE_NO_MODIF,
> > + DMA2D_ALPHA_MODE_REPLACE,
> > + DMA2D_ALPHA_MODE_COMBINE
> > +};
> > +
> > +struct dma2d_fmt {
> > + u32 fourcc;
> > + int depth;
> > + enum dma2d_cmode cmode;
> > +};
> > +
> > +struct dma2d_frame {
> > + /* Original dimensions */
> > + u32 width;
> > + u32 height;
> > + /* Crop size */
> > + u32 c_width;
> > + u32 c_height;
> > + /* Offset */
> > + u32 o_width;
> > + u32 o_height;
> > + u32 bottom;
> > + u32 right;
> > + u16 line_ofs;
> > + /* Image format */
> > + struct dma2d_fmt *fmt;
> > + /* [0]: blue
> > + * [1]: green
> > + * [2]: red
> > + * [3]: alpha
> > + */
> > + u8 a_rgb[4];
> > + /*
> > + * AM[1:0] of DMA2D_FGPFCCR
> > + */
> > + enum dma2d_alpha_mode a_mode;
> > + u32 size;
> > + unsigned int sequence;
> > +};
> > +
> > +struct dma2d_ctx {
> > + struct v4l2_fh fh;
> > + struct dma2d_dev *dev;
> > + struct dma2d_frame fg;
> > + struct dma2d_frame out;
> > + struct dma2d_frame bg;
> > + /* fb_buf always point to bg address */
> > + struct v4l2_framebuffer fb_buf;
> > + /*
> > + * MODE[17:16] of DMA2D_CR
> > + */
> > + enum dma2d_op_mode op_mode;
> > + u8 alpha_component;
> > + struct v4l2_ctrl_handler ctrl_handler;
> > + enum v4l2_colorspace colorspace;
> > + enum v4l2_ycbcr_encoding ycbcr_enc;
> > + enum v4l2_xfer_func xfer_func;
> > + enum v4l2_quantization quant;
> > +};
> > +
> > +struct dma2d_dev {
> > + struct v4l2_device v4l2_dev;
> > + struct v4l2_m2m_dev *m2m_dev;
> > + struct video_device *vfd;
> > + struct mutex mutex;
> > + spinlock_t ctrl_lock;
> > + atomic_t num_inst;
> > + void __iomem *regs;
> > + struct clk *gate;
> > + struct dma2d_ctx *curr;
> > + int irq;
> > +};
> > +
> > +void dma2d_start(struct dma2d_dev *d);
> > +u32 dma2d_get_int(struct dma2d_dev *d);
> > +void dma2d_clear_int(struct dma2d_dev *d);
> > +void dma2d_config_out(struct dma2d_dev *d, struct dma2d_frame *frm,
> > + dma_addr_t o_addr);
> > +void dma2d_config_fg(struct dma2d_dev *d, struct dma2d_frame *frm,
> > + dma_addr_t f_addr);
> > +void dma2d_config_bg(struct dma2d_dev *d, struct dma2d_frame *frm,
> > + dma_addr_t b_addr);
> > +void dma2d_config_common(struct dma2d_dev *d, enum dma2d_op_mode op_mode,
> > + u16 width, u16 height);
> > +
> > +#endif /* __DMA2D_H__ */
> >
>
> Regards,
>
> Hans

2021-06-17 11:27:04

by Dillon Min

[permalink] [raw]
Subject: Re: [PATCH 0/7] Add support for DMA2D of STMicroelectronics STM32 SoC series

Hi Hans,

On Thu, Jun 17, 2021 at 5:55 PM Hans Verkuil <[email protected]> wrote:
>
> Hi Dillon,
>
> Sorry for the delay in reviewing this series.
>
> On 20/05/2021 13:05, [email protected] wrote:
> > From: Dillon Min <[email protected]>
> >
> > This patchset introduces a basic support for DMA2D Interface
> > of STMicroelectronics STM32 SoC series.
> >
> > This first basic support implements R2M, M2M, M2M_PFC
> > M2M_BLEND support will be added later on.
> >
> > This has been tested on STM32469-DISCO board.
> >
> > The commit based on kernel(master): c3d0e3fd41b7f0f5d5d5b6022ab7e813f04ea727
> >
> > Note for v4l2-compliance tool on nu-mmu platform:
> > I add two change based on v4l-utils since commit:
> > f0c7e3d71eaf4182bae7eb3ee0e43b4eeb047ea9
> >
> > - change fork() to vfork() in v4l2-test-controls.cpp
> > since no-mmu platform don't include fork().
>
> I don't think you can use vfork here. What is better is that if fork is
> not available, this test is just skipped.

Okay, try to find a solution for the no-mmu platform, but no progress.
Anyway, if you update v4l2-utils to be compatible with the no-mmu platform,
please let me know, i will retest this case, thanks.

>
> >
> > - bypass VIDIOC_QUERYCAP nullptr check
> > I'm not sure if this is the bug from my cross compile tool
> > which created by buildroot. user area's nullptr can't be
> > detected by kernel side, i was try to add access_ok()
> > in v4l2-ioctl.c, but no help
> >
> > If this case must be fixed, i will continue to debug it.
> > the error log:
> > ...
> > fail: v4l2-compliance.cpp(623): doioctl(node, VIDIOC_QUERYCAP, nullptr) !=
> > EFAULT
> > ..
>
> Hmm, there is no mmu, so anything goes, really. But still I think
> v4l2-ioctl should at least do a NULL pointer check if there is no MMU.
>
> That seems reasonable to me. I wonder how other subsytems do that.

Okay, indeed, the application should check the NULL pointer. thanks.
I will continue to debug on this case.

Thanks
Best Regards

Dillon

>
> Regards,
>
> Hans
>
> >
> > My changes on v4l2-compliance:
> >
> > diff --git a/utils/v4l2-compliance/v4l2-compliance.cpp
> > b/utils/v4l2-compliance/v4l2-compliance.cpp
> > index 90a5036..a25fe4f 100644
> > --- a/utils/v4l2-compliance/v4l2-compliance.cpp
> > +++ b/utils/v4l2-compliance/v4l2-compliance.cpp
> > @@ -620,7 +620,7 @@ static int testCap(struct node *node)
> >
> > memset(&vcap, 0xff, sizeof(vcap));
> > // Must always be there
> > - fail_on_test(doioctl(node, VIDIOC_QUERYCAP, nullptr) != EFAULT);
> > + //fail_on_test(doioctl(node, VIDIOC_QUERYCAP, nullptr) != EFAULT);
> > fail_on_test(doioctl(node, VIDIOC_QUERYCAP, &vcap));
> > fail_on_test(check_ustring(vcap.driver, sizeof(vcap.driver)));
> > fail_on_test(check_ustring(vcap.card, sizeof(vcap.card)));
> >
> > with v4l2-compliance test log (with above modification):
> > since the stm32f469-disco ram limitation, there are 25 failed on
> > dma_alloc_coherent()
> >
> > Really appreciate if someone can help to test this patch on the STM32429I-EVAL
> > evaluation board (https://www.st.com/en/evaluation-tools/stm32429i-eval.html)
> > 8M x 32-bit SDRAM, 1M x 16-bit SRAM and 8M x 16-bit NOR Flash
> >
> > / # free
> > total used free shared buffers cached
> > Mem: 15604 6104 9500 0 0 2488
> > -/+ buffers/cache: 3616 11988
> >
> >
> >
> >
> > / # v4l2-compliance -s -f -d /dev/video0 &
> > [1] 45 v4l2-compliance -s -f -d /dev/video0
> > / # v4l2-compliance 1.21.0-4782, 32 bits, 32-bit time_t
> > v4l2-compliance SHA: f0c7e3d71eaf 2021-05-06 10:57:09
> >
> > Compliance test for stm-dma2d device /dev/video0:
> >
> > Driver Info:
> > Driver name : stm-dma2d
> > Card type : stm-dma2d
> > Bus info : platform:stm-dma2d
> > Driver version : 5.13.0
> > Capabilities : 0x84208000
> > Video Memory-to-Memory
> > Streaming
> > Extended Pix Format
> > Device Capabilities
> > Device Caps : 0x04208000
> > Video Memory-to-Memory
> > Streaming
> > Extended Pix Format
> >
> > Required ioctls:
> > test VIDIOC_QUERYCAP: OK
> > test invalid ioctls: OK
> >
> > Allow for multiple opens:
> > test second /dev/video0 open: OK
> > test VIDIOC_QUERYCAP: OK
> > test VIDIOC_G/S_PRIORITY: OK
> > test for unlimited opens: OK
> >
> > Debug ioctls:
> > test VIDIOC_DBG_G/S_REGISTER: OK (Not Supported)
> > test VIDIOC_LOG_STATUS: OK (Not Supported)
> >
> > Input ioctls:
> > test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK (Not Supported)
> > test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
> > test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported)
> > test VIDIOC_ENUMAUDIO: OK (Not Supported)
> > test VIDIOC_G/S/ENUMINPUT: OK (Not Supported)
> > test VIDIOC_G/S_AUDIO: OK (Not Supported)
> > Inputs: 0 Audio Inputs: 0 Tuners: 0
> >
> > Output ioctls:
> > test VIDIOC_G/S_MODULATOR: OK (Not Supported)
> > test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
> > test VIDIOC_ENUMAUDOUT: OK (Not Supported)
> > test VIDIOC_G/S/ENUMOUTPUT: OK (Not Supported)
> > test VIDIOC_G/S_AUDOUT: OK (Not Supported)
> > Outputs: 0 Audio Outputs: 0 Modulators: 0
> >
> > Input/Output configuration ioctls:
> > test VIDIOC_ENUM/G/S/QUERY_STD: OK (Not Supported)
> > test VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: OK (Not Supported)
> > test VIDIOC_DV_TIMINGS_CAP: OK (Not Supported)
> > test VIDIOC_G/S_EDID: OK (Not Supported)
> >
> > Control ioctls:
> > test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK
> > test VIDIOC_QUERYCTRL: OK
> > test VIDIOC_G/S_CTRL: OK
> > test VIDIOC_G/S/TRY_EXT_CTRLS: OK
> > test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK
> > test VIDIOC[G/S_JP 143.242702] stm-dma2d 4002b000.dma2d: Format not supported: , use the default.
> > EGCOMP: OK (Not Supported)
> > St[ndard Controls: 2 Private Contro s: 2
> >
> > Fo m1t ioctls:
> > test VIDIOC_4NUM_FMT/FRAMESIZES/FRAMEINTERV3LS: O.0m
> > test VIDIOC_G5S_PARM: 3K (Not 555] stm-dma2d 4002b000.dma2d: Format not supported: , use the default.
> > Supported)
> > test VIDIOC_G_FBUF: OK (Not Supported)
> > 143.280786] stm-dma2d 4002b000.dma2d: Format not supported: , use the default.
> > test VIDIOC_TRY_FMT: OK
> > [ 143.293759] stm-dma2d 4002b000.dma2d: Format not supported: , use the default.
> > test VIDIOC_S_FMT: OK
> > test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
> > test Cropping: OK (Not Supported)
> > test Composing: OK (Not Supported)
> > test Scaling: OK
> >
> > Codec ioctls:
> > test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported)
> > test VIDIOC_G_ENC_INDEX: OK (Not Supported)
> > test VIDIOC_(TRY_)DECODER_CMD: OK (Not Supported)
> >
> > Buffer ioctls:
> > test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK
> > test VIDIOC_EXPBUF: OK
> > test Requests: OK (Not Supported)
> >
> > Test input 0:
> >
> > Streaming ioctls:
> > test read/write: OK (Not Supported)
> > test blocking wait: OK
> > Video Capture: Captured 58 buffers
> > test MMAP (no poll): OK
> > Video Capture: Captured 58 buffers
> > test MMAP (select): OK
> > Video Capture: Captured 58 buffers
> > test MMAP (epoll): OK
> > test USERPTR (no poll): OK (Not Supported)
> > test USERPTR (select): OK (Not Supported)
> > test DMABUF: Cannot test, specify --expbuf-device
> >
> > Stream using all formats:
> > Video Capture: Captured 8 buffers
> > BA24 (32-bit ARGB 8-8-8-8) 1x1 -> BA24 (32-bit ARGB 8-8-8-8) 1x1: OK
> > Video Capture: Captured 8 buffers
> > BA24 (32-bit ARGB 8-8-8-8) 1x1 -> RGB3 (24-bit RGB 8-8-8) 1x1: OK
> > Video Capture: Captured 8 buffers
> > BA24 (32-bit ARGB 8-8-8-8) 1x1 -> RGBP (16-bit RGB 5-6-5) 1x1: OK
> > Video Capture: Captured 8 buffers
> > BA24 (32-bit ARGB 8-8-8-8) 1x1 -> AR15 (16-bit ARGB 1-5-5-5) 1x1: OK
> > Video Capture: Captured 8 buffers
> > BA24 (32-bit ARGB 8-8-8-8) 1x1 -[ AR12 (16-bit ARGB 4-4-4-4) 1x1: OK
> > 146.963829] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size -323584 failed
> > fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> > BA24 (32-bit ARGB 8-8-8-8) 16383x6[535 -> BA 4 (32-bit ARGB 8-8-8 8) 11383x65545: FAIL
> > 6.985202] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size -323584 failed
> > fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> > BA24 (32[bit ARGB 8-8-8-8) 16383x65 35 1> RGB3 (24-bit RGB48-8-8) 16383x65535: FAI7
> > .008713] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size -323584 failed
> > fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> > BA24 (32-bit ARG[ 8-8-8-8) 163 3x6 535 -> RGBP (16-bit RGB 5-6-5) 16383x65535: FAIL
> > 147.033896] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size -323584 failed
> > fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> > BA24 (32-[it ARGB 8- -8-8) 16383x65535 -> AR15 (16-bit ARGB -5-515) 16383x65535: FAIL
> > 47.058256] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size -323584 failed
> > fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> > BA24 (32-bit ARGB 8-8-8-8) 16383x65535 -> AR12 (16-bit ARGB 4-4-4-4) 16383x65535: FAIL
> > Video Capture: Captured 8 buffers
> > BA24 (32-bit ARGB 8-8-8-8) 240x320 -> BA24 (32-bit ARGB 8-8-8-8) 240x320: OK
> > Video Capture: Captured 8 buffers
> > BA24 (32-bit ARGB 8-8-8-8) 240x320 -> RGB3 (24-bit RGB 8-8-8) 240x320: OK
> > Video Capture: Captured 8 buffers
> > BA24 (32-bit ARGB 8-8-8-8) 240x320 -> RGBP (16-bit RGB 5-6-5) 240x320: OK
> > Video Capture: Captured 8 buffers
> > BA24 (32-bit ARGB 8-8-8-8) 240x320 -> AR15 (16-bit ARGB 1-5-5-5) 240x320: OK
> > Video Capture: Captured 8 buffers
> > BA24 (32-bit ARGB 8-8-8-8) 240x320 -> AR12 (16-bit ARGB 4-4-4-4) 240x320: OK
> > Video Capture: Captured 8 buffers
> > RGB3 (24-bit RGB 8-8-8) 1x1 -> BA24 (32-bit ARGB 8-8-8-8) 1x1: OK
> > Video Capture: Captured 8 buffers
> > RGB3 (24-bit RGB 8-8-8) 1x1 -> RGB3 (24-bit RGB 8-8-8) 1x1: OK
> > Video Capture: Captured 8 buffers
> > RGB3 (24-bit RGB 8-8-8) 1x1 -> RGBP (16-bit RGB 5-6-5) 1x1: OK
> > Video Capture: Captured 8 buffers
> > RGB3 (24-bit RGB 8-8-8) 1x1 -> AR15 (16-bit ARGB 1-5-5-5) 1x1: OK
> > Video Capture: Captured 8 buffers
> > RGB3 (24-bit RGB 8-8-8) 1x1 -> AR12 (16-bit ARGB 4-4-4-4) 1[1: K
> > 148.406686] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size -1073983488 failed
> > fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> > RGB3 (24-bit RGB 8-8-8) 1[383x 5 35 -1 BA24 (32-b4t ARGB 888-8-8) 16383x65.35: FAIL
> > 28566] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size -1073983488 failed
> > fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> > RGB3 (24-bit RGB 8-8-8) 16383x65535[-> RGB3 (24 bit RGB 8-8-8) 16383 65535: FAIL
> > 148.453973] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size -1073983488 failed
> > fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> > RGB3 (24-bit RGB 8-8-8) 16383x65535 [> RGBP (16-bit RGB 5-6-5) 16383x65535: FAIL
> > 148.477828] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size -1073983488 failed
> > fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> > RGB3 (24-bit RGB 8[8-8) 16383x 5535 -> AR 5 (16-bit ARGB 1-514-58 16383x65535: FAIL
> > .503495] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size -1073983488 failed
> > fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> > RGB3 (24-bit RGB 8-8-8) 16383x65535 -> AR12 (16-bit ARGB 4-4-4-4) 16383x65535: FAIL
> > Video Capture: Captured 8 buffers
> > RGB3 (24-bit RGB 8-8-8) 240x320 -> BA24 (32-bit ARGB 8-8-8-8) 240x320: OK
> > Video Capture: Captured 8 buffers
> > RGB3 (24-bit RGB 8-8-8) 240x320 -> RGB3 (24-bit RGB 8-8-8) 240x320: OK
> > Video Capture: Captured 8 buffers
> > RGB3 (24-bit RGB 8-8-8) 240x320 -> RGBP (16-bit RGB 5-6-5) 240x320: OK
> > Video Capture: Captured 8 buffers
> > RGB3 (24-bit RGB 8-8-8) 240x320 -> AR15 (16-bit ARGB 1-5-5-5) 240x320: OK
> > Video Capture: Captured 8 buffers
> > RGB3 (24-bit RGB 8-8-8) 240x320 -> AR12 (16-bit ARGB 4-4-4-4) 240x320: OK
> > Video Capture: Captured 8 buffers
> > RGBP (16-bit RGB 5-6-5) 1x1 -> BA24 (32-bit ARGB 8-8-8-8) 1x1: OK
> > Video Capture: Captured 8 buffers
> > RGBP (16-bit RGB 5-6-5) 1x1 -> RGB3 (24-bit RGB 8-8-8) 1x1: OK
> > Video Capture: Captured 8 buffers
> > RGBP (16-bit RGB 5-6-5) 1x1 -> RGBP (16-bit RGB 5-6-5) 1x1: OK
> > Video Capture: Captured 8 buffers
> > RGBP (16-bit RGB 5-6-5) 1x1 -> AR15 (16-bit ARGB 1-5-5-5) 1x1: OK
> > Video Capture: Captured 8 buffers
> > 149.725823] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
> > fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> > RGBP (16-bit RGB 5-6-5) 1[383x65535 -> BA24 (32-bi ARGB 8 8-8-8) 16383x65535: FAIL
> > 49.746860] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
> > fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> > RGBP (16-bit RGB 5-[-5) 16383x65535 -> RGB3 (24-b t RGB 8-8-8) 16383x65535: FAIL
> > 49.772555] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
> > fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> > RGBP (16-bit R[B 5-6-5 1638 x65535 -> RGBP1(16-bit 4GB 596-5. 16383x67535: FAIL
> > 330] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
> > fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> > RGBP (16-bit RGB 5-6-5) 16383x65535 ->[AR15 (1 -bit AR B 1-5-5-5) 11383x65535: 9IL
> > .821301] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
> > fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> > RGBP (16-bit RGB 5-6-5) 16383x65535 -> AR12 (16-bit ARGB 4-4-4-4) 16383x65535: FAIL
> > Video Capture: Captured 8 buffers
> > RGBP (16-bit RGB 5-6-5) 240x320 -> BA24 (32-bit ARGB 8-8-8-8) 240x320: OK
> > Video Capture: Captured 8 buffers
> > RGBP (16-bit RGB 5-6-5) 240x320 -> RGB3 (24-bit RGB 8-8-8) 240x320: OK
> > Video Capture: Captured 8 buffers
> > RGBP (16-bit RGB 5-6-5) 240x320 -> RGBP (16-bit RGB 5-6-5) 240x320: OK
> > Video Capture: Captured 8 buffers
> > RGBP (16-bit RGB 5-6-5) 240x320 -> AR15 (16-bit ARGB 1-5-5-5) 240x320: OK
> > Video Capture: Captured 8 buffers
> > RGBP (16-bit RGB 5-6-5) 240x320 -> AR12 (16-bit ARGB 4-4-4-4) 240x320: OK
> > Video Capture: Captured 8 buffers
> > AR15 (16-bit ARGB 1-5-5-5) 1x1 -> BA24 (32-bit ARGB 8-8-8-8) 1x1: OK
> > Video Capture: Captured 8 buffers
> > AR15 (16-bit ARGB 1-5-5-5) 1x1 -> RGB3 (24-bit RGB 8-8-8) 1x1: OK
> > Video Capture: Captured 8 buffers
> > AR15 (16-bit ARGB 1-5-5-5) 1x1 -> RGBP (16-bit RGB 5-6-5) 1x1: OK
> > Video Capture: Captured 8 buffers
> > AR15 (16-bit ARGB 1-5-5-5) 1x1 -> AR15 (16-bit ARGB 1-5-5-5) 1x1: OK
> > Video Capture: Captured 8 buffers
> > AR15 (16-bit ARGB 1-5-5-5) 1x1 -[ AR1 (16-bit ARGB 4-4-4-4) 1x15 O0
> > .956666] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
> > fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> > AR15 (16-bit ARGB 1-[-5-5) 16 83x65535 -> BA24 (32-bit ARGB 8 8-8-8) 16383x15535: FAIL
> > 50.977546] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
> > fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> > AR15 (16-bit AR[B 1-5-5-5) 16383x65535 -> R B3 ( 4-b1t RGB 8-8-8) 16383x65535:5FAIL
> > 1.003061] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
> > fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> > AR15 (16-bit ARGB 1-5-5[5) 16383x65535 -> RGBP (16-bit RGB 5-6-5) 16383x65535: FAIL
> > 151.027900] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
> > fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> > AR15 (16-bit ARGB 1-5[5-5) 16383x65535 -> AR15 (16- it ARGB 1-5-5-5) 16383x65 15: F5IL
> > 1.053781] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
> > fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> > AR15 (16-bit ARGB 1-5-5-5) 16383x65535 -> AR12 (16-bit ARGB 4-4-4-4) 16383x65535: FAIL
> > Video Capture: Captured 8 buffers
> > AR15 (16-bit ARGB 1-5-5-5) 240x320 -> BA24 (32-bit ARGB 8-8-8-8) 240x320: OK
> > Video Capture: Captured 8 buffers
> > AR15 (16-bit ARGB 1-5-5-5) 240x320 -> RGB3 (24-bit RGB 8-8-8) 240x320: OK
> > Video Capture: Captured 8 buffers
> > AR15 (16-bit ARGB 1-5-5-5) 240x320 -> RGBP (16-bit RGB 5-6-5) 240x320: OK
> > Video Capture: Captured 8 buffers
> > AR15 (16-bit ARGB 1-5-5-5) 240x320 -> AR15 (16-bit ARGB 1-5-5-5) 240x320: OK
> > Video Capture: Captured 8 buffers
> > AR15 (16-bit ARGB 1-5-5-5) 240x320 -> AR12 (16-bit ARGB 4-4-4-4) 240x320: OK
> > Video Capture: Captured 8 buffers
> > AR12 (16-bit ARGB 4-4-4-4) 1x1 -> BA24 (32-bit ARGB 8-8-8-8) 1x1: OK
> > Video Capture: Captured 8 buffers
> > AR12 (16-bit ARGB 4-4-4-4) 1x1 -> RGB3 (24-bit RGB 8-8-8) 1x1: OK
> > Video Capture: Captured 8 buffers
> > AR12 (16-bit ARGB 4-4-4-4) 1x1 -> RGBP (16-bit RGB 5-6-5) 1x1: OK
> > Video Capture: Captured 8 buffers
> > AR12 (16-bit ARGB 4-4-4-4) 1x1 -> AR15 (16-bit ARGB 1-5-5-5) 1x1: OK
> > Video Capture: Captured 8 buffers
> > AR12 (16-bit ARGB 4-4-4-4) 1x1 -> AR12 (16-bit A[GB 4-4-4 4) 1x1: OK
> > 152.187325] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
> > fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> > AR12 (16-[ t A GB 4-4-4-4) 16383x65135 -> BA24 (32-bit ARGB 8-8-8-8) 16383x6553552.21;31mFAIL0[0m
> > 8867] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
> > fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> > AR12 (16-bit ARGB 4-4[4-4 16383x6553 -> RGB3 (24-bit RGB18-8-8) 16383x65535: FAIL
> > 52.234355] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
> > fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> > AR12 (16-bit ARGB 4-4-4-4) 16383x65535 -> RGBP (16-bit RGB 5-6-5) 16383x65535: [ 1; 1mFAIL
> > 152.258077] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
> > fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> > AR12 (16-bit ARGB 4-4-4[4) 16383x65535 -> R15 (16-bit ARGB 1-5-5-5) 16383x65535: FAIL
> > 152.284054] stm-dma2d 4002b000.dma2d: dma_alloc_coherent of size 2147323904 failed
> > fail: v4l2-test-buffers.cpp(1300): q.reqbufs(node, 2)
> > AR12 (16-bit ARGB 4-4-4-4) 16383x65535 -> AR12 (16-bit ARGB 4-4-4-4) 16383x65535: FAIL
> > Video Capture: Captured 8 buffers
> > AR12 (16-bit ARGB 4-4-4-4) 240x320 -> BA24 (32-bit ARGB 8-8-8-8) 240x320: OK
> > Video Capture: Captured 8 buffers
> > AR12 (16-bit ARGB 4-4-4-4) 240x320 -> RGB3 (24-bit RGB 8-8-8) 240x320: OK
> > Video Capture: Captured 8 buffers
> > AR12 (16-bit ARGB 4-4-4-4) 240x320 -> RGBP (16-bit RGB 5-6-5) 240x320: OK
> > Video Capture: Captured 8 buffers
> > AR12 (16-bit ARGB 4-4-4-4) 240x320 -> AR15 (16-bit ARGB 1-5-5-5) 240x320: OK
> > Video Capture: Captured 8 buffers
> > AR12 (16-bit ARGB 4-4-4-4) 240x320 -> AR12 (16-bit ARGB 4-4-4-4) 240x320: OK
> > Total for stm-dma2d device /dev/video0: 127, Succeeded: 102, Failed: 25, Warnings: 0
> >
> > [1] Done v4l2-compliance -s -f -d /dev/video0
> > *** BLURB HERE ***
> >
> > Dillon Min (7):
> > media: admin-guide: add stm32-dma2d description
> > media: dt-bindings: media: add document for STM32 DMA2d bindings
> > clk: stm32: Fix ltdc's clock turn off by clk_disable_unused() after
> > kernel startup
> > ARM: dts: stm32: Enable DMA2D support on STM32F429 MCU
> > ARM: dts: stm32: Enable DMA2D on STM32F469-DISCO board
> > media: v4l2-mem2mem: add v4l2_m2m_get_unmapped_area for no-mmu
> > platform
> > media: stm32-dma2d: STM32 DMA2D driver
> >
> > .../admin-guide/media/platform-cardlist.rst | 1 +
> > .../devicetree/bindings/media/st,stm32-dma2d.yaml | 71 ++
> > arch/arm/boot/dts/stm32f429.dtsi | 10 +
> > arch/arm/boot/dts/stm32f469-disco.dts | 4 +
> > drivers/clk/clk-stm32f4.c | 7 +-
> > drivers/media/platform/Kconfig | 9 +
> > drivers/media/platform/Makefile | 1 +
> > drivers/media/platform/stm32/Makefile | 2 +
> > drivers/media/platform/stm32/dma2d/dma2d-hw.c | 143 ++++
> > drivers/media/platform/stm32/dma2d/dma2d-regs.h | 114 +++
> > drivers/media/platform/stm32/dma2d/dma2d.c | 791 +++++++++++++++++++++
> > drivers/media/platform/stm32/dma2d/dma2d.h | 132 ++++
> > drivers/media/v4l2-core/v4l2-mem2mem.c | 20 +
> > include/media/v4l2-mem2mem.h | 4 +
> > 14 files changed, 1305 insertions(+), 4 deletions(-)
> > create mode 100644 Documentation/devicetree/bindings/media/st,stm32-dma2d.yaml
> > create mode 100644 drivers/media/platform/stm32/dma2d/dma2d-hw.c
> > create mode 100644 drivers/media/platform/stm32/dma2d/dma2d-regs.h
> > create mode 100644 drivers/media/platform/stm32/dma2d/dma2d.c
> > create mode 100644 drivers/media/platform/stm32/dma2d/dma2d.h
> >
>

2021-06-17 15:29:49

by Hans Verkuil

[permalink] [raw]
Subject: Re: [PATCH 7/7] media: stm32-dma2d: STM32 DMA2D driver

On 17/06/2021 12:56, Dillon Min wrote:
> Hi Hans
>
> On Thu, Jun 17, 2021 at 6:18 PM Hans Verkuil <[email protected]> wrote:
>>
>> Hi Dillon,
>>
>> Please run checkpatch.pl --strict over this patch: I get too many 'CHECK' warnings.
>
> Okay, thanks.
>
>>
>> On 20/05/2021 13:05, [email protected] wrote:
>>> From: Dillon Min <[email protected]>
>>>
>>> This V4L2 subdev m2m driver enables Chrom-Art Accelerator unit
>>> of STMicroelectronics STM32 SoC series.
>>>
>>> Currently support r2m, m2m, m2m_pfc.
>>
>> What does this mean?
>
> This is means graphic data flow support following features:
> - Filling a part or the whole of a destination image with a specific color
> - Copying a part or the whole of a source image into a part or the
> whole of a destination
> image
> - Copying a part or the whole of a source image into a part or the
> whole of a destination
> image with a pixel format conversion
> - Blending a part and/or two complete source images with different
> pixel format and copy
> the result into a part or the whole of a destination image with a
> different color format
> (not support in this version)
>
> I will make the commit message to be more readable in v2.

I was actually looking for the meaning of the abbreviations r2m and m2m_pfc
(I assume 'm2m' just means memory-to-memory?). But this explanation is useful
to have as well. But add it to the source code as well as a top-level comment.

>
>>
>>>
>>> Signed-off-by: Dillon Min <[email protected]>
>>> ---
>>> drivers/media/platform/Kconfig | 9 +
>>> drivers/media/platform/Makefile | 1 +
>>> drivers/media/platform/stm32/Makefile | 2 +
>>> drivers/media/platform/stm32/dma2d/dma2d-hw.c | 143 +++++
>>> drivers/media/platform/stm32/dma2d/dma2d-regs.h | 114 ++++
>>> drivers/media/platform/stm32/dma2d/dma2d.c | 791 ++++++++++++++++++++++++
>>> drivers/media/platform/stm32/dma2d/dma2d.h | 132 ++++
>>> 7 files changed, 1192 insertions(+)
>>> create mode 100644 drivers/media/platform/stm32/dma2d/dma2d-hw.c
>>> create mode 100644 drivers/media/platform/stm32/dma2d/dma2d-regs.h
>>> create mode 100644 drivers/media/platform/stm32/dma2d/dma2d.c
>>> create mode 100644 drivers/media/platform/stm32/dma2d/dma2d.h
>>>
>>> diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
>>> index 157c924686e4..128f4420623f 100644
>>> --- a/drivers/media/platform/Kconfig
>>> +++ b/drivers/media/platform/Kconfig
>>> @@ -244,6 +244,15 @@ config VIDEO_CODA
>>> config VIDEO_IMX_VDOA
>>> def_tristate VIDEO_CODA if SOC_IMX6Q || COMPILE_TEST
>>>
>>> +config VIDEO_STM32_DMA2D
>>> + tristate "STM32 Chrom-Art Accelerator (DMA2D)"
>>> + depends on VIDEO_DEV && VIDEO_V4L2 && ARCH_STM32
>>
>> This needs to depend on COMPILE_TEST as well.
>
> Agree, thanks
>
>>
>>> + select VIDEOBUF2_DMA_CONTIG
>>> + select V4L2_MEM2MEM_DEV
>>> + help
>>> + The STM32 DMA2D is a memory-to-memory engine for pixel conversion,
>>> + specialized DMA dedicated to image manipulation
>>
>> Missing period at the end. And the , should probably be replaced with 'and'.
>
> Agree, thanks.
>
>>
>>> +
>>> config VIDEO_IMX_PXP
>>> tristate "i.MX Pixel Pipeline (PXP)"
>>> depends on VIDEO_DEV && VIDEO_V4L2 && (ARCH_MXC || COMPILE_TEST)
>>> diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
>>> index eedc14aafb32..93b2b319a2b2 100644
>>> --- a/drivers/media/platform/Makefile
>>> +++ b/drivers/media/platform/Makefile
>>> @@ -69,6 +69,7 @@ obj-$(CONFIG_VIDEO_ATMEL_ISC) += atmel/
>>> obj-$(CONFIG_VIDEO_ATMEL_ISI) += atmel/
>>>
>>> obj-$(CONFIG_VIDEO_STM32_DCMI) += stm32/
>>> +obj-$(CONFIG_VIDEO_STM32_DMA2D) += stm32/
>>>
>>> obj-$(CONFIG_VIDEO_MEDIATEK_VPU) += mtk-vpu/
>>>
>>> diff --git a/drivers/media/platform/stm32/Makefile b/drivers/media/platform/stm32/Makefile
>>> index 48b36db2c2e2..896ef98a73ab 100644
>>> --- a/drivers/media/platform/stm32/Makefile
>>> +++ b/drivers/media/platform/stm32/Makefile
>>> @@ -1,2 +1,4 @@
>>> # SPDX-License-Identifier: GPL-2.0-only
>>> obj-$(CONFIG_VIDEO_STM32_DCMI) += stm32-dcmi.o
>>> +stm32-dma2d-objs := dma2d/dma2d.o dma2d/dma2d-hw.o
>>> +obj-$(CONFIG_VIDEO_STM32_DMA2D) += stm32-dma2d.o
>>> diff --git a/drivers/media/platform/stm32/dma2d/dma2d-hw.c b/drivers/media/platform/stm32/dma2d/dma2d-hw.c
>>> new file mode 100644
>>> index 000000000000..6fdbf5a21151
>>> --- /dev/null
>>> +++ b/drivers/media/platform/stm32/dma2d/dma2d-hw.c
>>> @@ -0,0 +1,143 @@
>>> +// SPDX-License-Identifier: GPL-2.0-or-later
>>> +/*
>>> + * ST stm32 Chrom-Art - 2D Graphics Accelerator Driver
>>> + *
>>> + * Copyright (c) 2021 Dillon Min
>>> + * Dillon Min, <[email protected]>
>>> + *
>>> + * based on s5p-g2d
>>> + *
>>> + * Copyright (c) 2011 Samsung Electronics Co., Ltd.
>>> + * Kamil Debski, <[email protected]>
>>> + */
>>> +
>>> +#include <linux/io.h>
>>> +
>>> +#include "dma2d.h"
>>> +#include "dma2d-regs.h"
>>> +
>>> +static inline u32 reg_read(void __iomem *base, u32 reg)
>>> +{
>>> + return readl_relaxed(base + reg);
>>> +}
>>> +
>>> +static inline void reg_write(void __iomem *base, u32 reg, u32 val)
>>> +{
>>> + writel_relaxed(val, base + reg);
>>> +}
>>> +
>>> +static inline void reg_set(void __iomem *base, u32 reg, u32 mask)
>>> +{
>>> + reg_write(base, reg, reg_read(base, reg) | mask);
>>> +}
>>> +
>>> +static inline void reg_clear(void __iomem *base, u32 reg, u32 mask)
>>> +{
>>> + reg_write(base, reg, reg_read(base, reg) & ~mask);
>>> +}
>>> +
>>> +static inline void reg_update_bits(void __iomem *base, u32 reg, u32 mask,
>>> + u32 val)
>>> +{
>>> + reg_write(base, reg, (reg_read(base, reg) & ~mask) | val);
>>> +}
>>> +
>>> +void dma2d_start(struct dma2d_dev *d)
>>> +{
>>> + reg_update_bits(d->regs, DMA2D_CR_REG, CR_START, CR_START);
>>> +}
>>> +
>>> +u32 dma2d_get_int(struct dma2d_dev *d)
>>> +{
>>> + return reg_read(d->regs, DMA2D_ISR_REG);
>>> +}
>>> +
>>> +void dma2d_clear_int(struct dma2d_dev *d)
>>> +{
>>> + u32 isr_val = reg_read(d->regs, DMA2D_ISR_REG);
>>> +
>>> + reg_write(d->regs, DMA2D_IFCR_REG, isr_val & 0x003f);
>>> +}
>>> +
>>> +void dma2d_config_common(struct dma2d_dev *d, enum dma2d_op_mode op_mode,
>>> + u16 width, u16 height)
>>> +{
>>> + reg_update_bits(d->regs, DMA2D_CR_REG, CR_MODE_MASK,
>>> + op_mode << CR_MODE_SHIFT);
>>> +
>>> + reg_write(d->regs, DMA2D_NLR_REG, (width << 16) | height);
>>> +}
>>> +
>>> +void dma2d_config_out(struct dma2d_dev *d, struct dma2d_frame *frm,
>>> + dma_addr_t o_addr)
>>> +{
>>> + reg_update_bits(d->regs, DMA2D_CR_REG, CR_CEIE, CR_CEIE);
>>> + reg_update_bits(d->regs, DMA2D_CR_REG, CR_CTCIE, CR_CTCIE);
>>> + reg_update_bits(d->regs, DMA2D_CR_REG, CR_CAEIE, CR_CAEIE);
>>> + reg_update_bits(d->regs, DMA2D_CR_REG, CR_TCIE, CR_TCIE);
>>> + reg_update_bits(d->regs, DMA2D_CR_REG, CR_TEIE, CR_TEIE);
>>> +
>>> + if (frm->fmt->cmode >= CM_MODE_ARGB8888 &&
>>> + frm->fmt->cmode <= CM_MODE_ARGB4444)
>>> + reg_update_bits(d->regs, DMA2D_OPFCCR_REG, OPFCCR_CM_MASK,
>>> + frm->fmt->cmode);
>>> +
>>> + reg_write(d->regs, DMA2D_OMAR_REG, o_addr);
>>> +
>>> + reg_write(d->regs, DMA2D_OCOLR_REG,
>>> + (frm->a_rgb[3] << 24) |
>>> + (frm->a_rgb[2] << 16) |
>>> + (frm->a_rgb[1] << 8) |
>>> + frm->a_rgb[0]);
>>> +
>>> + reg_update_bits(d->regs, DMA2D_OOR_REG, OOR_LO_MASK,
>>> + frm->line_ofs & 0x3fff);
>>> +}
>>> +
>>> +void dma2d_config_fg(struct dma2d_dev *d, struct dma2d_frame *frm,
>>> + dma_addr_t f_addr)
>>> +{
>>> + reg_write(d->regs, DMA2D_FGMAR_REG, f_addr);
>>> + reg_update_bits(d->regs, DMA2D_FGOR_REG, FGOR_LO_MASK,
>>> + frm->line_ofs);
>>> +
>>> + if (frm->fmt->cmode >= CM_MODE_ARGB8888 &&
>>> + frm->fmt->cmode <= CM_MODE_A4)
>>> + reg_update_bits(d->regs, DMA2D_FGPFCCR_REG, FGPFCCR_CM_MASK,
>>> + frm->fmt->cmode);
>>> +
>>> + reg_update_bits(d->regs, DMA2D_FGPFCCR_REG, FGPFCCR_AM_MASK,
>>> + (frm->a_mode << 16) & 0x03);
>>> +
>>> + reg_update_bits(d->regs, DMA2D_FGPFCCR_REG, FGPFCCR_ALPHA_MASK,
>>> + frm->a_rgb[3] << 24);
>>> +
>>> + reg_write(d->regs, DMA2D_FGCOLR_REG,
>>> + (frm->a_rgb[2] << 16) |
>>> + (frm->a_rgb[1] << 8) |
>>> + frm->a_rgb[0]);
>>> +}
>>> +
>>> +void dma2d_config_bg(struct dma2d_dev *d, struct dma2d_frame *frm,
>>> + dma_addr_t b_addr)
>>> +{
>>> + reg_write(d->regs, DMA2D_BGMAR_REG, b_addr);
>>> + reg_update_bits(d->regs, DMA2D_BGOR_REG, BGOR_LO_MASK,
>>> + frm->line_ofs);
>>> +
>>> + if (frm->fmt->cmode >= CM_MODE_ARGB8888 &&
>>> + frm->fmt->cmode <= CM_MODE_A4)
>>> + reg_update_bits(d->regs, DMA2D_BGPFCCR_REG, BGPFCCR_CM_MASK,
>>> + frm->fmt->cmode);
>>> +
>>> + reg_update_bits(d->regs, DMA2D_BGPFCCR_REG, BGPFCCR_AM_MASK,
>>> + (frm->a_mode << 16) & 0x03);
>>> +
>>> + reg_update_bits(d->regs, DMA2D_BGPFCCR_REG, BGPFCCR_ALPHA_MASK,
>>> + frm->a_rgb[3] << 24);
>>> +
>>> + reg_write(d->regs, DMA2D_BGCOLR_REG,
>>> + (frm->a_rgb[2] << 16) |
>>> + (frm->a_rgb[1] << 8) |
>>> + frm->a_rgb[0]);
>>> +}
>>> diff --git a/drivers/media/platform/stm32/dma2d/dma2d-regs.h b/drivers/media/platform/stm32/dma2d/dma2d-regs.h
>>> new file mode 100644
>>> index 000000000000..6e375bcb1281
>>> --- /dev/null
>>> +++ b/drivers/media/platform/stm32/dma2d/dma2d-regs.h
>>> @@ -0,0 +1,114 @@
>>> +/* SPDX-License-Identifier: GPL-2.0-or-later */
>>> +/*
>>> + * ST stm32 Chrom-Art - 2D Graphics Accelerator Driver
>>> + *
>>> + * Copyright (c) 2021 Dillon Min
>>> + * Dillon Min, <[email protected]>
>>> + *
>>> + * based on s5p-g2d
>>> + *
>>> + * Copyright (c) 2011 Samsung Electronics Co., Ltd.
>>> + * Kamil Debski, <[email protected]>
>>> + */
>>> +
>>> +#ifndef __DMA2D_REGS_H__
>>> +#define __DMA2D_REGS_H__
>>> +
>>> +#define DMA2D_CR_REG 0x0000
>>> +#define CR_MODE_MASK GENMASK(17, 16)
>>> +#define CR_MODE_SHIFT 16
>>> +#define CR_M2M 0x0000
>>> +#define CR_M2M_PFC BIT(16)
>>> +#define CR_M2M_BLEND BIT(17)
>>> +#define CR_R2M (BIT(17) | BIT(16))
>>> +#define CR_CEIE BIT(13)
>>> +#define CR_CTCIE BIT(12)
>>> +#define CR_CAEIE BIT(11)
>>> +#define CR_TWIE BIT(10)
>>> +#define CR_TCIE BIT(9)
>>> +#define CR_TEIE BIT(8)
>>> +#define CR_ABORT BIT(2)
>>> +#define CR_SUSP BIT(1)
>>> +#define CR_START BIT(0)
>>> +
>>> +#define DMA2D_ISR_REG 0x0004
>>> +#define ISR_CEIF BIT(5)
>>> +#define ISR_CTCIF BIT(4)
>>> +#define ISR_CAEIF BIT(3)
>>> +#define ISR_TWIF BIT(2)
>>> +#define ISR_TCIF BIT(1)
>>> +#define ISR_TEIF BIT(0)
>>> +
>>> +#define DMA2D_IFCR_REG 0x0008
>>> +#define IFCR_CCEIF BIT(5)
>>> +#define IFCR_CCTCIF BIT(4)
>>> +#define IFCR_CAECIF BIT(3)
>>> +#define IFCR_CTWIF BIT(2)
>>> +#define IFCR_CTCIF BIT(1)
>>> +#define IFCR_CTEIF BIT(0)
>>> +
>>> +#define DMA2D_FGMAR_REG 0x000c
>>> +#define DMA2D_FGOR_REG 0x0010
>>> +#define FGOR_LO_MASK GENMASK(13, 0)
>>> +
>>> +#define DMA2D_BGMAR_REG 0x0014
>>> +#define DMA2D_BGOR_REG 0x0018
>>> +#define BGOR_LO_MASK GENMASK(13, 0)
>>> +
>>> +#define DMA2D_FGPFCCR_REG 0x001c
>>> +#define FGPFCCR_ALPHA_MASK GENMASK(31, 24)
>>> +#define FGPFCCR_AM_MASK GENMASK(17, 16)
>>> +#define FGPFCCR_CS_MASK GENMASK(15, 8)
>>> +#define FGPFCCR_START BIT(5)
>>> +#define FGPFCCR_CCM_RGB888 BIT(4)
>>> +#define FGPFCCR_CM_MASK GENMASK(3, 0)
>>> +
>>> +#define DMA2D_FGCOLR_REG 0x0020
>>> +#define FGCOLR_REG_MASK GENMASK(23, 16)
>>> +#define FGCOLR_GREEN_MASK GENMASK(15, 8)
>>> +#define FGCOLR_BLUE_MASK GENMASK(7, 0)
>>> +
>>> +#define DMA2D_BGPFCCR_REG 0x0024
>>> +#define BGPFCCR_ALPHA_MASK GENMASK(31, 24)
>>> +#define BGPFCCR_AM_MASK GENMASK(17, 16)
>>> +#define BGPFCCR_CS_MASK GENMASK(15, 8)
>>> +#define BGPFCCR_START BIT(5)
>>> +#define BGPFCCR_CCM_RGB888 BIT(4)
>>> +#define BGPFCCR_CM_MASK GENMASK(3, 0)
>>> +
>>> +#define DMA2D_BGCOLR_REG 0x0028
>>> +#define BGCOLR_REG_MASK GENMASK(23, 16)
>>> +#define BGCOLR_GREEN_MASK GENMASK(15, 8)
>>> +#define BGCOLR_BLUE_MASK GENMASK(7, 0)
>>> +
>>> +#define DMA2D_OPFCCR_REG 0x0034
>>> +#define OPFCCR_CM_MASK GENMASK(2, 0)
>>> +
>>> +#define DMA2D_OCOLR_REG 0x0038
>>> +#define OCOLR_ALPHA_MASK GENMASK(31, 24)
>>> +#define OCOLR_RED_MASK GENMASK(23, 16)
>>> +#define OCOLR_GREEN_MASK GENMASK(15, 8)
>>> +#define OCOLR_BLUE_MASK GENMASK(7, 0)
>>> +
>>> +#define DMA2D_OMAR_REG 0x003c
>>> +
>>> +#define DMA2D_OOR_REG 0x0040
>>> +#define OOR_LO_MASK GENMASK(13, 0)
>>> +
>>> +#define DMA2D_NLR_REG 0x0044
>>> +#define NLR_PL_MASK GENMASK(29, 16)
>>> +#define NLR_NL_MASK GENMASK(15, 0)
>>> +
>>> +
>>> +/* Hardware limits */
>>> +#define MAX_WIDTH 0x3fff
>>> +#define MAX_HEIGHT 0xffff
>>> +
>>> +#define DEFAULT_WIDTH 240
>>> +#define DEFAULT_HEIGHT 320
>>> +#define DEFAULT_SIZE 307200
>>> +
>>> +#define CM_MODE_ARGB8888 0x00
>>> +#define CM_MODE_ARGB4444 0x04
>>> +#define CM_MODE_A4 0x0a
>>> +#endif /* __DMA2D_REGS_H__ */
>>> diff --git a/drivers/media/platform/stm32/dma2d/dma2d.c b/drivers/media/platform/stm32/dma2d/dma2d.c
>>> new file mode 100644
>>> index 000000000000..88f8333a174a
>>> --- /dev/null
>>> +++ b/drivers/media/platform/stm32/dma2d/dma2d.c
>>> @@ -0,0 +1,791 @@
>>> +// SPDX-License-Identifier: GPL-2.0-or-later
>>> +/*
>>> + * STM32 DMA2D - 2D Graphics Accelerator Driver
>>> + *
>>> + * Copyright (c) 2021 Dillon Min
>>> + * Dillon Min, <[email protected]>
>>> + *
>>> + * based on s5p-g2d
>>> + *
>>> + * Copyright (c) 2011 Samsung Electronics Co., Ltd.
>>> + * Kamil Debski, <[email protected]>
>>> + */
>>> +
>>> +#include <linux/module.h>
>>> +#include <linux/fs.h>
>>> +#include <linux/timer.h>
>>> +#include <linux/sched.h>
>>> +#include <linux/slab.h>
>>> +#include <linux/clk.h>
>>> +#include <linux/interrupt.h>
>>> +#include <linux/of.h>
>>> +
>>> +#include <linux/platform_device.h>
>>> +#include <media/v4l2-mem2mem.h>
>>> +#include <media/v4l2-device.h>
>>> +#include <media/v4l2-ioctl.h>
>>> +#include <media/v4l2-event.h>
>>> +#include <media/videobuf2-v4l2.h>
>>> +#include <media/videobuf2-dma-contig.h>
>>> +
>>> +#include "dma2d.h"
>>> +#include "dma2d-regs.h"
>>> +
>>> +#define fh2ctx(__fh) container_of(__fh, struct dma2d_ctx, fh)
>>> +
>>> +static struct dma2d_fmt formats[] = {
>>
>> Can this be const?
>
> Agree
>
>>
>>> + {
>>> + .fourcc = V4L2_PIX_FMT_ARGB32,
>>> + .cmode = DMA2D_CMODE_ARGB8888,
>>> + .depth = 32,
>>> + },
>>> + {
>>> + .fourcc = V4L2_PIX_FMT_RGB24,
>>> + .cmode = DMA2D_CMODE_RGB888,
>>> + .depth = 24,
>>> + },
>>> + {
>>> + .fourcc = V4L2_PIX_FMT_RGB565,
>>> + .cmode = DMA2D_CMODE_RGB565,
>>> + .depth = 16,
>>> + },
>>> + {
>>> + .fourcc = V4L2_PIX_FMT_ARGB555,
>>> + .cmode = DMA2D_CMODE_ARGB1555,
>>> + .depth = 16,
>>> + },
>>> + {
>>> + .fourcc = V4L2_PIX_FMT_ARGB444,
>>> + .cmode = DMA2D_CMODE_ARGB4444,
>>> + .depth = 16,
>>> + },
>>> +};
>>> +#define NUM_FORMATS ARRAY_SIZE(formats)
>>> +
>>> +static struct dma2d_frame def_frame = {
>>
>> Ditto.
>
> Agree
>
>>
>>> + .width = DEFAULT_WIDTH,
>>> + .height = DEFAULT_HEIGHT,
>>> + .line_ofs = 0,
>>
>> Does 'ofs' stand for 'offset'? If so, then I prefer the full 'offset' over 'ofs'.
>> 'ofs' is a bit obscure.
>
> Agree
>
>>
>>> + .a_rgb = {0x00, 0x00, 0x00, 0xff},
>>> + .a_mode = DMA2D_ALPHA_MODE_NO_MODIF,
>>> + .fmt = &formats[0],
>>> + .size = DEFAULT_SIZE,
>>> +};
>>> +
>>> +static struct dma2d_fmt *find_fmt(int pixelformat)
>>> +{
>>> + unsigned int i;
>>> +
>>> + for (i = 0; i < NUM_FORMATS; i++) {
>>> + if (formats[i].fourcc == pixelformat)
>>> + return &formats[i];
>>> + }
>>> +
>>> + return NULL;
>>> +}
>>> +
>>> +static struct dma2d_frame *get_frame(struct dma2d_ctx *ctx,
>>> + enum v4l2_buf_type type)
>>> +{
>>> + switch (type) {
>>> + case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>>> + return &ctx->fg;
>>> + case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>>> + return &ctx->out;
>>
>> Hmm, capture returns ctx->out, and output returns ctx->fg. Not exactly what
>> you would expect.
>
> fg means foreground layer of dma2d controller, the name is indeed confusing.
> I'll change it to cap/out, is it reasonable?

Yes, that's how it is typically named.

>
>>
>>> + default:
>>> + return ERR_PTR(-EINVAL);
>>
>> Using error pointers here is a bit overkill IMHO. I think returning NULL is
>> typical. With a WARN_ON perhaps since this really shouldn't happen.
>>
>> Alternatively just do this:
>>
>> return V4L2_TYPE_IS_OUTPUT(type) ? &ctx->fg : &ctx->out;
>>
>> It avoids the whole problem.
>
> Agree, will add this suggestion to [v2], thanks.
>
>>
>>> + }
>>> +}
>>> +
>>> +static int dma2d_queue_setup(struct vb2_queue *vq,
>>> + unsigned int *nbuffers, unsigned int *nplanes,
>>> + unsigned int sizes[], struct device *alloc_devs[])
>>> +{
>>> + struct dma2d_ctx *ctx = vb2_get_drv_priv(vq);
>>> + struct dma2d_frame *f = get_frame(ctx, vq->type);
>>> +
>>> + if (IS_ERR(f))
>>> + return PTR_ERR(f);
>>> +
>>> + if (*nplanes)
>>> + return sizes[0] < f->size ? -EINVAL : 0;
>>> +
>>> + sizes[0] = f->size;
>>> + *nplanes = 1;
>>> +
>>> + if (*nbuffers == 0)
>>> + *nbuffers = 1;
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int dma2d_buf_out_validate(struct vb2_buffer *vb)
>>> +{
>>> + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
>>> +
>>> + if (vbuf->field == V4L2_FIELD_ANY)
>>> + vbuf->field = V4L2_FIELD_NONE;
>>> + if (vbuf->field != V4L2_FIELD_NONE)
>>> + return -EINVAL;
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int dma2d_buf_prepare(struct vb2_buffer *vb)
>>> +{
>>> + struct dma2d_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
>>> + struct dma2d_frame *f = get_frame(ctx, vb->vb2_queue->type);
>>> +
>>> + if (IS_ERR(f))
>>> + return PTR_ERR(f);
>>> +
>>> + if (vb2_plane_size(vb, 0) < f->size)
>>> + return -EINVAL;
>>> +
>>> + vb2_set_plane_payload(vb, 0, f->size);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static void dma2d_buf_queue(struct vb2_buffer *vb)
>>> +{
>>> + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
>>> + struct dma2d_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
>>> +
>>> + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
>>> +}
>>> +
>>> +static int dma2d_start_streaming(struct vb2_queue *q, unsigned int count)
>>> +{
>>> + struct dma2d_ctx *ctx = vb2_get_drv_priv(q);
>>> + struct dma2d_frame *f = get_frame(ctx, q->type);
>>> +
>>> + if (IS_ERR(f))
>>> + return -EINVAL;
>>> +
>>> + f->sequence = 0;
>>> + return 0;
>>> +}
>>> +
>>> +static void dma2d_stop_streaming(struct vb2_queue *q)
>>> +{
>>> + struct dma2d_ctx *ctx = vb2_get_drv_priv(q);
>>> + struct vb2_v4l2_buffer *vbuf;
>>> +
>>> + for (;;) {
>>> + if (V4L2_TYPE_IS_OUTPUT(q->type))
>>> + vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
>>> + else
>>> + vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
>>> + if (!vbuf)
>>> + return;
>>> + v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
>>> + }
>>> +}
>>> +
>>> +static const struct vb2_ops dma2d_qops = {
>>> + .queue_setup = dma2d_queue_setup,
>>> + .buf_out_validate = dma2d_buf_out_validate,
>>> + .buf_prepare = dma2d_buf_prepare,
>>> + .buf_queue = dma2d_buf_queue,
>>> + .start_streaming = dma2d_start_streaming,
>>> + .stop_streaming = dma2d_stop_streaming,
>>> + .wait_prepare = vb2_ops_wait_prepare,
>>> + .wait_finish = vb2_ops_wait_finish,
>>> +};
>>> +
>>> +static int queue_init(void *priv, struct vb2_queue *src_vq,
>>> + struct vb2_queue *dst_vq)
>>> +{
>>> + struct dma2d_ctx *ctx = priv;
>>> + int ret;
>>> +
>>> + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
>>> + src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
>>> + src_vq->drv_priv = ctx;
>>> + src_vq->ops = &dma2d_qops;
>>> + src_vq->mem_ops = &vb2_dma_contig_memops;
>>> + src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
>>> + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
>>> + src_vq->lock = &ctx->dev->mutex;
>>> + src_vq->dev = ctx->dev->v4l2_dev.dev;
>>> +
>>> + ret = vb2_queue_init(src_vq);
>>> + if (ret)
>>> + return ret;
>>> +
>>> + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
>>> + dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
>>> + dst_vq->drv_priv = ctx;
>>> + dst_vq->ops = &dma2d_qops;
>>> + dst_vq->mem_ops = &vb2_dma_contig_memops;
>>> + dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
>>> + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
>>> + dst_vq->lock = &ctx->dev->mutex;
>>> + dst_vq->dev = ctx->dev->v4l2_dev.dev;
>>> +
>>> + return vb2_queue_init(dst_vq);
>>> +}
>>> +
>>> +#define V4L2_CID_DMA2D_R2M_COLOR (V4L2_CID_USER_BASE | 0x1200)
>>> +#define V4L2_CID_DMA2D_R2M_MODE (V4L2_CID_USER_BASE | 0x1201)
>>
>> You need to reserve a range for driver-specific controls in include/uapi/linux/v4l2-controls.h.
>> Search for V4L2_CID_USER_CCS_BASE to see how it is done.
>
> Okay, I will optimize this part in [v2]. thanks.
>
>>
>>> +static int dma2d_s_ctrl(struct v4l2_ctrl *ctrl)
>>> +{
>>> + struct dma2d_frame *frm;
>>> + struct dma2d_ctx *ctx = container_of(ctrl->handler, struct dma2d_ctx,
>>> + ctrl_handler);
>>> + unsigned long flags;
>>> +
>>> + spin_lock_irqsave(&ctx->dev->ctrl_lock, flags);
>>> + switch (ctrl->id) {
>>> + case V4L2_CID_ALPHA_COMPONENT:
>>> + /* set the background alpha value*/
>>> + ctx->alpha_component = (u8) ctrl->val;
>>> + break;
>>> + case V4L2_CID_DMA2D_R2M_COLOR:
>>> + frm = get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
>>> + frm->a_rgb[3] = (ctrl->val >> 24) & 0xff;
>>> + frm->a_rgb[2] = (ctrl->val >> 16) & 0xff;
>>> + frm->a_rgb[1] = (ctrl->val >> 8) & 0xff;
>>> + frm->a_rgb[0] = ctrl->val & 0xff;
>>> + break;
>>> + case V4L2_CID_DMA2D_R2M_MODE:
>>> + if (ctrl->val)
>>> + ctx->op_mode = DMA2D_MODE_R2M;
>>> + break;
>>> + default:
>>> + v4l2_err(&ctx->dev->v4l2_dev, "Invalid control\n");
>>> + spin_unlock_irqrestore(&ctx->dev->ctrl_lock, flags);
>>> + return -EINVAL;
>>> +
>>> + }
>>> + spin_unlock_irqrestore(&ctx->dev->ctrl_lock, flags);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static const struct v4l2_ctrl_ops dma2d_ctrl_ops = {
>>> + .s_ctrl = dma2d_s_ctrl,
>>> +};
>>> +
>>> +static const struct v4l2_ctrl_config dma2d_r2m_control[] = {
>>> + {
>>> + .ops = &dma2d_ctrl_ops,
>>> + .id = V4L2_CID_DMA2D_R2M_COLOR,
>>> + .name = "R2M Alpha/Color Value",
>>> + .type = V4L2_CTRL_TYPE_INTEGER,
>>> + .min = 0xffffffff80000000ULL,
>>> + .max = 0x7fffffff,
>>> + .def = 0,
>>> + .step = 1,
>>> + },
>>> + {
>>> + .ops = &dma2d_ctrl_ops,
>>> + .id = V4L2_CID_DMA2D_R2M_MODE,
>>> + .name = "Set to r2m mode",
>>
>> "Set R2M Mode"
>
> Agree
>
>>
>> These controls need to be documented.
>
> Okay, I will add a doc patch in v2.

Pay special attention to how V4L2_CID_DMA2D_R2M_COLOR is interpreted.
What color component goes where, is it RGB or YUV, etc. It can be
surprisingly difficult to clearly and unambiguously define this.

Regards,

Hans

>
>>
>>> + .type = V4L2_CTRL_TYPE_BOOLEAN,
>>> + .min = 0,
>>> + .max = 1,
>>> + .def = 0,
>>> + .step = 1,
>>> + }
>>> +};
>>> +
>>> +static int dma2d_setup_ctrls(struct dma2d_ctx *ctx)
>>> +{
>>> + v4l2_ctrl_handler_init(&ctx->ctrl_handler, 3);
>>> +
>>> + v4l2_ctrl_new_std(&ctx->ctrl_handler, &dma2d_ctrl_ops,
>>> + V4L2_CID_ALPHA_COMPONENT, 0, 255, 1, 255);
>>> + v4l2_ctrl_new_custom(&ctx->ctrl_handler, &dma2d_r2m_control[0], NULL);
>>> + v4l2_ctrl_new_custom(&ctx->ctrl_handler, &dma2d_r2m_control[1], NULL);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int dma2d_open(struct file *file)
>>> +{
>>> + struct dma2d_dev *dev = video_drvdata(file);
>>> + struct dma2d_ctx *ctx = NULL;
>>> + int ret = 0;
>>> +
>>> + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
>>> + if (!ctx)
>>> + return -ENOMEM;
>>> + ctx->dev = dev;
>>> + /* Set default formats */
>>> + ctx->fg = def_frame;
>>> + ctx->bg = def_frame;
>>> + ctx->out = def_frame;
>>> + ctx->op_mode = DMA2D_MODE_M2M_FPC;
>>> + ctx->colorspace = V4L2_COLORSPACE_REC709;
>>> + ctx->alpha_component = 0x00;
>>> + if (mutex_lock_interruptible(&dev->mutex)) {
>>> + kfree(ctx);
>>> + return -ERESTARTSYS;
>>> + }
>>> +
>>> + ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init);
>>> + if (IS_ERR(ctx->fh.m2m_ctx)) {
>>> + ret = PTR_ERR(ctx->fh.m2m_ctx);
>>> + mutex_unlock(&dev->mutex);
>>> + kfree(ctx);
>>> + return ret;
>>> + }
>>> +
>>> + v4l2_fh_init(&ctx->fh, video_devdata(file));
>>> + file->private_data = &ctx->fh;
>>> + v4l2_fh_add(&ctx->fh);
>>> +
>>> + dma2d_setup_ctrls(ctx);
>>> +
>>> + /* Write the default values to the ctx struct */
>>> + v4l2_ctrl_handler_setup(&ctx->ctrl_handler);
>>> +
>>> + ctx->fh.ctrl_handler = &ctx->ctrl_handler;
>>> + mutex_unlock(&dev->mutex);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int dma2d_release(struct file *file)
>>> +{
>>> + struct dma2d_dev *dev = video_drvdata(file);
>>> + struct dma2d_ctx *ctx = fh2ctx(file->private_data);
>>> +
>>> + v4l2_ctrl_handler_free(&ctx->ctrl_handler);
>>> + v4l2_fh_del(&ctx->fh);
>>> + v4l2_fh_exit(&ctx->fh);
>>> + mutex_lock(&dev->mutex);
>>> + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
>>> + mutex_unlock(&dev->mutex);
>>> + kfree(ctx);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int vidioc_querycap(struct file *file, void *priv,
>>> + struct v4l2_capability *cap)
>>> +{
>>> + strscpy(cap->driver, DMA2D_NAME, sizeof(cap->driver));
>>> + strscpy(cap->card, DMA2D_NAME, sizeof(cap->card));
>>> + strscpy(cap->bus_info, BUS_INFO, sizeof(cap->bus_info));
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int vidioc_enum_fmt(struct file *file, void *prv, struct v4l2_fmtdesc *f)
>>> +{
>>> + if (f->index >= NUM_FORMATS)
>>> + return -EINVAL;
>>> +
>>> + f->pixelformat = formats[f->index].fourcc;
>>> + return 0;
>>> +}
>>> +
>>> +static int vidioc_g_fmt(struct file *file, void *prv, struct v4l2_format *f)
>>> +{
>>> + struct dma2d_ctx *ctx = prv;
>>> + struct vb2_queue *vq;
>>> + struct dma2d_frame *frm;
>>> +
>>> + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
>>> + if (!vq)
>>> + return -EINVAL;
>>> +
>>> + frm = get_frame(ctx, f->type);
>>> + if (IS_ERR(frm))
>>> + return PTR_ERR(frm);
>>> +
>>> + f->fmt.pix.width = frm->width;
>>> + f->fmt.pix.height = frm->height;
>>> + f->fmt.pix.field = V4L2_FIELD_NONE;
>>> + f->fmt.pix.pixelformat = frm->fmt->fourcc;
>>> + f->fmt.pix.bytesperline = (frm->width * frm->fmt->depth) >> 3;
>>> + f->fmt.pix.sizeimage = frm->size;
>>> + f->fmt.pix.colorspace = ctx->colorspace;
>>> + f->fmt.pix.xfer_func = ctx->xfer_func;
>>> + f->fmt.pix.ycbcr_enc = ctx->ycbcr_enc;
>>> + f->fmt.pix.quantization = ctx->quant;
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int vidioc_try_fmt(struct file *file, void *prv, struct v4l2_format *f)
>>> +{
>>> + struct dma2d_ctx *ctx = prv;
>>> + struct dma2d_dev *dev = ctx->dev;
>>> + struct dma2d_fmt *fmt;
>>> + enum v4l2_field *field;
>>> + u32 fourcc = f->fmt.pix.pixelformat;
>>> +
>>> + fmt = find_fmt(fourcc);
>>> + if (!fmt) {
>>> + v4l2_warn(&dev->v4l2_dev,
>>> + "Format not supported: %c%c%c%c, use the default.\n",
>>> + (fourcc & 0xff), (fourcc >> 8) & 0xff,
>>> + (fourcc >> 16) & 0xff, (fourcc >> 24) & 0xff);
>>> + f->fmt.pix.pixelformat = formats[0].fourcc;
>>> + fmt = find_fmt(f->fmt.pix.pixelformat);
>>> + }
>>> +
>>> + field = &f->fmt.pix.field;
>>> + if (*field == V4L2_FIELD_ANY)
>>> + *field = V4L2_FIELD_NONE;
>>> + else if (*field != V4L2_FIELD_NONE)
>>> + return -EINVAL;
>>> +
>>> + if (f->fmt.pix.width > MAX_WIDTH)
>>> + f->fmt.pix.width = MAX_WIDTH;
>>> + if (f->fmt.pix.height > MAX_HEIGHT)
>>> + f->fmt.pix.height = MAX_HEIGHT;
>>> +
>>> + if (f->fmt.pix.width < 1)
>>> + f->fmt.pix.width = 1;
>>> + if (f->fmt.pix.height < 1)
>>> + f->fmt.pix.height = 1;
>>> +
>>> + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT && !f->fmt.pix.colorspace)
>>> + f->fmt.pix.colorspace = V4L2_COLORSPACE_REC709;
>>> + else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
>>> + f->fmt.pix.colorspace = ctx->colorspace;
>>> + f->fmt.pix.xfer_func = ctx->xfer_func;
>>> + f->fmt.pix.ycbcr_enc = ctx->ycbcr_enc;
>>> + f->fmt.pix.quantization = ctx->quant;
>>> + }
>>> + f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3;
>>> + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int vidioc_s_fmt(struct file *file, void *prv, struct v4l2_format *f)
>>> +{
>>> + struct dma2d_ctx *ctx = prv;
>>> + struct dma2d_dev *dev = ctx->dev;
>>> + struct vb2_queue *vq;
>>> + struct dma2d_frame *frm;
>>> + struct dma2d_fmt *fmt;
>>> + int ret = 0;
>>> +
>>> + /* Adjust all values accordingly to the hardware capabilities
>>> + * and chosen format.
>>> + */
>>> + ret = vidioc_try_fmt(file, prv, f);
>>> + if (ret)
>>> + return ret;
>>> +
>>> + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
>>> + if (vb2_is_busy(vq)) {
>>> + v4l2_err(&dev->v4l2_dev, "queue (%d) bust\n", f->type);
>>> + return -EBUSY;
>>> + }
>>> +
>>> + frm = get_frame(ctx, f->type);
>>> + if (IS_ERR(frm))
>>> + return PTR_ERR(frm);
>>> +
>>> + fmt = find_fmt(f->fmt.pix.pixelformat);
>>> + if (!fmt)
>>> + return -EINVAL;
>>> +
>>> + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
>>> + ctx->colorspace = f->fmt.pix.colorspace;
>>> + ctx->xfer_func = f->fmt.pix.xfer_func;
>>> + ctx->ycbcr_enc = f->fmt.pix.ycbcr_enc;
>>> + ctx->quant = f->fmt.pix.quantization;
>>> + }
>>> +
>>> + frm->width = f->fmt.pix.width;
>>> + frm->height = f->fmt.pix.height;
>>> + frm->size = f->fmt.pix.sizeimage;
>>> + /* Reset crop settings */
>>> + frm->o_width = 0;
>>> + frm->o_height = 0;
>>> + frm->c_width = frm->width;
>>> + frm->c_height = frm->height;
>>> + frm->right = frm->width;
>>> + frm->bottom = frm->height;
>>> + frm->fmt = fmt;
>>> + frm->line_ofs = 0;
>>> + if (f->fmt.win.global_alpha != 0) {
>>> + frm->a_rgb[3] = f->fmt.win.global_alpha;
>>> + frm->a_mode = DMA2D_ALPHA_MODE_REPLACE;
>>> + }
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static void device_run(void *prv)
>>> +{
>>> + struct dma2d_ctx *ctx = prv;
>>> + struct dma2d_dev *dev = ctx->dev;
>>> + struct dma2d_frame *frm_out, *frm_cap;
>>> + struct vb2_v4l2_buffer *src, *dst;
>>> + unsigned long flags;
>>> +
>>> + spin_lock_irqsave(&dev->ctrl_lock, flags);
>>> + dev->curr = ctx;
>>> +
>>> + src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
>>> + dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
>>> + if (dst == NULL || src == NULL)
>>> + goto end;
>>> +
>>> + frm_cap = get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
>>> + frm_out = get_frame(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
>>> + if (!frm_cap || !frm_out)
>>> + goto end;
>>> +
>>> + src->sequence = frm_out->sequence++;
>>> + dst->sequence = frm_cap->sequence++;
>>> + v4l2_m2m_buf_copy_metadata(src, dst, true);
>>> +
>>> + clk_enable(dev->gate);
>>> +
>>> + dma2d_config_fg(dev, frm_out,
>>> + vb2_dma_contig_plane_dma_addr(&src->vb2_buf, 0));
>>> +
>>> + /* TODO: add M2M_BLEND handler here */
>>> +
>>> + if (ctx->op_mode != DMA2D_MODE_R2M) {
>>> + if (frm_out->fmt->fourcc == frm_cap->fmt->fourcc)
>>> + ctx->op_mode = DMA2D_MODE_M2M;
>>> + else
>>> + ctx->op_mode = DMA2D_MODE_M2M_FPC;
>>> + }
>>> +
>>> + dma2d_config_out(dev, frm_cap,
>>> + vb2_dma_contig_plane_dma_addr(&dst->vb2_buf, 0));
>>> + dma2d_config_common(dev, ctx->op_mode, frm_cap->width, frm_cap->height);
>>> +
>>> + dma2d_start(dev);
>>> +end:
>>> + spin_unlock_irqrestore(&dev->ctrl_lock, flags);
>>> +}
>>> +
>>> +static irqreturn_t dma2d_isr(int irq, void *prv)
>>> +{
>>> + struct dma2d_dev *dev = prv;
>>> + struct dma2d_ctx *ctx = dev->curr;
>>> + struct vb2_v4l2_buffer *src, *dst;
>>> + uint32_t s = dma2d_get_int(dev);
>>> +
>>> + if (s & ISR_TCIF || s == 0) {
>>> + dma2d_clear_int(dev);
>>> + clk_disable(dev->gate);
>>> +
>>> + WARN_ON(ctx == NULL);
>>> +
>>> + src = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
>>> + dst = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
>>> +
>>> + WARN_ON(dst == NULL);
>>> + WARN_ON(src == NULL);
>>> +
>>> + v4l2_m2m_buf_done(src, VB2_BUF_STATE_DONE);
>>> + v4l2_m2m_buf_done(dst, VB2_BUF_STATE_DONE);
>>> + v4l2_m2m_job_finish(dev->m2m_dev, ctx->fh.m2m_ctx);
>>> +
>>> + dev->curr = NULL;
>>> + } else {
>>> + dma2d_clear_int(dev);
>>> + }
>>> +
>>> + return IRQ_HANDLED;
>>> +}
>>> +
>>> +static const struct v4l2_file_operations dma2d_fops = {
>>> + .owner = THIS_MODULE,
>>> + .open = dma2d_open,
>>> + .release = dma2d_release,
>>> + .poll = v4l2_m2m_fop_poll,
>>> + .unlocked_ioctl = video_ioctl2,
>>> + .mmap = v4l2_m2m_fop_mmap,
>>> +#ifndef CONFIG_MMU
>>> + .get_unmapped_area = v4l2_m2m_get_unmapped_area,
>>> +#endif
>>> +};
>>> +
>>> +static const struct v4l2_ioctl_ops dma2d_ioctl_ops = {
>>> + .vidioc_querycap = vidioc_querycap,
>>> +
>>> + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt,
>>> + .vidioc_g_fmt_vid_cap = vidioc_g_fmt,
>>> + .vidioc_try_fmt_vid_cap = vidioc_try_fmt,
>>> + .vidioc_s_fmt_vid_cap = vidioc_s_fmt,
>>> +
>>> + .vidioc_enum_fmt_vid_out = vidioc_enum_fmt,
>>> + .vidioc_g_fmt_vid_out = vidioc_g_fmt,
>>> + .vidioc_try_fmt_vid_out = vidioc_try_fmt,
>>> + .vidioc_s_fmt_vid_out = vidioc_s_fmt,
>>> +
>>> + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
>>> + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
>>> + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
>>> + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
>>> + .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,
>>> + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
>>> + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
>>> +
>>> + .vidioc_streamon = v4l2_m2m_ioctl_streamon,
>>> + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
>>> +
>>> + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
>>> + .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
>>> +};
>>> +
>>> +static const struct video_device dma2d_videodev = {
>>> + .name = DMA2D_NAME,
>>> + .fops = &dma2d_fops,
>>> + .ioctl_ops = &dma2d_ioctl_ops,
>>> + .minor = -1,
>>> + .release = video_device_release,
>>> + .vfl_dir = VFL_DIR_M2M,
>>> +};
>>> +
>>> +static const struct v4l2_m2m_ops dma2d_m2m_ops = {
>>> + .device_run = device_run,
>>> +};
>>> +
>>> +static const struct of_device_id stm32_dma2d_match[];
>>> +
>>> +static int dma2d_probe(struct platform_device *pdev)
>>> +{
>>> + struct dma2d_dev *dev;
>>> + struct video_device *vfd;
>>> + struct resource *res;
>>> + int ret = 0;
>>> +
>>> + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
>>> + if (!dev)
>>> + return -ENOMEM;
>>> +
>>> + spin_lock_init(&dev->ctrl_lock);
>>> + mutex_init(&dev->mutex);
>>> + atomic_set(&dev->num_inst, 0);
>>> +
>>> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>> +
>>> + dev->regs = devm_ioremap_resource(&pdev->dev, res);
>>> + if (IS_ERR(dev->regs))
>>> + return PTR_ERR(dev->regs);
>>> +
>>> + dev->gate = clk_get(&pdev->dev, "dma2d");
>>> + if (IS_ERR(dev->gate)) {
>>> + dev_err(&pdev->dev, "failed to get dma2d clock gate\n");
>>> + ret = -ENXIO;
>>> + return ret;
>>> + }
>>> +
>>> + ret = clk_prepare(dev->gate);
>>> + if (ret) {
>>> + dev_err(&pdev->dev, "failed to prepare dma2d clock gate\n");
>>> + goto put_clk_gate;
>>> + }
>>> +
>>> + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
>>> + if (!res) {
>>> + dev_err(&pdev->dev, "failed to find IRQ\n");
>>> + ret = -ENXIO;
>>> + goto unprep_clk_gate;
>>> + }
>>> +
>>> + dev->irq = res->start;
>>> +
>>> + ret = devm_request_irq(&pdev->dev, dev->irq, dma2d_isr,
>>> + 0, pdev->name, dev);
>>> + if (ret) {
>>> + dev_err(&pdev->dev, "failed to install IRQ\n");
>>> + goto unprep_clk_gate;
>>> + }
>>> +
>>> + ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
>>> + if (ret)
>>> + goto unprep_clk_gate;
>>> +
>>> + vfd = video_device_alloc();
>>> + if (!vfd) {
>>> + v4l2_err(&dev->v4l2_dev, "Failed to allocate video device\n");
>>> + ret = -ENOMEM;
>>> + goto unreg_v4l2_dev;
>>> + }
>>> +
>>> + *vfd = dma2d_videodev;
>>> + set_bit(V4L2_FL_QUIRK_INVERTED_CROP, &vfd->flags);
>>> + vfd->lock = &dev->mutex;
>>> + vfd->v4l2_dev = &dev->v4l2_dev;
>>> + vfd->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING;
>>> + ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0);
>>> + if (ret) {
>>> + v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
>>> + goto rel_vdev;
>>> + }
>>> +
>>> + video_set_drvdata(vfd, dev);
>>> + dev->vfd = vfd;
>>> + v4l2_info(&dev->v4l2_dev, "device registered as /dev/video%d\n",
>>> + vfd->num);
>>> + platform_set_drvdata(pdev, dev);
>>> + dev->m2m_dev = v4l2_m2m_init(&dma2d_m2m_ops);
>>> + if (IS_ERR(dev->m2m_dev)) {
>>> + v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n");
>>> + ret = PTR_ERR(dev->m2m_dev);
>>> + goto unreg_video_dev;
>>> + }
>>> +
>>> + v4l2_info(&dev->v4l2_dev, "stm32 dma2d initialized\n");
>>> + return 0;
>>> +
>>> +unreg_video_dev:
>>> + video_unregister_device(dev->vfd);
>>> +rel_vdev:
>>> + video_device_release(vfd);
>>> +unreg_v4l2_dev:
>>> + v4l2_device_unregister(&dev->v4l2_dev);
>>> +unprep_clk_gate:
>>> + clk_unprepare(dev->gate);
>>> +put_clk_gate:
>>> + clk_put(dev->gate);
>>> +
>>> + return ret;
>>> +}
>>> +
>>> +static int dma2d_remove(struct platform_device *pdev)
>>> +{
>>> + struct dma2d_dev *dev = platform_get_drvdata(pdev);
>>> +
>>> + v4l2_info(&dev->v4l2_dev, "Removing " DMA2D_NAME);
>>> + v4l2_m2m_release(dev->m2m_dev);
>>> + video_unregister_device(dev->vfd);
>>> + v4l2_device_unregister(&dev->v4l2_dev);
>>> + vb2_dma_contig_clear_max_seg_size(&pdev->dev);
>>> + clk_unprepare(dev->gate);
>>> + clk_put(dev->gate);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static const struct of_device_id stm32_dma2d_match[] = {
>>> + {
>>> + .compatible = "st,stm32-dma2d",
>>> + .data = NULL,
>>> + },
>>> + {},
>>> +};
>>> +MODULE_DEVICE_TABLE(of, stm32_dma2d_match);
>>> +
>>> +static struct platform_driver dma2d_pdrv = {
>>> + .probe = dma2d_probe,
>>> + .remove = dma2d_remove,
>>> + .driver = {
>>> + .name = DMA2D_NAME,
>>> + .of_match_table = stm32_dma2d_match,
>>> + },
>>> +};
>>> +
>>> +module_platform_driver(dma2d_pdrv);
>>> +
>>> +MODULE_AUTHOR("Dillon Min <[email protected]>");
>>> +MODULE_DESCRIPTION("STM32 Chrom-Art Accelerator DMA2D driver");
>>> +MODULE_LICENSE("GPL");
>>> diff --git a/drivers/media/platform/stm32/dma2d/dma2d.h b/drivers/media/platform/stm32/dma2d/dma2d.h
>>> new file mode 100644
>>> index 000000000000..1c64cd2f7587
>>> --- /dev/null
>>> +++ b/drivers/media/platform/stm32/dma2d/dma2d.h
>>> @@ -0,0 +1,132 @@
>>> +/* SPDX-License-Identifier: GPL-2.0-or-later */
>>> +/*
>>> + * ST stm32 DMA2D - 2D Graphics Accelerator Driver
>>> + *
>>> + * Copyright (c) 2021 Dillon Min
>>> + * Dillon Min, <[email protected]>
>>> + *
>>> + * based on s5p-g2d
>>> + *
>>> + * Copyright (c) 2011 Samsung Electronics Co., Ltd.
>>> + * Kamil Debski, <[email protected]>
>>> + */
>>> +
>>> +#ifndef __DMA2D_H__
>>> +#define __DMA2D_H__
>>> +
>>> +#include <linux/platform_device.h>
>>> +#include <media/v4l2-device.h>
>>> +#include <media/v4l2-ctrls.h>
>>> +
>>> +#define DMA2D_NAME "stm-dma2d"
>>> +#define BUS_INFO "platform:stm-dma2d"
>>> +enum dma2d_op_mode {
>>> + DMA2D_MODE_M2M,
>>> + DMA2D_MODE_M2M_FPC,
>>> + DMA2D_MODE_M2M_BLEND,
>>> + DMA2D_MODE_R2M
>>> +};
>>> +
>>> +enum dma2d_cmode {
>>> + /* output pfc cmode from ARGB888 to ARGB4444 */
>>> + DMA2D_CMODE_ARGB8888,
>>> + DMA2D_CMODE_RGB888,
>>> + DMA2D_CMODE_RGB565,
>>> + DMA2D_CMODE_ARGB1555,
>>> + DMA2D_CMODE_ARGB4444,
>>> + /* bg or fg pfc cmode from L8 to A4 */
>>> + DMA2D_CMODE_L8,
>>> + DMA2D_CMODE_AL44,
>>> + DMA2D_CMODE_AL88,
>>> + DMA2D_CMODE_L4,
>>> + DMA2D_CMODE_A8,
>>> + DMA2D_CMODE_A4
>>> +};
>>> +
>>> +enum dma2d_alpha_mode {
>>> + DMA2D_ALPHA_MODE_NO_MODIF,
>>> + DMA2D_ALPHA_MODE_REPLACE,
>>> + DMA2D_ALPHA_MODE_COMBINE
>>> +};
>>> +
>>> +struct dma2d_fmt {
>>> + u32 fourcc;
>>> + int depth;
>>> + enum dma2d_cmode cmode;
>>> +};
>>> +
>>> +struct dma2d_frame {
>>> + /* Original dimensions */
>>> + u32 width;
>>> + u32 height;
>>> + /* Crop size */
>>> + u32 c_width;
>>> + u32 c_height;
>>> + /* Offset */
>>> + u32 o_width;
>>> + u32 o_height;
>>> + u32 bottom;
>>> + u32 right;
>>> + u16 line_ofs;
>>> + /* Image format */
>>> + struct dma2d_fmt *fmt;
>>> + /* [0]: blue
>>> + * [1]: green
>>> + * [2]: red
>>> + * [3]: alpha
>>> + */
>>> + u8 a_rgb[4];
>>> + /*
>>> + * AM[1:0] of DMA2D_FGPFCCR
>>> + */
>>> + enum dma2d_alpha_mode a_mode;
>>> + u32 size;
>>> + unsigned int sequence;
>>> +};
>>> +
>>> +struct dma2d_ctx {
>>> + struct v4l2_fh fh;
>>> + struct dma2d_dev *dev;
>>> + struct dma2d_frame fg;
>>> + struct dma2d_frame out;
>>> + struct dma2d_frame bg;
>>> + /* fb_buf always point to bg address */
>>> + struct v4l2_framebuffer fb_buf;
>>> + /*
>>> + * MODE[17:16] of DMA2D_CR
>>> + */
>>> + enum dma2d_op_mode op_mode;
>>> + u8 alpha_component;
>>> + struct v4l2_ctrl_handler ctrl_handler;
>>> + enum v4l2_colorspace colorspace;
>>> + enum v4l2_ycbcr_encoding ycbcr_enc;
>>> + enum v4l2_xfer_func xfer_func;
>>> + enum v4l2_quantization quant;
>>> +};
>>> +
>>> +struct dma2d_dev {
>>> + struct v4l2_device v4l2_dev;
>>> + struct v4l2_m2m_dev *m2m_dev;
>>> + struct video_device *vfd;
>>> + struct mutex mutex;
>>> + spinlock_t ctrl_lock;
>>> + atomic_t num_inst;
>>> + void __iomem *regs;
>>> + struct clk *gate;
>>> + struct dma2d_ctx *curr;
>>> + int irq;
>>> +};
>>> +
>>> +void dma2d_start(struct dma2d_dev *d);
>>> +u32 dma2d_get_int(struct dma2d_dev *d);
>>> +void dma2d_clear_int(struct dma2d_dev *d);
>>> +void dma2d_config_out(struct dma2d_dev *d, struct dma2d_frame *frm,
>>> + dma_addr_t o_addr);
>>> +void dma2d_config_fg(struct dma2d_dev *d, struct dma2d_frame *frm,
>>> + dma_addr_t f_addr);
>>> +void dma2d_config_bg(struct dma2d_dev *d, struct dma2d_frame *frm,
>>> + dma_addr_t b_addr);
>>> +void dma2d_config_common(struct dma2d_dev *d, enum dma2d_op_mode op_mode,
>>> + u16 width, u16 height);
>>> +
>>> +#endif /* __DMA2D_H__ */
>>>
>>
>> Regards,
>>
>> Hans