Commit | Line | Data |
---|---|---|
c8afe684 RC |
1 | /* |
2 | * Copyright (C) 2013 Red Hat | |
3 | * Author: Rob Clark <robdclark@gmail.com> | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify it | |
6 | * under the terms of the GNU General Public License version 2 as published by | |
7 | * the Free Software Foundation. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, but WITHOUT | |
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
12 | * more details. | |
13 | * | |
14 | * You should have received a copy of the GNU General Public License along with | |
15 | * this program. If not, see <http://www.gnu.org/licenses/>. | |
16 | */ | |
17 | ||
c8afe684 | 18 | #include "mdp4_kms.h" |
c8afe684 RC |
19 | |
20 | #include "drm_crtc.h" | |
21 | #include "drm_crtc_helper.h" | |
22 | ||
23 | ||
24 | struct mdp4_dtv_encoder { | |
25 | struct drm_encoder base; | |
26 | struct clk *src_clk; | |
27 | struct clk *hdmi_clk; | |
28 | struct clk *mdp_clk; | |
29 | unsigned long int pixclock; | |
30 | bool enabled; | |
31 | uint32_t bsc; | |
32 | }; | |
33 | #define to_mdp4_dtv_encoder(x) container_of(x, struct mdp4_dtv_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; | |
9e0efa63 | 38 | return to_mdp4_kms(to_mdp_kms(priv->kms)); |
c8afe684 RC |
39 | } |
40 | ||
6490ad47 | 41 | #ifdef DOWNSTREAM_CONFIG_MSM_BUS_SCALING |
c8afe684 RC |
42 | #include <mach/board.h> |
43 | /* not ironically named at all.. no, really.. */ | |
44 | static void bs_init(struct mdp4_dtv_encoder *mdp4_dtv_encoder) | |
45 | { | |
46 | struct drm_device *dev = mdp4_dtv_encoder->base.dev; | |
47 | struct lcdc_platform_data *dtv_pdata = mdp4_find_pdata("dtv.0"); | |
48 | ||
49 | if (!dtv_pdata) { | |
50 | dev_err(dev->dev, "could not find dtv pdata\n"); | |
51 | return; | |
52 | } | |
53 | ||
54 | if (dtv_pdata->bus_scale_table) { | |
55 | mdp4_dtv_encoder->bsc = msm_bus_scale_register_client( | |
56 | dtv_pdata->bus_scale_table); | |
57 | DBG("bus scale client: %08x", mdp4_dtv_encoder->bsc); | |
58 | DBG("lcdc_power_save: %p", dtv_pdata->lcdc_power_save); | |
59 | if (dtv_pdata->lcdc_power_save) | |
60 | dtv_pdata->lcdc_power_save(1); | |
61 | } | |
62 | } | |
63 | ||
64 | static void bs_fini(struct mdp4_dtv_encoder *mdp4_dtv_encoder) | |
65 | { | |
66 | if (mdp4_dtv_encoder->bsc) { | |
67 | msm_bus_scale_unregister_client(mdp4_dtv_encoder->bsc); | |
68 | mdp4_dtv_encoder->bsc = 0; | |
69 | } | |
70 | } | |
71 | ||
72 | static void bs_set(struct mdp4_dtv_encoder *mdp4_dtv_encoder, int idx) | |
73 | { | |
74 | if (mdp4_dtv_encoder->bsc) { | |
75 | DBG("set bus scaling: %d", idx); | |
76 | msm_bus_scale_client_update_request(mdp4_dtv_encoder->bsc, idx); | |
77 | } | |
78 | } | |
79 | #else | |
80 | static void bs_init(struct mdp4_dtv_encoder *mdp4_dtv_encoder) {} | |
81 | static void bs_fini(struct mdp4_dtv_encoder *mdp4_dtv_encoder) {} | |
82 | static void bs_set(struct mdp4_dtv_encoder *mdp4_dtv_encoder, int idx) {} | |
83 | #endif | |
84 | ||
85 | static void mdp4_dtv_encoder_destroy(struct drm_encoder *encoder) | |
86 | { | |
87 | struct mdp4_dtv_encoder *mdp4_dtv_encoder = to_mdp4_dtv_encoder(encoder); | |
88 | bs_fini(mdp4_dtv_encoder); | |
89 | drm_encoder_cleanup(encoder); | |
90 | kfree(mdp4_dtv_encoder); | |
91 | } | |
92 | ||
93 | static const struct drm_encoder_funcs mdp4_dtv_encoder_funcs = { | |
94 | .destroy = mdp4_dtv_encoder_destroy, | |
95 | }; | |
96 | ||
c8afe684 RC |
97 | static bool mdp4_dtv_encoder_mode_fixup(struct drm_encoder *encoder, |
98 | const struct drm_display_mode *mode, | |
99 | struct drm_display_mode *adjusted_mode) | |
100 | { | |
101 | return true; | |
102 | } | |
103 | ||
104 | static void mdp4_dtv_encoder_mode_set(struct drm_encoder *encoder, | |
105 | struct drm_display_mode *mode, | |
106 | struct drm_display_mode *adjusted_mode) | |
107 | { | |
108 | struct mdp4_dtv_encoder *mdp4_dtv_encoder = to_mdp4_dtv_encoder(encoder); | |
c8afe684 RC |
109 | struct mdp4_kms *mdp4_kms = get_kms(encoder); |
110 | uint32_t dtv_hsync_skew, vsync_period, vsync_len, ctrl_pol; | |
111 | uint32_t display_v_start, display_v_end; | |
112 | uint32_t hsync_start_x, hsync_end_x; | |
113 | ||
114 | mode = adjusted_mode; | |
115 | ||
116 | DBG("set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x", | |
117 | mode->base.id, mode->name, | |
118 | mode->vrefresh, mode->clock, | |
119 | mode->hdisplay, mode->hsync_start, | |
120 | mode->hsync_end, mode->htotal, | |
121 | mode->vdisplay, mode->vsync_start, | |
122 | mode->vsync_end, mode->vtotal, | |
123 | mode->type, mode->flags); | |
124 | ||
125 | mdp4_dtv_encoder->pixclock = mode->clock * 1000; | |
126 | ||
127 | DBG("pixclock=%lu", mdp4_dtv_encoder->pixclock); | |
128 | ||
129 | ctrl_pol = 0; | |
130 | if (mode->flags & DRM_MODE_FLAG_NHSYNC) | |
131 | ctrl_pol |= MDP4_DTV_CTRL_POLARITY_HSYNC_LOW; | |
132 | if (mode->flags & DRM_MODE_FLAG_NVSYNC) | |
133 | ctrl_pol |= MDP4_DTV_CTRL_POLARITY_VSYNC_LOW; | |
134 | /* probably need to get DATA_EN polarity from panel.. */ | |
135 | ||
136 | dtv_hsync_skew = 0; /* get this from panel? */ | |
137 | ||
138 | hsync_start_x = (mode->htotal - mode->hsync_start); | |
139 | hsync_end_x = mode->htotal - (mode->hsync_start - mode->hdisplay) - 1; | |
140 | ||
141 | vsync_period = mode->vtotal * mode->htotal; | |
142 | vsync_len = (mode->vsync_end - mode->vsync_start) * mode->htotal; | |
143 | display_v_start = (mode->vtotal - mode->vsync_start) * mode->htotal + dtv_hsync_skew; | |
144 | display_v_end = vsync_period - ((mode->vsync_start - mode->vdisplay) * mode->htotal) + dtv_hsync_skew - 1; | |
145 | ||
146 | mdp4_write(mdp4_kms, REG_MDP4_DTV_HSYNC_CTRL, | |
147 | MDP4_DTV_HSYNC_CTRL_PULSEW(mode->hsync_end - mode->hsync_start) | | |
148 | MDP4_DTV_HSYNC_CTRL_PERIOD(mode->htotal)); | |
149 | mdp4_write(mdp4_kms, REG_MDP4_DTV_VSYNC_PERIOD, vsync_period); | |
150 | mdp4_write(mdp4_kms, REG_MDP4_DTV_VSYNC_LEN, vsync_len); | |
151 | mdp4_write(mdp4_kms, REG_MDP4_DTV_DISPLAY_HCTRL, | |
152 | MDP4_DTV_DISPLAY_HCTRL_START(hsync_start_x) | | |
153 | MDP4_DTV_DISPLAY_HCTRL_END(hsync_end_x)); | |
154 | mdp4_write(mdp4_kms, REG_MDP4_DTV_DISPLAY_VSTART, display_v_start); | |
155 | mdp4_write(mdp4_kms, REG_MDP4_DTV_DISPLAY_VEND, display_v_end); | |
156 | mdp4_write(mdp4_kms, REG_MDP4_DTV_BORDER_CLR, 0); | |
157 | mdp4_write(mdp4_kms, REG_MDP4_DTV_UNDERFLOW_CLR, | |
158 | MDP4_DTV_UNDERFLOW_CLR_ENABLE_RECOVERY | | |
159 | MDP4_DTV_UNDERFLOW_CLR_COLOR(0xff)); | |
160 | mdp4_write(mdp4_kms, REG_MDP4_DTV_HSYNC_SKEW, dtv_hsync_skew); | |
161 | mdp4_write(mdp4_kms, REG_MDP4_DTV_CTRL_POLARITY, ctrl_pol); | |
162 | mdp4_write(mdp4_kms, REG_MDP4_DTV_ACTIVE_HCTL, | |
163 | MDP4_DTV_ACTIVE_HCTL_START(0) | | |
164 | MDP4_DTV_ACTIVE_HCTL_END(0)); | |
165 | mdp4_write(mdp4_kms, REG_MDP4_DTV_ACTIVE_VSTART, 0); | |
166 | mdp4_write(mdp4_kms, REG_MDP4_DTV_ACTIVE_VEND, 0); | |
c8afe684 RC |
167 | } |
168 | ||
0b776d45 | 169 | static void mdp4_dtv_encoder_disable(struct drm_encoder *encoder) |
c8afe684 | 170 | { |
0b776d45 RC |
171 | struct mdp4_dtv_encoder *mdp4_dtv_encoder = to_mdp4_dtv_encoder(encoder); |
172 | struct mdp4_kms *mdp4_kms = get_kms(encoder); | |
173 | ||
174 | if (WARN_ON(!mdp4_dtv_encoder->enabled)) | |
175 | return; | |
176 | ||
177 | mdp4_write(mdp4_kms, REG_MDP4_DTV_ENABLE, 0); | |
178 | ||
179 | /* | |
180 | * Wait for a vsync so we know the ENABLE=0 latched before | |
181 | * the (connector) source of the vsync's gets disabled, | |
182 | * otherwise we end up in a funny state if we re-enable | |
183 | * before the disable latches, which results that some of | |
184 | * the settings changes for the new modeset (like new | |
185 | * scanout buffer) don't latch properly.. | |
186 | */ | |
187 | mdp_irq_wait(&mdp4_kms->base, MDP4_IRQ_EXTERNAL_VSYNC); | |
188 | ||
189 | clk_disable_unprepare(mdp4_dtv_encoder->src_clk); | |
190 | clk_disable_unprepare(mdp4_dtv_encoder->hdmi_clk); | |
191 | clk_disable_unprepare(mdp4_dtv_encoder->mdp_clk); | |
192 | ||
193 | bs_set(mdp4_dtv_encoder, 0); | |
194 | ||
195 | mdp4_dtv_encoder->enabled = false; | |
c8afe684 RC |
196 | } |
197 | ||
0b776d45 | 198 | static void mdp4_dtv_encoder_enable(struct drm_encoder *encoder) |
c8afe684 | 199 | { |
0b776d45 RC |
200 | struct drm_device *dev = encoder->dev; |
201 | struct mdp4_dtv_encoder *mdp4_dtv_encoder = to_mdp4_dtv_encoder(encoder); | |
202 | struct mdp4_kms *mdp4_kms = get_kms(encoder); | |
203 | unsigned long pc = mdp4_dtv_encoder->pixclock; | |
204 | int ret; | |
205 | ||
206 | if (WARN_ON(mdp4_dtv_encoder->enabled)) | |
207 | return; | |
208 | ||
c8afe684 RC |
209 | mdp4_crtc_set_config(encoder->crtc, |
210 | MDP4_DMA_CONFIG_R_BPC(BPC8) | | |
211 | MDP4_DMA_CONFIG_G_BPC(BPC8) | | |
212 | MDP4_DMA_CONFIG_B_BPC(BPC8) | | |
213 | MDP4_DMA_CONFIG_PACK(0x21)); | |
d65bd0e4 | 214 | mdp4_crtc_set_intf(encoder->crtc, INTF_LCDC_DTV, 1); |
0b776d45 RC |
215 | |
216 | bs_set(mdp4_dtv_encoder, 1); | |
217 | ||
218 | DBG("setting src_clk=%lu", pc); | |
219 | ||
220 | ret = clk_set_rate(mdp4_dtv_encoder->src_clk, pc); | |
221 | if (ret) | |
222 | dev_err(dev->dev, "failed to set src_clk to %lu: %d\n", pc, ret); | |
223 | clk_prepare_enable(mdp4_dtv_encoder->src_clk); | |
224 | ret = clk_prepare_enable(mdp4_dtv_encoder->hdmi_clk); | |
225 | if (ret) | |
226 | dev_err(dev->dev, "failed to enable hdmi_clk: %d\n", ret); | |
227 | ret = clk_prepare_enable(mdp4_dtv_encoder->mdp_clk); | |
228 | if (ret) | |
229 | dev_err(dev->dev, "failed to enabled mdp_clk: %d\n", ret); | |
230 | ||
231 | mdp4_write(mdp4_kms, REG_MDP4_DTV_ENABLE, 1); | |
232 | ||
233 | mdp4_dtv_encoder->enabled = true; | |
c8afe684 RC |
234 | } |
235 | ||
236 | static const struct drm_encoder_helper_funcs mdp4_dtv_encoder_helper_funcs = { | |
c8afe684 RC |
237 | .mode_fixup = mdp4_dtv_encoder_mode_fixup, |
238 | .mode_set = mdp4_dtv_encoder_mode_set, | |
0b776d45 RC |
239 | .enable = mdp4_dtv_encoder_enable, |
240 | .disable = mdp4_dtv_encoder_disable, | |
c8afe684 RC |
241 | }; |
242 | ||
243 | long mdp4_dtv_round_pixclk(struct drm_encoder *encoder, unsigned long rate) | |
244 | { | |
245 | struct mdp4_dtv_encoder *mdp4_dtv_encoder = to_mdp4_dtv_encoder(encoder); | |
246 | return clk_round_rate(mdp4_dtv_encoder->src_clk, rate); | |
247 | } | |
248 | ||
249 | /* initialize encoder */ | |
250 | struct drm_encoder *mdp4_dtv_encoder_init(struct drm_device *dev) | |
251 | { | |
252 | struct drm_encoder *encoder = NULL; | |
253 | struct mdp4_dtv_encoder *mdp4_dtv_encoder; | |
254 | int ret; | |
255 | ||
256 | mdp4_dtv_encoder = kzalloc(sizeof(*mdp4_dtv_encoder), GFP_KERNEL); | |
257 | if (!mdp4_dtv_encoder) { | |
258 | ret = -ENOMEM; | |
259 | goto fail; | |
260 | } | |
261 | ||
262 | encoder = &mdp4_dtv_encoder->base; | |
263 | ||
264 | drm_encoder_init(dev, encoder, &mdp4_dtv_encoder_funcs, | |
13a3d91f | 265 | DRM_MODE_ENCODER_TMDS, NULL); |
c8afe684 RC |
266 | drm_encoder_helper_add(encoder, &mdp4_dtv_encoder_helper_funcs); |
267 | ||
268 | mdp4_dtv_encoder->src_clk = devm_clk_get(dev->dev, "src_clk"); | |
269 | if (IS_ERR(mdp4_dtv_encoder->src_clk)) { | |
270 | dev_err(dev->dev, "failed to get src_clk\n"); | |
271 | ret = PTR_ERR(mdp4_dtv_encoder->src_clk); | |
272 | goto fail; | |
273 | } | |
274 | ||
275 | mdp4_dtv_encoder->hdmi_clk = devm_clk_get(dev->dev, "hdmi_clk"); | |
276 | if (IS_ERR(mdp4_dtv_encoder->hdmi_clk)) { | |
277 | dev_err(dev->dev, "failed to get hdmi_clk\n"); | |
278 | ret = PTR_ERR(mdp4_dtv_encoder->hdmi_clk); | |
279 | goto fail; | |
280 | } | |
281 | ||
282 | mdp4_dtv_encoder->mdp_clk = devm_clk_get(dev->dev, "mdp_clk"); | |
283 | if (IS_ERR(mdp4_dtv_encoder->mdp_clk)) { | |
284 | dev_err(dev->dev, "failed to get mdp_clk\n"); | |
285 | ret = PTR_ERR(mdp4_dtv_encoder->mdp_clk); | |
286 | goto fail; | |
287 | } | |
288 | ||
289 | bs_init(mdp4_dtv_encoder); | |
290 | ||
291 | return encoder; | |
292 | ||
293 | fail: | |
294 | if (encoder) | |
295 | mdp4_dtv_encoder_destroy(encoder); | |
296 | ||
297 | return ERR_PTR(ret); | |
298 | } |