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