<html xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word" xmlns:m="http://schemas.microsoft.com/office/2004/12/omml" xmlns="http://www.w3.org/TR/REC-html40">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=us-ascii">
<meta name="Generator" content="Microsoft Word 14 (filtered medium)">
<style><!--
/* Font Definitions */
@font-face
        {font-family:SimSun;
        panose-1:2 1 6 0 3 1 1 1 1 1;}
@font-face
        {font-family:SimSun;
        panose-1:2 1 6 0 3 1 1 1 1 1;}
@font-face
        {font-family:Calibri;
        panose-1:2 15 5 2 2 2 4 3 2 4;}
@font-face
        {font-family:SimSun;
        panose-1:2 1 6 0 3 1 1 1 1 1;}
/* Style Definitions */
p.MsoNormal, li.MsoNormal, div.MsoNormal
        {margin:0cm;
        margin-bottom:.0001pt;
        text-align:justify;
        text-justify:inter-ideograph;
        font-size:10.5pt;
        font-family:"Calibri","sans-serif";}
a:link, span.MsoHyperlink
        {mso-style-priority:99;
        color:blue;
        text-decoration:underline;}
a:visited, span.MsoHyperlinkFollowed
        {mso-style-priority:99;
        color:purple;
        text-decoration:underline;}
span.EmailStyle17
        {mso-style-type:personal-compose;
        font-family:"Calibri","sans-serif";
        color:windowtext;}
.MsoChpDefault
        {mso-style-type:export-only;
        font-family:"Calibri","sans-serif";}
/* Page Definitions */
@page WordSection1
        {size:612.0pt 792.0pt;
        margin:72.0pt 90.0pt 72.0pt 90.0pt;}
div.WordSection1
        {page:WordSection1;}
