1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright (C) 2016 BayLibre, SAS |
4 | * Author: Neil Armstrong <narmstrong@baylibre.com> |
5 | * Copyright (C) 2015 Amlogic, Inc. All rights reserved. |
6 | */ |
7 | |
8 | #include <linux/kernel.h> |
9 | #include <linux/module.h> |
10 | #include <linux/of_graph.h> |
11 | |
12 | #include <drm/drm_atomic_helper.h> |
13 | #include <drm/drm_simple_kms_helper.h> |
14 | #include <drm/drm_bridge.h> |
15 | #include <drm/drm_bridge_connector.h> |
16 | #include <drm/drm_device.h> |
17 | #include <drm/drm_probe_helper.h> |
18 | |
19 | #include "meson_drv.h" |
20 | #include "meson_encoder_dsi.h" |
21 | #include "meson_registers.h" |
22 | #include "meson_venc.h" |
23 | #include "meson_vclk.h" |
24 | |
25 | struct meson_encoder_dsi { |
26 | struct drm_encoder encoder; |
27 | struct drm_bridge bridge; |
28 | struct drm_bridge *next_bridge; |
29 | struct meson_drm *priv; |
30 | }; |
31 | |
32 | #define bridge_to_meson_encoder_dsi(x) \ |
33 | container_of(x, struct meson_encoder_dsi, bridge) |
34 | |
35 | static int meson_encoder_dsi_attach(struct drm_bridge *bridge, |
36 | enum drm_bridge_attach_flags flags) |
37 | { |
38 | struct meson_encoder_dsi *encoder_dsi = bridge_to_meson_encoder_dsi(bridge); |
39 | |
40 | return drm_bridge_attach(encoder: bridge->encoder, bridge: encoder_dsi->next_bridge, |
41 | previous: &encoder_dsi->bridge, flags); |
42 | } |
43 | |
44 | static void meson_encoder_dsi_atomic_enable(struct drm_bridge *bridge, |
45 | struct drm_bridge_state *bridge_state) |
46 | { |
47 | struct meson_encoder_dsi *encoder_dsi = bridge_to_meson_encoder_dsi(bridge); |
48 | struct drm_atomic_state *state = bridge_state->base.state; |
49 | struct meson_drm *priv = encoder_dsi->priv; |
50 | struct drm_connector_state *conn_state; |
51 | struct drm_crtc_state *crtc_state; |
52 | struct drm_connector *connector; |
53 | |
54 | connector = drm_atomic_get_new_connector_for_encoder(state, encoder: bridge->encoder); |
55 | if (WARN_ON(!connector)) |
56 | return; |
57 | |
58 | conn_state = drm_atomic_get_new_connector_state(state, connector); |
59 | if (WARN_ON(!conn_state)) |
60 | return; |
61 | |
62 | crtc_state = drm_atomic_get_new_crtc_state(state, crtc: conn_state->crtc); |
63 | if (WARN_ON(!crtc_state)) |
64 | return; |
65 | |
66 | /* ENCL clock setup is handled by CCF */ |
67 | |
68 | meson_venc_mipi_dsi_mode_set(priv, mode: &crtc_state->adjusted_mode); |
69 | meson_encl_load_gamma(priv); |
70 | |
71 | writel_relaxed(0, priv->io_base + _REG(ENCL_VIDEO_EN)); |
72 | |
73 | writel_bits_relaxed(ENCL_VIDEO_MODE_ADV_VFIFO_EN, ENCL_VIDEO_MODE_ADV_VFIFO_EN, |
74 | priv->io_base + _REG(ENCL_VIDEO_MODE_ADV)); |
75 | writel_relaxed(0, priv->io_base + _REG(ENCL_TST_EN)); |
76 | |
77 | writel_bits_relaxed(BIT(0), 0, priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_EN_CTRL)); |
78 | |
79 | writel_relaxed(1, priv->io_base + _REG(ENCL_VIDEO_EN)); |
80 | } |
81 | |
82 | static void meson_encoder_dsi_atomic_disable(struct drm_bridge *bridge, |
83 | struct drm_bridge_state *bridge_state) |
84 | { |
85 | struct meson_encoder_dsi *meson_encoder_dsi = |
86 | bridge_to_meson_encoder_dsi(bridge); |
87 | struct meson_drm *priv = meson_encoder_dsi->priv; |
88 | |
89 | writel_relaxed(0, priv->io_base + _REG(ENCL_VIDEO_EN)); |
90 | |
91 | writel_bits_relaxed(BIT(0), BIT(0), priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_EN_CTRL)); |
92 | } |
93 | |
94 | static const struct drm_bridge_funcs meson_encoder_dsi_bridge_funcs = { |
95 | .attach = meson_encoder_dsi_attach, |
96 | .atomic_enable = meson_encoder_dsi_atomic_enable, |
97 | .atomic_disable = meson_encoder_dsi_atomic_disable, |
98 | .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, |
99 | .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, |
100 | .atomic_reset = drm_atomic_helper_bridge_reset, |
101 | }; |
102 | |
103 | int meson_encoder_dsi_probe(struct meson_drm *priv) |
104 | { |
105 | struct meson_encoder_dsi *meson_encoder_dsi; |
106 | struct device_node *remote; |
107 | int ret; |
108 | |
109 | meson_encoder_dsi = devm_kzalloc(dev: priv->dev, size: sizeof(*meson_encoder_dsi), GFP_KERNEL); |
110 | if (!meson_encoder_dsi) |
111 | return -ENOMEM; |
112 | |
113 | /* DSI Transceiver Bridge */ |
114 | remote = of_graph_get_remote_node(node: priv->dev->of_node, port: 2, endpoint: 0); |
115 | if (!remote) { |
116 | dev_err(priv->dev, "DSI transceiver device is disabled" ); |
117 | return 0; |
118 | } |
119 | |
120 | meson_encoder_dsi->next_bridge = of_drm_find_bridge(np: remote); |
121 | if (!meson_encoder_dsi->next_bridge) |
122 | return dev_err_probe(dev: priv->dev, err: -EPROBE_DEFER, |
123 | fmt: "Failed to find DSI transceiver bridge\n" ); |
124 | |
125 | /* DSI Encoder Bridge */ |
126 | meson_encoder_dsi->bridge.funcs = &meson_encoder_dsi_bridge_funcs; |
127 | meson_encoder_dsi->bridge.of_node = priv->dev->of_node; |
128 | meson_encoder_dsi->bridge.type = DRM_MODE_CONNECTOR_DSI; |
129 | |
130 | drm_bridge_add(bridge: &meson_encoder_dsi->bridge); |
131 | |
132 | meson_encoder_dsi->priv = priv; |
133 | |
134 | /* Encoder */ |
135 | ret = drm_simple_encoder_init(dev: priv->drm, encoder: &meson_encoder_dsi->encoder, |
136 | DRM_MODE_ENCODER_DSI); |
137 | if (ret) |
138 | return dev_err_probe(dev: priv->dev, err: ret, |
139 | fmt: "Failed to init DSI encoder\n" ); |
140 | |
141 | meson_encoder_dsi->encoder.possible_crtcs = BIT(0); |
142 | |
143 | /* Attach DSI Encoder Bridge to Encoder */ |
144 | ret = drm_bridge_attach(encoder: &meson_encoder_dsi->encoder, bridge: &meson_encoder_dsi->bridge, NULL, flags: 0); |
145 | if (ret) |
146 | return dev_err_probe(dev: priv->dev, err: ret, |
147 | fmt: "Failed to attach bridge\n" ); |
148 | |
149 | /* |
150 | * We should have now in place: |
151 | * encoder->[dsi encoder bridge]->[dw-mipi-dsi bridge]->[panel bridge]->[panel] |
152 | */ |
153 | |
154 | priv->encoders[MESON_ENC_DSI] = meson_encoder_dsi; |
155 | |
156 | dev_dbg(priv->dev, "DSI encoder initialized\n" ); |
157 | |
158 | return 0; |
159 | } |
160 | |
161 | void meson_encoder_dsi_remove(struct meson_drm *priv) |
162 | { |
163 | struct meson_encoder_dsi *meson_encoder_dsi; |
164 | |
165 | if (priv->encoders[MESON_ENC_DSI]) { |
166 | meson_encoder_dsi = priv->encoders[MESON_ENC_DSI]; |
167 | drm_bridge_remove(bridge: &meson_encoder_dsi->bridge); |
168 | } |
169 | } |
170 | |