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