/*++
* linux/drivers/media/video/wmt_v4l2/cmos/wmt-cmos.c
* WonderMedia v4l cmos device driver
*
* Copyright c 2010 WonderMedia Technologies, Inc.
*
* 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.
*
* 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 .
*
* WonderMedia Technologies, Inc.
* 4F, 533, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C
--*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "wmt-cmos.h"
#include "cmos-subdev.h"
#include "../wmt-vid.h"
#include "flash/flash.h"
#define THE_MB_USER "CMOS-MB"
#undef pr_err
#define pr_err(fmt, args...) printk("[" THE_MB_USER "] " "%s, %d:" fmt, __func__, __LINE__, ##args)
//#define CMOS_REG_TRACE
#ifdef CMOS_REG_TRACE
#define CMOS_REG_SET32(addr, val) \
PRINT("REG_SET:0x%x -> 0x%0x\n", addr, val);\
REG32_VAL(addr) = (val)
#else
#define CMOS_REG_SET32(addr, val) REG32_VAL(addr) = (val)
#endif
//#define CMOS_DEBUG /* Flag to enable debug message */
//#define CMOS_TRACE
#ifdef CMOS_DEBUG
#define DBG_MSG(fmt, args...) printk("{%s} " fmt, __FUNCTION__ , ## args)
#else
#define DBG_MSG(fmt, args...)
#endif
#ifdef CMOS_TRACE
#define TRACE(fmt, args...) printk("{%s}: " fmt, __FUNCTION__ , ## args)
#else
#define TRACE(fmt, args...)
#endif
#define MAX_FB_IN_QUEUE 10
#define NUM_INPUTS 3
#define CAMERA_TYPE_NA 0 /* not avaliable */
#define CAMERA_TYPE_CMOS 1 /* CMOS */
#define CAMERA_TYPE_USB 2 /* USB */
struct regulator *cam_vccvid = NULL;
struct regulator *cam_dvdd = NULL;
typedef enum {
STS_CMOS_READY = 0,
STS_CMOS_WAIT = 0x0001,
STS_CMOS_RUNNING = 0x0002,
STS_CMOS_FB_DONE = 0x0004
} cmos_status;
typedef struct {
VID_FB_M;
struct list_head list;
} cmos_fb_t;
struct cmos_drvinfo {
struct list_head head;
unsigned int frame_size;
unsigned int width;
unsigned int height;
unsigned int dft_y_addr;
unsigned int dft_c_addr;
cmos_fb_t fb_pool[MAX_FB_IN_QUEUE];
unsigned int fb_cnt;
unsigned int streamoff;
unsigned int dqbuf;
cmos_status status;
};
// sync with UbootParam.h in Hal.
enum {
CAMERA_CAP_FLASH = 1 << 0,
CAMERA_CAP_FLASH_AUTO = 1 << 1,
CAMERA_CAP_HDR = 1 << 2,
};
struct cmos_dev {
int type;
int pwdn_gpio;
int pwdn_pol;
int mirror;
u32 cap;
int id;
struct cmos_subdev *sd;
};
struct cmos_info {
int input;
int input_count;
struct cmos_drvinfo drvinfo;
struct cmos_dev cd[NUM_INPUTS];
struct flash_dev *fl;
struct mutex lock;
};
static inline struct cmos_subdev *current_subdev(struct cmos_info *info)
{
if (!info || info->input >= NUM_INPUTS)
return NULL;
return info->cd[info->input].sd;
}
static DECLARE_WAIT_QUEUE_HEAD(cmos_wait);
static spinlock_t cmos_lock;
static int cmos_dev_ref;
static int cmos_dev_setup(struct cmos_info *info)
{
struct cmos_dev *cd;
int rc = 0;
int i;
for (i = 0; i < info->input_count; i++) {
cd = &info->cd[i];
if (cd->type != CAMERA_TYPE_CMOS)
continue;
rc = gpio_request(cd->pwdn_gpio, "cmos pwdn");
if (rc) {
pr_err("cmos gpio%d request failed\n", cd->pwdn_gpio);
goto err_gpio_req;
}
gpio_direction_output(cd->pwdn_gpio, !cd->pwdn_pol);
}
return 0;
err_gpio_req:
while (i--)
gpio_free(info->cd[i].pwdn_gpio);
return rc;
}
static void cmos_dev_power_up(struct cmos_dev *cd)
{
if (cd->pwdn_gpio > 0) {
printk(KERN_DEBUG "%s, gpio-%d\n", __func__, cd->pwdn_gpio);
gpio_direction_output(cd->pwdn_gpio, cd->pwdn_pol);
msleep(20);
}
}
static void cmos_dev_power_down(struct cmos_dev *cd)
{
if (cd->pwdn_gpio > 0) {
printk(KERN_DEBUG "%s, gpio-%d\n", __func__, cd->pwdn_gpio);
gpio_direction_output(cd->pwdn_gpio, !cd->pwdn_pol);
}
}
static void cmos_dev_release(struct cmos_info *info)
{
struct cmos_dev *cd;
int i;
for (i = 0; i < info->input_count; i++) {
cd = &info->cd[i];
if (cd->type != CAMERA_TYPE_CMOS)
continue;
cmos_dev_power_down(cd);
gpio_free(cd->pwdn_gpio);
}
}
static int parse_camera_param(struct cmos_info *info)
{
static char env[] = "wmt.camera.param";
struct cmos_dev cd[2];
char buf[64];
size_t l = sizeof(buf);
int i, n;
int rc;
rc = wmt_getsyspara(env, buf, &l);
if (rc) {
pr_err("Invalid param, please set %s\n", env);
return -EINVAL;
}
memset(cd, 0, sizeof(cd));
sscanf(buf, "%d:%d:%d:%d:%d:%d:%d:%d:%d:%x",
&cd[1].type, &cd[1].pwdn_gpio, &cd[1].pwdn_pol, &cd[1].mirror, &cd[1].cap,
&cd[0].type, &cd[0].pwdn_gpio, &cd[0].pwdn_pol, &cd[0].mirror, &cd[0].cap);
for (n = 0, i = 0; i < ARRAY_SIZE(cd); i++) {
struct cmos_dev *c = &cd[i];
if (c->type != CAMERA_TYPE_CMOS && c->type != CAMERA_TYPE_USB)
continue;
info->cd[n] = cd[i];
info->cd[n].id = n;
pr_info("#%d: type %d, pwdn_gpio %d, pwdn_pol %d, mirror %d, cap 0x%x\n",
n, c->type, c->pwdn_gpio, c->pwdn_pol, c->mirror, c->cap);
n++;
}
info->input_count = n;
return (n == 0) ? -EINVAL : 0;
}
static int cam_enable(struct cmos_drvinfo *drv, bool en)
{
drv->streamoff = en ? 0 : 1;
CMOS_REG_SET32(REG_VID_CMOS_EN, en); /* enable CMOS */
return 0;
}
static int print_queue(struct cmos_drvinfo *drv)
{
#ifdef CMOS_DEBUG
struct list_head *next;
cmos_fb_t *fb;
TRACE("Enter\n");
list_for_each(next, &drv->head) {
fb = (cmos_fb_t *) list_entry(next, cmos_fb_t, list);
DBG_MSG("[%d] y_addr: 0x%x, c_addr: 0x%x\n",
fb->id, fb->y_addr, fb->c_addr );
}
TRACE("Leave\n");
#endif
return 0;
}
static int put_queue(struct cmos_drvinfo *drv, cmos_fb_t *fb_in)
{
TRACE("Enter\n");
list_add_tail(&fb_in->list, &drv->head);
wmb();
print_queue(drv);
TRACE("Leave\n");
return 0;
}
static int pop_queue(struct cmos_drvinfo *drv, cmos_fb_t *fb_in)
{
TRACE("Enter\n");
list_del(&fb_in->list);
wmb();
print_queue(drv);
TRACE("Leave\n");
return 0;
}
static int cmos_cam_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
{
TRACE("Enter\n");
strlcpy(cap->driver, "wm88xx", sizeof(cap->driver));
cap->version = KERNEL_VERSION(0, 0, 1);
cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
TRACE("Leave\n");
return 0;
}
static int cmos_cam_cropcap(struct file *file, void *fh, struct v4l2_cropcap *cropcap)
{
pr_err("Not support now!\n");
return -EINVAL;
}
struct wmt_fmt {
char *name;
u32 fourcc; /* v4l2 format id */
int depth;
};
static struct wmt_fmt formats[] = {
{
.name = "4:2:2, packed, YUYV",
.fourcc = V4L2_PIX_FMT_YUYV,
.depth = 16,
},
};
static int cmos_cam_enum_fmt_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f)
{
struct wmt_fmt *fmt;
if (f->index >= ARRAY_SIZE(formats))
return -EINVAL;
fmt = &formats[f->index];
strlcpy(f->description, fmt->name, sizeof(f->description));
f->pixelformat = fmt->fourcc;
return 0;
}
static int cmos_cam_enum_framesizes(struct file *file, void *fh,
struct v4l2_frmsizeenum *fsize)
{
struct cmos_info *info = file->private_data;
struct cmos_subdev *sd = current_subdev(info);
int ret;
mutex_lock(&info->lock);
ret = cmos_subdev_call(sd, enum_framesizes, fsize);
mutex_unlock(&info->lock);
return ret;
}
static int cmos_cam_g_fmt_cap(struct file *file, void *fh, struct v4l2_format *f)
{
struct cmos_info *info = file->private_data;
struct cmos_drvinfo *drv = &info->drvinfo;
f->fmt.pix.width = drv->width;
f->fmt.pix.height = drv->height;
return 0;
}
#define pixfmtstr(x) (x) & 0xff, ((x) >> 8) & 0xff, ((x) >> 16) & 0xff, \
((x) >> 24) & 0xff
static int cmos_cam_s_fmt_cap(struct file *file, void *fh, struct v4l2_format *f)
{
struct cmos_info *info = file->private_data;
struct cmos_drvinfo *drv = &info->drvinfo;
struct cmos_subdev *sd = current_subdev(info);
struct v4l2_mbus_framefmt mf;
int rc;
TRACE("Enter\n");
mutex_lock(&info->lock);
printk(KERN_DEBUG "%s: S_FMT %dx%d, fmt %c%c%c%c\n",
sd->name, f->fmt.pix.width, f->fmt.pix.height,
pixfmtstr(f->fmt.pix.pixelformat));
mf.width = f->fmt.pix.width;
mf.height = f->fmt.pix.height;
mf.field = f->fmt.pix.field;
mf.colorspace = f->fmt.pix.colorspace;
rc = cmos_subdev_call(sd, s_mbus_fmt, &mf);
if (rc) {
pr_err("%s, %s: s_mbus_fmt fail\n", __func__, sd->name);
goto out;
}
/* host set fmt */
rc = wmt_vid_set_mode(mf.width, mf.height, f->fmt.pix.pixelformat);
if (rc) {
goto out;
}
drv->width = mf.width;
drv->height = mf.height;
switch (f->fmt.pix.pixelformat) {
case V4L2_PIX_FMT_RGB32: // *4 for ARGB display frambuffer
drv->frame_size = drv->width * drv->height * 4;
break;
case V4L2_PIX_FMT_NV21:
case V4L2_PIX_FMT_NV12:
drv->frame_size = (drv->width * drv->height * 3) / 2;
break;
case V4L2_PIX_FMT_YUYV: // YC422
drv->frame_size = drv->width * drv->height * 2;
break;
default:
drv->frame_size = 0;
rc = -EINVAL;
break;
}
out:
mutex_unlock(&info->lock);
TRACE("Leave\n");
return rc;
}
static int cmos_cam_try_fmt_cap(struct file *file, void *fh, struct v4l2_format *f)
{
struct cmos_info *info = file->private_data;
struct cmos_subdev *sd = current_subdev(info);
struct v4l2_mbus_framefmt mf;
int ret;
TRACE("Enter\n");
mutex_lock(&info->lock);
mf.width = f->fmt.pix.width;
mf.height = f->fmt.pix.height;
mf.field = f->fmt.pix.field;
mf.colorspace = f->fmt.pix.colorspace;
ret = cmos_subdev_call(sd, try_mbus_fmt, &mf);
if (!ret) {
f->fmt.pix.width = mf.width;
f->fmt.pix.height = mf.height;
}
mutex_unlock(&info->lock);
TRACE("Leave\n");
return ret;
}
static int cmos_cam_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *rb)
{
struct cmos_info *info = file->private_data;
struct cmos_drvinfo *drv = &info->drvinfo;
cmos_fb_t *fb;
int i, j;
int ret = 0;
TRACE("Enter\n");
mutex_lock(&info->lock);
if ((rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ||
(rb->memory != V4L2_MEMORY_MMAP) ||
(rb->count > MAX_FB_IN_QUEUE) ) {
pr_err("rb->type: 0x%x, rb->memory: 0x%x, rb->count: %d\n",
rb->type, rb->memory, rb->count);
ret = -EINVAL;
goto out;
}
if (!drv->streamoff) {
pr_err("reqbufs: streaming active\n");
ret = -EBUSY;
goto out;
}
if (rb->count == 0) {
/* Free memory */
for (i = 0; i < drv->fb_cnt; i++) {
fb = &drv->fb_pool[i];
if (fb->y_addr) {
mb_free(fb->y_addr);
memset(fb, 0, sizeof(cmos_fb_t));
}
}
drv->fb_cnt = 0;
INIT_LIST_HEAD(&drv->head);
goto out;
}
drv->fb_cnt = rb->count;
DBG_MSG(" Frame Size: %d Bytes\n", drv->frame_size);
DBG_MSG(" fb_cnt: %d\n", drv->fb_cnt);
/* Allocate memory */
for (i = 0; i < drv->fb_cnt; i++) {
fb = &drv->fb_pool[i];
fb->y_addr = mb_alloc(drv->frame_size);
DBG_MSG("%d CMOS MB_allocate size %d\n", i,drv->frame_size);
if (fb->y_addr == 0) {
/* Allocate MB memory size fail */
pr_err("[%d] >> Allocate MB memory (%d) fail!\n", i, drv->frame_size);
for (j=0; jfb_pool[j];
mb_free(fb->y_addr);
}
ret = -EINVAL;
goto out;
}
fb->c_addr = fb->y_addr + drv->width * drv->height;
fb->id = i;
fb->is_busy = 0;
fb->done = 0;
DBG_MSG("[%d] fb: 0x%x, y_addr: 0x%x, c_addr: 0x%x\n",
i, (unsigned int)fb, fb->y_addr, fb->c_addr);
}
out:
mutex_unlock(&info->lock);
TRACE("Leave\n");
return ret;
}
static int cmos_cam_querybuf(struct file *file, void *fh, struct v4l2_buffer *b)
{
struct cmos_info *info = file->private_data;
struct cmos_drvinfo *drv = &info->drvinfo;
TRACE("Enter (index: %d)\n", b->index);
mutex_lock(&info->lock);
if (b->index < MAX_FB_IN_QUEUE) {
cmos_fb_t *fb = &drv->fb_pool[b->index];
b->length = drv->frame_size;
b->m.offset = fb->y_addr;
} else {
b->length = 0;
b->m.offset = 0;
}
DBG_MSG(" b->length: %d\n", b->length);
DBG_MSG(" b->m.offset: 0x%x\n", b->m.offset);
mutex_unlock(&info->lock);
TRACE("Leave (index: %d)\n", b->index);
return 0;
}
static int cmos_cam_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
{
struct cmos_info *info = file->private_data;
struct cmos_drvinfo *drv = &info->drvinfo;
cmos_fb_t *fb;
TRACE("Enter (index: %d)\n", b->index);
mutex_lock(&info->lock);
fb = &drv->fb_pool[b->index];
put_queue(drv, fb);
mutex_unlock(&info->lock);
TRACE("Leave (index: %d)\n", b->index);
return 0;
}
static int cmos_cam_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
{
struct cmos_info *info = file->private_data;
struct cmos_drvinfo *drv = &info->drvinfo;
unsigned long flags =0;
cmos_fb_t *fb = 0;
int ret = 0;
TRACE("Enter (index: %d)\n", b->index);
mutex_lock(&info->lock);
if (drv->streamoff) {
#if 0
struct list_head *next;
/* CMOS sensor did not work now */
list_for_each(next, &drv->head) {
fb = (cmos_fb_t *)list_entry(next, cmos_fb_t, list);
if (fb) {
pop_queue(drv, fb);
break;
}
}
#else
ret = -EINVAL;
pr_err("stream is off\n");
#endif
goto EXIT_cmos_cam_dqbuf;
}
fb = (cmos_fb_t *)wmt_vid_get_cur_fb();
if (!fb) {
goto EXIT_cmos_cam_dqbuf;
}
spin_lock_irqsave(&cmos_lock, flags);
DBG_MSG("Set DQBUF on\n");
drv->dqbuf = 1;
spin_unlock_irqrestore(&cmos_lock, flags);
if ((wait_event_interruptible_timeout(cmos_wait,
(drv->status & STS_CMOS_FB_DONE),
400) == 0)) {
pr_err("CMOS Time out in 400 ms\n");
ret = -ETIMEDOUT;
goto EXIT_cmos_cam_dqbuf;
}
drv->status &= (~STS_CMOS_FB_DONE);
DBG_MSG("[%d] fb: 0x%p\n", fb->id, fb);
pop_queue(drv, fb);
b->length = drv->frame_size;
b->m.offset = fb->y_addr;
b->index = fb->id;
EXIT_cmos_cam_dqbuf:
mutex_unlock(&info->lock);
TRACE("Leave (index: %d)\n", b->index);
return ret;
}
static int cmos_cam_enum_input(struct file *file, void *fh, struct v4l2_input *inp)
{
if (inp->index >= NUM_INPUTS)
return -EINVAL;
inp->type = V4L2_INPUT_TYPE_CAMERA;
inp->std = V4L2_STD_ALL;
sprintf(inp->name, "Camera %u", inp->index);
return 0;
}
static int cmos_cam_g_input(struct file *file, void *fh, unsigned int *i)
{
struct cmos_info *info = file->private_data;
TRACE("Enter\n");
mutex_lock(&info->lock);
*i = info->input;
mutex_unlock(&info->lock);
TRACE("Leave\n");
return 0;
}
static int cmos_cam_s_input(struct file *file, void *fh, unsigned int i)
{
struct cmos_info *info = file->private_data;
struct cmos_dev *cd;
struct cmos_subdev *sd;
int rc,j;
TRACE("Enter\n");
mutex_lock(&info->lock);
if (i >= info->input_count) {
rc = -EINVAL;
goto out;
}
{
/* Here we use info->cd[2] as nmi cmos device */
extern int nmi_running(void);
if (nmi_running()) {
pr_info("nmi running, use cmos device 2 ...\n");
i = 2;
}
}
for (j = 0; j < info->input_count; j++) {
if(j==i)
continue;
cd = &info->cd[j];
if (cd->type != CAMERA_TYPE_CMOS)
continue;
cmos_dev_power_down(cd);
}
info->input = i;
cd = &info->cd[i];
sd = cd->sd;
cmos_dev_power_up(cd);
if (!sd) {
sd = cmos_subdev_get_by_try();
if (!sd) {
pr_err("cmos detect failed\n");
rc = -EINVAL;
goto err_detect;
}
pr_info("cmos #%d: detected %s\n", i, sd->name);
cd->sd = sd;
}
rc = cmos_subdev_call(sd, init);
if (rc) {
goto err_init;
}
pr_info("[cmos] %s init success\n", sd->name);
goto out;
err_init:
err_detect:
cmos_dev_power_down(cd);
out:
mutex_unlock(&info->lock);
TRACE("Leave\n");
return rc;
}
static int cmos_cam_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *a)
{
struct cmos_info *info = file->private_data;
struct cmos_subdev *sd = current_subdev(info);
int ret;
TRACE("Enter\n");
mutex_lock(&info->lock);
if (a->id == V4L2_CID_CAMERA_FLASH_MODE) {
struct cmos_dev *cd = &info->cd[info->input];
ret = (cd->cap & CAMERA_CAP_FLASH) ? v4l2_ctrl_query_fill(a, 0, 4, 1, 0) : -EINVAL;
} else
ret = cmos_subdev_call(sd, queryctrl, a);
mutex_unlock(&info->lock);
TRACE("Leave\n");
return ret;
}
static int cmos_cam_g_ctrl(struct file *file, void *fh, struct v4l2_control *a)
{
struct cmos_info *info = file->private_data;
struct cmos_subdev *sd = current_subdev(info);
int ret;
TRACE("Enter\n");
mutex_lock(&info->lock);
ret = cmos_subdev_call(sd, g_ctrl, a);
mutex_unlock(&info->lock);
TRACE("Leave\n");
return ret;
}
static int cmos_cam_s_ctrl(struct file *file, void *fh, struct v4l2_control *a)
{
struct cmos_info *info = file->private_data;
struct cmos_subdev *sd = current_subdev(info);
char *s;
int ret;
TRACE("Enter\n");
mutex_lock(&info->lock);
/* for debug */
switch (a->id) {
case V4L2_CID_CAMERA_SCENE_MODE: s = "Scene Mode"; break;
case V4L2_CID_DO_WHITE_BALANCE: s = "White Balance"; break;
case V4L2_CID_EXPOSURE: s = "Exposure"; break;
case V4L2_CID_HFLIP: s = "H-flip"; break;
case V4L2_CID_VFLIP: s = "V-flip"; break;
case V4L2_CID_CAMERA_FLASH_MODE: s = "Flash Mode"; break;
case V4L2_CID_CAMERA_FOCUS_MODE: s = "Focus Mode"; break;
case V4L2_CID_CAMERA_FOCUS_POSITION_X: s = "Focus Area X"; break;
case V4L2_CID_CAMERA_FOCUS_POSITION_Y: s = "Focus Area Y"; break;
default: s = "Unknown"; break;
}
printk(KERN_DEBUG "%s: S_CTRL: %s, value %d\n", sd->name, s, a->value);
if (a->id == V4L2_CID_CAMERA_FLASH_MODE) {
struct cmos_dev *cd = &info->cd[info->input];
if (!(cd->cap & CAMERA_CAP_FLASH))
ret = -EINVAL;
else
ret = flash_set_mode(info->fl, a->value);
} else
ret = cmos_subdev_call(sd, s_ctrl, a);
mutex_unlock(&info->lock);
TRACE("Leave\n");
return ret;
}
static int cmos_cam_g_parm(struct file *file, void *fh,
struct v4l2_streamparm *parm)
{
struct cmos_info *info = file->private_data;
struct cmos_subdev *sd = current_subdev(info);
int ret;
TRACE("Enter\n");
mutex_lock(&info->lock);
ret= cmos_subdev_call(sd,g_parm, parm);
mutex_unlock(&info->lock);
TRACE("Leave\n");
return ret;
}
static int cmos_cam_s_parm(struct file *file, void *fh,
struct v4l2_streamparm *parm)
{
struct cmos_info *info = file->private_data;
struct cmos_subdev *sd = current_subdev(info);
int ret;
TRACE("Enter\n");
mutex_lock(&info->lock);
ret= cmos_subdev_call(sd,s_parm, parm);
mutex_unlock(&info->lock);
TRACE("Leave\n");
return ret;
}
static int cmos_cam_streamon(struct file *file, void *fh, enum v4l2_buf_type i)
{
struct cmos_info *info = file->private_data;
struct cmos_drvinfo *drv = &info->drvinfo;
cmos_fb_t *fb;
int ret = 0;
TRACE("Enter\n");
mutex_lock(&info->lock);
if (!list_empty(&drv->head)) {
fb = list_first_entry(&drv->head, cmos_fb_t, list);
wmt_vid_set_cur_fb((vid_fb_t *)fb);
cam_enable(drv, true);
} else
ret = -EINVAL;
mutex_unlock(&info->lock);
TRACE("Leave\n");
return 0;
}
static int cmos_cam_streamoff(struct file *file, void *fh, enum v4l2_buf_type i)
{
struct cmos_info *info = file->private_data;
struct cmos_drvinfo *drv = &info->drvinfo;
TRACE("Enter\n");
mutex_lock(&info->lock);
cam_enable(drv, false);
mutex_unlock(&info->lock);
TRACE("Leave\n");
return 0;
}
static int cmos_cam_mmap(struct file *file, struct vm_area_struct *vma)
{
int ret;
TRACE("Enter\n");
vma->vm_flags |= VM_IO | VM_RESERVED;
vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
ret = remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
vma->vm_end - vma->vm_start, vma->vm_page_prot);
TRACE("Leave\n");
return ret;
}
static irqreturn_t cmos_isr(int irq, void *data)
{
struct cmos_info *info = data;
struct cmos_drvinfo *drv;
struct list_head *next;
cmos_fb_t *cur_fb, *fb;
unsigned long flags = 0;
int mb_count = 0;
//TRACE("Enter\n");
// printk("i");
if (!info) {
return IRQ_NONE;
}
drv = &info->drvinfo;
if (drv->dqbuf == 1) {
spin_lock_irqsave(&cmos_lock, flags);
drv->dqbuf = 0;
// DBG_MSG("Set DBBUF off\n");
spin_unlock_irqrestore(&cmos_lock, flags);
cur_fb = (cmos_fb_t *)wmt_vid_get_cur_fb();
list_for_each(next, &drv->head) {
fb = (cmos_fb_t *) list_entry(next, cmos_fb_t, list);
if (fb == cur_fb) {
/*--------------------------------------------------------------
Get next FB
--------------------------------------------------------------*/
fb = (cmos_fb_t *) list_entry(cur_fb->list.next, cmos_fb_t, list);
do {
if (((vid_fb_t *)fb)->y_addr != 0)
mb_count = mb_counter(((vid_fb_t *)fb)->y_addr);
if (mb_count != 1) {
fb = (cmos_fb_t *) list_entry(fb->list.next, cmos_fb_t, list);
// DBG_MSG("mbcount skip 0x%08x mb_count %d \n",
// ((vid_fb_t *)fb)->y_addr,mb_count);
continue;
}
} while ((mb_count != 1) && (fb != cur_fb));
cur_fb->is_busy = 0;
cur_fb->done = 1;
// DBG_MSG("[%d] done: %d, is_busy: %d\n",
// cur_fb->id, cur_fb->done, cur_fb->is_busy);
// DBG_MSG("[%d] New FB done: %d, is_busy: %d\n",
// fb->id, fb->done, fb->is_busy);
wmt_vid_set_cur_fb((vid_fb_t *)fb);
drv->status |= STS_CMOS_FB_DONE;
wake_up_interruptible(&cmos_wait);
break;
}
}
} /* if( drv->dqbuf == 1 ) */
CMOS_REG_SET32(REG_VID_INT_CTRL, REG32_VAL(REG_VID_INT_CTRL));
//TRACE("Leave\n");
// printk("o");
return IRQ_HANDLED;
}
static int cmos_cam_open(struct file *filp)
{
struct cmos_info *info = video_drvdata(filp);
struct cmos_drvinfo *drv;
int ret = 0;
printk(KERN_DEBUG "%s()\n", __func__);
//if(cam_vccvid)
//regulator_enable(cam_vccvid);
//if(cam_dvdd)
//regulator_enable(cam_dvdd);
TRACE("Enter\n");
mutex_lock(&info->lock);
if (cmos_dev_ref) {
ret = -EBUSY;
goto out;
}
cmos_dev_ref++;
ret = request_irq(WMT_VID_IRQ, (void *)&cmos_isr, IRQF_SHARED,
"wmt-cmos", (void *)info);
if (ret) {
pr_err("CMOS: Failed to register CMOS irq %i\n", WMT_VID_IRQ);
cmos_dev_ref--;
goto out;
}
drv = &info->drvinfo;
drv->dqbuf = 0;
drv->status = STS_CMOS_READY;
INIT_LIST_HEAD(&drv->head);
spin_lock_init(&cmos_lock);
filp->private_data = info;
wmt_vid_open(VID_MODE_CMOS, NULL);
cam_enable(drv, false);
out:
mutex_unlock(&info->lock);
TRACE("Leave\n");
return ret;
}
static int cmos_cam_release(struct file *filp)
{
struct cmos_info *info = filp->private_data;
struct cmos_drvinfo *drv = &info->drvinfo;
struct cmos_subdev *sd;
int i;
TRACE("Enter\n");
mutex_lock(&info->lock);
free_irq(WMT_VID_IRQ, (void *)info);
if (drv) {
/* Free memory */
for (i = 0; i < drv->fb_cnt; i++) {
cmos_fb_t *fb = &drv->fb_pool[i];
if (fb->y_addr) {
mb_free(fb->y_addr);
memset(fb, 0, sizeof(cmos_fb_t));
}
}
}
sd = current_subdev(info);
if (sd) {
cmos_subdev_call(sd, exit);
}
cmos_dev_power_down(&info->cd[info->input]);
wmt_vid_close(VID_MODE_CMOS);
cmos_dev_ref--;
// if(cam_vccvid)
// regulator_disable(cam_vccvid);
//if(cam_dvdd)
//regulator_disable(cam_dvdd);
mutex_unlock(&info->lock);
TRACE("Leave\n");
return 0;
}
static const struct v4l2_ioctl_ops cmos_ioctl_ops = {
.vidioc_querycap = cmos_cam_querycap,
.vidioc_enum_input = cmos_cam_enum_input,
.vidioc_g_input = cmos_cam_g_input,
.vidioc_s_input = cmos_cam_s_input,
.vidioc_reqbufs = cmos_cam_reqbufs,
.vidioc_querybuf = cmos_cam_querybuf,
.vidioc_qbuf = cmos_cam_qbuf,
.vidioc_dqbuf = cmos_cam_dqbuf,
.vidioc_streamon = cmos_cam_streamon,
.vidioc_streamoff = cmos_cam_streamoff,
.vidioc_enum_fmt_vid_cap = cmos_cam_enum_fmt_cap,
.vidioc_enum_framesizes = cmos_cam_enum_framesizes,
.vidioc_g_fmt_vid_cap = cmos_cam_g_fmt_cap,
.vidioc_s_fmt_vid_cap = cmos_cam_s_fmt_cap,
.vidioc_try_fmt_vid_cap = cmos_cam_try_fmt_cap,
.vidioc_g_parm = cmos_cam_g_parm,
.vidioc_s_parm = cmos_cam_s_parm,
.vidioc_queryctrl = cmos_cam_queryctrl,
.vidioc_s_ctrl = cmos_cam_s_ctrl,
.vidioc_g_ctrl = cmos_cam_g_ctrl,
.vidioc_cropcap = cmos_cam_cropcap,
};
static const struct v4l2_file_operations cmos_cam_fops = {
.owner = THIS_MODULE,
.open = cmos_cam_open,
.release = cmos_cam_release,
.unlocked_ioctl = video_ioctl2,
.mmap = cmos_cam_mmap,
};
static struct video_device cmos_cam = {
.name = "cmos_cam",
.fops = &cmos_cam_fops,
.ioctl_ops = &cmos_ioctl_ops,
.release = video_device_release_empty,
.tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM,
.minor = -1
};
static int cmos_probe(struct platform_device *pdev)
{
int ret;
struct cmos_info *info;
info = kzalloc(sizeof *info, GFP_KERNEL);
if (!info)
return -ENOMEM;
// ldo6 can't disable.
cam_vccvid = regulator_get(NULL, "ldo6");
if (IS_ERR(cam_vccvid)){
cam_vccvid = NULL;
pr_err("failed to get camera vccvid\n");
}
cam_dvdd= regulator_get(NULL, "ldo5");
if (IS_ERR(cam_dvdd)){
cam_dvdd = NULL;
pr_err("failed to get camera dvdd\n");
}
if(cam_vccvid){
regulator_set_voltage(cam_vccvid, 2800000, 2800000);
regulator_enable(cam_vccvid);
// regulator_disable(cam_vccvid);
}
if(cam_dvdd){
regulator_set_voltage(cam_dvdd, 1800000, 1800000);
regulator_enable(cam_dvdd);
//regulator_disable(cam_dvdd);
}
ret = parse_camera_param(info);
if (ret)
goto err_param;
ret = cmos_dev_setup(info);
if (ret)
goto err_dev_seup;
ret = video_register_device(&cmos_cam, VFL_TYPE_GRABBER, -1);
if (ret) {
pr_err("WonderMedia CMOS camera register failed\n");
goto err_video_register;
}
mutex_init(&info->lock);
video_set_drvdata(&cmos_cam, info);
info->fl = flash_instantiation();
pr_info("WonderMedia CMOS camera register OK \n");
return 0;
err_video_register:
err_dev_seup:
err_param:
kfree(info);
return ret;
}
static int cmos_remove(struct platform_device *pdev)
{
struct cmos_info *info = video_get_drvdata(&cmos_cam);
pr_info("%s()\n", __func__);
flash_destroy(info->fl);
video_unregister_device(&cmos_cam);
cmos_dev_release(info);
mutex_destroy(&info->lock);
kfree(info);
if(cam_vccvid)
regulator_put(cam_vccvid);
if(cam_dvdd)
regulator_put(cam_dvdd);
return 0;
}
static int cmos_suspend(struct platform_device *pdev, pm_message_t state)
{
printk(KERN_DEBUG "%s()\n", __func__);
return 0;
}
static int cmos_resume(struct platform_device *pdev)
{
struct cmos_info *info = video_get_drvdata(&cmos_cam);
struct cmos_dev *cd;
int i;
printk(KERN_DEBUG "%s()\n", __func__);
for (i = 0; i < info->input_count; i++) {
cd = &info->cd[i];
if (cd->type != CAMERA_TYPE_CMOS)
continue;
gpio_re_enabled(cd->pwdn_gpio);
}
return 0;
}
static struct platform_driver cmos_driver = {
.driver = {
.name = "cmos",
},
.remove = cmos_remove,
.suspend = cmos_suspend,
.resume = cmos_resume,
};
static void cmos_platform_release(struct device *device) { }
static struct platform_device cmos_device = {
.name = "cmos",
.dev = {
.release = cmos_platform_release,
},
};
static int __init cmos_init(void)
{
int ret;
cmos_subdev_init();
ret = wmt_vid_i2c_init(0, 0x52);
if (ret) {
goto err_i2c_init;
}
ret = platform_device_register(&cmos_device);
if (ret) {
goto err_dev_register;
}
ret = platform_driver_probe(&cmos_driver, cmos_probe);
if (ret) {
goto err_drv_probe;
}
return 0;
err_drv_probe:
platform_device_unregister(&cmos_device);
err_dev_register:
wmt_vid_i2c_release();
err_i2c_init:
cmos_subdev_exit();
return ret;
}
static void __exit cmos_exit(void)
{
pr_info("%s()\n", __func__);
platform_driver_unregister(&cmos_driver);
platform_device_unregister(&cmos_device);
wmt_vid_i2c_release();
cmos_subdev_exit();
}
module_init(cmos_init);
module_exit(cmos_exit);
MODULE_AUTHOR("WonderMedia SW Team Max Chen");
MODULE_DESCRIPTION("cmos device driver");
MODULE_LICENSE("GPL");