diff -urN PR1.3/Documentation/video4linux/README.fcam PR1.3-fcam/Documentation/video4linux/README.fcam
--- PR1.3/Documentation/video4linux/README.fcam 1969-12-31 19:00:00.000000000 -0500
+++ PR1.3-fcam/Documentation/video4linux/README.fcam 2010-12-30 12:00:23.000000000 -0500
@@ -0,0 +1,87 @@
+FCAM-DRIVERS
+
+Description
+-------------
+
+FCam-drivers is a set of replacement kernel modules for the Nokia N900
+camera subsystem, meant to be used in conjunction with the FCam
+userspace camera API. These are all slightly modified from the
+original code that is included in the kernel tree for the Nokia N900.
+
+Partial list of changes relative to the N900 baseline kernel:
+
+v1.0.4: (first release)
+
+ - Added V4L2 ioctl for installing an FCam application as a special
+ user of the camera subsystem. This allows for spawning of
+ real-time threads by the FCam user-space API, and for disabling of
+ the built-in user space camera daemon while the FCam application
+ is running.
+
+ - Added V4L2 ioctl for waiting until the next sensor HSVS sync
+ signal arrives. This allows for timing synchronization between
+ the FCam user space API and the sensor.
+
+ - Added two custom V4L2 controls, one to control the frame time and
+ one for exact gain control. The frame time control violates the
+ spirit of the V4L2 specification, as frame time is not supposed to
+ vary for a given format, but variable frame time is highly
+ important for the FCam APi. The gain control is simply a
+ finer-grained control than what is included in the et8ek8.c driver
+ by default.
+
+ - Added ISP IOCTL (VIDIOC_PRIVATE_ISP_PIPELINE_STATS_REQ) to
+ retrieve pipeline statistics, needed for properly interpreting
+ statistics generator results.
+
+ - Disabled LSC (lens shading compensation) in the ISP
+ previewer. Causes sbl_overflow errors from the ISP when enabled,
+ and workarounds have not been implemented yet.
+
+ - Fixes to the ISP statistics module code to make histogram and
+ sharpness map modules provide the latest set of buffers to the
+ caller.
+
+
+Building:
+----------
+
+Building these modules has only been tested inside the Maemo 5 SDK
+scratchbox development environment, available from Nokia. See
+http://wiki.maemo.org/Documentation/Maemo_5_Final_SDK_Installation
+
+The below is known to work, although it may be overkill (getting only
+kernel headers may be sufficient)
+
+Once you have scratchbox set up, you'll need a copy of the Maemo
+kernel headers for the Nokia N900. Make sure you're using the FREMANTLE_ARMEL target,
+and then get the headers with:
+
+ fakeroot apt-get install kernel-headers
+
+which will create the directory /usr/src/kernel-headers inside scratchbox.
+
+You can then build the fcam-drivers Maemo package with
+
+ make deb
+
+inside the fcam-drivers directory. This will create the .deb package in the parent directory.
+
+Installation:
+--------------
+
+To install the .deb package directory, copy it to your N900, and when logged in as root, type
+
+ dpkg -i <package_name>
+
+You'll have to reboot to complete the installation process.
+
+Legal:
+-----------
+Modifications/additions to the kernel source code are:
+Copyright (C) 2010 Stanford University
+
+This package 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.
+
diff -urN PR1.3/arch/arm/mach-omap2/board-rx51-camera.c PR1.3-fcam/arch/arm/mach-omap2/board-rx51-camera.c
--- PR1.3/arch/arm/mach-omap2/board-rx51-camera.c 2010-12-30 11:37:23.000000000 -0500
+++ PR1.3-fcam/arch/arm/mach-omap2/board-rx51-camera.c 2010-12-30 12:00:23.000000000 -0500
@@ -561,7 +561,7 @@
gpio_set_value(ADP1653_GPIO_ENABLE, 1);
/* Some delay is apparently required. */
- udelay(400);
+ udelay(20);
return 0;
}
diff -urN PR1.3/arch/arm/plat-omap/include/mach/isp_user.h PR1.3-fcam/arch/arm/plat-omap/include/mach/isp_user.h
--- PR1.3/arch/arm/plat-omap/include/mach/isp_user.h 2010-12-30 11:37:23.000000000 -0500
+++ PR1.3-fcam/arch/arm/plat-omap/include/mach/isp_user.h 2010-12-30 12:00:23.000000000 -0500
@@ -38,6 +38,8 @@
_IOWR('V', BASE_VIDIOC_PRIVATE + 8, struct af_configuration)
#define VIDIOC_PRIVATE_ISP_AF_REQ \
_IOWR('V', BASE_VIDIOC_PRIVATE + 9, struct isp_af_data)
+#define VIDIOC_PRIVATE_ISP_PIPELINE_STATS_REQ \
+ _IOR('V', BASE_VIDIOC_PRIVATE + 10, struct isp_pipeline_stats)
/* AE/AWB related structures and flags*/
@@ -50,6 +52,8 @@
#define MAX_FRAME_COUNT 0x0FFF
#define MAX_FUTURE_FRAMES 10
+#define NEWEST_FRAME 0xFFFF
+#define OLDEST_FRAME 0xFFFE
#define MAX_SATURATION_LIM 1023
#define MIN_WIN_H 2
@@ -484,6 +488,24 @@
__u8 *lsc;
};
+// A means to query the raw output size from the ccdc in order to
+// setup the H3A and HIST modules correctly
+/**
+ * isp_pipeline_stats - Structure to query the intermediate sizes in the pipeline
+ * @ccdc_out_*: The output resolution of the CCDC, before demosaicing
+ * @prv_out_*: The output resolution of the preview engine, after demosaicing but before resizing
+ * @rsz_in_*: The crop of the preview engine output used by the resizer
+ * @rsz_out_*: The output resolution of the resize engine
+ * @*_active: Whether the given components of the pipeline is active
+ */
+struct isp_pipeline_stats {
+ __u16 ccdc_out_w, ccdc_out_h;
+ __u16 prv_out_w, prv_out_h;
+ __u16 rsz_in_x, rsz_in_y, rsz_in_w, rsz_in_h;
+ __u16 rsz_out_w, rsz_out_h;
+ __u8 rsz_active, prv_active;
+};
+
/* Preview configuration */
/*Abstraction layer preview configurations*/
diff -urN PR1.3/drivers/media/video/et8ek8.c PR1.3-fcam/drivers/media/video/et8ek8.c
--- PR1.3/drivers/media/video/et8ek8.c 2010-12-30 11:37:22.000000000 -0500
+++ PR1.3-fcam/drivers/media/video/et8ek8.c 2010-12-30 12:02:41.000000000 -0500
@@ -36,17 +36,25 @@
#include <media/smiaregs.h>
#include <media/v4l2-int-device.h>
+#include "omap34xxcam-fcam.h"
+
#include "et8ek8.h"
#define ET8EK8_XCLK_HZ 9600000
+
#define CTRL_GAIN 0
-#define CTRL_EXPOSURE 1
-#define CTRL_TEST_PATTERN 2
+#define CTRL_GAIN_EXACT 1
+#define CTRL_EXPOSURE 2
+#define CTRL_FRAME_TIME 3
+#define CTRL_TEST_PATTERN 4
+
#define CID_TO_CTRL(id) ((id)==V4L2_CID_GAIN ? CTRL_GAIN : \
+ (id)==V4L2_CID_GAIN_EXACT ? CTRL_GAIN_EXACT : \
(id)==V4L2_CID_EXPOSURE ? CTRL_EXPOSURE : \
(id)==V4L2_CID_TEST_PATTERN ? CTRL_TEST_PATTERN : \
+ (id)==V4L2_CID_FRAME_TIME ? CTRL_FRAME_TIME : \
-EINVAL)
enum et8ek8_versions {
@@ -54,6 +62,19 @@
ET8EK8_REV_2,
};
+
+/* Register definitions */
+#define REG_REVISION_NUMBER_L 0x1200
+#define REG_REVISION_NUMBER_H 0x1201
+
+#define PRIV_MEM_START_REG 0x0008
+#define PRIV_MEM_WIN_SIZE 8
+
+#define ET8EK8_I2C_DELAY 3 /* msec delay b/w accesses */
+
+#define USE_CRC 1
+
+
/*
* This table describes what should be written to the sensor register
* for each gain value. The gain(index in the table) is in terms of
@@ -109,16 +130,29 @@
{256, 1023}, /* x16 */
};
-/* Register definitions */
-#define REG_REVISION_NUMBER_L 0x1200
-#define REG_REVISION_NUMBER_H 0x1201
-
-#define PRIV_MEM_START_REG 0x0008
-#define PRIV_MEM_WIN_SIZE 8
-
-#define ET8EK8_I2C_DELAY 3 /* msec delay b/w accesses */
-
-#define USE_CRC 1
+static unsigned char et8ek8_exact_gain_to_gain(s32 g) {
+ static unsigned char lut[] = {0, 2, 3, 5, 6, 7, 8, 9,
+ 10, 11, 12, 12, 13, 14, 15, 15,
+ 16, 16, 17, 18, 18, 19, 19, 20,
+ 20, 20, 21, 21, 22, 22, 22, 23,
+ 23, 24, 24, 24, 25, 25, 25, 26,
+ 26, 26, 26, 27, 27, 27, 28, 28,
+ 28, 28, 29, 29, 29, 29, 30, 30,
+ 30, 30, 30, 31, 31, 31, 31, 31,
+ 32, 32, 32, 32, 32, 33, 33, 33,
+ 33, 33, 34, 34, 34, 34, 34, 34,
+ 35, 35, 35, 35, 35, 35, 36, 36,
+ 36, 36, 36, 36, 36, 37, 37, 37,
+ 37, 37, 37, 37, 38, 38, 38, 38,
+ 38, 38, 38, 38, 39, 39, 39, 39,
+ 39, 39, 39, 39, 40, 40, 40, 40};
+
+ // Drop the bottom two bits and subtract 8
+ g = (g >> 2) - 8;
+ if (g < 0) return 0;
+ if (g >= 120) return 40;
+ return lut[g];
+}
/* Called to change the V4L2 gain control value. This function
* rounds and clamps the given value and updates the V4L2 control value.
@@ -139,6 +173,12 @@
new = et8ek8_gain_table[sensor->controls[CTRL_GAIN].value];
+ // Set the equivalent exact gain
+ if (new.analog < 256)
+ sensor->controls[CTRL_GAIN_EXACT].value = new.analog;
+ else
+ sensor->controls[CTRL_GAIN_EXACT].value = (new.digital + 1024) / 4;
+
/* FIXME: optimise I2C writes! */
r = smia_i2c_write_reg(sensor->i2c_client, SMIA_REG_8BIT,
0x124a, new.analog >> 8);
@@ -159,6 +199,59 @@
return r;
}
+/* Called to change the V4L2 gain control value. This function
+ * rounds and clamps the given value and updates the V4L2 control value.
+ * If power is on, also updates the sensor analog and digital gains.
+ * gain is in 1/32 units (ie a setting of 32 means a gain of 1x)
+ */
+static int et8ek8_set_gain_exact(struct et8ek8_sensor *sensor, s32 gain)
+{
+ int r;
+ u16 analog, digital;
+
+ //printk(KERN_INFO "Setting et8ek8 exact gain to %d\n", gain);
+
+ gain = sensor->controls[CTRL_GAIN_EXACT].value =
+ clamp(gain,
+ sensor->controls[CTRL_GAIN_EXACT].minimum,
+ sensor->controls[CTRL_GAIN_EXACT].maximum);
+
+ if (sensor->power == V4L2_POWER_OFF)
+ return 0;
+
+ if (gain <= 256) {
+ // analog only
+ analog = gain;
+ digital = 0;
+ } else {
+ // digital gain as well
+ analog = 256;
+ digital = 0xc000 | gain;
+ }
+
+ // Set the (roughly) equivalent gain
+ sensor->controls[CTRL_GAIN].value = et8ek8_exact_gain_to_gain(gain);
+
+ // FIXME: optimise I2C writes!
+ r = smia_i2c_write_reg(sensor->i2c_client, SMIA_REG_8BIT,
+ 0x124a, analog >> 8);
+ if (r)
+ return r;
+ r = smia_i2c_write_reg(sensor->i2c_client, SMIA_REG_8BIT,
+ 0x1249, analog & 0xff);
+ if (r)
+ return r;
+
+ r = smia_i2c_write_reg(sensor->i2c_client, SMIA_REG_8BIT,
+ 0x124d, digital >> 8);
+ if (r)
+ return r;
+ r = smia_i2c_write_reg(sensor->i2c_client, SMIA_REG_8BIT,
+ 0x124c, digital & 0xff);
+
+ return r;
+}
+
/* Called to change the V4L2 exposure control value. This function
* rounds and clamps the given value and updates the V4L2 control value.
* If power is on, also update the sensor exposure time.
@@ -196,6 +289,56 @@
swab16(rows));
}
+static int et8ek8_set_frame_time(struct et8ek8_sensor *sensor, s32 frame_time)
+{
+ unsigned int clock, chunk_time, chunk_size, chunks;
+
+ frame_time = clamp(frame_time,
+ sensor->controls[CTRL_FRAME_TIME].minimum,
+ sensor->controls[CTRL_FRAME_TIME].maximum);
+
+ // One chunk is a number of rows dependent on the resolution (usually 12 or 24)
+ chunk_size = sensor->current_reglist->mode.height / 84;
+
+ // Compute the number of chunks using an analogous FP formula
+ // to the one in set_exposure
+ clock = sensor->current_reglist->mode.pixel_clock;
+ clock = (clock + (1 << 12)) >> 13;
+ chunk_time = sensor->current_reglist->mode.width * (1000000 >> 5) * chunk_size;
+ chunk_time = (chunk_time + (clock >> 1)) / clock;
+ chunks = ((frame_time << 8) + (chunk_time >> 1)) / chunk_time;
+
+ // printk(KERN_INFO "Setting frame time to %d us (%d chunks)\n", frame_time, chunks);
+ if (chunks < 84) chunks = 84;
+ if (chunks > 2730) chunks = 2730;
+
+
+ // Set the V4L2 control to the rounded value
+ sensor->controls[CTRL_FRAME_TIME].value = (chunk_time * chunks + (1<<7)) >> 8;
+
+ // Update the max allowable exposure time
+ // The max allowed is 4 lines less than the total number of lines
+ // We round this up to a whole chunk, just to be safe
+ sensor->controls[CTRL_EXPOSURE].maximum =
+ sensor->controls[CTRL_FRAME_TIME].value - ((chunk_time + (1<<7)) >> 8);
+
+ //printk(KERN_INFO "New max exposure time is %d us\n",
+ // sensor->controls[CTRL_EXPOSURE].maximum);
+
+ if (sensor->controls[CTRL_EXPOSURE].value >
+ sensor->controls[CTRL_EXPOSURE].maximum) {
+ et8ek8_set_exposure(sensor, sensor->controls[CTRL_EXPOSURE].maximum);
+ }
+
+ // do the i2c write
+ if (sensor->power == V4L2_POWER_OFF)
+ return 0;
+
+ return smia_i2c_write_reg(sensor->i2c_client, SMIA_REG_16BIT, 0x1222, swab16(chunks));
+}
+
+
+
static int et8ek8_set_test_pattern(struct et8ek8_sensor *sensor, s32 mode)
{
int cbh_mode, cbv_mode, tp_mode, din_sw, r1420, rval;
@@ -266,6 +409,7 @@
int i;
unsigned int rt; /* Row time in us */
unsigned int clock; /* Pixel clock in Hz>>2 fixed point */
+ unsigned int min; /* The minimum frame time */
if (sensor->current_reglist->mode.pixel_clock <= 0 ||
sensor->current_reglist->mode.width <= 0) {
@@ -278,15 +422,45 @@
rt = sensor->current_reglist->mode.width * (1000000 >> 2);
rt = (rt + (clock >> 1)) / clock;
+ // set the bounds on frame time
+
+ // We need at least enough time to read out all the rows
+ min = rt * sensor->current_reglist->mode.height;
+ sensor->controls[CTRL_FRAME_TIME].minimum = min;
+
+ // The vcount register goes from 84 to 2730, which is a ratio of 2 : 65
+ // Therefore the max frame time is 65/2 * the min frame time
+ sensor->controls[CTRL_FRAME_TIME].maximum = (min * 65) / 2;
+
+ // Vcount specifies chunks of rows of either height 12 or 24,
+ // depending on whether the sensor is reading out 1008 or 2016
+ // rows. 1008/12 = 2016/24 = 84
+ sensor->controls[CTRL_FRAME_TIME].step =
+ rt * (sensor->current_reglist->mode.height / 84);
+
+ // By default we want to run as fast as possible, and yes, we
+ // probably do want to reset it every time we change modes, so
+ // don't keep the old value around.
+ sensor->controls[CTRL_FRAME_TIME].default_value = min;
+ sensor->controls[CTRL_FRAME_TIME].value = min;
+
+ // set the bounds on exposure time
sensor->controls[CTRL_EXPOSURE].minimum = rt;
sensor->controls[CTRL_EXPOSURE].maximum =
sensor->current_reglist->mode.max_exp * rt;
sensor->controls[CTRL_EXPOSURE].step = rt;
+
+ // By default use a long exposure (given a short frame time)
+ // (1/30 for low res, 1/12 for high res)
sensor->controls[CTRL_EXPOSURE].default_value =
sensor->controls[CTRL_EXPOSURE].maximum;
- if (sensor->controls[CTRL_EXPOSURE].value == 0)
+
+ // Initialize exposure to the maximum possible
+ if (sensor->controls[CTRL_EXPOSURE].value == 0) {
sensor->controls[CTRL_EXPOSURE].value =
sensor->controls[CTRL_EXPOSURE].maximum;
+ }
+
/* Adjust V4L2 control values and write them to the sensor */
@@ -416,12 +590,24 @@
.flags = V4L2_CTRL_FLAG_SLIDER,
},
{
+ .id = V4L2_CID_GAIN_EXACT,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Exact gain [5.5 fixed point]",
+ .flags = V4L2_CTRL_FLAG_SLIDER,
+ },
+ {
.id = V4L2_CID_EXPOSURE,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Exposure time [us]",
.flags = V4L2_CTRL_FLAG_SLIDER,
},
{
+ .id = V4L2_CID_FRAME_TIME,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Frame time [us]",
+ .flags = V4L2_CTRL_FLAG_SLIDER,
+ },
+ {
.id = V4L2_CID_TEST_PATTERN,
.type = V4L2_CTRL_TYPE_MENU,
.name = "Test pattern mode",
@@ -967,8 +1153,18 @@
sensor->v4l2_int_device = &et8ek8_int_device;
/* Gain is initialized here permanently */
+
+ /* FCam-style gain (where you specify the multiplier in 5.5 fixed point) */
+ sensor->controls[CTRL_GAIN_EXACT].minimum = 32;
+ sensor->controls[CTRL_GAIN_EXACT].maximum = 1023;
+ sensor->controls[CTRL_GAIN_EXACT].step = 1;
+ sensor->controls[CTRL_GAIN_EXACT].default_value = 32;
+ sensor->controls[CTRL_GAIN_EXACT].value = 32;
+ sensor->controls[CTRL_GAIN_EXACT].set = et8ek8_set_gain_exact;
+
+ /* Old-style gain (where you specify log_2(multiplier)*10 as an integer) */
sensor->controls[CTRL_GAIN].minimum = 0;
- sensor->controls[CTRL_GAIN].maximum = ARRAY_SIZE(et8ek8_gain_table) - 1;
+ sensor->controls[CTRL_GAIN].maximum = ARRAY_SIZE(et8ek8_gain_table) - 1;;
sensor->controls[CTRL_GAIN].step = 1;
sensor->controls[CTRL_GAIN].default_value = 0;
sensor->controls[CTRL_GAIN].value = 0;
@@ -982,6 +1178,14 @@
sensor->controls[CTRL_EXPOSURE].value = 0;
sensor->controls[CTRL_EXPOSURE].set = et8ek8_set_exposure;
+ /* Frame time */
+ sensor->controls[CTRL_FRAME_TIME].minimum = 0;
+ sensor->controls[CTRL_FRAME_TIME].maximum = 0;
+ sensor->controls[CTRL_FRAME_TIME].step = 0;
+ sensor->controls[CTRL_FRAME_TIME].default_value = 0;
+ sensor->controls[CTRL_FRAME_TIME].value = 0;
+ sensor->controls[CTRL_FRAME_TIME].set = et8ek8_set_frame_time;
+
/* Test pattern mode control */
sensor->controls[CTRL_TEST_PATTERN].minimum = et8ek8_ctrls[CTRL_TEST_PATTERN].minimum;
sensor->controls[CTRL_TEST_PATTERN].maximum = et8ek8_ctrls[CTRL_TEST_PATTERN].maximum;
diff -urN PR1.3/drivers/media/video/et8ek8.h PR1.3-fcam/drivers/media/video/et8ek8.h
--- PR1.3/drivers/media/video/et8ek8.h 2010-12-30 11:37:22.000000000 -0500
+++ PR1.3-fcam/drivers/media/video/et8ek8.h 2010-12-30 12:00:23.000000000 -0500
@@ -32,7 +32,13 @@
#define ET8EK8_I2C_ADDR (0x7C >> 1)
#define ET8EK8_PRIV_MEM_SIZE 128
-#define ET8EK8_NCTRLS 3
+
+enum {CTRL_GAIN = 0,
+ CTRL_GAIN_EXACT,
+ CTRL_EXPOSURE,
+ CTRL_FRAME_TIME,
+ CTRL_TEST_PATTERN,
+ ET8EK8_NCTRLS};
struct et8ek8_platform_data {
int (*g_priv)(struct v4l2_int_device *s, void *priv);
diff -urN PR1.3/drivers/media/video/isp/isp.c PR1.3-fcam/drivers/media/video/isp/isp.c
--- PR1.3/drivers/media/video/isp/isp.c 2010-12-30 11:37:22.000000000 -0500
+++ PR1.3-fcam/drivers/media/video/isp/isp.c 2010-12-30 12:00:23.000000000 -0500
@@ -775,10 +775,6 @@
bufs->wait_hs_vs--;
if (irqstatus & HS_VS && bufs->wait_stats && !bufs->wait_hs_vs)
bufs->wait_stats = 0;
-
- if (irqstatus & LSC_PRE_ERR)
- ispccdc_lsc_error_handler(&isp->isp_ccdc);
-
/*
* We need to wait for the first HS_VS interrupt from CCDC.
* Otherwise our frame (and everything else) might be bad.
@@ -838,6 +834,8 @@
if (irqstatus & LSC_PRE_ERR) {
/* Mark buffer faulty. */
buf->vb_state = VIDEOBUF_ERROR;
+ ispccdc_lsc_error_handler(&isp->isp_ccdc);
+ printk(KERN_INFO "lsc prefetch error\n");
dev_dbg(dev, "lsc prefetch error\n");
}
@@ -863,6 +861,7 @@
ISPCSI1_LC01_IRQSTATUS);
if (ispcsi1_irqstatus & ISPCSI1_LC01_ERROR) {
buf->vb_state = VIDEOBUF_ERROR;
+ printk(KERN_INFO "CCP2 error: %x\n", ispcsi1_irqstatus);
dev_dbg(dev, "CCP2 err:%x\n", ispcsi1_irqstatus);
}
}
@@ -894,6 +893,7 @@
else if (!RAW_CAPTURE(isp)) {
if (ispresizer_busy(&isp->isp_res)) {
buf->vb_state = VIDEOBUF_ERROR;
+ printk(KERN_INFO "resizer busy\n");
dev_dbg(dev, "resizer busy.\n");
} else {
ispresizer_config_shadow_registers(
@@ -927,15 +927,18 @@
isp->isp_af.buf_err = 1;
isp->isp_h3a.buf_err = 1;
isp_hist_mark_invalid_buf(&isp->isp_hist);
+ printk(KERN_INFO "sbl overflow %8.8x\n", sbl_pcr);
dev_dbg(dev, "sbl overflow, sbl_pcr = %8.8x\n", sbl_pcr);
}
if (sbl_pcr & ISPSBL_PCR_H3A_AF_WBL_OVF) {
+ printk(KERN_INFO "af: sbl overflow detected.\n");
dev_dbg(dev, "af: sbl overflow detected.\n");
isp->isp_af.buf_err = 1;
}
if (sbl_pcr & ISPSBL_PCR_H3A_AEAWB_WBL_OVF) {
+ printk(KERN_INFO "h3a: sbl overflow detected.\n");
dev_dbg(dev, "h3a: sbl overflow detected.\n");
isp->isp_h3a.buf_err = 1;
}
@@ -948,6 +951,7 @@
isph3a_aewb_config_registers(&isp->isp_h3a);
} else {
ret = -1;
+ printk(KERN_INFO "h3a: cannot process buffer, device is busy\n");
dev_dbg(dev, "h3a: cannot process buffer, device is "
"busy.\n");
}
@@ -964,6 +968,7 @@
isp_af_config_registers(&isp->isp_af);
} else {
ret = -1;
+ printk(KERN_INFO "af: cannot process buffer, device is busy\n");
dev_dbg(dev, "af: cannot process buffer, device is "
"busy.\n");
}
@@ -1815,6 +1820,29 @@
}
EXPORT_SYMBOL(isp_s_ctrl);
+// Get the various internal sizes used by the ISP, to help configure
+// the H3A and HIST engines, which key off internal resolutions not
+// otherwise available to user space.
+int isp_get_pipeline_stats(struct isp_device *isp,
+ struct isp_pipeline_stats *stats) {
+ if (!stats) return -EFAULT;
+
+ stats->ccdc_out_w = isp->pipeline.ccdc_out_w_img;
+ stats->ccdc_out_h = isp->pipeline.ccdc_out_h;
+ stats->prv_out_w = isp->pipeline.prv_out_w_img;
+ stats->prv_out_h = isp->pipeline.prv_out_h_img;
+ stats->rsz_in_x = isp->pipeline.rsz_crop.left;
+ stats->rsz_in_y = isp->pipeline.rsz_crop.top;
+ stats->rsz_in_w = isp->pipeline.rsz_crop.width;
+ stats->rsz_in_h = isp->pipeline.rsz_crop.height;
+ stats->rsz_out_w = isp->pipeline.rsz_out_w_img;
+ stats->rsz_out_h = isp->pipeline.rsz_out_h;
+ stats->rsz_active = isp->pipeline.modules & OMAP_ISP_RESIZER;
+ stats->prv_active = isp->pipeline.modules & OMAP_ISP_PREVIEW;
+
+ return 0;
+}
+
/**
* isp_handle_private - Handle all private ioctls for isp module.
* @cmd: ioctl cmd value
@@ -1834,6 +1862,9 @@
return -EINVAL;
switch (cmd) {
+ case VIDIOC_PRIVATE_ISP_PIPELINE_STATS_REQ:
+ rval = isp_get_pipeline_stats(isp, arg);
+ break;
case VIDIOC_PRIVATE_ISP_CCDC_CFG:
rval = ispccdc_config(&isp->isp_ccdc, arg);
break;
diff -urN PR1.3/drivers/media/video/isp/isp_af.c PR1.3-fcam/drivers/media/video/isp/isp_af.c
--- PR1.3/drivers/media/video/isp/isp_af.c 2010-12-30 11:37:22.000000000 -0500
+++ PR1.3-fcam/drivers/media/video/isp/isp_af.c 2010-12-30 12:00:23.000000000 -0500
@@ -313,6 +313,12 @@
return -EINVAL;
}
+ if (!afconfig->af_config) {
+ /* Don't need to check or even set remaining parameters */
+ isp_af->config.af_config = 0;
+ return 0;
+ }
+
/* Check Parameters */
spin_lock_irqsave(isp_af->lock, irqflags);
result = isp_af_check_params(isp_af, afconfig);
@@ -387,6 +393,8 @@
isp_af->buf_next = ispstat_buf_next(&isp_af->stat);
return ret;
} else {
+ if (isp_af->buf_err) printk(KERN_INFO "af: buf_err %d\n", isp_af->buf_err);
+ else printk(KERN_INFO "af: af not configured\n");
isp_af->buf_err = 0;
return -1;
}
@@ -465,6 +473,15 @@
isp_af->lock = &isp->h3a_lock;
ispstat_init(dev, "AF", &isp_af->stat, H3A_MAX_BUFF, MAX_FRAME_COUNT);
+ // FIXME: For some reason the N900 camera daemon first tries to
+ // allocate buffers while the ISP is running. This naturally
+ // fails. I'm not sure how it ever worked. To help it out, I'll
+ // preallocate some buffers here of the appropriate size. A better
+ // fix would be to figure out when these buffers were allocated in
+ // the vanilla version of the drivers and do something more sane
+ // either in the drivers or the daemon.
+ ispstat_bufs_alloc(&isp_af->stat, 432, 0);
+
return 0;
}
diff -urN PR1.3/drivers/media/video/isp/ispccdc.c PR1.3-fcam/drivers/media/video/isp/ispccdc.c
--- PR1.3/drivers/media/video/isp/ispccdc.c 2010-12-30 11:37:22.000000000 -0500
+++ PR1.3-fcam/drivers/media/video/isp/ispccdc.c 2010-12-30 12:00:23.000000000 -0500
@@ -601,15 +601,6 @@
void ispccdc_lsc_error_handler(struct isp_ccdc_device *isp_ccdc)
{
- /*
- * From OMAP3 TRM: When this event is pending, the module
- * goes into transparent mode (output =input). Normal
- * operation can be resumed at the start of the next frame
- * after:
- * 1) Clearing this event
- * 2) Disabling the LSC module
- * 3) Enabling it
- */
ispccdc_enable_lsc(isp_ccdc, 0);
ispccdc_enable_lsc(isp_ccdc, 1);
}
diff -urN PR1.3/drivers/media/video/isp/isphist.c PR1.3-fcam/drivers/media/video/isp/isphist.c
--- PR1.3/drivers/media/video/isp/isphist.c 2010-12-30 11:37:22.000000000 -0500
+++ PR1.3-fcam/drivers/media/video/isp/isphist.c 2010-12-30 12:00:23.000000000 -0500
@@ -625,6 +625,7 @@
spin_lock_irqsave(&isp_hist->lock, irqflags);
isp_hist->buf_size = size;
+ isp_hist->stat.buf_size = size;
isp_hist->use_dma = use_dma;
isp_hist_update_params(isp_hist, histcfg);
spin_unlock_irqrestore(&isp_hist->lock, irqflags);
diff -urN PR1.3/drivers/media/video/isp/ispreg.h PR1.3-fcam/drivers/media/video/isp/ispreg.h
--- PR1.3/drivers/media/video/isp/ispreg.h 2010-12-30 11:37:22.000000000 -0500
+++ PR1.3-fcam/drivers/media/video/isp/ispreg.h 2010-12-30 12:00:23.000000000 -0500
@@ -1275,7 +1275,7 @@
#define ISPHIST_HV_INFO_MASK 0x3FFF3FFF
-#define ISPCCDC_LSC_ENABLE 1
+#define ISPCCDC_LSC_ENABLE 0
#define ISPCCDC_LSC_GAIN_MODE_N_MASK 0x700
#define ISPCCDC_LSC_GAIN_MODE_N_SHIFT 8
#define ISPCCDC_LSC_GAIN_MODE_M_MASK 0x3800
diff -urN PR1.3/drivers/media/video/isp/ispresizer.c PR1.3-fcam/drivers/media/video/isp/ispresizer.c
--- PR1.3/drivers/media/video/isp/ispresizer.c 2010-12-30 11:37:22.000000000 -0500
+++ PR1.3-fcam/drivers/media/video/isp/ispresizer.c 2010-12-30 12:00:23.000000000 -0500
@@ -287,60 +287,6 @@
}
/**
- * ispresizer_adjust_bandwidth - Reduces read bandwidth when scaling up.
- * Otherwise there will be SBL overflows.
- *
- * The ISP read speed is 256.0 / max(256, 1024 * ISPSBL_SDR_REQ_EXP). This
- * formula is correct, no matter what the TRM says. Thus, the first
- * step to use is 0.25 (REQ_EXP=1).
- *
- * Ratios:
- * 0 = 1.0
- * 1 = 0.25
- * 2 = 0.125
- * 3 = 0.083333...
- * 4 = 0.0625
- * 5 = 0.05 and so on...
- *
- * TRM says that read bandwidth should be no more than 83MB/s, half
- * of the maximum of 166MB/s.
- *
- * HOWEVER, the read speed must be chosen so that the resizer always
- * has time to process the frame before the next frame comes in.
- * Failure to do so will result in a pile-up and endless "resizer busy!"
- * messages.
- *
- * Zoom ratio must not exceed 4.0. This is checked in
- * ispresizer_check_crop_boundaries().
- **/
-static void ispresizer_adjust_bandwidth(struct isp_res_device *isp_res,
- struct isp_pipeline *pipe)
-{
- struct device *dev = to_device(isp_res);
-
- /* Table for dividers. This allows hand tuning. */
- static const unsigned char area_to_divider[] = {
- 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 4, 4, 5, 5, 5
- /* 1........2...........3.......................4 Zoom level */
- };
- unsigned int input_area = pipe->rsz_crop.width * pipe->rsz_crop.height;
- unsigned int output_area = pipe->rsz_out_w * pipe->rsz_out_h;
-
- if (input_area < output_area && input_area > 0) {
- u32 val = area_to_divider[output_area / input_area - 1];
- DPRINTK_ISPRESZ("%s: area factor = %i, val = %i\n",
- __func__, output_area / input_area, val);
- isp_reg_writel(dev, val << ISPSBL_SDR_REQ_RSZ_EXP_SHIFT,
- OMAP3_ISP_IOMEM_SBL, ISPSBL_SDR_REQ_EXP);
- } else {
- /* Required input bandwidth greater than output, no limit. */
- DPRINTK_ISPRESZ("%s: resetting\n", __func__);
- isp_reg_writel(dev, 0, OMAP3_ISP_IOMEM_SBL,
- ISPSBL_SDR_REQ_EXP);
- }
-}
-
-/**
* ispresizer_try_size - Validates input and output images size.
* @input_w: input width for the resizer in number of pixels per line
* @input_h: input height for the resizer in number of lines
@@ -526,9 +472,6 @@
if (rval)
return rval;
- /* Set read bandwidth */
- ispresizer_adjust_bandwidth(isp_res, pipe);
-
/* Set Resizer input address and offset adderss */
ispresizer_config_inlineoffset(isp_res,
pipe->prv_out_w * ISP_BYTES_PER_PIXEL);
diff -urN PR1.3/drivers/media/video/isp/ispstat.c PR1.3-fcam/drivers/media/video/isp/ispstat.c
--- PR1.3/drivers/media/video/isp/ispstat.c 2010-12-30 11:37:22.000000000 -0500
+++ PR1.3-fcam/drivers/media/video/isp/ispstat.c 2010-12-30 12:00:23.000000000 -0500
@@ -50,6 +50,8 @@
stat->active_buf->frame_number = stat->frame_number;
stat->active_buf->buf_size = stat->buf_size;
+ dev_dbg(stat->dev, "Buffer queued: %d\n", stat->frame_number);
+
stat->frame_number++;
if (stat->frame_number == stat->max_frame)
stat->frame_number = 0;
@@ -71,7 +73,6 @@
spin_lock_irqsave(&stat->lock, flags);
if (stat->active_buf) {
- spin_unlock_irqrestore(&stat->lock, flags);
dev_dbg(stat->dev, "%s: new buffer requested without queuing "
"active one.\n", stat->tag);
return stat->active_buf;
@@ -107,11 +108,15 @@
}
/* Get buffer to userspace. */
+#define NEWEST_FRAME 0xFFFF
+#define OLDEST_FRAME 0xFFFE
static struct ispstat_buffer *ispstat_buf_find(
struct ispstat *stat, u32 frame_number)
{
int i;
+ struct ispstat_buffer *best = NULL;
+
for (i = 0; i < stat->nbufs; i++) {
struct ispstat_buffer *curr = &stat->buf[i];
@@ -126,9 +131,17 @@
/* Found correct number. */
if (curr->frame_number == frame_number)
return curr;
+
+ /* Found newer (or older) buffer */
+ if ((frame_number == NEWEST_FRAME &&
+ (best == NULL || timeval_compare(&best->ts, &curr->ts) <= 0)) ||
+ (frame_number == OLDEST_FRAME &&
+ (best == NULL || timeval_compare(&best->ts, &curr->ts) >= 0))) {
+ best = curr;
+ }
}
- return NULL;
+ return best;
}
/**
diff -urN PR1.3/drivers/media/video/isp/omap_previewer_hack.c PR1.3-fcam/drivers/media/video/isp/omap_previewer_hack.c
--- PR1.3/drivers/media/video/isp/omap_previewer_hack.c 2010-12-30 11:37:22.000000000 -0500
+++ PR1.3-fcam/drivers/media/video/isp/omap_previewer_hack.c 2010-12-30 12:00:23.000000000 -0500
@@ -486,21 +486,6 @@
if (vb->memory == V4L2_MEMORY_MMAP)
return 0;
- if (current->flags & PF_EXITING) {
- /*
- * task is getting shutdown.
- * current->mm could have been released.
- *
- * For locking, we return error.
- * For unlocking, the subsequent release of
- * buffer should set things right
- */
- if (lock)
- return -EINVAL;
- else
- return 0;
- }
-
end = vb->baddr + vb->bsize;
down_write(¤t->mm->mmap_sem);
diff -urN PR1.3/drivers/media/video/omap34xxcam-daemon-req.c PR1.3-fcam/drivers/media/video/omap34xxcam-daemon-req.c
--- PR1.3/drivers/media/video/omap34xxcam-daemon-req.c 2010-12-30 11:37:22.000000000 -0500
+++ PR1.3-fcam/drivers/media/video/omap34xxcam-daemon-req.c 2010-12-30 12:00:23.000000000 -0500
@@ -108,6 +108,7 @@
struct omap34xxcam_daemon_daemon_req *get)
{
struct omap34xxcam_daemon *d = &vdev->daemon;
+ struct omap34xxcam_daemon_event e;
unsigned long flags;
int rval = 0;
@@ -117,10 +118,17 @@
get->req.size = sizeof(d->event);
get->req.type = OMAP34XXCAM_DAEMON_REQ_EVENTS;
+ // Shouldn't copy_to_user inside a spin lock, so copy to a
+ // temp buffer, then copy that to user space outside the spin lock
+
spin_lock_irqsave(&d->event_lock, flags);
- if (copy_to_user(get->req.blob, &d->event, sizeof(d->event)))
- rval = -EFAULT;
+ e = d->event;
spin_unlock_irqrestore(&d->event_lock, flags);
+ if (copy_to_user(get->req.blob, &e, sizeof(e))) {
+ rval = -EFAULT;
+ }
+
+
return rval;
}
diff -urN PR1.3/drivers/media/video/omap34xxcam-daemon.c PR1.3-fcam/drivers/media/video/omap34xxcam-daemon.c
--- PR1.3/drivers/media/video/omap34xxcam-daemon.c 2010-12-30 11:37:22.000000000 -0500
+++ PR1.3-fcam/drivers/media/video/omap34xxcam-daemon.c 2010-12-30 12:00:23.000000000 -0500
@@ -355,6 +355,19 @@
unsigned long flags;
u32 event = 0;
+ /* When FCam is running, parasitically subvert this callback */
+ if (vdev->fcam.file != NULL) {
+ if (status & HS_VS) {
+ /* Make a note of the time */
+ do_gettimeofday(&vdev->fcam.timestamp);
+
+ /* Wake up any waiting fcam client */
+ if (down_trylock(&vdev->fcam.sem));
+ up(&vdev->fcam.sem);
+ }
+ return;
+ }
+
if (status & HIST_DONE)
event |= OMAP34XXCAM_DAEMON_EVENT_HIST_DONE;
if (status & H3A_AWB_DONE)
diff -urN PR1.3/drivers/media/video/omap34xxcam-fcam.h PR1.3-fcam/drivers/media/video/omap34xxcam-fcam.h
--- PR1.3/drivers/media/video/omap34xxcam-fcam.h 1969-12-31 19:00:00.000000000 -0500
+++ PR1.3-fcam/drivers/media/video/omap34xxcam-fcam.h 2010-12-30 12:00:23.000000000 -0500
@@ -0,0 +1,58 @@
+/*
+ * fcam-drivers/omap34xxcam-fcam.h
+ *
+ * Copyright (C) 2010 Stanford University
+ *
+ * Contact: Andrew Adams <abadams@stanford.edu>
+ *
+ * Additional V4L2 ioctls and custom controls for the FCam camera control API.
+ *
+ * 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.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef OMAP34XXCAM_FCAM_H
+#define OMAP34XXCAM_FCAM_H
+
+#include <linux/videodev2.h>
+
+/* VIDIOCS, for kernel space and user space */
+#define VIDIOC_FCAM_INSTALL _IO('V', BASE_VIDIOC_PRIVATE + 25)
+#define VIDIOC_FCAM_WAIT_FOR_HS_VS _IOWR('V', BASE_VIDIOC_PRIVATE + 26, struct timeval)
+
+/* Extra sensor controls */
+#define V4L2_CID_FRAME_TIME (V4L2_CTRL_CLASS_CAMERA | 0x10ff)
+#define V4L2_CID_GAIN_EXACT (V4L2_CTRL_CLASS_CAMERA | 0x10fe)
+
+/* Kernel-side structures */
+
+#ifdef __KERNEL__
+
+struct omap34xxcam_fcam_client {
+ /* The file handle of the fcam process (or NULL if there isn't one) */
+ struct file *file;
+
+ /* A semaphore to use for waiting for HS_VS */
+ struct semaphore sem;
+
+ /* Timestamp of most recent HS_VS */
+ struct timeval timestamp;
+
+ /* TODO: Add a next pointer, to make a linked list in order to handle multiple fcam clients */
+};
+
+#endif
+
+#endif
diff -urN PR1.3/drivers/media/video/omap34xxcam.c PR1.3-fcam/drivers/media/video/omap34xxcam.c
--- PR1.3/drivers/media/video/omap34xxcam.c 2010-12-30 11:37:22.000000000 -0500
+++ PR1.3-fcam/drivers/media/video/omap34xxcam.c 2010-12-30 12:00:23.000000000 -0500
@@ -34,6 +34,7 @@
#include <linux/videodev2.h>
#include <linux/version.h>
+#include <linux/sched.h>
#include <asm/pgalloc.h>
#include <media/v4l2-common.h>
@@ -47,6 +48,12 @@
/* global variables */
static struct omap34xxcam_device *omap34xxcam;
+/* How large should the V4L2 circular buffer be? The default is large
+ * enough for 4 5MP buffers at 16bpp */
+
+unsigned int circular_buffer_size = 2600*2000*4*2;
+module_param(circular_buffer_size, uint, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
+
/*
*
* Sensor handling.
@@ -154,8 +161,21 @@
*size = vdev->pix.sizeimage;
- while (*size * *cnt > fh->vdev->vdev_sensor_config.capture_mem)
+ {
+ // Limit the circular buffer size to the larger of the default
+ // value declared in the board file, and the value passed on the
+ // command-line on module load. This is necessary because for some
+ // apps, the V4L2 default is not large enough (e.g. if the user
+ // wants 8 page-locked 5MP buffers to capture a really quick
+ // burst reliably, they should be able to configure the module to
+ // do that).
+ int max_size = circular_buffer_size;
+ int default_size = fh->vdev->vdev_sensor_config.capture_mem;
+ if (max_size < default_size) max_size = default_size;
+
+ while (*size * *cnt > max_size)
(*cnt)--;
+ }
return isp_vbq_setup(vdev->cam->isp, vbq, cnt, size);
}
@@ -674,9 +694,9 @@
if (vdev->vdev_sensor == v4l2_int_device_dummy())
return -EINVAL;
- omap34xxcam_daemon_req_hw_reconfig(
- vdev,
- OMAP34XXCAM_DAEMON_HW_RECONFIG_FMT);
+ if (vdev->fcam.file == NULL) {
+ omap34xxcam_daemon_req_hw_reconfig(vdev, OMAP34XXCAM_DAEMON_HW_RECONFIG_FMT);
+ }
mutex_lock(&vdev->mutex);
if (vdev->streaming) {
@@ -891,10 +911,11 @@
out:
mutex_unlock(&vdev->mutex);
- if (!rval)
+ if (!rval && vdev->fcam.file == NULL) {
omap34xxcam_daemon_req_hw_reconfig(
vdev,
OMAP34XXCAM_DAEMON_HW_RECONFIG_STREAMON);
+ }
return rval;
}
@@ -918,8 +939,10 @@
struct videobuf_queue *q = &ofh->vbq;
int rval;
+ if (vdev->fcam.file == NULL) {
omap34xxcam_daemon_req_hw_reconfig(
vdev, OMAP34XXCAM_DAEMON_HW_RECONFIG_STREAMOFF);
+ }
mutex_lock(&vdev->mutex);
@@ -1333,9 +1356,11 @@
if (vdev->vdev_sensor == v4l2_int_device_dummy())
return -EINVAL;
+ if (vdev->fcam.file == NULL) {
omap34xxcam_daemon_req_hw_reconfig(
vdev,
OMAP34XXCAM_DAEMON_HW_RECONFIG_CROP);
+ }
mutex_lock(&vdev->mutex);
@@ -1384,8 +1409,7 @@
pix_out.height = frms->discrete.height;
pix_out.pixelformat = frms->pixel_format;
- ival.numerator = 0;
- ival.denominator = 0;
+ ival = vdev->want_timeperframe;
rval = try_pix_parm(vdev, &pix_in, &pix_out, &ival);
if (rval < 0)
goto done;
@@ -1406,8 +1430,8 @@
struct v4l2_frmsizeenum frms;
unsigned int frmi_width;
unsigned int frmi_height;
- unsigned int width;
- unsigned int height;
+ unsigned int width = 0;
+ unsigned int height = 0;
unsigned int max_dist;
unsigned int dist;
u32 pixel_format;
@@ -1477,6 +1501,74 @@
return rval;
}
+static int omap34xxcam_fcam_remove(struct omap34xxcam_videodev *vdev)
+{
+ if (vdev->fcam.file == NULL) return 0;
+
+ /* Release any waiting threads. */
+ up(&vdev->fcam.sem);
+
+ vdev->fcam.file = NULL;
+
+ return 0;
+}
+
+static int omap34xxcam_fcam_install(struct omap34xxcam_videodev *vdev, struct file *file)
+{
+ mutex_lock(&vdev->mutex);
+
+ /* If there's already an fcam daemon, bail out. */
+ if (vdev->fcam.file) {
+ mutex_unlock(&vdev->mutex);
+ return -EBUSY;
+ }
+
+ /* Register myself as the fcam daemon. */
+ vdev->fcam.file = file;
+
+ /* Initialize the semaphore to zero */
+ sema_init(&vdev->fcam.sem, 0);
+
+ /* Drop us from use count, except the modules. */
+ if (atomic_dec_return(&vdev->users) == 0) {
+ omap34xxcam_slave_power_set(vdev, V4L2_POWER_OFF,
+ OMAP34XXCAM_SLAVE_POWER_ALL);
+ isp_put();
+ }
+ mutex_unlock(&vdev->mutex);
+
+
+ /* FIXME: Give the calling process SYS_CAP_NICE so it can
+ launch the real-time threads necessary to make FCam
+ work. This effectively gives all user code access to
+ real-time priority threads. A better solution would be
+ nice. */
+ cap_raise(current->cap_effective, CAP_SYS_NICE);
+
+ return 0;
+}
+
+static int omap34xxcam_fcam_wait_for_hs_vs(struct omap34xxcam_videodev *vdev,
+ struct file *file,
+ struct timeval *stamp)
+{
+ int rval;
+
+ if (file != vdev->fcam.file) {
+ printk(KERN_ERR "Non-fcam client trying to wait for hs_vs\n");
+ return -EINVAL;
+ }
+
+ if ((rval = down_interruptible(&vdev->fcam.sem))) {
+ printk(KERN_ERR "FCam client interrupted while waiting for HS_VS\n");
+ return rval;
+ }
+
+ *stamp = vdev->fcam.timestamp;
+
+ return 0;
+}
+
/**
* vidioc_default - private IOCTL handler
* @file: ptr. to system file structure
@@ -1498,10 +1590,29 @@
struct device *isp = vdev->cam->isp;
int rval;
+ //printk(KERN_INFO "VIDIOC_DEFAULT: %x %x\n", cmd, arg);
+ //printk(KERN_INFO "%x %x\n", VIDIOC_FCAM_INSTALL, VIDIOC_FCAM_WAIT_FOR_HS_VS);
+
+ /* Intercept all VIDIOCs by the daemon while FCam is running */
+ if (vdev->fcam.file != NULL && file == vdev->daemon.file) {
+ //printk(KERN_INFO "Discarded VIDIOC from daemon because FCam is running\n");
+ // sleep for 100 jiffies
+ schedule_timeout_interruptible(100);
+ rval = -EBUSY;
+ goto out;
+ }
+
if (vdev->vdev_sensor_config.sensor_isp) {
+ printk(KERN_ERR "Smart sensor, so ignoring vidioc\n");
rval = -EINVAL;
} else {
switch (cmd) {
+ case VIDIOC_FCAM_INSTALL:
+ rval = omap34xxcam_fcam_install(vdev, file);
+ goto out;
+ case VIDIOC_FCAM_WAIT_FOR_HS_VS:
+ rval = omap34xxcam_fcam_wait_for_hs_vs(vdev, file, arg);
+ goto out;
case VIDIOC_ENUM_FRAMESIZES:
rval = vidioc_enum_framesizes(file, fh, arg);
goto out;
@@ -1624,6 +1735,13 @@
unsigned long flags;
u32 pending;
+ if (vdev->fcam.file != NULL) {
+ //printk(KERN_INFO "daemon called poll while fcam running. Rejecting daemon.\n");
+ // sleep for 100 jiffies
+ schedule_timeout_interruptible(100);
+ return POLLERR;
+ }
+
poll_wait(file, &vdev->daemon.poll_wait, wait);
spin_lock_irqsave(&vdev->daemon.event_lock, flags);
@@ -1774,6 +1892,7 @@
out_no_pix:
mutex_unlock(&vdev->mutex);
+ /*
if (first_user && vdev->daemon.file) {
rval = omap34xxcam_daemon_req_hw_init(vdev);
if (rval) {
@@ -1781,6 +1900,7 @@
goto out_slave_power_set_standby;
}
}
+ */
file->private_data = fh;
@@ -1838,6 +1958,11 @@
if (omap34xxcam_daemon_release(vdev, file))
goto daemon_out;
+ if (vdev->fcam.file == file) {
+ omap34xxcam_fcam_remove(vdev);
+ goto daemon_out;
+ }
+
mutex_lock(&vdev->mutex);
if (vdev->streaming == file) {
isp_stop(isp);
@@ -2148,6 +2273,10 @@
vdev->vdev_lens =
vdev->vdev_flash = v4l2_int_device_dummy();
+ /* Initialize FCam stuff */
+ sema_init(&vdev->fcam.sem, 0);
+ vdev->fcam.file = NULL;
+
if (v4l2_int_device_register(m))
goto err;
}
diff -urN PR1.3/drivers/media/video/omap34xxcam.h PR1.3-fcam/drivers/media/video/omap34xxcam.h
--- PR1.3/drivers/media/video/omap34xxcam.h 2010-12-30 11:37:22.000000000 -0500
+++ PR1.3-fcam/drivers/media/video/omap34xxcam.h 2010-12-30 12:00:23.000000000 -0500
@@ -37,6 +37,7 @@
#include <media/v4l2-int-device.h>
#include <linux/omap34xxcam-daemon.h>
+#include "omap34xxcam-fcam.h"
#include "isp/isp.h"
#define CAM_NAME "omap34xxcam"
@@ -130,6 +131,8 @@
struct omap34xxcam_daemon daemon;
+ struct omap34xxcam_fcam_client fcam;
+
/* number of slaves attached */
int slaves;