blob: 38cf36b35b58ddaeac433986a5631117f90f6ceb [file] [log] [blame]
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/timer.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <media/v4l2-mem2mem.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-event.h>
#include <media/videobuf2-vmalloc.h>
#include "ambxc.h"
/* Flags that indicate a format can be used for capture/output */
#define MEM2MEM_CAPTURE (1 << 0)
#define MEM2MEM_OUTPUT (1 << 1)
extern struct ambxc_q_data* get_q_data(struct ambxc_ctx *ctx,
enum v4l2_buf_type type);
struct ambxc_fmt formats[] = {
{
.name = "H264 with start codes",
.fourcc = V4L2_PIX_FMT_H264,
.types = MEM2MEM_CAPTURE | MEM2MEM_OUTPUT,
},
{
.name = "MPEG-2 ES",
.fourcc = V4L2_PIX_FMT_MPEG2,
.types = MEM2MEM_CAPTURE | MEM2MEM_OUTPUT,
},
{
.name = "MPEG-1/2/4 Multiplexed",
.fourcc = V4L2_PIX_FMT_MPEG,
.types = MEM2MEM_CAPTURE | MEM2MEM_OUTPUT,
},
};
struct v4l2_frmsize_discrete framesize[] = {
{ 1920, 1080},
{ 1280, 720},
};
static struct ambxc_fmt* find_format(struct v4l2_format *f)
{
struct ambxc_fmt *fmt;
unsigned int k;
for (k = 0; k < NUM_FORMATS; k++) {
fmt = &formats[k];
if (fmt->fourcc == f->fmt.pix.pixelformat)
break;
}
if (k == NUM_FORMATS)
return NULL;
return &formats[k];
}
struct v4l2_frmsize_discrete* find_framesize(struct ambxc_ctx *ctx,
struct v4l2_format *f)
{
struct v4l2_frmsize_discrete *frmsz;
struct ambxc_q_data *q_data;
int i = 0;
frmsz = &framesize[0];
/* find the exact match first */
for (i = 0; i < ARRAY_SIZE(framesize); i++) {
frmsz = &framesize[i];
if (frmsz->height == f->fmt.pix.height &&
frmsz->width == f->fmt.pix.width)
return frmsz;
}
/* return current framesize */
q_data = get_q_data(ctx, f->type);
for (i = 0; i < ARRAY_SIZE(framesize); i++) {
frmsz = &framesize[i];
if (frmsz->height == q_data->height &&
frmsz->width == q_data->width)
return frmsz;
}
/* return the last available framesize */
return frmsz;
}
/*
* video ioctls
*/
static int vidioc_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
strncpy(cap->driver, AMBXC_NAME, sizeof(cap->driver) - 1);
strncpy(cap->card, AMBXC_NAME, sizeof(cap->card) - 1);
snprintf(cap->bus_info, sizeof(cap->bus_info),
"platform:%s", AMBXC_NAME);
cap->device_caps = V4L2_CAP_VIDEO_M2M |
V4L2_CAP_VIDEO_CAPTURE |
V4L2_CAP_VIDEO_OUTPUT |
V4L2_CAP_STREAMING;
cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
return 0;
}
static struct v4l2_input input = {
.name = "Camera 0",
.type = V4L2_INPUT_TYPE_CAMERA,
.index = 0,
};
static int enum_fmt(struct v4l2_fmtdesc *f, u32 type)
{
int i, num;
struct ambxc_fmt *fmt;
num = 0;
for (i = 0; i < NUM_FORMATS; ++i) {
if (formats[i].types & type) {
/* index-th format of type type found ? */
if (num == f->index)
break;
/* Correct type but haven't reached our index yet,
* just increment per-type index */
++num;
}
}
if (i < NUM_FORMATS) {
/* Format found */
fmt = &formats[i];
strncpy(f->description, fmt->name, sizeof(f->description) - 1);
f->pixelformat = fmt->fourcc;
f->flags = V4L2_FMT_FLAG_COMPRESSED;
return 0;
}
/* Format not found */
return -EINVAL;
}
static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
return enum_fmt(f, MEM2MEM_CAPTURE);
}
static int vidioc_enum_fmt_vid_out(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
return enum_fmt(f, MEM2MEM_OUTPUT);
}
static int vidioc_g_fmt(struct ambxc_ctx *ctx, struct v4l2_format *f)
{
struct vb2_queue *vq;
struct ambxc_q_data *q_data;
vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
if (!vq)
return -EINVAL;
q_data = get_q_data(ctx, f->type);
f->fmt.pix.width = q_data->width;
f->fmt.pix.height = q_data->height;
f->fmt.pix.field = V4L2_FIELD_NONE;
f->fmt.pix.pixelformat = q_data->fmt->fourcc;
f->fmt.pix.bytesperline = 0;
f->fmt.pix.sizeimage = BUF_SIZE;
return 0;
}
static int vidioc_g_fmt_vid_out(struct file *file, void *priv,
struct v4l2_format *f)
{
return vidioc_g_fmt(file2ctx(file), f);
}
static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
return vidioc_g_fmt(file2ctx(file), f);
}
static int vidioc_try_fmt(struct v4l2_format *f, struct ambxc_fmt *fmt)
{
enum v4l2_field field;
field = f->fmt.pix.field;
if (field == V4L2_FIELD_ANY)
field = V4L2_FIELD_NONE;
else if (field != V4L2_FIELD_NONE)
return -EINVAL;
f->fmt.pix.field = field;
f->fmt.pix.bytesperline = 0;
f->fmt.pix.sizeimage = BUF_SIZE;
return 0;
}
static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct ambxc_fmt *fmt;
struct ambxc_ctx *ctx = file2ctx(file);
fmt = find_format(f);
if (!fmt || !(fmt->types & MEM2MEM_CAPTURE)) {
v4l2_err(&ctx->dev->v4l2_dev,
"Fourcc format (0x%08x) invalid.\n",
f->fmt.pix.pixelformat);
return -EINVAL;
}
return vidioc_try_fmt(f, fmt);
}
static int vidioc_try_fmt_vid_out(struct file *file, void *priv,
struct v4l2_format *f)
{
struct ambxc_fmt *fmt;
struct ambxc_ctx *ctx = file2ctx(file);
fmt = find_format(f);
if (!fmt || !(fmt->types & MEM2MEM_OUTPUT)) {
v4l2_err(&ctx->dev->v4l2_dev,
"Fourcc format (0x%08x) invalid.\n",
f->fmt.pix.pixelformat);
return -EINVAL;
}
return vidioc_try_fmt(f, fmt);
}
static int vidioc_s_fmt(struct ambxc_ctx *ctx, struct v4l2_format *f)
{
struct ambxc_q_data *q_data;
struct vb2_queue *vq;
struct v4l2_frmsize_discrete *frmsz;
vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
if (!vq)
return -EINVAL;
q_data = get_q_data(ctx, f->type);
if (!q_data)
return -EINVAL;
if (vb2_is_busy(vq)) {
v4l2_err(&ctx->dev->v4l2_dev, "%s queue busy\n", __func__);
return -EBUSY;
}
frmsz = find_framesize(ctx, f);
q_data->fmt = find_format(f);
q_data->width = frmsz->width;
q_data->height = frmsz->height;
q_data->sizeimage = BUF_SIZE;
return 0;
}
static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
int ret;
ret = vidioc_try_fmt_vid_cap(file, priv, f);
if (ret)
return ret;
return vidioc_s_fmt(file2ctx(file), f);
}
static int vidioc_s_fmt_vid_out(struct file *file, void *priv,
struct v4l2_format *f)
{
int ret;
ret = vidioc_try_fmt_vid_out(file, priv, f);
if (ret)
return ret;
ret = vidioc_s_fmt(file2ctx(file), f);
return ret;
}
static int vidioc_enum_framesizes(struct file *file,
void *priv, struct v4l2_frmsizeenum *frms)
{
if (frms->index >= ARRAY_SIZE(framesize))
return -EINVAL;
switch (frms->pixel_format) {
case V4L2_PIX_FMT_H264:
case V4L2_PIX_FMT_MPEG2:
frms->type = V4L2_FRMSIZE_TYPE_DISCRETE;
frms->discrete = framesize[frms->index];
return 0;
default:
return -EINVAL;
}
}
static int vidioc_reqbufs(struct file *file, void *priv,
struct v4l2_requestbuffers *reqbufs)
{
struct ambxc_ctx *ctx = file2ctx(file);
return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
}
static int vidioc_querybuf(struct file *file, void *priv,
struct v4l2_buffer *buf)
{
struct ambxc_ctx *ctx = file2ctx(file);
return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
}
static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
{
struct ambxc_ctx *ctx = file2ctx(file);
return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
}
static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
{
struct ambxc_ctx *ctx = file2ctx(file);
return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
}
static int vidioc_streamon(struct file *file, void *priv,
enum v4l2_buf_type type)
{
struct ambxc_ctx *ctx = file2ctx(file);
return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
}
static int vidioc_streamoff(struct file *file, void *priv,
enum v4l2_buf_type type)
{
struct ambxc_ctx *ctx = file2ctx(file);
return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
}
static int vidioc_enum_input(struct file *file, void *priv,
struct v4l2_input *inp)
{
if (inp->index >= 1)
return -EINVAL;
inp->type = V4L2_INPUT_TYPE_CAMERA;
return 0;
}
static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
{
*i = 0;
return 0;
}
static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
{
if (i >= 1)
return -EINVAL;
return 0;
}
static int vidioc_enum_output(struct file *file, void *priv,
struct v4l2_output *outp)
{
if (outp->index >= 1)
return -EINVAL;
return 0;
}
static int vidioc_g_output(struct file *file, void *priv, unsigned int *i)
{
*i = 0;
return 0;
}
static int vidioc_s_output(struct file *file, void *priv, unsigned int i)
{
if (i >= 1)
return -EINVAL;
return 0;
}
extern int ambxc_ctl_send(const char *buf, int count);
struct ambxc_cmd {
char *cmd;
int dly;
};
struct ambxc_cmd cmd_1080i_xc_start[] = {
// {"d:\\ash\\Xcode\\XCODE_H2H_1080I_ES_NFS_CFG.ash", 8},
// {"d:\\ash\\Xcode\\XCODE_H2H_1080I_ES_NFS_START.ash", 0},
{ NULL, 0 }
};
struct ambxc_cmd cmd_720p_xc_start[] = {
// {"d:\\ash\\Xcode\\XCODE_H2H_720P_ES_NFS_CFG.ash", 8},
// {"d:\\ash\\Xcode\\XCODE_H2H_720P_ES_NFS_START.ash", 0},
{ NULL, 0 }
};
struct ambxc_cmd *cmd_xc_start = cmd_720p_xc_start;
struct timer_list cmd_timer;
struct ambxc_cmd cmd_enc_stop[] = {
{ "amba voch stop --par ENC_H264_1080I_AO_STOP -s 0 -c 0T", 1 },
{ NULL, 0 }
};
static void do_xc_cmd(unsigned long priv)
{
struct ambxc_cmd *cmds = (void *)priv;
char cmd_buf[128];
while (cmds->cmd) {
sprintf(cmd_buf, "%s\n", cmds->cmd);
printk(KERN_DEBUG "%s\n", cmd_buf);
ambxc_ctl_send(cmd_buf, strlen(cmd_buf));
mdelay(cmds->dly * 1000);
cmds++;
}
}
void do_xc_start(struct ambxc_ctx *ctx)
{
struct ambxc_q_data *q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
dprintk(ctx->dev, "do_xc_start:\n");
if (q_data->height == 720)
cmd_xc_start = cmd_720p_xc_start;
else
cmd_xc_start = cmd_1080i_xc_start;
do_xc_cmd((unsigned long)cmd_xc_start);
}
static void do_xc_stop(struct ambxc_ctx *ctx)
{
dprintk(ctx->dev, "do_xcode_stop: (wait EOS from itron for 20 secs)\n");
setup_timer(&cmd_timer, do_xc_cmd, (unsigned long)cmd_enc_stop);
mod_timer(&cmd_timer, jiffies + msecs_to_jiffies(20 * 1000));
}
int vidioc_encoder_cmd(struct file *file, void *priv,
struct v4l2_encoder_cmd *cmd)
{
struct ambxc_ctx *ctx = file2ctx(file);
switch (cmd->cmd) {
case V4L2_ENC_CMD_START:
if (cmd->flags != 0)
return -EINVAL;
do_xc_start(ctx);
break;
case V4L2_ENC_CMD_STOP:
if (cmd->flags != 0)
return -EINVAL;
break;
default:
return -EINVAL;
}
return 0;
}
int vidioc_decoder_cmd(struct file *file, void *priv,
struct v4l2_decoder_cmd *cmd)
{
struct ambxc_ctx *ctx = file2ctx(file);
switch (cmd->cmd) {
case V4L2_DEC_CMD_START:
if (cmd->flags != 0)
return -EINVAL;
do_xc_start(ctx);
break;
case V4L2_DEC_CMD_STOP:
if (cmd->flags != 0)
return -EINVAL;
while (!list_empty(&ctx->m2m_ctx->out_q_ctx.rdy_queue)) {
dprintk(ctx->dev, "wait until all queued OUTPUT buffers are sent\n");
msleep(1000);
}
do_xc_stop(ctx);
break;
default:
return -EINVAL;
}
return 0;
}
static int vidioc_subscribe_event(struct v4l2_fh *fh,
const struct v4l2_event_subscription *sub)
{
switch (sub->type) {
case V4L2_EVENT_EOS:
return v4l2_event_subscribe(fh, sub, 2, NULL);
default:
return -EINVAL;
}
}
static int vidioc_g_parm(struct file *file, void *fh,
struct v4l2_streamparm *a) {
int rval = 0;
#if 0
struct omap24xxcam_fh *ofh = fh;
struct omap24xxcam_device *cam = ofh->cam;
mutex_lock(&cam->mutex);
rval = vidioc_int_g_parm(cam->sdev, a);
mutex_unlock(&cam->mutex);
#endif
return rval;
}
static int vidioc_s_parm(struct file *file, void *fh,
struct v4l2_streamparm *a)
{
int rval = 0;
#if 0
struct omap24xxcam_fh *ofh = fh;
struct omap24xxcam_device *cam = ofh->cam;
struct v4l2_streamparm old_streamparm;
int rval;
mutex_lock(&cam->mutex);
if (cam->streaming) {
rval = -EBUSY;
goto out;
}
old_streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
rval = vidioc_int_g_parm(cam->sdev, &old_streamparm);
if (rval)
goto out;
rval = vidioc_int_s_parm(cam->sdev, a);
if (rval)
goto out;
rval = omap24xxcam_sensor_if_enable(cam);
/*
* Revert to old streaming parameters if enabling sensor
* interface with the new ones failed.
*/
if (rval)
vidioc_int_s_parm(cam->sdev, &old_streamparm);
out:
mutex_unlock(&cam->mutex);
#endif
return rval;
}
const struct v4l2_ioctl_ops ambxc_ioctl_ops = {
.vidioc_querycap = vidioc_querycap,
.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
.vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out,
.vidioc_g_fmt_vid_out = vidioc_g_fmt_vid_out,
.vidioc_try_fmt_vid_out = vidioc_try_fmt_vid_out,
.vidioc_s_fmt_vid_out = vidioc_s_fmt_vid_out,
.vidioc_reqbufs = vidioc_reqbufs,
.vidioc_querybuf = vidioc_querybuf,
.vidioc_qbuf = vidioc_qbuf,
.vidioc_dqbuf = vidioc_dqbuf,
.vidioc_streamon = vidioc_streamon,
.vidioc_streamoff = vidioc_streamoff,
.vidioc_enum_input = vidioc_enum_input,
.vidioc_g_input = vidioc_g_input,
.vidioc_s_input = vidioc_s_input,
.vidioc_enum_output = vidioc_enum_output,
.vidioc_g_output = vidioc_g_output,
.vidioc_s_output = vidioc_s_output,
.vidioc_encoder_cmd = vidioc_encoder_cmd,
.vidioc_decoder_cmd = vidioc_decoder_cmd,
.vidioc_enum_framesizes = vidioc_enum_framesizes,
.vidioc_subscribe_event = vidioc_subscribe_event,
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
.vidioc_g_parm = vidioc_g_parm,
.vidioc_s_parm = vidioc_s_parm,
};