summaryrefslogtreecommitdiffstats
path: root/camera
diff options
context:
space:
mode:
Diffstat (limited to 'camera')
-rw-r--r--camera/Android.mk43
-rw-r--r--camera/include/fimc.h405
-rw-r--r--camera/include/ion.h449
-rw-r--r--camera/include/linux/fimc.h381
-rw-r--r--camera/include/s5c73m3.h369
-rw-r--r--camera/smdk4x12_camera.c4491
-rw-r--r--camera/smdk4x12_camera.h694
-rw-r--r--camera/smdk4x12_exif.c888
-rw-r--r--camera/smdk4x12_ion.c159
-rw-r--r--camera/smdk4x12_jpeg.c402
-rw-r--r--camera/smdk4x12_param.c492
-rw-r--r--camera/smdk4x12_utils.c158
-rw-r--r--camera/smdk4x12_v4l2.c739
-rw-r--r--camera/smdk4x12_v4l2_output.c387
14 files changed, 10057 insertions, 0 deletions
diff --git a/camera/Android.mk b/camera/Android.mk
new file mode 100644
index 0000000..e4a6278
--- /dev/null
+++ b/camera/Android.mk
@@ -0,0 +1,43 @@
+# Copyright (C) 2013-2014 Paul Kocialkowski <contact@paulk.fr>
+#
+# 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/>.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ smdk4x12_camera.c \
+ smdk4x12_exif.c \
+ smdk4x12_jpeg.c \
+ smdk4x12_param.c \
+ smdk4x12_utils.c \
+ smdk4x12_v4l2.c \
+ smdk4x12_v4l2_output.c \
+ smdk4x12_ion.c
+
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/include \
+ hardware/samsung/exynos4/hal/include
+
+LOCAL_CFLAGS += -DEXYNOS_ION
+
+LOCAL_SHARED_LIBRARIES := libutils libcutils liblog libcamera_client libhardware libhwjpeg
+LOCAL_PRELINK_MODULE := false
+
+LOCAL_MODULE := camera.smdk4x12
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/camera/include/fimc.h b/camera/include/fimc.h
new file mode 100644
index 0000000..3e7732d
--- /dev/null
+++ b/camera/include/fimc.h
@@ -0,0 +1,405 @@
+/* linux/drivers/media/video/samsung/fimc/fimc.h
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * Header file for Samsung Camera Interface (FIMC) driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+
+#ifndef __FIMC_H
+#define __FIMC_H __FILE__
+
+typedef unsigned int dma_addr_t;
+typedef __u32 u32;
+typedef __s32 s32;
+
+#ifdef __KERNEL__
+#include <linux/wait.h>
+#include <linux/mutex.h>
+#include <linux/i2c.h>
+#include <linux/fb.h>
+#include <linux/videodev2.h>
+#include <linux/platform_device.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf-core.h>
+#ifdef CONFIG_SLP_DMABUF
+#include <media/videobuf2-core.h>
+#endif
+#include <media/v4l2-mediabus.h>
+#if defined(CONFIG_BUSFREQ_OPP) || defined(CONFIG_BUSFREQ_LOCK_WRAPPER)
+#include <mach/dev.h>
+#endif
+#include <plat/media.h>
+#include <plat/fimc.h>
+#include <plat/cpu.h>
+#endif
+
+#ifdef CONFIG_PM_RUNTIME
+#include <linux/pm_runtime.h>
+#endif
+
+#define FIMC_NAME "s3c-fimc"
+#define FIMC_CMA_NAME "fimc"
+
+#define FIMC_CORE_CLK "sclk_fimc"
+
+extern int fimc_clk_rate(void);
+
+#define EXYNOS_BUSFREQ_NAME "exynos-busfreq"
+
+#if defined(CONFIG_ARCH_EXYNOS4)
+#define FIMC_DEVICES 4
+#define FIMC_PHYBUFS 32
+#define FIMC_MAXCAMS 7
+#else
+#define FIMC_DEVICES 3
+#define FIMC_PHYBUFS 4
+#define FIMC_MAXCAMS 5
+#endif
+
+#define FIMC_SUBDEVS 3
+#define FIMC_OUTBUFS 3
+#define FIMC_INQUEUES 10
+#if defined(CONFIG_SLP)
+#define FIMC_MAX_CTXS 8
+#else
+#define FIMC_MAX_CTXS 4
+#endif
+#define FIMC_TPID 3
+#define FIMC_CAPBUFS 32
+#define FIMC_ONESHOT_TIMEOUT 200
+#define FIMC_DQUEUE_TIMEOUT 1000
+
+#define FIMC_FIFOOFF_CNT 1000000 /* Sufficiently big value for stop */
+
+#define FORMAT_FLAGS_PACKED 0x1
+#define FORMAT_FLAGS_PLANAR 0x2
+
+#define FIMC_ADDR_Y 0
+#define FIMC_ADDR_CB 1
+#define FIMC_ADDR_CR 2
+
+#define FIMC_HD_WIDTH 1280
+#define FIMC_HD_HEIGHT 720
+
+#define FIMC_FHD_WIDTH 1920
+#define FIMC_FHD_HEIGHT 1080
+
+#define FIMC_MMAP_IDX -1
+#define FIMC_USERPTR_IDX -2
+
+#define FIMC_HCLK 0
+#define FIMC_SCLK 1
+#define CSI_CH_0 0
+#define CSI_CH_1 1
+#if defined(CONFIG_VIDEO_FIMC_FIFO)
+#define FIMC_OVLY_MODE FIMC_OVLY_FIFO
+#elif defined(CONFIG_VIDEO_FIMC_DMA_AUTO)
+#define FIMC_OVLY_MODE FIMC_OVLY_DMA_AUTO
+#endif
+
+#define PINGPONG_2ADDR_MODE
+#if defined(PINGPONG_2ADDR_MODE)
+#define FIMC_PINGPONG 2
+#endif
+
+#define check_bit(data, loc) ((data) & (0x1<<(loc)))
+#define FRAME_SEQ 0xf
+
+#define fimc_cam_use ((pdata->use_cam) ? 1 : 0)
+
+#define L2_FLUSH_ALL SZ_1M
+#define L1_FLUSH_ALL SZ_64K
+
+/*
+ * ENUMERATIONS
+*/
+enum fimc_status {
+ FIMC_READY_OFF = 0x00,
+ FIMC_STREAMOFF = 0x01,
+ FIMC_READY_ON = 0x02,
+ FIMC_STREAMON = 0x03,
+ FIMC_STREAMON_IDLE = 0x04, /* oneshot mode */
+ FIMC_OFF_SLEEP = 0x05,
+ FIMC_ON_SLEEP = 0x06,
+ FIMC_ON_IDLE_SLEEP = 0x07, /* oneshot mode */
+ FIMC_READY_RESUME = 0x08,
+ FIMC_BUFFER_STOP = 0x09,
+ FIMC_BUFFER_START = 0x0A,
+};
+
+enum fimc_fifo_state {
+ FIFO_CLOSE,
+ FIFO_SLEEP,
+};
+
+enum fimc_fimd_state {
+ FIMD_OFF,
+ FIMD_ON,
+};
+
+enum fimc_rot_flip {
+ FIMC_XFLIP = 0x01,
+ FIMC_YFLIP = 0x02,
+ FIMC_ROT = 0x10,
+};
+
+enum fimc_input {
+ FIMC_SRC_CAM,
+ FIMC_SRC_MSDMA,
+};
+
+enum fimc_overlay_mode {
+ FIMC_OVLY_NOT_FIXED = 0x0, /* Overlay mode isn't fixed. */
+ FIMC_OVLY_FIFO = 0x1, /* Non-destructive Overlay with FIFO */
+ FIMC_OVLY_DMA_AUTO = 0x2, /* Non-destructive Overlay with DMA */
+ FIMC_OVLY_DMA_MANUAL = 0x3, /* Non-destructive Overlay with DMA */
+ FIMC_OVLY_NONE_SINGLE_BUF = 0x4, /* Destructive Overlay with DMA single destination buffer */
+ FIMC_OVLY_NONE_MULTI_BUF = 0x5, /* Destructive Overlay with DMA multiple dstination buffer */
+};
+
+enum fimc_autoload {
+ FIMC_AUTO_LOAD,
+ FIMC_ONE_SHOT,
+};
+
+enum fimc_log {
+ FIMC_LOG_DEBUG = 0x1000,
+ FIMC_LOG_INFO_L2 = 0x0200,
+ FIMC_LOG_INFO_L1 = 0x0100,
+ FIMC_LOG_WARN = 0x0010,
+ FIMC_LOG_ERR = 0x0001,
+};
+
+enum fimc_range {
+ FIMC_RANGE_NARROW = 0x0,
+ FIMC_RANGE_WIDE = 0x1,
+};
+
+enum fimc_pixel_format_type{
+ FIMC_RGB,
+ FIMC_YUV420,
+ FIMC_YUV422,
+ FIMC_YUV444,
+};
+
+enum fimc_framecnt_seq {
+ FIMC_FRAMECNT_SEQ_DISABLE,
+ FIMC_FRAMECNT_SEQ_ENABLE,
+};
+
+enum fimc_sysmmu_flag {
+ FIMC_SYSMMU_OFF,
+ FIMC_SYSMMU_ON,
+};
+
+enum fimc_id {
+ FIMC0 = 0x0,
+ FIMC1 = 0x1,
+ FIMC2 = 0x2,
+ FIMC3 = 0x3,
+};
+
+enum fimc_power_status {
+ FIMC_POWER_OFF,
+ FIMC_POWER_ON,
+ FIMC_POWER_SUSPEND,
+};
+
+enum cam_mclk_status {
+ CAM_MCLK_OFF,
+ CAM_MCLK_ON,
+};
+
+enum fimc_plane_num {
+ PLANE_1 = 0x1,
+ PLANE_2 = 0x2,
+ PLANE_3 = 0x3,
+ PLANE_4 = 0x4,
+};
+
+/*
+ * STRUCTURES
+*/
+
+/* for reserved memory */
+struct fimc_meminfo {
+#ifdef CONFIG_USE_FIMC_CMA
+ void *cpu_addr;
+#endif
+ dma_addr_t base; /* buffer base */
+ size_t size; /* total length */
+ dma_addr_t curr; /* current addr */
+ dma_addr_t vaddr_base; /* buffer base */
+ dma_addr_t vaddr_curr; /* current addr */
+};
+
+struct fimc_buf {
+ dma_addr_t base[3];
+ size_t length[3];
+};
+
+struct fimc_overlay_buf {
+ u32 vir_addr[3];
+ size_t size[3];
+ u32 phy_addr[3];
+};
+
+struct fimc_overlay {
+ enum fimc_overlay_mode mode;
+ struct fimc_overlay_buf buf;
+ s32 req_idx;
+};
+
+/* for output overlay device */
+struct fimc_idx {
+ int ctx;
+ int idx;
+};
+
+struct fimc_ctx_idx {
+ struct fimc_idx prev;
+ struct fimc_idx active;
+ struct fimc_idx next;
+};
+
+/* scaler abstraction: local use recommended */
+struct fimc_scaler {
+ u32 bypass;
+ u32 hfactor;
+ u32 vfactor;
+ u32 pre_hratio;
+ u32 pre_vratio;
+ u32 pre_dst_width;
+ u32 pre_dst_height;
+ u32 scaleup_h;
+ u32 scaleup_v;
+ u32 main_hratio;
+ u32 main_vratio;
+ u32 real_width;
+ u32 real_height;
+ u32 shfactor;
+ u32 skipline;
+};
+
+struct s3cfb_user_window {
+ int x;
+ int y;
+};
+
+enum s3cfb_data_path_t {
+ DATA_PATH_FIFO = 0,
+ DATA_PATH_DMA = 1,
+ DATA_PATH_IPC = 2,
+};
+
+enum s3cfb_mem_owner_t {
+ DMA_MEM_NONE = 0,
+ DMA_MEM_FIMD = 1,
+ DMA_MEM_OTHER = 2,
+};
+#define S3CFB_WIN_OFF_ALL _IO('F', 202)
+#define S3CFB_WIN_POSITION _IOW('F', 203, struct s3cfb_user_window)
+#define S3CFB_GET_LCD_WIDTH _IOR('F', 302, int)
+#define S3CFB_GET_LCD_HEIGHT _IOR('F', 303, int)
+#define S3CFB_SET_WRITEBACK _IOW('F', 304, u32)
+#define S3CFB_SET_WIN_ON _IOW('F', 305, u32)
+#define S3CFB_SET_WIN_OFF _IOW('F', 306, u32)
+#define S3CFB_SET_WIN_PATH _IOW('F', 307, enum s3cfb_data_path_t)
+#define S3CFB_SET_WIN_ADDR _IOW('F', 308, unsigned long)
+#define S3CFB_SET_WIN_MEM _IOW('F', 309, enum s3cfb_mem_owner_t)
+/* ------------------------------------------------------------------------ */
+
+struct fimc_fbinfo {
+ struct fb_fix_screeninfo *fix;
+ struct fb_var_screeninfo *var;
+ int lcd_hres;
+ int lcd_vres;
+ u32 is_enable;
+ /* lcd fifo control */
+
+ int (*open_fifo)(int id, int ch, int (*do_priv)(void *), void *param);
+ int (*close_fifo)(int id, int (*do_priv)(void *), void *param);
+};
+
+struct fimc_limit {
+ u32 pre_dst_w;
+ u32 bypass_w;
+ u32 trg_h_no_rot;
+ u32 trg_h_rot;
+ u32 real_w_no_rot;
+ u32 real_h_rot;
+};
+
+enum FIMC_EFFECT_FIN {
+ FIMC_EFFECT_FIN_BYPASS = 0,
+ FIMC_EFFECT_FIN_ARBITRARY_CBCR,
+ FIMC_EFFECT_FIN_NEGATIVE,
+ FIMC_EFFECT_FIN_ART_FREEZE,
+ FIMC_EFFECT_FIN_EMBOSSING,
+ FIMC_EFFECT_FIN_SILHOUETTE,
+};
+
+
+struct fimc_effect {
+ int ie_on;
+ int ie_after_sc;
+ enum FIMC_EFFECT_FIN fin;
+ int pat_cb;
+ int pat_cr;
+};
+
+/* debug macro */
+#define FIMC_LOG_DEFAULT (FIMC_LOG_WARN | FIMC_LOG_ERR)
+
+#define FIMC_DEBUG(fmt, ...) \
+ do { \
+ if (ctrl->log & FIMC_LOG_DEBUG) \
+ printk(KERN_DEBUG FIMC_NAME "%d: " \
+ fmt, ctrl->id, ##__VA_ARGS__); \
+ } while (0)
+
+#define FIMC_INFO_L2(fmt, ...) \
+ do { \
+ if (ctrl->log & FIMC_LOG_INFO_L2) \
+ printk(KERN_INFO FIMC_NAME "%d: " \
+ fmt, ctrl->id, ##__VA_ARGS__); \
+ } while (0)
+
+#define FIMC_INFO_L1(fmt, ...) \
+ do { \
+ if (ctrl->log & FIMC_LOG_INFO_L1) \
+ printk(KERN_INFO FIMC_NAME "%d: " \
+ fmt, ctrl->id, ##__VA_ARGS__); \
+ } while (0)
+
+#define FIMC_WARN(fmt, ...) \
+ do { \
+ if (ctrl->log & FIMC_LOG_WARN) \
+ printk(KERN_WARNING FIMC_NAME "%d: " \
+ fmt, ctrl->id, ##__VA_ARGS__); \
+ } while (0)
+
+
+#define FIMC_ERROR(fmt, ...) \
+ do { \
+ if (ctrl->log & FIMC_LOG_ERR) \
+ printk(KERN_ERR FIMC_NAME "%d: " \
+ fmt, ctrl->id, ##__VA_ARGS__); \
+ } while (0)
+
+
+#define fimc_dbg(fmt, ...) FIMC_DEBUG(fmt, ##__VA_ARGS__)
+#define fimc_info2(fmt, ...) FIMC_INFO_L2(fmt, ##__VA_ARGS__)
+#define fimc_info1(fmt, ...) FIMC_INFO_L1(fmt, ##__VA_ARGS__)
+#define fimc_warn(fmt, ...) FIMC_WARN(fmt, ##__VA_ARGS__)
+#define fimc_err(fmt, ...) FIMC_ERROR(fmt, ##__VA_ARGS__)
+
+#endif /* __FIMC_H */
diff --git a/camera/include/ion.h b/camera/include/ion.h
new file mode 100644
index 0000000..29dba57
--- /dev/null
+++ b/camera/include/ion.h
@@ -0,0 +1,449 @@
+/*
+ * include/linux/ion.h
+ *
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef _LINUX_ION_H
+#define _LINUX_ION_H
+
+#include <linux/types.h>
+
+#define CONFIG_ION_EXYNOS
+
+/* This should be removed some day when phys_addr_t's are fully
+ plumbed in the kernel, and all instances of ion_phys_addr_t should
+ be converted to phys_addr_t. For the time being many kernel interfaces
+ do not accept phys_addr_t's that would have to */
+#define ion_phys_addr_t unsigned long
+
+struct ion_handle;
+/**
+ * enum ion_heap_types - list of all possible types of heaps
+ * @ION_HEAP_TYPE_SYSTEM: memory allocated via vmalloc
+ * @ION_HEAP_TYPE_SYSTEM_CONTIG: memory allocated via kmalloc
+ * @ION_HEAP_TYPE_CARVEOUT: memory allocated from a prereserved
+ * carveout heap, allocations are physically
+ * contiguous
+ * @ION_HEAP_END: helper for iterating over heaps
+ */
+enum ion_heap_type {
+ ION_HEAP_TYPE_SYSTEM,
+ ION_HEAP_TYPE_SYSTEM_CONTIG,
+ ION_HEAP_TYPE_CARVEOUT,
+ ION_HEAP_TYPE_CUSTOM, /* must be last so device specific heaps always
+ are at the end of this enum */
+#ifdef CONFIG_ION_EXYNOS
+ ION_HEAP_TYPE_EXYNOS_CONTIG,
+ ION_HEAP_TYPE_EXYNOS,
+ ION_HEAP_TYPE_EXYNOS_USER,
+#endif
+ ION_NUM_HEAPS,
+};
+
+#define ION_HEAP_SYSTEM_MASK (1 << ION_HEAP_TYPE_SYSTEM)
+#define ION_HEAP_SYSTEM_CONTIG_MASK (1 << ION_HEAP_TYPE_SYSTEM_CONTIG)
+#define ION_HEAP_CARVEOUT_MASK (1 << ION_HEAP_TYPE_CARVEOUT)
+
+#ifdef CONFIG_ION_EXYNOS
+#define ION_HEAP_EXYNOS_MASK (1 << ION_HEAP_TYPE_EXYNOS)
+#define ION_HEAP_EXYNOS_CONTIG_MASK (1 << ION_HEAP_TYPE_EXYNOS_CONTIG)
+#define ION_HEAP_EXYNOS_USER_MASK (1 << ION_HEAP_TYPE_EXYNOS_USER)
+#define ION_EXYNOS_NONCACHE_MASK (1 << (BITS_PER_LONG - 2))
+#define ION_EXYNOS_WRITE_MASK (1 << (BITS_PER_LONG - 1))
+#endif
+
+#ifdef __KERNEL__
+struct ion_device;
+struct ion_heap;
+struct ion_mapper;
+struct ion_client;
+struct ion_buffer;
+
+/**
+ * struct ion_platform_heap - defines a heap in the given platform
+ * @type: type of the heap from ion_heap_type enum
+ * @id: unique identifier for heap. When allocating (lower numbers
+ * will be allocated from first)
+ * @name: used for debug purposes
+ * @base: base address of heap in physical memory if applicable
+ * @size: size of the heap in bytes if applicable
+ *
+ * Provided by the board file.
+ */
+struct ion_platform_heap {
+ enum ion_heap_type type;
+ unsigned int id;
+ const char *name;
+ ion_phys_addr_t base;
+ size_t size;
+};
+
+/**
+ * struct ion_platform_data - array of platform heaps passed from board file
+ * @nr: number of structures in the array
+ * @heaps: array of platform_heap structions
+ *
+ * Provided by the board file in the form of platform data to a platform device.
+ */
+struct ion_platform_data {
+ int nr;
+ struct ion_platform_heap heaps[];
+};
+
+/**
+ * ion_client_create() - allocate a client and returns it
+ * @dev: the global ion device
+ * @heap_mask: mask of heaps this client can allocate from
+ * @name: used for debugging
+ */
+struct ion_client *ion_client_create(struct ion_device *dev,
+ unsigned int heap_mask, const char *name);
+
+/**
+ * ion_client_destroy() - free's a client and all it's handles
+ * @client: the client
+ *
+ * Free the provided client and all it's resources including
+ * any handles it is holding.
+ */
+void ion_client_destroy(struct ion_client *client);
+
+/**
+ * ion_get_client() - obtain a user client from file descriptor from user
+ * @fd: the user client created by the request from user. This is
+ * passed from user.
+ *
+ * This function is requested by the device drivers that implement V4L2 and VB2
+ * interfaces. Those device drivers just obtains virtual address of a buffer
+ * even though it is allocated and mapped by ION. While they can retrieve the
+ * handle of the buffer, they are unable to access it because they do not know
+ * what client the handle belongs to.
+ * Note that the client obtained by this function is not released until
+ * ion_put_client() is called and the client is given.
+ */
+struct ion_client *ion_get_user_client(unsigned int fd_client);
+
+/**
+ * ion_put_client() - release the user client obtained by ion_get_client()
+ * @client - The user client to release.
+ */
+void ion_put_user_client(struct ion_client *user_client);
+
+/**
+ * ion_alloc - allocate ion memory
+ * @client: the client
+ * @len: size of the allocation
+ * @align: requested allocation alignment, lots of hardware blocks have
+ * alignment requirements of some kind
+ * @flags: mask of heaps to allocate from, if multiple bits are set
+ * heaps will be tried in order from lowest to highest order bit
+ *
+ * Allocate memory in one of the heaps provided in heap mask and return
+ * an opaque handle to it.
+ */
+struct ion_handle *ion_alloc(struct ion_client *client, size_t len,
+ size_t align, unsigned int flags);
+
+/**
+ * ion_free - free a handle
+ * @client: the client
+ * @handle: the handle to free
+ *
+ * Free the provided handle.
+ */
+void ion_free(struct ion_client *client, struct ion_handle *handle);
+
+/**
+ * ion_phys - returns the physical address and len of a handle
+ * @client: the client
+ * @handle: the handle
+ * @addr: a pointer to put the address in
+ * @len: a pointer to put the length in
+ *
+ * This function queries the heap for a particular handle to get the
+ * handle's physical address. It't output is only correct if
+ * a heap returns physically contiguous memory -- in other cases
+ * this api should not be implemented -- ion_map_dma should be used
+ * instead. Returns -EINVAL if the handle is invalid. This has
+ * no implications on the reference counting of the handle --
+ * the returned value may not be valid if the caller is not
+ * holding a reference.
+ */
+int ion_phys(struct ion_client *client, struct ion_handle *handle,
+ ion_phys_addr_t *addr, size_t *len);
+
+/**
+ * ion_map_kernel - create mapping for the given handle
+ * @client: the client
+ * @handle: handle to map
+ *
+ * Map the given handle into the kernel and return a kernel address that
+ * can be used to access this address.
+ */
+void *ion_map_kernel(struct ion_client *client, struct ion_handle *handle);
+
+/**
+ * ion_unmap_kernel() - destroy a kernel mapping for a handle
+ * @client: the client
+ * @handle: handle to unmap
+ */
+void ion_unmap_kernel(struct ion_client *client, struct ion_handle *handle);
+
+/**
+ * ion_map_dma - create a dma mapping for a given handle
+ * @client: the client
+ * @handle: handle to map
+ *
+ * Return an sglist describing the given handle
+ */
+struct scatterlist *ion_map_dma(struct ion_client *client,
+ struct ion_handle *handle);
+
+/**
+ * ion_unmap_dma() - destroy a dma mapping for a handle
+ * @client: the client
+ * @handle: handle to unmap
+ */
+void ion_unmap_dma(struct ion_client *client, struct ion_handle *handle);
+
+/**
+ * ion_share() - given a handle, obtain a buffer to pass to other clients
+ * @client: the client
+ * @handle: the handle to share
+ *
+ * Given a handle, return a buffer, which exists in a global name
+ * space, and can be passed to other clients. Should be passed into ion_import
+ * to obtain a new handle for this buffer.
+ *
+ * NOTE: This function does do not an extra reference. The burden is on the
+ * caller to make sure the buffer doesn't go away while it's being passed to
+ * another client. That is, ion_free should not be called on this handle until
+ * the buffer has been imported into the other client.
+ */
+struct ion_buffer *ion_share(struct ion_client *client,
+ struct ion_handle *handle);
+
+/**
+ * ion_import() - given an buffer in another client, import it
+ * @client: this blocks client
+ * @buffer: the buffer to import (as obtained from ion_share)
+ *
+ * Given a buffer, add it to the client and return the handle to use to refer
+ * to it further. This is called to share a handle from one kernel client to
+ * another.
+ */
+struct ion_handle *ion_import(struct ion_client *client,
+ struct ion_buffer *buffer);
+
+/**
+ * ion_share_fd() - given a handle, obtain a buffer(fd) to pass to userspace
+ * @client: the client
+ * @handle: the handle to share
+ *
+ * Given a handle, return a fd of a buffer which can be passed to userspace.
+ * Should be passed into userspace or ion_import_fd to obtain a new handle for
+ * this buffer.
+ */
+int ion_share_fd(struct ion_client *client, struct ion_handle *handle);
+
+/**
+ * ion_import_fd() - given an fd obtained via ION_IOC_SHARE ioctl, import it
+ * @client: this blocks client
+ * @fd: the fd
+ *
+ * A helper function for drivers that will be recieving ion buffers shared
+ * with them from userspace. These buffers are represented by a file
+ * descriptor obtained as the return from the ION_IOC_SHARE ioctl.
+ * This function coverts that fd into the underlying buffer, and returns
+ * the handle to use to refer to it further.
+ */
+struct ion_handle *ion_import_fd(struct ion_client *client, int fd);
+
+/**
+ * ion_import_uva() - given a virtual address from user, that is mmapped on an
+ * fd obtained via ION_IOCTL_SHARE ioctl, import it
+ * @client: this blocks client
+ * @uva: virtual address in userspace.
+ * @offset: How many bytes are distant from the beginning of the ION buffer
+ *
+ * A helper function for drivers that will be recieving ion buffers shared
+ * with them from userspace. These buffers are represented by a virtual
+ * address that is mmaped on a file descriptor obtained as the return from the
+ * ION_IOC_SHARE ioctl.
+ * This function does same job with ion_import_fd().
+ */
+struct ion_handle *ion_import_uva(struct ion_client *client, unsigned long uva,
+ off_t *offset);
+
+#ifdef CONFIG_ION_EXYNOS
+struct ion_handle *ion_exynos_get_user_pages(struct ion_client *client,
+ unsigned long uvaddr, size_t len, unsigned int flags);
+#else
+#include <linux/err.h>
+static inline struct ion_handle *ion_exynos_get_user_pages(
+ struct ion_client *client, unsigned long uvaddr,
+ size_t len, unsigned int flags)
+{
+ return ERR_PTR(-ENOSYS);
+}
+#endif
+
+#endif /* __KERNEL__ */
+
+/**
+ * DOC: Ion Userspace API
+ *
+ * create a client by opening /dev/ion
+ * most operations handled via following ioctls
+ *
+ */
+
+/**
+ * struct ion_allocation_data - metadata passed from userspace for allocations
+ * @len: size of the allocation
+ * @align: required alignment of the allocation
+ * @flags: flags passed to heap
+ * @handle: pointer that will be populated with a cookie to use to refer
+ * to this allocation
+ *
+ * Provided by userspace as an argument to the ioctl
+ */
+struct ion_allocation_data {
+ size_t len;
+ size_t align;
+ unsigned int flags;
+ struct ion_handle *handle;
+};
+
+/**
+ * struct ion_fd_data - metadata passed to/from userspace for a handle/fd pair
+ * @handle: a handle
+ * @fd: a file descriptor representing that handle
+ *
+ * For ION_IOC_SHARE or ION_IOC_MAP userspace populates the handle field with
+ * the handle returned from ion alloc, and the kernel returns the file
+ * descriptor to share or map in the fd field. For ION_IOC_IMPORT, userspace
+ * provides the file descriptor and the kernel returns the handle.
+ */
+struct ion_fd_data {
+ struct ion_handle *handle;
+ int fd;
+};
+
+/**
+ * struct ion_handle_data - a handle passed to/from the kernel
+ * @handle: a handle
+ */
+struct ion_handle_data {
+ struct ion_handle *handle;
+};
+
+/**
+ * struct ion_custom_data - metadata passed to/from userspace for a custom ioctl
+ * @cmd: the custom ioctl function to call
+ * @arg: additional data to pass to the custom ioctl, typically a user
+ * pointer to a predefined structure
+ *
+ * This works just like the regular cmd and arg fields of an ioctl.
+ */
+struct ion_custom_data {
+ unsigned int cmd;
+ unsigned long arg;
+};
+
+enum ION_MSYNC_TYPE {
+ IMSYNC_DEV_TO_READ = 0,
+ IMSYNC_DEV_TO_WRITE = 1,
+ IMSYNC_DEV_TO_RW = 2,
+ IMSYNC_BUF_TYPES_MASK = 3,
+ IMSYNC_BUF_TYPES_NUM = 4,
+ IMSYNC_SYNC_FOR_DEV = 0x10000,
+ IMSYNC_SYNC_FOR_CPU = 0x20000,
+};
+
+struct ion_msync_data {
+ enum ION_MSYNC_TYPE dir;
+ int fd_buffer;
+ size_t size;
+ off_t offset;
+};
+
+struct ion_phys_data {
+ int fd_buffer;
+ ion_phys_addr_t phys;
+ size_t size;
+};
+
+enum ION_EXYNOS_CUSTOM_CMD {
+ ION_EXYNOS_CUSTOM_MSYNC,
+ ION_EXYNOS_CUSTOM_PHYS
+};
+
+#define ION_IOC_MAGIC 'I'
+
+/**
+ * DOC: ION_IOC_ALLOC - allocate memory
+ *
+ * Takes an ion_allocation_data struct and returns it with the handle field
+ * populated with the opaque handle for the allocation.
+ */
+#define ION_IOC_ALLOC _IOWR(ION_IOC_MAGIC, 0, \
+ struct ion_allocation_data)
+
+/**
+ * DOC: ION_IOC_FREE - free memory
+ *
+ * Takes an ion_handle_data struct and frees the handle.
+ */
+#define ION_IOC_FREE _IOWR(ION_IOC_MAGIC, 1, struct ion_handle_data)
+
+/**
+ * DOC: ION_IOC_MAP - get a file descriptor to mmap
+ *
+ * Takes an ion_fd_data struct with the handle field populated with a valid
+ * opaque handle. Returns the struct with the fd field set to a file
+ * descriptor open in the current address space. This file descriptor
+ * can then be used as an argument to mmap.
+ */
+#define ION_IOC_MAP _IOWR(ION_IOC_MAGIC, 2, struct ion_fd_data)
+
+/**
+ * DOC: ION_IOC_SHARE - creates a file descriptor to use to share an allocation
+ *
+ * Takes an ion_fd_data struct with the handle field populated with a valid
+ * opaque handle. Returns the struct with the fd field set to a file
+ * descriptor open in the current address space. This file descriptor
+ * can then be passed to another process. The corresponding opaque handle can
+ * be retrieved via ION_IOC_IMPORT.
+ */
+#define ION_IOC_SHARE _IOWR(ION_IOC_MAGIC, 4, struct ion_fd_data)
+
+/**
+ * DOC: ION_IOC_IMPORT - imports a shared file descriptor
+ *
+ * Takes an ion_fd_data struct with the fd field populated with a valid file
+ * descriptor obtained from ION_IOC_SHARE and returns the struct with the handle
+ * filed set to the corresponding opaque handle.
+ */
+#define ION_IOC_IMPORT _IOWR(ION_IOC_MAGIC, 5, int)
+
+/**
+ * DOC: ION_IOC_CUSTOM - call architecture specific ion ioctl
+ *
+ * Takes the argument of the architecture specific ioctl to call and
+ * passes appropriate userdata for that ioctl
+ */
+#define ION_IOC_CUSTOM _IOWR(ION_IOC_MAGIC, 6, struct ion_custom_data)
+
+#endif /* _LINUX_ION_H */
diff --git a/camera/include/linux/fimc.h b/camera/include/linux/fimc.h
new file mode 100644
index 0000000..40deab0
--- /dev/null
+++ b/camera/include/linux/fimc.h
@@ -0,0 +1,381 @@
+/* linux/drivers/media/video/samsung/fimc/fimc.h
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * Header file for Samsung Camera Interface (FIMC) driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+
+#ifndef __FIMC_H
+#define __FIMC_H __FILE__
+
+typedef unsigned int dma_addr_t;
+typedef __u32 u32;
+typedef __s32 s32;
+
+#ifdef __KERNEL__
+#include <linux/wait.h>
+#include <linux/mutex.h>
+#include <linux/i2c.h>
+#include <linux/fb.h>
+#include <linux/videodev2.h>
+#include <linux/platform_device.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf-core.h>
+#include <media/v4l2-mediabus.h>
+#if defined(CONFIG_BUSFREQ_OPP) || defined(CONFIG_BUSFREQ_LOCK_WRAPPER)
+#include <mach/dev.h>
+#endif
+#include <plat/media.h>
+#include <plat/fimc.h>
+#include <plat/cpu.h>
+#endif
+
+#ifdef CONFIG_PM_RUNTIME
+#include <linux/pm_runtime.h>
+#endif
+
+#define FIMC_NAME "s3c-fimc"
+#define FIMC_CMA_NAME "fimc"
+
+#define FIMC_CORE_CLK "sclk_fimc"
+#define FIMC_CLK_RATE 166750000
+#define EXYNOS_BUSFREQ_NAME "exynos-busfreq"
+
+#if defined(CONFIG_ARCH_EXYNOS4)
+#define FIMC_DEVICES 4
+#define FIMC_PHYBUFS 32
+#define FIMC_MAXCAMS 7
+#else
+#define FIMC_DEVICES 3
+#define FIMC_PHYBUFS 4
+#define FIMC_MAXCAMS 5
+#endif
+
+#define FIMC_SUBDEVS 3
+#define FIMC_OUTBUFS 3
+#define FIMC_INQUEUES 10
+#define FIMC_MAX_CTXS 4
+#define FIMC_TPID 3
+#define FIMC_CAPBUFS 32
+#define FIMC_ONESHOT_TIMEOUT 200
+#define FIMC_DQUEUE_TIMEOUT 1000
+
+#define FIMC_FIFOOFF_CNT 1000000 /* Sufficiently big value for stop */
+
+#define FORMAT_FLAGS_PACKED 0x1
+#define FORMAT_FLAGS_PLANAR 0x2
+
+#define FIMC_ADDR_Y 0
+#define FIMC_ADDR_CB 1
+#define FIMC_ADDR_CR 2
+
+#define FIMC_HD_WIDTH 1280
+#define FIMC_HD_HEIGHT 720
+
+#define FIMC_FHD_WIDTH 1920
+#define FIMC_FHD_HEIGHT 1080
+
+#define FIMC_MMAP_IDX -1
+#define FIMC_USERPTR_IDX -2
+
+#define FIMC_HCLK 0
+#define FIMC_SCLK 1
+#define CSI_CH_0 0
+#define CSI_CH_1 1
+#if defined(CONFIG_VIDEO_FIMC_FIFO)
+#define FIMC_OVLY_MODE FIMC_OVLY_FIFO
+#elif defined(CONFIG_VIDEO_FIMC_DMA_AUTO)
+#define FIMC_OVLY_MODE FIMC_OVLY_DMA_AUTO
+#endif
+
+#define PINGPONG_2ADDR_MODE
+#if defined(PINGPONG_2ADDR_MODE)
+#define FIMC_PINGPONG 2
+#endif
+
+#define check_bit(data, loc) ((data) & (0x1<<(loc)))
+#define FRAME_SEQ 0xf
+
+#define fimc_cam_use ((pdata->use_cam) ? 1 : 0)
+
+#define L2_FLUSH_ALL SZ_1M
+#define L1_FLUSH_ALL SZ_64K
+
+/*
+ * ENUMERATIONS
+*/
+enum fimc_status {
+ FIMC_READY_OFF = 0x00,
+ FIMC_STREAMOFF = 0x01,
+ FIMC_READY_ON = 0x02,
+ FIMC_STREAMON = 0x03,
+ FIMC_STREAMON_IDLE = 0x04, /* oneshot mode */
+ FIMC_OFF_SLEEP = 0x05,
+ FIMC_ON_SLEEP = 0x06,
+ FIMC_ON_IDLE_SLEEP = 0x07, /* oneshot mode */
+ FIMC_READY_RESUME = 0x08,
+ FIMC_BUFFER_STOP = 0x09,
+ FIMC_BUFFER_START = 0x0A,
+};
+
+enum fimc_fifo_state {
+ FIFO_CLOSE,
+ FIFO_SLEEP,
+};
+
+enum fimc_fimd_state {
+ FIMD_OFF,
+ FIMD_ON,
+};
+
+enum fimc_rot_flip {
+ FIMC_XFLIP = 0x01,
+ FIMC_YFLIP = 0x02,
+ FIMC_ROT = 0x10,
+};
+
+enum fimc_input {
+ FIMC_SRC_CAM,
+ FIMC_SRC_MSDMA,
+};
+
+enum fimc_overlay_mode {
+ FIMC_OVLY_NOT_FIXED = 0x0, /* Overlay mode isn't fixed. */
+ FIMC_OVLY_FIFO = 0x1, /* Non-destructive Overlay with FIFO */
+ FIMC_OVLY_DMA_AUTO = 0x2, /* Non-destructive Overlay with DMA */
+ FIMC_OVLY_DMA_MANUAL = 0x3, /* Non-destructive Overlay with DMA */
+ FIMC_OVLY_NONE_SINGLE_BUF = 0x4, /* Destructive Overlay with DMA single destination buffer */
+ FIMC_OVLY_NONE_MULTI_BUF = 0x5, /* Destructive Overlay with DMA multiple dstination buffer */
+};
+
+enum fimc_autoload {
+ FIMC_AUTO_LOAD,
+ FIMC_ONE_SHOT,
+};
+
+enum fimc_log {
+ FIMC_LOG_DEBUG = 0x1000,
+ FIMC_LOG_INFO_L2 = 0x0200,
+ FIMC_LOG_INFO_L1 = 0x0100,
+ FIMC_LOG_WARN = 0x0010,
+ FIMC_LOG_ERR = 0x0001,
+};
+
+enum fimc_range {
+ FIMC_RANGE_NARROW = 0x0,
+ FIMC_RANGE_WIDE = 0x1,
+};
+
+enum fimc_pixel_format_type{
+ FIMC_RGB,
+ FIMC_YUV420,
+ FIMC_YUV422,
+ FIMC_YUV444,
+};
+
+enum fimc_framecnt_seq {
+ FIMC_FRAMECNT_SEQ_DISABLE,
+ FIMC_FRAMECNT_SEQ_ENABLE,
+};
+
+enum fimc_sysmmu_flag {
+ FIMC_SYSMMU_OFF,
+ FIMC_SYSMMU_ON,
+};
+
+enum fimc_id {
+ FIMC0 = 0x0,
+ FIMC1 = 0x1,
+ FIMC2 = 0x2,
+ FIMC3 = 0x3,
+};
+
+enum fimc_power_status {
+ FIMC_POWER_OFF,
+ FIMC_POWER_ON,
+ FIMC_POWER_SUSPEND,
+};
+
+enum cam_mclk_status {
+ CAM_MCLK_OFF,
+ CAM_MCLK_ON,
+};
+
+/*
+ * STRUCTURES
+*/
+
+/* for reserved memory */
+struct fimc_meminfo {
+ dma_addr_t base; /* buffer base */
+ size_t size; /* total length */
+ dma_addr_t curr; /* current addr */
+ dma_addr_t vaddr_base; /* buffer base */
+ dma_addr_t vaddr_curr; /* current addr */
+};
+
+struct fimc_buf {
+ dma_addr_t base[3];
+ size_t length[3];
+};
+
+struct fimc_overlay_buf {
+ u32 vir_addr[3];
+ size_t size[3];
+ u32 phy_addr[3];
+};
+
+struct fimc_overlay {
+ enum fimc_overlay_mode mode;
+ struct fimc_overlay_buf buf;
+ s32 req_idx;
+};
+
+/* for output overlay device */
+struct fimc_idx {
+ int ctx;
+ int idx;
+};
+
+struct fimc_ctx_idx {
+ struct fimc_idx prev;
+ struct fimc_idx active;
+ struct fimc_idx next;
+};
+
+/* scaler abstraction: local use recommended */
+struct fimc_scaler {
+ u32 bypass;
+ u32 hfactor;
+ u32 vfactor;
+ u32 pre_hratio;
+ u32 pre_vratio;
+ u32 pre_dst_width;
+ u32 pre_dst_height;
+ u32 scaleup_h;
+ u32 scaleup_v;
+ u32 main_hratio;
+ u32 main_vratio;
+ u32 real_width;
+ u32 real_height;
+ u32 shfactor;
+ u32 skipline;
+};
+
+struct s3cfb_user_window {
+ int x;
+ int y;
+};
+
+enum s3cfb_data_path_t {
+ DATA_PATH_FIFO = 0,
+ DATA_PATH_DMA = 1,
+ DATA_PATH_IPC = 2,
+};
+
+enum s3cfb_mem_owner_t {
+ DMA_MEM_NONE = 0,
+ DMA_MEM_FIMD = 1,
+ DMA_MEM_OTHER = 2,
+};
+#define S3CFB_WIN_OFF_ALL _IO('F', 202)
+#define S3CFB_WIN_POSITION _IOW('F', 203, struct s3cfb_user_window)
+#define S3CFB_GET_LCD_WIDTH _IOR('F', 302, int)
+#define S3CFB_GET_LCD_HEIGHT _IOR('F', 303, int)
+#define S3CFB_SET_WRITEBACK _IOW('F', 304, u32)
+#define S3CFB_SET_WIN_ON _IOW('F', 305, u32)
+#define S3CFB_SET_WIN_OFF _IOW('F', 306, u32)
+#define S3CFB_SET_WIN_PATH _IOW('F', 307, enum s3cfb_data_path_t)
+#define S3CFB_SET_WIN_ADDR _IOW('F', 308, unsigned long)
+#define S3CFB_SET_WIN_MEM _IOW('F', 309, enum s3cfb_mem_owner_t)
+/* ------------------------------------------------------------------------ */
+
+struct fimc_fbinfo {
+ struct fb_fix_screeninfo *fix;
+ struct fb_var_screeninfo *var;
+ int lcd_hres;
+ int lcd_vres;
+ u32 is_enable;
+ /* lcd fifo control */
+
+ int (*open_fifo)(int id, int ch, int (*do_priv)(void *), void *param);
+ int (*close_fifo)(int id, int (*do_priv)(void *), void *param);
+};
+
+struct fimc_limit {
+ u32 pre_dst_w;
+ u32 bypass_w;
+ u32 trg_h_no_rot;
+ u32 trg_h_rot;
+ u32 real_w_no_rot;
+ u32 real_h_rot;
+};
+
+enum FIMC_EFFECT_FIN {
+ FIMC_EFFECT_FIN_BYPASS = 0,
+ FIMC_EFFECT_FIN_ARBITRARY_CBCR,
+ FIMC_EFFECT_FIN_NEGATIVE,
+ FIMC_EFFECT_FIN_ART_FREEZE,
+ FIMC_EFFECT_FIN_EMBOSSING,
+ FIMC_EFFECT_FIN_SILHOUETTE,
+};
+
+
+struct fimc_effect {
+ int ie_on;
+ int ie_after_sc;
+ enum FIMC_EFFECT_FIN fin;
+ int pat_cb;
+ int pat_cr;
+};
+
+/* debug macro */
+#define FIMC_LOG_DEFAULT (FIMC_LOG_WARN | FIMC_LOG_ERR)
+
+#define FIMC_DEBUG(fmt, ...) \
+ do { \
+ printk(KERN_DEBUG FIMC_NAME "%d: " \
+ fmt, ctrl->id, ##__VA_ARGS__); \
+ } while (0)
+
+#define FIMC_INFO_L2(fmt, ...) \
+ do { \
+ printk(KERN_INFO FIMC_NAME "%d: " \
+ fmt, ctrl->id, ##__VA_ARGS__); \
+ } while (0)
+
+#define FIMC_INFO_L1(fmt, ...) \
+ do { \
+ printk(KERN_INFO FIMC_NAME "%d: " \
+ fmt, ctrl->id, ##__VA_ARGS__); \
+ } while (0)
+
+#define FIMC_WARN(fmt, ...) \
+ do { \
+ printk(KERN_WARNING FIMC_NAME "%d: " \
+ fmt, ctrl->id, ##__VA_ARGS__); \
+ } while (0)
+
+
+#define FIMC_ERROR(fmt, ...) \
+ do { \
+ printk(KERN_ERR FIMC_NAME "%d: " \
+ fmt, ctrl->id, ##__VA_ARGS__); \
+ } while (0)
+
+
+#define fimc_dbg(fmt, ...) FIMC_DEBUG(fmt, ##__VA_ARGS__)
+#define fimc_info2(fmt, ...) FIMC_INFO_L2(fmt, ##__VA_ARGS__)
+#define fimc_info1(fmt, ...) FIMC_INFO_L1(fmt, ##__VA_ARGS__)
+#define fimc_warn(fmt, ...) FIMC_WARN(fmt, ##__VA_ARGS__)
+#define fimc_err(fmt, ...) FIMC_ERROR(fmt, ##__VA_ARGS__)
+
+#endif /* __FIMC_H */
diff --git a/camera/include/s5c73m3.h b/camera/include/s5c73m3.h
new file mode 100644
index 0000000..ffb582e
--- /dev/null
+++ b/camera/include/s5c73m3.h
@@ -0,0 +1,369 @@
+/*
+ * Driver for LSI S5C73M3 (ISP for 8MP Camera)
+ *
+ * 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 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __S5C73M3_H
+#define __S5C73M3_H
+
+#define CONFIG_CAM_DEBUG 1
+/*#define FEATURE_DEBUG_DUMP*/
+
+#define cam_warn(fmt, ...) \
+ do { \
+ printk(KERN_WARNING "%s: " fmt, __func__, ##__VA_ARGS__); \
+ } while (0)
+
+#define cam_err(fmt, ...) \
+ do { \
+ printk(KERN_ERR "%s: " fmt, __func__, ##__VA_ARGS__); \
+ } while (0)
+
+#define cam_info(fmt, ...) \
+ do { \
+ printk(KERN_INFO "%s: " fmt, __func__, ##__VA_ARGS__); \
+ } while (0)
+
+#ifdef CONFIG_CAM_DEBUG
+#define CAM_DEBUG (1 << 0)
+#define CAM_TRACE (1 << 1)
+#define CAM_I2C (1 << 2)
+
+#define cam_dbg(fmt, ...) \
+ do { \
+ if (to_state(sd)->dbg_level & CAM_DEBUG) \
+ printk(KERN_DEBUG "%s: " fmt, __func__, ##__VA_ARGS__); \
+ } while (0)
+
+#define cam_trace(fmt, ...) \
+ do { \
+ if (to_state(sd)->dbg_level & CAM_TRACE) \
+ printk(KERN_DEBUG "%s: " fmt, __func__, ##__VA_ARGS__); \
+ } while (0)
+
+#define cam_i2c_dbg(fmt, ...) \
+ do { \
+ if (to_state(sd)->dbg_level & CAM_I2C) \
+ printk(KERN_DEBUG "%s: " fmt, __func__, ##__VA_ARGS__); \
+ } while (0)
+#else
+#define cam_dbg(fmt, ...)
+#define cam_trace(fmt, ...)
+#define cam_i2c_dbg(fmt, ...)
+#endif
+
+enum s5c73m3_fw_path{
+ S5C73M3_SD_CARD,
+ S5C73M3_IN_DATA,
+ S5C73M3_IN_SYSTEM,
+ S5C73M3_PATH_MAX,
+};
+
+enum s5c73m3_prev_frmsize {
+ S5C73M3_PREVIEW_QVGA,
+ S5C73M3_PREVIEW_CIF,
+ S5C73M3_PREVIEW_VGA,
+ S5C73M3_PREVIEW_D1,
+ S5C73M3_PREVIEW_800X600,
+ S5C73M3_PREVIEW_880X720,
+ S5C73M3_PREVIEW_960X720,
+ S5C73M3_PREVIEW_1008X672,
+ S5C73M3_PREVIEW_1184X666,
+ S5C73M3_PREVIEW_720P,
+ S5C73M3_PREVIEW_1280X960,
+ S5C73M3_VDIS_720P,
+ S5C73M3_PREVIEW_1080P,
+ S5C73M3_VDIS_1080P,
+};
+
+enum s5c73m3_cap_frmsize {
+ S5C73M3_CAPTURE_VGA, /* 640 x 480 */
+ S5C73M3_CAPTURE_960x540, /* 960 x 540 */
+ S5C73M3_CAPTURE_960x720, /* 960 x 720 */
+ S5C73M3_CAPTURE_1024X768, /* 1024 x 768 */
+ S5C73M3_CAPTURE_HD, /* 1280 x 720 */
+ S5C73M3_CAPTURE_2MP, /* UXGA - 1600 x 1200 */
+ S5C73M3_CAPTURE_W2MP, /* 2048 x 1232 */
+ S5C73M3_CAPTURE_3MP, /* QXGA - 2048 x 1536 */
+ S5C73M3_CAPTURE_5MP, /* 2560 x 1920 */
+ S5C73M3_CAPTURE_W6MP, /* 3072 x 1856 */
+ S5C73M3_CAPTURE_3264X2176, /* 3264 x 2176 */
+ S5C73M3_CAPTURE_8MP, /* 3264 x 2448 */
+};
+
+enum s5c73m3_isneed_flash_tristate {
+ S5C73M3_ISNEED_FLASH_OFF = 0x00,
+ S5C73M3_ISNEED_FLASH_ON = 0x01,
+ S5C73M3_ISNEED_FLASH_UNDEFINED = 0x02,
+};
+
+#define S5C73M3_IMG_OUTPUT 0x0902
+#define S5C73M3_HDR_OUTPUT 0x0008
+#define S5C73M3_YUV_OUTPUT 0x0009
+#define S5C73M3_INTERLEAVED_OUTPUT 0x000D
+#define S5C73M3_HYBRID_OUTPUT 0x0016
+
+#define S5C73M3_STILL_PRE_FLASH 0x0A00
+#define S5C73M3_STILL_PRE_FLASH_FIRE 0x0000
+#define S5C73M3_STILL_PRE_FLASH_NON_FIRED 0x0000
+#define S5C73M3_STILL_PRE_FLASH_FIRED 0x0001
+
+#define S5C73M3_STILL_MAIN_FLASH 0x0A02
+#define S5C73M3_STILL_MAIN_FLASH_CANCEL 0x0001
+#define S5C73M3_STILL_MAIN_FLASH_FIRE 0x0002
+
+
+#define S5C73M3_ZOOM_STEP 0x0B00
+
+
+#define S5C73M3_IMAGE_EFFECT 0x0B0A
+#define S5C73M3_IMAGE_EFFECT_NONE 0x0001
+#define S5C73M3_IMAGE_EFFECT_NEGATIVE 0x0002
+#define S5C73M3_IMAGE_EFFECT_AQUA 0x0003
+#define S5C73M3_IMAGE_EFFECT_SEPIA 0x0004
+#define S5C73M3_IMAGE_EFFECT_MONO 0x0005
+#define S5C73M3_IMAGE_EFFECT_SKETCH 0x0006
+#define S5C73M3_IMAGE_EFFECT_WASHED 0x0007
+#define S5C73M3_IMAGE_EFFECT_VINTAGE_WARM 0x0008
+#define S5C73M3_IMAGE_EFFECT_VINTAGE_COLD 0x0009
+#define S5C73M3_IMAGE_EFFECT_SOLARIZE 0x000A
+#define S5C73M3_IMAGE_EFFECT_POSTERIZE 0x000B
+#define S5C73M3_IMAGE_EFFECT_POINT_BLUE 0x000C
+#define S5C73M3_IMAGE_EFFECT_POINT_RED_YELLOW 0x000D
+#define S5C73M3_IMAGE_EFFECT_POINT_COLOR_3 0x000E
+#define S5C73M3_IMAGE_EFFECT_POINT_GREEN 0x000F
+#define S5C73M3_IMAGE_EFFECT_CARTOONIZE 0x001A
+
+#define S5C73M3_IMAGE_QUALITY 0x0B0C
+#define S5C73M3_IMAGE_QUALITY_SUPERFINE 0x0000
+#define S5C73M3_IMAGE_QUALITY_FINE 0x0001
+#define S5C73M3_IMAGE_QUALITY_NORMAL 0x0002
+
+
+#define S5C73M3_FLASH_MODE 0x0B0E
+#define S5C73M3_FLASH_MODE_OFF 0x0000
+#define S5C73M3_FLASH_MODE_ON 0x0001
+#define S5C73M3_FLASH_MODE_AUTO 0x0002
+
+#define S5C73M3_FLASH_TORCH 0x0B12
+#define S5C73M3_FLASH_TORCH_OFF 0x0000
+#define S5C73M3_FLASH_TORCH_ON 0x0001
+
+#define S5C73M3_AE_ISNEEDFLASH 0x0CBA
+#define S5C73M3_AE_ISNEEDFLASH_OFF 0x0000
+#define S5C73M3_AE_ISNEEDFLASH_ON 0x0001
+
+
+#define S5C73M3_CHG_MODE 0x0B10
+#define S5C73M3_DEFAULT_MODE 0x8000
+#define S5C73M3_FAST_MODE_SUBSAMPLING_HALF 0xA000
+#define S5C73M3_FAST_MODE_SUBSAMPLING_QUARTER 0xC000
+
+#define S5C73M3_AF_CON 0x0E00
+#define S5C73M3_AF_CON_STOP 0x0000
+#define S5C73M3_AF_CON_SCAN 0x0001/*AF_SCAN:Full Search*/
+#define S5C73M3_AF_CON_START 0x0002/*AF_START:Fast Search*/
+
+#define S5C73M3_AF_STATUS 0x5E80
+
+#define S5C73M3_AF_TOUCH_AF 0x0E0A
+
+#define S5C73M3_AF_CAL 0x0E06
+
+#define S5C73M3_CAF_STATUS_FIND_SEARCHING_DIR 0x0001
+#define S5C73M3_CAF_STATUS_FOCUSING 0x0002
+#define S5C73M3_CAF_STATUS_FOCUSED 0x0003
+#define S5C73M3_CAF_STATUS_UNFOCUSED 0x0004
+
+#define S5C73M3_AF_STATUS_INVALID 0x0010
+#define S5C73M3_AF_STATUS_FOCUSING 0x0020
+#define S5C73M3_AF_STATUS_FOCUSED 0x0030/*SUCCESS*/
+#define S5C73M3_AF_STATUS_UNFOCUSED 0x0040/*FAIL*/
+
+#define S5C73M3_AF_TOUCH_POSITION 0x5E8E
+
+#define S5C73M3_AF_FACE_ZOOM 0x0E10
+
+#define S5C73M3_AF_MODE 0x0E02
+#define S5C73M3_AF_MODE_NORMAL 0x0000
+#define S5C73M3_AF_MODE_MACRO 0x0001
+#define S5C73M3_AF_MODE_MOVIE_CAF_START 0x0002
+#define S5C73M3_AF_MODE_MOVIE_CAF_STOP 0x0003
+#define S5C73M3_AF_MODE_PREVIEW_CAF_START 0x0004
+#define S5C73M3_AF_MODE_PREVIEW_CAF_STOP 0x0005
+
+#define S5C73M3_AF_SOFTLANDING 0x0E16
+#define S5C73M3_AF_SOFTLANDING_ON 0x0000
+
+#define S5C73M3_FACE_DET 0x0E0C
+#define S5C73M3_FACE_DET_OFF 0x0000
+#define S5C73M3_FACE_DET_ON 0x0001
+
+#define S5C73M3_FACE_DET_OSD 0x0E0E
+#define S5C73M3_FACE_DET_OSD_OFF 0x0000
+#define S5C73M3_FACE_DET_OSD_ON 0x0001
+
+#define S5C73M3_AE_CON 0x0C00
+#define S5C73M3_AE_STOP 0x0000/*LOCK*/
+#define S5C73M3_AE_START 0x0001/*UNLOCK*/
+
+#define S5C73M3_ISO 0x0C02
+#define S5C73M3_ISO_AUTO 0x0000
+#define S5C73M3_ISO_100 0x0001
+#define S5C73M3_ISO_200 0x0002
+#define S5C73M3_ISO_400 0x0003
+#define S5C73M3_ISO_800 0x0004
+#define S5C73M3_ISO_SPORTS 0x0005
+#define S5C73M3_ISO_NIGHT 0x0006
+#define S5C73M3_ISO_INDOOR 0x0007
+
+#define S5C73M3_EV 0x0C04
+#define S5C73M3_EV_M20 0x0000
+#define S5C73M3_EV_M15 0x0001
+#define S5C73M3_EV_M10 0x0002
+#define S5C73M3_EV_M05 0x0003
+#define S5C73M3_EV_ZERO 0x0004
+#define S5C73M3_EV_P05 0x0005
+#define S5C73M3_EV_P10 0x0006
+#define S5C73M3_EV_P15 0x0007
+#define S5C73M3_EV_P20 0x0008
+
+#define S5C73M3_METER 0x0C06
+#define S5C73M3_METER_CENTER 0x0000
+#define S5C73M3_METER_SPOT 0x0001
+#define S5C73M3_METER_AVERAGE 0x0002
+#define S5C73M3_METER_SMART 0x0003
+
+#define S5C73M3_WDR 0x0C08
+#define S5C73M3_WDR_OFF 0x0000
+#define S5C73M3_WDR_ON 0x0001
+
+#define S5C73M3_FLICKER_MODE 0x0C12
+#define S5C73M3_FLICKER_NONE 0x0000
+#define S5C73M3_FLICKER_MANUAL_50HZ 0x0001
+#define S5C73M3_FLICKER_MANUAL_60HZ 0x0002
+#define S5C73M3_FLICKER_AUTO 0x0003
+#define S5C73M3_FLICKER_AUTO_50HZ 0x0004
+#define S5C73M3_FLICKER_AUTO_60HZ 0x0005
+
+#define S5C73M3_AE_MODE 0x0C1E
+#define S5C73M3_AUTO_MODE_AE_SET 0x0000
+#define S5C73M3_FIXED_30FPS 0x0002
+#define S5C73M3_FIXED_20FPS 0x0003
+#define S5C73M3_FIXED_15FPS 0x0004
+#define S5C73M3_FIXED_60FPS 0x0007
+#define S5C73M3_FIXED_120FPS 0x0008
+#define S5C73M3_FIXED_7FPS 0x0009
+#define S5C73M3_FIXED_10FPS 0x000A
+#define S5C73M3_FIXED_90FPS 0x000B
+#define S5C73M3_ANTI_SHAKE 0x0013
+
+#define S5C73M3_SHARPNESS 0x0C14
+#define S5C73M3_SHARPNESS_0 0x0000
+#define S5C73M3_SHARPNESS_1 0x0001
+#define S5C73M3_SHARPNESS_2 0x0002
+#define S5C73M3_SHARPNESS_M1 0x0003
+#define S5C73M3_SHARPNESS_M2 0x0004
+
+#define S5C73M3_SATURATION 0x0C16
+#define S5C73M3_SATURATION_0 0x0000
+#define S5C73M3_SATURATION_1 0x0001
+#define S5C73M3_SATURATION_2 0x0002
+#define S5C73M3_SATURATION_M1 0x0003
+#define S5C73M3_SATURATION_M2 0x0004
+
+#define S5C73M3_CONTRAST 0x0C18
+#define S5C73M3_CONTRAST_0 0x0000
+#define S5C73M3_CONTRAST_1 0x0001
+#define S5C73M3_CONTRAST_2 0x0002
+#define S5C73M3_CONTRAST_M1 0x0003
+#define S5C73M3_CONTRAST_M2 0x0004
+
+#define S5C73M3_SCENE_MODE 0x0C1A
+#define S5C73M3_SCENE_MODE_NONE 0x0000
+#define S5C73M3_SCENE_MODE_PORTRAIT 0x0001
+#define S5C73M3_SCENE_MODE_LANDSCAPE 0x0002
+#define S5C73M3_SCENE_MODE_SPORTS 0x0003
+#define S5C73M3_SCENE_MODE_INDOOR 0x0004
+#define S5C73M3_SCENE_MODE_BEACH 0x0005
+#define S5C73M3_SCENE_MODE_SUNSET 0x0006
+#define S5C73M3_SCENE_MODE_DAWN 0x0007
+#define S5C73M3_SCENE_MODE_FALL 0x0008
+#define S5C73M3_SCENE_MODE_NIGHT 0x0009
+#define S5C73M3_SCENE_MODE_AGAINSTLIGHT 0x000A
+#define S5C73M3_SCENE_MODE_FIRE 0x000B
+#define S5C73M3_SCENE_MODE_TEXT 0x000C
+#define S5C73M3_SCENE_MODE_CANDLE 0x000D
+#define S5C73M3_SCENE_MODE_LOW_LIGHT 0x0020
+
+#define S5C73M3_FIREWORK_CAPTURE 0x0C20
+#define S5C73M3_NIGHTSHOT_CAPTURE 0x0C22
+
+#define S5C73M3_AE_LOW_LIGHT_MODE 0x0C2C
+
+#define S5C73M3_AE_AUTO_BRAKET 0x0B14
+#define S5C73M3_AE_AUTO_BRAKET_EV05 0x0080
+#define S5C73M3_AE_AUTO_BRAKET_EV10 0x0100
+#define S5C73M3_AE_AUTO_BRAKET_EV15 0x0180
+#define S5C73M3_AE_AUTO_BRAKET_EV20 0x0200
+
+#define S5C73M3_SENSOR_STREAMING 0x090A
+#define S5C73M3_SENSOR_STREAMING_OFF 0x0000
+#define S5C73M3_SENSOR_STREAMING_ON 0x0001
+
+#define S5C73M3_AWB_MODE 0x0D02
+#define S5C73M3_AWB_MODE_INCANDESCENT 0x0000
+#define S5C73M3_AWB_MODE_FLUORESCENT1 0x0001
+#define S5C73M3_AWB_MODE_FLUORESCENT2 0x0002
+#define S5C73M3_AWB_MODE_DAYLIGHT 0x0003
+#define S5C73M3_AWB_MODE_CLOUDY 0x0004
+#define S5C73M3_AWB_MODE_AUTO 0x0005
+
+#define S5C73M3_AWB_CON 0x0D00
+#define S5C73M3_AWB_STOP 0x0000/*LOCK*/
+#define S5C73M3_AWB_START 0x0001/*UNLOCK*/
+
+#define S5C73M3_HYBRID_CAPTURE 0x0996
+
+#define S5C73M3_STATUS 0x5080
+#define BOOT_SUB_MAIN_ENTER 0xFF01
+#define BOOT_SRAM_TIMING_OK 0xFF02
+#define BOOT_INTERRUPTS_ENABLE 0xFF03
+#define BOOT_R_PLL_DONE 0xFF04
+#define BOOT_R_PLL_LOCKTIME_DONE 0xFF05
+#define BOOT_DELAY_COUNT_DONE 0xFF06
+#define BOOT_I_PLL_DONE 0xFF07
+#define BOOT_I_PLL_LOCKTIME_DONE 0xFF08
+#define BOOT_PLL_INIT_OK 0xFF09
+#define BOOT_SENSOR_INIT_OK 0xFF0A
+#define BOOT_GPIO_SETTING_OK 0xFF0B
+#define BOOT_READ_CAL_DATA_OK 0xFF0C
+#define BOOT_STABLE_AE_AWB_OK 0xFF0D
+#define EXCEPTION_OCCURED 0xDEAD
+
+#define S5C73M3_I2C_SEQ_STATUS 0x59A6
+#define SEQ_END_PLL (1<<0x0)
+#define SEQ_END_SENSOR (1<<0x1)
+#define SEQ_END_GPIO (1<<0x2)
+#define SEQ_END_FROM (1<<0x3)
+#define SEQ_END_STABLE_AE_AWB (1<<0x4)
+#define SEQ_END_READY_I2C_CMD (1<<0x5)
+
+#define S5C73M3_I2C_ERR_STATUS 0x599E
+#define ERR_STATUS_CIS_I2C (1<<0x0)
+#define ERR_STATUS_AF_INIT (1<<0x1)
+#define ERR_STATUS_CAL_DATA (1<<0x2)
+#define ERR_STATUS_FRAME_COUNT (1<<0x3)
+#define ERR_STATUS_FROM_INIT (1<<0x4)
+#define ERR_STATUS_I2C_CIS_STREAM_OFF (1<<0x5)
+#define ERR_STATUS_I2C_N_CMD_OVER (1<<0x6)
+#define ERROR_STATUS_I2C_N_CMD_MISMATCH (1<<0x7)
+#define ERROR_STATUS_CHECK_BIN_CRC (1<<0x8)
+#define ERROR_STATUS_EXCEPTION (1<<0x9)
+#define ERROR_STATUS_INIF_INIT_STATE (0x8)
+
+#endif /* __S5C73M3_H */
diff --git a/camera/smdk4x12_camera.c b/camera/smdk4x12_camera.c
new file mode 100644
index 0000000..3a525a9
--- /dev/null
+++ b/camera/smdk4x12_camera.c
@@ -0,0 +1,4491 @@
+/*
+ * Copyright (C) 2013-2014 Paul Kocialkowski <contact@paulk.fr>
+ *
+ * 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>
+
+#define LOG_TAG "smdk4x12_camera"
+#include <utils/Log.h>
+#include <utils/Timers.h>
+
+#include "smdk4x12_camera.h"
+
+#define BIG2LITTLE_ENDIAN(big) ((big & 0xff) << 24 | (big & 0xff00) << 8 | (big & 0xff0000) >> 8 | (big & 0xff000000) >> 24)
+
+/*
+ * Devices configurations
+ */
+
+struct smdk4x12_camera_mbus_resolution smdk4x12_camera_mbus_resolutions_s5k6a3_galaxys3[] = {
+ // 16:9 ratio
+ { 1280, 720, 1344, 756 },
+ // 4:3 ratio
+ { 1280, 960, 1392, 1044 },
+ { 960, 720, 1392, 1044 },
+ { 640, 480, 1392, 1044 },
+ { 320, 240, 1392, 1044 },
+ // 1:1 ratio
+ { 1392, 1392, 1392, 1392 },
+ { 704, 704, 1392, 1392 },
+ { 320, 320, 1392, 1392 },
+};
+
+struct smdk4x12_camera_preset smdk4x12_camera_presets_galaxys3[] = {
+ {
+ .name = "S5C73M3",
+ .facing = CAMERA_FACING_BACK,
+ .orientation = 90,
+ .rotation = 0,
+ .hflip = 0,
+ .vflip = 0,
+ .capture_format = V4L2_PIX_FMT_INTERLEAVED,
+ .picture_format = 0,
+ .fimc_is = 0,
+ .focal_length = 3.7f,
+ .horizontal_view_angle = 63.0f,
+ .vertical_view_angle = 49.3f,
+ .metering = METERING_CENTER,
+ .params = {
+ .preview_size_values = "960x720,1280x720,1184x666,960x640,704x576,640x480,352x288,320x240",
+ .preview_size = "960x720",
+ .preview_format_values = "yuv420sp,yuv420p,rgb565",
+ .preview_format = "yuv420sp",
+ .preview_frame_rate_values = "30,20,15",
+ .preview_frame_rate = 30,
+ .preview_fps_range_values = "(15000,15000),(15000,30000),(30000,30000)",
+ .preview_fps_range = "15000,30000",
+
+ .picture_size_values = "640x480,1024x768,1280x720,1600x1200,2560x1920,3264x2448,2048x1536,3264x1836,2048x1152,3264x2176",
+ .picture_size = "3264x2448",
+ .picture_format_values = "jpeg",
+ .picture_format = "jpeg",
+ .jpeg_thumbnail_size_values = "160x120,160x90,144x96",
+ .jpeg_thumbnail_width = 160,
+ .jpeg_thumbnail_height = 120,
+ .jpeg_thumbnail_quality = 100,
+ .jpeg_quality = 90,
+
+ .video_snapshot_supported = 0,
+ .full_video_snap_supported = 0,
+
+ .recording_size = "1280x720",
+ .recording_size_values = "1280x720,1920x1080,720x480,640x480,352x288,320x240,176x144",
+ .recording_format = "yuv420sp",
+
+ .focus_mode = "continuous-picture",
+ .focus_mode_values = "auto,infinity,macro,fixed,continuous-picture,continuous-video",
+ .focus_distances = "0.15,1.20,Infinity",
+ .focus_areas = NULL,
+ .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,
+
+ .auto_exposure_lock_supported = 1,
+ .auto_exposure_lock = 0,
+
+ .auto_white_balance_lock_supported = 1,
+ .auto_white_balance_lock = 0,
+
+ .flash_mode = "auto",
+ .flash_mode_values = "auto,on,torch,off",
+
+ .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",
+
+ .antibanding = "auto",
+ .antibanding_values = "auto,60hz,50hz,off",
+
+ .scene_mode = "auto",
+ .scene_mode_values = "auto,portrait,landscape,night,beach,snow,sunset,fireworks,sports,party,candlelight,dusk-dawn,fall-color,text,back-light",
+
+ .effect = "none",
+ .effect_values = "none,mono,negative,sepia,solarize,posterize,washed,vintage-warm,vintage-cold,point-blue,point-red-yellow,point-green",
+
+ .iso = "auto",
+ .iso_values = "auto,ISO100,ISO200,ISO400,ISO800",
+
+ .image_stabilization = "off",
+ .image_stabilization_values = "on,off",
+ },
+ .mbus_resolutions = NULL,
+ .mbus_resolutions_count = 0,
+ },
+ {
+ .name = "S5K6A3",
+ .facing = CAMERA_FACING_FRONT,
+ .orientation = 270,
+ .rotation = 0,
+ .hflip = 0,
+ .vflip = 0,
+ .capture_format = 0,
+ .picture_format = V4L2_PIX_FMT_YUYV,
+ .fimc_is = 1,
+ .focal_length = 2.73f,
+ .horizontal_view_angle = 52.58f,
+ .vertical_view_angle = 52.58f,
+ .metering = METERING_CENTER,
+ .params = {
+ .preview_size_values = "1280x720,960x720,640x480,320x240,704x704,320x320",
+ .preview_size = "960x720",
+ .preview_format_values = "yuv420sp,yuv420p,rgb565",
+ .preview_format = "yuv420sp",
+ .preview_frame_rate_values = "30,20,15,8",
+ .preview_frame_rate = 30,
+ .preview_fps_range_values = "(8000,8000),(15000,15000),(15000,30000),(30000,30000)",
+ .preview_fps_range = "15000,30000",
+
+ .picture_size_values = "1344x756,1280x720,1392x1044,1280x960,960x720,640x480,1392x1392",
+ .picture_size = "1280x960",
+ .picture_format_values = "jpeg",
+ .picture_format = "jpeg",
+ .jpeg_thumbnail_size_values = "160x120,160x160,160x90,144x96",
+ .jpeg_thumbnail_width = 160,
+ .jpeg_thumbnail_height = 120,
+ .jpeg_thumbnail_quality = 100,
+ .jpeg_quality = 90,
+
+ .video_snapshot_supported = 0,
+ .full_video_snap_supported = 0,
+
+ .recording_size = "1280x720",
+ .recording_size_values = "1280x720,720x480,640x480,352x288,320x320,320x240,176x144",
+ .recording_format = "yuv420sp",
+
+ .focus_mode = "fixed",
+ .focus_mode_values = "infinity,fixed",
+ .focus_distances = "0.20,0.25,Infinity",
+ .focus_areas = NULL,
+ .max_num_focus_areas = 0,
+
+ .zoom_supported = 0,
+
+ .auto_exposure_lock_supported = 0,
+ .auto_exposure_lock = 0,
+
+ .auto_white_balance_lock_supported = 0,
+ .auto_white_balance_lock = 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 = "auto",
+ .whitebalance_values = "auto,incandescent,fluorescent,daylight,cloudy-daylight",
+
+ .antibanding = NULL,
+ .antibanding_values = NULL,
+
+ .scene_mode = NULL,
+ .scene_mode_values = NULL,
+
+ .effect = "none",
+ .effect_values = "none,mono,negative,sepia,solarize,posterize,washed,vintage-warm,vintage-cold,point-blue,point-red-yellow,point-green",
+
+ .iso = "auto",
+ .iso_values = "auto",
+
+ .image_stabilization = "off",
+ .image_stabilization_values = "off",
+ },
+ .mbus_resolutions = (struct smdk4x12_camera_mbus_resolution *) &smdk4x12_camera_mbus_resolutions_s5k6a3_galaxys3,
+ .mbus_resolutions_count = 8,
+ },
+};
+
+struct smdk4x12_v4l2_node smdk4x12_v4l2_nodes_galaxys3[] = {
+ { // FIMC0 is used for capture
+ .id = 0,
+ .node = "/dev/video0",
+ },
+ { // FIMC1 is used for preview output
+ .id = 1,
+ .node = "/dev/video1",
+ },
+ { // FIMC2 is used for picture output
+ .id = 2,
+ .node = "/dev/video2",
+ },
+ { // FIMC3 is used for recording output
+ .id = 3,
+ .node = "/dev/video3",
+ },
+};
+
+struct exynox_camera_config smdk4x12_camera_config_galaxys3 = {
+ .presets = (struct smdk4x12_camera_preset *) &smdk4x12_camera_presets_galaxys3,
+ .presets_count = 2,
+ .v4l2_nodes = (struct smdk4x12_v4l2_node *) &smdk4x12_v4l2_nodes_galaxys3,
+ .v4l2_nodes_count = 4,
+};
+
+/*
+ * Exynos Camera
+ */
+
+struct exynox_camera_config *smdk4x12_camera_config =
+ &smdk4x12_camera_config_galaxys3;
+
+int smdk4x12_camera_start(struct smdk4x12_camera *smdk4x12_camera, int id)
+{
+ int rc;
+
+ if (smdk4x12_camera == NULL || id >= smdk4x12_camera->config->presets_count)
+ return -EINVAL;
+
+ // ION
+
+#ifdef EXYNOS_ION
+ rc = smdk4x12_ion_init(smdk4x12_camera);
+ if (rc < 0) {
+ ALOGE("%s: Unable to init ION", __func__);
+ goto error;
+ }
+
+ rc = smdk4x12_ion_open(smdk4x12_camera);
+ if (rc < 0) {
+ ALOGE("%s: Unable to open ION", __func__);
+ goto error;
+ }
+#endif
+
+ // V4L2
+
+ rc = smdk4x12_v4l2_init(smdk4x12_camera);
+ if (rc < 0) {
+ ALOGE("%s: Unable to init v4l2", __func__);
+ goto error;
+ }
+
+ // FIMC0
+
+ rc = smdk4x12_v4l2_open(smdk4x12_camera, 0);
+ if (rc < 0) {
+ ALOGE("%s: Unable to open v4l2 device", __func__);
+ goto error;
+ }
+
+ rc = smdk4x12_v4l2_querycap_cap(smdk4x12_camera, 0);
+ if (rc < 0) {
+ ALOGE("%s: Unable to query capabilities", __func__);
+ goto error;
+ }
+
+ rc = smdk4x12_v4l2_enum_input(smdk4x12_camera, 0, id);
+ if (rc < 0) {
+ ALOGE("%s: Unable to enumerate input", __func__);
+ goto error;
+ }
+
+ rc = smdk4x12_v4l2_s_input(smdk4x12_camera, 0, id);
+ if (rc < 0) {
+ ALOGE("%s: Unable to set inputs", __func__);
+ goto error;
+ }
+
+ // Recording
+
+ smdk4x12_camera->recording_metadata = 1;
+
+ // Params
+
+ rc = smdk4x12_camera_params_init(smdk4x12_camera, id);
+ if (rc < 0) {
+ ALOGE("%s: Unable to init params", __func__);
+ goto error;
+ }
+
+ // Gralloc
+
+ rc = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, (const struct hw_module_t **) &smdk4x12_camera->gralloc);
+ if (rc)
+ ALOGE("%s: Unable to get gralloc module", __func__);
+
+ rc = 0;
+ goto complete;
+
+error:
+ smdk4x12_v4l2_close(smdk4x12_camera, 0);
+
+#ifdef EXYNOS_ION
+ smdk4x12_ion_close(smdk4x12_camera);
+#endif
+
+ rc = -1;
+
+complete:
+ return rc;
+}
+
+void smdk4x12_camera_stop(struct smdk4x12_camera *smdk4x12_camera)
+{
+ int i;
+ int id;
+
+ if (smdk4x12_camera == NULL || smdk4x12_camera->config == NULL)
+ return;
+
+ smdk4x12_v4l2_close(smdk4x12_camera, 0);
+
+#ifdef EXYNOS_ION
+ smdk4x12_ion_close(smdk4x12_camera);
+#endif
+}
+
+// Params
+
+int smdk4x12_camera_params_init(struct smdk4x12_camera *smdk4x12_camera, int id)
+{
+ int rc;
+
+ if (smdk4x12_camera == NULL || id >= smdk4x12_camera->config->presets_count)
+ return -EINVAL;
+
+ // Camera params
+
+ smdk4x12_camera->camera_rotation = smdk4x12_camera->config->presets[id].rotation;
+ smdk4x12_camera->camera_hflip = smdk4x12_camera->config->presets[id].hflip;
+ smdk4x12_camera->camera_vflip = smdk4x12_camera->config->presets[id].vflip;
+ smdk4x12_camera->camera_capture_format = smdk4x12_camera->config->presets[id].capture_format;
+ smdk4x12_camera->camera_picture_format = smdk4x12_camera->config->presets[id].picture_format;
+ smdk4x12_camera->camera_fimc_is = smdk4x12_camera->config->presets[id].fimc_is;
+ smdk4x12_camera->camera_focal_length = (int) (smdk4x12_camera->config->presets[id].focal_length * 100);
+ smdk4x12_camera->camera_metering = smdk4x12_camera->config->presets[id].metering;
+
+ smdk4x12_camera->camera_mbus_resolutions = smdk4x12_camera->config->presets[id].mbus_resolutions;
+ smdk4x12_camera->camera_mbus_resolutions_count = smdk4x12_camera->config->presets[id].mbus_resolutions_count;
+
+ // Recording preview
+
+ smdk4x12_param_string_set(smdk4x12_camera, "preferred-preview-size-for-video",
+ smdk4x12_camera->config->presets[id].params.preview_size);
+
+ // Preview
+
+ smdk4x12_param_string_set(smdk4x12_camera, "preview-size-values",
+ smdk4x12_camera->config->presets[id].params.preview_size_values);
+ smdk4x12_param_string_set(smdk4x12_camera, "preview-size",
+ smdk4x12_camera->config->presets[id].params.preview_size);
+ smdk4x12_param_string_set(smdk4x12_camera, "preview-format-values",
+ smdk4x12_camera->config->presets[id].params.preview_format_values);
+ smdk4x12_param_string_set(smdk4x12_camera, "preview-format",
+ smdk4x12_camera->config->presets[id].params.preview_format);
+ smdk4x12_param_string_set(smdk4x12_camera, "preview-frame-rate-values",
+ smdk4x12_camera->config->presets[id].params.preview_frame_rate_values);
+ smdk4x12_param_int_set(smdk4x12_camera, "preview-frame-rate",
+ smdk4x12_camera->config->presets[id].params.preview_frame_rate);
+ smdk4x12_param_string_set(smdk4x12_camera, "preview-fps-range-values",
+ smdk4x12_camera->config->presets[id].params.preview_fps_range_values);
+ smdk4x12_param_string_set(smdk4x12_camera, "preview-fps-range",
+ smdk4x12_camera->config->presets[id].params.preview_fps_range);
+
+ // Picture
+
+ smdk4x12_param_string_set(smdk4x12_camera, "picture-size-values",
+ smdk4x12_camera->config->presets[id].params.picture_size_values);
+ smdk4x12_param_string_set(smdk4x12_camera, "picture-size",
+ smdk4x12_camera->config->presets[id].params.picture_size);
+ smdk4x12_param_string_set(smdk4x12_camera, "picture-format-values",
+ smdk4x12_camera->config->presets[id].params.picture_format_values);
+ smdk4x12_param_string_set(smdk4x12_camera, "picture-format",
+ smdk4x12_camera->config->presets[id].params.picture_format);
+ smdk4x12_param_string_set(smdk4x12_camera, "jpeg-thumbnail-size-values",
+ smdk4x12_camera->config->presets[id].params.jpeg_thumbnail_size_values);
+ smdk4x12_param_int_set(smdk4x12_camera, "jpeg-thumbnail-width",
+ smdk4x12_camera->config->presets[id].params.jpeg_thumbnail_width);
+ smdk4x12_param_int_set(smdk4x12_camera, "jpeg-thumbnail-height",
+ smdk4x12_camera->config->presets[id].params.jpeg_thumbnail_height);
+ smdk4x12_param_int_set(smdk4x12_camera, "jpeg-thumbnail-quality",
+ smdk4x12_camera->config->presets[id].params.jpeg_thumbnail_quality);
+ smdk4x12_param_int_set(smdk4x12_camera, "jpeg-quality",
+ smdk4x12_camera->config->presets[id].params.jpeg_quality);
+
+ if (smdk4x12_camera->config->presets[id].params.video_snapshot_supported == 1)
+ smdk4x12_param_string_set(smdk4x12_camera, "video-snapshot-supported", "true");
+ if (smdk4x12_camera->config->presets[id].params.full_video_snap_supported == 1)
+ smdk4x12_param_string_set(smdk4x12_camera, "full-video-snap-supported", "true");
+
+ // Recording
+
+ smdk4x12_param_string_set(smdk4x12_camera, "video-size",
+ smdk4x12_camera->config->presets[id].params.recording_size);
+ smdk4x12_param_string_set(smdk4x12_camera, "video-size-values",
+ smdk4x12_camera->config->presets[id].params.recording_size_values);
+ smdk4x12_param_string_set(smdk4x12_camera, "video-frame-format",
+ smdk4x12_camera->config->presets[id].params.recording_format);
+
+ // Focus
+
+ smdk4x12_param_string_set(smdk4x12_camera, "focus-mode",
+ smdk4x12_camera->config->presets[id].params.focus_mode);
+ smdk4x12_param_string_set(smdk4x12_camera, "focus-mode-values",
+ smdk4x12_camera->config->presets[id].params.focus_mode_values);
+ smdk4x12_param_string_set(smdk4x12_camera, "focus-distances",
+ smdk4x12_camera->config->presets[id].params.focus_distances);
+ if (smdk4x12_camera->config->presets[id].params.max_num_focus_areas > 0) {
+ smdk4x12_param_string_set(smdk4x12_camera, "focus-areas",
+ smdk4x12_camera->config->presets[id].params.focus_areas);
+ smdk4x12_param_int_set(smdk4x12_camera, "max-num-focus-areas",
+ smdk4x12_camera->config->presets[id].params.max_num_focus_areas);
+ }
+
+ // Zoom
+
+ if (smdk4x12_camera->config->presets[id].params.zoom_supported == 1) {
+ smdk4x12_param_string_set(smdk4x12_camera, "zoom-supported", "true");
+
+ if (smdk4x12_camera->config->presets[id].params.smooth_zoom_supported == 1)
+ smdk4x12_param_string_set(smdk4x12_camera, "smooth-zoom-supported", "true");
+
+ if (smdk4x12_camera->config->presets[id].params.zoom_ratios != NULL)
+ smdk4x12_param_string_set(smdk4x12_camera, "zoom-ratios", smdk4x12_camera->config->presets[id].params.zoom_ratios);
+
+ smdk4x12_param_int_set(smdk4x12_camera, "zoom", smdk4x12_camera->config->presets[id].params.zoom);
+ smdk4x12_param_int_set(smdk4x12_camera, "max-zoom", smdk4x12_camera->config->presets[id].params.max_zoom);
+
+ } else {
+ smdk4x12_param_string_set(smdk4x12_camera, "zoom-supported", "false");
+ }
+
+ // AE lock
+
+ if (smdk4x12_camera->config->presets[id].params.auto_exposure_lock_supported == 1) {
+ smdk4x12_param_string_set(smdk4x12_camera, "auto-exposure-lock-supported", "true");
+
+ if (smdk4x12_camera->config->presets[id].params.auto_exposure_lock)
+ smdk4x12_param_string_set(smdk4x12_camera, "auto-exposure-lock", "true");
+ else
+ smdk4x12_param_string_set(smdk4x12_camera, "auto-exposure-lock", "false");
+ }
+
+ // AWB lock
+
+ if (smdk4x12_camera->config->presets[id].params.auto_white_balance_lock_supported == 1) {
+ smdk4x12_param_string_set(smdk4x12_camera, "auto-whitebalance-lock-supported", "true");
+
+ if (smdk4x12_camera->config->presets[id].params.auto_white_balance_lock)
+ smdk4x12_param_string_set(smdk4x12_camera, "auto-whitebalance-lock", "true");
+ else
+ smdk4x12_param_string_set(smdk4x12_camera, "auto-whitebalance-lock", "false");
+ }
+
+ // Flash
+
+ smdk4x12_param_string_set(smdk4x12_camera, "flash-mode",
+ smdk4x12_camera->config->presets[id].params.flash_mode);
+ smdk4x12_param_string_set(smdk4x12_camera, "flash-mode-values",
+ smdk4x12_camera->config->presets[id].params.flash_mode_values);
+
+ // Exposure
+
+ smdk4x12_param_int_set(smdk4x12_camera, "exposure-compensation",
+ smdk4x12_camera->config->presets[id].params.exposure_compensation);
+ smdk4x12_param_float_set(smdk4x12_camera, "exposure-compensation-step",
+ smdk4x12_camera->config->presets[id].params.exposure_compensation_step);
+ smdk4x12_param_int_set(smdk4x12_camera, "min-exposure-compensation",
+ smdk4x12_camera->config->presets[id].params.min_exposure_compensation);
+ smdk4x12_param_int_set(smdk4x12_camera, "max-exposure-compensation",
+ smdk4x12_camera->config->presets[id].params.max_exposure_compensation);
+
+ // Antibanding
+
+ smdk4x12_param_string_set(smdk4x12_camera, "antibanding",
+ smdk4x12_camera->config->presets[id].params.antibanding);
+ smdk4x12_param_string_set(smdk4x12_camera, "antibanding-values",
+ smdk4x12_camera->config->presets[id].params.antibanding_values);
+
+ // WB
+
+ smdk4x12_param_string_set(smdk4x12_camera, "whitebalance",
+ smdk4x12_camera->config->presets[id].params.whitebalance);
+ smdk4x12_param_string_set(smdk4x12_camera, "whitebalance-values",
+ smdk4x12_camera->config->presets[id].params.whitebalance_values);
+
+ // Scene mode
+
+ smdk4x12_param_string_set(smdk4x12_camera, "scene-mode",
+ smdk4x12_camera->config->presets[id].params.scene_mode);
+ smdk4x12_param_string_set(smdk4x12_camera, "scene-mode-values",
+ smdk4x12_camera->config->presets[id].params.scene_mode_values);
+
+ // Effect
+
+ smdk4x12_param_string_set(smdk4x12_camera, "effect",
+ smdk4x12_camera->config->presets[id].params.effect);
+ smdk4x12_param_string_set(smdk4x12_camera, "effect-values",
+ smdk4x12_camera->config->presets[id].params.effect_values);
+
+ // ISO
+
+ smdk4x12_param_string_set(smdk4x12_camera, "iso",
+ smdk4x12_camera->config->presets[id].params.iso);
+ smdk4x12_param_string_set(smdk4x12_camera, "iso-values",
+ smdk4x12_camera->config->presets[id].params.iso_values);
+
+ // Image stabilization
+
+ smdk4x12_param_string_set(smdk4x12_camera, "image-stabilization",
+ smdk4x12_camera->config->presets[id].params.image_stabilization);
+ smdk4x12_param_string_set(smdk4x12_camera, "image-stabilization-values",
+ smdk4x12_camera->config->presets[id].params.image_stabilization_values);
+
+ // Camera
+
+ smdk4x12_param_float_set(smdk4x12_camera, "focal-length",
+ smdk4x12_camera->config->presets[id].focal_length);
+ smdk4x12_param_float_set(smdk4x12_camera, "horizontal-view-angle",
+ smdk4x12_camera->config->presets[id].horizontal_view_angle);
+ smdk4x12_param_float_set(smdk4x12_camera, "vertical-view-angle",
+ smdk4x12_camera->config->presets[id].vertical_view_angle);
+
+ rc = smdk4x12_camera_params_apply(smdk4x12_camera, 1);
+ if (rc < 0) {
+ ALOGE("%s: Unable to apply params", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int smdk4x12_camera_params_apply(struct smdk4x12_camera *smdk4x12_camera, int force)
+{
+ 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;
+ 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 fimc_is_mode = 0;
+
+ 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 *ae_lock_supported_string;
+ char *ae_lock_string;
+ int ae_lock = 0;
+
+ char *awb_lock_supported_string;
+ char *awb_lock_string;
+ int awb_lock = 0;
+ int aeawb = 0;
+
+ char *flash_mode_string;
+ int flash_mode;
+
+ int exposure_compensation;
+ int min_exposure_compensation;
+ int max_exposure_compensation;
+
+ char *antibanding_string;
+ int antibanding;
+
+ char *whitebalance_string;
+ int whitebalance;
+
+ char *scene_mode_string;
+ int scene_mode;
+
+ char *effect_string;
+ int effect;
+
+ char *iso_string;
+ int iso;
+
+ char *image_stabilization_string;
+ int image_stabilization;
+
+ int w, h;
+ char *k;
+ int rc;
+
+ if (smdk4x12_camera == NULL)
+ return -EINVAL;
+
+ // Preview
+
+ preview_size_string = smdk4x12_param_string_get(smdk4x12_camera, "preview-size");
+ if (preview_size_string != NULL) {
+ sscanf(preview_size_string, "%dx%d", &preview_width, &preview_height);
+
+ if (preview_width != 0 && preview_width != smdk4x12_camera->preview_width)
+ smdk4x12_camera->preview_width = preview_width;
+ if (preview_height != 0 && preview_height != smdk4x12_camera->preview_height)
+ smdk4x12_camera->preview_height = preview_height;
+ }
+
+ preview_format_string = smdk4x12_param_string_get(smdk4x12_camera, "preview-format");
+ if (preview_format_string != NULL) {
+ if (strcmp(preview_format_string, "yuv420sp") == 0) {
+ preview_format = V4L2_PIX_FMT_NV21;
+ } else if (strcmp(preview_format_string, "yuv420p") == 0) {
+ preview_format = V4L2_PIX_FMT_YUV420;
+ } else if (strcmp(preview_format_string, "rgb565") == 0) {
+ preview_format = V4L2_PIX_FMT_RGB565;
+ } else if (strcmp(preview_format_string, "rgb8888") == 0) {
+ preview_format = V4L2_PIX_FMT_RGB32;
+ } else {
+ ALOGE("%s: Unsupported preview format: %s", __func__, preview_format_string);
+ preview_format = V4L2_PIX_FMT_NV21;
+ }
+
+ if (preview_format != smdk4x12_camera->preview_format)
+ smdk4x12_camera->preview_format = preview_format;
+ }
+
+ preview_fps = smdk4x12_param_int_get(smdk4x12_camera, "preview-frame-rate");
+ if (preview_fps > 0)
+ smdk4x12_camera->preview_fps = preview_fps;
+ else
+ smdk4x12_camera->preview_fps = 0;
+
+ // Picture
+
+ picture_size_string = smdk4x12_param_string_get(smdk4x12_camera, "picture-size");
+ if (picture_size_string != NULL) {
+ sscanf(picture_size_string, "%dx%d", &picture_width, &picture_height);
+
+ if (picture_width != 0 && picture_height != 0 && (picture_width != smdk4x12_camera->picture_width || picture_height != smdk4x12_camera->picture_height)) {
+ smdk4x12_camera->picture_width = picture_width;
+ smdk4x12_camera->picture_height = picture_height;
+
+ if (smdk4x12_camera->camera_capture_format == V4L2_PIX_FMT_INTERLEAVED) {
+ rc = smdk4x12_v4l2_s_ctrl(smdk4x12_camera, 0, V4L2_CID_CAMERA_JPEG_RESOLUTION, (picture_width & 0xffff) << 16 | (picture_height & 0xffff));
+ if (rc < 0)
+ ALOGE("%s: Unablet to set jpeg resolution", __func__);
+ }
+ }
+ }
+
+ picture_format_string = smdk4x12_param_string_get(smdk4x12_camera, "picture-format");
+ if (picture_format_string != NULL) {
+ if (strcmp(picture_format_string, "jpeg") == 0) {
+ picture_format = V4L2_PIX_FMT_JPEG;
+ } else {
+ ALOGE("%s: Unsupported picture format: %s", __func__, picture_format_string);
+ picture_format = V4L2_PIX_FMT_JPEG;
+ }
+
+ if (picture_format != smdk4x12_camera->picture_format)
+ smdk4x12_camera->picture_format = picture_format;
+ }
+
+ jpeg_thumbnail_width = smdk4x12_param_int_get(smdk4x12_camera, "jpeg-thumbnail-width");
+ if (jpeg_thumbnail_width > 0)
+ smdk4x12_camera->jpeg_thumbnail_width = jpeg_thumbnail_width;
+
+ jpeg_thumbnail_height = smdk4x12_param_int_get(smdk4x12_camera, "jpeg-thumbnail-height");
+ if (jpeg_thumbnail_height > 0)
+ smdk4x12_camera->jpeg_thumbnail_height = jpeg_thumbnail_height;
+
+ jpeg_thumbnail_quality = smdk4x12_param_int_get(smdk4x12_camera, "jpeg-thumbnail-quality");
+ if (jpeg_thumbnail_quality > 0)
+ smdk4x12_camera->jpeg_thumbnail_quality = jpeg_thumbnail_quality;
+
+ jpeg_quality = smdk4x12_param_int_get(smdk4x12_camera, "jpeg-quality");
+ if (jpeg_quality <= 100 && jpeg_quality >= 0 && (jpeg_quality != smdk4x12_camera->jpeg_quality || force)) {
+ smdk4x12_camera->jpeg_quality = jpeg_quality;
+ rc = smdk4x12_v4l2_s_ctrl(smdk4x12_camera, 0, V4L2_CID_CAM_JPEG_QUALITY, jpeg_quality);
+ if (rc < 0)
+ ALOGE("%s: Unable to set jpeg quality", __func__);
+ }
+
+ // Recording
+
+ video_size_string = smdk4x12_param_string_get(smdk4x12_camera, "video-size");
+ if (video_size_string == NULL)
+ video_size_string = smdk4x12_param_string_get(smdk4x12_camera, "preview-size");
+
+ if (video_size_string != NULL) {
+ sscanf(video_size_string, "%dx%d", &recording_width, &recording_height);
+
+ if (recording_width != 0 && recording_width != smdk4x12_camera->recording_width)
+ smdk4x12_camera->recording_width = recording_width;
+ if (recording_height != 0 && recording_height != smdk4x12_camera->recording_height)
+ smdk4x12_camera->recording_height = recording_height;
+ }
+
+ video_frame_format_string = smdk4x12_param_string_get(smdk4x12_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 {
+ ALOGE("%s: Unsupported recording format: %s", __func__, video_frame_format_string);
+ recording_format = V4L2_PIX_FMT_NV12;
+ }
+
+ if (recording_format != smdk4x12_camera->recording_format)
+ smdk4x12_camera->recording_format = recording_format;
+ }
+
+ recording_hint_string = smdk4x12_param_string_get(smdk4x12_camera, "recording-hint");
+ if (recording_hint_string != NULL && strcmp(recording_hint_string, "true") == 0) {
+ camera_sensor_mode = SENSOR_MOVIE;
+
+ k = smdk4x12_param_string_get(smdk4x12_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 != smdk4x12_camera->preview_width)
+ smdk4x12_camera->preview_width = preview_width;
+ if (preview_height != 0 && preview_height != smdk4x12_camera->preview_height)
+ smdk4x12_camera->preview_height = preview_height;
+
+ if (smdk4x12_camera->camera_fimc_is)
+ fimc_is_mode = IS_MODE_PREVIEW_VIDEO;
+ } else {
+ camera_sensor_mode = SENSOR_CAMERA;
+
+ if (smdk4x12_camera->camera_fimc_is)
+ fimc_is_mode = IS_MODE_PREVIEW_STILL;
+ }
+
+ // Switching modes
+
+ if (camera_sensor_mode != smdk4x12_camera->camera_sensor_mode) {
+ smdk4x12_camera->camera_sensor_mode = camera_sensor_mode;
+ rc = smdk4x12_v4l2_s_ctrl(smdk4x12_camera, 0, V4L2_CID_CAMERA_SENSOR_MODE, camera_sensor_mode);
+ if (rc < 0)
+ ALOGE("%s: Unable to set sensor mode", __func__);
+ }
+
+ if (smdk4x12_camera->camera_fimc_is && fimc_is_mode != smdk4x12_camera->fimc_is_mode) {
+ smdk4x12_camera->fimc_is_mode = fimc_is_mode;
+
+ rc = smdk4x12_v4l2_s_ctrl(smdk4x12_camera, 0, V4L2_CID_IS_S_FORMAT_SCENARIO, smdk4x12_camera->fimc_is_mode);
+ if (rc < 0)
+ ALOGE("%s: Unable to set FIMC-IS scenario", __func__);
+ }
+
+ // Focus
+
+ focus_areas_string = smdk4x12_param_string_get(smdk4x12_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) {
+ ALOGE("%s: Unable to scan focus areas", __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 != smdk4x12_camera->focus_x || force) {
+ smdk4x12_camera->focus_x = focus_x;
+
+ rc = smdk4x12_v4l2_s_ctrl(smdk4x12_camera, 0, V4L2_CID_CAMERA_OBJECT_POSITION_X, focus_x);
+ if (rc < 0)
+ ALOGE("%s: Unable to set object x position", __func__);
+ }
+
+ if (focus_y != smdk4x12_camera->focus_y || force) {
+ smdk4x12_camera->focus_y = focus_y;
+
+ rc = smdk4x12_v4l2_s_ctrl(smdk4x12_camera, 0, V4L2_CID_CAMERA_OBJECT_POSITION_Y, focus_y);
+ if (rc < 0)
+ ALOGE("%s: Unable to set object y position", __func__);
+ }
+
+ focus_mode = FOCUS_MODE_TOUCH;
+ }
+ }
+
+ focus_mode_string = smdk4x12_param_string_get(smdk4x12_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_VIDEO;
+ else if (strcmp(focus_mode_string, "continuous-picture") == 0)
+ focus_mode = FOCUS_MODE_CONTINOUS_PICTURE;
+ else
+ focus_mode = FOCUS_MODE_AUTO;
+ }
+
+ if (focus_mode != smdk4x12_camera->focus_mode || force) {
+ rc = smdk4x12_v4l2_s_ctrl(smdk4x12_camera, 0, V4L2_CID_CAMERA_FOCUS_MODE, focus_mode);
+ if (rc < 0)
+ ALOGE("%s: Unable to set focus mode", __func__);
+ }
+
+ smdk4x12_camera->focus_mode = focus_mode;
+ }
+
+ // Zoom
+
+ zoom_supported_string = smdk4x12_param_string_get(smdk4x12_camera, "zoom-supported");
+ if (zoom_supported_string != NULL && strcmp(zoom_supported_string, "true") == 0) {
+ zoom = smdk4x12_param_int_get(smdk4x12_camera, "zoom");
+ max_zoom = smdk4x12_param_int_get(smdk4x12_camera, "max-zoom");
+ if (zoom <= max_zoom && zoom >= 0 && (zoom != smdk4x12_camera->zoom || force)) {
+ smdk4x12_camera->zoom = zoom;
+ rc = smdk4x12_v4l2_s_ctrl(smdk4x12_camera, 0, V4L2_CID_CAMERA_ZOOM, zoom);
+ if (rc < 0)
+ ALOGE("%s: Unable to set camera zoom", __func__);
+ }
+
+ }
+
+ // AE lock
+
+ ae_lock_supported_string = smdk4x12_param_string_get(smdk4x12_camera, "auto-exposure-lock-supported");
+ ae_lock_string = smdk4x12_param_string_get(smdk4x12_camera, "auto-exposure-lock");
+ if (ae_lock_supported_string != NULL && ae_lock_string != NULL && strcmp(ae_lock_supported_string, "true") == 0 && strcmp(ae_lock_string, "true") == 0)
+ ae_lock = 1;
+ else
+ ae_lock = 0;
+
+ // AWB lock
+
+ awb_lock_supported_string = smdk4x12_param_string_get(smdk4x12_camera, "auto-whitebalance-lock-supported");
+ awb_lock_string = smdk4x12_param_string_get(smdk4x12_camera, "auto-whitebalance-lock");
+ if (awb_lock_supported_string != NULL && awb_lock_string != NULL && strcmp(awb_lock_supported_string, "true") == 0 && strcmp(awb_lock_string, "true") == 0)
+ awb_lock = 1;
+ else
+ awb_lock = 0;
+
+ if (ae_lock != smdk4x12_camera->ae_lock || awb_lock != smdk4x12_camera->awb_lock || force) {
+ smdk4x12_camera->ae_lock = ae_lock;
+ smdk4x12_camera->awb_lock = awb_lock;
+ aeawb = (ae_lock ? 0x1 : 0x0) | (awb_lock ? 0x2 : 0x0);
+ rc = smdk4x12_v4l2_s_ctrl(smdk4x12_camera, 0, V4L2_CID_CAMERA_AEAWB_LOCK_UNLOCK, aeawb);
+ if (rc < 0)
+ ALOGE("%s: Unable to set AEAWB lock", __func__);
+ }
+
+ // Flash
+
+ flash_mode_string = smdk4x12_param_string_get(smdk4x12_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 != smdk4x12_camera->flash_mode || force) {
+ smdk4x12_camera->flash_mode = flash_mode;
+ rc = smdk4x12_v4l2_s_ctrl(smdk4x12_camera, 0, V4L2_CID_CAMERA_FLASH_MODE, flash_mode);
+ if (rc < 0)
+ ALOGE("%s:Unable to set flash mode", __func__);
+ }
+ }
+
+ // Exposure
+
+ exposure_compensation = smdk4x12_param_int_get(smdk4x12_camera, "exposure-compensation");
+ min_exposure_compensation = smdk4x12_param_int_get(smdk4x12_camera, "min-exposure-compensation");
+ max_exposure_compensation = smdk4x12_param_int_get(smdk4x12_camera, "max-exposure-compensation");
+
+ if (exposure_compensation <= max_exposure_compensation && exposure_compensation >= min_exposure_compensation &&
+ (exposure_compensation != smdk4x12_camera->exposure_compensation || force)) {
+ smdk4x12_camera->exposure_compensation = exposure_compensation;
+ rc = smdk4x12_v4l2_s_ctrl(smdk4x12_camera, 0, V4L2_CID_CAMERA_BRIGHTNESS, exposure_compensation);
+ if (rc < 0)
+ ALOGE("%s: Unable to set exposure", __func__);
+ }
+
+ // Antibanding
+
+ antibanding_string = smdk4x12_param_string_get(smdk4x12_camera, "antibanding");
+ if (antibanding_string != NULL) {
+ if (strcmp(antibanding_string, "auto") == 0)
+ antibanding = ANTI_BANDING_AUTO;
+ else if (strcmp(antibanding_string, "50hz") == 0)
+ antibanding = ANTI_BANDING_50HZ;
+ else if (strcmp(antibanding_string, "60hz") == 0)
+ antibanding = ANTI_BANDING_60HZ;
+ else if (strcmp(antibanding_string, "off") == 0)
+ antibanding = ANTI_BANDING_OFF;
+ else
+ antibanding = ANTI_BANDING_AUTO;
+
+ if (antibanding != smdk4x12_camera->antibanding || force) {
+ smdk4x12_camera->antibanding = antibanding;
+ rc = smdk4x12_v4l2_s_ctrl(smdk4x12_camera, 0, V4L2_CID_CAMERA_ANTI_BANDING, antibanding);
+ if (rc < 0)
+ ALOGE("%s: Unable to set antibanding", __func__);
+ }
+ }
+
+ // WB
+
+ whitebalance_string = smdk4x12_param_string_get(smdk4x12_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 != smdk4x12_camera->whitebalance || force) {
+ smdk4x12_camera->whitebalance = whitebalance;
+ rc = smdk4x12_v4l2_s_ctrl(smdk4x12_camera, 0, V4L2_CID_CAMERA_WHITE_BALANCE, whitebalance);
+ if (rc < 0)
+ ALOGE("%s: Unable to set whitebalance", __func__);
+ }
+ }
+
+ // Scene mode
+
+ scene_mode_string = smdk4x12_param_string_get(smdk4x12_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 != smdk4x12_camera->scene_mode || force) {
+ smdk4x12_camera->scene_mode = scene_mode;
+ rc = smdk4x12_v4l2_s_ctrl(smdk4x12_camera, 0, V4L2_CID_CAMERA_SCENE_MODE, scene_mode);
+ if (rc < 0)
+ ALOGE("%s: Unable to set scene mode", __func__);
+ }
+ }
+
+ // Effect
+
+ effect_string = smdk4x12_param_string_get(smdk4x12_camera, "effect");
+ if (effect_string != NULL) {
+ if (strcmp(effect_string, "auto") == 0)
+ effect = IMAGE_EFFECT_NONE;
+ if (strcmp(effect_string, "none") == 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 if (strcmp(effect_string, "solarize") == 0)
+ effect = IMAGE_EFFECT_SOLARIZE;
+ else if (strcmp(effect_string, "posterize") == 0)
+ effect = IMAGE_EFFECT_POSTERIZE;
+ else if (strcmp(effect_string, "washed") == 0)
+ effect = IMAGE_EFFECT_WASHED;
+ else if (strcmp(effect_string, "sketch") == 0)
+ effect = IMAGE_EFFECT_SKETCH;
+ else if (strcmp(effect_string, "vintage-warm") == 0)
+ effect = IMAGE_EFFECT_VINTAGE_WARM;
+ else if (strcmp(effect_string, "vintage-cold") == 0)
+ effect = IMAGE_EFFECT_VINTAGE_COLD;
+ else if (strcmp(effect_string, "point-blue") == 0)
+ effect = IMAGE_EFFECT_POINT_BLUE;
+ else if (strcmp(effect_string, "point-red-yellow") == 0)
+ effect = IMAGE_EFFECT_POINT_RED_YELLOW;
+ else if (strcmp(effect_string, "point-green") == 0)
+ effect = IMAGE_EFFECT_POINT_GREEN;
+ else
+ effect = IMAGE_EFFECT_NONE;
+
+ if (effect != smdk4x12_camera->effect || force) {
+ smdk4x12_camera->effect = effect;
+ rc = smdk4x12_v4l2_s_ctrl(smdk4x12_camera, 0, V4L2_CID_CAMERA_EFFECT, effect);
+ if (rc < 0)
+ ALOGE("%s: Unable to set effect", __func__);
+ }
+ }
+
+ // ISO
+
+ iso_string = smdk4x12_param_string_get(smdk4x12_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 != smdk4x12_camera->iso || force) {
+ smdk4x12_camera->iso = iso;
+ rc = smdk4x12_v4l2_s_ctrl(smdk4x12_camera, 0, V4L2_CID_CAMERA_ISO, iso);
+ if (rc < 0)
+ ALOGE("%s: Unable to set iso", __func__);
+ }
+ }
+
+ // Image stabilization
+
+ image_stabilization_string = smdk4x12_param_string_get(smdk4x12_camera, "image-stabilization");
+ if (image_stabilization_string != NULL) {
+ if (strcmp(image_stabilization_string, "on") == 0)
+ image_stabilization = ANTI_SHAKE_STILL_ON;
+ else
+ image_stabilization = ANTI_SHAKE_OFF;
+
+ if (image_stabilization != smdk4x12_camera->image_stabilization || force) {
+ smdk4x12_camera->image_stabilization = image_stabilization;
+ rc = smdk4x12_v4l2_s_ctrl(smdk4x12_camera, 0, V4L2_CID_CAMERA_ANTI_SHAKE, image_stabilization);
+ if (rc < 0)
+ ALOGE("%s: Unable to set image-stabilization", __func__);
+ }
+ }
+
+ ALOGD("%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;
+}
+
+// Capture
+
+int s5c73m3_interleaved_decode(void *data, int size,
+ void *yuv_data, int *yuv_size, int yuv_width, int yuv_height,
+ void *jpeg_data, int *jpeg_size, int *decoded, int *auto_focus_result,
+ int *exif_flash, int *exif_iso, int *exif_exposure,
+ int *exif_exposure_bias, int *exif_exposure_time)
+{
+ int yuv_length;
+ int jpeg_length;
+ unsigned char *yuv_p;
+ unsigned char *jpeg_p;
+ unsigned char *data_p;
+ unsigned int *offset_p;
+ unsigned int pointers_array_offset;
+ unsigned int pointers_array_size;
+ unsigned int interleaved_size;
+ unsigned char s5c73m3_auto_focus_result;
+ unsigned int yuv_offset_last;
+ unsigned int yuv_offset;
+ unsigned int yuv_line_size;
+ unsigned short *jpeg_start_p;
+ int gap;
+ unsigned int i;
+
+ if (data == NULL || size <= 0 || yuv_data == NULL || yuv_size == NULL || yuv_width <= 0 || yuv_height <= 0 || jpeg_data == NULL || jpeg_size == NULL || decoded == NULL || auto_focus_result == NULL || exif_flash == NULL || exif_iso == NULL || exif_exposure == NULL || exif_exposure_bias == NULL || exif_exposure_time == NULL)
+ return -EINVAL;
+
+ yuv_length = 0;
+ jpeg_length = 0;
+
+ data_p = (unsigned char *) data;
+ data_p += size - 0x1000; // End of the first plane (interleaved buffer)
+ data_p += 4046; // Experimental offset for decoded
+
+ *decoded = (int) *data_p;
+
+ data_p = (unsigned char *) data;
+ data_p += size - 0x1000; // End of the first plane (interleaved buffer)
+ data_p += 50; // Experimental offset for auto-focus result
+
+ s5c73m3_auto_focus_result = *data_p;
+ switch (s5c73m3_auto_focus_result) {
+ case S5C73M3_AF_STATUS_FOCUSING:
+ case S5C73M3_CAF_STATUS_FOCUSING:
+ *auto_focus_result = CAMERA_AF_STATUS_IN_PROGRESS;
+ break;
+ case S5C73M3_AF_STATUS_FOCUSED:
+ case S5C73M3_CAF_STATUS_FOCUSED:
+ *auto_focus_result = CAMERA_AF_STATUS_SUCCESS;
+ break;
+ case S5C73M3_AF_STATUS_INVALID:
+ case S5C73M3_CAF_STATUS_FIND_SEARCHING_DIR:
+ case S5C73M3_AF_STATUS_UNFOCUSED:
+ case S5C73M3_CAF_STATUS_UNFOCUSED:
+ default:
+ *auto_focus_result = CAMERA_AF_STATUS_FAIL;
+ break;
+ }
+
+ data_p = (unsigned char *) data;
+ data_p += size - 0x1000; // End of the first plane (interleaved buffer)
+ data_p += 4; // EXIF flash offset
+ *exif_flash = (int) *data_p;
+
+ data_p += 4; // EXIF ISO offset
+ *exif_iso = (int) ((data_p[1] << 8) | (data_p[0] & 0xff));
+
+ data_p += 4; // EXIF exposure offset
+ *exif_exposure = (int) *data_p;
+
+ data_p += 4; // EXIF exposure bias offset
+ *exif_exposure_bias = (int) ((data_p[1] << 8) | (data_p[0] & 0xff));
+
+ data_p += 8; // EXIF exposure time offset
+ *exif_exposure_time = (int) ((data_p[1] << 8) | (data_p[0] & 0xff));
+
+ data_p = (unsigned char *) data;
+ data_p += size - 0x1000; // End of the first plane (interleaved buffer)
+ data_p += 4084; // Experimental offset for interleaved size
+
+ // Read the pointers array offset
+ offset_p = (unsigned int *) data_p;
+ pointers_array_offset = BIG2LITTLE_ENDIAN(*offset_p);
+ interleaved_size = pointers_array_offset;
+
+ // Read the pointers array size, it should be 4 * yuv_height
+ data_p += sizeof(pointers_array_offset);
+ offset_p = (unsigned int *) data_p;
+ pointers_array_size = BIG2LITTLE_ENDIAN(*offset_p);
+
+ if (!*decoded)
+ return 0;
+
+ ALOGD("%s: Interleaved pointers array is at offset 0x%x, 0x%x bytes long\n", __func__, pointers_array_offset, pointers_array_size);
+
+ if ((int) pointers_array_offset > size || (int) pointers_array_size > size || (int) pointers_array_size < yuv_height * (int) sizeof(unsigned int)) {
+ ALOGE("%s: Invalid informations", __func__);
+ return -1;
+ }
+
+ data_p = (unsigned char *) data;
+ data_p += pointers_array_offset;
+ yuv_p = (unsigned char *) yuv_data;
+ jpeg_p = (unsigned char *) jpeg_data;
+ jpeg_start_p = NULL;
+
+ yuv_line_size = yuv_width * 2;
+ yuv_offset_last = 0;
+ yuv_offset = 0;
+ i = 0;
+
+ while (i < pointers_array_size) {
+ offset_p = (unsigned int *) data_p;
+ yuv_offset = BIG2LITTLE_ENDIAN(*offset_p);
+
+ if (yuv_offset > size - yuv_line_size)
+ return -1;
+
+ gap = yuv_offset - yuv_offset_last - yuv_line_size;
+
+ if (gap > 0) {
+ data_p = (unsigned char *) data + yuv_offset_last + yuv_line_size;
+
+ if (jpeg_start_p == NULL) {
+ jpeg_start_p = (unsigned short *) data_p;
+ if (*jpeg_start_p != 0xd8ff) {
+ ALOGE("%s: Invalid jpeg start", __func__);
+ return -1;
+ }
+ }
+
+ memcpy(jpeg_p, data_p, gap);
+ jpeg_p += gap;
+ jpeg_length += gap;
+ }
+
+ yuv_offset_last = yuv_offset;
+
+ data_p = (unsigned char *) data + yuv_offset;
+ memcpy(yuv_p, data_p, yuv_line_size);
+ yuv_p += yuv_line_size;
+ yuv_length += yuv_line_size;
+
+ data_p = (unsigned char *) offset_p;
+ data_p += sizeof(yuv_offset);
+ i += sizeof(yuv_offset);
+ }
+
+ gap = interleaved_size - yuv_offset_last - yuv_line_size;
+
+ if (gap > 0) {
+ data_p = (unsigned char *) data + yuv_offset_last + yuv_line_size;
+
+ memcpy(jpeg_p, data_p, gap);
+ jpeg_p += gap;
+ jpeg_length += gap;
+ }
+
+ *yuv_size = yuv_length;
+ *jpeg_size = jpeg_length;
+
+ return 0;
+}
+
+int smdk4x12_camera_capture(struct smdk4x12_camera *smdk4x12_camera)
+{
+ struct smdk4x12_camera_capture_listener *listener;
+ struct smdk4x12_camera_buffer *buffers = NULL;
+ struct smdk4x12_camera_buffer *buffer;
+ struct list_head *list;
+ int width, height, format;
+ int yuv_length, jpeg_length;
+ int jpeg_offset, jpeg_size;
+ int jpeg_thumbnail_offset, jpeg_thumbnail_size;
+ int buffers_count;
+ int buffer_length;
+ int auto_focus_result;
+ int decoded;
+ int busy;
+ void *pointer;
+ int address;
+ int offset;
+ int index;
+ int rc;
+
+ if (smdk4x12_camera == NULL)
+ return -EINVAL;
+
+ width = smdk4x12_camera->capture_width;
+ height = smdk4x12_camera->capture_height;
+ format = smdk4x12_camera->capture_format;
+
+ buffers_count = smdk4x12_camera->capture_buffers_count;
+ buffer_length = smdk4x12_camera->capture_buffer_length;
+
+ // V4L2
+
+ index = smdk4x12_v4l2_dqbuf_cap(smdk4x12_camera, 0);
+ if (index < 0 || index >= buffers_count) {
+ rc = smdk4x12_v4l2_poll(smdk4x12_camera, 0);
+ if (rc < 0) {
+ ALOGE("%s Unable to poll", __func__);
+ goto error;
+ } else if (rc == 0) {
+ // Timeout
+ rc = 0;
+ goto complete;
+ }
+
+ index = smdk4x12_v4l2_dqbuf_cap(smdk4x12_camera, 0);
+ if (index < 0 || index >= buffers_count) {
+ ALOGE("%s: Unable to dequeue buffer", __func__);
+ goto error;
+ }
+ }
+
+ smdk4x12_camera->capture_memory_index = index;
+
+ address = smdk4x12_v4l2_s_ctrl(smdk4x12_camera, 0, V4L2_CID_PADDR_Y, index);
+ if (address == 0 || address == (int) 0xffffffff) {
+ ALOGE("%s: Unable to get address", __func__);
+ goto error;
+ }
+
+ offset = address - smdk4x12_camera->capture_memory_address;
+ if (offset != index * buffer_length)
+ ALOGE("%s: Inconsistent memory offset (0x%x/0x%x)", __func__, offset, index * buffer_length);
+
+ pointer = (void *) ((unsigned char *) smdk4x12_camera->capture_memory->data + offset);
+
+ // Buffers
+
+ switch (format) {
+ case V4L2_PIX_FMT_INTERLEAVED:
+ yuv_length = jpeg_length = 0;
+ auto_focus_result = decoded = 0;
+
+ rc = s5c73m3_interleaved_decode(pointer, buffer_length, smdk4x12_camera->capture_yuv_buffer, &yuv_length, width, height, smdk4x12_camera->capture_jpeg_buffer, &jpeg_length, &decoded, &auto_focus_result, &smdk4x12_camera->capture_exif_flash, &smdk4x12_camera->capture_exif_iso, &smdk4x12_camera->capture_exif_exposure, &smdk4x12_camera->capture_exif_exposure_bias, &smdk4x12_camera->capture_exif_exposure_time);
+ if (rc < 0) {
+ ALOGE("%s: Unable to decode S5C73M3 interleaved", __func__);
+ goto error;
+ }
+
+ if (auto_focus_result != smdk4x12_camera->auto_focus_result) {
+ if (!smdk4x12_camera->auto_focus_thread_enabled) {
+ smdk4x12_camera->auto_focus_result = auto_focus_result;
+
+ rc = smdk4x12_camera_auto_focus(smdk4x12_camera, auto_focus_result);
+ if (rc < 0) {
+ ALOGE("%s: Unable to auto focus", __func__);
+ goto error;
+ }
+ }
+ }
+
+ if (!decoded) {
+ buffers_count = 1;
+ buffers = (struct smdk4x12_camera_buffer *) calloc(buffers_count, sizeof(struct smdk4x12_camera_buffer));
+
+ buffer = buffers;
+
+ buffer->pointer = pointer;
+ buffer->address = address;
+ buffer->length = smdk4x12_camera_buffer_length(width, height, V4L2_PIX_FMT_UYVY);
+ buffer->width = width;
+ buffer->height = height;
+ buffer->format = V4L2_PIX_FMT_UYVY;
+ } else {
+ buffers_count = 2;
+ buffers = (struct smdk4x12_camera_buffer *) calloc(buffers_count, sizeof(struct smdk4x12_camera_buffer));
+
+ buffer = buffers;
+
+ memcpy(pointer, smdk4x12_camera->capture_yuv_buffer, yuv_length);
+
+ buffer->pointer = pointer;
+ buffer->address = address;
+ buffer->length = yuv_length;
+ buffer->width = width;
+ buffer->height = height;
+ buffer->format = V4L2_PIX_FMT_UYVY;
+
+ pointer = (void *) ((unsigned char *) pointer + yuv_length);
+ address += yuv_length;
+ buffer = (struct smdk4x12_camera_buffer *) ((unsigned char *) buffer + sizeof(struct smdk4x12_camera_buffer));
+
+ memcpy(pointer, smdk4x12_camera->capture_jpeg_buffer, jpeg_length);
+
+ buffer->pointer = pointer;
+ buffer->address = address;
+ buffer->length = jpeg_length;
+ buffer->width = smdk4x12_camera->picture_width;
+ buffer->height = smdk4x12_camera->picture_height;
+ buffer->format = V4L2_PIX_FMT_JPEG;
+
+ smdk4x12_camera->capture_hybrid = 0;
+ }
+ break;
+ case V4L2_PIX_FMT_JPEG:
+ jpeg_size = jpeg_offset = 0;
+ jpeg_thumbnail_size = jpeg_thumbnail_offset = 0;
+
+ rc = smdk4x12_v4l2_g_ctrl(smdk4x12_camera, 0, V4L2_CID_CAM_JPEG_MAIN_SIZE, &jpeg_size);
+ if (rc < 0 || jpeg_size <= 0) {
+ ALOGE("%s: Unable to get jpeg size", __func__);
+ goto error;
+ }
+
+ rc = smdk4x12_v4l2_g_ctrl(smdk4x12_camera, 0, V4L2_CID_CAM_JPEG_MAIN_OFFSET, &jpeg_offset);
+ if (rc < 0) {
+ ALOGE("%s: Unable to get jpeg offset", __func__);
+ goto error;
+ }
+
+ rc = smdk4x12_v4l2_g_ctrl(smdk4x12_camera, 0, V4L2_CID_CAM_JPEG_THUMB_SIZE, &jpeg_thumbnail_size);
+ if (rc < 0 || jpeg_thumbnail_size <= 0) {
+ ALOGE("%s: Unable to get jpeg thumbnail size", __func__);
+ goto error;
+ }
+
+ rc = smdk4x12_v4l2_g_ctrl(smdk4x12_camera, 0, V4L2_CID_CAM_JPEG_THUMB_OFFSET, &jpeg_thumbnail_offset);
+ if (rc < 0) {
+ ALOGE("%s: Unable to get jpeg thumbnail offset", __func__);
+ goto error;
+ }
+
+ buffers_count = 2;
+ buffers = (struct smdk4x12_camera_buffer *) calloc(buffers_count, sizeof(struct smdk4x12_camera_buffer));
+
+ buffer = buffers;
+
+ buffer->pointer = (void *) ((unsigned char *) pointer + jpeg_offset);
+ buffer->address = address + jpeg_offset;
+ buffer->length = jpeg_size;
+ buffer->width = smdk4x12_camera->picture_width;
+ buffer->height = smdk4x12_camera->picture_height;
+ buffer->format = V4L2_PIX_FMT_JPEG;
+
+ buffer = (struct smdk4x12_camera_buffer *) ((unsigned char *) buffer + sizeof(struct smdk4x12_camera_buffer));
+
+ buffer->pointer = (void *) ((unsigned char *) pointer + jpeg_thumbnail_offset);
+ buffer->address = address + jpeg_thumbnail_offset;
+ buffer->length = jpeg_thumbnail_size;
+ buffer->width = smdk4x12_camera->jpeg_thumbnail_width;
+ buffer->height = smdk4x12_camera->jpeg_thumbnail_height;
+ buffer->format = V4L2_PIX_FMT_JPEG;
+ break;
+ default:
+ buffers_count = 1;
+ buffers = (struct smdk4x12_camera_buffer *) calloc(buffers_count, sizeof(struct smdk4x12_camera_buffer));
+
+ buffer = buffers;
+
+ buffer->pointer = pointer;
+ buffer->address = address;
+ buffer->length = buffer_length;
+ buffer->width = width;
+ buffer->height = height;
+ buffer->format = format;
+ break;
+ }
+
+ // Listeners
+
+ list = (struct list_head *) smdk4x12_camera->capture_listeners;
+ while (list != NULL) {
+ listener = (struct smdk4x12_camera_capture_listener *) list;
+
+ if (listener->callback == NULL)
+ goto list_continue_callback;
+
+ /*
+ * Callback must never call a capture-locked function or it will
+ * block. Hence, do not unregister the listener in callback.
+ */
+
+ listener->callback(smdk4x12_camera, buffers, buffers_count);
+
+list_continue_callback:
+ list = list->next;
+ }
+
+ do {
+ busy = 0;
+
+ list = (struct list_head *) smdk4x12_camera->capture_listeners;
+ while (list != NULL) {
+ listener = (struct smdk4x12_camera_capture_listener *) list;
+
+ if (listener->callback == NULL)
+ goto list_continue_busy;
+
+ busy |= listener->busy;
+
+list_continue_busy:
+ list = list->next;
+ }
+
+ if (busy)
+ usleep(1000);
+ } while (busy);
+
+ rc = smdk4x12_v4l2_qbuf_cap(smdk4x12_camera, 0, index);
+ if (rc < 0) {
+ ALOGE("%s: Unable to queue buffer", __func__);
+ goto error;
+ }
+
+ rc = 0;
+ goto complete;
+
+error:
+ rc = -1;
+
+complete:
+ if (buffers != NULL)
+ free(buffers);
+
+ return rc;
+}
+
+void *smdk4x12_camera_capture_thread(void *data)
+{
+ struct smdk4x12_camera *smdk4x12_camera;
+ int rc;
+
+ if (data == NULL)
+ return NULL;
+
+ smdk4x12_camera = (struct smdk4x12_camera *) data;
+
+ ALOGE("%s: Starting thread", __func__);
+ smdk4x12_camera->capture_thread_running = 1;
+
+ while (smdk4x12_camera->capture_thread_enabled) {
+ pthread_mutex_lock(&smdk4x12_camera->capture_lock_mutex);
+
+ while (smdk4x12_camera->capture_enabled) {
+ pthread_mutex_lock(&smdk4x12_camera->capture_mutex);
+
+ if (!smdk4x12_camera->capture_enabled) {
+ pthread_mutex_unlock(&smdk4x12_camera->capture_mutex);
+ break;
+ }
+
+ rc = smdk4x12_camera_capture(smdk4x12_camera);
+ if (rc < 0) {
+ ALOGE("%s: Unable to capture", __func__);
+ pthread_mutex_unlock(&smdk4x12_camera->capture_mutex);
+ break;
+ }
+
+ pthread_mutex_unlock(&smdk4x12_camera->capture_mutex);
+
+ // Wait a bit to let others lock the mutex if they need to
+ usleep(10);
+ }
+ }
+
+ smdk4x12_camera->capture_thread_running = 0;
+ ALOGE("%s: Exiting thread", __func__);
+
+ return NULL;
+}
+
+int smdk4x12_camera_capture_thread_start(struct smdk4x12_camera *smdk4x12_camera)
+{
+ pthread_attr_t thread_attr;
+ int rc;
+
+ if (smdk4x12_camera == NULL)
+ return -EINVAL;
+
+ ALOGD("%s()", __func__);
+
+ if (smdk4x12_camera->capture_thread_enabled) {
+ ALOGE("Capture thread was already started!");
+ return -1;
+ }
+
+ pthread_mutex_init(&smdk4x12_camera->capture_mutex, NULL);
+ pthread_mutex_init(&smdk4x12_camera->capture_lock_mutex, NULL);
+
+ // Initial lock
+ pthread_mutex_lock(&smdk4x12_camera->capture_lock_mutex);
+
+ pthread_attr_init(&thread_attr);
+ pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
+
+ smdk4x12_camera->capture_thread_enabled = 1;
+
+ rc = pthread_create(&smdk4x12_camera->capture_thread, &thread_attr, smdk4x12_camera_capture_thread, (void *) smdk4x12_camera);
+ if (rc < 0) {
+ ALOGE("%s: Unable to create thread", __func__);
+ goto error;
+ }
+
+ rc = 0;
+ goto complete;
+
+error:
+ pthread_mutex_destroy(&smdk4x12_camera->capture_mutex);
+ pthread_mutex_destroy(&smdk4x12_camera->capture_lock_mutex);
+
+ rc = -1;
+
+complete:
+ return rc;
+}
+
+void smdk4x12_camera_capture_thread_stop(struct smdk4x12_camera *smdk4x12_camera)
+{
+ int i;
+
+ if (smdk4x12_camera == NULL)
+ return;
+
+ ALOGD("%s()", __func__);
+
+ if (!smdk4x12_camera->capture_thread_enabled) {
+ ALOGE("Capture thread was already stopped!");
+ return;
+ }
+
+ smdk4x12_camera->capture_enabled = 0;
+ smdk4x12_camera->capture_thread_enabled = 0;
+
+ pthread_mutex_unlock(&smdk4x12_camera->capture_lock_mutex);
+
+ // Wait for the thread to end
+ i = 0;
+ while (smdk4x12_camera->capture_thread_running) {
+ if (i++ > 10000) {
+ ALOGE("Capture thread is taking too long to end, something is going wrong");
+ break;
+ }
+ usleep(100);
+ }
+
+ if (smdk4x12_camera->capture_enabled) {
+ pthread_mutex_lock(&smdk4x12_camera->capture_mutex);
+ smdk4x12_camera_capture_stop(smdk4x12_camera);
+ pthread_mutex_unlock(&smdk4x12_camera->capture_mutex);
+ }
+
+ pthread_mutex_destroy(&smdk4x12_camera->capture_mutex);
+ pthread_mutex_destroy(&smdk4x12_camera->capture_lock_mutex);
+}
+
+int smdk4x12_camera_capture_start(struct smdk4x12_camera *smdk4x12_camera)
+{
+ struct v4l2_streamparm fps_param;
+ int width, height, format;
+ int mbus_width, mbus_height;
+ int buffers_count, buffer_length;
+ camera_memory_t *memory = NULL;
+ int value;
+ int fd;
+ int rc;
+ int i;
+
+ if (smdk4x12_camera == NULL)
+ return -EINVAL;
+
+ ALOGD("%s()", __func__);
+
+ if (smdk4x12_camera->capture_enabled) {
+ ALOGE("Capture was already started!");
+ return -1;
+ }
+
+ width = smdk4x12_camera->capture_width;
+ height = smdk4x12_camera->capture_height;
+ format = smdk4x12_camera->capture_format;
+
+ // V4L2
+
+ if (format == V4L2_PIX_FMT_INTERLEAVED) {
+ ALOGD("Enabling hybrid capture");
+ rc = smdk4x12_v4l2_s_ctrl(smdk4x12_camera, 0, V4L2_CID_CAMERA_HYBRID, 1);
+ if (rc < 0) {
+ ALOGE("%s: Unable to set hybrid", __func__);
+ goto error;
+ }
+ }
+
+ if (smdk4x12_camera->camera_fimc_is) {
+ rc = smdk4x12_v4l2_s_ctrl(smdk4x12_camera, 0, V4L2_CID_IS_S_FORMAT_SCENARIO, smdk4x12_camera->fimc_is_mode);
+ if (rc < 0) {
+ ALOGE("%s: Unable to set FIMC-IS scenario", __func__);
+ goto error;
+ }
+ }
+
+ rc = smdk4x12_v4l2_enum_fmt_cap(smdk4x12_camera, 0, format);
+ if (rc < 0) {
+ ALOGE("%s: Unable to enumerate formats", __func__);
+ goto error;
+ }
+
+ mbus_width = width;
+ mbus_height = height;
+
+ if (smdk4x12_camera->camera_mbus_resolutions != NULL) {
+ for (i = 0; i < smdk4x12_camera->camera_mbus_resolutions_count; i++) {
+ if (smdk4x12_camera->camera_mbus_resolutions[i].width == width && smdk4x12_camera->camera_mbus_resolutions[i].height == height) {
+ mbus_width = smdk4x12_camera->camera_mbus_resolutions[i].mbus_width;
+ mbus_height = smdk4x12_camera->camera_mbus_resolutions[i].mbus_height;
+ break;
+ }
+ }
+ }
+
+ if (smdk4x12_camera->camera_fimc_is) {
+ // Set MBUS width/height/format
+ rc = smdk4x12_v4l2_s_fmt_pix(smdk4x12_camera, 0, V4L2_BUF_TYPE_PRIVATE, mbus_width, mbus_height, format, smdk4x12_camera->fimc_is_mode, V4L2_PIX_FMT_MODE_PREVIEW);
+ if (rc < 0) {
+ ALOGE("%s: Unable to set MBUS capture pixel format", __func__);
+ goto error;
+ }
+ }
+
+ rc = smdk4x12_v4l2_s_fmt_pix_cap(smdk4x12_camera, 0, width, height, format, V4L2_PIX_FMT_MODE_PREVIEW);
+ if (rc < 0) {
+ ALOGE("%s: Unable to set capture pixel format", __func__);
+ goto error;
+ }
+
+ if (!smdk4x12_camera->camera_fimc_is) {
+ // Set MBUS width/height/format
+ rc = smdk4x12_v4l2_s_fmt_pix(smdk4x12_camera, 0, V4L2_BUF_TYPE_PRIVATE, mbus_width, mbus_height, format, V4L2_FIELD_NONE, V4L2_PIX_FMT_MODE_PREVIEW);
+ if (rc < 0) {
+ ALOGE("%s: Unable to set MBUS capture pixel format", __func__);
+ goto error;
+ }
+ }
+
+ if (format == V4L2_PIX_FMT_INTERLEAVED)
+ rc = smdk4x12_v4l2_s_ctrl(smdk4x12_camera, 0, V4L2_CID_CACHEABLE, 0);
+ else
+ rc = smdk4x12_v4l2_s_ctrl(smdk4x12_camera, 0, V4L2_CID_CACHEABLE, 1);
+ if (rc < 0) {
+ ALOGE("%s: Unable to set cacheable", __func__);
+ goto error;
+ }
+
+ if (smdk4x12_camera->camera_fimc_is) {
+ rc = smdk4x12_v4l2_s_ctrl(smdk4x12_camera, 0, V4L2_CID_IS_S_SCENARIO_MODE, smdk4x12_camera->fimc_is_mode);
+ if (rc < 0) {
+ ALOGE("%s: Unable to set FIMC-IS scenario mode", __func__);
+ goto error;
+ }
+ }
+
+ if (format == V4L2_PIX_FMT_INTERLEAVED) {
+ // This must be set to 1 for interleaved data decoding
+ rc = smdk4x12_v4l2_s_ctrl(smdk4x12_camera, 0, V4L2_CID_EMBEDDEDDATA_ENABLE, 1);
+ if (rc < 0) {
+ ALOGE("%s: Unable to set embdedded data enable", __func__);
+ goto error;
+ }
+ }
+
+ // Let's assume FIMC0 has memory available through mmap
+
+ for (i = SMDK4x12_CAMERA_CAPTURE_BUFFERS_COUNT; i > 0; i--) {
+ rc = smdk4x12_v4l2_reqbufs_cap(smdk4x12_camera, 0, i);
+ if (rc >= 0)
+ break;
+ }
+
+ if (rc < 0) {
+ ALOGE("%s: Unable to request buffers", __func__);
+ goto error;
+ }
+
+ buffers_count = rc;
+ ALOGD("Found %d buffers available for capture!", buffers_count);
+
+ memset(&fps_param, 0, sizeof(fps_param));
+ fps_param.parm.capture.timeperframe.numerator = 1;
+ fps_param.parm.capture.timeperframe.denominator = smdk4x12_camera->preview_fps;
+
+ rc = smdk4x12_v4l2_s_parm_cap(smdk4x12_camera, 0, &fps_param);
+ if (rc < 0) {
+ ALOGE("%s: Unable to set fps", __func__);
+ goto error;
+ }
+
+ for (i = 0; i < buffers_count; i++) {
+ rc = smdk4x12_v4l2_querybuf_cap(smdk4x12_camera, 0, i);
+ if (rc < 0) {
+ ALOGE("%s: Unable to query buffers", __func__);
+ goto error;
+ }
+ }
+
+ buffer_length = rc;
+
+ value = smdk4x12_v4l2_s_ctrl(smdk4x12_camera, 0, V4L2_CID_PADDR_Y, 0);
+ if (value == 0 || value == (int) 0xffffffff) {
+ ALOGE("%s: Unable to get address", __func__);
+ goto error;
+ }
+
+ smdk4x12_camera->capture_memory_address = value;
+
+ if (SMDK4x12_CAMERA_CALLBACK_DEFINED(request_memory)) {
+ fd = smdk4x12_v4l2_fd(smdk4x12_camera, 0);
+ if (fd < 0) {
+ ALOGE("%s: Unable to get v4l2 fd for id %d", __func__, 0);
+ goto error;
+ }
+
+ smdk4x12_camera->capture_memory = NULL;
+
+ memory = smdk4x12_camera->callbacks.request_memory(fd, buffer_length, buffers_count, smdk4x12_camera->callbacks.user);
+ if (memory == NULL || memory->data == NULL || memory->data == MAP_FAILED) {
+ ALOGE("%s: Unable to request memory", __func__);
+ goto error;
+ }
+
+ smdk4x12_camera->capture_memory = memory;
+ } else {
+ ALOGE("%s: No memory request function!", __func__);
+ goto error;
+ }
+
+ if (format == V4L2_PIX_FMT_INTERLEAVED) {
+ smdk4x12_camera->capture_yuv_buffer = malloc(buffer_length);
+ smdk4x12_camera->capture_jpeg_buffer = malloc(buffer_length);
+ }
+
+ for (i = 0; i < buffers_count; i++) {
+ rc = smdk4x12_v4l2_qbuf_cap(smdk4x12_camera, 0, i);
+ if (rc < 0) {
+ ALOGE("%s: Unable to queue buffer", __func__);
+ goto error;
+ }
+ }
+
+ smdk4x12_camera->capture_buffers_count = buffers_count;
+ smdk4x12_camera->capture_buffer_length = buffer_length;
+
+ rc = smdk4x12_v4l2_s_ctrl(smdk4x12_camera, 0, V4L2_CID_ROTATION,
+ smdk4x12_camera->camera_rotation);
+ if (rc < 0) {
+ ALOGE("%s: Unable to set rotation", __func__);
+ goto error;
+ }
+
+ rc = smdk4x12_v4l2_s_ctrl(smdk4x12_camera, 0, V4L2_CID_HFLIP,
+ smdk4x12_camera->camera_hflip);
+ if (rc < 0) {
+ ALOGE("%s: Unable to set hflip", __func__);
+ goto error;
+ }
+
+ rc = smdk4x12_v4l2_s_ctrl(smdk4x12_camera, 0, V4L2_CID_VFLIP,
+ smdk4x12_camera->camera_vflip);
+ if (rc < 0) {
+ ALOGE("%s: Unable to set vflip", __func__);
+ goto error;
+ }
+
+ rc = smdk4x12_v4l2_streamon_cap(smdk4x12_camera, 0);
+ if (rc < 0) {
+ ALOGE("%s: Unable to start stream", __func__);
+ goto error;
+ }
+
+ rc = smdk4x12_v4l2_s_ctrl(smdk4x12_camera, 0, V4L2_CID_CAMERA_SCENE_MODE, smdk4x12_camera->scene_mode);
+ if (rc < 0) {
+ ALOGE("%s: Unable to set scene mode", __func__);
+ goto error;
+ }
+
+ smdk4x12_camera->capture_enabled = 1;
+ pthread_mutex_unlock(&smdk4x12_camera->capture_lock_mutex);
+
+ rc = 0;
+ goto complete;
+
+error:
+ if (smdk4x12_camera->capture_memory != NULL && smdk4x12_camera->capture_memory->release != NULL) {
+ smdk4x12_camera->capture_memory->release(smdk4x12_camera->capture_memory);
+ smdk4x12_camera->capture_memory = NULL;
+ }
+
+ rc = -1;
+
+complete:
+ return rc;
+}
+
+void smdk4x12_camera_capture_stop(struct smdk4x12_camera *smdk4x12_camera)
+{
+ int rc;
+ int i;
+
+ if (smdk4x12_camera == NULL)
+ return;
+
+ ALOGD("%s()", __func__);
+
+ if (!smdk4x12_camera->capture_enabled) {
+ ALOGE("Capture was already stopped!");
+ return;
+ }
+
+ if (smdk4x12_camera->capture_format == V4L2_PIX_FMT_INTERLEAVED) {
+ ALOGD("Disabling hybrid capture");
+ rc = smdk4x12_v4l2_s_ctrl(smdk4x12_camera, 0, V4L2_CID_CAMERA_HYBRID, 0);
+ if (rc < 0)
+ ALOGE("%s: Unable to set hybrid", __func__);
+ }
+
+ rc = smdk4x12_v4l2_streamoff_cap(smdk4x12_camera, 0);
+ if (rc < 0) {
+ ALOGE("%s: Unable to stop stream", __func__);
+ }
+
+ if (smdk4x12_camera->capture_memory != NULL && smdk4x12_camera->capture_memory->release != NULL) {
+ smdk4x12_camera->capture_memory->release(smdk4x12_camera->capture_memory);
+ smdk4x12_camera->capture_memory = NULL;
+ }
+
+ if (smdk4x12_camera->capture_yuv_buffer != NULL) {
+ free(smdk4x12_camera->capture_yuv_buffer);
+ smdk4x12_camera->capture_yuv_buffer = NULL;
+ }
+
+ if (smdk4x12_camera->capture_jpeg_buffer != NULL) {
+ free(smdk4x12_camera->capture_jpeg_buffer);
+ smdk4x12_camera->capture_jpeg_buffer = NULL;
+ }
+
+ smdk4x12_camera->capture_enabled = 0;
+}
+
+int smdk4x12_camera_capture_setup(struct smdk4x12_camera *smdk4x12_camera)
+{
+ struct smdk4x12_camera_capture_listener *listener;
+ struct list_head *list;
+ int width, height, format;
+ int rc;
+
+ if (smdk4x12_camera == NULL)
+ return -EINVAL;
+
+ ALOGD("%s()", __func__);
+
+ // No listener left
+ if (smdk4x12_camera->capture_listeners == NULL && smdk4x12_camera->capture_enabled) {
+ smdk4x12_camera_capture_stop(smdk4x12_camera);
+ return 0;
+ }
+
+ width = height = format = 0;
+
+ list = (struct list_head *) smdk4x12_camera->capture_listeners;
+ while (list != NULL) {
+ listener = (struct smdk4x12_camera_capture_listener *) list;
+
+ // Interleaved format already has the correct width and height for picture set through ioctl
+ if (smdk4x12_camera->camera_capture_format == V4L2_PIX_FMT_INTERLEAVED)
+ if (listener->format == V4L2_PIX_FMT_JPEG || listener->format == V4L2_PIX_FMT_INTERLEAVED)
+ goto list_continue;
+
+ if (listener->width >= width && listener->height >= height) {
+ width = listener->width;
+ height = listener->height;
+ format = listener->format;
+ }
+
+list_continue:
+ list = list->next;
+ }
+
+ // Override the capture format
+ if (smdk4x12_camera->camera_capture_format)
+ format = smdk4x12_camera->camera_capture_format;
+
+ // Only picture is listening, but we need some preview size anyway
+ if (format == V4L2_PIX_FMT_INTERLEAVED && (width == 0 || height == 0)) {
+ width = smdk4x12_camera->preview_width;
+ height = smdk4x12_camera->preview_height;
+ }
+
+ ALOGD("%s: Selected width: %d, height: %d, format: 0x%x", __func__, width, height, format);
+
+ if (!smdk4x12_camera->capture_enabled) {
+ smdk4x12_camera->capture_width = width;
+ smdk4x12_camera->capture_height = height;
+ smdk4x12_camera->capture_format = format;
+
+ rc = smdk4x12_camera_capture_start(smdk4x12_camera);
+ if (rc < 0) {
+ ALOGE("%s: Unable to start capture", __func__);
+ return -1;
+ }
+ } else if (smdk4x12_camera->capture_width != width || smdk4x12_camera->capture_height != height || smdk4x12_camera->capture_format != format) {
+ smdk4x12_camera_capture_stop(smdk4x12_camera);
+
+ smdk4x12_camera->capture_width = width;
+ smdk4x12_camera->capture_height = height;
+ smdk4x12_camera->capture_format = format;
+
+ rc = smdk4x12_camera_capture_start(smdk4x12_camera);
+ if (rc < 0) {
+ ALOGE("%s: Unable to start capture", __func__);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+struct smdk4x12_camera_capture_listener *smdk4x12_camera_capture_listener_register(
+ struct smdk4x12_camera *smdk4x12_camera, int width, int height, int format,
+ int (*callback)(struct smdk4x12_camera *smdk4x12_camera, struct smdk4x12_camera_buffer *buffers, int buffers_count))
+{
+ struct smdk4x12_camera_capture_listener *listener = NULL;
+ struct list_head *list_end;
+ struct list_head *list;
+ int rc;
+
+ if (smdk4x12_camera == NULL || callback == NULL)
+ return NULL;
+
+ pthread_mutex_lock(&smdk4x12_camera->capture_mutex);
+
+ listener = calloc(1, sizeof(struct smdk4x12_camera_capture_listener));
+ if (listener == NULL)
+ goto error;
+
+ listener->width = width;
+ listener->height = height;
+ listener->format = format;
+ listener->callback = callback;
+ listener->busy = 0;
+
+ list_end = (struct list_head *) smdk4x12_camera->capture_listeners;
+ while (list_end != NULL && list_end->next != NULL)
+ list_end = list_end->next;
+
+ list = (struct list_head *) listener;
+ list_head_insert(list, list_end, NULL);
+
+ if (smdk4x12_camera->capture_listeners == NULL)
+ smdk4x12_camera->capture_listeners = listener;
+
+ rc = smdk4x12_camera_capture_setup(smdk4x12_camera);
+ if (rc < 0) {
+ ALOGE("%s: Unable to setup capture", __func__);
+ goto error;
+ }
+
+ rc = 0;
+ goto complete;
+
+error:
+ listener = NULL;
+
+complete:
+ pthread_mutex_unlock(&smdk4x12_camera->capture_mutex);
+
+ return listener;
+}
+
+void smdk4x12_camera_capture_listener_unregister(
+ struct smdk4x12_camera *smdk4x12_camera,
+ struct smdk4x12_camera_capture_listener *listener)
+{
+ struct list_head *list;
+ int rc;
+
+ if (smdk4x12_camera == NULL || listener == NULL)
+ return;
+
+ pthread_mutex_lock(&smdk4x12_camera->capture_mutex);
+
+ list = (struct list_head *) smdk4x12_camera->capture_listeners;
+ while (list != NULL) {
+ if ((void *) list == (void *) listener) {
+ list_head_remove(list);
+
+ if ((void *) list == (void *) smdk4x12_camera->capture_listeners)
+ smdk4x12_camera->capture_listeners = (struct smdk4x12_camera_capture_listener *) list->next;
+
+ memset(listener, 0, sizeof(struct smdk4x12_camera_capture_listener));
+ free(listener);
+
+ break;
+ }
+list_continue:
+ list = list->next;
+ }
+
+ rc = smdk4x12_camera_capture_setup(smdk4x12_camera);
+ if (rc < 0) {
+ ALOGE("%s: Unable to setup capture", __func__);
+ goto complete;
+ }
+
+complete:
+ pthread_mutex_unlock(&smdk4x12_camera->capture_mutex);
+}
+
+// Preview
+
+int smdk4x12_camera_preview_output_start(struct smdk4x12_camera *smdk4x12_camera)
+{
+ struct smdk4x12_v4l2_output *output;
+ int rc;
+
+ if (smdk4x12_camera == NULL)
+ return -EINVAL;
+
+ ALOGD("%s()", __func__);
+
+ if (smdk4x12_camera->preview_output_enabled) {
+ ALOGE("Preview was already started!");
+ return -1;
+ }
+
+ output = &smdk4x12_camera->preview_output;
+
+ memset(output, 0, sizeof(struct smdk4x12_v4l2_output));
+ output->v4l2_id = 1;
+ output->width = smdk4x12_camera->preview_width;
+ output->height = smdk4x12_camera->preview_height;
+ output->format = smdk4x12_camera->preview_format;
+ output->buffer_width = smdk4x12_camera->preview_buffer.width;
+ output->buffer_height = smdk4x12_camera->preview_buffer.height;
+ output->buffer_format = smdk4x12_camera->preview_buffer.format;
+ output->buffers_count = SMDK4x12_CAMERA_PREVIEW_BUFFERS_COUNT;
+
+ rc = smdk4x12_v4l2_output_start(smdk4x12_camera, output);
+ if (rc < 0) {
+ ALOGE("%s: Unable to start preview output", __func__);
+ goto error;
+ }
+
+ smdk4x12_camera->preview_output_enabled = 1;
+
+ rc = 0;
+ goto complete;
+
+error:
+ rc = -1;
+
+complete:
+ return rc;
+}
+
+void smdk4x12_camera_preview_output_stop(struct smdk4x12_camera *smdk4x12_camera)
+{
+ struct smdk4x12_v4l2_output *output;
+
+ if (smdk4x12_camera == NULL)
+ return;
+
+ ALOGD("%s()", __func__);
+
+ if (!smdk4x12_camera->preview_output_enabled) {
+ ALOGE("Preview was already stopped!");
+ return;
+ }
+
+ output = &smdk4x12_camera->preview_output;
+
+ smdk4x12_v4l2_output_stop(smdk4x12_camera, output);
+
+ smdk4x12_camera->preview_output_enabled = 0;
+}
+
+int smdk4x12_camera_preview_callback(struct smdk4x12_camera *smdk4x12_camera,
+ struct smdk4x12_camera_buffer *buffers, int buffers_count)
+{
+ struct smdk4x12_camera_buffer *buffer = NULL;
+ int width, height, format;
+ int buffer_width, buffer_height, buffer_format;
+ int rc;
+ int i;
+
+ if (smdk4x12_camera == NULL || buffers == NULL || buffers_count <= 0)
+ return -EINVAL;
+
+// ALOGD("%s()", __func__);
+
+ if (smdk4x12_camera->preview_listener == NULL)
+ return -1;
+
+ if (smdk4x12_camera->preview_listener->busy) {
+ ALOGE("%s: Dropping buffer", __func__);
+ return 0;
+ }
+
+ smdk4x12_camera->preview_listener->busy = 1;
+
+ width = smdk4x12_camera->preview_width;
+ height = smdk4x12_camera->preview_height;
+ format = smdk4x12_camera->preview_format;
+
+ for (i = 0; i < buffers_count; i++) {
+ if (buffers->format == V4L2_PIX_FMT_JPEG)
+ goto buffers_continue;
+
+ if (buffers->format == V4L2_PIX_FMT_INTERLEAVED)
+ goto buffers_continue;
+
+ // Optimal buffer
+ if (buffers->width == width && buffers->height == height) {
+ buffer = buffers;
+ break;
+ }
+
+ // Might-work buffer, but not optimal
+ buffer = buffers;
+
+buffers_continue:
+ buffers = (struct smdk4x12_camera_buffer *) ((unsigned char *) buffers + sizeof(struct smdk4x12_camera_buffer));
+ }
+
+ if (buffer == NULL) {
+ ALOGE("%s: Unable to find an appropriate buffer for preview", __func__);
+ smdk4x12_camera->preview_listener->busy = 0;
+ return 0;
+ }
+
+ buffer_width = buffer->width;
+ buffer_height = buffer->height;
+ buffer_format = buffer->format;
+
+ pthread_mutex_lock(&smdk4x12_camera->preview_mutex);
+
+ if (buffer_width != width || buffer_height != height || buffer_format != format) {
+ if (!smdk4x12_camera->preview_output_enabled) {
+ memcpy(&smdk4x12_camera->preview_buffer, buffer, sizeof(struct smdk4x12_camera_buffer));
+
+ rc = smdk4x12_camera_preview_output_start(smdk4x12_camera);
+ if (rc < 0) {
+ ALOGE("%s: Unable to start preview", __func__);
+ goto error;
+ }
+ } else if (smdk4x12_camera->preview_buffer.width != buffer_width || smdk4x12_camera->preview_buffer.height != buffer_height || smdk4x12_camera->preview_buffer.format != buffer_format) {
+ smdk4x12_camera_preview_output_stop(smdk4x12_camera);
+
+ memcpy(&smdk4x12_camera->preview_buffer, buffer, sizeof(struct smdk4x12_camera_buffer));
+
+ rc = smdk4x12_camera_preview_output_start(smdk4x12_camera);
+ if (rc < 0) {
+ ALOGE("%s: Unable to start preview", __func__);
+ goto error;
+ }
+ } else {
+ memcpy(&smdk4x12_camera->preview_buffer, buffer, sizeof(struct smdk4x12_camera_buffer));
+ }
+ } else {
+ // The buffer format exactly matches our expectations
+
+ if (smdk4x12_camera->preview_output_enabled)
+ smdk4x12_camera_preview_output_stop(smdk4x12_camera);
+
+ memcpy(&smdk4x12_camera->preview_buffer, buffer, sizeof(struct smdk4x12_camera_buffer));
+ }
+
+ pthread_mutex_unlock(&smdk4x12_camera->preview_lock_mutex);
+
+ pthread_mutex_unlock(&smdk4x12_camera->preview_mutex);
+
+ rc = 0;
+ goto complete;
+
+error:
+ pthread_mutex_unlock(&smdk4x12_camera->preview_mutex);
+
+ smdk4x12_camera->preview_listener->busy = 0;
+
+ rc = -1;
+
+complete:
+ return rc;
+}
+
+int smdk4x12_camera_preview(struct smdk4x12_camera *smdk4x12_camera)
+{
+ struct smdk4x12_v4l2_output *output;
+ int width, height, format;
+ buffer_handle_t *window_buffer;
+ void *window_data;
+ int window_stride;
+ camera_memory_t *memory;
+ void *memory_pointer;
+ int memory_index;
+ int memory_size;
+ int rc;
+
+ if (smdk4x12_camera == NULL)
+ goto error;
+
+// ALOGD("%s()", __func__);
+
+ width = smdk4x12_camera->preview_width;
+ height = smdk4x12_camera->preview_height;
+ format = smdk4x12_camera->preview_format;
+
+ output = &smdk4x12_camera->preview_output;
+
+ if (smdk4x12_camera->preview_output_enabled) {
+ rc = smdk4x12_v4l2_output(smdk4x12_camera, output, smdk4x12_camera->preview_buffer.address);
+ if (rc < 0) {
+ ALOGE("%s: Unable to output preview", __func__);
+ goto error;
+ }
+
+ memory = output->memory;
+ memory_index = output->memory_index;
+ memory_pointer = (void *) ((unsigned char *) memory->data + output->buffer_length * memory_index);
+ memory_size = output->buffer_length;
+ } else {
+ // In that case, we can directly use the capture memory
+ memory = smdk4x12_camera->capture_memory;
+ memory_index = smdk4x12_camera->capture_memory_index;
+ memory_pointer = smdk4x12_camera->preview_buffer.pointer;
+ memory_size = smdk4x12_camera->preview_buffer.length;
+ }
+
+ if (smdk4x12_camera->preview_window != NULL && smdk4x12_camera->gralloc != NULL) {
+ smdk4x12_camera->preview_window->dequeue_buffer(smdk4x12_camera->preview_window, &window_buffer, &window_stride);
+ smdk4x12_camera->gralloc->lock(smdk4x12_camera->gralloc, *window_buffer, GRALLOC_USAGE_SW_WRITE_OFTEN, 0, 0, width, height, &window_data);
+
+ if (window_data == NULL) {
+ ALOGE("%s: Unable to lock gralloc", __func__);
+ goto error;
+ }
+
+ memcpy(window_data, memory_pointer, memory_size);
+
+ smdk4x12_camera->gralloc->unlock(smdk4x12_camera->gralloc, *window_buffer);
+ smdk4x12_camera->preview_window->enqueue_buffer(smdk4x12_camera->preview_window, window_buffer);
+ }
+
+ if (SMDK4x12_CAMERA_MSG_ENABLED(CAMERA_MSG_PREVIEW_FRAME) && SMDK4x12_CAMERA_CALLBACK_DEFINED(data) && !smdk4x12_camera->callback_lock) {
+ smdk4x12_camera->callbacks.data(CAMERA_MSG_PREVIEW_FRAME, memory, memory_index, NULL, smdk4x12_camera->callbacks.user);
+ }
+
+ if (smdk4x12_camera->preview_output_enabled) {
+ rc = smdk4x12_v4l2_output_release(smdk4x12_camera, output);
+ if (rc < 0) {
+ ALOGE("%s: Unable to release preview output", __func__);
+ goto error;
+ }
+ }
+
+ rc = 0;
+ goto complete;
+
+error:
+ rc = -1;
+
+complete:
+ smdk4x12_camera->preview_listener->busy = 0;
+
+ return rc;
+}
+
+void *smdk4x12_camera_preview_thread(void *data)
+{
+ struct smdk4x12_camera *smdk4x12_camera;
+ int rc;
+
+ if (data == NULL)
+ return NULL;
+
+ smdk4x12_camera = (struct smdk4x12_camera *) data;
+
+ ALOGE("%s: Starting thread", __func__);
+ smdk4x12_camera->preview_thread_running = 1;
+
+ while (smdk4x12_camera->preview_thread_enabled) {
+ pthread_mutex_lock(&smdk4x12_camera->preview_lock_mutex);
+
+ pthread_mutex_lock(&smdk4x12_camera->preview_mutex);
+
+ if (smdk4x12_camera->preview_listener == NULL) {
+ pthread_mutex_unlock(&smdk4x12_camera->preview_mutex);
+ break;
+ }
+
+ if (smdk4x12_camera->preview_listener->busy) {
+ rc = smdk4x12_camera_preview(smdk4x12_camera);
+ if (rc < 0) {
+ ALOGE("%s: Unable to preview", __func__);
+ pthread_mutex_unlock(&smdk4x12_camera->preview_mutex);
+ break;
+ }
+ }
+
+ pthread_mutex_unlock(&smdk4x12_camera->preview_mutex);
+ }
+
+ smdk4x12_camera->preview_thread_running = 0;
+ ALOGE("%s: Exiting thread", __func__);
+
+ return NULL;
+}
+
+int smdk4x12_camera_preview_thread_start(struct smdk4x12_camera *smdk4x12_camera)
+{
+ struct smdk4x12_camera_capture_listener *listener;
+ pthread_attr_t thread_attr;
+ int rc;
+
+ if (smdk4x12_camera == NULL)
+ return -EINVAL;
+
+ ALOGD("%s()", __func__);
+
+ if (smdk4x12_camera->preview_thread_enabled) {
+ ALOGE("Preview thread was already started!");
+ return -1;
+ }
+
+ pthread_mutex_init(&smdk4x12_camera->preview_mutex, NULL);
+ pthread_mutex_init(&smdk4x12_camera->preview_lock_mutex, NULL);
+
+ // Initial lock
+ pthread_mutex_lock(&smdk4x12_camera->preview_lock_mutex);
+
+ pthread_attr_init(&thread_attr);
+ pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
+
+ smdk4x12_camera->preview_thread_enabled = 1;
+
+ rc = pthread_create(&smdk4x12_camera->preview_thread, &thread_attr, smdk4x12_camera_preview_thread, (void *) smdk4x12_camera);
+ if (rc < 0) {
+ ALOGE("%s: Unable to create thread", __func__);
+ goto error;
+ }
+
+ listener = smdk4x12_camera_capture_listener_register(smdk4x12_camera, smdk4x12_camera->preview_width, smdk4x12_camera->preview_height, smdk4x12_camera->preview_format, smdk4x12_camera_preview_callback);
+ if (listener == NULL) {
+ ALOGE("%s: Unable to register preview capture listener", __func__);
+ goto error;
+ }
+
+ smdk4x12_camera->preview_listener = listener;
+
+ rc = 0;
+ goto complete;
+
+error:
+ pthread_mutex_destroy(&smdk4x12_camera->preview_mutex);
+ pthread_mutex_destroy(&smdk4x12_camera->preview_lock_mutex);
+
+ rc = -1;
+
+complete:
+ return rc;
+}
+
+void smdk4x12_camera_preview_thread_stop(struct smdk4x12_camera *smdk4x12_camera)
+{
+ int i;
+
+ if (smdk4x12_camera == NULL)
+ return;
+
+ ALOGD("%s()", __func__);
+
+ if (!smdk4x12_camera->preview_thread_enabled) {
+ ALOGE("Preview thread was already stopped!");
+ return;
+ }
+
+ if (smdk4x12_camera->preview_listener != NULL) {
+ smdk4x12_camera_capture_listener_unregister(smdk4x12_camera, smdk4x12_camera->preview_listener);
+ smdk4x12_camera->preview_listener = NULL;
+ }
+
+ smdk4x12_camera->preview_thread_enabled = 0;
+
+ pthread_mutex_unlock(&smdk4x12_camera->preview_lock_mutex);
+
+ // Wait for the thread to end
+ i = 0;
+ while (smdk4x12_camera->preview_thread_running) {
+ if (i++ > 10000) {
+ ALOGE("Preview thread is taking too long to end, something is going wrong");
+ break;
+ }
+ usleep(100);
+ }
+
+ if (smdk4x12_camera->preview_output_enabled) {
+ pthread_mutex_lock(&smdk4x12_camera->preview_mutex);
+ smdk4x12_camera_preview_output_stop(smdk4x12_camera);
+ pthread_mutex_unlock(&smdk4x12_camera->preview_mutex);
+ }
+
+ pthread_mutex_destroy(&smdk4x12_camera->preview_mutex);
+ pthread_mutex_destroy(&smdk4x12_camera->preview_lock_mutex);
+
+ // Invalidate the preview window
+ smdk4x12_camera->preview_window = NULL;
+}
+
+// Picture
+
+int smdk4x12_camera_picture_callback(struct smdk4x12_camera *smdk4x12_camera,
+ struct smdk4x12_camera_buffer *buffers, int buffers_count)
+{
+ struct smdk4x12_camera_buffer *jpeg_buffer = NULL;
+ struct smdk4x12_camera_buffer *jpeg_thumbnail_buffer = NULL;
+ struct smdk4x12_camera_buffer *yuv_buffer = NULL;
+ struct smdk4x12_camera_buffer *yuv_thumbnail_buffer = NULL;
+ int width, height;
+ int thumbnail_width, thumbnail_height;
+ time_t timestamp;
+ int rc;
+ int i;
+
+ if (smdk4x12_camera == NULL || buffers == NULL || buffers_count <= 0)
+ return -EINVAL;
+
+// ALOGD("%s()", __func__);
+
+ width = smdk4x12_camera->picture_width;
+ height = smdk4x12_camera->picture_height;
+ thumbnail_width = smdk4x12_camera->jpeg_thumbnail_width;
+ thumbnail_height = smdk4x12_camera->jpeg_thumbnail_height;
+
+ if (smdk4x12_camera->picture_completed)
+ return -1;
+
+ if (smdk4x12_camera->picture_listener == NULL)
+ return -1;
+
+ if (smdk4x12_camera->picture_listener->busy) {
+ ALOGE("%s: Dropping buffer", __func__);
+ return 0;
+ }
+
+ pthread_mutex_lock(&smdk4x12_camera->picture_mutex);
+
+ if (!smdk4x12_camera->picture_enabled && !smdk4x12_camera->camera_fimc_is) {
+ if (smdk4x12_camera->camera_capture_format == V4L2_PIX_FMT_INTERLEAVED && smdk4x12_camera->zoom == 0 && smdk4x12_camera->focus_mode == FOCUS_MODE_CONTINOUS_PICTURE) {
+ if (smdk4x12_camera->auto_focus_result == CAMERA_AF_STATUS_FAIL) {
+ // We don't want to take a picture out of focus, so wait a bit
+ timestamp = time(NULL);
+
+ if (smdk4x12_camera->picture_focus_timestamp > 0) {
+ if ((timestamp - smdk4x12_camera->picture_focus_timestamp) > 2) {
+ ALOGE("%s: Picture will be taken out of focus", __func__);
+ } else {
+ pthread_mutex_unlock(&smdk4x12_camera->picture_mutex);
+ return 0;
+ }
+ } else {
+ smdk4x12_camera->picture_focus_timestamp = timestamp;
+ pthread_mutex_unlock(&smdk4x12_camera->picture_mutex);
+ return 0;
+ }
+ } else if (smdk4x12_camera->auto_focus_result == CAMERA_AF_STATUS_IN_PROGRESS) {
+ ALOGD("%s: Not asking for picture until auto focus is done", __func__);
+ pthread_mutex_unlock(&smdk4x12_camera->picture_mutex);
+ return 0;
+ }
+ }
+
+ smdk4x12_camera->picture_focus_timestamp = 0;
+
+ rc = smdk4x12_v4l2_s_ctrl(smdk4x12_camera, 0, V4L2_CID_CAMERA_CAPTURE, 0);
+ if (rc < 0) {
+ ALOGE("%s: Unable to set capture", __func__);
+ goto error;
+ }
+
+ if (smdk4x12_camera->camera_capture_format == V4L2_PIX_FMT_INTERLEAVED && !smdk4x12_camera->capture_hybrid) {
+ rc = smdk4x12_v4l2_s_ctrl(smdk4x12_camera, 0, V4L2_CID_CAMERA_HYBRID_CAPTURE, 1);
+ if (rc < 0) {
+ ALOGE("%s: Unable to set hybrid capture", __func__);
+ goto error;
+ }
+
+ smdk4x12_camera->capture_hybrid = 1;
+ }
+
+ smdk4x12_camera->picture_enabled = 1;
+
+ pthread_mutex_unlock(&smdk4x12_camera->picture_mutex);
+ return 0;
+ }
+
+ pthread_mutex_unlock(&smdk4x12_camera->picture_mutex);
+
+ smdk4x12_camera->picture_listener->busy = 1;
+
+ // Let's assume the picture format is JPEG
+
+ for (i = 0; i < buffers_count; i++) {
+ if (buffers->format == V4L2_PIX_FMT_JPEG) {
+ if (buffers->width == width && buffers->height == height)
+ jpeg_buffer = buffers;
+ else if (buffers->width == thumbnail_width && buffers->height == thumbnail_height)
+ jpeg_thumbnail_buffer = buffers;
+ } else {
+ if (buffers->width >= width && buffers->height >= height)
+ yuv_buffer = buffers;
+ if (buffers->width >= thumbnail_width && buffers->height >= thumbnail_height)
+ yuv_thumbnail_buffer = buffers;
+ }
+
+buffers_continue:
+ buffers = (struct smdk4x12_camera_buffer *) ((unsigned char *) buffers + sizeof(struct smdk4x12_camera_buffer));
+ }
+
+ if (jpeg_buffer == NULL && yuv_buffer == NULL) {
+// ALOGE("%s: Unable to find an appropriate buffer for picture", __func__);
+ smdk4x12_camera->picture_listener->busy = 0;
+ return 0;
+ }
+
+ // Interleaved must not use a preview frame as picture
+ if (smdk4x12_camera->camera_capture_format == V4L2_PIX_FMT_INTERLEAVED && jpeg_buffer == NULL) {
+ smdk4x12_camera->picture_listener->busy = 0;
+ return 0;
+ }
+
+ pthread_mutex_lock(&smdk4x12_camera->picture_mutex);
+
+ if (jpeg_buffer == NULL)
+ memset(&smdk4x12_camera->picture_jpeg_buffer, 0, sizeof(smdk4x12_camera->picture_jpeg_buffer));
+ else
+ memcpy(&smdk4x12_camera->picture_jpeg_buffer, jpeg_buffer, sizeof(struct smdk4x12_camera_buffer));
+
+ if (jpeg_thumbnail_buffer == NULL)
+ memset(&smdk4x12_camera->picture_jpeg_thumbnail_buffer, 0, sizeof(smdk4x12_camera->picture_jpeg_thumbnail_buffer));
+ else
+ memcpy(&smdk4x12_camera->picture_jpeg_thumbnail_buffer, jpeg_thumbnail_buffer, sizeof(struct smdk4x12_camera_buffer));
+
+ if (yuv_buffer == NULL)
+ memset(&smdk4x12_camera->picture_yuv_buffer, 0, sizeof(smdk4x12_camera->picture_yuv_buffer));
+ else
+ memcpy(&smdk4x12_camera->picture_yuv_buffer, yuv_buffer, sizeof(struct smdk4x12_camera_buffer));
+
+ if (yuv_thumbnail_buffer == NULL)
+ memset(&smdk4x12_camera->picture_yuv_thumbnail_buffer, 0, sizeof(smdk4x12_camera->picture_yuv_thumbnail_buffer));
+ else
+ memcpy(&smdk4x12_camera->picture_yuv_thumbnail_buffer, yuv_thumbnail_buffer, sizeof(struct smdk4x12_camera_buffer));
+
+ pthread_mutex_unlock(&smdk4x12_camera->picture_lock_mutex);
+
+ pthread_mutex_unlock(&smdk4x12_camera->picture_mutex);
+
+ rc = 0;
+ goto complete;
+
+error:
+ pthread_mutex_unlock(&smdk4x12_camera->picture_mutex);
+
+ smdk4x12_camera->picture_listener->busy = 0;
+
+ rc = -1;
+
+complete:
+ return rc;
+}
+
+int smdk4x12_camera_picture(struct smdk4x12_camera *smdk4x12_camera)
+{
+ struct smdk4x12_camera_buffer *jpeg_buffer;
+ struct smdk4x12_camera_buffer *jpeg_thumbnail_buffer;
+ struct smdk4x12_camera_buffer *yuv_buffer;
+ struct smdk4x12_camera_buffer *yuv_thumbnail_buffer;
+ struct smdk4x12_v4l2_output output;
+ struct smdk4x12_jpeg jpeg;
+ struct smdk4x12_exif exif;
+ int output_enabled = 0;
+ int width, height, format;
+ int buffer_width, buffer_height, buffer_format, buffer_address;
+ camera_memory_t *memory = NULL;
+ int memory_size;
+ unsigned char *p;
+ camera_memory_t *jpeg_memory = NULL;
+ void *jpeg_data = NULL;
+ int jpeg_size = 0;
+ camera_memory_t *jpeg_thumbnail_memory = NULL;
+ void *jpeg_thumbnail_data = NULL;
+ int jpeg_thumbnail_size = 0;
+ void *yuv_data = NULL;
+ int yuv_address;
+ int yuv_size = 0;
+ void *yuv_thumbnail_data = NULL;
+ int yuv_thumbnail_address;
+ int yuv_thumbnail_size = 0;
+ int rc;
+
+ if (smdk4x12_camera == NULL)
+ goto error;
+
+// ALOGD("%s()", __func__);
+
+ jpeg_buffer = &smdk4x12_camera->picture_jpeg_buffer;
+ jpeg_thumbnail_buffer = &smdk4x12_camera->picture_jpeg_thumbnail_buffer;
+ yuv_buffer = &smdk4x12_camera->picture_yuv_buffer;
+ yuv_thumbnail_buffer = &smdk4x12_camera->picture_yuv_thumbnail_buffer;
+
+ if (jpeg_buffer->pointer != NULL && jpeg_buffer->length > 0) {
+ jpeg_data = jpeg_buffer->pointer;
+ jpeg_size = jpeg_buffer->length;
+ }
+
+ if (jpeg_thumbnail_buffer->pointer != NULL && jpeg_thumbnail_buffer->length > 0) {
+ jpeg_thumbnail_data = jpeg_thumbnail_buffer->pointer;
+ jpeg_thumbnail_size = jpeg_thumbnail_buffer->length;
+ }
+
+ if (yuv_buffer->pointer != NULL && yuv_buffer->length > 0) {
+ yuv_data = yuv_buffer->pointer;
+ yuv_address = yuv_buffer->address;
+ yuv_size = yuv_buffer->length;
+ }
+
+ if (yuv_thumbnail_buffer->pointer != NULL && yuv_thumbnail_buffer->length > 0) {
+ yuv_thumbnail_data = yuv_thumbnail_buffer->pointer;
+ yuv_thumbnail_address = yuv_thumbnail_buffer->address;
+ yuv_thumbnail_size = yuv_thumbnail_buffer->length;
+ }
+
+ // JPEG
+
+ if (jpeg_data == NULL) {
+ if (yuv_data == NULL || yuv_size <= 0) {
+ ALOGE("%s: Unable to create jpeg without an YUV buffer", __func__);
+ goto error;
+ }
+
+ width = smdk4x12_camera->picture_width;
+ height = smdk4x12_camera->picture_height;
+ format = yuv_buffer->format;
+
+ buffer_width = yuv_buffer->width;
+ buffer_height = yuv_buffer->height;
+ buffer_format = yuv_buffer->format;
+ buffer_address = yuv_buffer->address;
+
+ if (width != buffer_width && height != buffer_height) {
+ format = SMDK4x12_CAMERA_PICTURE_OUTPUT_FORMAT;
+
+ memset(&output, 0, sizeof(output));
+ output.v4l2_id = 2;
+ output.width = width;
+ output.height = height;
+ output.format = format;
+ output.buffer_width = buffer_width;
+ output.buffer_height = buffer_height;
+ output.buffer_format = buffer_format;
+ output.buffers_count = 1;
+
+ rc = smdk4x12_v4l2_output_start(smdk4x12_camera, &output);
+ if (rc < 0) {
+ ALOGE("%s: Unable to start picture output", __func__);
+ goto error;
+ }
+
+ rc = smdk4x12_v4l2_output(smdk4x12_camera, &output, buffer_address);
+ if (rc < 0) {
+ ALOGE("%s: Unable to output picture", __func__);
+ goto error;
+ }
+
+ output_enabled = 1;
+
+ yuv_data = output.memory->data;
+ yuv_address = output.memory_address;
+ yuv_size = output.buffer_length;
+ }
+
+ memset(&jpeg, 0, sizeof(jpeg));
+ jpeg.width = width;
+ jpeg.height = height;
+ jpeg.format = format;
+ jpeg.quality = smdk4x12_camera->jpeg_quality;
+
+ rc = smdk4x12_jpeg_start(smdk4x12_camera, &jpeg);
+ if (rc < 0) {
+ ALOGE("%s: Unable to start jpeg", __func__);
+ goto error;
+ }
+
+ if (jpeg.memory_in_pointer == NULL) {
+ ALOGE("%s: Invalid memory input pointer", __func__);
+ goto error;
+ }
+
+ memcpy(jpeg.memory_in_pointer, yuv_data, yuv_size);
+
+ rc = smdk4x12_jpeg(smdk4x12_camera, &jpeg);
+ if (rc < 0) {
+ ALOGE("%s: Unable to jpeg", __func__);
+ goto error;
+ }
+
+ jpeg_size = jpeg.memory_out_size;
+ if (jpeg_size <= 0) {
+ ALOGE("%s: Invalid jpeg size", __func__);
+ goto error;
+ }
+
+ if (SMDK4x12_CAMERA_CALLBACK_DEFINED(request_memory)) {
+ jpeg_memory = smdk4x12_camera->callbacks.request_memory(-1, jpeg_size, 1, smdk4x12_camera->callbacks.user);
+ if (jpeg_memory == NULL || jpeg_memory->data == NULL || jpeg_memory->data == MAP_FAILED) {
+ ALOGE("%s: Unable to request memory", __func__);
+ goto error;
+ }
+ } else {
+ ALOGE("%s: No memory request function!", __func__);
+ goto error;
+ }
+
+ jpeg_data = jpeg_memory->data;
+
+ memcpy(jpeg_data, jpeg.memory_out_pointer, jpeg_size);
+
+ smdk4x12_jpeg_stop(smdk4x12_camera, &jpeg);
+
+ if (output_enabled) {
+ smdk4x12_v4l2_output_stop(smdk4x12_camera, &output);
+ output_enabled = 0;
+ }
+ }
+
+ // Thumbnail
+
+ if (jpeg_thumbnail_data == NULL) {
+ if (yuv_thumbnail_data == NULL || yuv_thumbnail_size <= 0) {
+ ALOGE("%s: Unable to create jpeg thumbnail without an YUV buffer", __func__);
+ goto error;
+ }
+
+ width = smdk4x12_camera->jpeg_thumbnail_width;
+ height = smdk4x12_camera->jpeg_thumbnail_height;
+ format = yuv_thumbnail_buffer->format;
+
+ buffer_width = yuv_thumbnail_buffer->width;
+ buffer_height = yuv_thumbnail_buffer->height;
+ buffer_format = yuv_thumbnail_buffer->format;
+ buffer_address = yuv_thumbnail_buffer->address;
+
+ if (width != buffer_width && height != buffer_height) {
+ format = SMDK4x12_CAMERA_PICTURE_OUTPUT_FORMAT;
+
+ memset(&output, 0, sizeof(output));
+ output.v4l2_id = 2;
+ output.width = width;
+ output.height = height;
+ output.format = format;
+ output.buffer_width = buffer_width;
+ output.buffer_height = buffer_height;
+ output.buffer_format = buffer_format;
+ output.buffers_count = 1;
+
+ rc = smdk4x12_v4l2_output_start(smdk4x12_camera, &output);
+ if (rc < 0) {
+ ALOGE("%s: Unable to start thumbnail picture output", __func__);
+ goto error;
+ }
+
+ output_enabled = 1;
+
+ rc = smdk4x12_v4l2_output(smdk4x12_camera, &output, buffer_address);
+ if (rc < 0) {
+ ALOGE("%s: Unable to output thumbnail picture", __func__);
+ goto error;
+ }
+
+ yuv_thumbnail_data = output.memory->data;
+ yuv_thumbnail_address = output.memory_address;
+ yuv_thumbnail_size = output.buffer_length;
+ }
+
+ memset(&jpeg, 0, sizeof(jpeg));
+ jpeg.width = width;
+ jpeg.height = height;
+ jpeg.format = format;
+ jpeg.quality = smdk4x12_camera->jpeg_thumbnail_quality;
+
+ rc = smdk4x12_jpeg_start(smdk4x12_camera, &jpeg);
+ if (rc < 0) {
+ ALOGE("%s: Unable to start jpeg", __func__);
+ goto error;
+ }
+
+ if (jpeg.memory_in_pointer == NULL) {
+ ALOGE("%s: Invalid memory input pointer", __func__);
+ goto error;
+ }
+
+ memcpy(jpeg.memory_in_pointer, yuv_thumbnail_data, yuv_thumbnail_size);
+
+ rc = smdk4x12_jpeg(smdk4x12_camera, &jpeg);
+ if (rc < 0) {
+ ALOGE("%s: Unable to jpeg", __func__);
+ goto error;
+ }
+
+ jpeg_thumbnail_size = jpeg.memory_out_size;
+ if (jpeg_thumbnail_size <= 0) {
+ ALOGE("%s: Invalid jpeg size", __func__);
+ goto error;
+ }
+
+ if (SMDK4x12_CAMERA_CALLBACK_DEFINED(request_memory)) {
+ jpeg_thumbnail_memory = smdk4x12_camera->callbacks.request_memory(-1, jpeg_thumbnail_size, 1, smdk4x12_camera->callbacks.user);
+ if (jpeg_thumbnail_memory == NULL || jpeg_thumbnail_memory->data == NULL || jpeg_thumbnail_memory->data == MAP_FAILED) {
+ ALOGE("%s: Unable to request memory", __func__);
+ goto error;
+ }
+ } else {
+ ALOGE("%s: No memory request function!", __func__);
+ goto error;
+ }
+
+ jpeg_thumbnail_data = jpeg_thumbnail_memory->data;
+
+ memcpy(jpeg_thumbnail_data, jpeg.memory_out_pointer, jpeg_thumbnail_size);
+
+ smdk4x12_jpeg_stop(smdk4x12_camera, &jpeg);
+
+ if (output_enabled) {
+ smdk4x12_v4l2_output_stop(smdk4x12_camera, &output);
+ output_enabled = 0;
+ }
+ }
+
+ // EXIF
+
+ memset(&exif, 0, sizeof(exif));
+ exif.jpeg_thumbnail_data = jpeg_thumbnail_data;
+ exif.jpeg_thumbnail_size = jpeg_thumbnail_size;
+
+ rc = smdk4x12_exif_start(smdk4x12_camera, &exif);
+ if (rc < 0) {
+ ALOGE("%s: Unable to start exif", __func__);
+ goto error;
+ }
+
+ rc = smdk4x12_exif(smdk4x12_camera, &exif);
+ if (rc < 0) {
+ ALOGE("%s: Unable to exif", __func__);
+ goto error;
+ }
+
+ memory_size = exif.memory_size + jpeg_size;
+
+ if (SMDK4x12_CAMERA_CALLBACK_DEFINED(request_memory)) {
+ memory = smdk4x12_camera->callbacks.request_memory(-1, memory_size, 1, smdk4x12_camera->callbacks.user);
+ if (memory == NULL || memory->data == NULL || memory->data == MAP_FAILED) {
+ ALOGE("%s: Unable to request memory", __func__);
+ goto error;
+ }
+ } else {
+ ALOGE("%s: No memory request function!", __func__);
+ goto error;
+ }
+
+ p = (unsigned char *) memory->data;
+
+ // Copy the first two bytes of the JPEG picture
+ memcpy(p, jpeg_data, 2);
+ p += 2;
+
+ // Copy the EXIF data
+ memcpy(p, exif.memory->data, exif.memory_size);
+ p += exif.memory_size;
+
+ // Copy the JPEG picture
+ memcpy(p, (void *) ((unsigned char *) jpeg_data + 2), jpeg_size - 2);
+
+ smdk4x12_exif_stop(smdk4x12_camera, &exif);
+
+ smdk4x12_camera->picture_memory = memory;
+
+ rc = 0;
+ goto complete;
+
+error:
+ if (output_enabled)
+ smdk4x12_v4l2_output_stop(smdk4x12_camera, &output);
+
+ if (memory != NULL && memory->release != NULL) {
+ memory->release(memory);
+ smdk4x12_camera->picture_memory = NULL;
+ }
+
+ if (SMDK4x12_CAMERA_MSG_ENABLED(CAMERA_MSG_ERROR) && SMDK4x12_CAMERA_CALLBACK_DEFINED(notify) && !smdk4x12_camera->callback_lock)
+ smdk4x12_camera->callbacks.notify(CAMERA_MSG_ERROR, -1, 0, smdk4x12_camera->callbacks.user);
+
+ rc = -1;
+
+complete:
+ if (jpeg_memory != NULL && jpeg_memory->release != NULL)
+ jpeg_memory->release(jpeg_memory);
+
+ if (jpeg_thumbnail_memory != NULL && jpeg_thumbnail_memory->release != NULL)
+ jpeg_thumbnail_memory->release(jpeg_thumbnail_memory);
+
+ smdk4x12_camera->picture_completed = 1;
+ smdk4x12_camera->picture_listener->busy = 0;
+
+ return rc;
+}
+
+void *smdk4x12_camera_picture_thread(void *data)
+{
+ struct smdk4x12_camera *smdk4x12_camera;
+ int rc;
+
+ if (data == NULL)
+ return NULL;
+
+ smdk4x12_camera = (struct smdk4x12_camera *) data;
+
+ ALOGE("%s: Starting thread", __func__);
+ smdk4x12_camera->picture_thread_running = 1;
+
+ while (smdk4x12_camera->picture_thread_enabled) {
+ pthread_mutex_lock(&smdk4x12_camera->picture_lock_mutex);
+
+ pthread_mutex_lock(&smdk4x12_camera->picture_mutex);
+
+ if (smdk4x12_camera->picture_listener == NULL) {
+ pthread_mutex_unlock(&smdk4x12_camera->picture_mutex);
+ break;
+ }
+
+ if (smdk4x12_camera->picture_listener->busy) {
+ rc = smdk4x12_camera_picture(smdk4x12_camera);
+ if (rc < 0) {
+ ALOGE("%s: Unable to take picture", __func__);
+ pthread_mutex_unlock(&smdk4x12_camera->picture_mutex);
+ break;
+ }
+ }
+
+ pthread_mutex_unlock(&smdk4x12_camera->picture_mutex);
+
+ if (smdk4x12_camera->picture_completed) {
+ smdk4x12_camera->picture_thread_running = 0;
+ smdk4x12_camera_picture_thread_stop(smdk4x12_camera);
+ break;
+ }
+ }
+
+ smdk4x12_camera->picture_thread_running = 0;
+ ALOGE("%s: Exiting thread", __func__);
+
+ return NULL;
+}
+
+int smdk4x12_camera_picture_thread_start(struct smdk4x12_camera *smdk4x12_camera)
+{
+ struct smdk4x12_camera_capture_listener *listener;
+ pthread_attr_t thread_attr;
+ int format;
+ int rc;
+
+ if (smdk4x12_camera == NULL)
+ return -EINVAL;
+
+ ALOGD("%s()", __func__);
+
+ if (smdk4x12_camera->picture_thread_enabled) {
+ ALOGE("Picture thread was already started!");
+ return -1;
+ }
+
+ if (smdk4x12_camera->camera_picture_format)
+ format = smdk4x12_camera->camera_picture_format;
+ else
+ format = smdk4x12_camera->picture_format;
+
+ pthread_mutex_init(&smdk4x12_camera->picture_mutex, NULL);
+ pthread_mutex_init(&smdk4x12_camera->picture_lock_mutex, NULL);
+
+ // Initial lock
+ pthread_mutex_lock(&smdk4x12_camera->picture_lock_mutex);
+
+ pthread_attr_init(&thread_attr);
+ pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
+
+ smdk4x12_camera->picture_thread_enabled = 1;
+
+ rc = pthread_create(&smdk4x12_camera->picture_thread, &thread_attr, smdk4x12_camera_picture_thread, (void *) smdk4x12_camera);
+ if (rc < 0) {
+ ALOGE("%s: Unable to create thread", __func__);
+ goto error;
+ }
+
+ smdk4x12_camera->picture_completed = 0;
+
+ listener = smdk4x12_camera_capture_listener_register(smdk4x12_camera, smdk4x12_camera->picture_width, smdk4x12_camera->picture_height, format, smdk4x12_camera_picture_callback);
+ if (listener == NULL) {
+ ALOGE("%s: Unable to register picture capture listener", __func__);
+ goto error;
+ }
+
+ smdk4x12_camera->picture_listener = listener;
+
+ rc = 0;
+ goto complete;
+
+error:
+ pthread_mutex_destroy(&smdk4x12_camera->picture_mutex);
+ pthread_mutex_destroy(&smdk4x12_camera->picture_lock_mutex);
+
+ rc = -1;
+
+complete:
+ return rc;
+}
+
+void smdk4x12_camera_picture_thread_stop(struct smdk4x12_camera *smdk4x12_camera)
+{
+ camera_memory_t *memory;
+ int i;
+
+ if (smdk4x12_camera == NULL)
+ return;
+
+ ALOGD("%s()", __func__);
+
+ if (!smdk4x12_camera->picture_thread_enabled) {
+ ALOGE("Picture thread was already stopped!");
+ return;
+ }
+
+ memory = smdk4x12_camera->picture_memory;
+
+ if (smdk4x12_camera->picture_listener != NULL) {
+ smdk4x12_camera_capture_listener_unregister(smdk4x12_camera, smdk4x12_camera->picture_listener);
+ smdk4x12_camera->picture_listener = NULL;
+ }
+
+ smdk4x12_camera->picture_thread_enabled = 0;
+
+ pthread_mutex_unlock(&smdk4x12_camera->picture_lock_mutex);
+
+ // Wait for the thread to end
+ i = 0;
+ while (smdk4x12_camera->picture_thread_running) {
+ if (i++ > 10000) {
+ ALOGE("Picture thread is taking too long to end, something is going wrong");
+ break;
+ }
+ usleep(100);
+ }
+
+ if (smdk4x12_camera->picture_enabled) {
+ pthread_mutex_lock(&smdk4x12_camera->picture_mutex);
+ smdk4x12_camera->picture_enabled = 0;
+ pthread_mutex_unlock(&smdk4x12_camera->picture_mutex);
+ }
+
+ pthread_mutex_destroy(&smdk4x12_camera->picture_mutex);
+ pthread_mutex_destroy(&smdk4x12_camera->picture_lock_mutex);
+
+ if (smdk4x12_camera->picture_completed && memory != NULL) {
+ // It is important to return at this point (and not before) for burst
+
+ if (SMDK4x12_CAMERA_MSG_ENABLED(CAMERA_MSG_SHUTTER) && SMDK4x12_CAMERA_CALLBACK_DEFINED(notify) && !smdk4x12_camera->callback_lock)
+ smdk4x12_camera->callbacks.notify(CAMERA_MSG_SHUTTER, 0, 0, smdk4x12_camera->callbacks.user);
+
+ if (SMDK4x12_CAMERA_MSG_ENABLED(CAMERA_MSG_COMPRESSED_IMAGE) && SMDK4x12_CAMERA_CALLBACK_DEFINED(data) && !smdk4x12_camera->callback_lock)
+ smdk4x12_camera->callbacks.data(CAMERA_MSG_COMPRESSED_IMAGE, memory, 0, NULL, smdk4x12_camera->callbacks.user);
+
+ if (memory->release != NULL) {
+ memory->release(memory);
+ smdk4x12_camera->picture_memory = NULL;
+ }
+ }
+}
+
+// Recording
+
+int smdk4x12_camera_recording_output_start(struct smdk4x12_camera *smdk4x12_camera)
+{
+ struct smdk4x12_v4l2_output *output;
+ int rc;
+
+ if (smdk4x12_camera == NULL)
+ return -EINVAL;
+
+ ALOGD("%s()", __func__);
+
+ if (smdk4x12_camera->recording_output_enabled) {
+ ALOGE("Recording was already started!");
+ return -1;
+ }
+
+ output = &smdk4x12_camera->recording_output;
+
+ memset(output, 0, sizeof(struct smdk4x12_v4l2_output));
+ output->v4l2_id = 3;
+ output->width = smdk4x12_camera->recording_width;
+ output->height = smdk4x12_camera->recording_height;
+ output->format = smdk4x12_camera->recording_format;
+ output->buffer_width = smdk4x12_camera->recording_buffer.width;
+ output->buffer_height = smdk4x12_camera->recording_buffer.height;
+ output->buffer_format = smdk4x12_camera->recording_buffer.format;
+ output->buffers_count = SMDK4x12_CAMERA_RECORDING_BUFFERS_COUNT;
+
+ rc = smdk4x12_v4l2_output_start(smdk4x12_camera, output);
+ if (rc < 0) {
+ ALOGE("%s: Unable to start recording output", __func__);
+ goto error;
+ }
+
+ smdk4x12_camera->recording_output_enabled = 1;
+
+ rc = 0;
+ goto complete;
+
+error:
+ rc = -1;
+
+complete:
+ return rc;
+}
+
+void smdk4x12_camera_recording_output_stop(struct smdk4x12_camera *smdk4x12_camera)
+{
+ struct smdk4x12_v4l2_output *output;
+
+ if (smdk4x12_camera == NULL)
+ return;
+
+ ALOGD("%s()", __func__);
+
+ if (!smdk4x12_camera->recording_output_enabled) {
+ ALOGE("Recording was already stopped!");
+ return;
+ }
+
+ output = &smdk4x12_camera->recording_output;
+
+ smdk4x12_v4l2_output_stop(smdk4x12_camera, output);
+
+ smdk4x12_camera->recording_output_enabled = 0;
+}
+
+int smdk4x12_camera_recording_callback(struct smdk4x12_camera *smdk4x12_camera,
+ struct smdk4x12_camera_buffer *buffers, int buffers_count)
+{
+ struct smdk4x12_camera_buffer *buffer = NULL;
+ int width, height, format;
+ int buffer_width, buffer_height, buffer_format;
+ int rc;
+ int i;
+
+ if (smdk4x12_camera == NULL || buffers == NULL || buffers_count <= 0)
+ return -EINVAL;
+
+// ALOGD("%s()", __func__);
+
+ if (smdk4x12_camera->recording_listener == NULL)
+ return -1;
+
+ if (smdk4x12_camera->recording_listener->busy) {
+ ALOGE("%s: Dropping buffer", __func__);
+ return 0;
+ }
+
+ smdk4x12_camera->recording_listener->busy = 1;
+
+ width = smdk4x12_camera->recording_width;
+ height = smdk4x12_camera->recording_height;
+ format = smdk4x12_camera->recording_format;
+
+ for (i = 0; i < buffers_count; i++) {
+ if (buffers->format == V4L2_PIX_FMT_JPEG)
+ goto buffers_continue;
+
+ if (buffers->format == V4L2_PIX_FMT_INTERLEAVED)
+ goto buffers_continue;
+
+ // Optimal buffer
+ if (buffers->width == width && buffers->height == height) {
+ buffer = buffers;
+ break;
+ }
+
+ // Might-work buffer, but not optimal
+ buffer = buffers;
+
+buffers_continue:
+ buffers = (struct smdk4x12_camera_buffer *) ((unsigned char *) buffers + sizeof(struct smdk4x12_camera_buffer));
+ }
+
+ if (buffer == NULL) {
+ ALOGE("%s: Unable to find an appropriate buffer for recording", __func__);
+ smdk4x12_camera->recording_listener->busy = 0;
+ return 0;
+ }
+
+ buffer_width = buffer->width;
+ buffer_height = buffer->height;
+ buffer_format = buffer->format;
+
+ pthread_mutex_lock(&smdk4x12_camera->recording_mutex);
+
+ if (!smdk4x12_camera->recording_output_enabled) {
+ memcpy(&smdk4x12_camera->recording_buffer, buffer, sizeof(struct smdk4x12_camera_buffer));
+
+ rc = smdk4x12_camera_recording_output_start(smdk4x12_camera);
+ if (rc < 0) {
+ ALOGE("%s: Unable to start recording", __func__);
+ goto error;
+ }
+ } else if (smdk4x12_camera->recording_buffer.width != buffer_width || smdk4x12_camera->recording_buffer.height != buffer_height || smdk4x12_camera->recording_buffer.format != buffer_format) {
+ smdk4x12_camera_recording_output_stop(smdk4x12_camera);
+
+ memcpy(&smdk4x12_camera->recording_buffer, buffer, sizeof(struct smdk4x12_camera_buffer));
+
+ rc = smdk4x12_camera_recording_output_start(smdk4x12_camera);
+ if (rc < 0) {
+ ALOGE("%s: Unable to start recording", __func__);
+ goto error;
+ }
+ } else {
+ memcpy(&smdk4x12_camera->recording_buffer, buffer, sizeof(struct smdk4x12_camera_buffer));
+ }
+
+ pthread_mutex_unlock(&smdk4x12_camera->recording_lock_mutex);
+
+ pthread_mutex_unlock(&smdk4x12_camera->recording_mutex);
+
+ rc = 0;
+ goto complete;
+
+error:
+ pthread_mutex_unlock(&smdk4x12_camera->recording_mutex);
+
+ smdk4x12_camera->recording_listener->busy = 0;
+
+ rc = -1;
+
+complete:
+ return rc;
+}
+
+void smdk4x12_camera_recording_frame_release(struct smdk4x12_camera *smdk4x12_camera)
+{
+ struct smdk4x12_v4l2_output *output;
+ int rc;
+
+ if (smdk4x12_camera == NULL)
+ return;
+
+// ALOGD("%s()", __func__);
+
+ output = &smdk4x12_camera->recording_output;
+
+ if (!smdk4x12_camera->recording_output_enabled) {
+ ALOGE("%s: Recording output should always be enabled", __func__);
+ return;
+ }
+
+ rc = smdk4x12_v4l2_output_release(smdk4x12_camera, output);
+ if (rc < 0) {
+ ALOGE("%s: Unable to release recording output", __func__);
+ return;
+ }
+}
+
+int smdk4x12_camera_recording(struct smdk4x12_camera *smdk4x12_camera)
+{
+ struct smdk4x12_v4l2_output *output;
+ struct smdk4x12_camera_addrs *addrs;
+ int width, height, format;
+ camera_memory_t *memory;
+ int memory_address;
+ int memory_index;
+ int buffer_length;
+ int buffers_count;
+ nsecs_t timestamp;
+ int rc;
+
+ if (smdk4x12_camera == NULL)
+ goto error;
+
+// ALOGD("%s()", __func__);
+
+ width = smdk4x12_camera->recording_width;
+ height = smdk4x12_camera->recording_height;
+ format = smdk4x12_camera->recording_format;
+
+ output = &smdk4x12_camera->recording_output;
+
+ buffer_length = smdk4x12_camera->recording_buffer_length;
+ buffers_count = smdk4x12_camera->recording_buffers_count;
+
+ timestamp = systemTime(1);
+
+ if (!smdk4x12_camera->recording_output_enabled) {
+ ALOGE("%s: Recording output should always be enabled", __func__);
+ goto error;
+ }
+
+ rc = smdk4x12_v4l2_output(smdk4x12_camera, output, smdk4x12_camera->recording_buffer.address);
+ if (rc < 0) {
+ ALOGE("%s: Unable to output recording", __func__);
+ goto error;
+ }
+
+ if (smdk4x12_camera->recording_metadata) {
+ memory = smdk4x12_camera->recording_memory;
+ memory_index = smdk4x12_camera->recording_memory_index;
+ memory_address = output->memory_address + output->buffer_length * output->memory_index;
+
+ addrs = (struct smdk4x12_camera_addrs *) ((unsigned char *) memory->data + buffer_length * memory_index);
+ memset(addrs, 0, sizeof(struct smdk4x12_camera_addrs));
+ addrs->type = 0; // kMetadataBufferTypeCameraSource
+ addrs->index = memory_index;
+
+ smdk4x12_camera_yuv_planes(width, height, format, memory_address, (int *) &addrs->y, (int *) &addrs->cbcr, NULL);
+ } else {
+ memory = output->memory;
+ memory_index = output->memory_index;
+ }
+
+ if (SMDK4x12_CAMERA_MSG_ENABLED(CAMERA_MSG_VIDEO_FRAME) && SMDK4x12_CAMERA_CALLBACK_DEFINED(data_timestamp) && !smdk4x12_camera->callback_lock)
+ smdk4x12_camera->callbacks.data_timestamp(timestamp, CAMERA_MSG_VIDEO_FRAME, memory, memory_index, smdk4x12_camera->callbacks.user);
+ else
+ smdk4x12_camera_recording_frame_release(smdk4x12_camera);
+
+ if (smdk4x12_camera->recording_metadata) {
+ memory_index++;
+ smdk4x12_camera->recording_memory_index = memory_index % buffers_count;
+ }
+
+ rc = 0;
+ goto complete;
+
+error:
+ rc = -1;
+
+complete:
+ smdk4x12_camera->recording_listener->busy = 0;
+
+ return rc;
+}
+
+void *smdk4x12_camera_recording_thread(void *data)
+{
+ struct smdk4x12_camera *smdk4x12_camera;
+ int rc;
+
+ if (data == NULL)
+ return NULL;
+
+ smdk4x12_camera = (struct smdk4x12_camera *) data;
+
+ ALOGE("%s: Starting thread", __func__);
+ smdk4x12_camera->recording_thread_running = 1;
+
+ while (smdk4x12_camera->recording_thread_enabled) {
+ pthread_mutex_lock(&smdk4x12_camera->recording_lock_mutex);
+
+ pthread_mutex_lock(&smdk4x12_camera->recording_mutex);
+
+ if (smdk4x12_camera->recording_listener == NULL) {
+ pthread_mutex_unlock(&smdk4x12_camera->recording_mutex);
+ break;
+ }
+
+ if (smdk4x12_camera->recording_listener->busy) {
+ rc = smdk4x12_camera_recording(smdk4x12_camera);
+ if (rc < 0) {
+ ALOGE("%s: Unable to record", __func__);
+ pthread_mutex_unlock(&smdk4x12_camera->recording_mutex);
+ break;
+ }
+ }
+
+ pthread_mutex_unlock(&smdk4x12_camera->recording_mutex);
+ }
+
+ smdk4x12_camera->recording_thread_running = 0;
+ ALOGE("%s: Exiting thread", __func__);
+
+ return NULL;
+}
+
+int smdk4x12_camera_recording_thread_start(struct smdk4x12_camera *smdk4x12_camera)
+{
+ struct smdk4x12_camera_capture_listener *listener;
+ pthread_attr_t thread_attr;
+ camera_memory_t *memory = NULL;
+ int buffer_length;
+ int buffers_count;
+ int rc;
+
+ if (smdk4x12_camera == NULL)
+ return -EINVAL;
+
+ ALOGD("%s()", __func__);
+
+ if (smdk4x12_camera->recording_thread_enabled) {
+ ALOGE("Recording thread was already started!");
+ return -1;
+ }
+
+ pthread_mutex_init(&smdk4x12_camera->recording_mutex, NULL);
+ pthread_mutex_init(&smdk4x12_camera->recording_lock_mutex, NULL);
+
+ // Initial lock
+ pthread_mutex_lock(&smdk4x12_camera->recording_lock_mutex);
+
+ pthread_attr_init(&thread_attr);
+ pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
+
+ smdk4x12_camera->recording_thread_enabled = 1;
+
+ rc = pthread_create(&smdk4x12_camera->recording_thread, &thread_attr, smdk4x12_camera_recording_thread, (void *) smdk4x12_camera);
+ if (rc < 0) {
+ ALOGE("%s: Unable to create thread", __func__);
+ goto error;
+ }
+
+ if (smdk4x12_camera->recording_metadata) {
+ buffer_length = sizeof(struct smdk4x12_camera_addrs);
+ buffers_count = SMDK4x12_CAMERA_RECORDING_BUFFERS_COUNT;
+
+ if (SMDK4x12_CAMERA_CALLBACK_DEFINED(request_memory)) {
+ memory = smdk4x12_camera->callbacks.request_memory(-1, buffer_length, buffers_count, smdk4x12_camera->callbacks.user);
+ if (memory == NULL || memory->data == NULL || memory->data == MAP_FAILED) {
+ ALOGE("%s: Unable to request memory", __func__);
+ goto error;
+ }
+ } else {
+ ALOGE("%s: No memory request function!", __func__);
+ goto error;
+ }
+
+ smdk4x12_camera->recording_memory = memory;
+ smdk4x12_camera->recording_buffer_length = buffer_length;
+ smdk4x12_camera->recording_buffers_count = buffers_count;
+ }
+
+ listener = smdk4x12_camera_capture_listener_register(smdk4x12_camera, smdk4x12_camera->recording_width, smdk4x12_camera->recording_height, smdk4x12_camera->recording_format, smdk4x12_camera_recording_callback);
+ if (listener == NULL) {
+ ALOGE("%s: Unable to register recording capture listener", __func__);
+ goto error;
+ }
+
+ smdk4x12_camera->recording_listener = listener;
+
+ rc = 0;
+ goto complete;
+
+error:
+ if (memory != NULL && memory->release != NULL) {
+ memory->release(memory);
+ smdk4x12_camera->recording_memory = NULL;
+ }
+
+ pthread_mutex_destroy(&smdk4x12_camera->recording_mutex);
+ pthread_mutex_destroy(&smdk4x12_camera->recording_lock_mutex);
+
+ rc = -1;
+
+complete:
+ return rc;
+}
+
+void smdk4x12_camera_recording_thread_stop(struct smdk4x12_camera *smdk4x12_camera)
+{
+ camera_memory_t *memory;
+ int i;
+
+ if (smdk4x12_camera == NULL)
+ return;
+
+ ALOGD("%s()", __func__);
+
+ if (!smdk4x12_camera->recording_thread_enabled) {
+ ALOGE("Recording thread was already stopped!");
+ return;
+ }
+
+ memory = smdk4x12_camera->recording_memory;
+
+ if (smdk4x12_camera->recording_listener != NULL) {
+ smdk4x12_camera_capture_listener_unregister(smdk4x12_camera, smdk4x12_camera->recording_listener);
+ smdk4x12_camera->recording_listener = NULL;
+ }
+
+ smdk4x12_camera->recording_thread_enabled = 0;
+
+ pthread_mutex_unlock(&smdk4x12_camera->recording_lock_mutex);
+
+ // Wait for the thread to end
+ i = 0;
+ while (smdk4x12_camera->recording_thread_running) {
+ if (i++ > 10000) {
+ ALOGE("Recording thread is taking too long to end, something is going wrong");
+ break;
+ }
+ usleep(100);
+ }
+
+ if (smdk4x12_camera->recording_output_enabled) {
+ pthread_mutex_lock(&smdk4x12_camera->recording_mutex);
+ smdk4x12_camera_recording_output_stop(smdk4x12_camera);
+ pthread_mutex_unlock(&smdk4x12_camera->recording_mutex);
+ }
+
+ pthread_mutex_destroy(&smdk4x12_camera->recording_mutex);
+ pthread_mutex_destroy(&smdk4x12_camera->recording_lock_mutex);
+
+ if (memory != NULL && memory->release != NULL) {
+ memory->release(memory);
+ smdk4x12_camera->recording_memory = NULL;
+ }
+}
+
+// Auto-focus
+
+int smdk4x12_camera_auto_focus(struct smdk4x12_camera *smdk4x12_camera, int auto_focus_status)
+{
+ if (smdk4x12_camera == NULL)
+ return -EINVAL;
+
+// ALOGD("%s()", __func__);
+
+ switch (auto_focus_status) {
+ case CAMERA_AF_STATUS_IN_PROGRESS:
+ if (SMDK4x12_CAMERA_MSG_ENABLED(CAMERA_MSG_FOCUS_MOVE) && SMDK4x12_CAMERA_CALLBACK_DEFINED(notify) && !smdk4x12_camera->callback_lock)
+ smdk4x12_camera->callbacks.notify(CAMERA_MSG_FOCUS_MOVE, 1, 0, smdk4x12_camera->callbacks.user);
+ break;
+ case CAMERA_AF_STATUS_SUCCESS:
+ if (SMDK4x12_CAMERA_MSG_ENABLED(CAMERA_MSG_FOCUS) && SMDK4x12_CAMERA_CALLBACK_DEFINED(notify) && !smdk4x12_camera->callback_lock)
+ smdk4x12_camera->callbacks.notify(CAMERA_MSG_FOCUS, 1, 0, smdk4x12_camera->callbacks.user);
+ break;
+ case CAMERA_AF_STATUS_FAIL:
+ default:
+ if (SMDK4x12_CAMERA_MSG_ENABLED(CAMERA_MSG_FOCUS) && SMDK4x12_CAMERA_CALLBACK_DEFINED(notify) && !smdk4x12_camera->callback_lock)
+ smdk4x12_camera->callbacks.notify(CAMERA_MSG_FOCUS, 0, 0, smdk4x12_camera->callbacks.user);
+ break;
+ }
+
+ return 0;
+}
+
+void *smdk4x12_camera_auto_focus_thread(void *data)
+{
+ struct smdk4x12_camera *smdk4x12_camera;
+ int auto_focus_result = CAMERA_AF_STATUS_FAIL;
+ int auto_focus_completed = 0;
+ int rc;
+
+ if (data == NULL)
+ return NULL;
+
+ smdk4x12_camera = (struct smdk4x12_camera *) data;
+
+ ALOGE("%s: Starting thread", __func__);
+ smdk4x12_camera->auto_focus_thread_running = 1;
+
+ if (smdk4x12_camera->focus_mode == FOCUS_MODE_CONTINOUS_PICTURE) {
+ rc = smdk4x12_camera_auto_focus(smdk4x12_camera, smdk4x12_camera->auto_focus_result);
+ if (rc < 0)
+ ALOGE("%s: Unable to auto-focus", __func__);
+
+ smdk4x12_camera->auto_focus_thread_enabled = 0;
+ }
+
+ while (smdk4x12_camera->auto_focus_thread_enabled) {
+ pthread_mutex_lock(&smdk4x12_camera->auto_focus_mutex);
+
+ rc = smdk4x12_v4l2_g_ctrl(smdk4x12_camera, 0, V4L2_CID_CAMERA_AUTO_FOCUS_RESULT, &auto_focus_result);
+ if (rc < 0) {
+ ALOGE("%s: Unable to get auto-focus result", __func__);
+ auto_focus_result = CAMERA_AF_STATUS_FAIL;
+ }
+
+ smdk4x12_camera->auto_focus_result = auto_focus_result;
+
+ rc = smdk4x12_camera_auto_focus(smdk4x12_camera, auto_focus_result);
+ if (rc < 0) {
+ ALOGE("%s: Unable to auto-focus", __func__);
+ auto_focus_result = CAMERA_AF_STATUS_FAIL;
+ }
+
+ if (auto_focus_result == CAMERA_AF_STATUS_IN_PROGRESS)
+ usleep(10000);
+ else
+ auto_focus_completed = 1;
+
+ pthread_mutex_unlock(&smdk4x12_camera->auto_focus_mutex);
+
+ if (auto_focus_completed) {
+ smdk4x12_camera->auto_focus_thread_running = 0;
+ smdk4x12_camera_auto_focus_thread_stop(smdk4x12_camera);
+ }
+ }
+
+ smdk4x12_camera->auto_focus_thread_running = 0;
+ ALOGE("%s: Exiting thread", __func__);
+
+ return NULL;
+}
+
+int smdk4x12_camera_auto_focus_thread_start(struct smdk4x12_camera *smdk4x12_camera)
+{
+ pthread_attr_t thread_attr;
+ int auto_focus;
+ int rc;
+
+ if (smdk4x12_camera == NULL)
+ return -EINVAL;
+
+ ALOGD("%s()", __func__);
+
+ if (smdk4x12_camera->auto_focus_thread_enabled) {
+ ALOGE("Auto-focus thread was already started!");
+ return -1;
+ }
+
+ pthread_mutex_init(&smdk4x12_camera->auto_focus_mutex, NULL);
+
+ if (smdk4x12_camera->focus_mode != FOCUS_MODE_CONTINOUS_PICTURE) {
+ auto_focus = AUTO_FOCUS_ON | (smdk4x12_camera->preview_width & 0xfff) << 20 | (smdk4x12_camera->preview_height & 0xfff) << 8;
+
+ rc = smdk4x12_v4l2_s_ctrl(smdk4x12_camera, 0, V4L2_CID_CAMERA_SET_AUTO_FOCUS, auto_focus);
+ if (rc < 0) {
+ ALOGE("%s: Unable to set auto-focus on", __func__);
+ goto error;
+ }
+ }
+
+ pthread_attr_init(&thread_attr);
+ pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
+
+ smdk4x12_camera->auto_focus_thread_enabled = 1;
+
+ rc = pthread_create(&smdk4x12_camera->auto_focus_thread, &thread_attr, smdk4x12_camera_auto_focus_thread, (void *) smdk4x12_camera);
+ if (rc < 0) {
+ ALOGE("%s: Unable to create thread", __func__);
+ goto error;
+ }
+
+ rc = 0;
+ goto complete;
+
+error:
+ pthread_mutex_destroy(&smdk4x12_camera->auto_focus_mutex);
+
+ rc = -1;
+
+complete:
+ return rc;
+}
+
+void smdk4x12_camera_auto_focus_thread_stop(struct smdk4x12_camera *smdk4x12_camera)
+{
+ int rc;
+ int i;
+
+ if (smdk4x12_camera == NULL)
+ return;
+
+ ALOGD("%s()", __func__);
+
+ if (!smdk4x12_camera->auto_focus_thread_enabled) {
+ ALOGE("Auto-focus thread was already stopped!");
+ return;
+ }
+
+ smdk4x12_camera->auto_focus_thread_enabled = 0;
+
+ // Wait for the thread to end
+ i = 0;
+ while (smdk4x12_camera->auto_focus_thread_running) {
+ if (i++ > 10000) {
+ ALOGE("Auto-focus thread is taking too long to end, something is going wrong");
+ break;
+ }
+ usleep(100);
+ }
+ if (smdk4x12_camera->focus_mode != FOCUS_MODE_CONTINOUS_PICTURE) {
+ rc = smdk4x12_v4l2_s_ctrl(smdk4x12_camera, 0, V4L2_CID_CAMERA_SET_AUTO_FOCUS, AUTO_FOCUS_OFF);
+ if (rc < 0)
+ ALOGE("%s: Unable to set auto-focus off", __func__);
+ }
+
+ pthread_mutex_destroy(&smdk4x12_camera->auto_focus_mutex);
+}
+
+/*
+ * Exynos Camera OPS
+ */
+
+int smdk4x12_camera_set_preview_window(struct camera_device *dev,
+ struct preview_stream_ops *w)
+{
+ struct smdk4x12_camera *smdk4x12_camera;
+
+ int width, height, format, gralloc_format;
+
+ buffer_handle_t *buffer;
+ int stride;
+ void *addr = NULL;
+
+ int rc;
+
+ ALOGD("%s(%p, %p)", __func__, dev, w);
+
+ if (dev == NULL || dev->priv == NULL)
+ return -EINVAL;
+
+ smdk4x12_camera = (struct smdk4x12_camera *) dev->priv;
+
+ if (smdk4x12_camera->preview_thread_enabled)
+ pthread_mutex_lock(&smdk4x12_camera->preview_mutex);
+
+ if (w == NULL) {
+ smdk4x12_camera->preview_window = NULL;
+ return 0;
+ }
+
+ if (w->set_buffer_count == NULL || w->set_usage == NULL || w->set_buffers_geometry == NULL)
+ goto error;
+
+ rc = w->set_buffer_count(w, SMDK4x12_CAMERA_GRALLOC_BUFFERS_COUNT);
+ if (rc) {
+ ALOGE("%s: Unable to set buffer count: %d", __func__, SMDK4x12_CAMERA_GRALLOC_BUFFERS_COUNT);
+ goto error;
+ }
+
+ rc = w->set_usage(w, GRALLOC_USAGE_SW_WRITE_OFTEN);
+ if (rc) {
+ ALOGE("%s: Unable to set usage", __func__);
+ goto error;
+ }
+
+ width = smdk4x12_camera->preview_width;
+ height = smdk4x12_camera->preview_height;
+ format = smdk4x12_camera->preview_format;
+
+ switch (format) {
+ case V4L2_PIX_FMT_NV21:
+ gralloc_format = HAL_PIXEL_FORMAT_YCrCb_420_SP;
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ gralloc_format = HAL_PIXEL_FORMAT_YV12;
+ break;
+ case V4L2_PIX_FMT_RGB565:
+ gralloc_format = HAL_PIXEL_FORMAT_RGB_565;
+ break;
+ case V4L2_PIX_FMT_RGB32:
+ gralloc_format = HAL_PIXEL_FORMAT_RGBX_8888;
+ break;
+ default:
+ gralloc_format = HAL_PIXEL_FORMAT_YCrCb_420_SP;
+ break;
+ }
+
+ rc = w->set_buffers_geometry(w, width, height, gralloc_format);
+ if (rc) {
+ ALOGE("%s: Unable to set buffers geometry", __func__);
+ goto error;
+ }
+
+ smdk4x12_camera->preview_window = w;
+
+ rc = 0;
+ goto complete;
+
+error:
+ smdk4x12_camera->preview_window = NULL;
+ rc = -1;
+
+complete:
+ if (smdk4x12_camera->preview_thread_enabled)
+ pthread_mutex_unlock(&smdk4x12_camera->preview_mutex);
+
+ return rc;
+}
+
+void smdk4x12_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 smdk4x12_camera *smdk4x12_camera;
+
+ ALOGD("%s(%p, %p)", __func__, dev, user);
+
+ if (dev == NULL || dev->priv == NULL)
+ return;
+
+ smdk4x12_camera = (struct smdk4x12_camera *) dev->priv;
+
+ smdk4x12_camera->callbacks.notify = notify_cb;
+ smdk4x12_camera->callbacks.data = data_cb;
+ smdk4x12_camera->callbacks.data_timestamp = data_cb_timestamp;
+ smdk4x12_camera->callbacks.request_memory = get_memory;
+ smdk4x12_camera->callbacks.user = user;
+}
+
+void smdk4x12_camera_enable_msg_type(struct camera_device *dev, int32_t msg_type)
+{
+ struct smdk4x12_camera *smdk4x12_camera;
+
+ ALOGD("%s(%p, %d)", __func__, dev, msg_type);
+
+ if (dev == NULL || dev->priv == NULL)
+ return;
+
+ smdk4x12_camera = (struct smdk4x12_camera *) dev->priv;
+
+ smdk4x12_camera->messages_enabled |= msg_type;
+}
+
+void smdk4x12_camera_disable_msg_type(struct camera_device *dev, int32_t msg_type)
+{
+ struct smdk4x12_camera *smdk4x12_camera;
+
+ ALOGD("%s(%p, %d)", __func__, dev, msg_type);
+
+ if (dev == NULL || dev->priv == NULL)
+ return;
+
+ smdk4x12_camera = (struct smdk4x12_camera *) dev->priv;
+
+ smdk4x12_camera->messages_enabled &= ~msg_type;
+}
+
+int smdk4x12_camera_msg_type_enabled(struct camera_device *dev, int32_t msg_type)
+{
+ struct smdk4x12_camera *smdk4x12_camera;
+
+ ALOGD("%s(%p, %d)", __func__, dev, msg_type);
+
+ if (dev == NULL || dev->priv == NULL)
+ return -EINVAL;
+
+ smdk4x12_camera = (struct smdk4x12_camera *) dev->priv;
+
+ return smdk4x12_camera->messages_enabled & msg_type;
+}
+
+int smdk4x12_camera_start_preview(struct camera_device *dev)
+{
+ struct smdk4x12_camera *smdk4x12_camera;
+ int rc;
+
+ ALOGD("%s(%p)", __func__, dev);
+
+ if (dev == NULL || dev->priv == NULL)
+ return -EINVAL;
+
+ smdk4x12_camera = (struct smdk4x12_camera *) dev->priv;
+
+ smdk4x12_camera->callback_lock = 1;
+ rc = smdk4x12_camera_preview_thread_start(smdk4x12_camera);
+ smdk4x12_camera->callback_lock = 0;
+
+ return rc;
+}
+
+void smdk4x12_camera_stop_preview(struct camera_device *dev)
+{
+ struct smdk4x12_camera *smdk4x12_camera;
+
+ ALOGD("%s(%p)", __func__, dev);
+
+ if (dev == NULL || dev->priv == NULL)
+ return;
+
+ smdk4x12_camera = (struct smdk4x12_camera *) dev->priv;
+
+ smdk4x12_camera->callback_lock = 1;
+ smdk4x12_camera_preview_thread_stop(smdk4x12_camera);
+ smdk4x12_camera->callback_lock = 0;
+}
+
+int smdk4x12_camera_preview_enabled(struct camera_device *dev)
+{
+ struct smdk4x12_camera *smdk4x12_camera;
+
+ ALOGD("%s(%p)", __func__, dev);
+
+ if (dev == NULL || dev->priv == NULL)
+ return -EINVAL;
+
+ smdk4x12_camera = (struct smdk4x12_camera *) dev->priv;
+
+ return smdk4x12_camera->preview_thread_enabled;
+}
+
+int smdk4x12_camera_store_meta_data_in_buffers(struct camera_device *dev,
+ int enable)
+{
+ struct smdk4x12_camera *smdk4x12_camera;
+
+ ALOGD("%s(%p, %d)", __func__, dev, enable);
+
+ if (dev == NULL || dev->priv == NULL)
+ return -EINVAL;
+
+ smdk4x12_camera = (struct smdk4x12_camera *) dev->priv;
+
+ if (!smdk4x12_camera->recording_thread_enabled)
+ smdk4x12_camera->recording_metadata = enable;
+ else
+ ALOGE("%s: Recording is running!", __func__);
+
+ return 0;
+}
+
+int smdk4x12_camera_start_recording(struct camera_device *dev)
+{
+ struct smdk4x12_camera *smdk4x12_camera;
+ int rc;
+
+ ALOGD("%s(%p)", __func__, dev);
+
+ smdk4x12_camera = (struct smdk4x12_camera *) dev->priv;
+
+ smdk4x12_camera->callback_lock = 1;
+ rc = smdk4x12_camera_recording_thread_start(smdk4x12_camera);
+ smdk4x12_camera->callback_lock = 0;
+
+ return rc;
+}
+
+void smdk4x12_camera_stop_recording(struct camera_device *dev)
+{
+ struct smdk4x12_camera *smdk4x12_camera;
+
+ ALOGD("%s(%p)", __func__, dev);
+
+ smdk4x12_camera = (struct smdk4x12_camera *) dev->priv;
+
+ smdk4x12_camera->callback_lock = 1;
+ smdk4x12_camera_recording_thread_stop(smdk4x12_camera);
+ smdk4x12_camera->callback_lock = 0;
+}
+
+int smdk4x12_camera_recording_enabled(struct camera_device *dev)
+{
+ struct smdk4x12_camera *smdk4x12_camera;
+
+ ALOGD("%s(%p)", __func__, dev);
+
+ if (dev == NULL || dev->priv == NULL)
+ return -EINVAL;
+
+ smdk4x12_camera = (struct smdk4x12_camera *) dev->priv;
+
+ return smdk4x12_camera->recording_thread_enabled;
+}
+
+void smdk4x12_camera_release_recording_frame(struct camera_device *dev,
+ const void *opaque)
+{
+ struct smdk4x12_camera *smdk4x12_camera;
+
+// ALOGD("%s(%p, %p)", __func__, dev, opaque);
+
+ if (dev == NULL || dev->priv == NULL)
+ return;
+
+ smdk4x12_camera = (struct smdk4x12_camera *) dev->priv;
+
+ smdk4x12_camera_recording_frame_release(smdk4x12_camera);
+}
+
+int smdk4x12_camera_start_auto_focus(struct camera_device *dev)
+{
+ struct smdk4x12_camera *smdk4x12_camera;
+
+ ALOGD("%s(%p)", __func__, dev);
+
+ if (dev == NULL || dev->priv == NULL)
+ return -EINVAL;
+
+ smdk4x12_camera = (struct smdk4x12_camera *) dev->priv;
+
+ return smdk4x12_camera_auto_focus_thread_start(smdk4x12_camera);
+}
+
+int smdk4x12_camera_cancel_auto_focus(struct camera_device *dev)
+{
+ struct smdk4x12_camera *smdk4x12_camera;
+
+ ALOGD("%s(%p)", __func__, dev);
+
+ if (dev == NULL || dev->priv == NULL)
+ return -EINVAL;
+
+ smdk4x12_camera = (struct smdk4x12_camera *) dev->priv;
+
+ smdk4x12_camera_auto_focus_thread_stop(smdk4x12_camera);
+
+ return 0;
+}
+
+int smdk4x12_camera_take_picture(struct camera_device *dev)
+{
+ struct smdk4x12_camera *smdk4x12_camera;
+ int rc;
+
+ ALOGD("%s(%p)", __func__, dev);
+
+ if (dev == NULL || dev->priv == NULL)
+ return -EINVAL;
+
+ smdk4x12_camera = (struct smdk4x12_camera *) dev->priv;
+
+ smdk4x12_camera->callback_lock = 1;
+ rc = smdk4x12_camera_picture_thread_start(smdk4x12_camera);
+ smdk4x12_camera->callback_lock = 0;
+
+ return rc;
+}
+
+int smdk4x12_camera_cancel_picture(struct camera_device *dev)
+{
+ struct smdk4x12_camera *smdk4x12_camera;
+ int rc;
+
+ ALOGD("%s(%p)", __func__, dev);
+
+ if (dev == NULL || dev->priv == NULL)
+ return -EINVAL;
+
+ smdk4x12_camera = (struct smdk4x12_camera *) dev->priv;
+
+ smdk4x12_camera->callback_lock = 1;
+ smdk4x12_camera_picture_thread_stop(smdk4x12_camera);
+ smdk4x12_camera->callback_lock = 0;
+
+ return 0;
+}
+
+int smdk4x12_camera_set_parameters(struct camera_device *dev,
+ const char *params)
+{
+ struct smdk4x12_camera *smdk4x12_camera;
+ int rc;
+
+ ALOGD("%s(%p, %s)", __func__, dev, params);
+
+ if (dev == NULL || dev->priv == NULL || params == NULL)
+ return -EINVAL;
+
+ smdk4x12_camera = (struct smdk4x12_camera *) dev->priv;
+
+ rc = smdk4x12_params_string_set(smdk4x12_camera, (char *) params);
+ if (rc < 0) {
+ ALOGE("%s: Unable to set params string", __func__);
+ return -1;
+ }
+
+ rc = smdk4x12_camera_params_apply(smdk4x12_camera, 0);
+ if (rc < 0) {
+ ALOGE("%s: Unable to apply params", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+char *smdk4x12_camera_get_parameters(struct camera_device *dev)
+{
+ struct smdk4x12_camera *smdk4x12_camera;
+ char *params;
+
+ ALOGD("%s(%p)", __func__, dev);
+
+ if (dev == NULL || dev->priv == NULL)
+ return NULL;
+
+ smdk4x12_camera = (struct smdk4x12_camera *) dev->priv;
+
+ params = smdk4x12_params_string_get(smdk4x12_camera);
+ if (params == NULL) {
+ ALOGE("%s: Couldn't find any param", __func__);
+ return strdup("");
+ }
+
+ return params;
+}
+
+void smdk4x12_camera_put_parameters(struct camera_device *dev, char *params)
+{
+ ALOGD("%s(%p)", __func__, dev);
+
+ if (params != NULL)
+ free(params);
+}
+
+int smdk4x12_camera_send_command(struct camera_device *dev,
+ int32_t cmd, int32_t arg1, int32_t arg2)
+{
+ ALOGD("%s(%p, %d, %d, %d)", __func__, dev, cmd, arg1, arg2);
+
+ return 0;
+}
+
+void smdk4x12_camera_release(struct camera_device *dev)
+{
+ struct smdk4x12_camera *smdk4x12_camera;
+
+ ALOGD("%s(%p)", __func__, dev);
+
+ if (dev == NULL || dev->priv == NULL)
+ return;
+
+ smdk4x12_camera = (struct smdk4x12_camera *) dev->priv;
+
+ smdk4x12_camera_capture_thread_stop(smdk4x12_camera);
+
+ smdk4x12_camera_stop(smdk4x12_camera);
+}
+
+int smdk4x12_camera_dump(struct camera_device *dev, int fd)
+{
+ ALOGD("%s(%p, %d)", __func__, dev, fd);
+
+ return 0;
+}
+
+/*
+ * Interface
+ */
+
+struct camera_device_ops smdk4x12_camera_ops = {
+ .set_preview_window = smdk4x12_camera_set_preview_window,
+ .set_callbacks = smdk4x12_camera_set_callbacks,
+ .enable_msg_type = smdk4x12_camera_enable_msg_type,
+ .disable_msg_type = smdk4x12_camera_disable_msg_type,
+ .msg_type_enabled = smdk4x12_camera_msg_type_enabled,
+ .start_preview = smdk4x12_camera_start_preview,
+ .stop_preview = smdk4x12_camera_stop_preview,
+ .preview_enabled = smdk4x12_camera_preview_enabled,
+ .store_meta_data_in_buffers = smdk4x12_camera_store_meta_data_in_buffers,
+ .start_recording = smdk4x12_camera_start_recording,
+ .stop_recording = smdk4x12_camera_stop_recording,
+ .recording_enabled = smdk4x12_camera_recording_enabled,
+ .release_recording_frame = smdk4x12_camera_release_recording_frame,
+ .auto_focus = smdk4x12_camera_start_auto_focus,
+ .cancel_auto_focus = smdk4x12_camera_cancel_auto_focus,
+ .take_picture = smdk4x12_camera_take_picture,
+ .cancel_picture = smdk4x12_camera_cancel_picture,
+ .set_parameters = smdk4x12_camera_set_parameters,
+ .get_parameters = smdk4x12_camera_get_parameters,
+ .put_parameters = smdk4x12_camera_put_parameters,
+ .send_command = smdk4x12_camera_send_command,
+ .release = smdk4x12_camera_release,
+ .dump = smdk4x12_camera_dump,
+};
+
+int smdk4x12_camera_close(hw_device_t *device)
+{
+ struct camera_device *camera_device;
+ struct smdk4x12_camera *smdk4x12_camera;
+
+ ALOGD("%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 smdk4x12_camera_open(const struct hw_module_t* module, const char *camera_id,
+ struct hw_device_t** device)
+{
+ struct camera_device *camera_device = NULL;
+ struct smdk4x12_camera *smdk4x12_camera = NULL;
+ int id;
+ int rc;
+
+ ALOGD("%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;
+
+ smdk4x12_camera = calloc(1, sizeof(struct smdk4x12_camera));
+ smdk4x12_camera->config = smdk4x12_camera_config;
+
+ if (smdk4x12_camera->config->v4l2_nodes_count > SMDK4x12_CAMERA_MAX_V4L2_NODES_COUNT)
+ goto error_preset;
+
+ if (id >= smdk4x12_camera->config->presets_count)
+ goto error_preset;
+
+ rc = smdk4x12_camera_start(smdk4x12_camera, id);
+ if (rc < 0) {
+ ALOGE("%s: Unable to start camera", __func__);
+ goto error;
+ }
+
+ rc = smdk4x12_camera_capture_thread_start(smdk4x12_camera);
+ if (rc < 0) {
+ ALOGE("%s: Unable to start capture thread", __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 = smdk4x12_camera_close;
+
+ camera_device->ops = &smdk4x12_camera_ops;
+ camera_device->priv = smdk4x12_camera;
+
+ *device = (struct hw_device_t *) &(camera_device->common);
+
+ return 0;
+
+error:
+ smdk4x12_camera_stop(smdk4x12_camera);
+
+error_device:
+ if (camera_device != NULL)
+ free(camera_device);
+
+error_preset:
+ if (smdk4x12_camera != NULL)
+ free(smdk4x12_camera);
+
+ return -1;
+}
+
+int smdk4x12_camera_get_number_of_cameras(void)
+{
+ ALOGD("%s()", __func__);
+
+ if (smdk4x12_camera_config == NULL || smdk4x12_camera_config->presets == NULL) {
+ ALOGE("%s: Unable to find proper camera config", __func__);
+ return -1;
+ }
+
+ return smdk4x12_camera_config->presets_count;
+}
+
+int smdk4x12_camera_get_camera_info(int id, struct camera_info *info)
+{
+ ALOGD("%s(%d, %p)", __func__, id, info);
+
+ if (id < 0 || info == NULL)
+ return -EINVAL;
+
+ if (smdk4x12_camera_config == NULL || smdk4x12_camera_config->presets == NULL) {
+ ALOGE("%s: Unable to find proper camera config", __func__);
+ return -1;
+ }
+
+ if (id >= smdk4x12_camera_config->presets_count)
+ return -EINVAL;
+
+ ALOGD("Selected camera: %s", smdk4x12_camera_config->presets[id].name);
+
+ info->facing = smdk4x12_camera_config->presets[id].facing;
+ info->orientation = smdk4x12_camera_config->presets[id].orientation;
+
+ return 0;
+}
+
+struct hw_module_methods_t smdk4x12_camera_module_methods = {
+ .open = smdk4x12_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 = &smdk4x12_camera_module_methods,
+ },
+ .get_number_of_cameras = smdk4x12_camera_get_number_of_cameras,
+ .get_camera_info = smdk4x12_camera_get_camera_info,
+};
diff --git a/camera/smdk4x12_camera.h b/camera/smdk4x12_camera.h
new file mode 100644
index 0000000..b264f39
--- /dev/null
+++ b/camera/smdk4x12_camera.h
@@ -0,0 +1,694 @@
+/*
+ * Copyright (C) 2013-2014 Paul Kocialkowski <contact@paulk.fr>
+ *
+ * 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 <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <pthread.h>
+
+#include <videodev2.h>
+#include <videodev2_exynos_camera.h>
+#include <videodev2_exynos_media.h>
+#include <fimc.h>
+
+#include <s5c73m3.h>
+
+#include <jpeg_hal.h>
+#include <Exif.h>
+
+#include <hardware/hardware.h>
+#include <hardware/camera.h>
+
+#ifndef _SMDK4x12_CAMERA_H_
+#define _SMDK4x12_CAMERA_H_
+
+#define SMDK4x12_CAMERA_MAX_V4L2_NODES_COUNT 4
+
+#define SMDK4x12_CAMERA_CAPTURE_BUFFERS_COUNT 6
+#define SMDK4x12_CAMERA_PREVIEW_BUFFERS_COUNT 6
+#define SMDK4x12_CAMERA_RECORDING_BUFFERS_COUNT 6
+#define SMDK4x12_CAMERA_GRALLOC_BUFFERS_COUNT 3
+
+#define SMDK4x12_CAMERA_PICTURE_OUTPUT_FORMAT V4L2_PIX_FMT_YUYV
+
+#define SMDK4x12_CAMERA_MSG_ENABLED(msg) (smdk4x12_camera->messages_enabled & msg)
+#define SMDK4x12_CAMERA_CALLBACK_DEFINED(cb) (smdk4x12_camera->callbacks.cb != NULL)
+
+#define SMDK4x12_CAMERA_ALIGN(value) ((value + (0x10000 - 1)) & ~(0x10000 - 1))
+
+/*
+ * Structures
+ */
+
+struct smdk4x12_camera;
+
+struct list_head {
+ struct list_head *next;
+ struct list_head *prev;
+};
+
+enum smdk4x12_param_type {
+ EXYNOS_PARAM_INT,
+ EXYNOS_PARAM_FLOAT,
+ EXYNOS_PARAM_STRING,
+};
+
+union smdk4x12_param_data {
+ int integer;
+ float floating;
+ char *string;
+};
+
+struct smdk4x12_param {
+ struct list_head list;
+
+ char *key;
+ union smdk4x12_param_data data;
+ enum smdk4x12_param_type type;
+};
+
+struct smdk4x12_camera_buffer {
+ void *pointer;
+ int address;
+ int length;
+
+ int width;
+ int height;
+ int format;
+};
+
+struct smdk4x12_camera_capture_listener {
+ struct list_head list;
+
+ int width;
+ int height;
+ int format;
+
+ int (*callback)(struct smdk4x12_camera *smdk4x12_camera, struct smdk4x12_camera_buffer *buffers, int buffers_count);
+ int busy;
+};
+
+struct smdk4x12_camera_mbus_resolution {
+ int width;
+ int height;
+ int mbus_width;
+ int mbus_height;
+};
+
+struct smdk4x12_camera_params {
+ char *preview_size_values;
+ char *preview_size;
+ char *preview_format_values;
+ char *preview_format;
+ char *preview_frame_rate_values;
+ int preview_frame_rate;
+ char *preview_fps_range_values;
+ char *preview_fps_range;
+
+ char *picture_size_values;
+ char *picture_size;
+ char *picture_format_values;
+ char *picture_format;
+ char *jpeg_thumbnail_size_values;
+ int jpeg_thumbnail_width;
+ int jpeg_thumbnail_height;
+ int jpeg_thumbnail_quality;
+ int jpeg_quality;
+
+ int video_snapshot_supported;
+ int full_video_snap_supported;
+
+ char *recording_size;
+ char *recording_size_values;
+ char *recording_format;
+
+ char *focus_mode;
+ char *focus_mode_values;
+ char *focus_distances;
+ char *focus_areas;
+ int max_num_focus_areas;
+
+ int zoom_supported;
+ int smooth_zoom_supported;
+ char *zoom_ratios;
+ int zoom;
+ int max_zoom;
+
+ int auto_exposure_lock_supported;
+ int auto_exposure_lock;
+
+ int auto_white_balance_lock_supported;
+ int auto_white_balance_lock;
+
+ char *flash_mode;
+ char *flash_mode_values;
+
+ int exposure_compensation;
+ float exposure_compensation_step;
+ int min_exposure_compensation;
+ int max_exposure_compensation;
+
+ char *whitebalance;
+ char *whitebalance_values;
+
+ char *antibanding;
+ char *antibanding_values;
+
+ char *scene_mode;
+ char *scene_mode_values;
+
+ char *effect;
+ char *effect_values;
+
+ char *iso;
+ char *iso_values;
+
+ char *image_stabilization;
+ char *image_stabilization_values;
+};
+
+struct smdk4x12_camera_preset {
+ char *name;
+ int facing;
+ int orientation;
+
+ int rotation;
+ int hflip;
+ int vflip;
+
+ int capture_format;
+ int picture_format;
+ int fimc_is;
+
+ float focal_length;
+ float horizontal_view_angle;
+ float vertical_view_angle;
+
+ int metering;
+
+ struct smdk4x12_camera_params params;
+ struct smdk4x12_camera_mbus_resolution *mbus_resolutions;
+ int mbus_resolutions_count;
+};
+
+struct smdk4x12_v4l2_node {
+ int id;
+ char *node;
+};
+
+struct smdk4x12_v4l2_output {
+ int enabled;
+
+ int v4l2_id;
+
+ int width;
+ int height;
+ int format;
+
+ int buffer_width;
+ int buffer_height;
+ int buffer_format;
+
+ camera_memory_t *memory;
+ int memory_address;
+#ifdef EXYNOS_ION
+ int memory_ion_fd;
+#endif
+ int memory_index;
+ int buffers_count;
+ int buffer_length;
+};
+
+struct smdk4x12_exif {
+ int enabled;
+
+ exif_attribute_t attributes;
+ void *jpeg_thumbnail_data;
+ int jpeg_thumbnail_size;
+
+ camera_memory_t *memory;
+ int memory_size;
+};
+
+struct smdk4x12_jpeg {
+ int enabled;
+
+ int fd;
+ struct jpeg_buf buffer_in;
+ struct jpeg_buf buffer_out;
+ camera_memory_t *memory_in;
+ void *memory_in_pointer;
+#ifdef EXYNOS_ION
+ int memory_in_ion_fd;
+#endif
+ camera_memory_t *memory_out;
+ void *memory_out_pointer;
+ int memory_out_size;
+#ifdef EXYNOS_ION
+ int memory_out_ion_fd;
+#endif
+
+ int width;
+ int height;
+ int format;
+
+ int quality;
+};
+
+struct exynox_camera_config {
+ struct smdk4x12_camera_preset *presets;
+ int presets_count;
+
+ struct smdk4x12_v4l2_node *v4l2_nodes;
+ int v4l2_nodes_count;
+};
+
+struct smdk4x12_camera_callbacks {
+ camera_notify_callback notify;
+ camera_data_callback data;
+ camera_data_timestamp_callback data_timestamp;
+ camera_request_memory request_memory;
+ void *user;
+};
+
+struct smdk4x12_camera {
+ int v4l2_fds[SMDK4x12_CAMERA_MAX_V4L2_NODES_COUNT];
+ int ion_fd;
+
+ struct exynox_camera_config *config;
+ struct smdk4x12_param *params;
+
+ struct smdk4x12_camera_callbacks callbacks;
+ int callback_lock;
+ int messages_enabled;
+
+ gralloc_module_t *gralloc;
+
+ // Capture
+
+ pthread_t capture_thread;
+ pthread_mutex_t capture_mutex;
+ pthread_mutex_t capture_lock_mutex;
+ int capture_thread_running;
+ int capture_thread_enabled;
+
+ int capture_enabled;
+ struct smdk4x12_camera_capture_listener *capture_listeners;
+ camera_memory_t *capture_memory;
+ int capture_memory_address;
+ int capture_memory_index;
+ void *capture_yuv_buffer;
+ void *capture_jpeg_buffer;
+ int capture_exif_flash;
+ int capture_exif_iso;
+ int capture_exif_exposure;
+ int capture_exif_exposure_bias;
+ int capture_exif_exposure_time;
+ int capture_hybrid;
+ int capture_width;
+ int capture_height;
+ int capture_format;
+ int capture_buffers_count;
+ int capture_buffer_length;
+
+ // Preview
+
+ pthread_t preview_thread;
+ pthread_mutex_t preview_mutex;
+ pthread_mutex_t preview_lock_mutex;
+ int preview_thread_running;
+ int preview_thread_enabled;
+
+ int preview_output_enabled;
+ struct smdk4x12_camera_capture_listener *preview_listener;
+ struct preview_stream_ops *preview_window;
+ struct smdk4x12_camera_buffer preview_buffer;
+ struct smdk4x12_v4l2_output preview_output;
+
+ // Picture
+
+ pthread_t picture_thread;
+ pthread_mutex_t picture_mutex;
+ pthread_mutex_t picture_lock_mutex;
+ int picture_thread_running;
+ int picture_thread_enabled;
+
+ int picture_enabled;
+ int picture_completed;
+ struct smdk4x12_camera_capture_listener *picture_listener;
+ camera_memory_t *picture_memory;
+ struct smdk4x12_camera_buffer picture_jpeg_buffer;
+ struct smdk4x12_camera_buffer picture_jpeg_thumbnail_buffer;
+ struct smdk4x12_camera_buffer picture_yuv_buffer;
+ struct smdk4x12_camera_buffer picture_yuv_thumbnail_buffer;
+ time_t picture_focus_timestamp;
+
+ // Recording
+
+ pthread_t recording_thread;
+ pthread_mutex_t recording_mutex;
+ pthread_mutex_t recording_lock_mutex;
+ int recording_thread_running;
+ int recording_thread_enabled;
+
+ int recording_output_enabled;
+ struct smdk4x12_camera_capture_listener *recording_listener;
+ camera_memory_t *recording_memory;
+ int recording_memory_index;
+ struct smdk4x12_camera_buffer recording_buffer;
+ struct smdk4x12_v4l2_output recording_output;
+ int recording_buffers_count;
+ int recording_buffer_length;
+ int recording_metadata;
+
+ // Auto-focus
+
+ pthread_t auto_focus_thread;
+ pthread_mutex_t auto_focus_mutex;
+ int auto_focus_thread_enabled;
+ int auto_focus_thread_running;
+
+ int auto_focus_result;
+
+ // Camera params
+
+ int camera_rotation;
+ int camera_hflip;
+ int camera_vflip;
+ int camera_capture_format;
+ int camera_picture_format;
+ int camera_fimc_is;
+ int camera_focal_length;
+ int camera_metering;
+
+ struct smdk4x12_camera_mbus_resolution *camera_mbus_resolutions;
+ int camera_mbus_resolutions_count;
+
+ int camera_sensor_mode;
+ int fimc_is_mode;
+
+ // Params
+
+ int preview_width;
+ int preview_height;
+ int preview_format;
+ int preview_fps;
+ 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 recording_width;
+ int recording_height;
+ int recording_format;
+ int focus_mode;
+ int focus_x;
+ int focus_y;
+ int zoom;
+ int ae_lock;
+ int awb_lock;
+ int flash_mode;
+ int exposure_compensation;
+ int whitebalance;
+ int antibanding;
+ int scene_mode;
+ int effect;
+ int iso;
+ int metering;
+ int image_stabilization;
+};
+
+struct smdk4x12_camera_addrs {
+ unsigned int type;
+ unsigned int y;
+ unsigned int cbcr;
+ unsigned int index;
+ unsigned int reserved;
+};
+
+// This is because the linux header uses anonymous union
+struct smdk4x12_v4l2_ext_control {
+ __u32 id;
+ __u32 size;
+ __u32 reserved2[1];
+ union {
+ __s32 value;
+ __s64 value64;
+ char *string;
+ } data;
+} __attribute__ ((packed));
+
+/*
+ * Camera
+ */
+
+// Camera
+int smdk4x12_camera_start(struct smdk4x12_camera *smdk4x12_camera, int id);
+void smdk4x12_camera_stop(struct smdk4x12_camera *smdk4x12_camera);
+
+// Params
+int smdk4x12_camera_params_init(struct smdk4x12_camera *smdk4x12_camera, int id);
+int smdk4x12_camera_params_apply(struct smdk4x12_camera *smdk4x12_camera, int force);
+
+// Capture
+int smdk4x12_camera_capture(struct smdk4x12_camera *smdk4x12_camera);
+int smdk4x12_camera_capture_thread_start(struct smdk4x12_camera *smdk4x12_camera);
+void smdk4x12_camera_capture_thread_stop(struct smdk4x12_camera *smdk4x12_camera);
+int smdk4x12_camera_capture_start(struct smdk4x12_camera *smdk4x12_camera);
+void smdk4x12_camera_capture_stop(struct smdk4x12_camera *smdk4x12_camera);
+int smdk4x12_camera_capture_setup(struct smdk4x12_camera *smdk4x12_camera);
+struct smdk4x12_camera_capture_listener *smdk4x12_camera_capture_listener_register(
+ struct smdk4x12_camera *smdk4x12_camera, int width, int height, int format,
+ int (*callback)(struct smdk4x12_camera *smdk4x12_camera, struct smdk4x12_camera_buffer *buffers, int buffers_count));
+void smdk4x12_camera_capture_listener_unregister(
+ struct smdk4x12_camera *smdk4x12_camera,
+ struct smdk4x12_camera_capture_listener *listener);
+
+// Preview
+int smdk4x12_camera_preview_output_start(struct smdk4x12_camera *smdk4x12_camera);
+void smdk4x12_camera_preview_output_stop(struct smdk4x12_camera *smdk4x12_camera);
+int smdk4x12_camera_preview_callback(struct smdk4x12_camera *smdk4x12_camera,
+ struct smdk4x12_camera_buffer *buffers, int buffers_count);
+int smdk4x12_camera_preview(struct smdk4x12_camera *smdk4x12_camera);
+int smdk4x12_camera_preview_thread_start(struct smdk4x12_camera *smdk4x12_camera);
+void smdk4x12_camera_preview_thread_stop(struct smdk4x12_camera *smdk4x12_camera);
+
+// Picture
+int smdk4x12_camera_picture_callback(struct smdk4x12_camera *smdk4x12_camera,
+ struct smdk4x12_camera_buffer *buffers, int buffers_count);
+int smdk4x12_camera_picture(struct smdk4x12_camera *smdk4x12_camera);
+int smdk4x12_camera_picture_thread_start(struct smdk4x12_camera *smdk4x12_camera);
+void smdk4x12_camera_picture_thread_stop(struct smdk4x12_camera *smdk4x12_camera);
+
+// Recording
+int smdk4x12_camera_recording_output_start(struct smdk4x12_camera *smdk4x12_camera);
+void smdk4x12_camera_recording_output_stop(struct smdk4x12_camera *smdk4x12_camera);
+int smdk4x12_camera_recording_callback(struct smdk4x12_camera *smdk4x12_camera,
+ struct smdk4x12_camera_buffer *buffers, int buffers_count);
+void smdk4x12_camera_recording_frame_release(struct smdk4x12_camera *smdk4x12_camera);
+int smdk4x12_camera_recording(struct smdk4x12_camera *smdk4x12_camera);
+int smdk4x12_camera_recording_thread_start(struct smdk4x12_camera *smdk4x12_camera);
+void smdk4x12_camera_recording_thread_stop(struct smdk4x12_camera *smdk4x12_camera);
+
+// Auto-focus
+int smdk4x12_camera_auto_focus(struct smdk4x12_camera *smdk4x12_camera, int auto_focus_status);
+int smdk4x12_camera_auto_focus_thread_start(struct smdk4x12_camera *smdk4x12_camera);
+void smdk4x12_camera_auto_focus_thread_stop(struct smdk4x12_camera *smdk4x12_camera);
+
+/*
+ * EXIF
+ */
+
+int smdk4x12_exif_start(struct smdk4x12_camera *smdk4x12_camera, struct smdk4x12_exif *exif);
+void smdk4x12_exif_stop(struct smdk4x12_camera *smdk4x12_camera,
+ struct smdk4x12_exif *exif);
+int smdk4x12_exif(struct smdk4x12_camera *smdk4x12_camera, struct smdk4x12_exif *exif);
+
+/*
+ * ION
+ */
+
+#ifdef EXYNOS_ION
+int smdk4x12_ion_init(struct smdk4x12_camera *smdk4x12_camera);
+int smdk4x12_ion_open(struct smdk4x12_camera *smdk4x12_camera);
+void smdk4x12_ion_close(struct smdk4x12_camera *smdk4x12_camera);
+int smdk4x12_ion_alloc(struct smdk4x12_camera *smdk4x12_camera, int size);
+int smdk4x12_ion_free(struct smdk4x12_camera *smdk4x12_camera, int fd);
+int smdk4x12_ion_phys(struct smdk4x12_camera *smdk4x12_camera, int fd);
+int smdk4x12_ion_msync(struct smdk4x12_camera *smdk4x12_camera, int fd,
+ int offset, int size);
+#endif
+
+/*
+ * Jpeg
+ */
+
+int smdk4x12_jpeg_start(struct smdk4x12_camera *smdk4x12_camera,
+ struct smdk4x12_jpeg *jpeg);
+void smdk4x12_jpeg_stop(struct smdk4x12_camera *smdk4x12_camera,
+ struct smdk4x12_jpeg *jpeg);
+int smdk4x12_jpeg(struct smdk4x12_camera *smdk4x12_camera, struct smdk4x12_jpeg *jpeg);
+
+/*
+ * Param
+ */
+
+int smdk4x12_param_int_get(struct smdk4x12_camera *smdk4x12_camera,
+ char *key);
+float smdk4x12_param_float_get(struct smdk4x12_camera *smdk4x12_camera,
+ char *key);
+char *smdk4x12_param_string_get(struct smdk4x12_camera *smdk4x12_camera,
+ char *key);
+int smdk4x12_param_int_set(struct smdk4x12_camera *smdk4x12_camera,
+ char *key, int integer);
+int smdk4x12_param_float_set(struct smdk4x12_camera *smdk4x12_camera,
+ char *key, float floating);
+int smdk4x12_param_string_set(struct smdk4x12_camera *smdk4x12_camera,
+ char *key, char *string);
+char *smdk4x12_params_string_get(struct smdk4x12_camera *smdk4x12_camera);
+int smdk4x12_params_string_set(struct smdk4x12_camera *smdk4x12_camera, char *string);
+
+/*
+ * Utils
+ */
+
+int list_head_insert(struct list_head *list, struct list_head *prev,
+ struct list_head *next);
+void list_head_remove(struct list_head *list);
+
+int smdk4x12_camera_buffer_length(int width, int height, int format);
+void smdk4x12_camera_yuv_planes(int width, int height, int format, int address, int *address_y, int *address_cb, int *address_cr);
+
+/*
+ * V4L2
+ */
+
+int smdk4x12_v4l2_init(struct smdk4x12_camera *smdk4x12_camera);
+int smdk4x12_v4l2_index(struct smdk4x12_camera *smdk4x12_camera, int smdk4x12_v4l2_id);
+int smdk4x12_v4l2_fd(struct smdk4x12_camera *smdk4x12_camera, int smdk4x12_v4l2_id);
+
+int smdk4x12_v4l2_open(struct smdk4x12_camera *smdk4x12_camera, int smdk4x12_v4l2_id);
+void smdk4x12_v4l2_close(struct smdk4x12_camera *smdk4x12_camera, int smdk4x12_v4l2_id);
+int smdk4x12_v4l2_ioctl(struct smdk4x12_camera *smdk4x12_camera, int smdk4x12_v4l2_id,
+ int request, void *data);
+int smdk4x12_v4l2_poll(struct smdk4x12_camera *smdk4x12_camera, int smdk4x12_v4l2_id);
+int smdk4x12_v4l2_qbuf(struct smdk4x12_camera *smdk4x12_camera, int smdk4x12_v4l2_id,
+ int type, int memory, int index, unsigned long userptr);
+int smdk4x12_v4l2_qbuf_cap(struct smdk4x12_camera *smdk4x12_camera, int smdk4x12_v4l2_id,
+ int index);
+int smdk4x12_v4l2_qbuf_out(struct smdk4x12_camera *smdk4x12_camera, int smdk4x12_v4l2_id,
+ int index, unsigned long userptr);
+int smdk4x12_v4l2_dqbuf(struct smdk4x12_camera *smdk4x12_camera, int smdk4x12_v4l2_id,
+ int type, int memory);
+int smdk4x12_v4l2_dqbuf_cap(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id);
+int smdk4x12_v4l2_dqbuf_out(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id);
+int smdk4x12_v4l2_reqbufs(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id, int type, int memory, int count);
+int smdk4x12_v4l2_reqbufs_cap(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id, int count);
+int smdk4x12_v4l2_reqbufs_out(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id, int count);
+int smdk4x12_v4l2_querybuf(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id, int type, int memory, int index);
+int smdk4x12_v4l2_querybuf_cap(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id, int index);
+int smdk4x12_v4l2_querybuf_out(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id, int index);
+int smdk4x12_v4l2_querycap(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id, int flags);
+int smdk4x12_v4l2_querycap_cap(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id);
+int smdk4x12_v4l2_querycap_out(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id);
+int smdk4x12_v4l2_streamon(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id, int type);
+int smdk4x12_v4l2_streamon_cap(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id);
+int smdk4x12_v4l2_streamon_out(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id);
+int smdk4x12_v4l2_streamoff(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id, int type);
+int smdk4x12_v4l2_streamoff_cap(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id);
+int smdk4x12_v4l2_streamoff_out(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id);
+int smdk4x12_v4l2_g_fmt(struct smdk4x12_camera *smdk4x12_camera, int smdk4x12_v4l2_id,
+ int type, int *width, int *height, int *fmt);
+int smdk4x12_v4l2_g_fmt_cap(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id, int *width, int *height, int *fmt);
+int smdk4x12_v4l2_g_fmt_out(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id, int *width, int *height, int *fmt);
+int smdk4x12_v4l2_s_fmt_pix(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id, int type, int width, int height, int fmt, int field,
+ int priv);
+int smdk4x12_v4l2_s_fmt_pix_cap(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id, int width, int height, int fmt, int priv);
+int smdk4x12_v4l2_s_fmt_pix_out(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id, int width, int height, int fmt, int priv);
+int smdk4x12_v4l2_s_fmt_win(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id, int left, int top, int width, int height);
+int smdk4x12_v4l2_enum_fmt(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id, int type, int fmt);
+int smdk4x12_v4l2_enum_fmt_cap(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id, int fmt);
+int smdk4x12_v4l2_enum_fmt_out(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id, int fmt);
+int smdk4x12_v4l2_enum_input(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id, int id);
+int smdk4x12_v4l2_s_input(struct smdk4x12_camera *smdk4x12_camera, int smdk4x12_v4l2_id,
+ int id);
+int smdk4x12_v4l2_g_ext_ctrls(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id, struct v4l2_ext_control *control, int count);
+int smdk4x12_v4l2_g_ctrl(struct smdk4x12_camera *smdk4x12_camera, int smdk4x12_v4l2_id,
+ int id, int *value);
+int smdk4x12_v4l2_s_ctrl(struct smdk4x12_camera *smdk4x12_camera, int smdk4x12_v4l2_id,
+ int id, int value);
+int smdk4x12_v4l2_s_parm(struct smdk4x12_camera *smdk4x12_camera, int smdk4x12_v4l2_id,
+ int type, struct v4l2_streamparm *streamparm);
+int smdk4x12_v4l2_s_parm_cap(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id, struct v4l2_streamparm *streamparm);
+int smdk4x12_v4l2_s_parm_out(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id, struct v4l2_streamparm *streamparm);
+int smdk4x12_v4l2_s_crop(struct smdk4x12_camera *smdk4x12_camera, int smdk4x12_v4l2_id,
+ int type, int left, int top, int width, int height);
+int smdk4x12_v4l2_s_crop_cap(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id, int left, int top, int width, int height);
+int smdk4x12_v4l2_s_crop_out(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id, int left, int top, int width, int height);
+int smdk4x12_v4l2_g_fbuf(struct smdk4x12_camera *smdk4x12_camera, int smdk4x12_v4l2_id,
+ void **base, int *width, int *height, int *fmt);
+int smdk4x12_v4l2_s_fbuf(struct smdk4x12_camera *smdk4x12_camera, int smdk4x12_v4l2_id,
+ void *base, int width, int height, int fmt);
+
+/*
+ * V4L2 Output
+ */
+
+int smdk4x12_v4l2_output_start(struct smdk4x12_camera *smdk4x12_camera,
+ struct smdk4x12_v4l2_output *output);
+void smdk4x12_v4l2_output_stop(struct smdk4x12_camera *smdk4x12_camera,
+ struct smdk4x12_v4l2_output *output);
+int smdk4x12_v4l2_output(struct smdk4x12_camera *smdk4x12_camera,
+ struct smdk4x12_v4l2_output *output, int buffer_address);
+int smdk4x12_v4l2_output_release(struct smdk4x12_camera *smdk4x12_camera,
+ struct smdk4x12_v4l2_output *output);
+
+#endif
diff --git a/camera/smdk4x12_exif.c b/camera/smdk4x12_exif.c
new file mode 100644
index 0000000..f2024fd
--- /dev/null
+++ b/camera/smdk4x12_exif.c
@@ -0,0 +1,888 @@
+/*
+ * Copyright (C) 2013-2014 Paul Kocialkowski <contact@paulk.fr>
+ *
+ * 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 <stdbool.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 <Exif.h>
+
+#define LOG_TAG "smdk4x12_camera"
+#include <utils/Log.h>
+#include <cutils/properties.h>
+
+#include "smdk4x12_camera.h"
+
+int smdk4x12_exif_attributes_create_static(struct smdk4x12_camera *smdk4x12_camera,
+ struct smdk4x12_exif *exif)
+{
+ exif_attribute_t *attributes;
+ unsigned char gps_version[] = { 0x02, 0x02, 0x00, 0x00 };
+ char property[PROPERTY_VALUE_MAX];
+ uint32_t av;
+
+ if (smdk4x12_camera == NULL || exif == NULL)
+ return -EINVAL;
+
+ attributes = &exif->attributes;
+
+ // Device
+
+ property_get("ro.product.brand", property, EXIF_DEF_MAKER);
+ strncpy((char *) attributes->maker, property,
+ sizeof(attributes->maker) - 1);
+ attributes->maker[sizeof(attributes->maker) - 1] = '\0';
+
+ property_get("ro.product.model", property, EXIF_DEF_MODEL);
+ strncpy((char *) attributes->model, property,
+ sizeof(attributes->model) - 1);
+ attributes->model[sizeof(attributes->model) - 1] = '\0';
+
+ property_get("ro.build.id", property, EXIF_DEF_SOFTWARE);
+ strncpy((char *) attributes->software, property,
+ sizeof(attributes->software) - 1);
+ attributes->software[sizeof(attributes->software) - 1] = '\0';
+
+ attributes->ycbcr_positioning = EXIF_DEF_YCBCR_POSITIONING;
+
+ attributes->fnumber.num = EXIF_DEF_FNUMBER_NUM;
+ attributes->fnumber.den = EXIF_DEF_FNUMBER_DEN;
+
+ attributes->exposure_program = EXIF_DEF_EXPOSURE_PROGRAM;
+
+ memcpy(attributes->exif_version, EXIF_DEF_EXIF_VERSION,
+ sizeof(attributes->exif_version));
+
+ av = APEX_FNUM_TO_APERTURE((double) attributes->fnumber.num /
+ attributes->fnumber.den);
+ attributes->aperture.num = av;
+ attributes->aperture.den = EXIF_DEF_APEX_DEN;
+ attributes->max_aperture.num = av;
+ attributes->max_aperture.den = EXIF_DEF_APEX_DEN;
+
+ strcpy((char *) attributes->user_comment, EXIF_DEF_USERCOMMENTS);
+ attributes->color_space = EXIF_DEF_COLOR_SPACE;
+ attributes->exposure_mode = EXIF_DEF_EXPOSURE_MODE;
+
+ // GPS version
+
+ memcpy(attributes->gps_version_id, gps_version, sizeof(gps_version));
+
+ attributes->compression_scheme = EXIF_DEF_COMPRESSION;
+ attributes->x_resolution.num = EXIF_DEF_RESOLUTION_NUM;
+ attributes->x_resolution.den = EXIF_DEF_RESOLUTION_DEN;
+ attributes->y_resolution.num = EXIF_DEF_RESOLUTION_NUM;
+ attributes->y_resolution.den = EXIF_DEF_RESOLUTION_DEN;
+ attributes->resolution_unit = EXIF_DEF_RESOLUTION_UNIT;
+
+ return 0;
+}
+
+int smdk4x12_exif_attributes_create_gps(struct smdk4x12_camera *smdk4x12_camera,
+ struct smdk4x12_exif *exif)
+{
+ exif_attribute_t *attributes;
+ float gps_latitude_float, gps_longitude_float, gps_altitude_float;
+ int gps_timestamp_int;
+ char *gps_processing_method_string;
+ long gps_latitude, gps_longitude;
+ long gps_altitude, gps_timestamp;
+ double gps_latitude_abs, gps_longitude_abs, gps_altitude_abs;
+
+ struct tm time_info;
+
+ if (smdk4x12_camera == NULL || exif == NULL)
+ return -EINVAL;
+
+ attributes = &exif->attributes;
+
+ gps_latitude_float = smdk4x12_param_float_get(smdk4x12_camera, "gps-latitude");
+ gps_longitude_float = smdk4x12_param_float_get(smdk4x12_camera, "gps-longitude");
+ gps_altitude_float = smdk4x12_param_float_get(smdk4x12_camera, "gps-altitude");
+ if (gps_altitude_float == -1)
+ gps_altitude_float = (float) smdk4x12_param_int_get(smdk4x12_camera, "gps-altitude");
+ gps_timestamp_int = smdk4x12_param_int_get(smdk4x12_camera, "gps-timestamp");
+ gps_processing_method_string = smdk4x12_param_string_get(smdk4x12_camera, "gps-processing-method");
+
+ if (gps_latitude_float == -1 || gps_longitude_float == -1 ||
+ gps_altitude_float == -1 || gps_timestamp_int <= 0 ||
+ gps_processing_method_string == NULL) {
+ attributes->enableGps = false;
+ return 0;
+ }
+
+ gps_latitude = (long) (gps_latitude_float * 10000000) / 1;
+ gps_longitude = (long) (gps_longitude_float * 10000000) / 1;
+ gps_altitude = (long) (gps_altitude_float * 100) / 1;
+ gps_timestamp = (long) gps_timestamp_int;
+
+ if (gps_latitude == 0 || gps_longitude == 0) {
+ attributes->enableGps = false;
+ return 0;
+ }
+
+ if (gps_latitude > 0)
+ strcpy((char *) attributes->gps_latitude_ref, "N");
+ else
+ strcpy((char *) attributes->gps_latitude_ref, "S");
+
+ if (gps_longitude > 0)
+ strcpy((char *) attributes->gps_longitude_ref, "E");
+ else
+ strcpy((char *) attributes->gps_longitude_ref, "W");
+
+ if (gps_altitude > 0)
+ attributes->gps_altitude_ref = 0;
+ else
+ attributes->gps_altitude_ref = 1;
+
+
+ gps_latitude_abs = fabs(gps_latitude);
+ gps_longitude_abs = fabs(gps_longitude);
+ gps_altitude_abs = fabs(gps_altitude);
+
+ attributes->gps_latitude[0].num = (uint32_t) gps_latitude_abs;
+ attributes->gps_latitude[0].den = 10000000;
+ attributes->gps_latitude[1].num = 0;
+ attributes->gps_latitude[1].den = 1;
+ attributes->gps_latitude[2].num = 0;
+ attributes->gps_latitude[2].den = 1;
+
+ attributes->gps_longitude[0].num = (uint32_t) gps_longitude_abs;
+ attributes->gps_longitude[0].den = 10000000;
+ attributes->gps_longitude[1].num = 0;
+ attributes->gps_longitude[1].den = 1;
+ attributes->gps_longitude[2].num = 0;
+ attributes->gps_longitude[2].den = 1;
+
+ attributes->gps_altitude.num = (uint32_t) gps_altitude_abs;
+ attributes->gps_altitude.den = 100;
+
+ gmtime_r(&gps_timestamp, &time_info);
+
+ attributes->gps_timestamp[0].num = time_info.tm_hour;
+ attributes->gps_timestamp[0].den = 1;
+ attributes->gps_timestamp[1].num = time_info.tm_min;
+ attributes->gps_timestamp[1].den = 1;
+ attributes->gps_timestamp[2].num = time_info.tm_sec;
+ attributes->gps_timestamp[2].den = 1;
+ snprintf((char *) attributes->gps_datestamp, sizeof(attributes->gps_datestamp),
+ "%04d:%02d:%02d", time_info.tm_year + 1900, time_info.tm_mon + 1, time_info.tm_mday);
+
+ attributes->enableGps = true;
+
+ return 0;
+}
+
+int smdk4x12_exif_attributes_create_params(struct smdk4x12_camera *smdk4x12_camera,
+ struct smdk4x12_exif *exif)
+{
+ exif_attribute_t *attributes;
+ uint32_t av, tv, bv, sv, ev;
+ time_t time_data;
+ struct tm *time_info;
+ int rotation;
+ int shutter_speed;
+ int exposure_time;
+ int iso_speed;
+ int exposure;
+ int flash_results;
+
+ int rc;
+
+ if (smdk4x12_camera == NULL || exif == NULL)
+ return -EINVAL;
+
+ attributes = &exif->attributes;
+
+ // Picture size
+
+ attributes->width = smdk4x12_camera->picture_width;
+ attributes->height = smdk4x12_camera->picture_height;
+
+ // Thumbnail
+
+ attributes->widthThumb = smdk4x12_camera->jpeg_thumbnail_width;
+ attributes->heightThumb = smdk4x12_camera->jpeg_thumbnail_height;
+ attributes->enableThumb = true;
+
+ // Orientation
+
+ rotation = smdk4x12_param_int_get(smdk4x12_camera, "rotation");
+ switch (rotation) {
+ case 90:
+ attributes->orientation = EXIF_ORIENTATION_90;
+ break;
+ case 180:
+ attributes->orientation = EXIF_ORIENTATION_180;
+ break;
+ case 270:
+ attributes->orientation = EXIF_ORIENTATION_270;
+ break;
+ case 0:
+ default:
+ attributes->orientation = EXIF_ORIENTATION_UP;
+ break;
+ }
+
+ // Time
+
+ time(&time_data);
+ time_info = localtime(&time_data);
+ strftime((char *) attributes->date_time, sizeof(attributes->date_time),
+ "%Y:%m:%d %H:%M:%S", time_info);
+
+ attributes->focal_length.num = smdk4x12_camera->camera_focal_length;
+ attributes->focal_length.den = EXIF_DEF_FOCAL_LEN_DEN;
+
+ if (smdk4x12_camera->camera_capture_format == V4L2_PIX_FMT_INTERLEAVED) {
+ attributes->shutter_speed.num = APEX_EXPOSURE_TO_SHUTTER(smdk4x12_camera->capture_exif_exposure_time);
+ attributes->shutter_speed.den = 100;
+
+ attributes->exposure_time.num = 1;
+ attributes->exposure_time.den = smdk4x12_camera->capture_exif_exposure_time;
+
+ attributes->iso_speed_rating = smdk4x12_camera->capture_exif_iso;
+
+ attributes->flash = smdk4x12_camera->capture_exif_flash;
+
+ bv = smdk4x12_camera->capture_exif_exposure;
+ ev = smdk4x12_camera->capture_exif_exposure_bias;
+ } else {
+ shutter_speed = 100;
+ rc = smdk4x12_v4l2_g_ctrl(smdk4x12_camera, 0, V4L2_CID_CAMERA_EXIF_TV,
+ &shutter_speed);
+ if (rc < 0)
+ ALOGE("%s: Unable to get shutter speed", __func__);
+
+ attributes->shutter_speed.num = shutter_speed;
+ attributes->shutter_speed.den = 100;
+
+ attributes->exposure_time.num = 1;
+ attributes->exposure_time.den = APEX_SHUTTER_TO_EXPOSURE(shutter_speed);
+
+ rc = smdk4x12_v4l2_g_ctrl(smdk4x12_camera, 0, V4L2_CID_CAMERA_EXIF_ISO,
+ &iso_speed);
+ if (rc < 0)
+ ALOGE("%s: Unable to get iso", __func__);
+
+ attributes->iso_speed_rating = iso_speed;
+
+ rc = smdk4x12_v4l2_g_ctrl(smdk4x12_camera, 0, V4L2_CID_CAMERA_EXIF_FLASH,
+ &flash_results);
+ if (rc < 0)
+ ALOGE("%s: Unable to get flash", __func__);
+
+ attributes->flash = flash_results;
+
+ rc = smdk4x12_v4l2_g_ctrl(smdk4x12_camera, 0, V4L2_CID_CAMERA_EXIF_BV,
+ (int *) &bv);
+ if (rc < 0) {
+ ALOGE("%s: Unable to get bv", __func__);
+ goto bv_static;
+ }
+
+ rc = smdk4x12_v4l2_g_ctrl(smdk4x12_camera, 0, V4L2_CID_CAMERA_EXIF_EBV,
+ (int *) &ev);
+ if (rc < 0) {
+ ALOGE("%s: Unable to get ebv", __func__);
+ goto bv_static;
+ }
+ }
+
+ goto bv_camera;
+
+bv_static:
+ exposure = smdk4x12_param_int_get(smdk4x12_camera, "exposure-compensation");
+ if (exposure < 0)
+ exposure = EV_DEFAULT;
+
+ av = APEX_FNUM_TO_APERTURE((double) attributes->fnumber.num /
+ attributes->fnumber.den);
+ tv = APEX_EXPOSURE_TO_SHUTTER((double) attributes->exposure_time.num /
+ attributes->exposure_time.den);
+ sv = APEX_ISO_TO_FILMSENSITIVITY(iso_speed);
+ bv = av + tv - sv;
+ ev = exposure - EV_DEFAULT;
+
+bv_camera:
+ attributes->brightness.num = bv;
+ attributes->brightness.den = EXIF_DEF_APEX_DEN;
+
+ if (smdk4x12_camera->scene_mode == SCENE_MODE_BEACH_SNOW) {
+ attributes->exposure_bias.num = EXIF_DEF_APEX_DEN;
+ attributes->exposure_bias.den = EXIF_DEF_APEX_DEN;
+ } else {
+ attributes->exposure_bias.num = ev * EXIF_DEF_APEX_DEN;
+ attributes->exposure_bias.den = EXIF_DEF_APEX_DEN;
+ }
+
+ switch (smdk4x12_camera->camera_metering) {
+ case METERING_CENTER:
+ attributes->metering_mode = EXIF_METERING_CENTER;
+ break;
+ case METERING_MATRIX:
+ attributes->metering_mode = EXIF_METERING_AVERAGE;
+ break;
+ case METERING_SPOT:
+ attributes->metering_mode = EXIF_METERING_SPOT;
+ break;
+ default:
+ attributes->metering_mode = EXIF_METERING_AVERAGE;
+ break;
+ }
+
+ if (smdk4x12_camera->whitebalance == WHITE_BALANCE_AUTO ||
+ smdk4x12_camera->whitebalance == WHITE_BALANCE_BASE)
+ attributes->white_balance = EXIF_WB_AUTO;
+ else
+ attributes->white_balance = EXIF_WB_MANUAL;
+
+ switch (smdk4x12_camera->scene_mode) {
+ case SCENE_MODE_PORTRAIT:
+ attributes->scene_capture_type = EXIF_SCENE_PORTRAIT;
+ break;
+ case SCENE_MODE_LANDSCAPE:
+ attributes->scene_capture_type = EXIF_SCENE_LANDSCAPE;
+ break;
+ case SCENE_MODE_NIGHTSHOT:
+ attributes->scene_capture_type = EXIF_SCENE_NIGHT;
+ break;
+ default:
+ attributes->scene_capture_type = EXIF_SCENE_STANDARD;
+ break;
+ }
+
+ rc = smdk4x12_exif_attributes_create_gps(smdk4x12_camera, exif);
+ if (rc < 0) {
+ ALOGE("%s: Unable to create GPS attributes", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int smdk4x12_exif_write_data(void *exif_data, unsigned short tag,
+ unsigned short type, unsigned int count, unsigned int *offset, void *start,
+ void *data, int length)
+{
+ unsigned char *pointer;
+ int size;
+
+ if (exif_data == NULL || data == NULL || length <= 0)
+ return -EINVAL;
+
+ pointer = (unsigned char *) exif_data;
+
+ memcpy(pointer, &tag, sizeof(tag));
+ pointer += sizeof(tag);
+
+ memcpy(pointer, &type, sizeof(type));
+ pointer += sizeof(type);
+
+ memcpy(pointer, &count, sizeof(count));
+ pointer += sizeof(count);
+
+ if (offset != NULL && start != NULL) {
+ memcpy(pointer, offset, sizeof(*offset));
+ pointer += sizeof(*offset);
+
+ memcpy((void *) ((int) start + *offset), data, count * length);
+ *offset += count * length;
+ } else {
+ memcpy(pointer, data, count * length);
+ pointer += 4;
+ }
+
+ size = (int) pointer - (int) exif_data;
+ return size;
+}
+
+int smdk4x12_exif_start(struct smdk4x12_camera *smdk4x12_camera, struct smdk4x12_exif *exif)
+{
+ int rc;
+
+ if (smdk4x12_camera == NULL || exif == NULL)
+ return -EINVAL;
+
+ ALOGD("%s()", __func__);
+
+ if (exif->enabled) {
+ ALOGE("Exif was already started");
+ return -1;
+ }
+
+ rc = smdk4x12_exif_attributes_create_static(smdk4x12_camera, exif);
+ if (rc < 0) {
+ ALOGE("%s: Unable to create exif attributes", __func__);
+ goto error;
+ }
+
+ rc = smdk4x12_exif_attributes_create_params(smdk4x12_camera, exif);
+ if (rc < 0) {
+ ALOGE("%s: Unable to create exif parameters", __func__);
+ goto error;
+ }
+
+ exif->enabled = 1;
+
+ rc = 0;
+ goto complete;
+
+error:
+ rc = -1;
+
+complete:
+ return rc;
+}
+
+void smdk4x12_exif_stop(struct smdk4x12_camera *smdk4x12_camera,
+ struct smdk4x12_exif *exif)
+{
+ if (smdk4x12_camera == NULL || exif == NULL)
+ return;
+
+ ALOGD("%s()", __func__);
+
+ if (!exif->enabled) {
+ ALOGE("Exif was already stopped");
+ return;
+ }
+
+ if (exif->memory != NULL && exif->memory->release != NULL) {
+ exif->memory->release(exif->memory);
+ exif->memory = NULL;
+ }
+
+ exif->enabled = 0;
+}
+
+int smdk4x12_exif(struct smdk4x12_camera *smdk4x12_camera, struct smdk4x12_exif *exif)
+{
+ // Markers
+ unsigned char exif_app1_marker[] = { 0xff, 0xe1 };
+ unsigned char exif_app1_size[] = { 0x00, 0x00 };
+ unsigned char exif_marker[] = { 0x45, 0x78, 0x69, 0x66, 0x00, 0x00 };
+ unsigned char tiff_marker[] = { 0x49, 0x49, 0x2A, 0x00, 0x08, 0x00, 0x00, 0x00 };
+
+ unsigned char user_comment_code[] = { 0x41, 0x53, 0x43, 0x49, 0x49, 0x0, 0x0, 0x0 };
+ unsigned char exif_ascii_prefix[] = { 0x41, 0x53, 0x43, 0x49, 0x49, 0x0, 0x0, 0x0 };
+
+ void *jpeg_thumbnail_data;
+ int jpeg_thumbnail_size;
+ camera_memory_t *memory = NULL;
+ int memory_size;
+ exif_attribute_t *attributes;
+ void *exif_ifd_data_start = NULL;
+ void *exif_ifd_start = NULL;
+ void *exif_ifd_gps = NULL;
+ void *exif_ifd_thumb = NULL;
+ unsigned char *pointer;
+ unsigned int offset;
+ unsigned int value;
+ void *data;
+ int count;
+ int rc;
+
+ if (smdk4x12_camera == NULL || exif == NULL)
+ return -EINVAL;
+
+ ALOGD("%s()", __func__);
+
+ if (!exif->enabled) {
+ ALOGE("Exif was not started");
+ return -1;
+ }
+
+ jpeg_thumbnail_data = exif->jpeg_thumbnail_data;
+ jpeg_thumbnail_size = exif->jpeg_thumbnail_size;
+
+ if (jpeg_thumbnail_data == NULL || jpeg_thumbnail_size <= 0) {
+ ALOGE("%s: Invalid jpeg thumbnail", __func__);
+ goto error;
+ }
+
+ attributes = &exif->attributes;
+
+ memory_size = EXIF_FILE_SIZE + jpeg_thumbnail_size;
+
+ if (SMDK4x12_CAMERA_CALLBACK_DEFINED(request_memory)) {
+ memory = smdk4x12_camera->callbacks.request_memory(-1, memory_size, 1, smdk4x12_camera->callbacks.user);
+ if (memory == NULL || memory->data == NULL || memory->data == MAP_FAILED) {
+ ALOGE("%s: Unable to request memory", __func__);
+ goto error;
+ }
+ } else {
+ ALOGE("%s: No memory request function!", __func__);
+ goto error;
+ }
+
+ memset(memory->data, 0, memory_size);
+
+ pointer = (unsigned char *) memory->data;
+ exif_ifd_data_start = (void *) pointer;
+
+ // Skip 4 bytes for APP1 marker
+ pointer += 4;
+
+ // Copy EXIF marker
+ memcpy(pointer, exif_marker, sizeof(exif_marker));
+ pointer += sizeof(exif_marker);
+
+ // Copy TIFF marker
+ memcpy(pointer, tiff_marker, sizeof(tiff_marker));
+ exif_ifd_start = (void *) pointer;
+ pointer += sizeof(tiff_marker);
+
+ if (attributes->enableGps)
+ value = NUM_0TH_IFD_TIFF;
+ else
+ value = NUM_0TH_IFD_TIFF - 1;
+
+ memcpy(pointer, &value, NUM_SIZE);
+ pointer += NUM_SIZE;
+
+ offset = 8 + NUM_SIZE + value * IFD_SIZE + OFFSET_SIZE;
+
+ // Write EXIF data
+ count = smdk4x12_exif_write_data(pointer, EXIF_TAG_IMAGE_WIDTH,
+ EXIF_TYPE_LONG, 1, NULL, NULL, &attributes->width, sizeof(attributes->width));
+ pointer += count;
+
+ count = smdk4x12_exif_write_data(pointer, EXIF_TAG_IMAGE_HEIGHT,
+ EXIF_TYPE_LONG, 1, NULL, NULL, &attributes->height, sizeof(attributes->height));
+ pointer += count;
+
+ count = smdk4x12_exif_write_data(pointer, EXIF_TAG_MAKE,
+ EXIF_TYPE_ASCII, strlen((char *) attributes->maker) + 1, &offset, exif_ifd_start, &attributes->maker, sizeof(char));
+ pointer += count;
+
+ count = smdk4x12_exif_write_data(pointer, EXIF_TAG_MODEL,
+ EXIF_TYPE_ASCII, strlen((char *) attributes->model) + 1, &offset, exif_ifd_start, &attributes->model, sizeof(char));
+ pointer += count;
+
+ count = smdk4x12_exif_write_data(pointer, EXIF_TAG_ORIENTATION,
+ EXIF_TYPE_SHORT, 1, NULL, NULL, &attributes->orientation, sizeof(attributes->orientation));
+ pointer += count;
+
+ count = smdk4x12_exif_write_data(pointer, EXIF_TAG_SOFTWARE,
+ EXIF_TYPE_ASCII, strlen((char *) attributes->software) + 1, &offset, exif_ifd_start, &attributes->software, sizeof(char));
+ pointer += count;
+
+ count = smdk4x12_exif_write_data(pointer, EXIF_TAG_DATE_TIME,
+ EXIF_TYPE_ASCII, 20, &offset, exif_ifd_start, &attributes->date_time, sizeof(char));
+ pointer += count;
+
+ count = smdk4x12_exif_write_data(pointer, EXIF_TAG_YCBCR_POSITIONING,
+ EXIF_TYPE_SHORT, 1, NULL, NULL, &attributes->ycbcr_positioning, sizeof(attributes->ycbcr_positioning));
+ pointer += count;
+
+ count = smdk4x12_exif_write_data(pointer, EXIF_TAG_EXIF_IFD_POINTER,
+ EXIF_TYPE_LONG, 1, NULL, NULL, &offset, sizeof(offset));
+ pointer += count;
+
+ if (attributes->enableGps) {
+ exif_ifd_gps = (void *) pointer;
+ pointer += IFD_SIZE;
+ }
+
+ exif_ifd_thumb = (void *) pointer;
+ pointer += OFFSET_SIZE;
+
+ pointer = (unsigned char *) exif_ifd_start;
+ pointer += offset;
+
+ value = NUM_0TH_IFD_EXIF;
+ memcpy(pointer, &value, NUM_SIZE);
+ pointer += NUM_SIZE;
+
+ offset += NUM_SIZE + NUM_0TH_IFD_EXIF * IFD_SIZE + OFFSET_SIZE;
+
+ count = smdk4x12_exif_write_data(pointer, EXIF_TAG_EXPOSURE_TIME,
+ EXIF_TYPE_RATIONAL, 1, &offset, exif_ifd_start, &attributes->exposure_time, sizeof(attributes->exposure_time));
+ pointer += count;
+
+ count = smdk4x12_exif_write_data(pointer, EXIF_TAG_FNUMBER,
+ EXIF_TYPE_RATIONAL, 1, &offset, exif_ifd_start, &attributes->fnumber, sizeof(attributes->fnumber));
+ pointer += count;
+
+ count = smdk4x12_exif_write_data(pointer, EXIF_TAG_EXPOSURE_PROGRAM,
+ EXIF_TYPE_SHORT, 1, NULL, NULL, &attributes->exposure_program, sizeof(attributes->exposure_program));
+ pointer += count;
+
+ count = smdk4x12_exif_write_data(pointer, EXIF_TAG_ISO_SPEED_RATING,
+ EXIF_TYPE_SHORT, 1, NULL, NULL, &attributes->iso_speed_rating, sizeof(attributes->iso_speed_rating));
+ pointer += count;
+
+ count = smdk4x12_exif_write_data(pointer, EXIF_TAG_EXIF_VERSION,
+ EXIF_TYPE_UNDEFINED, 4, NULL, NULL, &attributes->exif_version, sizeof(char));
+ pointer += count;
+
+ count = smdk4x12_exif_write_data(pointer, EXIF_TAG_DATE_TIME_ORG,
+ EXIF_TYPE_ASCII, 20, &offset, exif_ifd_start, &attributes->date_time, sizeof(char));
+ pointer += count;
+
+ count = smdk4x12_exif_write_data(pointer, EXIF_TAG_DATE_TIME_DIGITIZE,
+ EXIF_TYPE_ASCII, 20, &offset, exif_ifd_start, &attributes->date_time, sizeof(char));
+ pointer += count;
+
+ count = smdk4x12_exif_write_data(pointer, EXIF_TAG_SHUTTER_SPEED,
+ EXIF_TYPE_SRATIONAL, 1, &offset, exif_ifd_start, &attributes->shutter_speed, sizeof(attributes->shutter_speed));
+ pointer += count;
+
+ count = smdk4x12_exif_write_data(pointer, EXIF_TAG_APERTURE,
+ EXIF_TYPE_RATIONAL, 1, &offset, exif_ifd_start, &attributes->aperture, sizeof(attributes->aperture));
+ pointer += count;
+
+ count = smdk4x12_exif_write_data(pointer, EXIF_TAG_BRIGHTNESS,
+ EXIF_TYPE_SRATIONAL, 1, &offset, exif_ifd_start, &attributes->brightness, sizeof(attributes->brightness));
+ pointer += count;
+
+ count = smdk4x12_exif_write_data(pointer, EXIF_TAG_EXPOSURE_BIAS,
+ EXIF_TYPE_SRATIONAL, 1, &offset, exif_ifd_start, &attributes->exposure_bias, sizeof(attributes->exposure_bias));
+ pointer += count;
+
+ count = smdk4x12_exif_write_data(pointer, EXIF_TAG_MAX_APERTURE,
+ EXIF_TYPE_RATIONAL, 1, &offset, exif_ifd_start, &attributes->max_aperture, sizeof(attributes->max_aperture));
+ pointer += count;
+
+ count = smdk4x12_exif_write_data(pointer, EXIF_TAG_METERING_MODE,
+ EXIF_TYPE_SHORT, 1, NULL, NULL, &attributes->metering_mode, sizeof(attributes->metering_mode));
+ pointer += count;
+
+ count = smdk4x12_exif_write_data(pointer, EXIF_TAG_FLASH,
+ EXIF_TYPE_SHORT, 1, NULL, NULL, &attributes->flash, sizeof(attributes->flash));
+ pointer += count;
+
+ count = smdk4x12_exif_write_data(pointer, EXIF_TAG_FOCAL_LENGTH,
+ EXIF_TYPE_RATIONAL, 1, &offset, exif_ifd_start, &attributes->focal_length, sizeof(attributes->focal_length));
+ pointer += count;
+
+ value = strlen((char *) attributes->user_comment) + 1;
+ memmove(attributes->user_comment + sizeof(user_comment_code), attributes->user_comment, value);
+ memcpy(attributes->user_comment, user_comment_code, sizeof(user_comment_code));
+
+ count = smdk4x12_exif_write_data(pointer, EXIF_TAG_USER_COMMENT,
+ EXIF_TYPE_UNDEFINED, value + sizeof(user_comment_code), &offset, exif_ifd_start, &attributes->user_comment, sizeof(char));
+ pointer += count;
+
+ count = smdk4x12_exif_write_data(pointer, EXIF_TAG_COLOR_SPACE,
+ EXIF_TYPE_SHORT, 1, NULL, NULL, &attributes->color_space, sizeof(attributes->color_space));
+ pointer += count;
+
+ count = smdk4x12_exif_write_data(pointer, EXIF_TAG_PIXEL_X_DIMENSION,
+ EXIF_TYPE_LONG, 1, NULL, NULL, &attributes->width, sizeof(attributes->width));
+ pointer += count;
+
+ count = smdk4x12_exif_write_data(pointer, EXIF_TAG_PIXEL_Y_DIMENSION,
+ EXIF_TYPE_LONG, 1, NULL, NULL, &attributes->height, sizeof(attributes->height));
+ pointer += count;
+
+ count = smdk4x12_exif_write_data(pointer, EXIF_TAG_EXPOSURE_MODE,
+ EXIF_TYPE_SHORT, 1, NULL, NULL, &attributes->exposure_mode, sizeof(attributes->exposure_mode));
+ pointer += count;
+
+ count = smdk4x12_exif_write_data(pointer, EXIF_TAG_WHITE_BALANCE,
+ EXIF_TYPE_SHORT, 1, NULL, NULL, &attributes->white_balance, sizeof(attributes->white_balance));
+ pointer += count;
+
+ count = smdk4x12_exif_write_data(pointer, EXIF_TAG_SCENCE_CAPTURE_TYPE,
+ EXIF_TYPE_SHORT, 1, NULL, NULL, &attributes->scene_capture_type, sizeof(attributes->scene_capture_type));
+ pointer += count;
+
+ value = 0;
+ memcpy(pointer, &value, OFFSET_SIZE);
+ pointer += OFFSET_SIZE;
+
+ // GPS
+ if (attributes->enableGps) {
+ pointer = (unsigned char *) exif_ifd_gps;
+ count = smdk4x12_exif_write_data(pointer, EXIF_TAG_GPS_IFD_POINTER,
+ EXIF_TYPE_LONG, 1, NULL, NULL, &offset, sizeof(offset));
+
+ pointer = (unsigned char *) exif_ifd_start + offset;
+
+ if (attributes->gps_processing_method[0] == 0)
+ value = NUM_0TH_IFD_GPS - 1;
+ else
+ value = NUM_0TH_IFD_GPS;
+
+ memcpy(pointer, &value, NUM_SIZE);
+ pointer += NUM_SIZE;
+
+ offset += NUM_SIZE + value * IFD_SIZE + OFFSET_SIZE;
+
+ count = smdk4x12_exif_write_data(pointer, EXIF_TAG_GPS_VERSION_ID,
+ EXIF_TYPE_BYTE, 4, NULL, NULL, &attributes->gps_version_id, sizeof(char));
+ pointer += count;
+
+ count = smdk4x12_exif_write_data(pointer, EXIF_TAG_GPS_LATITUDE_REF,
+ EXIF_TYPE_ASCII, 2, NULL, NULL, &attributes->gps_latitude_ref, sizeof(char));
+ pointer += count;
+
+ count = smdk4x12_exif_write_data(pointer, EXIF_TAG_GPS_LATITUDE,
+ EXIF_TYPE_RATIONAL, 3, &offset, exif_ifd_start, &attributes->gps_latitude, sizeof(attributes->gps_latitude[0]));
+ pointer += count;
+
+ count = smdk4x12_exif_write_data(pointer, EXIF_TAG_GPS_LONGITUDE_REF,
+ EXIF_TYPE_ASCII, 2, NULL, NULL, &attributes->gps_longitude_ref, sizeof(char));
+ pointer += count;
+
+ count = smdk4x12_exif_write_data(pointer, EXIF_TAG_GPS_LONGITUDE,
+ EXIF_TYPE_RATIONAL, 3, &offset, exif_ifd_start, &attributes->gps_longitude, sizeof(attributes->gps_longitude[0]));
+ pointer += count;
+
+ count = smdk4x12_exif_write_data(pointer, EXIF_TAG_GPS_ALTITUDE_REF,
+ EXIF_TYPE_BYTE, 1, NULL, NULL, &attributes->gps_altitude_ref, sizeof(char));
+ pointer += count;
+
+ count = smdk4x12_exif_write_data(pointer, EXIF_TAG_GPS_ALTITUDE,
+ EXIF_TYPE_RATIONAL, 1, &offset, exif_ifd_start, &attributes->gps_altitude, sizeof(attributes->gps_altitude));
+ pointer += count;
+
+ count = smdk4x12_exif_write_data(pointer, EXIF_TAG_GPS_TIMESTAMP,
+ EXIF_TYPE_RATIONAL, 3, &offset, exif_ifd_start, &attributes->gps_timestamp, sizeof(attributes->gps_timestamp[0]));
+ pointer += count;
+
+ value = strlen((char *) attributes->gps_processing_method);
+ if (value > 0) {
+ value = value > 100 ? 100 : value;
+
+ data = calloc(1, value + sizeof(exif_ascii_prefix));
+ memcpy(data, &exif_ascii_prefix, sizeof(exif_ascii_prefix));
+ memcpy((void *) ((int) data + (int) sizeof(exif_ascii_prefix)), attributes->gps_processing_method, value);
+
+ count = smdk4x12_exif_write_data(pointer, EXIF_TAG_GPS_PROCESSING_METHOD,
+ EXIF_TYPE_UNDEFINED, value + sizeof(exif_ascii_prefix), &offset, exif_ifd_start, data, sizeof(char));
+ pointer += count;
+
+ free(data);
+ }
+
+ count = smdk4x12_exif_write_data(pointer, EXIF_TAG_GPS_DATESTAMP,
+ EXIF_TYPE_ASCII, 11, &offset, exif_ifd_start, &attributes->gps_datestamp, 1);
+ pointer += count;
+
+ value = 0;
+ memcpy(pointer, &value, OFFSET_SIZE);
+ pointer += OFFSET_SIZE;
+ }
+
+ if (attributes->enableThumb) {
+ value = offset;
+ memcpy(exif_ifd_thumb, &value, OFFSET_SIZE);
+
+ pointer = (unsigned char *) ((int) exif_ifd_start + (int) offset);
+
+ value = NUM_1TH_IFD_TIFF;
+ memcpy(pointer, &value, NUM_SIZE);
+ pointer += NUM_SIZE;
+
+ offset += NUM_SIZE + NUM_1TH_IFD_TIFF * IFD_SIZE + OFFSET_SIZE;
+
+ count = smdk4x12_exif_write_data(pointer, EXIF_TAG_IMAGE_WIDTH,
+ EXIF_TYPE_LONG, 1, NULL, NULL, &attributes->widthThumb, sizeof(attributes->widthThumb));
+ pointer += count;
+
+ count = smdk4x12_exif_write_data(pointer, EXIF_TAG_IMAGE_HEIGHT,
+ EXIF_TYPE_LONG, 1, NULL, NULL, &attributes->heightThumb, sizeof(attributes->heightThumb));
+ pointer += count;
+
+ count = smdk4x12_exif_write_data(pointer, EXIF_TAG_COMPRESSION_SCHEME,
+ EXIF_TYPE_SHORT, 1, NULL, NULL, &attributes->compression_scheme, sizeof(attributes->compression_scheme));
+ pointer += count;
+
+ count = smdk4x12_exif_write_data(pointer, EXIF_TAG_ORIENTATION,
+ EXIF_TYPE_SHORT, 1, NULL, NULL, &attributes->orientation, sizeof(attributes->orientation));
+ pointer += count;
+
+ count = smdk4x12_exif_write_data(pointer, EXIF_TAG_X_RESOLUTION,
+ EXIF_TYPE_RATIONAL, 1, &offset, exif_ifd_start, &attributes->x_resolution, sizeof(attributes->x_resolution));
+ pointer += count;
+
+ count = smdk4x12_exif_write_data(pointer, EXIF_TAG_Y_RESOLUTION,
+ EXIF_TYPE_RATIONAL, 1, &offset, exif_ifd_start, &attributes->y_resolution, sizeof(attributes->y_resolution));
+ pointer += count;
+
+ count = smdk4x12_exif_write_data(pointer, EXIF_TAG_RESOLUTION_UNIT,
+ EXIF_TYPE_SHORT, 1, NULL, NULL, &attributes->resolution_unit, sizeof(attributes->resolution_unit));
+ pointer += count;
+
+ count = smdk4x12_exif_write_data(pointer, EXIF_TAG_JPEG_INTERCHANGE_FORMAT,
+ EXIF_TYPE_LONG, 1, NULL, NULL, &offset, sizeof(offset));
+ pointer += count;
+
+ value = (unsigned int) jpeg_thumbnail_size;
+
+ count = smdk4x12_exif_write_data(pointer, EXIF_TAG_JPEG_INTERCHANGE_FORMAT_LEN,
+ EXIF_TYPE_LONG, 1, NULL, NULL, &value, sizeof(value));
+ pointer += count;
+
+ value = 0;
+ memcpy(pointer, &value, OFFSET_SIZE);
+
+ pointer = (unsigned char *) ((int) exif_ifd_start + (int) offset);
+
+ memcpy(pointer, jpeg_thumbnail_data, jpeg_thumbnail_size);
+ offset += jpeg_thumbnail_size;
+ } else {
+ value = 0;
+ memcpy(exif_ifd_thumb, &value, OFFSET_SIZE);
+
+ }
+
+ pointer = (unsigned char *) exif_ifd_data_start;
+
+ memcpy(pointer, exif_app1_marker, sizeof(exif_app1_marker));
+ pointer += sizeof(exif_app1_marker);
+
+ memory_size = offset + 10;
+ value = memory_size - 2;
+ exif_app1_size[0] = (value >> 8) & 0xff;
+ exif_app1_size[1] = value & 0xff;
+
+ memcpy(pointer, exif_app1_size, sizeof(exif_app1_size));
+
+ exif->memory = memory;
+ exif->memory_size = memory_size;
+
+ rc = 0;
+ goto complete;
+
+error:
+ if (memory != NULL && memory->release != NULL) {
+ memory->release(memory);
+ exif->memory = NULL;
+ }
+
+ rc = -1;
+
+complete:
+ return rc;
+}
diff --git a/camera/smdk4x12_ion.c b/camera/smdk4x12_ion.c
new file mode 100644
index 0000000..39b2b06
--- /dev/null
+++ b/camera/smdk4x12_ion.c
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2013 Paul Kocialkowski <contact@paulk.fr>
+ *
+ * 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 <errno.h>
+#include <malloc.h>
+#include <ctype.h>
+
+#include <ion.h>
+
+#define LOG_TAG "smdk4x12_ion"
+#include <utils/Log.h>
+
+#include "smdk4x12_camera.h"
+
+int smdk4x12_ion_init(struct smdk4x12_camera *smdk4x12_camera)
+{
+ smdk4x12_camera->ion_fd = -1;
+
+ return 0;
+}
+
+int smdk4x12_ion_open(struct smdk4x12_camera *smdk4x12_camera)
+{
+ int fd;
+
+ fd = open("/dev/ion", O_RDWR);
+ if (fd < 0) {
+ ALOGE("%s: Unable to open ion device", __func__);
+ return -1;
+ }
+
+ smdk4x12_camera->ion_fd = fd;
+
+ return 0;
+}
+
+void smdk4x12_ion_close(struct smdk4x12_camera *smdk4x12_camera)
+{
+ if (smdk4x12_camera->ion_fd >= 0)
+ close(smdk4x12_camera->ion_fd);
+
+ smdk4x12_camera->ion_fd = -1;
+}
+
+int smdk4x12_ion_alloc(struct smdk4x12_camera *smdk4x12_camera, int size)
+{
+ struct ion_allocation_data alloc_data;
+ struct ion_fd_data share_data;
+ struct ion_handle_data free_data;
+ int page_size;
+ int fd;
+ int rc;
+
+ page_size = getpagesize();
+
+ fd = smdk4x12_camera->ion_fd;
+ if (fd < 0)
+ return -1;
+
+ memset(&alloc_data, 0, sizeof(alloc_data));
+ alloc_data.len = size;
+ alloc_data.align = page_size;
+ alloc_data.flags = ION_HEAP_EXYNOS_CONTIG_MASK;
+
+ rc = ioctl(fd, ION_IOC_ALLOC, &alloc_data);
+ if (rc < 0)
+ return -1;
+
+ memset(&share_data, 0, sizeof(share_data));
+ share_data.handle = alloc_data.handle;
+
+ rc = ioctl(fd, ION_IOC_SHARE, &share_data);
+ if (rc < 0)
+ return -1;
+
+ memset(&free_data, 0, sizeof(free_data));
+ free_data.handle = alloc_data.handle;
+
+ rc = ioctl(fd, ION_IOC_FREE, &free_data);
+ if (rc < 0)
+ return -1;
+
+ return share_data.fd;
+}
+
+int smdk4x12_ion_free(struct smdk4x12_camera *smdk4x12_camera, int fd)
+{
+ close(fd);
+ return 0;
+}
+
+int smdk4x12_ion_phys(struct smdk4x12_camera *smdk4x12_camera, int fd)
+{
+ struct ion_custom_data custom_data;
+ struct ion_phys_data phys_data;
+ int rc;
+
+ memset(&phys_data, 0, sizeof(phys_data));
+ phys_data.fd_buffer = fd;
+
+ memset(&custom_data, 0, sizeof(custom_data));
+ custom_data.cmd = ION_EXYNOS_CUSTOM_PHYS;
+ custom_data.arg = (unsigned long) &phys_data;
+
+ fd = smdk4x12_camera->ion_fd;
+ if (fd < 0)
+ return -1;
+
+ rc = ioctl(fd, ION_IOC_CUSTOM, &custom_data);
+ if (rc < 0)
+ return -1;
+
+ return (int) phys_data.phys;
+}
+
+int smdk4x12_ion_msync(struct smdk4x12_camera *smdk4x12_camera, int fd,
+ int offset, int size)
+{
+ struct ion_custom_data custom_data;
+ struct ion_msync_data msync_data;
+ int rc;
+
+ memset(&msync_data, 0, sizeof(msync_data));
+ msync_data.dir = IMSYNC_SYNC_FOR_DEV | IMSYNC_DEV_TO_RW;
+ msync_data.fd_buffer = fd;
+ msync_data.offset = offset;
+ msync_data.size = size;
+
+ memset(&custom_data, 0, sizeof(custom_data));
+ custom_data.cmd = ION_EXYNOS_CUSTOM_MSYNC;
+ custom_data.arg = (unsigned long) &msync_data;
+
+ fd = smdk4x12_camera->ion_fd;
+ if (fd < 0)
+ return -1;
+
+ rc = ioctl(fd, ION_IOC_CUSTOM, &custom_data);
+ if (rc < 0)
+ return -1;
+
+ return 0;
+}
diff --git a/camera/smdk4x12_jpeg.c b/camera/smdk4x12_jpeg.c
new file mode 100644
index 0000000..88cdd71
--- /dev/null
+++ b/camera/smdk4x12_jpeg.c
@@ -0,0 +1,402 @@
+/*
+ * Copyright (C) 2013 Paul Kocialkowski <contact@paulk.fr>
+ *
+ * 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 <errno.h>
+#include <malloc.h>
+#include <poll.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>
+
+#define LOG_TAG "smdk4x12_jpeg"
+#include <utils/Log.h>
+
+#include "smdk4x12_camera.h"
+
+int smdk4x12_jpeg_start(struct smdk4x12_camera *smdk4x12_camera,
+ struct smdk4x12_jpeg *jpeg)
+{
+ struct jpeg_config config;
+ struct jpeg_buf *buffer_in;
+ struct jpeg_buf *buffer_out;
+ camera_memory_t *memory = NULL;
+#ifdef EXYNOS_ION
+ int memory_ion_fd = -1;
+#endif
+ int address;
+ int fd = -1;
+ int rc;
+
+ if (smdk4x12_camera == NULL || jpeg == NULL)
+ return -EINVAL;
+
+ ALOGD("%s()", __func__);
+
+ if (jpeg->enabled) {
+ ALOGE("Jpeg was already started");
+ return -1;
+ }
+
+ buffer_in = &jpeg->buffer_in;
+ buffer_out = &jpeg->buffer_out;
+
+#ifdef EXYNOS_ION
+ jpeg->memory_in_ion_fd = -1;
+ jpeg->memory_out_ion_fd = -1;
+#endif
+
+ fd = jpeghal_enc_init();
+ if (fd < 0) {
+ ALOGE("%s: Unable to init jpeg encoder", __func__);
+ goto error;
+ }
+
+ jpeg->fd = fd;
+
+ memset(&config, 0, sizeof(config));
+ config.mode = JPEG_ENCODE;
+ config.width = jpeg->width;
+ config.height = jpeg->height;
+ config.num_planes = 1;
+ config.pix.enc_fmt.in_fmt = jpeg->format;
+ config.pix.enc_fmt.out_fmt = V4L2_PIX_FMT_JPEG_420;
+
+ if (jpeg->quality >= 90)
+ config.enc_qual = QUALITY_LEVEL_1;
+ else if (jpeg->quality >= 80)
+ config.enc_qual = QUALITY_LEVEL_2;
+ else if (jpeg->quality >= 70)
+ config.enc_qual = QUALITY_LEVEL_3;
+ else
+ config.enc_qual = QUALITY_LEVEL_4;
+
+ rc = jpeghal_enc_setconfig(fd, &config);
+ if (rc < 0) {
+ ALOGE("%s: Unable to set jpeg config", __func__);
+ goto error;
+ }
+
+ rc = jpeghal_s_ctrl(fd, V4L2_CID_CACHEABLE, 1);
+ if (rc < 0) {
+ ALOGE("%s: Unable to set cacheable control", __func__);
+ goto error;
+ }
+
+ memset(buffer_in, 0, sizeof(struct jpeg_buf));
+ buffer_in->memory = V4L2_MEMORY_MMAP;
+ buffer_in->num_planes = 1;
+
+ memset(buffer_out, 0, sizeof(struct jpeg_buf));
+ buffer_out->memory = V4L2_MEMORY_MMAP;
+ buffer_out->num_planes = 1;
+
+ rc = jpeghal_set_inbuf(fd, buffer_in);
+ if (rc < 0) {
+#ifdef EXYNOS_ION
+ // Input
+
+ buffer_in->memory = V4L2_MEMORY_USERPTR;
+ buffer_in->length[0] = smdk4x12_camera_buffer_length(jpeg->width, jpeg->height, jpeg->format);
+
+ memory_ion_fd = smdk4x12_ion_alloc(smdk4x12_camera, buffer_in->length[0]);
+ if (memory_ion_fd < 0) {
+ ALOGE("%s: Unable to alloc input ION memory", __func__);
+ goto error;
+ }
+
+ address = smdk4x12_ion_phys(smdk4x12_camera, memory_ion_fd);
+
+ if (SMDK4x12_CAMERA_CALLBACK_DEFINED(request_memory)) {
+ memory = smdk4x12_camera->callbacks.request_memory(memory_ion_fd, buffer_in->length[0], 1, smdk4x12_camera->callbacks.user);
+ if (memory == NULL || memory->data == NULL || memory->data == MAP_FAILED) {
+ ALOGE("%s: Unable to request memory", __func__);
+ goto error;
+ }
+ } else {
+ ALOGE("%s: No memory request function!", __func__);
+ goto error;
+ }
+
+ jpeg->memory_in = memory;
+ jpeg->memory_in_pointer = memory->data;
+ jpeg->memory_in_ion_fd = memory_ion_fd;
+ buffer_in->start[0] = (void *) address;
+
+ rc = jpeghal_set_inbuf(fd, buffer_in);
+ if (rc < 0) {
+ ALOGE("%s: Unable to set input buffer", __func__);
+ goto error;
+ }
+
+ // Output
+
+ buffer_out->memory = V4L2_MEMORY_USERPTR;
+ buffer_out->length[0] = jpeg->width * jpeg->height * 4;
+
+ memory_ion_fd = smdk4x12_ion_alloc(smdk4x12_camera, buffer_out->length[0]);
+ if (memory_ion_fd < 0) {
+ ALOGE("%s: Unable to alloc output ION memory", __func__);
+ goto error;
+ }
+
+ address = smdk4x12_ion_phys(smdk4x12_camera, memory_ion_fd);
+
+ if (SMDK4x12_CAMERA_CALLBACK_DEFINED(request_memory)) {
+ memory = smdk4x12_camera->callbacks.request_memory(memory_ion_fd, buffer_out->length[0], 1, smdk4x12_camera->callbacks.user);
+ if (memory == NULL || memory->data == NULL || memory->data == MAP_FAILED) {
+ ALOGE("%s: Unable to request memory", __func__);
+ goto error;
+ }
+ } else {
+ ALOGE("%s: No memory request function!", __func__);
+ goto error;
+ }
+
+ jpeg->memory_out = memory;
+ jpeg->memory_out_pointer = memory->data;
+ jpeg->memory_out_ion_fd = memory_ion_fd;
+ buffer_out->start[0] = (void *) address;
+#else
+ ALOGE("%s: Unable to set input buffer", __func__);
+ goto error;
+#endif
+ } else {
+ jpeg->memory_in_pointer = buffer_in->start[0];
+ jpeg->memory_out_pointer = buffer_out->start[0];
+ }
+
+ rc = jpeghal_set_outbuf(fd, buffer_out);
+ if (rc < 0) {
+ ALOGE("%s: Unable to set output buffer", __func__);
+ goto error;
+ }
+
+ jpeg->enabled = 1;
+
+ rc = 0;
+ goto complete;
+
+error:
+ if (fd >= 0) {
+ // Avoid releasing unrequested mmap buffers
+
+ if (buffer_in->memory == 0)
+ buffer_in->memory = V4L2_MEMORY_USERPTR;
+
+ if (buffer_out->memory == 0)
+ buffer_out->memory = V4L2_MEMORY_USERPTR;
+
+ jpeghal_deinit(fd, buffer_in, buffer_out);
+ jpeg->fd = -1;
+ }
+
+ if (jpeg->memory_in != NULL && jpeg->memory_in->release != NULL) {
+ jpeg->memory_in->release(jpeg->memory_in);
+ jpeg->memory_in = NULL;
+#ifdef EXYNOS_ION
+ if (jpeg->memory_in_ion_fd >= 0) {
+ smdk4x12_ion_free(smdk4x12_camera, jpeg->memory_in_ion_fd);
+ jpeg->memory_in_ion_fd = -1;
+ }
+#endif
+ }
+
+ if (jpeg->memory_out != NULL && jpeg->memory_out->release != NULL) {
+ jpeg->memory_out->release(jpeg->memory_out);
+ jpeg->memory_out = NULL;
+
+#ifdef EXYNOS_ION
+ if (jpeg->memory_out_ion_fd >= 0) {
+ smdk4x12_ion_free(smdk4x12_camera, jpeg->memory_out_ion_fd);
+ jpeg->memory_out_ion_fd = -1;
+ }
+#endif
+ }
+
+ rc = -1;
+
+complete:
+ return rc;
+}
+
+void smdk4x12_jpeg_stop(struct smdk4x12_camera *smdk4x12_camera,
+ struct smdk4x12_jpeg *jpeg)
+{
+ struct jpeg_buf *buffer_in;
+ struct jpeg_buf *buffer_out;
+ int fd = -1;
+ int rc;
+
+ if (smdk4x12_camera == NULL || jpeg == NULL)
+ return;
+
+ ALOGD("%s()", __func__);
+
+ if (!jpeg->enabled) {
+ ALOGE("Jpeg was already stopped");
+ return;
+ }
+
+ buffer_in = &jpeg->buffer_in;
+ buffer_out = &jpeg->buffer_out;
+
+ fd = jpeg->fd;
+
+ if (fd >= 0) {
+ jpeghal_deinit(fd, buffer_in, buffer_out);
+ jpeg->fd = -1;
+ }
+
+ if (jpeg->memory_in != NULL && jpeg->memory_in->release != NULL) {
+ jpeg->memory_in->release(jpeg->memory_in);
+ jpeg->memory_in = NULL;
+#ifdef EXYNOS_ION
+ if (jpeg->memory_in_ion_fd >= 0) {
+ smdk4x12_ion_free(smdk4x12_camera, jpeg->memory_in_ion_fd);
+ jpeg->memory_in_ion_fd = -1;
+ }
+#endif
+ }
+
+ if (jpeg->memory_out != NULL && jpeg->memory_out->release != NULL) {
+ jpeg->memory_out->release(jpeg->memory_out);
+ jpeg->memory_out = NULL;
+
+#ifdef EXYNOS_ION
+ if (jpeg->memory_out_ion_fd >= 0) {
+ smdk4x12_ion_free(smdk4x12_camera, jpeg->memory_out_ion_fd);
+ jpeg->memory_out_ion_fd = -1;
+ }
+#endif
+ }
+
+ jpeg->enabled = 0;
+}
+
+int smdk4x12_jpeg(struct smdk4x12_camera *smdk4x12_camera, struct smdk4x12_jpeg *jpeg)
+{
+ struct jpeg_buf *buffer_in;
+ struct jpeg_buf *buffer_out;
+ int memory_size;
+ int fd = -1;
+ int rc;
+
+ if (smdk4x12_camera == NULL || jpeg == NULL)
+ return -EINVAL;
+
+ ALOGD("%s()", __func__);
+
+ if (!jpeg->enabled) {
+ ALOGE("Jpeg was not started");
+ return -1;
+ }
+
+ buffer_in = &jpeg->buffer_in;
+ buffer_out = &jpeg->buffer_out;
+
+ fd = jpeg->fd;
+ if (fd < 0) {
+ ALOGE("%s: Invalid jpeg fd", __func__);
+ goto error;
+ }
+
+#ifdef EXYNOS_ION
+ if (jpeg->memory_in != NULL && jpeg->memory_in_ion_fd >= 0) {
+ rc = smdk4x12_ion_msync(smdk4x12_camera, jpeg->memory_in_ion_fd, 0, buffer_in->length[0]);
+ if (rc < 0) {
+ ALOGE("%s: Unable to sync ION memory", __func__);
+ goto error;
+ }
+ }
+#endif
+
+ rc = jpeghal_enc_exe(fd, buffer_in, buffer_out);
+ if (rc < 0) {
+ ALOGE("%s: Unable to encode jpeg", __func__);
+ goto error;
+ }
+
+ memory_size = jpeghal_g_ctrl(fd, V4L2_CID_CAM_JPEG_ENCODEDSIZE);
+ if (memory_size <= 0) {
+ ALOGE("%s: Unable to get jpeg size", __func__);
+ goto error;
+ }
+
+ jpeg->memory_out_size = memory_size;
+
+#ifdef EXYNOS_ION
+ if (jpeg->memory_out != NULL && jpeg->memory_out_ion_fd >= 0) {
+ rc = smdk4x12_ion_msync(smdk4x12_camera, jpeg->memory_out_ion_fd, 0, memory_size);
+ if (rc < 0) {
+ ALOGE("%s: Unable to sync ION memory", __func__);
+ goto error;
+ }
+ }
+#endif
+
+ rc = 0;
+ goto complete;
+
+error:
+ if (fd >= 0) {
+ // Avoid releasing unrequested mmap buffers
+
+ if (buffer_in->memory == 0)
+ buffer_in->memory = V4L2_MEMORY_USERPTR;
+
+ if (buffer_out->memory == 0)
+ buffer_out->memory = V4L2_MEMORY_USERPTR;
+
+ jpeghal_deinit(fd, buffer_in, buffer_out);
+ jpeg->fd = -1;
+ }
+
+ if (jpeg->memory_in != NULL && jpeg->memory_in->release != NULL) {
+ jpeg->memory_in->release(jpeg->memory_in);
+ jpeg->memory_in = NULL;
+
+#ifdef EXYNOS_ION
+ if (jpeg->memory_in_ion_fd >= 0) {
+ smdk4x12_ion_free(smdk4x12_camera, jpeg->memory_in_ion_fd);
+ jpeg->memory_in_ion_fd = -1;
+ }
+#endif
+ }
+
+ if (jpeg->memory_out != NULL && jpeg->memory_out->release != NULL) {
+ jpeg->memory_out->release(jpeg->memory_out);
+ jpeg->memory_out = NULL;
+
+#ifdef EXYNOS_ION
+ if (jpeg->memory_out_ion_fd >= 0) {
+ smdk4x12_ion_free(smdk4x12_camera, jpeg->memory_out_ion_fd);
+ jpeg->memory_out_ion_fd = -1;
+ }
+#endif
+ }
+
+ rc = -1;
+
+complete:
+ return rc;
+}
diff --git a/camera/smdk4x12_param.c b/camera/smdk4x12_param.c
new file mode 100644
index 0000000..b39ec7e
--- /dev/null
+++ b/camera/smdk4x12_param.c
@@ -0,0 +1,492 @@
+/*
+ * Copyright (C) 2013 Paul Kocialkowski <contact@paulk.fr>
+ *
+ * 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 <errno.h>
+#include <malloc.h>
+#include <ctype.h>
+
+#define LOG_TAG "smdk4x12_param"
+#include <utils/Log.h>
+
+#include "smdk4x12_camera.h"
+
+int smdk4x12_param_register(struct smdk4x12_camera *smdk4x12_camera, char *key,
+ union smdk4x12_param_data data, enum smdk4x12_param_type type)
+{
+ struct list_head *list_end;
+ struct list_head *list;
+ struct smdk4x12_param *param;
+
+ if (smdk4x12_camera == NULL || key == NULL)
+ return -EINVAL;
+
+ param = (struct smdk4x12_param *) calloc(1, sizeof(struct smdk4x12_param));
+ if (param == NULL)
+ return -ENOMEM;
+
+ param->key = strdup(key);
+ switch (type) {
+ case EXYNOS_PARAM_INT:
+ param->data.integer = data.integer;
+ break;
+ case EXYNOS_PARAM_FLOAT:
+ param->data.floating = data.floating;
+ break;
+ case EXYNOS_PARAM_STRING:
+ param->data.string = strdup(data.string);
+ break;
+ default:
+ ALOGE("%s: Invalid type", __func__);
+ goto error;
+ }
+ param->type = type;
+
+ list_end = (struct list_head *) smdk4x12_camera->params;
+ while (list_end != NULL && list_end->next != NULL)
+ list_end = list_end->next;
+
+ list = (struct list_head *) param;
+ list_head_insert(list, list_end, NULL);
+
+ if (smdk4x12_camera->params == NULL)
+ smdk4x12_camera->params = param;
+
+ return 0;
+
+error:
+ if (param != NULL) {
+ if (param->key != NULL)
+ free(param->key);
+
+ free(param);
+ }
+
+ return -1;
+}
+
+void smdk4x12_param_unregister(struct smdk4x12_camera *smdk4x12_camera,
+ struct smdk4x12_param *param)
+{
+ struct list_head *list;
+
+ if (smdk4x12_camera == NULL || param == NULL)
+ return;
+
+ list = (struct list_head *) smdk4x12_camera->params;
+ while (list != NULL) {
+ if ((void *) list == (void *) param) {
+ list_head_remove(list);
+
+ if ((void *) list == (void *) smdk4x12_camera->params)
+ smdk4x12_camera->params = (struct smdk4x12_param *) list->next;
+
+ if (param->type == EXYNOS_PARAM_STRING && param->data.string != NULL)
+ free(param->data.string);
+
+ memset(param, 0, sizeof(struct smdk4x12_param));
+ free(param);
+
+ break;
+ }
+
+list_continue:
+ list = list->next;
+ }
+}
+
+struct smdk4x12_param *smdk4x12_param_find_key(struct smdk4x12_camera *smdk4x12_camera,
+ char *key)
+{
+ struct smdk4x12_param *param;
+ struct list_head *list;
+
+ if (smdk4x12_camera == NULL || key == NULL)
+ return NULL;
+
+ list = (struct list_head *) smdk4x12_camera->params;
+ while (list != NULL) {
+ param = (struct smdk4x12_param *) list;
+ if (param->key == NULL)
+ goto list_continue;
+
+ if (strcmp(param->key, key) == 0)
+ return param;
+
+list_continue:
+ list = list->next;
+ }
+
+ return NULL;
+}
+
+int smdk4x12_param_data_set(struct smdk4x12_camera *smdk4x12_camera, char *key,
+ union smdk4x12_param_data data, enum smdk4x12_param_type type)
+{
+ struct smdk4x12_param *param;
+
+ if (smdk4x12_camera == NULL || key == NULL)
+ return -EINVAL;
+
+ if (strchr(key, '=') || strchr(key, ';'))
+ return -EINVAL;
+
+ if (type == EXYNOS_PARAM_STRING && data.string != NULL &&
+ (strchr(data.string, '=') || strchr(data.string, ';')))
+ return -EINVAL;
+
+ param = smdk4x12_param_find_key(smdk4x12_camera, key);
+ if (param == NULL) {
+ // The key isn't in the list yet
+ smdk4x12_param_register(smdk4x12_camera, key, data, type);
+ return 0;
+ }
+
+ if (param->type != type)
+ ALOGE("%s: Mismatching types for key %s", __func__, key);
+
+ if (param->type == EXYNOS_PARAM_STRING && param->data.string != NULL)
+ free(param->data.string);
+
+ switch (type) {
+ case EXYNOS_PARAM_INT:
+ param->data.integer = data.integer;
+ break;
+ case EXYNOS_PARAM_FLOAT:
+ param->data.floating = data.floating;
+ break;
+ case EXYNOS_PARAM_STRING:
+ param->data.string = strdup(data.string);
+ break;
+ default:
+ ALOGE("%s: Invalid type", __func__);
+ return -1;
+ }
+ param->type = type;
+
+ return 0;
+}
+
+int smdk4x12_param_data_get(struct smdk4x12_camera *smdk4x12_camera, char *key,
+ union smdk4x12_param_data *data, enum smdk4x12_param_type type)
+{
+ struct smdk4x12_param *param;
+
+ if (smdk4x12_camera == NULL || key == NULL || data == NULL)
+ return -EINVAL;
+
+ param = smdk4x12_param_find_key(smdk4x12_camera, key);
+ if (param == NULL || param->type != type)
+ return -1;
+
+ memcpy(data, &param->data, sizeof(param->data));
+
+ return 0;
+}
+
+int smdk4x12_param_int_get(struct smdk4x12_camera *smdk4x12_camera,
+ char *key)
+{
+ union smdk4x12_param_data data;
+ int rc;
+
+ if (smdk4x12_camera == NULL || key == NULL)
+ return -EINVAL;
+
+ rc = smdk4x12_param_data_get(smdk4x12_camera, key, &data, EXYNOS_PARAM_INT);
+ if (rc < 0) {
+ ALOGE("%s: Unable to get data for key %s", __func__, key);
+ return -1;
+ }
+
+ return data.integer;
+}
+
+float smdk4x12_param_float_get(struct smdk4x12_camera *smdk4x12_camera,
+ char *key)
+{
+ union smdk4x12_param_data data;
+ int rc;
+
+ if (smdk4x12_camera == NULL || key == NULL)
+ return -EINVAL;
+
+ rc = smdk4x12_param_data_get(smdk4x12_camera, key, &data, EXYNOS_PARAM_FLOAT);
+ if (rc < 0) {
+ ALOGE("%s: Unable to get data for key %s", __func__, key);
+ return -1;
+ }
+
+ return data.floating;
+}
+
+char *smdk4x12_param_string_get(struct smdk4x12_camera *smdk4x12_camera,
+ char *key)
+{
+ union smdk4x12_param_data data;
+ int rc;
+
+ if (smdk4x12_camera == NULL || key == NULL)
+ return NULL;
+
+ rc = smdk4x12_param_data_get(smdk4x12_camera, key, &data, EXYNOS_PARAM_STRING);
+ if (rc < 0) {
+ ALOGE("%s: Unable to get data for key %s", __func__, key);
+ return NULL;
+ }
+
+ return data.string;
+}
+
+int smdk4x12_param_int_set(struct smdk4x12_camera *smdk4x12_camera,
+ char *key, int integer)
+{
+ union smdk4x12_param_data data;
+ int rc;
+
+ if (smdk4x12_camera == NULL || key == NULL)
+ return -EINVAL;
+
+ data.integer = integer;
+
+ rc = smdk4x12_param_data_set(smdk4x12_camera, key, data, EXYNOS_PARAM_INT);
+ if (rc < 0) {
+ ALOGE("%s: Unable to set data for key %s", __func__, key);
+ return -1;
+ }
+
+ return 0;
+}
+
+int smdk4x12_param_float_set(struct smdk4x12_camera *smdk4x12_camera,
+ char *key, float floating)
+{
+ union smdk4x12_param_data data;
+ int rc;
+
+ if (smdk4x12_camera == NULL || key == NULL)
+ return -EINVAL;
+
+ data.floating = floating;
+
+ rc = smdk4x12_param_data_set(smdk4x12_camera, key, data, EXYNOS_PARAM_FLOAT);
+ if (rc < 0) {
+ ALOGE("%s: Unable to set data for key %s", __func__, key);
+ return -1;
+ }
+
+ return 0;
+}
+
+int smdk4x12_param_string_set(struct smdk4x12_camera *smdk4x12_camera,
+ char *key, char *string)
+{
+ union smdk4x12_param_data data;
+ int rc;
+
+ if (smdk4x12_camera == NULL || key == NULL || string == NULL)
+ return -EINVAL;
+
+ data.string = string;
+
+ rc = smdk4x12_param_data_set(smdk4x12_camera, key, data, EXYNOS_PARAM_STRING);
+ if (rc < 0) {
+ ALOGE("%s: Unable to set data for key %s", __func__, key);
+ return -1;
+ }
+
+ return 0;
+}
+
+char *smdk4x12_params_string_get(struct smdk4x12_camera *smdk4x12_camera)
+{
+ struct smdk4x12_param *param;
+ struct list_head *list;
+ char *string = NULL;
+ char *s = NULL;
+ int length = 0;
+ int l = 0;
+
+ if (smdk4x12_camera == NULL)
+ return NULL;
+
+ list = (struct list_head *) smdk4x12_camera->params;
+ while (list != NULL) {
+ param = (struct smdk4x12_param *) list;
+ if (param->key == NULL)
+ goto list_continue_length;
+
+ length += strlen(param->key);
+ length++;
+
+ switch (param->type) {
+ case EXYNOS_PARAM_INT:
+ case EXYNOS_PARAM_FLOAT:
+ length += 16;
+ break;
+ case EXYNOS_PARAM_STRING:
+ length += strlen(param->data.string);
+ break;
+ default:
+ ALOGE("%s: Invalid type", __func__);
+ return NULL;
+ }
+
+ length++;
+
+list_continue_length:
+ list = list->next;
+ }
+
+ if (length == 0)
+ return NULL;
+
+ string = calloc(1, length);
+ s = string;
+
+ list = (struct list_head *) smdk4x12_camera->params;
+ while (list != NULL) {
+ param = (struct smdk4x12_param *) list;
+ if (param->key == NULL)
+ goto list_continue;
+
+ l = sprintf(s, "%s=", param->key);
+ s += l;
+
+ switch (param->type) {
+ case EXYNOS_PARAM_INT:
+ l = snprintf(s, 16, "%d", param->data.integer);
+ s += l;
+ break;
+ case EXYNOS_PARAM_FLOAT:
+ l = snprintf(s, 16, "%g", param->data.floating);
+ s += l;
+ break;
+ case EXYNOS_PARAM_STRING:
+ l = sprintf(s, "%s", param->data.string);
+ s += l;
+ break;
+ default:
+ ALOGE("%s: Invalid type", __func__);
+ return NULL;
+ }
+
+ if (list->next != NULL) {
+ *s = ';';
+ s++;
+ } else {
+ *s = '\0';
+ break;
+ }
+
+list_continue:
+ list = list->next;
+ }
+
+ return string;
+}
+
+int smdk4x12_params_string_set(struct smdk4x12_camera *smdk4x12_camera, char *string)
+{
+ union smdk4x12_param_data data;
+ enum smdk4x12_param_type type;
+
+ char *d = NULL;
+ char *s = NULL;
+ char *k = NULL;
+ char *v = NULL;
+
+ char *key;
+ char *value;
+
+ int rc;
+ int i;
+
+ if (smdk4x12_camera == NULL || string == NULL)
+ return -1;
+
+ d = strdup(string);
+ s = d;
+
+ while (1) {
+ k = strchr(s, '=');
+ if (k == NULL)
+ break;
+ *k = '\0';
+ key = s;
+
+ v = strchr(k+1, ';');
+ if (v != NULL)
+ *v = '\0';
+ value = k+1;
+
+ k = value;
+ if (isdigit(k[0]) || k[0] == '-') {
+ type = EXYNOS_PARAM_INT;
+
+ for (i=1 ; k[i] != '\0' ; i++) {
+ if (k[i] == '.') {
+ type = EXYNOS_PARAM_FLOAT;
+ } else if (!isdigit(k[i])) {
+ type = EXYNOS_PARAM_STRING;
+ break;
+ }
+ }
+ } else {
+ type = EXYNOS_PARAM_STRING;
+ }
+
+ switch (type) {
+ case EXYNOS_PARAM_INT:
+ data.integer = atoi(value);
+ break;
+ case EXYNOS_PARAM_FLOAT:
+ data.floating = atof(value);
+ break;
+ case EXYNOS_PARAM_STRING:
+ data.string = value;
+ break;
+ default:
+ ALOGE("%s: Invalid type", __func__);
+ goto error;
+ }
+
+ rc = smdk4x12_param_data_set(smdk4x12_camera, key, data, type);
+ if (rc < 0) {
+ ALOGE("%s: Unable to set data for key %s", __func__, key);
+ goto error;
+ }
+
+ if (v == NULL)
+ break;
+
+ s = v+1;
+ }
+
+ if (d != NULL)
+ free(d);
+
+ return 0;
+
+error:
+ if (d != NULL)
+ free(d);
+
+ return -1;
+}
diff --git a/camera/smdk4x12_utils.c b/camera/smdk4x12_utils.c
new file mode 100644
index 0000000..7f8fc40
--- /dev/null
+++ b/camera/smdk4x12_utils.c
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2013 Paul Kocialkowski <contact@paulk.fr>
+ *
+ * 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 <errno.h>
+#include <malloc.h>
+#include <ctype.h>
+
+#define LOG_TAG "smdk4x12_utils"
+#include <utils/Log.h>
+
+#include "smdk4x12_camera.h"
+
+int list_head_insert(struct list_head *list, struct list_head *prev,
+ struct list_head *next)
+{
+ if (list == NULL)
+ return -EINVAL;
+
+ list->prev = prev;
+ list->next = next;
+
+ if(prev != NULL)
+ prev->next = list;
+ if(next != NULL)
+ next->prev = list;
+
+ return 0;
+}
+
+void list_head_remove(struct list_head *list)
+{
+ if(list == NULL)
+ return;
+
+ if(list->next != NULL)
+ list->next->prev = list->prev;
+ if(list->prev != NULL)
+ list->prev->next = list->next;
+}
+
+
+int smdk4x12_camera_buffer_length(int width, int height, int format)
+{
+ float bpp;
+ int buffer_length;
+
+ switch (format) {
+ case V4L2_PIX_FMT_RGB32:
+ bpp = 4.0f;
+ buffer_length = (int) ((float) width * (float) height * bpp);
+ break;
+ case V4L2_PIX_FMT_RGB565:
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_VYUY:
+ case V4L2_PIX_FMT_YVYU:
+ case V4L2_PIX_FMT_YUV422P:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ bpp = 2.0f;
+ buffer_length = (int) ((float) width * (float) height * bpp);
+ break;
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV12T:
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_YVU420:
+ bpp = 1.5f;
+ buffer_length = SMDK4x12_CAMERA_ALIGN(width * height);
+ buffer_length += SMDK4x12_CAMERA_ALIGN(width * height / 2);
+ break;
+ case V4L2_PIX_FMT_NV21:
+ bpp = 1.5f;
+ buffer_length = (int) ((float) width * (float) height * bpp);
+ break;
+ case V4L2_PIX_FMT_JPEG:
+ case V4L2_PIX_FMT_INTERLEAVED:
+ default:
+ buffer_length = -1;
+ bpp = 0;
+ break;
+ }
+
+ return buffer_length;
+}
+
+void smdk4x12_camera_yuv_planes(int width, int height, int format, int address, int *address_y, int *address_cb, int *address_cr)
+{
+ switch (format) {
+ case V4L2_PIX_FMT_RGB32:
+ case V4L2_PIX_FMT_RGB565:
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_VYUY:
+ case V4L2_PIX_FMT_YVYU:
+ if (address_y != NULL)
+ *address_y = address;
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ if (address_y != NULL)
+ *address_y = address;
+
+ address += SMDK4x12_CAMERA_ALIGN(width * height);
+
+ if (address_cb != NULL)
+ *address_cb = address;
+
+ address += SMDK4x12_CAMERA_ALIGN(width * height / 4);
+
+ if (address_cr != NULL)
+ *address_cr = address;
+ break;
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV12T:
+ if (address_y != NULL)
+ *address_y = address;
+
+ address += SMDK4x12_CAMERA_ALIGN(width * height);
+
+ if (address_cb != NULL)
+ *address_cb = address;
+
+ if (address_cr != NULL)
+ *address_cr = address;
+ break;
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ default:
+ if (address_y != NULL)
+ *address_y = address;
+
+ address += width * height;
+
+ if (address_cb != NULL)
+ *address_cb = address;
+
+ if (address_cr != NULL)
+ *address_cr = address;
+ break;
+ }
+}
diff --git a/camera/smdk4x12_v4l2.c b/camera/smdk4x12_v4l2.c
new file mode 100644
index 0000000..e8b130e
--- /dev/null
+++ b/camera/smdk4x12_v4l2.c
@@ -0,0 +1,739 @@
+/*
+ * Copyright (C) 2013 Paul Kocialkowski <contact@paulk.fr>
+ *
+ * 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 <errno.h>
+#include <malloc.h>
+#include <poll.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>
+
+#define LOG_TAG "smdk4x12_v4l2"
+#include <utils/Log.h>
+
+#include "smdk4x12_camera.h"
+
+int smdk4x12_v4l2_init(struct smdk4x12_camera *smdk4x12_camera)
+{
+ int i;
+
+ for (i = 0; i < SMDK4x12_CAMERA_MAX_V4L2_NODES_COUNT; i++)
+ smdk4x12_camera->v4l2_fds[i] = -1;
+
+ return 0;
+}
+
+int smdk4x12_v4l2_index(struct smdk4x12_camera *smdk4x12_camera, int smdk4x12_v4l2_id)
+{
+ int index;
+ int i;
+
+ if (smdk4x12_camera == NULL || smdk4x12_camera->config == NULL ||
+ smdk4x12_camera->config->v4l2_nodes == NULL)
+ return -EINVAL;
+
+ if (smdk4x12_v4l2_id > smdk4x12_camera->config->v4l2_nodes_count)
+ return -1;
+
+ index = -1;
+ for (i = 0; i < smdk4x12_camera->config->v4l2_nodes_count; i++) {
+ if (smdk4x12_camera->config->v4l2_nodes[i].id == smdk4x12_v4l2_id &&
+ smdk4x12_camera->config->v4l2_nodes[i].node != NULL) {
+ index = i;
+ }
+ }
+
+ return index;
+}
+
+int smdk4x12_v4l2_fd(struct smdk4x12_camera *smdk4x12_camera, int smdk4x12_v4l2_id)
+{
+ int index;
+
+ if (smdk4x12_camera == NULL)
+ return -EINVAL;
+
+ index = smdk4x12_v4l2_index(smdk4x12_camera, smdk4x12_v4l2_id);
+ if (index < 0) {
+ ALOGE("%s: Unable to get v4l2 index for id %d", __func__, smdk4x12_v4l2_id);
+ return -1;
+ }
+
+ return smdk4x12_camera->v4l2_fds[index];
+}
+
+int smdk4x12_v4l2_open(struct smdk4x12_camera *smdk4x12_camera, int smdk4x12_v4l2_id)
+{
+ char *node;
+ int index;
+ int fd;
+
+ if (smdk4x12_camera == NULL || smdk4x12_camera->config == NULL ||
+ smdk4x12_camera->config->v4l2_nodes == NULL)
+ return -EINVAL;
+
+ index = smdk4x12_v4l2_index(smdk4x12_camera, smdk4x12_v4l2_id);
+ if (index < 0) {
+ ALOGE("%s: Unable to get v4l2 node for id %d", __func__, smdk4x12_v4l2_id);
+ return -1;
+ }
+
+ node = smdk4x12_camera->config->v4l2_nodes[index].node;
+ fd = open(node, O_RDWR);
+ if (fd < 0) {
+ ALOGE("%s: Unable to open v4l2 node for id %d", __func__, smdk4x12_v4l2_id);
+ return -1;
+ }
+
+ smdk4x12_camera->v4l2_fds[index] = fd;
+
+ return 0;
+}
+
+void smdk4x12_v4l2_close(struct smdk4x12_camera *smdk4x12_camera, int smdk4x12_v4l2_id)
+{
+ int index;
+
+ if (smdk4x12_camera == NULL || smdk4x12_camera->config == NULL ||
+ smdk4x12_camera->config->v4l2_nodes == NULL)
+ return;
+
+ index = smdk4x12_v4l2_index(smdk4x12_camera, smdk4x12_v4l2_id);
+ if (index < 0) {
+ ALOGE("%s: Unable to get v4l2 node for id %d", __func__, smdk4x12_v4l2_id);
+ return;
+ }
+
+ if (smdk4x12_camera->v4l2_fds[index] >= 0)
+ close(smdk4x12_camera->v4l2_fds[index]);
+
+ smdk4x12_camera->v4l2_fds[index] = -1;
+}
+
+int smdk4x12_v4l2_ioctl(struct smdk4x12_camera *smdk4x12_camera, int smdk4x12_v4l2_id,
+ int request, void *data)
+{
+ int fd;
+
+ if (smdk4x12_camera == NULL)
+ return -EINVAL;
+
+ fd = smdk4x12_v4l2_fd(smdk4x12_camera, smdk4x12_v4l2_id);
+ if (fd < 0) {
+ ALOGE("%s: Unable to get v4l2 fd for id %d", __func__, smdk4x12_v4l2_id);
+ return -1;
+ }
+
+ return ioctl(fd, request, data);
+}
+
+int smdk4x12_v4l2_poll(struct smdk4x12_camera *smdk4x12_camera, int smdk4x12_v4l2_id)
+{
+ struct pollfd events;
+ int fd;
+ int rc;
+
+ if (smdk4x12_camera == NULL)
+ return -EINVAL;
+
+ fd = smdk4x12_v4l2_fd(smdk4x12_camera, smdk4x12_v4l2_id);
+ if (fd < 0) {
+ ALOGE("%s: Unable to get v4l2 fd for id %d", __func__, smdk4x12_v4l2_id);
+ return -1;
+ }
+
+ memset(&events, 0, sizeof(events));
+ events.fd = fd;
+ events.events = POLLIN | POLLERR;
+
+ rc = poll(&events, 1, 1000);
+ if (rc < 0 || events.revents & POLLERR)
+ return -1;
+
+ return rc;
+}
+
+int smdk4x12_v4l2_qbuf(struct smdk4x12_camera *smdk4x12_camera, int smdk4x12_v4l2_id,
+ int type, int memory, int index, unsigned long userptr)
+{
+ struct v4l2_buffer buffer;
+ int rc;
+
+ if (smdk4x12_camera == NULL || index < 0)
+ return -EINVAL;
+
+ memset(&buffer, 0, sizeof(buffer));
+ buffer.type = type;
+ buffer.memory = memory;
+ buffer.index = index;
+
+ if (userptr)
+ buffer.m.userptr = userptr;
+
+ rc = smdk4x12_v4l2_ioctl(smdk4x12_camera, smdk4x12_v4l2_id, VIDIOC_QBUF, &buffer);
+ return rc;
+}
+
+int smdk4x12_v4l2_qbuf_cap(struct smdk4x12_camera *smdk4x12_camera, int smdk4x12_v4l2_id,
+ int index)
+{
+ return smdk4x12_v4l2_qbuf(smdk4x12_camera, smdk4x12_v4l2_id, V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_MEMORY_MMAP, index, 0);
+}
+
+int smdk4x12_v4l2_qbuf_out(struct smdk4x12_camera *smdk4x12_camera, int smdk4x12_v4l2_id,
+ int index, unsigned long userptr)
+{
+ return smdk4x12_v4l2_qbuf(smdk4x12_camera, smdk4x12_v4l2_id, V4L2_BUF_TYPE_VIDEO_OUTPUT,
+ V4L2_MEMORY_USERPTR, index, userptr);
+}
+
+int smdk4x12_v4l2_dqbuf(struct smdk4x12_camera *smdk4x12_camera, int smdk4x12_v4l2_id,
+ int type, int memory)
+{
+ struct v4l2_buffer buffer;
+ int rc;
+
+ if (smdk4x12_camera == NULL)
+ return -EINVAL;
+
+ memset(&buffer, 0, sizeof(buffer));
+ buffer.type = type;
+ buffer.memory = memory;
+
+ rc = smdk4x12_v4l2_ioctl(smdk4x12_camera, smdk4x12_v4l2_id, VIDIOC_DQBUF, &buffer);
+ if (rc < 0)
+ return rc;
+
+ return buffer.index;
+}
+
+int smdk4x12_v4l2_dqbuf_cap(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id)
+{
+ return smdk4x12_v4l2_dqbuf(smdk4x12_camera, smdk4x12_v4l2_id, V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_MEMORY_MMAP);
+}
+
+int smdk4x12_v4l2_dqbuf_out(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id)
+{
+ return smdk4x12_v4l2_dqbuf(smdk4x12_camera, smdk4x12_v4l2_id, V4L2_BUF_TYPE_VIDEO_OUTPUT,
+ V4L2_MEMORY_USERPTR);
+}
+
+int smdk4x12_v4l2_reqbufs(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id, int type, int memory, int count)
+{
+ struct v4l2_requestbuffers requestbuffers;
+ int rc;
+
+ if (smdk4x12_camera == NULL || count < 0)
+ return -EINVAL;
+
+ requestbuffers.type = type;
+ requestbuffers.count = count;
+ requestbuffers.memory = memory;
+
+ rc = smdk4x12_v4l2_ioctl(smdk4x12_camera, smdk4x12_v4l2_id, VIDIOC_REQBUFS, &requestbuffers);
+ if (rc < 0)
+ return rc;
+
+ return requestbuffers.count;
+}
+
+int smdk4x12_v4l2_reqbufs_cap(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id, int count)
+{
+ return smdk4x12_v4l2_reqbufs(smdk4x12_camera, smdk4x12_v4l2_id, V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_MEMORY_MMAP, count);
+}
+
+int smdk4x12_v4l2_reqbufs_out(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id, int count)
+{
+ return smdk4x12_v4l2_reqbufs(smdk4x12_camera, smdk4x12_v4l2_id, V4L2_BUF_TYPE_VIDEO_OUTPUT,
+ V4L2_MEMORY_USERPTR, count);
+}
+
+int smdk4x12_v4l2_querybuf(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id, int type, int memory, int index)
+{
+ struct v4l2_buffer buffer;
+ int rc;
+
+ if (smdk4x12_camera == NULL)
+ return -EINVAL;
+
+ memset(&buffer, 0, sizeof(buffer));
+ buffer.type = type;
+ buffer.memory = memory;
+ buffer.index = index;
+
+ rc = smdk4x12_v4l2_ioctl(smdk4x12_camera, smdk4x12_v4l2_id, VIDIOC_QUERYBUF, &buffer);
+ if (rc < 0)
+ return rc;
+
+ return buffer.length;
+}
+
+int smdk4x12_v4l2_querybuf_cap(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id, int index)
+{
+ return smdk4x12_v4l2_querybuf(smdk4x12_camera, smdk4x12_v4l2_id, V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_MEMORY_MMAP, index);
+}
+
+int smdk4x12_v4l2_querybuf_out(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id, int index)
+{
+ return smdk4x12_v4l2_querybuf(smdk4x12_camera, smdk4x12_v4l2_id, V4L2_BUF_TYPE_VIDEO_OUTPUT,
+ V4L2_MEMORY_USERPTR, index);
+}
+
+int smdk4x12_v4l2_querycap(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id, int flags)
+{
+ struct v4l2_capability cap;
+ int rc;
+
+ if (smdk4x12_camera == NULL)
+ return -EINVAL;
+
+ rc = smdk4x12_v4l2_ioctl(smdk4x12_camera, smdk4x12_v4l2_id, VIDIOC_QUERYCAP, &cap);
+ if (rc < 0)
+ return rc;
+
+ if (!(cap.capabilities & flags))
+ return -1;
+
+ return 0;
+}
+
+int smdk4x12_v4l2_querycap_cap(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id)
+{
+ return smdk4x12_v4l2_querycap(smdk4x12_camera, smdk4x12_v4l2_id, V4L2_CAP_VIDEO_CAPTURE);
+}
+
+int smdk4x12_v4l2_querycap_out(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id)
+{
+ return smdk4x12_v4l2_querycap(smdk4x12_camera, smdk4x12_v4l2_id, V4L2_CAP_VIDEO_OUTPUT);
+}
+
+int smdk4x12_v4l2_streamon(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id, int type)
+{
+ enum v4l2_buf_type buf_type;
+ int rc;
+
+ if (smdk4x12_camera == NULL)
+ return -EINVAL;
+
+ buf_type = type;
+
+ rc = smdk4x12_v4l2_ioctl(smdk4x12_camera, smdk4x12_v4l2_id, VIDIOC_STREAMON, &buf_type);
+ return rc;
+}
+
+int smdk4x12_v4l2_streamon_cap(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id)
+{
+ return smdk4x12_v4l2_streamon(smdk4x12_camera, smdk4x12_v4l2_id, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+}
+
+int smdk4x12_v4l2_streamon_out(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id)
+{
+ return smdk4x12_v4l2_streamon(smdk4x12_camera, smdk4x12_v4l2_id, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+}
+
+int smdk4x12_v4l2_streamoff(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id, int type)
+{
+ enum v4l2_buf_type buf_type;
+ int rc;
+
+ if (smdk4x12_camera == NULL)
+ return -EINVAL;
+
+ buf_type = type;
+
+ rc = smdk4x12_v4l2_ioctl(smdk4x12_camera, smdk4x12_v4l2_id, VIDIOC_STREAMOFF, &buf_type);
+ return rc;
+}
+
+int smdk4x12_v4l2_streamoff_cap(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id)
+{
+ return smdk4x12_v4l2_streamoff(smdk4x12_camera, smdk4x12_v4l2_id, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+}
+
+int smdk4x12_v4l2_streamoff_out(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id)
+{
+ return smdk4x12_v4l2_streamoff(smdk4x12_camera, smdk4x12_v4l2_id, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+}
+
+int smdk4x12_v4l2_g_fmt(struct smdk4x12_camera *smdk4x12_camera, int smdk4x12_v4l2_id,
+ int type, int *width, int *height, int *fmt)
+{
+ struct v4l2_format format;
+ int rc;
+
+ if (smdk4x12_camera == NULL)
+ return -EINVAL;
+
+ format.type = type;
+ format.fmt.pix.field = V4L2_FIELD_NONE;
+
+ rc = smdk4x12_v4l2_ioctl(smdk4x12_camera, smdk4x12_v4l2_id, VIDIOC_G_FMT, &format);
+ if (rc < 0)
+ return rc;
+
+ if (width != NULL)
+ *width = format.fmt.pix.width;
+ if (height != NULL)
+ *height = format.fmt.pix.height;
+ if (fmt != NULL)
+ *fmt = format.fmt.pix.pixelformat;
+
+ return 0;
+}
+
+int smdk4x12_v4l2_g_fmt_cap(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id, int *width, int *height, int *fmt)
+{
+ return smdk4x12_v4l2_g_fmt(smdk4x12_camera, smdk4x12_v4l2_id, V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ width, height, fmt);
+}
+
+int smdk4x12_v4l2_g_fmt_out(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id, int *width, int *height, int *fmt)
+{
+ return smdk4x12_v4l2_g_fmt(smdk4x12_camera, smdk4x12_v4l2_id, V4L2_BUF_TYPE_VIDEO_OUTPUT,
+ width, height, fmt);
+}
+
+int smdk4x12_v4l2_s_fmt_pix(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id, int type, int width, int height, int fmt, int field,
+ int priv)
+{
+ struct v4l2_format format;
+ int rc;
+
+ if (smdk4x12_camera == NULL)
+ return -EINVAL;
+
+ memset(&format, 0, sizeof(format));
+ format.type = type;
+ format.fmt.pix.width = width;
+ format.fmt.pix.height = height;
+ format.fmt.pix.pixelformat = fmt;
+ format.fmt.pix.field = field;
+ format.fmt.pix.priv = priv;
+
+ rc = smdk4x12_v4l2_ioctl(smdk4x12_camera, smdk4x12_v4l2_id, VIDIOC_S_FMT, &format);
+ return rc;
+
+ return 0;
+}
+
+int smdk4x12_v4l2_s_fmt_pix_cap(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id, int width, int height, int fmt, int priv)
+{
+ return smdk4x12_v4l2_s_fmt_pix(smdk4x12_camera, smdk4x12_v4l2_id, V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ width, height, fmt, V4L2_FIELD_NONE, priv);
+}
+
+int smdk4x12_v4l2_s_fmt_pix_out(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id, int width, int height, int fmt, int priv)
+{
+ return smdk4x12_v4l2_s_fmt_pix(smdk4x12_camera, smdk4x12_v4l2_id, V4L2_BUF_TYPE_VIDEO_OUTPUT,
+ width, height, fmt, V4L2_FIELD_NONE, priv);
+}
+
+int smdk4x12_v4l2_s_fmt_win(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id, int left, int top, int width, int height)
+{
+ struct v4l2_format format;
+ int rc;
+
+ if (smdk4x12_camera == NULL)
+ return -EINVAL;
+
+ memset(&format, 0, sizeof(format));
+ format.type = V4L2_BUF_TYPE_VIDEO_OVERLAY;
+ format.fmt.win.w.left = left;
+ format.fmt.win.w.top = top;
+ format.fmt.win.w.width = width;
+ format.fmt.win.w.height = height;
+
+
+ rc = smdk4x12_v4l2_ioctl(smdk4x12_camera, smdk4x12_v4l2_id, VIDIOC_S_FMT, &format);
+ return rc;
+}
+
+int smdk4x12_v4l2_enum_fmt(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id, int type, int fmt)
+{
+ struct v4l2_fmtdesc fmtdesc;
+ int rc;
+
+ if (smdk4x12_camera == NULL)
+ return -EINVAL;
+
+ fmtdesc.type = type;
+ fmtdesc.index = 0;
+
+ do {
+ rc = smdk4x12_v4l2_ioctl(smdk4x12_camera, smdk4x12_v4l2_id, VIDIOC_ENUM_FMT, &fmtdesc);
+ if (rc < 0)
+ return rc;
+
+ if (fmtdesc.pixelformat == (unsigned int) fmt)
+ return 0;
+
+ fmtdesc.index++;
+ } while (rc >= 0);
+
+ return -1;
+}
+
+int smdk4x12_v4l2_enum_fmt_cap(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id, int fmt)
+{
+ return smdk4x12_v4l2_enum_fmt(smdk4x12_camera, smdk4x12_v4l2_id, V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ fmt);
+}
+
+int smdk4x12_v4l2_enum_fmt_out(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id, int fmt)
+{
+ return smdk4x12_v4l2_enum_fmt(smdk4x12_camera, smdk4x12_v4l2_id, V4L2_BUF_TYPE_VIDEO_OUTPUT,
+ fmt);
+}
+
+int smdk4x12_v4l2_enum_input(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id, int id)
+{
+ struct v4l2_input input;
+ int rc;
+
+ if (smdk4x12_camera == NULL || id < 0)
+ return -EINVAL;
+
+ input.index = id;
+
+ rc = smdk4x12_v4l2_ioctl(smdk4x12_camera, smdk4x12_v4l2_id, VIDIOC_ENUMINPUT, &input);
+ if (rc < 0)
+ return rc;
+
+ if (input.name[0] == '\0')
+ return -1;
+
+ return 0;
+}
+
+int smdk4x12_v4l2_s_input(struct smdk4x12_camera *smdk4x12_camera, int smdk4x12_v4l2_id,
+ int id)
+{
+ struct v4l2_input input;
+ int rc;
+
+ if (smdk4x12_camera == NULL || id < 0)
+ return -EINVAL;
+
+ input.index = id;
+
+ rc = smdk4x12_v4l2_ioctl(smdk4x12_camera, smdk4x12_v4l2_id, VIDIOC_S_INPUT, &input);
+ return rc;
+}
+
+int smdk4x12_v4l2_g_ext_ctrls(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id, struct v4l2_ext_control *control, int count)
+{
+ struct v4l2_ext_controls controls;
+ int rc;
+
+ if (smdk4x12_camera == NULL || control == NULL)
+ return -EINVAL;
+
+ memset(&controls, 0, sizeof(controls));
+ controls.ctrl_class = V4L2_CTRL_CLASS_CAMERA;
+ controls.count = count;
+ controls.controls = control;
+
+ rc = smdk4x12_v4l2_ioctl(smdk4x12_camera, smdk4x12_v4l2_id, VIDIOC_G_EXT_CTRLS, &controls);
+ return rc;
+}
+
+int smdk4x12_v4l2_g_ctrl(struct smdk4x12_camera *smdk4x12_camera, int smdk4x12_v4l2_id,
+ int id, int *value)
+{
+ struct v4l2_control control;
+ int rc;
+
+ if (smdk4x12_camera == NULL)
+ return -EINVAL;
+
+ control.id = id;
+
+ rc = smdk4x12_v4l2_ioctl(smdk4x12_camera, smdk4x12_v4l2_id, VIDIOC_G_CTRL, &control);
+ if (rc < 0)
+ return rc;
+
+ if (value != NULL)
+ *value = control.value;
+
+ return 0;
+}
+
+int smdk4x12_v4l2_s_ctrl(struct smdk4x12_camera *smdk4x12_camera, int smdk4x12_v4l2_id,
+ int id, int value)
+{
+ struct v4l2_control control;
+ int rc;
+
+ if (smdk4x12_camera == NULL)
+ return -EINVAL;
+
+ control.id = id;
+ control.value = value;
+
+ rc = smdk4x12_v4l2_ioctl(smdk4x12_camera, smdk4x12_v4l2_id, VIDIOC_S_CTRL, &control);
+ if (rc < 0)
+ return rc;
+
+ return control.value;
+}
+
+int smdk4x12_v4l2_s_parm(struct smdk4x12_camera *smdk4x12_camera, int smdk4x12_v4l2_id,
+ int type, struct v4l2_streamparm *streamparm)
+{
+ int rc;
+
+ if (smdk4x12_camera == NULL || streamparm == NULL)
+ return -EINVAL;
+
+ streamparm->type = type;
+
+ rc = smdk4x12_v4l2_ioctl(smdk4x12_camera, smdk4x12_v4l2_id, VIDIOC_S_PARM, streamparm);
+ return rc;
+}
+
+int smdk4x12_v4l2_s_parm_cap(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id, struct v4l2_streamparm *streamparm)
+{
+ return smdk4x12_v4l2_s_parm(smdk4x12_camera, smdk4x12_v4l2_id, V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ streamparm);
+}
+
+int smdk4x12_v4l2_s_parm_out(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id, struct v4l2_streamparm *streamparm)
+{
+ return smdk4x12_v4l2_s_parm(smdk4x12_camera, smdk4x12_v4l2_id, V4L2_BUF_TYPE_VIDEO_OUTPUT,
+ streamparm);
+}
+
+int smdk4x12_v4l2_s_crop(struct smdk4x12_camera *smdk4x12_camera, int smdk4x12_v4l2_id,
+ int type, int left, int top, int width, int height)
+{
+ struct v4l2_crop crop;
+ int rc;
+
+ if (smdk4x12_camera == NULL)
+ return -EINVAL;
+
+ crop.type = type;
+ crop.c.left = left;
+ crop.c.top = top;
+ crop.c.width = width;
+ crop.c.height = height;
+
+ rc = smdk4x12_v4l2_ioctl(smdk4x12_camera, smdk4x12_v4l2_id, VIDIOC_S_CROP, &crop);
+ return rc;
+}
+
+int smdk4x12_v4l2_s_crop_cap(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id, int left, int top, int width, int height)
+{
+ return smdk4x12_v4l2_s_crop(smdk4x12_camera, smdk4x12_v4l2_id, V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ left, top, width, height);
+}
+
+int smdk4x12_v4l2_s_crop_out(struct smdk4x12_camera *smdk4x12_camera,
+ int smdk4x12_v4l2_id, int left, int top, int width, int height)
+{
+ return smdk4x12_v4l2_s_crop(smdk4x12_camera, smdk4x12_v4l2_id, V4L2_BUF_TYPE_VIDEO_OUTPUT,
+ left, top, width, height);
+}
+
+int smdk4x12_v4l2_g_fbuf(struct smdk4x12_camera *smdk4x12_camera, int smdk4x12_v4l2_id,
+ void **base, int *width, int *height, int *fmt)
+{
+ struct v4l2_framebuffer framebuffer;
+ int rc;
+
+ if (smdk4x12_camera == NULL)
+ return -EINVAL;
+
+ rc = smdk4x12_v4l2_ioctl(smdk4x12_camera, smdk4x12_v4l2_id, VIDIOC_G_FBUF, &framebuffer);
+ if (rc < 0)
+ return rc;
+
+ if (base != NULL)
+ *base = framebuffer.base;
+ if (width != NULL)
+ *width = framebuffer.fmt.width;
+ if (height != NULL)
+ *height = framebuffer.fmt.height;
+ if (fmt != NULL)
+ *fmt = framebuffer.fmt.pixelformat;
+
+ return 0;
+}
+
+int smdk4x12_v4l2_s_fbuf(struct smdk4x12_camera *smdk4x12_camera, int smdk4x12_v4l2_id,
+ void *base, int width, int height, int fmt)
+{
+ struct v4l2_framebuffer framebuffer;
+ int rc;
+
+ if (smdk4x12_camera == NULL)
+ return -EINVAL;
+
+ memset(&framebuffer, 0, sizeof(framebuffer));
+ framebuffer.base = base;
+ framebuffer.fmt.width = width;
+ framebuffer.fmt.height = height;
+ framebuffer.fmt.pixelformat = fmt;
+
+ rc = smdk4x12_v4l2_ioctl(smdk4x12_camera, smdk4x12_v4l2_id, VIDIOC_S_FBUF, &framebuffer);
+ return rc;
+}
diff --git a/camera/smdk4x12_v4l2_output.c b/camera/smdk4x12_v4l2_output.c
new file mode 100644
index 0000000..0f8cad2
--- /dev/null
+++ b/camera/smdk4x12_v4l2_output.c
@@ -0,0 +1,387 @@
+/*
+ * Copyright (C) 2013 Paul Kocialkowski <contact@paulk.fr>
+ *
+ * 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 <errno.h>
+#include <malloc.h>
+#include <poll.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>
+
+#define LOG_TAG "smdk4x12_v4l2_output"
+#include <utils/Log.h>
+
+#include "smdk4x12_camera.h"
+
+int smdk4x12_v4l2_output_start(struct smdk4x12_camera *smdk4x12_camera,
+ struct smdk4x12_v4l2_output *output)
+{
+ int width, height, format;
+ int buffer_width, buffer_height, buffer_format;
+ camera_memory_t *memory = NULL;
+ int memory_address, memory_size;
+#ifdef EXYNOS_ION
+ int memory_ion_fd = -1;
+#endif
+ int buffers_count, buffer_length;
+ int v4l2_id;
+ int value;
+ int fd;
+ int rc;
+ int i;
+
+ if (smdk4x12_camera == NULL || output == NULL)
+ return -EINVAL;
+
+// ALOGD("%s()", __func__);
+
+ if (output->enabled) {
+ ALOGE("Output was already started");
+ return -1;
+ }
+
+ width = output->width;
+ height = output->height;
+ format = output->format;
+
+ buffer_width = output->buffer_width;
+ buffer_height = output->buffer_height;
+ buffer_format = output->buffer_format;
+
+ v4l2_id = output->v4l2_id;
+
+ buffers_count = output->buffers_count;
+ if (buffers_count <= 0) {
+ ALOGE("%s: Invalid buffers count: %d", __func__, buffers_count);
+ goto error;
+ }
+
+ buffer_length = smdk4x12_camera_buffer_length(width, height, format);
+
+ rc = smdk4x12_v4l2_open(smdk4x12_camera, v4l2_id);
+ if (rc < 0) {
+ ALOGE("%s: Unable to open v4l2 device", __func__);
+ goto error;
+ }
+
+ rc = smdk4x12_v4l2_querycap_out(smdk4x12_camera, v4l2_id);
+ if (rc < 0) {
+ ALOGE("%s: Unable to query capabilities", __func__);
+ goto error;
+ }
+
+ rc = smdk4x12_v4l2_g_fmt_out(smdk4x12_camera, v4l2_id, NULL, NULL, NULL);
+ if (rc < 0) {
+ ALOGE("%s: Unable to get format", __func__);
+ goto error;
+ }
+
+ value = 0;
+ rc = smdk4x12_v4l2_g_ctrl(smdk4x12_camera, v4l2_id, V4L2_CID_RESERVED_MEM_BASE_ADDR, &value);
+ if (rc < 0) {
+ ALOGE("%s: Unable to get address", __func__);
+ goto error;
+ }
+
+ memory_address = value;
+
+ value = 0;
+ rc = smdk4x12_v4l2_g_ctrl(smdk4x12_camera, v4l2_id, V4L2_CID_RESERVED_MEM_SIZE, &value);
+ if (rc < 0) {
+ ALOGE("%s: Unable to get size", __func__);
+ goto error;
+ }
+
+ memory_size = value * 1024;
+
+ rc = smdk4x12_v4l2_s_ctrl(smdk4x12_camera, v4l2_id, V4L2_CID_OVLY_MODE, FIMC_OVLY_NONE_MULTI_BUF);
+ if (rc < 0) {
+ ALOGE("%s: Unable to set overlay mode", __func__);
+ goto error;
+ }
+
+ rc = smdk4x12_v4l2_s_fmt_pix_out(smdk4x12_camera, v4l2_id, buffer_width, buffer_height, buffer_format, 0);
+ if (rc < 0) {
+ ALOGE("%s: Unable to set output pixel format!", __func__);
+ goto error;
+ }
+
+ rc = smdk4x12_v4l2_s_crop_out(smdk4x12_camera, v4l2_id, 0, 0, buffer_width, buffer_height);
+ if (rc < 0) {
+ ALOGE("%s: Unable to crop", __func__);
+ goto error;
+ }
+
+ rc = smdk4x12_v4l2_reqbufs_out(smdk4x12_camera, v4l2_id, 1);
+ if (rc < 0) {
+ ALOGE("%s: Unable to request buffers", __func__);
+ goto error;
+ }
+
+ if (memory_address != 0 && memory_address != (int) 0xffffffff && memory_size >= buffer_length) {
+ for (i = buffers_count; i > 0; i--) {
+ if (buffer_length * i < memory_size)
+ break;
+ }
+
+ // This should never happen
+ if (i == 0)
+ goto error;
+
+ buffers_count = i;
+ ALOGD("Found %d buffers available for output!", buffers_count);
+
+ if (SMDK4x12_CAMERA_CALLBACK_DEFINED(request_memory)) {
+ fd = smdk4x12_v4l2_fd(smdk4x12_camera, v4l2_id);
+ if (fd < 0) {
+ ALOGE("%s: Unable to get v4l2 fd for id %d", __func__, v4l2_id);
+ goto error;
+ }
+
+ memory = smdk4x12_camera->callbacks.request_memory(fd, buffer_length, buffers_count, smdk4x12_camera->callbacks.user);
+ if (memory == NULL || memory->data == NULL || memory->data == MAP_FAILED) {
+ ALOGE("%s: Unable to request memory", __func__);
+ goto error;
+ }
+ } else {
+ ALOGE("%s: No memory request function!", __func__);
+ goto error;
+ }
+ } else {
+#ifdef EXYNOS_ION
+ memory_ion_fd = smdk4x12_ion_alloc(smdk4x12_camera, buffers_count * buffer_length);
+ if (memory_ion_fd < 0) {
+ ALOGE("%s: Unable to alloc ION memory", __func__);
+ goto error;
+ }
+
+ if (SMDK4x12_CAMERA_CALLBACK_DEFINED(request_memory)) {
+ memory = smdk4x12_camera->callbacks.request_memory(memory_ion_fd, buffer_length, buffers_count, smdk4x12_camera->callbacks.user);
+ if (memory == NULL || memory->data == NULL || memory->data == MAP_FAILED) {
+ ALOGE("%s: Unable to request memory", __func__);
+ goto error;
+ }
+ } else {
+ ALOGE("%s: No memory request function!", __func__);
+ goto error;
+ }
+
+ memory_address = smdk4x12_ion_phys(smdk4x12_camera, memory_ion_fd);
+#else
+ ALOGE("%s: Unable to find memory", __func__);
+ goto error;
+#endif
+ }
+
+ output->memory = memory;
+ output->memory_address = memory_address;
+#ifdef EXYNOS_ION
+ output->memory_ion_fd = memory_ion_fd;
+#endif
+ output->memory_index = 0;
+ output->buffers_count = buffers_count;
+ output->buffer_length = buffer_length;
+
+ output->enabled = 1;
+
+ rc = 0;
+ goto complete;
+
+error:
+ if (memory != NULL && memory->release != NULL) {
+ memory->release(memory);
+ output->memory = NULL;
+ }
+
+#ifdef EXYNOS_ION
+ if (memory_ion_fd >= 0)
+ smdk4x12_ion_free(smdk4x12_camera, memory_ion_fd);
+#endif
+
+ smdk4x12_v4l2_close(smdk4x12_camera, v4l2_id);
+
+ rc = -1;
+
+complete:
+ return rc;
+}
+
+void smdk4x12_v4l2_output_stop(struct smdk4x12_camera *smdk4x12_camera,
+ struct smdk4x12_v4l2_output *output)
+{
+ int v4l2_id;
+ int rc;
+
+ if (smdk4x12_camera == NULL || output == NULL)
+ return;
+
+// ALOGD("%s()", __func__);
+
+ if (!output->enabled) {
+ ALOGE("Output was already stopped");
+ return;
+ }
+
+ v4l2_id = output->v4l2_id;
+
+ rc = smdk4x12_v4l2_reqbufs_out(smdk4x12_camera, v4l2_id, 0);
+ if (rc < 0)
+ ALOGE("%s: Unable to request buffers", __func__);
+
+ if (output->memory != NULL && output->memory->release != NULL) {
+ output->memory->release(output->memory);
+ output->memory = NULL;
+ }
+
+#ifdef EXYNOS_ION
+ if (output->memory_ion_fd >= 0) {
+ smdk4x12_ion_free(smdk4x12_camera, output->memory_ion_fd);
+ output->memory_ion_fd = -1;
+ }
+#endif
+
+ smdk4x12_v4l2_close(smdk4x12_camera, v4l2_id);
+
+ output->enabled = 0;
+}
+
+int smdk4x12_v4l2_output(struct smdk4x12_camera *smdk4x12_camera,
+ struct smdk4x12_v4l2_output *output, int buffer_address)
+{
+ struct fimc_buf fimc_buffer;
+ void *fb_base;
+ int width, height, format;
+ int buffer_width, buffer_height, buffer_format;
+ int buffer_length;
+ int address;
+ int v4l2_id;
+ int rc;
+
+ if (smdk4x12_camera == NULL || output == NULL)
+ return -EINVAL;
+
+// ALOGD("%s()", __func__);
+
+ if (!output->enabled) {
+ ALOGE("Output was not started");
+ return -1;
+ }
+
+ width = output->width;
+ height = output->height;
+ format = output->format;
+
+ buffer_width = output->buffer_width;
+ buffer_height = output->buffer_height;
+ buffer_format = output->buffer_format;
+
+ buffer_length = output->buffer_length;
+ v4l2_id = output->v4l2_id;
+
+ rc = smdk4x12_v4l2_g_fbuf(smdk4x12_camera, v4l2_id, &fb_base, NULL, NULL, NULL);
+ if (rc < 0) {
+ ALOGE("%s: Unable to get fbuf", __func__);
+ goto error;
+ }
+
+ rc = smdk4x12_v4l2_s_fbuf(smdk4x12_camera, v4l2_id, fb_base, width, height, format);
+ if (rc < 0) {
+ ALOGE("%s: Unable to set fbuf", __func__);
+ goto error;
+ }
+
+ memset(&fimc_buffer, 0, sizeof(fimc_buffer));
+
+ address = output->memory_address + buffer_length * output->memory_index;
+
+ smdk4x12_camera_yuv_planes(width, height, format, address, (int *) &fimc_buffer.base[0], (int *) &fimc_buffer.base[1], (int *) &fimc_buffer.base[2]);
+
+ rc = smdk4x12_v4l2_s_ctrl(smdk4x12_camera, v4l2_id, V4L2_CID_DST_INFO, (int) &fimc_buffer);
+ if (rc < 0) {
+ ALOGE("%s: Unable to set dst info", __func__);
+ goto error;
+ }
+
+ rc = smdk4x12_v4l2_s_fmt_win(smdk4x12_camera, v4l2_id, 0, 0, width, height);
+ if (rc < 0) {
+ ALOGE("%s: Unable to set overlay win", __func__);
+ goto error;
+ }
+
+ rc = smdk4x12_v4l2_streamon_out(smdk4x12_camera, v4l2_id);
+ if (rc < 0) {
+ ALOGE("%s: Unable to start stream", __func__);
+ goto error;
+ }
+
+ memset(&fimc_buffer, 0, sizeof(fimc_buffer));
+
+ smdk4x12_camera_yuv_planes(buffer_width, buffer_height, buffer_format, buffer_address, (int *) &fimc_buffer.base[0], (int *) &fimc_buffer.base[1], (int *) &fimc_buffer.base[2]);
+
+ rc = smdk4x12_v4l2_qbuf_out(smdk4x12_camera, v4l2_id, 0, (unsigned long) &fimc_buffer);
+ if (rc < 0) {
+ ALOGE("%s: Unable to queue buffer", __func__);
+ goto error;
+ }
+
+ rc = smdk4x12_v4l2_dqbuf_out(smdk4x12_camera, v4l2_id);
+ if (rc < 0) {
+ ALOGE("%s: Unable to dequeue buffer", __func__);
+ goto error;
+ }
+
+ rc = smdk4x12_v4l2_streamoff_out(smdk4x12_camera, v4l2_id);
+ if (rc < 0) {
+ ALOGE("%s: Unable to stop stream", __func__);
+ goto error;
+ }
+
+ rc = 0;
+ goto complete;
+
+error:
+ rc = -1;
+
+complete:
+ return rc;
+}
+
+int smdk4x12_v4l2_output_release(struct smdk4x12_camera *smdk4x12_camera,
+ struct smdk4x12_v4l2_output *output)
+{
+ int buffers_count;
+ int memory_index;
+
+ if (smdk4x12_camera == NULL || output == NULL)
+ return -EINVAL;
+
+// ALOGD("%s()", __func__);
+
+ buffers_count = output->buffers_count;
+ memory_index = output->memory_index;
+
+ memory_index++;
+ output->memory_index = memory_index % buffers_count;
+
+ return 0;
+}