요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
.. SPDX-License-Identifier: GPL-2.0
Media Controller devices
------------------------
Media Controller
~~~~~~~~~~~~~~~~
The media controller userspace API is documented in
:ref:`the Media Controller uAPI book <media_controller>`. This document focus
on the kernel-side implementation of the media framework.
Abstract media device model
^^^^^^^^^^^^^^^^^^^^^^^^^^^
Discovering a device internal topology, and configuring it at runtime, is one
of the goals of the media framework. To achieve this, hardware devices are
modelled as an oriented graph of building blocks called entities connected
through pads.
An entity is a basic media hardware building block. It can correspond to
a large variety of logical blocks such as physical hardware devices
(CMOS sensor for instance), logical hardware devices (a building block
in a System-on-Chip image processing pipeline), DMA channels or physical
connectors.
A pad is a connection endpoint through which an entity can interact with
other entities. Data (not restricted to video) produced by an entity
flows from the entity's output to one or more entity inputs. Pads should
not be confused with physical pins at chip boundaries.
A link is a point-to-point oriented connection between two pads, either
on the same entity or on different entities. Data flows from a source
pad to a sink pad.
Media device
^^^^^^^^^^^^
A media device is represented by a struct media_device
instance, defined in ``include/media/media-device.h``.
Allocation of the structure is handled by the media device driver, usually by
embedding the :c:type:`media_device` instance in a larger driver-specific
structure.
Drivers initialise media device instances by calling
:c:func:`media_device_init()`. After initialising a media device instance, it is
registered by calling :c:func:`__media_device_register()` via the macro
``media_device_register()`` and unregistered by calling
:c:func:`media_device_unregister()`. An initialised media device must be
eventually cleaned up by calling :c:func:`media_device_cleanup()`.
Note that it is not allowed to unregister a media device instance that was not
previously registered, or clean up a media device instance that was not
previously initialised.
Entities
^^^^^^^^
Entities are represented by a struct media_entity
instance, defined in ``include/media/media-entity.h``. The structure is usually
embedded into a higher-level structure, such as
:c:type:`v4l2_subdev` or :c:type:`video_device`
instances, although drivers can allocate entities directly.
Drivers initialize entity pads by calling
:c:func:`media_entity_pads_init()`.
Drivers register entities with a media device by calling
:c:func:`media_device_register_entity()`
and unregistered by calling
:c:func:`media_device_unregister_entity()`.
Interfaces
^^^^^^^^^^
Interfaces are represented by a
struct media_interface instance, defined in
``include/media/media-entity.h``. Currently, only one type of interface is
defined: a device node. Such interfaces are represented by a
struct media_intf_devnode.
Drivers initialize and create device node interfaces by calling
:c:func:`media_devnode_create()`
and remove them by calling:
:c:func:`media_devnode_remove()`.
Pads
^^^^
Pads are represented by a struct media_pad instance,
defined in ``include/media/media-entity.h``. Each entity stores its pads in
a pads array managed by the entity driver. Drivers usually embed the array in
a driver-specific structure.
Pads are identified by their entity and their 0-based index in the pads
array.
Both information are stored in the struct media_pad,
making the struct media_pad pointer the canonical way
to store and pass link references.
Pads have flags that describe the pad capabilities and state.
``MEDIA_PAD_FL_SINK`` indicates that the pad supports sinking data.
``MEDIA_PAD_FL_SOURCE`` indicates that the pad supports sourcing data.
.. note::
One and only one of ``MEDIA_PAD_FL_SINK`` or ``MEDIA_PAD_FL_SOURCE`` must
be set for each pad.
Links
^^^^^
Links are represented by a struct media_link instance,
defined in ``include/media/media-entity.h``. There are two types of links:
**1. pad to pad links**:
Associate two entities via their PADs. Each entity has a list that points
to all links originating at or targeting any of its pads.
A given link is thus stored twice, once in the source entity and once in
the target entity.
Drivers create pad to pad links by calling:
:c:func:`media_create_pad_link()` and remove with
:c:func:`media_entity_remove_links()`.
**2. interface to entity links**:
Associate one interface to a Link.
Drivers create interface to entity links by calling:
:c:func:`media_create_intf_link()` and remove with
:c:func:`media_remove_intf_links()`.
.. note::
Links can only be created after having both ends already created.
Links have flags that describe the link capabilities and state. The
valid values are described at :c:func:`media_create_pad_link()` and
:c:func:`media_create_intf_link()`.
Graph traversal
^^^^^^^^^^^^^^^
The media framework provides APIs to traverse media graphs, locating connected
entities and links.
To iterate over all entities belonging to a media device, drivers can use
the media_device_for_each_entity macro, defined in
``include/media/media-device.h``.
.. code-block:: c
struct media_entity *entity;
media_device_for_each_entity(entity, mdev) {
// entity will point to each entity in turn
...
}
Helper functions can be used to find a link between two given pads, or a pad
connected to another pad through an enabled link
(:c:func:`media_entity_find_link()`, :c:func:`media_pad_remote_pad_first()`,
:c:func:`media_entity_remote_source_pad_unique()` and
:c:func:`media_pad_remote_pad_unique()`).
Use count and power handling
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Due to the wide differences between drivers regarding power management
needs, the media controller does not implement power management. However,
the struct media_entity includes a ``use_count``
field that media drivers
can use to track the number of users of every entity for power management
needs.
The :c:type:`media_entity<media_entity>`.\ ``use_count`` field is owned by
media drivers and must not be
touched by entity drivers. Access to the field must be protected by the
:c:type:`media_device`.\ ``graph_mutex`` lock.
Links setup
^^^^^^^^^^^
Link properties can be modified at runtime by calling
:c:func:`media_entity_setup_link()`.
Pipelines and media streams
^^^^^^^^^^^^^^^^^^^^^^^^^^^
A media stream is a stream of pixels or metadata originating from one or more
source devices (such as a sensors) and flowing through media entity pads
towards the final sinks. The stream can be modified on the route by the
devices (e.g. scaling or pixel format conversions), or it can be split into
multiple branches, or multiple branches can be merged.
A media pipeline is a set of media streams which are interdependent. This
interdependency can be caused by the hardware (e.g. configuration of a second
stream cannot be changed if the first stream has been enabled) or by the driver
due to the software design. Most commonly a media pipeline consists of a single
stream which does not branch.
When starting streaming, drivers must notify all entities in the pipeline to
prevent link states from being modified during streaming by calling
:c:func:`media_pipeline_start()`.
The function will mark all the pads which are part of the pipeline as streaming.
The struct media_pipeline instance pointed to by the pipe argument will be
stored in every pad in the pipeline. Drivers should embed the struct
media_pipeline in higher-level pipeline structures and can then access the
pipeline through the struct media_pad pipe field.
Calls to :c:func:`media_pipeline_start()` can be nested.
The pipeline pointer must be identical for all nested calls to the function.
:c:func:`media_pipeline_start()` may return an error. In that case,
it will clean up any of the changes it did by itself.
When stopping the stream, drivers must notify the entities with
:c:func:`media_pipeline_stop()`.
If multiple calls to :c:func:`media_pipeline_start()` have been
made the same number of :c:func:`media_pipeline_stop()` calls
are required to stop streaming.
The :c:type:`media_entity`.\ ``pipe`` field is reset to ``NULL`` on the last
nested stop call.
Link configuration will fail with ``-EBUSY`` by default if either end of the
link is a streaming entity. Links that can be modified while streaming must
be marked with the ``MEDIA_LNK_FL_DYNAMIC`` flag.
If other operations need to be disallowed on streaming entities (such as
changing entities configuration parameters) drivers can explicitly check the
media_entity stream_count field to find out if an entity is streaming. This
operation must be done with the media_device graph_mutex held.
Link validation
^^^^^^^^^^^^^^^
Link validation is performed by :c:func:`media_pipeline_start()`
for any entity which has sink pads in the pipeline. The
:c:type:`media_entity`.\ ``link_validate()`` callback is used for that
purpose. In ``link_validate()`` callback, entity driver should check
that the properties of the source pad of the connected entity and its own
sink pad match. It is up to the type of the entity (and in the end, the
properties of the hardware) what matching actually means.
Subsystems should facilitate link validation by providing subsystem specific
helper functions to provide easy access for commonly needed information, and
in the end provide a way to use driver-specific callbacks.
Pipeline traversal
^^^^^^^^^^^^^^^^^^
Once a pipeline has been constructed with :c:func:`media_pipeline_start()`,
drivers can iterate over entities or pads in the pipeline with the
:c:macro:´media_pipeline_for_each_entity` and
:c:macro:´media_pipeline_for_each_pad` macros. Iterating over pads is
straightforward:
.. code-block:: c
media_pipeline_pad_iter iter;
struct media_pad *pad;
media_pipeline_for_each_pad(pipe, &iter, pad) {
/* 'pad' will point to each pad in turn */
...
}
To iterate over entities, the iterator needs to be initialized and cleaned up
as an additional steps:
.. code-block:: c
media_pipeline_entity_iter iter;
struct media_entity *entity;
int ret;
ret = media_pipeline_entity_iter_init(pipe, &iter);
if (ret)
...;
media_pipeline_for_each_entity(pipe, &iter, entity) {
/* 'entity' will point to each entity in turn */
...
}
media_pipeline_entity_iter_cleanup(&iter);
Media Controller Device Allocator API
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
When the media device belongs to more than one driver, the shared media
device is allocated with the shared struct device as the key for look ups.
The shared media device should stay in registered state until the last
driver unregisters it. In addition, the media device should be released when
all the references are released. Each driver gets a reference to the media
device during probe, when it allocates the media device. If media device is
already allocated, the allocate API bumps up the refcount and returns the
existing media device. The driver puts the reference back in its disconnect
routine when it calls :c:func:`media_device_delete()`.
The media device is unregistered and cleaned up from the kref put handler to
ensure that the media device stays in registered state until the last driver
unregisters the media device.
**Driver Usage**
Drivers should use the appropriate media-core routines to manage the shared
media device life-time handling the two states:
1. allocate -> register -> delete
2. get reference to already registered device -> delete
call :c:func:`media_device_delete()` routine to make sure the shared media
device delete is handled correctly.
**driver probe:**
Call :c:func:`media_device_usb_allocate()` to allocate or get a reference
Call :c:func:`media_device_register()`, if media devnode isn't registered
**driver disconnect:**
Call :c:func:`media_device_delete()` to free the media_device. Freeing is
handled by the kref put handler.
API Definitions
^^^^^^^^^^^^^^^
.. kernel-doc:: include/media/media-device.h
.. kernel-doc:: include/media/media-devnode.h
.. kernel-doc:: include/media/media-entity.h
.. kernel-doc:: include/media/media-request.h
.. kernel-doc:: include/media/media-dev-allocator.h
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
Media Controller kernel 구현
1-12이 `GPL-2.0` 문서는 Media Controller 장치와 media framework의 kernel 측 구현을 설명합니다. Media Controller userspace API는 `the Media Controller uAPI book <media_controller>`에 별도로 문서화되어 있습니다.
.. SPDX-License-Identifier: GPL-2.0
Media Controller devices
------------------------
Media Controller
~~~~~~~~~~~~~~~~
The media controller userspace API is documented in
:ref:`the Media Controller uAPI book <media_controller>`. This document focus
on the kernel-side implementation of the media framework.
추상 media device graph
13-35Media framework의 목표 가운데 하나는 장치 내부 topology를 발견하고 runtime에 구성하는 것입니다. 이를 위해 hardware device를 entity라는 building block과 이를 잇는 pad로 구성된 방향 graph로 모델링합니다.
Entity는 media hardware의 기본 building block입니다. CMOS sensor 같은 물리 장치, SoC image processing pipeline의 논리 block, DMA channel 또는 물리 connector 등 다양한 논리 단위에 대응할 수 있습니다.
Pad는 entity가 다른 entity와 상호작용하는 연결 endpoint입니다. Video에 한정되지 않는 data는 entity의 output에서 하나 이상의 entity input으로 흐릅니다. Pad는 chip 경계의 물리 pin과 다른 개념입니다.
Link는 같은 entity 또는 서로 다른 entity의 두 pad를 잇는 point-to-point 방향 연결입니다. Data는 source pad에서 sink pad로 흐릅니다.
Entity의 source pad와 sink pad를 방향 link로 연결해 data path를 표현합니다.
Abstract media device model
^^^^^^^^^^^^^^^^^^^^^^^^^^^
Discovering a device internal topology, and configuring it at runtime, is one
of the goals of the media framework. To achieve this, hardware devices are
modelled as an oriented graph of building blocks called entities connected
through pads.
An entity is a basic media hardware building block. It can correspond to
a large variety of logical blocks such as physical hardware devices
(CMOS sensor for instance), logical hardware devices (a building block
in a System-on-Chip image processing pipeline), DMA channels or physical
connectors.
A pad is a connection endpoint through which an entity can interact with
other entities. Data (not restricted to video) produced by an entity
flows from the entity's output to one or more entity inputs. Pads should
not be confused with physical pins at chip boundaries.
A link is a point-to-point oriented connection between two pads, either
on the same entity or on different entities. Data flows from a source
pad to a sink pad.
media_device 생명주기
36-55Media device는 `include/media/media-device.h`에 정의된 `struct media_device` instance로 표현합니다. 구조체 할당은 media device driver가 담당하며 일반적으로 더 큰 driver 전용 구조체 안에 `media_device`를 embed합니다.
Driver는 `media_device_init()`로 instance를 초기화합니다. 초기화한 뒤 macro `media_device_register()`를 통해 `__media_device_register()`를 호출해 등록하고, `media_device_unregister()`로 등록을 해제합니다. 초기화한 instance는 마지막에 반드시 `media_device_cleanup()`으로 정리해야 합니다.
이전에 등록하지 않은 media device를 unregister하거나, 이전에 초기화하지 않은 media device를 cleanup하는 것은 허용되지 않습니다.
초기화·등록·해제·정리 순서를 지켜야 합니다.
Media device
^^^^^^^^^^^^
A media device is represented by a struct media_device
instance, defined in ``include/media/media-device.h``.
Allocation of the structure is handled by the media device driver, usually by
embedding the :c:type:`media_device` instance in a larger driver-specific
structure.
Drivers initialise media device instances by calling
:c:func:`media_device_init()`. After initialising a media device instance, it is
registered by calling :c:func:`__media_device_register()` via the macro
``media_device_register()`` and unregistered by calling
:c:func:`media_device_unregister()`. An initialised media device must be
eventually cleaned up by calling :c:func:`media_device_cleanup()`.
Note that it is not allowed to unregister a media device instance that was not
previously registered, or clean up a media device instance that was not
previously initialised.
Entity, interface, pad
56-110Entity는 `include/media/media-entity.h`의 `struct media_entity` instance로 표현합니다. 보통 `v4l2_subdev`나 `video_device` 같은 상위 구조체에 embed하지만 driver가 entity를 직접 할당할 수도 있습니다.
Driver는 `media_entity_pads_init()`로 entity pad를 초기화하고 `media_device_register_entity()`로 media device에 entity를 등록하며 `media_device_unregister_entity()`로 등록을 해제합니다.
Interface는 `include/media/media-entity.h`의 `struct media_interface` instance입니다. 현재 정의된 interface type은 device node 하나뿐이고 `struct media_intf_devnode`로 표현합니다. Driver는 `media_devnode_create()`로 초기화·생성하고 `media_devnode_remove()`로 제거합니다.
Pad는 `include/media/media-entity.h`의 `struct media_pad` instance입니다. 각 entity는 entity driver가 관리하는 pad 배열을 가지며, driver는 보통 이 배열을 전용 구조체에 embed합니다.
Pad는 소속 entity와 pad 배열의 0-based index로 식별합니다. 두 정보가 `struct media_pad`에 저장되므로 `struct media_pad` pointer가 link reference를 저장하고 전달하는 표준 방식입니다.
`MEDIA_PAD_FL_SINK`는 data를 받는 pad를, `MEDIA_PAD_FL_SOURCE`는 data를 내보내는 pad를 뜻합니다. 모든 pad에는 두 flag 가운데 정확히 하나만 설정해야 합니다.
Entities
^^^^^^^^
Entities are represented by a struct media_entity
instance, defined in ``include/media/media-entity.h``. The structure is usually
embedded into a higher-level structure, such as
:c:type:`v4l2_subdev` or :c:type:`video_device`
instances, although drivers can allocate entities directly.
Drivers initialize entity pads by calling
:c:func:`media_entity_pads_init()`.
Drivers register entities with a media device by calling
:c:func:`media_device_register_entity()`
and unregistered by calling
:c:func:`media_device_unregister_entity()`.
Interfaces
^^^^^^^^^^
Interfaces are represented by a
struct media_interface instance, defined in
``include/media/media-entity.h``. Currently, only one type of interface is
defined: a device node. Such interfaces are represented by a
struct media_intf_devnode.
Drivers initialize and create device node interfaces by calling
:c:func:`media_devnode_create()`
and remove them by calling:
:c:func:`media_devnode_remove()`.
Pads
^^^^
Pads are represented by a struct media_pad instance,
defined in ``include/media/media-entity.h``. Each entity stores its pads in
a pads array managed by the entity driver. Drivers usually embed the array in
a driver-specific structure.
Pads are identified by their entity and their 0-based index in the pads
array.
Both information are stored in the struct media_pad,
making the struct media_pad pointer the canonical way
to store and pass link references.
Pads have flags that describe the pad capabilities and state.
``MEDIA_PAD_FL_SINK`` indicates that the pad supports sinking data.
``MEDIA_PAD_FL_SOURCE`` indicates that the pad supports sourcing data.
.. note::
One and only one of ``MEDIA_PAD_FL_SINK`` or ``MEDIA_PAD_FL_SOURCE`` must
be set for each pad.
Pad link와 interface link
111-143Link는 `include/media/media-entity.h`의 `struct media_link` instance로 표현하며 pad-to-pad link와 interface-to-entity link 두 종류가 있습니다.
Pad-to-pad link는 두 entity의 pad를 연결합니다. 각 entity는 자기 pad에서 시작하거나 자기 pad를 대상으로 하는 모든 link의 목록을 가지므로 한 link는 source entity와 target entity에 각각 한 번씩, 총 두 번 저장됩니다.
Driver는 `media_create_pad_link()`로 pad link를 만들고 `media_entity_remove_links()`로 제거합니다.
Interface-to-entity link는 interface를 entity와 연결합니다. Driver는 `media_create_intf_link()`로 만들고 `media_remove_intf_links()`로 제거합니다.
Link의 양 끝 object가 모두 만들어진 뒤에만 link를 생성할 수 있습니다. Link의 capability와 state는 flag로 나타내며 유효한 값은 `media_create_pad_link()`와 `media_create_intf_link()` 문서에 정의되어 있습니다.
Links
^^^^^
Links are represented by a struct media_link instance,
defined in ``include/media/media-entity.h``. There are two types of links:
**1. pad to pad links**:
Associate two entities via their PADs. Each entity has a list that points
to all links originating at or targeting any of its pads.
A given link is thus stored twice, once in the source entity and once in
the target entity.
Drivers create pad to pad links by calling:
:c:func:`media_create_pad_link()` and remove with
:c:func:`media_entity_remove_links()`.
**2. interface to entity links**:
Associate one interface to a Link.
Drivers create interface to entity links by calling:
:c:func:`media_create_intf_link()` and remove with
:c:func:`media_remove_intf_links()`.
.. note::
Links can only be created after having both ends already created.
Links have flags that describe the link capabilities and state. The
valid values are described at :c:func:`media_create_pad_link()` and
:c:func:`media_create_intf_link()`.
Media graph 순회
144-168Media framework는 media graph를 순회하면서 연결된 entity와 link를 찾는 API를 제공합니다.
한 media device의 모든 entity를 반복하려면 `include/media/media-device.h`에 정의된 `media_device_for_each_entity` macro를 사용합니다. 예제에서 `entity`는 반복할 때마다 다음 entity를 가리킵니다.
`media_entity_find_link()`는 지정한 두 pad 사이의 link를 찾습니다. `media_pad_remote_pad_first()`, `media_entity_remote_source_pad_unique()`, `media_pad_remote_pad_unique()`는 enabled link를 통해 연결된 pad를 찾을 때 사용합니다.
전체 entity 순회와 pad·link의 직접 탐색 helper를 구분합니다.
Graph traversal
^^^^^^^^^^^^^^^
The media framework provides APIs to traverse media graphs, locating connected
entities and links.
To iterate over all entities belonging to a media device, drivers can use
the media_device_for_each_entity macro, defined in
``include/media/media-device.h``.
.. code-block:: c
struct media_entity *entity;
media_device_for_each_entity(entity, mdev) {
// entity will point to each entity in turn
...
}
Helper functions can be used to find a link between two given pads, or a pad
connected to another pad through an enabled link
(:c:func:`media_entity_find_link()`, :c:func:`media_pad_remote_pad_first()`,
:c:func:`media_entity_remote_source_pad_unique()` and
:c:func:`media_pad_remote_pad_unique()`).
use_count와 runtime link 설정
169-189Driver마다 power management 요구가 크게 다르기 때문에 Media Controller 자체는 power management를 구현하지 않습니다. 대신 `struct media_entity`의 `use_count` 필드로 각 entity의 사용자 수를 추적할 수 있습니다.
`media_entity.use_count`는 media driver가 소유하며 entity driver가 건드리면 안 됩니다. 이 필드에 접근할 때는 `media_device.graph_mutex` lock으로 보호해야 합니다.
Runtime에는 `media_entity_setup_link()`를 호출해 link 속성을 바꿀 수 있습니다.
Use count and power handling
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Due to the wide differences between drivers regarding power management
needs, the media controller does not implement power management. However,
the struct media_entity includes a ``use_count``
field that media drivers
can use to track the number of users of every entity for power management
needs.
The :c:type:`media_entity<media_entity>`.\ ``use_count`` field is owned by
media drivers and must not be
touched by entity drivers. Access to the field must be protected by the
:c:type:`media_device`.\ ``graph_mutex`` lock.
Links setup
^^^^^^^^^^^
Link properties can be modified at runtime by calling
:c:func:`media_entity_setup_link()`.
Media stream과 pipeline 생명주기
190-239Media stream은 하나 이상의 sensor 같은 source device에서 시작하여 media entity pad를 지나 최종 sink로 흐르는 pixel 또는 metadata stream입니다. 경로의 장치는 scaling이나 pixel format 변환으로 stream을 수정할 수 있고, 여러 branch로 나누거나 여러 branch를 합칠 수도 있습니다.
Media pipeline은 서로 의존하는 media stream의 집합입니다. 두 번째 stream의 설정을 첫 번째 stream이 enabled인 동안 바꿀 수 없는 hardware 제약이나 driver software 설계 때문에 의존성이 생깁니다. 가장 흔한 pipeline은 branch가 없는 단일 stream입니다.
Streaming을 시작할 때 driver는 `media_pipeline_start()`를 호출하여 pipeline의 모든 entity에 알리고 streaming 중 link state가 바뀌지 않게 해야 합니다. 이 함수는 pipeline에 속한 모든 pad를 streaming 상태로 표시합니다.
`pipe` 인자가 가리키는 `struct media_pipeline` instance는 pipeline의 모든 pad에 저장됩니다. Driver는 이를 상위 pipeline 구조체에 embed하고 `struct media_pad`의 `pipe` 필드로 접근할 수 있습니다.
`media_pipeline_start()`는 중첩 호출할 수 있지만 모든 중첩 호출에서 pipeline pointer가 같아야 합니다. 함수가 오류를 반환하면 자신이 적용한 변경을 스스로 정리합니다.
Stream을 멈출 때는 `media_pipeline_stop()`으로 entity에 알려야 합니다. `start()`를 여러 번 호출했다면 streaming을 끝내기 위해 같은 횟수의 `stop()`을 호출해야 하며 마지막 nested stop에서 `media_entity.pipe`가 `NULL`로 reset됩니다.
기본적으로 link의 어느 한쪽 entity가 streaming 중이면 link 구성 변경은 `-EBUSY`로 실패합니다. Streaming 중 바꿀 수 있는 link는 `MEDIA_LNK_FL_DYNAMIC` flag로 표시해야 합니다.
Streaming entity에서 설정 parameter 변경 같은 다른 동작을 금지하려면 driver가 `media_entity.stream_count`를 명시적으로 확인할 수 있습니다. 이 검사는 `media_device.graph_mutex`를 잡은 상태에서 수행해야 합니다.
Start와 stop 횟수가 대칭이어야 하며 streaming 중 graph 변경은 기본적으로 차단됩니다.
Pipelines and media streams
^^^^^^^^^^^^^^^^^^^^^^^^^^^
A media stream is a stream of pixels or metadata originating from one or more
source devices (such as a sensors) and flowing through media entity pads
towards the final sinks. The stream can be modified on the route by the
devices (e.g. scaling or pixel format conversions), or it can be split into
multiple branches, or multiple branches can be merged.
A media pipeline is a set of media streams which are interdependent. This
interdependency can be caused by the hardware (e.g. configuration of a second
stream cannot be changed if the first stream has been enabled) or by the driver
due to the software design. Most commonly a media pipeline consists of a single
stream which does not branch.
When starting streaming, drivers must notify all entities in the pipeline to
prevent link states from being modified during streaming by calling
:c:func:`media_pipeline_start()`.
The function will mark all the pads which are part of the pipeline as streaming.
The struct media_pipeline instance pointed to by the pipe argument will be
stored in every pad in the pipeline. Drivers should embed the struct
media_pipeline in higher-level pipeline structures and can then access the
pipeline through the struct media_pad pipe field.
Calls to :c:func:`media_pipeline_start()` can be nested.
The pipeline pointer must be identical for all nested calls to the function.
:c:func:`media_pipeline_start()` may return an error. In that case,
it will clean up any of the changes it did by itself.
When stopping the stream, drivers must notify the entities with
:c:func:`media_pipeline_stop()`.
If multiple calls to :c:func:`media_pipeline_start()` have been
made the same number of :c:func:`media_pipeline_stop()` calls
are required to stop streaming.
The :c:type:`media_entity`.\ ``pipe`` field is reset to ``NULL`` on the last
nested stop call.
Link configuration will fail with ``-EBUSY`` by default if either end of the
link is a streaming entity. Links that can be modified while streaming must
be marked with the ``MEDIA_LNK_FL_DYNAMIC`` flag.
If other operations need to be disallowed on streaming entities (such as
changing entities configuration parameters) drivers can explicitly check the
media_entity stream_count field to find out if an entity is streaming. This
operation must be done with the media_device graph_mutex held.
Pipeline link validation
240-254`media_pipeline_start()`는 pipeline 안에서 sink pad를 가진 모든 entity에 대해 link validation을 수행합니다. 이때 `media_entity.link_validate()` callback을 사용합니다.
Entity driver의 `link_validate()`는 연결된 entity의 source pad 속성과 자기 sink pad 속성이 맞는지 검사해야 합니다. 무엇을 일치로 볼지는 entity type과 최종적으로 hardware 속성에 달려 있습니다.
Subsystem은 자주 필요한 정보에 쉽게 접근할 수 있는 전용 helper를 제공하고 마지막에는 driver 전용 callback을 사용할 방법을 제공하여 link validation을 지원해야 합니다.
Pipeline start 시 source와 sink의 subsystem별 속성 호환성을 확인합니다.
Link validation
^^^^^^^^^^^^^^^
Link validation is performed by :c:func:`media_pipeline_start()`
for any entity which has sink pads in the pipeline. The
:c:type:`media_entity`.\ ``link_validate()`` callback is used for that
purpose. In ``link_validate()`` callback, entity driver should check
that the properties of the source pad of the connected entity and its own
sink pad match. It is up to the type of the entity (and in the end, the
properties of the hardware) what matching actually means.
Subsystems should facilitate link validation by providing subsystem specific
helper functions to provide easy access for commonly needed information, and
in the end provide a way to use driver-specific callbacks.
Pipeline entity와 pad 순회
255-293`media_pipeline_start()`로 pipeline을 구성한 뒤에는 `media_pipeline_for_each_entity`와 `media_pipeline_for_each_pad` macro로 pipeline 안의 entity 또는 pad를 순회할 수 있습니다.
Pad 순회는 `media_pipeline_pad_iter`와 `struct media_pad *`를 선언한 뒤 `media_pipeline_for_each_pad(pipe, &iter, pad)`를 사용하면 됩니다. 반복할 때마다 `pad`가 다음 pad를 가리킵니다.
Entity 순회는 추가 초기화와 정리가 필요합니다. `media_pipeline_entity_iter_init(pipe, &iter)`의 반환값을 확인하고, 성공하면 `media_pipeline_for_each_entity()`로 순회한 뒤 `media_pipeline_entity_iter_cleanup(&iter)`를 호출합니다.
Pipeline traversal
^^^^^^^^^^^^^^^^^^
Once a pipeline has been constructed with :c:func:`media_pipeline_start()`,
drivers can iterate over entities or pads in the pipeline with the
:c:macro:´media_pipeline_for_each_entity` and
:c:macro:´media_pipeline_for_each_pad` macros. Iterating over pads is
straightforward:
.. code-block:: c
media_pipeline_pad_iter iter;
struct media_pad *pad;
media_pipeline_for_each_pad(pipe, &iter, pad) {
/* 'pad' will point to each pad in turn */
...
}
To iterate over entities, the iterator needs to be initialized and cleaned up
as an additional steps:
.. code-block:: c
media_pipeline_entity_iter iter;
struct media_entity *entity;
int ret;
ret = media_pipeline_entity_iter_init(pipe, &iter);
if (ret)
...;
media_pipeline_for_each_entity(pipe, &iter, entity) {
/* 'entity' will point to each entity in turn */
...
}
media_pipeline_entity_iter_cleanup(&iter);
공유 Media Controller Device Allocator
294-329Media device가 둘 이상의 driver에 속하면 공유 `struct device`를 lookup key로 사용하여 shared media device를 할당합니다.
공유 media device는 마지막 driver가 unregister할 때까지 등록 상태를 유지하고 모든 reference가 해제되었을 때 release되어야 합니다. 각 driver는 probe 중 media device를 할당하면서 reference를 얻습니다. 이미 할당되어 있으면 allocate API가 refcount를 증가시키고 기존 media device를 반환합니다.
Driver는 disconnect routine에서 `media_device_delete()`를 호출해 reference를 돌려놓습니다. kref put handler가 unregister와 cleanup을 수행하므로 마지막 driver가 unregister할 때까지 media device의 등록 상태가 유지됩니다.
Driver가 처리할 생명주기는 새 장치의 `allocate → register → delete`와 이미 등록된 장치의 `reference 획득 → delete` 두 가지입니다. 공유 장치 삭제를 정확히 처리하려면 반드시 `media_device_delete()`를 사용해야 합니다.
Driver probe에서는 `media_device_usb_allocate()`로 새 장치를 할당하거나 reference를 얻고 media devnode가 등록되지 않았다면 `media_device_register()`를 호출합니다. Driver disconnect에서는 `media_device_delete()`를 호출하며 실제 free는 kref put handler가 처리합니다.
여러 driver가 공유하는 장치는 마지막 reference가 사라질 때 unregister·cleanup됩니다.
Media Controller Device Allocator API
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
When the media device belongs to more than one driver, the shared media
device is allocated with the shared struct device as the key for look ups.
The shared media device should stay in registered state until the last
driver unregisters it. In addition, the media device should be released when
all the references are released. Each driver gets a reference to the media
device during probe, when it allocates the media device. If media device is
already allocated, the allocate API bumps up the refcount and returns the
existing media device. The driver puts the reference back in its disconnect
routine when it calls :c:func:`media_device_delete()`.
The media device is unregistered and cleaned up from the kref put handler to
ensure that the media device stays in registered state until the last driver
unregisters the media device.
**Driver Usage**
Drivers should use the appropriate media-core routines to manage the shared
media device life-time handling the two states:
1. allocate -> register -> delete
2. get reference to already registered device -> delete
call :c:func:`media_device_delete()` routine to make sure the shared media
device delete is handled correctly.
**driver probe:**
Call :c:func:`media_device_usb_allocate()` to allocate or get a reference
Call :c:func:`media_device_register()`, if media devnode isn't registered
**driver disconnect:**
Call :c:func:`media_device_delete()` to free the media_device. Freeing is
handled by the kref put handler.
Media Controller API 정의
330-341Media Controller의 API 정의는 media device, devnode, entity, request, shared device allocator header의 kernel-doc에서 가져옵니다.
API Definitions
^^^^^^^^^^^^^^^
.. kernel-doc:: include/media/media-device.h
.. kernel-doc:: include/media/media-devnode.h
.. kernel-doc:: include/media/media-entity.h
.. kernel-doc:: include/media/media-request.h
.. kernel-doc:: include/media/media-dev-allocator.h
요약과 해설
mc-core.rst:1-341Media Controller는 hardware topology를 entity·pad·방향 link graph로 나타냅니다. Driver는 object 생성 순서와 pad flag 불변식을 지키고 `graph_mutex`로 use count와 streaming 상태 검사를 보호해야 합니다.
Pipeline은 중첩 start와 같은 횟수의 stop을 요구하며 streaming 중 link 변경은 기본적으로 `-EBUSY`입니다. 여러 driver가 media device를 공유할 때는 allocator와 kref가 마지막 reference까지 등록 상태를 유지합니다.