Android 9.0でデュアルmipiスクリーンを実現
27719 ワード
Androidでは、もともとmipi(primary display)、HDMI(external display)、wifi display、virtual displayの4つの画面がサポートされていますが、デュアルmipi画面はサポートされていません.デュアルmipiスクリーンを統合する必要がある場合は、外部の一般的な方法は2つあります.
1)1セットのマザーボードに2つのcpu、2つのandroidコードを使用し、その間に1つのUSBデータ線で接続し、2つのdisplay間のデータ相互作用を実現する.
2.)ブリッジチップを使用する.
第1の方法は,ソフト・ハードウェア上でかなりの煩雑さだけでなく,コストが極めて高く,明らかに採算に合わない.2つ目の方法は、マザーボードにこのブリッジ変換チップがあるかどうかを見る必要があります.なければ、仕方がありません.
現在、私が引き継いだこのプロジェクトでは、お客様はブリッジチップを持たないマザーボードの上に、2つのmipiスクリーンを統合して、同顕と異顕を行うことを要求しています.この要求と客観的な状況に対して、私はよく分析して発見して、実はこの需要は実現しにくいわけではありません.
まず、androidはlinuxカーネル上で実行されます.lcdを使用しても、最終的にlinuxカーネルに対応するのは、fb 0、fb 1のようなデバイスノードにすぎません.
これを理解すると、androidシステム自体にあるhdmiスクリーンのインタフェースを利用して、fb 0をメインスクリーンに対応させ、fb 1をサブスクリーン、つまり2枚目のmipiスクリーンに対応させることができます.
次に具体的なコード実装についてお話しします.私のこのプロジェクトは高通プラットフォーム8953チップに基づいて行われています.高通の駆動コードはdtsiファイルで構成されています.対応するdtsiファイルはmsm 8953-mdssです.dtsi.このファイルではlcdのドライバを構成しています.例:
ここで、fb 0、fb 1、fb 2を配置した.Androidのデフォルトは1つの画面しかないので、デフォルトではfb 1はwifi画面、すなわちmdss_に割り当てられています.fb_wfd.今、私たちの2番目のスクリーン、すなわちfb 1は2番目のmipiスクリーンを作るために使用されるので、ここで変更します.もちろんここは直さないとカーネルコードにもmdss_dsi 1は、2番目のスクリーンの駆動を強引に指し、fb 1に対応する.しかし、私たちがソフトウェアを作っているのは、筋道がはっきりしていることにこだわっています.ここを直さないと、かなり違和感があるように見えますが、fb 1はwifi画面を指しているのに、なぜ実際に対応しているのが副画面なのでしょうか.
ここで変更する必要があるのはmdssです.dsi: qcom,mdss_dsi@0このコードには、hw-config=「dual_dsi」を付けます.デュアルスクリーンを作ることを示します.またsdm 450-qrd-sku 4.dtsiこのファイルにはmdss_dsi0、mdss_dsi 1は対応するlcdメーカーの駆動とgpioポートを配置し、もちろんこれらはmsm 8953-mdssに直接使用することもできる.dtsiというファイルの構成は、同じです.
私たちは同じmipiスクリーンを2枚持っているのでdsi_hx8394f_720p_ビデオとdsi_hx8394f_720p_dsi1_ビデオのコードは、ほとんど差がありません.dsi_でhx8394f_720p_ビデオは、dsi-panel-hx 8394 f-720 p-videoに対応するファイルである.dtsi
gpioピンmdss_dsi_active、mdss_dsi1_Active等はmsm 8953-pincterlに定義する.dtsi里
駆動上の構成は,しばらくここまで書き,hardware層の論理について述べる.メイン画面の作成はhardwareqcomdisplaysdmlibshwc 2hwc_session.cppというファイルのHWCSsion::Init()関数で行います.コードは次のとおりです.
ここでメインスクリーンのdisplayが作成されると、frameworksativeservicessurfaceflingerSurfaceFlingerにホットダイヤルイベントが送信されます.cppというファイルのprocessDisplayHotplugEvents()です.次に、対応する論理スクリーンが作成され、次のコードで保存されます.
将来、サブスクリーンを作成するときは、最終的にはここに呼び出されます.
メインスクリーンの作成が完了すると、2番目のmipiスクリーンの作成を開始することを考慮します.前述したように、mipiスクリーンでもhdmiスクリーンでもlinux層に対応してfb 0、fb 1のようなデバイスノードなので、上位層のインタフェースは借りることができます.
通常、電源を入れた後、hdmi画面が挿入されたとき、hardwareqcomdisplaysdmlibshwc 2hwc_session.cppのUeventHandler関数では、「change@/devices/virtual/switch/hdmi」というイベントが受信され、HotPlugHandler(connected)関数を呼び出して接続が開始されます.私たちは本当にhdmiスクリーンを持っていないので、私たちはこのことを受け取ることができません.hdmi挿入イベントの代わりに、「remove@/devices/platform/soc/1 de 0000.qcom,venus/firmware/venus.mdt」というグラフィックサブシステムイベントを一時的に使用することができます.コードは次のとおりです.
int HWCSsion::HotPlugHandler(bool connected)という関数を見てみましょう.
ConnectDisplayを見てみましょう
CreateExternalDisplayにフォローしてみましょう
HWCDisplayExternal::Create、hardwareqcomdisplaysdmlibshwc 2hwc_display_external.cppでは、
またhwcにフォローしますdisplay_external->Init();ここではhardwareqcomdisplaysdmlibshwc 2hwc_に定義されていますdisplay.cppでは、コードは以下の通りです.
このcore_intf_CoreInterfaceタイプですが、CoreImplクラスはそれを継承しています.ここのCreateDisplayは実際にhardwareqcomdisplaysdmlibscorecore_に呼び出されています.impl.cppのCreateDisplayでは、対応するコードは以下の通りです.
なお、本来の流れでは、CreateDisplayでタイプがkHDMIと判定された場合、DisplayHDMIが呼び出されます.しかし、私たちのはhdmiスクリーンではなくmipiスクリーンなので、ここには行けません.ここからprimary画面と同じ流れになりますが、伝わるidが違うだけです.次の点に注意してください.
display_base = new DisplayPrimary(event_handler, hw_info_intf_, buffer_sync_handler_, buffer_allocator_, &comp_mgr_, kHDMI);
もともとDisplayPrimaryのコンストラクション関数には、このidは付いていませんが、私たちは区別するために、新しいコンストラクション関数を追加して、私たちのidを伝えました.コードを見続けます:
hardware\qcom\display\sdm\libs\core\display_primary.cpp
hardware\qcom\display\sdm\libs\core\hw_interface.cpp
ここでは、kHDMIというidを伝達するためのHWPrimaryの構造関数が新たに追加され、以下に定義されています.
hardware\qcom\display\sdm\libs\core\fb\hw_primary.cpp
上の1に注意してHWDeviceのfb_に伝えましたnode_index_,この場所が最も重要であり、最終lcdがどのデバイスノードに対応するか、fb 0に対応するか、fb 1に対応するかに関係する.私がここに伝えた1は,対応するfb 1ノードを表す.詳細はコードを参照してください:
hardware\qcom\display\sdm\libs\core\fb\hw_device.cpp
ここにも構造関数HWDeviceが追加され、デフォルトのfb_node_index_-1で、私たちの副画面が伝わったのは1です.fb_node_index_-1の場合、initでGetFBNodeIndex(device_type_)を通過します.正しいidを検索するには、デフォルトのPrimary displayは0、サブスクリーンは1です.私たちが伝えたdeviceのためですtype_いずれもkDevicePrimaryで、GetFBNodeIndexでidを取ると正しく取れません.したがって、2番目の画面のidを1に直接指定します.次にinitで、次のコードを使用します.
対応するデバイスノードを取得します.メイン画面は/dev/graphics/fb 0"、"/dev/fb 0"、サブ画面は/dev/graphics/fb 1"、"/dev/fb 1"で、initの最後にdevice_を呼び出します.fd_ = Sys::open_(dev_name, O_RDWR);で開きます.正常に開くと、対応する画面が起動します.
ここまで、すべての変更が完了しました.すべてが正常であれば、サブスクリーンにはメインスクリーンと同じ内容、すなわち同表示が表示されます.このときPresentationクラスが呼び出され、display idが1と指定されると、サブ画面に異なるコンテンツ、すなわち異視が表示されます.
1)1セットのマザーボードに2つのcpu、2つのandroidコードを使用し、その間に1つのUSBデータ線で接続し、2つのdisplay間のデータ相互作用を実現する.
2.)ブリッジチップを使用する.
第1の方法は,ソフト・ハードウェア上でかなりの煩雑さだけでなく,コストが極めて高く,明らかに採算に合わない.2つ目の方法は、マザーボードにこのブリッジ変換チップがあるかどうかを見る必要があります.なければ、仕方がありません.
現在、私が引き継いだこのプロジェクトでは、お客様はブリッジチップを持たないマザーボードの上に、2つのmipiスクリーンを統合して、同顕と異顕を行うことを要求しています.この要求と客観的な状況に対して、私はよく分析して発見して、実はこの需要は実現しにくいわけではありません.
まず、androidはlinuxカーネル上で実行されます.lcdを使用しても、最終的にlinuxカーネルに対応するのは、fb 0、fb 1のようなデバイスノードにすぎません.
これを理解すると、androidシステム自体にあるhdmiスクリーンのインタフェースを利用して、fb 0をメインスクリーンに対応させ、fb 1をサブスクリーン、つまり2枚目のmipiスクリーンに対応させることができます.
次に具体的なコード実装についてお話しします.私のこのプロジェクトは高通プラットフォーム8953チップに基づいて行われています.高通の駆動コードはdtsiファイルで構成されています.対応するdtsiファイルはmsm 8953-mdssです.dtsi.このファイルではlcdのドライバを構成しています.例:
mdss_fb0: qcom,mdss_fb_primary {
cell-index = <0>;
compatible = "qcom,mdss-fb";
qcom,cont-splash-memory {
linux,contiguous-region = ;
};
};
mdss_fb2: qcom,mdss_fb_wfd {
cell-index = <2>;
compatible = "qcom,mdss-fb";
};
mdss_fb1: qcom,mdss_fb_secondary {
cell-index = <1>;
compatible = "qcom,mdss-fb";
};
};
qcom,mdss-fb-map-prim = ;
qcom,mdss-fb-map-sec = ;
mdss_dsi0: qcom,mdss_dsi_ctrl0@1a94000 {
compatible = "qcom,mdss-dsi-ctrl";
label = "MDSS DSI CTRL->0";
qcom,display-id = "primary";
cell-index = <0>;
reg = <0x1a94000 0x400>,
<0x1a94400 0x580>,
<0x193e000 0x30>;
reg-names = "dsi_ctrl", "dsi_phy", "mmss_misc_phys";
qcom,timing-db-mode;
qcom,mdss-mdp = ;
vdd-supply = ;
vddio-supply = ;
clocks = ,
,
,
,
,
,
,
,
,
,
;
clock-names = "byte_clk", "pixel_clk", "core_clk",
"byte_clk_rcg", "pixel_clk_rcg",
"pll_byte_clk_mux", "pll_pixel_clk_mux",
"pll_byte_clk_src", "pll_pixel_clk_src",
"pll_shadow_byte_clk_src",
"pll_shadow_pixel_clk_src";
qcom,platform-strength-ctrl = [ff 06
ff 06
ff 06
ff 06
ff 00];
qcom,platform-regulator-settings = [1d
1d 1d 1d 1d];
qcom,platform-lane-config = [00 00 10 0f
00 00 10 0f
00 00 10 0f
00 00 10 0f
00 00 10 8f];
};
mdss_dsi1: qcom,mdss_dsi_ctrl1@1a96000 {
compatible = "qcom,mdss-dsi-ctrl";
label = "MDSS DSI CTRL->1";
qcom,display-id = "secondary";
cell-index = <1>;
reg = <0x1a96000 0x400>,
<0x1a96400 0x588>,
<0x193e000 0x30>;
reg-names = "dsi_ctrl", "dsi_phy", "mmss_misc_phys";
qcom,mdss-mdp = ;
vdd-supply = ;
vddio-supply = ;
clocks = ,
,
,
,
,
,
,
,
,
,
;
clock-names = "byte_clk", "pixel_clk", "core_clk",
"byte_clk_rcg", "pixel_clk_rcg",
"pll_byte_clk_mux", "pll_pixel_clk_mux",
"pll_byte_clk_src", "pll_pixel_clk_src",
"pll_shadow_byte_clk_src",
"pll_shadow_pixel_clk_src";
qcom,timing-db-mode;
qcom,platform-strength-ctrl = [ff 06
ff 06
ff 06
ff 06
ff 00];
qcom,platform-regulator-settings = [1d
1d 1d 1d 1d];
qcom,platform-lane-config = [00 00 10 0f
00 00 10 0f
00 00 10 0f
00 00 10 0f
00 00 10 8f];
};
};
ここで、fb 0、fb 1、fb 2を配置した.Androidのデフォルトは1つの画面しかないので、デフォルトではfb 1はwifi画面、すなわちmdss_に割り当てられています.fb_wfd.今、私たちの2番目のスクリーン、すなわちfb 1は2番目のmipiスクリーンを作るために使用されるので、ここで変更します.もちろんここは直さないとカーネルコードにもmdss_dsi 1は、2番目のスクリーンの駆動を強引に指し、fb 1に対応する.しかし、私たちがソフトウェアを作っているのは、筋道がはっきりしていることにこだわっています.ここを直さないと、かなり違和感があるように見えますが、fb 1はwifi画面を指しているのに、なぜ実際に対応しているのが副画面なのでしょうか.
ここで変更する必要があるのはmdssです.dsi: qcom,mdss_dsi@0このコードには、hw-config=「dual_dsi」を付けます.デュアルスクリーンを作ることを示します.またsdm 450-qrd-sku 4.dtsiこのファイルにはmdss_dsi0、mdss_dsi 1は対応するlcdメーカーの駆動とgpioポートを配置し、もちろんこれらはmsm 8953-mdssに直接使用することもできる.dtsiというファイルの構成は、同じです.
&mdss_dsi0 {
status = "ok";
lab-supply = ;
ibb-supply = ;
/delete-property/ vdd-supply;
qcom,dsi-pref-prim-pan = ;//
/delete-property/ qcom,platform-bklight-en-gpio;
pinctrl-names = "mdss_default", "mdss_sleep";
pinctrl-0 = ;//gpio
pinctrl-1 = ;//gpio
qcom,platform-te-gpio = ;
qcom,platform-reset-gpio = ;
};
&mdss_dsi1 {
status = "ok";
//lab-supply = ;
//ibb-supply = ;
/delete-property/ vdd-supply;
qcom,dsi-pref-prim-pan = ;//
/delete-property/ qcom,platform-bklight-en-gpio;
pinctrl-names = "mdss_default", "mdss_sleep";
pinctrl-0 = ;//gpio
pinctrl-1 = ;//gpio
qcom,bridge-index = <0>;
qcom,pluggable;
qcom,platform-te-gpio = ;
qcom,platform-reset-gpio = ;
};
私たちは同じmipiスクリーンを2枚持っているのでdsi_hx8394f_720p_ビデオとdsi_hx8394f_720p_dsi1_ビデオのコードは、ほとんど差がありません.dsi_でhx8394f_720p_ビデオは、dsi-panel-hx 8394 f-720 p-videoに対応するファイルである.dtsi
&mdss_mdp {
dsi_hx8394f_720p_video: qcom,mdss_dsi_hx8394f_720p_video {
qcom,mdss-dsi-panel-name = "hx8394f 720p video mode dsi panel";
qcom,mdss-dsi-panel-controller = ;
qcom,mdss-dsi-panel-type = "dsi_video_mode";
qcom,mdss-dsi-panel-destination = "display_1";
qcom,mdss-dsi-panel-framerate = <60>;
qcom,mdss-dsi-virtual-channel-id = <0>;
qcom,mdss-dsi-stream = <0>;
qcom,mdss-dsi-panel-width = <720>;
qcom,mdss-dsi-panel-height = <1280>;
qcom,mdss-dsi-h-front-porch = <100>;
qcom,mdss-dsi-h-back-porch = <300>;
qcom,mdss-dsi-h-pulse-width = <2>;
qcom,mdss-dsi-h-sync-skew = <0>;
qcom,mdss-dsi-v-back-porch = <15>;
qcom,mdss-dsi-v-front-porch = <25>;
qcom,mdss-dsi-v-pulse-width = <4>;
qcom,mdss-dsi-h-left-border = <0>;
qcom,mdss-dsi-h-right-border = <0>;
qcom,mdss-dsi-v-top-border = <0>;
qcom,mdss-dsi-v-bottom-border = <0>;
qcom,mdss-dsi-bpp = <24>;
qcom,mdss-dsi-color-order = "rgb_swap_rgb";
qcom,mdss-dsi-underflow-color = <0xff>;
qcom,mdss-dsi-border-color = <0>;
qcom,mdss-dsi-on-command = [39 01 00 00 00 00 04 B9 FF 83 94
39 01 00 00 00 00 07 BA 63 03 68 6B B2 C0
39 01 00 00 00 00 0B B1 50 12 72 09 33 54 B1 31 6B 2F
39 01 00 00 00 00 07 B2 00 80 64 0E 0D 2F
39 01 00 00 00 00 16 B4 73 74 73 74 73 74 01 0C 86 75 00 3F 73 74 73 74 73 74 01 0C 86
39 01 00 00 00 00 22 D3 00 00 07 07 40 07 10 00 08 10 08 00 08 54 15 0E 05 0E 02 15 06 05 06 47 44 0A 0A 4B 10 07 07 0E 40
39 01 00 00 00 00 2D D5 1A 1A 1B 1B 00 01 02 03 04 05 06 07 08 09 0A 0B 24 25 18 18 26 27 18 18 18 18 18 18 18 18 18 18 18 18 18 18 18 18 20 21 18 18 18 18
39 01 00 00 00 00 2D D6 1A 1A 1B 1B 0B 0A 09 08 07 06 05 04 03 02 01 00 21 20 18 18 27 26 18 18 18 18 18 18 18 18 18 18 18 18 18 18 18 18 25 24 18 18 18 18
39 01 00 00 00 00 3B E0 00 0C 19 20 23 26 29 28 51 61 70 6F 76 86 89 8D 99 9A 95 A1 B0 57 55 58 5C 5e 64 6B 7F 00 0C 19 20 23 26 29 28 51 61 70 6F 76 86 89 8D 99 9A 95 A1 B0 57 55 58 5C 5E 64 6B 7F
39 01 00 00 00 00 03 C0 1F 31
15 01 00 00 00 00 02 CC 0B
15 01 00 00 00 00 02 D4 02
15 01 00 00 00 00 02 BD 02
39 01 00 00 00 00 0D D8 FF FF FF FF FF FF FF FF FF FF FF FF
15 01 00 00 00 00 02 BD 00
15 01 00 00 00 00 02 BD 01
15 01 00 00 00 00 02 B1 00
15 01 00 00 00 00 02 BD 00
39 01 00 00 00 00 08 BF 40 81 50 00 1A FC 01
39 01 00 00 00 00 03 B6 7D 7D
05 01 00 00 78 00 02 11 00
39 01 00 00 00 00 0D B2 00 80 64 0E 0D 2F 00 00 00 00 C0 18
05 01 00 00 14 00 02 29 00];
qcom,mdss-dsi-off-command = [05 01 00 00 78 00 02 28 00
05 01 00 00 96 00 02 10 00];
qcom,mdss-dsi-on-command-state = "dsi_lp_mode";
qcom,mdss-dsi-off-command-state = "dsi_hs_mode";
qcom,mdss-dsi-h-sync-pulse = <1>;
qcom,mdss-dsi-traffic-mode = "burst_mode";
qcom,mdss-dsi-lane-map = "lane_map_0123";
qcom,mdss-dsi-bllp-eof-power-mode;
qcom,mdss-dsi-bllp-power-mode;
qcom,mdss-dsi-lane-0-state;
qcom,mdss-dsi-lane-1-state;
qcom,mdss-dsi-lane-2-state;
qcom,mdss-dsi-lane-3-state;
qcom,mdss-dsi-panel-timings = [1F 10 05 06 03 1F 1C 05 06 03 02 04];
qcom,mdss-dsi-t-clk-post = <0x0B>;
qcom,mdss-dsi-t-clk-pre = <0x22>;
qcom,mdss-dsi-bl-min-level = <1>;
qcom,mdss-dsi-bl-max-level = <255>;
qcom,mdss-dsi-dma-trigger = "trigger_sw";
qcom,mdss-dsi-mdp-trigger = "none";
qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_pwm";
qcom,mdss-dsi-reset-sequence = <1 80>, <0 80>, <1 80>;
};
};
gpioピンmdss_dsi_active、mdss_dsi1_Active等はmsm 8953-pincterlに定義する.dtsi里
pmx_mdss: pmx_mdss {
mdss_dsi_active: mdss_dsi_active {
mux {
pins = "gpio61", "gpio59";
function = "gpio";
};
config {
pins = "gpio61", "gpio59";
drive-strength = <8>; /* 8 mA */
bias-disable = <0>; /* no pull */
output-high;
};
};
mdss_dsi_suspend: mdss_dsi_suspend {
mux {
pins = "gpio61", "gpio59";
function = "gpio";
};
config {
pins = "gpio61", "gpio59";
drive-strength = <2>; /* 2 mA */
bias-pull-down; /* pull down */
};
};
};
pmx_mdss1: pmx_mdss1 {
mdss_dsi1_active: mdss_dsi1_active {
mux {
pins = "gpio60";
function = "gpio";
};
config {
pins = "gpio60";
drive-strength = <8>; /* 8 mA */
bias-disable = <0>; /* no pull */
output-high;
};
};
mdss_dsi1_suspend: mdss_dsi1_suspend {
mux {
pins = "gpio60";
function = "gpio";
};
config {
pins = "gpio60";
drive-strength = <2>; /* 2 mA */
bias-pull-down; /* pull down */
};
};
};
駆動上の構成は,しばらくここまで書き,hardware層の論理について述べる.メイン画面の作成はhardwareqcomdisplaysdmlibshwc 2hwc_session.cppというファイルのHWCSsion::Init()関数で行います.コードは次のとおりです.
// Create and power on primary display
status = HWCDisplayPrimary::Create(core_intf_, &buffer_allocator_, &callbacks_, qservice_,
&hwc_display_[HWC_DISPLAY_PRIMARY]);
color_mgr_ = HWCColorManager::CreateColorManager(&buffer_allocator_);
ここでメインスクリーンのdisplayが作成されると、frameworksativeservicessurfaceflingerSurfaceFlingerにホットダイヤルイベントが送信されます.cppというファイルのprocessDisplayHotplugEvents()です.次に、対応する論理スクリーンが作成され、次のコードで保存されます.
if (event.connection == HWC2::Connection::Connected) {
if (!mBuiltinDisplays[displayType].get()) {
ALOGV("Creating built in display %d", displayType);
mBuiltinDisplays[displayType] = new BBinder();
// All non-virtual displays are currently considered secure.
DisplayDeviceState info(displayType, true);
info.displayName = displayType == DisplayDevice::DISPLAY_PRIMARY ?
"Built-in Screen" : "External Screen";
mCurrentState.displays.add(mBuiltinDisplays[displayType], info);
mInterceptor->saveDisplayCreation(info);
}
}
将来、サブスクリーンを作成するときは、最終的にはここに呼び出されます.
メインスクリーンの作成が完了すると、2番目のmipiスクリーンの作成を開始することを考慮します.前述したように、mipiスクリーンでもhdmiスクリーンでもlinux層に対応してfb 0、fb 1のようなデバイスノードなので、上位層のインタフェースは借りることができます.
通常、電源を入れた後、hdmi画面が挿入されたとき、hardwareqcomdisplaysdmlibshwc 2hwc_session.cppのUeventHandler関数では、「change@/devices/virtual/switch/hdmi」というイベントが受信され、HotPlugHandler(connected)関数を呼び出して接続が開始されます.私たちは本当にhdmiスクリーンを持っていないので、私たちはこのことを受け取ることができません.hdmi挿入イベントの代わりに、「remove@/devices/platform/soc/1 de 0000.qcom,venus/firmware/venus.mdt」というグラフィックサブシステムイベントを一時的に使用することができます.コードは次のとおりです.
#define HWC_UEVENT_SWITCH_TEST "remove@/devices/platform/soc/1de0000.qcom,venus/firmware/venus.mdt"
void HWCSession::UEventHandler(const char *uevent_data, int length) {
DLOGI("UEventHandler uevent_data = %s
", uevent_data);
if (!strcasecmp(uevent_data, HWC_UEVENT_SWITCH_TEST)) {
// if (!strcasecmp(uevent_data, HWC_UEVENT_SWITCH_HDMI)) {
int connected = 1;//GetEventValue(uevent_data, length, "SWITCH_STATE=");
if (connected >= 0) {
DLOGI("HDMI = %s
", connected ? "connected" : "disconnected");
if (HotPlugHandler(connected) == -1) {
DLOGE("Failed handling Hotplug = %s
", connected ? "connected" : "disconnected");
}
}
} else if (!strcasecmp(uevent_data, HWC_UEVENT_GRAPHICS_FB0)) {
DLOGI("UEventHandler Uevent FB0 = %s
", uevent_data);
int panel_reset = GetEventValue(uevent_data, length, "PANEL_ALIVE=");
if (panel_reset == 0) {
Refresh(0);
reset_panel_ = true;
}
}
}
int HWCSsion::HotPlugHandler(bool connected)という関数を見てみましょう.
int HWCSession::HotPlugHandler(bool connected) {
int status = 0;
bool notify_hotplug = false;
// To prevent sending events to client while a lock is held, acquire scope locks only within
// below scope so that those get automatically unlocked after the scope ends.
do {
// If HDMI is primary but not created yet (first time), create it and notify surfaceflinger.
// if it is already created, but got disconnected/connected again,
// just toggle display status and do not notify surfaceflinger.
// If HDMI is not primary, create/destroy external display normally.
if (hdmi_is_primary_) {
SCOPE_LOCK(locker_[HWC_DISPLAY_PRIMARY]);
if (hwc_display_[HWC_DISPLAY_PRIMARY]) {
status = hwc_display_[HWC_DISPLAY_PRIMARY]->SetState(connected);
} else {
status = CreateExternalDisplay(HWC_DISPLAY_PRIMARY);
notify_hotplug = true;
}
break;
}
{
SCOPE_LOCK(locker_[HWC_DISPLAY_PRIMARY]);
// Primary display must be connected for HDMI as secondary cases.
// ,
if (!hwc_display_[HWC_DISPLAY_PRIMARY]) {
DLOGE("xuhui Primary display is not connected.
");
return -1;
}
}
if (connected) {
SCOPE_LOCK(locker_[HWC_DISPLAY_EXTERNAL]);
Locker::ScopeLock lock_v(locker_[HWC_DISPLAY_VIRTUAL]);
// Connect external display if virtual display is not connected.
// Else, defer external display connection and process it when virtual display
// tears down; Do not notify SurfaceFlinger since connection is deferred now.
if (!hwc_display_[HWC_DISPLAY_EXTERNAL]) {
// 。
status = ConnectDisplay(HWC_DISPLAY_EXTERNAL);
DLOGE("xuhui HWCSession::HotPlugHandler status is %d
", status);
if (status) {
return status;
}
notify_hotplug = true;
} else {
DLOGI("Virtual display is connected, pending connection
");
external_pending_connect_ = true;
}
} else {
SEQUENCE_WAIT_SCOPE_LOCK(locker_[HWC_DISPLAY_EXTERNAL]);
// Do not return error if external display is not in connected status.
// Due to virtual display concurrency, external display connection might be still pending
// but hdmi got disconnected before pending connection could be processed.
if (hwc_display_[HWC_DISPLAY_EXTERNAL]) {
status = DisconnectDisplay(HWC_DISPLAY_EXTERNAL);
notify_hotplug = true;
}
external_pending_connect_ = false;
}
} while (0);
if (connected) {
Refresh(0);
if (!hdmi_is_primary_) {
// wait for sufficient time to ensure sufficient resources are available to process new
// new display connection.
uint32_t vsync_period = UINT32(GetVsyncPeriod(HWC_DISPLAY_PRIMARY));
usleep(vsync_period * 2 / 1000);
}
DLOGE("xuhui HWCSession::HotPlugHandler 6
");
}
// Cache hotplug for external till first present is called
if (notify_hotplug) {
if (!hdmi_is_primary_) {
if (!first_commit_) {
notify_hotplug = false;
external_pending_hotplug_ = connected;
}
}
}
// notify client
// , framework
if (notify_hotplug) {
HotPlug(hdmi_is_primary_ ? HWC_DISPLAY_PRIMARY : HWC_DISPLAY_EXTERNAL,
connected ? HWC2::Connection::Connected : HWC2::Connection::Disconnected);
}
qservice_->onHdmiHotplug(INT(connected));
return 0;
}
ConnectDisplayを見てみましょう
int32_t HWCSession::ConnectDisplay(int disp) {
int status = 0;
uint32_t primary_width = 0;
uint32_t primary_height = 0;
hwc_display_[HWC_DISPLAY_PRIMARY]->GetFrameBufferResolution(&primary_width, &primary_height);
if (disp == HWC_DISPLAY_EXTERNAL) {
// ,
status = CreateExternalDisplay(disp, primary_width, primary_height);
} else {
DLOGE("Invalid display type");
return -1;
}
if (!status) {
hwc_display_[disp]->SetSecureDisplay(secure_display_active_);
}
return status;
}
CreateExternalDisplayにフォローしてみましょう
int HWCSession::CreateExternalDisplay(int disp, uint32_t primary_width,
uint32_t primary_height, bool use_primary_res) {
uint32_t panel_bpp = 0;
uint32_t pattern_type = 0;
if (qdutils::isDPConnected()) {
qdutils::getDPTestConfig(&panel_bpp, &pattern_type);
}
if (panel_bpp && pattern_type) {
return HWCDisplayExternalTest::Create(core_intf_, &buffer_allocator_, &callbacks_,
qservice_, panel_bpp,
pattern_type, &hwc_display_[disp]);
}
if (use_primary_res) {
return HWCDisplayExternal::Create(core_intf_, &buffer_allocator_, &callbacks_,
primary_width, primary_height, qservice_,
use_primary_res, &hwc_display_[disp]);
} else {
//
return HWCDisplayExternal::Create(core_intf_, &buffer_allocator_, &callbacks_,
qservice_, &hwc_display_[disp]);
}
}
HWCDisplayExternal::Create、hardwareqcomdisplaysdmlibshwc 2hwc_display_external.cppでは、
int HWCDisplayExternal::Create(CoreInterface *core_intf, HWCBufferAllocator *buffer_allocator,
HWCCallbacks *callbacks, qService::QService *qservice,
HWCDisplay **hwc_display) {
return Create(core_intf, buffer_allocator, callbacks, 0, 0, qservice, false, hwc_display);
}
int HWCDisplayExternal::Create(CoreInterface *core_intf, HWCBufferAllocator *buffer_allocator,
HWCCallbacks *callbacks,
uint32_t primary_width, uint32_t primary_height,
qService::QService *qservice, bool use_primary_res,
HWCDisplay **hwc_display) {
uint32_t external_width = 0;
uint32_t external_height = 0;
DisplayError error = kErrorNone;
HWCDisplay *hwc_display_external = new HWCDisplayExternal(core_intf, buffer_allocator, callbacks,
qservice);
int status = hwc_display_external->Init();
......
return status;
}
またhwcにフォローしますdisplay_external->Init();ここではhardwareqcomdisplaysdmlibshwc 2hwc_に定義されていますdisplay.cppでは、コードは以下の通りです.
int HWCDisplay::Init() {
......
DisplayError error = core_intf_->CreateDisplay(type_, this, &display_intf_);
......
}
このcore_intf_CoreInterfaceタイプですが、CoreImplクラスはそれを継承しています.ここのCreateDisplayは実際にhardwareqcomdisplaysdmlibscorecore_に呼び出されています.impl.cppのCreateDisplayでは、対応するコードは以下の通りです.
DisplayError CoreImpl::CreateDisplay(DisplayType type, DisplayEventHandler *event_handler,
DisplayInterface **intf) {
SCOPE_LOCK(locker_);
if (!event_handler || !intf) {
return kErrorParameters;
}
DisplayBase *display_base = NULL;
switch (type) {
case kPrimary:
display_base = new DisplayPrimary(event_handler, hw_info_intf_, buffer_sync_handler_,
buffer_allocator_, &comp_mgr_);
break;
case kHDMI:
display_base = new DisplayPrimary(event_handler, hw_info_intf_, buffer_sync_handler_,
buffer_allocator_, &comp_mgr_, kHDMI);
// display_base = new DisplayHDMI(event_handler, hw_info_intf_, buffer_sync_handler_,
// buffer_allocator_, &comp_mgr_);
break;
}
なお、本来の流れでは、CreateDisplayでタイプがkHDMIと判定された場合、DisplayHDMIが呼び出されます.しかし、私たちのはhdmiスクリーンではなくmipiスクリーンなので、ここには行けません.ここからprimary画面と同じ流れになりますが、伝わるidが違うだけです.次の点に注意してください.
display_base = new DisplayPrimary(event_handler, hw_info_intf_, buffer_sync_handler_, buffer_allocator_, &comp_mgr_, kHDMI);
もともとDisplayPrimaryのコンストラクション関数には、このidは付いていませんが、私たちは区別するために、新しいコンストラクション関数を追加して、私たちのidを伝えました.コードを見続けます:
hardware\qcom\display\sdm\libs\core\display_primary.cpp
DisplayPrimary::DisplayPrimary(DisplayEventHandler *event_handler, HWInfoInterface *hw_info_intf,
BufferSyncHandler *buffer_sync_handler,
BufferAllocator *buffer_allocator, CompManager
*comp_manager, int id)
: DisplayBase(kHDMI, event_handler, kDeviceHDMI, buffer_sync_handler, buffer_allocator,
comp_manager, hw_info_intf) {
displayType = kHDMI;
}
DisplayError DisplayPrimary::Init() {
lock_guard obj(recursive_mutex_);
DLOGW("xuhui DisplayPrimary::Init() displayType is %d
", displayType);
DisplayError error = kErrorNone;
if(displayType == kPrimary)
{
error = HWInterface::Create(kPrimary, hw_info_intf_, buffer_sync_handler_,
buffer_allocator_, &hw_intf_);
}
else
{
// , id 。
error = HWInterface::Create(kHDMI, hw_info_intf_, buffer_sync_handler_,
buffer_allocator_, &hw_intf_);
}
......
}
hardware\qcom\display\sdm\libs\core\hw_interface.cpp
DisplayError HWInterface::Create(DisplayType type, HWInfoInterface *hw_info_intf,
BufferSyncHandler *buffer_sync_handler,
BufferAllocator *buffer_allocator, HWInterface **intf) {
DisplayError error = kErrorNone;
HWInterface *hw = nullptr;
DriverType driver_type = GetDriverType();
switch (type) {
case kPrimary:
if (driver_type == DriverType::FB) {
hw = new HWPrimary(buffer_sync_handler, hw_info_intf);
} else {
#ifdef COMPILE_DRM
hw = new HWDeviceDRM(buffer_sync_handler, buffer_allocator, hw_info_intf);
#endif
}
break;
case kHDMI:
if (driver_type == DriverType::FB) {
//hw = new HWHDMI(buffer_sync_handler, hw_info_intf);
// , HWPrimary HWHDMI, kHDMI ID
hw = new HWPrimary(buffer_sync_handler, hw_info_intf, kHDMI);
} else {
return kErrorNotSupported;
}
break;
........
}
ここでは、kHDMIというidを伝達するためのHWPrimaryの構造関数が新たに追加され、以下に定義されています.
hardware\qcom\display\sdm\libs\core\fb\hw_primary.cpp
HWPrimary::HWPrimary(BufferSyncHandler *buffer_sync_handler, HWInfoInterface *hw_info_intf)
: HWDevice(buffer_sync_handler) {
HWDevice::device_type_ = kDevicePrimary;
HWDevice::device_name_ = "Primary Display Device";
HWDevice::hw_info_intf_ = hw_info_intf;
}
HWPrimary::HWPrimary(BufferSyncHandler *buffer_sync_handler, HWInfoInterface
*hw_info_intf,DisplayType type)
// , 1
: HWDevice(buffer_sync_handler, 1) {
// , type , , lcd mipi
HWDevice::device_type_ = kDevicePrimary;
HWDevice::device_name_ = "Primary Display Device2";
HWDevice::hw_info_intf_ = hw_info_intf;
}
上の1に注意してHWDeviceのfb_に伝えましたnode_index_,この場所が最も重要であり、最終lcdがどのデバイスノードに対応するか、fb 0に対応するか、fb 1に対応するかに関係する.私がここに伝えた1は,対応するfb 1ノードを表す.詳細はコードを参照してください:
hardware\qcom\display\sdm\libs\core\fb\hw_device.cpp
HWDevice::HWDevice(BufferSyncHandler *buffer_sync_handler)
: fb_node_index_(-1), fb_path_("/sys/devices/virtual/graphics/fb"),
buffer_sync_handler_(buffer_sync_handler), synchronous_commit_(false) {
}
HWDevice::HWDevice(BufferSyncHandler *buffer_sync_handler, int index)
: fb_node_index_(index), fb_path_("/sys/devices/virtual/graphics/fb"),
buffer_sync_handler_(buffer_sync_handler), synchronous_commit_(false) {
}
DisplayError HWDevice::Init() {
// Read the fb node index
if(fb_node_index_ == -1)
fb_node_index_ = GetFBNodeIndex(device_type_);
if (fb_node_index_ == -1) {
DLOGE("device type = %d should be present", device_type_);
return kErrorHardware;
}
const char *dev_name = NULL;
vector dev_paths = {"/dev/graphics/fb", "/dev/fb"};
for (size_t i = 0; i < dev_paths.size(); i++) {
dev_paths[i] += to_string(fb_node_index_);
if (Sys::access_(dev_paths[i].c_str(), F_OK) >= 0) {
dev_name = dev_paths[i].c_str();
DLOGI("access(%s) successful", dev_name);
break;
}
DLOGI("access(%s), errno = %d, error = %s", dev_paths[i].c_str(), errno, strerror(errno));
}
if (!dev_name) {
DLOGE("access() failed for all possible paths");
return kErrorHardware;
}
// Populate Panel Info (Used for Partial Update)
PopulateHWPanelInfo();
// Populate Bit clk levels.
PopulateBitClkRates();
// Populate HW Capabilities
hw_resource_ = HWResourceInfo();
hw_info_intf_->GetHWResourceInfo(&hw_resource_);
device_fd_ = Sys::open_(dev_name, O_RDWR);
if (device_fd_ < 0) {
DLOGE("open %s failed errno = %d, error = %s", dev_name, errno, strerror(errno));
return kErrorResources;
}
return HWScale::Create(&hw_scale_, hw_resource_.has_qseed3);
}
ここにも構造関数HWDeviceが追加され、デフォルトのfb_node_index_-1で、私たちの副画面が伝わったのは1です.fb_node_index_-1の場合、initでGetFBNodeIndex(device_type_)を通過します.正しいidを検索するには、デフォルトのPrimary displayは0、サブスクリーンは1です.私たちが伝えたdeviceのためですtype_いずれもkDevicePrimaryで、GetFBNodeIndexでidを取ると正しく取れません.したがって、2番目の画面のidを1に直接指定します.次にinitで、次のコードを使用します.
for (size_t i = 0; i < dev_paths.size(); i++) {
dev_paths[i] += to_string(fb_node_index_);
if (Sys::access_(dev_paths[i].c_str(), F_OK) >= 0) {
dev_name = dev_paths[i].c_str();
DLOGI("access(%s) successful", dev_name);
break;
}
}
対応するデバイスノードを取得します.メイン画面は/dev/graphics/fb 0"、"/dev/fb 0"、サブ画面は/dev/graphics/fb 1"、"/dev/fb 1"で、initの最後にdevice_を呼び出します.fd_ = Sys::open_(dev_name, O_RDWR);で開きます.正常に開くと、対応する画面が起動します.
ここまで、すべての変更が完了しました.すべてが正常であれば、サブスクリーンにはメインスクリーンと同じ内容、すなわち同表示が表示されます.このときPresentationクラスが呼び出され、display idが1と指定されると、サブ画面に異なるコンテンツ、すなわち異視が表示されます.