요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
.. SPDX-License-Identifier: GPL-2.0
PXA-Camera Host Driver
======================
Author: Robert Jarzmik <robert.jarzmik@free.fr>
Constraints
-----------
a) Image size for YUV422P format
All YUV422P images are enforced to have width x height % 16 = 0.
This is due to DMA constraints, which transfers only planes of 8 byte
multiples.
Global video workflow
---------------------
a) QCI stopped
Initially, the QCI interface is stopped.
When a buffer is queued, start_streaming is called and the QCI starts.
b) QCI started
More buffers can be queued while the QCI is started without halting the
capture. The new buffers are "appended" at the tail of the DMA chain, and
smoothly captured one frame after the other.
Once a buffer is filled in the QCI interface, it is marked as "DONE" and
removed from the active buffers list. It can be then requeud or dequeued by
userland application.
Once the last buffer is filled in, the QCI interface stops.
c) Capture global finite state machine schema
.. code-block:: none
+----+ +---+ +----+
| DQ | | Q | | DQ |
| v | v | v
+-----------+ +------------------------+
| STOP | | Wait for capture start |
+-----------+ Q +------------------------+
+-> | QCI: stop | ------------------> | QCI: run | <------------+
| | DMA: stop | | DMA: stop | |
| +-----------+ +-----> +------------------------+ |
| / | |
| / +---+ +----+ | |
|capture list empty / | Q | | DQ | | QCI Irq EOF |
| / | v | v v |
| +--------------------+ +----------------------+ |
| | DMA hotlink missed | | Capture running | |
| +--------------------+ +----------------------+ |
| | QCI: run | +-----> | QCI: run | <-+ |
| | DMA: stop | / | DMA: run | | |
| +--------------------+ / +----------------------+ | Other |
| ^ /DMA still | | channels |
| | capture list / running | DMA Irq End | not |
| | not empty / | | finished |
| | / v | yet |
| +----------------------+ +----------------------+ | |
| | Videobuf released | | Channel completed | | |
| +----------------------+ +----------------------+ | |
+-- | QCI: run | | QCI: run | --+ |
| DMA: run | | DMA: run | |
+----------------------+ +----------------------+ |
^ / | |
| no overrun / | overrun |
| / v |
+--------------------+ / +----------------------+ |
| Frame completed | / | Frame overran | |
+--------------------+ <-----+ +----------------------+ restart frame |
| QCI: run | | QCI: stop | --------------+
| DMA: run | | DMA: stop |
+--------------------+ +----------------------+
Legend: - each box is a FSM state
- each arrow is the condition to transition to another state
- an arrow with a comment is a mandatory transition (no condition)
- arrow "Q" means : a buffer was enqueued
- arrow "DQ" means : a buffer was dequeued
- "QCI: stop" means the QCI interface is not enabled
- "DMA: stop" means all 3 DMA channels are stopped
- "DMA: run" means at least 1 DMA channel is still running
DMA usage
---------
a) DMA flow
- first buffer queued for capture
Once a first buffer is queued for capture, the QCI is started, but data
transfer is not started. On "End Of Frame" interrupt, the irq handler
starts the DMA chain.
- capture of one videobuffer
The DMA chain starts transferring data into videobuffer RAM pages.
When all pages are transferred, the DMA irq is raised on "ENDINTR" status
- finishing one videobuffer
The DMA irq handler marks the videobuffer as "done", and removes it from
the active running queue
Meanwhile, the next videobuffer (if there is one), is transferred by DMA
- finishing the last videobuffer
On the DMA irq of the last videobuffer, the QCI is stopped.
b) DMA prepared buffer will have this structure
.. code-block:: none
+------------+-----+---------------+-----------------+
| desc-sg[0] | ... | desc-sg[last] | finisher/linker |
+------------+-----+---------------+-----------------+
This structure is pointed by dma->sg_cpu.
The descriptors are used as follows:
- desc-sg[i]: i-th descriptor, transferring the i-th sg
element to the video buffer scatter gather
- finisher: has ddadr=DADDR_STOP, dcmd=ENDIRQEN
- linker: has ddadr= desc-sg[0] of next video buffer, dcmd=0
For the next schema, let's assume d0=desc-sg[0] .. dN=desc-sg[N],
"f" stands for finisher and "l" for linker.
A typical running chain is :
.. code-block:: none
Videobuffer 1 Videobuffer 2
+---------+----+---+ +----+----+----+---+
| d0 | .. | dN | l | | d0 | .. | dN | f |
+---------+----+-|-+ ^----+----+----+---+
| |
+----+
After the chaining is finished, the chain looks like :
.. code-block:: none
Videobuffer 1 Videobuffer 2 Videobuffer 3
+---------+----+---+ +----+----+----+---+ +----+----+----+---+
| d0 | .. | dN | l | | d0 | .. | dN | l | | d0 | .. | dN | f |
+---------+----+-|-+ ^----+----+----+-|-+ ^----+----+----+---+
| | | |
+----+ +----+
new_link
c) DMA hot chaining timeslice issue
As DMA chaining is done while DMA _is_ running, the linking may be done
while the DMA jumps from one Videobuffer to another. On the schema, that
would be a problem if the following sequence is encountered :
- DMA chain is Videobuffer1 + Videobuffer2
- pxa_videobuf_queue() is called to queue Videobuffer3
- DMA controller finishes Videobuffer2, and DMA stops
.. code-block:: none
=>
Videobuffer 1 Videobuffer 2
+---------+----+---+ +----+----+----+---+
| d0 | .. | dN | l | | d0 | .. | dN | f |
+---------+----+-|-+ ^----+----+----+-^-+
| | |
+----+ +-- DMA DDADR loads DDADR_STOP
- pxa_dma_add_tail_buf() is called, the Videobuffer2 "finisher" is
replaced by a "linker" to Videobuffer3 (creation of new_link)
- pxa_videobuf_queue() finishes
- the DMA irq handler is called, which terminates Videobuffer2
- Videobuffer3 capture is not scheduled on DMA chain (as it stopped !!!)
.. code-block:: none
Videobuffer 1 Videobuffer 2 Videobuffer 3
+---------+----+---+ +----+----+----+---+ +----+----+----+---+
| d0 | .. | dN | l | | d0 | .. | dN | l | | d0 | .. | dN | f |
+---------+----+-|-+ ^----+----+----+-|-+ ^----+----+----+---+
| | | |
+----+ +----+
new_link
DMA DDADR still is DDADR_STOP
- pxa_camera_check_link_miss() is called
This checks if the DMA is finished and a buffer is still on the
pcdev->capture list. If that's the case, the capture will be restarted,
and Videobuffer3 is scheduled on DMA chain.
- the DMA irq handler finishes
.. note::
If DMA stops just after pxa_camera_check_link_miss() reads DDADR()
value, we have the guarantee that the DMA irq handler will be called back
when the DMA will finish the buffer, and pxa_camera_check_link_miss() will
be called again, to reschedule Videobuffer3.
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
YUV422P image 제약
1-16Robert Jarzmik이 작성한 이 `GPL-2.0` 문서는 PXA-Camera host driver를 설명합니다. `YUV422P` 형식의 모든 image는 `width × height % 16 = 0`을 만족해야 합니다.
이 제약은 DMA가 8-byte 배수 크기의 plane만 전송할 수 있기 때문에 생깁니다. 따라서 format negotiation과 buffer 준비 단계에서 image geometry가 이 조건을 만족해야 합니다.
.. SPDX-License-Identifier: GPL-2.0
PXA-Camera Host Driver
======================
Author: Robert Jarzmik <robert.jarzmik@free.fr>
Constraints
-----------
a) Image size for YUV422P format
All YUV422P images are enforced to have width x height % 16 = 0.
This is due to DMA constraints, which transfers only planes of 8 byte
multiples.
전체 video capture 흐름
17-34초기에는 QCI interface가 정지해 있습니다. 첫 buffer가 queue되면 `start_streaming`이 호출되고 QCI가 시작됩니다.
QCI가 동작 중일 때도 capture를 멈추지 않고 buffer를 더 queue할 수 있습니다. 새 buffer는 DMA chain의 tail에 append되어 frame이 차례로 부드럽게 capture됩니다.
QCI가 buffer 하나를 채우면 해당 buffer를 `DONE`으로 표시하고 active buffer list에서 제거합니다. 이후 userspace application은 그 buffer를 다시 queue하거나 dequeue할 수 있습니다. 마지막 buffer까지 채우면 QCI interface가 멈춥니다.
첫 queue가 QCI를 시작하고 마지막 완료가 QCI를 정지시킵니다.
Global video workflow
---------------------
a) QCI stopped
Initially, the QCI interface is stopped.
When a buffer is queued, start_streaming is called and the QCI starts.
b) QCI started
More buffers can be queued while the QCI is started without halting the
capture. The new buffers are "appended" at the tail of the DMA chain, and
smoothly captured one frame after the other.
Once a buffer is filled in the QCI interface, it is marked as "DONE" and
removed from the active buffers list. It can be then requeud or dequeued by
userland application.
Once the last buffer is filled in, the QCI interface stops.
Capture finite-state machine
35-86원문의 FSM에서 각 box는 state, 각 arrow는 다른 state로 이동하는 condition입니다. 설명이 붙은 arrow는 별도 조건 없이 반드시 일어나는 전이입니다. `Q`는 buffer enqueue, `DQ`는 buffer dequeue를 뜻합니다.
`QCI: stop`은 QCI interface가 disable된 상태이고, `DMA: stop`은 세 DMA channel이 모두 정지한 상태입니다. `DMA: run`은 최소 한 channel이 아직 동작 중임을 뜻합니다.
STOP에서 `Q`가 발생하면 QCI는 동작하지만 DMA는 EOF를 기다리는 `Wait for capture start`로 갑니다. QCI EOF interrupt가 오면 DMA를 시작해 `Capture running`으로 전이합니다. Channel 완료가 이어지며 모든 channel이 끝나면 frame 완료 또는 overrun을 판정합니다.
Overrun이면 QCI와 DMA를 멈추고 frame을 restart합니다. Capture list가 비면 STOP으로 돌아갑니다. DMA hot-link를 놓친 경우 QCI는 계속 동작하지만 DMA가 멈춘 `DMA hotlink missed` 상태가 되며, 남은 buffer가 있으면 capture를 재시작합니다.
원문의 Q/DQ self-transition과 mandatory transition 의미를 상태별로 정리했습니다.
정상 frame 완료와 overrun/hot-link miss 복구 경로를 분리했습니다.
c) Capture global finite state machine schema
.. code-block:: none
+----+ +---+ +----+
| DQ | | Q | | DQ |
| v | v | v
+-----------+ +------------------------+
| STOP | | Wait for capture start |
+-----------+ Q +------------------------+
+-> | QCI: stop | ------------------> | QCI: run | <------------+
| | DMA: stop | | DMA: stop | |
| +-----------+ +-----> +------------------------+ |
| / | |
| / +---+ +----+ | |
|capture list empty / | Q | | DQ | | QCI Irq EOF |
| / | v | v v |
| +--------------------+ +----------------------+ |
| | DMA hotlink missed | | Capture running | |
| +--------------------+ +----------------------+ |
| | QCI: run | +-----> | QCI: run | <-+ |
| | DMA: stop | / | DMA: run | | |
| +--------------------+ / +----------------------+ | Other |
| ^ /DMA still | | channels |
| | capture list / running | DMA Irq End | not |
| | not empty / | | finished |
| | / v | yet |
| +----------------------+ +----------------------+ | |
| | Videobuf released | | Channel completed | | |
| +----------------------+ +----------------------+ | |
+-- | QCI: run | | QCI: run | --+ |
| DMA: run | | DMA: run | |
+----------------------+ +----------------------+ |
^ / | |
| no overrun / | overrun |
| / v |
+--------------------+ / +----------------------+ |
| Frame completed | / | Frame overran | |
+--------------------+ <-----+ +----------------------+ restart frame |
| QCI: run | | QCI: stop | --------------+
| DMA: run | | DMA: stop |
+--------------------+ +----------------------+
Legend: - each box is a FSM state
- each arrow is the condition to transition to another state
- an arrow with a comment is a mandatory transition (no condition)
- arrow "Q" means : a buffer was enqueued
- arrow "DQ" means : a buffer was dequeued
- "QCI: stop" means the QCI interface is not enabled
- "DMA: stop" means all 3 DMA channels are stopped
- "DMA: run" means at least 1 DMA channel is still running
DMA capture 흐름
87-104첫 capture buffer가 queue되면 QCI는 시작되지만 data transfer는 즉시 시작되지 않습니다. `End Of Frame` interrupt에서 IRQ handler가 DMA chain을 시작합니다.
DMA chain은 videobuffer의 RAM page로 data를 전송합니다. 모든 page 전송이 끝나면 DMA가 `ENDINTR` status로 IRQ를 발생시킵니다.
DMA IRQ handler는 videobuffer를 `done`으로 표시하고 active running queue에서 제거합니다. 다음 videobuffer가 있으면 DMA가 동시에 이어서 전송하며, 마지막 videobuffer의 DMA IRQ에서는 QCI를 정지합니다.
QCI EOF에서 DMA를 시작하고 마지막 DMA 완료에서 QCI를 멈춥니다.
DMA usage
---------
a) DMA flow
- first buffer queued for capture
Once a first buffer is queued for capture, the QCI is started, but data
transfer is not started. On "End Of Frame" interrupt, the irq handler
starts the DMA chain.
- capture of one videobuffer
The DMA chain starts transferring data into videobuffer RAM pages.
When all pages are transferred, the DMA irq is raised on "ENDINTR" status
- finishing one videobuffer
The DMA irq handler marks the videobuffer as "done", and removes it from
the active running queue
Meanwhile, the next videobuffer (if there is one), is transferred by DMA
- finishing the last videobuffer
On the DMA irq of the last videobuffer, the QCI is stopped.
DMA prepared buffer 구조
105-120준비된 DMA buffer는 여러 `desc-sg[]` descriptor와 마지막 `finisher` 또는 `linker`로 구성되며 `dma->sg_cpu`가 이 구조를 가리킵니다.
`desc-sg[i]`는 video buffer scatter-gather의 i번째 element를 전송하는 i번째 descriptor입니다. `finisher`는 `ddadr=DADDR_STOP`, `dcmd=ENDIRQEN`으로 chain을 끝내고 interrupt를 요청합니다. `linker`는 `ddadr`가 다음 video buffer의 `desc-sg[0]`을 가리키며 `dcmd=0`입니다.
`dma->sg_cpu`가 첫 descriptor를 가리키며 마지막 slot은 finisher 또는 linker입니다.
b) DMA prepared buffer will have this structure
.. code-block:: none
+------------+-----+---------------+-----------------+
| desc-sg[0] | ... | desc-sg[last] | finisher/linker |
+------------+-----+---------------+-----------------+
This structure is pointed by dma->sg_cpu.
The descriptors are used as follows:
- desc-sg[i]: i-th descriptor, transferring the i-th sg
element to the video buffer scatter gather
- finisher: has ddadr=DADDR_STOP, dcmd=ENDIRQEN
- linker: has ddadr= desc-sg[0] of next video buffer, dcmd=0
Videobuffer DMA chain 연결
121-145아래 표기에서 `d0`부터 `dN`은 각 buffer의 `desc-sg[0]`부터 `desc-sg[N]`, `f`는 finisher, `l`은 linker를 뜻합니다.
두 buffer의 일반적인 running chain에서 Videobuffer 1의 마지막 `l`은 Videobuffer 2의 `d0`을 가리키고, Videobuffer 2는 `f`로 끝납니다.
Videobuffer 3을 append하면 Videobuffer 2의 마지막 요소가 `f`에서 `l`로 바뀌어 Videobuffer 3의 `d0`을 가리키는 `new_link`가 됩니다. 새 tail인 Videobuffer 3은 `f`로 끝납니다.
첫 buffer는 linker로 다음 buffer에 연결되고 tail만 finisher를 갖습니다.
기존 tail의 finisher를 linker로 교체한 `new_link`가 새 tail을 연결합니다.
For the next schema, let's assume d0=desc-sg[0] .. dN=desc-sg[N],
"f" stands for finisher and "l" for linker.
A typical running chain is :
.. code-block:: none
Videobuffer 1 Videobuffer 2
+---------+----+---+ +----+----+----+---+
| d0 | .. | dN | l | | d0 | .. | dN | f |
+---------+----+-|-+ ^----+----+----+---+
| |
+----+
After the chaining is finished, the chain looks like :
.. code-block:: none
Videobuffer 1 Videobuffer 2 Videobuffer 3
+---------+----+---+ +----+----+----+---+ +----+----+----+---+
| d0 | .. | dN | l | | d0 | .. | dN | l | | d0 | .. | dN | f |
+---------+----+-|-+ ^----+----+----+-|-+ ^----+----+----+---+
| | | |
+----+ +----+
new_link
DMA hot chaining timeslice 문제
146-170DMA가 실행 중인 동안 chain을 연결하므로 DMA가 한 videobuffer에서 다음 videobuffer로 넘어가는 바로 그 시점에 linking이 일어날 수 있습니다.
문제 sequence는 다음과 같습니다. Chain이 Videobuffer 1과 2로 구성된 상태에서 `pxa_videobuf_queue()`가 Videobuffer 3을 queue합니다. 그 사이 DMA controller가 Videobuffer 2를 완료하고 `DDADR_STOP`을 load해 멈춥니다.
그 뒤 `pxa_dma_add_tail_buf()`가 Videobuffer 2의 finisher를 Videobuffer 3으로 향하는 linker로 바꾸고 `pxa_videobuf_queue()`가 끝납니다. DMA IRQ handler가 Videobuffer 2를 종료하지만 DMA 자체는 이미 멈췄으므로 Videobuffer 3 capture는 chain에 schedule되지 않습니다.
Software가 tail을 교체하기 직전에 DMA가 기존 finisher의 `DADDR_STOP`을 읽을 수 있습니다.
Memory의 chain은 연결됐지만 DMA register에는 이미 stop address가 load된 상태입니다.
c) DMA hot chaining timeslice issue
As DMA chaining is done while DMA _is_ running, the linking may be done
while the DMA jumps from one Videobuffer to another. On the schema, that
would be a problem if the following sequence is encountered :
- DMA chain is Videobuffer1 + Videobuffer2
- pxa_videobuf_queue() is called to queue Videobuffer3
- DMA controller finishes Videobuffer2, and DMA stops
.. code-block:: none
=>
Videobuffer 1 Videobuffer 2
+---------+----+---+ +----+----+----+---+
| d0 | .. | dN | l | | d0 | .. | dN | f |
+---------+----+-|-+ ^----+----+----+-^-+
| | |
+----+ +-- DMA DDADR loads DDADR_STOP
- pxa_dma_add_tail_buf() is called, the Videobuffer2 "finisher" is
replaced by a "linker" to Videobuffer3 (creation of new_link)
- pxa_videobuf_queue() finishes
- the DMA irq handler is called, which terminates Videobuffer2
- Videobuffer3 capture is not scheduled on DMA chain (as it stopped !!!)
놓친 DMA link 검사와 복구
171-194Memory 상의 chain은 Videobuffer 3까지 연결됐지만 DMA `DDADR`은 여전히 `DDADR_STOP`일 수 있습니다. 이 상태를 복구하기 위해 `pxa_camera_check_link_miss()`를 호출합니다.
이 함수는 DMA가 끝났는데도 `pcdev->capture` list에 buffer가 남아 있는지 확인합니다. 조건이 맞으면 capture를 다시 시작하고 Videobuffer 3을 DMA chain에 schedule합니다. 이후 DMA IRQ handler가 끝납니다.
`pxa_camera_check_link_miss()`가 `DDADR()` 값을 읽은 직후 DMA가 멈추는 더 좁은 race도 안전합니다. DMA가 buffer를 끝내면 DMA IRQ handler가 다시 호출되고, 그 안에서 `pxa_camera_check_link_miss()`를 다시 실행해 Videobuffer 3을 재배치할 것이 보장됩니다.
Capture list와 DMA 정지 상태를 함께 검사하고, 검사 직후 발생하는 stop은 다음 IRQ에서 다시 포착합니다.
.. code-block:: none
Videobuffer 1 Videobuffer 2 Videobuffer 3
+---------+----+---+ +----+----+----+---+ +----+----+----+---+
| d0 | .. | dN | l | | d0 | .. | dN | l | | d0 | .. | dN | f |
+---------+----+-|-+ ^----+----+----+-|-+ ^----+----+----+---+
| | | |
+----+ +----+
new_link
DMA DDADR still is DDADR_STOP
- pxa_camera_check_link_miss() is called
This checks if the DMA is finished and a buffer is still on the
pcdev->capture list. If that's the case, the capture will be restarted,
and Videobuffer3 is scheduled on DMA chain.
- the DMA irq handler finishes
.. note::
If DMA stops just after pxa_camera_check_link_miss() reads DDADR()
value, we have the guarantee that the DMA irq handler will be called back
when the DMA will finish the buffer, and pxa_camera_check_link_miss() will
be called again, to reschedule Videobuffer3.
요약과 해설
pxa_camera.rst:1-194PXA-Camera host driver는 QCI의 frame boundary와 세 DMA channel의 descriptor chain을 함께 관리합니다. 실행 중인 chain의 tail을 바꾸는 race는 `DDADR_STOP`과 capture list를 검사해 복구하며, 검사 직후 발생하는 정지도 다음 DMA IRQ에서 다시 확인합니다.