--></style><!--[if gte mso 9]><xml>
<o:shapedefaults v:ext="edit" spidmax="1026" />
</xml><![endif]--><!--[if gte mso 9]><xml>
<o:shapelayout v:ext="edit">
<o:idmap v:ext="edit" data="1" />
</o:shapelayout></xml><![endif]-->
</head>
<body lang="ZH-CN" link="blue" vlink="purple" style="text-justify-trim:punctuation">
<div class="WordSection1">
<p class="MsoNormal"><span lang="EN-US">This RFC is based on previous discussion to set up a generic communication channel between display and audio driver and<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">an internal design of Intel MCG/VPG HDMI audio driver. It's still an initial draft and your advice would be appreciated<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">to improve the design.<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">The basic idea is to create a new avsink module and let both drm and alsa depend on it.<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">This new module provides a framework and APIs for synchronization between the display and audio driver.
<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">1. Display/Audio Client<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">The avsink core provides APIs to create, register and lookup a display/audio client.<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">A specific display driver (eg. i915) or audio driver (eg. HD-Audio driver) can create a client, add some resources
<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">objects (shared power wells, display outputs, and audio inputs, register ops) to the client, and then register this
<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">client to avisink core. The peer driver can look up a registered client by a name or type, or both. If a client gives<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">a valid peer client name on registration, avsink core will bind the two clients as peer for each other. And we
<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">expect a display client and an audio client to be peers for each other in a system.<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">int avsink_new_client ( const char *name,
<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">                            int type,   /* client type, display or audio */<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">                            struct module *module,<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">                            void *context,<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">                            const char *peer_name,<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">                            struct avsink_client **client_ret);<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">int avsink_free_client (struct avsink_client *client);<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">int avsink_register_client(struct avsink_client *client);<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">int avisink_unregister_client(int client_handle);<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">struct avsink_client *avsink_lookup_client(const char *name, int type);<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">struct avsink_client {<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">         const char *name;  /* client name */<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">         int type; /* client type*/<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">         void *context;<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">         struct module *module;  /* top-level module for locking */<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">         <o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">         struct avsink_client *peer;      /* peer client */<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">         /* shared power wells */<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">         struct avsink_power_well *power_well; 
<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">         int num_power_wells;<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">         <o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">         /* endpoints, display outputs or audio inputs */<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">         struct avsink_endpoint * endpoint;<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">         int num_endpints;<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">         struct avsink_registers_ops *reg_ops; /* ops to access registers of a client */<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">         void *private_data;<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">         …<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">};       <o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">On system boots, the avsink module is loaded before the display and audio driver module. And the display and audio
<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">driver may be loaded on parallel. <o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">* If a specific display driver (eg. i915) supports avsink, it can create a display client, add power wells and display<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">  outputs to the client, and then register the display client to the avsink core. Then it may look up if there is any
<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">  audio client registered, by name or type, and may find an audio client registered by some audio driver.<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">* If an audio driver supports avsink, it usually should look up a registered display client by name or type at first,
<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">  because it may need the shared power well in GPU and check the display outputs’ name to bind the audio inputs. If
<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">  the display client is not registered yet, the audio driver can choose to wait (maybe in a work queue) or return
<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">  –EAGAIN for a deferred probe. After the display client is found, the audio driver can register an audio client with<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">  the display client’s name as the peer name, the avsink core will bind the display and audio clients to each other.<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">Open question:<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">If the display or audio driver is disabled by the black list, shall we introduce a time out to avoid waiting for the
<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">other client registered endlessly?<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">2. Shared power wells (optional)<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">The audio and display devices, maybe only part of them, may share a common power well (e.g. for Intel Haswell and
<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">Broadwell). If so, the driver that controls the power well should define a power well object, implement the get/put ops,<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">and add it to its avsink client before registering the client to avsink core. Then the peer client can look up this
<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">power well by its name, and get/put this power well as a user.
<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">A client can have multiple power well objects.<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">struct avsink_power_well {<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">         const char *name; /* name of the power well */<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">         void *context;   /* parameter of get/put ops, maybe device pointer for this power well */<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">         struct avsink_power_well_ops *ops<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">};<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">struct avsink_power_well_ops {<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">         int (*get)(void *context);<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">         int (*put)(void *context);<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">};<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">API:<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">int avsink_new_power(struct avsink_client *client,
<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">                   const char *power_name,<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">                   void * power_context,<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">                   struct avsink_power_well_ops *ops,<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">                   struct avsink_power_well **power_ret);<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">struct avsink_power_well *avisnk_lookup_power(const char *name);<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">int avsink_get_power(struct avsink_power_well *power);  /* Reqesut the power */<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">int avsink_put_power(struct avsink_power_well *power);    /* Release the power */<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">For example, the i915 display driver can create a device for the shared power well in Haswell GPU, implement its PM
<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">functions, and use the device pointer as the context when creating the power well object, like below
<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">struct avsink_power_well_ops i915_power_well_ops = {<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">         .get = pm_runtime_get_sync;<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">         .put = pm_runtime_put_sync;<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">};<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">...<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">avsink_new_power ( display_client,<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">                   "i915_display_power_well",<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">                   pdev,  /* pointer of the power well device */<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">                   &i915_power_well_ops,<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">                   …)<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">Power domain is not used here since a single device seems enough to represent a power well.
<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">3. Display output and audio input endpoints<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">A display client should register the display output endpoints and its audio peer client should register the audio input<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">endpoints. A client can have multiple endpoints. The avsink core will bind an audio input and a display output as peer
<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">to each other. This is to allow the audio and display driver to synchronize with each other for each display pipeline.<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">All endpoints should be added to a client before the client is registered to avsink core. Dynamic endpoints are not
<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">supported now.<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">A display out here represents a physical HDMI/DP output port. And as long as it’s usable in the system (i.e. physically<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">connected to the HDMP/DP port on the machine board), the display output should be registered not matter the port is
<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">connected to an external display device or not. And if HW and display driver can support DP1.2 daisy chain (multiple DP<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">display devices can be connected to a single port), multiple static displays outputs should be defined for the DP port
<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">according to the HW capability. The port & display device number can be indicated by the name (e.g. "i915_DDI_B",
<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">"i915_DDI_B_DEV0", "i915_DDI_B_DEV1", or "i915_DDI_B_DEV2"), defined by the display driver.<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">The audio driver can check the endpoints of its peer display client and use an display endpoint’s name, or a presumed
<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">display endpoint name, as peer name when registering an audio endpoint, thus the avsink core will bind the two display<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">and audio endpoints as peers.<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">struct avsink_endpoint {<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">         const char *name;  /*name of the endpoint */<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">         int type;            /* DISPLAY_OUTPUT or AUDIO_INPUT */<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">         void *context;           /* private data, used as parameter of the ops */<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">         struct avsink_endpoint_ops *ops;<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">         struct avsink_endpoint *peer; /* peer endpoint */<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">};<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">struct avsink_endpoint_ops {<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">         int (*get_caps) (enum had_caps_list query_element,<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">                            void *capabilities,<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">                            void *context);<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">         int (*set_caps) (enum had_caps_list set_element,<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">                            void *capabilities,<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">                            void *context);<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">         int (*event_handler) (enum avsink_event_type event_type, void *context);<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">};<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">API:<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">int avsink_new_endpoint (struct avsink_client *client,<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">                            const char *name,<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">                            int type, /* DISPLAY_OUTPUT or AUDIO_INPUT*/<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">                            void *context,<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">                            const char *peer_name, /* can be NULL if no clue */<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">                            avsink_endpoint_ops *ops,<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">                            struct avsink_endpoint **endpoint_ret);<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">int avsink_endpoint_get_caps(struct avsink_endpoint *endpoint,
<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">                            enum avsink_caps_list t get_element,
<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">                            void *capabilities);<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">int avsink_endpoint_set_caps(struct avsink_endpoint *endpoint,
<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">                            enum had_caps_list set_element,
<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">                            void *capabilities);<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">int avsink_endpoint_post_event(struct avsink_endpoint *endpoint,
<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">                            enum avsink_event_type event_type);<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">4. Get/Set caps on an endpoint<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">The display or audio driver can get or set capabilities on an endpoint. Depending on the capability ID, the avsink core<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">will call get_caps/set_caps ops of this endpoint, or call get_caps/set_caps ops of its peer endpoint and return the
<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">result to the caller.<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">enum avsink_caps_list {<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">         /* capabilities for display output endpoints */<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">         AVSINK_GET_DISPLAY_ELD = 1,<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">         AVSINK_GET_DISPLAY_TYPE, /* HDMI or DisplayPort */<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">         AVSINK_GET_DISPLAY_NAME, /* Hope to use display device name under /sys/class/drm, like “card0-DP-1”, for user<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">                                     * space to figure out which HDMI/DP output on the drm side corresponds to which audio
<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">                                       * stream device on the alsa side */<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">         AVSINK_GET_DISPLAY_SAMPLING_FREQ,      /* HDMI TMDS clock or DP link symbol clock, for audio driver to
<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">                                                         * program N value<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">                                                        */<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">         AVSINK_GET_DISPLAY_HDCP_STATUS,<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">         AVSINK_GET_DISPLAY_AUDIO_STATUS, /* Whether audio is enabled */
<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">         AVSINK_SET_DISPLAY_ENABLE_AUDIO, /* Enable audio */<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">         AVSINK_SET_DISPLAY_DISABLE_AUDIO,         /* Disable audio */<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">         AVSINK_SET_DISPLAY_ENABLE_AUDIO_INT, /* Enable audio interrupt */<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">         AVSINK_SET_DISPLAY_DISABLE_AUDIO_INT,         /* Disable audio interrupt */<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">         /* capabilities for audio input endpoints */<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">         AVSINK_GET_AUDIO_IS_BUSY,  /* Whether there is an active audio streaming */<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">         OTHERS_TBD,<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">};<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">For example, the audio driver can query ELD info on an audio input endpoint by using caps AVSINK_GET_DISPLAY_ELD, and<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">avsink core will call get_caps() on the peer display output endpoint and return the ELD info to the audio driver.<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">Some audio driver may only use part of these caps. E.g. HD-Audio driver can use bus commands instead of the ops to
<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">control the audio on gfx side, so it doesn’t use caps like ENABLE/DISABLE_AUDIO or ENABLE/DISABLE_AUDIO.
<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">When the display driver want to disable a display pipeline for hot-plug, mode change or power saving, it can use caps
<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">AVSINK_GET_AUDIO_IS_BUSY to check if the audio input is busy (active streaming) on this display pipeline. And if audio<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">is busy, the display driver can choose to wait or go ahead to disable display pipeline anyway. For the latter case, the<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">audio input endpoint will be notified by an event and should abort audio streaming.<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">5. Event handling of endpoints<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">A driver can post events on an endpoint. Depending on the event type, the avsink core will call the endpoint’s event
<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">handler or pass the event to its peer endpoint and trigger the peer’s event handler function if defined.<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">int avsink_endpoint_post_event(struct avsink_endpoint *endpoint,
<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">                                     enum avsink_event_type event_type);<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">Now we only defined event types which should be handled by the audio input endpoints. The event types can be extended<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">in the future.<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">enum avsink_event_type {<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">        AVSINK_EVENT_DISPLAY_DISABLE = 1,  /* The display pipeline is disabled for hot-plug, mode change or
<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">                                                         * suspend. Audio driver should stop any active streaming.<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">                                                        */<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">        AVSINK_EVENT_DISPLAY_ENABLE,                   /* The display pipeline is enabled after hot-plug, mode change or
<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">                                                         * resume. Audio driver can restore previously interrupted streaming
<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">                                                         */<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">        AVSINK_EVENT_DISPLAY_MODE_CHANGE,   /* Display mode change event. At this time, the new display mode is
<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">                                                         * configured but the display pipeline is not enabled yet. Audio driver<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">                                                        * can do some configurations such as programing N value */<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">        AVSINK_EVENT_DISPLAY_AUDIO_BUFFER_DONE,         /* Audio Buffer done interrupts. Only for audio drivers if DMA and
<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">                                                         * interrupt are handled by GPU<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">                                                        */<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">        AVSINK_EVENT_DISPLAY_AUDIO_BUFFER_UNDERRUN,       /* Audio Buffer under run interrupts. Only for audio drivers if
<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">                                                                  * DMA and interrupt are handled by GPU<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">                                                                 */<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">};<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">So for a display driver, it can post an event on a display output endpoint and get processed by the peer audio input
<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">endpoint. Or it can also directly post an event on a peer audio input endpoint, by using the 'peer' pointer on a
<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">display output endpoint. <o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">6. Display register operation (optional)<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">Some audio driver needs to access GPU audio registers. The register ops are provided by the peer display client.
<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">struct avsink_registers_ops {<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">         int (*read_register) (uint32_t reg_addr, uint32_t *data, void *context);<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">         int (*write_register) (uint32_t reg_addr, uint32_t data, void *context);<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">         int (*read_modify_register) (uint32_t reg_addr, uint32_t data, uint32_t mask, void *context);<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">int avsink_define_reg_ops (struct avsink_client *client, struct avsink_registers_ops *ops);<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">And avsink core provides API for the audio driver to access the display registers:<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">int avsink_read_display_register(struct avsink_client *client , uint32_t offset, uint32_t *data);<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">int avsink_write_display_register(struct avsink_client *client , uint32_t offset, uint32_t data);<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">int avsink_read_modify_display_register(struct avsink_client *client, uint32_t offset, uint32_t data, uint32_t mask);<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">If the client is an audio client, the avsink core will find it peer display client and call its register ops;
<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">and if the client is a display client, the avsink core will just call its own register ops.<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">Thanks<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">Mengdong<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
</div>
</body>
</html>