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
This commit is contained in:
parent
241f587f41
commit
59616c830f
7
Kconfig
Normal file
7
Kconfig
Normal file
|
@ -0,0 +1,7 @@
|
|||
config DRM_BRIDGE_DSI85
|
||||
tristate "DSI85 DSI-to-LVDS bridge"
|
||||
depends on OF
|
||||
select DRM_KMS_HELPER
|
||||
select REGMAP_I2C
|
||||
help
|
||||
Support for the DSI85 DSI-to-LVDS-bridge.
|
13
Makefile
13
Makefile
|
@ -1,11 +1,2 @@
|
|||
ifneq ($(KERNELRELEASE),)
|
||||
# kbuild part of makefile
|
||||
include Kbuild
|
||||
else
|
||||
# normal makefile
|
||||
KDIR ?= /lib/modules/`uname -r`/build
|
||||
|
||||
default:
|
||||
$(MAKE) -C $(KDIR) M=$$PWD
|
||||
|
||||
endif
|
||||
obj-$(CONFIG_DRM_BRIDGE_DSI85) := dsi85.o
|
||||
dsi85-y := dsi85_main.o
|
||||
|
|
269
dsi85.txt
Normal file
269
dsi85.txt
Normal file
|
@ -0,0 +1,269 @@
|
|||
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.
|
||||
|
972
dsi85_main.c
972
dsi85_main.c
File diff suppressed because it is too large
Load diff
46
dsi85_registers.h
Normal file
46
dsi85_registers.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
#ifndef DSI85_REGISTERS_H_INCLUDED
|
||||
#define DSI85_REGISTERS_H_INCLUDED
|
||||
|
||||
/* Register addresses */
|
||||
#define DSI85_SOFT_RESET 0x09
|
||||
#define DSI85_CORE_PLL 0x0A
|
||||
#define DSI85_PLL_DIV 0x0B
|
||||
#define DSI85_PLL_EN 0x0D
|
||||
#define DSI85_DSI_CFG 0x10
|
||||
#define DSI85_DSI_EQ 0x11
|
||||
#define DSI85_CHA_DSI_CLK_RNG 0x12
|
||||
#define DSI85_CHB_DSI_CLK_RNG 0x13
|
||||
#define DSI85_LVDS_MODE 0x18
|
||||
#define DSI85_LVDS_SIGN 0x19
|
||||
#define DSI85_LVDS_TERM 0x1A
|
||||
#define DSI85_LVDS_ADJUST 0x1B
|
||||
#define DSI85_CHA_LINE_LEN_LO 0x20
|
||||
#define DSI85_CHA_LINE_LEN_HI 0x21
|
||||
#define DSI85_CHB_LINE_LEN_LO 0x22
|
||||
#define DSI85_CHB_LINE_LEN_HI 0x23
|
||||
#define DSI85_CHA_VERT_LINES_LO 0x24
|
||||
#define DSI85_CHA_VERT_LINES_HI 0x25
|
||||
#define DSI85_CHB_VERT_LINES_LO 0x26
|
||||
#define DSI85_CHB_VERT_LINES_HI 0x27
|
||||
#define DSI85_CHA_SYNC_DELAY_LO 0x28
|
||||
#define DSI85_CHA_SYNC_DELAY_HI 0x29
|
||||
#define DSI85_CHB_SYNC_DELAY_LO 0x2A
|
||||
#define DSI85_CHB_SYNC_DELAY_HI 0x2B
|
||||
#define DSI85_CHA_HSYNC_WIDTH_LO 0x2C
|
||||
#define DSI85_CHA_HSYNC_WIDTH_HI 0x2D
|
||||
#define DSI85_CHB_HSYNC_WIDTH_LO 0x2E
|
||||
#define DSI85_CHB_HSYNC_WIDTH_HI 0x2F
|
||||
#define DSI85_CHA_VSYNC_WIDTH_LO 0x30
|
||||
#define DSI85_CHA_VSYNC_WIDTH_HI 0x31
|
||||
#define DSI85_CHB_VSYNC_WIDTH_LO 0x32
|
||||
#define DSI85_CHB_VSYNC_WIDTH_HI 0x33
|
||||
#define DSI85_CHA_HORZ_BACKPORCH 0x34
|
||||
#define DSI85_CHB_HORZ_BACKPORCH 0x35
|
||||
#define DSI85_CHA_VERT_BACKPORCH 0x36
|
||||
#define DSI85_CHB_VERT_BACKPORCH 0x37
|
||||
#define DSI85_CHA_HORZ_FRONTPORCH 0x38
|
||||
#define DSI85_CHB_HORZ_FRONTPORCH 0x39
|
||||
#define DSI85_CHA_VERT_FRONTPORCH 0x3A
|
||||
#define DSI85_CHB_VERT_FRONTPORCH 0x3B
|
||||
|
||||
#endif
|
Loading…
Reference in a new issue