From 59616c830f9de6e28c4754e9f99330697ff83dc2 Mon Sep 17 00:00:00 2001 From: awinia Date: Thu, 18 May 2017 11:27:11 +0200 Subject: [PATCH] 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 --- Kbuild | 2 - Kconfig | 7 + Makefile | 13 +- dsi85.txt | 269 +++++++++++++ dsi85_main.c | 972 +++++++++++++++++++++++++++++++++++++++++----- dsi85_registers.h | 46 +++ 6 files changed, 1203 insertions(+), 106 deletions(-) delete mode 100644 Kbuild create mode 100644 Kconfig create mode 100644 dsi85.txt create mode 100644 dsi85_registers.h diff --git a/Kbuild b/Kbuild deleted file mode 100644 index a8ddca9..0000000 --- a/Kbuild +++ /dev/null @@ -1,2 +0,0 @@ -obj-m := dsi85.o -dsi85-y := dsi85_main.o diff --git a/Kconfig b/Kconfig new file mode 100644 index 0000000..fd01cab --- /dev/null +++ b/Kconfig @@ -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. diff --git a/Makefile b/Makefile index 1669d2e..1528ead 100644 --- a/Makefile +++ b/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 diff --git a/dsi85.txt b/dsi85.txt new file mode 100644 index 0000000..3bccf65 --- /dev/null +++ b/dsi85.txt @@ -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 +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. + diff --git a/dsi85_main.c b/dsi85_main.c index 3ba2f3c..0fcff25 100755 --- a/dsi85_main.c +++ b/dsi85_main.c @@ -1,6 +1,13 @@ /* - * Copyright 2017 Hella Gutmann Solutions GmbH - * Author: Moritz Bitsch + * Copyright (C) 2017, Hella-Gutmann Solutions GmbH + * + * Partly based on panel-dsi85.c from: + * + * Copyright 2011 Texas Instruments, Inc. + * Author: Archit Taneja + * based on d2l panel driver by Jerry Alexander + * + * Also partly based on the ptn3460 and adv7533 kernel drivers. * * This program iss free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published by @@ -9,127 +16,906 @@ * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. + * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include +#include +#include +#include +#include +#include -const struct _cfg_reg -{ - uint8_t reg; - uint8_t val; -} cfg_reg[] = -{ - {0x09, 0x01}, // soft reset - {0x0D, 0x00}, // pll disable +#include +#include +#include +#include +#include +#include +#include - {0x09, 0x00}, - {0x0A, 0x05}, - {0x0B, 0x28}, - {0x0D, 0x00}, - {0x10, 0x26}, - {0x11, 0x00}, - {0x12, 0x5d}, - {0x13, 0x00}, - {0x18, 0x6c}, - {0x19, 0x00}, - {0x1A, 0x03}, - {0x1B, 0x00}, - {0x20, 0x80}, - {0x21, 0x07}, - {0x22, 0x00}, - {0x23, 0x00}, - {0x24, 0x00}, - {0x25, 0x00}, - {0x26, 0x00}, - {0x27, 0x00}, - {0x28, 0x21}, - {0x29, 0x00}, - {0x2A, 0x00}, - {0x2B, 0x00}, - {0x2C, 0x2c}, - {0x2D, 0x00}, - {0x2E, 0x00}, - {0x2F, 0x00}, - {0x30, 0x0f}, - {0x31, 0x00}, - {0x32, 0x00}, - {0x33, 0x00}, - {0x34, 0x30}, - {0x35, 0x00}, - {0x36, 0x00}, - {0x37, 0x00}, - {0x38, 0x00}, - {0x39, 0x00}, - {0x3A, 0x00}, - {0x3B, 0x00}, - {0x3C, 0x00}, - {0x3D, 0x00}, - {0x3E, 0x00}, +#include