summaryrefslogtreecommitdiffstats
path: root/camera/exynos_camera.c
diff options
context:
space:
mode:
Diffstat (limited to 'camera/exynos_camera.c')
-rw-r--r--camera/exynos_camera.c2865
1 files changed, 2865 insertions, 0 deletions
diff --git a/camera/exynos_camera.c b/camera/exynos_camera.c
new file mode 100644
index 0000000..c236c2e
--- /dev/null
+++ b/camera/exynos_camera.c
@@ -0,0 +1,2865 @@
+/*
+ * Copyright (C) 2013 Paul Kocialkowski
+ *
+ * Based on crespo libcamera and exynos4 hal libcamera:
+ * Copyright 2008, The Android Open Source Project
+ * Copyright 2010, Samsung Electronics Co. LTD
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <time.h>
+#include <errno.h>
+#include <malloc.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+
+#include <asm/types.h>
+#include <jpeg_api.h>
+
+#define LOG_TAG "exynos_camera"
+#include <utils/Log.h>
+#include <utils/Timers.h>
+
+#include "exynos_camera.h"
+
+/*
+ * Devices configurations
+ */
+
+struct exynos_camera_preset exynos_camera_presets_galaxynote[] = {
+ {
+ .name = "M5MO",
+ .facing = CAMERA_FACING_BACK,
+ .orientation = 90,
+ .rotation = 0,
+ .hflip = 0,
+ .vflip = 0,
+ .picture_format = V4L2_PIX_FMT_JPEG,
+ .focal_length = 4.03f,
+ .horizontal_view_angle = 60.5f,
+ .vertical_view_angle = 47.1f,
+ .metering = METERING_CENTER,
+ .params = {
+ .preview_size_values = "1280x720,640x480,720x480,800x480,800x450,352x288,320x240,176x144",
+ .preview_size = "640x480",
+ .preview_format_values = "yuv420sp,yuv420p,rgb565",
+ .preview_format = "yuv420sp",
+ .preview_frame_rate_values = "30,25,20,15,10,7",
+ .preview_frame_rate = 30,
+ .preview_fps_range_values = "(7000,30000)",
+ .preview_fps_range = "7000,30000",
+
+ .picture_size_values = "3264x2448,3264x1968,2048x1536,2048x1232,1280x960,800x480,640x480",
+ .picture_size = "3264x2448",
+ .picture_format_values = "jpeg",
+ .picture_format = "jpeg",
+ .jpeg_thumbnail_size_values = "320x240,400x240,0x0",
+ .jpeg_thumbnail_width = 320,
+ .jpeg_thumbnail_height = 240,
+ .jpeg_thumbnail_quality = 100,
+ .jpeg_quality = 90,
+
+ .recording_size = "720x480",
+ .recording_size_values = "1920x1080,1280x720,720x480,640x480",
+ .recording_format = "yuv420sp",
+
+ .focus_mode = "auto",
+ .focus_mode_values = "auto,infinity,macro,fixed,facedetect,continuous-video",
+ .focus_distances = "0.15,1.20,Infinity",
+ .focus_areas = "(0,0,0,0,0)",
+ .max_num_focus_areas = 1,
+
+ .zoom_supported = 1,
+ .smooth_zoom_supported = 0,
+ .zoom_ratios = "100,102,104,109,111,113,119,121,124,131,134,138,146,150,155,159,165,170,182,189,200,213,222,232,243,255,283,300,319,364,400",
+ .zoom = 0,
+ .max_zoom = 30,
+
+ .flash_mode = "off",
+ .flash_mode_values = "off,auto,on,torch",
+
+ .exposure_compensation = 0,
+ .exposure_compensation_step = 0.5,
+ .min_exposure_compensation = -4,
+ .max_exposure_compensation = 4,
+
+ .whitebalance = "auto",
+ .whitebalance_values = "auto,incandescent,fluorescent,daylight,cloudy-daylight",
+
+ .scene_mode = "auto",
+ .scene_mode_values = "auto,portrait,landscape,night,beach,snow,sunset,fireworks,sports,party,candlelight,dusk-dawn,fall-color,back-light,text",
+
+ .effect = "none",
+ .effect_values = "none,mono,negative,sepia,aqua",
+
+ .iso = "auto",
+ .iso_values = "auto,ISO50,ISO100,ISO200,ISO400,ISO800",
+ },
+ },
+ {
+ .name = "S5K5BAFX",
+ .facing = CAMERA_FACING_FRONT,
+ .orientation = 270,
+ .rotation = 0,
+ .hflip = 0,
+ .vflip = 0,
+ .picture_format = V4L2_PIX_FMT_YUYV,
+ .focal_length = 2.73f,
+ .horizontal_view_angle = 51.2f,
+ .vertical_view_angle = 39.4f,
+ .metering = METERING_CENTER,
+ .params = {
+ .preview_size_values = "640x480,352x288,320x240,176x144",
+ .preview_size = "640x480",
+ .preview_format_values = "yuv420sp,yuv420p,rgb565",
+ .preview_format = "yuv420sp",
+ .preview_frame_rate_values = "30,25,20,15,10,7",
+ .preview_frame_rate = 30,
+ .preview_fps_range_values = "(7000,30000)",
+ .preview_fps_range = "7000,30000",
+
+ .picture_size_values = "1600x1200,640x480",
+ .picture_size = "1600x1200",
+ .picture_format_values = "jpeg",
+ .picture_format = "jpeg",
+ .jpeg_thumbnail_size_values = "160x120,0x0",
+ .jpeg_thumbnail_width = 160,
+ .jpeg_thumbnail_height = 120,
+ .jpeg_thumbnail_quality = 100,
+ .jpeg_quality = 90,
+
+ .recording_size = "640x480",
+ .recording_size_values = "640x480",
+ .recording_format = "yuv420sp",
+
+ .focus_mode = "fixed",
+ .focus_mode_values = "fixed",
+ .focus_distances = "0.20,0.25,Infinity",
+ .focus_areas = NULL,
+ .max_num_focus_areas = 0,
+
+ .zoom_supported = 0,
+
+ .flash_mode = NULL,
+ .flash_mode_values = NULL,
+
+ .exposure_compensation = 0,
+ .exposure_compensation_step = 0.5,
+ .min_exposure_compensation = -4,
+ .max_exposure_compensation = 4,
+
+ .whitebalance = NULL,
+ .whitebalance_values = NULL,
+
+ .scene_mode = NULL,
+ .scene_mode_values = NULL,
+
+ .effect = NULL,
+ .effect_values = NULL,
+
+ .iso = "auto",
+ .iso_values = "auto",
+ },
+ },
+};
+
+struct exynos_v4l2_node exynos_v4l2_nodes_galaxynote[] = {
+ {
+ .id = 0,
+ .node = "/dev/video0",
+ },
+ {
+ .id = 1,
+ .node = "/dev/video1",
+ },
+ {
+ .id = 2,
+ .node = "/dev/video2",
+ },
+};
+
+struct exynox_camera_config exynos_camera_config_galaxynote = {
+ .presets = (struct exynos_camera_preset *) &exynos_camera_presets_galaxynote,
+ .presets_count = 2,
+ .v4l2_nodes = (struct exynos_v4l2_node *) &exynos_v4l2_nodes_galaxynote,
+ .v4l2_nodes_count = 3,
+};
+
+/*
+ * Exynos Camera
+ */
+
+struct exynox_camera_config *exynos_camera_config =
+ &exynos_camera_config_galaxynote;
+
+int exynos_camera_init(struct exynos_camera *exynos_camera, int id)
+{
+ char firmware_version[7] = { 0 };
+ struct exynos_v4l2_ext_control control;
+ int rc;
+
+ if (exynos_camera == NULL || id >= exynos_camera->config->presets_count)
+ return -EINVAL;
+
+ // Init FIMC1
+ rc = exynos_v4l2_open(exynos_camera, 0);
+ if (rc < 0) {
+ LOGE("Unable to open v4l2 device");
+ return -1;
+ }
+
+ rc = exynos_v4l2_querycap_cap(exynos_camera, 0);
+ if (rc < 0) {
+ LOGE("%s: querycap failed", __func__);
+ return -1;
+ }
+
+ rc = exynos_v4l2_enum_input(exynos_camera, 0, id);
+ if (rc < 0) {
+ LOGE("%s: enum input failed", __func__);
+ return -1;
+ }
+
+ rc = exynos_v4l2_s_input(exynos_camera, 0, id);
+ if (rc < 0) {
+ LOGE("%s: s input failed", __func__);
+ return -1;
+ }
+
+ // Init FIMC2
+ rc = exynos_v4l2_open(exynos_camera, 2);
+ if (rc < 0) {
+ LOGE("Unable to open v4l2 device");
+ return -1;
+ }
+
+ rc = exynos_v4l2_querycap_cap(exynos_camera, 2);
+ if (rc < 0) {
+ LOGE("%s: querycap failed", __func__);
+ return -1;
+ }
+
+ rc = exynos_v4l2_enum_input(exynos_camera, 2, id);
+ if (rc < 0) {
+ LOGE("%s: enum input failed", __func__);
+ return -1;
+ }
+
+ rc = exynos_v4l2_s_input(exynos_camera, 2, id);
+ if (rc < 0) {
+ LOGE("%s: s input failed", __func__);
+ return -1;
+ }
+
+ // Get firmware information
+ memset(&control, 0, sizeof(control));
+ control.id = V4L2_CID_CAM_SENSOR_FW_VER;
+ control.data.string = firmware_version;
+
+ rc = exynos_v4l2_g_ext_ctrls(exynos_camera, 0, (struct v4l2_ext_control *) &control, 1);
+ if (rc < 0) {
+ LOGE("%s: g ext ctrls failed", __func__);
+ } else {
+ LOGD("Firmware version: %s", firmware_version);
+ }
+
+ // Params
+ rc = exynos_camera_params_init(exynos_camera, id);
+ if (rc < 0)
+ LOGE("%s: Unable to init params", __func__);
+
+ // Gralloc
+ rc = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, (const struct hw_module_t **) &exynos_camera->gralloc);
+ if (rc)
+ LOGE("%s: Unable to get gralloc module", __func__);
+
+ return 0;
+}
+
+void exynos_camera_deinit(struct exynos_camera *exynos_camera)
+{
+ int i;
+ int id;
+
+ if (exynos_camera == NULL || exynos_camera->config == NULL)
+ return;
+
+ exynos_v4l2_close(exynos_camera, 0);
+ exynos_v4l2_close(exynos_camera, 2);
+}
+
+// Params
+
+int exynos_camera_params_init(struct exynos_camera *exynos_camera, int id)
+{
+ int rc;
+
+ if (exynos_camera == NULL || id >= exynos_camera->config->presets_count)
+ return -EINVAL;
+
+ // Camera params
+ exynos_camera->camera_rotation = exynos_camera->config->presets[id].rotation;
+ exynos_camera->camera_hflip = exynos_camera->config->presets[id].hflip;
+ exynos_camera->camera_vflip = exynos_camera->config->presets[id].vflip;
+ exynos_camera->camera_picture_format = exynos_camera->config->presets[id].picture_format;
+ exynos_camera->camera_focal_length = (int) (exynos_camera->config->presets[id].focal_length * 100);
+ exynos_camera->camera_metering = exynos_camera->config->presets[id].metering;
+
+ // Recording preview
+ exynos_param_string_set(exynos_camera, "preferred-preview-size-for-video",
+ exynos_camera->config->presets[id].params.preview_size);
+
+ // Preview
+ exynos_param_string_set(exynos_camera, "preview-size-values",
+ exynos_camera->config->presets[id].params.preview_size_values);
+ exynos_param_string_set(exynos_camera, "preview-size",
+ exynos_camera->config->presets[id].params.preview_size);
+ exynos_param_string_set(exynos_camera, "preview-format-values",
+ exynos_camera->config->presets[id].params.preview_format_values);
+ exynos_param_string_set(exynos_camera, "preview-format",
+ exynos_camera->config->presets[id].params.preview_format);
+ exynos_param_string_set(exynos_camera, "preview-frame-rate-values",
+ exynos_camera->config->presets[id].params.preview_frame_rate_values);
+ exynos_param_int_set(exynos_camera, "preview-frame-rate",
+ exynos_camera->config->presets[id].params.preview_frame_rate);
+ exynos_param_string_set(exynos_camera, "preview-fps-range-values",
+ exynos_camera->config->presets[id].params.preview_fps_range_values);
+ exynos_param_string_set(exynos_camera, "preview-fps-range",
+ exynos_camera->config->presets[id].params.preview_fps_range);
+
+ // Picture
+ exynos_param_string_set(exynos_camera, "picture-size-values",
+ exynos_camera->config->presets[id].params.picture_size_values);
+ exynos_param_string_set(exynos_camera, "picture-size",
+ exynos_camera->config->presets[id].params.picture_size);
+ exynos_param_string_set(exynos_camera, "picture-format-values",
+ exynos_camera->config->presets[id].params.picture_format_values);
+ exynos_param_string_set(exynos_camera, "picture-format",
+ exynos_camera->config->presets[id].params.picture_format);
+ exynos_param_string_set(exynos_camera, "jpeg-thumbnail-size-values",
+ exynos_camera->config->presets[id].params.jpeg_thumbnail_size_values);
+ exynos_param_int_set(exynos_camera, "jpeg-thumbnail-width",
+ exynos_camera->config->presets[id].params.jpeg_thumbnail_width);
+ exynos_param_int_set(exynos_camera, "jpeg-thumbnail-height",
+ exynos_camera->config->presets[id].params.jpeg_thumbnail_height);
+ exynos_param_int_set(exynos_camera, "jpeg-thumbnail-quality",
+ exynos_camera->config->presets[id].params.jpeg_thumbnail_quality);
+ exynos_param_int_set(exynos_camera, "jpeg-quality",
+ exynos_camera->config->presets[id].params.jpeg_quality);
+
+ // Recording
+ exynos_param_string_set(exynos_camera, "video-size",
+ exynos_camera->config->presets[id].params.recording_size);
+ exynos_param_string_set(exynos_camera, "video-size-values",
+ exynos_camera->config->presets[id].params.recording_size_values);
+ exynos_param_string_set(exynos_camera, "video-frame-format",
+ exynos_camera->config->presets[id].params.recording_format);
+
+ // Focus
+ exynos_param_string_set(exynos_camera, "focus-mode",
+ exynos_camera->config->presets[id].params.focus_mode);
+ exynos_param_string_set(exynos_camera, "focus-mode-values",
+ exynos_camera->config->presets[id].params.focus_mode_values);
+ exynos_param_string_set(exynos_camera, "focus-distances",
+ exynos_camera->config->presets[id].params.focus_distances);
+ if (exynos_camera->config->presets[id].params.max_num_focus_areas > 0) {
+ exynos_param_string_set(exynos_camera, "focus-areas",
+ exynos_camera->config->presets[id].params.focus_areas);
+ exynos_param_int_set(exynos_camera, "max-num-focus-areas",
+ exynos_camera->config->presets[id].params.max_num_focus_areas);
+ }
+
+ // Zoom
+ if (exynos_camera->config->presets[id].params.zoom_supported == 1) {
+ exynos_param_string_set(exynos_camera, "zoom-supported", "true");
+
+ if (exynos_camera->config->presets[id].params.smooth_zoom_supported == 1)
+ exynos_param_string_set(exynos_camera, "smooth-zoom-supported", "true");
+
+ if (exynos_camera->config->presets[id].params.zoom_ratios != NULL)
+ exynos_param_string_set(exynos_camera, "zoom-ratios", exynos_camera->config->presets[id].params.zoom_ratios);
+
+ exynos_param_int_set(exynos_camera, "zoom", exynos_camera->config->presets[id].params.zoom);
+ exynos_param_int_set(exynos_camera, "max-zoom", exynos_camera->config->presets[id].params.max_zoom);
+
+ } else {
+ exynos_param_string_set(exynos_camera, "zoom-supported", "false");
+ }
+
+ // Flash
+ exynos_param_string_set(exynos_camera, "flash-mode",
+ exynos_camera->config->presets[id].params.flash_mode);
+ exynos_param_string_set(exynos_camera, "flash-mode-values",
+ exynos_camera->config->presets[id].params.flash_mode_values);
+
+ // Exposure
+ exynos_param_int_set(exynos_camera, "exposure-compensation",
+ exynos_camera->config->presets[id].params.exposure_compensation);
+ exynos_param_float_set(exynos_camera, "exposure-compensation-step",
+ exynos_camera->config->presets[id].params.exposure_compensation_step);
+ exynos_param_int_set(exynos_camera, "min-exposure-compensation",
+ exynos_camera->config->presets[id].params.min_exposure_compensation);
+ exynos_param_int_set(exynos_camera, "max-exposure-compensation",
+ exynos_camera->config->presets[id].params.max_exposure_compensation);
+
+ // WB
+ exynos_param_string_set(exynos_camera, "whitebalance",
+ exynos_camera->config->presets[id].params.whitebalance);
+ exynos_param_string_set(exynos_camera, "whitebalance-values",
+ exynos_camera->config->presets[id].params.whitebalance_values);
+
+ // Scene mode
+ exynos_param_string_set(exynos_camera, "scene-mode",
+ exynos_camera->config->presets[id].params.scene_mode);
+ exynos_param_string_set(exynos_camera, "scene-mode-values",
+ exynos_camera->config->presets[id].params.scene_mode_values);
+
+ // Effect
+ exynos_param_string_set(exynos_camera, "effect",
+ exynos_camera->config->presets[id].params.effect);
+ exynos_param_string_set(exynos_camera, "effect-values",
+ exynos_camera->config->presets[id].params.effect_values);
+
+ // ISO
+ exynos_param_string_set(exynos_camera, "iso",
+ exynos_camera->config->presets[id].params.iso);
+ exynos_param_string_set(exynos_camera, "iso-values",
+ exynos_camera->config->presets[id].params.iso_values);
+
+ // Camera
+ exynos_param_float_set(exynos_camera, "focal-length",
+ exynos_camera->config->presets[id].focal_length);
+ exynos_param_float_set(exynos_camera, "horizontal-view-angle",
+ exynos_camera->config->presets[id].horizontal_view_angle);
+ exynos_param_float_set(exynos_camera, "vertical-view-angle",
+ exynos_camera->config->presets[id].vertical_view_angle);
+
+ rc = exynos_camera_params_apply(exynos_camera);
+ if (rc < 0) {
+ LOGE("%s: Unable to apply params", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int exynos_camera_params_apply(struct exynos_camera *exynos_camera)
+{
+ char *recording_hint_string;
+ char *recording_preview_size_string;
+
+ char *preview_size_string;
+ int preview_width = 0;
+ int preview_height = 0;
+ char *preview_format_string;
+ int preview_format;
+ float preview_format_bpp;
+ int preview_fps;
+
+ char *picture_size_string;
+ int picture_width = 0;
+ int picture_height = 0;
+ char *picture_format_string;
+ int picture_format;
+
+ int jpeg_thumbnail_width;
+ int jpeg_thumbnail_height;
+ int jpeg_thumbnail_quality;
+ int jpeg_quality;
+
+ char *video_size_string;
+ int recording_width = 0;
+ int recording_height = 0;
+ char *video_frame_format_string;
+ int recording_format;
+ int camera_sensor_mode;
+ int camera_sensor_output_size;
+
+ char *focus_mode_string;
+ int focus_mode = 0;
+ char *focus_areas_string;
+ int focus_left, focus_top, focus_right, focus_bottom, focus_weigth;
+ int focus_x;
+ int focus_y;
+
+ char *zoom_supported_string;
+ int zoom, max_zoom;
+
+ char *flash_mode_string;
+ int flash_mode;
+
+ int exposure_compensation;
+ int min_exposure_compensation;
+ int max_exposure_compensation;
+
+ char *whitebalance_string;
+ int whitebalance;
+
+ char *scene_mode_string;
+ int scene_mode;
+
+ char *effect_string;
+ int effect;
+
+ char *iso_string;
+ int iso;
+
+ int force = 0;
+
+ int w, h;
+ char *k;
+ int rc;
+
+ if (exynos_camera == NULL)
+ return -EINVAL;
+
+ if (!exynos_camera->preview_params_set) {
+ LOGE("%s: Setting preview params", __func__);
+ exynos_camera->preview_params_set = 1;
+ force = 1;
+ }
+
+ // Preview
+ preview_size_string = exynos_param_string_get(exynos_camera, "preview-size");
+ if (preview_size_string != NULL) {
+ sscanf(preview_size_string, "%dx%d", &preview_width, &preview_height);
+
+ if (preview_width != 0 && preview_width != exynos_camera->preview_width)
+ exynos_camera->preview_width = preview_width;
+ if (preview_height != 0 && preview_height != exynos_camera->preview_height)
+ exynos_camera->preview_height = preview_height;
+ }
+
+ preview_format_string = exynos_param_string_get(exynos_camera, "preview-format");
+ if (preview_format_string != NULL) {
+ if (strcmp(preview_format_string, "yuv420sp") == 0) {
+ preview_format = V4L2_PIX_FMT_NV21;
+ preview_format_bpp = 1.5f;
+ } else if (strcmp(preview_format_string, "yuv420p") == 0) {
+ preview_format = V4L2_PIX_FMT_YUV420;
+ preview_format_bpp = 1.5f;
+ } else if (strcmp(preview_format_string, "rgb565") == 0) {
+ preview_format = V4L2_PIX_FMT_RGB565;
+ preview_format_bpp = 2.0f;
+ } else if (strcmp(preview_format_string, "rgb8888") == 0) {
+ preview_format = V4L2_PIX_FMT_RGB32;
+ preview_format_bpp = 4.0f;
+ } else {
+ LOGE("%s: Unsupported preview format: %s", __func__, preview_format_string);
+ preview_format = V4L2_PIX_FMT_NV21;
+ preview_format_bpp = 1.5f;
+ }
+
+ if (preview_format != exynos_camera->preview_format) {
+ exynos_camera->preview_format = preview_format;
+ exynos_camera->preview_format_bpp = preview_format_bpp;
+ }
+ }
+
+ preview_fps = exynos_param_int_get(exynos_camera, "preview-frame-rate");
+ if (preview_fps > 0)
+ exynos_camera->preview_fps = preview_fps;
+ else
+ exynos_camera->preview_fps = 0;
+
+ // Picture
+ picture_size_string = exynos_param_string_get(exynos_camera, "picture-size");
+ if (picture_size_string != NULL) {
+ sscanf(picture_size_string, "%dx%d", &picture_width, &picture_height);
+
+ if (picture_width != 0 && picture_width != exynos_camera->picture_width)
+ exynos_camera->picture_width = picture_width;
+ if (picture_height != 0 && picture_height != exynos_camera->picture_height)
+ exynos_camera->picture_height = picture_height;
+ }
+
+ picture_format_string = exynos_param_string_get(exynos_camera, "picture-format");
+ if (picture_format_string != NULL) {
+ if (strcmp(picture_format_string, "jpeg") == 0) {
+ picture_format = V4L2_PIX_FMT_JPEG;
+ } else {
+ LOGE("%s: Unsupported picture format: %s", __func__, picture_format_string);
+ picture_format = V4L2_PIX_FMT_JPEG;
+ }
+
+ if (picture_format != exynos_camera->picture_format)
+ exynos_camera->picture_format = picture_format;
+ }
+
+ jpeg_thumbnail_width = exynos_param_int_get(exynos_camera, "jpeg-thumbnail-width");
+ if (jpeg_thumbnail_width > 0)
+ exynos_camera->jpeg_thumbnail_width = jpeg_thumbnail_width;
+
+ jpeg_thumbnail_height = exynos_param_int_get(exynos_camera, "jpeg-thumbnail-height");
+ if (jpeg_thumbnail_height > 0)
+ exynos_camera->jpeg_thumbnail_height = jpeg_thumbnail_height;
+
+ jpeg_thumbnail_quality = exynos_param_int_get(exynos_camera, "jpeg-thumbnail-quality");
+ if (jpeg_thumbnail_quality > 0)
+ exynos_camera->jpeg_thumbnail_quality = jpeg_thumbnail_quality;
+
+ jpeg_quality = exynos_param_int_get(exynos_camera, "jpeg-quality");
+ if (jpeg_quality <= 100 && jpeg_quality >= 0 && (jpeg_quality != exynos_camera->jpeg_quality || force)) {
+ exynos_camera->jpeg_quality = jpeg_quality;
+ rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_CAM_JPEG_QUALITY, jpeg_quality);
+ if (rc < 0)
+ LOGE("%s: s ctrl failed!", __func__);
+ }
+
+ // Recording
+ video_size_string = exynos_param_string_get(exynos_camera, "video-size");
+ if (video_size_string == NULL)
+ video_size_string = exynos_param_string_get(exynos_camera, "preview-size");
+
+ if (video_size_string != NULL) {
+ sscanf(video_size_string, "%dx%d", &recording_width, &recording_height);
+
+ if (recording_width != 0 && recording_width != exynos_camera->recording_width)
+ exynos_camera->recording_width = recording_width;
+ if (recording_height != 0 && recording_height != exynos_camera->recording_height)
+ exynos_camera->recording_height = recording_height;
+ }
+
+ video_frame_format_string = exynos_param_string_get(exynos_camera, "video-frame-format");
+ if (video_frame_format_string != NULL) {
+ if (strcmp(video_frame_format_string, "yuv420sp") == 0) {
+ recording_format = V4L2_PIX_FMT_NV12;
+ } else if (strcmp(video_frame_format_string, "yuv420p") == 0) {
+ recording_format = V4L2_PIX_FMT_YUV420;
+ } else if (strcmp(video_frame_format_string, "rgb565") == 0) {
+ recording_format = V4L2_PIX_FMT_RGB565;
+ } else if (strcmp(video_frame_format_string, "rgb8888") == 0) {
+ recording_format = V4L2_PIX_FMT_RGB32;
+ } else {
+ LOGE("%s: Unsupported recording format: %s", __func__, video_frame_format_string);
+ recording_format = V4L2_PIX_FMT_NV12;
+ }
+
+ if (recording_format != exynos_camera->recording_format)
+ exynos_camera->recording_format = recording_format;
+ }
+
+ recording_hint_string = exynos_param_string_get(exynos_camera, "recording-hint");
+ if (recording_hint_string != NULL && strcmp(recording_hint_string, "true") == 0) {
+ camera_sensor_mode = SENSOR_MOVIE;
+
+ k = exynos_param_string_get(exynos_camera, "preview-size-values");
+ while (recording_width != 0 && recording_height != 0) {
+ if (k == NULL)
+ break;
+
+ sscanf(k, "%dx%d", &w, &h);
+
+ // Look for same aspect ratio
+ if ((recording_width * h) / recording_height == w) {
+ preview_width = w;
+ preview_height = h;
+ break;
+ }
+
+ k = strchr(k, ',');
+ if (k == NULL)
+ break;
+
+ k++;
+ }
+
+ if (preview_width != 0 && preview_width != exynos_camera->preview_width)
+ exynos_camera->preview_width = preview_width;
+ if (preview_height != 0 && preview_height != exynos_camera->preview_height)
+ exynos_camera->preview_height = preview_height;
+
+ camera_sensor_output_size = ((recording_width & 0xffff) << 16) | (recording_height & 0xffff);
+ rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_CAMERA_SENSOR_OUTPUT_SIZE,
+ camera_sensor_output_size);
+ if (rc < 0)
+ LOGE("%s: s ctrl failed!", __func__);
+ } else {
+ camera_sensor_mode = SENSOR_CAMERA;
+ }
+
+ // Switching modes
+ if (camera_sensor_mode != exynos_camera->camera_sensor_mode) {
+ exynos_camera->camera_sensor_mode = camera_sensor_mode;
+ rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_CAMERA_SENSOR_MODE, camera_sensor_mode);
+ if (rc < 0)
+ LOGE("%s: s ctrl failed!", __func__);
+ }
+
+ // Focus
+ focus_areas_string = exynos_param_string_get(exynos_camera, "focus-areas");
+ if (focus_areas_string != NULL) {
+ focus_left = focus_top = focus_right = focus_bottom = focus_weigth = 0;
+
+ rc = sscanf(focus_areas_string, "(%d,%d,%d,%d,%d)",
+ &focus_left, &focus_top, &focus_right, &focus_bottom, &focus_weigth);
+ if (rc != 5) {
+ LOGE("%s: sscanf failed!", __func__);
+ } else if (focus_left != 0 && focus_top != 0 && focus_right != 0 && focus_bottom != 0) {
+ focus_x = (((focus_left + focus_right) / 2) + 1000) * preview_width / 2000;
+ focus_y = (((focus_top + focus_bottom) / 2) + 1000) * preview_height / 2000;
+
+ if (focus_x != exynos_camera->focus_x || force) {
+ exynos_camera->focus_x = focus_x;
+
+ rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_CAMERA_OBJECT_POSITION_X, focus_x);
+ if (rc < 0)
+ LOGE("%s: s ctrl failed!", __func__);
+ }
+
+ if (focus_y != exynos_camera->focus_y || force) {
+ exynos_camera->focus_y = focus_y;
+
+ rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_CAMERA_OBJECT_POSITION_Y, focus_y);
+ if (rc < 0)
+ LOGE("%s: s ctrl failed!", __func__);
+ }
+
+ focus_mode = FOCUS_MODE_TOUCH;
+ }
+ }
+
+ focus_mode_string = exynos_param_string_get(exynos_camera, "focus-mode");
+ if (focus_mode_string != NULL) {
+ if (focus_mode == 0) {
+ if (strcmp(focus_mode_string, "auto") == 0)
+ focus_mode = FOCUS_MODE_AUTO;
+ else if (strcmp(focus_mode_string, "infinity") == 0)
+ focus_mode = FOCUS_MODE_INFINITY;
+ else if (strcmp(focus_mode_string, "macro") == 0)
+ focus_mode = FOCUS_MODE_MACRO;
+ else if (strcmp(focus_mode_string, "fixed") == 0)
+ focus_mode = FOCUS_MODE_FIXED;
+ else if (strcmp(focus_mode_string, "facedetect") == 0)
+ focus_mode = FOCUS_MODE_FACEDETECT;
+ else if (strcmp(focus_mode_string, "continuous-video") == 0)
+ focus_mode = FOCUS_MODE_CONTINOUS;
+ else if (strcmp(focus_mode_string, "continuous-picture") == 0)
+ focus_mode = FOCUS_MODE_CONTINOUS;
+ else
+ focus_mode = FOCUS_MODE_AUTO;
+ }
+
+ if (focus_mode != exynos_camera->focus_mode || force) {
+ rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_CAMERA_FOCUS_MODE, focus_mode);
+ if (rc < 0)
+ LOGE("%s: s ctrl failed!", __func__);
+ }
+
+ if (focus_mode == FOCUS_MODE_TOUCH) {
+ rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_CAMERA_TOUCH_AF_START_STOP, 1);
+ if (rc < 0)
+ LOGE("%s: s ctrl failed!", __func__);
+ } else if (exynos_camera->focus_mode == FOCUS_MODE_TOUCH) {
+ rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_CAMERA_TOUCH_AF_START_STOP, 0);
+ if (rc < 0)
+ LOGE("%s: s ctrl failed!", __func__);
+ }
+
+ exynos_camera->focus_mode = focus_mode;
+ }
+
+ // Zoom
+ zoom_supported_string = exynos_param_string_get(exynos_camera, "zoom-supported");
+ if (zoom_supported_string != NULL && strcmp(zoom_supported_string, "true") == 0) {
+ zoom = exynos_param_int_get(exynos_camera, "zoom");
+ max_zoom = exynos_param_int_get(exynos_camera, "max-zoom");
+ if (zoom <= max_zoom && zoom >= 0 && (zoom != exynos_camera->zoom || force)) {
+ exynos_camera->zoom = zoom;
+ rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_CAMERA_ZOOM, zoom);
+ if (rc < 0)
+ LOGE("%s: s ctrl failed!", __func__);
+ }
+
+ }
+
+ // Flash
+ flash_mode_string = exynos_param_string_get(exynos_camera, "flash-mode");
+ if (flash_mode_string != NULL) {
+ if (strcmp(flash_mode_string, "off") == 0)
+ flash_mode = FLASH_MODE_OFF;
+ else if (strcmp(flash_mode_string, "auto") == 0)
+ flash_mode = FLASH_MODE_AUTO;
+ else if (strcmp(flash_mode_string, "on") == 0)
+ flash_mode = FLASH_MODE_ON;
+ else if (strcmp(flash_mode_string, "torch") == 0)
+ flash_mode = FLASH_MODE_TORCH;
+ else
+ flash_mode = FLASH_MODE_AUTO;
+
+ if (flash_mode != exynos_camera->flash_mode || force) {
+ exynos_camera->flash_mode = flash_mode;
+ rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_CAMERA_FLASH_MODE, flash_mode);
+ if (rc < 0)
+ LOGE("%s: s ctrl failed!", __func__);
+ }
+ }
+
+ // Exposure
+ exposure_compensation = exynos_param_int_get(exynos_camera, "exposure-compensation");
+ min_exposure_compensation = exynos_param_int_get(exynos_camera, "min-exposure-compensation");
+ max_exposure_compensation = exynos_param_int_get(exynos_camera, "max-exposure-compensation");
+
+ if (exposure_compensation <= max_exposure_compensation && exposure_compensation >= min_exposure_compensation &&
+ (exposure_compensation != exynos_camera->exposure_compensation || force)) {
+ exynos_camera->exposure_compensation = exposure_compensation;
+ rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_CAMERA_BRIGHTNESS, exposure_compensation);
+ if (rc < 0)
+ LOGE("%s: s ctrl failed!", __func__);
+ }
+
+ // WB
+ whitebalance_string = exynos_param_string_get(exynos_camera, "whitebalance");
+ if (whitebalance_string != NULL) {
+ if (strcmp(whitebalance_string, "auto") == 0)
+ whitebalance = WHITE_BALANCE_AUTO;
+ else if (strcmp(whitebalance_string, "incandescent") == 0)
+ whitebalance = WHITE_BALANCE_TUNGSTEN;
+ else if (strcmp(whitebalance_string, "fluorescent") == 0)
+ whitebalance = WHITE_BALANCE_FLUORESCENT;
+ else if (strcmp(whitebalance_string, "daylight") == 0)
+ whitebalance = WHITE_BALANCE_SUNNY;
+ else if (strcmp(whitebalance_string, "cloudy-daylight") == 0)
+ whitebalance = WHITE_BALANCE_CLOUDY;
+ else
+ whitebalance = WHITE_BALANCE_AUTO;
+
+ if (whitebalance != exynos_camera->whitebalance || force) {
+ exynos_camera->whitebalance = whitebalance;
+ rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_CAMERA_WHITE_BALANCE, whitebalance);
+ if (rc < 0)
+ LOGE("%s: s ctrl failed!", __func__);
+ }
+ }
+
+ // Scene mode
+ scene_mode_string = exynos_param_string_get(exynos_camera, "scene-mode");
+ if (scene_mode_string != NULL) {
+ if (strcmp(scene_mode_string, "auto") == 0)
+ scene_mode = SCENE_MODE_NONE;
+ else if (strcmp(scene_mode_string, "portrait") == 0)
+ scene_mode = SCENE_MODE_PORTRAIT;
+ else if (strcmp(scene_mode_string, "landscape") == 0)
+ scene_mode = SCENE_MODE_LANDSCAPE;
+ else if (strcmp(scene_mode_string, "night") == 0)
+ scene_mode = SCENE_MODE_NIGHTSHOT;
+ else if (strcmp(scene_mode_string, "beach") == 0)
+ scene_mode = SCENE_MODE_BEACH_SNOW;
+ else if (strcmp(scene_mode_string, "snow") == 0)
+ scene_mode = SCENE_MODE_BEACH_SNOW;
+ else if (strcmp(scene_mode_string, "sunset") == 0)
+ scene_mode = SCENE_MODE_SUNSET;
+ else if (strcmp(scene_mode_string, "fireworks") == 0)
+ scene_mode = SCENE_MODE_FIREWORKS;
+ else if (strcmp(scene_mode_string, "sports") == 0)
+ scene_mode = SCENE_MODE_SPORTS;
+ else if (strcmp(scene_mode_string, "party") == 0)
+ scene_mode = SCENE_MODE_PARTY_INDOOR;
+ else if (strcmp(scene_mode_string, "candlelight") == 0)
+ scene_mode = SCENE_MODE_CANDLE_LIGHT;
+ else if (strcmp(scene_mode_string, "dusk-dawn") == 0)
+ scene_mode = SCENE_MODE_DUSK_DAWN;
+ else if (strcmp(scene_mode_string, "fall-color") == 0)
+ scene_mode = SCENE_MODE_FALL_COLOR;
+ else if (strcmp(scene_mode_string, "back-light") == 0)
+ scene_mode = SCENE_MODE_BACK_LIGHT;
+ else if (strcmp(scene_mode_string, "text") == 0)
+ scene_mode = SCENE_MODE_TEXT;
+ else
+ scene_mode = SCENE_MODE_NONE;
+
+ if (scene_mode != exynos_camera->scene_mode || force) {
+ exynos_camera->scene_mode = scene_mode;
+ rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_CAMERA_SCENE_MODE, scene_mode);
+ if (rc < 0)
+ LOGE("%s: s ctrl failed!", __func__);
+ }
+ }
+
+ // Effect
+ effect_string = exynos_param_string_get(exynos_camera, "effect");
+ if (effect_string != NULL) {
+ if (strcmp(effect_string, "auto") == 0)
+ effect = IMAGE_EFFECT_NONE;
+ else if (strcmp(effect_string, "mono") == 0)
+ effect = IMAGE_EFFECT_BNW;
+ else if (strcmp(effect_string, "negative") == 0)
+ effect = IMAGE_EFFECT_NEGATIVE;
+ else if (strcmp(effect_string, "sepia") == 0)
+ effect = IMAGE_EFFECT_SEPIA;
+ else if (strcmp(effect_string, "aqua") == 0)
+ effect = IMAGE_EFFECT_AQUA;
+ else
+ effect = IMAGE_EFFECT_NONE;
+
+ if (effect != exynos_camera->effect || force) {
+ exynos_camera->effect = effect;
+ rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_CAMERA_EFFECT, effect);
+ if (rc < 0)
+ LOGE("%s: s ctrl failed!", __func__);
+ }
+ }
+
+ // ISO
+ iso_string = exynos_param_string_get(exynos_camera, "iso");
+ if (iso_string != NULL) {
+ if (strcmp(iso_string, "auto") == 0)
+ iso = ISO_AUTO;
+ else if (strcmp(iso_string, "ISO50") == 0)
+ iso = ISO_50;
+ else if (strcmp(iso_string, "ISO100") == 0)
+ iso = ISO_100;
+ else if (strcmp(iso_string, "ISO200") == 0)
+ iso = ISO_200;
+ else if (strcmp(iso_string, "ISO400") == 0)
+ iso = ISO_400;
+ else if (strcmp(iso_string, "ISO800") == 0)
+ iso = ISO_800;
+ else
+ iso = ISO_AUTO;
+
+ if (iso != exynos_camera->iso || force) {
+ exynos_camera->iso = iso;
+ rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_CAMERA_ISO, iso);
+ if (rc < 0)
+ LOGE("%s: s ctrl failed!", __func__);
+ }
+ }
+
+ LOGD("%s: Preview size: %dx%d, picture size: %dx%d, recording size: %dx%d",
+ __func__, preview_width, preview_height, picture_width, picture_height,
+ recording_width, recording_height);
+
+ return 0;
+}
+
+// Picture
+
+int exynos_camera_picture(struct exynos_camera *exynos_camera)
+{
+ camera_memory_t *data_memory = NULL;
+ camera_memory_t *exif_data_memory = NULL;
+ camera_memory_t *picture_data_memory = NULL;
+ camera_memory_t *jpeg_thumbnail_data_memory = NULL;
+
+ int camera_picture_format;
+ int picture_width;
+ int picture_height;
+ int picture_format;
+
+ int jpeg_thumbnail_width;
+ int jpeg_thumbnail_height;
+ int jpeg_thumbnail_quality;
+ int jpeg_quality;
+
+ int data_size;
+
+ int offset = 0;
+ void *picture_addr = NULL;
+ int picture_size = 0;
+ void *jpeg_thumbnail_addr = NULL;
+ int jpeg_thumbnail_size = 0;
+
+ int jpeg_fd;
+ struct jpeg_enc_param jpeg_enc_params;
+ enum jpeg_frame_format jpeg_in_format;
+ enum jpeg_stream_format jpeg_out_format;
+ enum jpeg_ret_type jpeg_result;
+ void *jpeg_in_buffer;
+ int jpeg_in_size;
+ void *jpeg_out_buffer;
+ int jpeg_out_size;
+
+ exif_attribute_t exif_attributes;
+ int exif_size = 0;
+
+ int index;
+ int rc;
+
+ if (exynos_camera == NULL)
+ return -EINVAL;
+
+ picture_width = exynos_camera->picture_width;
+ picture_height = exynos_camera->picture_height;
+ picture_format = exynos_camera->picture_format;
+ camera_picture_format = exynos_camera->camera_picture_format;
+ jpeg_thumbnail_width = exynos_camera->jpeg_thumbnail_width;
+ jpeg_thumbnail_height = exynos_camera->jpeg_thumbnail_height;
+ jpeg_thumbnail_quality = exynos_camera->jpeg_thumbnail_quality;
+ jpeg_quality = exynos_camera->jpeg_quality;
+
+ if (camera_picture_format == 0)
+ camera_picture_format = picture_format;
+
+ // V4L2
+
+ rc = exynos_v4l2_poll(exynos_camera, 0);
+ if (rc < 0) {
+ LOGE("%s: poll failed!", __func__);
+ return -1;
+ } else if (rc == 0) {
+ LOGE("%s: poll timeout!", __func__);
+ return -1;
+ }
+
+ rc = exynos_v4l2_streamoff_cap(exynos_camera, 0);
+ if (rc < 0) {
+ LOGE("%s: streamoff failed!", __func__);
+ return -1;
+ }
+
+ index = exynos_v4l2_dqbuf_cap(exynos_camera, 0);
+ if (index < 0) {
+ LOGE("%s: dqbuf failed!", __func__);
+ return -1;
+ }
+
+ // This assumes that the output format is JPEG
+
+ if (camera_picture_format == V4L2_PIX_FMT_JPEG) {
+ rc = exynos_v4l2_g_ctrl(exynos_camera, 0, V4L2_CID_CAM_JPEG_MAIN_SIZE,
+ &picture_size);
+ if (rc < 0) {
+ LOGE("%s: g ctrl failed!", __func__);
+ return -1;
+ }
+
+ rc = exynos_v4l2_g_ctrl(exynos_camera, 0, V4L2_CID_CAM_JPEG_MAIN_OFFSET,
+ &offset);
+ if (rc < 0) {
+ LOGE("%s: g ctrl failed!", __func__);
+ return -1;
+ }
+
+ picture_addr = (void *) ((int) exynos_camera->picture_memory->data + offset);
+
+ rc = exynos_v4l2_g_ctrl(exynos_camera, 0, V4L2_CID_CAM_JPEG_THUMB_SIZE,
+ &jpeg_thumbnail_size);
+ if (rc < 0) {
+ LOGE("%s: g ctrl failed!", __func__);
+ return -1;
+ }
+
+ rc = exynos_v4l2_g_ctrl(exynos_camera, 0, V4L2_CID_CAM_JPEG_THUMB_OFFSET,
+ &offset);
+ if (rc < 0) {
+ LOGE("%s: g ctrl failed!", __func__);
+ return -1;
+ }
+
+ jpeg_thumbnail_addr = (void *) ((int) exynos_camera->picture_memory->data + offset);
+ }
+
+ // Thumbnail
+
+ if (camera_picture_format == V4L2_PIX_FMT_JPEG && jpeg_thumbnail_addr != NULL && jpeg_thumbnail_size >= 0) {
+ if (exynos_camera->callbacks.request_memory != NULL) {
+ jpeg_thumbnail_data_memory =
+ exynos_camera->callbacks.request_memory(-1,
+ jpeg_thumbnail_size, 1, 0);
+ if (jpeg_thumbnail_data_memory == NULL) {
+ LOGE("%s: thumb memory request failed!", __func__);
+ goto error;
+ }
+ } else {
+ LOGE("%s: No memory request function!", __func__);
+ goto error;
+ }
+
+ memcpy(jpeg_thumbnail_data_memory->data, jpeg_thumbnail_addr, jpeg_thumbnail_size);
+ } else {
+ jpeg_fd = api_jpeg_encode_init();
+ if (jpeg_fd < 0) {
+ LOGE("%s: Failed to init JPEG", __func__);
+ goto error;
+ }
+
+ switch (camera_picture_format) {
+ case V4L2_PIX_FMT_RGB565:
+ jpeg_in_format = RGB_565;
+ jpeg_out_format = JPEG_420;
+ jpeg_in_size = (picture_width * picture_height * 2);
+ break;
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV12T:
+ case V4L2_PIX_FMT_YUV420:
+ jpeg_in_format = YUV_420;
+ jpeg_out_format = JPEG_420;
+ jpeg_in_size = (picture_width * picture_height * 1.5);
+ break;
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_YUV422P:
+ default:
+ jpeg_in_format = YUV_422;
+ jpeg_out_format = JPEG_422;
+ jpeg_in_size = (picture_width * picture_height * 2);
+ break;
+ }
+
+ memset(&jpeg_enc_params, 0, sizeof(jpeg_enc_params));
+
+ jpeg_enc_params.width = jpeg_thumbnail_width;
+ jpeg_enc_params.height = jpeg_thumbnail_height;
+ jpeg_enc_params.in_fmt = jpeg_in_format;
+ jpeg_enc_params.out_fmt = jpeg_out_format;
+
+ if (jpeg_thumbnail_quality >= 90)
+ jpeg_enc_params.quality = QUALITY_LEVEL_1;
+ else if (jpeg_thumbnail_quality >= 80)
+ jpeg_enc_params.quality = QUALITY_LEVEL_2;
+ else if (jpeg_thumbnail_quality >= 70)
+ jpeg_enc_params.quality = QUALITY_LEVEL_3;
+ else
+ jpeg_enc_params.quality = QUALITY_LEVEL_4;
+
+ api_jpeg_set_encode_param(&jpeg_enc_params);
+
+ jpeg_in_buffer = api_jpeg_get_encode_in_buf(jpeg_fd, jpeg_in_size);
+ if (jpeg_in_buffer == NULL) {
+ LOGE("%s: Failed to get JPEG in buffer", __func__);
+ api_jpeg_encode_deinit(jpeg_fd);
+ goto error;
+ }
+
+ jpeg_out_buffer = api_jpeg_get_encode_out_buf(jpeg_fd);
+ if (jpeg_out_buffer == NULL) {
+ LOGE("%s: Failed to get JPEG out buffer", __func__);
+ api_jpeg_encode_deinit(jpeg_fd);
+ goto error;
+ }
+
+ memcpy(jpeg_in_buffer, exynos_camera->picture_memory->data, jpeg_in_size);
+
+ jpeg_result = api_jpeg_encode_exe(jpeg_fd, &jpeg_enc_params);
+ if (jpeg_result != JPEG_ENCODE_OK) {
+ LOGE("%s: Failed to encode JPEG", __func__);
+ api_jpeg_encode_deinit(jpeg_fd);
+ goto error;
+ }
+
+ jpeg_out_size = jpeg_enc_params.size;
+ if (jpeg_out_size <= 0) {
+ LOGE("%s: Failed to get JPEG out size", __func__);
+ api_jpeg_encode_deinit(jpeg_fd);
+ goto error;
+ }
+
+ if (exynos_camera->callbacks.request_memory != NULL) {
+ jpeg_thumbnail_data_memory =
+ exynos_camera->callbacks.request_memory(-1,
+ jpeg_out_size, 1, 0);
+ if (jpeg_thumbnail_data_memory == NULL) {
+ LOGE("%s: thumbnail memory request failed!", __func__);
+ api_jpeg_encode_deinit(jpeg_fd);
+ goto error;
+ }
+ } else {
+ LOGE("%s: No memory request function!", __func__);
+ api_jpeg_encode_deinit(jpeg_fd);
+ goto error;
+ }
+
+ memcpy(jpeg_thumbnail_data_memory->data, jpeg_out_buffer, jpeg_out_size);
+ jpeg_thumbnail_size = jpeg_out_size;
+
+ api_jpeg_encode_deinit(jpeg_fd);
+ }
+
+ // Picture
+
+ if (camera_picture_format == V4L2_PIX_FMT_JPEG && picture_addr != NULL && picture_size >= 0) {
+ if (exynos_camera->callbacks.request_memory != NULL) {
+ picture_data_memory =
+ exynos_camera->callbacks.request_memory(-1,
+ picture_size, 1, 0);
+ if (picture_data_memory == NULL) {
+ LOGE("%s: picture memory request failed!", __func__);
+ goto error;
+ }
+ } else {
+ LOGE("%s: No memory request function!", __func__);
+ goto error;
+ }
+
+ memcpy(picture_data_memory->data, picture_addr, picture_size);
+ } else {
+ jpeg_fd = api_jpeg_encode_init();
+ if (jpeg_fd < 0) {
+ LOGE("%s: Failed to init JPEG", __func__);
+ goto error;
+ }
+
+ switch (camera_picture_format) {
+ case V4L2_PIX_FMT_RGB565:
+ jpeg_in_format = RGB_565;
+ jpeg_out_format = JPEG_420;
+ jpeg_in_size = (picture_width * picture_height * 2);
+ break;
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV12T:
+ case V4L2_PIX_FMT_YUV420:
+ jpeg_in_format = YUV_420;
+ jpeg_out_format = JPEG_420;
+ jpeg_in_size = (picture_width * picture_height * 1.5);
+ break;
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_YUV422P:
+ default:
+ jpeg_in_format = YUV_422;
+ jpeg_out_format = JPEG_422;
+ jpeg_in_size = (picture_width * picture_height * 2);
+ break;
+ }
+
+ memset(&jpeg_enc_params, 0, sizeof(jpeg_enc_params));
+
+ jpeg_enc_params.width = picture_width;
+ jpeg_enc_params.height = picture_height;
+ jpeg_enc_params.in_fmt = jpeg_in_format;
+ jpeg_enc_params.out_fmt = jpeg_out_format;
+
+ if (jpeg_quality >= 90)
+ jpeg_enc_params.quality = QUALITY_LEVEL_1;
+ else if (jpeg_quality >= 80)
+ jpeg_enc_params.quality = QUALITY_LEVEL_2;
+ else if (jpeg_quality >= 70)
+ jpeg_enc_params.quality = QUALITY_LEVEL_3;
+ else
+ jpeg_enc_params.quality = QUALITY_LEVEL_4;
+
+ api_jpeg_set_encode_param(&jpeg_enc_params);
+
+ jpeg_in_buffer = api_jpeg_get_encode_in_buf(jpeg_fd, jpeg_in_size);
+ if (jpeg_in_buffer == NULL) {
+ LOGE("%s: Failed to get JPEG in buffer", __func__);
+ api_jpeg_encode_deinit(jpeg_fd);
+ goto error;
+ }
+
+ jpeg_out_buffer = api_jpeg_get_encode_out_buf(jpeg_fd);
+ if (jpeg_out_buffer == NULL) {
+ LOGE("%s: Failed to get JPEG out buffer", __func__);
+ api_jpeg_encode_deinit(jpeg_fd);
+ goto error;
+ }
+
+ memcpy(jpeg_in_buffer, exynos_camera->picture_memory->data, jpeg_in_size);
+
+ jpeg_result = api_jpeg_encode_exe(jpeg_fd, &jpeg_enc_params);
+ if (jpeg_result != JPEG_ENCODE_OK) {
+ LOGE("%s: Failed to encode JPEG", __func__);
+ api_jpeg_encode_deinit(jpeg_fd);
+ goto error;
+ }
+
+ jpeg_out_size = jpeg_enc_params.size;
+ if (jpeg_out_size <= 0) {
+ LOGE("%s: Failed to get JPEG out size", __func__);
+ api_jpeg_encode_deinit(jpeg_fd);
+ goto error;
+ }
+
+ if (exynos_camera->callbacks.request_memory != NULL) {
+ picture_data_memory =
+ exynos_camera->callbacks.request_memory(-1,
+ jpeg_out_size, 1, 0);
+ if (picture_data_memory == NULL) {
+ LOGE("%s: picture memory request failed!", __func__);
+ api_jpeg_encode_deinit(jpeg_fd);
+ goto error;
+ }
+ } else {
+ LOGE("%s: No memory request function!", __func__);
+ api_jpeg_encode_deinit(jpeg_fd);
+ goto error;
+ }
+
+ memcpy(picture_data_memory->data, jpeg_out_buffer, jpeg_out_size);
+ picture_size = jpeg_out_size;
+
+ api_jpeg_encode_deinit(jpeg_fd);
+ }
+
+ // EXIF
+
+ memset(&exif_attributes, 0, sizeof(exif_attributes));
+ exynos_exif_attributes_create_static(exynos_camera, &exif_attributes);
+ exynos_exif_attributes_create_params(exynos_camera, &exif_attributes);
+
+ rc = exynos_exif_create(exynos_camera, &exif_attributes,
+ jpeg_thumbnail_data_memory, jpeg_thumbnail_size,
+ &exif_data_memory, &exif_size);
+ if (rc < 0 || exif_data_memory == NULL || exif_size <= 0) {
+ LOGE("%s: EXIF create failed!", __func__);
+ goto error;
+ }
+
+ data_size = exif_size + picture_size;
+
+ if (exynos_camera->callbacks.request_memory != NULL) {
+ data_memory =
+ exynos_camera->callbacks.request_memory(-1,
+ data_size, 1, 0);
+ if (data_memory == NULL) {
+ LOGE("%s: data memory request failed!", __func__);
+ goto error;
+ }
+ } else {
+ LOGE("%s: No memory request function!", __func__);
+ goto error;
+ }
+
+ // Copy the first two bytes of the JPEG picture
+ memcpy(data_memory->data, picture_data_memory->data, 2);
+
+ // Copy the EXIF data
+ memcpy((void *) ((int) data_memory->data + 2), exif_data_memory->data,
+ exif_size);
+
+ // Copy the JPEG picture
+ memcpy((void *) ((int) data_memory->data + 2 + exif_size),
+ (void *) ((int) picture_data_memory->data + 2), picture_size - 2);
+
+ // Callbacks
+
+ if (EXYNOS_CAMERA_MSG_ENABLED(CAMERA_MSG_SHUTTER) && EXYNOS_CAMERA_CALLBACK_DEFINED(notify))
+ exynos_camera->callbacks.notify(CAMERA_MSG_SHUTTER, 0, 0,
+ exynos_camera->callbacks.user);
+
+ if (EXYNOS_CAMERA_MSG_ENABLED(CAMERA_MSG_RAW_IMAGE) && EXYNOS_CAMERA_CALLBACK_DEFINED(data) &&
+ jpeg_thumbnail_data_memory != NULL)
+ exynos_camera->callbacks.data(CAMERA_MSG_RAW_IMAGE,
+ jpeg_thumbnail_data_memory, 0, NULL, exynos_camera->callbacks.user);
+
+ if (EXYNOS_CAMERA_MSG_ENABLED(CAMERA_MSG_COMPRESSED_IMAGE) && EXYNOS_CAMERA_CALLBACK_DEFINED(data) &&
+ data_memory != NULL)
+ exynos_camera->callbacks.data(CAMERA_MSG_COMPRESSED_IMAGE,
+ data_memory, 0, NULL, exynos_camera->callbacks.user);
+
+ // Release memory
+
+ if (jpeg_thumbnail_data_memory != NULL && jpeg_thumbnail_data_memory->release != NULL)
+ jpeg_thumbnail_data_memory->release(jpeg_thumbnail_data_memory);
+
+ if (picture_data_memory != NULL && picture_data_memory->release != NULL)
+ picture_data_memory->release(picture_data_memory);
+
+ if (exif_data_memory != NULL && exif_data_memory->release != NULL)
+ exif_data_memory->release(exif_data_memory);
+
+ if (data_memory != NULL && data_memory->release != NULL)
+ data_memory->release(data_memory);
+
+ return 0;
+
+error:
+ if (jpeg_thumbnail_data_memory != NULL && jpeg_thumbnail_data_memory->release != NULL)
+ jpeg_thumbnail_data_memory->release(jpeg_thumbnail_data_memory);
+
+ if (picture_data_memory != NULL && picture_data_memory->release != NULL)
+ picture_data_memory->release(picture_data_memory);
+
+ if (exif_data_memory != NULL && exif_data_memory->release != NULL)
+ exif_data_memory->release(exif_data_memory);
+
+ if (data_memory != NULL && data_memory->release != NULL)
+ data_memory->release(data_memory);
+
+ return -1;
+}
+
+void *exynos_camera_picture_thread(void *data)
+{
+ struct exynos_camera *exynos_camera;
+ int rc;
+ int i;
+
+ if (data == NULL)
+ return NULL;
+
+ exynos_camera = (struct exynos_camera *) data;
+
+ LOGE("%s: Starting thread", __func__);
+ exynos_camera->picture_thread_running = 1;
+
+ if (exynos_camera->picture_enabled == 1) {
+ pthread_mutex_lock(&exynos_camera->picture_mutex);
+
+ rc = exynos_camera_picture(exynos_camera);
+ if (rc < 0) {
+ LOGE("%s: picture failed!", __func__);
+ exynos_camera->picture_enabled = 0;
+ }
+
+ if (exynos_camera->picture_memory != NULL && exynos_camera->picture_memory->release != NULL) {
+ exynos_camera->picture_memory->release(exynos_camera->picture_memory);
+ exynos_camera->picture_memory = NULL;
+ }
+
+ pthread_mutex_unlock(&exynos_camera->picture_mutex);
+ }
+
+ exynos_camera->picture_thread_running = 0;
+ exynos_camera->picture_enabled = 0;
+
+ LOGE("%s: Exiting thread", __func__);
+
+ return NULL;
+}
+
+int exynos_camera_picture_start(struct exynos_camera *exynos_camera)
+{
+ pthread_attr_t thread_attr;
+
+ int width, height, format, camera_format;
+
+ int fd;
+ int rc;
+
+ if (exynos_camera == NULL)
+ return -EINVAL;
+
+ // Stop preview thread
+ exynos_camera_preview_stop(exynos_camera);
+
+ width = exynos_camera->picture_width;
+ height = exynos_camera->picture_height;
+ format = exynos_camera->picture_format;
+ camera_format = exynos_camera->camera_picture_format;
+
+ // V4L2
+
+ if (camera_format == 0)
+ camera_format = format;
+
+ rc = exynos_v4l2_enum_fmt_cap(exynos_camera, 0, camera_format);
+ if (rc < 0) {
+ LOGE("%s: enum fmt failed!", __func__);
+ return -1;
+ }
+
+ rc = exynos_v4l2_s_fmt_pix_cap(exynos_camera, 0, width, height, camera_format, V4L2_PIX_FMT_MODE_CAPTURE);
+ if (rc < 0) {
+ LOGE("%s: s fmt failed!", __func__);
+ return -1;
+ }
+
+ // Only use 1 buffer
+ rc = exynos_v4l2_reqbufs_cap(exynos_camera, 0, 1);
+ if (rc < 0) {
+ LOGE("%s: reqbufs failed!", __func__);
+ return -1;
+ }
+
+ rc = exynos_v4l2_querybuf_cap(exynos_camera, 0, 0);
+ if (rc < 0) {
+ LOGE("%s: querybuf failed!", __func__);
+ return -1;
+ }
+
+ exynos_camera->picture_buffer_length = rc;
+
+ if (exynos_camera->callbacks.request_memory != NULL) {
+ fd = exynos_v4l2_find_fd(exynos_camera, 0);
+ if (fd < 0) {
+ LOGE("%s: Unable to find v4l2 fd", __func__);
+ return -1;
+ }
+
+ if (exynos_camera->picture_memory != NULL && exynos_camera->picture_memory->release != NULL)
+ exynos_camera->picture_memory->release(exynos_camera->picture_memory);
+
+ exynos_camera->picture_memory =
+ exynos_camera->callbacks.request_memory(fd,
+ exynos_camera->picture_buffer_length, 1, 0);
+ if (exynos_camera->picture_memory == NULL) {
+ LOGE("%s: memory request failed!", __func__);
+ return -1;
+ }
+ } else {
+ LOGE("%s: No memory request function!", __func__);
+ return -1;
+ }
+
+ rc = exynos_v4l2_qbuf_cap(exynos_camera, 0, 0);
+ if (rc < 0) {
+ LOGE("%s: qbuf failed!", __func__);
+ return -1;
+ }
+
+ rc = exynos_v4l2_streamon_cap(exynos_camera, 0);
+ if (rc < 0) {
+ LOGE("%s: streamon failed!", __func__);
+ return -1;
+ }
+
+ rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_CAMERA_CAPTURE, 0);
+ if (rc < 0) {
+ LOGE("%s: s ctrl failed!", __func__);
+ return -1;
+ }
+
+ // Thread
+
+ if (exynos_camera->picture_thread_running) {
+ LOGE("Picture thread is already running!");
+ return -1;
+ }
+
+ pthread_mutex_init(&exynos_camera->picture_mutex, NULL);
+
+ pthread_attr_init(&thread_attr);
+ pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
+
+ exynos_camera->picture_enabled = 1;
+
+ rc = pthread_create(&exynos_camera->picture_thread, &thread_attr,
+ exynos_camera_picture_thread, (void *) exynos_camera);
+ if (rc < 0) {
+ LOGE("%s: Unable to create thread", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+void exynos_camera_picture_stop(struct exynos_camera *exynos_camera)
+{
+ int rc;
+ int i;
+
+ if (exynos_camera == NULL)
+ return;
+
+ if (!exynos_camera->picture_enabled) {
+ LOGE("Picture was already stopped!");
+ return;
+ }
+
+ pthread_mutex_lock(&exynos_camera->picture_mutex);
+
+ // Disable picture to make the thread end
+ exynos_camera->picture_enabled = 0;
+
+ pthread_mutex_unlock(&exynos_camera->picture_mutex);
+
+ // Wait for the thread to end
+ for (i = 0; i < 10; i++) {
+ if (!exynos_camera->picture_thread_running)
+ break;
+
+ usleep(500);
+ }
+
+ pthread_mutex_destroy(&exynos_camera->picture_mutex);
+}
+
+// Auto-focus
+
+void *exynos_camera_auto_focus_thread(void *data)
+{
+ struct exynos_camera *exynos_camera;
+ int auto_focus_status = -1;
+ int auto_focus_result = 0;
+ int rc;
+ int i;
+
+ if (data == NULL)
+ return NULL;
+
+ exynos_camera = (struct exynos_camera *) data;
+
+ LOGE("%s: Starting thread", __func__);
+ exynos_camera->auto_focus_thread_running = 1;
+
+ rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_CAMERA_SET_AUTO_FOCUS, AUTO_FOCUS_ON);
+ if (rc < 0) {
+ LOGE("%s: s ctrl failed!", __func__);
+ auto_focus_result = 0;
+ goto thread_exit;
+ }
+
+ while (exynos_camera->auto_focus_enabled == 1) {
+ pthread_mutex_lock(&exynos_camera->auto_focus_mutex);
+
+ rc = exynos_v4l2_g_ctrl(exynos_camera, 0, V4L2_CID_CAMERA_AUTO_FOCUS_RESULT, &auto_focus_status);
+ if (rc < 0) {
+ LOGE("%s: g ctrl failed!", __func__);
+ auto_focus_result = 0;
+ pthread_mutex_unlock(&exynos_camera->auto_focus_mutex);
+ goto thread_exit;
+ }
+
+ if (auto_focus_status & M5MO_AF_STATUS_IN_PROGRESS) {
+ usleep(10000);
+ } else if (auto_focus_status == M5MO_AF_STATUS_SUCCESS || auto_focus_status == M5MO_AF_STATUS_1ST_SUCCESS) {
+ auto_focus_result = 1;
+ pthread_mutex_unlock(&exynos_camera->auto_focus_mutex);
+ goto thread_exit;
+ } else {
+ auto_focus_result = 0;
+ pthread_mutex_unlock(&exynos_camera->auto_focus_mutex);
+ goto thread_exit;
+ }
+
+ pthread_mutex_unlock(&exynos_camera->auto_focus_mutex);
+ }
+
+thread_exit:
+ rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_CAMERA_SET_AUTO_FOCUS, AUTO_FOCUS_OFF);
+ if (rc < 0)
+ LOGE("%s: s ctrl failed!", __func__);
+
+ if (EXYNOS_CAMERA_MSG_ENABLED(CAMERA_MSG_FOCUS) && EXYNOS_CAMERA_CALLBACK_DEFINED(notify))
+ exynos_camera->callbacks.notify(CAMERA_MSG_FOCUS,
+ (int32_t) auto_focus_result, 0, exynos_camera->callbacks.user);
+
+ exynos_camera->auto_focus_thread_running = 0;
+ exynos_camera->auto_focus_enabled = 0;
+
+ LOGE("%s: Exiting thread", __func__);
+
+ return NULL;
+}
+
+int exynos_camera_auto_focus_start(struct exynos_camera *exynos_camera)
+{
+ pthread_attr_t thread_attr;
+ int rc;
+
+ if (exynos_camera == NULL)
+ return -EINVAL;
+
+ // Thread
+
+ if (exynos_camera->auto_focus_thread_running) {
+ LOGE("Auto-focus thread is already running!");
+ return -1;
+ }
+
+ pthread_mutex_init(&exynos_camera->auto_focus_mutex, NULL);
+
+ pthread_attr_init(&thread_attr);
+ pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
+
+ exynos_camera->auto_focus_enabled = 1;
+
+ rc = pthread_create(&exynos_camera->auto_focus_thread, &thread_attr,
+ exynos_camera_auto_focus_thread, (void *) exynos_camera);
+ if (rc < 0) {
+ LOGE("%s: Unable to create thread", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+void exynos_camera_auto_focus_stop(struct exynos_camera *exynos_camera)
+{
+ int rc;
+ int i;
+
+ if (exynos_camera == NULL)
+ return;
+
+ if (!exynos_camera->auto_focus_enabled) {
+ LOGE("Auto-focus was already stopped!");
+ return;
+ }
+
+ pthread_mutex_lock(&exynos_camera->auto_focus_mutex);
+
+ // Disable auto-focus to make the thread end
+ exynos_camera->auto_focus_enabled = 0;
+
+ pthread_mutex_unlock(&exynos_camera->auto_focus_mutex);
+
+ // Wait for the thread to end
+ for (i = 0; i < 10; i++) {
+ if (!exynos_camera->auto_focus_thread_running)
+ break;
+
+ usleep(500);
+ }
+
+ pthread_mutex_destroy(&exynos_camera->auto_focus_mutex);
+}
+
+// Preview
+
+int exynos_camera_preview(struct exynos_camera *exynos_camera)
+{
+ buffer_handle_t *buffer;
+ int stride;
+
+ int width, height;
+ float format_bpp;
+
+ char *preview_format_string;
+ int frame_size, offset;
+ void *preview_data;
+ void *window_data;
+
+ unsigned int recording_y_addr;
+ unsigned int recording_cbcr_addr;
+ nsecs_t timestamp;
+ struct exynos_camera_addrs *addrs;
+ struct timespec ts;
+
+ int index;
+ int rc;
+ int i;
+
+ if (exynos_camera == NULL || exynos_camera->preview_memory == NULL ||
+ exynos_camera->preview_window == NULL)
+ return -EINVAL;
+
+ timestamp = systemTime(1);
+
+ // V4L2
+
+ rc = exynos_v4l2_poll(exynos_camera, 0);
+ if (rc < 0) {
+ LOGE("%s: poll failed!", __func__);
+ return -1;
+ } else if (rc == 0) {
+ LOGE("%s: poll timeout!", __func__);
+ return -1;
+ }
+
+ index = exynos_v4l2_dqbuf_cap(exynos_camera, 0);
+ if (index < 0 || index >= exynos_camera->preview_buffers_count) {
+ LOGE("%s: dqbuf failed!", __func__);
+ return -1;
+ }
+
+ rc = exynos_v4l2_qbuf_cap(exynos_camera, 0, index);
+ if (rc < 0) {
+ LOGE("%s: qbuf failed!", __func__);
+ return -1;
+ }
+
+ // Preview window
+
+ width = exynos_camera->preview_width;
+ height = exynos_camera->preview_height;
+ format_bpp = exynos_camera->preview_format_bpp;
+
+ exynos_camera->preview_window->dequeue_buffer(exynos_camera->preview_window,
+ &buffer, &stride);
+ exynos_camera->gralloc->lock(exynos_camera->gralloc, *buffer, GRALLOC_USAGE_SW_WRITE_OFTEN,
+ 0, 0, width, height, &window_data);
+
+ if (window_data == NULL) {
+ LOGE("%s: gralloc lock failed!", __func__);
+ return -1;
+ }
+
+ frame_size = exynos_camera->preview_frame_size;
+ offset = index * frame_size;
+
+ preview_data = (void *) ((int) exynos_camera->preview_memory->data + offset);
+ memcpy(window_data, preview_data, frame_size);
+
+ exynos_camera->gralloc->unlock(exynos_camera->gralloc, *buffer);
+ exynos_camera->preview_window->enqueue_buffer(exynos_camera->preview_window,
+ buffer);
+
+ if (EXYNOS_CAMERA_MSG_ENABLED(CAMERA_MSG_PREVIEW_FRAME) && EXYNOS_CAMERA_CALLBACK_DEFINED(data)) {
+ exynos_camera->callbacks.data(CAMERA_MSG_PREVIEW_FRAME,
+ exynos_camera->preview_memory, index, NULL, exynos_camera->callbacks.user);
+ }
+
+ // Recording
+
+ if (exynos_camera->recording_enabled && exynos_camera->recording_memory != NULL) {
+ pthread_mutex_lock(&exynos_camera->recording_mutex);
+
+ // V4L2
+
+ rc = exynos_v4l2_poll(exynos_camera, 2);
+ if (rc < 0) {
+ LOGE("%s: poll failed!", __func__);
+ goto error_recording;
+ } else if (rc == 0) {
+ LOGE("%s: poll timeout!", __func__);
+ goto error_recording;
+ }
+
+ index = exynos_v4l2_dqbuf_cap(exynos_camera, 2);
+ if (index < 0) {
+ LOGE("%s: dqbuf failed!", __func__);
+ goto error_recording;
+ }
+
+ recording_y_addr = exynos_v4l2_s_ctrl(exynos_camera, 2, V4L2_CID_PADDR_Y, index);
+ if (recording_y_addr == 0xffffffff) {
+ LOGE("%s: s ctrl failed!", __func__);
+ goto error_recording;
+ }
+
+ recording_cbcr_addr = exynos_v4l2_s_ctrl(exynos_camera, 2, V4L2_CID_PADDR_CBCR, index);
+ if (recording_cbcr_addr == 0xffffffff) {
+ LOGE("%s: s ctrl failed!", __func__);
+ goto error_recording;
+ }
+
+ addrs = (struct exynos_camera_addrs *) exynos_camera->recording_memory->data;
+
+ addrs[index].type = 0; // kMetadataBufferTypeCameraSource
+ addrs[index].y = recording_y_addr;
+ addrs[index].cbcr = recording_cbcr_addr;
+ addrs[index].index = index;
+ addrs[index].reserved = 0;
+
+ pthread_mutex_unlock(&exynos_camera->recording_mutex);
+
+ if (EXYNOS_CAMERA_MSG_ENABLED(CAMERA_MSG_VIDEO_FRAME) && EXYNOS_CAMERA_CALLBACK_DEFINED(data_timestamp)) {
+ exynos_camera->callbacks.data_timestamp(timestamp, CAMERA_MSG_VIDEO_FRAME,
+ exynos_camera->recording_memory, index, exynos_camera->callbacks.user);
+ } else {
+ rc = exynos_v4l2_qbuf_cap(exynos_camera, 2, index);
+ if (rc < 0) {
+ LOGE("%s: qbuf failed!", __func__);
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+
+error_recording:
+ pthread_mutex_lock(&exynos_camera->recording_mutex);
+
+ return -1;
+}
+
+void *exynos_camera_preview_thread(void *data)
+{
+ struct exynos_camera *exynos_camera;
+ int rc;
+
+ if (data == NULL)
+ return NULL;
+
+ exynos_camera = (struct exynos_camera *) data;
+
+ LOGE("%s: Starting thread", __func__);
+ exynos_camera->preview_thread_running = 1;
+
+ if (exynos_camera->preview_window == NULL) {
+ // Lock preview lock mutex
+ pthread_mutex_lock(&exynos_camera->preview_lock_mutex);
+ }
+
+ while (exynos_camera->preview_enabled == 1) {
+ pthread_mutex_lock(&exynos_camera->preview_mutex);
+
+ rc = exynos_camera_preview(exynos_camera);
+ if (rc < 0) {
+ LOGE("%s: preview failed!", __func__);
+ exynos_camera->preview_enabled = 0;
+ }
+
+ pthread_mutex_unlock(&exynos_camera->preview_mutex);
+ }
+
+ exynos_camera->preview_thread_running = 0;
+ LOGE("%s: Exiting thread", __func__);
+
+ return NULL;
+}
+
+int exynos_camera_preview_start(struct exynos_camera *exynos_camera)
+{
+ struct v4l2_streamparm streamparm;
+ int width, height, format;
+ float format_bpp;
+ int fps, frame_size;
+ int fd;
+
+ pthread_attr_t thread_attr;
+
+ int rc;
+ int i;
+
+ if (exynos_camera == NULL)
+ return -EINVAL;
+
+ if (exynos_camera->preview_enabled) {
+ LOGE("Preview was already started!");
+ return 0;
+ }
+
+ // V4L2
+
+ format = exynos_camera->preview_format;
+
+ rc = exynos_v4l2_enum_fmt_cap(exynos_camera, 0, format);
+ if (rc < 0) {
+ LOGE("%s: enum fmt failed!", __func__);
+ return -1;
+ }
+
+ width = exynos_camera->preview_width;
+ height = exynos_camera->preview_height;
+ format_bpp = exynos_camera->preview_format_bpp;
+
+ rc = exynos_v4l2_s_fmt_pix_cap(exynos_camera, 0, width, height, format, V4L2_PIX_FMT_MODE_PREVIEW);
+ if (rc < 0) {
+ LOGE("%s: s fmt failed!", __func__);
+ return -1;
+ }
+
+ rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_CACHEABLE, 1);
+ if (rc < 0) {
+ LOGE("%s: s ctrl failed!", __func__);
+ return -1;
+ }
+
+ for (i = EXYNOS_CAMERA_MAX_BUFFERS_COUNT; i >= EXYNOS_CAMERA_MIN_BUFFERS_COUNT; i--) {
+ rc = exynos_v4l2_reqbufs_cap(exynos_camera, 0, i);
+ if (rc >= 0)
+ break;
+ }
+
+ if (rc < 0) {
+ LOGE("%s: reqbufs failed!", __func__);
+ return -1;
+ }
+
+ exynos_camera->preview_buffers_count = rc;
+ LOGD("Found %d preview buffers available!", exynos_camera->preview_buffers_count);
+
+ fps = exynos_camera->preview_fps;
+ memset(&streamparm, 0, sizeof(streamparm));
+ streamparm.parm.capture.timeperframe.numerator = 1;
+ streamparm.parm.capture.timeperframe.denominator = fps;
+
+ rc = exynos_v4l2_s_parm_cap(exynos_camera, 0, &streamparm);
+ if (rc < 0) {
+ LOGE("%s: s parm failed!", __func__);
+ return -1;
+ }
+
+ frame_size = (int) ((float) width * (float) height * format_bpp);
+ for (i = 0; i < exynos_camera->preview_buffers_count; i++) {
+ rc = exynos_v4l2_querybuf_cap(exynos_camera, 0, i);
+ if (rc < 0) {
+ LOGE("%s: querybuf failed!", __func__);
+ return -1;
+ }
+
+ if (rc < frame_size) {
+ LOGE("%s: problematic frame size: %d/%d", __func__, rc, frame_size);
+ return -1;
+ }
+ }
+
+ frame_size = rc;
+ exynos_camera->preview_frame_size = frame_size;
+
+ if (exynos_camera->callbacks.request_memory != NULL) {
+ fd = exynos_v4l2_find_fd(exynos_camera, 0);
+ if (fd < 0) {
+ LOGE("%s: Unable to find v4l2 fd", __func__);
+ return -1;
+ }
+
+ if (exynos_camera->preview_memory != NULL && exynos_camera->preview_memory->release != NULL)
+ exynos_camera->preview_memory->release(exynos_camera->preview_memory);
+
+ exynos_camera->preview_memory =
+ exynos_camera->callbacks.request_memory(fd,
+ frame_size, exynos_camera->preview_buffers_count, 0);
+ if (exynos_camera->preview_memory == NULL) {
+ LOGE("%s: memory request failed!", __func__);
+ return -1;
+ }
+ } else {
+ LOGE("%s: No memory request function!", __func__);
+ return -1;
+ }
+
+ for (i = 0; i < exynos_camera->preview_buffers_count; i++) {
+ rc = exynos_v4l2_qbuf_cap(exynos_camera, 0, i);
+ if (rc < 0) {
+ LOGE("%s: qbuf failed!", __func__);
+ return -1;
+ }
+ }
+
+ rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_ROTATION,
+ exynos_camera->camera_rotation);
+ if (rc < 0) {
+ LOGE("%s: s ctrl failed!", __func__);
+ return -1;
+ }
+
+ rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_HFLIP,
+ exynos_camera->camera_hflip);
+ if (rc < 0) {
+ LOGE("%s: s ctrl failed!", __func__);
+ return -1;
+ }
+
+ rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_VFLIP,
+ exynos_camera->camera_vflip);
+ if (rc < 0) {
+ LOGE("%s: s ctrl failed!", __func__);
+ return -1;
+ }
+
+ rc = exynos_v4l2_streamon_cap(exynos_camera, 0);
+ if (rc < 0) {
+ LOGE("%s: streamon failed!", __func__);
+ return -1;
+ }
+
+ // Thread
+
+ pthread_mutex_init(&exynos_camera->preview_mutex, NULL);
+ pthread_mutex_init(&exynos_camera->preview_lock_mutex, NULL);
+
+ // Lock preview lock
+ pthread_mutex_lock(&exynos_camera->preview_lock_mutex);
+
+ pthread_attr_init(&thread_attr);
+ pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
+
+ exynos_camera->preview_enabled = 1;
+
+ rc = pthread_create(&exynos_camera->preview_thread, &thread_attr,
+ exynos_camera_preview_thread, (void *) exynos_camera);
+ if (rc < 0) {
+ LOGE("%s: Unable to create thread", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+void exynos_camera_preview_stop(struct exynos_camera *exynos_camera)
+{
+ int rc;
+ int i;
+
+ if (exynos_camera == NULL)
+ return;
+
+ if (!exynos_camera->preview_enabled) {
+ LOGE("Preview was already stopped!");
+ return;
+ }
+
+ exynos_camera->preview_enabled = 0;
+
+ // Unlock preview lock
+ pthread_mutex_unlock(&exynos_camera->preview_lock_mutex);
+
+ pthread_mutex_lock(&exynos_camera->preview_mutex);
+
+ // Wait for the thread to end
+ for (i = 0; i < 10; i++) {
+ if (!exynos_camera->preview_thread_running)
+ break;
+
+ usleep(1000);
+ }
+
+ rc = exynos_v4l2_streamoff_cap(exynos_camera, 0);
+ if (rc < 0) {
+ LOGE("%s: streamoff failed!", __func__);
+ }
+
+ exynos_camera->preview_params_set = 0;
+
+ if (exynos_camera->preview_memory != NULL && exynos_camera->preview_memory->release != NULL) {
+ exynos_camera->preview_memory->release(exynos_camera->preview_memory);
+ exynos_camera->preview_memory = NULL;
+ }
+
+ exynos_camera->preview_window = NULL;
+
+ pthread_mutex_unlock(&exynos_camera->preview_mutex);
+
+ pthread_mutex_destroy(&exynos_camera->preview_lock_mutex);
+ pthread_mutex_destroy(&exynos_camera->preview_mutex);
+}
+
+// Recording
+
+void exynos_camera_recording_frame_release(struct exynos_camera *exynos_camera, void *data)
+{
+ struct exynos_camera_addrs *addrs;
+ int rc;
+
+ if (exynos_camera == NULL || data == NULL)
+ return;
+
+ addrs = (struct exynos_camera_addrs *) data;
+ if (addrs->index >= (unsigned int) exynos_camera->recording_buffers_count)
+ return;
+
+ pthread_mutex_lock(&exynos_camera->recording_mutex);
+
+ rc = exynos_v4l2_qbuf_cap(exynos_camera, 2, addrs->index);
+ if (rc < 0) {
+ LOGE("%s: qbuf failed!", __func__);
+ goto error;
+ }
+
+error:
+ pthread_mutex_unlock(&exynos_camera->recording_mutex);
+}
+
+int exynos_camera_recording_start(struct exynos_camera *exynos_camera)
+{
+ int width, height, format;
+ int fd;
+
+ int rc;
+ int i;
+
+ if (exynos_camera == NULL)
+ return -EINVAL;
+
+ if (exynos_camera->recording_enabled) {
+ LOGE("Recording was already started!");
+ return 0;
+ }
+
+ pthread_mutex_lock(&exynos_camera->preview_mutex);
+
+ // V4L2
+
+ format = exynos_camera->recording_format;
+
+ rc = exynos_v4l2_enum_fmt_cap(exynos_camera, 2, format);
+ if (rc < 0) {
+ LOGE("%s: enum fmt failed!", __func__);
+ goto error;
+ }
+
+ width = exynos_camera->recording_width;
+ height = exynos_camera->recording_height;
+
+ rc = exynos_v4l2_s_fmt_pix_cap(exynos_camera, 2, width, height, format, V4L2_PIX_FMT_MODE_CAPTURE);
+ if (rc < 0) {
+ LOGE("%s: s fmt failed!", __func__);
+ goto error;
+ }
+
+ for (i = EXYNOS_CAMERA_MAX_BUFFERS_COUNT; i >= EXYNOS_CAMERA_MIN_BUFFERS_COUNT; i--) {
+ rc = exynos_v4l2_reqbufs_cap(exynos_camera, 2, i);
+ if (rc >= 0)
+ break;
+ }
+
+ if (rc < 0) {
+ LOGE("%s: reqbufs failed!", __func__);
+ goto error;
+ }
+
+ exynos_camera->recording_buffers_count = rc;
+ LOGD("Found %d recording buffers available!", exynos_camera->recording_buffers_count);
+
+ for (i = 0; i < exynos_camera->recording_buffers_count; i++) {
+ rc = exynos_v4l2_querybuf_cap(exynos_camera, 2, i);
+ if (rc < 0) {
+ LOGE("%s: querybuf failed!", __func__);
+ goto error;
+ }
+ }
+
+ if (exynos_camera->callbacks.request_memory != NULL) {
+ if (exynos_camera->recording_memory != NULL && exynos_camera->recording_memory->release != NULL)
+ exynos_camera->recording_memory->release(exynos_camera->recording_memory);
+
+ exynos_camera->recording_memory =
+ exynos_camera->callbacks.request_memory(-1, sizeof(struct exynos_camera_addrs),
+ exynos_camera->recording_buffers_count, 0);
+ if (exynos_camera->recording_memory == NULL) {
+ LOGE("%s: memory request failed!", __func__);
+ goto error;
+ }
+ } else {
+ LOGE("%s: No memory request function!", __func__);
+ goto error;
+ }
+
+ for (i = 0; i < exynos_camera->recording_buffers_count; i++) {
+ rc = exynos_v4l2_qbuf_cap(exynos_camera, 2, i);
+ if (rc < 0) {
+ LOGE("%s: qbuf failed!", __func__);
+ goto error;
+ }
+ }
+
+ rc = exynos_v4l2_s_ctrl(exynos_camera, 2, V4L2_CID_ROTATION,
+ exynos_camera->camera_rotation);
+ if (rc < 0) {
+ LOGE("%s: s ctrl failed!", __func__);
+ goto error;
+ }
+
+ rc = exynos_v4l2_s_ctrl(exynos_camera, 2, V4L2_CID_HFLIP,
+ exynos_camera->camera_hflip);
+ if (rc < 0) {
+ LOGE("%s: s ctrl failed!", __func__);
+ goto error;
+ }
+
+ rc = exynos_v4l2_s_ctrl(exynos_camera, 2, V4L2_CID_VFLIP,
+ exynos_camera->camera_vflip);
+ if (rc < 0) {
+ LOGE("%s: s ctrl failed!", __func__);
+ goto error;
+ }
+
+ rc = exynos_v4l2_streamon_cap(exynos_camera, 2);
+ if (rc < 0) {
+ LOGE("%s: streamon failed!", __func__);
+ goto error;
+ }
+
+ pthread_mutex_init(&exynos_camera->recording_mutex, NULL);
+
+ exynos_camera->recording_enabled = 1;
+
+ pthread_mutex_unlock(&exynos_camera->preview_mutex);
+
+ return 0;
+error:
+ pthread_mutex_unlock(&exynos_camera->preview_mutex);
+
+ return -1;
+}
+
+void exynos_camera_recording_stop(struct exynos_camera *exynos_camera)
+{
+ int rc;
+
+ if (exynos_camera == NULL)
+ return;
+
+ if (!exynos_camera->recording_enabled) {
+ LOGE("Recording was already stopped!");
+ return;
+ }
+
+ exynos_camera->recording_enabled = 0;
+
+ pthread_mutex_lock(&exynos_camera->preview_mutex);
+
+ rc = exynos_v4l2_streamoff_cap(exynos_camera, 2);
+ if (rc < 0) {
+ LOGE("%s: streamoff failed!", __func__);
+ }
+
+ if (exynos_camera->recording_memory != NULL && exynos_camera->recording_memory->release != NULL) {
+ exynos_camera->recording_memory->release(exynos_camera->recording_memory);
+ exynos_camera->recording_memory = NULL;
+ }
+
+ pthread_mutex_unlock(&exynos_camera->preview_mutex);
+
+ pthread_mutex_destroy(&exynos_camera->recording_mutex);
+}
+
+/*
+ * Exynos Camera OPS
+ */
+
+int exynos_camera_set_preview_window(struct camera_device *dev,
+ struct preview_stream_ops *w)
+{
+ struct exynos_camera *exynos_camera;
+
+ int width, height, format, hal_format;
+
+ buffer_handle_t *buffer;
+ int stride;
+ void *addr = NULL;
+
+ int rc;
+
+ LOGD("%s(%p, %p)", __func__, dev, w);
+
+ if (dev == NULL || dev->priv == NULL)
+ return -EINVAL;
+
+ exynos_camera = (struct exynos_camera *) dev->priv;
+
+ if (w == NULL)
+ return 0;
+
+ exynos_camera->preview_window = w;
+
+ if (w->set_buffer_count == NULL || w->set_usage == NULL || w->set_buffers_geometry == NULL)
+ return -EINVAL;
+
+ if (exynos_camera->preview_buffers_count <= 0) {
+ LOGE("%s: Invalid preview buffers count", __func__);
+ exynos_camera->preview_buffers_count = EXYNOS_CAMERA_MAX_BUFFERS_COUNT;
+ }
+
+ rc = w->set_buffer_count(w, exynos_camera->preview_buffers_count);
+ if (rc) {
+ LOGE("%s: Unable to set buffer count (%d)", __func__,
+ exynos_camera->preview_buffers_count);
+ return -1;
+ }
+
+ rc = w->set_usage(w, GRALLOC_USAGE_SW_WRITE_OFTEN);
+ if (rc) {
+ LOGE("%s: Unable to set usage", __func__);
+ return -1;
+ }
+
+ width = exynos_camera->preview_width;
+ height = exynos_camera->preview_height;
+ format = exynos_camera->preview_format;
+
+ switch (format) {
+ case V4L2_PIX_FMT_NV21:
+ hal_format = HAL_PIXEL_FORMAT_YCrCb_420_SP;
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ hal_format = HAL_PIXEL_FORMAT_YV12;
+ break;
+ case V4L2_PIX_FMT_RGB565:
+ hal_format = HAL_PIXEL_FORMAT_RGB_565;
+ break;
+ case V4L2_PIX_FMT_RGB32:
+ hal_format = HAL_PIXEL_FORMAT_RGBX_8888;
+ break;
+ default:
+ hal_format = HAL_PIXEL_FORMAT_YCrCb_420_SP;
+ break;
+ }
+
+ rc = w->set_buffers_geometry(w, width, height, hal_format);
+ if (rc) {
+ LOGE("%s: Unable to set buffers geometry", __func__);
+ return -1;
+ }
+
+ // Unlock preview lock
+ pthread_mutex_unlock(&exynos_camera->preview_lock_mutex);
+
+ return 0;
+}
+
+void exynos_camera_set_callbacks(struct camera_device *dev,
+ camera_notify_callback notify_cb,
+ camera_data_callback data_cb,
+ camera_data_timestamp_callback data_cb_timestamp,
+ camera_request_memory get_memory,
+ void *user)
+{
+ struct exynos_camera *exynos_camera;
+
+ LOGD("%s(%p, %p)", __func__, dev, user);
+
+ if (dev == NULL || dev->priv == NULL)
+ return;
+
+ exynos_camera = (struct exynos_camera *) dev->priv;
+
+ exynos_camera->callbacks.notify = notify_cb;
+ exynos_camera->callbacks.data = data_cb;
+ exynos_camera->callbacks.data_timestamp = data_cb_timestamp;
+ exynos_camera->callbacks.request_memory = get_memory;
+ exynos_camera->callbacks.user = user;
+}
+
+void exynos_camera_enable_msg_type(struct camera_device *dev, int32_t msg_type)
+{
+ struct exynos_camera *exynos_camera;
+
+ LOGD("%s(%p, %d)", __func__, dev, msg_type);
+
+ if (dev == NULL || dev->priv == NULL)
+ return;
+
+ exynos_camera = (struct exynos_camera *) dev->priv;
+
+ exynos_camera->messages_enabled |= msg_type;
+}
+
+void exynos_camera_disable_msg_type(struct camera_device *dev, int32_t msg_type)
+{
+ struct exynos_camera *exynos_camera;
+
+ LOGD("%s(%p, %d)", __func__, dev, msg_type);
+
+ if (dev == NULL || dev->priv == NULL)
+ return;
+
+ exynos_camera = (struct exynos_camera *) dev->priv;
+
+ exynos_camera->messages_enabled &= ~msg_type;
+}
+
+int exynos_camera_msg_type_enabled(struct camera_device *dev, int32_t msg_type)
+{
+ struct exynos_camera *exynos_camera;
+
+ LOGD("%s(%p, %d)", __func__, dev, msg_type);
+
+ if (dev == NULL || dev->priv == NULL)
+ return -EINVAL;
+
+ exynos_camera = (struct exynos_camera *) dev->priv;
+
+ return exynos_camera->messages_enabled & msg_type;
+}
+
+int exynos_camera_start_preview(struct camera_device *dev)
+{
+ struct exynos_camera *exynos_camera;
+
+ LOGD("%s(%p)", __func__, dev);
+
+ if (dev == NULL || dev->priv == NULL)
+ return -EINVAL;
+
+ exynos_camera = (struct exynos_camera *) dev->priv;
+
+ return exynos_camera_preview_start(exynos_camera);
+}
+
+void exynos_camera_stop_preview(struct camera_device *dev)
+{
+ struct exynos_camera *exynos_camera;
+
+ LOGD("%s(%p)", __func__, dev);
+
+ if (dev == NULL || dev->priv == NULL)
+ return;
+
+ exynos_camera = (struct exynos_camera *) dev->priv;
+
+ exynos_camera_preview_stop(exynos_camera);
+}
+
+int exynos_camera_preview_enabled(struct camera_device *dev)
+{
+ struct exynos_camera *exynos_camera;
+
+ LOGD("%s(%p)", __func__, dev);
+
+ if (dev == NULL || dev->priv == NULL)
+ return -EINVAL;
+
+ exynos_camera = (struct exynos_camera *) dev->priv;
+
+ return exynos_camera->preview_enabled;
+}
+
+int exynos_camera_store_meta_data_in_buffers(struct camera_device *dev,
+ int enable)
+{
+ LOGD("%s(%p, %d)", __func__, dev, enable);
+
+ if (!enable) {
+ LOGE("%s: Cannot disable meta-data in buffers!", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int exynos_camera_start_recording(struct camera_device *dev)
+{
+ struct exynos_camera *exynos_camera;
+
+ LOGD("%s(%p)", __func__, dev);
+
+ exynos_camera = (struct exynos_camera *) dev->priv;
+
+ return exynos_camera_recording_start(exynos_camera);
+}
+
+void exynos_camera_stop_recording(struct camera_device *dev)
+{
+ struct exynos_camera *exynos_camera;
+
+ LOGD("%s(%p)", __func__, dev);
+
+ exynos_camera = (struct exynos_camera *) dev->priv;
+
+ exynos_camera_recording_stop(exynos_camera);
+}
+
+int exynos_camera_recording_enabled(struct camera_device *dev)
+{
+ struct exynos_camera *exynos_camera;
+
+ LOGD("%s(%p)", __func__, dev);
+
+ if (dev == NULL || dev->priv == NULL)
+ return -EINVAL;
+
+ exynos_camera = (struct exynos_camera *) dev->priv;
+
+ return exynos_camera->recording_enabled;
+}
+
+void exynos_camera_release_recording_frame(struct camera_device *dev,
+ const void *opaque)
+{
+ struct exynos_camera *exynos_camera;
+
+ LOGV("%s(%p, %p)", __func__, dev, opaque);
+
+ if (dev == NULL || dev->priv == NULL)
+ return;
+
+ exynos_camera = (struct exynos_camera *) dev->priv;
+
+ exynos_camera_recording_frame_release(exynos_camera, (void *) opaque);
+}
+
+int exynos_camera_auto_focus(struct camera_device *dev)
+{
+ struct exynos_camera *exynos_camera;
+
+ LOGD("%s(%p)", __func__, dev);
+
+ if (dev == NULL || dev->priv == NULL)
+ return -EINVAL;
+
+ exynos_camera = (struct exynos_camera *) dev->priv;
+
+ return exynos_camera_auto_focus_start(exynos_camera);
+}
+
+int exynos_camera_cancel_auto_focus(struct camera_device *dev)
+{
+ struct exynos_camera *exynos_camera;
+
+ LOGD("%s(%p)", __func__, dev);
+
+ if (dev == NULL || dev->priv == NULL)
+ return -EINVAL;
+
+ exynos_camera = (struct exynos_camera *) dev->priv;
+
+ exynos_camera_auto_focus_stop(exynos_camera);
+
+ return 0;
+}
+
+int exynos_camera_take_picture(struct camera_device *dev)
+{
+ struct exynos_camera *exynos_camera;
+
+ LOGD("%s(%p)", __func__, dev);
+
+ if (dev == NULL || dev->priv == NULL)
+ return -EINVAL;
+
+ exynos_camera = (struct exynos_camera *) dev->priv;
+
+ return exynos_camera_picture_start(exynos_camera);
+}
+
+int exynos_camera_cancel_picture(struct camera_device *dev)
+{
+ struct exynos_camera *exynos_camera;
+
+ LOGD("%s(%p)", __func__, dev);
+
+ if (dev == NULL || dev->priv == NULL)
+ return -EINVAL;
+
+ exynos_camera = (struct exynos_camera *) dev->priv;
+
+ exynos_camera_picture_stop(exynos_camera);
+
+ return 0;
+}
+
+int exynos_camera_set_parameters(struct camera_device *dev,
+ const char *params)
+{
+ struct exynos_camera *exynos_camera;
+ int rc;
+
+ LOGD("%s(%p, %s)", __func__, dev, params);
+
+ if (dev == NULL || dev->priv == NULL || params == NULL)
+ return -EINVAL;
+
+ exynos_camera = (struct exynos_camera *) dev->priv;
+
+ rc = exynos_params_string_set(exynos_camera, (char *) params);
+ if (rc < 0) {
+ LOGE("%s: Unable to set params string", __func__);
+ return -1;
+ }
+
+ rc = exynos_camera_params_apply(exynos_camera);
+ if (rc < 0) {
+ LOGE("%s: Unable to apply params", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+char *exynos_camera_get_parameters(struct camera_device *dev)
+{
+ struct exynos_camera *exynos_camera;
+ char *params;
+
+ LOGD("%s(%p)", __func__, dev);
+
+ if (dev == NULL || dev->priv == NULL)
+ return NULL;
+
+ exynos_camera = (struct exynos_camera *) dev->priv;
+
+ params = exynos_params_string_get(exynos_camera);
+ if (params == NULL) {
+ LOGE("%s: Couldn't find any param", __func__);
+ return strdup("");
+ }
+
+ return params;
+}
+
+void exynos_camera_put_parameters(struct camera_device *dev, char *params)
+{
+ LOGD("%s(%p)", __func__, dev);
+
+ if (params != NULL)
+ free(params);
+}
+
+int exynos_camera_send_command(struct camera_device *dev,
+ int32_t cmd, int32_t arg1, int32_t arg2)
+{
+ LOGD("%s(%p, %d, %d, %d)", __func__, dev, cmd, arg1, arg2);
+
+ return 0;
+}
+
+void exynos_camera_release(struct camera_device *dev)
+{
+ struct exynos_camera *exynos_camera;
+
+ LOGD("%s(%p)", __func__, dev);
+
+ if (dev == NULL || dev->priv == NULL)
+ return;
+
+ exynos_camera = (struct exynos_camera *) dev->priv;
+
+ if (exynos_camera->preview_memory != NULL && exynos_camera->preview_memory->release != NULL) {
+ exynos_camera->preview_memory->release(exynos_camera->preview_memory);
+ exynos_camera->preview_memory = NULL;
+ }
+
+ if (exynos_camera->picture_memory != NULL && exynos_camera->picture_memory->release != NULL) {
+ exynos_camera->picture_memory->release(exynos_camera->picture_memory);
+ exynos_camera->picture_memory = NULL;
+ }
+
+ exynos_camera_deinit(exynos_camera);
+}
+
+int exynos_camera_dump(struct camera_device *dev, int fd)
+{
+ LOGD("%s(%p, %d)", __func__, dev, fd);
+
+ return 0;
+}
+
+/*
+ * Interface
+ */
+
+struct camera_device_ops exynos_camera_ops = {
+ .set_preview_window = exynos_camera_set_preview_window,
+ .set_callbacks = exynos_camera_set_callbacks,
+ .enable_msg_type = exynos_camera_enable_msg_type,
+ .disable_msg_type = exynos_camera_disable_msg_type,
+ .msg_type_enabled = exynos_camera_msg_type_enabled,
+ .start_preview = exynos_camera_start_preview,
+ .stop_preview = exynos_camera_stop_preview,
+ .preview_enabled = exynos_camera_preview_enabled,
+ .store_meta_data_in_buffers = exynos_camera_store_meta_data_in_buffers,
+ .start_recording = exynos_camera_start_recording,
+ .stop_recording = exynos_camera_stop_recording,
+ .recording_enabled = exynos_camera_recording_enabled,
+ .release_recording_frame = exynos_camera_release_recording_frame,
+ .auto_focus = exynos_camera_auto_focus,
+ .cancel_auto_focus = exynos_camera_cancel_auto_focus,
+ .take_picture = exynos_camera_take_picture,
+ .cancel_picture = exynos_camera_cancel_picture,
+ .set_parameters = exynos_camera_set_parameters,
+ .get_parameters = exynos_camera_get_parameters,
+ .put_parameters = exynos_camera_put_parameters,
+ .send_command = exynos_camera_send_command,
+ .release = exynos_camera_release,
+ .dump = exynos_camera_dump,
+};
+
+int exynos_camera_close(hw_device_t *device)
+{
+ struct camera_device *camera_device;
+ struct exynos_camera *exynos_camera;
+
+ LOGD("%s(%p)", __func__, device);
+
+ if (device == NULL)
+ return -EINVAL;
+
+ camera_device = (struct camera_device *) device;
+
+ if (camera_device->priv != NULL) {
+ free(camera_device->priv);
+ }
+
+ free(camera_device);
+
+ return 0;
+}
+
+int exynos_camera_open(const struct hw_module_t* module, const char *camera_id,
+ struct hw_device_t** device)
+{
+ struct camera_device *camera_device = NULL;
+ struct exynos_camera *exynos_camera = NULL;
+ int id;
+ int rc;
+
+ LOGD("%s(%p, %s, %p)", __func__, module, camera_id, device);
+
+ if (module == NULL || camera_id == NULL || device == NULL)
+ return -EINVAL;
+
+ id = atoi(camera_id);
+ if (id < 0)
+ return -EINVAL;
+
+ exynos_camera = calloc(1, sizeof(struct exynos_camera));
+ exynos_camera->config = exynos_camera_config;
+
+ if (exynos_camera->config->presets_count > EXYNOS_CAMERA_MAX_PRESETS_COUNT ||
+ exynos_camera->config->v4l2_nodes_count > EXYNOS_CAMERA_MAX_V4L2_NODES_COUNT)
+ goto error_preset;
+
+ if (id >= exynos_camera->config->presets_count)
+ goto error_preset;
+
+ rc = exynos_camera_init(exynos_camera, id);
+ if (rc < 0) {
+ LOGE("%s: Unable to init camera", __func__);
+ goto error;
+ }
+
+ camera_device = calloc(1, sizeof(struct camera_device));
+ camera_device->common.tag = HARDWARE_DEVICE_TAG;
+ camera_device->common.version = 0;
+ camera_device->common.module = (struct hw_module_t *) module;
+ camera_device->common.close = exynos_camera_close;
+
+ camera_device->ops = &exynos_camera_ops;
+ camera_device->priv = exynos_camera;
+
+ *device = (struct hw_device_t *) &(camera_device->common);
+
+ return 0;
+
+error:
+ exynos_camera_deinit(exynos_camera);
+
+error_device:
+ if (camera_device != NULL)
+ free(camera_device);
+
+error_preset:
+ if (exynos_camera != NULL)
+ free(exynos_camera);
+
+ return -1;
+}
+
+int exynos_camera_get_number_of_cameras(void)
+{
+ LOGD("%s()", __func__);
+
+ if (exynos_camera_config == NULL || exynos_camera_config->presets == NULL) {
+ LOGE("%s: Unable to find proper camera config", __func__);
+ return -1;
+ }
+
+ return exynos_camera_config->presets_count;
+}
+
+int exynos_camera_get_camera_info(int id, struct camera_info *info)
+{
+ LOGD("%s(%d, %p)", __func__, id, info);
+
+ if (id < 0 || info == NULL)
+ return -EINVAL;
+
+ if (exynos_camera_config == NULL || exynos_camera_config->presets == NULL) {
+ LOGE("%s: Unable to find proper camera config", __func__);
+ return -1;
+ }
+
+ if (id >= exynos_camera_config->presets_count)
+ return -EINVAL;
+
+ LOGD("Selected camera: %s", exynos_camera_config->presets[id].name);
+
+ info->facing = exynos_camera_config->presets[id].facing;
+ info->orientation = exynos_camera_config->presets[id].orientation;
+
+ return 0;
+}
+
+struct hw_module_methods_t exynos_camera_module_methods = {
+ .open = exynos_camera_open,
+};
+
+struct camera_module HAL_MODULE_INFO_SYM = {
+ .common = {
+ .tag = HARDWARE_MODULE_TAG,
+ .version_major = 1,
+ .version_minor = 0,
+ .id = CAMERA_HARDWARE_MODULE_ID,
+ .name = "Exynos Camera",
+ .author = "Paul Kocialkowski",
+ .methods = &exynos_camera_module_methods,
+ },
+ .get_number_of_cameras = exynos_camera_get_number_of_cameras,
+ .get_camera_info = exynos_camera_get_camera_info,
+};