diff options
author | Dave Airlie <airlied@redhat.com> | 2013-02-23 21:38:22 -0500 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2013-02-23 21:38:22 -0500 |
commit | a3b1097c037ae992510fe8f1e933072280ef19b0 (patch) | |
tree | 1411de86b91f4407f3368b47b2c68ea6f6345b01 | |
parent | c976cb37a95a8dcaf23d04d6487fbacc33d3c913 (diff) | |
parent | e450fcc6669705ef49784080ac6dd8513df37763 (diff) |
Merge branch 'drm/tegra-for-3.9' of git://anongit.freedesktop.org/tegra/linux into drm-next
Thierry writes:
"Add support for 2 hardware overlays found on Tegra. These support YUV
pixel formats and can be used as video overlays. .mode_set_base() is
implemented and support for VBLANK and page-flipping is added.
A few minor bug fixes are also included and a new debugfs file allows
to inspect the framebuffers attached to the Tegra DRM device."
* 'drm/tegra-for-3.9' of git://anongit.freedesktop.org/tegra/linux:
drm/tegra: Add list of framebuffers to debugfs
drm/tegra: Fix color expansion
drm/tegra: Split DC_CMD_STATE_CONTROL register write
drm/tegra: Implement page-flipping support
drm/tegra: Implement VBLANK support
drm/tegra: Implement .mode_set_base()
drm/tegra: Add plane support
drm/tegra: Remove bogus tegra_framebuffer structure
drm: Add consistency check for page-flipping
-rw-r--r-- | Documentation/DocBook/drm.tmpl | 6 | ||||
-rw-r--r-- | drivers/gpu/drm/drm_crtc.c | 7 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/dc.c | 585 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/dc.h | 14 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/drm.c | 103 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/drm.h | 43 |
6 files changed, 635 insertions, 123 deletions
diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl index 51e1904ac4c7..a6428ddfcfc2 100644 --- a/Documentation/DocBook/drm.tmpl +++ b/Documentation/DocBook/drm.tmpl | |||
@@ -1161,6 +1161,12 @@ int max_width, max_height;</synopsis> | |||
1161 | any new rendering to the frame buffer until the page flip completes. | 1161 | any new rendering to the frame buffer until the page flip completes. |
1162 | </para> | 1162 | </para> |
1163 | <para> | 1163 | <para> |
1164 | If a page flip can be successfully scheduled the driver must set the | ||
1165 | <code>drm_crtc-<fb</code> field to the new framebuffer pointed to | ||
1166 | by <code>fb</code>. This is important so that the reference counting | ||
1167 | on framebuffers stays balanced. | ||
1168 | </para> | ||
1169 | <para> | ||
1164 | If a page flip is already pending, the | 1170 | If a page flip is already pending, the |
1165 | <methodname>page_flip</methodname> operation must return | 1171 | <methodname>page_flip</methodname> operation must return |
1166 | -<errorname>EBUSY</errorname>. | 1172 | -<errorname>EBUSY</errorname>. |
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 781aef524b3b..3bdf2a650d9c 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c | |||
@@ -3792,6 +3792,13 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, | |||
3792 | /* Keep the old fb, don't unref it. */ | 3792 | /* Keep the old fb, don't unref it. */ |
3793 | old_fb = NULL; | 3793 | old_fb = NULL; |
3794 | } else { | 3794 | } else { |
3795 | /* | ||
3796 | * Warn if the driver hasn't properly updated the crtc->fb | ||
3797 | * field to reflect that the new framebuffer is now used. | ||
3798 | * Failing to do so will screw with the reference counting | ||
3799 | * on framebuffers. | ||
3800 | */ | ||
3801 | WARN_ON(crtc->fb != fb); | ||
3795 | /* Unref only the old framebuffer. */ | 3802 | /* Unref only the old framebuffer. */ |
3796 | fb = NULL; | 3803 | fb = NULL; |
3797 | } | 3804 | } |
diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 656b2e3334a6..bf8095c55bd1 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c | |||
@@ -18,26 +18,257 @@ | |||
18 | #include "drm.h" | 18 | #include "drm.h" |
19 | #include "dc.h" | 19 | #include "dc.h" |
20 | 20 | ||
21 | struct tegra_dc_window { | 21 | struct tegra_plane { |
22 | fixed20_12 x; | 22 | struct drm_plane base; |
23 | fixed20_12 y; | 23 | unsigned int index; |
24 | fixed20_12 w; | ||
25 | fixed20_12 h; | ||
26 | unsigned int outx; | ||
27 | unsigned int outy; | ||
28 | unsigned int outw; | ||
29 | unsigned int outh; | ||
30 | unsigned int stride; | ||
31 | unsigned int fmt; | ||
32 | }; | 24 | }; |
33 | 25 | ||
26 | static inline struct tegra_plane *to_tegra_plane(struct drm_plane *plane) | ||
27 | { | ||
28 | return container_of(plane, struct tegra_plane, base); | ||
29 | } | ||
30 | |||
31 | static int tegra_plane_update(struct drm_plane *plane, struct drm_crtc *crtc, | ||
32 | struct drm_framebuffer *fb, int crtc_x, | ||
33 | int crtc_y, unsigned int crtc_w, | ||
34 | unsigned int crtc_h, uint32_t src_x, | ||
35 | uint32_t src_y, uint32_t src_w, uint32_t src_h) | ||
36 | { | ||
37 | struct tegra_plane *p = to_tegra_plane(plane); | ||
38 | struct tegra_dc *dc = to_tegra_dc(crtc); | ||
39 | struct tegra_dc_window window; | ||
40 | unsigned int i; | ||
41 | |||
42 | memset(&window, 0, sizeof(window)); | ||
43 | window.src.x = src_x >> 16; | ||
44 | window.src.y = src_y >> 16; | ||
45 | window.src.w = src_w >> 16; | ||
46 | window.src.h = src_h >> 16; | ||
47 | window.dst.x = crtc_x; | ||
48 | window.dst.y = crtc_y; | ||
49 | window.dst.w = crtc_w; | ||
50 | window.dst.h = crtc_h; | ||
51 | window.format = tegra_dc_format(fb->pixel_format); | ||
52 | window.bits_per_pixel = fb->bits_per_pixel; | ||
53 | |||
54 | for (i = 0; i < drm_format_num_planes(fb->pixel_format); i++) { | ||
55 | struct drm_gem_cma_object *gem = drm_fb_cma_get_gem_obj(fb, i); | ||
56 | |||
57 | window.base[i] = gem->paddr + fb->offsets[i]; | ||
58 | |||
59 | /* | ||
60 | * Tegra doesn't support different strides for U and V planes | ||
61 | * so we display a warning if the user tries to display a | ||
62 | * framebuffer with such a configuration. | ||
63 | */ | ||
64 | if (i >= 2) { | ||
65 | if (fb->pitches[i] != window.stride[1]) | ||
66 | DRM_ERROR("unsupported UV-plane configuration\n"); | ||
67 | } else { | ||
68 | window.stride[i] = fb->pitches[i]; | ||
69 | } | ||
70 | } | ||
71 | |||
72 | return tegra_dc_setup_window(dc, p->index, &window); | ||
73 | } | ||
74 | |||
75 | static int tegra_plane_disable(struct drm_plane *plane) | ||
76 | { | ||
77 | struct tegra_dc *dc = to_tegra_dc(plane->crtc); | ||
78 | struct tegra_plane *p = to_tegra_plane(plane); | ||
79 | unsigned long value; | ||
80 | |||
81 | value = WINDOW_A_SELECT << p->index; | ||
82 | tegra_dc_writel(dc, value, DC_CMD_DISPLAY_WINDOW_HEADER); | ||
83 | |||
84 | value = tegra_dc_readl(dc, DC_WIN_WIN_OPTIONS); | ||
85 | value &= ~WIN_ENABLE; | ||
86 | tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS); | ||
87 | |||
88 | tegra_dc_writel(dc, WIN_A_UPDATE << p->index, DC_CMD_STATE_CONTROL); | ||
89 | tegra_dc_writel(dc, WIN_A_ACT_REQ << p->index, DC_CMD_STATE_CONTROL); | ||
90 | |||
91 | return 0; | ||
92 | } | ||
93 | |||
94 | static void tegra_plane_destroy(struct drm_plane *plane) | ||
95 | { | ||
96 | tegra_plane_disable(plane); | ||
97 | drm_plane_cleanup(plane); | ||
98 | } | ||
99 | |||
100 | static const struct drm_plane_funcs tegra_plane_funcs = { | ||
101 | .update_plane = tegra_plane_update, | ||
102 | .disable_plane = tegra_plane_disable, | ||
103 | .destroy = tegra_plane_destroy, | ||
104 | }; | ||
105 | |||
106 | static const uint32_t plane_formats[] = { | ||
107 | DRM_FORMAT_XRGB8888, | ||
108 | DRM_FORMAT_UYVY, | ||
109 | DRM_FORMAT_YUV420, | ||
110 | DRM_FORMAT_YUV422, | ||
111 | }; | ||
112 | |||
113 | static int tegra_dc_add_planes(struct drm_device *drm, struct tegra_dc *dc) | ||
114 | { | ||
115 | unsigned int i; | ||
116 | int err = 0; | ||
117 | |||
118 | for (i = 0; i < 2; i++) { | ||
119 | struct tegra_plane *plane; | ||
120 | |||
121 | plane = devm_kzalloc(drm->dev, sizeof(*plane), GFP_KERNEL); | ||
122 | if (!plane) | ||
123 | return -ENOMEM; | ||
124 | |||
125 | plane->index = 1 + i; | ||
126 | |||
127 | err = drm_plane_init(drm, &plane->base, 1 << dc->pipe, | ||
128 | &tegra_plane_funcs, plane_formats, | ||
129 | ARRAY_SIZE(plane_formats), false); | ||
130 | if (err < 0) | ||
131 | return err; | ||
132 | } | ||
133 | |||
134 | return 0; | ||
135 | } | ||
136 | |||
137 | static int tegra_dc_set_base(struct tegra_dc *dc, int x, int y, | ||
138 | struct drm_framebuffer *fb) | ||
139 | { | ||
140 | struct drm_gem_cma_object *gem = drm_fb_cma_get_gem_obj(fb, 0); | ||
141 | unsigned long value; | ||
142 | |||
143 | tegra_dc_writel(dc, WINDOW_A_SELECT, DC_CMD_DISPLAY_WINDOW_HEADER); | ||
144 | |||
145 | value = fb->offsets[0] + y * fb->pitches[0] + | ||
146 | x * fb->bits_per_pixel / 8; | ||
147 | |||
148 | tegra_dc_writel(dc, gem->paddr + value, DC_WINBUF_START_ADDR); | ||
149 | tegra_dc_writel(dc, fb->pitches[0], DC_WIN_LINE_STRIDE); | ||
150 | |||
151 | value = GENERAL_UPDATE | WIN_A_UPDATE; | ||
152 | tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL); | ||
153 | |||
154 | value = GENERAL_ACT_REQ | WIN_A_ACT_REQ; | ||
155 | tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL); | ||
156 | |||
157 | return 0; | ||
158 | } | ||
159 | |||
160 | void tegra_dc_enable_vblank(struct tegra_dc *dc) | ||
161 | { | ||
162 | unsigned long value, flags; | ||
163 | |||
164 | spin_lock_irqsave(&dc->lock, flags); | ||
165 | |||
166 | value = tegra_dc_readl(dc, DC_CMD_INT_MASK); | ||
167 | value |= VBLANK_INT; | ||
168 | tegra_dc_writel(dc, value, DC_CMD_INT_MASK); | ||
169 | |||
170 | spin_unlock_irqrestore(&dc->lock, flags); | ||
171 | } | ||
172 | |||
173 | void tegra_dc_disable_vblank(struct tegra_dc *dc) | ||
174 | { | ||
175 | unsigned long value, flags; | ||
176 | |||
177 | spin_lock_irqsave(&dc->lock, flags); | ||
178 | |||
179 | value = tegra_dc_readl(dc, DC_CMD_INT_MASK); | ||
180 | value &= ~VBLANK_INT; | ||
181 | tegra_dc_writel(dc, value, DC_CMD_INT_MASK); | ||
182 | |||
183 | spin_unlock_irqrestore(&dc->lock, flags); | ||
184 | } | ||
185 | |||
186 | static void tegra_dc_finish_page_flip(struct tegra_dc *dc) | ||
187 | { | ||
188 | struct drm_device *drm = dc->base.dev; | ||
189 | struct drm_crtc *crtc = &dc->base; | ||
190 | struct drm_gem_cma_object *gem; | ||
191 | unsigned long flags, base; | ||
192 | |||
193 | if (!dc->event) | ||
194 | return; | ||
195 | |||
196 | gem = drm_fb_cma_get_gem_obj(crtc->fb, 0); | ||
197 | |||
198 | /* check if new start address has been latched */ | ||
199 | tegra_dc_writel(dc, READ_MUX, DC_CMD_STATE_ACCESS); | ||
200 | base = tegra_dc_readl(dc, DC_WINBUF_START_ADDR); | ||
201 | tegra_dc_writel(dc, 0, DC_CMD_STATE_ACCESS); | ||
202 | |||
203 | if (base == gem->paddr + crtc->fb->offsets[0]) { | ||
204 | spin_lock_irqsave(&drm->event_lock, flags); | ||
205 | drm_send_vblank_event(drm, dc->pipe, dc->event); | ||
206 | drm_vblank_put(drm, dc->pipe); | ||
207 | dc->event = NULL; | ||
208 | spin_unlock_irqrestore(&drm->event_lock, flags); | ||
209 | } | ||
210 | } | ||
211 | |||
212 | void tegra_dc_cancel_page_flip(struct drm_crtc *crtc, struct drm_file *file) | ||
213 | { | ||
214 | struct tegra_dc *dc = to_tegra_dc(crtc); | ||
215 | struct drm_device *drm = crtc->dev; | ||
216 | unsigned long flags; | ||
217 | |||
218 | spin_lock_irqsave(&drm->event_lock, flags); | ||
219 | |||
220 | if (dc->event && dc->event->base.file_priv == file) { | ||
221 | dc->event->base.destroy(&dc->event->base); | ||
222 | drm_vblank_put(drm, dc->pipe); | ||
223 | dc->event = NULL; | ||
224 | } | ||
225 | |||
226 | spin_unlock_irqrestore(&drm->event_lock, flags); | ||
227 | } | ||
228 | |||
229 | static int tegra_dc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, | ||
230 | struct drm_pending_vblank_event *event) | ||
231 | { | ||
232 | struct tegra_dc *dc = to_tegra_dc(crtc); | ||
233 | struct drm_device *drm = crtc->dev; | ||
234 | |||
235 | if (dc->event) | ||
236 | return -EBUSY; | ||
237 | |||
238 | if (event) { | ||
239 | event->pipe = dc->pipe; | ||
240 | dc->event = event; | ||
241 | drm_vblank_get(drm, dc->pipe); | ||
242 | } | ||
243 | |||
244 | tegra_dc_set_base(dc, 0, 0, fb); | ||
245 | crtc->fb = fb; | ||
246 | |||
247 | return 0; | ||
248 | } | ||
249 | |||
34 | static const struct drm_crtc_funcs tegra_crtc_funcs = { | 250 | static const struct drm_crtc_funcs tegra_crtc_funcs = { |
251 | .page_flip = tegra_dc_page_flip, | ||
35 | .set_config = drm_crtc_helper_set_config, | 252 | .set_config = drm_crtc_helper_set_config, |
36 | .destroy = drm_crtc_cleanup, | 253 | .destroy = drm_crtc_cleanup, |
37 | }; | 254 | }; |
38 | 255 | ||
39 | static void tegra_crtc_dpms(struct drm_crtc *crtc, int mode) | 256 | static void tegra_crtc_disable(struct drm_crtc *crtc) |
40 | { | 257 | { |
258 | struct drm_device *drm = crtc->dev; | ||
259 | struct drm_plane *plane; | ||
260 | |||
261 | list_for_each_entry(plane, &drm->mode_config.plane_list, head) { | ||
262 | if (plane->crtc == crtc) { | ||
263 | tegra_plane_disable(plane); | ||
264 | plane->crtc = NULL; | ||
265 | |||
266 | if (plane->fb) { | ||
267 | drm_framebuffer_unreference(plane->fb); | ||
268 | plane->fb = NULL; | ||
269 | } | ||
270 | } | ||
271 | } | ||
41 | } | 272 | } |
42 | 273 | ||
43 | static bool tegra_crtc_mode_fixup(struct drm_crtc *crtc, | 274 | static bool tegra_crtc_mode_fixup(struct drm_crtc *crtc, |
@@ -47,10 +278,11 @@ static bool tegra_crtc_mode_fixup(struct drm_crtc *crtc, | |||
47 | return true; | 278 | return true; |
48 | } | 279 | } |
49 | 280 | ||
50 | static inline u32 compute_dda_inc(fixed20_12 inf, unsigned int out, bool v, | 281 | static inline u32 compute_dda_inc(unsigned int in, unsigned int out, bool v, |
51 | unsigned int bpp) | 282 | unsigned int bpp) |
52 | { | 283 | { |
53 | fixed20_12 outf = dfixed_init(out); | 284 | fixed20_12 outf = dfixed_init(out); |
285 | fixed20_12 inf = dfixed_init(in); | ||
54 | u32 dda_inc; | 286 | u32 dda_inc; |
55 | int max; | 287 | int max; |
56 | 288 | ||
@@ -80,9 +312,10 @@ static inline u32 compute_dda_inc(fixed20_12 inf, unsigned int out, bool v, | |||
80 | return dda_inc; | 312 | return dda_inc; |
81 | } | 313 | } |
82 | 314 | ||
83 | static inline u32 compute_initial_dda(fixed20_12 in) | 315 | static inline u32 compute_initial_dda(unsigned int in) |
84 | { | 316 | { |
85 | return dfixed_frac(in); | 317 | fixed20_12 inf = dfixed_init(in); |
318 | return dfixed_frac(inf); | ||
86 | } | 319 | } |
87 | 320 | ||
88 | static int tegra_dc_set_timings(struct tegra_dc *dc, | 321 | static int tegra_dc_set_timings(struct tegra_dc *dc, |
@@ -153,18 +386,198 @@ static int tegra_crtc_setup_clk(struct drm_crtc *crtc, | |||
153 | return 0; | 386 | return 0; |
154 | } | 387 | } |
155 | 388 | ||
389 | static bool tegra_dc_format_is_yuv(unsigned int format, bool *planar) | ||
390 | { | ||
391 | switch (format) { | ||
392 | case WIN_COLOR_DEPTH_YCbCr422: | ||
393 | case WIN_COLOR_DEPTH_YUV422: | ||
394 | if (planar) | ||
395 | *planar = false; | ||
396 | |||
397 | return true; | ||
398 | |||
399 | case WIN_COLOR_DEPTH_YCbCr420P: | ||
400 | case WIN_COLOR_DEPTH_YUV420P: | ||
401 | case WIN_COLOR_DEPTH_YCbCr422P: | ||
402 | case WIN_COLOR_DEPTH_YUV422P: | ||
403 | case WIN_COLOR_DEPTH_YCbCr422R: | ||
404 | case WIN_COLOR_DEPTH_YUV422R: | ||
405 | case WIN_COLOR_DEPTH_YCbCr422RA: | ||
406 | case WIN_COLOR_DEPTH_YUV422RA: | ||
407 | if (planar) | ||
408 | *planar = true; | ||
409 | |||
410 | return true; | ||
411 | } | ||
412 | |||
413 | return false; | ||
414 | } | ||
415 | |||
416 | int tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index, | ||
417 | const struct tegra_dc_window *window) | ||
418 | { | ||
419 | unsigned h_offset, v_offset, h_size, v_size, h_dda, v_dda, bpp; | ||
420 | unsigned long value; | ||
421 | bool yuv, planar; | ||
422 | |||
423 | /* | ||
424 | * For YUV planar modes, the number of bytes per pixel takes into | ||
425 | * account only the luma component and therefore is 1. | ||
426 | */ | ||
427 | yuv = tegra_dc_format_is_yuv(window->format, &planar); | ||
428 | if (!yuv) | ||
429 | bpp = window->bits_per_pixel / 8; | ||
430 | else | ||
431 | bpp = planar ? 1 : 2; | ||
432 | |||
433 | value = WINDOW_A_SELECT << index; | ||
434 | tegra_dc_writel(dc, value, DC_CMD_DISPLAY_WINDOW_HEADER); | ||
435 | |||
436 | tegra_dc_writel(dc, window->format, DC_WIN_COLOR_DEPTH); | ||
437 | tegra_dc_writel(dc, 0, DC_WIN_BYTE_SWAP); | ||
438 | |||
439 | value = V_POSITION(window->dst.y) | H_POSITION(window->dst.x); | ||
440 | tegra_dc_writel(dc, value, DC_WIN_POSITION); | ||
441 | |||
442 | value = V_SIZE(window->dst.h) | H_SIZE(window->dst.w); | ||
443 | tegra_dc_writel(dc, value, DC_WIN_SIZE); | ||
444 | |||
445 | h_offset = window->src.x * bpp; | ||
446 | v_offset = window->src.y; | ||
447 | h_size = window->src.w * bpp; | ||
448 | v_size = window->src.h; | ||
449 | |||
450 | value = V_PRESCALED_SIZE(v_size) | H_PRESCALED_SIZE(h_size); | ||
451 | tegra_dc_writel(dc, value, DC_WIN_PRESCALED_SIZE); | ||
452 | |||
453 | /* | ||
454 | * For DDA computations the number of bytes per pixel for YUV planar | ||
455 | * modes needs to take into account all Y, U and V components. | ||
456 | */ | ||
457 | if (yuv && planar) | ||
458 | bpp = 2; | ||
459 | |||
460 | h_dda = compute_dda_inc(window->src.w, window->dst.w, false, bpp); | ||
461 | v_dda = compute_dda_inc(window->src.h, window->dst.h, true, bpp); | ||
462 | |||
463 | value = V_DDA_INC(v_dda) | H_DDA_INC(h_dda); | ||
464 | tegra_dc_writel(dc, value, DC_WIN_DDA_INC); | ||
465 | |||
466 | h_dda = compute_initial_dda(window->src.x); | ||
467 | v_dda = compute_initial_dda(window->src.y); | ||
468 | |||
469 | tegra_dc_writel(dc, h_dda, DC_WIN_H_INITIAL_DDA); | ||
470 | tegra_dc_writel(dc, v_dda, DC_WIN_V_INITIAL_DDA); | ||
471 | |||
472 | tegra_dc_writel(dc, 0, DC_WIN_UV_BUF_STRIDE); | ||
473 | tegra_dc_writel(dc, 0, DC_WIN_BUF_STRIDE); | ||
474 | |||
475 | tegra_dc_writel(dc, window->base[0], DC_WINBUF_START_ADDR); | ||
476 | |||
477 | if (yuv && planar) { | ||
478 | tegra_dc_writel(dc, window->base[1], DC_WINBUF_START_ADDR_U); | ||
479 | tegra_dc_writel(dc, window->base[2], DC_WINBUF_START_ADDR_V); | ||
480 | value = window->stride[1] << 16 | window->stride[0]; | ||
481 | tegra_dc_writel(dc, value, DC_WIN_LINE_STRIDE); | ||
482 | } else { | ||
483 | tegra_dc_writel(dc, window->stride[0], DC_WIN_LINE_STRIDE); | ||
484 | } | ||
485 | |||
486 | tegra_dc_writel(dc, h_offset, DC_WINBUF_ADDR_H_OFFSET); | ||
487 | tegra_dc_writel(dc, v_offset, DC_WINBUF_ADDR_V_OFFSET); | ||
488 | |||
489 | value = WIN_ENABLE; | ||
490 | |||
491 | if (yuv) { | ||
492 | /* setup default colorspace conversion coefficients */ | ||
493 | tegra_dc_writel(dc, 0x00f0, DC_WIN_CSC_YOF); | ||
494 | tegra_dc_writel(dc, 0x012a, DC_WIN_CSC_KYRGB); | ||
495 | tegra_dc_writel(dc, 0x0000, DC_WIN_CSC_KUR); | ||
496 | tegra_dc_writel(dc, 0x0198, DC_WIN_CSC_KVR); | ||
497 | tegra_dc_writel(dc, 0x039b, DC_WIN_CSC_KUG); | ||
498 | tegra_dc_writel(dc, 0x032f, DC_WIN_CSC_KVG); | ||
499 | tegra_dc_writel(dc, 0x0204, DC_WIN_CSC_KUB); | ||
500 | tegra_dc_writel(dc, 0x0000, DC_WIN_CSC_KVB); | ||
501 | |||
502 | value |= CSC_ENABLE; | ||
503 | } else if (window->bits_per_pixel < 24) { | ||
504 | value |= COLOR_EXPAND; | ||
505 | } | ||
506 | |||
507 | tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS); | ||
508 | |||
509 | /* | ||
510 | * Disable blending and assume Window A is the bottom-most window, | ||
511 | * Window C is the top-most window and Window B is in the middle. | ||
512 | */ | ||
513 | tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_NOKEY); | ||
514 | tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_1WIN); | ||
515 | |||
516 | switch (index) { | ||
517 | case 0: | ||
518 | tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_2WIN_X); | ||
519 | tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_2WIN_Y); | ||
520 | tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_3WIN_XY); | ||
521 | break; | ||
522 | |||
523 | case 1: | ||
524 | tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_2WIN_X); | ||
525 | tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_2WIN_Y); | ||
526 | tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_3WIN_XY); | ||
527 | break; | ||
528 | |||
529 | case 2: | ||
530 | tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_2WIN_X); | ||
531 | tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_2WIN_Y); | ||
532 | tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_3WIN_XY); | ||
533 | break; | ||
534 | } | ||
535 | |||
536 | tegra_dc_writel(dc, WIN_A_UPDATE << index, DC_CMD_STATE_CONTROL); | ||
537 | tegra_dc_writel(dc, WIN_A_ACT_REQ << index, DC_CMD_STATE_CONTROL); | ||
538 | |||
539 | return 0; | ||
540 | } | ||
541 | |||
542 | unsigned int tegra_dc_format(uint32_t format) | ||
543 | { | ||
544 | switch (format) { | ||
545 | case DRM_FORMAT_XRGB8888: | ||
546 | return WIN_COLOR_DEPTH_B8G8R8A8; | ||
547 | |||
548 | case DRM_FORMAT_RGB565: | ||
549 | return WIN_COLOR_DEPTH_B5G6R5; | ||
550 | |||
551 | case DRM_FORMAT_UYVY: | ||
552 | return WIN_COLOR_DEPTH_YCbCr422; | ||
553 | |||
554 | case DRM_FORMAT_YUV420: | ||
555 | return WIN_COLOR_DEPTH_YCbCr420P; | ||
556 | |||
557 | case DRM_FORMAT_YUV422: | ||
558 | return WIN_COLOR_DEPTH_YCbCr422P; | ||
559 | |||
560 | default: | ||
561 | break; | ||
562 | } | ||
563 | |||
564 | WARN(1, "unsupported pixel format %u, using default\n", format); | ||
565 | return WIN_COLOR_DEPTH_B8G8R8A8; | ||
566 | } | ||
567 | |||
156 | static int tegra_crtc_mode_set(struct drm_crtc *crtc, | 568 | static int tegra_crtc_mode_set(struct drm_crtc *crtc, |
157 | struct drm_display_mode *mode, | 569 | struct drm_display_mode *mode, |
158 | struct drm_display_mode *adjusted, | 570 | struct drm_display_mode *adjusted, |
159 | int x, int y, struct drm_framebuffer *old_fb) | 571 | int x, int y, struct drm_framebuffer *old_fb) |
160 | { | 572 | { |
161 | struct tegra_framebuffer *fb = to_tegra_fb(crtc->fb); | 573 | struct drm_gem_cma_object *gem = drm_fb_cma_get_gem_obj(crtc->fb, 0); |
162 | struct tegra_dc *dc = to_tegra_dc(crtc); | 574 | struct tegra_dc *dc = to_tegra_dc(crtc); |
163 | unsigned int h_dda, v_dda, bpp; | 575 | struct tegra_dc_window window; |
164 | struct tegra_dc_window win; | ||
165 | unsigned long div, value; | 576 | unsigned long div, value; |
166 | int err; | 577 | int err; |
167 | 578 | ||
579 | drm_vblank_pre_modeset(crtc->dev, dc->pipe); | ||
580 | |||
168 | err = tegra_crtc_setup_clk(crtc, mode, &div); | 581 | err = tegra_crtc_setup_clk(crtc, mode, &div); |
169 | if (err) { | 582 | if (err) { |
170 | dev_err(dc->dev, "failed to setup clock for CRTC: %d\n", err); | 583 | dev_err(dc->dev, "failed to setup clock for CRTC: %d\n", err); |
@@ -192,83 +605,33 @@ static int tegra_crtc_mode_set(struct drm_crtc *crtc, | |||
192 | tegra_dc_writel(dc, value, DC_DISP_DISP_CLOCK_CONTROL); | 605 | tegra_dc_writel(dc, value, DC_DISP_DISP_CLOCK_CONTROL); |
193 | 606 | ||
194 | /* setup window parameters */ | 607 | /* setup window parameters */ |
195 | memset(&win, 0, sizeof(win)); | 608 | memset(&window, 0, sizeof(window)); |
196 | win.x.full = dfixed_const(0); | 609 | window.src.x = 0; |
197 | win.y.full = dfixed_const(0); | 610 | window.src.y = 0; |
198 | win.w.full = dfixed_const(mode->hdisplay); | 611 | window.src.w = mode->hdisplay; |
199 | win.h.full = dfixed_const(mode->vdisplay); | 612 | window.src.h = mode->vdisplay; |
200 | win.outx = 0; | 613 | window.dst.x = 0; |
201 | win.outy = 0; | 614 | window.dst.y = 0; |
202 | win.outw = mode->hdisplay; | 615 | window.dst.w = mode->hdisplay; |
203 | win.outh = mode->vdisplay; | 616 | window.dst.h = mode->vdisplay; |
204 | 617 | window.format = tegra_dc_format(crtc->fb->pixel_format); | |
205 | switch (crtc->fb->pixel_format) { | 618 | window.bits_per_pixel = crtc->fb->bits_per_pixel; |
206 | case DRM_FORMAT_XRGB8888: | 619 | window.stride[0] = crtc->fb->pitches[0]; |
207 | win.fmt = WIN_COLOR_DEPTH_B8G8R8A8; | 620 | window.base[0] = gem->paddr; |
208 | break; | 621 | |
209 | 622 | err = tegra_dc_setup_window(dc, 0, &window); | |
210 | case DRM_FORMAT_RGB565: | 623 | if (err < 0) |
211 | win.fmt = WIN_COLOR_DEPTH_B5G6R5; | 624 | dev_err(dc->dev, "failed to enable root plane\n"); |
212 | break; | ||
213 | |||
214 | default: | ||
215 | win.fmt = WIN_COLOR_DEPTH_B8G8R8A8; | ||
216 | WARN_ON(1); | ||
217 | break; | ||
218 | } | ||
219 | |||
220 | bpp = crtc->fb->bits_per_pixel / 8; | ||
221 | win.stride = crtc->fb->pitches[0]; | ||
222 | |||
223 | /* program window registers */ | ||
224 | value = WINDOW_A_SELECT; | ||
225 | tegra_dc_writel(dc, value, DC_CMD_DISPLAY_WINDOW_HEADER); | ||
226 | |||
227 | tegra_dc_writel(dc, win.fmt, DC_WIN_COLOR_DEPTH); | ||
228 | tegra_dc_writel(dc, 0, DC_WIN_BYTE_SWAP); | ||
229 | |||
230 | value = V_POSITION(win.outy) | H_POSITION(win.outx); | ||
231 | tegra_dc_writel(dc, value, DC_WIN_POSITION); | ||
232 | |||
233 | value = V_SIZE(win.outh) | H_SIZE(win.outw); | ||
234 | tegra_dc_writel(dc, value, DC_WIN_SIZE); | ||
235 | |||
236 | value = V_PRESCALED_SIZE(dfixed_trunc(win.h)) | | ||
237 | H_PRESCALED_SIZE(dfixed_trunc(win.w) * bpp); | ||
238 | tegra_dc_writel(dc, value, DC_WIN_PRESCALED_SIZE); | ||
239 | |||
240 | h_dda = compute_dda_inc(win.w, win.outw, false, bpp); | ||
241 | v_dda = compute_dda_inc(win.h, win.outh, true, bpp); | ||
242 | |||
243 | value = V_DDA_INC(v_dda) | H_DDA_INC(h_dda); | ||
244 | tegra_dc_writel(dc, value, DC_WIN_DDA_INC); | ||
245 | |||
246 | h_dda = compute_initial_dda(win.x); | ||
247 | v_dda = compute_initial_dda(win.y); | ||
248 | |||
249 | tegra_dc_writel(dc, h_dda, DC_WIN_H_INITIAL_DDA); | ||
250 | tegra_dc_writel(dc, v_dda, DC_WIN_V_INITIAL_DDA); | ||
251 | |||
252 | tegra_dc_writel(dc, 0, DC_WIN_UV_BUF_STRIDE); | ||
253 | tegra_dc_writel(dc, 0, DC_WIN_BUF_STRIDE); | ||
254 | |||
255 | tegra_dc_writel(dc, fb->obj->paddr, DC_WINBUF_START_ADDR); | ||
256 | tegra_dc_writel(dc, win.stride, DC_WIN_LINE_STRIDE); | ||
257 | tegra_dc_writel(dc, dfixed_trunc(win.x) * bpp, | ||
258 | DC_WINBUF_ADDR_H_OFFSET); | ||
259 | tegra_dc_writel(dc, dfixed_trunc(win.y), DC_WINBUF_ADDR_V_OFFSET); | ||
260 | |||
261 | value = WIN_ENABLE; | ||
262 | |||
263 | if (bpp < 24) | ||
264 | value |= COLOR_EXPAND; | ||
265 | 625 | ||
266 | tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS); | 626 | return 0; |
627 | } | ||
267 | 628 | ||
268 | tegra_dc_writel(dc, 0xff00, DC_WIN_BLEND_NOKEY); | 629 | static int tegra_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, |
269 | tegra_dc_writel(dc, 0xff00, DC_WIN_BLEND_1WIN); | 630 | struct drm_framebuffer *old_fb) |
631 | { | ||
632 | struct tegra_dc *dc = to_tegra_dc(crtc); | ||
270 | 633 | ||
271 | return 0; | 634 | return tegra_dc_set_base(dc, x, y, crtc->fb); |
272 | } | 635 | } |
273 | 636 | ||
274 | static void tegra_crtc_prepare(struct drm_crtc *crtc) | 637 | static void tegra_crtc_prepare(struct drm_crtc *crtc) |
@@ -315,31 +678,24 @@ static void tegra_crtc_prepare(struct drm_crtc *crtc) | |||
315 | tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER); | 678 | tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER); |
316 | 679 | ||
317 | value = VBLANK_INT | WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT; | 680 | value = VBLANK_INT | WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT; |
318 | tegra_dc_writel(dc, value, DC_CMD_INT_MASK); | ||
319 | |||
320 | value = VBLANK_INT | WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT; | ||
321 | tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE); | 681 | tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE); |
682 | |||
683 | value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT; | ||
684 | tegra_dc_writel(dc, value, DC_CMD_INT_MASK); | ||
322 | } | 685 | } |
323 | 686 | ||
324 | static void tegra_crtc_commit(struct drm_crtc *crtc) | 687 | static void tegra_crtc_commit(struct drm_crtc *crtc) |
325 | { | 688 | { |
326 | struct tegra_dc *dc = to_tegra_dc(crtc); | 689 | struct tegra_dc *dc = to_tegra_dc(crtc); |
327 | unsigned long update_mask; | ||
328 | unsigned long value; | 690 | unsigned long value; |
329 | 691 | ||
330 | update_mask = GENERAL_ACT_REQ | WIN_A_ACT_REQ; | 692 | value = GENERAL_UPDATE | WIN_A_UPDATE; |
331 | 693 | tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL); | |
332 | tegra_dc_writel(dc, update_mask << 8, DC_CMD_STATE_CONTROL); | ||
333 | 694 | ||
334 | value = tegra_dc_readl(dc, DC_CMD_INT_ENABLE); | 695 | value = GENERAL_ACT_REQ | WIN_A_ACT_REQ; |
335 | value |= FRAME_END_INT; | 696 | tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL); |
336 | tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE); | ||
337 | |||
338 | value = tegra_dc_readl(dc, DC_CMD_INT_MASK); | ||
339 | value |= FRAME_END_INT; | ||
340 | tegra_dc_writel(dc, value, DC_CMD_INT_MASK); | ||
341 | 697 | ||
342 | tegra_dc_writel(dc, update_mask, DC_CMD_STATE_CONTROL); | 698 | drm_vblank_post_modeset(crtc->dev, dc->pipe); |
343 | } | 699 | } |
344 | 700 | ||
345 | static void tegra_crtc_load_lut(struct drm_crtc *crtc) | 701 | static void tegra_crtc_load_lut(struct drm_crtc *crtc) |
@@ -347,15 +703,16 @@ static void tegra_crtc_load_lut(struct drm_crtc *crtc) | |||
347 | } | 703 | } |
348 | 704 | ||
349 | static const struct drm_crtc_helper_funcs tegra_crtc_helper_funcs = { | 705 | static const struct drm_crtc_helper_funcs tegra_crtc_helper_funcs = { |
350 | .dpms = tegra_crtc_dpms, | 706 | .disable = tegra_crtc_disable, |
351 | .mode_fixup = tegra_crtc_mode_fixup, | 707 | .mode_fixup = tegra_crtc_mode_fixup, |
352 | .mode_set = tegra_crtc_mode_set, | 708 | .mode_set = tegra_crtc_mode_set, |
709 | .mode_set_base = tegra_crtc_mode_set_base, | ||
353 | .prepare = tegra_crtc_prepare, | 710 | .prepare = tegra_crtc_prepare, |
354 | .commit = tegra_crtc_commit, | 711 | .commit = tegra_crtc_commit, |
355 | .load_lut = tegra_crtc_load_lut, | 712 | .load_lut = tegra_crtc_load_lut, |
356 | }; | 713 | }; |
357 | 714 | ||
358 | static irqreturn_t tegra_drm_irq(int irq, void *data) | 715 | static irqreturn_t tegra_dc_irq(int irq, void *data) |
359 | { | 716 | { |
360 | struct tegra_dc *dc = data; | 717 | struct tegra_dc *dc = data; |
361 | unsigned long status; | 718 | unsigned long status; |
@@ -374,6 +731,7 @@ static irqreturn_t tegra_drm_irq(int irq, void *data) | |||
374 | dev_dbg(dc->dev, "%s(): vertical blank\n", __func__); | 731 | dev_dbg(dc->dev, "%s(): vertical blank\n", __func__); |
375 | */ | 732 | */ |
376 | drm_handle_vblank(dc->base.dev, dc->pipe); | 733 | drm_handle_vblank(dc->base.dev, dc->pipe); |
734 | tegra_dc_finish_page_flip(dc); | ||
377 | } | 735 | } |
378 | 736 | ||
379 | if (status & (WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT)) { | 737 | if (status & (WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT)) { |
@@ -588,7 +946,7 @@ static int tegra_dc_show_regs(struct seq_file *s, void *data) | |||
588 | DUMP_REG(DC_WIN_BLEND_1WIN); | 946 | DUMP_REG(DC_WIN_BLEND_1WIN); |
589 | DUMP_REG(DC_WIN_BLEND_2WIN_X); | 947 | DUMP_REG(DC_WIN_BLEND_2WIN_X); |
590 | DUMP_REG(DC_WIN_BLEND_2WIN_Y); | 948 | DUMP_REG(DC_WIN_BLEND_2WIN_Y); |
591 | DUMP_REG(DC_WIN_BLEND32WIN_XY); | 949 | DUMP_REG(DC_WIN_BLEND_3WIN_XY); |
592 | DUMP_REG(DC_WIN_HP_FETCH_CONTROL); | 950 | DUMP_REG(DC_WIN_HP_FETCH_CONTROL); |
593 | DUMP_REG(DC_WINBUF_START_ADDR); | 951 | DUMP_REG(DC_WINBUF_START_ADDR); |
594 | DUMP_REG(DC_WINBUF_START_ADDR_NS); | 952 | DUMP_REG(DC_WINBUF_START_ADDR_NS); |
@@ -690,13 +1048,17 @@ static int tegra_dc_drm_init(struct host1x_client *client, | |||
690 | return err; | 1048 | return err; |
691 | } | 1049 | } |
692 | 1050 | ||
1051 | err = tegra_dc_add_planes(drm, dc); | ||
1052 | if (err < 0) | ||
1053 | return err; | ||
1054 | |||
693 | if (IS_ENABLED(CONFIG_DEBUG_FS)) { | 1055 | if (IS_ENABLED(CONFIG_DEBUG_FS)) { |
694 | err = tegra_dc_debugfs_init(dc, drm->primary); | 1056 | err = tegra_dc_debugfs_init(dc, drm->primary); |
695 | if (err < 0) | 1057 | if (err < 0) |
696 | dev_err(dc->dev, "debugfs setup failed: %d\n", err); | 1058 | dev_err(dc->dev, "debugfs setup failed: %d\n", err); |
697 | } | 1059 | } |
698 | 1060 | ||
699 | err = devm_request_irq(dc->dev, dc->irq, tegra_drm_irq, 0, | 1061 | err = devm_request_irq(dc->dev, dc->irq, tegra_dc_irq, 0, |
700 | dev_name(dc->dev), dc); | 1062 | dev_name(dc->dev), dc); |
701 | if (err < 0) { | 1063 | if (err < 0) { |
702 | dev_err(dc->dev, "failed to request IRQ#%u: %d\n", dc->irq, | 1064 | dev_err(dc->dev, "failed to request IRQ#%u: %d\n", dc->irq, |
@@ -745,6 +1107,7 @@ static int tegra_dc_probe(struct platform_device *pdev) | |||
745 | if (!dc) | 1107 | if (!dc) |
746 | return -ENOMEM; | 1108 | return -ENOMEM; |
747 | 1109 | ||
1110 | spin_lock_init(&dc->lock); | ||
748 | INIT_LIST_HEAD(&dc->list); | 1111 | INIT_LIST_HEAD(&dc->list); |
749 | dc->dev = &pdev->dev; | 1112 | dc->dev = &pdev->dev; |
750 | 1113 | ||
diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h index 99977b5d5c36..79eaec9aac77 100644 --- a/drivers/gpu/drm/tegra/dc.h +++ b/drivers/gpu/drm/tegra/dc.h | |||
@@ -58,6 +58,8 @@ | |||
58 | #define DC_CMD_SIGNAL_RAISE3 0x03e | 58 | #define DC_CMD_SIGNAL_RAISE3 0x03e |
59 | 59 | ||
60 | #define DC_CMD_STATE_ACCESS 0x040 | 60 | #define DC_CMD_STATE_ACCESS 0x040 |
61 | #define READ_MUX (1 << 0) | ||
62 | #define WRITE_MUX (1 << 2) | ||
61 | 63 | ||
62 | #define DC_CMD_STATE_CONTROL 0x041 | 64 | #define DC_CMD_STATE_CONTROL 0x041 |
63 | #define GENERAL_ACT_REQ (1 << 0) | 65 | #define GENERAL_ACT_REQ (1 << 0) |
@@ -290,8 +292,18 @@ | |||
290 | #define DC_DISP_SD_HW_K_VALUES 0x4dd | 292 | #define DC_DISP_SD_HW_K_VALUES 0x4dd |
291 | #define DC_DISP_SD_MAN_K_VALUES 0x4de | 293 | #define DC_DISP_SD_MAN_K_VALUES 0x4de |
292 | 294 | ||
295 | #define DC_WIN_CSC_YOF 0x611 | ||
296 | #define DC_WIN_CSC_KYRGB 0x612 | ||
297 | #define DC_WIN_CSC_KUR 0x613 | ||
298 | #define DC_WIN_CSC_KVR 0x614 | ||
299 | #define DC_WIN_CSC_KUG 0x615 | ||
300 | #define DC_WIN_CSC_KVG 0x616 | ||
301 | #define DC_WIN_CSC_KUB 0x617 | ||
302 | #define DC_WIN_CSC_KVB 0x618 | ||
303 | |||
293 | #define DC_WIN_WIN_OPTIONS 0x700 | 304 | #define DC_WIN_WIN_OPTIONS 0x700 |
294 | #define COLOR_EXPAND (1 << 6) | 305 | #define COLOR_EXPAND (1 << 6) |
306 | #define CSC_ENABLE (1 << 18) | ||
295 | #define WIN_ENABLE (1 << 30) | 307 | #define WIN_ENABLE (1 << 30) |
296 | 308 | ||
297 | #define DC_WIN_BYTE_SWAP 0x701 | 309 | #define DC_WIN_BYTE_SWAP 0x701 |
@@ -359,7 +371,7 @@ | |||
359 | #define DC_WIN_BLEND_1WIN 0x710 | 371 | #define DC_WIN_BLEND_1WIN 0x710 |
360 | #define DC_WIN_BLEND_2WIN_X 0x711 | 372 | #define DC_WIN_BLEND_2WIN_X 0x711 |
361 | #define DC_WIN_BLEND_2WIN_Y 0x712 | 373 | #define DC_WIN_BLEND_2WIN_Y 0x712 |
362 | #define DC_WIN_BLEND32WIN_XY 0x713 | 374 | #define DC_WIN_BLEND_3WIN_XY 0x713 |
363 | 375 | ||
364 | #define DC_WIN_HP_FETCH_CONTROL 0x714 | 376 | #define DC_WIN_HP_FETCH_CONTROL 0x714 |
365 | 377 | ||
diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index 3a503c9e4686..181a370c56c1 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c | |||
@@ -40,6 +40,10 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags) | |||
40 | if (err < 0) | 40 | if (err < 0) |
41 | return err; | 41 | return err; |
42 | 42 | ||
43 | err = drm_vblank_init(drm, drm->mode_config.num_crtc); | ||
44 | if (err < 0) | ||
45 | return err; | ||
46 | |||
43 | err = tegra_drm_fb_init(drm); | 47 | err = tegra_drm_fb_init(drm); |
44 | if (err < 0) | 48 | if (err < 0) |
45 | return err; | 49 | return err; |
@@ -89,13 +93,112 @@ static const struct file_operations tegra_drm_fops = { | |||
89 | .llseek = noop_llseek, | 93 | .llseek = noop_llseek, |
90 | }; | 94 | }; |
91 | 95 | ||
96 | static struct drm_crtc *tegra_crtc_from_pipe(struct drm_device *drm, int pipe) | ||
97 | { | ||
98 | struct drm_crtc *crtc; | ||
99 | |||
100 | list_for_each_entry(crtc, &drm->mode_config.crtc_list, head) { | ||
101 | struct tegra_dc *dc = to_tegra_dc(crtc); | ||
102 | |||
103 | if (dc->pipe == pipe) | ||
104 | return crtc; | ||
105 | } | ||
106 | |||
107 | return NULL; | ||
108 | } | ||
109 | |||
110 | static u32 tegra_drm_get_vblank_counter(struct drm_device *dev, int crtc) | ||
111 | { | ||
112 | /* TODO: implement real hardware counter using syncpoints */ | ||
113 | return drm_vblank_count(dev, crtc); | ||
114 | } | ||
115 | |||
116 | static int tegra_drm_enable_vblank(struct drm_device *drm, int pipe) | ||
117 | { | ||
118 | struct drm_crtc *crtc = tegra_crtc_from_pipe(drm, pipe); | ||
119 | struct tegra_dc *dc = to_tegra_dc(crtc); | ||
120 | |||
121 | if (!crtc) | ||
122 | return -ENODEV; | ||
123 | |||
124 | tegra_dc_enable_vblank(dc); | ||
125 | |||
126 | return 0; | ||
127 | } | ||
128 | |||
129 | static void tegra_drm_disable_vblank(struct drm_device *drm, int pipe) | ||
130 | { | ||
131 | struct drm_crtc *crtc = tegra_crtc_from_pipe(drm, pipe); | ||
132 | struct tegra_dc *dc = to_tegra_dc(crtc); | ||
133 | |||
134 | if (crtc) | ||
135 | tegra_dc_disable_vblank(dc); | ||
136 | } | ||
137 | |||
138 | static void tegra_drm_preclose(struct drm_device *drm, struct drm_file *file) | ||
139 | { | ||
140 | struct drm_crtc *crtc; | ||
141 | |||
142 | list_for_each_entry(crtc, &drm->mode_config.crtc_list, head) | ||
143 | tegra_dc_cancel_page_flip(crtc, file); | ||
144 | } | ||
145 | |||
146 | #ifdef CONFIG_DEBUG_FS | ||
147 | static int tegra_debugfs_framebuffers(struct seq_file *s, void *data) | ||
148 | { | ||
149 | struct drm_info_node *node = (struct drm_info_node *)s->private; | ||
150 | struct drm_device *drm = node->minor->dev; | ||
151 | struct drm_framebuffer *fb; | ||
152 | |||
153 | mutex_lock(&drm->mode_config.fb_lock); | ||
154 | |||
155 | list_for_each_entry(fb, &drm->mode_config.fb_list, head) { | ||
156 | seq_printf(s, "%3d: user size: %d x %d, depth %d, %d bpp, refcount %d\n", | ||
157 | fb->base.id, fb->width, fb->height, fb->depth, | ||
158 | fb->bits_per_pixel, | ||
159 | atomic_read(&fb->refcount.refcount)); | ||
160 | } | ||
161 | |||
162 | mutex_unlock(&drm->mode_config.fb_lock); | ||
163 | |||
164 | return 0; | ||
165 | } | ||
166 | |||
167 | static struct drm_info_list tegra_debugfs_list[] = { | ||
168 | { "framebuffers", tegra_debugfs_framebuffers, 0 }, | ||
169 | }; | ||
170 | |||
171 | static int tegra_debugfs_init(struct drm_minor *minor) | ||
172 | { | ||
173 | return drm_debugfs_create_files(tegra_debugfs_list, | ||
174 | ARRAY_SIZE(tegra_debugfs_list), | ||
175 | minor->debugfs_root, minor); | ||
176 | } | ||
177 | |||
178 | static void tegra_debugfs_cleanup(struct drm_minor *minor) | ||
179 | { | ||
180 | drm_debugfs_remove_files(tegra_debugfs_list, | ||
181 | ARRAY_SIZE(tegra_debugfs_list), minor); | ||
182 | } | ||
183 | #endif | ||
184 | |||
92 | struct drm_driver tegra_drm_driver = { | 185 | struct drm_driver tegra_drm_driver = { |
93 | .driver_features = DRIVER_BUS_PLATFORM | DRIVER_MODESET | DRIVER_GEM, | 186 | .driver_features = DRIVER_BUS_PLATFORM | DRIVER_MODESET | DRIVER_GEM, |
94 | .load = tegra_drm_load, | 187 | .load = tegra_drm_load, |
95 | .unload = tegra_drm_unload, | 188 | .unload = tegra_drm_unload, |
96 | .open = tegra_drm_open, | 189 | .open = tegra_drm_open, |
190 | .preclose = tegra_drm_preclose, | ||
97 | .lastclose = tegra_drm_lastclose, | 191 | .lastclose = tegra_drm_lastclose, |
98 | 192 | ||
193 | .get_vblank_counter = tegra_drm_get_vblank_counter, | ||
194 | .enable_vblank = tegra_drm_enable_vblank, | ||
195 | .disable_vblank = tegra_drm_disable_vblank, | ||
196 | |||
197 | #if defined(CONFIG_DEBUG_FS) | ||
198 | .debugfs_init = tegra_debugfs_init, | ||
199 | .debugfs_cleanup = tegra_debugfs_cleanup, | ||
200 | #endif | ||
201 | |||
99 | .gem_free_object = drm_gem_cma_free_object, | 202 | .gem_free_object = drm_gem_cma_free_object, |
100 | .gem_vm_ops = &drm_gem_cma_vm_ops, | 203 | .gem_vm_ops = &drm_gem_cma_vm_ops, |
101 | .dumb_create = drm_gem_cma_dumb_create, | 204 | .dumb_create = drm_gem_cma_dumb_create, |
diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h index 741b5dc2742c..6dd75a2600eb 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h | |||
@@ -18,16 +18,6 @@ | |||
18 | #include <drm/drm_fb_cma_helper.h> | 18 | #include <drm/drm_fb_cma_helper.h> |
19 | #include <drm/drm_fixed.h> | 19 | #include <drm/drm_fixed.h> |
20 | 20 | ||
21 | struct tegra_framebuffer { | ||
22 | struct drm_framebuffer base; | ||
23 | struct drm_gem_cma_object *obj; | ||
24 | }; | ||
25 | |||
26 | static inline struct tegra_framebuffer *to_tegra_fb(struct drm_framebuffer *fb) | ||
27 | { | ||
28 | return container_of(fb, struct tegra_framebuffer, base); | ||
29 | } | ||
30 | |||
31 | struct host1x { | 21 | struct host1x { |
32 | struct drm_device *drm; | 22 | struct drm_device *drm; |
33 | struct device *dev; | 23 | struct device *dev; |
@@ -44,7 +34,6 @@ struct host1x { | |||
44 | struct list_head clients; | 34 | struct list_head clients; |
45 | 35 | ||
46 | struct drm_fbdev_cma *fbdev; | 36 | struct drm_fbdev_cma *fbdev; |
47 | struct tegra_framebuffer fb; | ||
48 | }; | 37 | }; |
49 | 38 | ||
50 | struct host1x_client; | 39 | struct host1x_client; |
@@ -75,6 +64,7 @@ struct tegra_output; | |||
75 | 64 | ||
76 | struct tegra_dc { | 65 | struct tegra_dc { |
77 | struct host1x_client client; | 66 | struct host1x_client client; |
67 | spinlock_t lock; | ||
78 | 68 | ||
79 | struct host1x *host1x; | 69 | struct host1x *host1x; |
80 | struct device *dev; | 70 | struct device *dev; |
@@ -94,6 +84,9 @@ struct tegra_dc { | |||
94 | struct drm_info_list *debugfs_files; | 84 | struct drm_info_list *debugfs_files; |
95 | struct drm_minor *minor; | 85 | struct drm_minor *minor; |
96 | struct dentry *debugfs; | 86 | struct dentry *debugfs; |
87 | |||
88 | /* page-flip handling */ | ||
89 | struct drm_pending_vblank_event *event; | ||
97 | }; | 90 | }; |
98 | 91 | ||
99 | static inline struct tegra_dc *host1x_client_to_dc(struct host1x_client *client) | 92 | static inline struct tegra_dc *host1x_client_to_dc(struct host1x_client *client) |
@@ -118,6 +111,34 @@ static inline unsigned long tegra_dc_readl(struct tegra_dc *dc, | |||
118 | return readl(dc->regs + (reg << 2)); | 111 | return readl(dc->regs + (reg << 2)); |
119 | } | 112 | } |
120 | 113 | ||
114 | struct tegra_dc_window { | ||
115 | struct { | ||
116 | unsigned int x; | ||
117 | unsigned int y; | ||
118 | unsigned int w; | ||
119 | unsigned int h; | ||
120 | } src; | ||
121 | struct { | ||
122 | unsigned int x; | ||
123 | unsigned int y; | ||
124 | unsigned int w; | ||
125 | unsigned int h; | ||
126 | } dst; | ||
127 | unsigned int bits_per_pixel; | ||
128 | unsigned int format; | ||
129 | unsigned int stride[2]; | ||
130 | unsigned long base[3]; | ||
131 | }; | ||
132 | |||
133 | /* from dc.c */ | ||
134 | extern unsigned int tegra_dc_format(uint32_t format); | ||
135 | extern int tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index, | ||
136 | const struct tegra_dc_window *window); | ||
137 | extern void tegra_dc_enable_vblank(struct tegra_dc *dc); | ||
138 | extern void tegra_dc_disable_vblank(struct tegra_dc *dc); | ||
139 | extern void tegra_dc_cancel_page_flip(struct drm_crtc *crtc, | ||
140 | struct drm_file *file); | ||
141 | |||
121 | struct tegra_output_ops { | 142 | struct tegra_output_ops { |
122 | int (*enable)(struct tegra_output *output); | 143 | int (*enable)(struct tegra_output *output); |
123 | int (*disable)(struct tegra_output *output); | 144 | int (*disable)(struct tegra_output *output); |