59616c830f
* Reimplement dsi85 as DRM bridge (squashed). - blunt import of preliminary bridge driver - make dsi85 a kernel extension instead of a module to circumvent problems with DRM init order - make i2c matchstring match our DTS - find and use associated drm_panel - add hardcoded DSI85 init in bridge-enable - Do not drm_connector_register yet at bridge_attach time (unlike other bridge drivers) - Add a mipi_dsi_device to configure #lanes The DSI host wants to know the number of lanes, and guesses 1 lane if not given. The old attempt using just panel-simple was easy; a panel declared as DSI panel in panel-simple gets a mipi_dsi_device which it can configure. This driver follows the example of the adv7533 driver and creates its own mipi_dsi_device, where it places a hard-coded number of lanes. - cleanup * Compute DSI85 register values from Panel At bridge_mode_set time, fill all logical DSI85 registers from panel parameters and constant assumptions, then write logical registers to physical registers. Still needs override for a few logical registers: related to DSI clock, and related to horizontal parameters which our panel_simple contains with factor 2 to force proper DSI timings. * compute DSI input clock from DT attributes and mode * add debugging mode_fixup handler to bridge * add DT attrs for more DSI85 registers sync delay, DSI clock divider. And cleanup. * remove bridge_mode_fixup debug helper * use DRM logging macros uniformly * Derive sync polarity from panel's flags but make sure that DSI always uses standard polarity; fix that in the mode_fixup callback. * fix computation of dsi_clk_divider Code assumed wrong bit position in 0x0B, but with the right bit position, the divider is trivial to compute from existing params. There goes one DT attribute. * make LVDS regs 0x19..0x1B configurable by DT * add documentation
270 lines
7.4 KiB
Plaintext
270 lines
7.4 KiB
Plaintext
dsi85 DSI-to-LVDS bridge driver
|
|
###############################
|
|
|
|
This driver integrates the TI SN65DSI85 DSI-to-LVDS converter into a
|
|
DRM-based graphics setup.
|
|
|
|
Author: Konrad Anton <konrad.anton@awinia.de>
|
|
for Hella-Gutmann Solutions GmbH.
|
|
|
|
Setup
|
|
=====
|
|
|
|
In order to use dsi85 in a project, copy the dsi85 tree to
|
|
drivers/gpu/drm/bridge/dsi85 (the "dsi85" buildroot package does that)
|
|
and set CONFIG_DSI85=y.
|
|
|
|
The dsi85 driver implements a DRM Bridge which sits between a DSI
|
|
output and a Panel. The Panel knows panel timings and how to switch
|
|
the backlight. Usually, the panel-simple driver is a good choice.
|
|
|
|
|
|
Configuration by DT
|
|
-------------------
|
|
|
|
Dsi85 needs three nodes in the Device Tree: the dsi85 node, the Panel
|
|
node and the DSI node. As an I2C client, the dsi85 node wants to be
|
|
nested in the node of the I2C bus::
|
|
|
|
i2c@12460000 {
|
|
//...
|
|
|
|
dsi85@2d {
|
|
compatible = "ti,dsi85"; /* instantiate the dsi85 driver */
|
|
reg = <0x2d>; /* i2c slave id */
|
|
|
|
dsi-lanes = <4>; /* tuning parameters -- see below */
|
|
lvds-channels = <2>;
|
|
sync-delay = <33>;
|
|
|
|
ports {
|
|
#address-cells = <1>;
|
|
#size-cells = <0>;
|
|
|
|
port@0 {
|
|
reg = <0>;
|
|
dsi85_in: endpoint {
|
|
remote-endpoint = <&dsi0_out>;
|
|
};
|
|
};
|
|
|
|
port@1 {
|
|
reg = <1>;
|
|
dsi85_out: endpoint {
|
|
remote-endpoint = <&panel_in>;
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
The dsi85 node must have exactly two ports. Port 0 points toward the
|
|
DSI output, Port 1 toward the Panel. The DSI output needs a port wired
|
|
to dsi85_in. In the Qualcomm MSM example::
|
|
|
|
dsi0: mdss_dsi@4700000 {
|
|
status = "okay";
|
|
vdda-supply = <&pm8921_l2>;/*VDD_MIPI1 to 4*/
|
|
/* ... */
|
|
|
|
ports {
|
|
port@0 {
|
|
reg = <0> ;
|
|
dsi0_in: endpoint {
|
|
remote-endpoint = <&mdp_dsi1_out>;
|
|
};
|
|
};
|
|
|
|
port@1 {
|
|
reg = <1> ;
|
|
dsi0_out: endpoint {
|
|
remote-endpoint = <&dsi85_in>;
|
|
data-lanes = <0 1 2 3>;
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
On the other side, the panel needs a port wired to dsi85_out::
|
|
|
|
dsi_lvds_panel: panel {
|
|
reg = <0>;
|
|
compatible = "nlt,192108AC18"; /* one of panel-simple's
|
|
match strings */
|
|
|
|
backlight = <&backlight>;
|
|
|
|
vddp-supply = <&pm8921_l17>;
|
|
iovcc-supply = <&pm8921_lvs7>;
|
|
|
|
enable-gpios = <&tlmm_pinmux 80 GPIO_ACTIVE_HIGH>;
|
|
reset-gpios = <&tlmm_pinmux 32 GPIO_ACTIVE_HIGH>;
|
|
|
|
pinctrl-names = "default";
|
|
pinctrl-0 = <&display_pins>;
|
|
|
|
port {
|
|
panel_in: endpoint {
|
|
remote-endpoint = <&dsi85_out>;
|
|
};
|
|
};
|
|
};
|
|
|
|
|
|
Tuning parameters
|
|
-----------------
|
|
|
|
Some of the configuration details of a DSI85 can be controlled by
|
|
setting properties inside the dsi85 DT node. See DSI85 datasheet for
|
|
more information.
|
|
|
|
dsi-lanes = N
|
|
number of DSI lanes to use. Only tested with 4.
|
|
|
|
lvds-channels = N
|
|
number of LVDS channels to use. (Only tested with 2).
|
|
|
|
sync-delay = N
|
|
Additional delay to insert when translating a DSI HSync or VSync to
|
|
a LVDS Hsync or VSync. Becomes DSI85 registers CHA_SYNC_DELAY_LOW/HIGH,
|
|
see there. Default 0.
|
|
|
|
de-neg-polarity = 0 or 1
|
|
default 0; when 1, set DE_NEG_POLARITY=1 in 0x18.7
|
|
|
|
force-24bpp-mode = 0 or 1
|
|
Default 0. Controls CHA_24BPP_MODE and
|
|
CHB_24BPP_MODE: If 1, force 24bpp; if 0, force 18bpp.
|
|
|
|
force-24bpp-format1 = 0 or 1
|
|
Default 0. Controls CHA_24BPP_FORMAT1 and CHB_24BPP_FORMAT1, see there.
|
|
|
|
lvds-vocm = 0..3
|
|
Default 0. Bit 1 controls CHA_LVDS_VOCM and Bit 0 controls CHB_LVDS_VOCM, see there.
|
|
false means 1.2V, true means 0.9V.
|
|
|
|
lvds-vod-swing-cha = 0..3
|
|
Default 0. Controls the CHA_LVDS_VOD_SWING field.
|
|
|
|
lvds-vod-swing-chb = 0..3
|
|
Default 0. Controls the CHB_LVDS_VOD_SWING field.
|
|
|
|
lvds-even-odd-swap = 0 or 1
|
|
Default 0. Controls EVEN_ODD_SWAP.
|
|
|
|
lvds-reverse = 0..3
|
|
Default 0. Bit 0 controls CHB_REVERSE_LVDS, and Bit 1 controls CHA_REVERSE_LVDS.
|
|
|
|
lvds-term = 0..3
|
|
Default 3. Bit 0 controls CHB_LVDS_TERM (true means termination on),
|
|
and Bit 1 controls CHA_LVDS_TERM.
|
|
|
|
lvds-cm-adjust-cha = 0..3
|
|
Default 0. Adjust common mode voltage for channel A, 0 means don't adjust.
|
|
See CHA_LVDS_CM_ADJUST.
|
|
|
|
lvds-cm-adjust-chb = 0..3
|
|
Default 0. Adjust common mode voltage for channel B, 0 means don't adjust.
|
|
See CHB_LVDS_CM_ADJUST.
|
|
|
|
|
|
Other parameters
|
|
----------------
|
|
|
|
The Panel will require a certain polarity of HSYNC and VSYNC
|
|
signals. These are configured in the drm_display_mode instance of the
|
|
panel-simple panel object, along with the display timings::
|
|
|
|
static const struct drm_display_mode nlt_192108AC18_mode = {
|
|
.clock = 74175 * 2,
|
|
.hdisplay = 960 * 2,
|
|
.hsync_start = (960 + 48) * 2,
|
|
.hsync_end = (960 + 48 + 44) * 2,
|
|
.htotal = (960 + 48 + 44 + 48) * 2,
|
|
.vdisplay = 1080,
|
|
.vsync_start = 1080 + 15,
|
|
.vsync_end = 1080 + 15 + 15,
|
|
.vtotal = 1080 + 15 + 15 + 15,
|
|
.vrefresh = 60,
|
|
.flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
|
|
};
|
|
|
|
The flags bit DRM_MODE_FLAG_NVSYNC causes VS_NEG_POLARITY=1 in DSI85
|
|
reg 0x18, i.e. VS is negative polarity driven 0 during corresponding
|
|
sync. Analogously, DRM_MODE_FLAG_NHSYNC causes HS_NEG_POLARITY=1 in
|
|
reg 0x18 for HSync. Default is VS_NEG_POLARITY=HS_NEG_POLARITY=0.
|
|
|
|
|
|
|
|
Notes for dual-channel LVDS
|
|
---------------------------
|
|
|
|
The NL192108AC18 panel's datasheet requests a clock of 74MHz and a
|
|
width of 960 double-pixel clocks. However, dsi85 expects the panel to
|
|
contain pixel timings, i.e. 148MHz and 1920 pixels width, because then
|
|
the DSI output can be configured directly from the panel parameters.
|
|
|
|
|
|
|
|
Debug messages
|
|
--------------
|
|
|
|
The dsi85 driver uses the DRM logging facility which can be controlled
|
|
by kernel parameter drm.debug=N, where N=0x3f enables all output.
|
|
|
|
Especially errors in the Device Tree configuration can be discovered
|
|
that way; grep for "dsi85".
|
|
|
|
|
|
|
|
Design and Implementation
|
|
=========================
|
|
|
|
Implementation follows the example of other bridge drivers in the
|
|
mainline kernel, especially nxp-ptn3460.
|
|
|
|
The dsi85 driver is implemented as an I2C client driver whose probe
|
|
method tries to locate panel and DSI drivers. Depending on
|
|
circumstances, this will fail if some of the other drivers (e.g. the
|
|
panel) are not yet ready. In that case, the probe callback uses the
|
|
EPROBE_DEFER mechanism to request a retry. Otherwise, the probe
|
|
callback installs the DRM bridge represented by sn65dsi85_bridge_funcs.
|
|
|
|
The first bridge callback is the attach callback, which assembles a
|
|
driver graph out of:
|
|
|
|
- a DRM Connector, implemented using drm_connector_helper, that helps
|
|
choose display modes by delegating to the Panel.
|
|
|
|
- the DRM Encoder supplied by the DRM framework
|
|
|
|
- a DSI client device to configure DSI parameters (number of lanes...)
|
|
|
|
The other callbacks deal with a proper sequence of initialization:
|
|
|
|
- mode_fixup adjusts a display mode and decides whether it can be
|
|
activated. The dsi85 implementation does not attempt to validate
|
|
whether the timings are possible -- in the intended setting, the
|
|
panel does not change that often -- but it normalizes the
|
|
sync-polarity flags so that DSI always uses the same sync polarity,
|
|
regardless of the panel's choice.
|
|
|
|
- mode_set is the place where the DSI85 register values are computed
|
|
according to the adjusted display mode and the DT settings, then
|
|
written to DSI85.
|
|
|
|
- pre_enable tells the panel to power itself up
|
|
|
|
- enable enables the DSI85 PLL and tells the panel to activate its backlight
|
|
|
|
- disable undoes enable
|
|
|
|
- post_disable undoes pre_enable
|
|
|
|
|
|
Caveats
|
|
-------
|
|
|
|
The dsi85 driver has only been tested in a Qualcomm MSM kernel, and
|
|
only with the panel NL192108AC18, dual-channel LVDS, four-lane DSI.
|
|
|