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 | ||
41 | #ifdef CONFIG_MSM_BUS_SCALING | |
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 | ||
97 | static void mdp4_dtv_encoder_dpms(struct drm_encoder *encoder, int mode) | |
98 | { | |
99 | struct drm_device *dev = encoder->dev; | |
100 | struct mdp4_dtv_encoder *mdp4_dtv_encoder = to_mdp4_dtv_encoder(encoder); | |
c8afe684 RC |
101 | struct mdp4_kms *mdp4_kms = get_kms(encoder); |
102 | bool enabled = (mode == DRM_MODE_DPMS_ON); | |
103 | ||
104 | DBG("mode=%d", mode); | |
105 | ||
106 | if (enabled == mdp4_dtv_encoder->enabled) | |
107 | return; | |
108 | ||
109 | if (enabled) { | |
110 | unsigned long pc = mdp4_dtv_encoder->pixclock; | |
111 | int ret; | |
112 | ||
113 | bs_set(mdp4_dtv_encoder, 1); | |
114 | ||
c8afe684 RC |
115 | DBG("setting src_clk=%lu", pc); |
116 | ||
117 | ret = clk_set_rate(mdp4_dtv_encoder->src_clk, pc); | |
118 | if (ret) | |
119 | dev_err(dev->dev, "failed to set src_clk to %lu: %d\n", pc, ret); | |
120 | clk_prepare_enable(mdp4_dtv_encoder->src_clk); | |
121 | ret = clk_prepare_enable(mdp4_dtv_encoder->hdmi_clk); | |
122 | if (ret) | |
123 | dev_err(dev->dev, "failed to enable hdmi_clk: %d\n", ret); | |
124 | ret = clk_prepare_enable(mdp4_dtv_encoder->mdp_clk); | |
125 | if (ret) | |
126 | dev_err(dev->dev, "failed to enabled mdp_clk: %d\n", ret); | |
127 | ||
128 | mdp4_write(mdp4_kms, REG_MDP4_DTV_ENABLE, 1); | |
129 | } else { | |
130 | mdp4_write(mdp4_kms, REG_MDP4_DTV_ENABLE, 0); | |
131 | ||
132 | /* | |
133 | * Wait for a vsync so we know the ENABLE=0 latched before | |
134 | * the (connector) source of the vsync's gets disabled, | |
135 | * otherwise we end up in a funny state if we re-enable | |
136 | * before the disable latches, which results that some of | |
137 | * the settings changes for the new modeset (like new | |
138 | * scanout buffer) don't latch properly.. | |
139 | */ | |
9e0efa63 | 140 | mdp_irq_wait(&mdp4_kms->base, MDP4_IRQ_EXTERNAL_VSYNC); |
c8afe684 RC |
141 | |
142 | clk_disable_unprepare(mdp4_dtv_encoder->src_clk); | |
143 | clk_disable_unprepare(mdp4_dtv_encoder->hdmi_clk); | |
144 | clk_disable_unprepare(mdp4_dtv_encoder->mdp_clk); | |
145 | ||
c8afe684 RC |
146 | bs_set(mdp4_dtv_encoder, 0); |
147 | } | |
148 | ||
149 | mdp4_dtv_encoder->enabled = enabled; | |
150 | } | |
151 | ||
152 | static bool mdp4_dtv_encoder_mode_fixup(struct drm_encoder *encoder, | |
153 | const struct drm_display_mode *mode, | |
154 | struct drm_display_mode *adjusted_mode) | |
155 | { | |
156 | return true; | |
157 | } | |
158 | ||
159 | static void mdp4_dtv_encoder_mode_set(struct drm_encoder *encoder, | |
160 | struct drm_display_mode *mode, | |
161 | struct drm_display_mode *adjusted_mode) | |
162 | { | |
163 | struct mdp4_dtv_encoder *mdp4_dtv_encoder = to_mdp4_dtv_encoder(encoder); | |
c8afe684 RC |
164 | struct mdp4_kms *mdp4_kms = get_kms(encoder); |
165 | uint32_t dtv_hsync_skew, vsync_period, vsync_len, ctrl_pol; | |
166 | uint32_t display_v_start, display_v_end; | |
167 | uint32_t hsync_start_x, hsync_end_x; | |
168 | ||
169 | mode = adjusted_mode; | |
170 | ||
171 | DBG("set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x", | |
172 | mode->base.id, mode->name, | |
173 | mode->vrefresh, mode->clock, | |
174 | mode->hdisplay, mode->hsync_start, | |
175 | mode->hsync_end, mode->htotal, | |
176 | mode->vdisplay, mode->vsync_start, | |
177 | mode->vsync_end, mode->vtotal, | |
178 | mode->type, mode->flags); | |
179 | ||
180 | mdp4_dtv_encoder->pixclock = mode->clock * 1000; | |
181 | ||
182 | DBG("pixclock=%lu", mdp4_dtv_encoder->pixclock); | |
183 | ||
184 | ctrl_pol = 0; | |
185 | if (mode->flags & DRM_MODE_FLAG_NHSYNC) | |
186 | ctrl_pol |= MDP4_DTV_CTRL_POLARITY_HSYNC_LOW; | |
187 | if (mode->flags & DRM_MODE_FLAG_NVSYNC) | |
188 | ctrl_pol |= MDP4_DTV_CTRL_POLARITY_VSYNC_LOW; | |
189 | /* probably need to get DATA_EN polarity from panel.. */ | |
190 | ||
191 | dtv_hsync_skew = 0; /* get this from panel? */ | |
192 | ||
193 | hsync_start_x = (mode->htotal - mode->hsync_start); | |
194 | hsync_end_x = mode->htotal - (mode->hsync_start - mode->hdisplay) - 1; | |
195 | ||
196 | vsync_period = mode->vtotal * mode->htotal; | |
197 | vsync_len = (mode->vsync_end - mode->vsync_start) * mode->htotal; | |
198 | display_v_start = (mode->vtotal - mode->vsync_start) * mode->htotal + dtv_hsync_skew; | |
199 | display_v_end = vsync_period - ((mode->vsync_start - mode->vdisplay) * mode->htotal) + dtv_hsync_skew - 1; | |
200 | ||
201 | mdp4_write(mdp4_kms, REG_MDP4_DTV_HSYNC_CTRL, | |
202 | MDP4_DTV_HSYNC_CTRL_PULSEW(mode->hsync_end - mode->hsync_start) | | |
203 | MDP4_DTV_HSYNC_CTRL_PERIOD(mode->htotal)); | |
204 | mdp4_write(mdp4_kms, REG_MDP4_DTV_VSYNC_PERIOD, vsync_period); | |
205 | mdp4_write(mdp4_kms, REG_MDP4_DTV_VSYNC_LEN, vsync_len); | |
206 | mdp4_write(mdp4_kms, REG_MDP4_DTV_DISPLAY_HCTRL, | |
207 | MDP4_DTV_DISPLAY_HCTRL_START(hsync_start_x) | | |
208 | MDP4_DTV_DISPLAY_HCTRL_END(hsync_end_x)); | |
209 | mdp4_write(mdp4_kms, REG_MDP4_DTV_DISPLAY_VSTART, display_v_start); | |
210 | mdp4_write(mdp4_kms, REG_MDP4_DTV_DISPLAY_VEND, display_v_end); | |
211 | mdp4_write(mdp4_kms, REG_MDP4_DTV_BORDER_CLR, 0); | |
212 | mdp4_write(mdp4_kms, REG_MDP4_DTV_UNDERFLOW_CLR, | |
213 | MDP4_DTV_UNDERFLOW_CLR_ENABLE_RECOVERY | | |
214 | MDP4_DTV_UNDERFLOW_CLR_COLOR(0xff)); | |
215 | mdp4_write(mdp4_kms, REG_MDP4_DTV_HSYNC_SKEW, dtv_hsync_skew); | |
216 | mdp4_write(mdp4_kms, REG_MDP4_DTV_CTRL_POLARITY, ctrl_pol); | |
217 | mdp4_write(mdp4_kms, REG_MDP4_DTV_ACTIVE_HCTL, | |
218 | MDP4_DTV_ACTIVE_HCTL_START(0) | | |
219 | MDP4_DTV_ACTIVE_HCTL_END(0)); | |
220 | mdp4_write(mdp4_kms, REG_MDP4_DTV_ACTIVE_VSTART, 0); | |
221 | mdp4_write(mdp4_kms, REG_MDP4_DTV_ACTIVE_VEND, 0); | |
c8afe684 RC |
222 | } |
223 | ||
224 | static void mdp4_dtv_encoder_prepare(struct drm_encoder *encoder) | |
225 | { | |
226 | mdp4_dtv_encoder_dpms(encoder, DRM_MODE_DPMS_OFF); | |
227 | } | |
228 | ||
229 | static void mdp4_dtv_encoder_commit(struct drm_encoder *encoder) | |
230 | { | |
231 | mdp4_crtc_set_config(encoder->crtc, | |
232 | MDP4_DMA_CONFIG_R_BPC(BPC8) | | |
233 | MDP4_DMA_CONFIG_G_BPC(BPC8) | | |
234 | MDP4_DMA_CONFIG_B_BPC(BPC8) | | |
235 | MDP4_DMA_CONFIG_PACK(0x21)); | |
236 | mdp4_crtc_set_intf(encoder->crtc, INTF_LCDC_DTV); | |
237 | mdp4_dtv_encoder_dpms(encoder, DRM_MODE_DPMS_ON); | |
238 | } | |
239 | ||
240 | static const struct drm_encoder_helper_funcs mdp4_dtv_encoder_helper_funcs = { | |
241 | .dpms = mdp4_dtv_encoder_dpms, | |
242 | .mode_fixup = mdp4_dtv_encoder_mode_fixup, | |
243 | .mode_set = mdp4_dtv_encoder_mode_set, | |
244 | .prepare = mdp4_dtv_encoder_prepare, | |
245 | .commit = mdp4_dtv_encoder_commit, | |
246 | }; | |
247 | ||
248 | long mdp4_dtv_round_pixclk(struct drm_encoder *encoder, unsigned long rate) | |
249 | { | |
250 | struct mdp4_dtv_encoder *mdp4_dtv_encoder = to_mdp4_dtv_encoder(encoder); | |
251 | return clk_round_rate(mdp4_dtv_encoder->src_clk, rate); | |
252 | } | |
253 | ||
254 | /* initialize encoder */ | |
255 | struct drm_encoder *mdp4_dtv_encoder_init(struct drm_device *dev) | |
256 | { | |
257 | struct drm_encoder *encoder = NULL; | |
258 | struct mdp4_dtv_encoder *mdp4_dtv_encoder; | |
259 | int ret; | |
260 | ||
261 | mdp4_dtv_encoder = kzalloc(sizeof(*mdp4_dtv_encoder), GFP_KERNEL); | |
262 | if (!mdp4_dtv_encoder) { | |
263 | ret = -ENOMEM; | |
264 | goto fail; | |
265 | } | |
266 | ||
267 | encoder = &mdp4_dtv_encoder->base; | |
268 | ||
269 | drm_encoder_init(dev, encoder, &mdp4_dtv_encoder_funcs, | |
270 | DRM_MODE_ENCODER_TMDS); | |
271 | drm_encoder_helper_add(encoder, &mdp4_dtv_encoder_helper_funcs); | |
272 | ||
273 | mdp4_dtv_encoder->src_clk = devm_clk_get(dev->dev, "src_clk"); | |
274 | if (IS_ERR(mdp4_dtv_encoder->src_clk)) { | |
275 | dev_err(dev->dev, "failed to get src_clk\n"); | |
276 | ret = PTR_ERR(mdp4_dtv_encoder->src_clk); | |
277 | goto fail; | |
278 | } | |
279 | ||
280 | mdp4_dtv_encoder->hdmi_clk = devm_clk_get(dev->dev, "hdmi_clk"); | |
281 | if (IS_ERR(mdp4_dtv_encoder->hdmi_clk)) { | |
282 | dev_err(dev->dev, "failed to get hdmi_clk\n"); | |
283 | ret = PTR_ERR(mdp4_dtv_encoder->hdmi_clk); | |
284 | goto fail; | |
285 | } | |
286 | ||
287 | mdp4_dtv_encoder->mdp_clk = devm_clk_get(dev->dev, "mdp_clk"); | |
288 | if (IS_ERR(mdp4_dtv_encoder->mdp_clk)) { | |
289 | dev_err(dev->dev, "failed to get mdp_clk\n"); | |
290 | ret = PTR_ERR(mdp4_dtv_encoder->mdp_clk); | |
291 | goto fail; | |
292 | } | |
293 | ||
294 | bs_init(mdp4_dtv_encoder); | |
295 | ||
296 | return encoder; | |
297 | ||
298 | fail: | |
299 | if (encoder) | |
300 | mdp4_dtv_encoder_destroy(encoder); | |
301 | ||
302 | return ERR_PTR(ret); | |
303 | } |