diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-01-13 11:46:37 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-01-13 11:46:37 -0800 |
commit | 77a76b04d2be1c45b8fd746b7ef754525029340c (patch) | |
tree | ef5db67c07d538a43d160847acefe80f3c049dba | |
parent | 50ae833e471fe1a1a906a0342bdaa690e69fcc19 (diff) | |
parent | be0270ec89e6b9b49de7e533dd1f3a89ad34d205 (diff) | |
download | op-kernel-dev-77a76b04d2be1c45b8fd746b7ef754525029340c.zip op-kernel-dev-77a76b04d2be1c45b8fd746b7ef754525029340c.tar.gz |
Merge tag 'media/v4.5-2' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media
Pull second batch of media updates from Mauro Carvalho Chehab:
"This is the second part of the media patches. It contains the media
controller next generation patches, with is the result of one year of
discussions and development. It also contains patches to enable media
controller support at the DVB subsystem.
The goal is to improve the media controller to allow proper support
for other types of Video4Linux devices (radio and TV ones) and to
extend the media controller functionality to allow it to be used by
other subsystems like DVB, ALSA and IIO.
In order to use the new functionality, a new ioctl is needed
(MEDIA_IOC_G_TOPOLOGY). As we're still discussing how to pack the
struct fields of this ioctl in order to avoid compat32 issues, I
decided to add a patch at the end of this series commenting out the
new ioctl, in order to postpone the addition of the new ioctl to the
next Kernel version (4.6).
With that, no userspace visible changes should happen at the media
controller API, as the existing ioctls are untouched. Yet, it helps
DVB, ALSA and IIO developers to develop and test the patches adding
media controller support there, as the core will contain all required
internal changes to allow adding support for devices that belong to
those subsystems"
* tag 'media/v4.5-2' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (177 commits)
[media] Postpone the addition of MEDIA_IOC_G_TOPOLOGY
[media] mxl111sf: Add a tuner entity
[media] dvbdev: create links on devices with multiple frontends
[media] media-entitiy: add a function to create multiple links
[media] dvb-usb-v2: postpone removal of media_device
[media] dvbdev: Add RF connector if needed
[media] dvbdev: remove two dead functions if !CONFIG_MEDIA_CONTROLLER_DVB
[media] call media_device_init() before registering the V4L2 device
[media] uapi/media.h: Use u32 for the number of graph objects
[media] media-entity: don't sleep at media_device_register_entity()
[media] media-entity: increase max number of PADs
[media] media-entity.h: document the remaining functions
[media] media-device.h: use just one u32 counter for object ID
[media] media-entity.h fix documentation for several parameters
[media] DocBook: document media_entity_graph_walk_cleanup()
[media] move documentation to the header files
[media] media: Move MEDIA_ENTITY_MAX_PADS from media-entity.h to media-entity.c
[media] media: Remove pre-allocated entity enumeration bitmap
[media] staging: v4l: davinci_vpbe: Use the new media graph walk interface
[media] staging: v4l: omap4iss: Use the new media graph walk interface
...
130 files changed, 5237 insertions, 1849 deletions
diff --git a/Documentation/DocBook/device-drivers.tmpl b/Documentation/DocBook/device-drivers.tmpl index 7b3fcc5..cdd8b24 100644 --- a/Documentation/DocBook/device-drivers.tmpl +++ b/Documentation/DocBook/device-drivers.tmpl @@ -263,6 +263,7 @@ X!Isound/sound_firmware.c !Iinclude/media/lirc_dev.h </sect1> <sect1><title>Media Controller devices</title> +!Pinclude/media/media-device.h Media Controller !Iinclude/media/media-device.h !Iinclude/media/media-devnode.h !Iinclude/media/media-entity.h diff --git a/Documentation/DocBook/media/v4l/media-controller.xml b/Documentation/DocBook/media/v4l/media-controller.xml index 873ac3a..5f2fc07 100644 --- a/Documentation/DocBook/media/v4l/media-controller.xml +++ b/Documentation/DocBook/media/v4l/media-controller.xml @@ -58,21 +58,36 @@ <title>Media device model</title> <para>Discovering a device internal topology, and configuring it at runtime, is one of the goals of the media controller API. To achieve this, hardware - devices are modelled as an oriented graph of building blocks called entities - connected through pads.</para> - <para>An entity is a basic media hardware or software 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.</para> - <para>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.</para> - <para>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.</para> + devices and Linux Kernel interfaces are modelled as graph objects on + an oriented graph. The object types that constitute the graph are:</para> + <itemizedlist> + <listitem><para>An <emphasis role="bold">entity</emphasis> + is a basic media hardware or software 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.</para></listitem> + <listitem><para>An <emphasis role="bold">interface</emphasis> + is a graph representation of a Linux Kernel userspace API interface, + like a device node or a sysfs file that controls one or more entities + in the graph.</para></listitem> + <listitem><para>A <emphasis role="bold">pad</emphasis> + is a data 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.</para></listitem> + <listitem><para>A <emphasis role="bold">data link</emphasis> + 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.</para></listitem> + <listitem><para>An <emphasis role="bold">interface link</emphasis> + is a point-to-point bidirectional control connection between a Linux + Kernel interface and an entity.m</para></listitem> + </itemizedlist> </section> + + <!-- All non-ioctl specific data types go here. --> + &sub-media-types; </chapter> <appendix id="media-user-func"> @@ -83,6 +98,7 @@ &sub-media-func-ioctl; <!-- All ioctls go here. --> &sub-media-ioc-device-info; + &sub-media-ioc-g-topology; &sub-media-ioc-enum-entities; &sub-media-ioc-enum-links; &sub-media-ioc-setup-link; diff --git a/Documentation/DocBook/media/v4l/media-ioc-enum-entities.xml b/Documentation/DocBook/media/v4l/media-ioc-enum-entities.xml index 5872f8bb..0c4f96b 100644 --- a/Documentation/DocBook/media/v4l/media-ioc-enum-entities.xml +++ b/Documentation/DocBook/media/v4l/media-ioc-enum-entities.xml @@ -59,15 +59,6 @@ <para>Entity IDs can be non-contiguous. Applications must <emphasis>not</emphasis> try to enumerate entities by calling MEDIA_IOC_ENUM_ENTITIES with increasing id's until they get an error.</para> - <para>Two or more entities that share a common non-zero - <structfield>group_id</structfield> value are considered as logically - grouped. Groups are used to report - <itemizedlist> - <listitem><para>ALSA, VBI and video nodes that carry the same media - stream</para></listitem> - <listitem><para>lens and flash controllers associated with a sensor</para></listitem> - </itemizedlist> - </para> <table pgwide="1" frame="none" id="media-entity-desc"> <title>struct <structname>media_entity_desc</structname></title> @@ -106,7 +97,7 @@ <entry><structfield>revision</structfield></entry> <entry></entry> <entry></entry> - <entry>Entity revision in a driver/hardware specific format.</entry> + <entry>Entity revision. Always zero (obsolete)</entry> </row> <row> <entry>__u32</entry> @@ -120,7 +111,7 @@ <entry><structfield>group_id</structfield></entry> <entry></entry> <entry></entry> - <entry>Entity group ID</entry> + <entry>Entity group ID. Always zero (obsolete)</entry> </row> <row> <entry>__u16</entry> @@ -171,97 +162,6 @@ </tbody> </tgroup> </table> - - <table frame="none" pgwide="1" id="media-entity-type"> - <title>Media entity types</title> - <tgroup cols="2"> - <colspec colname="c1"/> - <colspec colname="c2"/> - <tbody valign="top"> - <row> - <entry><constant>MEDIA_ENT_T_DEVNODE</constant></entry> - <entry>Unknown device node</entry> - </row> - <row> - <entry><constant>MEDIA_ENT_T_DEVNODE_V4L</constant></entry> - <entry>V4L video, radio or vbi device node</entry> - </row> - <row> - <entry><constant>MEDIA_ENT_T_DEVNODE_FB</constant></entry> - <entry>Frame buffer device node</entry> - </row> - <row> - <entry><constant>MEDIA_ENT_T_DEVNODE_ALSA</constant></entry> - <entry>ALSA card</entry> - </row> - <row> - <entry><constant>MEDIA_ENT_T_DEVNODE_DVB_FE</constant></entry> - <entry>DVB frontend devnode</entry> - </row> - <row> - <entry><constant>MEDIA_ENT_T_DEVNODE_DVB_DEMUX</constant></entry> - <entry>DVB demux devnode</entry> - </row> - <row> - <entry><constant>MEDIA_ENT_T_DEVNODE_DVB_DVR</constant></entry> - <entry>DVB DVR devnode</entry> - </row> - <row> - <entry><constant>MEDIA_ENT_T_DEVNODE_DVB_CA</constant></entry> - <entry>DVB CAM devnode</entry> - </row> - <row> - <entry><constant>MEDIA_ENT_T_DEVNODE_DVB_NET</constant></entry> - <entry>DVB network devnode</entry> - </row> - <row> - <entry><constant>MEDIA_ENT_T_V4L2_SUBDEV</constant></entry> - <entry>Unknown V4L sub-device</entry> - </row> - <row> - <entry><constant>MEDIA_ENT_T_V4L2_SUBDEV_SENSOR</constant></entry> - <entry>Video sensor</entry> - </row> - <row> - <entry><constant>MEDIA_ENT_T_V4L2_SUBDEV_FLASH</constant></entry> - <entry>Flash controller</entry> - </row> - <row> - <entry><constant>MEDIA_ENT_T_V4L2_SUBDEV_LENS</constant></entry> - <entry>Lens controller</entry> - </row> - <row> - <entry><constant>MEDIA_ENT_T_V4L2_SUBDEV_DECODER</constant></entry> - <entry>Video decoder, the basic function of the video decoder is to - accept analogue video from a wide variety of sources such as - broadcast, DVD players, cameras and video cassette recorders, in - either NTSC, PAL or HD format and still occasionally SECAM, separate - it into its component parts, luminance and chrominance, and output - it in some digital video standard, with appropriate embedded timing - signals.</entry> - </row> - <row> - <entry><constant>MEDIA_ENT_T_V4L2_SUBDEV_TUNER</constant></entry> - <entry>TV and/or radio tuner</entry> - </row> - </tbody> - </tgroup> - </table> - - <table frame="none" pgwide="1" id="media-entity-flag"> - <title>Media entity flags</title> - <tgroup cols="2"> - <colspec colname="c1"/> - <colspec colname="c2"/> - <tbody valign="top"> - <row> - <entry><constant>MEDIA_ENT_FL_DEFAULT</constant></entry> - <entry>Default entity for its type. Used to discover the default - audio, VBI and video devices, the default camera sensor, ...</entry> - </row> - </tbody> - </tgroup> - </table> </refsect1> <refsect1> diff --git a/Documentation/DocBook/media/v4l/media-ioc-enum-links.xml b/Documentation/DocBook/media/v4l/media-ioc-enum-links.xml index 74fb394..2bbeea9 100644 --- a/Documentation/DocBook/media/v4l/media-ioc-enum-links.xml +++ b/Documentation/DocBook/media/v4l/media-ioc-enum-links.xml @@ -118,35 +118,6 @@ </tgroup> </table> - <table frame="none" pgwide="1" id="media-pad-flag"> - <title>Media pad flags</title> - <tgroup cols="2"> - <colspec colname="c1"/> - <colspec colname="c2"/> - <tbody valign="top"> - <row> - <entry><constant>MEDIA_PAD_FL_SINK</constant></entry> - <entry>Input pad, relative to the entity. Input pads sink data and - are targets of links.</entry> - </row> - <row> - <entry><constant>MEDIA_PAD_FL_SOURCE</constant></entry> - <entry>Output pad, relative to the entity. Output pads source data - and are origins of links.</entry> - </row> - <row> - <entry><constant>MEDIA_PAD_FL_MUST_CONNECT</constant></entry> - <entry>If this flag is set and the pad is linked to any other - pad, then at least one of those links must be enabled for the - entity to be able to stream. There could be temporary reasons - (e.g. device configuration dependent) for the pad to need - enabled links even when this flag isn't set; the absence of the - flag doesn't imply there is none.</entry> - </row> - </tbody> - </tgroup> - </table> - <table pgwide="1" frame="none" id="media-link-desc"> <title>struct <structname>media_link_desc</structname></title> <tgroup cols="3"> @@ -171,33 +142,6 @@ </tgroup> </table> - <table frame="none" pgwide="1" id="media-link-flag"> - <title>Media link flags</title> - <tgroup cols="2"> - <colspec colname="c1"/> - <colspec colname="c2"/> - <tbody valign="top"> - <row> - <entry><constant>MEDIA_LNK_FL_ENABLED</constant></entry> - <entry>The link is enabled and can be used to transfer media data. - When two or more links target a sink pad, only one of them can be - enabled at a time.</entry> - </row> - <row> - <entry><constant>MEDIA_LNK_FL_IMMUTABLE</constant></entry> - <entry>The link enabled state can't be modified at runtime. An - immutable link is always enabled.</entry> - </row> - <row> - <entry><constant>MEDIA_LNK_FL_DYNAMIC</constant></entry> - <entry>The link enabled state can be modified during streaming. This - flag is set by drivers and is read-only for applications.</entry> - </row> - </tbody> - </tgroup> - </table> - <para>One and only one of <constant>MEDIA_PAD_FL_SINK</constant> and - <constant>MEDIA_PAD_FL_SOURCE</constant> must be set for every pad.</para> </refsect1> <refsect1> diff --git a/Documentation/DocBook/media/v4l/media-ioc-g-topology.xml b/Documentation/DocBook/media/v4l/media-ioc-g-topology.xml new file mode 100644 index 0000000..63152ab --- /dev/null +++ b/Documentation/DocBook/media/v4l/media-ioc-g-topology.xml @@ -0,0 +1,394 @@ +<refentry id="media-g-topology"> + <refmeta> + <refentrytitle>ioctl MEDIA_IOC_G_TOPOLOGY</refentrytitle> + &manvol; + </refmeta> + + <refnamediv> + <refname>MEDIA_IOC_G_TOPOLOGY</refname> + <refpurpose>Enumerate the graph topology and graph element properties</refpurpose> + </refnamediv> + + <refsynopsisdiv> + <funcsynopsis> + <funcprototype> + <funcdef>int <function>ioctl</function></funcdef> + <paramdef>int <parameter>fd</parameter></paramdef> + <paramdef>int <parameter>request</parameter></paramdef> + <paramdef>struct media_v2_topology *<parameter>argp</parameter></paramdef> + </funcprototype> + </funcsynopsis> + </refsynopsisdiv> + + <refsect1> + <title>Arguments</title> + + <variablelist> + <varlistentry> + <term><parameter>fd</parameter></term> + <listitem> + <para>File descriptor returned by + <link linkend='media-func-open'><function>open()</function></link>.</para> + </listitem> + </varlistentry> + <varlistentry> + <term><parameter>request</parameter></term> + <listitem> + <para>MEDIA_IOC_G_TOPOLOGY</para> + </listitem> + </varlistentry> + <varlistentry> + <term><parameter>argp</parameter></term> + <listitem> + <para></para> + </listitem> + </varlistentry> + </variablelist> + </refsect1> + + <refsect1> + <title>Description</title> + + <para><emphasis role="bold">NOTE:</emphasis> This new ioctl is programmed to be added on Kernel 4.6. Its definition/arguments may change until its final version.</para> + + <para>The typical usage of this ioctl is to call it twice. + On the first call, the structure defined at &media-v2-topology; should + be zeroed. At return, if no errors happen, this ioctl will return the + <constant>topology_version</constant> and the total number of entities, + interfaces, pads and links.</para> + <para>Before the second call, the userspace should allocate arrays to + store the graph elements that are desired, putting the pointers to them + at the ptr_entities, ptr_interfaces, ptr_links and/or ptr_pads, keeping + the other values untouched.</para> + <para>If the <constant>topology_version</constant> remains the same, the + ioctl should fill the desired arrays with the media graph elements.</para> + + <table pgwide="1" frame="none" id="media-v2-topology"> + <title>struct <structname>media_v2_topology</structname></title> + <tgroup cols="5"> + <colspec colname="c1" /> + <colspec colname="c2" /> + <colspec colname="c3" /> + <colspec colname="c4" /> + <colspec colname="c5" /> + <tbody valign="top"> + <row> + <entry>__u64</entry> + <entry><structfield>topology_version</structfield></entry> + <entry></entry> + <entry></entry> + <entry>Version of the media graph topology. When the graph is + created, this field starts with zero. Every time a graph + element is added or removed, this field is + incremented.</entry> + </row> + <row> + <entry>__u64</entry> + <entry><structfield>num_entities</structfield></entry> + <entry></entry> + <entry></entry> + <entry>Number of entities in the graph</entry> + </row> + <row> + <entry>__u64</entry> + <entry><structfield>ptr_entities</structfield></entry> + <entry></entry> + <entry></entry> + <entry>A pointer to a memory area where the entities array + will be stored, converted to a 64-bits integer. + It can be zero. if zero, the ioctl won't store the + entities. It will just update + <constant>num_entities</constant></entry> + </row> + <row> + <entry>__u64</entry> + <entry><structfield>num_interfaces</structfield></entry> + <entry></entry> + <entry></entry> + <entry>Number of interfaces in the graph</entry> + </row> + <row> + <entry>__u64</entry> + <entry><structfield>ptr_interfaces</structfield></entry> + <entry></entry> + <entry></entry> + <entry>A pointer to a memory area where the interfaces array + will be stored, converted to a 64-bits integer. + It can be zero. if zero, the ioctl won't store the + interfaces. It will just update + <constant>num_interfaces</constant></entry> + </row> + <row> + <entry>__u64</entry> + <entry><structfield>num_pads</structfield></entry> + <entry></entry> + <entry></entry> + <entry>Total number of pads in the graph</entry> + </row> + <row> + <entry>__u64</entry> + <entry><structfield>ptr_pads</structfield></entry> + <entry></entry> + <entry></entry> + <entry>A pointer to a memory area where the pads array + will be stored, converted to a 64-bits integer. + It can be zero. if zero, the ioctl won't store the + pads. It will just update + <constant>num_pads</constant></entry> + </row> + <row> + <entry>__u64</entry> + <entry><structfield>num_links</structfield></entry> + <entry></entry> + <entry></entry> + <entry>Total number of data and interface links in the graph</entry> + </row> + <row> + <entry>__u64</entry> + <entry><structfield>ptr_links</structfield></entry> + <entry></entry> + <entry></entry> + <entry>A pointer to a memory area where the links array + will be stored, converted to a 64-bits integer. + It can be zero. if zero, the ioctl won't store the + links. It will just update + <constant>num_links</constant></entry> + </row> + </tbody> + </tgroup> + </table> + + <table pgwide="1" frame="none" id="media-v2-entity"> + <title>struct <structname>media_v2_entity</structname></title> + <tgroup cols="5"> + <colspec colname="c1" /> + <colspec colname="c2" /> + <colspec colname="c3" /> + <colspec colname="c4" /> + <colspec colname="c5" /> + <tbody valign="top"> + <row> + <entry>__u32</entry> + <entry><structfield>id</structfield></entry> + <entry></entry> + <entry></entry> + <entry>Unique ID for the entity.</entry> + </row> + <row> + <entry>char</entry> + <entry><structfield>name</structfield>[64]</entry> + <entry></entry> + <entry></entry> + <entry>Entity name as an UTF-8 NULL-terminated string.</entry> + </row> + <row> + <entry>__u32</entry> + <entry><structfield>function</structfield></entry> + <entry></entry> + <entry></entry> + <entry>Entity main function, see <xref linkend="media-entity-type" /> for details.</entry> + </row> + <row> + <entry>__u32</entry> + <entry><structfield>reserved</structfield>[12]</entry> + <entry>Reserved for future extensions. Drivers and applications must + set this array to zero.</entry> + </row> + </tbody> + </tgroup> + </table> + + <table pgwide="1" frame="none" id="media-v2-interface"> + <title>struct <structname>media_v2_interface</structname></title> + <tgroup cols="5"> + <colspec colname="c1" /> + <colspec colname="c2" /> + <colspec colname="c3" /> + <colspec colname="c4" /> + <colspec colname="c5" /> + <tbody valign="top"> + <row> + <entry>__u32</entry> + <entry><structfield>id</structfield></entry> + <entry></entry> + <entry></entry> + <entry>Unique ID for the interface.</entry> + </row> + <row> + <entry>__u32</entry> + <entry><structfield>intf_type</structfield></entry> + <entry></entry> + <entry></entry> + <entry>Interface type, see <xref linkend="media-intf-type" /> for details.</entry> + </row> + <row> + <entry>__u32</entry> + <entry><structfield>flags</structfield></entry> + <entry></entry> + <entry></entry> + <entry>Interface flags. Currently unused.</entry> + </row> + <row> + <entry>__u32</entry> + <entry><structfield>reserved</structfield>[9]</entry> + <entry></entry> + <entry></entry> + <entry>Reserved for future extensions. Drivers and applications must + set this array to zero.</entry> + </row> + <row> + <entry>struct media_v2_intf_devnode</entry> + <entry><structfield>devnode</structfield></entry> + <entry></entry> + <entry></entry> + <entry>Used only for device node interfaces. See <xref linkend="media-v2-intf-devnode" /> for details..</entry> + </row> + </tbody> + </tgroup> + </table> + + <table pgwide="1" frame="none" id="media-v2-intf-devnode"> + <title>struct <structname>media_v2_interface</structname></title> + <tgroup cols="5"> + <colspec colname="c1" /> + <colspec colname="c2" /> + <colspec colname="c3" /> + <colspec colname="c4" /> + <colspec colname="c5" /> + <tbody valign="top"> + <row> + <entry>__u32</entry> + <entry><structfield>major</structfield></entry> + <entry></entry> + <entry></entry> + <entry>Device node major number.</entry> + </row> + <row> + <entry>__u32</entry> + <entry><structfield>minor</structfield></entry> + <entry></entry> + <entry></entry> + <entry>Device node minor number.</entry> + </row> + </tbody> + </tgroup> + </table> + + <table pgwide="1" frame="none" id="media-v2-pad"> + <title>struct <structname>media_v2_pad</structname></title> + <tgroup cols="5"> + <colspec colname="c1" /> + <colspec colname="c2" /> + <colspec colname="c3" /> + <colspec colname="c4" /> + <colspec colname="c5" /> + <tbody valign="top"> + <row> + <entry>__u32</entry> + <entry><structfield>id</structfield></entry> + <entry></entry> + <entry></entry> + <entry>Unique ID for the pad.</entry> + </row> + <row> + <entry>__u32</entry> + <entry><structfield>entity_id</structfield></entry> + <entry></entry> + <entry></entry> + <entry>Unique ID for the entity where this pad belongs.</entry> + </row> + <row> + <entry>__u32</entry> + <entry><structfield>flags</structfield></entry> + <entry></entry> + <entry></entry> + <entry>Pad flags, see <xref linkend="media-pad-flag" /> for more details.</entry> + </row> + <row> + <entry>__u32</entry> + <entry><structfield>reserved</structfield>[9]</entry> + <entry></entry> + <entry></entry> + <entry>Reserved for future extensions. Drivers and applications must + set this array to zero.</entry> + </row> + </tbody> + </tgroup> + </table> + + <table pgwide="1" frame="none" id="media-v2-link"> + <title>struct <structname>media_v2_pad</structname></title> + <tgroup cols="5"> + <colspec colname="c1" /> + <colspec colname="c2" /> + <colspec colname="c3" /> + <colspec colname="c4" /> + <colspec colname="c5" /> + <tbody valign="top"> + <row> + <entry>__u32</entry> + <entry><structfield>id</structfield></entry> + <entry></entry> + <entry></entry> + <entry>Unique ID for the pad.</entry> + </row> + <row> + <entry>__u32</entry> + <entry><structfield>source_id</structfield></entry> + <entry></entry> + <entry></entry> + <entry> + <para>On pad to pad links: unique ID for the source pad.</para> + <para>On interface to entity links: unique ID for the interface.</para> + </entry> + </row> + <row> + <entry>__u32</entry> + <entry><structfield>sink_id</structfield></entry> + <entry></entry> + <entry></entry> + <entry> + <para>On pad to pad links: unique ID for the sink pad.</para> + <para>On interface to entity links: unique ID for the entity.</para> + </entry> + </row> + <row> + <entry>__u32</entry> + <entry><structfield>flags</structfield></entry> + <entry></entry> + <entry></entry> + <entry>Link flags, see <xref linkend="media-link-flag" /> for more details.</entry> + </row> + <row> + <entry>__u32</entry> + <entry><structfield>reserved</structfield>[5]</entry> + <entry></entry> + <entry></entry> + <entry>Reserved for future extensions. Drivers and applications must + set this array to zero.</entry> + </row> + </tbody> + </tgroup> + </table> + + </refsect1> + + <refsect1> + &return-value; + + <variablelist> + <varlistentry> + <term><errorcode>ENOSPC</errorcode></term> + <listitem> + <para>This is returned when either one or more of the num_entities, + num_interfaces, num_links or num_pads are non-zero and are smaller + than the actual number of elements inside the graph. This may happen + if the <constant>topology_version</constant> changed when compared + to the last time this ioctl was called. Userspace should usually + free the area for the pointers, zero the struct elements and call + this ioctl again.</para> + </listitem> + </varlistentry> + </variablelist> + </refsect1> +</refentry> diff --git a/Documentation/DocBook/media/v4l/media-types.xml b/Documentation/DocBook/media/v4l/media-types.xml new file mode 100644 index 0000000..1af3842 --- /dev/null +++ b/Documentation/DocBook/media/v4l/media-types.xml @@ -0,0 +1,240 @@ +<section id="media-controller-types"> +<title>Types and flags used to represent the media graph elements</title> + + <table frame="none" pgwide="1" id="media-entity-type"> + <title>Media entity types</title> + <tgroup cols="2"> + <colspec colname="c1"/> + <colspec colname="c2"/> + <tbody valign="top"> + <row> + <entry><constant>MEDIA_ENT_F_UNKNOWN</constant> and <constant>MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN</constant></entry> + <entry>Unknown entity. That generally indicates that + a driver didn't initialize properly the entity, with is a Kernel bug</entry> + </row> + <row> + <entry><constant>MEDIA_ENT_F_IO_V4L</constant></entry> + <entry>Data streaming input and/or output entity.</entry> + </row> + <row> + <entry><constant>MEDIA_ENT_F_IO_VBI</constant></entry> + <entry>V4L VBI streaming input or output entity</entry> + </row> + <row> + <entry><constant>MEDIA_ENT_F_IO_SWRADIO</constant></entry> + <entry>V4L Software Digital Radio (SDR) streaming input or output entity</entry> + </row> + <row> + <entry><constant>MEDIA_ENT_F_IO_DTV</constant></entry> + <entry>DVB Digital TV streaming input or output entity</entry> + </row> + <row> + <entry><constant>MEDIA_ENT_F_DTV_DEMOD</constant></entry> + <entry>Digital TV demodulator entity.</entry> + </row> + <row> + <entry><constant>MEDIA_ENT_F_TS_DEMUX</constant></entry> + <entry>MPEG Transport stream demux entity. Could be implemented on hardware or in Kernelspace by the Linux DVB subsystem.</entry> + </row> + <row> + <entry><constant>MEDIA_ENT_F_DTV_CA</constant></entry> + <entry>Digital TV Conditional Access module (CAM) entity</entry> + </row> + <row> + <entry><constant>MEDIA_ENT_F_DTV_NET_DECAP</constant></entry> + <entry>Digital TV network ULE/MLE desencapsulation entity. Could be implemented on hardware or in Kernelspace</entry> + </row> + <row> + <entry><constant>MEDIA_ENT_F_CONN_RF</constant></entry> + <entry>Connector for a Radio Frequency (RF) signal.</entry> + </row> + <row> + <entry><constant>MEDIA_ENT_F_CONN_SVIDEO</constant></entry> + <entry>Connector for a S-Video signal.</entry> + </row> + <row> + <entry><constant>MEDIA_ENT_F_CONN_COMPOSITE</constant></entry> + <entry>Connector for a RGB composite signal.</entry> + </row> + <row> + <entry><constant>MEDIA_ENT_F_CONN_TEST</constant></entry> + <entry>Connector for a test generator.</entry> + </row> + <row> + <entry><constant>MEDIA_ENT_F_CAM_SENSOR</constant></entry> + <entry>Camera video sensor entity.</entry> + </row> + <row> + <entry><constant>MEDIA_ENT_F_FLASH</constant></entry> + <entry>Flash controller entity.</entry> + </row> + <row> + <entry><constant>MEDIA_ENT_F_LENS</constant></entry> + <entry>Lens controller entity.</entry> + </row> + <row> + <entry><constant>MEDIA_ENT_F_ATV_DECODER</constant></entry> + <entry>Analog video decoder, the basic function of the video decoder + is to accept analogue video from a wide variety of sources such as + broadcast, DVD players, cameras and video cassette recorders, in + either NTSC, PAL, SECAM or HD format, separating the stream + into its component parts, luminance and chrominance, and output + it in some digital video standard, with appropriate timing + signals.</entry> + </row> + <row> + <entry><constant>MEDIA_ENT_F_TUNER</constant></entry> + <entry>Digital TV, analog TV, radio and/or software radio tuner.</entry> + </row> + </tbody> + </tgroup> + </table> + + <table frame="none" pgwide="1" id="media-entity-flag"> + <title>Media entity flags</title> + <tgroup cols="2"> + <colspec colname="c1"/> + <colspec colname="c2"/> + <tbody valign="top"> + <row> + <entry><constant>MEDIA_ENT_FL_DEFAULT</constant></entry> + <entry>Default entity for its type. Used to discover the default + audio, VBI and video devices, the default camera sensor, ...</entry> + </row> + <row> + <entry><constant>MEDIA_ENT_FL_CONNECTOR</constant></entry> + <entry>The entity represents a data conector</entry> + </row> + </tbody> + </tgroup> + </table> + + <table frame="none" pgwide="1" id="media-intf-type"> + <title>Media interface types</title> + <tgroup cols="3"> + <colspec colname="c1"/> + <colspec colname="c2"/> + <colspec colname="c3"/> + <tbody valign="top"> + <row> + <entry><constant>MEDIA_INTF_T_DVB_FE</constant></entry> + <entry>Device node interface for the Digital TV frontend</entry> + <entry>typically, /dev/dvb/adapter?/frontend?</entry> + </row> + <row> + <entry><constant>MEDIA_INTF_T_DVB_DEMUX</constant></entry> + <entry>Device node interface for the Digital TV demux</entry> + <entry>typically, /dev/dvb/adapter?/demux?</entry> + </row> + <row> + <entry><constant>MEDIA_INTF_T_DVB_DVR</constant></entry> + <entry>Device node interface for the Digital TV DVR</entry> + <entry>typically, /dev/dvb/adapter?/dvr?</entry> + </row> + <row> + <entry><constant>MEDIA_INTF_T_DVB_CA</constant></entry> + <entry>Device node interface for the Digital TV Conditional Access</entry> + <entry>typically, /dev/dvb/adapter?/ca?</entry> + </row> + <row> + <entry><constant>MEDIA_INTF_T_DVB_FE</constant></entry> + <entry>Device node interface for the Digital TV network control</entry> + <entry>typically, /dev/dvb/adapter?/net?</entry> + </row> + <row> + <entry><constant>MEDIA_INTF_T_V4L_VIDEO</constant></entry> + <entry>Device node interface for video (V4L)</entry> + <entry>typically, /dev/video?</entry> + </row> + <row> + <entry><constant>MEDIA_INTF_T_V4L_VBI</constant></entry> + <entry>Device node interface for VBI (V4L)</entry> + <entry>typically, /dev/vbi?</entry> + </row> + <row> + <entry><constant>MEDIA_INTF_T_V4L_RADIO</constant></entry> + <entry>Device node interface for radio (V4L)</entry> + <entry>typically, /dev/vbi?</entry> + </row> + <row> + <entry><constant>MEDIA_INTF_T_V4L_SUBDEV</constant></entry> + <entry>Device node interface for a V4L subdevice</entry> + <entry>typically, /dev/v4l-subdev?</entry> + </row> + <row> + <entry><constant>MEDIA_INTF_T_V4L_SWRADIO</constant></entry> + <entry>Device node interface for Software Defined Radio (V4L)</entry> + <entry>typically, /dev/swradio?</entry> + </row> + </tbody> + </tgroup> + </table> + + <table frame="none" pgwide="1" id="media-pad-flag"> + <title>Media pad flags</title> + <tgroup cols="2"> + <colspec colname="c1"/> + <colspec colname="c2"/> + <tbody valign="top"> + <row> + <entry><constant>MEDIA_PAD_FL_SINK</constant></entry> + <entry>Input pad, relative to the entity. Input pads sink data and + are targets of links.</entry> + </row> + <row> + <entry><constant>MEDIA_PAD_FL_SOURCE</constant></entry> + <entry>Output pad, relative to the entity. Output pads source data + and are origins of links.</entry> + </row> + <row> + <entry><constant>MEDIA_PAD_FL_MUST_CONNECT</constant></entry> + <entry>If this flag is set and the pad is linked to any other + pad, then at least one of those links must be enabled for the + entity to be able to stream. There could be temporary reasons + (e.g. device configuration dependent) for the pad to need + enabled links even when this flag isn't set; the absence of the + flag doesn't imply there is none.</entry> + </row> + </tbody> + </tgroup> + </table> + + <para>One and only one of <constant>MEDIA_PAD_FL_SINK</constant> and + <constant>MEDIA_PAD_FL_SOURCE</constant> must be set for every pad.</para> + + <table frame="none" pgwide="1" id="media-link-flag"> + <title>Media link flags</title> + <tgroup cols="2"> + <colspec colname="c1"/> + <colspec colname="c2"/> + <tbody valign="top"> + <row> + <entry><constant>MEDIA_LNK_FL_ENABLED</constant></entry> + <entry>The link is enabled and can be used to transfer media data. + When two or more links target a sink pad, only one of them can be + enabled at a time.</entry> + </row> + <row> + <entry><constant>MEDIA_LNK_FL_IMMUTABLE</constant></entry> + <entry>The link enabled state can't be modified at runtime. An + immutable link is always enabled.</entry> + </row> + <row> + <entry><constant>MEDIA_LNK_FL_DYNAMIC</constant></entry> + <entry>The link enabled state can be modified during streaming. This + flag is set by drivers and is read-only for applications.</entry> + </row> + <row> + <entry><constant>MEDIA_LNK_FL_LINK_TYPE</constant></entry> + <entry><para>This is a bitmask that defines the type of the link. + Currently, two types of links are supported:</para> + <para><constant>MEDIA_LNK_FL_DATA_LINK</constant> + if the link is between two pads</para> + <para><constant>MEDIA_LNK_FL_INTERFACE_LINK</constant> + if the link is between an interface and an entity</para></entry> + </row> + </tbody> + </tgroup> + </table> + +</section> diff --git a/Documentation/media-framework.txt b/Documentation/media-framework.txt deleted file mode 100644 index f552a75..0000000 --- a/Documentation/media-framework.txt +++ /dev/null @@ -1,372 +0,0 @@ -Linux kernel media framework -============================ - -This document describes the Linux kernel media framework, its data structures, -functions and their usage. - - -Introduction ------------- - -The media controller API is documented in DocBook format in -Documentation/DocBook/media/v4l/media-controller.xml. This document will 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 media_device instance in a -larger driver-specific structure. - -Drivers register media device instances by calling - - media_device_register(struct media_device *mdev); - -The caller is responsible for initializing the media_device structure before -registration. The following fields must be set: - - - dev must point to the parent device (usually a pci_dev, usb_interface or - platform_device instance). - - - model must be filled with the device model name as a NUL-terminated UTF-8 - string. The device/model revision must not be stored in this field. - -The following fields are optional: - - - serial is a unique serial number stored as a NUL-terminated ASCII string. - The field is big enough to store a GUID in text form. If the hardware - doesn't provide a unique serial number this field must be left empty. - - - bus_info represents the location of the device in the system as a - NUL-terminated ASCII string. For PCI/PCIe devices bus_info must be set to - "PCI:" (or "PCIe:") followed by the value of pci_name(). For USB devices, - the usb_make_path() function must be used. This field is used by - applications to distinguish between otherwise identical devices that don't - provide a serial number. - - - hw_revision is the hardware device revision in a driver-specific format. - When possible the revision should be formatted with the KERNEL_VERSION - macro. - - - driver_version is formatted with the KERNEL_VERSION macro. The version - minor must be incremented when new features are added to the userspace API - without breaking binary compatibility. The version major must be - incremented when binary compatibility is broken. - -Upon successful registration a character device named media[0-9]+ is created. -The device major and minor numbers are dynamic. The model name is exported as -a sysfs attribute. - -Drivers unregister media device instances by calling - - media_device_unregister(struct media_device *mdev); - -Unregistering a media device that hasn't been registered is *NOT* safe. - - -Entities, pads and links ------------------------- - -- 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 a v4l2_subdev or video_device instance, -although drivers can allocate entities directly. - -Drivers initialize entities by calling - - media_entity_init(struct media_entity *entity, u16 num_pads, - struct media_pad *pads, u16 extra_links); - -The media_entity name, type, flags, revision and group_id fields can be -initialized before or after calling media_entity_init. Entities embedded in -higher-level standard structures can have some of those fields set by the -higher-level framework. - -As the number of pads is known in advance, the pads array is not allocated -dynamically but is managed by the entity driver. Most drivers will embed the -pads array in a driver-specific structure, avoiding dynamic allocation. - -Drivers must set the direction of every pad in the pads array before calling -media_entity_init. The function will initialize the other pads fields. - -Unlike the number of pads, the total number of links isn't always known in -advance by the entity driver. As an initial estimate, media_entity_init -pre-allocates a number of links equal to the number of pads plus an optional -number of extra links. The links array will be reallocated if it grows beyond -the initial estimate. - -Drivers register entities with a media device by calling - - media_device_register_entity(struct media_device *mdev, - struct media_entity *entity); - -Entities are identified by a unique positive integer ID. Drivers can provide an -ID by filling the media_entity id field prior to registration, or request the -media controller framework to assign an ID automatically. Drivers that provide -IDs manually must ensure that all IDs are unique. IDs are not guaranteed to be -contiguous even when they are all assigned automatically by the framework. - -Drivers unregister entities by calling - - media_device_unregister_entity(struct media_entity *entity); - -Unregistering an entity will not change the IDs of the other entities, and the -ID will never be reused for a newly registered entity. - -When a media device is unregistered, all its entities are unregistered -automatically. No manual entities unregistration is then required. - -Drivers free resources associated with an entity by calling - - media_entity_cleanup(struct media_entity *entity); - -This function must be called during the cleanup phase after unregistering the -entity. Note that the media_entity instance itself must be freed explicitly by -the driver if required. - -Entities have flags that describe the entity capabilities and state. - - MEDIA_ENT_FL_DEFAULT indicates the default entity for a given type. - This can be used to report the default audio and video devices or the - default camera sensor. - -Logical entity groups can be defined by setting the group ID of all member -entities to the same non-zero value. An entity group serves no purpose in the -kernel, but is reported to userspace during entities enumeration. The group_id -field belongs to the media device driver and must not by touched by entity -drivers. - -Media device drivers should define groups if several entities are logically -bound together. Example usages include reporting - - - ALSA, VBI and video nodes that carry the same media stream - - lens and flash controllers associated with a sensor - -- 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 media_pad structure, making the 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. - -One and only one of MEDIA_PAD_FL_SINK and 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. Each entity stores all links originating at or -targeting any of its pads in a links array. A given link is thus stored -twice, once in the source entity and once in the target entity. The array is -pre-allocated and grows dynamically as needed. - -Drivers create links by calling - - media_entity_create_link(struct media_entity *source, u16 source_pad, - struct media_entity *sink, u16 sink_pad, - u32 flags); - -An entry in the link array of each entity is allocated and stores pointers -to source and sink pads. - -Links have flags that describe the link capabilities and state. - - MEDIA_LNK_FL_ENABLED indicates that the link is enabled and can be used - to transfer media data. When two or more links target a sink pad, only - one of them can be enabled at a time. - MEDIA_LNK_FL_IMMUTABLE indicates that the link enabled state can't be - modified at runtime. If MEDIA_LNK_FL_IMMUTABLE is set, then - MEDIA_LNK_FL_ENABLED must also be set since an immutable link is always - enabled. - - -Graph traversal ---------------- - -The media framework provides APIs to iterate over entities in a graph. - -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. - - struct media_entity *entity; - - media_device_for_each_entity(entity, mdev) { - /* entity will point to each entity in turn */ - ... - } - -Drivers might also need to iterate over all entities in a graph that can be -reached only through enabled links starting at a given entity. The media -framework provides a depth-first graph traversal API for that purpose. - -Note that graphs with cycles (whether directed or undirected) are *NOT* -supported by the graph traversal API. To prevent infinite loops, the graph -traversal code limits the maximum depth to MEDIA_ENTITY_ENUM_MAX_DEPTH, -currently defined as 16. - -Drivers initiate a graph traversal by calling - - media_entity_graph_walk_start(struct media_entity_graph *graph, - struct media_entity *entity); - -The graph structure, provided by the caller, is initialized to start graph -traversal at the given entity. - -Drivers can then retrieve the next entity by calling - - media_entity_graph_walk_next(struct media_entity_graph *graph); - -When the graph traversal is complete the function will return NULL. - -Graph traversal can be interrupted at any moment. No cleanup function call is -required and the graph structure can be freed normally. - -Helper functions can be used to find a link between two given pads, or a pad -connected to another pad through an enabled link - - media_entity_find_link(struct media_pad *source, - struct media_pad *sink); - - media_entity_remote_pad(struct media_pad *pad); - -Refer to the kerneldoc documentation for more information. - - -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 -media_entity structure includes a use_count field that media drivers can use to -track the number of users of every entity for power management needs. - -The 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 media device graph_mutex -lock. - - -Links setup ------------ - -Link properties can be modified at runtime by calling - - media_entity_setup_link(struct media_link *link, u32 flags); - -The flags argument contains the requested new link flags. - -The only configurable property is the ENABLED link flag to enable/disable a -link. Links marked with the IMMUTABLE link flag can not be enabled or disabled. - -When a link is enabled or disabled, the media framework calls the -link_setup operation for the two entities at the source and sink of the link, -in that order. If the second link_setup call fails, another link_setup call is -made on the first entity to restore the original link flags. - -Media device drivers can be notified of link setup operations by setting the -media_device::link_notify pointer to a callback function. If provided, the -notification callback will be called before enabling and after disabling -links. - -Entity drivers must implement the link_setup operation if any of their links -is non-immutable. The operation must either configure the hardware or store -the configuration information to be applied later. - -Link configuration must not have any side effect on other links. If an enabled -link at a sink pad prevents another link at the same pad from being enabled, -the link_setup operation must return -EBUSY and can't implicitly disable the -first enabled link. - - -Pipelines and media streams ---------------------------- - -When starting streaming, drivers must notify all entities in the pipeline to -prevent link states from being modified during streaming by calling - - media_entity_pipeline_start(struct media_entity *entity, - struct media_pipeline *pipe); - -The function will mark all entities connected to the given entity through -enabled links, either directly or indirectly, as streaming. - -The media_pipeline instance pointed to by the pipe argument will be stored in -every entity in the pipeline. Drivers should embed the media_pipeline structure -in higher-level pipeline structures and can then access the pipeline through -the media_entity pipe field. - -Calls to media_entity_pipeline_start() can be nested. The pipeline pointer must -be identical for all nested calls to the function. - -media_entity_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 - - media_entity_pipeline_stop(struct media_entity *entity); - -If multiple calls to media_entity_pipeline_start() have been made the same -number of media_entity_pipeline_stop() calls are required to stop streaming. The -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 media_entity_pipeline_start() for any -entity which has sink pads in the pipeline. The -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. diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt index 75d5c18..fa41608 100644 --- a/Documentation/video4linux/v4l2-framework.txt +++ b/Documentation/video4linux/v4l2-framework.txt @@ -295,16 +295,16 @@ module owner. This is done for you if you use the i2c helper functions. If integration with the media framework is needed, you must initialize the media_entity struct embedded in the v4l2_subdev struct (entity field) by -calling media_entity_init(): +calling media_entity_pads_init(), if the entity has pads: struct media_pad *pads = &my_sd->pads; int err; - err = media_entity_init(&sd->entity, npads, pads, 0); + err = media_entity_pads_init(&sd->entity, npads, pads); The pads array must have been previously initialized. There is no need to -manually set the struct media_entity type and name fields, but the revision -field must be initialized if needed. +manually set the struct media_entity function and name fields, but the +revision field must be initialized if needed. A reference to the entity will be automatically acquired/released when the subdev device node (if any) is opened/closed. @@ -695,12 +695,12 @@ difference is that the inode argument is omitted since it is never used. If integration with the media framework is needed, you must initialize the media_entity struct embedded in the video_device struct (entity field) by -calling media_entity_init(): +calling media_entity_pads_init(): struct media_pad *pad = &my_vdev->pad; int err; - err = media_entity_init(&vdev->entity, 1, pad, 0); + err = media_entity_pads_init(&vdev->entity, 1, pad); The pads array must have been previously initialized. There is no need to manually set the struct media_entity type and name fields. diff --git a/Documentation/zh_CN/video4linux/v4l2-framework.txt b/Documentation/zh_CN/video4linux/v4l2-framework.txt index 2b828e6..698660b 100644 --- a/Documentation/zh_CN/video4linux/v4l2-framework.txt +++ b/Documentation/zh_CN/video4linux/v4l2-framework.txt @@ -289,13 +289,13 @@ struct v4l2_subdev_ops { 然后,你必须用一个唯一的名字初始化 subdev->name,并初始化模块的 owner 域。若使用 i2c 辅助函数,这些都会帮你处理好。 -若需同媒体框架整合,你必须调用 media_entity_init() 初始化 v4l2_subdev +若需同媒体框架整合,你必须调用 media_entity_pads_init() 初始化 v4l2_subdev 结构体中的 media_entity 结构体(entity 域): struct media_pad *pads = &my_sd->pads; int err; - err = media_entity_init(&sd->entity, npads, pads, 0); + err = media_entity_pads_init(&sd->entity, npads, pads); pads 数组必须预先初始化。无须手动设置 media_entity 的 type 和 name 域,但如有必要,revision 域必须初始化。 @@ -596,13 +596,13 @@ void v4l2_disable_ioctl(struct video_device *vdev, unsigned int cmd); v4l2_file_operations 结构体是 file_operations 的一个子集。其主要 区别在于:因 inode 参数从未被使用,它将被忽略。 -如果需要与媒体框架整合,你必须通过调用 media_entity_init() 初始化 +如果需要与媒体框架整合,你必须通过调用 media_entity_pads_init() 初始化 嵌入在 video_device 结构体中的 media_entity(entity 域)结构体: struct media_pad *pad = &my_vdev->pad; int err; - err = media_entity_init(&vdev->entity, 1, pad, 0); + err = media_entity_pads_init(&vdev->entity, 1, pad); pads 数组必须预先初始化。没有必要手动设置 media_entity 的 type 和 name 域。 diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig index 9264ea7..a8518fb 100644 --- a/drivers/media/Kconfig +++ b/drivers/media/Kconfig @@ -97,7 +97,6 @@ config MEDIA_CONTROLLER config MEDIA_CONTROLLER_DVB bool "Enable Media controller for DVB (EXPERIMENTAL)" depends on MEDIA_CONTROLLER - depends on BROKEN ---help--- Enable the media controller API support for DVB. diff --git a/drivers/media/common/siano/smsdvb-main.c b/drivers/media/common/siano/smsdvb-main.c index f4305ae..d31f468 100644 --- a/drivers/media/common/siano/smsdvb-main.c +++ b/drivers/media/common/siano/smsdvb-main.c @@ -617,6 +617,7 @@ static void smsdvb_media_device_unregister(struct smsdvb_client_t *client) if (!coredev->media_dev) return; media_device_unregister(coredev->media_dev); + media_device_cleanup(coredev->media_dev); kfree(coredev->media_dev); coredev->media_dev = NULL; #endif @@ -1183,7 +1184,11 @@ static int smsdvb_hotplug(struct smscore_device_t *coredev, if (smsdvb_debugfs_create(client) < 0) pr_info("failed to create debugfs node\n"); - dvb_create_media_graph(&client->adapter); + rc = dvb_create_media_graph(&client->adapter, true); + if (rc < 0) { + pr_err("dvb_create_media_graph failed %d\n", rc); + goto client_error; + } pr_info("DVB interface registered.\n"); return 0; diff --git a/drivers/media/dvb-core/dmxdev.c b/drivers/media/dvb-core/dmxdev.c index ea9abde..a168cbe 100644 --- a/drivers/media/dvb-core/dmxdev.c +++ b/drivers/media/dvb-core/dmxdev.c @@ -1244,9 +1244,9 @@ int dvb_dmxdev_init(struct dmxdev *dmxdev, struct dvb_adapter *dvb_adapter) } dvb_register_device(dvb_adapter, &dmxdev->dvbdev, &dvbdev_demux, dmxdev, - DVB_DEVICE_DEMUX); + DVB_DEVICE_DEMUX, dmxdev->filternum); dvb_register_device(dvb_adapter, &dmxdev->dvr_dvbdev, &dvbdev_dvr, - dmxdev, DVB_DEVICE_DVR); + dmxdev, DVB_DEVICE_DVR, dmxdev->filternum); dvb_ringbuffer_init(&dmxdev->dvr_buffer, NULL, 8192); diff --git a/drivers/media/dvb-core/dvb_ca_en50221.c b/drivers/media/dvb-core/dvb_ca_en50221.c index fb66184..f82cd1f 100644 --- a/drivers/media/dvb-core/dvb_ca_en50221.c +++ b/drivers/media/dvb-core/dvb_ca_en50221.c @@ -1695,7 +1695,7 @@ int dvb_ca_en50221_init(struct dvb_adapter *dvb_adapter, pubca->private = ca; /* register the DVB device */ - ret = dvb_register_device(dvb_adapter, &ca->dvbdev, &dvbdev_ca, ca, DVB_DEVICE_CA); + ret = dvb_register_device(dvb_adapter, &ca->dvbdev, &dvbdev_ca, ca, DVB_DEVICE_CA, 0); if (ret) goto free_slot_info; diff --git a/drivers/media/dvb-core/dvb_frontend.c b/drivers/media/dvb-core/dvb_frontend.c index b64f337..4008064 100644 --- a/drivers/media/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb-core/dvb_frontend.c @@ -622,7 +622,7 @@ static int dvb_enable_media_tuner(struct dvb_frontend *fe) struct media_device *mdev = adapter->mdev; struct media_entity *entity, *source; struct media_link *link, *found_link = NULL; - int i, ret, n_links = 0, active_links = 0; + int ret, n_links = 0, active_links = 0; fepriv->pipe_start_entity = NULL; @@ -632,8 +632,7 @@ static int dvb_enable_media_tuner(struct dvb_frontend *fe) entity = fepriv->dvbdev->entity; fepriv->pipe_start_entity = entity; - for (i = 0; i < entity->num_links; i++) { - link = &entity->links[i]; + list_for_each_entry(link, &entity->links, list) { if (link->sink->entity == entity) { found_link = link; n_links++; @@ -659,13 +658,11 @@ static int dvb_enable_media_tuner(struct dvb_frontend *fe) source = found_link->source->entity; fepriv->pipe_start_entity = source; - for (i = 0; i < source->num_links; i++) { + list_for_each_entry(link, &source->links, list) { struct media_entity *sink; int flags = 0; - link = &source->links[i]; sink = link->sink->entity; - if (sink == entity) flags = MEDIA_LNK_FL_ENABLED; @@ -2762,7 +2759,7 @@ int dvb_register_frontend(struct dvb_adapter* dvb, fe->dvb->num, fe->id, fe->ops.info.name); dvb_register_device (fe->dvb, &fepriv->dvbdev, &dvbdev_template, - fe, DVB_DEVICE_FRONTEND); + fe, DVB_DEVICE_FRONTEND, 0); /* * Initialize the cache to the proper values according with the diff --git a/drivers/media/dvb-core/dvb_net.c b/drivers/media/dvb-core/dvb_net.c index ce4332e..ce6a711 100644 --- a/drivers/media/dvb-core/dvb_net.c +++ b/drivers/media/dvb-core/dvb_net.c @@ -1502,6 +1502,6 @@ int dvb_net_init (struct dvb_adapter *adap, struct dvb_net *dvbnet, dvbnet->state[i] = 0; return dvb_register_device(adap, &dvbnet->dvbdev, &dvbdev_net, - dvbnet, DVB_DEVICE_NET); + dvbnet, DVB_DEVICE_NET, 0); } EXPORT_SYMBOL(dvb_net_init); diff --git a/drivers/media/dvb-core/dvbdev.c b/drivers/media/dvb-core/dvbdev.c index 13bb57f..560450a 100644 --- a/drivers/media/dvb-core/dvbdev.c +++ b/drivers/media/dvb-core/dvbdev.c @@ -34,6 +34,9 @@ #include <linux/mutex.h> #include "dvbdev.h" +/* Due to enum tuner_pad_index */ +#include <media/tuner.h> + static DEFINE_MUTEX(dvbdev_mutex); static int dvbdev_debug; @@ -180,102 +183,255 @@ skip: return -ENFILE; } -static void dvb_register_media_device(struct dvb_device *dvbdev, - int type, int minor) +static void dvb_media_device_free(struct dvb_device *dvbdev) { #if defined(CONFIG_MEDIA_CONTROLLER_DVB) - int ret = 0, npads; + if (dvbdev->entity) { + media_device_unregister_entity(dvbdev->entity); + kfree(dvbdev->entity); + kfree(dvbdev->pads); + dvbdev->entity = NULL; + dvbdev->pads = NULL; + } - if (!dvbdev->adapter->mdev) - return; + if (dvbdev->tsout_entity) { + int i; - dvbdev->entity = kzalloc(sizeof(*dvbdev->entity), GFP_KERNEL); - if (!dvbdev->entity) - return; + for (i = 0; i < dvbdev->tsout_num_entities; i++) { + media_device_unregister_entity(&dvbdev->tsout_entity[i]); + kfree(dvbdev->tsout_entity[i].name); + } + kfree(dvbdev->tsout_entity); + kfree(dvbdev->tsout_pads); + dvbdev->tsout_entity = NULL; + dvbdev->tsout_pads = NULL; - dvbdev->entity->info.dev.major = DVB_MAJOR; - dvbdev->entity->info.dev.minor = minor; - dvbdev->entity->name = dvbdev->name; + dvbdev->tsout_num_entities = 0; + } + + if (dvbdev->intf_devnode) { + media_devnode_remove(dvbdev->intf_devnode); + dvbdev->intf_devnode = NULL; + } + + if (dvbdev->adapter->conn) { + media_device_unregister_entity(dvbdev->adapter->conn); + dvbdev->adapter->conn = NULL; + kfree(dvbdev->adapter->conn_pads); + dvbdev->adapter->conn_pads = NULL; + } +#endif +} + +#if defined(CONFIG_MEDIA_CONTROLLER_DVB) +static int dvb_create_tsout_entity(struct dvb_device *dvbdev, + const char *name, int npads) +{ + int i, ret = 0; + + dvbdev->tsout_pads = kcalloc(npads, sizeof(*dvbdev->tsout_pads), + GFP_KERNEL); + if (!dvbdev->tsout_pads) + return -ENOMEM; + + dvbdev->tsout_entity = kcalloc(npads, sizeof(*dvbdev->tsout_entity), + GFP_KERNEL); + if (!dvbdev->tsout_entity) + return -ENOMEM; + + dvbdev->tsout_num_entities = npads; + + for (i = 0; i < npads; i++) { + struct media_pad *pads = &dvbdev->tsout_pads[i]; + struct media_entity *entity = &dvbdev->tsout_entity[i]; + + entity->name = kasprintf(GFP_KERNEL, "%s #%d", name, i); + if (!entity->name) + return -ENOMEM; + + entity->function = MEDIA_ENT_F_IO_DTV; + pads->flags = MEDIA_PAD_FL_SINK; + + ret = media_entity_pads_init(entity, 1, pads); + if (ret < 0) + return ret; + + ret = media_device_register_entity(dvbdev->adapter->mdev, + entity); + if (ret < 0) + return ret; + } + return 0; +} + +#define DEMUX_TSOUT "demux-tsout" +#define DVR_TSOUT "dvr-tsout" + +static int dvb_create_media_entity(struct dvb_device *dvbdev, + int type, int demux_sink_pads) +{ + int i, ret, npads; switch (type) { - case DVB_DEVICE_CA: - case DVB_DEVICE_DEMUX: case DVB_DEVICE_FRONTEND: npads = 2; break; - case DVB_DEVICE_NET: - npads = 0; + case DVB_DEVICE_DVR: + ret = dvb_create_tsout_entity(dvbdev, DVR_TSOUT, + demux_sink_pads); + return ret; + case DVB_DEVICE_DEMUX: + npads = 1 + demux_sink_pads; + ret = dvb_create_tsout_entity(dvbdev, DEMUX_TSOUT, + demux_sink_pads); + if (ret < 0) + return ret; + break; + case DVB_DEVICE_CA: + npads = 2; break; + case DVB_DEVICE_NET: + /* + * We should be creating entities for the MPE/ULE + * decapsulation hardware (or software implementation). + * + * However, the number of for the MPE/ULE decaps may not be + * fixed. As we don't have yet dynamic support for PADs at + * the Media Controller, let's not create the decap + * entities yet. + */ + return 0; default: - npads = 1; + return 0; } + dvbdev->entity = kzalloc(sizeof(*dvbdev->entity), GFP_KERNEL); + if (!dvbdev->entity) + return -ENOMEM; + + dvbdev->entity->name = dvbdev->name; + if (npads) { dvbdev->pads = kcalloc(npads, sizeof(*dvbdev->pads), GFP_KERNEL); - if (!dvbdev->pads) { - kfree(dvbdev->entity); - return; - } + if (!dvbdev->pads) + return -ENOMEM; } switch (type) { case DVB_DEVICE_FRONTEND: - dvbdev->entity->type = MEDIA_ENT_T_DEVNODE_DVB_FE; + dvbdev->entity->function = MEDIA_ENT_F_DTV_DEMOD; dvbdev->pads[0].flags = MEDIA_PAD_FL_SINK; dvbdev->pads[1].flags = MEDIA_PAD_FL_SOURCE; break; case DVB_DEVICE_DEMUX: - dvbdev->entity->type = MEDIA_ENT_T_DEVNODE_DVB_DEMUX; - dvbdev->pads[0].flags = MEDIA_PAD_FL_SINK; - dvbdev->pads[1].flags = MEDIA_PAD_FL_SOURCE; - break; - case DVB_DEVICE_DVR: - dvbdev->entity->type = MEDIA_ENT_T_DEVNODE_DVB_DVR; + dvbdev->entity->function = MEDIA_ENT_F_TS_DEMUX; dvbdev->pads[0].flags = MEDIA_PAD_FL_SINK; + for (i = 1; i < npads; i++) + dvbdev->pads[i].flags = MEDIA_PAD_FL_SOURCE; break; case DVB_DEVICE_CA: - dvbdev->entity->type = MEDIA_ENT_T_DEVNODE_DVB_CA; + dvbdev->entity->function = MEDIA_ENT_F_DTV_CA; dvbdev->pads[0].flags = MEDIA_PAD_FL_SINK; dvbdev->pads[1].flags = MEDIA_PAD_FL_SOURCE; break; - case DVB_DEVICE_NET: - dvbdev->entity->type = MEDIA_ENT_T_DEVNODE_DVB_NET; - break; default: + /* Should never happen, as the first switch prevents it */ kfree(dvbdev->entity); + kfree(dvbdev->pads); dvbdev->entity = NULL; - return; + dvbdev->pads = NULL; + return 0; } - if (npads) - ret = media_entity_init(dvbdev->entity, npads, dvbdev->pads, 0); - if (!ret) - ret = media_device_register_entity(dvbdev->adapter->mdev, - dvbdev->entity); - if (ret < 0) { - printk(KERN_ERR - "%s: media_device_register_entity failed for %s\n", - __func__, dvbdev->entity->name); - kfree(dvbdev->pads); - kfree(dvbdev->entity); - dvbdev->entity = NULL; - return; + if (npads) { + ret = media_entity_pads_init(dvbdev->entity, npads, dvbdev->pads); + if (ret) + return ret; } + ret = media_device_register_entity(dvbdev->adapter->mdev, + dvbdev->entity); + if (ret) + return (ret); - printk(KERN_DEBUG "%s: media device '%s' registered.\n", + printk(KERN_DEBUG "%s: media entity '%s' registered.\n", __func__, dvbdev->entity->name); + + return 0; +} +#endif + +static int dvb_register_media_device(struct dvb_device *dvbdev, + int type, int minor, + unsigned demux_sink_pads) +{ +#if defined(CONFIG_MEDIA_CONTROLLER_DVB) + struct media_link *link; + u32 intf_type; + int ret; + + if (!dvbdev->adapter->mdev) + return 0; + + ret = dvb_create_media_entity(dvbdev, type, demux_sink_pads); + if (ret) + return ret; + + switch (type) { + case DVB_DEVICE_FRONTEND: + intf_type = MEDIA_INTF_T_DVB_FE; + break; + case DVB_DEVICE_DEMUX: + intf_type = MEDIA_INTF_T_DVB_DEMUX; + break; + case DVB_DEVICE_DVR: + intf_type = MEDIA_INTF_T_DVB_DVR; + break; + case DVB_DEVICE_CA: + intf_type = MEDIA_INTF_T_DVB_CA; + break; + case DVB_DEVICE_NET: + intf_type = MEDIA_INTF_T_DVB_NET; + break; + default: + return 0; + } + + dvbdev->intf_devnode = media_devnode_create(dvbdev->adapter->mdev, + intf_type, 0, + DVB_MAJOR, minor); + + if (!dvbdev->intf_devnode) + return -ENOMEM; + + /* + * Create the "obvious" link, e. g. the ones that represent + * a direct association between an interface and an entity. + * Other links should be created elsewhere, like: + * DVB FE intf -> tuner + * DVB demux intf -> dvr + */ + + if (!dvbdev->entity) + return 0; + + link = media_create_intf_link(dvbdev->entity, &dvbdev->intf_devnode->intf, + MEDIA_LNK_FL_ENABLED); + if (!link) + return -ENOMEM; #endif + return 0; } int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev, - const struct dvb_device *template, void *priv, int type) + const struct dvb_device *template, void *priv, int type, + int demux_sink_pads) { struct dvb_device *dvbdev; struct file_operations *dvbdevfops; struct device *clsdev; int minor; - int id; + int id, ret; mutex_lock(&dvbdev_register_lock); @@ -286,7 +442,7 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev, return -ENFILE; } - *pdvbdev = dvbdev = kmalloc(sizeof(struct dvb_device), GFP_KERNEL); + *pdvbdev = dvbdev = kzalloc(sizeof(*dvbdev), GFP_KERNEL); if (!dvbdev){ mutex_unlock(&dvbdev_register_lock); @@ -335,6 +491,20 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev, dvb_minors[minor] = dvbdev; up_write(&minor_rwsem); + ret = dvb_register_media_device(dvbdev, type, minor, demux_sink_pads); + if (ret) { + printk(KERN_ERR + "%s: dvb_register_media_device failed to create the mediagraph\n", + __func__); + + dvb_media_device_free(dvbdev); + kfree(dvbdevfops); + kfree(dvbdev); + up_write(&minor_rwsem); + mutex_unlock(&dvbdev_register_lock); + return ret; + } + mutex_unlock(&dvbdev_register_lock); clsdev = device_create(dvb_class, adap->device, @@ -348,8 +518,6 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev, dprintk(KERN_DEBUG "DVB: register adapter%d/%s%d @ minor: %i (0x%02x)\n", adap->num, dnames[type], id, minor, minor); - dvb_register_media_device(dvbdev, type, minor); - return 0; } EXPORT_SYMBOL(dvb_register_device); @@ -364,15 +532,9 @@ void dvb_unregister_device(struct dvb_device *dvbdev) dvb_minors[dvbdev->minor] = NULL; up_write(&minor_rwsem); - device_destroy(dvb_class, MKDEV(DVB_MAJOR, dvbdev->minor)); + dvb_media_device_free(dvbdev); -#if defined(CONFIG_MEDIA_CONTROLLER_DVB) - if (dvbdev->entity) { - media_device_unregister_entity(dvbdev->entity); - kfree(dvbdev->entity); - kfree(dvbdev->pads); - } -#endif + device_destroy(dvb_class, MKDEV(DVB_MAJOR, dvbdev->minor)); list_del (&dvbdev->list_head); kfree (dvbdev->fops); @@ -382,46 +544,212 @@ EXPORT_SYMBOL(dvb_unregister_device); #ifdef CONFIG_MEDIA_CONTROLLER_DVB -void dvb_create_media_graph(struct dvb_adapter *adap) + +static int dvb_create_io_intf_links(struct dvb_adapter *adap, + struct media_interface *intf, + char *name) +{ + struct media_device *mdev = adap->mdev; + struct media_entity *entity; + struct media_link *link; + + media_device_for_each_entity(entity, mdev) { + if (entity->function == MEDIA_ENT_F_IO_DTV) { + if (strncmp(entity->name, name, strlen(name))) + continue; + link = media_create_intf_link(entity, intf, + MEDIA_LNK_FL_ENABLED); + if (!link) + return -ENOMEM; + } + } + return 0; +} + +int dvb_create_media_graph(struct dvb_adapter *adap, + bool create_rf_connector) { struct media_device *mdev = adap->mdev; - struct media_entity *entity, *tuner = NULL, *fe = NULL; - struct media_entity *demux = NULL, *dvr = NULL, *ca = NULL; + struct media_entity *entity, *tuner = NULL, *demod = NULL, *conn; + struct media_entity *demux = NULL, *ca = NULL; + struct media_link *link; + struct media_interface *intf; + unsigned demux_pad = 0; + unsigned dvr_pad = 0; + unsigned ntuner = 0, ndemod = 0; + int ret; + static const char *connector_name = "Television"; if (!mdev) - return; + return 0; media_device_for_each_entity(entity, mdev) { - switch (entity->type) { - case MEDIA_ENT_T_V4L2_SUBDEV_TUNER: + switch (entity->function) { + case MEDIA_ENT_F_TUNER: tuner = entity; + ntuner++; break; - case MEDIA_ENT_T_DEVNODE_DVB_FE: - fe = entity; + case MEDIA_ENT_F_DTV_DEMOD: + demod = entity; + ndemod++; break; - case MEDIA_ENT_T_DEVNODE_DVB_DEMUX: + case MEDIA_ENT_F_TS_DEMUX: demux = entity; break; - case MEDIA_ENT_T_DEVNODE_DVB_DVR: - dvr = entity; - break; - case MEDIA_ENT_T_DEVNODE_DVB_CA: + case MEDIA_ENT_F_DTV_CA: ca = entity; break; } } - if (tuner && fe) - media_entity_create_link(tuner, 0, fe, 0, 0); + /* + * Prepare to signalize to media_create_pad_links() that multiple + * entities of the same type exists and a 1:n or n:1 links need to be + * created. + * NOTE: if both tuner and demod have multiple instances, it is up + * to the caller driver to create such links. + */ + if (ntuner > 1) + tuner = NULL; + if (ndemod > 1) + demod = NULL; + + if (create_rf_connector) { + conn = kzalloc(sizeof(*conn), GFP_KERNEL); + if (!conn) + return -ENOMEM; + adap->conn = conn; + + adap->conn_pads = kcalloc(1, sizeof(*adap->conn_pads), + GFP_KERNEL); + if (!adap->conn_pads) + return -ENOMEM; + + conn->flags = MEDIA_ENT_FL_CONNECTOR; + conn->function = MEDIA_ENT_F_CONN_RF; + conn->name = connector_name; + adap->conn_pads->flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(conn, 1, adap->conn_pads); + if (ret) + return ret; + + ret = media_device_register_entity(mdev, conn); + if (ret) + return ret; + + if (!ntuner) + ret = media_create_pad_links(mdev, + MEDIA_ENT_F_CONN_RF, + conn, 0, + MEDIA_ENT_F_DTV_DEMOD, + demod, 0, + MEDIA_LNK_FL_ENABLED, + false); + else + ret = media_create_pad_links(mdev, + MEDIA_ENT_F_CONN_RF, + conn, 0, + MEDIA_ENT_F_TUNER, + tuner, TUNER_PAD_RF_INPUT, + MEDIA_LNK_FL_ENABLED, + false); + if (ret) + return ret; + } + + if (ntuner && ndemod) { + ret = media_create_pad_links(mdev, + MEDIA_ENT_F_TUNER, + tuner, TUNER_PAD_IF_OUTPUT, + MEDIA_ENT_F_DTV_DEMOD, + demod, 0, MEDIA_LNK_FL_ENABLED, + false); + if (ret) + return ret; + } + + if (ndemod && demux) { + ret = media_create_pad_links(mdev, + MEDIA_ENT_F_DTV_DEMOD, + demod, 1, + MEDIA_ENT_F_TS_DEMUX, + demux, 0, MEDIA_LNK_FL_ENABLED, + false); + if (ret) + return -ENOMEM; + } + if (demux && ca) { + ret = media_create_pad_link(demux, 1, ca, + 0, MEDIA_LNK_FL_ENABLED); + if (!ret) + return -ENOMEM; + } - if (fe && demux) - media_entity_create_link(fe, 1, demux, 0, MEDIA_LNK_FL_ENABLED); + /* Create demux links for each ringbuffer/pad */ + if (demux) { + media_device_for_each_entity(entity, mdev) { + if (entity->function == MEDIA_ENT_F_IO_DTV) { + if (!strncmp(entity->name, DVR_TSOUT, + strlen(DVR_TSOUT))) { + ret = media_create_pad_link(demux, + ++dvr_pad, + entity, 0, 0); + if (ret) + return ret; + } + if (!strncmp(entity->name, DEMUX_TSOUT, + strlen(DEMUX_TSOUT))) { + ret = media_create_pad_link(demux, + ++demux_pad, + entity, 0, 0); + if (ret) + return ret; + } + } + } + } - if (demux && dvr) - media_entity_create_link(demux, 1, dvr, 0, MEDIA_LNK_FL_ENABLED); + /* Create interface links for FE->tuner, DVR->demux and CA->ca */ + media_device_for_each_intf(intf, mdev) { + if (intf->type == MEDIA_INTF_T_DVB_CA && ca) { + link = media_create_intf_link(ca, intf, + MEDIA_LNK_FL_ENABLED); + if (!link) + return -ENOMEM; + } - if (demux && ca) - media_entity_create_link(demux, 1, ca, 0, MEDIA_LNK_FL_ENABLED); + if (intf->type == MEDIA_INTF_T_DVB_FE && tuner) { + link = media_create_intf_link(tuner, intf, + MEDIA_LNK_FL_ENABLED); + if (!link) + return -ENOMEM; + } +#if 0 + /* + * Indirect link - let's not create yet, as we don't know how + * to handle indirect links, nor if this will + * actually be needed. + */ + if (intf->type == MEDIA_INTF_T_DVB_DVR && demux) { + link = media_create_intf_link(demux, intf, + MEDIA_LNK_FL_ENABLED); + if (!link) + return -ENOMEM; + } +#endif + if (intf->type == MEDIA_INTF_T_DVB_DVR) { + ret = dvb_create_io_intf_links(adap, intf, DVR_TSOUT); + if (ret) + return ret; + } + if (intf->type == MEDIA_INTF_T_DVB_DEMUX) { + ret = dvb_create_io_intf_links(adap, intf, DEMUX_TSOUT); + if (ret) + return ret; + } + } + return 0; } EXPORT_SYMBOL_GPL(dvb_create_media_graph); #endif diff --git a/drivers/media/dvb-core/dvbdev.h b/drivers/media/dvb-core/dvbdev.h index 1069a77..4aff7bd 100644 --- a/drivers/media/dvb-core/dvbdev.h +++ b/drivers/media/dvb-core/dvbdev.h @@ -75,6 +75,9 @@ struct dvb_frontend; * used. * @mdev: pointer to struct media_device, used when the media * controller is used. + * @conn: RF connector. Used only if the device has no separate + * tuner. + * @conn_pads: pointer to struct media_pad associated with @conn; */ struct dvb_adapter { int num; @@ -94,6 +97,8 @@ struct dvb_adapter { #if defined(CONFIG_MEDIA_CONTROLLER_DVB) struct media_device *mdev; + struct media_entity *conn; + struct media_pad *conn_pads; #endif }; @@ -120,6 +125,11 @@ struct dvb_adapter { * @entity: pointer to struct media_entity associated with the device node * @pads: pointer to struct media_pad associated with @entity; * @priv: private data + * @intf_devnode: Pointer to media_intf_devnode. Used by the dvbdev core to + * store the MC device node interface + * @tsout_num_entities: Number of Transport Stream output entities + * @tsout_entity: array with MC entities associated to each TS output node + * @tsout_pads: array with the source pads for each @tsout_entity * * This structure is used by the DVB core (frontend, CA, net, demux) in * order to create the device nodes. Usually, driver should not initialize @@ -148,8 +158,11 @@ struct dvb_device { const char *name; /* Allocated and filled inside dvbdev.c */ - struct media_entity *entity; - struct media_pad *pads; + struct media_intf_devnode *intf_devnode; + + unsigned tsout_num_entities; + struct media_entity *entity, *tsout_entity; + struct media_pad *pads, *tsout_pads; #endif void *priv; @@ -185,14 +198,18 @@ int dvb_unregister_adapter(struct dvb_adapter *adap); * stored * @template: Template used to create &pdvbdev; * @priv: private data - * @type: type of the device: DVB_DEVICE_SEC, DVB_DEVICE_FRONTEND, - * DVB_DEVICE_DEMUX, DVB_DEVICE_DVR, DVB_DEVICE_CA, DVB_DEVICE_NET + * @type: type of the device: %DVB_DEVICE_SEC, %DVB_DEVICE_FRONTEND, + * %DVB_DEVICE_DEMUX, %DVB_DEVICE_DVR, %DVB_DEVICE_CA, + * %DVB_DEVICE_NET + * @demux_sink_pads: Number of demux outputs, to be used to create the TS + * outputs via the Media Controller. */ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev, const struct dvb_device *template, void *priv, - int type); + int type, + int demux_sink_pads); /** * dvb_unregister_device - Unregisters a DVB device @@ -202,16 +219,43 @@ int dvb_register_device(struct dvb_adapter *adap, void dvb_unregister_device(struct dvb_device *dvbdev); #ifdef CONFIG_MEDIA_CONTROLLER_DVB -void dvb_create_media_graph(struct dvb_adapter *adap); +/** + * dvb_create_media_graph - Creates media graph for the Digital TV part of the + * device. + * + * @adap: pointer to struct dvb_adapter + * @create_rf_connector: if true, it creates the RF connector too + * + * This function checks all DVB-related functions at the media controller + * entities and creates the needed links for the media graph. It is + * capable of working with multiple tuners or multiple frontends, but it + * won't create links if the device has multiple tuners and multiple frontends + * or if the device has multiple muxes. In such case, the caller driver should + * manually create the remaining links. + */ +__must_check int dvb_create_media_graph(struct dvb_adapter *adap, + bool create_rf_connector); + static inline void dvb_register_media_controller(struct dvb_adapter *adap, struct media_device *mdev) { adap->mdev = mdev; } +static inline struct media_device +*dvb_get_media_controller(struct dvb_adapter *adap) +{ + return adap->mdev; +} #else -static inline void dvb_create_media_graph(struct dvb_adapter *adap) {} +static inline +int dvb_create_media_graph(struct dvb_adapter *adap, + bool create_rf_connector) +{ + return 0; +}; #define dvb_register_media_controller(a, b) {} +#define dvb_get_media_controller(a) NULL #endif int dvb_generic_open (struct inode *inode, struct file *file); diff --git a/drivers/media/dvb-frontends/au8522_decoder.c b/drivers/media/dvb-frontends/au8522_decoder.c index c8f13d8..73612c5 100644 --- a/drivers/media/dvb-frontends/au8522_decoder.c +++ b/drivers/media/dvb-frontends/au8522_decoder.c @@ -730,6 +730,9 @@ static int au8522_probe(struct i2c_client *client, struct v4l2_ctrl_handler *hdl; struct v4l2_subdev *sd; int instance; +#ifdef CONFIG_MEDIA_CONTROLLER + int ret; +#endif /* Check if the adapter supports the needed features */ if (!i2c_check_functionality(client->adapter, @@ -758,6 +761,20 @@ static int au8522_probe(struct i2c_client *client, sd = &state->sd; v4l2_i2c_subdev_init(sd, client, &au8522_ops); +#if defined(CONFIG_MEDIA_CONTROLLER) + + state->pads[AU8522_PAD_INPUT].flags = MEDIA_PAD_FL_SINK; + state->pads[AU8522_PAD_VID_OUT].flags = MEDIA_PAD_FL_SOURCE; + state->pads[AU8522_PAD_VBI_OUT].flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_ATV_DECODER; + + ret = media_entity_pads_init(&sd->entity, ARRAY_SIZE(state->pads), + state->pads); + if (ret < 0) { + v4l_info(client, "failed to initialize media entity!\n"); + return ret; + } +#endif hdl = &state->hdl; v4l2_ctrl_handler_init(hdl, 4); diff --git a/drivers/media/dvb-frontends/au8522_priv.h b/drivers/media/dvb-frontends/au8522_priv.h index ee330c6..404a0cb 100644 --- a/drivers/media/dvb-frontends/au8522_priv.h +++ b/drivers/media/dvb-frontends/au8522_priv.h @@ -39,6 +39,14 @@ #define AU8522_DIGITAL_MODE 1 #define AU8522_SUSPEND_MODE 2 +enum au8522_media_pads { + AU8522_PAD_INPUT, + AU8522_PAD_VID_OUT, + AU8522_PAD_VBI_OUT, + + AU8522_NUM_PADS +}; + struct au8522_state { struct i2c_client *c; struct i2c_adapter *i2c; @@ -68,6 +76,10 @@ struct au8522_state { u32 id; u32 rev; struct v4l2_ctrl_handler hdl; + +#ifdef CONFIG_MEDIA_CONTROLLER + struct media_pad pads[AU8522_NUM_PADS]; +#endif }; /* These are routines shared by both the VSB/QAM demodulator and the analog diff --git a/drivers/media/firewire/firedtv-ci.c b/drivers/media/firewire/firedtv-ci.c index e63f582..edbb30f 100644 --- a/drivers/media/firewire/firedtv-ci.c +++ b/drivers/media/firewire/firedtv-ci.c @@ -241,7 +241,7 @@ int fdtv_ca_register(struct firedtv *fdtv) return -EFAULT; err = dvb_register_device(&fdtv->adapter, &fdtv->cadev, - &fdtv_ca, fdtv, DVB_DEVICE_CA); + &fdtv_ca, fdtv, DVB_DEVICE_CA, 0); if (stat.ca_application_info == 0) dev_err(fdtv->device, "CaApplicationInfo is not set\n"); diff --git a/drivers/media/i2c/ad9389b.c b/drivers/media/i2c/ad9389b.c index 0494a78..788967d 100644 --- a/drivers/media/i2c/ad9389b.c +++ b/drivers/media/i2c/ad9389b.c @@ -1158,7 +1158,7 @@ static int ad9389b_probe(struct i2c_client *client, const struct i2c_device_id * state->rgb_quantization_range_ctrl->is_private = true; state->pad.flags = MEDIA_PAD_FL_SINK; - err = media_entity_init(&sd->entity, 1, &state->pad, 0); + err = media_entity_pads_init(&sd->entity, 1, &state->pad); if (err) goto err_hdl; diff --git a/drivers/media/i2c/adp1653.c b/drivers/media/i2c/adp1653.c index f00745b..7e9cbf7 100644 --- a/drivers/media/i2c/adp1653.c +++ b/drivers/media/i2c/adp1653.c @@ -512,11 +512,11 @@ static int adp1653_probe(struct i2c_client *client, if (ret) goto free_and_quit; - ret = media_entity_init(&flash->subdev.entity, 0, NULL, 0); + ret = media_entity_pads_init(&flash->subdev.entity, 0, NULL); if (ret < 0) goto free_and_quit; - flash->subdev.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH; + flash->subdev.entity.function = MEDIA_ENT_F_FLASH; return 0; diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c index 3c3c4bf..ff57c1d 100644 --- a/drivers/media/i2c/adv7180.c +++ b/drivers/media/i2c/adv7180.c @@ -1213,8 +1213,8 @@ static int adv7180_probe(struct i2c_client *client, goto err_unregister_vpp_client; state->pad.flags = MEDIA_PAD_FL_SOURCE; - sd->entity.flags |= MEDIA_ENT_T_V4L2_SUBDEV_DECODER; - ret = media_entity_init(&sd->entity, 1, &state->pad, 0); + sd->entity.flags |= MEDIA_ENT_F_ATV_DECODER; + ret = media_entity_pads_init(&sd->entity, 1, &state->pad); if (ret) goto err_free_ctrl; diff --git a/drivers/media/i2c/adv7511.c b/drivers/media/i2c/adv7511.c index eeb2cd8..471fd23 100644 --- a/drivers/media/i2c/adv7511.c +++ b/drivers/media/i2c/adv7511.c @@ -1482,7 +1482,7 @@ static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id * state->rgb_quantization_range_ctrl->is_private = true; state->pad.flags = MEDIA_PAD_FL_SINK; - err = media_entity_init(&sd->entity, 1, &state->pad, 0); + err = media_entity_pads_init(&sd->entity, 1, &state->pad); if (err) goto err_hdl; diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index 7452862..f8dd750 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -3208,8 +3208,8 @@ static int adv76xx_probe(struct i2c_client *client, state->pads[i].flags = MEDIA_PAD_FL_SINK; state->pads[state->source_pad].flags = MEDIA_PAD_FL_SOURCE; - err = media_entity_init(&sd->entity, state->source_pad + 1, - state->pads, 0); + err = media_entity_pads_init(&sd->entity, state->source_pad + 1, + state->pads); if (err) goto err_work_queues; diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index 69378e4..5fbb788 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -3309,7 +3309,7 @@ static int adv7842_probe(struct i2c_client *client, adv7842_delayed_work_enable_hotplug); state->pad.flags = MEDIA_PAD_FL_SOURCE; - err = media_entity_init(&sd->entity, 1, &state->pad, 0); + err = media_entity_pads_init(&sd->entity, 1, &state->pad); if (err) goto err_work_queues; diff --git a/drivers/media/i2c/as3645a.c b/drivers/media/i2c/as3645a.c index 29a2e70..2e90e40 100644 --- a/drivers/media/i2c/as3645a.c +++ b/drivers/media/i2c/as3645a.c @@ -827,11 +827,11 @@ static int as3645a_probe(struct i2c_client *client, if (ret < 0) goto done; - ret = media_entity_init(&flash->subdev.entity, 0, NULL, 0); + ret = media_entity_pads_init(&flash->subdev.entity, 0, NULL); if (ret < 0) goto done; - flash->subdev.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH; + flash->subdev.entity.function = MEDIA_ENT_F_FLASH; mutex_init(&flash->power_lock); diff --git a/drivers/media/i2c/cx25840/cx25840-core.c b/drivers/media/i2c/cx25840/cx25840-core.c index f2e2c34..07a3e71 100644 --- a/drivers/media/i2c/cx25840/cx25840-core.c +++ b/drivers/media/i2c/cx25840/cx25840-core.c @@ -5211,10 +5211,10 @@ static int cx25840_probe(struct i2c_client *client, state->pads[CX25840_PAD_INPUT].flags = MEDIA_PAD_FL_SINK; state->pads[CX25840_PAD_VID_OUT].flags = MEDIA_PAD_FL_SOURCE; state->pads[CX25840_PAD_VBI_OUT].flags = MEDIA_PAD_FL_SOURCE; - sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_DECODER; + sd->entity.function = MEDIA_ENT_F_ATV_DECODER; - ret = media_entity_init(&sd->entity, ARRAY_SIZE(state->pads), - state->pads, 0); + ret = media_entity_pads_init(&sd->entity, ARRAY_SIZE(state->pads), + state->pads); if (ret < 0) { v4l_info(client, "failed to initialize media entity!\n"); return ret; diff --git a/drivers/media/i2c/lm3560.c b/drivers/media/i2c/lm3560.c index 19ecb88..251a2aa 100644 --- a/drivers/media/i2c/lm3560.c +++ b/drivers/media/i2c/lm3560.c @@ -365,10 +365,10 @@ static int lm3560_subdev_init(struct lm3560_flash *flash, rval = lm3560_init_controls(flash, led_no); if (rval) goto err_out; - rval = media_entity_init(&flash->subdev_led[led_no].entity, 0, NULL, 0); + rval = media_entity_pads_init(&flash->subdev_led[led_no].entity, 0, NULL); if (rval < 0) goto err_out; - flash->subdev_led[led_no].entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH; + flash->subdev_led[led_no].entity.function = MEDIA_ENT_F_FLASH; return rval; diff --git a/drivers/media/i2c/lm3646.c b/drivers/media/i2c/lm3646.c index 7fbe6ff..7e9967a 100644 --- a/drivers/media/i2c/lm3646.c +++ b/drivers/media/i2c/lm3646.c @@ -282,10 +282,10 @@ static int lm3646_subdev_init(struct lm3646_flash *flash) rval = lm3646_init_controls(flash); if (rval) goto err_out; - rval = media_entity_init(&flash->subdev_led.entity, 0, NULL, 0); + rval = media_entity_pads_init(&flash->subdev_led.entity, 0, NULL); if (rval < 0) goto err_out; - flash->subdev_led.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH; + flash->subdev_led.entity.function = MEDIA_ENT_F_FLASH; return rval; err_out: diff --git a/drivers/media/i2c/m5mols/m5mols_core.c b/drivers/media/i2c/m5mols/m5mols_core.c index f899393..acb804b 100644 --- a/drivers/media/i2c/m5mols/m5mols_core.c +++ b/drivers/media/i2c/m5mols/m5mols_core.c @@ -975,10 +975,10 @@ static int m5mols_probe(struct i2c_client *client, sd->internal_ops = &m5mols_subdev_internal_ops; info->pad.flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_init(&sd->entity, 1, &info->pad, 0); + ret = media_entity_pads_init(&sd->entity, 1, &info->pad); if (ret < 0) return ret; - sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; init_waitqueue_head(&info->irq_waitq); mutex_init(&info->lock); diff --git a/drivers/media/i2c/mt9m032.c b/drivers/media/i2c/mt9m032.c index 101cb26..da07679 100644 --- a/drivers/media/i2c/mt9m032.c +++ b/drivers/media/i2c/mt9m032.c @@ -799,7 +799,7 @@ static int mt9m032_probe(struct i2c_client *client, sensor->subdev.ctrl_handler = &sensor->ctrls; sensor->pad.flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_init(&sensor->subdev.entity, 1, &sensor->pad, 0); + ret = media_entity_pads_init(&sensor->subdev.entity, 1, &sensor->pad); if (ret < 0) goto error_ctrl; diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c index a3da0e9..237737f 100644 --- a/drivers/media/i2c/mt9p031.c +++ b/drivers/media/i2c/mt9p031.c @@ -1112,7 +1112,7 @@ static int mt9p031_probe(struct i2c_client *client, mt9p031->subdev.internal_ops = &mt9p031_subdev_internal_ops; mt9p031->pad.flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_init(&mt9p031->subdev.entity, 1, &mt9p031->pad, 0); + ret = media_entity_pads_init(&mt9p031->subdev.entity, 1, &mt9p031->pad); if (ret < 0) goto done; diff --git a/drivers/media/i2c/mt9t001.c b/drivers/media/i2c/mt9t001.c index b28fdff..702d562 100644 --- a/drivers/media/i2c/mt9t001.c +++ b/drivers/media/i2c/mt9t001.c @@ -933,7 +933,7 @@ static int mt9t001_probe(struct i2c_client *client, mt9t001->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; mt9t001->pad.flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_init(&mt9t001->subdev.entity, 1, &mt9t001->pad, 0); + ret = media_entity_pads_init(&mt9t001->subdev.entity, 1, &mt9t001->pad); done: if (ret < 0) { diff --git a/drivers/media/i2c/mt9v032.c b/drivers/media/i2c/mt9v032.c index 1dbbd23..2e1d116 100644 --- a/drivers/media/i2c/mt9v032.c +++ b/drivers/media/i2c/mt9v032.c @@ -1046,7 +1046,7 @@ static int mt9v032_probe(struct i2c_client *client, mt9v032->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; mt9v032->pad.flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_init(&mt9v032->subdev.entity, 1, &mt9v032->pad, 0); + ret = media_entity_pads_init(&mt9v032->subdev.entity, 1, &mt9v032->pad); if (ret < 0) goto err; diff --git a/drivers/media/i2c/noon010pc30.c b/drivers/media/i2c/noon010pc30.c index 69e4f30..30cb90b 100644 --- a/drivers/media/i2c/noon010pc30.c +++ b/drivers/media/i2c/noon010pc30.c @@ -779,8 +779,8 @@ static int noon010_probe(struct i2c_client *client, goto np_err; info->pad.flags = MEDIA_PAD_FL_SOURCE; - sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; - ret = media_entity_init(&sd->entity, 1, &info->pad, 0); + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &info->pad); if (ret < 0) goto np_err; diff --git a/drivers/media/i2c/ov2659.c b/drivers/media/i2c/ov2659.c index 82c7ac1..02b9a34 100644 --- a/drivers/media/i2c/ov2659.c +++ b/drivers/media/i2c/ov2659.c @@ -1445,8 +1445,8 @@ static int ov2659_probe(struct i2c_client *client, #if defined(CONFIG_MEDIA_CONTROLLER) ov2659->pad.flags = MEDIA_PAD_FL_SOURCE; - sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; - ret = media_entity_init(&sd->entity, 1, &ov2659->pad, 0); + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &ov2659->pad); if (ret < 0) { v4l2_ctrl_handler_free(&ov2659->ctrls); return ret; diff --git a/drivers/media/i2c/ov9650.c b/drivers/media/i2c/ov9650.c index 9fe9006..a0b3c9b 100644 --- a/drivers/media/i2c/ov9650.c +++ b/drivers/media/i2c/ov9650.c @@ -1500,8 +1500,8 @@ static int ov965x_probe(struct i2c_client *client, return ret; ov965x->pad.flags = MEDIA_PAD_FL_SOURCE; - sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; - ret = media_entity_init(&sd->entity, 1, &ov965x->pad, 0); + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &ov965x->pad); if (ret < 0) return ret; diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/drivers/media/i2c/s5c73m3/s5c73m3-core.c index 25f5e79..57b3d27 100644 --- a/drivers/media/i2c/s5c73m3/s5c73m3-core.c +++ b/drivers/media/i2c/s5c73m3/s5c73m3-core.c @@ -1482,11 +1482,11 @@ static int s5c73m3_oif_registered(struct v4l2_subdev *sd) return ret; } - ret = media_entity_create_link(&state->sensor_sd.entity, + ret = media_create_pad_link(&state->sensor_sd.entity, S5C73M3_ISP_PAD, &state->oif_sd.entity, OIF_ISP_PAD, MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); - ret = media_entity_create_link(&state->sensor_sd.entity, + ret = media_create_pad_link(&state->sensor_sd.entity, S5C73M3_JPEG_PAD, &state->oif_sd.entity, OIF_JPEG_PAD, MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); @@ -1688,10 +1688,10 @@ static int s5c73m3_probe(struct i2c_client *client, state->sensor_pads[S5C73M3_JPEG_PAD].flags = MEDIA_PAD_FL_SOURCE; state->sensor_pads[S5C73M3_ISP_PAD].flags = MEDIA_PAD_FL_SOURCE; - sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; - ret = media_entity_init(&sd->entity, S5C73M3_NUM_PADS, - state->sensor_pads, 0); + ret = media_entity_pads_init(&sd->entity, S5C73M3_NUM_PADS, + state->sensor_pads); if (ret < 0) return ret; @@ -1704,10 +1704,10 @@ static int s5c73m3_probe(struct i2c_client *client, state->oif_pads[OIF_ISP_PAD].flags = MEDIA_PAD_FL_SINK; state->oif_pads[OIF_JPEG_PAD].flags = MEDIA_PAD_FL_SINK; state->oif_pads[OIF_SOURCE_PAD].flags = MEDIA_PAD_FL_SOURCE; - oif_sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV; + oif_sd->entity.function = MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN; - ret = media_entity_init(&oif_sd->entity, OIF_NUM_PADS, - state->oif_pads, 0); + ret = media_entity_pads_init(&oif_sd->entity, OIF_NUM_PADS, + state->oif_pads); if (ret < 0) return ret; diff --git a/drivers/media/i2c/s5k4ecgx.c b/drivers/media/i2c/s5k4ecgx.c index 6757aca..8a0f22d 100644 --- a/drivers/media/i2c/s5k4ecgx.c +++ b/drivers/media/i2c/s5k4ecgx.c @@ -961,8 +961,8 @@ static int s5k4ecgx_probe(struct i2c_client *client, sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; priv->pad.flags = MEDIA_PAD_FL_SOURCE; - sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; - ret = media_entity_init(&sd->entity, 1, &priv->pad, 0); + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &priv->pad); if (ret) return ret; diff --git a/drivers/media/i2c/s5k5baf.c b/drivers/media/i2c/s5k5baf.c index 774e0d0..fc3a5a8 100644 --- a/drivers/media/i2c/s5k5baf.c +++ b/drivers/media/i2c/s5k5baf.c @@ -408,7 +408,7 @@ static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl) static inline bool s5k5baf_is_cis_subdev(struct v4l2_subdev *sd) { - return sd->entity.type == MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; + return sd->entity.function == MEDIA_ENT_F_CAM_SENSOR; } static inline struct s5k5baf *to_s5k5baf(struct v4l2_subdev *sd) @@ -1756,7 +1756,7 @@ static int s5k5baf_registered(struct v4l2_subdev *sd) v4l2_err(sd, "failed to register subdev %s\n", state->cis_sd.name); else - ret = media_entity_create_link(&state->cis_sd.entity, PAD_CIS, + ret = media_create_pad_link(&state->cis_sd.entity, PAD_CIS, &state->sd.entity, PAD_CIS, MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); @@ -1904,8 +1904,8 @@ static int s5k5baf_configure_subdevs(struct s5k5baf *state, sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; state->cis_pad.flags = MEDIA_PAD_FL_SOURCE; - sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; - ret = media_entity_init(&sd->entity, NUM_CIS_PADS, &state->cis_pad, 0); + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, NUM_CIS_PADS, &state->cis_pad); if (ret < 0) goto err; @@ -1919,8 +1919,8 @@ static int s5k5baf_configure_subdevs(struct s5k5baf *state, state->pads[PAD_CIS].flags = MEDIA_PAD_FL_SINK; state->pads[PAD_OUT].flags = MEDIA_PAD_FL_SOURCE; - sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV; - ret = media_entity_init(&sd->entity, NUM_ISP_PADS, state->pads, 0); + sd->entity.function = MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN; + ret = media_entity_pads_init(&sd->entity, NUM_ISP_PADS, state->pads); if (!ret) return 0; diff --git a/drivers/media/i2c/s5k6a3.c b/drivers/media/i2c/s5k6a3.c index b1b1574..b9e43ff 100644 --- a/drivers/media/i2c/s5k6a3.c +++ b/drivers/media/i2c/s5k6a3.c @@ -333,7 +333,7 @@ static int s5k6a3_probe(struct i2c_client *client, sensor->format.height = S5K6A3_DEFAULT_HEIGHT; sensor->pad.flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_init(&sd->entity, 1, &sensor->pad, 0); + ret = media_entity_pads_init(&sd->entity, 1, &sensor->pad); if (ret < 0) return ret; diff --git a/drivers/media/i2c/s5k6aa.c b/drivers/media/i2c/s5k6aa.c index 60aaff7..faee113 100644 --- a/drivers/media/i2c/s5k6aa.c +++ b/drivers/media/i2c/s5k6aa.c @@ -1577,8 +1577,8 @@ static int s5k6aa_probe(struct i2c_client *client, sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; s5k6aa->pad.flags = MEDIA_PAD_FL_SOURCE; - sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; - ret = media_entity_init(&sd->entity, 1, &s5k6aa->pad, 0); + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &s5k6aa->pad); if (ret) return ret; diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index fb39dfd..a215efe 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -2487,31 +2487,31 @@ static int smiapp_register_subdevs(struct smiapp_sensor *sensor) if (!last) continue; - rval = media_entity_init(&this->sd.entity, - this->npads, this->pads, 0); + rval = media_entity_pads_init(&this->sd.entity, + this->npads, this->pads); if (rval) { dev_err(&client->dev, - "media_entity_init failed\n"); + "media_entity_pads_init failed\n"); return rval; } - rval = media_entity_create_link(&this->sd.entity, - this->source_pad, - &last->sd.entity, - last->sink_pad, - MEDIA_LNK_FL_ENABLED | - MEDIA_LNK_FL_IMMUTABLE); + rval = v4l2_device_register_subdev(sensor->src->sd.v4l2_dev, + &this->sd); if (rval) { dev_err(&client->dev, - "media_entity_create_link failed\n"); + "v4l2_device_register_subdev failed\n"); return rval; } - rval = v4l2_device_register_subdev(sensor->src->sd.v4l2_dev, - &this->sd); + rval = media_create_pad_link(&this->sd.entity, + this->source_pad, + &last->sd.entity, + last->sink_pad, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); if (rval) { dev_err(&client->dev, - "v4l2_device_register_subdev failed\n"); + "media_create_pad_link failed\n"); return rval; } } @@ -2763,7 +2763,7 @@ static int smiapp_init(struct smiapp_sensor *sensor) dev_dbg(&client->dev, "profile %d\n", sensor->minfo.smiapp_profile); - sensor->pixel_array->sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; + sensor->pixel_array->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; /* final steps */ smiapp_read_frame_fmt(sensor); @@ -3077,8 +3077,8 @@ static int smiapp_probe(struct i2c_client *client, sensor->src->sensor = sensor; sensor->src->pads[0].flags = MEDIA_PAD_FL_SOURCE; - rval = media_entity_init(&sensor->src->sd.entity, 2, - sensor->src->pads, 0); + rval = media_entity_pads_init(&sensor->src->sd.entity, 2, + sensor->src->pads); if (rval < 0) return rval; diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index 77b8011..3397eb9 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -1889,7 +1889,7 @@ static int tc358743_probe(struct i2c_client *client, } state->pad.flags = MEDIA_PAD_FL_SOURCE; - err = media_entity_init(&sd->entity, 1, &state->pad, 0); + err = media_entity_pads_init(&sd->entity, 1, &state->pad); if (err < 0) goto err_hdl; diff --git a/drivers/media/i2c/tvp514x.c b/drivers/media/i2c/tvp514x.c index b5dba5b..7fa5f1e 100644 --- a/drivers/media/i2c/tvp514x.c +++ b/drivers/media/i2c/tvp514x.c @@ -1095,9 +1095,9 @@ tvp514x_probe(struct i2c_client *client, const struct i2c_device_id *id) #if defined(CONFIG_MEDIA_CONTROLLER) decoder->pad.flags = MEDIA_PAD_FL_SOURCE; decoder->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - decoder->sd.entity.flags |= MEDIA_ENT_T_V4L2_SUBDEV_DECODER; + decoder->sd.entity.flags |= MEDIA_ENT_F_ATV_DECODER; - ret = media_entity_init(&decoder->sd.entity, 1, &decoder->pad, 0); + ret = media_entity_pads_init(&decoder->sd.entity, 1, &decoder->pad); if (ret < 0) { v4l2_err(sd, "%s decoder driver failed to register !!\n", sd->name); diff --git a/drivers/media/i2c/tvp7002.c b/drivers/media/i2c/tvp7002.c index 772a304..83c79fa 100644 --- a/drivers/media/i2c/tvp7002.c +++ b/drivers/media/i2c/tvp7002.c @@ -1012,9 +1012,9 @@ static int tvp7002_probe(struct i2c_client *c, const struct i2c_device_id *id) #if defined(CONFIG_MEDIA_CONTROLLER) device->pad.flags = MEDIA_PAD_FL_SOURCE; device->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - device->sd.entity.flags |= MEDIA_ENT_T_V4L2_SUBDEV_DECODER; + device->sd.entity.flags |= MEDIA_ENT_F_ATV_DECODER; - error = media_entity_init(&device->sd.entity, 1, &device->pad, 0); + error = media_entity_pads_init(&device->sd.entity, 1, &device->pad); if (error < 0) return error; #endif diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c index 7b39440..7dae0ac 100644 --- a/drivers/media/media-device.c +++ b/drivers/media/media-device.c @@ -22,14 +22,18 @@ #include <linux/compat.h> #include <linux/export.h> +#include <linux/idr.h> #include <linux/ioctl.h> #include <linux/media.h> +#include <linux/slab.h> #include <linux/types.h> #include <media/media-device.h> #include <media/media-devnode.h> #include <media/media-entity.h> +#ifdef CONFIG_MEDIA_CONTROLLER + /* ----------------------------------------------------------------------------- * Userspace API */ @@ -75,8 +79,8 @@ static struct media_entity *find_entity(struct media_device *mdev, u32 id) spin_lock(&mdev->lock); media_device_for_each_entity(entity, mdev) { - if ((entity->id == id && !next) || - (entity->id > id && next)) { + if (((media_entity_id(entity) == id) && !next) || + ((media_entity_id(entity) > id) && next)) { spin_unlock(&mdev->lock); return entity; } @@ -102,13 +106,13 @@ static long media_device_enum_entities(struct media_device *mdev, if (ent == NULL) return -EINVAL; - u_ent.id = ent->id; + u_ent.id = media_entity_id(ent); if (ent->name) strlcpy(u_ent.name, ent->name, sizeof(u_ent.name)); - u_ent.type = ent->type; - u_ent.revision = ent->revision; + u_ent.type = ent->function; + u_ent.revision = 0; /* Unused */ u_ent.flags = ent->flags; - u_ent.group_id = ent->group_id; + u_ent.group_id = 0; /* Unused */ u_ent.pads = ent->num_pads; u_ent.links = ent->num_links - ent->num_backlinks; memcpy(&u_ent.raw, &ent->info, sizeof(ent->info)); @@ -120,7 +124,7 @@ static long media_device_enum_entities(struct media_device *mdev, static void media_device_kpad_to_upad(const struct media_pad *kpad, struct media_pad_desc *upad) { - upad->entity = kpad->entity->id; + upad->entity = media_entity_id(kpad->entity); upad->index = kpad->index; upad->flags = kpad->flags; } @@ -148,25 +152,25 @@ static long __media_device_enum_links(struct media_device *mdev, } if (links->links) { - struct media_link_desc __user *ulink; - unsigned int l; + struct media_link *link; + struct media_link_desc __user *ulink_desc = links->links; - for (l = 0, ulink = links->links; l < entity->num_links; l++) { - struct media_link_desc link; + list_for_each_entry(link, &entity->links, list) { + struct media_link_desc klink_desc; /* Ignore backlinks. */ - if (entity->links[l].source->entity != entity) + if (link->source->entity != entity) continue; - - memset(&link, 0, sizeof(link)); - media_device_kpad_to_upad(entity->links[l].source, - &link.source); - media_device_kpad_to_upad(entity->links[l].sink, - &link.sink); - link.flags = entity->links[l].flags; - if (copy_to_user(ulink, &link, sizeof(*ulink))) + memset(&klink_desc, 0, sizeof(klink_desc)); + media_device_kpad_to_upad(link->source, + &klink_desc.source); + media_device_kpad_to_upad(link->sink, + &klink_desc.sink); + klink_desc.flags = link->flags; + if (copy_to_user(ulink_desc, &klink_desc, + sizeof(*ulink_desc))) return -EFAULT; - ulink++; + ulink_desc++; } } @@ -230,6 +234,164 @@ static long media_device_setup_link(struct media_device *mdev, return ret; } +#if 0 /* Let's postpone it to Kernel 4.6 */ +static long __media_device_get_topology(struct media_device *mdev, + struct media_v2_topology *topo) +{ + struct media_entity *entity; + struct media_interface *intf; + struct media_pad *pad; + struct media_link *link; + struct media_v2_entity kentity, *uentity; + struct media_v2_interface kintf, *uintf; + struct media_v2_pad kpad, *upad; + struct media_v2_link klink, *ulink; + unsigned int i; + int ret = 0; + + topo->topology_version = mdev->topology_version; + + /* Get entities and number of entities */ + i = 0; + uentity = media_get_uptr(topo->ptr_entities); + media_device_for_each_entity(entity, mdev) { + i++; + if (ret || !uentity) + continue; + + if (i > topo->num_entities) { + ret = -ENOSPC; + continue; + } + + /* Copy fields to userspace struct if not error */ + memset(&kentity, 0, sizeof(kentity)); + kentity.id = entity->graph_obj.id; + kentity.function = entity->function; + strncpy(kentity.name, entity->name, + sizeof(kentity.name)); + + if (copy_to_user(uentity, &kentity, sizeof(kentity))) + ret = -EFAULT; + uentity++; + } + topo->num_entities = i; + + /* Get interfaces and number of interfaces */ + i = 0; + uintf = media_get_uptr(topo->ptr_interfaces); + media_device_for_each_intf(intf, mdev) { + i++; + if (ret || !uintf) + continue; + + if (i > topo->num_interfaces) { + ret = -ENOSPC; + continue; + } + + memset(&kintf, 0, sizeof(kintf)); + + /* Copy intf fields to userspace struct */ + kintf.id = intf->graph_obj.id; + kintf.intf_type = intf->type; + kintf.flags = intf->flags; + + if (media_type(&intf->graph_obj) == MEDIA_GRAPH_INTF_DEVNODE) { + struct media_intf_devnode *devnode; + + devnode = intf_to_devnode(intf); + + kintf.devnode.major = devnode->major; + kintf.devnode.minor = devnode->minor; + } + + if (copy_to_user(uintf, &kintf, sizeof(kintf))) + ret = -EFAULT; + uintf++; + } + topo->num_interfaces = i; + + /* Get pads and number of pads */ + i = 0; + upad = media_get_uptr(topo->ptr_pads); + media_device_for_each_pad(pad, mdev) { + i++; + if (ret || !upad) + continue; + + if (i > topo->num_pads) { + ret = -ENOSPC; + continue; + } + + memset(&kpad, 0, sizeof(kpad)); + + /* Copy pad fields to userspace struct */ + kpad.id = pad->graph_obj.id; + kpad.entity_id = pad->entity->graph_obj.id; + kpad.flags = pad->flags; + + if (copy_to_user(upad, &kpad, sizeof(kpad))) + ret = -EFAULT; + upad++; + } + topo->num_pads = i; + + /* Get links and number of links */ + i = 0; + ulink = media_get_uptr(topo->ptr_links); + media_device_for_each_link(link, mdev) { + if (link->is_backlink) + continue; + + i++; + + if (ret || !ulink) + continue; + + if (i > topo->num_links) { + ret = -ENOSPC; + continue; + } + + memset(&klink, 0, sizeof(klink)); + + /* Copy link fields to userspace struct */ + klink.id = link->graph_obj.id; + klink.source_id = link->gobj0->id; + klink.sink_id = link->gobj1->id; + klink.flags = link->flags; + + if (copy_to_user(ulink, &klink, sizeof(klink))) + ret = -EFAULT; + ulink++; + } + topo->num_links = i; + + return ret; +} + +static long media_device_get_topology(struct media_device *mdev, + struct media_v2_topology __user *utopo) +{ + struct media_v2_topology ktopo; + int ret; + + if (copy_from_user(&ktopo, utopo, sizeof(ktopo))) + return -EFAULT; + + ret = __media_device_get_topology(mdev, &ktopo); + if (ret < 0) + return ret; + + if (copy_to_user(utopo, &ktopo, sizeof(*utopo))) + return -EFAULT; + + return 0; +} +#endif + static long media_device_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { @@ -262,6 +424,14 @@ static long media_device_ioctl(struct file *filp, unsigned int cmd, mutex_unlock(&dev->graph_mutex); break; +#if 0 /* Let's postpone it to Kernel 4.6 */ + case MEDIA_IOC_G_TOPOLOGY: + mutex_lock(&dev->graph_mutex); + ret = media_device_get_topology(dev, + (struct media_v2_topology __user *)arg); + mutex_unlock(&dev->graph_mutex); + break; +#endif default: ret = -ENOIOCTLCMD; } @@ -310,6 +480,9 @@ static long media_device_compat_ioctl(struct file *filp, unsigned int cmd, case MEDIA_IOC_DEVICE_INFO: case MEDIA_IOC_ENUM_ENTITIES: case MEDIA_IOC_SETUP_LINK: +#if 0 /* Let's postpone it to Kernel 4.6 */ + case MEDIA_IOC_G_TOPOLOGY: +#endif return media_device_ioctl(filp, cmd, arg); case MEDIA_IOC_ENUM_LINKS32: @@ -357,10 +530,107 @@ static DEVICE_ATTR(model, S_IRUGO, show_model, NULL); static void media_device_release(struct media_devnode *mdev) { + dev_dbg(mdev->parent, "Media device released\n"); +} + +/** + * media_device_register_entity - Register an entity with a media device + * @mdev: The media device + * @entity: The entity + */ +int __must_check media_device_register_entity(struct media_device *mdev, + struct media_entity *entity) +{ + unsigned int i; + int ret; + + if (entity->function == MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN || + entity->function == MEDIA_ENT_F_UNKNOWN) + dev_warn(mdev->dev, + "Entity type for entity %s was not initialized!\n", + entity->name); + + /* Warn if we apparently re-register an entity */ + WARN_ON(entity->graph_obj.mdev != NULL); + entity->graph_obj.mdev = mdev; + INIT_LIST_HEAD(&entity->links); + entity->num_links = 0; + entity->num_backlinks = 0; + + if (!ida_pre_get(&mdev->entity_internal_idx, GFP_KERNEL)) + return -ENOMEM; + + spin_lock(&mdev->lock); + + ret = ida_get_new_above(&mdev->entity_internal_idx, 1, + &entity->internal_idx); + if (ret < 0) { + spin_unlock(&mdev->lock); + return ret; + } + + mdev->entity_internal_idx_max = + max(mdev->entity_internal_idx_max, entity->internal_idx); + + /* Initialize media_gobj embedded at the entity */ + media_gobj_create(mdev, MEDIA_GRAPH_ENTITY, &entity->graph_obj); + + /* Initialize objects at the pads */ + for (i = 0; i < entity->num_pads; i++) + media_gobj_create(mdev, MEDIA_GRAPH_PAD, + &entity->pads[i].graph_obj); + + spin_unlock(&mdev->lock); + + return 0; +} +EXPORT_SYMBOL_GPL(media_device_register_entity); + +static void __media_device_unregister_entity(struct media_entity *entity) +{ + struct media_device *mdev = entity->graph_obj.mdev; + struct media_link *link, *tmp; + struct media_interface *intf; + unsigned int i; + + ida_simple_remove(&mdev->entity_internal_idx, entity->internal_idx); + + /* Remove all interface links pointing to this entity */ + list_for_each_entry(intf, &mdev->interfaces, graph_obj.list) { + list_for_each_entry_safe(link, tmp, &intf->links, list) { + if (link->entity == entity) + __media_remove_intf_link(link); + } + } + + /* Remove all data links that belong to this entity */ + __media_entity_remove_links(entity); + + /* Remove all pads that belong to this entity */ + for (i = 0; i < entity->num_pads; i++) + media_gobj_destroy(&entity->pads[i].graph_obj); + + /* Remove the entity */ + media_gobj_destroy(&entity->graph_obj); + + entity->graph_obj.mdev = NULL; } +void media_device_unregister_entity(struct media_entity *entity) +{ + struct media_device *mdev = entity->graph_obj.mdev; + + if (mdev == NULL) + return; + + spin_lock(&mdev->lock); + __media_device_unregister_entity(entity); + spin_unlock(&mdev->lock); +} +EXPORT_SYMBOL_GPL(media_device_unregister_entity); + /** - * media_device_register - register a media device + * media_device_init() - initialize a media device * @mdev: The media device * * The caller is responsible for initializing the media device before @@ -369,23 +639,41 @@ static void media_device_release(struct media_devnode *mdev) * - dev must point to the parent device * - model must be filled with the device model name */ -int __must_check __media_device_register(struct media_device *mdev, - struct module *owner) +void media_device_init(struct media_device *mdev) { - int ret; - - if (WARN_ON(mdev->dev == NULL || mdev->model[0] == 0)) - return -EINVAL; - - mdev->entity_id = 1; INIT_LIST_HEAD(&mdev->entities); + INIT_LIST_HEAD(&mdev->interfaces); + INIT_LIST_HEAD(&mdev->pads); + INIT_LIST_HEAD(&mdev->links); spin_lock_init(&mdev->lock); mutex_init(&mdev->graph_mutex); + ida_init(&mdev->entity_internal_idx); + + dev_dbg(mdev->dev, "Media device initialized\n"); +} +EXPORT_SYMBOL_GPL(media_device_init); + +void media_device_cleanup(struct media_device *mdev) +{ + ida_destroy(&mdev->entity_internal_idx); + mdev->entity_internal_idx_max = 0; + mutex_destroy(&mdev->graph_mutex); +} +EXPORT_SYMBOL_GPL(media_device_cleanup); + +int __must_check __media_device_register(struct media_device *mdev, + struct module *owner) +{ + int ret; /* Register the device node. */ mdev->devnode.fops = &media_device_fops; mdev->devnode.parent = mdev->dev; mdev->devnode.release = media_device_release; + + /* Set version 0 to indicate user-space that the graph is static */ + mdev->topology_version = 0; + ret = media_devnode_register(&mdev->devnode, owner); if (ret < 0) return ret; @@ -396,69 +684,74 @@ int __must_check __media_device_register(struct media_device *mdev, return ret; } + dev_dbg(mdev->dev, "Media device registered\n"); + return 0; } EXPORT_SYMBOL_GPL(__media_device_register); -/** - * media_device_unregister - unregister a media device - * @mdev: The media device - * - */ void media_device_unregister(struct media_device *mdev) { struct media_entity *entity; struct media_entity *next; + struct media_interface *intf, *tmp_intf; - list_for_each_entry_safe(entity, next, &mdev->entities, list) - media_device_unregister_entity(entity); + if (mdev == NULL) + return; + + spin_lock(&mdev->lock); + + /* Check if mdev was ever registered at all */ + if (!media_devnode_is_registered(&mdev->devnode)) { + spin_unlock(&mdev->lock); + return; + } + + /* Remove all entities from the media device */ + list_for_each_entry_safe(entity, next, &mdev->entities, graph_obj.list) + __media_device_unregister_entity(entity); + + /* Remove all interfaces from the media device */ + list_for_each_entry_safe(intf, tmp_intf, &mdev->interfaces, + graph_obj.list) { + __media_remove_intf_links(intf); + media_gobj_destroy(&intf->graph_obj); + kfree(intf); + } + + spin_unlock(&mdev->lock); device_remove_file(&mdev->devnode.dev, &dev_attr_model); media_devnode_unregister(&mdev->devnode); + + dev_dbg(mdev->dev, "Media device unregistered\n"); } EXPORT_SYMBOL_GPL(media_device_unregister); -/** - * media_device_register_entity - Register an entity with a media device - * @mdev: The media device - * @entity: The entity - */ -int __must_check media_device_register_entity(struct media_device *mdev, - struct media_entity *entity) +static void media_device_release_devres(struct device *dev, void *res) { - /* Warn if we apparently re-register an entity */ - WARN_ON(entity->parent != NULL); - entity->parent = mdev; - - spin_lock(&mdev->lock); - if (entity->id == 0) - entity->id = mdev->entity_id++; - else - mdev->entity_id = max(entity->id + 1, mdev->entity_id); - list_add_tail(&entity->list, &mdev->entities); - spin_unlock(&mdev->lock); - - return 0; } -EXPORT_SYMBOL_GPL(media_device_register_entity); -/** - * media_device_unregister_entity - Unregister an entity - * @entity: The entity - * - * If the entity has never been registered this function will return - * immediately. - */ -void media_device_unregister_entity(struct media_entity *entity) +struct media_device *media_device_get_devres(struct device *dev) { - struct media_device *mdev = entity->parent; + struct media_device *mdev; - if (mdev == NULL) - return; + mdev = devres_find(dev, media_device_release_devres, NULL, NULL); + if (mdev) + return mdev; - spin_lock(&mdev->lock); - list_del(&entity->list); - spin_unlock(&mdev->lock); - entity->parent = NULL; + mdev = devres_alloc(media_device_release_devres, + sizeof(struct media_device), GFP_KERNEL); + if (!mdev) + return NULL; + return devres_get(dev, mdev, NULL, NULL); } -EXPORT_SYMBOL_GPL(media_device_unregister_entity); +EXPORT_SYMBOL_GPL(media_device_get_devres); + +struct media_device *media_device_find_devres(struct device *dev) +{ + return devres_find(dev, media_device_release_devres, NULL, NULL); +} +EXPORT_SYMBOL_GPL(media_device_find_devres); + +#endif /* CONFIG_MEDIA_CONTROLLER */ diff --git a/drivers/media/media-devnode.c b/drivers/media/media-devnode.c index ebf9626..cea35bf 100644 --- a/drivers/media/media-devnode.c +++ b/drivers/media/media-devnode.c @@ -217,20 +217,6 @@ static const struct file_operations media_devnode_fops = { .llseek = no_llseek, }; -/** - * media_devnode_register - register a media device node - * @mdev: media device node structure we want to register - * - * The registration code assigns minor numbers and registers the new device node - * with the kernel. An error is returned if no free minor number can be found, - * or if the registration of the device node fails. - * - * Zero is returned on success. - * - * Note that if the media_devnode_register call fails, the release() callback of - * the media_devnode structure is *not* called, so the caller is responsible for - * freeing any data. - */ int __must_check media_devnode_register(struct media_devnode *mdev, struct module *owner) { @@ -285,16 +271,6 @@ error: return ret; } -/** - * media_devnode_unregister - unregister a media device node - * @mdev: the device node to unregister - * - * This unregisters the passed device. Future open calls will be met with - * errors. - * - * This function can safely be called if the device node has never been - * registered or has already been unregistered. - */ void media_devnode_unregister(struct media_devnode *mdev) { /* Check if mdev was ever registered at all */ diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c index 767fe55..e89d85a 100644 --- a/drivers/media/media-entity.c +++ b/drivers/media/media-entity.c @@ -26,65 +26,198 @@ #include <media/media-entity.h> #include <media/media-device.h> +static inline const char *gobj_type(enum media_gobj_type type) +{ + switch (type) { + case MEDIA_GRAPH_ENTITY: + return "entity"; + case MEDIA_GRAPH_PAD: + return "pad"; + case MEDIA_GRAPH_LINK: + return "link"; + case MEDIA_GRAPH_INTF_DEVNODE: + return "intf-devnode"; + default: + return "unknown"; + } +} + +static inline const char *intf_type(struct media_interface *intf) +{ + switch (intf->type) { + case MEDIA_INTF_T_DVB_FE: + return "frontend"; + case MEDIA_INTF_T_DVB_DEMUX: + return "demux"; + case MEDIA_INTF_T_DVB_DVR: + return "DVR"; + case MEDIA_INTF_T_DVB_CA: + return "CA"; + case MEDIA_INTF_T_DVB_NET: + return "dvbnet"; + case MEDIA_INTF_T_V4L_VIDEO: + return "video"; + case MEDIA_INTF_T_V4L_VBI: + return "vbi"; + case MEDIA_INTF_T_V4L_RADIO: + return "radio"; + case MEDIA_INTF_T_V4L_SUBDEV: + return "v4l2-subdev"; + case MEDIA_INTF_T_V4L_SWRADIO: + return "swradio"; + default: + return "unknown-intf"; + } +}; + +__must_check int __media_entity_enum_init(struct media_entity_enum *ent_enum, + int idx_max) +{ + ent_enum->bmap = kcalloc(DIV_ROUND_UP(idx_max, BITS_PER_LONG), + sizeof(long), GFP_KERNEL); + if (!ent_enum->bmap) + return -ENOMEM; + + bitmap_zero(ent_enum->bmap, idx_max); + ent_enum->idx_max = idx_max; + + return 0; +} +EXPORT_SYMBOL_GPL(__media_entity_enum_init); + +void media_entity_enum_cleanup(struct media_entity_enum *ent_enum) +{ + kfree(ent_enum->bmap); +} +EXPORT_SYMBOL_GPL(media_entity_enum_cleanup); + /** - * media_entity_init - Initialize a media entity - * - * @num_pads: Total number of sink and source pads. - * @extra_links: Initial estimate of the number of extra links. - * @pads: Array of 'num_pads' pads. - * - * The total number of pads is an intrinsic property of entities known by the - * entity driver, while the total number of links depends on hardware design - * and is an extrinsic property unknown to the entity driver. However, in most - * use cases the entity driver can guess the number of links which can safely - * be assumed to be equal to or larger than the number of pads. - * - * For those reasons the links array can be preallocated based on the entity - * driver guess and will be reallocated later if extra links need to be - * created. + * dev_dbg_obj - Prints in debug mode a change on some object * - * This function allocates a links array with enough space to hold at least - * 'num_pads' + 'extra_links' elements. The media_entity::max_links field will - * be set to the number of allocated elements. + * @event_name: Name of the event to report. Could be __func__ + * @gobj: Pointer to the object * - * The pads array is managed by the entity driver and passed to - * media_entity_init() where its pointer will be stored in the entity structure. + * Enabled only if DEBUG or CONFIG_DYNAMIC_DEBUG. Otherwise, it + * won't produce any code. */ -int -media_entity_init(struct media_entity *entity, u16 num_pads, - struct media_pad *pads, u16 extra_links) +static void dev_dbg_obj(const char *event_name, struct media_gobj *gobj) { - struct media_link *links; - unsigned int max_links = num_pads + extra_links; - unsigned int i; +#if defined(DEBUG) || defined (CONFIG_DYNAMIC_DEBUG) + switch (media_type(gobj)) { + case MEDIA_GRAPH_ENTITY: + dev_dbg(gobj->mdev->dev, + "%s id %u: entity '%s'\n", + event_name, media_id(gobj), + gobj_to_entity(gobj)->name); + break; + case MEDIA_GRAPH_LINK: + { + struct media_link *link = gobj_to_link(gobj); + + dev_dbg(gobj->mdev->dev, + "%s id %u: %s link id %u ==> id %u\n", + event_name, media_id(gobj), + media_type(link->gobj0) == MEDIA_GRAPH_PAD ? + "data" : "interface", + media_id(link->gobj0), + media_id(link->gobj1)); + break; + } + case MEDIA_GRAPH_PAD: + { + struct media_pad *pad = gobj_to_pad(gobj); + + dev_dbg(gobj->mdev->dev, + "%s id %u: %s%spad '%s':%d\n", + event_name, media_id(gobj), + pad->flags & MEDIA_PAD_FL_SINK ? "sink " : "", + pad->flags & MEDIA_PAD_FL_SOURCE ? "source " : "", + pad->entity->name, pad->index); + break; + } + case MEDIA_GRAPH_INTF_DEVNODE: + { + struct media_interface *intf = gobj_to_intf(gobj); + struct media_intf_devnode *devnode = intf_to_devnode(intf); + + dev_dbg(gobj->mdev->dev, + "%s id %u: intf_devnode %s - major: %d, minor: %d\n", + event_name, media_id(gobj), + intf_type(intf), + devnode->major, devnode->minor); + break; + } + } +#endif +} - links = kzalloc(max_links * sizeof(links[0]), GFP_KERNEL); - if (links == NULL) - return -ENOMEM; +void media_gobj_create(struct media_device *mdev, + enum media_gobj_type type, + struct media_gobj *gobj) +{ + BUG_ON(!mdev); + + gobj->mdev = mdev; + + /* Create a per-type unique object ID */ + gobj->id = media_gobj_gen_id(type, ++mdev->id); + + switch (type) { + case MEDIA_GRAPH_ENTITY: + list_add_tail(&gobj->list, &mdev->entities); + break; + case MEDIA_GRAPH_PAD: + list_add_tail(&gobj->list, &mdev->pads); + break; + case MEDIA_GRAPH_LINK: + list_add_tail(&gobj->list, &mdev->links); + break; + case MEDIA_GRAPH_INTF_DEVNODE: + list_add_tail(&gobj->list, &mdev->interfaces); + break; + } + + mdev->topology_version++; + + dev_dbg_obj(__func__, gobj); +} + +void media_gobj_destroy(struct media_gobj *gobj) +{ + dev_dbg_obj(__func__, gobj); + + gobj->mdev->topology_version++; + + /* Remove the object from mdev list */ + list_del(&gobj->list); +} + +int media_entity_pads_init(struct media_entity *entity, u16 num_pads, + struct media_pad *pads) +{ + struct media_device *mdev = entity->graph_obj.mdev; + unsigned int i; - entity->group_id = 0; - entity->max_links = max_links; - entity->num_links = 0; - entity->num_backlinks = 0; entity->num_pads = num_pads; entity->pads = pads; - entity->links = links; + + if (mdev) + spin_lock(&mdev->lock); for (i = 0; i < num_pads; i++) { pads[i].entity = entity; pads[i].index = i; + if (mdev) + media_gobj_create(mdev, MEDIA_GRAPH_PAD, + &entity->pads[i].graph_obj); } - return 0; -} -EXPORT_SYMBOL_GPL(media_entity_init); + if (mdev) + spin_unlock(&mdev->lock); -void -media_entity_cleanup(struct media_entity *entity) -{ - kfree(entity->links); + return 0; } -EXPORT_SYMBOL_GPL(media_entity_cleanup); +EXPORT_SYMBOL_GPL(media_entity_pads_init); /* ----------------------------------------------------------------------------- * Graph traversal @@ -108,7 +241,7 @@ static void stack_push(struct media_entity_graph *graph, return; } graph->top++; - graph->stack[graph->top].link = 0; + graph->stack[graph->top].link = entity->links.next; graph->stack[graph->top].entity = entity; } @@ -125,43 +258,51 @@ static struct media_entity *stack_pop(struct media_entity_graph *graph) #define link_top(en) ((en)->stack[(en)->top].link) #define stack_top(en) ((en)->stack[(en)->top].entity) +/* + * TODO: Get rid of this. + */ +#define MEDIA_ENTITY_MAX_PADS 512 + /** - * media_entity_graph_walk_start - Start walking the media graph at a given entity + * media_entity_graph_walk_init - Allocate resources for graph walk * @graph: Media graph structure that will be used to walk the graph - * @entity: Starting entity + * @mdev: Media device * - * This function initializes the graph traversal structure to walk the entities - * graph starting at the given entity. The traversal structure must not be - * modified by the caller during graph traversal. When done the structure can - * safely be freed. + * Reserve resources for graph walk in media device's current + * state. The memory must be released using + * media_entity_graph_walk_free(). + * + * Returns error on failure, zero on success. */ +__must_check int media_entity_graph_walk_init( + struct media_entity_graph *graph, struct media_device *mdev) +{ + return media_entity_enum_init(&graph->ent_enum, mdev); +} +EXPORT_SYMBOL_GPL(media_entity_graph_walk_init); + +/** + * media_entity_graph_walk_cleanup - Release resources related to graph walking + * @graph: Media graph structure that was used to walk the graph + */ +void media_entity_graph_walk_cleanup(struct media_entity_graph *graph) +{ + media_entity_enum_cleanup(&graph->ent_enum); +} +EXPORT_SYMBOL_GPL(media_entity_graph_walk_cleanup); + void media_entity_graph_walk_start(struct media_entity_graph *graph, struct media_entity *entity) { + media_entity_enum_zero(&graph->ent_enum); + media_entity_enum_set(&graph->ent_enum, entity); + graph->top = 0; graph->stack[graph->top].entity = NULL; - bitmap_zero(graph->entities, MEDIA_ENTITY_ENUM_MAX_ID); - - if (WARN_ON(entity->id >= MEDIA_ENTITY_ENUM_MAX_ID)) - return; - - __set_bit(entity->id, graph->entities); stack_push(graph, entity); } EXPORT_SYMBOL_GPL(media_entity_graph_walk_start); -/** - * media_entity_graph_walk_next - Get the next entity in the graph - * @graph: Media graph structure - * - * Perform a depth-first traversal of the given media entities graph. - * - * The graph structure must have been previously initialized with a call to - * media_entity_graph_walk_start(). - * - * Return the next entity in the graph or NULL if the whole graph have been - * traversed. - */ struct media_entity * media_entity_graph_walk_next(struct media_entity_graph *graph) { @@ -173,30 +314,30 @@ media_entity_graph_walk_next(struct media_entity_graph *graph) * top of the stack until no more entities on the level can be * found. */ - while (link_top(graph) < stack_top(graph)->num_links) { + while (link_top(graph) != &stack_top(graph)->links) { struct media_entity *entity = stack_top(graph); - struct media_link *link = &entity->links[link_top(graph)]; + struct media_link *link; struct media_entity *next; + link = list_entry(link_top(graph), typeof(*link), list); + /* The link is not enabled so we do not follow. */ if (!(link->flags & MEDIA_LNK_FL_ENABLED)) { - link_top(graph)++; + link_top(graph) = link_top(graph)->next; continue; } /* Get the entity in the other end of the link . */ next = media_entity_other(entity, link); - if (WARN_ON(next->id >= MEDIA_ENTITY_ENUM_MAX_ID)) - return NULL; /* Has the entity already been visited? */ - if (__test_and_set_bit(next->id, graph->entities)) { - link_top(graph)++; + if (media_entity_enum_test_and_set(&graph->ent_enum, next)) { + link_top(graph) = link_top(graph)->next; continue; } /* Push the new entity to stack and start over. */ - link_top(graph)++; + link_top(graph) = link_top(graph)->next; stack_push(graph, next); } @@ -208,39 +349,36 @@ EXPORT_SYMBOL_GPL(media_entity_graph_walk_next); * Pipeline management */ -/** - * media_entity_pipeline_start - Mark a pipeline as streaming - * @entity: Starting entity - * @pipe: Media pipeline to be assigned to all entities in the pipeline. - * - * Mark all entities connected to a given entity through enabled links, either - * directly or indirectly, as streaming. The given pipeline object is assigned to - * every entity in the pipeline and stored in the media_entity pipe field. - * - * Calls to this function can be nested, in which case the same number of - * media_entity_pipeline_stop() calls will be required to stop streaming. The - * pipeline pointer must be identical for all nested calls to - * media_entity_pipeline_start(). - */ __must_check int media_entity_pipeline_start(struct media_entity *entity, struct media_pipeline *pipe) { - struct media_device *mdev = entity->parent; - struct media_entity_graph graph; + struct media_device *mdev = entity->graph_obj.mdev; + struct media_entity_graph *graph = &pipe->graph; struct media_entity *entity_err = entity; + struct media_link *link; int ret; mutex_lock(&mdev->graph_mutex); - media_entity_graph_walk_start(&graph, entity); + if (!pipe->streaming_count++) { + ret = media_entity_graph_walk_init(&pipe->graph, mdev); + if (ret) + goto error_graph_walk_start; + } + + media_entity_graph_walk_start(&pipe->graph, entity); - while ((entity = media_entity_graph_walk_next(&graph))) { + while ((entity = media_entity_graph_walk_next(graph))) { DECLARE_BITMAP(active, MEDIA_ENTITY_MAX_PADS); DECLARE_BITMAP(has_no_links, MEDIA_ENTITY_MAX_PADS); - unsigned int i; entity->stream_count++; - WARN_ON(entity->pipe && entity->pipe != pipe); + + if (WARN_ON(entity->pipe && entity->pipe != pipe)) { + ret = -EBUSY; + goto error; + } + entity->pipe = pipe; /* Already streaming --- no need to check. */ @@ -253,8 +391,7 @@ __must_check int media_entity_pipeline_start(struct media_entity *entity, bitmap_zero(active, entity->num_pads); bitmap_fill(has_no_links, entity->num_pads); - for (i = 0; i < entity->num_links; i++) { - struct media_link *link = &entity->links[i]; + list_for_each_entry(link, &entity->links, list) { struct media_pad *pad = link->sink->entity == entity ? link->sink : link->source; @@ -280,7 +417,7 @@ __must_check int media_entity_pipeline_start(struct media_entity *entity, ret = entity->ops->link_validate(link); if (ret < 0 && ret != -ENOIOCTLCMD) { - dev_dbg(entity->parent->dev, + dev_dbg(entity->graph_obj.mdev->dev, "link validation failed for \"%s\":%u -> \"%s\":%u, error %d\n", link->source->entity->name, link->source->index, @@ -294,7 +431,7 @@ __must_check int media_entity_pipeline_start(struct media_entity *entity, if (!bitmap_full(active, entity->num_pads)) { ret = -EPIPE; - dev_dbg(entity->parent->dev, + dev_dbg(entity->graph_obj.mdev->dev, "\"%s\":%u must be connected by an enabled link\n", entity->name, (unsigned)find_first_zero_bit( @@ -312,9 +449,9 @@ error: * Link validation on graph failed. We revert what we did and * return the error. */ - media_entity_graph_walk_start(&graph, entity_err); + media_entity_graph_walk_start(graph, entity_err); - while ((entity_err = media_entity_graph_walk_next(&graph))) { + while ((entity_err = media_entity_graph_walk_next(graph))) { entity_err->stream_count--; if (entity_err->stream_count == 0) entity_err->pipe = NULL; @@ -327,39 +464,36 @@ error: break; } +error_graph_walk_start: + if (!--pipe->streaming_count) + media_entity_graph_walk_cleanup(graph); + mutex_unlock(&mdev->graph_mutex); return ret; } EXPORT_SYMBOL_GPL(media_entity_pipeline_start); -/** - * media_entity_pipeline_stop - Mark a pipeline as not streaming - * @entity: Starting entity - * - * Mark all entities connected to a given entity through enabled links, either - * directly or indirectly, as not streaming. The media_entity pipe field is - * reset to NULL. - * - * If multiple calls to media_entity_pipeline_start() have been made, the same - * number of calls to this function are required to mark the pipeline as not - * streaming. - */ void media_entity_pipeline_stop(struct media_entity *entity) { - struct media_device *mdev = entity->parent; - struct media_entity_graph graph; + struct media_device *mdev = entity->graph_obj.mdev; + struct media_entity_graph *graph = &entity->pipe->graph; + struct media_pipeline *pipe = entity->pipe; mutex_lock(&mdev->graph_mutex); - media_entity_graph_walk_start(&graph, entity); + WARN_ON(!pipe->streaming_count); + media_entity_graph_walk_start(graph, entity); - while ((entity = media_entity_graph_walk_next(&graph))) { + while ((entity = media_entity_graph_walk_next(graph))) { entity->stream_count--; if (entity->stream_count == 0) entity->pipe = NULL; } + if (!--pipe->streaming_count) + media_entity_graph_walk_cleanup(graph); + mutex_unlock(&mdev->graph_mutex); } EXPORT_SYMBOL_GPL(media_entity_pipeline_stop); @@ -368,44 +502,26 @@ EXPORT_SYMBOL_GPL(media_entity_pipeline_stop); * Module use count */ -/* - * media_entity_get - Get a reference to the parent module - * @entity: The entity - * - * Get a reference to the parent media device module. - * - * The function will return immediately if @entity is NULL. - * - * Return a pointer to the entity on success or NULL on failure. - */ struct media_entity *media_entity_get(struct media_entity *entity) { if (entity == NULL) return NULL; - if (entity->parent->dev && - !try_module_get(entity->parent->dev->driver->owner)) + if (entity->graph_obj.mdev->dev && + !try_module_get(entity->graph_obj.mdev->dev->driver->owner)) return NULL; return entity; } EXPORT_SYMBOL_GPL(media_entity_get); -/* - * media_entity_put - Release the reference to the parent module - * @entity: The entity - * - * Release the reference count acquired by media_entity_get(). - * - * The function will return immediately if @entity is NULL. - */ void media_entity_put(struct media_entity *entity) { if (entity == NULL) return; - if (entity->parent->dev) - module_put(entity->parent->dev->driver->owner); + if (entity->graph_obj.mdev->dev) + module_put(entity->graph_obj.mdev->dev->driver->owner); } EXPORT_SYMBOL_GPL(media_entity_put); @@ -413,29 +529,52 @@ EXPORT_SYMBOL_GPL(media_entity_put); * Links management */ -static struct media_link *media_entity_add_link(struct media_entity *entity) +static struct media_link *media_add_link(struct list_head *head) { - if (entity->num_links >= entity->max_links) { - struct media_link *links = entity->links; - unsigned int max_links = entity->max_links + 2; - unsigned int i; + struct media_link *link; + + link = kzalloc(sizeof(*link), GFP_KERNEL); + if (link == NULL) + return NULL; - links = krealloc(links, max_links * sizeof(*links), GFP_KERNEL); - if (links == NULL) - return NULL; + list_add_tail(&link->list, head); - for (i = 0; i < entity->num_links; i++) - links[i].reverse->reverse = &links[i]; + return link; +} - entity->max_links = max_links; - entity->links = links; - } +static void __media_entity_remove_link(struct media_entity *entity, + struct media_link *link) +{ + struct media_link *rlink, *tmp; + struct media_entity *remote; + + if (link->source->entity == entity) + remote = link->sink->entity; + else + remote = link->source->entity; - return &entity->links[entity->num_links++]; + list_for_each_entry_safe(rlink, tmp, &remote->links, list) { + if (rlink != link->reverse) + continue; + + if (link->source->entity == entity) + remote->num_backlinks--; + + /* Remove the remote link */ + list_del(&rlink->list); + media_gobj_destroy(&rlink->graph_obj); + kfree(rlink); + + if (--remote->num_links == 0) + break; + } + list_del(&link->list); + media_gobj_destroy(&link->graph_obj); + kfree(link); } int -media_entity_create_link(struct media_entity *source, u16 source_pad, +media_create_pad_link(struct media_entity *source, u16 source_pad, struct media_entity *sink, u16 sink_pad, u32 flags) { struct media_link *link; @@ -445,68 +584,118 @@ media_entity_create_link(struct media_entity *source, u16 source_pad, BUG_ON(source_pad >= source->num_pads); BUG_ON(sink_pad >= sink->num_pads); - link = media_entity_add_link(source); + link = media_add_link(&source->links); if (link == NULL) return -ENOMEM; link->source = &source->pads[source_pad]; link->sink = &sink->pads[sink_pad]; - link->flags = flags; + link->flags = flags & ~MEDIA_LNK_FL_INTERFACE_LINK; + + /* Initialize graph object embedded at the new link */ + media_gobj_create(source->graph_obj.mdev, MEDIA_GRAPH_LINK, + &link->graph_obj); /* Create the backlink. Backlinks are used to help graph traversal and * are not reported to userspace. */ - backlink = media_entity_add_link(sink); + backlink = media_add_link(&sink->links); if (backlink == NULL) { - source->num_links--; + __media_entity_remove_link(source, link); return -ENOMEM; } backlink->source = &source->pads[source_pad]; backlink->sink = &sink->pads[sink_pad]; backlink->flags = flags; + backlink->is_backlink = true; + + /* Initialize graph object embedded at the new link */ + media_gobj_create(sink->graph_obj.mdev, MEDIA_GRAPH_LINK, + &backlink->graph_obj); link->reverse = backlink; backlink->reverse = link; sink->num_backlinks++; + sink->num_links++; + source->num_links++; return 0; } -EXPORT_SYMBOL_GPL(media_entity_create_link); - -void __media_entity_remove_links(struct media_entity *entity) +EXPORT_SYMBOL_GPL(media_create_pad_link); + +int media_create_pad_links(const struct media_device *mdev, + const u32 source_function, + struct media_entity *source, + const u16 source_pad, + const u32 sink_function, + struct media_entity *sink, + const u16 sink_pad, + u32 flags, + const bool allow_both_undefined) { - unsigned int i; - - for (i = 0; i < entity->num_links; i++) { - struct media_link *link = &entity->links[i]; - struct media_entity *remote; - unsigned int r = 0; - - if (link->source->entity == entity) - remote = link->sink->entity; - else - remote = link->source->entity; - - while (r < remote->num_links) { - struct media_link *rlink = &remote->links[r]; + struct media_entity *entity; + unsigned function; + int ret; - if (rlink != link->reverse) { - r++; + /* Trivial case: 1:1 relation */ + if (source && sink) + return media_create_pad_link(source, source_pad, + sink, sink_pad, flags); + + /* Worse case scenario: n:n relation */ + if (!source && !sink) { + if (!allow_both_undefined) + return 0; + media_device_for_each_entity(source, mdev) { + if (source->function != source_function) continue; + media_device_for_each_entity(sink, mdev) { + if (sink->function != sink_function) + continue; + ret = media_create_pad_link(source, source_pad, + sink, sink_pad, + flags); + if (ret) + return ret; + flags &= ~(MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); } + } + return 0; + } - if (link->source->entity == entity) - remote->num_backlinks--; + /* Handle 1:n and n:1 cases */ + if (source) + function = sink_function; + else + function = source_function; - if (--remote->num_links == 0) - break; + media_device_for_each_entity(entity, mdev) { + if (entity->function != function) + continue; - /* Insert last entry in place of the dropped link. */ - *rlink = remote->links[remote->num_links]; - } + if (source) + ret = media_create_pad_link(source, source_pad, + entity, sink_pad, flags); + else + ret = media_create_pad_link(entity, source_pad, + sink, sink_pad, flags); + if (ret) + return ret; + flags &= ~(MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE); } + return 0; +} +EXPORT_SYMBOL_GPL(media_create_pad_links); + +void __media_entity_remove_links(struct media_entity *entity) +{ + struct media_link *link, *tmp; + + list_for_each_entry_safe(link, tmp, &entity->links, list) + __media_entity_remove_link(entity, link); entity->num_links = 0; entity->num_backlinks = 0; @@ -515,13 +704,15 @@ EXPORT_SYMBOL_GPL(__media_entity_remove_links); void media_entity_remove_links(struct media_entity *entity) { + struct media_device *mdev = entity->graph_obj.mdev; + /* Do nothing if the entity is not registered. */ - if (entity->parent == NULL) + if (mdev == NULL) return; - mutex_lock(&entity->parent->graph_mutex); + spin_lock(&mdev->lock); __media_entity_remove_links(entity); - mutex_unlock(&entity->parent->graph_mutex); + spin_unlock(&mdev->lock); } EXPORT_SYMBOL_GPL(media_entity_remove_links); @@ -549,20 +740,6 @@ static int __media_entity_setup_link_notify(struct media_link *link, u32 flags) return 0; } -/** - * __media_entity_setup_link - Configure a media link - * @link: The link being configured - * @flags: Link configuration flags - * - * The bulk of link setup is handled by the two entities connected through the - * link. This function notifies both entities of the link configuration change. - * - * If the link is immutable or if the current and new configuration are - * identical, return immediately. - * - * The user is expected to hold link->source->parent->mutex. If not, - * media_entity_setup_link() should be used instead. - */ int __media_entity_setup_link(struct media_link *link, u32 flags) { const u32 mask = MEDIA_LNK_FL_ENABLED; @@ -590,7 +767,7 @@ int __media_entity_setup_link(struct media_link *link, u32 flags) (source->stream_count || sink->stream_count)) return -EBUSY; - mdev = source->parent; + mdev = source->graph_obj.mdev; if (mdev->link_notify) { ret = mdev->link_notify(link, flags, @@ -611,31 +788,20 @@ int media_entity_setup_link(struct media_link *link, u32 flags) { int ret; - mutex_lock(&link->source->entity->parent->graph_mutex); + mutex_lock(&link->graph_obj.mdev->graph_mutex); ret = __media_entity_setup_link(link, flags); - mutex_unlock(&link->source->entity->parent->graph_mutex); + mutex_unlock(&link->graph_obj.mdev->graph_mutex); return ret; } EXPORT_SYMBOL_GPL(media_entity_setup_link); -/** - * media_entity_find_link - Find a link between two pads - * @source: Source pad - * @sink: Sink pad - * - * Return a pointer to the link between the two entities. If no such link - * exists, return NULL. - */ struct media_link * media_entity_find_link(struct media_pad *source, struct media_pad *sink) { struct media_link *link; - unsigned int i; - - for (i = 0; i < source->entity->num_links; ++i) { - link = &source->entity->links[i]; + list_for_each_entry(link, &source->entity->links, list) { if (link->source->entity == source->entity && link->source->index == source->index && link->sink->entity == sink->entity && @@ -647,23 +813,11 @@ media_entity_find_link(struct media_pad *source, struct media_pad *sink) } EXPORT_SYMBOL_GPL(media_entity_find_link); -/** - * media_entity_remote_pad - Find the pad at the remote end of a link - * @pad: Pad at the local end of the link - * - * Search for a remote pad connected to the given pad by iterating over all - * links originating or terminating at that pad until an enabled link is found. - * - * Return a pointer to the pad at the remote end of the first found enabled - * link, or NULL if no enabled link has been found. - */ struct media_pad *media_entity_remote_pad(struct media_pad *pad) { - unsigned int i; - - for (i = 0; i < pad->entity->num_links; i++) { - struct media_link *link = &pad->entity->links[i]; + struct media_link *link; + list_for_each_entry(link, &pad->entity->links, list) { if (!(link->flags & MEDIA_LNK_FL_ENABLED)) continue; @@ -678,3 +832,113 @@ struct media_pad *media_entity_remote_pad(struct media_pad *pad) } EXPORT_SYMBOL_GPL(media_entity_remote_pad); + +static void media_interface_init(struct media_device *mdev, + struct media_interface *intf, + u32 gobj_type, + u32 intf_type, u32 flags) +{ + intf->type = intf_type; + intf->flags = flags; + INIT_LIST_HEAD(&intf->links); + + media_gobj_create(mdev, gobj_type, &intf->graph_obj); +} + +/* Functions related to the media interface via device nodes */ + +struct media_intf_devnode *media_devnode_create(struct media_device *mdev, + u32 type, u32 flags, + u32 major, u32 minor) +{ + struct media_intf_devnode *devnode; + + devnode = kzalloc(sizeof(*devnode), GFP_KERNEL); + if (!devnode) + return NULL; + + devnode->major = major; + devnode->minor = minor; + + media_interface_init(mdev, &devnode->intf, MEDIA_GRAPH_INTF_DEVNODE, + type, flags); + + return devnode; +} +EXPORT_SYMBOL_GPL(media_devnode_create); + +void media_devnode_remove(struct media_intf_devnode *devnode) +{ + media_remove_intf_links(&devnode->intf); + media_gobj_destroy(&devnode->intf.graph_obj); + kfree(devnode); +} +EXPORT_SYMBOL_GPL(media_devnode_remove); + +struct media_link *media_create_intf_link(struct media_entity *entity, + struct media_interface *intf, + u32 flags) +{ + struct media_link *link; + + link = media_add_link(&intf->links); + if (link == NULL) + return NULL; + + link->intf = intf; + link->entity = entity; + link->flags = flags | MEDIA_LNK_FL_INTERFACE_LINK; + + /* Initialize graph object embedded at the new link */ + media_gobj_create(intf->graph_obj.mdev, MEDIA_GRAPH_LINK, + &link->graph_obj); + + return link; +} +EXPORT_SYMBOL_GPL(media_create_intf_link); + +void __media_remove_intf_link(struct media_link *link) +{ + list_del(&link->list); + media_gobj_destroy(&link->graph_obj); + kfree(link); +} +EXPORT_SYMBOL_GPL(__media_remove_intf_link); + +void media_remove_intf_link(struct media_link *link) +{ + struct media_device *mdev = link->graph_obj.mdev; + + /* Do nothing if the intf is not registered. */ + if (mdev == NULL) + return; + + spin_lock(&mdev->lock); + __media_remove_intf_link(link); + spin_unlock(&mdev->lock); +} +EXPORT_SYMBOL_GPL(media_remove_intf_link); + +void __media_remove_intf_links(struct media_interface *intf) +{ + struct media_link *link, *tmp; + + list_for_each_entry_safe(link, tmp, &intf->links, list) + __media_remove_intf_link(link); + +} +EXPORT_SYMBOL_GPL(__media_remove_intf_links); + +void media_remove_intf_links(struct media_interface *intf) +{ + struct media_device *mdev = intf->graph_obj.mdev; + + /* Do nothing if the intf is not registered. */ + if (mdev == NULL) + return; + + spin_lock(&mdev->lock); + __media_remove_intf_links(intf); + spin_unlock(&mdev->lock); +} +EXPORT_SYMBOL_GPL(media_remove_intf_links); diff --git a/drivers/media/pci/bt8xx/dst_ca.c b/drivers/media/pci/bt8xx/dst_ca.c index c5cc14e..da8b414 100644 --- a/drivers/media/pci/bt8xx/dst_ca.c +++ b/drivers/media/pci/bt8xx/dst_ca.c @@ -705,7 +705,8 @@ struct dvb_device *dst_ca_attach(struct dst_state *dst, struct dvb_adapter *dvb_ struct dvb_device *dvbdev; dprintk(verbose, DST_CA_ERROR, 1, "registering DST-CA device"); - if (dvb_register_device(dvb_adapter, &dvbdev, &dvbdev_ca, dst, DVB_DEVICE_CA) == 0) { + if (dvb_register_device(dvb_adapter, &dvbdev, &dvbdev_ca, dst, + DVB_DEVICE_CA, 0) == 0) { dst->dst_ca = dvbdev; return dst->dst_ca; } diff --git a/drivers/media/pci/ddbridge/ddbridge-core.c b/drivers/media/pci/ddbridge/ddbridge-core.c index fba5b40..9d5b314 100644 --- a/drivers/media/pci/ddbridge/ddbridge-core.c +++ b/drivers/media/pci/ddbridge/ddbridge-core.c @@ -1065,7 +1065,7 @@ static int ddb_ci_attach(struct ddb_port *port) port->en, 0, 1); ret = dvb_register_device(&port->output->adap, &port->output->dev, &dvbdev_ci, (void *) port->output, - DVB_DEVICE_SEC); + DVB_DEVICE_SEC, 0); return ret; } diff --git a/drivers/media/pci/ngene/ngene-core.c b/drivers/media/pci/ngene/ngene-core.c index 1b92d83..4e924e2 100644 --- a/drivers/media/pci/ngene/ngene-core.c +++ b/drivers/media/pci/ngene/ngene-core.c @@ -1513,7 +1513,7 @@ static int init_channel(struct ngene_channel *chan) set_transfer(&chan->dev->channel[2], 1); dvb_register_device(adapter, &chan->ci_dev, &ngene_dvbdev_ci, (void *) chan, - DVB_DEVICE_SEC); + DVB_DEVICE_SEC, 0); if (!chan->ci_dev) goto err; } diff --git a/drivers/media/pci/ttpci/av7110.c b/drivers/media/pci/ttpci/av7110.c index 5e18b67..a69dc6a 100644 --- a/drivers/media/pci/ttpci/av7110.c +++ b/drivers/media/pci/ttpci/av7110.c @@ -1358,7 +1358,7 @@ static int av7110_register(struct av7110 *av7110) #ifdef CONFIG_DVB_AV7110_OSD dvb_register_device(&av7110->dvb_adapter, &av7110->osd_dev, - &dvbdev_osd, av7110, DVB_DEVICE_OSD); + &dvbdev_osd, av7110, DVB_DEVICE_OSD, 0); #endif dvb_net_init(&av7110->dvb_adapter, &av7110->dvb_net, &dvbdemux->dmx); diff --git a/drivers/media/pci/ttpci/av7110_av.c b/drivers/media/pci/ttpci/av7110_av.c index 6fc748e..26c5696 100644 --- a/drivers/media/pci/ttpci/av7110_av.c +++ b/drivers/media/pci/ttpci/av7110_av.c @@ -1594,10 +1594,10 @@ int av7110_av_register(struct av7110 *av7110) memset(&av7110->video_size, 0, sizeof (video_size_t)); dvb_register_device(&av7110->dvb_adapter, &av7110->video_dev, - &dvbdev_video, av7110, DVB_DEVICE_VIDEO); + &dvbdev_video, av7110, DVB_DEVICE_VIDEO, 0); dvb_register_device(&av7110->dvb_adapter, &av7110->audio_dev, - &dvbdev_audio, av7110, DVB_DEVICE_AUDIO); + &dvbdev_audio, av7110, DVB_DEVICE_AUDIO, 0); return 0; } diff --git a/drivers/media/pci/ttpci/av7110_ca.c b/drivers/media/pci/ttpci/av7110_ca.c index bc4c65f..96a130f 100644 --- a/drivers/media/pci/ttpci/av7110_ca.c +++ b/drivers/media/pci/ttpci/av7110_ca.c @@ -378,7 +378,7 @@ static struct dvb_device dvbdev_ca = { int av7110_ca_register(struct av7110 *av7110) { return dvb_register_device(&av7110->dvb_adapter, &av7110->ca_dev, - &dvbdev_ca, av7110, DVB_DEVICE_CA); + &dvbdev_ca, av7110, DVB_DEVICE_CA, 0); } void av7110_ca_unregister(struct av7110 *av7110) diff --git a/drivers/media/platform/exynos4-is/common.c b/drivers/media/platform/exynos4-is/common.c index b6716c5..b90f5bb 100644 --- a/drivers/media/platform/exynos4-is/common.c +++ b/drivers/media/platform/exynos4-is/common.c @@ -22,8 +22,7 @@ struct v4l2_subdev *fimc_find_remote_sensor(struct media_entity *entity) while (pad->flags & MEDIA_PAD_FL_SINK) { /* source pad */ pad = media_entity_remote_pad(pad); - if (pad == NULL || - media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) break; sd = media_entity_to_v4l2_subdev(pad->entity); diff --git a/drivers/media/platform/exynos4-is/fimc-capture.c b/drivers/media/platform/exynos4-is/fimc-capture.c index 0d549a6..bf47d3b 100644 --- a/drivers/media/platform/exynos4-is/fimc-capture.c +++ b/drivers/media/platform/exynos4-is/fimc-capture.c @@ -1136,8 +1136,7 @@ static int fimc_pipeline_validate(struct fimc_dev *fimc) } } - if (src_pad == NULL || - media_entity_type(src_pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + if (!src_pad || !is_media_entity_v4l2_subdev(src_pad->entity)) break; /* Don't call FIMC subdev operation to avoid nested locking */ @@ -1392,7 +1391,7 @@ static int fimc_link_setup(struct media_entity *entity, struct fimc_vid_cap *vc = &fimc->vid_cap; struct v4l2_subdev *sensor; - if (media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + if (!is_media_entity_v4l2_subdev(remote->entity)) return -EINVAL; if (WARN_ON(fimc == NULL)) @@ -1800,7 +1799,7 @@ static int fimc_register_capture_device(struct fimc_dev *fimc, vid_cap->wb_fmt.code = fmt->mbus_code; vid_cap->vd_pad.flags = MEDIA_PAD_FL_SINK; - ret = media_entity_init(&vfd->entity, 1, &vid_cap->vd_pad, 0); + ret = media_entity_pads_init(&vfd->entity, 1, &vid_cap->vd_pad); if (ret) goto err_free_ctx; @@ -1892,8 +1891,8 @@ int fimc_initialize_capture_subdev(struct fimc_dev *fimc) fimc->vid_cap.sd_pads[FIMC_SD_PAD_SINK_CAM].flags = MEDIA_PAD_FL_SINK; fimc->vid_cap.sd_pads[FIMC_SD_PAD_SINK_FIFO].flags = MEDIA_PAD_FL_SINK; fimc->vid_cap.sd_pads[FIMC_SD_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_init(&sd->entity, FIMC_SD_PADS_NUM, - fimc->vid_cap.sd_pads, 0); + ret = media_entity_pads_init(&sd->entity, FIMC_SD_PADS_NUM, + fimc->vid_cap.sd_pads); if (ret) return ret; diff --git a/drivers/media/platform/exynos4-is/fimc-isp-video.c b/drivers/media/platform/exynos4-is/fimc-isp-video.c index 0dd22ec..bf9261e 100644 --- a/drivers/media/platform/exynos4-is/fimc-isp-video.c +++ b/drivers/media/platform/exynos4-is/fimc-isp-video.c @@ -287,7 +287,7 @@ static int isp_video_open(struct file *file) goto rel_fh; if (v4l2_fh_is_singular_file(file)) { - mutex_lock(&me->parent->graph_mutex); + mutex_lock(&me->graph_obj.mdev->graph_mutex); ret = fimc_pipeline_call(ve, open, me, true); @@ -295,7 +295,7 @@ static int isp_video_open(struct file *file) if (ret == 0) me->use_count++; - mutex_unlock(&me->parent->graph_mutex); + mutex_unlock(&me->graph_obj.mdev->graph_mutex); } if (!ret) goto unlock; @@ -311,7 +311,7 @@ static int isp_video_release(struct file *file) struct fimc_isp *isp = video_drvdata(file); struct fimc_is_video *ivc = &isp->video_capture; struct media_entity *entity = &ivc->ve.vdev.entity; - struct media_device *mdev = entity->parent; + struct media_device *mdev = entity->graph_obj.mdev; mutex_lock(&isp->video_lock); @@ -466,8 +466,7 @@ static int isp_video_pipeline_validate(struct fimc_isp *isp) /* Retrieve format at the source pad */ pad = media_entity_remote_pad(pad); - if (pad == NULL || - media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) break; sd = media_entity_to_v4l2_subdev(pad->entity); @@ -617,7 +616,7 @@ int fimc_isp_video_device_register(struct fimc_isp *isp, vdev->lock = &isp->video_lock; iv->pad.flags = MEDIA_PAD_FL_SINK; - ret = media_entity_init(&vdev->entity, 1, &iv->pad, 0); + ret = media_entity_pads_init(&vdev->entity, 1, &iv->pad); if (ret < 0) return ret; diff --git a/drivers/media/platform/exynos4-is/fimc-isp.c b/drivers/media/platform/exynos4-is/fimc-isp.c index 5d78f57..293b807 100644 --- a/drivers/media/platform/exynos4-is/fimc-isp.c +++ b/drivers/media/platform/exynos4-is/fimc-isp.c @@ -708,8 +708,8 @@ int fimc_isp_subdev_create(struct fimc_isp *isp) isp->subdev_pads[FIMC_ISP_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK; isp->subdev_pads[FIMC_ISP_SD_PAD_SRC_FIFO].flags = MEDIA_PAD_FL_SOURCE; isp->subdev_pads[FIMC_ISP_SD_PAD_SRC_DMA].flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_init(&sd->entity, FIMC_ISP_SD_PADS_NUM, - isp->subdev_pads, 0); + ret = media_entity_pads_init(&sd->entity, FIMC_ISP_SD_PADS_NUM, + isp->subdev_pads); if (ret) return ret; diff --git a/drivers/media/platform/exynos4-is/fimc-lite.c b/drivers/media/platform/exynos4-is/fimc-lite.c index 639ee71..e856491 100644 --- a/drivers/media/platform/exynos4-is/fimc-lite.c +++ b/drivers/media/platform/exynos4-is/fimc-lite.c @@ -494,7 +494,7 @@ static int fimc_lite_open(struct file *file) atomic_read(&fimc->out_path) != FIMC_IO_DMA) goto unlock; - mutex_lock(&me->parent->graph_mutex); + mutex_lock(&me->graph_obj.mdev->graph_mutex); ret = fimc_pipeline_call(&fimc->ve, open, me, true); @@ -502,7 +502,7 @@ static int fimc_lite_open(struct file *file) if (ret == 0) me->use_count++; - mutex_unlock(&me->parent->graph_mutex); + mutex_unlock(&me->graph_obj.mdev->graph_mutex); if (!ret) { fimc_lite_clear_event_counters(fimc); @@ -535,9 +535,9 @@ static int fimc_lite_release(struct file *file) fimc_pipeline_call(&fimc->ve, close); clear_bit(ST_FLITE_IN_USE, &fimc->state); - mutex_lock(&entity->parent->graph_mutex); + mutex_lock(&entity->graph_obj.mdev->graph_mutex); entity->use_count--; - mutex_unlock(&entity->parent->graph_mutex); + mutex_unlock(&entity->graph_obj.mdev->graph_mutex); } _vb2_fop_release(file, NULL); @@ -808,8 +808,7 @@ static int fimc_pipeline_validate(struct fimc_lite *fimc) } /* Retrieve format at the source pad */ pad = media_entity_remote_pad(pad); - if (pad == NULL || - media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) break; sd = media_entity_to_v4l2_subdev(pad->entity); @@ -982,7 +981,6 @@ static int fimc_lite_link_setup(struct media_entity *entity, { struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); struct fimc_lite *fimc = v4l2_get_subdevdata(sd); - unsigned int remote_ent_type = media_entity_type(remote->entity); int ret = 0; if (WARN_ON(fimc == NULL)) @@ -994,7 +992,7 @@ static int fimc_lite_link_setup(struct media_entity *entity, switch (local->index) { case FLITE_SD_PAD_SINK: - if (remote_ent_type != MEDIA_ENT_T_V4L2_SUBDEV) { + if (!is_media_entity_v4l2_subdev(remote->entity)) { ret = -EINVAL; break; } @@ -1012,7 +1010,7 @@ static int fimc_lite_link_setup(struct media_entity *entity, case FLITE_SD_PAD_SOURCE_DMA: if (!(flags & MEDIA_LNK_FL_ENABLED)) atomic_set(&fimc->out_path, FIMC_IO_NONE); - else if (remote_ent_type == MEDIA_ENT_T_DEVNODE) + else if (is_media_entity_v4l2_io(remote->entity)) atomic_set(&fimc->out_path, FIMC_IO_DMA); else ret = -EINVAL; @@ -1021,7 +1019,7 @@ static int fimc_lite_link_setup(struct media_entity *entity, case FLITE_SD_PAD_SOURCE_ISP: if (!(flags & MEDIA_LNK_FL_ENABLED)) atomic_set(&fimc->out_path, FIMC_IO_NONE); - else if (remote_ent_type == MEDIA_ENT_T_V4L2_SUBDEV) + else if (is_media_entity_v4l2_subdev(remote->entity)) atomic_set(&fimc->out_path, FIMC_IO_ISP); else ret = -EINVAL; @@ -1316,7 +1314,7 @@ static int fimc_lite_subdev_registered(struct v4l2_subdev *sd) return ret; fimc->vd_pad.flags = MEDIA_PAD_FL_SINK; - ret = media_entity_init(&vfd->entity, 1, &fimc->vd_pad, 0); + ret = media_entity_pads_init(&vfd->entity, 1, &fimc->vd_pad); if (ret < 0) return ret; @@ -1430,8 +1428,8 @@ static int fimc_lite_create_capture_subdev(struct fimc_lite *fimc) fimc->subdev_pads[FLITE_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK; fimc->subdev_pads[FLITE_SD_PAD_SOURCE_DMA].flags = MEDIA_PAD_FL_SOURCE; fimc->subdev_pads[FLITE_SD_PAD_SOURCE_ISP].flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_init(&sd->entity, FLITE_SD_PADS_NUM, - fimc->subdev_pads, 0); + ret = media_entity_pads_init(&sd->entity, FLITE_SD_PADS_NUM, + fimc->subdev_pads); if (ret) return ret; diff --git a/drivers/media/platform/exynos4-is/fimc-m2m.c b/drivers/media/platform/exynos4-is/fimc-m2m.c index 5aa857c..55ec4c9 100644 --- a/drivers/media/platform/exynos4-is/fimc-m2m.c +++ b/drivers/media/platform/exynos4-is/fimc-m2m.c @@ -739,7 +739,7 @@ int fimc_register_m2m_device(struct fimc_dev *fimc, return PTR_ERR(fimc->m2m.m2m_dev); } - ret = media_entity_init(&vfd->entity, 0, NULL, 0); + ret = media_entity_pads_init(&vfd->entity, 0, NULL); if (ret) goto err_me; diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c index 9481ce3..f3b2dd3 100644 --- a/drivers/media/platform/exynos4-is/media-dev.c +++ b/drivers/media/platform/exynos4-is/media-dev.c @@ -88,8 +88,7 @@ static void fimc_pipeline_prepare(struct fimc_pipeline *p, break; } - if (pad == NULL || - media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) break; sd = media_entity_to_v4l2_subdev(pad->entity); @@ -729,7 +728,7 @@ static int __fimc_md_create_fimc_sink_links(struct fimc_md *fmd, flags = ((1 << i) & link_mask) ? MEDIA_LNK_FL_ENABLED : 0; sink = &fmd->fimc[i]->vid_cap.subdev.entity; - ret = media_entity_create_link(source, pad, sink, + ret = media_create_pad_link(source, pad, sink, FIMC_SD_PAD_SINK_CAM, flags); if (ret) return ret; @@ -749,7 +748,7 @@ static int __fimc_md_create_fimc_sink_links(struct fimc_md *fmd, continue; sink = &fmd->fimc_lite[i]->subdev.entity; - ret = media_entity_create_link(source, pad, sink, + ret = media_create_pad_link(source, pad, sink, FLITE_SD_PAD_SINK, 0); if (ret) return ret; @@ -781,13 +780,13 @@ static int __fimc_md_create_flite_source_links(struct fimc_md *fmd) source = &fimc->subdev.entity; sink = &fimc->ve.vdev.entity; /* FIMC-LITE's subdev and video node */ - ret = media_entity_create_link(source, FLITE_SD_PAD_SOURCE_DMA, + ret = media_create_pad_link(source, FLITE_SD_PAD_SOURCE_DMA, sink, 0, 0); if (ret) break; /* Link from FIMC-LITE to IS-ISP subdev */ sink = &fmd->fimc_is->isp.subdev.entity; - ret = media_entity_create_link(source, FLITE_SD_PAD_SOURCE_ISP, + ret = media_create_pad_link(source, FLITE_SD_PAD_SOURCE_ISP, sink, 0, 0); if (ret) break; @@ -811,7 +810,7 @@ static int __fimc_md_create_fimc_is_links(struct fimc_md *fmd) /* Link from FIMC-IS-ISP subdev to FIMC */ sink = &fmd->fimc[i]->vid_cap.subdev.entity; - ret = media_entity_create_link(source, FIMC_ISP_SD_PAD_SRC_FIFO, + ret = media_create_pad_link(source, FIMC_ISP_SD_PAD_SRC_FIFO, sink, FIMC_SD_PAD_SINK_FIFO, 0); if (ret) return ret; @@ -824,7 +823,7 @@ static int __fimc_md_create_fimc_is_links(struct fimc_md *fmd) if (sink->num_pads == 0) return 0; - return media_entity_create_link(source, FIMC_ISP_SD_PAD_SRC_DMA, + return media_create_pad_link(source, FIMC_ISP_SD_PAD_SRC_DMA, sink, 0, 0); } @@ -873,7 +872,7 @@ static int fimc_md_create_links(struct fimc_md *fmd) return -EINVAL; pad = sensor->entity.num_pads - 1; - ret = media_entity_create_link(&sensor->entity, pad, + ret = media_create_pad_link(&sensor->entity, pad, &csis->entity, CSIS_PAD_SINK, MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); @@ -927,7 +926,7 @@ static int fimc_md_create_links(struct fimc_md *fmd) source = &fmd->fimc[i]->vid_cap.subdev.entity; sink = &fmd->fimc[i]->vid_cap.ve.vdev.entity; - ret = media_entity_create_link(source, FIMC_SD_PAD_SOURCE, + ret = media_create_pad_link(source, FIMC_SD_PAD_SOURCE, sink, 0, flags); if (ret) break; @@ -1046,11 +1045,11 @@ static int __fimc_md_modify_pipeline(struct media_entity *entity, bool enable) return ret; } -/* Locking: called with entity->parent->graph_mutex mutex held. */ -static int __fimc_md_modify_pipelines(struct media_entity *entity, bool enable) +/* Locking: called with entity->graph_obj.mdev->graph_mutex mutex held. */ +static int __fimc_md_modify_pipelines(struct media_entity *entity, bool enable, + struct media_entity_graph *graph) { struct media_entity *entity_err = entity; - struct media_entity_graph graph; int ret; /* @@ -1059,10 +1058,10 @@ static int __fimc_md_modify_pipelines(struct media_entity *entity, bool enable) * through active links. This is needed as we cannot power on/off the * subdevs in random order. */ - media_entity_graph_walk_start(&graph, entity); + media_entity_graph_walk_start(graph, entity); - while ((entity = media_entity_graph_walk_next(&graph))) { - if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE) + while ((entity = media_entity_graph_walk_next(graph))) { + if (!is_media_entity_v4l2_io(entity)) continue; ret = __fimc_md_modify_pipeline(entity, enable); @@ -1072,11 +1071,12 @@ static int __fimc_md_modify_pipelines(struct media_entity *entity, bool enable) } return 0; - err: - media_entity_graph_walk_start(&graph, entity_err); - while ((entity_err = media_entity_graph_walk_next(&graph))) { - if (media_entity_type(entity_err) != MEDIA_ENT_T_DEVNODE) +err: + media_entity_graph_walk_start(graph, entity_err); + + while ((entity_err = media_entity_graph_walk_next(graph))) { + if (!is_media_entity_v4l2_io(entity_err)) continue; __fimc_md_modify_pipeline(entity_err, !enable); @@ -1091,21 +1091,29 @@ static int __fimc_md_modify_pipelines(struct media_entity *entity, bool enable) static int fimc_md_link_notify(struct media_link *link, unsigned int flags, unsigned int notification) { + struct media_entity_graph *graph = + &container_of(link->graph_obj.mdev, struct fimc_md, + media_dev)->link_setup_graph; struct media_entity *sink = link->sink->entity; int ret = 0; /* Before link disconnection */ if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH) { + ret = media_entity_graph_walk_init(graph, + link->graph_obj.mdev); + if (ret) + return ret; if (!(flags & MEDIA_LNK_FL_ENABLED)) - ret = __fimc_md_modify_pipelines(sink, false); + ret = __fimc_md_modify_pipelines(sink, false, graph); #if 0 else /* TODO: Link state change validation */ #endif /* After link activation */ - } else if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH && - (link->flags & MEDIA_LNK_FL_ENABLED)) { - ret = __fimc_md_modify_pipelines(sink, true); + } else if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH) { + if (link->flags & MEDIA_LNK_FL_ENABLED) + ret = __fimc_md_modify_pipelines(sink, true, graph); + media_entity_graph_walk_cleanup(graph); } return ret ? -EPIPE : 0; @@ -1314,7 +1322,10 @@ static int subdev_notifier_complete(struct v4l2_async_notifier *notifier) ret = v4l2_device_register_subdev_nodes(&fmd->v4l2_dev); unlock: mutex_unlock(&fmd->media_dev.graph_mutex); - return ret; + if (ret < 0) + return ret; + + return media_device_register(&fmd->media_dev); } static int fimc_md_probe(struct platform_device *pdev) @@ -1345,18 +1356,14 @@ static int fimc_md_probe(struct platform_device *pdev) fmd->use_isp = fimc_md_is_isp_available(dev->of_node); fmd->user_subdev_api = true; + media_device_init(&fmd->media_dev); + ret = v4l2_device_register(dev, &fmd->v4l2_dev); if (ret < 0) { v4l2_err(v4l2_dev, "Failed to register v4l2_device: %d\n", ret); return ret; } - ret = media_device_register(&fmd->media_dev); - if (ret < 0) { - v4l2_err(v4l2_dev, "Failed to register media device: %d\n", ret); - goto err_v4l2_dev; - } - ret = fimc_md_get_clocks(fmd); if (ret) goto err_md; @@ -1425,8 +1432,7 @@ err_clk: err_m_ent: fimc_md_unregister_entities(fmd); err_md: - media_device_unregister(&fmd->media_dev); -err_v4l2_dev: + media_device_cleanup(&fmd->media_dev); v4l2_device_unregister(&fmd->v4l2_dev); return ret; } @@ -1446,6 +1452,7 @@ static int fimc_md_remove(struct platform_device *pdev) fimc_md_unregister_entities(fmd); fimc_md_pipelines_free(fmd); media_device_unregister(&fmd->media_dev); + media_device_cleanup(&fmd->media_dev); fimc_md_put_clocks(fmd); return 0; diff --git a/drivers/media/platform/exynos4-is/media-dev.h b/drivers/media/platform/exynos4-is/media-dev.h index 93a96126..ed122cb 100644 --- a/drivers/media/platform/exynos4-is/media-dev.h +++ b/drivers/media/platform/exynos4-is/media-dev.h @@ -154,6 +154,7 @@ struct fimc_md { bool user_subdev_api; spinlock_t slock; struct list_head pipelines; + struct media_entity_graph link_setup_graph; }; static inline @@ -164,8 +165,8 @@ struct fimc_sensor_info *source_to_sensor_info(struct fimc_source_info *si) static inline struct fimc_md *entity_to_fimc_mdev(struct media_entity *me) { - return me->parent == NULL ? NULL : - container_of(me->parent, struct fimc_md, media_dev); + return me->graph_obj.mdev == NULL ? NULL : + container_of(me->graph_obj.mdev, struct fimc_md, media_dev); } static inline struct fimc_md *notifier_to_fimc_md(struct v4l2_async_notifier *n) @@ -175,12 +176,12 @@ static inline struct fimc_md *notifier_to_fimc_md(struct v4l2_async_notifier *n) static inline void fimc_md_graph_lock(struct exynos_video_entity *ve) { - mutex_lock(&ve->vdev.entity.parent->graph_mutex); + mutex_lock(&ve->vdev.entity.graph_obj.mdev->graph_mutex); } static inline void fimc_md_graph_unlock(struct exynos_video_entity *ve) { - mutex_unlock(&ve->vdev.entity.parent->graph_mutex); + mutex_unlock(&ve->vdev.entity.graph_obj.mdev->graph_mutex); } int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on); diff --git a/drivers/media/platform/exynos4-is/mipi-csis.c b/drivers/media/platform/exynos4-is/mipi-csis.c index ff5dabf..ac5e50e 100644 --- a/drivers/media/platform/exynos4-is/mipi-csis.c +++ b/drivers/media/platform/exynos4-is/mipi-csis.c @@ -866,8 +866,8 @@ static int s5pcsis_probe(struct platform_device *pdev) state->pads[CSIS_PAD_SINK].flags = MEDIA_PAD_FL_SINK; state->pads[CSIS_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_init(&state->sd.entity, - CSIS_PADS_NUM, state->pads, 0); + ret = media_entity_pads_init(&state->sd.entity, + CSIS_PADS_NUM, state->pads); if (ret < 0) goto e_clkdis; diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c index 56e683b..0bcfa55 100644 --- a/drivers/media/platform/omap3isp/isp.c +++ b/drivers/media/platform/omap3isp/isp.c @@ -683,15 +683,15 @@ static irqreturn_t isp_isr(int irq, void *_isp) * * Return the total number of users of all video device nodes in the pipeline. */ -static int isp_pipeline_pm_use_count(struct media_entity *entity) +static int isp_pipeline_pm_use_count(struct media_entity *entity, + struct media_entity_graph *graph) { - struct media_entity_graph graph; int use = 0; - media_entity_graph_walk_start(&graph, entity); + media_entity_graph_walk_start(graph, entity); - while ((entity = media_entity_graph_walk_next(&graph))) { - if (media_entity_type(entity) == MEDIA_ENT_T_DEVNODE) + while ((entity = media_entity_graph_walk_next(graph))) { + if (is_media_entity_v4l2_io(entity)) use += entity->use_count; } @@ -714,7 +714,7 @@ static int isp_pipeline_pm_power_one(struct media_entity *entity, int change) struct v4l2_subdev *subdev; int ret; - subdev = media_entity_type(entity) == MEDIA_ENT_T_V4L2_SUBDEV + subdev = is_media_entity_v4l2_subdev(entity) ? media_entity_to_v4l2_subdev(entity) : NULL; if (entity->use_count == 0 && change > 0 && subdev != NULL) { @@ -742,29 +742,29 @@ static int isp_pipeline_pm_power_one(struct media_entity *entity, int change) * * Return 0 on success or a negative error code on failure. */ -static int isp_pipeline_pm_power(struct media_entity *entity, int change) +static int isp_pipeline_pm_power(struct media_entity *entity, int change, + struct media_entity_graph *graph) { - struct media_entity_graph graph; struct media_entity *first = entity; int ret = 0; if (!change) return 0; - media_entity_graph_walk_start(&graph, entity); + media_entity_graph_walk_start(graph, entity); - while (!ret && (entity = media_entity_graph_walk_next(&graph))) - if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE) + while (!ret && (entity = media_entity_graph_walk_next(graph))) + if (is_media_entity_v4l2_subdev(entity)) ret = isp_pipeline_pm_power_one(entity, change); if (!ret) - return 0; + return ret; - media_entity_graph_walk_start(&graph, first); + media_entity_graph_walk_start(graph, first); - while ((first = media_entity_graph_walk_next(&graph)) + while ((first = media_entity_graph_walk_next(graph)) && first != entity) - if (media_entity_type(first) != MEDIA_ENT_T_DEVNODE) + if (is_media_entity_v4l2_subdev(first)) isp_pipeline_pm_power_one(first, -change); return ret; @@ -782,23 +782,24 @@ static int isp_pipeline_pm_power(struct media_entity *entity, int change) * off is assumed to never fail. No failure can occur when the use parameter is * set to 0. */ -int omap3isp_pipeline_pm_use(struct media_entity *entity, int use) +int omap3isp_pipeline_pm_use(struct media_entity *entity, int use, + struct media_entity_graph *graph) { int change = use ? 1 : -1; int ret; - mutex_lock(&entity->parent->graph_mutex); + mutex_lock(&entity->graph_obj.mdev->graph_mutex); /* Apply use count to node. */ entity->use_count += change; WARN_ON(entity->use_count < 0); /* Apply power change to connected non-nodes. */ - ret = isp_pipeline_pm_power(entity, change); + ret = isp_pipeline_pm_power(entity, change, graph); if (ret < 0) entity->use_count -= change; - mutex_unlock(&entity->parent->graph_mutex); + mutex_unlock(&entity->graph_obj.mdev->graph_mutex); return ret; } @@ -820,35 +821,49 @@ int omap3isp_pipeline_pm_use(struct media_entity *entity, int use) static int isp_pipeline_link_notify(struct media_link *link, u32 flags, unsigned int notification) { + struct media_entity_graph *graph = + &container_of(link->graph_obj.mdev, struct isp_device, + media_dev)->pm_count_graph; struct media_entity *source = link->source->entity; struct media_entity *sink = link->sink->entity; - int source_use = isp_pipeline_pm_use_count(source); - int sink_use = isp_pipeline_pm_use_count(sink); - int ret; + int source_use; + int sink_use; + int ret = 0; + + if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH) { + ret = media_entity_graph_walk_init(graph, + link->graph_obj.mdev); + if (ret) + return ret; + } + + source_use = isp_pipeline_pm_use_count(source, graph); + sink_use = isp_pipeline_pm_use_count(sink, graph); if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH && !(flags & MEDIA_LNK_FL_ENABLED)) { /* Powering off entities is assumed to never fail. */ - isp_pipeline_pm_power(source, -sink_use); - isp_pipeline_pm_power(sink, -source_use); + isp_pipeline_pm_power(source, -sink_use, graph); + isp_pipeline_pm_power(sink, -source_use, graph); return 0; } if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH && (flags & MEDIA_LNK_FL_ENABLED)) { - ret = isp_pipeline_pm_power(source, sink_use); + ret = isp_pipeline_pm_power(source, sink_use, graph); if (ret < 0) return ret; - ret = isp_pipeline_pm_power(sink, source_use); + ret = isp_pipeline_pm_power(sink, source_use, graph); if (ret < 0) - isp_pipeline_pm_power(source, -sink_use); - - return ret; + isp_pipeline_pm_power(source, -sink_use, graph); } - return 0; + if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH) + media_entity_graph_walk_cleanup(graph); + + return ret; } /* ----------------------------------------------------------------------------- @@ -881,7 +896,7 @@ static int isp_pipeline_enable(struct isp_pipeline *pipe, * starting entities if the pipeline won't start anyway (those entities * would then likely fail to stop, making the problem worse). */ - if (pipe->entities & isp->crashed) + if (media_entity_enum_intersects(&pipe->ent_enum, &isp->crashed)) return -EIO; spin_lock_irqsave(&pipe->lock, flags); @@ -897,8 +912,7 @@ static int isp_pipeline_enable(struct isp_pipeline *pipe, break; pad = media_entity_remote_pad(pad); - if (pad == NULL || - media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) break; entity = pad->entity; @@ -987,8 +1001,7 @@ static int isp_pipeline_disable(struct isp_pipeline *pipe) break; pad = media_entity_remote_pad(pad); - if (pad == NULL || - media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) break; entity = pad->entity; @@ -1028,7 +1041,8 @@ static int isp_pipeline_disable(struct isp_pipeline *pipe) dev_info(isp->dev, "Unable to stop %s\n", subdev->name); isp->stop_failure = true; if (subdev == &isp->isp_prev.subdev) - isp->crashed |= 1U << subdev->entity.id; + media_entity_enum_set(&isp->crashed, + &subdev->entity); failure = -ETIMEDOUT; } } @@ -1234,7 +1248,7 @@ static int isp_reset(struct isp_device *isp) } isp->stop_failure = false; - isp->crashed = 0; + media_entity_enum_zero(&isp->crashed); return 0; } @@ -1645,7 +1659,8 @@ static void __omap3isp_put(struct isp_device *isp, bool save_ctx) /* Reset the ISP if an entity has failed to stop. This is the * only way to recover from such conditions. */ - if (isp->crashed || isp->stop_failure) + if (!media_entity_enum_empty(&isp->crashed) || + isp->stop_failure) isp_reset(isp); isp_disable_clocks(isp); } @@ -1792,6 +1807,7 @@ static void isp_unregister_entities(struct isp_device *isp) v4l2_device_unregister(&isp->v4l2_dev); media_device_unregister(&isp->media_dev); + media_device_cleanup(&isp->media_dev); } static int isp_link_entity( @@ -1862,7 +1878,7 @@ static int isp_link_entity( return -EINVAL; } - return media_entity_create_link(entity, i, input, pad, flags); + return media_create_pad_link(entity, i, input, pad, flags); } static int isp_register_entities(struct isp_device *isp) @@ -1874,12 +1890,7 @@ static int isp_register_entities(struct isp_device *isp) sizeof(isp->media_dev.model)); isp->media_dev.hw_revision = isp->revision; isp->media_dev.link_notify = isp_pipeline_link_notify; - ret = media_device_register(&isp->media_dev); - if (ret < 0) { - dev_err(isp->dev, "%s: Media device registration failed (%d)\n", - __func__, ret); - return ret; - } + media_device_init(&isp->media_dev); isp->v4l2_dev.mdev = &isp->media_dev; ret = v4l2_device_register(isp->dev, &isp->v4l2_dev); @@ -1930,6 +1941,118 @@ done: return ret; } +/* + * isp_create_links() - Create links for internal and external ISP entities + * @isp : Pointer to ISP device + * + * This function creates all links between ISP internal and external entities. + * + * Return: A negative error code on failure or zero on success. Possible error + * codes are those returned by media_create_pad_link(). + */ +static int isp_create_links(struct isp_device *isp) +{ + int ret; + + /* Create links between entities and video nodes. */ + ret = media_create_pad_link( + &isp->isp_csi2a.subdev.entity, CSI2_PAD_SOURCE, + &isp->isp_csi2a.video_out.video.entity, 0, 0); + if (ret < 0) + return ret; + + ret = media_create_pad_link( + &isp->isp_ccp2.video_in.video.entity, 0, + &isp->isp_ccp2.subdev.entity, CCP2_PAD_SINK, 0); + if (ret < 0) + return ret; + + ret = media_create_pad_link( + &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_OF, + &isp->isp_ccdc.video_out.video.entity, 0, 0); + if (ret < 0) + return ret; + + ret = media_create_pad_link( + &isp->isp_prev.video_in.video.entity, 0, + &isp->isp_prev.subdev.entity, PREV_PAD_SINK, 0); + if (ret < 0) + return ret; + + ret = media_create_pad_link( + &isp->isp_prev.subdev.entity, PREV_PAD_SOURCE, + &isp->isp_prev.video_out.video.entity, 0, 0); + if (ret < 0) + return ret; + + ret = media_create_pad_link( + &isp->isp_res.video_in.video.entity, 0, + &isp->isp_res.subdev.entity, RESZ_PAD_SINK, 0); + if (ret < 0) + return ret; + + ret = media_create_pad_link( + &isp->isp_res.subdev.entity, RESZ_PAD_SOURCE, + &isp->isp_res.video_out.video.entity, 0, 0); + + if (ret < 0) + return ret; + + /* Create links between entities. */ + ret = media_create_pad_link( + &isp->isp_csi2a.subdev.entity, CSI2_PAD_SOURCE, + &isp->isp_ccdc.subdev.entity, CCDC_PAD_SINK, 0); + if (ret < 0) + return ret; + + ret = media_create_pad_link( + &isp->isp_ccp2.subdev.entity, CCP2_PAD_SOURCE, + &isp->isp_ccdc.subdev.entity, CCDC_PAD_SINK, 0); + if (ret < 0) + return ret; + + ret = media_create_pad_link( + &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP, + &isp->isp_prev.subdev.entity, PREV_PAD_SINK, 0); + if (ret < 0) + return ret; + + ret = media_create_pad_link( + &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_OF, + &isp->isp_res.subdev.entity, RESZ_PAD_SINK, 0); + if (ret < 0) + return ret; + + ret = media_create_pad_link( + &isp->isp_prev.subdev.entity, PREV_PAD_SOURCE, + &isp->isp_res.subdev.entity, RESZ_PAD_SINK, 0); + if (ret < 0) + return ret; + + ret = media_create_pad_link( + &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP, + &isp->isp_aewb.subdev.entity, 0, + MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE); + if (ret < 0) + return ret; + + ret = media_create_pad_link( + &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP, + &isp->isp_af.subdev.entity, 0, + MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE); + if (ret < 0) + return ret; + + ret = media_create_pad_link( + &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP, + &isp->isp_hist.subdev.entity, 0, + MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE); + if (ret < 0) + return ret; + + return 0; +} + static void isp_cleanup_modules(struct isp_device *isp) { omap3isp_h3a_aewb_cleanup(isp); @@ -2000,62 +2123,8 @@ static int isp_initialize_modules(struct isp_device *isp) goto error_h3a_af; } - /* Connect the submodules. */ - ret = media_entity_create_link( - &isp->isp_csi2a.subdev.entity, CSI2_PAD_SOURCE, - &isp->isp_ccdc.subdev.entity, CCDC_PAD_SINK, 0); - if (ret < 0) - goto error_link; - - ret = media_entity_create_link( - &isp->isp_ccp2.subdev.entity, CCP2_PAD_SOURCE, - &isp->isp_ccdc.subdev.entity, CCDC_PAD_SINK, 0); - if (ret < 0) - goto error_link; - - ret = media_entity_create_link( - &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP, - &isp->isp_prev.subdev.entity, PREV_PAD_SINK, 0); - if (ret < 0) - goto error_link; - - ret = media_entity_create_link( - &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_OF, - &isp->isp_res.subdev.entity, RESZ_PAD_SINK, 0); - if (ret < 0) - goto error_link; - - ret = media_entity_create_link( - &isp->isp_prev.subdev.entity, PREV_PAD_SOURCE, - &isp->isp_res.subdev.entity, RESZ_PAD_SINK, 0); - if (ret < 0) - goto error_link; - - ret = media_entity_create_link( - &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP, - &isp->isp_aewb.subdev.entity, 0, - MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE); - if (ret < 0) - goto error_link; - - ret = media_entity_create_link( - &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP, - &isp->isp_af.subdev.entity, 0, - MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE); - if (ret < 0) - goto error_link; - - ret = media_entity_create_link( - &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP, - &isp->isp_hist.subdev.entity, 0, - MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE); - if (ret < 0) - goto error_link; - return 0; -error_link: - omap3isp_h3a_af_cleanup(isp); error_h3a_af: omap3isp_h3a_aewb_cleanup(isp); error_h3a_aewb: @@ -2149,6 +2218,8 @@ static int isp_remove(struct platform_device *pdev) isp_detach_iommu(isp); __omap3isp_put(isp, false); + media_entity_enum_cleanup(&isp->crashed); + return 0; } @@ -2278,28 +2349,43 @@ static int isp_subdev_notifier_bound(struct v4l2_async_notifier *async, struct v4l2_subdev *subdev, struct v4l2_async_subdev *asd) { - struct isp_device *isp = container_of(async, struct isp_device, - notifier); struct isp_async_subdev *isd = container_of(asd, struct isp_async_subdev, asd); - int ret; - - ret = isp_link_entity(isp, &subdev->entity, isd->bus.interface); - if (ret < 0) - return ret; isd->sd = subdev; isd->sd->host_priv = &isd->bus; - return ret; + return 0; } static int isp_subdev_notifier_complete(struct v4l2_async_notifier *async) { struct isp_device *isp = container_of(async, struct isp_device, notifier); + struct v4l2_device *v4l2_dev = &isp->v4l2_dev; + struct v4l2_subdev *sd; + struct isp_bus_cfg *bus; + int ret; + + ret = media_entity_enum_init(&isp->crashed, &isp->media_dev); + if (ret) + return ret; - return v4l2_device_register_subdev_nodes(&isp->v4l2_dev); + list_for_each_entry(sd, &v4l2_dev->subdevs, list) { + /* Only try to link entities whose interface was set on bound */ + if (sd->host_priv) { + bus = (struct isp_bus_cfg *)sd->host_priv; + ret = isp_link_entity(isp, &sd->entity, bus->interface); + if (ret < 0) + return ret; + } + } + + ret = v4l2_device_register_subdev_nodes(&isp->v4l2_dev); + if (ret < 0) + return ret; + + return media_device_register(&isp->media_dev); } /* @@ -2465,6 +2551,10 @@ static int isp_probe(struct platform_device *pdev) if (ret < 0) goto error_modules; + ret = isp_create_links(isp); + if (ret < 0) + goto error_register_entities; + isp->notifier.bound = isp_subdev_notifier_bound; isp->notifier.complete = isp_subdev_notifier_complete; diff --git a/drivers/media/platform/omap3isp/isp.h b/drivers/media/platform/omap3isp/isp.h index 5acc2e6..49b7f71 100644 --- a/drivers/media/platform/omap3isp/isp.h +++ b/drivers/media/platform/omap3isp/isp.h @@ -17,6 +17,7 @@ #ifndef OMAP3_ISP_CORE_H #define OMAP3_ISP_CORE_H +#include <media/media-entity.h> #include <media/v4l2-async.h> #include <media/v4l2-device.h> #include <linux/clk-provider.h> @@ -152,7 +153,7 @@ struct isp_xclk { * @stat_lock: Spinlock for handling statistics * @isp_mutex: Mutex for serializing requests to ISP. * @stop_failure: Indicates that an entity failed to stop. - * @crashed: Bitmask of crashed entities (indexed by entity ID) + * @crashed: Crashed ent_enum * @has_context: Context has been saved at least once and can be restored. * @ref_count: Reference count for handling multiple ISP requests. * @cam_ick: Pointer to camera interface clock structure. @@ -176,6 +177,7 @@ struct isp_device { struct v4l2_device v4l2_dev; struct v4l2_async_notifier notifier; struct media_device media_dev; + struct media_entity_graph pm_count_graph; struct device *dev; u32 revision; @@ -194,7 +196,7 @@ struct isp_device { spinlock_t stat_lock; /* common lock for statistic drivers */ struct mutex isp_mutex; /* For handling ref_count field */ bool stop_failure; - u32 crashed; + struct media_entity_enum crashed; int has_context; int ref_count; unsigned int autoidle; @@ -265,7 +267,8 @@ void omap3isp_subclk_enable(struct isp_device *isp, void omap3isp_subclk_disable(struct isp_device *isp, enum isp_subclk_resource res); -int omap3isp_pipeline_pm_use(struct media_entity *entity, int use); +int omap3isp_pipeline_pm_use(struct media_entity *entity, int use, + struct media_entity_graph *graph); int omap3isp_register_entities(struct platform_device *pdev, struct v4l2_device *v4l2_dev); diff --git a/drivers/media/platform/omap3isp/ispccdc.c b/drivers/media/platform/omap3isp/ispccdc.c index a6a61cc..bb3974c 100644 --- a/drivers/media/platform/omap3isp/ispccdc.c +++ b/drivers/media/platform/omap3isp/ispccdc.c @@ -1608,7 +1608,7 @@ static int ccdc_isr_buffer(struct isp_ccdc_device *ccdc) /* Wait for the CCDC to become idle. */ if (ccdc_sbl_wait_idle(ccdc, 1000)) { dev_info(isp->dev, "CCDC won't become idle!\n"); - isp->crashed |= 1U << ccdc->subdev.entity.id; + media_entity_enum_set(&isp->crashed, &ccdc->subdev.entity); omap3isp_pipeline_cancel_stream(pipe); return 0; } @@ -2513,9 +2513,14 @@ static int ccdc_link_setup(struct media_entity *entity, struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); struct isp_device *isp = to_isp_device(ccdc); + unsigned int index = local->index; - switch (local->index | media_entity_type(remote->entity)) { - case CCDC_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + /* FIXME: this is actually a hack! */ + if (is_media_entity_v4l2_subdev(remote->entity)) + index |= 2 << 16; + + switch (index) { + case CCDC_PAD_SINK | 2 << 16: /* Read from the sensor (parallel interface), CCP2, CSI2a or * CSI2c. */ @@ -2543,7 +2548,7 @@ static int ccdc_link_setup(struct media_entity *entity, * Revisit this when it will be implemented, and return -EBUSY for now. */ - case CCDC_PAD_SOURCE_VP | MEDIA_ENT_T_V4L2_SUBDEV: + case CCDC_PAD_SOURCE_VP | 2 << 16: /* Write to preview engine, histogram and H3A. When none of * those links are active, the video port can be disabled. */ @@ -2556,7 +2561,7 @@ static int ccdc_link_setup(struct media_entity *entity, } break; - case CCDC_PAD_SOURCE_OF | MEDIA_ENT_T_DEVNODE: + case CCDC_PAD_SOURCE_OF: /* Write to memory */ if (flags & MEDIA_LNK_FL_ENABLED) { if (ccdc->output & ~CCDC_OUTPUT_MEMORY) @@ -2567,7 +2572,7 @@ static int ccdc_link_setup(struct media_entity *entity, } break; - case CCDC_PAD_SOURCE_OF | MEDIA_ENT_T_V4L2_SUBDEV: + case CCDC_PAD_SOURCE_OF | 2 << 16: /* Write to resizer */ if (flags & MEDIA_LNK_FL_ENABLED) { if (ccdc->output & ~CCDC_OUTPUT_RESIZER) @@ -2650,7 +2655,7 @@ static int ccdc_init_entities(struct isp_ccdc_device *ccdc) pads[CCDC_PAD_SOURCE_OF].flags = MEDIA_PAD_FL_SOURCE; me->ops = &ccdc_media_ops; - ret = media_entity_init(me, CCDC_PADS_NUM, pads, 0); + ret = media_entity_pads_init(me, CCDC_PADS_NUM, pads); if (ret < 0) return ret; @@ -2664,19 +2669,11 @@ static int ccdc_init_entities(struct isp_ccdc_device *ccdc) ret = omap3isp_video_init(&ccdc->video_out, "CCDC"); if (ret < 0) - goto error_video; - - /* Connect the CCDC subdev to the video node. */ - ret = media_entity_create_link(&ccdc->subdev.entity, CCDC_PAD_SOURCE_OF, - &ccdc->video_out.video.entity, 0, 0); - if (ret < 0) - goto error_link; + goto error; return 0; -error_link: - omap3isp_video_cleanup(&ccdc->video_out); -error_video: +error: media_entity_cleanup(me); return ret; } diff --git a/drivers/media/platform/omap3isp/ispccp2.c b/drivers/media/platform/omap3isp/ispccp2.c index 38e6a97..ca09523 100644 --- a/drivers/media/platform/omap3isp/ispccp2.c +++ b/drivers/media/platform/omap3isp/ispccp2.c @@ -956,9 +956,14 @@ static int ccp2_link_setup(struct media_entity *entity, { struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); struct isp_ccp2_device *ccp2 = v4l2_get_subdevdata(sd); + unsigned int index = local->index; - switch (local->index | media_entity_type(remote->entity)) { - case CCP2_PAD_SINK | MEDIA_ENT_T_DEVNODE: + /* FIXME: this is actually a hack! */ + if (is_media_entity_v4l2_subdev(remote->entity)) + index |= 2 << 16; + + switch (index) { + case CCP2_PAD_SINK: /* read from memory */ if (flags & MEDIA_LNK_FL_ENABLED) { if (ccp2->input == CCP2_INPUT_SENSOR) @@ -970,7 +975,7 @@ static int ccp2_link_setup(struct media_entity *entity, } break; - case CCP2_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + case CCP2_PAD_SINK | 2 << 16: /* read from sensor/phy */ if (flags & MEDIA_LNK_FL_ENABLED) { if (ccp2->input == CCP2_INPUT_MEMORY) @@ -981,7 +986,7 @@ static int ccp2_link_setup(struct media_entity *entity, ccp2->input = CCP2_INPUT_NONE; } break; - case CCP2_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV: + case CCP2_PAD_SOURCE | 2 << 16: /* write to video port/ccdc */ if (flags & MEDIA_LNK_FL_ENABLED) ccp2->output = CCP2_OUTPUT_CCDC; @@ -1071,7 +1076,7 @@ static int ccp2_init_entities(struct isp_ccp2_device *ccp2) pads[CCP2_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; me->ops = &ccp2_media_ops; - ret = media_entity_init(me, CCP2_PADS_NUM, pads, 0); + ret = media_entity_pads_init(me, CCP2_PADS_NUM, pads); if (ret < 0) return ret; @@ -1097,19 +1102,11 @@ static int ccp2_init_entities(struct isp_ccp2_device *ccp2) ret = omap3isp_video_init(&ccp2->video_in, "CCP2"); if (ret < 0) - goto error_video; - - /* Connect the video node to the ccp2 subdev. */ - ret = media_entity_create_link(&ccp2->video_in.video.entity, 0, - &ccp2->subdev.entity, CCP2_PAD_SINK, 0); - if (ret < 0) - goto error_link; + goto error; return 0; -error_link: - omap3isp_video_cleanup(&ccp2->video_in); -error_video: +error: media_entity_cleanup(&ccp2->subdev.entity); return ret; } diff --git a/drivers/media/platform/omap3isp/ispcsi2.c b/drivers/media/platform/omap3isp/ispcsi2.c index a78338d..f75a1be 100644 --- a/drivers/media/platform/omap3isp/ispcsi2.c +++ b/drivers/media/platform/omap3isp/ispcsi2.c @@ -1144,14 +1144,19 @@ static int csi2_link_setup(struct media_entity *entity, struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd); struct isp_csi2_ctrl_cfg *ctrl = &csi2->ctrl; + unsigned int index = local->index; /* * The ISP core doesn't support pipelines with multiple video outputs. * Revisit this when it will be implemented, and return -EBUSY for now. */ - switch (local->index | media_entity_type(remote->entity)) { - case CSI2_PAD_SOURCE | MEDIA_ENT_T_DEVNODE: + /* FIXME: this is actually a hack! */ + if (is_media_entity_v4l2_subdev(remote->entity)) + index |= 2 << 16; + + switch (index) { + case CSI2_PAD_SOURCE: if (flags & MEDIA_LNK_FL_ENABLED) { if (csi2->output & ~CSI2_OUTPUT_MEMORY) return -EBUSY; @@ -1161,7 +1166,7 @@ static int csi2_link_setup(struct media_entity *entity, } break; - case CSI2_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV: + case CSI2_PAD_SOURCE | 2 << 16: if (flags & MEDIA_LNK_FL_ENABLED) { if (csi2->output & ~CSI2_OUTPUT_CCDC) return -EBUSY; @@ -1245,7 +1250,7 @@ static int csi2_init_entities(struct isp_csi2_device *csi2) | MEDIA_PAD_FL_MUST_CONNECT; me->ops = &csi2_media_ops; - ret = media_entity_init(me, CSI2_PADS_NUM, pads, 0); + ret = media_entity_pads_init(me, CSI2_PADS_NUM, pads); if (ret < 0) return ret; @@ -1264,16 +1269,8 @@ static int csi2_init_entities(struct isp_csi2_device *csi2) if (ret < 0) goto error_video; - /* Connect the CSI2 subdev to the video node. */ - ret = media_entity_create_link(&csi2->subdev.entity, CSI2_PAD_SOURCE, - &csi2->video_out.video.entity, 0, 0); - if (ret < 0) - goto error_link; - return 0; -error_link: - omap3isp_video_cleanup(&csi2->video_out); error_video: media_entity_cleanup(&csi2->subdev.entity); return ret; diff --git a/drivers/media/platform/omap3isp/isppreview.c b/drivers/media/platform/omap3isp/isppreview.c index 1380327..84a9667 100644 --- a/drivers/media/platform/omap3isp/isppreview.c +++ b/drivers/media/platform/omap3isp/isppreview.c @@ -2144,9 +2144,14 @@ static int preview_link_setup(struct media_entity *entity, { struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); struct isp_prev_device *prev = v4l2_get_subdevdata(sd); + unsigned int index = local->index; - switch (local->index | media_entity_type(remote->entity)) { - case PREV_PAD_SINK | MEDIA_ENT_T_DEVNODE: + /* FIXME: this is actually a hack! */ + if (is_media_entity_v4l2_subdev(remote->entity)) + index |= 2 << 16; + + switch (index) { + case PREV_PAD_SINK: /* read from memory */ if (flags & MEDIA_LNK_FL_ENABLED) { if (prev->input == PREVIEW_INPUT_CCDC) @@ -2158,7 +2163,7 @@ static int preview_link_setup(struct media_entity *entity, } break; - case PREV_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + case PREV_PAD_SINK | 2 << 16: /* read from ccdc */ if (flags & MEDIA_LNK_FL_ENABLED) { if (prev->input == PREVIEW_INPUT_MEMORY) @@ -2175,7 +2180,7 @@ static int preview_link_setup(struct media_entity *entity, * Revisit this when it will be implemented, and return -EBUSY for now. */ - case PREV_PAD_SOURCE | MEDIA_ENT_T_DEVNODE: + case PREV_PAD_SOURCE: /* write to memory */ if (flags & MEDIA_LNK_FL_ENABLED) { if (prev->output & ~PREVIEW_OUTPUT_MEMORY) @@ -2186,7 +2191,7 @@ static int preview_link_setup(struct media_entity *entity, } break; - case PREV_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV: + case PREV_PAD_SOURCE | 2 << 16: /* write to resizer */ if (flags & MEDIA_LNK_FL_ENABLED) { if (prev->output & ~PREVIEW_OUTPUT_RESIZER) @@ -2282,7 +2287,7 @@ static int preview_init_entities(struct isp_prev_device *prev) pads[PREV_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; me->ops = &preview_media_ops; - ret = media_entity_init(me, PREV_PADS_NUM, pads, 0); + ret = media_entity_pads_init(me, PREV_PADS_NUM, pads); if (ret < 0) return ret; @@ -2311,21 +2316,8 @@ static int preview_init_entities(struct isp_prev_device *prev) if (ret < 0) goto error_video_out; - /* Connect the video nodes to the previewer subdev. */ - ret = media_entity_create_link(&prev->video_in.video.entity, 0, - &prev->subdev.entity, PREV_PAD_SINK, 0); - if (ret < 0) - goto error_link; - - ret = media_entity_create_link(&prev->subdev.entity, PREV_PAD_SOURCE, - &prev->video_out.video.entity, 0, 0); - if (ret < 0) - goto error_link; - return 0; -error_link: - omap3isp_video_cleanup(&prev->video_out); error_video_out: omap3isp_video_cleanup(&prev->video_in); error_video_in: diff --git a/drivers/media/platform/omap3isp/ispresizer.c b/drivers/media/platform/omap3isp/ispresizer.c index 7cfb43d..0b6a875 100644 --- a/drivers/media/platform/omap3isp/ispresizer.c +++ b/drivers/media/platform/omap3isp/ispresizer.c @@ -1623,9 +1623,14 @@ static int resizer_link_setup(struct media_entity *entity, { struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); struct isp_res_device *res = v4l2_get_subdevdata(sd); + unsigned int index = local->index; - switch (local->index | media_entity_type(remote->entity)) { - case RESZ_PAD_SINK | MEDIA_ENT_T_DEVNODE: + /* FIXME: this is actually a hack! */ + if (is_media_entity_v4l2_subdev(remote->entity)) + index |= 2 << 16; + + switch (index) { + case RESZ_PAD_SINK: /* read from memory */ if (flags & MEDIA_LNK_FL_ENABLED) { if (res->input == RESIZER_INPUT_VP) @@ -1637,7 +1642,7 @@ static int resizer_link_setup(struct media_entity *entity, } break; - case RESZ_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + case RESZ_PAD_SINK | 2 << 16: /* read from ccdc or previewer */ if (flags & MEDIA_LNK_FL_ENABLED) { if (res->input == RESIZER_INPUT_MEMORY) @@ -1649,7 +1654,7 @@ static int resizer_link_setup(struct media_entity *entity, } break; - case RESZ_PAD_SOURCE | MEDIA_ENT_T_DEVNODE: + case RESZ_PAD_SOURCE: /* resizer always write to memory */ break; @@ -1728,7 +1733,7 @@ static int resizer_init_entities(struct isp_res_device *res) pads[RESZ_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; me->ops = &resizer_media_ops; - ret = media_entity_init(me, RESZ_PADS_NUM, pads, 0); + ret = media_entity_pads_init(me, RESZ_PADS_NUM, pads); if (ret < 0) return ret; @@ -1755,21 +1760,8 @@ static int resizer_init_entities(struct isp_res_device *res) res->video_out.video.entity.flags |= MEDIA_ENT_FL_DEFAULT; - /* Connect the video nodes to the resizer subdev. */ - ret = media_entity_create_link(&res->video_in.video.entity, 0, - &res->subdev.entity, RESZ_PAD_SINK, 0); - if (ret < 0) - goto error_link; - - ret = media_entity_create_link(&res->subdev.entity, RESZ_PAD_SOURCE, - &res->video_out.video.entity, 0, 0); - if (ret < 0) - goto error_link; - return 0; -error_link: - omap3isp_video_cleanup(&res->video_out); error_video_out: omap3isp_video_cleanup(&res->video_in); error_video_in: diff --git a/drivers/media/platform/omap3isp/ispstat.c b/drivers/media/platform/omap3isp/ispstat.c index 94d4c29..1b9217d 100644 --- a/drivers/media/platform/omap3isp/ispstat.c +++ b/drivers/media/platform/omap3isp/ispstat.c @@ -1028,7 +1028,7 @@ static int isp_stat_init_entities(struct ispstat *stat, const char *name, stat->pad.flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT; me->ops = NULL; - return media_entity_init(me, 1, &stat->pad, 0); + return media_entity_pads_init(me, 1, &stat->pad); } int omap3isp_stat_init(struct ispstat *stat, const char *name, diff --git a/drivers/media/platform/omap3isp/ispvideo.c b/drivers/media/platform/omap3isp/ispvideo.c index ecadca3..994dfc0 100644 --- a/drivers/media/platform/omap3isp/ispvideo.c +++ b/drivers/media/platform/omap3isp/ispvideo.c @@ -210,8 +210,7 @@ isp_video_remote_subdev(struct isp_video *video, u32 *pad) remote = media_entity_remote_pad(&video->pad); - if (remote == NULL || - media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + if (!remote || !is_media_entity_v4l2_subdev(remote->entity)) return NULL; if (pad) @@ -226,16 +225,23 @@ static int isp_video_get_graph_data(struct isp_video *video, { struct media_entity_graph graph; struct media_entity *entity = &video->video.entity; - struct media_device *mdev = entity->parent; + struct media_device *mdev = entity->graph_obj.mdev; struct isp_video *far_end = NULL; + int ret; mutex_lock(&mdev->graph_mutex); + ret = media_entity_graph_walk_init(&graph, entity->graph_obj.mdev); + if (ret) { + mutex_unlock(&mdev->graph_mutex); + return ret; + } + media_entity_graph_walk_start(&graph, entity); while ((entity = media_entity_graph_walk_next(&graph))) { struct isp_video *__video; - pipe->entities |= 1 << entity->id; + media_entity_enum_set(&pipe->ent_enum, entity); if (far_end != NULL) continue; @@ -243,7 +249,7 @@ static int isp_video_get_graph_data(struct isp_video *video, if (entity == &video->video.entity) continue; - if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE) + if (!is_media_entity_v4l2_io(entity)) continue; __video = to_isp_video(media_entity_to_video_device(entity)); @@ -253,6 +259,8 @@ static int isp_video_get_graph_data(struct isp_video *video, mutex_unlock(&mdev->graph_mutex); + media_entity_graph_walk_cleanup(&graph); + if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { pipe->input = far_end; pipe->output = video; @@ -900,7 +908,7 @@ static int isp_video_check_external_subdevs(struct isp_video *video, for (i = 0; i < ARRAY_SIZE(ents); i++) { /* Is the entity part of the pipeline? */ - if (!(pipe->entities & (1 << ents[i]->id))) + if (!media_entity_enum_test(&pipe->ent_enum, ents[i])) continue; /* ISP entities have always sink pad == 0. Find source. */ @@ -918,7 +926,7 @@ static int isp_video_check_external_subdevs(struct isp_video *video, return -EINVAL; } - if (media_entity_type(source) != MEDIA_ENT_T_V4L2_SUBDEV) + if (!is_media_entity_v4l2_subdev(source)) return 0; pipe->external = media_entity_to_v4l2_subdev(source); @@ -952,7 +960,8 @@ static int isp_video_check_external_subdevs(struct isp_video *video, pipe->external_rate = ctrl.value64; - if (pipe->entities & (1 << isp->isp_ccdc.subdev.entity.id)) { + if (media_entity_enum_test(&pipe->ent_enum, + &isp->isp_ccdc.subdev.entity)) { unsigned int rate = UINT_MAX; /* * Check that maximum allowed CCDC pixel rate isn't @@ -1018,7 +1027,9 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) pipe = video->video.entity.pipe ? to_isp_pipeline(&video->video.entity) : &video->pipe; - pipe->entities = 0; + ret = media_entity_enum_init(&pipe->ent_enum, &video->isp->media_dev); + if (ret) + goto err_enum_init; /* TODO: Implement PM QoS */ pipe->l3_ick = clk_get_rate(video->isp->clock[ISP_CLK_L3_ICK]); @@ -1092,6 +1103,7 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) } mutex_unlock(&video->stream_lock); + return 0; err_set_stream: @@ -1112,7 +1124,11 @@ err_pipeline_start: INIT_LIST_HEAD(&video->dmaqueue); video->queue = NULL; + media_entity_enum_cleanup(&pipe->ent_enum); + +err_enum_init: mutex_unlock(&video->stream_lock); + return ret; } @@ -1164,6 +1180,8 @@ isp_video_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) /* TODO: Implement PM QoS */ media_entity_pipeline_stop(&video->video.entity); + media_entity_enum_cleanup(&pipe->ent_enum); + done: mutex_unlock(&video->stream_lock); return 0; @@ -1243,7 +1261,12 @@ static int isp_video_open(struct file *file) goto done; } - ret = omap3isp_pipeline_pm_use(&video->video.entity, 1); + ret = media_entity_graph_walk_init(&handle->graph, + &video->isp->media_dev); + if (ret) + goto done; + + ret = omap3isp_pipeline_pm_use(&video->video.entity, 1, &handle->graph); if (ret < 0) { omap3isp_put(video->isp); goto done; @@ -1274,6 +1297,7 @@ static int isp_video_open(struct file *file) done: if (ret < 0) { v4l2_fh_del(&handle->vfh); + media_entity_graph_walk_cleanup(&handle->graph); kfree(handle); } @@ -1293,7 +1317,8 @@ static int isp_video_release(struct file *file) vb2_queue_release(&handle->queue); mutex_unlock(&video->queue_lock); - omap3isp_pipeline_pm_use(&video->video.entity, 0); + omap3isp_pipeline_pm_use(&video->video.entity, 0, &handle->graph); + media_entity_graph_walk_cleanup(&handle->graph); /* Release the file handle. */ v4l2_fh_del(vfh); @@ -1367,7 +1392,7 @@ int omap3isp_video_init(struct isp_video *video, const char *name) if (IS_ERR(video->alloc_ctx)) return PTR_ERR(video->alloc_ctx); - ret = media_entity_init(&video->video.entity, 1, &video->pad, 0); + ret = media_entity_pads_init(&video->video.entity, 1, &video->pad); if (ret < 0) { vb2_dma_contig_cleanup_ctx(video->alloc_ctx); return ret; diff --git a/drivers/media/platform/omap3isp/ispvideo.h b/drivers/media/platform/omap3isp/ispvideo.h index bcf0e0a..1564298 100644 --- a/drivers/media/platform/omap3isp/ispvideo.h +++ b/drivers/media/platform/omap3isp/ispvideo.h @@ -80,7 +80,7 @@ enum isp_pipeline_state { * struct isp_pipeline - An ISP hardware pipeline * @field: The field being processed by the pipeline * @error: A hardware error occurred during capture - * @entities: Bitmask of entities in the pipeline (indexed by entity ID) + * @ent_enum: Entities in the pipeline */ struct isp_pipeline { struct media_pipeline pipe; @@ -89,7 +89,7 @@ struct isp_pipeline { enum isp_pipeline_stream_state stream_state; struct isp_video *input; struct isp_video *output; - u32 entities; + struct media_entity_enum ent_enum; unsigned long l3_ick; unsigned int max_rate; enum v4l2_field field; @@ -189,6 +189,7 @@ struct isp_video_fh { struct vb2_queue queue; struct v4l2_format format; struct v4l2_fract timeperframe; + struct media_entity_graph graph; }; #define to_isp_video_fh(fh) container_of(fh, struct isp_video_fh, vfh) diff --git a/drivers/media/platform/s3c-camif/camif-capture.c b/drivers/media/platform/s3c-camif/camif-capture.c index ec3abbe..bd060ef 100644 --- a/drivers/media/platform/s3c-camif/camif-capture.c +++ b/drivers/media/platform/s3c-camif/camif-capture.c @@ -822,7 +822,7 @@ static int camif_pipeline_validate(struct camif_dev *camif) /* Retrieve format at the sensor subdev source pad */ pad = media_entity_remote_pad(&camif->pads[0]); - if (!pad || media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) return -EPIPE; src_fmt.pad = pad->index; @@ -1144,7 +1144,7 @@ int s3c_camif_register_video_node(struct camif_dev *camif, int idx) goto err_vd_rel; vp->pad.flags = MEDIA_PAD_FL_SINK; - ret = media_entity_init(&vfd->entity, 1, &vp->pad, 0); + ret = media_entity_pads_init(&vfd->entity, 1, &vp->pad); if (ret) goto err_vd_rel; @@ -1559,8 +1559,8 @@ int s3c_camif_create_subdev(struct camif_dev *camif) camif->pads[CAMIF_SD_PAD_SOURCE_C].flags = MEDIA_PAD_FL_SOURCE; camif->pads[CAMIF_SD_PAD_SOURCE_P].flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_init(&sd->entity, CAMIF_SD_PADS_NUM, - camif->pads, 0); + ret = media_entity_pads_init(&sd->entity, CAMIF_SD_PADS_NUM, + camif->pads); if (ret) return ret; diff --git a/drivers/media/platform/s3c-camif/camif-core.c b/drivers/media/platform/s3c-camif/camif-core.c index 1ba9bb0..0b44b9a 100644 --- a/drivers/media/platform/s3c-camif/camif-core.c +++ b/drivers/media/platform/s3c-camif/camif-core.c @@ -263,7 +263,7 @@ static int camif_create_media_links(struct camif_dev *camif) { int i, ret; - ret = media_entity_create_link(&camif->sensor.sd->entity, 0, + ret = media_create_pad_link(&camif->sensor.sd->entity, 0, &camif->subdev.entity, CAMIF_SD_PAD_SINK, MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); @@ -271,7 +271,7 @@ static int camif_create_media_links(struct camif_dev *camif) return ret; for (i = 1; i < CAMIF_SD_PADS_NUM && !ret; i++) { - ret = media_entity_create_link(&camif->subdev.entity, i, + ret = media_create_pad_link(&camif->subdev.entity, i, &camif->vp[i - 1].vdev.entity, 0, MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); @@ -305,7 +305,7 @@ static void camif_unregister_media_entities(struct camif_dev *camif) /* * Media device */ -static int camif_media_dev_register(struct camif_dev *camif) +static int camif_media_dev_init(struct camif_dev *camif) { struct media_device *md = &camif->media_dev; struct v4l2_device *v4l2_dev = &camif->v4l2_dev; @@ -324,14 +324,12 @@ static int camif_media_dev_register(struct camif_dev *camif) strlcpy(v4l2_dev->name, "s3c-camif", sizeof(v4l2_dev->name)); v4l2_dev->mdev = md; + media_device_init(md); + ret = v4l2_device_register(camif->dev, v4l2_dev); if (ret < 0) return ret; - ret = media_device_register(md); - if (ret < 0) - v4l2_device_unregister(v4l2_dev); - return ret; } @@ -483,7 +481,7 @@ static int s3c_camif_probe(struct platform_device *pdev) goto err_alloc; } - ret = camif_media_dev_register(camif); + ret = camif_media_dev_init(camif); if (ret < 0) goto err_mdev; @@ -510,6 +508,11 @@ static int s3c_camif_probe(struct platform_device *pdev) goto err_unlock; mutex_unlock(&camif->media_dev.graph_mutex); + + ret = media_device_register(&camif->media_dev); + if (ret < 0) + goto err_sens; + pm_runtime_put(dev); return 0; @@ -518,6 +521,7 @@ err_unlock: err_sens: v4l2_device_unregister(&camif->v4l2_dev); media_device_unregister(&camif->media_dev); + media_device_cleanup(&camif->media_dev); camif_unregister_media_entities(camif); err_mdev: vb2_dma_contig_cleanup_ctx(camif->alloc_ctx); @@ -539,6 +543,7 @@ static int s3c_camif_remove(struct platform_device *pdev) struct s3c_camif_plat_data *pdata = &camif->pdata; media_device_unregister(&camif->media_dev); + media_device_cleanup(&camif->media_dev); camif_unregister_media_entities(camif); v4l2_device_unregister(&camif->v4l2_dev); diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c index 4e61886..42dff9d 100644 --- a/drivers/media/platform/vsp1/vsp1_drv.c +++ b/drivers/media/platform/vsp1/vsp1_drv.c @@ -101,7 +101,7 @@ static int vsp1_create_links(struct vsp1_device *vsp1, struct vsp1_entity *sink) if (!(entity->pads[pad].flags & MEDIA_PAD_FL_SINK)) continue; - ret = media_entity_create_link(&source->subdev.entity, + ret = media_create_pad_link(&source->subdev.entity, source->source_pad, entity, pad, flags); if (ret < 0) @@ -127,6 +127,7 @@ static void vsp1_destroy_entities(struct vsp1_device *vsp1) v4l2_device_unregister(&vsp1->v4l2_dev); media_device_unregister(&vsp1->media_dev); + media_device_cleanup(&vsp1->media_dev); } static int vsp1_create_entities(struct vsp1_device *vsp1) @@ -141,12 +142,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) strlcpy(mdev->model, "VSP1", sizeof(mdev->model)); snprintf(mdev->bus_info, sizeof(mdev->bus_info), "platform:%s", dev_name(mdev->dev)); - ret = media_device_register(mdev); - if (ret < 0) { - dev_err(vsp1->dev, "media device registration failed (%d)\n", - ret); - return ret; - } + media_device_init(mdev); vdev->mdev = mdev; ret = v4l2_device_register(vsp1->dev, vdev); @@ -250,34 +246,44 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) list_add_tail(&wpf->entity.list_dev, &vsp1->entities); } - /* Create links. */ + /* Register all subdevs. */ list_for_each_entry(entity, &vsp1->entities, list_dev) { - if (entity->type == VSP1_ENTITY_LIF || - entity->type == VSP1_ENTITY_RPF) - continue; - - ret = vsp1_create_links(vsp1, entity); + ret = v4l2_device_register_subdev(&vsp1->v4l2_dev, + &entity->subdev); if (ret < 0) goto done; } + /* Create links. */ + list_for_each_entry(entity, &vsp1->entities, list_dev) { + if (entity->type == VSP1_ENTITY_LIF) { + ret = vsp1_wpf_create_links(vsp1, entity); + if (ret < 0) + goto done; + } else if (entity->type == VSP1_ENTITY_RPF) { + ret = vsp1_rpf_create_links(vsp1, entity); + if (ret < 0) + goto done; + } else { + ret = vsp1_create_links(vsp1, entity); + if (ret < 0) + goto done; + } + } + if (vsp1->pdata.features & VSP1_HAS_LIF) { - ret = media_entity_create_link( + ret = media_create_pad_link( &vsp1->wpf[0]->entity.subdev.entity, RWPF_PAD_SOURCE, &vsp1->lif->entity.subdev.entity, LIF_PAD_SINK, 0); if (ret < 0) return ret; } - /* Register all subdevs. */ - list_for_each_entry(entity, &vsp1->entities, list_dev) { - ret = v4l2_device_register_subdev(&vsp1->v4l2_dev, - &entity->subdev); - if (ret < 0) - goto done; - } - ret = v4l2_device_register_subdev_nodes(&vsp1->v4l2_dev); + if (ret < 0) + goto done; + + ret = media_device_register(mdev); done: if (ret < 0) diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c index fd95a75..d730853 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.c +++ b/drivers/media/platform/vsp1/vsp1_entity.c @@ -219,8 +219,8 @@ int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, entity->pads[num_pads - 1].flags = MEDIA_PAD_FL_SOURCE; /* Initialize the media entity. */ - return media_entity_init(&entity->subdev.entity, num_pads, - entity->pads, 0); + return media_entity_pads_init(&entity->subdev.entity, num_pads, + entity->pads); } void vsp1_entity_destroy(struct vsp1_entity *entity) diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c index cd5248a..9245382 100644 --- a/drivers/media/platform/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/vsp1/vsp1_rpf.c @@ -277,18 +277,29 @@ struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index) rpf->entity.video = video; - /* Connect the video device to the RPF. */ - ret = media_entity_create_link(&rpf->video.video.entity, 0, - &rpf->entity.subdev.entity, - RWPF_PAD_SINK, - MEDIA_LNK_FL_ENABLED | - MEDIA_LNK_FL_IMMUTABLE); - if (ret < 0) - goto error; - return rpf; error: vsp1_entity_destroy(&rpf->entity); return ERR_PTR(ret); } + +/* + * vsp1_rpf_create_links() - RPF pads links creation + * @vsp1: Pointer to VSP1 device + * @entity: Pointer to VSP1 entity + * + * return negative error code or zero on success + */ +int vsp1_rpf_create_links(struct vsp1_device *vsp1, + struct vsp1_entity *entity) +{ + struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev); + + /* Connect the video device to the RPF. */ + return media_create_pad_link(&rpf->video.video.entity, 0, + &rpf->entity.subdev.entity, + RWPF_PAD_SINK, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); +} diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.h b/drivers/media/platform/vsp1/vsp1_rwpf.h index f452dce..731d36e 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.h +++ b/drivers/media/platform/vsp1/vsp1_rwpf.h @@ -50,6 +50,11 @@ static inline struct vsp1_rwpf *to_rwpf(struct v4l2_subdev *subdev) struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index); struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index); +int vsp1_rpf_create_links(struct vsp1_device *vsp1, + struct vsp1_entity *entity); +int vsp1_wpf_create_links(struct vsp1_device *vsp1, + struct vsp1_entity *entity); + int vsp1_rwpf_enum_mbus_code(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code); diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index 45eb65f..637d0d6 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -160,8 +160,7 @@ vsp1_video_remote_subdev(struct media_pad *local, u32 *pad) struct media_pad *remote; remote = media_entity_remote_pad(local); - if (remote == NULL || - media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + if (!remote || !is_media_entity_v4l2_subdev(remote->entity)) return NULL; if (pad) @@ -283,24 +282,35 @@ static int vsp1_pipeline_validate_branch(struct vsp1_pipeline *pipe, struct vsp1_rwpf *output) { struct vsp1_entity *entity; - unsigned int entities = 0; + struct media_entity_enum ent_enum; struct media_pad *pad; + int rval; bool bru_found = false; input->location.left = 0; input->location.top = 0; + rval = media_entity_enum_init( + &ent_enum, input->entity.pads[RWPF_PAD_SOURCE].graph_obj.mdev); + if (rval) + return rval; + pad = media_entity_remote_pad(&input->entity.pads[RWPF_PAD_SOURCE]); while (1) { - if (pad == NULL) - return -EPIPE; + if (pad == NULL) { + rval = -EPIPE; + goto out; + } /* We've reached a video node, that shouldn't have happened. */ - if (media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) - return -EPIPE; + if (!is_media_entity_v4l2_subdev(pad->entity)) { + rval = -EPIPE; + goto out; + } - entity = to_vsp1_entity(media_entity_to_v4l2_subdev(pad->entity)); + entity = to_vsp1_entity( + media_entity_to_v4l2_subdev(pad->entity)); /* A BRU is present in the pipeline, store the compose rectangle * location in the input RPF for use when configuring the RPF. @@ -323,15 +333,18 @@ static int vsp1_pipeline_validate_branch(struct vsp1_pipeline *pipe, break; /* Ensure the branch has no loop. */ - if (entities & (1 << entity->subdev.entity.id)) - return -EPIPE; - - entities |= 1 << entity->subdev.entity.id; + if (media_entity_enum_test_and_set(&ent_enum, + &entity->subdev.entity)) { + rval = -EPIPE; + goto out; + } /* UDS can't be chained. */ if (entity->type == VSP1_ENTITY_UDS) { - if (pipe->uds) - return -EPIPE; + if (pipe->uds) { + rval = -EPIPE; + goto out; + } pipe->uds = entity; pipe->uds_input = bru_found ? pipe->bru @@ -349,9 +362,12 @@ static int vsp1_pipeline_validate_branch(struct vsp1_pipeline *pipe, /* The last entity must be the output WPF. */ if (entity != &output->entity) - return -EPIPE; + rval = -EPIPE; - return 0; +out: + media_entity_enum_cleanup(&ent_enum); + + return rval; } static void __vsp1_pipeline_cleanup(struct vsp1_pipeline *pipe) @@ -380,13 +396,19 @@ static int vsp1_pipeline_validate(struct vsp1_pipeline *pipe, { struct media_entity_graph graph; struct media_entity *entity = &video->video.entity; - struct media_device *mdev = entity->parent; + struct media_device *mdev = entity->graph_obj.mdev; unsigned int i; int ret; mutex_lock(&mdev->graph_mutex); /* Walk the graph to locate the entities and video nodes. */ + ret = media_entity_graph_walk_init(&graph, mdev); + if (ret) { + mutex_unlock(&mdev->graph_mutex); + return ret; + } + media_entity_graph_walk_start(&graph, entity); while ((entity = media_entity_graph_walk_next(&graph))) { @@ -394,7 +416,7 @@ static int vsp1_pipeline_validate(struct vsp1_pipeline *pipe, struct vsp1_rwpf *rwpf; struct vsp1_entity *e; - if (media_entity_type(entity) != MEDIA_ENT_T_V4L2_SUBDEV) { + if (is_media_entity_v4l2_io(entity)) { pipe->num_video++; continue; } @@ -420,6 +442,8 @@ static int vsp1_pipeline_validate(struct vsp1_pipeline *pipe, mutex_unlock(&mdev->graph_mutex); + media_entity_graph_walk_cleanup(&graph); + /* We need one output and at least one input. */ if (pipe->num_inputs == 0 || !pipe->output) { ret = -EPIPE; @@ -663,7 +687,7 @@ void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe, pad = media_entity_remote_pad(&input->pads[RWPF_PAD_SOURCE]); while (pad) { - if (media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + if (!is_media_entity_v4l2_subdev(pad->entity)) break; entity = to_vsp1_entity(media_entity_to_v4l2_subdev(pad->entity)); @@ -1193,7 +1217,7 @@ int vsp1_video_init(struct vsp1_video *video, struct vsp1_entity *rwpf) video->pipe.state = VSP1_PIPELINE_STOPPED; /* Initialize the media entity... */ - ret = media_entity_init(&video->video.entity, 1, &video->pad, 0); + ret = media_entity_pads_init(&video->video.entity, 1, &video->pad); if (ret < 0) return ret; diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c index 95b62f4..cbf514a 100644 --- a/drivers/media/platform/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/vsp1/vsp1_wpf.c @@ -220,7 +220,6 @@ struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index) struct v4l2_subdev *subdev; struct vsp1_video *video; struct vsp1_rwpf *wpf; - unsigned int flags; int ret; wpf = devm_kzalloc(vsp1->dev, sizeof(*wpf), GFP_KERNEL); @@ -276,20 +275,6 @@ struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index) goto error; wpf->entity.video = video; - - /* Connect the video device to the WPF. All connections are immutable - * except for the WPF0 source link if a LIF is present. - */ - flags = MEDIA_LNK_FL_ENABLED; - if (!(vsp1->pdata.features & VSP1_HAS_LIF) || index != 0) - flags |= MEDIA_LNK_FL_IMMUTABLE; - - ret = media_entity_create_link(&wpf->entity.subdev.entity, - RWPF_PAD_SOURCE, - &wpf->video.video.entity, 0, flags); - if (ret < 0) - goto error; - wpf->entity.sink = &wpf->video.video.entity; return wpf; @@ -298,3 +283,28 @@ error: vsp1_entity_destroy(&wpf->entity); return ERR_PTR(ret); } + +/* + * vsp1_wpf_create_links() - RPF pads links creation + * @vsp1: Pointer to VSP1 device + * @entity: Pointer to VSP1 entity + * + * return negative error code or zero on success + */ +int vsp1_wpf_create_links(struct vsp1_device *vsp1, + struct vsp1_entity *entity) +{ + struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev); + unsigned int flags; + + /* Connect the video device to the WPF. All connections are immutable + * except for the WPF0 source link if a LIF is present. + */ + flags = MEDIA_LNK_FL_ENABLED; + if (!(vsp1->pdata.features & VSP1_HAS_LIF) || entity->index != 0) + flags |= MEDIA_LNK_FL_IMMUTABLE; + + return media_create_pad_link(&wpf->entity.subdev.entity, + RWPF_PAD_SOURCE, + &wpf->video.video.entity, 0, flags); +} diff --git a/drivers/media/platform/xilinx/xilinx-dma.c b/drivers/media/platform/xilinx/xilinx-dma.c index 722758f..7f6898b 100644 --- a/drivers/media/platform/xilinx/xilinx-dma.c +++ b/drivers/media/platform/xilinx/xilinx-dma.c @@ -49,8 +49,7 @@ xvip_dma_remote_subdev(struct media_pad *local, u32 *pad) struct media_pad *remote; remote = media_entity_remote_pad(local); - if (remote == NULL || - media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + if (!remote || !is_media_entity_v4l2_subdev(remote->entity)) return NULL; if (pad) @@ -113,8 +112,7 @@ static int xvip_pipeline_start_stop(struct xvip_pipeline *pipe, bool start) break; pad = media_entity_remote_pad(pad); - if (pad == NULL || - media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) break; entity = pad->entity; @@ -181,19 +179,26 @@ static int xvip_pipeline_validate(struct xvip_pipeline *pipe, { struct media_entity_graph graph; struct media_entity *entity = &start->video.entity; - struct media_device *mdev = entity->parent; + struct media_device *mdev = entity->graph_obj.mdev; unsigned int num_inputs = 0; unsigned int num_outputs = 0; + int ret; mutex_lock(&mdev->graph_mutex); /* Walk the graph to locate the video nodes. */ + ret = media_entity_graph_walk_init(&graph, entity->graph_obj.mdev); + if (ret) { + mutex_unlock(&mdev->graph_mutex); + return ret; + } + media_entity_graph_walk_start(&graph, entity); while ((entity = media_entity_graph_walk_next(&graph))) { struct xvip_dma *dma; - if (entity->type != MEDIA_ENT_T_DEVNODE_V4L) + if (entity->function != MEDIA_ENT_F_IO_V4L) continue; dma = to_xvip_dma(media_entity_to_video_device(entity)); @@ -208,6 +213,8 @@ static int xvip_pipeline_validate(struct xvip_pipeline *pipe, mutex_unlock(&mdev->graph_mutex); + media_entity_graph_walk_cleanup(&graph); + /* We need exactly one output and zero or one input. */ if (num_outputs != 1 || num_inputs > 1) return -EPIPE; @@ -677,7 +684,7 @@ int xvip_dma_init(struct xvip_composite_device *xdev, struct xvip_dma *dma, dma->pad.flags = type == V4L2_BUF_TYPE_VIDEO_CAPTURE ? MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE; - ret = media_entity_init(&dma->video.entity, 1, &dma->pad, 0); + ret = media_entity_pads_init(&dma->video.entity, 1, &dma->pad); if (ret < 0) goto error; diff --git a/drivers/media/platform/xilinx/xilinx-tpg.c b/drivers/media/platform/xilinx/xilinx-tpg.c index 8bd7e37..2ec1f6c 100644 --- a/drivers/media/platform/xilinx/xilinx-tpg.c +++ b/drivers/media/platform/xilinx/xilinx-tpg.c @@ -838,7 +838,7 @@ static int xtpg_probe(struct platform_device *pdev) subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; subdev->entity.ops = &xtpg_media_ops; - ret = media_entity_init(&subdev->entity, xtpg->npads, xtpg->pads, 0); + ret = media_entity_pads_init(&subdev->entity, xtpg->npads, xtpg->pads); if (ret < 0) goto error; diff --git a/drivers/media/platform/xilinx/xilinx-vipp.c b/drivers/media/platform/xilinx/xilinx-vipp.c index b9bf24f..e795a45 100644 --- a/drivers/media/platform/xilinx/xilinx-vipp.c +++ b/drivers/media/platform/xilinx/xilinx-vipp.c @@ -156,7 +156,7 @@ static int xvip_graph_build_one(struct xvip_composite_device *xdev, local->name, local_pad->index, remote->name, remote_pad->index); - ret = media_entity_create_link(local, local_pad->index, + ret = media_create_pad_link(local, local_pad->index, remote, remote_pad->index, link_flags); if (ret < 0) { @@ -270,7 +270,7 @@ static int xvip_graph_build_dma(struct xvip_composite_device *xdev) source->name, source_pad->index, sink->name, sink_pad->index); - ret = media_entity_create_link(source, source_pad->index, + ret = media_create_pad_link(source, source_pad->index, sink, sink_pad->index, link_flags); if (ret < 0) { @@ -311,7 +311,7 @@ static int xvip_graph_notify_complete(struct v4l2_async_notifier *notifier) if (ret < 0) dev_err(xdev->dev, "failed to register subdev nodes\n"); - return ret; + return media_device_register(&xdev->media_dev); } static int xvip_graph_notify_bound(struct v4l2_async_notifier *notifier, @@ -573,6 +573,7 @@ static void xvip_composite_v4l2_cleanup(struct xvip_composite_device *xdev) { v4l2_device_unregister(&xdev->v4l2_dev); media_device_unregister(&xdev->media_dev); + media_device_cleanup(&xdev->media_dev); } static int xvip_composite_v4l2_init(struct xvip_composite_device *xdev) @@ -584,19 +585,14 @@ static int xvip_composite_v4l2_init(struct xvip_composite_device *xdev) sizeof(xdev->media_dev.model)); xdev->media_dev.hw_revision = 0; - ret = media_device_register(&xdev->media_dev); - if (ret < 0) { - dev_err(xdev->dev, "media device registration failed (%d)\n", - ret); - return ret; - } + media_device_init(&xdev->media_dev); xdev->v4l2_dev.mdev = &xdev->media_dev; ret = v4l2_device_register(xdev->dev, &xdev->v4l2_dev); if (ret < 0) { dev_err(xdev->dev, "V4L2 device registration failed (%d)\n", ret); - media_device_unregister(&xdev->media_dev); + media_device_cleanup(&xdev->media_dev); return ret; } diff --git a/drivers/media/usb/au0828/au0828-cards.c b/drivers/media/usb/au0828/au0828-cards.c index 6b469e8..ca861ae 100644 --- a/drivers/media/usb/au0828/au0828-cards.c +++ b/drivers/media/usb/au0828/au0828-cards.c @@ -228,6 +228,10 @@ void au0828_card_analog_fe_setup(struct au0828_dev *dev) "au8522", 0x8e >> 1, NULL); if (sd == NULL) pr_err("analog subdev registration failed\n"); +#ifdef CONFIG_MEDIA_CONTROLLER + if (sd) + dev->decoder = &sd->entity; +#endif } /* Setup tuners */ diff --git a/drivers/media/usb/au0828/au0828-core.c b/drivers/media/usb/au0828/au0828-core.c index 0934024..9e29e70 100644 --- a/drivers/media/usb/au0828/au0828-core.c +++ b/drivers/media/usb/au0828/au0828-core.c @@ -27,6 +27,9 @@ #include <media/v4l2-common.h> #include <linux/mutex.h> +/* Due to enum tuner_pad_index */ +#include <media/tuner.h> + /* * 1 = General debug messages * 2 = USB handling @@ -127,8 +130,23 @@ static int recv_control_msg(struct au0828_dev *dev, u16 request, u32 value, return status; } +static void au0828_unregister_media_device(struct au0828_dev *dev) +{ + +#ifdef CONFIG_MEDIA_CONTROLLER + if (dev->media_dev) { + media_device_unregister(dev->media_dev); + media_device_cleanup(dev->media_dev); + kfree(dev->media_dev); + dev->media_dev = NULL; + } +#endif +} + static void au0828_usb_release(struct au0828_dev *dev) { + au0828_unregister_media_device(dev); + /* I2C */ au0828_i2c_unregister(dev); @@ -136,6 +154,20 @@ static void au0828_usb_release(struct au0828_dev *dev) } #ifdef CONFIG_VIDEO_AU0828_V4L2 + +static void au0828_usb_v4l2_media_release(struct au0828_dev *dev) +{ +#ifdef CONFIG_MEDIA_CONTROLLER + int i; + + for (i = 0; i < AU0828_MAX_INPUT; i++) { + if (AUVI_INPUT(i).type == AU0828_VMUX_UNDEFINED) + return; + media_device_unregister_entity(&dev->input_ent[i]); + } +#endif +} + static void au0828_usb_v4l2_release(struct v4l2_device *v4l2_dev) { struct au0828_dev *dev = @@ -143,6 +175,7 @@ static void au0828_usb_v4l2_release(struct v4l2_device *v4l2_dev) v4l2_ctrl_handler_free(&dev->v4l2_ctrl_hdl); v4l2_device_unregister(&dev->v4l2_dev); + au0828_usb_v4l2_media_release(dev); au0828_usb_release(dev); } #endif @@ -174,12 +207,123 @@ static void au0828_usb_disconnect(struct usb_interface *interface) au0828_analog_unregister(dev); v4l2_device_disconnect(&dev->v4l2_dev); v4l2_device_put(&dev->v4l2_dev); + /* + * No need to call au0828_usb_release() if V4L2 is enabled, + * as this is already called via au0828_usb_v4l2_release() + */ return; } #endif au0828_usb_release(dev); } +static int au0828_media_device_init(struct au0828_dev *dev, + struct usb_device *udev) +{ +#ifdef CONFIG_MEDIA_CONTROLLER + struct media_device *mdev; + + mdev = kzalloc(sizeof(*mdev), GFP_KERNEL); + if (!mdev) + return -ENOMEM; + + mdev->dev = &udev->dev; + + if (!dev->board.name) + strlcpy(mdev->model, "unknown au0828", sizeof(mdev->model)); + else + strlcpy(mdev->model, dev->board.name, sizeof(mdev->model)); + if (udev->serial) + strlcpy(mdev->serial, udev->serial, sizeof(mdev->serial)); + strcpy(mdev->bus_info, udev->devpath); + mdev->hw_revision = le16_to_cpu(udev->descriptor.bcdDevice); + mdev->driver_version = LINUX_VERSION_CODE; + + media_device_init(mdev); + + dev->media_dev = mdev; +#endif + return 0; +} + + +static int au0828_create_media_graph(struct au0828_dev *dev) +{ +#ifdef CONFIG_MEDIA_CONTROLLER + struct media_device *mdev = dev->media_dev; + struct media_entity *entity; + struct media_entity *tuner = NULL, *decoder = NULL; + int i, ret; + + if (!mdev) + return 0; + + media_device_for_each_entity(entity, mdev) { + switch (entity->function) { + case MEDIA_ENT_F_TUNER: + tuner = entity; + break; + case MEDIA_ENT_F_ATV_DECODER: + decoder = entity; + break; + } + } + + /* Analog setup, using tuner as a link */ + + /* Something bad happened! */ + if (!decoder) + return -EINVAL; + + if (tuner) { + ret = media_create_pad_link(tuner, TUNER_PAD_IF_OUTPUT, + decoder, 0, + MEDIA_LNK_FL_ENABLED); + if (ret) + return ret; + } + ret = media_create_pad_link(decoder, 1, &dev->vdev.entity, 0, + MEDIA_LNK_FL_ENABLED); + if (ret) + return ret; + ret = media_create_pad_link(decoder, 2, &dev->vbi_dev.entity, 0, + MEDIA_LNK_FL_ENABLED); + if (ret) + return ret; + + for (i = 0; i < AU0828_MAX_INPUT; i++) { + struct media_entity *ent = &dev->input_ent[i]; + + if (AUVI_INPUT(i).type == AU0828_VMUX_UNDEFINED) + break; + + switch (AUVI_INPUT(i).type) { + case AU0828_VMUX_CABLE: + case AU0828_VMUX_TELEVISION: + case AU0828_VMUX_DVB: + if (!tuner) + break; + + ret = media_create_pad_link(ent, 0, tuner, + TUNER_PAD_RF_INPUT, + MEDIA_LNK_FL_ENABLED); + if (ret) + return ret; + break; + case AU0828_VMUX_COMPOSITE: + case AU0828_VMUX_SVIDEO: + default: /* AU0828_VMUX_DEBUG */ + /* FIXME: fix the decoder PAD */ + ret = media_create_pad_link(ent, 0, decoder, 0, 0); + if (ret) + return ret; + break; + } + } +#endif + return 0; +} + static int au0828_usb_probe(struct usb_interface *interface, const struct usb_device_id *id) { @@ -224,11 +368,23 @@ static int au0828_usb_probe(struct usb_interface *interface, dev->boardnr = id->driver_info; dev->board = au0828_boards[dev->boardnr]; + /* Initialize the media controller */ + retval = au0828_media_device_init(dev, usbdev); + if (retval) { + pr_err("%s() au0828_media_device_init failed\n", + __func__); + mutex_unlock(&dev->lock); + kfree(dev); + return retval; + } #ifdef CONFIG_VIDEO_AU0828_V4L2 dev->v4l2_dev.release = au0828_usb_v4l2_release; /* Create the v4l2_device */ +#ifdef CONFIG_MEDIA_CONTROLLER + dev->v4l2_dev.mdev = dev->media_dev; +#endif retval = v4l2_device_register(&interface->dev, &dev->v4l2_dev); if (retval) { pr_err("%s() v4l2_device_register failed\n", @@ -287,6 +443,21 @@ static int au0828_usb_probe(struct usb_interface *interface, mutex_unlock(&dev->lock); + retval = au0828_create_media_graph(dev); + if (retval) { + pr_err("%s() au0282_dev_register failed to create graph\n", + __func__); + goto done; + } + +#ifdef CONFIG_MEDIA_CONTROLLER + retval = media_device_register(dev->media_dev); +#endif + +done: + if (retval < 0) + au0828_usb_disconnect(interface); + return retval; } diff --git a/drivers/media/usb/au0828/au0828-dvb.c b/drivers/media/usb/au0828/au0828-dvb.c index c267d76..94363a3 100644 --- a/drivers/media/usb/au0828/au0828-dvb.c +++ b/drivers/media/usb/au0828/au0828-dvb.c @@ -415,6 +415,11 @@ static int dvb_register(struct au0828_dev *dev) result); goto fail_adapter; } + +#ifdef CONFIG_MEDIA_CONTROLLER_DVB + dvb->adapter.mdev = dev->media_dev; +#endif + dvb->adapter.priv = dev; /* register frontend */ @@ -480,8 +485,15 @@ static int dvb_register(struct au0828_dev *dev) dvb->start_count = 0; dvb->stop_count = 0; + + result = dvb_create_media_graph(&dvb->adapter, false); + if (result < 0) + goto fail_create_graph; + return 0; +fail_create_graph: + dvb_net_release(&dvb->net); fail_fe_conn: dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); fail_fe_mem: diff --git a/drivers/media/usb/au0828/au0828-video.c b/drivers/media/usb/au0828/au0828-video.c index 0a725a1..8c54fd2 100644 --- a/drivers/media/usb/au0828/au0828-video.c +++ b/drivers/media/usb/au0828/au0828-video.c @@ -638,6 +638,64 @@ static inline int au0828_isoc_copy(struct au0828_dev *dev, struct urb *urb) return rc; } +static int au0828_enable_analog_tuner(struct au0828_dev *dev) +{ +#ifdef CONFIG_MEDIA_CONTROLLER + struct media_device *mdev = dev->media_dev; + struct media_entity *source; + struct media_link *link, *found_link = NULL; + int ret, active_links = 0; + + if (!mdev || !dev->decoder) + return 0; + + /* + * This will find the tuner that is connected into the decoder. + * Technically, this is not 100% correct, as the device may be + * using an analog input instead of the tuner. However, as we can't + * do DVB streaming while the DMA engine is being used for V4L2, + * this should be enough for the actual needs. + */ + list_for_each_entry(link, &dev->decoder->links, list) { + if (link->sink->entity == dev->decoder) { + found_link = link; + if (link->flags & MEDIA_LNK_FL_ENABLED) + active_links++; + break; + } + } + + if (active_links == 1 || !found_link) + return 0; + + source = found_link->source->entity; + list_for_each_entry(link, &source->links, list) { + struct media_entity *sink; + int flags = 0; + + sink = link->sink->entity; + + if (sink == dev->decoder) + flags = MEDIA_LNK_FL_ENABLED; + + ret = media_entity_setup_link(link, flags); + if (ret) { + pr_err( + "Couldn't change link %s->%s to %s. Error %d\n", + source->name, sink->name, + flags ? "enabled" : "disabled", + ret); + return ret; + } else + au0828_isocdbg( + "link %s->%s was %s\n", + source->name, sink->name, + flags ? "ENABLED" : "disabled"); + } +#endif + return 0; +} + static int queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) @@ -650,6 +708,8 @@ static int queue_setup(struct vb2_queue *vq, *nplanes = 1; sizes[0] = size; + au0828_enable_analog_tuner(dev); + return 0; } @@ -1735,6 +1795,69 @@ static int au0828_vb2_setup(struct au0828_dev *dev) return 0; } +static void au0828_analog_create_entities(struct au0828_dev *dev) +{ +#if defined(CONFIG_MEDIA_CONTROLLER) + static const char * const inames[] = { + [AU0828_VMUX_COMPOSITE] = "Composite", + [AU0828_VMUX_SVIDEO] = "S-Video", + [AU0828_VMUX_CABLE] = "Cable TV", + [AU0828_VMUX_TELEVISION] = "Television", + [AU0828_VMUX_DVB] = "DVB", + [AU0828_VMUX_DEBUG] = "tv debug" + }; + int ret, i; + + /* Initialize Video and VBI pads */ + dev->video_pad.flags = MEDIA_PAD_FL_SINK; + ret = media_entity_pads_init(&dev->vdev.entity, 1, &dev->video_pad); + if (ret < 0) + pr_err("failed to initialize video media entity!\n"); + + dev->vbi_pad.flags = MEDIA_PAD_FL_SINK; + ret = media_entity_pads_init(&dev->vbi_dev.entity, 1, &dev->vbi_pad); + if (ret < 0) + pr_err("failed to initialize vbi media entity!\n"); + + /* Create entities for each input connector */ + for (i = 0; i < AU0828_MAX_INPUT; i++) { + struct media_entity *ent = &dev->input_ent[i]; + + if (AUVI_INPUT(i).type == AU0828_VMUX_UNDEFINED) + break; + + ent->name = inames[AUVI_INPUT(i).type]; + ent->flags = MEDIA_ENT_FL_CONNECTOR; + dev->input_pad[i].flags = MEDIA_PAD_FL_SOURCE; + + switch (AUVI_INPUT(i).type) { + case AU0828_VMUX_COMPOSITE: + ent->function = MEDIA_ENT_F_CONN_COMPOSITE; + break; + case AU0828_VMUX_SVIDEO: + ent->function = MEDIA_ENT_F_CONN_SVIDEO; + break; + case AU0828_VMUX_CABLE: + case AU0828_VMUX_TELEVISION: + case AU0828_VMUX_DVB: + ent->function = MEDIA_ENT_F_CONN_RF; + break; + default: /* AU0828_VMUX_DEBUG */ + ent->function = MEDIA_ENT_F_CONN_TEST; + break; + } + + ret = media_entity_pads_init(ent, 1, &dev->input_pad[i]); + if (ret < 0) + pr_err("failed to initialize input pad[%d]!\n", i); + + ret = media_device_register_entity(dev->media_dev, ent); + if (ret < 0) + pr_err("failed to register input entity %d!\n", i); + } +#endif +} + /**************************************************************************/ int au0828_analog_register(struct au0828_dev *dev, @@ -1823,6 +1946,9 @@ int au0828_analog_register(struct au0828_dev *dev, dev->vbi_dev.queue->lock = &dev->vb_vbi_queue_lock; strcpy(dev->vbi_dev.name, "au0828a vbi"); + /* Init entities at the Media Controller */ + au0828_analog_create_entities(dev); + /* initialize videobuf2 stuff */ retval = au0828_vb2_setup(dev); if (retval != 0) { diff --git a/drivers/media/usb/au0828/au0828.h b/drivers/media/usb/au0828/au0828.h index 60b5939..8276072 100644 --- a/drivers/media/usb/au0828/au0828.h +++ b/drivers/media/usb/au0828/au0828.h @@ -33,6 +33,7 @@ #include <media/v4l2-device.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-fh.h> +#include <media/media-device.h> /* DVB */ #include "demux.h" @@ -93,7 +94,6 @@ struct au0828_board { unsigned char has_ir_i2c:1; unsigned char has_analog:1; struct au0828_input input[AU0828_MAX_INPUT]; - }; struct au0828_dvb { @@ -276,6 +276,14 @@ struct au0828_dev { /* Preallocated transfer digital transfer buffers */ char *dig_transfer_buffer[URB_COUNT]; + +#ifdef CONFIG_MEDIA_CONTROLLER + struct media_device *media_dev; + struct media_pad video_pad, vbi_pad; + struct media_entity *decoder; + struct media_entity input_ent[AU0828_MAX_INPUT]; + struct media_pad input_pad[AU0828_MAX_INPUT]; +#endif }; diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c index 89dc695..620b83d 100644 --- a/drivers/media/usb/cx231xx/cx231xx-cards.c +++ b/drivers/media/usb/cx231xx/cx231xx-cards.c @@ -1172,6 +1172,7 @@ static void cx231xx_unregister_media_device(struct cx231xx *dev) #ifdef CONFIG_MEDIA_CONTROLLER if (dev->media_dev) { media_device_unregister(dev->media_dev); + media_device_cleanup(dev->media_dev); kfree(dev->media_dev); dev->media_dev = NULL; } @@ -1185,8 +1186,6 @@ static void cx231xx_unregister_media_device(struct cx231xx *dev) */ void cx231xx_release_resources(struct cx231xx *dev) { - cx231xx_unregister_media_device(dev); - cx231xx_release_analog_resources(dev); cx231xx_remove_from_devlist(dev); @@ -1199,22 +1198,23 @@ void cx231xx_release_resources(struct cx231xx *dev) /* delete v4l2 device */ v4l2_device_unregister(&dev->v4l2_dev); + cx231xx_unregister_media_device(dev); + usb_put_dev(dev->udev); /* Mark device as unused */ clear_bit(dev->devno, &cx231xx_devused); } -static void cx231xx_media_device_register(struct cx231xx *dev, - struct usb_device *udev) +static int cx231xx_media_device_init(struct cx231xx *dev, + struct usb_device *udev) { #ifdef CONFIG_MEDIA_CONTROLLER struct media_device *mdev; - int ret; mdev = kzalloc(sizeof(*mdev), GFP_KERNEL); if (!mdev) - return; + return -ENOMEM; mdev->dev = dev->dev; strlcpy(mdev->model, dev->board.name, sizeof(mdev->model)); @@ -1224,35 +1224,30 @@ static void cx231xx_media_device_register(struct cx231xx *dev, mdev->hw_revision = le16_to_cpu(udev->descriptor.bcdDevice); mdev->driver_version = LINUX_VERSION_CODE; - ret = media_device_register(mdev); - if (ret) { - dev_err(dev->dev, - "Couldn't create a media device. Error: %d\n", - ret); - kfree(mdev); - return; - } + media_device_init(mdev); dev->media_dev = mdev; #endif + return 0; } -static void cx231xx_create_media_graph(struct cx231xx *dev) +static int cx231xx_create_media_graph(struct cx231xx *dev) { #ifdef CONFIG_MEDIA_CONTROLLER struct media_device *mdev = dev->media_dev; struct media_entity *entity; struct media_entity *tuner = NULL, *decoder = NULL; + int ret; if (!mdev) - return; + return 0; media_device_for_each_entity(entity, mdev) { - switch (entity->type) { - case MEDIA_ENT_T_V4L2_SUBDEV_TUNER: + switch (entity->function) { + case MEDIA_ENT_F_TUNER: tuner = entity; break; - case MEDIA_ENT_T_V4L2_SUBDEV_DECODER: + case MEDIA_ENT_F_ATV_DECODER: decoder = entity; break; } @@ -1261,16 +1256,24 @@ static void cx231xx_create_media_graph(struct cx231xx *dev) /* Analog setup, using tuner as a link */ if (!decoder) - return; + return 0; - if (tuner) - media_entity_create_link(tuner, 0, decoder, 0, - MEDIA_LNK_FL_ENABLED); - media_entity_create_link(decoder, 1, &dev->vdev.entity, 0, - MEDIA_LNK_FL_ENABLED); - media_entity_create_link(decoder, 2, &dev->vbi_dev.entity, 0, - MEDIA_LNK_FL_ENABLED); + if (tuner) { + ret = media_create_pad_link(tuner, TUNER_PAD_IF_OUTPUT, decoder, 0, + MEDIA_LNK_FL_ENABLED); + if (ret < 0) + return ret; + } + ret = media_create_pad_link(decoder, 1, &dev->vdev.entity, 0, + MEDIA_LNK_FL_ENABLED); + if (ret < 0) + return ret; + ret = media_create_pad_link(decoder, 2, &dev->vbi_dev.entity, 0, + MEDIA_LNK_FL_ENABLED); + if (ret < 0) + return ret; #endif + return 0; } /* @@ -1660,8 +1663,12 @@ static int cx231xx_usb_probe(struct usb_interface *interface, /* save our data pointer in this interface device */ usb_set_intfdata(interface, dev); - /* Register the media controller */ - cx231xx_media_device_register(dev, udev); + /* Initialize the media controller */ + retval = cx231xx_media_device_init(dev, udev); + if (retval) { + dev_err(d, "cx231xx_media_device_init failed\n"); + goto err_media_init; + } /* Create v4l2 device */ #ifdef CONFIG_MEDIA_CONTROLLER @@ -1732,9 +1739,19 @@ static int cx231xx_usb_probe(struct usb_interface *interface, /* load other modules required */ request_modules(dev); - cx231xx_create_media_graph(dev); + retval = cx231xx_create_media_graph(dev); + if (retval < 0) + goto done; + +#ifdef CONFIG_MEDIA_CONTROLLER + retval = media_device_register(dev->media_dev); +#endif + +done: + if (retval < 0) + cx231xx_release_resources(dev); + return retval; - return 0; err_video_alt: /* cx231xx_uninit_dev: */ cx231xx_close_extension(dev); @@ -1746,6 +1763,8 @@ err_video_alt: err_init: v4l2_device_unregister(&dev->v4l2_dev); err_v4l2: + cx231xx_unregister_media_device(dev); +err_media_init: usb_set_intfdata(interface, NULL); err_if: usb_put_dev(udev); diff --git a/drivers/media/usb/cx231xx/cx231xx-dvb.c b/drivers/media/usb/cx231xx/cx231xx-dvb.c index e3594b9..b8d5b2b 100644 --- a/drivers/media/usb/cx231xx/cx231xx-dvb.c +++ b/drivers/media/usb/cx231xx/cx231xx-dvb.c @@ -551,10 +551,14 @@ static int register_dvb(struct cx231xx_dvb *dvb, /* register network adapter */ dvb_net_init(&dvb->adapter, &dvb->net, &dvb->demux.dmx); - dvb_create_media_graph(&dvb->adapter); + result = dvb_create_media_graph(&dvb->adapter, false); + if (result < 0) + goto fail_create_graph; return 0; +fail_create_graph: + dvb_net_release(&dvb->net); fail_fe_conn: dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); fail_fe_mem: diff --git a/drivers/media/usb/cx231xx/cx231xx-video.c b/drivers/media/usb/cx231xx/cx231xx-video.c index a70850f..9b88cd8 100644 --- a/drivers/media/usb/cx231xx/cx231xx-video.c +++ b/drivers/media/usb/cx231xx/cx231xx-video.c @@ -106,7 +106,7 @@ static int cx231xx_enable_analog_tuner(struct cx231xx *dev) struct media_device *mdev = dev->media_dev; struct media_entity *entity, *decoder = NULL, *source; struct media_link *link, *found_link = NULL; - int i, ret, active_links = 0; + int ret, active_links = 0; if (!mdev) return 0; @@ -119,7 +119,7 @@ static int cx231xx_enable_analog_tuner(struct cx231xx *dev) * this should be enough for the actual needs. */ media_device_for_each_entity(entity, mdev) { - if (entity->type == MEDIA_ENT_T_V4L2_SUBDEV_DECODER) { + if (entity->function == MEDIA_ENT_F_ATV_DECODER) { decoder = entity; break; } @@ -127,8 +127,7 @@ static int cx231xx_enable_analog_tuner(struct cx231xx *dev) if (!decoder) return 0; - for (i = 0; i < decoder->num_links; i++) { - link = &decoder->links[i]; + list_for_each_entry(link, &decoder->links, list) { if (link->sink->entity == decoder) { found_link = link; if (link->flags & MEDIA_LNK_FL_ENABLED) @@ -141,11 +140,10 @@ static int cx231xx_enable_analog_tuner(struct cx231xx *dev) return 0; source = found_link->source->entity; - for (i = 0; i < source->num_links; i++) { + list_for_each_entry(link, &source->links, list) { struct media_entity *sink; int flags = 0; - link = &source->links[i]; sink = link->sink->entity; if (sink == entity) @@ -2177,7 +2175,7 @@ int cx231xx_register_analog_devices(struct cx231xx *dev) cx231xx_vdev_init(dev, &dev->vdev, &cx231xx_video_template, "video"); #if defined(CONFIG_MEDIA_CONTROLLER) dev->video_pad.flags = MEDIA_PAD_FL_SINK; - ret = media_entity_init(&dev->vdev.entity, 1, &dev->video_pad, 0); + ret = media_entity_pads_init(&dev->vdev.entity, 1, &dev->video_pad); if (ret < 0) dev_err(dev->dev, "failed to initialize video media entity!\n"); #endif @@ -2204,7 +2202,7 @@ int cx231xx_register_analog_devices(struct cx231xx *dev) #if defined(CONFIG_MEDIA_CONTROLLER) dev->vbi_pad.flags = MEDIA_PAD_FL_SINK; - ret = media_entity_init(&dev->vbi_dev.entity, 1, &dev->vbi_pad, 0); + ret = media_entity_pads_init(&dev->vbi_dev.entity, 1, &dev->vbi_pad); if (ret < 0) dev_err(dev->dev, "failed to initialize vbi media entity!\n"); #endif diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c index f5df9eaba0..f0565bf 100644 --- a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c @@ -400,17 +400,16 @@ skip_feed_stop: return ret; } -static void dvb_usbv2_media_device_register(struct dvb_usb_adapter *adap) +static int dvb_usbv2_media_device_init(struct dvb_usb_adapter *adap) { #ifdef CONFIG_MEDIA_CONTROLLER_DVB struct media_device *mdev; struct dvb_usb_device *d = adap_to_d(adap); struct usb_device *udev = d->udev; - int ret; mdev = kzalloc(sizeof(*mdev), GFP_KERNEL); if (!mdev) - return; + return -ENOMEM; mdev->dev = &udev->dev; strlcpy(mdev->model, d->name, sizeof(mdev->model)); @@ -420,19 +419,21 @@ static void dvb_usbv2_media_device_register(struct dvb_usb_adapter *adap) mdev->hw_revision = le16_to_cpu(udev->descriptor.bcdDevice); mdev->driver_version = LINUX_VERSION_CODE; - ret = media_device_register(mdev); - if (ret) { - dev_err(&d->udev->dev, - "Couldn't create a media device. Error: %d\n", - ret); - kfree(mdev); - return; - } + media_device_init(mdev); dvb_register_media_controller(&adap->dvb_adap, mdev); dev_info(&d->udev->dev, "media controller created\n"); +#endif + return 0; +} +static int dvb_usbv2_media_device_register(struct dvb_usb_adapter *adap) +{ +#ifdef CONFIG_MEDIA_CONTROLLER_DVB + return media_device_register(adap->dvb_adap.mdev); +#else + return 0; #endif } @@ -444,6 +445,7 @@ static void dvb_usbv2_media_device_unregister(struct dvb_usb_adapter *adap) return; media_device_unregister(adap->dvb_adap.mdev); + media_device_cleanup(adap->dvb_adap.mdev); kfree(adap->dvb_adap.mdev); adap->dvb_adap.mdev = NULL; @@ -467,7 +469,12 @@ static int dvb_usbv2_adapter_dvb_init(struct dvb_usb_adapter *adap) adap->dvb_adap.priv = adap; - dvb_usbv2_media_device_register(adap); + ret = dvb_usbv2_media_device_init(adap); + if (ret < 0) { + dev_dbg(&d->udev->dev, "%s: dvb_usbv2_media_device_init() failed=%d\n", + __func__, ret); + goto err_dvb_register_mc; + } if (d->props->read_mac_address) { ret = d->props->read_mac_address(adap, @@ -518,6 +525,7 @@ err_dvb_dmxdev_init: dvb_dmx_release(&adap->demux); err_dvb_dmx_init: dvb_usbv2_media_device_unregister(adap); +err_dvb_register_mc: dvb_unregister_adapter(&adap->dvb_adap); err_dvb_register_adapter: adap->dvb_adap.priv = NULL; @@ -534,7 +542,6 @@ static int dvb_usbv2_adapter_dvb_exit(struct dvb_usb_adapter *adap) adap->demux.dmx.close(&adap->demux.dmx); dvb_dmxdev_release(&adap->dmxdev); dvb_dmx_release(&adap->demux); - dvb_usbv2_media_device_unregister(adap); dvb_unregister_adapter(&adap->dvb_adap); } @@ -698,9 +705,13 @@ static int dvb_usbv2_adapter_frontend_init(struct dvb_usb_adapter *adap) } } - dvb_create_media_graph(&adap->dvb_adap); + ret = dvb_create_media_graph(&adap->dvb_adap, true); + if (ret < 0) + goto err_dvb_unregister_frontend; - return 0; + ret = dvb_usbv2_media_device_register(adap); + + return ret; err_dvb_unregister_frontend: for (i = count_registered - 1; i >= 0; i--) @@ -840,6 +851,7 @@ static int dvb_usbv2_adapter_exit(struct dvb_usb_device *d) dvb_usbv2_adapter_dvb_exit(&d->adapter[i]); dvb_usbv2_adapter_stream_exit(&d->adapter[i]); dvb_usbv2_adapter_frontend_exit(&d->adapter[i]); + dvb_usbv2_media_device_unregister(&d->adapter[i]); } } diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf.c b/drivers/media/usb/dvb-usb-v2/mxl111sf.c index 1710f90..b669dec 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf.c +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf.c @@ -10,6 +10,7 @@ #include <linux/vmalloc.h> #include <linux/i2c.h> +#include <media/tuner.h> #include "mxl111sf.h" #include "mxl111sf-reg.h" @@ -868,6 +869,10 @@ static struct mxl111sf_tuner_config mxl_tuner_config = { static int mxl111sf_attach_tuner(struct dvb_usb_adapter *adap) { struct mxl111sf_state *state = adap_to_priv(adap); +#ifdef CONFIG_MEDIA_CONTROLLER_DVB + struct media_device *mdev = dvb_get_media_controller(&adap->dvb_adap); + int ret; +#endif int i; pr_debug("%s()\n", __func__); @@ -879,6 +884,21 @@ static int mxl111sf_attach_tuner(struct dvb_usb_adapter *adap) adap->fe[i]->ops.read_signal_strength = adap->fe[i]->ops.tuner_ops.get_rf_strength; } +#ifdef CONFIG_MEDIA_CONTROLLER_DVB + state->tuner.function = MEDIA_ENT_F_TUNER; + state->tuner.name = "mxl111sf tuner"; + state->tuner_pads[TUNER_PAD_RF_INPUT].flags = MEDIA_PAD_FL_SINK; + state->tuner_pads[TUNER_PAD_IF_OUTPUT].flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&state->tuner, + TUNER_NUM_PADS, state->tuner_pads); + if (ret) + return ret; + + ret = media_device_register_entity(mdev, &state->tuner); + if (ret) + return ret; +#endif return 0; } diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf.h b/drivers/media/usb/dvb-usb-v2/mxl111sf.h index ee70df1..846260e 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf.h +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf.h @@ -17,6 +17,7 @@ #define DVB_USB_LOG_PREFIX "mxl111sf" #include "dvb_usb.h" #include <media/tveeprom.h> +#include <media/media-entity.h> #define MXL_EP1_REG_READ 1 #define MXL_EP2_REG_WRITE 2 @@ -85,6 +86,10 @@ struct mxl111sf_state { struct mutex fe_lock; u8 num_frontends; struct mxl111sf_adap_state adap_state[3]; +#ifdef CONFIG_MEDIA_CONTROLLER_DVB + struct media_entity tuner; + struct media_pad tuner_pads[2]; +#endif }; int mxl111sf_read_reg(struct mxl111sf_state *state, u8 addr, u8 *data); diff --git a/drivers/media/usb/dvb-usb/dvb-usb-dvb.c b/drivers/media/usb/dvb-usb/dvb-usb-dvb.c index 8a260c8..9ddfcab 100644 --- a/drivers/media/usb/dvb-usb/dvb-usb-dvb.c +++ b/drivers/media/usb/dvb-usb/dvb-usb-dvb.c @@ -95,17 +95,16 @@ static int dvb_usb_stop_feed(struct dvb_demux_feed *dvbdmxfeed) return dvb_usb_ctrl_feed(dvbdmxfeed, 0); } -static void dvb_usb_media_device_register(struct dvb_usb_adapter *adap) +static int dvb_usb_media_device_init(struct dvb_usb_adapter *adap) { #ifdef CONFIG_MEDIA_CONTROLLER_DVB struct media_device *mdev; struct dvb_usb_device *d = adap->dev; struct usb_device *udev = d->udev; - int ret; mdev = kzalloc(sizeof(*mdev), GFP_KERNEL); if (!mdev) - return; + return -ENOMEM; mdev->dev = &udev->dev; strlcpy(mdev->model, d->desc->name, sizeof(mdev->model)); @@ -115,18 +114,22 @@ static void dvb_usb_media_device_register(struct dvb_usb_adapter *adap) mdev->hw_revision = le16_to_cpu(udev->descriptor.bcdDevice); mdev->driver_version = LINUX_VERSION_CODE; - ret = media_device_register(mdev); - if (ret) { - dev_err(&d->udev->dev, - "Couldn't create a media device. Error: %d\n", - ret); - kfree(mdev); - return; - } + media_device_init(mdev); + dvb_register_media_controller(&adap->dvb_adap, mdev); dev_info(&d->udev->dev, "media controller created\n"); #endif + return 0; +} + +static int dvb_usb_media_device_register(struct dvb_usb_adapter *adap) +{ +#ifdef CONFIG_MEDIA_CONTROLLER_DVB + return media_device_register(adap->dvb_adap.mdev); +#else + return 0; +#endif } static void dvb_usb_media_device_unregister(struct dvb_usb_adapter *adap) @@ -136,6 +139,7 @@ static void dvb_usb_media_device_unregister(struct dvb_usb_adapter *adap) return; media_device_unregister(adap->dvb_adap.mdev); + media_device_cleanup(adap->dvb_adap.mdev); kfree(adap->dvb_adap.mdev); adap->dvb_adap.mdev = NULL; #endif @@ -154,7 +158,11 @@ int dvb_usb_adapter_dvb_init(struct dvb_usb_adapter *adap, short *adapter_nums) } adap->dvb_adap.priv = adap; - dvb_usb_media_device_register(adap); + ret = dvb_usb_media_device_init(adap); + if (ret < 0) { + deb_info("dvb_usb_media_device_init failed: error %d", ret); + goto err_mc; + } if (adap->dev->props.read_mac_address) { if (adap->dev->props.read_mac_address(adap->dev, adap->dvb_adap.proposed_mac) == 0) @@ -204,6 +212,7 @@ err_dmx_dev: dvb_dmx_release(&adap->demux); err_dmx: dvb_usb_media_device_unregister(adap); +err_mc: dvb_unregister_adapter(&adap->dvb_adap); err: return ret; @@ -318,10 +327,16 @@ int dvb_usb_adapter_frontend_init(struct dvb_usb_adapter *adap) adap->num_frontends_initialized++; } + if (ret) + return ret; - dvb_create_media_graph(&adap->dvb_adap); + ret = dvb_create_media_graph(&adap->dvb_adap, true); + if (ret) + return ret; - return 0; + ret = dvb_usb_media_device_register(adap); + + return ret; } int dvb_usb_adapter_frontend_exit(struct dvb_usb_adapter *adap) diff --git a/drivers/media/usb/siano/smsusb.c b/drivers/media/usb/siano/smsusb.c index c945e4c..8abbd3c 100644 --- a/drivers/media/usb/siano/smsusb.c +++ b/drivers/media/usb/siano/smsusb.c @@ -361,10 +361,11 @@ static void *siano_media_device_register(struct smsusb_device_t *dev, mdev->hw_revision = le16_to_cpu(udev->descriptor.bcdDevice); mdev->driver_version = LINUX_VERSION_CODE; + media_device_init(mdev); + ret = media_device_register(mdev); if (ret) { - pr_err("Couldn't create a media device. Error: %d\n", - ret); + media_device_cleanup(mdev); kfree(mdev); return NULL; } diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index 39abbaf..4e71488 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -1656,6 +1656,7 @@ static void uvc_delete(struct uvc_device *dev) #ifdef CONFIG_MEDIA_CONTROLLER if (media_devnode_is_registered(&dev->mdev.devnode)) media_device_unregister(&dev->mdev); + media_device_cleanup(&dev->mdev); #endif list_for_each_safe(p, n, &dev->chains) { @@ -1906,7 +1907,7 @@ static int uvc_probe(struct usb_interface *intf, "linux-uvc-devel mailing list.\n"); } - /* Register the media and V4L2 devices. */ + /* Initialize the media device and register the V4L2 device. */ #ifdef CONFIG_MEDIA_CONTROLLER dev->mdev.dev = &intf->dev; strlcpy(dev->mdev.model, dev->name, sizeof(dev->mdev.model)); @@ -1916,8 +1917,7 @@ static int uvc_probe(struct usb_interface *intf, strcpy(dev->mdev.bus_info, udev->devpath); dev->mdev.hw_revision = le16_to_cpu(udev->descriptor.bcdDevice); dev->mdev.driver_version = LINUX_VERSION_CODE; - if (media_device_register(&dev->mdev) < 0) - goto error; + media_device_init(&dev->mdev); dev->vdev.mdev = &dev->mdev; #endif @@ -1936,6 +1936,11 @@ static int uvc_probe(struct usb_interface *intf, if (uvc_register_chains(dev) < 0) goto error; +#ifdef CONFIG_MEDIA_CONTROLLER + /* Register the media device node */ + if (media_device_register(&dev->mdev) < 0) + goto error; +#endif /* Save our data pointer in the interface data. */ usb_set_intfdata(intf, dev); diff --git a/drivers/media/usb/uvc/uvc_entity.c b/drivers/media/usb/uvc/uvc_entity.c index dc56a59..ac386bb 100644 --- a/drivers/media/usb/uvc/uvc_entity.c +++ b/drivers/media/usb/uvc/uvc_entity.c @@ -19,12 +19,8 @@ #include "uvcvideo.h" -/* ------------------------------------------------------------------------ - * Video subdevices registration and unregistration - */ - -static int uvc_mc_register_entity(struct uvc_video_chain *chain, - struct uvc_entity *entity) +static int uvc_mc_create_links(struct uvc_video_chain *chain, + struct uvc_entity *entity) { const u32 flags = MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE; struct media_entity *sink; @@ -56,16 +52,13 @@ static int uvc_mc_register_entity(struct uvc_video_chain *chain, continue; remote_pad = remote->num_pads - 1; - ret = media_entity_create_link(source, remote_pad, + ret = media_create_pad_link(source, remote_pad, sink, i, flags); if (ret < 0) return ret; } - if (UVC_ENTITY_TYPE(entity) == UVC_TT_STREAMING) - return 0; - - return v4l2_device_register_subdev(&chain->dev->vdev, &entity->subdev); + return 0; } static struct v4l2_subdev_ops uvc_subdev_ops = { @@ -79,7 +72,8 @@ void uvc_mc_cleanup_entity(struct uvc_entity *entity) media_entity_cleanup(&entity->vdev->entity); } -static int uvc_mc_init_entity(struct uvc_entity *entity) +static int uvc_mc_init_entity(struct uvc_video_chain *chain, + struct uvc_entity *entity) { int ret; @@ -88,11 +82,17 @@ static int uvc_mc_init_entity(struct uvc_entity *entity) strlcpy(entity->subdev.name, entity->name, sizeof(entity->subdev.name)); - ret = media_entity_init(&entity->subdev.entity, - entity->num_pads, entity->pads, 0); + ret = media_entity_pads_init(&entity->subdev.entity, + entity->num_pads, entity->pads); + + if (ret < 0) + return ret; + + ret = v4l2_device_register_subdev(&chain->dev->vdev, + &entity->subdev); } else if (entity->vdev != NULL) { - ret = media_entity_init(&entity->vdev->entity, - entity->num_pads, entity->pads, 0); + ret = media_entity_pads_init(&entity->vdev->entity, + entity->num_pads, entity->pads); if (entity->flags & UVC_ENTITY_FLAG_DEFAULT) entity->vdev->entity.flags |= MEDIA_ENT_FL_DEFAULT; } else @@ -107,7 +107,7 @@ int uvc_mc_register_entities(struct uvc_video_chain *chain) int ret; list_for_each_entry(entity, &chain->entities, chain) { - ret = uvc_mc_init_entity(entity); + ret = uvc_mc_init_entity(chain, entity); if (ret < 0) { uvc_printk(KERN_INFO, "Failed to initialize entity for " "entity %u\n", entity->id); @@ -116,9 +116,9 @@ int uvc_mc_register_entities(struct uvc_video_chain *chain) } list_for_each_entry(entity, &chain->entities, chain) { - ret = uvc_mc_register_entity(chain, entity); + ret = uvc_mc_create_links(chain, entity); if (ret < 0) { - uvc_printk(KERN_INFO, "Failed to register entity for " + uvc_printk(KERN_INFO, "Failed to create links for " "entity %u\n", entity->id); return ret; } diff --git a/drivers/media/v4l2-core/tuner-core.c b/drivers/media/v4l2-core/tuner-core.c index 581e21a..76496fd 100644 --- a/drivers/media/v4l2-core/tuner-core.c +++ b/drivers/media/v4l2-core/tuner-core.c @@ -134,8 +134,9 @@ struct tuner { unsigned int type; /* chip type id */ void *config; const char *name; + #if defined(CONFIG_MEDIA_CONTROLLER) - struct media_pad pad; + struct media_pad pad[TUNER_NUM_PADS]; #endif }; @@ -695,11 +696,12 @@ static int tuner_probe(struct i2c_client *client, /* Should be just before return */ register_client: #if defined(CONFIG_MEDIA_CONTROLLER) - t->pad.flags = MEDIA_PAD_FL_SOURCE; - t->sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_TUNER; + t->pad[TUNER_PAD_RF_INPUT].flags = MEDIA_PAD_FL_SINK; + t->pad[TUNER_PAD_IF_OUTPUT].flags = MEDIA_PAD_FL_SOURCE; + t->sd.entity.function = MEDIA_ENT_F_TUNER; t->sd.entity.name = t->name; - ret = media_entity_init(&t->sd.entity, 1, &t->pad, 0); + ret = media_entity_pads_init(&t->sd.entity, TUNER_NUM_PADS, &t->pad[0]); if (ret < 0) { tuner_err("failed to initialize media entity!\n"); kfree(t); diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index 6b1eaed..d8e5994 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -194,9 +194,12 @@ static void v4l2_device_release(struct device *cd) mutex_unlock(&videodev_lock); #if defined(CONFIG_MEDIA_CONTROLLER) - if (v4l2_dev->mdev && - vdev->vfl_type != VFL_TYPE_SUBDEV) - media_device_unregister_entity(&vdev->entity); + if (v4l2_dev->mdev) { + /* Remove interfaces and interface links */ + media_devnode_remove(vdev->intf_devnode); + if (vdev->entity.function != MEDIA_ENT_F_UNKNOWN) + media_device_unregister_entity(&vdev->entity); + } #endif /* Do not call v4l2_device_put if there is no release callback set. @@ -723,6 +726,91 @@ static void determine_valid_ioctls(struct video_device *vdev) BASE_VIDIOC_PRIVATE); } +static int video_register_media_controller(struct video_device *vdev, int type) +{ +#if defined(CONFIG_MEDIA_CONTROLLER) + u32 intf_type; + int ret; + + if (!vdev->v4l2_dev->mdev) + return 0; + + vdev->entity.function = MEDIA_ENT_F_UNKNOWN; + + switch (type) { + case VFL_TYPE_GRABBER: + intf_type = MEDIA_INTF_T_V4L_VIDEO; + vdev->entity.function = MEDIA_ENT_F_IO_V4L; + break; + case VFL_TYPE_VBI: + intf_type = MEDIA_INTF_T_V4L_VBI; + vdev->entity.function = MEDIA_ENT_F_IO_VBI; + break; + case VFL_TYPE_SDR: + intf_type = MEDIA_INTF_T_V4L_SWRADIO; + vdev->entity.function = MEDIA_ENT_F_IO_SWRADIO; + break; + case VFL_TYPE_RADIO: + intf_type = MEDIA_INTF_T_V4L_RADIO; + /* + * Radio doesn't have an entity at the V4L2 side to represent + * radio input or output. Instead, the audio input/output goes + * via either physical wires or ALSA. + */ + break; + case VFL_TYPE_SUBDEV: + intf_type = MEDIA_INTF_T_V4L_SUBDEV; + /* Entity will be created via v4l2_device_register_subdev() */ + break; + default: + return 0; + } + + if (vdev->entity.function != MEDIA_ENT_F_UNKNOWN) { + vdev->entity.name = vdev->name; + + /* Needed just for backward compatibility with legacy MC API */ + vdev->entity.info.dev.major = VIDEO_MAJOR; + vdev->entity.info.dev.minor = vdev->minor; + + ret = media_device_register_entity(vdev->v4l2_dev->mdev, + &vdev->entity); + if (ret < 0) { + printk(KERN_WARNING + "%s: media_device_register_entity failed\n", + __func__); + return ret; + } + } + + vdev->intf_devnode = media_devnode_create(vdev->v4l2_dev->mdev, + intf_type, + 0, VIDEO_MAJOR, + vdev->minor); + if (!vdev->intf_devnode) { + media_device_unregister_entity(&vdev->entity); + return -ENOMEM; + } + + if (vdev->entity.function != MEDIA_ENT_F_UNKNOWN) { + struct media_link *link; + + link = media_create_intf_link(&vdev->entity, + &vdev->intf_devnode->intf, + MEDIA_LNK_FL_ENABLED); + if (!link) { + media_devnode_remove(vdev->intf_devnode); + media_device_unregister_entity(&vdev->entity); + return -ENOMEM; + } + } + + /* FIXME: how to create the other interface links? */ + +#endif + return 0; +} + /** * __video_register_device - register video4linux devices * @vdev: video device structure we want to register @@ -918,22 +1006,9 @@ int __video_register_device(struct video_device *vdev, int type, int nr, /* Increase v4l2_device refcount */ v4l2_device_get(vdev->v4l2_dev); -#if defined(CONFIG_MEDIA_CONTROLLER) /* Part 5: Register the entity. */ - if (vdev->v4l2_dev->mdev && - vdev->vfl_type != VFL_TYPE_SUBDEV) { - vdev->entity.type = MEDIA_ENT_T_DEVNODE_V4L; - vdev->entity.name = vdev->name; - vdev->entity.info.dev.major = VIDEO_MAJOR; - vdev->entity.info.dev.minor = vdev->minor; - ret = media_device_register_entity(vdev->v4l2_dev->mdev, - &vdev->entity); - if (ret < 0) - printk(KERN_WARNING - "%s: media_device_register_entity failed\n", - __func__); - } -#endif + ret = video_register_media_controller(vdev, type); + /* Part 6: Activate this minor. The char device can now be used. */ set_bit(V4L2_FL_REGISTERED, &vdev->flags); diff --git a/drivers/media/v4l2-core/v4l2-device.c b/drivers/media/v4l2-core/v4l2-device.c index 7129e43..06fa5f1 100644 --- a/drivers/media/v4l2-core/v4l2-device.c +++ b/drivers/media/v4l2-core/v4l2-device.c @@ -180,26 +180,26 @@ int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev, return -ENODEV; sd->v4l2_dev = v4l2_dev; - if (sd->internal_ops && sd->internal_ops->registered) { - err = sd->internal_ops->registered(sd); - if (err) - goto error_module; - } - /* This just returns 0 if either of the two args is NULL */ err = v4l2_ctrl_add_handler(v4l2_dev->ctrl_handler, sd->ctrl_handler, NULL); if (err) - goto error_unregister; + goto error_module; #if defined(CONFIG_MEDIA_CONTROLLER) /* Register the entity. */ if (v4l2_dev->mdev) { err = media_device_register_entity(v4l2_dev->mdev, entity); if (err < 0) - goto error_unregister; + goto error_module; } #endif + if (sd->internal_ops && sd->internal_ops->registered) { + err = sd->internal_ops->registered(sd); + if (err) + goto error_unregister; + } + spin_lock(&v4l2_dev->lock); list_add_tail(&sd->list, &v4l2_dev->subdevs); spin_unlock(&v4l2_dev->lock); @@ -207,8 +207,9 @@ int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev, return 0; error_unregister: - if (sd->internal_ops && sd->internal_ops->unregistered) - sd->internal_ops->unregistered(sd); +#if defined(CONFIG_MEDIA_CONTROLLER) + media_device_unregister_entity(entity); +#endif error_module: if (!sd->owner_v4l2_dev) module_put(sd->owner); @@ -258,6 +259,19 @@ int v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev) #if defined(CONFIG_MEDIA_CONTROLLER) sd->entity.info.dev.major = VIDEO_MAJOR; sd->entity.info.dev.minor = vdev->minor; + + /* Interface is created by __video_register_device() */ + if (vdev->v4l2_dev->mdev) { + struct media_link *link; + + link = media_create_intf_link(&sd->entity, + &vdev->intf_devnode->intf, + MEDIA_LNK_FL_ENABLED); + if (!link) { + err = -ENOMEM; + goto clean_up; + } + } #endif sd->devnode = vdev; } @@ -294,7 +308,10 @@ void v4l2_device_unregister_subdev(struct v4l2_subdev *sd) #if defined(CONFIG_MEDIA_CONTROLLER) if (v4l2_dev->mdev) { - media_entity_remove_links(&sd->entity); + /* + * No need to explicitly remove links, as both pads and + * links are removed by the function below, in the right order + */ media_device_unregister_entity(&sd->entity); } #endif diff --git a/drivers/media/v4l2-core/v4l2-flash-led-class.c b/drivers/media/v4l2-core/v4l2-flash-led-class.c index 5d67335..fc5ff8b 100644 --- a/drivers/media/v4l2-core/v4l2-flash-led-class.c +++ b/drivers/media/v4l2-core/v4l2-flash-led-class.c @@ -651,11 +651,11 @@ struct v4l2_flash *v4l2_flash_init( sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; strlcpy(sd->name, config->dev_name, sizeof(sd->name)); - ret = media_entity_init(&sd->entity, 0, NULL, 0); + ret = media_entity_pads_init(&sd->entity, 0, NULL); if (ret < 0) return ERR_PTR(ret); - sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH; + sd->entity.function = MEDIA_ENT_F_FLASH; ret = v4l2_flash_init_controls(v4l2_flash, config); if (ret < 0) diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 83615b8..d630838 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -526,7 +526,7 @@ static int v4l2_subdev_link_validate_get_format(struct media_pad *pad, struct v4l2_subdev_format *fmt) { - if (media_entity_type(pad->entity) == MEDIA_ENT_T_V4L2_SUBDEV) { + if (is_media_entity_v4l2_subdev(pad->entity)) { struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(pad->entity); @@ -535,9 +535,9 @@ v4l2_subdev_link_validate_get_format(struct media_pad *pad, return v4l2_subdev_call(sd, pad, get_fmt, NULL, fmt); } - WARN(pad->entity->type != MEDIA_ENT_T_DEVNODE_V4L, + WARN(pad->entity->function != MEDIA_ENT_F_IO_V4L, "Driver bug! Wrong media entity type 0x%08x, entity %s\n", - pad->entity->type, pad->entity->name); + pad->entity->function, pad->entity->name); return -EINVAL; } @@ -584,7 +584,7 @@ void v4l2_subdev_init(struct v4l2_subdev *sd, const struct v4l2_subdev_ops *ops) sd->host_priv = NULL; #if defined(CONFIG_MEDIA_CONTROLLER) sd->entity.name = sd->name; - sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV; + sd->entity.function = MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN; #endif } EXPORT_SYMBOL(v4l2_subdev_init); diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipe.c b/drivers/staging/media/davinci_vpfe/dm365_ipipe.c index c492914..ac78ed2 100644 --- a/drivers/staging/media/davinci_vpfe/dm365_ipipe.c +++ b/drivers/staging/media/davinci_vpfe/dm365_ipipe.c @@ -1712,8 +1712,11 @@ ipipe_link_setup(struct media_entity *entity, const struct media_pad *local, struct vpfe_device *vpfe_dev = to_vpfe_device(ipipe); u16 ipipeif_sink = vpfe_dev->vpfe_ipipeif.input; - switch (local->index | media_entity_type(remote->entity)) { - case IPIPE_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + if (!is_media_entity_v4l2_subdev(remote->entity)) + return -EINVAL; + + switch (local->index) { + case IPIPE_PAD_SINK: if (!(flags & MEDIA_LNK_FL_ENABLED)) { ipipe->input = IPIPE_INPUT_NONE; break; @@ -1726,7 +1729,7 @@ ipipe_link_setup(struct media_entity *entity, const struct media_pad *local, ipipe->input = IPIPE_INPUT_CCDC; break; - case IPIPE_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV: + case IPIPE_PAD_SOURCE: /* out to RESIZER */ if (flags & MEDIA_LNK_FL_ENABLED) ipipe->output = IPIPE_OUTPUT_RESIZER; @@ -1840,7 +1843,7 @@ vpfe_ipipe_init(struct vpfe_ipipe_device *ipipe, struct platform_device *pdev) v4l2_ctrl_handler_setup(&ipipe->ctrls); sd->ctrl_handler = &ipipe->ctrls; - return media_entity_init(me, IPIPE_PADS_NUM, pads, 0); + return media_entity_pads_init(me, IPIPE_PADS_NUM, pads); } /* diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c b/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c index 8b23054..633d645 100644 --- a/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c +++ b/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c @@ -885,9 +885,14 @@ ipipeif_link_setup(struct media_entity *entity, const struct media_pad *local, struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); struct vpfe_device *vpfe = to_vpfe_device(ipipeif); + unsigned int index = local->index; - switch (local->index | media_entity_type(remote->entity)) { - case IPIPEIF_PAD_SINK | MEDIA_ENT_T_DEVNODE: + /* FIXME: this is actually a hack! */ + if (is_media_entity_v4l2_subdev(remote->entity)) + index |= 2 << 16; + + switch (index) { + case IPIPEIF_PAD_SINK: /* Single shot mode */ if (!(flags & MEDIA_LNK_FL_ENABLED)) { ipipeif->input = IPIPEIF_INPUT_NONE; @@ -896,7 +901,7 @@ ipipeif_link_setup(struct media_entity *entity, const struct media_pad *local, ipipeif->input = IPIPEIF_INPUT_MEMORY; break; - case IPIPEIF_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + case IPIPEIF_PAD_SINK | 2 << 16: /* read from isif */ if (!(flags & MEDIA_LNK_FL_ENABLED)) { ipipeif->input = IPIPEIF_INPUT_NONE; @@ -908,7 +913,7 @@ ipipeif_link_setup(struct media_entity *entity, const struct media_pad *local, ipipeif->input = IPIPEIF_INPUT_ISIF; break; - case IPIPEIF_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV: + case IPIPEIF_PAD_SOURCE | 2 << 16: if (!(flags & MEDIA_LNK_FL_ENABLED)) { ipipeif->output = IPIPEIF_OUTPUT_NONE; break; @@ -971,7 +976,7 @@ vpfe_ipipeif_register_entities(struct vpfe_ipipeif_device *ipipeif, ipipeif->video_in.vpfe_dev = vpfe_dev; flags = 0; - ret = media_entity_create_link(&ipipeif->video_in.video_dev.entity, 0, + ret = media_create_pad_link(&ipipeif->video_in.video_dev.entity, 0, &ipipeif->subdev.entity, 0, flags); if (ret < 0) goto fail; @@ -1026,7 +1031,7 @@ int vpfe_ipipeif_init(struct vpfe_ipipeif_device *ipipeif, ipipeif->output = IPIPEIF_OUTPUT_NONE; me->ops = &ipipeif_media_ops; - ret = media_entity_init(me, IPIPEIF_NUM_PADS, pads, 0); + ret = media_entity_pads_init(me, IPIPEIF_NUM_PADS, pads); if (ret) goto fail; diff --git a/drivers/staging/media/davinci_vpfe/dm365_isif.c b/drivers/staging/media/davinci_vpfe/dm365_isif.c index 80907b4..9905789 100644 --- a/drivers/staging/media/davinci_vpfe/dm365_isif.c +++ b/drivers/staging/media/davinci_vpfe/dm365_isif.c @@ -1707,9 +1707,14 @@ isif_link_setup(struct media_entity *entity, const struct media_pad *local, { struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd); + unsigned int index = local->index; - switch (local->index | media_entity_type(remote->entity)) { - case ISIF_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + /* FIXME: this is actually a hack! */ + if (is_media_entity_v4l2_subdev(remote->entity)) + index |= 2 << 16; + + switch (index) { + case ISIF_PAD_SINK | 2 << 16: /* read from decoder/sensor */ if (!(flags & MEDIA_LNK_FL_ENABLED)) { isif->input = ISIF_INPUT_NONE; @@ -1720,7 +1725,7 @@ isif_link_setup(struct media_entity *entity, const struct media_pad *local, isif->input = ISIF_INPUT_PARALLEL; break; - case ISIF_PAD_SOURCE | MEDIA_ENT_T_DEVNODE: + case ISIF_PAD_SOURCE: /* write to memory */ if (flags & MEDIA_LNK_FL_ENABLED) isif->output = ISIF_OUTPUT_MEMORY; @@ -1728,7 +1733,7 @@ isif_link_setup(struct media_entity *entity, const struct media_pad *local, isif->output = ISIF_OUTPUT_NONE; break; - case ISIF_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV: + case ISIF_PAD_SOURCE | 2 << 16: if (flags & MEDIA_LNK_FL_ENABLED) isif->output = ISIF_OUTPUT_IPIPEIF; else @@ -1817,7 +1822,7 @@ int vpfe_isif_register_entities(struct vpfe_isif_device *isif, isif->video_out.vpfe_dev = vpfe_dev; flags = 0; /* connect isif to video node */ - ret = media_entity_create_link(&isif->subdev.entity, 1, + ret = media_create_pad_link(&isif->subdev.entity, 1, &isif->video_out.video_dev.entity, 0, flags); if (ret < 0) @@ -2052,7 +2057,7 @@ int vpfe_isif_init(struct vpfe_isif_device *isif, struct platform_device *pdev) isif->input = ISIF_INPUT_NONE; isif->output = ISIF_OUTPUT_NONE; me->ops = &isif_media_ops; - status = media_entity_init(me, ISIF_PADS_NUM, pads, 0); + status = media_entity_pads_init(me, ISIF_PADS_NUM, pads); if (status) goto isif_fail; isif->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; diff --git a/drivers/staging/media/davinci_vpfe/dm365_resizer.c b/drivers/staging/media/davinci_vpfe/dm365_resizer.c index d892fee..a91395c 100644 --- a/drivers/staging/media/davinci_vpfe/dm365_resizer.c +++ b/drivers/staging/media/davinci_vpfe/dm365_resizer.c @@ -1648,10 +1648,15 @@ static int resizer_link_setup(struct media_entity *entity, struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); u16 ipipeif_source = vpfe_dev->vpfe_ipipeif.output; u16 ipipe_source = vpfe_dev->vpfe_ipipe.output; + unsigned int index = local->index; + + /* FIXME: this is actually a hack! */ + if (is_media_entity_v4l2_subdev(remote->entity)) + index |= 2 << 16; if (&resizer->crop_resizer.subdev == sd) { - switch (local->index | media_entity_type(remote->entity)) { - case RESIZER_CROP_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + switch (index) { + case RESIZER_CROP_PAD_SINK | 2 << 16: if (!(flags & MEDIA_LNK_FL_ENABLED)) { resizer->crop_resizer.input = RESIZER_CROP_INPUT_NONE; @@ -1671,7 +1676,7 @@ static int resizer_link_setup(struct media_entity *entity, return -EINVAL; break; - case RESIZER_CROP_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV: + case RESIZER_CROP_PAD_SOURCE | 2 << 16: if (!(flags & MEDIA_LNK_FL_ENABLED)) { resizer->crop_resizer.output = RESIZER_CROP_OUTPUT_NONE; @@ -1683,7 +1688,7 @@ static int resizer_link_setup(struct media_entity *entity, resizer->crop_resizer.output = RESIZER_A; break; - case RESIZER_CROP_PAD_SOURCE2 | MEDIA_ENT_T_V4L2_SUBDEV: + case RESIZER_CROP_PAD_SOURCE2 | 2 << 16: if (!(flags & MEDIA_LNK_FL_ENABLED)) { resizer->crop_resizer.output2 = RESIZER_CROP_OUTPUT_NONE; @@ -1699,8 +1704,8 @@ static int resizer_link_setup(struct media_entity *entity, return -EINVAL; } } else if (&resizer->resizer_a.subdev == sd) { - switch (local->index | media_entity_type(remote->entity)) { - case RESIZER_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + switch (index) { + case RESIZER_PAD_SINK | 2 << 16: if (!(flags & MEDIA_LNK_FL_ENABLED)) { resizer->resizer_a.input = RESIZER_INPUT_NONE; break; @@ -1710,7 +1715,7 @@ static int resizer_link_setup(struct media_entity *entity, resizer->resizer_a.input = RESIZER_INPUT_CROP_RESIZER; break; - case RESIZER_PAD_SOURCE | MEDIA_ENT_T_DEVNODE: + case RESIZER_PAD_SOURCE: if (!(flags & MEDIA_LNK_FL_ENABLED)) { resizer->resizer_a.output = RESIZER_OUTPUT_NONE; break; @@ -1724,8 +1729,8 @@ static int resizer_link_setup(struct media_entity *entity, return -EINVAL; } } else if (&resizer->resizer_b.subdev == sd) { - switch (local->index | media_entity_type(remote->entity)) { - case RESIZER_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + switch (index) { + case RESIZER_PAD_SINK | 2 << 16: if (!(flags & MEDIA_LNK_FL_ENABLED)) { resizer->resizer_b.input = RESIZER_INPUT_NONE; break; @@ -1735,7 +1740,7 @@ static int resizer_link_setup(struct media_entity *entity, resizer->resizer_b.input = RESIZER_INPUT_CROP_RESIZER; break; - case RESIZER_PAD_SOURCE | MEDIA_ENT_T_DEVNODE: + case RESIZER_PAD_SOURCE: if (!(flags & MEDIA_LNK_FL_ENABLED)) { resizer->resizer_b.output = RESIZER_OUTPUT_NONE; break; @@ -1826,27 +1831,27 @@ int vpfe_resizer_register_entities(struct vpfe_resizer_device *resizer, resizer->resizer_b.video_out.vpfe_dev = vpfe_dev; /* create link between Resizer Crop----> Resizer A*/ - ret = media_entity_create_link(&resizer->crop_resizer.subdev.entity, 1, + ret = media_create_pad_link(&resizer->crop_resizer.subdev.entity, 1, &resizer->resizer_a.subdev.entity, 0, flags); if (ret < 0) goto out_create_link; /* create link between Resizer Crop----> Resizer B*/ - ret = media_entity_create_link(&resizer->crop_resizer.subdev.entity, 2, + ret = media_create_pad_link(&resizer->crop_resizer.subdev.entity, 2, &resizer->resizer_b.subdev.entity, 0, flags); if (ret < 0) goto out_create_link; /* create link between Resizer A ----> video out */ - ret = media_entity_create_link(&resizer->resizer_a.subdev.entity, 1, + ret = media_create_pad_link(&resizer->resizer_a.subdev.entity, 1, &resizer->resizer_a.video_out.video_dev.entity, 0, flags); if (ret < 0) goto out_create_link; /* create link between Resizer B ----> video out */ - ret = media_entity_create_link(&resizer->resizer_b.subdev.entity, 1, + ret = media_create_pad_link(&resizer->resizer_b.subdev.entity, 1, &resizer->resizer_b.video_out.video_dev.entity, 0, flags); if (ret < 0) goto out_create_link; @@ -1910,7 +1915,7 @@ int vpfe_resizer_init(struct vpfe_resizer_device *vpfe_rsz, vpfe_rsz->crop_resizer.output2 = RESIZER_CROP_OUTPUT_NONE; vpfe_rsz->crop_resizer.rsz_device = vpfe_rsz; me->ops = &resizer_media_ops; - ret = media_entity_init(me, RESIZER_CROP_PADS_NUM, pads, 0); + ret = media_entity_pads_init(me, RESIZER_CROP_PADS_NUM, pads); if (ret) return ret; @@ -1932,7 +1937,7 @@ int vpfe_resizer_init(struct vpfe_resizer_device *vpfe_rsz, vpfe_rsz->resizer_a.output = RESIZER_OUTPUT_NONE; vpfe_rsz->resizer_a.rsz_device = vpfe_rsz; me->ops = &resizer_media_ops; - ret = media_entity_init(me, RESIZER_PADS_NUM, pads, 0); + ret = media_entity_pads_init(me, RESIZER_PADS_NUM, pads); if (ret) return ret; @@ -1954,7 +1959,7 @@ int vpfe_resizer_init(struct vpfe_resizer_device *vpfe_rsz, vpfe_rsz->resizer_b.output = RESIZER_OUTPUT_NONE; vpfe_rsz->resizer_b.rsz_device = vpfe_rsz; me->ops = &resizer_media_ops; - ret = media_entity_init(me, RESIZER_PADS_NUM, pads, 0); + ret = media_entity_pads_init(me, RESIZER_PADS_NUM, pads); if (ret) return ret; diff --git a/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c b/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c index 69b678c..ec46f36 100644 --- a/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c +++ b/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c @@ -445,32 +445,32 @@ static int vpfe_register_entities(struct vpfe_device *vpfe_dev) /* if entity has no pads (ex: amplifier), cant establish link */ if (vpfe_dev->sd[i]->entity.num_pads) { - ret = media_entity_create_link(&vpfe_dev->sd[i]->entity, + ret = media_create_pad_link(&vpfe_dev->sd[i]->entity, 0, &vpfe_dev->vpfe_isif.subdev.entity, 0, flags); if (ret < 0) goto out_resizer_register; } - ret = media_entity_create_link(&vpfe_dev->vpfe_isif.subdev.entity, 1, + ret = media_create_pad_link(&vpfe_dev->vpfe_isif.subdev.entity, 1, &vpfe_dev->vpfe_ipipeif.subdev.entity, 0, flags); if (ret < 0) goto out_resizer_register; - ret = media_entity_create_link(&vpfe_dev->vpfe_ipipeif.subdev.entity, 1, + ret = media_create_pad_link(&vpfe_dev->vpfe_ipipeif.subdev.entity, 1, &vpfe_dev->vpfe_ipipe.subdev.entity, 0, flags); if (ret < 0) goto out_resizer_register; - ret = media_entity_create_link(&vpfe_dev->vpfe_ipipe.subdev.entity, + ret = media_create_pad_link(&vpfe_dev->vpfe_ipipe.subdev.entity, 1, &vpfe_dev->vpfe_resizer.crop_resizer.subdev.entity, 0, flags); if (ret < 0) goto out_resizer_register; - ret = media_entity_create_link(&vpfe_dev->vpfe_ipipeif.subdev.entity, 1, + ret = media_create_pad_link(&vpfe_dev->vpfe_ipipeif.subdev.entity, 1, &vpfe_dev->vpfe_resizer.crop_resizer.subdev.entity, 0, flags); if (ret < 0) diff --git a/drivers/staging/media/davinci_vpfe/vpfe_video.c b/drivers/staging/media/davinci_vpfe/vpfe_video.c index adb2bc8..3ec7e65 100644 --- a/drivers/staging/media/davinci_vpfe/vpfe_video.c +++ b/drivers/staging/media/davinci_vpfe/vpfe_video.c @@ -88,7 +88,7 @@ vpfe_video_remote_subdev(struct vpfe_video_device *video, u32 *pad) { struct media_pad *remote = media_entity_remote_pad(&video->pad); - if (remote == NULL || remote->entity->type != MEDIA_ENT_T_V4L2_SUBDEV) + if (!remote || !is_media_entity_v4l2_subdev(remote->entity)) return NULL; if (pad) *pad = remote->index; @@ -127,13 +127,14 @@ __vpfe_video_get_format(struct vpfe_video_device *video, } /* make a note of pipeline details */ -static void vpfe_prepare_pipeline(struct vpfe_video_device *video) +static int vpfe_prepare_pipeline(struct vpfe_video_device *video) { + struct media_entity_graph graph; struct media_entity *entity = &video->video_dev.entity; - struct media_device *mdev = entity->parent; + struct media_device *mdev = entity->graph_obj.mdev; struct vpfe_pipeline *pipe = &video->pipe; struct vpfe_video_device *far_end = NULL; - struct media_entity_graph graph; + int ret; pipe->input_num = 0; pipe->output_num = 0; @@ -144,11 +145,16 @@ static void vpfe_prepare_pipeline(struct vpfe_video_device *video) pipe->outputs[pipe->output_num++] = video; mutex_lock(&mdev->graph_mutex); + ret = media_entity_graph_walk_init(&graph, entity->graph_obj.mdev); + if (ret) { + mutex_unlock(&video->lock); + return -ENOMEM; + } media_entity_graph_walk_start(&graph, entity); while ((entity = media_entity_graph_walk_next(&graph))) { if (entity == &video->video_dev.entity) continue; - if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE) + if (!is_media_entity_v4l2_io(entity)) continue; far_end = to_vpfe_video(media_entity_to_video_device(entity)); if (far_end->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) @@ -156,7 +162,10 @@ static void vpfe_prepare_pipeline(struct vpfe_video_device *video) else pipe->outputs[pipe->output_num++] = far_end; } + media_entity_graph_walk_cleanup(&graph); mutex_unlock(&mdev->graph_mutex); + + return 0; } /* update pipe state selected by user */ @@ -165,7 +174,9 @@ static int vpfe_update_pipe_state(struct vpfe_video_device *video) struct vpfe_pipeline *pipe = &video->pipe; int ret; - vpfe_prepare_pipeline(video); + ret = vpfe_prepare_pipeline(video); + if (ret) + return ret; /* Find out if there is any input video if yes, it is single shot. @@ -243,8 +254,7 @@ static int vpfe_video_validate_pipeline(struct vpfe_pipeline *pipe) /* Retrieve the source format */ pad = media_entity_remote_pad(pad); - if (pad == NULL || - pad->entity->type != MEDIA_ENT_T_V4L2_SUBDEV) + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) break; subdev = media_entity_to_v4l2_subdev(pad->entity); @@ -277,29 +287,35 @@ static int vpfe_video_validate_pipeline(struct vpfe_pipeline *pipe) */ static int vpfe_pipeline_enable(struct vpfe_pipeline *pipe) { - struct media_entity_graph graph; struct media_entity *entity; struct v4l2_subdev *subdev; struct media_device *mdev; - int ret = 0; + int ret; if (pipe->state == VPFE_PIPELINE_STREAM_CONTINUOUS) entity = vpfe_get_input_entity(pipe->outputs[0]); else entity = &pipe->inputs[0]->video_dev.entity; - mdev = entity->parent; + mdev = entity->graph_obj.mdev; mutex_lock(&mdev->graph_mutex); - media_entity_graph_walk_start(&graph, entity); - while ((entity = media_entity_graph_walk_next(&graph))) { + ret = media_entity_graph_walk_init(&pipe->graph, + entity->graph_obj.mdev); + if (ret) + goto out; + media_entity_graph_walk_start(&pipe->graph, entity); + while ((entity = media_entity_graph_walk_next(&pipe->graph))) { - if (media_entity_type(entity) == MEDIA_ENT_T_DEVNODE) + if (!is_media_entity_v4l2_subdev(entity)) continue; subdev = media_entity_to_v4l2_subdev(entity); ret = v4l2_subdev_call(subdev, video, s_stream, 1); if (ret < 0 && ret != -ENOIOCTLCMD) break; } +out: + if (ret) + media_entity_graph_walk_cleanup(&pipe->graph); mutex_unlock(&mdev->graph_mutex); return ret; } @@ -317,7 +333,6 @@ static int vpfe_pipeline_enable(struct vpfe_pipeline *pipe) */ static int vpfe_pipeline_disable(struct vpfe_pipeline *pipe) { - struct media_entity_graph graph; struct media_entity *entity; struct v4l2_subdev *subdev; struct media_device *mdev; @@ -328,13 +343,13 @@ static int vpfe_pipeline_disable(struct vpfe_pipeline *pipe) else entity = &pipe->inputs[0]->video_dev.entity; - mdev = entity->parent; + mdev = entity->graph_obj.mdev; mutex_lock(&mdev->graph_mutex); - media_entity_graph_walk_start(&graph, entity); + media_entity_graph_walk_start(&pipe->graph, entity); - while ((entity = media_entity_graph_walk_next(&graph))) { + while ((entity = media_entity_graph_walk_next(&pipe->graph))) { - if (media_entity_type(entity) == MEDIA_ENT_T_DEVNODE) + if (!is_media_entity_v4l2_subdev(entity)) continue; subdev = media_entity_to_v4l2_subdev(entity); ret = v4l2_subdev_call(subdev, video, s_stream, 0); @@ -343,6 +358,7 @@ static int vpfe_pipeline_disable(struct vpfe_pipeline *pipe) } mutex_unlock(&mdev->graph_mutex); + media_entity_graph_walk_cleanup(&pipe->graph); return ret ? -ETIMEDOUT : 0; } @@ -1600,8 +1616,8 @@ int vpfe_video_init(struct vpfe_video_device *video, const char *name) spin_lock_init(&video->irqlock); spin_lock_init(&video->dma_queue_lock); mutex_init(&video->lock); - ret = media_entity_init(&video->video_dev.entity, - 1, &video->pad, 0); + ret = media_entity_pads_init(&video->video_dev.entity, + 1, &video->pad); if (ret < 0) return ret; diff --git a/drivers/staging/media/davinci_vpfe/vpfe_video.h b/drivers/staging/media/davinci_vpfe/vpfe_video.h index 673cefe..653334d 100644 --- a/drivers/staging/media/davinci_vpfe/vpfe_video.h +++ b/drivers/staging/media/davinci_vpfe/vpfe_video.h @@ -52,6 +52,7 @@ enum vpfe_video_state { struct vpfe_pipeline { /* media pipeline */ struct media_pipeline *pipe; + struct media_entity_graph graph; /* state of the pipeline, continuous, * single-shot or stopped */ diff --git a/drivers/staging/media/omap4iss/iss.c b/drivers/staging/media/omap4iss/iss.c index e27a988..30b473c 100644 --- a/drivers/staging/media/omap4iss/iss.c +++ b/drivers/staging/media/omap4iss/iss.c @@ -389,15 +389,15 @@ static irqreturn_t iss_isr(int irq, void *_iss) * * Return the total number of users of all video device nodes in the pipeline. */ -static int iss_pipeline_pm_use_count(struct media_entity *entity) +static int iss_pipeline_pm_use_count(struct media_entity *entity, + struct media_entity_graph *graph) { - struct media_entity_graph graph; int use = 0; - media_entity_graph_walk_start(&graph, entity); + media_entity_graph_walk_start(graph, entity); - while ((entity = media_entity_graph_walk_next(&graph))) { - if (media_entity_type(entity) == MEDIA_ENT_T_DEVNODE) + while ((entity = media_entity_graph_walk_next(graph))) { + if (is_media_entity_v4l2_io(entity)) use += entity->use_count; } @@ -419,7 +419,7 @@ static int iss_pipeline_pm_power_one(struct media_entity *entity, int change) { struct v4l2_subdev *subdev; - subdev = media_entity_type(entity) == MEDIA_ENT_T_V4L2_SUBDEV + subdev = is_media_entity_v4l2_subdev(entity) ? media_entity_to_v4l2_subdev(entity) : NULL; if (entity->use_count == 0 && change > 0 && subdev) { @@ -449,29 +449,29 @@ static int iss_pipeline_pm_power_one(struct media_entity *entity, int change) * * Return 0 on success or a negative error code on failure. */ -static int iss_pipeline_pm_power(struct media_entity *entity, int change) +static int iss_pipeline_pm_power(struct media_entity *entity, int change, + struct media_entity_graph *graph) { - struct media_entity_graph graph; struct media_entity *first = entity; int ret = 0; if (!change) return 0; - media_entity_graph_walk_start(&graph, entity); + media_entity_graph_walk_start(graph, entity); - while (!ret && (entity = media_entity_graph_walk_next(&graph))) - if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE) + while (!ret && (entity = media_entity_graph_walk_next(graph))) + if (is_media_entity_v4l2_subdev(entity)) ret = iss_pipeline_pm_power_one(entity, change); if (!ret) return 0; - media_entity_graph_walk_start(&graph, first); + media_entity_graph_walk_start(graph, first); - while ((first = media_entity_graph_walk_next(&graph)) && + while ((first = media_entity_graph_walk_next(graph)) && first != entity) - if (media_entity_type(first) != MEDIA_ENT_T_DEVNODE) + if (is_media_entity_v4l2_subdev(first)) iss_pipeline_pm_power_one(first, -change); return ret; @@ -489,23 +489,24 @@ static int iss_pipeline_pm_power(struct media_entity *entity, int change) * off is assumed to never fail. No failure can occur when the use parameter is * set to 0. */ -int omap4iss_pipeline_pm_use(struct media_entity *entity, int use) +int omap4iss_pipeline_pm_use(struct media_entity *entity, int use, + struct media_entity_graph *graph) { int change = use ? 1 : -1; int ret; - mutex_lock(&entity->parent->graph_mutex); + mutex_lock(&entity->graph_obj.mdev->graph_mutex); /* Apply use count to node. */ entity->use_count += change; WARN_ON(entity->use_count < 0); /* Apply power change to connected non-nodes. */ - ret = iss_pipeline_pm_power(entity, change); + ret = iss_pipeline_pm_power(entity, change, graph); if (ret < 0) entity->use_count -= change; - mutex_unlock(&entity->parent->graph_mutex); + mutex_unlock(&entity->graph_obj.mdev->graph_mutex); return ret; } @@ -526,34 +527,48 @@ int omap4iss_pipeline_pm_use(struct media_entity *entity, int use) static int iss_pipeline_link_notify(struct media_link *link, u32 flags, unsigned int notification) { + struct media_entity_graph *graph = + &container_of(link->graph_obj.mdev, struct iss_device, + media_dev)->pm_count_graph; struct media_entity *source = link->source->entity; struct media_entity *sink = link->sink->entity; - int source_use = iss_pipeline_pm_use_count(source); - int sink_use = iss_pipeline_pm_use_count(sink); + int source_use; + int sink_use; int ret; + if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH) { + ret = media_entity_graph_walk_init(graph, + link->graph_obj.mdev); + if (ret) + return ret; + } + + source_use = iss_pipeline_pm_use_count(source, graph); + sink_use = iss_pipeline_pm_use_count(sink, graph); + if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH && - !(link->flags & MEDIA_LNK_FL_ENABLED)) { + !(flags & MEDIA_LNK_FL_ENABLED)) { /* Powering off entities is assumed to never fail. */ - iss_pipeline_pm_power(source, -sink_use); - iss_pipeline_pm_power(sink, -source_use); + iss_pipeline_pm_power(source, -sink_use, graph); + iss_pipeline_pm_power(sink, -source_use, graph); return 0; } - if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH && + if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH && (flags & MEDIA_LNK_FL_ENABLED)) { - ret = iss_pipeline_pm_power(source, sink_use); + ret = iss_pipeline_pm_power(source, sink_use, graph); if (ret < 0) return ret; - ret = iss_pipeline_pm_power(sink, source_use); + ret = iss_pipeline_pm_power(sink, source_use, graph); if (ret < 0) - iss_pipeline_pm_power(source, -sink_use); - - return ret; + iss_pipeline_pm_power(source, -sink_use, graph); } - return 0; + if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH) + media_entity_graph_walk_cleanup(graph); + + return ret; } /* ----------------------------------------------------------------------------- @@ -590,8 +605,7 @@ static int iss_pipeline_disable(struct iss_pipeline *pipe, break; pad = media_entity_remote_pad(pad); - if (!pad || - media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) break; entity = pad->entity; @@ -607,7 +621,7 @@ static int iss_pipeline_disable(struct iss_pipeline *pipe, * crashed. Mark it as such, the ISS will be reset when * applications will release it. */ - iss->crashed |= 1U << subdev->entity.id; + media_entity_enum_set(&iss->crashed, &subdev->entity); failure = -ETIMEDOUT; } } @@ -642,7 +656,7 @@ static int iss_pipeline_enable(struct iss_pipeline *pipe, * pipeline won't start anyway (those entities would then likely fail to * stop, making the problem worse). */ - if (pipe->entities & iss->crashed) + if (media_entity_enum_intersects(&pipe->ent_enum, &iss->crashed)) return -EIO; spin_lock_irqsave(&pipe->lock, flags); @@ -658,8 +672,7 @@ static int iss_pipeline_enable(struct iss_pipeline *pipe, break; pad = media_entity_remote_pad(pad); - if (!pad || - media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) break; entity = pad->entity; @@ -763,7 +776,8 @@ static int iss_reset(struct iss_device *iss) return -ETIMEDOUT; } - iss->crashed = 0; + media_entity_enum_zero(&iss->crashed); + return 0; } @@ -1092,7 +1106,7 @@ void omap4iss_put(struct iss_device *iss) * be worth investigating whether resetting the ISP only can't * fix the problem in some cases. */ - if (iss->crashed) + if (!media_entity_enum_empty(&iss->crashed)) iss_reset(iss); iss_disable_clocks(iss); } @@ -1259,7 +1273,7 @@ static int iss_register_entities(struct iss_device *iss) goto done; } - ret = media_entity_create_link(&sensor->entity, 0, input, pad, + ret = media_create_pad_link(&sensor->entity, 0, input, pad, flags); if (ret < 0) goto done; @@ -1274,6 +1288,68 @@ done: return ret; } +/* + * iss_create_links() - Pads links creation for the subdevices + * @iss : Pointer to ISS device + * + * return negative error code or zero on success + */ +static int iss_create_links(struct iss_device *iss) +{ + int ret; + + ret = omap4iss_csi2_create_links(iss); + if (ret < 0) { + dev_err(iss->dev, "CSI2 pads links creation failed\n"); + return ret; + } + + ret = omap4iss_ipipeif_create_links(iss); + if (ret < 0) { + dev_err(iss->dev, "ISP IPIPEIF pads links creation failed\n"); + return ret; + } + + ret = omap4iss_resizer_create_links(iss); + if (ret < 0) { + dev_err(iss->dev, "ISP RESIZER pads links creation failed\n"); + return ret; + } + + /* Connect the submodules. */ + ret = media_create_pad_link( + &iss->csi2a.subdev.entity, CSI2_PAD_SOURCE, + &iss->ipipeif.subdev.entity, IPIPEIF_PAD_SINK, 0); + if (ret < 0) + return ret; + + ret = media_create_pad_link( + &iss->csi2b.subdev.entity, CSI2_PAD_SOURCE, + &iss->ipipeif.subdev.entity, IPIPEIF_PAD_SINK, 0); + if (ret < 0) + return ret; + + ret = media_create_pad_link( + &iss->ipipeif.subdev.entity, IPIPEIF_PAD_SOURCE_VP, + &iss->resizer.subdev.entity, RESIZER_PAD_SINK, 0); + if (ret < 0) + return ret; + + ret = media_create_pad_link( + &iss->ipipeif.subdev.entity, IPIPEIF_PAD_SOURCE_VP, + &iss->ipipe.subdev.entity, IPIPE_PAD_SINK, 0); + if (ret < 0) + return ret; + + ret = media_create_pad_link( + &iss->ipipe.subdev.entity, IPIPE_PAD_SOURCE_VP, + &iss->resizer.subdev.entity, RESIZER_PAD_SINK, 0); + if (ret < 0) + return ret; + + return 0; +}; + static void iss_cleanup_modules(struct iss_device *iss) { omap4iss_csi2_cleanup(iss); @@ -1316,41 +1392,8 @@ static int iss_initialize_modules(struct iss_device *iss) goto error_resizer; } - /* Connect the submodules. */ - ret = media_entity_create_link( - &iss->csi2a.subdev.entity, CSI2_PAD_SOURCE, - &iss->ipipeif.subdev.entity, IPIPEIF_PAD_SINK, 0); - if (ret < 0) - goto error_link; - - ret = media_entity_create_link( - &iss->csi2b.subdev.entity, CSI2_PAD_SOURCE, - &iss->ipipeif.subdev.entity, IPIPEIF_PAD_SINK, 0); - if (ret < 0) - goto error_link; - - ret = media_entity_create_link( - &iss->ipipeif.subdev.entity, IPIPEIF_PAD_SOURCE_VP, - &iss->resizer.subdev.entity, RESIZER_PAD_SINK, 0); - if (ret < 0) - goto error_link; - - ret = media_entity_create_link( - &iss->ipipeif.subdev.entity, IPIPEIF_PAD_SOURCE_VP, - &iss->ipipe.subdev.entity, IPIPE_PAD_SINK, 0); - if (ret < 0) - goto error_link; - - ret = media_entity_create_link( - &iss->ipipe.subdev.entity, IPIPE_PAD_SOURCE_VP, - &iss->resizer.subdev.entity, RESIZER_PAD_SINK, 0); - if (ret < 0) - goto error_link; - return 0; -error_link: - omap4iss_resizer_cleanup(iss); error_resizer: omap4iss_ipipe_cleanup(iss); error_ipipe: @@ -1464,10 +1507,21 @@ static int iss_probe(struct platform_device *pdev) if (ret < 0) goto error_modules; + ret = media_entity_enum_init(&iss->crashed, &iss->media_dev); + if (ret) + goto error_entities; + + ret = iss_create_links(iss); + if (ret < 0) + goto error_entities; + omap4iss_put(iss); return 0; +error_entities: + iss_unregister_entities(iss); + media_entity_enum_cleanup(&iss->crashed); error_modules: iss_cleanup_modules(iss); error_iss: @@ -1485,6 +1539,7 @@ static int iss_remove(struct platform_device *pdev) struct iss_device *iss = platform_get_drvdata(pdev); iss_unregister_entities(iss); + media_entity_enum_cleanup(&iss->crashed); iss_cleanup_modules(iss); return 0; diff --git a/drivers/staging/media/omap4iss/iss.h b/drivers/staging/media/omap4iss/iss.h index 5929357..05f08a3 100644 --- a/drivers/staging/media/omap4iss/iss.h +++ b/drivers/staging/media/omap4iss/iss.h @@ -82,11 +82,12 @@ struct iss_reg { /* * struct iss_device - ISS device structure. * @syscon: Regmap for the syscon register space - * @crashed: Bitmask of crashed entities (indexed by entity ID) + * @crashed: Crashed entities */ struct iss_device { struct v4l2_device v4l2_dev; struct media_device media_dev; + struct media_entity_graph pm_count_graph; struct device *dev; u32 revision; @@ -101,7 +102,7 @@ struct iss_device { u64 raw_dmamask; struct mutex iss_mutex; /* For handling ref_count field */ - unsigned int crashed; + struct media_entity_enum crashed; int has_context; int ref_count; @@ -151,7 +152,8 @@ void omap4iss_isp_subclk_enable(struct iss_device *iss, void omap4iss_isp_subclk_disable(struct iss_device *iss, enum iss_isp_subclk_resource res); -int omap4iss_pipeline_pm_use(struct media_entity *entity, int use); +int omap4iss_pipeline_pm_use(struct media_entity *entity, int use, + struct media_entity_graph *graph); int omap4iss_register_entities(struct platform_device *pdev, struct v4l2_device *v4l2_dev); diff --git a/drivers/staging/media/omap4iss/iss_csi2.c b/drivers/staging/media/omap4iss/iss_csi2.c index b941035..aaca39d 100644 --- a/drivers/staging/media/omap4iss/iss_csi2.c +++ b/drivers/staging/media/omap4iss/iss_csi2.c @@ -1170,14 +1170,19 @@ static int csi2_link_setup(struct media_entity *entity, struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd); struct iss_csi2_ctrl_cfg *ctrl = &csi2->ctrl; + unsigned int index = local->index; + + /* FIXME: this is actually a hack! */ + if (is_media_entity_v4l2_subdev(remote->entity)) + index |= 2 << 16; /* * The ISS core doesn't support pipelines with multiple video outputs. * Revisit this when it will be implemented, and return -EBUSY for now. */ - switch (local->index | media_entity_type(remote->entity)) { - case CSI2_PAD_SOURCE | MEDIA_ENT_T_DEVNODE: + switch (index) { + case CSI2_PAD_SOURCE: if (flags & MEDIA_LNK_FL_ENABLED) { if (csi2->output & ~CSI2_OUTPUT_MEMORY) return -EBUSY; @@ -1187,7 +1192,7 @@ static int csi2_link_setup(struct media_entity *entity, } break; - case CSI2_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV: + case CSI2_PAD_SOURCE | 2 << 16: if (flags & MEDIA_LNK_FL_ENABLED) { if (csi2->output & ~CSI2_OUTPUT_IPIPEIF) return -EBUSY; @@ -1271,7 +1276,7 @@ static int csi2_init_entities(struct iss_csi2_device *csi2, const char *subname) pads[CSI2_PAD_SINK].flags = MEDIA_PAD_FL_SINK; me->ops = &csi2_media_ops; - ret = media_entity_init(me, CSI2_PADS_NUM, pads, 0); + ret = media_entity_pads_init(me, CSI2_PADS_NUM, pads); if (ret < 0) return ret; @@ -1290,16 +1295,8 @@ static int csi2_init_entities(struct iss_csi2_device *csi2, const char *subname) if (ret < 0) goto error_video; - /* Connect the CSI2 subdev to the video node. */ - ret = media_entity_create_link(&csi2->subdev.entity, CSI2_PAD_SOURCE, - &csi2->video_out.video.entity, 0, 0); - if (ret < 0) - goto error_link; - return 0; -error_link: - omap4iss_video_cleanup(&csi2->video_out); error_video: media_entity_cleanup(&csi2->subdev.entity); return ret; @@ -1342,6 +1339,33 @@ int omap4iss_csi2_init(struct iss_device *iss) } /* + * omap4iss_csi2_create_links() - CSI2 pads links creation + * @iss: Pointer to ISS device + * + * return negative error code or zero on success + */ +int omap4iss_csi2_create_links(struct iss_device *iss) +{ + struct iss_csi2_device *csi2a = &iss->csi2a; + struct iss_csi2_device *csi2b = &iss->csi2b; + int ret; + + /* Connect the CSI2a subdev to the video node. */ + ret = media_create_pad_link(&csi2a->subdev.entity, CSI2_PAD_SOURCE, + &csi2a->video_out.video.entity, 0, 0); + if (ret < 0) + return ret; + + /* Connect the CSI2b subdev to the video node. */ + ret = media_create_pad_link(&csi2b->subdev.entity, CSI2_PAD_SOURCE, + &csi2b->video_out.video.entity, 0, 0); + if (ret < 0) + return ret; + + return 0; +} + +/* * omap4iss_csi2_cleanup - Routine for module driver cleanup */ void omap4iss_csi2_cleanup(struct iss_device *iss) diff --git a/drivers/staging/media/omap4iss/iss_csi2.h b/drivers/staging/media/omap4iss/iss_csi2.h index f2f5343..24ab378 100644 --- a/drivers/staging/media/omap4iss/iss_csi2.h +++ b/drivers/staging/media/omap4iss/iss_csi2.h @@ -151,6 +151,7 @@ struct iss_csi2_device { void omap4iss_csi2_isr(struct iss_csi2_device *csi2); int omap4iss_csi2_reset(struct iss_csi2_device *csi2); int omap4iss_csi2_init(struct iss_device *iss); +int omap4iss_csi2_create_links(struct iss_device *iss); void omap4iss_csi2_cleanup(struct iss_device *iss); void omap4iss_csi2_unregister_entities(struct iss_csi2_device *csi2); int omap4iss_csi2_register_entities(struct iss_csi2_device *csi2, diff --git a/drivers/staging/media/omap4iss/iss_ipipe.c b/drivers/staging/media/omap4iss/iss_ipipe.c index dd0abef..d38782e 100644 --- a/drivers/staging/media/omap4iss/iss_ipipe.c +++ b/drivers/staging/media/omap4iss/iss_ipipe.c @@ -447,8 +447,11 @@ static int ipipe_link_setup(struct media_entity *entity, struct iss_ipipe_device *ipipe = v4l2_get_subdevdata(sd); struct iss_device *iss = to_iss_device(ipipe); - switch (local->index | media_entity_type(remote->entity)) { - case IPIPE_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + if (!is_media_entity_v4l2_subdev(remote->entity)) + return -EINVAL; + + switch (local->index) { + case IPIPE_PAD_SINK: /* Read from IPIPEIF. */ if (!(flags & MEDIA_LNK_FL_ENABLED)) { ipipe->input = IPIPE_INPUT_NONE; @@ -463,7 +466,7 @@ static int ipipe_link_setup(struct media_entity *entity, break; - case IPIPE_PAD_SOURCE_VP | MEDIA_ENT_T_V4L2_SUBDEV: + case IPIPE_PAD_SOURCE_VP: /* Send to RESIZER */ if (flags & MEDIA_LNK_FL_ENABLED) { if (ipipe->output & ~IPIPE_OUTPUT_VP) @@ -513,7 +516,7 @@ static int ipipe_init_entities(struct iss_ipipe_device *ipipe) pads[IPIPE_PAD_SOURCE_VP].flags = MEDIA_PAD_FL_SOURCE; me->ops = &ipipe_media_ops; - ret = media_entity_init(me, IPIPE_PADS_NUM, pads, 0); + ret = media_entity_pads_init(me, IPIPE_PADS_NUM, pads); if (ret < 0) return ret; diff --git a/drivers/staging/media/omap4iss/iss_ipipeif.c b/drivers/staging/media/omap4iss/iss_ipipeif.c index 5f9e449..23de833 100644 --- a/drivers/staging/media/omap4iss/iss_ipipeif.c +++ b/drivers/staging/media/omap4iss/iss_ipipeif.c @@ -662,9 +662,14 @@ static int ipipeif_link_setup(struct media_entity *entity, struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); struct iss_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); struct iss_device *iss = to_iss_device(ipipeif); + unsigned int index = local->index; - switch (local->index | media_entity_type(remote->entity)) { - case IPIPEIF_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + /* FIXME: this is actually a hack! */ + if (is_media_entity_v4l2_subdev(remote->entity)) + index |= 2 << 16; + + switch (index) { + case IPIPEIF_PAD_SINK | 2 << 16: /* Read from the sensor CSI2a or CSI2b. */ if (!(flags & MEDIA_LNK_FL_ENABLED)) { ipipeif->input = IPIPEIF_INPUT_NONE; @@ -681,7 +686,7 @@ static int ipipeif_link_setup(struct media_entity *entity, break; - case IPIPEIF_PAD_SOURCE_ISIF_SF | MEDIA_ENT_T_DEVNODE: + case IPIPEIF_PAD_SOURCE_ISIF_SF: /* Write to memory */ if (flags & MEDIA_LNK_FL_ENABLED) { if (ipipeif->output & ~IPIPEIF_OUTPUT_MEMORY) @@ -692,7 +697,7 @@ static int ipipeif_link_setup(struct media_entity *entity, } break; - case IPIPEIF_PAD_SOURCE_VP | MEDIA_ENT_T_V4L2_SUBDEV: + case IPIPEIF_PAD_SOURCE_VP | 2 << 16: /* Send to IPIPE/RESIZER */ if (flags & MEDIA_LNK_FL_ENABLED) { if (ipipeif->output & ~IPIPEIF_OUTPUT_VP) @@ -743,7 +748,7 @@ static int ipipeif_init_entities(struct iss_ipipeif_device *ipipeif) pads[IPIPEIF_PAD_SOURCE_VP].flags = MEDIA_PAD_FL_SOURCE; me->ops = &ipipeif_media_ops; - ret = media_entity_init(me, IPIPEIF_PADS_NUM, pads, 0); + ret = media_entity_pads_init(me, IPIPEIF_PADS_NUM, pads); if (ret < 0) return ret; @@ -757,18 +762,7 @@ static int ipipeif_init_entities(struct iss_ipipeif_device *ipipeif) ipipeif->video_out.bpl_zero_padding = 1; ipipeif->video_out.bpl_max = 0x1ffe0; - ret = omap4iss_video_init(&ipipeif->video_out, "ISP IPIPEIF"); - if (ret < 0) - return ret; - - /* Connect the IPIPEIF subdev to the video node. */ - ret = media_entity_create_link(&ipipeif->subdev.entity, - IPIPEIF_PAD_SOURCE_ISIF_SF, - &ipipeif->video_out.video.entity, 0, 0); - if (ret < 0) - return ret; - - return 0; + return omap4iss_video_init(&ipipeif->video_out, "ISP IPIPEIF"); } void omap4iss_ipipeif_unregister_entities(struct iss_ipipeif_device *ipipeif) @@ -821,6 +815,22 @@ int omap4iss_ipipeif_init(struct iss_device *iss) } /* + * omap4iss_ipipeif_create_links() - IPIPEIF pads links creation + * @iss: Pointer to ISS device + * + * return negative error code or zero on success + */ +int omap4iss_ipipeif_create_links(struct iss_device *iss) +{ + struct iss_ipipeif_device *ipipeif = &iss->ipipeif; + + /* Connect the IPIPEIF subdev to the video node. */ + return media_create_pad_link(&ipipeif->subdev.entity, + IPIPEIF_PAD_SOURCE_ISIF_SF, + &ipipeif->video_out.video.entity, 0, 0); +} + +/* * omap4iss_ipipeif_cleanup - IPIPEIF module cleanup. * @iss: Device pointer specific to the OMAP4 ISS. */ diff --git a/drivers/staging/media/omap4iss/iss_ipipeif.h b/drivers/staging/media/omap4iss/iss_ipipeif.h index c6bd96d..bad32b1 100644 --- a/drivers/staging/media/omap4iss/iss_ipipeif.h +++ b/drivers/staging/media/omap4iss/iss_ipipeif.h @@ -78,6 +78,7 @@ struct iss_ipipeif_device { struct iss_device; int omap4iss_ipipeif_init(struct iss_device *iss); +int omap4iss_ipipeif_create_links(struct iss_device *iss); void omap4iss_ipipeif_cleanup(struct iss_device *iss); int omap4iss_ipipeif_register_entities(struct iss_ipipeif_device *ipipeif, struct v4l2_device *vdev); diff --git a/drivers/staging/media/omap4iss/iss_resizer.c b/drivers/staging/media/omap4iss/iss_resizer.c index 108961e..f1d352c 100644 --- a/drivers/staging/media/omap4iss/iss_resizer.c +++ b/drivers/staging/media/omap4iss/iss_resizer.c @@ -716,9 +716,14 @@ static int resizer_link_setup(struct media_entity *entity, struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); struct iss_resizer_device *resizer = v4l2_get_subdevdata(sd); struct iss_device *iss = to_iss_device(resizer); + unsigned int index = local->index; - switch (local->index | media_entity_type(remote->entity)) { - case RESIZER_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + /* FIXME: this is actually a hack! */ + if (is_media_entity_v4l2_subdev(remote->entity)) + index |= 2 << 16; + + switch (index) { + case RESIZER_PAD_SINK | 2 << 16: /* Read from IPIPE or IPIPEIF. */ if (!(flags & MEDIA_LNK_FL_ENABLED)) { resizer->input = RESIZER_INPUT_NONE; @@ -735,7 +740,7 @@ static int resizer_link_setup(struct media_entity *entity, break; - case RESIZER_PAD_SOURCE_MEM | MEDIA_ENT_T_DEVNODE: + case RESIZER_PAD_SOURCE_MEM: /* Write to memory */ if (flags & MEDIA_LNK_FL_ENABLED) { if (resizer->output & ~RESIZER_OUTPUT_MEMORY) @@ -785,7 +790,7 @@ static int resizer_init_entities(struct iss_resizer_device *resizer) pads[RESIZER_PAD_SOURCE_MEM].flags = MEDIA_PAD_FL_SOURCE; me->ops = &resizer_media_ops; - ret = media_entity_init(me, RESIZER_PADS_NUM, pads, 0); + ret = media_entity_pads_init(me, RESIZER_PADS_NUM, pads); if (ret < 0) return ret; @@ -799,18 +804,7 @@ static int resizer_init_entities(struct iss_resizer_device *resizer) resizer->video_out.bpl_zero_padding = 1; resizer->video_out.bpl_max = 0x1ffe0; - ret = omap4iss_video_init(&resizer->video_out, "ISP resizer a"); - if (ret < 0) - return ret; - - /* Connect the RESIZER subdev to the video node. */ - ret = media_entity_create_link(&resizer->subdev.entity, - RESIZER_PAD_SOURCE_MEM, - &resizer->video_out.video.entity, 0, 0); - if (ret < 0) - return ret; - - return 0; + return omap4iss_video_init(&resizer->video_out, "ISP resizer a"); } void omap4iss_resizer_unregister_entities(struct iss_resizer_device *resizer) @@ -863,6 +857,22 @@ int omap4iss_resizer_init(struct iss_device *iss) } /* + * omap4iss_resizer_create_links() - RESIZER pads links creation + * @iss: Pointer to ISS device + * + * return negative error code or zero on success + */ +int omap4iss_resizer_create_links(struct iss_device *iss) +{ + struct iss_resizer_device *resizer = &iss->resizer; + + /* Connect the RESIZER subdev to the video node. */ + return media_create_pad_link(&resizer->subdev.entity, + RESIZER_PAD_SOURCE_MEM, + &resizer->video_out.video.entity, 0, 0); +} + +/* * omap4iss_resizer_cleanup - RESIZER module cleanup. * @iss: Device pointer specific to the OMAP4 ISS. */ diff --git a/drivers/staging/media/omap4iss/iss_resizer.h b/drivers/staging/media/omap4iss/iss_resizer.h index 1e145ab..8b7c5fe 100644 --- a/drivers/staging/media/omap4iss/iss_resizer.h +++ b/drivers/staging/media/omap4iss/iss_resizer.h @@ -61,6 +61,7 @@ struct iss_resizer_device { struct iss_device; int omap4iss_resizer_init(struct iss_device *iss); +int omap4iss_resizer_create_links(struct iss_device *iss); void omap4iss_resizer_cleanup(struct iss_device *iss); int omap4iss_resizer_register_entities(struct iss_resizer_device *resizer, struct v4l2_device *vdev); diff --git a/drivers/staging/media/omap4iss/iss_video.c b/drivers/staging/media/omap4iss/iss_video.c index e9aeca0..058233a 100644 --- a/drivers/staging/media/omap4iss/iss_video.c +++ b/drivers/staging/media/omap4iss/iss_video.c @@ -190,8 +190,7 @@ iss_video_remote_subdev(struct iss_video *video, u32 *pad) remote = media_entity_remote_pad(&video->pad); - if (!remote || - media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + if (!remote || !is_media_entity_v4l2_subdev(remote->entity)) return NULL; if (pad) @@ -206,17 +205,23 @@ iss_video_far_end(struct iss_video *video) { struct media_entity_graph graph; struct media_entity *entity = &video->video.entity; - struct media_device *mdev = entity->parent; + struct media_device *mdev = entity->graph_obj.mdev; struct iss_video *far_end = NULL; mutex_lock(&mdev->graph_mutex); + + if (media_entity_graph_walk_init(&graph, mdev)) { + mutex_unlock(&mdev->graph_mutex); + return NULL; + } + media_entity_graph_walk_start(&graph, entity); while ((entity = media_entity_graph_walk_next(&graph))) { if (entity == &video->video.entity) continue; - if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE) + if (!is_media_entity_v4l2_io(entity)) continue; far_end = to_iss_video(media_entity_to_video_device(entity)); @@ -227,6 +232,9 @@ iss_video_far_end(struct iss_video *video) } mutex_unlock(&mdev->graph_mutex); + + media_entity_graph_walk_cleanup(&graph); + return far_end; } @@ -750,7 +758,7 @@ iss_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) struct iss_video_fh *vfh = to_iss_video_fh(fh); struct iss_video *video = video_drvdata(file); struct media_entity_graph graph; - struct media_entity *entity; + struct media_entity *entity = &video->video.entity; enum iss_pipeline_state state; struct iss_pipeline *pipe; struct iss_video *far_end; @@ -765,24 +773,30 @@ iss_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) /* Start streaming on the pipeline. No link touching an entity in the * pipeline can be activated or deactivated once streaming is started. */ - pipe = video->video.entity.pipe - ? to_iss_pipeline(&video->video.entity) : &video->pipe; + pipe = entity->pipe + ? to_iss_pipeline(entity) : &video->pipe; pipe->external = NULL; pipe->external_rate = 0; pipe->external_bpp = 0; - pipe->entities = 0; + + ret = media_entity_enum_init(&pipe->ent_enum, entity->graph_obj.mdev); + if (ret) + goto err_graph_walk_init; + + ret = media_entity_graph_walk_init(&graph, entity->graph_obj.mdev); + if (ret) + goto err_graph_walk_init; if (video->iss->pdata->set_constraints) video->iss->pdata->set_constraints(video->iss, true); - ret = media_entity_pipeline_start(&video->video.entity, &pipe->pipe); + ret = media_entity_pipeline_start(entity, &pipe->pipe); if (ret < 0) goto err_media_entity_pipeline_start; - entity = &video->video.entity; media_entity_graph_walk_start(&graph, entity); while ((entity = media_entity_graph_walk_next(&graph))) - pipe->entities |= 1 << entity->id; + media_entity_enum_set(&pipe->ent_enum, entity); /* Verify that the currently configured format matches the output of * the connected subdev. @@ -852,7 +866,10 @@ iss_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) spin_unlock_irqrestore(&video->qlock, flags); } + media_entity_graph_walk_cleanup(&graph); + mutex_unlock(&video->stream_lock); + return 0; err_omap4iss_set_stream: @@ -864,7 +881,13 @@ err_media_entity_pipeline_start: video->iss->pdata->set_constraints(video->iss, false); video->queue = NULL; + media_entity_graph_walk_cleanup(&graph); + +err_graph_walk_init: + media_entity_enum_cleanup(&pipe->ent_enum); + mutex_unlock(&video->stream_lock); + return ret; } @@ -902,6 +925,8 @@ iss_video_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) vb2_streamoff(&vfh->queue, type); video->queue = NULL; + media_entity_enum_cleanup(&pipe->ent_enum); + if (video->iss->pdata->set_constraints) video->iss->pdata->set_constraints(video->iss, false); media_entity_pipeline_stop(&video->video.entity); @@ -984,7 +1009,13 @@ static int iss_video_open(struct file *file) goto done; } - ret = omap4iss_pipeline_pm_use(&video->video.entity, 1); + ret = media_entity_graph_walk_init(&handle->graph, + &video->iss->media_dev); + if (ret) + goto done; + + ret = omap4iss_pipeline_pm_use(&video->video.entity, 1, + &handle->graph); if (ret < 0) { omap4iss_put(video->iss); goto done; @@ -1023,6 +1054,7 @@ static int iss_video_open(struct file *file) done: if (ret < 0) { v4l2_fh_del(&handle->vfh); + media_entity_graph_walk_cleanup(&handle->graph); kfree(handle); } @@ -1038,12 +1070,13 @@ static int iss_video_release(struct file *file) /* Disable streaming and free the buffers queue resources. */ iss_video_streamoff(file, vfh, video->type); - omap4iss_pipeline_pm_use(&video->video.entity, 0); + omap4iss_pipeline_pm_use(&video->video.entity, 0, &handle->graph); /* Release the videobuf2 queue */ vb2_queue_release(&handle->queue); /* Release the file handle. */ + media_entity_graph_walk_cleanup(&handle->graph); v4l2_fh_del(vfh); kfree(handle); file->private_data = NULL; @@ -1102,7 +1135,7 @@ int omap4iss_video_init(struct iss_video *video, const char *name) return -EINVAL; } - ret = media_entity_init(&video->video.entity, 1, &video->pad, 0); + ret = media_entity_pads_init(&video->video.entity, 1, &video->pad); if (ret < 0) return ret; diff --git a/drivers/staging/media/omap4iss/iss_video.h b/drivers/staging/media/omap4iss/iss_video.h index 41532ed..34588b7 100644 --- a/drivers/staging/media/omap4iss/iss_video.h +++ b/drivers/staging/media/omap4iss/iss_video.h @@ -77,7 +77,7 @@ enum iss_pipeline_state { /* * struct iss_pipeline - An OMAP4 ISS hardware pipeline - * @entities: Bitmask of entities in the pipeline (indexed by entity ID) + * @ent_enum: Entities in the pipeline * @error: A hardware error occurred during capture */ struct iss_pipeline { @@ -87,7 +87,7 @@ struct iss_pipeline { enum iss_pipeline_stream_state stream_state; struct iss_video *input; struct iss_video *output; - unsigned int entities; + struct media_entity_enum ent_enum; atomic_t frame_number; bool do_propagation; /* of frame number */ bool error; @@ -183,6 +183,7 @@ struct iss_video_fh { struct vb2_queue queue; struct v4l2_format format; struct v4l2_fract timeperframe; + struct media_entity_graph graph; }; #define to_iss_video_fh(fh) container_of(fh, struct iss_video_fh, vfh) diff --git a/include/media/media-device.h b/include/media/media-device.h index 6e6db78..d385589 100644 --- a/include/media/media-device.h +++ b/include/media/media-device.h @@ -30,6 +30,238 @@ #include <media/media-devnode.h> #include <media/media-entity.h> +/** + * DOC: Media Controller + * + * The media controller userspace API is documented in DocBook format in + * Documentation/DocBook/media/v4l/media-controller.xml. 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 &media_device instance in a + * larger driver-specific structure. + * + * Drivers register media device instances by calling + * __media_device_register() via the macro media_device_register() + * and unregistered by calling + * media_device_unregister(). + * + * * Entities, pads and links: + * + * - 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 a v4l2_subdev or video_device instance, + * although drivers can allocate entities directly. + * + * Drivers initialize entity pads by calling + * media_entity_pads_init(). + * + * Drivers register entities with a media device by calling + * media_device_register_entity() + * and unregistred by calling + * 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 + * media_devnode_create() + * and remove them by calling: + * 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 &media_pad structure, making the + * &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 and %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: + * media_create_pad_link() and remove with media_entity_remove_links(). + * + * 2. interface to entity links: + * + * Associate one interface to a Link. + * + * Drivers create interface to entity links by calling: + * media_create_intf_link() and remove with 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 media_create_pad_link() and + * media_create_intf_link(). + * + * Graph traversal: + * + * The media framework provides APIs to iterate over entities in a graph. + * + * 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. + * + * struct media_entity *entity; + * + * media_device_for_each_entity(entity, mdev) { + * // entity will point to each entity in turn + * ... + * } + * + * Drivers might also need to iterate over all entities in a graph that can be + * reached only through enabled links starting at a given entity. The media + * framework provides a depth-first graph traversal API for that purpose. + * + * Note that graphs with cycles (whether directed or undirected) are *NOT* + * supported by the graph traversal API. To prevent infinite loops, the graph + * traversal code limits the maximum depth to MEDIA_ENTITY_ENUM_MAX_DEPTH, + * currently defined as 16. + * + * Drivers initiate a graph traversal by calling + * media_entity_graph_walk_start() + * + * The graph structure, provided by the caller, is initialized to start graph + * traversal at the given entity. + * + * Drivers can then retrieve the next entity by calling + * media_entity_graph_walk_next() + * + * When the graph traversal is complete the function will return NULL. + * + * Graph traversal can be interrupted at any moment. No cleanup function call + * is required and the graph structure can be freed normally. + * + * Helper functions can be used to find a link between two given pads, or a pad + * connected to another pad through an enabled link + * media_entity_find_link() and media_entity_remote_pad() + * + * 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 &media_entity structure includes a use_count field that media drivers + * can use to track the number of users of every entity for power management + * needs. + * + * The &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 + * &media_device.@graph_mutex lock. + * + * Links setup: + * + * Link properties can be modified at runtime by calling + * media_entity_setup_link() + * + * Pipelines and media streams: + * + * When starting streaming, drivers must notify all entities in the pipeline to + * prevent link states from being modified during streaming by calling + * media_entity_pipeline_start(). + * + * The function will mark all entities connected to the given entity through + * enabled links, either directly or indirectly, as streaming. + * + * The &media_pipeline instance pointed to by the pipe argument will be stored + * in every entity in the pipeline. Drivers should embed the &media_pipeline + * structure in higher-level pipeline structures and can then access the + * pipeline through the &media_entity pipe field. + * + * Calls to media_entity_pipeline_start() can be nested. The pipeline pointer + * must be identical for all nested calls to the function. + * + * media_entity_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 + * media_entity_pipeline_stop(). + * + * If multiple calls to media_entity_pipeline_start() have been made the same + * number of media_entity_pipeline_stop() calls are required to stop streaming. + * The &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 media_entity_pipeline_start() for any + * entity which has sink pads in the pipeline. The + * &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. + */ + +struct ida; struct device; /** @@ -41,8 +273,16 @@ struct device; * @bus_info: Unique and stable device location identifier * @hw_revision: Hardware device revision * @driver_version: Device driver version - * @entity_id: ID of the next entity to be registered + * @topology_version: Monotonic counter for storing the version of the graph + * topology. Should be incremented each time the topology changes. + * @id: Unique ID used on the last registered graph object + * @entity_internal_idx: Unique internal entity ID used by the graph traversal + * algorithms + * @entity_internal_idx_max: Allocated internal entity indices * @entities: List of registered entities + * @interfaces: List of registered interfaces + * @pads: List of registered pads + * @links: List of registered links * @lock: Entities list lock * @graph_mutex: Entities graph operation lock * @link_notify: Link state change notification callback @@ -68,10 +308,18 @@ struct media_device { u32 hw_revision; u32 driver_version; - u32 entity_id; + u32 topology_version; + + u32 id; + struct ida entity_internal_idx; + int entity_internal_idx_max; + struct list_head entities; + struct list_head interfaces; + struct list_head pads; + struct list_head links; - /* Protects the entities list */ + /* Protects the graph objects creation/removal */ spinlock_t lock; /* Serializes graph operations. */ struct mutex graph_mutex; @@ -80,6 +328,8 @@ struct media_device { unsigned int notification); }; +#ifdef CONFIG_MEDIA_CONTROLLER + /* Supported link_notify @notification values. */ #define MEDIA_DEV_NOTIFY_PRE_LINK_CH 0 #define MEDIA_DEV_NOTIFY_POST_LINK_CH 1 @@ -87,17 +337,228 @@ struct media_device { /* media_devnode to media_device */ #define to_media_device(node) container_of(node, struct media_device, devnode) +/** + * media_entity_enum_init - Initialise an entity enumeration + * + * @ent_enum: Entity enumeration to be initialised + * @mdev: The related media device + * + * Returns zero on success or a negative error code. + */ +static inline __must_check int media_entity_enum_init( + struct media_entity_enum *ent_enum, struct media_device *mdev) +{ + return __media_entity_enum_init(ent_enum, + mdev->entity_internal_idx_max + 1); +} + +/** + * media_device_init() - Initializes a media device element + * + * @mdev: pointer to struct &media_device + * + * This function initializes the media device prior to its registration. + * The media device initialization and registration is split in two functions + * to avoid race conditions and make the media device available to user-space + * before the media graph has been completed. + * + * So drivers need to first initialize the media device, register any entity + * within the media device, create pad to pad links and then finally register + * the media device by calling media_device_register() as a final step. + */ +void media_device_init(struct media_device *mdev); + +/** + * media_device_cleanup() - Cleanups a media device element + * + * @mdev: pointer to struct &media_device + * + * This function that will destroy the graph_mutex that is + * initialized in media_device_init(). + */ +void media_device_cleanup(struct media_device *mdev); + +/** + * __media_device_register() - Registers a media device element + * + * @mdev: pointer to struct &media_device + * @owner: should be filled with %THIS_MODULE + * + * Users, should, instead, call the media_device_register() macro. + * + * The caller is responsible for initializing the media_device structure before + * registration. The following fields must be set: + * + * - dev must point to the parent device (usually a &pci_dev, &usb_interface or + * &platform_device instance). + * + * - model must be filled with the device model name as a NUL-terminated UTF-8 + * string. The device/model revision must not be stored in this field. + * + * The following fields are optional: + * + * - serial is a unique serial number stored as a NUL-terminated ASCII string. + * The field is big enough to store a GUID in text form. If the hardware + * doesn't provide a unique serial number this field must be left empty. + * + * - bus_info represents the location of the device in the system as a + * NUL-terminated ASCII string. For PCI/PCIe devices bus_info must be set to + * "PCI:" (or "PCIe:") followed by the value of pci_name(). For USB devices, + * the usb_make_path() function must be used. This field is used by + * applications to distinguish between otherwise identical devices that don't + * provide a serial number. + * + * - hw_revision is the hardware device revision in a driver-specific format. + * When possible the revision should be formatted with the KERNEL_VERSION + * macro. + * + * - driver_version is formatted with the KERNEL_VERSION macro. The version + * minor must be incremented when new features are added to the userspace API + * without breaking binary compatibility. The version major must be + * incremented when binary compatibility is broken. + * + * Notes: + * + * Upon successful registration a character device named media[0-9]+ is created. + * The device major and minor numbers are dynamic. The model name is exported as + * a sysfs attribute. + * + * Unregistering a media device that hasn't been registered is *NOT* safe. + * + * Return: returns zero on success or a negative error code. + */ int __must_check __media_device_register(struct media_device *mdev, struct module *owner); #define media_device_register(mdev) __media_device_register(mdev, THIS_MODULE) + +/** + * __media_device_unregister() - Unegisters a media device element + * + * @mdev: pointer to struct &media_device + * + * + * It is safe to call this function on an unregistered (but initialised) + * media device. + */ void media_device_unregister(struct media_device *mdev); +/** + * media_device_register_entity() - registers a media entity inside a + * previously registered media device. + * + * @mdev: pointer to struct &media_device + * @entity: pointer to struct &media_entity to be registered + * + * Entities are identified by a unique positive integer ID. The media + * controller framework will such ID automatically. IDs are not guaranteed + * to be contiguous, and the ID number can change on newer Kernel versions. + * So, neither the driver nor userspace should hardcode ID numbers to refer + * to the entities, but, instead, use the framework to find the ID, when + * needed. + * + * The media_entity name, type and flags fields should be initialized before + * calling media_device_register_entity(). Entities embedded in higher-level + * standard structures can have some of those fields set by the higher-level + * framework. + * + * If the device has pads, media_entity_pads_init() should be called before + * this function. Otherwise, the &media_entity.@pad and &media_entity.@num_pads + * should be zeroed before calling this function. + * + * Entities have flags that describe the entity capabilities and state: + * + * %MEDIA_ENT_FL_DEFAULT indicates the default entity for a given type. + * This can be used to report the default audio and video devices or the + * default camera sensor. + * + * NOTE: Drivers should set the entity function before calling this function. + * Please notice that the values %MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN and + * %MEDIA_ENT_F_UNKNOWN should not be used by the drivers. + */ int __must_check media_device_register_entity(struct media_device *mdev, struct media_entity *entity); + +/* + * media_device_unregister_entity() - unregisters a media entity. + * + * @entity: pointer to struct &media_entity to be unregistered + * + * All links associated with the entity and all PADs are automatically + * unregistered from the media_device when this function is called. + * + * Unregistering an entity will not change the IDs of the other entities and + * the previoully used ID will never be reused for a newly registered entities. + * + * When a media device is unregistered, all its entities are unregistered + * automatically. No manual entities unregistration is then required. + * + * Note: the media_entity instance itself must be freed explicitly by + * the driver if required. + */ void media_device_unregister_entity(struct media_entity *entity); +/** + * media_device_get_devres() - get media device as device resource + * creates if one doesn't exist + * + * @dev: pointer to struct &device. + * + * Sometimes, the media controller &media_device needs to be shared by more + * than one driver. This function adds support for that, by dynamically + * allocating the &media_device and allowing it to be obtained from the + * struct &device associated with the common device where all sub-device + * components belong. So, for example, on an USB device with multiple + * interfaces, each interface may be handled by a separate per-interface + * drivers. While each interface have its own &device, they all share a + * common &device associated with the hole USB device. + */ +struct media_device *media_device_get_devres(struct device *dev); + +/** + * media_device_find_devres() - find media device as device resource + * + * @dev: pointer to struct &device. + */ +struct media_device *media_device_find_devres(struct device *dev); + /* Iterate over all entities. */ #define media_device_for_each_entity(entity, mdev) \ - list_for_each_entry(entity, &(mdev)->entities, list) + list_for_each_entry(entity, &(mdev)->entities, graph_obj.list) + +/* Iterate over all interfaces. */ +#define media_device_for_each_intf(intf, mdev) \ + list_for_each_entry(intf, &(mdev)->interfaces, graph_obj.list) + +/* Iterate over all pads. */ +#define media_device_for_each_pad(pad, mdev) \ + list_for_each_entry(pad, &(mdev)->pads, graph_obj.list) +/* Iterate over all links. */ +#define media_device_for_each_link(link, mdev) \ + list_for_each_entry(link, &(mdev)->links, graph_obj.list) +#else +static inline int media_device_register(struct media_device *mdev) +{ + return 0; +} +static inline void media_device_unregister(struct media_device *mdev) +{ +} +static inline int media_device_register_entity(struct media_device *mdev, + struct media_entity *entity) +{ + return 0; +} +static inline void media_device_unregister_entity(struct media_entity *entity) +{ +} +static inline struct media_device *media_device_get_devres(struct device *dev) +{ + return NULL; +} +static inline struct media_device *media_device_find_devres(struct device *dev) +{ + return NULL; +} +#endif /* CONFIG_MEDIA_CONTROLLER */ #endif diff --git a/include/media/media-devnode.h b/include/media/media-devnode.h index 17ddae3..fe42f08 100644 --- a/include/media/media-devnode.h +++ b/include/media/media-devnode.h @@ -40,6 +40,20 @@ */ #define MEDIA_FLAG_REGISTERED 0 +/** + * struct media_file_operations - Media device file operations + * + * @owner: should be filled with %THIS_MODULE + * @read: pointer to the function that implements read() syscall + * @write: pointer to the function that implements write() syscall + * @poll: pointer to the function that implements poll() syscall + * @ioctl: pointer to the function that implements ioctl() syscall + * @compat_ioctl: pointer to the function that will handle 32 bits userspace + * calls to the the ioctl() syscall on a Kernel compiled with 64 bits. + * @open: pointer to the function that implements open() syscall + * @release: pointer to the function that will release the resources allocated + * by the @open function. + */ struct media_file_operations { struct module *owner; ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); @@ -53,7 +67,7 @@ struct media_file_operations { /** * struct media_devnode - Media device node - * @fops: pointer to struct media_file_operations with media device ops + * @fops: pointer to struct &media_file_operations with media device ops * @dev: struct device pointer for the media controller device * @cdev: struct cdev pointer character device * @parent: parent device @@ -86,15 +100,53 @@ struct media_devnode { /* dev to media_devnode */ #define to_media_devnode(cd) container_of(cd, struct media_devnode, dev) +/** + * media_devnode_register - register a media device node + * + * @mdev: media device node structure we want to register + * @owner: should be filled with %THIS_MODULE + * + * The registration code assigns minor numbers and registers the new device node + * with the kernel. An error is returned if no free minor number can be found, + * or if the registration of the device node fails. + * + * Zero is returned on success. + * + * Note that if the media_devnode_register call fails, the release() callback of + * the media_devnode structure is *not* called, so the caller is responsible for + * freeing any data. + */ int __must_check media_devnode_register(struct media_devnode *mdev, struct module *owner); + +/** + * media_devnode_unregister - unregister a media device node + * @mdev: the device node to unregister + * + * This unregisters the passed device. Future open calls will be met with + * errors. + * + * This function can safely be called if the device node has never been + * registered or has already been unregistered. + */ void media_devnode_unregister(struct media_devnode *mdev); +/** + * media_devnode_data - returns a pointer to the &media_devnode + * + * @filp: pointer to struct &file + */ static inline struct media_devnode *media_devnode_data(struct file *filp) { return filp->private_data; } +/** + * media_devnode_is_registered - returns true if &media_devnode is registered; + * false otherwise. + * + * @mdev: pointer to struct &media_devnode. + */ static inline int media_devnode_is_registered(struct media_devnode *mdev) { return test_bit(MEDIA_FLAG_REGISTERED, &mdev->flags); diff --git a/include/media/media-entity.h b/include/media/media-entity.h index 197f937..fe485d3 100644 --- a/include/media/media-entity.h +++ b/include/media/media-entity.h @@ -23,25 +23,151 @@ #ifndef _MEDIA_ENTITY_H #define _MEDIA_ENTITY_H -#include <linux/bitops.h> +#include <linux/bitmap.h> #include <linux/kernel.h> #include <linux/list.h> #include <linux/media.h> +/* Enums used internally at the media controller to represent graphs */ + +/** + * enum media_gobj_type - type of a graph object + * + * @MEDIA_GRAPH_ENTITY: Identify a media entity + * @MEDIA_GRAPH_PAD: Identify a media pad + * @MEDIA_GRAPH_LINK: Identify a media link + * @MEDIA_GRAPH_INTF_DEVNODE: Identify a media Kernel API interface via + * a device node + */ +enum media_gobj_type { + MEDIA_GRAPH_ENTITY, + MEDIA_GRAPH_PAD, + MEDIA_GRAPH_LINK, + MEDIA_GRAPH_INTF_DEVNODE, +}; + +#define MEDIA_BITS_PER_TYPE 8 +#define MEDIA_BITS_PER_ID (32 - MEDIA_BITS_PER_TYPE) +#define MEDIA_ID_MASK GENMASK_ULL(MEDIA_BITS_PER_ID - 1, 0) + +/* Structs to represent the objects that belong to a media graph */ + +/** + * struct media_gobj - Define a graph object. + * + * @mdev: Pointer to the struct media_device that owns the object + * @id: Non-zero object ID identifier. The ID should be unique + * inside a media_device, as it is composed by + * %MEDIA_BITS_PER_TYPE to store the type plus + * %MEDIA_BITS_PER_ID to store the ID + * @list: List entry stored in one of the per-type mdev object lists + * + * All objects on the media graph should have this struct embedded + */ +struct media_gobj { + struct media_device *mdev; + u32 id; + struct list_head list; +}; + +#define MEDIA_ENTITY_ENUM_MAX_DEPTH 16 + +/** + * struct media_entity_enum - An enumeration of media entities. + * + * @bmap: Bit map in which each bit represents one entity at struct + * media_entity->internal_idx. + * @idx_max: Number of bits in bmap + */ +struct media_entity_enum { + unsigned long *bmap; + int idx_max; +}; + +/** + * struct media_entity_graph - Media graph traversal state + * + * @stack: Graph traversal stack; the stack contains information + * on the path the media entities to be walked and the + * links through which they were reached. + * @ent_enum: Visited entities + * @top: The top of the stack + */ +struct media_entity_graph { + struct { + struct media_entity *entity; + struct list_head *link; + } stack[MEDIA_ENTITY_ENUM_MAX_DEPTH]; + + struct media_entity_enum ent_enum; + int top; +}; + +/* + * struct media_pipeline - Media pipeline related information + * + * @streaming_count: Streaming start count - streaming stop count + * @graph: Media graph walk during pipeline start / stop + */ struct media_pipeline { + int streaming_count; + struct media_entity_graph graph; }; +/** + * struct media_link - A link object part of a media graph. + * + * @graph_obj: Embedded structure containing the media object common data + * @list: Linked list associated with an entity or an interface that + * owns the link. + * @gobj0: Part of a union. Used to get the pointer for the first + * graph_object of the link. + * @source: Part of a union. Used only if the first object (gobj0) is + * a pad. In that case, it represents the source pad. + * @intf: Part of a union. Used only if the first object (gobj0) is + * an interface. + * @gobj1: Part of a union. Used to get the pointer for the second + * graph_object of the link. + * @source: Part of a union. Used only if the second object (gobj1) is + * a pad. In that case, it represents the sink pad. + * @entity: Part of a union. Used only if the second object (gobj1) is + * an entity. + * @reverse: Pointer to the link for the reverse direction of a pad to pad + * link. + * @flags: Link flags, as defined in uapi/media.h (MEDIA_LNK_FL_*) + * @is_backlink: Indicate if the link is a backlink. + */ struct media_link { - struct media_pad *source; /* Source pad */ - struct media_pad *sink; /* Sink pad */ - struct media_link *reverse; /* Link in the reverse direction */ - unsigned long flags; /* Link flags (MEDIA_LNK_FL_*) */ + struct media_gobj graph_obj; + struct list_head list; + union { + struct media_gobj *gobj0; + struct media_pad *source; + struct media_interface *intf; + }; + union { + struct media_gobj *gobj1; + struct media_pad *sink; + struct media_entity *entity; + }; + struct media_link *reverse; + unsigned long flags; + bool is_backlink; }; +/** + * struct media_pad - A media pad graph object. + * + * @graph_obj: Embedded structure containing the media object common data + * @entity: Entity this pad belongs to + * @index: Pad index in the entity pads array, numbered from 0 to n + * @flags: Pad flags, as defined in uapi/media.h (MEDIA_PAD_FL_*) + */ struct media_pad { - struct media_entity *entity; /* Entity this pad belongs to */ - u16 index; /* Pad index in the entity pads array */ - unsigned long flags; /* Pad flags (MEDIA_PAD_FL_*) */ + struct media_gobj graph_obj; /* must be first field in struct */ + struct media_entity *entity; + u16 index; + unsigned long flags; }; /** @@ -60,105 +186,763 @@ struct media_entity_operations { int (*link_validate)(struct media_link *link); }; +/** + * struct media_entity - A media entity graph object. + * + * @graph_obj: Embedded structure containing the media object common data. + * @name: Entity name. + * @function: Entity main function, as defined in uapi/media.h + * (MEDIA_ENT_F_*) + * @flags: Entity flags, as defined in uapi/media.h (MEDIA_ENT_FL_*) + * @num_pads: Number of sink and source pads. + * @num_links: Total number of links, forward and back, enabled and disabled. + * @num_backlinks: Number of backlinks + * @internal_idx: An unique internal entity specific number. The numbers are + * re-used if entities are unregistered or registered again. + * @pads: Pads array with the size defined by @num_pads. + * @links: List of data links. + * @ops: Entity operations. + * @stream_count: Stream count for the entity. + * @use_count: Use count for the entity. + * @pipe: Pipeline this entity belongs to. + * @info: Union with devnode information. Kept just for backward + * compatibility. + * @major: Devnode major number (zero if not applicable). Kept just + * for backward compatibility. + * @minor: Devnode minor number (zero if not applicable). Kept just + * for backward compatibility. + * + * NOTE: @stream_count and @use_count reference counts must never be + * negative, but are signed integers on purpose: a simple WARN_ON(<0) check + * can be used to detect reference count bugs that would make them negative. + */ struct media_entity { - struct list_head list; - struct media_device *parent; /* Media device this entity belongs to*/ - u32 id; /* Entity ID, unique in the parent media - * device context */ - const char *name; /* Entity name */ - u32 type; /* Entity type (MEDIA_ENT_T_*) */ - u32 revision; /* Entity revision, driver specific */ - unsigned long flags; /* Entity flags (MEDIA_ENT_FL_*) */ - u32 group_id; /* Entity group ID */ - - u16 num_pads; /* Number of sink and source pads */ - u16 num_links; /* Number of existing links, both - * enabled and disabled */ - u16 num_backlinks; /* Number of backlinks */ - u16 max_links; /* Maximum number of links */ - - struct media_pad *pads; /* Pads array (num_pads elements) */ - struct media_link *links; /* Links array (max_links elements)*/ - - const struct media_entity_operations *ops; /* Entity operations */ + struct media_gobj graph_obj; /* must be first field in struct */ + const char *name; + u32 function; + unsigned long flags; + + u16 num_pads; + u16 num_links; + u16 num_backlinks; + int internal_idx; + + struct media_pad *pads; + struct list_head links; + + const struct media_entity_operations *ops; /* Reference counts must never be negative, but are signed integers on * purpose: a simple WARN_ON(<0) check can be used to detect reference * count bugs that would make them negative. */ - int stream_count; /* Stream count for the entity. */ - int use_count; /* Use count for the entity. */ + int stream_count; + int use_count; - struct media_pipeline *pipe; /* Pipeline this entity belongs to. */ + struct media_pipeline *pipe; union { - /* Node specifications */ struct { u32 major; u32 minor; } dev; - - /* Sub-device specifications */ - /* Nothing needed yet */ } info; }; -static inline u32 media_entity_type(struct media_entity *entity) +/** + * struct media_interface - A media interface graph object. + * + * @graph_obj: embedded graph object + * @links: List of links pointing to graph entities + * @type: Type of the interface as defined in the + * uapi/media/media.h header, e. g. + * MEDIA_INTF_T_* + * @flags: Interface flags as defined in uapi/media/media.h + */ +struct media_interface { + struct media_gobj graph_obj; + struct list_head links; + u32 type; + u32 flags; +}; + +/** + * struct media_intf_devnode - A media interface via a device node. + * + * @intf: embedded interface object + * @major: Major number of a device node + * @minor: Minor number of a device node + */ +struct media_intf_devnode { + struct media_interface intf; + + /* Should match the fields at media_v2_intf_devnode */ + u32 major; + u32 minor; +}; + +/** + * media_entity_id() - return the media entity graph object id + * + * @entity: pointer to entity + */ +static inline u32 media_entity_id(struct media_entity *entity) { - return entity->type & MEDIA_ENT_TYPE_MASK; + return entity->graph_obj.id; } -static inline u32 media_entity_subtype(struct media_entity *entity) +/** + * media_type() - return the media object type + * + * @gobj: pointer to the media graph object + */ +static inline enum media_gobj_type media_type(struct media_gobj *gobj) { - return entity->type & MEDIA_ENT_SUBTYPE_MASK; + return gobj->id >> MEDIA_BITS_PER_ID; } -#define MEDIA_ENTITY_ENUM_MAX_DEPTH 16 -#define MEDIA_ENTITY_ENUM_MAX_ID 64 +/** + * media_id() - return the media object ID + * + * @gobj: pointer to the media graph object + */ +static inline u32 media_id(struct media_gobj *gobj) +{ + return gobj->id & MEDIA_ID_MASK; +} -/* - * The number of pads can't be bigger than the number of entities, - * as the worse-case scenario is to have one entity linked up to - * MEDIA_ENTITY_ENUM_MAX_ID - 1 entities. +/** + * media_gobj_gen_id() - encapsulates type and ID on at the object ID + * + * @type: object type as define at enum &media_gobj_type. + * @local_id: next ID, from struct &media_device.@id. */ -#define MEDIA_ENTITY_MAX_PADS (MEDIA_ENTITY_ENUM_MAX_ID - 1) +static inline u32 media_gobj_gen_id(enum media_gobj_type type, u64 local_id) +{ + u32 id; -struct media_entity_graph { - struct { - struct media_entity *entity; - int link; - } stack[MEDIA_ENTITY_ENUM_MAX_DEPTH]; + id = type << MEDIA_BITS_PER_ID; + id |= local_id & MEDIA_ID_MASK; - DECLARE_BITMAP(entities, MEDIA_ENTITY_ENUM_MAX_ID); - int top; -}; + return id; +} + +/** + * is_media_entity_v4l2_io() - identify if the entity main function + * is a V4L2 I/O + * + * @entity: pointer to entity + * + * Return: true if the entity main function is one of the V4L2 I/O types + * (video, VBI or SDR radio); false otherwise. + */ +static inline bool is_media_entity_v4l2_io(struct media_entity *entity) +{ + if (!entity) + return false; + + switch (entity->function) { + case MEDIA_ENT_F_IO_V4L: + case MEDIA_ENT_F_IO_VBI: + case MEDIA_ENT_F_IO_SWRADIO: + return true; + default: + return false; + } +} + +/** + * is_media_entity_v4l2_subdev - return true if the entity main function is + * associated with the V4L2 API subdev usage + * + * @entity: pointer to entity + * + * This is an ancillary function used by subdev-based V4L2 drivers. + * It checks if the entity function is one of functions used by a V4L2 subdev, + * e. g. camera-relatef functions, analog TV decoder, TV tuner, V4L2 DSPs. + */ +static inline bool is_media_entity_v4l2_subdev(struct media_entity *entity) +{ + if (!entity) + return false; + + switch (entity->function) { + case MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN: + case MEDIA_ENT_F_CAM_SENSOR: + case MEDIA_ENT_F_FLASH: + case MEDIA_ENT_F_LENS: + case MEDIA_ENT_F_ATV_DECODER: + case MEDIA_ENT_F_TUNER: + return true; + + default: + return false; + } +} + +/** + * __media_entity_enum_init - Initialise an entity enumeration + * + * @ent_enum: Entity enumeration to be initialised + * @idx_max: Maximum number of entities in the enumeration + * + * Return: Returns zero on success or a negative error code. + */ +__must_check int __media_entity_enum_init(struct media_entity_enum *ent_enum, + int idx_max); + +/** + * media_entity_enum_cleanup - Release resources of an entity enumeration + * + * @ent_enum: Entity enumeration to be released + */ +void media_entity_enum_cleanup(struct media_entity_enum *ent_enum); + +/** + * media_entity_enum_zero - Clear the entire enum + * + * @ent_enum: Entity enumeration to be cleared + */ +static inline void media_entity_enum_zero(struct media_entity_enum *ent_enum) +{ + bitmap_zero(ent_enum->bmap, ent_enum->idx_max); +} + +/** + * media_entity_enum_set - Mark a single entity in the enum + * + * @ent_enum: Entity enumeration + * @entity: Entity to be marked + */ +static inline void media_entity_enum_set(struct media_entity_enum *ent_enum, + struct media_entity *entity) +{ + if (WARN_ON(entity->internal_idx >= ent_enum->idx_max)) + return; + + __set_bit(entity->internal_idx, ent_enum->bmap); +} + +/** + * media_entity_enum_clear - Unmark a single entity in the enum + * + * @ent_enum: Entity enumeration + * @entity: Entity to be unmarked + */ +static inline void media_entity_enum_clear(struct media_entity_enum *ent_enum, + struct media_entity *entity) +{ + if (WARN_ON(entity->internal_idx >= ent_enum->idx_max)) + return; -int media_entity_init(struct media_entity *entity, u16 num_pads, - struct media_pad *pads, u16 extra_links); -void media_entity_cleanup(struct media_entity *entity); + __clear_bit(entity->internal_idx, ent_enum->bmap); +} + +/** + * media_entity_enum_test - Test whether the entity is marked + * + * @ent_enum: Entity enumeration + * @entity: Entity to be tested + * + * Returns true if the entity was marked. + */ +static inline bool media_entity_enum_test(struct media_entity_enum *ent_enum, + struct media_entity *entity) +{ + if (WARN_ON(entity->internal_idx >= ent_enum->idx_max)) + return true; + + return test_bit(entity->internal_idx, ent_enum->bmap); +} + +/** + * media_entity_enum_test - Test whether the entity is marked, and mark it + * + * @ent_enum: Entity enumeration + * @entity: Entity to be tested + * + * Returns true if the entity was marked, and mark it before doing so. + */ +static inline bool +media_entity_enum_test_and_set(struct media_entity_enum *ent_enum, + struct media_entity *entity) +{ + if (WARN_ON(entity->internal_idx >= ent_enum->idx_max)) + return true; + + return __test_and_set_bit(entity->internal_idx, ent_enum->bmap); +} + +/** + * media_entity_enum_empty - Test whether the entire enum is empty + * + * @ent_enum: Entity enumeration + * + * Returns true if the entity was marked. + */ +static inline bool media_entity_enum_empty(struct media_entity_enum *ent_enum) +{ + return bitmap_empty(ent_enum->bmap, ent_enum->idx_max); +} + +/** + * media_entity_enum_intersects - Test whether two enums intersect + * + * @ent_enum1: First entity enumeration + * @ent_enum2: Second entity enumeration + * + * Returns true if entity enumerations e and f intersect, otherwise false. + */ +static inline bool media_entity_enum_intersects( + struct media_entity_enum *ent_enum1, + struct media_entity_enum *ent_enum2) +{ + WARN_ON(ent_enum1->idx_max != ent_enum2->idx_max); + + return bitmap_intersects(ent_enum1->bmap, ent_enum2->bmap, + min(ent_enum1->idx_max, ent_enum2->idx_max)); +} + +#define gobj_to_entity(gobj) \ + container_of(gobj, struct media_entity, graph_obj) + +#define gobj_to_pad(gobj) \ + container_of(gobj, struct media_pad, graph_obj) + +#define gobj_to_link(gobj) \ + container_of(gobj, struct media_link, graph_obj) + +#define gobj_to_link(gobj) \ + container_of(gobj, struct media_link, graph_obj) + +#define gobj_to_pad(gobj) \ + container_of(gobj, struct media_pad, graph_obj) + +#define gobj_to_intf(gobj) \ + container_of(gobj, struct media_interface, graph_obj) + +#define intf_to_devnode(intf) \ + container_of(intf, struct media_intf_devnode, intf) + +/** + * media_gobj_create - Initialize a graph object + * + * @mdev: Pointer to the media_device that contains the object + * @type: Type of the object + * @gobj: Pointer to the graph object + * + * This routine initializes the embedded struct media_gobj inside a + * media graph object. It is called automatically if media_*_create() + * calls are used. However, if the object (entity, link, pad, interface) + * is embedded on some other object, this function should be called before + * registering the object at the media controller. + */ +void media_gobj_create(struct media_device *mdev, + enum media_gobj_type type, + struct media_gobj *gobj); + +/** + * media_gobj_destroy - Stop using a graph object on a media device + * + * @gobj: Pointer to the graph object + * + * This should be called by all routines like media_device_unregister() + * that remove/destroy media graph objects. + */ +void media_gobj_destroy(struct media_gobj *gobj); + +/** + * media_entity_pads_init() - Initialize the entity pads + * + * @entity: entity where the pads belong + * @num_pads: total number of sink and source pads + * @pads: Array of @num_pads pads. + * + * The pads array is managed by the entity driver and passed to + * media_entity_pads_init() where its pointer will be stored in the entity + * structure. + * + * If no pads are needed, drivers could either directly fill + * &media_entity->@num_pads with 0 and &media_entity->@pads with NULL or call + * this function that will do the same. + * + * As the number of pads is known in advance, the pads array is not allocated + * dynamically but is managed by the entity driver. Most drivers will embed the + * pads array in a driver-specific structure, avoiding dynamic allocation. + * + * Drivers must set the direction of every pad in the pads array before calling + * media_entity_pads_init(). The function will initialize the other pads fields. + */ +int media_entity_pads_init(struct media_entity *entity, u16 num_pads, + struct media_pad *pads); + +/** + * media_entity_cleanup() - free resources associated with an entity + * + * @entity: entity where the pads belong + * + * This function must be called during the cleanup phase after unregistering + * the entity (currently, it does nothing). + */ +static inline void media_entity_cleanup(struct media_entity *entity) {}; + +/** + * media_create_pad_link() - creates a link between two entities. + * + * @source: pointer to &media_entity of the source pad. + * @source_pad: number of the source pad in the pads array + * @sink: pointer to &media_entity of the sink pad. + * @sink_pad: number of the sink pad in the pads array. + * @flags: Link flags, as defined in include/uapi/linux/media.h. + * + * Valid values for flags: + * A %MEDIA_LNK_FL_ENABLED flag indicates that the link is enabled and can be + * used to transfer media data. When two or more links target a sink pad, + * only one of them can be enabled at a time. + * + * A %MEDIA_LNK_FL_IMMUTABLE flag indicates that the link enabled state can't + * be modified at runtime. If %MEDIA_LNK_FL_IMMUTABLE is set, then + * %MEDIA_LNK_FL_ENABLED must also be set since an immutable link is + * always enabled. + * + * NOTE: + * + * Before calling this function, media_entity_pads_init() and + * media_device_register_entity() should be called previously for both ends. + */ +__must_check int media_create_pad_link(struct media_entity *source, + u16 source_pad, struct media_entity *sink, + u16 sink_pad, u32 flags); + +/** + * media_create_pad_links() - creates a link between two entities. + * + * @mdev: Pointer to the media_device that contains the object + * @source_function: Function of the source entities. Used only if @source is + * NULL. + * @source: pointer to &media_entity of the source pad. If NULL, it will use + * all entities that matches the @sink_function. + * @source_pad: number of the source pad in the pads array + * @sink_function: Function of the sink entities. Used only if @sink is NULL. + * @sink: pointer to &media_entity of the sink pad. If NULL, it will use + * all entities that matches the @sink_function. + * @sink_pad: number of the sink pad in the pads array. + * @flags: Link flags, as defined in include/uapi/linux/media.h. + * @allow_both_undefined: if true, then both @source and @sink can be NULL. + * In such case, it will create a crossbar between all entities that + * matches @source_function to all entities that matches @sink_function. + * If false, it will return 0 and won't create any link if both @source + * and @sink are NULL. + * + * Valid values for flags: + * A %MEDIA_LNK_FL_ENABLED flag indicates that the link is enabled and can be + * used to transfer media data. If multiple links are created and this + * flag is passed as an argument, only the first created link will have + * this flag. + * + * A %MEDIA_LNK_FL_IMMUTABLE flag indicates that the link enabled state can't + * be modified at runtime. If %MEDIA_LNK_FL_IMMUTABLE is set, then + * %MEDIA_LNK_FL_ENABLED must also be set since an immutable link is + * always enabled. + * + * It is common for some devices to have multiple source and/or sink entities + * of the same type that should be linked. While media_create_pad_link() + * creates link by link, this function is meant to allow 1:n, n:1 and even + * cross-bar (n:n) links. + * + * NOTE: Before calling this function, media_entity_pads_init() and + * media_device_register_entity() should be called previously for the entities + * to be linked. + */ +int media_create_pad_links(const struct media_device *mdev, + const u32 source_function, + struct media_entity *source, + const u16 source_pad, + const u32 sink_function, + struct media_entity *sink, + const u16 sink_pad, + u32 flags, + const bool allow_both_undefined); -int media_entity_create_link(struct media_entity *source, u16 source_pad, - struct media_entity *sink, u16 sink_pad, u32 flags); void __media_entity_remove_links(struct media_entity *entity); + +/** + * media_entity_remove_links() - remove all links associated with an entity + * + * @entity: pointer to &media_entity + * + * Note: this is called automatically when an entity is unregistered via + * media_device_register_entity(). + */ void media_entity_remove_links(struct media_entity *entity); +/** + * __media_entity_setup_link - Configure a media link without locking + * @link: The link being configured + * @flags: Link configuration flags + * + * The bulk of link setup is handled by the two entities connected through the + * link. This function notifies both entities of the link configuration change. + * + * If the link is immutable or if the current and new configuration are + * identical, return immediately. + * + * The user is expected to hold link->source->parent->mutex. If not, + * media_entity_setup_link() should be used instead. + */ int __media_entity_setup_link(struct media_link *link, u32 flags); + +/** + * media_entity_setup_link() - changes the link flags properties in runtime + * + * @link: pointer to &media_link + * @flags: the requested new link flags + * + * The only configurable property is the %MEDIA_LNK_FL_ENABLED link flag + * flag to enable/disable a link. Links marked with the + * %MEDIA_LNK_FL_IMMUTABLE link flag can not be enabled or disabled. + * + * When a link is enabled or disabled, the media framework calls the + * link_setup operation for the two entities at the source and sink of the + * link, in that order. If the second link_setup call fails, another + * link_setup call is made on the first entity to restore the original link + * flags. + * + * Media device drivers can be notified of link setup operations by setting the + * media_device::link_notify pointer to a callback function. If provided, the + * notification callback will be called before enabling and after disabling + * links. + * + * Entity drivers must implement the link_setup operation if any of their links + * is non-immutable. The operation must either configure the hardware or store + * the configuration information to be applied later. + * + * Link configuration must not have any side effect on other links. If an + * enabled link at a sink pad prevents another link at the same pad from + * being enabled, the link_setup operation must return -EBUSY and can't + * implicitly disable the first enabled link. + * + * NOTE: the valid values of the flags for the link is the same as described + * on media_create_pad_link(), for pad to pad links or the same as described + * on media_create_intf_link(), for interface to entity links. + */ int media_entity_setup_link(struct media_link *link, u32 flags); + +/** + * media_entity_find_link - Find a link between two pads + * @source: Source pad + * @sink: Sink pad + * + * Return a pointer to the link between the two entities. If no such link + * exists, return NULL. + */ struct media_link *media_entity_find_link(struct media_pad *source, struct media_pad *sink); + +/** + * media_entity_remote_pad - Find the pad at the remote end of a link + * @pad: Pad at the local end of the link + * + * Search for a remote pad connected to the given pad by iterating over all + * links originating or terminating at that pad until an enabled link is found. + * + * Return a pointer to the pad at the remote end of the first found enabled + * link, or NULL if no enabled link has been found. + */ struct media_pad *media_entity_remote_pad(struct media_pad *pad); +/** + * media_entity_get - Get a reference to the parent module + * + * @entity: The entity + * + * Get a reference to the parent media device module. + * + * The function will return immediately if @entity is NULL. + * + * Return a pointer to the entity on success or NULL on failure. + */ struct media_entity *media_entity_get(struct media_entity *entity); + +__must_check int media_entity_graph_walk_init( + struct media_entity_graph *graph, struct media_device *mdev); + +/** + * media_entity_graph_walk_cleanup - Release resources used by graph walk. + * + * @graph: Media graph structure that will be used to walk the graph + */ +void media_entity_graph_walk_cleanup(struct media_entity_graph *graph); + +/** + * media_entity_put - Release the reference to the parent module + * + * @entity: The entity + * + * Release the reference count acquired by media_entity_get(). + * + * The function will return immediately if @entity is NULL. + */ void media_entity_put(struct media_entity *entity); +/** + * media_entity_graph_walk_start - Start walking the media graph at a given entity + * @graph: Media graph structure that will be used to walk the graph + * @entity: Starting entity + * + * Before using this function, media_entity_graph_walk_init() must be + * used to allocate resources used for walking the graph. This + * function initializes the graph traversal structure to walk the + * entities graph starting at the given entity. The traversal + * structure must not be modified by the caller during graph + * traversal. After the graph walk, the resources must be released + * using media_entity_graph_walk_cleanup(). + */ void media_entity_graph_walk_start(struct media_entity_graph *graph, - struct media_entity *entity); + struct media_entity *entity); + +/** + * media_entity_graph_walk_next - Get the next entity in the graph + * @graph: Media graph structure + * + * Perform a depth-first traversal of the given media entities graph. + * + * The graph structure must have been previously initialized with a call to + * media_entity_graph_walk_start(). + * + * Return the next entity in the graph or NULL if the whole graph have been + * traversed. + */ struct media_entity * media_entity_graph_walk_next(struct media_entity_graph *graph); + +/** + * media_entity_pipeline_start - Mark a pipeline as streaming + * @entity: Starting entity + * @pipe: Media pipeline to be assigned to all entities in the pipeline. + * + * Mark all entities connected to a given entity through enabled links, either + * directly or indirectly, as streaming. The given pipeline object is assigned to + * every entity in the pipeline and stored in the media_entity pipe field. + * + * Calls to this function can be nested, in which case the same number of + * media_entity_pipeline_stop() calls will be required to stop streaming. The + * pipeline pointer must be identical for all nested calls to + * media_entity_pipeline_start(). + */ __must_check int media_entity_pipeline_start(struct media_entity *entity, struct media_pipeline *pipe); + +/** + * media_entity_pipeline_stop - Mark a pipeline as not streaming + * @entity: Starting entity + * + * Mark all entities connected to a given entity through enabled links, either + * directly or indirectly, as not streaming. The media_entity pipe field is + * reset to NULL. + * + * If multiple calls to media_entity_pipeline_start() have been made, the same + * number of calls to this function are required to mark the pipeline as not + * streaming. + */ void media_entity_pipeline_stop(struct media_entity *entity); +/** + * media_devnode_create() - creates and initializes a device node interface + * + * @mdev: pointer to struct &media_device + * @type: type of the interface, as given by MEDIA_INTF_T_* macros + * as defined in the uapi/media/media.h header. + * @flags: Interface flags as defined in uapi/media/media.h. + * @major: Device node major number. + * @minor: Device node minor number. + * + * Return: if succeeded, returns a pointer to the newly allocated + * &media_intf_devnode pointer. + */ +struct media_intf_devnode * +__must_check media_devnode_create(struct media_device *mdev, + u32 type, u32 flags, + u32 major, u32 minor); +/** + * media_devnode_remove() - removes a device node interface + * + * @devnode: pointer to &media_intf_devnode to be freed. + * + * When a device node interface is removed, all links to it are automatically + * removed. + */ +void media_devnode_remove(struct media_intf_devnode *devnode); +struct media_link * + +/** + * media_create_intf_link() - creates a link between an entity and an interface + * + * @entity: pointer to %media_entity + * @intf: pointer to %media_interface + * @flags: Link flags, as defined in include/uapi/linux/media.h. + * + * + * Valid values for flags: + * The %MEDIA_LNK_FL_ENABLED flag indicates that the interface is connected to + * the entity hardware. That's the default value for interfaces. An + * interface may be disabled if the hardware is busy due to the usage + * of some other interface that it is currently controlling the hardware. + * A typical example is an hybrid TV device that handle only one type of + * stream on a given time. So, when the digital TV is streaming, + * the V4L2 interfaces won't be enabled, as such device is not able to + * also stream analog TV or radio. + * + * Note: + * + * Before calling this function, media_devnode_create() should be called for + * the interface and media_device_register_entity() should be called for the + * interface that will be part of the link. + */ +__must_check media_create_intf_link(struct media_entity *entity, + struct media_interface *intf, + u32 flags); +/** + * __media_remove_intf_link() - remove a single interface link + * + * @link: pointer to &media_link. + * + * Note: this is an unlocked version of media_remove_intf_link() + */ +void __media_remove_intf_link(struct media_link *link); + +/** + * media_remove_intf_link() - remove a single interface link + * + * @link: pointer to &media_link. + * + * Note: prefer to use this one, instead of __media_remove_intf_link() + */ +void media_remove_intf_link(struct media_link *link); + +/** + * __media_remove_intf_links() - remove all links associated with an interface + * + * @intf: pointer to &media_interface + * + * Note: this is an unlocked version of media_remove_intf_links(). + */ +void __media_remove_intf_links(struct media_interface *intf); + +/** + * media_remove_intf_links() - remove all links associated with an interface + * + * @intf: pointer to &media_interface + * + * Notes: + * + * this is called automatically when an entity is unregistered via + * media_device_register_entity() and by media_devnode_remove(). + * + * Prefer to use this one, instead of __media_remove_intf_links(). + */ +void media_remove_intf_links(struct media_interface *intf); + #define media_entity_call(entity, operation, args...) \ (((entity)->ops && (entity)->ops->operation) ? \ (entity)->ops->operation((entity) , ##args) : -ENOIOCTLCMD) diff --git a/include/media/tuner.h b/include/media/tuner.h index 486b6a5..e5321fd 100644 --- a/include/media/tuner.h +++ b/include/media/tuner.h @@ -21,6 +21,14 @@ #include <linux/videodev2.h> +/* Tuner PADs */ +/* FIXME: is this the right place for it? */ +enum tuner_pad_index { + TUNER_PAD_RF_INPUT, + TUNER_PAD_IF_OUTPUT, + TUNER_NUM_PADS +}; + #define ADDR_UNSET (255) #define TUNER_TEMIC_PAL 0 /* 4002 FH5 (3X 7756, 9483) */ diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h index acbcd2f..eeabf20 100644 --- a/include/media/v4l2-dev.h +++ b/include/media/v4l2-dev.h @@ -86,6 +86,7 @@ struct video_device { #if defined(CONFIG_MEDIA_CONTROLLER) struct media_entity entity; + struct media_intf_devnode *intf_devnode; #endif /* device ops */ const struct v4l2_file_operations *fops; diff --git a/include/uapi/linux/media.h b/include/uapi/linux/media.h index 4e816be..1e3c8cb 100644 --- a/include/uapi/linux/media.h +++ b/include/uapi/linux/media.h @@ -23,6 +23,9 @@ #ifndef __LINUX_MEDIA_H #define __LINUX_MEDIA_H +#ifndef __KERNEL__ +#include <stdint.h> +#endif #include <linux/ioctl.h> #include <linux/types.h> #include <linux/version.h> @@ -42,33 +45,107 @@ struct media_device_info { #define MEDIA_ENT_ID_FLAG_NEXT (1 << 31) +/* + * Initial value to be used when a new entity is created + * Drivers should change it to something useful + */ +#define MEDIA_ENT_F_UNKNOWN 0x00000000 + +/* + * Base number ranges for entity functions + * + * NOTE: those ranges and entity function number are phased just to + * make it easier to maintain this file. Userspace should not rely on + * the ranges to identify a group of function types, as newer + * functions can be added with any name within the full u32 range. + */ +#define MEDIA_ENT_F_BASE 0x00000000 +#define MEDIA_ENT_F_OLD_BASE 0x00010000 +#define MEDIA_ENT_F_OLD_SUBDEV_BASE 0x00020000 + +/* + * DVB entities + */ +#define MEDIA_ENT_F_DTV_DEMOD (MEDIA_ENT_F_BASE + 1) +#define MEDIA_ENT_F_TS_DEMUX (MEDIA_ENT_F_BASE + 2) +#define MEDIA_ENT_F_DTV_CA (MEDIA_ENT_F_BASE + 3) +#define MEDIA_ENT_F_DTV_NET_DECAP (MEDIA_ENT_F_BASE + 4) + +/* + * Connectors + */ +/* It is a responsibility of the entity drivers to add connectors and links */ +#define MEDIA_ENT_F_CONN_RF (MEDIA_ENT_F_BASE + 21) +#define MEDIA_ENT_F_CONN_SVIDEO (MEDIA_ENT_F_BASE + 22) +#define MEDIA_ENT_F_CONN_COMPOSITE (MEDIA_ENT_F_BASE + 23) +/* For internal test signal generators and other debug connectors */ +#define MEDIA_ENT_F_CONN_TEST (MEDIA_ENT_F_BASE + 24) + +/* + * I/O entities + */ +#define MEDIA_ENT_F_IO_DTV (MEDIA_ENT_F_BASE + 31) +#define MEDIA_ENT_F_IO_VBI (MEDIA_ENT_F_BASE + 32) +#define MEDIA_ENT_F_IO_SWRADIO (MEDIA_ENT_F_BASE + 33) + +/* + * Don't touch on those. The ranges MEDIA_ENT_F_OLD_BASE and + * MEDIA_ENT_F_OLD_SUBDEV_BASE are kept to keep backward compatibility + * with the legacy v1 API.The number range is out of range by purpose: + * several previously reserved numbers got excluded from this range. + * + * Subdevs are initialized with MEDIA_ENT_T_V4L2_SUBDEV_UNKNOWN, + * in order to preserve backward compatibility. + * Drivers must change to the proper subdev type before + * registering the entity. + */ + +#define MEDIA_ENT_F_IO_V4L (MEDIA_ENT_F_OLD_BASE + 1) + +#define MEDIA_ENT_F_CAM_SENSOR (MEDIA_ENT_F_OLD_SUBDEV_BASE + 1) +#define MEDIA_ENT_F_FLASH (MEDIA_ENT_F_OLD_SUBDEV_BASE + 2) +#define MEDIA_ENT_F_LENS (MEDIA_ENT_F_OLD_SUBDEV_BASE + 3) +#define MEDIA_ENT_F_ATV_DECODER (MEDIA_ENT_F_OLD_SUBDEV_BASE + 4) +/* + * It is a responsibility of the entity drivers to add connectors and links + * for the tuner entities. + */ +#define MEDIA_ENT_F_TUNER (MEDIA_ENT_F_OLD_SUBDEV_BASE + 5) + +#define MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN MEDIA_ENT_F_OLD_SUBDEV_BASE + +#ifndef __KERNEL__ + +/* + * Legacy symbols used to avoid userspace compilation breakages + * + * Those symbols map the entity function into types and should be + * used only on legacy programs for legacy hardware. Don't rely + * on those for MEDIA_IOC_G_TOPOLOGY. + */ #define MEDIA_ENT_TYPE_SHIFT 16 #define MEDIA_ENT_TYPE_MASK 0x00ff0000 #define MEDIA_ENT_SUBTYPE_MASK 0x0000ffff -#define MEDIA_ENT_T_DEVNODE (1 << MEDIA_ENT_TYPE_SHIFT) -#define MEDIA_ENT_T_DEVNODE_V4L (MEDIA_ENT_T_DEVNODE + 1) +#define MEDIA_ENT_T_DEVNODE MEDIA_ENT_F_OLD_BASE +#define MEDIA_ENT_T_DEVNODE_V4L MEDIA_ENT_F_IO_V4L #define MEDIA_ENT_T_DEVNODE_FB (MEDIA_ENT_T_DEVNODE + 2) #define MEDIA_ENT_T_DEVNODE_ALSA (MEDIA_ENT_T_DEVNODE + 3) -#define MEDIA_ENT_T_DEVNODE_DVB_FE (MEDIA_ENT_T_DEVNODE + 4) -#define MEDIA_ENT_T_DEVNODE_DVB_DEMUX (MEDIA_ENT_T_DEVNODE + 5) -#define MEDIA_ENT_T_DEVNODE_DVB_DVR (MEDIA_ENT_T_DEVNODE + 6) -#define MEDIA_ENT_T_DEVNODE_DVB_CA (MEDIA_ENT_T_DEVNODE + 7) -#define MEDIA_ENT_T_DEVNODE_DVB_NET (MEDIA_ENT_T_DEVNODE + 8) - -/* Legacy symbol. Use it to avoid userspace compilation breakages */ -#define MEDIA_ENT_T_DEVNODE_DVB MEDIA_ENT_T_DEVNODE_DVB_FE - -#define MEDIA_ENT_T_V4L2_SUBDEV (2 << MEDIA_ENT_TYPE_SHIFT) -#define MEDIA_ENT_T_V4L2_SUBDEV_SENSOR (MEDIA_ENT_T_V4L2_SUBDEV + 1) -#define MEDIA_ENT_T_V4L2_SUBDEV_FLASH (MEDIA_ENT_T_V4L2_SUBDEV + 2) -#define MEDIA_ENT_T_V4L2_SUBDEV_LENS (MEDIA_ENT_T_V4L2_SUBDEV + 3) -/* A converter of analogue video to its digital representation. */ -#define MEDIA_ENT_T_V4L2_SUBDEV_DECODER (MEDIA_ENT_T_V4L2_SUBDEV + 4) +#define MEDIA_ENT_T_DEVNODE_DVB (MEDIA_ENT_T_DEVNODE + 4) -#define MEDIA_ENT_T_V4L2_SUBDEV_TUNER (MEDIA_ENT_T_V4L2_SUBDEV + 5) +#define MEDIA_ENT_T_UNKNOWN MEDIA_ENT_F_UNKNOWN +#define MEDIA_ENT_T_V4L2_VIDEO MEDIA_ENT_F_IO_V4L +#define MEDIA_ENT_T_V4L2_SUBDEV MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN +#define MEDIA_ENT_T_V4L2_SUBDEV_SENSOR MEDIA_ENT_F_CAM_SENSOR +#define MEDIA_ENT_T_V4L2_SUBDEV_FLASH MEDIA_ENT_F_FLASH +#define MEDIA_ENT_T_V4L2_SUBDEV_LENS MEDIA_ENT_F_LENS +#define MEDIA_ENT_T_V4L2_SUBDEV_DECODER MEDIA_ENT_F_ATV_DECODER +#define MEDIA_ENT_T_V4L2_SUBDEV_TUNER MEDIA_ENT_F_TUNER +#endif +/* Entity flags */ #define MEDIA_ENT_FL_DEFAULT (1 << 0) +#define MEDIA_ENT_FL_CONNECTOR (1 << 1) struct media_entity_desc { __u32 id; @@ -151,6 +228,10 @@ struct media_pad_desc { #define MEDIA_LNK_FL_IMMUTABLE (1 << 1) #define MEDIA_LNK_FL_DYNAMIC (1 << 2) +#define MEDIA_LNK_FL_LINK_TYPE (0xf << 28) +# define MEDIA_LNK_FL_DATA_LINK (0 << 28) +# define MEDIA_LNK_FL_INTERFACE_LINK (1 << 28) + struct media_link_desc { struct media_pad_desc source; struct media_pad_desc sink; @@ -167,9 +248,120 @@ struct media_links_enum { __u32 reserved[4]; }; +/* Interface type ranges */ + +#define MEDIA_INTF_T_DVB_BASE 0x00000100 +#define MEDIA_INTF_T_V4L_BASE 0x00000200 + +/* Interface types */ + +#define MEDIA_INTF_T_DVB_FE (MEDIA_INTF_T_DVB_BASE) +#define MEDIA_INTF_T_DVB_DEMUX (MEDIA_INTF_T_DVB_BASE + 1) +#define MEDIA_INTF_T_DVB_DVR (MEDIA_INTF_T_DVB_BASE + 2) +#define MEDIA_INTF_T_DVB_CA (MEDIA_INTF_T_DVB_BASE + 3) +#define MEDIA_INTF_T_DVB_NET (MEDIA_INTF_T_DVB_BASE + 4) + +#define MEDIA_INTF_T_V4L_VIDEO (MEDIA_INTF_T_V4L_BASE) +#define MEDIA_INTF_T_V4L_VBI (MEDIA_INTF_T_V4L_BASE + 1) +#define MEDIA_INTF_T_V4L_RADIO (MEDIA_INTF_T_V4L_BASE + 2) +#define MEDIA_INTF_T_V4L_SUBDEV (MEDIA_INTF_T_V4L_BASE + 3) +#define MEDIA_INTF_T_V4L_SWRADIO (MEDIA_INTF_T_V4L_BASE + 4) + +/* + * MC next gen API definitions + * + * NOTE: The declarations below are close to the MC RFC for the Media + * Controller, the next generation. Yet, there are a few adjustments + * to do, as we want to be able to have a functional API before + * the MC properties change. Those will be properly marked below. + * Please also notice that I removed "num_pads", "num_links", + * from the proposal, as a proper userspace application will likely + * use lists for pads/links, just as we intend to do in Kernelspace. + * The API definition should be freed from fields that are bound to + * some specific data structure. + * + * FIXME: Currently, I opted to name the new types as "media_v2", as this + * won't cause any conflict with the Kernelspace namespace, nor with + * the previous kAPI media_*_desc namespace. This can be changed + * later, before the adding this API upstream. + */ + +#if 0 /* Let's postpone it to Kernel 4.6 */ +struct media_v2_entity { + __u32 id; + char name[64]; /* FIXME: move to a property? (RFC says so) */ + __u32 function; /* Main function of the entity */ + __u16 reserved[12]; +}; + +/* Should match the specific fields at media_intf_devnode */ +struct media_v2_intf_devnode { + __u32 major; + __u32 minor; +}; + +struct media_v2_interface { + __u32 id; + __u32 intf_type; + __u32 flags; + __u32 reserved[9]; + + union { + struct media_v2_intf_devnode devnode; + __u32 raw[16]; + }; +}; + +struct media_v2_pad { + __u32 id; + __u32 entity_id; + __u32 flags; + __u16 reserved[9]; +}; + +struct media_v2_link { + __u32 id; + __u32 source_id; + __u32 sink_id; + __u32 flags; + __u32 reserved[5]; +}; + +struct media_v2_topology { + __u64 topology_version; + + __u32 num_entities; + __u32 reserved1; + __u64 ptr_entities; + + __u32 num_interfaces; + __u32 reserved2; + __u64 ptr_interfaces; + + __u32 num_pads; + __u32 reserved3; + __u64 ptr_pads; + + __u32 num_links; + __u32 reserved4; + __u64 ptr_links; +}; + +static inline void __user *media_get_uptr(__u64 arg) +{ + return (void __user *)(uintptr_t)arg; +} +#endif + +/* ioctls */ + #define MEDIA_IOC_DEVICE_INFO _IOWR('|', 0x00, struct media_device_info) #define MEDIA_IOC_ENUM_ENTITIES _IOWR('|', 0x01, struct media_entity_desc) #define MEDIA_IOC_ENUM_LINKS _IOWR('|', 0x02, struct media_links_enum) #define MEDIA_IOC_SETUP_LINK _IOWR('|', 0x03, struct media_link_desc) +#if 0 /* Let's postpone it to Kernel 4.6 */ +#define MEDIA_IOC_G_TOPOLOGY _IOWR('|', 0x04, struct media_v2_topology) +#endif + #endif /* __LINUX_MEDIA_H */ |