diff options
author | Patrik Jakobsson <patrik.r.jakobsson@gmail.com> | 2013-07-05 10:41:49 -0400 |
---|---|---|
committer | Patrik Jakobsson <patrik.r.jakobsson@gmail.com> | 2013-07-23 19:47:19 -0400 |
commit | 2eff0b3359c097bbcfe4850dfdf9c94e514ddfee (patch) | |
tree | 67d068335a54064b4c2b4e7c156ad750df160a95 /drivers/gpu/drm/gma500 | |
parent | f0e9d89b9b7f3c4b1d21ca2e0d25e3ebe5d2a1d2 (diff) |
drm/gma500: Add generic pipe/crtc functions
Signed-off-by: Patrik Jakobsson <patrik.r.jakobsson@gmail.com>
Diffstat (limited to 'drivers/gpu/drm/gma500')
-rw-r--r-- | drivers/gpu/drm/gma500/gma_display.c | 326 | ||||
-rw-r--r-- | drivers/gpu/drm/gma500/gma_display.h | 14 |
2 files changed, 340 insertions, 0 deletions
diff --git a/drivers/gpu/drm/gma500/gma_display.c b/drivers/gpu/drm/gma500/gma_display.c index 8f66d5c6505b..297937d12fb9 100644 --- a/drivers/gpu/drm/gma500/gma_display.c +++ b/drivers/gpu/drm/gma500/gma_display.c | |||
@@ -24,6 +24,7 @@ | |||
24 | #include "psb_intel_drv.h" | 24 | #include "psb_intel_drv.h" |
25 | #include "psb_intel_reg.h" | 25 | #include "psb_intel_reg.h" |
26 | #include "psb_drv.h" | 26 | #include "psb_drv.h" |
27 | #include "framebuffer.h" | ||
27 | 28 | ||
28 | /** | 29 | /** |
29 | * Returns whether any output on the specified pipe is of the specified type | 30 | * Returns whether any output on the specified pipe is of the specified type |
@@ -46,6 +47,331 @@ bool gma_pipe_has_type(struct drm_crtc *crtc, int type) | |||
46 | return false; | 47 | return false; |
47 | } | 48 | } |
48 | 49 | ||
50 | void gma_wait_for_vblank(struct drm_device *dev) | ||
51 | { | ||
52 | /* Wait for 20ms, i.e. one cycle at 50hz. */ | ||
53 | mdelay(20); | ||
54 | } | ||
55 | |||
56 | int gma_pipe_set_base(struct drm_crtc *crtc, int x, int y, | ||
57 | struct drm_framebuffer *old_fb) | ||
58 | { | ||
59 | struct drm_device *dev = crtc->dev; | ||
60 | struct drm_psb_private *dev_priv = dev->dev_private; | ||
61 | struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); | ||
62 | struct psb_framebuffer *psbfb = to_psb_fb(crtc->fb); | ||
63 | int pipe = psb_intel_crtc->pipe; | ||
64 | const struct psb_offset *map = &dev_priv->regmap[pipe]; | ||
65 | unsigned long start, offset; | ||
66 | u32 dspcntr; | ||
67 | int ret = 0; | ||
68 | |||
69 | if (!gma_power_begin(dev, true)) | ||
70 | return 0; | ||
71 | |||
72 | /* no fb bound */ | ||
73 | if (!crtc->fb) { | ||
74 | dev_err(dev->dev, "No FB bound\n"); | ||
75 | goto gma_pipe_cleaner; | ||
76 | } | ||
77 | |||
78 | /* We are displaying this buffer, make sure it is actually loaded | ||
79 | into the GTT */ | ||
80 | ret = psb_gtt_pin(psbfb->gtt); | ||
81 | if (ret < 0) | ||
82 | goto gma_pipe_set_base_exit; | ||
83 | start = psbfb->gtt->offset; | ||
84 | offset = y * crtc->fb->pitches[0] + x * (crtc->fb->bits_per_pixel / 8); | ||
85 | |||
86 | REG_WRITE(map->stride, crtc->fb->pitches[0]); | ||
87 | |||
88 | dspcntr = REG_READ(map->cntr); | ||
89 | dspcntr &= ~DISPPLANE_PIXFORMAT_MASK; | ||
90 | |||
91 | switch (crtc->fb->bits_per_pixel) { | ||
92 | case 8: | ||
93 | dspcntr |= DISPPLANE_8BPP; | ||
94 | break; | ||
95 | case 16: | ||
96 | if (crtc->fb->depth == 15) | ||
97 | dspcntr |= DISPPLANE_15_16BPP; | ||
98 | else | ||
99 | dspcntr |= DISPPLANE_16BPP; | ||
100 | break; | ||
101 | case 24: | ||
102 | case 32: | ||
103 | dspcntr |= DISPPLANE_32BPP_NO_ALPHA; | ||
104 | break; | ||
105 | default: | ||
106 | dev_err(dev->dev, "Unknown color depth\n"); | ||
107 | ret = -EINVAL; | ||
108 | goto gma_pipe_set_base_exit; | ||
109 | } | ||
110 | REG_WRITE(map->cntr, dspcntr); | ||
111 | |||
112 | dev_dbg(dev->dev, | ||
113 | "Writing base %08lX %08lX %d %d\n", start, offset, x, y); | ||
114 | |||
115 | /* FIXME: Investigate whether this really is the base for psb and why | ||
116 | the linear offset is named base for the other chips. map->surf | ||
117 | should be the base and map->linoff the offset for all chips */ | ||
118 | if (IS_PSB(dev)) { | ||
119 | REG_WRITE(map->base, offset + start); | ||
120 | REG_READ(map->base); | ||
121 | } else { | ||
122 | REG_WRITE(map->base, offset); | ||
123 | REG_READ(map->base); | ||
124 | REG_WRITE(map->surf, start); | ||
125 | REG_READ(map->surf); | ||
126 | } | ||
127 | |||
128 | gma_pipe_cleaner: | ||
129 | /* If there was a previous display we can now unpin it */ | ||
130 | if (old_fb) | ||
131 | psb_gtt_unpin(to_psb_fb(old_fb)->gtt); | ||
132 | |||
133 | gma_pipe_set_base_exit: | ||
134 | gma_power_end(dev); | ||
135 | return ret; | ||
136 | } | ||
137 | |||
138 | /* Loads the palette/gamma unit for the CRTC with the prepared values */ | ||
139 | void gma_crtc_load_lut(struct drm_crtc *crtc) | ||
140 | { | ||
141 | struct drm_device *dev = crtc->dev; | ||
142 | struct drm_psb_private *dev_priv = dev->dev_private; | ||
143 | struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); | ||
144 | const struct psb_offset *map = &dev_priv->regmap[psb_intel_crtc->pipe]; | ||
145 | int palreg = map->palette; | ||
146 | int i; | ||
147 | |||
148 | /* The clocks have to be on to load the palette. */ | ||
149 | if (!crtc->enabled) | ||
150 | return; | ||
151 | |||
152 | if (gma_power_begin(dev, false)) { | ||
153 | for (i = 0; i < 256; i++) { | ||
154 | REG_WRITE(palreg + 4 * i, | ||
155 | ((psb_intel_crtc->lut_r[i] + | ||
156 | psb_intel_crtc->lut_adj[i]) << 16) | | ||
157 | ((psb_intel_crtc->lut_g[i] + | ||
158 | psb_intel_crtc->lut_adj[i]) << 8) | | ||
159 | (psb_intel_crtc->lut_b[i] + | ||
160 | psb_intel_crtc->lut_adj[i])); | ||
161 | } | ||
162 | gma_power_end(dev); | ||
163 | } else { | ||
164 | for (i = 0; i < 256; i++) { | ||
165 | /* FIXME: Why pipe[0] and not pipe[..._crtc->pipe]? */ | ||
166 | dev_priv->regs.pipe[0].palette[i] = | ||
167 | ((psb_intel_crtc->lut_r[i] + | ||
168 | psb_intel_crtc->lut_adj[i]) << 16) | | ||
169 | ((psb_intel_crtc->lut_g[i] + | ||
170 | psb_intel_crtc->lut_adj[i]) << 8) | | ||
171 | (psb_intel_crtc->lut_b[i] + | ||
172 | psb_intel_crtc->lut_adj[i]); | ||
173 | } | ||
174 | |||
175 | } | ||
176 | } | ||
177 | |||
178 | void gma_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, u16 *blue, | ||
179 | u32 start, u32 size) | ||
180 | { | ||
181 | struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); | ||
182 | int i; | ||
183 | int end = (start + size > 256) ? 256 : start + size; | ||
184 | |||
185 | for (i = start; i < end; i++) { | ||
186 | psb_intel_crtc->lut_r[i] = red[i] >> 8; | ||
187 | psb_intel_crtc->lut_g[i] = green[i] >> 8; | ||
188 | psb_intel_crtc->lut_b[i] = blue[i] >> 8; | ||
189 | } | ||
190 | |||
191 | gma_crtc_load_lut(crtc); | ||
192 | } | ||
193 | |||
194 | /** | ||
195 | * Sets the power management mode of the pipe and plane. | ||
196 | * | ||
197 | * This code should probably grow support for turning the cursor off and back | ||
198 | * on appropriately at the same time as we're turning the pipe off/on. | ||
199 | */ | ||
200 | void gma_crtc_dpms(struct drm_crtc *crtc, int mode) | ||
201 | { | ||
202 | struct drm_device *dev = crtc->dev; | ||
203 | struct drm_psb_private *dev_priv = dev->dev_private; | ||
204 | struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); | ||
205 | int pipe = psb_intel_crtc->pipe; | ||
206 | const struct psb_offset *map = &dev_priv->regmap[pipe]; | ||
207 | u32 temp; | ||
208 | |||
209 | /* XXX: When our outputs are all unaware of DPMS modes other than off | ||
210 | * and on, we should map those modes to DRM_MODE_DPMS_OFF in the CRTC. | ||
211 | */ | ||
212 | |||
213 | /* FIXME: Uncomment this when we move cdv to generic dpms | ||
214 | if (IS_CDV(dev)) | ||
215 | cdv_intel_disable_self_refresh(dev); | ||
216 | */ | ||
217 | |||
218 | switch (mode) { | ||
219 | case DRM_MODE_DPMS_ON: | ||
220 | case DRM_MODE_DPMS_STANDBY: | ||
221 | case DRM_MODE_DPMS_SUSPEND: | ||
222 | if (psb_intel_crtc->active) | ||
223 | break; | ||
224 | |||
225 | psb_intel_crtc->active = true; | ||
226 | |||
227 | /* Enable the DPLL */ | ||
228 | temp = REG_READ(map->dpll); | ||
229 | if ((temp & DPLL_VCO_ENABLE) == 0) { | ||
230 | REG_WRITE(map->dpll, temp); | ||
231 | REG_READ(map->dpll); | ||
232 | /* Wait for the clocks to stabilize. */ | ||
233 | udelay(150); | ||
234 | REG_WRITE(map->dpll, temp | DPLL_VCO_ENABLE); | ||
235 | REG_READ(map->dpll); | ||
236 | /* Wait for the clocks to stabilize. */ | ||
237 | udelay(150); | ||
238 | REG_WRITE(map->dpll, temp | DPLL_VCO_ENABLE); | ||
239 | REG_READ(map->dpll); | ||
240 | /* Wait for the clocks to stabilize. */ | ||
241 | udelay(150); | ||
242 | } | ||
243 | |||
244 | /* Enable the plane */ | ||
245 | temp = REG_READ(map->cntr); | ||
246 | if ((temp & DISPLAY_PLANE_ENABLE) == 0) { | ||
247 | REG_WRITE(map->cntr, | ||
248 | temp | DISPLAY_PLANE_ENABLE); | ||
249 | /* Flush the plane changes */ | ||
250 | REG_WRITE(map->base, REG_READ(map->base)); | ||
251 | } | ||
252 | |||
253 | udelay(150); | ||
254 | |||
255 | /* Enable the pipe */ | ||
256 | temp = REG_READ(map->conf); | ||
257 | if ((temp & PIPEACONF_ENABLE) == 0) | ||
258 | REG_WRITE(map->conf, temp | PIPEACONF_ENABLE); | ||
259 | |||
260 | temp = REG_READ(map->status); | ||
261 | temp &= ~(0xFFFF); | ||
262 | temp |= PIPE_FIFO_UNDERRUN; | ||
263 | REG_WRITE(map->status, temp); | ||
264 | REG_READ(map->status); | ||
265 | |||
266 | gma_crtc_load_lut(crtc); | ||
267 | |||
268 | /* Give the overlay scaler a chance to enable | ||
269 | * if it's on this pipe */ | ||
270 | /* psb_intel_crtc_dpms_video(crtc, true); TODO */ | ||
271 | break; | ||
272 | case DRM_MODE_DPMS_OFF: | ||
273 | if (!psb_intel_crtc->active) | ||
274 | break; | ||
275 | |||
276 | psb_intel_crtc->active = false; | ||
277 | |||
278 | /* Give the overlay scaler a chance to disable | ||
279 | * if it's on this pipe */ | ||
280 | /* psb_intel_crtc_dpms_video(crtc, FALSE); TODO */ | ||
281 | |||
282 | /* Disable the VGA plane that we never use */ | ||
283 | REG_WRITE(VGACNTRL, VGA_DISP_DISABLE); | ||
284 | |||
285 | /* Turn off vblank interrupts */ | ||
286 | drm_vblank_off(dev, pipe); | ||
287 | |||
288 | /* Wait for vblank for the disable to take effect */ | ||
289 | gma_wait_for_vblank(dev); | ||
290 | |||
291 | /* Disable plane */ | ||
292 | temp = REG_READ(map->cntr); | ||
293 | if ((temp & DISPLAY_PLANE_ENABLE) != 0) { | ||
294 | REG_WRITE(map->cntr, | ||
295 | temp & ~DISPLAY_PLANE_ENABLE); | ||
296 | /* Flush the plane changes */ | ||
297 | REG_WRITE(map->base, REG_READ(map->base)); | ||
298 | REG_READ(map->base); | ||
299 | } | ||
300 | |||
301 | /* Disable pipe */ | ||
302 | temp = REG_READ(map->conf); | ||
303 | if ((temp & PIPEACONF_ENABLE) != 0) { | ||
304 | REG_WRITE(map->conf, temp & ~PIPEACONF_ENABLE); | ||
305 | REG_READ(map->conf); | ||
306 | } | ||
307 | |||
308 | /* Wait for vblank for the disable to take effect. */ | ||
309 | gma_wait_for_vblank(dev); | ||
310 | |||
311 | udelay(150); | ||
312 | |||
313 | /* Disable DPLL */ | ||
314 | temp = REG_READ(map->dpll); | ||
315 | if ((temp & DPLL_VCO_ENABLE) != 0) { | ||
316 | REG_WRITE(map->dpll, temp & ~DPLL_VCO_ENABLE); | ||
317 | REG_READ(map->dpll); | ||
318 | } | ||
319 | |||
320 | /* Wait for the clocks to turn off. */ | ||
321 | udelay(150); | ||
322 | break; | ||
323 | } | ||
324 | |||
325 | /* FIXME: Uncomment this when we move cdv to generic dpms | ||
326 | if (IS_CDV(dev)) | ||
327 | cdv_intel_update_watermark(dev, crtc); | ||
328 | */ | ||
329 | |||
330 | /* Set FIFO watermarks */ | ||
331 | REG_WRITE(DSPARB, 0x3F3E); | ||
332 | } | ||
333 | |||
334 | bool gma_crtc_mode_fixup(struct drm_crtc *crtc, | ||
335 | const struct drm_display_mode *mode, | ||
336 | struct drm_display_mode *adjusted_mode) | ||
337 | { | ||
338 | return true; | ||
339 | } | ||
340 | |||
341 | void gma_crtc_prepare(struct drm_crtc *crtc) | ||
342 | { | ||
343 | struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; | ||
344 | crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF); | ||
345 | } | ||
346 | |||
347 | void gma_crtc_commit(struct drm_crtc *crtc) | ||
348 | { | ||
349 | struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; | ||
350 | crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON); | ||
351 | } | ||
352 | |||
353 | void gma_crtc_disable(struct drm_crtc *crtc) | ||
354 | { | ||
355 | struct gtt_range *gt; | ||
356 | struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; | ||
357 | |||
358 | crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF); | ||
359 | |||
360 | if (crtc->fb) { | ||
361 | gt = to_psb_fb(crtc->fb)->gtt; | ||
362 | psb_gtt_unpin(gt); | ||
363 | } | ||
364 | } | ||
365 | |||
366 | void gma_crtc_destroy(struct drm_crtc *crtc) | ||
367 | { | ||
368 | struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); | ||
369 | |||
370 | kfree(psb_intel_crtc->crtc_state); | ||
371 | drm_crtc_cleanup(crtc); | ||
372 | kfree(psb_intel_crtc); | ||
373 | } | ||
374 | |||
49 | #define GMA_PLL_INVALID(s) { /* DRM_ERROR(s); */ return false; } | 375 | #define GMA_PLL_INVALID(s) { /* DRM_ERROR(s); */ return false; } |
50 | 376 | ||
51 | bool gma_pll_is_valid(struct drm_crtc *crtc, | 377 | bool gma_pll_is_valid(struct drm_crtc *crtc, |
diff --git a/drivers/gpu/drm/gma500/gma_display.h b/drivers/gpu/drm/gma500/gma_display.h index a5d8aa31b5b7..24a582e009e6 100644 --- a/drivers/gpu/drm/gma500/gma_display.h +++ b/drivers/gpu/drm/gma500/gma_display.h | |||
@@ -61,6 +61,20 @@ struct gma_clock_funcs { | |||
61 | 61 | ||
62 | /* Common pipe related functions */ | 62 | /* Common pipe related functions */ |
63 | extern bool gma_pipe_has_type(struct drm_crtc *crtc, int type); | 63 | extern bool gma_pipe_has_type(struct drm_crtc *crtc, int type); |
64 | extern void gma_wait_for_vblank(struct drm_device *dev); | ||
65 | extern int gma_pipe_set_base(struct drm_crtc *crtc, int x, int y, | ||
66 | struct drm_framebuffer *old_fb); | ||
67 | extern void gma_crtc_load_lut(struct drm_crtc *crtc); | ||
68 | extern void gma_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, | ||
69 | u16 *blue, u32 start, u32 size); | ||
70 | extern void gma_crtc_dpms(struct drm_crtc *crtc, int mode); | ||
71 | extern bool gma_crtc_mode_fixup(struct drm_crtc *crtc, | ||
72 | const struct drm_display_mode *mode, | ||
73 | struct drm_display_mode *adjusted_mode); | ||
74 | extern void gma_crtc_prepare(struct drm_crtc *crtc); | ||
75 | extern void gma_crtc_commit(struct drm_crtc *crtc); | ||
76 | extern void gma_crtc_disable(struct drm_crtc *crtc); | ||
77 | extern void gma_crtc_destroy(struct drm_crtc *crtc); | ||
64 | 78 | ||
65 | /* Common clock related functions */ | 79 | /* Common clock related functions */ |
66 | extern const struct gma_limit_t *gma_limit(struct drm_crtc *crtc, int refclk); | 80 | extern const struct gma_limit_t *gma_limit(struct drm_crtc *crtc, int refclk); |