Commit | Line | Data |
---|---|---|
3e87599b RC |
1 | /* |
2 | * Copyright (C) 2014 Red Hat | |
3 | * Author: Rob Clark <robdclark@gmail.com> | |
4 | * Author: Vinay Simha <vinaysimha@inforcecomputing.com> | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify it | |
7 | * under the terms of the GNU General Public License version 2 as published by | |
8 | * the Free Software Foundation. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, but WITHOUT | |
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
13 | * more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License along with | |
16 | * this program. If not, see <http://www.gnu.org/licenses/>. | |
17 | */ | |
18 | ||
78f27b1c | 19 | #include <drm/drm_crtc.h> |
fcd70cd3 | 20 | #include <drm/drm_probe_helper.h> |
3e87599b | 21 | |
78f27b1c | 22 | #include "mdp4_kms.h" |
3e87599b RC |
23 | |
24 | struct mdp4_lcdc_encoder { | |
25 | struct drm_encoder base; | |
a6bf7f63 | 26 | struct device_node *panel_node; |
3e87599b RC |
27 | struct drm_panel *panel; |
28 | struct clk *lcdc_clk; | |
29 | unsigned long int pixclock; | |
30 | struct regulator *regs[3]; | |
31 | bool enabled; | |
32 | uint32_t bsc; | |
33 | }; | |
34 | #define to_mdp4_lcdc_encoder(x) container_of(x, struct mdp4_lcdc_encoder, base) | |
35 | ||
36 | static struct mdp4_kms *get_kms(struct drm_encoder *encoder) | |
37 | { | |
38 | struct msm_drm_private *priv = encoder->dev->dev_private; | |
39 | return to_mdp4_kms(to_mdp_kms(priv->kms)); | |
40 | } | |
41 | ||
6490ad47 | 42 | #ifdef DOWNSTREAM_CONFIG_MSM_BUS_SCALING |
3e87599b RC |
43 | #include <mach/board.h> |
44 | static void bs_init(struct mdp4_lcdc_encoder *mdp4_lcdc_encoder) | |
45 | { | |
46 | struct drm_device *dev = mdp4_lcdc_encoder->base.dev; | |
47 | struct lcdc_platform_data *lcdc_pdata = mdp4_find_pdata("lvds.0"); | |
48 | ||
49 | if (!lcdc_pdata) { | |
6a41da17 | 50 | DRM_DEV_ERROR(dev->dev, "could not find lvds pdata\n"); |
3e87599b RC |
51 | return; |
52 | } | |
53 | ||
54 | if (lcdc_pdata->bus_scale_table) { | |
55 | mdp4_lcdc_encoder->bsc = msm_bus_scale_register_client( | |
56 | lcdc_pdata->bus_scale_table); | |
57 | DBG("lvds : bus scale client: %08x", mdp4_lcdc_encoder->bsc); | |
58 | } | |
59 | } | |
60 | ||
61 | static void bs_fini(struct mdp4_lcdc_encoder *mdp4_lcdc_encoder) | |
62 | { | |
63 | if (mdp4_lcdc_encoder->bsc) { | |
64 | msm_bus_scale_unregister_client(mdp4_lcdc_encoder->bsc); | |
65 | mdp4_lcdc_encoder->bsc = 0; | |
66 | } | |
67 | } | |
68 | ||
69 | static void bs_set(struct mdp4_lcdc_encoder *mdp4_lcdc_encoder, int idx) | |
70 | { | |
71 | if (mdp4_lcdc_encoder->bsc) { | |
72 | DBG("set bus scaling: %d", idx); | |
73 | msm_bus_scale_client_update_request(mdp4_lcdc_encoder->bsc, idx); | |
74 | } | |
75 | } | |
76 | #else | |
77 | static void bs_init(struct mdp4_lcdc_encoder *mdp4_lcdc_encoder) {} | |
78 | static void bs_fini(struct mdp4_lcdc_encoder *mdp4_lcdc_encoder) {} | |
79 | static void bs_set(struct mdp4_lcdc_encoder *mdp4_lcdc_encoder, int idx) {} | |
80 | #endif | |
81 | ||
82 | static void mdp4_lcdc_encoder_destroy(struct drm_encoder *encoder) | |
83 | { | |
84 | struct mdp4_lcdc_encoder *mdp4_lcdc_encoder = | |
85 | to_mdp4_lcdc_encoder(encoder); | |
86 | bs_fini(mdp4_lcdc_encoder); | |
87 | drm_encoder_cleanup(encoder); | |
88 | kfree(mdp4_lcdc_encoder); | |
89 | } | |
90 | ||
91 | static const struct drm_encoder_funcs mdp4_lcdc_encoder_funcs = { | |
92 | .destroy = mdp4_lcdc_encoder_destroy, | |
93 | }; | |
94 | ||
95 | /* this should probably be a helper: */ | |
14edbde1 | 96 | static struct drm_connector *get_connector(struct drm_encoder *encoder) |
3e87599b RC |
97 | { |
98 | struct drm_device *dev = encoder->dev; | |
99 | struct drm_connector *connector; | |
100 | ||
101 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) | |
102 | if (connector->encoder == encoder) | |
103 | return connector; | |
104 | ||
105 | return NULL; | |
106 | } | |
107 | ||
108 | static void setup_phy(struct drm_encoder *encoder) | |
109 | { | |
110 | struct drm_device *dev = encoder->dev; | |
111 | struct drm_connector *connector = get_connector(encoder); | |
112 | struct mdp4_kms *mdp4_kms = get_kms(encoder); | |
113 | uint32_t lvds_intf = 0, lvds_phy_cfg0 = 0; | |
114 | int bpp, nchan, swap; | |
115 | ||
116 | if (!connector) | |
117 | return; | |
118 | ||
119 | bpp = 3 * connector->display_info.bpc; | |
120 | ||
121 | if (!bpp) | |
122 | bpp = 18; | |
123 | ||
124 | /* TODO, these should come from panel somehow: */ | |
125 | nchan = 1; | |
126 | swap = 0; | |
127 | ||
128 | switch (bpp) { | |
129 | case 24: | |
130 | mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_3_TO_0(0), | |
131 | MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT0(0x08) | | |
132 | MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT1(0x05) | | |
133 | MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT2(0x04) | | |
134 | MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT3(0x03)); | |
135 | mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_6_TO_4(0), | |
136 | MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT4(0x02) | | |
137 | MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT5(0x01) | | |
138 | MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT6(0x00)); | |
139 | mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_3_TO_0(1), | |
140 | MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT0(0x11) | | |
141 | MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT1(0x10) | | |
142 | MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT2(0x0d) | | |
143 | MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT3(0x0c)); | |
144 | mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_6_TO_4(1), | |
145 | MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT4(0x0b) | | |
146 | MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT5(0x0a) | | |
147 | MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT6(0x09)); | |
148 | mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_3_TO_0(2), | |
149 | MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT0(0x1a) | | |
150 | MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT1(0x19) | | |
151 | MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT2(0x18) | | |
152 | MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT3(0x15)); | |
153 | mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_6_TO_4(2), | |
154 | MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT4(0x14) | | |
155 | MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT5(0x13) | | |
156 | MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT6(0x12)); | |
157 | mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_3_TO_0(3), | |
158 | MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT0(0x1b) | | |
159 | MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT1(0x17) | | |
160 | MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT2(0x16) | | |
161 | MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT3(0x0f)); | |
162 | mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_6_TO_4(3), | |
163 | MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT4(0x0e) | | |
164 | MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT5(0x07) | | |
165 | MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT6(0x06)); | |
166 | if (nchan == 2) { | |
167 | lvds_intf |= MDP4_LCDC_LVDS_INTF_CTL_CH2_DATA_LANE3_EN | | |
168 | MDP4_LCDC_LVDS_INTF_CTL_CH2_DATA_LANE2_EN | | |
169 | MDP4_LCDC_LVDS_INTF_CTL_CH2_DATA_LANE1_EN | | |
170 | MDP4_LCDC_LVDS_INTF_CTL_CH2_DATA_LANE0_EN | | |
171 | MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE3_EN | | |
172 | MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE2_EN | | |
173 | MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE1_EN | | |
174 | MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE0_EN; | |
175 | } else { | |
176 | lvds_intf |= MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE3_EN | | |
177 | MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE2_EN | | |
178 | MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE1_EN | | |
179 | MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE0_EN; | |
180 | } | |
181 | break; | |
182 | ||
183 | case 18: | |
184 | mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_3_TO_0(0), | |
185 | MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT0(0x0a) | | |
186 | MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT1(0x07) | | |
187 | MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT2(0x06) | | |
188 | MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT3(0x05)); | |
189 | mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_6_TO_4(0), | |
190 | MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT4(0x04) | | |
191 | MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT5(0x03) | | |
192 | MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT6(0x02)); | |
193 | mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_3_TO_0(1), | |
194 | MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT0(0x13) | | |
195 | MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT1(0x12) | | |
196 | MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT2(0x0f) | | |
197 | MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT3(0x0e)); | |
198 | mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_6_TO_4(1), | |
199 | MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT4(0x0d) | | |
200 | MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT5(0x0c) | | |
201 | MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT6(0x0b)); | |
202 | mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_3_TO_0(2), | |
203 | MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT0(0x1a) | | |
204 | MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT1(0x19) | | |
205 | MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT2(0x18) | | |
206 | MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT3(0x17)); | |
207 | mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_6_TO_4(2), | |
208 | MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT4(0x16) | | |
209 | MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT5(0x15) | | |
210 | MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT6(0x14)); | |
211 | if (nchan == 2) { | |
212 | lvds_intf |= MDP4_LCDC_LVDS_INTF_CTL_CH2_DATA_LANE2_EN | | |
213 | MDP4_LCDC_LVDS_INTF_CTL_CH2_DATA_LANE1_EN | | |
214 | MDP4_LCDC_LVDS_INTF_CTL_CH2_DATA_LANE0_EN | | |
215 | MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE2_EN | | |
216 | MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE1_EN | | |
217 | MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE0_EN; | |
218 | } else { | |
219 | lvds_intf |= MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE2_EN | | |
220 | MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE1_EN | | |
221 | MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE0_EN; | |
222 | } | |
223 | lvds_intf |= MDP4_LCDC_LVDS_INTF_CTL_RGB_OUT; | |
224 | break; | |
225 | ||
226 | default: | |
6a41da17 | 227 | DRM_DEV_ERROR(dev->dev, "unknown bpp: %d\n", bpp); |
3e87599b RC |
228 | return; |
229 | } | |
230 | ||
231 | switch (nchan) { | |
232 | case 1: | |
233 | lvds_phy_cfg0 = MDP4_LVDS_PHY_CFG0_CHANNEL0; | |
234 | lvds_intf |= MDP4_LCDC_LVDS_INTF_CTL_CH1_CLK_LANE_EN | | |
235 | MDP4_LCDC_LVDS_INTF_CTL_MODE_SEL; | |
236 | break; | |
237 | case 2: | |
238 | lvds_phy_cfg0 = MDP4_LVDS_PHY_CFG0_CHANNEL0 | | |
239 | MDP4_LVDS_PHY_CFG0_CHANNEL1; | |
240 | lvds_intf |= MDP4_LCDC_LVDS_INTF_CTL_CH2_CLK_LANE_EN | | |
241 | MDP4_LCDC_LVDS_INTF_CTL_CH1_CLK_LANE_EN; | |
242 | break; | |
243 | default: | |
6a41da17 | 244 | DRM_DEV_ERROR(dev->dev, "unknown # of channels: %d\n", nchan); |
3e87599b RC |
245 | return; |
246 | } | |
247 | ||
248 | if (swap) | |
249 | lvds_intf |= MDP4_LCDC_LVDS_INTF_CTL_CH_SWAP; | |
250 | ||
251 | lvds_intf |= MDP4_LCDC_LVDS_INTF_CTL_ENABLE; | |
252 | ||
253 | mdp4_write(mdp4_kms, REG_MDP4_LVDS_PHY_CFG0, lvds_phy_cfg0); | |
254 | mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_INTF_CTL, lvds_intf); | |
255 | mdp4_write(mdp4_kms, REG_MDP4_LVDS_PHY_CFG2, 0x30); | |
256 | ||
257 | mb(); | |
258 | udelay(1); | |
259 | lvds_phy_cfg0 |= MDP4_LVDS_PHY_CFG0_SERIALIZATION_ENBLE; | |
260 | mdp4_write(mdp4_kms, REG_MDP4_LVDS_PHY_CFG0, lvds_phy_cfg0); | |
261 | } | |
262 | ||
3e87599b RC |
263 | static void mdp4_lcdc_encoder_mode_set(struct drm_encoder *encoder, |
264 | struct drm_display_mode *mode, | |
265 | struct drm_display_mode *adjusted_mode) | |
266 | { | |
267 | struct mdp4_lcdc_encoder *mdp4_lcdc_encoder = | |
268 | to_mdp4_lcdc_encoder(encoder); | |
269 | struct mdp4_kms *mdp4_kms = get_kms(encoder); | |
270 | uint32_t lcdc_hsync_skew, vsync_period, vsync_len, ctrl_pol; | |
271 | uint32_t display_v_start, display_v_end; | |
272 | uint32_t hsync_start_x, hsync_end_x; | |
273 | ||
274 | mode = adjusted_mode; | |
275 | ||
7510a9c6 | 276 | DBG("set mode: " DRM_MODE_FMT, DRM_MODE_ARG(mode)); |
3e87599b RC |
277 | |
278 | mdp4_lcdc_encoder->pixclock = mode->clock * 1000; | |
279 | ||
280 | DBG("pixclock=%lu", mdp4_lcdc_encoder->pixclock); | |
281 | ||
282 | ctrl_pol = 0; | |
283 | if (mode->flags & DRM_MODE_FLAG_NHSYNC) | |
284 | ctrl_pol |= MDP4_LCDC_CTRL_POLARITY_HSYNC_LOW; | |
285 | if (mode->flags & DRM_MODE_FLAG_NVSYNC) | |
286 | ctrl_pol |= MDP4_LCDC_CTRL_POLARITY_VSYNC_LOW; | |
287 | /* probably need to get DATA_EN polarity from panel.. */ | |
288 | ||
289 | lcdc_hsync_skew = 0; /* get this from panel? */ | |
290 | ||
291 | hsync_start_x = (mode->htotal - mode->hsync_start); | |
292 | hsync_end_x = mode->htotal - (mode->hsync_start - mode->hdisplay) - 1; | |
293 | ||
294 | vsync_period = mode->vtotal * mode->htotal; | |
295 | vsync_len = (mode->vsync_end - mode->vsync_start) * mode->htotal; | |
296 | display_v_start = (mode->vtotal - mode->vsync_start) * mode->htotal + lcdc_hsync_skew; | |
297 | display_v_end = vsync_period - ((mode->vsync_start - mode->vdisplay) * mode->htotal) + lcdc_hsync_skew - 1; | |
298 | ||
299 | mdp4_write(mdp4_kms, REG_MDP4_LCDC_HSYNC_CTRL, | |
300 | MDP4_LCDC_HSYNC_CTRL_PULSEW(mode->hsync_end - mode->hsync_start) | | |
301 | MDP4_LCDC_HSYNC_CTRL_PERIOD(mode->htotal)); | |
302 | mdp4_write(mdp4_kms, REG_MDP4_LCDC_VSYNC_PERIOD, vsync_period); | |
303 | mdp4_write(mdp4_kms, REG_MDP4_LCDC_VSYNC_LEN, vsync_len); | |
304 | mdp4_write(mdp4_kms, REG_MDP4_LCDC_DISPLAY_HCTRL, | |
305 | MDP4_LCDC_DISPLAY_HCTRL_START(hsync_start_x) | | |
306 | MDP4_LCDC_DISPLAY_HCTRL_END(hsync_end_x)); | |
307 | mdp4_write(mdp4_kms, REG_MDP4_LCDC_DISPLAY_VSTART, display_v_start); | |
308 | mdp4_write(mdp4_kms, REG_MDP4_LCDC_DISPLAY_VEND, display_v_end); | |
309 | mdp4_write(mdp4_kms, REG_MDP4_LCDC_BORDER_CLR, 0); | |
310 | mdp4_write(mdp4_kms, REG_MDP4_LCDC_UNDERFLOW_CLR, | |
311 | MDP4_LCDC_UNDERFLOW_CLR_ENABLE_RECOVERY | | |
312 | MDP4_LCDC_UNDERFLOW_CLR_COLOR(0xff)); | |
313 | mdp4_write(mdp4_kms, REG_MDP4_LCDC_HSYNC_SKEW, lcdc_hsync_skew); | |
314 | mdp4_write(mdp4_kms, REG_MDP4_LCDC_CTRL_POLARITY, ctrl_pol); | |
315 | mdp4_write(mdp4_kms, REG_MDP4_LCDC_ACTIVE_HCTL, | |
316 | MDP4_LCDC_ACTIVE_HCTL_START(0) | | |
317 | MDP4_LCDC_ACTIVE_HCTL_END(0)); | |
318 | mdp4_write(mdp4_kms, REG_MDP4_LCDC_ACTIVE_VSTART, 0); | |
319 | mdp4_write(mdp4_kms, REG_MDP4_LCDC_ACTIVE_VEND, 0); | |
320 | } | |
321 | ||
0b776d45 | 322 | static void mdp4_lcdc_encoder_disable(struct drm_encoder *encoder) |
3e87599b | 323 | { |
0b776d45 RC |
324 | struct drm_device *dev = encoder->dev; |
325 | struct mdp4_lcdc_encoder *mdp4_lcdc_encoder = | |
326 | to_mdp4_lcdc_encoder(encoder); | |
327 | struct mdp4_kms *mdp4_kms = get_kms(encoder); | |
a6bf7f63 | 328 | struct drm_panel *panel; |
0b776d45 RC |
329 | int i, ret; |
330 | ||
331 | if (WARN_ON(!mdp4_lcdc_encoder->enabled)) | |
332 | return; | |
333 | ||
334 | mdp4_write(mdp4_kms, REG_MDP4_LCDC_ENABLE, 0); | |
335 | ||
a6bf7f63 | 336 | panel = of_drm_find_panel(mdp4_lcdc_encoder->panel_node); |
5fa8e4a2 | 337 | if (!IS_ERR(panel)) { |
0b776d45 | 338 | drm_panel_disable(panel); |
095022b9 SK |
339 | drm_panel_unprepare(panel); |
340 | } | |
0b776d45 RC |
341 | |
342 | /* | |
343 | * Wait for a vsync so we know the ENABLE=0 latched before | |
344 | * the (connector) source of the vsync's gets disabled, | |
345 | * otherwise we end up in a funny state if we re-enable | |
346 | * before the disable latches, which results that some of | |
347 | * the settings changes for the new modeset (like new | |
348 | * scanout buffer) don't latch properly.. | |
349 | */ | |
350 | mdp_irq_wait(&mdp4_kms->base, MDP4_IRQ_PRIMARY_VSYNC); | |
351 | ||
352 | clk_disable_unprepare(mdp4_lcdc_encoder->lcdc_clk); | |
353 | ||
354 | for (i = 0; i < ARRAY_SIZE(mdp4_lcdc_encoder->regs); i++) { | |
355 | ret = regulator_disable(mdp4_lcdc_encoder->regs[i]); | |
356 | if (ret) | |
6a41da17 | 357 | DRM_DEV_ERROR(dev->dev, "failed to disable regulator: %d\n", ret); |
0b776d45 RC |
358 | } |
359 | ||
360 | bs_set(mdp4_lcdc_encoder, 0); | |
361 | ||
362 | mdp4_lcdc_encoder->enabled = false; | |
3e87599b RC |
363 | } |
364 | ||
0b776d45 | 365 | static void mdp4_lcdc_encoder_enable(struct drm_encoder *encoder) |
3e87599b | 366 | { |
0b776d45 RC |
367 | struct drm_device *dev = encoder->dev; |
368 | struct mdp4_lcdc_encoder *mdp4_lcdc_encoder = | |
369 | to_mdp4_lcdc_encoder(encoder); | |
370 | unsigned long pc = mdp4_lcdc_encoder->pixclock; | |
371 | struct mdp4_kms *mdp4_kms = get_kms(encoder); | |
a6bf7f63 | 372 | struct drm_panel *panel; |
f72f4f1a | 373 | uint32_t config; |
0b776d45 RC |
374 | int i, ret; |
375 | ||
376 | if (WARN_ON(mdp4_lcdc_encoder->enabled)) | |
377 | return; | |
378 | ||
3e87599b | 379 | /* TODO: hard-coded for 18bpp: */ |
f72f4f1a JM |
380 | config = |
381 | MDP4_DMA_CONFIG_R_BPC(BPC6) | | |
382 | MDP4_DMA_CONFIG_G_BPC(BPC6) | | |
383 | MDP4_DMA_CONFIG_B_BPC(BPC6) | | |
384 | MDP4_DMA_CONFIG_PACK(0x21) | | |
385 | MDP4_DMA_CONFIG_DEFLKR_EN | | |
386 | MDP4_DMA_CONFIG_DITHER_EN; | |
387 | ||
388 | if (!of_property_read_bool(dev->dev->of_node, "qcom,lcdc-align-lsb")) | |
389 | config |= MDP4_DMA_CONFIG_PACK_ALIGN_MSB; | |
390 | ||
391 | mdp4_crtc_set_config(encoder->crtc, config); | |
3e87599b | 392 | mdp4_crtc_set_intf(encoder->crtc, INTF_LCDC_DTV, 0); |
0b776d45 RC |
393 | |
394 | bs_set(mdp4_lcdc_encoder, 1); | |
395 | ||
396 | for (i = 0; i < ARRAY_SIZE(mdp4_lcdc_encoder->regs); i++) { | |
397 | ret = regulator_enable(mdp4_lcdc_encoder->regs[i]); | |
398 | if (ret) | |
6a41da17 | 399 | DRM_DEV_ERROR(dev->dev, "failed to enable regulator: %d\n", ret); |
0b776d45 RC |
400 | } |
401 | ||
402 | DBG("setting lcdc_clk=%lu", pc); | |
403 | ret = clk_set_rate(mdp4_lcdc_encoder->lcdc_clk, pc); | |
404 | if (ret) | |
6a41da17 | 405 | DRM_DEV_ERROR(dev->dev, "failed to configure lcdc_clk: %d\n", ret); |
0b776d45 RC |
406 | ret = clk_prepare_enable(mdp4_lcdc_encoder->lcdc_clk); |
407 | if (ret) | |
6a41da17 | 408 | DRM_DEV_ERROR(dev->dev, "failed to enable lcdc_clk: %d\n", ret); |
0b776d45 | 409 | |
a6bf7f63 | 410 | panel = of_drm_find_panel(mdp4_lcdc_encoder->panel_node); |
5fa8e4a2 | 411 | if (!IS_ERR(panel)) { |
095022b9 | 412 | drm_panel_prepare(panel); |
0b776d45 | 413 | drm_panel_enable(panel); |
095022b9 | 414 | } |
0b776d45 RC |
415 | |
416 | setup_phy(encoder); | |
417 | ||
418 | mdp4_write(mdp4_kms, REG_MDP4_LCDC_ENABLE, 1); | |
419 | ||
420 | mdp4_lcdc_encoder->enabled = true; | |
3e87599b RC |
421 | } |
422 | ||
423 | static const struct drm_encoder_helper_funcs mdp4_lcdc_encoder_helper_funcs = { | |
3e87599b | 424 | .mode_set = mdp4_lcdc_encoder_mode_set, |
0b776d45 RC |
425 | .disable = mdp4_lcdc_encoder_disable, |
426 | .enable = mdp4_lcdc_encoder_enable, | |
3e87599b RC |
427 | }; |
428 | ||
429 | long mdp4_lcdc_round_pixclk(struct drm_encoder *encoder, unsigned long rate) | |
430 | { | |
431 | struct mdp4_lcdc_encoder *mdp4_lcdc_encoder = | |
432 | to_mdp4_lcdc_encoder(encoder); | |
433 | return clk_round_rate(mdp4_lcdc_encoder->lcdc_clk, rate); | |
434 | } | |
435 | ||
436 | /* initialize encoder */ | |
437 | struct drm_encoder *mdp4_lcdc_encoder_init(struct drm_device *dev, | |
a6bf7f63 | 438 | struct device_node *panel_node) |
3e87599b RC |
439 | { |
440 | struct drm_encoder *encoder = NULL; | |
441 | struct mdp4_lcdc_encoder *mdp4_lcdc_encoder; | |
442 | struct regulator *reg; | |
443 | int ret; | |
444 | ||
445 | mdp4_lcdc_encoder = kzalloc(sizeof(*mdp4_lcdc_encoder), GFP_KERNEL); | |
446 | if (!mdp4_lcdc_encoder) { | |
447 | ret = -ENOMEM; | |
448 | goto fail; | |
449 | } | |
450 | ||
a6bf7f63 | 451 | mdp4_lcdc_encoder->panel_node = panel_node; |
3e87599b RC |
452 | |
453 | encoder = &mdp4_lcdc_encoder->base; | |
454 | ||
455 | drm_encoder_init(dev, encoder, &mdp4_lcdc_encoder_funcs, | |
13a3d91f | 456 | DRM_MODE_ENCODER_LVDS, NULL); |
3e87599b RC |
457 | drm_encoder_helper_add(encoder, &mdp4_lcdc_encoder_helper_funcs); |
458 | ||
459 | /* TODO: do we need different pll in other cases? */ | |
460 | mdp4_lcdc_encoder->lcdc_clk = mpd4_lvds_pll_init(dev); | |
461 | if (IS_ERR(mdp4_lcdc_encoder->lcdc_clk)) { | |
6a41da17 | 462 | DRM_DEV_ERROR(dev->dev, "failed to get lvds_clk\n"); |
3e87599b RC |
463 | ret = PTR_ERR(mdp4_lcdc_encoder->lcdc_clk); |
464 | goto fail; | |
465 | } | |
466 | ||
467 | /* TODO: different regulators in other cases? */ | |
468 | reg = devm_regulator_get(dev->dev, "lvds-vccs-3p3v"); | |
469 | if (IS_ERR(reg)) { | |
470 | ret = PTR_ERR(reg); | |
6a41da17 | 471 | DRM_DEV_ERROR(dev->dev, "failed to get lvds-vccs-3p3v: %d\n", ret); |
3e87599b RC |
472 | goto fail; |
473 | } | |
474 | mdp4_lcdc_encoder->regs[0] = reg; | |
475 | ||
476 | reg = devm_regulator_get(dev->dev, "lvds-pll-vdda"); | |
477 | if (IS_ERR(reg)) { | |
478 | ret = PTR_ERR(reg); | |
6a41da17 | 479 | DRM_DEV_ERROR(dev->dev, "failed to get lvds-pll-vdda: %d\n", ret); |
3e87599b RC |
480 | goto fail; |
481 | } | |
482 | mdp4_lcdc_encoder->regs[1] = reg; | |
483 | ||
484 | reg = devm_regulator_get(dev->dev, "lvds-vdda"); | |
485 | if (IS_ERR(reg)) { | |
486 | ret = PTR_ERR(reg); | |
6a41da17 | 487 | DRM_DEV_ERROR(dev->dev, "failed to get lvds-vdda: %d\n", ret); |
3e87599b RC |
488 | goto fail; |
489 | } | |
490 | mdp4_lcdc_encoder->regs[2] = reg; | |
491 | ||
492 | bs_init(mdp4_lcdc_encoder); | |
493 | ||
494 | return encoder; | |
495 | ||
496 | fail: | |
497 | if (encoder) | |
498 | mdp4_lcdc_encoder_destroy(encoder); | |
499 | ||
500 | return ERR_PTR(ret); | |
501 | } |