#include <linux/videodev2.h>
#include <media/tuner.h>
#include <media/v4l2-common.h>
+#include <media/v4l2-event.h>
#include "go7007-priv.h"
int go7007_start_encoder(struct go7007 *go)
{
u8 *fw;
- int fw_len, rv = 0, i;
+ int fw_len, rv = 0, i, x, y;
u16 intr_val, intr_data;
go->modet_enable = 0;
- if (!go->dvd_mode)
- for (i = 0; i < 4; ++i) {
- if (go->modet[i].enable) {
- go->modet_enable = 1;
- continue;
+ for (i = 0; i < 4; i++)
+ go->modet[i].enable = 0;
+
+ switch (v4l2_ctrl_g_ctrl(go->modet_mode)) {
+ case V4L2_DETECT_MD_MODE_GLOBAL:
+ memset(go->modet_map, 0, sizeof(go->modet_map));
+ go->modet[0].enable = 1;
+ go->modet_enable = 1;
+ break;
+ case V4L2_DETECT_MD_MODE_REGION_GRID:
+ for (y = 0; y < go->height / 16; y++) {
+ for (x = 0; x < go->width / 16; x++) {
+ int idx = y * go->width / 16 + x;
+
+ go->modet[go->modet_map[idx]].enable = 1;
}
- go->modet[i].pixel_threshold = 32767;
- go->modet[i].motion_threshold = 32767;
- go->modet[i].mb_threshold = 32767;
}
+ go->modet_enable = 1;
+ break;
+ }
+
+ if (go->dvd_mode)
+ go->modet_enable = 0;
if (go7007_construct_fw_image(go, &fw, &fw_len) < 0)
return -1;
}
}
+static void go7007_set_motion_regions(struct go7007 *go, struct go7007_buffer *vb,
+ u32 motion_regions)
+{
+ if (motion_regions != go->modet_event_status) {
+ struct v4l2_event ev = {
+ .type = V4L2_EVENT_MOTION_DET,
+ .u.motion_det = {
+ .flags = V4L2_EVENT_MD_FL_HAVE_FRAME_SEQ,
+ .frame_sequence = vb->vb.v4l2_buf.sequence,
+ .region_mask = motion_regions,
+ },
+ };
+
+ v4l2_event_queue(&go->vdev, &ev);
+ go->modet_event_status = motion_regions;
+ }
+}
+
/*
- * Deliver the last video buffer and get a new one to start writing to.
+ * Determine regions with motion and send a motion detection event
+ * in case of changes.
*/
-static struct go7007_buffer *frame_boundary(struct go7007 *go, struct go7007_buffer *vb)
+static void go7007_motion_regions(struct go7007 *go, struct go7007_buffer *vb)
{
- struct go7007_buffer *vb_tmp = NULL;
u32 *bytesused = &vb->vb.v4l2_planes[0].bytesused;
+ unsigned motion[4] = { 0, 0, 0, 0 };
+ u32 motion_regions = 0;
+ unsigned stride = (go->width + 7) >> 3;
+ unsigned x, y;
int i;
- if (vb) {
- if (vb->modet_active) {
- if (*bytesused + 216 < GO7007_BUF_SIZE) {
- for (i = 0; i < 216; ++i)
- store_byte(vb, go->active_map[i]);
- *bytesused -= 216;
- } else
- vb->modet_active = 0;
+ for (i = 0; i < 216; ++i)
+ store_byte(vb, go->active_map[i]);
+ for (y = 0; y < go->height / 16; y++) {
+ for (x = 0; x < go->width / 16; x++) {
+ if (!(go->active_map[y * stride + (x >> 3)] & (1 << (x & 7))))
+ continue;
+ motion[go->modet_map[y * (go->width / 16) + x]]++;
}
- vb->vb.v4l2_buf.sequence = go->next_seq++;
- v4l2_get_timestamp(&vb->vb.v4l2_buf.timestamp);
- vb_tmp = vb;
+ }
+ motion_regions = ((motion[0] > 0) << 0) |
+ ((motion[1] > 0) << 1) |
+ ((motion[2] > 0) << 2) |
+ ((motion[3] > 0) << 3);
+ *bytesused -= 216;
+ go7007_set_motion_regions(go, vb, motion_regions);
+}
+
+/*
+ * Deliver the last video buffer and get a new one to start writing to.
+ */
+static struct go7007_buffer *frame_boundary(struct go7007 *go, struct go7007_buffer *vb)
+{
+ u32 *bytesused = &vb->vb.v4l2_planes[0].bytesused;
+ struct go7007_buffer *vb_tmp = NULL;
+
+ if (vb == NULL) {
spin_lock(&go->spinlock);
- list_del(&vb->list);
- if (list_empty(&go->vidq_active))
- vb = NULL;
- else
- vb = list_first_entry(&go->vidq_active, struct go7007_buffer, list);
- go->active_buf = vb;
+ if (!list_empty(&go->vidq_active))
+ vb = go->active_buf =
+ list_first_entry(&go->vidq_active, struct go7007_buffer, list);
spin_unlock(&go->spinlock);
- vb2_buffer_done(&vb_tmp->vb, VB2_BUF_STATE_DONE);
+ go->next_seq++;
return vb;
}
+
+ vb->vb.v4l2_buf.sequence = go->next_seq++;
+ if (vb->modet_active && *bytesused + 216 < GO7007_BUF_SIZE)
+ go7007_motion_regions(go, vb);
+ else
+ go7007_set_motion_regions(go, vb, 0);
+
+ v4l2_get_timestamp(&vb->vb.v4l2_buf.timestamp);
+ vb_tmp = vb;
spin_lock(&go->spinlock);
- if (!list_empty(&go->vidq_active))
- vb = go->active_buf =
- list_first_entry(&go->vidq_active, struct go7007_buffer, list);
+ list_del(&vb->list);
+ if (list_empty(&go->vidq_active))
+ vb = NULL;
+ else
+ vb = list_first_entry(&go->vidq_active, struct go7007_buffer, list);
+ go->active_buf = vb;
spin_unlock(&go->spinlock);
- go->next_seq++;
+ vb2_buffer_done(&vb_tmp->vb, VB2_BUF_STATE_DONE);
return vb;
}
static int modet_to_package(struct go7007 *go, __le16 *code, int space)
{
+ bool has_modet0 = go->modet[0].enable;
+ bool has_modet1 = go->modet[1].enable;
+ bool has_modet2 = go->modet[2].enable;
+ bool has_modet3 = go->modet[3].enable;
int ret, mb, i, addr, cnt = 0;
u16 pack[32];
u16 thresholds[] = {
0x200e, 0,
- 0xbf82, go->modet[0].pixel_threshold,
- 0xbf83, go->modet[1].pixel_threshold,
- 0xbf84, go->modet[2].pixel_threshold,
- 0xbf85, go->modet[3].pixel_threshold,
- 0xbf86, go->modet[0].motion_threshold,
- 0xbf87, go->modet[1].motion_threshold,
- 0xbf88, go->modet[2].motion_threshold,
- 0xbf89, go->modet[3].motion_threshold,
- 0xbf8a, go->modet[0].mb_threshold,
- 0xbf8b, go->modet[1].mb_threshold,
- 0xbf8c, go->modet[2].mb_threshold,
- 0xbf8d, go->modet[3].mb_threshold,
+ 0xbf82, has_modet0 ? go->modet[0].pixel_threshold : 32767,
+ 0xbf83, has_modet1 ? go->modet[1].pixel_threshold : 32767,
+ 0xbf84, has_modet2 ? go->modet[2].pixel_threshold : 32767,
+ 0xbf85, has_modet3 ? go->modet[3].pixel_threshold : 32767,
+ 0xbf86, has_modet0 ? go->modet[0].motion_threshold : 32767,
+ 0xbf87, has_modet1 ? go->modet[1].motion_threshold : 32767,
+ 0xbf88, has_modet2 ? go->modet[2].motion_threshold : 32767,
+ 0xbf89, has_modet3 ? go->modet[3].motion_threshold : 32767,
+ 0xbf8a, has_modet0 ? go->modet[0].mb_threshold : 32767,
+ 0xbf8b, has_modet1 ? go->modet[1].mb_threshold : 32767,
+ 0xbf8c, has_modet2 ? go->modet[2].mb_threshold : 32767,
+ 0xbf8d, has_modet3 ? go->modet[3].mb_threshold : 32767,
0xbf8e, 0,
0xbf8f, 0,
0, 0,
#include <media/videobuf2-vmalloc.h>
#include <media/saa7115.h>
-#include "go7007.h"
#include "go7007-priv.h"
#define call_all(dev, o, f, args...) \
static int set_capture_size(struct go7007 *go, struct v4l2_format *fmt, int try)
{
int sensor_height = 0, sensor_width = 0;
- int width, height, i;
+ int width, height;
if (fmt != NULL && !valid_pixelformat(fmt->fmt.pix.pixelformat))
return -EINVAL;
go->height = height;
go->encoder_h_offset = go->board_info->sensor_h_offset;
go->encoder_v_offset = go->board_info->sensor_v_offset;
- for (i = 0; i < 4; ++i)
- go->modet[i].enable = 0;
- for (i = 0; i < 1624; ++i)
- go->modet_map[i] = 0;
if (go->board_info->sensor_flags & GO7007_SENSOR_SCALING) {
struct v4l2_mbus_framefmt mbus_fmt;
return 0;
}
-#if 0
-static int clip_to_modet_map(struct go7007 *go, int region,
- struct v4l2_clip *clip_list)
-{
- struct v4l2_clip clip, *clip_ptr;
- int x, y, mbnum;
-
- /* Check if coordinates are OK and if any macroblocks are already
- * used by other regions (besides 0) */
- clip_ptr = clip_list;
- while (clip_ptr) {
- if (copy_from_user(&clip, clip_ptr, sizeof(clip)))
- return -EFAULT;
- if (clip.c.left < 0 || (clip.c.left & 0xF) ||
- clip.c.width <= 0 || (clip.c.width & 0xF))
- return -EINVAL;
- if (clip.c.left + clip.c.width > go->width)
- return -EINVAL;
- if (clip.c.top < 0 || (clip.c.top & 0xF) ||
- clip.c.height <= 0 || (clip.c.height & 0xF))
- return -EINVAL;
- if (clip.c.top + clip.c.height > go->height)
- return -EINVAL;
- for (y = 0; y < clip.c.height; y += 16)
- for (x = 0; x < clip.c.width; x += 16) {
- mbnum = (go->width >> 4) *
- ((clip.c.top + y) >> 4) +
- ((clip.c.left + x) >> 4);
- if (go->modet_map[mbnum] != 0 &&
- go->modet_map[mbnum] != region)
- return -EBUSY;
- }
- clip_ptr = clip.next;
- }
-
- /* Clear old region macroblocks */
- for (mbnum = 0; mbnum < 1624; ++mbnum)
- if (go->modet_map[mbnum] == region)
- go->modet_map[mbnum] = 0;
-
- /* Claim macroblocks in this list */
- clip_ptr = clip_list;
- while (clip_ptr) {
- if (copy_from_user(&clip, clip_ptr, sizeof(clip)))
- return -EFAULT;
- for (y = 0; y < clip.c.height; y += 16)
- for (x = 0; x < clip.c.width; x += 16) {
- mbnum = (go->width >> 4) *
- ((clip.c.top + y) >> 4) +
- ((clip.c.left + x) >> 4);
- go->modet_map[mbnum] = region;
- }
- clip_ptr = clip.next;
- }
- return 0;
-}
-#endif
-
static int vidioc_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
mutex_lock(&go->hw_lock);
go->next_seq = 0;
go->active_buf = NULL;
+ go->modet_event_status = 0;
q->streaming = 1;
if (go7007_start_encoder(go) < 0)
ret = -EIO;
return call_all(&go->v4l2_dev, core, log_status);
}
-/* FIXME:
- Those ioctls are private, and not needed, since several standard
- extended controls already provide streaming control.
- So, those ioctls should be converted into vidioc_g_ext_ctrls()
- and vidioc_s_ext_ctrls()
- */
-
-#if 0
- case GO7007IOC_S_MD_PARAMS:
- {
- struct go7007_md_params *mdp = arg;
+static int vidioc_subscribe_event(struct v4l2_fh *fh,
+ const struct v4l2_event_subscription *sub)
+{
- if (mdp->region > 3)
- return -EINVAL;
- if (mdp->trigger > 0) {
- go->modet[mdp->region].pixel_threshold =
- mdp->pixel_threshold >> 1;
- go->modet[mdp->region].motion_threshold =
- mdp->motion_threshold >> 1;
- go->modet[mdp->region].mb_threshold =
- mdp->trigger >> 1;
- go->modet[mdp->region].enable = 1;
- } else
- go->modet[mdp->region].enable = 0;
- /* fall-through */
+ switch (sub->type) {
+ case V4L2_EVENT_CTRL:
+ return v4l2_ctrl_subscribe_event(fh, sub);
+ case V4L2_EVENT_MOTION_DET:
+ /* Allow for up to 30 events (1 second for NTSC) to be
+ * stored. */
+ return v4l2_event_subscribe(fh, sub, 30, NULL);
}
- case GO7007IOC_S_MD_REGION:
- {
- struct go7007_md_region *region = arg;
+ return -EINVAL;
+}
- if (region->region < 1 || region->region > 3)
- return -EINVAL;
- return clip_to_modet_map(go, region->region, region->clips);
+
+static int go7007_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct go7007 *go =
+ container_of(ctrl->handler, struct go7007, hdl);
+ unsigned y;
+ u8 *mt;
+
+ switch (ctrl->id) {
+ case V4L2_CID_PIXEL_THRESHOLD0:
+ go->modet[0].pixel_threshold = ctrl->val;
+ break;
+ case V4L2_CID_MOTION_THRESHOLD0:
+ go->modet[0].motion_threshold = ctrl->val;
+ break;
+ case V4L2_CID_MB_THRESHOLD0:
+ go->modet[0].mb_threshold = ctrl->val;
+ break;
+ case V4L2_CID_PIXEL_THRESHOLD1:
+ go->modet[1].pixel_threshold = ctrl->val;
+ break;
+ case V4L2_CID_MOTION_THRESHOLD1:
+ go->modet[1].motion_threshold = ctrl->val;
+ break;
+ case V4L2_CID_MB_THRESHOLD1:
+ go->modet[1].mb_threshold = ctrl->val;
+ break;
+ case V4L2_CID_PIXEL_THRESHOLD2:
+ go->modet[2].pixel_threshold = ctrl->val;
+ break;
+ case V4L2_CID_MOTION_THRESHOLD2:
+ go->modet[2].motion_threshold = ctrl->val;
+ break;
+ case V4L2_CID_MB_THRESHOLD2:
+ go->modet[2].mb_threshold = ctrl->val;
+ break;
+ case V4L2_CID_PIXEL_THRESHOLD3:
+ go->modet[3].pixel_threshold = ctrl->val;
+ break;
+ case V4L2_CID_MOTION_THRESHOLD3:
+ go->modet[3].motion_threshold = ctrl->val;
+ break;
+ case V4L2_CID_MB_THRESHOLD3:
+ go->modet[3].mb_threshold = ctrl->val;
+ break;
+ case V4L2_CID_DETECT_MD_REGION_GRID:
+ mt = go->modet_map;
+ for (y = 0; y < go->height / 16; y++, mt += go->width / 16)
+ memcpy(mt, ctrl->p_new.p_u8 + y * (720 / 16), go->width / 16);
+ break;
+ default:
+ return -EINVAL;
}
-#endif
+ return 0;
+}
static struct v4l2_file_operations go7007_fops = {
.owner = THIS_MODULE,
.vidioc_enum_framesizes = vidioc_enum_framesizes,
.vidioc_enum_frameintervals = vidioc_enum_frameintervals,
.vidioc_log_status = vidioc_log_status,
- .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_subscribe_event = vidioc_subscribe_event,
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
.tvnorms = V4L2_STD_ALL,
};
+static const struct v4l2_ctrl_ops go7007_ctrl_ops = {
+ .s_ctrl = go7007_s_ctrl,
+};
+
+static const struct v4l2_ctrl_config go7007_pixel_threshold0_ctrl = {
+ .ops = &go7007_ctrl_ops,
+ .id = V4L2_CID_PIXEL_THRESHOLD0,
+ .name = "Pixel Threshold Region 0",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .def = 20,
+ .max = 32767,
+ .step = 1,
+};
+
+static const struct v4l2_ctrl_config go7007_motion_threshold0_ctrl = {
+ .ops = &go7007_ctrl_ops,
+ .id = V4L2_CID_MOTION_THRESHOLD0,
+ .name = "Motion Threshold Region 0",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .def = 80,
+ .max = 32767,
+ .step = 1,
+};
+
+static const struct v4l2_ctrl_config go7007_mb_threshold0_ctrl = {
+ .ops = &go7007_ctrl_ops,
+ .id = V4L2_CID_MB_THRESHOLD0,
+ .name = "MB Threshold Region 0",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .def = 200,
+ .max = 32767,
+ .step = 1,
+};
+
+static const struct v4l2_ctrl_config go7007_pixel_threshold1_ctrl = {
+ .ops = &go7007_ctrl_ops,
+ .id = V4L2_CID_PIXEL_THRESHOLD1,
+ .name = "Pixel Threshold Region 1",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .def = 20,
+ .max = 32767,
+ .step = 1,
+};
+
+static const struct v4l2_ctrl_config go7007_motion_threshold1_ctrl = {
+ .ops = &go7007_ctrl_ops,
+ .id = V4L2_CID_MOTION_THRESHOLD1,
+ .name = "Motion Threshold Region 1",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .def = 80,
+ .max = 32767,
+ .step = 1,
+};
+
+static const struct v4l2_ctrl_config go7007_mb_threshold1_ctrl = {
+ .ops = &go7007_ctrl_ops,
+ .id = V4L2_CID_MB_THRESHOLD1,
+ .name = "MB Threshold Region 1",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .def = 200,
+ .max = 32767,
+ .step = 1,
+};
+
+static const struct v4l2_ctrl_config go7007_pixel_threshold2_ctrl = {
+ .ops = &go7007_ctrl_ops,
+ .id = V4L2_CID_PIXEL_THRESHOLD2,
+ .name = "Pixel Threshold Region 2",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .def = 20,
+ .max = 32767,
+ .step = 1,
+};
+
+static const struct v4l2_ctrl_config go7007_motion_threshold2_ctrl = {
+ .ops = &go7007_ctrl_ops,
+ .id = V4L2_CID_MOTION_THRESHOLD2,
+ .name = "Motion Threshold Region 2",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .def = 80,
+ .max = 32767,
+ .step = 1,
+};
+
+static const struct v4l2_ctrl_config go7007_mb_threshold2_ctrl = {
+ .ops = &go7007_ctrl_ops,
+ .id = V4L2_CID_MB_THRESHOLD2,
+ .name = "MB Threshold Region 2",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .def = 200,
+ .max = 32767,
+ .step = 1,
+};
+
+static const struct v4l2_ctrl_config go7007_pixel_threshold3_ctrl = {
+ .ops = &go7007_ctrl_ops,
+ .id = V4L2_CID_PIXEL_THRESHOLD3,
+ .name = "Pixel Threshold Region 3",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .def = 20,
+ .max = 32767,
+ .step = 1,
+};
+
+static const struct v4l2_ctrl_config go7007_motion_threshold3_ctrl = {
+ .ops = &go7007_ctrl_ops,
+ .id = V4L2_CID_MOTION_THRESHOLD3,
+ .name = "Motion Threshold Region 3",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .def = 80,
+ .max = 32767,
+ .step = 1,
+};
+
+static const struct v4l2_ctrl_config go7007_mb_threshold3_ctrl = {
+ .ops = &go7007_ctrl_ops,
+ .id = V4L2_CID_MB_THRESHOLD3,
+ .name = "MB Threshold Region 3",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .def = 200,
+ .max = 32767,
+ .step = 1,
+};
+
+static const struct v4l2_ctrl_config go7007_mb_regions_ctrl = {
+ .ops = &go7007_ctrl_ops,
+ .id = V4L2_CID_DETECT_MD_REGION_GRID,
+ .dims = { 576 / 16, 720 / 16 },
+ .max = 3,
+ .step = 1,
+};
+
int go7007_v4l2_ctrl_init(struct go7007 *go)
{
struct v4l2_ctrl_handler *hdl = &go->hdl;
struct v4l2_ctrl *ctrl;
- v4l2_ctrl_handler_init(hdl, 13);
+ v4l2_ctrl_handler_init(hdl, 22);
go->mpeg_video_gop_size = v4l2_ctrl_new_std(hdl, NULL,
V4L2_CID_MPEG_VIDEO_GOP_SIZE, 0, 34, 1, 15);
go->mpeg_video_gop_closure = v4l2_ctrl_new_std(hdl, NULL,
V4L2_JPEG_ACTIVE_MARKER_DHT);
if (ctrl)
ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ v4l2_ctrl_new_custom(hdl, &go7007_pixel_threshold0_ctrl, NULL);
+ v4l2_ctrl_new_custom(hdl, &go7007_motion_threshold0_ctrl, NULL);
+ v4l2_ctrl_new_custom(hdl, &go7007_mb_threshold0_ctrl, NULL);
+ v4l2_ctrl_new_custom(hdl, &go7007_pixel_threshold1_ctrl, NULL);
+ v4l2_ctrl_new_custom(hdl, &go7007_motion_threshold1_ctrl, NULL);
+ v4l2_ctrl_new_custom(hdl, &go7007_mb_threshold1_ctrl, NULL);
+ v4l2_ctrl_new_custom(hdl, &go7007_pixel_threshold2_ctrl, NULL);
+ v4l2_ctrl_new_custom(hdl, &go7007_motion_threshold2_ctrl, NULL);
+ v4l2_ctrl_new_custom(hdl, &go7007_mb_threshold2_ctrl, NULL);
+ v4l2_ctrl_new_custom(hdl, &go7007_pixel_threshold3_ctrl, NULL);
+ v4l2_ctrl_new_custom(hdl, &go7007_motion_threshold3_ctrl, NULL);
+ v4l2_ctrl_new_custom(hdl, &go7007_mb_threshold3_ctrl, NULL);
+ v4l2_ctrl_new_custom(hdl, &go7007_mb_regions_ctrl, NULL);
+ go->modet_mode = v4l2_ctrl_new_std_menu(hdl, NULL,
+ V4L2_CID_DETECT_MD_MODE,
+ V4L2_DETECT_MD_MODE_REGION_GRID,
+ 1 << V4L2_DETECT_MD_MODE_THRESHOLD_GRID,
+ V4L2_DETECT_MD_MODE_DISABLED);
if (hdl->error) {
int rv = hdl->error;