drm: Add support for IPK DSI Host Driver
Subsystems (3)
v3 cover drm: Add support for IPK DSI Host Driver
Adds support for the display subsystem in the Synopsys DesignWare IPK devices.
The display pipeline is limited and does not have access to memory, the validation is done using a VPG (Video Pattern Generator), as DPI stimulus for the DW MIPI DSI Host.
A Synopsys DesignWare MIPI DSI Host v1.40 is used in the IPK device, that so far, is fully compatible with the driver dw-mipi-dsi.
To activate the VPG use the sysfs pattern variable, assigning values from 0 (shutdown) to 4. The usage of the VPG and the Synopsys DesignWare MIPI DSI Host internal video generator is mutually exclusive.
The submission of this driver aims to be used as a work base for the submission of enhancements over the Synopsys DesignWare MIPI DSI Host.
Angelo Ribeiro (4):
dt-bindings: display: Add IPK DSI subsystem bindings
drm: ipk: Add DRM driver for DesignWare IPK DSI
drm: ipk: Add extensions for DW MIPI DSI Host driver
MAINTAINERS: Add IPK MIPI DSI Host driver entry
.../bindings/display/snps,dw-ipk-dsi.yaml | 159 ++++++ .../bindings/display/snps,dw-ipk-vpg.yaml | 73 +++ MAINTAINERS | 8 + drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/ipk/Kconfig | 22 + drivers/gpu/drm/ipk/Makefile | 6 + drivers/gpu/drm/ipk/dw-drv.c | 169 +++++++ drivers/gpu/drm/ipk/dw-ipk.h | 26 + drivers/gpu/drm/ipk/dw-mipi-dsi-ipk.c | 557 +++++++++++++++++++++ drivers/gpu/drm/ipk/dw-vpg.c | 412 +++++++++++++++ drivers/gpu/drm/ipk/dw-vpg.h | 48 ++ 12 files changed, 1483 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/snps,dw-ipk-dsi.yaml create mode 100644 Documentation/devicetree/bindings/display/snps,dw-ipk-vpg.yaml create mode 100644 drivers/gpu/drm/ipk/Kconfig create mode 100644 drivers/gpu/drm/ipk/Makefile create mode 100644 drivers/gpu/drm/ipk/dw-drv.c create mode 100644 drivers/gpu/drm/ipk/dw-ipk.h create mode 100644 drivers/gpu/drm/ipk/dw-mipi-dsi-ipk.c create mode 100644 drivers/gpu/drm/ipk/dw-vpg.c create mode 100644 drivers/gpu/drm/ipk/dw-vpg.h
Signature · 1 line
2.7.4
PATCH v3 4/4 MAINTAINERS: Add IPK MIPI DSI Host driver entry
Creates entry for Synopsys DesignWare IPK DRM driver and adds myself as maintainer.
--- MAINTAINERS | 8 ++++++++ 1 file changed, 8 insertions(+)
MAINTAINERS +8 -0
@@ -5507,6 +5507,14 @@ T: git git://anongit.freedesktop.org/drm/drm-misc F: Documentation/devicetree/bindings/display/ste,mcde.txt F: drivers/gpu/drm/mcde/ +DRM DRIVER FOR SYNOPSYS DESIGNWARE IPK +M: Angelo Ribeiro <[email protected]> +L: [email protected] +S: Maintained +F: drivers/gpu/drm/ipk/ +F: Documentation/devicetree/bindings/display/ipk/ +T: git git://anongit.freedesktop.org/drm/drm-misc + DRM DRIVER FOR TDFX VIDEO CARDS S: Orphan / Obsolete F: drivers/gpu/drm/tdfx/
Signature · 1 line
2.7.4
PATCH v3 3/4 drm: ipk: Add extensions for DW MIPI DSI Host driver
In reply to Angelo RibeiroAdd Synopsys DesignWare IPK specific extensions for Synopsys DesignWare MIPI DSI Host driver.
---
Changes since v3:
- Rearranged headers.
--- drivers/gpu/drm/ipk/Kconfig | 9 + drivers/gpu/drm/ipk/Makefile | 2 + drivers/gpu/drm/ipk/dw-mipi-dsi-ipk.c | 557 ++++++++++++++++++++++++++++++++++ 3 files changed, 568 insertions(+) create mode 100644 drivers/gpu/drm/ipk/dw-mipi-dsi-ipk.c
drivers/gpu/drm/ipk/Kconfig +9 -0
@@ -11,3 +11,12 @@ config DRM_IPK Enable support for the Synopsys DesignWare DRM DSI. To compile this driver as a module, choose M here: the module will be called ipk-drm. + +config DRM_IPK_DSI + tristate "Synopsys DesignWare IPK specific extensions for MIPI DSI" + depends on DRM_IPK + select DRM_DW_MIPI_DSI + help + Choose this option for Synopsys DesignWare IPK MIPI DSI support. + To compile this driver as a module, choose M here: the module + will be called dw-mipi-dsi-ipk.
drivers/gpu/drm/ipk/Makefile +2 -0
@@ -2,3 +2,5 @@ ipk-drm-y := dw-drv.o dw-vpg.o obj-$(CONFIG_DRM_IPK) += ipk-drm.o + +obj-$(CONFIG_DRM_IPK_DSI) += dw-mipi-dsi-ipk.o
drivers/gpu/drm/ipk/dw-mipi-dsi-ipk.c new file +557 -0
557 lines not on this page · raw message
Signature · 1 line
2.7.4
PATCH v3 2/4 drm: ipk: Add DRM driver for DesignWare IPK DSI
In reply to Angelo RibeiroAdd support for Synopsys DesignWare VPG (Video Pattern Generator) and DRM driver for Synopsys DesignWare DSI Host IPK solution.
This patch has to be applied on top of Daniel Vetter's implementation - devm_drm_dev_alloc https://patchwork.freedesktop.org/patch/359897/ .
---
Changes since v3:
- Changed Makefile to use '+=' instead of '\'.
- Rearranged headers.
- Replaced drm_mode_config_init by drmm_mode_config_init.
- Droped DRM legacy functions.
- Replaced DRM_xxx by drm_xxx.
- Changed load function to devm_drm_dev_alloc.
- Implemented pipeline as a drm_simple_display_pipe.
--- drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/ipk/Kconfig | 13 ++ drivers/gpu/drm/ipk/Makefile | 4 + drivers/gpu/drm/ipk/dw-drv.c | 169 ++++++++++++++++++ drivers/gpu/drm/ipk/dw-ipk.h | 26 +++ drivers/gpu/drm/ipk/dw-vpg.c | 412 +++++++++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/ipk/dw-vpg.h | 48 +++++ 8 files changed, 675 insertions(+) create mode 100644 drivers/gpu/drm/ipk/Kconfig create mode 100644 drivers/gpu/drm/ipk/Makefile create mode 100644 drivers/gpu/drm/ipk/dw-drv.c create mode 100644 drivers/gpu/drm/ipk/dw-ipk.h create mode 100644 drivers/gpu/drm/ipk/dw-vpg.c create mode 100644 drivers/gpu/drm/ipk/dw-vpg.h
drivers/gpu/drm/Kconfig +2 -0
@@ -388,6 +388,8 @@ source "drivers/gpu/drm/mcde/Kconfig" source "drivers/gpu/drm/tidss/Kconfig" +source "drivers/gpu/drm/ipk/Kconfig" + # Keep legacy drivers last menuconfig DRM_LEGACY
drivers/gpu/drm/Makefile +1 -0
@@ -124,3 +124,4 @@ obj-$(CONFIG_DRM_PANFROST) += panfrost/ obj-$(CONFIG_DRM_ASPEED_GFX) += aspeed/ obj-$(CONFIG_DRM_MCDE) += mcde/ obj-$(CONFIG_DRM_TIDSS) += tidss/ +obj-$(CONFIG_DRM_IPK) += ipk/
drivers/gpu/drm/ipk/Kconfig new file +13 -0
@@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0-only +config DRM_IPK + tristate "DRM Support for Synopsys DesignWare IPK DSI" + depends on DRM + select DRM_KMS_HELPER + select DRM_GEM_CMA_HELPER + select DRM_KMS_CMA_HELPER + select DRM_PANEL_BRIDGE + select VIDEOMODE_HELPERS + help + Enable support for the Synopsys DesignWare DRM DSI. + To compile this driver as a module, choose M here: the module + will be called ipk-drm.
drivers/gpu/drm/ipk/Makefile new file +4 -0
@@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only +ipk-drm-y := dw-drv.o dw-vpg.o + +obj-$(CONFIG_DRM_IPK) += ipk-drm.o
drivers/gpu/drm/ipk/dw-drv.c new file +169 -0
169 lines not on this page · raw message
drivers/gpu/drm/ipk/dw-ipk.h new file +26 -0
@@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* +* Copyright (c) 2019-2020 Synopsys, Inc. and/or its affiliates. +* Synopsys DesignWare IPK MIPI DSI DRM Driver +*/ + +#ifndef _DW_IPK_H +#define _DW_IPK_H + +#include <drm/drm_simple_kms_helper.h> + +struct vpg_device ; + +struct ipk_device { +struct drm_device drm; +struct platform_device *platform; +struct drm_simple_display_pipe pipe; +struct vpg_device *vpg; +}; + +#define drm_dev_to_ipk_dev (target) \ + container_of(target, struct ipk_device, drm) +#define display_pipe_to_ipk_dev (target) \ + container_of(target, struct ipk_device, pipe) + +#endif /* _DW_IPK_H */
drivers/gpu/drm/ipk/dw-vpg.c new file +412 -0
412 lines not on this page · raw message
drivers/gpu/drm/ipk/dw-vpg.h new file +48 -0
48 lines not on this page · raw message
Signature · 1 line
2.7.4
PATCH v3 1/4 dt-bindings: display: Add IPK DSI subsystem bindings
In reply to Angelo RibeiroAdd dt-bindings for Synopsys DesignWare MIPI DSI Host and VPG (Video Pattern Generator) support in the IPK display subsystem.
The Synopsys DesignWare IPK display video pipeline is composed by a DSI controller (snps,dw-ipk-dsi) and a VPG (snps,dw-ipk-vpg) as DPI stimulus. Typically is used the Raspberry Pi (raspberrypi,7inch-touchscreen-panel) as DSI panel that requires a I2C controller (snps,designware-i2c).
---
Changes since v3:
- Fixed dt-binding breaking on make dt_binding_check.
Changes since v2:
- Fixed dt-bindings issues, see
https://patchwork.ozlabs.org/patch/1260819/. --- .../bindings/display/snps,dw-ipk-dsi.yaml | 159 +++++++++++++++++++++ .../bindings/display/snps,dw-ipk-vpg.yaml | 73 ++++++++++ 2 files changed, 232 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/snps,dw-ipk-dsi.yaml create mode 100644 Documentation/devicetree/bindings/display/snps,dw-ipk-vpg.yaml
Documentation/devicetree/bindings/display/snps,dw-ipk-dsi.yaml new file +159 -0
@@ -0,0 +1,159 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/snps,dw-ipk-dsi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Synopsys DesignWare IPK specific extensions for the Synopsys DSI host + controller. + +maintainers: + - Angelo Ribeiro <[email protected]> + +description: | + The Synopsys DesignWare IPK DSI controller uses the Synopsys DesignWare MIPI + DSI host controller. + For more info refer to + Documentation/devicetree/bindings/display/bridge/dw_mipi_dsi.txt. + +properties: + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + + compatible: + items: + - const: snps,dw-ipk-dsi + - const: snps,dw-mipi-dsi + + reg: + items: + - description: DW MIPI DSI Host registers + - description: DW MIPI DSI Phy test-chip registers + minItems: 2 + + reg-names: + items: + - const: dsi + - const: phy + minItems: 2 + + clocks: + items: + - description: Peripheral clock + - description: PLL clock + - description: Pixel clock + minItems: 2 + + clock-names: + items: + - const: pclk + - const: ref + - const: px_clk + minItems: 2 + + resets: + items: + - description: APB reset line + minItems: 1 + + reset-names: + items: + - const: apb + minItems: 1 + + ports: + type: object + description: | + A port node containging a DSI input and outuput port nodes as defined in + Documentation/devicetree/bindings/media/video-interfaces.txt and + Documentation/devicetree/bindings/graph.txt. + properties: + port@0: + type: object + description: Input endpoint of the controller, connects to a DPI source. + + port@1: + type: object + description: Output endpoint of the controller, connects to a panel or + a bridge input port. + + required: + - port@0 + - port@1 + +additionalProperties: false + +required: + - "#address-cells" + - "#size-cells" + - compatible + - reg + - reg-names + - clocks + - clock-names + - resets + - reset-names + - ports + +examples: + - | + dsi1: dw-ipk-dsi@2000 { + compatible = "snps,dw-ipk-dsi"; + reg = <0x02000 0xfff>, <0x05000 0xfff>; + reg-names = "dsi", "phy"; + clocks = <&apb_clk>, <&pll_clk>; + clock-names = "pclk", "ref"; + resets = <&ipk_rst 1>; + reset-names = "apb"; + + #address-cells = <1>; + #size-cells = <0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + dsi1_in: endpoint { + remote-endpoint = <&vbridge_out>; + }; + }; + + port@1 { + reg = <1>; + dsi_out_port: endpoint { + remote-endpoint = <&panel_dsi_port>; + }; + }; + }; + }; + + i2c: i2c@1000 { + compatible = "snps,designware-i2c"; + reg = <0x01000 0x100>; + clock-frequency = <400000>; + clocks = <&i2cclk>; + interrupts = <0>; + resets = <&ipk_rst 0>; + reset-names = "i2c"; + + #address-cells = <1>; + #size-cells = <0>; + + lcd@45 { + compatible = "raspberrypi,7inch-touchscreen-panel"; + reg = <0x45>; + + port { + panel_dsi_port: endpoint { + remote-endpoint = <&dsi_out_port>; + }; + }; + }; + }; + +...
Documentation/devicetree/bindings/display/snps,dw-ipk-vpg.yaml new file +73 -0
73 lines not on this page · raw message
Signature · 1 line
2.7.4
Re: [PATCH v3 4/4] MAINTAINERS: Add IPK MIPI DSI Host driver entry
In reply to Angelo RibeiroQuoted 2 lines · Angelo Ribeiro
Creates entry for Synopsys DesignWare IPK DRM driver and adds myself as maintainer.
[]
MAINTAINERS
Quoted 1 line
MAINTAINERS +0
-0
[]
MAINTAINERS T: git git://anongit.freedesktop.org/drm/drm-misc · line 5516
Quoted 11 lines
MAINTAINERS +7
-0@@ -5507,6 +5507,14 @@ T: git git://anongit.freedesktop.org/drm/drm-misc F: Documentation/devicetree/bindings/display/ste,mcde.txt F: drivers/gpu/drm/mcde/ +DRM DRIVER FOR SYNOPSYS DESIGNWARE IPK +M: Angelo Ribeiro <[email protected]> +L: [email protected] +S: Maintained +F: drivers/gpu/drm/ipk/ +F: Documentation/devicetree/bindings/display/ipk/ +T: git git://anongit.freedesktop.org/drm/drm-misc
There is now a preferred order for the entries in a section.
Please use:
DRM DRIVER FOR SYNOPSYS DESIGNWARE IPK
M: Angelo Ribeiro <[email protected]>
L: [email protected]>
S: Maintained
T: git git://anongit.freedesktop.org/drm/drm-misc
F: Document
ation/devicetree/bindings/display/ipk/>
F: drivers/gpu/drm/ipk/
_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel
Rob Herring
maintainerRe: [PATCH v3 1/4] dt-bindings: display: Add IPK DSI subsystem bindings
In reply to Angelo RibeiroQuoted 24 lines · Angelo Ribeiro
Add dt-bindings for Synopsys DesignWare MIPI DSI Host and VPG (Video Pattern Generator) support in the IPK display subsystem.
The Synopsys DesignWare IPK display video pipeline is composed by a DSI controller (snps,dw-ipk-dsi) and a VPG (snps,dw-ipk-vpg) as DPI stimulus. Typically is used the Raspberry Pi (raspberrypi,7inch-touchscreen-panel) as DSI panel that requires a I2C controller (snps,designware-i2c).
Reported-by Rob Herring <[email protected]>Cc David Airlie <[email protected]>Cc Daniel Vetter <[email protected]>Cc Sam Ravnborg <[email protected]>Cc Rob Herring <[email protected]>Cc Mark Rutland <[email protected]>Cc Gustavo Pimentel <[email protected]>Cc Joao Pinto <[email protected]>Signed-off-by Angelo Ribeiro <[email protected]>---
Changes since v3:
- Fixed dt-binding breaking onmake dt_binding_check.Changes since v2:
- Fixed dt-bindings issues, see
Quoted 8 lines
https://patchwork.ozlabs.org/patch/1260819/. --- .../bindings/display/snps,dw-ipk-dsi.yaml | 159 +++++++++++++++++++++ .../bindings/display/snps,dw-ipk-vpg.yaml | 73 ++++++++++ 2 files changed, 232 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/snps,dw-ipk-dsi.yaml create mode 100644 Documentation/devicetree/bindings/display/snps,dw-ipk-vpg.yaml
My bot found errors running 'make dt_binding_check' on your patch:
/builds/robherring/linux-dt-review/Documentation/devicetree/bindings/display/snps,dw-ipk-dsi.example.dt.yaml: dw-ipk-dsi@2000: compatible: ['snps,dw-ipk-dsi'] is too short
See https://patchwork.ozlabs.org/patch/1277673
If you already ran 'make dt_binding_check' and didn't see the above error(s), then make sure dt-schema is up to date:
pip3 install git+https://github.com/devicetree-org/dt-schema.git@master --upgrade
Please check and re-submit.
_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel
Re: [PATCH v3 3/4] drm: ipk: Add extensions for DW MIPI DSI Host driver
In reply to Angelo RibeiroQuoted 11 lines · Angelo Ribeiro
Add Synopsys DesignWare IPK specific extensions for Synopsys DesignWare MIPI DSI Host driver.
Cc Maarten Lankhorst <[email protected]>Cc Maxime Ripard <[email protected]>Cc David Airlie <[email protected]>Cc Daniel Vetter <[email protected]>Cc Sam Ravnborg <[email protected]>Cc Gustavo Pimentel <[email protected]>Cc Joao Pinto <[email protected]>Signed-off-by Angelo Ribeiro <[email protected]>
I've dumped this on a pile of bridge drivers by now, but I don't think the dw-mipi-dsi organization makes much sense.
I think what we'd need is:
- drm_encoder is handled by the drm_device driver, not by dw-mipi-dsi drm_bridge driver
- the glue code for the various soc specific implementations (like ipk here) should be put behind the drm_bridge abstraction. Otherwise I'm not really seeing why exactly dw-mipi-dsi is a bridge driver if it doesn't work like a bridge driver
- Probably we should put all these files into drm/bridge/dw-mipi-dsi/
- drm_device drivers should get at their bridges with one of the standard of helpers we have in drm_bridge, not by directly calling into a bridge drivers.
I know that dw-hdmi is using the exact same code pattern, but we got to stop this eventually or it becomes an unfixable mess. -Daniel
Quoted 603 lines
---
Changes since v3:
- Rearranged headers.--- drivers/gpu/drm/ipk/Kconfig | 9 + drivers/gpu/drm/ipk/Makefile | 2 + drivers/gpu/drm/ipk/dw-mipi-dsi-ipk.c | 557 ++++++++++++++++++++++++++++++++++ 3 files changed, 568 insertions(+) create mode 100644 drivers/gpu/drm/ipk/dw-mipi-dsi-ipk.cdrivers/gpu/drm/ipk/Kconfig +9
-0@@ -11,3 +11,12 @@ config DRM_IPK Enable support for the Synopsys DesignWare DRM DSI. To compile this driver as a module, choose M here: the module will be called ipk-drm. + +config DRM_IPK_DSI + tristate "Synopsys DesignWare IPK specific extensions for MIPI DSI" + depends on DRM_IPK + select DRM_DW_MIPI_DSI + help + Choose this option for Synopsys DesignWare IPK MIPI DSI support. + To compile this driver as a module, choose M here: the module + will be called dw-mipi-dsi-ipk.drivers/gpu/drm/ipk/Makefile +2
-0@@ -2,3 +2,5 @@ ipk-drm-y := dw-drv.o dw-vpg.o obj-$(CONFIG_DRM_IPK) += ipk-drm.o + +obj-$(CONFIG_DRM_IPK_DSI) += dw-mipi-dsi-ipk.odrivers/gpu/drm/ipk/dw-mipi-dsi-ipk.c new file +557
-0@@ -0,0 +1,557 @@ +// SPDX-License-Identifier: GPL-2.0 +/* +* Copyright (c) 2019-2020 Synopsys, Inc. and/or its affiliates. +* Synopsys DesignWare MIPI DSI solution driver +* +* Author: Angelo Ribeiro <[email protected]> +* Author: Luis Oliveira <[email protected]> +*/ + +#include <linux/clk.h> +#include <linux/iopoll.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> + +#include <video/mipi_display.h> + +#include <drm/bridge/dw_mipi_dsi.h> +#include <drm/drm_crtc.h> +#include <drm/drm_device.h> +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_print.h> + +#define DW_DPHY_LPCLK_CTRL 0x94 +#define DW_DPHY_RSTZ 0xA0 +#define DW_DPHY_IF_CFG 0xA4 +#define DW_DPHY_ULPS_CTRL 0xA8 +#define DW_DPHY_TX_TRIGGERS 0xAC +#define DW_DPHY_STATUS 0xB0 +#define DW_DPHY_TST_CTRL0 0xB4 +#define DW_DPHY_TST_CTRL1 0xB8 +#define DW_GEN3_IF_TESTER 0x3c +#define DW_GEN3_IF_SOC_PLL 0x48 +#define DW_GEN3_IF_SOC_PLL_EN 0x4C + +#define DW_12BITS_DPHY_RDY_L0 0x507 +#define DW_12BITS_DPHY_RDY_L1 0x707 +#define DW_12BITS_DPHY_RDY_L2 0x907 +#define DW_12BITS_DPHY_RDY_L3 0xB07 + +#define DW_LANE_MIN_KBPS 80000 +#define DW_LANE_MAX_KBPS 2500000000 +#define DW_DPHY_DIV_UPPER_LIMIT 8000 +#define DW_DPHY_DIV_LOWER_LIMIT 2000 +#define DW_MIN_OUTPUT_FREQ 80 +#define DW_LPHS_TIM_TRANSIONS 0x40 + +enum dw_glueiftester { +GLUE_LOGIC =0x4 , +RX_PHY =0x2 , +TX_PHY =0x1 , +RESET =0x0 , +}; + +struct dw_range_dphy { +u32 freq; +u8 hs_freq_range; +u32 osc_freq_target; +} dw_range_gen3[] = { + {80 ,0x00 ,0x3f }, {90 ,0x10 ,0x3f }, {100 ,0x20 ,0x3f }, + {110 ,0x30 ,0x39 }, {120 ,0x01 ,0x39 }, {130 ,0x11 ,0x39 }, + {140 ,0x21 ,0x39 }, {150 ,0x31 ,0x39 }, {160 ,0x02 ,0x39 }, + {170 ,0x12 ,0x2f }, {180 ,0x22 ,0x2f }, {190 ,0x32 ,0x2f }, + {205 ,0x03 ,0x2f }, {220 ,0x13 ,0x29 }, {235 ,0x23 ,0x29 }, + {250 ,0x33 ,0x29 }, {275 ,0x04 ,0x29 }, {300 ,0x14 ,0x29 }, + {325 ,0x25 ,0x29 }, {350 ,0x35 ,0x1f }, {400 ,0x05 ,0x1f }, + {450 ,0x16 ,0x19 }, {500 ,0x26 ,0x19 }, {550 ,0x37 ,0x19 }, + {600 ,0x07 ,0x19 }, {650 ,0x18 ,0x19 }, {700 ,0x28 ,0x0f }, + {750 ,0x39 ,0x0f }, {800 ,0x09 ,0x0f }, {850 ,0x19 ,0x0f }, + {900 ,0x29 ,0x09 }, {950 ,0x3a ,0x09 }, {1000 ,0x0a ,0x09 }, + {1050 ,0x1a ,0x09 }, {1100 ,0x2a ,0x09 }, {1150 ,0x3b ,0x09 }, + {1200 ,0x0b ,0x09 }, {1250 ,0x1b ,0x09 }, {1300 ,0x2b ,0x09 }, + {1350 ,0x3c ,0x03 }, {1400 ,0x0c ,0x03 }, {1450 ,0x1c ,0x03 }, + {1500 ,0x2c ,0x03 }, {1550 ,0x3d ,0x03 }, {1600 ,0x0d ,0x03 }, + {1650 ,0x1d ,0x03 }, {1700 ,0x2e ,0x03 }, {1750 ,0x3e ,0x03 }, + {1800 ,0x0e ,0x03 }, {1850 ,0x1e ,0x03 }, {1900 ,0x2f ,0x03 }, + {1950 ,0x3f ,0x03 }, {2000 ,0x0f ,0x03 }, {2050 ,0x40 ,0x03 }, + {2100 ,0x41 ,0x03 }, {2150 ,0x42 ,0x03 }, {2200 ,0x43 ,0x03 }, + {2250 ,0x44 ,0x03 }, {2300 ,0x45 ,0x01 }, {2350 ,0x46 ,0x01 }, + {2400 ,0x47 ,0x01 }, {2450 ,0x48 ,0x01 }, {2500 ,0x49 ,0x01 } +}; + +struct dw_dsi_ipk { +void __iomem *base; +void __iomem *base_phy; +struct clk *pllref_clk; +struct dw_mipi_dsi *dsi; +u32 lane_min_kbps; +u32 lane_max_kbps; +int range; +int in_div; +int loop_div; +}; + +#define dw_mipi_dsi_to_dw_dsi_ipk (target) \ + container_of(target, struct dw_dsi_ipk, dsi) + +static void dw_dsi_write (struct dw_dsi_ipk *dsi,u32 reg,u32 val) +{ +writel (val, dsi->base + reg); +} + +static u32 dw_dsi_read (struct dw_dsi_ipk *dsi,u32 reg) +{ +return readl (dsi->base + reg); +} + +static void dw_phy_write (struct dw_dsi_ipk *dsi,u32 reg,u32 val) +{ +writel (val, dsi->base_phy + reg); +} + +static void dw_dsi_phy_write_part (struct dw_dsi_ipk *dsi,u32 reg_address, +u32 data,u8 shift,u8 width) +{ +u32 temp =dw_dsi_read (dsi, reg_address); +u32 mask = (1 << width) -1 ; + + temp &= ~(mask << shift); + temp |= (data & mask) << shift; +dw_dsi_write (dsi, reg_address, temp); +} + +static void dw_dsi_phy_test_data_in (struct dw_dsi_ipk *dsi,u8 test_data) +{ +dw_dsi_phy_write_part (dsi,DW_DPHY_TST_CTRL1 , test_data,0 ,8 ); +} + +static void dw_dsi_phy_test_clock (struct dw_dsi_ipk *dsi,int value) +{ +dw_dsi_phy_write_part (dsi,DW_DPHY_TST_CTRL0 , value,1 ,1 ); +} + +static void dw_dsi_phy_test_en (struct dw_dsi_ipk *dsi,u8 on_falling_edge) +{ +dw_dsi_phy_write_part (dsi,DW_DPHY_TST_CTRL1 , on_falling_edge,16 ,1 ); +} + +static void dw_dsi_phy_test_clear (struct dw_dsi_ipk *dsi,int value) +{ +dw_dsi_phy_write_part (dsi,DW_DPHY_TST_CTRL0 , value,0 ,1 ); +} + +static void dw_dsi_phy_write (struct dw_dsi_ipk *dsi,u16 address, +u32 value,u8 data_length) +{ +u8 data[4 ]; +int i; + + data[0 ] = value; + +dw_dsi_write (dsi,DW_DPHY_TST_CTRL0 ,0 ); +dw_dsi_write (dsi,DW_DPHY_TST_CTRL1 ,0 ); + +dw_dsi_phy_test_en (dsi,1 ); +dw_dsi_phy_test_clock (dsi,1 ); +dw_dsi_phy_test_data_in (dsi,0x00 ); +dw_dsi_phy_test_clock (dsi,0 ); +dw_dsi_write (dsi,DW_DPHY_TST_CTRL1 ,0 ); +dw_dsi_write (dsi,DW_DPHY_TST_CTRL1 , (u8)(address >>8 )); +dw_dsi_phy_test_clock (dsi,1 ); +dw_dsi_phy_test_clock (dsi,0 ); +dw_dsi_phy_test_en (dsi,1 ); +dw_dsi_phy_test_clock (dsi,1 ); +dw_dsi_phy_test_data_in (dsi, ((u8 )address)); +dw_dsi_phy_test_clock (dsi,0 ); +dw_dsi_phy_test_en (dsi,0 ); + +for (i = data_length; i >0 ; i--) { +dw_dsi_phy_test_data_in (dsi, ((u8 )data[i -1 ])); +dw_dsi_phy_test_clock (dsi,1 ); +dw_dsi_phy_test_clock (dsi,0 ); + } +} + +static void dw_dsi_phy_delay (struct dw_dsi_ipk *dsi,int value) +{ +u32 data = value <<2 ; + +dw_dsi_phy_write (dsi,DW_12BITS_DPHY_RDY_L0 , data,1 ); +dw_dsi_phy_write (dsi,DW_12BITS_DPHY_RDY_L1 , data,1 ); +dw_dsi_phy_write (dsi,DW_12BITS_DPHY_RDY_L2 , data,1 ); +dw_dsi_phy_write (dsi,DW_12BITS_DPHY_RDY_L3 , data,1 ); +} + +static int dsi_pll_get_clkout_khz (int clkin_khz,int idf,int ndiv,int odf) +{ +int divisor = idf * odf; + +/* prevent from division by 0 */ +if (!divisor) +return 0 ; + +return DIV_ROUND_CLOSEST (clkin_khz * ndiv, divisor); +} + +static int dsi_pll_get_params (struct dw_dsi_ipk *dsi,int in_freq, +int out_freq,int *idf,int *ndiv,int *odf) +{ +int range, tmp_loop_div, tmp_in_freq, delta, step =0 , flag =0 ; +int out_data_rate = out_freq *2 ; +int loop_div =0 ;/* M */ +int out_div;/* VCO */ +int in_div;/* N */ + +/* Find ranges */ +for (range =0 ;ARRAY_SIZE (dw_range_gen3) && + (out_data_rate /1000 ) > dw_range_gen3[range].freq; range++) + ; + +if (range >=ARRAY_SIZE (dw_range_gen3)) +return -EINVAL ; + +if ((dw_range_gen3[range].osc_freq_target >>4 ) ==3 ) + out_div =8 ; +else if ((dw_range_gen3[range].osc_freq_target >>4 ) ==2 ) + out_div =4 ; +else + out_div =2 ; + +if (dw_range_gen3[range].freq >640 ) + out_div =1 ; + + out_freq = out_freq * out_div; + + loop_div = (out_freq * (in_freq /DW_DPHY_DIV_LOWER_LIMIT )) / in_freq; + +/* here delta will account for the rounding */ + delta = (loop_div * in_freq) / (in_freq /DW_DPHY_DIV_LOWER_LIMIT ) - + out_freq; + +for (in_div =1 + in_freq /DW_DPHY_DIV_UPPER_LIMIT ; + (in_freq / in_div >=DW_DPHY_DIV_LOWER_LIMIT ) && !flag; in_div++) { + tmp_loop_div = out_freq * in_div / in_freq; + tmp_in_freq = in_freq / in_div; +if (tmp_loop_div %2 ) { + tmp_loop_div +=1 ; +if (out_freq == tmp_loop_div * tmp_in_freq) { +/* Exact values found */ + flag =1 ; + loop_div = tmp_loop_div; + delta = tmp_loop_div * tmp_in_freq - out_freq; + in_div--; + }else if (tmp_loop_div * tmp_in_freq - out_freq < + delta) { +/* Values found with smaller delta */ + loop_div = tmp_loop_div; + delta = tmp_loop_div * tmp_in_freq - out_freq; + step =0 ; + } + }else if (out_freq == tmp_loop_div * tmp_in_freq) { +/* Exact values found */ + flag =1 ; + loop_div = tmp_loop_div; + delta = out_freq - tmp_loop_div * tmp_in_freq; + in_div--; + }else if (out_freq - tmp_loop_div * tmp_in_freq < delta) { +/* Values found with smaller delta */ + loop_div = tmp_loop_div; + delta = out_freq - tmp_loop_div * tmp_in_freq; + step =1 ; + } + } + +if (!flag) + in_div = step + loop_div * in_freq / out_freq; + + *idf = in_div; + *ndiv = loop_div; + *odf = out_div; + + dsi->range = range; + dsi->in_div = in_div; + dsi->loop_div = loop_div; + +return 0 ; +} + +/* DPHY GEN 3 12 bits */ +static void dw_phy_init_gen3_128 (void *priv_data) +{ +struct dw_dsi_ipk *dsi = priv_data; +int loop_div = dsi->loop_div; +int in_div = dsi->in_div; +int range = dsi->range; +u32 data; + +/* hs frequency range [6:0] */ + data = dw_range_gen3[range].hs_freq_range; +dw_dsi_phy_write (dsi,0x02 , data,1 ); + +/* [7:6] reserved | [5] hsfreqrange_ovr_en_rw | +* [4:1] target_state_rw | [0] force_state_rw +*/ +dw_dsi_phy_write (dsi,0x01 ,0x20 ,1 ); + +/* PLL Lock Configurations */ +dw_dsi_phy_write (dsi,0x173 ,0x02 ,1 ); +dw_dsi_phy_write (dsi,0x174 ,0x00 ,1 ); +dw_dsi_phy_write (dsi,0x175 ,0x60 ,1 ); +dw_dsi_phy_write (dsi,0x176 ,0x03 ,1 ); +dw_dsi_phy_write (dsi,0x166 ,0x01 ,1 ); + +/* Charge-pump Programmability */ +/* [7] pll_vco_cntrl_ovr_en | +* [6:1] pll_vco_cntrl_ovr | [0] pll_m_ovr_en +*/ +if (dw_range_gen3[range].freq >640 ) + data =1 | (dw_range_gen3[range].osc_freq_target <<1 ); +else + data =1 | (1 <<7 ) | + (dw_range_gen3[range].osc_freq_target <<1 ); + +dw_dsi_phy_write (dsi,0x17b , data,1 ); +dw_dsi_phy_write (dsi,0x15e ,0x10 ,1 ); +dw_dsi_phy_write (dsi,0x162 ,0x04 ,1 ); +dw_dsi_phy_write (dsi,0x16e ,0x0c ,1 ); + +/* Slew-Rate */ +dw_dsi_phy_write (dsi,0x26b ,0x04 ,1 ); + +/* pll_n_ovr_en_rw | PLL input divider ratio [6:3] | +* pll_tstplldig_rw +*/ + data = (1 <<7 ) | (in_div -1 ) <<3 ; +dw_dsi_phy_write (dsi,0x178 , data,1 ); + +/* PLL loop divider ratio [7:0] */ + data = loop_div -2 ; +dw_dsi_phy_write (dsi,0x179 , data,1 ); + +/* PLL loop divider ratio [9:8] */ + data = (loop_div -2 ) >>8 ; +dw_dsi_phy_write (dsi,0x17a , data,1 ); + +if (dw_range_gen3[range].freq <450 ) +dw_dsi_phy_write (dsi,0x1ac ,0x1b ,1 ); +else +dw_dsi_phy_write (dsi,0x1ac ,0x0b ,1 ); +} + +static int dw_mipi_dsi_phy_init (void *priv_data) +{ +struct dw_dsi_ipk *dsi = priv_data; +int range = dsi->range; +unsigned int in_freq; +u32 data; + + in_freq = (unsigned int )(clk_get_rate (dsi->pllref_clk) /1000 ); + +dw_phy_write (dsi,DW_GEN3_IF_TESTER ,RESET ); +dw_phy_write (dsi,DW_GEN3_IF_TESTER ,GLUE_LOGIC ); +dw_dsi_phy_test_clear (dsi,1 ); +dw_dsi_phy_test_clear (dsi,0 ); + +dw_dsi_phy_write (dsi,0x30 ,0x0f ,1 ); + + data = ((in_freq /1000 ) -17 ) *4 ; +dw_dsi_phy_write (dsi,0x02 , data,1 ); + +dw_dsi_phy_write (dsi,0x20 ,0x3f ,1 ); + +/* RESET RX */ +dw_phy_write (dsi,DW_GEN3_IF_TESTER ,RESET ); +dw_phy_write (dsi,DW_GEN3_IF_TESTER ,RX_PHY ); +dw_dsi_phy_test_clear (dsi,1 ); +dw_dsi_phy_test_clear (dsi,0 ); + +/* RESET TX */ +dw_phy_write (dsi,DW_GEN3_IF_TESTER ,RESET ); +dw_phy_write (dsi,DW_GEN3_IF_TESTER ,TX_PHY ); +dw_dsi_phy_test_clear (dsi,1 ); +dw_dsi_phy_test_clear (dsi,0 ); + +dw_phy_init_gen3_128 (priv_data); + +if (dw_range_gen3[range].freq >648 ) +dw_dsi_phy_delay (dsi,5 ); +else +dw_dsi_phy_delay (dsi,4 ); + +DRM_DEBUG_DRIVER ("Phy configured\n" ); + +return 0 ; +} + +static int +dw_mipi_dsi_get_lane_mbps (void *priv_data,const struct drm_display_mode *mode, +unsigned long mode_flags,u32 lanes,u32 format, +unsigned int *lane_mbps) +{ +int idf =0 , ndiv =0 , odf =0 , pll_in_khz, pll_out_khz, ret, bpp; +struct dw_dsi_ipk *dsi = priv_data; + +DRM_DEBUG_DRIVER ("\n" ); + + dsi->lane_min_kbps = (unsigned int )DW_LANE_MIN_KBPS ; + dsi->lane_max_kbps = (unsigned int )DW_LANE_MAX_KBPS ; + + pll_in_khz = (unsigned int )(clk_get_rate (dsi->pllref_clk) /1000 ); + +/* Compute requested pll out */ + bpp =mipi_dsi_pixel_format_to_bpp ((enum mipi_dsi_pixel_format )format); + pll_out_khz = ((mode->clock * bpp) / lanes) /2 ; + +if (pll_out_khz > dsi->lane_max_kbps) { + pll_out_khz = dsi->lane_max_kbps; +DRM_WARN ("Warning max phy mbps is used\n" ); + } + +if (pll_out_khz < dsi->lane_min_kbps) { + pll_out_khz = dsi->lane_min_kbps; +DRM_WARN ("Warning min phy mbps is used\n" ); + } + + ret =dsi_pll_get_params (dsi, pll_in_khz, pll_out_khz, + &idf, &ndiv, &odf); +if (ret) +DRM_WARN ("Warning dsi_pll_get_params(): bad params\n" ); + +/* Get the adjusted pll out value */ + pll_out_khz =dsi_pll_get_clkout_khz (pll_in_khz, idf, ndiv, odf); + + *lane_mbps = (pll_out_khz /1000 ) *2 ; + +DRM_DEBUG_DRIVER ("pll_in %ukHz pll_out %ukHz lane_mbps %uMHz\n" , + pll_in_khz, pll_out_khz, *lane_mbps); + +return ret; +} + +static int +dw_mipi_dsi_phy_get_timing (void *priv_data,unsigned int lane_mbps, +struct dw_mipi_dsi_dphy_timing *timing) +{ + timing->clk_hs2lp =DW_LPHS_TIM_TRANSIONS ; + timing->clk_lp2hs =DW_LPHS_TIM_TRANSIONS ; + timing->data_hs2lp =DW_LPHS_TIM_TRANSIONS ; + timing->data_lp2hs =DW_LPHS_TIM_TRANSIONS ; + +return 0 ; +} + +static const struct dw_mipi_dsi_phy_ops dw_dsi_ipk_phy_ops = { + .init = dw_mipi_dsi_phy_init, + .get_lane_mbps = dw_mipi_dsi_get_lane_mbps, + .get_timing = dw_mipi_dsi_phy_get_timing, +}; + +static struct dw_mipi_dsi_plat_data dw_dsi_ipk_plat_data = { + .max_data_lanes =4 , + .phy_ops = &dw_dsi_ipk_phy_ops, +}; + +static const struct of_device_id dw_ipk_dt_ids[] = { + {.compatible ="snps,dw-ipk-dsi" , + .data = &dw_dsi_ipk_plat_data,}, + { }, +}; + +MODULE_DEVICE_TABLE (of, dw_ipk_dt_ids); + +static int dw_dsi_ipk_probe (struct platform_device *pdev) +{ +struct device *dev = &pdev->dev; +struct dw_dsi_ipk *dsi; +struct resource *res; +struct clk *pclk; +int ret; + +DRM_DEBUG_DRIVER ("\n" ); + + dsi =devm_kzalloc (dev,sizeof (*dsi),GFP_KERNEL ); +if (!dsi) +return -ENOMEM ; + + res =platform_get_resource_byname (pdev,IORESOURCE_MEM ,"dsi" ); + dsi->base =devm_ioremap_resource (dev, res); +if (IS_ERR (dsi->base)) { + ret =PTR_ERR (dsi->base); +DRM_ERROR ("Unable to get dsi registers %d\n" , ret); +return ret; + } + + res =platform_get_resource_byname (pdev,IORESOURCE_MEM ,"phy" ); + dsi->base_phy =devm_ioremap_resource (dev, res); +if (IS_ERR (dsi->base_phy)) { + ret =PTR_ERR (dsi->base_phy); +DRM_ERROR ("Unable to get PHY registers %d\n" , ret); +return ret; + } + + pclk =devm_clk_get (dev,"pclk" ); +if (IS_ERR (pclk)) { + ret =PTR_ERR (pclk); +DRM_ERROR ("Unable to get peripheral clock: %d\n" , ret); + goto err_dsi_probe; + } + + ret =clk_prepare_enable (pclk); +if (ret) + goto err_dsi_probe; + + dsi->pllref_clk =devm_clk_get (dev,"ref" ); +if (IS_ERR (dsi->pllref_clk)) { + ret =PTR_ERR (dsi->pllref_clk); +DRM_ERROR ("Unable to get pll reference clock: %d\n" , ret); +return ret; + } + + ret =clk_prepare_enable (dsi->pllref_clk); +if (ret) +return ret; + + dw_dsi_ipk_plat_data.base = dsi->base; + dw_dsi_ipk_plat_data.priv_data = dsi; + +platform_set_drvdata (pdev, dsi); + + dsi->dsi =dw_mipi_dsi_probe (pdev, &dw_dsi_ipk_plat_data); +if (IS_ERR (dsi->dsi)) { + ret =PTR_ERR (dsi->dsi); +DRM_ERROR ("Failed to initialize mipi dsi host: %d\n" , ret); + goto err_dsi_probe; + } + +return ret; + +err_dsi_probe: +clk_disable_unprepare (dsi->pllref_clk); +return ret; +} + +static int dw_dsi_ipk_remove (struct platform_device *pdev) +{ +struct dw_dsi_ipk *dsi =platform_get_drvdata (pdev); + +dw_mipi_dsi_remove (dsi->dsi); + +return 0 ; +} + +struct platform_driver dw_mipi_dsi_ipk_driver = { + .probe = dw_dsi_ipk_probe, + .remove = dw_dsi_ipk_remove, + .driver = { + .name ="ipk-dw-mipi-dsi" , + .of_match_table = dw_ipk_dt_ids, + }, +}; + +module_platform_driver (dw_mipi_dsi_ipk_driver); + +MODULE_AUTHOR ("Angelo Ribeiro <[email protected]>" ); +MODULE_AUTHOR ("Luis Oliveira <[email protected]>" ); +MODULE_DESCRIPTION ("Synopsys IPK DW MIPI DSI host controller driver" ); +MODULE_LICENSE ("GPL v2" );Signature · 1 line
2.7.4
Signature · 7 lines
Daniel Vetter Software Engineer, Intel Corporation http://blog.ffwll.ch _______________________________________________ dri-devel mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/dri-devel
RE: [PATCH v3 4/4] MAINTAINERS: Add IPK MIPI DSI Host driver entry
In reply to Joe PerchesFrom: Joe Perches <[email protected]>
Date: Mon, Apr 27, 2020 at 15:45:17
Quoted 22 lines · Angelo Ribeiro
Quoted 2 lines
Creates entry for Synopsys DesignWare IPK DRM driver and adds myself as maintainer.
[]
MAINTAINERS
Quoted 1 line
MAINTAINERS +0
-0[]
MAINTAINERS T: git git://anongit.freedesktop.org/drm/drm-misc · line 5516
Quoted 11 lines
MAINTAINERS +7
-0@@ -5507,6 +5507,14 @@ T: git git://anongit.freedesktop.org/drm/drm-misc F: Documentation/devicetree/bindings/display/ste,mcde.txt F: drivers/gpu/drm/mcde/ +DRM DRIVER FOR SYNOPSYS DESIGNWARE IPK +M: Angelo Ribeiro <[email protected]> +L: [email protected] +S: Maintained +F: drivers/gpu/drm/ipk/ +F: Documentation/devicetree/bindings/display/ipk/ +T: git git://anongit.freedesktop.org/drm/drm-miscThere is now a preferred order for the entries in a section.
Please use:
Quoted 8 lines
DRM DRIVER FOR SYNOPSYS DESIGNWARE IPK
M: Angelo Ribeiro <[email protected]>
L: [email protected]>
S: Maintained
T: gitgit://anongit.freedesktop.org/drm/drm-misc
F: Document
ation/devicetree/bindings/display/ipk/>
F:drivers/gpu/drm/ipk/
Hi Joe,
Thanks for the review I will apply it.
Angelo
RE: [PATCH v3 3/4] drm: ipk: Add extensions for DW MIPI DSI Host driver
In reply to Daniel VetterFrom: Daniel Vetter <[email protected]>
Date: Tue, Apr 28, 2020 at 16:28:15
Quoted 28 lines · Angelo Ribeiro
Quoted 11 lines
Add Synopsys DesignWare IPK specific extensions for Synopsys DesignWare MIPI DSI Host driver.
Cc Maarten Lankhorst <[email protected]>Cc Maxime Ripard <[email protected]>Cc David Airlie <[email protected]>Cc Daniel Vetter <[email protected]>Cc Sam Ravnborg <[email protected]>Cc Gustavo Pimentel <[email protected]>Cc Joao Pinto <[email protected]>Signed-off-by Angelo Ribeiro <[email protected]>I've dumped this on a pile of bridge drivers by now, but I don't think the dw-mipi-dsi organization makes much sense.
I think what we'd need is:
-
drm_encoderis handled by thedrm_devicedriver, not by dw-mipi-dsidrm_bridgedriver- the glue code for the various soc specific implementations (like ipk here) should be put behind the
drm_bridgeabstraction. Otherwise I'm not really seeing why exactly dw-mipi-dsi is a bridge driver if it doesn't work like a bridge driver- Probably we should put all these files into drm/bridge/dw-mipi-dsi/
Quoted 7 lines
-
drm_devicedrivers should get at their bridges with one of the standard of helpers we have indrm_bridge, not by directly calling into a bridge drivers.I know that dw-hdmi is using the exact same code pattern, but we got to stop this eventually or it becomes an unfixable mess. -Daniel
Hi Daniel,
Sorry for the late answer.
I understand what you stated and the conversion of this driver in a help library could be a good solution since you can use the DSI as bridge or as encoder, as your pipeline requires.
Also most of the code implemented by each glue is essential PHY related, the development of a PHY driver could make this more clear.
However, this needs a lot of work and consensus. Do you think that we can go ahead with this driver and do the rework later?
I'm available and interested to help on this rework.
Thanks,
Angelo
Quoted 609 lines
Quoted 604 lines
---
Changes since v3:
- Rearranged headers.--- drivers/gpu/drm/ipk/Kconfig | 9 + drivers/gpu/drm/ipk/Makefile | 2 + drivers/gpu/drm/ipk/dw-mipi-dsi-ipk.c | 557 ++++++++++++++++++++++++++++++++++ 3 files changed, 568 insertions(+) create mode 100644 drivers/gpu/drm/ipk/dw-mipi-dsi-ipk.cdrivers/gpu/drm/ipk/Kconfig +9
-0@@ -11,3 +11,12 @@ config DRM_IPK Enable support for the Synopsys DesignWare DRM DSI. To compile this driver as a module, choose M here: the module will be called ipk-drm. + +config DRM_IPK_DSI + tristate "Synopsys DesignWare IPK specific extensions for MIPI DSI" + depends on DRM_IPK + select DRM_DW_MIPI_DSI + help + Choose this option for Synopsys DesignWare IPK MIPI DSI support. + To compile this driver as a module, choose M here: the module + will be called dw-mipi-dsi-ipk.drivers/gpu/drm/ipk/Makefile +2
-0@@ -2,3 +2,5 @@ ipk-drm-y := dw-drv.o dw-vpg.o obj-$(CONFIG_DRM_IPK) += ipk-drm.o + +obj-$(CONFIG_DRM_IPK_DSI) += dw-mipi-dsi-ipk.odrivers/gpu/drm/ipk/dw-mipi-dsi-ipk.c new file +557
-0@@ -0,0 +1,557 @@ +// SPDX-License-Identifier: GPL-2.0 +/* +* Copyright (c) 2019-2020 Synopsys, Inc. and/or its affiliates. +* Synopsys DesignWare MIPI DSI solution driver +* +* Author: Angelo Ribeiro <[email protected]> +* Author: Luis Oliveira <[email protected]> +*/ + +#include <linux/clk.h> +#include <linux/iopoll.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> + +#include <video/mipi_display.h> + +#include <drm/bridge/dw_mipi_dsi.h> +#include <drm/drm_crtc.h> +#include <drm/drm_device.h> +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_print.h> + +#define DW_DPHY_LPCLK_CTRL 0x94 +#define DW_DPHY_RSTZ 0xA0 +#define DW_DPHY_IF_CFG 0xA4 +#define DW_DPHY_ULPS_CTRL 0xA8 +#define DW_DPHY_TX_TRIGGERS 0xAC +#define DW_DPHY_STATUS 0xB0 +#define DW_DPHY_TST_CTRL0 0xB4 +#define DW_DPHY_TST_CTRL1 0xB8 +#define DW_GEN3_IF_TESTER 0x3c +#define DW_GEN3_IF_SOC_PLL 0x48 +#define DW_GEN3_IF_SOC_PLL_EN 0x4C + +#define DW_12BITS_DPHY_RDY_L0 0x507 +#define DW_12BITS_DPHY_RDY_L1 0x707 +#define DW_12BITS_DPHY_RDY_L2 0x907 +#define DW_12BITS_DPHY_RDY_L3 0xB07 + +#define DW_LANE_MIN_KBPS 80000 +#define DW_LANE_MAX_KBPS 2500000000 +#define DW_DPHY_DIV_UPPER_LIMIT 8000 +#define DW_DPHY_DIV_LOWER_LIMIT 2000 +#define DW_MIN_OUTPUT_FREQ 80 +#define DW_LPHS_TIM_TRANSIONS 0x40 + +enum dw_glueiftester { +GLUE_LOGIC =0x4 , +RX_PHY =0x2 , +TX_PHY =0x1 , +RESET =0x0 , +}; + +struct dw_range_dphy { +u32 freq; +u8 hs_freq_range; +u32 osc_freq_target; +} dw_range_gen3[] = { + {80 ,0x00 ,0x3f }, {90 ,0x10 ,0x3f }, {100 ,0x20 ,0x3f }, + {110 ,0x30 ,0x39 }, {120 ,0x01 ,0x39 }, {130 ,0x11 ,0x39 }, + {140 ,0x21 ,0x39 }, {150 ,0x31 ,0x39 }, {160 ,0x02 ,0x39 }, + {170 ,0x12 ,0x2f }, {180 ,0x22 ,0x2f }, {190 ,0x32 ,0x2f }, + {205 ,0x03 ,0x2f }, {220 ,0x13 ,0x29 }, {235 ,0x23 ,0x29 }, + {250 ,0x33 ,0x29 }, {275 ,0x04 ,0x29 }, {300 ,0x14 ,0x29 }, + {325 ,0x25 ,0x29 }, {350 ,0x35 ,0x1f }, {400 ,0x05 ,0x1f }, + {450 ,0x16 ,0x19 }, {500 ,0x26 ,0x19 }, {550 ,0x37 ,0x19 }, + {600 ,0x07 ,0x19 }, {650 ,0x18 ,0x19 }, {700 ,0x28 ,0x0f }, + {750 ,0x39 ,0x0f }, {800 ,0x09 ,0x0f }, {850 ,0x19 ,0x0f }, + {900 ,0x29 ,0x09 }, {950 ,0x3a ,0x09 }, {1000 ,0x0a ,0x09 }, + {1050 ,0x1a ,0x09 }, {1100 ,0x2a ,0x09 }, {1150 ,0x3b ,0x09 }, + {1200 ,0x0b ,0x09 }, {1250 ,0x1b ,0x09 }, {1300 ,0x2b ,0x09 }, + {1350 ,0x3c ,0x03 }, {1400 ,0x0c ,0x03 }, {1450 ,0x1c ,0x03 }, + {1500 ,0x2c ,0x03 }, {1550 ,0x3d ,0x03 }, {1600 ,0x0d ,0x03 }, + {1650 ,0x1d ,0x03 }, {1700 ,0x2e ,0x03 }, {1750 ,0x3e ,0x03 }, + {1800 ,0x0e ,0x03 }, {1850 ,0x1e ,0x03 }, {1900 ,0x2f ,0x03 }, + {1950 ,0x3f ,0x03 }, {2000 ,0x0f ,0x03 }, {2050 ,0x40 ,0x03 }, + {2100 ,0x41 ,0x03 }, {2150 ,0x42 ,0x03 }, {2200 ,0x43 ,0x03 }, + {2250 ,0x44 ,0x03 }, {2300 ,0x45 ,0x01 }, {2350 ,0x46 ,0x01 }, + {2400 ,0x47 ,0x01 }, {2450 ,0x48 ,0x01 }, {2500 ,0x49 ,0x01 } +}; + +struct dw_dsi_ipk { +void __iomem *base; +void __iomem *base_phy; +struct clk *pllref_clk; +struct dw_mipi_dsi *dsi; +u32 lane_min_kbps; +u32 lane_max_kbps; +int range; +int in_div; +int loop_div; +}; + +#define dw_mipi_dsi_to_dw_dsi_ipk (target) \ + container_of(target, struct dw_dsi_ipk, dsi) + +static void dw_dsi_write (struct dw_dsi_ipk *dsi,u32 reg,u32 val) +{ +writel (val, dsi->base + reg); +} + +static u32 dw_dsi_read (struct dw_dsi_ipk *dsi,u32 reg) +{ +return readl (dsi->base + reg); +} + +static void dw_phy_write (struct dw_dsi_ipk *dsi,u32 reg,u32 val) +{ +writel (val, dsi->base_phy + reg); +} + +static void dw_dsi_phy_write_part (struct dw_dsi_ipk *dsi,u32 reg_address, +u32 data,u8 shift,u8 width) +{ +u32 temp =dw_dsi_read (dsi, reg_address); +u32 mask = (1 << width) -1 ; + + temp &= ~(mask << shift); + temp |= (data & mask) << shift; +dw_dsi_write (dsi, reg_address, temp); +} + +static void dw_dsi_phy_test_data_in (struct dw_dsi_ipk *dsi,u8 test_data) +{ +dw_dsi_phy_write_part (dsi,DW_DPHY_TST_CTRL1 , test_data,0 ,8 ); +} + +static void dw_dsi_phy_test_clock (struct dw_dsi_ipk *dsi,int value) +{ +dw_dsi_phy_write_part (dsi,DW_DPHY_TST_CTRL0 , value,1 ,1 ); +} + +static void dw_dsi_phy_test_en (struct dw_dsi_ipk *dsi,u8 on_falling_edge) +{ +dw_dsi_phy_write_part (dsi,DW_DPHY_TST_CTRL1 , on_falling_edge,16 ,1 ); +} + +static void dw_dsi_phy_test_clear (struct dw_dsi_ipk *dsi,int value) +{ +dw_dsi_phy_write_part (dsi,DW_DPHY_TST_CTRL0 , value,0 ,1 ); +} + +static void dw_dsi_phy_write (struct dw_dsi_ipk *dsi,u16 address, +u32 value,u8 data_length) +{ +u8 data[4 ]; +int i; + + data[0 ] = value; + +dw_dsi_write (dsi,DW_DPHY_TST_CTRL0 ,0 ); +dw_dsi_write (dsi,DW_DPHY_TST_CTRL1 ,0 ); + +dw_dsi_phy_test_en (dsi,1 ); +dw_dsi_phy_test_clock (dsi,1 ); +dw_dsi_phy_test_data_in (dsi,0x00 ); +dw_dsi_phy_test_clock (dsi,0 ); +dw_dsi_write (dsi,DW_DPHY_TST_CTRL1 ,0 ); +dw_dsi_write (dsi,DW_DPHY_TST_CTRL1 , (u8)(address >>8 )); +dw_dsi_phy_test_clock (dsi,1 ); +dw_dsi_phy_test_clock (dsi,0 ); +dw_dsi_phy_test_en (dsi,1 ); +dw_dsi_phy_test_clock (dsi,1 ); +dw_dsi_phy_test_data_in (dsi, ((u8 )address)); +dw_dsi_phy_test_clock (dsi,0 ); +dw_dsi_phy_test_en (dsi,0 ); + +for (i = data_length; i >0 ; i--) { +dw_dsi_phy_test_data_in (dsi, ((u8 )data[i -1 ])); +dw_dsi_phy_test_clock (dsi,1 ); +dw_dsi_phy_test_clock (dsi,0 ); + } +} + +static void dw_dsi_phy_delay (struct dw_dsi_ipk *dsi,int value) +{ +u32 data = value <<2 ; + +dw_dsi_phy_write (dsi,DW_12BITS_DPHY_RDY_L0 , data,1 ); +dw_dsi_phy_write (dsi,DW_12BITS_DPHY_RDY_L1 , data,1 ); +dw_dsi_phy_write (dsi,DW_12BITS_DPHY_RDY_L2 , data,1 ); +dw_dsi_phy_write (dsi,DW_12BITS_DPHY_RDY_L3 , data,1 ); +} + +static int dsi_pll_get_clkout_khz (int clkin_khz,int idf,int ndiv,int odf) +{ +int divisor = idf * odf; + +/* prevent from division by 0 */ +if (!divisor) +return 0 ; + +return DIV_ROUND_CLOSEST (clkin_khz * ndiv, divisor); +} + +static int dsi_pll_get_params (struct dw_dsi_ipk *dsi,int in_freq, +int out_freq,int *idf,int *ndiv,int *odf) +{ +int range, tmp_loop_div, tmp_in_freq, delta, step =0 , flag =0 ; +int out_data_rate = out_freq *2 ; +int loop_div =0 ;/* M */ +int out_div;/* VCO */ +int in_div;/* N */ + +/* Find ranges */ +for (range =0 ;ARRAY_SIZE (dw_range_gen3) && + (out_data_rate /1000 ) > dw_range_gen3[range].freq; range++) + ; + +if (range >=ARRAY_SIZE (dw_range_gen3)) +return -EINVAL ; + +if ((dw_range_gen3[range].osc_freq_target >>4 ) ==3 ) + out_div =8 ; +else if ((dw_range_gen3[range].osc_freq_target >>4 ) ==2 ) + out_div =4 ; +else + out_div =2 ; + +if (dw_range_gen3[range].freq >640 ) + out_div =1 ; + + out_freq = out_freq * out_div; + + loop_div = (out_freq * (in_freq /DW_DPHY_DIV_LOWER_LIMIT )) / in_freq; + +/* here delta will account for the rounding */ + delta = (loop_div * in_freq) / (in_freq /DW_DPHY_DIV_LOWER_LIMIT ) - + out_freq; + +for (in_div =1 + in_freq /DW_DPHY_DIV_UPPER_LIMIT ; + (in_freq / in_div >=DW_DPHY_DIV_LOWER_LIMIT ) && !flag; in_div++) { + tmp_loop_div = out_freq * in_div / in_freq; + tmp_in_freq = in_freq / in_div; +if (tmp_loop_div %2 ) { + tmp_loop_div +=1 ; +if (out_freq == tmp_loop_div * tmp_in_freq) { +/* Exact values found */ + flag =1 ; + loop_div = tmp_loop_div; + delta = tmp_loop_div * tmp_in_freq - out_freq; + in_div--; + }else if (tmp_loop_div * tmp_in_freq - out_freq < + delta) { +/* Values found with smaller delta */ + loop_div = tmp_loop_div; + delta = tmp_loop_div * tmp_in_freq - out_freq; + step =0 ; + } + }else if (out_freq == tmp_loop_div * tmp_in_freq) { +/* Exact values found */ + flag =1 ; + loop_div = tmp_loop_div; + delta = out_freq - tmp_loop_div * tmp_in_freq; + in_div--; + }else if (out_freq - tmp_loop_div * tmp_in_freq < delta) { +/* Values found with smaller delta */ + loop_div = tmp_loop_div; + delta = out_freq - tmp_loop_div * tmp_in_freq; + step =1 ; + } + } + +if (!flag) + in_div = step + loop_div * in_freq / out_freq; + + *idf = in_div; + *ndiv = loop_div; + *odf = out_div; + + dsi->range = range; + dsi->in_div = in_div; + dsi->loop_div = loop_div; + +return 0 ; +} + +/* DPHY GEN 3 12 bits */ +static void dw_phy_init_gen3_128 (void *priv_data) +{ +struct dw_dsi_ipk *dsi = priv_data; +int loop_div = dsi->loop_div; +int in_div = dsi->in_div; +int range = dsi->range; +u32 data; + +/* hs frequency range [6:0] */ + data = dw_range_gen3[range].hs_freq_range; +dw_dsi_phy_write (dsi,0x02 , data,1 ); + +/* [7:6] reserved | [5] hsfreqrange_ovr_en_rw | +* [4:1] target_state_rw | [0] force_state_rw +*/ +dw_dsi_phy_write (dsi,0x01 ,0x20 ,1 ); + +/* PLL Lock Configurations */ +dw_dsi_phy_write (dsi,0x173 ,0x02 ,1 ); +dw_dsi_phy_write (dsi,0x174 ,0x00 ,1 ); +dw_dsi_phy_write (dsi,0x175 ,0x60 ,1 ); +dw_dsi_phy_write (dsi,0x176 ,0x03 ,1 ); +dw_dsi_phy_write (dsi,0x166 ,0x01 ,1 ); + +/* Charge-pump Programmability */ +/* [7] pll_vco_cntrl_ovr_en | +* [6:1] pll_vco_cntrl_ovr | [0] pll_m_ovr_en +*/ +if (dw_range_gen3[range].freq >640 ) + data =1 | (dw_range_gen3[range].osc_freq_target <<1 ); +else + data =1 | (1 <<7 ) | + (dw_range_gen3[range].osc_freq_target <<1 ); + +dw_dsi_phy_write (dsi,0x17b , data,1 ); +dw_dsi_phy_write (dsi,0x15e ,0x10 ,1 ); +dw_dsi_phy_write (dsi,0x162 ,0x04 ,1 ); +dw_dsi_phy_write (dsi,0x16e ,0x0c ,1 ); + +/* Slew-Rate */ +dw_dsi_phy_write (dsi,0x26b ,0x04 ,1 ); + +/* pll_n_ovr_en_rw | PLL input divider ratio [6:3] | +* pll_tstplldig_rw +*/ + data = (1 <<7 ) | (in_div -1 ) <<3 ; +dw_dsi_phy_write (dsi,0x178 , data,1 ); + +/* PLL loop divider ratio [7:0] */ + data = loop_div -2 ; +dw_dsi_phy_write (dsi,0x179 , data,1 ); + +/* PLL loop divider ratio [9:8] */ + data = (loop_div -2 ) >>8 ; +dw_dsi_phy_write (dsi,0x17a , data,1 ); + +if (dw_range_gen3[range].freq <450 ) +dw_dsi_phy_write (dsi,0x1ac ,0x1b ,1 ); +else +dw_dsi_phy_write (dsi,0x1ac ,0x0b ,1 ); +} + +static int dw_mipi_dsi_phy_init (void *priv_data) +{ +struct dw_dsi_ipk *dsi = priv_data; +int range = dsi->range; +unsigned int in_freq; +u32 data; + + in_freq = (unsigned int )(clk_get_rate (dsi->pllref_clk) /1000 ); + +dw_phy_write (dsi,DW_GEN3_IF_TESTER ,RESET ); +dw_phy_write (dsi,DW_GEN3_IF_TESTER ,GLUE_LOGIC ); +dw_dsi_phy_test_clear (dsi,1 ); +dw_dsi_phy_test_clear (dsi,0 ); + +dw_dsi_phy_write (dsi,0x30 ,0x0f ,1 ); + + data = ((in_freq /1000 ) -17 ) *4 ; +dw_dsi_phy_write (dsi,0x02 , data,1 ); + +dw_dsi_phy_write (dsi,0x20 ,0x3f ,1 ); + +/* RESET RX */ +dw_phy_write (dsi,DW_GEN3_IF_TESTER ,RESET ); +dw_phy_write (dsi,DW_GEN3_IF_TESTER ,RX_PHY ); +dw_dsi_phy_test_clear (dsi,1 ); +dw_dsi_phy_test_clear (dsi,0 ); + +/* RESET TX */ +dw_phy_write (dsi,DW_GEN3_IF_TESTER ,RESET ); +dw_phy_write (dsi,DW_GEN3_IF_TESTER ,TX_PHY ); +dw_dsi_phy_test_clear (dsi,1 ); +dw_dsi_phy_test_clear (dsi,0 ); + +dw_phy_init_gen3_128 (priv_data); + +if (dw_range_gen3[range].freq >648 ) +dw_dsi_phy_delay (dsi,5 ); +else +dw_dsi_phy_delay (dsi,4 ); + +DRM_DEBUG_DRIVER ("Phy configured\n" ); + +return 0 ; +} + +static int +dw_mipi_dsi_get_lane_mbps (void *priv_data,const struct drm_display_mode *mode, +unsigned long mode_flags,u32 lanes,u32 format, +unsigned int *lane_mbps) +{ +int idf =0 , ndiv =0 , odf =0 , pll_in_khz, pll_out_khz, ret, bpp; +struct dw_dsi_ipk *dsi = priv_data; + +DRM_DEBUG_DRIVER ("\n" ); + + dsi->lane_min_kbps = (unsigned int )DW_LANE_MIN_KBPS ; + dsi->lane_max_kbps = (unsigned int )DW_LANE_MAX_KBPS ; + + pll_in_khz = (unsigned int )(clk_get_rate (dsi->pllref_clk) /1000 ); + +/* Compute requested pll out */ + bpp =mipi_dsi_pixel_format_to_bpp ((enum mipi_dsi_pixel_format )format); + pll_out_khz = ((mode->clock * bpp) / lanes) /2 ; + +if (pll_out_khz > dsi->lane_max_kbps) { + pll_out_khz = dsi->lane_max_kbps; +DRM_WARN ("Warning max phy mbps is used\n" ); + } + +if (pll_out_khz < dsi->lane_min_kbps) { + pll_out_khz = dsi->lane_min_kbps; +DRM_WARN ("Warning min phy mbps is used\n" ); + } + + ret =dsi_pll_get_params (dsi, pll_in_khz, pll_out_khz, + &idf, &ndiv, &odf); +if (ret) +DRM_WARN ("Warning dsi_pll_get_params(): bad params\n" ); + +/* Get the adjusted pll out value */ + pll_out_khz =dsi_pll_get_clkout_khz (pll_in_khz, idf, ndiv, odf); + + *lane_mbps = (pll_out_khz /1000 ) *2 ; + +DRM_DEBUG_DRIVER ("pll_in %ukHz pll_out %ukHz lane_mbps %uMHz\n" , + pll_in_khz, pll_out_khz, *lane_mbps); + +return ret; +} + +static int +dw_mipi_dsi_phy_get_timing (void *priv_data,unsigned int lane_mbps, +struct dw_mipi_dsi_dphy_timing *timing) +{ + timing->clk_hs2lp =DW_LPHS_TIM_TRANSIONS ; + timing->clk_lp2hs =DW_LPHS_TIM_TRANSIONS ; + timing->data_hs2lp =DW_LPHS_TIM_TRANSIONS ; + timing->data_lp2hs =DW_LPHS_TIM_TRANSIONS ; + +return 0 ; +} + +static const struct dw_mipi_dsi_phy_ops dw_dsi_ipk_phy_ops = { + .init = dw_mipi_dsi_phy_init, + .get_lane_mbps = dw_mipi_dsi_get_lane_mbps, + .get_timing = dw_mipi_dsi_phy_get_timing, +}; + +static struct dw_mipi_dsi_plat_data dw_dsi_ipk_plat_data = { + .max_data_lanes =4 , + .phy_ops = &dw_dsi_ipk_phy_ops, +}; + +static const struct of_device_id dw_ipk_dt_ids[] = { + {.compatible ="snps,dw-ipk-dsi" , + .data = &dw_dsi_ipk_plat_data,}, + { }, +}; + +MODULE_DEVICE_TABLE (of, dw_ipk_dt_ids); + +static int dw_dsi_ipk_probe (struct platform_device *pdev) +{ +struct device *dev = &pdev->dev; +struct dw_dsi_ipk *dsi; +struct resource *res; +struct clk *pclk; +int ret; + +DRM_DEBUG_DRIVER ("\n" ); + + dsi =devm_kzalloc (dev,sizeof (*dsi),GFP_KERNEL ); +if (!dsi) +return -ENOMEM ; + + res =platform_get_resource_byname (pdev,IORESOURCE_MEM ,"dsi" ); + dsi->base =devm_ioremap_resource (dev, res); +if (IS_ERR (dsi->base)) { + ret =PTR_ERR (dsi->base); +DRM_ERROR ("Unable to get dsi registers %d\n" , ret); +return ret; + } + + res =platform_get_resource_byname (pdev,IORESOURCE_MEM ,"phy" ); + dsi->base_phy =devm_ioremap_resource (dev, res); +if (IS_ERR (dsi->base_phy)) { + ret =PTR_ERR (dsi->base_phy); +DRM_ERROR ("Unable to get PHY registers %d\n" , ret); +return ret; + } + + pclk =devm_clk_get (dev,"pclk" ); +if (IS_ERR (pclk)) { + ret =PTR_ERR (pclk); +DRM_ERROR ("Unable to get peripheral clock: %d\n" , ret); + goto err_dsi_probe; + } + + ret =clk_prepare_enable (pclk); +if (ret) + goto err_dsi_probe; + + dsi->pllref_clk =devm_clk_get (dev,"ref" ); +if (IS_ERR (dsi->pllref_clk)) { + ret =PTR_ERR (dsi->pllref_clk); +DRM_ERROR ("Unable to get pll reference clock: %d\n" , ret); +return ret; + } + + ret =clk_prepare_enable (dsi->pllref_clk); +if (ret) +return ret; + + dw_dsi_ipk_plat_data.base = dsi->base; + dw_dsi_ipk_plat_data.priv_data = dsi; + +platform_set_drvdata (pdev, dsi); + + dsi->dsi =dw_mipi_dsi_probe (pdev, &dw_dsi_ipk_plat_data); +if (IS_ERR (dsi->dsi)) { + ret =PTR_ERR (dsi->dsi); +DRM_ERROR ("Failed to initialize mipi dsi host: %d\n" , ret); + goto err_dsi_probe; + } + +return ret; + +err_dsi_probe: +clk_disable_unprepare (dsi->pllref_clk); +return ret; +} + +static int dw_dsi_ipk_remove (struct platform_device *pdev) +{ +struct dw_dsi_ipk *dsi =platform_get_drvdata (pdev); + +dw_mipi_dsi_remove (dsi->dsi); + +return 0 ; +} + +struct platform_driver dw_mipi_dsi_ipk_driver = { + .probe = dw_dsi_ipk_probe, + .remove = dw_dsi_ipk_remove, + .driver = { + .name ="ipk-dw-mipi-dsi" , + .of_match_table = dw_ipk_dt_ids, + }, +}; + +module_platform_driver (dw_mipi_dsi_ipk_driver); + +MODULE_AUTHOR ("Angelo Ribeiro <[email protected]>" ); +MODULE_AUTHOR ("Luis Oliveira <[email protected]>" ); +MODULE_DESCRIPTION ("Synopsys IPK DW MIPI DSI host controller driver" ); +MODULE_LICENSE ("GPL v2" );Signature · 1 line
2.7.4Signature · 3 lines
Daniel Vetter Software Engineer, Intel Corporation https://urldefense.com/v3/__http://blog.ffwll.ch__;!!A4F2R9G_pg!Oe5QZPns5hZvX0GBstRz9Z4N97n6VR1TRkC51lVae1b5HxhrNe8W9QtqzyYCv2JaM5-vjQ$
Re: [PATCH v3 3/4] drm: ipk: Add extensions for DW MIPI DSI Host driver
Quoted 48 lines · Angelo Ribeiro
From: Daniel Vetter <[email protected]>
Date: Tue, Apr 28, 2020 at 16:28:15Quoted 28 lines · Angelo Ribeiro
Quoted 11 lines
Add Synopsys DesignWare IPK specific extensions for Synopsys DesignWare MIPI DSI Host driver.
Cc Maarten Lankhorst <[email protected]>Cc Maxime Ripard <[email protected]>Cc David Airlie <[email protected]>Cc Daniel Vetter <[email protected]>Cc Sam Ravnborg <[email protected]>Cc Gustavo Pimentel <[email protected]>Cc Joao Pinto <[email protected]>Signed-off-by Angelo Ribeiro <[email protected]>I've dumped this on a pile of bridge drivers by now, but I don't think the dw-mipi-dsi organization makes much sense.
I think what we'd need is:
-
drm_encoderis handled by thedrm_devicedriver, not by dw-mipi-dsidrm_bridgedriver- the glue code for the various soc specific implementations (like ipk here) should be put behind the
drm_bridgeabstraction. Otherwise I'm not really seeing why exactly dw-mipi-dsi is a bridge driver if it doesn't work like a bridge driver- Probably we should put all these files into drm/bridge/dw-mipi-dsi/
Quoted 7 lines
-
drm_devicedrivers should get at their bridges with one of the standard of helpers we have indrm_bridge, not by directly calling into a bridge drivers.I know that dw-hdmi is using the exact same code pattern, but we got to stop this eventually or it becomes an unfixable mess. -Daniel
Hi Daniel,
Sorry for the late answer.
I understand what you stated and the conversion of this driver in a help library could be a good solution since you can use the DSI as bridge or as encoder, as your pipeline requires.
Quoted 6 lines
Also most of the code implemented by each glue is essential PHY related, the development of a PHY driver could make this more clear.
However, this needs a lot of work and consensus. Do you think that we can go ahead with this driver and do the rework later?
I'm available and interested to help on this rework.
There's a bunch of these in the works right now, so minimally we need to make sure that we do actually have consensus among all stakeholders (all the existing and new drivers plus people working on dw-mipi-dsi drivers).
E.g. I'm not clear whether a helper library is a good interface for drm_device drivers, that really should be doable as a standard drm_bridge with no drm_encoder.
I think the best way to ensure that consensus is by adding a todo entry to Documentation/gpu/todo.rst for both dw-hdmi and dw-mipi-dsi (same design really), with acks from everyone. Once we have agreement and on how to best get there I'm all happy.
-Daniel
Quoted 615 lines
Thanks,
AngeloQuoted 609 lines
Quoted 604 lines
---
Changes since v3:
- Rearranged headers.--- drivers/gpu/drm/ipk/Kconfig | 9 + drivers/gpu/drm/ipk/Makefile | 2 + drivers/gpu/drm/ipk/dw-mipi-dsi-ipk.c | 557 ++++++++++++++++++++++++++++++++++ 3 files changed, 568 insertions(+) create mode 100644 drivers/gpu/drm/ipk/dw-mipi-dsi-ipk.cdrivers/gpu/drm/ipk/Kconfig +9
-0@@ -11,3 +11,12 @@ config DRM_IPK Enable support for the Synopsys DesignWare DRM DSI. To compile this driver as a module, choose M here: the module will be called ipk-drm. + +config DRM_IPK_DSI + tristate "Synopsys DesignWare IPK specific extensions for MIPI DSI" + depends on DRM_IPK + select DRM_DW_MIPI_DSI + help + Choose this option for Synopsys DesignWare IPK MIPI DSI support. + To compile this driver as a module, choose M here: the module + will be called dw-mipi-dsi-ipk.drivers/gpu/drm/ipk/Makefile +2
-0@@ -2,3 +2,5 @@ ipk-drm-y := dw-drv.o dw-vpg.o obj-$(CONFIG_DRM_IPK) += ipk-drm.o + +obj-$(CONFIG_DRM_IPK_DSI) += dw-mipi-dsi-ipk.odrivers/gpu/drm/ipk/dw-mipi-dsi-ipk.c new file +557
-0@@ -0,0 +1,557 @@ +// SPDX-License-Identifier: GPL-2.0 +/* +* Copyright (c) 2019-2020 Synopsys, Inc. and/or its affiliates. +* Synopsys DesignWare MIPI DSI solution driver +* +* Author: Angelo Ribeiro <[email protected]> +* Author: Luis Oliveira <[email protected]> +*/ + +#include <linux/clk.h> +#include <linux/iopoll.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> + +#include <video/mipi_display.h> + +#include <drm/bridge/dw_mipi_dsi.h> +#include <drm/drm_crtc.h> +#include <drm/drm_device.h> +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_print.h> + +#define DW_DPHY_LPCLK_CTRL 0x94 +#define DW_DPHY_RSTZ 0xA0 +#define DW_DPHY_IF_CFG 0xA4 +#define DW_DPHY_ULPS_CTRL 0xA8 +#define DW_DPHY_TX_TRIGGERS 0xAC +#define DW_DPHY_STATUS 0xB0 +#define DW_DPHY_TST_CTRL0 0xB4 +#define DW_DPHY_TST_CTRL1 0xB8 +#define DW_GEN3_IF_TESTER 0x3c +#define DW_GEN3_IF_SOC_PLL 0x48 +#define DW_GEN3_IF_SOC_PLL_EN 0x4C + +#define DW_12BITS_DPHY_RDY_L0 0x507 +#define DW_12BITS_DPHY_RDY_L1 0x707 +#define DW_12BITS_DPHY_RDY_L2 0x907 +#define DW_12BITS_DPHY_RDY_L3 0xB07 + +#define DW_LANE_MIN_KBPS 80000 +#define DW_LANE_MAX_KBPS 2500000000 +#define DW_DPHY_DIV_UPPER_LIMIT 8000 +#define DW_DPHY_DIV_LOWER_LIMIT 2000 +#define DW_MIN_OUTPUT_FREQ 80 +#define DW_LPHS_TIM_TRANSIONS 0x40 + +enum dw_glueiftester { +GLUE_LOGIC =0x4 , +RX_PHY =0x2 , +TX_PHY =0x1 , +RESET =0x0 , +}; + +struct dw_range_dphy { +u32 freq; +u8 hs_freq_range; +u32 osc_freq_target; +} dw_range_gen3[] = { + {80 ,0x00 ,0x3f }, {90 ,0x10 ,0x3f }, {100 ,0x20 ,0x3f }, + {110 ,0x30 ,0x39 }, {120 ,0x01 ,0x39 }, {130 ,0x11 ,0x39 }, + {140 ,0x21 ,0x39 }, {150 ,0x31 ,0x39 }, {160 ,0x02 ,0x39 }, + {170 ,0x12 ,0x2f }, {180 ,0x22 ,0x2f }, {190 ,0x32 ,0x2f }, + {205 ,0x03 ,0x2f }, {220 ,0x13 ,0x29 }, {235 ,0x23 ,0x29 }, + {250 ,0x33 ,0x29 }, {275 ,0x04 ,0x29 }, {300 ,0x14 ,0x29 }, + {325 ,0x25 ,0x29 }, {350 ,0x35 ,0x1f }, {400 ,0x05 ,0x1f }, + {450 ,0x16 ,0x19 }, {500 ,0x26 ,0x19 }, {550 ,0x37 ,0x19 }, + {600 ,0x07 ,0x19 }, {650 ,0x18 ,0x19 }, {700 ,0x28 ,0x0f }, + {750 ,0x39 ,0x0f }, {800 ,0x09 ,0x0f }, {850 ,0x19 ,0x0f }, + {900 ,0x29 ,0x09 }, {950 ,0x3a ,0x09 }, {1000 ,0x0a ,0x09 }, + {1050 ,0x1a ,0x09 }, {1100 ,0x2a ,0x09 }, {1150 ,0x3b ,0x09 }, + {1200 ,0x0b ,0x09 }, {1250 ,0x1b ,0x09 }, {1300 ,0x2b ,0x09 }, + {1350 ,0x3c ,0x03 }, {1400 ,0x0c ,0x03 }, {1450 ,0x1c ,0x03 }, + {1500 ,0x2c ,0x03 }, {1550 ,0x3d ,0x03 }, {1600 ,0x0d ,0x03 }, + {1650 ,0x1d ,0x03 }, {1700 ,0x2e ,0x03 }, {1750 ,0x3e ,0x03 }, + {1800 ,0x0e ,0x03 }, {1850 ,0x1e ,0x03 }, {1900 ,0x2f ,0x03 }, + {1950 ,0x3f ,0x03 }, {2000 ,0x0f ,0x03 }, {2050 ,0x40 ,0x03 }, + {2100 ,0x41 ,0x03 }, {2150 ,0x42 ,0x03 }, {2200 ,0x43 ,0x03 }, + {2250 ,0x44 ,0x03 }, {2300 ,0x45 ,0x01 }, {2350 ,0x46 ,0x01 }, + {2400 ,0x47 ,0x01 }, {2450 ,0x48 ,0x01 }, {2500 ,0x49 ,0x01 } +}; + +struct dw_dsi_ipk { +void __iomem *base; +void __iomem *base_phy; +struct clk *pllref_clk; +struct dw_mipi_dsi *dsi; +u32 lane_min_kbps; +u32 lane_max_kbps; +int range; +int in_div; +int loop_div; +}; + +#define dw_mipi_dsi_to_dw_dsi_ipk (target) \ + container_of(target, struct dw_dsi_ipk, dsi) + +static void dw_dsi_write (struct dw_dsi_ipk *dsi,u32 reg,u32 val) +{ +writel (val, dsi->base + reg); +} + +static u32 dw_dsi_read (struct dw_dsi_ipk *dsi,u32 reg) +{ +return readl (dsi->base + reg); +} + +static void dw_phy_write (struct dw_dsi_ipk *dsi,u32 reg,u32 val) +{ +writel (val, dsi->base_phy + reg); +} + +static void dw_dsi_phy_write_part (struct dw_dsi_ipk *dsi,u32 reg_address, +u32 data,u8 shift,u8 width) +{ +u32 temp =dw_dsi_read (dsi, reg_address); +u32 mask = (1 << width) -1 ; + + temp &= ~(mask << shift); + temp |= (data & mask) << shift; +dw_dsi_write (dsi, reg_address, temp); +} + +static void dw_dsi_phy_test_data_in (struct dw_dsi_ipk *dsi,u8 test_data) +{ +dw_dsi_phy_write_part (dsi,DW_DPHY_TST_CTRL1 , test_data,0 ,8 ); +} + +static void dw_dsi_phy_test_clock (struct dw_dsi_ipk *dsi,int value) +{ +dw_dsi_phy_write_part (dsi,DW_DPHY_TST_CTRL0 , value,1 ,1 ); +} + +static void dw_dsi_phy_test_en (struct dw_dsi_ipk *dsi,u8 on_falling_edge) +{ +dw_dsi_phy_write_part (dsi,DW_DPHY_TST_CTRL1 , on_falling_edge,16 ,1 ); +} + +static void dw_dsi_phy_test_clear (struct dw_dsi_ipk *dsi,int value) +{ +dw_dsi_phy_write_part (dsi,DW_DPHY_TST_CTRL0 , value,0 ,1 ); +} + +static void dw_dsi_phy_write (struct dw_dsi_ipk *dsi,u16 address, +u32 value,u8 data_length) +{ +u8 data[4 ]; +int i; + + data[0 ] = value; + +dw_dsi_write (dsi,DW_DPHY_TST_CTRL0 ,0 ); +dw_dsi_write (dsi,DW_DPHY_TST_CTRL1 ,0 ); + +dw_dsi_phy_test_en (dsi,1 ); +dw_dsi_phy_test_clock (dsi,1 ); +dw_dsi_phy_test_data_in (dsi,0x00 ); +dw_dsi_phy_test_clock (dsi,0 ); +dw_dsi_write (dsi,DW_DPHY_TST_CTRL1 ,0 ); +dw_dsi_write (dsi,DW_DPHY_TST_CTRL1 , (u8)(address >>8 )); +dw_dsi_phy_test_clock (dsi,1 ); +dw_dsi_phy_test_clock (dsi,0 ); +dw_dsi_phy_test_en (dsi,1 ); +dw_dsi_phy_test_clock (dsi,1 ); +dw_dsi_phy_test_data_in (dsi, ((u8 )address)); +dw_dsi_phy_test_clock (dsi,0 ); +dw_dsi_phy_test_en (dsi,0 ); + +for (i = data_length; i >0 ; i--) { +dw_dsi_phy_test_data_in (dsi, ((u8 )data[i -1 ])); +dw_dsi_phy_test_clock (dsi,1 ); +dw_dsi_phy_test_clock (dsi,0 ); + } +} + +static void dw_dsi_phy_delay (struct dw_dsi_ipk *dsi,int value) +{ +u32 data = value <<2 ; + +dw_dsi_phy_write (dsi,DW_12BITS_DPHY_RDY_L0 , data,1 ); +dw_dsi_phy_write (dsi,DW_12BITS_DPHY_RDY_L1 , data,1 ); +dw_dsi_phy_write (dsi,DW_12BITS_DPHY_RDY_L2 , data,1 ); +dw_dsi_phy_write (dsi,DW_12BITS_DPHY_RDY_L3 , data,1 ); +} + +static int dsi_pll_get_clkout_khz (int clkin_khz,int idf,int ndiv,int odf) +{ +int divisor = idf * odf; + +/* prevent from division by 0 */ +if (!divisor) +return 0 ; + +return DIV_ROUND_CLOSEST (clkin_khz * ndiv, divisor); +} + +static int dsi_pll_get_params (struct dw_dsi_ipk *dsi,int in_freq, +int out_freq,int *idf,int *ndiv,int *odf) +{ +int range, tmp_loop_div, tmp_in_freq, delta, step =0 , flag =0 ; +int out_data_rate = out_freq *2 ; +int loop_div =0 ;/* M */ +int out_div;/* VCO */ +int in_div;/* N */ + +/* Find ranges */ +for (range =0 ;ARRAY_SIZE (dw_range_gen3) && + (out_data_rate /1000 ) > dw_range_gen3[range].freq; range++) + ; + +if (range >=ARRAY_SIZE (dw_range_gen3)) +return -EINVAL ; + +if ((dw_range_gen3[range].osc_freq_target >>4 ) ==3 ) + out_div =8 ; +else if ((dw_range_gen3[range].osc_freq_target >>4 ) ==2 ) + out_div =4 ; +else + out_div =2 ; + +if (dw_range_gen3[range].freq >640 ) + out_div =1 ; + + out_freq = out_freq * out_div; + + loop_div = (out_freq * (in_freq /DW_DPHY_DIV_LOWER_LIMIT )) / in_freq; + +/* here delta will account for the rounding */ + delta = (loop_div * in_freq) / (in_freq /DW_DPHY_DIV_LOWER_LIMIT ) - + out_freq; + +for (in_div =1 + in_freq /DW_DPHY_DIV_UPPER_LIMIT ; + (in_freq / in_div >=DW_DPHY_DIV_LOWER_LIMIT ) && !flag; in_div++) { + tmp_loop_div = out_freq * in_div / in_freq; + tmp_in_freq = in_freq / in_div; +if (tmp_loop_div %2 ) { + tmp_loop_div +=1 ; +if (out_freq == tmp_loop_div * tmp_in_freq) { +/* Exact values found */ + flag =1 ; + loop_div = tmp_loop_div; + delta = tmp_loop_div * tmp_in_freq - out_freq; + in_div--; + }else if (tmp_loop_div * tmp_in_freq - out_freq < + delta) { +/* Values found with smaller delta */ + loop_div = tmp_loop_div; + delta = tmp_loop_div * tmp_in_freq - out_freq; + step =0 ; + } + }else if (out_freq == tmp_loop_div * tmp_in_freq) { +/* Exact values found */ + flag =1 ; + loop_div = tmp_loop_div; + delta = out_freq - tmp_loop_div * tmp_in_freq; + in_div--; + }else if (out_freq - tmp_loop_div * tmp_in_freq < delta) { +/* Values found with smaller delta */ + loop_div = tmp_loop_div; + delta = out_freq - tmp_loop_div * tmp_in_freq; + step =1 ; + } + } + +if (!flag) + in_div = step + loop_div * in_freq / out_freq; + + *idf = in_div; + *ndiv = loop_div; + *odf = out_div; + + dsi->range = range; + dsi->in_div = in_div; + dsi->loop_div = loop_div; + +return 0 ; +} + +/* DPHY GEN 3 12 bits */ +static void dw_phy_init_gen3_128 (void *priv_data) +{ +struct dw_dsi_ipk *dsi = priv_data; +int loop_div = dsi->loop_div; +int in_div = dsi->in_div; +int range = dsi->range; +u32 data; + +/* hs frequency range [6:0] */ + data = dw_range_gen3[range].hs_freq_range; +dw_dsi_phy_write (dsi,0x02 , data,1 ); + +/* [7:6] reserved | [5] hsfreqrange_ovr_en_rw | +* [4:1] target_state_rw | [0] force_state_rw +*/ +dw_dsi_phy_write (dsi,0x01 ,0x20 ,1 ); + +/* PLL Lock Configurations */ +dw_dsi_phy_write (dsi,0x173 ,0x02 ,1 ); +dw_dsi_phy_write (dsi,0x174 ,0x00 ,1 ); +dw_dsi_phy_write (dsi,0x175 ,0x60 ,1 ); +dw_dsi_phy_write (dsi,0x176 ,0x03 ,1 ); +dw_dsi_phy_write (dsi,0x166 ,0x01 ,1 ); + +/* Charge-pump Programmability */ +/* [7] pll_vco_cntrl_ovr_en | +* [6:1] pll_vco_cntrl_ovr | [0] pll_m_ovr_en +*/ +if (dw_range_gen3[range].freq >640 ) + data =1 | (dw_range_gen3[range].osc_freq_target <<1 ); +else + data =1 | (1 <<7 ) | + (dw_range_gen3[range].osc_freq_target <<1 ); + +dw_dsi_phy_write (dsi,0x17b , data,1 ); +dw_dsi_phy_write (dsi,0x15e ,0x10 ,1 ); +dw_dsi_phy_write (dsi,0x162 ,0x04 ,1 ); +dw_dsi_phy_write (dsi,0x16e ,0x0c ,1 ); + +/* Slew-Rate */ +dw_dsi_phy_write (dsi,0x26b ,0x04 ,1 ); + +/* pll_n_ovr_en_rw | PLL input divider ratio [6:3] | +* pll_tstplldig_rw +*/ + data = (1 <<7 ) | (in_div -1 ) <<3 ; +dw_dsi_phy_write (dsi,0x178 , data,1 ); + +/* PLL loop divider ratio [7:0] */ + data = loop_div -2 ; +dw_dsi_phy_write (dsi,0x179 , data,1 ); + +/* PLL loop divider ratio [9:8] */ + data = (loop_div -2 ) >>8 ; +dw_dsi_phy_write (dsi,0x17a , data,1 ); + +if (dw_range_gen3[range].freq <450 ) +dw_dsi_phy_write (dsi,0x1ac ,0x1b ,1 ); +else +dw_dsi_phy_write (dsi,0x1ac ,0x0b ,1 ); +} + +static int dw_mipi_dsi_phy_init (void *priv_data) +{ +struct dw_dsi_ipk *dsi = priv_data; +int range = dsi->range; +unsigned int in_freq; +u32 data; + + in_freq = (unsigned int )(clk_get_rate (dsi->pllref_clk) /1000 ); + +dw_phy_write (dsi,DW_GEN3_IF_TESTER ,RESET ); +dw_phy_write (dsi,DW_GEN3_IF_TESTER ,GLUE_LOGIC ); +dw_dsi_phy_test_clear (dsi,1 ); +dw_dsi_phy_test_clear (dsi,0 ); + +dw_dsi_phy_write (dsi,0x30 ,0x0f ,1 ); + + data = ((in_freq /1000 ) -17 ) *4 ; +dw_dsi_phy_write (dsi,0x02 , data,1 ); + +dw_dsi_phy_write (dsi,0x20 ,0x3f ,1 ); + +/* RESET RX */ +dw_phy_write (dsi,DW_GEN3_IF_TESTER ,RESET ); +dw_phy_write (dsi,DW_GEN3_IF_TESTER ,RX_PHY ); +dw_dsi_phy_test_clear (dsi,1 ); +dw_dsi_phy_test_clear (dsi,0 ); + +/* RESET TX */ +dw_phy_write (dsi,DW_GEN3_IF_TESTER ,RESET ); +dw_phy_write (dsi,DW_GEN3_IF_TESTER ,TX_PHY ); +dw_dsi_phy_test_clear (dsi,1 ); +dw_dsi_phy_test_clear (dsi,0 ); + +dw_phy_init_gen3_128 (priv_data); + +if (dw_range_gen3[range].freq >648 ) +dw_dsi_phy_delay (dsi,5 ); +else +dw_dsi_phy_delay (dsi,4 ); + +DRM_DEBUG_DRIVER ("Phy configured\n" ); + +return 0 ; +} + +static int +dw_mipi_dsi_get_lane_mbps (void *priv_data,const struct drm_display_mode *mode, +unsigned long mode_flags,u32 lanes,u32 format, +unsigned int *lane_mbps) +{ +int idf =0 , ndiv =0 , odf =0 , pll_in_khz, pll_out_khz, ret, bpp; +struct dw_dsi_ipk *dsi = priv_data; + +DRM_DEBUG_DRIVER ("\n" ); + + dsi->lane_min_kbps = (unsigned int )DW_LANE_MIN_KBPS ; + dsi->lane_max_kbps = (unsigned int )DW_LANE_MAX_KBPS ; + + pll_in_khz = (unsigned int )(clk_get_rate (dsi->pllref_clk) /1000 ); + +/* Compute requested pll out */ + bpp =mipi_dsi_pixel_format_to_bpp ((enum mipi_dsi_pixel_format )format); + pll_out_khz = ((mode->clock * bpp) / lanes) /2 ; + +if (pll_out_khz > dsi->lane_max_kbps) { + pll_out_khz = dsi->lane_max_kbps; +DRM_WARN ("Warning max phy mbps is used\n" ); + } + +if (pll_out_khz < dsi->lane_min_kbps) { + pll_out_khz = dsi->lane_min_kbps; +DRM_WARN ("Warning min phy mbps is used\n" ); + } + + ret =dsi_pll_get_params (dsi, pll_in_khz, pll_out_khz, + &idf, &ndiv, &odf); +if (ret) +DRM_WARN ("Warning dsi_pll_get_params(): bad params\n" ); + +/* Get the adjusted pll out value */ + pll_out_khz =dsi_pll_get_clkout_khz (pll_in_khz, idf, ndiv, odf); + + *lane_mbps = (pll_out_khz /1000 ) *2 ; + +DRM_DEBUG_DRIVER ("pll_in %ukHz pll_out %ukHz lane_mbps %uMHz\n" , + pll_in_khz, pll_out_khz, *lane_mbps); + +return ret; +} + +static int +dw_mipi_dsi_phy_get_timing (void *priv_data,unsigned int lane_mbps, +struct dw_mipi_dsi_dphy_timing *timing) +{ + timing->clk_hs2lp =DW_LPHS_TIM_TRANSIONS ; + timing->clk_lp2hs =DW_LPHS_TIM_TRANSIONS ; + timing->data_hs2lp =DW_LPHS_TIM_TRANSIONS ; + timing->data_lp2hs =DW_LPHS_TIM_TRANSIONS ; + +return 0 ; +} + +static const struct dw_mipi_dsi_phy_ops dw_dsi_ipk_phy_ops = { + .init = dw_mipi_dsi_phy_init, + .get_lane_mbps = dw_mipi_dsi_get_lane_mbps, + .get_timing = dw_mipi_dsi_phy_get_timing, +}; + +static struct dw_mipi_dsi_plat_data dw_dsi_ipk_plat_data = { + .max_data_lanes =4 , + .phy_ops = &dw_dsi_ipk_phy_ops, +}; + +static const struct of_device_id dw_ipk_dt_ids[] = { + {.compatible ="snps,dw-ipk-dsi" , + .data = &dw_dsi_ipk_plat_data,}, + { }, +}; + +MODULE_DEVICE_TABLE (of, dw_ipk_dt_ids); + +static int dw_dsi_ipk_probe (struct platform_device *pdev) +{ +struct device *dev = &pdev->dev; +struct dw_dsi_ipk *dsi; +struct resource *res; +struct clk *pclk; +int ret; + +DRM_DEBUG_DRIVER ("\n" ); + + dsi =devm_kzalloc (dev,sizeof (*dsi),GFP_KERNEL ); +if (!dsi) +return -ENOMEM ; + + res =platform_get_resource_byname (pdev,IORESOURCE_MEM ,"dsi" ); + dsi->base =devm_ioremap_resource (dev, res); +if (IS_ERR (dsi->base)) { + ret =PTR_ERR (dsi->base); +DRM_ERROR ("Unable to get dsi registers %d\n" , ret); +return ret; + } + + res =platform_get_resource_byname (pdev,IORESOURCE_MEM ,"phy" ); + dsi->base_phy =devm_ioremap_resource (dev, res); +if (IS_ERR (dsi->base_phy)) { + ret =PTR_ERR (dsi->base_phy); +DRM_ERROR ("Unable to get PHY registers %d\n" , ret); +return ret; + } + + pclk =devm_clk_get (dev,"pclk" ); +if (IS_ERR (pclk)) { + ret =PTR_ERR (pclk); +DRM_ERROR ("Unable to get peripheral clock: %d\n" , ret); + goto err_dsi_probe; + } + + ret =clk_prepare_enable (pclk); +if (ret) + goto err_dsi_probe; + + dsi->pllref_clk =devm_clk_get (dev,"ref" ); +if (IS_ERR (dsi->pllref_clk)) { + ret =PTR_ERR (dsi->pllref_clk); +DRM_ERROR ("Unable to get pll reference clock: %d\n" , ret); +return ret; + } + + ret =clk_prepare_enable (dsi->pllref_clk); +if (ret) +return ret; + + dw_dsi_ipk_plat_data.base = dsi->base; + dw_dsi_ipk_plat_data.priv_data = dsi; + +platform_set_drvdata (pdev, dsi); + + dsi->dsi =dw_mipi_dsi_probe (pdev, &dw_dsi_ipk_plat_data); +if (IS_ERR (dsi->dsi)) { + ret =PTR_ERR (dsi->dsi); +DRM_ERROR ("Failed to initialize mipi dsi host: %d\n" , ret); + goto err_dsi_probe; + } + +return ret; + +err_dsi_probe: +clk_disable_unprepare (dsi->pllref_clk); +return ret; +} + +static int dw_dsi_ipk_remove (struct platform_device *pdev) +{ +struct dw_dsi_ipk *dsi =platform_get_drvdata (pdev); + +dw_mipi_dsi_remove (dsi->dsi); + +return 0 ; +} + +struct platform_driver dw_mipi_dsi_ipk_driver = { + .probe = dw_dsi_ipk_probe, + .remove = dw_dsi_ipk_remove, + .driver = { + .name ="ipk-dw-mipi-dsi" , + .of_match_table = dw_ipk_dt_ids, + }, +}; + +module_platform_driver (dw_mipi_dsi_ipk_driver); + +MODULE_AUTHOR ("Angelo Ribeiro <[email protected]>" ); +MODULE_AUTHOR ("Luis Oliveira <[email protected]>" ); +MODULE_DESCRIPTION ("Synopsys IPK DW MIPI DSI host controller driver" ); +MODULE_LICENSE ("GPL v2" );Signature · 1 line
2.7.4Signature · 3 lines
Daniel Vetter Software Engineer, Intel Corporation https://urldefense.com/v3/__http://blog.ffwll.ch__;!!A4F2R9G_pg!Oe5QZPns5hZvX0GBstRz9Z4N97n6VR1TRkC51lVae1b5HxhrNe8W9QtqzyYCv2JaM5-vjQ$
Signature · 7 lines
Daniel Vetter Software Engineer, Intel Corporation http://blog.ffwll.ch _______________________________________________ dri-devel mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/dri-devel