diff options
Diffstat (limited to 'drivers/media/video')
209 files changed, 12299 insertions, 10295 deletions
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index ce1e7ba940f6..3dc0ea7ba927 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -472,6 +472,9 @@ comment "Camera sensor devices" config VIDEO_APTINA_PLL tristate +config VIDEO_SMIAPP_PLL + tristate + config VIDEO_OV7670 tristate "OmniVision OV7670 sensor support" depends on I2C && VIDEO_V4L2 @@ -556,6 +559,8 @@ config VIDEO_S5K6AA This is a V4L2 sensor-level driver for Samsung S5K6AA(FX) 1.3M camera sensor with an embedded SoC image signal processor. +source "drivers/media/video/smiapp/Kconfig" + comment "Flash devices" config VIDEO_ADP1653 @@ -662,8 +667,6 @@ source "drivers/media/video/tm6000/Kconfig" source "drivers/media/video/usbvision/Kconfig" -source "drivers/media/video/et61x251/Kconfig" - source "drivers/media/video/sn9c102/Kconfig" source "drivers/media/video/pwc/Kconfig" diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index a6282a3a6a82..5a97da2ae33b 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -79,9 +79,12 @@ obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o obj-$(CONFIG_VIDEO_NOON010PC30) += noon010pc30.o obj-$(CONFIG_VIDEO_M5MOLS) += m5mols/ obj-$(CONFIG_VIDEO_S5K6AA) += s5k6aa.o +obj-$(CONFIG_VIDEO_SMIAPP) += smiapp/ obj-$(CONFIG_VIDEO_ADP1653) += adp1653.o obj-$(CONFIG_VIDEO_AS3645A) += as3645a.o +obj-$(CONFIG_VIDEO_SMIAPP_PLL) += smiapp-pll.o + obj-$(CONFIG_SOC_CAMERA_IMX074) += imx074.o obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o obj-$(CONFIG_SOC_CAMERA_MT9M111) += mt9m111.o @@ -152,7 +155,6 @@ obj-$(CONFIG_USB_ZR364XX) += zr364xx.o obj-$(CONFIG_USB_STKWEBCAM) += stkwebcam.o obj-$(CONFIG_USB_SN9C102) += sn9c102/ -obj-$(CONFIG_USB_ET61X251) += et61x251/ obj-$(CONFIG_USB_PWC) += pwc/ obj-$(CONFIG_USB_GSPCA) += gspca/ diff --git a/drivers/media/video/adp1653.c b/drivers/media/video/adp1653.c index 5b045b4a66fe..57e87090388d 100644 --- a/drivers/media/video/adp1653.c +++ b/drivers/media/video/adp1653.c @@ -34,7 +34,6 @@ #include <linux/module.h> #include <linux/i2c.h> #include <linux/slab.h> -#include <linux/version.h> #include <media/adp1653.h> #include <media/v4l2-device.h> @@ -282,19 +281,19 @@ adp1653_init_device(struct adp1653_flash *flash) return -EIO; } - mutex_lock(&flash->ctrls.lock); + mutex_lock(flash->ctrls.lock); /* Reset faults before reading new ones. */ flash->fault = 0; rval = adp1653_get_fault(flash); - mutex_unlock(&flash->ctrls.lock); + mutex_unlock(flash->ctrls.lock); if (rval > 0) { dev_err(&client->dev, "faults detected: 0x%1.1x\n", rval); return -EIO; } - mutex_lock(&flash->ctrls.lock); + mutex_lock(flash->ctrls.lock); rval = adp1653_update_hw(flash); - mutex_unlock(&flash->ctrls.lock); + mutex_unlock(flash->ctrls.lock); if (rval) { dev_err(&client->dev, "adp1653_update_hw failed at %s\n", __func__); diff --git a/drivers/media/video/adv7343.c b/drivers/media/video/adv7343.c index 119b60401bf3..2b5aa676a84e 100644 --- a/drivers/media/video/adv7343.c +++ b/drivers/media/video/adv7343.c @@ -130,14 +130,12 @@ static int adv7343_setstd(struct v4l2_subdev *sd, v4l2_std_id std) { struct adv7343_state *state = to_state(sd); struct adv7343_std_info *std_info; - int output_idx, num_std; + int num_std; char *fsc_ptr; u8 reg, val; int err = 0; int i = 0; - output_idx = state->output; - std_info = (struct adv7343_std_info *)stdinfo; num_std = ARRAY_SIZE(stdinfo); diff --git a/drivers/media/video/aptina-pll.c b/drivers/media/video/aptina-pll.c index 0bd3813bb59d..8153a449846b 100644 --- a/drivers/media/video/aptina-pll.c +++ b/drivers/media/video/aptina-pll.c @@ -148,9 +148,8 @@ int aptina_pll_calculate(struct device *dev, unsigned int mf_high; unsigned int mf_low; - mf_low = max(roundup(mf_min, mf_inc), - DIV_ROUND_UP(pll->ext_clock * p1, - limits->int_clock_max * div)); + mf_low = roundup(max(mf_min, DIV_ROUND_UP(pll->ext_clock * p1, + limits->int_clock_max * div)), mf_inc); mf_high = min(mf_max, pll->ext_clock * p1 / (limits->int_clock_min * div)); diff --git a/drivers/media/video/as3645a.c b/drivers/media/video/as3645a.c index 7a3371f044fc..c4b03572dce8 100644 --- a/drivers/media/video/as3645a.c +++ b/drivers/media/video/as3645a.c @@ -713,7 +713,7 @@ static int as3645a_resume(struct device *dev) * The number of LEDs reported in platform data is used to compute default * limits. Parameters passed through platform data can override those limits. */ -static int as3645a_init_controls(struct as3645a *flash) +static int __devinit as3645a_init_controls(struct as3645a *flash) { const struct as3645a_platform_data *pdata = flash->pdata; struct v4l2_ctrl *ctrl; @@ -804,8 +804,8 @@ static int as3645a_init_controls(struct as3645a *flash) return flash->ctrls.error; } -static int as3645a_probe(struct i2c_client *client, - const struct i2c_device_id *devid) +static int __devinit as3645a_probe(struct i2c_client *client, + const struct i2c_device_id *devid) { struct as3645a *flash; int ret; @@ -846,7 +846,7 @@ done: return ret; } -static int __exit as3645a_remove(struct i2c_client *client) +static int __devexit as3645a_remove(struct i2c_client *client) { struct v4l2_subdev *subdev = i2c_get_clientdata(client); struct as3645a *flash = to_as3645a(subdev); @@ -877,7 +877,7 @@ static struct i2c_driver as3645a_i2c_driver = { .pm = &as3645a_pm_ops, }, .probe = as3645a_probe, - .remove = __exit_p(as3645a_remove), + .remove = __devexit_p(as3645a_remove), .id_table = as3645a_id_table, }; diff --git a/drivers/media/video/au0828/Kconfig b/drivers/media/video/au0828/Kconfig index 81ba9d9d1b52..23f7fd22f0eb 100644 --- a/drivers/media/video/au0828/Kconfig +++ b/drivers/media/video/au0828/Kconfig @@ -6,7 +6,8 @@ config VIDEO_AU0828 select I2C_ALGOBIT select VIDEO_TVEEPROM select VIDEOBUF_VMALLOC - select DVB_AU8522 if !DVB_FE_CUSTOMISE + select DVB_AU8522_DTV if !DVB_FE_CUSTOMISE + select DVB_AU8522_V4L if !DVB_FE_CUSTOMISE select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMISE select MEDIA_TUNER_MXL5007T if !MEDIA_TUNER_CUSTOMISE select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE diff --git a/drivers/media/video/au0828/au0828-dvb.c b/drivers/media/video/au0828/au0828-dvb.c index 518216743c9c..39ece8e24985 100644 --- a/drivers/media/video/au0828/au0828-dvb.c +++ b/drivers/media/video/au0828/au0828-dvb.c @@ -25,6 +25,7 @@ #include <linux/device.h> #include <linux/suspend.h> #include <media/v4l2-common.h> +#include <media/tuner.h> #include "au0828.h" #include "au8522.h" @@ -79,9 +80,16 @@ static struct au8522_config hauppauge_woodbury_config = { .vsb_if = AU8522_IF_3_25MHZ, }; -static struct xc5000_config hauppauge_hvr950q_tunerconfig = { +static struct xc5000_config hauppauge_xc5000a_config = { .i2c_address = 0x61, .if_khz = 6000, + .chip_id = XC5000A, +}; + +static struct xc5000_config hauppauge_xc5000c_config = { + .i2c_address = 0x61, + .if_khz = 6000, + .chip_id = XC5000C, }; static struct mxl5007t_config mxl5007t_hvr950q_config = { @@ -383,8 +391,19 @@ int au0828_dvb_register(struct au0828_dev *dev) &hauppauge_hvr950q_config, &dev->i2c_adap); if (dvb->frontend != NULL) - dvb_attach(xc5000_attach, dvb->frontend, &dev->i2c_adap, - &hauppauge_hvr950q_tunerconfig); + switch (dev->board.tuner_type) { + default: + case TUNER_XC5000: + dvb_attach(xc5000_attach, dvb->frontend, + &dev->i2c_adap, + &hauppauge_xc5000a_config); + break; + case TUNER_XC5000C: + dvb_attach(xc5000_attach, dvb->frontend, + &dev->i2c_adap, + &hauppauge_xc5000c_config); + break; + } break; case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL: dvb->frontend = dvb_attach(au8522_attach, @@ -411,7 +430,7 @@ int au0828_dvb_register(struct au0828_dev *dev) if (dvb->frontend != NULL) { dvb_attach(xc5000_attach, dvb->frontend, &dev->i2c_adap, - &hauppauge_hvr950q_tunerconfig); + &hauppauge_xc5000a_config); } break; default: diff --git a/drivers/media/video/au0828/au0828-video.c b/drivers/media/video/au0828/au0828-video.c index 0b3e481ffe8c..ac3dd733ab81 100644 --- a/drivers/media/video/au0828/au0828-video.c +++ b/drivers/media/video/au0828/au0828-video.c @@ -120,7 +120,7 @@ static void au0828_irq_callback(struct urb *urb) struct au0828_dmaqueue *dma_q = urb->context; struct au0828_dev *dev = container_of(dma_q, struct au0828_dev, vidq); unsigned long flags = 0; - int rc, i; + int i; switch (urb->status) { case 0: /* success */ @@ -138,7 +138,7 @@ static void au0828_irq_callback(struct urb *urb) /* Copy data from URB */ spin_lock_irqsave(&dev->slock, flags); - rc = dev->isoc_ctl.isoc_copy(dev, urb); + dev->isoc_ctl.isoc_copy(dev, urb); spin_unlock_irqrestore(&dev->slock, flags); /* Reset urb buffers */ @@ -1881,7 +1881,7 @@ int au0828_analog_register(struct au0828_dev *dev, int retval = -ENOMEM; struct usb_host_interface *iface_desc; struct usb_endpoint_descriptor *endpoint; - int i; + int i, ret; dprintk(1, "au0828_analog_register called!\n"); @@ -1951,8 +1951,8 @@ int au0828_analog_register(struct au0828_dev *dev, dev->vbi_dev = video_device_alloc(); if (NULL == dev->vbi_dev) { dprintk(1, "Can't allocate vbi_device.\n"); - kfree(dev->vdev); - return -ENOMEM; + ret = -ENOMEM; + goto err_vdev; } /* Fill the video capture device struct */ @@ -1971,8 +1971,8 @@ int au0828_analog_register(struct au0828_dev *dev, if (retval != 0) { dprintk(1, "unable to register video device (error = %d).\n", retval); - video_device_release(dev->vdev); - return -ENODEV; + ret = -ENODEV; + goto err_vbi_dev; } /* Register the vbi device */ @@ -1981,13 +1981,18 @@ int au0828_analog_register(struct au0828_dev *dev, if (retval != 0) { dprintk(1, "unable to register vbi device (error = %d).\n", retval); - video_device_release(dev->vbi_dev); - video_device_release(dev->vdev); - return -ENODEV; + ret = -ENODEV; + goto err_vbi_dev; } dprintk(1, "%s completed!\n", __func__); return 0; + +err_vbi_dev: + video_device_release(dev->vbi_dev); +err_vdev: + video_device_release(dev->vdev); + return ret; } diff --git a/drivers/media/video/blackfin/bfin_capture.c b/drivers/media/video/blackfin/bfin_capture.c index 514fcf742f5a..0aba45e34f70 100644 --- a/drivers/media/video/blackfin/bfin_capture.c +++ b/drivers/media/video/blackfin/bfin_capture.c @@ -942,6 +942,10 @@ static int __devinit bcap_probe(struct platform_device *pdev) INIT_LIST_HEAD(&bcap_dev->dma_queue); vfd->lock = &bcap_dev->mutex; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags); /* register video device */ ret = video_register_device(bcap_dev->video_dev, VFL_TYPE_GRABBER, -1); diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c index e581b37be789..a9cfb0f4be48 100644 --- a/drivers/media/video/bt8xx/bttv-driver.c +++ b/drivers/media/video/bt8xx/bttv-driver.c @@ -663,7 +663,7 @@ static const struct v4l2_queryctrl bttv_ctls[] = { .minimum = 0, .maximum = 65535, .step = 128, - .default_value = 32768, + .default_value = 27648, .type = V4L2_CTRL_TYPE_INTEGER, },{ .id = V4L2_CID_SATURATION, @@ -4394,7 +4394,7 @@ static int __devinit bttv_probe(struct pci_dev *dev, if (!bttv_tvcards[btv->c.type].no_video) { bttv_register_video(btv); bt848_bright(btv,32768); - bt848_contrast(btv,32768); + bt848_contrast(btv, 27648); bt848_hue(btv,32768); bt848_sat(btv,32768); audio_mute(btv, 1); diff --git a/drivers/media/video/c-qcam.c b/drivers/media/video/c-qcam.c index fda32f52554a..73c65c2bf178 100644 --- a/drivers/media/video/c-qcam.c +++ b/drivers/media/video/c-qcam.c @@ -378,7 +378,7 @@ get_fragment: static long qc_capture(struct qcam *qcam, char __user *buf, unsigned long len) { struct v4l2_device *v4l2_dev = &qcam->v4l2_dev; - unsigned lines, pixelsperline, bitsperxfer; + unsigned lines, pixelsperline; unsigned int is_bi_dir = qcam->bidirectional; size_t wantlen, outptr = 0; char tmpbuf[BUFSZ]; @@ -404,7 +404,6 @@ static long qc_capture(struct qcam *qcam, char __user *buf, unsigned long len) lines = qcam->height; pixelsperline = qcam->width; - bitsperxfer = (is_bi_dir) ? 24 : 8; if (is_bi_dir) { /* Turn the port around */ diff --git a/drivers/media/video/cpia2/cpia2.h b/drivers/media/video/cpia2/cpia2.h index ab252188981b..cdef677d57ec 100644 --- a/drivers/media/video/cpia2/cpia2.h +++ b/drivers/media/video/cpia2/cpia2.h @@ -32,11 +32,12 @@ #define __CPIA2_H__ #include <linux/videodev2.h> -#include <media/v4l2-common.h> #include <linux/usb.h> #include <linux/poll.h> +#include <media/v4l2-common.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ctrls.h> -#include "cpia2dev.h" #include "cpia2_registers.h" /* define for verbose debug output */ @@ -65,7 +66,6 @@ /* Flicker Modes */ #define NEVER_FLICKER 0 -#define ANTI_FLICKER_ON 1 #define FLICKER_60 60 #define FLICKER_50 50 @@ -148,7 +148,6 @@ enum { #define DEFAULT_BRIGHTNESS 0x46 #define DEFAULT_CONTRAST 0x93 #define DEFAULT_SATURATION 0x7f -#define DEFAULT_TARGET_KB 0x30 /* Power state */ #define HI_POWER_MODE CPIA2_SYSTEM_CONTROL_HIGH_POWER @@ -287,7 +286,6 @@ struct camera_params { struct { u8 cam_register; u8 flicker_mode_req; /* 1 if flicker on, else never flicker */ - int mains_frequency; } flicker_control; struct { @@ -337,7 +335,7 @@ struct camera_params { u8 vc_control; u8 vc_mp_direction; u8 vc_mp_data; - u8 target_kb; + u8 quality; } vc_params; struct { @@ -366,23 +364,23 @@ struct framebuf { struct framebuf *next; }; -struct cpia2_fh { - enum v4l2_priority prio; - u8 mmapped; -}; - struct camera_data { /* locks */ + struct v4l2_device v4l2_dev; struct mutex v4l2_lock; /* serialize file operations */ - struct v4l2_prio_state prio; + struct v4l2_ctrl_handler hdl; + struct { + /* Lights control cluster */ + struct v4l2_ctrl *top_light; + struct v4l2_ctrl *bottom_light; + }; + struct v4l2_ctrl *usb_alt; /* camera status */ - volatile int present; /* Is the camera still present? */ - int open_count; /* # of process that have camera open */ int first_image_seen; - u8 mains_freq; /* for flicker control */ enum sensors sensor_type; u8 flush; + struct v4l2_fh *stream_fh; u8 mmapped; int streaming; /* 0 = no, 1 = yes */ int xfer_mode; /* XFER_BULK or XFER_ISOC */ @@ -390,7 +388,7 @@ struct camera_data { /* v4l */ int video_size; /* VIDEO_SIZE_ */ - struct video_device *vdev; /* v4l videodev */ + struct video_device vdev; /* v4l videodev */ u32 width; u32 height; /* Its size */ __u32 pixelformat; /* Format fourcc */ @@ -425,6 +423,7 @@ struct camera_data { /* v4l */ int cpia2_register_camera(struct camera_data *cam); void cpia2_unregister_camera(struct camera_data *cam); +void cpia2_camera_release(struct v4l2_device *v4l2_dev); /* core */ int cpia2_reset_camera(struct camera_data *cam); @@ -443,7 +442,7 @@ int cpia2_send_command(struct camera_data *cam, struct cpia2_command *cmd); int cpia2_do_command(struct camera_data *cam, unsigned int command, unsigned char direction, unsigned char param); -struct camera_data *cpia2_init_camera_struct(void); +struct camera_data *cpia2_init_camera_struct(struct usb_interface *intf); int cpia2_init_camera(struct camera_data *cam); int cpia2_allocate_buffers(struct camera_data *cam); void cpia2_free_buffers(struct camera_data *cam); @@ -454,7 +453,6 @@ unsigned int cpia2_poll(struct camera_data *cam, int cpia2_remap_buffer(struct camera_data *cam, struct vm_area_struct *vma); void cpia2_set_property_flip(struct camera_data *cam, int prop_val); void cpia2_set_property_mirror(struct camera_data *cam, int prop_val); -int cpia2_set_target_kb(struct camera_data *cam, unsigned char value); int cpia2_set_gpio(struct camera_data *cam, unsigned char setting); int cpia2_set_fps(struct camera_data *cam, int framerate); diff --git a/drivers/media/video/cpia2/cpia2_core.c b/drivers/media/video/cpia2/cpia2_core.c index ee91e295c90a..17188e234770 100644 --- a/drivers/media/video/cpia2/cpia2_core.c +++ b/drivers/media/video/cpia2/cpia2_core.c @@ -66,7 +66,6 @@ static int config_sensor_410(struct camera_data *cam, static int config_sensor_500(struct camera_data *cam, int reqwidth, int reqheight); static int set_all_properties(struct camera_data *cam); -static void get_color_params(struct camera_data *cam); static void wake_system(struct camera_data *cam); static void set_lowlight_boost(struct camera_data *cam); static void reset_camera_struct(struct camera_data *cam); @@ -453,15 +452,6 @@ int cpia2_do_command(struct camera_data *cam, cam->params.version.vp_device_hi = cmd.buffer.block_data[0]; cam->params.version.vp_device_lo = cmd.buffer.block_data[1]; break; - case CPIA2_CMD_GET_VP_BRIGHTNESS: - cam->params.color_params.brightness = cmd.buffer.block_data[0]; - break; - case CPIA2_CMD_GET_CONTRAST: - cam->params.color_params.contrast = cmd.buffer.block_data[0]; - break; - case CPIA2_CMD_GET_VP_SATURATION: - cam->params.color_params.saturation = cmd.buffer.block_data[0]; - break; case CPIA2_CMD_GET_VP_GPIO_DATA: cam->params.vp_params.gpio_data = cmd.buffer.block_data[0]; break; @@ -617,6 +607,7 @@ int cpia2_reset_camera(struct camera_data *cam) { u8 tmp_reg; int retval = 0; + int target_kb; int i; struct cpia2_command cmd; @@ -800,9 +791,16 @@ int cpia2_reset_camera(struct camera_data *cam) } cpia2_do_command(cam, CPIA2_CMD_SET_VC_CONTROL, TRANSFER_WRITE,tmp_reg); - /* Set target size (kb) on vc */ + /* Set target size (kb) on vc + This is a heuristic based on the quality parameter and the raw + framesize in kB divided by 16 (the compression factor when the + quality is 100%) */ + target_kb = (cam->width * cam->height * 2 / 16384) * + cam->params.vc_params.quality / 100; + if (target_kb < 1) + target_kb = 1; cpia2_do_command(cam, CPIA2_CMD_SET_TARGET_KB, - TRANSFER_WRITE, cam->params.vc_params.target_kb); + TRANSFER_WRITE, target_kb); /* Wiggle VC Reset */ /*** @@ -1538,23 +1536,17 @@ static int set_all_properties(struct camera_data *cam) * framerate and user_mode were already set (set_default_user_mode). **/ - cpia2_set_color_params(cam); - cpia2_usb_change_streaming_alternate(cam, cam->params.camera_state.stream_mode); - cpia2_do_command(cam, CPIA2_CMD_SET_USER_EFFECTS, TRANSFER_WRITE, - cam->params.vp_params.user_effects); - - cpia2_set_flicker_mode(cam, - cam->params.flicker_control.flicker_mode_req); - cpia2_do_command(cam, CPIA2_CMD_SET_VC_MP_GPIO_DIRECTION, TRANSFER_WRITE, cam->params.vp_params.gpio_direction); cpia2_do_command(cam, CPIA2_CMD_SET_VC_MP_GPIO_DATA, TRANSFER_WRITE, cam->params.vp_params.gpio_data); + v4l2_ctrl_handler_setup(&cam->hdl); + wake_system(cam); set_lowlight_boost(cam); @@ -1569,7 +1561,6 @@ static int set_all_properties(struct camera_data *cam) *****************************************************************************/ void cpia2_save_camera_state(struct camera_data *cam) { - get_color_params(cam); cpia2_do_command(cam, CPIA2_CMD_GET_USER_EFFECTS, TRANSFER_READ, 0); cpia2_do_command(cam, CPIA2_CMD_GET_VC_MP_GPIO_DIRECTION, TRANSFER_READ, 0); @@ -1577,30 +1568,6 @@ void cpia2_save_camera_state(struct camera_data *cam) /* Don't get framerate or target_kb. Trust the values we already have */ } -/****************************************************************************** - * - * get_color_params - * - *****************************************************************************/ -static void get_color_params(struct camera_data *cam) -{ - cpia2_do_command(cam, CPIA2_CMD_GET_VP_BRIGHTNESS, TRANSFER_READ, 0); - cpia2_do_command(cam, CPIA2_CMD_GET_VP_SATURATION, TRANSFER_READ, 0); - cpia2_do_command(cam, CPIA2_CMD_GET_CONTRAST, TRANSFER_READ, 0); -} - -/****************************************************************************** - * - * cpia2_set_color_params - * - *****************************************************************************/ -void cpia2_set_color_params(struct camera_data *cam) -{ - DBG("Setting color params\n"); - cpia2_set_brightness(cam, cam->params.color_params.brightness); - cpia2_set_contrast(cam, cam->params.color_params.contrast); - cpia2_set_saturation(cam, cam->params.color_params.saturation); -} /****************************************************************************** * @@ -1664,15 +1631,9 @@ int cpia2_set_flicker_mode(struct camera_data *cam, int mode) switch(mode) { case NEVER_FLICKER: - cam->params.flicker_control.flicker_mode_req = mode; - break; case FLICKER_60: - cam->params.flicker_control.flicker_mode_req = mode; - cam->params.flicker_control.mains_frequency = 60; - break; case FLICKER_50: cam->params.flicker_control.flicker_mode_req = mode; - cam->params.flicker_control.mains_frequency = 50; break; default: err = -EINVAL; @@ -1701,6 +1662,7 @@ void cpia2_set_property_flip(struct camera_data *cam, int prop_val) { cam_reg &= ~CPIA2_VP_USER_EFFECTS_FLIP; } + cam->params.vp_params.user_effects = cam_reg; cpia2_do_command(cam, CPIA2_CMD_SET_USER_EFFECTS, TRANSFER_WRITE, cam_reg); } @@ -1725,37 +1687,13 @@ void cpia2_set_property_mirror(struct camera_data *cam, int prop_val) { cam_reg &= ~CPIA2_VP_USER_EFFECTS_MIRROR; } + cam->params.vp_params.user_effects = cam_reg; cpia2_do_command(cam, CPIA2_CMD_SET_USER_EFFECTS, TRANSFER_WRITE, cam_reg); } /****************************************************************************** * - * set_target_kb - * - * The new Target KB is set in cam->params.vc_params.target_kb and - * activates on reset. - *****************************************************************************/ - -int cpia2_set_target_kb(struct camera_data *cam, unsigned char value) -{ - DBG("Requested target_kb = %d\n", value); - if (value != cam->params.vc_params.target_kb) { - - cpia2_usb_stream_pause(cam); - - /* reset camera for new target_kb */ - cam->params.vc_params.target_kb = value; - cpia2_reset_camera(cam); - - cpia2_usb_stream_resume(cam); - } - - return 0; -} - -/****************************************************************************** - * * cpia2_set_gpio * *****************************************************************************/ @@ -1843,7 +1781,7 @@ void cpia2_set_brightness(struct camera_data *cam, unsigned char value) if (cam->params.pnp_id.device_type == DEVICE_STV_672 && value == 0) value++; DBG("Setting brightness to %d (0x%0x)\n", value, value); - cpia2_do_command(cam,CPIA2_CMD_SET_VP_BRIGHTNESS, TRANSFER_WRITE,value); + cpia2_do_command(cam, CPIA2_CMD_SET_VP_BRIGHTNESS, TRANSFER_WRITE, value); } /****************************************************************************** @@ -1854,7 +1792,6 @@ void cpia2_set_brightness(struct camera_data *cam, unsigned char value) void cpia2_set_contrast(struct camera_data *cam, unsigned char value) { DBG("Setting contrast to %d (0x%0x)\n", value, value); - cam->params.color_params.contrast = value; cpia2_do_command(cam, CPIA2_CMD_SET_CONTRAST, TRANSFER_WRITE, value); } @@ -1866,7 +1803,6 @@ void cpia2_set_contrast(struct camera_data *cam, unsigned char value) void cpia2_set_saturation(struct camera_data *cam, unsigned char value) { DBG("Setting saturation to %d (0x%0x)\n", value, value); - cam->params.color_params.saturation = value; cpia2_do_command(cam,CPIA2_CMD_SET_VP_SATURATION, TRANSFER_WRITE,value); } @@ -2168,14 +2104,10 @@ static void reset_camera_struct(struct camera_data *cam) /*** * The following parameter values are the defaults from the register map. ***/ - cam->params.color_params.brightness = DEFAULT_BRIGHTNESS; - cam->params.color_params.contrast = DEFAULT_CONTRAST; - cam->params.color_params.saturation = DEFAULT_SATURATION; cam->params.vp_params.lowlight_boost = 0; /* FlickerModes */ cam->params.flicker_control.flicker_mode_req = NEVER_FLICKER; - cam->params.flicker_control.mains_frequency = 60; /* jpeg params */ cam->params.compression.jpeg_options = CPIA2_VC_VC_JPEG_OPT_DEFAULT; @@ -2188,7 +2120,7 @@ static void reset_camera_struct(struct camera_data *cam) cam->params.vp_params.gpio_data = 0; /* Target kb params */ - cam->params.vc_params.target_kb = DEFAULT_TARGET_KB; + cam->params.vc_params.quality = 100; /*** * Set Sensor FPS as fast as possible. @@ -2228,7 +2160,7 @@ static void reset_camera_struct(struct camera_data *cam) * * Initializes camera struct, does not call reset to fill in defaults. *****************************************************************************/ -struct camera_data *cpia2_init_camera_struct(void) +struct camera_data *cpia2_init_camera_struct(struct usb_interface *intf) { struct camera_data *cam; @@ -2239,8 +2171,13 @@ struct camera_data *cpia2_init_camera_struct(void) return NULL; } + cam->v4l2_dev.release = cpia2_camera_release; + if (v4l2_device_register(&intf->dev, &cam->v4l2_dev) < 0) { + v4l2_err(&cam->v4l2_dev, "couldn't register v4l2_device\n"); + kfree(cam); + return NULL; + } - cam->present = 1; mutex_init(&cam->v4l2_lock); init_waitqueue_head(&cam->wq_stream); @@ -2373,11 +2310,6 @@ long cpia2_read(struct camera_data *cam, return -EINVAL; } - if (!cam->present) { - LOG("%s: camera removed\n",__func__); - return 0; /* EOF */ - } - if (!cam->streaming) { /* Start streaming */ cpia2_usb_stream_start(cam, @@ -2393,12 +2325,12 @@ long cpia2_read(struct camera_data *cam, if (frame->status != FRAME_READY) { mutex_unlock(&cam->v4l2_lock); wait_event_interruptible(cam->wq_stream, - !cam->present || + !video_is_registered(&cam->vdev) || (frame = cam->curbuff)->status == FRAME_READY); mutex_lock(&cam->v4l2_lock); if (signal_pending(current)) return -ERESTARTSYS; - if (!cam->present) + if (!video_is_registered(&cam->vdev)) return 0; } @@ -2423,17 +2355,10 @@ long cpia2_read(struct camera_data *cam, unsigned int cpia2_poll(struct camera_data *cam, struct file *filp, poll_table *wait) { - unsigned int status=0; + unsigned int status = v4l2_ctrl_poll(filp, wait); - if (!cam) { - ERR("%s: Internal error, camera_data not found!\n",__func__); - return POLLERR; - } - - if (!cam->present) - return POLLHUP; - - if(!cam->streaming) { + if ((poll_requested_events(wait) & (POLLIN | POLLRDNORM)) && + !cam->streaming) { /* Start streaming */ cpia2_usb_stream_start(cam, cam->params.camera_state.stream_mode); @@ -2441,10 +2366,8 @@ unsigned int cpia2_poll(struct camera_data *cam, struct file *filp, poll_wait(filp, &cam->wq_stream, wait); - if(!cam->present) - status = POLLHUP; - else if(cam->curbuff->status == FRAME_READY) - status = POLLIN | POLLRDNORM; + if (cam->curbuff->status == FRAME_READY) + status |= POLLIN | POLLRDNORM; return status; } @@ -2462,12 +2385,9 @@ int cpia2_remap_buffer(struct camera_data *cam, struct vm_area_struct *vma) unsigned long start = (unsigned long) adr; unsigned long page, pos; - if (!cam) - return -ENODEV; - DBG("mmap offset:%ld size:%ld\n", start_offset, size); - if (!cam->present) + if (!video_is_registered(&cam->vdev)) return -ENODEV; if (size > cam->frame_size*cam->num_frames || diff --git a/drivers/media/video/cpia2/cpia2_usb.c b/drivers/media/video/cpia2/cpia2_usb.c index 59c797c15277..95b5d6e7cdc4 100644 --- a/drivers/media/video/cpia2/cpia2_usb.c +++ b/drivers/media/video/cpia2/cpia2_usb.c @@ -54,6 +54,8 @@ static void cpia2_usb_complete(struct urb *urb); static int cpia2_usb_probe(struct usb_interface *intf, const struct usb_device_id *id); static void cpia2_usb_disconnect(struct usb_interface *intf); +static int cpia2_usb_suspend(struct usb_interface *intf, pm_message_t message); +static int cpia2_usb_resume(struct usb_interface *intf); static void free_sbufs(struct camera_data *cam); static void add_APPn(struct camera_data *cam); @@ -74,6 +76,9 @@ static struct usb_driver cpia2_driver = { .name = "cpia2", .probe = cpia2_usb_probe, .disconnect = cpia2_usb_disconnect, + .suspend = cpia2_usb_suspend, + .resume = cpia2_usb_resume, + .reset_resume = cpia2_usb_resume, .id_table = cpia2_id_table }; @@ -218,10 +223,9 @@ static void cpia2_usb_complete(struct urb *urb) return; } - if (!cam->streaming || !cam->present || cam->open_count == 0) { - LOG("Will now stop the streaming: streaming = %d, " - "present=%d, open_count=%d\n", - cam->streaming, cam->present, cam->open_count); + if (!cam->streaming || !video_is_registered(&cam->vdev)) { + LOG("Will now stop the streaming: streaming = %d, present=%d\n", + cam->streaming, video_is_registered(&cam->vdev)); return; } @@ -392,7 +396,7 @@ static int configure_transfer_mode(struct camera_data *cam, unsigned int alt) struct cpia2_command cmd; unsigned char reg; - if(!cam->present) + if (!video_is_registered(&cam->vdev)) return -ENODEV; /*** @@ -752,8 +756,8 @@ int cpia2_usb_stream_pause(struct camera_data *cam) { int ret = 0; if(cam->streaming) { - ret = set_alternate(cam, USBIF_CMDONLY); free_sbufs(cam); + ret = set_alternate(cam, USBIF_CMDONLY); } return ret; } @@ -770,6 +774,10 @@ int cpia2_usb_stream_resume(struct camera_data *cam) cam->first_image_seen = 0; ret = set_alternate(cam, cam->params.camera_state.stream_mode); if(ret == 0) { + /* for some reason the user effects need to be set + again when starting streaming. */ + cpia2_do_command(cam, CPIA2_CMD_SET_USER_EFFECTS, TRANSFER_WRITE, + cam->params.vp_params.user_effects); ret = submit_urbs(cam); } } @@ -784,6 +792,7 @@ int cpia2_usb_stream_resume(struct camera_data *cam) int cpia2_usb_stream_stop(struct camera_data *cam) { int ret; + ret = cpia2_usb_stream_pause(cam); cam->streaming = 0; configure_transfer_mode(cam, 0); @@ -812,7 +821,8 @@ static int cpia2_usb_probe(struct usb_interface *intf, /* If we get to this point, we found a CPiA2 camera */ LOG("CPiA2 USB camera found\n"); - if((cam = cpia2_init_camera_struct()) == NULL) + cam = cpia2_init_camera_struct(intf); + if (cam == NULL) return -ENOMEM; cam->dev = udev; @@ -825,16 +835,9 @@ static int cpia2_usb_probe(struct usb_interface *intf, return ret; } - if ((ret = cpia2_register_camera(cam)) < 0) { - ERR("%s: Failed to register cpia2 camera (ret = %d)\n", __func__, ret); - kfree(cam); - return ret; - } - if((ret = cpia2_init_camera(cam)) < 0) { ERR("%s: failed to initialize cpia2 camera (ret = %d)\n", __func__, ret); - cpia2_unregister_camera(cam); kfree(cam); return ret; } @@ -853,6 +856,13 @@ static int cpia2_usb_probe(struct usb_interface *intf, usb_set_intfdata(intf, cam); + ret = cpia2_register_camera(cam); + if (ret < 0) { + ERR("%s: Failed to register cpia2 camera (ret = %d)\n", __func__, ret); + kfree(cam); + return ret; + } + return 0; } @@ -865,13 +875,16 @@ static void cpia2_usb_disconnect(struct usb_interface *intf) { struct camera_data *cam = usb_get_intfdata(intf); usb_set_intfdata(intf, NULL); - cam->present = 0; DBG("Stopping stream\n"); cpia2_usb_stream_stop(cam); + mutex_lock(&cam->v4l2_lock); DBG("Unregistering camera\n"); cpia2_unregister_camera(cam); + v4l2_device_disconnect(&cam->v4l2_dev); + mutex_unlock(&cam->v4l2_lock); + v4l2_device_put(&cam->v4l2_dev); if(cam->buffers) { DBG("Wakeup waiting processes\n"); @@ -884,14 +897,41 @@ static void cpia2_usb_disconnect(struct usb_interface *intf) DBG("Releasing interface\n"); usb_driver_release_interface(&cpia2_driver, intf); - if (cam->open_count == 0) { - DBG("Freeing camera structure\n"); - kfree(cam); + LOG("CPiA2 camera disconnected.\n"); +} + +static int cpia2_usb_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct camera_data *cam = usb_get_intfdata(intf); + + mutex_lock(&cam->v4l2_lock); + if (cam->streaming) { + cpia2_usb_stream_stop(cam); + cam->streaming = 1; } + mutex_unlock(&cam->v4l2_lock); - LOG("CPiA2 camera disconnected.\n"); + dev_info(&intf->dev, "going into suspend..\n"); + return 0; } +/* Resume device - start device. */ +static int cpia2_usb_resume(struct usb_interface *intf) +{ + struct camera_data *cam = usb_get_intfdata(intf); + + mutex_lock(&cam->v4l2_lock); + v4l2_ctrl_handler_setup(&cam->hdl); + if (cam->streaming) { + cam->streaming = 0; + cpia2_usb_stream_start(cam, + cam->params.camera_state.stream_mode); + } + mutex_unlock(&cam->v4l2_lock); + + dev_info(&intf->dev, "coming out of suspend..\n"); + return 0; +} /****************************************************************************** * diff --git a/drivers/media/video/cpia2/cpia2_v4l.c b/drivers/media/video/cpia2/cpia2_v4l.c index 077eb1db80a1..55e92902a76c 100644 --- a/drivers/media/video/cpia2/cpia2_v4l.c +++ b/drivers/media/video/cpia2/cpia2_v4l.c @@ -39,15 +39,15 @@ #include <linux/videodev2.h> #include <linux/stringify.h> #include <media/v4l2-ioctl.h> +#include <media/v4l2-event.h> #include "cpia2.h" -#include "cpia2dev.h" static int video_nr = -1; module_param(video_nr, int, 0); -MODULE_PARM_DESC(video_nr,"video device to register (0=/dev/video0, etc)"); +MODULE_PARM_DESC(video_nr, "video device to register (0=/dev/video0, etc)"); -static int buffer_size = 68*1024; +static int buffer_size = 68 * 1024; module_param(buffer_size, int, 0); MODULE_PARM_DESC(buffer_size, "Size for each frame buffer in bytes (default 68k)"); @@ -62,18 +62,10 @@ MODULE_PARM_DESC(alternate, "USB Alternate (" __stringify(USBIF_ISO_1) "-" __stringify(USBIF_ISO_6) ", default " __stringify(DEFAULT_ALT) ")"); -static int flicker_freq = 60; -module_param(flicker_freq, int, 0); -MODULE_PARM_DESC(flicker_freq, "Flicker frequency (" __stringify(50) "or" - __stringify(60) ", default " - __stringify(60) ")"); - -static int flicker_mode = NEVER_FLICKER; +static int flicker_mode; module_param(flicker_mode, int, 0); -MODULE_PARM_DESC(flicker_mode, - "Flicker supression (" __stringify(NEVER_FLICKER) "or" - __stringify(ANTI_FLICKER_ON) ", default " - __stringify(NEVER_FLICKER) ")"); +MODULE_PARM_DESC(flicker_mode, "Flicker frequency (0 (disabled), " __stringify(50) " or " + __stringify(60) ", default 0)"); MODULE_AUTHOR("Steve Miller (STMicroelectronics) <steve.miller@st.com>"); MODULE_DESCRIPTION("V4L-driver for STMicroelectronics CPiA2 based cameras"); @@ -82,153 +74,7 @@ MODULE_LICENSE("GPL"); MODULE_VERSION(CPIA_VERSION); #define ABOUT "V4L-Driver for Vision CPiA2 based cameras" - -struct control_menu_info { - int value; - char name[32]; -}; - -static struct control_menu_info framerate_controls[] = -{ - { CPIA2_VP_FRAMERATE_6_25, "6.25 fps" }, - { CPIA2_VP_FRAMERATE_7_5, "7.5 fps" }, - { CPIA2_VP_FRAMERATE_12_5, "12.5 fps" }, - { CPIA2_VP_FRAMERATE_15, "15 fps" }, - { CPIA2_VP_FRAMERATE_25, "25 fps" }, - { CPIA2_VP_FRAMERATE_30, "30 fps" }, -}; -#define NUM_FRAMERATE_CONTROLS (ARRAY_SIZE(framerate_controls)) - -static struct control_menu_info flicker_controls[] = -{ - { NEVER_FLICKER, "Off" }, - { FLICKER_50, "50 Hz" }, - { FLICKER_60, "60 Hz" }, -}; -#define NUM_FLICKER_CONTROLS (ARRAY_SIZE(flicker_controls)) - -static struct control_menu_info lights_controls[] = -{ - { 0, "Off" }, - { 64, "Top" }, - { 128, "Bottom" }, - { 192, "Both" }, -}; -#define NUM_LIGHTS_CONTROLS (ARRAY_SIZE(lights_controls)) -#define GPIO_LIGHTS_MASK 192 - -static struct v4l2_queryctrl controls[] = { - { - .id = V4L2_CID_BRIGHTNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Brightness", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = DEFAULT_BRIGHTNESS, - }, - { - .id = V4L2_CID_CONTRAST, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Contrast", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = DEFAULT_CONTRAST, - }, - { - .id = V4L2_CID_SATURATION, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Saturation", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = DEFAULT_SATURATION, - }, - { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Mirror Horizontally", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Flip Vertically", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, - { - .id = CPIA2_CID_TARGET_KB, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Target KB", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = DEFAULT_TARGET_KB, - }, - { - .id = CPIA2_CID_GPIO, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "GPIO", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = 0, - }, - { - .id = CPIA2_CID_FLICKER_MODE, - .type = V4L2_CTRL_TYPE_MENU, - .name = "Flicker Reduction", - .minimum = 0, - .maximum = NUM_FLICKER_CONTROLS-1, - .step = 1, - .default_value = 0, - }, - { - .id = CPIA2_CID_FRAMERATE, - .type = V4L2_CTRL_TYPE_MENU, - .name = "Framerate", - .minimum = 0, - .maximum = NUM_FRAMERATE_CONTROLS-1, - .step = 1, - .default_value = NUM_FRAMERATE_CONTROLS-1, - }, - { - .id = CPIA2_CID_USB_ALT, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "USB Alternate", - .minimum = USBIF_ISO_1, - .maximum = USBIF_ISO_6, - .step = 1, - .default_value = DEFAULT_ALT, - }, - { - .id = CPIA2_CID_LIGHTS, - .type = V4L2_CTRL_TYPE_MENU, - .name = "Lights", - .minimum = 0, - .maximum = NUM_LIGHTS_CONTROLS-1, - .step = 1, - .default_value = 0, - }, - { - .id = CPIA2_CID_RESET_CAMERA, - .type = V4L2_CTRL_TYPE_BUTTON, - .name = "Reset Camera", - .minimum = 0, - .maximum = 0, - .step = 0, - .default_value = 0, - }, -}; -#define NUM_CONTROLS (ARRAY_SIZE(controls)) - +#define CPIA2_CID_USB_ALT (V4L2_CID_USER_BASE | 0xf000) /****************************************************************************** * @@ -238,38 +84,27 @@ static struct v4l2_queryctrl controls[] = { static int cpia2_open(struct file *file) { struct camera_data *cam = video_drvdata(file); - struct cpia2_fh *fh; - - if (!cam) { - ERR("Internal error, camera_data not found!\n"); - return -ENODEV; - } + int retval = v4l2_fh_open(file); - if (!cam->present) - return -ENODEV; + if (retval) + return retval; - if (cam->open_count == 0) { - if (cpia2_allocate_buffers(cam)) + if (v4l2_fh_is_singular_file(file)) { + if (cpia2_allocate_buffers(cam)) { + v4l2_fh_release(file); return -ENOMEM; + } /* reset the camera */ - if (cpia2_reset_camera(cam) < 0) + if (cpia2_reset_camera(cam) < 0) { + v4l2_fh_release(file); return -EIO; + } cam->APP_len = 0; cam->COM_len = 0; } - fh = kmalloc(sizeof(*fh), GFP_KERNEL); - if (!fh) - return -ENOMEM; - file->private_data = fh; - fh->prio = V4L2_PRIORITY_UNSET; - v4l2_prio_open(&cam->prio, &fh->prio); - fh->mmapped = 0; - - ++cam->open_count; - cpia2_dbg_dump_registers(cam); return 0; } @@ -283,37 +118,22 @@ static int cpia2_close(struct file *file) { struct video_device *dev = video_devdata(file); struct camera_data *cam = video_get_drvdata(dev); - struct cpia2_fh *fh = file->private_data; - if (cam->present && - (cam->open_count == 1 || fh->prio == V4L2_PRIORITY_RECORD)) { + if (video_is_registered(&cam->vdev) && v4l2_fh_is_singular_file(file)) { cpia2_usb_stream_stop(cam); - if (cam->open_count == 1) { - /* save camera state for later open */ - cpia2_save_camera_state(cam); + /* save camera state for later open */ + cpia2_save_camera_state(cam); - cpia2_set_low_power(cam); - cpia2_free_buffers(cam); - } + cpia2_set_low_power(cam); + cpia2_free_buffers(cam); } - if (fh->mmapped) + if (cam->stream_fh == file->private_data) { + cam->stream_fh = NULL; cam->mmapped = 0; - v4l2_prio_close(&cam->prio, fh->prio); - file->private_data = NULL; - kfree(fh); - - if (--cam->open_count == 0) { - cpia2_free_buffers(cam); - if (!cam->present) { - video_unregister_device(dev); - kfree(cam); - return 0; - } } - - return 0; + return v4l2_fh_release(file); } /****************************************************************************** @@ -327,16 +147,9 @@ static ssize_t cpia2_v4l_read(struct file *file, char __user *buf, size_t count, struct camera_data *cam = video_drvdata(file); int noblock = file->f_flags&O_NONBLOCK; - struct cpia2_fh *fh = file->private_data; - if(!cam) return -EINVAL; - /* Priority check */ - if(fh->prio != V4L2_PRIORITY_RECORD) { - return -EBUSY; - } - return cpia2_read(cam, buf, count, noblock); } @@ -349,15 +162,6 @@ static ssize_t cpia2_v4l_read(struct file *file, char __user *buf, size_t count, static unsigned int cpia2_v4l_poll(struct file *filp, struct poll_table_struct *wait) { struct camera_data *cam = video_drvdata(filp); - struct cpia2_fh *fh = filp->private_data; - - if(!cam) - return POLLERR; - - /* Priority check */ - if(fh->prio != V4L2_PRIORITY_RECORD) { - return POLLERR; - } return cpia2_poll(cam, filp, wait); } @@ -384,36 +188,13 @@ static int sync(struct camera_data *cam, int frame_nr) mutex_lock(&cam->v4l2_lock); if (signal_pending(current)) return -ERESTARTSYS; - if(!cam->present) + if (!video_is_registered(&cam->vdev)) return -ENOTTY; } } /****************************************************************************** * - * ioctl_set_gpio - * - *****************************************************************************/ - -static long cpia2_default(struct file *file, void *fh, bool valid_prio, - int cmd, void *arg) -{ - struct camera_data *cam = video_drvdata(file); - __u32 gpio_val; - - if (cmd != CPIA2_CID_GPIO) - return -EINVAL; - - gpio_val = *(__u32*) arg; - - if (gpio_val &~ 0xFFU) - return -EINVAL; - - return cpia2_set_gpio(cam, (unsigned char)gpio_val); -} - -/****************************************************************************** - * * ioctl_querycap * * V4L2 device capabilities @@ -465,9 +246,11 @@ static int cpia2_querycap(struct file *file, void *fh, struct v4l2_capability *v if (usb_make_path(cam->dev, vc->bus_info, sizeof(vc->bus_info)) <0) memset(vc->bus_info,0, sizeof(vc->bus_info)); - vc->capabilities = V4L2_CAP_VIDEO_CAPTURE | + vc->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; + vc->capabilities = vc->device_caps | + V4L2_CAP_DEVICE_CAPS; return 0; } @@ -610,22 +393,12 @@ static int cpia2_s_fmt_vid_cap(struct file *file, void *_fh, struct v4l2_format *f) { struct camera_data *cam = video_drvdata(file); - struct cpia2_fh *fh = _fh; int err, frame; - err = v4l2_prio_check(&cam->prio, fh->prio); - if (err) - return err; err = cpia2_try_fmt_vid_cap(file, _fh, f); if(err != 0) return err; - /* Ensure that only this process can change the format. */ - err = v4l2_prio_change(&cam->prio, &fh->prio, V4L2_PRIORITY_RECORD); - if(err != 0) { - return err; - } - cam->pixelformat = f->fmt.pix.pixelformat; /* NOTE: This should be set to 1 for MJPEG, but some apps don't handle @@ -713,240 +486,126 @@ static int cpia2_cropcap(struct file *file, void *fh, struct v4l2_cropcap *c) return 0; } -/****************************************************************************** - * - * ioctl_queryctrl - * - * V4L2 query possible control variables - * - *****************************************************************************/ +struct framerate_info { + int value; + struct v4l2_fract period; +}; -static int cpia2_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *c) +static const struct framerate_info framerate_controls[] = { + { CPIA2_VP_FRAMERATE_6_25, { 4, 25 } }, + { CPIA2_VP_FRAMERATE_7_5, { 2, 15 } }, + { CPIA2_VP_FRAMERATE_12_5, { 2, 25 } }, + { CPIA2_VP_FRAMERATE_15, { 1, 15 } }, + { CPIA2_VP_FRAMERATE_25, { 1, 25 } }, + { CPIA2_VP_FRAMERATE_30, { 1, 30 } }, +}; + +static int cpia2_g_parm(struct file *file, void *fh, struct v4l2_streamparm *p) { struct camera_data *cam = video_drvdata(file); + struct v4l2_captureparm *cap = &p->parm.capture; int i; - for(i=0; i<NUM_CONTROLS; ++i) { - if(c->id == controls[i].id) { - memcpy(c, controls+i, sizeof(*c)); - break; - } - } - - if(i == NUM_CONTROLS) + if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - /* Some devices have additional limitations */ - switch(c->id) { - case V4L2_CID_BRIGHTNESS: - /*** - * Don't let the register be set to zero - bug in VP4 - * flash of full brightness - ***/ - if (cam->params.pnp_id.device_type == DEVICE_STV_672) - c->minimum = 1; - break; - case V4L2_CID_VFLIP: - // VP5 Only - if(cam->params.pnp_id.device_type == DEVICE_STV_672) - c->flags |= V4L2_CTRL_FLAG_DISABLED; - break; - case CPIA2_CID_FRAMERATE: - if(cam->params.pnp_id.device_type == DEVICE_STV_672 && - cam->params.version.sensor_flags==CPIA2_VP_SENSOR_FLAGS_500){ - // Maximum 15fps - for(i=0; i<c->maximum; ++i) { - if(framerate_controls[i].value == - CPIA2_VP_FRAMERATE_15) { - c->maximum = i; - c->default_value = i; - } - } + cap->capability = V4L2_CAP_TIMEPERFRAME; + cap->readbuffers = cam->num_frames; + for (i = 0; i < ARRAY_SIZE(framerate_controls); i++) + if (cam->params.vp_params.frame_rate == framerate_controls[i].value) { + cap->timeperframe = framerate_controls[i].period; + break; } - break; - case CPIA2_CID_FLICKER_MODE: - // Flicker control only valid for 672. - if(cam->params.pnp_id.device_type != DEVICE_STV_672) - c->flags |= V4L2_CTRL_FLAG_DISABLED; - break; - case CPIA2_CID_LIGHTS: - // Light control only valid for the QX5 Microscope. - if(cam->params.pnp_id.product != 0x151) - c->flags |= V4L2_CTRL_FLAG_DISABLED; - break; - default: - break; - } - return 0; } -/****************************************************************************** - * - * ioctl_querymenu - * - * V4L2 query possible control variables - * - *****************************************************************************/ - -static int cpia2_querymenu(struct file *file, void *fh, struct v4l2_querymenu *m) +static int cpia2_s_parm(struct file *file, void *fh, struct v4l2_streamparm *p) { struct camera_data *cam = video_drvdata(file); + struct v4l2_captureparm *cap = &p->parm.capture; + struct v4l2_fract tpf = cap->timeperframe; + int max = ARRAY_SIZE(framerate_controls) - 1; + int ret; + int i; - switch(m->id) { - case CPIA2_CID_FLICKER_MODE: - if (m->index >= NUM_FLICKER_CONTROLS) - return -EINVAL; + ret = cpia2_g_parm(file, fh, p); + if (ret || !tpf.denominator || !tpf.numerator) + return ret; + + /* Maximum 15 fps for this model */ + if (cam->params.pnp_id.device_type == DEVICE_STV_672 && + cam->params.version.sensor_flags == CPIA2_VP_SENSOR_FLAGS_500) + max -= 2; + for (i = 0; i <= max; i++) { + struct v4l2_fract f1 = tpf; + struct v4l2_fract f2 = framerate_controls[i].period; + + f1.numerator *= f2.denominator; + f2.numerator *= f1.denominator; + if (f1.numerator >= f2.numerator) + break; + } + if (i > max) + i = max; + cap->timeperframe = framerate_controls[i].period; + return cpia2_set_fps(cam, framerate_controls[i].value); +} - strcpy(m->name, flicker_controls[m->index].name); - break; - case CPIA2_CID_FRAMERATE: - { - int maximum = NUM_FRAMERATE_CONTROLS - 1; - if(cam->params.pnp_id.device_type == DEVICE_STV_672 && - cam->params.version.sensor_flags==CPIA2_VP_SENSOR_FLAGS_500){ - // Maximum 15fps - int i; - for(i=0; i<maximum; ++i) { - if(framerate_controls[i].value == - CPIA2_VP_FRAMERATE_15) - maximum = i; - } - } - if (m->index > maximum) - return -EINVAL; +static const struct { + u32 width; + u32 height; +} cpia2_framesizes[] = { + { 640, 480 }, + { 352, 288 }, + { 320, 240 }, + { 288, 216 }, + { 256, 192 }, + { 224, 168 }, + { 192, 144 }, + { 176, 144 }, +}; - strcpy(m->name, framerate_controls[m->index].name); - break; - } - case CPIA2_CID_LIGHTS: - if (m->index >= NUM_LIGHTS_CONTROLS) - return -EINVAL; +static int cpia2_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ - strcpy(m->name, lights_controls[m->index].name); - break; - default: + if (fsize->pixel_format != V4L2_PIX_FMT_MJPEG && + fsize->pixel_format != V4L2_PIX_FMT_JPEG) return -EINVAL; - } + if (fsize->index >= ARRAY_SIZE(cpia2_framesizes)) + return -EINVAL; + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = cpia2_framesizes[fsize->index].width; + fsize->discrete.height = cpia2_framesizes[fsize->index].height; return 0; } -/****************************************************************************** - * - * ioctl_g_ctrl - * - * V4L2 get the value of a control variable - * - *****************************************************************************/ - -static int cpia2_g_ctrl(struct file *file, void *fh, struct v4l2_control *c) +static int cpia2_enum_frameintervals(struct file *file, void *fh, + struct v4l2_frmivalenum *fival) { struct camera_data *cam = video_drvdata(file); + int max = ARRAY_SIZE(framerate_controls) - 1; + int i; - switch(c->id) { - case V4L2_CID_BRIGHTNESS: - cpia2_do_command(cam, CPIA2_CMD_GET_VP_BRIGHTNESS, - TRANSFER_READ, 0); - c->value = cam->params.color_params.brightness; - break; - case V4L2_CID_CONTRAST: - cpia2_do_command(cam, CPIA2_CMD_GET_CONTRAST, - TRANSFER_READ, 0); - c->value = cam->params.color_params.contrast; - break; - case V4L2_CID_SATURATION: - cpia2_do_command(cam, CPIA2_CMD_GET_VP_SATURATION, - TRANSFER_READ, 0); - c->value = cam->params.color_params.saturation; - break; - case V4L2_CID_HFLIP: - cpia2_do_command(cam, CPIA2_CMD_GET_USER_EFFECTS, - TRANSFER_READ, 0); - c->value = (cam->params.vp_params.user_effects & - CPIA2_VP_USER_EFFECTS_MIRROR) != 0; - break; - case V4L2_CID_VFLIP: - cpia2_do_command(cam, CPIA2_CMD_GET_USER_EFFECTS, - TRANSFER_READ, 0); - c->value = (cam->params.vp_params.user_effects & - CPIA2_VP_USER_EFFECTS_FLIP) != 0; - break; - case CPIA2_CID_TARGET_KB: - c->value = cam->params.vc_params.target_kb; - break; - case CPIA2_CID_GPIO: - cpia2_do_command(cam, CPIA2_CMD_GET_VP_GPIO_DATA, - TRANSFER_READ, 0); - c->value = cam->params.vp_params.gpio_data; - break; - case CPIA2_CID_FLICKER_MODE: - { - int i, mode; - cpia2_do_command(cam, CPIA2_CMD_GET_FLICKER_MODES, - TRANSFER_READ, 0); - if(cam->params.flicker_control.cam_register & - CPIA2_VP_FLICKER_MODES_NEVER_FLICKER) { - mode = NEVER_FLICKER; - } else { - if(cam->params.flicker_control.cam_register & - CPIA2_VP_FLICKER_MODES_50HZ) { - mode = FLICKER_50; - } else { - mode = FLICKER_60; - } - } - for(i=0; i<NUM_FLICKER_CONTROLS; i++) { - if(flicker_controls[i].value == mode) { - c->value = i; - break; - } - } - if(i == NUM_FLICKER_CONTROLS) - return -EINVAL; - break; - } - case CPIA2_CID_FRAMERATE: - { - int maximum = NUM_FRAMERATE_CONTROLS - 1; - int i; - for(i=0; i<= maximum; i++) { - if(cam->params.vp_params.frame_rate == - framerate_controls[i].value) - break; - } - if(i > maximum) - return -EINVAL; - c->value = i; - break; - } - case CPIA2_CID_USB_ALT: - c->value = cam->params.camera_state.stream_mode; - break; - case CPIA2_CID_LIGHTS: - { - int i; - cpia2_do_command(cam, CPIA2_CMD_GET_VP_GPIO_DATA, - TRANSFER_READ, 0); - for(i=0; i<NUM_LIGHTS_CONTROLS; i++) { - if((cam->params.vp_params.gpio_data&GPIO_LIGHTS_MASK) == - lights_controls[i].value) { - break; - } - } - if(i == NUM_LIGHTS_CONTROLS) - return -EINVAL; - c->value = i; - break; - } - case CPIA2_CID_RESET_CAMERA: + if (fival->pixel_format != V4L2_PIX_FMT_MJPEG && + fival->pixel_format != V4L2_PIX_FMT_JPEG) return -EINVAL; - default: - return -EINVAL; - } - - DBG("Get control id:%d, value:%d\n", c->id, c->value); + /* Maximum 15 fps for this model */ + if (cam->params.pnp_id.device_type == DEVICE_STV_672 && + cam->params.version.sensor_flags == CPIA2_VP_SENSOR_FLAGS_500) + max -= 2; + if (fival->index > max) + return -EINVAL; + for (i = 0; i < ARRAY_SIZE(cpia2_framesizes); i++) + if (fival->width == cpia2_framesizes[i].width && + fival->height == cpia2_framesizes[i].height) + break; + if (i == ARRAY_SIZE(cpia2_framesizes)) + return -EINVAL; + fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; + fival->discrete = framerate_controls[fival->index].period; return 0; } @@ -958,72 +617,54 @@ static int cpia2_g_ctrl(struct file *file, void *fh, struct v4l2_control *c) * *****************************************************************************/ -static int cpia2_s_ctrl(struct file *file, void *fh, struct v4l2_control *c) +static int cpia2_s_ctrl(struct v4l2_ctrl *ctrl) { - struct camera_data *cam = video_drvdata(file); - int i; - int retval = 0; + struct camera_data *cam = + container_of(ctrl->handler, struct camera_data, hdl); + static const int flicker_table[] = { + NEVER_FLICKER, + FLICKER_50, + FLICKER_60, + }; - DBG("Set control id:%d, value:%d\n", c->id, c->value); - - /* Check that the value is in range */ - for(i=0; i<NUM_CONTROLS; i++) { - if(c->id == controls[i].id) { - if(c->value < controls[i].minimum || - c->value > controls[i].maximum) { - return -EINVAL; - } - break; - } - } - if(i == NUM_CONTROLS) - return -EINVAL; + DBG("Set control id:%d, value:%d\n", ctrl->id, ctrl->val); - switch(c->id) { + switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: - cpia2_set_brightness(cam, c->value); + cpia2_set_brightness(cam, ctrl->val); break; case V4L2_CID_CONTRAST: - cpia2_set_contrast(cam, c->value); + cpia2_set_contrast(cam, ctrl->val); break; case V4L2_CID_SATURATION: - cpia2_set_saturation(cam, c->value); + cpia2_set_saturation(cam, ctrl->val); break; case V4L2_CID_HFLIP: - cpia2_set_property_mirror(cam, c->value); + cpia2_set_property_mirror(cam, ctrl->val); break; case V4L2_CID_VFLIP: - cpia2_set_property_flip(cam, c->value); + cpia2_set_property_flip(cam, ctrl->val); break; - case CPIA2_CID_TARGET_KB: - retval = cpia2_set_target_kb(cam, c->value); + case V4L2_CID_POWER_LINE_FREQUENCY: + return cpia2_set_flicker_mode(cam, flicker_table[ctrl->val]); + case V4L2_CID_ILLUMINATORS_1: + return cpia2_set_gpio(cam, (cam->top_light->val << 6) | + (cam->bottom_light->val << 7)); + case V4L2_CID_JPEG_ACTIVE_MARKER: + cam->params.compression.inhibit_htables = + !(ctrl->val & V4L2_JPEG_ACTIVE_MARKER_DHT); break; - case CPIA2_CID_GPIO: - retval = cpia2_set_gpio(cam, c->value); - break; - case CPIA2_CID_FLICKER_MODE: - retval = cpia2_set_flicker_mode(cam, - flicker_controls[c->value].value); - break; - case CPIA2_CID_FRAMERATE: - retval = cpia2_set_fps(cam, framerate_controls[c->value].value); + case V4L2_CID_JPEG_COMPRESSION_QUALITY: + cam->params.vc_params.quality = ctrl->val; break; case CPIA2_CID_USB_ALT: - retval = cpia2_usb_change_streaming_alternate(cam, c->value); - break; - case CPIA2_CID_LIGHTS: - retval = cpia2_set_gpio(cam, lights_controls[c->value].value); - break; - case CPIA2_CID_RESET_CAMERA: - cpia2_usb_stream_pause(cam); - cpia2_reset_camera(cam); - cpia2_usb_stream_resume(cam); + cam->params.camera_state.stream_mode = ctrl->val; break; default: - retval = -EINVAL; + return -EINVAL; } - return retval; + return 0; } /****************************************************************************** @@ -1084,6 +725,8 @@ static int cpia2_s_jpegcomp(struct file *file, void *fh, struct v4l2_jpegcompres cam->params.compression.inhibit_htables = !(parms->jpeg_markers & V4L2_JPEG_MARKER_DHT); + parms->jpeg_markers &= V4L2_JPEG_MARKER_DQT | V4L2_JPEG_MARKER_DRI | + V4L2_JPEG_MARKER_DHT; if(parms->APP_len != 0) { if(parms->APP_len > 0 && @@ -1270,12 +913,12 @@ static int cpia2_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) struct framebuf *cb=cam->curbuff; mutex_unlock(&cam->v4l2_lock); wait_event_interruptible(cam->wq_stream, - !cam->present || + !video_is_registered(&cam->vdev) || (cb=cam->curbuff)->status == FRAME_READY); mutex_lock(&cam->v4l2_lock); if (signal_pending(current)) return -ERESTARTSYS; - if(!cam->present) + if (!video_is_registered(&cam->vdev)) return -ENOTTY; frame = cb->num; } @@ -1299,56 +942,39 @@ static int cpia2_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) return 0; } -static int cpia2_g_priority(struct file *file, void *_fh, enum v4l2_priority *p) -{ - struct cpia2_fh *fh = _fh; - - *p = fh->prio; - return 0; -} - -static int cpia2_s_priority(struct file *file, void *_fh, enum v4l2_priority prio) -{ - struct camera_data *cam = video_drvdata(file); - struct cpia2_fh *fh = _fh; - - if (cam->streaming && prio != fh->prio && - fh->prio == V4L2_PRIORITY_RECORD) - /* Can't drop record priority while streaming */ - return -EBUSY; - - if (prio == V4L2_PRIORITY_RECORD && prio != fh->prio && - v4l2_prio_max(&cam->prio) == V4L2_PRIORITY_RECORD) - /* Only one program can record at a time */ - return -EBUSY; - return v4l2_prio_change(&cam->prio, &fh->prio, prio); -} - static int cpia2_streamon(struct file *file, void *fh, enum v4l2_buf_type type) { struct camera_data *cam = video_drvdata(file); + int ret = -EINVAL; DBG("VIDIOC_STREAMON, streaming=%d\n", cam->streaming); if (!cam->mmapped || type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - if (!cam->streaming) - return cpia2_usb_stream_start(cam, + if (!cam->streaming) { + ret = cpia2_usb_stream_start(cam, cam->params.camera_state.stream_mode); - return -EINVAL; + if (!ret) + v4l2_ctrl_grab(cam->usb_alt, true); + } + return ret; } static int cpia2_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) { struct camera_data *cam = video_drvdata(file); + int ret = -EINVAL; DBG("VIDIOC_STREAMOFF, streaming=%d\n", cam->streaming); if (!cam->mmapped || type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - if (cam->streaming) - return cpia2_usb_stream_stop(cam); - return -EINVAL; + if (cam->streaming) { + ret = cpia2_usb_stream_stop(cam); + if (!ret) + v4l2_ctrl_grab(cam->usb_alt, false); + } + return ret; } /****************************************************************************** @@ -1361,16 +987,10 @@ static int cpia2_mmap(struct file *file, struct vm_area_struct *area) struct camera_data *cam = video_drvdata(file); int retval; - /* Priority check */ - struct cpia2_fh *fh = file->private_data; - if(fh->prio != V4L2_PRIORITY_RECORD) { - return -EBUSY; - } - retval = cpia2_remap_buffer(cam, area); if(!retval) - fh->mmapped = 1; + cam->stream_fh = file->private_data; return retval; } @@ -1388,15 +1008,13 @@ static void reset_camera_struct_v4l(struct camera_data *cam) cam->frame_size = buffer_size; cam->num_frames = num_buffers; - /* FlickerModes */ + /* Flicker modes */ cam->params.flicker_control.flicker_mode_req = flicker_mode; - cam->params.flicker_control.mains_frequency = flicker_freq; - /* streamMode */ + /* stream modes */ cam->params.camera_state.stream_mode = alternate; cam->pixelformat = V4L2_PIX_FMT_JPEG; - v4l2_prio_init(&cam->prio); } static const struct v4l2_ioctl_ops cpia2_ioctl_ops = { @@ -1408,10 +1026,6 @@ static const struct v4l2_ioctl_ops cpia2_ioctl_ops = { .vidioc_g_fmt_vid_cap = cpia2_g_fmt_vid_cap, .vidioc_s_fmt_vid_cap = cpia2_s_fmt_vid_cap, .vidioc_try_fmt_vid_cap = cpia2_try_fmt_vid_cap, - .vidioc_queryctrl = cpia2_queryctrl, - .vidioc_querymenu = cpia2_querymenu, - .vidioc_g_ctrl = cpia2_g_ctrl, - .vidioc_s_ctrl = cpia2_s_ctrl, .vidioc_g_jpegcomp = cpia2_g_jpegcomp, .vidioc_s_jpegcomp = cpia2_s_jpegcomp, .vidioc_cropcap = cpia2_cropcap, @@ -1421,9 +1035,12 @@ static const struct v4l2_ioctl_ops cpia2_ioctl_ops = { .vidioc_dqbuf = cpia2_dqbuf, .vidioc_streamon = cpia2_streamon, .vidioc_streamoff = cpia2_streamoff, - .vidioc_g_priority = cpia2_g_priority, - .vidioc_s_priority = cpia2_s_priority, - .vidioc_default = cpia2_default, + .vidioc_s_parm = cpia2_s_parm, + .vidioc_g_parm = cpia2_g_parm, + .vidioc_enum_framesizes = cpia2_enum_framesizes, + .vidioc_enum_frameintervals = cpia2_enum_frameintervals, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; /*** @@ -1444,7 +1061,21 @@ static struct video_device cpia2_template = { .name = "CPiA2 Camera", .fops = &cpia2_fops, .ioctl_ops = &cpia2_ioctl_ops, - .release = video_device_release, + .release = video_device_release_empty, +}; + +void cpia2_camera_release(struct v4l2_device *v4l2_dev) +{ + struct camera_data *cam = + container_of(v4l2_dev, struct camera_data, v4l2_dev); + + v4l2_ctrl_handler_free(&cam->hdl); + v4l2_device_unregister(&cam->v4l2_dev); + kfree(cam); +} + +static const struct v4l2_ctrl_ops cpia2_ctrl_ops = { + .s_ctrl = cpia2_s_ctrl, }; /****************************************************************************** @@ -1454,20 +1085,78 @@ static struct video_device cpia2_template = { *****************************************************************************/ int cpia2_register_camera(struct camera_data *cam) { - cam->vdev = video_device_alloc(); - if(!cam->vdev) - return -ENOMEM; + struct v4l2_ctrl_handler *hdl = &cam->hdl; + struct v4l2_ctrl_config cpia2_usb_alt = { + .ops = &cpia2_ctrl_ops, + .id = CPIA2_CID_USB_ALT, + .name = "USB Alternate", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = USBIF_ISO_1, + .max = USBIF_ISO_6, + .step = 1, + }; + int ret; + + v4l2_ctrl_handler_init(hdl, 12); + v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, + V4L2_CID_BRIGHTNESS, + cam->params.pnp_id.device_type == DEVICE_STV_672 ? 1 : 0, + 255, 1, DEFAULT_BRIGHTNESS); + v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, DEFAULT_CONTRAST); + v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, + V4L2_CID_SATURATION, 0, 255, 1, DEFAULT_SATURATION); + v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, + V4L2_CID_JPEG_ACTIVE_MARKER, 0, + V4L2_JPEG_ACTIVE_MARKER_DHT, 0, + V4L2_JPEG_ACTIVE_MARKER_DHT); + v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, + V4L2_CID_JPEG_COMPRESSION_QUALITY, 1, + 100, 1, 100); + cpia2_usb_alt.def = alternate; + cam->usb_alt = v4l2_ctrl_new_custom(hdl, &cpia2_usb_alt, NULL); + /* VP5 Only */ + if (cam->params.pnp_id.device_type != DEVICE_STV_672) + v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + /* Flicker control only valid for 672 */ + if (cam->params.pnp_id.device_type == DEVICE_STV_672) + v4l2_ctrl_new_std_menu(hdl, &cpia2_ctrl_ops, + V4L2_CID_POWER_LINE_FREQUENCY, + V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0, 0); + /* Light control only valid for the QX5 Microscope */ + if (cam->params.pnp_id.product == 0x151) { + cam->top_light = v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, + V4L2_CID_ILLUMINATORS_1, 0, 1, 1, 0); + cam->bottom_light = v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, + V4L2_CID_ILLUMINATORS_2, 0, 1, 1, 0); + v4l2_ctrl_cluster(2, &cam->top_light); + } - memcpy(cam->vdev, &cpia2_template, sizeof(cpia2_template)); - video_set_drvdata(cam->vdev, cam); - cam->vdev->lock = &cam->v4l2_lock; + if (hdl->error) { + ret = hdl->error; + v4l2_ctrl_handler_free(hdl); + return ret; + } + + cam->vdev = cpia2_template; + video_set_drvdata(&cam->vdev, cam); + cam->vdev.lock = &cam->v4l2_lock; + cam->vdev.ctrl_handler = hdl; + cam->vdev.v4l2_dev = &cam->v4l2_dev; + set_bit(V4L2_FL_USE_FH_PRIO, &cam->vdev.flags); + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &cam->vdev.flags); reset_camera_struct_v4l(cam); /* register v4l device */ - if (video_register_device(cam->vdev, VFL_TYPE_GRABBER, video_nr) < 0) { + if (video_register_device(&cam->vdev, VFL_TYPE_GRABBER, video_nr) < 0) { ERR("video_register_device failed\n"); - video_device_release(cam->vdev); return -ENODEV; } @@ -1481,13 +1170,7 @@ int cpia2_register_camera(struct camera_data *cam) *****************************************************************************/ void cpia2_unregister_camera(struct camera_data *cam) { - if (!cam->open_count) { - video_unregister_device(cam->vdev); - } else { - LOG("%s removed while open, deferring " - "video_unregister_device\n", - video_device_node_name(cam->vdev)); - } + video_unregister_device(&cam->vdev); } /****************************************************************************** @@ -1524,23 +1207,12 @@ static void __init check_parameters(void) LOG("alternate specified is invalid, using %d\n", alternate); } - if (flicker_mode != NEVER_FLICKER && flicker_mode != ANTI_FLICKER_ON) { - flicker_mode = NEVER_FLICKER; + if (flicker_mode != 0 && flicker_mode != FLICKER_50 && flicker_mode != FLICKER_60) { + flicker_mode = 0; LOG("Flicker mode specified is invalid, using %d\n", flicker_mode); } - if (flicker_freq != FLICKER_50 && flicker_freq != FLICKER_60) { - flicker_freq = FLICKER_60; - LOG("Flicker mode specified is invalid, using %d\n", - flicker_freq); - } - - if(video_nr < -1 || video_nr > 64) { - video_nr = -1; - LOG("invalid video_nr specified, must be -1 to 64\n"); - } - DBG("Using %d buffers, each %d bytes, alternate=%d\n", num_buffers, buffer_size, alternate); } diff --git a/drivers/media/video/cpia2/cpia2dev.h b/drivers/media/video/cpia2/cpia2dev.h deleted file mode 100644 index f66691fe5a35..000000000000 --- a/drivers/media/video/cpia2/cpia2dev.h +++ /dev/null @@ -1,50 +0,0 @@ -/**************************************************************************** - * - * Filename: cpia2dev.h - * - * Copyright 2001, STMicrolectronics, Inc. - * - * Contact: steve.miller@st.com - * - * Description: - * This file provides definitions for applications wanting to use the - * cpia2 driver beyond the generic v4l capabilities. - * - * 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, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - ****************************************************************************/ - -#ifndef CPIA2_DEV_HEADER -#define CPIA2_DEV_HEADER - -#include <linux/videodev2.h> - -/*** - * The following defines are ioctl numbers based on video4linux private ioctls, - * which can range from 192 (BASE_VIDIOCPRIVATE) to 255. All of these take int - * args - */ -#define CPIA2_IOC_SET_GPIO _IOW('v', BASE_VIDIOC_PRIVATE + 17, __u32) - -/* V4L2 driver specific controls */ -#define CPIA2_CID_TARGET_KB (V4L2_CID_PRIVATE_BASE+0) -#define CPIA2_CID_GPIO (V4L2_CID_PRIVATE_BASE+1) -#define CPIA2_CID_FLICKER_MODE (V4L2_CID_PRIVATE_BASE+2) -#define CPIA2_CID_FRAMERATE (V4L2_CID_PRIVATE_BASE+3) -#define CPIA2_CID_USB_ALT (V4L2_CID_PRIVATE_BASE+4) -#define CPIA2_CID_LIGHTS (V4L2_CID_PRIVATE_BASE+5) -#define CPIA2_CID_RESET_CAMERA (V4L2_CID_PRIVATE_BASE+6) - -#endif diff --git a/drivers/media/video/cx18/cx18-alsa-main.c b/drivers/media/video/cx18/cx18-alsa-main.c index e118361c2e7b..6d2a98246b6d 100644 --- a/drivers/media/video/cx18/cx18-alsa-main.c +++ b/drivers/media/video/cx18/cx18-alsa-main.c @@ -285,6 +285,7 @@ static void __exit cx18_alsa_exit(void) drv = driver_find("cx18", &pci_bus_type); ret = driver_for_each_device(drv, NULL, NULL, cx18_alsa_exit_callback); + (void)ret; /* suppress compiler warning */ cx18_ext_init = NULL; printk(KERN_INFO "cx18-alsa: module unload complete\n"); diff --git a/drivers/media/video/cx18/cx18-alsa-pcm.c b/drivers/media/video/cx18/cx18-alsa-pcm.c index 82d195be9197..7a5b84a86bb3 100644 --- a/drivers/media/video/cx18/cx18-alsa-pcm.c +++ b/drivers/media/video/cx18/cx18-alsa-pcm.c @@ -190,7 +190,7 @@ static int snd_cx18_pcm_capture_open(struct snd_pcm_substream *substream) ret = cx18_start_v4l2_encode_stream(s); snd_cx18_unlock(cxsc); - return 0; + return ret; } static int snd_cx18_pcm_capture_close(struct snd_pcm_substream *substream) @@ -199,12 +199,11 @@ static int snd_cx18_pcm_capture_close(struct snd_pcm_substream *substream) struct v4l2_device *v4l2_dev = cxsc->v4l2_dev; struct cx18 *cx = to_cx18(v4l2_dev); struct cx18_stream *s; - int ret; /* Instruct the cx18 to stop sending packets */ snd_cx18_lock(cxsc); s = &cx->streams[CX18_ENC_STREAM_TYPE_PCM]; - ret = cx18_stop_v4l2_encode_stream(s, 0); + cx18_stop_v4l2_encode_stream(s, 0); clear_bit(CX18_F_S_STREAMING, &s->s_flags); cx18_release_stream(s); @@ -252,13 +251,10 @@ static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs, static int snd_cx18_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - int ret; - dprintk("%s called\n", __func__); - ret = snd_pcm_alloc_vmalloc_buffer(substream, + return snd_pcm_alloc_vmalloc_buffer(substream, params_buffer_bytes(params)); - return 0; } static int snd_cx18_pcm_hw_free(struct snd_pcm_substream *substream) diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c index be49f68ddf37..35fde4e931f5 100644 --- a/drivers/media/video/cx18/cx18-ioctl.c +++ b/drivers/media/video/cx18/cx18-ioctl.c @@ -1137,7 +1137,7 @@ static long cx18_default(struct file *file, void *fh, bool valid_prio, } default: - return -EINVAL; + return -ENOTTY; } return 0; } diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c index 0c7796e76ac0..ed8118390b02 100644 --- a/drivers/media/video/cx18/cx18-mailbox.c +++ b/drivers/media/video/cx18/cx18-mailbox.c @@ -595,9 +595,8 @@ void cx18_api_epu_cmd_irq(struct cx18 *cx, int rpu) static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) { const struct cx18_api_info *info = find_api_info(cmd); - u32 state, irq, req, ack, err; + u32 irq, req, ack, err; struct cx18_mailbox __iomem *mb; - u32 __iomem *xpu_state; wait_queue_head_t *waitq; struct mutex *mb_lock; unsigned long int t0, timeout, ret; @@ -628,14 +627,12 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) mb_lock = &cx->epu2apu_mb_lock; irq = IRQ_EPU_TO_APU; mb = &cx->scb->epu2apu_mb; - xpu_state = &cx->scb->apu_state; break; case CPU: waitq = &cx->mb_cpu_waitq; mb_lock = &cx->epu2cpu_mb_lock; irq = IRQ_EPU_TO_CPU; mb = &cx->scb->epu2cpu_mb; - xpu_state = &cx->scb->cpu_state; break; default: CX18_WARN("Unknown RPU (%d) for API call\n", info->rpu); @@ -653,7 +650,6 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) * by a signal, we may get here and find a busy mailbox. After waiting, * mark it "not busy" from our end, if the XPU hasn't ack'ed it still. */ - state = cx18_readl(cx, xpu_state); req = cx18_readl(cx, &mb->request); timeout = msecs_to_jiffies(10); ret = wait_event_timeout(*waitq, diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c index 638cca156b58..4185bcb80ca3 100644 --- a/drivers/media/video/cx18/cx18-streams.c +++ b/drivers/media/video/cx18/cx18-streams.c @@ -980,7 +980,6 @@ void cx18_stop_all_captures(struct cx18 *cx) int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end) { struct cx18 *cx = s->cx; - unsigned long then; if (!cx18_stream_enabled(s)) return -EINVAL; @@ -999,8 +998,6 @@ int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end) else cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 1, s->handle); - then = jiffies; - if (s->type == CX18_ENC_STREAM_TYPE_MPG && gop_end) { CX18_INFO("ignoring gop_end: not (yet?) supported by the firmware\n"); } diff --git a/drivers/media/video/cx231xx/cx231xx-417.c b/drivers/media/video/cx231xx/cx231xx-417.c index d4327dab5a36..ce2f62238a19 100644 --- a/drivers/media/video/cx231xx/cx231xx-417.c +++ b/drivers/media/video/cx231xx/cx231xx-417.c @@ -1095,7 +1095,7 @@ static int cx231xx_initialize_codec(struct cx231xx *dev) { int version; int retval; - u32 i, data[7]; + u32 i; u32 val = 0; dprintk(1, "%s()\n", __func__); @@ -1154,6 +1154,11 @@ static int cx231xx_initialize_codec(struct cx231xx *dev) CX231xx_CUSTOM_EXTENSION_USR_DATA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); */ + +#if 0 + /* TODO */ + u32 data[7]; + /* Setup to capture VBI */ data[0] = 0x0001BD00; data[1] = 1; /* frames per interrupt */ @@ -1162,7 +1167,7 @@ static int cx231xx_initialize_codec(struct cx231xx *dev) data[4] = 0x206080C0; /* stop codes */ data[5] = 6; /* lines */ data[6] = 64; /* BPL */ -/* + cx231xx_api_cmd(dev, CX2341X_ENC_SET_VBI_CONFIG, 7, 0, data[0], data[1], data[2], data[3], data[4], data[5], data[6]); @@ -1175,7 +1180,7 @@ static int cx231xx_initialize_codec(struct cx231xx *dev) cx231xx_api_cmd(dev, CX2341X_ENC_SET_VBI_LINE, 5, 0, i | 0x80000000, valid, 0, 0, 0); } -*/ +#endif /* cx231xx_api_cmd(dev, CX2341X_ENC_MUTE_AUDIO, 1, 0, CX231xx_UNMUTE); msleep(60); */ @@ -1792,17 +1797,16 @@ static int vidioc_streamon(struct file *file, void *priv, struct cx231xx_fh *fh = file->private_data; struct cx231xx *dev = fh->dev; - int rc = 0; dprintk(3, "enter vidioc_streamon()\n"); cx231xx_set_alt_setting(dev, INDEX_TS1, 0); - rc = cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE); + cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE); if (dev->USE_ISO) - rc = cx231xx_init_isoc(dev, CX231XX_NUM_PACKETS, + cx231xx_init_isoc(dev, CX231XX_NUM_PACKETS, CX231XX_NUM_BUFS, dev->video_mode.max_pkt_size, cx231xx_isoc_copy); else { - rc = cx231xx_init_bulk(dev, 320, + cx231xx_init_bulk(dev, 320, 5, dev->ts1_mode.max_pkt_size, cx231xx_bulk_copy); diff --git a/drivers/media/video/cx231xx/cx231xx-audio.c b/drivers/media/video/cx231xx/cx231xx-audio.c index a2c2b7d343ec..068f78dc5d13 100644 --- a/drivers/media/video/cx231xx/cx231xx-audio.c +++ b/drivers/media/video/cx231xx/cx231xx-audio.c @@ -523,21 +523,24 @@ static int snd_cx231xx_pcm_close(struct snd_pcm_substream *substream) static int snd_cx231xx_hw_capture_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { - unsigned int channels, rate, format; int ret; dprintk("Setting capture parameters\n"); ret = snd_pcm_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params)); +#if 0 + /* TODO: set up cx231xx audio chip to deliver the correct audio format, + current default is 48000hz multiplexed => 96000hz mono + which shouldn't matter since analogue TV only supports mono */ + unsigned int channels, rate, format; + format = params_format(hw_params); rate = params_rate(hw_params); channels = params_channels(hw_params); +#endif - /* TODO: set up cx231xx audio chip to deliver the correct audio format, - current default is 48000hz multiplexed => 96000hz mono - which shouldn't matter since analogue TV only supports mono */ - return 0; + return ret; } static int snd_cx231xx_hw_capture_free(struct snd_pcm_substream *substream) @@ -586,7 +589,7 @@ static int snd_cx231xx_capture_trigger(struct snd_pcm_substream *substream, int cmd) { struct cx231xx *dev = snd_pcm_substream_chip(substream); - int retval; + int retval = 0; if (dev->state & DEV_DISCONNECTED) return -ENODEV; @@ -601,12 +604,13 @@ static int snd_cx231xx_capture_trigger(struct snd_pcm_substream *substream, break; default: retval = -EINVAL; + break; } spin_unlock(&dev->adev.slock); schedule_work(&dev->wq_trigger); - return 0; + return retval; } static snd_pcm_uframes_t snd_cx231xx_capture_pointer(struct snd_pcm_substream diff --git a/drivers/media/video/cx231xx/cx231xx-avcore.c b/drivers/media/video/cx231xx/cx231xx-avcore.c index 53ff26e7abf7..6e4bca251862 100644 --- a/drivers/media/video/cx231xx/cx231xx-avcore.c +++ b/drivers/media/video/cx231xx/cx231xx-avcore.c @@ -934,33 +934,29 @@ int cx231xx_set_decoder_video_input(struct cx231xx *dev, void cx231xx_enable656(struct cx231xx *dev) { u8 temp = 0; - int status; /*enable TS1 data[0:7] as output to export 656*/ - status = vid_blk_write_byte(dev, TS1_PIN_CTL0, 0xFF); + vid_blk_write_byte(dev, TS1_PIN_CTL0, 0xFF); /*enable TS1 clock as output to export 656*/ - status = vid_blk_read_byte(dev, TS1_PIN_CTL1, &temp); + vid_blk_read_byte(dev, TS1_PIN_CTL1, &temp); temp = temp|0x04; - status = vid_blk_write_byte(dev, TS1_PIN_CTL1, temp); - + vid_blk_write_byte(dev, TS1_PIN_CTL1, temp); } EXPORT_SYMBOL_GPL(cx231xx_enable656); void cx231xx_disable656(struct cx231xx *dev) { u8 temp = 0; - int status; - - status = vid_blk_write_byte(dev, TS1_PIN_CTL0, 0x00); + vid_blk_write_byte(dev, TS1_PIN_CTL0, 0x00); - status = vid_blk_read_byte(dev, TS1_PIN_CTL1, &temp); + vid_blk_read_byte(dev, TS1_PIN_CTL1, &temp); temp = temp&0xFB; - status = vid_blk_write_byte(dev, TS1_PIN_CTL1, temp); + vid_blk_write_byte(dev, TS1_PIN_CTL1, temp); } EXPORT_SYMBOL_GPL(cx231xx_disable656); @@ -1320,117 +1316,115 @@ void update_HH_register_after_set_DIF(struct cx231xx *dev) void cx231xx_dump_HH_reg(struct cx231xx *dev) { - u8 status = 0; u32 value = 0; u16 i = 0; value = 0x45005390; - status = vid_blk_write_word(dev, 0x104, value); + vid_blk_write_word(dev, 0x104, value); for (i = 0x100; i < 0x140; i++) { - status = vid_blk_read_word(dev, i, &value); + vid_blk_read_word(dev, i, &value); cx231xx_info("reg0x%x=0x%x\n", i, value); i = i+3; } for (i = 0x300; i < 0x400; i++) { - status = vid_blk_read_word(dev, i, &value); + vid_blk_read_word(dev, i, &value); cx231xx_info("reg0x%x=0x%x\n", i, value); i = i+3; } for (i = 0x400; i < 0x440; i++) { - status = vid_blk_read_word(dev, i, &value); + vid_blk_read_word(dev, i, &value); cx231xx_info("reg0x%x=0x%x\n", i, value); i = i+3; } - status = vid_blk_read_word(dev, AFE_CTRL_C2HH_SRC_CTRL, &value); + vid_blk_read_word(dev, AFE_CTRL_C2HH_SRC_CTRL, &value); cx231xx_info("AFE_CTRL_C2HH_SRC_CTRL=0x%x\n", value); vid_blk_write_word(dev, AFE_CTRL_C2HH_SRC_CTRL, 0x4485D390); - status = vid_blk_read_word(dev, AFE_CTRL_C2HH_SRC_CTRL, &value); + vid_blk_read_word(dev, AFE_CTRL_C2HH_SRC_CTRL, &value); cx231xx_info("AFE_CTRL_C2HH_SRC_CTRL=0x%x\n", value); } void cx231xx_dump_SC_reg(struct cx231xx *dev) { u8 value[4] = { 0, 0, 0, 0 }; - int status = 0; cx231xx_info("cx231xx_dump_SC_reg!\n"); - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, BOARD_CFG_STAT, + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, BOARD_CFG_STAT, value, 4); cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", BOARD_CFG_STAT, value[0], value[1], value[2], value[3]); - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS_MODE_REG, + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS_MODE_REG, value, 4); cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", TS_MODE_REG, value[0], value[1], value[2], value[3]); - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS1_CFG_REG, + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS1_CFG_REG, value, 4); cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", TS1_CFG_REG, value[0], value[1], value[2], value[3]); - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS1_LENGTH_REG, + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS1_LENGTH_REG, value, 4); cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", TS1_LENGTH_REG, value[0], value[1], value[2], value[3]); - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS2_CFG_REG, + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS2_CFG_REG, value, 4); cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", TS2_CFG_REG, value[0], value[1], value[2], value[3]); - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS2_LENGTH_REG, + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS2_LENGTH_REG, value, 4); cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", TS2_LENGTH_REG, value[0], value[1], value[2], value[3]); - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, EP_MODE_SET, + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, EP_MODE_SET, value, 4); cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", EP_MODE_SET, value[0], value[1], value[2], value[3]); - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_PTN1, + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_PTN1, value, 4); cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_PTN1, value[0], value[1], value[2], value[3]); - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_PTN2, + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_PTN2, value, 4); cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_PTN2, value[0], value[1], value[2], value[3]); - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_PTN3, + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_PTN3, value, 4); cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_PTN3, value[0], value[1], value[2], value[3]); - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_MASK0, + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_MASK0, value, 4); cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_MASK0, value[0], value[1], value[2], value[3]); - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_MASK1, + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_MASK1, value, 4); cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_MASK1, value[0], value[1], value[2], value[3]); - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_MASK2, + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_MASK2, value, 4); cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_MASK2, value[0], value[1], value[2], value[3]); - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_GAIN, + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_GAIN, value, 4); cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_GAIN, value[0], value[1], value[2], value[3]); - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_CAR_REG, + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_CAR_REG, value, 4); cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_CAR_REG, value[0], value[1], value[2], value[3]); - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_OT_CFG1, + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_OT_CFG1, value, 4); cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_OT_CFG1, value[0], value[1], value[2], value[3]); - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_OT_CFG2, + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_OT_CFG2, value, 4); cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_OT_CFG2, value[0], value[1], value[2], value[3]); - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, PWR_CTL_EN, + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, PWR_CTL_EN, value, 4); cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", PWR_CTL_EN, value[0], value[1], value[2], value[3]); @@ -1441,18 +1435,15 @@ void cx231xx_dump_SC_reg(struct cx231xx *dev) void cx231xx_Setup_AFE_for_LowIF(struct cx231xx *dev) { - u8 status = 0; u8 value = 0; - - - status = afe_read_byte(dev, ADC_STATUS2_CH3, &value); + afe_read_byte(dev, ADC_STATUS2_CH3, &value); value = (value & 0xFE)|0x01; - status = afe_write_byte(dev, ADC_STATUS2_CH3, value); + afe_write_byte(dev, ADC_STATUS2_CH3, value); - status = afe_read_byte(dev, ADC_STATUS2_CH3, &value); + afe_read_byte(dev, ADC_STATUS2_CH3, &value); value = (value & 0xFE)|0x00; - status = afe_write_byte(dev, ADC_STATUS2_CH3, value); + afe_write_byte(dev, ADC_STATUS2_CH3, value); /* @@ -1464,44 +1455,43 @@ void cx231xx_Setup_AFE_for_LowIF(struct cx231xx *dev) for low-if agc defect */ - status = afe_read_byte(dev, ADC_NTF_PRECLMP_EN_CH3, &value); + afe_read_byte(dev, ADC_NTF_PRECLMP_EN_CH3, &value); value = (value & 0xFC)|0x00; - status = afe_write_byte(dev, ADC_NTF_PRECLMP_EN_CH3, value); + afe_write_byte(dev, ADC_NTF_PRECLMP_EN_CH3, value); - status = afe_read_byte(dev, ADC_INPUT_CH3, &value); + afe_read_byte(dev, ADC_INPUT_CH3, &value); value = (value & 0xF9)|0x02; - status = afe_write_byte(dev, ADC_INPUT_CH3, value); + afe_write_byte(dev, ADC_INPUT_CH3, value); - status = afe_read_byte(dev, ADC_FB_FRCRST_CH3, &value); + afe_read_byte(dev, ADC_FB_FRCRST_CH3, &value); value = (value & 0xFB)|0x04; - status = afe_write_byte(dev, ADC_FB_FRCRST_CH3, value); + afe_write_byte(dev, ADC_FB_FRCRST_CH3, value); - status = afe_read_byte(dev, ADC_DCSERVO_DEM_CH3, &value); + afe_read_byte(dev, ADC_DCSERVO_DEM_CH3, &value); value = (value & 0xFC)|0x03; - status = afe_write_byte(dev, ADC_DCSERVO_DEM_CH3, value); + afe_write_byte(dev, ADC_DCSERVO_DEM_CH3, value); - status = afe_read_byte(dev, ADC_CTRL_DAC1_CH3, &value); + afe_read_byte(dev, ADC_CTRL_DAC1_CH3, &value); value = (value & 0xFB)|0x04; - status = afe_write_byte(dev, ADC_CTRL_DAC1_CH3, value); + afe_write_byte(dev, ADC_CTRL_DAC1_CH3, value); - status = afe_read_byte(dev, ADC_CTRL_DAC23_CH3, &value); + afe_read_byte(dev, ADC_CTRL_DAC23_CH3, &value); value = (value & 0xF8)|0x06; - status = afe_write_byte(dev, ADC_CTRL_DAC23_CH3, value); + afe_write_byte(dev, ADC_CTRL_DAC23_CH3, value); - status = afe_read_byte(dev, ADC_CTRL_DAC23_CH3, &value); + afe_read_byte(dev, ADC_CTRL_DAC23_CH3, &value); value = (value & 0x8F)|0x40; - status = afe_write_byte(dev, ADC_CTRL_DAC23_CH3, value); + afe_write_byte(dev, ADC_CTRL_DAC23_CH3, value); - status = afe_read_byte(dev, ADC_PWRDN_CLAMP_CH3, &value); + afe_read_byte(dev, ADC_PWRDN_CLAMP_CH3, &value); value = (value & 0xDF)|0x20; - status = afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3, value); + afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3, value); } void cx231xx_set_Colibri_For_LowIF(struct cx231xx *dev, u32 if_freq, u8 spectral_invert, u32 mode) { u32 colibri_carrier_offset = 0; - u8 status = 0; u32 func_mode = 0x01; /* Device has a DIF if this function is called */ u32 standard = 0; u8 value[4] = { 0, 0, 0, 0 }; @@ -1511,15 +1501,15 @@ void cx231xx_set_Colibri_For_LowIF(struct cx231xx *dev, u32 if_freq, value[1] = (u8) 0x6F; value[2] = (u8) 0x6F; value[3] = (u8) 0x6F; - status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, PWR_CTL_EN, value, 4); /*Set colibri for low IF*/ - status = cx231xx_afe_set_mode(dev, AFE_MODE_LOW_IF); + cx231xx_afe_set_mode(dev, AFE_MODE_LOW_IF); /* Set C2HH for low IF operation.*/ standard = dev->norm; - status = cx231xx_dif_configure_C2HH_for_low_IF(dev, dev->active_mode, + cx231xx_dif_configure_C2HH_for_low_IF(dev, dev->active_mode, func_mode, standard); /* Get colibri offsets.*/ @@ -1556,7 +1546,6 @@ void cx231xx_set_DIF_bandpass(struct cx231xx *dev, u32 if_freq, u8 spectral_invert, u32 mode) { unsigned long pll_freq_word; - int status = 0; u32 dif_misc_ctrl_value = 0; u64 pll_freq_u64 = 0; u32 i = 0; @@ -1567,7 +1556,7 @@ void cx231xx_set_DIF_bandpass(struct cx231xx *dev, u32 if_freq, if (mode == TUNER_MODE_FM_RADIO) { pll_freq_word = 0x905A1CAC; - status = vid_blk_write_word(dev, DIF_PLL_FREQ_WORD, pll_freq_word); + vid_blk_write_word(dev, DIF_PLL_FREQ_WORD, pll_freq_word); } else /*KSPROPERTY_TUNER_MODE_TV*/{ /* Calculate the PLL frequency word based on the adjusted if_freq*/ @@ -1576,23 +1565,23 @@ void cx231xx_set_DIF_bandpass(struct cx231xx *dev, u32 if_freq, do_div(pll_freq_u64, 50000000); pll_freq_word = (u32)pll_freq_u64; /*pll_freq_word = 0x3463497;*/ - status = vid_blk_write_word(dev, DIF_PLL_FREQ_WORD, pll_freq_word); + vid_blk_write_word(dev, DIF_PLL_FREQ_WORD, pll_freq_word); if (spectral_invert) { if_freq -= 400000; /* Enable Spectral Invert*/ - status = vid_blk_read_word(dev, DIF_MISC_CTRL, + vid_blk_read_word(dev, DIF_MISC_CTRL, &dif_misc_ctrl_value); dif_misc_ctrl_value = dif_misc_ctrl_value | 0x00200000; - status = vid_blk_write_word(dev, DIF_MISC_CTRL, + vid_blk_write_word(dev, DIF_MISC_CTRL, dif_misc_ctrl_value); } else { if_freq += 400000; /* Disable Spectral Invert*/ - status = vid_blk_read_word(dev, DIF_MISC_CTRL, + vid_blk_read_word(dev, DIF_MISC_CTRL, &dif_misc_ctrl_value); dif_misc_ctrl_value = dif_misc_ctrl_value & 0xFFDFFFFF; - status = vid_blk_write_word(dev, DIF_MISC_CTRL, + vid_blk_write_word(dev, DIF_MISC_CTRL, dif_misc_ctrl_value); } @@ -1609,7 +1598,7 @@ void cx231xx_set_DIF_bandpass(struct cx231xx *dev, u32 if_freq, sizeof(Dif_set_array)/sizeof(struct dif_settings)); for (i = 0; i < sizeof(Dif_set_array)/sizeof(struct dif_settings); i++) { if (Dif_set_array[i].if_freq == if_freq) { - status = vid_blk_write_word(dev, + vid_blk_write_word(dev, Dif_set_array[i].register_address, Dif_set_array[i].value); } } @@ -3090,31 +3079,30 @@ int cx231xx_gpio_i2c_read(struct cx231xx *dev, u8 dev_addr, u8 *buf, u8 len) */ int cx231xx_gpio_i2c_write(struct cx231xx *dev, u8 dev_addr, u8 *buf, u8 len) { - int status = 0; int i = 0; /* get the lock */ mutex_lock(&dev->gpio_i2c_lock); /* start */ - status = cx231xx_gpio_i2c_start(dev); + cx231xx_gpio_i2c_start(dev); /* write dev_addr */ - status = cx231xx_gpio_i2c_write_byte(dev, dev_addr << 1); + cx231xx_gpio_i2c_write_byte(dev, dev_addr << 1); /* read Ack */ - status = cx231xx_gpio_i2c_read_ack(dev); + cx231xx_gpio_i2c_read_ack(dev); for (i = 0; i < len; i++) { /* Write data */ - status = cx231xx_gpio_i2c_write_byte(dev, buf[i]); + cx231xx_gpio_i2c_write_byte(dev, buf[i]); /* read Ack */ - status = cx231xx_gpio_i2c_read_ack(dev); + cx231xx_gpio_i2c_read_ack(dev); } /* write End */ - status = cx231xx_gpio_i2c_end(dev); + cx231xx_gpio_i2c_end(dev); /* release the lock */ mutex_unlock(&dev->gpio_i2c_lock); diff --git a/drivers/media/video/cx231xx/cx231xx-core.c b/drivers/media/video/cx231xx/cx231xx-core.c index 08dd930f882a..05358d486135 100644 --- a/drivers/media/video/cx231xx/cx231xx-core.c +++ b/drivers/media/video/cx231xx/cx231xx-core.c @@ -754,7 +754,7 @@ int cx231xx_set_mode(struct cx231xx *dev, enum cx231xx_mode set_mode) } } - return 0; + return errCode ? -EINVAL : 0; } EXPORT_SYMBOL_GPL(cx231xx_set_mode); @@ -764,7 +764,7 @@ int cx231xx_ep5_bulkout(struct cx231xx *dev, u8 *firmware, u16 size) int actlen, ret = -ENOMEM; u32 *buffer; -buffer = kzalloc(4096, GFP_KERNEL); + buffer = kzalloc(4096, GFP_KERNEL); if (buffer == NULL) { cx231xx_info("out of mem\n"); return -ENOMEM; @@ -772,16 +772,16 @@ buffer = kzalloc(4096, GFP_KERNEL); memcpy(&buffer[0], firmware, 4096); ret = usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, 5), - buffer, 4096, &actlen, 2000); + buffer, 4096, &actlen, 2000); if (ret) cx231xx_info("bulk message failed: %d (%d/%d)", ret, - size, actlen); + size, actlen); else { errCode = actlen != size ? -1 : 0; } -kfree(buffer); - return 0; + kfree(buffer); + return errCode; } /***************************************************************** @@ -797,7 +797,7 @@ static void cx231xx_isoc_irq_callback(struct urb *urb) struct cx231xx_video_mode *vmode = container_of(dma_q, struct cx231xx_video_mode, vidq); struct cx231xx *dev = container_of(vmode, struct cx231xx, video_mode); - int rc, i; + int i; switch (urb->status) { case 0: /* success */ @@ -814,7 +814,7 @@ static void cx231xx_isoc_irq_callback(struct urb *urb) /* Copy data from URB */ spin_lock(&dev->video_mode.slock); - rc = dev->video_mode.isoc_ctl.isoc_copy(dev, urb); + dev->video_mode.isoc_ctl.isoc_copy(dev, urb); spin_unlock(&dev->video_mode.slock); /* Reset urb buffers */ @@ -822,7 +822,6 @@ static void cx231xx_isoc_irq_callback(struct urb *urb) urb->iso_frame_desc[i].status = 0; urb->iso_frame_desc[i].actual_length = 0; } - urb->status = 0; urb->status = usb_submit_urb(urb, GFP_ATOMIC); if (urb->status) { @@ -843,7 +842,6 @@ static void cx231xx_bulk_irq_callback(struct urb *urb) struct cx231xx_video_mode *vmode = container_of(dma_q, struct cx231xx_video_mode, vidq); struct cx231xx *dev = container_of(vmode, struct cx231xx, video_mode); - int rc; switch (urb->status) { case 0: /* success */ @@ -860,12 +858,10 @@ static void cx231xx_bulk_irq_callback(struct urb *urb) /* Copy data from URB */ spin_lock(&dev->video_mode.slock); - rc = dev->video_mode.bulk_ctl.bulk_copy(dev, urb); + dev->video_mode.bulk_ctl.bulk_copy(dev, urb); spin_unlock(&dev->video_mode.slock); /* Reset urb buffers */ - urb->status = 0; - urb->status = usb_submit_urb(urb, GFP_ATOMIC); if (urb->status) { cx231xx_isocdbg("urb resubmit failed (error=%i)\n", @@ -1231,42 +1227,40 @@ int cx231xx_init_bulk(struct cx231xx *dev, int max_packets, EXPORT_SYMBOL_GPL(cx231xx_init_bulk); void cx231xx_stop_TS1(struct cx231xx *dev) { - int status = 0; u8 val[4] = { 0, 0, 0, 0 }; - val[0] = 0x00; - val[1] = 0x03; - val[2] = 0x00; - val[3] = 0x00; - status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, - TS_MODE_REG, val, 4); - - val[0] = 0x00; - val[1] = 0x70; - val[2] = 0x04; - val[3] = 0x00; - status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, - TS1_CFG_REG, val, 4); + val[0] = 0x00; + val[1] = 0x03; + val[2] = 0x00; + val[3] = 0x00; + cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + TS_MODE_REG, val, 4); + + val[0] = 0x00; + val[1] = 0x70; + val[2] = 0x04; + val[3] = 0x00; + cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + TS1_CFG_REG, val, 4); } /* EXPORT_SYMBOL_GPL(cx231xx_stop_TS1); */ void cx231xx_start_TS1(struct cx231xx *dev) { - int status = 0; u8 val[4] = { 0, 0, 0, 0 }; - val[0] = 0x03; - val[1] = 0x03; - val[2] = 0x00; - val[3] = 0x00; - status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, - TS_MODE_REG, val, 4); - - val[0] = 0x04; - val[1] = 0xA3; - val[2] = 0x3B; - val[3] = 0x00; - status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, - TS1_CFG_REG, val, 4); + val[0] = 0x03; + val[1] = 0x03; + val[2] = 0x00; + val[3] = 0x00; + cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + TS_MODE_REG, val, 4); + + val[0] = 0x04; + val[1] = 0xA3; + val[2] = 0x3B; + val[3] = 0x00; + cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + TS1_CFG_REG, val, 4); } /* EXPORT_SYMBOL_GPL(cx231xx_start_TS1); */ /***************************************************************** diff --git a/drivers/media/video/cx231xx/cx231xx-vbi.c b/drivers/media/video/cx231xx/cx231xx-vbi.c index 8cdee5f78f13..3d15314e1f88 100644 --- a/drivers/media/video/cx231xx/cx231xx-vbi.c +++ b/drivers/media/video/cx231xx/cx231xx-vbi.c @@ -83,7 +83,6 @@ static inline void print_err_status(struct cx231xx *dev, int packet, int status) */ static inline int cx231xx_isoc_vbi_copy(struct cx231xx *dev, struct urb *urb) { - struct cx231xx_buffer *buf; struct cx231xx_dmaqueue *dma_q = urb->context; int rc = 1; unsigned char *p_buffer; @@ -102,8 +101,6 @@ static inline int cx231xx_isoc_vbi_copy(struct cx231xx *dev, struct urb *urb) return 0; } - buf = dev->vbi_mode.bulk_ctl.buf; - /* get buffer pointer and length */ p_buffer = urb->transfer_buffer; buffer_size = urb->actual_length; @@ -310,7 +307,6 @@ static void cx231xx_irq_vbi_callback(struct urb *urb) struct cx231xx_video_mode *vmode = container_of(dma_q, struct cx231xx_video_mode, vidq); struct cx231xx *dev = container_of(vmode, struct cx231xx, vbi_mode); - int rc; switch (urb->status) { case 0: /* success */ @@ -328,7 +324,7 @@ static void cx231xx_irq_vbi_callback(struct urb *urb) /* Copy data from URB */ spin_lock(&dev->vbi_mode.slock); - rc = dev->vbi_mode.bulk_ctl.bulk_copy(dev, urb); + dev->vbi_mode.bulk_ctl.bulk_copy(dev, urb); spin_unlock(&dev->vbi_mode.slock); /* Reset status */ diff --git a/drivers/media/video/cx231xx/cx231xx-video.c b/drivers/media/video/cx231xx/cx231xx-video.c index 7f916f0685e9..523aa49d6b86 100644 --- a/drivers/media/video/cx231xx/cx231xx-video.c +++ b/drivers/media/video/cx231xx/cx231xx-video.c @@ -326,9 +326,7 @@ static inline void get_next_buf(struct cx231xx_dmaqueue *dma_q, */ static inline int cx231xx_isoc_copy(struct cx231xx *dev, struct urb *urb) { - struct cx231xx_buffer *buf; struct cx231xx_dmaqueue *dma_q = urb->context; - unsigned char *outp = NULL; int i, rc = 1; unsigned char *p_buffer; u32 bytes_parsed = 0, buffer_size = 0; @@ -346,10 +344,6 @@ static inline int cx231xx_isoc_copy(struct cx231xx *dev, struct urb *urb) return 0; } - buf = dev->video_mode.isoc_ctl.buf; - if (buf != NULL) - outp = videobuf_to_vmalloc(&buf->vb); - for (i = 0; i < urb->number_of_packets; i++) { int status = urb->iso_frame_desc[i].status; @@ -429,9 +423,7 @@ static inline int cx231xx_isoc_copy(struct cx231xx *dev, struct urb *urb) static inline int cx231xx_bulk_copy(struct cx231xx *dev, struct urb *urb) { - struct cx231xx_buffer *buf; struct cx231xx_dmaqueue *dma_q = urb->context; - unsigned char *outp = NULL; int rc = 1; unsigned char *p_buffer; u32 bytes_parsed = 0, buffer_size = 0; @@ -449,10 +441,6 @@ static inline int cx231xx_bulk_copy(struct cx231xx *dev, struct urb *urb) return 0; } - buf = dev->video_mode.bulk_ctl.buf; - if (buf != NULL) - outp = videobuf_to_vmalloc(&buf->vb); - if (1) { /* get buffer pointer and length */ @@ -701,13 +689,9 @@ void cx231xx_reset_video_buffer(struct cx231xx *dev, buf = dev->video_mode.bulk_ctl.buf; if (buf == NULL) { - u8 *outp = NULL; /* first try to get the buffer */ get_next_buf(dma_q, &buf); - if (buf) - outp = videobuf_to_vmalloc(&buf->vb); - dma_q->pos = 0; dma_q->field1_done = 0; dma_q->current_field = -1; @@ -2561,6 +2545,10 @@ static struct video_device *cx231xx_vdev_init(struct cx231xx *dev, vfd->release = video_device_release; vfd->debug = video_debug; vfd->lock = &dev->lock; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags); snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name); diff --git a/drivers/media/video/cx23885/cx23888-ir.c b/drivers/media/video/cx23885/cx23888-ir.c index bb1ce346425d..c2bc39c58f82 100644 --- a/drivers/media/video/cx23885/cx23888-ir.c +++ b/drivers/media/video/cx23885/cx23888-ir.c @@ -331,9 +331,7 @@ static u64 ns_to_pulse_clocks(u32 ns) static u16 pulse_clocks_to_clock_divider(u64 count) { - u32 rem; - - rem = do_div(count, (FIFO_RXTX << 2) | 0x3); + do_div(count, (FIFO_RXTX << 2) | 0x3); /* net result needs to be rounded down and decremented by 1 */ if (count > RXCLK_RCD + 1) diff --git a/drivers/media/video/cx25821/cx25821-alsa.c b/drivers/media/video/cx25821/cx25821-alsa.c index 03cfac476b03..1858a45dd081 100644 --- a/drivers/media/video/cx25821/cx25821-alsa.c +++ b/drivers/media/video/cx25821/cx25821-alsa.c @@ -290,11 +290,9 @@ static irqreturn_t cx25821_irq(int irq, void *dev_id) u32 status, pci_status; u32 audint_status, audint_mask; int loop, handled = 0; - int audint_count = 0; audint_status = cx_read(AUD_A_INT_STAT); audint_mask = cx_read(AUD_A_INT_MSK); - audint_count = cx_read(AUD_A_GPCNT); status = cx_read(PCI_INT_STAT); for (loop = 0; loop < 1; loop++) { diff --git a/drivers/media/video/cx25821/cx25821-audio-upstream.c b/drivers/media/video/cx25821/cx25821-audio-upstream.c index 20c7ca3351a8..8b2a99975c23 100644 --- a/drivers/media/video/cx25821/cx25821-audio-upstream.c +++ b/drivers/media/video/cx25821/cx25821-audio-upstream.c @@ -585,7 +585,7 @@ int cx25821_audio_upstream_irq(struct cx25821_dev *dev, int chan_num, static irqreturn_t cx25821_upstream_irq_audio(int irq, void *dev_id) { struct cx25821_dev *dev = dev_id; - u32 msk_stat, audio_status; + u32 audio_status; int handled = 0; struct sram_channel *sram_ch; @@ -594,7 +594,6 @@ static irqreturn_t cx25821_upstream_irq_audio(int irq, void *dev_id) sram_ch = dev->channels[dev->_audio_upstream_channel].sram_channels; - msk_stat = cx_read(sram_ch->int_mstat); audio_status = cx_read(sram_ch->int_stat); /* Only deal with our interrupt */ diff --git a/drivers/media/video/cx25821/cx25821-core.c b/drivers/media/video/cx25821/cx25821-core.c index 7930ca58349f..83c1aa6b2e6c 100644 --- a/drivers/media/video/cx25821/cx25821-core.c +++ b/drivers/media/video/cx25821/cx25821-core.c @@ -379,14 +379,6 @@ static inline int i2c_slave_did_ack(struct i2c_adapter *i2c_adap) return cx_read(bus->reg_stat) & 0x01; } -void cx_i2c_read_print(struct cx25821_dev *dev, u32 reg, const char *reg_string) -{ - int tmp = 0; - u32 value = 0; - - value = cx25821_i2c_read(&dev->i2c_bus[0], reg, &tmp); -} - static void cx25821_registers_init(struct cx25821_dev *dev) { u32 tmp; @@ -895,7 +887,7 @@ static void cx25821_iounmap(struct cx25821_dev *dev) static int cx25821_dev_setup(struct cx25821_dev *dev) { - int io_size = 0, i; + int i; pr_info("\n***********************************\n"); pr_info("cx25821 set up\n"); @@ -960,7 +952,6 @@ static int cx25821_dev_setup(struct cx25821_dev *dev) /* PCIe stuff */ dev->base_io_addr = pci_resource_start(dev->pci, 0); - io_size = pci_resource_len(dev->pci, 0); if (!dev->base_io_addr) { CX25821_ERR("No PCI Memory resources, exiting!\n"); @@ -1317,13 +1308,12 @@ void cx25821_free_buffer(struct videobuf_queue *q, struct cx25821_buffer *buf) static irqreturn_t cx25821_irq(int irq, void *dev_id) { struct cx25821_dev *dev = dev_id; - u32 pci_status, pci_mask; + u32 pci_status; u32 vid_status; int i, handled = 0; u32 mask[8] = { 1, 2, 4, 8, 16, 32, 64, 128 }; pci_status = cx_read(PCI_INT_STAT); - pci_mask = cx_read(PCI_INT_MSK); if (pci_status == 0) goto out; diff --git a/drivers/media/video/cx25821/cx25821-i2c.c b/drivers/media/video/cx25821/cx25821-i2c.c index 12d7300fa1e9..6311180f430c 100644 --- a/drivers/media/video/cx25821/cx25821-i2c.c +++ b/drivers/media/video/cx25821/cx25821-i2c.c @@ -361,7 +361,6 @@ void cx25821_av_clk(struct cx25821_dev *dev, int enable) int cx25821_i2c_read(struct cx25821_i2c *bus, u16 reg_addr, int *value) { struct i2c_client *client = &bus->i2c_client; - int retval = 0; int v = 0; u8 addr[2] = { 0, 0 }; u8 buf[4] = { 0, 0, 0, 0 }; @@ -385,7 +384,7 @@ int cx25821_i2c_read(struct cx25821_i2c *bus, u16 reg_addr, int *value) msgs[0].addr = 0x44; msgs[1].addr = 0x44; - retval = i2c_xfer(client->adapter, msgs, 2); + i2c_xfer(client->adapter, msgs, 2); v = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; *value = v; diff --git a/drivers/media/video/cx25821/cx25821-medusa-video.c b/drivers/media/video/cx25821/cx25821-medusa-video.c index 298a68d98c2f..313fb20a0b47 100644 --- a/drivers/media/video/cx25821/cx25821-medusa-video.c +++ b/drivers/media/video/cx25821/cx25821-medusa-video.c @@ -35,7 +35,6 @@ static void medusa_enable_bluefield_output(struct cx25821_dev *dev, int channel, int enable) { - int ret_val = 1; u32 value = 0; u32 tmp = 0; int out_ctrl = OUT_CTRL1; @@ -79,13 +78,13 @@ static void medusa_enable_bluefield_output(struct cx25821_dev *dev, int channel, value &= 0xFFFFFF7F; /* clear BLUE_FIELD_EN */ if (enable) value |= 0x00000080; /* set BLUE_FIELD_EN */ - ret_val = cx25821_i2c_write(&dev->i2c_bus[0], out_ctrl, value); + cx25821_i2c_write(&dev->i2c_bus[0], out_ctrl, value); value = cx25821_i2c_read(&dev->i2c_bus[0], out_ctrl_ns, &tmp); value &= 0xFFFFFF7F; if (enable) value |= 0x00000080; /* set BLUE_FIELD_EN */ - ret_val = cx25821_i2c_write(&dev->i2c_bus[0], out_ctrl_ns, value); + cx25821_i2c_write(&dev->i2c_bus[0], out_ctrl_ns, value); } static int medusa_initialize_ntsc(struct cx25821_dev *dev) @@ -431,7 +430,6 @@ void medusa_set_resolution(struct cx25821_dev *dev, int width, { int decoder = 0; int decoder_count = 0; - int ret_val = 0; u32 hscale = 0x0; u32 vscale = 0x0; const int MAX_WIDTH = 720; @@ -482,9 +480,9 @@ void medusa_set_resolution(struct cx25821_dev *dev, int width, for (; decoder < decoder_count; decoder++) { /* write scaling values for each decoder */ - ret_val = cx25821_i2c_write(&dev->i2c_bus[0], + cx25821_i2c_write(&dev->i2c_bus[0], HSCALE_CTRL + (0x200 * decoder), hscale); - ret_val = cx25821_i2c_write(&dev->i2c_bus[0], + cx25821_i2c_write(&dev->i2c_bus[0], VSCALE_CTRL + (0x200 * decoder), vscale); } @@ -494,7 +492,6 @@ void medusa_set_resolution(struct cx25821_dev *dev, int width, static void medusa_set_decoderduration(struct cx25821_dev *dev, int decoder, int duration) { - int ret_val = 0; u32 fld_cnt = 0; u32 tmp = 0; u32 disp_cnt_reg = DISP_AB_CNT; @@ -537,7 +534,7 @@ static void medusa_set_decoderduration(struct cx25821_dev *dev, int decoder, fld_cnt |= ((u32) duration) << 16; } - ret_val = cx25821_i2c_write(&dev->i2c_bus[0], disp_cnt_reg, fld_cnt); + cx25821_i2c_write(&dev->i2c_bus[0], disp_cnt_reg, fld_cnt); mutex_unlock(&dev->lock); } diff --git a/drivers/media/video/cx25821/cx25821-video-upstream-ch2.c b/drivers/media/video/cx25821/cx25821-video-upstream-ch2.c index 5a157cf4a95e..c8c94fbf5d8d 100644 --- a/drivers/media/video/cx25821/cx25821-video-upstream-ch2.c +++ b/drivers/media/video/cx25821/cx25821-video-upstream-ch2.c @@ -587,7 +587,7 @@ int cx25821_video_upstream_irq_ch2(struct cx25821_dev *dev, int chan_num, static irqreturn_t cx25821_upstream_irq_ch2(int irq, void *dev_id) { struct cx25821_dev *dev = dev_id; - u32 msk_stat, vid_status; + u32 vid_status; int handled = 0; int channel_num = 0; struct sram_channel *sram_ch; @@ -598,7 +598,6 @@ static irqreturn_t cx25821_upstream_irq_ch2(int irq, void *dev_id) channel_num = VID_UPSTREAM_SRAM_CHANNEL_J; sram_ch = dev->channels[channel_num].sram_channels; - msk_stat = cx_read(sram_ch->int_mstat); vid_status = cx_read(sram_ch->int_stat); /* Only deal with our interrupt */ diff --git a/drivers/media/video/cx25821/cx25821-video-upstream.c b/drivers/media/video/cx25821/cx25821-video-upstream.c index 21e7d657f049..52c13e0b6492 100644 --- a/drivers/media/video/cx25821/cx25821-video-upstream.c +++ b/drivers/media/video/cx25821/cx25821-video-upstream.c @@ -637,7 +637,7 @@ int cx25821_video_upstream_irq(struct cx25821_dev *dev, int chan_num, static irqreturn_t cx25821_upstream_irq(int irq, void *dev_id) { struct cx25821_dev *dev = dev_id; - u32 msk_stat, vid_status; + u32 vid_status; int handled = 0; int channel_num = 0; struct sram_channel *sram_ch; @@ -649,7 +649,6 @@ static irqreturn_t cx25821_upstream_irq(int irq, void *dev_id) sram_ch = dev->channels[channel_num].sram_channels; - msk_stat = cx_read(sram_ch->int_mstat); vid_status = cx_read(sram_ch->int_stat); /* Only deal with our interrupt */ diff --git a/drivers/media/video/cx25821/cx25821-video.c b/drivers/media/video/cx25821/cx25821-video.c index ffd8bc79c02e..b38d4379cc36 100644 --- a/drivers/media/video/cx25821/cx25821-video.c +++ b/drivers/media/video/cx25821/cx25821-video.c @@ -109,25 +109,6 @@ struct cx25821_fmt *cx25821_format_by_fourcc(unsigned int fourcc) return NULL; } -void cx25821_dump_video_queue(struct cx25821_dev *dev, - struct cx25821_dmaqueue *q) -{ - struct cx25821_buffer *buf; - struct list_head *item; - dprintk(1, "%s()\n", __func__); - - if (!list_empty(&q->active)) { - list_for_each(item, &q->active) - buf = list_entry(item, struct cx25821_buffer, vb.queue); - } - - if (!list_empty(&q->queued)) { - list_for_each(item, &q->queued) - buf = list_entry(item, struct cx25821_buffer, vb.queue); - } - -} - void cx25821_video_wakeup(struct cx25821_dev *dev, struct cx25821_dmaqueue *q, u32 count) { @@ -557,7 +538,7 @@ int cx25821_buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, struct cx25821_buffer *buf = container_of(vb, struct cx25821_buffer, vb); int rc, init_buffer = 0; - u32 line0_offset, line1_offset; + u32 line0_offset; struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb); int bpl_local = LINE_SIZE_D1; int channel_opened = fh->channel_id; @@ -639,7 +620,6 @@ int cx25821_buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, case V4L2_FIELD_INTERLACED: /* All other formats are top field first */ line0_offset = 0; - line1_offset = buf->bpl; dprintk(1, "top field first\n"); cx25821_risc_buffer(dev->pci, &buf->risc, @@ -1830,7 +1810,6 @@ static long video_ioctl_set(struct file *file, unsigned int cmd, int i = 0; int cif_enable = 0; int cif_width = 0; - u32 value = 0; data_from_user = (struct downstream_user_struct *)arg; @@ -1914,7 +1893,7 @@ static long video_ioctl_set(struct file *file, unsigned int cmd, cx_write(data_from_user->reg_address, data_from_user->reg_data); break; case MEDUSA_READ: - value = cx25821_i2c_read(&dev->i2c_bus[0], + cx25821_i2c_read(&dev->i2c_bus[0], (u16) data_from_user->reg_address, &data_from_user->reg_data); break; diff --git a/drivers/media/video/cx25821/cx25821-video.h b/drivers/media/video/cx25821/cx25821-video.h index d0d9538ca5b3..9652a5e35ba2 100644 --- a/drivers/media/video/cx25821/cx25821-video.h +++ b/drivers/media/video/cx25821/cx25821-video.h @@ -86,8 +86,6 @@ extern struct cx25821_fmt formats[]; extern struct cx25821_fmt *cx25821_format_by_fourcc(unsigned int fourcc); extern struct cx25821_data timeout_data[MAX_VID_CHANNEL_NUM]; -extern void cx25821_dump_video_queue(struct cx25821_dev *dev, - struct cx25821_dmaqueue *q); extern void cx25821_video_wakeup(struct cx25821_dev *dev, struct cx25821_dmaqueue *q, u32 count); diff --git a/drivers/media/video/cx25840/cx25840-ir.c b/drivers/media/video/cx25840/cx25840-ir.c index 13c380ebb562..38ce76ed1924 100644 --- a/drivers/media/video/cx25840/cx25840-ir.c +++ b/drivers/media/video/cx25840/cx25840-ir.c @@ -316,9 +316,7 @@ static u64 ns_to_pulse_clocks(u32 ns) static u16 pulse_clocks_to_clock_divider(u64 count) { - u32 rem; - - rem = do_div(count, (FIFO_RXTX << 2) | 0x3); + do_div(count, (FIFO_RXTX << 2) | 0x3); /* net result needs to be rounded down and decremented by 1 */ if (count > RXCLK_RCD + 1) @@ -860,12 +858,10 @@ static int cx25840_ir_tx_write(struct v4l2_subdev *sd, u8 *buf, size_t count, ssize_t *num) { struct cx25840_ir_state *ir_state = to_ir_state(sd); - struct i2c_client *c; if (ir_state == NULL) return -ENODEV; - c = ir_state->c; #if 0 /* * FIXME - the code below is an incomplete and untested sketch of what diff --git a/drivers/media/video/davinci/Kconfig b/drivers/media/video/davinci/Kconfig index 60a456ebdc7c..9337b5605c90 100644 --- a/drivers/media/video/davinci/Kconfig +++ b/drivers/media/video/davinci/Kconfig @@ -40,6 +40,7 @@ config VIDEO_VPSS_SYSTEM config VIDEO_VPFE_CAPTURE tristate "VPFE Video Capture Driver" depends on VIDEO_V4L2 && (ARCH_DAVINCI || ARCH_OMAP3) + depends on I2C select VIDEOBUF_DMA_CONTIG help Support for DMx/AMx VPFE based frame grabber. This is the diff --git a/drivers/media/video/davinci/vpbe_display.c b/drivers/media/video/davinci/vpbe_display.c index 1f3b1c729252..e106b72810a9 100644 --- a/drivers/media/video/davinci/vpbe_display.c +++ b/drivers/media/video/davinci/vpbe_display.c @@ -1618,6 +1618,10 @@ static __devinit int init_vpbe_layer(int i, struct vpbe_display *disp_dev, vbd->ioctl_ops = &vpbe_ioctl_ops; vbd->minor = -1; vbd->v4l2_dev = &disp_dev->vpbe_dev->v4l2_dev; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &vbd->flags); vbd->lock = &vpbe_display_layer->opslock; if (disp_dev->vpbe_dev->current_timings.timings_type & diff --git a/drivers/media/video/davinci/vpfe_capture.c b/drivers/media/video/davinci/vpfe_capture.c index 20cf271a774b..49a845fb804a 100644 --- a/drivers/media/video/davinci/vpfe_capture.c +++ b/drivers/media/video/davinci/vpfe_capture.c @@ -1761,7 +1761,7 @@ static long vpfe_param_handler(struct file *file, void *priv, } break; default: - ret = -EINVAL; + ret = -ENOTTY; } unlock_out: mutex_unlock(&vpfe_dev->lock); diff --git a/drivers/media/video/davinci/vpif_capture.c b/drivers/media/video/davinci/vpif_capture.c index 6504e40a31dd..96046957bf21 100644 --- a/drivers/media/video/davinci/vpif_capture.c +++ b/drivers/media/video/davinci/vpif_capture.c @@ -2228,6 +2228,10 @@ static __init int vpif_probe(struct platform_device *pdev) common = &(ch->common[VPIF_VIDEO_INDEX]); spin_lock_init(&common->irqlock); mutex_init(&common->lock); + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &ch->video_dev->flags); ch->video_dev->lock = &common->lock; /* Initialize prio member of channel object */ v4l2_prio_init(&ch->prio); diff --git a/drivers/media/video/davinci/vpif_display.c b/drivers/media/video/davinci/vpif_display.c index 7fa34b4fae26..e6488ee7db18 100644 --- a/drivers/media/video/davinci/vpif_display.c +++ b/drivers/media/video/davinci/vpif_display.c @@ -1778,6 +1778,10 @@ static __init int vpif_probe(struct platform_device *pdev) v4l2_prio_init(&ch->prio); ch->common[VPIF_VIDEO_INDEX].fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &ch->video_dev->flags); ch->video_dev->lock = &common->lock; /* register video device */ diff --git a/drivers/media/video/em28xx/Kconfig b/drivers/media/video/em28xx/Kconfig index f6f622e123bd..928ef0d0429f 100644 --- a/drivers/media/video/em28xx/Kconfig +++ b/drivers/media/video/em28xx/Kconfig @@ -49,10 +49,10 @@ config VIDEO_EM28XX_DVB Empiatech em28xx chips. config VIDEO_EM28XX_RC - bool "EM28XX Remote Controller support" + tristate "EM28XX Remote Controller support" depends on RC_CORE depends on VIDEO_EM28XX depends on !(RC_CORE=m && VIDEO_EM28XX=y) - default y + default VIDEO_EM28XX ---help--- Enables Remote Controller support on em28xx driver. diff --git a/drivers/media/video/em28xx/Makefile b/drivers/media/video/em28xx/Makefile index 2abdf76c5203..c8b338d4be05 100644 --- a/drivers/media/video/em28xx/Makefile +++ b/drivers/media/video/em28xx/Makefile @@ -1,16 +1,15 @@ em28xx-y := em28xx-video.o em28xx-i2c.o em28xx-cards.o em28xx-y += em28xx-core.o em28xx-vbi.o -em28xx-$(CONFIG_VIDEO_EM28XX_RC) += em28xx-input.o - em28xx-alsa-objs := em28xx-audio.o +em28xx-rc-objs := em28xx-input.o obj-$(CONFIG_VIDEO_EM28XX) += em28xx.o obj-$(CONFIG_VIDEO_EM28XX_ALSA) += em28xx-alsa.o obj-$(CONFIG_VIDEO_EM28XX_DVB) += em28xx-dvb.o +obj-$(CONFIG_VIDEO_EM28XX_RC) += em28xx-rc.o ccflags-y += -Idrivers/media/video ccflags-y += -Idrivers/media/common/tuners ccflags-y += -Idrivers/media/dvb/dvb-core ccflags-y += -Idrivers/media/dvb/frontends - diff --git a/drivers/media/video/em28xx/em28xx-audio.c b/drivers/media/video/em28xx/em28xx-audio.c index e2a7b77c39c7..d7e2a3dc5525 100644 --- a/drivers/media/video/em28xx/em28xx-audio.c +++ b/drivers/media/video/em28xx/em28xx-audio.c @@ -343,7 +343,6 @@ static int snd_em28xx_pcm_close(struct snd_pcm_substream *substream) static int snd_em28xx_hw_capture_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { - unsigned int channels, rate, format; int ret; dprintk("Setting capture parameters\n"); @@ -352,13 +351,17 @@ static int snd_em28xx_hw_capture_params(struct snd_pcm_substream *substream, params_buffer_bytes(hw_params)); if (ret < 0) return ret; +#if 0 + /* TODO: set up em28xx audio chip to deliver the correct audio format, + current default is 48000hz multiplexed => 96000hz mono + which shouldn't matter since analogue TV only supports mono */ + unsigned int channels, rate, format; + format = params_format(hw_params); rate = params_rate(hw_params); channels = params_channels(hw_params); +#endif - /* TODO: set up em28xx audio chip to deliver the correct audio format, - current default is 48000hz multiplexed => 96000hz mono - which shouldn't matter since analogue TV only supports mono */ return 0; } diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index 9fd8cc7dbb23..20a7e24de6fb 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -69,6 +69,8 @@ struct em28xx_hash_table { unsigned int tuner; }; +static void em28xx_pre_card_setup(struct em28xx *dev); + /* * Reset sequences for analog/digital modes */ @@ -2361,7 +2363,7 @@ static int em28xx_hint_sensor(struct em28xx *dev) /* Since em28xx_pre_card_setup() requires a proper dev->model, * this won't work for boards with generic PCI IDs */ -void em28xx_pre_card_setup(struct em28xx *dev) +static void em28xx_pre_card_setup(struct em28xx *dev) { /* Set the initial XCLK and I2C clock values based on the board definition */ @@ -2661,55 +2663,7 @@ static int em28xx_hint_board(struct em28xx *dev) return -1; } -/* ----------------------------------------------------------------------- */ -void em28xx_register_i2c_ir(struct em28xx *dev) -{ - /* Leadtek winfast tv USBII deluxe can find a non working IR-device */ - /* at address 0x18, so if that address is needed for another board in */ - /* the future, please put it after 0x1f. */ - struct i2c_board_info info; - const unsigned short addr_list[] = { - 0x1f, 0x30, 0x47, I2C_CLIENT_END - }; - - if (disable_ir) - return; - - memset(&info, 0, sizeof(struct i2c_board_info)); - memset(&dev->init_data, 0, sizeof(dev->init_data)); - strlcpy(info.type, "ir_video", I2C_NAME_SIZE); - - /* detect & configure */ - switch (dev->model) { - case EM2800_BOARD_TERRATEC_CINERGY_200: - case EM2820_BOARD_TERRATEC_CINERGY_250: - dev->init_data.ir_codes = RC_MAP_EM_TERRATEC; - dev->init_data.get_key = em28xx_get_key_terratec; - dev->init_data.name = "i2c IR (EM28XX Terratec)"; - break; - case EM2820_BOARD_PINNACLE_USB_2: - dev->init_data.ir_codes = RC_MAP_PINNACLE_GREY; - dev->init_data.get_key = em28xx_get_key_pinnacle_usb_grey; - dev->init_data.name = "i2c IR (EM28XX Pinnacle PCTV)"; - break; - case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2: - dev->init_data.ir_codes = RC_MAP_HAUPPAUGE; - dev->init_data.get_key = em28xx_get_key_em_haup; - dev->init_data.name = "i2c IR (EM2840 Hauppauge)"; - break; - case EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE: - dev->init_data.ir_codes = RC_MAP_WINFAST_USBII_DELUXE; - dev->init_data.get_key = em28xx_get_key_winfast_usbii_deluxe; - dev->init_data.name = "i2c IR (EM2820 Winfast TV USBII Deluxe)"; - break; - } - - if (dev->init_data.name) - info.platform_data = &dev->init_data; - i2c_new_probed_device(&dev->i2c_adap, &info, addr_list, NULL); -} - -void em28xx_card_setup(struct em28xx *dev) +static void em28xx_card_setup(struct em28xx *dev) { /* * If the device can be a webcam, seek for a sensor. @@ -2849,13 +2803,6 @@ void em28xx_card_setup(struct em28xx *dev) break; } -#if defined(CONFIG_MODULES) && defined(MODULE) - if (dev->board.has_ir_i2c && !disable_ir) - request_module("ir-kbd-i2c"); -#endif - if (dev->board.has_snapshot_button) - em28xx_register_snapshot_button(dev); - if (dev->board.valid == EM28XX_BOARD_NOT_VALIDATED) { em28xx_errdev("\n\n"); em28xx_errdev("The support for this board weren't " @@ -2929,9 +2876,6 @@ void em28xx_card_setup(struct em28xx *dev) } em28xx_tuner_setup(dev); - - if(!disable_ir) - em28xx_ir_init(dev); } @@ -2948,6 +2892,8 @@ static void request_module_async(struct work_struct *work) if (dev->board.has_dvb) request_module("em28xx-dvb"); + if (dev->board.has_ir_i2c && !disable_ir) + request_module("em28xx-rc"); } static void request_modules(struct em28xx *dev) @@ -2972,12 +2918,6 @@ static void flush_request_modules(struct em28xx *dev) */ void em28xx_release_resources(struct em28xx *dev) { - if (dev->sbutton_input_dev) - em28xx_deregister_snapshot_button(dev); - - if (dev->ir) - em28xx_ir_fini(dev); - /*FIXME: I2C IR should be disconnected */ em28xx_release_analog_resources(dev); @@ -3005,9 +2945,6 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, dev->udev = udev; mutex_init(&dev->ctrl_urb_lock); spin_lock_init(&dev->slock); - init_waitqueue_head(&dev->open); - init_waitqueue_head(&dev->wait_frame); - init_waitqueue_head(&dev->wait_stream); dev->em28xx_write_regs = em28xx_write_regs; dev->em28xx_read_reg = em28xx_read_reg; @@ -3140,9 +3077,7 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, /* init video dma queues */ INIT_LIST_HEAD(&dev->vidq.active); - INIT_LIST_HEAD(&dev->vidq.queued); INIT_LIST_HEAD(&dev->vbiq.active); - INIT_LIST_HEAD(&dev->vbiq.queued); if (dev->board.has_msp34xx) { /* Send a reset to other chips via gpio */ @@ -3447,8 +3382,6 @@ static void em28xx_usb_disconnect(struct usb_interface *interface) resources */ mutex_lock(&dev->lock); - wake_up_interruptible_all(&dev->open); - v4l2_device_disconnect(&dev->v4l2_dev); if (dev->users) { @@ -3460,8 +3393,6 @@ static void em28xx_usb_disconnect(struct usb_interface *interface) dev->state |= DEV_MISCONFIGURED; em28xx_uninit_isoc(dev, dev->mode); dev->state |= DEV_DISCONNECTED; - wake_up_interruptible(&dev->wait_frame); - wake_up_interruptible(&dev->wait_stream); } else { dev->state |= DEV_DISCONNECTED; em28xx_release_resources(dev); diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c index 53a9fb91e97e..5717bdee8f1b 100644 --- a/drivers/media/video/em28xx/em28xx-core.c +++ b/drivers/media/video/em28xx/em28xx-core.c @@ -139,6 +139,7 @@ int em28xx_read_reg(struct em28xx *dev, u16 reg) { return em28xx_read_reg_req(dev, USB_REQ_GET_STATUS, reg); } +EXPORT_SYMBOL_GPL(em28xx_read_reg); /* * em28xx_write_regs_req() @@ -205,6 +206,7 @@ int em28xx_write_regs(struct em28xx *dev, u16 reg, char *buf, int len) return rc; } +EXPORT_SYMBOL_GPL(em28xx_write_regs); /* Write a single register */ int em28xx_write_reg(struct em28xx *dev, u16 reg, u8 val) @@ -239,6 +241,7 @@ int em28xx_write_reg_bits(struct em28xx *dev, u16 reg, u8 val, return em28xx_write_regs(dev, reg, &newval, 1); } +EXPORT_SYMBOL_GPL(em28xx_write_reg_bits); /* * em28xx_is_ac97_ready() @@ -666,7 +669,6 @@ int em28xx_capture_start(struct em28xx *dev, int start) return rc; } -EXPORT_SYMBOL_GPL(em28xx_capture_start); int em28xx_vbi_supported(struct em28xx *dev) { @@ -975,7 +977,6 @@ void em28xx_uninit_isoc(struct em28xx *dev, enum em28xx_mode mode) else isoc_bufs = &dev->isoc_ctl.analog_bufs; - dev->isoc_ctl.nfields = -1; for (i = 0; i < isoc_bufs->num_bufs; i++) { urb = isoc_bufs->urb[i]; if (urb) { @@ -1008,6 +1009,31 @@ void em28xx_uninit_isoc(struct em28xx *dev, enum em28xx_mode mode) EXPORT_SYMBOL_GPL(em28xx_uninit_isoc); /* + * Stop URBs + */ +void em28xx_stop_urbs(struct em28xx *dev) +{ + int i; + struct urb *urb; + struct em28xx_usb_isoc_bufs *isoc_bufs = &dev->isoc_ctl.digital_bufs; + + em28xx_isocdbg("em28xx: called em28xx_stop_urbs\n"); + + for (i = 0; i < isoc_bufs->num_bufs; i++) { + urb = isoc_bufs->urb[i]; + if (urb) { + if (!irqs_disabled()) + usb_kill_urb(urb); + else + usb_unlink_urb(urb); + } + } + + em28xx_capture_start(dev, 0); +} +EXPORT_SYMBOL_GPL(em28xx_stop_urbs); + +/* * Allocate URBs */ int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode, diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c index 503a8d5b5382..2d73ee2abf7e 100644 --- a/drivers/media/video/em28xx/em28xx-dvb.c +++ b/drivers/media/video/em28xx/em28xx-dvb.c @@ -183,7 +183,7 @@ static int em28xx_stop_streaming(struct em28xx_dvb *dvb) { struct em28xx *dev = dvb->adapter.priv; - em28xx_capture_start(dev, 0); + em28xx_stop_urbs(dev); em28xx_set_mode(dev, EM28XX_SUSPEND); diff --git a/drivers/media/video/em28xx/em28xx-i2c.c b/drivers/media/video/em28xx/em28xx-i2c.c index a88e169dba23..185db65b766e 100644 --- a/drivers/media/video/em28xx/em28xx-i2c.c +++ b/drivers/media/video/em28xx/em28xx-i2c.c @@ -553,9 +553,6 @@ int em28xx_i2c_register(struct em28xx *dev) if (i2c_scan) em28xx_do_i2c_scan(dev); - /* Instantiate the IR receiver device, if present */ - em28xx_register_i2c_ir(dev); - return 0; } diff --git a/drivers/media/video/em28xx/em28xx-input.c b/drivers/media/video/em28xx/em28xx-input.c index 2630b265b0e8..fce5f7680c99 100644 --- a/drivers/media/video/em28xx/em28xx-input.c +++ b/drivers/media/video/em28xx/em28xx-input.c @@ -80,7 +80,7 @@ struct em28xx_IR { I2C IR based get keycodes - should be used with ir-kbd-i2c **********************************************************/ -int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +static int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) { unsigned char b; @@ -108,7 +108,7 @@ int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) return 1; } -int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +static int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) { unsigned char buf[2]; u16 code; @@ -157,7 +157,7 @@ int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) return 1; } -int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key, +static int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) { unsigned char buf[3]; @@ -179,7 +179,8 @@ int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key, return 1; } -int em28xx_get_key_winfast_usbii_deluxe(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +static int em28xx_get_key_winfast_usbii_deluxe(struct IR_i2c *ir, u32 *ir_key, + u32 *ir_raw) { unsigned char subaddr, keydetect, key; @@ -387,7 +388,138 @@ int em28xx_ir_change_protocol(struct rc_dev *rc_dev, u64 rc_type) return rc; } -int em28xx_ir_init(struct em28xx *dev) +static void em28xx_register_i2c_ir(struct em28xx *dev) +{ + /* Leadtek winfast tv USBII deluxe can find a non working IR-device */ + /* at address 0x18, so if that address is needed for another board in */ + /* the future, please put it after 0x1f. */ + struct i2c_board_info info; + const unsigned short addr_list[] = { + 0x1f, 0x30, 0x47, I2C_CLIENT_END + }; + + memset(&info, 0, sizeof(struct i2c_board_info)); + memset(&dev->init_data, 0, sizeof(dev->init_data)); + strlcpy(info.type, "ir_video", I2C_NAME_SIZE); + + /* detect & configure */ + switch (dev->model) { + case EM2800_BOARD_TERRATEC_CINERGY_200: + case EM2820_BOARD_TERRATEC_CINERGY_250: + dev->init_data.ir_codes = RC_MAP_EM_TERRATEC; + dev->init_data.get_key = em28xx_get_key_terratec; + dev->init_data.name = "i2c IR (EM28XX Terratec)"; + break; + case EM2820_BOARD_PINNACLE_USB_2: + dev->init_data.ir_codes = RC_MAP_PINNACLE_GREY; + dev->init_data.get_key = em28xx_get_key_pinnacle_usb_grey; + dev->init_data.name = "i2c IR (EM28XX Pinnacle PCTV)"; + break; + case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2: + dev->init_data.ir_codes = RC_MAP_HAUPPAUGE; + dev->init_data.get_key = em28xx_get_key_em_haup; + dev->init_data.name = "i2c IR (EM2840 Hauppauge)"; + break; + case EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE: + dev->init_data.ir_codes = RC_MAP_WINFAST_USBII_DELUXE; + dev->init_data.get_key = em28xx_get_key_winfast_usbii_deluxe; + dev->init_data.name = "i2c IR (EM2820 Winfast TV USBII Deluxe)"; + break; + } + + if (dev->init_data.name) + info.platform_data = &dev->init_data; + i2c_new_probed_device(&dev->i2c_adap, &info, addr_list, NULL); +} + +/********************************************************** + Handle Webcam snapshot button + **********************************************************/ + +static void em28xx_query_sbutton(struct work_struct *work) +{ + /* Poll the register and see if the button is depressed */ + struct em28xx *dev = + container_of(work, struct em28xx, sbutton_query_work.work); + int ret; + + ret = em28xx_read_reg(dev, EM28XX_R0C_USBSUSP); + + if (ret & EM28XX_R0C_USBSUSP_SNAPSHOT) { + u8 cleared; + /* Button is depressed, clear the register */ + cleared = ((u8) ret) & ~EM28XX_R0C_USBSUSP_SNAPSHOT; + em28xx_write_regs(dev, EM28XX_R0C_USBSUSP, &cleared, 1); + + /* Not emulate the keypress */ + input_report_key(dev->sbutton_input_dev, EM28XX_SNAPSHOT_KEY, + 1); + /* Now unpress the key */ + input_report_key(dev->sbutton_input_dev, EM28XX_SNAPSHOT_KEY, + 0); + } + + /* Schedule next poll */ + schedule_delayed_work(&dev->sbutton_query_work, + msecs_to_jiffies(EM28XX_SBUTTON_QUERY_INTERVAL)); +} + +static void em28xx_register_snapshot_button(struct em28xx *dev) +{ + struct input_dev *input_dev; + int err; + + em28xx_info("Registering snapshot button...\n"); + input_dev = input_allocate_device(); + if (!input_dev) { + em28xx_errdev("input_allocate_device failed\n"); + return; + } + + usb_make_path(dev->udev, dev->snapshot_button_path, + sizeof(dev->snapshot_button_path)); + strlcat(dev->snapshot_button_path, "/sbutton", + sizeof(dev->snapshot_button_path)); + INIT_DELAYED_WORK(&dev->sbutton_query_work, em28xx_query_sbutton); + + input_dev->name = "em28xx snapshot button"; + input_dev->phys = dev->snapshot_button_path; + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); + set_bit(EM28XX_SNAPSHOT_KEY, input_dev->keybit); + input_dev->keycodesize = 0; + input_dev->keycodemax = 0; + input_dev->id.bustype = BUS_USB; + input_dev->id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor); + input_dev->id.product = le16_to_cpu(dev->udev->descriptor.idProduct); + input_dev->id.version = 1; + input_dev->dev.parent = &dev->udev->dev; + + err = input_register_device(input_dev); + if (err) { + em28xx_errdev("input_register_device failed\n"); + input_free_device(input_dev); + return; + } + + dev->sbutton_input_dev = input_dev; + schedule_delayed_work(&dev->sbutton_query_work, + msecs_to_jiffies(EM28XX_SBUTTON_QUERY_INTERVAL)); + return; + +} + +static void em28xx_deregister_snapshot_button(struct em28xx *dev) +{ + if (dev->sbutton_input_dev != NULL) { + em28xx_info("Deregistering snapshot button\n"); + cancel_delayed_work_sync(&dev->sbutton_query_work); + input_unregister_device(dev->sbutton_input_dev); + dev->sbutton_input_dev = NULL; + } + return; +} + +static int em28xx_ir_init(struct em28xx *dev) { struct em28xx_IR *ir; struct rc_dev *rc; @@ -448,6 +580,15 @@ int em28xx_ir_init(struct em28xx *dev) if (err) goto err_out_stop; + em28xx_register_i2c_ir(dev); + +#if defined(CONFIG_MODULES) && defined(MODULE) + if (dev->board.has_ir_i2c) + request_module("ir-kbd-i2c"); +#endif + if (dev->board.has_snapshot_button) + em28xx_register_snapshot_button(dev); + return 0; err_out_stop: @@ -458,10 +599,12 @@ int em28xx_ir_init(struct em28xx *dev) return err; } -int em28xx_ir_fini(struct em28xx *dev) +static int em28xx_ir_fini(struct em28xx *dev) { struct em28xx_IR *ir = dev->ir; + em28xx_deregister_snapshot_button(dev); + /* skip detach on non attached boards */ if (!ir) return 0; @@ -475,89 +618,26 @@ int em28xx_ir_fini(struct em28xx *dev) return 0; } -/********************************************************** - Handle Webcam snapshot button - **********************************************************/ +static struct em28xx_ops rc_ops = { + .id = EM28XX_RC, + .name = "Em28xx Input Extension", + .init = em28xx_ir_init, + .fini = em28xx_ir_fini, +}; -static void em28xx_query_sbutton(struct work_struct *work) +static int __init em28xx_rc_register(void) { - /* Poll the register and see if the button is depressed */ - struct em28xx *dev = - container_of(work, struct em28xx, sbutton_query_work.work); - int ret; - - ret = em28xx_read_reg(dev, EM28XX_R0C_USBSUSP); - - if (ret & EM28XX_R0C_USBSUSP_SNAPSHOT) { - u8 cleared; - /* Button is depressed, clear the register */ - cleared = ((u8) ret) & ~EM28XX_R0C_USBSUSP_SNAPSHOT; - em28xx_write_regs(dev, EM28XX_R0C_USBSUSP, &cleared, 1); - - /* Not emulate the keypress */ - input_report_key(dev->sbutton_input_dev, EM28XX_SNAPSHOT_KEY, - 1); - /* Now unpress the key */ - input_report_key(dev->sbutton_input_dev, EM28XX_SNAPSHOT_KEY, - 0); - } - - /* Schedule next poll */ - schedule_delayed_work(&dev->sbutton_query_work, - msecs_to_jiffies(EM28XX_SBUTTON_QUERY_INTERVAL)); + return em28xx_register_extension(&rc_ops); } -void em28xx_register_snapshot_button(struct em28xx *dev) +static void __exit em28xx_rc_unregister(void) { - struct input_dev *input_dev; - int err; - - em28xx_info("Registering snapshot button...\n"); - input_dev = input_allocate_device(); - if (!input_dev) { - em28xx_errdev("input_allocate_device failed\n"); - return; - } - - usb_make_path(dev->udev, dev->snapshot_button_path, - sizeof(dev->snapshot_button_path)); - strlcat(dev->snapshot_button_path, "/sbutton", - sizeof(dev->snapshot_button_path)); - INIT_DELAYED_WORK(&dev->sbutton_query_work, em28xx_query_sbutton); - - input_dev->name = "em28xx snapshot button"; - input_dev->phys = dev->snapshot_button_path; - input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); - set_bit(EM28XX_SNAPSHOT_KEY, input_dev->keybit); - input_dev->keycodesize = 0; - input_dev->keycodemax = 0; - input_dev->id.bustype = BUS_USB; - input_dev->id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor); - input_dev->id.product = le16_to_cpu(dev->udev->descriptor.idProduct); - input_dev->id.version = 1; - input_dev->dev.parent = &dev->udev->dev; - - err = input_register_device(input_dev); - if (err) { - em28xx_errdev("input_register_device failed\n"); - input_free_device(input_dev); - return; - } - - dev->sbutton_input_dev = input_dev; - schedule_delayed_work(&dev->sbutton_query_work, - msecs_to_jiffies(EM28XX_SBUTTON_QUERY_INTERVAL)); - return; - + em28xx_unregister_extension(&rc_ops); } -void em28xx_deregister_snapshot_button(struct em28xx *dev) -{ - if (dev->sbutton_input_dev != NULL) { - em28xx_info("Deregistering snapshot button\n"); - cancel_delayed_work_sync(&dev->sbutton_query_work); - input_unregister_device(dev->sbutton_input_dev); - dev->sbutton_input_dev = NULL; - } - return; -} +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); +MODULE_DESCRIPTION("Em28xx Input driver"); + +module_init(em28xx_rc_register); +module_exit(em28xx_rc_unregister); diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c index 324b695c0724..1d4068052ef0 100644 --- a/drivers/media/video/em28xx/em28xx-video.c +++ b/drivers/media/video/em28xx/em28xx-video.c @@ -1305,9 +1305,7 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i) if (0 == INPUT(i)->type) return -EINVAL; - dev->ctl_input = i; - - video_mux(dev, dev->ctl_input); + video_mux(dev, i); return 0; } @@ -2286,7 +2284,6 @@ static int em28xx_v4l2_close(struct file *filp) videobuf_mmap_free(&fh->vb_vbiq); kfree(fh); dev->users--; - wake_up_interruptible_nr(&dev->open, 1); return 0; } @@ -2497,6 +2494,10 @@ static struct video_device *em28xx_vdev_init(struct em28xx *dev, vfd->release = video_device_release; vfd->debug = video_debug; vfd->lock = &dev->lock; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags); snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name); @@ -2518,7 +2519,6 @@ int em28xx_register_analog_devices(struct em28xx *dev) dev->norm = em28xx_video_template.current_norm; v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm); dev->interlaced = EM28XX_INTERLACED_DEFAULT; - dev->ctl_input = 0; /* Analog specific initialization */ dev->format = &format[0]; @@ -2532,7 +2532,7 @@ int em28xx_register_analog_devices(struct em28xx *dev) em28xx_set_video_format(dev, format[0].fourcc, maxw, norm_maxh(dev)); - video_mux(dev, dev->ctl_input); + video_mux(dev, 0); /* Audio defaults */ dev->mute = 1; diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h index 2868b19f8b54..8757523e6863 100644 --- a/drivers/media/video/em28xx/em28xx.h +++ b/drivers/media/video/em28xx/em28xx.h @@ -226,24 +226,10 @@ struct em28xx_usb_isoc_ctl { /* isoc transfer buffers for digital mode */ struct em28xx_usb_isoc_bufs digital_bufs; - /* Last buffer command and region */ - u8 cmd; - int pos, size, pktsize; - - /* Last field: ODD or EVEN? */ - int field; - - /* Stores incomplete commands */ - u32 tmp_buf; - int tmp_buf_len; - /* Stores already requested buffers */ struct em28xx_buffer *vid_buf; struct em28xx_buffer *vbi_buf; - /* Stores the number of received fields */ - int nfields; - /* isoc urb callback */ int (*isoc_copy) (struct em28xx *dev, struct urb *urb); @@ -264,12 +250,10 @@ struct em28xx_buffer { struct list_head frame; int top_field; - int receiving; }; struct em28xx_dmaqueue { struct list_head active; - struct list_head queued; wait_queue_head_t wq; @@ -277,13 +261,6 @@ struct em28xx_dmaqueue { int pos; }; -/* io methods */ -enum em28xx_io_method { - IO_NONE, - IO_READ, - IO_MMAP, -}; - /* inputs */ #define MAX_EM28XX_INPUT 4 @@ -467,6 +444,7 @@ enum em28xx_dev_state { /* em28xx extensions */ #define EM28XX_AUDIO 0x10 #define EM28XX_DVB 0x20 +#define EM28XX_RC 0x30 /* em28xx resource types (used for res_get/res_lock etc */ #define EM28XX_RESOURCE_VIDEO 0x01 @@ -577,7 +555,6 @@ struct em28xx { /* states */ enum em28xx_dev_state state; - enum em28xx_io_method io; /* vbi related state tracking */ int capture_type; @@ -593,7 +570,6 @@ struct em28xx { struct mutex ctrl_urb_lock; /* protects urb_buf */ /* spinlock_t queue_lock; */ struct list_head inqueue, outqueue; - wait_queue_head_t open, wait_frame, wait_stream; struct video_device *vbi_dev; struct video_device *radio_dev; @@ -695,6 +671,7 @@ int em28xx_init_isoc(struct em28xx *dev, enum em28xx_mode mode, int max_packets, int num_bufs, int max_pkt_size, int (*isoc_copy) (struct em28xx *dev, struct urb *urb)); void em28xx_uninit_isoc(struct em28xx *dev, enum em28xx_mode mode); +void em28xx_stop_urbs(struct em28xx *dev); int em28xx_isoc_dvb_max_packetsize(struct em28xx *dev); int em28xx_set_mode(struct em28xx *dev, enum em28xx_mode set_mode); int em28xx_gpio_set(struct em28xx *dev, struct em28xx_reg_seq *gpio); @@ -710,45 +687,12 @@ void em28xx_release_analog_resources(struct em28xx *dev); /* Provided by em28xx-cards.c */ extern int em2800_variant_detect(struct usb_device *udev, int model); -extern void em28xx_pre_card_setup(struct em28xx *dev); -extern void em28xx_card_setup(struct em28xx *dev); extern struct em28xx_board em28xx_boards[]; extern struct usb_device_id em28xx_id_table[]; extern const unsigned int em28xx_bcount; -void em28xx_register_i2c_ir(struct em28xx *dev); int em28xx_tuner_callback(void *ptr, int component, int command, int arg); void em28xx_release_resources(struct em28xx *dev); -/* Provided by em28xx-input.c */ - -#ifdef CONFIG_VIDEO_EM28XX_RC - -int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw); -int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw); -int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key, - u32 *ir_raw); -int em28xx_get_key_winfast_usbii_deluxe(struct IR_i2c *ir, u32 *ir_key, - u32 *ir_raw); -void em28xx_register_snapshot_button(struct em28xx *dev); -void em28xx_deregister_snapshot_button(struct em28xx *dev); - -int em28xx_ir_init(struct em28xx *dev); -int em28xx_ir_fini(struct em28xx *dev); - -#else - -#define em28xx_get_key_terratec NULL -#define em28xx_get_key_em_haup NULL -#define em28xx_get_key_pinnacle_usb_grey NULL -#define em28xx_get_key_winfast_usbii_deluxe NULL - -static inline void em28xx_register_snapshot_button(struct em28xx *dev) {} -static inline void em28xx_deregister_snapshot_button(struct em28xx *dev) {} -static inline int em28xx_ir_init(struct em28xx *dev) { return 0; } -static inline int em28xx_ir_fini(struct em28xx *dev) { return 0; } - -#endif - /* Provided by em28xx-vbi.c */ extern struct videobuf_queue_ops em28xx_vbi_qops; diff --git a/drivers/media/video/et61x251/Kconfig b/drivers/media/video/et61x251/Kconfig deleted file mode 100644 index 87981b078fe6..000000000000 --- a/drivers/media/video/et61x251/Kconfig +++ /dev/null @@ -1,18 +0,0 @@ -config USB_ET61X251 - tristate "USB ET61X[12]51 PC Camera Controller support (DEPRECATED)" - depends on VIDEO_V4L2 - default n - ---help--- - This driver is DEPRECATED please use the gspca zc3xx module - instead. - - Say Y here if you want support for cameras based on Etoms ET61X151 - or ET61X251 PC Camera Controllers. - - See <file:Documentation/video4linux/et61x251.txt> for more info. - - This driver uses the Video For Linux API. You must say Y or M to - "Video For Linux" to use this driver. - - To compile this driver as a module, choose M here: the - module will be called et61x251. diff --git a/drivers/media/video/et61x251/Makefile b/drivers/media/video/et61x251/Makefile deleted file mode 100644 index 2ff4db9ec882..000000000000 --- a/drivers/media/video/et61x251/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -et61x251-objs := et61x251_core.o et61x251_tas5130d1b.o - -obj-$(CONFIG_USB_ET61X251) += et61x251.o - diff --git a/drivers/media/video/et61x251/et61x251.h b/drivers/media/video/et61x251/et61x251.h deleted file mode 100644 index 337ded4a6388..000000000000 --- a/drivers/media/video/et61x251/et61x251.h +++ /dev/null @@ -1,213 +0,0 @@ -/*************************************************************************** - * V4L2 driver for ET61X[12]51 PC Camera Controllers * - * * - * Copyright (C) 2006 by Luca Risolia <luca.risolia@studio.unibo.it> * - * * - * 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, write to the Free Software * - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * - ***************************************************************************/ - -#ifndef _ET61X251_H_ -#define _ET61X251_H_ - -#include <linux/usb.h> -#include <linux/videodev2.h> -#include <media/v4l2-common.h> -#include <linux/device.h> -#include <linux/list.h> -#include <linux/spinlock.h> -#include <linux/time.h> -#include <linux/wait.h> -#include <linux/types.h> -#include <linux/param.h> -#include <linux/rwsem.h> -#include <linux/mutex.h> -#include <linux/stddef.h> -#include <linux/string.h> -#include <linux/kref.h> - -#include "et61x251_sensor.h" - -/*****************************************************************************/ - -#define ET61X251_DEBUG -#define ET61X251_DEBUG_LEVEL 2 -#define ET61X251_MAX_DEVICES 64 -#define ET61X251_PRESERVE_IMGSCALE 0 -#define ET61X251_FORCE_MUNMAP 0 -#define ET61X251_MAX_FRAMES 32 -#define ET61X251_COMPRESSION_QUALITY 0 -#define ET61X251_URBS 2 -#define ET61X251_ISO_PACKETS 7 -#define ET61X251_ALTERNATE_SETTING 13 -#define ET61X251_URB_TIMEOUT msecs_to_jiffies(2 * ET61X251_ISO_PACKETS) -#define ET61X251_CTRL_TIMEOUT 100 -#define ET61X251_FRAME_TIMEOUT 2 - -/*****************************************************************************/ - -static const struct usb_device_id et61x251_id_table[] = { - { USB_DEVICE(0x102c, 0x6251), }, - { } -}; - -ET61X251_SENSOR_TABLE - -/*****************************************************************************/ - -enum et61x251_frame_state { - F_UNUSED, - F_QUEUED, - F_GRABBING, - F_DONE, - F_ERROR, -}; - -struct et61x251_frame_t { - void* bufmem; - struct v4l2_buffer buf; - enum et61x251_frame_state state; - struct list_head frame; - unsigned long vma_use_count; -}; - -enum et61x251_dev_state { - DEV_INITIALIZED = 0x01, - DEV_DISCONNECTED = 0x02, - DEV_MISCONFIGURED = 0x04, -}; - -enum et61x251_io_method { - IO_NONE, - IO_READ, - IO_MMAP, -}; - -enum et61x251_stream_state { - STREAM_OFF, - STREAM_INTERRUPT, - STREAM_ON, -}; - -struct et61x251_sysfs_attr { - u8 reg, i2c_reg; -}; - -struct et61x251_module_param { - u8 force_munmap; - u16 frame_timeout; -}; - -static DEFINE_MUTEX(et61x251_sysfs_lock); -static DECLARE_RWSEM(et61x251_dev_lock); - -struct et61x251_device { - struct video_device* v4ldev; - - struct et61x251_sensor sensor; - - struct usb_device* usbdev; - struct urb* urb[ET61X251_URBS]; - void* transfer_buffer[ET61X251_URBS]; - u8* control_buffer; - - struct et61x251_frame_t *frame_current, frame[ET61X251_MAX_FRAMES]; - struct list_head inqueue, outqueue; - u32 frame_count, nbuffers, nreadbuffers; - - enum et61x251_io_method io; - enum et61x251_stream_state stream; - - struct v4l2_jpegcompression compression; - - struct et61x251_sysfs_attr sysfs; - struct et61x251_module_param module_param; - - struct kref kref; - enum et61x251_dev_state state; - u8 users; - - struct completion probe; - struct mutex open_mutex, fileop_mutex; - spinlock_t queue_lock; - wait_queue_head_t wait_open, wait_frame, wait_stream; -}; - -/*****************************************************************************/ - -struct et61x251_device* -et61x251_match_id(struct et61x251_device* cam, const struct usb_device_id *id) -{ - return usb_match_id(usb_ifnum_to_if(cam->usbdev, 0), id) ? cam : NULL; -} - - -void -et61x251_attach_sensor(struct et61x251_device* cam, - const struct et61x251_sensor* sensor) -{ - memcpy(&cam->sensor, sensor, sizeof(struct et61x251_sensor)); -} - -/*****************************************************************************/ - -#undef DBG -#undef KDBG -#ifdef ET61X251_DEBUG -#define DBG(level, fmt, ...) \ -do { \ - if (debug >= (level)) { \ - if ((level) == 1) \ - dev_err(&cam->usbdev->dev, fmt "\n", \ - ##__VA_ARGS__); \ - else if ((level) == 2) \ - dev_info(&cam->usbdev->dev, fmt "\n", \ - ##__VA_ARGS__); \ - else if ((level) >= 3) \ - dev_info(&cam->usbdev->dev, "[%s:%s:%d] " fmt "\n", \ - __FILE__, __func__, __LINE__, \ - ##__VA_ARGS__); \ - } \ -} while (0) -#define KDBG(level, fmt, ...) \ -do { \ - if (debug >= (level)) { \ - if ((level) == 1 || (level) == 2) \ - pr_info(fmt "\n", ##__VA_ARGS__); \ - else if ((level) == 3) \ - pr_debug("[%s:%s:%d] " fmt "\n", \ - __FILE__, __func__, __LINE__, \ - ##__VA_ARGS__); \ - } \ -} while (0) -#define V4LDBG(level, name, cmd) \ -do { \ - if (debug >= (level)) \ - v4l_print_ioctl(name, cmd); \ -} while (0) -#else -#define DBG(level, fmt, ...) do {;} while(0) -#define KDBG(level, fmt, ...) do {;} while(0) -#define V4LDBG(level, name, cmd) do {;} while(0) -#endif - -#undef PDBG -#define PDBG(fmt, ...) \ - dev_info(&cam->usbdev->dev, "[%s:%s:%d] " fmt "\n", \ - __FILE__, __func__, __LINE__, ##__VA_ARGS__) - -#undef PDBGG -#define PDBGG(fmt, args...) do {;} while (0) /* placeholder */ - -#endif /* _ET61X251_H_ */ diff --git a/drivers/media/video/et61x251/et61x251_core.c b/drivers/media/video/et61x251/et61x251_core.c deleted file mode 100644 index 5539f09440ac..000000000000 --- a/drivers/media/video/et61x251/et61x251_core.c +++ /dev/null @@ -1,2683 +0,0 @@ -/*************************************************************************** - * V4L2 driver for ET61X[12]51 PC Camera Controllers * - * * - * Copyright (C) 2006-2007 by Luca Risolia <luca.risolia@studio.unibo.it> * - * * - * 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, write to the Free Software * - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * - ***************************************************************************/ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include <linux/version.h> -#include <linux/module.h> -#include <linux/init.h> -#include <linux/kernel.h> -#include <linux/param.h> -#include <linux/errno.h> -#include <linux/slab.h> -#include <linux/device.h> -#include <linux/fs.h> -#include <linux/delay.h> -#include <linux/compiler.h> -#include <linux/ioctl.h> -#include <linux/poll.h> -#include <linux/stat.h> -#include <linux/mm.h> -#include <linux/vmalloc.h> -#include <linux/page-flags.h> -#include <media/v4l2-ioctl.h> -#include <asm/byteorder.h> -#include <asm/page.h> -#include <asm/uaccess.h> - -#include "et61x251.h" - -/*****************************************************************************/ - -#define ET61X251_MODULE_NAME "V4L2 driver for ET61X[12]51 " \ - "PC Camera Controllers" -#define ET61X251_MODULE_AUTHOR "(C) 2006-2007 Luca Risolia" -#define ET61X251_AUTHOR_EMAIL "<luca.risolia@studio.unibo.it>" -#define ET61X251_MODULE_LICENSE "GPL" -#define ET61X251_MODULE_VERSION "1.1.10" - -/*****************************************************************************/ - -MODULE_DEVICE_TABLE(usb, et61x251_id_table); - -MODULE_AUTHOR(ET61X251_MODULE_AUTHOR " " ET61X251_AUTHOR_EMAIL); -MODULE_DESCRIPTION(ET61X251_MODULE_NAME); -MODULE_VERSION(ET61X251_MODULE_VERSION); -MODULE_LICENSE(ET61X251_MODULE_LICENSE); - -static short video_nr[] = {[0 ... ET61X251_MAX_DEVICES-1] = -1}; -module_param_array(video_nr, short, NULL, 0444); -MODULE_PARM_DESC(video_nr, - "\n<-1|n[,...]> Specify V4L2 minor mode number." - "\n -1 = use next available (default)" - "\n n = use minor number n (integer >= 0)" - "\nYou can specify up to " - __MODULE_STRING(ET61X251_MAX_DEVICES) " cameras this way." - "\nFor example:" - "\nvideo_nr=-1,2,-1 would assign minor number 2 to" - "\nthe second registered camera and use auto for the first" - "\none and for every other camera." - "\n"); - -static bool force_munmap[] = {[0 ... ET61X251_MAX_DEVICES-1] = - ET61X251_FORCE_MUNMAP}; -module_param_array(force_munmap, bool, NULL, 0444); -MODULE_PARM_DESC(force_munmap, - "\n<0|1[,...]> Force the application to unmap previously" - "\nmapped buffer memory before calling any VIDIOC_S_CROP or" - "\nVIDIOC_S_FMT ioctl's. Not all the applications support" - "\nthis feature. This parameter is specific for each" - "\ndetected camera." - "\n 0 = do not force memory unmapping" - "\n 1 = force memory unmapping (save memory)" - "\nDefault value is "__MODULE_STRING(ET61X251_FORCE_MUNMAP)"." - "\n"); - -static unsigned int frame_timeout[] = {[0 ... ET61X251_MAX_DEVICES-1] = - ET61X251_FRAME_TIMEOUT}; -module_param_array(frame_timeout, uint, NULL, 0644); -MODULE_PARM_DESC(frame_timeout, - "\n<n[,...]> Timeout for a video frame in seconds." - "\nThis parameter is specific for each detected camera." - "\nDefault value is " - __MODULE_STRING(ET61X251_FRAME_TIMEOUT)"." - "\n"); - -#ifdef ET61X251_DEBUG -static unsigned short debug = ET61X251_DEBUG_LEVEL; -module_param(debug, ushort, 0644); -MODULE_PARM_DESC(debug, - "\n<n> Debugging information level, from 0 to 3:" - "\n0 = none (use carefully)" - "\n1 = critical errors" - "\n2 = significant informations" - "\n3 = more verbose messages" - "\nLevel 3 is useful for testing only, when only " - "one device is used." - "\nDefault value is "__MODULE_STRING(ET61X251_DEBUG_LEVEL)"." - "\n"); -#endif - -/*****************************************************************************/ - -static u32 -et61x251_request_buffers(struct et61x251_device* cam, u32 count, - enum et61x251_io_method io) -{ - struct v4l2_pix_format* p = &(cam->sensor.pix_format); - struct v4l2_rect* r = &(cam->sensor.cropcap.bounds); - const size_t imagesize = cam->module_param.force_munmap || - io == IO_READ ? - (p->width * p->height * p->priv) / 8 : - (r->width * r->height * p->priv) / 8; - void* buff = NULL; - u32 i; - - if (count > ET61X251_MAX_FRAMES) - count = ET61X251_MAX_FRAMES; - - cam->nbuffers = count; - while (cam->nbuffers > 0) { - if ((buff = vmalloc_32_user(cam->nbuffers * - PAGE_ALIGN(imagesize)))) - break; - cam->nbuffers--; - } - - for (i = 0; i < cam->nbuffers; i++) { - cam->frame[i].bufmem = buff + i*PAGE_ALIGN(imagesize); - cam->frame[i].buf.index = i; - cam->frame[i].buf.m.offset = i*PAGE_ALIGN(imagesize); - cam->frame[i].buf.length = imagesize; - cam->frame[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - cam->frame[i].buf.sequence = 0; - cam->frame[i].buf.field = V4L2_FIELD_NONE; - cam->frame[i].buf.memory = V4L2_MEMORY_MMAP; - cam->frame[i].buf.flags = 0; - } - - return cam->nbuffers; -} - - -static void et61x251_release_buffers(struct et61x251_device* cam) -{ - if (cam->nbuffers) { - vfree(cam->frame[0].bufmem); - cam->nbuffers = 0; - } - cam->frame_current = NULL; -} - - -static void et61x251_empty_framequeues(struct et61x251_device* cam) -{ - u32 i; - - INIT_LIST_HEAD(&cam->inqueue); - INIT_LIST_HEAD(&cam->outqueue); - - for (i = 0; i < ET61X251_MAX_FRAMES; i++) { - cam->frame[i].state = F_UNUSED; - cam->frame[i].buf.bytesused = 0; - } -} - - -static void et61x251_requeue_outqueue(struct et61x251_device* cam) -{ - struct et61x251_frame_t *i; - - list_for_each_entry(i, &cam->outqueue, frame) { - i->state = F_QUEUED; - list_add(&i->frame, &cam->inqueue); - } - - INIT_LIST_HEAD(&cam->outqueue); -} - - -static void et61x251_queue_unusedframes(struct et61x251_device* cam) -{ - unsigned long lock_flags; - u32 i; - - for (i = 0; i < cam->nbuffers; i++) - if (cam->frame[i].state == F_UNUSED) { - cam->frame[i].state = F_QUEUED; - spin_lock_irqsave(&cam->queue_lock, lock_flags); - list_add_tail(&cam->frame[i].frame, &cam->inqueue); - spin_unlock_irqrestore(&cam->queue_lock, lock_flags); - } -} - -/*****************************************************************************/ - -int et61x251_write_reg(struct et61x251_device* cam, u8 value, u16 index) -{ - struct usb_device* udev = cam->usbdev; - u8* buff = cam->control_buffer; - int res; - - *buff = value; - - res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41, - 0, index, buff, 1, ET61X251_CTRL_TIMEOUT); - if (res < 0) { - DBG(3, "Failed to write a register (value 0x%02X, index " - "0x%02X, error %d)", value, index, res); - return -1; - } - - return 0; -} - - -static int et61x251_read_reg(struct et61x251_device* cam, u16 index) -{ - struct usb_device* udev = cam->usbdev; - u8* buff = cam->control_buffer; - int res; - - res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, 0xc1, - 0, index, buff, 1, ET61X251_CTRL_TIMEOUT); - if (res < 0) - DBG(3, "Failed to read a register (index 0x%02X, error %d)", - index, res); - - return (res >= 0) ? (int)(*buff) : -1; -} - - -static int -et61x251_i2c_wait(struct et61x251_device* cam, - const struct et61x251_sensor* sensor) -{ - int i, r; - - for (i = 1; i <= 8; i++) { - if (sensor->interface == ET61X251_I2C_3WIRES) { - r = et61x251_read_reg(cam, 0x8e); - if (!(r & 0x02) && (r >= 0)) - return 0; - } else { - r = et61x251_read_reg(cam, 0x8b); - if (!(r & 0x01) && (r >= 0)) - return 0; - } - if (r < 0) - return -EIO; - udelay(8*8); /* minimum for sensors at 400kHz */ - } - - return -EBUSY; -} - - -int -et61x251_i2c_raw_write(struct et61x251_device* cam, u8 n, u8 data1, u8 data2, - u8 data3, u8 data4, u8 data5, u8 data6, u8 data7, - u8 data8, u8 address) -{ - struct usb_device* udev = cam->usbdev; - u8* data = cam->control_buffer; - int err = 0, res; - - data[0] = data2; - data[1] = data3; - data[2] = data4; - data[3] = data5; - data[4] = data6; - data[5] = data7; - data[6] = data8; - res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41, - 0, 0x81, data, n-1, ET61X251_CTRL_TIMEOUT); - if (res < 0) - err += res; - - data[0] = address; - data[1] = cam->sensor.i2c_slave_id; - data[2] = cam->sensor.rsta | 0x02 | (n << 4); - res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41, - 0, 0x88, data, 3, ET61X251_CTRL_TIMEOUT); - if (res < 0) - err += res; - - /* Start writing through the serial interface */ - data[0] = data1; - res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41, - 0, 0x80, data, 1, ET61X251_CTRL_TIMEOUT); - if (res < 0) - err += res; - - err += et61x251_i2c_wait(cam, &cam->sensor); - - if (err) - DBG(3, "I2C raw write failed for %s image sensor", - cam->sensor.name); - - PDBGG("I2C raw write: %u bytes, address = 0x%02X, data1 = 0x%02X, " - "data2 = 0x%02X, data3 = 0x%02X, data4 = 0x%02X, data5 = 0x%02X," - " data6 = 0x%02X, data7 = 0x%02X, data8 = 0x%02X", n, address, - data1, data2, data3, data4, data5, data6, data7, data8); - - return err ? -1 : 0; - -} - - -/*****************************************************************************/ - -static void et61x251_urb_complete(struct urb *urb) -{ - struct et61x251_device* cam = urb->context; - struct et61x251_frame_t** f; - size_t imagesize; - u8 i; - int err = 0; - - if (urb->status == -ENOENT) - return; - - f = &cam->frame_current; - - if (cam->stream == STREAM_INTERRUPT) { - cam->stream = STREAM_OFF; - if ((*f)) - (*f)->state = F_QUEUED; - DBG(3, "Stream interrupted"); - wake_up(&cam->wait_stream); - } - - if (cam->state & DEV_DISCONNECTED) - return; - - if (cam->state & DEV_MISCONFIGURED) { - wake_up_interruptible(&cam->wait_frame); - return; - } - - if (cam->stream == STREAM_OFF || list_empty(&cam->inqueue)) - goto resubmit_urb; - - if (!(*f)) - (*f) = list_entry(cam->inqueue.next, struct et61x251_frame_t, - frame); - - imagesize = (cam->sensor.pix_format.width * - cam->sensor.pix_format.height * - cam->sensor.pix_format.priv) / 8; - - for (i = 0; i < urb->number_of_packets; i++) { - unsigned int len, status; - void *pos; - u8* b1, * b2, sof; - const u8 VOID_BYTES = 6; - size_t imglen; - - len = urb->iso_frame_desc[i].actual_length; - status = urb->iso_frame_desc[i].status; - pos = urb->iso_frame_desc[i].offset + urb->transfer_buffer; - - if (status) { - DBG(3, "Error in isochronous frame"); - (*f)->state = F_ERROR; - continue; - } - - b1 = pos++; - b2 = pos++; - sof = ((*b1 & 0x3f) == 63); - imglen = ((*b1 & 0xc0) << 2) | *b2; - - PDBGG("Isochrnous frame: length %u, #%u i, image length %zu", - len, i, imglen); - - if ((*f)->state == F_QUEUED || (*f)->state == F_ERROR) -start_of_frame: - if (sof) { - (*f)->state = F_GRABBING; - (*f)->buf.bytesused = 0; - do_gettimeofday(&(*f)->buf.timestamp); - pos += 22; - DBG(3, "SOF detected: new video frame"); - } - - if ((*f)->state == F_GRABBING) { - if (sof && (*f)->buf.bytesused) { - if (cam->sensor.pix_format.pixelformat == - V4L2_PIX_FMT_ET61X251) - goto end_of_frame; - else { - DBG(3, "Not expected SOF detected " - "after %lu bytes", - (unsigned long)(*f)->buf.bytesused); - (*f)->state = F_ERROR; - continue; - } - } - - if ((*f)->buf.bytesused + imglen > imagesize) { - DBG(3, "Video frame size exceeded"); - (*f)->state = F_ERROR; - continue; - } - - pos += VOID_BYTES; - - memcpy((*f)->bufmem+(*f)->buf.bytesused, pos, imglen); - (*f)->buf.bytesused += imglen; - - if ((*f)->buf.bytesused == imagesize) { - u32 b; -end_of_frame: - b = (*f)->buf.bytesused; - (*f)->state = F_DONE; - (*f)->buf.sequence= ++cam->frame_count; - spin_lock(&cam->queue_lock); - list_move_tail(&(*f)->frame, &cam->outqueue); - if (!list_empty(&cam->inqueue)) - (*f) = list_entry(cam->inqueue.next, - struct et61x251_frame_t, - frame); - else - (*f) = NULL; - spin_unlock(&cam->queue_lock); - DBG(3, "Video frame captured: : %lu bytes", - (unsigned long)(b)); - - if (!(*f)) - goto resubmit_urb; - - if (sof && - cam->sensor.pix_format.pixelformat == - V4L2_PIX_FMT_ET61X251) - goto start_of_frame; - } - } - } - -resubmit_urb: - urb->dev = cam->usbdev; - err = usb_submit_urb(urb, GFP_ATOMIC); - if (err < 0 && err != -EPERM) { - cam->state |= DEV_MISCONFIGURED; - DBG(1, "usb_submit_urb() failed"); - } - - wake_up_interruptible(&cam->wait_frame); -} - - -static int et61x251_start_transfer(struct et61x251_device* cam) -{ - struct usb_device *udev = cam->usbdev; - struct urb* urb; - struct usb_host_interface* altsetting = usb_altnum_to_altsetting( - usb_ifnum_to_if(udev, 0), - ET61X251_ALTERNATE_SETTING); - const unsigned int psz = le16_to_cpu(altsetting-> - endpoint[0].desc.wMaxPacketSize); - s8 i, j; - int err = 0; - - for (i = 0; i < ET61X251_URBS; i++) { - cam->transfer_buffer[i] = kzalloc(ET61X251_ISO_PACKETS * psz, - GFP_KERNEL); - if (!cam->transfer_buffer[i]) { - err = -ENOMEM; - DBG(1, "Not enough memory"); - goto free_buffers; - } - } - - for (i = 0; i < ET61X251_URBS; i++) { - urb = usb_alloc_urb(ET61X251_ISO_PACKETS, GFP_KERNEL); - cam->urb[i] = urb; - if (!urb) { - err = -ENOMEM; - DBG(1, "usb_alloc_urb() failed"); - goto free_urbs; - } - urb->dev = udev; - urb->context = cam; - urb->pipe = usb_rcvisocpipe(udev, 1); - urb->transfer_flags = URB_ISO_ASAP; - urb->number_of_packets = ET61X251_ISO_PACKETS; - urb->complete = et61x251_urb_complete; - urb->transfer_buffer = cam->transfer_buffer[i]; - urb->transfer_buffer_length = psz * ET61X251_ISO_PACKETS; - urb->interval = 1; - for (j = 0; j < ET61X251_ISO_PACKETS; j++) { - urb->iso_frame_desc[j].offset = psz * j; - urb->iso_frame_desc[j].length = psz; - } - } - - err = et61x251_write_reg(cam, 0x01, 0x03); - err = et61x251_write_reg(cam, 0x00, 0x03); - err = et61x251_write_reg(cam, 0x08, 0x03); - if (err) { - err = -EIO; - DBG(1, "I/O hardware error"); - goto free_urbs; - } - - err = usb_set_interface(udev, 0, ET61X251_ALTERNATE_SETTING); - if (err) { - DBG(1, "usb_set_interface() failed"); - goto free_urbs; - } - - cam->frame_current = NULL; - - for (i = 0; i < ET61X251_URBS; i++) { - err = usb_submit_urb(cam->urb[i], GFP_KERNEL); - if (err) { - for (j = i-1; j >= 0; j--) - usb_kill_urb(cam->urb[j]); - DBG(1, "usb_submit_urb() failed, error %d", err); - goto free_urbs; - } - } - - return 0; - -free_urbs: - for (i = 0; (i < ET61X251_URBS) && cam->urb[i]; i++) - usb_free_urb(cam->urb[i]); - -free_buffers: - for (i = 0; (i < ET61X251_URBS) && cam->transfer_buffer[i]; i++) - kfree(cam->transfer_buffer[i]); - - return err; -} - - -static int et61x251_stop_transfer(struct et61x251_device* cam) -{ - struct usb_device *udev = cam->usbdev; - s8 i; - int err = 0; - - if (cam->state & DEV_DISCONNECTED) - return 0; - - for (i = ET61X251_URBS-1; i >= 0; i--) { - usb_kill_urb(cam->urb[i]); - usb_free_urb(cam->urb[i]); - kfree(cam->transfer_buffer[i]); - } - - err = usb_set_interface(udev, 0, 0); /* 0 Mb/s */ - if (err) - DBG(3, "usb_set_interface() failed"); - - return err; -} - - -static int et61x251_stream_interrupt(struct et61x251_device* cam) -{ - long timeout; - - cam->stream = STREAM_INTERRUPT; - timeout = wait_event_timeout(cam->wait_stream, - (cam->stream == STREAM_OFF) || - (cam->state & DEV_DISCONNECTED), - ET61X251_URB_TIMEOUT); - if (cam->state & DEV_DISCONNECTED) - return -ENODEV; - else if (cam->stream != STREAM_OFF) { - cam->state |= DEV_MISCONFIGURED; - DBG(1, "URB timeout reached. The camera is misconfigured. To " - "use it, close and open %s again.", - video_device_node_name(cam->v4ldev)); - return -EIO; - } - - return 0; -} - -/*****************************************************************************/ - -#ifdef CONFIG_VIDEO_ADV_DEBUG - -static int et61x251_i2c_try_read(struct et61x251_device* cam, - const struct et61x251_sensor* sensor, - u8 address) -{ - struct usb_device* udev = cam->usbdev; - u8* data = cam->control_buffer; - int err = 0, res; - - data[0] = address; - data[1] = cam->sensor.i2c_slave_id; - data[2] = cam->sensor.rsta | 0x10; - data[3] = !(et61x251_read_reg(cam, 0x8b) & 0x02); - res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41, - 0, 0x88, data, 4, ET61X251_CTRL_TIMEOUT); - if (res < 0) - err += res; - - err += et61x251_i2c_wait(cam, sensor); - - res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, 0xc1, - 0, 0x80, data, 8, ET61X251_CTRL_TIMEOUT); - if (res < 0) - err += res; - - if (err) - DBG(3, "I2C read failed for %s image sensor", sensor->name); - - PDBGG("I2C read: address 0x%02X, value: 0x%02X", address, data[0]); - - return err ? -1 : (int)data[0]; -} - - -static int et61x251_i2c_try_write(struct et61x251_device* cam, - const struct et61x251_sensor* sensor, - u8 address, u8 value) -{ - struct usb_device* udev = cam->usbdev; - u8* data = cam->control_buffer; - int err = 0, res; - - data[0] = address; - data[1] = cam->sensor.i2c_slave_id; - data[2] = cam->sensor.rsta | 0x12; - res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41, - 0, 0x88, data, 3, ET61X251_CTRL_TIMEOUT); - if (res < 0) - err += res; - - data[0] = value; - res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41, - 0, 0x80, data, 1, ET61X251_CTRL_TIMEOUT); - if (res < 0) - err += res; - - err += et61x251_i2c_wait(cam, sensor); - - if (err) - DBG(3, "I2C write failed for %s image sensor", sensor->name); - - PDBGG("I2C write: address 0x%02X, value: 0x%02X", address, value); - - return err ? -1 : 0; -} - -static int et61x251_i2c_read(struct et61x251_device* cam, u8 address) -{ - return et61x251_i2c_try_read(cam, &cam->sensor, address); -} - -static int et61x251_i2c_write(struct et61x251_device* cam, - u8 address, u8 value) -{ - return et61x251_i2c_try_write(cam, &cam->sensor, address, value); -} - -static u8 et61x251_strtou8(const char* buff, size_t len, ssize_t* count) -{ - char str[5]; - char* endp; - unsigned long val; - - if (len < 4) { - strncpy(str, buff, len); - str[len] = '\0'; - } else { - strncpy(str, buff, 4); - str[4] = '\0'; - } - - val = simple_strtoul(str, &endp, 0); - - *count = 0; - if (val <= 0xff) - *count = (ssize_t)(endp - str); - if ((*count) && (len == *count+1) && (buff[*count] == '\n')) - *count += 1; - - return (u8)val; -} - -/* - NOTE 1: being inside one of the following methods implies that the v4l - device exists for sure (see kobjects and reference counters) - NOTE 2: buffers are PAGE_SIZE long -*/ - -static ssize_t et61x251_show_reg(struct device* cd, - struct device_attribute *attr, char* buf) -{ - struct et61x251_device* cam; - ssize_t count; - - if (mutex_lock_interruptible(&et61x251_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(to_video_device(cd)); - if (!cam) { - mutex_unlock(&et61x251_sysfs_lock); - return -ENODEV; - } - - count = sprintf(buf, "%u\n", cam->sysfs.reg); - - mutex_unlock(&et61x251_sysfs_lock); - - return count; -} - - -static ssize_t -et61x251_store_reg(struct device* cd, - struct device_attribute *attr, const char* buf, size_t len) -{ - struct et61x251_device* cam; - u8 index; - ssize_t count; - - if (mutex_lock_interruptible(&et61x251_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(to_video_device(cd)); - if (!cam) { - mutex_unlock(&et61x251_sysfs_lock); - return -ENODEV; - } - - index = et61x251_strtou8(buf, len, &count); - if (index > 0x8e || !count) { - mutex_unlock(&et61x251_sysfs_lock); - return -EINVAL; - } - - cam->sysfs.reg = index; - - DBG(2, "Moved ET61X[12]51 register index to 0x%02X", cam->sysfs.reg); - DBG(3, "Written bytes: %zd", count); - - mutex_unlock(&et61x251_sysfs_lock); - - return count; -} - - -static ssize_t et61x251_show_val(struct device* cd, - struct device_attribute *attr, char* buf) -{ - struct et61x251_device* cam; - ssize_t count; - int val; - - if (mutex_lock_interruptible(&et61x251_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(to_video_device(cd)); - if (!cam) { - mutex_unlock(&et61x251_sysfs_lock); - return -ENODEV; - } - - if ((val = et61x251_read_reg(cam, cam->sysfs.reg)) < 0) { - mutex_unlock(&et61x251_sysfs_lock); - return -EIO; - } - - count = sprintf(buf, "%d\n", val); - - DBG(3, "Read bytes: %zd", count); - - mutex_unlock(&et61x251_sysfs_lock); - - return count; -} - - -static ssize_t -et61x251_store_val(struct device* cd, struct device_attribute *attr, - const char* buf, size_t len) -{ - struct et61x251_device* cam; - u8 value; - ssize_t count; - int err; - - if (mutex_lock_interruptible(&et61x251_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(to_video_device(cd)); - if (!cam) { - mutex_unlock(&et61x251_sysfs_lock); - return -ENODEV; - } - - value = et61x251_strtou8(buf, len, &count); - if (!count) { - mutex_unlock(&et61x251_sysfs_lock); - return -EINVAL; - } - - err = et61x251_write_reg(cam, value, cam->sysfs.reg); - if (err) { - mutex_unlock(&et61x251_sysfs_lock); - return -EIO; - } - - DBG(2, "Written ET61X[12]51 reg. 0x%02X, val. 0x%02X", - cam->sysfs.reg, value); - DBG(3, "Written bytes: %zd", count); - - mutex_unlock(&et61x251_sysfs_lock); - - return count; -} - - -static ssize_t et61x251_show_i2c_reg(struct device* cd, - struct device_attribute *attr, char* buf) -{ - struct et61x251_device* cam; - ssize_t count; - - if (mutex_lock_interruptible(&et61x251_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(to_video_device(cd)); - if (!cam) { - mutex_unlock(&et61x251_sysfs_lock); - return -ENODEV; - } - - count = sprintf(buf, "%u\n", cam->sysfs.i2c_reg); - - DBG(3, "Read bytes: %zd", count); - - mutex_unlock(&et61x251_sysfs_lock); - - return count; -} - - -static ssize_t -et61x251_store_i2c_reg(struct device* cd, struct device_attribute *attr, - const char* buf, size_t len) -{ - struct et61x251_device* cam; - u8 index; - ssize_t count; - - if (mutex_lock_interruptible(&et61x251_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(to_video_device(cd)); - if (!cam) { - mutex_unlock(&et61x251_sysfs_lock); - return -ENODEV; - } - - index = et61x251_strtou8(buf, len, &count); - if (!count) { - mutex_unlock(&et61x251_sysfs_lock); - return -EINVAL; - } - - cam->sysfs.i2c_reg = index; - - DBG(2, "Moved sensor register index to 0x%02X", cam->sysfs.i2c_reg); - DBG(3, "Written bytes: %zd", count); - - mutex_unlock(&et61x251_sysfs_lock); - - return count; -} - - -static ssize_t et61x251_show_i2c_val(struct device* cd, - struct device_attribute *attr, char* buf) -{ - struct et61x251_device* cam; - ssize_t count; - int val; - - if (mutex_lock_interruptible(&et61x251_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(to_video_device(cd)); - if (!cam) { - mutex_unlock(&et61x251_sysfs_lock); - return -ENODEV; - } - - if (!(cam->sensor.sysfs_ops & ET61X251_I2C_READ)) { - mutex_unlock(&et61x251_sysfs_lock); - return -ENOSYS; - } - - if ((val = et61x251_i2c_read(cam, cam->sysfs.i2c_reg)) < 0) { - mutex_unlock(&et61x251_sysfs_lock); - return -EIO; - } - - count = sprintf(buf, "%d\n", val); - - DBG(3, "Read bytes: %zd", count); - - mutex_unlock(&et61x251_sysfs_lock); - - return count; -} - - -static ssize_t -et61x251_store_i2c_val(struct device* cd, struct device_attribute *attr, - const char* buf, size_t len) -{ - struct et61x251_device* cam; - u8 value; - ssize_t count; - int err; - - if (mutex_lock_interruptible(&et61x251_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(to_video_device(cd)); - if (!cam) { - mutex_unlock(&et61x251_sysfs_lock); - return -ENODEV; - } - - if (!(cam->sensor.sysfs_ops & ET61X251_I2C_READ)) { - mutex_unlock(&et61x251_sysfs_lock); - return -ENOSYS; - } - - value = et61x251_strtou8(buf, len, &count); - if (!count) { - mutex_unlock(&et61x251_sysfs_lock); - return -EINVAL; - } - - err = et61x251_i2c_write(cam, cam->sysfs.i2c_reg, value); - if (err) { - mutex_unlock(&et61x251_sysfs_lock); - return -EIO; - } - - DBG(2, "Written sensor reg. 0x%02X, val. 0x%02X", - cam->sysfs.i2c_reg, value); - DBG(3, "Written bytes: %zd", count); - - mutex_unlock(&et61x251_sysfs_lock); - - return count; -} - - -static DEVICE_ATTR(reg, S_IRUGO | S_IWUSR, - et61x251_show_reg, et61x251_store_reg); -static DEVICE_ATTR(val, S_IRUGO | S_IWUSR, - et61x251_show_val, et61x251_store_val); -static DEVICE_ATTR(i2c_reg, S_IRUGO | S_IWUSR, - et61x251_show_i2c_reg, et61x251_store_i2c_reg); -static DEVICE_ATTR(i2c_val, S_IRUGO | S_IWUSR, - et61x251_show_i2c_val, et61x251_store_i2c_val); - - -static int et61x251_create_sysfs(struct et61x251_device* cam) -{ - struct device *classdev = &(cam->v4ldev->dev); - int err = 0; - - if ((err = device_create_file(classdev, &dev_attr_reg))) - goto err_out; - if ((err = device_create_file(classdev, &dev_attr_val))) - goto err_reg; - - if (cam->sensor.sysfs_ops) { - if ((err = device_create_file(classdev, &dev_attr_i2c_reg))) - goto err_val; - if ((err = device_create_file(classdev, &dev_attr_i2c_val))) - goto err_i2c_reg; - } - -err_i2c_reg: - if (cam->sensor.sysfs_ops) - device_remove_file(classdev, &dev_attr_i2c_reg); -err_val: - device_remove_file(classdev, &dev_attr_val); -err_reg: - device_remove_file(classdev, &dev_attr_reg); -err_out: - return err; -} -#endif /* CONFIG_VIDEO_ADV_DEBUG */ - -/*****************************************************************************/ - -static int -et61x251_set_pix_format(struct et61x251_device* cam, - struct v4l2_pix_format* pix) -{ - int r, err = 0; - - if ((r = et61x251_read_reg(cam, 0x12)) < 0) - err += r; - if (pix->pixelformat == V4L2_PIX_FMT_ET61X251) - err += et61x251_write_reg(cam, r & 0xfd, 0x12); - else - err += et61x251_write_reg(cam, r | 0x02, 0x12); - - return err ? -EIO : 0; -} - - -static int -et61x251_set_compression(struct et61x251_device* cam, - struct v4l2_jpegcompression* compression) -{ - int r, err = 0; - - if ((r = et61x251_read_reg(cam, 0x12)) < 0) - err += r; - if (compression->quality == 0) - err += et61x251_write_reg(cam, r & 0xfb, 0x12); - else - err += et61x251_write_reg(cam, r | 0x04, 0x12); - - return err ? -EIO : 0; -} - - -static int et61x251_set_scale(struct et61x251_device* cam, u8 scale) -{ - int r = 0, err = 0; - - r = et61x251_read_reg(cam, 0x12); - if (r < 0) - err += r; - - if (scale == 1) - err += et61x251_write_reg(cam, r & ~0x01, 0x12); - else if (scale == 2) - err += et61x251_write_reg(cam, r | 0x01, 0x12); - - if (err) - return -EIO; - - PDBGG("Scaling factor: %u", scale); - - return 0; -} - - -static int -et61x251_set_crop(struct et61x251_device* cam, struct v4l2_rect* rect) -{ - struct et61x251_sensor* s = &cam->sensor; - u16 fmw_sx = (u16)(rect->left - s->cropcap.bounds.left + - s->active_pixel.left), - fmw_sy = (u16)(rect->top - s->cropcap.bounds.top + - s->active_pixel.top), - fmw_length = (u16)(rect->width), - fmw_height = (u16)(rect->height); - int err = 0; - - err += et61x251_write_reg(cam, fmw_sx & 0xff, 0x69); - err += et61x251_write_reg(cam, fmw_sy & 0xff, 0x6a); - err += et61x251_write_reg(cam, fmw_length & 0xff, 0x6b); - err += et61x251_write_reg(cam, fmw_height & 0xff, 0x6c); - err += et61x251_write_reg(cam, (fmw_sx >> 8) | ((fmw_sy & 0x300) >> 6) - | ((fmw_length & 0x300) >> 4) - | ((fmw_height & 0x300) >> 2), 0x6d); - if (err) - return -EIO; - - PDBGG("fmw_sx, fmw_sy, fmw_length, fmw_height: %u %u %u %u", - fmw_sx, fmw_sy, fmw_length, fmw_height); - - return 0; -} - - -static int et61x251_init(struct et61x251_device* cam) -{ - struct et61x251_sensor* s = &cam->sensor; - struct v4l2_control ctrl; - struct v4l2_queryctrl *qctrl; - struct v4l2_rect* rect; - u8 i = 0; - int err = 0; - - if (!(cam->state & DEV_INITIALIZED)) { - mutex_init(&cam->open_mutex); - init_waitqueue_head(&cam->wait_open); - qctrl = s->qctrl; - rect = &(s->cropcap.defrect); - cam->compression.quality = ET61X251_COMPRESSION_QUALITY; - } else { /* use current values */ - qctrl = s->_qctrl; - rect = &(s->_rect); - } - - err += et61x251_set_scale(cam, rect->width / s->pix_format.width); - err += et61x251_set_crop(cam, rect); - if (err) - return err; - - if (s->init) { - err = s->init(cam); - if (err) { - DBG(3, "Sensor initialization failed"); - return err; - } - } - - err += et61x251_set_compression(cam, &cam->compression); - err += et61x251_set_pix_format(cam, &s->pix_format); - if (s->set_pix_format) - err += s->set_pix_format(cam, &s->pix_format); - if (err) - return err; - - if (s->pix_format.pixelformat == V4L2_PIX_FMT_ET61X251) - DBG(3, "Compressed video format is active, quality %d", - cam->compression.quality); - else - DBG(3, "Uncompressed video format is active"); - - if (s->set_crop) - if ((err = s->set_crop(cam, rect))) { - DBG(3, "set_crop() failed"); - return err; - } - - if (s->set_ctrl) { - for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) - if (s->qctrl[i].id != 0 && - !(s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED)) { - ctrl.id = s->qctrl[i].id; - ctrl.value = qctrl[i].default_value; - err = s->set_ctrl(cam, &ctrl); - if (err) { - DBG(3, "Set %s control failed", - s->qctrl[i].name); - return err; - } - DBG(3, "Image sensor supports '%s' control", - s->qctrl[i].name); - } - } - - if (!(cam->state & DEV_INITIALIZED)) { - mutex_init(&cam->fileop_mutex); - spin_lock_init(&cam->queue_lock); - init_waitqueue_head(&cam->wait_frame); - init_waitqueue_head(&cam->wait_stream); - cam->nreadbuffers = 2; - memcpy(s->_qctrl, s->qctrl, sizeof(s->qctrl)); - memcpy(&(s->_rect), &(s->cropcap.defrect), - sizeof(struct v4l2_rect)); - cam->state |= DEV_INITIALIZED; - } - - DBG(2, "Initialization succeeded"); - return 0; -} - -/*****************************************************************************/ - -static void et61x251_release_resources(struct kref *kref) -{ - struct et61x251_device *cam; - - mutex_lock(&et61x251_sysfs_lock); - - cam = container_of(kref, struct et61x251_device, kref); - - DBG(2, "V4L2 device %s deregistered", - video_device_node_name(cam->v4ldev)); - video_set_drvdata(cam->v4ldev, NULL); - video_unregister_device(cam->v4ldev); - usb_put_dev(cam->usbdev); - kfree(cam->control_buffer); - kfree(cam); - - mutex_unlock(&et61x251_sysfs_lock); -} - - -static int et61x251_open(struct file *filp) -{ - struct et61x251_device* cam; - int err = 0; - - if (!down_read_trylock(&et61x251_dev_lock)) - return -ERESTARTSYS; - - cam = video_drvdata(filp); - - if (wait_for_completion_interruptible(&cam->probe)) { - up_read(&et61x251_dev_lock); - return -ERESTARTSYS; - } - - kref_get(&cam->kref); - - if (mutex_lock_interruptible(&cam->open_mutex)) { - kref_put(&cam->kref, et61x251_release_resources); - up_read(&et61x251_dev_lock); - return -ERESTARTSYS; - } - - if (cam->state & DEV_DISCONNECTED) { - DBG(1, "Device not present"); - err = -ENODEV; - goto out; - } - - if (cam->users) { - DBG(2, "Device %s is already in use", - video_device_node_name(cam->v4ldev)); - DBG(3, "Simultaneous opens are not supported"); - if ((filp->f_flags & O_NONBLOCK) || - (filp->f_flags & O_NDELAY)) { - err = -EWOULDBLOCK; - goto out; - } - DBG(2, "A blocking open() has been requested. Wait for the " - "device to be released..."); - up_read(&et61x251_dev_lock); - err = wait_event_interruptible_exclusive(cam->wait_open, - (cam->state & DEV_DISCONNECTED) - || !cam->users); - down_read(&et61x251_dev_lock); - if (err) - goto out; - if (cam->state & DEV_DISCONNECTED) { - err = -ENODEV; - goto out; - } - } - - if (cam->state & DEV_MISCONFIGURED) { - err = et61x251_init(cam); - if (err) { - DBG(1, "Initialization failed again. " - "I will retry on next open()."); - goto out; - } - cam->state &= ~DEV_MISCONFIGURED; - } - - if ((err = et61x251_start_transfer(cam))) - goto out; - - filp->private_data = cam; - cam->users++; - cam->io = IO_NONE; - cam->stream = STREAM_OFF; - cam->nbuffers = 0; - cam->frame_count = 0; - et61x251_empty_framequeues(cam); - - DBG(3, "Video device %s is open", - video_device_node_name(cam->v4ldev)); - -out: - mutex_unlock(&cam->open_mutex); - if (err) - kref_put(&cam->kref, et61x251_release_resources); - up_read(&et61x251_dev_lock); - return err; -} - - -static int et61x251_release(struct file *filp) -{ - struct et61x251_device* cam; - - down_write(&et61x251_dev_lock); - - cam = video_drvdata(filp); - - et61x251_stop_transfer(cam); - et61x251_release_buffers(cam); - cam->users--; - wake_up_interruptible_nr(&cam->wait_open, 1); - - DBG(3, "Video device %s closed", - video_device_node_name(cam->v4ldev)); - - kref_put(&cam->kref, et61x251_release_resources); - - up_write(&et61x251_dev_lock); - - return 0; -} - - -static ssize_t -et61x251_read(struct file* filp, char __user * buf, - size_t count, loff_t* f_pos) -{ - struct et61x251_device *cam = video_drvdata(filp); - struct et61x251_frame_t* f, * i; - unsigned long lock_flags; - long timeout; - int err = 0; - - if (mutex_lock_interruptible(&cam->fileop_mutex)) - return -ERESTARTSYS; - - if (cam->state & DEV_DISCONNECTED) { - DBG(1, "Device not present"); - mutex_unlock(&cam->fileop_mutex); - return -ENODEV; - } - - if (cam->state & DEV_MISCONFIGURED) { - DBG(1, "The camera is misconfigured. Close and open it " - "again."); - mutex_unlock(&cam->fileop_mutex); - return -EIO; - } - - if (cam->io == IO_MMAP) { - DBG(3, "Close and open the device again to choose the read " - "method"); - mutex_unlock(&cam->fileop_mutex); - return -EBUSY; - } - - if (cam->io == IO_NONE) { - if (!et61x251_request_buffers(cam, cam->nreadbuffers, - IO_READ)) { - DBG(1, "read() failed, not enough memory"); - mutex_unlock(&cam->fileop_mutex); - return -ENOMEM; - } - cam->io = IO_READ; - cam->stream = STREAM_ON; - } - - if (list_empty(&cam->inqueue)) { - if (!list_empty(&cam->outqueue)) - et61x251_empty_framequeues(cam); - et61x251_queue_unusedframes(cam); - } - - if (!count) { - mutex_unlock(&cam->fileop_mutex); - return 0; - } - - if (list_empty(&cam->outqueue)) { - if (filp->f_flags & O_NONBLOCK) { - mutex_unlock(&cam->fileop_mutex); - return -EAGAIN; - } - timeout = wait_event_interruptible_timeout - ( cam->wait_frame, - (!list_empty(&cam->outqueue)) || - (cam->state & DEV_DISCONNECTED) || - (cam->state & DEV_MISCONFIGURED), - msecs_to_jiffies( - cam->module_param.frame_timeout * 1000 - ) - ); - if (timeout < 0) { - mutex_unlock(&cam->fileop_mutex); - return timeout; - } - if (cam->state & DEV_DISCONNECTED) { - mutex_unlock(&cam->fileop_mutex); - return -ENODEV; - } - if (!timeout || (cam->state & DEV_MISCONFIGURED)) { - mutex_unlock(&cam->fileop_mutex); - return -EIO; - } - } - - f = list_entry(cam->outqueue.prev, struct et61x251_frame_t, frame); - - if (count > f->buf.bytesused) - count = f->buf.bytesused; - - if (copy_to_user(buf, f->bufmem, count)) { - err = -EFAULT; - goto exit; - } - *f_pos += count; - -exit: - spin_lock_irqsave(&cam->queue_lock, lock_flags); - list_for_each_entry(i, &cam->outqueue, frame) - i->state = F_UNUSED; - INIT_LIST_HEAD(&cam->outqueue); - spin_unlock_irqrestore(&cam->queue_lock, lock_flags); - - et61x251_queue_unusedframes(cam); - - PDBGG("Frame #%lu, bytes read: %zu", - (unsigned long)f->buf.index, count); - - mutex_unlock(&cam->fileop_mutex); - - return err ? err : count; -} - - -static unsigned int et61x251_poll(struct file *filp, poll_table *wait) -{ - struct et61x251_device *cam = video_drvdata(filp); - struct et61x251_frame_t* f; - unsigned long lock_flags; - unsigned int mask = 0; - - if (mutex_lock_interruptible(&cam->fileop_mutex)) - return POLLERR; - - if (cam->state & DEV_DISCONNECTED) { - DBG(1, "Device not present"); - goto error; - } - - if (cam->state & DEV_MISCONFIGURED) { - DBG(1, "The camera is misconfigured. Close and open it " - "again."); - goto error; - } - - if (cam->io == IO_NONE) { - if (!et61x251_request_buffers(cam, cam->nreadbuffers, - IO_READ)) { - DBG(1, "poll() failed, not enough memory"); - goto error; - } - cam->io = IO_READ; - cam->stream = STREAM_ON; - } - - if (cam->io == IO_READ) { - spin_lock_irqsave(&cam->queue_lock, lock_flags); - list_for_each_entry(f, &cam->outqueue, frame) - f->state = F_UNUSED; - INIT_LIST_HEAD(&cam->outqueue); - spin_unlock_irqrestore(&cam->queue_lock, lock_flags); - et61x251_queue_unusedframes(cam); - } - - poll_wait(filp, &cam->wait_frame, wait); - - if (!list_empty(&cam->outqueue)) - mask |= POLLIN | POLLRDNORM; - - mutex_unlock(&cam->fileop_mutex); - - return mask; - -error: - mutex_unlock(&cam->fileop_mutex); - return POLLERR; -} - - -static void et61x251_vm_open(struct vm_area_struct* vma) -{ - struct et61x251_frame_t* f = vma->vm_private_data; - f->vma_use_count++; -} - - -static void et61x251_vm_close(struct vm_area_struct* vma) -{ - /* NOTE: buffers are not freed here */ - struct et61x251_frame_t* f = vma->vm_private_data; - f->vma_use_count--; -} - - -static const struct vm_operations_struct et61x251_vm_ops = { - .open = et61x251_vm_open, - .close = et61x251_vm_close, -}; - - -static int et61x251_mmap(struct file* filp, struct vm_area_struct *vma) -{ - struct et61x251_device *cam = video_drvdata(filp); - unsigned long size = vma->vm_end - vma->vm_start, - start = vma->vm_start; - void *pos; - u32 i; - - if (mutex_lock_interruptible(&cam->fileop_mutex)) - return -ERESTARTSYS; - - if (cam->state & DEV_DISCONNECTED) { - DBG(1, "Device not present"); - mutex_unlock(&cam->fileop_mutex); - return -ENODEV; - } - - if (cam->state & DEV_MISCONFIGURED) { - DBG(1, "The camera is misconfigured. Close and open it " - "again."); - mutex_unlock(&cam->fileop_mutex); - return -EIO; - } - - if (!(vma->vm_flags & (VM_WRITE | VM_READ))) { - mutex_unlock(&cam->fileop_mutex); - return -EACCES; - } - - if (cam->io != IO_MMAP || - size != PAGE_ALIGN(cam->frame[0].buf.length)) { - mutex_unlock(&cam->fileop_mutex); - return -EINVAL; - } - - for (i = 0; i < cam->nbuffers; i++) { - if ((cam->frame[i].buf.m.offset>>PAGE_SHIFT) == vma->vm_pgoff) - break; - } - if (i == cam->nbuffers) { - mutex_unlock(&cam->fileop_mutex); - return -EINVAL; - } - - vma->vm_flags |= VM_IO; - vma->vm_flags |= VM_RESERVED; - - pos = cam->frame[i].bufmem; - while (size > 0) { /* size is page-aligned */ - if (vm_insert_page(vma, start, vmalloc_to_page(pos))) { - mutex_unlock(&cam->fileop_mutex); - return -EAGAIN; - } - start += PAGE_SIZE; - pos += PAGE_SIZE; - size -= PAGE_SIZE; - } - - vma->vm_ops = &et61x251_vm_ops; - vma->vm_private_data = &cam->frame[i]; - et61x251_vm_open(vma); - - mutex_unlock(&cam->fileop_mutex); - - return 0; -} - -/*****************************************************************************/ - -static int -et61x251_vidioc_querycap(struct et61x251_device* cam, void __user * arg) -{ - struct v4l2_capability cap = { - .driver = "et61x251", - .version = LINUX_VERSION_CODE, - .capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING, - }; - - strlcpy(cap.card, cam->v4ldev->name, sizeof(cap.card)); - if (usb_make_path(cam->usbdev, cap.bus_info, sizeof(cap.bus_info)) < 0) - strlcpy(cap.bus_info, dev_name(&cam->usbdev->dev), - sizeof(cap.bus_info)); - - if (copy_to_user(arg, &cap, sizeof(cap))) - return -EFAULT; - - return 0; -} - - -static int -et61x251_vidioc_enuminput(struct et61x251_device* cam, void __user * arg) -{ - struct v4l2_input i; - - if (copy_from_user(&i, arg, sizeof(i))) - return -EFAULT; - - if (i.index) - return -EINVAL; - - memset(&i, 0, sizeof(i)); - strcpy(i.name, "Camera"); - i.type = V4L2_INPUT_TYPE_CAMERA; - i.capabilities = V4L2_IN_CAP_STD; - - if (copy_to_user(arg, &i, sizeof(i))) - return -EFAULT; - - return 0; -} - - -static int -et61x251_vidioc_g_input(struct et61x251_device* cam, void __user * arg) -{ - int index = 0; - - if (copy_to_user(arg, &index, sizeof(index))) - return -EFAULT; - - return 0; -} - - -static int -et61x251_vidioc_s_input(struct et61x251_device* cam, void __user * arg) -{ - int index; - - if (copy_from_user(&index, arg, sizeof(index))) - return -EFAULT; - - if (index != 0) - return -EINVAL; - - return 0; -} - - -static int -et61x251_vidioc_query_ctrl(struct et61x251_device* cam, void __user * arg) -{ - struct et61x251_sensor* s = &cam->sensor; - struct v4l2_queryctrl qc; - u8 i; - - if (copy_from_user(&qc, arg, sizeof(qc))) - return -EFAULT; - - for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) - if (qc.id && qc.id == s->qctrl[i].id) { - memcpy(&qc, &(s->qctrl[i]), sizeof(qc)); - if (copy_to_user(arg, &qc, sizeof(qc))) - return -EFAULT; - return 0; - } - - return -EINVAL; -} - - -static int -et61x251_vidioc_g_ctrl(struct et61x251_device* cam, void __user * arg) -{ - struct et61x251_sensor* s = &cam->sensor; - struct v4l2_control ctrl; - int err = 0; - u8 i; - - if (!s->get_ctrl && !s->set_ctrl) - return -EINVAL; - - if (copy_from_user(&ctrl, arg, sizeof(ctrl))) - return -EFAULT; - - if (!s->get_ctrl) { - for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) - if (ctrl.id == s->qctrl[i].id) { - ctrl.value = s->_qctrl[i].default_value; - goto exit; - } - return -EINVAL; - } else - err = s->get_ctrl(cam, &ctrl); - -exit: - if (copy_to_user(arg, &ctrl, sizeof(ctrl))) - return -EFAULT; - - return err; -} - - -static int -et61x251_vidioc_s_ctrl(struct et61x251_device* cam, void __user * arg) -{ - struct et61x251_sensor* s = &cam->sensor; - struct v4l2_control ctrl; - u8 i; - int err = 0; - - if (!s->set_ctrl) - return -EINVAL; - - if (copy_from_user(&ctrl, arg, sizeof(ctrl))) - return -EFAULT; - - for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) { - if (ctrl.id == s->qctrl[i].id) { - if (s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED) - return -EINVAL; - if (ctrl.value < s->qctrl[i].minimum || - ctrl.value > s->qctrl[i].maximum) - return -ERANGE; - ctrl.value -= ctrl.value % s->qctrl[i].step; - break; - } - } - if (i == ARRAY_SIZE(s->qctrl)) - return -EINVAL; - if ((err = s->set_ctrl(cam, &ctrl))) - return err; - - s->_qctrl[i].default_value = ctrl.value; - - return 0; -} - - -static int -et61x251_vidioc_cropcap(struct et61x251_device* cam, void __user * arg) -{ - struct v4l2_cropcap* cc = &(cam->sensor.cropcap); - - cc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - cc->pixelaspect.numerator = 1; - cc->pixelaspect.denominator = 1; - - if (copy_to_user(arg, cc, sizeof(*cc))) - return -EFAULT; - - return 0; -} - - -static int -et61x251_vidioc_g_crop(struct et61x251_device* cam, void __user * arg) -{ - struct et61x251_sensor* s = &cam->sensor; - struct v4l2_crop crop = { - .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, - }; - - memcpy(&(crop.c), &(s->_rect), sizeof(struct v4l2_rect)); - - if (copy_to_user(arg, &crop, sizeof(crop))) - return -EFAULT; - - return 0; -} - - -static int -et61x251_vidioc_s_crop(struct et61x251_device* cam, void __user * arg) -{ - struct et61x251_sensor* s = &cam->sensor; - struct v4l2_crop crop; - struct v4l2_rect* rect; - struct v4l2_rect* bounds = &(s->cropcap.bounds); - struct v4l2_pix_format* pix_format = &(s->pix_format); - u8 scale; - const enum et61x251_stream_state stream = cam->stream; - const u32 nbuffers = cam->nbuffers; - u32 i; - int err = 0; - - if (copy_from_user(&crop, arg, sizeof(crop))) - return -EFAULT; - - rect = &(crop.c); - - if (crop.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if (cam->module_param.force_munmap) - for (i = 0; i < cam->nbuffers; i++) - if (cam->frame[i].vma_use_count) { - DBG(3, "VIDIOC_S_CROP failed. " - "Unmap the buffers first."); - return -EBUSY; - } - - /* Preserve R,G or B origin */ - rect->left = (s->_rect.left & 1L) ? rect->left | 1L : rect->left & ~1L; - rect->top = (s->_rect.top & 1L) ? rect->top | 1L : rect->top & ~1L; - - if (rect->width < 16) - rect->width = 16; - if (rect->height < 16) - rect->height = 16; - if (rect->width > bounds->width) - rect->width = bounds->width; - if (rect->height > bounds->height) - rect->height = bounds->height; - if (rect->left < bounds->left) - rect->left = bounds->left; - if (rect->top < bounds->top) - rect->top = bounds->top; - if (rect->left + rect->width > bounds->left + bounds->width) - rect->left = bounds->left+bounds->width - rect->width; - if (rect->top + rect->height > bounds->top + bounds->height) - rect->top = bounds->top+bounds->height - rect->height; - - rect->width &= ~15L; - rect->height &= ~15L; - - if (ET61X251_PRESERVE_IMGSCALE) { - /* Calculate the actual scaling factor */ - u32 a, b; - a = rect->width * rect->height; - b = pix_format->width * pix_format->height; - scale = b ? (u8)((a / b) < 4 ? 1 : 2) : 1; - } else - scale = 1; - - if (cam->stream == STREAM_ON) - if ((err = et61x251_stream_interrupt(cam))) - return err; - - if (copy_to_user(arg, &crop, sizeof(crop))) { - cam->stream = stream; - return -EFAULT; - } - - if (cam->module_param.force_munmap || cam->io == IO_READ) - et61x251_release_buffers(cam); - - err = et61x251_set_crop(cam, rect); - if (s->set_crop) - err += s->set_crop(cam, rect); - err += et61x251_set_scale(cam, scale); - - if (err) { /* atomic, no rollback in ioctl() */ - cam->state |= DEV_MISCONFIGURED; - DBG(1, "VIDIOC_S_CROP failed because of hardware problems. To " - "use the camera, close and open %s again.", - video_device_node_name(cam->v4ldev)); - return -EIO; - } - - s->pix_format.width = rect->width/scale; - s->pix_format.height = rect->height/scale; - memcpy(&(s->_rect), rect, sizeof(*rect)); - - if ((cam->module_param.force_munmap || cam->io == IO_READ) && - nbuffers != et61x251_request_buffers(cam, nbuffers, cam->io)) { - cam->state |= DEV_MISCONFIGURED; - DBG(1, "VIDIOC_S_CROP failed because of not enough memory. To " - "use the camera, close and open %s again.", - video_device_node_name(cam->v4ldev)); - return -ENOMEM; - } - - if (cam->io == IO_READ) - et61x251_empty_framequeues(cam); - else if (cam->module_param.force_munmap) - et61x251_requeue_outqueue(cam); - - cam->stream = stream; - - return 0; -} - - -static int -et61x251_vidioc_enum_framesizes(struct et61x251_device* cam, void __user * arg) -{ - struct v4l2_frmsizeenum frmsize; - - if (copy_from_user(&frmsize, arg, sizeof(frmsize))) - return -EFAULT; - - if (frmsize.index != 0) - return -EINVAL; - - if (frmsize.pixel_format != V4L2_PIX_FMT_ET61X251 && - frmsize.pixel_format != V4L2_PIX_FMT_SBGGR8) - return -EINVAL; - - frmsize.type = V4L2_FRMSIZE_TYPE_STEPWISE; - frmsize.stepwise.min_width = frmsize.stepwise.step_width = 16; - frmsize.stepwise.min_height = frmsize.stepwise.step_height = 16; - frmsize.stepwise.max_width = cam->sensor.cropcap.bounds.width; - frmsize.stepwise.max_height = cam->sensor.cropcap.bounds.height; - memset(&frmsize.reserved, 0, sizeof(frmsize.reserved)); - - if (copy_to_user(arg, &frmsize, sizeof(frmsize))) - return -EFAULT; - - return 0; -} - - -static int -et61x251_vidioc_enum_fmt(struct et61x251_device* cam, void __user * arg) -{ - struct v4l2_fmtdesc fmtd; - - if (copy_from_user(&fmtd, arg, sizeof(fmtd))) - return -EFAULT; - - if (fmtd.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if (fmtd.index == 0) { - strcpy(fmtd.description, "bayer rgb"); - fmtd.pixelformat = V4L2_PIX_FMT_SBGGR8; - } else if (fmtd.index == 1) { - strcpy(fmtd.description, "compressed"); - fmtd.pixelformat = V4L2_PIX_FMT_ET61X251; - fmtd.flags = V4L2_FMT_FLAG_COMPRESSED; - } else - return -EINVAL; - - fmtd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - memset(&fmtd.reserved, 0, sizeof(fmtd.reserved)); - - if (copy_to_user(arg, &fmtd, sizeof(fmtd))) - return -EFAULT; - - return 0; -} - - -static int -et61x251_vidioc_g_fmt(struct et61x251_device* cam, void __user * arg) -{ - struct v4l2_format format; - struct v4l2_pix_format* pfmt = &(cam->sensor.pix_format); - - if (copy_from_user(&format, arg, sizeof(format))) - return -EFAULT; - - if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - pfmt->colorspace = (pfmt->pixelformat == V4L2_PIX_FMT_ET61X251) ? - 0 : V4L2_COLORSPACE_SRGB; - pfmt->bytesperline = (pfmt->pixelformat==V4L2_PIX_FMT_ET61X251) - ? 0 : (pfmt->width * pfmt->priv) / 8; - pfmt->sizeimage = pfmt->height * ((pfmt->width*pfmt->priv)/8); - pfmt->field = V4L2_FIELD_NONE; - memcpy(&(format.fmt.pix), pfmt, sizeof(*pfmt)); - - if (copy_to_user(arg, &format, sizeof(format))) - return -EFAULT; - - return 0; -} - - -static int -et61x251_vidioc_try_s_fmt(struct et61x251_device* cam, unsigned int cmd, - void __user * arg) -{ - struct et61x251_sensor* s = &cam->sensor; - struct v4l2_format format; - struct v4l2_pix_format* pix; - struct v4l2_pix_format* pfmt = &(s->pix_format); - struct v4l2_rect* bounds = &(s->cropcap.bounds); - struct v4l2_rect rect; - u8 scale; - const enum et61x251_stream_state stream = cam->stream; - const u32 nbuffers = cam->nbuffers; - u32 i; - int err = 0; - - if (copy_from_user(&format, arg, sizeof(format))) - return -EFAULT; - - pix = &(format.fmt.pix); - - if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - memcpy(&rect, &(s->_rect), sizeof(rect)); - - { /* calculate the actual scaling factor */ - u32 a, b; - a = rect.width * rect.height; - b = pix->width * pix->height; - scale = b ? (u8)((a / b) < 4 ? 1 : 2) : 1; - } - - rect.width = scale * pix->width; - rect.height = scale * pix->height; - - if (rect.width < 16) - rect.width = 16; - if (rect.height < 16) - rect.height = 16; - if (rect.width > bounds->left + bounds->width - rect.left) - rect.width = bounds->left + bounds->width - rect.left; - if (rect.height > bounds->top + bounds->height - rect.top) - rect.height = bounds->top + bounds->height - rect.top; - - rect.width &= ~15L; - rect.height &= ~15L; - - { /* adjust the scaling factor */ - u32 a, b; - a = rect.width * rect.height; - b = pix->width * pix->height; - scale = b ? (u8)((a / b) < 4 ? 1 : 2) : 1; - } - - pix->width = rect.width / scale; - pix->height = rect.height / scale; - - if (pix->pixelformat != V4L2_PIX_FMT_ET61X251 && - pix->pixelformat != V4L2_PIX_FMT_SBGGR8) - pix->pixelformat = pfmt->pixelformat; - pix->priv = pfmt->priv; /* bpp */ - pix->colorspace = (pix->pixelformat == V4L2_PIX_FMT_ET61X251) ? - 0 : V4L2_COLORSPACE_SRGB; - pix->colorspace = pfmt->colorspace; - pix->bytesperline = (pix->pixelformat == V4L2_PIX_FMT_ET61X251) - ? 0 : (pix->width * pix->priv) / 8; - pix->sizeimage = pix->height * ((pix->width * pix->priv) / 8); - pix->field = V4L2_FIELD_NONE; - - if (cmd == VIDIOC_TRY_FMT) { - if (copy_to_user(arg, &format, sizeof(format))) - return -EFAULT; - return 0; - } - - if (cam->module_param.force_munmap) - for (i = 0; i < cam->nbuffers; i++) - if (cam->frame[i].vma_use_count) { - DBG(3, "VIDIOC_S_FMT failed. " - "Unmap the buffers first."); - return -EBUSY; - } - - if (cam->stream == STREAM_ON) - if ((err = et61x251_stream_interrupt(cam))) - return err; - - if (copy_to_user(arg, &format, sizeof(format))) { - cam->stream = stream; - return -EFAULT; - } - - if (cam->module_param.force_munmap || cam->io == IO_READ) - et61x251_release_buffers(cam); - - err += et61x251_set_pix_format(cam, pix); - err += et61x251_set_crop(cam, &rect); - if (s->set_pix_format) - err += s->set_pix_format(cam, pix); - if (s->set_crop) - err += s->set_crop(cam, &rect); - err += et61x251_set_scale(cam, scale); - - if (err) { /* atomic, no rollback in ioctl() */ - cam->state |= DEV_MISCONFIGURED; - DBG(1, "VIDIOC_S_FMT failed because of hardware problems. To " - "use the camera, close and open %s again.", - video_device_node_name(cam->v4ldev)); - return -EIO; - } - - memcpy(pfmt, pix, sizeof(*pix)); - memcpy(&(s->_rect), &rect, sizeof(rect)); - - if ((cam->module_param.force_munmap || cam->io == IO_READ) && - nbuffers != et61x251_request_buffers(cam, nbuffers, cam->io)) { - cam->state |= DEV_MISCONFIGURED; - DBG(1, "VIDIOC_S_FMT failed because of not enough memory. To " - "use the camera, close and open %s again.", - video_device_node_name(cam->v4ldev)); - return -ENOMEM; - } - - if (cam->io == IO_READ) - et61x251_empty_framequeues(cam); - else if (cam->module_param.force_munmap) - et61x251_requeue_outqueue(cam); - - cam->stream = stream; - - return 0; -} - - -static int -et61x251_vidioc_g_jpegcomp(struct et61x251_device* cam, void __user * arg) -{ - if (copy_to_user(arg, &cam->compression, - sizeof(cam->compression))) - return -EFAULT; - - return 0; -} - - -static int -et61x251_vidioc_s_jpegcomp(struct et61x251_device* cam, void __user * arg) -{ - struct v4l2_jpegcompression jc; - const enum et61x251_stream_state stream = cam->stream; - int err = 0; - - if (copy_from_user(&jc, arg, sizeof(jc))) - return -EFAULT; - - if (jc.quality != 0 && jc.quality != 1) - return -EINVAL; - - if (cam->stream == STREAM_ON) - if ((err = et61x251_stream_interrupt(cam))) - return err; - - err += et61x251_set_compression(cam, &jc); - if (err) { /* atomic, no rollback in ioctl() */ - cam->state |= DEV_MISCONFIGURED; - DBG(1, "VIDIOC_S_JPEGCOMP failed because of hardware " - "problems. To use the camera, close and open " - "%s again.", video_device_node_name(cam->v4ldev)); - return -EIO; - } - - cam->compression.quality = jc.quality; - - cam->stream = stream; - - return 0; -} - - -static int -et61x251_vidioc_reqbufs(struct et61x251_device* cam, void __user * arg) -{ - struct v4l2_requestbuffers rb; - u32 i; - int err; - - if (copy_from_user(&rb, arg, sizeof(rb))) - return -EFAULT; - - if (rb.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - rb.memory != V4L2_MEMORY_MMAP) - return -EINVAL; - - if (cam->io == IO_READ) { - DBG(3, "Close and open the device again to choose the mmap " - "I/O method"); - return -EBUSY; - } - - for (i = 0; i < cam->nbuffers; i++) - if (cam->frame[i].vma_use_count) { - DBG(3, "VIDIOC_REQBUFS failed. " - "Previous buffers are still mapped."); - return -EBUSY; - } - - if (cam->stream == STREAM_ON) - if ((err = et61x251_stream_interrupt(cam))) - return err; - - et61x251_empty_framequeues(cam); - - et61x251_release_buffers(cam); - if (rb.count) - rb.count = et61x251_request_buffers(cam, rb.count, IO_MMAP); - - if (copy_to_user(arg, &rb, sizeof(rb))) { - et61x251_release_buffers(cam); - cam->io = IO_NONE; - return -EFAULT; - } - - cam->io = rb.count ? IO_MMAP : IO_NONE; - - return 0; -} - - -static int -et61x251_vidioc_querybuf(struct et61x251_device* cam, void __user * arg) -{ - struct v4l2_buffer b; - - if (copy_from_user(&b, arg, sizeof(b))) - return -EFAULT; - - if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - b.index >= cam->nbuffers || cam->io != IO_MMAP) - return -EINVAL; - - memcpy(&b, &cam->frame[b.index].buf, sizeof(b)); - - if (cam->frame[b.index].vma_use_count) - b.flags |= V4L2_BUF_FLAG_MAPPED; - - if (cam->frame[b.index].state == F_DONE) - b.flags |= V4L2_BUF_FLAG_DONE; - else if (cam->frame[b.index].state != F_UNUSED) - b.flags |= V4L2_BUF_FLAG_QUEUED; - - if (copy_to_user(arg, &b, sizeof(b))) - return -EFAULT; - - return 0; -} - - -static int -et61x251_vidioc_qbuf(struct et61x251_device* cam, void __user * arg) -{ - struct v4l2_buffer b; - unsigned long lock_flags; - - if (copy_from_user(&b, arg, sizeof(b))) - return -EFAULT; - - if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - b.index >= cam->nbuffers || cam->io != IO_MMAP) - return -EINVAL; - - if (cam->frame[b.index].state != F_UNUSED) - return -EINVAL; - - cam->frame[b.index].state = F_QUEUED; - - spin_lock_irqsave(&cam->queue_lock, lock_flags); - list_add_tail(&cam->frame[b.index].frame, &cam->inqueue); - spin_unlock_irqrestore(&cam->queue_lock, lock_flags); - - PDBGG("Frame #%lu queued", (unsigned long)b.index); - - return 0; -} - - -static int -et61x251_vidioc_dqbuf(struct et61x251_device* cam, struct file* filp, - void __user * arg) -{ - struct v4l2_buffer b; - struct et61x251_frame_t *f; - unsigned long lock_flags; - long timeout; - - if (copy_from_user(&b, arg, sizeof(b))) - return -EFAULT; - - if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io!= IO_MMAP) - return -EINVAL; - - if (list_empty(&cam->outqueue)) { - if (cam->stream == STREAM_OFF) - return -EINVAL; - if (filp->f_flags & O_NONBLOCK) - return -EAGAIN; - timeout = wait_event_interruptible_timeout - ( cam->wait_frame, - (!list_empty(&cam->outqueue)) || - (cam->state & DEV_DISCONNECTED) || - (cam->state & DEV_MISCONFIGURED), - cam->module_param.frame_timeout * - 1000 * msecs_to_jiffies(1) ); - if (timeout < 0) - return timeout; - if (cam->state & DEV_DISCONNECTED) - return -ENODEV; - if (!timeout || (cam->state & DEV_MISCONFIGURED)) - return -EIO; - } - - spin_lock_irqsave(&cam->queue_lock, lock_flags); - f = list_entry(cam->outqueue.next, struct et61x251_frame_t, frame); - list_del(cam->outqueue.next); - spin_unlock_irqrestore(&cam->queue_lock, lock_flags); - - f->state = F_UNUSED; - - memcpy(&b, &f->buf, sizeof(b)); - if (f->vma_use_count) - b.flags |= V4L2_BUF_FLAG_MAPPED; - - if (copy_to_user(arg, &b, sizeof(b))) - return -EFAULT; - - PDBGG("Frame #%lu dequeued", (unsigned long)f->buf.index); - - return 0; -} - - -static int -et61x251_vidioc_streamon(struct et61x251_device* cam, void __user * arg) -{ - int type; - - if (copy_from_user(&type, arg, sizeof(type))) - return -EFAULT; - - if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP) - return -EINVAL; - - cam->stream = STREAM_ON; - - DBG(3, "Stream on"); - - return 0; -} - - -static int -et61x251_vidioc_streamoff(struct et61x251_device* cam, void __user * arg) -{ - int type, err; - - if (copy_from_user(&type, arg, sizeof(type))) - return -EFAULT; - - if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP) - return -EINVAL; - - if (cam->stream == STREAM_ON) - if ((err = et61x251_stream_interrupt(cam))) - return err; - - et61x251_empty_framequeues(cam); - - DBG(3, "Stream off"); - - return 0; -} - - -static int -et61x251_vidioc_g_parm(struct et61x251_device* cam, void __user * arg) -{ - struct v4l2_streamparm sp; - - if (copy_from_user(&sp, arg, sizeof(sp))) - return -EFAULT; - - if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - sp.parm.capture.extendedmode = 0; - sp.parm.capture.readbuffers = cam->nreadbuffers; - - if (copy_to_user(arg, &sp, sizeof(sp))) - return -EFAULT; - - return 0; -} - - -static int -et61x251_vidioc_s_parm(struct et61x251_device* cam, void __user * arg) -{ - struct v4l2_streamparm sp; - - if (copy_from_user(&sp, arg, sizeof(sp))) - return -EFAULT; - - if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - sp.parm.capture.extendedmode = 0; - - if (sp.parm.capture.readbuffers == 0) - sp.parm.capture.readbuffers = cam->nreadbuffers; - - if (sp.parm.capture.readbuffers > ET61X251_MAX_FRAMES) - sp.parm.capture.readbuffers = ET61X251_MAX_FRAMES; - - if (copy_to_user(arg, &sp, sizeof(sp))) - return -EFAULT; - - cam->nreadbuffers = sp.parm.capture.readbuffers; - - return 0; -} - - -static long et61x251_ioctl_v4l2(struct file *filp, - unsigned int cmd, void __user *arg) -{ - struct et61x251_device *cam = video_drvdata(filp); - - switch (cmd) { - - case VIDIOC_QUERYCAP: - return et61x251_vidioc_querycap(cam, arg); - - case VIDIOC_ENUMINPUT: - return et61x251_vidioc_enuminput(cam, arg); - - case VIDIOC_G_INPUT: - return et61x251_vidioc_g_input(cam, arg); - - case VIDIOC_S_INPUT: - return et61x251_vidioc_s_input(cam, arg); - - case VIDIOC_QUERYCTRL: - return et61x251_vidioc_query_ctrl(cam, arg); - - case VIDIOC_G_CTRL: - return et61x251_vidioc_g_ctrl(cam, arg); - - case VIDIOC_S_CTRL: - return et61x251_vidioc_s_ctrl(cam, arg); - - case VIDIOC_CROPCAP: - return et61x251_vidioc_cropcap(cam, arg); - - case VIDIOC_G_CROP: - return et61x251_vidioc_g_crop(cam, arg); - - case VIDIOC_S_CROP: - return et61x251_vidioc_s_crop(cam, arg); - - case VIDIOC_ENUM_FMT: - return et61x251_vidioc_enum_fmt(cam, arg); - - case VIDIOC_G_FMT: - return et61x251_vidioc_g_fmt(cam, arg); - - case VIDIOC_TRY_FMT: - case VIDIOC_S_FMT: - return et61x251_vidioc_try_s_fmt(cam, cmd, arg); - - case VIDIOC_ENUM_FRAMESIZES: - return et61x251_vidioc_enum_framesizes(cam, arg); - - case VIDIOC_G_JPEGCOMP: - return et61x251_vidioc_g_jpegcomp(cam, arg); - - case VIDIOC_S_JPEGCOMP: - return et61x251_vidioc_s_jpegcomp(cam, arg); - - case VIDIOC_REQBUFS: - return et61x251_vidioc_reqbufs(cam, arg); - - case VIDIOC_QUERYBUF: - return et61x251_vidioc_querybuf(cam, arg); - - case VIDIOC_QBUF: - return et61x251_vidioc_qbuf(cam, arg); - - case VIDIOC_DQBUF: - return et61x251_vidioc_dqbuf(cam, filp, arg); - - case VIDIOC_STREAMON: - return et61x251_vidioc_streamon(cam, arg); - - case VIDIOC_STREAMOFF: - return et61x251_vidioc_streamoff(cam, arg); - - case VIDIOC_G_PARM: - return et61x251_vidioc_g_parm(cam, arg); - - case VIDIOC_S_PARM: - return et61x251_vidioc_s_parm(cam, arg); - - default: - return -ENOTTY; - - } -} - - -static long et61x251_ioctl(struct file *filp, - unsigned int cmd, unsigned long arg) -{ - struct et61x251_device *cam = video_drvdata(filp); - long err = 0; - - if (mutex_lock_interruptible(&cam->fileop_mutex)) - return -ERESTARTSYS; - - if (cam->state & DEV_DISCONNECTED) { - DBG(1, "Device not present"); - mutex_unlock(&cam->fileop_mutex); - return -ENODEV; - } - - if (cam->state & DEV_MISCONFIGURED) { - DBG(1, "The camera is misconfigured. Close and open it " - "again."); - mutex_unlock(&cam->fileop_mutex); - return -EIO; - } - - V4LDBG(3, "et61x251", cmd); - - err = et61x251_ioctl_v4l2(filp, cmd, (void __user *)arg); - - mutex_unlock(&cam->fileop_mutex); - - return err; -} - - -static const struct v4l2_file_operations et61x251_fops = { - .owner = THIS_MODULE, - .open = et61x251_open, - .release = et61x251_release, - .unlocked_ioctl = et61x251_ioctl, - .read = et61x251_read, - .poll = et61x251_poll, - .mmap = et61x251_mmap, -}; - -/*****************************************************************************/ - -/* It exists a single interface only. We do not need to validate anything. */ -static int -et61x251_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) -{ - struct usb_device *udev = interface_to_usbdev(intf); - struct et61x251_device* cam; - static unsigned int dev_nr; - unsigned int i; - int err = 0; - - if (!(cam = kzalloc(sizeof(struct et61x251_device), GFP_KERNEL))) - return -ENOMEM; - - cam->usbdev = udev; - - if (!(cam->control_buffer = kzalloc(8, GFP_KERNEL))) { - DBG(1, "kmalloc() failed"); - err = -ENOMEM; - goto fail; - } - - if (!(cam->v4ldev = video_device_alloc())) { - DBG(1, "video_device_alloc() failed"); - err = -ENOMEM; - goto fail; - } - - DBG(2, "ET61X[12]51 PC Camera Controller detected " - "(vid/pid 0x%04X:0x%04X)",id->idVendor, id->idProduct); - - for (i = 0; et61x251_sensor_table[i]; i++) { - err = et61x251_sensor_table[i](cam); - if (!err) - break; - } - - if (!err) - DBG(2, "%s image sensor detected", cam->sensor.name); - else { - DBG(1, "No supported image sensor detected"); - err = -ENODEV; - goto fail; - } - - if (et61x251_init(cam)) { - DBG(1, "Initialization failed. I will retry on open()."); - cam->state |= DEV_MISCONFIGURED; - } - - strcpy(cam->v4ldev->name, "ET61X[12]51 PC Camera"); - cam->v4ldev->fops = &et61x251_fops; - cam->v4ldev->release = video_device_release; - cam->v4ldev->parent = &udev->dev; - video_set_drvdata(cam->v4ldev, cam); - - init_completion(&cam->probe); - - err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER, - video_nr[dev_nr]); - if (err) { - DBG(1, "V4L2 device registration failed"); - if (err == -ENFILE && video_nr[dev_nr] == -1) - DBG(1, "Free /dev/videoX node not found"); - video_nr[dev_nr] = -1; - dev_nr = (dev_nr < ET61X251_MAX_DEVICES-1) ? dev_nr+1 : 0; - complete_all(&cam->probe); - goto fail; - } - - DBG(2, "V4L2 device registered as %s", - video_device_node_name(cam->v4ldev)); - - cam->module_param.force_munmap = force_munmap[dev_nr]; - cam->module_param.frame_timeout = frame_timeout[dev_nr]; - - dev_nr = (dev_nr < ET61X251_MAX_DEVICES-1) ? dev_nr+1 : 0; - -#ifdef CONFIG_VIDEO_ADV_DEBUG - err = et61x251_create_sysfs(cam); - if (!err) - DBG(2, "Optional device control through 'sysfs' " - "interface ready"); - else - DBG(2, "Failed to create 'sysfs' interface for optional " - "device controlling. Error #%d", err); -#else - DBG(2, "Optional device control through 'sysfs' interface disabled"); - DBG(3, "Compile the kernel with the 'CONFIG_VIDEO_ADV_DEBUG' " - "configuration option to enable it."); -#endif - - usb_set_intfdata(intf, cam); - kref_init(&cam->kref); - usb_get_dev(cam->usbdev); - - complete_all(&cam->probe); - - return 0; - -fail: - if (cam) { - kfree(cam->control_buffer); - if (cam->v4ldev) - video_device_release(cam->v4ldev); - kfree(cam); - } - return err; -} - - -static void et61x251_usb_disconnect(struct usb_interface* intf) -{ - struct et61x251_device* cam; - - down_write(&et61x251_dev_lock); - - cam = usb_get_intfdata(intf); - - DBG(2, "Disconnecting %s...", cam->v4ldev->name); - - if (cam->users) { - DBG(2, "Device %s is open! Deregistration and memory " - "deallocation are deferred.", - video_device_node_name(cam->v4ldev)); - cam->state |= DEV_MISCONFIGURED; - et61x251_stop_transfer(cam); - cam->state |= DEV_DISCONNECTED; - wake_up_interruptible(&cam->wait_frame); - wake_up(&cam->wait_stream); - } else - cam->state |= DEV_DISCONNECTED; - - wake_up_interruptible_all(&cam->wait_open); - - kref_put(&cam->kref, et61x251_release_resources); - - up_write(&et61x251_dev_lock); -} - - -static struct usb_driver et61x251_usb_driver = { - .name = "et61x251", - .id_table = et61x251_id_table, - .probe = et61x251_usb_probe, - .disconnect = et61x251_usb_disconnect, -}; - -module_usb_driver(et61x251_usb_driver); diff --git a/drivers/media/video/et61x251/et61x251_sensor.h b/drivers/media/video/et61x251/et61x251_sensor.h deleted file mode 100644 index 71a03148cb09..000000000000 --- a/drivers/media/video/et61x251/et61x251_sensor.h +++ /dev/null @@ -1,108 +0,0 @@ -/*************************************************************************** - * API for image sensors connected to ET61X[12]51 PC Camera Controllers * - * * - * Copyright (C) 2006-2007 by Luca Risolia <luca.risolia@studio.unibo.it> * - * * - * 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, write to the Free Software * - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * - ***************************************************************************/ - -#ifndef _ET61X251_SENSOR_H_ -#define _ET61X251_SENSOR_H_ - -#include <linux/usb.h> -#include <linux/videodev2.h> -#include <linux/device.h> -#include <linux/stddef.h> -#include <linux/errno.h> -#include <asm/types.h> - -struct et61x251_device; -struct et61x251_sensor; - -/*****************************************************************************/ - -extern int et61x251_probe_tas5130d1b(struct et61x251_device* cam); - -#define ET61X251_SENSOR_TABLE \ -/* Weak detections must go at the end of the list */ \ -static int (*et61x251_sensor_table[])(struct et61x251_device*) = { \ - &et61x251_probe_tas5130d1b, \ - NULL, \ -}; - -extern struct et61x251_device* -et61x251_match_id(struct et61x251_device* cam, const struct usb_device_id *id); - -extern void -et61x251_attach_sensor(struct et61x251_device* cam, - const struct et61x251_sensor* sensor); - -/*****************************************************************************/ - -extern int et61x251_write_reg(struct et61x251_device*, u8 value, u16 index); -extern int et61x251_i2c_raw_write(struct et61x251_device*, u8 n, u8 data1, - u8 data2, u8 data3, u8 data4, u8 data5, - u8 data6, u8 data7, u8 data8, u8 address); - -/*****************************************************************************/ - -enum et61x251_i2c_sysfs_ops { - ET61X251_I2C_READ = 0x01, - ET61X251_I2C_WRITE = 0x02, -}; - -enum et61x251_i2c_interface { - ET61X251_I2C_2WIRES, - ET61X251_I2C_3WIRES, -}; - -/* Repeat start condition when RSTA is high */ -enum et61x251_i2c_rsta { - ET61X251_I2C_RSTA_STOP = 0x00, /* stop then start */ - ET61X251_I2C_RSTA_REPEAT = 0x01, /* repeat start */ -}; - -#define ET61X251_MAX_CTRLS (V4L2_CID_LASTP1-V4L2_CID_BASE+10) - -struct et61x251_sensor { - char name[32]; - - enum et61x251_i2c_sysfs_ops sysfs_ops; - - enum et61x251_i2c_interface interface; - u8 i2c_slave_id; - enum et61x251_i2c_rsta rsta; - struct v4l2_rect active_pixel; /* left and top define FVSX and FVSY */ - - struct v4l2_queryctrl qctrl[ET61X251_MAX_CTRLS]; - struct v4l2_cropcap cropcap; - struct v4l2_pix_format pix_format; - - int (*init)(struct et61x251_device* cam); - int (*get_ctrl)(struct et61x251_device* cam, - struct v4l2_control* ctrl); - int (*set_ctrl)(struct et61x251_device* cam, - const struct v4l2_control* ctrl); - int (*set_crop)(struct et61x251_device* cam, - const struct v4l2_rect* rect); - int (*set_pix_format)(struct et61x251_device* cam, - const struct v4l2_pix_format* pix); - - /* Private */ - struct v4l2_queryctrl _qctrl[ET61X251_MAX_CTRLS]; - struct v4l2_rect _rect; -}; - -#endif /* _ET61X251_SENSOR_H_ */ diff --git a/drivers/media/video/et61x251/et61x251_tas5130d1b.c b/drivers/media/video/et61x251/et61x251_tas5130d1b.c deleted file mode 100644 index ced2e167935d..000000000000 --- a/drivers/media/video/et61x251/et61x251_tas5130d1b.c +++ /dev/null @@ -1,143 +0,0 @@ -/*************************************************************************** - * Plug-in for TAS5130D1B image sensor connected to the ET61X[12]51 * - * PC Camera Controllers * - * * - * Copyright (C) 2006-2007 by Luca Risolia <luca.risolia@studio.unibo.it> * - * * - * 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, write to the Free Software * - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * - ***************************************************************************/ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include "et61x251_sensor.h" - - -static int tas5130d1b_init(struct et61x251_device* cam) -{ - int err = 0; - - err += et61x251_write_reg(cam, 0x14, 0x01); - err += et61x251_write_reg(cam, 0x1b, 0x02); - err += et61x251_write_reg(cam, 0x02, 0x12); - err += et61x251_write_reg(cam, 0x0e, 0x60); - err += et61x251_write_reg(cam, 0x80, 0x61); - err += et61x251_write_reg(cam, 0xf0, 0x62); - err += et61x251_write_reg(cam, 0x03, 0x63); - err += et61x251_write_reg(cam, 0x14, 0x64); - err += et61x251_write_reg(cam, 0xf4, 0x65); - err += et61x251_write_reg(cam, 0x01, 0x66); - err += et61x251_write_reg(cam, 0x05, 0x67); - err += et61x251_write_reg(cam, 0x8f, 0x68); - err += et61x251_write_reg(cam, 0x0f, 0x8d); - err += et61x251_write_reg(cam, 0x08, 0x8e); - - return err; -} - - -static int tas5130d1b_set_ctrl(struct et61x251_device* cam, - const struct v4l2_control* ctrl) -{ - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_GAIN: - err += et61x251_i2c_raw_write(cam, 2, 0x20, - 0xf6-ctrl->value, 0, 0, 0, - 0, 0, 0, 0); - break; - case V4L2_CID_EXPOSURE: - err += et61x251_i2c_raw_write(cam, 2, 0x40, - 0x47-ctrl->value, 0, 0, 0, - 0, 0, 0, 0); - break; - default: - return -EINVAL; - } - - return err ? -EIO : 0; -} - - -static const struct et61x251_sensor tas5130d1b = { - .name = "TAS5130D1B", - .interface = ET61X251_I2C_3WIRES, - .rsta = ET61X251_I2C_RSTA_STOP, - .active_pixel = { - .left = 106, - .top = 13, - }, - .init = &tas5130d1b_init, - .qctrl = { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "global gain", - .minimum = 0x00, - .maximum = 0xf6, - .step = 0x02, - .default_value = 0x0d, - .flags = 0, - }, - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0x00, - .maximum = 0x47, - .step = 0x01, - .default_value = 0x23, - .flags = 0, - }, - }, - .set_ctrl = &tas5130d1b_set_ctrl, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - }, - .pix_format = { - .width = 640, - .height = 480, - .pixelformat = V4L2_PIX_FMT_SBGGR8, - .priv = 8, - }, -}; - - -int et61x251_probe_tas5130d1b(struct et61x251_device* cam) -{ - const struct usb_device_id tas5130d1b_id_table[] = { - { USB_DEVICE(0x102c, 0x6251), }, - { } - }; - - /* Sensor detection is based on USB pid/vid */ - if (!et61x251_match_id(cam, tas5130d1b_id_table)) - return -ENODEV; - - et61x251_attach_sensor(cam, &tas5130d1b); - - return 0; -} diff --git a/drivers/media/video/fsl-viu.c b/drivers/media/video/fsl-viu.c index 27e3e0c0b219..777486f7cadb 100644 --- a/drivers/media/video/fsl-viu.c +++ b/drivers/media/video/fsl-viu.c @@ -1544,6 +1544,10 @@ static int __devinit viu_of_probe(struct platform_device *op) /* initialize locks */ mutex_init(&viu_dev->lock); + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &viu_dev->vdev->flags); viu_dev->vdev->lock = &viu_dev->lock; spin_lock_init(&viu_dev->slock); diff --git a/drivers/media/video/gspca/Makefile b/drivers/media/video/gspca/Makefile index 79ebe46e1ad7..c901da0bd657 100644 --- a/drivers/media/video/gspca/Makefile +++ b/drivers/media/video/gspca/Makefile @@ -43,7 +43,7 @@ obj-$(CONFIG_USB_GSPCA_VICAM) += gspca_vicam.o obj-$(CONFIG_USB_GSPCA_XIRLINK_CIT) += gspca_xirlink_cit.o obj-$(CONFIG_USB_GSPCA_ZC3XX) += gspca_zc3xx.o -gspca_main-objs := gspca.o +gspca_main-objs := gspca.o autogain_functions.o gspca_benq-objs := benq.o gspca_conex-objs := conex.o gspca_cpia1-objs := cpia1.o diff --git a/drivers/media/video/gspca/autogain_functions.c b/drivers/media/video/gspca/autogain_functions.c new file mode 100644 index 000000000000..67db674bb044 --- /dev/null +++ b/drivers/media/video/gspca/autogain_functions.c @@ -0,0 +1,178 @@ +/* + * Functions for auto gain. + * + * Copyright (C) 2010-2012 Hans de Goede <hdegoede@redhat.com> + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "gspca.h" + +/* auto gain and exposure algorithm based on the knee algorithm described here: + http://ytse.tricolour.net/docs/LowLightOptimization.html + + Returns 0 if no changes were made, 1 if the gain and or exposure settings + where changed. */ +int gspca_expo_autogain( + struct gspca_dev *gspca_dev, + int avg_lum, + int desired_avg_lum, + int deadzone, + int gain_knee, + int exposure_knee) +{ + s32 gain, orig_gain, exposure, orig_exposure; + int i, steps, retval = 0; + + if (v4l2_ctrl_g_ctrl(gspca_dev->autogain) == 0) + return 0; + + orig_gain = gain = v4l2_ctrl_g_ctrl(gspca_dev->gain); + orig_exposure = exposure = v4l2_ctrl_g_ctrl(gspca_dev->exposure); + + /* If we are of a multiple of deadzone, do multiple steps to reach the + desired lumination fast (with the risc of a slight overshoot) */ + steps = abs(desired_avg_lum - avg_lum) / deadzone; + + PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d", + avg_lum, desired_avg_lum, steps); + + for (i = 0; i < steps; i++) { + if (avg_lum > desired_avg_lum) { + if (gain > gain_knee) + gain--; + else if (exposure > exposure_knee) + exposure--; + else if (gain > gspca_dev->gain->default_value) + gain--; + else if (exposure > gspca_dev->exposure->minimum) + exposure--; + else if (gain > gspca_dev->gain->minimum) + gain--; + else + break; + } else { + if (gain < gspca_dev->gain->default_value) + gain++; + else if (exposure < exposure_knee) + exposure++; + else if (gain < gain_knee) + gain++; + else if (exposure < gspca_dev->exposure->maximum) + exposure++; + else if (gain < gspca_dev->gain->maximum) + gain++; + else + break; + } + } + + if (gain != orig_gain) { + v4l2_ctrl_s_ctrl(gspca_dev->gain, gain); + retval = 1; + } + if (exposure != orig_exposure) { + v4l2_ctrl_s_ctrl(gspca_dev->exposure, exposure); + retval = 1; + } + + if (retval) + PDEBUG(D_FRAM, "autogain: changed gain: %d, expo: %d", + gain, exposure); + return retval; +} +EXPORT_SYMBOL(gspca_expo_autogain); + +/* Autogain + exposure algorithm for cameras with a coarse exposure control + (usually this means we can only control the clockdiv to change exposure) + As changing the clockdiv so that the fps drops from 30 to 15 fps for + example, will lead to a huge exposure change (it effectively doubles), + this algorithm normally tries to only adjust the gain (between 40 and + 80 %) and if that does not help, only then changes exposure. This leads + to a much more stable image then using the knee algorithm which at + certain points of the knee graph will only try to adjust exposure, + which leads to oscilating as one exposure step is huge. + + Returns 0 if no changes were made, 1 if the gain and or exposure settings + where changed. */ +int gspca_coarse_grained_expo_autogain( + struct gspca_dev *gspca_dev, + int avg_lum, + int desired_avg_lum, + int deadzone) +{ + s32 gain_low, gain_high, gain, orig_gain, exposure, orig_exposure; + int steps, retval = 0; + + if (v4l2_ctrl_g_ctrl(gspca_dev->autogain) == 0) + return 0; + + orig_gain = gain = v4l2_ctrl_g_ctrl(gspca_dev->gain); + orig_exposure = exposure = v4l2_ctrl_g_ctrl(gspca_dev->exposure); + + gain_low = (gspca_dev->gain->maximum - gspca_dev->gain->minimum) / + 5 * 2 + gspca_dev->gain->minimum; + gain_high = (gspca_dev->gain->maximum - gspca_dev->gain->minimum) / + 5 * 4 + gspca_dev->gain->minimum; + + /* If we are of a multiple of deadzone, do multiple steps to reach the + desired lumination fast (with the risc of a slight overshoot) */ + steps = (desired_avg_lum - avg_lum) / deadzone; + + PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d", + avg_lum, desired_avg_lum, steps); + + if ((gain + steps) > gain_high && + exposure < gspca_dev->exposure->maximum) { + gain = gain_high; + gspca_dev->exp_too_low_cnt++; + gspca_dev->exp_too_high_cnt = 0; + } else if ((gain + steps) < gain_low && + exposure > gspca_dev->exposure->minimum) { + gain = gain_low; + gspca_dev->exp_too_high_cnt++; + gspca_dev->exp_too_low_cnt = 0; + } else { + gain += steps; + if (gain > gspca_dev->gain->maximum) + gain = gspca_dev->gain->maximum; + else if (gain < gspca_dev->gain->minimum) + gain = gspca_dev->gain->minimum; + gspca_dev->exp_too_high_cnt = 0; + gspca_dev->exp_too_low_cnt = 0; + } + + if (gspca_dev->exp_too_high_cnt > 3) { + exposure--; + gspca_dev->exp_too_high_cnt = 0; + } else if (gspca_dev->exp_too_low_cnt > 3) { + exposure++; + gspca_dev->exp_too_low_cnt = 0; + } + + if (gain != orig_gain) { + v4l2_ctrl_s_ctrl(gspca_dev->gain, gain); + retval = 1; + } + if (exposure != orig_exposure) { + v4l2_ctrl_s_ctrl(gspca_dev->exposure, exposure); + retval = 1; + } + + if (retval) + PDEBUG(D_FRAM, "autogain: changed gain: %d, expo: %d", + gain, exposure); + return retval; +} +EXPORT_SYMBOL(gspca_coarse_grained_expo_autogain); diff --git a/drivers/media/video/gspca/autogain_functions.h b/drivers/media/video/gspca/autogain_functions.h index 46777eee678b..d625eafe63eb 100644 --- a/drivers/media/video/gspca/autogain_functions.h +++ b/drivers/media/video/gspca/autogain_functions.h @@ -18,6 +18,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#ifdef WANT_REGULAR_AUTOGAIN /* auto gain and exposure algorithm based on the knee algorithm described here: http://ytse.tricolour.net/docs/LowLightOptimization.html @@ -91,7 +92,9 @@ static inline int auto_gain_n_exposure( gain, exposure); return retval; } +#endif +#ifdef WANT_COARSE_EXPO_AUTOGAIN /* Autogain + exposure algorithm for cameras with a coarse exposure control (usually this means we can only control the clockdiv to change exposure) As changing the clockdiv so that the fps drops from 30 to 15 fps for @@ -103,7 +106,7 @@ static inline int auto_gain_n_exposure( which leads to oscilating as one exposure step is huge. Note this assumes that the sd struct for the cam in question has - exp_too_high_cnt and exp_too_high_cnt int members for use by this function. + exp_too_low_cnt and exp_too_high_cnt int members for use by this function. Returns 0 if no changes were made, 1 if the gain and or exposure settings where changed. */ @@ -177,3 +180,4 @@ static inline int coarse_grained_expo_autogain( gain, exposure); return retval; } +#endif diff --git a/drivers/media/video/gspca/conex.c b/drivers/media/video/gspca/conex.c index ea17b5d94ea4..f39fee0fd10f 100644 --- a/drivers/media/video/gspca/conex.c +++ b/drivers/media/video/gspca/conex.c @@ -306,7 +306,7 @@ static void cx_sensor(struct gspca_dev*gspca_dev) reg_w(gspca_dev, 0x0020, reg20, 8); reg_w(gspca_dev, 0x0028, reg28, 8); - reg_w(gspca_dev, 0x0010, reg10, 8); + reg_w(gspca_dev, 0x0010, reg10, 2); reg_w_val(gspca_dev, 0x0092, 0x03); switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) { @@ -326,7 +326,7 @@ static void cx_sensor(struct gspca_dev*gspca_dev) } reg_w(gspca_dev, 0x007b, reg7b, 6); reg_w_val(gspca_dev, 0x00f8, 0x00); - reg_w(gspca_dev, 0x0010, reg10, 8); + reg_w(gspca_dev, 0x0010, reg10, 2); reg_w_val(gspca_dev, 0x0098, 0x41); for (i = 0; i < 11; i++) { if (i == 3 || i == 5 || i == 8) diff --git a/drivers/media/video/gspca/finepix.c b/drivers/media/video/gspca/finepix.c index 0107513cd728..d0befe981098 100644 --- a/drivers/media/video/gspca/finepix.c +++ b/drivers/media/video/gspca/finepix.c @@ -94,7 +94,7 @@ static void dostream(struct work_struct *work) /* loop reading a frame */ again: - while (gspca_dev->present && gspca_dev->streaming) { + while (!gspca_dev->frozen && gspca_dev->dev && gspca_dev->streaming) { /* request a frame */ mutex_lock(&gspca_dev->usb_lock); @@ -102,7 +102,8 @@ again: mutex_unlock(&gspca_dev->usb_lock); if (ret < 0) break; - if (!gspca_dev->present || !gspca_dev->streaming) + if (gspca_dev->frozen || !gspca_dev->dev || + !gspca_dev->streaming) break; /* the frame comes in parts */ @@ -117,7 +118,8 @@ again: * error. Just restart. */ goto again; } - if (!gspca_dev->present || !gspca_dev->streaming) + if (gspca_dev->frozen || !gspca_dev->dev || + !gspca_dev->streaming) goto out; if (len < FPIX_MAX_TRANSFER || (data[len - 2] == 0xff && diff --git a/drivers/media/video/gspca/gl860/gl860.c b/drivers/media/video/gspca/gl860/gl860.c index c84e26006fc3..c549574c1c7e 100644 --- a/drivers/media/video/gspca/gl860/gl860.c +++ b/drivers/media/video/gspca/gl860/gl860.c @@ -405,6 +405,9 @@ static void sd_stop0(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + if (!sd->gspca_dev.present) + return; + return sd->dev_post_unset_alt(gspca_dev); } diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index ca5a2b139d0b..137166d73945 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -38,6 +38,9 @@ #include <linux/uaccess.h> #include <linux/ktime.h> #include <media/v4l2-ioctl.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-fh.h> +#include <media/v4l2-event.h> #include "gspca.h" @@ -592,16 +595,13 @@ static int gspca_set_alt0(struct gspca_dev *gspca_dev) static void gspca_stream_off(struct gspca_dev *gspca_dev) { gspca_dev->streaming = 0; - if (gspca_dev->present) { - if (gspca_dev->sd_desc->stopN) - gspca_dev->sd_desc->stopN(gspca_dev); - destroy_urbs(gspca_dev); - gspca_input_destroy_urb(gspca_dev); - gspca_set_alt0(gspca_dev); - gspca_input_create_urb(gspca_dev); - } - - /* always call stop0 to free the subdriver's resources */ + gspca_dev->usb_err = 0; + if (gspca_dev->sd_desc->stopN) + gspca_dev->sd_desc->stopN(gspca_dev); + destroy_urbs(gspca_dev); + gspca_input_destroy_urb(gspca_dev); + gspca_set_alt0(gspca_dev); + gspca_input_create_urb(gspca_dev); if (gspca_dev->sd_desc->stop0) gspca_dev->sd_desc->stop0(gspca_dev); PDEBUG(D_STREAM, "stream off OK"); @@ -847,14 +847,6 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) struct ep_tb_s ep_tb[MAX_ALT]; int n, ret, xfer, alt, alt_idx; - if (mutex_lock_interruptible(&gspca_dev->usb_lock)) - return -ERESTARTSYS; - - if (!gspca_dev->present) { - ret = -ENODEV; - goto unlock; - } - /* reset the streaming variables */ gspca_dev->image = NULL; gspca_dev->image_len = 0; @@ -869,7 +861,7 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) if (gspca_dev->sd_desc->isoc_init) { ret = gspca_dev->sd_desc->isoc_init(gspca_dev); if (ret < 0) - goto unlock; + return ret; } xfer = gspca_dev->cam.bulk ? USB_ENDPOINT_XFER_BULK : USB_ENDPOINT_XFER_ISOC; @@ -880,8 +872,7 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) ep = alt_xfer(&intf->altsetting[gspca_dev->alt], xfer); if (ep == NULL) { pr_err("bad altsetting %d\n", gspca_dev->alt); - ret = -EIO; - goto out; + return -EIO; } ep_tb[0].alt = gspca_dev->alt; alt_idx = 1; @@ -892,8 +883,7 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) alt_idx = build_isoc_ep_tb(gspca_dev, intf, ep_tb); if (alt_idx <= 0) { pr_err("no transfer endpoint found\n"); - ret = -EIO; - goto unlock; + return -EIO; } } @@ -988,8 +978,6 @@ retry: } out: gspca_input_create_urb(gspca_dev); -unlock: - mutex_unlock(&gspca_dev->usb_lock); return ret; } @@ -1006,6 +994,8 @@ static void gspca_set_default_mode(struct gspca_dev *gspca_dev) /* set the current control values to their default values * which may have changed in sd_init() */ + /* does nothing if ctrl_handler == NULL */ + v4l2_ctrl_handler_setup(gspca_dev->vdev.ctrl_handler); ctrl = gspca_dev->cam.ctrls; if (ctrl != NULL) { for (i = 0; @@ -1057,77 +1047,50 @@ static int gspca_get_mode(struct gspca_dev *gspca_dev, static int vidioc_g_register(struct file *file, void *priv, struct v4l2_dbg_register *reg) { - int ret; - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); if (!gspca_dev->sd_desc->get_chip_ident) - return -EINVAL; + return -ENOTTY; if (!gspca_dev->sd_desc->get_register) - return -EINVAL; + return -ENOTTY; - if (mutex_lock_interruptible(&gspca_dev->usb_lock)) - return -ERESTARTSYS; gspca_dev->usb_err = 0; - if (gspca_dev->present) - ret = gspca_dev->sd_desc->get_register(gspca_dev, reg); - else - ret = -ENODEV; - mutex_unlock(&gspca_dev->usb_lock); - - return ret; + return gspca_dev->sd_desc->get_register(gspca_dev, reg); } static int vidioc_s_register(struct file *file, void *priv, struct v4l2_dbg_register *reg) { - int ret; - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); if (!gspca_dev->sd_desc->get_chip_ident) - return -EINVAL; + return -ENOTTY; if (!gspca_dev->sd_desc->set_register) - return -EINVAL; + return -ENOTTY; - if (mutex_lock_interruptible(&gspca_dev->usb_lock)) - return -ERESTARTSYS; gspca_dev->usb_err = 0; - if (gspca_dev->present) - ret = gspca_dev->sd_desc->set_register(gspca_dev, reg); - else - ret = -ENODEV; - mutex_unlock(&gspca_dev->usb_lock); - - return ret; + return gspca_dev->sd_desc->set_register(gspca_dev, reg); } #endif static int vidioc_g_chip_ident(struct file *file, void *priv, struct v4l2_dbg_chip_ident *chip) { - int ret; - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); if (!gspca_dev->sd_desc->get_chip_ident) - return -EINVAL; + return -ENOTTY; - if (mutex_lock_interruptible(&gspca_dev->usb_lock)) - return -ERESTARTSYS; gspca_dev->usb_err = 0; - if (gspca_dev->present) - ret = gspca_dev->sd_desc->get_chip_ident(gspca_dev, chip); - else - ret = -ENODEV; - mutex_unlock(&gspca_dev->usb_lock); - - return ret; + return gspca_dev->sd_desc->get_chip_ident(gspca_dev, chip); } static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *fmtdesc) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); int i, j, index; __u32 fmt_tb[8]; @@ -1169,7 +1132,7 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *fmt) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); int mode; mode = gspca_dev->curr_mode; @@ -1214,7 +1177,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *fmt) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); int ret; ret = try_fmt_vid_cap(gspca_dev, fmt); @@ -1226,7 +1189,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *fmt) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); int ret; if (mutex_lock_interruptible(&gspca_dev->queue_lock)) @@ -1265,7 +1228,7 @@ out: static int vidioc_enum_framesizes(struct file *file, void *priv, struct v4l2_frmsizeenum *fsize) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); int i; __u32 index = 0; @@ -1291,7 +1254,7 @@ static int vidioc_enum_framesizes(struct file *file, void *priv, static int vidioc_enum_frameintervals(struct file *filp, void *priv, struct v4l2_frmivalenum *fival) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(filp); int mode = wxh_to_mode(gspca_dev, fival->width, fival->height); __u32 i; @@ -1316,31 +1279,30 @@ static int vidioc_enum_frameintervals(struct file *filp, void *priv, return -EINVAL; } -static void gspca_release(struct video_device *vfd) +static void gspca_release(struct v4l2_device *v4l2_device) { - struct gspca_dev *gspca_dev = container_of(vfd, struct gspca_dev, vdev); + struct gspca_dev *gspca_dev = + container_of(v4l2_device, struct gspca_dev, v4l2_dev); PDEBUG(D_PROBE, "%s released", video_device_node_name(&gspca_dev->vdev)); + v4l2_ctrl_handler_free(gspca_dev->vdev.ctrl_handler); + v4l2_device_unregister(&gspca_dev->v4l2_dev); kfree(gspca_dev->usb_buf); kfree(gspca_dev); } static int dev_open(struct file *file) { - struct gspca_dev *gspca_dev; + struct gspca_dev *gspca_dev = video_drvdata(file); PDEBUG(D_STREAM, "[%s] open", current->comm); - gspca_dev = (struct gspca_dev *) video_devdata(file); - if (!gspca_dev->present) - return -ENODEV; /* protect the subdriver against rmmod */ if (!try_module_get(gspca_dev->module)) return -ENODEV; - file->private_data = gspca_dev; #ifdef GSPCA_DEBUG /* activate the v4l2 debug */ if (gspca_debug & D_V4L2) @@ -1350,49 +1312,44 @@ static int dev_open(struct file *file) gspca_dev->vdev.debug &= ~(V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG); #endif - return 0; + return v4l2_fh_open(file); } static int dev_close(struct file *file) { - struct gspca_dev *gspca_dev = file->private_data; + struct gspca_dev *gspca_dev = video_drvdata(file); PDEBUG(D_STREAM, "[%s] close", current->comm); - if (mutex_lock_interruptible(&gspca_dev->queue_lock)) + + /* Needed for gspca_stream_off, always lock before queue_lock! */ + if (mutex_lock_interruptible(&gspca_dev->usb_lock)) return -ERESTARTSYS; + if (mutex_lock_interruptible(&gspca_dev->queue_lock)) { + mutex_unlock(&gspca_dev->usb_lock); + return -ERESTARTSYS; + } + /* if the file did the capture, free the streaming resources */ if (gspca_dev->capt_file == file) { - if (gspca_dev->streaming) { - mutex_lock(&gspca_dev->usb_lock); - gspca_dev->usb_err = 0; + if (gspca_dev->streaming) gspca_stream_off(gspca_dev); - mutex_unlock(&gspca_dev->usb_lock); - } frame_free(gspca_dev); } - file->private_data = NULL; module_put(gspca_dev->module); mutex_unlock(&gspca_dev->queue_lock); + mutex_unlock(&gspca_dev->usb_lock); PDEBUG(D_STREAM, "close done"); - return 0; + return v4l2_fh_release(file); } static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { - struct gspca_dev *gspca_dev = priv; - int ret; + struct gspca_dev *gspca_dev = video_drvdata(file); - /* protect the access to the usb device */ - if (mutex_lock_interruptible(&gspca_dev->usb_lock)) - return -ERESTARTSYS; - if (!gspca_dev->present) { - ret = -ENODEV; - goto out; - } strlcpy((char *) cap->driver, gspca_dev->sd_desc->name, sizeof cap->driver); if (gspca_dev->dev->product != NULL) { @@ -1406,13 +1363,11 @@ static int vidioc_querycap(struct file *file, void *priv, } usb_make_path(gspca_dev->dev, (char *) cap->bus_info, sizeof(cap->bus_info)); - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; - ret = 0; -out: - mutex_unlock(&gspca_dev->usb_lock); - return ret; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + return 0; } static int get_ctrl(struct gspca_dev *gspca_dev, @@ -1435,7 +1390,7 @@ static int get_ctrl(struct gspca_dev *gspca_dev, static int vidioc_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *q_ctrl) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); const struct ctrl *ctrls; struct gspca_ctrl *gspca_ctrl; int i, idx; @@ -1478,10 +1433,10 @@ static int vidioc_queryctrl(struct file *file, void *priv, static int vidioc_s_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); const struct ctrl *ctrls; struct gspca_ctrl *gspca_ctrl; - int idx, ret; + int idx; idx = get_ctrl(gspca_dev, ctrl->id); if (idx < 0) @@ -1501,74 +1456,52 @@ static int vidioc_s_ctrl(struct file *file, void *priv, return -ERANGE; } PDEBUG(D_CONF, "set ctrl [%08x] = %d", ctrl->id, ctrl->value); - if (mutex_lock_interruptible(&gspca_dev->usb_lock)) - return -ERESTARTSYS; - if (!gspca_dev->present) { - ret = -ENODEV; - goto out; - } gspca_dev->usb_err = 0; - if (ctrls->set != NULL) { - ret = ctrls->set(gspca_dev, ctrl->value); - goto out; - } + if (ctrls->set != NULL) + return ctrls->set(gspca_dev, ctrl->value); if (gspca_ctrl != NULL) { gspca_ctrl->val = ctrl->value; if (ctrls->set_control != NULL && gspca_dev->streaming) ctrls->set_control(gspca_dev); } - ret = gspca_dev->usb_err; -out: - mutex_unlock(&gspca_dev->usb_lock); - return ret; + return gspca_dev->usb_err; } static int vidioc_g_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); const struct ctrl *ctrls; - int idx, ret; + int idx; idx = get_ctrl(gspca_dev, ctrl->id); if (idx < 0) return -EINVAL; ctrls = &gspca_dev->sd_desc->ctrls[idx]; - if (mutex_lock_interruptible(&gspca_dev->usb_lock)) - return -ERESTARTSYS; - if (!gspca_dev->present) { - ret = -ENODEV; - goto out; - } gspca_dev->usb_err = 0; - if (ctrls->get != NULL) { - ret = ctrls->get(gspca_dev, &ctrl->value); - goto out; - } + if (ctrls->get != NULL) + return ctrls->get(gspca_dev, &ctrl->value); if (gspca_dev->cam.ctrls != NULL) ctrl->value = gspca_dev->cam.ctrls[idx].val; - ret = 0; -out: - mutex_unlock(&gspca_dev->usb_lock); - return ret; + return 0; } static int vidioc_querymenu(struct file *file, void *priv, struct v4l2_querymenu *qmenu) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); if (!gspca_dev->sd_desc->querymenu) - return -EINVAL; + return -ENOTTY; return gspca_dev->sd_desc->querymenu(gspca_dev, qmenu); } static int vidioc_enum_input(struct file *file, void *priv, struct v4l2_input *input) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); if (input->index != 0) return -EINVAL; @@ -1595,7 +1528,7 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i) static int vidioc_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *rb) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); int i, ret = 0, streaming; i = rb->memory; /* (avoid compilation warning) */ @@ -1635,10 +1568,7 @@ static int vidioc_reqbufs(struct file *file, void *priv, /* stop streaming */ streaming = gspca_dev->streaming; if (streaming) { - mutex_lock(&gspca_dev->usb_lock); - gspca_dev->usb_err = 0; gspca_stream_off(gspca_dev); - mutex_unlock(&gspca_dev->usb_lock); /* Don't restart the stream when switching from read * to mmap mode */ @@ -1666,7 +1596,7 @@ out: static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *v4l2_buf) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); struct gspca_frame *frame; if (v4l2_buf->index < 0 @@ -1681,7 +1611,7 @@ static int vidioc_querybuf(struct file *file, void *priv, static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type buf_type) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); int ret; if (buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) @@ -1722,7 +1652,7 @@ out: static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type buf_type) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); int ret; if (buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) @@ -1743,13 +1673,7 @@ static int vidioc_streamoff(struct file *file, void *priv, } /* stop streaming */ - if (mutex_lock_interruptible(&gspca_dev->usb_lock)) { - ret = -ERESTARTSYS; - goto out; - } - gspca_dev->usb_err = 0; gspca_stream_off(gspca_dev); - mutex_unlock(&gspca_dev->usb_lock); /* In case another thread is waiting in dqbuf */ wake_up_interruptible(&gspca_dev->wq); @@ -1766,71 +1690,44 @@ out: static int vidioc_g_jpegcomp(struct file *file, void *priv, struct v4l2_jpegcompression *jpegcomp) { - struct gspca_dev *gspca_dev = priv; - int ret; + struct gspca_dev *gspca_dev = video_drvdata(file); if (!gspca_dev->sd_desc->get_jcomp) - return -EINVAL; - if (mutex_lock_interruptible(&gspca_dev->usb_lock)) - return -ERESTARTSYS; + return -ENOTTY; gspca_dev->usb_err = 0; - if (gspca_dev->present) - ret = gspca_dev->sd_desc->get_jcomp(gspca_dev, jpegcomp); - else - ret = -ENODEV; - mutex_unlock(&gspca_dev->usb_lock); - return ret; + return gspca_dev->sd_desc->get_jcomp(gspca_dev, jpegcomp); } static int vidioc_s_jpegcomp(struct file *file, void *priv, struct v4l2_jpegcompression *jpegcomp) { - struct gspca_dev *gspca_dev = priv; - int ret; + struct gspca_dev *gspca_dev = video_drvdata(file); if (!gspca_dev->sd_desc->set_jcomp) - return -EINVAL; - if (mutex_lock_interruptible(&gspca_dev->usb_lock)) - return -ERESTARTSYS; + return -ENOTTY; gspca_dev->usb_err = 0; - if (gspca_dev->present) - ret = gspca_dev->sd_desc->set_jcomp(gspca_dev, jpegcomp); - else - ret = -ENODEV; - mutex_unlock(&gspca_dev->usb_lock); - return ret; + return gspca_dev->sd_desc->set_jcomp(gspca_dev, jpegcomp); } static int vidioc_g_parm(struct file *filp, void *priv, struct v4l2_streamparm *parm) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(filp); parm->parm.capture.readbuffers = gspca_dev->nbufread; if (gspca_dev->sd_desc->get_streamparm) { - int ret; - - if (mutex_lock_interruptible(&gspca_dev->usb_lock)) - return -ERESTARTSYS; - if (gspca_dev->present) { - gspca_dev->usb_err = 0; - gspca_dev->sd_desc->get_streamparm(gspca_dev, parm); - ret = gspca_dev->usb_err; - } else { - ret = -ENODEV; - } - mutex_unlock(&gspca_dev->usb_lock); - return ret; + gspca_dev->usb_err = 0; + gspca_dev->sd_desc->get_streamparm(gspca_dev, parm); + return gspca_dev->usb_err; } - return 0; } static int vidioc_s_parm(struct file *filp, void *priv, struct v4l2_streamparm *parm) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(filp); int n; n = parm->parm.capture.readbuffers; @@ -1840,19 +1737,9 @@ static int vidioc_s_parm(struct file *filp, void *priv, gspca_dev->nbufread = n; if (gspca_dev->sd_desc->set_streamparm) { - int ret; - - if (mutex_lock_interruptible(&gspca_dev->usb_lock)) - return -ERESTARTSYS; - if (gspca_dev->present) { - gspca_dev->usb_err = 0; - gspca_dev->sd_desc->set_streamparm(gspca_dev, parm); - ret = gspca_dev->usb_err; - } else { - ret = -ENODEV; - } - mutex_unlock(&gspca_dev->usb_lock); - return ret; + gspca_dev->usb_err = 0; + gspca_dev->sd_desc->set_streamparm(gspca_dev, parm); + return gspca_dev->usb_err; } return 0; @@ -1860,7 +1747,7 @@ static int vidioc_s_parm(struct file *filp, void *priv, static int dev_mmap(struct file *file, struct vm_area_struct *vma) { - struct gspca_dev *gspca_dev = file->private_data; + struct gspca_dev *gspca_dev = video_drvdata(file); struct gspca_frame *frame; struct page *page; unsigned long addr, start, size; @@ -1872,10 +1759,6 @@ static int dev_mmap(struct file *file, struct vm_area_struct *vma) if (mutex_lock_interruptible(&gspca_dev->queue_lock)) return -ERESTARTSYS; - if (!gspca_dev->present) { - ret = -ENODEV; - goto out; - } if (gspca_dev->capt_file != file) { ret = -EINVAL; goto out; @@ -1963,7 +1846,7 @@ static int frame_ready(struct gspca_dev *gspca_dev, struct file *file, static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *v4l2_buf) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); struct gspca_frame *frame; int i, j, ret; @@ -2003,14 +1886,6 @@ static int vidioc_dqbuf(struct file *file, void *priv, gspca_dev->fr_o = (i + 1) % GSPCA_MAX_FRAMES; - if (gspca_dev->sd_desc->dq_callback) { - mutex_lock(&gspca_dev->usb_lock); - gspca_dev->usb_err = 0; - if (gspca_dev->present) - gspca_dev->sd_desc->dq_callback(gspca_dev); - mutex_unlock(&gspca_dev->usb_lock); - } - frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_DONE; memcpy(v4l2_buf, &frame->v4l2_buf, sizeof *v4l2_buf); PDEBUG(D_FRAM, "dqbuf %d", j); @@ -2027,6 +1902,15 @@ static int vidioc_dqbuf(struct file *file, void *priv, } out: mutex_unlock(&gspca_dev->queue_lock); + + if (ret == 0 && gspca_dev->sd_desc->dq_callback) { + mutex_lock(&gspca_dev->usb_lock); + gspca_dev->usb_err = 0; + if (gspca_dev->present) + gspca_dev->sd_desc->dq_callback(gspca_dev); + mutex_unlock(&gspca_dev->usb_lock); + } + return ret; } @@ -2039,7 +1923,7 @@ out: static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *v4l2_buf) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); struct gspca_frame *frame; int i, index, ret; @@ -2098,6 +1982,10 @@ static int read_alloc(struct gspca_dev *gspca_dev, int i, ret; PDEBUG(D_STREAM, "read alloc"); + + if (mutex_lock_interruptible(&gspca_dev->usb_lock)) + return -ERESTARTSYS; + if (gspca_dev->nframes == 0) { struct v4l2_requestbuffers rb; @@ -2108,7 +1996,7 @@ static int read_alloc(struct gspca_dev *gspca_dev, ret = vidioc_reqbufs(file, gspca_dev, &rb); if (ret != 0) { PDEBUG(D_STREAM, "read reqbuf err %d", ret); - return ret; + goto out; } memset(&v4l2_buf, 0, sizeof v4l2_buf); v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; @@ -2118,61 +2006,69 @@ static int read_alloc(struct gspca_dev *gspca_dev, ret = vidioc_qbuf(file, gspca_dev, &v4l2_buf); if (ret != 0) { PDEBUG(D_STREAM, "read qbuf err: %d", ret); - return ret; + goto out; } } - gspca_dev->memory = GSPCA_MEMORY_READ; } /* start streaming */ ret = vidioc_streamon(file, gspca_dev, V4L2_BUF_TYPE_VIDEO_CAPTURE); if (ret != 0) PDEBUG(D_STREAM, "read streamon err %d", ret); +out: + mutex_unlock(&gspca_dev->usb_lock); return ret; } static unsigned int dev_poll(struct file *file, poll_table *wait) { - struct gspca_dev *gspca_dev = file->private_data; - int ret; + struct gspca_dev *gspca_dev = video_drvdata(file); + unsigned long req_events = poll_requested_events(wait); + int ret = 0; PDEBUG(D_FRAM, "poll"); - poll_wait(file, &gspca_dev->wq, wait); + if (req_events & POLLPRI) + ret |= v4l2_ctrl_poll(file, wait); - /* if reqbufs is not done, the user would use read() */ - if (gspca_dev->memory == GSPCA_MEMORY_NO) { - ret = read_alloc(gspca_dev, file); - if (ret != 0) - return POLLERR; - } + if (req_events & (POLLIN | POLLRDNORM)) { + /* if reqbufs is not done, the user would use read() */ + if (gspca_dev->memory == GSPCA_MEMORY_NO) { + if (read_alloc(gspca_dev, file) != 0) { + ret |= POLLERR; + goto out; + } + } - if (mutex_lock_interruptible(&gspca_dev->queue_lock) != 0) - return POLLERR; + poll_wait(file, &gspca_dev->wq, wait); - /* check if an image has been received */ - if (gspca_dev->fr_o != atomic_read(&gspca_dev->fr_i)) - ret = POLLIN | POLLRDNORM; /* yes */ - else - ret = 0; - mutex_unlock(&gspca_dev->queue_lock); + /* check if an image has been received */ + if (mutex_lock_interruptible(&gspca_dev->queue_lock) != 0) { + ret |= POLLERR; + goto out; + } + if (gspca_dev->fr_o != atomic_read(&gspca_dev->fr_i)) + ret |= POLLIN | POLLRDNORM; + mutex_unlock(&gspca_dev->queue_lock); + } + +out: if (!gspca_dev->present) - return POLLHUP; + ret |= POLLHUP; + return ret; } static ssize_t dev_read(struct file *file, char __user *data, size_t count, loff_t *ppos) { - struct gspca_dev *gspca_dev = file->private_data; + struct gspca_dev *gspca_dev = video_drvdata(file); struct gspca_frame *frame; struct v4l2_buffer v4l2_buf; struct timeval timestamp; int n, ret, ret2; PDEBUG(D_FRAM, "read (%zd)", count); - if (!gspca_dev->present) - return -ENODEV; if (gspca_dev->memory == GSPCA_MEMORY_NO) { /* first time ? */ ret = read_alloc(gspca_dev, file); if (ret != 0) @@ -2266,13 +2162,15 @@ static const struct v4l2_ioctl_ops dev_ioctl_ops = { .vidioc_s_register = vidioc_s_register, #endif .vidioc_g_chip_ident = vidioc_g_chip_ident, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; static const struct video_device gspca_template = { .name = "gspca main driver", .fops = &dev_fops, .ioctl_ops = &dev_ioctl_ops, - .release = gspca_release, + .release = video_device_release_empty, /* We use v4l2_dev.release */ }; /* initialize the controls */ @@ -2344,9 +2242,24 @@ int gspca_dev_probe2(struct usb_interface *intf, } } + gspca_dev->v4l2_dev.release = gspca_release; + ret = v4l2_device_register(&intf->dev, &gspca_dev->v4l2_dev); + if (ret) + goto out; gspca_dev->sd_desc = sd_desc; gspca_dev->nbufread = 2; gspca_dev->empty_packet = -1; /* don't check the empty packets */ + gspca_dev->vdev = gspca_template; + gspca_dev->vdev.v4l2_dev = &gspca_dev->v4l2_dev; + video_set_drvdata(&gspca_dev->vdev, gspca_dev); + set_bit(V4L2_FL_USE_FH_PRIO, &gspca_dev->vdev.flags); + gspca_dev->module = module; + gspca_dev->present = 1; + + mutex_init(&gspca_dev->usb_lock); + gspca_dev->vdev.lock = &gspca_dev->usb_lock; + mutex_init(&gspca_dev->queue_lock); + init_waitqueue_head(&gspca_dev->wq); /* configure the subdriver and initialize the USB device */ ret = sd_desc->config(gspca_dev, id); @@ -2357,21 +2270,26 @@ int gspca_dev_probe2(struct usb_interface *intf, ret = sd_desc->init(gspca_dev); if (ret < 0) goto out; + if (sd_desc->init_controls) + ret = sd_desc->init_controls(gspca_dev); + if (ret < 0) + goto out; gspca_set_default_mode(gspca_dev); ret = gspca_input_connect(gspca_dev); if (ret) goto out; - mutex_init(&gspca_dev->usb_lock); - mutex_init(&gspca_dev->queue_lock); - init_waitqueue_head(&gspca_dev->wq); + /* + * Don't take usb_lock for these ioctls. This improves latency if + * usb_lock is taken for a long time, e.g. when changing a control + * value, and a new frame is ready to be dequeued. + */ + v4l2_disable_ioctl_locking(&gspca_dev->vdev, VIDIOC_DQBUF); + v4l2_disable_ioctl_locking(&gspca_dev->vdev, VIDIOC_QBUF); + v4l2_disable_ioctl_locking(&gspca_dev->vdev, VIDIOC_QUERYBUF); /* init video stuff */ - memcpy(&gspca_dev->vdev, &gspca_template, sizeof gspca_template); - gspca_dev->vdev.parent = &intf->dev; - gspca_dev->module = module; - gspca_dev->present = 1; ret = video_register_device(&gspca_dev->vdev, VFL_TYPE_GRABBER, -1); @@ -2391,6 +2309,7 @@ out: if (gspca_dev->input_dev) input_unregister_device(gspca_dev->input_dev); #endif + v4l2_ctrl_handler_free(gspca_dev->vdev.ctrl_handler); kfree(gspca_dev->usb_buf); kfree(gspca_dev); return ret; @@ -2437,11 +2356,12 @@ void gspca_disconnect(struct usb_interface *intf) PDEBUG(D_PROBE, "%s disconnect", video_device_node_name(&gspca_dev->vdev)); + mutex_lock(&gspca_dev->usb_lock); + usb_set_intfdata(intf, NULL); + gspca_dev->dev = NULL; gspca_dev->present = 0; - wake_up_interruptible(&gspca_dev->wq); - destroy_urbs(gspca_dev); #if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) @@ -2452,18 +2372,19 @@ void gspca_disconnect(struct usb_interface *intf) input_unregister_device(input_dev); } #endif + /* Free subdriver's streaming resources / stop sd workqueue(s) */ + if (gspca_dev->sd_desc->stop0 && gspca_dev->streaming) + gspca_dev->sd_desc->stop0(gspca_dev); + gspca_dev->streaming = 0; + wake_up_interruptible(&gspca_dev->wq); - /* the device is freed at exit of this function */ - gspca_dev->dev = NULL; - mutex_unlock(&gspca_dev->usb_lock); + v4l2_device_disconnect(&gspca_dev->v4l2_dev); + video_unregister_device(&gspca_dev->vdev); - usb_set_intfdata(intf, NULL); + mutex_unlock(&gspca_dev->usb_lock); - /* release the device */ /* (this will call gspca_release() immediately or on last close) */ - video_unregister_device(&gspca_dev->vdev); - -/* PDEBUG(D_PROBE, "disconnect complete"); */ + v4l2_device_put(&gspca_dev->v4l2_dev); } EXPORT_SYMBOL(gspca_disconnect); @@ -2474,7 +2395,9 @@ int gspca_suspend(struct usb_interface *intf, pm_message_t message) if (!gspca_dev->streaming) return 0; + mutex_lock(&gspca_dev->usb_lock); gspca_dev->frozen = 1; /* avoid urb error messages */ + gspca_dev->usb_err = 0; if (gspca_dev->sd_desc->stopN) gspca_dev->sd_desc->stopN(gspca_dev); destroy_urbs(gspca_dev); @@ -2482,6 +2405,7 @@ int gspca_suspend(struct usb_interface *intf, pm_message_t message) gspca_set_alt0(gspca_dev); if (gspca_dev->sd_desc->stop0) gspca_dev->sd_desc->stop0(gspca_dev); + mutex_unlock(&gspca_dev->usb_lock); return 0; } EXPORT_SYMBOL(gspca_suspend); @@ -2489,105 +2413,28 @@ EXPORT_SYMBOL(gspca_suspend); int gspca_resume(struct usb_interface *intf) { struct gspca_dev *gspca_dev = usb_get_intfdata(intf); + int streaming, ret = 0; + mutex_lock(&gspca_dev->usb_lock); gspca_dev->frozen = 0; + gspca_dev->usb_err = 0; gspca_dev->sd_desc->init(gspca_dev); gspca_input_create_urb(gspca_dev); - if (gspca_dev->streaming) - return gspca_init_transfer(gspca_dev); - return 0; + /* + * Most subdrivers send all ctrl values on sd_start and thus + * only write to the device registers on s_ctrl when streaming -> + * Clear streaming to avoid setting all ctrls twice. + */ + streaming = gspca_dev->streaming; + gspca_dev->streaming = 0; + v4l2_ctrl_handler_setup(gspca_dev->vdev.ctrl_handler); + if (streaming) + ret = gspca_init_transfer(gspca_dev); + mutex_unlock(&gspca_dev->usb_lock); + return ret; } EXPORT_SYMBOL(gspca_resume); #endif -/* -- cam driver utility functions -- */ - -/* auto gain and exposure algorithm based on the knee algorithm described here: - http://ytse.tricolour.net/docs/LowLightOptimization.html - - Returns 0 if no changes were made, 1 if the gain and or exposure settings - where changed. */ -int gspca_auto_gain_n_exposure(struct gspca_dev *gspca_dev, int avg_lum, - int desired_avg_lum, int deadzone, int gain_knee, int exposure_knee) -{ - int i, steps, gain, orig_gain, exposure, orig_exposure, autogain; - const struct ctrl *gain_ctrl = NULL; - const struct ctrl *exposure_ctrl = NULL; - const struct ctrl *autogain_ctrl = NULL; - int retval = 0; - - for (i = 0; i < gspca_dev->sd_desc->nctrls; i++) { - if (gspca_dev->ctrl_dis & (1 << i)) - continue; - if (gspca_dev->sd_desc->ctrls[i].qctrl.id == V4L2_CID_GAIN) - gain_ctrl = &gspca_dev->sd_desc->ctrls[i]; - if (gspca_dev->sd_desc->ctrls[i].qctrl.id == V4L2_CID_EXPOSURE) - exposure_ctrl = &gspca_dev->sd_desc->ctrls[i]; - if (gspca_dev->sd_desc->ctrls[i].qctrl.id == V4L2_CID_AUTOGAIN) - autogain_ctrl = &gspca_dev->sd_desc->ctrls[i]; - } - if (!gain_ctrl || !exposure_ctrl || !autogain_ctrl) { - PDEBUG(D_ERR, "Error: gspca_auto_gain_n_exposure called " - "on cam without (auto)gain/exposure"); - return 0; - } - - if (gain_ctrl->get(gspca_dev, &gain) || - exposure_ctrl->get(gspca_dev, &exposure) || - autogain_ctrl->get(gspca_dev, &autogain) || !autogain) - return 0; - - orig_gain = gain; - orig_exposure = exposure; - - /* If we are of a multiple of deadzone, do multiple steps to reach the - desired lumination fast (with the risc of a slight overshoot) */ - steps = abs(desired_avg_lum - avg_lum) / deadzone; - - PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d", - avg_lum, desired_avg_lum, steps); - - for (i = 0; i < steps; i++) { - if (avg_lum > desired_avg_lum) { - if (gain > gain_knee) - gain--; - else if (exposure > exposure_knee) - exposure--; - else if (gain > gain_ctrl->qctrl.default_value) - gain--; - else if (exposure > exposure_ctrl->qctrl.minimum) - exposure--; - else if (gain > gain_ctrl->qctrl.minimum) - gain--; - else - break; - } else { - if (gain < gain_ctrl->qctrl.default_value) - gain++; - else if (exposure < exposure_knee) - exposure++; - else if (gain < gain_knee) - gain++; - else if (exposure < exposure_ctrl->qctrl.maximum) - exposure++; - else if (gain < gain_ctrl->qctrl.maximum) - gain++; - else - break; - } - } - - if (gain != orig_gain) { - gain_ctrl->set(gspca_dev, gain); - retval = 1; - } - if (exposure != orig_exposure) { - exposure_ctrl->set(gspca_dev, exposure); - retval = 1; - } - - return retval; -} -EXPORT_SYMBOL(gspca_auto_gain_n_exposure); /* -- module insert / remove -- */ static int __init gspca_init(void) diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h index 589009f4496f..dc688c7f5e48 100644 --- a/drivers/media/video/gspca/gspca.h +++ b/drivers/media/video/gspca/gspca.h @@ -6,6 +6,8 @@ #include <linux/usb.h> #include <linux/videodev2.h> #include <media/v4l2-common.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> #include <linux/mutex.h> /* compilation option */ @@ -115,6 +117,7 @@ struct sd_desc { /* mandatory operations */ cam_cf_op config; /* called on probe */ cam_op init; /* called on probe and resume */ + cam_op init_controls; /* called on probe */ cam_op start; /* called on stream on after URBs creation */ cam_pkt_op pkt_scan; /* optional operations */ @@ -158,8 +161,10 @@ struct gspca_frame { struct gspca_dev { struct video_device vdev; /* !! must be the first item */ struct module *module; /* subdriver handling the device */ + struct v4l2_device v4l2_dev; struct usb_device *dev; struct file *capt_file; /* file doing video capture */ + /* protected by queue_lock */ #if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) struct input_dev *input_dev; char phys[64]; /* physical device path */ @@ -169,6 +174,16 @@ struct gspca_dev { const struct sd_desc *sd_desc; /* subdriver description */ unsigned ctrl_dis; /* disabled controls (bit map) */ unsigned ctrl_inac; /* inactive controls (bit map) */ + struct v4l2_ctrl_handler ctrl_handler; + + /* autogain and exposure or gain control cluster, these are global as + the autogain/exposure functions in autogain_functions.c use them */ + struct { + struct v4l2_ctrl *autogain; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *gain; + int exp_too_low_cnt, exp_too_high_cnt; + }; #define USB_BUF_SZ 64 __u8 *usb_buf; /* buffer for USB exchanges */ @@ -189,7 +204,7 @@ struct gspca_dev { u8 fr_o; /* next frame to dequeue */ __u8 last_packet_type; __s8 empty_packet; /* if (-1) don't check empty packets */ - __u8 streaming; + __u8 streaming; /* protected by both mutexes (*) */ __u8 curr_mode; /* current camera mode */ __u32 pixfmt; /* current mode parameters */ @@ -211,6 +226,10 @@ struct gspca_dev { __u8 iface; /* USB interface number */ __u8 alt; /* USB alternate setting */ u8 audio; /* presence of audio device */ + + /* (*) These variables are proteced by both usb_lock and queue_lock, + that is any code setting them is holding *both*, which means that + any code getting them needs to hold at least one of them */ }; int gspca_dev_probe(struct usb_interface *intf, @@ -232,6 +251,9 @@ void gspca_frame_add(struct gspca_dev *gspca_dev, int gspca_suspend(struct usb_interface *intf, pm_message_t message); int gspca_resume(struct usb_interface *intf); #endif -int gspca_auto_gain_n_exposure(struct gspca_dev *gspca_dev, int avg_lum, +int gspca_expo_autogain(struct gspca_dev *gspca_dev, int avg_lum, int desired_avg_lum, int deadzone, int gain_knee, int exposure_knee); +int gspca_coarse_grained_expo_autogain(struct gspca_dev *gspca_dev, + int avg_lum, int desired_avg_lum, int deadzone); + #endif /* GSPCAV2_H */ diff --git a/drivers/media/video/gspca/jl2005bcd.c b/drivers/media/video/gspca/jl2005bcd.c index 53f58ef367cf..e1fc2561e4bc 100644 --- a/drivers/media/video/gspca/jl2005bcd.c +++ b/drivers/media/video/gspca/jl2005bcd.c @@ -335,7 +335,7 @@ static void jl2005c_dostream(struct work_struct *work) goto quit_stream; } - while (gspca_dev->present && gspca_dev->streaming) { + while (!gspca_dev->frozen && gspca_dev->dev && gspca_dev->streaming) { /* Check if this is a new frame. If so, start the frame first */ if (!header_read) { mutex_lock(&gspca_dev->usb_lock); @@ -367,7 +367,7 @@ static void jl2005c_dostream(struct work_struct *work) buffer, act_len); header_read = 1; } - while (bytes_left > 0 && gspca_dev->present) { + while (bytes_left > 0 && gspca_dev->dev) { data_len = bytes_left > JL2005C_MAX_TRANSFER ? JL2005C_MAX_TRANSFER : bytes_left; ret = usb_bulk_msg(gspca_dev->dev, @@ -390,7 +390,7 @@ static void jl2005c_dostream(struct work_struct *work) } } quit_stream: - if (gspca_dev->present) { + if (gspca_dev->dev) { mutex_lock(&gspca_dev->usb_lock); jl2005c_stop(gspca_dev); mutex_unlock(&gspca_dev->usb_lock); diff --git a/drivers/media/video/gspca/mars.c b/drivers/media/video/gspca/mars.c index b0231465afae..ec7b21ee79fb 100644 --- a/drivers/media/video/gspca/mars.c +++ b/drivers/media/video/gspca/mars.c @@ -30,22 +30,19 @@ MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>"); MODULE_DESCRIPTION("GSPCA/Mars USB Camera Driver"); MODULE_LICENSE("GPL"); -/* controls */ -enum e_ctrl { - BRIGHTNESS, - COLORS, - GAMMA, - SHARPNESS, - ILLUM_TOP, - ILLUM_BOT, - NCTRLS /* number of controls */ -}; - /* specific webcam descriptor */ struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - struct gspca_ctrl ctrls[NCTRLS]; + struct v4l2_ctrl *brightness; + struct v4l2_ctrl *saturation; + struct v4l2_ctrl *sharpness; + struct v4l2_ctrl *gamma; + struct { /* illuminator control cluster */ + struct v4l2_ctrl *illum_top; + struct v4l2_ctrl *illum_bottom; + }; + struct v4l2_ctrl *jpegqual; u8 quality; #define QUALITY_MIN 40 @@ -56,89 +53,10 @@ struct sd { }; /* V4L2 controls supported by the driver */ -static void setbrightness(struct gspca_dev *gspca_dev); -static void setcolors(struct gspca_dev *gspca_dev); -static void setgamma(struct gspca_dev *gspca_dev); -static void setsharpness(struct gspca_dev *gspca_dev); -static int sd_setilluminator1(struct gspca_dev *gspca_dev, __s32 val); -static int sd_setilluminator2(struct gspca_dev *gspca_dev, __s32 val); - -static const struct ctrl sd_ctrls[NCTRLS] = { -[BRIGHTNESS] = { - { - .id = V4L2_CID_BRIGHTNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Brightness", - .minimum = 0, - .maximum = 30, - .step = 1, - .default_value = 15, - }, - .set_control = setbrightness - }, -[COLORS] = { - { - .id = V4L2_CID_SATURATION, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Color", - .minimum = 1, - .maximum = 255, - .step = 1, - .default_value = 200, - }, - .set_control = setcolors - }, -[GAMMA] = { - { - .id = V4L2_CID_GAMMA, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gamma", - .minimum = 0, - .maximum = 3, - .step = 1, - .default_value = 1, - }, - .set_control = setgamma - }, -[SHARPNESS] = { - { - .id = V4L2_CID_SHARPNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Sharpness", - .minimum = 0, - .maximum = 2, - .step = 1, - .default_value = 1, - }, - .set_control = setsharpness - }, -[ILLUM_TOP] = { - { - .id = V4L2_CID_ILLUMINATORS_1, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Top illuminator", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - .flags = V4L2_CTRL_FLAG_UPDATE, - }, - .set = sd_setilluminator1 - }, -[ILLUM_BOT] = { - { - .id = V4L2_CID_ILLUMINATORS_2, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Bottom illuminator", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - .flags = V4L2_CTRL_FLAG_UPDATE, - }, - .set = sd_setilluminator2 - }, -}; +static void setbrightness(struct gspca_dev *gspca_dev, s32 val); +static void setcolors(struct gspca_dev *gspca_dev, s32 val); +static void setgamma(struct gspca_dev *gspca_dev, s32 val); +static void setsharpness(struct gspca_dev *gspca_dev, s32 val); static const struct v4l2_pix_format vga_mode[] = { {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, @@ -198,59 +116,130 @@ static void mi_w(struct gspca_dev *gspca_dev, reg_w(gspca_dev, 4); } -static void setbrightness(struct gspca_dev *gspca_dev) +static void setbrightness(struct gspca_dev *gspca_dev, s32 val) { - struct sd *sd = (struct sd *) gspca_dev; - gspca_dev->usb_buf[0] = 0x61; - gspca_dev->usb_buf[1] = sd->ctrls[BRIGHTNESS].val; + gspca_dev->usb_buf[1] = val; reg_w(gspca_dev, 2); } -static void setcolors(struct gspca_dev *gspca_dev) +static void setcolors(struct gspca_dev *gspca_dev, s32 val) { - struct sd *sd = (struct sd *) gspca_dev; - s16 val; - - val = sd->ctrls[COLORS].val; gspca_dev->usb_buf[0] = 0x5f; gspca_dev->usb_buf[1] = val << 3; gspca_dev->usb_buf[2] = ((val >> 2) & 0xf8) | 0x04; reg_w(gspca_dev, 3); } -static void setgamma(struct gspca_dev *gspca_dev) +static void setgamma(struct gspca_dev *gspca_dev, s32 val) { - struct sd *sd = (struct sd *) gspca_dev; - gspca_dev->usb_buf[0] = 0x06; - gspca_dev->usb_buf[1] = sd->ctrls[GAMMA].val * 0x40; + gspca_dev->usb_buf[1] = val * 0x40; reg_w(gspca_dev, 2); } -static void setsharpness(struct gspca_dev *gspca_dev) +static void setsharpness(struct gspca_dev *gspca_dev, s32 val) { - struct sd *sd = (struct sd *) gspca_dev; - gspca_dev->usb_buf[0] = 0x67; - gspca_dev->usb_buf[1] = sd->ctrls[SHARPNESS].val * 4 + 3; + gspca_dev->usb_buf[1] = val * 4 + 3; reg_w(gspca_dev, 2); } -static void setilluminators(struct gspca_dev *gspca_dev) +static void setilluminators(struct gspca_dev *gspca_dev, bool top, bool bottom) { - struct sd *sd = (struct sd *) gspca_dev; - + /* both are off if not streaming */ gspca_dev->usb_buf[0] = 0x22; - if (sd->ctrls[ILLUM_TOP].val) + if (top) gspca_dev->usb_buf[1] = 0x76; - else if (sd->ctrls[ILLUM_BOT].val) + else if (bottom) gspca_dev->usb_buf[1] = 0x7a; else gspca_dev->usb_buf[1] = 0x7e; reg_w(gspca_dev, 2); } +static int mars_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + struct sd *sd = (struct sd *)gspca_dev; + + gspca_dev->usb_err = 0; + + if (ctrl->id == V4L2_CID_ILLUMINATORS_1) { + /* only one can be on at a time */ + if (ctrl->is_new && ctrl->val) + sd->illum_bottom->val = 0; + if (sd->illum_bottom->is_new && sd->illum_bottom->val) + sd->illum_top->val = 0; + } + + if (!gspca_dev->streaming) + return 0; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + setbrightness(gspca_dev, ctrl->val); + break; + case V4L2_CID_SATURATION: + setcolors(gspca_dev, ctrl->val); + break; + case V4L2_CID_GAMMA: + setgamma(gspca_dev, ctrl->val); + break; + case V4L2_CID_ILLUMINATORS_1: + setilluminators(gspca_dev, sd->illum_top->val, + sd->illum_bottom->val); + break; + case V4L2_CID_SHARPNESS: + setsharpness(gspca_dev, ctrl->val); + break; + case V4L2_CID_JPEG_COMPRESSION_QUALITY: + jpeg_set_qual(sd->jpeg_hdr, ctrl->val); + break; + default: + return -EINVAL; + } + return gspca_dev->usb_err; +} + +static const struct v4l2_ctrl_ops mars_ctrl_ops = { + .s_ctrl = mars_s_ctrl, +}; + +/* this function is called at probe time */ +static int sd_init_controls(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; + + gspca_dev->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 7); + sd->brightness = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 30, 1, 15); + sd->saturation = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops, + V4L2_CID_SATURATION, 0, 255, 1, 200); + sd->gamma = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops, + V4L2_CID_GAMMA, 0, 3, 1, 1); + sd->sharpness = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops, + V4L2_CID_SHARPNESS, 0, 2, 1, 1); + sd->illum_top = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops, + V4L2_CID_ILLUMINATORS_1, 0, 1, 1, 0); + sd->illum_top->flags |= V4L2_CTRL_FLAG_UPDATE; + sd->illum_bottom = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops, + V4L2_CID_ILLUMINATORS_2, 0, 1, 1, 0); + sd->illum_bottom->flags |= V4L2_CTRL_FLAG_UPDATE; + sd->jpegqual = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops, + V4L2_CID_JPEG_COMPRESSION_QUALITY, + QUALITY_MIN, QUALITY_MAX, 1, QUALITY_DEF); + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } + v4l2_ctrl_cluster(2, &sd->illum_top); + return 0; +} + /* this function is called at probe time */ static int sd_config(struct gspca_dev *gspca_dev, const struct usb_device_id *id) @@ -261,7 +250,6 @@ static int sd_config(struct gspca_dev *gspca_dev, cam = &gspca_dev->cam; cam->cam_mode = vga_mode; cam->nmodes = ARRAY_SIZE(vga_mode); - cam->ctrls = sd->ctrls; sd->quality = QUALITY_DEF; return 0; } @@ -269,7 +257,6 @@ static int sd_config(struct gspca_dev *gspca_dev, /* this function is called at probe and resume time */ static int sd_init(struct gspca_dev *gspca_dev) { - gspca_dev->ctrl_inac = (1 << ILLUM_TOP) | (1 << ILLUM_BOT); return 0; } @@ -282,7 +269,7 @@ static int sd_start(struct gspca_dev *gspca_dev) /* create the JPEG header */ jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, 0x21); /* JPEG 422 */ - jpeg_set_qual(sd->jpeg_hdr, sd->quality); + jpeg_set_qual(sd->jpeg_hdr, v4l2_ctrl_g_ctrl(sd->jpegqual)); data = gspca_dev->usb_buf; @@ -301,7 +288,7 @@ static int sd_start(struct gspca_dev *gspca_dev) data[5] = 0x30; /* reg 4, MI, PAS5101 : * 0x30 for 24mhz , 0x28 for 12mhz */ data[6] = 0x02; /* reg 5, H start - was 0x04 */ - data[7] = sd->ctrls[GAMMA].val * 0x40; /* reg 0x06: gamma */ + data[7] = v4l2_ctrl_g_ctrl(sd->gamma) * 0x40; /* reg 0x06: gamma */ data[8] = 0x01; /* reg 7, V start - was 0x03 */ /* if (h_size == 320 ) */ /* data[9]= 0x56; * reg 8, 24MHz, 2:1 scale down */ @@ -333,16 +320,16 @@ static int sd_start(struct gspca_dev *gspca_dev) /* reg 0x5f/0x60 (LE) = saturation */ /* h (60): xxxx x100 * l (5f): xxxx x000 */ - data[2] = sd->ctrls[COLORS].val << 3; - data[3] = ((sd->ctrls[COLORS].val >> 2) & 0xf8) | 0x04; - data[4] = sd->ctrls[BRIGHTNESS].val; /* reg 0x61 = brightness */ + data[2] = v4l2_ctrl_g_ctrl(sd->saturation) << 3; + data[3] = ((v4l2_ctrl_g_ctrl(sd->saturation) >> 2) & 0xf8) | 0x04; + data[4] = v4l2_ctrl_g_ctrl(sd->brightness); /* reg 0x61 = brightness */ data[5] = 0x00; reg_w(gspca_dev, 6); data[0] = 0x67; /*jfm: from win trace*/ - data[1] = sd->ctrls[SHARPNESS].val * 4 + 3; + data[1] = v4l2_ctrl_g_ctrl(sd->sharpness) * 4 + 3; data[2] = 0x14; reg_w(gspca_dev, 3); @@ -365,7 +352,9 @@ static int sd_start(struct gspca_dev *gspca_dev) data[1] = 0x4d; /* ISOC transferring enable... */ reg_w(gspca_dev, 2); - gspca_dev->ctrl_inac = 0; /* activate the illuminator controls */ + setilluminators(gspca_dev, v4l2_ctrl_g_ctrl(sd->illum_top), + v4l2_ctrl_g_ctrl(sd->illum_bottom)); + return gspca_dev->usb_err; } @@ -373,11 +362,9 @@ static void sd_stopN(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - gspca_dev->ctrl_inac = (1 << ILLUM_TOP) | (1 << ILLUM_BOT); - if (sd->ctrls[ILLUM_TOP].val || sd->ctrls[ILLUM_BOT].val) { - sd->ctrls[ILLUM_TOP].val = 0; - sd->ctrls[ILLUM_BOT].val = 0; - setilluminators(gspca_dev); + if (v4l2_ctrl_g_ctrl(sd->illum_top) || + v4l2_ctrl_g_ctrl(sd->illum_bottom)) { + setilluminators(gspca_dev, false, false); msleep(20); } @@ -424,43 +411,16 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } -static int sd_setilluminator1(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - /* only one illuminator may be on */ - sd->ctrls[ILLUM_TOP].val = val; - if (val) - sd->ctrls[ILLUM_BOT].val = 0; - setilluminators(gspca_dev); - return gspca_dev->usb_err; -} - -static int sd_setilluminator2(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - /* only one illuminator may be on */ - sd->ctrls[ILLUM_BOT].val = val; - if (val) - sd->ctrls[ILLUM_TOP].val = 0; - setilluminators(gspca_dev); - return gspca_dev->usb_err; -} - static int sd_set_jcomp(struct gspca_dev *gspca_dev, struct v4l2_jpegcompression *jcomp) { struct sd *sd = (struct sd *) gspca_dev; + int ret; - if (jcomp->quality < QUALITY_MIN) - sd->quality = QUALITY_MIN; - else if (jcomp->quality > QUALITY_MAX) - sd->quality = QUALITY_MAX; - else - sd->quality = jcomp->quality; - if (gspca_dev->streaming) - jpeg_set_qual(sd->jpeg_hdr, sd->quality); + ret = v4l2_ctrl_s_ctrl(sd->jpegqual, jcomp->quality); + if (ret) + return ret; + jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual); return 0; } @@ -470,7 +430,7 @@ static int sd_get_jcomp(struct gspca_dev *gspca_dev, struct sd *sd = (struct sd *) gspca_dev; memset(jcomp, 0, sizeof *jcomp); - jcomp->quality = sd->quality; + jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual); jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT | V4L2_JPEG_MARKER_DQT; return 0; @@ -479,10 +439,9 @@ static int sd_get_jcomp(struct gspca_dev *gspca_dev, /* sub-driver description */ static const struct sd_desc sd_desc = { .name = MODULE_NAME, - .ctrls = sd_ctrls, - .nctrls = NCTRLS, .config = sd_config, .init = sd_init, + .init_controls = sd_init_controls, .start = sd_start, .stopN = sd_stopN, .pkt_scan = sd_pkt_scan, @@ -513,6 +472,7 @@ static struct usb_driver sd_driver = { #ifdef CONFIG_PM .suspend = gspca_suspend, .resume = gspca_resume, + .reset_resume = gspca_resume, #endif }; diff --git a/drivers/media/video/gspca/nw80x.c b/drivers/media/video/gspca/nw80x.c index 7167cac7359c..42e021931e60 100644 --- a/drivers/media/video/gspca/nw80x.c +++ b/drivers/media/video/gspca/nw80x.c @@ -2001,6 +2001,8 @@ static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) return gspca_dev->usb_err; } +#define WANT_REGULAR_AUTOGAIN +#define WANT_COARSE_EXPO_AUTOGAIN #include "autogain_functions.h" static void do_autogain(struct gspca_dev *gspca_dev) diff --git a/drivers/media/video/gspca/ov519.c b/drivers/media/video/gspca/ov519.c index 739e8a2a2d30..183457c5cfdb 100644 --- a/drivers/media/video/gspca/ov519.c +++ b/drivers/media/video/gspca/ov519.c @@ -2804,7 +2804,7 @@ static void ov7xx0_configure(struct sd *sd) /* add OV7670 here * it appears to be wrongly detected as a 7610 by default */ if (rc < 0) { - PDEBUG(D_ERR, "Error detecting sensor type"); + pr_err("Error detecting sensor type\n"); return; } if ((rc & 3) == 3) { @@ -2832,12 +2832,12 @@ static void ov7xx0_configure(struct sd *sd) /* try to read product id registers */ high = i2c_r(sd, 0x0a); if (high < 0) { - PDEBUG(D_ERR, "Error detecting camera chip PID"); + pr_err("Error detecting camera chip PID\n"); return; } low = i2c_r(sd, 0x0b); if (low < 0) { - PDEBUG(D_ERR, "Error detecting camera chip VER"); + pr_err("Error detecting camera chip VER\n"); return; } if (high == 0x76) { @@ -2863,7 +2863,7 @@ static void ov7xx0_configure(struct sd *sd) sd->sensor = SEN_OV7660; break; default: - PDEBUG(D_PROBE, "Unknown sensor: 0x76%x", low); + pr_err("Unknown sensor: 0x76%02x\n", low); return; } } else { @@ -2884,7 +2884,7 @@ static void ov6xx0_configure(struct sd *sd) /* Detect sensor (sub)type */ rc = i2c_r(sd, OV7610_REG_COM_I); if (rc < 0) { - PDEBUG(D_ERR, "Error detecting sensor type"); + pr_err("Error detecting sensor type\n"); return; } diff --git a/drivers/media/video/gspca/ov534.c b/drivers/media/video/gspca/ov534.c index 04753391de3e..b5acb1e4b4e7 100644 --- a/drivers/media/video/gspca/ov534.c +++ b/drivers/media/video/gspca/ov534.c @@ -34,6 +34,8 @@ #include "gspca.h" +#include <linux/fixp-arith.h> + #define OV534_REG_ADDRESS 0xf1 /* sensor address */ #define OV534_REG_SUBADDR 0xf2 #define OV534_REG_WRITE 0xf3 @@ -53,6 +55,8 @@ MODULE_LICENSE("GPL"); /* controls */ enum e_ctrl { + HUE, + SATURATION, BRIGHTNESS, CONTRAST, GAIN, @@ -63,7 +67,6 @@ enum e_ctrl { SHARPNESS, HFLIP, VFLIP, - COLORS, LIGHTFREQ, NCTRLS /* number of controls */ }; @@ -87,6 +90,8 @@ enum sensors { }; /* V4L2 controls supported by the driver */ +static void sethue(struct gspca_dev *gspca_dev); +static void setsaturation(struct gspca_dev *gspca_dev); static void setbrightness(struct gspca_dev *gspca_dev); static void setcontrast(struct gspca_dev *gspca_dev); static void setgain(struct gspca_dev *gspca_dev); @@ -96,13 +101,36 @@ static void setawb(struct gspca_dev *gspca_dev); static void setaec(struct gspca_dev *gspca_dev); static void setsharpness(struct gspca_dev *gspca_dev); static void sethvflip(struct gspca_dev *gspca_dev); -static void setcolors(struct gspca_dev *gspca_dev); static void setlightfreq(struct gspca_dev *gspca_dev); static int sd_start(struct gspca_dev *gspca_dev); static void sd_stopN(struct gspca_dev *gspca_dev); static const struct ctrl sd_ctrls[] = { +[HUE] = { + { + .id = V4L2_CID_HUE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Hue", + .minimum = -90, + .maximum = 90, + .step = 1, + .default_value = 0, + }, + .set_control = sethue + }, +[SATURATION] = { + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Saturation", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 64, + }, + .set_control = setsaturation + }, [BRIGHTNESS] = { { .id = V4L2_CID_BRIGHTNESS, @@ -223,18 +251,6 @@ static const struct ctrl sd_ctrls[] = { }, .set_control = sethvflip }, -[COLORS] = { - { - .id = V4L2_CID_SATURATION, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Saturation", - .minimum = 0, - .maximum = 6, - .step = 1, - .default_value = 3, - }, - .set_control = setcolors - }, [LIGHTFREQ] = { { .id = V4L2_CID_POWER_LINE_FREQUENCY, @@ -684,7 +700,7 @@ static const u8 sensor_init_772x[][2] = { { 0x9c, 0x20 }, { 0x9e, 0x81 }, - { 0xa6, 0x04 }, + { 0xa6, 0x07 }, { 0x7e, 0x0c }, { 0x7f, 0x16 }, { 0x80, 0x2a }, @@ -955,6 +971,74 @@ static void set_frame_rate(struct gspca_dev *gspca_dev) PDEBUG(D_PROBE, "frame_rate: %d", r->fps); } +static void sethue(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int val; + + val = sd->ctrls[HUE].val; + if (sd->sensor == SENSOR_OV767x) { + /* TBD */ + } else { + s16 huesin; + s16 huecos; + + /* fixp_sin and fixp_cos accept only positive values, while + * our val is between -90 and 90 + */ + val += 360; + + /* According to the datasheet the registers expect HUESIN and + * HUECOS to be the result of the trigonometric functions, + * scaled by 0x80. + * + * The 0x100 here represents the maximun absolute value + * returned byt fixp_sin and fixp_cos, so the scaling will + * consider the result like in the interval [-1.0, 1.0]. + */ + huesin = fixp_sin(val) * 0x80 / 0x100; + huecos = fixp_cos(val) * 0x80 / 0x100; + + if (huesin < 0) { + sccb_reg_write(gspca_dev, 0xab, + sccb_reg_read(gspca_dev, 0xab) | 0x2); + huesin = -huesin; + } else { + sccb_reg_write(gspca_dev, 0xab, + sccb_reg_read(gspca_dev, 0xab) & ~0x2); + + } + sccb_reg_write(gspca_dev, 0xa9, (u8)huecos); + sccb_reg_write(gspca_dev, 0xaa, (u8)huesin); + } +} + +static void setsaturation(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int val; + + val = sd->ctrls[SATURATION].val; + if (sd->sensor == SENSOR_OV767x) { + int i; + static u8 color_tb[][6] = { + {0x42, 0x42, 0x00, 0x11, 0x30, 0x41}, + {0x52, 0x52, 0x00, 0x16, 0x3c, 0x52}, + {0x66, 0x66, 0x00, 0x1b, 0x4b, 0x66}, + {0x80, 0x80, 0x00, 0x22, 0x5e, 0x80}, + {0x9a, 0x9a, 0x00, 0x29, 0x71, 0x9a}, + {0xb8, 0xb8, 0x00, 0x31, 0x87, 0xb8}, + {0xdd, 0xdd, 0x00, 0x3b, 0xa2, 0xdd}, + }; + + for (i = 0; i < ARRAY_SIZE(color_tb[0]); i++) + sccb_reg_write(gspca_dev, 0x4f + i, color_tb[val][i]); + } else { + sccb_reg_write(gspca_dev, 0xa7, val); /* U saturation */ + sccb_reg_write(gspca_dev, 0xa8, val); /* V saturation */ + } +} + static void setbrightness(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -1132,26 +1216,6 @@ static void sethvflip(struct gspca_dev *gspca_dev) } } -static void setcolors(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - u8 val; - int i; - static u8 color_tb[][6] = { - {0x42, 0x42, 0x00, 0x11, 0x30, 0x41}, - {0x52, 0x52, 0x00, 0x16, 0x3c, 0x52}, - {0x66, 0x66, 0x00, 0x1b, 0x4b, 0x66}, - {0x80, 0x80, 0x00, 0x22, 0x5e, 0x80}, - {0x9a, 0x9a, 0x00, 0x29, 0x71, 0x9a}, - {0xb8, 0xb8, 0x00, 0x31, 0x87, 0xb8}, - {0xdd, 0xdd, 0x00, 0x3b, 0xa2, 0xdd}, - }; - - val = sd->ctrls[COLORS].val; - for (i = 0; i < ARRAY_SIZE(color_tb[0]); i++) - sccb_reg_write(gspca_dev, 0x4f + i, color_tb[val][i]); -} - static void setlightfreq(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -1225,9 +1289,13 @@ static int sd_init(struct gspca_dev *gspca_dev) if ((sensor_id & 0xfff0) == 0x7670) { sd->sensor = SENSOR_OV767x; - gspca_dev->ctrl_dis = (1 << GAIN) | + gspca_dev->ctrl_dis = (1 << HUE) | + (1 << GAIN) | (1 << AGC) | (1 << SHARPNESS); /* auto */ + sd->ctrls[SATURATION].min = 0, + sd->ctrls[SATURATION].max = 6, + sd->ctrls[SATURATION].def = 3, sd->ctrls[BRIGHTNESS].min = -127; sd->ctrls[BRIGHTNESS].max = 127; sd->ctrls[BRIGHTNESS].def = 0; @@ -1243,7 +1311,6 @@ static int sd_init(struct gspca_dev *gspca_dev) gspca_dev->cam.nmodes = ARRAY_SIZE(ov767x_mode); } else { sd->sensor = SENSOR_OV772x; - gspca_dev->ctrl_dis = (1 << COLORS); gspca_dev->cam.bulk = 1; gspca_dev->cam.bulk_size = 16384; gspca_dev->cam.bulk_nurbs = 2; @@ -1302,6 +1369,9 @@ static int sd_start(struct gspca_dev *gspca_dev) set_frame_rate(gspca_dev); + if (!(gspca_dev->ctrl_dis & (1 << HUE))) + sethue(gspca_dev); + setsaturation(gspca_dev); if (!(gspca_dev->ctrl_dis & (1 << AGC))) setagc(gspca_dev); setawb(gspca_dev); @@ -1314,8 +1384,6 @@ static int sd_start(struct gspca_dev *gspca_dev) if (!(gspca_dev->ctrl_dis & (1 << SHARPNESS))) setsharpness(gspca_dev); sethvflip(gspca_dev); - if (!(gspca_dev->ctrl_dis & (1 << COLORS))) - setcolors(gspca_dev); setlightfreq(gspca_dev); ov534_set_led(gspca_dev, 1); diff --git a/drivers/media/video/gspca/pac207.c b/drivers/media/video/gspca/pac207.c index 3844c49f269c..fa661c6d6d55 100644 --- a/drivers/media/video/gspca/pac207.c +++ b/drivers/media/video/gspca/pac207.c @@ -29,6 +29,8 @@ #include <linux/input.h> #include "gspca.h" +/* Include pac common sof detection functions */ +#include "pac_common.h" MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); MODULE_DESCRIPTION("Pixart PAC207"); @@ -39,16 +41,17 @@ MODULE_LICENSE("GPL"); #define PAC207_BRIGHTNESS_MIN 0 #define PAC207_BRIGHTNESS_MAX 255 #define PAC207_BRIGHTNESS_DEFAULT 46 +#define PAC207_BRIGHTNESS_REG 0x08 #define PAC207_EXPOSURE_MIN 3 #define PAC207_EXPOSURE_MAX 90 /* 1 sec expo time / 1 fps */ #define PAC207_EXPOSURE_DEFAULT 5 /* power on default: 3 */ -#define PAC207_EXPOSURE_KNEE 9 /* fps: 90 / exposure -> 9: 10 fps */ +#define PAC207_EXPOSURE_REG 0x02 #define PAC207_GAIN_MIN 0 #define PAC207_GAIN_MAX 31 #define PAC207_GAIN_DEFAULT 7 /* power on default: 9 */ -#define PAC207_GAIN_KNEE 15 +#define PAC207_GAIN_REG 0x0e #define PAC207_AUTOGAIN_DEADZONE 30 @@ -56,13 +59,9 @@ MODULE_LICENSE("GPL"); struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - u8 mode; - - u8 brightness; - u8 exposure; - u8 autogain; - u8 gain; + struct v4l2_ctrl *brightness; + u8 mode; u8 sof_read; u8 header_read; u8 autogain_ignore_frames; @@ -70,80 +69,6 @@ struct sd { atomic_t avg_lum; }; -/* V4L2 controls supported by the driver */ -static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val); - -static const struct ctrl sd_ctrls[] = { -#define SD_BRIGHTNESS 0 - { - { - .id = V4L2_CID_BRIGHTNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Brightness", - .minimum = PAC207_BRIGHTNESS_MIN, - .maximum = PAC207_BRIGHTNESS_MAX, - .step = 1, - .default_value = PAC207_BRIGHTNESS_DEFAULT, - .flags = 0, - }, - .set = sd_setbrightness, - .get = sd_getbrightness, - }, -#define SD_EXPOSURE 1 - { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Exposure", - .minimum = PAC207_EXPOSURE_MIN, - .maximum = PAC207_EXPOSURE_MAX, - .step = 1, - .default_value = PAC207_EXPOSURE_DEFAULT, - .flags = 0, - }, - .set = sd_setexposure, - .get = sd_getexposure, - }, -#define SD_AUTOGAIN 2 - { - { - .id = V4L2_CID_AUTOGAIN, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Auto Gain", - .minimum = 0, - .maximum = 1, - .step = 1, -#define AUTOGAIN_DEF 1 - .default_value = AUTOGAIN_DEF, - .flags = 0, - }, - .set = sd_setautogain, - .get = sd_getautogain, - }, -#define SD_GAIN 3 - { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gain", - .minimum = PAC207_GAIN_MIN, - .maximum = PAC207_GAIN_MAX, - .step = 1, - .default_value = PAC207_GAIN_DEFAULT, - .flags = 0, - }, - .set = sd_setgain, - .get = sd_getgain, - }, -}; - static const struct v4l2_pix_format sif_mode[] = { {176, 144, V4L2_PIX_FMT_PAC207, V4L2_FIELD_NONE, .bytesperline = 176, @@ -167,39 +92,44 @@ static const __u8 pac207_sensor_init[][8] = { {0x32, 0x00, 0x96, 0x00, 0xa2, 0x02, 0xaf, 0x00}, }; -static int pac207_write_regs(struct gspca_dev *gspca_dev, u16 index, +static void pac207_write_regs(struct gspca_dev *gspca_dev, u16 index, const u8 *buffer, u16 length) { struct usb_device *udev = gspca_dev->dev; int err; + if (gspca_dev->usb_err < 0) + return; + memcpy(gspca_dev->usb_buf, buffer, length); err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x01, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, 0x00, index, gspca_dev->usb_buf, length, PAC207_CTRL_TIMEOUT); - if (err < 0) + if (err < 0) { pr_err("Failed to write registers to index 0x%04X, error %d\n", index, err); - - return err; + gspca_dev->usb_err = err; + } } - -static int pac207_write_reg(struct gspca_dev *gspca_dev, u16 index, u16 value) +static void pac207_write_reg(struct gspca_dev *gspca_dev, u16 index, u16 value) { struct usb_device *udev = gspca_dev->dev; int err; + if (gspca_dev->usb_err < 0) + return; + err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, value, index, NULL, 0, PAC207_CTRL_TIMEOUT); - if (err) + if (err) { pr_err("Failed to write a register (index 0x%04X, value 0x%02X, error %d)\n", index, value, err); - - return err; + gspca_dev->usb_err = err; + } } static int pac207_read_reg(struct gspca_dev *gspca_dev, u16 index) @@ -207,6 +137,9 @@ static int pac207_read_reg(struct gspca_dev *gspca_dev, u16 index) struct usb_device *udev = gspca_dev->dev; int res; + if (gspca_dev->usb_err < 0) + return 0; + res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, 0x00, index, @@ -214,7 +147,8 @@ static int pac207_read_reg(struct gspca_dev *gspca_dev, u16 index) if (res < 0) { pr_err("Failed to read a register (index 0x%04X, error %d)\n", index, res); - return res; + gspca_dev->usb_err = res; + return 0; } return gspca_dev->usb_buf[0]; @@ -224,7 +158,6 @@ static int pac207_read_reg(struct gspca_dev *gspca_dev, u16 index) static int sd_config(struct gspca_dev *gspca_dev, const struct usb_device_id *id) { - struct sd *sd = (struct sd *) gspca_dev; struct cam *cam; u8 idreg[2]; @@ -247,10 +180,6 @@ static int sd_config(struct gspca_dev *gspca_dev, cam = &gspca_dev->cam; cam->cam_mode = sif_mode; cam->nmodes = ARRAY_SIZE(sif_mode); - sd->brightness = PAC207_BRIGHTNESS_DEFAULT; - sd->exposure = PAC207_EXPOSURE_DEFAULT; - sd->gain = PAC207_GAIN_DEFAULT; - sd->autogain = AUTOGAIN_DEF; return 0; } @@ -264,6 +193,87 @@ static int sd_init(struct gspca_dev *gspca_dev) * Bit_2=Compression test mode enable */ pac207_write_reg(gspca_dev, 0x0f, 0x00); /* Power Control */ + return gspca_dev->usb_err; +} + +static void setcontrol(struct gspca_dev *gspca_dev, u16 reg, u16 val) +{ + pac207_write_reg(gspca_dev, reg, val); + pac207_write_reg(gspca_dev, 0x13, 0x01); /* Bit 0, auto clear */ + pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */ +} + +static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + struct sd *sd = (struct sd *)gspca_dev; + + gspca_dev->usb_err = 0; + + if (ctrl->id == V4L2_CID_AUTOGAIN && ctrl->is_new && ctrl->val) { + /* when switching to autogain set defaults to make sure + we are on a valid point of the autogain gain / + exposure knee graph, and give this change time to + take effect before doing autogain. */ + gspca_dev->exposure->val = PAC207_EXPOSURE_DEFAULT; + gspca_dev->gain->val = PAC207_GAIN_DEFAULT; + sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES; + } + + if (!gspca_dev->streaming) + return 0; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + setcontrol(gspca_dev, PAC207_BRIGHTNESS_REG, ctrl->val); + break; + case V4L2_CID_AUTOGAIN: + if (gspca_dev->exposure->is_new || (ctrl->is_new && ctrl->val)) + setcontrol(gspca_dev, PAC207_EXPOSURE_REG, + gspca_dev->exposure->val); + if (gspca_dev->gain->is_new || (ctrl->is_new && ctrl->val)) + setcontrol(gspca_dev, PAC207_GAIN_REG, + gspca_dev->gain->val); + break; + default: + return -EINVAL; + } + return gspca_dev->usb_err; +} + +static const struct v4l2_ctrl_ops sd_ctrl_ops = { + .s_ctrl = sd_s_ctrl, +}; + +/* this function is called at probe time */ +static int sd_init_controls(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; + + gspca_dev->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 4); + + sd->brightness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_BRIGHTNESS, + PAC207_BRIGHTNESS_MIN, PAC207_BRIGHTNESS_MAX, + 1, PAC207_BRIGHTNESS_DEFAULT); + gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, 1); + gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_EXPOSURE, + PAC207_EXPOSURE_MIN, PAC207_EXPOSURE_MAX, + 1, PAC207_EXPOSURE_DEFAULT); + gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_GAIN, + PAC207_GAIN_MIN, PAC207_GAIN_MAX, + 1, PAC207_GAIN_DEFAULT); + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } + v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false); return 0; } @@ -285,11 +295,13 @@ static int sd_start(struct gspca_dev *gspca_dev) else pac207_write_reg(gspca_dev, 0x4a, 0x30); pac207_write_reg(gspca_dev, 0x4b, 0x00); /* Sram test value */ - pac207_write_reg(gspca_dev, 0x08, sd->brightness); + pac207_write_reg(gspca_dev, 0x08, v4l2_ctrl_g_ctrl(sd->brightness)); /* PGA global gain (Bit 4-0) */ - pac207_write_reg(gspca_dev, 0x0e, sd->gain); - pac207_write_reg(gspca_dev, 0x02, sd->exposure); /* PXCK = 12MHz /n */ + pac207_write_reg(gspca_dev, 0x0e, + v4l2_ctrl_g_ctrl(gspca_dev->gain)); + pac207_write_reg(gspca_dev, 0x02, + v4l2_ctrl_g_ctrl(gspca_dev->exposure)); /* PXCK = 12MHz /n */ mode = 0x02; /* Image Format (Bit 0), LED (1), Compr. test mode (2) */ if (gspca_dev->width == 176) { /* 176x144 */ @@ -308,7 +320,7 @@ static int sd_start(struct gspca_dev *gspca_dev) sd->sof_read = 0; sd->autogain_ignore_frames = 0; atomic_set(&sd->avg_lum, -1); - return 0; + return gspca_dev->usb_err; } static void sd_stopN(struct gspca_dev *gspca_dev) @@ -318,8 +330,6 @@ static void sd_stopN(struct gspca_dev *gspca_dev) pac207_write_reg(gspca_dev, 0x0f, 0x00); /* Power Control */ } -/* Include pac common sof detection functions */ -#include "pac_common.h" static void pac207_do_auto_gain(struct gspca_dev *gspca_dev) { @@ -331,9 +341,8 @@ static void pac207_do_auto_gain(struct gspca_dev *gspca_dev) if (sd->autogain_ignore_frames > 0) sd->autogain_ignore_frames--; - else if (gspca_auto_gain_n_exposure(gspca_dev, avg_lum, - 90, PAC207_AUTOGAIN_DEADZONE, - PAC207_GAIN_KNEE, PAC207_EXPOSURE_KNEE)) + else if (gspca_coarse_grained_expo_autogain(gspca_dev, avg_lum, + 90, PAC207_AUTOGAIN_DEADZONE)) sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES; } @@ -384,118 +393,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } -static void setbrightness(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - pac207_write_reg(gspca_dev, 0x08, sd->brightness); - pac207_write_reg(gspca_dev, 0x13, 0x01); /* Bit 0, auto clear */ - pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */ -} - -static void setexposure(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - pac207_write_reg(gspca_dev, 0x02, sd->exposure); - pac207_write_reg(gspca_dev, 0x13, 0x01); /* Bit 0, auto clear */ - pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */ -} - -static void setgain(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - pac207_write_reg(gspca_dev, 0x0e, sd->gain); - pac207_write_reg(gspca_dev, 0x13, 0x01); /* Bit 0, auto clear */ - pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */ -} - -static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->brightness = val; - if (gspca_dev->streaming) - setbrightness(gspca_dev); - return 0; -} - -static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->brightness; - return 0; -} - -static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->exposure = val; - if (gspca_dev->streaming) - setexposure(gspca_dev); - return 0; -} - -static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->exposure; - return 0; -} - -static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->gain = val; - if (gspca_dev->streaming) - setgain(gspca_dev); - return 0; -} - -static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->gain; - return 0; -} - -static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->autogain = val; - /* when switching to autogain set defaults to make sure - we are on a valid point of the autogain gain / - exposure knee graph, and give this change time to - take effect before doing autogain. */ - if (sd->autogain) { - sd->exposure = PAC207_EXPOSURE_DEFAULT; - sd->gain = PAC207_GAIN_DEFAULT; - if (gspca_dev->streaming) { - sd->autogain_ignore_frames = - PAC_AUTOGAIN_IGNORE_FRAMES; - setexposure(gspca_dev); - setgain(gspca_dev); - } - } - - return 0; -} - -static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->autogain; - return 0; -} - #if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, /* interrupt packet data */ @@ -518,10 +415,9 @@ static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, /* sub-driver description */ static const struct sd_desc sd_desc = { .name = MODULE_NAME, - .ctrls = sd_ctrls, - .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, .init = sd_init, + .init_controls = sd_init_controls, .start = sd_start, .stopN = sd_stopN, .dq_callback = pac207_do_auto_gain, diff --git a/drivers/media/video/gspca/pac7302.c b/drivers/media/video/gspca/pac7302.c index 30662fccb0cf..f196a0ff4fab 100644 --- a/drivers/media/video/gspca/pac7302.c +++ b/drivers/media/video/gspca/pac7302.c @@ -23,43 +23,58 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -/* Some documentation about various registers as determined by trial and error. - - Register page 1: - - Address Description - 0x78 Global control, bit 6 controls the LED (inverted) - - Register page 3: - - Address Description - 0x02 Clock divider 3-63, fps = 90 / val. Must be a multiple of 3 on - the 7302, so one of 3, 6, 9, ..., except when between 6 and 12? - 0x03 Variable framerate ctrl reg2==3: 0 -> ~30 fps, 255 -> ~22fps - 0x04 Another var framerate ctrl reg2==3, reg3==0: 0 -> ~30 fps, - 63 -> ~27 fps, the 2 msb's must always be 1 !! - 0x05 Another var framerate ctrl reg2==3, reg3==0, reg4==0xc0: - 1 -> ~30 fps, 2 -> ~20 fps - 0x0e Exposure bits 0-7, 0-448, 0 = use full frame time - 0x0f Exposure bit 8, 0-448, 448 = no exposure at all - 0x10 Master gain 0-31 - 0x21 Bitfield: 0-1 unused, 2-3 vflip/hflip, 4-5 unknown, 6-7 unused - - The registers are accessed in the following functions: - - Page | Register | Function - -----+------------+--------------------------------------------------- - 0 | 0x0f..0x20 | setcolors() - 0 | 0xa2..0xab | setbrightcont() - 0 | 0xc5 | setredbalance() - 0 | 0xc6 | setwhitebalance() - 0 | 0xc7 | setbluebalance() - 0 | 0xdc | setbrightcont(), setcolors() - 3 | 0x02 | setexposure() - 3 | 0x10 | setgain() - 3 | 0x11 | setcolors(), setgain(), setexposure(), sethvflip() - 3 | 0x21 | sethvflip() -*/ +/* + * Some documentation about various registers as determined by trial and error. + * + * Register page 1: + * + * Address Description + * 0x78 Global control, bit 6 controls the LED (inverted) + * 0x80 Compression balance, 2 interesting settings: + * 0x0f Default + * 0x50 Values >= this switch the camera to a lower compression, + * using the same table for both luminance and chrominance. + * This gives a sharper picture. Only usable when running + * at < 15 fps! Note currently the driver does not use this + * as the quality gain is small and the generated JPG-s are + * only understood by v4l-utils >= 0.8.9 + * + * Register page 3: + * + * Address Description + * 0x02 Clock divider 3-63, fps = 90 / val. Must be a multiple of 3 on + * the 7302, so one of 3, 6, 9, ..., except when between 6 and 12? + * 0x03 Variable framerate ctrl reg2==3: 0 -> ~30 fps, 255 -> ~22fps + * 0x04 Another var framerate ctrl reg2==3, reg3==0: 0 -> ~30 fps, + * 63 -> ~27 fps, the 2 msb's must always be 1 !! + * 0x05 Another var framerate ctrl reg2==3, reg3==0, reg4==0xc0: + * 1 -> ~30 fps, 2 -> ~20 fps + * 0x0e Exposure bits 0-7, 0-448, 0 = use full frame time + * 0x0f Exposure bit 8, 0-448, 448 = no exposure at all + * 0x10 Gain 0-31 + * 0x12 Another gain 0-31, unlike 0x10 this one seems to start with an + * amplification value of 1 rather then 0 at its lowest setting + * 0x21 Bitfield: 0-1 unused, 2-3 vflip/hflip, 4-5 unknown, 6-7 unused + * 0x80 Another framerate control, best left at 1, moving it from 1 to + * 2 causes the framerate to become 3/4th of what it was, and + * also seems to cause pixel averaging, resulting in an effective + * resolution of 320x240 and thus a much blockier image + * + * The registers are accessed in the following functions: + * + * Page | Register | Function + * -----+------------+--------------------------------------------------- + * 0 | 0x0f..0x20 | setcolors() + * 0 | 0xa2..0xab | setbrightcont() + * 0 | 0xc5 | setredbalance() + * 0 | 0xc6 | setwhitebalance() + * 0 | 0xc7 | setbluebalance() + * 0 | 0xdc | setbrightcont(), setcolors() + * 3 | 0x02 | setexposure() + * 3 | 0x10, 0x12 | setgain() + * 3 | 0x11 | setcolors(), setgain(), setexposure(), sethvflip() + * 3 | 0x21 | sethvflip() + */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -89,7 +104,6 @@ enum e_ctrl { NCTRLS /* number of controls */ }; -/* specific webcam descriptor for pac7302 */ struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ @@ -198,10 +212,10 @@ static const struct ctrl sd_ctrls[] = { .type = V4L2_CTRL_TYPE_INTEGER, .name = "Gain", .minimum = 0, - .maximum = 255, + .maximum = 62, .step = 1, -#define GAIN_DEF 127 -#define GAIN_KNEE 255 /* Gain seems to cause little noise on the pac73xx */ +#define GAIN_DEF 15 +#define GAIN_KNEE 46 .default_value = GAIN_DEF, }, .set_control = setgain @@ -270,7 +284,6 @@ static const struct v4l2_pix_format vga_mode[] = { #define LOAD_PAGE3 255 #define END_OF_SEQUENCE 0 -/* pac 7302 */ static const u8 init_7302[] = { /* index,value */ 0xff, 0x01, /* page 1 */ @@ -509,7 +522,6 @@ static int sd_config(struct gspca_dev *gspca_dev, return 0; } -/* This function is used by pac7302 only */ static void setbrightcont(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -536,7 +548,6 @@ static void setbrightcont(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0xdc, 0x01); } -/* This function is used by pac7302 only */ static void setcolors(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -590,9 +601,19 @@ static void setbluebalance(struct gspca_dev *gspca_dev) static void setgain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + u8 reg10, reg12; + + if (sd->ctrls[GAIN].val < 32) { + reg10 = sd->ctrls[GAIN].val; + reg12 = 0; + } else { + reg10 = 31; + reg12 = sd->ctrls[GAIN].val - 31; + } reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ - reg_w(gspca_dev, 0x10, sd->ctrls[GAIN].val >> 3); + reg_w(gspca_dev, 0x10, reg10); + reg_w(gspca_dev, 0x12, reg12); /* load registers to sensor (Bit 0, auto clear) */ reg_w(gspca_dev, 0x11, 0x01); @@ -604,28 +625,36 @@ static void setexposure(struct gspca_dev *gspca_dev) u8 clockdiv; u16 exposure; - /* register 2 of frame 3 contains the clock divider configuring the - no fps according to the formula: 90 / reg. sd->exposure is the - desired exposure time in 0.5 ms. */ + /* + * Register 2 of frame 3 contains the clock divider configuring the + * no fps according to the formula: 90 / reg. sd->exposure is the + * desired exposure time in 0.5 ms. + */ clockdiv = (90 * sd->ctrls[EXPOSURE].val + 1999) / 2000; - /* Note clockdiv = 3 also works, but when running at 30 fps, depending - on the scene being recorded, the camera switches to another - quantization table for certain JPEG blocks, and we don't know how - to decompress these blocks. So we cap the framerate at 15 fps */ + /* + * Note clockdiv = 3 also works, but when running at 30 fps, depending + * on the scene being recorded, the camera switches to another + * quantization table for certain JPEG blocks, and we don't know how + * to decompress these blocks. So we cap the framerate at 15 fps. + */ if (clockdiv < 6) clockdiv = 6; else if (clockdiv > 63) clockdiv = 63; - /* reg2 MUST be a multiple of 3, except when between 6 and 12? - Always round up, otherwise we cannot get the desired frametime - using the partial frame time exposure control */ + /* + * Register 2 MUST be a multiple of 3, except when between 6 and 12? + * Always round up, otherwise we cannot get the desired frametime + * using the partial frame time exposure control. + */ if (clockdiv < 6 || clockdiv > 12) clockdiv = ((clockdiv + 2) / 3) * 3; - /* frame exposure time in ms = 1000 * clockdiv / 90 -> - exposure = (sd->exposure / 2) * 448 / (1000 * clockdiv / 90) */ + /* + * frame exposure time in ms = 1000 * clockdiv / 90 -> + * exposure = (sd->exposure / 2) * 448 / (1000 * clockdiv / 90) + */ exposure = (sd->ctrls[EXPOSURE].val * 45 * 448) / (1000 * clockdiv); /* 0 = use full frametime, 448 = no exposure, reverse it */ exposure = 448 - exposure; @@ -643,10 +672,12 @@ static void setautogain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - /* when switching to autogain set defaults to make sure - we are on a valid point of the autogain gain / - exposure knee graph, and give this change time to - take effect before doing autogain. */ + /* + * When switching to autogain set defaults to make sure + * we are on a valid point of the autogain gain / + * exposure knee graph, and give this change time to + * take effect before doing autogain. + */ if (sd->ctrls[AUTOGAIN].val) { sd->ctrls[EXPOSURE].val = EXPOSURE_DEF; sd->ctrls[GAIN].val = GAIN_DEF; @@ -700,8 +731,6 @@ static int sd_start(struct gspca_dev *gspca_dev) setautogain(gspca_dev); sethvflip(gspca_dev); - /* only resolution 640x480 is supported for pac7302 */ - sd->sof_read = 0; atomic_set(&sd->avg_lum, 270 + sd->ctrls[BRIGHTNESS].val); @@ -729,9 +758,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0x78, 0x40); } -/* !! coarse_grained_expo_autogain is not used !! */ -#define exp_too_low_cnt flags -#define exp_too_high_cnt sof_read +#define WANT_REGULAR_AUTOGAIN #include "autogain_functions.h" static void do_autogain(struct gspca_dev *gspca_dev) @@ -792,10 +819,12 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, if (sof) { int n, lum_offset, footer_length; - /* 6 bytes after the FF D9 EOF marker a number of lumination - bytes are send corresponding to different parts of the - image, the 14th and 15th byte after the EOF seem to - correspond to the center of the image */ + /* + * 6 bytes after the FF D9 EOF marker a number of lumination + * bytes are send corresponding to different parts of the + * image, the 14th and 15th byte after the EOF seem to + * correspond to the center of the image. + */ lum_offset = 61 + sizeof pac_sof_marker; footer_length = 74; @@ -839,9 +868,10 @@ static int sd_dbg_s_register(struct gspca_dev *gspca_dev, u8 index; u8 value; - /* reg->reg: bit0..15: reserved for register index (wIndex is 16bit - long on the USB bus) - */ + /* + * reg->reg: bit0..15: reserved for register index (wIndex is 16bit + * long on the USB bus) + */ if (reg->match.type == V4L2_CHIP_MATCH_HOST && reg->match.addr == 0 && (reg->reg < 0x000000ff) && @@ -852,9 +882,11 @@ static int sd_dbg_s_register(struct gspca_dev *gspca_dev, index = reg->reg; value = reg->val; - /* Note that there shall be no access to other page - by any other function between the page swith and - the actual register write */ + /* + * Note that there shall be no access to other page + * by any other function between the page switch and + * the actual register write. + */ reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ reg_w(gspca_dev, index, value); diff --git a/drivers/media/video/gspca/pac7311.c b/drivers/media/video/gspca/pac7311.c index 1ac111176ffa..2cb7d95f7be7 100644 --- a/drivers/media/video/gspca/pac7311.c +++ b/drivers/media/video/gspca/pac7311.c @@ -20,34 +20,42 @@ */ /* Some documentation about various registers as determined by trial and error. - When the register addresses differ between the 7202 and the 7311 the 2 - different addresses are written as 7302addr/7311addr, when one of the 2 - addresses is a - sign that register description is not valid for the - matching IC. - - Register page 1: - - Address Description - -/0x08 Unknown compressor related, must always be 8 except when not - in 640x480 resolution and page 4 reg 2 <= 3 then set it to 9 ! - -/0x1b Auto white balance related, bit 0 is AWB enable (inverted) - bits 345 seem to toggle per color gains on/off (inverted) - 0x78 Global control, bit 6 controls the LED (inverted) - -/0x80 JPEG compression ratio ? Best not touched - - Register page 3/4: - - Address Description - 0x02 Clock divider 2-63, fps =~ 60 / val. Must be a multiple of 3 on - the 7302, so one of 3, 6, 9, ..., except when between 6 and 12? - -/0x0f Master gain 1-245, low value = high gain - 0x10/- Master gain 0-31 - -/0x10 Another gain 0-15, limited influence (1-2x gain I guess) - 0x21 Bitfield: 0-1 unused, 2-3 vflip/hflip, 4-5 unknown, 6-7 unused - -/0x27 Seems to toggle various gains on / off, Setting bit 7 seems to - completely disable the analog amplification block. Set to 0x68 - for max gain, 0x14 for minimal gain. -*/ + * + * Register page 1: + * + * Address Description + * 0x08 Unknown compressor related, must always be 8 except when not + * in 640x480 resolution and page 4 reg 2 <= 3 then set it to 9 ! + * 0x1b Auto white balance related, bit 0 is AWB enable (inverted) + * bits 345 seem to toggle per color gains on/off (inverted) + * 0x78 Global control, bit 6 controls the LED (inverted) + * 0x80 Compression balance, interesting settings: + * 0x01 Use this to allow the camera to switch to higher compr. + * on the fly. Needed to stay within bandwidth @ 640x480@30 + * 0x1c From usb captures under Windows for 640x480 + * 0x2a Values >= this switch the camera to a lower compression, + * using the same table for both luminance and chrominance. + * This gives a sharper picture. Usable only at 640x480@ < + * 15 fps or 320x240 / 160x120. Note currently the driver + * does not use this as the quality gain is small and the + * generated JPG-s are only understood by v4l-utils >= 0.8.9 + * 0x3f From usb captures under Windows for 320x240 + * 0x69 From usb captures under Windows for 160x120 + * + * Register page 4: + * + * Address Description + * 0x02 Clock divider 2-63, fps =~ 60 / val. Must be a multiple of 3 on + * the 7302, so one of 3, 6, 9, ..., except when between 6 and 12? + * 0x0f Master gain 1-245, low value = high gain + * 0x10 Another gain 0-15, limited influence (1-2x gain I guess) + * 0x21 Bitfield: 0-1 unused, 2-3 vflip/hflip, 4-5 unknown, 6-7 unused + * Note setting vflip disabled leads to a much lower image quality, + * so we always vflip, and tell userspace to flip it back + * 0x27 Seems to toggle various gains on / off, Setting bit 7 seems to + * completely disable the analog amplification block. Set to 0x68 + * for max gain, 0x14 for minimal gain. + */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -55,21 +63,21 @@ #include <linux/input.h> #include "gspca.h" +/* Include pac common sof detection functions */ +#include "pac_common.h" + +#define PAC7311_GAIN_DEFAULT 122 +#define PAC7311_EXPOSURE_DEFAULT 3 /* 20 fps, avoid using high compr. */ MODULE_AUTHOR("Thomas Kaiser thomas@kaiser-linux.li"); MODULE_DESCRIPTION("Pixart PAC7311"); MODULE_LICENSE("GPL"); -/* specific webcam descriptor for pac7311 */ struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - unsigned char contrast; - unsigned char gain; - unsigned char exposure; - unsigned char autogain; - __u8 hflip; - __u8 vflip; + struct v4l2_ctrl *contrast; + struct v4l2_ctrl *hflip; u8 sof_read; u8 autogain_ignore_frames; @@ -77,114 +85,6 @@ struct sd { atomic_t avg_lum; }; -/* V4L2 controls supported by the driver */ -static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val); -static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val); - -static const struct ctrl sd_ctrls[] = { -/* This control is for both the 7302 and the 7311 */ - { - { - .id = V4L2_CID_CONTRAST, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Contrast", - .minimum = 0, -#define CONTRAST_MAX 255 - .maximum = CONTRAST_MAX, - .step = 1, -#define CONTRAST_DEF 127 - .default_value = CONTRAST_DEF, - }, - .set = sd_setcontrast, - .get = sd_getcontrast, - }, -/* All controls below are for both the 7302 and the 7311 */ - { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gain", - .minimum = 0, -#define GAIN_MAX 255 - .maximum = GAIN_MAX, - .step = 1, -#define GAIN_DEF 127 -#define GAIN_KNEE 255 /* Gain seems to cause little noise on the pac73xx */ - .default_value = GAIN_DEF, - }, - .set = sd_setgain, - .get = sd_getgain, - }, - { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Exposure", - .minimum = 0, -#define EXPOSURE_MAX 255 - .maximum = EXPOSURE_MAX, - .step = 1, -#define EXPOSURE_DEF 16 /* 32 ms / 30 fps */ -#define EXPOSURE_KNEE 50 /* 100 ms / 10 fps */ - .default_value = EXPOSURE_DEF, - }, - .set = sd_setexposure, - .get = sd_getexposure, - }, - { - { - .id = V4L2_CID_AUTOGAIN, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Auto Gain", - .minimum = 0, - .maximum = 1, - .step = 1, -#define AUTOGAIN_DEF 1 - .default_value = AUTOGAIN_DEF, - }, - .set = sd_setautogain, - .get = sd_getautogain, - }, - { - { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Mirror", - .minimum = 0, - .maximum = 1, - .step = 1, -#define HFLIP_DEF 0 - .default_value = HFLIP_DEF, - }, - .set = sd_sethflip, - .get = sd_gethflip, - }, - { - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Vflip", - .minimum = 0, - .maximum = 1, - .step = 1, -#define VFLIP_DEF 0 - .default_value = VFLIP_DEF, - }, - .set = sd_setvflip, - .get = sd_getvflip, - }, -}; - static const struct v4l2_pix_format vga_mode[] = { {160, 120, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE, .bytesperline = 160, @@ -206,8 +106,8 @@ static const struct v4l2_pix_format vga_mode[] = { #define LOAD_PAGE4 254 #define END_OF_SEQUENCE 0 -/* pac 7311 */ static const __u8 init_7311[] = { + 0xff, 0x01, 0x78, 0x40, /* Bit_0=start stream, Bit_6=LED */ 0x78, 0x40, /* Bit_0=start stream, Bit_6=LED */ 0x78, 0x44, /* Bit_0=start stream, Bit_6=LED */ @@ -387,90 +287,73 @@ static void reg_w_var(struct gspca_dev *gspca_dev, static int sd_config(struct gspca_dev *gspca_dev, const struct usb_device_id *id) { - struct sd *sd = (struct sd *) gspca_dev; - struct cam *cam; + struct cam *cam = &gspca_dev->cam; - cam = &gspca_dev->cam; - - PDEBUG(D_CONF, "Find Sensor PAC7311"); cam->cam_mode = vga_mode; cam->nmodes = ARRAY_SIZE(vga_mode); + cam->input_flags = V4L2_IN_ST_VFLIP; - sd->contrast = CONTRAST_DEF; - sd->gain = GAIN_DEF; - sd->exposure = EXPOSURE_DEF; - sd->autogain = AUTOGAIN_DEF; - sd->hflip = HFLIP_DEF; - sd->vflip = VFLIP_DEF; return 0; } -/* This function is used by pac7311 only */ -static void setcontrast(struct gspca_dev *gspca_dev) +static void setcontrast(struct gspca_dev *gspca_dev, s32 val) { - struct sd *sd = (struct sd *) gspca_dev; - reg_w(gspca_dev, 0xff, 0x04); - reg_w(gspca_dev, 0x10, sd->contrast >> 4); + reg_w(gspca_dev, 0x10, val); /* load registers to sensor (Bit 0, auto clear) */ reg_w(gspca_dev, 0x11, 0x01); } -static void setgain(struct gspca_dev *gspca_dev) +static void setgain(struct gspca_dev *gspca_dev, s32 val) { - struct sd *sd = (struct sd *) gspca_dev; - int gain = GAIN_MAX - sd->gain; - - if (gain < 1) - gain = 1; - else if (gain > 245) - gain = 245; reg_w(gspca_dev, 0xff, 0x04); /* page 4 */ reg_w(gspca_dev, 0x0e, 0x00); - reg_w(gspca_dev, 0x0f, gain); + reg_w(gspca_dev, 0x0f, gspca_dev->gain->maximum - val + 1); /* load registers to sensor (Bit 0, auto clear) */ reg_w(gspca_dev, 0x11, 0x01); } -static void setexposure(struct gspca_dev *gspca_dev) +static void setexposure(struct gspca_dev *gspca_dev, s32 val) { - struct sd *sd = (struct sd *) gspca_dev; - __u8 reg; - - /* register 2 of frame 3/4 contains the clock divider configuring the - no fps according to the formula: 60 / reg. sd->exposure is the - desired exposure time in ms. */ - reg = 120 * sd->exposure / 1000; - if (reg < 2) - reg = 2; - else if (reg > 63) - reg = 63; - reg_w(gspca_dev, 0xff, 0x04); /* page 4 */ - reg_w(gspca_dev, 0x02, reg); + reg_w(gspca_dev, 0x02, val); - /* Page 1 register 8 must always be 0x08 except when not in - 640x480 mode and Page3/4 reg 2 <= 3 then it must be 9 */ + /* load registers to sensor (Bit 0, auto clear) */ + reg_w(gspca_dev, 0x11, 0x01); + + /* + * Page 1 register 8 must always be 0x08 except when not in + * 640x480 mode and page 4 reg 2 <= 3 then it must be 9 + */ reg_w(gspca_dev, 0xff, 0x01); - if (gspca_dev->cam.cam_mode[(int)gspca_dev->curr_mode].priv && - reg <= 3) { + if (gspca_dev->width != 640 && val <= 3) reg_w(gspca_dev, 0x08, 0x09); - } else { + else reg_w(gspca_dev, 0x08, 0x08); - } + + /* + * Page1 register 80 sets the compression balance, normally we + * want / use 0x1c, but for 640x480@30fps we must allow the + * camera to use higher compression or we may run out of + * bandwidth. + */ + if (gspca_dev->width == 640 && val == 2) + reg_w(gspca_dev, 0x80, 0x01); + else + reg_w(gspca_dev, 0x80, 0x1c); /* load registers to sensor (Bit 0, auto clear) */ reg_w(gspca_dev, 0x11, 0x01); } -static void sethvflip(struct gspca_dev *gspca_dev) +static void sethvflip(struct gspca_dev *gspca_dev, s32 hflip, s32 vflip) { - struct sd *sd = (struct sd *) gspca_dev; __u8 data; reg_w(gspca_dev, 0xff, 0x04); /* page 4 */ - data = (sd->hflip ? 0x04 : 0x00) | (sd->vflip ? 0x08 : 0x00); + data = (hflip ? 0x04 : 0x00) | + (vflip ? 0x08 : 0x00); reg_w(gspca_dev, 0x21, data); /* load registers to sensor (Bit 0, auto clear) */ @@ -484,6 +367,82 @@ static int sd_init(struct gspca_dev *gspca_dev) return gspca_dev->usb_err; } +static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + struct sd *sd = (struct sd *)gspca_dev; + + gspca_dev->usb_err = 0; + + if (ctrl->id == V4L2_CID_AUTOGAIN && ctrl->is_new && ctrl->val) { + /* when switching to autogain set defaults to make sure + we are on a valid point of the autogain gain / + exposure knee graph, and give this change time to + take effect before doing autogain. */ + gspca_dev->exposure->val = PAC7311_EXPOSURE_DEFAULT; + gspca_dev->gain->val = PAC7311_GAIN_DEFAULT; + sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES; + } + + if (!gspca_dev->streaming) + return 0; + + switch (ctrl->id) { + case V4L2_CID_CONTRAST: + setcontrast(gspca_dev, ctrl->val); + break; + case V4L2_CID_AUTOGAIN: + if (gspca_dev->exposure->is_new || (ctrl->is_new && ctrl->val)) + setexposure(gspca_dev, gspca_dev->exposure->val); + if (gspca_dev->gain->is_new || (ctrl->is_new && ctrl->val)) + setgain(gspca_dev, gspca_dev->gain->val); + break; + case V4L2_CID_HFLIP: + sethvflip(gspca_dev, sd->hflip->val, 1); + break; + default: + return -EINVAL; + } + return gspca_dev->usb_err; +} + +static const struct v4l2_ctrl_ops sd_ctrl_ops = { + .s_ctrl = sd_s_ctrl, +}; + +/* this function is called at probe time */ +static int sd_init_controls(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; + + gspca_dev->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 4); + + sd->contrast = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_CONTRAST, 0, 15, 1, 7); + gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, 1); + gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_EXPOSURE, 2, 63, 1, + PAC7311_EXPOSURE_DEFAULT); + gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_GAIN, 0, 244, 1, + PAC7311_GAIN_DEFAULT); + sd->hflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } + + v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false); + return 0; +} + +/* -- start the camera -- */ static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -492,19 +451,19 @@ static int sd_start(struct gspca_dev *gspca_dev) reg_w_var(gspca_dev, start_7311, page4_7311, sizeof(page4_7311)); - setcontrast(gspca_dev); - setgain(gspca_dev); - setexposure(gspca_dev); - sethvflip(gspca_dev); + setcontrast(gspca_dev, v4l2_ctrl_g_ctrl(sd->contrast)); + setgain(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->gain)); + setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure)); + sethvflip(gspca_dev, v4l2_ctrl_g_ctrl(sd->hflip), 1); /* set correct resolution */ switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) { - case 2: /* 160x120 pac7311 */ + case 2: /* 160x120 */ reg_w(gspca_dev, 0xff, 0x01); reg_w(gspca_dev, 0x17, 0x20); reg_w(gspca_dev, 0x87, 0x10); break; - case 1: /* 320x240 pac7311 */ + case 1: /* 320x240 */ reg_w(gspca_dev, 0xff, 0x01); reg_w(gspca_dev, 0x17, 0x30); reg_w(gspca_dev, 0x87, 0x11); @@ -541,14 +500,6 @@ static void sd_stopN(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */ } -/* called on streamoff with alt 0 and on disconnect for 7311 */ -static void sd_stop0(struct gspca_dev *gspca_dev) -{ -} - -/* Include pac common sof detection functions */ -#include "pac_common.h" - static void do_autogain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -558,13 +509,13 @@ static void do_autogain(struct gspca_dev *gspca_dev) if (avg_lum == -1) return; - desired_lum = 200; + desired_lum = 170; deadzone = 20; if (sd->autogain_ignore_frames > 0) sd->autogain_ignore_frames--; - else if (gspca_auto_gain_n_exposure(gspca_dev, avg_lum, desired_lum, - deadzone, GAIN_KNEE, EXPOSURE_KNEE)) + else if (gspca_coarse_grained_expo_autogain(gspca_dev, avg_lum, + desired_lum, deadzone)) sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES; } @@ -628,10 +579,12 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, if (sof) { int n, lum_offset, footer_length; - /* 6 bytes after the FF D9 EOF marker a number of lumination - bytes are send corresponding to different parts of the - image, the 14th and 15th byte after the EOF seem to - correspond to the center of the image */ + /* + * 6 bytes after the FF D9 EOF marker a number of lumination + * bytes are send corresponding to different parts of the + * image, the 14th and 15th byte after the EOF seem to + * correspond to the center of the image. + */ lum_offset = 24 + sizeof pac_sof_marker; footer_length = 26; @@ -668,127 +621,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } -static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->contrast = val; - if (gspca_dev->streaming) - setcontrast(gspca_dev); - return gspca_dev->usb_err; -} - -static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->contrast; - return 0; -} - -static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->gain = val; - if (gspca_dev->streaming) - setgain(gspca_dev); - return gspca_dev->usb_err; -} - -static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->gain; - return 0; -} - -static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->exposure = val; - if (gspca_dev->streaming) - setexposure(gspca_dev); - return gspca_dev->usb_err; -} - -static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->exposure; - return 0; -} - -static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->autogain = val; - /* when switching to autogain set defaults to make sure - we are on a valid point of the autogain gain / - exposure knee graph, and give this change time to - take effect before doing autogain. */ - if (sd->autogain) { - sd->exposure = EXPOSURE_DEF; - sd->gain = GAIN_DEF; - if (gspca_dev->streaming) { - sd->autogain_ignore_frames = - PAC_AUTOGAIN_IGNORE_FRAMES; - setexposure(gspca_dev); - setgain(gspca_dev); - } - } - - return gspca_dev->usb_err; -} - -static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->autogain; - return 0; -} - -static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->hflip = val; - if (gspca_dev->streaming) - sethvflip(gspca_dev); - return gspca_dev->usb_err; -} - -static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->hflip; - return 0; -} - -static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->vflip = val; - if (gspca_dev->streaming) - sethvflip(gspca_dev); - return gspca_dev->usb_err; -} - -static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->vflip; - return 0; -} - #if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, /* interrupt packet data */ @@ -820,16 +652,13 @@ static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, } #endif -/* sub-driver description for pac7311 */ static const struct sd_desc sd_desc = { .name = MODULE_NAME, - .ctrls = sd_ctrls, - .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, .init = sd_init, + .init_controls = sd_init_controls, .start = sd_start, .stopN = sd_stopN, - .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, .dq_callback = do_autogain, #if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) diff --git a/drivers/media/video/gspca/sn9c20x.c b/drivers/media/video/gspca/sn9c20x.c index 7e71aa2d2522..ad098202d7f0 100644 --- a/drivers/media/video/gspca/sn9c20x.c +++ b/drivers/media/video/gspca/sn9c20x.c @@ -59,35 +59,38 @@ MODULE_LICENSE("GPL"); #define SENSOR_MT9M111 9 #define SENSOR_MT9M112 10 #define SENSOR_HV7131R 11 -#define SENSOR_MT9VPRB 20 +#define SENSOR_MT9VPRB 12 /* camera flags */ #define HAS_NO_BUTTON 0x1 #define LED_REVERSE 0x2 /* some cameras unset gpio to turn on leds */ #define FLIP_DETECT 0x4 -enum e_ctrl { - BRIGHTNESS, - CONTRAST, - SATURATION, - HUE, - GAMMA, - BLUE, - RED, - VFLIP, - HFLIP, - EXPOSURE, - GAIN, - AUTOGAIN, - QUALITY, - NCTRLS /* number of controls */ -}; - /* specific webcam descriptor */ struct sd { struct gspca_dev gspca_dev; - struct gspca_ctrl ctrls[NCTRLS]; + struct { /* color control cluster */ + struct v4l2_ctrl *brightness; + struct v4l2_ctrl *contrast; + struct v4l2_ctrl *saturation; + struct v4l2_ctrl *hue; + }; + struct { /* blue/red balance control cluster */ + struct v4l2_ctrl *blue; + struct v4l2_ctrl *red; + }; + struct { /* h/vflip control cluster */ + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; + }; + struct v4l2_ctrl *gamma; + struct { /* autogain and exposure or gain control cluster */ + struct v4l2_ctrl *autogain; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *gain; + }; + struct v4l2_ctrl *jpegqual; struct work_struct work; struct workqueue_struct *work_thread; @@ -105,6 +108,7 @@ struct sd { u8 exposure_step; u8 i2c_addr; + u8 i2c_intf; u8 sensor; u8 hstart; u8 vstart; @@ -166,175 +170,6 @@ static const struct dmi_system_id flip_dmi_table[] = { {} }; -static void set_cmatrix(struct gspca_dev *gspca_dev); -static void set_gamma(struct gspca_dev *gspca_dev); -static void set_redblue(struct gspca_dev *gspca_dev); -static void set_hvflip(struct gspca_dev *gspca_dev); -static void set_exposure(struct gspca_dev *gspca_dev); -static void set_gain(struct gspca_dev *gspca_dev); -static void set_quality(struct gspca_dev *gspca_dev); - -static const struct ctrl sd_ctrls[NCTRLS] = { -[BRIGHTNESS] = { - { - .id = V4L2_CID_BRIGHTNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Brightness", - .minimum = 0, - .maximum = 0xff, - .step = 1, - .default_value = 0x7f - }, - .set_control = set_cmatrix - }, -[CONTRAST] = { - { - .id = V4L2_CID_CONTRAST, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Contrast", - .minimum = 0, - .maximum = 0xff, - .step = 1, - .default_value = 0x7f - }, - .set_control = set_cmatrix - }, -[SATURATION] = { - { - .id = V4L2_CID_SATURATION, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Saturation", - .minimum = 0, - .maximum = 0xff, - .step = 1, - .default_value = 0x7f - }, - .set_control = set_cmatrix - }, -[HUE] = { - { - .id = V4L2_CID_HUE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Hue", - .minimum = -180, - .maximum = 180, - .step = 1, - .default_value = 0 - }, - .set_control = set_cmatrix - }, -[GAMMA] = { - { - .id = V4L2_CID_GAMMA, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gamma", - .minimum = 0, - .maximum = 0xff, - .step = 1, - .default_value = 0x10 - }, - .set_control = set_gamma - }, -[BLUE] = { - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Blue Balance", - .minimum = 0, - .maximum = 0x7f, - .step = 1, - .default_value = 0x28 - }, - .set_control = set_redblue - }, -[RED] = { - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Red Balance", - .minimum = 0, - .maximum = 0x7f, - .step = 1, - .default_value = 0x28 - }, - .set_control = set_redblue - }, -[HFLIP] = { - { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Horizontal Flip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, - .set_control = set_hvflip - }, -[VFLIP] = { - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Vertical Flip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, - .set_control = set_hvflip - }, -[EXPOSURE] = { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Exposure", - .minimum = 0, - .maximum = 0x1780, - .step = 1, - .default_value = 0x33, - }, - .set_control = set_exposure - }, -[GAIN] = { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gain", - .minimum = 0, - .maximum = 28, - .step = 1, - .default_value = 0, - }, - .set_control = set_gain - }, -[AUTOGAIN] = { - { - .id = V4L2_CID_AUTOGAIN, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Auto Exposure", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1, - }, - }, -[QUALITY] = { - { - .id = V4L2_CID_JPEG_COMPRESSION_QUALITY, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Compression Quality", -#define QUALITY_MIN 50 -#define QUALITY_MAX 90 -#define QUALITY_DEF 80 - .minimum = QUALITY_MIN, - .maximum = QUALITY_MAX, - .step = 1, - .default_value = QUALITY_DEF, - }, - .set_control = set_quality - }, -}; - static const struct v4l2_pix_format vga_mode[] = { {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 160, @@ -747,7 +582,7 @@ static const s16 hsv_blue_y[] = { 4, 2, 0, -1, -3, -5, -7, -9, -11 }; -static u16 i2c_ident[] = { +static const u16 i2c_ident[] = { V4L2_IDENT_OV9650, V4L2_IDENT_OV9655, V4L2_IDENT_SOI968, @@ -760,9 +595,10 @@ static u16 i2c_ident[] = { V4L2_IDENT_MT9M111, V4L2_IDENT_MT9M112, V4L2_IDENT_HV7131R, +[SENSOR_MT9VPRB] = V4L2_IDENT_UNKNOWN, }; -static u16 bridge_init[][2] = { +static const u16 bridge_init[][2] = { {0x1000, 0x78}, {0x1001, 0x40}, {0x1002, 0x1c}, {0x1020, 0x80}, {0x1061, 0x01}, {0x1067, 0x40}, {0x1068, 0x30}, {0x1069, 0x20}, {0x106a, 0x10}, @@ -786,7 +622,7 @@ static u16 bridge_init[][2] = { }; /* Gain = (bit[3:0] / 16 + 1) * (bit[4] + 1) * (bit[5] + 1) * (bit[6] + 1) */ -static u8 ov_gain[] = { +static const u8 ov_gain[] = { 0x00 /* 1x */, 0x04 /* 1.25x */, 0x08 /* 1.5x */, 0x0c /* 1.75x */, 0x10 /* 2x */, 0x12 /* 2.25x */, 0x14 /* 2.5x */, 0x16 /* 2.75x */, 0x18 /* 3x */, 0x1a /* 3.25x */, 0x1c /* 3.5x */, 0x1e /* 3.75x */, @@ -798,7 +634,7 @@ static u8 ov_gain[] = { }; /* Gain = (bit[8] + 1) * (bit[7] + 1) * (bit[6:0] * 0.03125) */ -static u16 micron1_gain[] = { +static const u16 micron1_gain[] = { /* 1x 1.25x 1.5x 1.75x */ 0x0020, 0x0028, 0x0030, 0x0038, /* 2x 2.25x 2.5x 2.75x */ @@ -819,7 +655,7 @@ static u16 micron1_gain[] = { /* mt9m001 sensor uses a different gain formula then other micron sensors */ /* Gain = (bit[6] + 1) * (bit[5-0] * 0.125) */ -static u16 micron2_gain[] = { +static const u16 micron2_gain[] = { /* 1x 1.25x 1.5x 1.75x */ 0x0008, 0x000a, 0x000c, 0x000e, /* 2x 2.25x 2.5x 2.75x */ @@ -839,7 +675,7 @@ static u16 micron2_gain[] = { }; /* Gain = .5 + bit[7:0] / 16 */ -static u8 hv7131r_gain[] = { +static const u8 hv7131r_gain[] = { 0x08 /* 1x */, 0x0c /* 1.25x */, 0x10 /* 1.5x */, 0x14 /* 1.75x */, 0x18 /* 2x */, 0x1c /* 2.25x */, 0x20 /* 2.5x */, 0x24 /* 2.75x */, 0x28 /* 3x */, 0x2c /* 3.25x */, 0x30 /* 3.5x */, 0x34 /* 3.75x */, @@ -850,7 +686,7 @@ static u8 hv7131r_gain[] = { 0x78 /* 8x */ }; -static struct i2c_reg_u8 soi968_init[] = { +static const struct i2c_reg_u8 soi968_init[] = { {0x0c, 0x00}, {0x0f, 0x1f}, {0x11, 0x80}, {0x38, 0x52}, {0x1e, 0x00}, {0x33, 0x08}, {0x35, 0x8c}, {0x36, 0x0c}, @@ -864,7 +700,7 @@ static struct i2c_reg_u8 soi968_init[] = { {0x00, 0x00}, {0x01, 0x80}, {0x02, 0x80}, }; -static struct i2c_reg_u8 ov7660_init[] = { +static const struct i2c_reg_u8 ov7660_init[] = { {0x0e, 0x80}, {0x0d, 0x08}, {0x0f, 0xc3}, {0x04, 0xc3}, {0x10, 0x40}, {0x11, 0x40}, {0x12, 0x05}, {0x13, 0xba}, {0x14, 0x2a}, @@ -872,11 +708,11 @@ static struct i2c_reg_u8 ov7660_init[] = { 0x10, 0x61 and sd->hstart, vstart = 3, fixes ugly colored borders */ {0x17, 0x10}, {0x18, 0x61}, {0x37, 0x0f}, {0x38, 0x02}, {0x39, 0x43}, - {0x3a, 0x00}, {0x69, 0x90}, {0x2d, 0xf6}, - {0x2e, 0x0b}, {0x01, 0x78}, {0x02, 0x50}, + {0x3a, 0x00}, {0x69, 0x90}, {0x2d, 0x00}, + {0x2e, 0x00}, {0x01, 0x78}, {0x02, 0x50}, }; -static struct i2c_reg_u8 ov7670_init[] = { +static const struct i2c_reg_u8 ov7670_init[] = { {0x11, 0x80}, {0x3a, 0x04}, {0x12, 0x01}, {0x32, 0xb6}, {0x03, 0x0a}, {0x0c, 0x00}, {0x3e, 0x00}, {0x70, 0x3a}, {0x71, 0x35}, {0x72, 0x11}, {0x73, 0xf0}, @@ -933,7 +769,7 @@ static struct i2c_reg_u8 ov7670_init[] = { {0x93, 0x00}, }; -static struct i2c_reg_u8 ov9650_init[] = { +static const struct i2c_reg_u8 ov9650_init[] = { {0x00, 0x00}, {0x01, 0x78}, {0x02, 0x78}, {0x03, 0x36}, {0x04, 0x03}, {0x05, 0x00}, {0x06, 0x00}, {0x08, 0x00}, @@ -963,7 +799,7 @@ static struct i2c_reg_u8 ov9650_init[] = { {0xaa, 0x92}, {0xab, 0x0a}, }; -static struct i2c_reg_u8 ov9655_init[] = { +static const struct i2c_reg_u8 ov9655_init[] = { {0x0e, 0x61}, {0x11, 0x80}, {0x13, 0xba}, {0x14, 0x2e}, {0x16, 0x24}, {0x1e, 0x04}, {0x27, 0x08}, {0x28, 0x08}, {0x29, 0x15}, {0x2c, 0x08}, {0x34, 0x3d}, @@ -990,7 +826,7 @@ static struct i2c_reg_u8 ov9655_init[] = { {0x04, 0x03}, {0x00, 0x13}, }; -static struct i2c_reg_u16 mt9v112_init[] = { +static const struct i2c_reg_u16 mt9v112_init[] = { {0xf0, 0x0000}, {0x0d, 0x0021}, {0x0d, 0x0020}, {0x34, 0xc019}, {0x0a, 0x0011}, {0x0b, 0x000b}, {0x20, 0x0703}, {0x35, 0x2022}, {0xf0, 0x0001}, @@ -1009,7 +845,7 @@ static struct i2c_reg_u16 mt9v112_init[] = { {0x2c, 0x00ae}, {0x2d, 0x00ae}, {0x2e, 0x00ae}, }; -static struct i2c_reg_u16 mt9v111_init[] = { +static const struct i2c_reg_u16 mt9v111_init[] = { {0x01, 0x0004}, {0x0d, 0x0001}, {0x0d, 0x0000}, {0x01, 0x0001}, {0x05, 0x0004}, {0x2d, 0xe0a0}, {0x2e, 0x0c64}, {0x2f, 0x0064}, {0x06, 0x600e}, @@ -1019,7 +855,7 @@ static struct i2c_reg_u16 mt9v111_init[] = { {0x0e, 0x0008}, {0x20, 0x0000} }; -static struct i2c_reg_u16 mt9v011_init[] = { +static const struct i2c_reg_u16 mt9v011_init[] = { {0x07, 0x0002}, {0x0d, 0x0001}, {0x0d, 0x0000}, {0x01, 0x0008}, {0x02, 0x0016}, {0x03, 0x01e1}, {0x04, 0x0281}, {0x05, 0x0083}, {0x06, 0x0006}, @@ -1046,7 +882,7 @@ static struct i2c_reg_u16 mt9v011_init[] = { {0x06, 0x0029}, {0x05, 0x0009}, }; -static struct i2c_reg_u16 mt9m001_init[] = { +static const struct i2c_reg_u16 mt9m001_init[] = { {0x0d, 0x0001}, {0x0d, 0x0000}, {0x04, 0x0500}, /* hres = 1280 */ @@ -1062,21 +898,21 @@ static struct i2c_reg_u16 mt9m001_init[] = { {0x35, 0x0057}, }; -static struct i2c_reg_u16 mt9m111_init[] = { +static const struct i2c_reg_u16 mt9m111_init[] = { {0xf0, 0x0000}, {0x0d, 0x0021}, {0x0d, 0x0008}, {0xf0, 0x0001}, {0x3a, 0x4300}, {0x9b, 0x4300}, {0x06, 0x708e}, {0xf0, 0x0002}, {0x2e, 0x0a1e}, {0xf0, 0x0000}, }; -static struct i2c_reg_u16 mt9m112_init[] = { +static const struct i2c_reg_u16 mt9m112_init[] = { {0xf0, 0x0000}, {0x0d, 0x0021}, {0x0d, 0x0008}, {0xf0, 0x0001}, {0x3a, 0x4300}, {0x9b, 0x4300}, {0x06, 0x708e}, {0xf0, 0x0002}, {0x2e, 0x0a1e}, {0xf0, 0x0000}, }; -static struct i2c_reg_u8 hv7131r_init[] = { +static const struct i2c_reg_u8 hv7131r_init[] = { {0x02, 0x08}, {0x02, 0x00}, {0x01, 0x08}, {0x02, 0x00}, {0x20, 0x00}, {0x21, 0xd0}, {0x22, 0x00}, {0x23, 0x09}, {0x01, 0x08}, @@ -1167,7 +1003,7 @@ static void i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val) * from the point of view of the bridge, the length * includes the address */ - row[0] = 0x81 | (2 << 4); + row[0] = sd->i2c_intf | (2 << 4); row[1] = sd->i2c_addr; row[2] = reg; row[3] = val; @@ -1180,7 +1016,7 @@ static void i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val) } static void i2c_w1_buf(struct gspca_dev *gspca_dev, - struct i2c_reg_u8 *buf, int sz) + const struct i2c_reg_u8 *buf, int sz) { while (--sz >= 0) { i2c_w1(gspca_dev, buf->reg, buf->val); @@ -1197,7 +1033,7 @@ static void i2c_w2(struct gspca_dev *gspca_dev, u8 reg, u16 val) * from the point of view of the bridge, the length * includes the address */ - row[0] = 0x81 | (3 << 4); + row[0] = sd->i2c_intf | (3 << 4); row[1] = sd->i2c_addr; row[2] = reg; row[3] = val >> 8; @@ -1210,7 +1046,7 @@ static void i2c_w2(struct gspca_dev *gspca_dev, u8 reg, u16 val) } static void i2c_w2_buf(struct gspca_dev *gspca_dev, - struct i2c_reg_u16 *buf, int sz) + const struct i2c_reg_u16 *buf, int sz) { while (--sz >= 0) { i2c_w2(gspca_dev, buf->reg, buf->val); @@ -1223,7 +1059,7 @@ static void i2c_r1(struct gspca_dev *gspca_dev, u8 reg, u8 *val) struct sd *sd = (struct sd *) gspca_dev; u8 row[8]; - row[0] = 0x81 | (1 << 4); + row[0] = sd->i2c_intf | (1 << 4); row[1] = sd->i2c_addr; row[2] = reg; row[3] = 0; @@ -1232,7 +1068,7 @@ static void i2c_r1(struct gspca_dev *gspca_dev, u8 reg, u8 *val) row[6] = 0; row[7] = 0x10; i2c_w(gspca_dev, row); - row[0] = 0x81 | (1 << 4) | 0x02; + row[0] = sd->i2c_intf | (1 << 4) | 0x02; row[2] = 0; i2c_w(gspca_dev, row); reg_r(gspca_dev, 0x10c2, 5); @@ -1244,7 +1080,7 @@ static void i2c_r2(struct gspca_dev *gspca_dev, u8 reg, u16 *val) struct sd *sd = (struct sd *) gspca_dev; u8 row[8]; - row[0] = 0x81 | (1 << 4); + row[0] = sd->i2c_intf | (1 << 4); row[1] = sd->i2c_addr; row[2] = reg; row[3] = 0; @@ -1253,7 +1089,7 @@ static void i2c_r2(struct gspca_dev *gspca_dev, u8 reg, u16 *val) row[6] = 0; row[7] = 0x10; i2c_w(gspca_dev, row); - row[0] = 0x81 | (2 << 4) | 0x02; + row[0] = sd->i2c_intf | (2 << 4) | 0x02; row[2] = 0; i2c_w(gspca_dev, row); reg_r(gspca_dev, 0x10c2, 5); @@ -1294,8 +1130,6 @@ static void ov9655_init_sensor(struct gspca_dev *gspca_dev) if (gspca_dev->usb_err < 0) pr_err("OV9655 sensor initialization failed\n"); - /* disable hflip and vflip */ - gspca_dev->ctrl_dis = (1 << HFLIP) | (1 << VFLIP); sd->hstart = 1; sd->vstart = 2; } @@ -1310,9 +1144,6 @@ static void soi968_init_sensor(struct gspca_dev *gspca_dev) if (gspca_dev->usb_err < 0) pr_err("SOI968 sensor initialization failed\n"); - /* disable hflip and vflip */ - gspca_dev->ctrl_dis = (1 << HFLIP) | (1 << VFLIP) - | (1 << EXPOSURE); sd->hstart = 60; sd->vstart = 11; } @@ -1340,8 +1171,6 @@ static void ov7670_init_sensor(struct gspca_dev *gspca_dev) if (gspca_dev->usb_err < 0) pr_err("OV7670 sensor initialization failed\n"); - /* disable hflip and vflip */ - gspca_dev->ctrl_dis = (1 << HFLIP) | (1 << VFLIP); sd->hstart = 0; sd->vstart = 1; } @@ -1378,9 +1207,6 @@ static void mt9v_init_sensor(struct gspca_dev *gspca_dev) pr_err("MT9V111 sensor initialization failed\n"); return; } - gspca_dev->ctrl_dis = (1 << EXPOSURE) - | (1 << AUTOGAIN) - | (1 << GAIN); sd->hstart = 2; sd->vstart = 2; sd->sensor = SENSOR_MT9V111; @@ -1422,8 +1248,6 @@ static void mt9m112_init_sensor(struct gspca_dev *gspca_dev) if (gspca_dev->usb_err < 0) pr_err("MT9M112 sensor initialization failed\n"); - gspca_dev->ctrl_dis = (1 << EXPOSURE) | (1 << AUTOGAIN) - | (1 << GAIN); sd->hstart = 0; sd->vstart = 2; } @@ -1436,8 +1260,6 @@ static void mt9m111_init_sensor(struct gspca_dev *gspca_dev) if (gspca_dev->usb_err < 0) pr_err("MT9M111 sensor initialization failed\n"); - gspca_dev->ctrl_dis = (1 << EXPOSURE) | (1 << AUTOGAIN) - | (1 << GAIN); sd->hstart = 0; sd->vstart = 2; } @@ -1470,8 +1292,6 @@ static void mt9m001_init_sensor(struct gspca_dev *gspca_dev) if (gspca_dev->usb_err < 0) pr_err("MT9M001 sensor initialization failed\n"); - /* disable hflip and vflip */ - gspca_dev->ctrl_dis = (1 << HFLIP) | (1 << VFLIP); sd->hstart = 1; sd->vstart = 1; } @@ -1488,20 +1308,18 @@ static void hv7131r_init_sensor(struct gspca_dev *gspca_dev) sd->vstart = 1; } -static void set_cmatrix(struct gspca_dev *gspca_dev) +static void set_cmatrix(struct gspca_dev *gspca_dev, + s32 brightness, s32 contrast, s32 satur, s32 hue) { - struct sd *sd = (struct sd *) gspca_dev; - int satur; - s32 hue_coord, hue_index = 180 + sd->ctrls[HUE].val; + s32 hue_coord, hue_index = 180 + hue; u8 cmatrix[21]; memset(cmatrix, 0, sizeof cmatrix); - cmatrix[2] = (sd->ctrls[CONTRAST].val * 0x25 / 0x100) + 0x26; + cmatrix[2] = (contrast * 0x25 / 0x100) + 0x26; cmatrix[0] = 0x13 + (cmatrix[2] - 0x26) * 0x13 / 0x25; cmatrix[4] = 0x07 + (cmatrix[2] - 0x26) * 0x07 / 0x25; - cmatrix[18] = sd->ctrls[BRIGHTNESS].val - 0x80; + cmatrix[18] = brightness - 0x80; - satur = sd->ctrls[SATURATION].val; hue_coord = (hsv_red_x[hue_index] * satur) >> 8; cmatrix[6] = hue_coord; cmatrix[7] = (hue_coord >> 8) & 0x0f; @@ -1529,11 +1347,10 @@ static void set_cmatrix(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0x10e1, cmatrix, 21); } -static void set_gamma(struct gspca_dev *gspca_dev) +static void set_gamma(struct gspca_dev *gspca_dev, s32 val) { - struct sd *sd = (struct sd *) gspca_dev; u8 gamma[17]; - u8 gval = sd->ctrls[GAMMA].val * 0xb8 / 0x100; + u8 gval = val * 0xb8 / 0x100; gamma[0] = 0x0a; gamma[1] = 0x13 + (gval * (0xcb - 0x13) / 0xb8); @@ -1556,26 +1373,21 @@ static void set_gamma(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0x1190, gamma, 17); } -static void set_redblue(struct gspca_dev *gspca_dev) +static void set_redblue(struct gspca_dev *gspca_dev, s32 blue, s32 red) { - struct sd *sd = (struct sd *) gspca_dev; - - reg_w1(gspca_dev, 0x118c, sd->ctrls[RED].val); - reg_w1(gspca_dev, 0x118f, sd->ctrls[BLUE].val); + reg_w1(gspca_dev, 0x118c, red); + reg_w1(gspca_dev, 0x118f, blue); } -static void set_hvflip(struct gspca_dev *gspca_dev) +static void set_hvflip(struct gspca_dev *gspca_dev, s32 hflip, s32 vflip) { - u8 value, tslb, hflip, vflip; + u8 value, tslb; u16 value2; struct sd *sd = (struct sd *) gspca_dev; if ((sd->flags & FLIP_DETECT) && dmi_check_system(flip_dmi_table)) { - hflip = !sd->ctrls[HFLIP].val; - vflip = !sd->ctrls[VFLIP].val; - } else { - hflip = sd->ctrls[HFLIP].val; - vflip = sd->ctrls[VFLIP].val; + hflip = !hflip; + vflip = !vflip; } switch (sd->sensor) { @@ -1638,20 +1450,38 @@ static void set_hvflip(struct gspca_dev *gspca_dev) } } -static void set_exposure(struct gspca_dev *gspca_dev) +static void set_exposure(struct gspca_dev *gspca_dev, s32 expo) { struct sd *sd = (struct sd *) gspca_dev; - u8 exp[8] = {0x81, sd->i2c_addr, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e}; - int expo; + u8 exp[8] = {sd->i2c_intf, sd->i2c_addr, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}; + int expo2; + + if (gspca_dev->streaming) + exp[7] = 0x1e; - expo = sd->ctrls[EXPOSURE].val; switch (sd->sensor) { case SENSOR_OV7660: case SENSOR_OV7670: case SENSOR_OV9655: case SENSOR_OV9650: + if (expo > 547) + expo2 = 547; + else + expo2 = expo; + exp[0] |= (2 << 4); + exp[2] = 0x10; /* AECH */ + exp[3] = expo2 >> 2; + exp[7] = 0x10; + i2c_w(gspca_dev, exp); + exp[2] = 0x04; /* COM1 */ + exp[3] = expo2 & 0x0003; + exp[7] = 0x10; + i2c_w(gspca_dev, exp); + expo -= expo2; + exp[7] = 0x1e; exp[0] |= (3 << 4); - exp[2] = 0x2d; + exp[2] = 0x2d; /* ADVFL & ADVFH */ exp[3] = expo; exp[4] = expo >> 8; break; @@ -1676,13 +1506,15 @@ static void set_exposure(struct gspca_dev *gspca_dev) i2c_w(gspca_dev, exp); } -static void set_gain(struct gspca_dev *gspca_dev) +static void set_gain(struct gspca_dev *gspca_dev, s32 g) { struct sd *sd = (struct sd *) gspca_dev; - u8 gain[8] = {0x81, sd->i2c_addr, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d}; - int g; + u8 gain[8] = {sd->i2c_intf, sd->i2c_addr, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}; + + if (gspca_dev->streaming) + gain[7] = 0x15; /* or 1d ? */ - g = sd->ctrls[GAIN].val; switch (sd->sensor) { case SENSOR_OV7660: case SENSOR_OV7670: @@ -1721,11 +1553,11 @@ static void set_gain(struct gspca_dev *gspca_dev) i2c_w(gspca_dev, gain); } -static void set_quality(struct gspca_dev *gspca_dev) +static void set_quality(struct gspca_dev *gspca_dev, s32 val) { struct sd *sd = (struct sd *) gspca_dev; - jpeg_set_qual(sd->jpeg_hdr, sd->ctrls[QUALITY].val); + jpeg_set_qual(sd->jpeg_hdr, val); reg_w1(gspca_dev, 0x1061, 0x01); /* stop transfer */ reg_w1(gspca_dev, 0x10e0, sd->fmt | 0x20); /* write QTAB */ reg_w(gspca_dev, 0x1100, &sd->jpeg_hdr[JPEG_QT0_OFFSET], 64); @@ -1827,6 +1659,7 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->sensor = id->driver_info >> 8; sd->i2c_addr = id->driver_info; sd->flags = id->driver_info >> 16; + sd->i2c_intf = 0x80; /* i2c 100 Kb/s */ switch (sd->sensor) { case SENSOR_MT9M112: @@ -1840,6 +1673,9 @@ static int sd_config(struct gspca_dev *gspca_dev, cam->cam_mode = mono_mode; cam->nmodes = ARRAY_SIZE(mono_mode); break; + case SENSOR_HV7131R: + sd->i2c_intf = 0x81; /* i2c 400 Kb/s */ + /* fall thru */ default: cam->cam_mode = vga_mode; cam->nmodes = ARRAY_SIZE(vga_mode); @@ -1850,13 +1686,133 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->older_step = 0; sd->exposure_step = 16; - gspca_dev->cam.ctrls = sd->ctrls; - INIT_WORK(&sd->work, qual_upd); return 0; } +static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + struct sd *sd = (struct sd *)gspca_dev; + + gspca_dev->usb_err = 0; + + if (!gspca_dev->streaming) + return 0; + + switch (ctrl->id) { + /* color control cluster */ + case V4L2_CID_BRIGHTNESS: + set_cmatrix(gspca_dev, sd->brightness->val, + sd->contrast->val, sd->saturation->val, sd->hue->val); + break; + case V4L2_CID_GAMMA: + set_gamma(gspca_dev, ctrl->val); + break; + /* blue/red balance cluster */ + case V4L2_CID_BLUE_BALANCE: + set_redblue(gspca_dev, sd->blue->val, sd->red->val); + break; + /* h/vflip cluster */ + case V4L2_CID_HFLIP: + set_hvflip(gspca_dev, sd->hflip->val, sd->vflip->val); + break; + /* standalone exposure control */ + case V4L2_CID_EXPOSURE: + set_exposure(gspca_dev, ctrl->val); + break; + /* standalone gain control */ + case V4L2_CID_GAIN: + set_gain(gspca_dev, ctrl->val); + break; + /* autogain + exposure or gain control cluster */ + case V4L2_CID_AUTOGAIN: + if (sd->sensor == SENSOR_SOI968) + set_gain(gspca_dev, sd->gain->val); + else + set_exposure(gspca_dev, sd->exposure->val); + break; + case V4L2_CID_JPEG_COMPRESSION_QUALITY: + set_quality(gspca_dev, ctrl->val); + break; + } + return gspca_dev->usb_err; +} + +static const struct v4l2_ctrl_ops sd_ctrl_ops = { + .s_ctrl = sd_s_ctrl, +}; + +static int sd_init_controls(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; + + gspca_dev->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 13); + + sd->brightness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 127); + sd->contrast = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, 127); + sd->saturation = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_SATURATION, 0, 255, 1, 127); + sd->hue = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_HUE, -180, 180, 1, 0); + v4l2_ctrl_cluster(4, &sd->brightness); + + sd->gamma = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_GAMMA, 0, 255, 1, 0x10); + + sd->blue = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_BLUE_BALANCE, 0, 127, 1, 0x28); + sd->red = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_RED_BALANCE, 0, 127, 1, 0x28); + v4l2_ctrl_cluster(2, &sd->blue); + + if (sd->sensor != SENSOR_OV9655 && sd->sensor != SENSOR_SOI968 && + sd->sensor != SENSOR_OV7670 && sd->sensor != SENSOR_MT9M001 && + sd->sensor != SENSOR_MT9VPRB) { + sd->hflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + sd->vflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_cluster(2, &sd->hflip); + } + + if (sd->sensor != SENSOR_SOI968 && sd->sensor != SENSOR_MT9VPRB && + sd->sensor != SENSOR_MT9M112 && sd->sensor != SENSOR_MT9M111 && + sd->sensor != SENSOR_MT9V111) + sd->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_EXPOSURE, 0, 0x1780, 1, 0x33); + + if (sd->sensor != SENSOR_MT9VPRB && sd->sensor != SENSOR_MT9M112 && + sd->sensor != SENSOR_MT9M111 && sd->sensor != SENSOR_MT9V111) { + sd->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_GAIN, 0, 28, 1, 0); + sd->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, 1); + if (sd->sensor == SENSOR_SOI968) + /* this sensor doesn't have the exposure control and + autogain is clustered with gain instead. This works + because sd->exposure == NULL. */ + v4l2_ctrl_auto_cluster(3, &sd->autogain, 0, false); + else + /* Otherwise autogain is clustered with exposure. */ + v4l2_ctrl_auto_cluster(2, &sd->autogain, 0, false); + } + + sd->jpegqual = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_JPEG_COMPRESSION_QUALITY, 50, 90, 1, 80); + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } + return 0; +} + static int sd_init(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -1949,7 +1905,6 @@ static int sd_init(struct gspca_dev *gspca_dev) pr_err("Unsupported sensor\n"); gspca_dev->usb_err = -ENODEV; } - return gspca_dev->usb_err; } @@ -2025,8 +1980,8 @@ static int sd_isoc_init(struct gspca_dev *gspca_dev) if (intf->num_altsetting != 9) { pr_warn("sn9c20x camera with unknown number of alt " - "settings (%d), please report!\n", - intf->num_altsetting); + "settings (%d), please report!\n", + intf->num_altsetting); gspca_dev->alt = intf->num_altsetting; return 0; } @@ -2067,7 +2022,7 @@ static int sd_start(struct gspca_dev *gspca_dev) jpeg_define(sd->jpeg_hdr, height, width, 0x21); - jpeg_set_qual(sd->jpeg_hdr, sd->ctrls[QUALITY].val); + jpeg_set_qual(sd->jpeg_hdr, v4l2_ctrl_g_ctrl(sd->jpegqual)); if (mode & MODE_RAW) fmt = 0x2d; @@ -2104,12 +2059,17 @@ static int sd_start(struct gspca_dev *gspca_dev) reg_w1(gspca_dev, 0x1189, scale); reg_w1(gspca_dev, 0x10e0, fmt); - set_cmatrix(gspca_dev); - set_gamma(gspca_dev); - set_redblue(gspca_dev); - set_gain(gspca_dev); - set_exposure(gspca_dev); - set_hvflip(gspca_dev); + set_cmatrix(gspca_dev, v4l2_ctrl_g_ctrl(sd->brightness), + v4l2_ctrl_g_ctrl(sd->contrast), + v4l2_ctrl_g_ctrl(sd->saturation), + v4l2_ctrl_g_ctrl(sd->hue)); + set_gamma(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma)); + set_redblue(gspca_dev, v4l2_ctrl_g_ctrl(sd->blue), + v4l2_ctrl_g_ctrl(sd->red)); + set_gain(gspca_dev, v4l2_ctrl_g_ctrl(sd->gain)); + set_exposure(gspca_dev, v4l2_ctrl_g_ctrl(sd->exposure)); + set_hvflip(gspca_dev, v4l2_ctrl_g_ctrl(sd->hflip), + v4l2_ctrl_g_ctrl(sd->vflip)); reg_w1(gspca_dev, 0x1007, 0x20); reg_w1(gspca_dev, 0x1061, 0x03); @@ -2148,6 +2108,9 @@ static void sd_stop0(struct gspca_dev *gspca_dev) static void do_autoexposure(struct gspca_dev *gspca_dev, u16 avg_lum) { struct sd *sd = (struct sd *) gspca_dev; + s32 cur_exp = v4l2_ctrl_g_ctrl(sd->exposure); + s32 max = sd->exposure->maximum - sd->exposure_step; + s32 min = sd->exposure->minimum + sd->exposure_step; s16 new_exp; /* @@ -2156,16 +2119,15 @@ static void do_autoexposure(struct gspca_dev *gspca_dev, u16 avg_lum) * and exposure steps */ if (avg_lum < MIN_AVG_LUM) { - if (sd->ctrls[EXPOSURE].val > 0x1770) + if (cur_exp > max) return; - new_exp = sd->ctrls[EXPOSURE].val + sd->exposure_step; - if (new_exp > 0x1770) - new_exp = 0x1770; - if (new_exp < 0x10) - new_exp = 0x10; - sd->ctrls[EXPOSURE].val = new_exp; - set_exposure(gspca_dev); + new_exp = cur_exp + sd->exposure_step; + if (new_exp > max) + new_exp = max; + if (new_exp < min) + new_exp = min; + v4l2_ctrl_s_ctrl(sd->exposure, new_exp); sd->older_step = sd->old_step; sd->old_step = 1; @@ -2176,15 +2138,14 @@ static void do_autoexposure(struct gspca_dev *gspca_dev, u16 avg_lum) sd->exposure_step += 2; } if (avg_lum > MAX_AVG_LUM) { - if (sd->ctrls[EXPOSURE].val < 0x10) + if (cur_exp < min) return; - new_exp = sd->ctrls[EXPOSURE].val - sd->exposure_step; - if (new_exp > 0x1700) - new_exp = 0x1770; - if (new_exp < 0x10) - new_exp = 0x10; - sd->ctrls[EXPOSURE].val = new_exp; - set_exposure(gspca_dev); + new_exp = cur_exp - sd->exposure_step; + if (new_exp > max) + new_exp = max; + if (new_exp < min) + new_exp = min; + v4l2_ctrl_s_ctrl(sd->exposure, new_exp); sd->older_step = sd->old_step; sd->old_step = 0; @@ -2198,19 +2159,12 @@ static void do_autoexposure(struct gspca_dev *gspca_dev, u16 avg_lum) static void do_autogain(struct gspca_dev *gspca_dev, u16 avg_lum) { struct sd *sd = (struct sd *) gspca_dev; + s32 cur_gain = v4l2_ctrl_g_ctrl(sd->gain); - if (avg_lum < MIN_AVG_LUM) { - if (sd->ctrls[GAIN].val + 1 <= 28) { - sd->ctrls[GAIN].val++; - set_gain(gspca_dev); - } - } - if (avg_lum > MAX_AVG_LUM) { - if (sd->ctrls[GAIN].val > 0) { - sd->ctrls[GAIN].val--; - set_gain(gspca_dev); - } - } + if (avg_lum < MIN_AVG_LUM && cur_gain < sd->gain->maximum) + v4l2_ctrl_s_ctrl(sd->gain, cur_gain + 1); + if (avg_lum > MAX_AVG_LUM && cur_gain > sd->gain->minimum) + v4l2_ctrl_s_ctrl(sd->gain, cur_gain - 1); } static void sd_dqcallback(struct gspca_dev *gspca_dev) @@ -2218,7 +2172,7 @@ static void sd_dqcallback(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; int avg_lum; - if (!sd->ctrls[AUTOGAIN].val) + if (!v4l2_ctrl_g_ctrl(sd->autogain)) return; avg_lum = atomic_read(&sd->avg_lum); @@ -2234,10 +2188,11 @@ static void qual_upd(struct work_struct *work) { struct sd *sd = container_of(work, struct sd, work); struct gspca_dev *gspca_dev = &sd->gspca_dev; + s32 qual = v4l2_ctrl_g_ctrl(sd->jpegqual); mutex_lock(&gspca_dev->usb_lock); - PDEBUG(D_STREAM, "qual_upd %d%%", sd->ctrls[QUALITY].val); - set_quality(gspca_dev); + PDEBUG(D_STREAM, "qual_upd %d%%", qual); + set_quality(gspca_dev, qual); mutex_unlock(&gspca_dev->usb_lock); } @@ -2286,14 +2241,18 @@ static void transfer_check(struct gspca_dev *gspca_dev, if (new_qual != 0) { sd->nchg += new_qual; if (sd->nchg < -6 || sd->nchg >= 12) { + /* Note: we are in interrupt context, so we can't + use v4l2_ctrl_g/s_ctrl here. Access the value + directly instead. */ + s32 curqual = sd->jpegqual->cur.val; sd->nchg = 0; - new_qual += sd->ctrls[QUALITY].val; - if (new_qual < QUALITY_MIN) - new_qual = QUALITY_MIN; - else if (new_qual > QUALITY_MAX) - new_qual = QUALITY_MAX; - if (new_qual != sd->ctrls[QUALITY].val) { - sd->ctrls[QUALITY].val = new_qual; + new_qual += curqual; + if (new_qual < sd->jpegqual->minimum) + new_qual = sd->jpegqual->minimum; + else if (new_qual > sd->jpegqual->maximum) + new_qual = sd->jpegqual->maximum; + if (new_qual != curqual) { + sd->jpegqual->cur.val = new_qual; queue_work(sd->work_thread, &sd->work); } } @@ -2309,7 +2268,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, { struct sd *sd = (struct sd *) gspca_dev; int avg_lum, is_jpeg; - static u8 frame_header[] = + static const u8 frame_header[] = {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96}; is_jpeg = (sd->fmt & 0x03) == 0; @@ -2373,10 +2332,9 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, /* sub-driver description */ static const struct sd_desc sd_desc = { .name = KBUILD_MODNAME, - .ctrls = sd_ctrls, - .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, .init = sd_init, + .init_controls = sd_init_controls, .isoc_init = sd_isoc_init, .start = sd_start, .stopN = sd_stopN, diff --git a/drivers/media/video/gspca/sonixb.c b/drivers/media/video/gspca/sonixb.c index 6a1148d7fe92..e2bdf8f632f4 100644 --- a/drivers/media/video/gspca/sonixb.c +++ b/drivers/media/video/gspca/sonixb.c @@ -1000,6 +1000,8 @@ static void setfreq(struct gspca_dev *gspca_dev) } } +#define WANT_REGULAR_AUTOGAIN +#define WANT_COARSE_EXPO_AUTOGAIN #include "autogain_functions.h" static void do_autogain(struct gspca_dev *gspca_dev) diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c index 863c755dd2b7..4d1696d1a7f4 100644 --- a/drivers/media/video/gspca/sonixj.c +++ b/drivers/media/video/gspca/sonixj.c @@ -2800,10 +2800,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev) } } -/* !! coarse_grained_expo_autogain is not used !! */ -#define exp_too_low_cnt bridge -#define exp_too_high_cnt sensor - +#define WANT_REGULAR_AUTOGAIN #include "autogain_functions.h" static void do_autogain(struct gspca_dev *gspca_dev) diff --git a/drivers/media/video/gspca/sq905.c b/drivers/media/video/gspca/sq905.c index 2fe3c29bd6b7..a144ce759b66 100644 --- a/drivers/media/video/gspca/sq905.c +++ b/drivers/media/video/gspca/sq905.c @@ -232,7 +232,7 @@ static void sq905_dostream(struct work_struct *work) frame_sz = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].sizeimage + FRAME_HEADER_LEN; - while (gspca_dev->present && gspca_dev->streaming) { + while (!gspca_dev->frozen && gspca_dev->dev && gspca_dev->streaming) { /* request some data and then read it until we have * a complete frame. */ bytes_left = frame_sz; @@ -242,7 +242,7 @@ static void sq905_dostream(struct work_struct *work) we must finish reading an entire frame, otherwise the next time we stream we start reading in the middle of a frame. */ - while (bytes_left > 0 && gspca_dev->present) { + while (bytes_left > 0 && gspca_dev->dev) { data_len = bytes_left > SQ905_MAX_TRANSFER ? SQ905_MAX_TRANSFER : bytes_left; ret = sq905_read_data(gspca_dev, buffer, data_len, 1); @@ -274,7 +274,7 @@ static void sq905_dostream(struct work_struct *work) gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); } - if (gspca_dev->present) { + if (gspca_dev->dev) { /* acknowledge the frame */ mutex_lock(&gspca_dev->usb_lock); ret = sq905_ack_frame(gspca_dev); @@ -284,7 +284,7 @@ static void sq905_dostream(struct work_struct *work) } } quit_stream: - if (gspca_dev->present) { + if (gspca_dev->dev) { mutex_lock(&gspca_dev->usb_lock); sq905_command(gspca_dev, SQ905_CLEAR); mutex_unlock(&gspca_dev->usb_lock); diff --git a/drivers/media/video/gspca/sq905c.c b/drivers/media/video/gspca/sq905c.c index ae783634712f..720c187f6ec7 100644 --- a/drivers/media/video/gspca/sq905c.c +++ b/drivers/media/video/gspca/sq905c.c @@ -150,7 +150,7 @@ static void sq905c_dostream(struct work_struct *work) goto quit_stream; } - while (gspca_dev->present && gspca_dev->streaming) { + while (!gspca_dev->frozen && gspca_dev->dev && gspca_dev->streaming) { /* Request the header, which tells the size to download */ ret = usb_bulk_msg(gspca_dev->dev, usb_rcvbulkpipe(gspca_dev->dev, 0x81), @@ -169,7 +169,7 @@ static void sq905c_dostream(struct work_struct *work) packet_type = FIRST_PACKET; gspca_frame_add(gspca_dev, packet_type, buffer, FRAME_HEADER_LEN); - while (bytes_left > 0 && gspca_dev->present) { + while (bytes_left > 0 && gspca_dev->dev) { data_len = bytes_left > SQ905C_MAX_TRANSFER ? SQ905C_MAX_TRANSFER : bytes_left; ret = usb_bulk_msg(gspca_dev->dev, @@ -191,7 +191,7 @@ static void sq905c_dostream(struct work_struct *work) } } quit_stream: - if (gspca_dev->present) { + if (gspca_dev->dev) { mutex_lock(&gspca_dev->usb_lock); sq905c_command(gspca_dev, SQ905C_CLEAR, 0); mutex_unlock(&gspca_dev->usb_lock); diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.c b/drivers/media/video/gspca/stv06xx/stv06xx.c index 91d99b4cc57b..999ec7764449 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx.c +++ b/drivers/media/video/gspca/stv06xx/stv06xx.c @@ -261,6 +261,17 @@ static int stv06xx_init(struct gspca_dev *gspca_dev) return (err < 0) ? err : 0; } +/* this function is called at probe time */ +static int stv06xx_init_controls(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + PDEBUG(D_PROBE, "Initializing controls"); + + gspca_dev->vdev.ctrl_handler = &gspca_dev->ctrl_handler; + return sd->sensor->init_controls(sd); +} + /* Start the camera */ static int stv06xx_start(struct gspca_dev *gspca_dev) { @@ -512,6 +523,7 @@ static const struct sd_desc sd_desc = { .name = MODULE_NAME, .config = stv06xx_config, .init = stv06xx_init, + .init_controls = stv06xx_init_controls, .start = stv06xx_start, .stopN = stv06xx_stopN, .pkt_scan = stv06xx_pkt_scan, @@ -530,9 +542,8 @@ static int stv06xx_config(struct gspca_dev *gspca_dev, PDEBUG(D_PROBE, "Configuring camera"); - sd->desc = sd_desc; sd->bridge = id->driver_info; - gspca_dev->sd_desc = &sd->desc; + gspca_dev->sd_desc = &sd_desc; if (dump_bridge) stv06xx_dump_bridge(sd); @@ -594,11 +605,12 @@ static void sd_disconnect(struct usb_interface *intf) { struct gspca_dev *gspca_dev = usb_get_intfdata(intf); struct sd *sd = (struct sd *) gspca_dev; + void *priv = sd->sensor_priv; PDEBUG(D_PROBE, "Disconnecting the stv06xx device"); - if (sd->sensor->disconnect) - sd->sensor->disconnect(sd); + sd->sensor = NULL; gspca_disconnect(intf); + kfree(priv); } static struct usb_driver sd_driver = { @@ -609,6 +621,7 @@ static struct usb_driver sd_driver = { #ifdef CONFIG_PM .suspend = gspca_suspend, .resume = gspca_resume, + .reset_resume = gspca_resume, #endif }; diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.h b/drivers/media/video/gspca/stv06xx/stv06xx.h index d270a5981afe..34957a4ec150 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx.h +++ b/drivers/media/video/gspca/stv06xx/stv06xx.h @@ -89,9 +89,6 @@ struct sd { /* A pointer to the currently connected sensor */ const struct stv06xx_sensor *sensor; - /* A pointer to the sd_desc struct */ - struct sd_desc desc; - /* Sensor private data */ void *sensor_priv; diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c index a8698b7a7566..06fa54c5efb2 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c +++ b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c @@ -32,36 +32,6 @@ #include "stv06xx_hdcs.h" -static const struct ctrl hdcs1x00_ctrl[] = { - { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0x00, - .maximum = 0xff, - .step = 0x1, - .default_value = HDCS_DEFAULT_EXPOSURE, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = hdcs_set_exposure, - .get = hdcs_get_exposure - }, { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "gain", - .minimum = 0x00, - .maximum = 0xff, - .step = 0x1, - .default_value = HDCS_DEFAULT_GAIN, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = hdcs_set_gain, - .get = hdcs_get_gain - } -}; - static struct v4l2_pix_format hdcs1x00_mode[] = { { HDCS_1X00_DEF_WIDTH, @@ -76,36 +46,6 @@ static struct v4l2_pix_format hdcs1x00_mode[] = { } }; -static const struct ctrl hdcs1020_ctrl[] = { - { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0x00, - .maximum = 0xffff, - .step = 0x1, - .default_value = HDCS_DEFAULT_EXPOSURE, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = hdcs_set_exposure, - .get = hdcs_get_exposure - }, { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "gain", - .minimum = 0x00, - .maximum = 0xff, - .step = 0x1, - .default_value = HDCS_DEFAULT_GAIN, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = hdcs_set_gain, - .get = hdcs_get_gain - } -}; - static struct v4l2_pix_format hdcs1020_mode[] = { { HDCS_1020_DEF_WIDTH, @@ -150,7 +90,6 @@ struct hdcs { } exp; int psmp; - u8 exp_cache, gain_cache; }; static int hdcs_reg_write_seq(struct sd *sd, u8 reg, u8 *vals, u8 len) @@ -232,16 +171,6 @@ static int hdcs_reset(struct sd *sd) return err; } -static int hdcs_get_exposure(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct hdcs *hdcs = sd->sensor_priv; - - *val = hdcs->exp_cache; - - return 0; -} - static int hdcs_set_exposure(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; @@ -260,9 +189,6 @@ static int hdcs_set_exposure(struct gspca_dev *gspca_dev, __s32 val) int cycles, err; u8 exp[14]; - val &= 0xff; - hdcs->exp_cache = val; - cycles = val * HDCS_CLK_FREQ_MHZ * 257; ct = hdcs->exp.cto + hdcs->psmp + (HDCS_ADC_START_SIG_DUR + 2); @@ -336,12 +262,9 @@ static int hdcs_set_exposure(struct gspca_dev *gspca_dev, __s32 val) static int hdcs_set_gains(struct sd *sd, u8 g) { - struct hdcs *hdcs = sd->sensor_priv; int err; u8 gains[4]; - hdcs->gain_cache = g; - /* the voltage gain Av = (1 + 19 * val / 127) * (1 + bit7) */ if (g > 127) g = 0x80 | (g / 2); @@ -352,17 +275,7 @@ static int hdcs_set_gains(struct sd *sd, u8 g) gains[3] = g; err = hdcs_reg_write_seq(sd, HDCS_ERECPGA, gains, 4); - return err; -} - -static int hdcs_get_gain(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct hdcs *hdcs = sd->sensor_priv; - - *val = hdcs->gain_cache; - - return 0; + return err; } static int hdcs_set_gain(struct gspca_dev *gspca_dev, __s32 val) @@ -420,6 +333,39 @@ static int hdcs_set_size(struct sd *sd, return err; } +static int hdcs_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + int err = -EINVAL; + + switch (ctrl->id) { + case V4L2_CID_GAIN: + err = hdcs_set_gain(gspca_dev, ctrl->val); + break; + case V4L2_CID_EXPOSURE: + err = hdcs_set_exposure(gspca_dev, ctrl->val); + break; + } + return err; +} + +static const struct v4l2_ctrl_ops hdcs_ctrl_ops = { + .s_ctrl = hdcs_s_ctrl, +}; + +static int hdcs_init_controls(struct sd *sd) +{ + struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler; + + v4l2_ctrl_handler_init(hdl, 2); + v4l2_ctrl_new_std(hdl, &hdcs_ctrl_ops, + V4L2_CID_EXPOSURE, 0, 0xff, 1, HDCS_DEFAULT_EXPOSURE); + v4l2_ctrl_new_std(hdl, &hdcs_ctrl_ops, + V4L2_CID_GAIN, 0, 0xff, 1, HDCS_DEFAULT_GAIN); + return hdl->error; +} + static int hdcs_probe_1x00(struct sd *sd) { struct hdcs *hdcs; @@ -434,8 +380,6 @@ static int hdcs_probe_1x00(struct sd *sd) sd->gspca_dev.cam.cam_mode = hdcs1x00_mode; sd->gspca_dev.cam.nmodes = ARRAY_SIZE(hdcs1x00_mode); - sd->desc.ctrls = hdcs1x00_ctrl; - sd->desc.nctrls = ARRAY_SIZE(hdcs1x00_ctrl); hdcs = kmalloc(sizeof(struct hdcs), GFP_KERNEL); if (!hdcs) @@ -493,8 +437,6 @@ static int hdcs_probe_1020(struct sd *sd) sd->gspca_dev.cam.cam_mode = hdcs1020_mode; sd->gspca_dev.cam.nmodes = ARRAY_SIZE(hdcs1020_mode); - sd->desc.ctrls = hdcs1020_ctrl; - sd->desc.nctrls = ARRAY_SIZE(hdcs1020_ctrl); hdcs = kmalloc(sizeof(struct hdcs), GFP_KERNEL); if (!hdcs) @@ -537,12 +479,6 @@ static int hdcs_stop(struct sd *sd) return hdcs_set_state(sd, HDCS_STATE_SLEEP); } -static void hdcs_disconnect(struct sd *sd) -{ - PDEBUG(D_PROBE, "Disconnecting the sensor"); - kfree(sd->sensor_priv); -} - static int hdcs_init(struct sd *sd) { struct hdcs *hdcs = sd->sensor_priv; @@ -587,16 +523,7 @@ static int hdcs_init(struct sd *sd) if (err < 0) return err; - err = hdcs_set_gains(sd, HDCS_DEFAULT_GAIN); - if (err < 0) - return err; - - err = hdcs_set_size(sd, hdcs->array.width, hdcs->array.height); - if (err < 0) - return err; - - err = hdcs_set_exposure(&sd->gspca_dev, HDCS_DEFAULT_EXPOSURE); - return err; + return hdcs_set_size(sd, hdcs->array.width, hdcs->array.height); } static int hdcs_dump(struct sd *sd) diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h index a14a84a5079b..1ba9158d0102 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h +++ b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h @@ -131,14 +131,12 @@ static int hdcs_probe_1x00(struct sd *sd); static int hdcs_probe_1020(struct sd *sd); static int hdcs_start(struct sd *sd); static int hdcs_init(struct sd *sd); +static int hdcs_init_controls(struct sd *sd); static int hdcs_stop(struct sd *sd); static int hdcs_dump(struct sd *sd); -static void hdcs_disconnect(struct sd *sd); -static int hdcs_get_exposure(struct gspca_dev *gspca_dev, __s32 *val); static int hdcs_set_exposure(struct gspca_dev *gspca_dev, __s32 val); static int hdcs_set_gain(struct gspca_dev *gspca_dev, __s32 val); -static int hdcs_get_gain(struct gspca_dev *gspca_dev, __s32 *val); const struct stv06xx_sensor stv06xx_sensor_hdcs1x00 = { .name = "HP HDCS-1000/1100", @@ -152,10 +150,10 @@ const struct stv06xx_sensor stv06xx_sensor_hdcs1x00 = { .max_packet_size = { 847 }, .init = hdcs_init, + .init_controls = hdcs_init_controls, .probe = hdcs_probe_1x00, .start = hdcs_start, .stop = hdcs_stop, - .disconnect = hdcs_disconnect, .dump = hdcs_dump, }; @@ -171,6 +169,7 @@ const struct stv06xx_sensor stv06xx_sensor_hdcs1020 = { .max_packet_size = { 847 }, .init = hdcs_init, + .init_controls = hdcs_init_controls, .probe = hdcs_probe_1020, .start = hdcs_start, .stop = hdcs_stop, diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c b/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c index 26f14fc4a135..cdfc3d05ab6b 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c +++ b/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c @@ -48,105 +48,16 @@ #include "stv06xx_pb0100.h" -static const struct ctrl pb0100_ctrl[] = { -#define GAIN_IDX 0 - { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gain", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = 128 - }, - .set = pb0100_set_gain, - .get = pb0100_get_gain - }, -#define RED_BALANCE_IDX 1 - { - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Red Balance", - .minimum = -255, - .maximum = 255, - .step = 1, - .default_value = 0 - }, - .set = pb0100_set_red_balance, - .get = pb0100_get_red_balance - }, -#define BLUE_BALANCE_IDX 2 - { - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Blue Balance", - .minimum = -255, - .maximum = 255, - .step = 1, - .default_value = 0 - }, - .set = pb0100_set_blue_balance, - .get = pb0100_get_blue_balance - }, -#define EXPOSURE_IDX 3 - { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Exposure", - .minimum = 0, - .maximum = 511, - .step = 1, - .default_value = 12 - }, - .set = pb0100_set_exposure, - .get = pb0100_get_exposure - }, -#define AUTOGAIN_IDX 4 - { - { - .id = V4L2_CID_AUTOGAIN, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Automatic Gain and Exposure", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1 - }, - .set = pb0100_set_autogain, - .get = pb0100_get_autogain - }, -#define AUTOGAIN_TARGET_IDX 5 - { - { - .id = V4L2_CTRL_CLASS_USER + 0x1000, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Automatic Gain Target", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = 128 - }, - .set = pb0100_set_autogain_target, - .get = pb0100_get_autogain_target - }, -#define NATURAL_IDX 6 - { - { - .id = V4L2_CTRL_CLASS_USER + 0x1001, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Natural Light Source", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1 - }, - .set = pb0100_set_natural, - .get = pb0100_get_natural - } +struct pb0100_ctrls { + struct { /* one big happy control cluster... */ + struct v4l2_ctrl *autogain; + struct v4l2_ctrl *gain; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *red; + struct v4l2_ctrl *blue; + struct v4l2_ctrl *natural; + }; + struct v4l2_ctrl *target; }; static struct v4l2_pix_format pb0100_mode[] = { @@ -174,38 +85,104 @@ static struct v4l2_pix_format pb0100_mode[] = { } }; +static int pb0100_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + struct sd *sd = (struct sd *)gspca_dev; + struct pb0100_ctrls *ctrls = sd->sensor_priv; + int err = -EINVAL; + + switch (ctrl->id) { + case V4L2_CID_AUTOGAIN: + err = pb0100_set_autogain(gspca_dev, ctrl->val); + if (err) + break; + if (ctrl->val) + break; + err = pb0100_set_gain(gspca_dev, ctrls->gain->val); + if (err) + break; + err = pb0100_set_exposure(gspca_dev, ctrls->exposure->val); + break; + case V4L2_CTRL_CLASS_USER + 0x1001: + err = pb0100_set_autogain_target(gspca_dev, ctrl->val); + break; + } + return err; +} + +static const struct v4l2_ctrl_ops pb0100_ctrl_ops = { + .s_ctrl = pb0100_s_ctrl, +}; + +static int pb0100_init_controls(struct sd *sd) +{ + struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler; + struct pb0100_ctrls *ctrls; + static const struct v4l2_ctrl_config autogain_target = { + .ops = &pb0100_ctrl_ops, + .id = V4L2_CTRL_CLASS_USER + 0x1000, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Automatic Gain Target", + .max = 255, + .step = 1, + .def = 128, + }; + static const struct v4l2_ctrl_config natural_light = { + .ops = &pb0100_ctrl_ops, + .id = V4L2_CTRL_CLASS_USER + 0x1001, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Natural Light Source", + .max = 1, + .step = 1, + .def = 1, + }; + + ctrls = kzalloc(sizeof(*ctrls), GFP_KERNEL); + if (!ctrls) + return -ENOMEM; + + v4l2_ctrl_handler_init(hdl, 6); + ctrls->autogain = v4l2_ctrl_new_std(hdl, &pb0100_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, 1); + ctrls->exposure = v4l2_ctrl_new_std(hdl, &pb0100_ctrl_ops, + V4L2_CID_EXPOSURE, 0, 511, 1, 12); + ctrls->gain = v4l2_ctrl_new_std(hdl, &pb0100_ctrl_ops, + V4L2_CID_GAIN, 0, 255, 1, 128); + ctrls->red = v4l2_ctrl_new_std(hdl, &pb0100_ctrl_ops, + V4L2_CID_RED_BALANCE, -255, 255, 1, 0); + ctrls->blue = v4l2_ctrl_new_std(hdl, &pb0100_ctrl_ops, + V4L2_CID_BLUE_BALANCE, -255, 255, 1, 0); + ctrls->natural = v4l2_ctrl_new_custom(hdl, &natural_light, NULL); + ctrls->target = v4l2_ctrl_new_custom(hdl, &autogain_target, NULL); + if (hdl->error) { + kfree(ctrls); + return hdl->error; + } + sd->sensor_priv = ctrls; + v4l2_ctrl_auto_cluster(5, &ctrls->autogain, 0, false); + return 0; +} + static int pb0100_probe(struct sd *sd) { u16 sensor; - int i, err; - s32 *sensor_settings; + int err; err = stv06xx_read_sensor(sd, PB_IDENT, &sensor); if (err < 0) return -ENODEV; + if ((sensor >> 8) != 0x64) + return -ENODEV; - if ((sensor >> 8) == 0x64) { - sensor_settings = kmalloc( - ARRAY_SIZE(pb0100_ctrl) * sizeof(s32), - GFP_KERNEL); - if (!sensor_settings) - return -ENOMEM; - - pr_info("Photobit pb0100 sensor detected\n"); - - sd->gspca_dev.cam.cam_mode = pb0100_mode; - sd->gspca_dev.cam.nmodes = ARRAY_SIZE(pb0100_mode); - sd->desc.ctrls = pb0100_ctrl; - sd->desc.nctrls = ARRAY_SIZE(pb0100_ctrl); - for (i = 0; i < sd->desc.nctrls; i++) - sensor_settings[i] = pb0100_ctrl[i].qctrl.default_value; - sd->sensor_priv = sensor_settings; + pr_info("Photobit pb0100 sensor detected\n"); - return 0; - } + sd->gspca_dev.cam.cam_mode = pb0100_mode; + sd->gspca_dev.cam.nmodes = ARRAY_SIZE(pb0100_mode); - return -ENODEV; + return 0; } static int pb0100_start(struct sd *sd) @@ -214,7 +191,6 @@ static int pb0100_start(struct sd *sd) struct usb_host_interface *alt; struct usb_interface *intf; struct cam *cam = &sd->gspca_dev.cam; - s32 *sensor_settings = sd->sensor_priv; u32 mode = cam->cam_mode[sd->gspca_dev.curr_mode].priv; intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface); @@ -255,13 +231,6 @@ static int pb0100_start(struct sd *sd) stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x20); } - /* set_gain also sets red and blue balance */ - pb0100_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]); - pb0100_set_exposure(&sd->gspca_dev, sensor_settings[EXPOSURE_IDX]); - pb0100_set_autogain_target(&sd->gspca_dev, - sensor_settings[AUTOGAIN_TARGET_IDX]); - pb0100_set_autogain(&sd->gspca_dev, sensor_settings[AUTOGAIN_IDX]); - err = stv06xx_write_sensor(sd, PB_CONTROL, BIT(5)|BIT(3)|BIT(1)); PDEBUG(D_STREAM, "Started stream, status: %d", err); @@ -285,12 +254,6 @@ out: return (err < 0) ? err : 0; } -static void pb0100_disconnect(struct sd *sd) -{ - sd->sensor = NULL; - kfree(sd->sensor_priv); -} - /* FIXME: Sort the init commands out and put them into tables, this is only for getting the camera to work */ /* FIXME: No error handling for now, @@ -362,62 +325,32 @@ static int pb0100_dump(struct sd *sd) return 0; } -static int pb0100_get_gain(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[GAIN_IDX]; - - return 0; -} - static int pb0100_set_gain(struct gspca_dev *gspca_dev, __s32 val) { int err; struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; + struct pb0100_ctrls *ctrls = sd->sensor_priv; - if (sensor_settings[AUTOGAIN_IDX]) - return -EBUSY; - - sensor_settings[GAIN_IDX] = val; err = stv06xx_write_sensor(sd, PB_G1GAIN, val); if (!err) err = stv06xx_write_sensor(sd, PB_G2GAIN, val); PDEBUG(D_V4L2, "Set green gain to %d, status: %d", val, err); if (!err) - err = pb0100_set_red_balance(gspca_dev, - sensor_settings[RED_BALANCE_IDX]); + err = pb0100_set_red_balance(gspca_dev, ctrls->red->val); if (!err) - err = pb0100_set_blue_balance(gspca_dev, - sensor_settings[BLUE_BALANCE_IDX]); + err = pb0100_set_blue_balance(gspca_dev, ctrls->blue->val); return err; } -static int pb0100_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[RED_BALANCE_IDX]; - - return 0; -} - static int pb0100_set_red_balance(struct gspca_dev *gspca_dev, __s32 val) { int err; struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; + struct pb0100_ctrls *ctrls = sd->sensor_priv; - if (sensor_settings[AUTOGAIN_IDX]) - return -EBUSY; - - sensor_settings[RED_BALANCE_IDX] = val; - val += sensor_settings[GAIN_IDX]; + val += ctrls->gain->val; if (val < 0) val = 0; else if (val > 255) @@ -429,27 +362,13 @@ static int pb0100_set_red_balance(struct gspca_dev *gspca_dev, __s32 val) return err; } -static int pb0100_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[BLUE_BALANCE_IDX]; - - return 0; -} - static int pb0100_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val) { int err; struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; + struct pb0100_ctrls *ctrls = sd->sensor_priv; - if (sensor_settings[AUTOGAIN_IDX]) - return -EBUSY; - - sensor_settings[BLUE_BALANCE_IDX] = val; - val += sensor_settings[GAIN_IDX]; + val += ctrls->gain->val; if (val < 0) val = 0; else if (val > 255) @@ -461,51 +380,25 @@ static int pb0100_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val) return err; } -static int pb0100_get_exposure(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[EXPOSURE_IDX]; - - return 0; -} - static int pb0100_set_exposure(struct gspca_dev *gspca_dev, __s32 val) { - int err; struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - if (sensor_settings[AUTOGAIN_IDX]) - return -EBUSY; + int err; - sensor_settings[EXPOSURE_IDX] = val; err = stv06xx_write_sensor(sd, PB_RINTTIME, val); PDEBUG(D_V4L2, "Set exposure to %d, status: %d", val, err); return err; } -static int pb0100_get_autogain(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[AUTOGAIN_IDX]; - - return 0; -} - static int pb0100_set_autogain(struct gspca_dev *gspca_dev, __s32 val) { int err; struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; + struct pb0100_ctrls *ctrls = sd->sensor_priv; - sensor_settings[AUTOGAIN_IDX] = val; - if (sensor_settings[AUTOGAIN_IDX]) { - if (sensor_settings[NATURAL_IDX]) + if (val) { + if (ctrls->natural->val) val = BIT(6)|BIT(4)|BIT(0); else val = BIT(4)|BIT(0); @@ -514,29 +407,15 @@ static int pb0100_set_autogain(struct gspca_dev *gspca_dev, __s32 val) err = stv06xx_write_sensor(sd, PB_EXPGAIN, val); PDEBUG(D_V4L2, "Set autogain to %d (natural: %d), status: %d", - sensor_settings[AUTOGAIN_IDX], sensor_settings[NATURAL_IDX], - err); + val, ctrls->natural->val, err); return err; } -static int pb0100_get_autogain_target(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[AUTOGAIN_TARGET_IDX]; - - return 0; -} - static int pb0100_set_autogain_target(struct gspca_dev *gspca_dev, __s32 val) { int err, totalpixels, brightpixels, darkpixels; struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - sensor_settings[AUTOGAIN_TARGET_IDX] = val; /* Number of pixels counted by the sensor when subsampling the pixels. * Slightly larger than the real value to avoid oscillation */ @@ -553,23 +432,3 @@ static int pb0100_set_autogain_target(struct gspca_dev *gspca_dev, __s32 val) return err; } - -static int pb0100_get_natural(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[NATURAL_IDX]; - - return 0; -} - -static int pb0100_set_natural(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - sensor_settings[NATURAL_IDX] = val; - - return pb0100_set_autogain(gspca_dev, sensor_settings[AUTOGAIN_IDX]); -} diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h b/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h index 757de246dc75..5071e5353fd3 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h +++ b/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h @@ -112,25 +112,17 @@ static int pb0100_probe(struct sd *sd); static int pb0100_start(struct sd *sd); static int pb0100_init(struct sd *sd); +static int pb0100_init_controls(struct sd *sd); static int pb0100_stop(struct sd *sd); static int pb0100_dump(struct sd *sd); -static void pb0100_disconnect(struct sd *sd); /* V4L2 controls supported by the driver */ -static int pb0100_get_gain(struct gspca_dev *gspca_dev, __s32 *val); static int pb0100_set_gain(struct gspca_dev *gspca_dev, __s32 val); -static int pb0100_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val); static int pb0100_set_red_balance(struct gspca_dev *gspca_dev, __s32 val); -static int pb0100_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val); static int pb0100_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val); -static int pb0100_get_exposure(struct gspca_dev *gspca_dev, __s32 *val); static int pb0100_set_exposure(struct gspca_dev *gspca_dev, __s32 val); -static int pb0100_get_autogain(struct gspca_dev *gspca_dev, __s32 *val); static int pb0100_set_autogain(struct gspca_dev *gspca_dev, __s32 val); -static int pb0100_get_autogain_target(struct gspca_dev *gspca_dev, __s32 *val); static int pb0100_set_autogain_target(struct gspca_dev *gspca_dev, __s32 val); -static int pb0100_get_natural(struct gspca_dev *gspca_dev, __s32 *val); -static int pb0100_set_natural(struct gspca_dev *gspca_dev, __s32 val); const struct stv06xx_sensor stv06xx_sensor_pb0100 = { .name = "PB-0100", @@ -142,11 +134,11 @@ const struct stv06xx_sensor stv06xx_sensor_pb0100 = { .max_packet_size = { 847, 923 }, .init = pb0100_init, + .init_controls = pb0100_init_controls, .probe = pb0100_probe, .start = pb0100_start, .stop = pb0100_stop, .dump = pb0100_dump, - .disconnect = pb0100_disconnect, }; #endif diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h b/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h index fb229d8ded58..3a498c2495c6 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h +++ b/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h @@ -63,8 +63,8 @@ struct stv06xx_sensor { /* Performs a initialization sequence */ int (*init)(struct sd *sd); - /* Executed at device disconnect */ - void (*disconnect)(struct sd *sd); + /* Initializes the controls */ + int (*init_controls)(struct sd *sd); /* Reads a sensor register */ int (*read_sensor)(struct sd *sd, const u8 address, diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c b/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c index 9940e035b3ab..8a57990dfe0f 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c +++ b/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c @@ -30,20 +30,6 @@ #include "stv06xx_st6422.h" -/* controls */ -enum e_ctrl { - BRIGHTNESS, - CONTRAST, - GAIN, - EXPOSURE, - NCTRLS /* number of controls */ -}; - -/* sensor settings */ -struct st6422_settings { - struct gspca_ctrl ctrls[NCTRLS]; -}; - static struct v4l2_pix_format st6422_mode[] = { /* Note we actually get 124 lines of data, of which we skip the 4st 4 as they are garbage */ @@ -74,83 +60,70 @@ static struct v4l2_pix_format st6422_mode[] = { }; /* V4L2 controls supported by the driver */ -static void st6422_set_brightness(struct gspca_dev *gspca_dev); -static void st6422_set_contrast(struct gspca_dev *gspca_dev); -static void st6422_set_gain(struct gspca_dev *gspca_dev); -static void st6422_set_exposure(struct gspca_dev *gspca_dev); - -static const struct ctrl st6422_ctrl[NCTRLS] = { -[BRIGHTNESS] = { - { - .id = V4L2_CID_BRIGHTNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Brightness", - .minimum = 0, - .maximum = 31, - .step = 1, - .default_value = 3 - }, - .set_control = st6422_set_brightness - }, -[CONTRAST] = { - { - .id = V4L2_CID_CONTRAST, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Contrast", - .minimum = 0, - .maximum = 15, - .step = 1, - .default_value = 11 - }, - .set_control = st6422_set_contrast - }, -[GAIN] = { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gain", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = 64 - }, - .set_control = st6422_set_gain - }, -[EXPOSURE] = { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Exposure", - .minimum = 0, -#define EXPOSURE_MAX 1023 - .maximum = EXPOSURE_MAX, - .step = 1, - .default_value = 256 - }, - .set_control = st6422_set_exposure - }, +static int setbrightness(struct sd *sd, s32 val); +static int setcontrast(struct sd *sd, s32 val); +static int setgain(struct sd *sd, u8 gain); +static int setexposure(struct sd *sd, s16 expo); + +static int st6422_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + struct sd *sd = (struct sd *)gspca_dev; + int err = -EINVAL; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + err = setbrightness(sd, ctrl->val); + break; + case V4L2_CID_CONTRAST: + err = setcontrast(sd, ctrl->val); + break; + case V4L2_CID_GAIN: + err = setgain(sd, ctrl->val); + break; + case V4L2_CID_EXPOSURE: + err = setexposure(sd, ctrl->val); + break; + } + + /* commit settings */ + if (err >= 0) + err = stv06xx_write_bridge(sd, 0x143f, 0x01); + sd->gspca_dev.usb_err = err; + return err; +} + +static const struct v4l2_ctrl_ops st6422_ctrl_ops = { + .s_ctrl = st6422_s_ctrl, }; -static int st6422_probe(struct sd *sd) +static int st6422_init_controls(struct sd *sd) { - struct st6422_settings *sensor_settings; + struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler; + + v4l2_ctrl_handler_init(hdl, 4); + v4l2_ctrl_new_std(hdl, &st6422_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 31, 1, 3); + v4l2_ctrl_new_std(hdl, &st6422_ctrl_ops, + V4L2_CID_CONTRAST, 0, 15, 1, 11); + v4l2_ctrl_new_std(hdl, &st6422_ctrl_ops, + V4L2_CID_EXPOSURE, 0, 1023, 1, 256); + v4l2_ctrl_new_std(hdl, &st6422_ctrl_ops, + V4L2_CID_GAIN, 0, 255, 1, 64); + + return hdl->error; +} +static int st6422_probe(struct sd *sd) +{ if (sd->bridge != BRIDGE_ST6422) return -ENODEV; pr_info("st6422 sensor detected\n"); - sensor_settings = kmalloc(sizeof *sensor_settings, GFP_KERNEL); - if (!sensor_settings) - return -ENOMEM; - sd->gspca_dev.cam.cam_mode = st6422_mode; sd->gspca_dev.cam.nmodes = ARRAY_SIZE(st6422_mode); - sd->gspca_dev.cam.ctrls = sensor_settings->ctrls; - sd->desc.ctrls = st6422_ctrl; - sd->desc.nctrls = ARRAY_SIZE(st6422_ctrl); - sd->sensor_priv = sensor_settings; - return 0; } @@ -239,38 +212,22 @@ static int st6422_init(struct sd *sd) return err; } -static void st6422_disconnect(struct sd *sd) -{ - sd->sensor = NULL; - kfree(sd->sensor_priv); -} - -static int setbrightness(struct sd *sd) +static int setbrightness(struct sd *sd, s32 val) { - struct st6422_settings *sensor_settings = sd->sensor_priv; - /* val goes from 0 -> 31 */ - return stv06xx_write_bridge(sd, 0x1432, - sensor_settings->ctrls[BRIGHTNESS].val); + return stv06xx_write_bridge(sd, 0x1432, val); } -static int setcontrast(struct sd *sd) +static int setcontrast(struct sd *sd, s32 val) { - struct st6422_settings *sensor_settings = sd->sensor_priv; - /* Val goes from 0 -> 15 */ - return stv06xx_write_bridge(sd, 0x143a, - sensor_settings->ctrls[CONTRAST].val | 0xf0); + return stv06xx_write_bridge(sd, 0x143a, val | 0xf0); } -static int setgain(struct sd *sd) +static int setgain(struct sd *sd, u8 gain) { - struct st6422_settings *sensor_settings = sd->sensor_priv; - u8 gain; int err; - gain = sensor_settings->ctrls[GAIN].val; - /* Set red, green, blue, gain */ err = stv06xx_write_bridge(sd, 0x0509, gain); if (err < 0) @@ -292,13 +249,10 @@ static int setgain(struct sd *sd) return stv06xx_write_bridge(sd, 0x050d, 0x01); } -static int setexposure(struct sd *sd) +static int setexposure(struct sd *sd, s16 expo) { - struct st6422_settings *sensor_settings = sd->sensor_priv; - u16 expo; int err; - expo = sensor_settings->ctrls[EXPOSURE].val; err = stv06xx_write_bridge(sd, 0x143d, expo & 0xff); if (err < 0) return err; @@ -318,22 +272,6 @@ static int st6422_start(struct sd *sd) if (err < 0) return err; - err = setbrightness(sd); - if (err < 0) - return err; - - err = setcontrast(sd); - if (err < 0) - return err; - - err = setexposure(sd); - if (err < 0) - return err; - - err = setgain(sd); - if (err < 0) - return err; - /* commit settings */ err = stv06xx_write_bridge(sd, 0x143f, 0x01); return (err < 0) ? err : 0; @@ -345,59 +283,3 @@ static int st6422_stop(struct sd *sd) return 0; } - -static void st6422_set_brightness(struct gspca_dev *gspca_dev) -{ - int err; - struct sd *sd = (struct sd *) gspca_dev; - - err = setbrightness(sd); - - /* commit settings */ - if (err >= 0) - err = stv06xx_write_bridge(sd, 0x143f, 0x01); - - gspca_dev->usb_err = err; -} - -static void st6422_set_contrast(struct gspca_dev *gspca_dev) -{ - int err; - struct sd *sd = (struct sd *) gspca_dev; - - err = setcontrast(sd); - - /* commit settings */ - if (err >= 0) - err = stv06xx_write_bridge(sd, 0x143f, 0x01); - - gspca_dev->usb_err = err; -} - -static void st6422_set_gain(struct gspca_dev *gspca_dev) -{ - int err; - struct sd *sd = (struct sd *) gspca_dev; - - err = setgain(sd); - - /* commit settings */ - if (err >= 0) - err = stv06xx_write_bridge(sd, 0x143f, 0x01); - - gspca_dev->usb_err = err; -} - -static void st6422_set_exposure(struct gspca_dev *gspca_dev) -{ - int err; - struct sd *sd = (struct sd *) gspca_dev; - - err = setexposure(sd); - - /* commit settings */ - if (err >= 0) - err = stv06xx_write_bridge(sd, 0x143f, 0x01); - - gspca_dev->usb_err = err; -} diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_st6422.h b/drivers/media/video/gspca/stv06xx/stv06xx_st6422.h index d7498e06432b..8f20fbf30f33 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx_st6422.h +++ b/drivers/media/video/gspca/stv06xx/stv06xx_st6422.h @@ -34,8 +34,8 @@ static int st6422_probe(struct sd *sd); static int st6422_start(struct sd *sd); static int st6422_init(struct sd *sd); +static int st6422_init_controls(struct sd *sd); static int st6422_stop(struct sd *sd); -static void st6422_disconnect(struct sd *sd); const struct stv06xx_sensor stv06xx_sensor_st6422 = { .name = "ST6422", @@ -43,10 +43,10 @@ const struct stv06xx_sensor stv06xx_sensor_st6422 = { .min_packet_size = { 300, 847 }, .max_packet_size = { 300, 847 }, .init = st6422_init, + .init_controls = st6422_init_controls, .probe = st6422_probe, .start = st6422_start, .stop = st6422_stop, - .disconnect = st6422_disconnect, }; #endif diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c index a5c69d9ebdd4..748e1421d6d8 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c +++ b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c @@ -44,130 +44,83 @@ static struct v4l2_pix_format vv6410_mode[] = { } }; -static const struct ctrl vv6410_ctrl[] = { -#define HFLIP_IDX 0 - { - { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "horizontal flip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0 - }, - .set = vv6410_set_hflip, - .get = vv6410_get_hflip - }, -#define VFLIP_IDX 1 - { - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "vertical flip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0 - }, - .set = vv6410_set_vflip, - .get = vv6410_get_vflip - }, -#define GAIN_IDX 2 - { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "analog gain", - .minimum = 0, - .maximum = 15, - .step = 1, - .default_value = 10 - }, - .set = vv6410_set_analog_gain, - .get = vv6410_get_analog_gain - }, -#define EXPOSURE_IDX 3 - { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0, - .maximum = 32768, - .step = 1, - .default_value = 20000 - }, - .set = vv6410_set_exposure, - .get = vv6410_get_exposure +static int vv6410_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + int err = -EINVAL; + + switch (ctrl->id) { + case V4L2_CID_HFLIP: + err = vv6410_set_hflip(gspca_dev, ctrl->val); + break; + case V4L2_CID_VFLIP: + err = vv6410_set_vflip(gspca_dev, ctrl->val); + break; + case V4L2_CID_GAIN: + err = vv6410_set_analog_gain(gspca_dev, ctrl->val); + break; + case V4L2_CID_EXPOSURE: + err = vv6410_set_exposure(gspca_dev, ctrl->val); + break; } - }; + return err; +} + +static const struct v4l2_ctrl_ops vv6410_ctrl_ops = { + .s_ctrl = vv6410_s_ctrl, +}; static int vv6410_probe(struct sd *sd) { u16 data; - int err, i; - s32 *sensor_settings; + int err; err = stv06xx_read_sensor(sd, VV6410_DEVICEH, &data); if (err < 0) return -ENODEV; - if (data == 0x19) { - pr_info("vv6410 sensor detected\n"); + if (data != 0x19) + return -ENODEV; - sensor_settings = kmalloc(ARRAY_SIZE(vv6410_ctrl) * sizeof(s32), - GFP_KERNEL); - if (!sensor_settings) - return -ENOMEM; + pr_info("vv6410 sensor detected\n"); - sd->gspca_dev.cam.cam_mode = vv6410_mode; - sd->gspca_dev.cam.nmodes = ARRAY_SIZE(vv6410_mode); - sd->desc.ctrls = vv6410_ctrl; - sd->desc.nctrls = ARRAY_SIZE(vv6410_ctrl); + sd->gspca_dev.cam.cam_mode = vv6410_mode; + sd->gspca_dev.cam.nmodes = ARRAY_SIZE(vv6410_mode); + return 0; +} - for (i = 0; i < sd->desc.nctrls; i++) - sensor_settings[i] = vv6410_ctrl[i].qctrl.default_value; - sd->sensor_priv = sensor_settings; - return 0; - } - return -ENODEV; +static int vv6410_init_controls(struct sd *sd) +{ + struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler; + + v4l2_ctrl_handler_init(hdl, 4); + v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, + V4L2_CID_EXPOSURE, 0, 32768, 1, 20000); + v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, + V4L2_CID_GAIN, 0, 15, 1, 10); + return hdl->error; } static int vv6410_init(struct sd *sd) { int err = 0, i; - s32 *sensor_settings = sd->sensor_priv; - for (i = 0; i < ARRAY_SIZE(stv_bridge_init); i++) { + for (i = 0; i < ARRAY_SIZE(stv_bridge_init); i++) stv06xx_write_bridge(sd, stv_bridge_init[i].addr, stv_bridge_init[i].data); - } if (err < 0) return err; err = stv06xx_write_sensor_bytes(sd, (u8 *) vv6410_sensor_init, ARRAY_SIZE(vv6410_sensor_init)); - if (err < 0) - return err; - - err = vv6410_set_exposure(&sd->gspca_dev, - sensor_settings[EXPOSURE_IDX]); - if (err < 0) - return err; - - err = vv6410_set_analog_gain(&sd->gspca_dev, - sensor_settings[GAIN_IDX]); - return (err < 0) ? err : 0; } -static void vv6410_disconnect(struct sd *sd) -{ - sd->sensor = NULL; - kfree(sd->sensor_priv); -} - static int vv6410_start(struct sd *sd) { int err; @@ -233,25 +186,12 @@ static int vv6410_dump(struct sd *sd) return (err < 0) ? err : 0; } -static int vv6410_get_hflip(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[HFLIP_IDX]; - PDEBUG(D_V4L2, "Read horizontal flip %d", *val); - - return 0; -} - static int vv6410_set_hflip(struct gspca_dev *gspca_dev, __s32 val) { int err; u16 i2c_data; struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - sensor_settings[HFLIP_IDX] = val; err = stv06xx_read_sensor(sd, VV6410_DATAFORMAT, &i2c_data); if (err < 0) return err; @@ -267,25 +207,12 @@ static int vv6410_set_hflip(struct gspca_dev *gspca_dev, __s32 val) return (err < 0) ? err : 0; } -static int vv6410_get_vflip(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[VFLIP_IDX]; - PDEBUG(D_V4L2, "Read vertical flip %d", *val); - - return 0; -} - static int vv6410_set_vflip(struct gspca_dev *gspca_dev, __s32 val) { int err; u16 i2c_data; struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - sensor_settings[VFLIP_IDX] = val; err = stv06xx_read_sensor(sd, VV6410_DATAFORMAT, &i2c_data); if (err < 0) return err; @@ -301,52 +228,23 @@ static int vv6410_set_vflip(struct gspca_dev *gspca_dev, __s32 val) return (err < 0) ? err : 0; } -static int vv6410_get_analog_gain(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[GAIN_IDX]; - - PDEBUG(D_V4L2, "Read analog gain %d", *val); - - return 0; -} - static int vv6410_set_analog_gain(struct gspca_dev *gspca_dev, __s32 val) { int err; struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - sensor_settings[GAIN_IDX] = val; PDEBUG(D_V4L2, "Set analog gain to %d", val); err = stv06xx_write_sensor(sd, VV6410_ANALOGGAIN, 0xf0 | (val & 0xf)); return (err < 0) ? err : 0; } -static int vv6410_get_exposure(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[EXPOSURE_IDX]; - - PDEBUG(D_V4L2, "Read exposure %d", *val); - - return 0; -} - static int vv6410_set_exposure(struct gspca_dev *gspca_dev, __s32 val) { int err; struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; unsigned int fine, coarse; - sensor_settings[EXPOSURE_IDX] = val; - val = (val * val >> 14) + val / 4; fine = val % VV6410_CIF_LINELENGTH; diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h index a25b8873f2e6..53e67b40ca05 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h +++ b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h @@ -178,18 +178,14 @@ static int vv6410_probe(struct sd *sd); static int vv6410_start(struct sd *sd); static int vv6410_init(struct sd *sd); +static int vv6410_init_controls(struct sd *sd); static int vv6410_stop(struct sd *sd); static int vv6410_dump(struct sd *sd); -static void vv6410_disconnect(struct sd *sd); /* V4L2 controls supported by the driver */ -static int vv6410_get_hflip(struct gspca_dev *gspca_dev, __s32 *val); static int vv6410_set_hflip(struct gspca_dev *gspca_dev, __s32 val); -static int vv6410_get_vflip(struct gspca_dev *gspca_dev, __s32 *val); static int vv6410_set_vflip(struct gspca_dev *gspca_dev, __s32 val); -static int vv6410_get_analog_gain(struct gspca_dev *gspca_dev, __s32 *val); static int vv6410_set_analog_gain(struct gspca_dev *gspca_dev, __s32 val); -static int vv6410_get_exposure(struct gspca_dev *gspca_dev, __s32 *val); static int vv6410_set_exposure(struct gspca_dev *gspca_dev, __s32 val); const struct stv06xx_sensor stv06xx_sensor_vv6410 = { @@ -202,11 +198,11 @@ const struct stv06xx_sensor stv06xx_sensor_vv6410 = { .min_packet_size = { 1023 }, .max_packet_size = { 1023 }, .init = vv6410_init, + .init_controls = vv6410_init_controls, .probe = vv6410_probe, .start = vv6410_start, .stop = vv6410_stop, .dump = vv6410_dump, - .disconnect = vv6410_disconnect, }; /* If NULL, only single value to write, stored in len */ diff --git a/drivers/media/video/gspca/topro.c b/drivers/media/video/gspca/topro.c index 444d3c5b9079..c6326d177a3d 100644 --- a/drivers/media/video/gspca/topro.c +++ b/drivers/media/video/gspca/topro.c @@ -4675,11 +4675,9 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, /* -- do autogain -- */ /* gain setting is done in setexposure() for tp6810 */ static void setgain(struct gspca_dev *gspca_dev) {} -/* !! coarse_grained_expo_autogain is not used !! */ -#define exp_too_low_cnt bridge -#define exp_too_high_cnt sensor - +#define WANT_REGULAR_AUTOGAIN #include "autogain_functions.h" + static void sd_dq_callback(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; diff --git a/drivers/media/video/gspca/vicam.c b/drivers/media/video/gspca/vicam.c index 911152e169d6..432d6cd99cd6 100644 --- a/drivers/media/video/gspca/vicam.c +++ b/drivers/media/video/gspca/vicam.c @@ -37,9 +37,12 @@ #include <linux/ihex.h> #include "gspca.h" +#define VICAM_FIRMWARE "vicam/firmware.fw" + MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); MODULE_DESCRIPTION("GSPCA ViCam USB Camera Driver"); MODULE_LICENSE("GPL"); +MODULE_FIRMWARE(VICAM_FIRMWARE); enum e_ctrl { GAIN, @@ -222,7 +225,7 @@ static void vicam_dostream(struct work_struct *work) goto exit; } - while (gspca_dev->present && gspca_dev->streaming) { + while (!gspca_dev->frozen && gspca_dev->dev && gspca_dev->streaming) { ret = vicam_read_frame(gspca_dev, buffer, frame_sz); if (ret < 0) break; @@ -268,7 +271,7 @@ static int sd_init(struct gspca_dev *gspca_dev) const struct firmware *uninitialized_var(fw); u8 *firmware_buf; - ret = request_ihex_firmware(&fw, "vicam/firmware.fw", + ret = request_ihex_firmware(&fw, VICAM_FIRMWARE, &gspca_dev->dev->dev); if (ret) { pr_err("Failed to load \"vicam/firmware.fw\": %d\n", ret); @@ -324,7 +327,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev) dev->work_thread = NULL; mutex_lock(&gspca_dev->usb_lock); - if (gspca_dev->present) + if (gspca_dev->dev) vicam_set_camera_power(gspca_dev, 0); } diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c index 7d9a4f1be9dc..0d504a7c512c 100644 --- a/drivers/media/video/gspca/zc3xx.c +++ b/drivers/media/video/gspca/zc3xx.c @@ -32,29 +32,25 @@ MODULE_LICENSE("GPL"); static int force_sensor = -1; -#define REG08_DEF 3 /* default JPEG compression (70%) */ +#define REG08_DEF 3 /* default JPEG compression (75%) */ #include "zc3xx-reg.h" -/* controls */ -enum e_ctrl { - BRIGHTNESS, - CONTRAST, - EXPOSURE, - GAMMA, - AUTOGAIN, - LIGHTFREQ, - SHARPNESS, - QUALITY, - NCTRLS /* number of controls */ -}; - -#define AUTOGAIN_DEF 1 - /* specific webcam descriptor */ struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - struct gspca_ctrl ctrls[NCTRLS]; + struct { /* gamma/brightness/contrast control cluster */ + struct v4l2_ctrl *gamma; + struct v4l2_ctrl *brightness; + struct v4l2_ctrl *contrast; + }; + struct { /* autogain/exposure control cluster */ + struct v4l2_ctrl *autogain; + struct v4l2_ctrl *exposure; + }; + struct v4l2_ctrl *plfreq; + struct v4l2_ctrl *sharpness; + struct v4l2_ctrl *jpegqual; struct work_struct work; struct workqueue_struct *work_thread; @@ -94,114 +90,6 @@ enum sensors { SENSOR_MAX }; -/* V4L2 controls supported by the driver */ -static void setcontrast(struct gspca_dev *gspca_dev); -static void setexposure(struct gspca_dev *gspca_dev); -static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); -static void setlightfreq(struct gspca_dev *gspca_dev); -static void setsharpness(struct gspca_dev *gspca_dev); -static int sd_setquality(struct gspca_dev *gspca_dev, __s32 val); - -static const struct ctrl sd_ctrls[NCTRLS] = { -[BRIGHTNESS] = { - { - .id = V4L2_CID_BRIGHTNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Brightness", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = 128, - }, - .set_control = setcontrast - }, -[CONTRAST] = { - { - .id = V4L2_CID_CONTRAST, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Contrast", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = 128, - }, - .set_control = setcontrast - }, -[EXPOSURE] = { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Exposure", - .minimum = 0x30d, - .maximum = 0x493e, - .step = 1, - .default_value = 0x927 - }, - .set_control = setexposure - }, -[GAMMA] = { - { - .id = V4L2_CID_GAMMA, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gamma", - .minimum = 1, - .maximum = 6, - .step = 1, - .default_value = 4, - }, - .set_control = setcontrast - }, -[AUTOGAIN] = { - { - .id = V4L2_CID_AUTOGAIN, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Auto Gain", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = AUTOGAIN_DEF, - .flags = V4L2_CTRL_FLAG_UPDATE - }, - .set = sd_setautogain - }, -[LIGHTFREQ] = { - { - .id = V4L2_CID_POWER_LINE_FREQUENCY, - .type = V4L2_CTRL_TYPE_MENU, - .name = "Light frequency filter", - .minimum = 0, - .maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */ - .step = 1, - .default_value = 0, - }, - .set_control = setlightfreq - }, -[SHARPNESS] = { - { - .id = V4L2_CID_SHARPNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Sharpness", - .minimum = 0, - .maximum = 3, - .step = 1, - .default_value = 2, - }, - .set_control = setsharpness - }, -[QUALITY] = { - { - .id = V4L2_CID_JPEG_COMPRESSION_QUALITY, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Compression Quality", - .minimum = 40, - .maximum = 70, - .step = 1, - .default_value = 70 /* updated in sd_init() */ - }, - .set = sd_setquality - }, -}; - static const struct v4l2_pix_format vga_mode[] = { {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 320, @@ -241,8 +129,11 @@ static const struct v4l2_pix_format sif_mode[] = { .priv = 0}, }; -/* bridge reg08 -> JPEG quality conversion table */ -static u8 jpeg_qual[] = {40, 50, 60, 70, /*80*/}; +/* + * Bridge reg08 bits 1-2 -> JPEG quality conversion table. Note the highest + * quality setting is not usable as USB 1 does not have enough bandwidth. + */ +static u8 jpeg_qual[] = {50, 75, 87, /* 94 */}; /* usb exchanges */ struct usb_action { @@ -5818,10 +5709,8 @@ static void setmatrix(struct gspca_dev *gspca_dev) reg_w(gspca_dev, matrix[i], 0x010a + i); } -static void setsharpness(struct gspca_dev *gspca_dev) +static void setsharpness(struct gspca_dev *gspca_dev, s32 val) { - struct sd *sd = (struct sd *) gspca_dev; - int sharpness; static const u8 sharpness_tb[][2] = { {0x02, 0x03}, {0x04, 0x07}, @@ -5829,19 +5718,18 @@ static void setsharpness(struct gspca_dev *gspca_dev) {0x10, 0x1e} }; - sharpness = sd->ctrls[SHARPNESS].val; - reg_w(gspca_dev, sharpness_tb[sharpness][0], 0x01c6); + reg_w(gspca_dev, sharpness_tb[val][0], 0x01c6); reg_r(gspca_dev, 0x01c8); reg_r(gspca_dev, 0x01c9); reg_r(gspca_dev, 0x01ca); - reg_w(gspca_dev, sharpness_tb[sharpness][1], 0x01cb); + reg_w(gspca_dev, sharpness_tb[val][1], 0x01cb); } -static void setcontrast(struct gspca_dev *gspca_dev) +static void setcontrast(struct gspca_dev *gspca_dev, + s32 gamma, s32 brightness, s32 contrast) { - struct sd *sd = (struct sd *) gspca_dev; const u8 *Tgamma; - int g, i, brightness, contrast, adj, gp1, gp2; + int g, i, adj, gp1, gp2; u8 gr[16]; static const u8 delta_b[16] = /* delta for brightness */ {0x50, 0x38, 0x2d, 0x28, 0x24, 0x21, 0x1e, 0x1d, @@ -5864,10 +5752,10 @@ static void setcontrast(struct gspca_dev *gspca_dev) 0xe0, 0xeb, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff}, }; - Tgamma = gamma_tb[sd->ctrls[GAMMA].val - 1]; + Tgamma = gamma_tb[gamma - 1]; - contrast = ((int) sd->ctrls[CONTRAST].val - 128); /* -128 / 127 */ - brightness = ((int) sd->ctrls[BRIGHTNESS].val - 128); /* -128 / 92 */ + contrast -= 128; /* -128 / 127 */ + brightness -= 128; /* -128 / 92 */ adj = 0; gp1 = gp2 = 0; for (i = 0; i < 16; i++) { @@ -5894,25 +5782,15 @@ static void setcontrast(struct gspca_dev *gspca_dev) reg_w(gspca_dev, gr[i], 0x0130 + i); /* gradient */ } -static void getexposure(struct gspca_dev *gspca_dev) +static s32 getexposure(struct gspca_dev *gspca_dev) { - struct sd *sd = (struct sd *) gspca_dev; - - if (sd->sensor != SENSOR_HV7131R) - return; - sd->ctrls[EXPOSURE].val = (i2c_read(gspca_dev, 0x25) << 9) + return (i2c_read(gspca_dev, 0x25) << 9) | (i2c_read(gspca_dev, 0x26) << 1) | (i2c_read(gspca_dev, 0x27) >> 7); } -static void setexposure(struct gspca_dev *gspca_dev) +static void setexposure(struct gspca_dev *gspca_dev, s32 val) { - struct sd *sd = (struct sd *) gspca_dev; - int val; - - if (sd->sensor != SENSOR_HV7131R) - return; - val = sd->ctrls[EXPOSURE].val; i2c_write(gspca_dev, 0x25, val >> 9, 0x00); i2c_write(gspca_dev, 0x26, val >> 1, 0x00); i2c_write(gspca_dev, 0x27, val << 7, 0x00); @@ -5921,20 +5799,8 @@ static void setexposure(struct gspca_dev *gspca_dev) static void setquality(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - s8 reg07; - - reg07 = 0; - switch (sd->sensor) { - case SENSOR_OV7620: - reg07 = 0x30; - break; - case SENSOR_HV7131R: - case SENSOR_PAS202B: - return; /* done by work queue */ - } + jpeg_set_qual(sd->jpeg_hdr, jpeg_qual[sd->reg08 >> 1]); reg_w(gspca_dev, sd->reg08, ZC3XX_R008_CLOCKSETTING); - if (reg07 != 0) - reg_w(gspca_dev, reg07, 0x0007); } /* Matches the sensor's internal frame rate to the lighting frequency. @@ -5943,7 +5809,7 @@ static void setquality(struct gspca_dev *gspca_dev) * 60Hz, for American lighting * 0 = No Fliker (for outdoore usage) */ -static void setlightfreq(struct gspca_dev *gspca_dev) +static void setlightfreq(struct gspca_dev *gspca_dev, s32 val) { struct sd *sd = (struct sd *) gspca_dev; int i, mode; @@ -6027,7 +5893,7 @@ static void setlightfreq(struct gspca_dev *gspca_dev) tas5130c_60HZ, tas5130c_60HZScale}, }; - i = sd->ctrls[LIGHTFREQ].val * 2; + i = val * 2; mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; if (mode) i++; /* 320x240 */ @@ -6037,14 +5903,14 @@ static void setlightfreq(struct gspca_dev *gspca_dev) usb_exchange(gspca_dev, zc3_freq); switch (sd->sensor) { case SENSOR_GC0305: - if (mode /* if 320x240 */ - && sd->ctrls[LIGHTFREQ].val == 1) /* and 50Hz */ + if (mode /* if 320x240 */ + && val == 1) /* and 50Hz */ reg_w(gspca_dev, 0x85, 0x018d); /* win: 0x80, 0x018d */ break; case SENSOR_OV7620: - if (!mode) { /* if 640x480 */ - if (sd->ctrls[LIGHTFREQ].val != 0) /* and filter */ + if (!mode) { /* if 640x480 */ + if (val != 0) /* and filter */ reg_w(gspca_dev, 0x40, 0x0002); else reg_w(gspca_dev, 0x44, 0x0002); @@ -6056,22 +5922,15 @@ static void setlightfreq(struct gspca_dev *gspca_dev) } } -static void setautogain(struct gspca_dev *gspca_dev) +static void setautogain(struct gspca_dev *gspca_dev, s32 val) { - struct sd *sd = (struct sd *) gspca_dev; - u8 autoval; - - if (sd->ctrls[AUTOGAIN].val) - autoval = 0x42; - else - autoval = 0x02; - reg_w(gspca_dev, autoval, 0x0180); + reg_w(gspca_dev, val ? 0x42 : 0x02, 0x0180); } -/* update the transfer parameters */ -/* This function is executed from a work queue. */ -/* The exact use of the bridge registers 07 and 08 is not known. - * The following algorithm has been adapted from ms-win traces */ +/* + * Update the transfer parameters. + * This function is executed from a work queue. + */ static void transfer_update(struct work_struct *work) { struct sd *sd = container_of(work, struct sd, work); @@ -6079,96 +5938,52 @@ static void transfer_update(struct work_struct *work) int change, good; u8 reg07, reg11; - /* synchronize with the main driver and initialize the registers */ - mutex_lock(&gspca_dev->usb_lock); - reg07 = 0; /* max */ - reg_w(gspca_dev, reg07, 0x0007); - reg_w(gspca_dev, sd->reg08, ZC3XX_R008_CLOCKSETTING); - mutex_unlock(&gspca_dev->usb_lock); + /* reg07 gets set to 0 by sd_start before starting us */ + reg07 = 0; good = 0; for (;;) { msleep(100); - /* get the transfer status */ - /* the bit 0 of the bridge register 11 indicates overflow */ mutex_lock(&gspca_dev->usb_lock); - if (!gspca_dev->present || !gspca_dev->streaming) + if (gspca_dev->frozen || !gspca_dev->dev || + !gspca_dev->streaming) goto err; + + /* Bit 0 of register 11 indicates FIFO overflow */ + gspca_dev->usb_err = 0; reg11 = reg_r(gspca_dev, 0x0011); - if (gspca_dev->usb_err < 0 - || !gspca_dev->present || !gspca_dev->streaming) + if (gspca_dev->usb_err) goto err; change = reg11 & 0x01; if (change) { /* overflow */ - switch (reg07) { - case 0: /* max */ - reg07 = sd->sensor == SENSOR_HV7131R - ? 0x30 : 0x32; - if (sd->reg08 != 0) { - change = 3; - sd->reg08--; - } - break; - case 0x32: - reg07 -= 4; - break; - default: - reg07 -= 2; - break; - case 2: - change = 0; /* already min */ - break; - } good = 0; + + if (reg07 == 0) /* Bit Rate Control not enabled? */ + reg07 = 0x32; /* Allow 98 bytes / unit */ + else if (reg07 > 2) + reg07 -= 2; /* Decrease allowed bytes / unit */ + else + change = 0; } else { /* no overflow */ - if (reg07 != 0) { /* if not max */ - good++; - if (good >= 10) { - good = 0; + good++; + if (good >= 10) { + good = 0; + if (reg07) { /* BRC enabled? */ change = 1; - reg07 += 2; - switch (reg07) { - case 0x30: - if (sd->sensor == SENSOR_PAS202B) - reg07 += 2; - break; - case 0x32: - case 0x34: + if (reg07 < 0x32) + reg07 += 2; + else reg07 = 0; - break; - } - } - } else { /* reg07 max */ - if (sd->reg08 < sizeof jpeg_qual - 1) { - good++; - if (good > 10) { - sd->reg08++; - change = 2; - } } } } if (change) { - if (change & 1) { - reg_w(gspca_dev, reg07, 0x0007); - if (gspca_dev->usb_err < 0 - || !gspca_dev->present - || !gspca_dev->streaming) - goto err; - } - if (change & 2) { - reg_w(gspca_dev, sd->reg08, - ZC3XX_R008_CLOCKSETTING); - if (gspca_dev->usb_err < 0 - || !gspca_dev->present - || !gspca_dev->streaming) - goto err; - sd->ctrls[QUALITY].val = jpeg_qual[sd->reg08]; - jpeg_set_qual(sd->jpeg_hdr, - jpeg_qual[sd->reg08]); - } + gspca_dev->usb_err = 0; + reg_w(gspca_dev, reg07, 0x0007); + if (gspca_dev->usb_err) + goto err; } mutex_unlock(&gspca_dev->usb_lock); } @@ -6503,7 +6318,6 @@ static int sd_config(struct gspca_dev *gspca_dev, /* define some sensors from the vendor/product */ sd->sensor = id->driver_info; - gspca_dev->cam.ctrls = sd->ctrls; sd->reg08 = REG08_DEF; INIT_WORK(&sd->work, transfer_update); @@ -6511,12 +6325,87 @@ static int sd_config(struct gspca_dev *gspca_dev, return 0; } -/* this function is called at probe and resume time */ -static int sd_init(struct gspca_dev *gspca_dev) +static int zcxx_g_volatile_ctrl(struct v4l2_ctrl *ctrl) { - struct sd *sd = (struct sd *) gspca_dev; - struct cam *cam; - int sensor; + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + struct sd *sd = (struct sd *)gspca_dev; + + switch (ctrl->id) { + case V4L2_CID_AUTOGAIN: + gspca_dev->usb_err = 0; + if (ctrl->val && sd->exposure && gspca_dev->streaming) + sd->exposure->val = getexposure(gspca_dev); + return gspca_dev->usb_err; + } + return -EINVAL; +} + +static int zcxx_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + struct sd *sd = (struct sd *)gspca_dev; + int i, qual; + + gspca_dev->usb_err = 0; + + if (ctrl->id == V4L2_CID_JPEG_COMPRESSION_QUALITY) { + qual = sd->reg08 >> 1; + + for (i = 0; i < ARRAY_SIZE(jpeg_qual); i++) { + if (ctrl->val <= jpeg_qual[i]) + break; + } + if (i > 0 && i == qual && ctrl->val < jpeg_qual[i]) + i--; + + /* With high quality settings we need max bandwidth */ + if (i >= 2 && gspca_dev->streaming && + !gspca_dev->cam.needs_full_bandwidth) + return -EBUSY; + + sd->reg08 = (i << 1) | 1; + ctrl->val = jpeg_qual[i]; + } + + if (!gspca_dev->streaming) + return 0; + + switch (ctrl->id) { + /* gamma/brightness/contrast cluster */ + case V4L2_CID_GAMMA: + setcontrast(gspca_dev, sd->gamma->val, + sd->brightness->val, sd->contrast->val); + break; + /* autogain/exposure cluster */ + case V4L2_CID_AUTOGAIN: + setautogain(gspca_dev, ctrl->val); + if (!gspca_dev->usb_err && !ctrl->val && sd->exposure) + setexposure(gspca_dev, sd->exposure->val); + break; + case V4L2_CID_POWER_LINE_FREQUENCY: + setlightfreq(gspca_dev, ctrl->val); + break; + case V4L2_CID_SHARPNESS: + setsharpness(gspca_dev, ctrl->val); + break; + case V4L2_CID_JPEG_COMPRESSION_QUALITY: + setquality(gspca_dev); + break; + } + return gspca_dev->usb_err; +} + +static const struct v4l2_ctrl_ops zcxx_ctrl_ops = { + .g_volatile_ctrl = zcxx_g_volatile_ctrl, + .s_ctrl = zcxx_s_ctrl, +}; + +static int sd_init_controls(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *)gspca_dev; + struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; static const u8 gamma[SENSOR_MAX] = { [SENSOR_ADCM2700] = 4, [SENSOR_CS2102] = 4, @@ -6538,6 +6427,48 @@ static int sd_init(struct gspca_dev *gspca_dev) [SENSOR_PO2030] = 4, [SENSOR_TAS5130C] = 3, }; + + gspca_dev->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 8); + sd->brightness = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); + sd->contrast = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, 128); + sd->gamma = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops, + V4L2_CID_GAMMA, 1, 6, 1, gamma[sd->sensor]); + if (sd->sensor == SENSOR_HV7131R) + sd->exposure = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops, + V4L2_CID_EXPOSURE, 0x30d, 0x493e, 1, 0x927); + sd->autogain = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, 1); + if (sd->sensor != SENSOR_OV7630C) + sd->plfreq = v4l2_ctrl_new_std_menu(hdl, &zcxx_ctrl_ops, + V4L2_CID_POWER_LINE_FREQUENCY, + V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0, + V4L2_CID_POWER_LINE_FREQUENCY_DISABLED); + sd->sharpness = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops, + V4L2_CID_SHARPNESS, 0, 3, 1, + sd->sensor == SENSOR_PO2030 ? 0 : 2); + sd->jpegqual = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops, + V4L2_CID_JPEG_COMPRESSION_QUALITY, + jpeg_qual[0], jpeg_qual[ARRAY_SIZE(jpeg_qual) - 1], 1, + jpeg_qual[REG08_DEF >> 1]); + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } + v4l2_ctrl_cluster(3, &sd->gamma); + if (sd->sensor == SENSOR_HV7131R) + v4l2_ctrl_auto_cluster(2, &sd->autogain, 0, true); + return 0; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + int sensor; static const u8 mode_tb[SENSOR_MAX] = { [SENSOR_ADCM2700] = 2, [SENSOR_CS2102] = 1, @@ -6559,27 +6490,6 @@ static int sd_init(struct gspca_dev *gspca_dev) [SENSOR_PO2030] = 1, [SENSOR_TAS5130C] = 1, }; - static const u8 reg08_tb[SENSOR_MAX] = { - [SENSOR_ADCM2700] = 1, - [SENSOR_CS2102] = 3, - [SENSOR_CS2102K] = 3, - [SENSOR_GC0303] = 2, - [SENSOR_GC0305] = 3, - [SENSOR_HDCS2020] = 1, - [SENSOR_HV7131B] = 3, - [SENSOR_HV7131R] = 3, - [SENSOR_ICM105A] = 3, - [SENSOR_MC501CB] = 3, - [SENSOR_MT9V111_1] = 3, - [SENSOR_MT9V111_3] = 3, - [SENSOR_OV7620] = 1, - [SENSOR_OV7630C] = 3, - [SENSOR_PAS106] = 3, - [SENSOR_PAS202B] = 3, - [SENSOR_PB0330] = 3, - [SENSOR_PO2030] = 2, - [SENSOR_TAS5130C] = 3, - }; sensor = zcxx_probeSensor(gspca_dev); if (sensor >= 0) @@ -6688,7 +6598,6 @@ static int sd_init(struct gspca_dev *gspca_dev) case 0x2030: PDEBUG(D_PROBE, "Find Sensor PO2030"); sd->sensor = SENSOR_PO2030; - sd->ctrls[SHARPNESS].def = 0; /* from win traces */ break; case 0x7620: PDEBUG(D_PROBE, "Find Sensor OV7620"); @@ -6730,36 +6639,18 @@ static int sd_init(struct gspca_dev *gspca_dev) break; } - sd->ctrls[GAMMA].def = gamma[sd->sensor]; - sd->reg08 = reg08_tb[sd->sensor]; - sd->ctrls[QUALITY].def = jpeg_qual[sd->reg08]; - sd->ctrls[QUALITY].min = jpeg_qual[0]; - sd->ctrls[QUALITY].max = jpeg_qual[ARRAY_SIZE(jpeg_qual) - 1]; - - switch (sd->sensor) { - case SENSOR_HV7131R: - gspca_dev->ctrl_dis = (1 << QUALITY); - break; - case SENSOR_OV7630C: - gspca_dev->ctrl_dis = (1 << LIGHTFREQ) | (1 << EXPOSURE); - break; - case SENSOR_PAS202B: - gspca_dev->ctrl_dis = (1 << QUALITY) | (1 << EXPOSURE); - break; - default: - gspca_dev->ctrl_dis = (1 << EXPOSURE); - break; - } -#if AUTOGAIN_DEF - if (sd->ctrls[AUTOGAIN].val) - gspca_dev->ctrl_inac = (1 << EXPOSURE); -#endif - /* switch off the led */ reg_w(gspca_dev, 0x01, 0x0000); return gspca_dev->usb_err; } +static int sd_pre_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + gspca_dev->cam.needs_full_bandwidth = (sd->reg08 >= 4) ? 1 : 0; + return 0; +} + static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -6864,7 +6755,7 @@ static int sd_start(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0x03, 0x0008); break; } - setsharpness(gspca_dev); + setsharpness(gspca_dev, v4l2_ctrl_g_ctrl(sd->sharpness)); /* set the gamma tables when not set */ switch (sd->sensor) { @@ -6873,7 +6764,9 @@ static int sd_start(struct gspca_dev *gspca_dev) case SENSOR_OV7630C: break; default: - setcontrast(gspca_dev); + setcontrast(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma), + v4l2_ctrl_g_ctrl(sd->brightness), + v4l2_ctrl_g_ctrl(sd->contrast)); break; } setmatrix(gspca_dev); /* one more time? */ @@ -6885,8 +6778,10 @@ static int sd_start(struct gspca_dev *gspca_dev) break; } setquality(gspca_dev); - jpeg_set_qual(sd->jpeg_hdr, jpeg_qual[sd->reg08]); - setlightfreq(gspca_dev); + /* Start with BRC disabled, transfer_update will enable it if needed */ + reg_w(gspca_dev, 0x00, 0x0007); + if (sd->plfreq) + setlightfreq(gspca_dev, v4l2_ctrl_g_ctrl(sd->plfreq)); switch (sd->sensor) { case SENSOR_ADCM2700: @@ -6897,7 +6792,7 @@ static int sd_start(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0x40, 0x0117); break; case SENSOR_HV7131R: - setexposure(gspca_dev); + setexposure(gspca_dev, v4l2_ctrl_g_ctrl(sd->exposure)); reg_w(gspca_dev, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN); break; case SENSOR_GC0305: @@ -6921,21 +6816,16 @@ static int sd_start(struct gspca_dev *gspca_dev) break; } - setautogain(gspca_dev); + setautogain(gspca_dev, v4l2_ctrl_g_ctrl(sd->autogain)); - /* start the transfer update thread if needed */ - if (gspca_dev->usb_err >= 0) { - switch (sd->sensor) { - case SENSOR_HV7131R: - case SENSOR_PAS202B: - sd->work_thread = - create_singlethread_workqueue(KBUILD_MODNAME); - queue_work(sd->work_thread, &sd->work); - break; - } - } + if (gspca_dev->usb_err < 0) + return gspca_dev->usb_err; - return gspca_dev->usb_err; + /* Start the transfer parameters update thread */ + sd->work_thread = create_singlethread_workqueue(KBUILD_MODNAME); + queue_work(sd->work_thread, &sd->work); + + return 0; } /* called on streamoff with alt 0 and on disconnect */ @@ -6949,7 +6839,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev) mutex_lock(&gspca_dev->usb_lock); sd->work_thread = NULL; } - if (!gspca_dev->present) + if (!gspca_dev->dev) return; send_unknown(gspca_dev, sd->sensor); } @@ -6987,72 +6877,17 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } -static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->ctrls[AUTOGAIN].val = val; - if (val) { - gspca_dev->ctrl_inac |= (1 << EXPOSURE); - } else { - gspca_dev->ctrl_inac &= ~(1 << EXPOSURE); - if (gspca_dev->streaming) - getexposure(gspca_dev); - } - if (gspca_dev->streaming) - setautogain(gspca_dev); - return gspca_dev->usb_err; -} - -static int sd_querymenu(struct gspca_dev *gspca_dev, - struct v4l2_querymenu *menu) -{ - switch (menu->id) { - case V4L2_CID_POWER_LINE_FREQUENCY: - switch (menu->index) { - case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */ - strcpy((char *) menu->name, "NoFliker"); - return 0; - case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */ - strcpy((char *) menu->name, "50 Hz"); - return 0; - case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */ - strcpy((char *) menu->name, "60 Hz"); - return 0; - } - break; - } - return -EINVAL; -} - -static int sd_setquality(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - int i; - - for (i = 0; i < ARRAY_SIZE(jpeg_qual) - 1; i++) { - if (val <= jpeg_qual[i]) - break; - } - if (i > 0 - && i == sd->reg08 - && val < jpeg_qual[sd->reg08]) - i--; - sd->reg08 = i; - sd->ctrls[QUALITY].val = jpeg_qual[i]; - if (gspca_dev->streaming) - jpeg_set_qual(sd->jpeg_hdr, sd->ctrls[QUALITY].val); - return gspca_dev->usb_err; -} - static int sd_set_jcomp(struct gspca_dev *gspca_dev, struct v4l2_jpegcompression *jcomp) { struct sd *sd = (struct sd *) gspca_dev; + int ret; - sd_setquality(gspca_dev, jcomp->quality); - jcomp->quality = sd->ctrls[QUALITY].val; - return gspca_dev->usb_err; + ret = v4l2_ctrl_s_ctrl(sd->jpegqual, jcomp->quality); + if (ret) + return ret; + jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual); + return 0; } static int sd_get_jcomp(struct gspca_dev *gspca_dev, @@ -7061,7 +6896,7 @@ static int sd_get_jcomp(struct gspca_dev *gspca_dev, struct sd *sd = (struct sd *) gspca_dev; memset(jcomp, 0, sizeof *jcomp); - jcomp->quality = sd->ctrls[QUALITY].val; + jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual); jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT | V4L2_JPEG_MARKER_DQT; return 0; @@ -7085,14 +6920,13 @@ static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, static const struct sd_desc sd_desc = { .name = KBUILD_MODNAME, - .ctrls = sd_ctrls, - .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, .init = sd_init, + .init_controls = sd_init_controls, + .isoc_init = sd_pre_start, .start = sd_start, .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, - .querymenu = sd_querymenu, .get_jcomp = sd_get_jcomp, .set_jcomp = sd_set_jcomp, #if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) @@ -7176,6 +7010,7 @@ static struct usb_driver sd_driver = { #ifdef CONFIG_PM .suspend = gspca_suspend, .resume = gspca_resume, + .reset_resume = gspca_resume, #endif }; diff --git a/drivers/media/video/hdpvr/hdpvr-control.c b/drivers/media/video/hdpvr/hdpvr-control.c index 068df4ba3f51..ae8f229d1141 100644 --- a/drivers/media/video/hdpvr/hdpvr-control.c +++ b/drivers/media/video/hdpvr/hdpvr-control.c @@ -113,6 +113,8 @@ int get_input_lines_info(struct hdpvr_device *dev) "get input lines info returned: %d, %s\n", ret, print_buf); } +#else + (void)ret; /* suppress compiler warning */ #endif lines = dev->usbc_buf[1] << 8 | dev->usbc_buf[0]; mutex_unlock(&dev->usbc_mutex); diff --git a/drivers/media/video/hdpvr/hdpvr-video.c b/drivers/media/video/hdpvr/hdpvr-video.c index 11ffe9cc1780..0e9e156bb2aa 100644 --- a/drivers/media/video/hdpvr/hdpvr-video.c +++ b/drivers/media/video/hdpvr/hdpvr-video.c @@ -994,7 +994,7 @@ static int hdpvr_try_ctrl(struct v4l2_ext_control *ctrl, int ac3) default: return -EINVAL; } - return 0; + return ret; } static int vidioc_try_ext_ctrls(struct file *file, void *priv, diff --git a/drivers/media/video/hexium_gemini.c b/drivers/media/video/hexium_gemini.c index a62322d5c0d8..366434f5647e 100644 --- a/drivers/media/video/hexium_gemini.c +++ b/drivers/media/video/hexium_gemini.c @@ -40,15 +40,15 @@ static int hexium_num; #define HEXIUM_INPUTS 9 static struct v4l2_input hexium_inputs[HEXIUM_INPUTS] = { - { 0, "CVBS 1", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { 1, "CVBS 2", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { 2, "CVBS 3", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { 3, "CVBS 4", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { 4, "CVBS 5", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { 5, "CVBS 6", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { 6, "Y/C 1", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { 7, "Y/C 2", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { 8, "Y/C 3", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, + { 0, "CVBS 1", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 1, "CVBS 2", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 2, "CVBS 3", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 3, "CVBS 4", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 4, "CVBS 5", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 5, "CVBS 6", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 6, "Y/C 1", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 7, "Y/C 2", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 8, "Y/C 3", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, }; #define HEXIUM_AUDIOS 0 @@ -59,11 +59,6 @@ struct hexium_data u8 byte; }; -#define HEXIUM_CONTROLS 1 -static struct v4l2_queryctrl hexium_controls[] = { - { V4L2_CID_PRIVATE_BASE, V4L2_CTRL_TYPE_BOOLEAN, "B/W", 0, 1, 1, 0, 0 }, -}; - #define HEXIUM_GEMINI_V_1_0 1 #define HEXIUM_GEMINI_DUAL_V_1_0 2 @@ -76,7 +71,6 @@ struct hexium int cur_input; /* current input */ v4l2_std_id cur_std; /* current standard */ - int cur_bw; /* current black/white status */ }; /* Samsung KS0127B decoder default registers */ @@ -119,18 +113,10 @@ static struct hexium_data hexium_pal[] = { { 0x01, 0x52 }, { 0x12, 0x64 }, { 0x2D, 0x2C }, { 0x2E, 0x9B }, { -1 , 0xFF } }; -static struct hexium_data hexium_pal_bw[] = { - { 0x01, 0x52 }, { 0x12, 0x64 }, { 0x2D, 0x2C }, { 0x2E, 0x9B }, { -1 , 0xFF } -}; - static struct hexium_data hexium_ntsc[] = { { 0x01, 0x53 }, { 0x12, 0x04 }, { 0x2D, 0x23 }, { 0x2E, 0x81 }, { -1 , 0xFF } }; -static struct hexium_data hexium_ntsc_bw[] = { - { 0x01, 0x53 }, { 0x12, 0x04 }, { 0x2D, 0x23 }, { 0x2E, 0x81 }, { -1 , 0xFF } -}; - static struct hexium_data hexium_secam[] = { { 0x01, 0x52 }, { 0x12, 0x64 }, { 0x2D, 0x2C }, { 0x2E, 0x9B }, { -1 , 0xFF } }; @@ -264,93 +250,6 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int input) return 0; } -/* the saa7146 provides some controls (brightness, contrast, saturation) - which gets registered *after* this function. because of this we have - to return with a value != 0 even if the function succeeded.. */ -static int vidioc_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qc) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - int i; - - for (i = HEXIUM_CONTROLS - 1; i >= 0; i--) { - if (hexium_controls[i].id == qc->id) { - *qc = hexium_controls[i]; - DEB_D("VIDIOC_QUERYCTRL %d\n", qc->id); - return 0; - } - } - return dev->ext_vv_data->core_ops->vidioc_queryctrl(file, fh, qc); -} - -static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *vc) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct hexium *hexium = (struct hexium *) dev->ext_priv; - int i; - - for (i = HEXIUM_CONTROLS - 1; i >= 0; i--) { - if (hexium_controls[i].id == vc->id) - break; - } - - if (i < 0) - return dev->ext_vv_data->core_ops->vidioc_g_ctrl(file, fh, vc); - - if (vc->id == V4L2_CID_PRIVATE_BASE) { - vc->value = hexium->cur_bw; - DEB_D("VIDIOC_G_CTRL BW:%d\n", vc->value); - return 0; - } - return -EINVAL; -} - -static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *vc) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct hexium *hexium = (struct hexium *) dev->ext_priv; - int i = 0; - - for (i = HEXIUM_CONTROLS - 1; i >= 0; i--) { - if (hexium_controls[i].id == vc->id) - break; - } - - if (i < 0) - return dev->ext_vv_data->core_ops->vidioc_s_ctrl(file, fh, vc); - - if (vc->id == V4L2_CID_PRIVATE_BASE) - hexium->cur_bw = vc->value; - - DEB_D("VIDIOC_S_CTRL BW:%d\n", hexium->cur_bw); - - if (0 == hexium->cur_bw && V4L2_STD_PAL == hexium->cur_std) { - hexium_set_standard(hexium, hexium_pal); - return 0; - } - if (0 == hexium->cur_bw && V4L2_STD_NTSC == hexium->cur_std) { - hexium_set_standard(hexium, hexium_ntsc); - return 0; - } - if (0 == hexium->cur_bw && V4L2_STD_SECAM == hexium->cur_std) { - hexium_set_standard(hexium, hexium_secam); - return 0; - } - if (1 == hexium->cur_bw && V4L2_STD_PAL == hexium->cur_std) { - hexium_set_standard(hexium, hexium_pal_bw); - return 0; - } - if (1 == hexium->cur_bw && V4L2_STD_NTSC == hexium->cur_std) { - hexium_set_standard(hexium, hexium_ntsc_bw); - return 0; - } - if (1 == hexium->cur_bw && V4L2_STD_SECAM == hexium->cur_std) - /* fixme: is there no bw secam mode? */ - return -EINVAL; - - return -EINVAL; -} - - static struct saa7146_ext_vv vv_data; /* this function only gets called when the probing was successful */ @@ -399,12 +298,10 @@ static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_d hexium->cur_input = 0; saa7146_vv_init(dev, &vv_data); - vv_data.ops.vidioc_queryctrl = vidioc_queryctrl; - vv_data.ops.vidioc_g_ctrl = vidioc_g_ctrl; - vv_data.ops.vidioc_s_ctrl = vidioc_s_ctrl; - vv_data.ops.vidioc_enum_input = vidioc_enum_input; - vv_data.ops.vidioc_g_input = vidioc_g_input; - vv_data.ops.vidioc_s_input = vidioc_s_input; + + vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input; + vv_data.vid_ops.vidioc_g_input = vidioc_g_input; + vv_data.vid_ops.vidioc_s_input = vidioc_s_input; ret = saa7146_register_device(&hexium->video_dev, dev, "hexium gemini", VFL_TYPE_GRABBER); if (ret < 0) { pr_err("cannot register capture v4l2 device. skipping.\n"); diff --git a/drivers/media/video/hexium_orion.c b/drivers/media/video/hexium_orion.c index 23debc967d94..a1eb26d11070 100644 --- a/drivers/media/video/hexium_orion.c +++ b/drivers/media/video/hexium_orion.c @@ -41,15 +41,15 @@ static int hexium_num; #define HEXIUM_INPUTS 9 static struct v4l2_input hexium_inputs[HEXIUM_INPUTS] = { - { 0, "CVBS 1", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { 1, "CVBS 2", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { 2, "CVBS 3", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { 3, "CVBS 4", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { 4, "CVBS 5", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { 5, "CVBS 6", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { 6, "Y/C 1", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { 7, "Y/C 2", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { 8, "Y/C 3", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, + { 0, "CVBS 1", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 1, "CVBS 2", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 2, "CVBS 3", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 3, "CVBS 4", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 4, "CVBS 5", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 5, "CVBS 6", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 6, "Y/C 1", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 7, "Y/C 2", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 8, "Y/C 3", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, }; #define HEXIUM_AUDIOS 0 @@ -371,9 +371,9 @@ static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_d DEB_EE("\n"); saa7146_vv_init(dev, &vv_data); - vv_data.ops.vidioc_enum_input = vidioc_enum_input; - vv_data.ops.vidioc_g_input = vidioc_g_input; - vv_data.ops.vidioc_s_input = vidioc_s_input; + vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input; + vv_data.vid_ops.vidioc_g_input = vidioc_g_input; + vv_data.vid_ops.vidioc_s_input = vidioc_s_input; if (0 != saa7146_register_device(&hexium->video_dev, dev, "hexium orion", VFL_TYPE_GRABBER)) { pr_err("cannot register capture v4l2 device. skipping.\n"); return -1; diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c index 679262ed13bc..057929e165ab 100644 --- a/drivers/media/video/ivtv/ivtv-driver.c +++ b/drivers/media/video/ivtv/ivtv-driver.c @@ -1201,9 +1201,9 @@ static int __devinit ivtv_probe(struct pci_dev *pdev, struct v4l2_ctrl_handler *hdl = itv->v4l2_dev.ctrl_handler; itv->ctrl_pts = v4l2_ctrl_new_std(hdl, &ivtv_hdl_out_ops, - V4L2_CID_MPEG_VIDEO_DEC_PTS, 0, 0, 1, 0); + V4L2_CID_MPEG_VIDEO_DEC_PTS, 0, 0, 0, 0); itv->ctrl_frame = v4l2_ctrl_new_std(hdl, &ivtv_hdl_out_ops, - V4L2_CID_MPEG_VIDEO_DEC_FRAME, 0, 0x7fffffff, 1, 0); + V4L2_CID_MPEG_VIDEO_DEC_FRAME, 0, 0, 0, 0); /* Note: V4L2_MPEG_AUDIO_DEC_PLAYBACK_AUTO is not supported, mask that menu item. */ itv->ctrl_audio_playback = diff --git a/drivers/media/video/ivtv/ivtv-fileops.c b/drivers/media/video/ivtv/ivtv-fileops.c index c9663e885b9f..9ff69b5a87e2 100644 --- a/drivers/media/video/ivtv/ivtv-fileops.c +++ b/drivers/media/video/ivtv/ivtv-fileops.c @@ -746,8 +746,9 @@ unsigned int ivtv_v4l2_dec_poll(struct file *filp, poll_table *wait) return res; } -unsigned int ivtv_v4l2_enc_poll(struct file *filp, poll_table * wait) +unsigned int ivtv_v4l2_enc_poll(struct file *filp, poll_table *wait) { + unsigned long req_events = poll_requested_events(wait); struct ivtv_open_id *id = fh2id(filp->private_data); struct ivtv *itv = id->itv; struct ivtv_stream *s = &itv->streams[id->type]; @@ -755,7 +756,8 @@ unsigned int ivtv_v4l2_enc_poll(struct file *filp, poll_table * wait) unsigned res = 0; /* Start a capture if there is none */ - if (!eof && !test_bit(IVTV_F_S_STREAMING, &s->s_flags)) { + if (!eof && !test_bit(IVTV_F_S_STREAMING, &s->s_flags) && + (req_events & (POLLIN | POLLRDNORM))) { int rc; rc = ivtv_start_capture(id); diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c index 989e556913ed..f7d57b3f2842 100644 --- a/drivers/media/video/ivtv/ivtv-ioctl.c +++ b/drivers/media/video/ivtv/ivtv-ioctl.c @@ -135,7 +135,6 @@ void ivtv_set_osd_alpha(struct ivtv *itv) int ivtv_set_speed(struct ivtv *itv, int speed) { u32 data[CX2341X_MBOX_MAX_DATA]; - struct ivtv_stream *s; int single_step = (speed == 1 || speed == -1); DEFINE_WAIT(wait); @@ -145,8 +144,6 @@ int ivtv_set_speed(struct ivtv *itv, int speed) if (speed == itv->speed && !single_step) return 0; - s = &itv->streams[IVTV_DEC_STREAM_TYPE_MPG]; - if (single_step && (speed < 0) == (itv->speed < 0)) { /* Single step video and no need to change direction */ ivtv_vapi(itv, CX2341X_DEC_STEP_VIDEO, 1, 0); @@ -1468,8 +1465,9 @@ static int ivtv_subscribe_event(struct v4l2_fh *fh, struct v4l2_event_subscripti switch (sub->type) { case V4L2_EVENT_VSYNC: case V4L2_EVENT_EOS: + return v4l2_event_subscribe(fh, sub, 0, NULL); case V4L2_EVENT_CTRL: - return v4l2_event_subscribe(fh, sub, 0); + return v4l2_event_subscribe(fh, sub, 0, &v4l2_ctrl_sub_ev_ops); default: return -EINVAL; } @@ -1827,7 +1825,7 @@ static long ivtv_default(struct file *file, void *fh, bool valid_prio, return ivtv_decoder_ioctls(file, cmd, (void *)arg); default: - return -EINVAL; + return -ENOTTY; } return 0; } diff --git a/drivers/media/video/ivtv/ivtv-streams.c b/drivers/media/video/ivtv/ivtv-streams.c index 7ea5ca7f012b..6738592aa35d 100644 --- a/drivers/media/video/ivtv/ivtv-streams.c +++ b/drivers/media/video/ivtv/ivtv-streams.c @@ -228,6 +228,10 @@ static int ivtv_prep_dev(struct ivtv *itv, int type) s->vdev->release = video_device_release; s->vdev->tvnorms = V4L2_STD_ALL; s->vdev->lock = &itv->serialize_lock; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &s->vdev->flags); set_bit(V4L2_FL_USE_FH_PRIO, &s->vdev->flags); ivtv_set_funcs(s->vdev); return 0; diff --git a/drivers/media/video/ivtv/ivtvfb.c b/drivers/media/video/ivtv/ivtvfb.c index e5e7fa9e737b..05b94aa8ba32 100644 --- a/drivers/media/video/ivtv/ivtvfb.c +++ b/drivers/media/video/ivtv/ivtvfb.c @@ -1293,6 +1293,7 @@ static int __init ivtvfb_init(void) drv = driver_find("ivtv", &pci_bus_type); err = driver_for_each_device(drv, NULL, ®istered, ivtvfb_callback_init); + (void)err; /* suppress compiler warning */ if (!registered) { printk(KERN_ERR "ivtvfb: no cards found\n"); return -ENODEV; @@ -1309,6 +1310,7 @@ static void ivtvfb_cleanup(void) drv = driver_find("ivtv", &pci_bus_type); err = driver_for_each_device(drv, NULL, NULL, ivtvfb_callback_cleanup); + (void)err; /* suppress compiler warning */ } module_init(ivtvfb_init); diff --git a/drivers/media/video/m5mols/m5mols.h b/drivers/media/video/m5mols/m5mols.h index 4b021e1ee5f2..bb589917b65b 100644 --- a/drivers/media/video/m5mols/m5mols.h +++ b/drivers/media/video/m5mols/m5mols.h @@ -21,11 +21,6 @@ extern int m5mols_debug; -#define to_m5mols(__sd) container_of(__sd, struct m5mols_info, sd) - -#define to_sd(__ctrl) \ - (&container_of(__ctrl->handler, struct m5mols_info, handle)->sd) - enum m5mols_restype { M5MOLS_RESTYPE_MONITOR, M5MOLS_RESTYPE_CAPTURE, @@ -163,21 +158,27 @@ struct m5mols_version { * @ffmt: current fmt according to resolution type * @res_type: current resolution type * @irq_waitq: waitqueue for the capture - * @flags: state variable for the interrupt handler + * @irq_done: set to 1 in the interrupt handler * @handle: control handler - * @autoexposure: Auto Exposure control - * @exposure: Exposure control - * @autowb: Auto White Balance control - * @colorfx: Color effect control - * @saturation: Saturation control - * @zoom: Zoom control + * @auto_exposure: auto/manual exposure control + * @exposure_bias: exposure compensation control + * @exposure: manual exposure control + * @metering: exposure metering control + * @auto_iso: auto/manual ISO sensitivity control + * @iso: manual ISO sensitivity control + * @auto_wb: auto white balance control + * @lock_3a: 3A lock control + * @colorfx: color effect control + * @saturation: saturation control + * @zoom: zoom control + * @wdr: wide dynamic range control + * @stabilization: image stabilization control + * @jpeg_quality: JPEG compression quality control * @ver: information of the version * @cap: the capture mode attributes - * @power: current sensor's power status * @isp_ready: 1 when the ISP controller has completed booting + * @power: current sensor's power status * @ctrl_sync: 1 when the control handler state is restored in H/W - * @lock_ae: true means the Auto Exposure is locked - * @lock_awb: true means the Aut WhiteBalance is locked * @resolution: register value for current resolution * @mode: register value for current operation mode * @set_power: optional power callback to the board code @@ -193,15 +194,27 @@ struct m5mols_info { atomic_t irq_done; struct v4l2_ctrl_handler handle; + struct { + /* exposure/exposure bias/auto exposure cluster */ + struct v4l2_ctrl *auto_exposure; + struct v4l2_ctrl *exposure_bias; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *metering; + }; + struct { + /* iso/auto iso cluster */ + struct v4l2_ctrl *auto_iso; + struct v4l2_ctrl *iso; + }; + struct v4l2_ctrl *auto_wb; - /* Autoexposure/exposure control cluster */ - struct v4l2_ctrl *autoexposure; - struct v4l2_ctrl *exposure; - - struct v4l2_ctrl *autowb; + struct v4l2_ctrl *lock_3a; struct v4l2_ctrl *colorfx; struct v4l2_ctrl *saturation; struct v4l2_ctrl *zoom; + struct v4l2_ctrl *wdr; + struct v4l2_ctrl *stabilization; + struct v4l2_ctrl *jpeg_quality; struct m5mols_version ver; struct m5mols_capture cap; @@ -210,8 +223,6 @@ struct m5mols_info { unsigned int power:1; unsigned int ctrl_sync:1; - bool lock_ae; - bool lock_awb; u8 resolution; u8 mode; @@ -282,7 +293,7 @@ int m5mols_busy_wait(struct v4l2_subdev *sd, u32 reg, u32 value, u32 mask, * The available executing order between each modes are as follows: * PARAMETER <---> MONITOR <---> CAPTURE */ -int m5mols_mode(struct m5mols_info *info, u8 mode); +int m5mols_set_mode(struct m5mols_info *info, u8 mode); int m5mols_enable_interrupt(struct v4l2_subdev *sd, u8 reg); int m5mols_wait_interrupt(struct v4l2_subdev *sd, u8 condition, u32 timeout); @@ -291,9 +302,33 @@ int m5mols_start_capture(struct m5mols_info *info); int m5mols_do_scenemode(struct m5mols_info *info, u8 mode); int m5mols_lock_3a(struct m5mols_info *info, bool lock); int m5mols_set_ctrl(struct v4l2_ctrl *ctrl); +int m5mols_init_controls(struct v4l2_subdev *sd); /* The firmware function */ int m5mols_update_fw(struct v4l2_subdev *sd, int (*set_power)(struct m5mols_info *, bool)); +static inline struct m5mols_info *to_m5mols(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct m5mols_info, sd); +} + +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + struct m5mols_info *info = container_of(ctrl->handler, + struct m5mols_info, handle); + return &info->sd; +} + +static inline void m5mols_set_ctrl_mode(struct v4l2_ctrl *ctrl, + unsigned int mode) +{ + ctrl->priv = (void *)mode; +} + +static inline unsigned int m5mols_get_ctrl_mode(struct v4l2_ctrl *ctrl) +{ + return (unsigned int)ctrl->priv; +} + #endif /* M5MOLS_H */ diff --git a/drivers/media/video/m5mols/m5mols_capture.c b/drivers/media/video/m5mols/m5mols_capture.c index ba25e8e2ba4c..cb243bd278ce 100644 --- a/drivers/media/video/m5mols/m5mols_capture.c +++ b/drivers/media/video/m5mols/m5mols_capture.c @@ -106,7 +106,6 @@ static int m5mols_capture_info(struct m5mols_info *info) int m5mols_start_capture(struct m5mols_info *info) { struct v4l2_subdev *sd = &info->sd; - u8 resolution = info->resolution; int ret; /* @@ -114,22 +113,18 @@ int m5mols_start_capture(struct m5mols_info *info) * format. The frame capture is initiated during switching from Monitor * to Capture mode. */ - ret = m5mols_mode(info, REG_MONITOR); + ret = m5mols_set_mode(info, REG_MONITOR); if (!ret) ret = m5mols_restore_controls(info); if (!ret) ret = m5mols_write(sd, CAPP_YUVOUT_MAIN, REG_JPEG); if (!ret) - ret = m5mols_write(sd, CAPP_MAIN_IMAGE_SIZE, resolution); + ret = m5mols_write(sd, CAPP_MAIN_IMAGE_SIZE, info->resolution); if (!ret) - ret = m5mols_lock_3a(info, true); - if (!ret) - ret = m5mols_mode(info, REG_CAPTURE); + ret = m5mols_set_mode(info, REG_CAPTURE); if (!ret) /* Wait until a frame is captured to ISP internal memory */ ret = m5mols_wait_interrupt(sd, REG_INT_CAPTURE, 2000); - if (!ret) - ret = m5mols_lock_3a(info, false); if (ret) return ret; diff --git a/drivers/media/video/m5mols/m5mols_controls.c b/drivers/media/video/m5mols/m5mols_controls.c index d135d20d09cf..392a028730e2 100644 --- a/drivers/media/video/m5mols/m5mols_controls.c +++ b/drivers/media/video/m5mols/m5mols_controls.c @@ -139,7 +139,7 @@ int m5mols_do_scenemode(struct m5mols_info *info, u8 mode) if (mode > REG_SCENE_CANDLE) return -EINVAL; - ret = m5mols_lock_3a(info, false); + ret = v4l2_ctrl_s_ctrl(info->lock_3a, 0); if (!ret) ret = m5mols_write(sd, AE_EV_PRESET_MONITOR, mode); if (!ret) @@ -169,7 +169,7 @@ int m5mols_do_scenemode(struct m5mols_info *info, u8 mode) if (!ret) ret = m5mols_write(sd, AE_ISO, scenemode.iso); if (!ret) - ret = m5mols_mode(info, REG_CAPTURE); + ret = m5mols_set_mode(info, REG_CAPTURE); if (!ret) ret = m5mols_write(sd, CAPP_WDR_EN, scenemode.wdr); if (!ret) @@ -181,119 +181,448 @@ int m5mols_do_scenemode(struct m5mols_info *info, u8 mode) if (!ret) ret = m5mols_write(sd, CAPC_MODE, scenemode.capt_mode); if (!ret) - ret = m5mols_mode(info, REG_MONITOR); + ret = m5mols_set_mode(info, REG_MONITOR); return ret; } -static int m5mols_lock_ae(struct m5mols_info *info, bool lock) +static int m5mols_3a_lock(struct m5mols_info *info, struct v4l2_ctrl *ctrl) { + bool af_lock = ctrl->val & V4L2_LOCK_FOCUS; int ret = 0; - if (info->lock_ae != lock) - ret = m5mols_write(&info->sd, AE_LOCK, - lock ? REG_AE_LOCK : REG_AE_UNLOCK); - if (!ret) - info->lock_ae = lock; + if ((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_EXPOSURE) { + bool ae_lock = ctrl->val & V4L2_LOCK_EXPOSURE; + + ret = m5mols_write(&info->sd, AE_LOCK, ae_lock ? + REG_AE_LOCK : REG_AE_UNLOCK); + if (ret) + return ret; + } + + if (((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_WHITE_BALANCE) + && info->auto_wb->val) { + bool awb_lock = ctrl->val & V4L2_LOCK_WHITE_BALANCE; + + ret = m5mols_write(&info->sd, AWB_LOCK, awb_lock ? + REG_AWB_LOCK : REG_AWB_UNLOCK); + if (ret) + return ret; + } + + if (!info->ver.af || !af_lock) + return ret; + + if ((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_FOCUS) + ret = m5mols_write(&info->sd, AF_EXECUTE, REG_AF_STOP); + + return ret; +} + +static int m5mols_set_metering_mode(struct m5mols_info *info, int mode) +{ + unsigned int metering; + + switch (mode) { + case V4L2_EXPOSURE_METERING_CENTER_WEIGHTED: + metering = REG_AE_CENTER; + break; + case V4L2_EXPOSURE_METERING_SPOT: + metering = REG_AE_SPOT; + break; + default: + metering = REG_AE_ALL; + break; + } + + return m5mols_write(&info->sd, AE_MODE, metering); +} + +static int m5mols_set_exposure(struct m5mols_info *info, int exposure) +{ + struct v4l2_subdev *sd = &info->sd; + int ret = 0; + + if (exposure == V4L2_EXPOSURE_AUTO) { + /* Unlock auto exposure */ + info->lock_3a->val &= ~V4L2_LOCK_EXPOSURE; + m5mols_3a_lock(info, info->lock_3a); + + ret = m5mols_set_metering_mode(info, info->metering->val); + if (ret < 0) + return ret; + + v4l2_dbg(1, m5mols_debug, sd, + "%s: exposure bias: %#x, metering: %#x\n", + __func__, info->exposure_bias->val, + info->metering->val); + + return m5mols_write(sd, AE_INDEX, info->exposure_bias->val); + } + + if (exposure == V4L2_EXPOSURE_MANUAL) { + ret = m5mols_write(sd, AE_MODE, REG_AE_OFF); + if (ret == 0) + ret = m5mols_write(sd, AE_MAN_GAIN_MON, + info->exposure->val); + if (ret == 0) + ret = m5mols_write(sd, AE_MAN_GAIN_CAP, + info->exposure->val); + + v4l2_dbg(1, m5mols_debug, sd, "%s: exposure: %#x\n", + __func__, info->exposure->val); + } + + return ret; +} + +static int m5mols_set_white_balance(struct m5mols_info *info, int val) +{ + static const unsigned short wb[][2] = { + { V4L2_WHITE_BALANCE_INCANDESCENT, REG_AWB_INCANDESCENT }, + { V4L2_WHITE_BALANCE_FLUORESCENT, REG_AWB_FLUORESCENT_1 }, + { V4L2_WHITE_BALANCE_FLUORESCENT_H, REG_AWB_FLUORESCENT_2 }, + { V4L2_WHITE_BALANCE_HORIZON, REG_AWB_HORIZON }, + { V4L2_WHITE_BALANCE_DAYLIGHT, REG_AWB_DAYLIGHT }, + { V4L2_WHITE_BALANCE_FLASH, REG_AWB_LEDLIGHT }, + { V4L2_WHITE_BALANCE_CLOUDY, REG_AWB_CLOUDY }, + { V4L2_WHITE_BALANCE_SHADE, REG_AWB_SHADE }, + { V4L2_WHITE_BALANCE_AUTO, REG_AWB_AUTO }, + }; + int i; + struct v4l2_subdev *sd = &info->sd; + int ret = -EINVAL; + + for (i = 0; i < ARRAY_SIZE(wb); i++) { + int awb; + if (wb[i][0] != val) + continue; + + v4l2_dbg(1, m5mols_debug, sd, + "Setting white balance to: %#x\n", wb[i][0]); + + awb = wb[i][0] == V4L2_WHITE_BALANCE_AUTO; + ret = m5mols_write(sd, AWB_MODE, awb ? REG_AWB_AUTO : + REG_AWB_PRESET); + if (ret < 0) + return ret; + + if (!awb) + ret = m5mols_write(sd, AWB_MANUAL, wb[i][1]); + } return ret; } -static int m5mols_lock_awb(struct m5mols_info *info, bool lock) +static int m5mols_set_saturation(struct m5mols_info *info, int val) +{ + int ret = m5mols_write(&info->sd, MON_CHROMA_LVL, val); + if (ret < 0) + return ret; + + return m5mols_write(&info->sd, MON_CHROMA_EN, REG_CHROMA_ON); +} + +static int m5mols_set_color_effect(struct m5mols_info *info, int val) { + unsigned int m_effect = REG_COLOR_EFFECT_OFF; + unsigned int p_effect = REG_EFFECT_OFF; + unsigned int cfix_r = 0, cfix_b = 0; + struct v4l2_subdev *sd = &info->sd; int ret = 0; - if (info->lock_awb != lock) - ret = m5mols_write(&info->sd, AWB_LOCK, - lock ? REG_AWB_LOCK : REG_AWB_UNLOCK); + switch (val) { + case V4L2_COLORFX_BW: + m_effect = REG_COLOR_EFFECT_ON; + break; + case V4L2_COLORFX_NEGATIVE: + p_effect = REG_EFFECT_NEGA; + break; + case V4L2_COLORFX_EMBOSS: + p_effect = REG_EFFECT_EMBOSS; + break; + case V4L2_COLORFX_SEPIA: + m_effect = REG_COLOR_EFFECT_ON; + cfix_r = REG_CFIXR_SEPIA; + cfix_b = REG_CFIXB_SEPIA; + break; + } + + ret = m5mols_write(sd, PARM_EFFECT, p_effect); if (!ret) - info->lock_awb = lock; + ret = m5mols_write(sd, MON_EFFECT, m_effect); + + if (ret == 0 && m_effect == REG_COLOR_EFFECT_ON) { + ret = m5mols_write(sd, MON_CFIXR, cfix_r); + if (!ret) + ret = m5mols_write(sd, MON_CFIXB, cfix_b); + } + + v4l2_dbg(1, m5mols_debug, sd, + "p_effect: %#x, m_effect: %#x, r: %#x, b: %#x (%d)\n", + p_effect, m_effect, cfix_r, cfix_b, ret); return ret; } -/* m5mols_lock_3a() - Lock 3A(Auto Exposure, Auto Whitebalance, Auto Focus) */ -int m5mols_lock_3a(struct m5mols_info *info, bool lock) +static int m5mols_set_iso(struct m5mols_info *info, int auto_iso) +{ + u32 iso = auto_iso ? 0 : info->iso->val + 1; + + return m5mols_write(&info->sd, AE_ISO, iso); +} + +static int m5mols_set_wdr(struct m5mols_info *info, int wdr) { int ret; - ret = m5mols_lock_ae(info, lock); - if (!ret) - ret = m5mols_lock_awb(info, lock); - /* Don't need to handle unlocking AF */ - if (!ret && is_available_af(info) && lock) - ret = m5mols_write(&info->sd, AF_EXECUTE, REG_AF_STOP); + ret = m5mols_write(&info->sd, MON_TONE_CTL, wdr ? 9 : 5); + if (ret < 0) + return ret; + + ret = m5mols_set_mode(info, REG_CAPTURE); + if (ret < 0) + return ret; + + return m5mols_write(&info->sd, CAPP_WDR_EN, wdr); +} + +static int m5mols_set_stabilization(struct m5mols_info *info, int val) +{ + struct v4l2_subdev *sd = &info->sd; + unsigned int evp = val ? 0xe : 0x0; + int ret; + + ret = m5mols_write(sd, AE_EV_PRESET_MONITOR, evp); + if (ret < 0) + return ret; + + return m5mols_write(sd, AE_EV_PRESET_CAPTURE, evp); +} + +static int m5mols_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + struct m5mols_info *info = to_m5mols(sd); + int ret = 0; + u8 status; + + v4l2_dbg(1, m5mols_debug, sd, "%s: ctrl: %s (%d)\n", + __func__, ctrl->name, info->isp_ready); + + if (!info->isp_ready) + return -EBUSY; + + switch (ctrl->id) { + case V4L2_CID_ISO_SENSITIVITY_AUTO: + ret = m5mols_read_u8(sd, AE_ISO, &status); + if (ret == 0) + ctrl->val = !status; + if (status != REG_ISO_AUTO) + info->iso->val = status - 1; + break; + + case V4L2_CID_3A_LOCK: + ctrl->val &= ~0x7; + + ret = m5mols_read_u8(sd, AE_LOCK, &status); + if (ret) + return ret; + if (status) + info->lock_3a->val |= V4L2_LOCK_EXPOSURE; + + ret = m5mols_read_u8(sd, AWB_LOCK, &status); + if (ret) + return ret; + if (status) + info->lock_3a->val |= V4L2_LOCK_EXPOSURE; + + ret = m5mols_read_u8(sd, AF_EXECUTE, &status); + if (!status) + info->lock_3a->val |= V4L2_LOCK_EXPOSURE; + break; + } return ret; } -/* m5mols_set_ctrl() - The main s_ctrl function called by m5mols_set_ctrl() */ -int m5mols_set_ctrl(struct v4l2_ctrl *ctrl) +static int m5mols_s_ctrl(struct v4l2_ctrl *ctrl) { + unsigned int ctrl_mode = m5mols_get_ctrl_mode(ctrl); struct v4l2_subdev *sd = to_sd(ctrl); struct m5mols_info *info = to_m5mols(sd); - int ret; + int last_mode = info->mode; + int ret = 0; + + /* + * If needed, defer restoring the controls until + * the device is fully initialized. + */ + if (!info->isp_ready) { + info->ctrl_sync = 0; + return 0; + } + + v4l2_dbg(1, m5mols_debug, sd, "%s: %s, val: %d, priv: %#x\n", + __func__, ctrl->name, ctrl->val, (int)ctrl->priv); + + if (ctrl_mode && ctrl_mode != info->mode) { + ret = m5mols_set_mode(info, ctrl_mode); + if (ret < 0) + return ret; + } switch (ctrl->id) { + case V4L2_CID_3A_LOCK: + ret = m5mols_3a_lock(info, ctrl); + break; + case V4L2_CID_ZOOM_ABSOLUTE: - return m5mols_write(sd, MON_ZOOM, ctrl->val); + ret = m5mols_write(sd, MON_ZOOM, ctrl->val); + break; case V4L2_CID_EXPOSURE_AUTO: - ret = m5mols_lock_ae(info, - ctrl->val == V4L2_EXPOSURE_AUTO ? false : true); - if (!ret && ctrl->val == V4L2_EXPOSURE_AUTO) - ret = m5mols_write(sd, AE_MODE, REG_AE_ALL); - if (!ret && ctrl->val == V4L2_EXPOSURE_MANUAL) { - int val = info->exposure->val; - ret = m5mols_write(sd, AE_MODE, REG_AE_OFF); - if (!ret) - ret = m5mols_write(sd, AE_MAN_GAIN_MON, val); - if (!ret) - ret = m5mols_write(sd, AE_MAN_GAIN_CAP, val); - } - return ret; + ret = m5mols_set_exposure(info, ctrl->val); + break; - case V4L2_CID_AUTO_WHITE_BALANCE: - ret = m5mols_lock_awb(info, ctrl->val ? false : true); - if (!ret) - ret = m5mols_write(sd, AWB_MODE, ctrl->val ? - REG_AWB_AUTO : REG_AWB_PRESET); - return ret; + case V4L2_CID_ISO_SENSITIVITY: + ret = m5mols_set_iso(info, ctrl->val); + break; + + case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE: + ret = m5mols_set_white_balance(info, ctrl->val); + break; case V4L2_CID_SATURATION: - ret = m5mols_write(sd, MON_CHROMA_LVL, ctrl->val); - if (!ret) - ret = m5mols_write(sd, MON_CHROMA_EN, REG_CHROMA_ON); - return ret; + ret = m5mols_set_saturation(info, ctrl->val); + break; case V4L2_CID_COLORFX: - /* - * This control uses two kinds of registers: normal & color. - * The normal effect belongs to category 1, while the color - * one belongs to category 2. - * - * The normal effect uses one register: CAT1_EFFECT. - * The color effect uses three registers: - * CAT2_COLOR_EFFECT, CAT2_CFIXR, CAT2_CFIXB. - */ - ret = m5mols_write(sd, PARM_EFFECT, - ctrl->val == V4L2_COLORFX_NEGATIVE ? REG_EFFECT_NEGA : - ctrl->val == V4L2_COLORFX_EMBOSS ? REG_EFFECT_EMBOSS : - REG_EFFECT_OFF); - if (!ret) - ret = m5mols_write(sd, MON_EFFECT, - ctrl->val == V4L2_COLORFX_SEPIA ? - REG_COLOR_EFFECT_ON : REG_COLOR_EFFECT_OFF); - if (!ret) - ret = m5mols_write(sd, MON_CFIXR, - ctrl->val == V4L2_COLORFX_SEPIA ? - REG_CFIXR_SEPIA : 0); - if (!ret) - ret = m5mols_write(sd, MON_CFIXB, - ctrl->val == V4L2_COLORFX_SEPIA ? - REG_CFIXB_SEPIA : 0); + ret = m5mols_set_color_effect(info, ctrl->val); + break; + + case V4L2_CID_WIDE_DYNAMIC_RANGE: + ret = m5mols_set_wdr(info, ctrl->val); + break; + + case V4L2_CID_IMAGE_STABILIZATION: + ret = m5mols_set_stabilization(info, ctrl->val); + break; + + case V4L2_CID_JPEG_COMPRESSION_QUALITY: + ret = m5mols_write(sd, CAPP_JPEG_RATIO, ctrl->val); + break; + } + + if (ret == 0 && info->mode != last_mode) + ret = m5mols_set_mode(info, last_mode); + + return ret; +} + +static const struct v4l2_ctrl_ops m5mols_ctrl_ops = { + .g_volatile_ctrl = m5mols_g_volatile_ctrl, + .s_ctrl = m5mols_s_ctrl, +}; + +/* Supported manual ISO values */ +static const s64 iso_qmenu[] = { + /* AE_ISO: 0x01...0x07 */ + 50, 100, 200, 400, 800, 1600, 3200 +}; + +/* Supported Exposure Bias values, -2.0EV...+2.0EV */ +static const s64 ev_bias_qmenu[] = { + /* AE_INDEX: 0x00...0x08 */ + -2000, -1500, -1000, -500, 0, 500, 1000, 1500, 2000 +}; + +int m5mols_init_controls(struct v4l2_subdev *sd) +{ + struct m5mols_info *info = to_m5mols(sd); + u16 exposure_max; + u16 zoom_step; + int ret; + + /* Determine the firmware dependant control range and step values */ + ret = m5mols_read_u16(sd, AE_MAX_GAIN_MON, &exposure_max); + if (ret < 0) + return ret; + + zoom_step = is_manufacturer(info, REG_SAMSUNG_OPTICS) ? 31 : 1; + v4l2_ctrl_handler_init(&info->handle, 20); + + info->auto_wb = v4l2_ctrl_new_std_menu(&info->handle, + &m5mols_ctrl_ops, V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE, + 9, ~0x3fe, V4L2_WHITE_BALANCE_AUTO); + + /* Exposure control cluster */ + info->auto_exposure = v4l2_ctrl_new_std_menu(&info->handle, + &m5mols_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, + 1, ~0x03, V4L2_EXPOSURE_AUTO); + + info->exposure = v4l2_ctrl_new_std(&info->handle, + &m5mols_ctrl_ops, V4L2_CID_EXPOSURE, + 0, exposure_max, 1, exposure_max / 2); + + info->exposure_bias = v4l2_ctrl_new_int_menu(&info->handle, + &m5mols_ctrl_ops, V4L2_CID_AUTO_EXPOSURE_BIAS, + ARRAY_SIZE(ev_bias_qmenu) - 1, + ARRAY_SIZE(ev_bias_qmenu)/2 - 1, + ev_bias_qmenu); + + info->metering = v4l2_ctrl_new_std_menu(&info->handle, + &m5mols_ctrl_ops, V4L2_CID_EXPOSURE_METERING, + 2, ~0x7, V4L2_EXPOSURE_METERING_AVERAGE); + + /* ISO control cluster */ + info->auto_iso = v4l2_ctrl_new_std_menu(&info->handle, &m5mols_ctrl_ops, + V4L2_CID_ISO_SENSITIVITY_AUTO, 1, ~0x03, 1); + + info->iso = v4l2_ctrl_new_int_menu(&info->handle, &m5mols_ctrl_ops, + V4L2_CID_ISO_SENSITIVITY, ARRAY_SIZE(iso_qmenu) - 1, + ARRAY_SIZE(iso_qmenu)/2 - 1, iso_qmenu); + + info->saturation = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, + V4L2_CID_SATURATION, 1, 5, 1, 3); + + info->zoom = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, + V4L2_CID_ZOOM_ABSOLUTE, 1, 70, zoom_step, 1); + + info->colorfx = v4l2_ctrl_new_std_menu(&info->handle, &m5mols_ctrl_ops, + V4L2_CID_COLORFX, 4, 0, V4L2_COLORFX_NONE); + + info->wdr = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, + V4L2_CID_WIDE_DYNAMIC_RANGE, 0, 1, 1, 0); + + info->stabilization = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, + V4L2_CID_IMAGE_STABILIZATION, 0, 1, 1, 0); + + info->jpeg_quality = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, + V4L2_CID_JPEG_COMPRESSION_QUALITY, 1, 100, 1, 80); + + info->lock_3a = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, + V4L2_CID_3A_LOCK, 0, 0x7, 0, 0); + + if (info->handle.error) { + int ret = info->handle.error; + v4l2_err(sd, "Failed to initialize controls: %d\n", ret); + v4l2_ctrl_handler_free(&info->handle); return ret; } - return -EINVAL; + v4l2_ctrl_auto_cluster(4, &info->auto_exposure, 1, false); + info->auto_iso->flags |= V4L2_CTRL_FLAG_VOLATILE | + V4L2_CTRL_FLAG_UPDATE; + v4l2_ctrl_auto_cluster(2, &info->auto_iso, 0, false); + + info->lock_3a->flags |= V4L2_CTRL_FLAG_VOLATILE; + + m5mols_set_ctrl_mode(info->auto_exposure, REG_PARAMETER); + m5mols_set_ctrl_mode(info->auto_wb, REG_PARAMETER); + m5mols_set_ctrl_mode(info->colorfx, REG_MONITOR); + + sd->ctrl_handler = &info->handle; + + return 0; } diff --git a/drivers/media/video/m5mols/m5mols_core.c b/drivers/media/video/m5mols/m5mols_core.c index d718aee01c77..ac7d28b6ddf2 100644 --- a/drivers/media/video/m5mols/m5mols_core.c +++ b/drivers/media/video/m5mols/m5mols_core.c @@ -362,14 +362,14 @@ static int m5mols_reg_mode(struct v4l2_subdev *sd, u8 mode) } /** - * m5mols_mode - manage the M-5MOLS's mode + * m5mols_set_mode - set the M-5MOLS controller mode * @mode: the required operation mode * * The commands of M-5MOLS are grouped into specific modes. Each functionality - * can be guaranteed only when the sensor is operating in mode which which - * a command belongs to. + * can be guaranteed only when the sensor is operating in mode which a command + * belongs to. */ -int m5mols_mode(struct m5mols_info *info, u8 mode) +int m5mols_set_mode(struct m5mols_info *info, u8 mode) { struct v4l2_subdev *sd = &info->sd; int ret = -EINVAL; @@ -645,13 +645,13 @@ static int m5mols_start_monitor(struct m5mols_info *info) struct v4l2_subdev *sd = &info->sd; int ret; - ret = m5mols_mode(info, REG_PARAMETER); + ret = m5mols_set_mode(info, REG_PARAMETER); if (!ret) ret = m5mols_write(sd, PARM_MON_SIZE, info->resolution); if (!ret) ret = m5mols_write(sd, PARM_MON_FPS, REG_FPS_30); if (!ret) - ret = m5mols_mode(info, REG_MONITOR); + ret = m5mols_set_mode(info, REG_MONITOR); if (!ret) ret = m5mols_restore_controls(info); @@ -674,42 +674,13 @@ static int m5mols_s_stream(struct v4l2_subdev *sd, int enable) return ret; } - return m5mols_mode(info, REG_PARAMETER); + return m5mols_set_mode(info, REG_PARAMETER); } static const struct v4l2_subdev_video_ops m5mols_video_ops = { .s_stream = m5mols_s_stream, }; -static int m5mols_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = to_sd(ctrl); - struct m5mols_info *info = to_m5mols(sd); - int ispstate = info->mode; - int ret; - - /* - * If needed, defer restoring the controls until - * the device is fully initialized. - */ - if (!info->isp_ready) { - info->ctrl_sync = 0; - return 0; - } - - ret = m5mols_mode(info, REG_PARAMETER); - if (ret < 0) - return ret; - ret = m5mols_set_ctrl(ctrl); - if (ret < 0) - return ret; - return m5mols_mode(info, ispstate); -} - -static const struct v4l2_ctrl_ops m5mols_ctrl_ops = { - .s_ctrl = m5mols_s_ctrl, -}; - static int m5mols_sensor_power(struct m5mols_info *info, bool enable) { struct v4l2_subdev *sd = &info->sd; @@ -802,52 +773,6 @@ static int m5mols_fw_start(struct v4l2_subdev *sd) return ret; } -static int m5mols_init_controls(struct m5mols_info *info) -{ - struct v4l2_subdev *sd = &info->sd; - u16 max_exposure; - u16 step_zoom; - int ret; - - /* Determine value's range & step of controls for various FW version */ - ret = m5mols_read_u16(sd, AE_MAX_GAIN_MON, &max_exposure); - if (!ret) - step_zoom = is_manufacturer(info, REG_SAMSUNG_OPTICS) ? 31 : 1; - if (ret) - return ret; - - v4l2_ctrl_handler_init(&info->handle, 6); - info->autowb = v4l2_ctrl_new_std(&info->handle, - &m5mols_ctrl_ops, V4L2_CID_AUTO_WHITE_BALANCE, - 0, 1, 1, 0); - info->saturation = v4l2_ctrl_new_std(&info->handle, - &m5mols_ctrl_ops, V4L2_CID_SATURATION, - 1, 5, 1, 3); - info->zoom = v4l2_ctrl_new_std(&info->handle, - &m5mols_ctrl_ops, V4L2_CID_ZOOM_ABSOLUTE, - 1, 70, step_zoom, 1); - info->exposure = v4l2_ctrl_new_std(&info->handle, - &m5mols_ctrl_ops, V4L2_CID_EXPOSURE, - 0, max_exposure, 1, (int)max_exposure/2); - info->colorfx = v4l2_ctrl_new_std_menu(&info->handle, - &m5mols_ctrl_ops, V4L2_CID_COLORFX, - 4, (1 << V4L2_COLORFX_BW), V4L2_COLORFX_NONE); - info->autoexposure = v4l2_ctrl_new_std_menu(&info->handle, - &m5mols_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, - 1, 0, V4L2_EXPOSURE_AUTO); - - sd->ctrl_handler = &info->handle; - if (info->handle.error) { - v4l2_err(sd, "Failed to initialize controls: %d\n", ret); - v4l2_ctrl_handler_free(&info->handle); - return info->handle.error; - } - - v4l2_ctrl_cluster(2, &info->autoexposure); - - return 0; -} - /** * m5mols_s_power - Main sensor power control function * @@ -868,7 +793,7 @@ static int m5mols_s_power(struct v4l2_subdev *sd, int on) } if (is_manufacturer(info, REG_SAMSUNG_TECHWIN)) { - ret = m5mols_mode(info, REG_MONITOR); + ret = m5mols_set_mode(info, REG_MONITOR); if (!ret) ret = m5mols_write(sd, AF_EXECUTE, REG_AF_STOP); if (!ret) @@ -1010,7 +935,7 @@ static int __devinit m5mols_probe(struct i2c_client *client, ret = m5mols_fw_start(sd); if (!ret) - ret = m5mols_init_controls(info); + ret = m5mols_init_controls(sd); m5mols_sensor_power(info, false); if (!ret) diff --git a/drivers/media/video/m5mols/m5mols_reg.h b/drivers/media/video/m5mols/m5mols_reg.h index ae4aced0f9b2..14d4be72aeff 100644 --- a/drivers/media/video/m5mols/m5mols_reg.h +++ b/drivers/media/video/m5mols/m5mols_reg.h @@ -310,6 +310,7 @@ #define REG_JPEG 0x10 #define CAPP_MAIN_IMAGE_SIZE I2C_REG(CAT_CAPT_PARM, 0x01, 1) +#define CAPP_JPEG_RATIO I2C_REG(CAT_CAPT_PARM, 0x17, 1) #define CAPP_MCC_MODE I2C_REG(CAT_CAPT_PARM, 0x1d, 1) #define REG_MCC_OFF 0x00 diff --git a/drivers/media/video/mem2mem_testdev.c b/drivers/media/video/mem2mem_testdev.c index 12897e8a3314..ee3efbd83bdb 100644 --- a/drivers/media/video/mem2mem_testdev.c +++ b/drivers/media/video/mem2mem_testdev.c @@ -958,6 +958,10 @@ static int m2mtest_probe(struct platform_device *pdev) } *vfd = m2mtest_videodev; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags); vfd->lock = &dev->dev_mutex; ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); diff --git a/drivers/media/video/meye.c b/drivers/media/video/meye.c index b09a3c80a15e..7bc775219f97 100644 --- a/drivers/media/video/meye.c +++ b/drivers/media/video/meye.c @@ -1570,7 +1570,7 @@ static long vidioc_default(struct file *file, void *fh, bool valid_prio, return meyeioc_stilljcapt((int *) arg); default: - return -EINVAL; + return -ENOTTY; } } diff --git a/drivers/media/video/msp3400-driver.c b/drivers/media/video/msp3400-driver.c index 82ce50721de3..aeb22be7dcbd 100644 --- a/drivers/media/video/msp3400-driver.c +++ b/drivers/media/video/msp3400-driver.c @@ -597,19 +597,23 @@ static int msp_log_status(struct v4l2_subdev *sd) return 0; } -static int msp_suspend(struct i2c_client *client, pm_message_t state) +#ifdef CONFIG_PM_SLEEP +static int msp_suspend(struct device *dev) { + struct i2c_client *client = to_i2c_client(dev); v4l_dbg(1, msp_debug, client, "suspend\n"); msp_reset(client); return 0; } -static int msp_resume(struct i2c_client *client) +static int msp_resume(struct device *dev) { + struct i2c_client *client = to_i2c_client(dev); v4l_dbg(1, msp_debug, client, "resume\n"); msp_wake_thread(client); return 0; } +#endif /* ----------------------------------------------------------------------- */ @@ -863,6 +867,10 @@ static int msp_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ +static const struct dev_pm_ops msp3400_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(msp_suspend, msp_resume) +}; + static const struct i2c_device_id msp_id[] = { { "msp3400", 0 }, { } @@ -873,11 +881,10 @@ static struct i2c_driver msp_driver = { .driver = { .owner = THIS_MODULE, .name = "msp3400", + .pm = &msp3400_pm_ops, }, .probe = msp_probe, .remove = msp_remove, - .suspend = msp_suspend, - .resume = msp_resume, .id_table = msp_id, }; diff --git a/drivers/media/video/mt9m032.c b/drivers/media/video/mt9m032.c index 645973c5feb0..3c1e626139b7 100644 --- a/drivers/media/video/mt9m032.c +++ b/drivers/media/video/mt9m032.c @@ -838,9 +838,9 @@ static int mt9m032_remove(struct i2c_client *client) struct v4l2_subdev *subdev = i2c_get_clientdata(client); struct mt9m032 *sensor = to_mt9m032(subdev); - v4l2_device_unregister_subdev(&sensor->subdev); + v4l2_device_unregister_subdev(subdev); v4l2_ctrl_handler_free(&sensor->ctrls); - media_entity_cleanup(&sensor->subdev.entity); + media_entity_cleanup(&subdev->entity); mutex_destroy(&sensor->lock); kfree(sensor); return 0; diff --git a/drivers/media/video/mt9p031.c b/drivers/media/video/mt9p031.c index c81eaf4fbe01..8f061d9ac443 100644 --- a/drivers/media/video/mt9p031.c +++ b/drivers/media/video/mt9p031.c @@ -14,6 +14,7 @@ #include <linux/delay.h> #include <linux/device.h> +#include <linux/gpio.h> #include <linux/module.h> #include <linux/i2c.h> #include <linux/log2.h> @@ -90,7 +91,14 @@ #define MT9P031_GLOBAL_GAIN_MAX 1024 #define MT9P031_GLOBAL_GAIN_DEF 8 #define MT9P031_GLOBAL_GAIN_MULT (1 << 6) +#define MT9P031_ROW_BLACK_TARGET 0x49 #define MT9P031_ROW_BLACK_DEF_OFFSET 0x4b +#define MT9P031_GREEN1_OFFSET 0x60 +#define MT9P031_GREEN2_OFFSET 0x61 +#define MT9P031_BLACK_LEVEL_CALIBRATION 0x62 +#define MT9P031_BLC_MANUAL_BLC (1 << 0) +#define MT9P031_RED_OFFSET 0x63 +#define MT9P031_BLUE_OFFSET 0x64 #define MT9P031_TEST_PATTERN 0xa0 #define MT9P031_TEST_PATTERN_SHIFT 3 #define MT9P031_TEST_PATTERN_ENABLE (1 << 0) @@ -99,17 +107,27 @@ #define MT9P031_TEST_PATTERN_RED 0xa2 #define MT9P031_TEST_PATTERN_BLUE 0xa3 +enum mt9p031_model { + MT9P031_MODEL_COLOR, + MT9P031_MODEL_MONOCHROME, +}; + struct mt9p031 { struct v4l2_subdev subdev; struct media_pad pad; struct v4l2_rect crop; /* Sensor window */ struct v4l2_mbus_framefmt format; - struct v4l2_ctrl_handler ctrls; struct mt9p031_platform_data *pdata; struct mutex power_lock; /* lock to protect power_count */ int power_count; + enum mt9p031_model model; struct aptina_pll pll; + int reset; + + struct v4l2_ctrl_handler ctrls; + struct v4l2_ctrl *blc_auto; + struct v4l2_ctrl *blc_offset; /* Registers cache */ u16 output_control; @@ -241,8 +259,8 @@ static inline int mt9p031_pll_disable(struct mt9p031 *mt9p031) static int mt9p031_power_on(struct mt9p031 *mt9p031) { /* Ensure RESET_BAR is low */ - if (mt9p031->pdata->reset) { - mt9p031->pdata->reset(&mt9p031->subdev, 1); + if (mt9p031->reset != -1) { + gpio_set_value(mt9p031->reset, 0); usleep_range(1000, 2000); } @@ -252,8 +270,8 @@ static int mt9p031_power_on(struct mt9p031 *mt9p031) mt9p031->pdata->ext_freq); /* Now RESET_BAR must be high */ - if (mt9p031->pdata->reset) { - mt9p031->pdata->reset(&mt9p031->subdev, 0); + if (mt9p031->reset != -1) { + gpio_set_value(mt9p031->reset, 1); usleep_range(1000, 2000); } @@ -262,8 +280,8 @@ static int mt9p031_power_on(struct mt9p031 *mt9p031) static void mt9p031_power_off(struct mt9p031 *mt9p031) { - if (mt9p031->pdata->reset) { - mt9p031->pdata->reset(&mt9p031->subdev, 1); + if (mt9p031->reset != -1) { + gpio_set_value(mt9p031->reset, 0); usleep_range(1000, 2000); } @@ -557,6 +575,10 @@ static int mt9p031_set_crop(struct v4l2_subdev *subdev, */ #define V4L2_CID_TEST_PATTERN (V4L2_CID_USER_BASE | 0x1001) +#define V4L2_CID_BLC_AUTO (V4L2_CID_USER_BASE | 0x1002) +#define V4L2_CID_BLC_TARGET_LEVEL (V4L2_CID_USER_BASE | 0x1003) +#define V4L2_CID_BLC_ANALOG_OFFSET (V4L2_CID_USER_BASE | 0x1004) +#define V4L2_CID_BLC_DIGITAL_OFFSET (V4L2_CID_USER_BASE | 0x1005) static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl) { @@ -621,11 +643,17 @@ static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_TEST_PATTERN: if (!ctrl->val) { - ret = mt9p031_set_mode2(mt9p031, - 0, MT9P031_READ_MODE_2_ROW_BLC); - if (ret < 0) - return ret; - + /* Restore the black level compensation settings. */ + if (mt9p031->blc_auto->cur.val != 0) { + ret = mt9p031_s_ctrl(mt9p031->blc_auto); + if (ret < 0) + return ret; + } + if (mt9p031->blc_offset->cur.val != 0) { + ret = mt9p031_s_ctrl(mt9p031->blc_offset); + if (ret < 0) + return ret; + } return mt9p031_write(client, MT9P031_TEST_PATTERN, MT9P031_TEST_PATTERN_DISABLE); } @@ -640,10 +668,14 @@ static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl) if (ret < 0) return ret; + /* Disable digital black level compensation when using a test + * pattern. + */ ret = mt9p031_set_mode2(mt9p031, MT9P031_READ_MODE_2_ROW_BLC, 0); if (ret < 0) return ret; + ret = mt9p031_write(client, MT9P031_ROW_BLACK_DEF_OFFSET, 0); if (ret < 0) return ret; @@ -651,7 +683,40 @@ static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl) return mt9p031_write(client, MT9P031_TEST_PATTERN, ((ctrl->val - 1) << MT9P031_TEST_PATTERN_SHIFT) | MT9P031_TEST_PATTERN_ENABLE); + + case V4L2_CID_BLC_AUTO: + ret = mt9p031_set_mode2(mt9p031, + ctrl->val ? 0 : MT9P031_READ_MODE_2_ROW_BLC, + ctrl->val ? MT9P031_READ_MODE_2_ROW_BLC : 0); + if (ret < 0) + return ret; + + return mt9p031_write(client, MT9P031_BLACK_LEVEL_CALIBRATION, + ctrl->val ? 0 : MT9P031_BLC_MANUAL_BLC); + + case V4L2_CID_BLC_TARGET_LEVEL: + return mt9p031_write(client, MT9P031_ROW_BLACK_TARGET, + ctrl->val); + + case V4L2_CID_BLC_ANALOG_OFFSET: + data = ctrl->val & ((1 << 9) - 1); + + ret = mt9p031_write(client, MT9P031_GREEN1_OFFSET, data); + if (ret < 0) + return ret; + ret = mt9p031_write(client, MT9P031_GREEN2_OFFSET, data); + if (ret < 0) + return ret; + ret = mt9p031_write(client, MT9P031_RED_OFFSET, data); + if (ret < 0) + return ret; + return mt9p031_write(client, MT9P031_BLUE_OFFSET, data); + + case V4L2_CID_BLC_DIGITAL_OFFSET: + return mt9p031_write(client, MT9P031_ROW_BLACK_DEF_OFFSET, + ctrl->val & ((1 << 12) - 1)); } + return 0; } @@ -685,6 +750,46 @@ static const struct v4l2_ctrl_config mt9p031_ctrls[] = { .flags = 0, .menu_skip_mask = 0, .qmenu = mt9p031_test_pattern_menu, + }, { + .ops = &mt9p031_ctrl_ops, + .id = V4L2_CID_BLC_AUTO, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "BLC, Auto", + .min = 0, + .max = 1, + .step = 1, + .def = 1, + .flags = 0, + }, { + .ops = &mt9p031_ctrl_ops, + .id = V4L2_CID_BLC_TARGET_LEVEL, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "BLC Target Level", + .min = 0, + .max = 4095, + .step = 1, + .def = 168, + .flags = 0, + }, { + .ops = &mt9p031_ctrl_ops, + .id = V4L2_CID_BLC_ANALOG_OFFSET, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "BLC Analog Offset", + .min = -255, + .max = 255, + .step = 1, + .def = 32, + .flags = 0, + }, { + .ops = &mt9p031_ctrl_ops, + .id = V4L2_CID_BLC_DIGITAL_OFFSET, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "BLC Digital Offset", + .min = -2048, + .max = 2047, + .step = 1, + .def = 40, + .flags = 0, } }; @@ -764,7 +869,7 @@ static int mt9p031_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) format = v4l2_subdev_get_try_format(fh, 0); - if (mt9p031->pdata->version == MT9P031_MONOCHROME_VERSION) + if (mt9p031->model == MT9P031_MODEL_MONOCHROME) format->code = V4L2_MBUS_FMT_Y12_1X12; else format->code = V4L2_MBUS_FMT_SGRBG12_1X12; @@ -842,6 +947,8 @@ static int mt9p031_probe(struct i2c_client *client, mt9p031->pdata = pdata; mt9p031->output_control = MT9P031_OUTPUT_CONTROL_DEF; mt9p031->mode2 = MT9P031_READ_MODE_2_ROW_BLC; + mt9p031->model = did->driver_data; + mt9p031->reset = -1; v4l2_ctrl_handler_init(&mt9p031->ctrls, ARRAY_SIZE(mt9p031_ctrls) + 4); @@ -862,9 +969,16 @@ static int mt9p031_probe(struct i2c_client *client, mt9p031->subdev.ctrl_handler = &mt9p031->ctrls; - if (mt9p031->ctrls.error) + if (mt9p031->ctrls.error) { printk(KERN_INFO "%s: control initialization error %d\n", __func__, mt9p031->ctrls.error); + ret = mt9p031->ctrls.error; + goto done; + } + + mt9p031->blc_auto = v4l2_ctrl_find(&mt9p031->ctrls, V4L2_CID_BLC_AUTO); + mt9p031->blc_offset = v4l2_ctrl_find(&mt9p031->ctrls, + V4L2_CID_BLC_DIGITAL_OFFSET); mutex_init(&mt9p031->power_lock); v4l2_i2c_subdev_init(&mt9p031->subdev, client, &mt9p031_subdev_ops); @@ -882,7 +996,7 @@ static int mt9p031_probe(struct i2c_client *client, mt9p031->crop.left = MT9P031_COLUMN_START_DEF; mt9p031->crop.top = MT9P031_ROW_START_DEF; - if (mt9p031->pdata->version == MT9P031_MONOCHROME_VERSION) + if (mt9p031->model == MT9P031_MODEL_MONOCHROME) mt9p031->format.code = V4L2_MBUS_FMT_Y12_1X12; else mt9p031->format.code = V4L2_MBUS_FMT_SGRBG12_1X12; @@ -892,10 +1006,22 @@ static int mt9p031_probe(struct i2c_client *client, mt9p031->format.field = V4L2_FIELD_NONE; mt9p031->format.colorspace = V4L2_COLORSPACE_SRGB; + if (pdata->reset != -1) { + ret = gpio_request_one(pdata->reset, GPIOF_OUT_INIT_LOW, + "mt9p031_rst"); + if (ret < 0) + goto done; + + mt9p031->reset = pdata->reset; + } + ret = mt9p031_pll_setup(mt9p031); done: if (ret < 0) { + if (mt9p031->reset != -1) + gpio_free(mt9p031->reset); + v4l2_ctrl_handler_free(&mt9p031->ctrls); media_entity_cleanup(&mt9p031->subdev.entity); kfree(mt9p031); @@ -912,13 +1038,16 @@ static int mt9p031_remove(struct i2c_client *client) v4l2_ctrl_handler_free(&mt9p031->ctrls); v4l2_device_unregister_subdev(subdev); media_entity_cleanup(&subdev->entity); + if (mt9p031->reset != -1) + gpio_free(mt9p031->reset); kfree(mt9p031); return 0; } static const struct i2c_device_id mt9p031_id[] = { - { "mt9p031", 0 }, + { "mt9p031", MT9P031_MODEL_COLOR }, + { "mt9p031m", MT9P031_MODEL_MONOCHROME }, { } }; MODULE_DEVICE_TABLE(i2c, mt9p031_id); diff --git a/drivers/media/video/mt9v032.c b/drivers/media/video/mt9v032.c index 75e253a343c5..4ba4884c016e 100644 --- a/drivers/media/video/mt9v032.c +++ b/drivers/media/video/mt9v032.c @@ -481,7 +481,7 @@ static int mt9v032_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_EXPOSURE_AUTO: return mt9v032_update_aec_agc(mt9v032, MT9V032_AEC_ENABLE, - ctrl->val); + !ctrl->val); case V4L2_CID_EXPOSURE: return mt9v032_write(client, MT9V032_TOTAL_SHUTTER_WIDTH, diff --git a/drivers/media/video/mx2_emmaprp.c b/drivers/media/video/mx2_emmaprp.c index ba89a7401c8c..0bd5815de369 100644 --- a/drivers/media/video/mx2_emmaprp.c +++ b/drivers/media/video/mx2_emmaprp.c @@ -755,7 +755,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, memset(src_vq, 0, sizeof(*src_vq)); src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; - src_vq->io_modes = VB2_MMAP; + src_vq->io_modes = VB2_MMAP | VB2_USERPTR; src_vq->drv_priv = ctx; src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); src_vq->ops = &emmaprp_qops; @@ -767,7 +767,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, memset(dst_vq, 0, sizeof(*dst_vq)); dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - dst_vq->io_modes = VB2_MMAP; + dst_vq->io_modes = VB2_MMAP | VB2_USERPTR; dst_vq->drv_priv = ctx; dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); dst_vq->ops = &emmaprp_qops; @@ -904,6 +904,10 @@ static int emmaprp_probe(struct platform_device *pdev) } *vfd = emmaprp_videodev; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags); vfd->lock = &pcdev->dev_mutex; video_set_drvdata(vfd, pcdev); diff --git a/drivers/media/video/mxb.c b/drivers/media/video/mxb.c index 2e4131748438..b520a45cb3f3 100644 --- a/drivers/media/video/mxb.c +++ b/drivers/media/video/mxb.c @@ -31,10 +31,11 @@ #include <media/saa7115.h> #include <linux/module.h> -#include "mxb.h" #include "tea6415c.h" #include "tea6420.h" +#define MXB_AUDIOS 6 + #define I2C_SAA7111A 0x24 #define I2C_TDA9840 0x42 #define I2C_TEA6415C 0x43 @@ -62,10 +63,14 @@ MODULE_PARM_DESC(debug, "Turn on/off device debugging (default:off)."); enum { TUNER, AUX1, AUX3, AUX3_YC }; static struct v4l2_input mxb_inputs[MXB_INPUTS] = { - { TUNER, "Tuner", V4L2_INPUT_TYPE_TUNER, 1, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { AUX1, "AUX1", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { AUX3, "AUX3 Composite", V4L2_INPUT_TYPE_CAMERA, 4, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { AUX3_YC, "AUX3 S-Video", V4L2_INPUT_TYPE_CAMERA, 4, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, + { TUNER, "Tuner", V4L2_INPUT_TYPE_TUNER, 0x3f, 0, + V4L2_STD_PAL_BG | V4L2_STD_PAL_I, 0, V4L2_IN_CAP_STD }, + { AUX1, "AUX1", V4L2_INPUT_TYPE_CAMERA, 0x3f, 0, + V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { AUX3, "AUX3 Composite", V4L2_INPUT_TYPE_CAMERA, 0x3f, 0, + V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { AUX3_YC, "AUX3 S-Video", V4L2_INPUT_TYPE_CAMERA, 0x3f, 0, + V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, }; /* this array holds the information, which port of the saa7146 each @@ -90,6 +95,36 @@ struct mxb_routing { u32 output; }; +/* these are the available audio sources, which can switched + to the line- and cd-output individually */ +static struct v4l2_audio mxb_audios[MXB_AUDIOS] = { + { + .index = 0, + .name = "Tuner", + .capability = V4L2_AUDCAP_STEREO, + } , { + .index = 1, + .name = "AUX1", + .capability = V4L2_AUDCAP_STEREO, + } , { + .index = 2, + .name = "AUX2", + .capability = V4L2_AUDCAP_STEREO, + } , { + .index = 3, + .name = "AUX3", + .capability = V4L2_AUDCAP_STEREO, + } , { + .index = 4, + .name = "Radio (X9)", + .capability = V4L2_AUDCAP_STEREO, + } , { + .index = 5, + .name = "CD-ROM (X10)", + .capability = V4L2_AUDCAP_STEREO, + } +}; + /* These are the necessary input-output-pins for bringing one audio source (see above) to the CD-output. Note that gain is set to 0 in this table. */ static struct mxb_routing TEA6420_cd[MXB_AUDIOS + 1][2] = { @@ -114,11 +149,6 @@ static struct mxb_routing TEA6420_line[MXB_AUDIOS + 1][2] = { { { 6, 3 }, { 6, 2 } } /* Mute */ }; -#define MAXCONTROLS 1 -static struct v4l2_queryctrl mxb_controls[] = { - { V4L2_CID_AUDIO_MUTE, V4L2_CTRL_TYPE_BOOLEAN, "Mute", 0, 1, 1, 0, 0 }, -}; - struct mxb { struct video_device *video_dev; @@ -135,6 +165,7 @@ struct mxb int cur_mode; /* current audio mode (mono, stereo, ...) */ int cur_input; /* current input */ + int cur_audinput; /* current audio input */ int cur_mute; /* current mute status */ struct v4l2_frequency cur_freq; /* current frequency the tuner is tuned to */ }; @@ -150,16 +181,21 @@ struct mxb #define call_all(dev, o, f, args...) \ v4l2_device_call_until_err(&dev->v4l2_dev, 0, o, f, ##args) -static inline void tea6420_route_cd(struct mxb *mxb, int idx) +static void mxb_update_audmode(struct mxb *mxb) +{ + struct v4l2_tuner t = { + .audmode = mxb->cur_mode, + }; + + tda9840_call(mxb, tuner, s_tuner, &t); +} + +static inline void tea6420_route(struct mxb *mxb, int idx) { v4l2_subdev_call(mxb->tea6420_1, audio, s_routing, TEA6420_cd[idx][0].input, TEA6420_cd[idx][0].output, 0); v4l2_subdev_call(mxb->tea6420_2, audio, s_routing, TEA6420_cd[idx][1].input, TEA6420_cd[idx][1].output, 0); -} - -static inline void tea6420_route_line(struct mxb *mxb, int idx) -{ v4l2_subdev_call(mxb->tea6420_1, audio, s_routing, TEA6420_line[idx][0].input, TEA6420_line[idx][0].output, 0); v4l2_subdev_call(mxb->tea6420_2, audio, s_routing, @@ -168,16 +204,45 @@ static inline void tea6420_route_line(struct mxb *mxb, int idx) static struct saa7146_extension extension; +static int mxb_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct saa7146_dev *dev = container_of(ctrl->handler, + struct saa7146_dev, ctrl_handler); + struct mxb *mxb = dev->ext_priv; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + mxb->cur_mute = ctrl->val; + /* switch the audio-source */ + tea6420_route(mxb, ctrl->val ? 6 : + video_audio_connect[mxb->cur_input]); + break; + default: + return -EINVAL; + } + return 0; +} + +static const struct v4l2_ctrl_ops mxb_ctrl_ops = { + .s_ctrl = mxb_s_ctrl, +}; + static int mxb_probe(struct saa7146_dev *dev) { + struct v4l2_ctrl_handler *hdl = &dev->ctrl_handler; struct mxb *mxb = NULL; + v4l2_ctrl_new_std(hdl, &mxb_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1); + if (hdl->error) + return hdl->error; mxb = kzalloc(sizeof(struct mxb), GFP_KERNEL); if (mxb == NULL) { DEB_D("not enough kernel memory\n"); return -ENOMEM; } + snprintf(mxb->i2c_adapter.name, sizeof(mxb->i2c_adapter.name), "mxb%d", mxb_num); saa7146_i2c_adapter_prepare(dev, &mxb->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480); @@ -214,6 +279,8 @@ static int mxb_probe(struct saa7146_dev *dev) /* we store the pointer in our private data field */ dev->ext_priv = mxb; + v4l2_ctrl_handler_setup(hdl); + return 0; } @@ -286,6 +353,9 @@ static int mxb_init_done(struct saa7146_dev* dev) int i = 0, err = 0; + /* mute audio on tea6420s */ + tea6420_route(mxb, 6); + /* select video mode in saa7111a */ saa7111a_call(mxb, core, s_std, std); @@ -306,12 +376,12 @@ static int mxb_init_done(struct saa7146_dev* dev) tuner_call(mxb, tuner, s_frequency, &mxb->cur_freq); /* set a default video standard */ + /* These two gpio calls set the GPIO pins that control the tda9820 */ + saa7146_write(dev, GPIO_CTRL, 0x00404050); + saa7111a_call(mxb, core, s_gpio, 1); + saa7111a_call(mxb, core, s_std, std); tuner_call(mxb, core, s_std, std); - /* mute audio on tea6420s */ - tea6420_route_line(mxb, 6); - tea6420_route_cd(mxb, 6); - /* switch to tuner-channel on tea6415c */ tea6415c_call(mxb, video, s_routing, 3, 17, 0); @@ -320,9 +390,11 @@ static int mxb_init_done(struct saa7146_dev* dev) /* the rest for mxb */ mxb->cur_input = 0; + mxb->cur_audinput = video_audio_connect[mxb->cur_input]; mxb->cur_mute = 1; mxb->cur_mode = V4L2_TUNER_MODE_STEREO; + mxb_update_audmode(mxb); /* check if the saa7740 (aka 'sound arena module') is present on the mxb. if so, we must initialize it. due to lack of @@ -385,69 +457,6 @@ void mxb_irq_bh(struct saa7146_dev* dev, u32* irq_mask) } */ -static int vidioc_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qc) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - int i; - - for (i = MAXCONTROLS - 1; i >= 0; i--) { - if (mxb_controls[i].id == qc->id) { - *qc = mxb_controls[i]; - DEB_D("VIDIOC_QUERYCTRL %d\n", qc->id); - return 0; - } - } - return dev->ext_vv_data->core_ops->vidioc_queryctrl(file, fh, qc); -} - -static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *vc) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct mxb *mxb = (struct mxb *)dev->ext_priv; - int i; - - for (i = MAXCONTROLS - 1; i >= 0; i--) { - if (mxb_controls[i].id == vc->id) - break; - } - - if (i < 0) - return dev->ext_vv_data->core_ops->vidioc_g_ctrl(file, fh, vc); - - if (vc->id == V4L2_CID_AUDIO_MUTE) { - vc->value = mxb->cur_mute; - DEB_D("VIDIOC_G_CTRL V4L2_CID_AUDIO_MUTE:%d\n", vc->value); - return 0; - } - - DEB_EE("VIDIOC_G_CTRL V4L2_CID_AUDIO_MUTE:%d\n", vc->value); - return 0; -} - -static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *vc) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct mxb *mxb = (struct mxb *)dev->ext_priv; - int i = 0; - - for (i = MAXCONTROLS - 1; i >= 0; i--) { - if (mxb_controls[i].id == vc->id) - break; - } - - if (i < 0) - return dev->ext_vv_data->core_ops->vidioc_s_ctrl(file, fh, vc); - - if (vc->id == V4L2_CID_AUDIO_MUTE) { - mxb->cur_mute = vc->value; - /* switch the audio-source */ - tea6420_route_line(mxb, vc->value ? 6 : - video_audio_connect[mxb->cur_input]); - DEB_EE("VIDIOC_S_CTRL, V4L2_CID_AUDIO_MUTE: %d\n", vc->value); - } - return 0; -} - static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i) { DEB_EE("VIDIOC_ENUMINPUT %d\n", i->index); @@ -519,9 +528,12 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int input) if (saa7111a_call(mxb, video, s_routing, i, SAA7111_FMT_CCIR, 0)) pr_err("VIDIOC_S_INPUT: could not address saa7111a\n"); + mxb->cur_audinput = video_audio_connect[input]; /* switch the audio-source only if necessary */ if (0 == mxb->cur_mute) - tea6420_route_line(mxb, video_audio_connect[input]); + tea6420_route(mxb, mxb->cur_audinput); + if (mxb->cur_audinput == 0) + mxb_update_audmode(mxb); return 0; } @@ -563,17 +575,20 @@ static int vidioc_s_tuner(struct file *file, void *fh, struct v4l2_tuner *t) return call_all(dev, tuner, s_tuner, t); } +static int vidioc_querystd(struct file *file, void *fh, v4l2_std_id *norm) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + + return call_all(dev, video, querystd, norm); +} + static int vidioc_g_frequency(struct file *file, void *fh, struct v4l2_frequency *f) { struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; struct mxb *mxb = (struct mxb *)dev->ext_priv; - if (mxb->cur_input) { - DEB_D("VIDIOC_G_FREQ: channel %d does not have a tuner!\n", - mxb->cur_input); + if (f->tuner) return -EINVAL; - } - *f = mxb->cur_freq; DEB_EE("VIDIOC_G_FREQ: freq:0x%08x\n", mxb->cur_freq.frequency); @@ -592,17 +607,18 @@ static int vidioc_s_frequency(struct file *file, void *fh, struct v4l2_frequency if (V4L2_TUNER_ANALOG_TV != f->type) return -EINVAL; - if (mxb->cur_input) { - DEB_D("VIDIOC_S_FREQ: channel %d does not have a tuner!\n", - mxb->cur_input); - return -EINVAL; - } - - mxb->cur_freq = *f; DEB_EE("VIDIOC_S_FREQUENCY: freq:0x%08x\n", mxb->cur_freq.frequency); /* tune in desired frequency */ - tuner_call(mxb, tuner, s_frequency, &mxb->cur_freq); + tuner_call(mxb, tuner, s_frequency, f); + /* let the tuner subdev clamp the frequency to the tuner range */ + tuner_call(mxb, tuner, g_frequency, f); + mxb->cur_freq = *f; + if (mxb->cur_audinput == 0) + mxb_update_audmode(mxb); + + if (mxb->cur_input) + return 0; /* hack: changing the frequency should invalidate the vbi-counter (=> alevt) */ spin_lock(&dev->slock); @@ -612,25 +628,40 @@ static int vidioc_s_frequency(struct file *file, void *fh, struct v4l2_frequency return 0; } +static int vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *a) +{ + if (a->index >= MXB_AUDIOS) + return -EINVAL; + *a = mxb_audios[a->index]; + return 0; +} + static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a) { struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; struct mxb *mxb = (struct mxb *)dev->ext_priv; - if (a->index > MXB_INPUTS) { - DEB_D("VIDIOC_G_AUDIO %d out of range\n", a->index); - return -EINVAL; - } - - DEB_EE("VIDIOC_G_AUDIO %d\n", a->index); - memcpy(a, &mxb_audios[video_audio_connect[mxb->cur_input]], sizeof(struct v4l2_audio)); + DEB_EE("VIDIOC_G_AUDIO\n"); + *a = mxb_audios[mxb->cur_audinput]; return 0; } static int vidioc_s_audio(struct file *file, void *fh, struct v4l2_audio *a) { + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct mxb *mxb = (struct mxb *)dev->ext_priv; + DEB_D("VIDIOC_S_AUDIO %d\n", a->index); - return 0; + if (mxb_inputs[mxb->cur_input].audioset & (1 << a->index)) { + if (mxb->cur_audinput != a->index) { + mxb->cur_audinput = a->index; + tea6420_route(mxb, a->index); + if (mxb->cur_audinput == 0) + mxb_update_audmode(mxb); + } + return 0; + } + return -EINVAL; } #ifdef CONFIG_VIDEO_ADV_DEBUG @@ -638,60 +669,31 @@ static int vidioc_g_register(struct file *file, void *fh, struct v4l2_dbg_regist { struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - return call_all(dev, core, g_register, reg); + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (v4l2_chip_match_host(®->match)) { + reg->val = saa7146_read(dev, reg->reg); + reg->size = 4; + return 0; + } + call_all(dev, core, g_register, reg); + return 0; } static int vidioc_s_register(struct file *file, void *fh, struct v4l2_dbg_register *reg) { struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - return call_all(dev, core, s_register, reg); -} -#endif - -static long vidioc_default(struct file *file, void *fh, bool valid_prio, - int cmd, void *arg) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct mxb *mxb = (struct mxb *)dev->ext_priv; - - switch (cmd) { - case MXB_S_AUDIO_CD: - { - int i = *(int *)arg; - - if (i < 0 || i >= MXB_AUDIOS) { - DEB_D("invalid argument to MXB_S_AUDIO_CD: i:%d\n", i); - return -EINVAL; - } - - DEB_EE("MXB_S_AUDIO_CD: i:%d\n", i); - - tea6420_route_cd(mxb, i); - return 0; - } - case MXB_S_AUDIO_LINE: - { - int i = *(int *)arg; - - if (i < 0 || i >= MXB_AUDIOS) { - DEB_D("invalid argument to MXB_S_AUDIO_LINE: i:%d\n", - i); - return -EINVAL; - } - - DEB_EE("MXB_S_AUDIO_LINE: i:%d\n", i); - tea6420_route_line(mxb, i); + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (v4l2_chip_match_host(®->match)) { + saa7146_write(dev, reg->reg, reg->val); + reg->size = 4; return 0; } - default: -/* - DEB2(pr_err("does not handle this ioctl\n")); -*/ - return -ENOIOCTLCMD; - } - return 0; + return call_all(dev, core, s_register, reg); } +#endif static struct saa7146_ext_vv vv_data; @@ -709,23 +711,21 @@ static int mxb_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data } mxb = (struct mxb *)dev->ext_priv; - vv_data.ops.vidioc_queryctrl = vidioc_queryctrl; - vv_data.ops.vidioc_g_ctrl = vidioc_g_ctrl; - vv_data.ops.vidioc_s_ctrl = vidioc_s_ctrl; - vv_data.ops.vidioc_enum_input = vidioc_enum_input; - vv_data.ops.vidioc_g_input = vidioc_g_input; - vv_data.ops.vidioc_s_input = vidioc_s_input; - vv_data.ops.vidioc_g_tuner = vidioc_g_tuner; - vv_data.ops.vidioc_s_tuner = vidioc_s_tuner; - vv_data.ops.vidioc_g_frequency = vidioc_g_frequency; - vv_data.ops.vidioc_s_frequency = vidioc_s_frequency; - vv_data.ops.vidioc_g_audio = vidioc_g_audio; - vv_data.ops.vidioc_s_audio = vidioc_s_audio; + vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input; + vv_data.vid_ops.vidioc_g_input = vidioc_g_input; + vv_data.vid_ops.vidioc_s_input = vidioc_s_input; + vv_data.vid_ops.vidioc_querystd = vidioc_querystd; + vv_data.vid_ops.vidioc_g_tuner = vidioc_g_tuner; + vv_data.vid_ops.vidioc_s_tuner = vidioc_s_tuner; + vv_data.vid_ops.vidioc_g_frequency = vidioc_g_frequency; + vv_data.vid_ops.vidioc_s_frequency = vidioc_s_frequency; + vv_data.vid_ops.vidioc_enumaudio = vidioc_enumaudio; + vv_data.vid_ops.vidioc_g_audio = vidioc_g_audio; + vv_data.vid_ops.vidioc_s_audio = vidioc_s_audio; #ifdef CONFIG_VIDEO_ADV_DEBUG - vv_data.ops.vidioc_g_register = vidioc_g_register; - vv_data.ops.vidioc_s_register = vidioc_s_register; + vv_data.vid_ops.vidioc_g_register = vidioc_g_register; + vv_data.vid_ops.vidioc_s_register = vidioc_s_register; #endif - vv_data.ops.vidioc_default = vidioc_default; if (saa7146_register_device(&mxb->video_dev, dev, "mxb", VFL_TYPE_GRABBER)) { ERR("cannot register capture v4l2 device. skipping.\n"); saa7146_vv_release(dev); @@ -752,6 +752,9 @@ static int mxb_detach(struct saa7146_dev *dev) DEB_EE("dev:%p\n", dev); + /* mute audio on tea6420s */ + tea6420_route(mxb, 6); + saa7146_unregister_device(&mxb->video_dev,dev); if (MXB_BOARD_CAN_DO_VBI(dev)) saa7146_unregister_device(&mxb->vbi_dev, dev); @@ -773,20 +776,24 @@ static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *standa v4l2_std_id std = V4L2_STD_PAL_I; DEB_D("VIDIOC_S_STD: setting mxb for PAL_I\n"); - /* set the 7146 gpio register -- I don't know what this does exactly */ + /* These two gpio calls set the GPIO pins that control the tda9820 */ saa7146_write(dev, GPIO_CTRL, 0x00404050); - /* unset the 7111 gpio register -- I don't know what this does exactly */ saa7111a_call(mxb, core, s_gpio, 0); - tuner_call(mxb, core, s_std, std); + saa7111a_call(mxb, core, s_std, std); + if (mxb->cur_input == 0) + tuner_call(mxb, core, s_std, std); } else { v4l2_std_id std = V4L2_STD_PAL_BG; + if (mxb->cur_input) + std = standard->id; DEB_D("VIDIOC_S_STD: setting mxb for PAL/NTSC/SECAM\n"); - /* set the 7146 gpio register -- I don't know what this does exactly */ + /* These two gpio calls set the GPIO pins that control the tda9820 */ saa7146_write(dev, GPIO_CTRL, 0x00404050); - /* set the 7111 gpio register -- I don't know what this does exactly */ saa7111a_call(mxb, core, s_gpio, 1); - tuner_call(mxb, core, s_std, std); + saa7111a_call(mxb, core, s_std, std); + if (mxb->cur_input == 0) + tuner_call(mxb, core, s_std, std); } return 0; } @@ -836,14 +843,14 @@ MODULE_DEVICE_TABLE(pci, pci_tbl); static struct saa7146_ext_vv vv_data = { .inputs = MXB_INPUTS, - .capabilities = V4L2_CAP_TUNER | V4L2_CAP_VBI_CAPTURE, + .capabilities = V4L2_CAP_TUNER | V4L2_CAP_VBI_CAPTURE | V4L2_CAP_AUDIO, .stds = &standard[0], .num_stds = sizeof(standard)/sizeof(struct saa7146_standard), .std_callback = &std_callback, }; static struct saa7146_extension extension = { - .name = MXB_IDENTIFIER, + .name = "Multimedia eXtension Board", .flags = SAA7146_USE_I2C_IRQ, .pci_tbl = &pci_tbl[0], diff --git a/drivers/media/video/mxb.h b/drivers/media/video/mxb.h deleted file mode 100644 index 400a57ba62ec..000000000000 --- a/drivers/media/video/mxb.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef __MXB__ -#define __MXB__ - -#define BASE_VIDIOC_MXB 10 - -#define MXB_S_AUDIO_CD _IOW ('V', BASE_VIDIOC_PRIVATE+BASE_VIDIOC_MXB+0, int) -#define MXB_S_AUDIO_LINE _IOW ('V', BASE_VIDIOC_PRIVATE+BASE_VIDIOC_MXB+1, int) - -#define MXB_IDENTIFIER "Multimedia eXtension Board" - -#define MXB_AUDIOS 6 - -/* these are the available audio sources, which can switched - to the line- and cd-output individually */ -static struct v4l2_audio mxb_audios[MXB_AUDIOS] = { - { - .index = 0, - .name = "Tuner", - .capability = V4L2_AUDCAP_STEREO, - } , { - .index = 1, - .name = "AUX1", - .capability = V4L2_AUDCAP_STEREO, - } , { - .index = 2, - .name = "AUX2", - .capability = V4L2_AUDCAP_STEREO, - } , { - .index = 3, - .name = "AUX3", - .capability = V4L2_AUDCAP_STEREO, - } , { - .index = 4, - .name = "Radio (X9)", - .capability = V4L2_AUDCAP_STEREO, - } , { - .index = 5, - .name = "CD-ROM (X10)", - .capability = V4L2_AUDCAP_STEREO, - } -}; -#endif diff --git a/drivers/media/video/omap24xxcam-dma.c b/drivers/media/video/omap24xxcam-dma.c index 3ea38a8def8e..b5ae170de4a5 100644 --- a/drivers/media/video/omap24xxcam-dma.c +++ b/drivers/media/video/omap24xxcam-dma.c @@ -38,7 +38,7 @@ */ /* Ack all interrupt on CSR and IRQSTATUS_L0 */ -static void omap24xxcam_dmahw_ack_all(unsigned long base) +static void omap24xxcam_dmahw_ack_all(void __iomem *base) { u32 csr; int i; @@ -52,7 +52,7 @@ static void omap24xxcam_dmahw_ack_all(unsigned long base) } /* Ack dmach on CSR and IRQSTATUS_L0 */ -static u32 omap24xxcam_dmahw_ack_ch(unsigned long base, int dmach) +static u32 omap24xxcam_dmahw_ack_ch(void __iomem *base, int dmach) { u32 csr; @@ -65,12 +65,12 @@ static u32 omap24xxcam_dmahw_ack_ch(unsigned long base, int dmach) return csr; } -static int omap24xxcam_dmahw_running(unsigned long base, int dmach) +static int omap24xxcam_dmahw_running(void __iomem *base, int dmach) { return omap24xxcam_reg_in(base, CAMDMA_CCR(dmach)) & CAMDMA_CCR_ENABLE; } -static void omap24xxcam_dmahw_transfer_setup(unsigned long base, int dmach, +static void omap24xxcam_dmahw_transfer_setup(void __iomem *base, int dmach, dma_addr_t start, u32 len) { omap24xxcam_reg_out(base, CAMDMA_CCR(dmach), @@ -112,7 +112,7 @@ static void omap24xxcam_dmahw_transfer_setup(unsigned long base, int dmach, | CAMDMA_CICR_DROP_IE); } -static void omap24xxcam_dmahw_transfer_start(unsigned long base, int dmach) +static void omap24xxcam_dmahw_transfer_start(void __iomem *base, int dmach) { omap24xxcam_reg_out(base, CAMDMA_CCR(dmach), CAMDMA_CCR_SEL_SRC_DST_SYNC @@ -124,7 +124,7 @@ static void omap24xxcam_dmahw_transfer_start(unsigned long base, int dmach) | CAMDMA_CCR_SYNCHRO_CAMERA); } -static void omap24xxcam_dmahw_transfer_chain(unsigned long base, int dmach, +static void omap24xxcam_dmahw_transfer_chain(void __iomem *base, int dmach, int free_dmach) { int prev_dmach, ch; @@ -160,7 +160,7 @@ static void omap24xxcam_dmahw_transfer_chain(unsigned long base, int dmach, * controller may not be idle after this routine completes, because * the completion routines might start new transfers. */ -static void omap24xxcam_dmahw_abort_ch(unsigned long base, int dmach) +static void omap24xxcam_dmahw_abort_ch(void __iomem *base, int dmach) { /* mask all interrupts from this channel */ omap24xxcam_reg_out(base, CAMDMA_CICR(dmach), 0); @@ -171,7 +171,7 @@ static void omap24xxcam_dmahw_abort_ch(unsigned long base, int dmach) omap24xxcam_reg_merge(base, CAMDMA_CCR(dmach), 0, CAMDMA_CCR_ENABLE); } -static void omap24xxcam_dmahw_init(unsigned long base) +static void omap24xxcam_dmahw_init(void __iomem *base) { omap24xxcam_reg_out(base, CAMDMA_OCP_SYSCONFIG, CAMDMA_OCP_SYSCONFIG_MIDLEMODE_FSTANDBY @@ -362,7 +362,7 @@ void omap24xxcam_dma_hwinit(struct omap24xxcam_dma *dma) } static void omap24xxcam_dma_init(struct omap24xxcam_dma *dma, - unsigned long base) + void __iomem *base) { int ch; @@ -577,7 +577,7 @@ void omap24xxcam_sgdma_sync(struct omap24xxcam_sgdma *sgdma) } void omap24xxcam_sgdma_init(struct omap24xxcam_sgdma *sgdma, - unsigned long base, + void __iomem *base, void (*reset_callback)(unsigned long data), unsigned long reset_callback_data) { diff --git a/drivers/media/video/omap24xxcam.c b/drivers/media/video/omap24xxcam.c index 7d3864144368..e5015b0d5508 100644 --- a/drivers/media/video/omap24xxcam.c +++ b/drivers/media/video/omap24xxcam.c @@ -1776,8 +1776,7 @@ static int __devinit omap24xxcam_probe(struct platform_device *pdev) cam->mmio_size = resource_size(mem); /* map the region */ - cam->mmio_base = (unsigned long) - ioremap_nocache(cam->mmio_base_phys, cam->mmio_size); + cam->mmio_base = ioremap_nocache(cam->mmio_base_phys, cam->mmio_size); if (!cam->mmio_base) { dev_err(cam->dev, "cannot map camera register I/O region\n"); goto err; diff --git a/drivers/media/video/omap24xxcam.h b/drivers/media/video/omap24xxcam.h index 2ce67f5a48d5..d59727afe894 100644 --- a/drivers/media/video/omap24xxcam.h +++ b/drivers/media/video/omap24xxcam.h @@ -429,7 +429,7 @@ struct sgdma_state { struct omap24xxcam_dma { spinlock_t lock; /* Lock for the whole structure. */ - unsigned long base; /* base address for dma controller */ + void __iomem *base; /* base address for dma controller */ /* While dma_stop!=0, an attempt to start a new DMA transfer will * fail. @@ -491,7 +491,7 @@ struct omap24xxcam_device { /*** hardware resources ***/ unsigned int irq; - unsigned long mmio_base; + void __iomem *mmio_base; unsigned long mmio_base_phys; unsigned long mmio_size; @@ -544,22 +544,22 @@ struct omap24xxcam_fh { * */ -static inline u32 omap24xxcam_reg_in(unsigned long base, u32 offset) +static inline u32 omap24xxcam_reg_in(u32 __iomem *base, u32 offset) { return readl(base + offset); } -static inline u32 omap24xxcam_reg_out(unsigned long base, u32 offset, +static inline u32 omap24xxcam_reg_out(u32 __iomem *base, u32 offset, u32 val) { writel(val, base + offset); return val; } -static inline u32 omap24xxcam_reg_merge(unsigned long base, u32 offset, +static inline u32 omap24xxcam_reg_merge(u32 __iomem *base, u32 offset, u32 val, u32 mask) { - u32 addr = base + offset; + u32 __iomem *addr = base + offset; u32 new_val = (readl(addr) & ~mask) | (val & mask); writel(new_val, addr); @@ -585,7 +585,7 @@ int omap24xxcam_sgdma_queue(struct omap24xxcam_sgdma *sgdma, int len, sgdma_callback_t callback, void *arg); void omap24xxcam_sgdma_sync(struct omap24xxcam_sgdma *sgdma); void omap24xxcam_sgdma_init(struct omap24xxcam_sgdma *sgdma, - unsigned long base, + void __iomem *base, void (*reset_callback)(unsigned long data), unsigned long reset_callback_data); void omap24xxcam_sgdma_exit(struct omap24xxcam_sgdma *sgdma); diff --git a/drivers/media/video/omap3isp/isp.c b/drivers/media/video/omap3isp/isp.c index 12d5f923e1d0..1c347633e663 100644 --- a/drivers/media/video/omap3isp/isp.c +++ b/drivers/media/video/omap3isp/isp.c @@ -329,19 +329,6 @@ void omap3isp_configure_bridge(struct isp_device *isp, isp_reg_writel(isp, ispctrl_val, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL); } -/** - * isp_set_pixel_clock - Configures the ISP pixel clock - * @isp: OMAP3 ISP device - * @pixelclk: Average pixel clock in Hz - * - * Set the average pixel clock required by the sensor. The ISP will use the - * lowest possible memory bandwidth settings compatible with the clock. - **/ -static void isp_set_pixel_clock(struct isp_device *isp, unsigned int pixelclk) -{ - isp->isp_ccdc.vpcfg.pixelclk = pixelclk; -} - void omap3isp_hist_dma_done(struct isp_device *isp) { if (omap3isp_ccdc_busy(&isp->isp_ccdc) || @@ -739,6 +726,17 @@ static int isp_pipeline_enable(struct isp_pipeline *pipe, unsigned long flags; int ret; + /* If the preview engine crashed it might not respond to read/write + * operations on the L4 bus. This would result in a bus fault and a + * kernel oops. Refuse to start streaming in that case. This check must + * be performed before the loop below to avoid starting entities if the + * pipeline won't start anyway (those entities would then likely fail to + * stop, making the problem worse). + */ + if ((pipe->entities & isp->crashed) & + (1U << isp->isp_prev.subdev.entity.id)) + return -EIO; + spin_lock_irqsave(&pipe->lock, flags); pipe->state &= ~(ISP_PIPELINE_IDLE_INPUT | ISP_PIPELINE_IDLE_OUTPUT); spin_unlock_irqrestore(&pipe->lock, flags); @@ -774,14 +772,6 @@ static int isp_pipeline_enable(struct isp_pipeline *pipe, } } - /* Frame number propagation. In continuous streaming mode the number - * is incremented in the frame start ISR. In mem-to-mem mode - * singleshot is used and frame start IRQs are not available. - * Thus we have to increment the number here. - */ - if (pipe->do_propagation && mode == ISP_PIPELINE_STREAM_SINGLESHOT) - atomic_inc(&pipe->frame_number); - return 0; } @@ -879,13 +869,15 @@ static int isp_pipeline_disable(struct isp_pipeline *pipe) if (ret) { dev_info(isp->dev, "Unable to stop %s\n", subdev->name); + /* If the entity failed to stopped, assume it has + * crashed. Mark it as such, the ISP will be reset when + * applications will release it. + */ + isp->crashed |= 1U << subdev->entity.id; failure = -ETIMEDOUT; } } - if (failure < 0) - isp->needs_reset = true; - return failure; } @@ -1069,6 +1061,7 @@ static int isp_reset(struct isp_device *isp) udelay(1); } + isp->crashed = 0; return 0; } @@ -1495,11 +1488,13 @@ void omap3isp_put(struct isp_device *isp) BUG_ON(isp->ref_count == 0); if (--isp->ref_count == 0) { isp_disable_interrupts(isp); - isp_save_ctx(isp); - if (isp->needs_reset) { + if (isp->domain) + isp_save_ctx(isp); + /* Reset the ISP if an entity has failed to stop. This is the + * only way to recover from such conditions. + */ + if (isp->crashed) isp_reset(isp); - isp->needs_reset = false; - } isp_disable_clocks(isp); } mutex_unlock(&isp->isp_mutex); @@ -1970,7 +1965,7 @@ error_csiphy: * * Always returns 0. */ -static int isp_remove(struct platform_device *pdev) +static int __devexit isp_remove(struct platform_device *pdev) { struct isp_device *isp = platform_get_drvdata(pdev); int i; @@ -1981,6 +1976,7 @@ static int isp_remove(struct platform_device *pdev) omap3isp_get(isp); iommu_detach_device(isp->domain, &pdev->dev); iommu_domain_free(isp->domain); + isp->domain = NULL; omap3isp_put(isp); free_irq(isp->irq_num, isp); @@ -2050,7 +2046,7 @@ static int isp_map_mem_resource(struct platform_device *pdev, * -EINVAL if couldn't install ISR, * or clk_get return error value. */ -static int isp_probe(struct platform_device *pdev) +static int __devinit isp_probe(struct platform_device *pdev) { struct isp_platform_data *pdata = pdev->dev.platform_data; struct isp_device *isp; @@ -2068,7 +2064,6 @@ static int isp_probe(struct platform_device *pdev) isp->autoidle = autoidle; isp->platform_cb.set_xclk = isp_set_xclk; - isp->platform_cb.set_pixel_clock = isp_set_pixel_clock; mutex_init(&isp->isp_mutex); spin_lock_init(&isp->stat_lock); @@ -2218,7 +2213,7 @@ MODULE_DEVICE_TABLE(platform, omap3isp_id_table); static struct platform_driver omap3isp_driver = { .probe = isp_probe, - .remove = isp_remove, + .remove = __devexit_p(isp_remove), .id_table = omap3isp_id_table, .driver = { .owner = THIS_MODULE, diff --git a/drivers/media/video/omap3isp/isp.h b/drivers/media/video/omap3isp/isp.h index d96603eb0d17..fc7af3e32efd 100644 --- a/drivers/media/video/omap3isp/isp.h +++ b/drivers/media/video/omap3isp/isp.h @@ -129,7 +129,6 @@ struct isp_platform_callback { int (*csiphy_config)(struct isp_csiphy *phy, struct isp_csiphy_dphy_cfg *dphy, struct isp_csiphy_lanes_cfg *lanes); - void (*set_pixel_clock)(struct isp_device *isp, unsigned int pixelclk); }; /* @@ -145,6 +144,7 @@ struct isp_platform_callback { * @raw_dmamask: Raw DMA mask * @stat_lock: Spinlock for handling statistics * @isp_mutex: Mutex for serializing requests to ISP. + * @crashed: Bitmask of crashed entities (indexed by entity ID) * @has_context: Context has been saved at least once and can be restored. * @ref_count: Reference count for handling multiple ISP requests. * @cam_ick: Pointer to camera interface clock structure. @@ -184,7 +184,7 @@ struct isp_device { /* ISP Obj */ spinlock_t stat_lock; /* common lock for statistic drivers */ struct mutex isp_mutex; /* For handling ref_count field */ - bool needs_reset; + u32 crashed; int has_context; int ref_count; unsigned int autoidle; @@ -237,10 +237,6 @@ void omap3isp_configure_bridge(struct isp_device *isp, const struct isp_parallel_platform_data *pdata, unsigned int shift); -#define ISP_XCLK_NONE 0 -#define ISP_XCLK_A 1 -#define ISP_XCLK_B 2 - struct isp_device *omap3isp_get(struct isp_device *isp); void omap3isp_put(struct isp_device *isp); diff --git a/drivers/media/video/omap3isp/ispccdc.c b/drivers/media/video/omap3isp/ispccdc.c index eaabc27f0fa2..7e32331b60fb 100644 --- a/drivers/media/video/omap3isp/ispccdc.c +++ b/drivers/media/video/omap3isp/ispccdc.c @@ -38,6 +38,9 @@ #include "ispreg.h" #include "ispccdc.h" +#define CCDC_MIN_WIDTH 32 +#define CCDC_MIN_HEIGHT 32 + static struct v4l2_mbus_framefmt * __ccdc_get_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, unsigned int pad, enum v4l2_subdev_format_whence which); @@ -836,8 +839,8 @@ static void ccdc_config_vp(struct isp_ccdc_device *ccdc) if (pipe->input) div = DIV_ROUND_UP(l3_ick, pipe->max_rate); - else if (ccdc->vpcfg.pixelclk) - div = l3_ick / ccdc->vpcfg.pixelclk; + else if (pipe->external_rate) + div = l3_ick / pipe->external_rate; div = clamp(div, 2U, max_div); fmtcfg_vp |= (div - 2) << ISPCCDC_FMTCFG_VPIF_FRQ_SHIFT; @@ -1118,6 +1121,7 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc) struct isp_parallel_platform_data *pdata = NULL; struct v4l2_subdev *sensor; struct v4l2_mbus_framefmt *format; + const struct v4l2_rect *crop; const struct isp_format_info *fmt_info; struct v4l2_subdev_format fmt_src; unsigned int depth_out; @@ -1211,14 +1215,14 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc) OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VDINT); /* CCDC_PAD_SOURCE_OF */ - format = &ccdc->formats[CCDC_PAD_SOURCE_OF]; + crop = &ccdc->crop; - isp_reg_writel(isp, (0 << ISPCCDC_HORZ_INFO_SPH_SHIFT) | - ((format->width - 1) << ISPCCDC_HORZ_INFO_NPH_SHIFT), + isp_reg_writel(isp, (crop->left << ISPCCDC_HORZ_INFO_SPH_SHIFT) | + ((crop->width - 1) << ISPCCDC_HORZ_INFO_NPH_SHIFT), OMAP3_ISP_IOMEM_CCDC, ISPCCDC_HORZ_INFO); - isp_reg_writel(isp, 0 << ISPCCDC_VERT_START_SLV0_SHIFT, + isp_reg_writel(isp, crop->top << ISPCCDC_VERT_START_SLV0_SHIFT, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VERT_START); - isp_reg_writel(isp, (format->height - 1) + isp_reg_writel(isp, (crop->height - 1) << ISPCCDC_VERT_LINES_NLV_SHIFT, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VERT_LINES); @@ -1410,6 +1414,9 @@ static void ccdc_hs_vs_isr(struct isp_ccdc_device *ccdc) struct video_device *vdev = ccdc->subdev.devnode; struct v4l2_event event; + /* Frame number propagation */ + atomic_inc(&pipe->frame_number); + memset(&event, 0, sizeof(event)); event.type = V4L2_EVENT_FRAME_SYNC; event.u.frame_sync.frame_sequence = atomic_read(&pipe->frame_number); @@ -1703,7 +1710,7 @@ static int ccdc_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, if (sub->id != 0) return -EINVAL; - return v4l2_event_subscribe(fh, sub, OMAP3ISP_CCDC_NEVENTS); + return v4l2_event_subscribe(fh, sub, OMAP3ISP_CCDC_NEVENTS, NULL); } static int ccdc_unsubscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, @@ -1790,6 +1797,16 @@ __ccdc_get_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, return &ccdc->formats[pad]; } +static struct v4l2_rect * +__ccdc_get_crop(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, + enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_crop(fh, CCDC_PAD_SOURCE_OF); + else + return &ccdc->crop; +} + /* * ccdc_try_format - Try video format on a pad * @ccdc: ISP CCDC device @@ -1806,6 +1823,7 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, const struct isp_format_info *info; unsigned int width = fmt->width; unsigned int height = fmt->height; + struct v4l2_rect *crop; unsigned int i; switch (pad) { @@ -1831,14 +1849,10 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, which); memcpy(fmt, format, sizeof(*fmt)); - /* The data formatter truncates the number of horizontal output - * pixels to a multiple of 16. To avoid clipping data, allow - * callers to request an output size bigger than the input size - * up to the nearest multiple of 16. - */ - fmt->width = clamp_t(u32, width, 32, fmt->width + 15); - fmt->width &= ~15; - fmt->height = clamp_t(u32, height, 32, fmt->height); + /* Hardcode the output size to the crop rectangle size. */ + crop = __ccdc_get_crop(ccdc, fh, which); + fmt->width = crop->width; + fmt->height = crop->height; break; case CCDC_PAD_SOURCE_VP: @@ -1866,6 +1880,49 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, } /* + * ccdc_try_crop - Validate a crop rectangle + * @ccdc: ISP CCDC device + * @sink: format on the sink pad + * @crop: crop rectangle to be validated + */ +static void ccdc_try_crop(struct isp_ccdc_device *ccdc, + const struct v4l2_mbus_framefmt *sink, + struct v4l2_rect *crop) +{ + const struct isp_format_info *info; + unsigned int max_width; + + /* For Bayer formats, restrict left/top and width/height to even values + * to keep the Bayer pattern. + */ + info = omap3isp_video_format_info(sink->code); + if (info->flavor != V4L2_MBUS_FMT_Y8_1X8) { + crop->left &= ~1; + crop->top &= ~1; + } + + crop->left = clamp_t(u32, crop->left, 0, sink->width - CCDC_MIN_WIDTH); + crop->top = clamp_t(u32, crop->top, 0, sink->height - CCDC_MIN_HEIGHT); + + /* The data formatter truncates the number of horizontal output pixels + * to a multiple of 16. To avoid clipping data, allow callers to request + * an output size bigger than the input size up to the nearest multiple + * of 16. + */ + max_width = (sink->width - crop->left + 15) & ~15; + crop->width = clamp_t(u32, crop->width, CCDC_MIN_WIDTH, max_width) + & ~15; + crop->height = clamp_t(u32, crop->height, CCDC_MIN_HEIGHT, + sink->height - crop->top); + + /* Odd width/height values don't make sense for Bayer formats. */ + if (info->flavor != V4L2_MBUS_FMT_Y8_1X8) { + crop->width &= ~1; + crop->height &= ~1; + } +} + +/* * ccdc_enum_mbus_code - Handle pixel format enumeration * @sd : pointer to v4l2 subdev structure * @fh : V4L2 subdev file handle @@ -1937,6 +1994,93 @@ static int ccdc_enum_frame_size(struct v4l2_subdev *sd, } /* + * ccdc_get_selection - Retrieve a selection rectangle on a pad + * @sd: ISP CCDC V4L2 subdevice + * @fh: V4L2 subdev file handle + * @sel: Selection rectangle + * + * The only supported rectangles are the crop rectangles on the output formatter + * source pad. + * + * Return 0 on success or a negative error code otherwise. + */ +static int ccdc_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) +{ + struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + if (sel->pad != CCDC_PAD_SOURCE_OF) + return -EINVAL; + + switch (sel->target) { + case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS: + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = INT_MAX; + sel->r.height = INT_MAX; + + format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, sel->which); + ccdc_try_crop(ccdc, format, &sel->r); + break; + + case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL: + sel->r = *__ccdc_get_crop(ccdc, fh, sel->which); + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* + * ccdc_set_selection - Set a selection rectangle on a pad + * @sd: ISP CCDC V4L2 subdevice + * @fh: V4L2 subdev file handle + * @sel: Selection rectangle + * + * The only supported rectangle is the actual crop rectangle on the output + * formatter source pad. + * + * Return 0 on success or a negative error code otherwise. + */ +static int ccdc_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) +{ + struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + if (sel->target != V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL || + sel->pad != CCDC_PAD_SOURCE_OF) + return -EINVAL; + + /* The crop rectangle can't be changed while streaming. */ + if (ccdc->state != ISP_PIPELINE_STREAM_STOPPED) + return -EBUSY; + + /* Modifying the crop rectangle always changes the format on the source + * pad. If the KEEP_CONFIG flag is set, just return the current crop + * rectangle. + */ + if (sel->flags & V4L2_SUBDEV_SEL_FLAG_KEEP_CONFIG) { + sel->r = *__ccdc_get_crop(ccdc, fh, sel->which); + return 0; + } + + format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, sel->which); + ccdc_try_crop(ccdc, format, &sel->r); + *__ccdc_get_crop(ccdc, fh, sel->which) = sel->r; + + /* Update the source format. */ + format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SOURCE_OF, sel->which); + ccdc_try_format(ccdc, fh, CCDC_PAD_SOURCE_OF, format, sel->which); + + return 0; +} + +/* * ccdc_get_format - Retrieve the video format on a pad * @sd : ISP CCDC V4L2 subdevice * @fh : V4L2 subdev file handle @@ -1973,6 +2117,7 @@ static int ccdc_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, { struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; + struct v4l2_rect *crop; format = __ccdc_get_format(ccdc, fh, fmt->pad, fmt->which); if (format == NULL) @@ -1983,6 +2128,16 @@ static int ccdc_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, /* Propagate the format from sink to source */ if (fmt->pad == CCDC_PAD_SINK) { + /* Reset the crop rectangle. */ + crop = __ccdc_get_crop(ccdc, fh, fmt->which); + crop->left = 0; + crop->top = 0; + crop->width = fmt->format.width; + crop->height = fmt->format.height; + + ccdc_try_crop(ccdc, &fmt->format, crop); + + /* Update the source formats. */ format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SOURCE_OF, fmt->which); *format = fmt->format; @@ -2000,6 +2155,69 @@ static int ccdc_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, } /* + * Decide whether desired output pixel code can be obtained with + * the lane shifter by shifting the input pixel code. + * @in: input pixelcode to shifter + * @out: output pixelcode from shifter + * @additional_shift: # of bits the sensor's LSB is offset from CAMEXT[0] + * + * return true if the combination is possible + * return false otherwise + */ +static bool ccdc_is_shiftable(enum v4l2_mbus_pixelcode in, + enum v4l2_mbus_pixelcode out, + unsigned int additional_shift) +{ + const struct isp_format_info *in_info, *out_info; + + if (in == out) + return true; + + in_info = omap3isp_video_format_info(in); + out_info = omap3isp_video_format_info(out); + + if ((in_info->flavor == 0) || (out_info->flavor == 0)) + return false; + + if (in_info->flavor != out_info->flavor) + return false; + + return in_info->bpp - out_info->bpp + additional_shift <= 6; +} + +static int ccdc_link_validate(struct v4l2_subdev *sd, + struct media_link *link, + struct v4l2_subdev_format *source_fmt, + struct v4l2_subdev_format *sink_fmt) +{ + struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); + unsigned long parallel_shift; + + /* Check if the two ends match */ + if (source_fmt->format.width != sink_fmt->format.width || + source_fmt->format.height != sink_fmt->format.height) + return -EPIPE; + + /* We've got a parallel sensor here. */ + if (ccdc->input == CCDC_INPUT_PARALLEL) { + struct isp_parallel_platform_data *pdata = + &((struct isp_v4l2_subdevs_group *) + media_entity_to_v4l2_subdev(link->source->entity) + ->host_priv)->bus.parallel; + parallel_shift = pdata->data_lane_shift * 2; + } else { + parallel_shift = 0; + } + + /* Lane shifter may be used to drop bits on CCDC sink pad */ + if (!ccdc_is_shiftable(source_fmt->format.code, + sink_fmt->format.code, parallel_shift)) + return -EPIPE; + + return 0; +} + +/* * ccdc_init_formats - Initialize formats on all pads * @sd: ISP CCDC V4L2 subdevice * @fh: V4L2 subdev file handle @@ -2041,6 +2259,9 @@ static const struct v4l2_subdev_pad_ops ccdc_v4l2_pad_ops = { .enum_frame_size = ccdc_enum_frame_size, .get_fmt = ccdc_get_format, .set_fmt = ccdc_set_format, + .get_selection = ccdc_get_selection, + .set_selection = ccdc_set_selection, + .link_validate = ccdc_link_validate, }; /* V4L2 subdev operations */ @@ -2150,6 +2371,7 @@ static int ccdc_link_setup(struct media_entity *entity, /* media operations */ static const struct media_entity_operations ccdc_media_ops = { .link_setup = ccdc_link_setup, + .link_validate = v4l2_subdev_link_validate, }; void omap3isp_ccdc_unregister_entities(struct isp_ccdc_device *ccdc) @@ -2276,8 +2498,6 @@ int omap3isp_ccdc_init(struct isp_device *isp) ccdc->clamp.oblen = 0; ccdc->clamp.dcsubval = 0; - ccdc->vpcfg.pixelclk = 0; - ccdc->update = OMAP3ISP_CCDC_BLCLAMP; ccdc_apply_controls(ccdc); diff --git a/drivers/media/video/omap3isp/ispccdc.h b/drivers/media/video/omap3isp/ispccdc.h index 6d0264bab75b..890f6b3a68fd 100644 --- a/drivers/media/video/omap3isp/ispccdc.h +++ b/drivers/media/video/omap3isp/ispccdc.h @@ -80,14 +80,6 @@ struct ispccdc_syncif { u8 bt_r656_en; }; -/* - * struct ispccdc_vp - Structure for Video Port parameters - * @pixelclk: Input pixel clock in Hz - */ -struct ispccdc_vp { - unsigned int pixelclk; -}; - enum ispccdc_lsc_state { LSC_STATE_STOPPED = 0, LSC_STATE_STOPPING = 1, @@ -147,6 +139,7 @@ struct ispccdc_lsc { * @subdev: V4L2 subdevice * @pads: Sink and source media entity pads * @formats: Active video formats + * @crop: Active crop rectangle on the OF source pad * @input: Active input * @output: Active outputs * @video_out: Output video node @@ -161,7 +154,6 @@ struct ispccdc_lsc { * @update: Bitmask of controls to update during the next interrupt * @shadow_update: Controls update in progress by userspace * @syncif: Interface synchronization configuration - * @vpcfg: Video port configuration * @underrun: A buffer underrun occurred and a new buffer has been queued * @state: Streaming state * @lock: Serializes shadow_update with interrupt handler @@ -173,6 +165,7 @@ struct isp_ccdc_device { struct v4l2_subdev subdev; struct media_pad pads[CCDC_PADS_NUM]; struct v4l2_mbus_framefmt formats[CCDC_PADS_NUM]; + struct v4l2_rect crop; enum ccdc_input_entity input; unsigned int output; @@ -190,7 +183,6 @@ struct isp_ccdc_device { unsigned int shadow_update; struct ispccdc_syncif syncif; - struct ispccdc_vp vpcfg; unsigned int underrun:1; enum isp_pipeline_stream_state state; diff --git a/drivers/media/video/omap3isp/ispccp2.c b/drivers/media/video/omap3isp/ispccp2.c index 70ddbf35b223..85f0de85f37c 100644 --- a/drivers/media/video/omap3isp/ispccp2.c +++ b/drivers/media/video/omap3isp/ispccp2.c @@ -161,7 +161,6 @@ static void ccp2_pwr_cfg(struct isp_ccp2_device *ccp2) static void ccp2_if_enable(struct isp_ccp2_device *ccp2, u8 enable) { struct isp_device *isp = to_isp_device(ccp2); - struct isp_pipeline *pipe = to_isp_pipeline(&ccp2->subdev.entity); int i; if (enable && ccp2->vdds_csib) @@ -178,19 +177,6 @@ static void ccp2_if_enable(struct isp_ccp2_device *ccp2, u8 enable) ISPCCP2_CTRL_MODE | ISPCCP2_CTRL_IF_EN, enable ? (ISPCCP2_CTRL_MODE | ISPCCP2_CTRL_IF_EN) : 0); - /* For frame count propagation */ - if (pipe->do_propagation) { - /* We may want the Frame Start IRQ from LC0 */ - if (enable) - isp_reg_set(isp, OMAP3_ISP_IOMEM_CCP2, - ISPCCP2_LC01_IRQENABLE, - ISPCCP2_LC01_IRQSTATUS_LC0_FS_IRQ); - else - isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCP2, - ISPCCP2_LC01_IRQENABLE, - ISPCCP2_LC01_IRQSTATUS_LC0_FS_IRQ); - } - if (!enable && ccp2->vdds_csib) regulator_disable(ccp2->vdds_csib); } @@ -350,7 +336,6 @@ static void ccp2_lcx_config(struct isp_ccp2_device *ccp2, ISPCCP2_LC01_IRQSTATUS_LC0_CRC_IRQ | ISPCCP2_LC01_IRQSTATUS_LC0_FSP_IRQ | ISPCCP2_LC01_IRQSTATUS_LC0_FW_IRQ | - ISPCCP2_LC01_IRQSTATUS_LC0_FS_IRQ | ISPCCP2_LC01_IRQSTATUS_LC0_FSC_IRQ | ISPCCP2_LC01_IRQSTATUS_LC0_SSC_IRQ; @@ -613,14 +598,6 @@ void omap3isp_ccp2_isr(struct isp_ccp2_device *ccp2) if (omap3isp_module_sync_is_stopping(&ccp2->wait, &ccp2->stopping)) return; - /* Frame number propagation */ - if (lcx_irqstatus & ISPCCP2_LC01_IRQSTATUS_LC0_FS_IRQ) { - struct isp_pipeline *pipe = - to_isp_pipeline(&ccp2->subdev.entity); - if (pipe->do_propagation) - atomic_inc(&pipe->frame_number); - } - /* Handle queued buffers on frame end interrupts */ if (lcm_irqstatus & ISPCCP2_LCM_IRQSTATUS_EOF_IRQ) ccp2_isr_buffer(ccp2); @@ -1021,6 +998,7 @@ static int ccp2_link_setup(struct media_entity *entity, /* media operations */ static const struct media_entity_operations ccp2_media_ops = { .link_setup = ccp2_link_setup, + .link_validate = v4l2_subdev_link_validate, }; /* diff --git a/drivers/media/video/omap3isp/ispcsi2.c b/drivers/media/video/omap3isp/ispcsi2.c index fcb5168996a7..a1724362b6d5 100644 --- a/drivers/media/video/omap3isp/ispcsi2.c +++ b/drivers/media/video/omap3isp/ispcsi2.c @@ -378,21 +378,17 @@ static void csi2_timing_config(struct isp_device *isp, static void csi2_irq_ctx_set(struct isp_device *isp, struct isp_csi2_device *csi2, int enable) { - u32 reg = ISPCSI2_CTX_IRQSTATUS_FE_IRQ; int i; - if (csi2->use_fs_irq) - reg |= ISPCSI2_CTX_IRQSTATUS_FS_IRQ; - for (i = 0; i < 8; i++) { - isp_reg_writel(isp, reg, csi2->regs1, + isp_reg_writel(isp, ISPCSI2_CTX_IRQSTATUS_FE_IRQ, csi2->regs1, ISPCSI2_CTX_IRQSTATUS(i)); if (enable) isp_reg_set(isp, csi2->regs1, ISPCSI2_CTX_IRQENABLE(i), - reg); + ISPCSI2_CTX_IRQSTATUS_FE_IRQ); else isp_reg_clr(isp, csi2->regs1, ISPCSI2_CTX_IRQENABLE(i), - reg); + ISPCSI2_CTX_IRQSTATUS_FE_IRQ); } } @@ -690,14 +686,6 @@ static void csi2_isr_ctx(struct isp_csi2_device *csi2, status = isp_reg_readl(isp, csi2->regs1, ISPCSI2_CTX_IRQSTATUS(n)); isp_reg_writel(isp, status, csi2->regs1, ISPCSI2_CTX_IRQSTATUS(n)); - /* Propagate frame number */ - if (status & ISPCSI2_CTX_IRQSTATUS_FS_IRQ) { - struct isp_pipeline *pipe = - to_isp_pipeline(&csi2->subdev.entity); - if (pipe->do_propagation) - atomic_inc(&pipe->frame_number); - } - if (!(status & ISPCSI2_CTX_IRQSTATUS_FE_IRQ)) return; @@ -1047,14 +1035,12 @@ static int csi2_set_stream(struct v4l2_subdev *sd, int enable) { struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd); struct isp_device *isp = csi2->isp; - struct isp_pipeline *pipe = to_isp_pipeline(&csi2->subdev.entity); struct isp_video *video_out = &csi2->video_out; switch (enable) { case ISP_PIPELINE_STREAM_CONTINUOUS: if (omap3isp_csiphy_acquire(csi2->phy) < 0) return -ENODEV; - csi2->use_fs_irq = pipe->do_propagation; if (csi2->output & CSI2_OUTPUT_MEMORY) omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_CSI2A_WRITE); csi2_configure(csi2); @@ -1181,6 +1167,7 @@ static int csi2_link_setup(struct media_entity *entity, /* media operations */ static const struct media_entity_operations csi2_media_ops = { .link_setup = csi2_link_setup, + .link_validate = v4l2_subdev_link_validate, }; void omap3isp_csi2_unregister_entities(struct isp_csi2_device *csi2) diff --git a/drivers/media/video/omap3isp/ispcsi2.h b/drivers/media/video/omap3isp/ispcsi2.h index 885ad79a7678..c57729b7e86e 100644 --- a/drivers/media/video/omap3isp/ispcsi2.h +++ b/drivers/media/video/omap3isp/ispcsi2.h @@ -145,7 +145,6 @@ struct isp_csi2_device { u32 output; /* output to CCDC, memory or both? */ bool dpcm_decompress; unsigned int frame_skip; - bool use_fs_irq; struct isp_csiphy *phy; struct isp_csi2_ctx_cfg contexts[ISP_CSI2_MAX_CTX_NUM + 1]; diff --git a/drivers/media/video/omap3isp/ispcsiphy.c b/drivers/media/video/omap3isp/ispcsiphy.c index 5be37ce7d0c2..348f67ebbbc9 100644 --- a/drivers/media/video/omap3isp/ispcsiphy.c +++ b/drivers/media/video/omap3isp/ispcsiphy.c @@ -186,7 +186,9 @@ int omap3isp_csiphy_acquire(struct isp_csiphy *phy) if (rval < 0) goto done; - omap3isp_csi2_reset(phy->csi2); + rval = omap3isp_csi2_reset(phy->csi2); + if (rval < 0) + goto done; csiphy_dphy_config(phy); csiphy_lanes_config(phy); diff --git a/drivers/media/video/omap3isp/ispcsiphy.h b/drivers/media/video/omap3isp/ispcsiphy.h index 9596dc6830a6..e93a661e65d9 100644 --- a/drivers/media/video/omap3isp/ispcsiphy.h +++ b/drivers/media/video/omap3isp/ispcsiphy.h @@ -27,22 +27,11 @@ #ifndef OMAP3_ISP_CSI_PHY_H #define OMAP3_ISP_CSI_PHY_H +#include <media/omap3isp.h> + struct isp_csi2_device; struct regulator; -struct csiphy_lane { - u8 pos; - u8 pol; -}; - -#define ISP_CSIPHY2_NUM_DATA_LANES 2 -#define ISP_CSIPHY1_NUM_DATA_LANES 1 - -struct isp_csiphy_lanes_cfg { - struct csiphy_lane data[ISP_CSIPHY2_NUM_DATA_LANES]; - struct csiphy_lane clk; -}; - struct isp_csiphy_dphy_cfg { u8 ths_term; u8 ths_settle; diff --git a/drivers/media/video/omap3isp/isppreview.c b/drivers/media/video/omap3isp/isppreview.c index 6d0fb2c8c26d..8a4935ecc655 100644 --- a/drivers/media/video/omap3isp/isppreview.c +++ b/drivers/media/video/omap3isp/isppreview.c @@ -441,23 +441,6 @@ preview_enable_dcor(struct isp_prev_device *prev, u8 enable) } /* - * preview_enable_cfa - Enable/Disable the CFA Interpolation. - * @enable: 1 - Enables the CFA. - */ -static void -preview_enable_cfa(struct isp_prev_device *prev, u8 enable) -{ - struct isp_device *isp = to_isp_device(prev); - - if (enable) - isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, - ISPPRV_PCR_CFAEN); - else - isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, - ISPPRV_PCR_CFAEN); -} - -/* * preview_enable_gammabypass - Enables/Disables the GammaByPass * @enable: 1 - Bypasses Gamma - 10bit input is cropped to 8MSB. * 0 - Goes through Gamma Correction. input and output is 10bit. @@ -608,12 +591,12 @@ preview_config_rgb_blending(struct isp_prev_device *prev, const void *rgb2rgb) } /* - * Configures the RGB-YCbYCr conversion matrix + * Configures the color space conversion (RGB toYCbYCr) matrix * @prev_csc: Structure containing the RGB to YCbYCr matrix and the * YCbCr offset. */ static void -preview_config_rgb_to_ycbcr(struct isp_prev_device *prev, const void *prev_csc) +preview_config_csc(struct isp_prev_device *prev, const void *prev_csc) { struct isp_device *isp = to_isp_device(prev); const struct omap3isp_prev_csc *csc = prev_csc; @@ -649,12 +632,18 @@ preview_config_rgb_to_ycbcr(struct isp_prev_device *prev, const void *prev_csc) static void preview_update_contrast(struct isp_prev_device *prev, u8 contrast) { - struct prev_params *params = &prev->params; + struct prev_params *params; + unsigned long flags; + + spin_lock_irqsave(&prev->params.lock, flags); + params = (prev->params.active & OMAP3ISP_PREV_CONTRAST) + ? &prev->params.params[0] : &prev->params.params[1]; if (params->contrast != (contrast * ISPPRV_CONTRAST_UNITS)) { params->contrast = contrast * ISPPRV_CONTRAST_UNITS; - prev->update |= PREV_CONTRAST; + params->update |= OMAP3ISP_PREV_CONTRAST; } + spin_unlock_irqrestore(&prev->params.lock, flags); } /* @@ -681,12 +670,18 @@ preview_config_contrast(struct isp_prev_device *prev, const void *params) static void preview_update_brightness(struct isp_prev_device *prev, u8 brightness) { - struct prev_params *params = &prev->params; + struct prev_params *params; + unsigned long flags; + + spin_lock_irqsave(&prev->params.lock, flags); + params = (prev->params.active & OMAP3ISP_PREV_BRIGHTNESS) + ? &prev->params.params[0] : &prev->params.params[1]; if (params->brightness != (brightness * ISPPRV_BRIGHT_UNITS)) { params->brightness = brightness * ISPPRV_BRIGHT_UNITS; - prev->update |= PREV_BRIGHTNESS; + params->update |= OMAP3ISP_PREV_BRIGHTNESS; } + spin_unlock_irqrestore(&prev->params.lock, flags); } /* @@ -721,159 +716,188 @@ preview_config_yc_range(struct isp_prev_device *prev, const void *yclimit) OMAP3_ISP_IOMEM_PREV, ISPPRV_SETUP_YC); } +static u32 +preview_params_lock(struct isp_prev_device *prev, u32 update, bool shadow) +{ + u32 active = prev->params.active; + + if (shadow) { + /* Mark all shadow parameters we are going to touch as busy. */ + prev->params.params[0].busy |= ~active & update; + prev->params.params[1].busy |= active & update; + } else { + /* Mark all active parameters we are going to touch as busy. */ + update = (prev->params.params[0].update & active) + | (prev->params.params[1].update & ~active); + + prev->params.params[0].busy |= active & update; + prev->params.params[1].busy |= ~active & update; + } + + return update; +} + +static void +preview_params_unlock(struct isp_prev_device *prev, u32 update, bool shadow) +{ + u32 active = prev->params.active; + + if (shadow) { + /* Set the update flag for shadow parameters that have been + * updated and clear the busy flag for all shadow parameters. + */ + prev->params.params[0].update |= (~active & update); + prev->params.params[1].update |= (active & update); + prev->params.params[0].busy &= active; + prev->params.params[1].busy &= ~active; + } else { + /* Clear the update flag for active parameters that have been + * applied and the busy flag for all active parameters. + */ + prev->params.params[0].update &= ~(active & update); + prev->params.params[1].update &= ~(~active & update); + prev->params.params[0].busy &= ~active; + prev->params.params[1].busy &= active; + } +} + +static void preview_params_switch(struct isp_prev_device *prev) +{ + u32 to_switch; + + /* Switch active parameters with updated shadow parameters when the + * shadow parameter has been updated and neither the active not the + * shadow parameter is busy. + */ + to_switch = (prev->params.params[0].update & ~prev->params.active) + | (prev->params.params[1].update & prev->params.active); + to_switch &= ~(prev->params.params[0].busy | + prev->params.params[1].busy); + if (to_switch == 0) + return; + + prev->params.active ^= to_switch; + + /* Remove the update flag for the shadow copy of parameters we have + * switched. + */ + prev->params.params[0].update &= ~(~prev->params.active & to_switch); + prev->params.params[1].update &= ~(prev->params.active & to_switch); +} + /* preview parameters update structure */ struct preview_update { - int cfg_bit; - int feature_bit; void (*config)(struct isp_prev_device *, const void *); void (*enable)(struct isp_prev_device *, u8); + unsigned int param_offset; + unsigned int param_size; + unsigned int config_offset; + bool skip; }; -static struct preview_update update_attrs[] = { - {OMAP3ISP_PREV_LUMAENH, PREV_LUMA_ENHANCE, +/* Keep the array indexed by the OMAP3ISP_PREV_* bit number. */ +static const struct preview_update update_attrs[] = { + /* OMAP3ISP_PREV_LUMAENH */ { preview_config_luma_enhancement, - preview_enable_luma_enhancement}, - {OMAP3ISP_PREV_INVALAW, PREV_INVERSE_ALAW, + preview_enable_luma_enhancement, + offsetof(struct prev_params, luma), + FIELD_SIZEOF(struct prev_params, luma), + offsetof(struct omap3isp_prev_update_config, luma), + }, /* OMAP3ISP_PREV_INVALAW */ { NULL, - preview_enable_invalaw}, - {OMAP3ISP_PREV_HRZ_MED, PREV_HORZ_MEDIAN_FILTER, + preview_enable_invalaw, + }, /* OMAP3ISP_PREV_HRZ_MED */ { preview_config_hmed, - preview_enable_hmed}, - {OMAP3ISP_PREV_CFA, PREV_CFA, + preview_enable_hmed, + offsetof(struct prev_params, hmed), + FIELD_SIZEOF(struct prev_params, hmed), + offsetof(struct omap3isp_prev_update_config, hmed), + }, /* OMAP3ISP_PREV_CFA */ { preview_config_cfa, - preview_enable_cfa}, - {OMAP3ISP_PREV_CHROMA_SUPP, PREV_CHROMA_SUPPRESS, + NULL, + offsetof(struct prev_params, cfa), + FIELD_SIZEOF(struct prev_params, cfa), + offsetof(struct omap3isp_prev_update_config, cfa), + }, /* OMAP3ISP_PREV_CHROMA_SUPP */ { preview_config_chroma_suppression, - preview_enable_chroma_suppression}, - {OMAP3ISP_PREV_WB, PREV_WB, + preview_enable_chroma_suppression, + offsetof(struct prev_params, csup), + FIELD_SIZEOF(struct prev_params, csup), + offsetof(struct omap3isp_prev_update_config, csup), + }, /* OMAP3ISP_PREV_WB */ { preview_config_whitebalance, - NULL}, - {OMAP3ISP_PREV_BLKADJ, PREV_BLKADJ, + NULL, + offsetof(struct prev_params, wbal), + FIELD_SIZEOF(struct prev_params, wbal), + offsetof(struct omap3isp_prev_update_config, wbal), + }, /* OMAP3ISP_PREV_BLKADJ */ { preview_config_blkadj, - NULL}, - {OMAP3ISP_PREV_RGB2RGB, PREV_RGB2RGB, + NULL, + offsetof(struct prev_params, blkadj), + FIELD_SIZEOF(struct prev_params, blkadj), + offsetof(struct omap3isp_prev_update_config, blkadj), + }, /* OMAP3ISP_PREV_RGB2RGB */ { preview_config_rgb_blending, - NULL}, - {OMAP3ISP_PREV_COLOR_CONV, PREV_COLOR_CONV, - preview_config_rgb_to_ycbcr, - NULL}, - {OMAP3ISP_PREV_YC_LIMIT, PREV_YCLIMITS, + NULL, + offsetof(struct prev_params, rgb2rgb), + FIELD_SIZEOF(struct prev_params, rgb2rgb), + offsetof(struct omap3isp_prev_update_config, rgb2rgb), + }, /* OMAP3ISP_PREV_COLOR_CONV */ { + preview_config_csc, + NULL, + offsetof(struct prev_params, csc), + FIELD_SIZEOF(struct prev_params, csc), + offsetof(struct omap3isp_prev_update_config, csc), + }, /* OMAP3ISP_PREV_YC_LIMIT */ { preview_config_yc_range, - NULL}, - {OMAP3ISP_PREV_DEFECT_COR, PREV_DEFECT_COR, + NULL, + offsetof(struct prev_params, yclimit), + FIELD_SIZEOF(struct prev_params, yclimit), + offsetof(struct omap3isp_prev_update_config, yclimit), + }, /* OMAP3ISP_PREV_DEFECT_COR */ { preview_config_dcor, - preview_enable_dcor}, - {OMAP3ISP_PREV_GAMMABYPASS, PREV_GAMMA_BYPASS, + preview_enable_dcor, + offsetof(struct prev_params, dcor), + FIELD_SIZEOF(struct prev_params, dcor), + offsetof(struct omap3isp_prev_update_config, dcor), + }, /* OMAP3ISP_PREV_GAMMABYPASS */ { NULL, - preview_enable_gammabypass}, - {OMAP3ISP_PREV_DRK_FRM_CAPTURE, PREV_DARK_FRAME_CAPTURE, + preview_enable_gammabypass, + }, /* OMAP3ISP_PREV_DRK_FRM_CAPTURE */ { NULL, - preview_enable_drkframe_capture}, - {OMAP3ISP_PREV_DRK_FRM_SUBTRACT, PREV_DARK_FRAME_SUBTRACT, + preview_enable_drkframe_capture, + }, /* OMAP3ISP_PREV_DRK_FRM_SUBTRACT */ { NULL, - preview_enable_drkframe}, - {OMAP3ISP_PREV_LENS_SHADING, PREV_LENS_SHADING, + preview_enable_drkframe, + }, /* OMAP3ISP_PREV_LENS_SHADING */ { preview_config_drkf_shadcomp, - preview_enable_drkframe}, - {OMAP3ISP_PREV_NF, PREV_NOISE_FILTER, + preview_enable_drkframe, + }, /* OMAP3ISP_PREV_NF */ { preview_config_noisefilter, - preview_enable_noisefilter}, - {OMAP3ISP_PREV_GAMMA, PREV_GAMMA, + preview_enable_noisefilter, + offsetof(struct prev_params, nf), + FIELD_SIZEOF(struct prev_params, nf), + offsetof(struct omap3isp_prev_update_config, nf), + }, /* OMAP3ISP_PREV_GAMMA */ { preview_config_gammacorrn, - NULL}, - {-1, PREV_CONTRAST, + NULL, + offsetof(struct prev_params, gamma), + FIELD_SIZEOF(struct prev_params, gamma), + offsetof(struct omap3isp_prev_update_config, gamma), + }, /* OMAP3ISP_PREV_CONTRAST */ { preview_config_contrast, - NULL}, - {-1, PREV_BRIGHTNESS, + NULL, + offsetof(struct prev_params, contrast), + 0, true, + }, /* OMAP3ISP_PREV_BRIGHTNESS */ { preview_config_brightness, - NULL}, + NULL, + offsetof(struct prev_params, brightness), + 0, true, + }, }; /* - * __preview_get_ptrs - helper function which return pointers to members - * of params and config structures. - * @params - pointer to preview_params structure. - * @param - return pointer to appropriate structure field. - * @configs - pointer to update config structure. - * @config - return pointer to appropriate structure field. - * @bit - for which feature to return pointers. - * Return size of corresponding prev_params member - */ -static u32 -__preview_get_ptrs(struct prev_params *params, void **param, - struct omap3isp_prev_update_config *configs, - void __user **config, u32 bit) -{ -#define CHKARG(cfgs, cfg, field) \ - if (cfgs && cfg) { \ - *(cfg) = (cfgs)->field; \ - } - - switch (bit) { - case PREV_HORZ_MEDIAN_FILTER: - *param = ¶ms->hmed; - CHKARG(configs, config, hmed) - return sizeof(params->hmed); - case PREV_NOISE_FILTER: - *param = ¶ms->nf; - CHKARG(configs, config, nf) - return sizeof(params->nf); - break; - case PREV_CFA: - *param = ¶ms->cfa; - CHKARG(configs, config, cfa) - return sizeof(params->cfa); - case PREV_LUMA_ENHANCE: - *param = ¶ms->luma; - CHKARG(configs, config, luma) - return sizeof(params->luma); - case PREV_CHROMA_SUPPRESS: - *param = ¶ms->csup; - CHKARG(configs, config, csup) - return sizeof(params->csup); - case PREV_DEFECT_COR: - *param = ¶ms->dcor; - CHKARG(configs, config, dcor) - return sizeof(params->dcor); - case PREV_BLKADJ: - *param = ¶ms->blk_adj; - CHKARG(configs, config, blkadj) - return sizeof(params->blk_adj); - case PREV_YCLIMITS: - *param = ¶ms->yclimit; - CHKARG(configs, config, yclimit) - return sizeof(params->yclimit); - case PREV_RGB2RGB: - *param = ¶ms->rgb2rgb; - CHKARG(configs, config, rgb2rgb) - return sizeof(params->rgb2rgb); - case PREV_COLOR_CONV: - *param = ¶ms->rgb2ycbcr; - CHKARG(configs, config, csc) - return sizeof(params->rgb2ycbcr); - case PREV_WB: - *param = ¶ms->wbal; - CHKARG(configs, config, wbal) - return sizeof(params->wbal); - case PREV_GAMMA: - *param = ¶ms->gamma; - CHKARG(configs, config, gamma) - return sizeof(params->gamma); - case PREV_CONTRAST: - *param = ¶ms->contrast; - return 0; - case PREV_BRIGHTNESS: - *param = ¶ms->brightness; - return 0; - default: - *param = NULL; - *config = NULL; - break; - } - return 0; -} - -/* * preview_config - Copy and update local structure with userspace preview * configuration. * @prev: ISP preview engine @@ -885,84 +909,103 @@ __preview_get_ptrs(struct prev_params *params, void **param, static int preview_config(struct isp_prev_device *prev, struct omap3isp_prev_update_config *cfg) { - struct prev_params *params; - struct preview_update *attr; - int i, bit, rval = 0; + unsigned long flags; + unsigned int i; + int rval = 0; + u32 update; + u32 active; - params = &prev->params; + if (cfg->update == 0) + return 0; - if (prev->state != ISP_PIPELINE_STREAM_STOPPED) { - unsigned long flags; + /* Mark the shadow parameters we're going to update as busy. */ + spin_lock_irqsave(&prev->params.lock, flags); + preview_params_lock(prev, cfg->update, true); + active = prev->params.active; + spin_unlock_irqrestore(&prev->params.lock, flags); - spin_lock_irqsave(&prev->lock, flags); - prev->shadow_update = 1; - spin_unlock_irqrestore(&prev->lock, flags); - } + update = 0; for (i = 0; i < ARRAY_SIZE(update_attrs); i++) { - attr = &update_attrs[i]; - bit = 0; + const struct preview_update *attr = &update_attrs[i]; + struct prev_params *params; + unsigned int bit = 1 << i; - if (!(cfg->update & attr->cfg_bit)) + if (attr->skip || !(cfg->update & bit)) continue; - bit = cfg->flag & attr->cfg_bit; - if (bit) { - void *to = NULL, __user *from = NULL; - unsigned long sz = 0; + params = &prev->params.params[!!(active & bit)]; + + if (cfg->flag & bit) { + void __user *from = *(void * __user *) + ((void *)cfg + attr->config_offset); + void *to = (void *)params + attr->param_offset; + size_t size = attr->param_size; - sz = __preview_get_ptrs(params, &to, cfg, &from, - bit); - if (to && from && sz) { - if (copy_from_user(to, from, sz)) { + if (to && from && size) { + if (copy_from_user(to, from, size)) { rval = -EFAULT; break; } } - params->features |= attr->feature_bit; + params->features |= bit; } else { - params->features &= ~attr->feature_bit; + params->features &= ~bit; } - prev->update |= attr->feature_bit; + update |= bit; } - prev->shadow_update = 0; + spin_lock_irqsave(&prev->params.lock, flags); + preview_params_unlock(prev, update, true); + preview_params_switch(prev); + spin_unlock_irqrestore(&prev->params.lock, flags); + return rval; } /* * preview_setup_hw - Setup preview registers and/or internal memory * @prev: pointer to preview private structure + * @update: Bitmask of parameters to setup + * @active: Bitmask of parameters active in set 0 * Note: can be called from interrupt context * Return none */ -static void preview_setup_hw(struct isp_prev_device *prev) +static void preview_setup_hw(struct isp_prev_device *prev, u32 update, + u32 active) { - struct prev_params *params = &prev->params; - struct preview_update *attr; - int i, bit; - void *param_ptr; + unsigned int i; + u32 features; + + if (update == 0) + return; + + features = (prev->params.params[0].features & active) + | (prev->params.params[1].features & ~active); for (i = 0; i < ARRAY_SIZE(update_attrs); i++) { - attr = &update_attrs[i]; + const struct preview_update *attr = &update_attrs[i]; + struct prev_params *params; + unsigned int bit = 1 << i; + void *param_ptr; - if (!(prev->update & attr->feature_bit)) + if (!(update & bit)) continue; - bit = params->features & attr->feature_bit; - if (bit) { + + params = &prev->params.params[!(active & bit)]; + + if (params->features & bit) { if (attr->config) { - __preview_get_ptrs(params, ¶m_ptr, NULL, - NULL, bit); + param_ptr = (void *)params + attr->param_offset; attr->config(prev, param_ptr); } if (attr->enable) attr->enable(prev, 1); - } else + } else { if (attr->enable) attr->enable(prev, 0); - - prev->update &= ~attr->feature_bit; + } } } @@ -1000,13 +1043,17 @@ preview_config_ycpos(struct isp_prev_device *prev, static void preview_config_averager(struct isp_prev_device *prev, u8 average) { struct isp_device *isp = to_isp_device(prev); + struct prev_params *params; int reg = 0; - if (prev->params.cfa.format == OMAP3ISP_CFAFMT_BAYER) + params = (prev->params.active & OMAP3ISP_PREV_CFA) + ? &prev->params.params[0] : &prev->params.params[1]; + + if (params->cfa.format == OMAP3ISP_CFAFMT_BAYER) reg = ISPPRV_AVE_EVENDIST_2 << ISPPRV_AVE_EVENDIST_SHIFT | ISPPRV_AVE_ODDDIST_2 << ISPPRV_AVE_ODDDIST_SHIFT | average; - else if (prev->params.cfa.format == OMAP3ISP_CFAFMT_RGBFOVEON) + else if (params->cfa.format == OMAP3ISP_CFAFMT_RGBFOVEON) reg = ISPPRV_AVE_EVENDIST_3 << ISPPRV_AVE_EVENDIST_SHIFT | ISPPRV_AVE_ODDDIST_3 << ISPPRV_AVE_ODDDIST_SHIFT | average; @@ -1014,6 +1061,27 @@ static void preview_config_averager(struct isp_prev_device *prev, u8 average) } /* + * preview_config_input_format - Configure the input format + * @prev: The preview engine + * @format: Format on the preview engine sink pad + * + * Enable CFA interpolation for Bayer formats and disable it for greyscale + * formats. + */ +static void preview_config_input_format(struct isp_prev_device *prev, + const struct v4l2_mbus_framefmt *format) +{ + struct isp_device *isp = to_isp_device(prev); + + if (format->code != V4L2_MBUS_FMT_Y10_1X10) + isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ISPPRV_PCR_CFAEN); + else + isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ISPPRV_PCR_CFAEN); +} + +/* * preview_config_input_size - Configure the input frame size * * The preview engine crops several rows and columns internally depending on @@ -1024,32 +1092,37 @@ static void preview_config_averager(struct isp_prev_device *prev, u8 average) * * See the explanation at the PREV_MARGIN_* definitions for more details. */ -static void preview_config_input_size(struct isp_prev_device *prev) +static void preview_config_input_size(struct isp_prev_device *prev, u32 active) { + const struct v4l2_mbus_framefmt *format = &prev->formats[PREV_PAD_SINK]; struct isp_device *isp = to_isp_device(prev); - struct prev_params *params = &prev->params; unsigned int sph = prev->crop.left; unsigned int eph = prev->crop.left + prev->crop.width - 1; unsigned int slv = prev->crop.top; unsigned int elv = prev->crop.top + prev->crop.height - 1; + u32 features; - if (params->features & PREV_CFA) { + if (format->code == V4L2_MBUS_FMT_Y10_1X10) { sph -= 2; eph += 2; slv -= 2; elv += 2; } - if (params->features & (PREV_DEFECT_COR | PREV_NOISE_FILTER)) { + + features = (prev->params.params[0].features & active) + | (prev->params.params[1].features & ~active); + + if (features & (OMAP3ISP_PREV_DEFECT_COR | OMAP3ISP_PREV_NF)) { sph -= 2; eph += 2; slv -= 2; elv += 2; } - if (params->features & PREV_HORZ_MEDIAN_FILTER) { + if (features & OMAP3ISP_PREV_HRZ_MED) { sph -= 2; eph += 2; } - if (params->features & (PREV_CHROMA_SUPPRESS | PREV_LUMA_ENHANCE)) + if (features & (OMAP3ISP_PREV_CHROMA_SUPP | OMAP3ISP_PREV_LUMAENH)) sph -= 2; isp_reg_writel(isp, (sph << ISPPRV_HORZ_INFO_SPH_SHIFT) | eph, @@ -1184,8 +1257,16 @@ int omap3isp_preview_busy(struct isp_prev_device *prev) */ void omap3isp_preview_restore_context(struct isp_device *isp) { - isp->isp_prev.update = PREV_FEATURES_END - 1; - preview_setup_hw(&isp->isp_prev); + struct isp_prev_device *prev = &isp->isp_prev; + const u32 update = OMAP3ISP_PREV_FEATURES_END - 1; + + prev->params.params[0].update = prev->params.active & update; + prev->params.params[1].update = ~prev->params.active & update; + + preview_setup_hw(prev, update, prev->params.active); + + prev->params.params[0].update = 0; + prev->params.params[1].update = 0; } /* @@ -1244,12 +1325,21 @@ static void preview_print_status(struct isp_prev_device *prev) /* * preview_init_params - init image processing parameters. * @prev: pointer to previewer private structure - * return none */ static void preview_init_params(struct isp_prev_device *prev) { - struct prev_params *params = &prev->params; - int i = 0; + struct prev_params *params; + unsigned int i; + + spin_lock_init(&prev->params.lock); + + prev->params.active = ~0; + prev->params.params[0].busy = 0; + prev->params.params[0].update = OMAP3ISP_PREV_FEATURES_END - 1; + prev->params.params[1].busy = 0; + prev->params.params[1].update = 0; + + params = &prev->params.params[0]; /* Init values */ params->contrast = ISPPRV_CONTRAST_DEF * ISPPRV_CONTRAST_UNITS; @@ -1277,22 +1367,22 @@ static void preview_init_params(struct isp_prev_device *prev) params->wbal.coef1 = FLR_WBAL_COEF; params->wbal.coef2 = FLR_WBAL_COEF; params->wbal.coef3 = FLR_WBAL_COEF; - params->blk_adj.red = FLR_BLKADJ_RED; - params->blk_adj.green = FLR_BLKADJ_GREEN; - params->blk_adj.blue = FLR_BLKADJ_BLUE; + params->blkadj.red = FLR_BLKADJ_RED; + params->blkadj.green = FLR_BLKADJ_GREEN; + params->blkadj.blue = FLR_BLKADJ_BLUE; params->rgb2rgb = flr_rgb2rgb; - params->rgb2ycbcr = flr_prev_csc; + params->csc = flr_prev_csc; params->yclimit.minC = ISPPRV_YC_MIN; params->yclimit.maxC = ISPPRV_YC_MAX; params->yclimit.minY = ISPPRV_YC_MIN; params->yclimit.maxY = ISPPRV_YC_MAX; - params->features = PREV_CFA | PREV_DEFECT_COR | PREV_NOISE_FILTER - | PREV_GAMMA | PREV_BLKADJ | PREV_YCLIMITS - | PREV_RGB2RGB | PREV_COLOR_CONV | PREV_WB - | PREV_BRIGHTNESS | PREV_CONTRAST; - - prev->update = PREV_FEATURES_END - 1; + params->features = OMAP3ISP_PREV_CFA | OMAP3ISP_PREV_DEFECT_COR + | OMAP3ISP_PREV_NF | OMAP3ISP_PREV_GAMMA + | OMAP3ISP_PREV_BLKADJ | OMAP3ISP_PREV_YC_LIMIT + | OMAP3ISP_PREV_RGB2RGB | OMAP3ISP_PREV_COLOR_CONV + | OMAP3ISP_PREV_WB | OMAP3ISP_PREV_BRIGHTNESS + | OMAP3ISP_PREV_CONTRAST; } /* @@ -1321,8 +1411,17 @@ static void preview_configure(struct isp_prev_device *prev) { struct isp_device *isp = to_isp_device(prev); struct v4l2_mbus_framefmt *format; + unsigned long flags; + u32 update; + u32 active; - preview_setup_hw(prev); + spin_lock_irqsave(&prev->params.lock, flags); + /* Mark all active parameters we are going to touch as busy. */ + update = preview_params_lock(prev, 0, false); + active = prev->params.active; + spin_unlock_irqrestore(&prev->params.lock, flags); + + preview_setup_hw(prev, update, active); if (prev->output & PREVIEW_OUTPUT_MEMORY) isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, @@ -1343,7 +1442,8 @@ static void preview_configure(struct isp_prev_device *prev) preview_adjust_bandwidth(prev); - preview_config_input_size(prev); + preview_config_input_format(prev, format); + preview_config_input_size(prev, active); if (prev->input == PREVIEW_INPUT_CCDC) preview_config_inlineoffset(prev, 0); @@ -1360,6 +1460,10 @@ static void preview_configure(struct isp_prev_device *prev) preview_config_averager(prev, 0); preview_config_ycpos(prev, format->code); + + spin_lock_irqsave(&prev->params.lock, flags); + preview_params_unlock(prev, update, false); + spin_unlock_irqrestore(&prev->params.lock, flags); } /* ----------------------------------------------------------------------------- @@ -1448,25 +1552,30 @@ static void preview_isr_buffer(struct isp_prev_device *prev) void omap3isp_preview_isr(struct isp_prev_device *prev) { unsigned long flags; + u32 update; + u32 active; if (omap3isp_module_sync_is_stopping(&prev->wait, &prev->stopping)) return; - spin_lock_irqsave(&prev->lock, flags); - if (prev->shadow_update) - goto done; + spin_lock_irqsave(&prev->params.lock, flags); + preview_params_switch(prev); + update = preview_params_lock(prev, 0, false); + active = prev->params.active; + spin_unlock_irqrestore(&prev->params.lock, flags); - preview_setup_hw(prev); - preview_config_input_size(prev); - -done: - spin_unlock_irqrestore(&prev->lock, flags); + preview_setup_hw(prev, update, active); + preview_config_input_size(prev, active); if (prev->input == PREVIEW_INPUT_MEMORY || prev->output & PREVIEW_OUTPUT_MEMORY) preview_isr_buffer(prev); else if (prev->state == ISP_PIPELINE_STREAM_CONTINUOUS) preview_enable_oneshot(prev); + + spin_lock_irqsave(&prev->params.lock, flags); + preview_params_unlock(prev, update, false); + spin_unlock_irqrestore(&prev->params.lock, flags); } /* ----------------------------------------------------------------------------- @@ -1552,7 +1661,6 @@ static int preview_set_stream(struct v4l2_subdev *sd, int enable) struct isp_video *video_out = &prev->video_out; struct isp_device *isp = to_isp_device(prev); struct device *dev = to_device(prev); - unsigned long flags; if (prev->state == ISP_PIPELINE_STREAM_STOPPED) { if (enable == ISP_PIPELINE_STREAM_STOPPED) @@ -1589,11 +1697,9 @@ static int preview_set_stream(struct v4l2_subdev *sd, int enable) if (omap3isp_module_sync_idle(&sd->entity, &prev->wait, &prev->stopping)) dev_dbg(dev, "%s: stop timeout.\n", sd->name); - spin_lock_irqsave(&prev->lock, flags); omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_PREVIEW_READ); omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_PREVIEW_WRITE); omap3isp_subclk_disable(isp, OMAP3_ISP_SUBCLK_PREVIEW); - spin_unlock_irqrestore(&prev->lock, flags); isp_video_dmaqueue_flags_clr(video_out); break; } @@ -1624,6 +1730,7 @@ __preview_get_crop(struct isp_prev_device *prev, struct v4l2_subdev_fh *fh, /* previewer format descriptions */ static const unsigned int preview_input_fmts[] = { + V4L2_MBUS_FMT_Y10_1X10, V4L2_MBUS_FMT_SGRBG10_1X10, V4L2_MBUS_FMT_SRGGB10_1X10, V4L2_MBUS_FMT_SBGGR10_1X10, @@ -1822,55 +1929,89 @@ static int preview_enum_frame_size(struct v4l2_subdev *sd, } /* - * preview_get_crop - Retrieve the crop rectangle on a pad + * preview_get_selection - Retrieve a selection rectangle on a pad * @sd: ISP preview V4L2 subdevice * @fh: V4L2 subdev file handle - * @crop: crop rectangle + * @sel: Selection rectangle + * + * The only supported rectangles are the crop rectangles on the sink pad. * * Return 0 on success or a negative error code otherwise. */ -static int preview_get_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) +static int preview_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) { struct isp_prev_device *prev = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + if (sel->pad != PREV_PAD_SINK) + return -EINVAL; + + switch (sel->target) { + case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS: + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = INT_MAX; + sel->r.height = INT_MAX; + + format = __preview_get_format(prev, fh, PREV_PAD_SINK, + sel->which); + preview_try_crop(prev, format, &sel->r); + break; + + case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL: + sel->r = *__preview_get_crop(prev, fh, sel->which); + break; - /* Cropping is only supported on the sink pad. */ - if (crop->pad != PREV_PAD_SINK) + default: return -EINVAL; + } - crop->rect = *__preview_get_crop(prev, fh, crop->which); return 0; } /* - * preview_set_crop - Retrieve the crop rectangle on a pad + * preview_set_selection - Set a selection rectangle on a pad * @sd: ISP preview V4L2 subdevice * @fh: V4L2 subdev file handle - * @crop: crop rectangle + * @sel: Selection rectangle + * + * The only supported rectangle is the actual crop rectangle on the sink pad. * * Return 0 on success or a negative error code otherwise. */ -static int preview_set_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) +static int preview_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) { struct isp_prev_device *prev = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - /* Cropping is only supported on the sink pad. */ - if (crop->pad != PREV_PAD_SINK) + if (sel->target != V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL || + sel->pad != PREV_PAD_SINK) return -EINVAL; /* The crop rectangle can't be changed while streaming. */ if (prev->state != ISP_PIPELINE_STREAM_STOPPED) return -EBUSY; - format = __preview_get_format(prev, fh, PREV_PAD_SINK, crop->which); - preview_try_crop(prev, format, &crop->rect); - *__preview_get_crop(prev, fh, crop->which) = crop->rect; + /* Modifying the crop rectangle always changes the format on the source + * pad. If the KEEP_CONFIG flag is set, just return the current crop + * rectangle. + */ + if (sel->flags & V4L2_SUBDEV_SEL_FLAG_KEEP_CONFIG) { + sel->r = *__preview_get_crop(prev, fh, sel->which); + return 0; + } + + format = __preview_get_format(prev, fh, PREV_PAD_SINK, sel->which); + preview_try_crop(prev, format, &sel->r); + *__preview_get_crop(prev, fh, sel->which) = sel->r; /* Update the source format. */ - format = __preview_get_format(prev, fh, PREV_PAD_SOURCE, crop->which); - preview_try_format(prev, fh, PREV_PAD_SOURCE, format, crop->which); + format = __preview_get_format(prev, fh, PREV_PAD_SOURCE, sel->which); + preview_try_format(prev, fh, PREV_PAD_SOURCE, format, sel->which); return 0; } @@ -1979,8 +2120,8 @@ static const struct v4l2_subdev_pad_ops preview_v4l2_pad_ops = { .enum_frame_size = preview_enum_frame_size, .get_fmt = preview_get_format, .set_fmt = preview_set_format, - .get_crop = preview_get_crop, - .set_crop = preview_set_crop, + .get_selection = preview_get_selection, + .set_selection = preview_set_selection, }; /* subdev operations */ @@ -2076,6 +2217,7 @@ static int preview_link_setup(struct media_entity *entity, /* media operations */ static const struct media_entity_operations preview_media_ops = { .link_setup = preview_link_setup, + .link_validate = v4l2_subdev_link_validate, }; void omap3isp_preview_unregister_entities(struct isp_prev_device *prev) @@ -2201,7 +2343,7 @@ error_video_in: } /* - * isp_preview_init - Previewer initialization. + * omap3isp_preview_init - Previewer initialization. * @dev : Pointer to ISP device * return -ENOMEM or zero on success */ @@ -2209,8 +2351,8 @@ int omap3isp_preview_init(struct isp_device *isp) { struct isp_prev_device *prev = &isp->isp_prev; - spin_lock_init(&prev->lock); init_waitqueue_head(&prev->wait); + preview_init_params(prev); return preview_init_entities(prev); diff --git a/drivers/media/video/omap3isp/isppreview.h b/drivers/media/video/omap3isp/isppreview.h index 09686607973c..6663ab64e4b1 100644 --- a/drivers/media/video/omap3isp/isppreview.h +++ b/drivers/media/video/omap3isp/isppreview.h @@ -45,29 +45,10 @@ #define ISPPRV_CONTRAST_HIGH 0xFF #define ISPPRV_CONTRAST_UNITS 0x1 -/* Features list */ -#define PREV_LUMA_ENHANCE OMAP3ISP_PREV_LUMAENH -#define PREV_INVERSE_ALAW OMAP3ISP_PREV_INVALAW -#define PREV_HORZ_MEDIAN_FILTER OMAP3ISP_PREV_HRZ_MED -#define PREV_CFA OMAP3ISP_PREV_CFA -#define PREV_CHROMA_SUPPRESS OMAP3ISP_PREV_CHROMA_SUPP -#define PREV_WB OMAP3ISP_PREV_WB -#define PREV_BLKADJ OMAP3ISP_PREV_BLKADJ -#define PREV_RGB2RGB OMAP3ISP_PREV_RGB2RGB -#define PREV_COLOR_CONV OMAP3ISP_PREV_COLOR_CONV -#define PREV_YCLIMITS OMAP3ISP_PREV_YC_LIMIT -#define PREV_DEFECT_COR OMAP3ISP_PREV_DEFECT_COR -#define PREV_GAMMA_BYPASS OMAP3ISP_PREV_GAMMABYPASS -#define PREV_DARK_FRAME_CAPTURE OMAP3ISP_PREV_DRK_FRM_CAPTURE -#define PREV_DARK_FRAME_SUBTRACT OMAP3ISP_PREV_DRK_FRM_SUBTRACT -#define PREV_LENS_SHADING OMAP3ISP_PREV_LENS_SHADING -#define PREV_NOISE_FILTER OMAP3ISP_PREV_NF -#define PREV_GAMMA OMAP3ISP_PREV_GAMMA - -#define PREV_CONTRAST (1 << 17) -#define PREV_BRIGHTNESS (1 << 18) -#define PREV_AVERAGER (1 << 19) -#define PREV_FEATURES_END (1 << 20) +/* Additional features not listed in linux/omap3isp.h */ +#define OMAP3ISP_PREV_CONTRAST (1 << 17) +#define OMAP3ISP_PREV_BRIGHTNESS (1 << 18) +#define OMAP3ISP_PREV_FEATURES_END (1 << 19) enum preview_input_entity { PREVIEW_INPUT_NONE, @@ -88,6 +69,8 @@ enum preview_ycpos_mode { /* * struct prev_params - Structure for all configuration + * @busy: Bitmask of busy parameters (being updated or used) + * @update: Bitmask of the parameters to be updated * @features: Set of features enabled. * @cfa: CFA coefficients. * @csup: Chroma suppression coefficients. @@ -96,15 +79,17 @@ enum preview_ycpos_mode { * @dcor: Noise filter coefficients. * @gamma: Gamma coefficients. * @wbal: White Balance parameters. - * @blk_adj: Black adjustment parameters. + * @blkadj: Black adjustment parameters. * @rgb2rgb: RGB blending parameters. - * @rgb2ycbcr: RGB to ycbcr parameters. + * @csc: Color space conversion (RGB to YCbCr) parameters. * @hmed: Horizontal median filter. * @yclimit: YC limits parameters. * @contrast: Contrast. * @brightness: Brightness. */ struct prev_params { + u32 busy; + u32 update; u32 features; struct omap3isp_prev_cfa cfa; struct omap3isp_prev_csup csup; @@ -113,35 +98,15 @@ struct prev_params { struct omap3isp_prev_dcor dcor; struct omap3isp_prev_gtables gamma; struct omap3isp_prev_wbal wbal; - struct omap3isp_prev_blkadj blk_adj; + struct omap3isp_prev_blkadj blkadj; struct omap3isp_prev_rgbtorgb rgb2rgb; - struct omap3isp_prev_csc rgb2ycbcr; + struct omap3isp_prev_csc csc; struct omap3isp_prev_hmed hmed; struct omap3isp_prev_yclimit yclimit; u8 contrast; u8 brightness; }; -/* - * struct isptables_update - Structure for Table Configuration. - * @update: Specifies which tables should be updated. - * @flag: Specifies which tables should be enabled. - * @nf: Pointer to structure for Noise Filter - * @lsc: Pointer to LSC gain table. (currently not used) - * @gamma: Pointer to gamma correction tables. - * @cfa: Pointer to color filter array configuration. - * @wbal: Pointer to colour and digital gain configuration. - */ -struct isptables_update { - u32 update; - u32 flag; - struct omap3isp_prev_nf *nf; - u32 *lsc; - struct omap3isp_prev_gtables *gamma; - struct omap3isp_prev_cfa *cfa; - struct omap3isp_prev_wbal *wbal; -}; - /* Sink and source previewer pads */ #define PREV_PAD_SINK 0 #define PREV_PAD_SOURCE 1 @@ -157,12 +122,11 @@ struct isptables_update { * @output: Bitmask of the active output * @video_in: Input video entity * @video_out: Output video entity - * @params: Module configuration data - * @shadow_update: If set, update the hardware configured in the next interrupt + * @params.params : Active and shadow parameters sets + * @params.active: Bitmask of parameters active in set 0 + * @params.lock: Parameters lock, protects params.active and params.shadow * @underrun: Whether the preview entity has queued buffers on the output * @state: Current preview pipeline state - * @lock: Shadow update lock - * @update: Bitmask of the parameters to be updated * * This structure is used to store the OMAP ISP Preview module Information. */ @@ -179,13 +143,15 @@ struct isp_prev_device { struct isp_video video_in; struct isp_video video_out; - struct prev_params params; - unsigned int shadow_update:1; + struct { + struct prev_params params[2]; + u32 active; + spinlock_t lock; + } params; + enum isp_pipeline_stream_state state; wait_queue_head_t wait; atomic_t stopping; - spinlock_t lock; - u32 update; }; struct isp_device; diff --git a/drivers/media/video/omap3isp/ispqueue.h b/drivers/media/video/omap3isp/ispqueue.h index 92c5a12157d5..908dfd712e8e 100644 --- a/drivers/media/video/omap3isp/ispqueue.h +++ b/drivers/media/video/omap3isp/ispqueue.h @@ -90,7 +90,7 @@ struct isp_video_buffer { void *vaddr; /* For userspace buffers. */ - unsigned long vm_flags; + vm_flags_t vm_flags; unsigned long offset; unsigned int npages; struct page **pages; diff --git a/drivers/media/video/omap3isp/ispresizer.c b/drivers/media/video/omap3isp/ispresizer.c index 6958a9e3dc22..14041c9c8643 100644 --- a/drivers/media/video/omap3isp/ispresizer.c +++ b/drivers/media/video/omap3isp/ispresizer.c @@ -1188,32 +1188,6 @@ static int resizer_set_stream(struct v4l2_subdev *sd, int enable) } /* - * resizer_g_crop - handle get crop subdev operation - * @sd : pointer to v4l2 subdev structure - * @pad : subdev pad - * @crop : pointer to crop structure - * @which : active or try format - * return zero - */ -static int resizer_g_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) -{ - struct isp_res_device *res = v4l2_get_subdevdata(sd); - struct v4l2_mbus_framefmt *format; - struct resizer_ratio ratio; - - /* Only sink pad has crop capability */ - if (crop->pad != RESZ_PAD_SINK) - return -EINVAL; - - format = __resizer_get_format(res, fh, RESZ_PAD_SOURCE, crop->which); - crop->rect = *__resizer_get_crop(res, fh, crop->which); - resizer_calc_ratios(res, &crop->rect, format, &ratio); - - return 0; -} - -/* * resizer_try_crop - mangles crop parameters. */ static void resizer_try_crop(const struct v4l2_mbus_framefmt *sink, @@ -1223,7 +1197,7 @@ static void resizer_try_crop(const struct v4l2_mbus_framefmt *sink, const unsigned int spv = DEFAULT_PHASE; const unsigned int sph = DEFAULT_PHASE; - /* Crop rectangle is constrained to the output size so that zoom ratio + /* Crop rectangle is constrained by the output size so that zoom ratio * cannot exceed +/-4.0. */ unsigned int min_width = @@ -1248,51 +1222,115 @@ static void resizer_try_crop(const struct v4l2_mbus_framefmt *sink, } /* - * resizer_s_crop - handle set crop subdev operation - * @sd : pointer to v4l2 subdev structure - * @pad : subdev pad - * @crop : pointer to crop structure - * @which : active or try format - * return -EINVAL or zero when succeed + * resizer_get_selection - Retrieve a selection rectangle on a pad + * @sd: ISP resizer V4L2 subdevice + * @fh: V4L2 subdev file handle + * @sel: Selection rectangle + * + * The only supported rectangles are the crop rectangles on the sink pad. + * + * Return 0 on success or a negative error code otherwise. */ -static int resizer_s_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) +static int resizer_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) +{ + struct isp_res_device *res = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format_source; + struct v4l2_mbus_framefmt *format_sink; + struct resizer_ratio ratio; + + if (sel->pad != RESZ_PAD_SINK) + return -EINVAL; + + format_sink = __resizer_get_format(res, fh, RESZ_PAD_SINK, + sel->which); + format_source = __resizer_get_format(res, fh, RESZ_PAD_SOURCE, + sel->which); + + switch (sel->target) { + case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS: + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = INT_MAX; + sel->r.height = INT_MAX; + + resizer_try_crop(format_sink, format_source, &sel->r); + resizer_calc_ratios(res, &sel->r, format_source, &ratio); + break; + + case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL: + sel->r = *__resizer_get_crop(res, fh, sel->which); + resizer_calc_ratios(res, &sel->r, format_source, &ratio); + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* + * resizer_set_selection - Set a selection rectangle on a pad + * @sd: ISP resizer V4L2 subdevice + * @fh: V4L2 subdev file handle + * @sel: Selection rectangle + * + * The only supported rectangle is the actual crop rectangle on the sink pad. + * + * FIXME: This function currently behaves as if the KEEP_CONFIG selection flag + * was always set. + * + * Return 0 on success or a negative error code otherwise. + */ +static int resizer_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) { struct isp_res_device *res = v4l2_get_subdevdata(sd); struct isp_device *isp = to_isp_device(res); struct v4l2_mbus_framefmt *format_sink, *format_source; struct resizer_ratio ratio; - /* Only sink pad has crop capability */ - if (crop->pad != RESZ_PAD_SINK) + if (sel->target != V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL || + sel->pad != RESZ_PAD_SINK) return -EINVAL; format_sink = __resizer_get_format(res, fh, RESZ_PAD_SINK, - crop->which); + sel->which); format_source = __resizer_get_format(res, fh, RESZ_PAD_SOURCE, - crop->which); + sel->which); dev_dbg(isp->dev, "%s: L=%d,T=%d,W=%d,H=%d,which=%d\n", __func__, - crop->rect.left, crop->rect.top, crop->rect.width, - crop->rect.height, crop->which); + sel->r.left, sel->r.top, sel->r.width, sel->r.height, + sel->which); dev_dbg(isp->dev, "%s: input=%dx%d, output=%dx%d\n", __func__, format_sink->width, format_sink->height, format_source->width, format_source->height); - resizer_try_crop(format_sink, format_source, &crop->rect); - *__resizer_get_crop(res, fh, crop->which) = crop->rect; - resizer_calc_ratios(res, &crop->rect, format_source, &ratio); + /* Clamp the crop rectangle to the bounds, and then mangle it further to + * fulfill the TRM equations. Store the clamped but otherwise unmangled + * rectangle to avoid cropping the input multiple times: when an + * application sets the output format, the current crop rectangle is + * mangled during crop rectangle computation, which would lead to a new, + * smaller input crop rectangle every time the output size is set if we + * stored the mangled rectangle. + */ + resizer_try_crop(format_sink, format_source, &sel->r); + *__resizer_get_crop(res, fh, sel->which) = sel->r; + resizer_calc_ratios(res, &sel->r, format_source, &ratio); - if (crop->which == V4L2_SUBDEV_FORMAT_TRY) + if (sel->which == V4L2_SUBDEV_FORMAT_TRY) return 0; res->ratio = ratio; - res->crop.active = crop->rect; + res->crop.active = sel->r; /* - * s_crop can be called while streaming is on. In this case - * the crop values will be set in the next IRQ. + * set_selection can be called while streaming is on. In this case the + * crop values will be set in the next IRQ. */ if (res->state != ISP_PIPELINE_STREAM_STOPPED) res->applycrop = 1; @@ -1530,8 +1568,8 @@ static const struct v4l2_subdev_pad_ops resizer_v4l2_pad_ops = { .enum_frame_size = resizer_enum_frame_size, .get_fmt = resizer_get_format, .set_fmt = resizer_set_format, - .get_crop = resizer_g_crop, - .set_crop = resizer_s_crop, + .get_selection = resizer_get_selection, + .set_selection = resizer_set_selection, }; /* subdev operations */ @@ -1603,6 +1641,7 @@ static int resizer_link_setup(struct media_entity *entity, /* media operations */ static const struct media_entity_operations resizer_media_ops = { .link_setup = resizer_link_setup, + .link_validate = v4l2_subdev_link_validate, }; void omap3isp_resizer_unregister_entities(struct isp_res_device *res) diff --git a/drivers/media/video/omap3isp/ispstat.c b/drivers/media/video/omap3isp/ispstat.c index 11871ecc6d25..b8640be692f1 100644 --- a/drivers/media/video/omap3isp/ispstat.c +++ b/drivers/media/video/omap3isp/ispstat.c @@ -1032,7 +1032,7 @@ int omap3isp_stat_subscribe_event(struct v4l2_subdev *subdev, if (sub->type != stat->event_type) return -EINVAL; - return v4l2_event_subscribe(fh, sub, STAT_NEVENTS); + return v4l2_event_subscribe(fh, sub, STAT_NEVENTS, NULL); } int omap3isp_stat_unsubscribe_event(struct v4l2_subdev *subdev, diff --git a/drivers/media/video/omap3isp/ispvideo.c b/drivers/media/video/omap3isp/ispvideo.c index b02070057724..b37379d39cdd 100644 --- a/drivers/media/video/omap3isp/ispvideo.c +++ b/drivers/media/video/omap3isp/ispvideo.c @@ -46,6 +46,10 @@ * Helper functions */ +/* + * NOTE: When adding new media bus codes, always remember to add + * corresponding in-memory formats to the table below!!! + */ static struct isp_format_info formats[] = { { V4L2_MBUS_FMT_Y8_1X8, V4L2_MBUS_FMT_Y8_1X8, V4L2_MBUS_FMT_Y8_1X8, V4L2_MBUS_FMT_Y8_1X8, @@ -68,9 +72,18 @@ static struct isp_format_info formats[] = { { V4L2_MBUS_FMT_SRGGB8_1X8, V4L2_MBUS_FMT_SRGGB8_1X8, V4L2_MBUS_FMT_SRGGB8_1X8, V4L2_MBUS_FMT_SRGGB8_1X8, V4L2_PIX_FMT_SRGGB8, 8, }, + { V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8, V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8, + V4L2_MBUS_FMT_SBGGR10_1X10, 0, + V4L2_PIX_FMT_SBGGR10DPCM8, 8, }, + { V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8, V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8, + V4L2_MBUS_FMT_SGBRG10_1X10, 0, + V4L2_PIX_FMT_SGBRG10DPCM8, 8, }, { V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, V4L2_MBUS_FMT_SGRBG10_1X10, 0, V4L2_PIX_FMT_SGRBG10DPCM8, 8, }, + { V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8, V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8, + V4L2_MBUS_FMT_SRGGB10_1X10, 0, + V4L2_PIX_FMT_SRGGB10DPCM8, 8, }, { V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_PIX_FMT_SBGGR10, 10, }, @@ -117,37 +130,6 @@ omap3isp_video_format_info(enum v4l2_mbus_pixelcode code) } /* - * Decide whether desired output pixel code can be obtained with - * the lane shifter by shifting the input pixel code. - * @in: input pixelcode to shifter - * @out: output pixelcode from shifter - * @additional_shift: # of bits the sensor's LSB is offset from CAMEXT[0] - * - * return true if the combination is possible - * return false otherwise - */ -static bool isp_video_is_shiftable(enum v4l2_mbus_pixelcode in, - enum v4l2_mbus_pixelcode out, - unsigned int additional_shift) -{ - const struct isp_format_info *in_info, *out_info; - - if (in == out) - return true; - - in_info = omap3isp_video_format_info(in); - out_info = omap3isp_video_format_info(out); - - if ((in_info->flavor == 0) || (out_info->flavor == 0)) - return false; - - if (in_info->flavor != out_info->flavor) - return false; - - return in_info->bpp - out_info->bpp + additional_shift <= 6; -} - -/* * isp_video_mbus_to_pix - Convert v4l2_mbus_framefmt to v4l2_pix_format * @video: ISP video instance * @mbus: v4l2_mbus_framefmt format (input) @@ -242,8 +224,8 @@ isp_video_remote_subdev(struct isp_video *video, u32 *pad) } /* Return a pointer to the ISP video instance at the far end of the pipeline. */ -static struct isp_video * -isp_video_far_end(struct isp_video *video) +static int isp_video_get_graph_data(struct isp_video *video, + struct isp_pipeline *pipe) { struct media_entity_graph graph; struct media_entity *entity = &video->video.entity; @@ -254,21 +236,38 @@ isp_video_far_end(struct isp_video *video) media_entity_graph_walk_start(&graph, entity); while ((entity = media_entity_graph_walk_next(&graph))) { + struct isp_video *__video; + + pipe->entities |= 1 << entity->id; + + if (far_end != NULL) + continue; + if (entity == &video->video.entity) continue; if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE) continue; - far_end = to_isp_video(media_entity_to_video_device(entity)); - if (far_end->type != video->type) - break; - - far_end = NULL; + __video = to_isp_video(media_entity_to_video_device(entity)); + if (__video->type != video->type) + far_end = __video; } mutex_unlock(&mdev->graph_mutex); - return far_end; + + if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + pipe->input = far_end; + pipe->output = video; + } else { + if (far_end == NULL) + return -EPIPE; + + pipe->input = video; + pipe->output = far_end; + } + + return 0; } /* @@ -285,52 +284,24 @@ isp_video_far_end(struct isp_video *video) static int isp_video_validate_pipeline(struct isp_pipeline *pipe) { struct isp_device *isp = pipe->output->isp; - struct v4l2_subdev_format fmt_source; - struct v4l2_subdev_format fmt_sink; struct media_pad *pad; struct v4l2_subdev *subdev; - int ret; - - pipe->max_rate = pipe->l3_ick; subdev = isp_video_remote_subdev(pipe->output, NULL); if (subdev == NULL) return -EPIPE; while (1) { - unsigned int shifter_link; /* Retrieve the sink format */ pad = &subdev->entity.pads[0]; if (!(pad->flags & MEDIA_PAD_FL_SINK)) break; - fmt_sink.pad = pad->index; - fmt_sink.which = V4L2_SUBDEV_FORMAT_ACTIVE; - ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt_sink); - if (ret < 0 && ret != -ENOIOCTLCMD) - return -EPIPE; - /* Update the maximum frame rate */ if (subdev == &isp->isp_res.subdev) omap3isp_resizer_max_rate(&isp->isp_res, &pipe->max_rate); - /* Check ccdc maximum data rate when data comes from sensor - * TODO: Include ccdc rate in pipe->max_rate and compare the - * total pipe rate with the input data rate from sensor. - */ - if (subdev == &isp->isp_ccdc.subdev && pipe->input == NULL) { - unsigned int rate = UINT_MAX; - - omap3isp_ccdc_max_rate(&isp->isp_ccdc, &rate); - if (isp->isp_ccdc.vpcfg.pixelclk > rate) - return -ENOSPC; - } - - /* If sink pad is on CCDC, the link has the lane shifter - * in the middle of it. */ - shifter_link = subdev == &isp->isp_ccdc.subdev; - /* Retrieve the source format. Return an error if no source * entity can be found, and stop checking the pipeline if the * source entity isn't a subdev. @@ -343,32 +314,6 @@ static int isp_video_validate_pipeline(struct isp_pipeline *pipe) break; subdev = media_entity_to_v4l2_subdev(pad->entity); - - fmt_source.pad = pad->index; - fmt_source.which = V4L2_SUBDEV_FORMAT_ACTIVE; - ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt_source); - if (ret < 0 && ret != -ENOIOCTLCMD) - return -EPIPE; - - /* Check if the two ends match */ - if (fmt_source.format.width != fmt_sink.format.width || - fmt_source.format.height != fmt_sink.format.height) - return -EPIPE; - - if (shifter_link) { - unsigned int parallel_shift = 0; - if (isp->isp_ccdc.input == CCDC_INPUT_PARALLEL) { - struct isp_parallel_platform_data *pdata = - &((struct isp_v4l2_subdevs_group *) - subdev->host_priv)->bus.parallel; - parallel_shift = pdata->data_lane_shift * 2; - } - if (!isp_video_is_shiftable(fmt_source.format.code, - fmt_sink.format.code, - parallel_shift)) - return -EPIPE; - } else if (fmt_source.format.code != fmt_sink.format.code) - return -EPIPE; } return 0; @@ -923,6 +868,92 @@ isp_video_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b) file->f_flags & O_NONBLOCK); } +static int isp_video_check_external_subdevs(struct isp_video *video, + struct isp_pipeline *pipe) +{ + struct isp_device *isp = video->isp; + struct media_entity *ents[] = { + &isp->isp_csi2a.subdev.entity, + &isp->isp_csi2c.subdev.entity, + &isp->isp_ccp2.subdev.entity, + &isp->isp_ccdc.subdev.entity + }; + struct media_pad *source_pad; + struct media_entity *source = NULL; + struct media_entity *sink; + struct v4l2_subdev_format fmt; + struct v4l2_ext_controls ctrls; + struct v4l2_ext_control ctrl; + unsigned int i; + int ret = 0; + + for (i = 0; i < ARRAY_SIZE(ents); i++) { + /* Is the entity part of the pipeline? */ + if (!(pipe->entities & (1 << ents[i]->id))) + continue; + + /* ISP entities have always sink pad == 0. Find source. */ + source_pad = media_entity_remote_source(&ents[i]->pads[0]); + if (source_pad == NULL) + continue; + + source = source_pad->entity; + sink = ents[i]; + break; + } + + if (!source) { + dev_warn(isp->dev, "can't find source, failing now\n"); + return ret; + } + + if (media_entity_type(source) != MEDIA_ENT_T_V4L2_SUBDEV) + return 0; + + pipe->external = media_entity_to_v4l2_subdev(source); + + fmt.pad = source_pad->index; + fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + ret = v4l2_subdev_call(media_entity_to_v4l2_subdev(sink), + pad, get_fmt, NULL, &fmt); + if (unlikely(ret < 0)) { + dev_warn(isp->dev, "get_fmt returned null!\n"); + return ret; + } + + pipe->external_bpp = omap3isp_video_format_info(fmt.format.code)->bpp; + + memset(&ctrls, 0, sizeof(ctrls)); + memset(&ctrl, 0, sizeof(ctrl)); + + ctrl.id = V4L2_CID_PIXEL_RATE; + + ctrls.count = 1; + ctrls.controls = &ctrl; + + ret = v4l2_g_ext_ctrls(pipe->external->ctrl_handler, &ctrls); + if (ret < 0) { + dev_warn(isp->dev, "no pixel rate control in subdev %s\n", + pipe->external->name); + return ret; + } + + pipe->external_rate = ctrl.value64; + + if (pipe->entities & (1 << isp->isp_ccdc.subdev.entity.id)) { + unsigned int rate = UINT_MAX; + /* + * Check that maximum allowed CCDC pixel rate isn't + * exceeded by the pixel rate. + */ + omap3isp_ccdc_max_rate(&isp->isp_ccdc, &rate); + if (pipe->external_rate > rate) + return -ENOSPC; + } + + return 0; +} + /* * Stream management * @@ -961,7 +992,6 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) struct isp_video *video = video_drvdata(file); enum isp_pipeline_state state; struct isp_pipeline *pipe; - struct isp_video *far_end; unsigned long flags; int ret; @@ -980,46 +1010,45 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) */ pipe = video->video.entity.pipe ? to_isp_pipeline(&video->video.entity) : &video->pipe; - media_entity_pipeline_start(&video->video.entity, &pipe->pipe); + + pipe->entities = 0; + + if (video->isp->pdata->set_constraints) + video->isp->pdata->set_constraints(video->isp, true); + pipe->l3_ick = clk_get_rate(video->isp->clock[ISP_CLK_L3_ICK]); + pipe->max_rate = pipe->l3_ick; + + ret = media_entity_pipeline_start(&video->video.entity, &pipe->pipe); + if (ret < 0) + goto err_pipeline_start; /* Verify that the currently configured format matches the output of * the connected subdev. */ ret = isp_video_check_format(video, vfh); if (ret < 0) - goto error; + goto err_check_format; video->bpl_padding = ret; video->bpl_value = vfh->format.fmt.pix.bytesperline; - /* Find the ISP video node connected at the far end of the pipeline and - * update the pipeline. - */ - far_end = isp_video_far_end(video); + ret = isp_video_get_graph_data(video, pipe); + if (ret < 0) + goto err_check_format; - if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) state = ISP_PIPELINE_STREAM_OUTPUT | ISP_PIPELINE_IDLE_OUTPUT; - pipe->input = far_end; - pipe->output = video; - } else { - if (far_end == NULL) { - ret = -EPIPE; - goto error; - } - + else state = ISP_PIPELINE_STREAM_INPUT | ISP_PIPELINE_IDLE_INPUT; - pipe->input = video; - pipe->output = far_end; - } - if (video->isp->pdata->set_constraints) - video->isp->pdata->set_constraints(video->isp, true); - pipe->l3_ick = clk_get_rate(video->isp->clock[ISP_CLK_L3_ICK]); + ret = isp_video_check_external_subdevs(video, pipe); + if (ret < 0) + goto err_check_format; /* Validate the pipeline and update its state. */ ret = isp_video_validate_pipeline(pipe); if (ret < 0) - goto error; + goto err_check_format; pipe->error = false; @@ -1041,7 +1070,7 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) ret = omap3isp_video_queue_streamon(&vfh->queue); if (ret < 0) - goto error; + goto err_check_format; /* In sensor-to-memory mode, the stream can be started synchronously * to the stream on command. In memory-to-memory mode, it will be @@ -1051,32 +1080,34 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) ret = omap3isp_pipeline_set_stream(pipe, ISP_PIPELINE_STREAM_CONTINUOUS); if (ret < 0) - goto error; + goto err_set_stream; spin_lock_irqsave(&video->queue->irqlock, flags); if (list_empty(&video->dmaqueue)) video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_UNDERRUN; spin_unlock_irqrestore(&video->queue->irqlock, flags); } -error: - if (ret < 0) { - omap3isp_video_queue_streamoff(&vfh->queue); - if (video->isp->pdata->set_constraints) - video->isp->pdata->set_constraints(video->isp, false); - media_entity_pipeline_stop(&video->video.entity); - /* The DMA queue must be emptied here, otherwise CCDC interrupts - * that will get triggered the next time the CCDC is powered up - * will try to access buffers that might have been freed but - * still present in the DMA queue. This can easily get triggered - * if the above omap3isp_pipeline_set_stream() call fails on a - * system with a free-running sensor. - */ - INIT_LIST_HEAD(&video->dmaqueue); - video->queue = NULL; - } + video->streaming = 1; + + mutex_unlock(&video->stream_lock); + return 0; - if (!ret) - video->streaming = 1; +err_set_stream: + omap3isp_video_queue_streamoff(&vfh->queue); +err_check_format: + media_entity_pipeline_stop(&video->video.entity); +err_pipeline_start: + if (video->isp->pdata->set_constraints) + video->isp->pdata->set_constraints(video->isp, false); + /* The DMA queue must be emptied here, otherwise CCDC interrupts that + * will get triggered the next time the CCDC is powered up will try to + * access buffers that might have been freed but still present in the + * DMA queue. This can easily get triggered if the above + * omap3isp_pipeline_set_stream() call fails on a system with a + * free-running sensor. + */ + INIT_LIST_HEAD(&video->dmaqueue); + video->queue = NULL; mutex_unlock(&video->stream_lock); return ret; diff --git a/drivers/media/video/omap3isp/ispvideo.h b/drivers/media/video/omap3isp/ispvideo.h index d91bdb919be0..5acc909500ec 100644 --- a/drivers/media/video/omap3isp/ispvideo.h +++ b/drivers/media/video/omap3isp/ispvideo.h @@ -88,6 +88,7 @@ enum isp_pipeline_state { /* * struct isp_pipeline - An ISP hardware pipeline * @error: A hardware error occurred during capture + * @entities: Bitmask of entities in the pipeline (indexed by entity ID) */ struct isp_pipeline { struct media_pipeline pipe; @@ -96,12 +97,16 @@ struct isp_pipeline { enum isp_pipeline_stream_state stream_state; struct isp_video *input; struct isp_video *output; + u32 entities; unsigned long l3_ick; unsigned int max_rate; atomic_t frame_number; bool do_propagation; /* of frame number */ bool error; struct v4l2_fract max_timeperframe; + struct v4l2_subdev *external; + unsigned int external_rate; + unsigned int external_bpp; }; #define to_isp_pipeline(__e) \ diff --git a/drivers/media/video/ov5642.c b/drivers/media/video/ov5642.c index 80e07794ac8e..0bc93313d37a 100644 --- a/drivers/media/video/ov5642.c +++ b/drivers/media/video/ov5642.c @@ -1025,8 +1025,6 @@ static int ov5642_probe(struct i2c_client *client, priv->crop_rect.height = OV5642_DEFAULT_HEIGHT; priv->crop_rect.left = (OV5642_MAX_WIDTH - OV5642_DEFAULT_WIDTH) / 2; priv->crop_rect.top = (OV5642_MAX_HEIGHT - OV5642_DEFAULT_HEIGHT) / 2; - priv->crop_rect.width = OV5642_DEFAULT_WIDTH; - priv->crop_rect.height = OV5642_DEFAULT_HEIGHT; priv->total_width = OV5642_DEFAULT_WIDTH + BLANKING_EXTRA_WIDTH; priv->total_height = BLANKING_MIN_HEIGHT; diff --git a/drivers/media/video/pms.c b/drivers/media/video/pms.c index e753b5e4d2ce..af2d9086d7e8 100644 --- a/drivers/media/video/pms.c +++ b/drivers/media/video/pms.c @@ -30,15 +30,19 @@ #include <linux/init.h> #include <linux/mutex.h> #include <linux/uaccess.h> +#include <linux/isa.h> #include <asm/io.h> #include <linux/videodev2.h> #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-fh.h> +#include <media/v4l2-event.h> #include <media/v4l2-device.h> MODULE_LICENSE("GPL"); -MODULE_VERSION("0.0.4"); +MODULE_VERSION("0.0.5"); #define MOTOROLA 1 #define PHILIPS2 2 /* SAA7191 */ @@ -55,11 +59,11 @@ struct i2c_info { struct pms { struct v4l2_device v4l2_dev; struct video_device vdev; + struct v4l2_ctrl_handler hdl; int height; int width; int depth; int input; - s32 brightness, saturation, hue, contrast; struct mutex lock; int i2c_count; struct i2c_info i2cinfo[64]; @@ -72,8 +76,6 @@ struct pms { void __iomem *mem; }; -static struct pms pms_card; - /* * I/O ports and Shared Memory */ @@ -676,8 +678,10 @@ static int pms_querycap(struct file *file, void *priv, strlcpy(vcap->driver, dev->v4l2_dev.name, sizeof(vcap->driver)); strlcpy(vcap->card, "Mediavision PMS", sizeof(vcap->card)); - strlcpy(vcap->bus_info, "ISA", sizeof(vcap->bus_info)); - vcap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; + snprintf(vcap->bus_info, sizeof(vcap->bus_info), + "ISA:%s", dev->v4l2_dev.name); + vcap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; + vcap->capabilities = vcap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -716,11 +720,9 @@ static int pms_s_input(struct file *file, void *fh, unsigned int inp) if (inp > 3) return -EINVAL; - mutex_lock(&dev->lock); dev->input = inp; pms_videosource(dev, inp & 1); pms_vcrinput(dev, inp >> 1); - mutex_unlock(&dev->lock); return 0; } @@ -738,7 +740,6 @@ static int pms_s_std(struct file *file, void *fh, v4l2_std_id *std) int ret = 0; dev->std = *std; - mutex_lock(&dev->lock); if (dev->std & V4L2_STD_NTSC) { pms_framerate(dev, 30); pms_secamcross(dev, 0); @@ -762,81 +763,31 @@ static int pms_s_std(struct file *file, void *fh, v4l2_std_id *std) pms_format(dev, 0); break; }*/ - mutex_unlock(&dev->lock); - return 0; -} - -static int pms_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) -{ - switch (qc->id) { - case V4L2_CID_BRIGHTNESS: - return v4l2_ctrl_query_fill(qc, 0, 255, 1, 139); - case V4L2_CID_CONTRAST: - return v4l2_ctrl_query_fill(qc, 0, 255, 1, 70); - case V4L2_CID_SATURATION: - return v4l2_ctrl_query_fill(qc, 0, 255, 1, 64); - case V4L2_CID_HUE: - return v4l2_ctrl_query_fill(qc, 0, 255, 1, 0); - } - return -EINVAL; -} - -static int pms_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct pms *dev = video_drvdata(file); - int ret = 0; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - ctrl->value = dev->brightness; - break; - case V4L2_CID_CONTRAST: - ctrl->value = dev->contrast; - break; - case V4L2_CID_SATURATION: - ctrl->value = dev->saturation; - break; - case V4L2_CID_HUE: - ctrl->value = dev->hue; - break; - default: - ret = -EINVAL; - break; - } return ret; } -static int pms_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) +static int pms_s_ctrl(struct v4l2_ctrl *ctrl) { - struct pms *dev = video_drvdata(file); + struct pms *dev = container_of(ctrl->handler, struct pms, hdl); int ret = 0; - mutex_lock(&dev->lock); switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: - dev->brightness = ctrl->value; - pms_brightness(dev, dev->brightness); + pms_brightness(dev, ctrl->val); break; case V4L2_CID_CONTRAST: - dev->contrast = ctrl->value; - pms_contrast(dev, dev->contrast); + pms_contrast(dev, ctrl->val); break; case V4L2_CID_SATURATION: - dev->saturation = ctrl->value; - pms_saturation(dev, dev->saturation); + pms_saturation(dev, ctrl->val); break; case V4L2_CID_HUE: - dev->hue = ctrl->value; - pms_hue(dev, dev->hue); + pms_hue(dev, ctrl->val); break; default: ret = -EINVAL; break; } - mutex_unlock(&dev->lock); return ret; } @@ -884,13 +835,11 @@ static int pms_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fm if (ret) return ret; - mutex_lock(&dev->lock); dev->width = pix->width; dev->height = pix->height; dev->depth = (pix->pixelformat == V4L2_PIX_FMT_RGB555) ? 15 : 16; pms_resolution(dev, dev->width, dev->height); /* Ok we figured out what to use from our wide choice */ - mutex_unlock(&dev->lock); return 0; } @@ -901,7 +850,7 @@ static int pms_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc "RGB 5:5:5", V4L2_PIX_FMT_RGB555, { 0, 0, 0, 0 } }, - { 0, 0, 0, + { 1, 0, 0, "RGB 5:6:5", V4L2_PIX_FMT_RGB565, { 0, 0, 0, 0 } }, @@ -922,32 +871,43 @@ static ssize_t pms_read(struct file *file, char __user *buf, struct pms *dev = video_drvdata(file); int len; - mutex_lock(&dev->lock); len = pms_capture(dev, buf, (dev->depth == 15), count); - mutex_unlock(&dev->lock); return len; } +static unsigned int pms_poll(struct file *file, struct poll_table_struct *wait) +{ + struct v4l2_fh *fh = file->private_data; + unsigned int res = POLLIN | POLLRDNORM; + + if (v4l2_event_pending(fh)) + res |= POLLPRI; + poll_wait(file, &fh->wait, wait); + return res; +} + static const struct v4l2_file_operations pms_fops = { .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = v4l2_fh_release, + .poll = pms_poll, .unlocked_ioctl = video_ioctl2, .read = pms_read, }; static const struct v4l2_ioctl_ops pms_ioctl_ops = { - .vidioc_querycap = pms_querycap, - .vidioc_g_input = pms_g_input, - .vidioc_s_input = pms_s_input, - .vidioc_enum_input = pms_enum_input, - .vidioc_g_std = pms_g_std, - .vidioc_s_std = pms_s_std, - .vidioc_queryctrl = pms_queryctrl, - .vidioc_g_ctrl = pms_g_ctrl, - .vidioc_s_ctrl = pms_s_ctrl, - .vidioc_enum_fmt_vid_cap = pms_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = pms_g_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = pms_s_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = pms_try_fmt_vid_cap, + .vidioc_querycap = pms_querycap, + .vidioc_g_input = pms_g_input, + .vidioc_s_input = pms_s_input, + .vidioc_enum_input = pms_enum_input, + .vidioc_g_std = pms_g_std, + .vidioc_s_std = pms_s_std, + .vidioc_enum_fmt_vid_cap = pms_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = pms_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = pms_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = pms_try_fmt_vid_cap, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; /* @@ -956,7 +916,6 @@ static const struct v4l2_ioctl_ops pms_ioctl_ops = { static int init_mediavision(struct pms *dev) { - int id; int idec, decst; int i; static const unsigned char i2c_defs[] = { @@ -988,7 +947,6 @@ static int init_mediavision(struct pms *dev) outb(dev->io >> 4, 0x9a01); /* Set IO port */ - id = mvv_read(dev, 3); decst = pms_i2c_stat(dev, 0x43); if (decst != -1) @@ -1068,76 +1026,125 @@ static int enable; module_param(enable, int, 0); #endif -static int __init pms_init(void) +static const struct v4l2_ctrl_ops pms_ctrl_ops = { + .s_ctrl = pms_s_ctrl, +}; + +static int pms_probe(struct device *pdev, unsigned int card) { - struct pms *dev = &pms_card; - struct v4l2_device *v4l2_dev = &dev->v4l2_dev; + struct pms *dev; + struct v4l2_device *v4l2_dev; + struct v4l2_ctrl_handler *hdl; int res; - strlcpy(v4l2_dev->name, "pms", sizeof(v4l2_dev->name)); - - v4l2_info(v4l2_dev, "Mediavision Pro Movie Studio driver 0.03\n"); - #ifndef MODULE if (!enable) { - v4l2_err(v4l2_dev, - "PMS: not enabled, use pms.enable=1 to probe\n"); + pr_err("PMS: not enabled, use pms.enable=1 to probe\n"); return -ENODEV; } #endif + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (dev == NULL) + return -ENOMEM; + dev->decoder = PHILIPS2; dev->io = io_port; dev->data = io_port + 1; + v4l2_dev = &dev->v4l2_dev; + hdl = &dev->hdl; - if (init_mediavision(dev)) { + res = v4l2_device_register(pdev, v4l2_dev); + if (res < 0) { + v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); + goto free_dev; + } + v4l2_info(v4l2_dev, "Mediavision Pro Movie Studio driver 0.05\n"); + + res = init_mediavision(dev); + if (res) { v4l2_err(v4l2_dev, "Board not found.\n"); - return -ENODEV; + goto free_io; } - res = v4l2_device_register(NULL, v4l2_dev); - if (res < 0) { - v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); - return res; + v4l2_ctrl_handler_init(hdl, 4); + v4l2_ctrl_new_std(hdl, &pms_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 139); + v4l2_ctrl_new_std(hdl, &pms_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, 70); + v4l2_ctrl_new_std(hdl, &pms_ctrl_ops, + V4L2_CID_SATURATION, 0, 255, 1, 64); + v4l2_ctrl_new_std(hdl, &pms_ctrl_ops, + V4L2_CID_HUE, 0, 255, 1, 0); + if (hdl->error) { + res = hdl->error; + goto free_hdl; } + mutex_init(&dev->lock); strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name)); dev->vdev.v4l2_dev = v4l2_dev; + dev->vdev.ctrl_handler = hdl; dev->vdev.fops = &pms_fops; dev->vdev.ioctl_ops = &pms_ioctl_ops; dev->vdev.release = video_device_release_empty; + dev->vdev.lock = &dev->lock; + dev->vdev.tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM; + set_bit(V4L2_FL_USE_FH_PRIO, &dev->vdev.flags); video_set_drvdata(&dev->vdev, dev); - mutex_init(&dev->lock); dev->std = V4L2_STD_NTSC_M; dev->height = 240; dev->width = 320; - dev->depth = 15; - dev->brightness = 139; - dev->contrast = 70; - dev->hue = 0; - dev->saturation = 64; + dev->depth = 16; pms_swsense(dev, 75); pms_resolution(dev, 320, 240); pms_videosource(dev, 0); pms_vcrinput(dev, 0); - if (video_register_device(&dev->vdev, VFL_TYPE_GRABBER, video_nr) < 0) { - v4l2_device_unregister(&dev->v4l2_dev); - release_region(dev->io, 3); - release_region(0x9a01, 1); - iounmap(dev->mem); - return -EINVAL; - } - return 0; + v4l2_ctrl_handler_setup(hdl); + res = video_register_device(&dev->vdev, VFL_TYPE_GRABBER, video_nr); + if (res >= 0) + return 0; + +free_hdl: + v4l2_ctrl_handler_free(hdl); + v4l2_device_unregister(&dev->v4l2_dev); +free_io: + release_region(dev->io, 3); + release_region(0x9a01, 1); + iounmap(dev->mem); +free_dev: + kfree(dev); + return res; } -static void __exit pms_exit(void) +static int pms_remove(struct device *pdev, unsigned int card) { - struct pms *dev = &pms_card; + struct pms *dev = dev_get_drvdata(pdev); video_unregister_device(&dev->vdev); + v4l2_ctrl_handler_free(&dev->hdl); release_region(dev->io, 3); release_region(0x9a01, 1); iounmap(dev->mem); + return 0; +} + +static struct isa_driver pms_driver = { + .probe = pms_probe, + .remove = pms_remove, + .driver = { + .name = "pms", + }, +}; + +static int __init pms_init(void) +{ + return isa_register_driver(&pms_driver, 1); +} + +static void __exit pms_exit(void) +{ + isa_unregister_driver(&pms_driver); } module_init(pms_init); diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h index 305e6aaa844a..036952f2a3cb 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h @@ -317,18 +317,16 @@ struct pvr2_hdw { v4l2_std_id std_mask_eeprom; // Hardware supported selections v4l2_std_id std_mask_avail; // Which standards we may select from v4l2_std_id std_mask_cur; // Currently selected standard(s) - unsigned int std_enum_cnt; // # of enumerated standards int std_enum_cur; // selected standard enumeration value int std_dirty; // True if std_mask_cur has changed struct pvr2_ctl_info std_info_enum; struct pvr2_ctl_info std_info_avail; struct pvr2_ctl_info std_info_cur; - struct v4l2_standard *std_defs; - const char **std_enum_names; + struct pvr2_ctl_info std_info_detect; // Generated string names, one per actual V4L2 standard const char *std_mask_ptrs[32]; - char std_mask_names[32][10]; + char std_mask_names[32][16]; int unit_number; /* ID for driver instance */ unsigned long serial_number; /* ID for hardware itself */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index ebc2c7e39233..fb828ba1dbbe 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -334,8 +334,6 @@ static void pvr2_hdw_state_log_state(struct pvr2_hdw *); static int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl); static int pvr2_hdw_commit_setup(struct pvr2_hdw *hdw); static int pvr2_hdw_get_eeprom_addr(struct pvr2_hdw *hdw); -static void pvr2_hdw_internal_find_stdenum(struct pvr2_hdw *hdw); -static void pvr2_hdw_internal_set_std_avail(struct pvr2_hdw *hdw); static void pvr2_hdw_quiescent_timeout(unsigned long); static void pvr2_hdw_decoder_stabilization_timeout(unsigned long); static void pvr2_hdw_encoder_wait_timeout(unsigned long); @@ -346,7 +344,7 @@ static int pvr2_send_request_ex(struct pvr2_hdw *hdw, void *write_data,unsigned int write_len, void *read_data,unsigned int read_len); static int pvr2_hdw_check_cropcap(struct pvr2_hdw *hdw); - +static v4l2_std_id pvr2_hdw_get_detected_std(struct pvr2_hdw *hdw); static void trace_stbit(const char *name,int val) { @@ -840,6 +838,12 @@ static int ctrl_hsm_get(struct pvr2_ctrl *cptr,int *vp) return 0; } +static int ctrl_stddetect_get(struct pvr2_ctrl *cptr, int *vp) +{ + *vp = pvr2_hdw_get_detected_std(cptr->hdw); + return 0; +} + static int ctrl_stdavail_get(struct pvr2_ctrl *cptr,int *vp) { *vp = cptr->hdw->std_mask_avail; @@ -854,8 +858,7 @@ static int ctrl_stdavail_set(struct pvr2_ctrl *cptr,int m,int v) ns = (ns & ~m) | (v & m); if (ns == hdw->std_mask_avail) return 0; hdw->std_mask_avail = ns; - pvr2_hdw_internal_set_std_avail(hdw); - pvr2_hdw_internal_find_stdenum(hdw); + hdw->std_info_cur.def.type_bitmask.valid_bits = hdw->std_mask_avail; return 0; } @@ -895,7 +898,6 @@ static int ctrl_stdcur_set(struct pvr2_ctrl *cptr,int m,int v) if (ns == hdw->std_mask_cur) return 0; hdw->std_mask_cur = ns; hdw->std_dirty = !0; - pvr2_hdw_internal_find_stdenum(hdw); return 0; } @@ -941,40 +943,6 @@ static int ctrl_audio_modes_present_get(struct pvr2_ctrl *cptr,int *vp) } -static int ctrl_stdenumcur_set(struct pvr2_ctrl *cptr,int m,int v) -{ - struct pvr2_hdw *hdw = cptr->hdw; - if (v < 0) return -EINVAL; - if (v > hdw->std_enum_cnt) return -EINVAL; - hdw->std_enum_cur = v; - if (!v) return 0; - v--; - if (hdw->std_mask_cur == hdw->std_defs[v].id) return 0; - hdw->std_mask_cur = hdw->std_defs[v].id; - hdw->std_dirty = !0; - return 0; -} - - -static int ctrl_stdenumcur_get(struct pvr2_ctrl *cptr,int *vp) -{ - *vp = cptr->hdw->std_enum_cur; - return 0; -} - - -static int ctrl_stdenumcur_is_dirty(struct pvr2_ctrl *cptr) -{ - return cptr->hdw->std_dirty != 0; -} - - -static void ctrl_stdenumcur_clear_dirty(struct pvr2_ctrl *cptr) -{ - cptr->hdw->std_dirty = 0; -} - - #define DEFINT(vmin,vmax) \ .type = pvr2_ctl_int, \ .def.type_int.min_value = vmin, \ @@ -1293,15 +1261,14 @@ static const struct pvr2_ctl_info control_defs[] = { .sym_to_val = ctrl_std_sym_to_val, .type = pvr2_ctl_bitmask, },{ - .desc = "Video Standard Name", - .name = "video_standard", - .internal_id = PVR2_CID_STDENUM, + .desc = "Video Standards Detected Mask", + .name = "video_standard_mask_detected", + .internal_id = PVR2_CID_STDDETECT, .skip_init = !0, - .get_value = ctrl_stdenumcur_get, - .set_value = ctrl_stdenumcur_set, - .is_dirty = ctrl_stdenumcur_is_dirty, - .clear_dirty = ctrl_stdenumcur_clear_dirty, - .type = pvr2_ctl_enum, + .get_value = ctrl_stddetect_get, + .val_to_sym = ctrl_std_val_to_sym, + .sym_to_val = ctrl_std_sym_to_val, + .type = pvr2_ctl_bitmask, } }; @@ -1936,7 +1903,7 @@ static void pvr2_hdw_setup_std(struct pvr2_hdw *hdw) hdw->std_mask_avail |= std2; } - pvr2_hdw_internal_set_std_avail(hdw); + hdw->std_info_cur.def.type_bitmask.valid_bits = hdw->std_mask_avail; if (std1) { bcnt = pvr2_std_id_to_str(buf,sizeof(buf),std1); @@ -1945,7 +1912,6 @@ static void pvr2_hdw_setup_std(struct pvr2_hdw *hdw) bcnt,buf); hdw->std_mask_cur = std1; hdw->std_dirty = !0; - pvr2_hdw_internal_find_stdenum(hdw); return; } if (std3) { @@ -1955,7 +1921,6 @@ static void pvr2_hdw_setup_std(struct pvr2_hdw *hdw) " (determined by device type): %.*s",bcnt,buf); hdw->std_mask_cur = std3; hdw->std_dirty = !0; - pvr2_hdw_internal_find_stdenum(hdw); return; } @@ -1975,24 +1940,10 @@ static void pvr2_hdw_setup_std(struct pvr2_hdw *hdw) bcnt,buf); hdw->std_mask_cur = std_eeprom_maps[idx].std; hdw->std_dirty = !0; - pvr2_hdw_internal_find_stdenum(hdw); return; } } - if (hdw->std_enum_cnt > 1) { - // Autoselect the first listed standard - hdw->std_enum_cur = 1; - hdw->std_mask_cur = hdw->std_defs[hdw->std_enum_cur-1].id; - hdw->std_dirty = !0; - pvr2_trace(PVR2_TRACE_STD, - "Initial video standard auto-selected to %s", - hdw->std_defs[hdw->std_enum_cur-1].name); - return; - } - - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "Unable to select a viable initial video standard"); } @@ -2594,14 +2545,6 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, cptr->info = ciptr; } - // Initialize video standard enum dynamic control - cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDENUM); - if (cptr) { - memcpy(&hdw->std_info_enum,cptr->info, - sizeof(hdw->std_info_enum)); - cptr->info = &hdw->std_info_enum; - - } // Initialize control data regarding video standard masks valid_std_mask = pvr2_std_get_usable(); for (idx = 0; idx < 32; idx++) { @@ -2629,7 +2572,17 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, cptr->info = &hdw->std_info_cur; hdw->std_info_cur.def.type_bitmask.bit_names = hdw->std_mask_ptrs; - hdw->std_info_avail.def.type_bitmask.valid_bits = + hdw->std_info_cur.def.type_bitmask.valid_bits = + valid_std_mask; + } + cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDDETECT); + if (cptr) { + memcpy(&hdw->std_info_detect,cptr->info, + sizeof(hdw->std_info_detect)); + cptr->info = &hdw->std_info_detect; + hdw->std_info_detect.def.type_bitmask.bit_names = + hdw->std_mask_ptrs; + hdw->std_info_detect.def.type_bitmask.valid_bits = valid_std_mask; } @@ -2711,8 +2664,6 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, kfree(hdw->ctl_write_buffer); kfree(hdw->controls); kfree(hdw->mpeg_ctrl_info); - kfree(hdw->std_defs); - kfree(hdw->std_enum_names); kfree(hdw); } return NULL; @@ -2788,8 +2739,6 @@ void pvr2_hdw_destroy(struct pvr2_hdw *hdw) } while (0); mutex_unlock(&pvr2_unit_mtx); kfree(hdw->controls); kfree(hdw->mpeg_ctrl_info); - kfree(hdw->std_defs); - kfree(hdw->std_enum_names); kfree(hdw); } @@ -2812,86 +2761,6 @@ void pvr2_hdw_disconnect(struct pvr2_hdw *hdw) } -// Attempt to autoselect an appropriate value for std_enum_cur given -// whatever is currently in std_mask_cur -static void pvr2_hdw_internal_find_stdenum(struct pvr2_hdw *hdw) -{ - unsigned int idx; - for (idx = 1; idx < hdw->std_enum_cnt; idx++) { - if (hdw->std_defs[idx-1].id == hdw->std_mask_cur) { - hdw->std_enum_cur = idx; - return; - } - } - hdw->std_enum_cur = 0; -} - - -// Calculate correct set of enumerated standards based on currently known -// set of available standards bits. -static void pvr2_hdw_internal_set_std_avail(struct pvr2_hdw *hdw) -{ - struct v4l2_standard *newstd; - unsigned int std_cnt; - unsigned int idx; - - newstd = pvr2_std_create_enum(&std_cnt,hdw->std_mask_avail); - - if (hdw->std_defs) { - kfree(hdw->std_defs); - hdw->std_defs = NULL; - } - hdw->std_enum_cnt = 0; - if (hdw->std_enum_names) { - kfree(hdw->std_enum_names); - hdw->std_enum_names = NULL; - } - - if (!std_cnt) { - pvr2_trace( - PVR2_TRACE_ERROR_LEGS, - "WARNING: Failed to identify any viable standards"); - } - - /* Set up the dynamic control for this standard */ - hdw->std_enum_names = kmalloc(sizeof(char *)*(std_cnt+1),GFP_KERNEL); - if (hdw->std_enum_names) { - hdw->std_enum_names[0] = "none"; - for (idx = 0; idx < std_cnt; idx++) - hdw->std_enum_names[idx+1] = newstd[idx].name; - hdw->std_info_enum.def.type_enum.value_names = - hdw->std_enum_names; - hdw->std_info_enum.def.type_enum.count = std_cnt+1; - } else { - pvr2_trace( - PVR2_TRACE_ERROR_LEGS, - "WARNING: Failed to alloc memory for names"); - hdw->std_info_enum.def.type_enum.value_names = NULL; - hdw->std_info_enum.def.type_enum.count = 0; - } - hdw->std_defs = newstd; - hdw->std_enum_cnt = std_cnt+1; - hdw->std_enum_cur = 0; - hdw->std_info_cur.def.type_bitmask.valid_bits = hdw->std_mask_avail; -} - - -int pvr2_hdw_get_stdenum_value(struct pvr2_hdw *hdw, - struct v4l2_standard *std, - unsigned int idx) -{ - int ret = -EINVAL; - if (!idx) return ret; - LOCK_TAKE(hdw->big_lock); do { - if (idx >= hdw->std_enum_cnt) break; - idx--; - memcpy(std,hdw->std_defs+idx,sizeof(*std)); - ret = 0; - } while (0); LOCK_GIVE(hdw->big_lock); - return ret; -} - - /* Get the number of defined controls */ unsigned int pvr2_hdw_get_ctrl_count(struct pvr2_hdw *hdw) { @@ -2995,11 +2864,13 @@ static void pvr2_subdev_set_control(struct pvr2_hdw *hdw, int id, pvr2_subdev_set_control(hdw, id, #lab, (hdw)->lab##_val); \ } -int pvr2_hdw_get_detected_std(struct pvr2_hdw *hdw, v4l2_std_id *std) +v4l2_std_id pvr2_hdw_get_detected_std(struct pvr2_hdw *hdw) { + v4l2_std_id std; + std = (v4l2_std_id)hdw->std_mask_avail; v4l2_device_call_all(&hdw->v4l2_dev, 0, - video, querystd, std); - return 0; + video, querystd, &std); + return std; } /* Execute whatever commands are required to update the state of all the diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/drivers/media/video/pvrusb2/pvrusb2-hdw.h index 66546580b17d..8060fc666eeb 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.h +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.h @@ -28,7 +28,6 @@ /* Private internal control ids, look these up with pvr2_hdw_get_ctrl_by_id() - these are NOT visible in V4L */ -#define PVR2_CID_STDENUM 1 #define PVR2_CID_STDCUR 2 #define PVR2_CID_STDAVAIL 3 #define PVR2_CID_INPUT 4 @@ -46,6 +45,7 @@ #define PVR2_CID_CROPCAPBT 16 #define PVR2_CID_CROPCAPBW 17 #define PVR2_CID_CROPCAPBH 18 +#define PVR2_CID_STDDETECT 19 /* Legal values for the INPUT state variable */ #define PVR2_CVAL_INPUT_TV 0 @@ -210,13 +210,6 @@ int pvr2_hdw_set_stream_type(struct pvr2_hdw *, enum pvr2_config); /* Get handle to video output stream */ struct pvr2_stream *pvr2_hdw_get_video_stream(struct pvr2_hdw *); -/* Emit a video standard struct */ -int pvr2_hdw_get_stdenum_value(struct pvr2_hdw *hdw,struct v4l2_standard *std, - unsigned int idx); - -/* Get the detected video standard */ -int pvr2_hdw_get_detected_std(struct pvr2_hdw *hdw, v4l2_std_id *std); - /* Enable / disable retrieval of CPU firmware or prom contents. This must be enabled before pvr2_hdw_cpufw_get() will function. Note that doing this may prevent the device from running (and leaving this mode may diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index e1111d968a3d..7bddfaeeafc3 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -107,7 +107,6 @@ static struct v4l2_fmtdesc pvr_fmtdesc [] = { // This should really be V4L2_PIX_FMT_MPEG, but xawtv // breaks when I do that. .pixelformat = 0, // V4L2_PIX_FMT_MPEG, - .reserved = { 0, 0, 0, 0 } } }; @@ -145,740 +144,739 @@ static struct v4l2_format pvr_format [] = { .start = { 0, 0 }, .count = { 0, 0 }, .flags = 0, - .reserved = { 0, 0 } } } } }; + /* - * pvr_ioctl() - * - * This is part of Video 4 Linux API. The procedure handles ioctl() calls. - * + * This is part of Video 4 Linux API. These procedures handle ioctl() calls. */ -static long pvr2_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) +static int pvr2_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { struct pvr2_v4l2_fh *fh = file->private_data; - struct pvr2_v4l2 *vp = fh->vhead; - struct pvr2_v4l2_dev *pdi = fh->pdi; struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; - long ret = -EINVAL; - if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) { - v4l_print_ioctl(pvr2_hdw_get_driver_name(hdw),cmd); - } + memcpy(cap, &pvr_capability, sizeof(struct v4l2_capability)); + strlcpy(cap->bus_info, pvr2_hdw_get_bus_info(hdw), + sizeof(cap->bus_info)); + strlcpy(cap->card, pvr2_hdw_get_desc(hdw), sizeof(cap->card)); + return 0; +} - if (!pvr2_hdw_dev_ok(hdw)) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "ioctl failed - bad or no context"); - return -EFAULT; - } +static int pvr2_g_priority(struct file *file, void *priv, enum v4l2_priority *p) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_v4l2 *vp = fh->vhead; - /* check priority */ - switch (cmd) { - case VIDIOC_S_CTRL: - case VIDIOC_S_STD: - case VIDIOC_S_INPUT: - case VIDIOC_S_TUNER: - case VIDIOC_S_FREQUENCY: - ret = v4l2_prio_check(&vp->prio, fh->prio); - if (ret) - return ret; - } + *p = v4l2_prio_max(&vp->prio); + return 0; +} - switch (cmd) { - case VIDIOC_QUERYCAP: - { - struct v4l2_capability *cap = arg; +static int pvr2_s_priority(struct file *file, void *priv, enum v4l2_priority prio) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_v4l2 *vp = fh->vhead; - memcpy(cap, &pvr_capability, sizeof(struct v4l2_capability)); - strlcpy(cap->bus_info,pvr2_hdw_get_bus_info(hdw), - sizeof(cap->bus_info)); - strlcpy(cap->card,pvr2_hdw_get_desc(hdw),sizeof(cap->card)); + return v4l2_prio_change(&vp->prio, &fh->prio, prio); +} - ret = 0; - break; - } +static int pvr2_g_std(struct file *file, void *priv, v4l2_std_id *std) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + int val = 0; + int ret; - case VIDIOC_G_PRIORITY: - { - enum v4l2_priority *p = arg; + ret = pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_STDCUR), &val); + *std = val; + return ret; +} - *p = v4l2_prio_max(&vp->prio); - ret = 0; - break; - } +int pvr2_s_std(struct file *file, void *priv, v4l2_std_id *std) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; - case VIDIOC_S_PRIORITY: - { - enum v4l2_priority *prio = arg; + return pvr2_ctrl_set_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_STDCUR), *std); +} - ret = v4l2_prio_change(&vp->prio, &fh->prio, *prio); - break; - } +static int pvr2_querystd(struct file *file, void *priv, v4l2_std_id *std) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + int val = 0; + int ret; - case VIDIOC_ENUMSTD: - { - struct v4l2_standard *vs = (struct v4l2_standard *)arg; - int idx = vs->index; - ret = pvr2_hdw_get_stdenum_value(hdw,vs,idx+1); - break; - } + ret = pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_STDDETECT), &val); + *std = val; + return ret; +} - case VIDIOC_QUERYSTD: - { - v4l2_std_id *std = arg; - *std = V4L2_STD_ALL; - ret = pvr2_hdw_get_detected_std(hdw, std); - break; - } +static int pvr2_enum_input(struct file *file, void *priv, struct v4l2_input *vi) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + struct pvr2_ctrl *cptr; + struct v4l2_input tmp; + unsigned int cnt; + int val; + int ret; - case VIDIOC_G_STD: - { - int val = 0; - ret = pvr2_ctrl_get_value( - pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDCUR),&val); - *(v4l2_std_id *)arg = val; + cptr = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT); + + memset(&tmp, 0, sizeof(tmp)); + tmp.index = vi->index; + ret = 0; + if (vi->index >= fh->input_cnt) + return -EINVAL; + val = fh->input_map[vi->index]; + switch (val) { + case PVR2_CVAL_INPUT_TV: + case PVR2_CVAL_INPUT_DTV: + case PVR2_CVAL_INPUT_RADIO: + tmp.type = V4L2_INPUT_TYPE_TUNER; break; - } - - case VIDIOC_S_STD: - { - ret = pvr2_ctrl_set_value( - pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDCUR), - *(v4l2_std_id *)arg); + case PVR2_CVAL_INPUT_SVIDEO: + case PVR2_CVAL_INPUT_COMPOSITE: + tmp.type = V4L2_INPUT_TYPE_CAMERA; break; + default: + return -EINVAL; } - case VIDIOC_ENUMINPUT: - { - struct pvr2_ctrl *cptr; - struct v4l2_input *vi = (struct v4l2_input *)arg; - struct v4l2_input tmp; - unsigned int cnt; - int val; + cnt = 0; + pvr2_ctrl_get_valname(cptr, val, + tmp.name, sizeof(tmp.name) - 1, &cnt); + tmp.name[cnt] = 0; - cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT); + /* Don't bother with audioset, since this driver currently + always switches the audio whenever the video is + switched. */ - memset(&tmp,0,sizeof(tmp)); - tmp.index = vi->index; - ret = 0; - if (vi->index >= fh->input_cnt) { - ret = -EINVAL; - break; - } - val = fh->input_map[vi->index]; - switch (val) { - case PVR2_CVAL_INPUT_TV: - case PVR2_CVAL_INPUT_DTV: - case PVR2_CVAL_INPUT_RADIO: - tmp.type = V4L2_INPUT_TYPE_TUNER; - break; - case PVR2_CVAL_INPUT_SVIDEO: - case PVR2_CVAL_INPUT_COMPOSITE: - tmp.type = V4L2_INPUT_TYPE_CAMERA; - break; - default: - ret = -EINVAL; - break; - } - if (ret < 0) break; - - cnt = 0; - pvr2_ctrl_get_valname(cptr,val, - tmp.name,sizeof(tmp.name)-1,&cnt); - tmp.name[cnt] = 0; - - /* Don't bother with audioset, since this driver currently - always switches the audio whenever the video is - switched. */ - - /* Handling std is a tougher problem. It doesn't make - sense in cases where a device might be multi-standard. - We could just copy out the current value for the - standard, but it can change over time. For now just - leave it zero. */ - - memcpy(vi, &tmp, sizeof(tmp)); - - ret = 0; - break; - } + /* Handling std is a tougher problem. It doesn't make + sense in cases where a device might be multi-standard. + We could just copy out the current value for the + standard, but it can change over time. For now just + leave it zero. */ + *vi = tmp; + return 0; +} - case VIDIOC_G_INPUT: - { - unsigned int idx; - struct pvr2_ctrl *cptr; - struct v4l2_input *vi = (struct v4l2_input *)arg; - int val; - cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT); - val = 0; - ret = pvr2_ctrl_get_value(cptr,&val); - vi->index = 0; - for (idx = 0; idx < fh->input_cnt; idx++) { - if (fh->input_map[idx] == val) { - vi->index = idx; - break; - } - } - break; - } +static int pvr2_g_input(struct file *file, void *priv, unsigned int *i) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + unsigned int idx; + struct pvr2_ctrl *cptr; + int val; + int ret; - case VIDIOC_S_INPUT: - { - struct v4l2_input *vi = (struct v4l2_input *)arg; - if (vi->index >= fh->input_cnt) { - ret = -ERANGE; + cptr = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT); + val = 0; + ret = pvr2_ctrl_get_value(cptr, &val); + *i = 0; + for (idx = 0; idx < fh->input_cnt; idx++) { + if (fh->input_map[idx] == val) { + *i = idx; break; } - ret = pvr2_ctrl_set_value( - pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT), - fh->input_map[vi->index]); - break; } + return ret; +} - case VIDIOC_ENUMAUDIO: - { - /* pkt: FIXME: We are returning one "fake" input here - which could very well be called "whatever_we_like". - This is for apps that want to see an audio input - just to feel comfortable, as well as to test if - it can do stereo or sth. There is actually no guarantee - that the actual audio input cannot change behind the app's - back, but most applications should not mind that either. - - Hopefully, mplayer people will work with us on this (this - whole mess is to support mplayer pvr://), or Hans will come - up with a more standard way to say "we have inputs but we - don 't want you to change them independent of video" which - will sort this mess. - */ - struct v4l2_audio *vin = arg; - ret = -EINVAL; - if (vin->index > 0) break; - strncpy(vin->name, "PVRUSB2 Audio",14); - vin->capability = V4L2_AUDCAP_STEREO; - ret = 0; - break; - break; - } +static int pvr2_s_input(struct file *file, void *priv, unsigned int inp) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; - case VIDIOC_G_AUDIO: - { - /* pkt: FIXME: see above comment (VIDIOC_ENUMAUDIO) */ - struct v4l2_audio *vin = arg; - memset(vin,0,sizeof(*vin)); - vin->index = 0; - strncpy(vin->name, "PVRUSB2 Audio",14); - vin->capability = V4L2_AUDCAP_STEREO; - ret = 0; - break; - } + if (inp >= fh->input_cnt) + return -EINVAL; + return pvr2_ctrl_set_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT), + fh->input_map[inp]); +} - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *vt = (struct v4l2_tuner *)arg; +static int pvr2_enumaudio(struct file *file, void *priv, struct v4l2_audio *vin) +{ + /* pkt: FIXME: We are returning one "fake" input here + which could very well be called "whatever_we_like". + This is for apps that want to see an audio input + just to feel comfortable, as well as to test if + it can do stereo or sth. There is actually no guarantee + that the actual audio input cannot change behind the app's + back, but most applications should not mind that either. + + Hopefully, mplayer people will work with us on this (this + whole mess is to support mplayer pvr://), or Hans will come + up with a more standard way to say "we have inputs but we + don 't want you to change them independent of video" which + will sort this mess. + */ + + if (vin->index > 0) + return -EINVAL; + strncpy(vin->name, "PVRUSB2 Audio", 14); + vin->capability = V4L2_AUDCAP_STEREO; + return 0; +} - if (vt->index != 0) break; /* Only answer for the 1st tuner */ +static int pvr2_g_audio(struct file *file, void *priv, struct v4l2_audio *vin) +{ + /* pkt: FIXME: see above comment (VIDIOC_ENUMAUDIO) */ + vin->index = 0; + strncpy(vin->name, "PVRUSB2 Audio", 14); + vin->capability = V4L2_AUDCAP_STEREO; + return 0; +} - pvr2_hdw_execute_tuner_poll(hdw); - ret = pvr2_hdw_get_tuner_status(hdw,vt); - break; - } +static int pvr2_s_audio(struct file *file, void *priv, struct v4l2_audio *vout) +{ + if (vout->index) + return -EINVAL; + return 0; +} - case VIDIOC_S_TUNER: - { - struct v4l2_tuner *vt=(struct v4l2_tuner *)arg; +static int pvr2_g_tuner(struct file *file, void *priv, struct v4l2_tuner *vt) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; - if (vt->index != 0) - break; + if (vt->index != 0) + return -EINVAL; /* Only answer for the 1st tuner */ - ret = pvr2_ctrl_set_value( - pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_AUDIOMODE), + pvr2_hdw_execute_tuner_poll(hdw); + return pvr2_hdw_get_tuner_status(hdw, vt); +} + +static int pvr2_s_tuner(struct file *file, void *priv, struct v4l2_tuner *vt) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + + if (vt->index != 0) + return -EINVAL; + + return pvr2_ctrl_set_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_AUDIOMODE), vt->audmode); - break; - } +} - case VIDIOC_S_FREQUENCY: - { - const struct v4l2_frequency *vf = (struct v4l2_frequency *)arg; - unsigned long fv; - struct v4l2_tuner vt; - int cur_input; - struct pvr2_ctrl *ctrlp; - ret = pvr2_hdw_get_tuner_status(hdw,&vt); - if (ret != 0) break; - ctrlp = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT); - ret = pvr2_ctrl_get_value(ctrlp,&cur_input); - if (ret != 0) break; - if (vf->type == V4L2_TUNER_RADIO) { - if (cur_input != PVR2_CVAL_INPUT_RADIO) { - pvr2_ctrl_set_value(ctrlp, - PVR2_CVAL_INPUT_RADIO); - } - } else { - if (cur_input == PVR2_CVAL_INPUT_RADIO) { - pvr2_ctrl_set_value(ctrlp, - PVR2_CVAL_INPUT_TV); - } - } - fv = vf->frequency; - if (vt.capability & V4L2_TUNER_CAP_LOW) { - fv = (fv * 125) / 2; - } else { - fv = fv * 62500; - } - ret = pvr2_ctrl_set_value( +int pvr2_s_frequency(struct file *file, void *priv, struct v4l2_frequency *vf) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + unsigned long fv; + struct v4l2_tuner vt; + int cur_input; + struct pvr2_ctrl *ctrlp; + int ret; + + ret = pvr2_hdw_get_tuner_status(hdw, &vt); + if (ret != 0) + return ret; + ctrlp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT); + ret = pvr2_ctrl_get_value(ctrlp, &cur_input); + if (ret != 0) + return ret; + if (vf->type == V4L2_TUNER_RADIO) { + if (cur_input != PVR2_CVAL_INPUT_RADIO) + pvr2_ctrl_set_value(ctrlp, PVR2_CVAL_INPUT_RADIO); + } else { + if (cur_input == PVR2_CVAL_INPUT_RADIO) + pvr2_ctrl_set_value(ctrlp, PVR2_CVAL_INPUT_TV); + } + fv = vf->frequency; + if (vt.capability & V4L2_TUNER_CAP_LOW) + fv = (fv * 125) / 2; + else + fv = fv * 62500; + return pvr2_ctrl_set_value( pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_FREQUENCY),fv); - break; - } +} - case VIDIOC_G_FREQUENCY: - { - struct v4l2_frequency *vf = (struct v4l2_frequency *)arg; - int val = 0; - int cur_input; - struct v4l2_tuner vt; - ret = pvr2_hdw_get_tuner_status(hdw,&vt); - if (ret != 0) break; - ret = pvr2_ctrl_get_value( - pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_FREQUENCY), +static int pvr2_g_frequency(struct file *file, void *priv, struct v4l2_frequency *vf) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + int val = 0; + int cur_input; + struct v4l2_tuner vt; + int ret; + + ret = pvr2_hdw_get_tuner_status(hdw, &vt); + if (ret != 0) + return ret; + ret = pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_FREQUENCY), &val); - if (ret != 0) break; - pvr2_ctrl_get_value( - pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT), + if (ret != 0) + return ret; + pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT), &cur_input); - if (cur_input == PVR2_CVAL_INPUT_RADIO) { - vf->type = V4L2_TUNER_RADIO; - } else { - vf->type = V4L2_TUNER_ANALOG_TV; - } - if (vt.capability & V4L2_TUNER_CAP_LOW) { - val = (val * 2) / 125; - } else { - val /= 62500; - } - vf->frequency = val; - break; - } + if (cur_input == PVR2_CVAL_INPUT_RADIO) + vf->type = V4L2_TUNER_RADIO; + else + vf->type = V4L2_TUNER_ANALOG_TV; + if (vt.capability & V4L2_TUNER_CAP_LOW) + val = (val * 2) / 125; + else + val /= 62500; + vf->frequency = val; + return 0; +} - case VIDIOC_ENUM_FMT: - { - struct v4l2_fmtdesc *fd = (struct v4l2_fmtdesc *)arg; +static int pvr2_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *fd) +{ + /* Only one format is supported : mpeg.*/ + if (fd->index != 0) + return -EINVAL; - /* Only one format is supported : mpeg.*/ - if (fd->index != 0) - break; + memcpy(fd, pvr_fmtdesc, sizeof(struct v4l2_fmtdesc)); + return 0; +} - memcpy(fd, pvr_fmtdesc, sizeof(struct v4l2_fmtdesc)); - ret = 0; - break; - } +static int pvr2_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *vf) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + int val; - case VIDIOC_G_FMT: - { - struct v4l2_format *vf = (struct v4l2_format *)arg; - int val; - switch(vf->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - memcpy(vf, &pvr_format[PVR_FORMAT_PIX], - sizeof(struct v4l2_format)); - val = 0; - pvr2_ctrl_get_value( - pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_HRES), - &val); - vf->fmt.pix.width = val; - val = 0; - pvr2_ctrl_get_value( - pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_VRES), - &val); - vf->fmt.pix.height = val; - ret = 0; - break; - case V4L2_BUF_TYPE_VBI_CAPTURE: - // ????? Still need to figure out to do VBI correctly - ret = -EINVAL; - break; - default: - ret = -EINVAL; - break; - } - break; - } + memcpy(vf, &pvr_format[PVR_FORMAT_PIX], sizeof(struct v4l2_format)); + val = 0; + pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_HRES), + &val); + vf->fmt.pix.width = val; + val = 0; + pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_VRES), + &val); + vf->fmt.pix.height = val; + return 0; +} - case VIDIOC_TRY_FMT: - case VIDIOC_S_FMT: - { - struct v4l2_format *vf = (struct v4l2_format *)arg; - - ret = 0; - switch(vf->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: { - int lmin,lmax,ldef; - struct pvr2_ctrl *hcp,*vcp; - int h = vf->fmt.pix.height; - int w = vf->fmt.pix.width; - hcp = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_HRES); - vcp = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_VRES); - - lmin = pvr2_ctrl_get_min(hcp); - lmax = pvr2_ctrl_get_max(hcp); - pvr2_ctrl_get_def(hcp, &ldef); - if (w == -1) { - w = ldef; - } else if (w < lmin) { - w = lmin; - } else if (w > lmax) { - w = lmax; - } - lmin = pvr2_ctrl_get_min(vcp); - lmax = pvr2_ctrl_get_max(vcp); - pvr2_ctrl_get_def(vcp, &ldef); - if (h == -1) { - h = ldef; - } else if (h < lmin) { - h = lmin; - } else if (h > lmax) { - h = lmax; - } +static int pvr2_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *vf) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + int lmin, lmax, ldef; + struct pvr2_ctrl *hcp, *vcp; + int h = vf->fmt.pix.height; + int w = vf->fmt.pix.width; + + hcp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_HRES); + vcp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_VRES); + + lmin = pvr2_ctrl_get_min(hcp); + lmax = pvr2_ctrl_get_max(hcp); + pvr2_ctrl_get_def(hcp, &ldef); + if (w == -1) + w = ldef; + else if (w < lmin) + w = lmin; + else if (w > lmax) + w = lmax; + lmin = pvr2_ctrl_get_min(vcp); + lmax = pvr2_ctrl_get_max(vcp); + pvr2_ctrl_get_def(vcp, &ldef); + if (h == -1) + h = ldef; + else if (h < lmin) + h = lmin; + else if (h > lmax) + h = lmax; + + memcpy(vf, &pvr_format[PVR_FORMAT_PIX], + sizeof(struct v4l2_format)); + vf->fmt.pix.width = w; + vf->fmt.pix.height = h; + return 0; +} - memcpy(vf, &pvr_format[PVR_FORMAT_PIX], - sizeof(struct v4l2_format)); - vf->fmt.pix.width = w; - vf->fmt.pix.height = h; +static int pvr2_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *vf) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + struct pvr2_ctrl *hcp, *vcp; + int ret = pvr2_try_fmt_vid_cap(file, fh, vf); - if (cmd == VIDIOC_S_FMT) { - pvr2_ctrl_set_value(hcp,vf->fmt.pix.width); - pvr2_ctrl_set_value(vcp,vf->fmt.pix.height); - } - } break; - case V4L2_BUF_TYPE_VBI_CAPTURE: - // ????? Still need to figure out to do VBI correctly - ret = -EINVAL; - break; - default: - ret = -EINVAL; - break; - } - break; - } + if (ret) + return ret; + hcp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_HRES); + vcp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_VRES); + pvr2_ctrl_set_value(hcp, vf->fmt.pix.width); + pvr2_ctrl_set_value(vcp, vf->fmt.pix.height); + return 0; +} - case VIDIOC_STREAMON: - { - if (!fh->pdi->stream) { - /* No stream defined for this node. This means - that we're not currently allowed to stream from - this node. */ - ret = -EPERM; - break; - } - ret = pvr2_hdw_set_stream_type(hdw,pdi->config); - if (ret < 0) return ret; - ret = pvr2_hdw_set_streaming(hdw,!0); - break; +static int pvr2_streamon(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + struct pvr2_v4l2_dev *pdi = fh->pdi; + int ret; + + if (!fh->pdi->stream) { + /* No stream defined for this node. This means + that we're not currently allowed to stream from + this node. */ + return -EPERM; } + ret = pvr2_hdw_set_stream_type(hdw, pdi->config); + if (ret < 0) + return ret; + return pvr2_hdw_set_streaming(hdw, !0); +} - case VIDIOC_STREAMOFF: - { - if (!fh->pdi->stream) { - /* No stream defined for this node. This means - that we're not currently allowed to stream from - this node. */ - ret = -EPERM; - break; - } - ret = pvr2_hdw_set_streaming(hdw,0); - break; +static int pvr2_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + + if (!fh->pdi->stream) { + /* No stream defined for this node. This means + that we're not currently allowed to stream from + this node. */ + return -EPERM; } + return pvr2_hdw_set_streaming(hdw, 0); +} - case VIDIOC_QUERYCTRL: - { - struct pvr2_ctrl *cptr; - int val; - struct v4l2_queryctrl *vc = (struct v4l2_queryctrl *)arg; - ret = 0; - if (vc->id & V4L2_CTRL_FLAG_NEXT_CTRL) { - cptr = pvr2_hdw_get_ctrl_nextv4l( - hdw,(vc->id & ~V4L2_CTRL_FLAG_NEXT_CTRL)); - if (cptr) vc->id = pvr2_ctrl_get_v4lid(cptr); - } else { - cptr = pvr2_hdw_get_ctrl_v4l(hdw,vc->id); - } - if (!cptr) { - pvr2_trace(PVR2_TRACE_V4LIOCTL, - "QUERYCTRL id=0x%x not implemented here", - vc->id); - ret = -EINVAL; - break; - } +static int pvr2_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *vc) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + struct pvr2_ctrl *cptr; + int val; + int ret; + ret = 0; + if (vc->id & V4L2_CTRL_FLAG_NEXT_CTRL) { + cptr = pvr2_hdw_get_ctrl_nextv4l( + hdw, (vc->id & ~V4L2_CTRL_FLAG_NEXT_CTRL)); + if (cptr) + vc->id = pvr2_ctrl_get_v4lid(cptr); + } else { + cptr = pvr2_hdw_get_ctrl_v4l(hdw, vc->id); + } + if (!cptr) { pvr2_trace(PVR2_TRACE_V4LIOCTL, - "QUERYCTRL id=0x%x mapping name=%s (%s)", - vc->id,pvr2_ctrl_get_name(cptr), - pvr2_ctrl_get_desc(cptr)); - strlcpy(vc->name,pvr2_ctrl_get_desc(cptr),sizeof(vc->name)); - vc->flags = pvr2_ctrl_get_v4lflags(cptr); - pvr2_ctrl_get_def(cptr, &val); - vc->default_value = val; - switch (pvr2_ctrl_get_type(cptr)) { - case pvr2_ctl_enum: - vc->type = V4L2_CTRL_TYPE_MENU; - vc->minimum = 0; - vc->maximum = pvr2_ctrl_get_cnt(cptr) - 1; - vc->step = 1; - break; - case pvr2_ctl_bool: - vc->type = V4L2_CTRL_TYPE_BOOLEAN; - vc->minimum = 0; - vc->maximum = 1; - vc->step = 1; - break; - case pvr2_ctl_int: - vc->type = V4L2_CTRL_TYPE_INTEGER; - vc->minimum = pvr2_ctrl_get_min(cptr); - vc->maximum = pvr2_ctrl_get_max(cptr); - vc->step = 1; - break; - default: - pvr2_trace(PVR2_TRACE_V4LIOCTL, - "QUERYCTRL id=0x%x name=%s not mappable", - vc->id,pvr2_ctrl_get_name(cptr)); - ret = -EINVAL; - break; - } + "QUERYCTRL id=0x%x not implemented here", + vc->id); + return -EINVAL; + } + + pvr2_trace(PVR2_TRACE_V4LIOCTL, + "QUERYCTRL id=0x%x mapping name=%s (%s)", + vc->id, pvr2_ctrl_get_name(cptr), + pvr2_ctrl_get_desc(cptr)); + strlcpy(vc->name, pvr2_ctrl_get_desc(cptr), sizeof(vc->name)); + vc->flags = pvr2_ctrl_get_v4lflags(cptr); + pvr2_ctrl_get_def(cptr, &val); + vc->default_value = val; + switch (pvr2_ctrl_get_type(cptr)) { + case pvr2_ctl_enum: + vc->type = V4L2_CTRL_TYPE_MENU; + vc->minimum = 0; + vc->maximum = pvr2_ctrl_get_cnt(cptr) - 1; + vc->step = 1; break; - } - - case VIDIOC_QUERYMENU: - { - struct v4l2_querymenu *vm = (struct v4l2_querymenu *)arg; - unsigned int cnt = 0; - ret = pvr2_ctrl_get_valname(pvr2_hdw_get_ctrl_v4l(hdw,vm->id), - vm->index, - vm->name,sizeof(vm->name)-1, - &cnt); - vm->name[cnt] = 0; + case pvr2_ctl_bool: + vc->type = V4L2_CTRL_TYPE_BOOLEAN; + vc->minimum = 0; + vc->maximum = 1; + vc->step = 1; break; - } - - case VIDIOC_G_CTRL: - { - struct v4l2_control *vc = (struct v4l2_control *)arg; - int val = 0; - ret = pvr2_ctrl_get_value(pvr2_hdw_get_ctrl_v4l(hdw,vc->id), - &val); - vc->value = val; + case pvr2_ctl_int: + vc->type = V4L2_CTRL_TYPE_INTEGER; + vc->minimum = pvr2_ctrl_get_min(cptr); + vc->maximum = pvr2_ctrl_get_max(cptr); + vc->step = 1; break; + default: + pvr2_trace(PVR2_TRACE_V4LIOCTL, + "QUERYCTRL id=0x%x name=%s not mappable", + vc->id, pvr2_ctrl_get_name(cptr)); + return -EINVAL; } + return 0; +} - case VIDIOC_S_CTRL: - { - struct v4l2_control *vc = (struct v4l2_control *)arg; - ret = pvr2_ctrl_set_value(pvr2_hdw_get_ctrl_v4l(hdw,vc->id), - vc->value); - break; - } +static int pvr2_querymenu(struct file *file, void *priv, struct v4l2_querymenu *vm) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + unsigned int cnt = 0; + int ret; - case VIDIOC_G_EXT_CTRLS: - { - struct v4l2_ext_controls *ctls = - (struct v4l2_ext_controls *)arg; - struct v4l2_ext_control *ctrl; - unsigned int idx; - int val; - ret = 0; - for (idx = 0; idx < ctls->count; idx++) { - ctrl = ctls->controls + idx; - ret = pvr2_ctrl_get_value( - pvr2_hdw_get_ctrl_v4l(hdw,ctrl->id),&val); - if (ret) { - ctls->error_idx = idx; - break; - } - /* Ensure that if read as a 64 bit value, the user - will still get a hopefully sane value */ - ctrl->value64 = 0; - ctrl->value = val; + ret = pvr2_ctrl_get_valname(pvr2_hdw_get_ctrl_v4l(hdw, vm->id), + vm->index, + vm->name, sizeof(vm->name) - 1, + &cnt); + vm->name[cnt] = 0; + return ret; +} + +static int pvr2_g_ctrl(struct file *file, void *priv, struct v4l2_control *vc) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + int val = 0; + int ret; + + ret = pvr2_ctrl_get_value(pvr2_hdw_get_ctrl_v4l(hdw, vc->id), + &val); + vc->value = val; + return ret; +} + +static int pvr2_s_ctrl(struct file *file, void *priv, struct v4l2_control *vc) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + + return pvr2_ctrl_set_value(pvr2_hdw_get_ctrl_v4l(hdw, vc->id), + vc->value); +} + +static int pvr2_g_ext_ctrls(struct file *file, void *priv, + struct v4l2_ext_controls *ctls) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + struct v4l2_ext_control *ctrl; + unsigned int idx; + int val; + int ret; + + ret = 0; + for (idx = 0; idx < ctls->count; idx++) { + ctrl = ctls->controls + idx; + ret = pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_v4l(hdw, ctrl->id), &val); + if (ret) { + ctls->error_idx = idx; + return ret; } - break; + /* Ensure that if read as a 64 bit value, the user + will still get a hopefully sane value */ + ctrl->value64 = 0; + ctrl->value = val; } + return 0; +} - case VIDIOC_S_EXT_CTRLS: - { - struct v4l2_ext_controls *ctls = - (struct v4l2_ext_controls *)arg; - struct v4l2_ext_control *ctrl; - unsigned int idx; - ret = 0; - for (idx = 0; idx < ctls->count; idx++) { - ctrl = ctls->controls + idx; - ret = pvr2_ctrl_set_value( - pvr2_hdw_get_ctrl_v4l(hdw,ctrl->id), +static int pvr2_s_ext_ctrls(struct file *file, void *priv, + struct v4l2_ext_controls *ctls) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + struct v4l2_ext_control *ctrl; + unsigned int idx; + int ret; + + ret = 0; + for (idx = 0; idx < ctls->count; idx++) { + ctrl = ctls->controls + idx; + ret = pvr2_ctrl_set_value( + pvr2_hdw_get_ctrl_v4l(hdw, ctrl->id), ctrl->value); - if (ret) { - ctls->error_idx = idx; - break; - } + if (ret) { + ctls->error_idx = idx; + return ret; } - break; } + return 0; +} - case VIDIOC_TRY_EXT_CTRLS: - { - struct v4l2_ext_controls *ctls = - (struct v4l2_ext_controls *)arg; - struct v4l2_ext_control *ctrl; - struct pvr2_ctrl *pctl; - unsigned int idx; - /* For the moment just validate that the requested control - actually exists. */ - ret = 0; - for (idx = 0; idx < ctls->count; idx++) { - ctrl = ctls->controls + idx; - pctl = pvr2_hdw_get_ctrl_v4l(hdw,ctrl->id); - if (!pctl) { - ret = -EINVAL; - ctls->error_idx = idx; - break; - } - } - break; - } +static int pvr2_try_ext_ctrls(struct file *file, void *priv, + struct v4l2_ext_controls *ctls) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + struct v4l2_ext_control *ctrl; + struct pvr2_ctrl *pctl; + unsigned int idx; + int ret; - case VIDIOC_CROPCAP: - { - struct v4l2_cropcap *cap = (struct v4l2_cropcap *)arg; - if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { - ret = -EINVAL; - break; + /* For the moment just validate that the requested control + actually exists. */ + ret = 0; + for (idx = 0; idx < ctls->count; idx++) { + ctrl = ctls->controls + idx; + pctl = pvr2_hdw_get_ctrl_v4l(hdw, ctrl->id); + if (!pctl) { + ctls->error_idx = idx; + return -EINVAL; } - ret = pvr2_hdw_get_cropcap(hdw, cap); - cap->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; /* paranoia */ - break; } - case VIDIOC_G_CROP: - { - struct v4l2_crop *crop = (struct v4l2_crop *)arg; - int val = 0; - if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { - ret = -EINVAL; - break; - } - ret = pvr2_ctrl_get_value( + return 0; +} + +static int pvr2_cropcap(struct file *file, void *priv, struct v4l2_cropcap *cap) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + int ret; + + if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + ret = pvr2_hdw_get_cropcap(hdw, cap); + cap->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; /* paranoia */ + return ret; +} + +static int pvr2_g_crop(struct file *file, void *priv, struct v4l2_crop *crop) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + int val = 0; + int ret; + + if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + ret = pvr2_ctrl_get_value( pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPL), &val); - if (ret != 0) { - ret = -EINVAL; - break; - } - crop->c.left = val; - ret = pvr2_ctrl_get_value( + if (ret != 0) + return -EINVAL; + crop->c.left = val; + ret = pvr2_ctrl_get_value( pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPT), &val); - if (ret != 0) { - ret = -EINVAL; - break; - } - crop->c.top = val; - ret = pvr2_ctrl_get_value( + if (ret != 0) + return -EINVAL; + crop->c.top = val; + ret = pvr2_ctrl_get_value( pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPW), &val); - if (ret != 0) { - ret = -EINVAL; - break; - } - crop->c.width = val; - ret = pvr2_ctrl_get_value( + if (ret != 0) + return -EINVAL; + crop->c.width = val; + ret = pvr2_ctrl_get_value( pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPH), &val); - if (ret != 0) { - ret = -EINVAL; - break; - } - crop->c.height = val; - } - case VIDIOC_S_CROP: - { - struct v4l2_crop *crop = (struct v4l2_crop *)arg; - if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { - ret = -EINVAL; - break; - } - ret = pvr2_ctrl_set_value( + if (ret != 0) + return -EINVAL; + crop->c.height = val; + return 0; +} + +static int pvr2_s_crop(struct file *file, void *priv, struct v4l2_crop *crop) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + struct v4l2_cropcap cap; + int ret; + + if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + ret = pvr2_ctrl_set_value( pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPL), crop->c.left); - if (ret != 0) { - ret = -EINVAL; - break; - } - ret = pvr2_ctrl_set_value( + if (ret != 0) + return -EINVAL; + ret = pvr2_ctrl_set_value( pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPT), crop->c.top); - if (ret != 0) { - ret = -EINVAL; - break; - } - ret = pvr2_ctrl_set_value( + if (ret != 0) + return -EINVAL; + ret = pvr2_ctrl_set_value( pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPW), crop->c.width); - if (ret != 0) { - ret = -EINVAL; - break; - } - ret = pvr2_ctrl_set_value( + if (ret != 0) + return -EINVAL; + ret = pvr2_ctrl_set_value( pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPH), crop->c.height); - if (ret != 0) { - ret = -EINVAL; - break; - } - } - case VIDIOC_LOG_STATUS: - { - pvr2_hdw_trigger_module_log(hdw); - ret = 0; - break; - } + if (ret != 0) + return -EINVAL; + return 0; +} + +static int pvr2_log_status(struct file *file, void *priv) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + + pvr2_hdw_trigger_module_log(hdw); + return 0; +} + #ifdef CONFIG_VIDEO_ADV_DEBUG - case VIDIOC_DBG_S_REGISTER: - case VIDIOC_DBG_G_REGISTER: - { - u64 val; - struct v4l2_dbg_register *req = (struct v4l2_dbg_register *)arg; - if (cmd == VIDIOC_DBG_S_REGISTER) val = req->val; - ret = pvr2_hdw_register_access( - hdw, &req->match, req->reg, - cmd == VIDIOC_DBG_S_REGISTER, &val); - if (cmd == VIDIOC_DBG_G_REGISTER) req->val = val; - break; - } -#endif +static int pvr2_g_register(struct file *file, void *priv, struct v4l2_dbg_register *req) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + u64 val; + int ret; - default : - ret = -ENOTTY; - break; - } + ret = pvr2_hdw_register_access( + hdw, &req->match, req->reg, + 0, &val); + req->val = val; + return ret; +} - pvr2_hdw_commit_ctl(hdw); +static int pvr2_s_register(struct file *file, void *priv, struct v4l2_dbg_register *req) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + u64 val; + int ret; - if (ret < 0) { - if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) { - pvr2_trace(PVR2_TRACE_V4LIOCTL, - "pvr2_v4l2_do_ioctl failure, ret=%ld", ret); - } else { - if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) { - pvr2_trace(PVR2_TRACE_V4LIOCTL, - "pvr2_v4l2_do_ioctl failure, ret=%ld" - " command was:", ret); - v4l_print_ioctl(pvr2_hdw_get_driver_name(hdw), - cmd); - } - } - } else { - pvr2_trace(PVR2_TRACE_V4LIOCTL, - "pvr2_v4l2_do_ioctl complete, ret=%ld (0x%lx)", - ret, ret); - } + val = req->val; + ret = pvr2_hdw_register_access( + hdw, &req->match, req->reg, + 1, &val); return ret; } +#endif + +static const struct v4l2_ioctl_ops pvr2_ioctl_ops = { + .vidioc_querycap = pvr2_querycap, + .vidioc_g_priority = pvr2_g_priority, + .vidioc_s_priority = pvr2_s_priority, + .vidioc_s_audio = pvr2_s_audio, + .vidioc_g_audio = pvr2_g_audio, + .vidioc_enumaudio = pvr2_enumaudio, + .vidioc_enum_input = pvr2_enum_input, + .vidioc_cropcap = pvr2_cropcap, + .vidioc_s_crop = pvr2_s_crop, + .vidioc_g_crop = pvr2_g_crop, + .vidioc_g_input = pvr2_g_input, + .vidioc_s_input = pvr2_s_input, + .vidioc_g_frequency = pvr2_g_frequency, + .vidioc_s_frequency = pvr2_s_frequency, + .vidioc_s_tuner = pvr2_s_tuner, + .vidioc_g_tuner = pvr2_g_tuner, + .vidioc_g_std = pvr2_g_std, + .vidioc_s_std = pvr2_s_std, + .vidioc_querystd = pvr2_querystd, + .vidioc_log_status = pvr2_log_status, + .vidioc_enum_fmt_vid_cap = pvr2_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = pvr2_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = pvr2_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = pvr2_try_fmt_vid_cap, + .vidioc_streamon = pvr2_streamon, + .vidioc_streamoff = pvr2_streamoff, + .vidioc_queryctrl = pvr2_queryctrl, + .vidioc_querymenu = pvr2_querymenu, + .vidioc_g_ctrl = pvr2_g_ctrl, + .vidioc_s_ctrl = pvr2_s_ctrl, + .vidioc_g_ext_ctrls = pvr2_g_ext_ctrls, + .vidioc_s_ext_ctrls = pvr2_s_ext_ctrls, + .vidioc_try_ext_ctrls = pvr2_try_ext_ctrls, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = pvr2_g_register, + .vidioc_s_register = pvr2_s_register, +#endif +}; static void pvr2_v4l2_dev_destroy(struct pvr2_v4l2_dev *dip) { @@ -961,7 +959,56 @@ static long pvr2_v4l2_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - return video_usercopy(file, cmd, arg, pvr2_v4l2_do_ioctl); + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_v4l2 *vp = fh->vhead; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + long ret = -EINVAL; + + if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) + v4l_print_ioctl(pvr2_hdw_get_driver_name(hdw), cmd); + + if (!pvr2_hdw_dev_ok(hdw)) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "ioctl failed - bad or no context"); + return -EFAULT; + } + + /* check priority */ + switch (cmd) { + case VIDIOC_S_CTRL: + case VIDIOC_S_STD: + case VIDIOC_S_INPUT: + case VIDIOC_S_TUNER: + case VIDIOC_S_FREQUENCY: + ret = v4l2_prio_check(&vp->prio, fh->prio); + if (ret) + return ret; + } + + ret = video_ioctl2(file, cmd, arg); + + pvr2_hdw_commit_ctl(hdw); + + if (ret < 0) { + if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) { + pvr2_trace(PVR2_TRACE_V4LIOCTL, + "pvr2_v4l2_do_ioctl failure, ret=%ld", ret); + } else { + if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) { + pvr2_trace(PVR2_TRACE_V4LIOCTL, + "pvr2_v4l2_do_ioctl failure, ret=%ld" + " command was:", ret); + v4l_print_ioctl(pvr2_hdw_get_driver_name(hdw), + cmd); + } + } + } else { + pvr2_trace(PVR2_TRACE_V4LIOCTL, + "pvr2_v4l2_do_ioctl complete, ret=%ld (0x%lx)", + ret, ret); + } + return ret; + } @@ -1262,10 +1309,12 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip, struct usb_device *usbdev; int mindevnum; int unit_number; + struct pvr2_hdw *hdw; int *nr_ptr = NULL; dip->v4lp = vp; - usbdev = pvr2_hdw_get_dev(vp->channel.mc_head->hdw); + hdw = vp->channel.mc_head->hdw; + usbdev = pvr2_hdw_get_dev(hdw); dip->v4l_type = v4l_type; switch (v4l_type) { case VFL_TYPE_GRABBER: @@ -1300,9 +1349,17 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip, memcpy(&dip->devbase,&vdev_template,sizeof(vdev_template)); dip->devbase.release = pvr2_video_device_release; + dip->devbase.ioctl_ops = &pvr2_ioctl_ops; + { + int val; + pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw, + PVR2_CID_STDAVAIL), &val); + dip->devbase.tvnorms = (v4l2_std_id)val; + } mindevnum = -1; - unit_number = pvr2_hdw_get_unit_number(vp->channel.mc_head->hdw); + unit_number = pvr2_hdw_get_unit_number(hdw); if (nr_ptr && (unit_number >= 0) && (unit_number < PVR_NUM)) { mindevnum = nr_ptr[unit_number]; } @@ -1319,7 +1376,7 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip, video_device_node_name(&dip->devbase), pvr2_config_get_name(dip->config)); - pvr2_hdw_v4l_store_minor_number(vp->channel.mc_head->hdw, + pvr2_hdw_v4l_store_minor_number(hdw, dip->minor_type,dip->devbase.minor); } diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c index 122fbd0081eb..ec4e2ef54e65 100644 --- a/drivers/media/video/pwc/pwc-if.c +++ b/drivers/media/video/pwc/pwc-if.c @@ -357,6 +357,7 @@ handler_end: PWC_ERROR("Error (%d) re-submitting urb in pwc_isoc_handler.\n", i); } +/* Both v4l2_lock and vb_queue_lock should be locked when calling this */ static int pwc_isoc_init(struct pwc_device *pdev) { struct usb_device *udev; @@ -366,9 +367,6 @@ static int pwc_isoc_init(struct pwc_device *pdev) struct usb_host_interface *idesc = NULL; int compression = 0; /* 0..3 = uncompressed..high */ - if (pdev->iso_init) - return 0; - pdev->vsync = 0; pdev->vlast_packet_size = 0; pdev->fill_buf = NULL; @@ -418,7 +416,6 @@ retry: urb = usb_alloc_urb(ISO_FRAMES_PER_DESC, GFP_KERNEL); if (urb == NULL) { PWC_ERROR("Failed to allocate urb %d\n", i); - pdev->iso_init = 1; pwc_isoc_cleanup(pdev); return -ENOMEM; } @@ -435,7 +432,6 @@ retry: &urb->transfer_dma); if (urb->transfer_buffer == NULL) { PWC_ERROR("Failed to allocate urb buffer %d\n", i); - pdev->iso_init = 1; pwc_isoc_cleanup(pdev); return -ENOMEM; } @@ -455,13 +451,11 @@ retry: ret = usb_submit_urb(pdev->urbs[i], GFP_KERNEL); if (ret == -ENOSPC && compression < 3) { compression++; - pdev->iso_init = 1; pwc_isoc_cleanup(pdev); goto retry; } if (ret) { PWC_ERROR("isoc_init() submit_urb %d failed with error %d\n", i, ret); - pdev->iso_init = 1; pwc_isoc_cleanup(pdev); return ret; } @@ -469,7 +463,6 @@ retry: } /* All is done... */ - pdev->iso_init = 1; PWC_DEBUG_OPEN("<< pwc_isoc_init()\n"); return 0; } @@ -507,21 +500,19 @@ static void pwc_iso_free(struct pwc_device *pdev) } } +/* Both v4l2_lock and vb_queue_lock should be locked when calling this */ static void pwc_isoc_cleanup(struct pwc_device *pdev) { PWC_DEBUG_OPEN(">> pwc_isoc_cleanup()\n"); - if (pdev->iso_init == 0) - return; - pwc_iso_stop(pdev); pwc_iso_free(pdev); usb_set_interface(pdev->udev, 0, 0); - pdev->iso_init = 0; PWC_DEBUG_OPEN("<< pwc_isoc_cleanup()\n"); } +/* Must be called with vb_queue_lock hold */ static void pwc_cleanup_queued_bufs(struct pwc_device *pdev) { unsigned long flags = 0; @@ -573,18 +564,13 @@ static const char *pwc_sensor_type_to_string(unsigned int sensor_type) int pwc_test_n_set_capt_file(struct pwc_device *pdev, struct file *file) { - int r = 0; - - mutex_lock(&pdev->capt_file_lock); if (pdev->capt_file != NULL && - pdev->capt_file != file) { - r = -EBUSY; - goto leave; - } + pdev->capt_file != file) + return -EBUSY; + pdev->capt_file = file; -leave: - mutex_unlock(&pdev->capt_file_lock); - return r; + + return 0; } static void pwc_video_release(struct v4l2_device *v) @@ -592,6 +578,7 @@ static void pwc_video_release(struct v4l2_device *v) struct pwc_device *pdev = container_of(v, struct pwc_device, v4l2_dev); v4l2_ctrl_handler_free(&pdev->ctrl_handler); + v4l2_device_unregister(&pdev->v4l2_dev); kfree(pdev->ctrl_buf); kfree(pdev); } @@ -600,10 +587,25 @@ static int pwc_video_close(struct file *file) { struct pwc_device *pdev = video_drvdata(file); + /* + * If we're still streaming vb2_queue_release will call stream_stop + * so we must take both the v4l2_lock and the vb_queue_lock. + */ + if (mutex_lock_interruptible(&pdev->v4l2_lock)) + return -ERESTARTSYS; + if (mutex_lock_interruptible(&pdev->vb_queue_lock)) { + mutex_unlock(&pdev->v4l2_lock); + return -ERESTARTSYS; + } + if (pdev->capt_file == file) { vb2_queue_release(&pdev->vb_queue); pdev->capt_file = NULL; } + + mutex_unlock(&pdev->vb_queue_lock); + mutex_unlock(&pdev->v4l2_lock); + return v4l2_fh_release(file); } @@ -611,35 +613,81 @@ static ssize_t pwc_video_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { struct pwc_device *pdev = video_drvdata(file); + int lock_v4l2 = 0; + ssize_t ret; - if (!pdev->udev) - return -ENODEV; + if (mutex_lock_interruptible(&pdev->vb_queue_lock)) + return -ERESTARTSYS; - if (pwc_test_n_set_capt_file(pdev, file)) - return -EBUSY; + ret = pwc_test_n_set_capt_file(pdev, file); + if (ret) + goto out; - return vb2_read(&pdev->vb_queue, buf, count, ppos, - file->f_flags & O_NONBLOCK); + /* stream_start will get called so we must take the v4l2_lock */ + if (pdev->vb_queue.fileio == NULL) + lock_v4l2 = 1; + + /* Use try_lock, since we're taking the locks in the *wrong* order! */ + if (lock_v4l2 && !mutex_trylock(&pdev->v4l2_lock)) { + ret = -ERESTARTSYS; + goto out; + } + ret = vb2_read(&pdev->vb_queue, buf, count, ppos, + file->f_flags & O_NONBLOCK); + if (lock_v4l2) + mutex_unlock(&pdev->v4l2_lock); +out: + mutex_unlock(&pdev->vb_queue_lock); + return ret; } static unsigned int pwc_video_poll(struct file *file, poll_table *wait) { struct pwc_device *pdev = video_drvdata(file); + struct vb2_queue *q = &pdev->vb_queue; + unsigned long req_events = poll_requested_events(wait); + unsigned int ret = POLL_ERR; + int lock_v4l2 = 0; - if (!pdev->udev) + if (mutex_lock_interruptible(&pdev->vb_queue_lock)) return POLL_ERR; - return vb2_poll(&pdev->vb_queue, file, wait); + /* Will this start fileio and thus call start_stream? */ + if ((req_events & (POLLIN | POLLRDNORM)) && + q->num_buffers == 0 && !q->streaming && q->fileio == NULL) { + if (pwc_test_n_set_capt_file(pdev, file)) + goto out; + lock_v4l2 = 1; + } + + /* Use try_lock, since we're taking the locks in the *wrong* order! */ + if (lock_v4l2 && !mutex_trylock(&pdev->v4l2_lock)) + goto out; + ret = vb2_poll(&pdev->vb_queue, file, wait); + if (lock_v4l2) + mutex_unlock(&pdev->v4l2_lock); + +out: + if (!pdev->udev) + ret |= POLLHUP; + mutex_unlock(&pdev->vb_queue_lock); + return ret; } static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma) { struct pwc_device *pdev = video_drvdata(file); + int ret; - if (pdev->capt_file != file) - return -EBUSY; + if (mutex_lock_interruptible(&pdev->vb_queue_lock)) + return -ERESTARTSYS; - return vb2_mmap(&pdev->vb_queue, vma); + ret = pwc_test_n_set_capt_file(pdev, file); + if (ret == 0) + ret = vb2_mmap(&pdev->vb_queue, vma); + + mutex_unlock(&pdev->vb_queue_lock); + return ret; } /***************************************************************************/ @@ -715,12 +763,14 @@ static void buffer_queue(struct vb2_buffer *vb) struct pwc_frame_buf *buf = container_of(vb, struct pwc_frame_buf, vb); unsigned long flags = 0; - spin_lock_irqsave(&pdev->queued_bufs_lock, flags); /* Check the device has not disconnected between prep and queuing */ - if (pdev->udev) - list_add_tail(&buf->list, &pdev->queued_bufs); - else + if (!pdev->udev) { vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + return; + } + + spin_lock_irqsave(&pdev->queued_bufs_lock, flags); + list_add_tail(&buf->list, &pdev->queued_bufs); spin_unlock_irqrestore(&pdev->queued_bufs_lock, flags); } @@ -729,11 +779,8 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count) struct pwc_device *pdev = vb2_get_drv_priv(vq); int r; - mutex_lock(&pdev->udevlock); - if (!pdev->udev) { - r = -ENODEV; - goto leave; - } + if (!pdev->udev) + return -ENODEV; /* Turn on camera and set LEDS on */ pwc_camera_power(pdev, 1); @@ -747,8 +794,7 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count) /* And cleanup any queued bufs!! */ pwc_cleanup_queued_bufs(pdev); } -leave: - mutex_unlock(&pdev->udevlock); + return r; } @@ -756,19 +802,29 @@ static int stop_streaming(struct vb2_queue *vq) { struct pwc_device *pdev = vb2_get_drv_priv(vq); - mutex_lock(&pdev->udevlock); if (pdev->udev) { pwc_set_leds(pdev, 0, 0); pwc_camera_power(pdev, 0); pwc_isoc_cleanup(pdev); } - mutex_unlock(&pdev->udevlock); pwc_cleanup_queued_bufs(pdev); return 0; } +static void wait_prepare(struct vb2_queue *vq) +{ + struct pwc_device *pdev = vb2_get_drv_priv(vq); + mutex_unlock(&pdev->vb_queue_lock); +} + +static void wait_finish(struct vb2_queue *vq) +{ + struct pwc_device *pdev = vb2_get_drv_priv(vq); + mutex_lock(&pdev->vb_queue_lock); +} + static struct vb2_ops pwc_vb_queue_ops = { .queue_setup = queue_setup, .buf_init = buffer_init, @@ -778,6 +834,8 @@ static struct vb2_ops pwc_vb_queue_ops = { .buf_queue = buffer_queue, .start_streaming = start_streaming, .stop_streaming = stop_streaming, + .wait_prepare = wait_prepare, + .wait_finish = wait_finish, }; /***************************************************************************/ @@ -1057,8 +1115,8 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id pdev->features = features; pwc_construct(pdev); /* set min/max sizes correct */ - mutex_init(&pdev->capt_file_lock); - mutex_init(&pdev->udevlock); + mutex_init(&pdev->v4l2_lock); + mutex_init(&pdev->vb_queue_lock); spin_lock_init(&pdev->queued_bufs_lock); INIT_LIST_HEAD(&pdev->queued_bufs); @@ -1130,6 +1188,16 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id pdev->v4l2_dev.ctrl_handler = &pdev->ctrl_handler; pdev->vdev.v4l2_dev = &pdev->v4l2_dev; + pdev->vdev.lock = &pdev->v4l2_lock; + + /* + * Don't take v4l2_lock for these ioctls. This improves latency if + * v4l2_lock is taken for a long time, e.g. when changing a control + * value, and a new frame is ready to be dequeued. + */ + v4l2_disable_ioctl_locking(&pdev->vdev, VIDIOC_DQBUF); + v4l2_disable_ioctl_locking(&pdev->vdev, VIDIOC_QBUF); + v4l2_disable_ioctl_locking(&pdev->vdev, VIDIOC_QUERYBUF); rc = video_register_device(&pdev->vdev, VFL_TYPE_GRABBER, -1); if (rc < 0) { @@ -1185,16 +1253,20 @@ static void usb_pwc_disconnect(struct usb_interface *intf) struct v4l2_device *v = usb_get_intfdata(intf); struct pwc_device *pdev = container_of(v, struct pwc_device, v4l2_dev); - mutex_lock(&pdev->udevlock); + mutex_lock(&pdev->v4l2_lock); + + mutex_lock(&pdev->vb_queue_lock); /* No need to keep the urbs around after disconnection */ - pwc_isoc_cleanup(pdev); + if (pdev->vb_queue.streaming) + pwc_isoc_cleanup(pdev); pdev->udev = NULL; - mutex_unlock(&pdev->udevlock); - pwc_cleanup_queued_bufs(pdev); + mutex_unlock(&pdev->vb_queue_lock); + v4l2_device_disconnect(&pdev->v4l2_dev); video_unregister_device(&pdev->vdev); - v4l2_device_unregister(&pdev->v4l2_dev); + + mutex_unlock(&pdev->v4l2_lock); #ifdef CONFIG_USB_PWC_INPUT_EVDEV if (pdev->button_dev) @@ -1229,15 +1301,4 @@ MODULE_LICENSE("GPL"); MODULE_ALIAS("pwcx"); MODULE_VERSION( PWC_VERSION ); -static int __init usb_pwc_init(void) -{ - return usb_register(&pwc_driver); -} - -static void __exit usb_pwc_exit(void) -{ - usb_deregister(&pwc_driver); -} - -module_init(usb_pwc_init); -module_exit(usb_pwc_exit); +module_usb_driver(pwc_driver); diff --git a/drivers/media/video/pwc/pwc-v4l.c b/drivers/media/video/pwc/pwc-v4l.c index 2834e3e65b39..c691e29cc36e 100644 --- a/drivers/media/video/pwc/pwc-v4l.c +++ b/drivers/media/video/pwc/pwc-v4l.c @@ -464,26 +464,24 @@ static int pwc_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) struct pwc_device *pdev = video_drvdata(file); int ret, pixelformat, compression = 0; - if (pwc_test_n_set_capt_file(pdev, file)) - return -EBUSY; - ret = pwc_vidioc_try_fmt(pdev, f); if (ret < 0) return ret; - pixelformat = f->fmt.pix.pixelformat; + if (mutex_lock_interruptible(&pdev->vb_queue_lock)) + return -ERESTARTSYS; - mutex_lock(&pdev->udevlock); - if (!pdev->udev) { - ret = -ENODEV; + ret = pwc_test_n_set_capt_file(pdev, file); + if (ret) goto leave; - } - if (pdev->iso_init) { + if (pdev->vb_queue.streaming) { ret = -EBUSY; goto leave; } + pixelformat = f->fmt.pix.pixelformat; + PWC_DEBUG_IOCTL("Trying to set format to: width=%d height=%d fps=%d " "format=%c%c%c%c\n", f->fmt.pix.width, f->fmt.pix.height, pdev->vframes, @@ -499,7 +497,7 @@ static int pwc_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) pwc_vidioc_fill_fmt(f, pdev->width, pdev->height, pdev->pixfmt); leave: - mutex_unlock(&pdev->udevlock); + mutex_unlock(&pdev->vb_queue_lock); return ret; } @@ -507,9 +505,6 @@ static int pwc_querycap(struct file *file, void *fh, struct v4l2_capability *cap { struct pwc_device *pdev = video_drvdata(file); - if (!pdev->udev) - return -ENODEV; - strcpy(cap->driver, PWC_NAME); strlcpy(cap->card, pdev->vdev.name, sizeof(cap->card)); usb_make_path(pdev->udev, cap->bus_info, sizeof(cap->bus_info)); @@ -540,15 +535,12 @@ static int pwc_s_input(struct file *file, void *fh, unsigned int i) return i ? -EINVAL : 0; } -static int pwc_g_volatile_ctrl_unlocked(struct v4l2_ctrl *ctrl) +static int pwc_g_volatile_ctrl(struct v4l2_ctrl *ctrl) { struct pwc_device *pdev = container_of(ctrl->handler, struct pwc_device, ctrl_handler); int ret = 0; - if (!pdev->udev) - return -ENODEV; - switch (ctrl->id) { case V4L2_CID_AUTO_WHITE_BALANCE: if (pdev->color_bal_valid && @@ -615,18 +607,6 @@ static int pwc_g_volatile_ctrl_unlocked(struct v4l2_ctrl *ctrl) return ret; } -static int pwc_g_volatile_ctrl(struct v4l2_ctrl *ctrl) -{ - struct pwc_device *pdev = - container_of(ctrl->handler, struct pwc_device, ctrl_handler); - int ret; - - mutex_lock(&pdev->udevlock); - ret = pwc_g_volatile_ctrl_unlocked(ctrl); - mutex_unlock(&pdev->udevlock); - return ret; -} - static int pwc_set_awb(struct pwc_device *pdev) { int ret; @@ -648,7 +628,7 @@ static int pwc_set_awb(struct pwc_device *pdev) if (pdev->auto_white_balance->val == awb_indoor || pdev->auto_white_balance->val == awb_outdoor || pdev->auto_white_balance->val == awb_fl) - pwc_g_volatile_ctrl_unlocked(pdev->auto_white_balance); + pwc_g_volatile_ctrl(pdev->auto_white_balance); } if (pdev->auto_white_balance->val != awb_manual) return 0; @@ -812,13 +792,6 @@ static int pwc_s_ctrl(struct v4l2_ctrl *ctrl) container_of(ctrl->handler, struct pwc_device, ctrl_handler); int ret = 0; - mutex_lock(&pdev->udevlock); - - if (!pdev->udev) { - ret = -ENODEV; - goto leave; - } - switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, @@ -915,8 +888,6 @@ static int pwc_s_ctrl(struct v4l2_ctrl *ctrl) if (ret) PWC_ERROR("s_ctrl %s error %d\n", ctrl->name, ret); -leave: - mutex_unlock(&pdev->udevlock); return ret; } @@ -949,11 +920,9 @@ static int pwc_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - mutex_lock(&pdev->udevlock); /* To avoid race with s_fmt */ PWC_DEBUG_IOCTL("ioctl(VIDIOC_G_FMT) return size %dx%d\n", pdev->width, pdev->height); pwc_vidioc_fill_fmt(f, pdev->width, pdev->height, pdev->pixfmt); - mutex_unlock(&pdev->udevlock); return 0; } @@ -968,70 +937,98 @@ static int pwc_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *rb) { struct pwc_device *pdev = video_drvdata(file); + int ret; - if (pwc_test_n_set_capt_file(pdev, file)) - return -EBUSY; + if (mutex_lock_interruptible(&pdev->vb_queue_lock)) + return -ERESTARTSYS; - return vb2_reqbufs(&pdev->vb_queue, rb); + ret = pwc_test_n_set_capt_file(pdev, file); + if (ret == 0) + ret = vb2_reqbufs(&pdev->vb_queue, rb); + + mutex_unlock(&pdev->vb_queue_lock); + return ret; } static int pwc_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf) { struct pwc_device *pdev = video_drvdata(file); + int ret; - return vb2_querybuf(&pdev->vb_queue, buf); + if (mutex_lock_interruptible(&pdev->vb_queue_lock)) + return -ERESTARTSYS; + + ret = pwc_test_n_set_capt_file(pdev, file); + if (ret == 0) + ret = vb2_querybuf(&pdev->vb_queue, buf); + + mutex_unlock(&pdev->vb_queue_lock); + return ret; } static int pwc_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf) { struct pwc_device *pdev = video_drvdata(file); + int ret; - if (!pdev->udev) - return -ENODEV; + if (mutex_lock_interruptible(&pdev->vb_queue_lock)) + return -ERESTARTSYS; - if (pdev->capt_file != file) - return -EBUSY; + ret = pwc_test_n_set_capt_file(pdev, file); + if (ret == 0) + ret = vb2_qbuf(&pdev->vb_queue, buf); - return vb2_qbuf(&pdev->vb_queue, buf); + mutex_unlock(&pdev->vb_queue_lock); + return ret; } static int pwc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) { struct pwc_device *pdev = video_drvdata(file); + int ret; - if (!pdev->udev) - return -ENODEV; + if (mutex_lock_interruptible(&pdev->vb_queue_lock)) + return -ERESTARTSYS; - if (pdev->capt_file != file) - return -EBUSY; + ret = pwc_test_n_set_capt_file(pdev, file); + if (ret == 0) + ret = vb2_dqbuf(&pdev->vb_queue, buf, + file->f_flags & O_NONBLOCK); - return vb2_dqbuf(&pdev->vb_queue, buf, file->f_flags & O_NONBLOCK); + mutex_unlock(&pdev->vb_queue_lock); + return ret; } static int pwc_streamon(struct file *file, void *fh, enum v4l2_buf_type i) { struct pwc_device *pdev = video_drvdata(file); + int ret; - if (!pdev->udev) - return -ENODEV; + if (mutex_lock_interruptible(&pdev->vb_queue_lock)) + return -ERESTARTSYS; - if (pdev->capt_file != file) - return -EBUSY; + ret = pwc_test_n_set_capt_file(pdev, file); + if (ret == 0) + ret = vb2_streamon(&pdev->vb_queue, i); - return vb2_streamon(&pdev->vb_queue, i); + mutex_unlock(&pdev->vb_queue_lock); + return ret; } static int pwc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i) { struct pwc_device *pdev = video_drvdata(file); + int ret; - if (!pdev->udev) - return -ENODEV; + if (mutex_lock_interruptible(&pdev->vb_queue_lock)) + return -ERESTARTSYS; - if (pdev->capt_file != file) - return -EBUSY; + ret = pwc_test_n_set_capt_file(pdev, file); + if (ret == 0) + ret = vb2_streamoff(&pdev->vb_queue, i); - return vb2_streamoff(&pdev->vb_queue, i); + mutex_unlock(&pdev->vb_queue_lock); + return ret; } static int pwc_enum_framesizes(struct file *file, void *fh, @@ -1119,19 +1116,17 @@ static int pwc_s_parm(struct file *file, void *fh, parm->parm.capture.timeperframe.numerator == 0) return -EINVAL; - if (pwc_test_n_set_capt_file(pdev, file)) - return -EBUSY; - fps = parm->parm.capture.timeperframe.denominator / parm->parm.capture.timeperframe.numerator; - mutex_lock(&pdev->udevlock); - if (!pdev->udev) { - ret = -ENODEV; + if (mutex_lock_interruptible(&pdev->vb_queue_lock)) + return -ERESTARTSYS; + + ret = pwc_test_n_set_capt_file(pdev, file); + if (ret) goto leave; - } - if (pdev->iso_init) { + if (pdev->vb_queue.streaming) { ret = -EBUSY; goto leave; } @@ -1142,7 +1137,7 @@ static int pwc_s_parm(struct file *file, void *fh, pwc_g_parm(file, fh, parm); leave: - mutex_unlock(&pdev->udevlock); + mutex_unlock(&pdev->vb_queue_lock); return ret; } @@ -1166,4 +1161,6 @@ const struct v4l2_ioctl_ops pwc_ioctl_ops = { .vidioc_enum_frameintervals = pwc_enum_frameintervals, .vidioc_g_parm = pwc_g_parm, .vidioc_s_parm = pwc_s_parm, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; diff --git a/drivers/media/video/pwc/pwc.h b/drivers/media/video/pwc/pwc.h index e4d4d711dd1f..d6b5b216b9d6 100644 --- a/drivers/media/video/pwc/pwc.h +++ b/drivers/media/video/pwc/pwc.h @@ -221,9 +221,17 @@ struct pwc_device struct video_device vdev; struct v4l2_device v4l2_dev; - /* Pointer to our usb_device, may be NULL after unplug */ - struct usb_device *udev; - struct mutex udevlock; + /* videobuf2 queue and queued buffers list */ + struct vb2_queue vb_queue; + struct list_head queued_bufs; + spinlock_t queued_bufs_lock; /* Protects queued_bufs */ + + /* Note if taking both locks v4l2_lock must always be locked first! */ + struct mutex v4l2_lock; /* Protects everything else */ + struct mutex vb_queue_lock; /* Protects vb_queue and capt_file */ + + /* Pointer to our usb_device, will be NULL after unplug */ + struct usb_device *udev; /* Both mutexes most be hold when setting! */ /* type of cam (645, 646, 675, 680, 690, 720, 730, 740, 750) */ int type; @@ -232,7 +240,6 @@ struct pwc_device /*** Video data ***/ struct file *capt_file; /* file doing video capture */ - struct mutex capt_file_lock; int vendpoint; /* video isoc endpoint */ int vcinterface; /* video control interface */ int valternate; /* alternate interface needed */ @@ -251,12 +258,6 @@ struct pwc_device unsigned char *ctrl_buf; struct urb *urbs[MAX_ISO_BUFS]; - char iso_init; - - /* videobuf2 queue and queued buffers list */ - struct vb2_queue vb_queue; - struct list_head queued_bufs; - spinlock_t queued_bufs_lock; /* * Frame currently being filled, this only gets touched by the diff --git a/drivers/media/video/s2255drv.c b/drivers/media/video/s2255drv.c index 4894cbb1c547..01c2179f0520 100644 --- a/drivers/media/video/s2255drv.c +++ b/drivers/media/video/s2255drv.c @@ -634,13 +634,11 @@ static void s2255_fillbuff(struct s2255_channel *channel, const char *tmpbuf; char *vbuf = videobuf_to_vmalloc(&buf->vb); unsigned long last_frame; - struct s2255_framei *frm; if (!vbuf) return; last_frame = channel->last_frame; if (last_frame != -1) { - frm = &channel->buffer.frame[last_frame]; tmpbuf = (const char *)channel->buffer.frame[last_frame].lpvbits; switch (buf->fmt->fourcc) { @@ -987,7 +985,6 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct videobuf_queue *q = &fh->vb_vidq; struct s2255_mode mode; int ret; - int norm; ret = vidioc_try_fmt_vid_cap(file, fh, f); @@ -1018,7 +1015,6 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, channel->height = f->fmt.pix.height; fh->vb_vidq.field = f->fmt.pix.field; fh->type = f->type; - norm = norm_minw(&channel->vdev); if (channel->width > norm_minw(&channel->vdev)) { if (channel->height > norm_minh(&channel->vdev)) { if (channel->cap_parm.capturemode & @@ -1826,8 +1822,7 @@ static void s2255_destroy(struct s2255_dev *dev) usb_free_urb(dev->fw_data->fw_urb); dev->fw_data->fw_urb = NULL; } - if (dev->fw_data->fw) - release_firmware(dev->fw_data->fw); + release_firmware(dev->fw_data->fw); kfree(dev->fw_data->pfw_data); kfree(dev->fw_data); /* reset the DSP so firmware can be reloaded next time */ @@ -1949,6 +1944,10 @@ static int s2255_probe_v4l(struct s2255_dev *dev) /* register 4 video devices */ channel->vdev = template; channel->vdev.lock = &dev->lock; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &channel->vdev.flags); channel->vdev.v4l2_dev = &dev->v4l2_dev; video_set_drvdata(&channel->vdev, channel); if (video_nr == -1) diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c index 7e9b2c612b03..be5e4e237297 100644 --- a/drivers/media/video/s5p-fimc/fimc-capture.c +++ b/drivers/media/video/s5p-fimc/fimc-capture.c @@ -83,7 +83,9 @@ static int fimc_capture_state_cleanup(struct fimc_dev *fimc, bool suspend) fimc->state &= ~(1 << ST_CAPT_RUN | 1 << ST_CAPT_SHUT | 1 << ST_CAPT_STREAM | 1 << ST_CAPT_ISP_STREAM); - if (!suspend) + if (suspend) + fimc->state |= (1 << ST_CAPT_SUSPENDED); + else fimc->state &= ~(1 << ST_CAPT_PEND | 1 << ST_CAPT_SUSPENDED); /* Release unused buffers */ @@ -99,7 +101,6 @@ static int fimc_capture_state_cleanup(struct fimc_dev *fimc, bool suspend) else vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); } - set_bit(ST_CAPT_SUSPENDED, &fimc->state); fimc_hw_reset(fimc); cap->buf_index = 0; @@ -146,21 +147,22 @@ int fimc_capture_config_update(struct fimc_ctx *ctx) if (!test_bit(ST_CAPT_APPLY_CFG, &fimc->state)) return 0; - spin_lock(&ctx->slock); fimc_hw_set_camera_offset(fimc, &ctx->s_frame); + ret = fimc_set_scaler_info(ctx); - if (ret == 0) { - fimc_hw_set_prescaler(ctx); - fimc_hw_set_mainscaler(ctx); - fimc_hw_set_target_format(ctx); - fimc_hw_set_rotation(ctx); - fimc_prepare_dma_offset(ctx, &ctx->d_frame); - fimc_hw_set_out_dma(ctx); - if (fimc->variant->has_alpha) - fimc_hw_set_rgb_alpha(ctx); - clear_bit(ST_CAPT_APPLY_CFG, &fimc->state); - } - spin_unlock(&ctx->slock); + if (ret) + return ret; + + fimc_hw_set_prescaler(ctx); + fimc_hw_set_mainscaler(ctx); + fimc_hw_set_target_format(ctx); + fimc_hw_set_rotation(ctx); + fimc_prepare_dma_offset(ctx, &ctx->d_frame); + fimc_hw_set_out_dma(ctx); + if (fimc->variant->has_alpha) + fimc_hw_set_rgb_alpha(ctx); + + clear_bit(ST_CAPT_APPLY_CFG, &fimc->state); return ret; } @@ -1523,6 +1525,10 @@ int fimc_register_capture_device(struct fimc_dev *fimc, vfd->minor = -1; vfd->release = video_device_release; vfd->lock = &fimc->lock; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags); video_set_drvdata(vfd, fimc); vid_cap = &fimc->vid_cap; @@ -1533,7 +1539,6 @@ int fimc_register_capture_device(struct fimc_dev *fimc, INIT_LIST_HEAD(&vid_cap->pending_buf_q); INIT_LIST_HEAD(&vid_cap->active_buf_q); - spin_lock_init(&ctx->slock); vid_cap->ctx = ctx; q = &fimc->vid_cap.vbq; diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c index e09ba7b0076e..f6b9060a0c0f 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.c +++ b/drivers/media/video/s5p-fimc/fimc-core.c @@ -320,7 +320,7 @@ static int fimc_m2m_shutdown(struct fimc_ctx *ctx) if (!fimc_m2m_pending(fimc)) return 0; - fimc_ctx_state_lock_set(FIMC_CTX_SHUT, ctx); + fimc_ctx_state_set(FIMC_CTX_SHUT, ctx); ret = wait_event_timeout(fimc->irq_queue, !fimc_ctx_state_is_set(FIMC_CTX_SHUT, ctx), @@ -430,14 +430,12 @@ static irqreturn_t fimc_irq_handler(int irq, void *priv) spin_unlock(&fimc->slock); fimc_m2m_job_finish(ctx, VB2_BUF_STATE_DONE); - spin_lock(&ctx->slock); if (ctx->state & FIMC_CTX_SHUT) { ctx->state &= ~FIMC_CTX_SHUT; wake_up(&fimc->irq_queue); } - spin_unlock(&ctx->slock); + return IRQ_HANDLED; } - return IRQ_HANDLED; } else if (test_bit(ST_CAPT_PEND, &fimc->state)) { fimc_capture_irq_handler(fimc, !test_bit(ST_CAPT_JPEG, &fimc->state)); @@ -584,55 +582,11 @@ void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f) f->fmt->color, f->dma_offset.y_h, f->dma_offset.y_v); } -/** - * fimc_prepare_config - check dimensions, operation and color mode - * and pre-calculate offset and the scaling coefficients. - * - * @ctx: hardware context information - * @flags: flags indicating which parameters to check/update - * - * Return: 0 if dimensions are valid or non zero otherwise. - */ -int fimc_prepare_config(struct fimc_ctx *ctx, u32 flags) -{ - struct fimc_frame *s_frame, *d_frame; - struct vb2_buffer *vb = NULL; - int ret = 0; - - s_frame = &ctx->s_frame; - d_frame = &ctx->d_frame; - - if (flags & FIMC_PARAMS) { - /* Prepare the DMA offset ratios for scaler. */ - fimc_prepare_dma_offset(ctx, &ctx->s_frame); - fimc_prepare_dma_offset(ctx, &ctx->d_frame); - - if (s_frame->height > (SCALER_MAX_VRATIO * d_frame->height) || - s_frame->width > (SCALER_MAX_HRATIO * d_frame->width)) { - err("out of scaler range"); - return -EINVAL; - } - fimc_set_yuv_order(ctx); - } - - if (flags & FIMC_SRC_ADDR) { - vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx); - ret = fimc_prepare_addr(ctx, vb, s_frame, &s_frame->paddr); - if (ret) - return ret; - } - - if (flags & FIMC_DST_ADDR) { - vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); - ret = fimc_prepare_addr(ctx, vb, d_frame, &d_frame->paddr); - } - - return ret; -} - static void fimc_dma_run(void *priv) { + struct vb2_buffer *vb = NULL; struct fimc_ctx *ctx = priv; + struct fimc_frame *sf, *df; struct fimc_dev *fimc; unsigned long flags; u32 ret; @@ -643,10 +597,22 @@ static void fimc_dma_run(void *priv) fimc = ctx->fimc_dev; spin_lock_irqsave(&fimc->slock, flags); set_bit(ST_M2M_PEND, &fimc->state); + sf = &ctx->s_frame; + df = &ctx->d_frame; + + if (ctx->state & FIMC_PARAMS) { + /* Prepare the DMA offsets for scaler */ + fimc_prepare_dma_offset(ctx, sf); + fimc_prepare_dma_offset(ctx, df); + } + + vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx); + ret = fimc_prepare_addr(ctx, vb, sf, &sf->paddr); + if (ret) + goto dma_unlock; - spin_lock(&ctx->slock); - ctx->state |= (FIMC_SRC_ADDR | FIMC_DST_ADDR); - ret = fimc_prepare_config(ctx, ctx->state); + vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); + ret = fimc_prepare_addr(ctx, vb, df, &df->paddr); if (ret) goto dma_unlock; @@ -655,32 +621,26 @@ static void fimc_dma_run(void *priv) ctx->state |= FIMC_PARAMS; fimc->m2m.ctx = ctx; } - fimc_hw_set_input_addr(fimc, &ctx->s_frame.paddr); if (ctx->state & FIMC_PARAMS) { + fimc_set_yuv_order(ctx); fimc_hw_set_input_path(ctx); fimc_hw_set_in_dma(ctx); ret = fimc_set_scaler_info(ctx); - if (ret) { - spin_unlock(&fimc->slock); + if (ret) goto dma_unlock; - } fimc_hw_set_prescaler(ctx); fimc_hw_set_mainscaler(ctx); fimc_hw_set_target_format(ctx); fimc_hw_set_rotation(ctx); fimc_hw_set_effect(ctx, false); - } - - fimc_hw_set_output_path(ctx); - if (ctx->state & (FIMC_DST_ADDR | FIMC_PARAMS)) - fimc_hw_set_output_addr(fimc, &ctx->d_frame.paddr, -1); - - if (ctx->state & FIMC_PARAMS) { fimc_hw_set_out_dma(ctx); if (fimc->variant->has_alpha) fimc_hw_set_rgb_alpha(ctx); + fimc_hw_set_output_path(ctx); } + fimc_hw_set_input_addr(fimc, &sf->paddr); + fimc_hw_set_output_addr(fimc, &df->paddr, -1); fimc_activate_capture(ctx); @@ -688,7 +648,6 @@ static void fimc_dma_run(void *priv) FIMC_SRC_FMT | FIMC_DST_FMT); fimc_hw_activate_input_dma(fimc, true); dma_unlock: - spin_unlock(&ctx->slock); spin_unlock_irqrestore(&fimc->slock, flags); } @@ -827,9 +786,9 @@ static int fimc_s_ctrl(struct v4l2_ctrl *ctrl) unsigned long flags; int ret; - spin_lock_irqsave(&ctx->slock, flags); + spin_lock_irqsave(&ctx->fimc_dev->slock, flags); ret = __fimc_s_ctrl(ctx, ctrl); - spin_unlock_irqrestore(&ctx->slock, flags); + spin_unlock_irqrestore(&ctx->fimc_dev->slock, flags); return ret; } @@ -1174,9 +1133,9 @@ static int fimc_m2m_s_fmt_mplane(struct file *file, void *fh, ctx->scaler.enabled = 1; if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) - fimc_ctx_state_lock_set(FIMC_PARAMS | FIMC_DST_FMT, ctx); + fimc_ctx_state_set(FIMC_PARAMS | FIMC_DST_FMT, ctx); else - fimc_ctx_state_lock_set(FIMC_PARAMS | FIMC_SRC_FMT, ctx); + fimc_ctx_state_set(FIMC_PARAMS | FIMC_SRC_FMT, ctx); dbg("f_w: %d, f_h: %d", frame->f_width, frame->f_height); @@ -1363,7 +1322,7 @@ static int fimc_m2m_s_crop(struct file *file, void *fh, struct v4l2_crop *cr) f->width = cr->c.width; f->height = cr->c.height; - fimc_ctx_state_lock_set(FIMC_PARAMS, ctx); + fimc_ctx_state_set(FIMC_PARAMS, ctx); return 0; } @@ -1467,7 +1426,6 @@ static int fimc_m2m_open(struct file *file) ctx->flags = 0; ctx->in_path = FIMC_DMA; ctx->out_path = FIMC_DMA; - spin_lock_init(&ctx->slock); ctx->m2m_ctx = v4l2_m2m_ctx_init(fimc->m2m.m2m_dev, ctx, queue_init); if (IS_ERR(ctx->m2m_ctx)) { @@ -1562,6 +1520,10 @@ int fimc_register_m2m_device(struct fimc_dev *fimc, vfd->minor = -1; vfd->release = video_device_release; vfd->lock = &fimc->lock; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags); snprintf(vfd->name, sizeof(vfd->name), "%s.m2m", dev_name(&pdev->dev)); video_set_drvdata(vfd, fimc); @@ -1601,7 +1563,7 @@ void fimc_unregister_m2m_device(struct fimc_dev *fimc) static void fimc_clk_put(struct fimc_dev *fimc) { int i; - for (i = 0; i < fimc->num_clocks; i++) { + for (i = 0; i < MAX_FIMC_CLOCKS; i++) { if (IS_ERR_OR_NULL(fimc->clock[i])) continue; clk_unprepare(fimc->clock[i]); @@ -1614,7 +1576,7 @@ static int fimc_clk_get(struct fimc_dev *fimc) { int i, ret; - for (i = 0; i < fimc->num_clocks; i++) { + for (i = 0; i < MAX_FIMC_CLOCKS; i++) { fimc->clock[i] = clk_get(&fimc->pdev->dev, fimc_clocks[i]); if (IS_ERR(fimc->clock[i])) goto err; @@ -1714,9 +1676,7 @@ static int fimc_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Failed to get IRQ resource\n"); return -ENXIO; } - fimc->irq = res->start; - fimc->num_clocks = MAX_FIMC_CLOCKS; ret = fimc_clk_get(fimc); if (ret) return ret; @@ -1725,7 +1685,7 @@ static int fimc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, fimc); - ret = devm_request_irq(&pdev->dev, fimc->irq, fimc_irq_handler, + ret = devm_request_irq(&pdev->dev, res->start, fimc_irq_handler, 0, pdev->name, fimc); if (ret) { dev_err(&pdev->dev, "failed to install irq (%d)\n", ret); @@ -2036,7 +1996,7 @@ static struct platform_driver fimc_driver = { int __init fimc_register_driver(void) { - return platform_driver_probe(&fimc_driver, fimc_probe); + return platform_driver_register(&fimc_driver); } void __exit fimc_unregister_driver(void) diff --git a/drivers/media/video/s5p-fimc/fimc-core.h b/drivers/media/video/s5p-fimc/fimc-core.h index 84fd83550bd7..7afabb0e1907 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.h +++ b/drivers/media/video/s5p-fimc/fimc-core.h @@ -119,8 +119,6 @@ enum fimc_color_fmt { /* The hardware context state. */ #define FIMC_PARAMS (1 << 0) -#define FIMC_SRC_ADDR (1 << 1) -#define FIMC_DST_ADDR (1 << 2) #define FIMC_SRC_FMT (1 << 3) #define FIMC_DST_FMT (1 << 4) #define FIMC_DST_CROP (1 << 5) @@ -431,10 +429,8 @@ struct fimc_ctx; * @pdata: pointer to the device platform data * @variant: the IP variant information * @id: FIMC device index (0..FIMC_MAX_DEVS) - * @num_clocks: the number of clocks managed by this device instance * @clock: clocks required for FIMC operation * @regs: the mapped hardware registers - * @irq: FIMC interrupt number * @irq_queue: interrupt handler waitqueue * @v4l2_dev: root v4l2_device * @m2m: memory-to-memory V4L2 device information @@ -450,10 +446,8 @@ struct fimc_dev { struct s5p_platform_fimc *pdata; struct samsung_fimc_variant *variant; u16 id; - u16 num_clocks; struct clk *clock[MAX_FIMC_CLOCKS]; void __iomem *regs; - int irq; wait_queue_head_t irq_queue; struct v4l2_device *v4l2_dev; struct fimc_m2m_device m2m; @@ -465,7 +459,6 @@ struct fimc_dev { /** * fimc_ctx - the device context data - * @slock: spinlock protecting this data structure * @s_frame: source frame properties * @d_frame: destination frame properties * @out_order_1p: output 1-plane YCBCR order @@ -492,7 +485,6 @@ struct fimc_dev { * @ctrls_rdy: true if the control handler is initialized */ struct fimc_ctx { - spinlock_t slock; struct fimc_frame s_frame; struct fimc_frame d_frame; u32 out_order_1p; @@ -560,13 +552,13 @@ static inline bool fimc_capture_active(struct fimc_dev *fimc) return ret; } -static inline void fimc_ctx_state_lock_set(u32 state, struct fimc_ctx *ctx) +static inline void fimc_ctx_state_set(u32 state, struct fimc_ctx *ctx) { unsigned long flags; - spin_lock_irqsave(&ctx->slock, flags); + spin_lock_irqsave(&ctx->fimc_dev->slock, flags); ctx->state |= state; - spin_unlock_irqrestore(&ctx->slock, flags); + spin_unlock_irqrestore(&ctx->fimc_dev->slock, flags); } static inline bool fimc_ctx_state_is_set(u32 mask, struct fimc_ctx *ctx) @@ -574,9 +566,9 @@ static inline bool fimc_ctx_state_is_set(u32 mask, struct fimc_ctx *ctx) unsigned long flags; bool ret; - spin_lock_irqsave(&ctx->slock, flags); + spin_lock_irqsave(&ctx->fimc_dev->slock, flags); ret = (ctx->state & mask) == mask; - spin_unlock_irqrestore(&ctx->slock, flags); + spin_unlock_irqrestore(&ctx->fimc_dev->slock, flags); return ret; } diff --git a/drivers/media/video/s5p-fimc/fimc-mdevice.c b/drivers/media/video/s5p-fimc/fimc-mdevice.c index 62ed37e40149..75296a625a9d 100644 --- a/drivers/media/video/s5p-fimc/fimc-mdevice.c +++ b/drivers/media/video/s5p-fimc/fimc-mdevice.c @@ -214,14 +214,20 @@ static struct v4l2_subdev *fimc_md_register_sensor(struct fimc_md *fmd, return NULL; adapter = i2c_get_adapter(s_info->pdata->i2c_bus_num); - if (!adapter) - return NULL; + if (!adapter) { + v4l2_warn(&fmd->v4l2_dev, + "Failed to get I2C adapter %d, deferring probe\n", + s_info->pdata->i2c_bus_num); + return ERR_PTR(-EPROBE_DEFER); + } sd = v4l2_i2c_new_subdev_board(&fmd->v4l2_dev, adapter, s_info->pdata->board_info, NULL); if (IS_ERR_OR_NULL(sd)) { i2c_put_adapter(adapter); - v4l2_err(&fmd->v4l2_dev, "Failed to acquire subdev\n"); - return NULL; + v4l2_warn(&fmd->v4l2_dev, + "Failed to acquire subdev %s, deferring probe\n", + s_info->pdata->board_info->type); + return ERR_PTR(-EPROBE_DEFER); } v4l2_set_subdev_hostdata(sd, s_info); sd->grp_id = SENSOR_GROUP_ID; @@ -269,13 +275,22 @@ static int fimc_md_register_sensor_entities(struct fimc_md *fmd) fmd->num_sensors = num_clients; for (i = 0; i < num_clients; i++) { + struct v4l2_subdev *sd; + fmd->sensor[i].pdata = &pdata->isp_info[i]; ret = __fimc_md_set_camclk(fmd, &fmd->sensor[i], true); if (ret) break; - fmd->sensor[i].subdev = - fimc_md_register_sensor(fmd, &fmd->sensor[i]); + sd = fimc_md_register_sensor(fmd, &fmd->sensor[i]); ret = __fimc_md_set_camclk(fmd, &fmd->sensor[i], false); + + if (!IS_ERR(sd)) { + fmd->sensor[i].subdev = sd; + } else { + fmd->sensor[i].subdev = NULL; + ret = PTR_ERR(sd); + break; + } if (ret) break; } @@ -336,22 +351,45 @@ static int csis_register_callback(struct device *dev, void *p) */ static int fimc_md_register_platform_entities(struct fimc_md *fmd) { + struct s5p_platform_fimc *pdata = fmd->pdev->dev.platform_data; struct device_driver *driver; - int ret; + int ret, i; driver = driver_find(FIMC_MODULE_NAME, &platform_bus_type); - if (!driver) - return -ENODEV; + if (!driver) { + v4l2_warn(&fmd->v4l2_dev, + "%s driver not found, deffering probe\n", + FIMC_MODULE_NAME); + return -EPROBE_DEFER; + } + ret = driver_for_each_device(driver, NULL, fmd, fimc_register_callback); if (ret) return ret; + /* + * Check if there is any sensor on the MIPI-CSI2 bus and + * if not skip the s5p-csis module loading. + */ + for (i = 0; i < pdata->num_clients; i++) { + if (pdata->isp_info[i].bus_type == FIMC_MIPI_CSI2) { + ret = 1; + break; + } + } + if (!ret) + return 0; driver = driver_find(CSIS_DRIVER_NAME, &platform_bus_type); - if (driver) - ret = driver_for_each_device(driver, NULL, fmd, - csis_register_callback); - return ret; + if (!driver || !try_module_get(driver->owner)) { + v4l2_warn(&fmd->v4l2_dev, + "%s driver not found, deffering probe\n", + CSIS_DRIVER_NAME); + return -EPROBE_DEFER; + } + + return driver_for_each_device(driver, NULL, fmd, + csis_register_callback); } static void fimc_md_unregister_entities(struct fimc_md *fmd) @@ -369,6 +407,7 @@ static void fimc_md_unregister_entities(struct fimc_md *fmd) if (fmd->csis[i].sd == NULL) continue; v4l2_device_unregister_subdev(fmd->csis[i].sd); + module_put(fmd->csis[i].sd->owner); fmd->csis[i].sd = NULL; } for (i = 0; i < fmd->num_sensors; i++) { @@ -744,7 +783,7 @@ static ssize_t fimc_md_sysfs_store(struct device *dev, static DEVICE_ATTR(subdev_conf_mode, S_IWUSR | S_IRUGO, fimc_md_sysfs_show, fimc_md_sysfs_store); -static int __devinit fimc_md_probe(struct platform_device *pdev) +static int fimc_md_probe(struct platform_device *pdev) { struct v4l2_device *v4l2_dev; struct fimc_md *fmd; @@ -841,10 +880,12 @@ static struct platform_driver fimc_md_driver = { int __init fimc_md_init(void) { int ret; + request_module("s5p-csis"); ret = fimc_register_driver(); if (ret) return ret; + return platform_driver_register(&fimc_md_driver); } void __exit fimc_md_exit(void) diff --git a/drivers/media/video/s5p-fimc/fimc-reg.c b/drivers/media/video/s5p-fimc/fimc-reg.c index 15466d0529c1..ff11f10fea0b 100644 --- a/drivers/media/video/s5p-fimc/fimc-reg.c +++ b/drivers/media/video/s5p-fimc/fimc-reg.c @@ -674,6 +674,7 @@ int fimc_hw_set_camera_type(struct fimc_dev *fimc, { u32 cfg, tmp; struct fimc_vid_cap *vid_cap = &fimc->vid_cap; + u32 csis_data_alignment = 32; cfg = readl(fimc->regs + S5P_CIGCTRL); @@ -703,7 +704,7 @@ int fimc_hw_set_camera_type(struct fimc_dev *fimc, vid_cap->mf.code); return -EINVAL; } - tmp |= (cam->csi_data_align == 32) << 8; + tmp |= (csis_data_alignment == 32) << 8; writel(tmp, fimc->regs + S5P_CSIIMGFMT); diff --git a/drivers/media/video/s5p-fimc/mipi-csis.c b/drivers/media/video/s5p-fimc/mipi-csis.c index f44f690397f7..2f73d9e3d0b7 100644 --- a/drivers/media/video/s5p-fimc/mipi-csis.c +++ b/drivers/media/video/s5p-fimc/mipi-csis.c @@ -127,20 +127,24 @@ struct csis_state { * multiple of 2^pix_width_alignment * @code: corresponding media bus code * @fmt_reg: S5PCSIS_CONFIG register value + * @data_alignment: MIPI-CSI data alignment in bits */ struct csis_pix_format { unsigned int pix_width_alignment; enum v4l2_mbus_pixelcode code; u32 fmt_reg; + u8 data_alignment; }; static const struct csis_pix_format s5pcsis_formats[] = { { .code = V4L2_MBUS_FMT_VYUY8_2X8, .fmt_reg = S5PCSIS_CFG_FMT_YCBCR422_8BIT, + .data_alignment = 32, }, { .code = V4L2_MBUS_FMT_JPEG_1X8, .fmt_reg = S5PCSIS_CFG_FMT_USER(1), + .data_alignment = 32, }, }; @@ -239,7 +243,7 @@ static void s5pcsis_set_params(struct csis_state *state) s5pcsis_set_hsync_settle(state, pdata->hs_settle); val = s5pcsis_read(state, S5PCSIS_CTRL); - if (pdata->alignment == 32) + if (state->csis_fmt->data_alignment == 32) val |= S5PCSIS_CTRL_ALIGN_32BIT; else /* 24-bits */ val &= ~S5PCSIS_CTRL_ALIGN_32BIT; @@ -711,19 +715,8 @@ static struct platform_driver s5pcsis_driver = { }, }; -static int __init s5pcsis_init(void) -{ - return platform_driver_probe(&s5pcsis_driver, s5pcsis_probe); -} - -static void __exit s5pcsis_exit(void) -{ - platform_driver_unregister(&s5pcsis_driver); -} - -module_init(s5pcsis_init); -module_exit(s5pcsis_exit); +module_platform_driver(s5pcsis_driver); MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>"); -MODULE_DESCRIPTION("S5P/EXYNOS4 MIPI CSI receiver driver"); +MODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC MIPI-CSI2 receiver driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/s5p-g2d/g2d.c b/drivers/media/video/s5p-g2d/g2d.c index 789de74014e5..02605cecfd65 100644 --- a/drivers/media/video/s5p-g2d/g2d.c +++ b/drivers/media/video/s5p-g2d/g2d.c @@ -762,6 +762,10 @@ static int g2d_probe(struct platform_device *pdev) goto unreg_v4l2_dev; } *vfd = g2d_videodev; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags); vfd->lock = &dev->mutex; ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); if (ret) { diff --git a/drivers/media/video/s5p-jpeg/jpeg-core.c b/drivers/media/video/s5p-jpeg/jpeg-core.c index 5a49c307f9c1..ecf7b0b04c78 100644 --- a/drivers/media/video/s5p-jpeg/jpeg-core.c +++ b/drivers/media/video/s5p-jpeg/jpeg-core.c @@ -1386,6 +1386,10 @@ static int s5p_jpeg_probe(struct platform_device *pdev) jpeg->vfd_encoder->release = video_device_release; jpeg->vfd_encoder->lock = &jpeg->lock; jpeg->vfd_encoder->v4l2_dev = &jpeg->v4l2_dev; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &jpeg->vfd_encoder->flags); ret = video_register_device(jpeg->vfd_encoder, VFL_TYPE_GRABBER, -1); if (ret) { @@ -1413,6 +1417,10 @@ static int s5p_jpeg_probe(struct platform_device *pdev) jpeg->vfd_decoder->release = video_device_release; jpeg->vfd_decoder->lock = &jpeg->lock; jpeg->vfd_decoder->v4l2_dev = &jpeg->v4l2_dev; + /* Locking in file operations other than ioctl should be done by the driver, + not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &jpeg->vfd_decoder->flags); ret = video_register_device(jpeg->vfd_decoder, VFL_TYPE_GRABBER, -1); if (ret) { diff --git a/drivers/media/video/s5p-mfc/s5p_mfc.c b/drivers/media/video/s5p-mfc/s5p_mfc.c index 83fe461af263..76008549b3f1 100644 --- a/drivers/media/video/s5p-mfc/s5p_mfc.c +++ b/drivers/media/video/s5p-mfc/s5p_mfc.c @@ -1048,6 +1048,10 @@ static int s5p_mfc_probe(struct platform_device *pdev) vfd->ioctl_ops = get_dec_v4l2_ioctl_ops(); vfd->release = video_device_release, vfd->lock = &dev->mfc_mutex; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags); vfd->v4l2_dev = &dev->v4l2_dev; snprintf(vfd->name, sizeof(vfd->name), "%s", S5P_MFC_DEC_NAME); dev->vfd_dec = vfd; @@ -1072,6 +1076,8 @@ static int s5p_mfc_probe(struct platform_device *pdev) vfd->ioctl_ops = get_enc_v4l2_ioctl_ops(); vfd->release = video_device_release, vfd->lock = &dev->mfc_mutex; + /* This should not be necessary */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags); vfd->v4l2_dev = &dev->v4l2_dev; snprintf(vfd->name, sizeof(vfd->name), "%s", S5P_MFC_ENC_NAME); dev->vfd_enc = vfd; diff --git a/drivers/media/video/s5p-tv/mixer_drv.c b/drivers/media/video/s5p-tv/mixer_drv.c index a2c0c25ad130..edca06592883 100644 --- a/drivers/media/video/s5p-tv/mixer_drv.c +++ b/drivers/media/video/s5p-tv/mixer_drv.c @@ -461,7 +461,7 @@ static struct platform_driver mxr_driver __refdata = { static int __init mxr_init(void) { int i, ret; - static const char banner[] __initdata = KERN_INFO + static const char banner[] __initconst = KERN_INFO "Samsung TV Mixer driver, " "(c) 2010-2011 Samsung Electronics Co., Ltd.\n"; printk(banner); diff --git a/drivers/media/video/s5p-tv/mixer_video.c b/drivers/media/video/s5p-tv/mixer_video.c index f7ca5cc143c6..c0eadd75c9ac 100644 --- a/drivers/media/video/s5p-tv/mixer_video.c +++ b/drivers/media/video/s5p-tv/mixer_video.c @@ -1069,6 +1069,10 @@ struct mxr_layer *mxr_base_layer_create(struct mxr_device *mdev, set_bit(V4L2_FL_USE_FH_PRIO, &layer->vfd.flags); video_set_drvdata(&layer->vfd, layer); + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &layer->vfd.flags); layer->vfd.lock = &layer->mutex; layer->vfd.v4l2_dev = &mdev->v4l2_dev; diff --git a/drivers/media/video/saa7134/saa7134-video.c b/drivers/media/video/saa7134/saa7134-video.c index 417034eb6ad2..6de10b1e7251 100644 --- a/drivers/media/video/saa7134/saa7134-video.c +++ b/drivers/media/video/saa7134/saa7134-video.c @@ -2036,7 +2036,7 @@ static int saa7134_s_tuner(struct file *file, void *priv, mode = dev->thread.mode; if (UNSET == mode) { rx = saa7134_tvaudio_getstereo(dev); - mode = saa7134_tvaudio_rx2mode(t->rxsubchans); + mode = saa7134_tvaudio_rx2mode(rx); } if (mode != t->audmode) dev->thread.mode = t->audmode; diff --git a/drivers/media/video/saa7164/saa7164-vbi.c b/drivers/media/video/saa7164/saa7164-vbi.c index 273cf807401c..d8e6c8f14079 100644 --- a/drivers/media/video/saa7164/saa7164-vbi.c +++ b/drivers/media/video/saa7164/saa7164-vbi.c @@ -952,7 +952,7 @@ static int saa7164_vbi_start_streaming(struct saa7164_port *port) /* Stop the hardware, regardless */ result = saa7164_vbi_stop_port(port); - if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) { + if (result != SAA_OK) { printk(KERN_ERR "%s() pause/forced stop transition " "failed, res = 0x%x\n", __func__, result); } @@ -971,7 +971,7 @@ static int saa7164_vbi_start_streaming(struct saa7164_port *port) /* Stop the hardware, regardless */ result = saa7164_vbi_acquire_port(port); result = saa7164_vbi_stop_port(port); - if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) { + if (result != SAA_OK) { printk(KERN_ERR "%s() run/forced stop transition " "failed, res = 0x%x\n", __func__, result); } diff --git a/drivers/media/video/sh_vou.c b/drivers/media/video/sh_vou.c index 9644bd861abc..8fd1874382c6 100644 --- a/drivers/media/video/sh_vou.c +++ b/drivers/media/video/sh_vou.c @@ -1390,6 +1390,10 @@ static int __devinit sh_vou_probe(struct platform_device *pdev) vdev->v4l2_dev = &vou_dev->v4l2_dev; vdev->release = video_device_release; vdev->lock = &vou_dev->fop_lock; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags); vou_dev->vdev = vdev; video_set_drvdata(vdev, vou_dev); diff --git a/drivers/media/video/smiapp-pll.c b/drivers/media/video/smiapp-pll.c new file mode 100644 index 000000000000..501da413dfad --- /dev/null +++ b/drivers/media/video/smiapp-pll.c @@ -0,0 +1,419 @@ +/* + * drivers/media/video/smiapp-pll.c + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2011--2012 Nokia Corporation + * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> + * + * 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 + * + */ + +#include "smiapp/smiapp-debug.h" + +#include <linux/gcd.h> +#include <linux/lcm.h> +#include <linux/module.h> + +#include "smiapp-pll.h" + +/* Return an even number or one. */ +static inline uint32_t clk_div_even(uint32_t a) +{ + return max_t(uint32_t, 1, a & ~1); +} + +/* Return an even number or one. */ +static inline uint32_t clk_div_even_up(uint32_t a) +{ + if (a == 1) + return 1; + return (a + 1) & ~1; +} + +static inline uint32_t is_one_or_even(uint32_t a) +{ + if (a == 1) + return 1; + if (a & 1) + return 0; + + return 1; +} + +static int bounds_check(struct device *dev, uint32_t val, + uint32_t min, uint32_t max, char *str) +{ + if (val >= min && val <= max) + return 0; + + dev_warn(dev, "%s out of bounds: %d (%d--%d)\n", str, val, min, max); + + return -EINVAL; +} + +static void print_pll(struct device *dev, struct smiapp_pll *pll) +{ + dev_dbg(dev, "pre_pll_clk_div\t%d\n", pll->pre_pll_clk_div); + dev_dbg(dev, "pll_multiplier \t%d\n", pll->pll_multiplier); + if (pll->flags != SMIAPP_PLL_FLAG_NO_OP_CLOCKS) { + dev_dbg(dev, "op_sys_clk_div \t%d\n", pll->op_sys_clk_div); + dev_dbg(dev, "op_pix_clk_div \t%d\n", pll->op_pix_clk_div); + } + dev_dbg(dev, "vt_sys_clk_div \t%d\n", pll->vt_sys_clk_div); + dev_dbg(dev, "vt_pix_clk_div \t%d\n", pll->vt_pix_clk_div); + + dev_dbg(dev, "ext_clk_freq_hz \t%d\n", pll->ext_clk_freq_hz); + dev_dbg(dev, "pll_ip_clk_freq_hz \t%d\n", pll->pll_ip_clk_freq_hz); + dev_dbg(dev, "pll_op_clk_freq_hz \t%d\n", pll->pll_op_clk_freq_hz); + if (pll->flags & SMIAPP_PLL_FLAG_NO_OP_CLOCKS) { + dev_dbg(dev, "op_sys_clk_freq_hz \t%d\n", + pll->op_sys_clk_freq_hz); + dev_dbg(dev, "op_pix_clk_freq_hz \t%d\n", + pll->op_pix_clk_freq_hz); + } + dev_dbg(dev, "vt_sys_clk_freq_hz \t%d\n", pll->vt_sys_clk_freq_hz); + dev_dbg(dev, "vt_pix_clk_freq_hz \t%d\n", pll->vt_pix_clk_freq_hz); +} + +int smiapp_pll_calculate(struct device *dev, struct smiapp_pll_limits *limits, + struct smiapp_pll *pll) +{ + uint32_t sys_div; + uint32_t best_pix_div = INT_MAX >> 1; + uint32_t vt_op_binning_div; + uint32_t lane_op_clock_ratio; + uint32_t mul, div; + uint32_t more_mul_min, more_mul_max; + uint32_t more_mul_factor; + uint32_t min_vt_div, max_vt_div, vt_div; + uint32_t min_sys_div, max_sys_div; + unsigned int i; + int rval; + + if (pll->flags & SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE) + lane_op_clock_ratio = pll->lanes; + else + lane_op_clock_ratio = 1; + dev_dbg(dev, "lane_op_clock_ratio: %d\n", lane_op_clock_ratio); + + dev_dbg(dev, "binning: %dx%d\n", pll->binning_horizontal, + pll->binning_vertical); + + /* CSI transfers 2 bits per clock per lane; thus times 2 */ + pll->pll_op_clk_freq_hz = pll->link_freq * 2 + * (pll->lanes / lane_op_clock_ratio); + + /* Figure out limits for pre-pll divider based on extclk */ + dev_dbg(dev, "min / max pre_pll_clk_div: %d / %d\n", + limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div); + limits->max_pre_pll_clk_div = + min_t(uint16_t, limits->max_pre_pll_clk_div, + clk_div_even(pll->ext_clk_freq_hz / + limits->min_pll_ip_freq_hz)); + limits->min_pre_pll_clk_div = + max_t(uint16_t, limits->min_pre_pll_clk_div, + clk_div_even(pll->ext_clk_freq_hz / + limits->max_pll_ip_freq_hz)); + dev_dbg(dev, "pre-pll check: min / max pre_pll_clk_div: %d / %d\n", + limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div); + + i = gcd(pll->pll_op_clk_freq_hz, pll->ext_clk_freq_hz); + mul = div_u64(pll->pll_op_clk_freq_hz, i); + div = pll->ext_clk_freq_hz / i; + dev_dbg(dev, "mul %d / div %d\n", mul, div); + + limits->min_pre_pll_clk_div = + max_t(uint16_t, limits->min_pre_pll_clk_div, + clk_div_even_up( + DIV_ROUND_UP(mul * pll->ext_clk_freq_hz, + limits->max_pll_op_freq_hz))); + dev_dbg(dev, "pll_op check: min / max pre_pll_clk_div: %d / %d\n", + limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div); + + if (limits->min_pre_pll_clk_div > limits->max_pre_pll_clk_div) { + dev_err(dev, "unable to compute pre_pll divisor\n"); + return -EINVAL; + } + + pll->pre_pll_clk_div = limits->min_pre_pll_clk_div; + + /* + * Get pre_pll_clk_div so that our pll_op_clk_freq_hz won't be + * too high. + */ + dev_dbg(dev, "pre_pll_clk_div %d\n", pll->pre_pll_clk_div); + + /* Don't go above max pll multiplier. */ + more_mul_max = limits->max_pll_multiplier / mul; + dev_dbg(dev, "more_mul_max: max_pll_multiplier check: %d\n", + more_mul_max); + /* Don't go above max pll op frequency. */ + more_mul_max = + min_t(int, + more_mul_max, + limits->max_pll_op_freq_hz + / (pll->ext_clk_freq_hz / pll->pre_pll_clk_div * mul)); + dev_dbg(dev, "more_mul_max: max_pll_op_freq_hz check: %d\n", + more_mul_max); + /* Don't go above the division capability of op sys clock divider. */ + more_mul_max = min(more_mul_max, + limits->max_op_sys_clk_div * pll->pre_pll_clk_div + / div); + dev_dbg(dev, "more_mul_max: max_op_sys_clk_div check: %d\n", + more_mul_max); + /* Ensure we won't go above min_pll_multiplier. */ + more_mul_max = min(more_mul_max, + DIV_ROUND_UP(limits->max_pll_multiplier, mul)); + dev_dbg(dev, "more_mul_max: min_pll_multiplier check: %d\n", + more_mul_max); + + /* Ensure we won't go below min_pll_op_freq_hz. */ + more_mul_min = DIV_ROUND_UP(limits->min_pll_op_freq_hz, + pll->ext_clk_freq_hz / pll->pre_pll_clk_div + * mul); + dev_dbg(dev, "more_mul_min: min_pll_op_freq_hz check: %d\n", + more_mul_min); + /* Ensure we won't go below min_pll_multiplier. */ + more_mul_min = max(more_mul_min, + DIV_ROUND_UP(limits->min_pll_multiplier, mul)); + dev_dbg(dev, "more_mul_min: min_pll_multiplier check: %d\n", + more_mul_min); + + if (more_mul_min > more_mul_max) { + dev_warn(dev, + "unable to compute more_mul_min and more_mul_max"); + return -EINVAL; + } + + more_mul_factor = lcm(div, pll->pre_pll_clk_div) / div; + dev_dbg(dev, "more_mul_factor: %d\n", more_mul_factor); + more_mul_factor = lcm(more_mul_factor, limits->min_op_sys_clk_div); + dev_dbg(dev, "more_mul_factor: min_op_sys_clk_div: %d\n", + more_mul_factor); + i = roundup(more_mul_min, more_mul_factor); + if (!is_one_or_even(i)) + i <<= 1; + + dev_dbg(dev, "final more_mul: %d\n", i); + if (i > more_mul_max) { + dev_warn(dev, "final more_mul is bad, max %d", more_mul_max); + return -EINVAL; + } + + pll->pll_multiplier = mul * i; + pll->op_sys_clk_div = div * i / pll->pre_pll_clk_div; + dev_dbg(dev, "op_sys_clk_div: %d\n", pll->op_sys_clk_div); + + pll->pll_ip_clk_freq_hz = pll->ext_clk_freq_hz + / pll->pre_pll_clk_div; + + pll->pll_op_clk_freq_hz = pll->pll_ip_clk_freq_hz + * pll->pll_multiplier; + + /* Derive pll_op_clk_freq_hz. */ + pll->op_sys_clk_freq_hz = + pll->pll_op_clk_freq_hz / pll->op_sys_clk_div; + + pll->op_pix_clk_div = pll->bits_per_pixel; + dev_dbg(dev, "op_pix_clk_div: %d\n", pll->op_pix_clk_div); + + pll->op_pix_clk_freq_hz = + pll->op_sys_clk_freq_hz / pll->op_pix_clk_div; + + /* + * Some sensors perform analogue binning and some do this + * digitally. The ones doing this digitally can be roughly be + * found out using this formula. The ones doing this digitally + * should run at higher clock rate, so smaller divisor is used + * on video timing side. + */ + if (limits->min_line_length_pck_bin > limits->min_line_length_pck + / pll->binning_horizontal) + vt_op_binning_div = pll->binning_horizontal; + else + vt_op_binning_div = 1; + dev_dbg(dev, "vt_op_binning_div: %d\n", vt_op_binning_div); + + /* + * Profile 2 supports vt_pix_clk_div E [4, 10] + * + * Horizontal binning can be used as a base for difference in + * divisors. One must make sure that horizontal blanking is + * enough to accommodate the CSI-2 sync codes. + * + * Take scaling factor into account as well. + * + * Find absolute limits for the factor of vt divider. + */ + dev_dbg(dev, "scale_m: %d\n", pll->scale_m); + min_vt_div = DIV_ROUND_UP(pll->op_pix_clk_div * pll->op_sys_clk_div + * pll->scale_n, + lane_op_clock_ratio * vt_op_binning_div + * pll->scale_m); + + /* Find smallest and biggest allowed vt divisor. */ + dev_dbg(dev, "min_vt_div: %d\n", min_vt_div); + min_vt_div = max(min_vt_div, + DIV_ROUND_UP(pll->pll_op_clk_freq_hz, + limits->max_vt_pix_clk_freq_hz)); + dev_dbg(dev, "min_vt_div: max_vt_pix_clk_freq_hz: %d\n", + min_vt_div); + min_vt_div = max_t(uint32_t, min_vt_div, + limits->min_vt_pix_clk_div + * limits->min_vt_sys_clk_div); + dev_dbg(dev, "min_vt_div: min_vt_clk_div: %d\n", min_vt_div); + + max_vt_div = limits->max_vt_sys_clk_div * limits->max_vt_pix_clk_div; + dev_dbg(dev, "max_vt_div: %d\n", max_vt_div); + max_vt_div = min(max_vt_div, + DIV_ROUND_UP(pll->pll_op_clk_freq_hz, + limits->min_vt_pix_clk_freq_hz)); + dev_dbg(dev, "max_vt_div: min_vt_pix_clk_freq_hz: %d\n", + max_vt_div); + + /* + * Find limitsits for sys_clk_div. Not all values are possible + * with all values of pix_clk_div. + */ + min_sys_div = limits->min_vt_sys_clk_div; + dev_dbg(dev, "min_sys_div: %d\n", min_sys_div); + min_sys_div = max(min_sys_div, + DIV_ROUND_UP(min_vt_div, + limits->max_vt_pix_clk_div)); + dev_dbg(dev, "min_sys_div: max_vt_pix_clk_div: %d\n", min_sys_div); + min_sys_div = max(min_sys_div, + pll->pll_op_clk_freq_hz + / limits->max_vt_sys_clk_freq_hz); + dev_dbg(dev, "min_sys_div: max_pll_op_clk_freq_hz: %d\n", min_sys_div); + min_sys_div = clk_div_even_up(min_sys_div); + dev_dbg(dev, "min_sys_div: one or even: %d\n", min_sys_div); + + max_sys_div = limits->max_vt_sys_clk_div; + dev_dbg(dev, "max_sys_div: %d\n", max_sys_div); + max_sys_div = min(max_sys_div, + DIV_ROUND_UP(max_vt_div, + limits->min_vt_pix_clk_div)); + dev_dbg(dev, "max_sys_div: min_vt_pix_clk_div: %d\n", max_sys_div); + max_sys_div = min(max_sys_div, + DIV_ROUND_UP(pll->pll_op_clk_freq_hz, + limits->min_vt_pix_clk_freq_hz)); + dev_dbg(dev, "max_sys_div: min_vt_pix_clk_freq_hz: %d\n", max_sys_div); + + /* + * Find pix_div such that a legal pix_div * sys_div results + * into a value which is not smaller than div, the desired + * divisor. + */ + for (vt_div = min_vt_div; vt_div <= max_vt_div; + vt_div += 2 - (vt_div & 1)) { + for (sys_div = min_sys_div; + sys_div <= max_sys_div; + sys_div += 2 - (sys_div & 1)) { + int pix_div = DIV_ROUND_UP(vt_div, sys_div); + + if (pix_div < limits->min_vt_pix_clk_div + || pix_div > limits->max_vt_pix_clk_div) { + dev_dbg(dev, + "pix_div %d too small or too big (%d--%d)\n", + pix_div, + limits->min_vt_pix_clk_div, + limits->max_vt_pix_clk_div); + continue; + } + + /* Check if this one is better. */ + if (pix_div * sys_div + <= roundup(min_vt_div, best_pix_div)) + best_pix_div = pix_div; + } + if (best_pix_div < INT_MAX >> 1) + break; + } + + pll->vt_sys_clk_div = DIV_ROUND_UP(min_vt_div, best_pix_div); + pll->vt_pix_clk_div = best_pix_div; + + pll->vt_sys_clk_freq_hz = + pll->pll_op_clk_freq_hz / pll->vt_sys_clk_div; + pll->vt_pix_clk_freq_hz = + pll->vt_sys_clk_freq_hz / pll->vt_pix_clk_div; + + pll->pixel_rate_csi = + pll->op_pix_clk_freq_hz * lane_op_clock_ratio; + + print_pll(dev, pll); + + rval = bounds_check(dev, pll->pre_pll_clk_div, + limits->min_pre_pll_clk_div, + limits->max_pre_pll_clk_div, "pre_pll_clk_div"); + if (!rval) + rval = bounds_check( + dev, pll->pll_ip_clk_freq_hz, + limits->min_pll_ip_freq_hz, limits->max_pll_ip_freq_hz, + "pll_ip_clk_freq_hz"); + if (!rval) + rval = bounds_check( + dev, pll->pll_multiplier, + limits->min_pll_multiplier, limits->max_pll_multiplier, + "pll_multiplier"); + if (!rval) + rval = bounds_check( + dev, pll->pll_op_clk_freq_hz, + limits->min_pll_op_freq_hz, limits->max_pll_op_freq_hz, + "pll_op_clk_freq_hz"); + if (!rval) + rval = bounds_check( + dev, pll->op_sys_clk_div, + limits->min_op_sys_clk_div, limits->max_op_sys_clk_div, + "op_sys_clk_div"); + if (!rval) + rval = bounds_check( + dev, pll->op_pix_clk_div, + limits->min_op_pix_clk_div, limits->max_op_pix_clk_div, + "op_pix_clk_div"); + if (!rval) + rval = bounds_check( + dev, pll->op_sys_clk_freq_hz, + limits->min_op_sys_clk_freq_hz, + limits->max_op_sys_clk_freq_hz, + "op_sys_clk_freq_hz"); + if (!rval) + rval = bounds_check( + dev, pll->op_pix_clk_freq_hz, + limits->min_op_pix_clk_freq_hz, + limits->max_op_pix_clk_freq_hz, + "op_pix_clk_freq_hz"); + if (!rval) + rval = bounds_check( + dev, pll->vt_sys_clk_freq_hz, + limits->min_vt_sys_clk_freq_hz, + limits->max_vt_sys_clk_freq_hz, + "vt_sys_clk_freq_hz"); + if (!rval) + rval = bounds_check( + dev, pll->vt_pix_clk_freq_hz, + limits->min_vt_pix_clk_freq_hz, + limits->max_vt_pix_clk_freq_hz, + "vt_pix_clk_freq_hz"); + + return rval; +} +EXPORT_SYMBOL_GPL(smiapp_pll_calculate); + +MODULE_AUTHOR("Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>"); +MODULE_DESCRIPTION("Generic SMIA/SMIA++ PLL calculator"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/smiapp-pll.h b/drivers/media/video/smiapp-pll.h new file mode 100644 index 000000000000..9eab63f23afb --- /dev/null +++ b/drivers/media/video/smiapp-pll.h @@ -0,0 +1,103 @@ +/* + * drivers/media/video/smiapp-pll.h + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2012 Nokia Corporation + * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> + * + * 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 SMIAPP_PLL_H +#define SMIAPP_PLL_H + +#include <linux/device.h> + +struct smiapp_pll { + uint8_t lanes; + uint8_t binning_horizontal; + uint8_t binning_vertical; + uint8_t scale_m; + uint8_t scale_n; + uint8_t bits_per_pixel; + uint16_t flags; + uint32_t link_freq; + + uint16_t pre_pll_clk_div; + uint16_t pll_multiplier; + uint16_t op_sys_clk_div; + uint16_t op_pix_clk_div; + uint16_t vt_sys_clk_div; + uint16_t vt_pix_clk_div; + + uint32_t ext_clk_freq_hz; + uint32_t pll_ip_clk_freq_hz; + uint32_t pll_op_clk_freq_hz; + uint32_t op_sys_clk_freq_hz; + uint32_t op_pix_clk_freq_hz; + uint32_t vt_sys_clk_freq_hz; + uint32_t vt_pix_clk_freq_hz; + + uint32_t pixel_rate_csi; +}; + +struct smiapp_pll_limits { + /* Strict PLL limits */ + uint32_t min_ext_clk_freq_hz; + uint32_t max_ext_clk_freq_hz; + uint16_t min_pre_pll_clk_div; + uint16_t max_pre_pll_clk_div; + uint32_t min_pll_ip_freq_hz; + uint32_t max_pll_ip_freq_hz; + uint16_t min_pll_multiplier; + uint16_t max_pll_multiplier; + uint32_t min_pll_op_freq_hz; + uint32_t max_pll_op_freq_hz; + + uint16_t min_vt_sys_clk_div; + uint16_t max_vt_sys_clk_div; + uint32_t min_vt_sys_clk_freq_hz; + uint32_t max_vt_sys_clk_freq_hz; + uint16_t min_vt_pix_clk_div; + uint16_t max_vt_pix_clk_div; + uint32_t min_vt_pix_clk_freq_hz; + uint32_t max_vt_pix_clk_freq_hz; + + uint16_t min_op_sys_clk_div; + uint16_t max_op_sys_clk_div; + uint32_t min_op_sys_clk_freq_hz; + uint32_t max_op_sys_clk_freq_hz; + uint16_t min_op_pix_clk_div; + uint16_t max_op_pix_clk_div; + uint32_t min_op_pix_clk_freq_hz; + uint32_t max_op_pix_clk_freq_hz; + + /* Other relevant limits */ + uint32_t min_line_length_pck_bin; + uint32_t min_line_length_pck; +}; + +/* op pix clock is for all lanes in total normally */ +#define SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE (1 << 0) +#define SMIAPP_PLL_FLAG_NO_OP_CLOCKS (1 << 1) + +struct device; + +int smiapp_pll_calculate(struct device *dev, struct smiapp_pll_limits *limits, + struct smiapp_pll *pll); + +#endif /* SMIAPP_PLL_H */ diff --git a/drivers/media/video/smiapp/Kconfig b/drivers/media/video/smiapp/Kconfig new file mode 100644 index 000000000000..9504c436a5ca --- /dev/null +++ b/drivers/media/video/smiapp/Kconfig @@ -0,0 +1,13 @@ +config VIDEO_SMIAPP + tristate "SMIA++/SMIA sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + select VIDEO_SMIAPP_PLL + ---help--- + This is a generic driver for SMIA++/SMIA camera modules. + +config VIDEO_SMIAPP_DEBUG + bool "Enable debugging for the generic SMIA++/SMIA driver" + depends on VIDEO_SMIAPP + ---help--- + Enable debugging output in the generic SMIA++/SMIA driver. If you + are developing the driver you might want to enable this. diff --git a/drivers/media/video/smiapp/Makefile b/drivers/media/video/smiapp/Makefile new file mode 100644 index 000000000000..5a207eecd357 --- /dev/null +++ b/drivers/media/video/smiapp/Makefile @@ -0,0 +1,3 @@ +smiapp-objs += smiapp-core.o smiapp-regs.o \ + smiapp-quirk.o smiapp-limits.o +obj-$(CONFIG_VIDEO_SMIAPP) += smiapp.o diff --git a/drivers/media/video/smiapp/smiapp-core.c b/drivers/media/video/smiapp/smiapp-core.c new file mode 100644 index 000000000000..3991c452acb2 --- /dev/null +++ b/drivers/media/video/smiapp/smiapp-core.c @@ -0,0 +1,2832 @@ +/* + * drivers/media/video/smiapp/smiapp-core.c + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2010--2012 Nokia Corporation + * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> + * + * Based on smiapp driver by Vimarsh Zutshi + * Based on jt8ev1.c by Vimarsh Zutshi + * Based on smia-sensor.c by Tuukka Toivonen <tuukkat76@gmail.com> + * + * 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 + * + */ + +#include "smiapp-debug.h" + +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/gpio.h> +#include <linux/module.h> +#include <linux/regulator/consumer.h> +#include <linux/v4l2-mediabus.h> +#include <media/v4l2-device.h> + +#include "smiapp.h" + +#define SMIAPP_ALIGN_DIM(dim, flags) \ + ((flags) & V4L2_SUBDEV_SEL_FLAG_SIZE_GE \ + ? ALIGN((dim), 2) \ + : (dim) & ~1) + +/* + * smiapp_module_idents - supported camera modules + */ +static const struct smiapp_module_ident smiapp_module_idents[] = { + SMIAPP_IDENT_L(0x01, 0x022b, -1, "vs6555"), + SMIAPP_IDENT_L(0x01, 0x022e, -1, "vw6558"), + SMIAPP_IDENT_L(0x07, 0x7698, -1, "ovm7698"), + SMIAPP_IDENT_L(0x0b, 0x4242, -1, "smiapp-003"), + SMIAPP_IDENT_L(0x0c, 0x208a, -1, "tcm8330md"), + SMIAPP_IDENT_LQ(0x0c, 0x2134, -1, "tcm8500md", &smiapp_tcm8500md_quirk), + SMIAPP_IDENT_L(0x0c, 0x213e, -1, "et8en2"), + SMIAPP_IDENT_L(0x0c, 0x2184, -1, "tcm8580md"), + SMIAPP_IDENT_LQ(0x0c, 0x560f, -1, "jt8ew9", &smiapp_jt8ew9_quirk), + SMIAPP_IDENT_LQ(0x10, 0x4141, -1, "jt8ev1", &smiapp_jt8ev1_quirk), + SMIAPP_IDENT_LQ(0x10, 0x4241, -1, "imx125es", &smiapp_imx125es_quirk), +}; + +/* + * + * Dynamic Capability Identification + * + */ + +static int smiapp_read_frame_fmt(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + u32 fmt_model_type, fmt_model_subtype, ncol_desc, nrow_desc; + unsigned int i; + int rval; + int line_count = 0; + int embedded_start = -1, embedded_end = -1; + int image_start = 0; + + rval = smiapp_read(client, SMIAPP_REG_U8_FRAME_FORMAT_MODEL_TYPE, + &fmt_model_type); + if (rval) + return rval; + + rval = smiapp_read(client, SMIAPP_REG_U8_FRAME_FORMAT_MODEL_SUBTYPE, + &fmt_model_subtype); + if (rval) + return rval; + + ncol_desc = (fmt_model_subtype + & SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NCOLS_MASK) + >> SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NCOLS_SHIFT; + nrow_desc = fmt_model_subtype + & SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NROWS_MASK; + + dev_dbg(&client->dev, "format_model_type %s\n", + fmt_model_type == SMIAPP_FRAME_FORMAT_MODEL_TYPE_2BYTE + ? "2 byte" : + fmt_model_type == SMIAPP_FRAME_FORMAT_MODEL_TYPE_4BYTE + ? "4 byte" : "is simply bad"); + + for (i = 0; i < ncol_desc + nrow_desc; i++) { + u32 desc; + u32 pixelcode; + u32 pixels; + char *which; + char *what; + + if (fmt_model_type == SMIAPP_FRAME_FORMAT_MODEL_TYPE_2BYTE) { + rval = smiapp_read( + client, + SMIAPP_REG_U16_FRAME_FORMAT_DESCRIPTOR_2(i), + &desc); + if (rval) + return rval; + + pixelcode = + (desc + & SMIAPP_FRAME_FORMAT_DESC_2_PIXELCODE_MASK) + >> SMIAPP_FRAME_FORMAT_DESC_2_PIXELCODE_SHIFT; + pixels = desc & SMIAPP_FRAME_FORMAT_DESC_2_PIXELS_MASK; + } else if (fmt_model_type + == SMIAPP_FRAME_FORMAT_MODEL_TYPE_4BYTE) { + rval = smiapp_read( + client, + SMIAPP_REG_U32_FRAME_FORMAT_DESCRIPTOR_4(i), + &desc); + if (rval) + return rval; + + pixelcode = + (desc + & SMIAPP_FRAME_FORMAT_DESC_4_PIXELCODE_MASK) + >> SMIAPP_FRAME_FORMAT_DESC_4_PIXELCODE_SHIFT; + pixels = desc & SMIAPP_FRAME_FORMAT_DESC_4_PIXELS_MASK; + } else { + dev_dbg(&client->dev, + "invalid frame format model type %d\n", + fmt_model_type); + return -EINVAL; + } + + if (i < ncol_desc) + which = "columns"; + else + which = "rows"; + + switch (pixelcode) { + case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_EMBEDDED: + what = "embedded"; + break; + case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_DUMMY: + what = "dummy"; + break; + case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_BLACK: + what = "black"; + break; + case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_DARK: + what = "dark"; + break; + case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_VISIBLE: + what = "visible"; + break; + default: + what = "invalid"; + dev_dbg(&client->dev, "pixelcode %d\n", pixelcode); + break; + } + + dev_dbg(&client->dev, "%s pixels: %d %s\n", + what, pixels, which); + + if (i < ncol_desc) + continue; + + /* Handle row descriptors */ + if (pixelcode + == SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_EMBEDDED) { + embedded_start = line_count; + } else { + if (pixelcode == SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_VISIBLE + || pixels >= sensor->limits[SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES] / 2) + image_start = line_count; + if (embedded_start != -1 && embedded_end == -1) + embedded_end = line_count; + } + line_count += pixels; + } + + if (embedded_start == -1 || embedded_end == -1) { + embedded_start = 0; + embedded_end = 0; + } + + dev_dbg(&client->dev, "embedded data from lines %d to %d\n", + embedded_start, embedded_end); + dev_dbg(&client->dev, "image data starts at line %d\n", image_start); + + return 0; +} + +static int smiapp_pll_configure(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + struct smiapp_pll *pll = &sensor->pll; + int rval; + + rval = smiapp_write( + client, SMIAPP_REG_U16_VT_PIX_CLK_DIV, pll->vt_pix_clk_div); + if (rval < 0) + return rval; + + rval = smiapp_write( + client, SMIAPP_REG_U16_VT_SYS_CLK_DIV, pll->vt_sys_clk_div); + if (rval < 0) + return rval; + + rval = smiapp_write( + client, SMIAPP_REG_U16_PRE_PLL_CLK_DIV, pll->pre_pll_clk_div); + if (rval < 0) + return rval; + + rval = smiapp_write( + client, SMIAPP_REG_U16_PLL_MULTIPLIER, pll->pll_multiplier); + if (rval < 0) + return rval; + + /* Lane op clock ratio does not apply here. */ + rval = smiapp_write( + client, SMIAPP_REG_U32_REQUESTED_LINK_BIT_RATE_MBPS, + DIV_ROUND_UP(pll->op_sys_clk_freq_hz, 1000000 / 256 / 256)); + if (rval < 0 || sensor->minfo.smiapp_profile == SMIAPP_PROFILE_0) + return rval; + + rval = smiapp_write( + client, SMIAPP_REG_U16_OP_PIX_CLK_DIV, pll->op_pix_clk_div); + if (rval < 0) + return rval; + + return smiapp_write( + client, SMIAPP_REG_U16_OP_SYS_CLK_DIV, pll->op_sys_clk_div); +} + +static int smiapp_pll_update(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + struct smiapp_pll_limits lim = { + .min_pre_pll_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_PRE_PLL_CLK_DIV], + .max_pre_pll_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_PRE_PLL_CLK_DIV], + .min_pll_ip_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_PLL_IP_FREQ_HZ], + .max_pll_ip_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_PLL_IP_FREQ_HZ], + .min_pll_multiplier = sensor->limits[SMIAPP_LIMIT_MIN_PLL_MULTIPLIER], + .max_pll_multiplier = sensor->limits[SMIAPP_LIMIT_MAX_PLL_MULTIPLIER], + .min_pll_op_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_PLL_OP_FREQ_HZ], + .max_pll_op_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_PLL_OP_FREQ_HZ], + + .min_op_sys_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_OP_SYS_CLK_DIV], + .max_op_sys_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_OP_SYS_CLK_DIV], + .min_op_pix_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_OP_PIX_CLK_DIV], + .max_op_pix_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_OP_PIX_CLK_DIV], + .min_op_sys_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_OP_SYS_CLK_FREQ_HZ], + .max_op_sys_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_OP_SYS_CLK_FREQ_HZ], + .min_op_pix_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_OP_PIX_CLK_FREQ_HZ], + .max_op_pix_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_OP_PIX_CLK_FREQ_HZ], + + .min_vt_sys_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_VT_SYS_CLK_DIV], + .max_vt_sys_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_VT_SYS_CLK_DIV], + .min_vt_pix_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_VT_PIX_CLK_DIV], + .max_vt_pix_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_VT_PIX_CLK_DIV], + .min_vt_sys_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_VT_SYS_CLK_FREQ_HZ], + .max_vt_sys_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_VT_SYS_CLK_FREQ_HZ], + .min_vt_pix_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_VT_PIX_CLK_FREQ_HZ], + .max_vt_pix_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_VT_PIX_CLK_FREQ_HZ], + + .min_line_length_pck_bin = sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN], + .min_line_length_pck = sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK], + }; + struct smiapp_pll *pll = &sensor->pll; + int rval; + + memset(&sensor->pll, 0, sizeof(sensor->pll)); + + pll->lanes = sensor->platform_data->lanes; + pll->ext_clk_freq_hz = sensor->platform_data->ext_clk; + + if (sensor->minfo.smiapp_profile == SMIAPP_PROFILE_0) { + /* + * Fill in operational clock divisors limits from the + * video timing ones. On profile 0 sensors the + * requirements regarding them are essentially the + * same as on VT ones. + */ + lim.min_op_sys_clk_div = lim.min_vt_sys_clk_div; + lim.max_op_sys_clk_div = lim.max_vt_sys_clk_div; + lim.min_op_pix_clk_div = lim.min_vt_pix_clk_div; + lim.max_op_pix_clk_div = lim.max_vt_pix_clk_div; + lim.min_op_sys_clk_freq_hz = lim.min_vt_sys_clk_freq_hz; + lim.max_op_sys_clk_freq_hz = lim.max_vt_sys_clk_freq_hz; + lim.min_op_pix_clk_freq_hz = lim.min_vt_pix_clk_freq_hz; + lim.max_op_pix_clk_freq_hz = lim.max_vt_pix_clk_freq_hz; + /* Profile 0 sensors have no separate OP clock branch. */ + pll->flags |= SMIAPP_PLL_FLAG_NO_OP_CLOCKS; + } + + if (smiapp_needs_quirk(sensor, + SMIAPP_QUIRK_FLAG_OP_PIX_CLOCK_PER_LANE)) + pll->flags |= SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE; + + pll->binning_horizontal = sensor->binning_horizontal; + pll->binning_vertical = sensor->binning_vertical; + pll->link_freq = + sensor->link_freq->qmenu_int[sensor->link_freq->val]; + pll->scale_m = sensor->scale_m; + pll->scale_n = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]; + pll->bits_per_pixel = sensor->csi_format->compressed; + + rval = smiapp_pll_calculate(&client->dev, &lim, pll); + if (rval < 0) + return rval; + + sensor->pixel_rate_parray->cur.val64 = pll->vt_pix_clk_freq_hz; + sensor->pixel_rate_csi->cur.val64 = pll->pixel_rate_csi; + + return 0; +} + + +/* + * + * V4L2 Controls handling + * + */ + +static void __smiapp_update_exposure_limits(struct smiapp_sensor *sensor) +{ + struct v4l2_ctrl *ctrl = sensor->exposure; + int max; + + max = sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height + + sensor->vblank->val + - sensor->limits[SMIAPP_LIMIT_COARSE_INTEGRATION_TIME_MAX_MARGIN]; + + ctrl->maximum = max; + if (ctrl->default_value > max) + ctrl->default_value = max; + if (ctrl->val > max) + ctrl->val = max; + if (ctrl->cur.val > max) + ctrl->cur.val = max; +} + +/* + * Order matters. + * + * 1. Bits-per-pixel, descending. + * 2. Bits-per-pixel compressed, descending. + * 3. Pixel order, same as in pixel_order_str. Formats for all four pixel + * orders must be defined. + */ +static const struct smiapp_csi_data_format smiapp_csi_data_formats[] = { + { V4L2_MBUS_FMT_SGRBG12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_GRBG, }, + { V4L2_MBUS_FMT_SRGGB12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_RGGB, }, + { V4L2_MBUS_FMT_SBGGR12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_BGGR, }, + { V4L2_MBUS_FMT_SGBRG12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_GBRG, }, + { V4L2_MBUS_FMT_SGRBG10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_GRBG, }, + { V4L2_MBUS_FMT_SRGGB10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_RGGB, }, + { V4L2_MBUS_FMT_SBGGR10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_BGGR, }, + { V4L2_MBUS_FMT_SGBRG10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_GBRG, }, + { V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_GRBG, }, + { V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_RGGB, }, + { V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_BGGR, }, + { V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_GBRG, }, +}; + +const char *pixel_order_str[] = { "GRBG", "RGGB", "BGGR", "GBRG" }; + +#define to_csi_format_idx(fmt) (((unsigned long)(fmt) \ + - (unsigned long)smiapp_csi_data_formats) \ + / sizeof(*smiapp_csi_data_formats)) + +static u32 smiapp_pixel_order(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int flip = 0; + + if (sensor->hflip) { + if (sensor->hflip->val) + flip |= SMIAPP_IMAGE_ORIENTATION_HFLIP; + + if (sensor->vflip->val) + flip |= SMIAPP_IMAGE_ORIENTATION_VFLIP; + } + + flip ^= sensor->hvflip_inv_mask; + + dev_dbg(&client->dev, "flip %d\n", flip); + return sensor->default_pixel_order ^ flip; +} + +static void smiapp_update_mbus_formats(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + unsigned int csi_format_idx = + to_csi_format_idx(sensor->csi_format) & ~3; + unsigned int internal_csi_format_idx = + to_csi_format_idx(sensor->internal_csi_format) & ~3; + unsigned int pixel_order = smiapp_pixel_order(sensor); + + sensor->mbus_frame_fmts = + sensor->default_mbus_frame_fmts << pixel_order; + sensor->csi_format = + &smiapp_csi_data_formats[csi_format_idx + pixel_order]; + sensor->internal_csi_format = + &smiapp_csi_data_formats[internal_csi_format_idx + + pixel_order]; + + BUG_ON(max(internal_csi_format_idx, csi_format_idx) + pixel_order + >= ARRAY_SIZE(smiapp_csi_data_formats)); + BUG_ON(min(internal_csi_format_idx, csi_format_idx) < 0); + + dev_dbg(&client->dev, "new pixel order %s\n", + pixel_order_str[pixel_order]); +} + +static int smiapp_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct smiapp_sensor *sensor = + container_of(ctrl->handler, struct smiapp_subdev, ctrl_handler) + ->sensor; + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + u32 orient = 0; + int exposure; + int rval; + + switch (ctrl->id) { + case V4L2_CID_ANALOGUE_GAIN: + return smiapp_write( + client, + SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GLOBAL, ctrl->val); + + case V4L2_CID_EXPOSURE: + return smiapp_write( + client, + SMIAPP_REG_U16_COARSE_INTEGRATION_TIME, ctrl->val); + + case V4L2_CID_HFLIP: + case V4L2_CID_VFLIP: + if (sensor->streaming) + return -EBUSY; + + if (sensor->hflip->val) + orient |= SMIAPP_IMAGE_ORIENTATION_HFLIP; + + if (sensor->vflip->val) + orient |= SMIAPP_IMAGE_ORIENTATION_VFLIP; + + orient ^= sensor->hvflip_inv_mask; + rval = smiapp_write(client, + SMIAPP_REG_U8_IMAGE_ORIENTATION, + orient); + if (rval < 0) + return rval; + + smiapp_update_mbus_formats(sensor); + + return 0; + + case V4L2_CID_VBLANK: + exposure = sensor->exposure->val; + + __smiapp_update_exposure_limits(sensor); + + if (exposure > sensor->exposure->maximum) { + sensor->exposure->val = + sensor->exposure->maximum; + rval = smiapp_set_ctrl( + sensor->exposure); + if (rval < 0) + return rval; + } + + return smiapp_write( + client, SMIAPP_REG_U16_FRAME_LENGTH_LINES, + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height + + ctrl->val); + + case V4L2_CID_HBLANK: + return smiapp_write( + client, SMIAPP_REG_U16_LINE_LENGTH_PCK, + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width + + ctrl->val); + + case V4L2_CID_LINK_FREQ: + if (sensor->streaming) + return -EBUSY; + + return smiapp_pll_update(sensor); + + default: + return -EINVAL; + } +} + +static const struct v4l2_ctrl_ops smiapp_ctrl_ops = { + .s_ctrl = smiapp_set_ctrl, +}; + +static int smiapp_init_controls(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + struct v4l2_ctrl_config cfg; + int rval; + + rval = v4l2_ctrl_handler_init(&sensor->pixel_array->ctrl_handler, 7); + if (rval) + return rval; + sensor->pixel_array->ctrl_handler.lock = &sensor->mutex; + + sensor->analog_gain = v4l2_ctrl_new_std( + &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, + sensor->limits[SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MIN], + sensor->limits[SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MAX], + max(sensor->limits[SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_STEP], 1U), + sensor->limits[SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MIN]); + + /* Exposure limits will be updated soon, use just something here. */ + sensor->exposure = v4l2_ctrl_new_std( + &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, + V4L2_CID_EXPOSURE, 0, 0, 1, 0); + + sensor->hflip = v4l2_ctrl_new_std( + &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + sensor->vflip = v4l2_ctrl_new_std( + &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + + sensor->vblank = v4l2_ctrl_new_std( + &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, + V4L2_CID_VBLANK, 0, 1, 1, 0); + + if (sensor->vblank) + sensor->vblank->flags |= V4L2_CTRL_FLAG_UPDATE; + + sensor->hblank = v4l2_ctrl_new_std( + &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, + V4L2_CID_HBLANK, 0, 1, 1, 0); + + if (sensor->hblank) + sensor->hblank->flags |= V4L2_CTRL_FLAG_UPDATE; + + sensor->pixel_rate_parray = v4l2_ctrl_new_std( + &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, + V4L2_CID_PIXEL_RATE, 0, 0, 1, 0); + + if (sensor->pixel_array->ctrl_handler.error) { + dev_err(&client->dev, + "pixel array controls initialization failed (%d)\n", + sensor->pixel_array->ctrl_handler.error); + rval = sensor->pixel_array->ctrl_handler.error; + goto error; + } + + sensor->pixel_array->sd.ctrl_handler = + &sensor->pixel_array->ctrl_handler; + + v4l2_ctrl_cluster(2, &sensor->hflip); + + rval = v4l2_ctrl_handler_init(&sensor->src->ctrl_handler, 0); + if (rval) + goto error; + sensor->src->ctrl_handler.lock = &sensor->mutex; + + memset(&cfg, 0, sizeof(cfg)); + + cfg.ops = &smiapp_ctrl_ops; + cfg.id = V4L2_CID_LINK_FREQ; + cfg.type = V4L2_CTRL_TYPE_INTEGER_MENU; + while (sensor->platform_data->op_sys_clock[cfg.max + 1]) + cfg.max++; + cfg.qmenu_int = sensor->platform_data->op_sys_clock; + + sensor->link_freq = v4l2_ctrl_new_custom( + &sensor->src->ctrl_handler, &cfg, NULL); + + sensor->pixel_rate_csi = v4l2_ctrl_new_std( + &sensor->src->ctrl_handler, &smiapp_ctrl_ops, + V4L2_CID_PIXEL_RATE, 0, 0, 1, 0); + + if (sensor->src->ctrl_handler.error) { + dev_err(&client->dev, + "src controls initialization failed (%d)\n", + sensor->src->ctrl_handler.error); + rval = sensor->src->ctrl_handler.error; + goto error; + } + + sensor->src->sd.ctrl_handler = + &sensor->src->ctrl_handler; + + return 0; + +error: + v4l2_ctrl_handler_free(&sensor->pixel_array->ctrl_handler); + v4l2_ctrl_handler_free(&sensor->src->ctrl_handler); + + return rval; +} + +static void smiapp_free_controls(struct smiapp_sensor *sensor) +{ + unsigned int i; + + for (i = 0; i < sensor->ssds_used; i++) + v4l2_ctrl_handler_free(&sensor->ssds[i].ctrl_handler); +} + +static int smiapp_get_limits(struct smiapp_sensor *sensor, int const *limit, + unsigned int n) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + unsigned int i; + u32 val; + int rval; + + for (i = 0; i < n; i++) { + rval = smiapp_read( + client, smiapp_reg_limits[limit[i]].addr, &val); + if (rval) + return rval; + sensor->limits[limit[i]] = val; + dev_dbg(&client->dev, "0x%8.8x \"%s\" = %d, 0x%x\n", + smiapp_reg_limits[limit[i]].addr, + smiapp_reg_limits[limit[i]].what, val, val); + } + + return 0; +} + +static int smiapp_get_all_limits(struct smiapp_sensor *sensor) +{ + unsigned int i; + int rval; + + for (i = 0; i < SMIAPP_LIMIT_LAST; i++) { + rval = smiapp_get_limits(sensor, &i, 1); + if (rval < 0) + return rval; + } + + if (sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN] == 0) + smiapp_replace_limit(sensor, SMIAPP_LIMIT_SCALER_N_MIN, 16); + + return 0; +} + +static int smiapp_get_limits_binning(struct smiapp_sensor *sensor) +{ + static u32 const limits[] = { + SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN, + SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES_BIN, + SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN, + SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK_BIN, + SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN, + SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MIN_BIN, + SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN, + }; + static u32 const limits_replace[] = { + SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES, + SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES, + SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK, + SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK, + SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK, + SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MIN, + SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MAX_MARGIN, + }; + + if (sensor->limits[SMIAPP_LIMIT_BINNING_CAPABILITY] == + SMIAPP_BINNING_CAPABILITY_NO) { + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(limits); i++) + sensor->limits[limits[i]] = + sensor->limits[limits_replace[i]]; + + return 0; + } + + return smiapp_get_limits(sensor, limits, ARRAY_SIZE(limits)); +} + +static int smiapp_get_mbus_formats(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + unsigned int type, n; + unsigned int i, pixel_order; + int rval; + + rval = smiapp_read( + client, SMIAPP_REG_U8_DATA_FORMAT_MODEL_TYPE, &type); + if (rval) + return rval; + + dev_dbg(&client->dev, "data_format_model_type %d\n", type); + + rval = smiapp_read(client, SMIAPP_REG_U8_PIXEL_ORDER, + &pixel_order); + if (rval) + return rval; + + if (pixel_order >= ARRAY_SIZE(pixel_order_str)) { + dev_dbg(&client->dev, "bad pixel order %d\n", pixel_order); + return -EINVAL; + } + + dev_dbg(&client->dev, "pixel order %d (%s)\n", pixel_order, + pixel_order_str[pixel_order]); + + switch (type) { + case SMIAPP_DATA_FORMAT_MODEL_TYPE_NORMAL: + n = SMIAPP_DATA_FORMAT_MODEL_TYPE_NORMAL_N; + break; + case SMIAPP_DATA_FORMAT_MODEL_TYPE_EXTENDED: + n = SMIAPP_DATA_FORMAT_MODEL_TYPE_EXTENDED_N; + break; + default: + return -EINVAL; + } + + sensor->default_pixel_order = pixel_order; + sensor->mbus_frame_fmts = 0; + + for (i = 0; i < n; i++) { + unsigned int fmt, j; + + rval = smiapp_read( + client, + SMIAPP_REG_U16_DATA_FORMAT_DESCRIPTOR(i), &fmt); + if (rval) + return rval; + + dev_dbg(&client->dev, "bpp %d, compressed %d\n", + fmt >> 8, (u8)fmt); + + for (j = 0; j < ARRAY_SIZE(smiapp_csi_data_formats); j++) { + const struct smiapp_csi_data_format *f = + &smiapp_csi_data_formats[j]; + + if (f->pixel_order != SMIAPP_PIXEL_ORDER_GRBG) + continue; + + if (f->width != fmt >> 8 || f->compressed != (u8)fmt) + continue; + + dev_dbg(&client->dev, "jolly good! %d\n", j); + + sensor->default_mbus_frame_fmts |= 1 << j; + if (!sensor->csi_format) { + sensor->csi_format = f; + sensor->internal_csi_format = f; + } + } + } + + if (!sensor->csi_format) { + dev_err(&client->dev, "no supported mbus code found\n"); + return -EINVAL; + } + + smiapp_update_mbus_formats(sensor); + + return 0; +} + +static void smiapp_update_blanking(struct smiapp_sensor *sensor) +{ + struct v4l2_ctrl *vblank = sensor->vblank; + struct v4l2_ctrl *hblank = sensor->hblank; + + vblank->minimum = + max_t(int, + sensor->limits[SMIAPP_LIMIT_MIN_FRAME_BLANKING_LINES], + sensor->limits[SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN] - + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height); + vblank->maximum = + sensor->limits[SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES_BIN] - + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height; + + vblank->val = clamp_t(int, vblank->val, + vblank->minimum, vblank->maximum); + vblank->default_value = vblank->minimum; + vblank->val = vblank->val; + vblank->cur.val = vblank->val; + + hblank->minimum = + max_t(int, + sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN] - + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width, + sensor->limits[SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN]); + hblank->maximum = + sensor->limits[SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK_BIN] - + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width; + + hblank->val = clamp_t(int, hblank->val, + hblank->minimum, hblank->maximum); + hblank->default_value = hblank->minimum; + hblank->val = hblank->val; + hblank->cur.val = hblank->val; + + __smiapp_update_exposure_limits(sensor); +} + +static int smiapp_update_mode(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + unsigned int binning_mode; + int rval; + + dev_dbg(&client->dev, "frame size: %dx%d\n", + sensor->src->crop[SMIAPP_PAD_SRC].width, + sensor->src->crop[SMIAPP_PAD_SRC].height); + dev_dbg(&client->dev, "csi format width: %d\n", + sensor->csi_format->width); + + /* Binning has to be set up here; it affects limits */ + if (sensor->binning_horizontal == 1 && + sensor->binning_vertical == 1) { + binning_mode = 0; + } else { + u8 binning_type = + (sensor->binning_horizontal << 4) + | sensor->binning_vertical; + + rval = smiapp_write( + client, SMIAPP_REG_U8_BINNING_TYPE, binning_type); + if (rval < 0) + return rval; + + binning_mode = 1; + } + rval = smiapp_write(client, SMIAPP_REG_U8_BINNING_MODE, binning_mode); + if (rval < 0) + return rval; + + /* Get updated limits due to binning */ + rval = smiapp_get_limits_binning(sensor); + if (rval < 0) + return rval; + + rval = smiapp_pll_update(sensor); + if (rval < 0) + return rval; + + /* Output from pixel array, including blanking */ + smiapp_update_blanking(sensor); + + dev_dbg(&client->dev, "vblank\t\t%d\n", sensor->vblank->val); + dev_dbg(&client->dev, "hblank\t\t%d\n", sensor->hblank->val); + + dev_dbg(&client->dev, "real timeperframe\t100/%d\n", + sensor->pll.vt_pix_clk_freq_hz / + ((sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width + + sensor->hblank->val) * + (sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height + + sensor->vblank->val) / 100)); + + return 0; +} + +/* + * + * SMIA++ NVM handling + * + */ +static int smiapp_read_nvm(struct smiapp_sensor *sensor, + unsigned char *nvm) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + u32 i, s, p, np, v; + int rval, rval2; + + np = sensor->nvm_size / SMIAPP_NVM_PAGE_SIZE; + for (p = 0; p < np; p++) { + rval = smiapp_write( + client, + SMIAPP_REG_U8_DATA_TRANSFER_IF_1_PAGE_SELECT, p); + if (rval) + goto out; + + rval = smiapp_write(client, + SMIAPP_REG_U8_DATA_TRANSFER_IF_1_CTRL, + SMIAPP_DATA_TRANSFER_IF_1_CTRL_EN | + SMIAPP_DATA_TRANSFER_IF_1_CTRL_RD_EN); + if (rval) + goto out; + + for (i = 0; i < 1000; i++) { + rval = smiapp_read( + client, + SMIAPP_REG_U8_DATA_TRANSFER_IF_1_STATUS, &s); + + if (rval) + goto out; + + if (s & SMIAPP_DATA_TRANSFER_IF_1_STATUS_RD_READY) + break; + + if (--i == 0) { + rval = -ETIMEDOUT; + goto out; + } + + } + + for (i = 0; i < SMIAPP_NVM_PAGE_SIZE; i++) { + rval = smiapp_read( + client, + SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_0 + i, + &v); + if (rval) + goto out; + + *nvm++ = v; + } + } + +out: + rval2 = smiapp_write(client, SMIAPP_REG_U8_DATA_TRANSFER_IF_1_CTRL, 0); + if (rval < 0) + return rval; + else + return rval2; +} + +/* + * + * SMIA++ CCI address control + * + */ +static int smiapp_change_cci_addr(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int rval; + u32 val; + + client->addr = sensor->platform_data->i2c_addr_dfl; + + rval = smiapp_write(client, + SMIAPP_REG_U8_CCI_ADDRESS_CONTROL, + sensor->platform_data->i2c_addr_alt << 1); + if (rval) + return rval; + + client->addr = sensor->platform_data->i2c_addr_alt; + + /* verify addr change went ok */ + rval = smiapp_read(client, SMIAPP_REG_U8_CCI_ADDRESS_CONTROL, &val); + if (rval) + return rval; + + if (val != sensor->platform_data->i2c_addr_alt << 1) + return -ENODEV; + + return 0; +} + +/* + * + * SMIA++ Mode Control + * + */ +static int smiapp_setup_flash_strobe(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + struct smiapp_flash_strobe_parms *strobe_setup; + unsigned int ext_freq = sensor->platform_data->ext_clk; + u32 tmp; + u32 strobe_adjustment; + u32 strobe_width_high_rs; + int rval; + + strobe_setup = sensor->platform_data->strobe_setup; + + /* + * How to calculate registers related to strobe length. Please + * do not change, or if you do at least know what you're + * doing. :-) + * + * Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> 2010-10-25 + * + * flash_strobe_length [us] / 10^6 = (tFlash_strobe_width_ctrl + * / EXTCLK freq [Hz]) * flash_strobe_adjustment + * + * tFlash_strobe_width_ctrl E N, [1 - 0xffff] + * flash_strobe_adjustment E N, [1 - 0xff] + * + * The formula above is written as below to keep it on one + * line: + * + * l / 10^6 = w / e * a + * + * Let's mark w * a by x: + * + * x = w * a + * + * Thus, we get: + * + * x = l * e / 10^6 + * + * The strobe width must be at least as long as requested, + * thus rounding upwards is needed. + * + * x = (l * e + 10^6 - 1) / 10^6 + * ----------------------------- + * + * Maximum possible accuracy is wanted at all times. Thus keep + * a as small as possible. + * + * Calculate a, assuming maximum w, with rounding upwards: + * + * a = (x + (2^16 - 1) - 1) / (2^16 - 1) + * ------------------------------------- + * + * Thus, we also get w, with that a, with rounding upwards: + * + * w = (x + a - 1) / a + * ------------------- + * + * To get limits: + * + * x E [1, (2^16 - 1) * (2^8 - 1)] + * + * Substituting maximum x to the original formula (with rounding), + * the maximum l is thus + * + * (2^16 - 1) * (2^8 - 1) * 10^6 = l * e + 10^6 - 1 + * + * l = (10^6 * (2^16 - 1) * (2^8 - 1) - 10^6 + 1) / e + * -------------------------------------------------- + * + * flash_strobe_length must be clamped between 1 and + * (10^6 * (2^16 - 1) * (2^8 - 1) - 10^6 + 1) / EXTCLK freq. + * + * Then, + * + * flash_strobe_adjustment = ((flash_strobe_length * + * EXTCLK freq + 10^6 - 1) / 10^6 + (2^16 - 1) - 1) / (2^16 - 1) + * + * tFlash_strobe_width_ctrl = ((flash_strobe_length * + * EXTCLK freq + 10^6 - 1) / 10^6 + + * flash_strobe_adjustment - 1) / flash_strobe_adjustment + */ + tmp = div_u64(1000000ULL * ((1 << 16) - 1) * ((1 << 8) - 1) - + 1000000 + 1, ext_freq); + strobe_setup->strobe_width_high_us = + clamp_t(u32, strobe_setup->strobe_width_high_us, 1, tmp); + + tmp = div_u64(((u64)strobe_setup->strobe_width_high_us * (u64)ext_freq + + 1000000 - 1), 1000000ULL); + strobe_adjustment = (tmp + (1 << 16) - 1 - 1) / ((1 << 16) - 1); + strobe_width_high_rs = (tmp + strobe_adjustment - 1) / + strobe_adjustment; + + rval = smiapp_write(client, SMIAPP_REG_U8_FLASH_MODE_RS, + strobe_setup->mode); + if (rval < 0) + goto out; + + rval = smiapp_write(client, SMIAPP_REG_U8_FLASH_STROBE_ADJUSTMENT, + strobe_adjustment); + if (rval < 0) + goto out; + + rval = smiapp_write( + client, SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_HIGH_RS_CTRL, + strobe_width_high_rs); + if (rval < 0) + goto out; + + rval = smiapp_write(client, SMIAPP_REG_U16_TFLASH_STROBE_DELAY_RS_CTRL, + strobe_setup->strobe_delay); + if (rval < 0) + goto out; + + rval = smiapp_write(client, SMIAPP_REG_U16_FLASH_STROBE_START_POINT, + strobe_setup->stobe_start_point); + if (rval < 0) + goto out; + + rval = smiapp_write(client, SMIAPP_REG_U8_FLASH_TRIGGER_RS, + strobe_setup->trigger); + +out: + sensor->platform_data->strobe_setup->trigger = 0; + + return rval; +} + +/* ----------------------------------------------------------------------------- + * Power management + */ + +static int smiapp_power_on(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + unsigned int sleep; + int rval; + + rval = regulator_enable(sensor->vana); + if (rval) { + dev_err(&client->dev, "failed to enable vana regulator\n"); + return rval; + } + usleep_range(1000, 1000); + + rval = sensor->platform_data->set_xclk(&sensor->src->sd, + sensor->platform_data->ext_clk); + if (rval < 0) { + dev_dbg(&client->dev, "failed to set xclk\n"); + goto out_xclk_fail; + } + usleep_range(1000, 1000); + + if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) + gpio_set_value(sensor->platform_data->xshutdown, 1); + + sleep = SMIAPP_RESET_DELAY(sensor->platform_data->ext_clk); + usleep_range(sleep, sleep); + + /* + * Failures to respond to the address change command have been noticed. + * Those failures seem to be caused by the sensor requiring a longer + * boot time than advertised. An additional 10ms delay seems to work + * around the issue, but the SMIA++ I2C write retry hack makes the delay + * unnecessary. The failures need to be investigated to find a proper + * fix, and a delay will likely need to be added here if the I2C write + * retry hack is reverted before the root cause of the boot time issue + * is found. + */ + + if (sensor->platform_data->i2c_addr_alt) { + rval = smiapp_change_cci_addr(sensor); + if (rval) { + dev_err(&client->dev, "cci address change error\n"); + goto out_cci_addr_fail; + } + } + + rval = smiapp_write(client, SMIAPP_REG_U8_SOFTWARE_RESET, + SMIAPP_SOFTWARE_RESET); + if (rval < 0) { + dev_err(&client->dev, "software reset failed\n"); + goto out_cci_addr_fail; + } + + if (sensor->platform_data->i2c_addr_alt) { + rval = smiapp_change_cci_addr(sensor); + if (rval) { + dev_err(&client->dev, "cci address change error\n"); + goto out_cci_addr_fail; + } + } + + rval = smiapp_write(client, SMIAPP_REG_U16_COMPRESSION_MODE, + SMIAPP_COMPRESSION_MODE_SIMPLE_PREDICTOR); + if (rval) { + dev_err(&client->dev, "compression mode set failed\n"); + goto out_cci_addr_fail; + } + + rval = smiapp_write( + client, SMIAPP_REG_U16_EXTCLK_FREQUENCY_MHZ, + sensor->platform_data->ext_clk / (1000000 / (1 << 8))); + if (rval) { + dev_err(&client->dev, "extclk frequency set failed\n"); + goto out_cci_addr_fail; + } + + rval = smiapp_write(client, SMIAPP_REG_U8_CSI_LANE_MODE, + sensor->platform_data->lanes - 1); + if (rval) { + dev_err(&client->dev, "csi lane mode set failed\n"); + goto out_cci_addr_fail; + } + + rval = smiapp_write(client, SMIAPP_REG_U8_FAST_STANDBY_CTRL, + SMIAPP_FAST_STANDBY_CTRL_IMMEDIATE); + if (rval) { + dev_err(&client->dev, "fast standby set failed\n"); + goto out_cci_addr_fail; + } + + rval = smiapp_write(client, SMIAPP_REG_U8_CSI_SIGNALLING_MODE, + sensor->platform_data->csi_signalling_mode); + if (rval) { + dev_err(&client->dev, "csi signalling mode set failed\n"); + goto out_cci_addr_fail; + } + + /* DPHY control done by sensor based on requested link rate */ + rval = smiapp_write(client, SMIAPP_REG_U8_DPHY_CTRL, + SMIAPP_DPHY_CTRL_UI); + if (rval < 0) + return rval; + + rval = smiapp_call_quirk(sensor, post_poweron); + if (rval) { + dev_err(&client->dev, "post_poweron quirks failed\n"); + goto out_cci_addr_fail; + } + + /* Are we still initialising...? If yes, return here. */ + if (!sensor->pixel_array) + return 0; + + rval = v4l2_ctrl_handler_setup( + &sensor->pixel_array->ctrl_handler); + if (rval) + goto out_cci_addr_fail; + + rval = v4l2_ctrl_handler_setup(&sensor->src->ctrl_handler); + if (rval) + goto out_cci_addr_fail; + + mutex_lock(&sensor->mutex); + rval = smiapp_update_mode(sensor); + mutex_unlock(&sensor->mutex); + if (rval < 0) + goto out_cci_addr_fail; + + return 0; + +out_cci_addr_fail: + if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) + gpio_set_value(sensor->platform_data->xshutdown, 0); + sensor->platform_data->set_xclk(&sensor->src->sd, 0); + +out_xclk_fail: + regulator_disable(sensor->vana); + return rval; +} + +static void smiapp_power_off(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + + /* + * Currently power/clock to lens are enable/disabled separately + * but they are essentially the same signals. So if the sensor is + * powered off while the lens is powered on the sensor does not + * really see a power off and next time the cci address change + * will fail. So do a soft reset explicitly here. + */ + if (sensor->platform_data->i2c_addr_alt) + smiapp_write(client, + SMIAPP_REG_U8_SOFTWARE_RESET, + SMIAPP_SOFTWARE_RESET); + + if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) + gpio_set_value(sensor->platform_data->xshutdown, 0); + sensor->platform_data->set_xclk(&sensor->src->sd, 0); + usleep_range(5000, 5000); + regulator_disable(sensor->vana); + sensor->streaming = 0; +} + +static int smiapp_set_power(struct v4l2_subdev *subdev, int on) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + int ret = 0; + + mutex_lock(&sensor->power_mutex); + + /* + * If the power count is modified from 0 to != 0 or from != 0 + * to 0, update the power state. + */ + if (!sensor->power_count == !on) + goto out; + + if (on) { + /* Power on and perform initialisation. */ + ret = smiapp_power_on(sensor); + if (ret < 0) + goto out; + } else { + smiapp_power_off(sensor); + } + + /* Update the power count. */ + sensor->power_count += on ? 1 : -1; + WARN_ON(sensor->power_count < 0); + +out: + mutex_unlock(&sensor->power_mutex); + return ret; +} + +/* ----------------------------------------------------------------------------- + * Video stream management + */ + +static int smiapp_start_streaming(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int rval; + + mutex_lock(&sensor->mutex); + + rval = smiapp_write(client, SMIAPP_REG_U16_CSI_DATA_FORMAT, + (sensor->csi_format->width << 8) | + sensor->csi_format->compressed); + if (rval) + goto out; + + rval = smiapp_pll_configure(sensor); + if (rval) + goto out; + + /* Analog crop start coordinates */ + rval = smiapp_write(client, SMIAPP_REG_U16_X_ADDR_START, + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].left); + if (rval < 0) + goto out; + + rval = smiapp_write(client, SMIAPP_REG_U16_Y_ADDR_START, + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].top); + if (rval < 0) + goto out; + + /* Analog crop end coordinates */ + rval = smiapp_write( + client, SMIAPP_REG_U16_X_ADDR_END, + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].left + + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width - 1); + if (rval < 0) + goto out; + + rval = smiapp_write( + client, SMIAPP_REG_U16_Y_ADDR_END, + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].top + + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height - 1); + if (rval < 0) + goto out; + + /* + * Output from pixel array, including blanking, is set using + * controls below. No need to set here. + */ + + /* Digital crop */ + if (sensor->limits[SMIAPP_LIMIT_DIGITAL_CROP_CAPABILITY] + == SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP) { + rval = smiapp_write( + client, SMIAPP_REG_U16_DIGITAL_CROP_X_OFFSET, + sensor->scaler->crop[SMIAPP_PAD_SINK].left); + if (rval < 0) + goto out; + + rval = smiapp_write( + client, SMIAPP_REG_U16_DIGITAL_CROP_Y_OFFSET, + sensor->scaler->crop[SMIAPP_PAD_SINK].top); + if (rval < 0) + goto out; + + rval = smiapp_write( + client, SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_WIDTH, + sensor->scaler->crop[SMIAPP_PAD_SINK].width); + if (rval < 0) + goto out; + + rval = smiapp_write( + client, SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_HEIGHT, + sensor->scaler->crop[SMIAPP_PAD_SINK].height); + if (rval < 0) + goto out; + } + + /* Scaling */ + if (sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY] + != SMIAPP_SCALING_CAPABILITY_NONE) { + rval = smiapp_write(client, SMIAPP_REG_U16_SCALING_MODE, + sensor->scaling_mode); + if (rval < 0) + goto out; + + rval = smiapp_write(client, SMIAPP_REG_U16_SCALE_M, + sensor->scale_m); + if (rval < 0) + goto out; + } + + /* Output size from sensor */ + rval = smiapp_write(client, SMIAPP_REG_U16_X_OUTPUT_SIZE, + sensor->src->crop[SMIAPP_PAD_SRC].width); + if (rval < 0) + goto out; + rval = smiapp_write(client, SMIAPP_REG_U16_Y_OUTPUT_SIZE, + sensor->src->crop[SMIAPP_PAD_SRC].height); + if (rval < 0) + goto out; + + if ((sensor->flash_capability & + (SMIAPP_FLASH_MODE_CAPABILITY_SINGLE_STROBE | + SMIAPP_FLASH_MODE_CAPABILITY_MULTIPLE_STROBE)) && + sensor->platform_data->strobe_setup != NULL && + sensor->platform_data->strobe_setup->trigger != 0) { + rval = smiapp_setup_flash_strobe(sensor); + if (rval) + goto out; + } + + rval = smiapp_call_quirk(sensor, pre_streamon); + if (rval) { + dev_err(&client->dev, "pre_streamon quirks failed\n"); + goto out; + } + + rval = smiapp_write(client, SMIAPP_REG_U8_MODE_SELECT, + SMIAPP_MODE_SELECT_STREAMING); + +out: + mutex_unlock(&sensor->mutex); + + return rval; +} + +static int smiapp_stop_streaming(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int rval; + + mutex_lock(&sensor->mutex); + rval = smiapp_write(client, SMIAPP_REG_U8_MODE_SELECT, + SMIAPP_MODE_SELECT_SOFTWARE_STANDBY); + if (rval) + goto out; + + rval = smiapp_call_quirk(sensor, post_streamoff); + if (rval) + dev_err(&client->dev, "post_streamoff quirks failed\n"); + +out: + mutex_unlock(&sensor->mutex); + return rval; +} + +/* ----------------------------------------------------------------------------- + * V4L2 subdev video operations + */ + +static int smiapp_set_stream(struct v4l2_subdev *subdev, int enable) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + int rval; + + if (sensor->streaming == enable) + return 0; + + if (enable) { + sensor->streaming = 1; + rval = smiapp_start_streaming(sensor); + if (rval < 0) + sensor->streaming = 0; + } else { + rval = smiapp_stop_streaming(sensor); + sensor->streaming = 0; + } + + return rval; +} + +static int smiapp_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct i2c_client *client = v4l2_get_subdevdata(subdev); + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + unsigned int i; + int idx = -1; + int rval = -EINVAL; + + mutex_lock(&sensor->mutex); + + dev_err(&client->dev, "subdev %s, pad %d, index %d\n", + subdev->name, code->pad, code->index); + + if (subdev != &sensor->src->sd || code->pad != SMIAPP_PAD_SRC) { + if (code->index) + goto out; + + code->code = sensor->internal_csi_format->code; + rval = 0; + goto out; + } + + for (i = 0; i < ARRAY_SIZE(smiapp_csi_data_formats); i++) { + if (sensor->mbus_frame_fmts & (1 << i)) + idx++; + + if (idx == code->index) { + code->code = smiapp_csi_data_formats[i].code; + dev_err(&client->dev, "found index %d, i %d, code %x\n", + code->index, i, code->code); + rval = 0; + break; + } + } + +out: + mutex_unlock(&sensor->mutex); + + return rval; +} + +static u32 __smiapp_get_mbus_code(struct v4l2_subdev *subdev, + unsigned int pad) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + + if (subdev == &sensor->src->sd && pad == SMIAPP_PAD_SRC) + return sensor->csi_format->code; + else + return sensor->internal_csi_format->code; +} + +static int __smiapp_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + fmt->format = *v4l2_subdev_get_try_format(fh, fmt->pad); + } else { + struct v4l2_rect *r; + + if (fmt->pad == ssd->source_pad) + r = &ssd->crop[ssd->source_pad]; + else + r = &ssd->sink_fmt; + + fmt->format.code = __smiapp_get_mbus_code(subdev, fmt->pad); + fmt->format.width = r->width; + fmt->format.height = r->height; + } + + return 0; +} + +static int smiapp_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + int rval; + + mutex_lock(&sensor->mutex); + rval = __smiapp_get_format(subdev, fh, fmt); + mutex_unlock(&sensor->mutex); + + return rval; +} + +static void smiapp_get_crop_compose(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_rect **crops, + struct v4l2_rect **comps, int which) +{ + struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); + unsigned int i; + + if (which == V4L2_SUBDEV_FORMAT_ACTIVE) { + if (crops) + for (i = 0; i < subdev->entity.num_pads; i++) + crops[i] = &ssd->crop[i]; + if (comps) + *comps = &ssd->compose; + } else { + if (crops) { + for (i = 0; i < subdev->entity.num_pads; i++) { + crops[i] = v4l2_subdev_get_try_crop(fh, i); + BUG_ON(!crops[i]); + } + } + if (comps) { + *comps = v4l2_subdev_get_try_compose(fh, + SMIAPP_PAD_SINK); + BUG_ON(!*comps); + } + } +} + +/* Changes require propagation only on sink pad. */ +static void smiapp_propagate(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, int which, + int target) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); + struct v4l2_rect *comp, *crops[SMIAPP_PADS]; + + smiapp_get_crop_compose(subdev, fh, crops, &comp, which); + + switch (target) { + case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL: + comp->width = crops[SMIAPP_PAD_SINK]->width; + comp->height = crops[SMIAPP_PAD_SINK]->height; + if (which == V4L2_SUBDEV_FORMAT_ACTIVE) { + if (ssd == sensor->scaler) { + sensor->scale_m = + sensor->limits[ + SMIAPP_LIMIT_SCALER_N_MIN]; + sensor->scaling_mode = + SMIAPP_SCALING_MODE_NONE; + } else if (ssd == sensor->binner) { + sensor->binning_horizontal = 1; + sensor->binning_vertical = 1; + } + } + /* Fall through */ + case V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL: + *crops[SMIAPP_PAD_SRC] = *comp; + break; + default: + BUG(); + } +} + +static const struct smiapp_csi_data_format +*smiapp_validate_csi_data_format(struct smiapp_sensor *sensor, u32 code) +{ + const struct smiapp_csi_data_format *csi_format = sensor->csi_format; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(smiapp_csi_data_formats); i++) { + if (sensor->mbus_frame_fmts & (1 << i) + && smiapp_csi_data_formats[i].code == code) + return &smiapp_csi_data_formats[i]; + } + + return csi_format; +} + +static int smiapp_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); + struct v4l2_rect *crops[SMIAPP_PADS]; + + mutex_lock(&sensor->mutex); + + /* + * Media bus code is changeable on src subdev's source pad. On + * other source pads we just get format here. + */ + if (fmt->pad == ssd->source_pad) { + u32 code = fmt->format.code; + int rval = __smiapp_get_format(subdev, fh, fmt); + + if (!rval && subdev == &sensor->src->sd) { + const struct smiapp_csi_data_format *csi_format = + smiapp_validate_csi_data_format(sensor, code); + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) + sensor->csi_format = csi_format; + fmt->format.code = csi_format->code; + } + + mutex_unlock(&sensor->mutex); + return rval; + } + + /* Sink pad. Width and height are changeable here. */ + fmt->format.code = __smiapp_get_mbus_code(subdev, fmt->pad); + fmt->format.width &= ~1; + fmt->format.height &= ~1; + + fmt->format.width = + clamp(fmt->format.width, + sensor->limits[SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE], + sensor->limits[SMIAPP_LIMIT_MAX_X_OUTPUT_SIZE]); + fmt->format.height = + clamp(fmt->format.height, + sensor->limits[SMIAPP_LIMIT_MIN_Y_OUTPUT_SIZE], + sensor->limits[SMIAPP_LIMIT_MAX_Y_OUTPUT_SIZE]); + + smiapp_get_crop_compose(subdev, fh, crops, NULL, fmt->which); + + crops[ssd->sink_pad]->left = 0; + crops[ssd->sink_pad]->top = 0; + crops[ssd->sink_pad]->width = fmt->format.width; + crops[ssd->sink_pad]->height = fmt->format.height; + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) + ssd->sink_fmt = *crops[ssd->sink_pad]; + smiapp_propagate(subdev, fh, fmt->which, + V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL); + + mutex_unlock(&sensor->mutex); + + return 0; +} + +/* + * Calculate goodness of scaled image size compared to expected image + * size and flags provided. + */ +#define SCALING_GOODNESS 100000 +#define SCALING_GOODNESS_EXTREME 100000000 +static int scaling_goodness(struct v4l2_subdev *subdev, int w, int ask_w, + int h, int ask_h, u32 flags) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + struct i2c_client *client = v4l2_get_subdevdata(subdev); + int val = 0; + + w &= ~1; + ask_w &= ~1; + h &= ~1; + ask_h &= ~1; + + if (flags & V4L2_SUBDEV_SEL_FLAG_SIZE_GE) { + if (w < ask_w) + val -= SCALING_GOODNESS; + if (h < ask_h) + val -= SCALING_GOODNESS; + } + + if (flags & V4L2_SUBDEV_SEL_FLAG_SIZE_LE) { + if (w > ask_w) + val -= SCALING_GOODNESS; + if (h > ask_h) + val -= SCALING_GOODNESS; + } + + val -= abs(w - ask_w); + val -= abs(h - ask_h); + + if (w < sensor->limits[SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE]) + val -= SCALING_GOODNESS_EXTREME; + + dev_dbg(&client->dev, "w %d ask_w %d h %d ask_h %d goodness %d\n", + w, ask_h, h, ask_h, val); + + return val; +} + +static void smiapp_set_compose_binner(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel, + struct v4l2_rect **crops, + struct v4l2_rect *comp) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + unsigned int i; + unsigned int binh = 1, binv = 1; + unsigned int best = scaling_goodness( + subdev, + crops[SMIAPP_PAD_SINK]->width, sel->r.width, + crops[SMIAPP_PAD_SINK]->height, sel->r.height, sel->flags); + + for (i = 0; i < sensor->nbinning_subtypes; i++) { + int this = scaling_goodness( + subdev, + crops[SMIAPP_PAD_SINK]->width + / sensor->binning_subtypes[i].horizontal, + sel->r.width, + crops[SMIAPP_PAD_SINK]->height + / sensor->binning_subtypes[i].vertical, + sel->r.height, sel->flags); + + if (this > best) { + binh = sensor->binning_subtypes[i].horizontal; + binv = sensor->binning_subtypes[i].vertical; + best = this; + } + } + if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + sensor->binning_vertical = binv; + sensor->binning_horizontal = binh; + } + + sel->r.width = (crops[SMIAPP_PAD_SINK]->width / binh) & ~1; + sel->r.height = (crops[SMIAPP_PAD_SINK]->height / binv) & ~1; +} + +/* + * Calculate best scaling ratio and mode for given output resolution. + * + * Try all of these: horizontal ratio, vertical ratio and smallest + * size possible (horizontally). + * + * Also try whether horizontal scaler or full scaler gives a better + * result. + */ +static void smiapp_set_compose_scaler(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel, + struct v4l2_rect **crops, + struct v4l2_rect *comp) +{ + struct i2c_client *client = v4l2_get_subdevdata(subdev); + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + u32 min, max, a, b, max_m; + u32 scale_m = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]; + int mode = SMIAPP_SCALING_MODE_HORIZONTAL; + u32 try[4]; + u32 ntry = 0; + unsigned int i; + int best = INT_MIN; + + sel->r.width = min_t(unsigned int, sel->r.width, + crops[SMIAPP_PAD_SINK]->width); + sel->r.height = min_t(unsigned int, sel->r.height, + crops[SMIAPP_PAD_SINK]->height); + + a = crops[SMIAPP_PAD_SINK]->width + * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN] / sel->r.width; + b = crops[SMIAPP_PAD_SINK]->height + * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN] / sel->r.height; + max_m = crops[SMIAPP_PAD_SINK]->width + * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN] + / sensor->limits[SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE]; + + a = min(sensor->limits[SMIAPP_LIMIT_SCALER_M_MAX], + max(a, sensor->limits[SMIAPP_LIMIT_SCALER_M_MIN])); + b = min(sensor->limits[SMIAPP_LIMIT_SCALER_M_MAX], + max(b, sensor->limits[SMIAPP_LIMIT_SCALER_M_MIN])); + max_m = min(sensor->limits[SMIAPP_LIMIT_SCALER_M_MAX], + max(max_m, sensor->limits[SMIAPP_LIMIT_SCALER_M_MIN])); + + dev_dbg(&client->dev, "scaling: a %d b %d max_m %d\n", a, b, max_m); + + min = min(max_m, min(a, b)); + max = min(max_m, max(a, b)); + + try[ntry] = min; + ntry++; + if (min != max) { + try[ntry] = max; + ntry++; + } + if (max != max_m) { + try[ntry] = min + 1; + ntry++; + if (min != max) { + try[ntry] = max + 1; + ntry++; + } + } + + for (i = 0; i < ntry; i++) { + int this = scaling_goodness( + subdev, + crops[SMIAPP_PAD_SINK]->width + / try[i] + * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN], + sel->r.width, + crops[SMIAPP_PAD_SINK]->height, + sel->r.height, + sel->flags); + + dev_dbg(&client->dev, "trying factor %d (%d)\n", try[i], i); + + if (this > best) { + scale_m = try[i]; + mode = SMIAPP_SCALING_MODE_HORIZONTAL; + best = this; + } + + if (sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY] + == SMIAPP_SCALING_CAPABILITY_HORIZONTAL) + continue; + + this = scaling_goodness( + subdev, crops[SMIAPP_PAD_SINK]->width + / try[i] + * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN], + sel->r.width, + crops[SMIAPP_PAD_SINK]->height + / try[i] + * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN], + sel->r.height, + sel->flags); + + if (this > best) { + scale_m = try[i]; + mode = SMIAPP_SCALING_MODE_BOTH; + best = this; + } + } + + sel->r.width = + (crops[SMIAPP_PAD_SINK]->width + / scale_m + * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]) & ~1; + if (mode == SMIAPP_SCALING_MODE_BOTH) + sel->r.height = + (crops[SMIAPP_PAD_SINK]->height + / scale_m + * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]) + & ~1; + else + sel->r.height = crops[SMIAPP_PAD_SINK]->height; + + if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + sensor->scale_m = scale_m; + sensor->scaling_mode = mode; + } +} +/* We're only called on source pads. This function sets scaling. */ +static int smiapp_set_compose(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); + struct v4l2_rect *comp, *crops[SMIAPP_PADS]; + + smiapp_get_crop_compose(subdev, fh, crops, &comp, sel->which); + + sel->r.top = 0; + sel->r.left = 0; + + if (ssd == sensor->binner) + smiapp_set_compose_binner(subdev, fh, sel, crops, comp); + else + smiapp_set_compose_scaler(subdev, fh, sel, crops, comp); + + *comp = sel->r; + smiapp_propagate(subdev, fh, sel->which, + V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL); + + if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) + return smiapp_update_mode(sensor); + + return 0; +} + +static int __smiapp_sel_supported(struct v4l2_subdev *subdev, + struct v4l2_subdev_selection *sel) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); + + /* We only implement crop in three places. */ + switch (sel->target) { + case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL: + case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS: + if (ssd == sensor->pixel_array + && sel->pad == SMIAPP_PA_PAD_SRC) + return 0; + if (ssd == sensor->src + && sel->pad == SMIAPP_PAD_SRC) + return 0; + if (ssd == sensor->scaler + && sel->pad == SMIAPP_PAD_SINK + && sensor->limits[SMIAPP_LIMIT_DIGITAL_CROP_CAPABILITY] + == SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP) + return 0; + return -EINVAL; + case V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL: + case V4L2_SUBDEV_SEL_TGT_COMPOSE_BOUNDS: + if (sel->pad == ssd->source_pad) + return -EINVAL; + if (ssd == sensor->binner) + return 0; + if (ssd == sensor->scaler + && sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY] + != SMIAPP_SCALING_CAPABILITY_NONE) + return 0; + /* Fall through */ + default: + return -EINVAL; + } +} + +static int smiapp_set_crop(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); + struct v4l2_rect *src_size, *crops[SMIAPP_PADS]; + struct v4l2_rect _r; + + smiapp_get_crop_compose(subdev, fh, crops, NULL, sel->which); + + if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + if (sel->pad == ssd->sink_pad) + src_size = &ssd->sink_fmt; + else + src_size = &ssd->compose; + } else { + if (sel->pad == ssd->sink_pad) { + _r.left = 0; + _r.top = 0; + _r.width = v4l2_subdev_get_try_format(fh, sel->pad) + ->width; + _r.height = v4l2_subdev_get_try_format(fh, sel->pad) + ->height; + src_size = &_r; + } else { + src_size = + v4l2_subdev_get_try_compose( + fh, ssd->sink_pad); + } + } + + if (ssd == sensor->src && sel->pad == SMIAPP_PAD_SRC) { + sel->r.left = 0; + sel->r.top = 0; + } + + sel->r.width = min(sel->r.width, src_size->width); + sel->r.height = min(sel->r.height, src_size->height); + + sel->r.left = min(sel->r.left, src_size->width - sel->r.width); + sel->r.top = min(sel->r.top, src_size->height - sel->r.height); + + *crops[sel->pad] = sel->r; + + if (ssd != sensor->pixel_array && sel->pad == SMIAPP_PAD_SINK) + smiapp_propagate(subdev, fh, sel->which, + V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL); + + return 0; +} + +static int __smiapp_get_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); + struct v4l2_rect *comp, *crops[SMIAPP_PADS]; + struct v4l2_rect sink_fmt; + int ret; + + ret = __smiapp_sel_supported(subdev, sel); + if (ret) + return ret; + + smiapp_get_crop_compose(subdev, fh, crops, &comp, sel->which); + + if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + sink_fmt = ssd->sink_fmt; + } else { + struct v4l2_mbus_framefmt *fmt = + v4l2_subdev_get_try_format(fh, ssd->sink_pad); + + sink_fmt.left = 0; + sink_fmt.top = 0; + sink_fmt.width = fmt->width; + sink_fmt.height = fmt->height; + } + + switch (sel->target) { + case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS: + if (ssd == sensor->pixel_array) { + sel->r.width = + sensor->limits[SMIAPP_LIMIT_X_ADDR_MAX] + 1; + sel->r.height = + sensor->limits[SMIAPP_LIMIT_Y_ADDR_MAX] + 1; + } else if (sel->pad == ssd->sink_pad) { + sel->r = sink_fmt; + } else { + sel->r = *comp; + } + break; + case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL: + case V4L2_SUBDEV_SEL_TGT_COMPOSE_BOUNDS: + sel->r = *crops[sel->pad]; + break; + case V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL: + sel->r = *comp; + break; + } + + return 0; +} + +static int smiapp_get_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + int rval; + + mutex_lock(&sensor->mutex); + rval = __smiapp_get_selection(subdev, fh, sel); + mutex_unlock(&sensor->mutex); + + return rval; +} +static int smiapp_set_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + int ret; + + ret = __smiapp_sel_supported(subdev, sel); + if (ret) + return ret; + + mutex_lock(&sensor->mutex); + + sel->r.left = max(0, sel->r.left & ~1); + sel->r.top = max(0, sel->r.top & ~1); + sel->r.width = max(0, SMIAPP_ALIGN_DIM(sel->r.width, sel->flags)); + sel->r.height = max(0, SMIAPP_ALIGN_DIM(sel->r.height, sel->flags)); + + sel->r.width = max_t(unsigned int, + sensor->limits[SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE], + sel->r.width); + sel->r.height = max_t(unsigned int, + sensor->limits[SMIAPP_LIMIT_MIN_Y_OUTPUT_SIZE], + sel->r.height); + + switch (sel->target) { + case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL: + ret = smiapp_set_crop(subdev, fh, sel); + break; + case V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL: + ret = smiapp_set_compose(subdev, fh, sel); + break; + default: + BUG(); + } + + mutex_unlock(&sensor->mutex); + return ret; +} + +static int smiapp_get_skip_frames(struct v4l2_subdev *subdev, u32 *frames) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + + *frames = sensor->frame_skip; + return 0; +} + +/* ----------------------------------------------------------------------------- + * sysfs attributes + */ + +static ssize_t +smiapp_sysfs_nvm_read(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct v4l2_subdev *subdev = i2c_get_clientdata(to_i2c_client(dev)); + struct i2c_client *client = v4l2_get_subdevdata(subdev); + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + unsigned int nbytes; + + if (!sensor->dev_init_done) + return -EBUSY; + + if (!sensor->nvm_size) { + /* NVM not read yet - read it now */ + sensor->nvm_size = sensor->platform_data->nvm_size; + if (smiapp_set_power(subdev, 1) < 0) + return -ENODEV; + if (smiapp_read_nvm(sensor, sensor->nvm)) { + dev_err(&client->dev, "nvm read failed\n"); + return -ENODEV; + } + smiapp_set_power(subdev, 0); + } + /* + * NVM is still way below a PAGE_SIZE, so we can safely + * assume this for now. + */ + nbytes = min_t(unsigned int, sensor->nvm_size, PAGE_SIZE); + memcpy(buf, sensor->nvm, nbytes); + + return nbytes; +} +static DEVICE_ATTR(nvm, S_IRUGO, smiapp_sysfs_nvm_read, NULL); + +/* ----------------------------------------------------------------------------- + * V4L2 subdev core operations + */ + +static int smiapp_identify_module(struct v4l2_subdev *subdev) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + struct i2c_client *client = v4l2_get_subdevdata(subdev); + struct smiapp_module_info *minfo = &sensor->minfo; + unsigned int i; + int rval = 0; + + minfo->name = SMIAPP_NAME; + + /* Module info */ + rval = smiapp_read(client, SMIAPP_REG_U8_MANUFACTURER_ID, + &minfo->manufacturer_id); + if (!rval) + rval = smiapp_read(client, SMIAPP_REG_U16_MODEL_ID, + &minfo->model_id); + if (!rval) + rval = smiapp_read(client, SMIAPP_REG_U8_REVISION_NUMBER_MAJOR, + &minfo->revision_number_major); + if (!rval) + rval = smiapp_read(client, SMIAPP_REG_U8_REVISION_NUMBER_MINOR, + &minfo->revision_number_minor); + if (!rval) + rval = smiapp_read(client, SMIAPP_REG_U8_MODULE_DATE_YEAR, + &minfo->module_year); + if (!rval) + rval = smiapp_read(client, SMIAPP_REG_U8_MODULE_DATE_MONTH, + &minfo->module_month); + if (!rval) + rval = smiapp_read(client, SMIAPP_REG_U8_MODULE_DATE_DAY, + &minfo->module_day); + + /* Sensor info */ + if (!rval) + rval = smiapp_read(client, + SMIAPP_REG_U8_SENSOR_MANUFACTURER_ID, + &minfo->sensor_manufacturer_id); + if (!rval) + rval = smiapp_read(client, SMIAPP_REG_U16_SENSOR_MODEL_ID, + &minfo->sensor_model_id); + if (!rval) + rval = smiapp_read(client, + SMIAPP_REG_U8_SENSOR_REVISION_NUMBER, + &minfo->sensor_revision_number); + if (!rval) + rval = smiapp_read(client, + SMIAPP_REG_U8_SENSOR_FIRMWARE_VERSION, + &minfo->sensor_firmware_version); + + /* SMIA */ + if (!rval) + rval = smiapp_read(client, SMIAPP_REG_U8_SMIA_VERSION, + &minfo->smia_version); + if (!rval) + rval = smiapp_read(client, SMIAPP_REG_U8_SMIAPP_VERSION, + &minfo->smiapp_version); + + if (rval) { + dev_err(&client->dev, "sensor detection failed\n"); + return -ENODEV; + } + + dev_dbg(&client->dev, "module 0x%2.2x-0x%4.4x\n", + minfo->manufacturer_id, minfo->model_id); + + dev_dbg(&client->dev, + "module revision 0x%2.2x-0x%2.2x date %2.2d-%2.2d-%2.2d\n", + minfo->revision_number_major, minfo->revision_number_minor, + minfo->module_year, minfo->module_month, minfo->module_day); + + dev_dbg(&client->dev, "sensor 0x%2.2x-0x%4.4x\n", + minfo->sensor_manufacturer_id, minfo->sensor_model_id); + + dev_dbg(&client->dev, + "sensor revision 0x%2.2x firmware version 0x%2.2x\n", + minfo->sensor_revision_number, minfo->sensor_firmware_version); + + dev_dbg(&client->dev, "smia version %2.2d smiapp version %2.2d\n", + minfo->smia_version, minfo->smiapp_version); + + /* + * Some modules have bad data in the lvalues below. Hope the + * rvalues have better stuff. The lvalues are module + * parameters whereas the rvalues are sensor parameters. + */ + if (!minfo->manufacturer_id && !minfo->model_id) { + minfo->manufacturer_id = minfo->sensor_manufacturer_id; + minfo->model_id = minfo->sensor_model_id; + minfo->revision_number_major = minfo->sensor_revision_number; + } + + for (i = 0; i < ARRAY_SIZE(smiapp_module_idents); i++) { + if (smiapp_module_idents[i].manufacturer_id + != minfo->manufacturer_id) + continue; + if (smiapp_module_idents[i].model_id != minfo->model_id) + continue; + if (smiapp_module_idents[i].flags + & SMIAPP_MODULE_IDENT_FLAG_REV_LE) { + if (smiapp_module_idents[i].revision_number_major + < minfo->revision_number_major) + continue; + } else { + if (smiapp_module_idents[i].revision_number_major + != minfo->revision_number_major) + continue; + } + + minfo->name = smiapp_module_idents[i].name; + minfo->quirk = smiapp_module_idents[i].quirk; + break; + } + + if (i >= ARRAY_SIZE(smiapp_module_idents)) + dev_warn(&client->dev, + "no quirks for this module; let's hope it's fully compliant\n"); + + dev_dbg(&client->dev, "the sensor is called %s, ident %2.2x%4.4x%2.2x\n", + minfo->name, minfo->manufacturer_id, minfo->model_id, + minfo->revision_number_major); + + strlcpy(subdev->name, sensor->minfo.name, sizeof(subdev->name)); + + return 0; +} + +static const struct v4l2_subdev_ops smiapp_ops; +static const struct v4l2_subdev_internal_ops smiapp_internal_ops; +static const struct media_entity_operations smiapp_entity_ops; + +static int smiapp_registered(struct v4l2_subdev *subdev) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + struct i2c_client *client = v4l2_get_subdevdata(subdev); + struct smiapp_subdev *last = NULL; + u32 tmp; + unsigned int i; + int rval; + + sensor->vana = regulator_get(&client->dev, "VANA"); + if (IS_ERR(sensor->vana)) { + dev_err(&client->dev, "could not get regulator for vana\n"); + return -ENODEV; + } + + if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) { + if (gpio_request_one(sensor->platform_data->xshutdown, 0, + "SMIA++ xshutdown") != 0) { + dev_err(&client->dev, + "unable to acquire reset gpio %d\n", + sensor->platform_data->xshutdown); + rval = -ENODEV; + goto out_gpio_request; + } + } + + rval = smiapp_power_on(sensor); + if (rval) { + rval = -ENODEV; + goto out_smiapp_power_on; + } + + rval = smiapp_identify_module(subdev); + if (rval) { + rval = -ENODEV; + goto out_power_off; + } + + rval = smiapp_get_all_limits(sensor); + if (rval) { + rval = -ENODEV; + goto out_power_off; + } + + /* + * Handle Sensor Module orientation on the board. + * + * The application of H-FLIP and V-FLIP on the sensor is modified by + * the sensor orientation on the board. + * + * For SMIAPP_BOARD_SENSOR_ORIENT_180 the default behaviour is to set + * both H-FLIP and V-FLIP for normal operation which also implies + * that a set/unset operation for user space HFLIP and VFLIP v4l2 + * controls will need to be internally inverted. + * + * Rotation also changes the bayer pattern. + */ + if (sensor->platform_data->module_board_orient == + SMIAPP_MODULE_BOARD_ORIENT_180) + sensor->hvflip_inv_mask = SMIAPP_IMAGE_ORIENTATION_HFLIP | + SMIAPP_IMAGE_ORIENTATION_VFLIP; + + rval = smiapp_get_mbus_formats(sensor); + if (rval) { + rval = -ENODEV; + goto out_power_off; + } + + if (sensor->limits[SMIAPP_LIMIT_BINNING_CAPABILITY]) { + u32 val; + + rval = smiapp_read(client, + SMIAPP_REG_U8_BINNING_SUBTYPES, &val); + if (rval < 0) { + rval = -ENODEV; + goto out_power_off; + } + sensor->nbinning_subtypes = min_t(u8, val, + SMIAPP_BINNING_SUBTYPES); + + for (i = 0; i < sensor->nbinning_subtypes; i++) { + rval = smiapp_read( + client, SMIAPP_REG_U8_BINNING_TYPE_n(i), &val); + if (rval < 0) { + rval = -ENODEV; + goto out_power_off; + } + sensor->binning_subtypes[i] = + *(struct smiapp_binning_subtype *)&val; + + dev_dbg(&client->dev, "binning %xx%x\n", + sensor->binning_subtypes[i].horizontal, + sensor->binning_subtypes[i].vertical); + } + } + sensor->binning_horizontal = 1; + sensor->binning_vertical = 1; + + /* SMIA++ NVM initialization - it will be read from the sensor + * when it is first requested by userspace. + */ + if (sensor->minfo.smiapp_version && sensor->platform_data->nvm_size) { + sensor->nvm = kzalloc(sensor->platform_data->nvm_size, + GFP_KERNEL); + if (sensor->nvm == NULL) { + dev_err(&client->dev, "nvm buf allocation failed\n"); + rval = -ENOMEM; + goto out_power_off; + } + + if (device_create_file(&client->dev, &dev_attr_nvm) != 0) { + dev_err(&client->dev, "sysfs nvm entry failed\n"); + rval = -EBUSY; + goto out_power_off; + } + } + + rval = smiapp_call_quirk(sensor, limits); + if (rval) { + dev_err(&client->dev, "limits quirks failed\n"); + goto out_nvm_release; + } + + /* We consider this as profile 0 sensor if any of these are zero. */ + if (!sensor->limits[SMIAPP_LIMIT_MIN_OP_SYS_CLK_DIV] || + !sensor->limits[SMIAPP_LIMIT_MAX_OP_SYS_CLK_DIV] || + !sensor->limits[SMIAPP_LIMIT_MIN_OP_PIX_CLK_DIV] || + !sensor->limits[SMIAPP_LIMIT_MAX_OP_PIX_CLK_DIV]) { + sensor->minfo.smiapp_profile = SMIAPP_PROFILE_0; + } else if (sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY] + != SMIAPP_SCALING_CAPABILITY_NONE) { + if (sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY] + == SMIAPP_SCALING_CAPABILITY_HORIZONTAL) + sensor->minfo.smiapp_profile = SMIAPP_PROFILE_1; + else + sensor->minfo.smiapp_profile = SMIAPP_PROFILE_2; + sensor->scaler = &sensor->ssds[sensor->ssds_used]; + sensor->ssds_used++; + } else if (sensor->limits[SMIAPP_LIMIT_DIGITAL_CROP_CAPABILITY] + == SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP) { + sensor->scaler = &sensor->ssds[sensor->ssds_used]; + sensor->ssds_used++; + } + sensor->binner = &sensor->ssds[sensor->ssds_used]; + sensor->ssds_used++; + sensor->pixel_array = &sensor->ssds[sensor->ssds_used]; + sensor->ssds_used++; + + sensor->scale_m = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]; + + for (i = 0; i < SMIAPP_SUBDEVS; i++) { + struct { + struct smiapp_subdev *ssd; + char *name; + } const __this[] = { + { sensor->scaler, "scaler", }, + { sensor->binner, "binner", }, + { sensor->pixel_array, "pixel array", }, + }, *_this = &__this[i]; + struct smiapp_subdev *this = _this->ssd; + + if (!this) + continue; + + if (this != sensor->src) + v4l2_subdev_init(&this->sd, &smiapp_ops); + + this->sensor = sensor; + + if (this == sensor->pixel_array) { + this->npads = 1; + } else { + this->npads = 2; + this->source_pad = 1; + } + + snprintf(this->sd.name, + sizeof(this->sd.name), "%s %s", + sensor->minfo.name, _this->name); + + this->sink_fmt.width = + sensor->limits[SMIAPP_LIMIT_X_ADDR_MAX] + 1; + this->sink_fmt.height = + sensor->limits[SMIAPP_LIMIT_Y_ADDR_MAX] + 1; + this->compose.width = this->sink_fmt.width; + this->compose.height = this->sink_fmt.height; + this->crop[this->source_pad] = this->compose; + this->pads[this->source_pad].flags = MEDIA_PAD_FL_SOURCE; + if (this != sensor->pixel_array) { + this->crop[this->sink_pad] = this->compose; + this->pads[this->sink_pad].flags = MEDIA_PAD_FL_SINK; + } + + this->sd.entity.ops = &smiapp_entity_ops; + + if (last == NULL) { + last = this; + continue; + } + + this->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + this->sd.internal_ops = &smiapp_internal_ops; + this->sd.owner = NULL; + v4l2_set_subdevdata(&this->sd, client); + + rval = media_entity_init(&this->sd.entity, + this->npads, this->pads, 0); + if (rval) { + dev_err(&client->dev, + "media_entity_init failed\n"); + goto out_nvm_release; + } + + rval = media_entity_create_link(&this->sd.entity, + this->source_pad, + &last->sd.entity, + last->sink_pad, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (rval) { + dev_err(&client->dev, + "media_entity_create_link failed\n"); + goto out_nvm_release; + } + + rval = v4l2_device_register_subdev(sensor->src->sd.v4l2_dev, + &this->sd); + if (rval) { + dev_err(&client->dev, + "v4l2_device_register_subdev failed\n"); + goto out_nvm_release; + } + + last = this; + } + + dev_dbg(&client->dev, "profile %d\n", sensor->minfo.smiapp_profile); + + sensor->pixel_array->sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; + + /* final steps */ + smiapp_read_frame_fmt(sensor); + rval = smiapp_init_controls(sensor); + if (rval < 0) + goto out_nvm_release; + + rval = smiapp_update_mode(sensor); + if (rval) { + dev_err(&client->dev, "update mode failed\n"); + goto out_nvm_release; + } + + sensor->streaming = false; + sensor->dev_init_done = true; + + /* check flash capability */ + rval = smiapp_read(client, SMIAPP_REG_U8_FLASH_MODE_CAPABILITY, &tmp); + sensor->flash_capability = tmp; + if (rval) + goto out_nvm_release; + + smiapp_power_off(sensor); + + return 0; + +out_nvm_release: + device_remove_file(&client->dev, &dev_attr_nvm); + +out_power_off: + kfree(sensor->nvm); + sensor->nvm = NULL; + smiapp_power_off(sensor); + +out_smiapp_power_on: + if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) + gpio_free(sensor->platform_data->xshutdown); + +out_gpio_request: + regulator_put(sensor->vana); + sensor->vana = NULL; + return rval; +} + +static int smiapp_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct smiapp_subdev *ssd = to_smiapp_subdev(sd); + struct smiapp_sensor *sensor = ssd->sensor; + u32 mbus_code = + smiapp_csi_data_formats[smiapp_pixel_order(sensor)].code; + unsigned int i; + + mutex_lock(&sensor->mutex); + + for (i = 0; i < ssd->npads; i++) { + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(fh, i); + struct v4l2_rect *try_crop = v4l2_subdev_get_try_crop(fh, i); + struct v4l2_rect *try_comp; + + try_fmt->width = sensor->limits[SMIAPP_LIMIT_X_ADDR_MAX] + 1; + try_fmt->height = sensor->limits[SMIAPP_LIMIT_Y_ADDR_MAX] + 1; + try_fmt->code = mbus_code; + + try_crop->top = 0; + try_crop->left = 0; + try_crop->width = try_fmt->width; + try_crop->height = try_fmt->height; + + if (ssd != sensor->pixel_array) + continue; + + try_comp = v4l2_subdev_get_try_compose(fh, i); + *try_comp = *try_crop; + } + + mutex_unlock(&sensor->mutex); + + return smiapp_set_power(sd, 1); +} + +static int smiapp_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + return smiapp_set_power(sd, 0); +} + +static const struct v4l2_subdev_video_ops smiapp_video_ops = { + .s_stream = smiapp_set_stream, +}; + +static const struct v4l2_subdev_core_ops smiapp_core_ops = { + .s_power = smiapp_set_power, +}; + +static const struct v4l2_subdev_pad_ops smiapp_pad_ops = { + .enum_mbus_code = smiapp_enum_mbus_code, + .get_fmt = smiapp_get_format, + .set_fmt = smiapp_set_format, + .get_selection = smiapp_get_selection, + .set_selection = smiapp_set_selection, +}; + +static const struct v4l2_subdev_sensor_ops smiapp_sensor_ops = { + .g_skip_frames = smiapp_get_skip_frames, +}; + +static const struct v4l2_subdev_ops smiapp_ops = { + .core = &smiapp_core_ops, + .video = &smiapp_video_ops, + .pad = &smiapp_pad_ops, + .sensor = &smiapp_sensor_ops, +}; + +static const struct media_entity_operations smiapp_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static const struct v4l2_subdev_internal_ops smiapp_internal_src_ops = { + .registered = smiapp_registered, + .open = smiapp_open, + .close = smiapp_close, +}; + +static const struct v4l2_subdev_internal_ops smiapp_internal_ops = { + .open = smiapp_open, + .close = smiapp_close, +}; + +/* ----------------------------------------------------------------------------- + * I2C Driver + */ + +#ifdef CONFIG_PM + +static int smiapp_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + bool streaming; + + BUG_ON(mutex_is_locked(&sensor->mutex)); + + if (sensor->power_count == 0) + return 0; + + if (sensor->streaming) + smiapp_stop_streaming(sensor); + + streaming = sensor->streaming; + + smiapp_power_off(sensor); + + /* save state for resume */ + sensor->streaming = streaming; + + return 0; +} + +static int smiapp_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + int rval; + + if (sensor->power_count == 0) + return 0; + + rval = smiapp_power_on(sensor); + if (rval) + return rval; + + if (sensor->streaming) + rval = smiapp_start_streaming(sensor); + + return rval; +} + +#else + +#define smiapp_suspend NULL +#define smiapp_resume NULL + +#endif /* CONFIG_PM */ + +static int smiapp_probe(struct i2c_client *client, + const struct i2c_device_id *devid) +{ + struct smiapp_sensor *sensor; + int rval; + + if (client->dev.platform_data == NULL) + return -ENODEV; + + sensor = kzalloc(sizeof(*sensor), GFP_KERNEL); + if (sensor == NULL) + return -ENOMEM; + + sensor->platform_data = client->dev.platform_data; + mutex_init(&sensor->mutex); + mutex_init(&sensor->power_mutex); + sensor->src = &sensor->ssds[sensor->ssds_used]; + + v4l2_i2c_subdev_init(&sensor->src->sd, client, &smiapp_ops); + sensor->src->sd.internal_ops = &smiapp_internal_src_ops; + sensor->src->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sensor->src->sensor = sensor; + + sensor->src->pads[0].flags = MEDIA_PAD_FL_SOURCE; + rval = media_entity_init(&sensor->src->sd.entity, 2, + sensor->src->pads, 0); + if (rval < 0) + kfree(sensor); + + return rval; +} + +static int __exit smiapp_remove(struct i2c_client *client) +{ + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + unsigned int i; + + if (sensor->power_count) { + if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) + gpio_set_value(sensor->platform_data->xshutdown, 0); + sensor->platform_data->set_xclk(&sensor->src->sd, 0); + sensor->power_count = 0; + } + + if (sensor->nvm) { + device_remove_file(&client->dev, &dev_attr_nvm); + kfree(sensor->nvm); + } + + for (i = 0; i < sensor->ssds_used; i++) { + media_entity_cleanup(&sensor->ssds[i].sd.entity); + v4l2_device_unregister_subdev(&sensor->ssds[i].sd); + } + smiapp_free_controls(sensor); + if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) + gpio_free(sensor->platform_data->xshutdown); + if (sensor->vana) + regulator_put(sensor->vana); + + kfree(sensor); + + return 0; +} + +static const struct i2c_device_id smiapp_id_table[] = { + { SMIAPP_NAME, 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, smiapp_id_table); + +static const struct dev_pm_ops smiapp_pm_ops = { + .suspend = smiapp_suspend, + .resume = smiapp_resume, +}; + +static struct i2c_driver smiapp_i2c_driver = { + .driver = { + .name = SMIAPP_NAME, + .pm = &smiapp_pm_ops, + }, + .probe = smiapp_probe, + .remove = __exit_p(smiapp_remove), + .id_table = smiapp_id_table, +}; + +module_i2c_driver(smiapp_i2c_driver); + +MODULE_AUTHOR("Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>"); +MODULE_DESCRIPTION("Generic SMIA/SMIA++ camera module driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/smiapp/smiapp-debug.h b/drivers/media/video/smiapp/smiapp-debug.h new file mode 100644 index 000000000000..627809eed1d9 --- /dev/null +++ b/drivers/media/video/smiapp/smiapp-debug.h @@ -0,0 +1,32 @@ +/* + * drivers/media/video/smiapp/smiapp-debug.h + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2011--2012 Nokia Corporation + * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> + * + * 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 SMIAPP_DEBUG_H +#define SMIAPP_DEBUG_H + +#ifdef CONFIG_VIDEO_SMIAPP_DEBUG +#define DEBUG +#endif + +#endif diff --git a/drivers/media/video/smiapp/smiapp-limits.c b/drivers/media/video/smiapp/smiapp-limits.c new file mode 100644 index 000000000000..0800e095724e --- /dev/null +++ b/drivers/media/video/smiapp/smiapp-limits.c @@ -0,0 +1,132 @@ +/* + * drivers/media/video/smiapp/smiapp-limits.c + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2011--2012 Nokia Corporation + * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> + * + * 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 + * + */ + +#include "smiapp.h" + +struct smiapp_reg_limits smiapp_reg_limits[] = { + { SMIAPP_REG_U16_ANALOGUE_GAIN_CAPABILITY, "analogue_gain_capability" }, /* 0 */ + { SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_MIN, "analogue_gain_code_min" }, + { SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_MAX, "analogue_gain_code_max" }, + { SMIAPP_REG_U8_THS_ZERO_MIN, "ths_zero_min" }, + { SMIAPP_REG_U8_TCLK_TRAIL_MIN, "tclk_trail_min" }, + { SMIAPP_REG_U16_INTEGRATION_TIME_CAPABILITY, "integration_time_capability" }, /* 5 */ + { SMIAPP_REG_U16_COARSE_INTEGRATION_TIME_MIN, "coarse_integration_time_min" }, + { SMIAPP_REG_U16_COARSE_INTEGRATION_TIME_MAX_MARGIN, "coarse_integration_time_max_margin" }, + { SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MIN, "fine_integration_time_min" }, + { SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MAX_MARGIN, "fine_integration_time_max_margin" }, + { SMIAPP_REG_U16_DIGITAL_GAIN_CAPABILITY, "digital_gain_capability" }, /* 10 */ + { SMIAPP_REG_U16_DIGITAL_GAIN_MIN, "digital_gain_min" }, + { SMIAPP_REG_U16_DIGITAL_GAIN_MAX, "digital_gain_max" }, + { SMIAPP_REG_F32_MIN_EXT_CLK_FREQ_HZ, "min_ext_clk_freq_hz" }, + { SMIAPP_REG_F32_MAX_EXT_CLK_FREQ_HZ, "max_ext_clk_freq_hz" }, + { SMIAPP_REG_U16_MIN_PRE_PLL_CLK_DIV, "min_pre_pll_clk_div" }, /* 15 */ + { SMIAPP_REG_U16_MAX_PRE_PLL_CLK_DIV, "max_pre_pll_clk_div" }, + { SMIAPP_REG_F32_MIN_PLL_IP_FREQ_HZ, "min_pll_ip_freq_hz" }, + { SMIAPP_REG_F32_MAX_PLL_IP_FREQ_HZ, "max_pll_ip_freq_hz" }, + { SMIAPP_REG_U16_MIN_PLL_MULTIPLIER, "min_pll_multiplier" }, + { SMIAPP_REG_U16_MAX_PLL_MULTIPLIER, "max_pll_multiplier" }, /* 20 */ + { SMIAPP_REG_F32_MIN_PLL_OP_FREQ_HZ, "min_pll_op_freq_hz" }, + { SMIAPP_REG_F32_MAX_PLL_OP_FREQ_HZ, "max_pll_op_freq_hz" }, + { SMIAPP_REG_U16_MIN_VT_SYS_CLK_DIV, "min_vt_sys_clk_div" }, + { SMIAPP_REG_U16_MAX_VT_SYS_CLK_DIV, "max_vt_sys_clk_div" }, + { SMIAPP_REG_F32_MIN_VT_SYS_CLK_FREQ_HZ, "min_vt_sys_clk_freq_hz" }, /* 25 */ + { SMIAPP_REG_F32_MAX_VT_SYS_CLK_FREQ_HZ, "max_vt_sys_clk_freq_hz" }, + { SMIAPP_REG_F32_MIN_VT_PIX_CLK_FREQ_HZ, "min_vt_pix_clk_freq_hz" }, + { SMIAPP_REG_F32_MAX_VT_PIX_CLK_FREQ_HZ, "max_vt_pix_clk_freq_hz" }, + { SMIAPP_REG_U16_MIN_VT_PIX_CLK_DIV, "min_vt_pix_clk_div" }, + { SMIAPP_REG_U16_MAX_VT_PIX_CLK_DIV, "max_vt_pix_clk_div" }, /* 30 */ + { SMIAPP_REG_U16_MIN_FRAME_LENGTH_LINES, "min_frame_length_lines" }, + { SMIAPP_REG_U16_MAX_FRAME_LENGTH_LINES, "max_frame_length_lines" }, + { SMIAPP_REG_U16_MIN_LINE_LENGTH_PCK, "min_line_length_pck" }, + { SMIAPP_REG_U16_MAX_LINE_LENGTH_PCK, "max_line_length_pck" }, + { SMIAPP_REG_U16_MIN_LINE_BLANKING_PCK, "min_line_blanking_pck" }, /* 35 */ + { SMIAPP_REG_U16_MIN_FRAME_BLANKING_LINES, "min_frame_blanking_lines" }, + { SMIAPP_REG_U8_MIN_LINE_LENGTH_PCK_STEP_SIZE, "min_line_length_pck_step_size" }, + { SMIAPP_REG_U16_MIN_OP_SYS_CLK_DIV, "min_op_sys_clk_div" }, + { SMIAPP_REG_U16_MAX_OP_SYS_CLK_DIV, "max_op_sys_clk_div" }, + { SMIAPP_REG_F32_MIN_OP_SYS_CLK_FREQ_HZ, "min_op_sys_clk_freq_hz" }, /* 40 */ + { SMIAPP_REG_F32_MAX_OP_SYS_CLK_FREQ_HZ, "max_op_sys_clk_freq_hz" }, + { SMIAPP_REG_U16_MIN_OP_PIX_CLK_DIV, "min_op_pix_clk_div" }, + { SMIAPP_REG_U16_MAX_OP_PIX_CLK_DIV, "max_op_pix_clk_div" }, + { SMIAPP_REG_F32_MIN_OP_PIX_CLK_FREQ_HZ, "min_op_pix_clk_freq_hz" }, + { SMIAPP_REG_F32_MAX_OP_PIX_CLK_FREQ_HZ, "max_op_pix_clk_freq_hz" }, /* 45 */ + { SMIAPP_REG_U16_X_ADDR_MIN, "x_addr_min" }, + { SMIAPP_REG_U16_Y_ADDR_MIN, "y_addr_min" }, + { SMIAPP_REG_U16_X_ADDR_MAX, "x_addr_max" }, + { SMIAPP_REG_U16_Y_ADDR_MAX, "y_addr_max" }, + { SMIAPP_REG_U16_MIN_X_OUTPUT_SIZE, "min_x_output_size" }, /* 50 */ + { SMIAPP_REG_U16_MIN_Y_OUTPUT_SIZE, "min_y_output_size" }, + { SMIAPP_REG_U16_MAX_X_OUTPUT_SIZE, "max_x_output_size" }, + { SMIAPP_REG_U16_MAX_Y_OUTPUT_SIZE, "max_y_output_size" }, + { SMIAPP_REG_U16_MIN_EVEN_INC, "min_even_inc" }, + { SMIAPP_REG_U16_MAX_EVEN_INC, "max_even_inc" }, /* 55 */ + { SMIAPP_REG_U16_MIN_ODD_INC, "min_odd_inc" }, + { SMIAPP_REG_U16_MAX_ODD_INC, "max_odd_inc" }, + { SMIAPP_REG_U16_SCALING_CAPABILITY, "scaling_capability" }, + { SMIAPP_REG_U16_SCALER_M_MIN, "scaler_m_min" }, + { SMIAPP_REG_U16_SCALER_M_MAX, "scaler_m_max" }, /* 60 */ + { SMIAPP_REG_U16_SCALER_N_MIN, "scaler_n_min" }, + { SMIAPP_REG_U16_SCALER_N_MAX, "scaler_n_max" }, + { SMIAPP_REG_U16_SPATIAL_SAMPLING_CAPABILITY, "spatial_sampling_capability" }, + { SMIAPP_REG_U8_DIGITAL_CROP_CAPABILITY, "digital_crop_capability" }, + { SMIAPP_REG_U16_COMPRESSION_CAPABILITY, "compression_capability" }, /* 65 */ + { SMIAPP_REG_U8_FIFO_SUPPORT_CAPABILITY, "fifo_support_capability" }, + { SMIAPP_REG_U8_DPHY_CTRL_CAPABILITY, "dphy_ctrl_capability" }, + { SMIAPP_REG_U8_CSI_LANE_MODE_CAPABILITY, "csi_lane_mode_capability" }, + { SMIAPP_REG_U8_CSI_SIGNALLING_MODE_CAPABILITY, "csi_signalling_mode_capability" }, + { SMIAPP_REG_U8_FAST_STANDBY_CAPABILITY, "fast_standby_capability" }, /* 70 */ + { SMIAPP_REG_U8_CCI_ADDRESS_CONTROL_CAPABILITY, "cci_address_control_capability" }, + { SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_1_LANE_MODE_MBPS, "max_per_lane_bitrate_1_lane_mode_mbps" }, + { SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_2_LANE_MODE_MBPS, "max_per_lane_bitrate_2_lane_mode_mbps" }, + { SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_3_LANE_MODE_MBPS, "max_per_lane_bitrate_3_lane_mode_mbps" }, + { SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_4_LANE_MODE_MBPS, "max_per_lane_bitrate_4_lane_mode_mbps" }, /* 75 */ + { SMIAPP_REG_U8_TEMP_SENSOR_CAPABILITY, "temp_sensor_capability" }, + { SMIAPP_REG_U16_MIN_FRAME_LENGTH_LINES_BIN, "min_frame_length_lines_bin" }, + { SMIAPP_REG_U16_MAX_FRAME_LENGTH_LINES_BIN, "max_frame_length_lines_bin" }, + { SMIAPP_REG_U16_MIN_LINE_LENGTH_PCK_BIN, "min_line_length_pck_bin" }, + { SMIAPP_REG_U16_MAX_LINE_LENGTH_PCK_BIN, "max_line_length_pck_bin" }, /* 80 */ + { SMIAPP_REG_U16_MIN_LINE_BLANKING_PCK_BIN, "min_line_blanking_pck_bin" }, + { SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MIN_BIN, "fine_integration_time_min_bin" }, + { SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN, "fine_integration_time_max_margin_bin" }, + { SMIAPP_REG_U8_BINNING_CAPABILITY, "binning_capability" }, + { SMIAPP_REG_U8_BINNING_WEIGHTING_CAPABILITY, "binning_weighting_capability" }, /* 85 */ + { SMIAPP_REG_U8_DATA_TRANSFER_IF_CAPABILITY, "data_transfer_if_capability" }, + { SMIAPP_REG_U8_SHADING_CORRECTION_CAPABILITY, "shading_correction_capability" }, + { SMIAPP_REG_U8_GREEN_IMBALANCE_CAPABILITY, "green_imbalance_capability" }, + { SMIAPP_REG_U8_BLACK_LEVEL_CAPABILITY, "black_level_capability" }, + { SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_CAPABILITY, "module_specific_correction_capability" }, /* 90 */ + { SMIAPP_REG_U16_DEFECT_CORRECTION_CAPABILITY, "defect_correction_capability" }, + { SMIAPP_REG_U16_DEFECT_CORRECTION_CAPABILITY_2, "defect_correction_capability_2" }, + { SMIAPP_REG_U8_EDOF_CAPABILITY, "edof_capability" }, + { SMIAPP_REG_U8_COLOUR_FEEDBACK_CAPABILITY, "colour_feedback_capability" }, + { SMIAPP_REG_U8_ESTIMATION_MODE_CAPABILITY, "estimation_mode_capability" }, /* 95 */ + { SMIAPP_REG_U8_ESTIMATION_ZONE_CAPABILITY, "estimation_zone_capability" }, + { SMIAPP_REG_U16_CAPABILITY_TRDY_MIN, "capability_trdy_min" }, + { SMIAPP_REG_U8_FLASH_MODE_CAPABILITY, "flash_mode_capability" }, + { SMIAPP_REG_U8_ACTUATOR_CAPABILITY, "actuator_capability" }, + { SMIAPP_REG_U8_BRACKETING_LUT_CAPABILITY_1, "bracketing_lut_capability_1" }, /* 100 */ + { SMIAPP_REG_U8_BRACKETING_LUT_CAPABILITY_2, "bracketing_lut_capability_2" }, + { SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_STEP, "analogue_gain_code_step" }, + { 0, NULL }, +}; diff --git a/drivers/media/video/smiapp/smiapp-limits.h b/drivers/media/video/smiapp/smiapp-limits.h new file mode 100644 index 000000000000..7f4836bb78db --- /dev/null +++ b/drivers/media/video/smiapp/smiapp-limits.h @@ -0,0 +1,128 @@ +/* + * drivers/media/video/smiapp/smiapp-limits.h + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2011--2012 Nokia Corporation + * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> + * + * 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 + * + */ + +#define SMIAPP_LIMIT_ANALOGUE_GAIN_CAPABILITY 0 +#define SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MIN 1 +#define SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MAX 2 +#define SMIAPP_LIMIT_THS_ZERO_MIN 3 +#define SMIAPP_LIMIT_TCLK_TRAIL_MIN 4 +#define SMIAPP_LIMIT_INTEGRATION_TIME_CAPABILITY 5 +#define SMIAPP_LIMIT_COARSE_INTEGRATION_TIME_MIN 6 +#define SMIAPP_LIMIT_COARSE_INTEGRATION_TIME_MAX_MARGIN 7 +#define SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MIN 8 +#define SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MAX_MARGIN 9 +#define SMIAPP_LIMIT_DIGITAL_GAIN_CAPABILITY 10 +#define SMIAPP_LIMIT_DIGITAL_GAIN_MIN 11 +#define SMIAPP_LIMIT_DIGITAL_GAIN_MAX 12 +#define SMIAPP_LIMIT_MIN_EXT_CLK_FREQ_HZ 13 +#define SMIAPP_LIMIT_MAX_EXT_CLK_FREQ_HZ 14 +#define SMIAPP_LIMIT_MIN_PRE_PLL_CLK_DIV 15 +#define SMIAPP_LIMIT_MAX_PRE_PLL_CLK_DIV 16 +#define SMIAPP_LIMIT_MIN_PLL_IP_FREQ_HZ 17 +#define SMIAPP_LIMIT_MAX_PLL_IP_FREQ_HZ 18 +#define SMIAPP_LIMIT_MIN_PLL_MULTIPLIER 19 +#define SMIAPP_LIMIT_MAX_PLL_MULTIPLIER 20 +#define SMIAPP_LIMIT_MIN_PLL_OP_FREQ_HZ 21 +#define SMIAPP_LIMIT_MAX_PLL_OP_FREQ_HZ 22 +#define SMIAPP_LIMIT_MIN_VT_SYS_CLK_DIV 23 +#define SMIAPP_LIMIT_MAX_VT_SYS_CLK_DIV 24 +#define SMIAPP_LIMIT_MIN_VT_SYS_CLK_FREQ_HZ 25 +#define SMIAPP_LIMIT_MAX_VT_SYS_CLK_FREQ_HZ 26 +#define SMIAPP_LIMIT_MIN_VT_PIX_CLK_FREQ_HZ 27 +#define SMIAPP_LIMIT_MAX_VT_PIX_CLK_FREQ_HZ 28 +#define SMIAPP_LIMIT_MIN_VT_PIX_CLK_DIV 29 +#define SMIAPP_LIMIT_MAX_VT_PIX_CLK_DIV 30 +#define SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES 31 +#define SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES 32 +#define SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK 33 +#define SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK 34 +#define SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK 35 +#define SMIAPP_LIMIT_MIN_FRAME_BLANKING_LINES 36 +#define SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_STEP_SIZE 37 +#define SMIAPP_LIMIT_MIN_OP_SYS_CLK_DIV 38 +#define SMIAPP_LIMIT_MAX_OP_SYS_CLK_DIV 39 +#define SMIAPP_LIMIT_MIN_OP_SYS_CLK_FREQ_HZ 40 +#define SMIAPP_LIMIT_MAX_OP_SYS_CLK_FREQ_HZ 41 +#define SMIAPP_LIMIT_MIN_OP_PIX_CLK_DIV 42 +#define SMIAPP_LIMIT_MAX_OP_PIX_CLK_DIV 43 +#define SMIAPP_LIMIT_MIN_OP_PIX_CLK_FREQ_HZ 44 +#define SMIAPP_LIMIT_MAX_OP_PIX_CLK_FREQ_HZ 45 +#define SMIAPP_LIMIT_X_ADDR_MIN 46 +#define SMIAPP_LIMIT_Y_ADDR_MIN 47 +#define SMIAPP_LIMIT_X_ADDR_MAX 48 +#define SMIAPP_LIMIT_Y_ADDR_MAX 49 +#define SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE 50 +#define SMIAPP_LIMIT_MIN_Y_OUTPUT_SIZE 51 +#define SMIAPP_LIMIT_MAX_X_OUTPUT_SIZE 52 +#define SMIAPP_LIMIT_MAX_Y_OUTPUT_SIZE 53 +#define SMIAPP_LIMIT_MIN_EVEN_INC 54 +#define SMIAPP_LIMIT_MAX_EVEN_INC 55 +#define SMIAPP_LIMIT_MIN_ODD_INC 56 +#define SMIAPP_LIMIT_MAX_ODD_INC 57 +#define SMIAPP_LIMIT_SCALING_CAPABILITY 58 +#define SMIAPP_LIMIT_SCALER_M_MIN 59 +#define SMIAPP_LIMIT_SCALER_M_MAX 60 +#define SMIAPP_LIMIT_SCALER_N_MIN 61 +#define SMIAPP_LIMIT_SCALER_N_MAX 62 +#define SMIAPP_LIMIT_SPATIAL_SAMPLING_CAPABILITY 63 +#define SMIAPP_LIMIT_DIGITAL_CROP_CAPABILITY 64 +#define SMIAPP_LIMIT_COMPRESSION_CAPABILITY 65 +#define SMIAPP_LIMIT_FIFO_SUPPORT_CAPABILITY 66 +#define SMIAPP_LIMIT_DPHY_CTRL_CAPABILITY 67 +#define SMIAPP_LIMIT_CSI_LANE_MODE_CAPABILITY 68 +#define SMIAPP_LIMIT_CSI_SIGNALLING_MODE_CAPABILITY 69 +#define SMIAPP_LIMIT_FAST_STANDBY_CAPABILITY 70 +#define SMIAPP_LIMIT_CCI_ADDRESS_CONTROL_CAPABILITY 71 +#define SMIAPP_LIMIT_MAX_PER_LANE_BITRATE_1_LANE_MODE_MBPS 72 +#define SMIAPP_LIMIT_MAX_PER_LANE_BITRATE_2_LANE_MODE_MBPS 73 +#define SMIAPP_LIMIT_MAX_PER_LANE_BITRATE_3_LANE_MODE_MBPS 74 +#define SMIAPP_LIMIT_MAX_PER_LANE_BITRATE_4_LANE_MODE_MBPS 75 +#define SMIAPP_LIMIT_TEMP_SENSOR_CAPABILITY 76 +#define SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN 77 +#define SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES_BIN 78 +#define SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN 79 +#define SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK_BIN 80 +#define SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN 81 +#define SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MIN_BIN 82 +#define SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN 83 +#define SMIAPP_LIMIT_BINNING_CAPABILITY 84 +#define SMIAPP_LIMIT_BINNING_WEIGHTING_CAPABILITY 85 +#define SMIAPP_LIMIT_DATA_TRANSFER_IF_CAPABILITY 86 +#define SMIAPP_LIMIT_SHADING_CORRECTION_CAPABILITY 87 +#define SMIAPP_LIMIT_GREEN_IMBALANCE_CAPABILITY 88 +#define SMIAPP_LIMIT_BLACK_LEVEL_CAPABILITY 89 +#define SMIAPP_LIMIT_MODULE_SPECIFIC_CORRECTION_CAPABILITY 90 +#define SMIAPP_LIMIT_DEFECT_CORRECTION_CAPABILITY 91 +#define SMIAPP_LIMIT_DEFECT_CORRECTION_CAPABILITY_2 92 +#define SMIAPP_LIMIT_EDOF_CAPABILITY 93 +#define SMIAPP_LIMIT_COLOUR_FEEDBACK_CAPABILITY 94 +#define SMIAPP_LIMIT_ESTIMATION_MODE_CAPABILITY 95 +#define SMIAPP_LIMIT_ESTIMATION_ZONE_CAPABILITY 96 +#define SMIAPP_LIMIT_CAPABILITY_TRDY_MIN 97 +#define SMIAPP_LIMIT_FLASH_MODE_CAPABILITY 98 +#define SMIAPP_LIMIT_ACTUATOR_CAPABILITY 99 +#define SMIAPP_LIMIT_BRACKETING_LUT_CAPABILITY_1 100 +#define SMIAPP_LIMIT_BRACKETING_LUT_CAPABILITY_2 101 +#define SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_STEP 102 +#define SMIAPP_LIMIT_LAST 103 diff --git a/drivers/media/video/smiapp/smiapp-quirk.c b/drivers/media/video/smiapp/smiapp-quirk.c new file mode 100644 index 000000000000..dae85a12f7ec --- /dev/null +++ b/drivers/media/video/smiapp/smiapp-quirk.c @@ -0,0 +1,264 @@ +/* + * drivers/media/video/smiapp/smiapp-quirk.c + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2011--2012 Nokia Corporation + * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> + * + * 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 + * + */ + +#include "smiapp-debug.h" + +#include <linux/delay.h> + +#include "smiapp.h" + +static int smiapp_write_8(struct smiapp_sensor *sensor, u16 reg, u8 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + + return smiapp_write(client, (SMIA_REG_8BIT << 16) | reg, val); +} + +static int smiapp_write_8s(struct smiapp_sensor *sensor, + struct smiapp_reg_8 *regs, int len) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int rval; + + for (; len > 0; len--, regs++) { + rval = smiapp_write_8(sensor, regs->reg, regs->val); + if (rval < 0) { + dev_err(&client->dev, + "error %d writing reg 0x%4.4x, val 0x%2.2x", + rval, regs->reg, regs->val); + return rval; + } + } + + return 0; +} + +void smiapp_replace_limit(struct smiapp_sensor *sensor, + u32 limit, u32 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + + dev_dbg(&client->dev, "quirk: 0x%8.8x \"%s\" = %d, 0x%x\n", + smiapp_reg_limits[limit].addr, + smiapp_reg_limits[limit].what, val, val); + sensor->limits[limit] = val; +} + +int smiapp_replace_limit_at(struct smiapp_sensor *sensor, + u32 reg, u32 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int i; + + for (i = 0; smiapp_reg_limits[i].addr; i++) { + if ((smiapp_reg_limits[i].addr & 0xffff) != reg) + continue; + + smiapp_replace_limit(sensor, i, val); + + return 0; + } + + dev_dbg(&client->dev, "quirk: bad register 0x%4.4x\n", reg); + + return -EINVAL; +} + +static int jt8ew9_limits(struct smiapp_sensor *sensor) +{ + if (sensor->minfo.revision_number_major < 0x03) + sensor->frame_skip = 1; + + /* Below 24 gain doesn't have effect at all, */ + /* but ~59 is needed for full dynamic range */ + smiapp_replace_limit(sensor, SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MIN, 59); + smiapp_replace_limit( + sensor, SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MAX, 6000); + + return 0; +} + +static int jt8ew9_post_poweron(struct smiapp_sensor *sensor) +{ + struct smiapp_reg_8 regs[] = { + { 0x30a3, 0xd8 }, /* Output port control : LVDS ports only */ + { 0x30ae, 0x00 }, /* 0x0307 pll_multiplier maximum value on PLL input 9.6MHz ( 19.2MHz is divided on pre_pll_div) */ + { 0x30af, 0xd0 }, /* 0x0307 pll_multiplier maximum value on PLL input 9.6MHz ( 19.2MHz is divided on pre_pll_div) */ + { 0x322d, 0x04 }, /* Adjusting Processing Image Size to Scaler Toshiba Recommendation Setting */ + { 0x3255, 0x0f }, /* Horizontal Noise Reduction Control Toshiba Recommendation Setting */ + { 0x3256, 0x15 }, /* Horizontal Noise Reduction Control Toshiba Recommendation Setting */ + { 0x3258, 0x70 }, /* Analog Gain Control Toshiba Recommendation Setting */ + { 0x3259, 0x70 }, /* Analog Gain Control Toshiba Recommendation Setting */ + { 0x325f, 0x7c }, /* Analog Gain Control Toshiba Recommendation Setting */ + { 0x3302, 0x06 }, /* Pixel Reference Voltage Control Toshiba Recommendation Setting */ + { 0x3304, 0x00 }, /* Pixel Reference Voltage Control Toshiba Recommendation Setting */ + { 0x3307, 0x22 }, /* Pixel Reference Voltage Control Toshiba Recommendation Setting */ + { 0x3308, 0x8d }, /* Pixel Reference Voltage Control Toshiba Recommendation Setting */ + { 0x331e, 0x0f }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */ + { 0x3320, 0x30 }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */ + { 0x3321, 0x11 }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */ + { 0x3322, 0x98 }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */ + { 0x3323, 0x64 }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */ + { 0x3325, 0x83 }, /* Read Out Timing Control Toshiba Recommendation Setting */ + { 0x3330, 0x18 }, /* Read Out Timing Control Toshiba Recommendation Setting */ + { 0x333c, 0x01 }, /* Read Out Timing Control Toshiba Recommendation Setting */ + { 0x3345, 0x2f }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */ + { 0x33de, 0x38 }, /* Horizontal Noise Reduction Control Toshiba Recommendation Setting */ + /* Taken from v03. No idea what the rest are. */ + { 0x32e0, 0x05 }, + { 0x32e1, 0x05 }, + { 0x32e2, 0x04 }, + { 0x32e5, 0x04 }, + { 0x32e6, 0x04 }, + + }; + + return smiapp_write_8s(sensor, regs, ARRAY_SIZE(regs)); +} + +const struct smiapp_quirk smiapp_jt8ew9_quirk = { + .limits = jt8ew9_limits, + .post_poweron = jt8ew9_post_poweron, +}; + +static int imx125es_post_poweron(struct smiapp_sensor *sensor) +{ + /* Taken from v02. No idea what the other two are. */ + struct smiapp_reg_8 regs[] = { + /* + * 0x3302: clk during frame blanking: + * 0x00 - HS mode, 0x01 - LP11 + */ + { 0x3302, 0x01 }, + { 0x302d, 0x00 }, + { 0x3b08, 0x8c }, + }; + + return smiapp_write_8s(sensor, regs, ARRAY_SIZE(regs)); +} + +const struct smiapp_quirk smiapp_imx125es_quirk = { + .post_poweron = imx125es_post_poweron, +}; + +static int jt8ev1_limits(struct smiapp_sensor *sensor) +{ + smiapp_replace_limit(sensor, SMIAPP_LIMIT_X_ADDR_MAX, 4271); + smiapp_replace_limit(sensor, + SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN, 184); + + return 0; +} + +static int jt8ev1_post_poweron(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int rval; + + struct smiapp_reg_8 regs[] = { + { 0x3031, 0xcd }, /* For digital binning (EQ_MONI) */ + { 0x30a3, 0xd0 }, /* FLASH STROBE enable */ + { 0x3237, 0x00 }, /* For control of pulse timing for ADC */ + { 0x3238, 0x43 }, + { 0x3301, 0x06 }, /* For analog bias for sensor */ + { 0x3302, 0x06 }, + { 0x3304, 0x00 }, + { 0x3305, 0x88 }, + { 0x332a, 0x14 }, + { 0x332c, 0x6b }, + { 0x3336, 0x01 }, + { 0x333f, 0x1f }, + { 0x3355, 0x00 }, + { 0x3356, 0x20 }, + { 0x33bf, 0x20 }, /* Adjust the FBC speed */ + { 0x33c9, 0x20 }, + { 0x33ce, 0x30 }, /* Adjust the parameter for logic function */ + { 0x33cf, 0xec }, /* For Black sun */ + { 0x3328, 0x80 }, /* Ugh. No idea what's this. */ + }; + + struct smiapp_reg_8 regs_96[] = { + { 0x30ae, 0x00 }, /* For control of ADC clock */ + { 0x30af, 0xd0 }, + { 0x30b0, 0x01 }, + }; + + rval = smiapp_write_8s(sensor, regs, ARRAY_SIZE(regs)); + if (rval < 0) + return rval; + + switch (sensor->platform_data->ext_clk) { + case 9600000: + return smiapp_write_8s(sensor, regs_96, + ARRAY_SIZE(regs_96)); + default: + dev_warn(&client->dev, "no MSRs for %d Hz ext_clk\n", + sensor->platform_data->ext_clk); + return 0; + } +} + +static int jt8ev1_pre_streamon(struct smiapp_sensor *sensor) +{ + return smiapp_write_8(sensor, 0x3328, 0x00); +} + +static int jt8ev1_post_streamoff(struct smiapp_sensor *sensor) +{ + int rval; + + /* Workaround: allows fast standby to work properly */ + rval = smiapp_write_8(sensor, 0x3205, 0x04); + if (rval < 0) + return rval; + + /* Wait for 1 ms + one line => 2 ms is likely enough */ + usleep_range(2000, 2000); + + /* Restore it */ + rval = smiapp_write_8(sensor, 0x3205, 0x00); + if (rval < 0) + return rval; + + return smiapp_write_8(sensor, 0x3328, 0x80); +} + +const struct smiapp_quirk smiapp_jt8ev1_quirk = { + .limits = jt8ev1_limits, + .post_poweron = jt8ev1_post_poweron, + .pre_streamon = jt8ev1_pre_streamon, + .post_streamoff = jt8ev1_post_streamoff, + .flags = SMIAPP_QUIRK_FLAG_OP_PIX_CLOCK_PER_LANE, +}; + +static int tcm8500md_limits(struct smiapp_sensor *sensor) +{ + smiapp_replace_limit(sensor, SMIAPP_LIMIT_MIN_PLL_IP_FREQ_HZ, 2700000); + + return 0; +} + +const struct smiapp_quirk smiapp_tcm8500md_quirk = { + .limits = tcm8500md_limits, +}; diff --git a/drivers/media/video/smiapp/smiapp-quirk.h b/drivers/media/video/smiapp/smiapp-quirk.h new file mode 100644 index 000000000000..7a1b3a02a7bd --- /dev/null +++ b/drivers/media/video/smiapp/smiapp-quirk.h @@ -0,0 +1,72 @@ +/* + * drivers/media/video/smiapp/smiapp-quirk.h + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2011--2012 Nokia Corporation + * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> + * + * 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 __SMIAPP_QUIRK__ +#define __SMIAPP_QUIRK__ + +struct smiapp_sensor; + +/** + * struct smiapp_quirk - quirks for sensors that deviate from SMIA++ standard + * + * @limits: Replace sensor->limits with values which can't be read from + * sensor registers. Called the first time the sensor is powered up. + * @post_poweron: Called always after the sensor has been fully powered on. + * @pre_streamon: Called just before streaming is enabled. + * @post_streamon: Called right after stopping streaming. + */ +struct smiapp_quirk { + int (*limits)(struct smiapp_sensor *sensor); + int (*post_poweron)(struct smiapp_sensor *sensor); + int (*pre_streamon)(struct smiapp_sensor *sensor); + int (*post_streamoff)(struct smiapp_sensor *sensor); + unsigned long flags; +}; + +/* op pix clock is for all lanes in total normally */ +#define SMIAPP_QUIRK_FLAG_OP_PIX_CLOCK_PER_LANE (1 << 0) + +struct smiapp_reg_8 { + u16 reg; + u8 val; +}; + +void smiapp_replace_limit(struct smiapp_sensor *sensor, + u32 limit, u32 val); + +#define smiapp_call_quirk(_sensor, _quirk, ...) \ + (_sensor->minfo.quirk && \ + _sensor->minfo.quirk->_quirk ? \ + _sensor->minfo.quirk->_quirk(_sensor, ##__VA_ARGS__) : 0) + +#define smiapp_needs_quirk(_sensor, _quirk) \ + (_sensor->minfo.quirk ? \ + _sensor->minfo.quirk->flags & _quirk : 0) + +extern const struct smiapp_quirk smiapp_jt8ev1_quirk; +extern const struct smiapp_quirk smiapp_imx125es_quirk; +extern const struct smiapp_quirk smiapp_jt8ew9_quirk; +extern const struct smiapp_quirk smiapp_tcm8500md_quirk; + +#endif /* __SMIAPP_QUIRK__ */ diff --git a/drivers/media/video/smiapp/smiapp-reg-defs.h b/drivers/media/video/smiapp/smiapp-reg-defs.h new file mode 100644 index 000000000000..a089eb8161e1 --- /dev/null +++ b/drivers/media/video/smiapp/smiapp-reg-defs.h @@ -0,0 +1,503 @@ +/* + * drivers/media/video/smiapp/smiapp-reg-defs.h + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2011--2012 Nokia Corporation + * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> + * + * 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 + * + */ +#define SMIAPP_REG_MK_U8(r) ((SMIA_REG_8BIT << 16) | (r)) +#define SMIAPP_REG_MK_U16(r) ((SMIA_REG_16BIT << 16) | (r)) +#define SMIAPP_REG_MK_U32(r) ((SMIA_REG_32BIT << 16) | (r)) + +#define SMIAPP_REG_MK_F32(r) (SMIA_REG_FLAG_FLOAT | (SMIA_REG_32BIT << 16) | (r)) + +#define SMIAPP_REG_U16_MODEL_ID SMIAPP_REG_MK_U16(0x0000) +#define SMIAPP_REG_U8_REVISION_NUMBER_MAJOR SMIAPP_REG_MK_U8(0x0002) +#define SMIAPP_REG_U8_MANUFACTURER_ID SMIAPP_REG_MK_U8(0x0003) +#define SMIAPP_REG_U8_SMIA_VERSION SMIAPP_REG_MK_U8(0x0004) +#define SMIAPP_REG_U8_FRAME_COUNT SMIAPP_REG_MK_U8(0x0005) +#define SMIAPP_REG_U8_PIXEL_ORDER SMIAPP_REG_MK_U8(0x0006) +#define SMIAPP_REG_U16_DATA_PEDESTAL SMIAPP_REG_MK_U16(0x0008) +#define SMIAPP_REG_U8_PIXEL_DEPTH SMIAPP_REG_MK_U8(0x000c) +#define SMIAPP_REG_U8_REVISION_NUMBER_MINOR SMIAPP_REG_MK_U8(0x0010) +#define SMIAPP_REG_U8_SMIAPP_VERSION SMIAPP_REG_MK_U8(0x0011) +#define SMIAPP_REG_U8_MODULE_DATE_YEAR SMIAPP_REG_MK_U8(0x0012) +#define SMIAPP_REG_U8_MODULE_DATE_MONTH SMIAPP_REG_MK_U8(0x0013) +#define SMIAPP_REG_U8_MODULE_DATE_DAY SMIAPP_REG_MK_U8(0x0014) +#define SMIAPP_REG_U8_MODULE_DATE_PHASE SMIAPP_REG_MK_U8(0x0015) +#define SMIAPP_REG_U16_SENSOR_MODEL_ID SMIAPP_REG_MK_U16(0x0016) +#define SMIAPP_REG_U8_SENSOR_REVISION_NUMBER SMIAPP_REG_MK_U8(0x0018) +#define SMIAPP_REG_U8_SENSOR_MANUFACTURER_ID SMIAPP_REG_MK_U8(0x0019) +#define SMIAPP_REG_U8_SENSOR_FIRMWARE_VERSION SMIAPP_REG_MK_U8(0x001a) +#define SMIAPP_REG_U32_SERIAL_NUMBER SMIAPP_REG_MK_U32(0x001c) +#define SMIAPP_REG_U8_FRAME_FORMAT_MODEL_TYPE SMIAPP_REG_MK_U8(0x0040) +#define SMIAPP_REG_U8_FRAME_FORMAT_MODEL_SUBTYPE SMIAPP_REG_MK_U8(0x0041) +#define SMIAPP_REG_U16_FRAME_FORMAT_DESCRIPTOR_2(n) SMIAPP_REG_MK_U16(0x0042 + ((n) << 1)) /* 0 <= n <= 14 */ +#define SMIAPP_REG_U32_FRAME_FORMAT_DESCRIPTOR_4(n) SMIAPP_REG_MK_U32(0x0060 + ((n) << 2)) /* 0 <= n <= 7 */ +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CAPABILITY SMIAPP_REG_MK_U16(0x0080) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_MIN SMIAPP_REG_MK_U16(0x0084) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_MAX SMIAPP_REG_MK_U16(0x0086) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_STEP SMIAPP_REG_MK_U16(0x0088) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_TYPE SMIAPP_REG_MK_U16(0x008a) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_M0 SMIAPP_REG_MK_U16(0x008c) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_C0 SMIAPP_REG_MK_U16(0x008e) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_M1 SMIAPP_REG_MK_U16(0x0090) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_C1 SMIAPP_REG_MK_U16(0x0092) +#define SMIAPP_REG_U8_DATA_FORMAT_MODEL_TYPE SMIAPP_REG_MK_U8(0x00c0) +#define SMIAPP_REG_U8_DATA_FORMAT_MODEL_SUBTYPE SMIAPP_REG_MK_U8(0x00c1) +#define SMIAPP_REG_U16_DATA_FORMAT_DESCRIPTOR(n) SMIAPP_REG_MK_U16(0x00c2 + ((n) << 1)) +#define SMIAPP_REG_U8_MODE_SELECT SMIAPP_REG_MK_U8(0x0100) +#define SMIAPP_REG_U8_IMAGE_ORIENTATION SMIAPP_REG_MK_U8(0x0101) +#define SMIAPP_REG_U8_SOFTWARE_RESET SMIAPP_REG_MK_U8(0x0103) +#define SMIAPP_REG_U8_GROUPED_PARAMETER_HOLD SMIAPP_REG_MK_U8(0x0104) +#define SMIAPP_REG_U8_MASK_CORRUPTED_FRAMES SMIAPP_REG_MK_U8(0x0105) +#define SMIAPP_REG_U8_FAST_STANDBY_CTRL SMIAPP_REG_MK_U8(0x0106) +#define SMIAPP_REG_U8_CCI_ADDRESS_CONTROL SMIAPP_REG_MK_U8(0x0107) +#define SMIAPP_REG_U8_2ND_CCI_IF_CONTROL SMIAPP_REG_MK_U8(0x0108) +#define SMIAPP_REG_U8_2ND_CCI_ADDRESS_CONTROL SMIAPP_REG_MK_U8(0x0109) +#define SMIAPP_REG_U8_CSI_CHANNEL_IDENTIFIER SMIAPP_REG_MK_U8(0x0110) +#define SMIAPP_REG_U8_CSI_SIGNALLING_MODE SMIAPP_REG_MK_U8(0x0111) +#define SMIAPP_REG_U16_CSI_DATA_FORMAT SMIAPP_REG_MK_U16(0x0112) +#define SMIAPP_REG_U8_CSI_LANE_MODE SMIAPP_REG_MK_U8(0x0114) +#define SMIAPP_REG_U8_CSI2_10_TO_8_DT SMIAPP_REG_MK_U8(0x0115) +#define SMIAPP_REG_U8_CSI2_10_TO_7_DT SMIAPP_REG_MK_U8(0x0116) +#define SMIAPP_REG_U8_CSI2_10_TO_6_DT SMIAPP_REG_MK_U8(0x0117) +#define SMIAPP_REG_U8_CSI2_12_TO_8_DT SMIAPP_REG_MK_U8(0x0118) +#define SMIAPP_REG_U8_CSI2_12_TO_7_DT SMIAPP_REG_MK_U8(0x0119) +#define SMIAPP_REG_U8_CSI2_12_TO_6_DT SMIAPP_REG_MK_U8(0x011a) +#define SMIAPP_REG_U8_CSI2_14_TO_10_DT SMIAPP_REG_MK_U8(0x011b) +#define SMIAPP_REG_U8_CSI2_14_TO_8_DT SMIAPP_REG_MK_U8(0x011c) +#define SMIAPP_REG_U8_CSI2_16_TO_10_DT SMIAPP_REG_MK_U8(0x011d) +#define SMIAPP_REG_U8_CSI2_16_TO_8_DT SMIAPP_REG_MK_U8(0x011e) +#define SMIAPP_REG_U8_GAIN_MODE SMIAPP_REG_MK_U8(0x0120) +#define SMIAPP_REG_U16_VANA_VOLTAGE SMIAPP_REG_MK_U16(0x0130) +#define SMIAPP_REG_U16_VDIG_VOLTAGE SMIAPP_REG_MK_U16(0x0132) +#define SMIAPP_REG_U16_VIO_VOLTAGE SMIAPP_REG_MK_U16(0x0134) +#define SMIAPP_REG_U16_EXTCLK_FREQUENCY_MHZ SMIAPP_REG_MK_U16(0x0136) +#define SMIAPP_REG_U8_TEMP_SENSOR_CONTROL SMIAPP_REG_MK_U8(0x0138) +#define SMIAPP_REG_U8_TEMP_SENSOR_MODE SMIAPP_REG_MK_U8(0x0139) +#define SMIAPP_REG_U8_TEMP_SENSOR_OUTPUT SMIAPP_REG_MK_U8(0x013a) +#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME SMIAPP_REG_MK_U16(0x0200) +#define SMIAPP_REG_U16_COARSE_INTEGRATION_TIME SMIAPP_REG_MK_U16(0x0202) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GLOBAL SMIAPP_REG_MK_U16(0x0204) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GREENR SMIAPP_REG_MK_U16(0x0206) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_RED SMIAPP_REG_MK_U16(0x0208) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_BLUE SMIAPP_REG_MK_U16(0x020a) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GREENB SMIAPP_REG_MK_U16(0x020c) +#define SMIAPP_REG_U16_DIGITAL_GAIN_GREENR SMIAPP_REG_MK_U16(0x020e) +#define SMIAPP_REG_U16_DIGITAL_GAIN_RED SMIAPP_REG_MK_U16(0x0210) +#define SMIAPP_REG_U16_DIGITAL_GAIN_BLUE SMIAPP_REG_MK_U16(0x0212) +#define SMIAPP_REG_U16_DIGITAL_GAIN_GREENB SMIAPP_REG_MK_U16(0x0214) +#define SMIAPP_REG_U16_VT_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x0300) +#define SMIAPP_REG_U16_VT_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x0302) +#define SMIAPP_REG_U16_PRE_PLL_CLK_DIV SMIAPP_REG_MK_U16(0x0304) +#define SMIAPP_REG_U16_PLL_MULTIPLIER SMIAPP_REG_MK_U16(0x0306) +#define SMIAPP_REG_U16_OP_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x0308) +#define SMIAPP_REG_U16_OP_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x030a) +#define SMIAPP_REG_U16_FRAME_LENGTH_LINES SMIAPP_REG_MK_U16(0x0340) +#define SMIAPP_REG_U16_LINE_LENGTH_PCK SMIAPP_REG_MK_U16(0x0342) +#define SMIAPP_REG_U16_X_ADDR_START SMIAPP_REG_MK_U16(0x0344) +#define SMIAPP_REG_U16_Y_ADDR_START SMIAPP_REG_MK_U16(0x0346) +#define SMIAPP_REG_U16_X_ADDR_END SMIAPP_REG_MK_U16(0x0348) +#define SMIAPP_REG_U16_Y_ADDR_END SMIAPP_REG_MK_U16(0x034a) +#define SMIAPP_REG_U16_X_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x034c) +#define SMIAPP_REG_U16_Y_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x034e) +#define SMIAPP_REG_U16_X_EVEN_INC SMIAPP_REG_MK_U16(0x0380) +#define SMIAPP_REG_U16_X_ODD_INC SMIAPP_REG_MK_U16(0x0382) +#define SMIAPP_REG_U16_Y_EVEN_INC SMIAPP_REG_MK_U16(0x0384) +#define SMIAPP_REG_U16_Y_ODD_INC SMIAPP_REG_MK_U16(0x0386) +#define SMIAPP_REG_U16_SCALING_MODE SMIAPP_REG_MK_U16(0x0400) +#define SMIAPP_REG_U16_SPATIAL_SAMPLING SMIAPP_REG_MK_U16(0x0402) +#define SMIAPP_REG_U16_SCALE_M SMIAPP_REG_MK_U16(0x0404) +#define SMIAPP_REG_U16_SCALE_N SMIAPP_REG_MK_U16(0x0406) +#define SMIAPP_REG_U16_DIGITAL_CROP_X_OFFSET SMIAPP_REG_MK_U16(0x0408) +#define SMIAPP_REG_U16_DIGITAL_CROP_Y_OFFSET SMIAPP_REG_MK_U16(0x040a) +#define SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_WIDTH SMIAPP_REG_MK_U16(0x040c) +#define SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_HEIGHT SMIAPP_REG_MK_U16(0x040e) +#define SMIAPP_REG_U16_COMPRESSION_MODE SMIAPP_REG_MK_U16(0x0500) +#define SMIAPP_REG_U16_TEST_PATTERN_MODE SMIAPP_REG_MK_U16(0x0600) +#define SMIAPP_REG_U16_TEST_DATA_RED SMIAPP_REG_MK_U16(0x0602) +#define SMIAPP_REG_U16_TEST_DATA_GREENR SMIAPP_REG_MK_U16(0x0604) +#define SMIAPP_REG_U16_TEST_DATA_BLUE SMIAPP_REG_MK_U16(0x0606) +#define SMIAPP_REG_U16_TEST_DATA_GREENB SMIAPP_REG_MK_U16(0x0608) +#define SMIAPP_REG_U16_HORIZONTAL_CURSOR_WIDTH SMIAPP_REG_MK_U16(0x060a) +#define SMIAPP_REG_U16_HORIZONTAL_CURSOR_POSITION SMIAPP_REG_MK_U16(0x060c) +#define SMIAPP_REG_U16_VERTICAL_CURSOR_WIDTH SMIAPP_REG_MK_U16(0x060e) +#define SMIAPP_REG_U16_VERTICAL_CURSOR_POSITION SMIAPP_REG_MK_U16(0x0610) +#define SMIAPP_REG_U16_FIFO_WATER_MARK_PIXELS SMIAPP_REG_MK_U16(0x0700) +#define SMIAPP_REG_U8_TCLK_POST SMIAPP_REG_MK_U8(0x0800) +#define SMIAPP_REG_U8_THS_PREPARE SMIAPP_REG_MK_U8(0x0801) +#define SMIAPP_REG_U8_THS_ZERO_MIN SMIAPP_REG_MK_U8(0x0802) +#define SMIAPP_REG_U8_THS_TRAIL SMIAPP_REG_MK_U8(0x0803) +#define SMIAPP_REG_U8_TCLK_TRAIL_MIN SMIAPP_REG_MK_U8(0x0804) +#define SMIAPP_REG_U8_TCLK_PREPARE SMIAPP_REG_MK_U8(0x0805) +#define SMIAPP_REG_U8_TCLK_ZERO SMIAPP_REG_MK_U8(0x0806) +#define SMIAPP_REG_U8_TLPX SMIAPP_REG_MK_U8(0x0807) +#define SMIAPP_REG_U8_DPHY_CTRL SMIAPP_REG_MK_U8(0x0808) +#define SMIAPP_REG_U32_REQUESTED_LINK_BIT_RATE_MBPS SMIAPP_REG_MK_U32(0x0820) +#define SMIAPP_REG_U8_BINNING_MODE SMIAPP_REG_MK_U8(0x0900) +#define SMIAPP_REG_U8_BINNING_TYPE SMIAPP_REG_MK_U8(0x0901) +#define SMIAPP_REG_U8_BINNING_WEIGHTING SMIAPP_REG_MK_U8(0x0902) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_CTRL SMIAPP_REG_MK_U8(0x0a00) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_STATUS SMIAPP_REG_MK_U8(0x0a01) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_PAGE_SELECT SMIAPP_REG_MK_U8(0x0a02) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_0 SMIAPP_REG_MK_U8(0x0a04) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_1 SMIAPP_REG_MK_U8(0x0a05) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_2 SMIAPP_REG_MK_U8(0x0a06) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_3 SMIAPP_REG_MK_U8(0x0a07) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_4 SMIAPP_REG_MK_U8(0x0a08) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_5 SMIAPP_REG_MK_U8(0x0a09) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_12 SMIAPP_REG_MK_U8(0x0a10) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_13 SMIAPP_REG_MK_U8(0x0a11) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_14 SMIAPP_REG_MK_U8(0x0a12) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_15 SMIAPP_REG_MK_U8(0x0a13) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_16 SMIAPP_REG_MK_U8(0x0a14) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_17 SMIAPP_REG_MK_U8(0x0a15) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_18 SMIAPP_REG_MK_U8(0x0a16) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_19 SMIAPP_REG_MK_U8(0x0a17) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_20 SMIAPP_REG_MK_U8(0x0a18) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_21 SMIAPP_REG_MK_U8(0x0a19) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_22 SMIAPP_REG_MK_U8(0x0a1a) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_23 SMIAPP_REG_MK_U8(0x0a1b) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_24 SMIAPP_REG_MK_U8(0x0a1c) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_25 SMIAPP_REG_MK_U8(0x0a1d) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_26 SMIAPP_REG_MK_U8(0x0a1e) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_27 SMIAPP_REG_MK_U8(0x0a1f) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_28 SMIAPP_REG_MK_U8(0x0a20) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_29 SMIAPP_REG_MK_U8(0x0a21) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_30 SMIAPP_REG_MK_U8(0x0a22) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_31 SMIAPP_REG_MK_U8(0x0a23) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_32 SMIAPP_REG_MK_U8(0x0a24) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_33 SMIAPP_REG_MK_U8(0x0a25) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_34 SMIAPP_REG_MK_U8(0x0a26) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_35 SMIAPP_REG_MK_U8(0x0a27) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_36 SMIAPP_REG_MK_U8(0x0a28) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_37 SMIAPP_REG_MK_U8(0x0a29) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_38 SMIAPP_REG_MK_U8(0x0a2a) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_39 SMIAPP_REG_MK_U8(0x0a2b) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_40 SMIAPP_REG_MK_U8(0x0a2c) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_41 SMIAPP_REG_MK_U8(0x0a2d) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_42 SMIAPP_REG_MK_U8(0x0a2e) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_43 SMIAPP_REG_MK_U8(0x0a2f) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_44 SMIAPP_REG_MK_U8(0x0a30) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_45 SMIAPP_REG_MK_U8(0x0a31) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_46 SMIAPP_REG_MK_U8(0x0a32) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_47 SMIAPP_REG_MK_U8(0x0a33) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_48 SMIAPP_REG_MK_U8(0x0a34) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_49 SMIAPP_REG_MK_U8(0x0a35) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_50 SMIAPP_REG_MK_U8(0x0a36) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_51 SMIAPP_REG_MK_U8(0x0a37) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_52 SMIAPP_REG_MK_U8(0x0a38) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_53 SMIAPP_REG_MK_U8(0x0a39) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_54 SMIAPP_REG_MK_U8(0x0a3a) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_55 SMIAPP_REG_MK_U8(0x0a3b) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_56 SMIAPP_REG_MK_U8(0x0a3c) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_57 SMIAPP_REG_MK_U8(0x0a3d) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_58 SMIAPP_REG_MK_U8(0x0a3e) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_59 SMIAPP_REG_MK_U8(0x0a3f) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_60 SMIAPP_REG_MK_U8(0x0a40) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_61 SMIAPP_REG_MK_U8(0x0a41) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_62 SMIAPP_REG_MK_U8(0x0a42) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_63 SMIAPP_REG_MK_U8(0x0a43) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_CTRL SMIAPP_REG_MK_U8(0x0a44) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_STATUS SMIAPP_REG_MK_U8(0x0a45) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_PAGE_SELECT SMIAPP_REG_MK_U8(0x0a46) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_0 SMIAPP_REG_MK_U8(0x0a48) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_1 SMIAPP_REG_MK_U8(0x0a49) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_2 SMIAPP_REG_MK_U8(0x0a4a) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_3 SMIAPP_REG_MK_U8(0x0a4b) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_4 SMIAPP_REG_MK_U8(0x0a4c) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_5 SMIAPP_REG_MK_U8(0x0a4d) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_6 SMIAPP_REG_MK_U8(0x0a4e) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_7 SMIAPP_REG_MK_U8(0x0a4f) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_8 SMIAPP_REG_MK_U8(0x0a50) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_9 SMIAPP_REG_MK_U8(0x0a51) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_10 SMIAPP_REG_MK_U8(0x0a52) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_11 SMIAPP_REG_MK_U8(0x0a53) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_12 SMIAPP_REG_MK_U8(0x0a54) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_13 SMIAPP_REG_MK_U8(0x0a55) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_14 SMIAPP_REG_MK_U8(0x0a56) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_15 SMIAPP_REG_MK_U8(0x0a57) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_16 SMIAPP_REG_MK_U8(0x0a58) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_17 SMIAPP_REG_MK_U8(0x0a59) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_18 SMIAPP_REG_MK_U8(0x0a5a) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_19 SMIAPP_REG_MK_U8(0x0a5b) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_20 SMIAPP_REG_MK_U8(0x0a5c) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_21 SMIAPP_REG_MK_U8(0x0a5d) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_22 SMIAPP_REG_MK_U8(0x0a5e) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_23 SMIAPP_REG_MK_U8(0x0a5f) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_24 SMIAPP_REG_MK_U8(0x0a60) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_25 SMIAPP_REG_MK_U8(0x0a61) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_26 SMIAPP_REG_MK_U8(0x0a62) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_27 SMIAPP_REG_MK_U8(0x0a63) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_28 SMIAPP_REG_MK_U8(0x0a64) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_29 SMIAPP_REG_MK_U8(0x0a65) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_30 SMIAPP_REG_MK_U8(0x0a66) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_31 SMIAPP_REG_MK_U8(0x0a67) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_32 SMIAPP_REG_MK_U8(0x0a68) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_33 SMIAPP_REG_MK_U8(0x0a69) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_34 SMIAPP_REG_MK_U8(0x0a6a) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_35 SMIAPP_REG_MK_U8(0x0a6b) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_36 SMIAPP_REG_MK_U8(0x0a6c) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_37 SMIAPP_REG_MK_U8(0x0a6d) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_38 SMIAPP_REG_MK_U8(0x0a6e) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_39 SMIAPP_REG_MK_U8(0x0a6f) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_40 SMIAPP_REG_MK_U8(0x0a70) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_41 SMIAPP_REG_MK_U8(0x0a71) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_42 SMIAPP_REG_MK_U8(0x0a72) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_43 SMIAPP_REG_MK_U8(0x0a73) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_44 SMIAPP_REG_MK_U8(0x0a74) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_45 SMIAPP_REG_MK_U8(0x0a75) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_46 SMIAPP_REG_MK_U8(0x0a76) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_47 SMIAPP_REG_MK_U8(0x0a77) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_48 SMIAPP_REG_MK_U8(0x0a78) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_49 SMIAPP_REG_MK_U8(0x0a79) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_50 SMIAPP_REG_MK_U8(0x0a7a) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_51 SMIAPP_REG_MK_U8(0x0a7b) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_52 SMIAPP_REG_MK_U8(0x0a7c) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_53 SMIAPP_REG_MK_U8(0x0a7d) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_54 SMIAPP_REG_MK_U8(0x0a7e) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_55 SMIAPP_REG_MK_U8(0x0a7f) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_56 SMIAPP_REG_MK_U8(0x0a80) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_57 SMIAPP_REG_MK_U8(0x0a81) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_58 SMIAPP_REG_MK_U8(0x0a82) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_59 SMIAPP_REG_MK_U8(0x0a83) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_60 SMIAPP_REG_MK_U8(0x0a84) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_61 SMIAPP_REG_MK_U8(0x0a85) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_62 SMIAPP_REG_MK_U8(0x0a86) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_63 SMIAPP_REG_MK_U8(0x0a87) +#define SMIAPP_REG_U8_SHADING_CORRECTION_ENABLE SMIAPP_REG_MK_U8(0x0b00) +#define SMIAPP_REG_U8_LUMINANCE_CORRECTION_LEVEL SMIAPP_REG_MK_U8(0x0b01) +#define SMIAPP_REG_U8_GREEN_IMBALANCE_FILTER_ENABLE SMIAPP_REG_MK_U8(0x0b02) +#define SMIAPP_REG_U8_GREEN_IMBALANCE_FILTER_WEIGHT SMIAPP_REG_MK_U8(0x0b03) +#define SMIAPP_REG_U8_BLACK_LEVEL_CORRECTION_ENABLE SMIAPP_REG_MK_U8(0x0b04) +#define SMIAPP_REG_U8_MAPPED_COUPLET_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b05) +#define SMIAPP_REG_U8_SINGLE_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b06) +#define SMIAPP_REG_U8_SINGLE_DEFECT_CORRECT_WEIGHT SMIAPP_REG_MK_U8(0x0b07) +#define SMIAPP_REG_U8_DYNAMIC_COUPLET_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b08) +#define SMIAPP_REG_U8_DYNAMIC_COUPLET_CORRECT_WEIGHT SMIAPP_REG_MK_U8(0x0b09) +#define SMIAPP_REG_U8_COMBINED_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b0a) +#define SMIAPP_REG_U8_COMBINED_DEFECT_CORRECT_WEIGHT SMIAPP_REG_MK_U8(0x0b0b) +#define SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_ENABLE SMIAPP_REG_MK_U8(0x0b0c) +#define SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_WEIGHT SMIAPP_REG_MK_U8(0x0b0d) +#define SMIAPP_REG_U8_MAPPED_LINE_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b0e) +#define SMIAPP_REG_U8_MAPPED_LINE_DEFECT_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b0f) +#define SMIAPP_REG_U8_MAPPED_COUPLET_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b10) +#define SMIAPP_REG_U8_MAPPED_TRIPLET_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b11) +#define SMIAPP_REG_U8_MAPPED_TRIPLET_DEFECT_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b12) +#define SMIAPP_REG_U8_DYNAMIC_TRIPLET_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b13) +#define SMIAPP_REG_U8_DYNAMIC_TRIPLET_DEFECT_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b14) +#define SMIAPP_REG_U8_DYNAMIC_LINE_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b15) +#define SMIAPP_REG_U8_DYNAMIC_LINE_DEFECT_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b16) +#define SMIAPP_REG_U8_EDOF_MODE SMIAPP_REG_MK_U8(0x0b80) +#define SMIAPP_REG_U8_SHARPNESS SMIAPP_REG_MK_U8(0x0b83) +#define SMIAPP_REG_U8_DENOISING SMIAPP_REG_MK_U8(0x0b84) +#define SMIAPP_REG_U8_MODULE_SPECIFIC SMIAPP_REG_MK_U8(0x0b85) +#define SMIAPP_REG_U16_DEPTH_OF_FIELD SMIAPP_REG_MK_U16(0x0b86) +#define SMIAPP_REG_U16_FOCUS_DISTANCE SMIAPP_REG_MK_U16(0x0b88) +#define SMIAPP_REG_U8_ESTIMATION_MODE_CTRL SMIAPP_REG_MK_U8(0x0b8a) +#define SMIAPP_REG_U16_COLOUR_TEMPERATURE SMIAPP_REG_MK_U16(0x0b8c) +#define SMIAPP_REG_U16_ABSOLUTE_GAIN_GREENR SMIAPP_REG_MK_U16(0x0b8e) +#define SMIAPP_REG_U16_ABSOLUTE_GAIN_RED SMIAPP_REG_MK_U16(0x0b90) +#define SMIAPP_REG_U16_ABSOLUTE_GAIN_BLUE SMIAPP_REG_MK_U16(0x0b92) +#define SMIAPP_REG_U16_ABSOLUTE_GAIN_GREENB SMIAPP_REG_MK_U16(0x0b94) +#define SMIAPP_REG_U8_ESTIMATION_ZONE_MODE SMIAPP_REG_MK_U8(0x0bc0) +#define SMIAPP_REG_U16_FIXED_ZONE_WEIGHTING SMIAPP_REG_MK_U16(0x0bc2) +#define SMIAPP_REG_U16_CUSTOM_ZONE_X_START SMIAPP_REG_MK_U16(0x0bc4) +#define SMIAPP_REG_U16_CUSTOM_ZONE_Y_START SMIAPP_REG_MK_U16(0x0bc6) +#define SMIAPP_REG_U16_CUSTOM_ZONE_WIDTH SMIAPP_REG_MK_U16(0x0bc8) +#define SMIAPP_REG_U16_CUSTOM_ZONE_HEIGHT SMIAPP_REG_MK_U16(0x0bca) +#define SMIAPP_REG_U8_GLOBAL_RESET_CTRL1 SMIAPP_REG_MK_U8(0x0c00) +#define SMIAPP_REG_U8_GLOBAL_RESET_CTRL2 SMIAPP_REG_MK_U8(0x0c01) +#define SMIAPP_REG_U8_GLOBAL_RESET_MODE_CONFIG_1 SMIAPP_REG_MK_U8(0x0c02) +#define SMIAPP_REG_U8_GLOBAL_RESET_MODE_CONFIG_2 SMIAPP_REG_MK_U8(0x0c03) +#define SMIAPP_REG_U16_TRDY_CTRL SMIAPP_REG_MK_U16(0x0c04) +#define SMIAPP_REG_U16_TRDOUT_CTRL SMIAPP_REG_MK_U16(0x0c06) +#define SMIAPP_REG_U16_TSHUTTER_STROBE_DELAY_CTRL SMIAPP_REG_MK_U16(0x0c08) +#define SMIAPP_REG_U16_TSHUTTER_STROBE_WIDTH_CTRL SMIAPP_REG_MK_U16(0x0c0a) +#define SMIAPP_REG_U16_TFLASH_STROBE_DELAY_CTRL SMIAPP_REG_MK_U16(0x0c0c) +#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_HIGH_CTRL SMIAPP_REG_MK_U16(0x0c0e) +#define SMIAPP_REG_U16_TGRST_INTERVAL_CTRL SMIAPP_REG_MK_U16(0x0c10) +#define SMIAPP_REG_U8_FLASH_STROBE_ADJUSTMENT SMIAPP_REG_MK_U8(0x0c12) +#define SMIAPP_REG_U16_FLASH_STROBE_START_POINT SMIAPP_REG_MK_U16(0x0c14) +#define SMIAPP_REG_U16_TFLASH_STROBE_DELAY_RS_CTRL SMIAPP_REG_MK_U16(0x0c16) +#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_HIGH_RS_CTRL SMIAPP_REG_MK_U16(0x0c18) +#define SMIAPP_REG_U8_FLASH_MODE_RS SMIAPP_REG_MK_U8(0x0c1a) +#define SMIAPP_REG_U8_FLASH_TRIGGER_RS SMIAPP_REG_MK_U8(0x0c1b) +#define SMIAPP_REG_U8_FLASH_STATUS SMIAPP_REG_MK_U8(0x0c1c) +#define SMIAPP_REG_U8_SA_STROBE_MODE SMIAPP_REG_MK_U8(0x0c1d) +#define SMIAPP_REG_U16_SA_STROBE_START_POINT SMIAPP_REG_MK_U16(0x0c1e) +#define SMIAPP_REG_U16_TSA_STROBE_DELAY_CTRL SMIAPP_REG_MK_U16(0x0c20) +#define SMIAPP_REG_U16_TSA_STROBE_WIDTH_CTRL SMIAPP_REG_MK_U16(0x0c22) +#define SMIAPP_REG_U8_SA_STROBE_TRIGGER SMIAPP_REG_MK_U8(0x0c24) +#define SMIAPP_REG_U8_SPECIAL_ACTUATOR_STATUS SMIAPP_REG_MK_U8(0x0c25) +#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH2_HIGH_RS_CTRL SMIAPP_REG_MK_U16(0x0c26) +#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_LOW_RS_CTRL SMIAPP_REG_MK_U16(0x0c28) +#define SMIAPP_REG_U8_TFLASH_STROBE_COUNT_RS_CTRL SMIAPP_REG_MK_U8(0x0c2a) +#define SMIAPP_REG_U8_TFLASH_STROBE_COUNT_CTRL SMIAPP_REG_MK_U8(0x0c2b) +#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH2_HIGH_CTRL SMIAPP_REG_MK_U16(0x0c2c) +#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_LOW_CTRL SMIAPP_REG_MK_U16(0x0c2e) +#define SMIAPP_REG_U8_LOW_LEVEL_CTRL SMIAPP_REG_MK_U8(0x0c80) +#define SMIAPP_REG_U16_MAIN_TRIGGER_REF_POINT SMIAPP_REG_MK_U16(0x0c82) +#define SMIAPP_REG_U16_MAIN_TRIGGER_T3 SMIAPP_REG_MK_U16(0x0c84) +#define SMIAPP_REG_U8_MAIN_TRIGGER_COUNT SMIAPP_REG_MK_U8(0x0c86) +#define SMIAPP_REG_U16_PHASE1_TRIGGER_T3 SMIAPP_REG_MK_U16(0x0c88) +#define SMIAPP_REG_U8_PHASE1_TRIGGER_COUNT SMIAPP_REG_MK_U8(0x0c8a) +#define SMIAPP_REG_U16_PHASE2_TRIGGER_T3 SMIAPP_REG_MK_U16(0x0c8c) +#define SMIAPP_REG_U8_PHASE2_TRIGGER_COUNT SMIAPP_REG_MK_U8(0x0c8e) +#define SMIAPP_REG_U8_MECH_SHUTTER_CTRL SMIAPP_REG_MK_U8(0x0d00) +#define SMIAPP_REG_U8_OPERATION_MODE SMIAPP_REG_MK_U8(0x0d01) +#define SMIAPP_REG_U8_ACT_STATE1 SMIAPP_REG_MK_U8(0x0d02) +#define SMIAPP_REG_U8_ACT_STATE2 SMIAPP_REG_MK_U8(0x0d03) +#define SMIAPP_REG_U16_FOCUS_CHANGE SMIAPP_REG_MK_U16(0x0d80) +#define SMIAPP_REG_U16_FOCUS_CHANGE_CONTROL SMIAPP_REG_MK_U16(0x0d82) +#define SMIAPP_REG_U16_FOCUS_CHANGE_NUMBER_PHASE1 SMIAPP_REG_MK_U16(0x0d84) +#define SMIAPP_REG_U16_FOCUS_CHANGE_NUMBER_PHASE2 SMIAPP_REG_MK_U16(0x0d86) +#define SMIAPP_REG_U8_STROBE_COUNT_PHASE1 SMIAPP_REG_MK_U8(0x0d88) +#define SMIAPP_REG_U8_STROBE_COUNT_PHASE2 SMIAPP_REG_MK_U8(0x0d89) +#define SMIAPP_REG_U8_POSITION SMIAPP_REG_MK_U8(0x0d8a) +#define SMIAPP_REG_U8_BRACKETING_LUT_CONTROL SMIAPP_REG_MK_U8(0x0e00) +#define SMIAPP_REG_U8_BRACKETING_LUT_MODE SMIAPP_REG_MK_U8(0x0e01) +#define SMIAPP_REG_U8_BRACKETING_LUT_ENTRY_CONTROL SMIAPP_REG_MK_U8(0x0e02) +#define SMIAPP_REG_U8_LUT_PARAMETERS_START SMIAPP_REG_MK_U8(0x0e10) +#define SMIAPP_REG_U8_LUT_PARAMETERS_END SMIAPP_REG_MK_U8(0x0eff) +#define SMIAPP_REG_U16_INTEGRATION_TIME_CAPABILITY SMIAPP_REG_MK_U16(0x1000) +#define SMIAPP_REG_U16_COARSE_INTEGRATION_TIME_MIN SMIAPP_REG_MK_U16(0x1004) +#define SMIAPP_REG_U16_COARSE_INTEGRATION_TIME_MAX_MARGIN SMIAPP_REG_MK_U16(0x1006) +#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MIN SMIAPP_REG_MK_U16(0x1008) +#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MAX_MARGIN SMIAPP_REG_MK_U16(0x100a) +#define SMIAPP_REG_U16_DIGITAL_GAIN_CAPABILITY SMIAPP_REG_MK_U16(0x1080) +#define SMIAPP_REG_U16_DIGITAL_GAIN_MIN SMIAPP_REG_MK_U16(0x1084) +#define SMIAPP_REG_U16_DIGITAL_GAIN_MAX SMIAPP_REG_MK_U16(0x1086) +#define SMIAPP_REG_U16_DIGITAL_GAIN_STEP_SIZE SMIAPP_REG_MK_U16(0x1088) +#define SMIAPP_REG_F32_MIN_EXT_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1100) +#define SMIAPP_REG_F32_MAX_EXT_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1104) +#define SMIAPP_REG_U16_MIN_PRE_PLL_CLK_DIV SMIAPP_REG_MK_U16(0x1108) +#define SMIAPP_REG_U16_MAX_PRE_PLL_CLK_DIV SMIAPP_REG_MK_U16(0x110a) +#define SMIAPP_REG_F32_MIN_PLL_IP_FREQ_HZ SMIAPP_REG_MK_F32(0x110c) +#define SMIAPP_REG_F32_MAX_PLL_IP_FREQ_HZ SMIAPP_REG_MK_F32(0x1110) +#define SMIAPP_REG_U16_MIN_PLL_MULTIPLIER SMIAPP_REG_MK_U16(0x1114) +#define SMIAPP_REG_U16_MAX_PLL_MULTIPLIER SMIAPP_REG_MK_U16(0x1116) +#define SMIAPP_REG_F32_MIN_PLL_OP_FREQ_HZ SMIAPP_REG_MK_F32(0x1118) +#define SMIAPP_REG_F32_MAX_PLL_OP_FREQ_HZ SMIAPP_REG_MK_F32(0x111c) +#define SMIAPP_REG_U16_MIN_VT_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x1120) +#define SMIAPP_REG_U16_MAX_VT_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x1122) +#define SMIAPP_REG_F32_MIN_VT_SYS_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1124) +#define SMIAPP_REG_F32_MAX_VT_SYS_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1128) +#define SMIAPP_REG_F32_MIN_VT_PIX_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x112c) +#define SMIAPP_REG_F32_MAX_VT_PIX_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1130) +#define SMIAPP_REG_U16_MIN_VT_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x1134) +#define SMIAPP_REG_U16_MAX_VT_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x1136) +#define SMIAPP_REG_U16_MIN_FRAME_LENGTH_LINES SMIAPP_REG_MK_U16(0x1140) +#define SMIAPP_REG_U16_MAX_FRAME_LENGTH_LINES SMIAPP_REG_MK_U16(0x1142) +#define SMIAPP_REG_U16_MIN_LINE_LENGTH_PCK SMIAPP_REG_MK_U16(0x1144) +#define SMIAPP_REG_U16_MAX_LINE_LENGTH_PCK SMIAPP_REG_MK_U16(0x1146) +#define SMIAPP_REG_U16_MIN_LINE_BLANKING_PCK SMIAPP_REG_MK_U16(0x1148) +#define SMIAPP_REG_U16_MIN_FRAME_BLANKING_LINES SMIAPP_REG_MK_U16(0x114a) +#define SMIAPP_REG_U8_MIN_LINE_LENGTH_PCK_STEP_SIZE SMIAPP_REG_MK_U8(0x114c) +#define SMIAPP_REG_U16_MIN_OP_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x1160) +#define SMIAPP_REG_U16_MAX_OP_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x1162) +#define SMIAPP_REG_F32_MIN_OP_SYS_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1164) +#define SMIAPP_REG_F32_MAX_OP_SYS_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1168) +#define SMIAPP_REG_U16_MIN_OP_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x116c) +#define SMIAPP_REG_U16_MAX_OP_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x116e) +#define SMIAPP_REG_F32_MIN_OP_PIX_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1170) +#define SMIAPP_REG_F32_MAX_OP_PIX_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1174) +#define SMIAPP_REG_U16_X_ADDR_MIN SMIAPP_REG_MK_U16(0x1180) +#define SMIAPP_REG_U16_Y_ADDR_MIN SMIAPP_REG_MK_U16(0x1182) +#define SMIAPP_REG_U16_X_ADDR_MAX SMIAPP_REG_MK_U16(0x1184) +#define SMIAPP_REG_U16_Y_ADDR_MAX SMIAPP_REG_MK_U16(0x1186) +#define SMIAPP_REG_U16_MIN_X_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x1188) +#define SMIAPP_REG_U16_MIN_Y_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x118a) +#define SMIAPP_REG_U16_MAX_X_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x118c) +#define SMIAPP_REG_U16_MAX_Y_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x118e) +#define SMIAPP_REG_U16_MIN_EVEN_INC SMIAPP_REG_MK_U16(0x11c0) +#define SMIAPP_REG_U16_MAX_EVEN_INC SMIAPP_REG_MK_U16(0x11c2) +#define SMIAPP_REG_U16_MIN_ODD_INC SMIAPP_REG_MK_U16(0x11c4) +#define SMIAPP_REG_U16_MAX_ODD_INC SMIAPP_REG_MK_U16(0x11c6) +#define SMIAPP_REG_U16_SCALING_CAPABILITY SMIAPP_REG_MK_U16(0x1200) +#define SMIAPP_REG_U16_SCALER_M_MIN SMIAPP_REG_MK_U16(0x1204) +#define SMIAPP_REG_U16_SCALER_M_MAX SMIAPP_REG_MK_U16(0x1206) +#define SMIAPP_REG_U16_SCALER_N_MIN SMIAPP_REG_MK_U16(0x1208) +#define SMIAPP_REG_U16_SCALER_N_MAX SMIAPP_REG_MK_U16(0x120a) +#define SMIAPP_REG_U16_SPATIAL_SAMPLING_CAPABILITY SMIAPP_REG_MK_U16(0x120c) +#define SMIAPP_REG_U8_DIGITAL_CROP_CAPABILITY SMIAPP_REG_MK_U8(0x120e) +#define SMIAPP_REG_U16_COMPRESSION_CAPABILITY SMIAPP_REG_MK_U16(0x1300) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_REDINRED SMIAPP_REG_MK_U16(0x1400) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_GREENINRED SMIAPP_REG_MK_U16(0x1402) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_BLUEINRED SMIAPP_REG_MK_U16(0x1404) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_REDINGREEN SMIAPP_REG_MK_U16(0x1406) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_GREENINGREEN SMIAPP_REG_MK_U16(0x1408) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_BLUEINGREEN SMIAPP_REG_MK_U16(0x140a) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_REDINBLUE SMIAPP_REG_MK_U16(0x140c) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_GREENINBLUE SMIAPP_REG_MK_U16(0x140e) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_BLUEINBLUE SMIAPP_REG_MK_U16(0x1410) +#define SMIAPP_REG_U16_FIFO_SIZE_PIXELS SMIAPP_REG_MK_U16(0x1500) +#define SMIAPP_REG_U8_FIFO_SUPPORT_CAPABILITY SMIAPP_REG_MK_U8(0x1502) +#define SMIAPP_REG_U8_DPHY_CTRL_CAPABILITY SMIAPP_REG_MK_U8(0x1600) +#define SMIAPP_REG_U8_CSI_LANE_MODE_CAPABILITY SMIAPP_REG_MK_U8(0x1601) +#define SMIAPP_REG_U8_CSI_SIGNALLING_MODE_CAPABILITY SMIAPP_REG_MK_U8(0x1602) +#define SMIAPP_REG_U8_FAST_STANDBY_CAPABILITY SMIAPP_REG_MK_U8(0x1603) +#define SMIAPP_REG_U8_CCI_ADDRESS_CONTROL_CAPABILITY SMIAPP_REG_MK_U8(0x1604) +#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_1_LANE_MODE_MBPS SMIAPP_REG_MK_U32(0x1608) +#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_2_LANE_MODE_MBPS SMIAPP_REG_MK_U32(0x160c) +#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_3_LANE_MODE_MBPS SMIAPP_REG_MK_U32(0x1610) +#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_4_LANE_MODE_MBPS SMIAPP_REG_MK_U32(0x1614) +#define SMIAPP_REG_U8_TEMP_SENSOR_CAPABILITY SMIAPP_REG_MK_U8(0x1618) +#define SMIAPP_REG_U16_MIN_FRAME_LENGTH_LINES_BIN SMIAPP_REG_MK_U16(0x1700) +#define SMIAPP_REG_U16_MAX_FRAME_LENGTH_LINES_BIN SMIAPP_REG_MK_U16(0x1702) +#define SMIAPP_REG_U16_MIN_LINE_LENGTH_PCK_BIN SMIAPP_REG_MK_U16(0x1704) +#define SMIAPP_REG_U16_MAX_LINE_LENGTH_PCK_BIN SMIAPP_REG_MK_U16(0x1706) +#define SMIAPP_REG_U16_MIN_LINE_BLANKING_PCK_BIN SMIAPP_REG_MK_U16(0x1708) +#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MIN_BIN SMIAPP_REG_MK_U16(0x170a) +#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN SMIAPP_REG_MK_U16(0x170c) +#define SMIAPP_REG_U8_BINNING_CAPABILITY SMIAPP_REG_MK_U8(0x1710) +#define SMIAPP_REG_U8_BINNING_WEIGHTING_CAPABILITY SMIAPP_REG_MK_U8(0x1711) +#define SMIAPP_REG_U8_BINNING_SUBTYPES SMIAPP_REG_MK_U8(0x1712) +#define SMIAPP_REG_U8_BINNING_TYPE_n(n) SMIAPP_REG_MK_U8(0x1713 + (n)) /* 1 <= n <= 237 */ +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_CAPABILITY SMIAPP_REG_MK_U8(0x1800) +#define SMIAPP_REG_U8_SHADING_CORRECTION_CAPABILITY SMIAPP_REG_MK_U8(0x1900) +#define SMIAPP_REG_U8_GREEN_IMBALANCE_CAPABILITY SMIAPP_REG_MK_U8(0x1901) +#define SMIAPP_REG_U8_BLACK_LEVEL_CAPABILITY SMIAPP_REG_MK_U8(0x1902) +#define SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_CAPABILITY SMIAPP_REG_MK_U8(0x1903) +#define SMIAPP_REG_U16_DEFECT_CORRECTION_CAPABILITY SMIAPP_REG_MK_U16(0x1904) +#define SMIAPP_REG_U16_DEFECT_CORRECTION_CAPABILITY_2 SMIAPP_REG_MK_U16(0x1906) +#define SMIAPP_REG_U8_EDOF_CAPABILITY SMIAPP_REG_MK_U8(0x1980) +#define SMIAPP_REG_U8_ESTIMATION_FRAMES SMIAPP_REG_MK_U8(0x1981) +#define SMIAPP_REG_U8_SUPPORTS_SHARPNESS_ADJ SMIAPP_REG_MK_U8(0x1982) +#define SMIAPP_REG_U8_SUPPORTS_DENOISING_ADJ SMIAPP_REG_MK_U8(0x1983) +#define SMIAPP_REG_U8_SUPPORTS_MODULE_SPECIFIC_ADJ SMIAPP_REG_MK_U8(0x1984) +#define SMIAPP_REG_U8_SUPPORTS_DEPTH_OF_FIELD_ADJ SMIAPP_REG_MK_U8(0x1985) +#define SMIAPP_REG_U8_SUPPORTS_FOCUS_DISTANCE_ADJ SMIAPP_REG_MK_U8(0x1986) +#define SMIAPP_REG_U8_COLOUR_FEEDBACK_CAPABILITY SMIAPP_REG_MK_U8(0x1987) +#define SMIAPP_REG_U8_EDOF_SUPPORT_AB_NXM SMIAPP_REG_MK_U8(0x1988) +#define SMIAPP_REG_U8_ESTIMATION_MODE_CAPABILITY SMIAPP_REG_MK_U8(0x19c0) +#define SMIAPP_REG_U8_ESTIMATION_ZONE_CAPABILITY SMIAPP_REG_MK_U8(0x19c1) +#define SMIAPP_REG_U16_EST_DEPTH_OF_FIELD SMIAPP_REG_MK_U16(0x19c2) +#define SMIAPP_REG_U16_EST_FOCUS_DISTANCE SMIAPP_REG_MK_U16(0x19c4) +#define SMIAPP_REG_U16_CAPABILITY_TRDY_MIN SMIAPP_REG_MK_U16(0x1a00) +#define SMIAPP_REG_U8_FLASH_MODE_CAPABILITY SMIAPP_REG_MK_U8(0x1a02) +#define SMIAPP_REG_U16_MECH_SHUT_AND_ACT_START_ADDR SMIAPP_REG_MK_U16(0x1b02) +#define SMIAPP_REG_U8_ACTUATOR_CAPABILITY SMIAPP_REG_MK_U8(0x1b04) +#define SMIAPP_REG_U16_ACTUATOR_TYPE SMIAPP_REG_MK_U16(0x1b40) +#define SMIAPP_REG_U8_AF_DEVICE_ADDRESS SMIAPP_REG_MK_U8(0x1b42) +#define SMIAPP_REG_U16_FOCUS_CHANGE_ADDRESS SMIAPP_REG_MK_U16(0x1b44) +#define SMIAPP_REG_U8_BRACKETING_LUT_CAPABILITY_1 SMIAPP_REG_MK_U8(0x1c00) +#define SMIAPP_REG_U8_BRACKETING_LUT_CAPABILITY_2 SMIAPP_REG_MK_U8(0x1c01) +#define SMIAPP_REG_U8_BRACKETING_LUT_SIZE SMIAPP_REG_MK_U8(0x1c02) diff --git a/drivers/media/video/smiapp/smiapp-reg.h b/drivers/media/video/smiapp/smiapp-reg.h new file mode 100644 index 000000000000..d0167aa17534 --- /dev/null +++ b/drivers/media/video/smiapp/smiapp-reg.h @@ -0,0 +1,122 @@ +/* + * drivers/media/video/smiapp/smiapp-reg.h + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2011--2012 Nokia Corporation + * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> + * + * 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 __SMIAPP_REG_H_ +#define __SMIAPP_REG_H_ + +#include "smiapp-reg-defs.h" + +/* Bits for above register */ +#define SMIAPP_IMAGE_ORIENTATION_HFLIP (1 << 0) +#define SMIAPP_IMAGE_ORIENTATION_VFLIP (1 << 1) + +#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_EN (1 << 0) +#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_RD_EN (0 << 1) +#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_WR_EN (1 << 1) +#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_ERR_CLEAR (1 << 2) +#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_RD_READY (1 << 0) +#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_WR_READY (1 << 1) +#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_EDATA (1 << 2) +#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_EUSAGE (1 << 3) + +#define SMIAPP_SOFTWARE_RESET (1 << 0) + +#define SMIAPP_FLASH_MODE_CAPABILITY_SINGLE_STROBE (1 << 0) +#define SMIAPP_FLASH_MODE_CAPABILITY_MULTIPLE_STROBE (1 << 1) + +#define SMIAPP_DPHY_CTRL_AUTOMATIC 0 +/* DPHY control based on REQUESTED_LINK_BIT_RATE_MBPS */ +#define SMIAPP_DPHY_CTRL_UI 1 +#define SMIAPP_DPHY_CTRL_REGISTER 2 + +#define SMIAPP_COMPRESSION_MODE_SIMPLE_PREDICTOR 1 +#define SMIAPP_COMPRESSION_MODE_ADVANCED_PREDICTOR 2 + +#define SMIAPP_MODE_SELECT_SOFTWARE_STANDBY 0 +#define SMIAPP_MODE_SELECT_STREAMING 1 + +#define SMIAPP_SCALING_MODE_NONE 0 +#define SMIAPP_SCALING_MODE_HORIZONTAL 1 +#define SMIAPP_SCALING_MODE_BOTH 2 + +#define SMIAPP_SCALING_CAPABILITY_NONE 0 +#define SMIAPP_SCALING_CAPABILITY_HORIZONTAL 1 +#define SMIAPP_SCALING_CAPABILITY_BOTH 2 /* horizontal/both */ + +/* digital crop right before scaler */ +#define SMIAPP_DIGITAL_CROP_CAPABILITY_NONE 0 +#define SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP 1 + +#define SMIAPP_BINNING_CAPABILITY_NO 0 +#define SMIAPP_BINNING_CAPABILITY_YES 1 + +/* Maximum number of binning subtypes */ +#define SMIAPP_BINNING_SUBTYPES 253 + +#define SMIAPP_PIXEL_ORDER_GRBG 0 +#define SMIAPP_PIXEL_ORDER_RGGB 1 +#define SMIAPP_PIXEL_ORDER_BGGR 2 +#define SMIAPP_PIXEL_ORDER_GBRG 3 + +#define SMIAPP_DATA_FORMAT_MODEL_TYPE_NORMAL 1 +#define SMIAPP_DATA_FORMAT_MODEL_TYPE_EXTENDED 2 +#define SMIAPP_DATA_FORMAT_MODEL_TYPE_NORMAL_N 8 +#define SMIAPP_DATA_FORMAT_MODEL_TYPE_EXTENDED_N 16 + +#define SMIAPP_FRAME_FORMAT_MODEL_TYPE_2BYTE 0x01 +#define SMIAPP_FRAME_FORMAT_MODEL_TYPE_4BYTE 0x02 +#define SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NROWS_MASK 0x0f +#define SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NCOLS_MASK 0xf0 +#define SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NCOLS_SHIFT 4 + +#define SMIAPP_FRAME_FORMAT_DESC_2_PIXELCODE_MASK 0xf000 +#define SMIAPP_FRAME_FORMAT_DESC_2_PIXELCODE_SHIFT 12 +#define SMIAPP_FRAME_FORMAT_DESC_2_PIXELS_MASK 0x0fff + +#define SMIAPP_FRAME_FORMAT_DESC_4_PIXELCODE_MASK 0xf0000000 +#define SMIAPP_FRAME_FORMAT_DESC_4_PIXELCODE_SHIFT 28 +#define SMIAPP_FRAME_FORMAT_DESC_4_PIXELS_MASK 0x0000ffff + +#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_EMBEDDED 1 +#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_DUMMY 2 +#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_BLACK 3 +#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_DARK 4 +#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_VISIBLE 5 + +#define SMIAPP_FAST_STANDBY_CTRL_COMPLETE_FRAMES 0 +#define SMIAPP_FAST_STANDBY_CTRL_IMMEDIATE 1 + +/* Scaling N factor */ +#define SMIAPP_SCALE_N 16 + +/* Image statistics registers */ +/* Registers 0x2000 to 0x2fff are reserved for future + * use for statistics features. + */ + +/* Manufacturer Specific Registers: 0x3000 to 0x3fff + * The manufacturer specifies these as a black box. + */ + +#endif /* __SMIAPP_REG_H_ */ diff --git a/drivers/media/video/smiapp/smiapp-regs.c b/drivers/media/video/smiapp/smiapp-regs.c new file mode 100644 index 000000000000..4851ff710779 --- /dev/null +++ b/drivers/media/video/smiapp/smiapp-regs.c @@ -0,0 +1,213 @@ +/* + * drivers/media/video/smiapp/smiapp-regs.c + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2011--2012 Nokia Corporation + * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> + * + * 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 + * + */ + +#include "smiapp-debug.h" + +#include <linux/delay.h> +#include <linux/i2c.h> + +#include "smiapp-regs.h" + +static uint32_t float_to_u32_mul_1000000(struct i2c_client *client, + uint32_t phloat) +{ + int32_t exp; + uint64_t man; + + if (phloat >= 0x80000000) { + dev_err(&client->dev, "this is a negative number\n"); + return 0; + } + + if (phloat == 0x7f800000) + return ~0; /* Inf. */ + + if ((phloat & 0x7f800000) == 0x7f800000) { + dev_err(&client->dev, "NaN or other special number\n"); + return 0; + } + + /* Valid cases begin here */ + if (phloat == 0) + return 0; /* Valid zero */ + + if (phloat > 0x4f800000) + return ~0; /* larger than 4294967295 */ + + /* + * Unbias exponent (note how phloat is now guaranteed to + * have 0 in the high bit) + */ + exp = ((int32_t)phloat >> 23) - 127; + + /* Extract mantissa, add missing '1' bit and it's in MHz */ + man = ((phloat & 0x7fffff) | 0x800000) * 1000000ULL; + + if (exp < 0) + man >>= -exp; + else + man <<= exp; + + man >>= 23; /* Remove mantissa bias */ + + return man & 0xffffffff; +} + + +/* + * Read a 8/16/32-bit i2c register. The value is returned in 'val'. + * Returns zero if successful, or non-zero otherwise. + */ +int smiapp_read(struct i2c_client *client, u32 reg, u32 *val) +{ + struct i2c_msg msg; + unsigned char data[4]; + unsigned int len = (u8)(reg >> 16); + u16 offset = reg; + int r; + + if (len != SMIA_REG_8BIT && len != SMIA_REG_16BIT + && len != SMIA_REG_32BIT) + return -EINVAL; + + msg.addr = client->addr; + msg.flags = 0; + msg.len = 2; + msg.buf = data; + + /* high byte goes out first */ + data[0] = (u8) (offset >> 8); + data[1] = (u8) offset; + r = i2c_transfer(client->adapter, &msg, 1); + if (r != 1) { + if (r >= 0) + r = -EBUSY; + goto err; + } + + msg.len = len; + msg.flags = I2C_M_RD; + r = i2c_transfer(client->adapter, &msg, 1); + if (r != 1) { + if (r >= 0) + r = -EBUSY; + goto err; + } + + *val = 0; + /* high byte comes first */ + switch (len) { + case SMIA_REG_32BIT: + *val = (data[0] << 24) + (data[1] << 16) + (data[2] << 8) + + data[3]; + break; + case SMIA_REG_16BIT: + *val = (data[0] << 8) + data[1]; + break; + case SMIA_REG_8BIT: + *val = data[0]; + break; + default: + BUG(); + } + + if (reg & SMIA_REG_FLAG_FLOAT) + *val = float_to_u32_mul_1000000(client, *val); + + return 0; + +err: + dev_err(&client->dev, "read from offset 0x%x error %d\n", offset, r); + + return r; +} + +/* + * Write to a 8/16-bit register. + * Returns zero if successful, or non-zero otherwise. + */ +int smiapp_write(struct i2c_client *client, u32 reg, u32 val) +{ + struct i2c_msg msg; + unsigned char data[6]; + unsigned int retries; + unsigned int flags = reg >> 24; + unsigned int len = (u8)(reg >> 16); + u16 offset = reg; + int r; + + if ((len != SMIA_REG_8BIT && len != SMIA_REG_16BIT && + len != SMIA_REG_32BIT) || flags) + return -EINVAL; + + msg.addr = client->addr; + msg.flags = 0; /* Write */ + msg.len = 2 + len; + msg.buf = data; + + /* high byte goes out first */ + data[0] = (u8) (reg >> 8); + data[1] = (u8) (reg & 0xff); + + switch (len) { + case SMIA_REG_8BIT: + data[2] = val; + break; + case SMIA_REG_16BIT: + data[2] = val >> 8; + data[3] = val; + break; + case SMIA_REG_32BIT: + data[2] = val >> 24; + data[3] = val >> 16; + data[4] = val >> 8; + data[5] = val; + break; + default: + BUG(); + } + + for (retries = 0; retries < 5; retries++) { + /* + * Due to unknown reason sensor stops responding. This + * loop is a temporaty solution until the root cause + * is found. + */ + r = i2c_transfer(client->adapter, &msg, 1); + if (r == 1) { + if (retries) + dev_err(&client->dev, + "sensor i2c stall encountered. " + "retries: %d\n", retries); + return 0; + } + + usleep_range(2000, 2000); + } + + dev_err(&client->dev, + "wrote 0x%x to offset 0x%x error %d\n", val, offset, r); + + return r; +} diff --git a/drivers/media/video/smiapp/smiapp-regs.h b/drivers/media/video/smiapp/smiapp-regs.h new file mode 100644 index 000000000000..58e8009d4aa5 --- /dev/null +++ b/drivers/media/video/smiapp/smiapp-regs.h @@ -0,0 +1,46 @@ +/* + * include/media/smiapp/smiapp-regs.h + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2011--2012 Nokia Corporation + * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> + * + * 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 SMIAPP_REGS_H +#define SMIAPP_REGS_H + +#include <linux/i2c.h> +#include <linux/types.h> + +/* Use upper 8 bits of the type field for flags */ +#define SMIA_REG_FLAG_FLOAT (1 << 24) + +#define SMIA_REG_8BIT 1 +#define SMIA_REG_16BIT 2 +#define SMIA_REG_32BIT 4 +struct smia_reg { + u16 type; + u16 reg; /* 16-bit offset */ + u32 val; /* 8/16/32-bit value */ +}; + +int smiapp_read(struct i2c_client *client, u32 reg, u32 *val); +int smiapp_write(struct i2c_client *client, u32 reg, u32 val); + +#endif diff --git a/drivers/media/video/smiapp/smiapp.h b/drivers/media/video/smiapp/smiapp.h new file mode 100644 index 000000000000..805d8c8a3c18 --- /dev/null +++ b/drivers/media/video/smiapp/smiapp.h @@ -0,0 +1,251 @@ +/* + * drivers/media/video/smiapp/smiapp.h + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2010--2012 Nokia Corporation + * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> + * + * 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 __SMIAPP_PRIV_H_ +#define __SMIAPP_PRIV_H_ + +#include <linux/mutex.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-subdev.h> +#include <media/smiapp.h> + +#include "../smiapp-pll.h" +#include "smiapp-reg.h" +#include "smiapp-regs.h" +#include "smiapp-quirk.h" + +/* + * Standard SMIA++ constants + */ +#define SMIA_VERSION_1 10 +#define SMIAPP_VERSION_0_8 8 /* Draft 0.8 */ +#define SMIAPP_VERSION_0_9 9 /* Draft 0.9 */ +#define SMIAPP_VERSION_1 10 + +#define SMIAPP_PROFILE_0 0 +#define SMIAPP_PROFILE_1 1 +#define SMIAPP_PROFILE_2 2 + +#define SMIAPP_NVM_PAGE_SIZE 64 /* bytes */ + +#define SMIAPP_RESET_DELAY_CLOCKS 2400 +#define SMIAPP_RESET_DELAY(clk) \ + (1000 + (SMIAPP_RESET_DELAY_CLOCKS * 1000 \ + + (clk) / 1000 - 1) / ((clk) / 1000)) + +#include "smiapp-limits.h" + +struct smiapp_quirk; + +#define SMIAPP_MODULE_IDENT_FLAG_REV_LE (1 << 0) + +struct smiapp_module_ident { + u8 manufacturer_id; + u16 model_id; + u8 revision_number_major; + + u8 flags; + + char *name; + const struct smiapp_quirk *quirk; +}; + +struct smiapp_module_info { + u32 manufacturer_id; + u32 model_id; + u32 revision_number_major; + u32 revision_number_minor; + + u32 module_year; + u32 module_month; + u32 module_day; + + u32 sensor_manufacturer_id; + u32 sensor_model_id; + u32 sensor_revision_number; + u32 sensor_firmware_version; + + u32 smia_version; + u32 smiapp_version; + + u32 smiapp_profile; + + char *name; + const struct smiapp_quirk *quirk; +}; + +#define SMIAPP_IDENT_FQ(manufacturer, model, rev, fl, _name, _quirk) \ + { .manufacturer_id = manufacturer, \ + .model_id = model, \ + .revision_number_major = rev, \ + .flags = fl, \ + .name = _name, \ + .quirk = _quirk, } + +#define SMIAPP_IDENT_LQ(manufacturer, model, rev, _name, _quirk) \ + { .manufacturer_id = manufacturer, \ + .model_id = model, \ + .revision_number_major = rev, \ + .flags = SMIAPP_MODULE_IDENT_FLAG_REV_LE, \ + .name = _name, \ + .quirk = _quirk, } + +#define SMIAPP_IDENT_L(manufacturer, model, rev, _name) \ + { .manufacturer_id = manufacturer, \ + .model_id = model, \ + .revision_number_major = rev, \ + .flags = SMIAPP_MODULE_IDENT_FLAG_REV_LE, \ + .name = _name, } + +#define SMIAPP_IDENT_Q(manufacturer, model, rev, _name, _quirk) \ + { .manufacturer_id = manufacturer, \ + .model_id = model, \ + .revision_number_major = rev, \ + .flags = 0, \ + .name = _name, \ + .quirk = _quirk, } + +#define SMIAPP_IDENT(manufacturer, model, rev, _name) \ + { .manufacturer_id = manufacturer, \ + .model_id = model, \ + .revision_number_major = rev, \ + .flags = 0, \ + .name = _name, } + +struct smiapp_reg_limits { + u32 addr; + char *what; +}; + +extern struct smiapp_reg_limits smiapp_reg_limits[]; + +struct smiapp_csi_data_format { + u32 code; + u8 width; + u8 compressed; + u8 pixel_order; +}; + +#define SMIAPP_SUBDEVS 3 + +#define SMIAPP_PA_PAD_SRC 0 +#define SMIAPP_PAD_SINK 0 +#define SMIAPP_PAD_SRC 1 +#define SMIAPP_PADS 2 + +struct smiapp_binning_subtype { + u8 horizontal:4; + u8 vertical:4; +} __packed; + +struct smiapp_subdev { + struct v4l2_subdev sd; + struct media_pad pads[2]; + struct v4l2_rect sink_fmt; + struct v4l2_rect crop[2]; + struct v4l2_rect compose; /* compose on sink */ + unsigned short sink_pad; + unsigned short source_pad; + int npads; + struct smiapp_sensor *sensor; + struct v4l2_ctrl_handler ctrl_handler; +}; + +/* + * struct smiapp_sensor - Main device structure + */ +struct smiapp_sensor { + /* + * "mutex" is used to serialise access to all fields here + * except v4l2_ctrls at the end of the struct. "mutex" is also + * used to serialise access to file handle specific + * information. The exception to this rule is the power_mutex + * below. + */ + struct mutex mutex; + /* + * power_mutex is used to serialise power management related + * activities. Acquiring "mutex" at that time isn't necessary + * since there are no other users anyway. + */ + struct mutex power_mutex; + struct smiapp_subdev ssds[SMIAPP_SUBDEVS]; + u32 ssds_used; + struct smiapp_subdev *src; + struct smiapp_subdev *binner; + struct smiapp_subdev *scaler; + struct smiapp_subdev *pixel_array; + struct smiapp_platform_data *platform_data; + struct regulator *vana; + u32 limits[SMIAPP_LIMIT_LAST]; + u8 nbinning_subtypes; + struct smiapp_binning_subtype binning_subtypes[SMIAPP_BINNING_SUBTYPES]; + u32 mbus_frame_fmts; + const struct smiapp_csi_data_format *csi_format; + const struct smiapp_csi_data_format *internal_csi_format; + u32 default_mbus_frame_fmts; + int default_pixel_order; + + u8 binning_horizontal; + u8 binning_vertical; + + u8 scale_m; + u8 scaling_mode; + + u8 hvflip_inv_mask; /* H/VFLIP inversion due to sensor orientation */ + u8 flash_capability; + u8 frame_skip; + + int power_count; + + bool streaming; + bool dev_init_done; + + u8 *nvm; /* nvm memory buffer */ + unsigned int nvm_size; /* bytes */ + + struct smiapp_module_info minfo; + + struct smiapp_pll pll; + + /* Pixel array controls */ + struct v4l2_ctrl *analog_gain; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *pixel_rate_parray; + /* src controls */ + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *pixel_rate_csi; +}; + +#define to_smiapp_subdev(_sd) \ + container_of(_sd, struct smiapp_subdev, sd) + +#define to_smiapp_sensor(_sd) \ + (to_smiapp_subdev(_sd)->sensor) + +#endif /* __SMIAPP_PRIV_H_ */ diff --git a/drivers/media/video/sn9c102/sn9c102_core.c b/drivers/media/video/sn9c102/sn9c102_core.c index c2882fa5be85..19ea780b16ff 100644 --- a/drivers/media/video/sn9c102/sn9c102_core.c +++ b/drivers/media/video/sn9c102/sn9c102_core.c @@ -995,10 +995,8 @@ static int sn9c102_stop_transfer(struct sn9c102_device* cam) static int sn9c102_stream_interrupt(struct sn9c102_device* cam) { - long timeout; - cam->stream = STREAM_INTERRUPT; - timeout = wait_event_timeout(cam->wait_stream, + wait_event_timeout(cam->wait_stream, (cam->stream == STREAM_OFF) || (cam->state & DEV_DISCONNECTED), SN9C102_URB_TIMEOUT); diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index aedb970d13f6..c3ab55883c2d 100644 --- a/drivers/media/video/soc_camera.c +++ b/drivers/media/video/soc_camera.c @@ -1429,6 +1429,10 @@ static int video_dev_create(struct soc_camera_device *icd) vdev->tvnorms = V4L2_STD_UNKNOWN; vdev->ctrl_handler = &icd->ctrl_handler; vdev->lock = &icd->video_lock; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags); icd->vdev = vdev; diff --git a/drivers/media/video/stk-webcam.c b/drivers/media/video/stk-webcam.c index d427f8436c70..86a0fc56c330 100644 --- a/drivers/media/video/stk-webcam.c +++ b/drivers/media/video/stk-webcam.c @@ -38,13 +38,13 @@ #include "stk-webcam.h" -static bool hflip = 1; +static bool hflip; module_param(hflip, bool, 0444); -MODULE_PARM_DESC(hflip, "Horizontal image flip (mirror). Defaults to 1"); +MODULE_PARM_DESC(hflip, "Horizontal image flip (mirror). Defaults to 0"); -static bool vflip = 1; +static bool vflip; module_param(vflip, bool, 0444); -MODULE_PARM_DESC(vflip, "Vertical image flip. Defaults to 1"); +MODULE_PARM_DESC(vflip, "Vertical image flip. Defaults to 0"); static int debug; module_param(debug, int, 0444); diff --git a/drivers/media/video/tda9840.c b/drivers/media/video/tda9840.c index 465d7086babf..3d7ddd93282d 100644 --- a/drivers/media/video/tda9840.c +++ b/drivers/media/video/tda9840.c @@ -66,29 +66,53 @@ static void tda9840_write(struct v4l2_subdev *sd, u8 reg, u8 val) val, reg); } +static int tda9840_status(struct v4l2_subdev *sd) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 byte; + + if (1 != i2c_master_recv(client, &byte, 1)) { + v4l2_dbg(1, debug, sd, + "i2c_master_recv() failed\n"); + return -EIO; + } + + if (byte & 0x80) { + v4l2_dbg(1, debug, sd, + "TDA9840_DETECT: register contents invalid\n"); + return -EINVAL; + } + + v4l2_dbg(1, debug, sd, "TDA9840_DETECT: byte: 0x%02x\n", byte); + return byte & 0x60; +} + static int tda9840_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *t) { + int stat = tda9840_status(sd); int byte; if (t->index) return -EINVAL; - switch (t->audmode) { - case V4L2_TUNER_MODE_STEREO: - byte = TDA9840_SET_STEREO; - break; - case V4L2_TUNER_MODE_LANG1_LANG2: - byte = TDA9840_SET_BOTH; - break; - case V4L2_TUNER_MODE_LANG1: - byte = TDA9840_SET_LANG1; - break; - case V4L2_TUNER_MODE_LANG2: - byte = TDA9840_SET_LANG2; - break; - default: + stat = stat < 0 ? 0 : stat; + if (stat == 0 || stat == 0x60) /* mono input */ byte = TDA9840_SET_MONO; - break; + else if (stat == 0x40) /* stereo input */ + byte = (t->audmode == V4L2_TUNER_MODE_MONO) ? + TDA9840_SET_MONO : TDA9840_SET_STEREO; + else { /* bilingual */ + switch (t->audmode) { + case V4L2_TUNER_MODE_LANG1_LANG2: + byte = TDA9840_SET_BOTH; + break; + case V4L2_TUNER_MODE_LANG2: + byte = TDA9840_SET_LANG2; + break; + default: + byte = TDA9840_SET_LANG1; + break; + } } v4l2_dbg(1, debug, sd, "TDA9840_SWITCH: 0x%02x\n", byte); tda9840_write(sd, SWITCH, byte); @@ -97,25 +121,14 @@ static int tda9840_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *t) static int tda9840_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *t) { - struct i2c_client *client = v4l2_get_subdevdata(sd); - u8 byte; - - t->rxsubchans = V4L2_TUNER_SUB_MONO; - if (1 != i2c_master_recv(client, &byte, 1)) { - v4l2_dbg(1, debug, sd, - "i2c_master_recv() failed\n"); - return -EIO; - } + int stat = tda9840_status(sd); - if (byte & 0x80) { - v4l2_dbg(1, debug, sd, - "TDA9840_DETECT: register contents invalid\n"); - return -EINVAL; - } + if (stat < 0) + return stat; - v4l2_dbg(1, debug, sd, "TDA9840_DETECT: byte: 0x%02x\n", byte); + t->rxsubchans = V4L2_TUNER_SUB_MONO; - switch (byte & 0x60) { + switch (stat & 0x60) { case 0x00: t->rxsubchans = V4L2_TUNER_SUB_MONO; break; diff --git a/drivers/media/video/tlg2300/pd-video.c b/drivers/media/video/tlg2300/pd-video.c index a794ae62aebf..bfbf9e56b0a4 100644 --- a/drivers/media/video/tlg2300/pd-video.c +++ b/drivers/media/video/tlg2300/pd-video.c @@ -150,7 +150,6 @@ static int vidioc_querycap(struct file *file, void *fh, strcpy(cap->driver, "tele-video"); strcpy(cap->card, "Telegent Poseidon"); usb_make_path(p->udev, cap->bus_info, sizeof(cap->bus_info)); - cap->version = KERNEL_VERSION(0, 0, 1); cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_STREAMING | V4L2_CAP_READWRITE | V4L2_CAP_VBI_CAPTURE; diff --git a/drivers/media/video/tm6000/tm6000-input.c b/drivers/media/video/tm6000/tm6000-input.c index 859eb90e4d56..e80b7e190471 100644 --- a/drivers/media/video/tm6000/tm6000-input.c +++ b/drivers/media/video/tm6000/tm6000-input.c @@ -168,7 +168,6 @@ static void tm6000_ir_urb_received(struct urb *urb) struct tm6000_IR *ir = dev->ir; struct tm6000_ir_poll_result poll_result; char *buf; - int rc; dprintk(2, "%s\n",__func__); if (urb->status < 0 || urb->actual_length <= 0) { @@ -192,7 +191,7 @@ static void tm6000_ir_urb_received(struct urb *urb) dprintk(1, "%s, scancode: 0x%04x\n",__func__, poll_result.rc_data); rc_keydown(ir->rc, poll_result.rc_data, 0); - rc = usb_submit_urb(urb, GFP_ATOMIC); + usb_submit_urb(urb, GFP_ATOMIC); /* * Flash the led. We can't do it here, as it is running on IRQ context. * So, use the scheduler to do it, in a few ms. diff --git a/drivers/media/video/tm6000/tm6000-stds.c b/drivers/media/video/tm6000/tm6000-stds.c index 9dc0831d813f..5e28d6a2412f 100644 --- a/drivers/media/video/tm6000/tm6000-stds.c +++ b/drivers/media/video/tm6000/tm6000-stds.c @@ -338,7 +338,6 @@ static int tm6000_set_audio_std(struct tm6000_core *dev) uint8_t areg_02 = 0x04; /* GC1 Fixed gain 0dB */ uint8_t areg_05 = 0x01; /* Auto 4.5 = M Japan, Auto 6.5 = DK */ uint8_t areg_06 = 0x02; /* Auto de-emphasis, mannual channel mode */ - uint8_t nicam_flag = 0; /* No NICAM */ if (dev->radio) { tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x00); @@ -398,7 +397,6 @@ static int tm6000_set_audio_std(struct tm6000_core *dev) } else { areg_05 = 0x07; } - nicam_flag = 1; break; /* other */ case 3: diff --git a/drivers/media/video/tm6000/tm6000-video.c b/drivers/media/video/tm6000/tm6000-video.c index bc13db736e24..f7034df94e0a 100644 --- a/drivers/media/video/tm6000/tm6000-video.c +++ b/drivers/media/video/tm6000/tm6000-video.c @@ -169,7 +169,6 @@ static inline void get_next_buf(struct tm6000_dmaqueue *dma_q, struct tm6000_buffer **buf) { struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq); - char *outp; if (list_empty(&dma_q->active)) { dprintk(dev, V4L2_DEBUG_QUEUE, "No active queue to serve\n"); @@ -179,11 +178,6 @@ static inline void get_next_buf(struct tm6000_dmaqueue *dma_q, *buf = list_entry(dma_q->active.next, struct tm6000_buffer, vb.queue); - - /* Cleans up buffer - Useful for testing for frame/URB loss */ - outp = videobuf_to_vmalloc(&(*buf)->vb); - - return; } /* @@ -211,7 +205,7 @@ static int copy_streams(u8 *data, unsigned long len, { struct tm6000_dmaqueue *dma_q = urb->context; struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq); - u8 *ptr = data, *endp = data+len, c; + u8 *ptr = data, *endp = data+len; unsigned long header = 0; int rc = 0; unsigned int cmd, cpysize, pktsize, size, field, block, line, pos = 0; @@ -264,7 +258,6 @@ static int copy_streams(u8 *data, unsigned long len, } /* split the header fields */ - c = (header >> 24) & 0xff; size = ((header & 0x7e) << 1); if (size > 0) size -= 4; @@ -889,7 +882,6 @@ static int vidioc_querycap(struct file *file, void *priv, strlcpy(cap->driver, "tm6000", sizeof(cap->driver)); strlcpy(cap->card, "Trident TVMaster TM5600/6000/6010", sizeof(cap->card)); - cap->version = TM6000_VERSION; cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | V4L2_CAP_AUDIO | @@ -1732,6 +1724,10 @@ static struct video_device *vdev_init(struct tm6000_core *dev, vfd->release = video_device_release; vfd->debug = tm6000_debug; vfd->lock = &dev->lock; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags); snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name); diff --git a/drivers/media/video/tm6000/tm6000.h b/drivers/media/video/tm6000/tm6000.h index 27ba659cfa85..6df418658c9c 100644 --- a/drivers/media/video/tm6000/tm6000.h +++ b/drivers/media/video/tm6000/tm6000.h @@ -33,8 +33,6 @@ #include "dvb_frontend.h" #include "dmxdev.h" -#define TM6000_VERSION KERNEL_VERSION(0, 0, 2) - /* Inputs */ enum tm6000_itype { TM6000_INPUT_TV = 1, diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c index a5c6397ad591..3e050e12153b 100644 --- a/drivers/media/video/tuner-core.c +++ b/drivers/media/video/tuner-core.c @@ -1241,8 +1241,10 @@ static int tuner_log_status(struct v4l2_subdev *sd) return 0; } -static int tuner_suspend(struct i2c_client *c, pm_message_t state) +#ifdef CONFIG_PM_SLEEP +static int tuner_suspend(struct device *dev) { + struct i2c_client *c = to_i2c_client(dev); struct tuner *t = to_tuner(i2c_get_clientdata(c)); struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; @@ -1254,8 +1256,9 @@ static int tuner_suspend(struct i2c_client *c, pm_message_t state) return 0; } -static int tuner_resume(struct i2c_client *c) +static int tuner_resume(struct device *dev) { + struct i2c_client *c = to_i2c_client(dev); struct tuner *t = to_tuner(i2c_get_clientdata(c)); tuner_dbg("resume\n"); @@ -1266,6 +1269,7 @@ static int tuner_resume(struct i2c_client *c) return 0; } +#endif static int tuner_command(struct i2c_client *client, unsigned cmd, void *arg) { @@ -1310,6 +1314,10 @@ static const struct v4l2_subdev_ops tuner_ops = { * I2C structs and module init functions */ +static const struct dev_pm_ops tuner_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(tuner_suspend, tuner_resume) +}; + static const struct i2c_device_id tuner_id[] = { { "tuner", }, /* autodetect */ { } @@ -1320,12 +1328,11 @@ static struct i2c_driver tuner_driver = { .driver = { .owner = THIS_MODULE, .name = "tuner", + .pm = &tuner_pm_ops, }, .probe = tuner_probe, .remove = tuner_remove, .command = tuner_command, - .suspend = tuner_suspend, - .resume = tuner_resume, .id_table = tuner_id, }; diff --git a/drivers/media/video/tvp5150.c b/drivers/media/video/tvp5150.c index 1326e11cf4a9..476a6a0bd108 100644 --- a/drivers/media/video/tvp5150.c +++ b/drivers/media/video/tvp5150.c @@ -830,19 +830,12 @@ static int tvp5150_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *f) { struct tvp5150 *decoder = to_tvp5150(sd); - v4l2_std_id std; if (f == NULL) return -EINVAL; tvp5150_reset(sd, 0); - /* Calculate height and width based on current standard */ - if (decoder->norm == V4L2_STD_ALL) - std = tvp5150_read_std(sd); - else - std = decoder->norm; - f->width = decoder->rect.width; f->height = decoder->rect.height; diff --git a/drivers/media/video/tvp7002.c b/drivers/media/video/tvp7002.c index d7676d85c4df..408b65e782e5 100644 --- a/drivers/media/video/tvp7002.c +++ b/drivers/media/video/tvp7002.c @@ -670,7 +670,6 @@ static int tvp7002_query_dv_preset(struct v4l2_subdev *sd, struct v4l2_dv_preset *qpreset) { const struct tvp7002_preset_definition *presets = tvp7002_presets; - struct tvp7002 *device; u8 progressive; u32 lpfr; u32 cpln; @@ -684,8 +683,6 @@ static int tvp7002_query_dv_preset(struct v4l2_subdev *sd, /* Return invalid preset if no active input is detected */ qpreset->preset = V4L2_DV_INVALID; - device = to_tvp7002(sd); - /* Read standards from device registers */ tvp7002_read_err(sd, TVP7002_L_FRAME_STAT_LSBS, &lpf_lsb, &error); tvp7002_read_err(sd, TVP7002_L_FRAME_STAT_MSBS, &lpf_msb, &error); diff --git a/drivers/media/video/usbvision/usbvision-core.c b/drivers/media/video/usbvision/usbvision-core.c index f344411a4578..c9b2042f8bdf 100644 --- a/drivers/media/video/usbvision/usbvision-core.c +++ b/drivers/media/video/usbvision/usbvision-core.c @@ -601,13 +601,12 @@ static int usbvision_decompress(struct usb_usbvision *usbvision, unsigned char * unsigned char *decompressed, int *start_pos, int *block_typestart_pos, int len) { - int rest_pixel, idx, max_pos, pos, extra_pos, block_len, block_type_pos, block_type_len; + int rest_pixel, idx, pos, extra_pos, block_len, block_type_pos, block_type_len; unsigned char block_byte, block_code, block_type, block_type_byte, integrator; integrator = 0; pos = *start_pos; block_type_pos = *block_typestart_pos; - max_pos = 396; /* pos + len; */ extra_pos = pos; block_len = 0; block_byte = 0; @@ -702,7 +701,7 @@ static enum parse_state usbvision_parse_compress(struct usb_usbvision *usbvision unsigned char strip_data[USBVISION_STRIP_LEN_MAX]; unsigned char strip_header[USBVISION_STRIP_HEADER_LEN]; int idx, idx_end, strip_len, strip_ptr, startblock_pos, block_pos, block_type_pos; - int clipmask_index, bytes_per_pixel, rc; + int clipmask_index; int image_size; unsigned char rv, gv, bv; static unsigned char *Y, *U, *V; @@ -769,7 +768,6 @@ static enum parse_state usbvision_parse_compress(struct usb_usbvision *usbvision return parse_state_next_frame; } - bytes_per_pixel = frame->v4l2_format.bytes_per_pixel; clipmask_index = frame->curline * MAX_FRAME_WIDTH; scratch_get(usbvision, strip_data, strip_len); @@ -781,14 +779,14 @@ static enum parse_state usbvision_parse_compress(struct usb_usbvision *usbvision usbvision->block_pos = block_pos; - rc = usbvision_decompress(usbvision, strip_data, Y, &block_pos, &block_type_pos, idx_end); + usbvision_decompress(usbvision, strip_data, Y, &block_pos, &block_type_pos, idx_end); if (strip_len > usbvision->max_strip_len) usbvision->max_strip_len = strip_len; if (frame->curline % 2) - rc = usbvision_decompress(usbvision, strip_data, V, &block_pos, &block_type_pos, idx_end / 2); + usbvision_decompress(usbvision, strip_data, V, &block_pos, &block_type_pos, idx_end / 2); else - rc = usbvision_decompress(usbvision, strip_data, U, &block_pos, &block_type_pos, idx_end / 2); + usbvision_decompress(usbvision, strip_data, U, &block_pos, &block_type_pos, idx_end / 2); if (block_pos > usbvision->comprblock_pos) usbvision->comprblock_pos = block_pos; diff --git a/drivers/media/video/usbvision/usbvision-video.c b/drivers/media/video/usbvision/usbvision-video.c index 5a74f5e07d7d..9bd8f084f348 100644 --- a/drivers/media/video/usbvision/usbvision-video.c +++ b/drivers/media/video/usbvision/usbvision-video.c @@ -1296,6 +1296,10 @@ static struct video_device *usbvision_vdev_init(struct usb_usbvision *usbvision, if (NULL == vdev) return NULL; *vdev = *vdev_template; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags); vdev->lock = &usbvision->v4l2_lock; vdev->v4l2_dev = &usbvision->v4l2_dev; snprintf(vdev->name, sizeof(vdev->name), "%s", name); diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c index 0efd3b10b353..28363b72ff8a 100644 --- a/drivers/media/video/uvc/uvc_ctrl.c +++ b/drivers/media/video/uvc/uvc_ctrl.c @@ -21,6 +21,7 @@ #include <linux/vmalloc.h> #include <linux/wait.h> #include <linux/atomic.h> +#include <media/v4l2-ctrls.h> #include "uvcvideo.h" @@ -420,6 +421,8 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_INTEGER, .data_type = UVC_CTRL_DATA_TYPE_SIGNED, + .master_id = V4L2_CID_HUE_AUTO, + .master_manual = 0, }, { .id = V4L2_CID_SATURATION, @@ -492,6 +495,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, + .slave_ids = { V4L2_CID_HUE, }, }, { .id = V4L2_CID_EXPOSURE_AUTO, @@ -504,6 +508,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .data_type = UVC_CTRL_DATA_TYPE_BITMASK, .menu_info = exposure_auto_controls, .menu_count = ARRAY_SIZE(exposure_auto_controls), + .slave_ids = { V4L2_CID_EXPOSURE_ABSOLUTE, }, }, { .id = V4L2_CID_EXPOSURE_AUTO_PRIORITY, @@ -524,6 +529,8 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_INTEGER, .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + .master_id = V4L2_CID_EXPOSURE_AUTO, + .master_manual = V4L2_EXPOSURE_MANUAL, }, { .id = V4L2_CID_AUTO_WHITE_BALANCE, @@ -534,6 +541,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, + .slave_ids = { V4L2_CID_WHITE_BALANCE_TEMPERATURE, }, }, { .id = V4L2_CID_WHITE_BALANCE_TEMPERATURE, @@ -544,6 +552,8 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_INTEGER, .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + .master_id = V4L2_CID_AUTO_WHITE_BALANCE, + .master_manual = 0, }, { .id = V4L2_CID_AUTO_WHITE_BALANCE, @@ -554,6 +564,8 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, + .slave_ids = { V4L2_CID_BLUE_BALANCE, + V4L2_CID_RED_BALANCE }, }, { .id = V4L2_CID_BLUE_BALANCE, @@ -564,6 +576,8 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_INTEGER, .data_type = UVC_CTRL_DATA_TYPE_SIGNED, + .master_id = V4L2_CID_AUTO_WHITE_BALANCE, + .master_manual = 0, }, { .id = V4L2_CID_RED_BALANCE, @@ -574,6 +588,8 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .offset = 16, .v4l2_type = V4L2_CTRL_TYPE_INTEGER, .data_type = UVC_CTRL_DATA_TYPE_SIGNED, + .master_id = V4L2_CID_AUTO_WHITE_BALANCE, + .master_manual = 0, }, { .id = V4L2_CID_FOCUS_ABSOLUTE, @@ -584,6 +600,8 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_INTEGER, .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + .master_id = V4L2_CID_FOCUS_AUTO, + .master_manual = 0, }, { .id = V4L2_CID_FOCUS_AUTO, @@ -594,6 +612,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, + .slave_ids = { V4L2_CID_FOCUS_ABSOLUTE, }, }, { .id = V4L2_CID_IRIS_ABSOLUTE, @@ -899,25 +918,54 @@ static int uvc_ctrl_populate_cache(struct uvc_video_chain *chain, return 0; } -int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, - struct v4l2_queryctrl *v4l2_ctrl) +static int __uvc_ctrl_get(struct uvc_video_chain *chain, + struct uvc_control *ctrl, struct uvc_control_mapping *mapping, + s32 *value) { - struct uvc_control *ctrl; - struct uvc_control_mapping *mapping; struct uvc_menu_info *menu; unsigned int i; int ret; - ret = mutex_lock_interruptible(&chain->ctrl_mutex); - if (ret < 0) - return -ERESTARTSYS; + if ((ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR) == 0) + return -EINVAL; - ctrl = uvc_find_control(chain, v4l2_ctrl->id, &mapping); - if (ctrl == NULL) { - ret = -EINVAL; - goto done; + if (!ctrl->loaded) { + ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, ctrl->entity->id, + chain->dev->intfnum, ctrl->info.selector, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), + ctrl->info.size); + if (ret < 0) + return ret; + + ctrl->loaded = 1; } + *value = mapping->get(mapping, UVC_GET_CUR, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT)); + + if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) { + menu = mapping->menu_info; + for (i = 0; i < mapping->menu_count; ++i, ++menu) { + if (menu->value == *value) { + *value = i; + break; + } + } + } + + return 0; +} + +static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, + struct uvc_control *ctrl, + struct uvc_control_mapping *mapping, + struct v4l2_queryctrl *v4l2_ctrl) +{ + struct uvc_control_mapping *master_map = NULL; + struct uvc_control *master_ctrl = NULL; + struct uvc_menu_info *menu; + unsigned int i; + memset(v4l2_ctrl, 0, sizeof *v4l2_ctrl); v4l2_ctrl->id = mapping->id; v4l2_ctrl->type = mapping->v4l2_type; @@ -929,10 +977,23 @@ int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, if (!(ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR)) v4l2_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + if (mapping->master_id) + __uvc_find_control(ctrl->entity, mapping->master_id, + &master_map, &master_ctrl, 0); + if (master_ctrl && (master_ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR)) { + s32 val; + int ret = __uvc_ctrl_get(chain, master_ctrl, master_map, &val); + if (ret < 0) + return ret; + + if (val != mapping->master_manual) + v4l2_ctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; + } + if (!ctrl->cached) { - ret = uvc_ctrl_populate_cache(chain, ctrl); + int ret = uvc_ctrl_populate_cache(chain, ctrl); if (ret < 0) - goto done; + return ret; } if (ctrl->info.flags & UVC_CTRL_FLAG_GET_DEF) { @@ -954,19 +1015,19 @@ int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, } } - goto done; + return 0; case V4L2_CTRL_TYPE_BOOLEAN: v4l2_ctrl->minimum = 0; v4l2_ctrl->maximum = 1; v4l2_ctrl->step = 1; - goto done; + return 0; case V4L2_CTRL_TYPE_BUTTON: v4l2_ctrl->minimum = 0; v4l2_ctrl->maximum = 0; v4l2_ctrl->step = 0; - goto done; + return 0; default: break; @@ -984,6 +1045,27 @@ int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, v4l2_ctrl->step = mapping->get(mapping, UVC_GET_RES, uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES)); + return 0; +} + +int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, + struct v4l2_queryctrl *v4l2_ctrl) +{ + struct uvc_control *ctrl; + struct uvc_control_mapping *mapping; + int ret; + + ret = mutex_lock_interruptible(&chain->ctrl_mutex); + if (ret < 0) + return -ERESTARTSYS; + + ctrl = uvc_find_control(chain, v4l2_ctrl->id, &mapping); + if (ctrl == NULL) { + ret = -EINVAL; + goto done; + } + + ret = __uvc_query_v4l2_ctrl(chain, ctrl, mapping, v4l2_ctrl); done: mutex_unlock(&chain->ctrl_mutex); return ret; @@ -1054,6 +1136,171 @@ done: return ret; } +/* -------------------------------------------------------------------------- + * Ctrl event handling + */ + +static void uvc_ctrl_fill_event(struct uvc_video_chain *chain, + struct v4l2_event *ev, + struct uvc_control *ctrl, + struct uvc_control_mapping *mapping, + s32 value, u32 changes) +{ + struct v4l2_queryctrl v4l2_ctrl; + + __uvc_query_v4l2_ctrl(chain, ctrl, mapping, &v4l2_ctrl); + + memset(ev->reserved, 0, sizeof(ev->reserved)); + ev->type = V4L2_EVENT_CTRL; + ev->id = v4l2_ctrl.id; + ev->u.ctrl.value = value; + ev->u.ctrl.changes = changes; + ev->u.ctrl.type = v4l2_ctrl.type; + ev->u.ctrl.flags = v4l2_ctrl.flags; + ev->u.ctrl.minimum = v4l2_ctrl.minimum; + ev->u.ctrl.maximum = v4l2_ctrl.maximum; + ev->u.ctrl.step = v4l2_ctrl.step; + ev->u.ctrl.default_value = v4l2_ctrl.default_value; +} + +static void uvc_ctrl_send_event(struct uvc_fh *handle, + struct uvc_control *ctrl, struct uvc_control_mapping *mapping, + s32 value, u32 changes) +{ + struct v4l2_subscribed_event *sev; + struct v4l2_event ev; + + if (list_empty(&mapping->ev_subs)) + return; + + uvc_ctrl_fill_event(handle->chain, &ev, ctrl, mapping, value, changes); + + list_for_each_entry(sev, &mapping->ev_subs, node) { + if (sev->fh && (sev->fh != &handle->vfh || + (sev->flags & V4L2_EVENT_SUB_FL_ALLOW_FEEDBACK) || + (changes & V4L2_EVENT_CTRL_CH_FLAGS))) + v4l2_event_queue_fh(sev->fh, &ev); + } +} + +static void uvc_ctrl_send_slave_event(struct uvc_fh *handle, + struct uvc_control *master, u32 slave_id, + const struct v4l2_ext_control *xctrls, unsigned int xctrls_count) +{ + struct uvc_control_mapping *mapping = NULL; + struct uvc_control *ctrl = NULL; + u32 changes = V4L2_EVENT_CTRL_CH_FLAGS; + unsigned int i; + s32 val = 0; + + /* + * We can skip sending an event for the slave if the slave + * is being modified in the same transaction. + */ + for (i = 0; i < xctrls_count; i++) { + if (xctrls[i].id == slave_id) + return; + } + + __uvc_find_control(master->entity, slave_id, &mapping, &ctrl, 0); + if (ctrl == NULL) + return; + + if (__uvc_ctrl_get(handle->chain, ctrl, mapping, &val) == 0) + changes |= V4L2_EVENT_CTRL_CH_VALUE; + + uvc_ctrl_send_event(handle, ctrl, mapping, val, changes); +} + +static void uvc_ctrl_send_events(struct uvc_fh *handle, + const struct v4l2_ext_control *xctrls, unsigned int xctrls_count) +{ + struct uvc_control_mapping *mapping; + struct uvc_control *ctrl; + u32 changes = V4L2_EVENT_CTRL_CH_VALUE; + unsigned int i; + unsigned int j; + + for (i = 0; i < xctrls_count; ++i) { + ctrl = uvc_find_control(handle->chain, xctrls[i].id, &mapping); + + for (j = 0; j < ARRAY_SIZE(mapping->slave_ids); ++j) { + if (!mapping->slave_ids[j]) + break; + uvc_ctrl_send_slave_event(handle, ctrl, + mapping->slave_ids[j], + xctrls, xctrls_count); + } + + /* + * If the master is being modified in the same transaction + * flags may change too. + */ + if (mapping->master_id) { + for (j = 0; j < xctrls_count; j++) { + if (xctrls[j].id == mapping->master_id) { + changes |= V4L2_EVENT_CTRL_CH_FLAGS; + break; + } + } + } + + uvc_ctrl_send_event(handle, ctrl, mapping, xctrls[i].value, + changes); + } +} + +static int uvc_ctrl_add_event(struct v4l2_subscribed_event *sev) +{ + struct uvc_fh *handle = container_of(sev->fh, struct uvc_fh, vfh); + struct uvc_control_mapping *mapping; + struct uvc_control *ctrl; + int ret; + + ret = mutex_lock_interruptible(&handle->chain->ctrl_mutex); + if (ret < 0) + return -ERESTARTSYS; + + ctrl = uvc_find_control(handle->chain, sev->id, &mapping); + if (ctrl == NULL) { + ret = -EINVAL; + goto done; + } + + list_add_tail(&sev->node, &mapping->ev_subs); + if (sev->flags & V4L2_EVENT_SUB_FL_SEND_INITIAL) { + struct v4l2_event ev; + u32 changes = V4L2_EVENT_CTRL_CH_FLAGS; + s32 val = 0; + + if (__uvc_ctrl_get(handle->chain, ctrl, mapping, &val) == 0) + changes |= V4L2_EVENT_CTRL_CH_VALUE; + + uvc_ctrl_fill_event(handle->chain, &ev, ctrl, mapping, val, + changes); + v4l2_event_queue_fh(sev->fh, &ev); + } + +done: + mutex_unlock(&handle->chain->ctrl_mutex); + return ret; +} + +static void uvc_ctrl_del_event(struct v4l2_subscribed_event *sev) +{ + struct uvc_fh *handle = container_of(sev->fh, struct uvc_fh, vfh); + + mutex_lock(&handle->chain->ctrl_mutex); + list_del(&sev->node); + mutex_unlock(&handle->chain->ctrl_mutex); +} + +const struct v4l2_subscribed_event_ops uvc_ctrl_sub_ev_ops = { + .add = uvc_ctrl_add_event, + .del = uvc_ctrl_del_event, + .replace = v4l2_ctrl_replace, + .merge = v4l2_ctrl_merge, +}; /* -------------------------------------------------------------------------- * Control transactions @@ -1131,8 +1378,11 @@ static int uvc_ctrl_commit_entity(struct uvc_device *dev, return 0; } -int __uvc_ctrl_commit(struct uvc_video_chain *chain, int rollback) +int __uvc_ctrl_commit(struct uvc_fh *handle, int rollback, + const struct v4l2_ext_control *xctrls, + unsigned int xctrls_count) { + struct uvc_video_chain *chain = handle->chain; struct uvc_entity *entity; int ret = 0; @@ -1143,6 +1393,8 @@ int __uvc_ctrl_commit(struct uvc_video_chain *chain, int rollback) goto done; } + if (!rollback) + uvc_ctrl_send_events(handle, xctrls, xctrls_count); done: mutex_unlock(&chain->ctrl_mutex); return ret; @@ -1153,39 +1405,12 @@ int uvc_ctrl_get(struct uvc_video_chain *chain, { struct uvc_control *ctrl; struct uvc_control_mapping *mapping; - struct uvc_menu_info *menu; - unsigned int i; - int ret; ctrl = uvc_find_control(chain, xctrl->id, &mapping); - if (ctrl == NULL || (ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR) == 0) + if (ctrl == NULL) return -EINVAL; - if (!ctrl->loaded) { - ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, ctrl->entity->id, - chain->dev->intfnum, ctrl->info.selector, - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), - ctrl->info.size); - if (ret < 0) - return ret; - - ctrl->loaded = 1; - } - - xctrl->value = mapping->get(mapping, UVC_GET_CUR, - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT)); - - if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) { - menu = mapping->menu_info; - for (i = 0; i < mapping->menu_count; ++i, ++menu) { - if (menu->value == xctrl->value) { - xctrl->value = i; - break; - } - } - } - - return 0; + return __uvc_ctrl_get(chain, ctrl, mapping, &xctrl->value); } int uvc_ctrl_set(struct uvc_video_chain *chain, @@ -1641,6 +1866,8 @@ static int __uvc_ctrl_add_mapping(struct uvc_device *dev, if (map == NULL) return -ENOMEM; + INIT_LIST_HEAD(&map->ev_subs); + size = sizeof(*mapping->menu_info) * mapping->menu_count; map->menu_info = kmemdup(mapping->menu_info, size, GFP_KERNEL); if (map->menu_info == NULL) { @@ -1653,7 +1880,6 @@ static int __uvc_ctrl_add_mapping(struct uvc_device *dev, if (map->set == NULL) map->set = uvc_set_le_value; - map->ctrl = &ctrl->info; list_add_tail(&map->list, &ctrl->info.mappings); uvc_trace(UVC_TRACE_CONTROL, "Adding mapping '%s' to control %pUl/%u.\n", diff --git a/drivers/media/video/uvc/uvc_queue.c b/drivers/media/video/uvc/uvc_queue.c index 8f54e24e3f35..9288fbd5001b 100644 --- a/drivers/media/video/uvc/uvc_queue.c +++ b/drivers/media/video/uvc/uvc_queue.c @@ -207,6 +207,19 @@ int uvc_queue_mmap(struct uvc_video_queue *queue, struct vm_area_struct *vma) return ret; } +#ifndef CONFIG_MMU +unsigned long uvc_queue_get_unmapped_area(struct uvc_video_queue *queue, + unsigned long pgoff) +{ + unsigned long ret; + + mutex_lock(&queue->mutex); + ret = vb2_get_unmapped_area(&queue->queue, 0, 0, pgoff, 0); + mutex_unlock(&queue->mutex); + return ret; +} +#endif + unsigned int uvc_queue_poll(struct uvc_video_queue *queue, struct file *file, poll_table *wait) { @@ -237,36 +250,6 @@ int uvc_queue_allocated(struct uvc_video_queue *queue) return allocated; } -#ifndef CONFIG_MMU -/* - * Get unmapped area. - * - * NO-MMU arch need this function to make mmap() work correctly. - */ -unsigned long uvc_queue_get_unmapped_area(struct uvc_video_queue *queue, - unsigned long pgoff) -{ - struct uvc_buffer *buffer; - unsigned int i; - unsigned long ret; - - mutex_lock(&queue->mutex); - for (i = 0; i < queue->count; ++i) { - buffer = &queue->buffer[i]; - if ((buffer->buf.m.offset >> PAGE_SHIFT) == pgoff) - break; - } - if (i == queue->count) { - ret = -EINVAL; - goto done; - } - ret = (unsigned long)buf->mem; -done: - mutex_unlock(&queue->mutex); - return ret; -} -#endif - /* * Enable or disable the video buffers queue. * diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c index ff2cdddf9bc6..759bef8897e9 100644 --- a/drivers/media/video/uvc/uvc_v4l2.c +++ b/drivers/media/video/uvc/uvc_v4l2.c @@ -25,6 +25,8 @@ #include <linux/atomic.h> #include <media/v4l2-common.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-event.h> #include <media/v4l2-ioctl.h> #include "uvcvideo.h" @@ -505,6 +507,8 @@ static int uvc_v4l2_open(struct file *file) } } + v4l2_fh_init(&handle->vfh, stream->vdev); + v4l2_fh_add(&handle->vfh); handle->chain = stream->chain; handle->stream = stream; handle->state = UVC_HANDLE_PASSIVE; @@ -528,6 +532,8 @@ static int uvc_v4l2_release(struct file *file) /* Release the file handle. */ uvc_dismiss_privileges(handle); + v4l2_fh_del(&handle->vfh); + v4l2_fh_exit(&handle->vfh); kfree(handle); file->private_data = NULL; @@ -584,7 +590,7 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) return ret; ret = uvc_ctrl_get(chain, &xctrl); - uvc_ctrl_rollback(chain); + uvc_ctrl_rollback(handle); if (ret >= 0) ctrl->value = xctrl.value; break; @@ -605,10 +611,10 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) ret = uvc_ctrl_set(chain, &xctrl); if (ret < 0) { - uvc_ctrl_rollback(chain); + uvc_ctrl_rollback(handle); return ret; } - ret = uvc_ctrl_commit(chain); + ret = uvc_ctrl_commit(handle, &xctrl, 1); if (ret == 0) ctrl->value = xctrl.value; break; @@ -630,13 +636,13 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) for (i = 0; i < ctrls->count; ++ctrl, ++i) { ret = uvc_ctrl_get(chain, ctrl); if (ret < 0) { - uvc_ctrl_rollback(chain); + uvc_ctrl_rollback(handle); ctrls->error_idx = i; return ret; } } ctrls->error_idx = 0; - ret = uvc_ctrl_rollback(chain); + ret = uvc_ctrl_rollback(handle); break; } @@ -654,7 +660,7 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) for (i = 0; i < ctrls->count; ++ctrl, ++i) { ret = uvc_ctrl_set(chain, ctrl); if (ret < 0) { - uvc_ctrl_rollback(chain); + uvc_ctrl_rollback(handle); ctrls->error_idx = i; return ret; } @@ -663,9 +669,10 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) ctrls->error_idx = 0; if (cmd == VIDIOC_S_EXT_CTRLS) - ret = uvc_ctrl_commit(chain); + ret = uvc_ctrl_commit(handle, + ctrls->controls, ctrls->count); else - ret = uvc_ctrl_rollback(chain); + ret = uvc_ctrl_rollback(handle); break; } @@ -687,7 +694,7 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) break; } pin = iterm->id; - } else if (pin < selector->bNrInPins) { + } else if (index < selector->bNrInPins) { pin = selector->baSourceID[index]; list_for_each_entry(iterm, &chain->entities, chain) { if (!UVC_ENTITY_IS_ITERM(iterm)) @@ -990,6 +997,26 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) return uvc_video_enable(stream, 0); } + case VIDIOC_SUBSCRIBE_EVENT: + { + struct v4l2_event_subscription *sub = arg; + + switch (sub->type) { + case V4L2_EVENT_CTRL: + return v4l2_event_subscribe(&handle->vfh, sub, 0, + &uvc_ctrl_sub_ev_ops); + default: + return -EINVAL; + } + } + + case VIDIOC_UNSUBSCRIBE_EVENT: + return v4l2_event_unsubscribe(&handle->vfh, arg); + + case VIDIOC_DQEVENT: + return v4l2_event_dequeue(&handle->vfh, arg, + file->f_flags & O_NONBLOCK); + /* Analog video standards make no sense for digital cameras. */ case VIDIOC_ENUMSTD: case VIDIOC_QUERYSTD: @@ -1097,7 +1124,8 @@ static int uvc_v4l2_put_xu_mapping(const struct uvc_xu_control_mapping *kp, __put_user(kp->menu_count, &up->menu_count)) return -EFAULT; - __clear_user(up->reserved, sizeof(up->reserved)); + if (__clear_user(up->reserved, sizeof(up->reserved))) + return -EFAULT; if (kp->menu_count == 0) return 0; @@ -1105,8 +1133,6 @@ static int uvc_v4l2_put_xu_mapping(const struct uvc_xu_control_mapping *kp, if (get_user(p, &up->menu_info)) return -EFAULT; umenus = compat_ptr(p); - if (!access_ok(VERIFY_WRITE, umenus, kp->menu_count * sizeof(*umenus))) - return -EFAULT; if (copy_in_user(umenus, kmenus, kp->menu_count * sizeof(*umenus))) return -EFAULT; diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h index 67f88d85bb16..7c3d082505b7 100644 --- a/drivers/media/video/uvc/uvcvideo.h +++ b/drivers/media/video/uvc/uvcvideo.h @@ -13,6 +13,8 @@ #include <linux/videodev2.h> #include <media/media-device.h> #include <media/v4l2-device.h> +#include <media/v4l2-event.h> +#include <media/v4l2-fh.h> #include <media/videobuf2-core.h> /* -------------------------------------------------------------------------- @@ -153,8 +155,7 @@ struct uvc_control_info { struct uvc_control_mapping { struct list_head list; - - struct uvc_control_info *ctrl; + struct list_head ev_subs; __u32 id; __u8 name[32]; @@ -169,6 +170,10 @@ struct uvc_control_mapping { struct uvc_menu_info *menu_info; __u32 menu_count; + __u32 master_id; + __s32 master_manual; + __u32 slave_ids[2]; + __s32 (*get) (struct uvc_control_mapping *mapping, __u8 query, const __u8 *data); void (*set) (struct uvc_control_mapping *mapping, __s32 value, @@ -524,6 +529,7 @@ enum uvc_handle_state { }; struct uvc_fh { + struct v4l2_fh vfh; struct uvc_video_chain *chain; struct uvc_streaming *stream; enum uvc_handle_state state; @@ -643,6 +649,8 @@ extern int uvc_status_suspend(struct uvc_device *dev); extern int uvc_status_resume(struct uvc_device *dev); /* Controls */ +extern const struct v4l2_subscribed_event_ops uvc_ctrl_sub_ev_ops; + extern int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, struct v4l2_queryctrl *v4l2_ctrl); extern int uvc_query_v4l2_menu(struct uvc_video_chain *chain, @@ -655,14 +663,18 @@ extern void uvc_ctrl_cleanup_device(struct uvc_device *dev); extern int uvc_ctrl_resume_device(struct uvc_device *dev); extern int uvc_ctrl_begin(struct uvc_video_chain *chain); -extern int __uvc_ctrl_commit(struct uvc_video_chain *chain, int rollback); -static inline int uvc_ctrl_commit(struct uvc_video_chain *chain) +extern int __uvc_ctrl_commit(struct uvc_fh *handle, int rollback, + const struct v4l2_ext_control *xctrls, + unsigned int xctrls_count); +static inline int uvc_ctrl_commit(struct uvc_fh *handle, + const struct v4l2_ext_control *xctrls, + unsigned int xctrls_count) { - return __uvc_ctrl_commit(chain, 0); + return __uvc_ctrl_commit(handle, 0, xctrls, xctrls_count); } -static inline int uvc_ctrl_rollback(struct uvc_video_chain *chain) +static inline int uvc_ctrl_rollback(struct uvc_fh *handle) { - return __uvc_ctrl_commit(chain, 1); + return __uvc_ctrl_commit(handle, 1, NULL, 0); } extern int uvc_ctrl_get(struct uvc_video_chain *chain, diff --git a/drivers/media/video/v4l2-compat-ioctl32.c b/drivers/media/video/v4l2-compat-ioctl32.c index 2829d256e4b7..89ae433877e6 100644 --- a/drivers/media/video/v4l2-compat-ioctl32.c +++ b/drivers/media/video/v4l2-compat-ioctl32.c @@ -37,7 +37,7 @@ struct v4l2_clip32 { struct v4l2_window32 { struct v4l2_rect w; - enum v4l2_field field; + __u32 field; /* enum v4l2_field */ __u32 chromakey; compat_caddr_t clips; /* actually struct v4l2_clip32 * */ __u32 clipcount; @@ -147,7 +147,7 @@ static inline int put_v4l2_sliced_vbi_format(struct v4l2_sliced_vbi_format *kp, } struct v4l2_format32 { - enum v4l2_buf_type type; + __u32 type; /* enum v4l2_buf_type */ union { struct v4l2_pix_format pix; struct v4l2_pix_format_mplane pix_mp; @@ -170,7 +170,7 @@ struct v4l2_format32 { struct v4l2_create_buffers32 { __u32 index; __u32 count; - enum v4l2_memory memory; + __u32 memory; /* enum v4l2_memory */ struct v4l2_format32 format; __u32 reserved[8]; }; @@ -311,16 +311,16 @@ struct v4l2_plane32 { struct v4l2_buffer32 { __u32 index; - enum v4l2_buf_type type; + __u32 type; /* enum v4l2_buf_type */ __u32 bytesused; __u32 flags; - enum v4l2_field field; + __u32 field; /* enum v4l2_field */ struct compat_timeval timestamp; struct v4l2_timecode timecode; __u32 sequence; /* memory location */ - enum v4l2_memory memory; + __u32 memory; /* enum v4l2_memory */ union { __u32 offset; compat_long_t userptr; diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c index 18015c0a8d31..a5fbace4c059 100644 --- a/drivers/media/video/v4l2-ctrls.c +++ b/drivers/media/video/v4l2-ctrls.c @@ -230,6 +230,19 @@ const char * const *v4l2_ctrl_get_menu(u32 id) "Aperture Priority Mode", NULL }; + static const char * const camera_exposure_metering[] = { + "Average", + "Center Weighted", + "Spot", + NULL + }; + static const char * const camera_auto_focus_range[] = { + "Auto", + "Normal", + "Macro", + "Infinity", + NULL + }; static const char * const colorfx[] = { "None", "Black & White", @@ -241,6 +254,47 @@ const char * const *v4l2_ctrl_get_menu(u32 id) "Grass Green", "Skin Whiten", "Vivid", + "Aqua", + "Art Freeze", + "Silhouette", + "Solarization", + "Antique", + "Set Cb/Cr", + NULL + }; + static const char * const auto_n_preset_white_balance[] = { + "Manual", + "Auto", + "Incandescent", + "Fluorescent", + "Fluorescent H", + "Horizon", + "Daylight", + "Flash", + "Cloudy", + "Shade", + NULL, + }; + static const char * const camera_iso_sensitivity_auto[] = { + "Manual", + "Auto", + NULL + }; + static const char * const scene_mode[] = { + "None", + "Backlight", + "Beach/Snow", + "Candle Light", + "Dusk/Dawn", + "Fall Colors", + "Fireworks", + "Landscape", + "Night", + "Party/Indoor", + "Portrait", + "Sports", + "Sunset", + "Text", NULL }; static const char * const tune_preemphasis[] = { @@ -410,8 +464,18 @@ const char * const *v4l2_ctrl_get_menu(u32 id) return camera_power_line_frequency; case V4L2_CID_EXPOSURE_AUTO: return camera_exposure_auto; + case V4L2_CID_EXPOSURE_METERING: + return camera_exposure_metering; + case V4L2_CID_AUTO_FOCUS_RANGE: + return camera_auto_focus_range; case V4L2_CID_COLORFX: return colorfx; + case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE: + return auto_n_preset_white_balance; + case V4L2_CID_ISO_SENSITIVITY_AUTO: + return camera_iso_sensitivity_auto; + case V4L2_CID_SCENE_MODE: + return scene_mode; case V4L2_CID_TUNE_PREEMPHASIS: return tune_preemphasis; case V4L2_CID_FLASH_LED_MODE: @@ -493,6 +557,7 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE: return "Min Number of Capture Buffers"; case V4L2_CID_MIN_BUFFERS_FOR_OUTPUT: return "Min Number of Output Buffers"; case V4L2_CID_ALPHA_COMPONENT: return "Alpha Component"; + case V4L2_CID_COLORFX_CBCR: return "Color Effects, CbCr"; /* MPEG controls */ /* Keep the order of the 'case's the same as in videodev2.h! */ @@ -590,13 +655,26 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_TILT_ABSOLUTE: return "Tilt, Absolute"; case V4L2_CID_FOCUS_ABSOLUTE: return "Focus, Absolute"; case V4L2_CID_FOCUS_RELATIVE: return "Focus, Relative"; - case V4L2_CID_FOCUS_AUTO: return "Focus, Automatic"; + case V4L2_CID_FOCUS_AUTO: return "Focus, Automatic Continuous"; case V4L2_CID_ZOOM_ABSOLUTE: return "Zoom, Absolute"; case V4L2_CID_ZOOM_RELATIVE: return "Zoom, Relative"; case V4L2_CID_ZOOM_CONTINUOUS: return "Zoom, Continuous"; case V4L2_CID_PRIVACY: return "Privacy"; case V4L2_CID_IRIS_ABSOLUTE: return "Iris, Absolute"; case V4L2_CID_IRIS_RELATIVE: return "Iris, Relative"; + case V4L2_CID_AUTO_EXPOSURE_BIAS: return "Auto Exposure, Bias"; + case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE: return "White Balance, Auto & Preset"; + case V4L2_CID_WIDE_DYNAMIC_RANGE: return "Wide Dynamic Range"; + case V4L2_CID_IMAGE_STABILIZATION: return "Image Stabilization"; + case V4L2_CID_ISO_SENSITIVITY: return "ISO Sensitivity"; + case V4L2_CID_ISO_SENSITIVITY_AUTO: return "ISO Sensitivity, Auto"; + case V4L2_CID_EXPOSURE_METERING: return "Exposure, Metering Mode"; + case V4L2_CID_SCENE_MODE: return "Scene Mode"; + case V4L2_CID_3A_LOCK: return "3A Lock"; + case V4L2_CID_AUTO_FOCUS_START: return "Auto Focus, Start"; + case V4L2_CID_AUTO_FOCUS_STOP: return "Auto Focus, Stop"; + case V4L2_CID_AUTO_FOCUS_STATUS: return "Auto Focus, Status"; + case V4L2_CID_AUTO_FOCUS_RANGE: return "Auto Focus, Range"; /* FM Radio Modulator control */ /* Keep the order of the 'case's the same as in videodev2.h! */ @@ -644,6 +722,17 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_JPEG_COMPRESSION_QUALITY: return "Compression Quality"; case V4L2_CID_JPEG_ACTIVE_MARKER: return "Active Markers"; + /* Image source controls */ + case V4L2_CID_IMAGE_SOURCE_CLASS: return "Image Source Controls"; + case V4L2_CID_VBLANK: return "Vertical Blanking"; + case V4L2_CID_HBLANK: return "Horizontal Blanking"; + case V4L2_CID_ANALOGUE_GAIN: return "Analogue Gain"; + + /* Image processing controls */ + case V4L2_CID_IMAGE_PROC_CLASS: return "Image Processing Controls"; + case V4L2_CID_LINK_FREQ: return "Link Frequency"; + case V4L2_CID_PIXEL_RATE: return "Pixel Rate"; + default: return NULL; } @@ -688,6 +777,8 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM: case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE: case V4L2_CID_MPEG_VIDEO_MPEG4_QPEL: + case V4L2_CID_WIDE_DYNAMIC_RANGE: + case V4L2_CID_IMAGE_STABILIZATION: *type = V4L2_CTRL_TYPE_BOOLEAN; *min = 0; *max = *step = 1; @@ -696,6 +787,8 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_TILT_RESET: case V4L2_CID_FLASH_STROBE: case V4L2_CID_FLASH_STROBE_STOP: + case V4L2_CID_AUTO_FOCUS_START: + case V4L2_CID_AUTO_FOCUS_STOP: *type = V4L2_CTRL_TYPE_BUTTON; *flags |= V4L2_CTRL_FLAG_WRITE_ONLY; *min = *max = *step = *def = 0; @@ -719,7 +812,9 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_MPEG_STREAM_TYPE: case V4L2_CID_MPEG_STREAM_VBI_FMT: case V4L2_CID_EXPOSURE_AUTO: + case V4L2_CID_AUTO_FOCUS_RANGE: case V4L2_CID_COLORFX: + case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE: case V4L2_CID_TUNE_PREEMPHASIS: case V4L2_CID_FLASH_LED_MODE: case V4L2_CID_FLASH_STROBE_SOURCE: @@ -733,18 +828,30 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL: case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE: case V4L2_CID_JPEG_CHROMA_SUBSAMPLING: + case V4L2_CID_ISO_SENSITIVITY_AUTO: + case V4L2_CID_EXPOSURE_METERING: + case V4L2_CID_SCENE_MODE: *type = V4L2_CTRL_TYPE_MENU; break; + case V4L2_CID_LINK_FREQ: + *type = V4L2_CTRL_TYPE_INTEGER_MENU; + break; case V4L2_CID_RDS_TX_PS_NAME: case V4L2_CID_RDS_TX_RADIO_TEXT: *type = V4L2_CTRL_TYPE_STRING; break; + case V4L2_CID_ISO_SENSITIVITY: + case V4L2_CID_AUTO_EXPOSURE_BIAS: + *type = V4L2_CTRL_TYPE_INTEGER_MENU; + break; case V4L2_CID_USER_CLASS: case V4L2_CID_CAMERA_CLASS: case V4L2_CID_MPEG_CLASS: case V4L2_CID_FM_TX_CLASS: case V4L2_CID_FLASH_CLASS: case V4L2_CID_JPEG_CLASS: + case V4L2_CID_IMAGE_SOURCE_CLASS: + case V4L2_CID_IMAGE_PROC_CLASS: *type = V4L2_CTRL_TYPE_CTRL_CLASS; /* You can neither read not write these */ *flags |= V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_WRITE_ONLY; @@ -759,6 +866,8 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, break; case V4L2_CID_FLASH_FAULT: case V4L2_CID_JPEG_ACTIVE_MARKER: + case V4L2_CID_3A_LOCK: + case V4L2_CID_AUTO_FOCUS_STATUS: *type = V4L2_CTRL_TYPE_BITMASK; break; case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE: @@ -768,8 +877,12 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, break; case V4L2_CID_MPEG_VIDEO_DEC_FRAME: case V4L2_CID_MPEG_VIDEO_DEC_PTS: + *flags |= V4L2_CTRL_FLAG_VOLATILE; + /* Fall through */ + case V4L2_CID_PIXEL_RATE: *type = V4L2_CTRL_TYPE_INTEGER64; - *flags |= V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_VOLATILE; + *flags |= V4L2_CTRL_FLAG_READ_ONLY; + *min = *max = *step = *def = 0; break; default: *type = V4L2_CTRL_TYPE_INTEGER; @@ -817,6 +930,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, *flags |= V4L2_CTRL_FLAG_WRITE_ONLY; break; case V4L2_CID_FLASH_STROBE_STATUS: + case V4L2_CID_AUTO_FOCUS_STATUS: case V4L2_CID_FLASH_READY: *flags |= V4L2_CTRL_FLAG_READ_ONLY; break; @@ -852,7 +966,8 @@ static void fill_event(struct v4l2_event *ev, struct v4l2_ctrl *ctrl, u32 change ev->u.ctrl.value64 = ctrl->cur.val64; ev->u.ctrl.minimum = ctrl->minimum; ev->u.ctrl.maximum = ctrl->maximum; - if (ctrl->type == V4L2_CTRL_TYPE_MENU) + if (ctrl->type == V4L2_CTRL_TYPE_MENU + || ctrl->type == V4L2_CTRL_TYPE_INTEGER_MENU) ev->u.ctrl.step = 1; else ev->u.ctrl.step = ctrl->step; @@ -1083,10 +1198,13 @@ static int validate_new_int(const struct v4l2_ctrl *ctrl, s32 *pval) return 0; case V4L2_CTRL_TYPE_MENU: + case V4L2_CTRL_TYPE_INTEGER_MENU: if (val < ctrl->minimum || val > ctrl->maximum) return -ERANGE; - if (ctrl->qmenu[val][0] == '\0' || - (ctrl->menu_skip_mask & (1 << val))) + if (ctrl->menu_skip_mask & (1 << val)) + return -EINVAL; + if (ctrl->type == V4L2_CTRL_TYPE_MENU && + ctrl->qmenu[val][0] == '\0') return -EINVAL; return 0; @@ -1114,6 +1232,7 @@ static int validate_new(const struct v4l2_ctrl *ctrl, struct v4l2_ext_control *c case V4L2_CTRL_TYPE_INTEGER: case V4L2_CTRL_TYPE_BOOLEAN: case V4L2_CTRL_TYPE_MENU: + case V4L2_CTRL_TYPE_INTEGER_MENU: case V4L2_CTRL_TYPE_BITMASK: case V4L2_CTRL_TYPE_BUTTON: case V4L2_CTRL_TYPE_CTRL_CLASS: @@ -1152,7 +1271,8 @@ static inline int handler_set_err(struct v4l2_ctrl_handler *hdl, int err) int v4l2_ctrl_handler_init(struct v4l2_ctrl_handler *hdl, unsigned nr_of_controls_hint) { - mutex_init(&hdl->lock); + hdl->lock = &hdl->_lock; + mutex_init(hdl->lock); INIT_LIST_HEAD(&hdl->ctrls); INIT_LIST_HEAD(&hdl->ctrl_refs); hdl->nr_of_buckets = 1 + nr_of_controls_hint / 8; @@ -1173,7 +1293,7 @@ void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl) if (hdl == NULL || hdl->buckets == NULL) return; - mutex_lock(&hdl->lock); + mutex_lock(hdl->lock); /* Free all nodes */ list_for_each_entry_safe(ref, next_ref, &hdl->ctrl_refs, node) { list_del(&ref->node); @@ -1190,7 +1310,7 @@ void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl) hdl->buckets = NULL; hdl->cached = NULL; hdl->error = 0; - mutex_unlock(&hdl->lock); + mutex_unlock(hdl->lock); } EXPORT_SYMBOL(v4l2_ctrl_handler_free); @@ -1255,9 +1375,9 @@ static struct v4l2_ctrl_ref *find_ref_lock( struct v4l2_ctrl_ref *ref = NULL; if (hdl) { - mutex_lock(&hdl->lock); + mutex_lock(hdl->lock); ref = find_ref(hdl, id); - mutex_unlock(&hdl->lock); + mutex_unlock(hdl->lock); } return ref; } @@ -1304,7 +1424,7 @@ static int handler_new_ref(struct v4l2_ctrl_handler *hdl, INIT_LIST_HEAD(&new_ref->node); - mutex_lock(&hdl->lock); + mutex_lock(hdl->lock); /* Add immediately at the end of the list if the list is empty, or if the last element in the list has a lower ID. @@ -1334,7 +1454,7 @@ insert_in_hash: hdl->buckets[bucket] = new_ref; unlock: - mutex_unlock(&hdl->lock); + mutex_unlock(hdl->lock); return 0; } @@ -1343,7 +1463,8 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_ops *ops, u32 id, const char *name, enum v4l2_ctrl_type type, s32 min, s32 max, u32 step, s32 def, - u32 flags, const char * const *qmenu, void *priv) + u32 flags, const char * const *qmenu, + const s64 *qmenu_int, void *priv) { struct v4l2_ctrl *ctrl; unsigned sz_extra = 0; @@ -1356,6 +1477,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, (type == V4L2_CTRL_TYPE_INTEGER && step == 0) || (type == V4L2_CTRL_TYPE_BITMASK && max == 0) || (type == V4L2_CTRL_TYPE_MENU && qmenu == NULL) || + (type == V4L2_CTRL_TYPE_INTEGER_MENU && qmenu_int == NULL) || (type == V4L2_CTRL_TYPE_STRING && max == 0)) { handler_set_err(hdl, -ERANGE); return NULL; @@ -1366,6 +1488,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, } if ((type == V4L2_CTRL_TYPE_INTEGER || type == V4L2_CTRL_TYPE_MENU || + type == V4L2_CTRL_TYPE_INTEGER_MENU || type == V4L2_CTRL_TYPE_BOOLEAN) && (def < min || def > max)) { handler_set_err(hdl, -ERANGE); @@ -1400,7 +1523,10 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, ctrl->minimum = min; ctrl->maximum = max; ctrl->step = step; - ctrl->qmenu = qmenu; + if (type == V4L2_CTRL_TYPE_MENU) + ctrl->qmenu = qmenu; + else if (type == V4L2_CTRL_TYPE_INTEGER_MENU) + ctrl->qmenu_int = qmenu_int; ctrl->priv = priv; ctrl->cur.val = ctrl->val = ctrl->default_value = def; @@ -1414,9 +1540,9 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, kfree(ctrl); return NULL; } - mutex_lock(&hdl->lock); + mutex_lock(hdl->lock); list_add_tail(&ctrl->node, &hdl->ctrls); - mutex_unlock(&hdl->lock); + mutex_unlock(hdl->lock); return ctrl; } @@ -1427,6 +1553,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl, struct v4l2_ctrl *ctrl; const char *name = cfg->name; const char * const *qmenu = cfg->qmenu; + const s64 *qmenu_int = cfg->qmenu_int; enum v4l2_ctrl_type type = cfg->type; u32 flags = cfg->flags; s32 min = cfg->min; @@ -1438,18 +1565,24 @@ struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl, v4l2_ctrl_fill(cfg->id, &name, &type, &min, &max, &step, &def, &flags); - is_menu = (cfg->type == V4L2_CTRL_TYPE_MENU); + is_menu = (cfg->type == V4L2_CTRL_TYPE_MENU || + cfg->type == V4L2_CTRL_TYPE_INTEGER_MENU); if (is_menu) WARN_ON(step); else WARN_ON(cfg->menu_skip_mask); - if (is_menu && qmenu == NULL) + if (cfg->type == V4L2_CTRL_TYPE_MENU && qmenu == NULL) qmenu = v4l2_ctrl_get_menu(cfg->id); + else if (cfg->type == V4L2_CTRL_TYPE_INTEGER_MENU && + qmenu_int == NULL) { + handler_set_err(hdl, -EINVAL); + return NULL; + } ctrl = v4l2_ctrl_new(hdl, cfg->ops, cfg->id, name, type, min, max, is_menu ? cfg->menu_skip_mask : step, - def, flags, qmenu, priv); + def, flags, qmenu, qmenu_int, priv); if (ctrl) ctrl->is_private = cfg->is_private; return ctrl; @@ -1466,12 +1599,13 @@ struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl, u32 flags; v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags); - if (type == V4L2_CTRL_TYPE_MENU) { + if (type == V4L2_CTRL_TYPE_MENU + || type == V4L2_CTRL_TYPE_INTEGER_MENU) { handler_set_err(hdl, -EINVAL); return NULL; } return v4l2_ctrl_new(hdl, ops, id, name, type, - min, max, step, def, flags, NULL, NULL); + min, max, step, def, flags, NULL, NULL, NULL); } EXPORT_SYMBOL(v4l2_ctrl_new_std); @@ -1493,10 +1627,31 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl, return NULL; } return v4l2_ctrl_new(hdl, ops, id, name, type, - 0, max, mask, def, flags, qmenu, NULL); + 0, max, mask, def, flags, qmenu, NULL, NULL); } EXPORT_SYMBOL(v4l2_ctrl_new_std_menu); +/* Helper function for standard integer menu controls */ +struct v4l2_ctrl *v4l2_ctrl_new_int_menu(struct v4l2_ctrl_handler *hdl, + const struct v4l2_ctrl_ops *ops, + u32 id, s32 max, s32 def, const s64 *qmenu_int) +{ + const char *name; + enum v4l2_ctrl_type type; + s32 min; + s32 step; + u32 flags; + + v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags); + if (type != V4L2_CTRL_TYPE_INTEGER_MENU) { + handler_set_err(hdl, -EINVAL); + return NULL; + } + return v4l2_ctrl_new(hdl, ops, id, name, type, + 0, max, 0, def, flags, NULL, qmenu_int, NULL); +} +EXPORT_SYMBOL(v4l2_ctrl_new_int_menu); + /* Add a control from another handler to this handler */ struct v4l2_ctrl *v4l2_ctrl_add_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_ctrl *ctrl) @@ -1525,7 +1680,7 @@ int v4l2_ctrl_add_handler(struct v4l2_ctrl_handler *hdl, return 0; if (hdl->error) return hdl->error; - mutex_lock(&add->lock); + mutex_lock(add->lock); list_for_each_entry(ref, &add->ctrl_refs, node) { struct v4l2_ctrl *ctrl = ref->ctrl; @@ -1539,7 +1694,7 @@ int v4l2_ctrl_add_handler(struct v4l2_ctrl_handler *hdl, if (ret) break; } - mutex_unlock(&add->lock); + mutex_unlock(add->lock); return ret; } EXPORT_SYMBOL(v4l2_ctrl_add_handler); @@ -1659,6 +1814,9 @@ static void log_ctrl(const struct v4l2_ctrl *ctrl, case V4L2_CTRL_TYPE_MENU: printk(KERN_CONT "%s", ctrl->qmenu[ctrl->cur.val]); break; + case V4L2_CTRL_TYPE_INTEGER_MENU: + printk(KERN_CONT "%lld", ctrl->qmenu_int[ctrl->cur.val]); + break; case V4L2_CTRL_TYPE_BITMASK: printk(KERN_CONT "0x%08x", ctrl->cur.val); break; @@ -1700,11 +1858,11 @@ void v4l2_ctrl_handler_log_status(struct v4l2_ctrl_handler *hdl, len = strlen(prefix); if (len && prefix[len - 1] != ' ') colon = ": "; - mutex_lock(&hdl->lock); + mutex_lock(hdl->lock); list_for_each_entry(ctrl, &hdl->ctrls, node) if (!(ctrl->flags & V4L2_CTRL_FLAG_DISABLED)) log_ctrl(ctrl, prefix, colon); - mutex_unlock(&hdl->lock); + mutex_unlock(hdl->lock); } EXPORT_SYMBOL(v4l2_ctrl_handler_log_status); @@ -1716,7 +1874,7 @@ int v4l2_ctrl_handler_setup(struct v4l2_ctrl_handler *hdl) if (hdl == NULL) return 0; - mutex_lock(&hdl->lock); + mutex_lock(hdl->lock); list_for_each_entry(ctrl, &hdl->ctrls, node) ctrl->done = false; @@ -1741,7 +1899,7 @@ int v4l2_ctrl_handler_setup(struct v4l2_ctrl_handler *hdl) if (ret) break; } - mutex_unlock(&hdl->lock); + mutex_unlock(hdl->lock); return ret; } EXPORT_SYMBOL(v4l2_ctrl_handler_setup); @@ -1756,7 +1914,7 @@ int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc) if (hdl == NULL) return -EINVAL; - mutex_lock(&hdl->lock); + mutex_lock(hdl->lock); /* Try to find it */ ref = find_ref(hdl, id); @@ -1781,7 +1939,7 @@ int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc) break; } } - mutex_unlock(&hdl->lock); + mutex_unlock(hdl->lock); if (!ref) return -EINVAL; @@ -1795,7 +1953,8 @@ int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc) qc->minimum = ctrl->minimum; qc->maximum = ctrl->maximum; qc->default_value = ctrl->default_value; - if (ctrl->type == V4L2_CTRL_TYPE_MENU) + if (ctrl->type == V4L2_CTRL_TYPE_MENU + || ctrl->type == V4L2_CTRL_TYPE_INTEGER_MENU) qc->step = 1; else qc->step = ctrl->step; @@ -1825,16 +1984,33 @@ int v4l2_querymenu(struct v4l2_ctrl_handler *hdl, struct v4l2_querymenu *qm) qm->reserved = 0; /* Sanity checks */ - if (ctrl->qmenu == NULL || - i < ctrl->minimum || i > ctrl->maximum) + switch (ctrl->type) { + case V4L2_CTRL_TYPE_MENU: + if (ctrl->qmenu == NULL) + return -EINVAL; + break; + case V4L2_CTRL_TYPE_INTEGER_MENU: + if (ctrl->qmenu_int == NULL) + return -EINVAL; + break; + default: return -EINVAL; + } + + if (i < ctrl->minimum || i > ctrl->maximum) + return -EINVAL; + /* Use mask to see if this menu item should be skipped */ if (ctrl->menu_skip_mask & (1 << i)) return -EINVAL; /* Empty menu items should also be skipped */ - if (ctrl->qmenu[i] == NULL || ctrl->qmenu[i][0] == '\0') - return -EINVAL; - strlcpy(qm->name, ctrl->qmenu[i], sizeof(qm->name)); + if (ctrl->type == V4L2_CTRL_TYPE_MENU) { + if (ctrl->qmenu[i] == NULL || ctrl->qmenu[i][0] == '\0') + return -EINVAL; + strlcpy(qm->name, ctrl->qmenu[i], sizeof(qm->name)); + } else { + qm->value = ctrl->qmenu_int[i]; + } return 0; } EXPORT_SYMBOL(v4l2_querymenu); @@ -1940,7 +2116,7 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl, belong to the same cluster. */ /* This has to be done with the handler lock taken. */ - mutex_lock(&hdl->lock); + mutex_lock(hdl->lock); /* First zero the helper field in the master control references */ for (i = 0; i < cs->count; i++) @@ -1962,7 +2138,7 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl, /* Point the mref helper to the current helper struct. */ mref->helper = h; } - mutex_unlock(&hdl->lock); + mutex_unlock(hdl->lock); return 0; } @@ -1996,7 +2172,8 @@ int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs return class_check(hdl, cs->ctrl_class); if (cs->count > ARRAY_SIZE(helper)) { - helpers = kmalloc(sizeof(helper[0]) * cs->count, GFP_KERNEL); + helpers = kmalloc_array(cs->count, sizeof(helper[0]), + GFP_KERNEL); if (helpers == NULL) return -ENOMEM; } @@ -2218,7 +2395,8 @@ static int try_set_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl, return class_check(hdl, cs->ctrl_class); if (cs->count > ARRAY_SIZE(helper)) { - helpers = kmalloc(sizeof(helper[0]) * cs->count, GFP_KERNEL); + helpers = kmalloc_array(cs->count, sizeof(helper[0]), + GFP_KERNEL); if (!helpers) return -ENOMEM; } @@ -2381,9 +2559,13 @@ int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val) } EXPORT_SYMBOL(v4l2_ctrl_s_ctrl); -void v4l2_ctrl_add_event(struct v4l2_ctrl *ctrl, - struct v4l2_subscribed_event *sev) +static int v4l2_ctrl_add_event(struct v4l2_subscribed_event *sev) { + struct v4l2_ctrl *ctrl = v4l2_ctrl_find(sev->fh->ctrl_handler, sev->id); + + if (ctrl == NULL) + return -EINVAL; + v4l2_ctrl_lock(ctrl); list_add_tail(&sev->node, &ctrl->ev_subs); if (ctrl->type != V4L2_CTRL_TYPE_CTRL_CLASS && @@ -2397,17 +2579,40 @@ void v4l2_ctrl_add_event(struct v4l2_ctrl *ctrl, v4l2_event_queue_fh(sev->fh, &ev); } v4l2_ctrl_unlock(ctrl); + return 0; } -EXPORT_SYMBOL(v4l2_ctrl_add_event); -void v4l2_ctrl_del_event(struct v4l2_ctrl *ctrl, - struct v4l2_subscribed_event *sev) +static void v4l2_ctrl_del_event(struct v4l2_subscribed_event *sev) { + struct v4l2_ctrl *ctrl = v4l2_ctrl_find(sev->fh->ctrl_handler, sev->id); + v4l2_ctrl_lock(ctrl); list_del(&sev->node); v4l2_ctrl_unlock(ctrl); } -EXPORT_SYMBOL(v4l2_ctrl_del_event); + +void v4l2_ctrl_replace(struct v4l2_event *old, const struct v4l2_event *new) +{ + u32 old_changes = old->u.ctrl.changes; + + old->u.ctrl = new->u.ctrl; + old->u.ctrl.changes |= old_changes; +} +EXPORT_SYMBOL(v4l2_ctrl_replace); + +void v4l2_ctrl_merge(const struct v4l2_event *old, struct v4l2_event *new) +{ + new->u.ctrl.changes |= old->u.ctrl.changes; +} +EXPORT_SYMBOL(v4l2_ctrl_merge); + +const struct v4l2_subscribed_event_ops v4l2_ctrl_sub_ev_ops = { + .add = v4l2_ctrl_add_event, + .del = v4l2_ctrl_del_event, + .replace = v4l2_ctrl_replace, + .merge = v4l2_ctrl_merge, +}; +EXPORT_SYMBOL(v4l2_ctrl_sub_ev_ops); int v4l2_ctrl_log_status(struct file *file, void *fh) { @@ -2425,7 +2630,7 @@ int v4l2_ctrl_subscribe_event(struct v4l2_fh *fh, struct v4l2_event_subscription *sub) { if (sub->type == V4L2_EVENT_CTRL) - return v4l2_event_subscribe(fh, sub, 0); + return v4l2_event_subscribe(fh, sub, 0, &v4l2_ctrl_sub_ev_ops); return -EINVAL; } EXPORT_SYMBOL(v4l2_ctrl_subscribe_event); diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c index 70bec548d904..5ccbd4629f9c 100644 --- a/drivers/media/video/v4l2-dev.c +++ b/drivers/media/video/v4l2-dev.c @@ -274,11 +274,12 @@ static ssize_t v4l2_read(struct file *filp, char __user *buf, if (!vdev->fops->read) return -EINVAL; - if (vdev->lock && mutex_lock_interruptible(vdev->lock)) + if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags) && + mutex_lock_interruptible(vdev->lock)) return -ERESTARTSYS; if (video_is_registered(vdev)) ret = vdev->fops->read(filp, buf, sz, off); - if (vdev->lock) + if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags)) mutex_unlock(vdev->lock); return ret; } @@ -291,11 +292,12 @@ static ssize_t v4l2_write(struct file *filp, const char __user *buf, if (!vdev->fops->write) return -EINVAL; - if (vdev->lock && mutex_lock_interruptible(vdev->lock)) + if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags) && + mutex_lock_interruptible(vdev->lock)) return -ERESTARTSYS; if (video_is_registered(vdev)) ret = vdev->fops->write(filp, buf, sz, off); - if (vdev->lock) + if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags)) mutex_unlock(vdev->lock); return ret; } @@ -307,11 +309,11 @@ static unsigned int v4l2_poll(struct file *filp, struct poll_table_struct *poll) if (!vdev->fops->poll) return DEFAULT_POLLMASK; - if (vdev->lock) + if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags)) mutex_lock(vdev->lock); if (video_is_registered(vdev)) ret = vdev->fops->poll(filp, poll); - if (vdev->lock) + if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags)) mutex_unlock(vdev->lock); return ret; } @@ -322,11 +324,19 @@ static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) int ret = -ENODEV; if (vdev->fops->unlocked_ioctl) { - if (vdev->lock && mutex_lock_interruptible(vdev->lock)) - return -ERESTARTSYS; + bool locked = false; + + if (vdev->lock) { + /* always lock unless the cmd is marked as "don't use lock" */ + locked = !v4l2_is_known_ioctl(cmd) || + !test_bit(_IOC_NR(cmd), vdev->disable_locking); + + if (locked && mutex_lock_interruptible(vdev->lock)) + return -ERESTARTSYS; + } if (video_is_registered(vdev)) ret = vdev->fops->unlocked_ioctl(filp, cmd, arg); - if (vdev->lock) + if (locked) mutex_unlock(vdev->lock); } else if (vdev->fops->ioctl) { /* This code path is a replacement for the BKL. It is a major @@ -391,11 +401,12 @@ static int v4l2_mmap(struct file *filp, struct vm_area_struct *vm) if (!vdev->fops->mmap) return ret; - if (vdev->lock && mutex_lock_interruptible(vdev->lock)) + if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags) && + mutex_lock_interruptible(vdev->lock)) return -ERESTARTSYS; if (video_is_registered(vdev)) ret = vdev->fops->mmap(filp, vm); - if (vdev->lock) + if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags)) mutex_unlock(vdev->lock); return ret; } @@ -418,7 +429,8 @@ static int v4l2_open(struct inode *inode, struct file *filp) video_get(vdev); mutex_unlock(&videodev_lock); if (vdev->fops->open) { - if (vdev->lock && mutex_lock_interruptible(vdev->lock)) { + if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags) && + mutex_lock_interruptible(vdev->lock)) { ret = -ERESTARTSYS; goto err; } @@ -426,7 +438,7 @@ static int v4l2_open(struct inode *inode, struct file *filp) ret = vdev->fops->open(filp); else ret = -ENODEV; - if (vdev->lock) + if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags)) mutex_unlock(vdev->lock); } @@ -444,10 +456,10 @@ static int v4l2_release(struct inode *inode, struct file *filp) int ret = 0; if (vdev->fops->release) { - if (vdev->lock) + if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags)) mutex_lock(vdev->lock); vdev->fops->release(filp); - if (vdev->lock) + if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags)) mutex_unlock(vdev->lock); } /* decrease the refcount unconditionally since the release() @@ -508,6 +520,175 @@ static int get_index(struct video_device *vdev) return find_first_zero_bit(used, VIDEO_NUM_DEVICES); } +#define SET_VALID_IOCTL(ops, cmd, op) \ + if (ops->op) \ + set_bit(_IOC_NR(cmd), valid_ioctls) + +/* This determines which ioctls are actually implemented in the driver. + It's a one-time thing which simplifies video_ioctl2 as it can just do + a bit test. + + Note that drivers can override this by setting bits to 1 in + vdev->valid_ioctls. If an ioctl is marked as 1 when this function is + called, then that ioctl will actually be marked as unimplemented. + + It does that by first setting up the local valid_ioctls bitmap, and + at the end do a: + + vdev->valid_ioctls = valid_ioctls & ~(vdev->valid_ioctls) + */ +static void determine_valid_ioctls(struct video_device *vdev) +{ + DECLARE_BITMAP(valid_ioctls, BASE_VIDIOC_PRIVATE); + const struct v4l2_ioctl_ops *ops = vdev->ioctl_ops; + + bitmap_zero(valid_ioctls, BASE_VIDIOC_PRIVATE); + + SET_VALID_IOCTL(ops, VIDIOC_QUERYCAP, vidioc_querycap); + if (ops->vidioc_g_priority || + test_bit(V4L2_FL_USE_FH_PRIO, &vdev->flags)) + set_bit(_IOC_NR(VIDIOC_G_PRIORITY), valid_ioctls); + if (ops->vidioc_s_priority || + test_bit(V4L2_FL_USE_FH_PRIO, &vdev->flags)) + set_bit(_IOC_NR(VIDIOC_S_PRIORITY), valid_ioctls); + if (ops->vidioc_enum_fmt_vid_cap || + ops->vidioc_enum_fmt_vid_out || + ops->vidioc_enum_fmt_vid_cap_mplane || + ops->vidioc_enum_fmt_vid_out_mplane || + ops->vidioc_enum_fmt_vid_overlay || + ops->vidioc_enum_fmt_type_private) + set_bit(_IOC_NR(VIDIOC_ENUM_FMT), valid_ioctls); + if (ops->vidioc_g_fmt_vid_cap || + ops->vidioc_g_fmt_vid_out || + ops->vidioc_g_fmt_vid_cap_mplane || + ops->vidioc_g_fmt_vid_out_mplane || + ops->vidioc_g_fmt_vid_overlay || + ops->vidioc_g_fmt_vbi_cap || + ops->vidioc_g_fmt_vid_out_overlay || + ops->vidioc_g_fmt_vbi_out || + ops->vidioc_g_fmt_sliced_vbi_cap || + ops->vidioc_g_fmt_sliced_vbi_out || + ops->vidioc_g_fmt_type_private) + set_bit(_IOC_NR(VIDIOC_G_FMT), valid_ioctls); + if (ops->vidioc_s_fmt_vid_cap || + ops->vidioc_s_fmt_vid_out || + ops->vidioc_s_fmt_vid_cap_mplane || + ops->vidioc_s_fmt_vid_out_mplane || + ops->vidioc_s_fmt_vid_overlay || + ops->vidioc_s_fmt_vbi_cap || + ops->vidioc_s_fmt_vid_out_overlay || + ops->vidioc_s_fmt_vbi_out || + ops->vidioc_s_fmt_sliced_vbi_cap || + ops->vidioc_s_fmt_sliced_vbi_out || + ops->vidioc_s_fmt_type_private) + set_bit(_IOC_NR(VIDIOC_S_FMT), valid_ioctls); + if (ops->vidioc_try_fmt_vid_cap || + ops->vidioc_try_fmt_vid_out || + ops->vidioc_try_fmt_vid_cap_mplane || + ops->vidioc_try_fmt_vid_out_mplane || + ops->vidioc_try_fmt_vid_overlay || + ops->vidioc_try_fmt_vbi_cap || + ops->vidioc_try_fmt_vid_out_overlay || + ops->vidioc_try_fmt_vbi_out || + ops->vidioc_try_fmt_sliced_vbi_cap || + ops->vidioc_try_fmt_sliced_vbi_out || + ops->vidioc_try_fmt_type_private) + set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls); + SET_VALID_IOCTL(ops, VIDIOC_REQBUFS, vidioc_reqbufs); + SET_VALID_IOCTL(ops, VIDIOC_QUERYBUF, vidioc_querybuf); + SET_VALID_IOCTL(ops, VIDIOC_QBUF, vidioc_qbuf); + SET_VALID_IOCTL(ops, VIDIOC_DQBUF, vidioc_dqbuf); + SET_VALID_IOCTL(ops, VIDIOC_OVERLAY, vidioc_overlay); + SET_VALID_IOCTL(ops, VIDIOC_G_FBUF, vidioc_g_fbuf); + SET_VALID_IOCTL(ops, VIDIOC_S_FBUF, vidioc_s_fbuf); + SET_VALID_IOCTL(ops, VIDIOC_STREAMON, vidioc_streamon); + SET_VALID_IOCTL(ops, VIDIOC_STREAMOFF, vidioc_streamoff); + if (vdev->tvnorms) + set_bit(_IOC_NR(VIDIOC_ENUMSTD), valid_ioctls); + if (ops->vidioc_g_std || vdev->current_norm) + set_bit(_IOC_NR(VIDIOC_G_STD), valid_ioctls); + SET_VALID_IOCTL(ops, VIDIOC_S_STD, vidioc_s_std); + SET_VALID_IOCTL(ops, VIDIOC_QUERYSTD, vidioc_querystd); + SET_VALID_IOCTL(ops, VIDIOC_ENUMINPUT, vidioc_enum_input); + SET_VALID_IOCTL(ops, VIDIOC_G_INPUT, vidioc_g_input); + SET_VALID_IOCTL(ops, VIDIOC_S_INPUT, vidioc_s_input); + SET_VALID_IOCTL(ops, VIDIOC_ENUMOUTPUT, vidioc_enum_output); + SET_VALID_IOCTL(ops, VIDIOC_G_OUTPUT, vidioc_g_output); + SET_VALID_IOCTL(ops, VIDIOC_S_OUTPUT, vidioc_s_output); + /* Note: the control handler can also be passed through the filehandle, + and that can't be tested here. If the bit for these control ioctls + is set, then the ioctl is valid. But if it is 0, then it can still + be valid if the filehandle passed the control handler. */ + if (vdev->ctrl_handler || ops->vidioc_queryctrl) + set_bit(_IOC_NR(VIDIOC_QUERYCTRL), valid_ioctls); + if (vdev->ctrl_handler || ops->vidioc_g_ctrl || ops->vidioc_g_ext_ctrls) + set_bit(_IOC_NR(VIDIOC_G_CTRL), valid_ioctls); + if (vdev->ctrl_handler || ops->vidioc_s_ctrl || ops->vidioc_s_ext_ctrls) + set_bit(_IOC_NR(VIDIOC_S_CTRL), valid_ioctls); + if (vdev->ctrl_handler || ops->vidioc_g_ext_ctrls) + set_bit(_IOC_NR(VIDIOC_G_EXT_CTRLS), valid_ioctls); + if (vdev->ctrl_handler || ops->vidioc_s_ext_ctrls) + set_bit(_IOC_NR(VIDIOC_S_EXT_CTRLS), valid_ioctls); + if (vdev->ctrl_handler || ops->vidioc_try_ext_ctrls) + set_bit(_IOC_NR(VIDIOC_TRY_EXT_CTRLS), valid_ioctls); + if (vdev->ctrl_handler || ops->vidioc_querymenu) + set_bit(_IOC_NR(VIDIOC_QUERYMENU), valid_ioctls); + SET_VALID_IOCTL(ops, VIDIOC_ENUMAUDIO, vidioc_enumaudio); + SET_VALID_IOCTL(ops, VIDIOC_G_AUDIO, vidioc_g_audio); + SET_VALID_IOCTL(ops, VIDIOC_S_AUDIO, vidioc_s_audio); + SET_VALID_IOCTL(ops, VIDIOC_ENUMAUDOUT, vidioc_enumaudout); + SET_VALID_IOCTL(ops, VIDIOC_G_AUDOUT, vidioc_g_audout); + SET_VALID_IOCTL(ops, VIDIOC_S_AUDOUT, vidioc_s_audout); + SET_VALID_IOCTL(ops, VIDIOC_G_MODULATOR, vidioc_g_modulator); + SET_VALID_IOCTL(ops, VIDIOC_S_MODULATOR, vidioc_s_modulator); + if (ops->vidioc_g_crop || ops->vidioc_g_selection) + set_bit(_IOC_NR(VIDIOC_G_CROP), valid_ioctls); + if (ops->vidioc_s_crop || ops->vidioc_s_selection) + set_bit(_IOC_NR(VIDIOC_S_CROP), valid_ioctls); + SET_VALID_IOCTL(ops, VIDIOC_G_SELECTION, vidioc_g_selection); + SET_VALID_IOCTL(ops, VIDIOC_S_SELECTION, vidioc_s_selection); + if (ops->vidioc_cropcap || ops->vidioc_g_selection) + set_bit(_IOC_NR(VIDIOC_CROPCAP), valid_ioctls); + SET_VALID_IOCTL(ops, VIDIOC_G_JPEGCOMP, vidioc_g_jpegcomp); + SET_VALID_IOCTL(ops, VIDIOC_S_JPEGCOMP, vidioc_s_jpegcomp); + SET_VALID_IOCTL(ops, VIDIOC_G_ENC_INDEX, vidioc_g_enc_index); + SET_VALID_IOCTL(ops, VIDIOC_ENCODER_CMD, vidioc_encoder_cmd); + SET_VALID_IOCTL(ops, VIDIOC_TRY_ENCODER_CMD, vidioc_try_encoder_cmd); + SET_VALID_IOCTL(ops, VIDIOC_DECODER_CMD, vidioc_decoder_cmd); + SET_VALID_IOCTL(ops, VIDIOC_TRY_DECODER_CMD, vidioc_try_decoder_cmd); + if (ops->vidioc_g_parm || vdev->current_norm) + set_bit(_IOC_NR(VIDIOC_G_PARM), valid_ioctls); + SET_VALID_IOCTL(ops, VIDIOC_S_PARM, vidioc_s_parm); + SET_VALID_IOCTL(ops, VIDIOC_G_TUNER, vidioc_g_tuner); + SET_VALID_IOCTL(ops, VIDIOC_S_TUNER, vidioc_s_tuner); + SET_VALID_IOCTL(ops, VIDIOC_G_FREQUENCY, vidioc_g_frequency); + SET_VALID_IOCTL(ops, VIDIOC_S_FREQUENCY, vidioc_s_frequency); + SET_VALID_IOCTL(ops, VIDIOC_G_SLICED_VBI_CAP, vidioc_g_sliced_vbi_cap); + SET_VALID_IOCTL(ops, VIDIOC_LOG_STATUS, vidioc_log_status); +#ifdef CONFIG_VIDEO_ADV_DEBUG + SET_VALID_IOCTL(ops, VIDIOC_DBG_G_REGISTER, vidioc_g_register); + SET_VALID_IOCTL(ops, VIDIOC_DBG_S_REGISTER, vidioc_s_register); +#endif + SET_VALID_IOCTL(ops, VIDIOC_DBG_G_CHIP_IDENT, vidioc_g_chip_ident); + SET_VALID_IOCTL(ops, VIDIOC_S_HW_FREQ_SEEK, vidioc_s_hw_freq_seek); + SET_VALID_IOCTL(ops, VIDIOC_ENUM_FRAMESIZES, vidioc_enum_framesizes); + SET_VALID_IOCTL(ops, VIDIOC_ENUM_FRAMEINTERVALS, vidioc_enum_frameintervals); + SET_VALID_IOCTL(ops, VIDIOC_ENUM_DV_PRESETS, vidioc_enum_dv_presets); + SET_VALID_IOCTL(ops, VIDIOC_S_DV_PRESET, vidioc_s_dv_preset); + SET_VALID_IOCTL(ops, VIDIOC_G_DV_PRESET, vidioc_g_dv_preset); + SET_VALID_IOCTL(ops, VIDIOC_QUERY_DV_PRESET, vidioc_query_dv_preset); + SET_VALID_IOCTL(ops, VIDIOC_S_DV_TIMINGS, vidioc_s_dv_timings); + SET_VALID_IOCTL(ops, VIDIOC_G_DV_TIMINGS, vidioc_g_dv_timings); + /* yes, really vidioc_subscribe_event */ + SET_VALID_IOCTL(ops, VIDIOC_DQEVENT, vidioc_subscribe_event); + SET_VALID_IOCTL(ops, VIDIOC_SUBSCRIBE_EVENT, vidioc_subscribe_event); + SET_VALID_IOCTL(ops, VIDIOC_UNSUBSCRIBE_EVENT, vidioc_unsubscribe_event); + SET_VALID_IOCTL(ops, VIDIOC_CREATE_BUFS, vidioc_create_bufs); + SET_VALID_IOCTL(ops, VIDIOC_PREPARE_BUF, vidioc_prepare_buf); + bitmap_andnot(vdev->valid_ioctls, valid_ioctls, vdev->valid_ioctls, + BASE_VIDIOC_PRIVATE); +} + /** * __video_register_device - register video4linux devices * @vdev: video device structure we want to register @@ -654,6 +835,13 @@ int __video_register_device(struct video_device *vdev, int type, int nr, WARN_ON(video_device[vdev->minor] != NULL); vdev->index = get_index(vdev); mutex_unlock(&videodev_lock); + /* if no lock was passed, then make sure the LOCK_ALL_FOPS bit is + clear and warn if it wasn't. */ + if (vdev->lock == NULL) + WARN_ON(test_and_clear_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags)); + + if (vdev->ioctl_ops) + determine_valid_ioctls(vdev); /* Part 3: Initialize the character device */ vdev->cdev = cdev_alloc(); diff --git a/drivers/media/video/v4l2-event.c b/drivers/media/video/v4l2-event.c index c26ad9637143..60b4e2e9c874 100644 --- a/drivers/media/video/v4l2-event.c +++ b/drivers/media/video/v4l2-event.c @@ -25,7 +25,6 @@ #include <media/v4l2-dev.h> #include <media/v4l2-fh.h> #include <media/v4l2-event.h> -#include <media/v4l2-ctrls.h> #include <linux/sched.h> #include <linux/slab.h> @@ -120,6 +119,14 @@ static void __v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *e if (sev == NULL) return; + /* + * If the event has been added to the fh->subscribed list, but its + * add op has not completed yet elems will be 0, treat this as + * not being subscribed. + */ + if (!sev->elems) + return; + /* Increase event sequence number on fh. */ fh->sequence++; @@ -132,14 +139,14 @@ static void __v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *e sev->first = sev_pos(sev, 1); fh->navailable--; if (sev->elems == 1) { - if (sev->replace) { - sev->replace(&kev->event, ev); + if (sev->ops && sev->ops->replace) { + sev->ops->replace(&kev->event, ev); copy_payload = false; } - } else if (sev->merge) { + } else if (sev->ops && sev->ops->merge) { struct v4l2_kevent *second_oldest = sev->events + sev_pos(sev, 0); - sev->merge(&kev->event, &second_oldest->event); + sev->ops->merge(&kev->event, &second_oldest->event); } } @@ -195,24 +202,11 @@ int v4l2_event_pending(struct v4l2_fh *fh) } EXPORT_SYMBOL_GPL(v4l2_event_pending); -static void ctrls_replace(struct v4l2_event *old, const struct v4l2_event *new) -{ - u32 old_changes = old->u.ctrl.changes; - - old->u.ctrl = new->u.ctrl; - old->u.ctrl.changes |= old_changes; -} - -static void ctrls_merge(const struct v4l2_event *old, struct v4l2_event *new) -{ - new->u.ctrl.changes |= old->u.ctrl.changes; -} - int v4l2_event_subscribe(struct v4l2_fh *fh, - struct v4l2_event_subscription *sub, unsigned elems) + struct v4l2_event_subscription *sub, unsigned elems, + const struct v4l2_subscribed_event_ops *ops) { struct v4l2_subscribed_event *sev, *found_ev; - struct v4l2_ctrl *ctrl = NULL; unsigned long flags; unsigned i; @@ -221,11 +215,6 @@ int v4l2_event_subscribe(struct v4l2_fh *fh, if (elems < 1) elems = 1; - if (sub->type == V4L2_EVENT_CTRL) { - ctrl = v4l2_ctrl_find(fh->ctrl_handler, sub->id); - if (ctrl == NULL) - return -EINVAL; - } sev = kzalloc(sizeof(*sev) + sizeof(struct v4l2_kevent) * elems, GFP_KERNEL); if (!sev) @@ -236,11 +225,7 @@ int v4l2_event_subscribe(struct v4l2_fh *fh, sev->id = sub->id; sev->flags = sub->flags; sev->fh = fh; - sev->elems = elems; - if (ctrl) { - sev->replace = ctrls_replace; - sev->merge = ctrls_merge; - } + sev->ops = ops; spin_lock_irqsave(&fh->vdev->fh_lock, flags); found_ev = v4l2_event_subscribed(fh, sub->type, sub->id); @@ -248,11 +233,22 @@ int v4l2_event_subscribe(struct v4l2_fh *fh, list_add(&sev->list, &fh->subscribed); spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); - /* v4l2_ctrl_add_event uses a mutex, so do this outside the spin lock */ - if (found_ev) + if (found_ev) { kfree(sev); - else if (ctrl) - v4l2_ctrl_add_event(ctrl, sev); + return 0; /* Already listening */ + } + + if (sev->ops && sev->ops->add) { + int ret = sev->ops->add(sev); + if (ret) { + sev->ops = NULL; + v4l2_event_unsubscribe(fh, sub); + return ret; + } + } + + /* Mark as ready for use */ + sev->elems = elems; return 0; } @@ -306,12 +302,9 @@ int v4l2_event_unsubscribe(struct v4l2_fh *fh, } spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); - if (sev && sev->type == V4L2_EVENT_CTRL) { - struct v4l2_ctrl *ctrl = v4l2_ctrl_find(fh->ctrl_handler, sev->id); - if (ctrl) - v4l2_ctrl_del_event(ctrl, sev); - } + if (sev && sev->ops && sev->ops->del) + sev->ops->del(sev); kfree(sev); diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c index 5b2ec1fd2d0a..623d280ce095 100644 --- a/drivers/media/video/v4l2-ioctl.c +++ b/drivers/media/video/v4l2-ioctl.c @@ -55,19 +55,6 @@ memset((u8 *)(p) + offsetof(typeof(*(p)), field) + sizeof((p)->field), \ 0, sizeof(*(p)) - offsetof(typeof(*(p)), field) - sizeof((p)->field)) -#define have_fmt_ops(foo) ( \ - ops->vidioc_##foo##_fmt_vid_cap || \ - ops->vidioc_##foo##_fmt_vid_out || \ - ops->vidioc_##foo##_fmt_vid_cap_mplane || \ - ops->vidioc_##foo##_fmt_vid_out_mplane || \ - ops->vidioc_##foo##_fmt_vid_overlay || \ - ops->vidioc_##foo##_fmt_vbi_cap || \ - ops->vidioc_##foo##_fmt_vid_out_overlay || \ - ops->vidioc_##foo##_fmt_vbi_out || \ - ops->vidioc_##foo##_fmt_sliced_vbi_cap || \ - ops->vidioc_##foo##_fmt_sliced_vbi_out || \ - ops->vidioc_##foo##_fmt_type_private) - struct std_descr { v4l2_std_id std; const char *descr; @@ -195,93 +182,115 @@ static const char *v4l2_memory_names[] = { /* ------------------------------------------------------------------ */ /* debug help functions */ -static const char *v4l2_ioctls[] = { - [_IOC_NR(VIDIOC_QUERYCAP)] = "VIDIOC_QUERYCAP", - [_IOC_NR(VIDIOC_RESERVED)] = "VIDIOC_RESERVED", - [_IOC_NR(VIDIOC_ENUM_FMT)] = "VIDIOC_ENUM_FMT", - [_IOC_NR(VIDIOC_G_FMT)] = "VIDIOC_G_FMT", - [_IOC_NR(VIDIOC_S_FMT)] = "VIDIOC_S_FMT", - [_IOC_NR(VIDIOC_REQBUFS)] = "VIDIOC_REQBUFS", - [_IOC_NR(VIDIOC_QUERYBUF)] = "VIDIOC_QUERYBUF", - [_IOC_NR(VIDIOC_G_FBUF)] = "VIDIOC_G_FBUF", - [_IOC_NR(VIDIOC_S_FBUF)] = "VIDIOC_S_FBUF", - [_IOC_NR(VIDIOC_OVERLAY)] = "VIDIOC_OVERLAY", - [_IOC_NR(VIDIOC_QBUF)] = "VIDIOC_QBUF", - [_IOC_NR(VIDIOC_DQBUF)] = "VIDIOC_DQBUF", - [_IOC_NR(VIDIOC_STREAMON)] = "VIDIOC_STREAMON", - [_IOC_NR(VIDIOC_STREAMOFF)] = "VIDIOC_STREAMOFF", - [_IOC_NR(VIDIOC_G_PARM)] = "VIDIOC_G_PARM", - [_IOC_NR(VIDIOC_S_PARM)] = "VIDIOC_S_PARM", - [_IOC_NR(VIDIOC_G_STD)] = "VIDIOC_G_STD", - [_IOC_NR(VIDIOC_S_STD)] = "VIDIOC_S_STD", - [_IOC_NR(VIDIOC_ENUMSTD)] = "VIDIOC_ENUMSTD", - [_IOC_NR(VIDIOC_ENUMINPUT)] = "VIDIOC_ENUMINPUT", - [_IOC_NR(VIDIOC_G_CTRL)] = "VIDIOC_G_CTRL", - [_IOC_NR(VIDIOC_S_CTRL)] = "VIDIOC_S_CTRL", - [_IOC_NR(VIDIOC_G_TUNER)] = "VIDIOC_G_TUNER", - [_IOC_NR(VIDIOC_S_TUNER)] = "VIDIOC_S_TUNER", - [_IOC_NR(VIDIOC_G_AUDIO)] = "VIDIOC_G_AUDIO", - [_IOC_NR(VIDIOC_S_AUDIO)] = "VIDIOC_S_AUDIO", - [_IOC_NR(VIDIOC_QUERYCTRL)] = "VIDIOC_QUERYCTRL", - [_IOC_NR(VIDIOC_QUERYMENU)] = "VIDIOC_QUERYMENU", - [_IOC_NR(VIDIOC_G_INPUT)] = "VIDIOC_G_INPUT", - [_IOC_NR(VIDIOC_S_INPUT)] = "VIDIOC_S_INPUT", - [_IOC_NR(VIDIOC_G_OUTPUT)] = "VIDIOC_G_OUTPUT", - [_IOC_NR(VIDIOC_S_OUTPUT)] = "VIDIOC_S_OUTPUT", - [_IOC_NR(VIDIOC_ENUMOUTPUT)] = "VIDIOC_ENUMOUTPUT", - [_IOC_NR(VIDIOC_G_AUDOUT)] = "VIDIOC_G_AUDOUT", - [_IOC_NR(VIDIOC_S_AUDOUT)] = "VIDIOC_S_AUDOUT", - [_IOC_NR(VIDIOC_G_MODULATOR)] = "VIDIOC_G_MODULATOR", - [_IOC_NR(VIDIOC_S_MODULATOR)] = "VIDIOC_S_MODULATOR", - [_IOC_NR(VIDIOC_G_FREQUENCY)] = "VIDIOC_G_FREQUENCY", - [_IOC_NR(VIDIOC_S_FREQUENCY)] = "VIDIOC_S_FREQUENCY", - [_IOC_NR(VIDIOC_CROPCAP)] = "VIDIOC_CROPCAP", - [_IOC_NR(VIDIOC_G_CROP)] = "VIDIOC_G_CROP", - [_IOC_NR(VIDIOC_S_CROP)] = "VIDIOC_S_CROP", - [_IOC_NR(VIDIOC_G_SELECTION)] = "VIDIOC_G_SELECTION", - [_IOC_NR(VIDIOC_S_SELECTION)] = "VIDIOC_S_SELECTION", - [_IOC_NR(VIDIOC_G_JPEGCOMP)] = "VIDIOC_G_JPEGCOMP", - [_IOC_NR(VIDIOC_S_JPEGCOMP)] = "VIDIOC_S_JPEGCOMP", - [_IOC_NR(VIDIOC_QUERYSTD)] = "VIDIOC_QUERYSTD", - [_IOC_NR(VIDIOC_TRY_FMT)] = "VIDIOC_TRY_FMT", - [_IOC_NR(VIDIOC_ENUMAUDIO)] = "VIDIOC_ENUMAUDIO", - [_IOC_NR(VIDIOC_ENUMAUDOUT)] = "VIDIOC_ENUMAUDOUT", - [_IOC_NR(VIDIOC_G_PRIORITY)] = "VIDIOC_G_PRIORITY", - [_IOC_NR(VIDIOC_S_PRIORITY)] = "VIDIOC_S_PRIORITY", - [_IOC_NR(VIDIOC_G_SLICED_VBI_CAP)] = "VIDIOC_G_SLICED_VBI_CAP", - [_IOC_NR(VIDIOC_LOG_STATUS)] = "VIDIOC_LOG_STATUS", - [_IOC_NR(VIDIOC_G_EXT_CTRLS)] = "VIDIOC_G_EXT_CTRLS", - [_IOC_NR(VIDIOC_S_EXT_CTRLS)] = "VIDIOC_S_EXT_CTRLS", - [_IOC_NR(VIDIOC_TRY_EXT_CTRLS)] = "VIDIOC_TRY_EXT_CTRLS", -#if 1 - [_IOC_NR(VIDIOC_ENUM_FRAMESIZES)] = "VIDIOC_ENUM_FRAMESIZES", - [_IOC_NR(VIDIOC_ENUM_FRAMEINTERVALS)] = "VIDIOC_ENUM_FRAMEINTERVALS", - [_IOC_NR(VIDIOC_G_ENC_INDEX)] = "VIDIOC_G_ENC_INDEX", - [_IOC_NR(VIDIOC_ENCODER_CMD)] = "VIDIOC_ENCODER_CMD", - [_IOC_NR(VIDIOC_TRY_ENCODER_CMD)] = "VIDIOC_TRY_ENCODER_CMD", - - [_IOC_NR(VIDIOC_DECODER_CMD)] = "VIDIOC_DECODER_CMD", - [_IOC_NR(VIDIOC_TRY_DECODER_CMD)] = "VIDIOC_TRY_DECODER_CMD", - [_IOC_NR(VIDIOC_DBG_S_REGISTER)] = "VIDIOC_DBG_S_REGISTER", - [_IOC_NR(VIDIOC_DBG_G_REGISTER)] = "VIDIOC_DBG_G_REGISTER", - - [_IOC_NR(VIDIOC_DBG_G_CHIP_IDENT)] = "VIDIOC_DBG_G_CHIP_IDENT", - [_IOC_NR(VIDIOC_S_HW_FREQ_SEEK)] = "VIDIOC_S_HW_FREQ_SEEK", + +struct v4l2_ioctl_info { + unsigned int ioctl; + u16 flags; + const char * const name; +}; + +/* This control needs a priority check */ +#define INFO_FL_PRIO (1 << 0) +/* This control can be valid if the filehandle passes a control handler. */ +#define INFO_FL_CTRL (1 << 1) + +#define IOCTL_INFO(_ioctl, _flags) [_IOC_NR(_ioctl)] = { \ + .ioctl = _ioctl, \ + .flags = _flags, \ + .name = #_ioctl, \ +} + +static struct v4l2_ioctl_info v4l2_ioctls[] = { + IOCTL_INFO(VIDIOC_QUERYCAP, 0), + IOCTL_INFO(VIDIOC_ENUM_FMT, 0), + IOCTL_INFO(VIDIOC_G_FMT, 0), + IOCTL_INFO(VIDIOC_S_FMT, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_REQBUFS, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_QUERYBUF, 0), + IOCTL_INFO(VIDIOC_G_FBUF, 0), + IOCTL_INFO(VIDIOC_S_FBUF, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_OVERLAY, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_QBUF, 0), + IOCTL_INFO(VIDIOC_DQBUF, 0), + IOCTL_INFO(VIDIOC_STREAMON, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_STREAMOFF, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_G_PARM, 0), + IOCTL_INFO(VIDIOC_S_PARM, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_G_STD, 0), + IOCTL_INFO(VIDIOC_S_STD, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_ENUMSTD, 0), + IOCTL_INFO(VIDIOC_ENUMINPUT, 0), + IOCTL_INFO(VIDIOC_G_CTRL, INFO_FL_CTRL), + IOCTL_INFO(VIDIOC_S_CTRL, INFO_FL_PRIO | INFO_FL_CTRL), + IOCTL_INFO(VIDIOC_G_TUNER, 0), + IOCTL_INFO(VIDIOC_S_TUNER, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_G_AUDIO, 0), + IOCTL_INFO(VIDIOC_S_AUDIO, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_QUERYCTRL, INFO_FL_CTRL), + IOCTL_INFO(VIDIOC_QUERYMENU, INFO_FL_CTRL), + IOCTL_INFO(VIDIOC_G_INPUT, 0), + IOCTL_INFO(VIDIOC_S_INPUT, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_G_OUTPUT, 0), + IOCTL_INFO(VIDIOC_S_OUTPUT, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_ENUMOUTPUT, 0), + IOCTL_INFO(VIDIOC_G_AUDOUT, 0), + IOCTL_INFO(VIDIOC_S_AUDOUT, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_G_MODULATOR, 0), + IOCTL_INFO(VIDIOC_S_MODULATOR, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_G_FREQUENCY, 0), + IOCTL_INFO(VIDIOC_S_FREQUENCY, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_CROPCAP, 0), + IOCTL_INFO(VIDIOC_G_CROP, 0), + IOCTL_INFO(VIDIOC_S_CROP, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_G_SELECTION, 0), + IOCTL_INFO(VIDIOC_S_SELECTION, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_G_JPEGCOMP, 0), + IOCTL_INFO(VIDIOC_S_JPEGCOMP, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_QUERYSTD, 0), + IOCTL_INFO(VIDIOC_TRY_FMT, 0), + IOCTL_INFO(VIDIOC_ENUMAUDIO, 0), + IOCTL_INFO(VIDIOC_ENUMAUDOUT, 0), + IOCTL_INFO(VIDIOC_G_PRIORITY, 0), + IOCTL_INFO(VIDIOC_S_PRIORITY, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_G_SLICED_VBI_CAP, 0), + IOCTL_INFO(VIDIOC_LOG_STATUS, 0), + IOCTL_INFO(VIDIOC_G_EXT_CTRLS, INFO_FL_CTRL), + IOCTL_INFO(VIDIOC_S_EXT_CTRLS, INFO_FL_PRIO | INFO_FL_CTRL), + IOCTL_INFO(VIDIOC_TRY_EXT_CTRLS, 0), + IOCTL_INFO(VIDIOC_ENUM_FRAMESIZES, 0), + IOCTL_INFO(VIDIOC_ENUM_FRAMEINTERVALS, 0), + IOCTL_INFO(VIDIOC_G_ENC_INDEX, 0), + IOCTL_INFO(VIDIOC_ENCODER_CMD, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_TRY_ENCODER_CMD, 0), + IOCTL_INFO(VIDIOC_DECODER_CMD, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_TRY_DECODER_CMD, 0), +#ifdef CONFIG_VIDEO_ADV_DEBUG + IOCTL_INFO(VIDIOC_DBG_S_REGISTER, 0), + IOCTL_INFO(VIDIOC_DBG_G_REGISTER, 0), #endif - [_IOC_NR(VIDIOC_ENUM_DV_PRESETS)] = "VIDIOC_ENUM_DV_PRESETS", - [_IOC_NR(VIDIOC_S_DV_PRESET)] = "VIDIOC_S_DV_PRESET", - [_IOC_NR(VIDIOC_G_DV_PRESET)] = "VIDIOC_G_DV_PRESET", - [_IOC_NR(VIDIOC_QUERY_DV_PRESET)] = "VIDIOC_QUERY_DV_PRESET", - [_IOC_NR(VIDIOC_S_DV_TIMINGS)] = "VIDIOC_S_DV_TIMINGS", - [_IOC_NR(VIDIOC_G_DV_TIMINGS)] = "VIDIOC_G_DV_TIMINGS", - [_IOC_NR(VIDIOC_DQEVENT)] = "VIDIOC_DQEVENT", - [_IOC_NR(VIDIOC_SUBSCRIBE_EVENT)] = "VIDIOC_SUBSCRIBE_EVENT", - [_IOC_NR(VIDIOC_UNSUBSCRIBE_EVENT)] = "VIDIOC_UNSUBSCRIBE_EVENT", - [_IOC_NR(VIDIOC_CREATE_BUFS)] = "VIDIOC_CREATE_BUFS", - [_IOC_NR(VIDIOC_PREPARE_BUF)] = "VIDIOC_PREPARE_BUF", + IOCTL_INFO(VIDIOC_DBG_G_CHIP_IDENT, 0), + IOCTL_INFO(VIDIOC_S_HW_FREQ_SEEK, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_ENUM_DV_PRESETS, 0), + IOCTL_INFO(VIDIOC_S_DV_PRESET, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_G_DV_PRESET, 0), + IOCTL_INFO(VIDIOC_QUERY_DV_PRESET, 0), + IOCTL_INFO(VIDIOC_S_DV_TIMINGS, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_G_DV_TIMINGS, 0), + IOCTL_INFO(VIDIOC_DQEVENT, 0), + IOCTL_INFO(VIDIOC_SUBSCRIBE_EVENT, 0), + IOCTL_INFO(VIDIOC_UNSUBSCRIBE_EVENT, 0), + IOCTL_INFO(VIDIOC_CREATE_BUFS, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_PREPARE_BUF, 0), }; #define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls) +bool v4l2_is_known_ioctl(unsigned int cmd) +{ + if (_IOC_NR(cmd) >= V4L2_IOCTLS) + return false; + return v4l2_ioctls[_IOC_NR(cmd)].ioctl == cmd; +} + /* Common ioctl debug function. This function can be used by external ioctl messages as well as internal V4L ioctl */ void v4l_printk_ioctl(unsigned int cmd) @@ -297,7 +306,7 @@ void v4l_printk_ioctl(unsigned int cmd) type = "v4l2"; break; } - printk("%s", v4l2_ioctls[_IOC_NR(cmd)]); + printk("%s", v4l2_ioctls[_IOC_NR(cmd)].name); return; default: type = "unknown"; @@ -504,7 +513,6 @@ static long __video_do_ioctl(struct file *file, void *fh = file->private_data; struct v4l2_fh *vfh = NULL; int use_fh_prio = 0; - long ret_prio = 0; long ret = -ENOTTY; if (ops == NULL) { @@ -513,19 +521,30 @@ static long __video_do_ioctl(struct file *file, return ret; } - if ((vfd->debug & V4L2_DEBUG_IOCTL) && - !(vfd->debug & V4L2_DEBUG_IOCTL_ARG)) { - v4l_print_ioctl(vfd->name, cmd); - printk(KERN_CONT "\n"); - } - if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) { vfh = file->private_data; use_fh_prio = test_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags); } - if (use_fh_prio) - ret_prio = v4l2_prio_check(vfd->prio, vfh->prio); + if (v4l2_is_known_ioctl(cmd)) { + struct v4l2_ioctl_info *info = &v4l2_ioctls[_IOC_NR(cmd)]; + + if (!test_bit(_IOC_NR(cmd), vfd->valid_ioctls) && + !((info->flags & INFO_FL_CTRL) && vfh && vfh->ctrl_handler)) + return -ENOTTY; + + if (use_fh_prio && (info->flags & INFO_FL_PRIO)) { + ret = v4l2_prio_check(vfd->prio, vfh->prio); + if (ret) + return ret; + } + } + + if ((vfd->debug & V4L2_DEBUG_IOCTL) && + !(vfd->debug & V4L2_DEBUG_IOCTL_ARG)) { + v4l_print_ioctl(vfd->name, cmd); + printk(KERN_CONT "\n"); + } switch (cmd) { @@ -534,9 +553,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_capability *cap = (struct v4l2_capability *)arg; - if (!ops->vidioc_querycap) - break; - cap->version = LINUX_VERSION_CODE; ret = ops->vidioc_querycap(file, fh, cap); if (!ret) @@ -570,14 +586,11 @@ static long __video_do_ioctl(struct file *file, { enum v4l2_priority *p = arg; - if (!ops->vidioc_s_priority && !use_fh_prio) - break; dbgarg(cmd, "setting priority to %d\n", *p); if (ops->vidioc_s_priority) ret = ops->vidioc_s_priority(file, fh, *p); else - ret = ret_prio ? ret_prio : - v4l2_prio_change(&vfd->v4l2_dev->prio, + ret = v4l2_prio_change(&vfd->v4l2_dev->prio, &vfh->prio, *p); break; } @@ -587,6 +600,7 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_fmtdesc *f = arg; + ret = -EINVAL; switch (f->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: if (likely(ops->vidioc_enum_fmt_vid_cap)) @@ -619,7 +633,7 @@ static long __video_do_ioctl(struct file *file, default: break; } - if (likely (!ret)) + if (likely(!ret)) dbgarg(cmd, "index=%d, type=%d, flags=%d, " "pixelformat=%c%c%c%c, description='%s'\n", f->index, f->type, f->flags, @@ -628,14 +642,6 @@ static long __video_do_ioctl(struct file *file, (f->pixelformat >> 16) & 0xff, (f->pixelformat >> 24) & 0xff, f->description); - else if (ret == -ENOTTY && - (ops->vidioc_enum_fmt_vid_cap || - ops->vidioc_enum_fmt_vid_out || - ops->vidioc_enum_fmt_vid_cap_mplane || - ops->vidioc_enum_fmt_vid_out_mplane || - ops->vidioc_enum_fmt_vid_overlay || - ops->vidioc_enum_fmt_type_private)) - ret = -EINVAL; break; } case VIDIOC_G_FMT: @@ -645,6 +651,7 @@ static long __video_do_ioctl(struct file *file, /* FIXME: Should be one dump per type */ dbgarg(cmd, "type=%s\n", prt_names(f->type, v4l2_type_names)); + ret = -EINVAL; switch (f->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: if (ops->vidioc_g_fmt_vid_cap) @@ -706,21 +713,12 @@ static long __video_do_ioctl(struct file *file, fh, f); break; } - if (unlikely(ret == -ENOTTY && have_fmt_ops(g))) - ret = -EINVAL; - break; } case VIDIOC_S_FMT: { struct v4l2_format *f = (struct v4l2_format *)arg; - if (!have_fmt_ops(s)) - break; - if (ret_prio) { - ret = ret_prio; - break; - } ret = -EINVAL; /* FIXME: Should be one dump per type */ @@ -804,6 +802,7 @@ static long __video_do_ioctl(struct file *file, /* FIXME: Should be one dump per type */ dbgarg(cmd, "type=%s\n", prt_names(f->type, v4l2_type_names)); + ret = -EINVAL; switch (f->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: CLEAR_AFTER_FIELD(f, fmt.pix); @@ -876,8 +875,6 @@ static long __video_do_ioctl(struct file *file, fh, f); break; } - if (unlikely(ret == -ENOTTY && have_fmt_ops(try))) - ret = -EINVAL; break; } /* FIXME: Those buf reqs could be handled here, @@ -888,12 +885,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_requestbuffers *p = arg; - if (!ops->vidioc_reqbufs) - break; - if (ret_prio) { - ret = ret_prio; - break; - } ret = check_fmt(ops, p->type); if (ret) break; @@ -912,8 +903,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_buffer *p = arg; - if (!ops->vidioc_querybuf) - break; ret = check_fmt(ops, p->type); if (ret) break; @@ -927,8 +916,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_buffer *p = arg; - if (!ops->vidioc_qbuf) - break; ret = check_fmt(ops, p->type); if (ret) break; @@ -942,8 +929,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_buffer *p = arg; - if (!ops->vidioc_dqbuf) - break; ret = check_fmt(ops, p->type); if (ret) break; @@ -957,12 +942,6 @@ static long __video_do_ioctl(struct file *file, { int *i = arg; - if (!ops->vidioc_overlay) - break; - if (ret_prio) { - ret = ret_prio; - break; - } dbgarg(cmd, "value=%d\n", *i); ret = ops->vidioc_overlay(file, fh, *i); break; @@ -971,8 +950,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_framebuffer *p = arg; - if (!ops->vidioc_g_fbuf) - break; ret = ops->vidioc_g_fbuf(file, fh, arg); if (!ret) { dbgarg(cmd, "capability=0x%x, flags=%d, base=0x%08lx\n", @@ -986,12 +963,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_framebuffer *p = arg; - if (!ops->vidioc_s_fbuf) - break; - if (ret_prio) { - ret = ret_prio; - break; - } dbgarg(cmd, "capability=0x%x, flags=%d, base=0x%08lx\n", p->capability, p->flags, (unsigned long)p->base); v4l_print_pix_fmt(vfd, &p->fmt); @@ -1002,12 +973,6 @@ static long __video_do_ioctl(struct file *file, { enum v4l2_buf_type i = *(int *)arg; - if (!ops->vidioc_streamon) - break; - if (ret_prio) { - ret = ret_prio; - break; - } dbgarg(cmd, "type=%s\n", prt_names(i, v4l2_type_names)); ret = ops->vidioc_streamon(file, fh, i); break; @@ -1016,12 +981,6 @@ static long __video_do_ioctl(struct file *file, { enum v4l2_buf_type i = *(int *)arg; - if (!ops->vidioc_streamoff) - break; - if (ret_prio) { - ret = ret_prio; - break; - } dbgarg(cmd, "type=%s\n", prt_names(i, v4l2_type_names)); ret = ops->vidioc_streamoff(file, fh, i); break; @@ -1091,13 +1050,6 @@ static long __video_do_ioctl(struct file *file, dbgarg(cmd, "std=%08Lx\n", (long long unsigned)*id); - if (!ops->vidioc_s_std) - break; - - if (ret_prio) { - ret = ret_prio; - break; - } ret = -EINVAL; norm = (*id) & vfd->tvnorms; if (vfd->tvnorms && !norm) /* Check if std is supported */ @@ -1115,8 +1067,6 @@ static long __video_do_ioctl(struct file *file, { v4l2_std_id *p = arg; - if (!ops->vidioc_querystd) - break; /* * If nothing detected, it should return all supported * Drivers just need to mask the std argument, in order @@ -1150,9 +1100,6 @@ static long __video_do_ioctl(struct file *file, if (ops->vidioc_s_dv_timings) p->capabilities |= V4L2_IN_CAP_CUSTOM_TIMINGS; - if (!ops->vidioc_enum_input) - break; - ret = ops->vidioc_enum_input(file, fh, p); if (!ret) dbgarg(cmd, "index=%d, name=%s, type=%d, " @@ -1168,8 +1115,6 @@ static long __video_do_ioctl(struct file *file, { unsigned int *i = arg; - if (!ops->vidioc_g_input) - break; ret = ops->vidioc_g_input(file, fh, i); if (!ret) dbgarg(cmd, "value=%d\n", *i); @@ -1179,12 +1124,6 @@ static long __video_do_ioctl(struct file *file, { unsigned int *i = arg; - if (!ops->vidioc_s_input) - break; - if (ret_prio) { - ret = ret_prio; - break; - } dbgarg(cmd, "value=%d\n", *i); ret = ops->vidioc_s_input(file, fh, *i); break; @@ -1195,9 +1134,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_output *p = arg; - if (!ops->vidioc_enum_output) - break; - /* * We set the flags for CAP_PRESETS, CAP_CUSTOM_TIMINGS & * CAP_STD here based on ioctl handler provided by the @@ -1224,8 +1160,6 @@ static long __video_do_ioctl(struct file *file, { unsigned int *i = arg; - if (!ops->vidioc_g_output) - break; ret = ops->vidioc_g_output(file, fh, i); if (!ret) dbgarg(cmd, "value=%d\n", *i); @@ -1235,12 +1169,6 @@ static long __video_do_ioctl(struct file *file, { unsigned int *i = arg; - if (!ops->vidioc_s_output) - break; - if (ret_prio) { - ret = ret_prio; - break; - } dbgarg(cmd, "value=%d\n", *i); ret = ops->vidioc_s_output(file, fh, *i); break; @@ -1310,10 +1238,6 @@ static long __video_do_ioctl(struct file *file, if (!(vfh && vfh->ctrl_handler) && !vfd->ctrl_handler && !ops->vidioc_s_ctrl && !ops->vidioc_s_ext_ctrls) break; - if (ret_prio) { - ret = ret_prio; - break; - } dbgarg(cmd, "id=0x%x, value=%d\n", p->id, p->value); @@ -1369,10 +1293,6 @@ static long __video_do_ioctl(struct file *file, if (!(vfh && vfh->ctrl_handler) && !vfd->ctrl_handler && !ops->vidioc_s_ext_ctrls) break; - if (ret_prio) { - ret = ret_prio; - break; - } v4l_print_ext_ctrls(cmd, vfd, p, 1); if (vfh && vfh->ctrl_handler) ret = v4l2_s_ext_ctrls(vfh, vfh->ctrl_handler, p); @@ -1428,8 +1348,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_audio *p = arg; - if (!ops->vidioc_enumaudio) - break; ret = ops->vidioc_enumaudio(file, fh, p); if (!ret) dbgarg(cmd, "index=%d, name=%s, capability=0x%x, " @@ -1443,9 +1361,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_audio *p = arg; - if (!ops->vidioc_g_audio) - break; - ret = ops->vidioc_g_audio(file, fh, p); if (!ret) dbgarg(cmd, "index=%d, name=%s, capability=0x%x, " @@ -1459,12 +1374,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_audio *p = arg; - if (!ops->vidioc_s_audio) - break; - if (ret_prio) { - ret = ret_prio; - break; - } dbgarg(cmd, "index=%d, name=%s, capability=0x%x, " "mode=0x%x\n", p->index, p->name, p->capability, p->mode); @@ -1475,8 +1384,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_audioout *p = arg; - if (!ops->vidioc_enumaudout) - break; dbgarg(cmd, "Enum for index=%d\n", p->index); ret = ops->vidioc_enumaudout(file, fh, p); if (!ret) @@ -1489,9 +1396,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_audioout *p = arg; - if (!ops->vidioc_g_audout) - break; - ret = ops->vidioc_g_audout(file, fh, p); if (!ret) dbgarg2("index=%d, name=%s, capability=%d, " @@ -1503,12 +1407,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_audioout *p = arg; - if (!ops->vidioc_s_audout) - break; - if (ret_prio) { - ret = ret_prio; - break; - } dbgarg(cmd, "index=%d, name=%s, capability=%d, " "mode=%d\n", p->index, p->name, p->capability, p->mode); @@ -1520,8 +1418,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_modulator *p = arg; - if (!ops->vidioc_g_modulator) - break; ret = ops->vidioc_g_modulator(file, fh, p); if (!ret) dbgarg(cmd, "index=%d, name=%s, " @@ -1536,12 +1432,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_modulator *p = arg; - if (!ops->vidioc_s_modulator) - break; - if (ret_prio) { - ret = ret_prio; - break; - } dbgarg(cmd, "index=%d, name=%s, capability=%d, " "rangelow=%d, rangehigh=%d, txsubchans=%d\n", p->index, p->name, p->capability, p->rangelow, @@ -1553,9 +1443,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_crop *p = arg; - if (!ops->vidioc_g_crop && !ops->vidioc_g_selection) - break; - dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names)); if (ops->vidioc_g_crop) { @@ -1587,13 +1474,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_crop *p = arg; - if (!ops->vidioc_s_crop && !ops->vidioc_s_selection) - break; - - if (ret_prio) { - ret = ret_prio; - break; - } dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names)); dbgrect(vfd, "", &p->c); @@ -1620,9 +1500,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_selection *p = arg; - if (!ops->vidioc_g_selection) - break; - dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names)); ret = ops->vidioc_g_selection(file, fh, p); @@ -1634,13 +1511,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_selection *p = arg; - if (!ops->vidioc_s_selection) - break; - - if (ret_prio) { - ret = ret_prio; - break; - } dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names)); dbgrect(vfd, "", &p->r); @@ -1653,9 +1523,6 @@ static long __video_do_ioctl(struct file *file, struct v4l2_cropcap *p = arg; /*FIXME: Should also show v4l2_fract pixelaspect */ - if (!ops->vidioc_cropcap && !ops->vidioc_g_selection) - break; - dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names)); if (ops->vidioc_cropcap) { ret = ops->vidioc_cropcap(file, fh, p); @@ -1699,9 +1566,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_jpegcompression *p = arg; - if (!ops->vidioc_g_jpegcomp) - break; - ret = ops->vidioc_g_jpegcomp(file, fh, p); if (!ret) dbgarg(cmd, "quality=%d, APPn=%d, " @@ -1715,12 +1579,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_jpegcompression *p = arg; - if (!ops->vidioc_g_jpegcomp) - break; - if (ret_prio) { - ret = ret_prio; - break; - } dbgarg(cmd, "quality=%d, APPn=%d, APP_len=%d, " "COM_len=%d, jpeg_markers=%d\n", p->quality, p->APPn, p->APP_len, @@ -1732,8 +1590,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_enc_idx *p = arg; - if (!ops->vidioc_g_enc_index) - break; ret = ops->vidioc_g_enc_index(file, fh, p); if (!ret) dbgarg(cmd, "entries=%d, entries_cap=%d\n", @@ -1744,12 +1600,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_encoder_cmd *p = arg; - if (!ops->vidioc_encoder_cmd) - break; - if (ret_prio) { - ret = ret_prio; - break; - } ret = ops->vidioc_encoder_cmd(file, fh, p); if (!ret) dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags); @@ -1759,8 +1609,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_encoder_cmd *p = arg; - if (!ops->vidioc_try_encoder_cmd) - break; ret = ops->vidioc_try_encoder_cmd(file, fh, p); if (!ret) dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags); @@ -1770,12 +1618,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_decoder_cmd *p = arg; - if (!ops->vidioc_decoder_cmd) - break; - if (ret_prio) { - ret = ret_prio; - break; - } ret = ops->vidioc_decoder_cmd(file, fh, p); if (!ret) dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags); @@ -1785,8 +1627,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_decoder_cmd *p = arg; - if (!ops->vidioc_try_decoder_cmd) - break; ret = ops->vidioc_try_decoder_cmd(file, fh, p); if (!ret) dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags); @@ -1796,8 +1636,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_streamparm *p = arg; - if (!ops->vidioc_g_parm && !vfd->current_norm) - break; if (ops->vidioc_g_parm) { ret = check_fmt(ops, p->type); if (ret) @@ -1825,12 +1663,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_streamparm *p = arg; - if (!ops->vidioc_s_parm) - break; - if (ret_prio) { - ret = ret_prio; - break; - } ret = check_fmt(ops, p->type); if (ret) break; @@ -1843,9 +1675,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_tuner *p = arg; - if (!ops->vidioc_g_tuner) - break; - p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; ret = ops->vidioc_g_tuner(file, fh, p); @@ -1864,12 +1693,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_tuner *p = arg; - if (!ops->vidioc_s_tuner) - break; - if (ret_prio) { - ret = ret_prio; - break; - } p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; dbgarg(cmd, "index=%d, name=%s, type=%d, " @@ -1887,9 +1710,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_frequency *p = arg; - if (!ops->vidioc_g_frequency) - break; - p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; ret = ops->vidioc_g_frequency(file, fh, p); @@ -1903,12 +1723,6 @@ static long __video_do_ioctl(struct file *file, struct v4l2_frequency *p = arg; enum v4l2_tuner_type type; - if (!ops->vidioc_s_frequency) - break; - if (ret_prio) { - ret = ret_prio; - break; - } type = (vfd->vfl_type == VFL_TYPE_RADIO) ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; dbgarg(cmd, "tuner=%d, type=%d, frequency=%d\n", @@ -1923,9 +1737,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_sliced_vbi_cap *p = arg; - if (!ops->vidioc_g_sliced_vbi_cap) - break; - /* Clear up to type, everything after type is zerod already */ memset(p, 0, offsetof(struct v4l2_sliced_vbi_cap, type)); @@ -1937,8 +1748,6 @@ static long __video_do_ioctl(struct file *file, } case VIDIOC_LOG_STATUS: { - if (!ops->vidioc_log_status) - break; if (vfd->v4l2_dev) pr_info("%s: ================= START STATUS =================\n", vfd->v4l2_dev->name); @@ -1948,38 +1757,34 @@ static long __video_do_ioctl(struct file *file, vfd->v4l2_dev->name); break; } -#ifdef CONFIG_VIDEO_ADV_DEBUG case VIDIOC_DBG_G_REGISTER: { +#ifdef CONFIG_VIDEO_ADV_DEBUG struct v4l2_dbg_register *p = arg; - if (ops->vidioc_g_register) { - if (!capable(CAP_SYS_ADMIN)) - ret = -EPERM; - else - ret = ops->vidioc_g_register(file, fh, p); - } + if (!capable(CAP_SYS_ADMIN)) + ret = -EPERM; + else + ret = ops->vidioc_g_register(file, fh, p); +#endif break; } case VIDIOC_DBG_S_REGISTER: { +#ifdef CONFIG_VIDEO_ADV_DEBUG struct v4l2_dbg_register *p = arg; - if (ops->vidioc_s_register) { - if (!capable(CAP_SYS_ADMIN)) - ret = -EPERM; - else - ret = ops->vidioc_s_register(file, fh, p); - } + if (!capable(CAP_SYS_ADMIN)) + ret = -EPERM; + else + ret = ops->vidioc_s_register(file, fh, p); +#endif break; } -#endif case VIDIOC_DBG_G_CHIP_IDENT: { struct v4l2_dbg_chip_ident *p = arg; - if (!ops->vidioc_g_chip_ident) - break; p->ident = V4L2_IDENT_NONE; p->revision = 0; ret = ops->vidioc_g_chip_ident(file, fh, p); @@ -1992,12 +1797,6 @@ static long __video_do_ioctl(struct file *file, struct v4l2_hw_freq_seek *p = arg; enum v4l2_tuner_type type; - if (!ops->vidioc_s_hw_freq_seek) - break; - if (ret_prio) { - ret = ret_prio; - break; - } type = (vfd->vfl_type == VFL_TYPE_RADIO) ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; dbgarg(cmd, @@ -2013,9 +1812,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_frmsizeenum *p = arg; - if (!ops->vidioc_enum_framesizes) - break; - ret = ops->vidioc_enum_framesizes(file, fh, p); dbgarg(cmd, "index=%d, pixelformat=%c%c%c%c, type=%d ", @@ -2049,9 +1845,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_frmivalenum *p = arg; - if (!ops->vidioc_enum_frameintervals) - break; - ret = ops->vidioc_enum_frameintervals(file, fh, p); dbgarg(cmd, "index=%d, pixelformat=%d, width=%d, height=%d, type=%d ", @@ -2084,9 +1877,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_dv_enum_preset *p = arg; - if (!ops->vidioc_enum_dv_presets) - break; - ret = ops->vidioc_enum_dv_presets(file, fh, p); if (!ret) dbgarg(cmd, @@ -2100,13 +1890,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_dv_preset *p = arg; - if (!ops->vidioc_s_dv_preset) - break; - if (ret_prio) { - ret = ret_prio; - break; - } - dbgarg(cmd, "preset=%d\n", p->preset); ret = ops->vidioc_s_dv_preset(file, fh, p); break; @@ -2115,9 +1898,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_dv_preset *p = arg; - if (!ops->vidioc_g_dv_preset) - break; - ret = ops->vidioc_g_dv_preset(file, fh, p); if (!ret) dbgarg(cmd, "preset=%d\n", p->preset); @@ -2127,9 +1907,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_dv_preset *p = arg; - if (!ops->vidioc_query_dv_preset) - break; - ret = ops->vidioc_query_dv_preset(file, fh, p); if (!ret) dbgarg(cmd, "preset=%d\n", p->preset); @@ -2139,13 +1916,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_dv_timings *p = arg; - if (!ops->vidioc_s_dv_timings) - break; - if (ret_prio) { - ret = ret_prio; - break; - } - switch (p->type) { case V4L2_DV_BT_656_1120: dbgarg2("bt-656/1120:interlaced=%d, pixelclock=%lld," @@ -2173,9 +1943,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_dv_timings *p = arg; - if (!ops->vidioc_g_dv_timings) - break; - ret = ops->vidioc_g_dv_timings(file, fh, p); if (!ret) { switch (p->type) { @@ -2207,9 +1974,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_event *ev = arg; - if (!ops->vidioc_subscribe_event) - break; - ret = v4l2_event_dequeue(fh, ev, file->f_flags & O_NONBLOCK); if (ret < 0) { dbgarg(cmd, "no pending events?"); @@ -2226,9 +1990,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_event_subscription *sub = arg; - if (!ops->vidioc_subscribe_event) - break; - ret = ops->vidioc_subscribe_event(fh, sub); if (ret < 0) { dbgarg(cmd, "failed, ret=%ld", ret); @@ -2241,9 +2002,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_event_subscription *sub = arg; - if (!ops->vidioc_unsubscribe_event) - break; - ret = ops->vidioc_unsubscribe_event(fh, sub); if (ret < 0) { dbgarg(cmd, "failed, ret=%ld", ret); @@ -2256,12 +2014,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_create_buffers *create = arg; - if (!ops->vidioc_create_bufs) - break; - if (ret_prio) { - ret = ret_prio; - break; - } ret = check_fmt(ops, create->format.type); if (ret) break; @@ -2275,8 +2027,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_buffer *b = arg; - if (!ops->vidioc_prepare_buf) - break; ret = check_fmt(ops, b->type); if (ret) break; @@ -2289,7 +2039,9 @@ static long __video_do_ioctl(struct file *file, default: if (!ops->vidioc_default) break; - ret = ops->vidioc_default(file, fh, ret_prio >= 0, cmd, arg); + ret = ops->vidioc_default(file, fh, use_fh_prio ? + v4l2_prio_check(vfd->prio, vfh->prio) >= 0 : 0, + cmd, arg); break; } /* switch */ diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c index 6fe88e965a8c..db6e859b93d4 100644 --- a/drivers/media/video/v4l2-subdev.c +++ b/drivers/media/video/v4l2-subdev.c @@ -35,14 +35,9 @@ static int subdev_fh_init(struct v4l2_subdev_fh *fh, struct v4l2_subdev *sd) { #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) - /* Allocate try format and crop in the same memory block */ - fh->try_fmt = kzalloc((sizeof(*fh->try_fmt) + sizeof(*fh->try_crop)) - * sd->entity.num_pads, GFP_KERNEL); - if (fh->try_fmt == NULL) + fh->pad = kzalloc(sizeof(*fh->pad) * sd->entity.num_pads, GFP_KERNEL); + if (fh->pad == NULL) return -ENOMEM; - - fh->try_crop = (struct v4l2_rect *) - (fh->try_fmt + sd->entity.num_pads); #endif return 0; } @@ -50,9 +45,8 @@ static int subdev_fh_init(struct v4l2_subdev_fh *fh, struct v4l2_subdev *sd) static void subdev_fh_free(struct v4l2_subdev_fh *fh) { #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) - kfree(fh->try_fmt); - fh->try_fmt = NULL; - fh->try_crop = NULL; + kfree(fh->pad); + fh->pad = NULL; #endif } @@ -234,6 +228,8 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) case VIDIOC_SUBDEV_G_CROP: { struct v4l2_subdev_crop *crop = arg; + struct v4l2_subdev_selection sel; + int rval; if (crop->which != V4L2_SUBDEV_FORMAT_TRY && crop->which != V4L2_SUBDEV_FORMAT_ACTIVE) @@ -242,11 +238,27 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) if (crop->pad >= sd->entity.num_pads) return -EINVAL; - return v4l2_subdev_call(sd, pad, get_crop, subdev_fh, crop); + rval = v4l2_subdev_call(sd, pad, get_crop, subdev_fh, crop); + if (rval != -ENOIOCTLCMD) + return rval; + + memset(&sel, 0, sizeof(sel)); + sel.which = crop->which; + sel.pad = crop->pad; + sel.target = V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL; + + rval = v4l2_subdev_call( + sd, pad, get_selection, subdev_fh, &sel); + + crop->rect = sel.r; + + return rval; } case VIDIOC_SUBDEV_S_CROP: { struct v4l2_subdev_crop *crop = arg; + struct v4l2_subdev_selection sel; + int rval; if (crop->which != V4L2_SUBDEV_FORMAT_TRY && crop->which != V4L2_SUBDEV_FORMAT_ACTIVE) @@ -255,7 +267,22 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) if (crop->pad >= sd->entity.num_pads) return -EINVAL; - return v4l2_subdev_call(sd, pad, set_crop, subdev_fh, crop); + rval = v4l2_subdev_call(sd, pad, set_crop, subdev_fh, crop); + if (rval != -ENOIOCTLCMD) + return rval; + + memset(&sel, 0, sizeof(sel)); + sel.which = crop->which; + sel.pad = crop->pad; + sel.target = V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL; + sel.r = crop->rect; + + rval = v4l2_subdev_call( + sd, pad, set_selection, subdev_fh, &sel); + + crop->rect = sel.r; + + return rval; } case VIDIOC_SUBDEV_ENUM_MBUS_CODE: { @@ -293,6 +320,34 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) return v4l2_subdev_call(sd, pad, enum_frame_interval, subdev_fh, fie); } + + case VIDIOC_SUBDEV_G_SELECTION: { + struct v4l2_subdev_selection *sel = arg; + + if (sel->which != V4L2_SUBDEV_FORMAT_TRY && + sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + + if (sel->pad >= sd->entity.num_pads) + return -EINVAL; + + return v4l2_subdev_call( + sd, pad, get_selection, subdev_fh, sel); + } + + case VIDIOC_SUBDEV_S_SELECTION: { + struct v4l2_subdev_selection *sel = arg; + + if (sel->which != V4L2_SUBDEV_FORMAT_TRY && + sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + + if (sel->pad >= sd->entity.num_pads) + return -EINVAL; + + return v4l2_subdev_call( + sd, pad, set_selection, subdev_fh, sel); + } #endif default: return v4l2_subdev_call(sd, core, ioctl, cmd, arg); @@ -332,6 +387,70 @@ const struct v4l2_file_operations v4l2_subdev_fops = { .poll = subdev_poll, }; +#ifdef CONFIG_MEDIA_CONTROLLER +int v4l2_subdev_link_validate_default(struct v4l2_subdev *sd, + struct media_link *link, + struct v4l2_subdev_format *source_fmt, + struct v4l2_subdev_format *sink_fmt) +{ + if (source_fmt->format.width != sink_fmt->format.width + || source_fmt->format.height != sink_fmt->format.height + || source_fmt->format.code != sink_fmt->format.code) + return -EINVAL; + + return 0; +} +EXPORT_SYMBOL_GPL(v4l2_subdev_link_validate_default); + +static int +v4l2_subdev_link_validate_get_format(struct media_pad *pad, + struct v4l2_subdev_format *fmt) +{ + switch (media_entity_type(pad->entity)) { + case MEDIA_ENT_T_V4L2_SUBDEV: + fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE; + fmt->pad = pad->index; + return v4l2_subdev_call(media_entity_to_v4l2_subdev( + pad->entity), + pad, get_fmt, NULL, fmt); + default: + WARN(1, "Driver bug! Wrong media entity type %d, entity %s\n", + media_entity_type(pad->entity), pad->entity->name); + /* Fall through */ + case MEDIA_ENT_T_DEVNODE_V4L: + return -EINVAL; + } +} + +int v4l2_subdev_link_validate(struct media_link *link) +{ + struct v4l2_subdev *sink; + struct v4l2_subdev_format sink_fmt, source_fmt; + int rval; + + rval = v4l2_subdev_link_validate_get_format( + link->source, &source_fmt); + if (rval < 0) + return 0; + + rval = v4l2_subdev_link_validate_get_format( + link->sink, &sink_fmt); + if (rval < 0) + return 0; + + sink = media_entity_to_v4l2_subdev(link->sink->entity); + + rval = v4l2_subdev_call(sink, pad, link_validate, link, + &source_fmt, &sink_fmt); + if (rval != -ENOIOCTLCMD) + return rval; + + return v4l2_subdev_link_validate_default( + sink, link, &source_fmt, &sink_fmt); +} +EXPORT_SYMBOL_GPL(v4l2_subdev_link_validate); +#endif /* CONFIG_MEDIA_CONTROLLER */ + void v4l2_subdev_init(struct v4l2_subdev *sd, const struct v4l2_subdev_ops *ops) { INIT_LIST_HEAD(&sd->list); diff --git a/drivers/media/video/via-camera.c b/drivers/media/video/via-camera.c index 20f7237b8242..308e150a39bc 100644 --- a/drivers/media/video/via-camera.c +++ b/drivers/media/video/via-camera.c @@ -18,6 +18,7 @@ #include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> #include <media/v4l2-chip-ident.h> +#include <media/ov7670.h> #include <media/videobuf-dma-sg.h> #include <linux/delay.h> #include <linux/dma-mapping.h> @@ -1347,11 +1348,21 @@ static __devinit bool viacam_serial_is_enabled(void) return false; } +static struct ov7670_config sensor_cfg = { + /* The XO-1.5 (only known user) clocks the camera at 90MHz. */ + .clock_speed = 90, +}; + static __devinit int viacam_probe(struct platform_device *pdev) { int ret; struct i2c_adapter *sensor_adapter; struct viafb_dev *viadev = pdev->dev.platform_data; + struct i2c_board_info ov7670_info = { + .type = "ov7670", + .addr = 0x42 >> 1, + .platform_data = &sensor_cfg, + }; /* * Note that there are actually two capture channels on @@ -1433,8 +1444,8 @@ static __devinit int viacam_probe(struct platform_device *pdev) * is OLPC-specific. 0x42 assumption is ov7670-specific. */ sensor_adapter = viafb_find_i2c_adapter(VIA_PORT_31); - cam->sensor = v4l2_i2c_new_subdev(&cam->v4l2_dev, sensor_adapter, - "ov7670", 0x42 >> 1, NULL); + cam->sensor = v4l2_i2c_new_subdev_board(&cam->v4l2_dev, sensor_adapter, + &ov7670_info, NULL); if (cam->sensor == NULL) { dev_err(&pdev->dev, "Unable to find the sensor!\n"); ret = -ENODEV; diff --git a/drivers/media/video/videobuf-core.c b/drivers/media/video/videobuf-core.c index de4fa4eb8844..ffdf59cfe405 100644 --- a/drivers/media/video/videobuf-core.c +++ b/drivers/media/video/videobuf-core.c @@ -1129,6 +1129,7 @@ unsigned int videobuf_poll_stream(struct file *file, struct videobuf_queue *q, poll_table *wait) { + unsigned long req_events = poll_requested_events(wait); struct videobuf_buffer *buf = NULL; unsigned int rc = 0; @@ -1137,7 +1138,7 @@ unsigned int videobuf_poll_stream(struct file *file, if (!list_empty(&q->stream)) buf = list_entry(q->stream.next, struct videobuf_buffer, stream); - } else { + } else if (req_events & (POLLIN | POLLRDNORM)) { if (!q->reading) __videobuf_read_start(q); if (!q->reading) { diff --git a/drivers/media/video/videobuf-dvb.c b/drivers/media/video/videobuf-dvb.c index 59cb54aa2946..94d83a41381b 100644 --- a/drivers/media/video/videobuf-dvb.c +++ b/drivers/media/video/videobuf-dvb.c @@ -45,7 +45,6 @@ static int videobuf_dvb_thread(void *data) struct videobuf_dvb *dvb = data; struct videobuf_buffer *buf; unsigned long flags; - int err; void *outp; dprintk("dvb thread started\n"); @@ -57,7 +56,7 @@ static int videobuf_dvb_thread(void *data) buf = list_entry(dvb->dvbq.stream.next, struct videobuf_buffer, stream); list_del(&buf->stream); - err = videobuf_waiton(&dvb->dvbq, buf, 0, 1); + videobuf_waiton(&dvb->dvbq, buf, 0, 1); /* no more feeds left or stop_feed() asked us to quit */ if (0 == dvb->nfeeds) diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c index 2e8f1df775b6..9d4e9edbd2e7 100644 --- a/drivers/media/video/videobuf2-core.c +++ b/drivers/media/video/videobuf2-core.c @@ -19,6 +19,9 @@ #include <linux/slab.h> #include <linux/sched.h> +#include <media/v4l2-dev.h> +#include <media/v4l2-fh.h> +#include <media/v4l2-event.h> #include <media/videobuf2-core.h> static int debug; @@ -1642,32 +1645,46 @@ static int __vb2_cleanup_fileio(struct vb2_queue *q); * For OUTPUT queues, if a buffer is ready to be dequeued, the file descriptor * will be reported as available for writing. * + * If the driver uses struct v4l2_fh, then vb2_poll() will also check for any + * pending events. + * * The return values from this function are intended to be directly returned * from poll handler in driver. */ unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait) { - unsigned long flags; - unsigned int ret; + struct video_device *vfd = video_devdata(file); + unsigned long req_events = poll_requested_events(wait); struct vb2_buffer *vb = NULL; + unsigned int res = 0; + unsigned long flags; + + if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) { + struct v4l2_fh *fh = file->private_data; + + if (v4l2_event_pending(fh)) + res = POLLPRI; + else if (req_events & POLLPRI) + poll_wait(file, &fh->wait, wait); + } /* * Start file I/O emulator only if streaming API has not been used yet. */ if (q->num_buffers == 0 && q->fileio == NULL) { - if (!V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_READ)) { - ret = __vb2_init_fileio(q, 1); - if (ret) - return POLLERR; + if (!V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_READ) && + (req_events & (POLLIN | POLLRDNORM))) { + if (__vb2_init_fileio(q, 1)) + return res | POLLERR; } - if (V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_WRITE)) { - ret = __vb2_init_fileio(q, 0); - if (ret) - return POLLERR; + if (V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_WRITE) && + (req_events & (POLLOUT | POLLWRNORM))) { + if (__vb2_init_fileio(q, 0)) + return res | POLLERR; /* * Write to OUTPUT queue can be done immediately. */ - return POLLOUT | POLLWRNORM; + return res | POLLOUT | POLLWRNORM; } } @@ -1675,7 +1692,7 @@ unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait) * There is nothing to wait for if no buffers have already been queued. */ if (list_empty(&q->queued_list)) - return POLLERR; + return res | POLLERR; poll_wait(file, &q->done_wq, wait); @@ -1690,10 +1707,11 @@ unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait) if (vb && (vb->state == VB2_BUF_STATE_DONE || vb->state == VB2_BUF_STATE_ERROR)) { - return (V4L2_TYPE_IS_OUTPUT(q->type)) ? POLLOUT | POLLWRNORM : - POLLIN | POLLRDNORM; + return (V4L2_TYPE_IS_OUTPUT(q->type)) ? + res | POLLOUT | POLLWRNORM : + res | POLLIN | POLLRDNORM; } - return 0; + return res; } EXPORT_SYMBOL_GPL(vb2_poll); @@ -1839,7 +1857,6 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read) * (multiplane buffers are not supported). */ if (q->bufs[0]->num_planes != 1) { - fileio->req.count = 0; ret = -EBUSY; goto err_reqbufs; } @@ -1886,6 +1903,7 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read) return ret; err_reqbufs: + fileio->req.count = 0; vb2_reqbufs(q, &fileio->req); err_kfree: diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c index 5e8b0710105b..0960d7f0d394 100644 --- a/drivers/media/video/vivi.c +++ b/drivers/media/video/vivi.c @@ -95,6 +95,16 @@ static struct vivi_fmt formats[] = { .depth = 16, }, { + .name = "4:2:2, packed, YVYU", + .fourcc = V4L2_PIX_FMT_YVYU, + .depth = 16, + }, + { + .name = "4:2:2, packed, VYUY", + .fourcc = V4L2_PIX_FMT_VYUY, + .depth = 16, + }, + { .name = "RGB565 (LE)", .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */ .depth = 16, @@ -114,6 +124,26 @@ static struct vivi_fmt formats[] = { .fourcc = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */ .depth = 16, }, + { + .name = "RGB24 (LE)", + .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */ + .depth = 24, + }, + { + .name = "RGB24 (BE)", + .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */ + .depth = 24, + }, + { + .name = "RGB32 (LE)", + .fourcc = V4L2_PIX_FMT_RGB32, /* argb */ + .depth = 32, + }, + { + .name = "RGB32 (BE)", + .fourcc = V4L2_PIX_FMT_BGR32, /* bgra */ + .depth = 32, + }, }; static struct vivi_fmt *get_format(struct v4l2_format *f) @@ -170,6 +200,7 @@ struct vivi_dev { struct v4l2_ctrl *gain; }; struct v4l2_ctrl *volume; + struct v4l2_ctrl *alpha; struct v4l2_ctrl *button; struct v4l2_ctrl *boolean; struct v4l2_ctrl *int32; @@ -177,6 +208,7 @@ struct vivi_dev { struct v4l2_ctrl *menu; struct v4l2_ctrl *string; struct v4l2_ctrl *bitmask; + struct v4l2_ctrl *int_menu; spinlock_t slock; struct mutex mutex; @@ -203,8 +235,10 @@ struct vivi_dev { enum v4l2_field field; unsigned int field_count; - u8 bars[9][3]; - u8 line[MAX_WIDTH * 4]; + u8 bars[9][3]; + u8 line[MAX_WIDTH * 8]; + unsigned int pixelsize; + u8 alpha_component; }; /* ------------------------------------------------------------------ @@ -283,6 +317,8 @@ static void precalculate_bars(struct vivi_dev *dev) switch (dev->fmt->fourcc) { case V4L2_PIX_FMT_YUYV: case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_YVYU: + case V4L2_PIX_FMT_VYUY: is_yuv = 1; break; case V4L2_PIX_FMT_RGB565: @@ -297,6 +333,11 @@ static void precalculate_bars(struct vivi_dev *dev) g >>= 3; b >>= 3; break; + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + case V4L2_PIX_FMT_RGB32: + case V4L2_PIX_FMT_BGR32: + break; } if (is_yuv) { @@ -316,9 +357,11 @@ static void precalculate_bars(struct vivi_dev *dev) #define TSTAMP_INPUT_X 10 #define TSTAMP_MIN_X (54 + TSTAMP_INPUT_X) -static void gen_twopix(struct vivi_dev *dev, u8 *buf, int colorpos) +/* 'odd' is true for pixels 1, 3, 5, etc. and false for pixels 0, 2, 4, etc. */ +static void gen_twopix(struct vivi_dev *dev, u8 *buf, int colorpos, bool odd) { u8 r_y, g_u, b_v; + u8 alpha = dev->alpha_component; int color; u8 *p; @@ -326,46 +369,56 @@ static void gen_twopix(struct vivi_dev *dev, u8 *buf, int colorpos) g_u = dev->bars[colorpos][1]; /* G or precalculated U */ b_v = dev->bars[colorpos][2]; /* B or precalculated V */ - for (color = 0; color < 4; color++) { + for (color = 0; color < dev->pixelsize; color++) { p = buf + color; switch (dev->fmt->fourcc) { case V4L2_PIX_FMT_YUYV: switch (color) { case 0: - case 2: *p = r_y; break; case 1: - *p = g_u; - break; - case 3: - *p = b_v; + *p = odd ? b_v : g_u; break; } break; case V4L2_PIX_FMT_UYVY: switch (color) { + case 0: + *p = odd ? b_v : g_u; + break; case 1: - case 3: *p = r_y; break; + } + break; + case V4L2_PIX_FMT_YVYU: + switch (color) { case 0: - *p = g_u; + *p = r_y; break; - case 2: - *p = b_v; + case 1: + *p = odd ? g_u : b_v; + break; + } + break; + case V4L2_PIX_FMT_VYUY: + switch (color) { + case 0: + *p = odd ? g_u : b_v; + break; + case 1: + *p = r_y; break; } break; case V4L2_PIX_FMT_RGB565: switch (color) { case 0: - case 2: *p = (g_u << 5) | b_v; break; case 1: - case 3: *p = (r_y << 3) | (g_u >> 3); break; } @@ -373,11 +426,9 @@ static void gen_twopix(struct vivi_dev *dev, u8 *buf, int colorpos) case V4L2_PIX_FMT_RGB565X: switch (color) { case 0: - case 2: *p = (r_y << 3) | (g_u >> 3); break; case 1: - case 3: *p = (g_u << 5) | b_v; break; } @@ -385,24 +436,78 @@ static void gen_twopix(struct vivi_dev *dev, u8 *buf, int colorpos) case V4L2_PIX_FMT_RGB555: switch (color) { case 0: - case 2: *p = (g_u << 5) | b_v; break; case 1: - case 3: - *p = (r_y << 2) | (g_u >> 3); + *p = (alpha & 0x80) | (r_y << 2) | (g_u >> 3); break; } break; case V4L2_PIX_FMT_RGB555X: switch (color) { case 0: + *p = (alpha & 0x80) | (r_y << 2) | (g_u >> 3); + break; + case 1: + *p = (g_u << 5) | b_v; + break; + } + break; + case V4L2_PIX_FMT_RGB24: + switch (color) { + case 0: + *p = r_y; + break; + case 1: + *p = g_u; + break; case 2: - *p = (r_y << 2) | (g_u >> 3); + *p = b_v; + break; + } + break; + case V4L2_PIX_FMT_BGR24: + switch (color) { + case 0: + *p = b_v; break; case 1: + *p = g_u; + break; + case 2: + *p = r_y; + break; + } + break; + case V4L2_PIX_FMT_RGB32: + switch (color) { + case 0: + *p = alpha; + break; + case 1: + *p = r_y; + break; + case 2: + *p = g_u; + break; case 3: - *p = (g_u << 5) | b_v; + *p = b_v; + break; + } + break; + case V4L2_PIX_FMT_BGR32: + switch (color) { + case 0: + *p = b_v; + break; + case 1: + *p = g_u; + break; + case 2: + *p = r_y; + break; + case 3: + *p = alpha; break; } break; @@ -414,10 +519,10 @@ static void precalculate_line(struct vivi_dev *dev) { int w; - for (w = 0; w < dev->width * 2; w += 2) { - int colorpos = (w / (dev->width / 8) % 8); + for (w = 0; w < dev->width * 2; w++) { + int colorpos = w / (dev->width / 8) % 8; - gen_twopix(dev, dev->line + w * 2, colorpos); + gen_twopix(dev, dev->line + w * dev->pixelsize, colorpos, w & 1); } } @@ -433,7 +538,7 @@ static void gen_text(struct vivi_dev *dev, char *basep, /* Print stream time */ for (line = y; line < y + 16; line++) { int j = 0; - char *pos = basep + line * dev->width * 2 + x * 2; + char *pos = basep + line * dev->width * dev->pixelsize + x * dev->pixelsize; char *s; for (s = text; *s; s++) { @@ -443,9 +548,9 @@ static void gen_text(struct vivi_dev *dev, char *basep, for (i = 0; i < 7; i++, j++) { /* Draw white font on black background */ if (chr & (1 << (7 - i))) - gen_twopix(dev, pos + j * 2, WHITE); + gen_twopix(dev, pos + j * dev->pixelsize, WHITE, (x+y) & 1); else - gen_twopix(dev, pos + j * 2, TEXT_BLACK); + gen_twopix(dev, pos + j * dev->pixelsize, TEXT_BLACK, (x+y) & 1); } } } @@ -466,7 +571,9 @@ static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf) return; for (h = 0; h < hmax; h++) - memcpy(vbuf + h * wmax * 2, dev->line + (dev->mv_count % wmax) * 2, wmax * 2); + memcpy(vbuf + h * wmax * dev->pixelsize, + dev->line + (dev->mv_count % wmax) * dev->pixelsize, + wmax * dev->pixelsize); /* Updates stream time */ @@ -484,15 +591,16 @@ static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf) gen_text(dev, vbuf, line++ * 16, 16, str); gain = v4l2_ctrl_g_ctrl(dev->gain); - mutex_lock(&dev->ctrl_handler.lock); + mutex_lock(dev->ctrl_handler.lock); snprintf(str, sizeof(str), " brightness %3d, contrast %3d, saturation %3d, hue %d ", dev->brightness->cur.val, dev->contrast->cur.val, dev->saturation->cur.val, dev->hue->cur.val); gen_text(dev, vbuf, line++ * 16, 16, str); - snprintf(str, sizeof(str), " autogain %d, gain %3d, volume %3d ", - dev->autogain->cur.val, gain, dev->volume->cur.val); + snprintf(str, sizeof(str), " autogain %d, gain %3d, volume %3d, alpha 0x%02x ", + dev->autogain->cur.val, gain, dev->volume->cur.val, + dev->alpha->cur.val); gen_text(dev, vbuf, line++ * 16, 16, str); snprintf(str, sizeof(str), " int32 %d, int64 %lld, bitmask %08x ", dev->int32->cur.val, @@ -503,8 +611,12 @@ static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf) dev->boolean->cur.val, dev->menu->qmenu[dev->menu->cur.val], dev->string->cur.string); - mutex_unlock(&dev->ctrl_handler.lock); gen_text(dev, vbuf, line++ * 16, 16, str); + snprintf(str, sizeof(str), " integer_menu %lld, value %d ", + dev->int_menu->qmenu_int[dev->int_menu->cur.val], + dev->int_menu->cur.val); + gen_text(dev, vbuf, line++ * 16, 16, str); + mutex_unlock(dev->ctrl_handler.lock); if (dev->button_pressed) { dev->button_pressed--; snprintf(str, sizeof(str), " button pressed!"); @@ -657,7 +769,7 @@ static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, struct vivi_dev *dev = vb2_get_drv_priv(vq); unsigned long size; - size = dev->width * dev->height * 2; + size = dev->width * dev->height * dev->pixelsize; if (0 == *nbuffers) *nbuffers = 32; @@ -721,7 +833,7 @@ static int buffer_prepare(struct vb2_buffer *vb) dev->height < 32 || dev->height > MAX_HEIGHT) return -EINVAL; - size = dev->width * dev->height * 2; + size = dev->width * dev->height * dev->pixelsize; if (vb2_plane_size(vb, 0) < size) { dprintk(dev, 1, "%s data will not fit into plane (%lu < %lu)\n", __func__, vb2_plane_size(vb, 0), size); @@ -915,6 +1027,7 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, } dev->fmt = get_format(f); + dev->pixelsize = dev->fmt->depth / 8; dev->width = f->fmt.pix.width; dev->height = f->fmt.pix.height; dev->field = f->fmt.pix.field; @@ -1016,8 +1129,15 @@ static int vivi_s_ctrl(struct v4l2_ctrl *ctrl) { struct vivi_dev *dev = container_of(ctrl->handler, struct vivi_dev, ctrl_handler); - if (ctrl == dev->button) - dev->button_pressed = 30; + switch (ctrl->id) { + case V4L2_CID_ALPHA_COMPONENT: + dev->alpha_component = ctrl->val; + break; + default: + if (ctrl == dev->button) + dev->button_pressed = 30; + break; + } return 0; } @@ -1039,17 +1159,10 @@ static unsigned int vivi_poll(struct file *file, struct poll_table_struct *wait) { struct vivi_dev *dev = video_drvdata(file); - struct v4l2_fh *fh = file->private_data; struct vb2_queue *q = &dev->vb_vidq; - unsigned int res; dprintk(dev, 1, "%s\n", __func__); - res = vb2_poll(q, file, wait); - if (v4l2_event_pending(fh)) - res |= POLLPRI; - else - poll_wait(file, &fh->wait, wait); - return res; + return vb2_poll(q, file, wait); } static int vivi_close(struct file *file) @@ -1165,6 +1278,22 @@ static const struct v4l2_ctrl_config vivi_ctrl_bitmask = { .step = 0, }; +static const s64 vivi_ctrl_int_menu_values[] = { + 1, 1, 2, 3, 5, 8, 13, 21, 42, +}; + +static const struct v4l2_ctrl_config vivi_ctrl_int_menu = { + .ops = &vivi_ctrl_ops, + .id = VIVI_CID_CUSTOM_BASE + 7, + .name = "Integer menu", + .type = V4L2_CTRL_TYPE_INTEGER_MENU, + .min = 1, + .max = 8, + .def = 4, + .menu_skip_mask = 0x02, + .qmenu_int = vivi_ctrl_int_menu_values, +}; + static const struct v4l2_file_operations vivi_fops = { .owner = THIS_MODULE, .open = v4l2_fh_open, @@ -1252,6 +1381,7 @@ static int __init vivi_create_instance(int inst) dev->fmt = &formats[0]; dev->width = 640; dev->height = 480; + dev->pixelsize = dev->fmt->depth / 8; hdl = &dev->ctrl_handler; v4l2_ctrl_handler_init(hdl, 11); dev->volume = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops, @@ -1268,6 +1398,8 @@ static int __init vivi_create_instance(int inst) V4L2_CID_AUTOGAIN, 0, 1, 1, 1); dev->gain = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops, V4L2_CID_GAIN, 0, 255, 1, 100); + dev->alpha = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops, + V4L2_CID_ALPHA_COMPONENT, 0, 255, 1, 0); dev->button = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_button, NULL); dev->int32 = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_int32, NULL); dev->int64 = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_int64, NULL); @@ -1275,6 +1407,7 @@ static int __init vivi_create_instance(int inst) dev->menu = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_menu, NULL); dev->string = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_string, NULL); dev->bitmask = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_bitmask, NULL); + dev->int_menu = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_int_menu, NULL); if (hdl->error) { ret = hdl->error; goto unreg_dev; diff --git a/drivers/media/video/zoran/zoran_device.c b/drivers/media/video/zoran/zoran_device.c index e86173bd1327..a4cd504b8eee 100644 --- a/drivers/media/video/zoran/zoran_device.c +++ b/drivers/media/video/zoran/zoran_device.c @@ -542,11 +542,9 @@ void write_overlay_mask(struct zoran_fh *fh, struct v4l2_clip *vp, int count) u32 *mask; int x, y, width, height; unsigned i, j, k; - u32 reg; /* fill mask with one bits */ memset(fh->overlay_mask, ~0, mask_line_size * 4 * BUZ_MAX_HEIGHT); - reg = 0; for (i = 0; i < count; ++i) { /* pick up local copy of clip */ diff --git a/drivers/media/video/zoran/zoran_driver.c b/drivers/media/video/zoran/zoran_driver.c index 4c09ab781ec3..c57310931810 100644 --- a/drivers/media/video/zoran/zoran_driver.c +++ b/drivers/media/video/zoran/zoran_driver.c @@ -1131,8 +1131,14 @@ static int setup_fbuffer(struct zoran_fh *fh, } -static int setup_window(struct zoran_fh *fh, int x, int y, int width, int height, - struct v4l2_clip __user *clips, int clipcount, void __user *bitmap) +static int setup_window(struct zoran_fh *fh, + int x, + int y, + int width, + int height, + struct v4l2_clip __user *clips, + unsigned int clipcount, + void __user *bitmap) { struct zoran *zr = fh->zr; struct v4l2_clip *vcp = NULL; @@ -1155,6 +1161,14 @@ static int setup_window(struct zoran_fh *fh, int x, int y, int width, int height return -EINVAL; } + if (clipcount > 2048) { + dprintk(1, + KERN_ERR + "%s: %s - invalid clipcount\n", + ZR_DEVNAME(zr), __func__); + return -EINVAL; + } + /* * The video front end needs 4-byte alinged line sizes, we correct that * silently here if necessary @@ -1218,7 +1232,7 @@ static int setup_window(struct zoran_fh *fh, int x, int y, int width, int height (width * height + 7) / 8)) { return -EFAULT; } - } else if (clipcount > 0) { + } else if (clipcount) { /* write our own bitmap from the clips */ vcp = vmalloc(sizeof(struct v4l2_clip) * (clipcount + 4)); if (vcp == NULL) { diff --git a/drivers/media/video/zr364xx.c b/drivers/media/video/zr364xx.c index cd2e39fc4bf0..e44cb330bbc8 100644 --- a/drivers/media/video/zr364xx.c +++ b/drivers/media/video/zr364xx.c @@ -507,14 +507,12 @@ static void zr364xx_fillbuff(struct zr364xx_camera *cam, const char *tmpbuf; char *vbuf = videobuf_to_vmalloc(&buf->vb); unsigned long last_frame; - struct zr364xx_framei *frm; if (!vbuf) return; last_frame = cam->last_frame; if (last_frame != -1) { - frm = &cam->buffer.frame[last_frame]; tmpbuf = (const char *)cam->buffer.frame[last_frame].lpvbits; switch (buf->fmt->fourcc) { case V4L2_PIX_FMT_JPEG: |