From ea3aba8482be6f3e4815f4014fa7302fc77c9c3f Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Tue, 21 May 2013 05:11:35 -0300 Subject: [media] videobuf2: Add support for file access mode flags for DMABUF exporting Currently it is not possible for userspace to map a DMABUF exported buffer with write permissions. This patch allows to also pass O_RDONLY/O_RDWR when exporting the buffer, so that userspace may map it with write permissions. Signed-off-by: Philipp Zabel Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/vidioc-expbuf.xml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'Documentation') diff --git a/Documentation/DocBook/media/v4l/vidioc-expbuf.xml b/Documentation/DocBook/media/v4l/vidioc-expbuf.xml index e287c8fc803b..4165e7bfa4ff 100644 --- a/Documentation/DocBook/media/v4l/vidioc-expbuf.xml +++ b/Documentation/DocBook/media/v4l/vidioc-expbuf.xml @@ -73,7 +73,8 @@ range from zero to the maximal number of valid planes for the currently active format. For the single-planar API, applications must set plane to zero. Additional flags may be posted in the flags field. Refer to a manual for open() for details. -Currently only O_CLOEXEC is supported. All other fields must be set to zero. +Currently only O_CLOEXEC, O_RDONLY, O_WRONLY, and O_RDWR are supported. All +other fields must be set to zero. In the case of multi-planar API, every plane is exported separately using multiple VIDIOC_EXPBUF calls. @@ -170,8 +171,9 @@ multi-planar API. Otherwise this value must be set to zero. __u32 flags Flags for the newly created file, currently only -O_CLOEXEC is supported, refer to the manual of open() for more -details. +O_CLOEXEC , O_RDONLY, O_WRONLY +, and O_RDWR are supported, refer to the manual +of open() for more details. __s32 -- cgit v1.2.3 From 59f0ad8076816d13f7cba80d2b178ff5ab787e2e Mon Sep 17 00:00:00 2001 From: Sergio Aguirre Date: Mon, 24 Jan 2011 15:48:19 -0300 Subject: [media] v4l: omap4iss: Add support for OMAP4 camera interface - Core This adds a very simplistic driver to utilize the CSI2A interface inside the ISS subsystem in OMAP4, and dump the data to memory. Check Documentation/video4linux/omap4_camera.txt for details. This commit adds the driver core, registers definitions and documentation. Signed-off-by: Sergio Aguirre Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/omap4_camera.txt | 60 ++ drivers/staging/media/omap4iss/iss.c | 1477 ++++++++++++++++++++++++++++ drivers/staging/media/omap4iss/iss.h | 153 +++ drivers/staging/media/omap4iss/iss_regs.h | 883 +++++++++++++++++ include/media/omap4iss.h | 65 ++ 5 files changed, 2638 insertions(+) create mode 100644 Documentation/video4linux/omap4_camera.txt create mode 100644 drivers/staging/media/omap4iss/iss.c create mode 100644 drivers/staging/media/omap4iss/iss.h create mode 100644 drivers/staging/media/omap4iss/iss_regs.h create mode 100644 include/media/omap4iss.h (limited to 'Documentation') diff --git a/Documentation/video4linux/omap4_camera.txt b/Documentation/video4linux/omap4_camera.txt new file mode 100644 index 000000000000..25d9b40a4651 --- /dev/null +++ b/Documentation/video4linux/omap4_camera.txt @@ -0,0 +1,60 @@ + OMAP4 ISS Driver + ================ + +Introduction +------------ + +The OMAP44XX family of chips contains the Imaging SubSystem (a.k.a. ISS), +Which contains several components that can be categorized in 3 big groups: + +- Interfaces (2 Interfaces: CSI2-A & CSI2-B/CCP2) +- ISP (Image Signal Processor) +- SIMCOP (Still Image Coprocessor) + +For more information, please look in [1] for latest version of: + "OMAP4430 Multimedia Device Silicon Revision 2.x" + +As of Revision AB, the ISS is described in detail in section 8. + +This driver is supporting _only_ the CSI2-A/B interfaces for now. + +It makes use of the Media Controller framework [2], and inherited most of the +code from OMAP3 ISP driver (found under drivers/media/platform/omap3isp/*), +except that it doesn't need an IOMMU now for ISS buffers memory mapping. + +Supports usage of MMAP buffers only (for now). + +Tested platforms +---------------- + +- OMAP4430SDP, w/ ES2.1 GP & SEVM4430-CAM-V1-0 (Contains IMX060 & OV5640, in + which only the last one is supported, outputting YUV422 frames). + +- TI Blaze MDP, w/ OMAP4430 ES2.2 EMU (Contains 1 IMX060 & 2 OV5650 sensors, in + which only the OV5650 are supported, outputting RAW10 frames). + +- PandaBoard, Rev. A2, w/ OMAP4430 ES2.1 GP & OV adapter board, tested with + following sensors: + * OV5640 + * OV5650 + +- Tested on mainline kernel: + + http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=summary + + Tag: v3.3 (commit c16fa4f2ad19908a47c63d8fa436a1178438c7e7) + +File list +--------- +drivers/staging/media/omap4iss/ +include/media/omap4iss.h + +References +---------- + +[1] http://focus.ti.com/general/docs/wtbu/wtbudocumentcenter.tsp?navigationId=12037&templateId=6123#62 +[2] http://lwn.net/Articles/420485/ +[3] http://www.spinics.net/lists/linux-media/msg44370.html +-- +Author: Sergio Aguirre +Copyright (C) 2012, Texas Instruments diff --git a/drivers/staging/media/omap4iss/iss.c b/drivers/staging/media/omap4iss/iss.c new file mode 100644 index 000000000000..d054d9b6eb0f --- /dev/null +++ b/drivers/staging/media/omap4iss/iss.c @@ -0,0 +1,1477 @@ +/* + * TI OMAP4 ISS V4L2 Driver + * + * Copyright (C) 2012, Texas Instruments + * + * Author: Sergio Aguirre + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "iss.h" +#include "iss_regs.h" + +#define ISS_PRINT_REGISTER(iss, name)\ + dev_dbg(iss->dev, "###ISS " #name "=0x%08x\n", \ + readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_##name)) + +static void iss_print_status(struct iss_device *iss) +{ + dev_dbg(iss->dev, "-------------ISS HL Register dump-------------\n"); + + ISS_PRINT_REGISTER(iss, HL_REVISION); + ISS_PRINT_REGISTER(iss, HL_SYSCONFIG); + ISS_PRINT_REGISTER(iss, HL_IRQSTATUS_5); + ISS_PRINT_REGISTER(iss, HL_IRQENABLE_5_SET); + ISS_PRINT_REGISTER(iss, HL_IRQENABLE_5_CLR); + ISS_PRINT_REGISTER(iss, CTRL); + ISS_PRINT_REGISTER(iss, CLKCTRL); + ISS_PRINT_REGISTER(iss, CLKSTAT); + + dev_dbg(iss->dev, "-----------------------------------------------\n"); +} + +/* + * omap4iss_flush - Post pending L3 bus writes by doing a register readback + * @iss: OMAP4 ISS device + * + * In order to force posting of pending writes, we need to write and + * readback the same register, in this case the revision register. + * + * See this link for reference: + * http://www.mail-archive.com/linux-omap@vger.kernel.org/msg08149.html + */ +void omap4iss_flush(struct iss_device *iss) +{ + writel(0, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_REVISION); + readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_REVISION); +} + +/* + * iss_enable_interrupts - Enable ISS interrupts. + * @iss: OMAP4 ISS device + */ +static void iss_enable_interrupts(struct iss_device *iss) +{ + static const u32 hl_irq = ISS_HL_IRQ_CSIA | ISS_HL_IRQ_CSIB | ISS_HL_IRQ_ISP(0); + + /* Enable HL interrupts */ + writel(hl_irq, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQSTATUS_5); + writel(hl_irq, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQENABLE_5_SET); + +} + +/* + * iss_disable_interrupts - Disable ISS interrupts. + * @iss: OMAP4 ISS device + */ +static void iss_disable_interrupts(struct iss_device *iss) +{ + writel(-1, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQENABLE_5_CLR); +} + +/* + * iss_isp_enable_interrupts - Enable ISS ISP interrupts. + * @iss: OMAP4 ISS device + */ +void omap4iss_isp_enable_interrupts(struct iss_device *iss) +{ + static const u32 isp_irq = ISP5_IRQ_OCP_ERR | + ISP5_IRQ_RSZ_FIFO_IN_BLK | + ISP5_IRQ_RSZ_FIFO_OVF | + ISP5_IRQ_RSZ_INT_DMA | + ISP5_IRQ_ISIF0; + + /* Enable ISP interrupts */ + writel(isp_irq, iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_IRQSTATUS(0)); + writel(isp_irq, iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_IRQENABLE_SET(0)); +} + +/* + * iss_isp_disable_interrupts - Disable ISS interrupts. + * @iss: OMAP4 ISS device + */ +void omap4iss_isp_disable_interrupts(struct iss_device *iss) +{ + writel(-1, iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_IRQENABLE_CLR(0)); +} + +int omap4iss_get_external_info(struct iss_pipeline *pipe, + struct media_link *link) +{ + struct iss_device *iss = + container_of(pipe, struct iss_video, pipe)->iss; + struct v4l2_subdev_format fmt; + struct v4l2_ext_controls ctrls; + struct v4l2_ext_control ctrl; + int ret; + + if (!pipe->external) + return 0; + + if (pipe->external_rate) + return 0; + + memset(&fmt, 0, sizeof(fmt)); + + fmt.pad = link->source->index; + fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + ret = v4l2_subdev_call(media_entity_to_v4l2_subdev(link->sink->entity), + pad, get_fmt, NULL, &fmt); + if (ret < 0) + return -EPIPE; + + pipe->external_bpp = omap4iss_video_format_info(fmt.format.code)->bpp; + + memset(&ctrls, 0, sizeof(ctrls)); + memset(&ctrl, 0, sizeof(ctrl)); + + ctrl.id = V4L2_CID_PIXEL_RATE; + + ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(ctrl.id); + ctrls.count = 1; + ctrls.controls = &ctrl; + + ret = v4l2_g_ext_ctrls(pipe->external->ctrl_handler, &ctrls); + if (ret < 0) { + dev_warn(iss->dev, "no pixel rate control in subdev %s\n", + pipe->external->name); + return ret; + } + + pipe->external_rate = ctrl.value64; + + return 0; +} + +/* + * Configure the bridge. Valid inputs are + * + * IPIPEIF_INPUT_CSI2A: CSI2a receiver + * IPIPEIF_INPUT_CSI2B: CSI2b receiver + * + * The bridge and lane shifter are configured according to the selected input + * and the ISP platform data. + */ +void omap4iss_configure_bridge(struct iss_device *iss, + enum ipipeif_input_entity input) +{ + u32 issctrl_val; + u32 isp5ctrl_val; + + issctrl_val = readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_CTRL); + issctrl_val &= ~ISS_CTRL_INPUT_SEL_MASK; + issctrl_val &= ~ISS_CTRL_CLK_DIV_MASK; + + isp5ctrl_val = readl(iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_CTRL); + + switch (input) { + case IPIPEIF_INPUT_CSI2A: + issctrl_val |= ISS_CTRL_INPUT_SEL_CSI2A; + isp5ctrl_val |= ISP5_CTRL_VD_PULSE_EXT; + break; + + case IPIPEIF_INPUT_CSI2B: + issctrl_val |= ISS_CTRL_INPUT_SEL_CSI2B; + isp5ctrl_val |= ISP5_CTRL_VD_PULSE_EXT; + break; + + default: + return; + } + + issctrl_val |= ISS_CTRL_SYNC_DETECT_VS_RAISING; + + isp5ctrl_val |= ISP5_CTRL_PSYNC_CLK_SEL | ISP5_CTRL_SYNC_ENABLE; + + writel(issctrl_val, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_CTRL); + writel(isp5ctrl_val, iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_CTRL); +} + +static inline void iss_isr_dbg(struct iss_device *iss, u32 irqstatus) +{ + static const char *name[] = { + "ISP_IRQ0", + "ISP_IRQ1", + "ISP_IRQ2", + "ISP_IRQ3", + "CSIA_IRQ", + "CSIB_IRQ", + "CCP2_IRQ0", + "CCP2_IRQ1", + "CCP2_IRQ2", + "CCP2_IRQ3", + "CBUFF_IRQ", + "BTE_IRQ", + "SIMCOP_IRQ0", + "SIMCOP_IRQ1", + "SIMCOP_IRQ2", + "SIMCOP_IRQ3", + "CCP2_IRQ8", + "HS_VS_IRQ", + "res18", + "res19", + "res20", + "res21", + "res22", + "res23", + "res24", + "res25", + "res26", + "res27", + "res28", + "res29", + "res30", + "res31", + }; + int i; + + dev_dbg(iss->dev, "ISS IRQ: "); + + for (i = 0; i < ARRAY_SIZE(name); i++) { + if ((1 << i) & irqstatus) + pr_cont("%s ", name[i]); + } + pr_cont("\n"); +} + +/* + * iss_isr - Interrupt Service Routine for ISS module. + * @irq: Not used currently. + * @_iss: Pointer to the OMAP4 ISS device + * + * Handles the corresponding callback if plugged in. + * + * Returns IRQ_HANDLED when IRQ was correctly handled, or IRQ_NONE when the + * IRQ wasn't handled. + */ +static irqreturn_t iss_isr(int irq, void *_iss) +{ + static const u32 ipipeif_events = ISP5_IRQ_IPIPEIF | + ISP5_IRQ_ISIF0; + static const u32 resizer_events = ISP5_IRQ_RSZ_FIFO_IN_BLK | + ISP5_IRQ_RSZ_FIFO_OVF | + ISP5_IRQ_RSZ_INT_DMA; + struct iss_device *iss = _iss; + u32 irqstatus; + + irqstatus = readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQSTATUS_5); + writel(irqstatus, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQSTATUS_5); + + if (irqstatus & ISS_HL_IRQ_CSIA) + omap4iss_csi2_isr(&iss->csi2a); + + if (irqstatus & ISS_HL_IRQ_CSIB) + omap4iss_csi2_isr(&iss->csi2b); + + if (irqstatus & ISS_HL_IRQ_ISP(0)) { + u32 isp_irqstatus = readl(iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + + ISP5_IRQSTATUS(0)); + writel(isp_irqstatus, iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + + ISP5_IRQSTATUS(0)); + + if (isp_irqstatus & ISP5_IRQ_OCP_ERR) + dev_dbg(iss->dev, "ISP5 OCP Error!\n"); + + if (isp_irqstatus & ipipeif_events) { + omap4iss_ipipeif_isr(&iss->ipipeif, + isp_irqstatus & ipipeif_events); + } + + if (isp_irqstatus & resizer_events) + omap4iss_resizer_isr(&iss->resizer, + isp_irqstatus & resizer_events); + } + + omap4iss_flush(iss); + +#if defined(DEBUG) && defined(ISS_ISR_DEBUG) + iss_isr_dbg(iss, irqstatus); +#endif + + return IRQ_HANDLED; +} + +/* ----------------------------------------------------------------------------- + * Pipeline power management + * + * Entities must be powered up when part of a pipeline that contains at least + * one open video device node. + * + * To achieve this use the entity use_count field to track the number of users. + * For entities corresponding to video device nodes the use_count field stores + * the users count of the node. For entities corresponding to subdevs the + * use_count field stores the total number of users of all video device nodes + * in the pipeline. + * + * The omap4iss_pipeline_pm_use() function must be called in the open() and + * close() handlers of video device nodes. It increments or decrements the use + * count of all subdev entities in the pipeline. + * + * To react to link management on powered pipelines, the link setup notification + * callback updates the use count of all entities in the source and sink sides + * of the link. + */ + +/* + * iss_pipeline_pm_use_count - Count the number of users of a pipeline + * @entity: The entity + * + * Return the total number of users of all video device nodes in the pipeline. + */ +static int iss_pipeline_pm_use_count(struct media_entity *entity) +{ + struct media_entity_graph graph; + int use = 0; + + media_entity_graph_walk_start(&graph, entity); + + while ((entity = media_entity_graph_walk_next(&graph))) { + if (media_entity_type(entity) == MEDIA_ENT_T_DEVNODE) + use += entity->use_count; + } + + return use; +} + +/* + * iss_pipeline_pm_power_one - Apply power change to an entity + * @entity: The entity + * @change: Use count change + * + * Change the entity use count by @change. If the entity is a subdev update its + * power state by calling the core::s_power operation when the use count goes + * from 0 to != 0 or from != 0 to 0. + * + * Return 0 on success or a negative error code on failure. + */ +static int iss_pipeline_pm_power_one(struct media_entity *entity, int change) +{ + struct v4l2_subdev *subdev; + + subdev = media_entity_type(entity) == MEDIA_ENT_T_V4L2_SUBDEV + ? media_entity_to_v4l2_subdev(entity) : NULL; + + if (entity->use_count == 0 && change > 0 && subdev != NULL) { + int ret; + + ret = v4l2_subdev_call(subdev, core, s_power, 1); + if (ret < 0 && ret != -ENOIOCTLCMD) + return ret; + } + + entity->use_count += change; + WARN_ON(entity->use_count < 0); + + if (entity->use_count == 0 && change < 0 && subdev != NULL) + v4l2_subdev_call(subdev, core, s_power, 0); + + return 0; +} + +/* + * iss_pipeline_pm_power - Apply power change to all entities in a pipeline + * @entity: The entity + * @change: Use count change + * + * Walk the pipeline to update the use count and the power state of all non-node + * entities. + * + * Return 0 on success or a negative error code on failure. + */ +static int iss_pipeline_pm_power(struct media_entity *entity, int change) +{ + struct media_entity_graph graph; + struct media_entity *first = entity; + int ret = 0; + + if (!change) + return 0; + + media_entity_graph_walk_start(&graph, entity); + + while (!ret && (entity = media_entity_graph_walk_next(&graph))) + if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE) + ret = iss_pipeline_pm_power_one(entity, change); + + if (!ret) + return 0; + + media_entity_graph_walk_start(&graph, first); + + while ((first = media_entity_graph_walk_next(&graph)) + && first != entity) + if (media_entity_type(first) != MEDIA_ENT_T_DEVNODE) + iss_pipeline_pm_power_one(first, -change); + + return ret; +} + +/* + * omap4iss_pipeline_pm_use - Update the use count of an entity + * @entity: The entity + * @use: Use (1) or stop using (0) the entity + * + * Update the use count of all entities in the pipeline and power entities on or + * off accordingly. + * + * Return 0 on success or a negative error code on failure. Powering entities + * off is assumed to never fail. No failure can occur when the use parameter is + * set to 0. + */ +int omap4iss_pipeline_pm_use(struct media_entity *entity, int use) +{ + int change = use ? 1 : -1; + int ret; + + mutex_lock(&entity->parent->graph_mutex); + + /* Apply use count to node. */ + entity->use_count += change; + WARN_ON(entity->use_count < 0); + + /* Apply power change to connected non-nodes. */ + ret = iss_pipeline_pm_power(entity, change); + if (ret < 0) + entity->use_count -= change; + + mutex_unlock(&entity->parent->graph_mutex); + + return ret; +} + +/* + * iss_pipeline_link_notify - Link management notification callback + * @link: The link + * @flags: New link flags that will be applied + * + * React to link management on powered pipelines by updating the use count of + * all entities in the source and sink sides of the link. Entities are powered + * on or off accordingly. + * + * Return 0 on success or a negative error code on failure. Powering entities + * off is assumed to never fail. This function will not fail for disconnection + * events. + */ +static int iss_pipeline_link_notify(struct media_link *link, u32 flags, + unsigned int notification) +{ + struct media_entity *source = link->source->entity; + struct media_entity *sink = link->sink->entity; + int source_use = iss_pipeline_pm_use_count(source); + int sink_use = iss_pipeline_pm_use_count(sink); + int ret; + + if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH && + !(link->flags & MEDIA_LNK_FL_ENABLED)) { + /* Powering off entities is assumed to never fail. */ + iss_pipeline_pm_power(source, -sink_use); + iss_pipeline_pm_power(sink, -source_use); + return 0; + } + + if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH && + (flags & MEDIA_LNK_FL_ENABLED)) { + ret = iss_pipeline_pm_power(source, sink_use); + if (ret < 0) + return ret; + + ret = iss_pipeline_pm_power(sink, source_use); + if (ret < 0) + iss_pipeline_pm_power(source, -sink_use); + + return ret; + } + + return 0; +} + +/* ----------------------------------------------------------------------------- + * Pipeline stream management + */ + +/* + * iss_pipeline_enable - Enable streaming on a pipeline + * @pipe: ISS pipeline + * @mode: Stream mode (single shot or continuous) + * + * Walk the entities chain starting at the pipeline output video node and start + * all modules in the chain in the given mode. + * + * Return 0 if successful, or the return value of the failed video::s_stream + * operation otherwise. + */ +static int iss_pipeline_enable(struct iss_pipeline *pipe, + enum iss_pipeline_stream_state mode) +{ + struct media_entity *entity; + struct media_pad *pad; + struct v4l2_subdev *subdev; + unsigned long flags; + int ret; + + spin_lock_irqsave(&pipe->lock, flags); + pipe->state &= ~(ISS_PIPELINE_IDLE_INPUT | ISS_PIPELINE_IDLE_OUTPUT); + spin_unlock_irqrestore(&pipe->lock, flags); + + pipe->do_propagation = false; + + entity = &pipe->output->video.entity; + while (1) { + pad = &entity->pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + break; + + pad = media_entity_remote_pad(pad); + if (pad == NULL || + media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + break; + + entity = pad->entity; + subdev = media_entity_to_v4l2_subdev(entity); + + ret = v4l2_subdev_call(subdev, video, s_stream, mode); + if (ret < 0 && ret != -ENOIOCTLCMD) + return ret; + } + iss_print_status(pipe->output->iss); + return 0; +} + +/* + * iss_pipeline_disable - Disable streaming on a pipeline + * @pipe: ISS pipeline + * + * Walk the entities chain starting at the pipeline output video node and stop + * all modules in the chain. Wait synchronously for the modules to be stopped if + * necessary. + */ +static int iss_pipeline_disable(struct iss_pipeline *pipe) +{ + struct media_entity *entity; + struct media_pad *pad; + struct v4l2_subdev *subdev; + int failure = 0; + + entity = &pipe->output->video.entity; + while (1) { + pad = &entity->pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + break; + + pad = media_entity_remote_pad(pad); + if (pad == NULL || + media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + break; + + entity = pad->entity; + subdev = media_entity_to_v4l2_subdev(entity); + + v4l2_subdev_call(subdev, video, s_stream, 0); + } + + return failure; +} + +/* + * omap4iss_pipeline_set_stream - Enable/disable streaming on a pipeline + * @pipe: ISS pipeline + * @state: Stream state (stopped, single shot or continuous) + * + * Set the pipeline to the given stream state. Pipelines can be started in + * single-shot or continuous mode. + * + * Return 0 if successful, or the return value of the failed video::s_stream + * operation otherwise. The pipeline state is not updated when the operation + * fails, except when stopping the pipeline. + */ +int omap4iss_pipeline_set_stream(struct iss_pipeline *pipe, + enum iss_pipeline_stream_state state) +{ + int ret; + + if (state == ISS_PIPELINE_STREAM_STOPPED) + ret = iss_pipeline_disable(pipe); + else + ret = iss_pipeline_enable(pipe, state); + + if (ret == 0 || state == ISS_PIPELINE_STREAM_STOPPED) + pipe->stream_state = state; + + return ret; +} + +/* + * iss_pipeline_is_last - Verify if entity has an enabled link to the output + * video node + * @me: ISS module's media entity + * + * Returns 1 if the entity has an enabled link to the output video node or 0 + * otherwise. It's true only while pipeline can have no more than one output + * node. + */ +static int iss_pipeline_is_last(struct media_entity *me) +{ + struct iss_pipeline *pipe; + struct media_pad *pad; + + if (!me->pipe) + return 0; + pipe = to_iss_pipeline(me); + if (pipe->stream_state == ISS_PIPELINE_STREAM_STOPPED) + return 0; + pad = media_entity_remote_pad(&pipe->output->pad); + return pad->entity == me; +} + +static int iss_reset(struct iss_device *iss) +{ + unsigned long timeout = 0; + + writel(readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_SYSCONFIG) | + ISS_HL_SYSCONFIG_SOFTRESET, + iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_SYSCONFIG); + + while (readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_SYSCONFIG) & + ISS_HL_SYSCONFIG_SOFTRESET) { + if (timeout++ > 10000) { + dev_alert(iss->dev, "cannot reset ISS\n"); + return -ETIMEDOUT; + } + udelay(1); + } + + return 0; +} + +static int iss_isp_reset(struct iss_device *iss) +{ + unsigned long timeout = 0; + + /* Fist, ensure that the ISP is IDLE (no transactions happening) */ + writel((readl(iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_SYSCONFIG) & + ~ISP5_SYSCONFIG_STANDBYMODE_MASK) | + ISP5_SYSCONFIG_STANDBYMODE_SMART, + iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_SYSCONFIG); + + writel(readl(iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_CTRL) | + ISP5_CTRL_MSTANDBY, + iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_CTRL); + + for (;;) { + if (readl(iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_CTRL) & + ISP5_CTRL_MSTANDBY_WAIT) + break; + if (timeout++ > 1000) { + dev_alert(iss->dev, "cannot set ISP5 to standby\n"); + return -ETIMEDOUT; + } + msleep(1); + } + + /* Now finally, do the reset */ + writel(readl(iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_SYSCONFIG) | + ISP5_SYSCONFIG_SOFTRESET, + iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_SYSCONFIG); + + timeout = 0; + while (readl(iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_SYSCONFIG) & + ISP5_SYSCONFIG_SOFTRESET) { + if (timeout++ > 1000) { + dev_alert(iss->dev, "cannot reset ISP5\n"); + return -ETIMEDOUT; + } + msleep(1); + } + + return 0; +} + +/* + * iss_module_sync_idle - Helper to sync module with its idle state + * @me: ISS submodule's media entity + * @wait: ISS submodule's wait queue for streamoff/interrupt synchronization + * @stopping: flag which tells module wants to stop + * + * This function checks if ISS submodule needs to wait for next interrupt. If + * yes, makes the caller to sleep while waiting for such event. + */ +int omap4iss_module_sync_idle(struct media_entity *me, wait_queue_head_t *wait, + atomic_t *stopping) +{ + struct iss_pipeline *pipe = to_iss_pipeline(me); + struct iss_video *video = pipe->output; + unsigned long flags; + + if (pipe->stream_state == ISS_PIPELINE_STREAM_STOPPED || + (pipe->stream_state == ISS_PIPELINE_STREAM_SINGLESHOT && + !iss_pipeline_ready(pipe))) + return 0; + + /* + * atomic_set() doesn't include memory barrier on ARM platform for SMP + * scenario. We'll call it here to avoid race conditions. + */ + atomic_set(stopping, 1); + smp_wmb(); + + /* + * If module is the last one, it's writing to memory. In this case, + * it's necessary to check if the module is already paused due to + * DMA queue underrun or if it has to wait for next interrupt to be + * idle. + * If it isn't the last one, the function won't sleep but *stopping + * will still be set to warn next submodule caller's interrupt the + * module wants to be idle. + */ + if (!iss_pipeline_is_last(me)) + return 0; + + spin_lock_irqsave(&video->qlock, flags); + if (video->dmaqueue_flags & ISS_VIDEO_DMAQUEUE_UNDERRUN) { + spin_unlock_irqrestore(&video->qlock, flags); + atomic_set(stopping, 0); + smp_wmb(); + return 0; + } + spin_unlock_irqrestore(&video->qlock, flags); + if (!wait_event_timeout(*wait, !atomic_read(stopping), + msecs_to_jiffies(1000))) { + atomic_set(stopping, 0); + smp_wmb(); + return -ETIMEDOUT; + } + + return 0; +} + +/* + * omap4iss_module_sync_is_stopped - Helper to verify if module was stopping + * @wait: ISS submodule's wait queue for streamoff/interrupt synchronization + * @stopping: flag which tells module wants to stop + * + * This function checks if ISS submodule was stopping. In case of yes, it + * notices the caller by setting stopping to 0 and waking up the wait queue. + * Returns 1 if it was stopping or 0 otherwise. + */ +int omap4iss_module_sync_is_stopping(wait_queue_head_t *wait, + atomic_t *stopping) +{ + if (atomic_cmpxchg(stopping, 1, 0)) { + wake_up(wait); + return 1; + } + + return 0; +} + +/* -------------------------------------------------------------------------- + * Clock management + */ + +#define ISS_CLKCTRL_MASK (ISS_CLKCTRL_CSI2_A |\ + ISS_CLKCTRL_CSI2_B |\ + ISS_CLKCTRL_ISP) + +static int __iss_subclk_update(struct iss_device *iss) +{ + u32 clk = 0; + int ret = 0, timeout = 1000; + + if (iss->subclk_resources & OMAP4_ISS_SUBCLK_CSI2_A) + clk |= ISS_CLKCTRL_CSI2_A; + + if (iss->subclk_resources & OMAP4_ISS_SUBCLK_CSI2_B) + clk |= ISS_CLKCTRL_CSI2_B; + + if (iss->subclk_resources & OMAP4_ISS_SUBCLK_ISP) + clk |= ISS_CLKCTRL_ISP; + + writel((readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_CLKCTRL) & + ~ISS_CLKCTRL_MASK) | clk, + iss->regs[OMAP4_ISS_MEM_TOP] + ISS_CLKCTRL); + + /* Wait for HW assertion */ + while (--timeout > 0) { + udelay(1); + if ((readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_CLKSTAT) & + ISS_CLKCTRL_MASK) == clk) + break; + } + + if (!timeout) + ret = -EBUSY; + + return ret; +} + +int omap4iss_subclk_enable(struct iss_device *iss, + enum iss_subclk_resource res) +{ + iss->subclk_resources |= res; + + return __iss_subclk_update(iss); +} + +int omap4iss_subclk_disable(struct iss_device *iss, + enum iss_subclk_resource res) +{ + iss->subclk_resources &= ~res; + + return __iss_subclk_update(iss); +} + +#define ISS_ISP5_CLKCTRL_MASK (ISP5_CTRL_BL_CLK_ENABLE |\ + ISP5_CTRL_ISIF_CLK_ENABLE |\ + ISP5_CTRL_H3A_CLK_ENABLE |\ + ISP5_CTRL_RSZ_CLK_ENABLE |\ + ISP5_CTRL_IPIPE_CLK_ENABLE |\ + ISP5_CTRL_IPIPEIF_CLK_ENABLE) + +static int __iss_isp_subclk_update(struct iss_device *iss) +{ + u32 clk = 0; + + if (iss->isp_subclk_resources & OMAP4_ISS_ISP_SUBCLK_ISIF) + clk |= ISP5_CTRL_ISIF_CLK_ENABLE; + + if (iss->isp_subclk_resources & OMAP4_ISS_ISP_SUBCLK_H3A) + clk |= ISP5_CTRL_H3A_CLK_ENABLE; + + if (iss->isp_subclk_resources & OMAP4_ISS_ISP_SUBCLK_RSZ) + clk |= ISP5_CTRL_RSZ_CLK_ENABLE; + + if (iss->isp_subclk_resources & OMAP4_ISS_ISP_SUBCLK_IPIPE) + clk |= ISP5_CTRL_IPIPE_CLK_ENABLE; + + if (iss->isp_subclk_resources & OMAP4_ISS_ISP_SUBCLK_IPIPEIF) + clk |= ISP5_CTRL_IPIPEIF_CLK_ENABLE; + + if (clk) + clk |= ISP5_CTRL_BL_CLK_ENABLE; + + writel((readl(iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_CTRL) & + ~ISS_ISP5_CLKCTRL_MASK) | clk, + iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_CTRL); + + return 0; +} + +int omap4iss_isp_subclk_enable(struct iss_device *iss, + enum iss_isp_subclk_resource res) +{ + iss->isp_subclk_resources |= res; + + return __iss_isp_subclk_update(iss); +} + +int omap4iss_isp_subclk_disable(struct iss_device *iss, + enum iss_isp_subclk_resource res) +{ + iss->isp_subclk_resources &= ~res; + + return __iss_isp_subclk_update(iss); +} + +/* + * iss_enable_clocks - Enable ISS clocks + * @iss: OMAP4 ISS device + * + * Return 0 if successful, or clk_enable return value if any of tthem fails. + */ +static int iss_enable_clocks(struct iss_device *iss) +{ + int r; + + r = clk_enable(iss->iss_fck); + if (r) { + dev_err(iss->dev, "clk_enable iss_fck failed\n"); + return r; + } + + r = clk_enable(iss->iss_ctrlclk); + if (r) { + dev_err(iss->dev, "clk_enable iss_ctrlclk failed\n"); + goto out_clk_enable_ctrlclk; + } + return 0; + +out_clk_enable_ctrlclk: + clk_disable(iss->iss_fck); + return r; +} + +/* + * iss_disable_clocks - Disable ISS clocks + * @iss: OMAP4 ISS device + */ +static void iss_disable_clocks(struct iss_device *iss) +{ + clk_disable(iss->iss_ctrlclk); + clk_disable(iss->iss_fck); +} + +static void iss_put_clocks(struct iss_device *iss) +{ + if (iss->iss_fck) { + clk_put(iss->iss_fck); + iss->iss_fck = NULL; + } + + if (iss->iss_ctrlclk) { + clk_put(iss->iss_ctrlclk); + iss->iss_ctrlclk = NULL; + } +} + +static int iss_get_clocks(struct iss_device *iss) +{ + iss->iss_fck = clk_get(iss->dev, "iss_fck"); + if (IS_ERR(iss->iss_fck)) { + dev_err(iss->dev, "Unable to get iss_fck clock info\n"); + iss_put_clocks(iss); + return PTR_ERR(iss->iss_fck); + } + + iss->iss_ctrlclk = clk_get(iss->dev, "iss_ctrlclk"); + if (IS_ERR(iss->iss_ctrlclk)) { + dev_err(iss->dev, "Unable to get iss_ctrlclk clock info\n"); + iss_put_clocks(iss); + return PTR_ERR(iss->iss_fck); + } + + return 0; +} + +/* + * omap4iss_get - Acquire the ISS resource. + * + * Initializes the clocks for the first acquire. + * + * Increment the reference count on the ISS. If the first reference is taken, + * enable clocks and power-up all submodules. + * + * Return a pointer to the ISS device structure, or NULL if an error occurred. + */ +struct iss_device *omap4iss_get(struct iss_device *iss) +{ + struct iss_device *__iss = iss; + + if (iss == NULL) + return NULL; + + mutex_lock(&iss->iss_mutex); + if (iss->ref_count > 0) + goto out; + + if (iss_enable_clocks(iss) < 0) { + __iss = NULL; + goto out; + } + + iss_enable_interrupts(iss); + +out: + if (__iss != NULL) + iss->ref_count++; + mutex_unlock(&iss->iss_mutex); + + return __iss; +} + +/* + * omap4iss_put - Release the ISS + * + * Decrement the reference count on the ISS. If the last reference is released, + * power-down all submodules, disable clocks and free temporary buffers. + */ +void omap4iss_put(struct iss_device *iss) +{ + if (iss == NULL) + return; + + mutex_lock(&iss->iss_mutex); + BUG_ON(iss->ref_count == 0); + if (--iss->ref_count == 0) { + iss_disable_interrupts(iss); + iss_disable_clocks(iss); + } + mutex_unlock(&iss->iss_mutex); +} + +static int iss_map_mem_resource(struct platform_device *pdev, + struct iss_device *iss, + enum iss_mem_resources res) +{ + struct resource *mem; + + /* request the mem region for the camera registers */ + + mem = platform_get_resource(pdev, IORESOURCE_MEM, res); + if (!mem) { + dev_err(iss->dev, "no mem resource?\n"); + return -ENODEV; + } + + if (!request_mem_region(mem->start, resource_size(mem), pdev->name)) { + dev_err(iss->dev, + "cannot reserve camera register I/O region\n"); + return -ENODEV; + } + iss->res[res] = mem; + + /* map the region */ + iss->regs[res] = ioremap_nocache(mem->start, resource_size(mem)); + if (!iss->regs[res]) { + dev_err(iss->dev, "cannot map camera register I/O region\n"); + return -ENODEV; + } + + return 0; +} + +static void iss_unregister_entities(struct iss_device *iss) +{ + omap4iss_resizer_unregister_entities(&iss->resizer); + omap4iss_ipipe_unregister_entities(&iss->ipipe); + omap4iss_ipipeif_unregister_entities(&iss->ipipeif); + omap4iss_csi2_unregister_entities(&iss->csi2a); + omap4iss_csi2_unregister_entities(&iss->csi2b); + + v4l2_device_unregister(&iss->v4l2_dev); + media_device_unregister(&iss->media_dev); +} + +/* + * iss_register_subdev_group - Register a group of subdevices + * @iss: OMAP4 ISS device + * @board_info: I2C subdevs board information array + * + * Register all I2C subdevices in the board_info array. The array must be + * terminated by a NULL entry, and the first entry must be the sensor. + * + * Return a pointer to the sensor media entity if it has been successfully + * registered, or NULL otherwise. + */ +static struct v4l2_subdev * +iss_register_subdev_group(struct iss_device *iss, + struct iss_subdev_i2c_board_info *board_info) +{ + struct v4l2_subdev *sensor = NULL; + unsigned int first; + + if (board_info->board_info == NULL) + return NULL; + + for (first = 1; board_info->board_info; ++board_info, first = 0) { + struct v4l2_subdev *subdev; + struct i2c_adapter *adapter; + + adapter = i2c_get_adapter(board_info->i2c_adapter_id); + if (adapter == NULL) { + dev_err(iss->dev, "%s: Unable to get I2C adapter %d for " + "device %s\n", __func__, + board_info->i2c_adapter_id, + board_info->board_info->type); + continue; + } + + subdev = v4l2_i2c_new_subdev_board(&iss->v4l2_dev, adapter, + board_info->board_info, NULL); + if (subdev == NULL) { + dev_err(iss->dev, "%s: Unable to register subdev %s\n", + __func__, board_info->board_info->type); + continue; + } + + if (first) + sensor = subdev; + } + + return sensor; +} + +static int iss_register_entities(struct iss_device *iss) +{ + struct iss_platform_data *pdata = iss->pdata; + struct iss_v4l2_subdevs_group *subdevs; + int ret; + + iss->media_dev.dev = iss->dev; + strlcpy(iss->media_dev.model, "TI OMAP4 ISS", + sizeof(iss->media_dev.model)); + iss->media_dev.hw_revision = iss->revision; + iss->media_dev.link_notify = iss_pipeline_link_notify; + ret = media_device_register(&iss->media_dev); + if (ret < 0) { + printk(KERN_ERR "%s: Media device registration failed (%d)\n", + __func__, ret); + return ret; + } + + iss->v4l2_dev.mdev = &iss->media_dev; + ret = v4l2_device_register(iss->dev, &iss->v4l2_dev); + if (ret < 0) { + printk(KERN_ERR "%s: V4L2 device registration failed (%d)\n", + __func__, ret); + goto done; + } + + /* Register internal entities */ + ret = omap4iss_csi2_register_entities(&iss->csi2a, &iss->v4l2_dev); + if (ret < 0) + goto done; + + ret = omap4iss_csi2_register_entities(&iss->csi2b, &iss->v4l2_dev); + if (ret < 0) + goto done; + + ret = omap4iss_ipipeif_register_entities(&iss->ipipeif, &iss->v4l2_dev); + if (ret < 0) + goto done; + + ret = omap4iss_ipipe_register_entities(&iss->ipipe, &iss->v4l2_dev); + if (ret < 0) + goto done; + + ret = omap4iss_resizer_register_entities(&iss->resizer, &iss->v4l2_dev); + if (ret < 0) + goto done; + + /* Register external entities */ + for (subdevs = pdata->subdevs; subdevs && subdevs->subdevs; ++subdevs) { + struct v4l2_subdev *sensor; + struct media_entity *input; + unsigned int flags; + unsigned int pad; + + sensor = iss_register_subdev_group(iss, subdevs->subdevs); + if (sensor == NULL) + continue; + + sensor->host_priv = subdevs; + + /* Connect the sensor to the correct interface module. + * CSI2a receiver through CSIPHY1, or + * CSI2b receiver through CSIPHY2 + */ + switch (subdevs->interface) { + case ISS_INTERFACE_CSI2A_PHY1: + input = &iss->csi2a.subdev.entity; + pad = CSI2_PAD_SINK; + flags = MEDIA_LNK_FL_IMMUTABLE + | MEDIA_LNK_FL_ENABLED; + break; + + case ISS_INTERFACE_CSI2B_PHY2: + input = &iss->csi2b.subdev.entity; + pad = CSI2_PAD_SINK; + flags = MEDIA_LNK_FL_IMMUTABLE + | MEDIA_LNK_FL_ENABLED; + break; + + default: + printk(KERN_ERR "%s: invalid interface type %u\n", + __func__, subdevs->interface); + ret = -EINVAL; + goto done; + } + + ret = media_entity_create_link(&sensor->entity, 0, input, pad, + flags); + if (ret < 0) + goto done; + } + + ret = v4l2_device_register_subdev_nodes(&iss->v4l2_dev); + +done: + if (ret < 0) + iss_unregister_entities(iss); + + return ret; +} + +static void iss_cleanup_modules(struct iss_device *iss) +{ + omap4iss_csi2_cleanup(iss); + omap4iss_ipipeif_cleanup(iss); + omap4iss_ipipe_cleanup(iss); + omap4iss_resizer_cleanup(iss); +} + +static int iss_initialize_modules(struct iss_device *iss) +{ + int ret; + + ret = omap4iss_csiphy_init(iss); + if (ret < 0) { + dev_err(iss->dev, "CSI PHY initialization failed\n"); + goto error_csiphy; + } + + ret = omap4iss_csi2_init(iss); + if (ret < 0) { + dev_err(iss->dev, "CSI2 initialization failed\n"); + goto error_csi2; + } + + ret = omap4iss_ipipeif_init(iss); + if (ret < 0) { + dev_err(iss->dev, "ISP IPIPEIF initialization failed\n"); + goto error_ipipeif; + } + + ret = omap4iss_ipipe_init(iss); + if (ret < 0) { + dev_err(iss->dev, "ISP IPIPE initialization failed\n"); + goto error_ipipe; + } + + ret = omap4iss_resizer_init(iss); + if (ret < 0) { + dev_err(iss->dev, "ISP RESIZER initialization failed\n"); + goto error_resizer; + } + + /* Connect the submodules. */ + ret = media_entity_create_link( + &iss->csi2a.subdev.entity, CSI2_PAD_SOURCE, + &iss->ipipeif.subdev.entity, IPIPEIF_PAD_SINK, 0); + if (ret < 0) + goto error_link; + + ret = media_entity_create_link( + &iss->csi2b.subdev.entity, CSI2_PAD_SOURCE, + &iss->ipipeif.subdev.entity, IPIPEIF_PAD_SINK, 0); + if (ret < 0) + goto error_link; + + ret = media_entity_create_link( + &iss->ipipeif.subdev.entity, IPIPEIF_PAD_SOURCE_VP, + &iss->resizer.subdev.entity, RESIZER_PAD_SINK, 0); + if (ret < 0) + goto error_link; + + ret = media_entity_create_link( + &iss->ipipeif.subdev.entity, IPIPEIF_PAD_SOURCE_VP, + &iss->ipipe.subdev.entity, IPIPE_PAD_SINK, 0); + if (ret < 0) + goto error_link; + + ret = media_entity_create_link( + &iss->ipipe.subdev.entity, IPIPE_PAD_SOURCE_VP, + &iss->resizer.subdev.entity, RESIZER_PAD_SINK, 0); + if (ret < 0) + goto error_link; + + return 0; + +error_link: + omap4iss_resizer_cleanup(iss); +error_resizer: + omap4iss_ipipe_cleanup(iss); +error_ipipe: + omap4iss_ipipeif_cleanup(iss); +error_ipipeif: + omap4iss_csi2_cleanup(iss); +error_csi2: +error_csiphy: + return ret; +} + +static int iss_probe(struct platform_device *pdev) +{ + struct iss_platform_data *pdata = pdev->dev.platform_data; + struct iss_device *iss; + int i, ret; + + if (pdata == NULL) + return -EINVAL; + + iss = kzalloc(sizeof(*iss), GFP_KERNEL); + if (!iss) { + dev_err(&pdev->dev, "Could not allocate memory\n"); + return -ENOMEM; + } + + mutex_init(&iss->iss_mutex); + + iss->dev = &pdev->dev; + iss->pdata = pdata; + iss->ref_count = 0; + + iss->raw_dmamask = DMA_BIT_MASK(32); + iss->dev->dma_mask = &iss->raw_dmamask; + iss->dev->coherent_dma_mask = DMA_BIT_MASK(32); + + platform_set_drvdata(pdev, iss); + + /* Clocks */ + ret = iss_map_mem_resource(pdev, iss, OMAP4_ISS_MEM_TOP); + if (ret < 0) + goto error; + + ret = iss_get_clocks(iss); + if (ret < 0) + goto error; + + if (omap4iss_get(iss) == NULL) + goto error; + + ret = iss_reset(iss); + if (ret < 0) + goto error_iss; + + iss->revision = readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_REVISION); + dev_info(iss->dev, "Revision %08x found\n", iss->revision); + + for (i = 1; i < OMAP4_ISS_MEM_LAST; i++) { + ret = iss_map_mem_resource(pdev, iss, i); + if (ret) + goto error_iss; + } + + /* Configure BTE BW_LIMITER field to max recommended value (1 GB) */ + writel((readl(iss->regs[OMAP4_ISS_MEM_BTE] + BTE_CTRL) & ~BTE_CTRL_BW_LIMITER_MASK) | + (18 << BTE_CTRL_BW_LIMITER_SHIFT), + iss->regs[OMAP4_ISS_MEM_BTE] + BTE_CTRL); + + /* Perform ISP reset */ + ret = omap4iss_subclk_enable(iss, OMAP4_ISS_SUBCLK_ISP); + if (ret < 0) + goto error_iss; + + ret = iss_isp_reset(iss); + if (ret < 0) + goto error_iss; + + dev_info(iss->dev, "ISP Revision %08x found\n", + readl(iss->regs[OMAP4_ISS_MEM_ISP_SYS1] + ISP5_REVISION)); + + /* Interrupt */ + iss->irq_num = platform_get_irq(pdev, 0); + if (iss->irq_num <= 0) { + dev_err(iss->dev, "No IRQ resource\n"); + ret = -ENODEV; + goto error_iss; + } + + if (request_irq(iss->irq_num, iss_isr, IRQF_SHARED, "OMAP4 ISS", iss)) { + dev_err(iss->dev, "Unable to request IRQ\n"); + ret = -EINVAL; + goto error_iss; + } + + /* Entities */ + ret = iss_initialize_modules(iss); + if (ret < 0) + goto error_irq; + + ret = iss_register_entities(iss); + if (ret < 0) + goto error_modules; + + omap4iss_put(iss); + + return 0; + +error_modules: + iss_cleanup_modules(iss); +error_irq: + free_irq(iss->irq_num, iss); +error_iss: + omap4iss_put(iss); +error: + iss_put_clocks(iss); + + for (i = 0; i < OMAP4_ISS_MEM_LAST; i++) { + if (iss->regs[i]) { + iounmap(iss->regs[i]); + iss->regs[i] = NULL; + } + + if (iss->res[i]) { + release_mem_region(iss->res[i]->start, + resource_size(iss->res[i])); + iss->res[i] = NULL; + } + } + platform_set_drvdata(pdev, NULL); + + mutex_destroy(&iss->iss_mutex); + kfree(iss); + + return ret; +} + +static int iss_remove(struct platform_device *pdev) +{ + struct iss_device *iss = platform_get_drvdata(pdev); + int i; + + iss_unregister_entities(iss); + iss_cleanup_modules(iss); + + free_irq(iss->irq_num, iss); + iss_put_clocks(iss); + + for (i = 0; i < OMAP4_ISS_MEM_LAST; i++) { + if (iss->regs[i]) { + iounmap(iss->regs[i]); + iss->regs[i] = NULL; + } + + if (iss->res[i]) { + release_mem_region(iss->res[i]->start, + resource_size(iss->res[i])); + iss->res[i] = NULL; + } + } + + kfree(iss); + + return 0; +} + +static struct platform_device_id omap4iss_id_table[] = { + { "omap4iss", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(platform, omap4iss_id_table); + +static struct platform_driver iss_driver = { + .probe = iss_probe, + .remove = iss_remove, + .id_table = omap4iss_id_table, + .driver = { + .owner = THIS_MODULE, + .name = "omap4iss", + }, +}; + +module_platform_driver(iss_driver); + +MODULE_DESCRIPTION("TI OMAP4 ISS driver"); +MODULE_AUTHOR("Sergio Aguirre "); +MODULE_LICENSE("GPL"); +MODULE_VERSION(ISS_VIDEO_DRIVER_VERSION); diff --git a/drivers/staging/media/omap4iss/iss.h b/drivers/staging/media/omap4iss/iss.h new file mode 100644 index 000000000000..cc24f1ae17c6 --- /dev/null +++ b/drivers/staging/media/omap4iss/iss.h @@ -0,0 +1,153 @@ +/* + * TI OMAP4 ISS V4L2 Driver + * + * Copyright (C) 2012 Texas Instruments. + * + * Author: Sergio Aguirre + * + * 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. + */ + +#ifndef _OMAP4_ISS_H_ +#define _OMAP4_ISS_H_ + +#include +#include +#include +#include +#include + +#include + +#include "iss_regs.h" +#include "iss_csiphy.h" +#include "iss_csi2.h" +#include "iss_ipipeif.h" +#include "iss_ipipe.h" +#include "iss_resizer.h" + +#define to_iss_device(ptr_module) \ + container_of(ptr_module, struct iss_device, ptr_module) +#define to_device(ptr_module) \ + (to_iss_device(ptr_module)->dev) + +enum iss_mem_resources { + OMAP4_ISS_MEM_TOP, + OMAP4_ISS_MEM_CSI2_A_REGS1, + OMAP4_ISS_MEM_CAMERARX_CORE1, + OMAP4_ISS_MEM_CSI2_B_REGS1, + OMAP4_ISS_MEM_CAMERARX_CORE2, + OMAP4_ISS_MEM_BTE, + OMAP4_ISS_MEM_ISP_SYS1, + OMAP4_ISS_MEM_ISP_RESIZER, + OMAP4_ISS_MEM_ISP_IPIPE, + OMAP4_ISS_MEM_ISP_ISIF, + OMAP4_ISS_MEM_ISP_IPIPEIF, + OMAP4_ISS_MEM_LAST, +}; + +enum iss_subclk_resource { + OMAP4_ISS_SUBCLK_SIMCOP = (1 << 0), + OMAP4_ISS_SUBCLK_ISP = (1 << 1), + OMAP4_ISS_SUBCLK_CSI2_A = (1 << 2), + OMAP4_ISS_SUBCLK_CSI2_B = (1 << 3), + OMAP4_ISS_SUBCLK_CCP2 = (1 << 4), +}; + +enum iss_isp_subclk_resource { + OMAP4_ISS_ISP_SUBCLK_BL = (1 << 0), + OMAP4_ISS_ISP_SUBCLK_ISIF = (1 << 1), + OMAP4_ISS_ISP_SUBCLK_H3A = (1 << 2), + OMAP4_ISS_ISP_SUBCLK_RSZ = (1 << 3), + OMAP4_ISS_ISP_SUBCLK_IPIPE = (1 << 4), + OMAP4_ISS_ISP_SUBCLK_IPIPEIF = (1 << 5), +}; + +/* + * struct iss_reg - Structure for ISS register values. + * @reg: 32-bit Register address. + * @val: 32-bit Register value. + */ +struct iss_reg { + enum iss_mem_resources mmio_range; + u32 reg; + u32 val; +}; + +struct iss_device { + struct v4l2_device v4l2_dev; + struct media_device media_dev; + struct device *dev; + u32 revision; + + /* platform HW resources */ + struct iss_platform_data *pdata; + unsigned int irq_num; + + struct resource *res[OMAP4_ISS_MEM_LAST]; + void __iomem *regs[OMAP4_ISS_MEM_LAST]; + + u64 raw_dmamask; + + struct mutex iss_mutex; /* For handling ref_count field */ + int has_context; + int ref_count; + + struct clk *iss_fck; + struct clk *iss_ctrlclk; + + /* ISS modules */ + struct iss_csi2_device csi2a; + struct iss_csi2_device csi2b; + struct iss_csiphy csiphy1; + struct iss_csiphy csiphy2; + struct iss_ipipeif_device ipipeif; + struct iss_ipipe_device ipipe; + struct iss_resizer_device resizer; + + unsigned int subclk_resources; + unsigned int isp_subclk_resources; +}; + +#define v4l2_dev_to_iss_device(dev) \ + container_of(dev, struct iss_device, v4l2_dev) + +int omap4iss_get_external_info(struct iss_pipeline *pipe, + struct media_link *link); + +int omap4iss_module_sync_idle(struct media_entity *me, wait_queue_head_t *wait, + atomic_t *stopping); + +int omap4iss_module_sync_is_stopping(wait_queue_head_t *wait, + atomic_t *stopping); + +int omap4iss_pipeline_set_stream(struct iss_pipeline *pipe, + enum iss_pipeline_stream_state state); + +void omap4iss_configure_bridge(struct iss_device *iss, + enum ipipeif_input_entity input); + +struct iss_device *omap4iss_get(struct iss_device *iss); +void omap4iss_put(struct iss_device *iss); +int omap4iss_subclk_enable(struct iss_device *iss, + enum iss_subclk_resource res); +int omap4iss_subclk_disable(struct iss_device *iss, + enum iss_subclk_resource res); +int omap4iss_isp_subclk_enable(struct iss_device *iss, + enum iss_isp_subclk_resource res); +int omap4iss_isp_subclk_disable(struct iss_device *iss, + enum iss_isp_subclk_resource res); + +void omap4iss_isp_enable_interrupts(struct iss_device *iss); +void omap4iss_isp_disable_interrupts(struct iss_device *iss); + +int omap4iss_pipeline_pm_use(struct media_entity *entity, int use); + +int omap4iss_register_entities(struct platform_device *pdev, + struct v4l2_device *v4l2_dev); +void omap4iss_unregister_entities(struct platform_device *pdev); + +#endif /* _OMAP4_ISS_H_ */ diff --git a/drivers/staging/media/omap4iss/iss_regs.h b/drivers/staging/media/omap4iss/iss_regs.h new file mode 100644 index 000000000000..7327d0c1ae37 --- /dev/null +++ b/drivers/staging/media/omap4iss/iss_regs.h @@ -0,0 +1,883 @@ +/* + * TI OMAP4 ISS V4L2 Driver - Register defines + * + * Copyright (C) 2012 Texas Instruments. + * + * Author: Sergio Aguirre + * + * 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. + */ + +#ifndef _OMAP4_ISS_REGS_H_ +#define _OMAP4_ISS_REGS_H_ + +/* ISS */ +#define ISS_HL_REVISION 0x0 + +#define ISS_HL_SYSCONFIG 0x10 +#define ISS_HL_SYSCONFIG_IDLEMODE_SHIFT 2 +#define ISS_HL_SYSCONFIG_IDLEMODE_FORCEIDLE 0x0 +#define ISS_HL_SYSCONFIG_IDLEMODE_NOIDLE 0x1 +#define ISS_HL_SYSCONFIG_IDLEMODE_SMARTIDLE 0x2 +#define ISS_HL_SYSCONFIG_SOFTRESET (1 << 0) + +#define ISS_HL_IRQSTATUS_5 (0x24 + (0x10 * 5)) +#define ISS_HL_IRQENABLE_5_SET (0x28 + (0x10 * 5)) +#define ISS_HL_IRQENABLE_5_CLR (0x2C + (0x10 * 5)) + +#define ISS_HL_IRQ_BTE (1 << 11) +#define ISS_HL_IRQ_CBUFF (1 << 10) +#define ISS_HL_IRQ_CSIB (1 << 5) +#define ISS_HL_IRQ_CSIA (1 << 4) +#define ISS_HL_IRQ_ISP(i) (1 << (i)) + +#define ISS_CTRL 0x80 +#define ISS_CTRL_CLK_DIV_MASK (3 << 4) +#define ISS_CTRL_INPUT_SEL_MASK (3 << 2) +#define ISS_CTRL_INPUT_SEL_CSI2A (0 << 2) +#define ISS_CTRL_INPUT_SEL_CSI2B (1 << 2) +#define ISS_CTRL_SYNC_DETECT_VS_RAISING (3 << 0) + +#define ISS_CLKCTRL 0x84 +#define ISS_CLKCTRL_VPORT2_CLK (1 << 30) +#define ISS_CLKCTRL_VPORT1_CLK (1 << 29) +#define ISS_CLKCTRL_VPORT0_CLK (1 << 28) +#define ISS_CLKCTRL_CCP2 (1 << 4) +#define ISS_CLKCTRL_CSI2_B (1 << 3) +#define ISS_CLKCTRL_CSI2_A (1 << 2) +#define ISS_CLKCTRL_ISP (1 << 1) +#define ISS_CLKCTRL_SIMCOP (1 << 0) + +#define ISS_CLKSTAT 0x88 +#define ISS_CLKSTAT_VPORT2_CLK (1 << 30) +#define ISS_CLKSTAT_VPORT1_CLK (1 << 29) +#define ISS_CLKSTAT_VPORT0_CLK (1 << 28) +#define ISS_CLKSTAT_CCP2 (1 << 4) +#define ISS_CLKSTAT_CSI2_B (1 << 3) +#define ISS_CLKSTAT_CSI2_A (1 << 2) +#define ISS_CLKSTAT_ISP (1 << 1) +#define ISS_CLKSTAT_SIMCOP (1 << 0) + +#define ISS_PM_STATUS 0x8C +#define ISS_PM_STATUS_CBUFF_PM_MASK (3 << 12) +#define ISS_PM_STATUS_BTE_PM_MASK (3 << 10) +#define ISS_PM_STATUS_SIMCOP_PM_MASK (3 << 8) +#define ISS_PM_STATUS_ISP_PM_MASK (3 << 6) +#define ISS_PM_STATUS_CCP2_PM_MASK (3 << 4) +#define ISS_PM_STATUS_CSI2_B_PM_MASK (3 << 2) +#define ISS_PM_STATUS_CSI2_A_PM_MASK (3 << 0) + +#define REGISTER0 0x0 +#define REGISTER0_HSCLOCKCONFIG (1 << 24) +#define REGISTER0_THS_TERM_MASK (0xFF << 8) +#define REGISTER0_THS_TERM_SHIFT 8 +#define REGISTER0_THS_SETTLE_MASK (0xFF << 0) +#define REGISTER0_THS_SETTLE_SHIFT 0 + +#define REGISTER1 0x4 +#define REGISTER1_RESET_DONE_CTRLCLK (1 << 29) +#define REGISTER1_CLOCK_MISS_DETECTOR_STATUS (1 << 25) +#define REGISTER1_TCLK_TERM_MASK (0x3F << 18) +#define REGISTER1_TCLK_TERM_SHIFT 18 +#define REGISTER1_DPHY_HS_SYNC_PATTERN_SHIFT 10 +#define REGISTER1_CTRLCLK_DIV_FACTOR_MASK (0x3 << 8) +#define REGISTER1_CTRLCLK_DIV_FACTOR_SHIFT 8 +#define REGISTER1_TCLK_SETTLE_MASK (0xFF << 0) +#define REGISTER1_TCLK_SETTLE_SHIFT 0 + +#define REGISTER2 0x8 + +#define CSI2_SYSCONFIG 0x10 +#define CSI2_SYSCONFIG_MSTANDBY_MODE_MASK (3 << 12) +#define CSI2_SYSCONFIG_MSTANDBY_MODE_FORCE (0 << 12) +#define CSI2_SYSCONFIG_MSTANDBY_MODE_NO (1 << 12) +#define CSI2_SYSCONFIG_MSTANDBY_MODE_SMART (2 << 12) +#define CSI2_SYSCONFIG_SOFT_RESET (1 << 1) +#define CSI2_SYSCONFIG_AUTO_IDLE (1 << 0) + +#define CSI2_SYSSTATUS 0x14 +#define CSI2_SYSSTATUS_RESET_DONE (1 << 0) + +#define CSI2_IRQSTATUS 0x18 +#define CSI2_IRQENABLE 0x1C + +/* Shared bits across CSI2_IRQENABLE and IRQSTATUS */ + +#define CSI2_IRQ_OCP_ERR (1 << 14) +#define CSI2_IRQ_SHORT_PACKET (1 << 13) +#define CSI2_IRQ_ECC_CORRECTION (1 << 12) +#define CSI2_IRQ_ECC_NO_CORRECTION (1 << 11) +#define CSI2_IRQ_COMPLEXIO_ERR (1 << 9) +#define CSI2_IRQ_FIFO_OVF (1 << 8) +#define CSI2_IRQ_CONTEXT0 (1 << 0) + +#define CSI2_CTRL 0x40 +#define CSI2_CTRL_MFLAG_LEVH_MASK (7 << 20) +#define CSI2_CTRL_MFLAG_LEVH_SHIFT 20 +#define CSI2_CTRL_MFLAG_LEVL_MASK (7 << 17) +#define CSI2_CTRL_MFLAG_LEVL_SHIFT 17 +#define CSI2_CTRL_BURST_SIZE_EXPAND (1 << 16) +#define CSI2_CTRL_VP_CLK_EN (1 << 15) +#define CSI2_CTRL_NON_POSTED_WRITE (1 << 13) +#define CSI2_CTRL_VP_ONLY_EN (1 << 11) +#define CSI2_CTRL_VP_OUT_CTRL_MASK (3 << 8) +#define CSI2_CTRL_VP_OUT_CTRL_SHIFT 8 +#define CSI2_CTRL_DBG_EN (1 << 7) +#define CSI2_CTRL_BURST_SIZE_MASK (3 << 5) +#define CSI2_CTRL_ENDIANNESS (1 << 4) +#define CSI2_CTRL_FRAME (1 << 3) +#define CSI2_CTRL_ECC_EN (1 << 2) +#define CSI2_CTRL_IF_EN (1 << 0) + +#define CSI2_DBG_H 0x44 + +#define CSI2_COMPLEXIO_CFG 0x50 +#define CSI2_COMPLEXIO_CFG_RESET_CTRL (1 << 30) +#define CSI2_COMPLEXIO_CFG_RESET_DONE (1 << 29) +#define CSI2_COMPLEXIO_CFG_PWD_CMD_MASK (3 << 27) +#define CSI2_COMPLEXIO_CFG_PWD_CMD_OFF (0 << 27) +#define CSI2_COMPLEXIO_CFG_PWD_CMD_ON (1 << 27) +#define CSI2_COMPLEXIO_CFG_PWD_CMD_ULP (2 << 27) +#define CSI2_COMPLEXIO_CFG_PWD_STATUS_MASK (3 << 25) +#define CSI2_COMPLEXIO_CFG_PWD_STATUS_OFF (0 << 25) +#define CSI2_COMPLEXIO_CFG_PWD_STATUS_ON (1 << 25) +#define CSI2_COMPLEXIO_CFG_PWD_STATUS_ULP (2 << 25) +#define CSI2_COMPLEXIO_CFG_PWR_AUTO (1 << 24) +#define CSI2_COMPLEXIO_CFG_DATA_POL(i) (1 << (((i) * 4) + 3)) +#define CSI2_COMPLEXIO_CFG_DATA_POSITION_MASK(i) (7 << ((i) * 4)) +#define CSI2_COMPLEXIO_CFG_DATA_POSITION_SHIFT(i) ((i) * 4) +#define CSI2_COMPLEXIO_CFG_CLOCK_POL (1 << 3) +#define CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK (7 << 0) +#define CSI2_COMPLEXIO_CFG_CLOCK_POSITION_SHIFT 0 + +#define CSI2_COMPLEXIO_IRQSTATUS 0x54 + +#define CSI2_SHORT_PACKET 0x5C + +#define CSI2_COMPLEXIO_IRQENABLE 0x60 + +/* Shared bits across CSI2_COMPLEXIO_IRQENABLE and IRQSTATUS */ +#define CSI2_COMPLEXIO_IRQ_STATEALLULPMEXIT (1 << 26) +#define CSI2_COMPLEXIO_IRQ_STATEALLULPMENTER (1 << 25) +#define CSI2_COMPLEXIO_IRQ_STATEULPM5 (1 << 24) +#define CSI2_COMPLEXIO_IRQ_STATEULPM4 (1 << 23) +#define CSI2_COMPLEXIO_IRQ_STATEULPM3 (1 << 22) +#define CSI2_COMPLEXIO_IRQ_STATEULPM2 (1 << 21) +#define CSI2_COMPLEXIO_IRQ_STATEULPM1 (1 << 20) +#define CSI2_COMPLEXIO_IRQ_ERRCONTROL5 (1 << 19) +#define CSI2_COMPLEXIO_IRQ_ERRCONTROL4 (1 << 18) +#define CSI2_COMPLEXIO_IRQ_ERRCONTROL3 (1 << 17) +#define CSI2_COMPLEXIO_IRQ_ERRCONTROL2 (1 << 16) +#define CSI2_COMPLEXIO_IRQ_ERRCONTROL1 (1 << 15) +#define CSI2_COMPLEXIO_IRQ_ERRESC5 (1 << 14) +#define CSI2_COMPLEXIO_IRQ_ERRESC4 (1 << 13) +#define CSI2_COMPLEXIO_IRQ_ERRESC3 (1 << 12) +#define CSI2_COMPLEXIO_IRQ_ERRESC2 (1 << 11) +#define CSI2_COMPLEXIO_IRQ_ERRESC1 (1 << 10) +#define CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS5 (1 << 9) +#define CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS4 (1 << 8) +#define CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS3 (1 << 7) +#define CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS2 (1 << 6) +#define CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS1 (1 << 5) +#define CSI2_COMPLEXIO_IRQ_ERRSOTHS5 (1 << 4) +#define CSI2_COMPLEXIO_IRQ_ERRSOTHS4 (1 << 3) +#define CSI2_COMPLEXIO_IRQ_ERRSOTHS3 (1 << 2) +#define CSI2_COMPLEXIO_IRQ_ERRSOTHS2 (1 << 1) +#define CSI2_COMPLEXIO_IRQ_ERRSOTHS1 (1 << 0) + +#define CSI2_DBG_P 0x68 + +#define CSI2_TIMING 0x6C +#define CSI2_TIMING_FORCE_RX_MODE_IO1 (1 << 15) +#define CSI2_TIMING_STOP_STATE_X16_IO1 (1 << 14) +#define CSI2_TIMING_STOP_STATE_X4_IO1 (1 << 13) +#define CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK (0x1FFF << 0) +#define CSI2_TIMING_STOP_STATE_COUNTER_IO1_SHIFT 0 + +#define CSI2_CTX_CTRL1(i) (0x70 + (0x20 * i)) +#define CSI2_CTX_CTRL1_GENERIC (1 << 30) +#define CSI2_CTX_CTRL1_TRANSCODE (0xF << 24) +#define CSI2_CTX_CTRL1_FEC_NUMBER_MASK (0xFF << 16) +#define CSI2_CTX_CTRL1_COUNT_MASK (0xFF << 8) +#define CSI2_CTX_CTRL1_COUNT_SHIFT 8 +#define CSI2_CTX_CTRL1_EOF_EN (1 << 7) +#define CSI2_CTX_CTRL1_EOL_EN (1 << 6) +#define CSI2_CTX_CTRL1_CS_EN (1 << 5) +#define CSI2_CTX_CTRL1_COUNT_UNLOCK (1 << 4) +#define CSI2_CTX_CTRL1_PING_PONG (1 << 3) +#define CSI2_CTX_CTRL1_CTX_EN (1 << 0) + +#define CSI2_CTX_CTRL2(i) (0x74 + (0x20 * i)) +#define CSI2_CTX_CTRL2_USER_DEF_MAP_SHIFT 13 +#define CSI2_CTX_CTRL2_USER_DEF_MAP_MASK \ + (0x3 << CSI2_CTX_CTRL2_USER_DEF_MAP_SHIFT) +#define CSI2_CTX_CTRL2_VIRTUAL_ID_MASK (3 << 11) +#define CSI2_CTX_CTRL2_VIRTUAL_ID_SHIFT 11 +#define CSI2_CTX_CTRL2_DPCM_PRED (1 << 10) +#define CSI2_CTX_CTRL2_FORMAT_MASK (0x3FF << 0) +#define CSI2_CTX_CTRL2_FORMAT_SHIFT 0 + +#define CSI2_CTX_DAT_OFST(i) (0x78 + (0x20 * i)) +#define CSI2_CTX_DAT_OFST_MASK (0xFFF << 5) + +#define CSI2_CTX_PING_ADDR(i) (0x7C + (0x20 * i)) +#define CSI2_CTX_PING_ADDR_MASK 0xFFFFFFE0 + +#define CSI2_CTX_PONG_ADDR(i) (0x80 + (0x20 * i)) +#define CSI2_CTX_PONG_ADDR_MASK CSI2_CTX_PING_ADDR_MASK + +#define CSI2_CTX_IRQENABLE(i) (0x84 + (0x20 * i)) +#define CSI2_CTX_IRQSTATUS(i) (0x88 + (0x20 * i)) + +#define CSI2_CTX_CTRL3(i) (0x8C + (0x20 * i)) +#define CSI2_CTX_CTRL3_ALPHA_SHIFT 5 +#define CSI2_CTX_CTRL3_ALPHA_MASK \ + (0x3fff << CSI2_CTX_CTRL3_ALPHA_SHIFT) + +/* Shared bits across CSI2_CTX_IRQENABLE and IRQSTATUS */ +#define CSI2_CTX_IRQ_ECC_CORRECTION (1 << 8) +#define CSI2_CTX_IRQ_LINE_NUMBER (1 << 7) +#define CSI2_CTX_IRQ_FRAME_NUMBER (1 << 6) +#define CSI2_CTX_IRQ_CS (1 << 5) +#define CSI2_CTX_IRQ_LE (1 << 3) +#define CSI2_CTX_IRQ_LS (1 << 2) +#define CSI2_CTX_IRQ_FE (1 << 1) +#define CSI2_CTX_IRQ_FS (1 << 0) + +/* ISS BTE */ +#define BTE_CTRL (0x0030) +#define BTE_CTRL_BW_LIMITER_MASK (0x3FF << 22) +#define BTE_CTRL_BW_LIMITER_SHIFT 22 + +/* ISS ISP_SYS1 */ +#define ISP5_REVISION (0x0000) +#define ISP5_SYSCONFIG (0x0010) +#define ISP5_SYSCONFIG_STANDBYMODE_MASK (3 << 4) +#define ISP5_SYSCONFIG_STANDBYMODE_FORCE (0 << 4) +#define ISP5_SYSCONFIG_STANDBYMODE_NO (1 << 4) +#define ISP5_SYSCONFIG_STANDBYMODE_SMART (2 << 4) +#define ISP5_SYSCONFIG_SOFTRESET (1 << 1) + +#define ISP5_IRQSTATUS(i) (0x0028 + (0x10 * (i))) +#define ISP5_IRQENABLE_SET(i) (0x002C + (0x10 * (i))) +#define ISP5_IRQENABLE_CLR(i) (0x0030 + (0x10 * (i))) + +/* Bits shared for ISP5_IRQ* registers */ +#define ISP5_IRQ_OCP_ERR (1 << 31) +#define ISP5_IRQ_RSZ_INT_EOF0 (1 << 22) +#define ISP5_IRQ_RSZ_FIFO_IN_BLK (1 << 19) +#define ISP5_IRQ_RSZ_FIFO_OVF (1 << 18) +#define ISP5_IRQ_RSZ_INT_CYC_RSZA (1 << 16) +#define ISP5_IRQ_RSZ_INT_DMA (1 << 15) +#define ISP5_IRQ_IPIPEIF (1 << 9) +#define ISP5_IRQ_ISIF3 (1 << 3) +#define ISP5_IRQ_ISIF2 (1 << 2) +#define ISP5_IRQ_ISIF1 (1 << 1) +#define ISP5_IRQ_ISIF0 (1 << 0) + +#define ISP5_CTRL (0x006C) +#define ISP5_CTRL_MSTANDBY (1 << 24) +#define ISP5_CTRL_VD_PULSE_EXT (1 << 23) +#define ISP5_CTRL_MSTANDBY_WAIT (1 << 20) +#define ISP5_CTRL_BL_CLK_ENABLE (1 << 15) +#define ISP5_CTRL_ISIF_CLK_ENABLE (1 << 14) +#define ISP5_CTRL_H3A_CLK_ENABLE (1 << 13) +#define ISP5_CTRL_RSZ_CLK_ENABLE (1 << 12) +#define ISP5_CTRL_IPIPE_CLK_ENABLE (1 << 11) +#define ISP5_CTRL_IPIPEIF_CLK_ENABLE (1 << 10) +#define ISP5_CTRL_SYNC_ENABLE (1 << 9) +#define ISP5_CTRL_PSYNC_CLK_SEL (1 << 8) + +/* ISS ISP ISIF register offsets */ +#define ISIF_SYNCEN (0x0000) +#define ISIF_SYNCEN_DWEN (1 << 1) +#define ISIF_SYNCEN_SYEN (1 << 0) + +#define ISIF_MODESET (0x0004) +#define ISIF_MODESET_INPMOD_MASK (3 << 12) +#define ISIF_MODESET_INPMOD_RAW (0 << 12) +#define ISIF_MODESET_INPMOD_YCBCR16 (1 << 12) +#define ISIF_MODESET_INPMOD_YCBCR8 (2 << 12) +#define ISIF_MODESET_CCDW_MASK (7 << 8) +#define ISIF_MODESET_CCDW_2BIT (2 << 8) +#define ISIF_MODESET_CCDMD (1 << 7) +#define ISIF_MODESET_SWEN (1 << 5) +#define ISIF_MODESET_HDPOL (1 << 3) +#define ISIF_MODESET_VDPOL (1 << 2) + +#define ISIF_SPH (0x0018) +#define ISIF_SPH_MASK (0x7FFF) + +#define ISIF_LNH (0x001C) +#define ISIF_LNH_MASK (0x7FFF) + +#define ISIF_LNV (0x0028) +#define ISIF_LNV_MASK (0x7FFF) + +#define ISIF_HSIZE (0x0034) +#define ISIF_HSIZE_ADCR (1 << 12) +#define ISIF_HSIZE_HSIZE_MASK (0xFFF) + +#define ISIF_CADU (0x003C) +#define ISIF_CADU_MASK (0x7FF) + +#define ISIF_CADL (0x0040) +#define ISIF_CADL_MASK (0xFFFF) + +#define ISIF_CCOLP (0x004C) +#define ISIF_CCOLP_CP0_F0_R (0 << 6) +#define ISIF_CCOLP_CP0_F0_GR (1 << 6) +#define ISIF_CCOLP_CP0_F0_B (3 << 6) +#define ISIF_CCOLP_CP0_F0_GB (2 << 6) +#define ISIF_CCOLP_CP1_F0_R (0 << 4) +#define ISIF_CCOLP_CP1_F0_GR (1 << 4) +#define ISIF_CCOLP_CP1_F0_B (3 << 4) +#define ISIF_CCOLP_CP1_F0_GB (2 << 4) +#define ISIF_CCOLP_CP2_F0_R (0 << 2) +#define ISIF_CCOLP_CP2_F0_GR (1 << 2) +#define ISIF_CCOLP_CP2_F0_B (3 << 2) +#define ISIF_CCOLP_CP2_F0_GB (2 << 2) +#define ISIF_CCOLP_CP3_F0_R (0 << 0) +#define ISIF_CCOLP_CP3_F0_GR (1 << 0) +#define ISIF_CCOLP_CP3_F0_B (3 << 0) +#define ISIF_CCOLP_CP3_F0_GB (2 << 0) + +#define ISIF_VDINT0 (0x0070) +#define ISIF_VDINT0_MASK (0x7FFF) + +#define ISIF_CGAMMAWD (0x0080) +#define ISIF_CGAMMAWD_GWDI_MASK (0xF << 1) +#define ISIF_CGAMMAWD_GWDI_BIT11 (0x4 << 1) + +#define ISIF_CCDCFG (0x0088) +#define ISIF_CCDCFG_Y8POS (1 << 11) + +/* ISS ISP IPIPEIF register offsets */ +#define IPIPEIF_ENABLE (0x0000) + +#define IPIPEIF_CFG1 (0x0004) +#define IPIPEIF_CFG1_INPSRC1_MASK (3 << 14) +#define IPIPEIF_CFG1_INPSRC1_VPORT_RAW (0 << 14) +#define IPIPEIF_CFG1_INPSRC1_SDRAM_RAW (1 << 14) +#define IPIPEIF_CFG1_INPSRC1_ISIF_DARKFM (2 << 14) +#define IPIPEIF_CFG1_INPSRC1_SDRAM_YUV (3 << 14) +#define IPIPEIF_CFG1_INPSRC2_MASK (3 << 2) +#define IPIPEIF_CFG1_INPSRC2_ISIF (0 << 2) +#define IPIPEIF_CFG1_INPSRC2_SDRAM_RAW (1 << 2) +#define IPIPEIF_CFG1_INPSRC2_ISIF_DARKFM (2 << 2) +#define IPIPEIF_CFG1_INPSRC2_SDRAM_YUV (3 << 2) + +#define IPIPEIF_CFG2 (0x0030) +#define IPIPEIF_CFG2_YUV8P (1 << 7) +#define IPIPEIF_CFG2_YUV8 (1 << 6) +#define IPIPEIF_CFG2_YUV16 (1 << 3) +#define IPIPEIF_CFG2_VDPOL (1 << 2) +#define IPIPEIF_CFG2_HDPOL (1 << 1) +#define IPIPEIF_CFG2_INTSW (1 << 0) + +#define IPIPEIF_CLKDIV (0x0040) + +/* ISS ISP IPIPE register offsets */ +#define IPIPE_SRC_EN (0x0000) +#define IPIPE_SRC_EN_EN (1 << 0) + +#define IPIPE_SRC_MODE (0x0004) +#define IPIPE_SRC_MODE_WRT (1 << 1) +#define IPIPE_SRC_MODE_OST (1 << 0) + +#define IPIPE_SRC_FMT (0x0008) +#define IPIPE_SRC_FMT_RAW2YUV (0 << 0) +#define IPIPE_SRC_FMT_RAW2RAW (1 << 0) +#define IPIPE_SRC_FMT_RAW2STATS (2 << 0) +#define IPIPE_SRC_FMT_YUV2YUV (3 << 0) + +#define IPIPE_SRC_COL (0x000C) +#define IPIPE_SRC_COL_OO_R (0 << 6) +#define IPIPE_SRC_COL_OO_GR (1 << 6) +#define IPIPE_SRC_COL_OO_B (3 << 6) +#define IPIPE_SRC_COL_OO_GB (2 << 6) +#define IPIPE_SRC_COL_OE_R (0 << 4) +#define IPIPE_SRC_COL_OE_GR (1 << 4) +#define IPIPE_SRC_COL_OE_B (3 << 4) +#define IPIPE_SRC_COL_OE_GB (2 << 4) +#define IPIPE_SRC_COL_EO_R (0 << 2) +#define IPIPE_SRC_COL_EO_GR (1 << 2) +#define IPIPE_SRC_COL_EO_B (3 << 2) +#define IPIPE_SRC_COL_EO_GB (2 << 2) +#define IPIPE_SRC_COL_EE_R (0 << 0) +#define IPIPE_SRC_COL_EE_GR (1 << 0) +#define IPIPE_SRC_COL_EE_B (3 << 0) +#define IPIPE_SRC_COL_EE_GB (2 << 0) + +#define IPIPE_SRC_VPS (0x0010) +#define IPIPE_SRC_VPS_MASK (0xFFFF) + +#define IPIPE_SRC_VSZ (0x0014) +#define IPIPE_SRC_VSZ_MASK (0x1FFF) + +#define IPIPE_SRC_HPS (0x0018) +#define IPIPE_SRC_HPS_MASK (0xFFFF) + +#define IPIPE_SRC_HSZ (0x001C) +#define IPIPE_SRC_HSZ_MASK (0x1FFE) + +#define IPIPE_SEL_SBU (0x0020) + +#define IPIPE_SRC_STA (0x0024) + +#define IPIPE_GCK_MMR (0x0028) +#define IPIPE_GCK_MMR_REG (1 << 0) + +#define IPIPE_GCK_PIX (0x002C) +#define IPIPE_GCK_PIX_G3 (1 << 3) +#define IPIPE_GCK_PIX_G2 (1 << 2) +#define IPIPE_GCK_PIX_G1 (1 << 1) +#define IPIPE_GCK_PIX_G0 (1 << 0) + +#define IPIPE_DPC_LUT_EN (0x0034) +#define IPIPE_DPC_LUT_SEL (0x0038) +#define IPIPE_DPC_LUT_ADR (0x003C) +#define IPIPE_DPC_LUT_SIZ (0x0040) + +#define IPIPE_DPC_OTF_EN (0x0044) +#define IPIPE_DPC_OTF_TYP (0x0048) +#define IPIPE_DPC_OTF_2_D_THR_R (0x004C) +#define IPIPE_DPC_OTF_2_D_THR_GR (0x0050) +#define IPIPE_DPC_OTF_2_D_THR_GB (0x0054) +#define IPIPE_DPC_OTF_2_D_THR_B (0x0058) +#define IPIPE_DPC_OTF_2_C_THR_R (0x005C) +#define IPIPE_DPC_OTF_2_C_THR_GR (0x0060) +#define IPIPE_DPC_OTF_2_C_THR_GB (0x0064) +#define IPIPE_DPC_OTF_2_C_THR_B (0x0068) +#define IPIPE_DPC_OTF_3_SHF (0x006C) +#define IPIPE_DPC_OTF_3_D_THR (0x0070) +#define IPIPE_DPC_OTF_3_D_SPL (0x0074) +#define IPIPE_DPC_OTF_3_D_MIN (0x0078) +#define IPIPE_DPC_OTF_3_D_MAX (0x007C) +#define IPIPE_DPC_OTF_3_C_THR (0x0080) +#define IPIPE_DPC_OTF_3_C_SLP (0x0084) +#define IPIPE_DPC_OTF_3_C_MIN (0x0088) +#define IPIPE_DPC_OTF_3_C_MAX (0x008C) + +#define IPIPE_LSC_VOFT (0x0090) +#define IPIPE_LSC_VA2 (0x0094) +#define IPIPE_LSC_VA1 (0x0098) +#define IPIPE_LSC_VS (0x009C) +#define IPIPE_LSC_HOFT (0x00A0) +#define IPIPE_LSC_HA2 (0x00A4) +#define IPIPE_LSC_HA1 (0x00A8) +#define IPIPE_LSC_HS (0x00AC) +#define IPIPE_LSC_GAN_R (0x00B0) +#define IPIPE_LSC_GAN_GR (0x00B4) +#define IPIPE_LSC_GAN_GB (0x00B8) +#define IPIPE_LSC_GAN_B (0x00BC) +#define IPIPE_LSC_OFT_R (0x00C0) +#define IPIPE_LSC_OFT_GR (0x00C4) +#define IPIPE_LSC_OFT_GB (0x00C8) +#define IPIPE_LSC_OFT_B (0x00CC) +#define IPIPE_LSC_SHF (0x00D0) +#define IPIPE_LSC_MAX (0x00D4) + +#define IPIPE_D2F_1ST_EN (0x00D8) +#define IPIPE_D2F_1ST_TYP (0x00DC) +#define IPIPE_D2F_1ST_THR_00 (0x00E0) +#define IPIPE_D2F_1ST_THR_01 (0x00E4) +#define IPIPE_D2F_1ST_THR_02 (0x00E8) +#define IPIPE_D2F_1ST_THR_03 (0x00EC) +#define IPIPE_D2F_1ST_THR_04 (0x00F0) +#define IPIPE_D2F_1ST_THR_05 (0x00F4) +#define IPIPE_D2F_1ST_THR_06 (0x00F8) +#define IPIPE_D2F_1ST_THR_07 (0x00FC) +#define IPIPE_D2F_1ST_STR_00 (0x0100) +#define IPIPE_D2F_1ST_STR_01 (0x0104) +#define IPIPE_D2F_1ST_STR_02 (0x0108) +#define IPIPE_D2F_1ST_STR_03 (0x010C) +#define IPIPE_D2F_1ST_STR_04 (0x0110) +#define IPIPE_D2F_1ST_STR_05 (0x0114) +#define IPIPE_D2F_1ST_STR_06 (0x0118) +#define IPIPE_D2F_1ST_STR_07 (0x011C) +#define IPIPE_D2F_1ST_SPR_00 (0x0120) +#define IPIPE_D2F_1ST_SPR_01 (0x0124) +#define IPIPE_D2F_1ST_SPR_02 (0x0128) +#define IPIPE_D2F_1ST_SPR_03 (0x012C) +#define IPIPE_D2F_1ST_SPR_04 (0x0130) +#define IPIPE_D2F_1ST_SPR_05 (0x0134) +#define IPIPE_D2F_1ST_SPR_06 (0x0138) +#define IPIPE_D2F_1ST_SPR_07 (0x013C) +#define IPIPE_D2F_1ST_EDG_MIN (0x0140) +#define IPIPE_D2F_1ST_EDG_MAX (0x0144) +#define IPIPE_D2F_2ND_EN (0x0148) +#define IPIPE_D2F_2ND_TYP (0x014C) +#define IPIPE_D2F_2ND_THR00 (0x0150) +#define IPIPE_D2F_2ND_THR01 (0x0154) +#define IPIPE_D2F_2ND_THR02 (0x0158) +#define IPIPE_D2F_2ND_THR03 (0x015C) +#define IPIPE_D2F_2ND_THR04 (0x0160) +#define IPIPE_D2F_2ND_THR05 (0x0164) +#define IPIPE_D2F_2ND_THR06 (0x0168) +#define IPIPE_D2F_2ND_THR07 (0x016C) +#define IPIPE_D2F_2ND_STR_00 (0x0170) +#define IPIPE_D2F_2ND_STR_01 (0x0174) +#define IPIPE_D2F_2ND_STR_02 (0x0178) +#define IPIPE_D2F_2ND_STR_03 (0x017C) +#define IPIPE_D2F_2ND_STR_04 (0x0180) +#define IPIPE_D2F_2ND_STR_05 (0x0184) +#define IPIPE_D2F_2ND_STR_06 (0x0188) +#define IPIPE_D2F_2ND_STR_07 (0x018C) +#define IPIPE_D2F_2ND_SPR_00 (0x0190) +#define IPIPE_D2F_2ND_SPR_01 (0x0194) +#define IPIPE_D2F_2ND_SPR_02 (0x0198) +#define IPIPE_D2F_2ND_SPR_03 (0x019C) +#define IPIPE_D2F_2ND_SPR_04 (0x01A0) +#define IPIPE_D2F_2ND_SPR_05 (0x01A4) +#define IPIPE_D2F_2ND_SPR_06 (0x01A8) +#define IPIPE_D2F_2ND_SPR_07 (0x01AC) +#define IPIPE_D2F_2ND_EDG_MIN (0x01B0) +#define IPIPE_D2F_2ND_EDG_MAX (0x01B4) + +#define IPIPE_GIC_EN (0x01B8) +#define IPIPE_GIC_TYP (0x01BC) +#define IPIPE_GIC_GAN (0x01C0) +#define IPIPE_GIC_NFGAIN (0x01C4) +#define IPIPE_GIC_THR (0x01C8) +#define IPIPE_GIC_SLP (0x01CC) + +#define IPIPE_WB2_OFT_R (0x01D0) +#define IPIPE_WB2_OFT_GR (0x01D4) +#define IPIPE_WB2_OFT_GB (0x01D8) +#define IPIPE_WB2_OFT_B (0x01DC) + +#define IPIPE_WB2_WGN_R (0x01E0) +#define IPIPE_WB2_WGN_GR (0x01E4) +#define IPIPE_WB2_WGN_GB (0x01E8) +#define IPIPE_WB2_WGN_B (0x01EC) + +#define IPIPE_CFA_MODE (0x01F0) +#define IPIPE_CFA_2DIR_HPF_THR (0x01F4) +#define IPIPE_CFA_2DIR_HPF_SLP (0x01F8) +#define IPIPE_CFA_2DIR_MIX_THR (0x01FC) +#define IPIPE_CFA_2DIR_MIX_SLP (0x0200) +#define IPIPE_CFA_2DIR_DIR_TRH (0x0204) +#define IPIPE_CFA_2DIR_DIR_SLP (0x0208) +#define IPIPE_CFA_2DIR_NDWT (0x020C) +#define IPIPE_CFA_MONO_HUE_FRA (0x0210) +#define IPIPE_CFA_MONO_EDG_THR (0x0214) +#define IPIPE_CFA_MONO_THR_MIN (0x0218) + +#define IPIPE_CFA_MONO_THR_SLP (0x021C) +#define IPIPE_CFA_MONO_SLP_MIN (0x0220) +#define IPIPE_CFA_MONO_SLP_SLP (0x0224) +#define IPIPE_CFA_MONO_LPWT (0x0228) + +#define IPIPE_RGB1_MUL_RR (0x022C) +#define IPIPE_RGB1_MUL_GR (0x0230) +#define IPIPE_RGB1_MUL_BR (0x0234) +#define IPIPE_RGB1_MUL_RG (0x0238) +#define IPIPE_RGB1_MUL_GG (0x023C) +#define IPIPE_RGB1_MUL_BG (0x0240) +#define IPIPE_RGB1_MUL_RB (0x0244) +#define IPIPE_RGB1_MUL_GB (0x0248) +#define IPIPE_RGB1_MUL_BB (0x024C) +#define IPIPE_RGB1_OFT_OR (0x0250) +#define IPIPE_RGB1_OFT_OG (0x0254) +#define IPIPE_RGB1_OFT_OB (0x0258) +#define IPIPE_GMM_CFG (0x025C) +#define IPIPE_RGB2_MUL_RR (0x0260) +#define IPIPE_RGB2_MUL_GR (0x0264) +#define IPIPE_RGB2_MUL_BR (0x0268) +#define IPIPE_RGB2_MUL_RG (0x026C) +#define IPIPE_RGB2_MUL_GG (0x0270) +#define IPIPE_RGB2_MUL_BG (0x0274) +#define IPIPE_RGB2_MUL_RB (0x0278) +#define IPIPE_RGB2_MUL_GB (0x027C) +#define IPIPE_RGB2_MUL_BB (0x0280) +#define IPIPE_RGB2_OFT_OR (0x0284) +#define IPIPE_RGB2_OFT_OG (0x0288) +#define IPIPE_RGB2_OFT_OB (0x028C) + +#define IPIPE_YUV_ADJ (0x0294) +#define IPIPE_YUV_MUL_RY (0x0298) +#define IPIPE_YUV_MUL_GY (0x029C) +#define IPIPE_YUV_MUL_BY (0x02A0) +#define IPIPE_YUV_MUL_RCB (0x02A4) +#define IPIPE_YUV_MUL_GCB (0x02A8) +#define IPIPE_YUV_MUL_BCB (0x02AC) +#define IPIPE_YUV_MUL_RCR (0x02B0) +#define IPIPE_YUV_MUL_GCR (0x02B4) +#define IPIPE_YUV_MUL_BCR (0x02B8) +#define IPIPE_YUV_OFT_Y (0x02BC) +#define IPIPE_YUV_OFT_CB (0x02C0) +#define IPIPE_YUV_OFT_CR (0x02C4) + +#define IPIPE_YUV_PHS (0x02C8) +#define IPIPE_YUV_PHS_LPF (1 << 1) +#define IPIPE_YUV_PHS_POS (1 << 0) + +#define IPIPE_YEE_EN (0x02D4) +#define IPIPE_YEE_TYP (0x02D8) +#define IPIPE_YEE_SHF (0x02DC) +#define IPIPE_YEE_MUL_00 (0x02E0) +#define IPIPE_YEE_MUL_01 (0x02E4) +#define IPIPE_YEE_MUL_02 (0x02E8) +#define IPIPE_YEE_MUL_10 (0x02EC) +#define IPIPE_YEE_MUL_11 (0x02F0) +#define IPIPE_YEE_MUL_12 (0x02F4) +#define IPIPE_YEE_MUL_20 (0x02F8) +#define IPIPE_YEE_MUL_21 (0x02FC) +#define IPIPE_YEE_MUL_22 (0x0300) +#define IPIPE_YEE_THR (0x0304) +#define IPIPE_YEE_E_GAN (0x0308) +#define IPIPE_YEE_E_THR_1 (0x030C) +#define IPIPE_YEE_E_THR_2 (0x0310) +#define IPIPE_YEE_G_GAN (0x0314) +#define IPIPE_YEE_G_OFT (0x0318) + +#define IPIPE_CAR_EN (0x031C) +#define IPIPE_CAR_TYP (0x0320) +#define IPIPE_CAR_SW (0x0324) +#define IPIPE_CAR_HPF_TYP (0x0328) +#define IPIPE_CAR_HPF_SHF (0x032C) +#define IPIPE_CAR_HPF_THR (0x0330) +#define IPIPE_CAR_GN1_GAN (0x0334) +#define IPIPE_CAR_GN1_SHF (0x0338) +#define IPIPE_CAR_GN1_MIN (0x033C) +#define IPIPE_CAR_GN2_GAN (0x0340) +#define IPIPE_CAR_GN2_SHF (0x0344) +#define IPIPE_CAR_GN2_MIN (0x0348) +#define IPIPE_CGS_EN (0x034C) +#define IPIPE_CGS_GN1_L_THR (0x0350) +#define IPIPE_CGS_GN1_L_GAIN (0x0354) +#define IPIPE_CGS_GN1_L_SHF (0x0358) +#define IPIPE_CGS_GN1_L_MIN (0x035C) +#define IPIPE_CGS_GN1_H_THR (0x0360) +#define IPIPE_CGS_GN1_H_GAIN (0x0364) +#define IPIPE_CGS_GN1_H_SHF (0x0368) +#define IPIPE_CGS_GN1_H_MIN (0x036C) +#define IPIPE_CGS_GN2_L_THR (0x0370) +#define IPIPE_CGS_GN2_L_GAIN (0x0374) +#define IPIPE_CGS_GN2_L_SHF (0x0378) +#define IPIPE_CGS_GN2_L_MIN (0x037C) + +#define IPIPE_BOX_EN (0x0380) +#define IPIPE_BOX_MODE (0x0384) +#define IPIPE_BOX_TYP (0x0388) +#define IPIPE_BOX_SHF (0x038C) +#define IPIPE_BOX_SDR_SAD_H (0x0390) +#define IPIPE_BOX_SDR_SAD_L (0x0394) + +#define IPIPE_HST_EN (0x039C) +#define IPIPE_HST_MODE (0x03A0) +#define IPIPE_HST_SEL (0x03A4) +#define IPIPE_HST_PARA (0x03A8) +#define IPIPE_HST_0_VPS (0x03AC) +#define IPIPE_HST_0_VSZ (0x03B0) +#define IPIPE_HST_0_HPS (0x03B4) +#define IPIPE_HST_0_HSZ (0x03B8) +#define IPIPE_HST_1_VPS (0x03BC) +#define IPIPE_HST_1_VSZ (0x03C0) +#define IPIPE_HST_1_HPS (0x03C4) +#define IPIPE_HST_1_HSZ (0x03C8) +#define IPIPE_HST_2_VPS (0x03CC) +#define IPIPE_HST_2_VSZ (0x03D0) +#define IPIPE_HST_2_HPS (0x03D4) +#define IPIPE_HST_2_HSZ (0x03D8) +#define IPIPE_HST_3_VPS (0x03DC) +#define IPIPE_HST_3_VSZ (0x03E0) +#define IPIPE_HST_3_HPS (0x03E4) +#define IPIPE_HST_3_HSZ (0x03E8) +#define IPIPE_HST_TBL (0x03EC) +#define IPIPE_HST_MUL_R (0x03F0) +#define IPIPE_HST_MUL_GR (0x03F4) +#define IPIPE_HST_MUL_GB (0x03F8) +#define IPIPE_HST_MUL_B (0x03FC) + +#define IPIPE_BSC_EN (0x0400) +#define IPIPE_BSC_MODE (0x0404) +#define IPIPE_BSC_TYP (0x0408) +#define IPIPE_BSC_ROW_VCT (0x040C) +#define IPIPE_BSC_ROW_SHF (0x0410) +#define IPIPE_BSC_ROW_VPO (0x0414) +#define IPIPE_BSC_ROW_VNU (0x0418) +#define IPIPE_BSC_ROW_VSKIP (0x041C) +#define IPIPE_BSC_ROW_HPO (0x0420) +#define IPIPE_BSC_ROW_HNU (0x0424) +#define IPIPE_BSC_ROW_HSKIP (0x0428) +#define IPIPE_BSC_COL_VCT (0x042C) +#define IPIPE_BSC_COL_SHF (0x0430) +#define IPIPE_BSC_COL_VPO (0x0434) +#define IPIPE_BSC_COL_VNU (0x0438) +#define IPIPE_BSC_COL_VSKIP (0x043C) +#define IPIPE_BSC_COL_HPO (0x0440) +#define IPIPE_BSC_COL_HNU (0x0444) +#define IPIPE_BSC_COL_HSKIP (0x0448) + +#define IPIPE_BSC_EN (0x0400) + +/* ISS ISP Resizer register offsets */ +#define RSZ_REVISION (0x0000) +#define RSZ_SYSCONFIG (0x0004) +#define RSZ_SYSCONFIG_RSZB_CLK_EN (1 << 9) +#define RSZ_SYSCONFIG_RSZA_CLK_EN (1 << 8) + +#define RSZ_IN_FIFO_CTRL (0x000C) +#define RSZ_IN_FIFO_CTRL_THRLD_LOW_MASK (0x1FF << 16) +#define RSZ_IN_FIFO_CTRL_THRLD_LOW_SHIFT 16 +#define RSZ_IN_FIFO_CTRL_THRLD_HIGH_MASK (0x1FF << 0) +#define RSZ_IN_FIFO_CTRL_THRLD_HIGH_SHIFT 0 + +#define RSZ_FRACDIV (0x0008) +#define RSZ_FRACDIV_MASK (0xFFFF) + +#define RSZ_SRC_EN (0x0020) +#define RSZ_SRC_EN_SRC_EN (1 << 0) + +#define RSZ_SRC_MODE (0x0024) +#define RSZ_SRC_MODE_OST (1 << 0) +#define RSZ_SRC_MODE_WRT (1 << 1) + +#define RSZ_SRC_FMT0 (0x0028) +#define RSZ_SRC_FMT0_BYPASS (1 << 1) +#define RSZ_SRC_FMT0_SEL (1 << 0) + +#define RSZ_SRC_FMT1 (0x002C) +#define RSZ_SRC_FMT1_IN420 (1 << 1) + +#define RSZ_SRC_VPS (0x0030) +#define RSZ_SRC_VSZ (0x0034) +#define RSZ_SRC_HPS (0x0038) +#define RSZ_SRC_HSZ (0x003C) +#define RSZ_DMA_RZA (0x0040) +#define RSZ_DMA_RZB (0x0044) +#define RSZ_DMA_STA (0x0048) +#define RSZ_GCK_MMR (0x004C) +#define RSZ_GCK_MMR_MMR (1 << 0) + +#define RSZ_GCK_SDR (0x0054) +#define RSZ_GCK_SDR_CORE (1 << 0) + +#define RSZ_IRQ_RZA (0x0058) +#define RSZ_IRQ_RZA_MASK (0x1FFF) + +#define RSZ_IRQ_RZB (0x005C) +#define RSZ_IRQ_RZB_MASK (0x1FFF) + +#define RSZ_YUV_Y_MIN (0x0060) +#define RSZ_YUV_Y_MAX (0x0064) +#define RSZ_YUV_C_MIN (0x0068) +#define RSZ_YUV_C_MAX (0x006C) + +#define RSZ_SEQ (0x0074) +#define RSZ_SEQ_HRVB (1 << 2) +#define RSZ_SEQ_HRVA (1 << 0) + +#define RZA_EN (0x0078) +#define RZA_MODE (0x007C) +#define RZA_MODE_ONE_SHOT (1 << 0) + +#define RZA_420 (0x0080) +#define RZA_I_VPS (0x0084) +#define RZA_I_HPS (0x0088) +#define RZA_O_VSZ (0x008C) +#define RZA_O_HSZ (0x0090) +#define RZA_V_PHS_Y (0x0094) +#define RZA_V_PHS_C (0x0098) +#define RZA_V_DIF (0x009C) +#define RZA_V_TYP (0x00A0) +#define RZA_V_LPF (0x00A4) +#define RZA_H_PHS (0x00A8) +#define RZA_H_DIF (0x00B0) +#define RZA_H_TYP (0x00B4) +#define RZA_H_LPF (0x00B8) +#define RZA_DWN_EN (0x00BC) +#define RZA_SDR_Y_BAD_H (0x00D0) +#define RZA_SDR_Y_BAD_L (0x00D4) +#define RZA_SDR_Y_SAD_H (0x00D8) +#define RZA_SDR_Y_SAD_L (0x00DC) +#define RZA_SDR_Y_OFT (0x00E0) +#define RZA_SDR_Y_PTR_S (0x00E4) +#define RZA_SDR_Y_PTR_E (0x00E8) +#define RZA_SDR_C_BAD_H (0x00EC) +#define RZA_SDR_C_BAD_L (0x00F0) +#define RZA_SDR_C_SAD_H (0x00F4) +#define RZA_SDR_C_SAD_L (0x00F8) +#define RZA_SDR_C_OFT (0x00FC) +#define RZA_SDR_C_PTR_S (0x0100) +#define RZA_SDR_C_PTR_E (0x0104) + +#define RZB_EN (0x0108) +#define RZB_MODE (0x010C) +#define RZB_420 (0x0110) +#define RZB_I_VPS (0x0114) +#define RZB_I_HPS (0x0118) +#define RZB_O_VSZ (0x011C) +#define RZB_O_HSZ (0x0120) + +#define RZB_V_DIF (0x012C) +#define RZB_V_TYP (0x0130) +#define RZB_V_LPF (0x0134) + +#define RZB_H_DIF (0x0140) +#define RZB_H_TYP (0x0144) +#define RZB_H_LPF (0x0148) + +#define RZB_SDR_Y_BAD_H (0x0160) +#define RZB_SDR_Y_BAD_L (0x0164) +#define RZB_SDR_Y_SAD_H (0x0168) +#define RZB_SDR_Y_SAD_L (0x016C) +#define RZB_SDR_Y_OFT (0x0170) +#define RZB_SDR_Y_PTR_S (0x0174) +#define RZB_SDR_Y_PTR_E (0x0178) +#define RZB_SDR_C_BAD_H (0x017C) +#define RZB_SDR_C_BAD_L (0x0180) +#define RZB_SDR_C_SAD_H (0x0184) +#define RZB_SDR_C_SAD_L (0x0188) + +#define RZB_SDR_C_PTR_S (0x0190) +#define RZB_SDR_C_PTR_E (0x0194) + +/* Shared Bitmasks between RZA & RZB */ +#define RSZ_EN_EN (1 << 0) + +#define RSZ_420_CEN (1 << 1) +#define RSZ_420_YEN (1 << 0) + +#define RSZ_I_VPS_MASK (0x1FFF) + +#define RSZ_I_HPS_MASK (0x1FFF) + +#define RSZ_O_VSZ_MASK (0x1FFF) + +#define RSZ_O_HSZ_MASK (0x1FFE) + +#define RSZ_V_PHS_Y_MASK (0x3FFF) + +#define RSZ_V_PHS_C_MASK (0x3FFF) + +#define RSZ_V_DIF_MASK (0x3FFF) + +#define RSZ_V_TYP_C (1 << 1) +#define RSZ_V_TYP_Y (1 << 0) + +#define RSZ_V_LPF_C_MASK (0x3F << 6) +#define RSZ_V_LPF_C_SHIFT 6 +#define RSZ_V_LPF_Y_MASK (0x3F << 0) +#define RSZ_V_LPF_Y_SHIFT 0 + +#define RSZ_H_PHS_MASK (0x3FFF) + +#define RSZ_H_DIF_MASK (0x3FFF) + +#define RSZ_H_TYP_C (1 << 1) +#define RSZ_H_TYP_Y (1 << 0) + +#define RSZ_H_LPF_C_MASK (0x3F << 6) +#define RSZ_H_LPF_C_SHIFT 6 +#define RSZ_H_LPF_Y_MASK (0x3F << 0) +#define RSZ_H_LPF_Y_SHIFT 0 + +#define RSZ_DWN_EN_DWN_EN (1 << 0) + +#endif /* _OMAP4_ISS_REGS_H_ */ diff --git a/include/media/omap4iss.h b/include/media/omap4iss.h new file mode 100644 index 000000000000..0d7620db5e32 --- /dev/null +++ b/include/media/omap4iss.h @@ -0,0 +1,65 @@ +#ifndef ARCH_ARM_PLAT_OMAP4_ISS_H +#define ARCH_ARM_PLAT_OMAP4_ISS_H + +#include + +struct iss_device; + +enum iss_interface_type { + ISS_INTERFACE_CSI2A_PHY1, + ISS_INTERFACE_CSI2B_PHY2, +}; + +/** + * struct iss_csiphy_lane: CSI2 lane position and polarity + * @pos: position of the lane + * @pol: polarity of the lane + */ +struct iss_csiphy_lane { + u8 pos; + u8 pol; +}; + +#define ISS_CSIPHY1_NUM_DATA_LANES 4 +#define ISS_CSIPHY2_NUM_DATA_LANES 1 + +/** + * struct iss_csiphy_lanes_cfg - CSI2 lane configuration + * @data: Configuration of one or two data lanes + * @clk: Clock lane configuration + */ +struct iss_csiphy_lanes_cfg { + struct iss_csiphy_lane data[ISS_CSIPHY1_NUM_DATA_LANES]; + struct iss_csiphy_lane clk; +}; + +/** + * struct iss_csi2_platform_data - CSI2 interface platform data + * @crc: Enable the cyclic redundancy check + * @vpclk_div: Video port output clock control + */ +struct iss_csi2_platform_data { + unsigned crc:1; + unsigned vpclk_div:2; + struct iss_csiphy_lanes_cfg lanecfg; +}; + +struct iss_subdev_i2c_board_info { + struct i2c_board_info *board_info; + int i2c_adapter_id; +}; + +struct iss_v4l2_subdevs_group { + struct iss_subdev_i2c_board_info *subdevs; + enum iss_interface_type interface; + union { + struct iss_csi2_platform_data csi2; + } bus; /* gcc < 4.6.0 chokes on anonymous union initializers */ +}; + +struct iss_platform_data { + struct iss_v4l2_subdevs_group *subdevs; + void (*set_constraints)(struct iss_device *iss, bool enable); +}; + +#endif -- cgit v1.2.3 From d0700c5175b0684c9935ca57deae733c2758667c Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sun, 13 Oct 2013 07:58:43 -0300 Subject: [media] media: Add pad flag MEDIA_PAD_FL_MUST_CONNECT Pads that set this flag must be connected by an active link for the entity to stream. Signed-off-by: Sakari Ailus Acked-by: Sylwester Nawrocki Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/media-ioc-enum-links.xml | 9 +++++++++ include/uapi/linux/media.h | 1 + 2 files changed, 10 insertions(+) (limited to 'Documentation') diff --git a/Documentation/DocBook/media/v4l/media-ioc-enum-links.xml b/Documentation/DocBook/media/v4l/media-ioc-enum-links.xml index 355df43badc5..cf8548556c7d 100644 --- a/Documentation/DocBook/media/v4l/media-ioc-enum-links.xml +++ b/Documentation/DocBook/media/v4l/media-ioc-enum-links.xml @@ -134,6 +134,15 @@ Output pad, relative to the entity. Output pads source data and are origins of links. + + MEDIA_PAD_FL_MUST_CONNECT + If this flag is set and the pad is linked to any other + pad, then at least one of those links must be enabled for the + entity to be able to stream. There could be temporary reasons + (e.g. device configuration dependent) for the pad to need + enabled links even when this flag isn't set; the absence of the + flag doesn't imply there is none. + diff --git a/include/uapi/linux/media.h b/include/uapi/linux/media.h index ed49574ad757..d847c760e8f0 100644 --- a/include/uapi/linux/media.h +++ b/include/uapi/linux/media.h @@ -98,6 +98,7 @@ struct media_entity_desc { #define MEDIA_PAD_FL_SINK (1 << 0) #define MEDIA_PAD_FL_SOURCE (1 << 1) +#define MEDIA_PAD_FL_MUST_CONNECT (1 << 2) struct media_pad_desc { __u32 entity; /* entity ID */ -- cgit v1.2.3 From 8a38db133358f9370e6bb453371e630495c59070 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Thu, 5 Dec 2013 15:38:19 -0300 Subject: [media] doc: no singing Stop that, stop that! You're not going to do a song while I'm here. Signed-off-by: Kees Cook Signed-off-by: Mauro Carvalho Chehab --- Documentation/cgroups/resource_counter.txt | 2 +- Documentation/video4linux/si476x.txt | 2 +- arch/score/lib/checksum.S | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'Documentation') diff --git a/Documentation/cgroups/resource_counter.txt b/Documentation/cgroups/resource_counter.txt index c4d99ed0b418..caa6d662b230 100644 --- a/Documentation/cgroups/resource_counter.txt +++ b/Documentation/cgroups/resource_counter.txt @@ -95,7 +95,7 @@ to work with it. f. u64 res_counter_uncharge_until (struct res_counter *rc, struct res_counter *top, - unsinged long val) + unsigned long val) Almost same as res_cunter_uncharge() but propagation of uncharge stops when rc == top. This is useful when kill a res_coutner in diff --git a/Documentation/video4linux/si476x.txt b/Documentation/video4linux/si476x.txt index 2f9b4875ab8a..616607955aaf 100644 --- a/Documentation/video4linux/si476x.txt +++ b/Documentation/video4linux/si476x.txt @@ -147,7 +147,7 @@ The drivers exposes following files: -------------------------------------------------------------------- 0x12 | readfreq | Current tuned frequency -------------------------------------------------------------------- - 0x14 | freqoff | Singed frequency offset in units of + 0x14 | freqoff | Signed frequency offset in units of | | 2ppm -------------------------------------------------------------------- 0x15 | rssi | Signed value of RSSI in dBuV diff --git a/arch/score/lib/checksum.S b/arch/score/lib/checksum.S index 706157edc7d5..1141f2b4a501 100644 --- a/arch/score/lib/checksum.S +++ b/arch/score/lib/checksum.S @@ -137,7 +137,7 @@ ENTRY(csum_partial) ldi r25, 0 mv r10, r5 cmpi.c r5, 0x8 - blt small_csumcpy /* < 8(singed) bytes to copy */ + blt small_csumcpy /* < 8(signed) bytes to copy */ cmpi.c r5, 0x0 beq out andri.c r25, src, 0x1 /* odd buffer? */ -- cgit v1.2.3 From 7e941a7999fe5ece856c066ba94c929870e30fe0 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 10 Jul 2013 12:05:06 -0300 Subject: [media] v4l: Add media format codes for AHSV8888 on 32-bit busses Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/subdev-formats.xml | 157 +++++++++++++++++++++ include/uapi/linux/v4l2-mediabus.h | 3 + 2 files changed, 160 insertions(+) (limited to 'Documentation') diff --git a/Documentation/DocBook/media/v4l/subdev-formats.xml b/Documentation/DocBook/media/v4l/subdev-formats.xml index f72c1cc93a9b..bfaef5078eb8 100644 --- a/Documentation/DocBook/media/v4l/subdev-formats.xml +++ b/Documentation/DocBook/media/v4l/subdev-formats.xml @@ -2491,6 +2491,163 @@ +
+ HSV/HSL Formats + + Those formats transfer pixel data as RGB values in a cylindrical-coordinate + system using Hue-Saturation-Value or Hue-Saturation-Lightness components. The + format code is made of the following information. + + The hue, saturation, value or lightness and optional alpha + components order code, as encoded in a pixel sample. The only currently + supported value is AHSV. + + The number of bits per component, for each component. The values + can be different for all components. The only currently supported value is 8888. + + The number of bus samples per pixel. Pixels that are wider than + the bus width must be transferred in multiple samples. The only currently + supported value is 1. + The bus width. + For formats where the total number of bits per pixel is smaller + than the number of bus samples per pixel times the bus width, a padding + value stating if the bytes are padded in their most high order bits + (PADHI) or low order bits (PADLO). + For formats where the number of bus samples per pixel is larger + than 1, an endianness value stating if the pixel is transferred MSB first + (BE) or LSB first (LE). + + + + The following table lists existing HSV/HSL formats. + + + HSV/HSL formats + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Identifier + Code + + Data organization + + + + + Bit + 31 + 30 + 29 + 28 + 27 + 26 + 25 + 24 + 23 + 22 + 21 + 20 + 19 + 18 + 17 + 16 + 15 + 14 + 13 + 12 + 11 + 10 + 9 + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1 + 0 + + + + + V4L2_MBUS_FMT_AHSV8888_1X32 + 0x6001 + + a7 + a6 + a5 + a4 + a3 + a2 + a1 + a0 + h7 + h6 + h5 + h4 + h3 + h2 + h1 + h0 + s7 + s6 + s5 + s4 + s3 + s2 + s1 + s0 + v7 + v6 + v5 + v4 + v3 + v2 + v1 + v0 + + + +
+
+
JPEG Compressed Formats diff --git a/include/uapi/linux/v4l2-mediabus.h b/include/uapi/linux/v4l2-mediabus.h index a9601257bb43..b5c3aab6e82c 100644 --- a/include/uapi/linux/v4l2-mediabus.h +++ b/include/uapi/linux/v4l2-mediabus.h @@ -110,6 +110,9 @@ enum v4l2_mbus_pixelcode { /* S5C73M3 sensor specific interleaved UYVY and JPEG */ V4L2_MBUS_FMT_S5C_UYVY_JPEG_1X8 = 0x5001, + + /* HSV - next is 0x6002 */ + V4L2_MBUS_FMT_AHSV8888_1X32 = 0x6001, }; /** -- cgit v1.2.3 From f7074ab348e29c8068a2daaaf89920aebc903a98 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Sun, 18 Aug 2013 16:14:27 -0300 Subject: [media] s5p-jpeg: Add initial device tree support for S5PV210/Exynos4210 SoCs This patch enables the JPEG codec on S5PV210 and Exynos4210 SoCs. There are some differences in newer versions of the JPEG codec IP on SoCs like Exynos4x12 and Exynos5 series and support for them will be added in subsequent patches. Signed-off-by: Sylwester Nawrocki Acked-by: Stephen Warren Signed-off-by: Mauro Carvalho Chehab --- .../devicetree/bindings/media/exynos-jpeg-codec.txt | 11 +++++++++++ drivers/media/platform/s5p-jpeg/jpeg-core.c | 12 +++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/media/exynos-jpeg-codec.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/media/exynos-jpeg-codec.txt b/Documentation/devicetree/bindings/media/exynos-jpeg-codec.txt new file mode 100644 index 000000000000..937b755baf8f --- /dev/null +++ b/Documentation/devicetree/bindings/media/exynos-jpeg-codec.txt @@ -0,0 +1,11 @@ +Samsung S5P/EXYNOS SoC series JPEG codec + +Required properties: + +- compatible : should be one of: + "samsung,s5pv210-jpeg", "samsung,exynos4210-jpeg"; +- reg : address and length of the JPEG codec IP register set; +- interrupts : specifies the JPEG codec IP interrupt; +- clocks : should contain the JPEG codec IP gate clock specifier, from the + common clock bindings; +- clock-names : should contain "jpeg" entry. diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index 091b06af84ea..a4bff8ec982e 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -1428,10 +1429,20 @@ static const struct dev_pm_ops s5p_jpeg_pm_ops = { .runtime_resume = s5p_jpeg_runtime_resume, }; +#ifdef CONFIG_OF +static const struct of_device_id s5p_jpeg_of_match[] = { + { .compatible = "samsung,s5pv210-jpeg" }, + { .compatible = "samsung,exynos4210-jpeg" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, s5p_jpeg_of_match); +#endif + static struct platform_driver s5p_jpeg_driver = { .probe = s5p_jpeg_probe, .remove = s5p_jpeg_remove, .driver = { + .of_match_table = of_match_ptr(s5p_jpeg_of_match), .owner = THIS_MODULE, .name = S5P_JPEG_M2M_NAME, .pm = &s5p_jpeg_pm_ops, @@ -1443,4 +1454,3 @@ module_platform_driver(s5p_jpeg_driver); MODULE_AUTHOR("Andrzej Pietrasiewicz "); MODULE_DESCRIPTION("Samsung JPEG codec driver"); MODULE_LICENSE("GPL"); - -- cgit v1.2.3 From b8da9d3aaff26edcdf88751c6d2763f4337c79e3 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 12 Dec 2013 07:35:22 -0300 Subject: [media] sn9c102: prepare for removal by moving it to staging During the last media summit meeting it was decided to move this driver to staging as the first step to removing it altogether. Most webcams covered by this driver are now supported by gspca. Nobody has the hardware to convert the remaining devices to gspca. This driver needs a major overhaul to have it conform to the latest frameworks and compliancy tests. Without hardware, however, this is next to impossible. Given the fact that this driver seems to be pretty much unused (it has been removed from Fedora several versions ago and nobody complained about that), we decided to drop this driver. This patch moves it to staging. Some time in 2014 we will drop it completely. Signed-off-by: Hans Verkuil Cc: Hans de Goede Cc: Luca Risolia Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/sn9c102.txt | 592 ---- MAINTAINERS | 3 +- drivers/media/usb/Kconfig | 1 - drivers/media/usb/Makefile | 1 - drivers/media/usb/sn9c102/Kconfig | 14 - drivers/media/usb/sn9c102/Makefile | 15 - drivers/media/usb/sn9c102/sn9c102.h | 214 -- drivers/media/usb/sn9c102/sn9c102_config.h | 86 - drivers/media/usb/sn9c102/sn9c102_core.c | 3434 -------------------- drivers/media/usb/sn9c102/sn9c102_devtable.h | 147 - drivers/media/usb/sn9c102/sn9c102_hv7131d.c | 264 -- drivers/media/usb/sn9c102/sn9c102_hv7131r.c | 363 --- drivers/media/usb/sn9c102/sn9c102_mi0343.c | 352 -- drivers/media/usb/sn9c102/sn9c102_mi0360.c | 453 --- drivers/media/usb/sn9c102/sn9c102_mt9v111.c | 260 -- drivers/media/usb/sn9c102/sn9c102_ov7630.c | 626 ---- drivers/media/usb/sn9c102/sn9c102_ov7660.c | 538 --- drivers/media/usb/sn9c102/sn9c102_pas106b.c | 302 -- drivers/media/usb/sn9c102/sn9c102_pas202bcb.c | 335 -- drivers/media/usb/sn9c102/sn9c102_sensor.h | 307 -- drivers/media/usb/sn9c102/sn9c102_tas5110c1b.c | 154 - drivers/media/usb/sn9c102/sn9c102_tas5110d.c | 119 - drivers/media/usb/sn9c102/sn9c102_tas5130d1b.c | 165 - drivers/staging/media/Kconfig | 2 + drivers/staging/media/Makefile | 1 + drivers/staging/media/sn9c102/Kconfig | 17 + drivers/staging/media/sn9c102/Makefile | 15 + drivers/staging/media/sn9c102/sn9c102.h | 214 ++ drivers/staging/media/sn9c102/sn9c102.txt | 592 ++++ drivers/staging/media/sn9c102/sn9c102_config.h | 86 + drivers/staging/media/sn9c102/sn9c102_core.c | 3434 ++++++++++++++++++++ drivers/staging/media/sn9c102/sn9c102_devtable.h | 147 + drivers/staging/media/sn9c102/sn9c102_hv7131d.c | 264 ++ drivers/staging/media/sn9c102/sn9c102_hv7131r.c | 363 +++ drivers/staging/media/sn9c102/sn9c102_mi0343.c | 352 ++ drivers/staging/media/sn9c102/sn9c102_mi0360.c | 453 +++ drivers/staging/media/sn9c102/sn9c102_mt9v111.c | 260 ++ drivers/staging/media/sn9c102/sn9c102_ov7630.c | 626 ++++ drivers/staging/media/sn9c102/sn9c102_ov7660.c | 538 +++ drivers/staging/media/sn9c102/sn9c102_pas106b.c | 302 ++ drivers/staging/media/sn9c102/sn9c102_pas202bcb.c | 335 ++ drivers/staging/media/sn9c102/sn9c102_sensor.h | 307 ++ drivers/staging/media/sn9c102/sn9c102_tas5110c1b.c | 154 + drivers/staging/media/sn9c102/sn9c102_tas5110d.c | 119 + drivers/staging/media/sn9c102/sn9c102_tas5130d1b.c | 165 + 45 files changed, 8747 insertions(+), 8744 deletions(-) delete mode 100644 Documentation/video4linux/sn9c102.txt delete mode 100644 drivers/media/usb/sn9c102/Kconfig delete mode 100644 drivers/media/usb/sn9c102/Makefile delete mode 100644 drivers/media/usb/sn9c102/sn9c102.h delete mode 100644 drivers/media/usb/sn9c102/sn9c102_config.h delete mode 100644 drivers/media/usb/sn9c102/sn9c102_core.c delete mode 100644 drivers/media/usb/sn9c102/sn9c102_devtable.h delete mode 100644 drivers/media/usb/sn9c102/sn9c102_hv7131d.c delete mode 100644 drivers/media/usb/sn9c102/sn9c102_hv7131r.c delete mode 100644 drivers/media/usb/sn9c102/sn9c102_mi0343.c delete mode 100644 drivers/media/usb/sn9c102/sn9c102_mi0360.c delete mode 100644 drivers/media/usb/sn9c102/sn9c102_mt9v111.c delete mode 100644 drivers/media/usb/sn9c102/sn9c102_ov7630.c delete mode 100644 drivers/media/usb/sn9c102/sn9c102_ov7660.c delete mode 100644 drivers/media/usb/sn9c102/sn9c102_pas106b.c delete mode 100644 drivers/media/usb/sn9c102/sn9c102_pas202bcb.c delete mode 100644 drivers/media/usb/sn9c102/sn9c102_sensor.h delete mode 100644 drivers/media/usb/sn9c102/sn9c102_tas5110c1b.c delete mode 100644 drivers/media/usb/sn9c102/sn9c102_tas5110d.c delete mode 100644 drivers/media/usb/sn9c102/sn9c102_tas5130d1b.c create mode 100644 drivers/staging/media/sn9c102/Kconfig create mode 100644 drivers/staging/media/sn9c102/Makefile create mode 100644 drivers/staging/media/sn9c102/sn9c102.h create mode 100644 drivers/staging/media/sn9c102/sn9c102.txt create mode 100644 drivers/staging/media/sn9c102/sn9c102_config.h create mode 100644 drivers/staging/media/sn9c102/sn9c102_core.c create mode 100644 drivers/staging/media/sn9c102/sn9c102_devtable.h create mode 100644 drivers/staging/media/sn9c102/sn9c102_hv7131d.c create mode 100644 drivers/staging/media/sn9c102/sn9c102_hv7131r.c create mode 100644 drivers/staging/media/sn9c102/sn9c102_mi0343.c create mode 100644 drivers/staging/media/sn9c102/sn9c102_mi0360.c create mode 100644 drivers/staging/media/sn9c102/sn9c102_mt9v111.c create mode 100644 drivers/staging/media/sn9c102/sn9c102_ov7630.c create mode 100644 drivers/staging/media/sn9c102/sn9c102_ov7660.c create mode 100644 drivers/staging/media/sn9c102/sn9c102_pas106b.c create mode 100644 drivers/staging/media/sn9c102/sn9c102_pas202bcb.c create mode 100644 drivers/staging/media/sn9c102/sn9c102_sensor.h create mode 100644 drivers/staging/media/sn9c102/sn9c102_tas5110c1b.c create mode 100644 drivers/staging/media/sn9c102/sn9c102_tas5110d.c create mode 100644 drivers/staging/media/sn9c102/sn9c102_tas5130d1b.c (limited to 'Documentation') diff --git a/Documentation/video4linux/sn9c102.txt b/Documentation/video4linux/sn9c102.txt deleted file mode 100644 index b4f67040403a..000000000000 --- a/Documentation/video4linux/sn9c102.txt +++ /dev/null @@ -1,592 +0,0 @@ - - SN9C1xx PC Camera Controllers - Driver for Linux - ============================= - - - Documentation - - - -Index -===== -1. Copyright -2. Disclaimer -3. License -4. Overview and features -5. Module dependencies -6. Module loading -7. Module parameters -8. Optional device control through "sysfs" -9. Supported devices -10. Notes for V4L2 application developers -11. Video frame formats -12. Contact information -13. Credits - - -1. Copyright -============ -Copyright (C) 2004-2007 by Luca Risolia - - -2. Disclaimer -============= -SONiX is a trademark of SONiX Technology Company Limited, inc. -This software is not sponsored or developed by SONiX. - - -3. License -========== -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. - - -4. Overview and features -======================== -This driver attempts to support the video interface of the devices assembling -the SONiX SN9C101, SN9C102, SN9C103, SN9C105 and SN9C120 PC Camera Controllers -("SN9C1xx" from now on). - -The driver relies on the Video4Linux2 and USB core modules. It has been -designed to run properly on SMP systems as well. - -The latest version of the SN9C1xx driver can be found at the following URL: -http://www.linux-projects.org/ - -Some of the features of the driver are: - -- full compliance with the Video4Linux2 API (see also "Notes for V4L2 - application developers" paragraph); -- available mmap or read/poll methods for video streaming through isochronous - data transfers; -- automatic detection of image sensor; -- support for built-in microphone interface; -- support for any window resolutions and optional panning within the maximum - pixel area of image sensor; -- image downscaling with arbitrary scaling factors from 1, 2 and 4 in both - directions (see "Notes for V4L2 application developers" paragraph); -- two different video formats for uncompressed or compressed data in low or - high compression quality (see also "Notes for V4L2 application developers" - and "Video frame formats" paragraphs); -- full support for the capabilities of many of the possible image sensors that - can be connected to the SN9C1xx bridges, including, for instance, red, green, - blue and global gain adjustments and exposure (see "Supported devices" - paragraph for details); -- use of default color settings for sunlight conditions; -- dynamic I/O interface for both SN9C1xx and image sensor control and - monitoring (see "Optional device control through 'sysfs'" paragraph); -- dynamic driver control thanks to various module parameters (see "Module - parameters" paragraph); -- up to 64 cameras can be handled at the same time; they can be connected and - disconnected from the host many times without turning off the computer, if - the system supports hotplugging; -- no known bugs. - - -5. Module dependencies -====================== -For it to work properly, the driver needs kernel support for Video4Linux and -USB. - -The following options of the kernel configuration file must be enabled and -corresponding modules must be compiled: - - # Multimedia devices - # - CONFIG_VIDEO_DEV=m - -To enable advanced debugging functionality on the device through /sysfs: - - # Multimedia devices - # - CONFIG_VIDEO_ADV_DEBUG=y - - # USB support - # - CONFIG_USB=m - -In addition, depending on the hardware being used, the modules below are -necessary: - - # USB Host Controller Drivers - # - CONFIG_USB_EHCI_HCD=m - CONFIG_USB_UHCI_HCD=m - CONFIG_USB_OHCI_HCD=m - -The SN9C103, SN9c105 and SN9C120 controllers also provide a built-in microphone -interface. It is supported by the USB Audio driver thanks to the ALSA API: - - # Sound - # - CONFIG_SOUND=y - - # Advanced Linux Sound Architecture - # - CONFIG_SND=m - - # USB devices - # - CONFIG_SND_USB_AUDIO=m - -And finally: - - # USB Multimedia devices - # - CONFIG_USB_SN9C102=m - - -6. Module loading -================= -To use the driver, it is necessary to load the "sn9c102" module into memory -after every other module required: "videodev", "v4l2_common", "compat_ioctl32", -"usbcore" and, depending on the USB host controller you have, "ehci-hcd", -"uhci-hcd" or "ohci-hcd". - -Loading can be done as shown below: - - [root@localhost home]# modprobe sn9c102 - -Note that the module is called "sn9c102" for historic reasons, although it -does not just support the SN9C102. - -At this point all the devices supported by the driver and connected to the USB -ports should be recognized. You can invoke "dmesg" to analyze kernel messages -and verify that the loading process has gone well: - - [user@localhost home]$ dmesg - -or, to isolate all the kernel messages generated by the driver: - - [user@localhost home]$ dmesg | grep sn9c102 - - -7. Module parameters -==================== -Module parameters are listed below: -------------------------------------------------------------------------------- -Name: video_nr -Type: short array (min = 0, max = 64) -Syntax: <-1|n[,...]> -Description: Specify V4L2 minor mode number: - -1 = use next available - n = use minor number n - You can specify up to 64 cameras this way. - For example: - video_nr=-1,2,-1 would assign minor number 2 to the second - recognized camera and use auto for the first one and for every - other camera. -Default: -1 -------------------------------------------------------------------------------- -Name: force_munmap -Type: bool array (min = 0, max = 64) -Syntax: <0|1[,...]> -Description: Force the application to unmap previously mapped buffer memory - before calling any VIDIOC_S_CROP or VIDIOC_S_FMT ioctl's. Not - all the applications support this feature. This parameter is - specific for each detected camera. - 0 = do not force memory unmapping - 1 = force memory unmapping (save memory) -Default: 0 -------------------------------------------------------------------------------- -Name: frame_timeout -Type: uint array (min = 0, max = 64) -Syntax: <0|n[,...]> -Description: Timeout for a video frame in seconds before returning an I/O - error; 0 for infinity. This parameter is specific for each - detected camera and can be changed at runtime thanks to the - /sys filesystem interface. -Default: 2 -------------------------------------------------------------------------------- -Name: debug -Type: ushort -Syntax: -Description: Debugging information level, from 0 to 3: - 0 = none (use carefully) - 1 = critical errors - 2 = significant information - 3 = more verbose messages - Level 3 is useful for testing only. It also shows some more - information about the hardware being detected. - This parameter can be changed at runtime thanks to the /sys - filesystem interface. -Default: 2 -------------------------------------------------------------------------------- - - -8. Optional device control through "sysfs" [1] -========================================== -If the kernel has been compiled with the CONFIG_VIDEO_ADV_DEBUG option enabled, -it is possible to read and write both the SN9C1xx and the image sensor -registers by using the "sysfs" filesystem interface. - -Every time a supported device is recognized, a write-only file named "green" is -created in the /sys/class/video4linux/videoX directory. You can set the green -channel's gain by writing the desired value to it. The value may range from 0 -to 15 for the SN9C101 or SN9C102 bridges, from 0 to 127 for the SN9C103, -SN9C105 and SN9C120 bridges. -Similarly, only for the SN9C103, SN9C105 and SN9C120 controllers, blue and red -gain control files are available in the same directory, for which accepted -values may range from 0 to 127. - -There are other four entries in the directory above for each registered camera: -"reg", "val", "i2c_reg" and "i2c_val". The first two files control the -SN9C1xx bridge, while the other two control the sensor chip. "reg" and -"i2c_reg" hold the values of the current register index where the following -reading/writing operations are addressed at through "val" and "i2c_val". Their -use is not intended for end-users. Note that "i2c_reg" and "i2c_val" will not -be created if the sensor does not actually support the standard I2C protocol or -its registers are not 8-bit long. Also, remember that you must be logged in as -root before writing to them. - -As an example, suppose we were to want to read the value contained in the -register number 1 of the sensor register table - which is usually the product -identifier - of the camera registered as "/dev/video0": - - [root@localhost #] cd /sys/class/video4linux/video0 - [root@localhost #] echo 1 > i2c_reg - [root@localhost #] cat i2c_val - -Note that "cat" will fail if sensor registers cannot be read. - -Now let's set the green gain's register of the SN9C101 or SN9C102 chips to 2: - - [root@localhost #] echo 0x11 > reg - [root@localhost #] echo 2 > val - -Note that the SN9C1xx always returns 0 when some of its registers are read. -To avoid race conditions, all the I/O accesses to the above files are -serialized. -The sysfs interface also provides the "frame_header" entry, which exports the -frame header of the most recent requested and captured video frame. The header -is always 18-bytes long and is appended to every video frame by the SN9C1xx -controllers. As an example, this additional information can be used by the user -application for implementing auto-exposure features via software. - -The following table describes the frame header exported by the SN9C101 and -SN9C102: - -Byte # Value or bits Description ------- ------------- ----------- -0x00 0xFF Frame synchronisation pattern -0x01 0xFF Frame synchronisation pattern -0x02 0x00 Frame synchronisation pattern -0x03 0xC4 Frame synchronisation pattern -0x04 0xC4 Frame synchronisation pattern -0x05 0x96 Frame synchronisation pattern -0x06 [3:0] Read channel gain control = (1+R_GAIN/8) - [7:4] Blue channel gain control = (1+B_GAIN/8) -0x07 [ 0 ] Compression mode. 0=No compression, 1=Compression enabled - [2:1] Maximum scale factor for compression - [ 3 ] 1 = USB fifo(2K bytes) is full - [ 4 ] 1 = Digital gain is finish - [ 5 ] 1 = Exposure is finish - [7:6] Frame index -0x08 [7:0] Y sum inside Auto-Exposure area (low-byte) -0x09 [7:0] Y sum inside Auto-Exposure area (high-byte) - where Y sum = (R/4 + 5G/16 + B/8) / 32 -0x0A [7:0] Y sum outside Auto-Exposure area (low-byte) -0x0B [7:0] Y sum outside Auto-Exposure area (high-byte) - where Y sum = (R/4 + 5G/16 + B/8) / 128 -0x0C 0xXX Not used -0x0D 0xXX Not used -0x0E 0xXX Not used -0x0F 0xXX Not used -0x10 0xXX Not used -0x11 0xXX Not used - -The following table describes the frame header exported by the SN9C103: - -Byte # Value or bits Description ------- ------------- ----------- -0x00 0xFF Frame synchronisation pattern -0x01 0xFF Frame synchronisation pattern -0x02 0x00 Frame synchronisation pattern -0x03 0xC4 Frame synchronisation pattern -0x04 0xC4 Frame synchronisation pattern -0x05 0x96 Frame synchronisation pattern -0x06 [6:0] Read channel gain control = (1/2+R_GAIN/64) -0x07 [6:0] Blue channel gain control = (1/2+B_GAIN/64) - [7:4] -0x08 [ 0 ] Compression mode. 0=No compression, 1=Compression enabled - [2:1] Maximum scale factor for compression - [ 3 ] 1 = USB fifo(2K bytes) is full - [ 4 ] 1 = Digital gain is finish - [ 5 ] 1 = Exposure is finish - [7:6] Frame index -0x09 [7:0] Y sum inside Auto-Exposure area (low-byte) -0x0A [7:0] Y sum inside Auto-Exposure area (high-byte) - where Y sum = (R/4 + 5G/16 + B/8) / 32 -0x0B [7:0] Y sum outside Auto-Exposure area (low-byte) -0x0C [7:0] Y sum outside Auto-Exposure area (high-byte) - where Y sum = (R/4 + 5G/16 + B/8) / 128 -0x0D [1:0] Audio frame number - [ 2 ] 1 = Audio is recording -0x0E [7:0] Audio summation (low-byte) -0x0F [7:0] Audio summation (high-byte) -0x10 [7:0] Audio sample count -0x11 [7:0] Audio peak data in audio frame - -The AE area (sx, sy, ex, ey) in the active window can be set by programming the -registers 0x1c, 0x1d, 0x1e and 0x1f of the SN9C1xx controllers, where one unit -corresponds to 32 pixels. - -[1] The frame headers exported by the SN9C105 and SN9C120 are not described. - - -9. Supported devices -==================== -None of the names of the companies as well as their products will be mentioned -here. They have never collaborated with the author, so no advertising. - -From the point of view of a driver, what unambiguously identify a device are -its vendor and product USB identifiers. Below is a list of known identifiers of -devices assembling the SN9C1xx PC camera controllers: - -Vendor ID Product ID ---------- ---------- -0x0458 0x7025 -0x045e 0x00f5 -0x045e 0x00f7 -0x0471 0x0327 -0x0471 0x0328 -0x0c45 0x6001 -0x0c45 0x6005 -0x0c45 0x6007 -0x0c45 0x6009 -0x0c45 0x600d -0x0c45 0x6011 -0x0c45 0x6019 -0x0c45 0x6024 -0x0c45 0x6025 -0x0c45 0x6028 -0x0c45 0x6029 -0x0c45 0x602a -0x0c45 0x602b -0x0c45 0x602c -0x0c45 0x602d -0x0c45 0x602e -0x0c45 0x6030 -0x0c45 0x603f -0x0c45 0x6080 -0x0c45 0x6082 -0x0c45 0x6083 -0x0c45 0x6088 -0x0c45 0x608a -0x0c45 0x608b -0x0c45 0x608c -0x0c45 0x608e -0x0c45 0x608f -0x0c45 0x60a0 -0x0c45 0x60a2 -0x0c45 0x60a3 -0x0c45 0x60a8 -0x0c45 0x60aa -0x0c45 0x60ab -0x0c45 0x60ac -0x0c45 0x60ae -0x0c45 0x60af -0x0c45 0x60b0 -0x0c45 0x60b2 -0x0c45 0x60b3 -0x0c45 0x60b8 -0x0c45 0x60ba -0x0c45 0x60bb -0x0c45 0x60bc -0x0c45 0x60be -0x0c45 0x60c0 -0x0c45 0x60c2 -0x0c45 0x60c8 -0x0c45 0x60cc -0x0c45 0x60ea -0x0c45 0x60ec -0x0c45 0x60ef -0x0c45 0x60fa -0x0c45 0x60fb -0x0c45 0x60fc -0x0c45 0x60fe -0x0c45 0x6102 -0x0c45 0x6108 -0x0c45 0x610f -0x0c45 0x6130 -0x0c45 0x6138 -0x0c45 0x613a -0x0c45 0x613b -0x0c45 0x613c -0x0c45 0x613e - -The list above does not imply that all those devices work with this driver: up -until now only the ones that assemble the following pairs of SN9C1xx bridges -and image sensors are supported; kernel messages will always tell you whether -this is the case (see "Module loading" paragraph): - -Image sensor / SN9C1xx bridge | SN9C10[12] SN9C103 SN9C105 SN9C120 -------------------------------------------------------------------------------- -HV7131D Hynix Semiconductor | Yes No No No -HV7131R Hynix Semiconductor | No Yes Yes Yes -MI-0343 Micron Technology | Yes No No No -MI-0360 Micron Technology | No Yes Yes Yes -OV7630 OmniVision Technologies | Yes Yes Yes Yes -OV7660 OmniVision Technologies | No No Yes Yes -PAS106B PixArt Imaging | Yes No No No -PAS202B PixArt Imaging | Yes Yes No No -TAS5110C1B Taiwan Advanced Sensor | Yes No No No -TAS5110D Taiwan Advanced Sensor | Yes No No No -TAS5130D1B Taiwan Advanced Sensor | Yes No No No - -"Yes" means that the pair is supported by the driver, while "No" means that the -pair does not exist or is not supported by the driver. - -Only some of the available control settings of each image sensor are supported -through the V4L2 interface. - -Donations of new models for further testing and support would be much -appreciated. Non-available hardware will not be supported by the author of this -driver. - - -10. Notes for V4L2 application developers -========================================= -This driver follows the V4L2 API specifications. In particular, it enforces two -rules: - -- exactly one I/O method, either "mmap" or "read", is associated with each -file descriptor. Once it is selected, the application must close and reopen the -device to switch to the other I/O method; - -- although it is not mandatory, previously mapped buffer memory should always -be unmapped before calling any "VIDIOC_S_CROP" or "VIDIOC_S_FMT" ioctl's. -The same number of buffers as before will be allocated again to match the size -of the new video frames, so you have to map the buffers again before any I/O -attempts on them. - -Consistently with the hardware limits, this driver also supports image -downscaling with arbitrary scaling factors from 1, 2 and 4 in both directions. -However, the V4L2 API specifications don't correctly define how the scaling -factor can be chosen arbitrarily by the "negotiation" of the "source" and -"target" rectangles. To work around this flaw, we have added the convention -that, during the negotiation, whenever the "VIDIOC_S_CROP" ioctl is issued, the -scaling factor is restored to 1. - -This driver supports two different video formats: the first one is the "8-bit -Sequential Bayer" format and can be used to obtain uncompressed video data -from the device through the current I/O method, while the second one provides -either "raw" compressed video data (without frame headers not related to the -compressed data) or standard JPEG (with frame headers). The compression quality -may vary from 0 to 1 and can be selected or queried thanks to the -VIDIOC_S_JPEGCOMP and VIDIOC_G_JPEGCOMP V4L2 ioctl's. For maximum flexibility, -both the default active video format and the default compression quality -depend on how the image sensor being used is initialized. - - -11. Video frame formats [1] -======================= -The SN9C1xx PC Camera Controllers can send images in two possible video -formats over the USB: either native "Sequential RGB Bayer" or compressed. -The compression is used to achieve high frame rates. With regard to the -SN9C101, SN9C102 and SN9C103, the compression is based on the Huffman encoding -algorithm described below, while with regard to the SN9C105 and SN9C120 the -compression is based on the JPEG standard. -The current video format may be selected or queried from the user application -by calling the VIDIOC_S_FMT or VIDIOC_G_FMT ioctl's, as described in the V4L2 -API specifications. - -The name "Sequential Bayer" indicates the organization of the red, green and -blue pixels in one video frame. Each pixel is associated with a 8-bit long -value and is disposed in memory according to the pattern shown below: - -B[0] G[1] B[2] G[3] ... B[m-2] G[m-1] -G[m] R[m+1] G[m+2] R[m+2] ... G[2m-2] R[2m-1] -... -... B[(n-1)(m-2)] G[(n-1)(m-1)] -... G[n(m-2)] R[n(m-1)] - -The above matrix also represents the sequential or progressive read-out mode of -the (n, m) Bayer color filter array used in many CCD or CMOS image sensors. - -The Huffman compressed video frame consists of a bitstream that encodes for -every R, G, or B pixel the difference between the value of the pixel itself and -some reference pixel value. Pixels are organised in the Bayer pattern and the -Bayer sub-pixels are tracked individually and alternatingly. For example, in -the first line values for the B and G1 pixels are alternatingly encoded, while -in the second line values for the G2 and R pixels are alternatingly encoded. - -The pixel reference value is calculated as follows: -- the 4 top left pixels are encoded in raw uncompressed 8-bit format; -- the value in the top two rows is the value of the pixel left of the current - pixel; -- the value in the left column is the value of the pixel above the current - pixel; -- for all other pixels, the reference value is the average of the value of the - pixel on the left and the value of the pixel above the current pixel; -- there is one code in the bitstream that specifies the value of a pixel - directly (in 4-bit resolution); -- pixel values need to be clamped inside the range [0..255] for proper - decoding. - -The algorithm purely describes the conversion from compressed Bayer code used -in the SN9C101, SN9C102 and SN9C103 chips to uncompressed Bayer. Additional -steps are required to convert this to a color image (i.e. a color interpolation -algorithm). - -The following Huffman codes have been found: -0: +0 (relative to reference pixel value) -100: +4 -101: -4? -1110xxxx: set absolute value to xxxx.0000 -1101: +11 -1111: -11 -11001: +20 -110000: -20 -110001: ??? - these codes are apparently not used - -[1] The Huffman compression algorithm has been reverse-engineered and - documented by Bertrik Sikken. - - -12. Contact information -======================= -The author may be contacted by e-mail at . - -GPG/PGP encrypted e-mail's are accepted. The GPG key ID of the author is -'FCE635A4'; the public 1024-bit key should be available at any keyserver; -the fingerprint is: '88E8 F32F 7244 68BA 3958 5D40 99DA 5D2A FCE6 35A4'. - - -13. Credits -=========== -Many thanks to following persons for their contribute (listed in alphabetical -order): - -- David Anderson for the donation of a webcam; -- Luca Capello for the donation of a webcam; -- Philippe Coval for having helped testing the PAS202BCA image sensor; -- Joao Rodrigo Fuzaro, Joao Limirio, Claudio Filho and Caio Begotti for the - donation of a webcam; -- Dennis Heitmann for the donation of a webcam; -- Jon Hollstrom for the donation of a webcam; -- Nick McGill for the donation of a webcam; -- Carlos Eduardo Medaglia Dyonisio, who added the support for the PAS202BCB - image sensor; -- Stefano Mozzi, who donated 45 EU; -- Andrew Pearce for the donation of a webcam; -- John Pullan for the donation of a webcam; -- Bertrik Sikken, who reverse-engineered and documented the Huffman compression - algorithm used in the SN9C101, SN9C102 and SN9C103 controllers and - implemented the first decoder; -- Ronny Standke for the donation of a webcam; -- Mizuno Takafumi for the donation of a webcam; -- an "anonymous" donator (who didn't want his name to be revealed) for the - donation of a webcam. -- an anonymous donator for the donation of four webcams and two boards with ten - image sensors. diff --git a/MAINTAINERS b/MAINTAINERS index 71399a2502a3..ac8642602821 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9053,8 +9053,7 @@ L: linux-media@vger.kernel.org T: git git://linuxtv.org/media_tree.git W: http://www.linux-projects.org S: Maintained -F: Documentation/video4linux/sn9c102.txt -F: drivers/media/usb/sn9c102/ +F: drivers/staging/media/sn9c102/ USB SUBSYSTEM M: Greg Kroah-Hartman diff --git a/drivers/media/usb/Kconfig b/drivers/media/usb/Kconfig index cfe8056b91aa..39d824e2bb69 100644 --- a/drivers/media/usb/Kconfig +++ b/drivers/media/usb/Kconfig @@ -17,7 +17,6 @@ source "drivers/media/usb/cpia2/Kconfig" source "drivers/media/usb/zr364xx/Kconfig" source "drivers/media/usb/stkwebcam/Kconfig" source "drivers/media/usb/s2255/Kconfig" -source "drivers/media/usb/sn9c102/Kconfig" source "drivers/media/usb/usbtv/Kconfig" endif diff --git a/drivers/media/usb/Makefile b/drivers/media/usb/Makefile index 0935f47497a6..7ac4b143dce8 100644 --- a/drivers/media/usb/Makefile +++ b/drivers/media/usb/Makefile @@ -10,7 +10,6 @@ obj-$(CONFIG_USB_VIDEO_CLASS) += uvc/ obj-$(CONFIG_USB_GSPCA) += gspca/ obj-$(CONFIG_USB_PWC) += pwc/ obj-$(CONFIG_VIDEO_CPIA2) += cpia2/ -obj-$(CONFIG_USB_SN9C102) += sn9c102/ obj-$(CONFIG_VIDEO_AU0828) += au0828/ obj-$(CONFIG_VIDEO_HDPVR) += hdpvr/ obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2/ diff --git a/drivers/media/usb/sn9c102/Kconfig b/drivers/media/usb/sn9c102/Kconfig deleted file mode 100644 index 6ebaf2940d06..000000000000 --- a/drivers/media/usb/sn9c102/Kconfig +++ /dev/null @@ -1,14 +0,0 @@ -config USB_SN9C102 - tristate "USB SN9C1xx PC Camera Controller support (DEPRECATED)" - depends on VIDEO_V4L2 - ---help--- - This driver is DEPRECATED please use the gspca sonixb and - sonixj modules instead. - - Say Y here if you want support for cameras based on SONiX SN9C101, - SN9C102, SN9C103, SN9C105 and SN9C120 PC Camera Controllers. - - See for more info. - - To compile this driver as a module, choose M here: the - module will be called sn9c102. diff --git a/drivers/media/usb/sn9c102/Makefile b/drivers/media/usb/sn9c102/Makefile deleted file mode 100644 index 7ecd5a90c7c9..000000000000 --- a/drivers/media/usb/sn9c102/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -sn9c102-objs := sn9c102_core.o \ - sn9c102_hv7131d.o \ - sn9c102_hv7131r.o \ - sn9c102_mi0343.o \ - sn9c102_mi0360.o \ - sn9c102_mt9v111.o \ - sn9c102_ov7630.o \ - sn9c102_ov7660.o \ - sn9c102_pas106b.o \ - sn9c102_pas202bcb.o \ - sn9c102_tas5110c1b.o \ - sn9c102_tas5110d.o \ - sn9c102_tas5130d1b.o - -obj-$(CONFIG_USB_SN9C102) += sn9c102.o diff --git a/drivers/media/usb/sn9c102/sn9c102.h b/drivers/media/usb/sn9c102/sn9c102.h deleted file mode 100644 index 8a917f060503..000000000000 --- a/drivers/media/usb/sn9c102/sn9c102.h +++ /dev/null @@ -1,214 +0,0 @@ -/*************************************************************************** - * V4L2 driver for SN9C1xx PC Camera Controllers * - * * - * Copyright (C) 2004-2006 by Luca Risolia * - * * - * 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 _SN9C102_H_ -#define _SN9C102_H_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "sn9c102_config.h" -#include "sn9c102_sensor.h" -#include "sn9c102_devtable.h" - - -enum sn9c102_frame_state { - F_UNUSED, - F_QUEUED, - F_GRABBING, - F_DONE, - F_ERROR, -}; - -struct sn9c102_frame_t { - void* bufmem; - struct v4l2_buffer buf; - enum sn9c102_frame_state state; - struct list_head frame; - unsigned long vma_use_count; -}; - -enum sn9c102_dev_state { - DEV_INITIALIZED = 0x01, - DEV_DISCONNECTED = 0x02, - DEV_MISCONFIGURED = 0x04, -}; - -enum sn9c102_io_method { - IO_NONE, - IO_READ, - IO_MMAP, -}; - -enum sn9c102_stream_state { - STREAM_OFF, - STREAM_INTERRUPT, - STREAM_ON, -}; - -typedef char sn9c102_sof_header_t[62]; - -struct sn9c102_sof_t { - sn9c102_sof_header_t header; - u16 bytesread; -}; - -struct sn9c102_sysfs_attr { - u16 reg, i2c_reg; - sn9c102_sof_header_t frame_header; -}; - -struct sn9c102_module_param { - u8 force_munmap; - u16 frame_timeout; -}; - -static DEFINE_MUTEX(sn9c102_sysfs_lock); -static DECLARE_RWSEM(sn9c102_dev_lock); - -struct sn9c102_device { - struct video_device* v4ldev; - - struct v4l2_device v4l2_dev; - - enum sn9c102_bridge bridge; - struct sn9c102_sensor sensor; - - struct usb_device* usbdev; - struct urb* urb[SN9C102_URBS]; - void* transfer_buffer[SN9C102_URBS]; - u8* control_buffer; - - struct sn9c102_frame_t *frame_current, frame[SN9C102_MAX_FRAMES]; - struct list_head inqueue, outqueue; - u32 frame_count, nbuffers, nreadbuffers; - - enum sn9c102_io_method io; - enum sn9c102_stream_state stream; - - struct v4l2_jpegcompression compression; - - struct sn9c102_sysfs_attr sysfs; - struct sn9c102_sof_t sof; - u16 reg[384]; - - struct sn9c102_module_param module_param; - - struct kref kref; - enum sn9c102_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 sn9c102_device* -sn9c102_match_id(struct sn9c102_device* cam, const struct usb_device_id *id) -{ - return usb_match_id(usb_ifnum_to_if(cam->usbdev, 0), id) ? cam : NULL; -} - - -void -sn9c102_attach_sensor(struct sn9c102_device* cam, - const struct sn9c102_sensor* sensor) -{ - memcpy(&cam->sensor, sensor, sizeof(struct sn9c102_sensor)); -} - - -enum sn9c102_bridge -sn9c102_get_bridge(struct sn9c102_device* cam) -{ - return cam->bridge; -} - - -struct sn9c102_sensor* sn9c102_get_sensor(struct sn9c102_device* cam) -{ - return &cam->sensor; -} - -/*****************************************************************************/ - -#undef DBG -#undef KDBG -#ifdef SN9C102_DEBUG -# define DBG(level, fmt, args...) \ -do { \ - if (debug >= (level)) { \ - if ((level) == 1) \ - dev_err(&cam->usbdev->dev, fmt "\n", ## args); \ - else if ((level) == 2) \ - dev_info(&cam->usbdev->dev, fmt "\n", ## args); \ - else if ((level) >= 3) \ - dev_info(&cam->usbdev->dev, "[%s:%d] " fmt "\n", \ - __func__, __LINE__ , ## args); \ - } \ -} while (0) -# define V4LDBG(level, name, cmd) \ -do { \ - if (debug >= (level)) \ - v4l_printk_ioctl(name, cmd); \ -} while (0) -# define KDBG(level, fmt, args...) \ -do { \ - if (debug >= (level)) { \ - if ((level) == 1 || (level) == 2) \ - pr_info("sn9c102: " fmt "\n", ## args); \ - else if ((level) == 3) \ - pr_debug("sn9c102: [%s:%d] " fmt "\n", \ - __func__, __LINE__ , ## args); \ - } \ -} while (0) -#else -# define DBG(level, fmt, args...) do {;} while(0) -# define V4LDBG(level, name, cmd) do {;} while(0) -# define KDBG(level, fmt, args...) do {;} while(0) -#endif - -#undef PDBG -#define PDBG(fmt, args...) \ -dev_info(&cam->usbdev->dev, "[%s:%s:%d] " fmt "\n", __FILE__, __func__, \ - __LINE__ , ## args) - -#undef PDBGG -#define PDBGG(fmt, args...) do {;} while(0) /* placeholder */ - -#endif /* _SN9C102_H_ */ diff --git a/drivers/media/usb/sn9c102/sn9c102_config.h b/drivers/media/usb/sn9c102/sn9c102_config.h deleted file mode 100644 index 0f4e0378b071..000000000000 --- a/drivers/media/usb/sn9c102/sn9c102_config.h +++ /dev/null @@ -1,86 +0,0 @@ -/*************************************************************************** - * Global parameters for the V4L2 driver for SN9C1xx PC Camera Controllers * - * * - * Copyright (C) 2007 by Luca Risolia * - * * - * 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 _SN9C102_CONFIG_H_ -#define _SN9C102_CONFIG_H_ - -#include -#include - -#define SN9C102_DEBUG -#define SN9C102_DEBUG_LEVEL 2 -#define SN9C102_MAX_DEVICES 64 -#define SN9C102_PRESERVE_IMGSCALE 0 -#define SN9C102_FORCE_MUNMAP 0 -#define SN9C102_MAX_FRAMES 32 -#define SN9C102_URBS 2 -#define SN9C102_ISO_PACKETS 7 -#define SN9C102_ALTERNATE_SETTING 8 -#define SN9C102_URB_TIMEOUT msecs_to_jiffies(2 * SN9C102_ISO_PACKETS) -#define SN9C102_CTRL_TIMEOUT 300 -#define SN9C102_FRAME_TIMEOUT 0 - -/*****************************************************************************/ - -static const u8 SN9C102_Y_QTABLE0[64] = { - 8, 5, 5, 8, 12, 20, 25, 30, - 6, 6, 7, 9, 13, 29, 30, 27, - 7, 6, 8, 12, 20, 28, 34, 28, - 7, 8, 11, 14, 25, 43, 40, 31, - 9, 11, 18, 28, 34, 54, 51, 38, - 12, 17, 27, 32, 40, 52, 56, 46, - 24, 32, 39, 43, 51, 60, 60, 50, - 36, 46, 47, 49, 56, 50, 51, 49 -}; - -static const u8 SN9C102_UV_QTABLE0[64] = { - 8, 9, 12, 23, 49, 49, 49, 49, - 9, 10, 13, 33, 49, 49, 49, 49, - 12, 13, 28, 49, 49, 49, 49, 49, - 23, 33, 49, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 49 -}; - -static const u8 SN9C102_Y_QTABLE1[64] = { - 16, 11, 10, 16, 24, 40, 51, 61, - 12, 12, 14, 19, 26, 58, 60, 55, - 14, 13, 16, 24, 40, 57, 69, 56, - 14, 17, 22, 29, 51, 87, 80, 62, - 18, 22, 37, 56, 68, 109, 103, 77, - 24, 35, 55, 64, 81, 104, 113, 92, - 49, 64, 78, 87, 103, 121, 120, 101, - 72, 92, 95, 98, 112, 100, 103, 99 -}; - -static const u8 SN9C102_UV_QTABLE1[64] = { - 17, 18, 24, 47, 99, 99, 99, 99, - 18, 21, 26, 66, 99, 99, 99, 99, - 24, 26, 56, 99, 99, 99, 99, 99, - 47, 66, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99 -}; - -#endif /* _SN9C102_CONFIG_H_ */ diff --git a/drivers/media/usb/sn9c102/sn9c102_core.c b/drivers/media/usb/sn9c102/sn9c102_core.c deleted file mode 100644 index 2cb44de2b92c..000000000000 --- a/drivers/media/usb/sn9c102/sn9c102_core.c +++ /dev/null @@ -1,3434 +0,0 @@ -/*************************************************************************** - * V4L2 driver for SN9C1xx PC Camera Controllers * - * * - * Copyright (C) 2004-2007 by Luca Risolia * - * * - * 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. * - ***************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "sn9c102.h" - -/*****************************************************************************/ - -#define SN9C102_MODULE_NAME "V4L2 driver for SN9C1xx PC Camera Controllers" -#define SN9C102_MODULE_ALIAS "sn9c1xx" -#define SN9C102_MODULE_AUTHOR "(C) 2004-2007 Luca Risolia" -#define SN9C102_AUTHOR_EMAIL "" -#define SN9C102_MODULE_LICENSE "GPL" -#define SN9C102_MODULE_VERSION "1:1.48" - -/*****************************************************************************/ - -MODULE_DEVICE_TABLE(usb, sn9c102_id_table); - -MODULE_AUTHOR(SN9C102_MODULE_AUTHOR " " SN9C102_AUTHOR_EMAIL); -MODULE_DESCRIPTION(SN9C102_MODULE_NAME); -MODULE_ALIAS(SN9C102_MODULE_ALIAS); -MODULE_VERSION(SN9C102_MODULE_VERSION); -MODULE_LICENSE(SN9C102_MODULE_LICENSE); - -static short video_nr[] = {[0 ... SN9C102_MAX_DEVICES-1] = -1}; -module_param_array(video_nr, short, NULL, 0444); -MODULE_PARM_DESC(video_nr, - " <-1|n[,...]>" - "\nSpecify 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(SN9C102_MAX_DEVICES) - " cameras this way." - "\nFor example:" - "\nvideo_nr=-1,2,-1 would assign minor number 2 to" - "\nthe second camera and use auto for the first" - "\none and for every other camera." - "\n"); - -static bool force_munmap[] = {[0 ... SN9C102_MAX_DEVICES-1] = - SN9C102_FORCE_MUNMAP}; -module_param_array(force_munmap, bool, NULL, 0444); -MODULE_PARM_DESC(force_munmap, - " <0|1[,...]>" - "\nForce 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." - "\n0 = do not force memory unmapping" - "\n1 = force memory unmapping (save memory)" - "\nDefault value is "__MODULE_STRING(SN9C102_FORCE_MUNMAP)"." - "\n"); - -static unsigned int frame_timeout[] = {[0 ... SN9C102_MAX_DEVICES-1] = - SN9C102_FRAME_TIMEOUT}; -module_param_array(frame_timeout, uint, NULL, 0644); -MODULE_PARM_DESC(frame_timeout, - " <0|n[,...]>" - "\nTimeout for a video frame in seconds before" - "\nreturning an I/O error; 0 for infinity." - "\nThis parameter is specific for each detected camera." - "\nDefault value is "__MODULE_STRING(SN9C102_FRAME_TIMEOUT)"." - "\n"); - -#ifdef SN9C102_DEBUG -static unsigned short debug = SN9C102_DEBUG_LEVEL; -module_param(debug, ushort, 0644); -MODULE_PARM_DESC(debug, - " " - "\nDebugging 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." - "\nDefault value is "__MODULE_STRING(SN9C102_DEBUG_LEVEL)"." - "\n"); -#endif - -/* - Add the probe entries to this table. Be sure to add the entry in the right - place, since, on failure, the next probing routine is called according to - the order of the list below, from top to bottom. -*/ -static int (*sn9c102_sensor_table[])(struct sn9c102_device *) = { - &sn9c102_probe_hv7131d, /* strong detection based on SENSOR ids */ - &sn9c102_probe_hv7131r, /* strong detection based on SENSOR ids */ - &sn9c102_probe_mi0343, /* strong detection based on SENSOR ids */ - &sn9c102_probe_mi0360, /* strong detection based on SENSOR ids */ - &sn9c102_probe_mt9v111, /* strong detection based on SENSOR ids */ - &sn9c102_probe_pas106b, /* strong detection based on SENSOR ids */ - &sn9c102_probe_pas202bcb, /* strong detection based on SENSOR ids */ - &sn9c102_probe_ov7630, /* strong detection based on SENSOR ids */ - &sn9c102_probe_ov7660, /* strong detection based on SENSOR ids */ - &sn9c102_probe_tas5110c1b, /* detection based on USB pid/vid */ - &sn9c102_probe_tas5110d, /* detection based on USB pid/vid */ - &sn9c102_probe_tas5130d1b, /* detection based on USB pid/vid */ -}; - -/*****************************************************************************/ - -static u32 -sn9c102_request_buffers(struct sn9c102_device* cam, u32 count, - enum sn9c102_io_method io) -{ - struct v4l2_pix_format* p = &(cam->sensor.pix_format); - struct v4l2_rect* r = &(cam->sensor.cropcap.bounds); - 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 > SN9C102_MAX_FRAMES) - count = SN9C102_MAX_FRAMES; - - if (cam->bridge == BRIDGE_SN9C105 || cam->bridge == BRIDGE_SN9C120) - imagesize += 589 + 2; /* length of JPEG header + EOI marker */ - - 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 = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - } - - return cam->nbuffers; -} - - -static void sn9c102_release_buffers(struct sn9c102_device* cam) -{ - if (cam->nbuffers) { - vfree(cam->frame[0].bufmem); - cam->nbuffers = 0; - } - cam->frame_current = NULL; -} - - -static void sn9c102_empty_framequeues(struct sn9c102_device* cam) -{ - u32 i; - - INIT_LIST_HEAD(&cam->inqueue); - INIT_LIST_HEAD(&cam->outqueue); - - for (i = 0; i < SN9C102_MAX_FRAMES; i++) { - cam->frame[i].state = F_UNUSED; - cam->frame[i].buf.bytesused = 0; - } -} - - -static void sn9c102_requeue_outqueue(struct sn9c102_device* cam) -{ - struct sn9c102_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 sn9c102_queue_unusedframes(struct sn9c102_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); - } -} - -/*****************************************************************************/ - -/* - Write a sequence of count value/register pairs. Returns -1 after the first - failed write, or 0 for no errors. -*/ -int sn9c102_write_regs(struct sn9c102_device* cam, const u8 valreg[][2], - int count) -{ - struct usb_device* udev = cam->usbdev; - u8* buff = cam->control_buffer; - int i, res; - - for (i = 0; i < count; i++) { - u8 index = valreg[i][1]; - - /* - index is a u8, so it must be <256 and can't be out of range. - If we put in a check anyway, gcc annoys us with a warning - hat our check is useless. People get all uppity when they - see warnings in the kernel compile. - */ - - *buff = valreg[i][0]; - - res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, - 0x41, index, 0, buff, 1, - SN9C102_CTRL_TIMEOUT); - - if (res < 0) { - DBG(3, "Failed to write a register (value 0x%02X, " - "index 0x%02X, error %d)", *buff, index, res); - return -1; - } - - cam->reg[index] = *buff; - } - - return 0; -} - - -int sn9c102_write_reg(struct sn9c102_device* cam, u8 value, u16 index) -{ - struct usb_device* udev = cam->usbdev; - u8* buff = cam->control_buffer; - int res; - - if (index >= ARRAY_SIZE(cam->reg)) - return -1; - - *buff = value; - - res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41, - index, 0, buff, 1, SN9C102_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; - } - - cam->reg[index] = value; - - return 0; -} - - -/* NOTE: with the SN9C10[123] reading some registers always returns 0 */ -int sn9c102_read_reg(struct sn9c102_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, - index, 0, buff, 1, SN9C102_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; -} - - -int sn9c102_pread_reg(struct sn9c102_device* cam, u16 index) -{ - if (index >= ARRAY_SIZE(cam->reg)) - return -1; - - return cam->reg[index]; -} - - -static int -sn9c102_i2c_wait(struct sn9c102_device* cam, - const struct sn9c102_sensor* sensor) -{ - int i, r; - - for (i = 1; i <= 5; i++) { - r = sn9c102_read_reg(cam, 0x08); - if (r < 0) - return -EIO; - if (r & 0x04) - return 0; - if (sensor->frequency & SN9C102_I2C_400KHZ) - udelay(5*16); - else - udelay(16*16); - } - return -EBUSY; -} - - -static int -sn9c102_i2c_detect_read_error(struct sn9c102_device* cam, - const struct sn9c102_sensor* sensor) -{ - int r , err = 0; - - r = sn9c102_read_reg(cam, 0x08); - if (r < 0) - err += r; - - if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102) { - if (!(r & 0x08)) - err += -1; - } else { - if (r & 0x08) - err += -1; - } - - return err ? -EIO : 0; -} - - -static int -sn9c102_i2c_detect_write_error(struct sn9c102_device* cam, - const struct sn9c102_sensor* sensor) -{ - int r; - r = sn9c102_read_reg(cam, 0x08); - return (r < 0 || (r >= 0 && (r & 0x08))) ? -EIO : 0; -} - - -int -sn9c102_i2c_try_raw_read(struct sn9c102_device* cam, - const struct sn9c102_sensor* sensor, u8 data0, - u8 data1, u8 n, u8 buffer[]) -{ - struct usb_device* udev = cam->usbdev; - u8* data = cam->control_buffer; - int i = 0, err = 0, res; - - /* Write cycle */ - data[0] = ((sensor->interface == SN9C102_I2C_2WIRES) ? 0x80 : 0) | - ((sensor->frequency & SN9C102_I2C_400KHZ) ? 0x01 : 0) | 0x10; - data[1] = data0; /* I2C slave id */ - data[2] = data1; /* address */ - data[7] = 0x10; - res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41, - 0x08, 0, data, 8, SN9C102_CTRL_TIMEOUT); - if (res < 0) - err += res; - - err += sn9c102_i2c_wait(cam, sensor); - - /* Read cycle - n bytes */ - data[0] = ((sensor->interface == SN9C102_I2C_2WIRES) ? 0x80 : 0) | - ((sensor->frequency & SN9C102_I2C_400KHZ) ? 0x01 : 0) | - (n << 4) | 0x02; - data[1] = data0; - data[7] = 0x10; - res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41, - 0x08, 0, data, 8, SN9C102_CTRL_TIMEOUT); - if (res < 0) - err += res; - - err += sn9c102_i2c_wait(cam, sensor); - - /* The first read byte will be placed in data[4] */ - res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, 0xc1, - 0x0a, 0, data, 5, SN9C102_CTRL_TIMEOUT); - if (res < 0) - err += res; - - err += sn9c102_i2c_detect_read_error(cam, sensor); - - PDBGG("I2C read: address 0x%02X, first read byte: 0x%02X", data1, - data[4]); - - if (err) { - DBG(3, "I2C read failed for %s image sensor", sensor->name); - return -1; - } - - if (buffer) - for (i = 0; i < n && i < 5; i++) - buffer[n-i-1] = data[4-i]; - - return (int)data[4]; -} - - -int -sn9c102_i2c_try_raw_write(struct sn9c102_device* cam, - const struct sn9c102_sensor* sensor, u8 n, u8 data0, - u8 data1, u8 data2, u8 data3, u8 data4, u8 data5) -{ - struct usb_device* udev = cam->usbdev; - u8* data = cam->control_buffer; - int err = 0, res; - - /* Write cycle. It usually is address + value */ - data[0] = ((sensor->interface == SN9C102_I2C_2WIRES) ? 0x80 : 0) | - ((sensor->frequency & SN9C102_I2C_400KHZ) ? 0x01 : 0) - | ((n - 1) << 4); - data[1] = data0; - data[2] = data1; - data[3] = data2; - data[4] = data3; - data[5] = data4; - data[6] = data5; - data[7] = 0x17; - res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41, - 0x08, 0, data, 8, SN9C102_CTRL_TIMEOUT); - if (res < 0) - err += res; - - err += sn9c102_i2c_wait(cam, sensor); - err += sn9c102_i2c_detect_write_error(cam, sensor); - - if (err) - DBG(3, "I2C write failed for %s image sensor", sensor->name); - - PDBGG("I2C raw write: %u bytes, data0 = 0x%02X, data1 = 0x%02X, " - "data2 = 0x%02X, data3 = 0x%02X, data4 = 0x%02X, data5 = 0x%02X", - n, data0, data1, data2, data3, data4, data5); - - return err ? -1 : 0; -} - - -int -sn9c102_i2c_try_read(struct sn9c102_device* cam, - const struct sn9c102_sensor* sensor, u8 address) -{ - return sn9c102_i2c_try_raw_read(cam, sensor, sensor->i2c_slave_id, - address, 1, NULL); -} - - -static int sn9c102_i2c_try_write(struct sn9c102_device* cam, - const struct sn9c102_sensor* sensor, - u8 address, u8 value) -{ - return sn9c102_i2c_try_raw_write(cam, sensor, 3, - sensor->i2c_slave_id, address, - value, 0, 0, 0); -} - - -int sn9c102_i2c_read(struct sn9c102_device* cam, u8 address) -{ - return sn9c102_i2c_try_read(cam, &cam->sensor, address); -} - - -int sn9c102_i2c_write(struct sn9c102_device* cam, u8 address, u8 value) -{ - return sn9c102_i2c_try_write(cam, &cam->sensor, address, value); -} - -/*****************************************************************************/ - -static size_t sn9c102_sof_length(struct sn9c102_device* cam) -{ - switch (cam->bridge) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - return 12; - case BRIDGE_SN9C103: - return 18; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - return 62; - } - - return 0; -} - - -static void* -sn9c102_find_sof_header(struct sn9c102_device* cam, void* mem, size_t len) -{ - static const char marker[6] = {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96}; - const char *m = mem; - size_t soflen = 0, i, j; - - soflen = sn9c102_sof_length(cam); - - for (i = 0; i < len; i++) { - size_t b; - - /* Read the variable part of the header */ - if (unlikely(cam->sof.bytesread >= sizeof(marker))) { - cam->sof.header[cam->sof.bytesread] = *(m+i); - if (++cam->sof.bytesread == soflen) { - cam->sof.bytesread = 0; - return mem + i; - } - continue; - } - - /* Search for the SOF marker (fixed part) in the header */ - for (j = 0, b=cam->sof.bytesread; j+b < sizeof(marker); j++) { - if (unlikely(i+j == len)) - return NULL; - if (*(m+i+j) == marker[cam->sof.bytesread]) { - cam->sof.header[cam->sof.bytesread] = *(m+i+j); - if (++cam->sof.bytesread == sizeof(marker)) { - PDBGG("Bytes to analyze: %zd. SOF " - "starts at byte #%zd", len, i); - i += j+1; - break; - } - } else { - cam->sof.bytesread = 0; - break; - } - } - } - - return NULL; -} - - -static void* -sn9c102_find_eof_header(struct sn9c102_device* cam, void* mem, size_t len) -{ - static const u8 eof_header[4][4] = { - {0x00, 0x00, 0x00, 0x00}, - {0x40, 0x00, 0x00, 0x00}, - {0x80, 0x00, 0x00, 0x00}, - {0xc0, 0x00, 0x00, 0x00}, - }; - size_t i, j; - - /* The EOF header does not exist in compressed data */ - if (cam->sensor.pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X || - cam->sensor.pix_format.pixelformat == V4L2_PIX_FMT_JPEG) - return NULL; - - /* - The EOF header might cross the packet boundary, but this is not a - problem, since the end of a frame is determined by checking its size - in the first place. - */ - for (i = 0; (len >= 4) && (i <= len - 4); i++) - for (j = 0; j < ARRAY_SIZE(eof_header); j++) - if (!memcmp(mem + i, eof_header[j], 4)) - return mem + i; - - return NULL; -} - - -static void -sn9c102_write_jpegheader(struct sn9c102_device* cam, struct sn9c102_frame_t* f) -{ - static const u8 jpeg_header[589] = { - 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x06, 0x04, 0x05, - 0x06, 0x05, 0x04, 0x06, 0x06, 0x05, 0x06, 0x07, 0x07, 0x06, - 0x08, 0x0a, 0x10, 0x0a, 0x0a, 0x09, 0x09, 0x0a, 0x14, 0x0e, - 0x0f, 0x0c, 0x10, 0x17, 0x14, 0x18, 0x18, 0x17, 0x14, 0x16, - 0x16, 0x1a, 0x1d, 0x25, 0x1f, 0x1a, 0x1b, 0x23, 0x1c, 0x16, - 0x16, 0x20, 0x2c, 0x20, 0x23, 0x26, 0x27, 0x29, 0x2a, 0x29, - 0x19, 0x1f, 0x2d, 0x30, 0x2d, 0x28, 0x30, 0x25, 0x28, 0x29, - 0x28, 0x01, 0x07, 0x07, 0x07, 0x0a, 0x08, 0x0a, 0x13, 0x0a, - 0x0a, 0x13, 0x28, 0x1a, 0x16, 0x1a, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0xff, 0xc4, 0x01, 0xa2, - 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, - 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x01, - 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, - 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x10, 0x00, - 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, - 0x04, 0x00, 0x00, 0x01, 0x7d, 0x01, 0x02, 0x03, 0x00, 0x04, - 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, - 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23, - 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, - 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25, - 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, - 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, - 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, - 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, - 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, - 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, - 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, - 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, - 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, - 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, 0xe3, - 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, - 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0x11, 0x00, 0x02, - 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, - 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, - 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, - 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1, - 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1, - 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, - 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, - 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, - 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, - 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, - 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, - 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, - 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, - 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, - 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, - 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2, 0xe3, - 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, - 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 0xc0, 0x00, 0x11, - 0x08, 0x01, 0xe0, 0x02, 0x80, 0x03, 0x01, 0x21, 0x00, 0x02, - 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xda, 0x00, 0x0c, 0x03, - 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00 - }; - u8 *pos = f->bufmem; - - memcpy(pos, jpeg_header, sizeof(jpeg_header)); - *(pos + 6) = 0x00; - *(pos + 7 + 64) = 0x01; - if (cam->compression.quality == 0) { - memcpy(pos + 7, SN9C102_Y_QTABLE0, 64); - memcpy(pos + 8 + 64, SN9C102_UV_QTABLE0, 64); - } else if (cam->compression.quality == 1) { - memcpy(pos + 7, SN9C102_Y_QTABLE1, 64); - memcpy(pos + 8 + 64, SN9C102_UV_QTABLE1, 64); - } - *(pos + 564) = cam->sensor.pix_format.width & 0xFF; - *(pos + 563) = (cam->sensor.pix_format.width >> 8) & 0xFF; - *(pos + 562) = cam->sensor.pix_format.height & 0xFF; - *(pos + 561) = (cam->sensor.pix_format.height >> 8) & 0xFF; - *(pos + 567) = 0x21; - - f->buf.bytesused += sizeof(jpeg_header); -} - - -static void sn9c102_urb_complete(struct urb *urb) -{ - struct sn9c102_device* cam = urb->context; - struct sn9c102_frame_t** f; - size_t imagesize, soflen; - 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; - cam->sof.bytesread = 0; - DBG(3, "Stream interrupted by application"); - 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 sn9c102_frame_t, - frame); - - imagesize = (cam->sensor.pix_format.width * - cam->sensor.pix_format.height * - cam->sensor.pix_format.priv) / 8; - if (cam->sensor.pix_format.pixelformat == V4L2_PIX_FMT_JPEG) - imagesize += 589; /* length of jpeg header */ - soflen = sn9c102_sof_length(cam); - - for (i = 0; i < urb->number_of_packets; i++) { - unsigned int img, len, status; - void *pos, *sof, *eof; - - 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; - cam->sof.bytesread = 0; - continue; - } - - PDBGG("Isochrnous frame: length %u, #%u i", len, i); - -redo: - sof = sn9c102_find_sof_header(cam, pos, len); - if (likely(!sof)) { - eof = sn9c102_find_eof_header(cam, pos, len); - if ((*f)->state == F_GRABBING) { -end_of_frame: - img = len; - - if (eof) - img = (eof > pos) ? eof - pos - 1 : 0; - - if ((*f)->buf.bytesused + img > imagesize) { - u32 b; - b = (*f)->buf.bytesused + img - - imagesize; - img = imagesize - (*f)->buf.bytesused; - PDBGG("Expected EOF not found: video " - "frame cut"); - if (eof) - DBG(3, "Exceeded limit: +%u " - "bytes", (unsigned)(b)); - } - - memcpy((*f)->bufmem + (*f)->buf.bytesused, pos, - img); - - if ((*f)->buf.bytesused == 0) - v4l2_get_timestamp( - &(*f)->buf.timestamp); - - (*f)->buf.bytesused += img; - - if ((*f)->buf.bytesused == imagesize || - ((cam->sensor.pix_format.pixelformat == - V4L2_PIX_FMT_SN9C10X || - cam->sensor.pix_format.pixelformat == - V4L2_PIX_FMT_JPEG) && eof)) { - u32 b; - - 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 sn9c102_frame_t, - frame ); - else - (*f) = NULL; - spin_unlock(&cam->queue_lock); - - memcpy(cam->sysfs.frame_header, - cam->sof.header, soflen); - - DBG(3, "Video frame captured: %lu " - "bytes", (unsigned long)(b)); - - if (!(*f)) - goto resubmit_urb; - - } else if (eof) { - (*f)->state = F_ERROR; - DBG(3, "Not expected EOF after %lu " - "bytes of image data", - (unsigned long) - ((*f)->buf.bytesused)); - } - - if (sof) /* (1) */ - goto start_of_frame; - - } else if (eof) { - DBG(3, "EOF without SOF"); - continue; - - } else { - PDBGG("Ignoring pointless isochronous frame"); - continue; - } - - } else if ((*f)->state == F_QUEUED || (*f)->state == F_ERROR) { -start_of_frame: - (*f)->state = F_GRABBING; - (*f)->buf.bytesused = 0; - len -= (sof - pos); - pos = sof; - if (cam->sensor.pix_format.pixelformat == - V4L2_PIX_FMT_JPEG) - sn9c102_write_jpegheader(cam, (*f)); - DBG(3, "SOF detected: new video frame"); - if (len) - goto redo; - - } else if ((*f)->state == F_GRABBING) { - eof = sn9c102_find_eof_header(cam, pos, len); - if (eof && eof < sof) - goto end_of_frame; /* (1) */ - else { - if (cam->sensor.pix_format.pixelformat == - V4L2_PIX_FMT_SN9C10X || - cam->sensor.pix_format.pixelformat == - V4L2_PIX_FMT_JPEG) { - if (sof - pos >= soflen) { - eof = sof - soflen; - } else { /* remove header */ - eof = pos; - (*f)->buf.bytesused -= - (soflen - (sof - pos)); - } - goto end_of_frame; - } else { - DBG(3, "SOF before expected EOF after " - "%lu bytes of image data", - (unsigned long) - ((*f)->buf.bytesused)); - 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 sn9c102_start_transfer(struct sn9c102_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), - SN9C102_ALTERNATE_SETTING); - const unsigned int psz = le16_to_cpu(altsetting-> - endpoint[0].desc.wMaxPacketSize); - s8 i, j; - int err = 0; - - for (i = 0; i < SN9C102_URBS; i++) { - cam->transfer_buffer[i] = kzalloc(SN9C102_ISO_PACKETS * psz, - GFP_KERNEL); - if (!cam->transfer_buffer[i]) { - err = -ENOMEM; - DBG(1, "Not enough memory"); - goto free_buffers; - } - } - - for (i = 0; i < SN9C102_URBS; i++) { - urb = usb_alloc_urb(SN9C102_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 = SN9C102_ISO_PACKETS; - urb->complete = sn9c102_urb_complete; - urb->transfer_buffer = cam->transfer_buffer[i]; - urb->transfer_buffer_length = psz * SN9C102_ISO_PACKETS; - urb->interval = 1; - for (j = 0; j < SN9C102_ISO_PACKETS; j++) { - urb->iso_frame_desc[j].offset = psz * j; - urb->iso_frame_desc[j].length = psz; - } - } - - /* Enable video */ - if (!(cam->reg[0x01] & 0x04)) { - err = sn9c102_write_reg(cam, cam->reg[0x01] | 0x04, 0x01); - if (err) { - err = -EIO; - DBG(1, "I/O hardware error"); - goto free_urbs; - } - } - - err = usb_set_interface(udev, 0, SN9C102_ALTERNATE_SETTING); - if (err) { - DBG(1, "usb_set_interface() failed"); - goto free_urbs; - } - - cam->frame_current = NULL; - cam->sof.bytesread = 0; - - for (i = 0; i < SN9C102_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 < SN9C102_URBS) && cam->urb[i]; i++) - usb_free_urb(cam->urb[i]); - -free_buffers: - for (i = 0; (i < SN9C102_URBS) && cam->transfer_buffer[i]; i++) - kfree(cam->transfer_buffer[i]); - - return err; -} - - -static int sn9c102_stop_transfer(struct sn9c102_device* cam) -{ - struct usb_device *udev = cam->usbdev; - s8 i; - int err = 0; - - if (cam->state & DEV_DISCONNECTED) - return 0; - - for (i = SN9C102_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 sn9c102_stream_interrupt(struct sn9c102_device* cam) -{ - cam->stream = STREAM_INTERRUPT; - wait_event_timeout(cam->wait_stream, - (cam->stream == STREAM_OFF) || - (cam->state & DEV_DISCONNECTED), - SN9C102_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 u16 sn9c102_strtou16(const char* buff, size_t len, ssize_t* count) -{ - char str[7]; - char* endp; - unsigned long val; - - if (len < 6) { - strncpy(str, buff, len); - str[len] = '\0'; - } else { - strncpy(str, buff, 6); - str[6] = '\0'; - } - - val = simple_strtoul(str, &endp, 0); - - *count = 0; - if (val <= 0xffff) - *count = (ssize_t)(endp - str); - if ((*count) && (len == *count+1) && (buff[*count] == '\n')) - *count += 1; - - return (u16)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 sn9c102_show_reg(struct device* cd, - struct device_attribute *attr, char* buf) -{ - struct sn9c102_device* cam; - ssize_t count; - - if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(container_of(cd, struct video_device, dev)); - if (!cam) { - mutex_unlock(&sn9c102_sysfs_lock); - return -ENODEV; - } - - count = sprintf(buf, "%u\n", cam->sysfs.reg); - - mutex_unlock(&sn9c102_sysfs_lock); - - return count; -} - - -static ssize_t -sn9c102_store_reg(struct device* cd, struct device_attribute *attr, - const char* buf, size_t len) -{ - struct sn9c102_device* cam; - u16 index; - ssize_t count; - - if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(container_of(cd, struct video_device, dev)); - if (!cam) { - mutex_unlock(&sn9c102_sysfs_lock); - return -ENODEV; - } - - index = sn9c102_strtou16(buf, len, &count); - if (index >= ARRAY_SIZE(cam->reg) || !count) { - mutex_unlock(&sn9c102_sysfs_lock); - return -EINVAL; - } - - cam->sysfs.reg = index; - - DBG(2, "Moved SN9C1XX register index to 0x%02X", cam->sysfs.reg); - DBG(3, "Written bytes: %zd", count); - - mutex_unlock(&sn9c102_sysfs_lock); - - return count; -} - - -static ssize_t sn9c102_show_val(struct device* cd, - struct device_attribute *attr, char* buf) -{ - struct sn9c102_device* cam; - ssize_t count; - int val; - - if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(container_of(cd, struct video_device, dev)); - if (!cam) { - mutex_unlock(&sn9c102_sysfs_lock); - return -ENODEV; - } - - if ((val = sn9c102_read_reg(cam, cam->sysfs.reg)) < 0) { - mutex_unlock(&sn9c102_sysfs_lock); - return -EIO; - } - - count = sprintf(buf, "%d\n", val); - - DBG(3, "Read bytes: %zd, value: %d", count, val); - - mutex_unlock(&sn9c102_sysfs_lock); - - return count; -} - - -static ssize_t -sn9c102_store_val(struct device* cd, struct device_attribute *attr, - const char* buf, size_t len) -{ - struct sn9c102_device* cam; - u16 value; - ssize_t count; - int err; - - if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(container_of(cd, struct video_device, dev)); - if (!cam) { - mutex_unlock(&sn9c102_sysfs_lock); - return -ENODEV; - } - - value = sn9c102_strtou16(buf, len, &count); - if (!count) { - mutex_unlock(&sn9c102_sysfs_lock); - return -EINVAL; - } - - err = sn9c102_write_reg(cam, value, cam->sysfs.reg); - if (err) { - mutex_unlock(&sn9c102_sysfs_lock); - return -EIO; - } - - DBG(2, "Written SN9C1XX reg. 0x%02X, val. 0x%02X", - cam->sysfs.reg, value); - DBG(3, "Written bytes: %zd", count); - - mutex_unlock(&sn9c102_sysfs_lock); - - return count; -} - - -static ssize_t sn9c102_show_i2c_reg(struct device* cd, - struct device_attribute *attr, char* buf) -{ - struct sn9c102_device* cam; - ssize_t count; - - if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(container_of(cd, struct video_device, dev)); - if (!cam) { - mutex_unlock(&sn9c102_sysfs_lock); - return -ENODEV; - } - - count = sprintf(buf, "%u\n", cam->sysfs.i2c_reg); - - DBG(3, "Read bytes: %zd", count); - - mutex_unlock(&sn9c102_sysfs_lock); - - return count; -} - - -static ssize_t -sn9c102_store_i2c_reg(struct device* cd, struct device_attribute *attr, - const char* buf, size_t len) -{ - struct sn9c102_device* cam; - u16 index; - ssize_t count; - - if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(container_of(cd, struct video_device, dev)); - if (!cam) { - mutex_unlock(&sn9c102_sysfs_lock); - return -ENODEV; - } - - index = sn9c102_strtou16(buf, len, &count); - if (!count) { - mutex_unlock(&sn9c102_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(&sn9c102_sysfs_lock); - - return count; -} - - -static ssize_t sn9c102_show_i2c_val(struct device* cd, - struct device_attribute *attr, char* buf) -{ - struct sn9c102_device* cam; - ssize_t count; - int val; - - if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(container_of(cd, struct video_device, dev)); - if (!cam) { - mutex_unlock(&sn9c102_sysfs_lock); - return -ENODEV; - } - - if (!(cam->sensor.sysfs_ops & SN9C102_I2C_READ)) { - mutex_unlock(&sn9c102_sysfs_lock); - return -ENOSYS; - } - - if ((val = sn9c102_i2c_read(cam, cam->sysfs.i2c_reg)) < 0) { - mutex_unlock(&sn9c102_sysfs_lock); - return -EIO; - } - - count = sprintf(buf, "%d\n", val); - - DBG(3, "Read bytes: %zd, value: %d", count, val); - - mutex_unlock(&sn9c102_sysfs_lock); - - return count; -} - - -static ssize_t -sn9c102_store_i2c_val(struct device* cd, struct device_attribute *attr, - const char* buf, size_t len) -{ - struct sn9c102_device* cam; - u16 value; - ssize_t count; - int err; - - if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(container_of(cd, struct video_device, dev)); - if (!cam) { - mutex_unlock(&sn9c102_sysfs_lock); - return -ENODEV; - } - - if (!(cam->sensor.sysfs_ops & SN9C102_I2C_WRITE)) { - mutex_unlock(&sn9c102_sysfs_lock); - return -ENOSYS; - } - - value = sn9c102_strtou16(buf, len, &count); - if (!count) { - mutex_unlock(&sn9c102_sysfs_lock); - return -EINVAL; - } - - err = sn9c102_i2c_write(cam, cam->sysfs.i2c_reg, value); - if (err) { - mutex_unlock(&sn9c102_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(&sn9c102_sysfs_lock); - - return count; -} - - -static ssize_t -sn9c102_store_green(struct device* cd, struct device_attribute *attr, - const char* buf, size_t len) -{ - struct sn9c102_device* cam; - enum sn9c102_bridge bridge; - ssize_t res = 0; - u16 value; - ssize_t count; - - if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(container_of(cd, struct video_device, dev)); - if (!cam) { - mutex_unlock(&sn9c102_sysfs_lock); - return -ENODEV; - } - - bridge = cam->bridge; - - mutex_unlock(&sn9c102_sysfs_lock); - - value = sn9c102_strtou16(buf, len, &count); - if (!count) - return -EINVAL; - - switch (bridge) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - if (value > 0x0f) - return -EINVAL; - if ((res = sn9c102_store_reg(cd, attr, "0x11", 4)) >= 0) - res = sn9c102_store_val(cd, attr, buf, len); - break; - case BRIDGE_SN9C103: - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - if (value > 0x7f) - return -EINVAL; - if ((res = sn9c102_store_reg(cd, attr, "0x07", 4)) >= 0) - res = sn9c102_store_val(cd, attr, buf, len); - break; - } - - return res; -} - - -static ssize_t -sn9c102_store_blue(struct device* cd, struct device_attribute *attr, - const char* buf, size_t len) -{ - ssize_t res = 0; - u16 value; - ssize_t count; - - value = sn9c102_strtou16(buf, len, &count); - if (!count || value > 0x7f) - return -EINVAL; - - if ((res = sn9c102_store_reg(cd, attr, "0x06", 4)) >= 0) - res = sn9c102_store_val(cd, attr, buf, len); - - return res; -} - - -static ssize_t -sn9c102_store_red(struct device* cd, struct device_attribute *attr, - const char* buf, size_t len) -{ - ssize_t res = 0; - u16 value; - ssize_t count; - - value = sn9c102_strtou16(buf, len, &count); - if (!count || value > 0x7f) - return -EINVAL; - - if ((res = sn9c102_store_reg(cd, attr, "0x05", 4)) >= 0) - res = sn9c102_store_val(cd, attr, buf, len); - - return res; -} - - -static ssize_t sn9c102_show_frame_header(struct device* cd, - struct device_attribute *attr, - char* buf) -{ - struct sn9c102_device* cam; - ssize_t count; - - cam = video_get_drvdata(container_of(cd, struct video_device, dev)); - if (!cam) - return -ENODEV; - - count = sizeof(cam->sysfs.frame_header); - memcpy(buf, cam->sysfs.frame_header, count); - - DBG(3, "Frame header, read bytes: %zd", count); - - return count; -} - - -static DEVICE_ATTR(reg, S_IRUGO | S_IWUSR, sn9c102_show_reg, sn9c102_store_reg); -static DEVICE_ATTR(val, S_IRUGO | S_IWUSR, sn9c102_show_val, sn9c102_store_val); -static DEVICE_ATTR(i2c_reg, S_IRUGO | S_IWUSR, - sn9c102_show_i2c_reg, sn9c102_store_i2c_reg); -static DEVICE_ATTR(i2c_val, S_IRUGO | S_IWUSR, - sn9c102_show_i2c_val, sn9c102_store_i2c_val); -static DEVICE_ATTR(green, S_IWUSR, NULL, sn9c102_store_green); -static DEVICE_ATTR(blue, S_IWUSR, NULL, sn9c102_store_blue); -static DEVICE_ATTR(red, S_IWUSR, NULL, sn9c102_store_red); -static DEVICE_ATTR(frame_header, S_IRUGO, sn9c102_show_frame_header, NULL); - - -static int sn9c102_create_sysfs(struct sn9c102_device* cam) -{ - struct device *dev = &(cam->v4ldev->dev); - int err = 0; - - if ((err = device_create_file(dev, &dev_attr_reg))) - goto err_out; - if ((err = device_create_file(dev, &dev_attr_val))) - goto err_reg; - if ((err = device_create_file(dev, &dev_attr_frame_header))) - goto err_val; - - if (cam->sensor.sysfs_ops) { - if ((err = device_create_file(dev, &dev_attr_i2c_reg))) - goto err_frame_header; - if ((err = device_create_file(dev, &dev_attr_i2c_val))) - goto err_i2c_reg; - } - - if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102) { - if ((err = device_create_file(dev, &dev_attr_green))) - goto err_i2c_val; - } else { - if ((err = device_create_file(dev, &dev_attr_blue))) - goto err_i2c_val; - if ((err = device_create_file(dev, &dev_attr_red))) - goto err_blue; - } - - return 0; - -err_blue: - device_remove_file(dev, &dev_attr_blue); -err_i2c_val: - if (cam->sensor.sysfs_ops) - device_remove_file(dev, &dev_attr_i2c_val); -err_i2c_reg: - if (cam->sensor.sysfs_ops) - device_remove_file(dev, &dev_attr_i2c_reg); -err_frame_header: - device_remove_file(dev, &dev_attr_frame_header); -err_val: - device_remove_file(dev, &dev_attr_val); -err_reg: - device_remove_file(dev, &dev_attr_reg); -err_out: - return err; -} -#endif /* CONFIG_VIDEO_ADV_DEBUG */ - -/*****************************************************************************/ - -static int -sn9c102_set_pix_format(struct sn9c102_device* cam, struct v4l2_pix_format* pix) -{ - int err = 0; - - if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X || - pix->pixelformat == V4L2_PIX_FMT_JPEG) { - switch (cam->bridge) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - case BRIDGE_SN9C103: - err += sn9c102_write_reg(cam, cam->reg[0x18] | 0x80, - 0x18); - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - err += sn9c102_write_reg(cam, cam->reg[0x18] & 0x7f, - 0x18); - break; - } - } else { - switch (cam->bridge) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - case BRIDGE_SN9C103: - err += sn9c102_write_reg(cam, cam->reg[0x18] & 0x7f, - 0x18); - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - err += sn9c102_write_reg(cam, cam->reg[0x18] | 0x80, - 0x18); - break; - } - } - - return err ? -EIO : 0; -} - - -static int -sn9c102_set_compression(struct sn9c102_device* cam, - struct v4l2_jpegcompression* compression) -{ - int i, err = 0; - - switch (cam->bridge) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - case BRIDGE_SN9C103: - if (compression->quality == 0) - err += sn9c102_write_reg(cam, cam->reg[0x17] | 0x01, - 0x17); - else if (compression->quality == 1) - err += sn9c102_write_reg(cam, cam->reg[0x17] & 0xfe, - 0x17); - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - if (compression->quality == 0) { - for (i = 0; i <= 63; i++) { - err += sn9c102_write_reg(cam, - SN9C102_Y_QTABLE1[i], - 0x100 + i); - err += sn9c102_write_reg(cam, - SN9C102_UV_QTABLE1[i], - 0x140 + i); - } - err += sn9c102_write_reg(cam, cam->reg[0x18] & 0xbf, - 0x18); - } else if (compression->quality == 1) { - for (i = 0; i <= 63; i++) { - err += sn9c102_write_reg(cam, - SN9C102_Y_QTABLE1[i], - 0x100 + i); - err += sn9c102_write_reg(cam, - SN9C102_UV_QTABLE1[i], - 0x140 + i); - } - err += sn9c102_write_reg(cam, cam->reg[0x18] | 0x40, - 0x18); - } - break; - } - - return err ? -EIO : 0; -} - - -static int sn9c102_set_scale(struct sn9c102_device* cam, u8 scale) -{ - u8 r = 0; - int err = 0; - - if (scale == 1) - r = cam->reg[0x18] & 0xcf; - else if (scale == 2) { - r = cam->reg[0x18] & 0xcf; - r |= 0x10; - } else if (scale == 4) - r = cam->reg[0x18] | 0x20; - - err += sn9c102_write_reg(cam, r, 0x18); - if (err) - return -EIO; - - PDBGG("Scaling factor: %u", scale); - - return 0; -} - - -static int sn9c102_set_crop(struct sn9c102_device* cam, struct v4l2_rect* rect) -{ - struct sn9c102_sensor* s = &cam->sensor; - u8 h_start = (u8)(rect->left - s->cropcap.bounds.left), - v_start = (u8)(rect->top - s->cropcap.bounds.top), - h_size = (u8)(rect->width / 16), - v_size = (u8)(rect->height / 16); - int err = 0; - - err += sn9c102_write_reg(cam, h_start, 0x12); - err += sn9c102_write_reg(cam, v_start, 0x13); - err += sn9c102_write_reg(cam, h_size, 0x15); - err += sn9c102_write_reg(cam, v_size, 0x16); - if (err) - return -EIO; - - PDBGG("h_start, v_start, h_size, v_size, ho_size, vo_size " - "%u %u %u %u", h_start, v_start, h_size, v_size); - - return 0; -} - - -static int sn9c102_init(struct sn9c102_device* cam) -{ - struct sn9c102_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); - } else { /* use current values */ - qctrl = s->_qctrl; - rect = &(s->_rect); - } - - err += sn9c102_set_scale(cam, rect->width / s->pix_format.width); - err += sn9c102_set_crop(cam, rect); - if (err) - return err; - - if (s->init) { - err = s->init(cam); - if (err) { - DBG(3, "Sensor initialization failed"); - return err; - } - } - - if (!(cam->state & DEV_INITIALIZED)) - if (cam->bridge == BRIDGE_SN9C101 || - cam->bridge == BRIDGE_SN9C102 || - cam->bridge == BRIDGE_SN9C103) { - if (s->pix_format.pixelformat == V4L2_PIX_FMT_JPEG) - s->pix_format.pixelformat= V4L2_PIX_FMT_SBGGR8; - cam->compression.quality = cam->reg[0x17] & 0x01 ? - 0 : 1; - } else { - if (s->pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X) - s->pix_format.pixelformat = V4L2_PIX_FMT_JPEG; - cam->compression.quality = cam->reg[0x18] & 0x40 ? - 0 : 1; - err += sn9c102_set_compression(cam, &cam->compression); - } - else - err += sn9c102_set_compression(cam, &cam->compression); - err += sn9c102_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_SN9C10X || - s->pix_format.pixelformat == V4L2_PIX_FMT_JPEG) - 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 sn9c102_release_resources(struct kref *kref) -{ - struct sn9c102_device *cam; - - mutex_lock(&sn9c102_sysfs_lock); - - cam = container_of(kref, struct sn9c102_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); - v4l2_device_unregister(&cam->v4l2_dev); - usb_put_dev(cam->usbdev); - kfree(cam->control_buffer); - kfree(cam); - - mutex_unlock(&sn9c102_sysfs_lock); - -} - - -static int sn9c102_open(struct file *filp) -{ - struct sn9c102_device* cam; - int err = 0; - - /* - A read_trylock() in open() is the only safe way to prevent race - conditions with disconnect(), one close() and multiple (not - necessarily simultaneous) attempts to open(). For example, it - prevents from waiting for a second access, while the device - structure is being deallocated, after a possible disconnect() and - during a following close() holding the write lock: given that, after - this deallocation, no access will be possible anymore, using the - non-trylock version would have let open() gain the access to the - device structure improperly. - For this reason the lock must also not be per-device. - */ - if (!down_read_trylock(&sn9c102_dev_lock)) - return -ERESTARTSYS; - - cam = video_drvdata(filp); - - if (wait_for_completion_interruptible(&cam->probe)) { - up_read(&sn9c102_dev_lock); - return -ERESTARTSYS; - } - - kref_get(&cam->kref); - - /* - Make sure to isolate all the simultaneous opens. - */ - if (mutex_lock_interruptible(&cam->open_mutex)) { - kref_put(&cam->kref, sn9c102_release_resources); - up_read(&sn9c102_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"); - /* - open() must follow the open flags and should block - eventually while the device is in use. - */ - 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(&sn9c102_dev_lock); - /* - We will not release the "open_mutex" lock, so that only one - process can be in the wait queue below. This way the process - will be sleeping while holding the lock, without losing its - priority after any wake_up(). - */ - err = wait_event_interruptible_exclusive(cam->wait_open, - (cam->state & DEV_DISCONNECTED) - || !cam->users); - down_read(&sn9c102_dev_lock); - if (err) - goto out; - if (cam->state & DEV_DISCONNECTED) { - err = -ENODEV; - goto out; - } - } - - if (cam->state & DEV_MISCONFIGURED) { - err = sn9c102_init(cam); - if (err) { - DBG(1, "Initialization failed again. " - "I will retry on next open()."); - goto out; - } - cam->state &= ~DEV_MISCONFIGURED; - } - - if ((err = sn9c102_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; - sn9c102_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, sn9c102_release_resources); - - up_read(&sn9c102_dev_lock); - return err; -} - - -static int sn9c102_release(struct file *filp) -{ - struct sn9c102_device* cam; - - down_write(&sn9c102_dev_lock); - - cam = video_drvdata(filp); - - sn9c102_stop_transfer(cam); - sn9c102_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, sn9c102_release_resources); - - up_write(&sn9c102_dev_lock); - - return 0; -} - - -static ssize_t -sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos) -{ - struct sn9c102_device *cam = video_drvdata(filp); - struct sn9c102_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 (!sn9c102_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)) - sn9c102_empty_framequeues(cam); - sn9c102_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; - } - if (!cam->module_param.frame_timeout) { - err = wait_event_interruptible - ( cam->wait_frame, - (!list_empty(&cam->outqueue)) || - (cam->state & DEV_DISCONNECTED) || - (cam->state & DEV_MISCONFIGURED) ); - if (err) { - mutex_unlock(&cam->fileop_mutex); - return err; - } - } else { - 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; - } else if (timeout == 0 && - !(cam->state & DEV_DISCONNECTED)) { - DBG(1, "Video frame timeout elapsed"); - mutex_unlock(&cam->fileop_mutex); - return -EIO; - } - } - if (cam->state & DEV_DISCONNECTED) { - mutex_unlock(&cam->fileop_mutex); - return -ENODEV; - } - if (cam->state & DEV_MISCONFIGURED) { - mutex_unlock(&cam->fileop_mutex); - return -EIO; - } - } - - f = list_entry(cam->outqueue.prev, struct sn9c102_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); - - sn9c102_queue_unusedframes(cam); - - PDBGG("Frame #%lu, bytes read: %zu", - (unsigned long)f->buf.index, count); - - mutex_unlock(&cam->fileop_mutex); - - return count; -} - - -static unsigned int sn9c102_poll(struct file *filp, poll_table *wait) -{ - struct sn9c102_device *cam = video_drvdata(filp); - struct sn9c102_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 (!sn9c102_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); - sn9c102_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 sn9c102_vm_open(struct vm_area_struct* vma) -{ - struct sn9c102_frame_t* f = vma->vm_private_data; - f->vma_use_count++; -} - - -static void sn9c102_vm_close(struct vm_area_struct* vma) -{ - /* NOTE: buffers are not freed here */ - struct sn9c102_frame_t* f = vma->vm_private_data; - f->vma_use_count--; -} - - -static const struct vm_operations_struct sn9c102_vm_ops = { - .open = sn9c102_vm_open, - .close = sn9c102_vm_close, -}; - - -static int sn9c102_mmap(struct file* filp, struct vm_area_struct *vma) -{ - struct sn9c102_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 | VM_DONTEXPAND | VM_DONTDUMP; - - 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 = &sn9c102_vm_ops; - vma->vm_private_data = &cam->frame[i]; - sn9c102_vm_open(vma); - - mutex_unlock(&cam->fileop_mutex); - - return 0; -} - -/*****************************************************************************/ - -static int -sn9c102_vidioc_querycap(struct sn9c102_device* cam, void __user * arg) -{ - struct v4l2_capability cap = { - .driver = "sn9c102", - .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 -sn9c102_vidioc_enuminput(struct sn9c102_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 -sn9c102_vidioc_g_input(struct sn9c102_device* cam, void __user * arg) -{ - int index = 0; - - if (copy_to_user(arg, &index, sizeof(index))) - return -EFAULT; - - return 0; -} - - -static int -sn9c102_vidioc_s_input(struct sn9c102_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 -sn9c102_vidioc_query_ctrl(struct sn9c102_device* cam, void __user * arg) -{ - struct sn9c102_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 -sn9c102_vidioc_g_ctrl(struct sn9c102_device* cam, void __user * arg) -{ - struct sn9c102_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 && 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; - - PDBGG("VIDIOC_G_CTRL: id %lu, value %lu", - (unsigned long)ctrl.id, (unsigned long)ctrl.value); - - return err; -} - - -static int -sn9c102_vidioc_s_ctrl(struct sn9c102_device* cam, void __user * arg) -{ - struct sn9c102_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; - - PDBGG("VIDIOC_S_CTRL: id %lu, value %lu", - (unsigned long)ctrl.id, (unsigned long)ctrl.value); - - return 0; -} - - -static int -sn9c102_vidioc_cropcap(struct sn9c102_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 -sn9c102_vidioc_g_crop(struct sn9c102_device* cam, void __user * arg) -{ - struct sn9c102_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 -sn9c102_vidioc_s_crop(struct sn9c102_device* cam, void __user * arg) -{ - struct sn9c102_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 sn9c102_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 (SN9C102_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 : ((a / b) < 16 ? 2 : 4)) : 1; - } else - scale = 1; - - if (cam->stream == STREAM_ON) - if ((err = sn9c102_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) - sn9c102_release_buffers(cam); - - err = sn9c102_set_crop(cam, rect); - if (s->set_crop) - err += s->set_crop(cam, rect); - err += sn9c102_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 != sn9c102_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) - sn9c102_empty_framequeues(cam); - else if (cam->module_param.force_munmap) - sn9c102_requeue_outqueue(cam); - - cam->stream = stream; - - return 0; -} - - -static int -sn9c102_vidioc_enum_framesizes(struct sn9c102_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; - - switch (cam->bridge) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - case BRIDGE_SN9C103: - if (frmsize.pixel_format != V4L2_PIX_FMT_SN9C10X && - frmsize.pixel_format != V4L2_PIX_FMT_SBGGR8) - return -EINVAL; - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - if (frmsize.pixel_format != V4L2_PIX_FMT_JPEG && - frmsize.pixel_format != V4L2_PIX_FMT_SBGGR8) - return -EINVAL; - break; - } - - 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 -sn9c102_vidioc_enum_fmt(struct sn9c102_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) { - switch (cam->bridge) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - case BRIDGE_SN9C103: - strcpy(fmtd.description, "compressed"); - fmtd.pixelformat = V4L2_PIX_FMT_SN9C10X; - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - strcpy(fmtd.description, "JPEG"); - fmtd.pixelformat = V4L2_PIX_FMT_JPEG; - break; - } - 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 -sn9c102_vidioc_g_fmt(struct sn9c102_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_JPEG) ? - V4L2_COLORSPACE_JPEG : V4L2_COLORSPACE_SRGB; - pfmt->bytesperline = (pfmt->pixelformat == V4L2_PIX_FMT_SN9C10X || - pfmt->pixelformat == V4L2_PIX_FMT_JPEG) - ? 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 -sn9c102_vidioc_try_s_fmt(struct sn9c102_device* cam, unsigned int cmd, - void __user * arg) -{ - struct sn9c102_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 sn9c102_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 : ((a / b) < 16 ? 2 : 4)) : 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 : ((a / b) < 16 ? 2 : 4)) : 1; - } - - pix->width = rect.width / scale; - pix->height = rect.height / scale; - - switch (cam->bridge) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - case BRIDGE_SN9C103: - if (pix->pixelformat != V4L2_PIX_FMT_SN9C10X && - pix->pixelformat != V4L2_PIX_FMT_SBGGR8) - pix->pixelformat = pfmt->pixelformat; - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - if (pix->pixelformat != V4L2_PIX_FMT_JPEG && - pix->pixelformat != V4L2_PIX_FMT_SBGGR8) - pix->pixelformat = pfmt->pixelformat; - break; - } - pix->priv = pfmt->priv; /* bpp */ - pix->colorspace = (pix->pixelformat == V4L2_PIX_FMT_JPEG) ? - V4L2_COLORSPACE_JPEG : V4L2_COLORSPACE_SRGB; - pix->bytesperline = (pix->pixelformat == V4L2_PIX_FMT_SN9C10X || - pix->pixelformat == V4L2_PIX_FMT_JPEG) - ? 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 = sn9c102_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) - sn9c102_release_buffers(cam); - - err += sn9c102_set_pix_format(cam, pix); - err += sn9c102_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 += sn9c102_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 != sn9c102_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) - sn9c102_empty_framequeues(cam); - else if (cam->module_param.force_munmap) - sn9c102_requeue_outqueue(cam); - - cam->stream = stream; - - return 0; -} - - -static int -sn9c102_vidioc_g_jpegcomp(struct sn9c102_device* cam, void __user * arg) -{ - if (copy_to_user(arg, &cam->compression, sizeof(cam->compression))) - return -EFAULT; - - return 0; -} - - -static int -sn9c102_vidioc_s_jpegcomp(struct sn9c102_device* cam, void __user * arg) -{ - struct v4l2_jpegcompression jc; - const enum sn9c102_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 = sn9c102_stream_interrupt(cam))) - return err; - - err += sn9c102_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 -sn9c102_vidioc_reqbufs(struct sn9c102_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 = sn9c102_stream_interrupt(cam))) - return err; - - sn9c102_empty_framequeues(cam); - - sn9c102_release_buffers(cam); - if (rb.count) - rb.count = sn9c102_request_buffers(cam, rb.count, IO_MMAP); - - if (copy_to_user(arg, &rb, sizeof(rb))) { - sn9c102_release_buffers(cam); - cam->io = IO_NONE; - return -EFAULT; - } - - cam->io = rb.count ? IO_MMAP : IO_NONE; - - return 0; -} - - -static int -sn9c102_vidioc_querybuf(struct sn9c102_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; - - b = cam->frame[b.index].buf; - - 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 -sn9c102_vidioc_qbuf(struct sn9c102_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 -sn9c102_vidioc_dqbuf(struct sn9c102_device* cam, struct file* filp, - void __user * arg) -{ - struct v4l2_buffer b; - struct sn9c102_frame_t *f; - unsigned long lock_flags; - long timeout; - int err = 0; - - 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; - if (!cam->module_param.frame_timeout) { - err = wait_event_interruptible - ( cam->wait_frame, - (!list_empty(&cam->outqueue)) || - (cam->state & DEV_DISCONNECTED) || - (cam->state & DEV_MISCONFIGURED) ); - if (err) - return err; - } else { - 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; - else if (timeout == 0 && - !(cam->state & DEV_DISCONNECTED)) { - DBG(1, "Video frame timeout elapsed"); - return -EIO; - } - } - if (cam->state & DEV_DISCONNECTED) - return -ENODEV; - if (cam->state & DEV_MISCONFIGURED) - return -EIO; - } - - spin_lock_irqsave(&cam->queue_lock, lock_flags); - f = list_entry(cam->outqueue.next, struct sn9c102_frame_t, frame); - list_del(cam->outqueue.next); - spin_unlock_irqrestore(&cam->queue_lock, lock_flags); - - f->state = F_UNUSED; - - b = f->buf; - 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 -sn9c102_vidioc_streamon(struct sn9c102_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 -sn9c102_vidioc_streamoff(struct sn9c102_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 = sn9c102_stream_interrupt(cam))) - return err; - - sn9c102_empty_framequeues(cam); - - DBG(3, "Stream off"); - - return 0; -} - - -static int -sn9c102_vidioc_g_parm(struct sn9c102_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 -sn9c102_vidioc_s_parm(struct sn9c102_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 > SN9C102_MAX_FRAMES) - sp.parm.capture.readbuffers = SN9C102_MAX_FRAMES; - - if (copy_to_user(arg, &sp, sizeof(sp))) - return -EFAULT; - - cam->nreadbuffers = sp.parm.capture.readbuffers; - - return 0; -} - - -static int -sn9c102_vidioc_enumaudio(struct sn9c102_device* cam, void __user * arg) -{ - struct v4l2_audio audio; - - if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102) - return -EINVAL; - - if (copy_from_user(&audio, arg, sizeof(audio))) - return -EFAULT; - - if (audio.index != 0) - return -EINVAL; - - strcpy(audio.name, "Microphone"); - audio.capability = 0; - audio.mode = 0; - - if (copy_to_user(arg, &audio, sizeof(audio))) - return -EFAULT; - - return 0; -} - - -static int -sn9c102_vidioc_g_audio(struct sn9c102_device* cam, void __user * arg) -{ - struct v4l2_audio audio; - - if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102) - return -EINVAL; - - if (copy_from_user(&audio, arg, sizeof(audio))) - return -EFAULT; - - memset(&audio, 0, sizeof(audio)); - strcpy(audio.name, "Microphone"); - - if (copy_to_user(arg, &audio, sizeof(audio))) - return -EFAULT; - - return 0; -} - - -static int -sn9c102_vidioc_s_audio(struct sn9c102_device* cam, void __user * arg) -{ - struct v4l2_audio audio; - - if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102) - return -EINVAL; - - if (copy_from_user(&audio, arg, sizeof(audio))) - return -EFAULT; - - if (audio.index != 0) - return -EINVAL; - - return 0; -} - - -static long sn9c102_ioctl_v4l2(struct file *filp, - unsigned int cmd, void __user *arg) -{ - struct sn9c102_device *cam = video_drvdata(filp); - - switch (cmd) { - - case VIDIOC_QUERYCAP: - return sn9c102_vidioc_querycap(cam, arg); - - case VIDIOC_ENUMINPUT: - return sn9c102_vidioc_enuminput(cam, arg); - - case VIDIOC_G_INPUT: - return sn9c102_vidioc_g_input(cam, arg); - - case VIDIOC_S_INPUT: - return sn9c102_vidioc_s_input(cam, arg); - - case VIDIOC_QUERYCTRL: - return sn9c102_vidioc_query_ctrl(cam, arg); - - case VIDIOC_G_CTRL: - return sn9c102_vidioc_g_ctrl(cam, arg); - - case VIDIOC_S_CTRL: - return sn9c102_vidioc_s_ctrl(cam, arg); - - case VIDIOC_CROPCAP: - return sn9c102_vidioc_cropcap(cam, arg); - - case VIDIOC_G_CROP: - return sn9c102_vidioc_g_crop(cam, arg); - - case VIDIOC_S_CROP: - return sn9c102_vidioc_s_crop(cam, arg); - - case VIDIOC_ENUM_FRAMESIZES: - return sn9c102_vidioc_enum_framesizes(cam, arg); - - case VIDIOC_ENUM_FMT: - return sn9c102_vidioc_enum_fmt(cam, arg); - - case VIDIOC_G_FMT: - return sn9c102_vidioc_g_fmt(cam, arg); - - case VIDIOC_TRY_FMT: - case VIDIOC_S_FMT: - return sn9c102_vidioc_try_s_fmt(cam, cmd, arg); - - case VIDIOC_G_JPEGCOMP: - return sn9c102_vidioc_g_jpegcomp(cam, arg); - - case VIDIOC_S_JPEGCOMP: - return sn9c102_vidioc_s_jpegcomp(cam, arg); - - case VIDIOC_REQBUFS: - return sn9c102_vidioc_reqbufs(cam, arg); - - case VIDIOC_QUERYBUF: - return sn9c102_vidioc_querybuf(cam, arg); - - case VIDIOC_QBUF: - return sn9c102_vidioc_qbuf(cam, arg); - - case VIDIOC_DQBUF: - return sn9c102_vidioc_dqbuf(cam, filp, arg); - - case VIDIOC_STREAMON: - return sn9c102_vidioc_streamon(cam, arg); - - case VIDIOC_STREAMOFF: - return sn9c102_vidioc_streamoff(cam, arg); - - case VIDIOC_G_PARM: - return sn9c102_vidioc_g_parm(cam, arg); - - case VIDIOC_S_PARM: - return sn9c102_vidioc_s_parm(cam, arg); - - case VIDIOC_ENUMAUDIO: - return sn9c102_vidioc_enumaudio(cam, arg); - - case VIDIOC_G_AUDIO: - return sn9c102_vidioc_g_audio(cam, arg); - - case VIDIOC_S_AUDIO: - return sn9c102_vidioc_s_audio(cam, arg); - - default: - return -ENOTTY; - - } -} - - -static long sn9c102_ioctl(struct file *filp, - unsigned int cmd, unsigned long arg) -{ - struct sn9c102_device *cam = video_drvdata(filp); - 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; - } - - V4LDBG(3, "sn9c102", cmd); - - err = sn9c102_ioctl_v4l2(filp, cmd, (void __user *)arg); - - mutex_unlock(&cam->fileop_mutex); - - return err; -} - -/*****************************************************************************/ - -static const struct v4l2_file_operations sn9c102_fops = { - .owner = THIS_MODULE, - .open = sn9c102_open, - .release = sn9c102_release, - .unlocked_ioctl = sn9c102_ioctl, - .read = sn9c102_read, - .poll = sn9c102_poll, - .mmap = sn9c102_mmap, -}; - -/*****************************************************************************/ - -/* It exists a single interface only. We do not need to validate anything. */ -static int -sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) -{ - struct usb_device *udev = interface_to_usbdev(intf); - struct sn9c102_device* cam; - static unsigned int dev_nr; - unsigned int i; - int err = 0, r; - - if (!(cam = kzalloc(sizeof(struct sn9c102_device), GFP_KERNEL))) - return -ENOMEM; - - cam->usbdev = udev; - - /* register v4l2_device early so it can be used for printks */ - if (v4l2_device_register(&intf->dev, &cam->v4l2_dev)) { - dev_err(&intf->dev, "v4l2_device_register failed\n"); - err = -ENOMEM; - goto fail; - } - - if (!(cam->control_buffer = kzalloc(8, GFP_KERNEL))) { - DBG(1, "kzalloc() failed"); - err = -ENOMEM; - goto fail; - } - - if (!(cam->v4ldev = video_device_alloc())) { - DBG(1, "video_device_alloc() failed"); - err = -ENOMEM; - goto fail; - } - - r = sn9c102_read_reg(cam, 0x00); - if (r < 0 || (r != 0x10 && r != 0x11 && r != 0x12)) { - DBG(1, "Sorry, this is not a SN9C1xx-based camera " - "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); - err = -ENODEV; - goto fail; - } - - cam->bridge = id->driver_info; - switch (cam->bridge) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - DBG(2, "SN9C10[12] PC Camera Controller detected " - "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); - break; - case BRIDGE_SN9C103: - DBG(2, "SN9C103 PC Camera Controller detected " - "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); - break; - case BRIDGE_SN9C105: - DBG(2, "SN9C105 PC Camera Controller detected " - "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); - break; - case BRIDGE_SN9C120: - DBG(2, "SN9C120 PC Camera Controller detected " - "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); - break; - } - - for (i = 0; i < ARRAY_SIZE(sn9c102_sensor_table); i++) { - err = sn9c102_sensor_table[i](cam); - if (!err) - break; - } - - if (!err) { - DBG(2, "%s image sensor detected", cam->sensor.name); - DBG(3, "Support for %s maintained by %s", - cam->sensor.name, cam->sensor.maintainer); - } else { - DBG(1, "No supported image sensor detected for this bridge"); - err = -ENODEV; - goto fail; - } - - if (!(cam->bridge & cam->sensor.supported_bridge)) { - DBG(1, "Bridge not supported"); - err = -ENODEV; - goto fail; - } - - if (sn9c102_init(cam)) { - DBG(1, "Initialization failed. I will retry on open()."); - cam->state |= DEV_MISCONFIGURED; - } - - strcpy(cam->v4ldev->name, "SN9C1xx PC Camera"); - cam->v4ldev->fops = &sn9c102_fops; - cam->v4ldev->release = video_device_release; - cam->v4ldev->v4l2_dev = &cam->v4l2_dev; - - 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 < SN9C102_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)); - - video_set_drvdata(cam->v4ldev, cam); - cam->module_param.force_munmap = force_munmap[dev_nr]; - cam->module_param.frame_timeout = frame_timeout[dev_nr]; - - dev_nr = (dev_nr < SN9C102_MAX_DEVICES-1) ? dev_nr+1 : 0; - -#ifdef CONFIG_VIDEO_ADV_DEBUG - err = sn9c102_create_sysfs(cam); - if (!err) - DBG(2, "Optional device control through 'sysfs' " - "interface ready"); - else - DBG(2, "Failed to create optional 'sysfs' interface for " - "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); - v4l2_device_unregister(&cam->v4l2_dev); - kfree(cam); - } - return err; -} - - -static void sn9c102_usb_disconnect(struct usb_interface* intf) -{ - struct sn9c102_device* cam; - - down_write(&sn9c102_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; - sn9c102_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); - - v4l2_device_disconnect(&cam->v4l2_dev); - - kref_put(&cam->kref, sn9c102_release_resources); - - up_write(&sn9c102_dev_lock); -} - - -static struct usb_driver sn9c102_usb_driver = { - .name = "sn9c102", - .id_table = sn9c102_id_table, - .probe = sn9c102_usb_probe, - .disconnect = sn9c102_usb_disconnect, -}; - -module_usb_driver(sn9c102_usb_driver); diff --git a/drivers/media/usb/sn9c102/sn9c102_devtable.h b/drivers/media/usb/sn9c102/sn9c102_devtable.h deleted file mode 100644 index b3d2cc729657..000000000000 --- a/drivers/media/usb/sn9c102/sn9c102_devtable.h +++ /dev/null @@ -1,147 +0,0 @@ -/*************************************************************************** - * Table of device identifiers of the SN9C1xx PC Camera Controllers * - * * - * Copyright (C) 2007 by Luca Risolia * - * * - * 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 _SN9C102_DEVTABLE_H_ -#define _SN9C102_DEVTABLE_H_ - -#include - -struct sn9c102_device; - -/* - Each SN9C1xx camera has proper PID/VID identifiers. - SN9C103, SN9C105, SN9C120 support multiple interfaces, but we only have to - handle the video class interface. -*/ -#define SN9C102_USB_DEVICE(vend, prod, bridge) \ - .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \ - USB_DEVICE_ID_MATCH_INT_CLASS, \ - .idVendor = (vend), \ - .idProduct = (prod), \ - .bInterfaceClass = 0xff, \ - .driver_info = (bridge) - -static const struct usb_device_id sn9c102_id_table[] = { - /* SN9C101 and SN9C102 */ -#if !defined CONFIG_USB_GSPCA_SONIXB && !defined CONFIG_USB_GSPCA_SONIXB_MODULE - { SN9C102_USB_DEVICE(0x0c45, 0x6001, BRIDGE_SN9C102), }, - { SN9C102_USB_DEVICE(0x0c45, 0x6005, BRIDGE_SN9C102), }, - { SN9C102_USB_DEVICE(0x0c45, 0x6007, BRIDGE_SN9C102), }, - { SN9C102_USB_DEVICE(0x0c45, 0x6009, BRIDGE_SN9C102), }, - { SN9C102_USB_DEVICE(0x0c45, 0x600d, BRIDGE_SN9C102), }, -/* { SN9C102_USB_DEVICE(0x0c45, 0x6011, BRIDGE_SN9C102), }, OV6650 */ - { SN9C102_USB_DEVICE(0x0c45, 0x6019, BRIDGE_SN9C102), }, -#endif - { SN9C102_USB_DEVICE(0x0c45, 0x6024, BRIDGE_SN9C102), }, - { SN9C102_USB_DEVICE(0x0c45, 0x6025, BRIDGE_SN9C102), }, -#if !defined CONFIG_USB_GSPCA_SONIXB && !defined CONFIG_USB_GSPCA_SONIXB_MODULE - { SN9C102_USB_DEVICE(0x0c45, 0x6028, BRIDGE_SN9C102), }, - { SN9C102_USB_DEVICE(0x0c45, 0x6029, BRIDGE_SN9C102), }, - { SN9C102_USB_DEVICE(0x0c45, 0x602a, BRIDGE_SN9C102), }, -#endif - { SN9C102_USB_DEVICE(0x0c45, 0x602b, BRIDGE_SN9C102), }, /* not in sonixb */ -#if !defined CONFIG_USB_GSPCA_SONIXB && !defined CONFIG_USB_GSPCA_SONIXB_MODULE - { SN9C102_USB_DEVICE(0x0c45, 0x602c, BRIDGE_SN9C102), }, -/* { SN9C102_USB_DEVICE(0x0c45, 0x602d, BRIDGE_SN9C102), }, HV7131R */ - { SN9C102_USB_DEVICE(0x0c45, 0x602e, BRIDGE_SN9C102), }, -#endif - { SN9C102_USB_DEVICE(0x0c45, 0x6030, BRIDGE_SN9C102), }, /* not in sonixb */ - /* SN9C103 */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x6080, BRIDGE_SN9C103), }, non existent ? */ - { SN9C102_USB_DEVICE(0x0c45, 0x6082, BRIDGE_SN9C103), }, /* not in sonixb */ -#if !defined CONFIG_USB_GSPCA_SONIXB && !defined CONFIG_USB_GSPCA_SONIXB_MODULE -/* { SN9C102_USB_DEVICE(0x0c45, 0x6083, BRIDGE_SN9C103), }, HY7131D/E */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x6088, BRIDGE_SN9C103), }, non existent ? */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x608a, BRIDGE_SN9C103), }, non existent ? */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x608b, BRIDGE_SN9C103), }, non existent ? */ - { SN9C102_USB_DEVICE(0x0c45, 0x608c, BRIDGE_SN9C103), }, -/* { SN9C102_USB_DEVICE(0x0c45, 0x608e, BRIDGE_SN9C103), }, CISVF10 */ - { SN9C102_USB_DEVICE(0x0c45, 0x608f, BRIDGE_SN9C103), }, -/* { SN9C102_USB_DEVICE(0x0c45, 0x60a0, BRIDGE_SN9C103), }, non existent ? */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60a2, BRIDGE_SN9C103), }, non existent ? */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60a3, BRIDGE_SN9C103), }, non existent ? */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60a8, BRIDGE_SN9C103), }, PAS106 */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60aa, BRIDGE_SN9C103), }, TAS5130 */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60ab, BRIDGE_SN9C103), }, TAS5110, non existent */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60ac, BRIDGE_SN9C103), }, non existent ? */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60ae, BRIDGE_SN9C103), }, non existent ? */ - { SN9C102_USB_DEVICE(0x0c45, 0x60af, BRIDGE_SN9C103), }, - { SN9C102_USB_DEVICE(0x0c45, 0x60b0, BRIDGE_SN9C103), }, -/* { SN9C102_USB_DEVICE(0x0c45, 0x60b2, BRIDGE_SN9C103), }, non existent ? */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60b3, BRIDGE_SN9C103), }, non existent ? */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60b8, BRIDGE_SN9C103), }, non existent ? */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60ba, BRIDGE_SN9C103), }, non existent ? */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60bb, BRIDGE_SN9C103), }, non existent ? */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60bc, BRIDGE_SN9C103), }, non existent ? */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60be, BRIDGE_SN9C103), }, non existent ? */ -#endif - /* SN9C105 */ -#if !defined CONFIG_USB_GSPCA_SONIXJ && !defined CONFIG_USB_GSPCA_SONIXJ_MODULE - { SN9C102_USB_DEVICE(0x045e, 0x00f5, BRIDGE_SN9C105), }, - { SN9C102_USB_DEVICE(0x045e, 0x00f7, BRIDGE_SN9C105), }, - { SN9C102_USB_DEVICE(0x0471, 0x0327, BRIDGE_SN9C105), }, - { SN9C102_USB_DEVICE(0x0471, 0x0328, BRIDGE_SN9C105), }, - { SN9C102_USB_DEVICE(0x0c45, 0x60c0, BRIDGE_SN9C105), }, -/* { SN9C102_USB_DEVICE(0x0c45, 0x60c2, BRIDGE_SN9C105), }, PO1030 */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60c8, BRIDGE_SN9C105), }, OM6801 */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60cc, BRIDGE_SN9C105), }, HV7131GP */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60ea, BRIDGE_SN9C105), }, non existent ? */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60ec, BRIDGE_SN9C105), }, MO4000 */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60ef, BRIDGE_SN9C105), }, ICM105C */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60fa, BRIDGE_SN9C105), }, OV7648 */ - { SN9C102_USB_DEVICE(0x0c45, 0x60fb, BRIDGE_SN9C105), }, - { SN9C102_USB_DEVICE(0x0c45, 0x60fc, BRIDGE_SN9C105), }, - { SN9C102_USB_DEVICE(0x0c45, 0x60fe, BRIDGE_SN9C105), }, - /* SN9C120 */ - { SN9C102_USB_DEVICE(0x0458, 0x7025, BRIDGE_SN9C120), }, -/* { SN9C102_USB_DEVICE(0x0c45, 0x6102, BRIDGE_SN9C120), }, po2030 */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x6108, BRIDGE_SN9C120), }, om6801 */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x610f, BRIDGE_SN9C120), }, S5K53BEB */ - { SN9C102_USB_DEVICE(0x0c45, 0x6130, BRIDGE_SN9C120), }, -/* { SN9C102_USB_DEVICE(0x0c45, 0x6138, BRIDGE_SN9C120), }, MO8000 */ - { SN9C102_USB_DEVICE(0x0c45, 0x613a, BRIDGE_SN9C120), }, - { SN9C102_USB_DEVICE(0x0c45, 0x613b, BRIDGE_SN9C120), }, - { SN9C102_USB_DEVICE(0x0c45, 0x613c, BRIDGE_SN9C120), }, - { SN9C102_USB_DEVICE(0x0c45, 0x613e, BRIDGE_SN9C120), }, -#endif - { } -}; - -/* - Probing functions: on success, you must attach the sensor to the camera - by calling sn9c102_attach_sensor(). - To enable the I2C communication, you might need to perform a really basic - initialization of the SN9C1XX chip. - Functions must return 0 on success, the appropriate error otherwise. -*/ -extern int sn9c102_probe_hv7131d(struct sn9c102_device* cam); -extern int sn9c102_probe_hv7131r(struct sn9c102_device* cam); -extern int sn9c102_probe_mi0343(struct sn9c102_device* cam); -extern int sn9c102_probe_mi0360(struct sn9c102_device* cam); -extern int sn9c102_probe_mt9v111(struct sn9c102_device *cam); -extern int sn9c102_probe_ov7630(struct sn9c102_device* cam); -extern int sn9c102_probe_ov7660(struct sn9c102_device* cam); -extern int sn9c102_probe_pas106b(struct sn9c102_device* cam); -extern int sn9c102_probe_pas202bcb(struct sn9c102_device* cam); -extern int sn9c102_probe_tas5110c1b(struct sn9c102_device* cam); -extern int sn9c102_probe_tas5110d(struct sn9c102_device* cam); -extern int sn9c102_probe_tas5130d1b(struct sn9c102_device* cam); - -#endif /* _SN9C102_DEVTABLE_H_ */ diff --git a/drivers/media/usb/sn9c102/sn9c102_hv7131d.c b/drivers/media/usb/sn9c102/sn9c102_hv7131d.c deleted file mode 100644 index 2dce5c908c8e..000000000000 --- a/drivers/media/usb/sn9c102/sn9c102_hv7131d.c +++ /dev/null @@ -1,264 +0,0 @@ -/*************************************************************************** - * Plug-in for HV7131D image sensor connected to the SN9C1xx PC Camera * - * Controllers * - * * - * Copyright (C) 2004-2007 by Luca Risolia * - * * - * 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. * - ***************************************************************************/ - -#include "sn9c102_sensor.h" -#include "sn9c102_devtable.h" - - -static int hv7131d_init(struct sn9c102_device* cam) -{ - int err; - - err = sn9c102_write_const_regs(cam, {0x00, 0x10}, {0x00, 0x11}, - {0x00, 0x14}, {0x60, 0x17}, - {0x0e, 0x18}, {0xf2, 0x19}); - - err += sn9c102_i2c_write(cam, 0x01, 0x04); - err += sn9c102_i2c_write(cam, 0x02, 0x00); - err += sn9c102_i2c_write(cam, 0x28, 0x00); - - return err; -} - - -static int hv7131d_get_ctrl(struct sn9c102_device* cam, - struct v4l2_control* ctrl) -{ - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - { - int r1 = sn9c102_i2c_read(cam, 0x26), - r2 = sn9c102_i2c_read(cam, 0x27); - if (r1 < 0 || r2 < 0) - return -EIO; - ctrl->value = (r1 << 8) | (r2 & 0xff); - } - return 0; - case V4L2_CID_RED_BALANCE: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x31)) < 0) - return -EIO; - ctrl->value = 0x3f - (ctrl->value & 0x3f); - return 0; - case V4L2_CID_BLUE_BALANCE: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x33)) < 0) - return -EIO; - ctrl->value = 0x3f - (ctrl->value & 0x3f); - return 0; - case SN9C102_V4L2_CID_GREEN_BALANCE: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x32)) < 0) - return -EIO; - ctrl->value = 0x3f - (ctrl->value & 0x3f); - return 0; - case SN9C102_V4L2_CID_RESET_LEVEL: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x30)) < 0) - return -EIO; - ctrl->value &= 0x3f; - return 0; - case SN9C102_V4L2_CID_PIXEL_BIAS_VOLTAGE: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x34)) < 0) - return -EIO; - ctrl->value &= 0x07; - return 0; - default: - return -EINVAL; - } -} - - -static int hv7131d_set_ctrl(struct sn9c102_device* cam, - const struct v4l2_control* ctrl) -{ - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - err += sn9c102_i2c_write(cam, 0x26, ctrl->value >> 8); - err += sn9c102_i2c_write(cam, 0x27, ctrl->value & 0xff); - break; - case V4L2_CID_RED_BALANCE: - err += sn9c102_i2c_write(cam, 0x31, 0x3f - ctrl->value); - break; - case V4L2_CID_BLUE_BALANCE: - err += sn9c102_i2c_write(cam, 0x33, 0x3f - ctrl->value); - break; - case SN9C102_V4L2_CID_GREEN_BALANCE: - err += sn9c102_i2c_write(cam, 0x32, 0x3f - ctrl->value); - break; - case SN9C102_V4L2_CID_RESET_LEVEL: - err += sn9c102_i2c_write(cam, 0x30, ctrl->value); - break; - case SN9C102_V4L2_CID_PIXEL_BIAS_VOLTAGE: - err += sn9c102_i2c_write(cam, 0x34, ctrl->value); - break; - default: - return -EINVAL; - } - - return err ? -EIO : 0; -} - - -static int hv7131d_set_crop(struct sn9c102_device* cam, - const struct v4l2_rect* rect) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - int err = 0; - u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 2, - v_start = (u8)(rect->top - s->cropcap.bounds.top) + 2; - - err += sn9c102_write_reg(cam, h_start, 0x12); - err += sn9c102_write_reg(cam, v_start, 0x13); - - return err; -} - - -static int hv7131d_set_pix_format(struct sn9c102_device* cam, - const struct v4l2_pix_format* pix) -{ - int err = 0; - - if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) - err += sn9c102_write_reg(cam, 0x42, 0x19); - else - err += sn9c102_write_reg(cam, 0xf2, 0x19); - - return err; -} - - -static const struct sn9c102_sensor hv7131d = { - .name = "HV7131D", - .maintainer = "Luca Risolia ", - .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102, - .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, - .frequency = SN9C102_I2C_100KHZ, - .interface = SN9C102_I2C_2WIRES, - .i2c_slave_id = 0x11, - .init = &hv7131d_init, - .qctrl = { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0x0250, - .maximum = 0xffff, - .step = 0x0001, - .default_value = 0x0250, - .flags = 0, - }, - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "red balance", - .minimum = 0x00, - .maximum = 0x3f, - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "blue balance", - .minimum = 0x00, - .maximum = 0x3f, - .step = 0x01, - .default_value = 0x20, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_GREEN_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "green balance", - .minimum = 0x00, - .maximum = 0x3f, - .step = 0x01, - .default_value = 0x1e, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_RESET_LEVEL, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "reset level", - .minimum = 0x19, - .maximum = 0x3f, - .step = 0x01, - .default_value = 0x30, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_PIXEL_BIAS_VOLTAGE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "pixel bias voltage", - .minimum = 0x00, - .maximum = 0x07, - .step = 0x01, - .default_value = 0x02, - .flags = 0, - }, - }, - .get_ctrl = &hv7131d_get_ctrl, - .set_ctrl = &hv7131d_set_ctrl, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - }, - .set_crop = &hv7131d_set_crop, - .pix_format = { - .width = 640, - .height = 480, - .pixelformat = V4L2_PIX_FMT_SBGGR8, - .priv = 8, - }, - .set_pix_format = &hv7131d_set_pix_format -}; - - -int sn9c102_probe_hv7131d(struct sn9c102_device* cam) -{ - int r0 = 0, r1 = 0, err; - - err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x00, 0x01}, - {0x28, 0x17}); - - r0 = sn9c102_i2c_try_read(cam, &hv7131d, 0x00); - r1 = sn9c102_i2c_try_read(cam, &hv7131d, 0x01); - if (err || r0 < 0 || r1 < 0) - return -EIO; - - if ((r0 != 0x00 && r0 != 0x01) || r1 != 0x04) - return -ENODEV; - - sn9c102_attach_sensor(cam, &hv7131d); - - return 0; -} diff --git a/drivers/media/usb/sn9c102/sn9c102_hv7131r.c b/drivers/media/usb/sn9c102/sn9c102_hv7131r.c deleted file mode 100644 index 4295887ff609..000000000000 --- a/drivers/media/usb/sn9c102/sn9c102_hv7131r.c +++ /dev/null @@ -1,363 +0,0 @@ -/*************************************************************************** - * Plug-in for HV7131R image sensor connected to the SN9C1xx PC Camera * - * Controllers * - * * - * Copyright (C) 2007 by Luca Risolia * - * * - * 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. * - ***************************************************************************/ - -#include "sn9c102_sensor.h" -#include "sn9c102_devtable.h" - - -static int hv7131r_init(struct sn9c102_device* cam) -{ - int err = 0; - - switch (sn9c102_get_bridge(cam)) { - case BRIDGE_SN9C103: - err = sn9c102_write_const_regs(cam, {0x00, 0x03}, {0x1a, 0x04}, - {0x20, 0x05}, {0x20, 0x06}, - {0x03, 0x10}, {0x00, 0x14}, - {0x60, 0x17}, {0x0a, 0x18}, - {0xf0, 0x19}, {0x1d, 0x1a}, - {0x10, 0x1b}, {0x02, 0x1c}, - {0x03, 0x1d}, {0x0f, 0x1e}, - {0x0c, 0x1f}, {0x00, 0x20}, - {0x10, 0x21}, {0x20, 0x22}, - {0x30, 0x23}, {0x40, 0x24}, - {0x50, 0x25}, {0x60, 0x26}, - {0x70, 0x27}, {0x80, 0x28}, - {0x90, 0x29}, {0xa0, 0x2a}, - {0xb0, 0x2b}, {0xc0, 0x2c}, - {0xd0, 0x2d}, {0xe0, 0x2e}, - {0xf0, 0x2f}, {0xff, 0x30}); - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - err = sn9c102_write_const_regs(cam, {0x44, 0x01}, {0x40, 0x02}, - {0x00, 0x03}, {0x1a, 0x04}, - {0x44, 0x05}, {0x3e, 0x06}, - {0x1a, 0x07}, {0x03, 0x10}, - {0x08, 0x14}, {0xa3, 0x17}, - {0x4b, 0x18}, {0x00, 0x19}, - {0x1d, 0x1a}, {0x10, 0x1b}, - {0x02, 0x1c}, {0x03, 0x1d}, - {0x0f, 0x1e}, {0x0c, 0x1f}, - {0x00, 0x20}, {0x29, 0x21}, - {0x40, 0x22}, {0x54, 0x23}, - {0x66, 0x24}, {0x76, 0x25}, - {0x85, 0x26}, {0x94, 0x27}, - {0xa1, 0x28}, {0xae, 0x29}, - {0xbb, 0x2a}, {0xc7, 0x2b}, - {0xd3, 0x2c}, {0xde, 0x2d}, - {0xea, 0x2e}, {0xf4, 0x2f}, - {0xff, 0x30}, {0x00, 0x3F}, - {0xC7, 0x40}, {0x01, 0x41}, - {0x44, 0x42}, {0x00, 0x43}, - {0x44, 0x44}, {0x00, 0x45}, - {0x44, 0x46}, {0x00, 0x47}, - {0xC7, 0x48}, {0x01, 0x49}, - {0xC7, 0x4A}, {0x01, 0x4B}, - {0xC7, 0x4C}, {0x01, 0x4D}, - {0x44, 0x4E}, {0x00, 0x4F}, - {0x44, 0x50}, {0x00, 0x51}, - {0x44, 0x52}, {0x00, 0x53}, - {0xC7, 0x54}, {0x01, 0x55}, - {0xC7, 0x56}, {0x01, 0x57}, - {0xC7, 0x58}, {0x01, 0x59}, - {0x44, 0x5A}, {0x00, 0x5B}, - {0x44, 0x5C}, {0x00, 0x5D}, - {0x44, 0x5E}, {0x00, 0x5F}, - {0xC7, 0x60}, {0x01, 0x61}, - {0xC7, 0x62}, {0x01, 0x63}, - {0xC7, 0x64}, {0x01, 0x65}, - {0x44, 0x66}, {0x00, 0x67}, - {0x44, 0x68}, {0x00, 0x69}, - {0x44, 0x6A}, {0x00, 0x6B}, - {0xC7, 0x6C}, {0x01, 0x6D}, - {0xC7, 0x6E}, {0x01, 0x6F}, - {0xC7, 0x70}, {0x01, 0x71}, - {0x44, 0x72}, {0x00, 0x73}, - {0x44, 0x74}, {0x00, 0x75}, - {0x44, 0x76}, {0x00, 0x77}, - {0xC7, 0x78}, {0x01, 0x79}, - {0xC7, 0x7A}, {0x01, 0x7B}, - {0xC7, 0x7C}, {0x01, 0x7D}, - {0x44, 0x7E}, {0x00, 0x7F}, - {0x14, 0x84}, {0x00, 0x85}, - {0x27, 0x86}, {0x00, 0x87}, - {0x07, 0x88}, {0x00, 0x89}, - {0xEC, 0x8A}, {0x0f, 0x8B}, - {0xD8, 0x8C}, {0x0f, 0x8D}, - {0x3D, 0x8E}, {0x00, 0x8F}, - {0x3D, 0x90}, {0x00, 0x91}, - {0xCD, 0x92}, {0x0f, 0x93}, - {0xf7, 0x94}, {0x0f, 0x95}, - {0x0C, 0x96}, {0x00, 0x97}, - {0x00, 0x98}, {0x66, 0x99}, - {0x05, 0x9A}, {0x00, 0x9B}, - {0x04, 0x9C}, {0x00, 0x9D}, - {0x08, 0x9E}, {0x00, 0x9F}, - {0x2D, 0xC0}, {0x2D, 0xC1}, - {0x3A, 0xC2}, {0x05, 0xC3}, - {0x04, 0xC4}, {0x3F, 0xC5}, - {0x00, 0xC6}, {0x00, 0xC7}, - {0x50, 0xC8}, {0x3C, 0xC9}, - {0x28, 0xCA}, {0xD8, 0xCB}, - {0x14, 0xCC}, {0xEC, 0xCD}, - {0x32, 0xCE}, {0xDD, 0xCF}, - {0x32, 0xD0}, {0xDD, 0xD1}, - {0x6A, 0xD2}, {0x50, 0xD3}, - {0x00, 0xD4}, {0x00, 0xD5}, - {0x00, 0xD6}); - break; - default: - break; - } - - err += sn9c102_i2c_write(cam, 0x20, 0x00); - err += sn9c102_i2c_write(cam, 0x21, 0xd6); - err += sn9c102_i2c_write(cam, 0x25, 0x06); - - return err; -} - - -static int hv7131r_get_ctrl(struct sn9c102_device* cam, - struct v4l2_control* ctrl) -{ - switch (ctrl->id) { - case V4L2_CID_GAIN: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x30)) < 0) - return -EIO; - return 0; - case V4L2_CID_RED_BALANCE: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x31)) < 0) - return -EIO; - ctrl->value = ctrl->value & 0x3f; - return 0; - case V4L2_CID_BLUE_BALANCE: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x33)) < 0) - return -EIO; - ctrl->value = ctrl->value & 0x3f; - return 0; - case SN9C102_V4L2_CID_GREEN_BALANCE: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x32)) < 0) - return -EIO; - ctrl->value = ctrl->value & 0x3f; - return 0; - case V4L2_CID_BLACK_LEVEL: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x01)) < 0) - return -EIO; - ctrl->value = (ctrl->value & 0x08) ? 1 : 0; - return 0; - default: - return -EINVAL; - } -} - - -static int hv7131r_set_ctrl(struct sn9c102_device* cam, - const struct v4l2_control* ctrl) -{ - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_GAIN: - err += sn9c102_i2c_write(cam, 0x30, ctrl->value); - break; - case V4L2_CID_RED_BALANCE: - err += sn9c102_i2c_write(cam, 0x31, ctrl->value); - break; - case V4L2_CID_BLUE_BALANCE: - err += sn9c102_i2c_write(cam, 0x33, ctrl->value); - break; - case SN9C102_V4L2_CID_GREEN_BALANCE: - err += sn9c102_i2c_write(cam, 0x32, ctrl->value); - break; - case V4L2_CID_BLACK_LEVEL: - { - int r = sn9c102_i2c_read(cam, 0x01); - if (r < 0) - return -EIO; - err += sn9c102_i2c_write(cam, 0x01, - (ctrl->value<<3) | (r&0xf7)); - } - break; - default: - return -EINVAL; - } - - return err ? -EIO : 0; -} - - -static int hv7131r_set_crop(struct sn9c102_device* cam, - const struct v4l2_rect* rect) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - int err = 0; - u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 1, - v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1; - - err += sn9c102_write_reg(cam, h_start, 0x12); - err += sn9c102_write_reg(cam, v_start, 0x13); - - return err; -} - - -static int hv7131r_set_pix_format(struct sn9c102_device* cam, - const struct v4l2_pix_format* pix) -{ - int err = 0; - - switch (sn9c102_get_bridge(cam)) { - case BRIDGE_SN9C103: - if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) { - err += sn9c102_write_reg(cam, 0xa0, 0x19); - err += sn9c102_i2c_write(cam, 0x01, 0x04); - } else { - err += sn9c102_write_reg(cam, 0x30, 0x19); - err += sn9c102_i2c_write(cam, 0x01, 0x04); - } - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) { - err += sn9c102_write_reg(cam, 0xa5, 0x17); - err += sn9c102_i2c_write(cam, 0x01, 0x24); - } else { - err += sn9c102_write_reg(cam, 0xa3, 0x17); - err += sn9c102_i2c_write(cam, 0x01, 0x04); - } - break; - default: - break; - } - - return err; -} - - -static const struct sn9c102_sensor hv7131r = { - .name = "HV7131R", - .maintainer = "Luca Risolia ", - .supported_bridge = BRIDGE_SN9C103 | BRIDGE_SN9C105 | BRIDGE_SN9C120, - .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, - .frequency = SN9C102_I2C_100KHZ, - .interface = SN9C102_I2C_2WIRES, - .i2c_slave_id = 0x11, - .init = &hv7131r_init, - .qctrl = { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "global gain", - .minimum = 0x00, - .maximum = 0xff, - .step = 0x01, - .default_value = 0x40, - .flags = 0, - }, - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "red balance", - .minimum = 0x00, - .maximum = 0x3f, - .step = 0x01, - .default_value = 0x08, - .flags = 0, - }, - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "blue balance", - .minimum = 0x00, - .maximum = 0x3f, - .step = 0x01, - .default_value = 0x1a, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_GREEN_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "green balance", - .minimum = 0x00, - .maximum = 0x3f, - .step = 0x01, - .default_value = 0x2f, - .flags = 0, - }, - { - .id = V4L2_CID_BLACK_LEVEL, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "auto black level compensation", - .minimum = 0x00, - .maximum = 0x01, - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - }, - .get_ctrl = &hv7131r_get_ctrl, - .set_ctrl = &hv7131r_set_ctrl, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - }, - .set_crop = &hv7131r_set_crop, - .pix_format = { - .width = 640, - .height = 480, - .pixelformat = V4L2_PIX_FMT_SBGGR8, - .priv = 8, - }, - .set_pix_format = &hv7131r_set_pix_format -}; - - -int sn9c102_probe_hv7131r(struct sn9c102_device* cam) -{ - int devid, err; - - err = sn9c102_write_const_regs(cam, {0x09, 0x01}, {0x44, 0x02}, - {0x34, 0x01}, {0x20, 0x17}, - {0x34, 0x01}, {0x46, 0x01}); - - devid = sn9c102_i2c_try_read(cam, &hv7131r, 0x00); - if (err || devid < 0) - return -EIO; - - if (devid != 0x02) - return -ENODEV; - - sn9c102_attach_sensor(cam, &hv7131r); - - return 0; -} diff --git a/drivers/media/usb/sn9c102/sn9c102_mi0343.c b/drivers/media/usb/sn9c102/sn9c102_mi0343.c deleted file mode 100644 index 1f5b09bec89c..000000000000 --- a/drivers/media/usb/sn9c102/sn9c102_mi0343.c +++ /dev/null @@ -1,352 +0,0 @@ -/*************************************************************************** - * Plug-in for MI-0343 image sensor connected to the SN9C1xx PC Camera * - * Controllers * - * * - * Copyright (C) 2004-2007 by Luca Risolia * - * * - * 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. * - ***************************************************************************/ - -#include "sn9c102_sensor.h" -#include "sn9c102_devtable.h" - - -static int mi0343_init(struct sn9c102_device* cam) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - int err = 0; - - err = sn9c102_write_const_regs(cam, {0x00, 0x10}, {0x00, 0x11}, - {0x0a, 0x14}, {0x40, 0x01}, - {0x20, 0x17}, {0x07, 0x18}, - {0xa0, 0x19}); - - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d, - 0x00, 0x01, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d, - 0x00, 0x00, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x03, - 0x01, 0xe1, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x04, - 0x02, 0x81, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x05, - 0x00, 0x17, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x06, - 0x00, 0x11, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x62, - 0x04, 0x9a, 0, 0); - - return err; -} - - -static int mi0343_get_ctrl(struct sn9c102_device* cam, - struct v4l2_control* ctrl) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - u8 data[2]; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x09, 2, - data) < 0) - return -EIO; - ctrl->value = data[0]; - return 0; - case V4L2_CID_GAIN: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x35, 2, - data) < 0) - return -EIO; - break; - case V4L2_CID_HFLIP: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20, 2, - data) < 0) - return -EIO; - ctrl->value = data[1] & 0x20 ? 1 : 0; - return 0; - case V4L2_CID_VFLIP: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20, 2, - data) < 0) - return -EIO; - ctrl->value = data[1] & 0x80 ? 1 : 0; - return 0; - case V4L2_CID_RED_BALANCE: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2d, 2, - data) < 0) - return -EIO; - break; - case V4L2_CID_BLUE_BALANCE: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2c, 2, - data) < 0) - return -EIO; - break; - case SN9C102_V4L2_CID_GREEN_BALANCE: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2e, 2, - data) < 0) - return -EIO; - break; - default: - return -EINVAL; - } - - switch (ctrl->id) { - case V4L2_CID_GAIN: - case V4L2_CID_RED_BALANCE: - case V4L2_CID_BLUE_BALANCE: - case SN9C102_V4L2_CID_GREEN_BALANCE: - ctrl->value = data[1] | (data[0] << 8); - if (ctrl->value >= 0x10 && ctrl->value <= 0x3f) - ctrl->value -= 0x10; - else if (ctrl->value >= 0x60 && ctrl->value <= 0x7f) - ctrl->value -= 0x60; - else if (ctrl->value >= 0xe0 && ctrl->value <= 0xff) - ctrl->value -= 0xe0; - } - - return 0; -} - - -static int mi0343_set_ctrl(struct sn9c102_device* cam, - const struct v4l2_control* ctrl) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - u16 reg = 0; - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_GAIN: - case V4L2_CID_RED_BALANCE: - case V4L2_CID_BLUE_BALANCE: - case SN9C102_V4L2_CID_GREEN_BALANCE: - if (ctrl->value <= (0x3f-0x10)) - reg = 0x10 + ctrl->value; - else if (ctrl->value <= ((0x3f-0x10) + (0x7f-0x60))) - reg = 0x60 + (ctrl->value - (0x3f-0x10)); - else - reg = 0xe0 + (ctrl->value - (0x3f-0x10) - (0x7f-0x60)); - break; - } - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x09, ctrl->value, 0x00, - 0, 0); - break; - case V4L2_CID_GAIN: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x35, reg >> 8, reg & 0xff, - 0, 0); - break; - case V4L2_CID_HFLIP: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x20, ctrl->value ? 0x40:0x00, - ctrl->value ? 0x20:0x00, - 0, 0); - break; - case V4L2_CID_VFLIP: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x20, ctrl->value ? 0x80:0x00, - ctrl->value ? 0x80:0x00, - 0, 0); - break; - case V4L2_CID_RED_BALANCE: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x2d, reg >> 8, reg & 0xff, - 0, 0); - break; - case V4L2_CID_BLUE_BALANCE: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x2c, reg >> 8, reg & 0xff, - 0, 0); - break; - case SN9C102_V4L2_CID_GREEN_BALANCE: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x2b, reg >> 8, reg & 0xff, - 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x2e, reg >> 8, reg & 0xff, - 0, 0); - break; - default: - return -EINVAL; - } - - return err ? -EIO : 0; -} - - -static int mi0343_set_crop(struct sn9c102_device* cam, - const struct v4l2_rect* rect) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - int err = 0; - u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 0, - v_start = (u8)(rect->top - s->cropcap.bounds.top) + 2; - - err += sn9c102_write_reg(cam, h_start, 0x12); - err += sn9c102_write_reg(cam, v_start, 0x13); - - return err; -} - - -static int mi0343_set_pix_format(struct sn9c102_device* cam, - const struct v4l2_pix_format* pix) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - int err = 0; - - if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) { - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x0a, 0x00, 0x03, 0, 0); - err += sn9c102_write_reg(cam, 0x20, 0x19); - } else { - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x0a, 0x00, 0x05, 0, 0); - err += sn9c102_write_reg(cam, 0xa0, 0x19); - } - - return err; -} - - -static const struct sn9c102_sensor mi0343 = { - .name = "MI-0343", - .maintainer = "Luca Risolia ", - .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102, - .frequency = SN9C102_I2C_100KHZ, - .interface = SN9C102_I2C_2WIRES, - .i2c_slave_id = 0x5d, - .init = &mi0343_init, - .qctrl = { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0x00, - .maximum = 0x0f, - .step = 0x01, - .default_value = 0x06, - .flags = 0, - }, - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "global gain", - .minimum = 0x00, - .maximum = (0x3f-0x10)+(0x7f-0x60)+(0xff-0xe0),/*0x6d*/ - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "horizontal mirror", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - .flags = 0, - }, - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "vertical mirror", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - .flags = 0, - }, - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "red balance", - .minimum = 0x00, - .maximum = (0x3f-0x10)+(0x7f-0x60)+(0xff-0xe0), - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "blue balance", - .minimum = 0x00, - .maximum = (0x3f-0x10)+(0x7f-0x60)+(0xff-0xe0), - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_GREEN_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "green balance", - .minimum = 0x00, - .maximum = ((0x3f-0x10)+(0x7f-0x60)+(0xff-0xe0)), - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - }, - .get_ctrl = &mi0343_get_ctrl, - .set_ctrl = &mi0343_set_ctrl, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - }, - .set_crop = &mi0343_set_crop, - .pix_format = { - .width = 640, - .height = 480, - .pixelformat = V4L2_PIX_FMT_SBGGR8, - .priv = 8, - }, - .set_pix_format = &mi0343_set_pix_format -}; - - -int sn9c102_probe_mi0343(struct sn9c102_device* cam) -{ - u8 data[2]; - - if (sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x00, 0x01}, - {0x28, 0x17})) - return -EIO; - - if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id, 0x00, - 2, data) < 0) - return -EIO; - - if (data[1] != 0x42 || data[0] != 0xe3) - return -ENODEV; - - sn9c102_attach_sensor(cam, &mi0343); - - return 0; -} diff --git a/drivers/media/usb/sn9c102/sn9c102_mi0360.c b/drivers/media/usb/sn9c102/sn9c102_mi0360.c deleted file mode 100644 index d973fc1973d9..000000000000 --- a/drivers/media/usb/sn9c102/sn9c102_mi0360.c +++ /dev/null @@ -1,453 +0,0 @@ -/*************************************************************************** - * Plug-in for MI-0360 image sensor connected to the SN9C1xx PC Camera * - * Controllers * - * * - * Copyright (C) 2007 by Luca Risolia * - * * - * 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. * - ***************************************************************************/ - -#include "sn9c102_sensor.h" -#include "sn9c102_devtable.h" - - -static int mi0360_init(struct sn9c102_device* cam) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - int err = 0; - - switch (sn9c102_get_bridge(cam)) { - case BRIDGE_SN9C103: - err = sn9c102_write_const_regs(cam, {0x00, 0x10}, {0x00, 0x11}, - {0x0a, 0x14}, {0x40, 0x01}, - {0x20, 0x17}, {0x07, 0x18}, - {0xa0, 0x19}, {0x02, 0x1c}, - {0x03, 0x1d}, {0x0f, 0x1e}, - {0x0c, 0x1f}, {0x00, 0x20}, - {0x10, 0x21}, {0x20, 0x22}, - {0x30, 0x23}, {0x40, 0x24}, - {0x50, 0x25}, {0x60, 0x26}, - {0x70, 0x27}, {0x80, 0x28}, - {0x90, 0x29}, {0xa0, 0x2a}, - {0xb0, 0x2b}, {0xc0, 0x2c}, - {0xd0, 0x2d}, {0xe0, 0x2e}, - {0xf0, 0x2f}, {0xff, 0x30}); - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - err = sn9c102_write_const_regs(cam, {0x44, 0x01}, {0x40, 0x02}, - {0x00, 0x03}, {0x1a, 0x04}, - {0x50, 0x05}, {0x20, 0x06}, - {0x10, 0x07}, {0x03, 0x10}, - {0x08, 0x14}, {0xa2, 0x17}, - {0x47, 0x18}, {0x00, 0x19}, - {0x1d, 0x1a}, {0x10, 0x1b}, - {0x02, 0x1c}, {0x03, 0x1d}, - {0x0f, 0x1e}, {0x0c, 0x1f}, - {0x00, 0x20}, {0x29, 0x21}, - {0x40, 0x22}, {0x54, 0x23}, - {0x66, 0x24}, {0x76, 0x25}, - {0x85, 0x26}, {0x94, 0x27}, - {0xa1, 0x28}, {0xae, 0x29}, - {0xbb, 0x2a}, {0xc7, 0x2b}, - {0xd3, 0x2c}, {0xde, 0x2d}, - {0xea, 0x2e}, {0xf4, 0x2f}, - {0xff, 0x30}, {0x00, 0x3F}, - {0xC7, 0x40}, {0x01, 0x41}, - {0x44, 0x42}, {0x00, 0x43}, - {0x44, 0x44}, {0x00, 0x45}, - {0x44, 0x46}, {0x00, 0x47}, - {0xC7, 0x48}, {0x01, 0x49}, - {0xC7, 0x4A}, {0x01, 0x4B}, - {0xC7, 0x4C}, {0x01, 0x4D}, - {0x44, 0x4E}, {0x00, 0x4F}, - {0x44, 0x50}, {0x00, 0x51}, - {0x44, 0x52}, {0x00, 0x53}, - {0xC7, 0x54}, {0x01, 0x55}, - {0xC7, 0x56}, {0x01, 0x57}, - {0xC7, 0x58}, {0x01, 0x59}, - {0x44, 0x5A}, {0x00, 0x5B}, - {0x44, 0x5C}, {0x00, 0x5D}, - {0x44, 0x5E}, {0x00, 0x5F}, - {0xC7, 0x60}, {0x01, 0x61}, - {0xC7, 0x62}, {0x01, 0x63}, - {0xC7, 0x64}, {0x01, 0x65}, - {0x44, 0x66}, {0x00, 0x67}, - {0x44, 0x68}, {0x00, 0x69}, - {0x44, 0x6A}, {0x00, 0x6B}, - {0xC7, 0x6C}, {0x01, 0x6D}, - {0xC7, 0x6E}, {0x01, 0x6F}, - {0xC7, 0x70}, {0x01, 0x71}, - {0x44, 0x72}, {0x00, 0x73}, - {0x44, 0x74}, {0x00, 0x75}, - {0x44, 0x76}, {0x00, 0x77}, - {0xC7, 0x78}, {0x01, 0x79}, - {0xC7, 0x7A}, {0x01, 0x7B}, - {0xC7, 0x7C}, {0x01, 0x7D}, - {0x44, 0x7E}, {0x00, 0x7F}, - {0x14, 0x84}, {0x00, 0x85}, - {0x27, 0x86}, {0x00, 0x87}, - {0x07, 0x88}, {0x00, 0x89}, - {0xEC, 0x8A}, {0x0f, 0x8B}, - {0xD8, 0x8C}, {0x0f, 0x8D}, - {0x3D, 0x8E}, {0x00, 0x8F}, - {0x3D, 0x90}, {0x00, 0x91}, - {0xCD, 0x92}, {0x0f, 0x93}, - {0xf7, 0x94}, {0x0f, 0x95}, - {0x0C, 0x96}, {0x00, 0x97}, - {0x00, 0x98}, {0x66, 0x99}, - {0x05, 0x9A}, {0x00, 0x9B}, - {0x04, 0x9C}, {0x00, 0x9D}, - {0x08, 0x9E}, {0x00, 0x9F}, - {0x2D, 0xC0}, {0x2D, 0xC1}, - {0x3A, 0xC2}, {0x05, 0xC3}, - {0x04, 0xC4}, {0x3F, 0xC5}, - {0x00, 0xC6}, {0x00, 0xC7}, - {0x50, 0xC8}, {0x3C, 0xC9}, - {0x28, 0xCA}, {0xD8, 0xCB}, - {0x14, 0xCC}, {0xEC, 0xCD}, - {0x32, 0xCE}, {0xDD, 0xCF}, - {0x32, 0xD0}, {0xDD, 0xD1}, - {0x6A, 0xD2}, {0x50, 0xD3}, - {0x00, 0xD4}, {0x00, 0xD5}, - {0x00, 0xD6}); - break; - default: - break; - } - - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d, - 0x00, 0x01, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d, - 0x00, 0x00, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x03, - 0x01, 0xe1, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x04, - 0x02, 0x81, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x05, - 0x00, 0x17, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x06, - 0x00, 0x11, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x62, - 0x04, 0x9a, 0, 0); - - return err; -} - - -static int mi0360_get_ctrl(struct sn9c102_device* cam, - struct v4l2_control* ctrl) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - u8 data[2]; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x09, 2, - data) < 0) - return -EIO; - ctrl->value = data[0]; - return 0; - case V4L2_CID_GAIN: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x35, 2, - data) < 0) - return -EIO; - ctrl->value = data[1]; - return 0; - case V4L2_CID_RED_BALANCE: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2c, 2, - data) < 0) - return -EIO; - ctrl->value = data[1]; - return 0; - case V4L2_CID_BLUE_BALANCE: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2d, 2, - data) < 0) - return -EIO; - ctrl->value = data[1]; - return 0; - case SN9C102_V4L2_CID_GREEN_BALANCE: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2e, 2, - data) < 0) - return -EIO; - ctrl->value = data[1]; - return 0; - case V4L2_CID_HFLIP: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20, 2, - data) < 0) - return -EIO; - ctrl->value = data[1] & 0x20 ? 1 : 0; - return 0; - case V4L2_CID_VFLIP: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20, 2, - data) < 0) - return -EIO; - ctrl->value = data[1] & 0x80 ? 1 : 0; - return 0; - default: - return -EINVAL; - } - - return 0; -} - - -static int mi0360_set_ctrl(struct sn9c102_device* cam, - const struct v4l2_control* ctrl) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x09, ctrl->value, 0x00, - 0, 0); - break; - case V4L2_CID_GAIN: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x35, 0x03, ctrl->value, - 0, 0); - break; - case V4L2_CID_RED_BALANCE: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x2c, 0x03, ctrl->value, - 0, 0); - break; - case V4L2_CID_BLUE_BALANCE: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x2d, 0x03, ctrl->value, - 0, 0); - break; - case SN9C102_V4L2_CID_GREEN_BALANCE: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x2b, 0x03, ctrl->value, - 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x2e, 0x03, ctrl->value, - 0, 0); - break; - case V4L2_CID_HFLIP: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x20, ctrl->value ? 0x40:0x00, - ctrl->value ? 0x20:0x00, - 0, 0); - break; - case V4L2_CID_VFLIP: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x20, ctrl->value ? 0x80:0x00, - ctrl->value ? 0x80:0x00, - 0, 0); - break; - default: - return -EINVAL; - } - - return err ? -EIO : 0; -} - - -static int mi0360_set_crop(struct sn9c102_device* cam, - const struct v4l2_rect* rect) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - int err = 0; - u8 h_start = 0, v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1; - - switch (sn9c102_get_bridge(cam)) { - case BRIDGE_SN9C103: - h_start = (u8)(rect->left - s->cropcap.bounds.left) + 0; - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - h_start = (u8)(rect->left - s->cropcap.bounds.left) + 1; - break; - default: - break; - } - - err += sn9c102_write_reg(cam, h_start, 0x12); - err += sn9c102_write_reg(cam, v_start, 0x13); - - return err; -} - - -static int mi0360_set_pix_format(struct sn9c102_device* cam, - const struct v4l2_pix_format* pix) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - int err = 0; - - if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) { - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x0a, 0x00, 0x05, 0, 0); - err += sn9c102_write_reg(cam, 0x60, 0x19); - if (sn9c102_get_bridge(cam) == BRIDGE_SN9C105 || - sn9c102_get_bridge(cam) == BRIDGE_SN9C120) - err += sn9c102_write_reg(cam, 0xa6, 0x17); - } else { - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x0a, 0x00, 0x02, 0, 0); - err += sn9c102_write_reg(cam, 0x20, 0x19); - if (sn9c102_get_bridge(cam) == BRIDGE_SN9C105 || - sn9c102_get_bridge(cam) == BRIDGE_SN9C120) - err += sn9c102_write_reg(cam, 0xa2, 0x17); - } - - return err; -} - - -static const struct sn9c102_sensor mi0360 = { - .name = "MI-0360", - .maintainer = "Luca Risolia ", - .supported_bridge = BRIDGE_SN9C103 | BRIDGE_SN9C105 | BRIDGE_SN9C120, - .frequency = SN9C102_I2C_100KHZ, - .interface = SN9C102_I2C_2WIRES, - .i2c_slave_id = 0x5d, - .init = &mi0360_init, - .qctrl = { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0x00, - .maximum = 0x0f, - .step = 0x01, - .default_value = 0x05, - .flags = 0, - }, - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "global gain", - .minimum = 0x00, - .maximum = 0x7f, - .step = 0x01, - .default_value = 0x25, - .flags = 0, - }, - { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "horizontal mirror", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - .flags = 0, - }, - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "vertical mirror", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - .flags = 0, - }, - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "blue balance", - .minimum = 0x00, - .maximum = 0x7f, - .step = 0x01, - .default_value = 0x0f, - .flags = 0, - }, - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "red balance", - .minimum = 0x00, - .maximum = 0x7f, - .step = 0x01, - .default_value = 0x32, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_GREEN_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "green balance", - .minimum = 0x00, - .maximum = 0x7f, - .step = 0x01, - .default_value = 0x25, - .flags = 0, - }, - }, - .get_ctrl = &mi0360_get_ctrl, - .set_ctrl = &mi0360_set_ctrl, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - }, - .set_crop = &mi0360_set_crop, - .pix_format = { - .width = 640, - .height = 480, - .pixelformat = V4L2_PIX_FMT_SBGGR8, - .priv = 8, - }, - .set_pix_format = &mi0360_set_pix_format -}; - - -int sn9c102_probe_mi0360(struct sn9c102_device* cam) -{ - - u8 data[2]; - - switch (sn9c102_get_bridge(cam)) { - case BRIDGE_SN9C103: - if (sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x00, 0x01}, - {0x28, 0x17})) - return -EIO; - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - if (sn9c102_write_const_regs(cam, {0x01, 0xf1}, {0x00, 0xf1}, - {0x01, 0x01}, {0x00, 0x01}, - {0x28, 0x17})) - return -EIO; - break; - default: - break; - } - - if (sn9c102_i2c_try_raw_read(cam, &mi0360, mi0360.i2c_slave_id, 0x00, - 2, data) < 0) - return -EIO; - - if (data[0] != 0x82 || data[1] != 0x43) - return -ENODEV; - - sn9c102_attach_sensor(cam, &mi0360); - - return 0; -} diff --git a/drivers/media/usb/sn9c102/sn9c102_mt9v111.c b/drivers/media/usb/sn9c102/sn9c102_mt9v111.c deleted file mode 100644 index 95986eb492e4..000000000000 --- a/drivers/media/usb/sn9c102/sn9c102_mt9v111.c +++ /dev/null @@ -1,260 +0,0 @@ -/*************************************************************************** - * Plug-in for MT9V111 image sensor connected to the SN9C1xx PC Camera * - * Controllers * - * * - * Copyright (C) 2007 by Luca Risolia * - * * - * 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. * - ***************************************************************************/ - -#include "sn9c102_sensor.h" -#include "sn9c102_devtable.h" - - -static int mt9v111_init(struct sn9c102_device *cam) -{ - struct sn9c102_sensor *s = sn9c102_get_sensor(cam); - int err = 0; - - err = sn9c102_write_const_regs(cam, {0x44, 0x01}, {0x40, 0x02}, - {0x00, 0x03}, {0x1a, 0x04}, - {0x1f, 0x05}, {0x20, 0x06}, - {0x1f, 0x07}, {0x81, 0x08}, - {0x5c, 0x09}, {0x00, 0x0a}, - {0x00, 0x0b}, {0x00, 0x0c}, - {0x00, 0x0d}, {0x00, 0x0e}, - {0x00, 0x0f}, {0x03, 0x10}, - {0x00, 0x11}, {0x00, 0x12}, - {0x02, 0x13}, {0x14, 0x14}, - {0x28, 0x15}, {0x1e, 0x16}, - {0xe2, 0x17}, {0x06, 0x18}, - {0x00, 0x19}, {0x00, 0x1a}, - {0x00, 0x1b}, {0x08, 0x20}, - {0x39, 0x21}, {0x51, 0x22}, - {0x63, 0x23}, {0x73, 0x24}, - {0x82, 0x25}, {0x8f, 0x26}, - {0x9b, 0x27}, {0xa7, 0x28}, - {0xb1, 0x29}, {0xbc, 0x2a}, - {0xc6, 0x2b}, {0xcf, 0x2c}, - {0xd8, 0x2d}, {0xe1, 0x2e}, - {0xea, 0x2f}, {0xf2, 0x30}, - {0x13, 0x84}, {0x00, 0x85}, - {0x25, 0x86}, {0x00, 0x87}, - {0x07, 0x88}, {0x00, 0x89}, - {0xee, 0x8a}, {0x0f, 0x8b}, - {0xe5, 0x8c}, {0x0f, 0x8d}, - {0x2e, 0x8e}, {0x00, 0x8f}, - {0x30, 0x90}, {0x00, 0x91}, - {0xd4, 0x92}, {0x0f, 0x93}, - {0xfc, 0x94}, {0x0f, 0x95}, - {0x14, 0x96}, {0x00, 0x97}, - {0x00, 0x98}, {0x60, 0x99}, - {0x07, 0x9a}, {0x40, 0x9b}, - {0x20, 0x9c}, {0x00, 0x9d}, - {0x00, 0x9e}, {0x00, 0x9f}, - {0x2d, 0xc0}, {0x2d, 0xc1}, - {0x3a, 0xc2}, {0x05, 0xc3}, - {0x04, 0xc4}, {0x3f, 0xc5}, - {0x00, 0xc6}, {0x00, 0xc7}, - {0x50, 0xc8}, {0x3c, 0xc9}, - {0x28, 0xca}, {0xd8, 0xcb}, - {0x14, 0xcc}, {0xec, 0xcd}, - {0x32, 0xce}, {0xdd, 0xcf}, - {0x2d, 0xd0}, {0xdd, 0xd1}, - {0x6a, 0xd2}, {0x50, 0xd3}, - {0x60, 0xd4}, {0x00, 0xd5}, - {0x00, 0xd6}); - - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x01, - 0x00, 0x01, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d, - 0x00, 0x01, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d, - 0x00, 0x00, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x08, - 0x04, 0x80, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x01, - 0x00, 0x04, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x08, - 0x00, 0x08, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x02, - 0x00, 0x16, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x03, - 0x01, 0xe7, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x04, - 0x02, 0x87, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x06, - 0x00, 0x40, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x05, - 0x00, 0x09, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x07, - 0x30, 0x02, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0c, - 0x00, 0x00, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x12, - 0x00, 0xb0, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x13, - 0x00, 0x7c, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x1e, - 0x00, 0x00, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x20, - 0x00, 0x00, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x20, - 0x00, 0x00, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x01, - 0x00, 0x04, 0, 0); - - return err; -} - -static int mt9v111_get_ctrl(struct sn9c102_device *cam, - struct v4l2_control *ctrl) -{ - struct sn9c102_sensor *s = sn9c102_get_sensor(cam); - u8 data[2]; - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_VFLIP: - if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20, 2, - data) < 0) - return -EIO; - ctrl->value = data[1] & 0x80 ? 1 : 0; - return 0; - default: - return -EINVAL; - } - - return err ? -EIO : 0; -} - -static int mt9v111_set_ctrl(struct sn9c102_device *cam, - const struct v4l2_control *ctrl) -{ - struct sn9c102_sensor *s = sn9c102_get_sensor(cam); - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_VFLIP: - err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, - 0x20, - ctrl->value ? 0x80 : 0x00, - ctrl->value ? 0x80 : 0x00, 0, - 0); - break; - default: - return -EINVAL; - } - - return err ? -EIO : 0; -} - -static int mt9v111_set_crop(struct sn9c102_device *cam, - const struct v4l2_rect *rect) -{ - struct sn9c102_sensor *s = sn9c102_get_sensor(cam); - int err = 0; - u8 v_start = (u8) (rect->top - s->cropcap.bounds.top) + 2; - - err += sn9c102_write_reg(cam, v_start, 0x13); - - return err; -} - -static int mt9v111_set_pix_format(struct sn9c102_device *cam, - const struct v4l2_pix_format *pix) -{ - int err = 0; - - if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) { - err += sn9c102_write_reg(cam, 0xb4, 0x17); - } else { - err += sn9c102_write_reg(cam, 0xe2, 0x17); - } - - return err; -} - - -static const struct sn9c102_sensor mt9v111 = { - .name = "MT9V111", - .maintainer = "Luca Risolia ", - .supported_bridge = BRIDGE_SN9C105 | BRIDGE_SN9C120, - .frequency = SN9C102_I2C_100KHZ, - .interface = SN9C102_I2C_2WIRES, - .i2c_slave_id = 0x5c, - .init = &mt9v111_init, - .qctrl = { - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "vertical mirror", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - .flags = 0, - }, - }, - .get_ctrl = &mt9v111_get_ctrl, - .set_ctrl = &mt9v111_set_ctrl, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - }, - .set_crop = &mt9v111_set_crop, - .pix_format = { - .width = 640, - .height = 480, - .pixelformat = V4L2_PIX_FMT_SBGGR8, - .priv = 8, - }, - .set_pix_format = &mt9v111_set_pix_format -}; - - -int sn9c102_probe_mt9v111(struct sn9c102_device *cam) -{ - u8 data[2]; - int err = 0; - - err += sn9c102_write_const_regs(cam, {0x01, 0xf1}, {0x00, 0xf1}, - {0x29, 0x01}, {0x42, 0x17}, - {0x62, 0x17}, {0x08, 0x01}); - err += sn9c102_i2c_try_raw_write(cam, &mt9v111, 4, - mt9v111.i2c_slave_id, 0x01, 0x00, - 0x04, 0, 0); - if (err || sn9c102_i2c_try_raw_read(cam, &mt9v111, - mt9v111.i2c_slave_id, 0x36, 2, - data) < 0) - return -EIO; - - if (data[0] != 0x82 || data[1] != 0x3a) - return -ENODEV; - - sn9c102_attach_sensor(cam, &mt9v111); - - return 0; -} diff --git a/drivers/media/usb/sn9c102/sn9c102_ov7630.c b/drivers/media/usb/sn9c102/sn9c102_ov7630.c deleted file mode 100644 index 803712c29f02..000000000000 --- a/drivers/media/usb/sn9c102/sn9c102_ov7630.c +++ /dev/null @@ -1,626 +0,0 @@ -/*************************************************************************** - * Plug-in for OV7630 image sensor connected to the SN9C1xx PC Camera * - * Controllers * - * * - * Copyright (C) 2006-2007 by Luca Risolia * - * * - * 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. * - ***************************************************************************/ - -#include "sn9c102_sensor.h" -#include "sn9c102_devtable.h" - - -static int ov7630_init(struct sn9c102_device* cam) -{ - int err = 0; - - switch (sn9c102_get_bridge(cam)) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - err = sn9c102_write_const_regs(cam, {0x00, 0x14}, {0x60, 0x17}, - {0x0f, 0x18}, {0x50, 0x19}); - - err += sn9c102_i2c_write(cam, 0x12, 0x8d); - err += sn9c102_i2c_write(cam, 0x12, 0x0d); - err += sn9c102_i2c_write(cam, 0x11, 0x00); - err += sn9c102_i2c_write(cam, 0x15, 0x35); - err += sn9c102_i2c_write(cam, 0x16, 0x03); - err += sn9c102_i2c_write(cam, 0x17, 0x1c); - err += sn9c102_i2c_write(cam, 0x18, 0xbd); - err += sn9c102_i2c_write(cam, 0x19, 0x06); - err += sn9c102_i2c_write(cam, 0x1a, 0xf6); - err += sn9c102_i2c_write(cam, 0x1b, 0x04); - err += sn9c102_i2c_write(cam, 0x20, 0x44); - err += sn9c102_i2c_write(cam, 0x23, 0xee); - err += sn9c102_i2c_write(cam, 0x26, 0xa0); - err += sn9c102_i2c_write(cam, 0x27, 0x9a); - err += sn9c102_i2c_write(cam, 0x28, 0x20); - err += sn9c102_i2c_write(cam, 0x29, 0x30); - err += sn9c102_i2c_write(cam, 0x2f, 0x3d); - err += sn9c102_i2c_write(cam, 0x30, 0x24); - err += sn9c102_i2c_write(cam, 0x32, 0x86); - err += sn9c102_i2c_write(cam, 0x60, 0xa9); - err += sn9c102_i2c_write(cam, 0x61, 0x42); - err += sn9c102_i2c_write(cam, 0x65, 0x00); - err += sn9c102_i2c_write(cam, 0x69, 0x38); - err += sn9c102_i2c_write(cam, 0x6f, 0x88); - err += sn9c102_i2c_write(cam, 0x70, 0x0b); - err += sn9c102_i2c_write(cam, 0x71, 0x00); - err += sn9c102_i2c_write(cam, 0x74, 0x21); - err += sn9c102_i2c_write(cam, 0x7d, 0xf7); - break; - case BRIDGE_SN9C103: - err = sn9c102_write_const_regs(cam, {0x00, 0x02}, {0x00, 0x03}, - {0x1a, 0x04}, {0x20, 0x05}, - {0x20, 0x06}, {0x20, 0x07}, - {0x03, 0x10}, {0x0a, 0x14}, - {0x60, 0x17}, {0x0f, 0x18}, - {0x50, 0x19}, {0x1d, 0x1a}, - {0x10, 0x1b}, {0x02, 0x1c}, - {0x03, 0x1d}, {0x0f, 0x1e}, - {0x0c, 0x1f}, {0x00, 0x20}, - {0x10, 0x21}, {0x20, 0x22}, - {0x30, 0x23}, {0x40, 0x24}, - {0x50, 0x25}, {0x60, 0x26}, - {0x70, 0x27}, {0x80, 0x28}, - {0x90, 0x29}, {0xa0, 0x2a}, - {0xb0, 0x2b}, {0xc0, 0x2c}, - {0xd0, 0x2d}, {0xe0, 0x2e}, - {0xf0, 0x2f}, {0xff, 0x30}); - - err += sn9c102_i2c_write(cam, 0x12, 0x8d); - err += sn9c102_i2c_write(cam, 0x12, 0x0d); - err += sn9c102_i2c_write(cam, 0x15, 0x34); - err += sn9c102_i2c_write(cam, 0x11, 0x01); - err += sn9c102_i2c_write(cam, 0x1b, 0x04); - err += sn9c102_i2c_write(cam, 0x20, 0x44); - err += sn9c102_i2c_write(cam, 0x23, 0xee); - err += sn9c102_i2c_write(cam, 0x26, 0xa0); - err += sn9c102_i2c_write(cam, 0x27, 0x9a); - err += sn9c102_i2c_write(cam, 0x28, 0x20); - err += sn9c102_i2c_write(cam, 0x29, 0x30); - err += sn9c102_i2c_write(cam, 0x2f, 0x3d); - err += sn9c102_i2c_write(cam, 0x30, 0x24); - err += sn9c102_i2c_write(cam, 0x32, 0x86); - err += sn9c102_i2c_write(cam, 0x60, 0xa9); - err += sn9c102_i2c_write(cam, 0x61, 0x42); - err += sn9c102_i2c_write(cam, 0x65, 0x00); - err += sn9c102_i2c_write(cam, 0x69, 0x38); - err += sn9c102_i2c_write(cam, 0x6f, 0x88); - err += sn9c102_i2c_write(cam, 0x70, 0x0b); - err += sn9c102_i2c_write(cam, 0x71, 0x00); - err += sn9c102_i2c_write(cam, 0x74, 0x21); - err += sn9c102_i2c_write(cam, 0x7d, 0xf7); - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - err = sn9c102_write_const_regs(cam, {0x40, 0x02}, {0x00, 0x03}, - {0x1a, 0x04}, {0x03, 0x10}, - {0x0a, 0x14}, {0xe2, 0x17}, - {0x0b, 0x18}, {0x00, 0x19}, - {0x1d, 0x1a}, {0x10, 0x1b}, - {0x02, 0x1c}, {0x03, 0x1d}, - {0x0f, 0x1e}, {0x0c, 0x1f}, - {0x00, 0x20}, {0x24, 0x21}, - {0x3b, 0x22}, {0x47, 0x23}, - {0x60, 0x24}, {0x71, 0x25}, - {0x80, 0x26}, {0x8f, 0x27}, - {0x9d, 0x28}, {0xaa, 0x29}, - {0xb8, 0x2a}, {0xc4, 0x2b}, - {0xd1, 0x2c}, {0xdd, 0x2d}, - {0xe8, 0x2e}, {0xf4, 0x2f}, - {0xff, 0x30}, {0x00, 0x3f}, - {0xc7, 0x40}, {0x01, 0x41}, - {0x44, 0x42}, {0x00, 0x43}, - {0x44, 0x44}, {0x00, 0x45}, - {0x44, 0x46}, {0x00, 0x47}, - {0xc7, 0x48}, {0x01, 0x49}, - {0xc7, 0x4a}, {0x01, 0x4b}, - {0xc7, 0x4c}, {0x01, 0x4d}, - {0x44, 0x4e}, {0x00, 0x4f}, - {0x44, 0x50}, {0x00, 0x51}, - {0x44, 0x52}, {0x00, 0x53}, - {0xc7, 0x54}, {0x01, 0x55}, - {0xc7, 0x56}, {0x01, 0x57}, - {0xc7, 0x58}, {0x01, 0x59}, - {0x44, 0x5a}, {0x00, 0x5b}, - {0x44, 0x5c}, {0x00, 0x5d}, - {0x44, 0x5e}, {0x00, 0x5f}, - {0xc7, 0x60}, {0x01, 0x61}, - {0xc7, 0x62}, {0x01, 0x63}, - {0xc7, 0x64}, {0x01, 0x65}, - {0x44, 0x66}, {0x00, 0x67}, - {0x44, 0x68}, {0x00, 0x69}, - {0x44, 0x6a}, {0x00, 0x6b}, - {0xc7, 0x6c}, {0x01, 0x6d}, - {0xc7, 0x6e}, {0x01, 0x6f}, - {0xc7, 0x70}, {0x01, 0x71}, - {0x44, 0x72}, {0x00, 0x73}, - {0x44, 0x74}, {0x00, 0x75}, - {0x44, 0x76}, {0x00, 0x77}, - {0xc7, 0x78}, {0x01, 0x79}, - {0xc7, 0x7a}, {0x01, 0x7b}, - {0xc7, 0x7c}, {0x01, 0x7d}, - {0x44, 0x7e}, {0x00, 0x7f}, - {0x17, 0x84}, {0x00, 0x85}, - {0x2e, 0x86}, {0x00, 0x87}, - {0x09, 0x88}, {0x00, 0x89}, - {0xe8, 0x8a}, {0x0f, 0x8b}, - {0xda, 0x8c}, {0x0f, 0x8d}, - {0x40, 0x8e}, {0x00, 0x8f}, - {0x37, 0x90}, {0x00, 0x91}, - {0xcf, 0x92}, {0x0f, 0x93}, - {0xfa, 0x94}, {0x0f, 0x95}, - {0x00, 0x96}, {0x00, 0x97}, - {0x00, 0x98}, {0x66, 0x99}, - {0x00, 0x9a}, {0x40, 0x9b}, - {0x20, 0x9c}, {0x00, 0x9d}, - {0x00, 0x9e}, {0x00, 0x9f}, - {0x2d, 0xc0}, {0x2d, 0xc1}, - {0x3a, 0xc2}, {0x00, 0xc3}, - {0x04, 0xc4}, {0x3f, 0xc5}, - {0x00, 0xc6}, {0x00, 0xc7}, - {0x50, 0xc8}, {0x3c, 0xc9}, - {0x28, 0xca}, {0xd8, 0xcb}, - {0x14, 0xcc}, {0xec, 0xcd}, - {0x32, 0xce}, {0xdd, 0xcf}, - {0x32, 0xd0}, {0xdd, 0xd1}, - {0x6a, 0xd2}, {0x50, 0xd3}, - {0x60, 0xd4}, {0x00, 0xd5}, - {0x00, 0xd6}); - - err += sn9c102_i2c_write(cam, 0x12, 0x80); - err += sn9c102_i2c_write(cam, 0x12, 0x48); - err += sn9c102_i2c_write(cam, 0x01, 0x80); - err += sn9c102_i2c_write(cam, 0x02, 0x80); - err += sn9c102_i2c_write(cam, 0x03, 0x80); - err += sn9c102_i2c_write(cam, 0x04, 0x10); - err += sn9c102_i2c_write(cam, 0x05, 0x20); - err += sn9c102_i2c_write(cam, 0x06, 0x80); - err += sn9c102_i2c_write(cam, 0x11, 0x00); - err += sn9c102_i2c_write(cam, 0x0c, 0x20); - err += sn9c102_i2c_write(cam, 0x0d, 0x20); - err += sn9c102_i2c_write(cam, 0x15, 0x80); - err += sn9c102_i2c_write(cam, 0x16, 0x03); - err += sn9c102_i2c_write(cam, 0x17, 0x1b); - err += sn9c102_i2c_write(cam, 0x18, 0xbd); - err += sn9c102_i2c_write(cam, 0x19, 0x05); - err += sn9c102_i2c_write(cam, 0x1a, 0xf6); - err += sn9c102_i2c_write(cam, 0x1b, 0x04); - err += sn9c102_i2c_write(cam, 0x21, 0x1b); - err += sn9c102_i2c_write(cam, 0x22, 0x00); - err += sn9c102_i2c_write(cam, 0x23, 0xde); - err += sn9c102_i2c_write(cam, 0x24, 0x10); - err += sn9c102_i2c_write(cam, 0x25, 0x8a); - err += sn9c102_i2c_write(cam, 0x26, 0xa0); - err += sn9c102_i2c_write(cam, 0x27, 0xca); - err += sn9c102_i2c_write(cam, 0x28, 0xa2); - err += sn9c102_i2c_write(cam, 0x29, 0x74); - err += sn9c102_i2c_write(cam, 0x2a, 0x88); - err += sn9c102_i2c_write(cam, 0x2b, 0x34); - err += sn9c102_i2c_write(cam, 0x2c, 0x88); - err += sn9c102_i2c_write(cam, 0x2e, 0x00); - err += sn9c102_i2c_write(cam, 0x2f, 0x00); - err += sn9c102_i2c_write(cam, 0x30, 0x00); - err += sn9c102_i2c_write(cam, 0x32, 0xc2); - err += sn9c102_i2c_write(cam, 0x33, 0x08); - err += sn9c102_i2c_write(cam, 0x4c, 0x40); - err += sn9c102_i2c_write(cam, 0x4d, 0xf3); - err += sn9c102_i2c_write(cam, 0x60, 0x05); - err += sn9c102_i2c_write(cam, 0x61, 0x40); - err += sn9c102_i2c_write(cam, 0x62, 0x12); - err += sn9c102_i2c_write(cam, 0x63, 0x57); - err += sn9c102_i2c_write(cam, 0x64, 0x73); - err += sn9c102_i2c_write(cam, 0x65, 0x00); - err += sn9c102_i2c_write(cam, 0x66, 0x55); - err += sn9c102_i2c_write(cam, 0x67, 0x01); - err += sn9c102_i2c_write(cam, 0x68, 0xac); - err += sn9c102_i2c_write(cam, 0x69, 0x38); - err += sn9c102_i2c_write(cam, 0x6f, 0x1f); - err += sn9c102_i2c_write(cam, 0x70, 0x01); - err += sn9c102_i2c_write(cam, 0x71, 0x00); - err += sn9c102_i2c_write(cam, 0x72, 0x10); - err += sn9c102_i2c_write(cam, 0x73, 0x50); - err += sn9c102_i2c_write(cam, 0x74, 0x20); - err += sn9c102_i2c_write(cam, 0x76, 0x01); - err += sn9c102_i2c_write(cam, 0x77, 0xf3); - err += sn9c102_i2c_write(cam, 0x78, 0x90); - err += sn9c102_i2c_write(cam, 0x79, 0x98); - err += sn9c102_i2c_write(cam, 0x7a, 0x98); - err += sn9c102_i2c_write(cam, 0x7b, 0x00); - err += sn9c102_i2c_write(cam, 0x7c, 0x38); - err += sn9c102_i2c_write(cam, 0x7d, 0xff); - break; - default: - break; - } - - return err; -} - - -static int ov7630_get_ctrl(struct sn9c102_device* cam, - struct v4l2_control* ctrl) -{ - enum sn9c102_bridge bridge = sn9c102_get_bridge(cam); - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x10)) < 0) - return -EIO; - break; - case V4L2_CID_RED_BALANCE: - if (bridge == BRIDGE_SN9C105 || bridge == BRIDGE_SN9C120) - ctrl->value = sn9c102_pread_reg(cam, 0x05); - else - ctrl->value = sn9c102_pread_reg(cam, 0x07); - break; - case V4L2_CID_BLUE_BALANCE: - ctrl->value = sn9c102_pread_reg(cam, 0x06); - break; - case SN9C102_V4L2_CID_GREEN_BALANCE: - if (bridge == BRIDGE_SN9C105 || bridge == BRIDGE_SN9C120) - ctrl->value = sn9c102_pread_reg(cam, 0x07); - else - ctrl->value = sn9c102_pread_reg(cam, 0x05); - break; - break; - case V4L2_CID_GAIN: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x00)) < 0) - return -EIO; - ctrl->value &= 0x3f; - break; - case V4L2_CID_DO_WHITE_BALANCE: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x0c)) < 0) - return -EIO; - ctrl->value &= 0x3f; - break; - case V4L2_CID_WHITENESS: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x0d)) < 0) - return -EIO; - ctrl->value &= 0x3f; - break; - case V4L2_CID_AUTOGAIN: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x13)) < 0) - return -EIO; - ctrl->value &= 0x01; - break; - case V4L2_CID_VFLIP: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x75)) < 0) - return -EIO; - ctrl->value = (ctrl->value & 0x80) ? 1 : 0; - break; - case SN9C102_V4L2_CID_GAMMA: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x14)) < 0) - return -EIO; - ctrl->value = (ctrl->value & 0x02) ? 1 : 0; - break; - case SN9C102_V4L2_CID_BAND_FILTER: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x2d)) < 0) - return -EIO; - ctrl->value = (ctrl->value & 0x02) ? 1 : 0; - break; - default: - return -EINVAL; - } - - return err ? -EIO : 0; -} - - -static int ov7630_set_ctrl(struct sn9c102_device* cam, - const struct v4l2_control* ctrl) -{ - enum sn9c102_bridge bridge = sn9c102_get_bridge(cam); - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - err += sn9c102_i2c_write(cam, 0x10, ctrl->value); - break; - case V4L2_CID_RED_BALANCE: - if (bridge == BRIDGE_SN9C105 || bridge == BRIDGE_SN9C120) - err += sn9c102_write_reg(cam, ctrl->value, 0x05); - else - err += sn9c102_write_reg(cam, ctrl->value, 0x07); - break; - case V4L2_CID_BLUE_BALANCE: - err += sn9c102_write_reg(cam, ctrl->value, 0x06); - break; - case SN9C102_V4L2_CID_GREEN_BALANCE: - if (bridge == BRIDGE_SN9C105 || bridge == BRIDGE_SN9C120) - err += sn9c102_write_reg(cam, ctrl->value, 0x07); - else - err += sn9c102_write_reg(cam, ctrl->value, 0x05); - break; - case V4L2_CID_GAIN: - err += sn9c102_i2c_write(cam, 0x00, ctrl->value); - break; - case V4L2_CID_DO_WHITE_BALANCE: - err += sn9c102_i2c_write(cam, 0x0c, ctrl->value); - break; - case V4L2_CID_WHITENESS: - err += sn9c102_i2c_write(cam, 0x0d, ctrl->value); - break; - case V4L2_CID_AUTOGAIN: - err += sn9c102_i2c_write(cam, 0x13, ctrl->value | - (ctrl->value << 1)); - break; - case V4L2_CID_VFLIP: - err += sn9c102_i2c_write(cam, 0x75, 0x0e | (ctrl->value << 7)); - break; - case SN9C102_V4L2_CID_GAMMA: - err += sn9c102_i2c_write(cam, 0x14, ctrl->value << 2); - break; - case SN9C102_V4L2_CID_BAND_FILTER: - err += sn9c102_i2c_write(cam, 0x2d, ctrl->value << 2); - break; - default: - return -EINVAL; - } - - return err ? -EIO : 0; -} - - -static int ov7630_set_crop(struct sn9c102_device* cam, - const struct v4l2_rect* rect) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - int err = 0; - u8 h_start = 0, v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1; - - switch (sn9c102_get_bridge(cam)) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - case BRIDGE_SN9C103: - h_start = (u8)(rect->left - s->cropcap.bounds.left) + 1; - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - h_start = (u8)(rect->left - s->cropcap.bounds.left) + 4; - break; - default: - break; - } - - err += sn9c102_write_reg(cam, h_start, 0x12); - err += sn9c102_write_reg(cam, v_start, 0x13); - - return err; -} - - -static int ov7630_set_pix_format(struct sn9c102_device* cam, - const struct v4l2_pix_format* pix) -{ - int err = 0; - - switch (sn9c102_get_bridge(cam)) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - case BRIDGE_SN9C103: - if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) - err += sn9c102_write_reg(cam, 0x50, 0x19); - else - err += sn9c102_write_reg(cam, 0x20, 0x19); - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) { - err += sn9c102_write_reg(cam, 0xe5, 0x17); - err += sn9c102_i2c_write(cam, 0x11, 0x04); - } else { - err += sn9c102_write_reg(cam, 0xe2, 0x17); - err += sn9c102_i2c_write(cam, 0x11, 0x02); - } - break; - default: - break; - } - - return err; -} - - -static const struct sn9c102_sensor ov7630 = { - .name = "OV7630", - .maintainer = "Luca Risolia ", - .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102 | BRIDGE_SN9C103 | - BRIDGE_SN9C105 | BRIDGE_SN9C120, - .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, - .frequency = SN9C102_I2C_100KHZ, - .interface = SN9C102_I2C_2WIRES, - .i2c_slave_id = 0x21, - .init = &ov7630_init, - .qctrl = { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "global gain", - .minimum = 0x00, - .maximum = 0x3f, - .step = 0x01, - .default_value = 0x14, - .flags = 0, - }, - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0x00, - .maximum = 0xff, - .step = 0x01, - .default_value = 0x60, - .flags = 0, - }, - { - .id = V4L2_CID_WHITENESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "white balance background: red", - .minimum = 0x00, - .maximum = 0x3f, - .step = 0x01, - .default_value = 0x20, - .flags = 0, - }, - { - .id = V4L2_CID_DO_WHITE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "white balance background: blue", - .minimum = 0x00, - .maximum = 0x3f, - .step = 0x01, - .default_value = 0x20, - .flags = 0, - }, - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "red balance", - .minimum = 0x00, - .maximum = 0x7f, - .step = 0x01, - .default_value = 0x20, - .flags = 0, - }, - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "blue balance", - .minimum = 0x00, - .maximum = 0x7f, - .step = 0x01, - .default_value = 0x20, - .flags = 0, - }, - { - .id = V4L2_CID_AUTOGAIN, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "auto adjust", - .minimum = 0x00, - .maximum = 0x01, - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "vertical flip", - .minimum = 0x00, - .maximum = 0x01, - .step = 0x01, - .default_value = 0x01, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_GREEN_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "green balance", - .minimum = 0x00, - .maximum = 0x7f, - .step = 0x01, - .default_value = 0x20, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_BAND_FILTER, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "band filter", - .minimum = 0x00, - .maximum = 0x01, - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_GAMMA, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "rgb gamma", - .minimum = 0x00, - .maximum = 0x01, - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - }, - .get_ctrl = &ov7630_get_ctrl, - .set_ctrl = &ov7630_set_ctrl, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - }, - .set_crop = &ov7630_set_crop, - .pix_format = { - .width = 640, - .height = 480, - .pixelformat = V4L2_PIX_FMT_SN9C10X, - .priv = 8, - }, - .set_pix_format = &ov7630_set_pix_format -}; - - -int sn9c102_probe_ov7630(struct sn9c102_device* cam) -{ - int pid, ver, err = 0; - - switch (sn9c102_get_bridge(cam)) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x00, 0x01}, - {0x28, 0x17}); - break; - case BRIDGE_SN9C103: /* do _not_ change anything! */ - err = sn9c102_write_const_regs(cam, {0x09, 0x01}, {0x42, 0x01}, - {0x28, 0x17}, {0x44, 0x02}); - pid = sn9c102_i2c_try_read(cam, &ov7630, 0x0a); - if (err || pid < 0) /* try a different initialization */ - err += sn9c102_write_const_regs(cam, {0x01, 0x01}, - {0x00, 0x01}); - break; - case BRIDGE_SN9C105: - case BRIDGE_SN9C120: - err = sn9c102_write_const_regs(cam, {0x01, 0xf1}, {0x00, 0xf1}, - {0x29, 0x01}, {0x74, 0x02}, - {0x0e, 0x01}, {0x44, 0x01}); - break; - default: - break; - } - - pid = sn9c102_i2c_try_read(cam, &ov7630, 0x0a); - ver = sn9c102_i2c_try_read(cam, &ov7630, 0x0b); - if (err || pid < 0 || ver < 0) - return -EIO; - if (pid != 0x76 || ver != 0x31) - return -ENODEV; - sn9c102_attach_sensor(cam, &ov7630); - - return 0; -} diff --git a/drivers/media/usb/sn9c102/sn9c102_ov7660.c b/drivers/media/usb/sn9c102/sn9c102_ov7660.c deleted file mode 100644 index 7977795d342b..000000000000 --- a/drivers/media/usb/sn9c102/sn9c102_ov7660.c +++ /dev/null @@ -1,538 +0,0 @@ -/*************************************************************************** - * Plug-in for OV7660 image sensor connected to the SN9C1xx PC Camera * - * Controllers * - * * - * Copyright (C) 2007 by Luca Risolia * - * * - * 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. * - ***************************************************************************/ - -#include "sn9c102_sensor.h" -#include "sn9c102_devtable.h" - - -static int ov7660_init(struct sn9c102_device* cam) -{ - int err = 0; - - err = sn9c102_write_const_regs(cam, {0x40, 0x02}, {0x00, 0x03}, - {0x1a, 0x04}, {0x03, 0x10}, - {0x08, 0x14}, {0x20, 0x17}, - {0x8b, 0x18}, {0x00, 0x19}, - {0x1d, 0x1a}, {0x10, 0x1b}, - {0x02, 0x1c}, {0x03, 0x1d}, - {0x0f, 0x1e}, {0x0c, 0x1f}, - {0x00, 0x20}, {0x29, 0x21}, - {0x40, 0x22}, {0x54, 0x23}, - {0x66, 0x24}, {0x76, 0x25}, - {0x85, 0x26}, {0x94, 0x27}, - {0xa1, 0x28}, {0xae, 0x29}, - {0xbb, 0x2a}, {0xc7, 0x2b}, - {0xd3, 0x2c}, {0xde, 0x2d}, - {0xea, 0x2e}, {0xf4, 0x2f}, - {0xff, 0x30}, {0x00, 0x3f}, - {0xc7, 0x40}, {0x01, 0x41}, - {0x44, 0x42}, {0x00, 0x43}, - {0x44, 0x44}, {0x00, 0x45}, - {0x44, 0x46}, {0x00, 0x47}, - {0xc7, 0x48}, {0x01, 0x49}, - {0xc7, 0x4a}, {0x01, 0x4b}, - {0xc7, 0x4c}, {0x01, 0x4d}, - {0x44, 0x4e}, {0x00, 0x4f}, - {0x44, 0x50}, {0x00, 0x51}, - {0x44, 0x52}, {0x00, 0x53}, - {0xc7, 0x54}, {0x01, 0x55}, - {0xc7, 0x56}, {0x01, 0x57}, - {0xc7, 0x58}, {0x01, 0x59}, - {0x44, 0x5a}, {0x00, 0x5b}, - {0x44, 0x5c}, {0x00, 0x5d}, - {0x44, 0x5e}, {0x00, 0x5f}, - {0xc7, 0x60}, {0x01, 0x61}, - {0xc7, 0x62}, {0x01, 0x63}, - {0xc7, 0x64}, {0x01, 0x65}, - {0x44, 0x66}, {0x00, 0x67}, - {0x44, 0x68}, {0x00, 0x69}, - {0x44, 0x6a}, {0x00, 0x6b}, - {0xc7, 0x6c}, {0x01, 0x6d}, - {0xc7, 0x6e}, {0x01, 0x6f}, - {0xc7, 0x70}, {0x01, 0x71}, - {0x44, 0x72}, {0x00, 0x73}, - {0x44, 0x74}, {0x00, 0x75}, - {0x44, 0x76}, {0x00, 0x77}, - {0xc7, 0x78}, {0x01, 0x79}, - {0xc7, 0x7a}, {0x01, 0x7b}, - {0xc7, 0x7c}, {0x01, 0x7d}, - {0x44, 0x7e}, {0x00, 0x7f}, - {0x14, 0x84}, {0x00, 0x85}, - {0x27, 0x86}, {0x00, 0x87}, - {0x07, 0x88}, {0x00, 0x89}, - {0xec, 0x8a}, {0x0f, 0x8b}, - {0xd8, 0x8c}, {0x0f, 0x8d}, - {0x3d, 0x8e}, {0x00, 0x8f}, - {0x3d, 0x90}, {0x00, 0x91}, - {0xcd, 0x92}, {0x0f, 0x93}, - {0xf7, 0x94}, {0x0f, 0x95}, - {0x0c, 0x96}, {0x00, 0x97}, - {0x00, 0x98}, {0x66, 0x99}, - {0x05, 0x9a}, {0x00, 0x9b}, - {0x04, 0x9c}, {0x00, 0x9d}, - {0x08, 0x9e}, {0x00, 0x9f}, - {0x2d, 0xc0}, {0x2d, 0xc1}, - {0x3a, 0xc2}, {0x05, 0xc3}, - {0x04, 0xc4}, {0x3f, 0xc5}, - {0x00, 0xc6}, {0x00, 0xc7}, - {0x50, 0xc8}, {0x3C, 0xc9}, - {0x28, 0xca}, {0xd8, 0xcb}, - {0x14, 0xcc}, {0xec, 0xcd}, - {0x32, 0xce}, {0xdd, 0xcf}, - {0x32, 0xd0}, {0xdd, 0xd1}, - {0x6a, 0xd2}, {0x50, 0xd3}, - {0x00, 0xd4}, {0x00, 0xd5}, - {0x00, 0xd6}); - - err += sn9c102_i2c_write(cam, 0x12, 0x80); - err += sn9c102_i2c_write(cam, 0x11, 0x09); - err += sn9c102_i2c_write(cam, 0x00, 0x0A); - err += sn9c102_i2c_write(cam, 0x01, 0x80); - err += sn9c102_i2c_write(cam, 0x02, 0x80); - err += sn9c102_i2c_write(cam, 0x03, 0x00); - err += sn9c102_i2c_write(cam, 0x04, 0x00); - err += sn9c102_i2c_write(cam, 0x05, 0x08); - err += sn9c102_i2c_write(cam, 0x06, 0x0B); - err += sn9c102_i2c_write(cam, 0x07, 0x00); - err += sn9c102_i2c_write(cam, 0x08, 0x1C); - err += sn9c102_i2c_write(cam, 0x09, 0x01); - err += sn9c102_i2c_write(cam, 0x0A, 0x76); - err += sn9c102_i2c_write(cam, 0x0B, 0x60); - err += sn9c102_i2c_write(cam, 0x0C, 0x00); - err += sn9c102_i2c_write(cam, 0x0D, 0x08); - err += sn9c102_i2c_write(cam, 0x0E, 0x04); - err += sn9c102_i2c_write(cam, 0x0F, 0x6F); - err += sn9c102_i2c_write(cam, 0x10, 0x20); - err += sn9c102_i2c_write(cam, 0x11, 0x03); - err += sn9c102_i2c_write(cam, 0x12, 0x05); - err += sn9c102_i2c_write(cam, 0x13, 0xC7); - err += sn9c102_i2c_write(cam, 0x14, 0x2C); - err += sn9c102_i2c_write(cam, 0x15, 0x00); - err += sn9c102_i2c_write(cam, 0x16, 0x02); - err += sn9c102_i2c_write(cam, 0x17, 0x10); - err += sn9c102_i2c_write(cam, 0x18, 0x60); - err += sn9c102_i2c_write(cam, 0x19, 0x02); - err += sn9c102_i2c_write(cam, 0x1A, 0x7B); - err += sn9c102_i2c_write(cam, 0x1B, 0x02); - err += sn9c102_i2c_write(cam, 0x1C, 0x7F); - err += sn9c102_i2c_write(cam, 0x1D, 0xA2); - err += sn9c102_i2c_write(cam, 0x1E, 0x01); - err += sn9c102_i2c_write(cam, 0x1F, 0x0E); - err += sn9c102_i2c_write(cam, 0x20, 0x05); - err += sn9c102_i2c_write(cam, 0x21, 0x05); - err += sn9c102_i2c_write(cam, 0x22, 0x05); - err += sn9c102_i2c_write(cam, 0x23, 0x05); - err += sn9c102_i2c_write(cam, 0x24, 0x68); - err += sn9c102_i2c_write(cam, 0x25, 0x58); - err += sn9c102_i2c_write(cam, 0x26, 0xD4); - err += sn9c102_i2c_write(cam, 0x27, 0x80); - err += sn9c102_i2c_write(cam, 0x28, 0x80); - err += sn9c102_i2c_write(cam, 0x29, 0x30); - err += sn9c102_i2c_write(cam, 0x2A, 0x00); - err += sn9c102_i2c_write(cam, 0x2B, 0x00); - err += sn9c102_i2c_write(cam, 0x2C, 0x80); - err += sn9c102_i2c_write(cam, 0x2D, 0x00); - err += sn9c102_i2c_write(cam, 0x2E, 0x00); - err += sn9c102_i2c_write(cam, 0x2F, 0x0E); - err += sn9c102_i2c_write(cam, 0x30, 0x08); - err += sn9c102_i2c_write(cam, 0x31, 0x30); - err += sn9c102_i2c_write(cam, 0x32, 0xB4); - err += sn9c102_i2c_write(cam, 0x33, 0x00); - err += sn9c102_i2c_write(cam, 0x34, 0x07); - err += sn9c102_i2c_write(cam, 0x35, 0x84); - err += sn9c102_i2c_write(cam, 0x36, 0x00); - err += sn9c102_i2c_write(cam, 0x37, 0x0C); - err += sn9c102_i2c_write(cam, 0x38, 0x02); - err += sn9c102_i2c_write(cam, 0x39, 0x43); - err += sn9c102_i2c_write(cam, 0x3A, 0x00); - err += sn9c102_i2c_write(cam, 0x3B, 0x0A); - err += sn9c102_i2c_write(cam, 0x3C, 0x6C); - err += sn9c102_i2c_write(cam, 0x3D, 0x99); - err += sn9c102_i2c_write(cam, 0x3E, 0x0E); - err += sn9c102_i2c_write(cam, 0x3F, 0x41); - err += sn9c102_i2c_write(cam, 0x40, 0xC1); - err += sn9c102_i2c_write(cam, 0x41, 0x22); - err += sn9c102_i2c_write(cam, 0x42, 0x08); - err += sn9c102_i2c_write(cam, 0x43, 0xF0); - err += sn9c102_i2c_write(cam, 0x44, 0x10); - err += sn9c102_i2c_write(cam, 0x45, 0x78); - err += sn9c102_i2c_write(cam, 0x46, 0xA8); - err += sn9c102_i2c_write(cam, 0x47, 0x60); - err += sn9c102_i2c_write(cam, 0x48, 0x80); - err += sn9c102_i2c_write(cam, 0x49, 0x00); - err += sn9c102_i2c_write(cam, 0x4A, 0x00); - err += sn9c102_i2c_write(cam, 0x4B, 0x00); - err += sn9c102_i2c_write(cam, 0x4C, 0x00); - err += sn9c102_i2c_write(cam, 0x4D, 0x00); - err += sn9c102_i2c_write(cam, 0x4E, 0x00); - err += sn9c102_i2c_write(cam, 0x4F, 0x46); - err += sn9c102_i2c_write(cam, 0x50, 0x36); - err += sn9c102_i2c_write(cam, 0x51, 0x0F); - err += sn9c102_i2c_write(cam, 0x52, 0x17); - err += sn9c102_i2c_write(cam, 0x53, 0x7F); - err += sn9c102_i2c_write(cam, 0x54, 0x96); - err += sn9c102_i2c_write(cam, 0x55, 0x40); - err += sn9c102_i2c_write(cam, 0x56, 0x40); - err += sn9c102_i2c_write(cam, 0x57, 0x40); - err += sn9c102_i2c_write(cam, 0x58, 0x0F); - err += sn9c102_i2c_write(cam, 0x59, 0xBA); - err += sn9c102_i2c_write(cam, 0x5A, 0x9A); - err += sn9c102_i2c_write(cam, 0x5B, 0x22); - err += sn9c102_i2c_write(cam, 0x5C, 0xB9); - err += sn9c102_i2c_write(cam, 0x5D, 0x9B); - err += sn9c102_i2c_write(cam, 0x5E, 0x10); - err += sn9c102_i2c_write(cam, 0x5F, 0xF0); - err += sn9c102_i2c_write(cam, 0x60, 0x05); - err += sn9c102_i2c_write(cam, 0x61, 0x60); - err += sn9c102_i2c_write(cam, 0x62, 0x00); - err += sn9c102_i2c_write(cam, 0x63, 0x00); - err += sn9c102_i2c_write(cam, 0x64, 0x50); - err += sn9c102_i2c_write(cam, 0x65, 0x30); - err += sn9c102_i2c_write(cam, 0x66, 0x00); - err += sn9c102_i2c_write(cam, 0x67, 0x80); - err += sn9c102_i2c_write(cam, 0x68, 0x7A); - err += sn9c102_i2c_write(cam, 0x69, 0x90); - err += sn9c102_i2c_write(cam, 0x6A, 0x80); - err += sn9c102_i2c_write(cam, 0x6B, 0x0A); - err += sn9c102_i2c_write(cam, 0x6C, 0x30); - err += sn9c102_i2c_write(cam, 0x6D, 0x48); - err += sn9c102_i2c_write(cam, 0x6E, 0x80); - err += sn9c102_i2c_write(cam, 0x6F, 0x74); - err += sn9c102_i2c_write(cam, 0x70, 0x64); - err += sn9c102_i2c_write(cam, 0x71, 0x60); - err += sn9c102_i2c_write(cam, 0x72, 0x5C); - err += sn9c102_i2c_write(cam, 0x73, 0x58); - err += sn9c102_i2c_write(cam, 0x74, 0x54); - err += sn9c102_i2c_write(cam, 0x75, 0x4C); - err += sn9c102_i2c_write(cam, 0x76, 0x40); - err += sn9c102_i2c_write(cam, 0x77, 0x38); - err += sn9c102_i2c_write(cam, 0x78, 0x34); - err += sn9c102_i2c_write(cam, 0x79, 0x30); - err += sn9c102_i2c_write(cam, 0x7A, 0x2F); - err += sn9c102_i2c_write(cam, 0x7B, 0x2B); - err += sn9c102_i2c_write(cam, 0x7C, 0x03); - err += sn9c102_i2c_write(cam, 0x7D, 0x07); - err += sn9c102_i2c_write(cam, 0x7E, 0x17); - err += sn9c102_i2c_write(cam, 0x7F, 0x34); - err += sn9c102_i2c_write(cam, 0x80, 0x41); - err += sn9c102_i2c_write(cam, 0x81, 0x4D); - err += sn9c102_i2c_write(cam, 0x82, 0x58); - err += sn9c102_i2c_write(cam, 0x83, 0x63); - err += sn9c102_i2c_write(cam, 0x84, 0x6E); - err += sn9c102_i2c_write(cam, 0x85, 0x77); - err += sn9c102_i2c_write(cam, 0x86, 0x87); - err += sn9c102_i2c_write(cam, 0x87, 0x95); - err += sn9c102_i2c_write(cam, 0x88, 0xAF); - err += sn9c102_i2c_write(cam, 0x89, 0xC7); - err += sn9c102_i2c_write(cam, 0x8A, 0xDF); - err += sn9c102_i2c_write(cam, 0x8B, 0x99); - err += sn9c102_i2c_write(cam, 0x8C, 0x99); - err += sn9c102_i2c_write(cam, 0x8D, 0xCF); - err += sn9c102_i2c_write(cam, 0x8E, 0x20); - err += sn9c102_i2c_write(cam, 0x8F, 0x26); - err += sn9c102_i2c_write(cam, 0x90, 0x10); - err += sn9c102_i2c_write(cam, 0x91, 0x0C); - err += sn9c102_i2c_write(cam, 0x92, 0x25); - err += sn9c102_i2c_write(cam, 0x93, 0x00); - err += sn9c102_i2c_write(cam, 0x94, 0x50); - err += sn9c102_i2c_write(cam, 0x95, 0x50); - err += sn9c102_i2c_write(cam, 0x96, 0x00); - err += sn9c102_i2c_write(cam, 0x97, 0x01); - err += sn9c102_i2c_write(cam, 0x98, 0x10); - err += sn9c102_i2c_write(cam, 0x99, 0x40); - err += sn9c102_i2c_write(cam, 0x9A, 0x40); - err += sn9c102_i2c_write(cam, 0x9B, 0x20); - err += sn9c102_i2c_write(cam, 0x9C, 0x00); - err += sn9c102_i2c_write(cam, 0x9D, 0x99); - err += sn9c102_i2c_write(cam, 0x9E, 0x7F); - err += sn9c102_i2c_write(cam, 0x9F, 0x00); - err += sn9c102_i2c_write(cam, 0xA0, 0x00); - err += sn9c102_i2c_write(cam, 0xA1, 0x00); - - return err; -} - - -static int ov7660_get_ctrl(struct sn9c102_device* cam, - struct v4l2_control* ctrl) -{ - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x10)) < 0) - return -EIO; - break; - case V4L2_CID_DO_WHITE_BALANCE: - if ((ctrl->value = sn9c102_read_reg(cam, 0x02)) < 0) - return -EIO; - ctrl->value = (ctrl->value & 0x04) ? 1 : 0; - break; - case V4L2_CID_RED_BALANCE: - if ((ctrl->value = sn9c102_read_reg(cam, 0x05)) < 0) - return -EIO; - ctrl->value &= 0x7f; - break; - case V4L2_CID_BLUE_BALANCE: - if ((ctrl->value = sn9c102_read_reg(cam, 0x06)) < 0) - return -EIO; - ctrl->value &= 0x7f; - break; - case SN9C102_V4L2_CID_GREEN_BALANCE: - if ((ctrl->value = sn9c102_read_reg(cam, 0x07)) < 0) - return -EIO; - ctrl->value &= 0x7f; - break; - case SN9C102_V4L2_CID_BAND_FILTER: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x3b)) < 0) - return -EIO; - ctrl->value &= 0x08; - break; - case V4L2_CID_GAIN: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x00)) < 0) - return -EIO; - ctrl->value &= 0x1f; - break; - case V4L2_CID_AUTOGAIN: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x13)) < 0) - return -EIO; - ctrl->value &= 0x01; - break; - default: - return -EINVAL; - } - - return err ? -EIO : 0; -} - - -static int ov7660_set_ctrl(struct sn9c102_device* cam, - const struct v4l2_control* ctrl) -{ - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - err += sn9c102_i2c_write(cam, 0x10, ctrl->value); - break; - case V4L2_CID_DO_WHITE_BALANCE: - err += sn9c102_write_reg(cam, 0x43 | (ctrl->value << 2), 0x02); - break; - case V4L2_CID_RED_BALANCE: - err += sn9c102_write_reg(cam, ctrl->value, 0x05); - break; - case V4L2_CID_BLUE_BALANCE: - err += sn9c102_write_reg(cam, ctrl->value, 0x06); - break; - case SN9C102_V4L2_CID_GREEN_BALANCE: - err += sn9c102_write_reg(cam, ctrl->value, 0x07); - break; - case SN9C102_V4L2_CID_BAND_FILTER: - err += sn9c102_i2c_write(cam, ctrl->value << 3, 0x3b); - break; - case V4L2_CID_GAIN: - err += sn9c102_i2c_write(cam, 0x00, 0x60 + ctrl->value); - break; - case V4L2_CID_AUTOGAIN: - err += sn9c102_i2c_write(cam, 0x13, 0xc0 | - (ctrl->value * 0x07)); - break; - default: - return -EINVAL; - } - - return err ? -EIO : 0; -} - - -static int ov7660_set_crop(struct sn9c102_device* cam, - const struct v4l2_rect* rect) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - int err = 0; - u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 1, - v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1; - - err += sn9c102_write_reg(cam, h_start, 0x12); - err += sn9c102_write_reg(cam, v_start, 0x13); - - return err; -} - - -static int ov7660_set_pix_format(struct sn9c102_device* cam, - const struct v4l2_pix_format* pix) -{ - int r0, err = 0; - - r0 = sn9c102_pread_reg(cam, 0x01); - - if (pix->pixelformat == V4L2_PIX_FMT_JPEG) { - err += sn9c102_write_reg(cam, r0 | 0x40, 0x01); - err += sn9c102_write_reg(cam, 0xa2, 0x17); - err += sn9c102_i2c_write(cam, 0x11, 0x00); - } else { - err += sn9c102_write_reg(cam, r0 | 0x40, 0x01); - err += sn9c102_write_reg(cam, 0xa2, 0x17); - err += sn9c102_i2c_write(cam, 0x11, 0x0d); - } - - return err; -} - - -static const struct sn9c102_sensor ov7660 = { - .name = "OV7660", - .maintainer = "Luca Risolia ", - .supported_bridge = BRIDGE_SN9C105 | BRIDGE_SN9C120, - .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, - .frequency = SN9C102_I2C_100KHZ, - .interface = SN9C102_I2C_2WIRES, - .i2c_slave_id = 0x21, - .init = &ov7660_init, - .qctrl = { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "global gain", - .minimum = 0x00, - .maximum = 0x1f, - .step = 0x01, - .default_value = 0x09, - .flags = 0, - }, - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0x00, - .maximum = 0xff, - .step = 0x01, - .default_value = 0x27, - .flags = 0, - }, - { - .id = V4L2_CID_DO_WHITE_BALANCE, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "night mode", - .minimum = 0x00, - .maximum = 0x01, - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "red balance", - .minimum = 0x00, - .maximum = 0x7f, - .step = 0x01, - .default_value = 0x14, - .flags = 0, - }, - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "blue balance", - .minimum = 0x00, - .maximum = 0x7f, - .step = 0x01, - .default_value = 0x14, - .flags = 0, - }, - { - .id = V4L2_CID_AUTOGAIN, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "auto adjust", - .minimum = 0x00, - .maximum = 0x01, - .step = 0x01, - .default_value = 0x01, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_GREEN_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "green balance", - .minimum = 0x00, - .maximum = 0x7f, - .step = 0x01, - .default_value = 0x14, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_BAND_FILTER, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "band filter", - .minimum = 0x00, - .maximum = 0x01, - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - }, - .get_ctrl = &ov7660_get_ctrl, - .set_ctrl = &ov7660_set_ctrl, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - }, - .set_crop = &ov7660_set_crop, - .pix_format = { - .width = 640, - .height = 480, - .pixelformat = V4L2_PIX_FMT_JPEG, - .priv = 8, - }, - .set_pix_format = &ov7660_set_pix_format -}; - - -int sn9c102_probe_ov7660(struct sn9c102_device* cam) -{ - int pid, ver, err; - - err = sn9c102_write_const_regs(cam, {0x01, 0xf1}, {0x00, 0xf1}, - {0x01, 0x01}, {0x00, 0x01}, - {0x28, 0x17}); - - pid = sn9c102_i2c_try_read(cam, &ov7660, 0x0a); - ver = sn9c102_i2c_try_read(cam, &ov7660, 0x0b); - if (err || pid < 0 || ver < 0) - return -EIO; - if (pid != 0x76 || ver != 0x60) - return -ENODEV; - - sn9c102_attach_sensor(cam, &ov7660); - - return 0; -} diff --git a/drivers/media/usb/sn9c102/sn9c102_pas106b.c b/drivers/media/usb/sn9c102/sn9c102_pas106b.c deleted file mode 100644 index 81cd969c1b7b..000000000000 --- a/drivers/media/usb/sn9c102/sn9c102_pas106b.c +++ /dev/null @@ -1,302 +0,0 @@ -/*************************************************************************** - * Plug-in for PAS106B image sensor connected to the SN9C1xx PC Camera * - * Controllers * - * * - * Copyright (C) 2004-2007 by Luca Risolia * - * * - * 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. * - ***************************************************************************/ - -#include -#include "sn9c102_sensor.h" -#include "sn9c102_devtable.h" - - -static int pas106b_init(struct sn9c102_device* cam) -{ - int err = 0; - - err = sn9c102_write_const_regs(cam, {0x00, 0x10}, {0x00, 0x11}, - {0x00, 0x14}, {0x20, 0x17}, - {0x20, 0x19}, {0x09, 0x18}); - - err += sn9c102_i2c_write(cam, 0x02, 0x0c); - err += sn9c102_i2c_write(cam, 0x05, 0x5a); - err += sn9c102_i2c_write(cam, 0x06, 0x88); - err += sn9c102_i2c_write(cam, 0x07, 0x80); - err += sn9c102_i2c_write(cam, 0x10, 0x06); - err += sn9c102_i2c_write(cam, 0x11, 0x06); - err += sn9c102_i2c_write(cam, 0x12, 0x00); - err += sn9c102_i2c_write(cam, 0x14, 0x02); - err += sn9c102_i2c_write(cam, 0x13, 0x01); - - msleep(400); - - return err; -} - - -static int pas106b_get_ctrl(struct sn9c102_device* cam, - struct v4l2_control* ctrl) -{ - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - { - int r1 = sn9c102_i2c_read(cam, 0x03), - r2 = sn9c102_i2c_read(cam, 0x04); - if (r1 < 0 || r2 < 0) - return -EIO; - ctrl->value = (r1 << 4) | (r2 & 0x0f); - } - return 0; - case V4L2_CID_RED_BALANCE: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x0c)) < 0) - return -EIO; - ctrl->value &= 0x1f; - return 0; - case V4L2_CID_BLUE_BALANCE: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x09)) < 0) - return -EIO; - ctrl->value &= 0x1f; - return 0; - case V4L2_CID_GAIN: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x0e)) < 0) - return -EIO; - ctrl->value &= 0x1f; - return 0; - case V4L2_CID_CONTRAST: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x0f)) < 0) - return -EIO; - ctrl->value &= 0x07; - return 0; - case SN9C102_V4L2_CID_GREEN_BALANCE: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x0a)) < 0) - return -EIO; - ctrl->value = (ctrl->value & 0x1f) << 1; - return 0; - case SN9C102_V4L2_CID_DAC_MAGNITUDE: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x08)) < 0) - return -EIO; - ctrl->value &= 0xf8; - return 0; - default: - return -EINVAL; - } -} - - -static int pas106b_set_ctrl(struct sn9c102_device* cam, - const struct v4l2_control* ctrl) -{ - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - err += sn9c102_i2c_write(cam, 0x03, ctrl->value >> 4); - err += sn9c102_i2c_write(cam, 0x04, ctrl->value & 0x0f); - break; - case V4L2_CID_RED_BALANCE: - err += sn9c102_i2c_write(cam, 0x0c, ctrl->value); - break; - case V4L2_CID_BLUE_BALANCE: - err += sn9c102_i2c_write(cam, 0x09, ctrl->value); - break; - case V4L2_CID_GAIN: - err += sn9c102_i2c_write(cam, 0x0e, ctrl->value); - break; - case V4L2_CID_CONTRAST: - err += sn9c102_i2c_write(cam, 0x0f, ctrl->value); - break; - case SN9C102_V4L2_CID_GREEN_BALANCE: - err += sn9c102_i2c_write(cam, 0x0a, ctrl->value >> 1); - err += sn9c102_i2c_write(cam, 0x0b, ctrl->value >> 1); - break; - case SN9C102_V4L2_CID_DAC_MAGNITUDE: - err += sn9c102_i2c_write(cam, 0x08, ctrl->value << 3); - break; - default: - return -EINVAL; - } - err += sn9c102_i2c_write(cam, 0x13, 0x01); - - return err ? -EIO : 0; -} - - -static int pas106b_set_crop(struct sn9c102_device* cam, - const struct v4l2_rect* rect) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - int err = 0; - u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 4, - v_start = (u8)(rect->top - s->cropcap.bounds.top) + 3; - - err += sn9c102_write_reg(cam, h_start, 0x12); - err += sn9c102_write_reg(cam, v_start, 0x13); - - return err; -} - - -static int pas106b_set_pix_format(struct sn9c102_device* cam, - const struct v4l2_pix_format* pix) -{ - int err = 0; - - if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) - err += sn9c102_write_reg(cam, 0x2c, 0x17); - else - err += sn9c102_write_reg(cam, 0x20, 0x17); - - return err; -} - - -static const struct sn9c102_sensor pas106b = { - .name = "PAS106B", - .maintainer = "Luca Risolia ", - .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102, - .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, - .frequency = SN9C102_I2C_400KHZ | SN9C102_I2C_100KHZ, - .interface = SN9C102_I2C_2WIRES, - .i2c_slave_id = 0x40, - .init = &pas106b_init, - .qctrl = { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0x125, - .maximum = 0xfff, - .step = 0x001, - .default_value = 0x140, - .flags = 0, - }, - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "global gain", - .minimum = 0x00, - .maximum = 0x1f, - .step = 0x01, - .default_value = 0x0d, - .flags = 0, - }, - { - .id = V4L2_CID_CONTRAST, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "contrast", - .minimum = 0x00, - .maximum = 0x07, - .step = 0x01, - .default_value = 0x00, /* 0x00~0x03 have same effect */ - .flags = 0, - }, - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "red balance", - .minimum = 0x00, - .maximum = 0x1f, - .step = 0x01, - .default_value = 0x04, - .flags = 0, - }, - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "blue balance", - .minimum = 0x00, - .maximum = 0x1f, - .step = 0x01, - .default_value = 0x06, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_GREEN_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "green balance", - .minimum = 0x00, - .maximum = 0x3e, - .step = 0x02, - .default_value = 0x02, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_DAC_MAGNITUDE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "DAC magnitude", - .minimum = 0x00, - .maximum = 0x1f, - .step = 0x01, - .default_value = 0x01, - .flags = 0, - }, - }, - .get_ctrl = &pas106b_get_ctrl, - .set_ctrl = &pas106b_set_ctrl, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 352, - .height = 288, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 352, - .height = 288, - }, - }, - .set_crop = &pas106b_set_crop, - .pix_format = { - .width = 352, - .height = 288, - .pixelformat = V4L2_PIX_FMT_SBGGR8, - .priv = 8, /* we use this field as 'bits per pixel' */ - }, - .set_pix_format = &pas106b_set_pix_format -}; - - -int sn9c102_probe_pas106b(struct sn9c102_device* cam) -{ - int r0 = 0, r1 = 0; - unsigned int pid = 0; - - /* - Minimal initialization to enable the I2C communication - NOTE: do NOT change the values! - */ - if (sn9c102_write_const_regs(cam, - {0x01, 0x01}, /* sensor power down */ - {0x00, 0x01}, /* sensor power on */ - {0x28, 0x17})) /* sensor clock at 24 MHz */ - return -EIO; - - r0 = sn9c102_i2c_try_read(cam, &pas106b, 0x00); - r1 = sn9c102_i2c_try_read(cam, &pas106b, 0x01); - if (r0 < 0 || r1 < 0) - return -EIO; - - pid = (r0 << 11) | ((r1 & 0xf0) >> 4); - if (pid != 0x007) - return -ENODEV; - - sn9c102_attach_sensor(cam, &pas106b); - - return 0; -} diff --git a/drivers/media/usb/sn9c102/sn9c102_pas202bcb.c b/drivers/media/usb/sn9c102/sn9c102_pas202bcb.c deleted file mode 100644 index 2e86fdc86989..000000000000 --- a/drivers/media/usb/sn9c102/sn9c102_pas202bcb.c +++ /dev/null @@ -1,335 +0,0 @@ -/*************************************************************************** - * Plug-in for PAS202BCB image sensor connected to the SN9C1xx PC Camera * - * Controllers * - * * - * Copyright (C) 2004 by Carlos Eduardo Medaglia Dyonisio * - * * - * * - * Support for SN9C103, DAC Magnitude, exposure and green gain controls * - * added by Luca Risolia * - * * - * 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. * - ***************************************************************************/ - -#include -#include "sn9c102_sensor.h" -#include "sn9c102_devtable.h" - - -static int pas202bcb_init(struct sn9c102_device* cam) -{ - int err = 0; - - switch (sn9c102_get_bridge(cam)) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - err = sn9c102_write_const_regs(cam, {0x00, 0x10}, {0x00, 0x11}, - {0x00, 0x14}, {0x20, 0x17}, - {0x30, 0x19}, {0x09, 0x18}); - break; - case BRIDGE_SN9C103: - err = sn9c102_write_const_regs(cam, {0x00, 0x02}, {0x00, 0x03}, - {0x1a, 0x04}, {0x20, 0x05}, - {0x20, 0x06}, {0x20, 0x07}, - {0x00, 0x10}, {0x00, 0x11}, - {0x00, 0x14}, {0x20, 0x17}, - {0x30, 0x19}, {0x09, 0x18}, - {0x02, 0x1c}, {0x03, 0x1d}, - {0x0f, 0x1e}, {0x0c, 0x1f}, - {0x00, 0x20}, {0x10, 0x21}, - {0x20, 0x22}, {0x30, 0x23}, - {0x40, 0x24}, {0x50, 0x25}, - {0x60, 0x26}, {0x70, 0x27}, - {0x80, 0x28}, {0x90, 0x29}, - {0xa0, 0x2a}, {0xb0, 0x2b}, - {0xc0, 0x2c}, {0xd0, 0x2d}, - {0xe0, 0x2e}, {0xf0, 0x2f}, - {0xff, 0x30}); - break; - default: - break; - } - - err += sn9c102_i2c_write(cam, 0x02, 0x14); - err += sn9c102_i2c_write(cam, 0x03, 0x40); - err += sn9c102_i2c_write(cam, 0x0d, 0x2c); - err += sn9c102_i2c_write(cam, 0x0e, 0x01); - err += sn9c102_i2c_write(cam, 0x0f, 0xa9); - err += sn9c102_i2c_write(cam, 0x10, 0x08); - err += sn9c102_i2c_write(cam, 0x13, 0x63); - err += sn9c102_i2c_write(cam, 0x15, 0x70); - err += sn9c102_i2c_write(cam, 0x11, 0x01); - - msleep(400); - - return err; -} - - -static int pas202bcb_get_ctrl(struct sn9c102_device* cam, - struct v4l2_control* ctrl) -{ - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - { - int r1 = sn9c102_i2c_read(cam, 0x04), - r2 = sn9c102_i2c_read(cam, 0x05); - if (r1 < 0 || r2 < 0) - return -EIO; - ctrl->value = (r1 << 6) | (r2 & 0x3f); - } - return 0; - case V4L2_CID_RED_BALANCE: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x09)) < 0) - return -EIO; - ctrl->value &= 0x0f; - return 0; - case V4L2_CID_BLUE_BALANCE: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x07)) < 0) - return -EIO; - ctrl->value &= 0x0f; - return 0; - case V4L2_CID_GAIN: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x10)) < 0) - return -EIO; - ctrl->value &= 0x1f; - return 0; - case SN9C102_V4L2_CID_GREEN_BALANCE: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x08)) < 0) - return -EIO; - ctrl->value &= 0x0f; - return 0; - case SN9C102_V4L2_CID_DAC_MAGNITUDE: - if ((ctrl->value = sn9c102_i2c_read(cam, 0x0c)) < 0) - return -EIO; - return 0; - default: - return -EINVAL; - } -} - - -static int pas202bcb_set_pix_format(struct sn9c102_device* cam, - const struct v4l2_pix_format* pix) -{ - int err = 0; - - if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) - err += sn9c102_write_reg(cam, 0x28, 0x17); - else - err += sn9c102_write_reg(cam, 0x20, 0x17); - - return err; -} - - -static int pas202bcb_set_ctrl(struct sn9c102_device* cam, - const struct v4l2_control* ctrl) -{ - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - err += sn9c102_i2c_write(cam, 0x04, ctrl->value >> 6); - err += sn9c102_i2c_write(cam, 0x05, ctrl->value & 0x3f); - break; - case V4L2_CID_RED_BALANCE: - err += sn9c102_i2c_write(cam, 0x09, ctrl->value); - break; - case V4L2_CID_BLUE_BALANCE: - err += sn9c102_i2c_write(cam, 0x07, ctrl->value); - break; - case V4L2_CID_GAIN: - err += sn9c102_i2c_write(cam, 0x10, ctrl->value); - break; - case SN9C102_V4L2_CID_GREEN_BALANCE: - err += sn9c102_i2c_write(cam, 0x08, ctrl->value); - break; - case SN9C102_V4L2_CID_DAC_MAGNITUDE: - err += sn9c102_i2c_write(cam, 0x0c, ctrl->value); - break; - default: - return -EINVAL; - } - err += sn9c102_i2c_write(cam, 0x11, 0x01); - - return err ? -EIO : 0; -} - - -static int pas202bcb_set_crop(struct sn9c102_device* cam, - const struct v4l2_rect* rect) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - int err = 0; - u8 h_start = 0, - v_start = (u8)(rect->top - s->cropcap.bounds.top) + 3; - - switch (sn9c102_get_bridge(cam)) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - h_start = (u8)(rect->left - s->cropcap.bounds.left) + 4; - break; - case BRIDGE_SN9C103: - h_start = (u8)(rect->left - s->cropcap.bounds.left) + 3; - break; - default: - break; - } - - err += sn9c102_write_reg(cam, h_start, 0x12); - err += sn9c102_write_reg(cam, v_start, 0x13); - - return err; -} - - -static const struct sn9c102_sensor pas202bcb = { - .name = "PAS202BCB", - .maintainer = "Luca Risolia ", - .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102 | BRIDGE_SN9C103, - .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, - .frequency = SN9C102_I2C_400KHZ | SN9C102_I2C_100KHZ, - .interface = SN9C102_I2C_2WIRES, - .i2c_slave_id = 0x40, - .init = &pas202bcb_init, - .qctrl = { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0x01e5, - .maximum = 0x3fff, - .step = 0x0001, - .default_value = 0x01e5, - .flags = 0, - }, - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "global gain", - .minimum = 0x00, - .maximum = 0x1f, - .step = 0x01, - .default_value = 0x0b, - .flags = 0, - }, - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "red balance", - .minimum = 0x00, - .maximum = 0x0f, - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "blue balance", - .minimum = 0x00, - .maximum = 0x0f, - .step = 0x01, - .default_value = 0x05, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_GREEN_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "green balance", - .minimum = 0x00, - .maximum = 0x0f, - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_DAC_MAGNITUDE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "DAC magnitude", - .minimum = 0x00, - .maximum = 0xff, - .step = 0x01, - .default_value = 0x04, - .flags = 0, - }, - }, - .get_ctrl = &pas202bcb_get_ctrl, - .set_ctrl = &pas202bcb_set_ctrl, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - }, - .set_crop = &pas202bcb_set_crop, - .pix_format = { - .width = 640, - .height = 480, - .pixelformat = V4L2_PIX_FMT_SBGGR8, - .priv = 8, - }, - .set_pix_format = &pas202bcb_set_pix_format -}; - - -int sn9c102_probe_pas202bcb(struct sn9c102_device* cam) -{ - int r0 = 0, r1 = 0, err = 0; - unsigned int pid = 0; - - /* - * Minimal initialization to enable the I2C communication - * NOTE: do NOT change the values! - */ - switch (sn9c102_get_bridge(cam)) { - case BRIDGE_SN9C101: - case BRIDGE_SN9C102: - err = sn9c102_write_const_regs(cam, - {0x01, 0x01}, /* power down */ - {0x40, 0x01}, /* power on */ - {0x28, 0x17});/* clock 24 MHz */ - break; - case BRIDGE_SN9C103: /* do _not_ change anything! */ - err = sn9c102_write_const_regs(cam, {0x09, 0x01}, {0x44, 0x01}, - {0x44, 0x02}, {0x29, 0x17}); - break; - default: - break; - } - - r0 = sn9c102_i2c_try_read(cam, &pas202bcb, 0x00); - r1 = sn9c102_i2c_try_read(cam, &pas202bcb, 0x01); - - if (err || r0 < 0 || r1 < 0) - return -EIO; - - pid = (r0 << 4) | ((r1 & 0xf0) >> 4); - if (pid != 0x017) - return -ENODEV; - - sn9c102_attach_sensor(cam, &pas202bcb); - - return 0; -} diff --git a/drivers/media/usb/sn9c102/sn9c102_sensor.h b/drivers/media/usb/sn9c102/sn9c102_sensor.h deleted file mode 100644 index 3679970dba2c..000000000000 --- a/drivers/media/usb/sn9c102/sn9c102_sensor.h +++ /dev/null @@ -1,307 +0,0 @@ -/*************************************************************************** - * API for image sensors connected to the SN9C1xx PC Camera Controllers * - * * - * Copyright (C) 2004-2007 by Luca Risolia * - * * - * 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 _SN9C102_SENSOR_H_ -#define _SN9C102_SENSOR_H_ - -#include -#include -#include -#include -#include -#include - -struct sn9c102_device; -struct sn9c102_sensor; - -/*****************************************************************************/ - -/* - OVERVIEW. - This is a small interface that allows you to add support for any CCD/CMOS - image sensors connected to the SN9C1XX bridges. The entire API is documented - below. In the most general case, to support a sensor there are three steps - you have to follow: - 1) define the main "sn9c102_sensor" structure by setting the basic fields; - 2) write a probing function to be called by the core module when the USB - camera is recognized, then add both the USB ids and the name of that - function to the two corresponding tables in sn9c102_devtable.h; - 3) implement the methods that you want/need (and fill the rest of the main - structure accordingly). - "sn9c102_pas106b.c" is an example of all this stuff. Remember that you do - NOT need to touch the source code of the core module for the things to work - properly, unless you find bugs or flaws in it. Finally, do not forget to - read the V4L2 API for completeness. -*/ - -/*****************************************************************************/ - -enum sn9c102_bridge { - BRIDGE_SN9C101 = 0x01, - BRIDGE_SN9C102 = 0x02, - BRIDGE_SN9C103 = 0x04, - BRIDGE_SN9C105 = 0x08, - BRIDGE_SN9C120 = 0x10, -}; - -/* Return the bridge name */ -enum sn9c102_bridge sn9c102_get_bridge(struct sn9c102_device* cam); - -/* Return a pointer the sensor struct attached to the camera */ -struct sn9c102_sensor* sn9c102_get_sensor(struct sn9c102_device* cam); - -/* Identify a device */ -extern struct sn9c102_device* -sn9c102_match_id(struct sn9c102_device* cam, const struct usb_device_id *id); - -/* Attach a probed sensor to the camera. */ -extern void -sn9c102_attach_sensor(struct sn9c102_device* cam, - const struct sn9c102_sensor* sensor); - -/* - Read/write routines: they always return -1 on error, 0 or the read value - otherwise. NOTE that a real read operation is not supported by the SN9C1XX - chip for some of its registers. To work around this problem, a pseudo-read - call is provided instead: it returns the last successfully written value - on the register (0 if it has never been written), the usual -1 on error. -*/ - -/* The "try" I2C I/O versions are used when probing the sensor */ -extern int sn9c102_i2c_try_read(struct sn9c102_device*, - const struct sn9c102_sensor*, u8 address); - -/* - These must be used if and only if the sensor doesn't implement the standard - I2C protocol. There are a number of good reasons why you must use the - single-byte versions of these functions: do not abuse. The first function - writes n bytes, from data0 to datan, to registers 0x09 - 0x09+n of SN9C1XX - chip. The second one programs the registers 0x09 and 0x10 with data0 and - data1, and places the n bytes read from the sensor register table in the - buffer pointed by 'buffer'. Both the functions return -1 on error; the write - version returns 0 on success, while the read version returns the first read - byte. -*/ -extern int sn9c102_i2c_try_raw_write(struct sn9c102_device* cam, - const struct sn9c102_sensor* sensor, u8 n, - u8 data0, u8 data1, u8 data2, u8 data3, - u8 data4, u8 data5); -extern int sn9c102_i2c_try_raw_read(struct sn9c102_device* cam, - const struct sn9c102_sensor* sensor, - u8 data0, u8 data1, u8 n, u8 buffer[]); - -/* To be used after the sensor struct has been attached to the camera struct */ -extern int sn9c102_i2c_write(struct sn9c102_device*, u8 address, u8 value); -extern int sn9c102_i2c_read(struct sn9c102_device*, u8 address); - -/* I/O on registers in the bridge. Could be used by the sensor methods too */ -extern int sn9c102_read_reg(struct sn9c102_device*, u16 index); -extern int sn9c102_pread_reg(struct sn9c102_device*, u16 index); -extern int sn9c102_write_reg(struct sn9c102_device*, u8 value, u16 index); -extern int sn9c102_write_regs(struct sn9c102_device*, const u8 valreg[][2], - int count); -/* - Write multiple registers with constant values. For example: - sn9c102_write_const_regs(cam, {0x00, 0x14}, {0x60, 0x17}, {0x0f, 0x18}); - Register addresses must be < 256. -*/ -#define sn9c102_write_const_regs(sn9c102_device, data...) \ - ({ static const u8 _valreg[][2] = {data}; \ - sn9c102_write_regs(sn9c102_device, _valreg, ARRAY_SIZE(_valreg)); }) - -/*****************************************************************************/ - -enum sn9c102_i2c_sysfs_ops { - SN9C102_I2C_READ = 0x01, - SN9C102_I2C_WRITE = 0x02, -}; - -enum sn9c102_i2c_frequency { /* sensors may support both the frequencies */ - SN9C102_I2C_100KHZ = 0x01, - SN9C102_I2C_400KHZ = 0x02, -}; - -enum sn9c102_i2c_interface { - SN9C102_I2C_2WIRES, - SN9C102_I2C_3WIRES, -}; - -#define SN9C102_MAX_CTRLS (V4L2_CID_LASTP1-V4L2_CID_BASE+10) - -struct sn9c102_sensor { - char name[32], /* sensor name */ - maintainer[64]; /* name of the maintainer */ - - enum sn9c102_bridge supported_bridge; /* supported SN9C1xx bridges */ - - /* Supported operations through the 'sysfs' interface */ - enum sn9c102_i2c_sysfs_ops sysfs_ops; - - /* - These sensor capabilities must be provided if the SN9C1XX controller - needs to communicate through the sensor serial interface by using - at least one of the i2c functions available. - */ - enum sn9c102_i2c_frequency frequency; - enum sn9c102_i2c_interface interface; - - /* - This identifier must be provided if the image sensor implements - the standard I2C protocol. - */ - u8 i2c_slave_id; /* reg. 0x09 */ - - /* - NOTE: Where not noted,most of the functions below are not mandatory. - Set to null if you do not implement them. If implemented, - they must return 0 on success, the proper error otherwise. - */ - - int (*init)(struct sn9c102_device* cam); - /* - This function will be called after the sensor has been attached. - It should be used to initialize the sensor only, but may also - configure part of the SN9C1XX chip if necessary. You don't need to - setup picture settings like brightness, contrast, etc.. here, if - the corresponding controls are implemented (see below), since - they are adjusted in the core driver by calling the set_ctrl() - method after init(), where the arguments are the default values - specified in the v4l2_queryctrl list of supported controls; - Same suggestions apply for other settings, _if_ the corresponding - methods are present; if not, the initialization must configure the - sensor according to the default configuration structures below. - */ - - struct v4l2_queryctrl qctrl[SN9C102_MAX_CTRLS]; - /* - Optional list of default controls, defined as indicated in the - V4L2 API. Menu type controls are not handled by this interface. - */ - - int (*get_ctrl)(struct sn9c102_device* cam, struct v4l2_control* ctrl); - int (*set_ctrl)(struct sn9c102_device* cam, - const struct v4l2_control* ctrl); - /* - You must implement at least the set_ctrl method if you have defined - the list above. The returned value must follow the V4L2 - specifications for the VIDIOC_G|C_CTRL ioctls. V4L2_CID_H|VCENTER - are not supported by this driver, so do not implement them. Also, - you don't have to check whether the passed values are out of bounds, - given that this is done by the core module. - */ - - struct v4l2_cropcap cropcap; - /* - Think the image sensor as a grid of R,G,B monochromatic pixels - disposed according to a particular Bayer pattern, which describes - the complete array of pixels, from (0,0) to (xmax, ymax). We will - use this coordinate system from now on. It is assumed the sensor - chip can be programmed to capture/transmit a subsection of that - array of pixels: we will call this subsection "active window". - It is not always true that the largest achievable active window can - cover the whole array of pixels. The V4L2 API defines another - area called "source rectangle", which, in turn, is a subrectangle of - the active window. The SN9C1XX chip is always programmed to read the - source rectangle. - The bounds of both the active window and the source rectangle are - specified in the cropcap substructures 'bounds' and 'defrect'. - By default, the source rectangle should cover the largest possible - area. Again, it is not always true that the largest source rectangle - can cover the entire active window, although it is a rare case for - the hardware we have. The bounds of the source rectangle _must_ be - multiple of 16 and must use the same coordinate system as indicated - before; their centers shall align initially. - If necessary, the sensor chip must be initialized during init() to - set the bounds of the active sensor window; however, by default, it - usually covers the largest achievable area (maxwidth x maxheight) - of pixels, so no particular initialization is needed, if you have - defined the correct default bounds in the structures. - See the V4L2 API for further details. - NOTE: once you have defined the bounds of the active window - (struct cropcap.bounds) you must not change them.anymore. - Only 'bounds' and 'defrect' fields are mandatory, other fields - will be ignored. - */ - - int (*set_crop)(struct sn9c102_device* cam, - const struct v4l2_rect* rect); - /* - To be called on VIDIOC_C_SETCROP. The core module always calls a - default routine which configures the appropriate SN9C1XX regs (also - scaling), but you may need to override/adjust specific stuff. - 'rect' contains width and height values that are multiple of 16: in - case you override the default function, you always have to program - the chip to match those values; on error return the corresponding - error code without rolling back. - NOTE: in case, you must program the SN9C1XX chip to get rid of - blank pixels or blank lines at the _start_ of each line or - frame after each HSYNC or VSYNC, so that the image starts with - real RGB data (see regs 0x12, 0x13) (having set H_SIZE and, - V_SIZE you don't have to care about blank pixels or blank - lines at the end of each line or frame). - */ - - struct v4l2_pix_format pix_format; - /* - What you have to define here are: 1) initial 'width' and 'height' of - the target rectangle 2) the initial 'pixelformat', which can be - either V4L2_PIX_FMT_SN9C10X, V4L2_PIX_FMT_JPEG (for ompressed video) - or V4L2_PIX_FMT_SBGGR8 3) 'priv', which we'll be used to indicate - the number of bits per pixel for uncompressed video, 8 or 9 (despite - the current value of 'pixelformat'). - NOTE 1: both 'width' and 'height' _must_ be either 1/1 or 1/2 or 1/4 - of cropcap.defrect.width and cropcap.defrect.height. I - suggest 1/1. - NOTE 2: The initial compression quality is defined by the first bit - of reg 0x17 during the initialization of the image sensor. - NOTE 3: as said above, you have to program the SN9C1XX chip to get - rid of any blank pixels, so that the output of the sensor - matches the RGB bayer sequence (i.e. BGBGBG...GRGRGR). - */ - - int (*set_pix_format)(struct sn9c102_device* cam, - const struct v4l2_pix_format* pix); - /* - To be called on VIDIOC_S_FMT, when switching from the SBGGR8 to - SN9C10X pixel format or viceversa. On error return the corresponding - error code without rolling back. - */ - - /* - Do NOT write to the data below, it's READ ONLY. It is used by the - core module to store successfully updated values of the above - settings, for rollbacks..etc..in case of errors during atomic I/O - */ - struct v4l2_queryctrl _qctrl[SN9C102_MAX_CTRLS]; - struct v4l2_rect _rect; -}; - -/*****************************************************************************/ - -/* Private ioctl's for control settings supported by some image sensors */ -#define SN9C102_V4L2_CID_DAC_MAGNITUDE (V4L2_CID_PRIVATE_BASE + 0) -#define SN9C102_V4L2_CID_GREEN_BALANCE (V4L2_CID_PRIVATE_BASE + 1) -#define SN9C102_V4L2_CID_RESET_LEVEL (V4L2_CID_PRIVATE_BASE + 2) -#define SN9C102_V4L2_CID_PIXEL_BIAS_VOLTAGE (V4L2_CID_PRIVATE_BASE + 3) -#define SN9C102_V4L2_CID_GAMMA (V4L2_CID_PRIVATE_BASE + 4) -#define SN9C102_V4L2_CID_BAND_FILTER (V4L2_CID_PRIVATE_BASE + 5) -#define SN9C102_V4L2_CID_BRIGHT_LEVEL (V4L2_CID_PRIVATE_BASE + 6) - -#endif /* _SN9C102_SENSOR_H_ */ diff --git a/drivers/media/usb/sn9c102/sn9c102_tas5110c1b.c b/drivers/media/usb/sn9c102/sn9c102_tas5110c1b.c deleted file mode 100644 index 04cdfdde8564..000000000000 --- a/drivers/media/usb/sn9c102/sn9c102_tas5110c1b.c +++ /dev/null @@ -1,154 +0,0 @@ -/*************************************************************************** - * Plug-in for TAS5110C1B image sensor connected to the SN9C1xx PC Camera * - * Controllers * - * * - * Copyright (C) 2004-2007 by Luca Risolia * - * * - * 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. * - ***************************************************************************/ - -#include "sn9c102_sensor.h" -#include "sn9c102_devtable.h" - - -static int tas5110c1b_init(struct sn9c102_device* cam) -{ - int err = 0; - - err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x44, 0x01}, - {0x00, 0x10}, {0x00, 0x11}, - {0x0a, 0x14}, {0x60, 0x17}, - {0x06, 0x18}, {0xfb, 0x19}); - - err += sn9c102_i2c_write(cam, 0xc0, 0x80); - - return err; -} - - -static int tas5110c1b_set_ctrl(struct sn9c102_device* cam, - const struct v4l2_control* ctrl) -{ - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_GAIN: - err += sn9c102_i2c_write(cam, 0x20, 0xf6 - ctrl->value); - break; - default: - return -EINVAL; - } - - return err ? -EIO : 0; -} - - -static int tas5110c1b_set_crop(struct sn9c102_device* cam, - const struct v4l2_rect* rect) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - int err = 0; - u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 69, - v_start = (u8)(rect->top - s->cropcap.bounds.top) + 9; - - err += sn9c102_write_reg(cam, h_start, 0x12); - err += sn9c102_write_reg(cam, v_start, 0x13); - - /* Don't change ! */ - err += sn9c102_write_reg(cam, 0x14, 0x1a); - err += sn9c102_write_reg(cam, 0x0a, 0x1b); - err += sn9c102_write_reg(cam, sn9c102_pread_reg(cam, 0x19), 0x19); - - return err; -} - - -static int tas5110c1b_set_pix_format(struct sn9c102_device* cam, - const struct v4l2_pix_format* pix) -{ - int err = 0; - - if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) - err += sn9c102_write_reg(cam, 0x2b, 0x19); - else - err += sn9c102_write_reg(cam, 0xfb, 0x19); - - return err; -} - - -static const struct sn9c102_sensor tas5110c1b = { - .name = "TAS5110C1B", - .maintainer = "Luca Risolia ", - .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102, - .sysfs_ops = SN9C102_I2C_WRITE, - .frequency = SN9C102_I2C_100KHZ, - .interface = SN9C102_I2C_3WIRES, - .init = &tas5110c1b_init, - .qctrl = { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "global gain", - .minimum = 0x00, - .maximum = 0xf6, - .step = 0x01, - .default_value = 0x40, - .flags = 0, - }, - }, - .set_ctrl = &tas5110c1b_set_ctrl, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 352, - .height = 288, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 352, - .height = 288, - }, - }, - .set_crop = &tas5110c1b_set_crop, - .pix_format = { - .width = 352, - .height = 288, - .pixelformat = V4L2_PIX_FMT_SBGGR8, - .priv = 8, - }, - .set_pix_format = &tas5110c1b_set_pix_format -}; - - -int sn9c102_probe_tas5110c1b(struct sn9c102_device* cam) -{ - const struct usb_device_id tas5110c1b_id_table[] = { - { USB_DEVICE(0x0c45, 0x6001), }, - { USB_DEVICE(0x0c45, 0x6005), }, - { USB_DEVICE(0x0c45, 0x60ab), }, - { } - }; - - /* Sensor detection is based on USB pid/vid */ - if (!sn9c102_match_id(cam, tas5110c1b_id_table)) - return -ENODEV; - - sn9c102_attach_sensor(cam, &tas5110c1b); - - return 0; -} diff --git a/drivers/media/usb/sn9c102/sn9c102_tas5110d.c b/drivers/media/usb/sn9c102/sn9c102_tas5110d.c deleted file mode 100644 index 9372e6f9fcff..000000000000 --- a/drivers/media/usb/sn9c102/sn9c102_tas5110d.c +++ /dev/null @@ -1,119 +0,0 @@ -/*************************************************************************** - * Plug-in for TAS5110D image sensor connected to the SN9C1xx PC Camera * - * Controllers * - * * - * Copyright (C) 2007 by Luca Risolia * - * * - * 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. * - ***************************************************************************/ - -#include "sn9c102_sensor.h" -#include "sn9c102_devtable.h" - - -static int tas5110d_init(struct sn9c102_device* cam) -{ - int err; - - err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x04, 0x01}, - {0x0a, 0x14}, {0x60, 0x17}, - {0x06, 0x18}, {0xfb, 0x19}); - - err += sn9c102_i2c_write(cam, 0x9a, 0xca); - - return err; -} - - -static int tas5110d_set_crop(struct sn9c102_device* cam, - const struct v4l2_rect* rect) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - int err = 0; - u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 69, - v_start = (u8)(rect->top - s->cropcap.bounds.top) + 9; - - err += sn9c102_write_reg(cam, h_start, 0x12); - err += sn9c102_write_reg(cam, v_start, 0x13); - - err += sn9c102_write_reg(cam, 0x14, 0x1a); - err += sn9c102_write_reg(cam, 0x0a, 0x1b); - - return err; -} - - -static int tas5110d_set_pix_format(struct sn9c102_device* cam, - const struct v4l2_pix_format* pix) -{ - int err = 0; - - if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) - err += sn9c102_write_reg(cam, 0x3b, 0x19); - else - err += sn9c102_write_reg(cam, 0xfb, 0x19); - - return err; -} - - -static const struct sn9c102_sensor tas5110d = { - .name = "TAS5110D", - .maintainer = "Luca Risolia ", - .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102, - .sysfs_ops = SN9C102_I2C_WRITE, - .frequency = SN9C102_I2C_100KHZ, - .interface = SN9C102_I2C_2WIRES, - .i2c_slave_id = 0x61, - .init = &tas5110d_init, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 352, - .height = 288, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 352, - .height = 288, - }, - }, - .set_crop = &tas5110d_set_crop, - .pix_format = { - .width = 352, - .height = 288, - .pixelformat = V4L2_PIX_FMT_SBGGR8, - .priv = 8, - }, - .set_pix_format = &tas5110d_set_pix_format -}; - - -int sn9c102_probe_tas5110d(struct sn9c102_device* cam) -{ - const struct usb_device_id tas5110d_id_table[] = { - { USB_DEVICE(0x0c45, 0x6007), }, - { } - }; - - if (!sn9c102_match_id(cam, tas5110d_id_table)) - return -ENODEV; - - sn9c102_attach_sensor(cam, &tas5110d); - - return 0; -} diff --git a/drivers/media/usb/sn9c102/sn9c102_tas5130d1b.c b/drivers/media/usb/sn9c102/sn9c102_tas5130d1b.c deleted file mode 100644 index a30bbc4389f5..000000000000 --- a/drivers/media/usb/sn9c102/sn9c102_tas5130d1b.c +++ /dev/null @@ -1,165 +0,0 @@ -/*************************************************************************** - * Plug-in for TAS5130D1B image sensor connected to the SN9C1xx PC Camera * - * Controllers * - * * - * Copyright (C) 2004-2007 by Luca Risolia * - * * - * 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. * - ***************************************************************************/ - -#include "sn9c102_sensor.h" -#include "sn9c102_devtable.h" - - -static int tas5130d1b_init(struct sn9c102_device* cam) -{ - int err; - - err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x20, 0x17}, - {0x04, 0x01}, {0x01, 0x10}, - {0x00, 0x11}, {0x00, 0x14}, - {0x60, 0x17}, {0x07, 0x18}); - - return err; -} - - -static int tas5130d1b_set_ctrl(struct sn9c102_device* cam, - const struct v4l2_control* ctrl) -{ - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_GAIN: - err += sn9c102_i2c_write(cam, 0x20, 0xf6 - ctrl->value); - break; - case V4L2_CID_EXPOSURE: - err += sn9c102_i2c_write(cam, 0x40, 0x47 - ctrl->value); - break; - default: - return -EINVAL; - } - - return err ? -EIO : 0; -} - - -static int tas5130d1b_set_crop(struct sn9c102_device* cam, - const struct v4l2_rect* rect) -{ - struct sn9c102_sensor* s = sn9c102_get_sensor(cam); - u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 104, - v_start = (u8)(rect->top - s->cropcap.bounds.top) + 12; - int err = 0; - - err += sn9c102_write_reg(cam, h_start, 0x12); - err += sn9c102_write_reg(cam, v_start, 0x13); - - /* Do NOT change! */ - err += sn9c102_write_reg(cam, 0x1f, 0x1a); - err += sn9c102_write_reg(cam, 0x1a, 0x1b); - err += sn9c102_write_reg(cam, sn9c102_pread_reg(cam, 0x19), 0x19); - - return err; -} - - -static int tas5130d1b_set_pix_format(struct sn9c102_device* cam, - const struct v4l2_pix_format* pix) -{ - int err = 0; - - if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) - err += sn9c102_write_reg(cam, 0x63, 0x19); - else - err += sn9c102_write_reg(cam, 0xf3, 0x19); - - return err; -} - - -static const struct sn9c102_sensor tas5130d1b = { - .name = "TAS5130D1B", - .maintainer = "Luca Risolia ", - .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102, - .sysfs_ops = SN9C102_I2C_WRITE, - .frequency = SN9C102_I2C_100KHZ, - .interface = SN9C102_I2C_3WIRES, - .init = &tas5130d1b_init, - .qctrl = { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "global gain", - .minimum = 0x00, - .maximum = 0xf6, - .step = 0x02, - .default_value = 0x00, - .flags = 0, - }, - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0x00, - .maximum = 0x47, - .step = 0x01, - .default_value = 0x00, - .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, - }, - }, - .set_crop = &tas5130d1b_set_crop, - .pix_format = { - .width = 640, - .height = 480, - .pixelformat = V4L2_PIX_FMT_SBGGR8, - .priv = 8, - }, - .set_pix_format = &tas5130d1b_set_pix_format -}; - - -int sn9c102_probe_tas5130d1b(struct sn9c102_device* cam) -{ - const struct usb_device_id tas5130d1b_id_table[] = { - { USB_DEVICE(0x0c45, 0x6024), }, - { USB_DEVICE(0x0c45, 0x6025), }, - { USB_DEVICE(0x0c45, 0x60aa), }, - { } - }; - - /* Sensor detection is based on USB pid/vid */ - if (!sn9c102_match_id(cam, tas5130d1b_id_table)) - return -ENODEV; - - sn9c102_attach_sensor(cam, &tas5130d1b); - - return 0; -} diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index 77288796540b..6a202170ab8c 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -33,6 +33,8 @@ source "drivers/staging/media/go7007/Kconfig" source "drivers/staging/media/msi3101/Kconfig" +source "drivers/staging/media/sn9c102/Kconfig" + source "drivers/staging/media/solo6x10/Kconfig" source "drivers/staging/media/omap4iss/Kconfig" diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index 0bd1a88a27fa..2a154517e105 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -8,3 +8,4 @@ obj-$(CONFIG_VIDEO_GO7007) += go7007/ obj-$(CONFIG_USB_MSI3101) += msi3101/ obj-$(CONFIG_VIDEO_DM365_VPFE) += davinci_vpfe/ obj-$(CONFIG_VIDEO_OMAP4) += omap4iss/ +obj-$(CONFIG_USB_SN9C102) += sn9c102/ diff --git a/drivers/staging/media/sn9c102/Kconfig b/drivers/staging/media/sn9c102/Kconfig new file mode 100644 index 000000000000..d8ae2354b626 --- /dev/null +++ b/drivers/staging/media/sn9c102/Kconfig @@ -0,0 +1,17 @@ +config USB_SN9C102 + tristate "USB SN9C1xx PC Camera Controller support (DEPRECATED)" + depends on VIDEO_V4L2 + ---help--- + This driver is DEPRECATED, please use the gspca sonixb and + sonixj modules instead. + + Say Y here if you want support for cameras based on SONiX SN9C101, + SN9C102, SN9C103, SN9C105 and SN9C120 PC Camera Controllers. + + See for more info. + + If you have webcams that are only supported by this driver and not by + the gspca driver, then contact the linux-media mailinglist. + + To compile this driver as a module, choose M here: the + module will be called sn9c102. diff --git a/drivers/staging/media/sn9c102/Makefile b/drivers/staging/media/sn9c102/Makefile new file mode 100644 index 000000000000..7ecd5a90c7c9 --- /dev/null +++ b/drivers/staging/media/sn9c102/Makefile @@ -0,0 +1,15 @@ +sn9c102-objs := sn9c102_core.o \ + sn9c102_hv7131d.o \ + sn9c102_hv7131r.o \ + sn9c102_mi0343.o \ + sn9c102_mi0360.o \ + sn9c102_mt9v111.o \ + sn9c102_ov7630.o \ + sn9c102_ov7660.o \ + sn9c102_pas106b.o \ + sn9c102_pas202bcb.o \ + sn9c102_tas5110c1b.o \ + sn9c102_tas5110d.o \ + sn9c102_tas5130d1b.o + +obj-$(CONFIG_USB_SN9C102) += sn9c102.o diff --git a/drivers/staging/media/sn9c102/sn9c102.h b/drivers/staging/media/sn9c102/sn9c102.h new file mode 100644 index 000000000000..8a917f060503 --- /dev/null +++ b/drivers/staging/media/sn9c102/sn9c102.h @@ -0,0 +1,214 @@ +/*************************************************************************** + * V4L2 driver for SN9C1xx PC Camera Controllers * + * * + * Copyright (C) 2004-2006 by Luca Risolia * + * * + * 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 _SN9C102_H_ +#define _SN9C102_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sn9c102_config.h" +#include "sn9c102_sensor.h" +#include "sn9c102_devtable.h" + + +enum sn9c102_frame_state { + F_UNUSED, + F_QUEUED, + F_GRABBING, + F_DONE, + F_ERROR, +}; + +struct sn9c102_frame_t { + void* bufmem; + struct v4l2_buffer buf; + enum sn9c102_frame_state state; + struct list_head frame; + unsigned long vma_use_count; +}; + +enum sn9c102_dev_state { + DEV_INITIALIZED = 0x01, + DEV_DISCONNECTED = 0x02, + DEV_MISCONFIGURED = 0x04, +}; + +enum sn9c102_io_method { + IO_NONE, + IO_READ, + IO_MMAP, +}; + +enum sn9c102_stream_state { + STREAM_OFF, + STREAM_INTERRUPT, + STREAM_ON, +}; + +typedef char sn9c102_sof_header_t[62]; + +struct sn9c102_sof_t { + sn9c102_sof_header_t header; + u16 bytesread; +}; + +struct sn9c102_sysfs_attr { + u16 reg, i2c_reg; + sn9c102_sof_header_t frame_header; +}; + +struct sn9c102_module_param { + u8 force_munmap; + u16 frame_timeout; +}; + +static DEFINE_MUTEX(sn9c102_sysfs_lock); +static DECLARE_RWSEM(sn9c102_dev_lock); + +struct sn9c102_device { + struct video_device* v4ldev; + + struct v4l2_device v4l2_dev; + + enum sn9c102_bridge bridge; + struct sn9c102_sensor sensor; + + struct usb_device* usbdev; + struct urb* urb[SN9C102_URBS]; + void* transfer_buffer[SN9C102_URBS]; + u8* control_buffer; + + struct sn9c102_frame_t *frame_current, frame[SN9C102_MAX_FRAMES]; + struct list_head inqueue, outqueue; + u32 frame_count, nbuffers, nreadbuffers; + + enum sn9c102_io_method io; + enum sn9c102_stream_state stream; + + struct v4l2_jpegcompression compression; + + struct sn9c102_sysfs_attr sysfs; + struct sn9c102_sof_t sof; + u16 reg[384]; + + struct sn9c102_module_param module_param; + + struct kref kref; + enum sn9c102_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 sn9c102_device* +sn9c102_match_id(struct sn9c102_device* cam, const struct usb_device_id *id) +{ + return usb_match_id(usb_ifnum_to_if(cam->usbdev, 0), id) ? cam : NULL; +} + + +void +sn9c102_attach_sensor(struct sn9c102_device* cam, + const struct sn9c102_sensor* sensor) +{ + memcpy(&cam->sensor, sensor, sizeof(struct sn9c102_sensor)); +} + + +enum sn9c102_bridge +sn9c102_get_bridge(struct sn9c102_device* cam) +{ + return cam->bridge; +} + + +struct sn9c102_sensor* sn9c102_get_sensor(struct sn9c102_device* cam) +{ + return &cam->sensor; +} + +/*****************************************************************************/ + +#undef DBG +#undef KDBG +#ifdef SN9C102_DEBUG +# define DBG(level, fmt, args...) \ +do { \ + if (debug >= (level)) { \ + if ((level) == 1) \ + dev_err(&cam->usbdev->dev, fmt "\n", ## args); \ + else if ((level) == 2) \ + dev_info(&cam->usbdev->dev, fmt "\n", ## args); \ + else if ((level) >= 3) \ + dev_info(&cam->usbdev->dev, "[%s:%d] " fmt "\n", \ + __func__, __LINE__ , ## args); \ + } \ +} while (0) +# define V4LDBG(level, name, cmd) \ +do { \ + if (debug >= (level)) \ + v4l_printk_ioctl(name, cmd); \ +} while (0) +# define KDBG(level, fmt, args...) \ +do { \ + if (debug >= (level)) { \ + if ((level) == 1 || (level) == 2) \ + pr_info("sn9c102: " fmt "\n", ## args); \ + else if ((level) == 3) \ + pr_debug("sn9c102: [%s:%d] " fmt "\n", \ + __func__, __LINE__ , ## args); \ + } \ +} while (0) +#else +# define DBG(level, fmt, args...) do {;} while(0) +# define V4LDBG(level, name, cmd) do {;} while(0) +# define KDBG(level, fmt, args...) do {;} while(0) +#endif + +#undef PDBG +#define PDBG(fmt, args...) \ +dev_info(&cam->usbdev->dev, "[%s:%s:%d] " fmt "\n", __FILE__, __func__, \ + __LINE__ , ## args) + +#undef PDBGG +#define PDBGG(fmt, args...) do {;} while(0) /* placeholder */ + +#endif /* _SN9C102_H_ */ diff --git a/drivers/staging/media/sn9c102/sn9c102.txt b/drivers/staging/media/sn9c102/sn9c102.txt new file mode 100644 index 000000000000..b4f67040403a --- /dev/null +++ b/drivers/staging/media/sn9c102/sn9c102.txt @@ -0,0 +1,592 @@ + + SN9C1xx PC Camera Controllers + Driver for Linux + ============================= + + - Documentation - + + +Index +===== +1. Copyright +2. Disclaimer +3. License +4. Overview and features +5. Module dependencies +6. Module loading +7. Module parameters +8. Optional device control through "sysfs" +9. Supported devices +10. Notes for V4L2 application developers +11. Video frame formats +12. Contact information +13. Credits + + +1. Copyright +============ +Copyright (C) 2004-2007 by Luca Risolia + + +2. Disclaimer +============= +SONiX is a trademark of SONiX Technology Company Limited, inc. +This software is not sponsored or developed by SONiX. + + +3. License +========== +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. + + +4. Overview and features +======================== +This driver attempts to support the video interface of the devices assembling +the SONiX SN9C101, SN9C102, SN9C103, SN9C105 and SN9C120 PC Camera Controllers +("SN9C1xx" from now on). + +The driver relies on the Video4Linux2 and USB core modules. It has been +designed to run properly on SMP systems as well. + +The latest version of the SN9C1xx driver can be found at the following URL: +http://www.linux-projects.org/ + +Some of the features of the driver are: + +- full compliance with the Video4Linux2 API (see also "Notes for V4L2 + application developers" paragraph); +- available mmap or read/poll methods for video streaming through isochronous + data transfers; +- automatic detection of image sensor; +- support for built-in microphone interface; +- support for any window resolutions and optional panning within the maximum + pixel area of image sensor; +- image downscaling with arbitrary scaling factors from 1, 2 and 4 in both + directions (see "Notes for V4L2 application developers" paragraph); +- two different video formats for uncompressed or compressed data in low or + high compression quality (see also "Notes for V4L2 application developers" + and "Video frame formats" paragraphs); +- full support for the capabilities of many of the possible image sensors that + can be connected to the SN9C1xx bridges, including, for instance, red, green, + blue and global gain adjustments and exposure (see "Supported devices" + paragraph for details); +- use of default color settings for sunlight conditions; +- dynamic I/O interface for both SN9C1xx and image sensor control and + monitoring (see "Optional device control through 'sysfs'" paragraph); +- dynamic driver control thanks to various module parameters (see "Module + parameters" paragraph); +- up to 64 cameras can be handled at the same time; they can be connected and + disconnected from the host many times without turning off the computer, if + the system supports hotplugging; +- no known bugs. + + +5. Module dependencies +====================== +For it to work properly, the driver needs kernel support for Video4Linux and +USB. + +The following options of the kernel configuration file must be enabled and +corresponding modules must be compiled: + + # Multimedia devices + # + CONFIG_VIDEO_DEV=m + +To enable advanced debugging functionality on the device through /sysfs: + + # Multimedia devices + # + CONFIG_VIDEO_ADV_DEBUG=y + + # USB support + # + CONFIG_USB=m + +In addition, depending on the hardware being used, the modules below are +necessary: + + # USB Host Controller Drivers + # + CONFIG_USB_EHCI_HCD=m + CONFIG_USB_UHCI_HCD=m + CONFIG_USB_OHCI_HCD=m + +The SN9C103, SN9c105 and SN9C120 controllers also provide a built-in microphone +interface. It is supported by the USB Audio driver thanks to the ALSA API: + + # Sound + # + CONFIG_SOUND=y + + # Advanced Linux Sound Architecture + # + CONFIG_SND=m + + # USB devices + # + CONFIG_SND_USB_AUDIO=m + +And finally: + + # USB Multimedia devices + # + CONFIG_USB_SN9C102=m + + +6. Module loading +================= +To use the driver, it is necessary to load the "sn9c102" module into memory +after every other module required: "videodev", "v4l2_common", "compat_ioctl32", +"usbcore" and, depending on the USB host controller you have, "ehci-hcd", +"uhci-hcd" or "ohci-hcd". + +Loading can be done as shown below: + + [root@localhost home]# modprobe sn9c102 + +Note that the module is called "sn9c102" for historic reasons, although it +does not just support the SN9C102. + +At this point all the devices supported by the driver and connected to the USB +ports should be recognized. You can invoke "dmesg" to analyze kernel messages +and verify that the loading process has gone well: + + [user@localhost home]$ dmesg + +or, to isolate all the kernel messages generated by the driver: + + [user@localhost home]$ dmesg | grep sn9c102 + + +7. Module parameters +==================== +Module parameters are listed below: +------------------------------------------------------------------------------- +Name: video_nr +Type: short array (min = 0, max = 64) +Syntax: <-1|n[,...]> +Description: Specify V4L2 minor mode number: + -1 = use next available + n = use minor number n + You can specify up to 64 cameras this way. + For example: + video_nr=-1,2,-1 would assign minor number 2 to the second + recognized camera and use auto for the first one and for every + other camera. +Default: -1 +------------------------------------------------------------------------------- +Name: force_munmap +Type: bool array (min = 0, max = 64) +Syntax: <0|1[,...]> +Description: Force the application to unmap previously mapped buffer memory + before calling any VIDIOC_S_CROP or VIDIOC_S_FMT ioctl's. Not + all the applications support this feature. This parameter is + specific for each detected camera. + 0 = do not force memory unmapping + 1 = force memory unmapping (save memory) +Default: 0 +------------------------------------------------------------------------------- +Name: frame_timeout +Type: uint array (min = 0, max = 64) +Syntax: <0|n[,...]> +Description: Timeout for a video frame in seconds before returning an I/O + error; 0 for infinity. This parameter is specific for each + detected camera and can be changed at runtime thanks to the + /sys filesystem interface. +Default: 2 +------------------------------------------------------------------------------- +Name: debug +Type: ushort +Syntax: +Description: Debugging information level, from 0 to 3: + 0 = none (use carefully) + 1 = critical errors + 2 = significant information + 3 = more verbose messages + Level 3 is useful for testing only. It also shows some more + information about the hardware being detected. + This parameter can be changed at runtime thanks to the /sys + filesystem interface. +Default: 2 +------------------------------------------------------------------------------- + + +8. Optional device control through "sysfs" [1] +========================================== +If the kernel has been compiled with the CONFIG_VIDEO_ADV_DEBUG option enabled, +it is possible to read and write both the SN9C1xx and the image sensor +registers by using the "sysfs" filesystem interface. + +Every time a supported device is recognized, a write-only file named "green" is +created in the /sys/class/video4linux/videoX directory. You can set the green +channel's gain by writing the desired value to it. The value may range from 0 +to 15 for the SN9C101 or SN9C102 bridges, from 0 to 127 for the SN9C103, +SN9C105 and SN9C120 bridges. +Similarly, only for the SN9C103, SN9C105 and SN9C120 controllers, blue and red +gain control files are available in the same directory, for which accepted +values may range from 0 to 127. + +There are other four entries in the directory above for each registered camera: +"reg", "val", "i2c_reg" and "i2c_val". The first two files control the +SN9C1xx bridge, while the other two control the sensor chip. "reg" and +"i2c_reg" hold the values of the current register index where the following +reading/writing operations are addressed at through "val" and "i2c_val". Their +use is not intended for end-users. Note that "i2c_reg" and "i2c_val" will not +be created if the sensor does not actually support the standard I2C protocol or +its registers are not 8-bit long. Also, remember that you must be logged in as +root before writing to them. + +As an example, suppose we were to want to read the value contained in the +register number 1 of the sensor register table - which is usually the product +identifier - of the camera registered as "/dev/video0": + + [root@localhost #] cd /sys/class/video4linux/video0 + [root@localhost #] echo 1 > i2c_reg + [root@localhost #] cat i2c_val + +Note that "cat" will fail if sensor registers cannot be read. + +Now let's set the green gain's register of the SN9C101 or SN9C102 chips to 2: + + [root@localhost #] echo 0x11 > reg + [root@localhost #] echo 2 > val + +Note that the SN9C1xx always returns 0 when some of its registers are read. +To avoid race conditions, all the I/O accesses to the above files are +serialized. +The sysfs interface also provides the "frame_header" entry, which exports the +frame header of the most recent requested and captured video frame. The header +is always 18-bytes long and is appended to every video frame by the SN9C1xx +controllers. As an example, this additional information can be used by the user +application for implementing auto-exposure features via software. + +The following table describes the frame header exported by the SN9C101 and +SN9C102: + +Byte # Value or bits Description +------ ------------- ----------- +0x00 0xFF Frame synchronisation pattern +0x01 0xFF Frame synchronisation pattern +0x02 0x00 Frame synchronisation pattern +0x03 0xC4 Frame synchronisation pattern +0x04 0xC4 Frame synchronisation pattern +0x05 0x96 Frame synchronisation pattern +0x06 [3:0] Read channel gain control = (1+R_GAIN/8) + [7:4] Blue channel gain control = (1+B_GAIN/8) +0x07 [ 0 ] Compression mode. 0=No compression, 1=Compression enabled + [2:1] Maximum scale factor for compression + [ 3 ] 1 = USB fifo(2K bytes) is full + [ 4 ] 1 = Digital gain is finish + [ 5 ] 1 = Exposure is finish + [7:6] Frame index +0x08 [7:0] Y sum inside Auto-Exposure area (low-byte) +0x09 [7:0] Y sum inside Auto-Exposure area (high-byte) + where Y sum = (R/4 + 5G/16 + B/8) / 32 +0x0A [7:0] Y sum outside Auto-Exposure area (low-byte) +0x0B [7:0] Y sum outside Auto-Exposure area (high-byte) + where Y sum = (R/4 + 5G/16 + B/8) / 128 +0x0C 0xXX Not used +0x0D 0xXX Not used +0x0E 0xXX Not used +0x0F 0xXX Not used +0x10 0xXX Not used +0x11 0xXX Not used + +The following table describes the frame header exported by the SN9C103: + +Byte # Value or bits Description +------ ------------- ----------- +0x00 0xFF Frame synchronisation pattern +0x01 0xFF Frame synchronisation pattern +0x02 0x00 Frame synchronisation pattern +0x03 0xC4 Frame synchronisation pattern +0x04 0xC4 Frame synchronisation pattern +0x05 0x96 Frame synchronisation pattern +0x06 [6:0] Read channel gain control = (1/2+R_GAIN/64) +0x07 [6:0] Blue channel gain control = (1/2+B_GAIN/64) + [7:4] +0x08 [ 0 ] Compression mode. 0=No compression, 1=Compression enabled + [2:1] Maximum scale factor for compression + [ 3 ] 1 = USB fifo(2K bytes) is full + [ 4 ] 1 = Digital gain is finish + [ 5 ] 1 = Exposure is finish + [7:6] Frame index +0x09 [7:0] Y sum inside Auto-Exposure area (low-byte) +0x0A [7:0] Y sum inside Auto-Exposure area (high-byte) + where Y sum = (R/4 + 5G/16 + B/8) / 32 +0x0B [7:0] Y sum outside Auto-Exposure area (low-byte) +0x0C [7:0] Y sum outside Auto-Exposure area (high-byte) + where Y sum = (R/4 + 5G/16 + B/8) / 128 +0x0D [1:0] Audio frame number + [ 2 ] 1 = Audio is recording +0x0E [7:0] Audio summation (low-byte) +0x0F [7:0] Audio summation (high-byte) +0x10 [7:0] Audio sample count +0x11 [7:0] Audio peak data in audio frame + +The AE area (sx, sy, ex, ey) in the active window can be set by programming the +registers 0x1c, 0x1d, 0x1e and 0x1f of the SN9C1xx controllers, where one unit +corresponds to 32 pixels. + +[1] The frame headers exported by the SN9C105 and SN9C120 are not described. + + +9. Supported devices +==================== +None of the names of the companies as well as their products will be mentioned +here. They have never collaborated with the author, so no advertising. + +From the point of view of a driver, what unambiguously identify a device are +its vendor and product USB identifiers. Below is a list of known identifiers of +devices assembling the SN9C1xx PC camera controllers: + +Vendor ID Product ID +--------- ---------- +0x0458 0x7025 +0x045e 0x00f5 +0x045e 0x00f7 +0x0471 0x0327 +0x0471 0x0328 +0x0c45 0x6001 +0x0c45 0x6005 +0x0c45 0x6007 +0x0c45 0x6009 +0x0c45 0x600d +0x0c45 0x6011 +0x0c45 0x6019 +0x0c45 0x6024 +0x0c45 0x6025 +0x0c45 0x6028 +0x0c45 0x6029 +0x0c45 0x602a +0x0c45 0x602b +0x0c45 0x602c +0x0c45 0x602d +0x0c45 0x602e +0x0c45 0x6030 +0x0c45 0x603f +0x0c45 0x6080 +0x0c45 0x6082 +0x0c45 0x6083 +0x0c45 0x6088 +0x0c45 0x608a +0x0c45 0x608b +0x0c45 0x608c +0x0c45 0x608e +0x0c45 0x608f +0x0c45 0x60a0 +0x0c45 0x60a2 +0x0c45 0x60a3 +0x0c45 0x60a8 +0x0c45 0x60aa +0x0c45 0x60ab +0x0c45 0x60ac +0x0c45 0x60ae +0x0c45 0x60af +0x0c45 0x60b0 +0x0c45 0x60b2 +0x0c45 0x60b3 +0x0c45 0x60b8 +0x0c45 0x60ba +0x0c45 0x60bb +0x0c45 0x60bc +0x0c45 0x60be +0x0c45 0x60c0 +0x0c45 0x60c2 +0x0c45 0x60c8 +0x0c45 0x60cc +0x0c45 0x60ea +0x0c45 0x60ec +0x0c45 0x60ef +0x0c45 0x60fa +0x0c45 0x60fb +0x0c45 0x60fc +0x0c45 0x60fe +0x0c45 0x6102 +0x0c45 0x6108 +0x0c45 0x610f +0x0c45 0x6130 +0x0c45 0x6138 +0x0c45 0x613a +0x0c45 0x613b +0x0c45 0x613c +0x0c45 0x613e + +The list above does not imply that all those devices work with this driver: up +until now only the ones that assemble the following pairs of SN9C1xx bridges +and image sensors are supported; kernel messages will always tell you whether +this is the case (see "Module loading" paragraph): + +Image sensor / SN9C1xx bridge | SN9C10[12] SN9C103 SN9C105 SN9C120 +------------------------------------------------------------------------------- +HV7131D Hynix Semiconductor | Yes No No No +HV7131R Hynix Semiconductor | No Yes Yes Yes +MI-0343 Micron Technology | Yes No No No +MI-0360 Micron Technology | No Yes Yes Yes +OV7630 OmniVision Technologies | Yes Yes Yes Yes +OV7660 OmniVision Technologies | No No Yes Yes +PAS106B PixArt Imaging | Yes No No No +PAS202B PixArt Imaging | Yes Yes No No +TAS5110C1B Taiwan Advanced Sensor | Yes No No No +TAS5110D Taiwan Advanced Sensor | Yes No No No +TAS5130D1B Taiwan Advanced Sensor | Yes No No No + +"Yes" means that the pair is supported by the driver, while "No" means that the +pair does not exist or is not supported by the driver. + +Only some of the available control settings of each image sensor are supported +through the V4L2 interface. + +Donations of new models for further testing and support would be much +appreciated. Non-available hardware will not be supported by the author of this +driver. + + +10. Notes for V4L2 application developers +========================================= +This driver follows the V4L2 API specifications. In particular, it enforces two +rules: + +- exactly one I/O method, either "mmap" or "read", is associated with each +file descriptor. Once it is selected, the application must close and reopen the +device to switch to the other I/O method; + +- although it is not mandatory, previously mapped buffer memory should always +be unmapped before calling any "VIDIOC_S_CROP" or "VIDIOC_S_FMT" ioctl's. +The same number of buffers as before will be allocated again to match the size +of the new video frames, so you have to map the buffers again before any I/O +attempts on them. + +Consistently with the hardware limits, this driver also supports image +downscaling with arbitrary scaling factors from 1, 2 and 4 in both directions. +However, the V4L2 API specifications don't correctly define how the scaling +factor can be chosen arbitrarily by the "negotiation" of the "source" and +"target" rectangles. To work around this flaw, we have added the convention +that, during the negotiation, whenever the "VIDIOC_S_CROP" ioctl is issued, the +scaling factor is restored to 1. + +This driver supports two different video formats: the first one is the "8-bit +Sequential Bayer" format and can be used to obtain uncompressed video data +from the device through the current I/O method, while the second one provides +either "raw" compressed video data (without frame headers not related to the +compressed data) or standard JPEG (with frame headers). The compression quality +may vary from 0 to 1 and can be selected or queried thanks to the +VIDIOC_S_JPEGCOMP and VIDIOC_G_JPEGCOMP V4L2 ioctl's. For maximum flexibility, +both the default active video format and the default compression quality +depend on how the image sensor being used is initialized. + + +11. Video frame formats [1] +======================= +The SN9C1xx PC Camera Controllers can send images in two possible video +formats over the USB: either native "Sequential RGB Bayer" or compressed. +The compression is used to achieve high frame rates. With regard to the +SN9C101, SN9C102 and SN9C103, the compression is based on the Huffman encoding +algorithm described below, while with regard to the SN9C105 and SN9C120 the +compression is based on the JPEG standard. +The current video format may be selected or queried from the user application +by calling the VIDIOC_S_FMT or VIDIOC_G_FMT ioctl's, as described in the V4L2 +API specifications. + +The name "Sequential Bayer" indicates the organization of the red, green and +blue pixels in one video frame. Each pixel is associated with a 8-bit long +value and is disposed in memory according to the pattern shown below: + +B[0] G[1] B[2] G[3] ... B[m-2] G[m-1] +G[m] R[m+1] G[m+2] R[m+2] ... G[2m-2] R[2m-1] +... +... B[(n-1)(m-2)] G[(n-1)(m-1)] +... G[n(m-2)] R[n(m-1)] + +The above matrix also represents the sequential or progressive read-out mode of +the (n, m) Bayer color filter array used in many CCD or CMOS image sensors. + +The Huffman compressed video frame consists of a bitstream that encodes for +every R, G, or B pixel the difference between the value of the pixel itself and +some reference pixel value. Pixels are organised in the Bayer pattern and the +Bayer sub-pixels are tracked individually and alternatingly. For example, in +the first line values for the B and G1 pixels are alternatingly encoded, while +in the second line values for the G2 and R pixels are alternatingly encoded. + +The pixel reference value is calculated as follows: +- the 4 top left pixels are encoded in raw uncompressed 8-bit format; +- the value in the top two rows is the value of the pixel left of the current + pixel; +- the value in the left column is the value of the pixel above the current + pixel; +- for all other pixels, the reference value is the average of the value of the + pixel on the left and the value of the pixel above the current pixel; +- there is one code in the bitstream that specifies the value of a pixel + directly (in 4-bit resolution); +- pixel values need to be clamped inside the range [0..255] for proper + decoding. + +The algorithm purely describes the conversion from compressed Bayer code used +in the SN9C101, SN9C102 and SN9C103 chips to uncompressed Bayer. Additional +steps are required to convert this to a color image (i.e. a color interpolation +algorithm). + +The following Huffman codes have been found: +0: +0 (relative to reference pixel value) +100: +4 +101: -4? +1110xxxx: set absolute value to xxxx.0000 +1101: +11 +1111: -11 +11001: +20 +110000: -20 +110001: ??? - these codes are apparently not used + +[1] The Huffman compression algorithm has been reverse-engineered and + documented by Bertrik Sikken. + + +12. Contact information +======================= +The author may be contacted by e-mail at . + +GPG/PGP encrypted e-mail's are accepted. The GPG key ID of the author is +'FCE635A4'; the public 1024-bit key should be available at any keyserver; +the fingerprint is: '88E8 F32F 7244 68BA 3958 5D40 99DA 5D2A FCE6 35A4'. + + +13. Credits +=========== +Many thanks to following persons for their contribute (listed in alphabetical +order): + +- David Anderson for the donation of a webcam; +- Luca Capello for the donation of a webcam; +- Philippe Coval for having helped testing the PAS202BCA image sensor; +- Joao Rodrigo Fuzaro, Joao Limirio, Claudio Filho and Caio Begotti for the + donation of a webcam; +- Dennis Heitmann for the donation of a webcam; +- Jon Hollstrom for the donation of a webcam; +- Nick McGill for the donation of a webcam; +- Carlos Eduardo Medaglia Dyonisio, who added the support for the PAS202BCB + image sensor; +- Stefano Mozzi, who donated 45 EU; +- Andrew Pearce for the donation of a webcam; +- John Pullan for the donation of a webcam; +- Bertrik Sikken, who reverse-engineered and documented the Huffman compression + algorithm used in the SN9C101, SN9C102 and SN9C103 controllers and + implemented the first decoder; +- Ronny Standke for the donation of a webcam; +- Mizuno Takafumi for the donation of a webcam; +- an "anonymous" donator (who didn't want his name to be revealed) for the + donation of a webcam. +- an anonymous donator for the donation of four webcams and two boards with ten + image sensors. diff --git a/drivers/staging/media/sn9c102/sn9c102_config.h b/drivers/staging/media/sn9c102/sn9c102_config.h new file mode 100644 index 000000000000..0f4e0378b071 --- /dev/null +++ b/drivers/staging/media/sn9c102/sn9c102_config.h @@ -0,0 +1,86 @@ +/*************************************************************************** + * Global parameters for the V4L2 driver for SN9C1xx PC Camera Controllers * + * * + * Copyright (C) 2007 by Luca Risolia * + * * + * 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 _SN9C102_CONFIG_H_ +#define _SN9C102_CONFIG_H_ + +#include +#include + +#define SN9C102_DEBUG +#define SN9C102_DEBUG_LEVEL 2 +#define SN9C102_MAX_DEVICES 64 +#define SN9C102_PRESERVE_IMGSCALE 0 +#define SN9C102_FORCE_MUNMAP 0 +#define SN9C102_MAX_FRAMES 32 +#define SN9C102_URBS 2 +#define SN9C102_ISO_PACKETS 7 +#define SN9C102_ALTERNATE_SETTING 8 +#define SN9C102_URB_TIMEOUT msecs_to_jiffies(2 * SN9C102_ISO_PACKETS) +#define SN9C102_CTRL_TIMEOUT 300 +#define SN9C102_FRAME_TIMEOUT 0 + +/*****************************************************************************/ + +static const u8 SN9C102_Y_QTABLE0[64] = { + 8, 5, 5, 8, 12, 20, 25, 30, + 6, 6, 7, 9, 13, 29, 30, 27, + 7, 6, 8, 12, 20, 28, 34, 28, + 7, 8, 11, 14, 25, 43, 40, 31, + 9, 11, 18, 28, 34, 54, 51, 38, + 12, 17, 27, 32, 40, 52, 56, 46, + 24, 32, 39, 43, 51, 60, 60, 50, + 36, 46, 47, 49, 56, 50, 51, 49 +}; + +static const u8 SN9C102_UV_QTABLE0[64] = { + 8, 9, 12, 23, 49, 49, 49, 49, + 9, 10, 13, 33, 49, 49, 49, 49, + 12, 13, 28, 49, 49, 49, 49, 49, + 23, 33, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49 +}; + +static const u8 SN9C102_Y_QTABLE1[64] = { + 16, 11, 10, 16, 24, 40, 51, 61, + 12, 12, 14, 19, 26, 58, 60, 55, + 14, 13, 16, 24, 40, 57, 69, 56, + 14, 17, 22, 29, 51, 87, 80, 62, + 18, 22, 37, 56, 68, 109, 103, 77, + 24, 35, 55, 64, 81, 104, 113, 92, + 49, 64, 78, 87, 103, 121, 120, 101, + 72, 92, 95, 98, 112, 100, 103, 99 +}; + +static const u8 SN9C102_UV_QTABLE1[64] = { + 17, 18, 24, 47, 99, 99, 99, 99, + 18, 21, 26, 66, 99, 99, 99, 99, + 24, 26, 56, 99, 99, 99, 99, 99, + 47, 66, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99 +}; + +#endif /* _SN9C102_CONFIG_H_ */ diff --git a/drivers/staging/media/sn9c102/sn9c102_core.c b/drivers/staging/media/sn9c102/sn9c102_core.c new file mode 100644 index 000000000000..2cb44de2b92c --- /dev/null +++ b/drivers/staging/media/sn9c102/sn9c102_core.c @@ -0,0 +1,3434 @@ +/*************************************************************************** + * V4L2 driver for SN9C1xx PC Camera Controllers * + * * + * Copyright (C) 2004-2007 by Luca Risolia * + * * + * 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. * + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sn9c102.h" + +/*****************************************************************************/ + +#define SN9C102_MODULE_NAME "V4L2 driver for SN9C1xx PC Camera Controllers" +#define SN9C102_MODULE_ALIAS "sn9c1xx" +#define SN9C102_MODULE_AUTHOR "(C) 2004-2007 Luca Risolia" +#define SN9C102_AUTHOR_EMAIL "" +#define SN9C102_MODULE_LICENSE "GPL" +#define SN9C102_MODULE_VERSION "1:1.48" + +/*****************************************************************************/ + +MODULE_DEVICE_TABLE(usb, sn9c102_id_table); + +MODULE_AUTHOR(SN9C102_MODULE_AUTHOR " " SN9C102_AUTHOR_EMAIL); +MODULE_DESCRIPTION(SN9C102_MODULE_NAME); +MODULE_ALIAS(SN9C102_MODULE_ALIAS); +MODULE_VERSION(SN9C102_MODULE_VERSION); +MODULE_LICENSE(SN9C102_MODULE_LICENSE); + +static short video_nr[] = {[0 ... SN9C102_MAX_DEVICES-1] = -1}; +module_param_array(video_nr, short, NULL, 0444); +MODULE_PARM_DESC(video_nr, + " <-1|n[,...]>" + "\nSpecify 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(SN9C102_MAX_DEVICES) + " cameras this way." + "\nFor example:" + "\nvideo_nr=-1,2,-1 would assign minor number 2 to" + "\nthe second camera and use auto for the first" + "\none and for every other camera." + "\n"); + +static bool force_munmap[] = {[0 ... SN9C102_MAX_DEVICES-1] = + SN9C102_FORCE_MUNMAP}; +module_param_array(force_munmap, bool, NULL, 0444); +MODULE_PARM_DESC(force_munmap, + " <0|1[,...]>" + "\nForce 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." + "\n0 = do not force memory unmapping" + "\n1 = force memory unmapping (save memory)" + "\nDefault value is "__MODULE_STRING(SN9C102_FORCE_MUNMAP)"." + "\n"); + +static unsigned int frame_timeout[] = {[0 ... SN9C102_MAX_DEVICES-1] = + SN9C102_FRAME_TIMEOUT}; +module_param_array(frame_timeout, uint, NULL, 0644); +MODULE_PARM_DESC(frame_timeout, + " <0|n[,...]>" + "\nTimeout for a video frame in seconds before" + "\nreturning an I/O error; 0 for infinity." + "\nThis parameter is specific for each detected camera." + "\nDefault value is "__MODULE_STRING(SN9C102_FRAME_TIMEOUT)"." + "\n"); + +#ifdef SN9C102_DEBUG +static unsigned short debug = SN9C102_DEBUG_LEVEL; +module_param(debug, ushort, 0644); +MODULE_PARM_DESC(debug, + " " + "\nDebugging 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." + "\nDefault value is "__MODULE_STRING(SN9C102_DEBUG_LEVEL)"." + "\n"); +#endif + +/* + Add the probe entries to this table. Be sure to add the entry in the right + place, since, on failure, the next probing routine is called according to + the order of the list below, from top to bottom. +*/ +static int (*sn9c102_sensor_table[])(struct sn9c102_device *) = { + &sn9c102_probe_hv7131d, /* strong detection based on SENSOR ids */ + &sn9c102_probe_hv7131r, /* strong detection based on SENSOR ids */ + &sn9c102_probe_mi0343, /* strong detection based on SENSOR ids */ + &sn9c102_probe_mi0360, /* strong detection based on SENSOR ids */ + &sn9c102_probe_mt9v111, /* strong detection based on SENSOR ids */ + &sn9c102_probe_pas106b, /* strong detection based on SENSOR ids */ + &sn9c102_probe_pas202bcb, /* strong detection based on SENSOR ids */ + &sn9c102_probe_ov7630, /* strong detection based on SENSOR ids */ + &sn9c102_probe_ov7660, /* strong detection based on SENSOR ids */ + &sn9c102_probe_tas5110c1b, /* detection based on USB pid/vid */ + &sn9c102_probe_tas5110d, /* detection based on USB pid/vid */ + &sn9c102_probe_tas5130d1b, /* detection based on USB pid/vid */ +}; + +/*****************************************************************************/ + +static u32 +sn9c102_request_buffers(struct sn9c102_device* cam, u32 count, + enum sn9c102_io_method io) +{ + struct v4l2_pix_format* p = &(cam->sensor.pix_format); + struct v4l2_rect* r = &(cam->sensor.cropcap.bounds); + 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 > SN9C102_MAX_FRAMES) + count = SN9C102_MAX_FRAMES; + + if (cam->bridge == BRIDGE_SN9C105 || cam->bridge == BRIDGE_SN9C120) + imagesize += 589 + 2; /* length of JPEG header + EOI marker */ + + 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 = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + } + + return cam->nbuffers; +} + + +static void sn9c102_release_buffers(struct sn9c102_device* cam) +{ + if (cam->nbuffers) { + vfree(cam->frame[0].bufmem); + cam->nbuffers = 0; + } + cam->frame_current = NULL; +} + + +static void sn9c102_empty_framequeues(struct sn9c102_device* cam) +{ + u32 i; + + INIT_LIST_HEAD(&cam->inqueue); + INIT_LIST_HEAD(&cam->outqueue); + + for (i = 0; i < SN9C102_MAX_FRAMES; i++) { + cam->frame[i].state = F_UNUSED; + cam->frame[i].buf.bytesused = 0; + } +} + + +static void sn9c102_requeue_outqueue(struct sn9c102_device* cam) +{ + struct sn9c102_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 sn9c102_queue_unusedframes(struct sn9c102_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); + } +} + +/*****************************************************************************/ + +/* + Write a sequence of count value/register pairs. Returns -1 after the first + failed write, or 0 for no errors. +*/ +int sn9c102_write_regs(struct sn9c102_device* cam, const u8 valreg[][2], + int count) +{ + struct usb_device* udev = cam->usbdev; + u8* buff = cam->control_buffer; + int i, res; + + for (i = 0; i < count; i++) { + u8 index = valreg[i][1]; + + /* + index is a u8, so it must be <256 and can't be out of range. + If we put in a check anyway, gcc annoys us with a warning + hat our check is useless. People get all uppity when they + see warnings in the kernel compile. + */ + + *buff = valreg[i][0]; + + res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, + 0x41, index, 0, buff, 1, + SN9C102_CTRL_TIMEOUT); + + if (res < 0) { + DBG(3, "Failed to write a register (value 0x%02X, " + "index 0x%02X, error %d)", *buff, index, res); + return -1; + } + + cam->reg[index] = *buff; + } + + return 0; +} + + +int sn9c102_write_reg(struct sn9c102_device* cam, u8 value, u16 index) +{ + struct usb_device* udev = cam->usbdev; + u8* buff = cam->control_buffer; + int res; + + if (index >= ARRAY_SIZE(cam->reg)) + return -1; + + *buff = value; + + res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41, + index, 0, buff, 1, SN9C102_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; + } + + cam->reg[index] = value; + + return 0; +} + + +/* NOTE: with the SN9C10[123] reading some registers always returns 0 */ +int sn9c102_read_reg(struct sn9c102_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, + index, 0, buff, 1, SN9C102_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; +} + + +int sn9c102_pread_reg(struct sn9c102_device* cam, u16 index) +{ + if (index >= ARRAY_SIZE(cam->reg)) + return -1; + + return cam->reg[index]; +} + + +static int +sn9c102_i2c_wait(struct sn9c102_device* cam, + const struct sn9c102_sensor* sensor) +{ + int i, r; + + for (i = 1; i <= 5; i++) { + r = sn9c102_read_reg(cam, 0x08); + if (r < 0) + return -EIO; + if (r & 0x04) + return 0; + if (sensor->frequency & SN9C102_I2C_400KHZ) + udelay(5*16); + else + udelay(16*16); + } + return -EBUSY; +} + + +static int +sn9c102_i2c_detect_read_error(struct sn9c102_device* cam, + const struct sn9c102_sensor* sensor) +{ + int r , err = 0; + + r = sn9c102_read_reg(cam, 0x08); + if (r < 0) + err += r; + + if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102) { + if (!(r & 0x08)) + err += -1; + } else { + if (r & 0x08) + err += -1; + } + + return err ? -EIO : 0; +} + + +static int +sn9c102_i2c_detect_write_error(struct sn9c102_device* cam, + const struct sn9c102_sensor* sensor) +{ + int r; + r = sn9c102_read_reg(cam, 0x08); + return (r < 0 || (r >= 0 && (r & 0x08))) ? -EIO : 0; +} + + +int +sn9c102_i2c_try_raw_read(struct sn9c102_device* cam, + const struct sn9c102_sensor* sensor, u8 data0, + u8 data1, u8 n, u8 buffer[]) +{ + struct usb_device* udev = cam->usbdev; + u8* data = cam->control_buffer; + int i = 0, err = 0, res; + + /* Write cycle */ + data[0] = ((sensor->interface == SN9C102_I2C_2WIRES) ? 0x80 : 0) | + ((sensor->frequency & SN9C102_I2C_400KHZ) ? 0x01 : 0) | 0x10; + data[1] = data0; /* I2C slave id */ + data[2] = data1; /* address */ + data[7] = 0x10; + res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41, + 0x08, 0, data, 8, SN9C102_CTRL_TIMEOUT); + if (res < 0) + err += res; + + err += sn9c102_i2c_wait(cam, sensor); + + /* Read cycle - n bytes */ + data[0] = ((sensor->interface == SN9C102_I2C_2WIRES) ? 0x80 : 0) | + ((sensor->frequency & SN9C102_I2C_400KHZ) ? 0x01 : 0) | + (n << 4) | 0x02; + data[1] = data0; + data[7] = 0x10; + res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41, + 0x08, 0, data, 8, SN9C102_CTRL_TIMEOUT); + if (res < 0) + err += res; + + err += sn9c102_i2c_wait(cam, sensor); + + /* The first read byte will be placed in data[4] */ + res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, 0xc1, + 0x0a, 0, data, 5, SN9C102_CTRL_TIMEOUT); + if (res < 0) + err += res; + + err += sn9c102_i2c_detect_read_error(cam, sensor); + + PDBGG("I2C read: address 0x%02X, first read byte: 0x%02X", data1, + data[4]); + + if (err) { + DBG(3, "I2C read failed for %s image sensor", sensor->name); + return -1; + } + + if (buffer) + for (i = 0; i < n && i < 5; i++) + buffer[n-i-1] = data[4-i]; + + return (int)data[4]; +} + + +int +sn9c102_i2c_try_raw_write(struct sn9c102_device* cam, + const struct sn9c102_sensor* sensor, u8 n, u8 data0, + u8 data1, u8 data2, u8 data3, u8 data4, u8 data5) +{ + struct usb_device* udev = cam->usbdev; + u8* data = cam->control_buffer; + int err = 0, res; + + /* Write cycle. It usually is address + value */ + data[0] = ((sensor->interface == SN9C102_I2C_2WIRES) ? 0x80 : 0) | + ((sensor->frequency & SN9C102_I2C_400KHZ) ? 0x01 : 0) + | ((n - 1) << 4); + data[1] = data0; + data[2] = data1; + data[3] = data2; + data[4] = data3; + data[5] = data4; + data[6] = data5; + data[7] = 0x17; + res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41, + 0x08, 0, data, 8, SN9C102_CTRL_TIMEOUT); + if (res < 0) + err += res; + + err += sn9c102_i2c_wait(cam, sensor); + err += sn9c102_i2c_detect_write_error(cam, sensor); + + if (err) + DBG(3, "I2C write failed for %s image sensor", sensor->name); + + PDBGG("I2C raw write: %u bytes, data0 = 0x%02X, data1 = 0x%02X, " + "data2 = 0x%02X, data3 = 0x%02X, data4 = 0x%02X, data5 = 0x%02X", + n, data0, data1, data2, data3, data4, data5); + + return err ? -1 : 0; +} + + +int +sn9c102_i2c_try_read(struct sn9c102_device* cam, + const struct sn9c102_sensor* sensor, u8 address) +{ + return sn9c102_i2c_try_raw_read(cam, sensor, sensor->i2c_slave_id, + address, 1, NULL); +} + + +static int sn9c102_i2c_try_write(struct sn9c102_device* cam, + const struct sn9c102_sensor* sensor, + u8 address, u8 value) +{ + return sn9c102_i2c_try_raw_write(cam, sensor, 3, + sensor->i2c_slave_id, address, + value, 0, 0, 0); +} + + +int sn9c102_i2c_read(struct sn9c102_device* cam, u8 address) +{ + return sn9c102_i2c_try_read(cam, &cam->sensor, address); +} + + +int sn9c102_i2c_write(struct sn9c102_device* cam, u8 address, u8 value) +{ + return sn9c102_i2c_try_write(cam, &cam->sensor, address, value); +} + +/*****************************************************************************/ + +static size_t sn9c102_sof_length(struct sn9c102_device* cam) +{ + switch (cam->bridge) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + return 12; + case BRIDGE_SN9C103: + return 18; + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + return 62; + } + + return 0; +} + + +static void* +sn9c102_find_sof_header(struct sn9c102_device* cam, void* mem, size_t len) +{ + static const char marker[6] = {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96}; + const char *m = mem; + size_t soflen = 0, i, j; + + soflen = sn9c102_sof_length(cam); + + for (i = 0; i < len; i++) { + size_t b; + + /* Read the variable part of the header */ + if (unlikely(cam->sof.bytesread >= sizeof(marker))) { + cam->sof.header[cam->sof.bytesread] = *(m+i); + if (++cam->sof.bytesread == soflen) { + cam->sof.bytesread = 0; + return mem + i; + } + continue; + } + + /* Search for the SOF marker (fixed part) in the header */ + for (j = 0, b=cam->sof.bytesread; j+b < sizeof(marker); j++) { + if (unlikely(i+j == len)) + return NULL; + if (*(m+i+j) == marker[cam->sof.bytesread]) { + cam->sof.header[cam->sof.bytesread] = *(m+i+j); + if (++cam->sof.bytesread == sizeof(marker)) { + PDBGG("Bytes to analyze: %zd. SOF " + "starts at byte #%zd", len, i); + i += j+1; + break; + } + } else { + cam->sof.bytesread = 0; + break; + } + } + } + + return NULL; +} + + +static void* +sn9c102_find_eof_header(struct sn9c102_device* cam, void* mem, size_t len) +{ + static const u8 eof_header[4][4] = { + {0x00, 0x00, 0x00, 0x00}, + {0x40, 0x00, 0x00, 0x00}, + {0x80, 0x00, 0x00, 0x00}, + {0xc0, 0x00, 0x00, 0x00}, + }; + size_t i, j; + + /* The EOF header does not exist in compressed data */ + if (cam->sensor.pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X || + cam->sensor.pix_format.pixelformat == V4L2_PIX_FMT_JPEG) + return NULL; + + /* + The EOF header might cross the packet boundary, but this is not a + problem, since the end of a frame is determined by checking its size + in the first place. + */ + for (i = 0; (len >= 4) && (i <= len - 4); i++) + for (j = 0; j < ARRAY_SIZE(eof_header); j++) + if (!memcmp(mem + i, eof_header[j], 4)) + return mem + i; + + return NULL; +} + + +static void +sn9c102_write_jpegheader(struct sn9c102_device* cam, struct sn9c102_frame_t* f) +{ + static const u8 jpeg_header[589] = { + 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x06, 0x04, 0x05, + 0x06, 0x05, 0x04, 0x06, 0x06, 0x05, 0x06, 0x07, 0x07, 0x06, + 0x08, 0x0a, 0x10, 0x0a, 0x0a, 0x09, 0x09, 0x0a, 0x14, 0x0e, + 0x0f, 0x0c, 0x10, 0x17, 0x14, 0x18, 0x18, 0x17, 0x14, 0x16, + 0x16, 0x1a, 0x1d, 0x25, 0x1f, 0x1a, 0x1b, 0x23, 0x1c, 0x16, + 0x16, 0x20, 0x2c, 0x20, 0x23, 0x26, 0x27, 0x29, 0x2a, 0x29, + 0x19, 0x1f, 0x2d, 0x30, 0x2d, 0x28, 0x30, 0x25, 0x28, 0x29, + 0x28, 0x01, 0x07, 0x07, 0x07, 0x0a, 0x08, 0x0a, 0x13, 0x0a, + 0x0a, 0x13, 0x28, 0x1a, 0x16, 0x1a, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0xff, 0xc4, 0x01, 0xa2, + 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, + 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x01, + 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, + 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x10, 0x00, + 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, + 0x04, 0x00, 0x00, 0x01, 0x7d, 0x01, 0x02, 0x03, 0x00, 0x04, + 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, + 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23, + 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, + 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25, + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, + 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, + 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, + 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, + 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, + 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, + 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, + 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, + 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, + 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, 0xe3, + 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, + 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0x11, 0x00, 0x02, + 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, + 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, + 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, + 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1, + 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1, + 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, + 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, + 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, + 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, + 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, + 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, + 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, + 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, + 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, + 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2, 0xe3, + 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, + 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 0xc0, 0x00, 0x11, + 0x08, 0x01, 0xe0, 0x02, 0x80, 0x03, 0x01, 0x21, 0x00, 0x02, + 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xda, 0x00, 0x0c, 0x03, + 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00 + }; + u8 *pos = f->bufmem; + + memcpy(pos, jpeg_header, sizeof(jpeg_header)); + *(pos + 6) = 0x00; + *(pos + 7 + 64) = 0x01; + if (cam->compression.quality == 0) { + memcpy(pos + 7, SN9C102_Y_QTABLE0, 64); + memcpy(pos + 8 + 64, SN9C102_UV_QTABLE0, 64); + } else if (cam->compression.quality == 1) { + memcpy(pos + 7, SN9C102_Y_QTABLE1, 64); + memcpy(pos + 8 + 64, SN9C102_UV_QTABLE1, 64); + } + *(pos + 564) = cam->sensor.pix_format.width & 0xFF; + *(pos + 563) = (cam->sensor.pix_format.width >> 8) & 0xFF; + *(pos + 562) = cam->sensor.pix_format.height & 0xFF; + *(pos + 561) = (cam->sensor.pix_format.height >> 8) & 0xFF; + *(pos + 567) = 0x21; + + f->buf.bytesused += sizeof(jpeg_header); +} + + +static void sn9c102_urb_complete(struct urb *urb) +{ + struct sn9c102_device* cam = urb->context; + struct sn9c102_frame_t** f; + size_t imagesize, soflen; + 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; + cam->sof.bytesread = 0; + DBG(3, "Stream interrupted by application"); + 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 sn9c102_frame_t, + frame); + + imagesize = (cam->sensor.pix_format.width * + cam->sensor.pix_format.height * + cam->sensor.pix_format.priv) / 8; + if (cam->sensor.pix_format.pixelformat == V4L2_PIX_FMT_JPEG) + imagesize += 589; /* length of jpeg header */ + soflen = sn9c102_sof_length(cam); + + for (i = 0; i < urb->number_of_packets; i++) { + unsigned int img, len, status; + void *pos, *sof, *eof; + + 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; + cam->sof.bytesread = 0; + continue; + } + + PDBGG("Isochrnous frame: length %u, #%u i", len, i); + +redo: + sof = sn9c102_find_sof_header(cam, pos, len); + if (likely(!sof)) { + eof = sn9c102_find_eof_header(cam, pos, len); + if ((*f)->state == F_GRABBING) { +end_of_frame: + img = len; + + if (eof) + img = (eof > pos) ? eof - pos - 1 : 0; + + if ((*f)->buf.bytesused + img > imagesize) { + u32 b; + b = (*f)->buf.bytesused + img - + imagesize; + img = imagesize - (*f)->buf.bytesused; + PDBGG("Expected EOF not found: video " + "frame cut"); + if (eof) + DBG(3, "Exceeded limit: +%u " + "bytes", (unsigned)(b)); + } + + memcpy((*f)->bufmem + (*f)->buf.bytesused, pos, + img); + + if ((*f)->buf.bytesused == 0) + v4l2_get_timestamp( + &(*f)->buf.timestamp); + + (*f)->buf.bytesused += img; + + if ((*f)->buf.bytesused == imagesize || + ((cam->sensor.pix_format.pixelformat == + V4L2_PIX_FMT_SN9C10X || + cam->sensor.pix_format.pixelformat == + V4L2_PIX_FMT_JPEG) && eof)) { + u32 b; + + 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 sn9c102_frame_t, + frame ); + else + (*f) = NULL; + spin_unlock(&cam->queue_lock); + + memcpy(cam->sysfs.frame_header, + cam->sof.header, soflen); + + DBG(3, "Video frame captured: %lu " + "bytes", (unsigned long)(b)); + + if (!(*f)) + goto resubmit_urb; + + } else if (eof) { + (*f)->state = F_ERROR; + DBG(3, "Not expected EOF after %lu " + "bytes of image data", + (unsigned long) + ((*f)->buf.bytesused)); + } + + if (sof) /* (1) */ + goto start_of_frame; + + } else if (eof) { + DBG(3, "EOF without SOF"); + continue; + + } else { + PDBGG("Ignoring pointless isochronous frame"); + continue; + } + + } else if ((*f)->state == F_QUEUED || (*f)->state == F_ERROR) { +start_of_frame: + (*f)->state = F_GRABBING; + (*f)->buf.bytesused = 0; + len -= (sof - pos); + pos = sof; + if (cam->sensor.pix_format.pixelformat == + V4L2_PIX_FMT_JPEG) + sn9c102_write_jpegheader(cam, (*f)); + DBG(3, "SOF detected: new video frame"); + if (len) + goto redo; + + } else if ((*f)->state == F_GRABBING) { + eof = sn9c102_find_eof_header(cam, pos, len); + if (eof && eof < sof) + goto end_of_frame; /* (1) */ + else { + if (cam->sensor.pix_format.pixelformat == + V4L2_PIX_FMT_SN9C10X || + cam->sensor.pix_format.pixelformat == + V4L2_PIX_FMT_JPEG) { + if (sof - pos >= soflen) { + eof = sof - soflen; + } else { /* remove header */ + eof = pos; + (*f)->buf.bytesused -= + (soflen - (sof - pos)); + } + goto end_of_frame; + } else { + DBG(3, "SOF before expected EOF after " + "%lu bytes of image data", + (unsigned long) + ((*f)->buf.bytesused)); + 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 sn9c102_start_transfer(struct sn9c102_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), + SN9C102_ALTERNATE_SETTING); + const unsigned int psz = le16_to_cpu(altsetting-> + endpoint[0].desc.wMaxPacketSize); + s8 i, j; + int err = 0; + + for (i = 0; i < SN9C102_URBS; i++) { + cam->transfer_buffer[i] = kzalloc(SN9C102_ISO_PACKETS * psz, + GFP_KERNEL); + if (!cam->transfer_buffer[i]) { + err = -ENOMEM; + DBG(1, "Not enough memory"); + goto free_buffers; + } + } + + for (i = 0; i < SN9C102_URBS; i++) { + urb = usb_alloc_urb(SN9C102_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 = SN9C102_ISO_PACKETS; + urb->complete = sn9c102_urb_complete; + urb->transfer_buffer = cam->transfer_buffer[i]; + urb->transfer_buffer_length = psz * SN9C102_ISO_PACKETS; + urb->interval = 1; + for (j = 0; j < SN9C102_ISO_PACKETS; j++) { + urb->iso_frame_desc[j].offset = psz * j; + urb->iso_frame_desc[j].length = psz; + } + } + + /* Enable video */ + if (!(cam->reg[0x01] & 0x04)) { + err = sn9c102_write_reg(cam, cam->reg[0x01] | 0x04, 0x01); + if (err) { + err = -EIO; + DBG(1, "I/O hardware error"); + goto free_urbs; + } + } + + err = usb_set_interface(udev, 0, SN9C102_ALTERNATE_SETTING); + if (err) { + DBG(1, "usb_set_interface() failed"); + goto free_urbs; + } + + cam->frame_current = NULL; + cam->sof.bytesread = 0; + + for (i = 0; i < SN9C102_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 < SN9C102_URBS) && cam->urb[i]; i++) + usb_free_urb(cam->urb[i]); + +free_buffers: + for (i = 0; (i < SN9C102_URBS) && cam->transfer_buffer[i]; i++) + kfree(cam->transfer_buffer[i]); + + return err; +} + + +static int sn9c102_stop_transfer(struct sn9c102_device* cam) +{ + struct usb_device *udev = cam->usbdev; + s8 i; + int err = 0; + + if (cam->state & DEV_DISCONNECTED) + return 0; + + for (i = SN9C102_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 sn9c102_stream_interrupt(struct sn9c102_device* cam) +{ + cam->stream = STREAM_INTERRUPT; + wait_event_timeout(cam->wait_stream, + (cam->stream == STREAM_OFF) || + (cam->state & DEV_DISCONNECTED), + SN9C102_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 u16 sn9c102_strtou16(const char* buff, size_t len, ssize_t* count) +{ + char str[7]; + char* endp; + unsigned long val; + + if (len < 6) { + strncpy(str, buff, len); + str[len] = '\0'; + } else { + strncpy(str, buff, 6); + str[6] = '\0'; + } + + val = simple_strtoul(str, &endp, 0); + + *count = 0; + if (val <= 0xffff) + *count = (ssize_t)(endp - str); + if ((*count) && (len == *count+1) && (buff[*count] == '\n')) + *count += 1; + + return (u16)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 sn9c102_show_reg(struct device* cd, + struct device_attribute *attr, char* buf) +{ + struct sn9c102_device* cam; + ssize_t count; + + if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(container_of(cd, struct video_device, dev)); + if (!cam) { + mutex_unlock(&sn9c102_sysfs_lock); + return -ENODEV; + } + + count = sprintf(buf, "%u\n", cam->sysfs.reg); + + mutex_unlock(&sn9c102_sysfs_lock); + + return count; +} + + +static ssize_t +sn9c102_store_reg(struct device* cd, struct device_attribute *attr, + const char* buf, size_t len) +{ + struct sn9c102_device* cam; + u16 index; + ssize_t count; + + if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(container_of(cd, struct video_device, dev)); + if (!cam) { + mutex_unlock(&sn9c102_sysfs_lock); + return -ENODEV; + } + + index = sn9c102_strtou16(buf, len, &count); + if (index >= ARRAY_SIZE(cam->reg) || !count) { + mutex_unlock(&sn9c102_sysfs_lock); + return -EINVAL; + } + + cam->sysfs.reg = index; + + DBG(2, "Moved SN9C1XX register index to 0x%02X", cam->sysfs.reg); + DBG(3, "Written bytes: %zd", count); + + mutex_unlock(&sn9c102_sysfs_lock); + + return count; +} + + +static ssize_t sn9c102_show_val(struct device* cd, + struct device_attribute *attr, char* buf) +{ + struct sn9c102_device* cam; + ssize_t count; + int val; + + if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(container_of(cd, struct video_device, dev)); + if (!cam) { + mutex_unlock(&sn9c102_sysfs_lock); + return -ENODEV; + } + + if ((val = sn9c102_read_reg(cam, cam->sysfs.reg)) < 0) { + mutex_unlock(&sn9c102_sysfs_lock); + return -EIO; + } + + count = sprintf(buf, "%d\n", val); + + DBG(3, "Read bytes: %zd, value: %d", count, val); + + mutex_unlock(&sn9c102_sysfs_lock); + + return count; +} + + +static ssize_t +sn9c102_store_val(struct device* cd, struct device_attribute *attr, + const char* buf, size_t len) +{ + struct sn9c102_device* cam; + u16 value; + ssize_t count; + int err; + + if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(container_of(cd, struct video_device, dev)); + if (!cam) { + mutex_unlock(&sn9c102_sysfs_lock); + return -ENODEV; + } + + value = sn9c102_strtou16(buf, len, &count); + if (!count) { + mutex_unlock(&sn9c102_sysfs_lock); + return -EINVAL; + } + + err = sn9c102_write_reg(cam, value, cam->sysfs.reg); + if (err) { + mutex_unlock(&sn9c102_sysfs_lock); + return -EIO; + } + + DBG(2, "Written SN9C1XX reg. 0x%02X, val. 0x%02X", + cam->sysfs.reg, value); + DBG(3, "Written bytes: %zd", count); + + mutex_unlock(&sn9c102_sysfs_lock); + + return count; +} + + +static ssize_t sn9c102_show_i2c_reg(struct device* cd, + struct device_attribute *attr, char* buf) +{ + struct sn9c102_device* cam; + ssize_t count; + + if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(container_of(cd, struct video_device, dev)); + if (!cam) { + mutex_unlock(&sn9c102_sysfs_lock); + return -ENODEV; + } + + count = sprintf(buf, "%u\n", cam->sysfs.i2c_reg); + + DBG(3, "Read bytes: %zd", count); + + mutex_unlock(&sn9c102_sysfs_lock); + + return count; +} + + +static ssize_t +sn9c102_store_i2c_reg(struct device* cd, struct device_attribute *attr, + const char* buf, size_t len) +{ + struct sn9c102_device* cam; + u16 index; + ssize_t count; + + if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(container_of(cd, struct video_device, dev)); + if (!cam) { + mutex_unlock(&sn9c102_sysfs_lock); + return -ENODEV; + } + + index = sn9c102_strtou16(buf, len, &count); + if (!count) { + mutex_unlock(&sn9c102_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(&sn9c102_sysfs_lock); + + return count; +} + + +static ssize_t sn9c102_show_i2c_val(struct device* cd, + struct device_attribute *attr, char* buf) +{ + struct sn9c102_device* cam; + ssize_t count; + int val; + + if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(container_of(cd, struct video_device, dev)); + if (!cam) { + mutex_unlock(&sn9c102_sysfs_lock); + return -ENODEV; + } + + if (!(cam->sensor.sysfs_ops & SN9C102_I2C_READ)) { + mutex_unlock(&sn9c102_sysfs_lock); + return -ENOSYS; + } + + if ((val = sn9c102_i2c_read(cam, cam->sysfs.i2c_reg)) < 0) { + mutex_unlock(&sn9c102_sysfs_lock); + return -EIO; + } + + count = sprintf(buf, "%d\n", val); + + DBG(3, "Read bytes: %zd, value: %d", count, val); + + mutex_unlock(&sn9c102_sysfs_lock); + + return count; +} + + +static ssize_t +sn9c102_store_i2c_val(struct device* cd, struct device_attribute *attr, + const char* buf, size_t len) +{ + struct sn9c102_device* cam; + u16 value; + ssize_t count; + int err; + + if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(container_of(cd, struct video_device, dev)); + if (!cam) { + mutex_unlock(&sn9c102_sysfs_lock); + return -ENODEV; + } + + if (!(cam->sensor.sysfs_ops & SN9C102_I2C_WRITE)) { + mutex_unlock(&sn9c102_sysfs_lock); + return -ENOSYS; + } + + value = sn9c102_strtou16(buf, len, &count); + if (!count) { + mutex_unlock(&sn9c102_sysfs_lock); + return -EINVAL; + } + + err = sn9c102_i2c_write(cam, cam->sysfs.i2c_reg, value); + if (err) { + mutex_unlock(&sn9c102_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(&sn9c102_sysfs_lock); + + return count; +} + + +static ssize_t +sn9c102_store_green(struct device* cd, struct device_attribute *attr, + const char* buf, size_t len) +{ + struct sn9c102_device* cam; + enum sn9c102_bridge bridge; + ssize_t res = 0; + u16 value; + ssize_t count; + + if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(container_of(cd, struct video_device, dev)); + if (!cam) { + mutex_unlock(&sn9c102_sysfs_lock); + return -ENODEV; + } + + bridge = cam->bridge; + + mutex_unlock(&sn9c102_sysfs_lock); + + value = sn9c102_strtou16(buf, len, &count); + if (!count) + return -EINVAL; + + switch (bridge) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + if (value > 0x0f) + return -EINVAL; + if ((res = sn9c102_store_reg(cd, attr, "0x11", 4)) >= 0) + res = sn9c102_store_val(cd, attr, buf, len); + break; + case BRIDGE_SN9C103: + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + if (value > 0x7f) + return -EINVAL; + if ((res = sn9c102_store_reg(cd, attr, "0x07", 4)) >= 0) + res = sn9c102_store_val(cd, attr, buf, len); + break; + } + + return res; +} + + +static ssize_t +sn9c102_store_blue(struct device* cd, struct device_attribute *attr, + const char* buf, size_t len) +{ + ssize_t res = 0; + u16 value; + ssize_t count; + + value = sn9c102_strtou16(buf, len, &count); + if (!count || value > 0x7f) + return -EINVAL; + + if ((res = sn9c102_store_reg(cd, attr, "0x06", 4)) >= 0) + res = sn9c102_store_val(cd, attr, buf, len); + + return res; +} + + +static ssize_t +sn9c102_store_red(struct device* cd, struct device_attribute *attr, + const char* buf, size_t len) +{ + ssize_t res = 0; + u16 value; + ssize_t count; + + value = sn9c102_strtou16(buf, len, &count); + if (!count || value > 0x7f) + return -EINVAL; + + if ((res = sn9c102_store_reg(cd, attr, "0x05", 4)) >= 0) + res = sn9c102_store_val(cd, attr, buf, len); + + return res; +} + + +static ssize_t sn9c102_show_frame_header(struct device* cd, + struct device_attribute *attr, + char* buf) +{ + struct sn9c102_device* cam; + ssize_t count; + + cam = video_get_drvdata(container_of(cd, struct video_device, dev)); + if (!cam) + return -ENODEV; + + count = sizeof(cam->sysfs.frame_header); + memcpy(buf, cam->sysfs.frame_header, count); + + DBG(3, "Frame header, read bytes: %zd", count); + + return count; +} + + +static DEVICE_ATTR(reg, S_IRUGO | S_IWUSR, sn9c102_show_reg, sn9c102_store_reg); +static DEVICE_ATTR(val, S_IRUGO | S_IWUSR, sn9c102_show_val, sn9c102_store_val); +static DEVICE_ATTR(i2c_reg, S_IRUGO | S_IWUSR, + sn9c102_show_i2c_reg, sn9c102_store_i2c_reg); +static DEVICE_ATTR(i2c_val, S_IRUGO | S_IWUSR, + sn9c102_show_i2c_val, sn9c102_store_i2c_val); +static DEVICE_ATTR(green, S_IWUSR, NULL, sn9c102_store_green); +static DEVICE_ATTR(blue, S_IWUSR, NULL, sn9c102_store_blue); +static DEVICE_ATTR(red, S_IWUSR, NULL, sn9c102_store_red); +static DEVICE_ATTR(frame_header, S_IRUGO, sn9c102_show_frame_header, NULL); + + +static int sn9c102_create_sysfs(struct sn9c102_device* cam) +{ + struct device *dev = &(cam->v4ldev->dev); + int err = 0; + + if ((err = device_create_file(dev, &dev_attr_reg))) + goto err_out; + if ((err = device_create_file(dev, &dev_attr_val))) + goto err_reg; + if ((err = device_create_file(dev, &dev_attr_frame_header))) + goto err_val; + + if (cam->sensor.sysfs_ops) { + if ((err = device_create_file(dev, &dev_attr_i2c_reg))) + goto err_frame_header; + if ((err = device_create_file(dev, &dev_attr_i2c_val))) + goto err_i2c_reg; + } + + if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102) { + if ((err = device_create_file(dev, &dev_attr_green))) + goto err_i2c_val; + } else { + if ((err = device_create_file(dev, &dev_attr_blue))) + goto err_i2c_val; + if ((err = device_create_file(dev, &dev_attr_red))) + goto err_blue; + } + + return 0; + +err_blue: + device_remove_file(dev, &dev_attr_blue); +err_i2c_val: + if (cam->sensor.sysfs_ops) + device_remove_file(dev, &dev_attr_i2c_val); +err_i2c_reg: + if (cam->sensor.sysfs_ops) + device_remove_file(dev, &dev_attr_i2c_reg); +err_frame_header: + device_remove_file(dev, &dev_attr_frame_header); +err_val: + device_remove_file(dev, &dev_attr_val); +err_reg: + device_remove_file(dev, &dev_attr_reg); +err_out: + return err; +} +#endif /* CONFIG_VIDEO_ADV_DEBUG */ + +/*****************************************************************************/ + +static int +sn9c102_set_pix_format(struct sn9c102_device* cam, struct v4l2_pix_format* pix) +{ + int err = 0; + + if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X || + pix->pixelformat == V4L2_PIX_FMT_JPEG) { + switch (cam->bridge) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + case BRIDGE_SN9C103: + err += sn9c102_write_reg(cam, cam->reg[0x18] | 0x80, + 0x18); + break; + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + err += sn9c102_write_reg(cam, cam->reg[0x18] & 0x7f, + 0x18); + break; + } + } else { + switch (cam->bridge) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + case BRIDGE_SN9C103: + err += sn9c102_write_reg(cam, cam->reg[0x18] & 0x7f, + 0x18); + break; + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + err += sn9c102_write_reg(cam, cam->reg[0x18] | 0x80, + 0x18); + break; + } + } + + return err ? -EIO : 0; +} + + +static int +sn9c102_set_compression(struct sn9c102_device* cam, + struct v4l2_jpegcompression* compression) +{ + int i, err = 0; + + switch (cam->bridge) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + case BRIDGE_SN9C103: + if (compression->quality == 0) + err += sn9c102_write_reg(cam, cam->reg[0x17] | 0x01, + 0x17); + else if (compression->quality == 1) + err += sn9c102_write_reg(cam, cam->reg[0x17] & 0xfe, + 0x17); + break; + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + if (compression->quality == 0) { + for (i = 0; i <= 63; i++) { + err += sn9c102_write_reg(cam, + SN9C102_Y_QTABLE1[i], + 0x100 + i); + err += sn9c102_write_reg(cam, + SN9C102_UV_QTABLE1[i], + 0x140 + i); + } + err += sn9c102_write_reg(cam, cam->reg[0x18] & 0xbf, + 0x18); + } else if (compression->quality == 1) { + for (i = 0; i <= 63; i++) { + err += sn9c102_write_reg(cam, + SN9C102_Y_QTABLE1[i], + 0x100 + i); + err += sn9c102_write_reg(cam, + SN9C102_UV_QTABLE1[i], + 0x140 + i); + } + err += sn9c102_write_reg(cam, cam->reg[0x18] | 0x40, + 0x18); + } + break; + } + + return err ? -EIO : 0; +} + + +static int sn9c102_set_scale(struct sn9c102_device* cam, u8 scale) +{ + u8 r = 0; + int err = 0; + + if (scale == 1) + r = cam->reg[0x18] & 0xcf; + else if (scale == 2) { + r = cam->reg[0x18] & 0xcf; + r |= 0x10; + } else if (scale == 4) + r = cam->reg[0x18] | 0x20; + + err += sn9c102_write_reg(cam, r, 0x18); + if (err) + return -EIO; + + PDBGG("Scaling factor: %u", scale); + + return 0; +} + + +static int sn9c102_set_crop(struct sn9c102_device* cam, struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = &cam->sensor; + u8 h_start = (u8)(rect->left - s->cropcap.bounds.left), + v_start = (u8)(rect->top - s->cropcap.bounds.top), + h_size = (u8)(rect->width / 16), + v_size = (u8)(rect->height / 16); + int err = 0; + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + err += sn9c102_write_reg(cam, h_size, 0x15); + err += sn9c102_write_reg(cam, v_size, 0x16); + if (err) + return -EIO; + + PDBGG("h_start, v_start, h_size, v_size, ho_size, vo_size " + "%u %u %u %u", h_start, v_start, h_size, v_size); + + return 0; +} + + +static int sn9c102_init(struct sn9c102_device* cam) +{ + struct sn9c102_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); + } else { /* use current values */ + qctrl = s->_qctrl; + rect = &(s->_rect); + } + + err += sn9c102_set_scale(cam, rect->width / s->pix_format.width); + err += sn9c102_set_crop(cam, rect); + if (err) + return err; + + if (s->init) { + err = s->init(cam); + if (err) { + DBG(3, "Sensor initialization failed"); + return err; + } + } + + if (!(cam->state & DEV_INITIALIZED)) + if (cam->bridge == BRIDGE_SN9C101 || + cam->bridge == BRIDGE_SN9C102 || + cam->bridge == BRIDGE_SN9C103) { + if (s->pix_format.pixelformat == V4L2_PIX_FMT_JPEG) + s->pix_format.pixelformat= V4L2_PIX_FMT_SBGGR8; + cam->compression.quality = cam->reg[0x17] & 0x01 ? + 0 : 1; + } else { + if (s->pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X) + s->pix_format.pixelformat = V4L2_PIX_FMT_JPEG; + cam->compression.quality = cam->reg[0x18] & 0x40 ? + 0 : 1; + err += sn9c102_set_compression(cam, &cam->compression); + } + else + err += sn9c102_set_compression(cam, &cam->compression); + err += sn9c102_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_SN9C10X || + s->pix_format.pixelformat == V4L2_PIX_FMT_JPEG) + 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 sn9c102_release_resources(struct kref *kref) +{ + struct sn9c102_device *cam; + + mutex_lock(&sn9c102_sysfs_lock); + + cam = container_of(kref, struct sn9c102_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); + v4l2_device_unregister(&cam->v4l2_dev); + usb_put_dev(cam->usbdev); + kfree(cam->control_buffer); + kfree(cam); + + mutex_unlock(&sn9c102_sysfs_lock); + +} + + +static int sn9c102_open(struct file *filp) +{ + struct sn9c102_device* cam; + int err = 0; + + /* + A read_trylock() in open() is the only safe way to prevent race + conditions with disconnect(), one close() and multiple (not + necessarily simultaneous) attempts to open(). For example, it + prevents from waiting for a second access, while the device + structure is being deallocated, after a possible disconnect() and + during a following close() holding the write lock: given that, after + this deallocation, no access will be possible anymore, using the + non-trylock version would have let open() gain the access to the + device structure improperly. + For this reason the lock must also not be per-device. + */ + if (!down_read_trylock(&sn9c102_dev_lock)) + return -ERESTARTSYS; + + cam = video_drvdata(filp); + + if (wait_for_completion_interruptible(&cam->probe)) { + up_read(&sn9c102_dev_lock); + return -ERESTARTSYS; + } + + kref_get(&cam->kref); + + /* + Make sure to isolate all the simultaneous opens. + */ + if (mutex_lock_interruptible(&cam->open_mutex)) { + kref_put(&cam->kref, sn9c102_release_resources); + up_read(&sn9c102_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"); + /* + open() must follow the open flags and should block + eventually while the device is in use. + */ + 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(&sn9c102_dev_lock); + /* + We will not release the "open_mutex" lock, so that only one + process can be in the wait queue below. This way the process + will be sleeping while holding the lock, without losing its + priority after any wake_up(). + */ + err = wait_event_interruptible_exclusive(cam->wait_open, + (cam->state & DEV_DISCONNECTED) + || !cam->users); + down_read(&sn9c102_dev_lock); + if (err) + goto out; + if (cam->state & DEV_DISCONNECTED) { + err = -ENODEV; + goto out; + } + } + + if (cam->state & DEV_MISCONFIGURED) { + err = sn9c102_init(cam); + if (err) { + DBG(1, "Initialization failed again. " + "I will retry on next open()."); + goto out; + } + cam->state &= ~DEV_MISCONFIGURED; + } + + if ((err = sn9c102_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; + sn9c102_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, sn9c102_release_resources); + + up_read(&sn9c102_dev_lock); + return err; +} + + +static int sn9c102_release(struct file *filp) +{ + struct sn9c102_device* cam; + + down_write(&sn9c102_dev_lock); + + cam = video_drvdata(filp); + + sn9c102_stop_transfer(cam); + sn9c102_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, sn9c102_release_resources); + + up_write(&sn9c102_dev_lock); + + return 0; +} + + +static ssize_t +sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos) +{ + struct sn9c102_device *cam = video_drvdata(filp); + struct sn9c102_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 (!sn9c102_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)) + sn9c102_empty_framequeues(cam); + sn9c102_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; + } + if (!cam->module_param.frame_timeout) { + err = wait_event_interruptible + ( cam->wait_frame, + (!list_empty(&cam->outqueue)) || + (cam->state & DEV_DISCONNECTED) || + (cam->state & DEV_MISCONFIGURED) ); + if (err) { + mutex_unlock(&cam->fileop_mutex); + return err; + } + } else { + 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; + } else if (timeout == 0 && + !(cam->state & DEV_DISCONNECTED)) { + DBG(1, "Video frame timeout elapsed"); + mutex_unlock(&cam->fileop_mutex); + return -EIO; + } + } + if (cam->state & DEV_DISCONNECTED) { + mutex_unlock(&cam->fileop_mutex); + return -ENODEV; + } + if (cam->state & DEV_MISCONFIGURED) { + mutex_unlock(&cam->fileop_mutex); + return -EIO; + } + } + + f = list_entry(cam->outqueue.prev, struct sn9c102_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); + + sn9c102_queue_unusedframes(cam); + + PDBGG("Frame #%lu, bytes read: %zu", + (unsigned long)f->buf.index, count); + + mutex_unlock(&cam->fileop_mutex); + + return count; +} + + +static unsigned int sn9c102_poll(struct file *filp, poll_table *wait) +{ + struct sn9c102_device *cam = video_drvdata(filp); + struct sn9c102_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 (!sn9c102_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); + sn9c102_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 sn9c102_vm_open(struct vm_area_struct* vma) +{ + struct sn9c102_frame_t* f = vma->vm_private_data; + f->vma_use_count++; +} + + +static void sn9c102_vm_close(struct vm_area_struct* vma) +{ + /* NOTE: buffers are not freed here */ + struct sn9c102_frame_t* f = vma->vm_private_data; + f->vma_use_count--; +} + + +static const struct vm_operations_struct sn9c102_vm_ops = { + .open = sn9c102_vm_open, + .close = sn9c102_vm_close, +}; + + +static int sn9c102_mmap(struct file* filp, struct vm_area_struct *vma) +{ + struct sn9c102_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 | VM_DONTEXPAND | VM_DONTDUMP; + + 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 = &sn9c102_vm_ops; + vma->vm_private_data = &cam->frame[i]; + sn9c102_vm_open(vma); + + mutex_unlock(&cam->fileop_mutex); + + return 0; +} + +/*****************************************************************************/ + +static int +sn9c102_vidioc_querycap(struct sn9c102_device* cam, void __user * arg) +{ + struct v4l2_capability cap = { + .driver = "sn9c102", + .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 +sn9c102_vidioc_enuminput(struct sn9c102_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 +sn9c102_vidioc_g_input(struct sn9c102_device* cam, void __user * arg) +{ + int index = 0; + + if (copy_to_user(arg, &index, sizeof(index))) + return -EFAULT; + + return 0; +} + + +static int +sn9c102_vidioc_s_input(struct sn9c102_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 +sn9c102_vidioc_query_ctrl(struct sn9c102_device* cam, void __user * arg) +{ + struct sn9c102_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 +sn9c102_vidioc_g_ctrl(struct sn9c102_device* cam, void __user * arg) +{ + struct sn9c102_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 && 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; + + PDBGG("VIDIOC_G_CTRL: id %lu, value %lu", + (unsigned long)ctrl.id, (unsigned long)ctrl.value); + + return err; +} + + +static int +sn9c102_vidioc_s_ctrl(struct sn9c102_device* cam, void __user * arg) +{ + struct sn9c102_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; + + PDBGG("VIDIOC_S_CTRL: id %lu, value %lu", + (unsigned long)ctrl.id, (unsigned long)ctrl.value); + + return 0; +} + + +static int +sn9c102_vidioc_cropcap(struct sn9c102_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 +sn9c102_vidioc_g_crop(struct sn9c102_device* cam, void __user * arg) +{ + struct sn9c102_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 +sn9c102_vidioc_s_crop(struct sn9c102_device* cam, void __user * arg) +{ + struct sn9c102_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 sn9c102_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 (SN9C102_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 : ((a / b) < 16 ? 2 : 4)) : 1; + } else + scale = 1; + + if (cam->stream == STREAM_ON) + if ((err = sn9c102_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) + sn9c102_release_buffers(cam); + + err = sn9c102_set_crop(cam, rect); + if (s->set_crop) + err += s->set_crop(cam, rect); + err += sn9c102_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 != sn9c102_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) + sn9c102_empty_framequeues(cam); + else if (cam->module_param.force_munmap) + sn9c102_requeue_outqueue(cam); + + cam->stream = stream; + + return 0; +} + + +static int +sn9c102_vidioc_enum_framesizes(struct sn9c102_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; + + switch (cam->bridge) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + case BRIDGE_SN9C103: + if (frmsize.pixel_format != V4L2_PIX_FMT_SN9C10X && + frmsize.pixel_format != V4L2_PIX_FMT_SBGGR8) + return -EINVAL; + break; + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + if (frmsize.pixel_format != V4L2_PIX_FMT_JPEG && + frmsize.pixel_format != V4L2_PIX_FMT_SBGGR8) + return -EINVAL; + break; + } + + 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 +sn9c102_vidioc_enum_fmt(struct sn9c102_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) { + switch (cam->bridge) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + case BRIDGE_SN9C103: + strcpy(fmtd.description, "compressed"); + fmtd.pixelformat = V4L2_PIX_FMT_SN9C10X; + break; + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + strcpy(fmtd.description, "JPEG"); + fmtd.pixelformat = V4L2_PIX_FMT_JPEG; + break; + } + 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 +sn9c102_vidioc_g_fmt(struct sn9c102_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_JPEG) ? + V4L2_COLORSPACE_JPEG : V4L2_COLORSPACE_SRGB; + pfmt->bytesperline = (pfmt->pixelformat == V4L2_PIX_FMT_SN9C10X || + pfmt->pixelformat == V4L2_PIX_FMT_JPEG) + ? 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 +sn9c102_vidioc_try_s_fmt(struct sn9c102_device* cam, unsigned int cmd, + void __user * arg) +{ + struct sn9c102_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 sn9c102_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 : ((a / b) < 16 ? 2 : 4)) : 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 : ((a / b) < 16 ? 2 : 4)) : 1; + } + + pix->width = rect.width / scale; + pix->height = rect.height / scale; + + switch (cam->bridge) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + case BRIDGE_SN9C103: + if (pix->pixelformat != V4L2_PIX_FMT_SN9C10X && + pix->pixelformat != V4L2_PIX_FMT_SBGGR8) + pix->pixelformat = pfmt->pixelformat; + break; + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + if (pix->pixelformat != V4L2_PIX_FMT_JPEG && + pix->pixelformat != V4L2_PIX_FMT_SBGGR8) + pix->pixelformat = pfmt->pixelformat; + break; + } + pix->priv = pfmt->priv; /* bpp */ + pix->colorspace = (pix->pixelformat == V4L2_PIX_FMT_JPEG) ? + V4L2_COLORSPACE_JPEG : V4L2_COLORSPACE_SRGB; + pix->bytesperline = (pix->pixelformat == V4L2_PIX_FMT_SN9C10X || + pix->pixelformat == V4L2_PIX_FMT_JPEG) + ? 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 = sn9c102_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) + sn9c102_release_buffers(cam); + + err += sn9c102_set_pix_format(cam, pix); + err += sn9c102_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 += sn9c102_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 != sn9c102_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) + sn9c102_empty_framequeues(cam); + else if (cam->module_param.force_munmap) + sn9c102_requeue_outqueue(cam); + + cam->stream = stream; + + return 0; +} + + +static int +sn9c102_vidioc_g_jpegcomp(struct sn9c102_device* cam, void __user * arg) +{ + if (copy_to_user(arg, &cam->compression, sizeof(cam->compression))) + return -EFAULT; + + return 0; +} + + +static int +sn9c102_vidioc_s_jpegcomp(struct sn9c102_device* cam, void __user * arg) +{ + struct v4l2_jpegcompression jc; + const enum sn9c102_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 = sn9c102_stream_interrupt(cam))) + return err; + + err += sn9c102_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 +sn9c102_vidioc_reqbufs(struct sn9c102_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 = sn9c102_stream_interrupt(cam))) + return err; + + sn9c102_empty_framequeues(cam); + + sn9c102_release_buffers(cam); + if (rb.count) + rb.count = sn9c102_request_buffers(cam, rb.count, IO_MMAP); + + if (copy_to_user(arg, &rb, sizeof(rb))) { + sn9c102_release_buffers(cam); + cam->io = IO_NONE; + return -EFAULT; + } + + cam->io = rb.count ? IO_MMAP : IO_NONE; + + return 0; +} + + +static int +sn9c102_vidioc_querybuf(struct sn9c102_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; + + b = cam->frame[b.index].buf; + + 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 +sn9c102_vidioc_qbuf(struct sn9c102_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 +sn9c102_vidioc_dqbuf(struct sn9c102_device* cam, struct file* filp, + void __user * arg) +{ + struct v4l2_buffer b; + struct sn9c102_frame_t *f; + unsigned long lock_flags; + long timeout; + int err = 0; + + 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; + if (!cam->module_param.frame_timeout) { + err = wait_event_interruptible + ( cam->wait_frame, + (!list_empty(&cam->outqueue)) || + (cam->state & DEV_DISCONNECTED) || + (cam->state & DEV_MISCONFIGURED) ); + if (err) + return err; + } else { + 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; + else if (timeout == 0 && + !(cam->state & DEV_DISCONNECTED)) { + DBG(1, "Video frame timeout elapsed"); + return -EIO; + } + } + if (cam->state & DEV_DISCONNECTED) + return -ENODEV; + if (cam->state & DEV_MISCONFIGURED) + return -EIO; + } + + spin_lock_irqsave(&cam->queue_lock, lock_flags); + f = list_entry(cam->outqueue.next, struct sn9c102_frame_t, frame); + list_del(cam->outqueue.next); + spin_unlock_irqrestore(&cam->queue_lock, lock_flags); + + f->state = F_UNUSED; + + b = f->buf; + 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 +sn9c102_vidioc_streamon(struct sn9c102_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 +sn9c102_vidioc_streamoff(struct sn9c102_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 = sn9c102_stream_interrupt(cam))) + return err; + + sn9c102_empty_framequeues(cam); + + DBG(3, "Stream off"); + + return 0; +} + + +static int +sn9c102_vidioc_g_parm(struct sn9c102_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 +sn9c102_vidioc_s_parm(struct sn9c102_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 > SN9C102_MAX_FRAMES) + sp.parm.capture.readbuffers = SN9C102_MAX_FRAMES; + + if (copy_to_user(arg, &sp, sizeof(sp))) + return -EFAULT; + + cam->nreadbuffers = sp.parm.capture.readbuffers; + + return 0; +} + + +static int +sn9c102_vidioc_enumaudio(struct sn9c102_device* cam, void __user * arg) +{ + struct v4l2_audio audio; + + if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102) + return -EINVAL; + + if (copy_from_user(&audio, arg, sizeof(audio))) + return -EFAULT; + + if (audio.index != 0) + return -EINVAL; + + strcpy(audio.name, "Microphone"); + audio.capability = 0; + audio.mode = 0; + + if (copy_to_user(arg, &audio, sizeof(audio))) + return -EFAULT; + + return 0; +} + + +static int +sn9c102_vidioc_g_audio(struct sn9c102_device* cam, void __user * arg) +{ + struct v4l2_audio audio; + + if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102) + return -EINVAL; + + if (copy_from_user(&audio, arg, sizeof(audio))) + return -EFAULT; + + memset(&audio, 0, sizeof(audio)); + strcpy(audio.name, "Microphone"); + + if (copy_to_user(arg, &audio, sizeof(audio))) + return -EFAULT; + + return 0; +} + + +static int +sn9c102_vidioc_s_audio(struct sn9c102_device* cam, void __user * arg) +{ + struct v4l2_audio audio; + + if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102) + return -EINVAL; + + if (copy_from_user(&audio, arg, sizeof(audio))) + return -EFAULT; + + if (audio.index != 0) + return -EINVAL; + + return 0; +} + + +static long sn9c102_ioctl_v4l2(struct file *filp, + unsigned int cmd, void __user *arg) +{ + struct sn9c102_device *cam = video_drvdata(filp); + + switch (cmd) { + + case VIDIOC_QUERYCAP: + return sn9c102_vidioc_querycap(cam, arg); + + case VIDIOC_ENUMINPUT: + return sn9c102_vidioc_enuminput(cam, arg); + + case VIDIOC_G_INPUT: + return sn9c102_vidioc_g_input(cam, arg); + + case VIDIOC_S_INPUT: + return sn9c102_vidioc_s_input(cam, arg); + + case VIDIOC_QUERYCTRL: + return sn9c102_vidioc_query_ctrl(cam, arg); + + case VIDIOC_G_CTRL: + return sn9c102_vidioc_g_ctrl(cam, arg); + + case VIDIOC_S_CTRL: + return sn9c102_vidioc_s_ctrl(cam, arg); + + case VIDIOC_CROPCAP: + return sn9c102_vidioc_cropcap(cam, arg); + + case VIDIOC_G_CROP: + return sn9c102_vidioc_g_crop(cam, arg); + + case VIDIOC_S_CROP: + return sn9c102_vidioc_s_crop(cam, arg); + + case VIDIOC_ENUM_FRAMESIZES: + return sn9c102_vidioc_enum_framesizes(cam, arg); + + case VIDIOC_ENUM_FMT: + return sn9c102_vidioc_enum_fmt(cam, arg); + + case VIDIOC_G_FMT: + return sn9c102_vidioc_g_fmt(cam, arg); + + case VIDIOC_TRY_FMT: + case VIDIOC_S_FMT: + return sn9c102_vidioc_try_s_fmt(cam, cmd, arg); + + case VIDIOC_G_JPEGCOMP: + return sn9c102_vidioc_g_jpegcomp(cam, arg); + + case VIDIOC_S_JPEGCOMP: + return sn9c102_vidioc_s_jpegcomp(cam, arg); + + case VIDIOC_REQBUFS: + return sn9c102_vidioc_reqbufs(cam, arg); + + case VIDIOC_QUERYBUF: + return sn9c102_vidioc_querybuf(cam, arg); + + case VIDIOC_QBUF: + return sn9c102_vidioc_qbuf(cam, arg); + + case VIDIOC_DQBUF: + return sn9c102_vidioc_dqbuf(cam, filp, arg); + + case VIDIOC_STREAMON: + return sn9c102_vidioc_streamon(cam, arg); + + case VIDIOC_STREAMOFF: + return sn9c102_vidioc_streamoff(cam, arg); + + case VIDIOC_G_PARM: + return sn9c102_vidioc_g_parm(cam, arg); + + case VIDIOC_S_PARM: + return sn9c102_vidioc_s_parm(cam, arg); + + case VIDIOC_ENUMAUDIO: + return sn9c102_vidioc_enumaudio(cam, arg); + + case VIDIOC_G_AUDIO: + return sn9c102_vidioc_g_audio(cam, arg); + + case VIDIOC_S_AUDIO: + return sn9c102_vidioc_s_audio(cam, arg); + + default: + return -ENOTTY; + + } +} + + +static long sn9c102_ioctl(struct file *filp, + unsigned int cmd, unsigned long arg) +{ + struct sn9c102_device *cam = video_drvdata(filp); + 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; + } + + V4LDBG(3, "sn9c102", cmd); + + err = sn9c102_ioctl_v4l2(filp, cmd, (void __user *)arg); + + mutex_unlock(&cam->fileop_mutex); + + return err; +} + +/*****************************************************************************/ + +static const struct v4l2_file_operations sn9c102_fops = { + .owner = THIS_MODULE, + .open = sn9c102_open, + .release = sn9c102_release, + .unlocked_ioctl = sn9c102_ioctl, + .read = sn9c102_read, + .poll = sn9c102_poll, + .mmap = sn9c102_mmap, +}; + +/*****************************************************************************/ + +/* It exists a single interface only. We do not need to validate anything. */ +static int +sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct sn9c102_device* cam; + static unsigned int dev_nr; + unsigned int i; + int err = 0, r; + + if (!(cam = kzalloc(sizeof(struct sn9c102_device), GFP_KERNEL))) + return -ENOMEM; + + cam->usbdev = udev; + + /* register v4l2_device early so it can be used for printks */ + if (v4l2_device_register(&intf->dev, &cam->v4l2_dev)) { + dev_err(&intf->dev, "v4l2_device_register failed\n"); + err = -ENOMEM; + goto fail; + } + + if (!(cam->control_buffer = kzalloc(8, GFP_KERNEL))) { + DBG(1, "kzalloc() failed"); + err = -ENOMEM; + goto fail; + } + + if (!(cam->v4ldev = video_device_alloc())) { + DBG(1, "video_device_alloc() failed"); + err = -ENOMEM; + goto fail; + } + + r = sn9c102_read_reg(cam, 0x00); + if (r < 0 || (r != 0x10 && r != 0x11 && r != 0x12)) { + DBG(1, "Sorry, this is not a SN9C1xx-based camera " + "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); + err = -ENODEV; + goto fail; + } + + cam->bridge = id->driver_info; + switch (cam->bridge) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + DBG(2, "SN9C10[12] PC Camera Controller detected " + "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); + break; + case BRIDGE_SN9C103: + DBG(2, "SN9C103 PC Camera Controller detected " + "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); + break; + case BRIDGE_SN9C105: + DBG(2, "SN9C105 PC Camera Controller detected " + "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); + break; + case BRIDGE_SN9C120: + DBG(2, "SN9C120 PC Camera Controller detected " + "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); + break; + } + + for (i = 0; i < ARRAY_SIZE(sn9c102_sensor_table); i++) { + err = sn9c102_sensor_table[i](cam); + if (!err) + break; + } + + if (!err) { + DBG(2, "%s image sensor detected", cam->sensor.name); + DBG(3, "Support for %s maintained by %s", + cam->sensor.name, cam->sensor.maintainer); + } else { + DBG(1, "No supported image sensor detected for this bridge"); + err = -ENODEV; + goto fail; + } + + if (!(cam->bridge & cam->sensor.supported_bridge)) { + DBG(1, "Bridge not supported"); + err = -ENODEV; + goto fail; + } + + if (sn9c102_init(cam)) { + DBG(1, "Initialization failed. I will retry on open()."); + cam->state |= DEV_MISCONFIGURED; + } + + strcpy(cam->v4ldev->name, "SN9C1xx PC Camera"); + cam->v4ldev->fops = &sn9c102_fops; + cam->v4ldev->release = video_device_release; + cam->v4ldev->v4l2_dev = &cam->v4l2_dev; + + 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 < SN9C102_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)); + + video_set_drvdata(cam->v4ldev, cam); + cam->module_param.force_munmap = force_munmap[dev_nr]; + cam->module_param.frame_timeout = frame_timeout[dev_nr]; + + dev_nr = (dev_nr < SN9C102_MAX_DEVICES-1) ? dev_nr+1 : 0; + +#ifdef CONFIG_VIDEO_ADV_DEBUG + err = sn9c102_create_sysfs(cam); + if (!err) + DBG(2, "Optional device control through 'sysfs' " + "interface ready"); + else + DBG(2, "Failed to create optional 'sysfs' interface for " + "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); + v4l2_device_unregister(&cam->v4l2_dev); + kfree(cam); + } + return err; +} + + +static void sn9c102_usb_disconnect(struct usb_interface* intf) +{ + struct sn9c102_device* cam; + + down_write(&sn9c102_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; + sn9c102_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); + + v4l2_device_disconnect(&cam->v4l2_dev); + + kref_put(&cam->kref, sn9c102_release_resources); + + up_write(&sn9c102_dev_lock); +} + + +static struct usb_driver sn9c102_usb_driver = { + .name = "sn9c102", + .id_table = sn9c102_id_table, + .probe = sn9c102_usb_probe, + .disconnect = sn9c102_usb_disconnect, +}; + +module_usb_driver(sn9c102_usb_driver); diff --git a/drivers/staging/media/sn9c102/sn9c102_devtable.h b/drivers/staging/media/sn9c102/sn9c102_devtable.h new file mode 100644 index 000000000000..b3d2cc729657 --- /dev/null +++ b/drivers/staging/media/sn9c102/sn9c102_devtable.h @@ -0,0 +1,147 @@ +/*************************************************************************** + * Table of device identifiers of the SN9C1xx PC Camera Controllers * + * * + * Copyright (C) 2007 by Luca Risolia * + * * + * 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 _SN9C102_DEVTABLE_H_ +#define _SN9C102_DEVTABLE_H_ + +#include + +struct sn9c102_device; + +/* + Each SN9C1xx camera has proper PID/VID identifiers. + SN9C103, SN9C105, SN9C120 support multiple interfaces, but we only have to + handle the video class interface. +*/ +#define SN9C102_USB_DEVICE(vend, prod, bridge) \ + .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \ + USB_DEVICE_ID_MATCH_INT_CLASS, \ + .idVendor = (vend), \ + .idProduct = (prod), \ + .bInterfaceClass = 0xff, \ + .driver_info = (bridge) + +static const struct usb_device_id sn9c102_id_table[] = { + /* SN9C101 and SN9C102 */ +#if !defined CONFIG_USB_GSPCA_SONIXB && !defined CONFIG_USB_GSPCA_SONIXB_MODULE + { SN9C102_USB_DEVICE(0x0c45, 0x6001, BRIDGE_SN9C102), }, + { SN9C102_USB_DEVICE(0x0c45, 0x6005, BRIDGE_SN9C102), }, + { SN9C102_USB_DEVICE(0x0c45, 0x6007, BRIDGE_SN9C102), }, + { SN9C102_USB_DEVICE(0x0c45, 0x6009, BRIDGE_SN9C102), }, + { SN9C102_USB_DEVICE(0x0c45, 0x600d, BRIDGE_SN9C102), }, +/* { SN9C102_USB_DEVICE(0x0c45, 0x6011, BRIDGE_SN9C102), }, OV6650 */ + { SN9C102_USB_DEVICE(0x0c45, 0x6019, BRIDGE_SN9C102), }, +#endif + { SN9C102_USB_DEVICE(0x0c45, 0x6024, BRIDGE_SN9C102), }, + { SN9C102_USB_DEVICE(0x0c45, 0x6025, BRIDGE_SN9C102), }, +#if !defined CONFIG_USB_GSPCA_SONIXB && !defined CONFIG_USB_GSPCA_SONIXB_MODULE + { SN9C102_USB_DEVICE(0x0c45, 0x6028, BRIDGE_SN9C102), }, + { SN9C102_USB_DEVICE(0x0c45, 0x6029, BRIDGE_SN9C102), }, + { SN9C102_USB_DEVICE(0x0c45, 0x602a, BRIDGE_SN9C102), }, +#endif + { SN9C102_USB_DEVICE(0x0c45, 0x602b, BRIDGE_SN9C102), }, /* not in sonixb */ +#if !defined CONFIG_USB_GSPCA_SONIXB && !defined CONFIG_USB_GSPCA_SONIXB_MODULE + { SN9C102_USB_DEVICE(0x0c45, 0x602c, BRIDGE_SN9C102), }, +/* { SN9C102_USB_DEVICE(0x0c45, 0x602d, BRIDGE_SN9C102), }, HV7131R */ + { SN9C102_USB_DEVICE(0x0c45, 0x602e, BRIDGE_SN9C102), }, +#endif + { SN9C102_USB_DEVICE(0x0c45, 0x6030, BRIDGE_SN9C102), }, /* not in sonixb */ + /* SN9C103 */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x6080, BRIDGE_SN9C103), }, non existent ? */ + { SN9C102_USB_DEVICE(0x0c45, 0x6082, BRIDGE_SN9C103), }, /* not in sonixb */ +#if !defined CONFIG_USB_GSPCA_SONIXB && !defined CONFIG_USB_GSPCA_SONIXB_MODULE +/* { SN9C102_USB_DEVICE(0x0c45, 0x6083, BRIDGE_SN9C103), }, HY7131D/E */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x6088, BRIDGE_SN9C103), }, non existent ? */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x608a, BRIDGE_SN9C103), }, non existent ? */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x608b, BRIDGE_SN9C103), }, non existent ? */ + { SN9C102_USB_DEVICE(0x0c45, 0x608c, BRIDGE_SN9C103), }, +/* { SN9C102_USB_DEVICE(0x0c45, 0x608e, BRIDGE_SN9C103), }, CISVF10 */ + { SN9C102_USB_DEVICE(0x0c45, 0x608f, BRIDGE_SN9C103), }, +/* { SN9C102_USB_DEVICE(0x0c45, 0x60a0, BRIDGE_SN9C103), }, non existent ? */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60a2, BRIDGE_SN9C103), }, non existent ? */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60a3, BRIDGE_SN9C103), }, non existent ? */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60a8, BRIDGE_SN9C103), }, PAS106 */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60aa, BRIDGE_SN9C103), }, TAS5130 */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60ab, BRIDGE_SN9C103), }, TAS5110, non existent */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60ac, BRIDGE_SN9C103), }, non existent ? */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60ae, BRIDGE_SN9C103), }, non existent ? */ + { SN9C102_USB_DEVICE(0x0c45, 0x60af, BRIDGE_SN9C103), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60b0, BRIDGE_SN9C103), }, +/* { SN9C102_USB_DEVICE(0x0c45, 0x60b2, BRIDGE_SN9C103), }, non existent ? */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60b3, BRIDGE_SN9C103), }, non existent ? */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60b8, BRIDGE_SN9C103), }, non existent ? */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60ba, BRIDGE_SN9C103), }, non existent ? */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60bb, BRIDGE_SN9C103), }, non existent ? */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60bc, BRIDGE_SN9C103), }, non existent ? */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60be, BRIDGE_SN9C103), }, non existent ? */ +#endif + /* SN9C105 */ +#if !defined CONFIG_USB_GSPCA_SONIXJ && !defined CONFIG_USB_GSPCA_SONIXJ_MODULE + { SN9C102_USB_DEVICE(0x045e, 0x00f5, BRIDGE_SN9C105), }, + { SN9C102_USB_DEVICE(0x045e, 0x00f7, BRIDGE_SN9C105), }, + { SN9C102_USB_DEVICE(0x0471, 0x0327, BRIDGE_SN9C105), }, + { SN9C102_USB_DEVICE(0x0471, 0x0328, BRIDGE_SN9C105), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60c0, BRIDGE_SN9C105), }, +/* { SN9C102_USB_DEVICE(0x0c45, 0x60c2, BRIDGE_SN9C105), }, PO1030 */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60c8, BRIDGE_SN9C105), }, OM6801 */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60cc, BRIDGE_SN9C105), }, HV7131GP */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60ea, BRIDGE_SN9C105), }, non existent ? */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60ec, BRIDGE_SN9C105), }, MO4000 */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60ef, BRIDGE_SN9C105), }, ICM105C */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60fa, BRIDGE_SN9C105), }, OV7648 */ + { SN9C102_USB_DEVICE(0x0c45, 0x60fb, BRIDGE_SN9C105), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60fc, BRIDGE_SN9C105), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60fe, BRIDGE_SN9C105), }, + /* SN9C120 */ + { SN9C102_USB_DEVICE(0x0458, 0x7025, BRIDGE_SN9C120), }, +/* { SN9C102_USB_DEVICE(0x0c45, 0x6102, BRIDGE_SN9C120), }, po2030 */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x6108, BRIDGE_SN9C120), }, om6801 */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x610f, BRIDGE_SN9C120), }, S5K53BEB */ + { SN9C102_USB_DEVICE(0x0c45, 0x6130, BRIDGE_SN9C120), }, +/* { SN9C102_USB_DEVICE(0x0c45, 0x6138, BRIDGE_SN9C120), }, MO8000 */ + { SN9C102_USB_DEVICE(0x0c45, 0x613a, BRIDGE_SN9C120), }, + { SN9C102_USB_DEVICE(0x0c45, 0x613b, BRIDGE_SN9C120), }, + { SN9C102_USB_DEVICE(0x0c45, 0x613c, BRIDGE_SN9C120), }, + { SN9C102_USB_DEVICE(0x0c45, 0x613e, BRIDGE_SN9C120), }, +#endif + { } +}; + +/* + Probing functions: on success, you must attach the sensor to the camera + by calling sn9c102_attach_sensor(). + To enable the I2C communication, you might need to perform a really basic + initialization of the SN9C1XX chip. + Functions must return 0 on success, the appropriate error otherwise. +*/ +extern int sn9c102_probe_hv7131d(struct sn9c102_device* cam); +extern int sn9c102_probe_hv7131r(struct sn9c102_device* cam); +extern int sn9c102_probe_mi0343(struct sn9c102_device* cam); +extern int sn9c102_probe_mi0360(struct sn9c102_device* cam); +extern int sn9c102_probe_mt9v111(struct sn9c102_device *cam); +extern int sn9c102_probe_ov7630(struct sn9c102_device* cam); +extern int sn9c102_probe_ov7660(struct sn9c102_device* cam); +extern int sn9c102_probe_pas106b(struct sn9c102_device* cam); +extern int sn9c102_probe_pas202bcb(struct sn9c102_device* cam); +extern int sn9c102_probe_tas5110c1b(struct sn9c102_device* cam); +extern int sn9c102_probe_tas5110d(struct sn9c102_device* cam); +extern int sn9c102_probe_tas5130d1b(struct sn9c102_device* cam); + +#endif /* _SN9C102_DEVTABLE_H_ */ diff --git a/drivers/staging/media/sn9c102/sn9c102_hv7131d.c b/drivers/staging/media/sn9c102/sn9c102_hv7131d.c new file mode 100644 index 000000000000..2dce5c908c8e --- /dev/null +++ b/drivers/staging/media/sn9c102/sn9c102_hv7131d.c @@ -0,0 +1,264 @@ +/*************************************************************************** + * Plug-in for HV7131D image sensor connected to the SN9C1xx PC Camera * + * Controllers * + * * + * Copyright (C) 2004-2007 by Luca Risolia * + * * + * 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. * + ***************************************************************************/ + +#include "sn9c102_sensor.h" +#include "sn9c102_devtable.h" + + +static int hv7131d_init(struct sn9c102_device* cam) +{ + int err; + + err = sn9c102_write_const_regs(cam, {0x00, 0x10}, {0x00, 0x11}, + {0x00, 0x14}, {0x60, 0x17}, + {0x0e, 0x18}, {0xf2, 0x19}); + + err += sn9c102_i2c_write(cam, 0x01, 0x04); + err += sn9c102_i2c_write(cam, 0x02, 0x00); + err += sn9c102_i2c_write(cam, 0x28, 0x00); + + return err; +} + + +static int hv7131d_get_ctrl(struct sn9c102_device* cam, + struct v4l2_control* ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + { + int r1 = sn9c102_i2c_read(cam, 0x26), + r2 = sn9c102_i2c_read(cam, 0x27); + if (r1 < 0 || r2 < 0) + return -EIO; + ctrl->value = (r1 << 8) | (r2 & 0xff); + } + return 0; + case V4L2_CID_RED_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x31)) < 0) + return -EIO; + ctrl->value = 0x3f - (ctrl->value & 0x3f); + return 0; + case V4L2_CID_BLUE_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x33)) < 0) + return -EIO; + ctrl->value = 0x3f - (ctrl->value & 0x3f); + return 0; + case SN9C102_V4L2_CID_GREEN_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x32)) < 0) + return -EIO; + ctrl->value = 0x3f - (ctrl->value & 0x3f); + return 0; + case SN9C102_V4L2_CID_RESET_LEVEL: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x30)) < 0) + return -EIO; + ctrl->value &= 0x3f; + return 0; + case SN9C102_V4L2_CID_PIXEL_BIAS_VOLTAGE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x34)) < 0) + return -EIO; + ctrl->value &= 0x07; + return 0; + default: + return -EINVAL; + } +} + + +static int hv7131d_set_ctrl(struct sn9c102_device* cam, + const struct v4l2_control* ctrl) +{ + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + err += sn9c102_i2c_write(cam, 0x26, ctrl->value >> 8); + err += sn9c102_i2c_write(cam, 0x27, ctrl->value & 0xff); + break; + case V4L2_CID_RED_BALANCE: + err += sn9c102_i2c_write(cam, 0x31, 0x3f - ctrl->value); + break; + case V4L2_CID_BLUE_BALANCE: + err += sn9c102_i2c_write(cam, 0x33, 0x3f - ctrl->value); + break; + case SN9C102_V4L2_CID_GREEN_BALANCE: + err += sn9c102_i2c_write(cam, 0x32, 0x3f - ctrl->value); + break; + case SN9C102_V4L2_CID_RESET_LEVEL: + err += sn9c102_i2c_write(cam, 0x30, ctrl->value); + break; + case SN9C102_V4L2_CID_PIXEL_BIAS_VOLTAGE: + err += sn9c102_i2c_write(cam, 0x34, ctrl->value); + break; + default: + return -EINVAL; + } + + return err ? -EIO : 0; +} + + +static int hv7131d_set_crop(struct sn9c102_device* cam, + const struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + int err = 0; + u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 2, + v_start = (u8)(rect->top - s->cropcap.bounds.top) + 2; + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + + return err; +} + + +static int hv7131d_set_pix_format(struct sn9c102_device* cam, + const struct v4l2_pix_format* pix) +{ + int err = 0; + + if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) + err += sn9c102_write_reg(cam, 0x42, 0x19); + else + err += sn9c102_write_reg(cam, 0xf2, 0x19); + + return err; +} + + +static const struct sn9c102_sensor hv7131d = { + .name = "HV7131D", + .maintainer = "Luca Risolia ", + .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102, + .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, + .frequency = SN9C102_I2C_100KHZ, + .interface = SN9C102_I2C_2WIRES, + .i2c_slave_id = 0x11, + .init = &hv7131d_init, + .qctrl = { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = 0x0250, + .maximum = 0xffff, + .step = 0x0001, + .default_value = 0x0250, + .flags = 0, + }, + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "red balance", + .minimum = 0x00, + .maximum = 0x3f, + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "blue balance", + .minimum = 0x00, + .maximum = 0x3f, + .step = 0x01, + .default_value = 0x20, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_GREEN_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "green balance", + .minimum = 0x00, + .maximum = 0x3f, + .step = 0x01, + .default_value = 0x1e, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_RESET_LEVEL, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "reset level", + .minimum = 0x19, + .maximum = 0x3f, + .step = 0x01, + .default_value = 0x30, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_PIXEL_BIAS_VOLTAGE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "pixel bias voltage", + .minimum = 0x00, + .maximum = 0x07, + .step = 0x01, + .default_value = 0x02, + .flags = 0, + }, + }, + .get_ctrl = &hv7131d_get_ctrl, + .set_ctrl = &hv7131d_set_ctrl, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + }, + .set_crop = &hv7131d_set_crop, + .pix_format = { + .width = 640, + .height = 480, + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .priv = 8, + }, + .set_pix_format = &hv7131d_set_pix_format +}; + + +int sn9c102_probe_hv7131d(struct sn9c102_device* cam) +{ + int r0 = 0, r1 = 0, err; + + err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x00, 0x01}, + {0x28, 0x17}); + + r0 = sn9c102_i2c_try_read(cam, &hv7131d, 0x00); + r1 = sn9c102_i2c_try_read(cam, &hv7131d, 0x01); + if (err || r0 < 0 || r1 < 0) + return -EIO; + + if ((r0 != 0x00 && r0 != 0x01) || r1 != 0x04) + return -ENODEV; + + sn9c102_attach_sensor(cam, &hv7131d); + + return 0; +} diff --git a/drivers/staging/media/sn9c102/sn9c102_hv7131r.c b/drivers/staging/media/sn9c102/sn9c102_hv7131r.c new file mode 100644 index 000000000000..4295887ff609 --- /dev/null +++ b/drivers/staging/media/sn9c102/sn9c102_hv7131r.c @@ -0,0 +1,363 @@ +/*************************************************************************** + * Plug-in for HV7131R image sensor connected to the SN9C1xx PC Camera * + * Controllers * + * * + * Copyright (C) 2007 by Luca Risolia * + * * + * 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. * + ***************************************************************************/ + +#include "sn9c102_sensor.h" +#include "sn9c102_devtable.h" + + +static int hv7131r_init(struct sn9c102_device* cam) +{ + int err = 0; + + switch (sn9c102_get_bridge(cam)) { + case BRIDGE_SN9C103: + err = sn9c102_write_const_regs(cam, {0x00, 0x03}, {0x1a, 0x04}, + {0x20, 0x05}, {0x20, 0x06}, + {0x03, 0x10}, {0x00, 0x14}, + {0x60, 0x17}, {0x0a, 0x18}, + {0xf0, 0x19}, {0x1d, 0x1a}, + {0x10, 0x1b}, {0x02, 0x1c}, + {0x03, 0x1d}, {0x0f, 0x1e}, + {0x0c, 0x1f}, {0x00, 0x20}, + {0x10, 0x21}, {0x20, 0x22}, + {0x30, 0x23}, {0x40, 0x24}, + {0x50, 0x25}, {0x60, 0x26}, + {0x70, 0x27}, {0x80, 0x28}, + {0x90, 0x29}, {0xa0, 0x2a}, + {0xb0, 0x2b}, {0xc0, 0x2c}, + {0xd0, 0x2d}, {0xe0, 0x2e}, + {0xf0, 0x2f}, {0xff, 0x30}); + break; + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + err = sn9c102_write_const_regs(cam, {0x44, 0x01}, {0x40, 0x02}, + {0x00, 0x03}, {0x1a, 0x04}, + {0x44, 0x05}, {0x3e, 0x06}, + {0x1a, 0x07}, {0x03, 0x10}, + {0x08, 0x14}, {0xa3, 0x17}, + {0x4b, 0x18}, {0x00, 0x19}, + {0x1d, 0x1a}, {0x10, 0x1b}, + {0x02, 0x1c}, {0x03, 0x1d}, + {0x0f, 0x1e}, {0x0c, 0x1f}, + {0x00, 0x20}, {0x29, 0x21}, + {0x40, 0x22}, {0x54, 0x23}, + {0x66, 0x24}, {0x76, 0x25}, + {0x85, 0x26}, {0x94, 0x27}, + {0xa1, 0x28}, {0xae, 0x29}, + {0xbb, 0x2a}, {0xc7, 0x2b}, + {0xd3, 0x2c}, {0xde, 0x2d}, + {0xea, 0x2e}, {0xf4, 0x2f}, + {0xff, 0x30}, {0x00, 0x3F}, + {0xC7, 0x40}, {0x01, 0x41}, + {0x44, 0x42}, {0x00, 0x43}, + {0x44, 0x44}, {0x00, 0x45}, + {0x44, 0x46}, {0x00, 0x47}, + {0xC7, 0x48}, {0x01, 0x49}, + {0xC7, 0x4A}, {0x01, 0x4B}, + {0xC7, 0x4C}, {0x01, 0x4D}, + {0x44, 0x4E}, {0x00, 0x4F}, + {0x44, 0x50}, {0x00, 0x51}, + {0x44, 0x52}, {0x00, 0x53}, + {0xC7, 0x54}, {0x01, 0x55}, + {0xC7, 0x56}, {0x01, 0x57}, + {0xC7, 0x58}, {0x01, 0x59}, + {0x44, 0x5A}, {0x00, 0x5B}, + {0x44, 0x5C}, {0x00, 0x5D}, + {0x44, 0x5E}, {0x00, 0x5F}, + {0xC7, 0x60}, {0x01, 0x61}, + {0xC7, 0x62}, {0x01, 0x63}, + {0xC7, 0x64}, {0x01, 0x65}, + {0x44, 0x66}, {0x00, 0x67}, + {0x44, 0x68}, {0x00, 0x69}, + {0x44, 0x6A}, {0x00, 0x6B}, + {0xC7, 0x6C}, {0x01, 0x6D}, + {0xC7, 0x6E}, {0x01, 0x6F}, + {0xC7, 0x70}, {0x01, 0x71}, + {0x44, 0x72}, {0x00, 0x73}, + {0x44, 0x74}, {0x00, 0x75}, + {0x44, 0x76}, {0x00, 0x77}, + {0xC7, 0x78}, {0x01, 0x79}, + {0xC7, 0x7A}, {0x01, 0x7B}, + {0xC7, 0x7C}, {0x01, 0x7D}, + {0x44, 0x7E}, {0x00, 0x7F}, + {0x14, 0x84}, {0x00, 0x85}, + {0x27, 0x86}, {0x00, 0x87}, + {0x07, 0x88}, {0x00, 0x89}, + {0xEC, 0x8A}, {0x0f, 0x8B}, + {0xD8, 0x8C}, {0x0f, 0x8D}, + {0x3D, 0x8E}, {0x00, 0x8F}, + {0x3D, 0x90}, {0x00, 0x91}, + {0xCD, 0x92}, {0x0f, 0x93}, + {0xf7, 0x94}, {0x0f, 0x95}, + {0x0C, 0x96}, {0x00, 0x97}, + {0x00, 0x98}, {0x66, 0x99}, + {0x05, 0x9A}, {0x00, 0x9B}, + {0x04, 0x9C}, {0x00, 0x9D}, + {0x08, 0x9E}, {0x00, 0x9F}, + {0x2D, 0xC0}, {0x2D, 0xC1}, + {0x3A, 0xC2}, {0x05, 0xC3}, + {0x04, 0xC4}, {0x3F, 0xC5}, + {0x00, 0xC6}, {0x00, 0xC7}, + {0x50, 0xC8}, {0x3C, 0xC9}, + {0x28, 0xCA}, {0xD8, 0xCB}, + {0x14, 0xCC}, {0xEC, 0xCD}, + {0x32, 0xCE}, {0xDD, 0xCF}, + {0x32, 0xD0}, {0xDD, 0xD1}, + {0x6A, 0xD2}, {0x50, 0xD3}, + {0x00, 0xD4}, {0x00, 0xD5}, + {0x00, 0xD6}); + break; + default: + break; + } + + err += sn9c102_i2c_write(cam, 0x20, 0x00); + err += sn9c102_i2c_write(cam, 0x21, 0xd6); + err += sn9c102_i2c_write(cam, 0x25, 0x06); + + return err; +} + + +static int hv7131r_get_ctrl(struct sn9c102_device* cam, + struct v4l2_control* ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_GAIN: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x30)) < 0) + return -EIO; + return 0; + case V4L2_CID_RED_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x31)) < 0) + return -EIO; + ctrl->value = ctrl->value & 0x3f; + return 0; + case V4L2_CID_BLUE_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x33)) < 0) + return -EIO; + ctrl->value = ctrl->value & 0x3f; + return 0; + case SN9C102_V4L2_CID_GREEN_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x32)) < 0) + return -EIO; + ctrl->value = ctrl->value & 0x3f; + return 0; + case V4L2_CID_BLACK_LEVEL: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x01)) < 0) + return -EIO; + ctrl->value = (ctrl->value & 0x08) ? 1 : 0; + return 0; + default: + return -EINVAL; + } +} + + +static int hv7131r_set_ctrl(struct sn9c102_device* cam, + const struct v4l2_control* ctrl) +{ + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_GAIN: + err += sn9c102_i2c_write(cam, 0x30, ctrl->value); + break; + case V4L2_CID_RED_BALANCE: + err += sn9c102_i2c_write(cam, 0x31, ctrl->value); + break; + case V4L2_CID_BLUE_BALANCE: + err += sn9c102_i2c_write(cam, 0x33, ctrl->value); + break; + case SN9C102_V4L2_CID_GREEN_BALANCE: + err += sn9c102_i2c_write(cam, 0x32, ctrl->value); + break; + case V4L2_CID_BLACK_LEVEL: + { + int r = sn9c102_i2c_read(cam, 0x01); + if (r < 0) + return -EIO; + err += sn9c102_i2c_write(cam, 0x01, + (ctrl->value<<3) | (r&0xf7)); + } + break; + default: + return -EINVAL; + } + + return err ? -EIO : 0; +} + + +static int hv7131r_set_crop(struct sn9c102_device* cam, + const struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + int err = 0; + u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 1, + v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1; + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + + return err; +} + + +static int hv7131r_set_pix_format(struct sn9c102_device* cam, + const struct v4l2_pix_format* pix) +{ + int err = 0; + + switch (sn9c102_get_bridge(cam)) { + case BRIDGE_SN9C103: + if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) { + err += sn9c102_write_reg(cam, 0xa0, 0x19); + err += sn9c102_i2c_write(cam, 0x01, 0x04); + } else { + err += sn9c102_write_reg(cam, 0x30, 0x19); + err += sn9c102_i2c_write(cam, 0x01, 0x04); + } + break; + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) { + err += sn9c102_write_reg(cam, 0xa5, 0x17); + err += sn9c102_i2c_write(cam, 0x01, 0x24); + } else { + err += sn9c102_write_reg(cam, 0xa3, 0x17); + err += sn9c102_i2c_write(cam, 0x01, 0x04); + } + break; + default: + break; + } + + return err; +} + + +static const struct sn9c102_sensor hv7131r = { + .name = "HV7131R", + .maintainer = "Luca Risolia ", + .supported_bridge = BRIDGE_SN9C103 | BRIDGE_SN9C105 | BRIDGE_SN9C120, + .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, + .frequency = SN9C102_I2C_100KHZ, + .interface = SN9C102_I2C_2WIRES, + .i2c_slave_id = 0x11, + .init = &hv7131r_init, + .qctrl = { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "global gain", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x01, + .default_value = 0x40, + .flags = 0, + }, + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "red balance", + .minimum = 0x00, + .maximum = 0x3f, + .step = 0x01, + .default_value = 0x08, + .flags = 0, + }, + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "blue balance", + .minimum = 0x00, + .maximum = 0x3f, + .step = 0x01, + .default_value = 0x1a, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_GREEN_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "green balance", + .minimum = 0x00, + .maximum = 0x3f, + .step = 0x01, + .default_value = 0x2f, + .flags = 0, + }, + { + .id = V4L2_CID_BLACK_LEVEL, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "auto black level compensation", + .minimum = 0x00, + .maximum = 0x01, + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + }, + .get_ctrl = &hv7131r_get_ctrl, + .set_ctrl = &hv7131r_set_ctrl, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + }, + .set_crop = &hv7131r_set_crop, + .pix_format = { + .width = 640, + .height = 480, + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .priv = 8, + }, + .set_pix_format = &hv7131r_set_pix_format +}; + + +int sn9c102_probe_hv7131r(struct sn9c102_device* cam) +{ + int devid, err; + + err = sn9c102_write_const_regs(cam, {0x09, 0x01}, {0x44, 0x02}, + {0x34, 0x01}, {0x20, 0x17}, + {0x34, 0x01}, {0x46, 0x01}); + + devid = sn9c102_i2c_try_read(cam, &hv7131r, 0x00); + if (err || devid < 0) + return -EIO; + + if (devid != 0x02) + return -ENODEV; + + sn9c102_attach_sensor(cam, &hv7131r); + + return 0; +} diff --git a/drivers/staging/media/sn9c102/sn9c102_mi0343.c b/drivers/staging/media/sn9c102/sn9c102_mi0343.c new file mode 100644 index 000000000000..1f5b09bec89c --- /dev/null +++ b/drivers/staging/media/sn9c102/sn9c102_mi0343.c @@ -0,0 +1,352 @@ +/*************************************************************************** + * Plug-in for MI-0343 image sensor connected to the SN9C1xx PC Camera * + * Controllers * + * * + * Copyright (C) 2004-2007 by Luca Risolia * + * * + * 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. * + ***************************************************************************/ + +#include "sn9c102_sensor.h" +#include "sn9c102_devtable.h" + + +static int mi0343_init(struct sn9c102_device* cam) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + int err = 0; + + err = sn9c102_write_const_regs(cam, {0x00, 0x10}, {0x00, 0x11}, + {0x0a, 0x14}, {0x40, 0x01}, + {0x20, 0x17}, {0x07, 0x18}, + {0xa0, 0x19}); + + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d, + 0x00, 0x01, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d, + 0x00, 0x00, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x03, + 0x01, 0xe1, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x04, + 0x02, 0x81, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x05, + 0x00, 0x17, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x06, + 0x00, 0x11, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x62, + 0x04, 0x9a, 0, 0); + + return err; +} + + +static int mi0343_get_ctrl(struct sn9c102_device* cam, + struct v4l2_control* ctrl) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + u8 data[2]; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x09, 2, + data) < 0) + return -EIO; + ctrl->value = data[0]; + return 0; + case V4L2_CID_GAIN: + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x35, 2, + data) < 0) + return -EIO; + break; + case V4L2_CID_HFLIP: + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20, 2, + data) < 0) + return -EIO; + ctrl->value = data[1] & 0x20 ? 1 : 0; + return 0; + case V4L2_CID_VFLIP: + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20, 2, + data) < 0) + return -EIO; + ctrl->value = data[1] & 0x80 ? 1 : 0; + return 0; + case V4L2_CID_RED_BALANCE: + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2d, 2, + data) < 0) + return -EIO; + break; + case V4L2_CID_BLUE_BALANCE: + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2c, 2, + data) < 0) + return -EIO; + break; + case SN9C102_V4L2_CID_GREEN_BALANCE: + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2e, 2, + data) < 0) + return -EIO; + break; + default: + return -EINVAL; + } + + switch (ctrl->id) { + case V4L2_CID_GAIN: + case V4L2_CID_RED_BALANCE: + case V4L2_CID_BLUE_BALANCE: + case SN9C102_V4L2_CID_GREEN_BALANCE: + ctrl->value = data[1] | (data[0] << 8); + if (ctrl->value >= 0x10 && ctrl->value <= 0x3f) + ctrl->value -= 0x10; + else if (ctrl->value >= 0x60 && ctrl->value <= 0x7f) + ctrl->value -= 0x60; + else if (ctrl->value >= 0xe0 && ctrl->value <= 0xff) + ctrl->value -= 0xe0; + } + + return 0; +} + + +static int mi0343_set_ctrl(struct sn9c102_device* cam, + const struct v4l2_control* ctrl) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + u16 reg = 0; + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_GAIN: + case V4L2_CID_RED_BALANCE: + case V4L2_CID_BLUE_BALANCE: + case SN9C102_V4L2_CID_GREEN_BALANCE: + if (ctrl->value <= (0x3f-0x10)) + reg = 0x10 + ctrl->value; + else if (ctrl->value <= ((0x3f-0x10) + (0x7f-0x60))) + reg = 0x60 + (ctrl->value - (0x3f-0x10)); + else + reg = 0xe0 + (ctrl->value - (0x3f-0x10) - (0x7f-0x60)); + break; + } + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x09, ctrl->value, 0x00, + 0, 0); + break; + case V4L2_CID_GAIN: + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x35, reg >> 8, reg & 0xff, + 0, 0); + break; + case V4L2_CID_HFLIP: + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x20, ctrl->value ? 0x40:0x00, + ctrl->value ? 0x20:0x00, + 0, 0); + break; + case V4L2_CID_VFLIP: + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x20, ctrl->value ? 0x80:0x00, + ctrl->value ? 0x80:0x00, + 0, 0); + break; + case V4L2_CID_RED_BALANCE: + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x2d, reg >> 8, reg & 0xff, + 0, 0); + break; + case V4L2_CID_BLUE_BALANCE: + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x2c, reg >> 8, reg & 0xff, + 0, 0); + break; + case SN9C102_V4L2_CID_GREEN_BALANCE: + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x2b, reg >> 8, reg & 0xff, + 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x2e, reg >> 8, reg & 0xff, + 0, 0); + break; + default: + return -EINVAL; + } + + return err ? -EIO : 0; +} + + +static int mi0343_set_crop(struct sn9c102_device* cam, + const struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + int err = 0; + u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 0, + v_start = (u8)(rect->top - s->cropcap.bounds.top) + 2; + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + + return err; +} + + +static int mi0343_set_pix_format(struct sn9c102_device* cam, + const struct v4l2_pix_format* pix) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + int err = 0; + + if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) { + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x0a, 0x00, 0x03, 0, 0); + err += sn9c102_write_reg(cam, 0x20, 0x19); + } else { + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x0a, 0x00, 0x05, 0, 0); + err += sn9c102_write_reg(cam, 0xa0, 0x19); + } + + return err; +} + + +static const struct sn9c102_sensor mi0343 = { + .name = "MI-0343", + .maintainer = "Luca Risolia ", + .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102, + .frequency = SN9C102_I2C_100KHZ, + .interface = SN9C102_I2C_2WIRES, + .i2c_slave_id = 0x5d, + .init = &mi0343_init, + .qctrl = { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = 0x00, + .maximum = 0x0f, + .step = 0x01, + .default_value = 0x06, + .flags = 0, + }, + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "global gain", + .minimum = 0x00, + .maximum = (0x3f-0x10)+(0x7f-0x60)+(0xff-0xe0),/*0x6d*/ + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "horizontal mirror", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + .flags = 0, + }, + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "vertical mirror", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + .flags = 0, + }, + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "red balance", + .minimum = 0x00, + .maximum = (0x3f-0x10)+(0x7f-0x60)+(0xff-0xe0), + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "blue balance", + .minimum = 0x00, + .maximum = (0x3f-0x10)+(0x7f-0x60)+(0xff-0xe0), + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_GREEN_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "green balance", + .minimum = 0x00, + .maximum = ((0x3f-0x10)+(0x7f-0x60)+(0xff-0xe0)), + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + }, + .get_ctrl = &mi0343_get_ctrl, + .set_ctrl = &mi0343_set_ctrl, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + }, + .set_crop = &mi0343_set_crop, + .pix_format = { + .width = 640, + .height = 480, + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .priv = 8, + }, + .set_pix_format = &mi0343_set_pix_format +}; + + +int sn9c102_probe_mi0343(struct sn9c102_device* cam) +{ + u8 data[2]; + + if (sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x00, 0x01}, + {0x28, 0x17})) + return -EIO; + + if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id, 0x00, + 2, data) < 0) + return -EIO; + + if (data[1] != 0x42 || data[0] != 0xe3) + return -ENODEV; + + sn9c102_attach_sensor(cam, &mi0343); + + return 0; +} diff --git a/drivers/staging/media/sn9c102/sn9c102_mi0360.c b/drivers/staging/media/sn9c102/sn9c102_mi0360.c new file mode 100644 index 000000000000..d973fc1973d9 --- /dev/null +++ b/drivers/staging/media/sn9c102/sn9c102_mi0360.c @@ -0,0 +1,453 @@ +/*************************************************************************** + * Plug-in for MI-0360 image sensor connected to the SN9C1xx PC Camera * + * Controllers * + * * + * Copyright (C) 2007 by Luca Risolia * + * * + * 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. * + ***************************************************************************/ + +#include "sn9c102_sensor.h" +#include "sn9c102_devtable.h" + + +static int mi0360_init(struct sn9c102_device* cam) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + int err = 0; + + switch (sn9c102_get_bridge(cam)) { + case BRIDGE_SN9C103: + err = sn9c102_write_const_regs(cam, {0x00, 0x10}, {0x00, 0x11}, + {0x0a, 0x14}, {0x40, 0x01}, + {0x20, 0x17}, {0x07, 0x18}, + {0xa0, 0x19}, {0x02, 0x1c}, + {0x03, 0x1d}, {0x0f, 0x1e}, + {0x0c, 0x1f}, {0x00, 0x20}, + {0x10, 0x21}, {0x20, 0x22}, + {0x30, 0x23}, {0x40, 0x24}, + {0x50, 0x25}, {0x60, 0x26}, + {0x70, 0x27}, {0x80, 0x28}, + {0x90, 0x29}, {0xa0, 0x2a}, + {0xb0, 0x2b}, {0xc0, 0x2c}, + {0xd0, 0x2d}, {0xe0, 0x2e}, + {0xf0, 0x2f}, {0xff, 0x30}); + break; + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + err = sn9c102_write_const_regs(cam, {0x44, 0x01}, {0x40, 0x02}, + {0x00, 0x03}, {0x1a, 0x04}, + {0x50, 0x05}, {0x20, 0x06}, + {0x10, 0x07}, {0x03, 0x10}, + {0x08, 0x14}, {0xa2, 0x17}, + {0x47, 0x18}, {0x00, 0x19}, + {0x1d, 0x1a}, {0x10, 0x1b}, + {0x02, 0x1c}, {0x03, 0x1d}, + {0x0f, 0x1e}, {0x0c, 0x1f}, + {0x00, 0x20}, {0x29, 0x21}, + {0x40, 0x22}, {0x54, 0x23}, + {0x66, 0x24}, {0x76, 0x25}, + {0x85, 0x26}, {0x94, 0x27}, + {0xa1, 0x28}, {0xae, 0x29}, + {0xbb, 0x2a}, {0xc7, 0x2b}, + {0xd3, 0x2c}, {0xde, 0x2d}, + {0xea, 0x2e}, {0xf4, 0x2f}, + {0xff, 0x30}, {0x00, 0x3F}, + {0xC7, 0x40}, {0x01, 0x41}, + {0x44, 0x42}, {0x00, 0x43}, + {0x44, 0x44}, {0x00, 0x45}, + {0x44, 0x46}, {0x00, 0x47}, + {0xC7, 0x48}, {0x01, 0x49}, + {0xC7, 0x4A}, {0x01, 0x4B}, + {0xC7, 0x4C}, {0x01, 0x4D}, + {0x44, 0x4E}, {0x00, 0x4F}, + {0x44, 0x50}, {0x00, 0x51}, + {0x44, 0x52}, {0x00, 0x53}, + {0xC7, 0x54}, {0x01, 0x55}, + {0xC7, 0x56}, {0x01, 0x57}, + {0xC7, 0x58}, {0x01, 0x59}, + {0x44, 0x5A}, {0x00, 0x5B}, + {0x44, 0x5C}, {0x00, 0x5D}, + {0x44, 0x5E}, {0x00, 0x5F}, + {0xC7, 0x60}, {0x01, 0x61}, + {0xC7, 0x62}, {0x01, 0x63}, + {0xC7, 0x64}, {0x01, 0x65}, + {0x44, 0x66}, {0x00, 0x67}, + {0x44, 0x68}, {0x00, 0x69}, + {0x44, 0x6A}, {0x00, 0x6B}, + {0xC7, 0x6C}, {0x01, 0x6D}, + {0xC7, 0x6E}, {0x01, 0x6F}, + {0xC7, 0x70}, {0x01, 0x71}, + {0x44, 0x72}, {0x00, 0x73}, + {0x44, 0x74}, {0x00, 0x75}, + {0x44, 0x76}, {0x00, 0x77}, + {0xC7, 0x78}, {0x01, 0x79}, + {0xC7, 0x7A}, {0x01, 0x7B}, + {0xC7, 0x7C}, {0x01, 0x7D}, + {0x44, 0x7E}, {0x00, 0x7F}, + {0x14, 0x84}, {0x00, 0x85}, + {0x27, 0x86}, {0x00, 0x87}, + {0x07, 0x88}, {0x00, 0x89}, + {0xEC, 0x8A}, {0x0f, 0x8B}, + {0xD8, 0x8C}, {0x0f, 0x8D}, + {0x3D, 0x8E}, {0x00, 0x8F}, + {0x3D, 0x90}, {0x00, 0x91}, + {0xCD, 0x92}, {0x0f, 0x93}, + {0xf7, 0x94}, {0x0f, 0x95}, + {0x0C, 0x96}, {0x00, 0x97}, + {0x00, 0x98}, {0x66, 0x99}, + {0x05, 0x9A}, {0x00, 0x9B}, + {0x04, 0x9C}, {0x00, 0x9D}, + {0x08, 0x9E}, {0x00, 0x9F}, + {0x2D, 0xC0}, {0x2D, 0xC1}, + {0x3A, 0xC2}, {0x05, 0xC3}, + {0x04, 0xC4}, {0x3F, 0xC5}, + {0x00, 0xC6}, {0x00, 0xC7}, + {0x50, 0xC8}, {0x3C, 0xC9}, + {0x28, 0xCA}, {0xD8, 0xCB}, + {0x14, 0xCC}, {0xEC, 0xCD}, + {0x32, 0xCE}, {0xDD, 0xCF}, + {0x32, 0xD0}, {0xDD, 0xD1}, + {0x6A, 0xD2}, {0x50, 0xD3}, + {0x00, 0xD4}, {0x00, 0xD5}, + {0x00, 0xD6}); + break; + default: + break; + } + + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d, + 0x00, 0x01, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d, + 0x00, 0x00, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x03, + 0x01, 0xe1, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x04, + 0x02, 0x81, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x05, + 0x00, 0x17, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x06, + 0x00, 0x11, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x62, + 0x04, 0x9a, 0, 0); + + return err; +} + + +static int mi0360_get_ctrl(struct sn9c102_device* cam, + struct v4l2_control* ctrl) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + u8 data[2]; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x09, 2, + data) < 0) + return -EIO; + ctrl->value = data[0]; + return 0; + case V4L2_CID_GAIN: + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x35, 2, + data) < 0) + return -EIO; + ctrl->value = data[1]; + return 0; + case V4L2_CID_RED_BALANCE: + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2c, 2, + data) < 0) + return -EIO; + ctrl->value = data[1]; + return 0; + case V4L2_CID_BLUE_BALANCE: + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2d, 2, + data) < 0) + return -EIO; + ctrl->value = data[1]; + return 0; + case SN9C102_V4L2_CID_GREEN_BALANCE: + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2e, 2, + data) < 0) + return -EIO; + ctrl->value = data[1]; + return 0; + case V4L2_CID_HFLIP: + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20, 2, + data) < 0) + return -EIO; + ctrl->value = data[1] & 0x20 ? 1 : 0; + return 0; + case V4L2_CID_VFLIP: + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20, 2, + data) < 0) + return -EIO; + ctrl->value = data[1] & 0x80 ? 1 : 0; + return 0; + default: + return -EINVAL; + } + + return 0; +} + + +static int mi0360_set_ctrl(struct sn9c102_device* cam, + const struct v4l2_control* ctrl) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x09, ctrl->value, 0x00, + 0, 0); + break; + case V4L2_CID_GAIN: + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x35, 0x03, ctrl->value, + 0, 0); + break; + case V4L2_CID_RED_BALANCE: + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x2c, 0x03, ctrl->value, + 0, 0); + break; + case V4L2_CID_BLUE_BALANCE: + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x2d, 0x03, ctrl->value, + 0, 0); + break; + case SN9C102_V4L2_CID_GREEN_BALANCE: + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x2b, 0x03, ctrl->value, + 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x2e, 0x03, ctrl->value, + 0, 0); + break; + case V4L2_CID_HFLIP: + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x20, ctrl->value ? 0x40:0x00, + ctrl->value ? 0x20:0x00, + 0, 0); + break; + case V4L2_CID_VFLIP: + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x20, ctrl->value ? 0x80:0x00, + ctrl->value ? 0x80:0x00, + 0, 0); + break; + default: + return -EINVAL; + } + + return err ? -EIO : 0; +} + + +static int mi0360_set_crop(struct sn9c102_device* cam, + const struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + int err = 0; + u8 h_start = 0, v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1; + + switch (sn9c102_get_bridge(cam)) { + case BRIDGE_SN9C103: + h_start = (u8)(rect->left - s->cropcap.bounds.left) + 0; + break; + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + h_start = (u8)(rect->left - s->cropcap.bounds.left) + 1; + break; + default: + break; + } + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + + return err; +} + + +static int mi0360_set_pix_format(struct sn9c102_device* cam, + const struct v4l2_pix_format* pix) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + int err = 0; + + if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) { + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x0a, 0x00, 0x05, 0, 0); + err += sn9c102_write_reg(cam, 0x60, 0x19); + if (sn9c102_get_bridge(cam) == BRIDGE_SN9C105 || + sn9c102_get_bridge(cam) == BRIDGE_SN9C120) + err += sn9c102_write_reg(cam, 0xa6, 0x17); + } else { + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x0a, 0x00, 0x02, 0, 0); + err += sn9c102_write_reg(cam, 0x20, 0x19); + if (sn9c102_get_bridge(cam) == BRIDGE_SN9C105 || + sn9c102_get_bridge(cam) == BRIDGE_SN9C120) + err += sn9c102_write_reg(cam, 0xa2, 0x17); + } + + return err; +} + + +static const struct sn9c102_sensor mi0360 = { + .name = "MI-0360", + .maintainer = "Luca Risolia ", + .supported_bridge = BRIDGE_SN9C103 | BRIDGE_SN9C105 | BRIDGE_SN9C120, + .frequency = SN9C102_I2C_100KHZ, + .interface = SN9C102_I2C_2WIRES, + .i2c_slave_id = 0x5d, + .init = &mi0360_init, + .qctrl = { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = 0x00, + .maximum = 0x0f, + .step = 0x01, + .default_value = 0x05, + .flags = 0, + }, + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "global gain", + .minimum = 0x00, + .maximum = 0x7f, + .step = 0x01, + .default_value = 0x25, + .flags = 0, + }, + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "horizontal mirror", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + .flags = 0, + }, + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "vertical mirror", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + .flags = 0, + }, + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "blue balance", + .minimum = 0x00, + .maximum = 0x7f, + .step = 0x01, + .default_value = 0x0f, + .flags = 0, + }, + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "red balance", + .minimum = 0x00, + .maximum = 0x7f, + .step = 0x01, + .default_value = 0x32, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_GREEN_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "green balance", + .minimum = 0x00, + .maximum = 0x7f, + .step = 0x01, + .default_value = 0x25, + .flags = 0, + }, + }, + .get_ctrl = &mi0360_get_ctrl, + .set_ctrl = &mi0360_set_ctrl, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + }, + .set_crop = &mi0360_set_crop, + .pix_format = { + .width = 640, + .height = 480, + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .priv = 8, + }, + .set_pix_format = &mi0360_set_pix_format +}; + + +int sn9c102_probe_mi0360(struct sn9c102_device* cam) +{ + + u8 data[2]; + + switch (sn9c102_get_bridge(cam)) { + case BRIDGE_SN9C103: + if (sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x00, 0x01}, + {0x28, 0x17})) + return -EIO; + break; + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + if (sn9c102_write_const_regs(cam, {0x01, 0xf1}, {0x00, 0xf1}, + {0x01, 0x01}, {0x00, 0x01}, + {0x28, 0x17})) + return -EIO; + break; + default: + break; + } + + if (sn9c102_i2c_try_raw_read(cam, &mi0360, mi0360.i2c_slave_id, 0x00, + 2, data) < 0) + return -EIO; + + if (data[0] != 0x82 || data[1] != 0x43) + return -ENODEV; + + sn9c102_attach_sensor(cam, &mi0360); + + return 0; +} diff --git a/drivers/staging/media/sn9c102/sn9c102_mt9v111.c b/drivers/staging/media/sn9c102/sn9c102_mt9v111.c new file mode 100644 index 000000000000..95986eb492e4 --- /dev/null +++ b/drivers/staging/media/sn9c102/sn9c102_mt9v111.c @@ -0,0 +1,260 @@ +/*************************************************************************** + * Plug-in for MT9V111 image sensor connected to the SN9C1xx PC Camera * + * Controllers * + * * + * Copyright (C) 2007 by Luca Risolia * + * * + * 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. * + ***************************************************************************/ + +#include "sn9c102_sensor.h" +#include "sn9c102_devtable.h" + + +static int mt9v111_init(struct sn9c102_device *cam) +{ + struct sn9c102_sensor *s = sn9c102_get_sensor(cam); + int err = 0; + + err = sn9c102_write_const_regs(cam, {0x44, 0x01}, {0x40, 0x02}, + {0x00, 0x03}, {0x1a, 0x04}, + {0x1f, 0x05}, {0x20, 0x06}, + {0x1f, 0x07}, {0x81, 0x08}, + {0x5c, 0x09}, {0x00, 0x0a}, + {0x00, 0x0b}, {0x00, 0x0c}, + {0x00, 0x0d}, {0x00, 0x0e}, + {0x00, 0x0f}, {0x03, 0x10}, + {0x00, 0x11}, {0x00, 0x12}, + {0x02, 0x13}, {0x14, 0x14}, + {0x28, 0x15}, {0x1e, 0x16}, + {0xe2, 0x17}, {0x06, 0x18}, + {0x00, 0x19}, {0x00, 0x1a}, + {0x00, 0x1b}, {0x08, 0x20}, + {0x39, 0x21}, {0x51, 0x22}, + {0x63, 0x23}, {0x73, 0x24}, + {0x82, 0x25}, {0x8f, 0x26}, + {0x9b, 0x27}, {0xa7, 0x28}, + {0xb1, 0x29}, {0xbc, 0x2a}, + {0xc6, 0x2b}, {0xcf, 0x2c}, + {0xd8, 0x2d}, {0xe1, 0x2e}, + {0xea, 0x2f}, {0xf2, 0x30}, + {0x13, 0x84}, {0x00, 0x85}, + {0x25, 0x86}, {0x00, 0x87}, + {0x07, 0x88}, {0x00, 0x89}, + {0xee, 0x8a}, {0x0f, 0x8b}, + {0xe5, 0x8c}, {0x0f, 0x8d}, + {0x2e, 0x8e}, {0x00, 0x8f}, + {0x30, 0x90}, {0x00, 0x91}, + {0xd4, 0x92}, {0x0f, 0x93}, + {0xfc, 0x94}, {0x0f, 0x95}, + {0x14, 0x96}, {0x00, 0x97}, + {0x00, 0x98}, {0x60, 0x99}, + {0x07, 0x9a}, {0x40, 0x9b}, + {0x20, 0x9c}, {0x00, 0x9d}, + {0x00, 0x9e}, {0x00, 0x9f}, + {0x2d, 0xc0}, {0x2d, 0xc1}, + {0x3a, 0xc2}, {0x05, 0xc3}, + {0x04, 0xc4}, {0x3f, 0xc5}, + {0x00, 0xc6}, {0x00, 0xc7}, + {0x50, 0xc8}, {0x3c, 0xc9}, + {0x28, 0xca}, {0xd8, 0xcb}, + {0x14, 0xcc}, {0xec, 0xcd}, + {0x32, 0xce}, {0xdd, 0xcf}, + {0x2d, 0xd0}, {0xdd, 0xd1}, + {0x6a, 0xd2}, {0x50, 0xd3}, + {0x60, 0xd4}, {0x00, 0xd5}, + {0x00, 0xd6}); + + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x01, + 0x00, 0x01, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d, + 0x00, 0x01, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d, + 0x00, 0x00, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x08, + 0x04, 0x80, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x01, + 0x00, 0x04, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x08, + 0x00, 0x08, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x02, + 0x00, 0x16, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x03, + 0x01, 0xe7, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x04, + 0x02, 0x87, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x06, + 0x00, 0x40, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x05, + 0x00, 0x09, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x07, + 0x30, 0x02, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0c, + 0x00, 0x00, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x12, + 0x00, 0xb0, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x13, + 0x00, 0x7c, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x1e, + 0x00, 0x00, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x20, + 0x00, 0x00, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x20, + 0x00, 0x00, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x01, + 0x00, 0x04, 0, 0); + + return err; +} + +static int mt9v111_get_ctrl(struct sn9c102_device *cam, + struct v4l2_control *ctrl) +{ + struct sn9c102_sensor *s = sn9c102_get_sensor(cam); + u8 data[2]; + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_VFLIP: + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20, 2, + data) < 0) + return -EIO; + ctrl->value = data[1] & 0x80 ? 1 : 0; + return 0; + default: + return -EINVAL; + } + + return err ? -EIO : 0; +} + +static int mt9v111_set_ctrl(struct sn9c102_device *cam, + const struct v4l2_control *ctrl) +{ + struct sn9c102_sensor *s = sn9c102_get_sensor(cam); + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_VFLIP: + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x20, + ctrl->value ? 0x80 : 0x00, + ctrl->value ? 0x80 : 0x00, 0, + 0); + break; + default: + return -EINVAL; + } + + return err ? -EIO : 0; +} + +static int mt9v111_set_crop(struct sn9c102_device *cam, + const struct v4l2_rect *rect) +{ + struct sn9c102_sensor *s = sn9c102_get_sensor(cam); + int err = 0; + u8 v_start = (u8) (rect->top - s->cropcap.bounds.top) + 2; + + err += sn9c102_write_reg(cam, v_start, 0x13); + + return err; +} + +static int mt9v111_set_pix_format(struct sn9c102_device *cam, + const struct v4l2_pix_format *pix) +{ + int err = 0; + + if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) { + err += sn9c102_write_reg(cam, 0xb4, 0x17); + } else { + err += sn9c102_write_reg(cam, 0xe2, 0x17); + } + + return err; +} + + +static const struct sn9c102_sensor mt9v111 = { + .name = "MT9V111", + .maintainer = "Luca Risolia ", + .supported_bridge = BRIDGE_SN9C105 | BRIDGE_SN9C120, + .frequency = SN9C102_I2C_100KHZ, + .interface = SN9C102_I2C_2WIRES, + .i2c_slave_id = 0x5c, + .init = &mt9v111_init, + .qctrl = { + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "vertical mirror", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + .flags = 0, + }, + }, + .get_ctrl = &mt9v111_get_ctrl, + .set_ctrl = &mt9v111_set_ctrl, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + }, + .set_crop = &mt9v111_set_crop, + .pix_format = { + .width = 640, + .height = 480, + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .priv = 8, + }, + .set_pix_format = &mt9v111_set_pix_format +}; + + +int sn9c102_probe_mt9v111(struct sn9c102_device *cam) +{ + u8 data[2]; + int err = 0; + + err += sn9c102_write_const_regs(cam, {0x01, 0xf1}, {0x00, 0xf1}, + {0x29, 0x01}, {0x42, 0x17}, + {0x62, 0x17}, {0x08, 0x01}); + err += sn9c102_i2c_try_raw_write(cam, &mt9v111, 4, + mt9v111.i2c_slave_id, 0x01, 0x00, + 0x04, 0, 0); + if (err || sn9c102_i2c_try_raw_read(cam, &mt9v111, + mt9v111.i2c_slave_id, 0x36, 2, + data) < 0) + return -EIO; + + if (data[0] != 0x82 || data[1] != 0x3a) + return -ENODEV; + + sn9c102_attach_sensor(cam, &mt9v111); + + return 0; +} diff --git a/drivers/staging/media/sn9c102/sn9c102_ov7630.c b/drivers/staging/media/sn9c102/sn9c102_ov7630.c new file mode 100644 index 000000000000..803712c29f02 --- /dev/null +++ b/drivers/staging/media/sn9c102/sn9c102_ov7630.c @@ -0,0 +1,626 @@ +/*************************************************************************** + * Plug-in for OV7630 image sensor connected to the SN9C1xx PC Camera * + * Controllers * + * * + * Copyright (C) 2006-2007 by Luca Risolia * + * * + * 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. * + ***************************************************************************/ + +#include "sn9c102_sensor.h" +#include "sn9c102_devtable.h" + + +static int ov7630_init(struct sn9c102_device* cam) +{ + int err = 0; + + switch (sn9c102_get_bridge(cam)) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + err = sn9c102_write_const_regs(cam, {0x00, 0x14}, {0x60, 0x17}, + {0x0f, 0x18}, {0x50, 0x19}); + + err += sn9c102_i2c_write(cam, 0x12, 0x8d); + err += sn9c102_i2c_write(cam, 0x12, 0x0d); + err += sn9c102_i2c_write(cam, 0x11, 0x00); + err += sn9c102_i2c_write(cam, 0x15, 0x35); + err += sn9c102_i2c_write(cam, 0x16, 0x03); + err += sn9c102_i2c_write(cam, 0x17, 0x1c); + err += sn9c102_i2c_write(cam, 0x18, 0xbd); + err += sn9c102_i2c_write(cam, 0x19, 0x06); + err += sn9c102_i2c_write(cam, 0x1a, 0xf6); + err += sn9c102_i2c_write(cam, 0x1b, 0x04); + err += sn9c102_i2c_write(cam, 0x20, 0x44); + err += sn9c102_i2c_write(cam, 0x23, 0xee); + err += sn9c102_i2c_write(cam, 0x26, 0xa0); + err += sn9c102_i2c_write(cam, 0x27, 0x9a); + err += sn9c102_i2c_write(cam, 0x28, 0x20); + err += sn9c102_i2c_write(cam, 0x29, 0x30); + err += sn9c102_i2c_write(cam, 0x2f, 0x3d); + err += sn9c102_i2c_write(cam, 0x30, 0x24); + err += sn9c102_i2c_write(cam, 0x32, 0x86); + err += sn9c102_i2c_write(cam, 0x60, 0xa9); + err += sn9c102_i2c_write(cam, 0x61, 0x42); + err += sn9c102_i2c_write(cam, 0x65, 0x00); + err += sn9c102_i2c_write(cam, 0x69, 0x38); + err += sn9c102_i2c_write(cam, 0x6f, 0x88); + err += sn9c102_i2c_write(cam, 0x70, 0x0b); + err += sn9c102_i2c_write(cam, 0x71, 0x00); + err += sn9c102_i2c_write(cam, 0x74, 0x21); + err += sn9c102_i2c_write(cam, 0x7d, 0xf7); + break; + case BRIDGE_SN9C103: + err = sn9c102_write_const_regs(cam, {0x00, 0x02}, {0x00, 0x03}, + {0x1a, 0x04}, {0x20, 0x05}, + {0x20, 0x06}, {0x20, 0x07}, + {0x03, 0x10}, {0x0a, 0x14}, + {0x60, 0x17}, {0x0f, 0x18}, + {0x50, 0x19}, {0x1d, 0x1a}, + {0x10, 0x1b}, {0x02, 0x1c}, + {0x03, 0x1d}, {0x0f, 0x1e}, + {0x0c, 0x1f}, {0x00, 0x20}, + {0x10, 0x21}, {0x20, 0x22}, + {0x30, 0x23}, {0x40, 0x24}, + {0x50, 0x25}, {0x60, 0x26}, + {0x70, 0x27}, {0x80, 0x28}, + {0x90, 0x29}, {0xa0, 0x2a}, + {0xb0, 0x2b}, {0xc0, 0x2c}, + {0xd0, 0x2d}, {0xe0, 0x2e}, + {0xf0, 0x2f}, {0xff, 0x30}); + + err += sn9c102_i2c_write(cam, 0x12, 0x8d); + err += sn9c102_i2c_write(cam, 0x12, 0x0d); + err += sn9c102_i2c_write(cam, 0x15, 0x34); + err += sn9c102_i2c_write(cam, 0x11, 0x01); + err += sn9c102_i2c_write(cam, 0x1b, 0x04); + err += sn9c102_i2c_write(cam, 0x20, 0x44); + err += sn9c102_i2c_write(cam, 0x23, 0xee); + err += sn9c102_i2c_write(cam, 0x26, 0xa0); + err += sn9c102_i2c_write(cam, 0x27, 0x9a); + err += sn9c102_i2c_write(cam, 0x28, 0x20); + err += sn9c102_i2c_write(cam, 0x29, 0x30); + err += sn9c102_i2c_write(cam, 0x2f, 0x3d); + err += sn9c102_i2c_write(cam, 0x30, 0x24); + err += sn9c102_i2c_write(cam, 0x32, 0x86); + err += sn9c102_i2c_write(cam, 0x60, 0xa9); + err += sn9c102_i2c_write(cam, 0x61, 0x42); + err += sn9c102_i2c_write(cam, 0x65, 0x00); + err += sn9c102_i2c_write(cam, 0x69, 0x38); + err += sn9c102_i2c_write(cam, 0x6f, 0x88); + err += sn9c102_i2c_write(cam, 0x70, 0x0b); + err += sn9c102_i2c_write(cam, 0x71, 0x00); + err += sn9c102_i2c_write(cam, 0x74, 0x21); + err += sn9c102_i2c_write(cam, 0x7d, 0xf7); + break; + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + err = sn9c102_write_const_regs(cam, {0x40, 0x02}, {0x00, 0x03}, + {0x1a, 0x04}, {0x03, 0x10}, + {0x0a, 0x14}, {0xe2, 0x17}, + {0x0b, 0x18}, {0x00, 0x19}, + {0x1d, 0x1a}, {0x10, 0x1b}, + {0x02, 0x1c}, {0x03, 0x1d}, + {0x0f, 0x1e}, {0x0c, 0x1f}, + {0x00, 0x20}, {0x24, 0x21}, + {0x3b, 0x22}, {0x47, 0x23}, + {0x60, 0x24}, {0x71, 0x25}, + {0x80, 0x26}, {0x8f, 0x27}, + {0x9d, 0x28}, {0xaa, 0x29}, + {0xb8, 0x2a}, {0xc4, 0x2b}, + {0xd1, 0x2c}, {0xdd, 0x2d}, + {0xe8, 0x2e}, {0xf4, 0x2f}, + {0xff, 0x30}, {0x00, 0x3f}, + {0xc7, 0x40}, {0x01, 0x41}, + {0x44, 0x42}, {0x00, 0x43}, + {0x44, 0x44}, {0x00, 0x45}, + {0x44, 0x46}, {0x00, 0x47}, + {0xc7, 0x48}, {0x01, 0x49}, + {0xc7, 0x4a}, {0x01, 0x4b}, + {0xc7, 0x4c}, {0x01, 0x4d}, + {0x44, 0x4e}, {0x00, 0x4f}, + {0x44, 0x50}, {0x00, 0x51}, + {0x44, 0x52}, {0x00, 0x53}, + {0xc7, 0x54}, {0x01, 0x55}, + {0xc7, 0x56}, {0x01, 0x57}, + {0xc7, 0x58}, {0x01, 0x59}, + {0x44, 0x5a}, {0x00, 0x5b}, + {0x44, 0x5c}, {0x00, 0x5d}, + {0x44, 0x5e}, {0x00, 0x5f}, + {0xc7, 0x60}, {0x01, 0x61}, + {0xc7, 0x62}, {0x01, 0x63}, + {0xc7, 0x64}, {0x01, 0x65}, + {0x44, 0x66}, {0x00, 0x67}, + {0x44, 0x68}, {0x00, 0x69}, + {0x44, 0x6a}, {0x00, 0x6b}, + {0xc7, 0x6c}, {0x01, 0x6d}, + {0xc7, 0x6e}, {0x01, 0x6f}, + {0xc7, 0x70}, {0x01, 0x71}, + {0x44, 0x72}, {0x00, 0x73}, + {0x44, 0x74}, {0x00, 0x75}, + {0x44, 0x76}, {0x00, 0x77}, + {0xc7, 0x78}, {0x01, 0x79}, + {0xc7, 0x7a}, {0x01, 0x7b}, + {0xc7, 0x7c}, {0x01, 0x7d}, + {0x44, 0x7e}, {0x00, 0x7f}, + {0x17, 0x84}, {0x00, 0x85}, + {0x2e, 0x86}, {0x00, 0x87}, + {0x09, 0x88}, {0x00, 0x89}, + {0xe8, 0x8a}, {0x0f, 0x8b}, + {0xda, 0x8c}, {0x0f, 0x8d}, + {0x40, 0x8e}, {0x00, 0x8f}, + {0x37, 0x90}, {0x00, 0x91}, + {0xcf, 0x92}, {0x0f, 0x93}, + {0xfa, 0x94}, {0x0f, 0x95}, + {0x00, 0x96}, {0x00, 0x97}, + {0x00, 0x98}, {0x66, 0x99}, + {0x00, 0x9a}, {0x40, 0x9b}, + {0x20, 0x9c}, {0x00, 0x9d}, + {0x00, 0x9e}, {0x00, 0x9f}, + {0x2d, 0xc0}, {0x2d, 0xc1}, + {0x3a, 0xc2}, {0x00, 0xc3}, + {0x04, 0xc4}, {0x3f, 0xc5}, + {0x00, 0xc6}, {0x00, 0xc7}, + {0x50, 0xc8}, {0x3c, 0xc9}, + {0x28, 0xca}, {0xd8, 0xcb}, + {0x14, 0xcc}, {0xec, 0xcd}, + {0x32, 0xce}, {0xdd, 0xcf}, + {0x32, 0xd0}, {0xdd, 0xd1}, + {0x6a, 0xd2}, {0x50, 0xd3}, + {0x60, 0xd4}, {0x00, 0xd5}, + {0x00, 0xd6}); + + err += sn9c102_i2c_write(cam, 0x12, 0x80); + err += sn9c102_i2c_write(cam, 0x12, 0x48); + err += sn9c102_i2c_write(cam, 0x01, 0x80); + err += sn9c102_i2c_write(cam, 0x02, 0x80); + err += sn9c102_i2c_write(cam, 0x03, 0x80); + err += sn9c102_i2c_write(cam, 0x04, 0x10); + err += sn9c102_i2c_write(cam, 0x05, 0x20); + err += sn9c102_i2c_write(cam, 0x06, 0x80); + err += sn9c102_i2c_write(cam, 0x11, 0x00); + err += sn9c102_i2c_write(cam, 0x0c, 0x20); + err += sn9c102_i2c_write(cam, 0x0d, 0x20); + err += sn9c102_i2c_write(cam, 0x15, 0x80); + err += sn9c102_i2c_write(cam, 0x16, 0x03); + err += sn9c102_i2c_write(cam, 0x17, 0x1b); + err += sn9c102_i2c_write(cam, 0x18, 0xbd); + err += sn9c102_i2c_write(cam, 0x19, 0x05); + err += sn9c102_i2c_write(cam, 0x1a, 0xf6); + err += sn9c102_i2c_write(cam, 0x1b, 0x04); + err += sn9c102_i2c_write(cam, 0x21, 0x1b); + err += sn9c102_i2c_write(cam, 0x22, 0x00); + err += sn9c102_i2c_write(cam, 0x23, 0xde); + err += sn9c102_i2c_write(cam, 0x24, 0x10); + err += sn9c102_i2c_write(cam, 0x25, 0x8a); + err += sn9c102_i2c_write(cam, 0x26, 0xa0); + err += sn9c102_i2c_write(cam, 0x27, 0xca); + err += sn9c102_i2c_write(cam, 0x28, 0xa2); + err += sn9c102_i2c_write(cam, 0x29, 0x74); + err += sn9c102_i2c_write(cam, 0x2a, 0x88); + err += sn9c102_i2c_write(cam, 0x2b, 0x34); + err += sn9c102_i2c_write(cam, 0x2c, 0x88); + err += sn9c102_i2c_write(cam, 0x2e, 0x00); + err += sn9c102_i2c_write(cam, 0x2f, 0x00); + err += sn9c102_i2c_write(cam, 0x30, 0x00); + err += sn9c102_i2c_write(cam, 0x32, 0xc2); + err += sn9c102_i2c_write(cam, 0x33, 0x08); + err += sn9c102_i2c_write(cam, 0x4c, 0x40); + err += sn9c102_i2c_write(cam, 0x4d, 0xf3); + err += sn9c102_i2c_write(cam, 0x60, 0x05); + err += sn9c102_i2c_write(cam, 0x61, 0x40); + err += sn9c102_i2c_write(cam, 0x62, 0x12); + err += sn9c102_i2c_write(cam, 0x63, 0x57); + err += sn9c102_i2c_write(cam, 0x64, 0x73); + err += sn9c102_i2c_write(cam, 0x65, 0x00); + err += sn9c102_i2c_write(cam, 0x66, 0x55); + err += sn9c102_i2c_write(cam, 0x67, 0x01); + err += sn9c102_i2c_write(cam, 0x68, 0xac); + err += sn9c102_i2c_write(cam, 0x69, 0x38); + err += sn9c102_i2c_write(cam, 0x6f, 0x1f); + err += sn9c102_i2c_write(cam, 0x70, 0x01); + err += sn9c102_i2c_write(cam, 0x71, 0x00); + err += sn9c102_i2c_write(cam, 0x72, 0x10); + err += sn9c102_i2c_write(cam, 0x73, 0x50); + err += sn9c102_i2c_write(cam, 0x74, 0x20); + err += sn9c102_i2c_write(cam, 0x76, 0x01); + err += sn9c102_i2c_write(cam, 0x77, 0xf3); + err += sn9c102_i2c_write(cam, 0x78, 0x90); + err += sn9c102_i2c_write(cam, 0x79, 0x98); + err += sn9c102_i2c_write(cam, 0x7a, 0x98); + err += sn9c102_i2c_write(cam, 0x7b, 0x00); + err += sn9c102_i2c_write(cam, 0x7c, 0x38); + err += sn9c102_i2c_write(cam, 0x7d, 0xff); + break; + default: + break; + } + + return err; +} + + +static int ov7630_get_ctrl(struct sn9c102_device* cam, + struct v4l2_control* ctrl) +{ + enum sn9c102_bridge bridge = sn9c102_get_bridge(cam); + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x10)) < 0) + return -EIO; + break; + case V4L2_CID_RED_BALANCE: + if (bridge == BRIDGE_SN9C105 || bridge == BRIDGE_SN9C120) + ctrl->value = sn9c102_pread_reg(cam, 0x05); + else + ctrl->value = sn9c102_pread_reg(cam, 0x07); + break; + case V4L2_CID_BLUE_BALANCE: + ctrl->value = sn9c102_pread_reg(cam, 0x06); + break; + case SN9C102_V4L2_CID_GREEN_BALANCE: + if (bridge == BRIDGE_SN9C105 || bridge == BRIDGE_SN9C120) + ctrl->value = sn9c102_pread_reg(cam, 0x07); + else + ctrl->value = sn9c102_pread_reg(cam, 0x05); + break; + break; + case V4L2_CID_GAIN: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x00)) < 0) + return -EIO; + ctrl->value &= 0x3f; + break; + case V4L2_CID_DO_WHITE_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x0c)) < 0) + return -EIO; + ctrl->value &= 0x3f; + break; + case V4L2_CID_WHITENESS: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x0d)) < 0) + return -EIO; + ctrl->value &= 0x3f; + break; + case V4L2_CID_AUTOGAIN: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x13)) < 0) + return -EIO; + ctrl->value &= 0x01; + break; + case V4L2_CID_VFLIP: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x75)) < 0) + return -EIO; + ctrl->value = (ctrl->value & 0x80) ? 1 : 0; + break; + case SN9C102_V4L2_CID_GAMMA: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x14)) < 0) + return -EIO; + ctrl->value = (ctrl->value & 0x02) ? 1 : 0; + break; + case SN9C102_V4L2_CID_BAND_FILTER: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x2d)) < 0) + return -EIO; + ctrl->value = (ctrl->value & 0x02) ? 1 : 0; + break; + default: + return -EINVAL; + } + + return err ? -EIO : 0; +} + + +static int ov7630_set_ctrl(struct sn9c102_device* cam, + const struct v4l2_control* ctrl) +{ + enum sn9c102_bridge bridge = sn9c102_get_bridge(cam); + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + err += sn9c102_i2c_write(cam, 0x10, ctrl->value); + break; + case V4L2_CID_RED_BALANCE: + if (bridge == BRIDGE_SN9C105 || bridge == BRIDGE_SN9C120) + err += sn9c102_write_reg(cam, ctrl->value, 0x05); + else + err += sn9c102_write_reg(cam, ctrl->value, 0x07); + break; + case V4L2_CID_BLUE_BALANCE: + err += sn9c102_write_reg(cam, ctrl->value, 0x06); + break; + case SN9C102_V4L2_CID_GREEN_BALANCE: + if (bridge == BRIDGE_SN9C105 || bridge == BRIDGE_SN9C120) + err += sn9c102_write_reg(cam, ctrl->value, 0x07); + else + err += sn9c102_write_reg(cam, ctrl->value, 0x05); + break; + case V4L2_CID_GAIN: + err += sn9c102_i2c_write(cam, 0x00, ctrl->value); + break; + case V4L2_CID_DO_WHITE_BALANCE: + err += sn9c102_i2c_write(cam, 0x0c, ctrl->value); + break; + case V4L2_CID_WHITENESS: + err += sn9c102_i2c_write(cam, 0x0d, ctrl->value); + break; + case V4L2_CID_AUTOGAIN: + err += sn9c102_i2c_write(cam, 0x13, ctrl->value | + (ctrl->value << 1)); + break; + case V4L2_CID_VFLIP: + err += sn9c102_i2c_write(cam, 0x75, 0x0e | (ctrl->value << 7)); + break; + case SN9C102_V4L2_CID_GAMMA: + err += sn9c102_i2c_write(cam, 0x14, ctrl->value << 2); + break; + case SN9C102_V4L2_CID_BAND_FILTER: + err += sn9c102_i2c_write(cam, 0x2d, ctrl->value << 2); + break; + default: + return -EINVAL; + } + + return err ? -EIO : 0; +} + + +static int ov7630_set_crop(struct sn9c102_device* cam, + const struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + int err = 0; + u8 h_start = 0, v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1; + + switch (sn9c102_get_bridge(cam)) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + case BRIDGE_SN9C103: + h_start = (u8)(rect->left - s->cropcap.bounds.left) + 1; + break; + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + h_start = (u8)(rect->left - s->cropcap.bounds.left) + 4; + break; + default: + break; + } + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + + return err; +} + + +static int ov7630_set_pix_format(struct sn9c102_device* cam, + const struct v4l2_pix_format* pix) +{ + int err = 0; + + switch (sn9c102_get_bridge(cam)) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + case BRIDGE_SN9C103: + if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) + err += sn9c102_write_reg(cam, 0x50, 0x19); + else + err += sn9c102_write_reg(cam, 0x20, 0x19); + break; + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) { + err += sn9c102_write_reg(cam, 0xe5, 0x17); + err += sn9c102_i2c_write(cam, 0x11, 0x04); + } else { + err += sn9c102_write_reg(cam, 0xe2, 0x17); + err += sn9c102_i2c_write(cam, 0x11, 0x02); + } + break; + default: + break; + } + + return err; +} + + +static const struct sn9c102_sensor ov7630 = { + .name = "OV7630", + .maintainer = "Luca Risolia ", + .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102 | BRIDGE_SN9C103 | + BRIDGE_SN9C105 | BRIDGE_SN9C120, + .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, + .frequency = SN9C102_I2C_100KHZ, + .interface = SN9C102_I2C_2WIRES, + .i2c_slave_id = 0x21, + .init = &ov7630_init, + .qctrl = { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "global gain", + .minimum = 0x00, + .maximum = 0x3f, + .step = 0x01, + .default_value = 0x14, + .flags = 0, + }, + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x01, + .default_value = 0x60, + .flags = 0, + }, + { + .id = V4L2_CID_WHITENESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "white balance background: red", + .minimum = 0x00, + .maximum = 0x3f, + .step = 0x01, + .default_value = 0x20, + .flags = 0, + }, + { + .id = V4L2_CID_DO_WHITE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "white balance background: blue", + .minimum = 0x00, + .maximum = 0x3f, + .step = 0x01, + .default_value = 0x20, + .flags = 0, + }, + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "red balance", + .minimum = 0x00, + .maximum = 0x7f, + .step = 0x01, + .default_value = 0x20, + .flags = 0, + }, + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "blue balance", + .minimum = 0x00, + .maximum = 0x7f, + .step = 0x01, + .default_value = 0x20, + .flags = 0, + }, + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "auto adjust", + .minimum = 0x00, + .maximum = 0x01, + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "vertical flip", + .minimum = 0x00, + .maximum = 0x01, + .step = 0x01, + .default_value = 0x01, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_GREEN_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "green balance", + .minimum = 0x00, + .maximum = 0x7f, + .step = 0x01, + .default_value = 0x20, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_BAND_FILTER, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "band filter", + .minimum = 0x00, + .maximum = 0x01, + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_GAMMA, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "rgb gamma", + .minimum = 0x00, + .maximum = 0x01, + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + }, + .get_ctrl = &ov7630_get_ctrl, + .set_ctrl = &ov7630_set_ctrl, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + }, + .set_crop = &ov7630_set_crop, + .pix_format = { + .width = 640, + .height = 480, + .pixelformat = V4L2_PIX_FMT_SN9C10X, + .priv = 8, + }, + .set_pix_format = &ov7630_set_pix_format +}; + + +int sn9c102_probe_ov7630(struct sn9c102_device* cam) +{ + int pid, ver, err = 0; + + switch (sn9c102_get_bridge(cam)) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x00, 0x01}, + {0x28, 0x17}); + break; + case BRIDGE_SN9C103: /* do _not_ change anything! */ + err = sn9c102_write_const_regs(cam, {0x09, 0x01}, {0x42, 0x01}, + {0x28, 0x17}, {0x44, 0x02}); + pid = sn9c102_i2c_try_read(cam, &ov7630, 0x0a); + if (err || pid < 0) /* try a different initialization */ + err += sn9c102_write_const_regs(cam, {0x01, 0x01}, + {0x00, 0x01}); + break; + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + err = sn9c102_write_const_regs(cam, {0x01, 0xf1}, {0x00, 0xf1}, + {0x29, 0x01}, {0x74, 0x02}, + {0x0e, 0x01}, {0x44, 0x01}); + break; + default: + break; + } + + pid = sn9c102_i2c_try_read(cam, &ov7630, 0x0a); + ver = sn9c102_i2c_try_read(cam, &ov7630, 0x0b); + if (err || pid < 0 || ver < 0) + return -EIO; + if (pid != 0x76 || ver != 0x31) + return -ENODEV; + sn9c102_attach_sensor(cam, &ov7630); + + return 0; +} diff --git a/drivers/staging/media/sn9c102/sn9c102_ov7660.c b/drivers/staging/media/sn9c102/sn9c102_ov7660.c new file mode 100644 index 000000000000..7977795d342b --- /dev/null +++ b/drivers/staging/media/sn9c102/sn9c102_ov7660.c @@ -0,0 +1,538 @@ +/*************************************************************************** + * Plug-in for OV7660 image sensor connected to the SN9C1xx PC Camera * + * Controllers * + * * + * Copyright (C) 2007 by Luca Risolia * + * * + * 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. * + ***************************************************************************/ + +#include "sn9c102_sensor.h" +#include "sn9c102_devtable.h" + + +static int ov7660_init(struct sn9c102_device* cam) +{ + int err = 0; + + err = sn9c102_write_const_regs(cam, {0x40, 0x02}, {0x00, 0x03}, + {0x1a, 0x04}, {0x03, 0x10}, + {0x08, 0x14}, {0x20, 0x17}, + {0x8b, 0x18}, {0x00, 0x19}, + {0x1d, 0x1a}, {0x10, 0x1b}, + {0x02, 0x1c}, {0x03, 0x1d}, + {0x0f, 0x1e}, {0x0c, 0x1f}, + {0x00, 0x20}, {0x29, 0x21}, + {0x40, 0x22}, {0x54, 0x23}, + {0x66, 0x24}, {0x76, 0x25}, + {0x85, 0x26}, {0x94, 0x27}, + {0xa1, 0x28}, {0xae, 0x29}, + {0xbb, 0x2a}, {0xc7, 0x2b}, + {0xd3, 0x2c}, {0xde, 0x2d}, + {0xea, 0x2e}, {0xf4, 0x2f}, + {0xff, 0x30}, {0x00, 0x3f}, + {0xc7, 0x40}, {0x01, 0x41}, + {0x44, 0x42}, {0x00, 0x43}, + {0x44, 0x44}, {0x00, 0x45}, + {0x44, 0x46}, {0x00, 0x47}, + {0xc7, 0x48}, {0x01, 0x49}, + {0xc7, 0x4a}, {0x01, 0x4b}, + {0xc7, 0x4c}, {0x01, 0x4d}, + {0x44, 0x4e}, {0x00, 0x4f}, + {0x44, 0x50}, {0x00, 0x51}, + {0x44, 0x52}, {0x00, 0x53}, + {0xc7, 0x54}, {0x01, 0x55}, + {0xc7, 0x56}, {0x01, 0x57}, + {0xc7, 0x58}, {0x01, 0x59}, + {0x44, 0x5a}, {0x00, 0x5b}, + {0x44, 0x5c}, {0x00, 0x5d}, + {0x44, 0x5e}, {0x00, 0x5f}, + {0xc7, 0x60}, {0x01, 0x61}, + {0xc7, 0x62}, {0x01, 0x63}, + {0xc7, 0x64}, {0x01, 0x65}, + {0x44, 0x66}, {0x00, 0x67}, + {0x44, 0x68}, {0x00, 0x69}, + {0x44, 0x6a}, {0x00, 0x6b}, + {0xc7, 0x6c}, {0x01, 0x6d}, + {0xc7, 0x6e}, {0x01, 0x6f}, + {0xc7, 0x70}, {0x01, 0x71}, + {0x44, 0x72}, {0x00, 0x73}, + {0x44, 0x74}, {0x00, 0x75}, + {0x44, 0x76}, {0x00, 0x77}, + {0xc7, 0x78}, {0x01, 0x79}, + {0xc7, 0x7a}, {0x01, 0x7b}, + {0xc7, 0x7c}, {0x01, 0x7d}, + {0x44, 0x7e}, {0x00, 0x7f}, + {0x14, 0x84}, {0x00, 0x85}, + {0x27, 0x86}, {0x00, 0x87}, + {0x07, 0x88}, {0x00, 0x89}, + {0xec, 0x8a}, {0x0f, 0x8b}, + {0xd8, 0x8c}, {0x0f, 0x8d}, + {0x3d, 0x8e}, {0x00, 0x8f}, + {0x3d, 0x90}, {0x00, 0x91}, + {0xcd, 0x92}, {0x0f, 0x93}, + {0xf7, 0x94}, {0x0f, 0x95}, + {0x0c, 0x96}, {0x00, 0x97}, + {0x00, 0x98}, {0x66, 0x99}, + {0x05, 0x9a}, {0x00, 0x9b}, + {0x04, 0x9c}, {0x00, 0x9d}, + {0x08, 0x9e}, {0x00, 0x9f}, + {0x2d, 0xc0}, {0x2d, 0xc1}, + {0x3a, 0xc2}, {0x05, 0xc3}, + {0x04, 0xc4}, {0x3f, 0xc5}, + {0x00, 0xc6}, {0x00, 0xc7}, + {0x50, 0xc8}, {0x3C, 0xc9}, + {0x28, 0xca}, {0xd8, 0xcb}, + {0x14, 0xcc}, {0xec, 0xcd}, + {0x32, 0xce}, {0xdd, 0xcf}, + {0x32, 0xd0}, {0xdd, 0xd1}, + {0x6a, 0xd2}, {0x50, 0xd3}, + {0x00, 0xd4}, {0x00, 0xd5}, + {0x00, 0xd6}); + + err += sn9c102_i2c_write(cam, 0x12, 0x80); + err += sn9c102_i2c_write(cam, 0x11, 0x09); + err += sn9c102_i2c_write(cam, 0x00, 0x0A); + err += sn9c102_i2c_write(cam, 0x01, 0x80); + err += sn9c102_i2c_write(cam, 0x02, 0x80); + err += sn9c102_i2c_write(cam, 0x03, 0x00); + err += sn9c102_i2c_write(cam, 0x04, 0x00); + err += sn9c102_i2c_write(cam, 0x05, 0x08); + err += sn9c102_i2c_write(cam, 0x06, 0x0B); + err += sn9c102_i2c_write(cam, 0x07, 0x00); + err += sn9c102_i2c_write(cam, 0x08, 0x1C); + err += sn9c102_i2c_write(cam, 0x09, 0x01); + err += sn9c102_i2c_write(cam, 0x0A, 0x76); + err += sn9c102_i2c_write(cam, 0x0B, 0x60); + err += sn9c102_i2c_write(cam, 0x0C, 0x00); + err += sn9c102_i2c_write(cam, 0x0D, 0x08); + err += sn9c102_i2c_write(cam, 0x0E, 0x04); + err += sn9c102_i2c_write(cam, 0x0F, 0x6F); + err += sn9c102_i2c_write(cam, 0x10, 0x20); + err += sn9c102_i2c_write(cam, 0x11, 0x03); + err += sn9c102_i2c_write(cam, 0x12, 0x05); + err += sn9c102_i2c_write(cam, 0x13, 0xC7); + err += sn9c102_i2c_write(cam, 0x14, 0x2C); + err += sn9c102_i2c_write(cam, 0x15, 0x00); + err += sn9c102_i2c_write(cam, 0x16, 0x02); + err += sn9c102_i2c_write(cam, 0x17, 0x10); + err += sn9c102_i2c_write(cam, 0x18, 0x60); + err += sn9c102_i2c_write(cam, 0x19, 0x02); + err += sn9c102_i2c_write(cam, 0x1A, 0x7B); + err += sn9c102_i2c_write(cam, 0x1B, 0x02); + err += sn9c102_i2c_write(cam, 0x1C, 0x7F); + err += sn9c102_i2c_write(cam, 0x1D, 0xA2); + err += sn9c102_i2c_write(cam, 0x1E, 0x01); + err += sn9c102_i2c_write(cam, 0x1F, 0x0E); + err += sn9c102_i2c_write(cam, 0x20, 0x05); + err += sn9c102_i2c_write(cam, 0x21, 0x05); + err += sn9c102_i2c_write(cam, 0x22, 0x05); + err += sn9c102_i2c_write(cam, 0x23, 0x05); + err += sn9c102_i2c_write(cam, 0x24, 0x68); + err += sn9c102_i2c_write(cam, 0x25, 0x58); + err += sn9c102_i2c_write(cam, 0x26, 0xD4); + err += sn9c102_i2c_write(cam, 0x27, 0x80); + err += sn9c102_i2c_write(cam, 0x28, 0x80); + err += sn9c102_i2c_write(cam, 0x29, 0x30); + err += sn9c102_i2c_write(cam, 0x2A, 0x00); + err += sn9c102_i2c_write(cam, 0x2B, 0x00); + err += sn9c102_i2c_write(cam, 0x2C, 0x80); + err += sn9c102_i2c_write(cam, 0x2D, 0x00); + err += sn9c102_i2c_write(cam, 0x2E, 0x00); + err += sn9c102_i2c_write(cam, 0x2F, 0x0E); + err += sn9c102_i2c_write(cam, 0x30, 0x08); + err += sn9c102_i2c_write(cam, 0x31, 0x30); + err += sn9c102_i2c_write(cam, 0x32, 0xB4); + err += sn9c102_i2c_write(cam, 0x33, 0x00); + err += sn9c102_i2c_write(cam, 0x34, 0x07); + err += sn9c102_i2c_write(cam, 0x35, 0x84); + err += sn9c102_i2c_write(cam, 0x36, 0x00); + err += sn9c102_i2c_write(cam, 0x37, 0x0C); + err += sn9c102_i2c_write(cam, 0x38, 0x02); + err += sn9c102_i2c_write(cam, 0x39, 0x43); + err += sn9c102_i2c_write(cam, 0x3A, 0x00); + err += sn9c102_i2c_write(cam, 0x3B, 0x0A); + err += sn9c102_i2c_write(cam, 0x3C, 0x6C); + err += sn9c102_i2c_write(cam, 0x3D, 0x99); + err += sn9c102_i2c_write(cam, 0x3E, 0x0E); + err += sn9c102_i2c_write(cam, 0x3F, 0x41); + err += sn9c102_i2c_write(cam, 0x40, 0xC1); + err += sn9c102_i2c_write(cam, 0x41, 0x22); + err += sn9c102_i2c_write(cam, 0x42, 0x08); + err += sn9c102_i2c_write(cam, 0x43, 0xF0); + err += sn9c102_i2c_write(cam, 0x44, 0x10); + err += sn9c102_i2c_write(cam, 0x45, 0x78); + err += sn9c102_i2c_write(cam, 0x46, 0xA8); + err += sn9c102_i2c_write(cam, 0x47, 0x60); + err += sn9c102_i2c_write(cam, 0x48, 0x80); + err += sn9c102_i2c_write(cam, 0x49, 0x00); + err += sn9c102_i2c_write(cam, 0x4A, 0x00); + err += sn9c102_i2c_write(cam, 0x4B, 0x00); + err += sn9c102_i2c_write(cam, 0x4C, 0x00); + err += sn9c102_i2c_write(cam, 0x4D, 0x00); + err += sn9c102_i2c_write(cam, 0x4E, 0x00); + err += sn9c102_i2c_write(cam, 0x4F, 0x46); + err += sn9c102_i2c_write(cam, 0x50, 0x36); + err += sn9c102_i2c_write(cam, 0x51, 0x0F); + err += sn9c102_i2c_write(cam, 0x52, 0x17); + err += sn9c102_i2c_write(cam, 0x53, 0x7F); + err += sn9c102_i2c_write(cam, 0x54, 0x96); + err += sn9c102_i2c_write(cam, 0x55, 0x40); + err += sn9c102_i2c_write(cam, 0x56, 0x40); + err += sn9c102_i2c_write(cam, 0x57, 0x40); + err += sn9c102_i2c_write(cam, 0x58, 0x0F); + err += sn9c102_i2c_write(cam, 0x59, 0xBA); + err += sn9c102_i2c_write(cam, 0x5A, 0x9A); + err += sn9c102_i2c_write(cam, 0x5B, 0x22); + err += sn9c102_i2c_write(cam, 0x5C, 0xB9); + err += sn9c102_i2c_write(cam, 0x5D, 0x9B); + err += sn9c102_i2c_write(cam, 0x5E, 0x10); + err += sn9c102_i2c_write(cam, 0x5F, 0xF0); + err += sn9c102_i2c_write(cam, 0x60, 0x05); + err += sn9c102_i2c_write(cam, 0x61, 0x60); + err += sn9c102_i2c_write(cam, 0x62, 0x00); + err += sn9c102_i2c_write(cam, 0x63, 0x00); + err += sn9c102_i2c_write(cam, 0x64, 0x50); + err += sn9c102_i2c_write(cam, 0x65, 0x30); + err += sn9c102_i2c_write(cam, 0x66, 0x00); + err += sn9c102_i2c_write(cam, 0x67, 0x80); + err += sn9c102_i2c_write(cam, 0x68, 0x7A); + err += sn9c102_i2c_write(cam, 0x69, 0x90); + err += sn9c102_i2c_write(cam, 0x6A, 0x80); + err += sn9c102_i2c_write(cam, 0x6B, 0x0A); + err += sn9c102_i2c_write(cam, 0x6C, 0x30); + err += sn9c102_i2c_write(cam, 0x6D, 0x48); + err += sn9c102_i2c_write(cam, 0x6E, 0x80); + err += sn9c102_i2c_write(cam, 0x6F, 0x74); + err += sn9c102_i2c_write(cam, 0x70, 0x64); + err += sn9c102_i2c_write(cam, 0x71, 0x60); + err += sn9c102_i2c_write(cam, 0x72, 0x5C); + err += sn9c102_i2c_write(cam, 0x73, 0x58); + err += sn9c102_i2c_write(cam, 0x74, 0x54); + err += sn9c102_i2c_write(cam, 0x75, 0x4C); + err += sn9c102_i2c_write(cam, 0x76, 0x40); + err += sn9c102_i2c_write(cam, 0x77, 0x38); + err += sn9c102_i2c_write(cam, 0x78, 0x34); + err += sn9c102_i2c_write(cam, 0x79, 0x30); + err += sn9c102_i2c_write(cam, 0x7A, 0x2F); + err += sn9c102_i2c_write(cam, 0x7B, 0x2B); + err += sn9c102_i2c_write(cam, 0x7C, 0x03); + err += sn9c102_i2c_write(cam, 0x7D, 0x07); + err += sn9c102_i2c_write(cam, 0x7E, 0x17); + err += sn9c102_i2c_write(cam, 0x7F, 0x34); + err += sn9c102_i2c_write(cam, 0x80, 0x41); + err += sn9c102_i2c_write(cam, 0x81, 0x4D); + err += sn9c102_i2c_write(cam, 0x82, 0x58); + err += sn9c102_i2c_write(cam, 0x83, 0x63); + err += sn9c102_i2c_write(cam, 0x84, 0x6E); + err += sn9c102_i2c_write(cam, 0x85, 0x77); + err += sn9c102_i2c_write(cam, 0x86, 0x87); + err += sn9c102_i2c_write(cam, 0x87, 0x95); + err += sn9c102_i2c_write(cam, 0x88, 0xAF); + err += sn9c102_i2c_write(cam, 0x89, 0xC7); + err += sn9c102_i2c_write(cam, 0x8A, 0xDF); + err += sn9c102_i2c_write(cam, 0x8B, 0x99); + err += sn9c102_i2c_write(cam, 0x8C, 0x99); + err += sn9c102_i2c_write(cam, 0x8D, 0xCF); + err += sn9c102_i2c_write(cam, 0x8E, 0x20); + err += sn9c102_i2c_write(cam, 0x8F, 0x26); + err += sn9c102_i2c_write(cam, 0x90, 0x10); + err += sn9c102_i2c_write(cam, 0x91, 0x0C); + err += sn9c102_i2c_write(cam, 0x92, 0x25); + err += sn9c102_i2c_write(cam, 0x93, 0x00); + err += sn9c102_i2c_write(cam, 0x94, 0x50); + err += sn9c102_i2c_write(cam, 0x95, 0x50); + err += sn9c102_i2c_write(cam, 0x96, 0x00); + err += sn9c102_i2c_write(cam, 0x97, 0x01); + err += sn9c102_i2c_write(cam, 0x98, 0x10); + err += sn9c102_i2c_write(cam, 0x99, 0x40); + err += sn9c102_i2c_write(cam, 0x9A, 0x40); + err += sn9c102_i2c_write(cam, 0x9B, 0x20); + err += sn9c102_i2c_write(cam, 0x9C, 0x00); + err += sn9c102_i2c_write(cam, 0x9D, 0x99); + err += sn9c102_i2c_write(cam, 0x9E, 0x7F); + err += sn9c102_i2c_write(cam, 0x9F, 0x00); + err += sn9c102_i2c_write(cam, 0xA0, 0x00); + err += sn9c102_i2c_write(cam, 0xA1, 0x00); + + return err; +} + + +static int ov7660_get_ctrl(struct sn9c102_device* cam, + struct v4l2_control* ctrl) +{ + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x10)) < 0) + return -EIO; + break; + case V4L2_CID_DO_WHITE_BALANCE: + if ((ctrl->value = sn9c102_read_reg(cam, 0x02)) < 0) + return -EIO; + ctrl->value = (ctrl->value & 0x04) ? 1 : 0; + break; + case V4L2_CID_RED_BALANCE: + if ((ctrl->value = sn9c102_read_reg(cam, 0x05)) < 0) + return -EIO; + ctrl->value &= 0x7f; + break; + case V4L2_CID_BLUE_BALANCE: + if ((ctrl->value = sn9c102_read_reg(cam, 0x06)) < 0) + return -EIO; + ctrl->value &= 0x7f; + break; + case SN9C102_V4L2_CID_GREEN_BALANCE: + if ((ctrl->value = sn9c102_read_reg(cam, 0x07)) < 0) + return -EIO; + ctrl->value &= 0x7f; + break; + case SN9C102_V4L2_CID_BAND_FILTER: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x3b)) < 0) + return -EIO; + ctrl->value &= 0x08; + break; + case V4L2_CID_GAIN: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x00)) < 0) + return -EIO; + ctrl->value &= 0x1f; + break; + case V4L2_CID_AUTOGAIN: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x13)) < 0) + return -EIO; + ctrl->value &= 0x01; + break; + default: + return -EINVAL; + } + + return err ? -EIO : 0; +} + + +static int ov7660_set_ctrl(struct sn9c102_device* cam, + const struct v4l2_control* ctrl) +{ + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + err += sn9c102_i2c_write(cam, 0x10, ctrl->value); + break; + case V4L2_CID_DO_WHITE_BALANCE: + err += sn9c102_write_reg(cam, 0x43 | (ctrl->value << 2), 0x02); + break; + case V4L2_CID_RED_BALANCE: + err += sn9c102_write_reg(cam, ctrl->value, 0x05); + break; + case V4L2_CID_BLUE_BALANCE: + err += sn9c102_write_reg(cam, ctrl->value, 0x06); + break; + case SN9C102_V4L2_CID_GREEN_BALANCE: + err += sn9c102_write_reg(cam, ctrl->value, 0x07); + break; + case SN9C102_V4L2_CID_BAND_FILTER: + err += sn9c102_i2c_write(cam, ctrl->value << 3, 0x3b); + break; + case V4L2_CID_GAIN: + err += sn9c102_i2c_write(cam, 0x00, 0x60 + ctrl->value); + break; + case V4L2_CID_AUTOGAIN: + err += sn9c102_i2c_write(cam, 0x13, 0xc0 | + (ctrl->value * 0x07)); + break; + default: + return -EINVAL; + } + + return err ? -EIO : 0; +} + + +static int ov7660_set_crop(struct sn9c102_device* cam, + const struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + int err = 0; + u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 1, + v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1; + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + + return err; +} + + +static int ov7660_set_pix_format(struct sn9c102_device* cam, + const struct v4l2_pix_format* pix) +{ + int r0, err = 0; + + r0 = sn9c102_pread_reg(cam, 0x01); + + if (pix->pixelformat == V4L2_PIX_FMT_JPEG) { + err += sn9c102_write_reg(cam, r0 | 0x40, 0x01); + err += sn9c102_write_reg(cam, 0xa2, 0x17); + err += sn9c102_i2c_write(cam, 0x11, 0x00); + } else { + err += sn9c102_write_reg(cam, r0 | 0x40, 0x01); + err += sn9c102_write_reg(cam, 0xa2, 0x17); + err += sn9c102_i2c_write(cam, 0x11, 0x0d); + } + + return err; +} + + +static const struct sn9c102_sensor ov7660 = { + .name = "OV7660", + .maintainer = "Luca Risolia ", + .supported_bridge = BRIDGE_SN9C105 | BRIDGE_SN9C120, + .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, + .frequency = SN9C102_I2C_100KHZ, + .interface = SN9C102_I2C_2WIRES, + .i2c_slave_id = 0x21, + .init = &ov7660_init, + .qctrl = { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "global gain", + .minimum = 0x00, + .maximum = 0x1f, + .step = 0x01, + .default_value = 0x09, + .flags = 0, + }, + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x01, + .default_value = 0x27, + .flags = 0, + }, + { + .id = V4L2_CID_DO_WHITE_BALANCE, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "night mode", + .minimum = 0x00, + .maximum = 0x01, + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "red balance", + .minimum = 0x00, + .maximum = 0x7f, + .step = 0x01, + .default_value = 0x14, + .flags = 0, + }, + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "blue balance", + .minimum = 0x00, + .maximum = 0x7f, + .step = 0x01, + .default_value = 0x14, + .flags = 0, + }, + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "auto adjust", + .minimum = 0x00, + .maximum = 0x01, + .step = 0x01, + .default_value = 0x01, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_GREEN_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "green balance", + .minimum = 0x00, + .maximum = 0x7f, + .step = 0x01, + .default_value = 0x14, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_BAND_FILTER, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "band filter", + .minimum = 0x00, + .maximum = 0x01, + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + }, + .get_ctrl = &ov7660_get_ctrl, + .set_ctrl = &ov7660_set_ctrl, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + }, + .set_crop = &ov7660_set_crop, + .pix_format = { + .width = 640, + .height = 480, + .pixelformat = V4L2_PIX_FMT_JPEG, + .priv = 8, + }, + .set_pix_format = &ov7660_set_pix_format +}; + + +int sn9c102_probe_ov7660(struct sn9c102_device* cam) +{ + int pid, ver, err; + + err = sn9c102_write_const_regs(cam, {0x01, 0xf1}, {0x00, 0xf1}, + {0x01, 0x01}, {0x00, 0x01}, + {0x28, 0x17}); + + pid = sn9c102_i2c_try_read(cam, &ov7660, 0x0a); + ver = sn9c102_i2c_try_read(cam, &ov7660, 0x0b); + if (err || pid < 0 || ver < 0) + return -EIO; + if (pid != 0x76 || ver != 0x60) + return -ENODEV; + + sn9c102_attach_sensor(cam, &ov7660); + + return 0; +} diff --git a/drivers/staging/media/sn9c102/sn9c102_pas106b.c b/drivers/staging/media/sn9c102/sn9c102_pas106b.c new file mode 100644 index 000000000000..81cd969c1b7b --- /dev/null +++ b/drivers/staging/media/sn9c102/sn9c102_pas106b.c @@ -0,0 +1,302 @@ +/*************************************************************************** + * Plug-in for PAS106B image sensor connected to the SN9C1xx PC Camera * + * Controllers * + * * + * Copyright (C) 2004-2007 by Luca Risolia * + * * + * 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. * + ***************************************************************************/ + +#include +#include "sn9c102_sensor.h" +#include "sn9c102_devtable.h" + + +static int pas106b_init(struct sn9c102_device* cam) +{ + int err = 0; + + err = sn9c102_write_const_regs(cam, {0x00, 0x10}, {0x00, 0x11}, + {0x00, 0x14}, {0x20, 0x17}, + {0x20, 0x19}, {0x09, 0x18}); + + err += sn9c102_i2c_write(cam, 0x02, 0x0c); + err += sn9c102_i2c_write(cam, 0x05, 0x5a); + err += sn9c102_i2c_write(cam, 0x06, 0x88); + err += sn9c102_i2c_write(cam, 0x07, 0x80); + err += sn9c102_i2c_write(cam, 0x10, 0x06); + err += sn9c102_i2c_write(cam, 0x11, 0x06); + err += sn9c102_i2c_write(cam, 0x12, 0x00); + err += sn9c102_i2c_write(cam, 0x14, 0x02); + err += sn9c102_i2c_write(cam, 0x13, 0x01); + + msleep(400); + + return err; +} + + +static int pas106b_get_ctrl(struct sn9c102_device* cam, + struct v4l2_control* ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + { + int r1 = sn9c102_i2c_read(cam, 0x03), + r2 = sn9c102_i2c_read(cam, 0x04); + if (r1 < 0 || r2 < 0) + return -EIO; + ctrl->value = (r1 << 4) | (r2 & 0x0f); + } + return 0; + case V4L2_CID_RED_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x0c)) < 0) + return -EIO; + ctrl->value &= 0x1f; + return 0; + case V4L2_CID_BLUE_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x09)) < 0) + return -EIO; + ctrl->value &= 0x1f; + return 0; + case V4L2_CID_GAIN: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x0e)) < 0) + return -EIO; + ctrl->value &= 0x1f; + return 0; + case V4L2_CID_CONTRAST: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x0f)) < 0) + return -EIO; + ctrl->value &= 0x07; + return 0; + case SN9C102_V4L2_CID_GREEN_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x0a)) < 0) + return -EIO; + ctrl->value = (ctrl->value & 0x1f) << 1; + return 0; + case SN9C102_V4L2_CID_DAC_MAGNITUDE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x08)) < 0) + return -EIO; + ctrl->value &= 0xf8; + return 0; + default: + return -EINVAL; + } +} + + +static int pas106b_set_ctrl(struct sn9c102_device* cam, + const struct v4l2_control* ctrl) +{ + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + err += sn9c102_i2c_write(cam, 0x03, ctrl->value >> 4); + err += sn9c102_i2c_write(cam, 0x04, ctrl->value & 0x0f); + break; + case V4L2_CID_RED_BALANCE: + err += sn9c102_i2c_write(cam, 0x0c, ctrl->value); + break; + case V4L2_CID_BLUE_BALANCE: + err += sn9c102_i2c_write(cam, 0x09, ctrl->value); + break; + case V4L2_CID_GAIN: + err += sn9c102_i2c_write(cam, 0x0e, ctrl->value); + break; + case V4L2_CID_CONTRAST: + err += sn9c102_i2c_write(cam, 0x0f, ctrl->value); + break; + case SN9C102_V4L2_CID_GREEN_BALANCE: + err += sn9c102_i2c_write(cam, 0x0a, ctrl->value >> 1); + err += sn9c102_i2c_write(cam, 0x0b, ctrl->value >> 1); + break; + case SN9C102_V4L2_CID_DAC_MAGNITUDE: + err += sn9c102_i2c_write(cam, 0x08, ctrl->value << 3); + break; + default: + return -EINVAL; + } + err += sn9c102_i2c_write(cam, 0x13, 0x01); + + return err ? -EIO : 0; +} + + +static int pas106b_set_crop(struct sn9c102_device* cam, + const struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + int err = 0; + u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 4, + v_start = (u8)(rect->top - s->cropcap.bounds.top) + 3; + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + + return err; +} + + +static int pas106b_set_pix_format(struct sn9c102_device* cam, + const struct v4l2_pix_format* pix) +{ + int err = 0; + + if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) + err += sn9c102_write_reg(cam, 0x2c, 0x17); + else + err += sn9c102_write_reg(cam, 0x20, 0x17); + + return err; +} + + +static const struct sn9c102_sensor pas106b = { + .name = "PAS106B", + .maintainer = "Luca Risolia ", + .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102, + .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, + .frequency = SN9C102_I2C_400KHZ | SN9C102_I2C_100KHZ, + .interface = SN9C102_I2C_2WIRES, + .i2c_slave_id = 0x40, + .init = &pas106b_init, + .qctrl = { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = 0x125, + .maximum = 0xfff, + .step = 0x001, + .default_value = 0x140, + .flags = 0, + }, + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "global gain", + .minimum = 0x00, + .maximum = 0x1f, + .step = 0x01, + .default_value = 0x0d, + .flags = 0, + }, + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "contrast", + .minimum = 0x00, + .maximum = 0x07, + .step = 0x01, + .default_value = 0x00, /* 0x00~0x03 have same effect */ + .flags = 0, + }, + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "red balance", + .minimum = 0x00, + .maximum = 0x1f, + .step = 0x01, + .default_value = 0x04, + .flags = 0, + }, + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "blue balance", + .minimum = 0x00, + .maximum = 0x1f, + .step = 0x01, + .default_value = 0x06, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_GREEN_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "green balance", + .minimum = 0x00, + .maximum = 0x3e, + .step = 0x02, + .default_value = 0x02, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_DAC_MAGNITUDE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "DAC magnitude", + .minimum = 0x00, + .maximum = 0x1f, + .step = 0x01, + .default_value = 0x01, + .flags = 0, + }, + }, + .get_ctrl = &pas106b_get_ctrl, + .set_ctrl = &pas106b_set_ctrl, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 352, + .height = 288, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 352, + .height = 288, + }, + }, + .set_crop = &pas106b_set_crop, + .pix_format = { + .width = 352, + .height = 288, + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .priv = 8, /* we use this field as 'bits per pixel' */ + }, + .set_pix_format = &pas106b_set_pix_format +}; + + +int sn9c102_probe_pas106b(struct sn9c102_device* cam) +{ + int r0 = 0, r1 = 0; + unsigned int pid = 0; + + /* + Minimal initialization to enable the I2C communication + NOTE: do NOT change the values! + */ + if (sn9c102_write_const_regs(cam, + {0x01, 0x01}, /* sensor power down */ + {0x00, 0x01}, /* sensor power on */ + {0x28, 0x17})) /* sensor clock at 24 MHz */ + return -EIO; + + r0 = sn9c102_i2c_try_read(cam, &pas106b, 0x00); + r1 = sn9c102_i2c_try_read(cam, &pas106b, 0x01); + if (r0 < 0 || r1 < 0) + return -EIO; + + pid = (r0 << 11) | ((r1 & 0xf0) >> 4); + if (pid != 0x007) + return -ENODEV; + + sn9c102_attach_sensor(cam, &pas106b); + + return 0; +} diff --git a/drivers/staging/media/sn9c102/sn9c102_pas202bcb.c b/drivers/staging/media/sn9c102/sn9c102_pas202bcb.c new file mode 100644 index 000000000000..2e86fdc86989 --- /dev/null +++ b/drivers/staging/media/sn9c102/sn9c102_pas202bcb.c @@ -0,0 +1,335 @@ +/*************************************************************************** + * Plug-in for PAS202BCB image sensor connected to the SN9C1xx PC Camera * + * Controllers * + * * + * Copyright (C) 2004 by Carlos Eduardo Medaglia Dyonisio * + * * + * * + * Support for SN9C103, DAC Magnitude, exposure and green gain controls * + * added by Luca Risolia * + * * + * 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. * + ***************************************************************************/ + +#include +#include "sn9c102_sensor.h" +#include "sn9c102_devtable.h" + + +static int pas202bcb_init(struct sn9c102_device* cam) +{ + int err = 0; + + switch (sn9c102_get_bridge(cam)) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + err = sn9c102_write_const_regs(cam, {0x00, 0x10}, {0x00, 0x11}, + {0x00, 0x14}, {0x20, 0x17}, + {0x30, 0x19}, {0x09, 0x18}); + break; + case BRIDGE_SN9C103: + err = sn9c102_write_const_regs(cam, {0x00, 0x02}, {0x00, 0x03}, + {0x1a, 0x04}, {0x20, 0x05}, + {0x20, 0x06}, {0x20, 0x07}, + {0x00, 0x10}, {0x00, 0x11}, + {0x00, 0x14}, {0x20, 0x17}, + {0x30, 0x19}, {0x09, 0x18}, + {0x02, 0x1c}, {0x03, 0x1d}, + {0x0f, 0x1e}, {0x0c, 0x1f}, + {0x00, 0x20}, {0x10, 0x21}, + {0x20, 0x22}, {0x30, 0x23}, + {0x40, 0x24}, {0x50, 0x25}, + {0x60, 0x26}, {0x70, 0x27}, + {0x80, 0x28}, {0x90, 0x29}, + {0xa0, 0x2a}, {0xb0, 0x2b}, + {0xc0, 0x2c}, {0xd0, 0x2d}, + {0xe0, 0x2e}, {0xf0, 0x2f}, + {0xff, 0x30}); + break; + default: + break; + } + + err += sn9c102_i2c_write(cam, 0x02, 0x14); + err += sn9c102_i2c_write(cam, 0x03, 0x40); + err += sn9c102_i2c_write(cam, 0x0d, 0x2c); + err += sn9c102_i2c_write(cam, 0x0e, 0x01); + err += sn9c102_i2c_write(cam, 0x0f, 0xa9); + err += sn9c102_i2c_write(cam, 0x10, 0x08); + err += sn9c102_i2c_write(cam, 0x13, 0x63); + err += sn9c102_i2c_write(cam, 0x15, 0x70); + err += sn9c102_i2c_write(cam, 0x11, 0x01); + + msleep(400); + + return err; +} + + +static int pas202bcb_get_ctrl(struct sn9c102_device* cam, + struct v4l2_control* ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + { + int r1 = sn9c102_i2c_read(cam, 0x04), + r2 = sn9c102_i2c_read(cam, 0x05); + if (r1 < 0 || r2 < 0) + return -EIO; + ctrl->value = (r1 << 6) | (r2 & 0x3f); + } + return 0; + case V4L2_CID_RED_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x09)) < 0) + return -EIO; + ctrl->value &= 0x0f; + return 0; + case V4L2_CID_BLUE_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x07)) < 0) + return -EIO; + ctrl->value &= 0x0f; + return 0; + case V4L2_CID_GAIN: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x10)) < 0) + return -EIO; + ctrl->value &= 0x1f; + return 0; + case SN9C102_V4L2_CID_GREEN_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x08)) < 0) + return -EIO; + ctrl->value &= 0x0f; + return 0; + case SN9C102_V4L2_CID_DAC_MAGNITUDE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x0c)) < 0) + return -EIO; + return 0; + default: + return -EINVAL; + } +} + + +static int pas202bcb_set_pix_format(struct sn9c102_device* cam, + const struct v4l2_pix_format* pix) +{ + int err = 0; + + if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) + err += sn9c102_write_reg(cam, 0x28, 0x17); + else + err += sn9c102_write_reg(cam, 0x20, 0x17); + + return err; +} + + +static int pas202bcb_set_ctrl(struct sn9c102_device* cam, + const struct v4l2_control* ctrl) +{ + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + err += sn9c102_i2c_write(cam, 0x04, ctrl->value >> 6); + err += sn9c102_i2c_write(cam, 0x05, ctrl->value & 0x3f); + break; + case V4L2_CID_RED_BALANCE: + err += sn9c102_i2c_write(cam, 0x09, ctrl->value); + break; + case V4L2_CID_BLUE_BALANCE: + err += sn9c102_i2c_write(cam, 0x07, ctrl->value); + break; + case V4L2_CID_GAIN: + err += sn9c102_i2c_write(cam, 0x10, ctrl->value); + break; + case SN9C102_V4L2_CID_GREEN_BALANCE: + err += sn9c102_i2c_write(cam, 0x08, ctrl->value); + break; + case SN9C102_V4L2_CID_DAC_MAGNITUDE: + err += sn9c102_i2c_write(cam, 0x0c, ctrl->value); + break; + default: + return -EINVAL; + } + err += sn9c102_i2c_write(cam, 0x11, 0x01); + + return err ? -EIO : 0; +} + + +static int pas202bcb_set_crop(struct sn9c102_device* cam, + const struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + int err = 0; + u8 h_start = 0, + v_start = (u8)(rect->top - s->cropcap.bounds.top) + 3; + + switch (sn9c102_get_bridge(cam)) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + h_start = (u8)(rect->left - s->cropcap.bounds.left) + 4; + break; + case BRIDGE_SN9C103: + h_start = (u8)(rect->left - s->cropcap.bounds.left) + 3; + break; + default: + break; + } + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + + return err; +} + + +static const struct sn9c102_sensor pas202bcb = { + .name = "PAS202BCB", + .maintainer = "Luca Risolia ", + .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102 | BRIDGE_SN9C103, + .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, + .frequency = SN9C102_I2C_400KHZ | SN9C102_I2C_100KHZ, + .interface = SN9C102_I2C_2WIRES, + .i2c_slave_id = 0x40, + .init = &pas202bcb_init, + .qctrl = { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = 0x01e5, + .maximum = 0x3fff, + .step = 0x0001, + .default_value = 0x01e5, + .flags = 0, + }, + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "global gain", + .minimum = 0x00, + .maximum = 0x1f, + .step = 0x01, + .default_value = 0x0b, + .flags = 0, + }, + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "red balance", + .minimum = 0x00, + .maximum = 0x0f, + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "blue balance", + .minimum = 0x00, + .maximum = 0x0f, + .step = 0x01, + .default_value = 0x05, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_GREEN_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "green balance", + .minimum = 0x00, + .maximum = 0x0f, + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_DAC_MAGNITUDE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "DAC magnitude", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x01, + .default_value = 0x04, + .flags = 0, + }, + }, + .get_ctrl = &pas202bcb_get_ctrl, + .set_ctrl = &pas202bcb_set_ctrl, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + }, + .set_crop = &pas202bcb_set_crop, + .pix_format = { + .width = 640, + .height = 480, + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .priv = 8, + }, + .set_pix_format = &pas202bcb_set_pix_format +}; + + +int sn9c102_probe_pas202bcb(struct sn9c102_device* cam) +{ + int r0 = 0, r1 = 0, err = 0; + unsigned int pid = 0; + + /* + * Minimal initialization to enable the I2C communication + * NOTE: do NOT change the values! + */ + switch (sn9c102_get_bridge(cam)) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + err = sn9c102_write_const_regs(cam, + {0x01, 0x01}, /* power down */ + {0x40, 0x01}, /* power on */ + {0x28, 0x17});/* clock 24 MHz */ + break; + case BRIDGE_SN9C103: /* do _not_ change anything! */ + err = sn9c102_write_const_regs(cam, {0x09, 0x01}, {0x44, 0x01}, + {0x44, 0x02}, {0x29, 0x17}); + break; + default: + break; + } + + r0 = sn9c102_i2c_try_read(cam, &pas202bcb, 0x00); + r1 = sn9c102_i2c_try_read(cam, &pas202bcb, 0x01); + + if (err || r0 < 0 || r1 < 0) + return -EIO; + + pid = (r0 << 4) | ((r1 & 0xf0) >> 4); + if (pid != 0x017) + return -ENODEV; + + sn9c102_attach_sensor(cam, &pas202bcb); + + return 0; +} diff --git a/drivers/staging/media/sn9c102/sn9c102_sensor.h b/drivers/staging/media/sn9c102/sn9c102_sensor.h new file mode 100644 index 000000000000..3679970dba2c --- /dev/null +++ b/drivers/staging/media/sn9c102/sn9c102_sensor.h @@ -0,0 +1,307 @@ +/*************************************************************************** + * API for image sensors connected to the SN9C1xx PC Camera Controllers * + * * + * Copyright (C) 2004-2007 by Luca Risolia * + * * + * 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 _SN9C102_SENSOR_H_ +#define _SN9C102_SENSOR_H_ + +#include +#include +#include +#include +#include +#include + +struct sn9c102_device; +struct sn9c102_sensor; + +/*****************************************************************************/ + +/* + OVERVIEW. + This is a small interface that allows you to add support for any CCD/CMOS + image sensors connected to the SN9C1XX bridges. The entire API is documented + below. In the most general case, to support a sensor there are three steps + you have to follow: + 1) define the main "sn9c102_sensor" structure by setting the basic fields; + 2) write a probing function to be called by the core module when the USB + camera is recognized, then add both the USB ids and the name of that + function to the two corresponding tables in sn9c102_devtable.h; + 3) implement the methods that you want/need (and fill the rest of the main + structure accordingly). + "sn9c102_pas106b.c" is an example of all this stuff. Remember that you do + NOT need to touch the source code of the core module for the things to work + properly, unless you find bugs or flaws in it. Finally, do not forget to + read the V4L2 API for completeness. +*/ + +/*****************************************************************************/ + +enum sn9c102_bridge { + BRIDGE_SN9C101 = 0x01, + BRIDGE_SN9C102 = 0x02, + BRIDGE_SN9C103 = 0x04, + BRIDGE_SN9C105 = 0x08, + BRIDGE_SN9C120 = 0x10, +}; + +/* Return the bridge name */ +enum sn9c102_bridge sn9c102_get_bridge(struct sn9c102_device* cam); + +/* Return a pointer the sensor struct attached to the camera */ +struct sn9c102_sensor* sn9c102_get_sensor(struct sn9c102_device* cam); + +/* Identify a device */ +extern struct sn9c102_device* +sn9c102_match_id(struct sn9c102_device* cam, const struct usb_device_id *id); + +/* Attach a probed sensor to the camera. */ +extern void +sn9c102_attach_sensor(struct sn9c102_device* cam, + const struct sn9c102_sensor* sensor); + +/* + Read/write routines: they always return -1 on error, 0 or the read value + otherwise. NOTE that a real read operation is not supported by the SN9C1XX + chip for some of its registers. To work around this problem, a pseudo-read + call is provided instead: it returns the last successfully written value + on the register (0 if it has never been written), the usual -1 on error. +*/ + +/* The "try" I2C I/O versions are used when probing the sensor */ +extern int sn9c102_i2c_try_read(struct sn9c102_device*, + const struct sn9c102_sensor*, u8 address); + +/* + These must be used if and only if the sensor doesn't implement the standard + I2C protocol. There are a number of good reasons why you must use the + single-byte versions of these functions: do not abuse. The first function + writes n bytes, from data0 to datan, to registers 0x09 - 0x09+n of SN9C1XX + chip. The second one programs the registers 0x09 and 0x10 with data0 and + data1, and places the n bytes read from the sensor register table in the + buffer pointed by 'buffer'. Both the functions return -1 on error; the write + version returns 0 on success, while the read version returns the first read + byte. +*/ +extern int sn9c102_i2c_try_raw_write(struct sn9c102_device* cam, + const struct sn9c102_sensor* sensor, u8 n, + u8 data0, u8 data1, u8 data2, u8 data3, + u8 data4, u8 data5); +extern int sn9c102_i2c_try_raw_read(struct sn9c102_device* cam, + const struct sn9c102_sensor* sensor, + u8 data0, u8 data1, u8 n, u8 buffer[]); + +/* To be used after the sensor struct has been attached to the camera struct */ +extern int sn9c102_i2c_write(struct sn9c102_device*, u8 address, u8 value); +extern int sn9c102_i2c_read(struct sn9c102_device*, u8 address); + +/* I/O on registers in the bridge. Could be used by the sensor methods too */ +extern int sn9c102_read_reg(struct sn9c102_device*, u16 index); +extern int sn9c102_pread_reg(struct sn9c102_device*, u16 index); +extern int sn9c102_write_reg(struct sn9c102_device*, u8 value, u16 index); +extern int sn9c102_write_regs(struct sn9c102_device*, const u8 valreg[][2], + int count); +/* + Write multiple registers with constant values. For example: + sn9c102_write_const_regs(cam, {0x00, 0x14}, {0x60, 0x17}, {0x0f, 0x18}); + Register addresses must be < 256. +*/ +#define sn9c102_write_const_regs(sn9c102_device, data...) \ + ({ static const u8 _valreg[][2] = {data}; \ + sn9c102_write_regs(sn9c102_device, _valreg, ARRAY_SIZE(_valreg)); }) + +/*****************************************************************************/ + +enum sn9c102_i2c_sysfs_ops { + SN9C102_I2C_READ = 0x01, + SN9C102_I2C_WRITE = 0x02, +}; + +enum sn9c102_i2c_frequency { /* sensors may support both the frequencies */ + SN9C102_I2C_100KHZ = 0x01, + SN9C102_I2C_400KHZ = 0x02, +}; + +enum sn9c102_i2c_interface { + SN9C102_I2C_2WIRES, + SN9C102_I2C_3WIRES, +}; + +#define SN9C102_MAX_CTRLS (V4L2_CID_LASTP1-V4L2_CID_BASE+10) + +struct sn9c102_sensor { + char name[32], /* sensor name */ + maintainer[64]; /* name of the maintainer */ + + enum sn9c102_bridge supported_bridge; /* supported SN9C1xx bridges */ + + /* Supported operations through the 'sysfs' interface */ + enum sn9c102_i2c_sysfs_ops sysfs_ops; + + /* + These sensor capabilities must be provided if the SN9C1XX controller + needs to communicate through the sensor serial interface by using + at least one of the i2c functions available. + */ + enum sn9c102_i2c_frequency frequency; + enum sn9c102_i2c_interface interface; + + /* + This identifier must be provided if the image sensor implements + the standard I2C protocol. + */ + u8 i2c_slave_id; /* reg. 0x09 */ + + /* + NOTE: Where not noted,most of the functions below are not mandatory. + Set to null if you do not implement them. If implemented, + they must return 0 on success, the proper error otherwise. + */ + + int (*init)(struct sn9c102_device* cam); + /* + This function will be called after the sensor has been attached. + It should be used to initialize the sensor only, but may also + configure part of the SN9C1XX chip if necessary. You don't need to + setup picture settings like brightness, contrast, etc.. here, if + the corresponding controls are implemented (see below), since + they are adjusted in the core driver by calling the set_ctrl() + method after init(), where the arguments are the default values + specified in the v4l2_queryctrl list of supported controls; + Same suggestions apply for other settings, _if_ the corresponding + methods are present; if not, the initialization must configure the + sensor according to the default configuration structures below. + */ + + struct v4l2_queryctrl qctrl[SN9C102_MAX_CTRLS]; + /* + Optional list of default controls, defined as indicated in the + V4L2 API. Menu type controls are not handled by this interface. + */ + + int (*get_ctrl)(struct sn9c102_device* cam, struct v4l2_control* ctrl); + int (*set_ctrl)(struct sn9c102_device* cam, + const struct v4l2_control* ctrl); + /* + You must implement at least the set_ctrl method if you have defined + the list above. The returned value must follow the V4L2 + specifications for the VIDIOC_G|C_CTRL ioctls. V4L2_CID_H|VCENTER + are not supported by this driver, so do not implement them. Also, + you don't have to check whether the passed values are out of bounds, + given that this is done by the core module. + */ + + struct v4l2_cropcap cropcap; + /* + Think the image sensor as a grid of R,G,B monochromatic pixels + disposed according to a particular Bayer pattern, which describes + the complete array of pixels, from (0,0) to (xmax, ymax). We will + use this coordinate system from now on. It is assumed the sensor + chip can be programmed to capture/transmit a subsection of that + array of pixels: we will call this subsection "active window". + It is not always true that the largest achievable active window can + cover the whole array of pixels. The V4L2 API defines another + area called "source rectangle", which, in turn, is a subrectangle of + the active window. The SN9C1XX chip is always programmed to read the + source rectangle. + The bounds of both the active window and the source rectangle are + specified in the cropcap substructures 'bounds' and 'defrect'. + By default, the source rectangle should cover the largest possible + area. Again, it is not always true that the largest source rectangle + can cover the entire active window, although it is a rare case for + the hardware we have. The bounds of the source rectangle _must_ be + multiple of 16 and must use the same coordinate system as indicated + before; their centers shall align initially. + If necessary, the sensor chip must be initialized during init() to + set the bounds of the active sensor window; however, by default, it + usually covers the largest achievable area (maxwidth x maxheight) + of pixels, so no particular initialization is needed, if you have + defined the correct default bounds in the structures. + See the V4L2 API for further details. + NOTE: once you have defined the bounds of the active window + (struct cropcap.bounds) you must not change them.anymore. + Only 'bounds' and 'defrect' fields are mandatory, other fields + will be ignored. + */ + + int (*set_crop)(struct sn9c102_device* cam, + const struct v4l2_rect* rect); + /* + To be called on VIDIOC_C_SETCROP. The core module always calls a + default routine which configures the appropriate SN9C1XX regs (also + scaling), but you may need to override/adjust specific stuff. + 'rect' contains width and height values that are multiple of 16: in + case you override the default function, you always have to program + the chip to match those values; on error return the corresponding + error code without rolling back. + NOTE: in case, you must program the SN9C1XX chip to get rid of + blank pixels or blank lines at the _start_ of each line or + frame after each HSYNC or VSYNC, so that the image starts with + real RGB data (see regs 0x12, 0x13) (having set H_SIZE and, + V_SIZE you don't have to care about blank pixels or blank + lines at the end of each line or frame). + */ + + struct v4l2_pix_format pix_format; + /* + What you have to define here are: 1) initial 'width' and 'height' of + the target rectangle 2) the initial 'pixelformat', which can be + either V4L2_PIX_FMT_SN9C10X, V4L2_PIX_FMT_JPEG (for ompressed video) + or V4L2_PIX_FMT_SBGGR8 3) 'priv', which we'll be used to indicate + the number of bits per pixel for uncompressed video, 8 or 9 (despite + the current value of 'pixelformat'). + NOTE 1: both 'width' and 'height' _must_ be either 1/1 or 1/2 or 1/4 + of cropcap.defrect.width and cropcap.defrect.height. I + suggest 1/1. + NOTE 2: The initial compression quality is defined by the first bit + of reg 0x17 during the initialization of the image sensor. + NOTE 3: as said above, you have to program the SN9C1XX chip to get + rid of any blank pixels, so that the output of the sensor + matches the RGB bayer sequence (i.e. BGBGBG...GRGRGR). + */ + + int (*set_pix_format)(struct sn9c102_device* cam, + const struct v4l2_pix_format* pix); + /* + To be called on VIDIOC_S_FMT, when switching from the SBGGR8 to + SN9C10X pixel format or viceversa. On error return the corresponding + error code without rolling back. + */ + + /* + Do NOT write to the data below, it's READ ONLY. It is used by the + core module to store successfully updated values of the above + settings, for rollbacks..etc..in case of errors during atomic I/O + */ + struct v4l2_queryctrl _qctrl[SN9C102_MAX_CTRLS]; + struct v4l2_rect _rect; +}; + +/*****************************************************************************/ + +/* Private ioctl's for control settings supported by some image sensors */ +#define SN9C102_V4L2_CID_DAC_MAGNITUDE (V4L2_CID_PRIVATE_BASE + 0) +#define SN9C102_V4L2_CID_GREEN_BALANCE (V4L2_CID_PRIVATE_BASE + 1) +#define SN9C102_V4L2_CID_RESET_LEVEL (V4L2_CID_PRIVATE_BASE + 2) +#define SN9C102_V4L2_CID_PIXEL_BIAS_VOLTAGE (V4L2_CID_PRIVATE_BASE + 3) +#define SN9C102_V4L2_CID_GAMMA (V4L2_CID_PRIVATE_BASE + 4) +#define SN9C102_V4L2_CID_BAND_FILTER (V4L2_CID_PRIVATE_BASE + 5) +#define SN9C102_V4L2_CID_BRIGHT_LEVEL (V4L2_CID_PRIVATE_BASE + 6) + +#endif /* _SN9C102_SENSOR_H_ */ diff --git a/drivers/staging/media/sn9c102/sn9c102_tas5110c1b.c b/drivers/staging/media/sn9c102/sn9c102_tas5110c1b.c new file mode 100644 index 000000000000..04cdfdde8564 --- /dev/null +++ b/drivers/staging/media/sn9c102/sn9c102_tas5110c1b.c @@ -0,0 +1,154 @@ +/*************************************************************************** + * Plug-in for TAS5110C1B image sensor connected to the SN9C1xx PC Camera * + * Controllers * + * * + * Copyright (C) 2004-2007 by Luca Risolia * + * * + * 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. * + ***************************************************************************/ + +#include "sn9c102_sensor.h" +#include "sn9c102_devtable.h" + + +static int tas5110c1b_init(struct sn9c102_device* cam) +{ + int err = 0; + + err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x44, 0x01}, + {0x00, 0x10}, {0x00, 0x11}, + {0x0a, 0x14}, {0x60, 0x17}, + {0x06, 0x18}, {0xfb, 0x19}); + + err += sn9c102_i2c_write(cam, 0xc0, 0x80); + + return err; +} + + +static int tas5110c1b_set_ctrl(struct sn9c102_device* cam, + const struct v4l2_control* ctrl) +{ + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_GAIN: + err += sn9c102_i2c_write(cam, 0x20, 0xf6 - ctrl->value); + break; + default: + return -EINVAL; + } + + return err ? -EIO : 0; +} + + +static int tas5110c1b_set_crop(struct sn9c102_device* cam, + const struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + int err = 0; + u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 69, + v_start = (u8)(rect->top - s->cropcap.bounds.top) + 9; + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + + /* Don't change ! */ + err += sn9c102_write_reg(cam, 0x14, 0x1a); + err += sn9c102_write_reg(cam, 0x0a, 0x1b); + err += sn9c102_write_reg(cam, sn9c102_pread_reg(cam, 0x19), 0x19); + + return err; +} + + +static int tas5110c1b_set_pix_format(struct sn9c102_device* cam, + const struct v4l2_pix_format* pix) +{ + int err = 0; + + if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) + err += sn9c102_write_reg(cam, 0x2b, 0x19); + else + err += sn9c102_write_reg(cam, 0xfb, 0x19); + + return err; +} + + +static const struct sn9c102_sensor tas5110c1b = { + .name = "TAS5110C1B", + .maintainer = "Luca Risolia ", + .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102, + .sysfs_ops = SN9C102_I2C_WRITE, + .frequency = SN9C102_I2C_100KHZ, + .interface = SN9C102_I2C_3WIRES, + .init = &tas5110c1b_init, + .qctrl = { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "global gain", + .minimum = 0x00, + .maximum = 0xf6, + .step = 0x01, + .default_value = 0x40, + .flags = 0, + }, + }, + .set_ctrl = &tas5110c1b_set_ctrl, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 352, + .height = 288, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 352, + .height = 288, + }, + }, + .set_crop = &tas5110c1b_set_crop, + .pix_format = { + .width = 352, + .height = 288, + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .priv = 8, + }, + .set_pix_format = &tas5110c1b_set_pix_format +}; + + +int sn9c102_probe_tas5110c1b(struct sn9c102_device* cam) +{ + const struct usb_device_id tas5110c1b_id_table[] = { + { USB_DEVICE(0x0c45, 0x6001), }, + { USB_DEVICE(0x0c45, 0x6005), }, + { USB_DEVICE(0x0c45, 0x60ab), }, + { } + }; + + /* Sensor detection is based on USB pid/vid */ + if (!sn9c102_match_id(cam, tas5110c1b_id_table)) + return -ENODEV; + + sn9c102_attach_sensor(cam, &tas5110c1b); + + return 0; +} diff --git a/drivers/staging/media/sn9c102/sn9c102_tas5110d.c b/drivers/staging/media/sn9c102/sn9c102_tas5110d.c new file mode 100644 index 000000000000..9372e6f9fcff --- /dev/null +++ b/drivers/staging/media/sn9c102/sn9c102_tas5110d.c @@ -0,0 +1,119 @@ +/*************************************************************************** + * Plug-in for TAS5110D image sensor connected to the SN9C1xx PC Camera * + * Controllers * + * * + * Copyright (C) 2007 by Luca Risolia * + * * + * 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. * + ***************************************************************************/ + +#include "sn9c102_sensor.h" +#include "sn9c102_devtable.h" + + +static int tas5110d_init(struct sn9c102_device* cam) +{ + int err; + + err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x04, 0x01}, + {0x0a, 0x14}, {0x60, 0x17}, + {0x06, 0x18}, {0xfb, 0x19}); + + err += sn9c102_i2c_write(cam, 0x9a, 0xca); + + return err; +} + + +static int tas5110d_set_crop(struct sn9c102_device* cam, + const struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + int err = 0; + u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 69, + v_start = (u8)(rect->top - s->cropcap.bounds.top) + 9; + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + + err += sn9c102_write_reg(cam, 0x14, 0x1a); + err += sn9c102_write_reg(cam, 0x0a, 0x1b); + + return err; +} + + +static int tas5110d_set_pix_format(struct sn9c102_device* cam, + const struct v4l2_pix_format* pix) +{ + int err = 0; + + if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) + err += sn9c102_write_reg(cam, 0x3b, 0x19); + else + err += sn9c102_write_reg(cam, 0xfb, 0x19); + + return err; +} + + +static const struct sn9c102_sensor tas5110d = { + .name = "TAS5110D", + .maintainer = "Luca Risolia ", + .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102, + .sysfs_ops = SN9C102_I2C_WRITE, + .frequency = SN9C102_I2C_100KHZ, + .interface = SN9C102_I2C_2WIRES, + .i2c_slave_id = 0x61, + .init = &tas5110d_init, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 352, + .height = 288, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 352, + .height = 288, + }, + }, + .set_crop = &tas5110d_set_crop, + .pix_format = { + .width = 352, + .height = 288, + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .priv = 8, + }, + .set_pix_format = &tas5110d_set_pix_format +}; + + +int sn9c102_probe_tas5110d(struct sn9c102_device* cam) +{ + const struct usb_device_id tas5110d_id_table[] = { + { USB_DEVICE(0x0c45, 0x6007), }, + { } + }; + + if (!sn9c102_match_id(cam, tas5110d_id_table)) + return -ENODEV; + + sn9c102_attach_sensor(cam, &tas5110d); + + return 0; +} diff --git a/drivers/staging/media/sn9c102/sn9c102_tas5130d1b.c b/drivers/staging/media/sn9c102/sn9c102_tas5130d1b.c new file mode 100644 index 000000000000..a30bbc4389f5 --- /dev/null +++ b/drivers/staging/media/sn9c102/sn9c102_tas5130d1b.c @@ -0,0 +1,165 @@ +/*************************************************************************** + * Plug-in for TAS5130D1B image sensor connected to the SN9C1xx PC Camera * + * Controllers * + * * + * Copyright (C) 2004-2007 by Luca Risolia * + * * + * 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. * + ***************************************************************************/ + +#include "sn9c102_sensor.h" +#include "sn9c102_devtable.h" + + +static int tas5130d1b_init(struct sn9c102_device* cam) +{ + int err; + + err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x20, 0x17}, + {0x04, 0x01}, {0x01, 0x10}, + {0x00, 0x11}, {0x00, 0x14}, + {0x60, 0x17}, {0x07, 0x18}); + + return err; +} + + +static int tas5130d1b_set_ctrl(struct sn9c102_device* cam, + const struct v4l2_control* ctrl) +{ + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_GAIN: + err += sn9c102_i2c_write(cam, 0x20, 0xf6 - ctrl->value); + break; + case V4L2_CID_EXPOSURE: + err += sn9c102_i2c_write(cam, 0x40, 0x47 - ctrl->value); + break; + default: + return -EINVAL; + } + + return err ? -EIO : 0; +} + + +static int tas5130d1b_set_crop(struct sn9c102_device* cam, + const struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 104, + v_start = (u8)(rect->top - s->cropcap.bounds.top) + 12; + int err = 0; + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + + /* Do NOT change! */ + err += sn9c102_write_reg(cam, 0x1f, 0x1a); + err += sn9c102_write_reg(cam, 0x1a, 0x1b); + err += sn9c102_write_reg(cam, sn9c102_pread_reg(cam, 0x19), 0x19); + + return err; +} + + +static int tas5130d1b_set_pix_format(struct sn9c102_device* cam, + const struct v4l2_pix_format* pix) +{ + int err = 0; + + if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) + err += sn9c102_write_reg(cam, 0x63, 0x19); + else + err += sn9c102_write_reg(cam, 0xf3, 0x19); + + return err; +} + + +static const struct sn9c102_sensor tas5130d1b = { + .name = "TAS5130D1B", + .maintainer = "Luca Risolia ", + .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102, + .sysfs_ops = SN9C102_I2C_WRITE, + .frequency = SN9C102_I2C_100KHZ, + .interface = SN9C102_I2C_3WIRES, + .init = &tas5130d1b_init, + .qctrl = { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "global gain", + .minimum = 0x00, + .maximum = 0xf6, + .step = 0x02, + .default_value = 0x00, + .flags = 0, + }, + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = 0x00, + .maximum = 0x47, + .step = 0x01, + .default_value = 0x00, + .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, + }, + }, + .set_crop = &tas5130d1b_set_crop, + .pix_format = { + .width = 640, + .height = 480, + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .priv = 8, + }, + .set_pix_format = &tas5130d1b_set_pix_format +}; + + +int sn9c102_probe_tas5130d1b(struct sn9c102_device* cam) +{ + const struct usb_device_id tas5130d1b_id_table[] = { + { USB_DEVICE(0x0c45, 0x6024), }, + { USB_DEVICE(0x0c45, 0x6025), }, + { USB_DEVICE(0x0c45, 0x60aa), }, + { } + }; + + /* Sensor detection is based on USB pid/vid */ + if (!sn9c102_match_id(cam, tas5130d1b_id_table)) + return -ENODEV; + + sn9c102_attach_sensor(cam, &tas5130d1b); + + return 0; +} -- cgit v1.2.3 From 2310e3c41a71f3db88039aabbd8c9ab5a4d57e65 Mon Sep 17 00:00:00 2001 From: Andrzej Hajda Date: Thu, 5 Dec 2013 08:38:40 -0300 Subject: [media] Add DT binding documentation for Samsung S5K5BAF camera sensor This patch adds the DT bindings documentation for Samsung S5K5BAF Image Sensor. Signed-off-by: Andrzej Hajda Signed-off-by: Kyungmin Park Acked-by: Mark Rutland Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- .../devicetree/bindings/media/samsung-s5k5baf.txt | 58 ++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/samsung-s5k5baf.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/media/samsung-s5k5baf.txt b/Documentation/devicetree/bindings/media/samsung-s5k5baf.txt new file mode 100644 index 000000000000..1f51e0439c96 --- /dev/null +++ b/Documentation/devicetree/bindings/media/samsung-s5k5baf.txt @@ -0,0 +1,58 @@ +Samsung S5K5BAF UXGA 1/5" 2M CMOS Image Sensor with embedded SoC ISP +-------------------------------------------------------------------- + +Required properties: + +- compatible : "samsung,s5k5baf"; +- reg : I2C slave address of the sensor; +- vdda-supply : analog power supply 2.8V (2.6V to 3.0V); +- vddreg-supply : regulator input power supply 1.8V (1.7V to 1.9V) + or 2.8V (2.6V to 3.0); +- vddio-supply : I/O power supply 1.8V (1.65V to 1.95V) + or 2.8V (2.5V to 3.1V); +- stbyn-gpios : GPIO connected to STDBYN pin; +- rstn-gpios : GPIO connected to RSTN pin; +- clocks : list of phandle and clock specifier pairs + according to common clock bindings for the + clocks described in clock-names; +- clock-names : should include "mclk" for the sensor's master clock; + +Optional properties: + +- clock-frequency : the frequency at which the "mclk" clock should be + configured to operate, in Hz; if this property is not + specified default 24 MHz value will be used. + +The device node should contain one 'port' child node with one child 'endpoint' +node, according to the bindings defined in Documentation/devicetree/bindings/ +media/video-interfaces.txt. The following are properties specific to those +nodes. + +endpoint node +------------- + +- data-lanes : (optional) specifies MIPI CSI-2 data lanes as covered in + video-interfaces.txt. If present it should be <1> - the device + supports only one data lane without re-mapping. + +Example: + +s5k5bafx@2d { + compatible = "samsung,s5k5baf"; + reg = <0x2d>; + vdda-supply = <&cam_io_en_reg>; + vddreg-supply = <&vt_core_15v_reg>; + vddio-supply = <&vtcam_reg>; + stbyn-gpios = <&gpl2 0 1>; + rstn-gpios = <&gpl2 1 1>; + clock-names = "mclk"; + clocks = <&clock_cam 0>; + clock-frequency = <24000000>; + + port { + s5k5bafx_ep: endpoint { + remote-endpoint = <&csis1_ep>; + data-lanes = <1>; + }; + }; +}; -- cgit v1.2.3 From df739b5a8078a554b54456a7659d229b176b2f8c Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Mon, 16 Dec 2013 05:16:45 -0300 Subject: [media] Documentation/DocBook/media/v4l/subdev-formats.xml: fix a typo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The xref to the v4l2-mbus-pixelcode-yuv8 table gets rendered as "Table 4.22, “YUV Formats”", so use the verb in the third person singular because it refers to "Table": s/list/lists/ Signed-off-by: Antonio Ospite Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/subdev-formats.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/DocBook/media/v4l/subdev-formats.xml b/Documentation/DocBook/media/v4l/subdev-formats.xml index bfaef5078eb8..923d2b070287 100644 --- a/Documentation/DocBook/media/v4l/subdev-formats.xml +++ b/Documentation/DocBook/media/v4l/subdev-formats.xml @@ -1178,7 +1178,7 @@ U, Y, V, Y order will be named V4L2_MBUS_FMT_UYVY8_2X8. - list existing packet YUV + lists existing packet YUV formats and describes the organization of each pixel data in each sample. When a format pattern is split across multiple samples each of the samples in the pattern is described. -- cgit v1.2.3 From 3fe6d4b9620a653f46c5566016c8b060d21e0543 Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Mon, 16 Dec 2013 05:16:46 -0300 Subject: [media] Documentation/DocBook/media/v4l: fix typo, s/packet/packed/ Change "packet" to "packed" where the doc is talking about packed data formats. Signed-off-by: Antonio Ospite Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/subdev-formats.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Documentation') diff --git a/Documentation/DocBook/media/v4l/subdev-formats.xml b/Documentation/DocBook/media/v4l/subdev-formats.xml index 923d2b070287..7331ce116f4c 100644 --- a/Documentation/DocBook/media/v4l/subdev-formats.xml +++ b/Documentation/DocBook/media/v4l/subdev-formats.xml @@ -89,7 +89,7 @@ V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE. - The following tables list existing packet RGB formats. + The following tables list existing packed RGB formats. RGB formats @@ -615,7 +615,7 @@ - The following table lists existing packet Bayer formats. The data + The following table lists existing packed Bayer formats. The data organization is given as an example for the first pixel only.
@@ -1178,7 +1178,7 @@ U, Y, V, Y order will be named V4L2_MBUS_FMT_UYVY8_2X8. - lists existing packet YUV + lists existing packed YUV formats and describes the organization of each pixel data in each sample. When a format pattern is split across multiple samples each of the samples in the pattern is described. -- cgit v1.2.3 From dc18d1bea679c6b6bfdc570469160aec5c1d3689 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 13 Dec 2013 13:13:46 -0300 Subject: [media] DocBook: drop the word 'only' There are already video output drivers that allow STREAMON without any buffers queued, and with the change in vb2 there are now more drivers like that. So saying "The ioctl will succeed only when at least one output buffer is in the incoming queue." isn't true. Just drop the word 'only'. We cannot say that it will also work if no output buffers are queued as long as not all drivers are converted to vb2. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/vidioc-streamon.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/DocBook/media/v4l/vidioc-streamon.xml b/Documentation/DocBook/media/v4l/vidioc-streamon.xml index 716ea15e54a1..65dff55079d7 100644 --- a/Documentation/DocBook/media/v4l/vidioc-streamon.xml +++ b/Documentation/DocBook/media/v4l/vidioc-streamon.xml @@ -59,7 +59,7 @@ buffers are filled (if there are any empty buffers in the incoming queue) until VIDIOC_STREAMON has been called. Accordingly the output hardware is disabled, no video signal is produced until VIDIOC_STREAMON has been called. -The ioctl will succeed only when at least one output buffer is in the +The ioctl will succeed when at least one output buffer is in the incoming queue.The VIDIOC_STREAMOFF ioctl, apart of -- cgit v1.2.3 From f90580ca0133c533763a6cb3e632a21098a382df Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Tue, 26 Nov 2013 05:31:42 -0300 Subject: [media] videodev2: Set vb2_rect's width and height as unsigned As discussed on the media summit 2013, there is no reason for the width and height to be signed. Therefore this patch is an attempt to convert those fields from __s32 to __u32. Signed-off-by: Ricardo Ribalda Delgado Acked-by: Sakari Ailus (documentation and smiapp) Acked-by: Lad, Prabhakar Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/compat.xml | 12 +++++++ Documentation/DocBook/media/v4l/dev-overlay.xml | 9 ++--- Documentation/DocBook/media/v4l/v4l2.xml | 10 +++++- Documentation/DocBook/media/v4l/vidioc-cropcap.xml | 10 ++---- drivers/media/i2c/mt9m032.c | 16 +++++---- drivers/media/i2c/mt9p031.c | 28 +++++++++------- drivers/media/i2c/mt9t001.c | 26 ++++++++------- drivers/media/i2c/mt9v032.c | 38 ++++++++++++---------- drivers/media/i2c/smiapp/smiapp-core.c | 8 ++--- drivers/media/i2c/soc_camera/mt9m111.c | 4 +-- drivers/media/i2c/tvp5150.c | 14 ++++---- drivers/media/pci/bt8xx/bttv-driver.c | 6 ++-- drivers/media/pci/saa7134/saa7134-video.c | 4 --- drivers/media/platform/soc_camera/soc_scale_crop.c | 4 +-- include/uapi/linux/videodev2.h | 4 +-- 15 files changed, 108 insertions(+), 85 deletions(-) (limited to 'Documentation') diff --git a/Documentation/DocBook/media/v4l/compat.xml b/Documentation/DocBook/media/v4l/compat.xml index 0c7195e3e093..c4cac6dbf9af 100644 --- a/Documentation/DocBook/media/v4l/compat.xml +++ b/Documentation/DocBook/media/v4l/compat.xml @@ -2523,6 +2523,18 @@ that used it. It was originally scheduled for removal in 2.6.35. +
+ V4L2 in Linux 3.14 + + + In struct v4l2_rect, the type +of width and height +fields changed from _s32 to _u32. + + + +
+
Relation of V4L2 to other Linux multimedia APIs diff --git a/Documentation/DocBook/media/v4l/dev-overlay.xml b/Documentation/DocBook/media/v4l/dev-overlay.xml index 40d1d7681439..cc6e0c5c960c 100644 --- a/Documentation/DocBook/media/v4l/dev-overlay.xml +++ b/Documentation/DocBook/media/v4l/dev-overlay.xml @@ -346,17 +346,14 @@ rectangle, in pixels. rectangle, in pixels. Offsets increase to the right and down. - __s32 + __u32 width Width of the rectangle, in pixels. - __s32 + __u32 height - Height of the rectangle, in pixels. Width and -height cannot be negative, the fields are signed for hysterical -reasons. + Height of the rectangle, in pixels. diff --git a/Documentation/DocBook/media/v4l/v4l2.xml b/Documentation/DocBook/media/v4l/v4l2.xml index 8469fe13945c..74b7f27af71a 100644 --- a/Documentation/DocBook/media/v4l/v4l2.xml +++ b/Documentation/DocBook/media/v4l/v4l2.xml @@ -140,6 +140,14 @@ structs, ioctls) must be noted in more detail in the history chapter (compat.xml), along with the possible impact on existing drivers and applications. --> + + 3.14 + 2013-11-25 + rr + Set width and height as unsigned on v4l2_rect. + + + 3.11 2013-05-26 @@ -501,7 +509,7 @@ and discussions on the V4L mailing list. Video for Linux Two API Specification - Revision 3.11 + Revision 3.14 &sub-common; diff --git a/Documentation/DocBook/media/v4l/vidioc-cropcap.xml b/Documentation/DocBook/media/v4l/vidioc-cropcap.xml index bf7cc979fdfa..1f5ed64cd75a 100644 --- a/Documentation/DocBook/media/v4l/vidioc-cropcap.xml +++ b/Documentation/DocBook/media/v4l/vidioc-cropcap.xml @@ -133,18 +133,14 @@ rectangle, in pixels. rectangle, in pixels. - __s32 + __u32 width Width of the rectangle, in pixels. - __s32 + __u32 height - Height of the rectangle, in pixels. Width -and height cannot be negative, the fields are signed for -hysterical reasons. - + Height of the rectangle, in pixels. diff --git a/drivers/media/i2c/mt9m032.c b/drivers/media/i2c/mt9m032.c index 846b15f0bf64..85ec3bacdf1c 100644 --- a/drivers/media/i2c/mt9m032.c +++ b/drivers/media/i2c/mt9m032.c @@ -459,13 +459,15 @@ static int mt9m032_set_pad_crop(struct v4l2_subdev *subdev, MT9M032_COLUMN_START_MAX); rect.top = clamp(ALIGN(crop->rect.top, 2), MT9M032_ROW_START_MIN, MT9M032_ROW_START_MAX); - rect.width = clamp(ALIGN(crop->rect.width, 2), MT9M032_COLUMN_SIZE_MIN, - MT9M032_COLUMN_SIZE_MAX); - rect.height = clamp(ALIGN(crop->rect.height, 2), MT9M032_ROW_SIZE_MIN, - MT9M032_ROW_SIZE_MAX); - - rect.width = min(rect.width, MT9M032_PIXEL_ARRAY_WIDTH - rect.left); - rect.height = min(rect.height, MT9M032_PIXEL_ARRAY_HEIGHT - rect.top); + rect.width = clamp_t(unsigned int, ALIGN(crop->rect.width, 2), + MT9M032_COLUMN_SIZE_MIN, MT9M032_COLUMN_SIZE_MAX); + rect.height = clamp_t(unsigned int, ALIGN(crop->rect.height, 2), + MT9M032_ROW_SIZE_MIN, MT9M032_ROW_SIZE_MAX); + + rect.width = min_t(unsigned int, rect.width, + MT9M032_PIXEL_ARRAY_WIDTH - rect.left); + rect.height = min_t(unsigned int, rect.height, + MT9M032_PIXEL_ARRAY_HEIGHT - rect.top); __crop = __mt9m032_get_pad_crop(sensor, fh, crop->which); diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c index 1c2303d18bf4..e5ddf47030fd 100644 --- a/drivers/media/i2c/mt9p031.c +++ b/drivers/media/i2c/mt9p031.c @@ -519,11 +519,13 @@ static int mt9p031_set_format(struct v4l2_subdev *subdev, /* Clamp the width and height to avoid dividing by zero. */ width = clamp_t(unsigned int, ALIGN(format->format.width, 2), - max(__crop->width / 7, MT9P031_WINDOW_WIDTH_MIN), + max_t(unsigned int, __crop->width / 7, + MT9P031_WINDOW_WIDTH_MIN), __crop->width); height = clamp_t(unsigned int, ALIGN(format->format.height, 2), - max(__crop->height / 8, MT9P031_WINDOW_HEIGHT_MIN), - __crop->height); + max_t(unsigned int, __crop->height / 8, + MT9P031_WINDOW_HEIGHT_MIN), + __crop->height); hratio = DIV_ROUND_CLOSEST(__crop->width, width); vratio = DIV_ROUND_CLOSEST(__crop->height, height); @@ -565,15 +567,17 @@ static int mt9p031_set_crop(struct v4l2_subdev *subdev, MT9P031_COLUMN_START_MAX); rect.top = clamp(ALIGN(crop->rect.top, 2), MT9P031_ROW_START_MIN, MT9P031_ROW_START_MAX); - rect.width = clamp(ALIGN(crop->rect.width, 2), - MT9P031_WINDOW_WIDTH_MIN, - MT9P031_WINDOW_WIDTH_MAX); - rect.height = clamp(ALIGN(crop->rect.height, 2), - MT9P031_WINDOW_HEIGHT_MIN, - MT9P031_WINDOW_HEIGHT_MAX); - - rect.width = min(rect.width, MT9P031_PIXEL_ARRAY_WIDTH - rect.left); - rect.height = min(rect.height, MT9P031_PIXEL_ARRAY_HEIGHT - rect.top); + rect.width = clamp_t(unsigned int, ALIGN(crop->rect.width, 2), + MT9P031_WINDOW_WIDTH_MIN, + MT9P031_WINDOW_WIDTH_MAX); + rect.height = clamp_t(unsigned int, ALIGN(crop->rect.height, 2), + MT9P031_WINDOW_HEIGHT_MIN, + MT9P031_WINDOW_HEIGHT_MAX); + + rect.width = min_t(unsigned int, rect.width, + MT9P031_PIXEL_ARRAY_WIDTH - rect.left); + rect.height = min_t(unsigned int, rect.height, + MT9P031_PIXEL_ARRAY_HEIGHT - rect.top); __crop = __mt9p031_get_pad_crop(mt9p031, fh, crop->pad, crop->which); diff --git a/drivers/media/i2c/mt9t001.c b/drivers/media/i2c/mt9t001.c index 796463466ef0..d41c70eaf838 100644 --- a/drivers/media/i2c/mt9t001.c +++ b/drivers/media/i2c/mt9t001.c @@ -291,10 +291,12 @@ static int mt9t001_set_format(struct v4l2_subdev *subdev, /* Clamp the width and height to avoid dividing by zero. */ width = clamp_t(unsigned int, ALIGN(format->format.width, 2), - max(__crop->width / 8, MT9T001_WINDOW_HEIGHT_MIN + 1), + max_t(unsigned int, __crop->width / 8, + MT9T001_WINDOW_HEIGHT_MIN + 1), __crop->width); height = clamp_t(unsigned int, ALIGN(format->format.height, 2), - max(__crop->height / 8, MT9T001_WINDOW_HEIGHT_MIN + 1), + max_t(unsigned int, __crop->height / 8, + MT9T001_WINDOW_HEIGHT_MIN + 1), __crop->height); hratio = DIV_ROUND_CLOSEST(__crop->width, width); @@ -339,15 +341,17 @@ static int mt9t001_set_crop(struct v4l2_subdev *subdev, rect.top = clamp(ALIGN(crop->rect.top, 2), MT9T001_ROW_START_MIN, MT9T001_ROW_START_MAX); - rect.width = clamp(ALIGN(crop->rect.width, 2), - MT9T001_WINDOW_WIDTH_MIN + 1, - MT9T001_WINDOW_WIDTH_MAX + 1); - rect.height = clamp(ALIGN(crop->rect.height, 2), - MT9T001_WINDOW_HEIGHT_MIN + 1, - MT9T001_WINDOW_HEIGHT_MAX + 1); - - rect.width = min(rect.width, MT9T001_PIXEL_ARRAY_WIDTH - rect.left); - rect.height = min(rect.height, MT9T001_PIXEL_ARRAY_HEIGHT - rect.top); + rect.width = clamp_t(unsigned int, ALIGN(crop->rect.width, 2), + MT9T001_WINDOW_WIDTH_MIN + 1, + MT9T001_WINDOW_WIDTH_MAX + 1); + rect.height = clamp_t(unsigned int, ALIGN(crop->rect.height, 2), + MT9T001_WINDOW_HEIGHT_MIN + 1, + MT9T001_WINDOW_HEIGHT_MAX + 1); + + rect.width = min_t(unsigned int, rect.width, + MT9T001_PIXEL_ARRAY_WIDTH - rect.left); + rect.height = min_t(unsigned int, rect.height, + MT9T001_PIXEL_ARRAY_HEIGHT - rect.top); __crop = __mt9t001_get_pad_crop(mt9t001, fh, crop->pad, crop->which); diff --git a/drivers/media/i2c/mt9v032.c b/drivers/media/i2c/mt9v032.c index 0d2b4a8cf911..36c504b78f2c 100644 --- a/drivers/media/i2c/mt9v032.c +++ b/drivers/media/i2c/mt9v032.c @@ -305,8 +305,8 @@ mt9v032_update_hblank(struct mt9v032 *mt9v032) if (mt9v032->version->version == MT9V034_CHIP_ID_REV1) min_hblank += (mt9v032->hratio - 1) * 10; - min_hblank = max((int)mt9v032->model->data->min_row_time - crop->width, - (int)min_hblank); + min_hblank = max_t(unsigned int, (int)mt9v032->model->data->min_row_time - crop->width, + (int)min_hblank); hblank = max_t(unsigned int, mt9v032->hblank, min_hblank); return mt9v032_write(client, MT9V032_HORIZONTAL_BLANKING, hblank); @@ -525,12 +525,14 @@ static int mt9v032_set_format(struct v4l2_subdev *subdev, format->which); /* Clamp the width and height to avoid dividing by zero. */ - width = clamp_t(unsigned int, ALIGN(format->format.width, 2), - max(__crop->width / 4, MT9V032_WINDOW_WIDTH_MIN), - __crop->width); - height = clamp_t(unsigned int, ALIGN(format->format.height, 2), - max(__crop->height / 4, MT9V032_WINDOW_HEIGHT_MIN), - __crop->height); + width = clamp(ALIGN(format->format.width, 2), + max_t(unsigned int, __crop->width / 4, + MT9V032_WINDOW_WIDTH_MIN), + __crop->width); + height = clamp(ALIGN(format->format.height, 2), + max_t(unsigned int, __crop->height / 4, + MT9V032_WINDOW_HEIGHT_MIN), + __crop->height); hratio = mt9v032_calc_ratio(__crop->width, width); vratio = mt9v032_calc_ratio(__crop->height, height); @@ -580,15 +582,17 @@ static int mt9v032_set_crop(struct v4l2_subdev *subdev, rect.top = clamp(ALIGN(crop->rect.top + 1, 2) - 1, MT9V032_ROW_START_MIN, MT9V032_ROW_START_MAX); - rect.width = clamp(ALIGN(crop->rect.width, 2), - MT9V032_WINDOW_WIDTH_MIN, - MT9V032_WINDOW_WIDTH_MAX); - rect.height = clamp(ALIGN(crop->rect.height, 2), - MT9V032_WINDOW_HEIGHT_MIN, - MT9V032_WINDOW_HEIGHT_MAX); - - rect.width = min(rect.width, MT9V032_PIXEL_ARRAY_WIDTH - rect.left); - rect.height = min(rect.height, MT9V032_PIXEL_ARRAY_HEIGHT - rect.top); + rect.width = clamp_t(unsigned int, ALIGN(crop->rect.width, 2), + MT9V032_WINDOW_WIDTH_MIN, + MT9V032_WINDOW_WIDTH_MAX); + rect.height = clamp_t(unsigned int, ALIGN(crop->rect.height, 2), + MT9V032_WINDOW_HEIGHT_MIN, + MT9V032_WINDOW_HEIGHT_MAX); + + rect.width = min_t(unsigned int, + rect.width, MT9V032_PIXEL_ARRAY_WIDTH - rect.left); + rect.height = min_t(unsigned int, + rect.height, MT9V032_PIXEL_ARRAY_HEIGHT - rect.top); __crop = __mt9v032_get_pad_crop(mt9v032, fh, crop->pad, crop->which); diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index fbd48f04b5c8..8741cae9c9f2 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -2027,8 +2027,8 @@ static int smiapp_set_crop(struct v4l2_subdev *subdev, 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); + sel->r.left = min_t(int, sel->r.left, src_size->width - sel->r.width); + sel->r.top = min_t(int, sel->r.top, src_size->height - sel->r.height); *crops[sel->pad] = sel->r; @@ -2120,8 +2120,8 @@ static int smiapp_set_selection(struct v4l2_subdev *subdev, 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 = SMIAPP_ALIGN_DIM(sel->r.width, sel->flags); + sel->r.height = SMIAPP_ALIGN_DIM(sel->r.height, sel->flags); sel->r.width = max_t(unsigned int, sensor->limits[SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE], diff --git a/drivers/media/i2c/soc_camera/mt9m111.c b/drivers/media/i2c/soc_camera/mt9m111.c index 6f4056668bbc..ccf59406a172 100644 --- a/drivers/media/i2c/soc_camera/mt9m111.c +++ b/drivers/media/i2c/soc_camera/mt9m111.c @@ -208,8 +208,8 @@ struct mt9m111 { struct mt9m111_context *ctx; struct v4l2_rect rect; /* cropping rectangle */ struct v4l2_clk *clk; - int width; /* output */ - int height; /* sizes */ + unsigned int width; /* output */ + unsigned int height; /* sizes */ struct mutex power_lock; /* lock to protect power_count */ int power_count; const struct mt9m111_datafmt *fmt; diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index 2ed05b67218b..542d2528b3f9 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -863,7 +863,7 @@ static int tvp5150_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) struct v4l2_rect rect = a->c; struct tvp5150 *decoder = to_tvp5150(sd); v4l2_std_id std; - int hmax; + unsigned int hmax; v4l2_dbg(1, debug, sd, "%s left=%d, top=%d, width=%d, height=%d\n", __func__, rect.left, rect.top, rect.width, rect.height); @@ -873,9 +873,9 @@ static int tvp5150_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) /* tvp5150 has some special limits */ rect.left = clamp(rect.left, 0, TVP5150_MAX_CROP_LEFT); - rect.width = clamp(rect.width, - TVP5150_H_MAX - TVP5150_MAX_CROP_LEFT - rect.left, - TVP5150_H_MAX - rect.left); + rect.width = clamp_t(unsigned int, rect.width, + TVP5150_H_MAX - TVP5150_MAX_CROP_LEFT - rect.left, + TVP5150_H_MAX - rect.left); rect.top = clamp(rect.top, 0, TVP5150_MAX_CROP_TOP); /* Calculate height based on current standard */ @@ -889,9 +889,9 @@ static int tvp5150_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) else hmax = TVP5150_V_MAX_OTHERS; - rect.height = clamp(rect.height, - hmax - TVP5150_MAX_CROP_TOP - rect.top, - hmax - rect.top); + rect.height = clamp_t(unsigned int, rect.height, + hmax - TVP5150_MAX_CROP_TOP - rect.top, + hmax - rect.top); tvp5150_write(sd, TVP5150_VERT_BLANKING_START, rect.top); tvp5150_write(sd, TVP5150_VERT_BLANKING_STOP, diff --git a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c index c2aaadcd9318..afcd53bfcf8e 100644 --- a/drivers/media/pci/bt8xx/bttv-driver.c +++ b/drivers/media/pci/bt8xx/bttv-driver.c @@ -1126,9 +1126,9 @@ bttv_crop_calc_limits(struct bttv_crop *c) c->min_scaled_height = 32; } else { c->min_scaled_width = - (max(48, c->rect.width >> 4) + 3) & ~3; + (max_t(unsigned int, 48, c->rect.width >> 4) + 3) & ~3; c->min_scaled_height = - max(32, c->rect.height >> 4); + max_t(unsigned int, 32, c->rect.height >> 4); } c->max_scaled_width = c->rect.width & ~3; @@ -2024,7 +2024,7 @@ limit_scaled_size_lock (struct bttv_fh * fh, /* We cannot scale up. When the scaled image is larger than crop.rect we adjust the crop.rect as required by the V4L2 spec, hence cropcap.bounds are our limit. */ - max_width = min(b->width, (__s32) MAX_HACTIVE); + max_width = min_t(unsigned int, b->width, MAX_HACTIVE); max_height = b->height; /* We cannot capture the same line as video and VBI data. diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c index 36026b1cb3f8..eb472b5b26a0 100644 --- a/drivers/media/pci/saa7134/saa7134-video.c +++ b/drivers/media/pci/saa7134/saa7134-video.c @@ -1729,10 +1729,6 @@ static int saa7134_s_crop(struct file *file, void *f, const struct v4l2_crop *cr if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) return -EINVAL; - if (crop->c.height < 0) - return -EINVAL; - if (crop->c.width < 0) - return -EINVAL; if (res_locked(dev, RESOURCE_OVERLAY)) return -EBUSY; diff --git a/drivers/media/platform/soc_camera/soc_scale_crop.c b/drivers/media/platform/soc_camera/soc_scale_crop.c index cbd3a34f4f3f..8e74fb7f2a07 100644 --- a/drivers/media/platform/soc_camera/soc_scale_crop.c +++ b/drivers/media/platform/soc_camera/soc_scale_crop.c @@ -141,8 +141,8 @@ int soc_camera_client_s_crop(struct v4l2_subdev *sd, * Popular special case - some cameras can only handle fixed sizes like * QVGA, VGA,... Take care to avoid infinite loop. */ - width = max(cam_rect->width, 2); - height = max(cam_rect->height, 2); + width = max_t(unsigned int, cam_rect->width, 2); + height = max_t(unsigned int, cam_rect->height, 2); /* * Loop as long as sensor is not covering the requested rectangle and diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 437f1b0f8937..6ae7bbe988cc 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -207,8 +207,8 @@ enum v4l2_priority { struct v4l2_rect { __s32 left; __s32 top; - __s32 width; - __s32 height; + __u32 width; + __u32 height; }; struct v4l2_fract { -- cgit v1.2.3 From 4773ab99aa8bda57de22bf54ddbaa1a941b25fb0 Mon Sep 17 00:00:00 2001 From: Arun Kumar K Date: Fri, 15 Nov 2013 02:29:22 -0300 Subject: [media] s5p-mfc: Add QP setting support for vp8 encoder Adds v4l2 controls to set MIN, MAX QP values and I, P frame QP for vp8 encoder. Signed-off-by: Kiran AVND Signed-off-by: Arun Kumar K Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/controls.xml | 32 ++++++++++++++++++ drivers/media/platform/s5p-mfc/s5p_mfc_common.h | 4 +++ drivers/media/platform/s5p-mfc/s5p_mfc_enc.c | 44 +++++++++++++++++++++++++ drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c | 20 +++++++++++ drivers/media/v4l2-core/v4l2-ctrls.c | 4 +++ include/uapi/linux/v4l2-controls.h | 4 +++ 6 files changed, 108 insertions(+) (limited to 'Documentation') diff --git a/Documentation/DocBook/media/v4l/controls.xml b/Documentation/DocBook/media/v4l/controls.xml index 7a3b49b3cc3b..e4db4ac4533f 100644 --- a/Documentation/DocBook/media/v4l/controls.xml +++ b/Documentation/DocBook/media/v4l/controls.xml @@ -3161,6 +3161,38 @@ V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_REF_PERIOD as a golden frame. + + + V4L2_CID_MPEG_VIDEO_VPX_MIN_QP + integer + + Minimum quantization parameter for VP8. + + + + + V4L2_CID_MPEG_VIDEO_VPX_MAX_QP + integer + + Maximum quantization parameter for VP8. + + + + + V4L2_CID_MPEG_VIDEO_VPX_I_FRAME_QP  + integer + + Quantization parameter for an I frame for VP8. + + + + + V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP  + integer + + Quantization parameter for a P frame for VP8. + + diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h index 6920b546181a..d91f7575b088 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h @@ -422,6 +422,10 @@ struct s5p_mfc_vp8_enc_params { enum v4l2_vp8_golden_frame_sel golden_frame_sel; u8 hier_layer; u8 hier_layer_qp[3]; + u8 rc_min_qp; + u8 rc_max_qp; + u8 rc_frame_qp; + u8 rc_p_frame_qp; }; /** diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c index f0b41f85ac71..cdf672c8f11e 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c @@ -618,6 +618,38 @@ static struct mfc_control controls[] = { .default_value = V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_USE_PREV, .menu_skip_mask = 0, }, + { + .id = V4L2_CID_MPEG_VIDEO_VPX_MAX_QP, + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 127, + .step = 1, + .default_value = 127, + }, + { + .id = V4L2_CID_MPEG_VIDEO_VPX_MIN_QP, + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 11, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_MPEG_VIDEO_VPX_I_FRAME_QP, + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 127, + .step = 1, + .default_value = 10, + }, + { + .id = V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP, + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 127, + .step = 1, + .default_value = 10, + }, }; #define NUM_CTRLS ARRAY_SIZE(controls) @@ -1557,6 +1589,18 @@ static int s5p_mfc_enc_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_SEL: p->codec.vp8.golden_frame_sel = ctrl->val; break; + case V4L2_CID_MPEG_VIDEO_VPX_MIN_QP: + p->codec.vp8.rc_min_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_VPX_MAX_QP: + p->codec.vp8.rc_max_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_VPX_I_FRAME_QP: + p->codec.vp8.rc_frame_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP: + p->codec.vp8.rc_p_frame_qp = ctrl->val; + break; default: v4l2_err(&dev->v4l2_dev, "Invalid control, id=%d, val=%d\n", ctrl->id, ctrl->val); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c index 461358c4a790..b4886d636dbe 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c @@ -1218,6 +1218,26 @@ static int s5p_mfc_set_enc_params_vp8(struct s5p_mfc_ctx *ctx) WRITEL(reg, S5P_FIMV_E_RC_FRAME_RATE_V6); } + /* frame QP */ + reg &= ~(0x7F); + reg |= p_vp8->rc_frame_qp & 0x7F; + WRITEL(reg, S5P_FIMV_E_RC_CONFIG_V6); + + /* other QPs */ + WRITEL(0x0, S5P_FIMV_E_FIXED_PICTURE_QP_V6); + if (!p->rc_frame && !p->rc_mb) { + reg = 0; + reg |= ((p_vp8->rc_p_frame_qp & 0x7F) << 8); + reg |= p_vp8->rc_frame_qp & 0x7F; + WRITEL(reg, S5P_FIMV_E_FIXED_PICTURE_QP_V6); + } + + /* max QP */ + reg = ((p_vp8->rc_max_qp & 0x7F) << 8); + /* min QP */ + reg |= p_vp8->rc_min_qp & 0x7F; + WRITEL(reg, S5P_FIMV_E_RC_QP_BOUND_V6); + /* vbv buffer size */ if (p->frame_skip_mode == V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT) { diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index fb46790d0eca..20840dff78e6 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -745,6 +745,10 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_MPEG_VIDEO_VPX_FILTER_SHARPNESS: return "VPX Deblocking Effect Control"; case V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_REF_PERIOD: return "VPX Golden Frame Refresh Period"; case V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_SEL: return "VPX Golden Frame Indicator"; + case V4L2_CID_MPEG_VIDEO_VPX_MIN_QP: return "VPX Minimum QP Value"; + case V4L2_CID_MPEG_VIDEO_VPX_MAX_QP: return "VPX Maximum QP Value"; + case V4L2_CID_MPEG_VIDEO_VPX_I_FRAME_QP: return "VPX I-Frame QP Value"; + case V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP: return "VPX P-Frame QP Value"; /* CAMERA controls */ /* Keep the order of the 'case's the same as in videodev2.h! */ diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index 8e4d1d974eda..28cddb45afe0 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -558,6 +558,10 @@ enum v4l2_vp8_golden_frame_sel { V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_USE_PREV = 0, V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_USE_REF_PERIOD = 1, }; +#define V4L2_CID_MPEG_VIDEO_VPX_MIN_QP (V4L2_CID_MPEG_BASE+507) +#define V4L2_CID_MPEG_VIDEO_VPX_MAX_QP (V4L2_CID_MPEG_BASE+508) +#define V4L2_CID_MPEG_VIDEO_VPX_I_FRAME_QP (V4L2_CID_MPEG_BASE+509) +#define V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP (V4L2_CID_MPEG_BASE+510) /* MPEG-class control IDs specific to the CX2341x driver as defined by V4L2 */ #define V4L2_CID_MPEG_CX2341X_BASE (V4L2_CTRL_CLASS_MPEG | 0x1000) -- cgit v1.2.3 From bbd8f3fef9d289fcfddaefccc2e5a2355da5d2f4 Mon Sep 17 00:00:00 2001 From: Kiran AVND Date: Mon, 16 Dec 2013 06:40:42 -0300 Subject: [media] s5p-mfc: Add controls to set vp8 enc profile Add v4l2 controls to set desired profile for VP8 encoder. Acceptable levels for VP8 encoder are 0: Version 0 1: Version 1 2: Version 2 3: Version 3 Signed-off-by: Kiran AVND Signed-off-by: Pawel Osciak Signed-off-by: Arun Kumar K Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/controls.xml | 9 +++++++++ drivers/media/platform/s5p-mfc/s5p_mfc_common.h | 1 + drivers/media/platform/s5p-mfc/s5p_mfc_enc.c | 11 +++++++++++ drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c | 6 ++---- drivers/media/v4l2-core/v4l2-ctrls.c | 1 + include/uapi/linux/v4l2-controls.h | 1 + 6 files changed, 25 insertions(+), 4 deletions(-) (limited to 'Documentation') diff --git a/Documentation/DocBook/media/v4l/controls.xml b/Documentation/DocBook/media/v4l/controls.xml index e4db4ac4533f..a5a3188e5af7 100644 --- a/Documentation/DocBook/media/v4l/controls.xml +++ b/Documentation/DocBook/media/v4l/controls.xml @@ -3193,6 +3193,15 @@ V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_REF_PERIOD as a golden frame. Quantization parameter for a P frame for VP8. + + + V4L2_CID_MPEG_VIDEO_VPX_PROFILE  + integer + + Select the desired profile for VPx encoder. +Acceptable values are 0, 1, 2 and 3 corresponding to encoder profiles 0, 1, 2 and 3. + + diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h index 3874f8b639fe..f723f1f2f578 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h @@ -417,6 +417,7 @@ struct s5p_mfc_vp8_enc_params { u8 rc_max_qp; u8 rc_frame_qp; u8 rc_p_frame_qp; + u8 profile; }; /** diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c index cdf672c8f11e..91b6e020ddf3 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c @@ -650,6 +650,14 @@ static struct mfc_control controls[] = { .step = 1, .default_value = 10, }, + { + .id = V4L2_CID_MPEG_VIDEO_VPX_PROFILE, + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 3, + .step = 1, + .default_value = 0, + }, }; #define NUM_CTRLS ARRAY_SIZE(controls) @@ -1601,6 +1609,9 @@ static int s5p_mfc_enc_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP: p->codec.vp8.rc_p_frame_qp = ctrl->val; break; + case V4L2_CID_MPEG_VIDEO_VPX_PROFILE: + p->codec.vp8.profile = ctrl->val; + break; default: v4l2_err(&dev->v4l2_dev, "Invalid control, id=%d, val=%d\n", ctrl->id, ctrl->val); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c index b4886d636dbe..f6ff2dbf3a1d 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c @@ -1197,10 +1197,8 @@ static int s5p_mfc_set_enc_params_vp8(struct s5p_mfc_ctx *ctx) reg |= ((p->num_b_frame & 0x3) << 16); WRITEL(reg, S5P_FIMV_E_GOP_CONFIG_V6); - /* profile & level */ - reg = 0; - /** profile */ - reg |= (0x1 << 4); + /* profile - 0 ~ 3 */ + reg = p_vp8->profile & 0x3; WRITEL(reg, S5P_FIMV_E_PICTURE_PROFILE_V6); /* rate control config. */ diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 20840dff78e6..6ff002bd5909 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -749,6 +749,7 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_MPEG_VIDEO_VPX_MAX_QP: return "VPX Maximum QP Value"; case V4L2_CID_MPEG_VIDEO_VPX_I_FRAME_QP: return "VPX I-Frame QP Value"; case V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP: return "VPX P-Frame QP Value"; + case V4L2_CID_MPEG_VIDEO_VPX_PROFILE: return "VPX Profile"; /* CAMERA controls */ /* Keep the order of the 'case's the same as in videodev2.h! */ diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index 28cddb45afe0..2cbe605bbe04 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -562,6 +562,7 @@ enum v4l2_vp8_golden_frame_sel { #define V4L2_CID_MPEG_VIDEO_VPX_MAX_QP (V4L2_CID_MPEG_BASE+508) #define V4L2_CID_MPEG_VIDEO_VPX_I_FRAME_QP (V4L2_CID_MPEG_BASE+509) #define V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP (V4L2_CID_MPEG_BASE+510) +#define V4L2_CID_MPEG_VIDEO_VPX_PROFILE (V4L2_CID_MPEG_BASE+511) /* MPEG-class control IDs specific to the CX2341x driver as defined by V4L2 */ #define V4L2_CID_MPEG_CX2341X_BASE (V4L2_CTRL_CLASS_MPEG | 0x1000) -- cgit v1.2.3