dsi85/dsi85.txt
awinia 59616c830f Reimplement dsi85 as DRM bridge (squashed). (#1)
* 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
2017-05-18 11:27:11 +02:00

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.