blob: 44258a8d2c72e17a3cf8b619e6cb57c35b11a71d [file] [log] [blame]
The Android Open Source Project7c1b96a2008-10-21 07:00:00 -07001/*
2 **
3 ** Copyright 2007 The Android Open Source Project
4 **
5 ** Licensed under the Apache License Version 2.0(the "License");
6 ** you may not use this file except in compliance with the License.
7 ** You may obtain a copy of the License at
8 **
9 ** http://www.apache.org/licenses/LICENSE-2.0
10 **
11 ** Unless required by applicable law or agreed to in writing software
12 ** distributed under the License is distributed on an "AS IS" BASIS
13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND either express or implied.
14 ** See the License for the specific language governing permissions and
15 ** limitations under the License.
16 */
17
18#define LOG_TAG "EGLDisplaySurface"
19
20#include <stdlib.h>
21#include <stdio.h>
22#include <string.h>
23#include <unistd.h>
24#include <fcntl.h>
25#include <sys/ioctl.h>
26#include <sys/types.h>
27#include <sys/mman.h>
28
29#include <cutils/log.h>
30#include <cutils/atomic.h>
The Android Open Source Projecte09fd9e2008-12-17 18:05:43 -080031#include <cutils/properties.h>
32
33#include <hardware/copybit.h>
The Android Open Source Project7c1b96a2008-10-21 07:00:00 -070034
35#include <ui/SurfaceComposerClient.h>
36#include <ui/DisplayInfo.h>
37#include <ui/Rect.h>
38#include <ui/Region.h>
The Android Open Source Projecte09fd9e2008-12-17 18:05:43 -080039#include <ui/EGLDisplaySurface.h>
The Android Open Source Project7c1b96a2008-10-21 07:00:00 -070040
41#if HAVE_ANDROID_OS
42#include <linux/msm_mdp.h>
43#endif
44
45#include <GLES/egl.h>
46
47#include <pixelflinger/format.h>
48
The Android Open Source Project7c1b96a2008-10-21 07:00:00 -070049
50// ----------------------------------------------------------------------------
51
52egl_native_window_t* android_createDisplaySurface()
53{
54 egl_native_window_t* s = new android::EGLDisplaySurface();
55 s->memory_type = NATIVE_MEMORY_TYPE_GPU;
56 return s;
57}
58
59#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true ))
60#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false ))
61
62// ----------------------------------------------------------------------------
63namespace android {
64// ----------------------------------------------------------------------------
65
66EGLDisplaySurface::EGLDisplaySurface()
67 : EGLNativeSurface<EGLDisplaySurface>()
68{
69 egl_native_window_t::version = sizeof(egl_native_window_t);
70 egl_native_window_t::ident = 0;
71 egl_native_window_t::incRef = &EGLDisplaySurface::hook_incRef;
72 egl_native_window_t::decRef = &EGLDisplaySurface::hook_decRef;
73 egl_native_window_t::swapBuffers = &EGLDisplaySurface::hook_swapBuffers;
74 egl_native_window_t::setSwapRectangle = &EGLDisplaySurface::hook_setSwapRectangle;
75 egl_native_window_t::nextBuffer = &EGLDisplaySurface::hook_nextBuffer;
76 egl_native_window_t::connect = 0;
77 egl_native_window_t::disconnect = 0;
78
79 mFb[0].data = 0;
80 mFb[1].data = 0;
81 mBlitEngine = 0;
82 egl_native_window_t::fd = mapFrameBuffer();
83 if (egl_native_window_t::fd >= 0) {
The Android Open Source Projecte09fd9e2008-12-17 18:05:43 -080084
85 hw_module_t const* module;
86 if (hw_get_module(COPYBIT_HARDWARE_MODULE_ID, &module) == 0) {
87 copybit_open(module, &mBlitEngine);
88 }
89
The Android Open Source Project7c1b96a2008-10-21 07:00:00 -070090 const float in2mm = 25.4f;
91 float refreshRate = 1000000000000000LLU / (
92 float( mInfo.upper_margin + mInfo.lower_margin + mInfo.yres )
93 * ( mInfo.left_margin + mInfo.right_margin + mInfo.xres )
94 * mInfo.pixclock);
95
96 const GGLSurface& buffer = mFb[1 - mIndex];
97 egl_native_window_t::width = buffer.width;
98 egl_native_window_t::height = buffer.height;
99 egl_native_window_t::stride = buffer.stride;
100 egl_native_window_t::format = buffer.format;
101 egl_native_window_t::base = intptr_t(mFb[0].data);
102 egl_native_window_t::offset =
103 intptr_t(buffer.data) - egl_native_window_t::base;
104 egl_native_window_t::flags = 0;
105 egl_native_window_t::xdpi = (mInfo.xres * in2mm) / mInfo.width;
106 egl_native_window_t::ydpi = (mInfo.yres * in2mm) / mInfo.height;
107 egl_native_window_t::fps = refreshRate;
108 egl_native_window_t::memory_type = NATIVE_MEMORY_TYPE_FB;
109 // no error, set the magic word
110 egl_native_window_t::magic = 0x600913;
111 }
112 mSwapCount = -1;
113 mPageFlipCount = 0;
114}
115
116EGLDisplaySurface::~EGLDisplaySurface()
117{
118 magic = 0;
The Android Open Source Projecte09fd9e2008-12-17 18:05:43 -0800119 copybit_close(mBlitEngine);
The Android Open Source Project7c1b96a2008-10-21 07:00:00 -0700120 mBlitEngine = 0;
121 close(egl_native_window_t::fd);
122 munmap(mFb[0].data, mSize);
123 if (!(mFlags & PAGE_FLIP))
124 free((void*)mFb[1].data);
125}
126
127void EGLDisplaySurface::hook_incRef(NativeWindowType window) {
128 EGLDisplaySurface* that = static_cast<EGLDisplaySurface*>(window);
129 that->incStrong(that);
130}
131void EGLDisplaySurface::hook_decRef(NativeWindowType window) {
132 EGLDisplaySurface* that = static_cast<EGLDisplaySurface*>(window);
133 that->decStrong(that);
134}
135uint32_t EGLDisplaySurface::hook_swapBuffers(NativeWindowType window) {
136 EGLDisplaySurface* that = static_cast<EGLDisplaySurface*>(window);
137 return that->swapBuffers();
138}
139uint32_t EGLDisplaySurface::hook_nextBuffer(NativeWindowType window) {
140 EGLDisplaySurface* that = static_cast<EGLDisplaySurface*>(window);
141 return that->nextBuffer();
142}
143void EGLDisplaySurface::hook_setSwapRectangle(NativeWindowType window,
144 int l, int t, int w, int h) {
145 EGLDisplaySurface* that = static_cast<EGLDisplaySurface*>(window);
146 that->setSwapRectangle(l, t, w, h);
147}
148
149void EGLDisplaySurface::setSwapRectangle(int l, int t, int w, int h)
150{
151 mInfo.reserved[0] = 0x54445055; // "UPDT";
152 mInfo.reserved[1] = (uint16_t)l | ((uint32_t)t << 16);
153 mInfo.reserved[2] = (uint16_t)(l+w) | ((uint32_t)(t+h) << 16);
154}
155
156uint32_t EGLDisplaySurface::swapBuffers()
157{
The Android Open Source Project7c1b96a2008-10-21 07:00:00 -0700158#define SHOW_FPS 0
159#if SHOW_FPS
160 nsecs_t now = systemTime();
161 if (mSwapCount == -1) {
162 mTime = now;
163 mSwapCount = 0;
164 mSleep = 0;
165 } else {
166 nsecs_t d = now-mTime;
167 if (d >= seconds(1)) {
168 double fps = (mSwapCount * double(seconds(1))) / double(d);
169 LOGD("%f fps, sleep=%d / frame",
170 fps, (int)ns2us(mSleep / mSwapCount));
171 mSwapCount = 0;
172 mTime = now;
173 mSleep = 0;
174 } else {
175 mSwapCount++;
176 }
177 }
178#endif
The Android Open Source Projecte09fd9e2008-12-17 18:05:43 -0800179 /* If we can't do the page_flip, just copy the back buffer to the front */
180 if (!(mFlags & PAGE_FLIP)) {
181 memcpy(mFb[0].data, mFb[1].data, mInfo.xres*mInfo.yres*2);
182 return 0;
183 }
The Android Open Source Project7c1b96a2008-10-21 07:00:00 -0700184
185 // do the actual flip
186 mIndex = 1 - mIndex;
187 mInfo.activate = FB_ACTIVATE_VBL;
188 mInfo.yoffset = mIndex ? mInfo.yres : 0;
189 if (ioctl(egl_native_window_t::fd, FBIOPUT_VSCREENINFO, &mInfo) == -1) {
190 LOGE("FBIOPUT_VSCREENINFO failed");
191 return 0;
192 }
193
194 /*
195 * this is a monstrous hack: Because the h/w accelerator is not able
196 * to render directly into the framebuffer, we need to copy its
197 * internal framebuffer out to the fb.
198 * oem[0] is used to access the fd of internal fb.
199 * All this is needed only in standalone mode, in SurfaceFlinger mode
200 * we control where the GPU renders.
201 * We do this only if we have copybit, since this hack is needed only
202 * with msm7k.
203 */
204 if (egl_native_window_t::memory_type == NATIVE_MEMORY_TYPE_GPU && oem[0] && mBlitEngine) {
The Android Open Source Projecte09fd9e2008-12-17 18:05:43 -0800205 copybit_device_t *copybit = mBlitEngine;
The Android Open Source Project7c1b96a2008-10-21 07:00:00 -0700206 copybit_rect_t sdrect = { 0, 0,
207 egl_native_window_t::width, egl_native_window_t::height };
208 copybit_image_t dst = {
209 egl_native_window_t::width,
210 egl_native_window_t::height,
211 egl_native_window_t::format,
212 egl_native_window_t::offset,
213 (void*)egl_native_window_t::base,
214 egl_native_window_t::fd
215 };
216 copybit_image_t src = {
217 egl_native_window_t::width,
218 egl_native_window_t::height,
219 egl_native_window_t::format, // XXX: use proper format
220 egl_native_window_t::offset,
221 (void*)egl_native_window_t::base, // XXX: use proper base
222 egl_native_window_t::oem[0]
223 };
224 region_iterator it(Region(Rect(
225 egl_native_window_t::width, egl_native_window_t::height)));
226 copybit->set_parameter(copybit, COPYBIT_TRANSFORM, 0);
227 copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, 0xFF);
228 copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_DISABLE);
229 copybit->stretch(copybit, &dst, &src, &sdrect, &sdrect, &it);
230 }
231
232 // update the address of the buffer to draw to next
233 const GGLSurface& buffer = mFb[1 - mIndex];
234 egl_native_window_t::offset =
235 intptr_t(buffer.data) - egl_native_window_t::base;
236
237#if SHOW_FPS
238 mSleep += systemTime()-now;
239#endif
240
241 mPageFlipCount++;
242
243 // We don't support screen-size changes for now
244 return 0;
245}
246
247int32_t EGLDisplaySurface::getPageFlipCount() const
248{
249 return mPageFlipCount;
250}
251
252uint32_t EGLDisplaySurface::nextBuffer()
253{
254 // update the address of the buffer to draw to next
255 const GGLSurface& buffer = mFb[mIndex];
256 egl_native_window_t::offset =
257 intptr_t(buffer.data) - egl_native_window_t::base;
258 return 0;
259}
260
261void EGLDisplaySurface::copyFrontToBack(const Region& copyback)
262{
263#if HAVE_ANDROID_OS
264 if (mBlitEngine) {
265 copybit_image_t dst = {
266 w: egl_native_window_t::stride,
267 h: egl_native_window_t::height,
268 format: egl_native_window_t::format,
269 offset: mFb[1-mIndex].data - mFb[0].data,
270 base: (void*)egl_native_window_t::base,
271 fd: egl_native_window_t::fd
272 };
273 copybit_image_t src = {
274 w: egl_native_window_t::stride,
275 h: egl_native_window_t::height,
276 format: egl_native_window_t::format,
277 offset: mFb[mIndex].data - mFb[0].data,
278 base: (void*)egl_native_window_t::base,
279 fd: egl_native_window_t::fd
280 };
281 region_iterator it(copyback);
282 mBlitEngine->blit(mBlitEngine, &dst, &src, &it);
283 } else
284#endif
285 {
The Android Open Source Projecte09fd9e2008-12-17 18:05:43 -0800286 /* no extra copy needed since we copied back to front instead of
287 * flipping */
288 if (!(mFlags & PAGE_FLIP)) {
289 return;
290 }
291
The Android Open Source Project7c1b96a2008-10-21 07:00:00 -0700292 Region::iterator iterator(copyback);
293 if (iterator) {
294 Rect r;
295 uint8_t* const screen_src = mFb[ mIndex].data;
296 uint8_t* const screen_dst = mFb[1-mIndex].data;
297 const size_t bpp = bytesPerPixel(egl_native_window_t::format);
298 const size_t bpr = egl_native_window_t::stride * bpp;
299 while (iterator.iterate(&r)) {
300 ssize_t h = r.bottom - r.top;
301 if (h) {
302 size_t size = (r.right - r.left) * bpp;
303 size_t o = (r.left + egl_native_window_t::stride * r.top) * bpp;
304 uint8_t* s = screen_src + o;
305 uint8_t* d = screen_dst + o;
306 if (size == bpr) {
307 size *= h;
308 h = 1;
309 }
310 do {
311 memcpy(d, s, size);
312 d += bpr;
313 s += bpr;
314 } while (--h > 0);
315 }
316 }
317 }
318 }
319}
320
321status_t EGLDisplaySurface::mapFrameBuffer()
322{
323 char const * const device_template[] = {
324 "/dev/graphics/fb%u",
325 "/dev/fb%u",
326 0 };
327 int fd = -1;
328 int i=0;
329 char name[64];
330 while ((fd==-1) && device_template[i]) {
331 snprintf(name, 64, device_template[i], 0);
332 fd = open(name, O_RDWR, 0);
333 i++;
334 }
335 if (fd < 0)
336 return -errno;
337
338 struct fb_fix_screeninfo finfo;
339 if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) == -1)
340 return -errno;
341
342 struct fb_var_screeninfo info;
343 if (ioctl(fd, FBIOGET_VSCREENINFO, &info) == -1)
344 return -errno;
345
346 info.reserved[0] = 0;
347 info.reserved[1] = 0;
348 info.reserved[2] = 0;
349 info.xoffset = 0;
350 info.yoffset = 0;
351 info.yres_virtual = info.yres * 2;
352 info.bits_per_pixel = 16;
353 /* Explicitly request 5/6/5 */
354 info.red.offset = 11;
355 info.red.length = 5;
356 info.green.offset = 5;
357 info.green.length = 6;
358 info.blue.offset = 0;
359 info.blue.length = 5;
360 info.transp.offset = 0;
361 info.transp.length = 0;
362 info.activate = FB_ACTIVATE_NOW;
363
364 uint32_t flags = PAGE_FLIP;
365 if (ioctl(fd, FBIOPUT_VSCREENINFO, &info) == -1) {
366 info.yres_virtual = info.yres;
367 flags &= ~PAGE_FLIP;
368 LOGW("FBIOPUT_VSCREENINFO failed, page flipping not supported");
369 }
370
371 if (info.yres_virtual < info.yres * 2) {
372 info.yres_virtual = info.yres;
373 flags &= ~PAGE_FLIP;
374 LOGW("page flipping not supported (yres_virtual=%d, requested=%d)",
375 info.yres_virtual, info.yres*2);
376 }
377
378 if (ioctl(fd, FBIOGET_VSCREENINFO, &info) == -1)
379 return -errno;
380
381 int refreshRate = 1000000000000000LLU /
382 (
383 uint64_t( info.upper_margin + info.lower_margin + info.yres )
384 * ( info.left_margin + info.right_margin + info.xres )
385 * info.pixclock
386 );
387
388 if (refreshRate == 0) {
389 // bleagh, bad info from the driver
390 refreshRate = 60*1000; // 60 Hz
391 }
The Android Open Source Project7c1b96a2008-10-21 07:00:00 -0700392 if (int(info.width) <= 0 || int(info.height) <= 0) {
393 // the driver doesn't return that information
394 // default to 160 dpi
The Android Open Source Projecte09fd9e2008-12-17 18:05:43 -0800395 info.width = ((info.xres * 25.4f)/160.0f + 0.5f);
396 info.height = ((info.yres * 25.4f)/160.0f + 0.5f);
The Android Open Source Project7c1b96a2008-10-21 07:00:00 -0700397 }
398
399 float xdpi = (info.xres * 25.4f) / info.width;
400 float ydpi = (info.yres * 25.4f) / info.height;
401 float fps = refreshRate / 1000.0f;
402
403 LOGI( "using (fd=%d)\n"
404 "id = %s\n"
405 "xres = %d px\n"
406 "yres = %d px\n"
407 "xres_virtual = %d px\n"
408 "yres_virtual = %d px\n"
409 "bpp = %d\n"
410 "r = %2u:%u\n"
411 "g = %2u:%u\n"
412 "b = %2u:%u\n",
413 fd,
414 finfo.id,
415 info.xres,
416 info.yres,
417 info.xres_virtual,
418 info.yres_virtual,
419 info.bits_per_pixel,
420 info.red.offset, info.red.length,
421 info.green.offset, info.green.length,
422 info.blue.offset, info.blue.length
423 );
424
425 LOGI( "width = %d mm (%f dpi)\n"
426 "height = %d mm (%f dpi)\n"
427 "refresh rate = %.2f Hz\n",
428 info.width, xdpi,
429 info.height, ydpi,
430 fps
431 );
432
433
434 if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) == -1)
435 return -errno;
436
437 if (finfo.smem_len <= 0)
438 return -errno;
439
440 /*
441 * Open and map the display.
442 */
443
444 void* buffer = (uint16_t*) mmap(
445 0, finfo.smem_len,
446 PROT_READ | PROT_WRITE,
447 MAP_SHARED,
448 fd, 0);
449
450 if (buffer == MAP_FAILED)
451 return -errno;
452
453 // at least for now, always clear the fb
454 memset(buffer, 0, finfo.smem_len);
455
456 uint8_t* offscreen[2];
457 offscreen[0] = (uint8_t*)buffer;
458 if (flags & PAGE_FLIP) {
459 offscreen[1] = (uint8_t*)buffer + finfo.line_length*info.yres;
460 } else {
461 offscreen[1] = (uint8_t*)malloc(finfo.smem_len);
462 if (offscreen[1] == 0) {
463 munmap(buffer, finfo.smem_len);
464 return NO_MEMORY;
465 }
466 }
467
468 mFlags = flags;
469 mInfo = info;
470 mFinfo = finfo;
471 mSize = finfo.smem_len;
472 mIndex = 0;
473 for (int i=0 ; i<2 ; i++) {
474 mFb[i].version = sizeof(GGLSurface);
475 mFb[i].width = info.xres;
476 mFb[i].height = info.yres;
477 mFb[i].stride = finfo.line_length / (info.bits_per_pixel >> 3);
478 mFb[i].data = (GGLubyte*)(offscreen[i]);
479 mFb[i].format = GGL_PIXEL_FORMAT_RGB_565;
480 }
481 return fd;
482}
483
484// ----------------------------------------------------------------------------
485}; // namespace android
486// ----------------------------------------------------------------------------