summaryrefslogtreecommitdiffstats
path: root/camera/exynos_v4l2_output.c
diff options
context:
space:
mode:
authorWolfgang Wiedmeyer <wolfgit@wiedmeyer.de>2016-03-18 15:40:14 +0100
committerWolfgang Wiedmeyer <wolfgit@wiedmeyer.de>2016-03-18 15:40:26 +0100
commit5fe9b2c621b08a16b00e7080c30b9875155fdc43 (patch)
tree91b3a5569c4300bff48e3ba935b9d8d9634a4593 /camera/exynos_v4l2_output.c
parent062cf13b091d82edc08f3d8fa8bfa3961aae9ee2 (diff)
downloaddevice_samsung_smdk4412-common-5fe9b2c621b08a16b00e7080c30b9875155fdc43.tar.gz
device_samsung_smdk4412-common-5fe9b2c621b08a16b00e7080c30b9875155fdc43.tar.bz2
device_samsung_smdk4412-common-5fe9b2c621b08a16b00e7080c30b9875155fdc43.zip
camera: switch to CyanogenMod's version of the free software camera
Replicant's version locks up, sometimes already after taking only one picture. CyanogenMod's camera seems more stable. Only the preview format was changed to RGB565. TODO: Incorporate fixes from both version. Signed-off-by: Wolfgang Wiedmeyer <wolfgit@wiedmeyer.de>
Diffstat (limited to 'camera/exynos_v4l2_output.c')
-rw-r--r--camera/exynos_v4l2_output.c387
1 files changed, 387 insertions, 0 deletions
diff --git a/camera/exynos_v4l2_output.c b/camera/exynos_v4l2_output.c
new file mode 100644
index 0000000..2a39fd2
--- /dev/null
+++ b/camera/exynos_v4l2_output.c
@@ -0,0 +1,387 @@
+/*
+ * Copyright (C) 2013 Paul Kocialkowski
+ *
+ * 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 "exynos_v4l2_output"
+#include <utils/Log.h>
+
+#include "exynos_camera.h"
+
+int exynos_v4l2_output_start(struct exynos_camera *exynos_camera,
+ struct exynos_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 (exynos_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 = exynos_camera_buffer_length(width, height, format);
+
+ rc = exynos_v4l2_open(exynos_camera, v4l2_id);
+ if (rc < 0) {
+ ALOGE("%s: Unable to open v4l2 device", __func__);
+ goto error;
+ }
+
+ rc = exynos_v4l2_querycap_out(exynos_camera, v4l2_id);
+ if (rc < 0) {
+ ALOGE("%s: Unable to query capabilities", __func__);
+ goto error;
+ }
+
+ rc = exynos_v4l2_g_fmt_out(exynos_camera, v4l2_id, NULL, NULL, NULL);
+ if (rc < 0) {
+ ALOGE("%s: Unable to get format", __func__);
+ goto error;
+ }
+
+ value = 0;
+ rc = exynos_v4l2_g_ctrl(exynos_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 = exynos_v4l2_g_ctrl(exynos_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 = exynos_v4l2_s_ctrl(exynos_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 = exynos_v4l2_s_fmt_pix_out(exynos_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 = exynos_v4l2_s_crop_out(exynos_camera, v4l2_id, 0, 0, buffer_width, buffer_height);
+ if (rc < 0) {
+ ALOGE("%s: Unable to crop", __func__);
+ goto error;
+ }
+
+ rc = exynos_v4l2_reqbufs_out(exynos_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 (EXYNOS_CAMERA_CALLBACK_DEFINED(request_memory)) {
+ fd = exynos_v4l2_fd(exynos_camera, v4l2_id);
+ if (fd < 0) {
+ ALOGE("%s: Unable to get v4l2 fd for id %d", __func__, v4l2_id);
+ goto error;
+ }
+
+ memory = exynos_camera->callbacks.request_memory(fd, buffer_length, buffers_count, exynos_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 = exynos_ion_alloc(exynos_camera, buffers_count * buffer_length);
+ if (memory_ion_fd < 0) {
+ ALOGE("%s: Unable to alloc ION memory", __func__);
+ goto error;
+ }
+
+ if (EXYNOS_CAMERA_CALLBACK_DEFINED(request_memory)) {
+ memory = exynos_camera->callbacks.request_memory(memory_ion_fd, buffer_length, buffers_count, exynos_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 = exynos_ion_phys(exynos_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)
+ exynos_ion_free(exynos_camera, memory_ion_fd);
+#endif
+
+ exynos_v4l2_close(exynos_camera, v4l2_id);
+
+ rc = -1;
+
+complete:
+ return rc;
+}
+
+void exynos_v4l2_output_stop(struct exynos_camera *exynos_camera,
+ struct exynos_v4l2_output *output)
+{
+ int v4l2_id;
+ int rc;
+
+ if (exynos_camera == NULL || output == NULL)
+ return;
+
+// ALOGD("%s()", __func__);
+
+ if (!output->enabled) {
+ ALOGE("Output was already stopped");
+ return;
+ }
+
+ v4l2_id = output->v4l2_id;
+
+ rc = exynos_v4l2_reqbufs_out(exynos_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) {
+ exynos_ion_free(exynos_camera, output->memory_ion_fd);
+ output->memory_ion_fd = -1;
+ }
+#endif
+
+ exynos_v4l2_close(exynos_camera, v4l2_id);
+
+ output->enabled = 0;
+}
+
+int exynos_v4l2_output(struct exynos_camera *exynos_camera,
+ struct exynos_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 (exynos_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 = exynos_v4l2_g_fbuf(exynos_camera, v4l2_id, &fb_base, NULL, NULL, NULL);
+ if (rc < 0) {
+ ALOGE("%s: Unable to get fbuf", __func__);
+ goto error;
+ }
+
+ rc = exynos_v4l2_s_fbuf(exynos_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;
+
+ exynos_camera_yuv_planes(width, height, format, address, (int *) &fimc_buffer.base[0], (int *) &fimc_buffer.base[1], (int *) &fimc_buffer.base[2]);
+
+ rc = exynos_v4l2_s_ctrl(exynos_camera, v4l2_id, V4L2_CID_DST_INFO, (int) &fimc_buffer);
+ if (rc < 0) {
+ ALOGE("%s: Unable to set dst info", __func__);
+ goto error;
+ }
+
+ rc = exynos_v4l2_s_fmt_win(exynos_camera, v4l2_id, 0, 0, width, height);
+ if (rc < 0) {
+ ALOGE("%s: Unable to set overlay win", __func__);
+ goto error;
+ }
+
+ rc = exynos_v4l2_streamon_out(exynos_camera, v4l2_id);
+ if (rc < 0) {
+ ALOGE("%s: Unable to start stream", __func__);
+ goto error;
+ }
+
+ memset(&fimc_buffer, 0, sizeof(fimc_buffer));
+
+ exynos_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 = exynos_v4l2_qbuf_out(exynos_camera, v4l2_id, 0, (unsigned long) &fimc_buffer);
+ if (rc < 0) {
+ ALOGE("%s: Unable to queue buffer", __func__);
+ goto error;
+ }
+
+ rc = exynos_v4l2_dqbuf_out(exynos_camera, v4l2_id);
+ if (rc < 0) {
+ ALOGE("%s: Unable to dequeue buffer", __func__);
+ goto error;
+ }
+
+ rc = exynos_v4l2_streamoff_out(exynos_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 exynos_v4l2_output_release(struct exynos_camera *exynos_camera,
+ struct exynos_v4l2_output *output)
+{
+ int buffers_count;
+ int memory_index;
+
+ if (exynos_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;
+}