1 /*
   2  * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 #ifdef IMX6_PLATFORM
  27 
  28 #include <stdio.h>
  29 #include <stdlib.h>
  30 #include <unistd.h>
  31 #include <fcntl.h>
  32 #include <linux/fb.h>
  33 #include <sys/ioctl.h>
  34 #include <sys/types.h>
  35 #include <stdlib.h>
  36 #include <stdint.h>
  37 #include <string.h>
  38 #include <dlfcn.h>
  39 #include <errno.h>
  40 #include <linux/mxcfb.h>  // note: i.MX unique header
  41 
  42 #include "lensPort.h"
  43 #include "lensPortInternal.h"
  44 #include "lensPortLogger.h"
  45 
  46 #define LENSFB_IMX6_CURSOR_DEVICE "/dev/fb1"
  47 #define LENSFB_IMX6_CURSOR_SIZE 16
  48 
  49 typedef struct {
  50     int fd;
  51     int width;
  52     int height;
  53     int x,y;
  54     int screenWidth;
  55     int screenHeight;
  56     jlong currentCursor;
  57     // When the cursor is at the extreme right or bottom of the screen, it
  58     // needs to be shifted to show in the correct location. IMX doesn't let us
  59     // position the framebuffer so that it is only partically visible.
  60     int xShift;
  61     int yShift;
  62     jboolean isVisible;
  63 } Imx6FBCursor;
  64 
  65 static Imx6FBCursor cursor = { .fd = -1, .width = 0, .height = 0, .x = 0, .y = 0, .currentCursor = 0, .isVisible = 0,
  66                               .xShift = 0, .yShift = 0 };
  67 
  68 //TODO : platform supports 32 and 16 bits, how do we choose what to use ?
  69 static int use32bit = 0;
  70 
  71 typedef struct {
  72     jint width;
  73     jint height;
  74     jint x;
  75     jint y;
  76     jbyte* buffer;
  77     jint bufferSize;
  78 } Imx6CursorImage;
  79 
  80 
  81 static void fbImx6BlankCursor(){
  82      char buffer[256];
  83      int bytesToWrite;
  84 
  85      // Set buffer to be transparent
  86      memset((void*)buffer, use32bit ? 0 : LENSFB_16_CURSOR_COLOR_KEY, sizeof(buffer));
  87 
  88      if (lseek(cursor.fd, 0, SEEK_SET) == -1) {
  89          GLASS_LOG_SEVERE("Cannot rewrite cursor image");
  90          return;
  91      }
  92 
  93      bytesToWrite = cursor.width * cursor.height * (use32bit ? 4 : 2);
  94      while (bytesToWrite > 0) {
  95          int n = bytesToWrite > sizeof(buffer) ? sizeof(buffer) : bytesToWrite;
  96          int res;
  97          if ((res = write(cursor.fd, buffer, n)) < n) {
  98              GLASS_LOG_SEVERE("Cannot write cursor plane %i bytes, wrote %i bytes", n, res);
  99              return;
 100          }
 101          bytesToWrite -= n;
 102      }
 103 }
 104 
 105 
 106 /* Updates values of xShift and yShift based on the cursor location */
 107 static void fbImx6AdjustShift(){
 108     if (cursor.x > cursor.screenWidth - cursor.width) {
 109         cursor.xShift = cursor.width + cursor.x - cursor.screenWidth;
 110     } else {
 111         cursor.xShift = 0;
 112     }
 113     if (cursor.y > cursor.screenHeight - cursor.height) {
 114         cursor.yShift = cursor.height + cursor.y - cursor.screenHeight;
 115     } else {
 116         cursor.yShift = 0;
 117     }
 118 }
 119 
 120 /* Writes an image into the cursor framebuffer with the given x and y shifts. */
 121 static void fbImx6WriteCursor(int fd, jbyte *cursorImage, int bpp) {
 122     int i, j, k;
 123     char buffer[256];
 124     int cursorSize = cursor.width * cursor.height * bpp;
 125     int xShift = cursor.xShift;
 126     int yShift = cursor.yShift;
 127 
 128     if (!cursor.isVisible) {
 129         return;
 130     }
 131 
 132     if (lseek(cursor.fd, 0, SEEK_SET) == -1) {
 133         GLASS_LOG_SEVERE("Cannot rewrite cursor image");
 134         return;
 135      }
 136 
 137     GLASS_LOG_FINEST("Cursor shift = (%i, %i) at (%i, %i)\n",
 138                      xShift, yShift, cursor.x, cursor.y);
 139     if (xShift == 0 && yShift == 0) {
 140         GLASS_LOG_FINEST("write(cursor.fd, .. %i)", cursorSize);
 141         if ((i = write(cursor.fd, cursorImage, cursorSize)) < cursorSize) {
 142             GLASS_LOG_SEVERE("Cannot write cursor plane cursorSize : %i, wrote %i bytes", cursorSize, i);
 143         }
 144         return;
 145     }
 146 
 147     // Set buffer to be transparent
 148     memset((void*)buffer, use32bit ? 0 : LENSFB_16_CURSOR_COLOR_KEY, sizeof(buffer));
 149 
 150     // fill the y-shift rectangular area
 151     for (i = 0; i < yShift; i++) {
 152         for (j = 0; j < cursor.width * bpp; j += sizeof(buffer)) {
 153             size_t n = cursor.width * bpp - j;
 154             if (n > sizeof(buffer)) {
 155                 n = sizeof(buffer);
 156             }
 157             GLASS_LOG_FINEST("write(cursor.fd, .. %u)", n);
 158             if (write(cursor.fd, buffer, n) < (int)n) {
 159                 GLASS_LOG_SEVERE("Cannot write cursor plane");
 160                 return;
 161             }
 162         }
 163     }
 164     // set the rest of the image
 165     for (i = 0; i < cursor.height - yShift; i++) {
 166         for (j = 0; j < xShift * bpp; j += sizeof(buffer)) {
 167             size_t n = xShift * bpp;
 168             if (n > sizeof(buffer)) {
 169                 n = sizeof(buffer);
 170             }
 171             GLASS_LOG_FINEST("write(cursor.fd, .. %u)", n);
 172             if (write(cursor.fd, buffer, n) < (int)n) {
 173                 GLASS_LOG_SEVERE("Cannot write cursor plane");
 174                 return;
 175             }
 176         }
 177         size_t n = (cursor.width - xShift) * bpp;
 178         GLASS_LOG_FINEST("write(cursor.fd, .. %u)", n);
 179         if (write(cursor.fd, cursorImage + i * cursor.width * bpp, n) < (int)n) {
 180             GLASS_LOG_SEVERE("Cannot write cursor plane");
 181             return;
 182         }
 183     }
 184 }
 185 
 186 
 187 
 188 
 189 
 190 static int fbImx6ChangeCursorSize(int width, int height) {
 191 
 192     struct fb_var_screeninfo screenInfo;
 193     if (ioctl(cursor.fd, FBIOGET_VSCREENINFO, &screenInfo)) {
 194         GLASS_LOG_SEVERE("Error %s in getting screen info", strerror(errno));
 195         return -1;
 196     }
 197 
 198     screenInfo.xres = width;
 199     screenInfo.yres = height;
 200     screenInfo.xres_virtual = width;
 201     screenInfo.yres_virtual = height;
 202     screenInfo.xoffset = 0;
 203     screenInfo.yoffset = 0;
 204     screenInfo.activate = 0;
 205 
 206     if(ioctl(cursor.fd, FBIOPUT_VSCREENINFO, &screenInfo)) {
 207         GLASS_LOG_SEVERE("Error %s in setting screen info", strerror(errno));
 208         return -1;
 209     }
 210 
 211     cursor.width = width;
 212     cursor.height = height;
 213 
 214     return 0;
 215 }
 216 
 217 
 218 
 219 static void fbImx6CursorInitialize(int screenWidth, int screenHeight, int screenDepth) {
 220 
 221     struct fb_var_screeninfo screenInfo;
 222     int rc;
 223     char * zero = "0\n";
 224     int fbc = -1;
 225     int fbo = -1;
 226 
 227     //TODO : the following 2 settings can be moved to a setup script procedure
 228     if ((fbc = open("/sys/class/graphics/fbcon/cursor_blink",O_RDWR)) < 0) {
 229         GLASS_LOG_SEVERE("Error %s in opening /sys/class/graphics/fbcon/cursor_blink", strerror(errno));
 230     } else {
 231         write(fbc,zero,1);
 232         close(fbc);
 233     }
 234     if ((fbo = open("/sys/class/graphics/fb1/blank", O_RDWR)) < 0) {
 235         GLASS_LOG_SEVERE("Error %s in opening /sys/class/graphics/fb1/blank", strerror(errno));
 236     } else {
 237         write(fbo,zero,1);
 238         close(fbo);
 239     }
 240 
 241     // Init cursor global variable fields
 242     cursor.width = LENSFB_IMX6_CURSOR_SIZE;
 243     cursor.height = LENSFB_IMX6_CURSOR_SIZE;
 244     cursor.x = 0;
 245     cursor.y = 0;
 246     cursor.currentCursor = 0;
 247     cursor.isVisible = 0;
 248     cursor.screenWidth = screenWidth;
 249     cursor.screenHeight = screenHeight;
 250 
 251     cursor.fd = open(LENSFB_IMX6_CURSOR_DEVICE, O_RDWR);
 252     if (cursor.fd < 0) {
 253         GLASS_LOG_SEVERE("Cannot open framebuffer device %s",LENSFB_IMX6_CURSOR_DEVICE);
 254         return;
 255     }
 256 
 257     if (ioctl(cursor.fd, FBIOGET_VSCREENINFO, &screenInfo)) {
 258         GLASS_LOG_SEVERE("Error %s in getting screen info", strerror(errno));
 259         return;
 260     }
 261 
 262     GLASS_LOG_INFO("Initializing %d bits pixel %dx%d cursor, current %d bits\n",
 263                    (use32bit ? 32 : 16), LENSFB_IMX6_CURSOR_SIZE, LENSFB_IMX6_CURSOR_SIZE, screenInfo.bits_per_pixel);
 264 
 265     screenInfo.xres = LENSFB_IMX6_CURSOR_SIZE;
 266     screenInfo.yres = LENSFB_IMX6_CURSOR_SIZE;
 267     screenInfo.xres_virtual = LENSFB_IMX6_CURSOR_SIZE;
 268     screenInfo.yres_virtual = LENSFB_IMX6_CURSOR_SIZE;
 269     screenInfo.xoffset = 0;
 270     screenInfo.yoffset = 0;
 271     screenInfo.activate = 0;
 272 
 273     if (use32bit) {
 274         screenInfo.bits_per_pixel = 32;
 275         screenInfo.red.length = 8;
 276         screenInfo.red.offset = 16;
 277         screenInfo.green.length = 8;
 278         screenInfo.green.offset = 8;
 279         screenInfo.blue.length = 8;
 280         screenInfo.blue.offset = 0;
 281         screenInfo.transp.length = 8;
 282         screenInfo.transp.offset = 24;
 283     }  else {
 284         // 565
 285         screenInfo.bits_per_pixel = 16;
 286         screenInfo.red.length = 5;
 287         screenInfo.red.offset = 11;
 288         screenInfo.green.length = 6;
 289         screenInfo.green.offset = 5;
 290         screenInfo.blue.length = 5;
 291         screenInfo.blue.offset = 0;
 292         screenInfo.transp.length = 0;
 293         screenInfo.transp.offset = 0;
 294     }
 295 
 296     if(ioctl(cursor.fd, FBIOPUT_VSCREENINFO, &screenInfo)) {
 297         GLASS_LOG_SEVERE("Error %s in setting screen info", strerror(errno));
 298         return;
 299     }
 300 
 301     if(ioctl(cursor.fd, FBIOBLANK, FB_BLANK_UNBLANK)) {
 302          GLASS_LOG_SEVERE("Error %s in gstting cursor no-blanking", strerror(errno));
 303          return;
 304     }
 305 
 306 
 307     if (use32bit) {
 308         // alpha is taken from each pixel
 309         struct mxcfb_loc_alpha loc_alpha;
 310         loc_alpha.enable = 1;
 311         loc_alpha.alpha_in_pixel = 1;
 312         if (ioctl(cursor.fd, MXCFB_SET_LOC_ALPHA, &loc_alpha) < 0) {
 313             GLASS_LOG_SEVERE("Error %s in setting local alpha", strerror(errno));
 314         }
 315 
 316     } else {
 317         struct mxcfb_color_key color_key;
 318         color_key.color_key = RGB565TOCOLORKEY(LENSFB_16_CURSOR_COLOR_KEY);
 319         color_key.enable = 1;
 320         if ( ioctl(cursor.fd, MXCFB_SET_CLR_KEY, &color_key) < 0) {
 321             GLASS_LOG_SEVERE("Error %s in setting 16 bits color key", strerror(errno));
 322         }
 323 
 324         struct mxcfb_gbl_alpha gbl_alpha;
 325         gbl_alpha.alpha = 255;
 326         gbl_alpha.enable = 1;
 327         if(ioctl(cursor.fd, MXCFB_SET_GBL_ALPHA, &gbl_alpha) < 0) {
 328               GLASS_LOG_SEVERE("Error %s in setting global alpha", strerror(errno));
 329         }
 330     }
 331 
 332     struct mxcfb_pos cpos = {(screenWidth - 16)/2, (screenHeight - 16)/2};
 333     if (ioctl(cursor.fd, MXCFB_SET_OVERLAY_POS, &cpos)) {
 334         GLASS_LOG_SEVERE("Error %s in setting overlay position", strerror(errno));
 335     }
 336 
 337     fbImx6BlankCursor();
 338 }
 339 
 340 
 341 
 342 static jlong fbImx6CreateNativeCursor(JNIEnv *env, jint x, jint y,  jbyte *srcArray, jint width, jint height) {
 343 
 344     Imx6CursorImage *cursorImage = (Imx6CursorImage *)malloc(sizeof(Imx6CursorImage));
 345     cursorImage->x = x;
 346     cursorImage->y = y;
 347     cursorImage->width = width;
 348     cursorImage->height = height;
 349     cursorImage->bufferSize = width * height * (use32bit ? 4 : 2);
 350     cursorImage->buffer = (jbyte*)malloc(cursorImage->bufferSize);
 351 
 352     GLASS_LOG_INFO("Creating x : %d y : %d width : %d height : %d cursor %d bits per pixel",x, y, width, height, (use32bit ? 32 : 16));
 353 
 354     if (use32bit) {
 355         memcpy((void*)(cursorImage->buffer), srcArray, cursorImage->bufferSize);
 356     } else {
 357         //565
 358         int i;
 359         uint16_t* dst = (uint16_t*)(cursorImage->buffer);
 360         uint32_t* src = (uint32_t*)srcArray;
 361         for (i = 0; i < cursorImage->bufferSize; i += 2) {
 362             int pixel = *src++;
 363             if ((pixel & 0xff000000) != 0) {
 364                 *dst++ = ((pixel >> 8) & 0xf800)
 365                          | ((pixel >> 5) & 0x7e0)
 366                          | ((pixel >> 3) & 0x1f);
 367             } else {
 368                 *dst++ = LENSFB_16_CURSOR_COLOR_KEY;
 369             }
 370         }
 371     }
 372 
 373     return ptr_to_jlong(cursorImage);
 374 }
 375 
 376 
 377 static void fbImx6ReleaseNativeCursor(jlong nativeCursorHandle) {
 378 
 379     Imx6CursorImage *cursorImage = (Imx6CursorImage *)jlong_to_ptr(nativeCursorHandle);
 380 
 381     if (cursorImage->buffer != NULL) {
 382         free(cursorImage->buffer);
 383     }
 384 
 385     free(cursorImage);
 386 }
 387 
 388 
 389 static void fbImx6SetNativeCursor(jlong nativeCursorHandle) {
 390 
 391     Imx6CursorImage *cursorImage = (Imx6CursorImage *)jlong_to_ptr(nativeCursorHandle);
 392 
 393     if (cursor.fd != -1 && cursor.currentCursor != nativeCursorHandle &&
 394         cursorImage != NULL && cursorImage->buffer != NULL)
 395     {
 396         if (cursorImage->width != cursor.width || cursorImage->height != cursor.height) {
 397             fbImx6BlankCursor();
 398             if (fbImx6ChangeCursorSize(cursorImage->width, cursorImage->height)) {
 399                 GLASS_LOG_SEVERE("Error in fbImx6ChangeCursorSize() w : %d h : %d", cursorImage->width, cursorImage->height);
 400                 return;
 401             }
 402         }
 403 
 404         cursor.currentCursor = nativeCursorHandle;
 405 
 406         fbImx6AdjustShift();
 407         fbImx6WriteCursor(cursor.fd, cursorImage->buffer, use32bit ? 4 : 2);
 408     }
 409 }
 410 
 411 
 412 
 413 static void fbImx6CursorSetPosition(int x, int y) {
 414     int xShift = cursor.xShift;
 415     int yShift = cursor.yShift;
 416 
 417     if (x < 0) {
 418         x = 0;
 419     }
 420     if (y < 0) {
 421         y = 0;
 422     }
 423     if (x > cursor.screenWidth - 1) {
 424         x = cursor.screenWidth - 1;
 425     }
 426     if (y > cursor.screenHeight - 1) {
 427         y = cursor.screenHeight - 1;
 428     }
 429 
 430     cursor.x = x;
 431     cursor.y = y;
 432 
 433     if (cursor.isVisible) {
 434 
 435         fbImx6AdjustShift();
 436         x -= cursor.xShift;
 437         y -= cursor.yShift;
 438         if (xShift != cursor.xShift || yShift != cursor.yShift) {
 439             GLASS_LOG_FINEST("Calling lseek to rewind cursor fd");
 440             Imx6CursorImage *fbCursorImage = (Imx6CursorImage *)
 441                 jlong_to_ptr(cursor.currentCursor);
 442             fbImx6WriteCursor(cursor.fd, fbCursorImage->buffer, use32bit ? 4 : 2);
 443         }
 444 
 445         struct mxcfb_pos cpos = {x, y};
 446         if (ioctl(cursor.fd, MXCFB_SET_OVERLAY_POS, &cpos)) {
 447             GLASS_LOG_SEVERE("Error %s in setting overlay position", strerror(errno));
 448         }
 449     }
 450 }
 451 
 452 
 453 void fbImx6CursorClose() {
 454     if (cursor.fd >= 0) {
 455         if (cursor.isVisible) {
 456             fbImx6BlankCursor();
 457         }
 458         close(cursor.fd);
 459         cursor.fd = -1;
 460         cursor.isVisible = 0;
 461         cursor.currentCursor = 0;
 462         cursor.width = 0;
 463         cursor.height = 0;
 464     }
 465 }
 466 
 467 static void fbImx6SetVisible(jboolean isVisible) {
 468     if (isVisible) {
 469         if (!cursor.isVisible && cursor.currentCursor != 0) {
 470             cursor.isVisible = 1;
 471             Imx6CursorImage *fbCursorImage = (Imx6CursorImage *)
 472                   jlong_to_ptr(cursor.currentCursor);
 473             fbImx6WriteCursor(cursor.fd, fbCursorImage->buffer, use32bit ? 4 : 2);
 474         }
 475     } else {
 476         if (cursor.isVisible) {
 477             fbImx6BlankCursor();
 478         }
 479         cursor.isVisible = 0;
 480     }
 481 }
 482 
 483 static char * platformName = "imx6";
 484 
 485 jboolean check_imx6_cursor(LensNativePort *lensPort) {
 486 
 487     if (access("/dev/mxc_vpu", F_OK) == 0) {
 488         lensPort->platformName = platformName;
 489         lensPort->setNativeCursor = fbImx6SetNativeCursor;
 490         lensPort->cursorInitialize = fbImx6CursorInitialize;
 491         lensPort->cursorSetPosition = fbImx6CursorSetPosition;
 492         lensPort->cursorClose = fbImx6CursorClose;
 493         lensPort->createNativeCursor = fbImx6CreateNativeCursor;
 494         lensPort->releaseNativeCursor = fbImx6ReleaseNativeCursor;
 495         lensPort->setVisible = fbImx6SetVisible;
 496         lensPort->cursorTranslucency = (use32bit ? JNI_TRUE : JNI_FALSE);
 497 
 498         return JNI_TRUE;
 499     }
 500 
 501     return JNI_FALSE;
 502 }
 503 
 504 #endif // IMX6_PLATFORM