diff options
Diffstat (limited to 'camera')
-rw-r--r-- | camera/Android.mk | 43 | ||||
-rw-r--r-- | camera/include/fimc.h | 405 | ||||
-rw-r--r-- | camera/include/ion.h | 449 | ||||
-rw-r--r-- | camera/include/linux/fimc.h | 381 | ||||
-rw-r--r-- | camera/include/s5c73m3.h | 369 | ||||
-rw-r--r-- | camera/smdk4x12_camera.c | 4491 | ||||
-rw-r--r-- | camera/smdk4x12_camera.h | 694 | ||||
-rw-r--r-- | camera/smdk4x12_exif.c | 888 | ||||
-rw-r--r-- | camera/smdk4x12_ion.c | 159 | ||||
-rw-r--r-- | camera/smdk4x12_jpeg.c | 402 | ||||
-rw-r--r-- | camera/smdk4x12_param.c | 492 | ||||
-rw-r--r-- | camera/smdk4x12_utils.c | 158 | ||||
-rw-r--r-- | camera/smdk4x12_v4l2.c | 739 | ||||
-rw-r--r-- | camera/smdk4x12_v4l2_output.c | 387 |
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, ¶m->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; +} |