diff options
Diffstat (limited to 'drivers/media/video')
302 files changed, 38943 insertions, 7438 deletions
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index f574dc012ca..b303a3f8a9f 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -467,6 +467,20 @@ config VIDEO_OV7670 OV7670 VGA camera. It currently only works with the M88ALP01 controller. +config VIDEO_MT9P031 + tristate "Aptina MT9P031 support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + ---help--- + This is a Video4Linux2 sensor-level driver for the Aptina + (Micron) mt9p031 5 Mpixel camera. + +config VIDEO_MT9T001 + tristate "Aptina MT9T001 support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + ---help--- + This is a Video4Linux2 sensor-level driver for the Aptina + (Micron) mt0t001 3 Mpixel camera. + config VIDEO_MT9V011 tristate "Micron mt9v011 sensor support" depends on I2C && VIDEO_V4L2 @@ -489,6 +503,27 @@ config VIDEO_TCM825X This is a driver for the Toshiba TCM825x VGA camera sensor. It is used for example in Nokia N800. +config VIDEO_SR030PC30 + tristate "Siliconfile SR030PC30 sensor support" + depends on I2C && VIDEO_V4L2 + ---help--- + This driver supports SR030PC30 VGA camera from Siliconfile + +config VIDEO_NOON010PC30 + tristate "Siliconfile NOON010PC30 sensor support" + depends on I2C && VIDEO_V4L2 && EXPERIMENTAL && VIDEO_V4L2_SUBDEV_API + ---help--- + This driver supports NOON010PC30 CIF camera from Siliconfile + +source "drivers/media/video/m5mols/Kconfig" + +config VIDEO_S5K6AA + tristate "Samsung S5K6AAFX sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + ---help--- + This is a V4L2 sensor-level driver for Samsung S5K6AA(FX) 1.3M + camera sensor with an embedded SoC image signal processor. + comment "Flash devices" config VIDEO_ADP1653 @@ -708,6 +743,8 @@ source "drivers/media/video/cx88/Kconfig" source "drivers/media/video/cx23885/Kconfig" +source "drivers/media/video/cx25821/Kconfig" + source "drivers/media/video/au0828/Kconfig" source "drivers/media/video/ivtv/Kconfig" @@ -737,12 +774,6 @@ config VIDEO_M32R_AR_M64278 To compile this driver as a module, choose M here: the module will be called arv. -config VIDEO_SR030PC30 - tristate "SR030PC30 VGA camera sensor support" - depends on I2C && VIDEO_V4L2 - ---help--- - This driver supports SR030PC30 VGA camera from Siliconfile - config VIDEO_VIA_CAMERA tristate "VIAFB camera controller support" depends on FB_VIA @@ -753,18 +784,9 @@ config VIDEO_VIA_CAMERA Chrome9 chipsets. Currently only tested on OLPC xo-1.5 systems with ov7670 sensors. -config VIDEO_NOON010PC30 - tristate "NOON010PC30 CIF camera sensor support" - depends on I2C && VIDEO_V4L2 - ---help--- - This driver supports NOON010PC30 CIF camera from Siliconfile - -source "drivers/media/video/m5mols/Kconfig" - config VIDEO_OMAP3 tristate "OMAP 3 Camera support (EXPERIMENTAL)" - select OMAP_IOMMU - depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && ARCH_OMAP3 && EXPERIMENTAL + depends on OMAP_IOVMM && VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && ARCH_OMAP3 && EXPERIMENTAL ---help--- Driver for an OMAP 3 camera controller. @@ -949,8 +971,9 @@ config VIDEO_MX2 Interface config VIDEO_SAMSUNG_S5P_FIMC - tristate "Samsung S5P and EXYNOS4 camera host interface driver" - depends on VIDEO_DEV && VIDEO_V4L2 && PLAT_S5P + tristate "Samsung S5P and EXYNOS4 camera interface driver (EXPERIMENTAL)" + depends on VIDEO_V4L2 && I2C && PLAT_S5P && PM_RUNTIME && \ + VIDEO_V4L2_SUBDEV_API && EXPERIMENTAL select VIDEOBUF2_DMA_CONTIG select V4L2_MEM2MEM_DEV ---help--- @@ -1004,6 +1027,8 @@ source "drivers/media/video/tlg2300/Kconfig" source "drivers/media/video/cx231xx/Kconfig" +source "drivers/media/video/tm6000/Kconfig" + source "drivers/media/video/usbvision/Kconfig" source "drivers/media/video/et61x251/Kconfig" diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 272390072ae..117f9c4b4cb 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -65,11 +65,14 @@ obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o obj-$(CONFIG_VIDEO_OV7670) += ov7670.o obj-$(CONFIG_VIDEO_TCM825X) += tcm825x.o obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o +obj-$(CONFIG_VIDEO_MT9P031) += mt9p031.o +obj-$(CONFIG_VIDEO_MT9T001) += mt9t001.o obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o obj-$(CONFIG_VIDEO_NOON010PC30) += noon010pc30.o obj-$(CONFIG_VIDEO_M5MOLS) += m5mols/ +obj-$(CONFIG_VIDEO_S5K6AA) += s5k6aa.o obj-$(CONFIG_VIDEO_ADP1653) += adp1653.o obj-$(CONFIG_SOC_CAMERA_IMX074) += imx074.o @@ -102,9 +105,11 @@ obj-$(CONFIG_VIDEO_CX88) += cx88/ obj-$(CONFIG_VIDEO_EM28XX) += em28xx/ obj-$(CONFIG_VIDEO_TLG2300) += tlg2300/ obj-$(CONFIG_VIDEO_CX231XX) += cx231xx/ +obj-$(CONFIG_VIDEO_CX25821) += cx25821/ obj-$(CONFIG_VIDEO_USBVISION) += usbvision/ obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2/ obj-$(CONFIG_VIDEO_CPIA2) += cpia2/ +obj-$(CONFIG_VIDEO_TM6000) += tm6000/ obj-$(CONFIG_VIDEO_MXB) += mxb.o obj-$(CONFIG_VIDEO_HEXIUM_ORION) += hexium_orion.o obj-$(CONFIG_VIDEO_HEXIUM_GEMINI) += hexium_gemini.o @@ -190,6 +195,6 @@ obj-y += davinci/ obj-$(CONFIG_ARCH_OMAP) += omap/ -EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core -EXTRA_CFLAGS += -Idrivers/media/dvb/frontends -EXTRA_CFLAGS += -Idrivers/media/common/tuners +ccflags-y += -Idrivers/media/dvb/dvb-core +ccflags-y += -Idrivers/media/dvb/frontends +ccflags-y += -Idrivers/media/common/tuners diff --git a/drivers/media/video/adp1653.c b/drivers/media/video/adp1653.c index be7befd6094..12eedf4d515 100644 --- a/drivers/media/video/adp1653.c +++ b/drivers/media/video/adp1653.c @@ -31,7 +31,9 @@ */ #include <linux/delay.h> +#include <linux/module.h> #include <linux/i2c.h> +#include <linux/module.h> #include <linux/slab.h> #include <linux/version.h> #include <media/adp1653.h> @@ -258,7 +260,7 @@ static int adp1653_init_controls(struct adp1653_flash *flash) if (flash->ctrls.error) return flash->ctrls.error; - fault->is_volatile = 1; + fault->flags |= V4L2_CTRL_FLAG_VOLATILE; flash->subdev.ctrl_handler = &flash->ctrls; return 0; @@ -413,6 +415,10 @@ static int adp1653_probe(struct i2c_client *client, struct adp1653_flash *flash; int ret; + /* we couldn't work without platform data */ + if (client->dev.platform_data == NULL) + return -ENODEV; + flash = kzalloc(sizeof(*flash), GFP_KERNEL); if (flash == NULL) return -ENOMEM; @@ -425,12 +431,21 @@ static int adp1653_probe(struct i2c_client *client, flash->subdev.internal_ops = &adp1653_internal_ops; flash->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - adp1653_init_controls(flash); + ret = adp1653_init_controls(flash); + if (ret) + goto free_and_quit; ret = media_entity_init(&flash->subdev.entity, 0, NULL, 0); if (ret < 0) - kfree(flash); + goto free_and_quit; + + flash->subdev.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH; + + return 0; +free_and_quit: + v4l2_ctrl_handler_free(&flash->ctrls); + kfree(flash); return ret; } diff --git a/drivers/media/video/adv7175.c b/drivers/media/video/adv7175.c index d2327dbb473..206078eca85 100644 --- a/drivers/media/video/adv7175.c +++ b/drivers/media/video/adv7175.c @@ -61,6 +61,11 @@ static inline struct adv7175 *to_adv7175(struct v4l2_subdev *sd) static char *inputs[] = { "pass_through", "play_back", "color_bar" }; +static enum v4l2_mbus_pixelcode adv7175_codes[] = { + V4L2_MBUS_FMT_UYVY8_2X8, + V4L2_MBUS_FMT_UYVY8_1X16, +}; + /* ----------------------------------------------------------------------- */ static inline int adv7175_write(struct v4l2_subdev *sd, u8 reg, u8 value) @@ -296,6 +301,60 @@ static int adv7175_s_routing(struct v4l2_subdev *sd, return 0; } +static int adv7175_enum_fmt(struct v4l2_subdev *sd, unsigned int index, + enum v4l2_mbus_pixelcode *code) +{ + if (index >= ARRAY_SIZE(adv7175_codes)) + return -EINVAL; + + *code = adv7175_codes[index]; + return 0; +} + +static int adv7175_g_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + u8 val = adv7175_read(sd, 0x7); + + if ((val & 0x40) == (1 << 6)) + mf->code = V4L2_MBUS_FMT_UYVY8_1X16; + else + mf->code = V4L2_MBUS_FMT_UYVY8_2X8; + + mf->colorspace = V4L2_COLORSPACE_SMPTE170M; + mf->width = 0; + mf->height = 0; + mf->field = V4L2_FIELD_ANY; + + return 0; +} + +static int adv7175_s_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + u8 val = adv7175_read(sd, 0x7); + int ret; + + switch (mf->code) { + case V4L2_MBUS_FMT_UYVY8_2X8: + val &= ~0x40; + break; + + case V4L2_MBUS_FMT_UYVY8_1X16: + val |= 0x40; + break; + + default: + v4l2_dbg(1, debug, sd, + "illegal v4l2_mbus_framefmt code: %d\n", mf->code); + return -EINVAL; + } + + ret = adv7175_write(sd, 0x7, val); + + return ret; +} + static int adv7175_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) { struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -324,6 +383,9 @@ static const struct v4l2_subdev_core_ops adv7175_core_ops = { static const struct v4l2_subdev_video_ops adv7175_video_ops = { .s_std_output = adv7175_s_std_output, .s_routing = adv7175_s_routing, + .s_mbus_fmt = adv7175_s_fmt, + .g_mbus_fmt = adv7175_g_fmt, + .enum_mbus_fmt = adv7175_enum_fmt, }; static const struct v4l2_subdev_ops adv7175_ops = { diff --git a/drivers/media/video/ak881x.c b/drivers/media/video/ak881x.c index b388654d48c..53c496c00fb 100644 --- a/drivers/media/video/ak881x.c +++ b/drivers/media/video/ak881x.c @@ -13,6 +13,7 @@ #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/videodev2.h> +#include <linux/module.h> #include <media/ak881x.h> #include <media/v4l2-chip-ident.h> diff --git a/drivers/media/video/atmel-isi.c b/drivers/media/video/atmel-isi.c index 7b89f00501b..8c775c59e12 100644 --- a/drivers/media/video/atmel-isi.c +++ b/drivers/media/video/atmel-isi.c @@ -94,6 +94,7 @@ struct atmel_isi { unsigned int irq; struct isi_platform_data *pdata; + u16 width_flags; /* max 12 bits */ struct list_head video_buffer_list; struct frame_buffer *active; @@ -248,9 +249,9 @@ static int atmel_isi_wait_status(struct atmel_isi *isi, int wait_reset) /* ------------------------------------------------------------------ Videobuf operations ------------------------------------------------------------------*/ -static int queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, - unsigned int *nplanes, unsigned long sizes[], - void *alloc_ctxs[]) +static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) { struct soc_camera_device *icd = soc_camera_from_vb2q(vq); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); @@ -341,7 +342,7 @@ static int buffer_prepare(struct vb2_buffer *vb) /* Initialize the dma descriptor */ desc->p_fbd->fb_address = - vb2_dma_contig_plane_paddr(vb, 0); + vb2_dma_contig_plane_dma_addr(vb, 0); desc->p_fbd->next_fbd_address = 0; set_dma_ctrl(desc->p_fbd, ISI_DMA_CTRL_WB); @@ -404,12 +405,13 @@ static void buffer_queue(struct vb2_buffer *vb) if (isi->active == NULL) { isi->active = buf; - start_dma(isi, buf); + if (vb2_is_streaming(vb->vb2_queue)) + start_dma(isi, buf); } spin_unlock_irqrestore(&isi->lock, flags); } -static int start_streaming(struct vb2_queue *vq) +static int start_streaming(struct vb2_queue *vq, unsigned int count) { struct soc_camera_device *icd = soc_camera_from_vb2q(vq); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); @@ -431,17 +433,26 @@ static int start_streaming(struct vb2_queue *vq) ret = wait_event_interruptible(isi->vsync_wq, isi->state != ISI_STATE_IDLE); if (ret) - return ret; + goto err; - if (isi->state != ISI_STATE_READY) - return -EIO; + if (isi->state != ISI_STATE_READY) { + ret = -EIO; + goto err; + } spin_lock_irq(&isi->lock); isi->state = ISI_STATE_WAIT_SOF; isi_writel(isi, ISI_INTDIS, ISI_SR_VSYNC); + if (count) + start_dma(isi, isi->active); spin_unlock_irq(&isi->lock); return 0; +err: + isi->active = NULL; + isi->sequence = 0; + INIT_LIST_HEAD(&isi->video_buffer_list); + return ret; } /* abort streaming and wait for last buffer */ @@ -637,50 +648,42 @@ static bool isi_camera_packing_supported(const struct soc_mbus_pixelfmt *fmt) fmt->packing == SOC_MBUS_PACKING_EXTEND16); } -static unsigned long make_bus_param(struct atmel_isi *isi) -{ - unsigned long flags; - /* - * Platform specified synchronization and pixel clock polarities are - * only a recommendation and are only used during probing. Atmel ISI - * camera interface only works in master mode, i.e., uses HSYNC and - * VSYNC signals from the sensor - */ - flags = SOCAM_MASTER | - SOCAM_HSYNC_ACTIVE_HIGH | - SOCAM_HSYNC_ACTIVE_LOW | - SOCAM_VSYNC_ACTIVE_HIGH | - SOCAM_VSYNC_ACTIVE_LOW | - SOCAM_PCLK_SAMPLE_RISING | - SOCAM_PCLK_SAMPLE_FALLING | - SOCAM_DATA_ACTIVE_HIGH; - - if (isi->pdata->data_width_flags & ISI_DATAWIDTH_10) - flags |= SOCAM_DATAWIDTH_10; - - if (isi->pdata->data_width_flags & ISI_DATAWIDTH_8) - flags |= SOCAM_DATAWIDTH_8; - - if (flags & SOCAM_DATAWIDTH_MASK) - return flags; - - return 0; -} +#define ISI_BUS_PARAM (V4L2_MBUS_MASTER | \ + V4L2_MBUS_HSYNC_ACTIVE_HIGH | \ + V4L2_MBUS_HSYNC_ACTIVE_LOW | \ + V4L2_MBUS_VSYNC_ACTIVE_HIGH | \ + V4L2_MBUS_VSYNC_ACTIVE_LOW | \ + V4L2_MBUS_PCLK_SAMPLE_RISING | \ + V4L2_MBUS_PCLK_SAMPLE_FALLING | \ + V4L2_MBUS_DATA_ACTIVE_HIGH) static int isi_camera_try_bus_param(struct soc_camera_device *icd, unsigned char buswidth) { + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct atmel_isi *isi = ici->priv; - unsigned long camera_flags; + struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,}; + unsigned long common_flags; int ret; - camera_flags = icd->ops->query_bus_param(icd); - ret = soc_camera_bus_param_compatible(camera_flags, - make_bus_param(isi)); - if (!ret) - return -EINVAL; - return 0; + ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg); + if (!ret) { + common_flags = soc_mbus_config_compatible(&cfg, + ISI_BUS_PARAM); + if (!common_flags) { + dev_warn(icd->parent, + "Flags incompatible: camera 0x%x, host 0x%x\n", + cfg.flags, ISI_BUS_PARAM); + return -EINVAL; + } + } else if (ret != -ENOIOCTLCMD) { + return ret; + } + + if ((1 << (buswidth - 1)) & isi->width_flags) + return 0; + return -EINVAL; } @@ -802,59 +805,71 @@ static int isi_camera_querycap(struct soc_camera_host *ici, static int isi_camera_set_bus_param(struct soc_camera_device *icd, u32 pixfmt) { + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct atmel_isi *isi = ici->priv; - unsigned long bus_flags, camera_flags, common_flags; + struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,}; + unsigned long common_flags; int ret; u32 cfg1 = 0; - camera_flags = icd->ops->query_bus_param(icd); - - bus_flags = make_bus_param(isi); - common_flags = soc_camera_bus_param_compatible(camera_flags, bus_flags); - dev_dbg(icd->parent, "Flags cam: 0x%lx host: 0x%lx common: 0x%lx\n", - camera_flags, bus_flags, common_flags); - if (!common_flags) - return -EINVAL; + ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg); + if (!ret) { + common_flags = soc_mbus_config_compatible(&cfg, + ISI_BUS_PARAM); + if (!common_flags) { + dev_warn(icd->parent, + "Flags incompatible: camera 0x%x, host 0x%x\n", + cfg.flags, ISI_BUS_PARAM); + return -EINVAL; + } + } else if (ret != -ENOIOCTLCMD) { + return ret; + } else { + common_flags = ISI_BUS_PARAM; + } + dev_dbg(icd->parent, "Flags cam: 0x%x host: 0x%x common: 0x%lx\n", + cfg.flags, ISI_BUS_PARAM, common_flags); /* Make choises, based on platform preferences */ - if ((common_flags & SOCAM_HSYNC_ACTIVE_HIGH) && - (common_flags & SOCAM_HSYNC_ACTIVE_LOW)) { + if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) && + (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) { if (isi->pdata->hsync_act_low) - common_flags &= ~SOCAM_HSYNC_ACTIVE_HIGH; + common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH; else - common_flags &= ~SOCAM_HSYNC_ACTIVE_LOW; + common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW; } - if ((common_flags & SOCAM_VSYNC_ACTIVE_HIGH) && - (common_flags & SOCAM_VSYNC_ACTIVE_LOW)) { + if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) && + (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) { if (isi->pdata->vsync_act_low) - common_flags &= ~SOCAM_VSYNC_ACTIVE_HIGH; + common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH; else - common_flags &= ~SOCAM_VSYNC_ACTIVE_LOW; + common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW; } - if ((common_flags & SOCAM_PCLK_SAMPLE_RISING) && - (common_flags & SOCAM_PCLK_SAMPLE_FALLING)) { + if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) && + (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) { if (isi->pdata->pclk_act_falling) - common_flags &= ~SOCAM_PCLK_SAMPLE_RISING; + common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING; else - common_flags &= ~SOCAM_PCLK_SAMPLE_FALLING; + common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING; } - ret = icd->ops->set_bus_param(icd, common_flags); - if (ret < 0) { - dev_dbg(icd->parent, "Camera set_bus_param(%lx) returned %d\n", + cfg.flags = common_flags; + ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg); + if (ret < 0 && ret != -ENOIOCTLCMD) { + dev_dbg(icd->parent, "camera s_mbus_config(0x%lx) returned %d\n", common_flags, ret); return ret; } /* set bus param for ISI */ - if (common_flags & SOCAM_HSYNC_ACTIVE_LOW) + if (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) cfg1 |= ISI_CFG1_HSYNC_POL_ACTIVE_LOW; - if (common_flags & SOCAM_VSYNC_ACTIVE_LOW) + if (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) cfg1 |= ISI_CFG1_VSYNC_POL_ACTIVE_LOW; - if (common_flags & SOCAM_PCLK_SAMPLE_FALLING) + if (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) cfg1 |= ISI_CFG1_PIXCLK_POL_ACTIVE_FALLING; if (isi->pdata->has_emb_sync) @@ -973,6 +988,11 @@ static int __devinit atmel_isi_probe(struct platform_device *pdev) goto err_ioremap; } + if (pdata->data_width_flags & ISI_DATAWIDTH_8) + isi->width_flags = 1 << 7; + if (pdata->data_width_flags & ISI_DATAWIDTH_10) + isi->width_flags |= 1 << 9; + isi_writel(isi, ISI_CTRL, ISI_CTRL_DIS); irq = platform_get_irq(pdev, 0); diff --git a/drivers/media/video/au0828/Makefile b/drivers/media/video/au0828/Makefile index 5c7f2f7d980..bd22223f8d9 100644 --- a/drivers/media/video/au0828/Makefile +++ b/drivers/media/video/au0828/Makefile @@ -2,8 +2,8 @@ au0828-objs := au0828-core.o au0828-i2c.o au0828-cards.o au0828-dvb.o au0828-vid obj-$(CONFIG_VIDEO_AU0828) += au0828.o -EXTRA_CFLAGS += -Idrivers/media/common/tuners -EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core -EXTRA_CFLAGS += -Idrivers/media/dvb/frontends +ccflags-y += -Idrivers/media/common/tuners +ccflags-y += -Idrivers/media/dvb/dvb-core +ccflags-y += -Idrivers/media/dvb/frontends -EXTRA_CFLAGS += $(extra-cflags-y) $(extra-cflags-m) +ccflags-y += $(extra-cflags-y) $(extra-cflags-m) diff --git a/drivers/media/video/bt819.c b/drivers/media/video/bt819.c index f87204461cb..859eabf5797 100644 --- a/drivers/media/video/bt819.c +++ b/drivers/media/video/bt819.c @@ -229,7 +229,7 @@ static int bt819_status(struct v4l2_subdev *sd, u32 *pstatus, v4l2_std_id *pstd) if (pstd) *pstd = std; if (pstatus) - *pstatus = status; + *pstatus = res; v4l2_dbg(1, debug, sd, "get status %x\n", status); return 0; diff --git a/drivers/media/video/bt8xx/Makefile b/drivers/media/video/bt8xx/Makefile index e415f6fc447..3f9a2b22d3d 100644 --- a/drivers/media/video/bt8xx/Makefile +++ b/drivers/media/video/bt8xx/Makefile @@ -8,6 +8,6 @@ bttv-objs := bttv-driver.o bttv-cards.o bttv-if.o \ obj-$(CONFIG_VIDEO_BT848) += bttv.o -EXTRA_CFLAGS += -Idrivers/media/video -EXTRA_CFLAGS += -Idrivers/media/common/tuners -EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core +ccflags-y += -Idrivers/media/video +ccflags-y += -Idrivers/media/common/tuners +ccflags-y += -Idrivers/media/dvb/dvb-core diff --git a/drivers/media/video/bt8xx/bttv-cards.c b/drivers/media/video/bt8xx/bttv-cards.c index 5b15f63bf06..5939021d8eb 100644 --- a/drivers/media/video/bt8xx/bttv-cards.c +++ b/drivers/media/video/bt8xx/bttv-cards.c @@ -25,6 +25,8 @@ */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/delay.h> #include <linux/module.h> #include <linux/kmod.h> @@ -2905,19 +2907,17 @@ void __devinit bttv_idcard(struct bttv *btv) if (type != -1) { /* found it */ - printk(KERN_INFO "bttv%d: detected: %s [card=%d], " - "PCI subsystem ID is %04x:%04x\n", - btv->c.nr,cards[type].name,cards[type].cardnr, - btv->cardid & 0xffff, - (btv->cardid >> 16) & 0xffff); + pr_info("%d: detected: %s [card=%d], PCI subsystem ID is %04x:%04x\n", + btv->c.nr, cards[type].name, cards[type].cardnr, + btv->cardid & 0xffff, + (btv->cardid >> 16) & 0xffff); btv->c.type = cards[type].cardnr; } else { /* 404 */ - printk(KERN_INFO "bttv%d: subsystem: %04x:%04x (UNKNOWN)\n", - btv->c.nr, btv->cardid & 0xffff, - (btv->cardid >> 16) & 0xffff); - printk(KERN_DEBUG "please mail id, board name and " - "the correct card= insmod option to linux-media@vger.kernel.org\n"); + pr_info("%d: subsystem: %04x:%04x (UNKNOWN)\n", + btv->c.nr, btv->cardid & 0xffff, + (btv->cardid >> 16) & 0xffff); + pr_debug("please mail id, board name and the correct card= insmod option to linux-media@vger.kernel.org\n"); } } @@ -2926,10 +2926,10 @@ void __devinit bttv_idcard(struct bttv *btv) btv->c.type=card[btv->c.nr]; /* print which card config we are using */ - printk(KERN_INFO "bttv%d: using: %s [card=%d,%s]\n",btv->c.nr, - bttv_tvcards[btv->c.type].name, btv->c.type, - card[btv->c.nr] < bttv_num_tvcards - ? "insmod option" : "autodetected"); + pr_info("%d: using: %s [card=%d,%s]\n", + btv->c.nr, bttv_tvcards[btv->c.type].name, btv->c.type, + card[btv->c.nr] < bttv_num_tvcards + ? "insmod option" : "autodetected"); /* overwrite gpio stuff ?? */ if (UNSET == audioall && UNSET == audiomux[0]) @@ -2948,12 +2948,13 @@ void __devinit bttv_idcard(struct bttv *btv) } } bttv_tvcards[btv->c.type].gpiomask = (UNSET != gpiomask) ? gpiomask : gpiobits; - printk(KERN_INFO "bttv%d: gpio config override: mask=0x%x, mux=", - btv->c.nr,bttv_tvcards[btv->c.type].gpiomask); + pr_info("%d: gpio config override: mask=0x%x, mux=", + btv->c.nr, bttv_tvcards[btv->c.type].gpiomask); for (i = 0; i < ARRAY_SIZE(bttv_tvcards->gpiomux); i++) { - printk("%s0x%x", i ? "," : "", bttv_tvcards[btv->c.type].gpiomux[i]); + pr_cont("%s0x%x", + i ? "," : "", bttv_tvcards[btv->c.type].gpiomux[i]); } - printk("\n"); + pr_cont("\n"); } /* @@ -2974,8 +2975,8 @@ static void identify_by_eeprom(struct bttv *btv, unsigned char eeprom_data[256]) if (-1 != type) { btv->c.type = type; - printk("bttv%d: detected by eeprom: %s [card=%d]\n", - btv->c.nr, bttv_tvcards[btv->c.type].name, btv->c.type); + pr_info("%d: detected by eeprom: %s [card=%d]\n", + btv->c.nr, bttv_tvcards[btv->c.type].name, btv->c.type); } } @@ -3019,7 +3020,7 @@ static void flyvideo_gpio(struct bttv *btv) tuner_type = 3; /* Philips SECAM(+PAL) FQ1216ME or FI1216MF */ break; default: - printk(KERN_INFO "bttv%d: FlyVideo_gpio: unknown tuner type.\n", btv->c.nr); + pr_info("%d: FlyVideo_gpio: unknown tuner type\n", btv->c.nr); break; } @@ -3036,13 +3037,13 @@ static void flyvideo_gpio(struct bttv *btv) if (is_capture_only) tuner_type = TUNER_ABSENT; /* No tuner present */ - printk(KERN_INFO "bttv%d: FlyVideo Radio=%s RemoteControl=%s Tuner=%d gpio=0x%06x\n", - btv->c.nr, has_radio ? "yes" : "no ", - has_remote ? "yes" : "no ", tuner_type, gpio); - printk(KERN_INFO "bttv%d: FlyVideo LR90=%s tda9821/tda9820=%s capture_only=%s\n", - btv->c.nr, is_lr90 ? "yes" : "no ", - has_tda9820_tda9821 ? "yes" : "no ", - is_capture_only ? "yes" : "no "); + pr_info("%d: FlyVideo Radio=%s RemoteControl=%s Tuner=%d gpio=0x%06x\n", + btv->c.nr, has_radio ? "yes" : "no", + has_remote ? "yes" : "no", tuner_type, gpio); + pr_info("%d: FlyVideo LR90=%s tda9821/tda9820=%s capture_only=%s\n", + btv->c.nr, is_lr90 ? "yes" : "no", + has_tda9820_tda9821 ? "yes" : "no", + is_capture_only ? "yes" : "no"); if (tuner_type != UNSET) /* only set if known tuner autodetected, else let insmod option through */ btv->tuner_type = tuner_type; @@ -3091,12 +3092,11 @@ static void miro_pinnacle_gpio(struct bttv *btv) if (btv->c.type == BTTV_BOARD_PINNACLE) btv->c.type = BTTV_BOARD_PINNACLEPRO; } - printk(KERN_INFO - "bttv%d: miro: id=%d tuner=%d radio=%s stereo=%s\n", - btv->c.nr, id+1, btv->tuner_type, - !btv->has_radio ? "no" : - (btv->has_matchbox ? "matchbox" : "fmtuner"), - (-1 == msp) ? "no" : "yes"); + pr_info("%d: miro: id=%d tuner=%d radio=%s stereo=%s\n", + btv->c.nr, id+1, btv->tuner_type, + !btv->has_radio ? "no" : + (btv->has_matchbox ? "matchbox" : "fmtuner"), + (-1 == msp) ? "no" : "yes"); } else { /* new cards with microtune tuner */ id = 63 - id; @@ -3138,9 +3138,8 @@ static void miro_pinnacle_gpio(struct bttv *btv) } if (-1 != msp) btv->c.type = BTTV_BOARD_PINNACLEPRO; - printk(KERN_INFO - "bttv%d: pinnacle/mt: id=%d info=\"%s\" radio=%s\n", - btv->c.nr, id, info, btv->has_radio ? "yes" : "no"); + pr_info("%d: pinnacle/mt: id=%d info=\"%s\" radio=%s\n", + btv->c.nr, id, info, btv->has_radio ? "yes" : "no"); btv->tuner_type = TUNER_MT2032; } } @@ -3202,7 +3201,7 @@ static void gvc1100_muxsel(struct bttv *btv, unsigned int input) static void init_lmlbt4x(struct bttv *btv) { - printk(KERN_DEBUG "LMLBT4x init\n"); + pr_debug("LMLBT4x init\n"); btwrite(0x000000, BT848_GPIO_REG_INP); gpio_inout(0xffffff, 0x0006C0); gpio_write(0x000000); @@ -3246,7 +3245,7 @@ static void bttv_reset_audio(struct bttv *btv) return; if (bttv_debug) - printk("bttv%d: BT878A ARESET\n",btv->c.nr); + pr_debug("%d: BT878A ARESET\n", btv->c.nr); btwrite((1<<7), 0x058); udelay(10); btwrite( 0, 0x058); @@ -3349,7 +3348,8 @@ void __devinit bttv_init_card2(struct bttv *btv) case BTTV_BOARD_MAGICTVIEW061: if (btv->cardid == 0x3002144f) { btv->has_radio=1; - printk("bttv%d: radio detected by subsystem id (CPH05x)\n",btv->c.nr); + pr_info("%d: radio detected by subsystem id (CPH05x)\n", + btv->c.nr); } break; case BTTV_BOARD_STB2: @@ -3438,17 +3438,16 @@ void __devinit bttv_init_card2(struct bttv *btv) btv->tuner_type = tuner[btv->c.nr]; if (btv->tuner_type == TUNER_ABSENT) - printk(KERN_INFO "bttv%d: tuner absent\n", btv->c.nr); - else if(btv->tuner_type == UNSET) - printk(KERN_WARNING "bttv%d: tuner type unset\n", btv->c.nr); + pr_info("%d: tuner absent\n", btv->c.nr); + else if (btv->tuner_type == UNSET) + pr_warn("%d: tuner type unset\n", btv->c.nr); else - printk(KERN_INFO "bttv%d: tuner type=%d\n", btv->c.nr, - btv->tuner_type); + pr_info("%d: tuner type=%d\n", btv->c.nr, btv->tuner_type); if (autoload != UNSET) { - printk(KERN_WARNING "bttv%d: the autoload option is obsolete.\n", btv->c.nr); - printk(KERN_WARNING "bttv%d: use option msp3400, tda7432 or tvaudio to\n", btv->c.nr); - printk(KERN_WARNING "bttv%d: override which audio module should be used.\n", btv->c.nr); + pr_warn("%d: the autoload option is obsolete\n", btv->c.nr); + pr_warn("%d: use option msp3400, tda7432 or tvaudio to override which audio module should be used\n", + btv->c.nr); } if (UNSET == btv->tuner_type) @@ -3541,8 +3540,7 @@ void __devinit bttv_init_card2(struct bttv *btv) } default: - printk(KERN_WARNING "bttv%d: unknown audiodev value!\n", - btv->c.nr); + pr_warn("%d: unknown audiodev value!\n", btv->c.nr); return; } @@ -3585,8 +3583,7 @@ void __devinit bttv_init_card2(struct bttv *btv) return; no_audio: - printk(KERN_WARNING "bttv%d: audio absent, no audio device found!\n", - btv->c.nr); + pr_warn("%d: audio absent, no audio device found!\n", btv->c.nr); } @@ -3639,19 +3636,19 @@ static void modtec_eeprom(struct bttv *btv) { if( strncmp(&(eeprom_data[0x1e]),"Temic 4066 FY5",14) ==0) { btv->tuner_type=TUNER_TEMIC_4066FY5_PAL_I; - printk("bttv%d: Modtec: Tuner autodetected by eeprom: %s\n", - btv->c.nr,&eeprom_data[0x1e]); + pr_info("%d: Modtec: Tuner autodetected by eeprom: %s\n", + btv->c.nr, &eeprom_data[0x1e]); } else if (strncmp(&(eeprom_data[0x1e]),"Alps TSBB5",10) ==0) { btv->tuner_type=TUNER_ALPS_TSBB5_PAL_I; - printk("bttv%d: Modtec: Tuner autodetected by eeprom: %s\n", - btv->c.nr,&eeprom_data[0x1e]); + pr_info("%d: Modtec: Tuner autodetected by eeprom: %s\n", + btv->c.nr, &eeprom_data[0x1e]); } else if (strncmp(&(eeprom_data[0x1e]),"Philips FM1246",14) ==0) { btv->tuner_type=TUNER_PHILIPS_NTSC; - printk("bttv%d: Modtec: Tuner autodetected by eeprom: %s\n", - btv->c.nr,&eeprom_data[0x1e]); + pr_info("%d: Modtec: Tuner autodetected by eeprom: %s\n", + btv->c.nr, &eeprom_data[0x1e]); } else { - printk("bttv%d: Modtec: Unknown TunerString: %s\n", - btv->c.nr,&eeprom_data[0x1e]); + pr_info("%d: Modtec: Unknown TunerString: %s\n", + btv->c.nr, &eeprom_data[0x1e]); } } @@ -3663,7 +3660,7 @@ static void __devinit hauppauge_eeprom(struct bttv *btv) btv->tuner_type = tv.tuner_type; btv->has_radio = tv.has_radio; - printk("bttv%d: Hauppauge eeprom indicates model#%d\n", + pr_info("%d: Hauppauge eeprom indicates model#%d\n", btv->c.nr, tv.model); /* @@ -3671,7 +3668,7 @@ static void __devinit hauppauge_eeprom(struct bttv *btv) * type based on model #. */ if(tv.model == 64900) { - printk("bttv%d: Switching board type from %s to %s\n", + pr_info("%d: Switching board type from %s to %s\n", btv->c.nr, bttv_tvcards[btv->c.type].name, bttv_tvcards[BTTV_BOARD_HAUPPAUGE_IMPACTVCB].name); @@ -3698,8 +3695,7 @@ static int terratec_active_radio_upgrade(struct bttv *btv) freq=88000/62.5; tea5757_write(btv, 5 * freq + 0x358); /* write 0x1ed8 */ if (0x1ed8 == tea5757_read(btv)) { - printk("bttv%d: Terratec Active Radio Upgrade found.\n", - btv->c.nr); + pr_info("%d: Terratec Active Radio Upgrade found\n", btv->c.nr); btv->has_radio = 1; btv->has_saa6588 = 1; btv->has_matchbox = 1; @@ -3771,13 +3767,12 @@ static int __devinit pvr_boot(struct bttv *btv) rc = request_firmware(&fw_entry, "hcwamc.rbf", &btv->c.pci->dev); if (rc != 0) { - printk(KERN_WARNING "bttv%d: no altera firmware [via hotplug]\n", - btv->c.nr); + pr_warn("%d: no altera firmware [via hotplug]\n", btv->c.nr); return rc; } rc = pvr_altera_load(btv, fw_entry->data, fw_entry->size); - printk(KERN_INFO "bttv%d: altera firmware upload %s\n", - btv->c.nr, (rc < 0) ? "failed" : "ok"); + pr_info("%d: altera firmware upload %s\n", + btv->c.nr, (rc < 0) ? "failed" : "ok"); release_firmware(fw_entry); return rc; } @@ -3873,29 +3868,27 @@ static void __devinit osprey_eeprom(struct bttv *btv, const u8 ee[256]) break; default: /* unknown...leave generic, but get serial # */ - printk(KERN_INFO "bttv%d: " - "osprey eeprom: unknown card type 0x%04x\n", - btv->c.nr, type); + pr_info("%d: osprey eeprom: unknown card type 0x%04x\n", + btv->c.nr, type); break; } serial = get_unaligned_be32((__be32 *)(ee+6)); } - printk(KERN_INFO "bttv%d: osprey eeprom: card=%d '%s' serial=%u\n", - btv->c.nr, cardid, - cardid>0 ? bttv_tvcards[cardid].name : "Unknown", serial); + pr_info("%d: osprey eeprom: card=%d '%s' serial=%u\n", + btv->c.nr, cardid, + cardid > 0 ? bttv_tvcards[cardid].name : "Unknown", serial); if (cardid<0 || btv->c.type == cardid) return; /* card type isn't set correctly */ if (card[btv->c.nr] < bttv_num_tvcards) { - printk(KERN_WARNING "bttv%d: osprey eeprom: " - "Not overriding user specified card type\n", btv->c.nr); + pr_warn("%d: osprey eeprom: Not overriding user specified card type\n", + btv->c.nr); } else { - printk(KERN_INFO "bttv%d: osprey eeprom: " - "Changing card type from %d to %d\n", btv->c.nr, - btv->c.type, cardid); + pr_info("%d: osprey eeprom: Changing card type from %d to %d\n", + btv->c.nr, btv->c.type, cardid); btv->c.type = cardid; } } @@ -3938,14 +3931,14 @@ static void __devinit avermedia_eeprom(struct bttv *btv) if (tuner_format == 0x09) tuner_type = TUNER_LG_NTSC_NEW_TAPC; /* TAPC-G702P */ - printk(KERN_INFO "bttv%d: Avermedia eeprom[0x%02x%02x]: tuner=", + pr_info("%d: Avermedia eeprom[0x%02x%02x]: tuner=", btv->c.nr, eeprom_data[0x41], eeprom_data[0x42]); if (tuner_type) { btv->tuner_type = tuner_type; - printk(KERN_CONT "%d", tuner_type); + pr_cont("%d", tuner_type); } else - printk(KERN_CONT "Unknown type"); - printk(KERN_CONT " radio:%s remote control:%s\n", + pr_cont("Unknown type"); + pr_cont(" radio:%s remote control:%s\n", tuner_tv_fm ? "yes" : "no", btv->has_remote ? "yes" : "no"); } @@ -3993,8 +3986,8 @@ static void __devinit boot_msp34xx(struct bttv *btv, int pin) if (bttv_gpio) bttv_gpio_tracking(btv,"msp34xx"); if (bttv_verbose) - printk(KERN_INFO "bttv%d: Hauppauge/Voodoo msp34xx: reset line " - "init [%d]\n", btv->c.nr, pin); + pr_info("%d: Hauppauge/Voodoo msp34xx: reset line init [%d]\n", + btv->c.nr, pin); } /* ----------------------------------------------------------------------- */ @@ -4034,7 +4027,7 @@ static void __devinit init_PXC200(struct bttv *btv) btwrite(BT848_ADC_RESERVED|BT848_ADC_AGC_EN, BT848_ADC); /* Initialise MAX517 DAC */ - printk(KERN_INFO "Setting DAC reference voltage level ...\n"); + pr_info("Setting DAC reference voltage level ...\n"); bttv_I2CWrite(btv,0x5E,0,0x80,1); /* Initialise 12C508 PIC */ @@ -4043,7 +4036,7 @@ static void __devinit init_PXC200(struct bttv *btv) * argument so the numbers are different */ - printk(KERN_INFO "Initialising 12C508 PIC chip ...\n"); + pr_info("Initialising 12C508 PIC chip ...\n"); /* First of all, enable the clock line. This is used in the PXC200-F */ val = btread(BT848_GPIO_DMA_CTL); @@ -4062,13 +4055,12 @@ static void __devinit init_PXC200(struct bttv *btv) for (i = 0; i < ARRAY_SIZE(vals); i++) { tmp=bttv_I2CWrite(btv,0x1E,0,vals[i],1); if (tmp != -1) { - printk(KERN_INFO - "I2C Write(%2.2x) = %i\nI2C Read () = %2.2x\n\n", + pr_info("I2C Write(%2.2x) = %i\nI2C Read () = %2.2x\n\n", vals[i],tmp,bttv_I2CRead(btv,0x1F,NULL)); } } - printk(KERN_INFO "PXC200 Initialised.\n"); + pr_info("PXC200 Initialised\n"); } @@ -4107,8 +4099,7 @@ init_RTV24 (struct bttv *btv) uint32_t dataRead = 0; long watchdog_value = 0x0E; - printk (KERN_INFO - "bttv%d: Adlink RTV-24 initialisation in progress ...\n", + pr_info("%d: Adlink RTV-24 initialisation in progress ...\n", btv->c.nr); btwrite (0x00c3feff, BT848_GPIO_OUT_EN); @@ -4122,8 +4113,7 @@ init_RTV24 (struct bttv *btv) dataRead = btread (BT848_GPIO_DATA); if ((((dataRead >> 18) & 0x01) != 0) || (((dataRead >> 19) & 0x01) != 1)) { - printk (KERN_INFO - "bttv%d: Adlink RTV-24 initialisation(1) ERROR_CPLD_Check_Failed (read %d)\n", + pr_info("%d: Adlink RTV-24 initialisation(1) ERROR_CPLD_Check_Failed (read %d)\n", btv->c.nr, dataRead); } @@ -4136,15 +4126,13 @@ init_RTV24 (struct bttv *btv) dataRead = btread (BT848_GPIO_DATA); if ((((dataRead >> 18) & 0x01) != 0) || (((dataRead >> 19) & 0x01) != 0)) { - printk (KERN_INFO - "bttv%d: Adlink RTV-24 initialisation(2) ERROR_CPLD_Check_Failed (read %d)\n", + pr_info("%d: Adlink RTV-24 initialisation(2) ERROR_CPLD_Check_Failed (read %d)\n", btv->c.nr, dataRead); return; } - printk (KERN_INFO - "bttv%d: Adlink RTV-24 initialisation complete.\n", btv->c.nr); + pr_info("%d: Adlink RTV-24 initialisation complete\n", btv->c.nr); } @@ -4261,22 +4249,25 @@ static int tea5757_read(struct bttv *btv) while (bus_in(btv,btv->mbox_data) && time_before(jiffies, timeout)) schedule(); if (bus_in(btv,btv->mbox_data)) { - printk(KERN_WARNING "bttv%d: tea5757: read timeout\n",btv->c.nr); + pr_warn("%d: tea5757: read timeout\n", btv->c.nr); return -1; } - dprintk("bttv%d: tea5757:",btv->c.nr); + dprintk("%d: tea5757:", btv->c.nr); for (i = 0; i < 24; i++) { udelay(5); bus_high(btv,btv->mbox_clk); udelay(5); - dprintk("%c",(bus_in(btv,btv->mbox_most) == 0)?'T':'-'); + dprintk_cont("%c", + bus_in(btv, btv->mbox_most) == 0 ? 'T' : '-'); bus_low(btv,btv->mbox_clk); value <<= 1; value |= (bus_in(btv,btv->mbox_data) == 0)?0:1; /* MSB first */ - dprintk("%c", (bus_in(btv,btv->mbox_most) == 0)?'S':'M'); + dprintk_cont("%c", + bus_in(btv, btv->mbox_most) == 0 ? 'S' : 'M'); } - dprintk("\nbttv%d: tea5757: read 0x%X\n", btv->c.nr, value); + dprintk_cont("\n"); + dprintk("%d: tea5757: read 0x%X\n", btv->c.nr, value); return value; } @@ -4295,7 +4286,7 @@ static int tea5757_write(struct bttv *btv, int value) if (bttv_gpio) bttv_gpio_tracking(btv,"tea5757 write"); - dprintk("bttv%d: tea5757: write 0x%X\n", btv->c.nr, value); + dprintk("%d: tea5757: write 0x%X\n", btv->c.nr, value); bus_low(btv,btv->mbox_clk); bus_high(btv,btv->mbox_we); for (i = 0; i < 25; i++) { @@ -4547,7 +4538,7 @@ static void picolo_tetra_init(struct bttv *btv) static void picolo_tetra_muxsel (struct bttv* btv, unsigned int input) { - dprintk (KERN_DEBUG "bttv%d : picolo_tetra_muxsel => input = %d\n",btv->c.nr,input); + dprintk("%d : picolo_tetra_muxsel => input = %d\n", btv->c.nr, input); /*Just set the right path in the analog multiplexers : channel 1 -> 4 ==> Analog Mux ==> MUX0*/ /*GPIO[20]&GPIO[21] used to choose the right input*/ btwrite (input<<20,BT848_GPIO_DATA); @@ -4592,7 +4583,7 @@ static void ivc120_muxsel(struct bttv *btv, unsigned int input) int key = input % 4; int matrix = input / 4; - dprintk("bttv%d: ivc120_muxsel: Input - %02d | TDA - %02d | In - %02d\n", + dprintk("%d: ivc120_muxsel: Input - %02d | TDA - %02d | In - %02d\n", btv->c.nr, input, matrix, key); /* Handles the input selection on the TDA8540's */ @@ -4649,15 +4640,17 @@ static void PXC200_muxsel(struct bttv *btv, unsigned int input) buf[1]=0; rc=bttv_I2CWrite(btv,(PX_I2C_PIC<<1),buf[0],buf[1],1); if (rc) { - printk(KERN_DEBUG "bttv%d: PXC200_muxsel: pic cfg write failed:%d\n", btv->c.nr,rc); + pr_debug("%d: PXC200_muxsel: pic cfg write failed:%d\n", + btv->c.nr, rc); /* not PXC ? do nothing */ - return; + return; } rc=bttv_I2CRead(btv,(PX_I2C_PIC<<1),NULL); if (!(rc & PX_CFG_PXC200F)) { - printk(KERN_DEBUG "bttv%d: PXC200_muxsel: not PXC200F rc:%d \n", btv->c.nr,rc); - return; + pr_debug("%d: PXC200_muxsel: not PXC200F rc:%d\n", + btv->c.nr, rc); + return; } @@ -4696,7 +4689,7 @@ static void PXC200_muxsel(struct bttv *btv, unsigned int input) else /* older device */ btand(~BT848_IFORM_MUXSEL,BT848_IFORM); - printk(KERN_DEBUG "bttv%d: setting input channel to:%d\n", btv->c.nr,(int)mux); + pr_debug("%d: setting input channel to:%d\n", btv->c.nr, (int)mux); } static void phytec_muxsel(struct bttv *btv, unsigned int input) @@ -4847,29 +4840,27 @@ void __init bttv_check_chipset(void) /* print warnings about any quirks found */ if (triton1) - printk(KERN_INFO "bttv: Host bridge needs ETBF enabled.\n"); + pr_info("Host bridge needs ETBF enabled\n"); if (vsfx) - printk(KERN_INFO "bttv: Host bridge needs VSFX enabled.\n"); + pr_info("Host bridge needs VSFX enabled\n"); if (pcipci_fail) { - printk(KERN_INFO "bttv: bttv and your chipset may not work " - "together.\n"); + pr_info("bttv and your chipset may not work together\n"); if (!no_overlay) { - printk(KERN_INFO "bttv: overlay will be disabled.\n"); + pr_info("overlay will be disabled\n"); no_overlay = 1; } else { - printk(KERN_INFO "bttv: overlay forced. Use this " - "option at your own risk.\n"); + pr_info("overlay forced. Use this option at your own risk.\n"); } } if (UNSET != latency) - printk(KERN_INFO "bttv: pci latency fixup [%d]\n",latency); + pr_info("pci latency fixup [%d]\n", latency); while ((dev = pci_get_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82441, dev))) { unsigned char b; pci_read_config_byte(dev, 0x53, &b); if (bttv_debug) - printk(KERN_INFO "bttv: Host bridge: 82441FX Natoma, " - "bufcon=0x%02x\n",b); + pr_info("Host bridge: 82441FX Natoma, bufcon=0x%02x\n", + b); } } @@ -4882,12 +4873,13 @@ int __devinit bttv_handle_chipset(struct bttv *btv) if (bttv_verbose) { if (triton1) - printk(KERN_INFO "bttv%d: enabling ETBF (430FX/VP3 compatibilty)\n",btv->c.nr); + pr_info("%d: enabling ETBF (430FX/VP3 compatibility)\n", + btv->c.nr); if (vsfx && btv->id >= 878) - printk(KERN_INFO "bttv%d: enabling VSFX\n",btv->c.nr); + pr_info("%d: enabling VSFX\n", btv->c.nr); if (UNSET != latency) - printk(KERN_INFO "bttv%d: setting pci timer to %d\n", - btv->c.nr,latency); + pr_info("%d: setting pci timer to %d\n", + btv->c.nr, latency); } if (btv->id < 878) { diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c index 14444de67d5..3dd06607aec 100644 --- a/drivers/media/video/bt8xx/bttv-driver.c +++ b/drivers/media/video/bt8xx/bttv-driver.c @@ -34,6 +34,8 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/init.h> #include <linux/module.h> #include <linux/delay.h> @@ -942,8 +944,8 @@ static void free_btres_lock(struct bttv *btv, struct bttv_fh *fh, int bits) { if ((fh->resources & bits) != bits) { - /* trying to free ressources not allocated by us ... */ - printk("bttv: BUG! (btres)\n"); + /* trying to free resources not allocated by us ... */ + pr_err("BUG! (btres)\n"); } fh->resources &= ~bits; btv->resources &= ~bits; @@ -1000,7 +1002,7 @@ static void set_pll(struct bttv *btv) return; if (btv->pll.pll_ofreq == btv->pll.pll_current) { - dprintk("bttv%d: PLL: no change required\n",btv->c.nr); + dprintk("%d: PLL: no change required\n", btv->c.nr); return; } @@ -1008,21 +1010,23 @@ static void set_pll(struct bttv *btv) /* no PLL needed */ if (btv->pll.pll_current == 0) return; - bttv_printk(KERN_INFO "bttv%d: PLL can sleep, using XTAL (%d).\n", - btv->c.nr,btv->pll.pll_ifreq); + if (bttv_verbose) + pr_info("%d: PLL can sleep, using XTAL (%d)\n", + btv->c.nr, btv->pll.pll_ifreq); btwrite(0x00,BT848_TGCTRL); btwrite(0x00,BT848_PLL_XCI); btv->pll.pll_current = 0; return; } - bttv_printk(KERN_INFO "bttv%d: PLL: %d => %d ",btv->c.nr, - btv->pll.pll_ifreq, btv->pll.pll_ofreq); + if (bttv_verbose) + pr_info("%d: Setting PLL: %d => %d (needs up to 100ms)\n", + btv->c.nr, + btv->pll.pll_ifreq, btv->pll.pll_ofreq); set_pll_freq(btv, btv->pll.pll_ifreq, btv->pll.pll_ofreq); for (i=0; i<10; i++) { /* Let other people run while the PLL stabilizes */ - bttv_printk("."); msleep(10); if (btread(BT848_DSTATUS) & BT848_DSTATUS_PLOCK) { @@ -1030,12 +1034,14 @@ static void set_pll(struct bttv *btv) } else { btwrite(0x08,BT848_TGCTRL); btv->pll.pll_current = btv->pll.pll_ofreq; - bttv_printk(" ok\n"); + if (bttv_verbose) + pr_info("PLL set ok\n"); return; } } btv->pll.pll_current = -1; - bttv_printk("failed\n"); + if (bttv_verbose) + pr_info("Setting PLL failed\n"); return; } @@ -1047,7 +1053,7 @@ static void bt848A_set_timing(struct bttv *btv) int fsc = bttv_tvnorms[btv->tvnorm].Fsc; if (btv->input == btv->dig) { - dprintk("bttv%d: load digital timing table (table_idx=%d)\n", + dprintk("%d: load digital timing table (table_idx=%d)\n", btv->c.nr,table_idx); /* timing change...reset timing generator address */ @@ -1076,7 +1082,7 @@ static void bt848_bright(struct bttv *btv, int bright) { int value; - // printk("bttv: set bright: %d\n",bright); // DEBUG + // printk("set bright: %d\n", bright); // DEBUG btv->bright = bright; /* We want -128 to 127 we get 0-65535 */ @@ -1150,8 +1156,7 @@ video_mux(struct bttv *btv, unsigned int input) } mux = bttv_muxsel(btv, input); btaor(mux<<5, ~(3<<5), BT848_IFORM); - dprintk(KERN_DEBUG "bttv%d: video mux: input=%d mux=%d\n", - btv->c.nr,input,mux); + dprintk("%d: video mux: input=%d mux=%d\n", btv->c.nr, input, mux); /* card specific hook */ if(bttv_tvcards[btv->c.type].muxsel_hook) @@ -1440,7 +1445,7 @@ static void bttv_reinit_bt848(struct bttv *btv) unsigned long flags; if (bttv_verbose) - printk(KERN_INFO "bttv%d: reset, reinitialize\n",btv->c.nr); + pr_info("%d: reset, reinitialize\n", btv->c.nr); spin_lock_irqsave(&btv->s_lock,flags); btv->errors=0; bttv_set_dma(btv,0); @@ -1622,8 +1627,8 @@ void bttv_gpio_tracking(struct bttv *btv, char *comment) unsigned int outbits, data; outbits = btread(BT848_GPIO_OUT_EN); data = btread(BT848_GPIO_DATA); - printk(KERN_DEBUG "bttv%d: gpio: en=%08x, out=%08x in=%08x [%s]\n", - btv->c.nr,outbits,data & outbits, data & ~outbits, comment); + pr_debug("%d: gpio: en=%08x, out=%08x in=%08x [%s]\n", + btv->c.nr, outbits, data & outbits, data & ~outbits, comment); } static void bttv_field_count(struct bttv *btv) @@ -1668,7 +1673,7 @@ bttv_switch_overlay(struct bttv *btv, struct bttv_fh *fh, unsigned long flags; int retval = 0; - dprintk("switch_overlay: enter [new=%p]\n",new); + dprintk("switch_overlay: enter [new=%p]\n", new); if (new) new->vb.state = VIDEOBUF_DONE; spin_lock_irqsave(&btv->s_lock,flags); @@ -1678,7 +1683,8 @@ bttv_switch_overlay(struct bttv *btv, struct bttv_fh *fh, bttv_set_dma(btv, 0x03); spin_unlock_irqrestore(&btv->s_lock,flags); if (NULL != old) { - dprintk("switch_overlay: old=%p state is %d\n",old,old->vb.state); + dprintk("switch_overlay: old=%p state is %d\n", + old, old->vb.state); bttv_dma_free(&fh->cap,btv, old); kfree(old); } @@ -2029,11 +2035,11 @@ static int bttv_log_status(struct file *file, void *f) struct bttv_fh *fh = f; struct bttv *btv = fh->btv; - printk(KERN_INFO "bttv%d: ======== START STATUS CARD #%d ========\n", - btv->c.nr, btv->c.nr); + pr_info("%d: ======== START STATUS CARD #%d ========\n", + btv->c.nr, btv->c.nr); bttv_call_all(btv, core, log_status); - printk(KERN_INFO "bttv%d: ======== END STATUS CARD #%d ========\n", - btv->c.nr, btv->c.nr); + pr_info("%d: ======== END STATUS CARD #%d ========\n", + btv->c.nr, btv->c.nr); return 0; } @@ -2598,7 +2604,7 @@ static int bttv_s_fmt_vid_overlay(struct file *file, void *priv, struct bttv *btv = fh->btv; if (no_overlay > 0) { - printk(KERN_ERR "V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n"); + pr_err("V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n"); return -EINVAL; } @@ -2673,7 +2679,7 @@ static int bttv_enum_fmt_vid_overlay(struct file *file, void *priv, int rc; if (no_overlay > 0) { - printk(KERN_ERR "V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n"); + pr_err("V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n"); return -EINVAL; } @@ -2714,7 +2720,7 @@ static int bttv_overlay(struct file *file, void *f, unsigned int on) return -EINVAL; } if (unlikely(!fh->ov.setup_ok)) { - dprintk("bttv%d: overlay: !setup_ok\n", btv->c.nr); + dprintk("%d: overlay: !setup_ok\n", btv->c.nr); retval = -EINVAL; } if (retval) @@ -3091,8 +3097,8 @@ static ssize_t bttv_read(struct file *file, char __user *data, if (fh->btv->errors) bttv_reinit_bt848(fh->btv); - dprintk("bttv%d: read count=%d type=%s\n", - fh->btv->c.nr,(int)count,v4l2_type_names[fh->type]); + dprintk("%d: read count=%d type=%s\n", + fh->btv->c.nr, (int)count, v4l2_type_names[fh->type]); switch (fh->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: @@ -3174,7 +3180,7 @@ static int bttv_open(struct file *file) struct bttv_fh *fh; enum v4l2_buf_type type = 0; - dprintk(KERN_DEBUG "bttv: open dev=%s\n", video_device_node_name(vdev)); + dprintk("open dev=%s\n", video_device_node_name(vdev)); if (vdev->vfl_type == VFL_TYPE_GRABBER) { type = V4L2_BUF_TYPE_VIDEO_CAPTURE; @@ -3185,8 +3191,8 @@ static int bttv_open(struct file *file) return -ENODEV; } - dprintk(KERN_DEBUG "bttv%d: open called (type=%s)\n", - btv->c.nr,v4l2_type_names[type]); + dprintk("%d: open called (type=%s)\n", + btv->c.nr, v4l2_type_names[type]); /* allocate per filehandle data */ fh = kmalloc(sizeof(*fh), GFP_KERNEL); @@ -3288,7 +3294,7 @@ bttv_mmap(struct file *file, struct vm_area_struct *vma) { struct bttv_fh *fh = file->private_data; - dprintk("bttv%d: mmap type=%s 0x%lx+%ld\n", + dprintk("%d: mmap type=%s 0x%lx+%ld\n", fh->btv->c.nr, v4l2_type_names[fh->type], vma->vm_start, vma->vm_end - vma->vm_start); return videobuf_mmap_mapper(bttv_queue(fh),vma); @@ -3370,9 +3376,9 @@ static int radio_open(struct file *file) struct bttv *btv = video_drvdata(file); struct bttv_fh *fh; - dprintk("bttv: open dev=%s\n", video_device_node_name(vdev)); + dprintk("open dev=%s\n", video_device_node_name(vdev)); - dprintk("bttv%d: open called (radio)\n",btv->c.nr); + dprintk("%d: open called (radio)\n", btv->c.nr); /* allocate per filehandle data */ fh = kmalloc(sizeof(*fh), GFP_KERNEL); @@ -3616,12 +3622,12 @@ static int bttv_risc_decode(u32 risc) }; int i; - printk("0x%08x [ %s", risc, + pr_cont("0x%08x [ %s", risc, instr[risc >> 28] ? instr[risc >> 28] : "INVALID"); for (i = ARRAY_SIZE(bits)-1; i >= 0; i--) if (risc & (1 << (i + 12))) - printk(" %s",bits[i]); - printk(" count=%d ]\n", risc & 0xfff); + pr_cont(" %s", bits[i]); + pr_cont(" count=%d ]\n", risc & 0xfff); return incr[risc >> 28] ? incr[risc >> 28] : 1; } @@ -3630,16 +3636,18 @@ static void bttv_risc_disasm(struct bttv *btv, { unsigned int i,j,n; - printk("%s: risc disasm: %p [dma=0x%08lx]\n", - btv->c.v4l2_dev.name, risc->cpu, (unsigned long)risc->dma); + pr_info("%s: risc disasm: %p [dma=0x%08lx]\n", + btv->c.v4l2_dev.name, risc->cpu, (unsigned long)risc->dma); for (i = 0; i < (risc->size >> 2); i += n) { - printk("%s: 0x%lx: ", btv->c.v4l2_dev.name, - (unsigned long)(risc->dma + (i<<2))); + pr_info("%s: 0x%lx: ", + btv->c.v4l2_dev.name, + (unsigned long)(risc->dma + (i<<2))); n = bttv_risc_decode(le32_to_cpu(risc->cpu[i])); for (j = 1; j < n; j++) - printk("%s: 0x%lx: 0x%08x [ arg #%d ]\n", - btv->c.v4l2_dev.name, (unsigned long)(risc->dma + ((i+j)<<2)), - risc->cpu[i+j], j); + pr_info("%s: 0x%lx: 0x%08x [ arg #%d ]\n", + btv->c.v4l2_dev.name, + (unsigned long)(risc->dma + ((i+j)<<2)), + risc->cpu[i+j], j); if (0 == risc->cpu[i]) break; } @@ -3647,17 +3655,18 @@ static void bttv_risc_disasm(struct bttv *btv, static void bttv_print_riscaddr(struct bttv *btv) { - printk(" main: %08Lx\n", - (unsigned long long)btv->main.dma); - printk(" vbi : o=%08Lx e=%08Lx\n", - btv->cvbi ? (unsigned long long)btv->cvbi->top.dma : 0, - btv->cvbi ? (unsigned long long)btv->cvbi->bottom.dma : 0); - printk(" cap : o=%08Lx e=%08Lx\n", - btv->curr.top ? (unsigned long long)btv->curr.top->top.dma : 0, - btv->curr.bottom ? (unsigned long long)btv->curr.bottom->bottom.dma : 0); - printk(" scr : o=%08Lx e=%08Lx\n", - btv->screen ? (unsigned long long)btv->screen->top.dma : 0, - btv->screen ? (unsigned long long)btv->screen->bottom.dma : 0); + pr_info(" main: %08llx\n", (unsigned long long)btv->main.dma); + pr_info(" vbi : o=%08llx e=%08llx\n", + btv->cvbi ? (unsigned long long)btv->cvbi->top.dma : 0, + btv->cvbi ? (unsigned long long)btv->cvbi->bottom.dma : 0); + pr_info(" cap : o=%08llx e=%08llx\n", + btv->curr.top + ? (unsigned long long)btv->curr.top->top.dma : 0, + btv->curr.bottom + ? (unsigned long long)btv->curr.bottom->bottom.dma : 0); + pr_info(" scr : o=%08llx e=%08llx\n", + btv->screen ? (unsigned long long)btv->screen->top.dma : 0, + btv->screen ? (unsigned long long)btv->screen->bottom.dma : 0); bttv_risc_disasm(btv, &btv->main); } @@ -3690,34 +3699,34 @@ static void bttv_print_irqbits(u32 print, u32 mark) { unsigned int i; - printk("bits:"); + pr_cont("bits:"); for (i = 0; i < ARRAY_SIZE(irq_name); i++) { if (print & (1 << i)) - printk(" %s",irq_name[i]); + pr_cont(" %s", irq_name[i]); if (mark & (1 << i)) - printk("*"); + pr_cont("*"); } } static void bttv_irq_debug_low_latency(struct bttv *btv, u32 rc) { - printk("bttv%d: irq: skipped frame [main=%lx,o_vbi=%lx,o_field=%lx,rc=%lx]\n", - btv->c.nr, - (unsigned long)btv->main.dma, - (unsigned long)le32_to_cpu(btv->main.cpu[RISC_SLOT_O_VBI+1]), - (unsigned long)le32_to_cpu(btv->main.cpu[RISC_SLOT_O_FIELD+1]), - (unsigned long)rc); + pr_warn("%d: irq: skipped frame [main=%lx,o_vbi=%lx,o_field=%lx,rc=%lx]\n", + btv->c.nr, + (unsigned long)btv->main.dma, + (unsigned long)le32_to_cpu(btv->main.cpu[RISC_SLOT_O_VBI+1]), + (unsigned long)le32_to_cpu(btv->main.cpu[RISC_SLOT_O_FIELD+1]), + (unsigned long)rc); if (0 == (btread(BT848_DSTATUS) & BT848_DSTATUS_HLOC)) { - printk("bttv%d: Oh, there (temporarely?) is no input signal. " - "Ok, then this is harmless, don't worry ;)\n", - btv->c.nr); + pr_notice("%d: Oh, there (temporarily?) is no input signal. " + "Ok, then this is harmless, don't worry ;)\n", + btv->c.nr); return; } - printk("bttv%d: Uhm. Looks like we have unusual high IRQ latencies.\n", - btv->c.nr); - printk("bttv%d: Lets try to catch the culpit red-handed ...\n", - btv->c.nr); + pr_notice("%d: Uhm. Looks like we have unusual high IRQ latencies\n", + btv->c.nr); + pr_notice("%d: Lets try to catch the culpit red-handed ...\n", + btv->c.nr); dump_stack(); } @@ -3798,9 +3807,9 @@ bttv_irq_next_video(struct bttv *btv, struct bttv_buffer_set *set) } } - dprintk("bttv%d: next set: top=%p bottom=%p [screen=%p,irq=%d,%d]\n", - btv->c.nr,set->top, set->bottom, - btv->screen,set->frame_irq,set->top_irq); + dprintk("%d: next set: top=%p bottom=%p [screen=%p,irq=%d,%d]\n", + btv->c.nr, set->top, set->bottom, + btv->screen, set->frame_irq, set->top_irq); return 0; } @@ -3815,7 +3824,8 @@ bttv_irq_wakeup_video(struct bttv *btv, struct bttv_buffer_set *wakeup, if (wakeup->top == wakeup->bottom) { if (NULL != wakeup->top && curr->top != wakeup->top) { if (irq_debug > 1) - printk("bttv%d: wakeup: both=%p\n",btv->c.nr,wakeup->top); + pr_debug("%d: wakeup: both=%p\n", + btv->c.nr, wakeup->top); wakeup->top->vb.ts = ts; wakeup->top->vb.field_count = btv->field_count; wakeup->top->vb.state = state; @@ -3824,7 +3834,8 @@ bttv_irq_wakeup_video(struct bttv *btv, struct bttv_buffer_set *wakeup, } else { if (NULL != wakeup->top && curr->top != wakeup->top) { if (irq_debug > 1) - printk("bttv%d: wakeup: top=%p\n",btv->c.nr,wakeup->top); + pr_debug("%d: wakeup: top=%p\n", + btv->c.nr, wakeup->top); wakeup->top->vb.ts = ts; wakeup->top->vb.field_count = btv->field_count; wakeup->top->vb.state = state; @@ -3832,7 +3843,8 @@ bttv_irq_wakeup_video(struct bttv *btv, struct bttv_buffer_set *wakeup, } if (NULL != wakeup->bottom && curr->bottom != wakeup->bottom) { if (irq_debug > 1) - printk("bttv%d: wakeup: bottom=%p\n",btv->c.nr,wakeup->bottom); + pr_debug("%d: wakeup: bottom=%p\n", + btv->c.nr, wakeup->bottom); wakeup->bottom->vb.ts = ts; wakeup->bottom->vb.field_count = btv->field_count; wakeup->bottom->vb.state = state; @@ -3866,11 +3878,11 @@ static void bttv_irq_timeout(unsigned long data) unsigned long flags; if (bttv_verbose) { - printk(KERN_INFO "bttv%d: timeout: drop=%d irq=%d/%d, risc=%08x, ", - btv->c.nr, btv->framedrop, btv->irq_me, btv->irq_total, - btread(BT848_RISC_COUNT)); + pr_info("%d: timeout: drop=%d irq=%d/%d, risc=%08x, ", + btv->c.nr, btv->framedrop, btv->irq_me, btv->irq_total, + btread(BT848_RISC_COUNT)); bttv_print_irqbits(btread(BT848_INT_STAT),0); - printk("\n"); + pr_cont("\n"); } spin_lock_irqsave(&btv->s_lock,flags); @@ -4033,21 +4045,23 @@ static irqreturn_t bttv_irq(int irq, void *dev_id) dstat=btread(BT848_DSTATUS); if (irq_debug) { - printk(KERN_DEBUG "bttv%d: irq loop=%d fc=%d " - "riscs=%x, riscc=%08x, ", - btv->c.nr, count, btv->field_count, - stat>>28, btread(BT848_RISC_COUNT)); + pr_debug("%d: irq loop=%d fc=%d riscs=%x, riscc=%08x, ", + btv->c.nr, count, btv->field_count, + stat>>28, btread(BT848_RISC_COUNT)); bttv_print_irqbits(stat,astat); if (stat & BT848_INT_HLOCK) - printk(" HLOC => %s", (dstat & BT848_DSTATUS_HLOC) - ? "yes" : "no"); + pr_cont(" HLOC => %s", + dstat & BT848_DSTATUS_HLOC + ? "yes" : "no"); if (stat & BT848_INT_VPRES) - printk(" PRES => %s", (dstat & BT848_DSTATUS_PRES) - ? "yes" : "no"); + pr_cont(" PRES => %s", + dstat & BT848_DSTATUS_PRES + ? "yes" : "no"); if (stat & BT848_INT_FMTCHG) - printk(" NUML => %s", (dstat & BT848_DSTATUS_NUML) - ? "625" : "525"); - printk("\n"); + pr_cont(" NUML => %s", + dstat & BT848_DSTATUS_NUML + ? "625" : "525"); + pr_cont("\n"); } if (astat&BT848_INT_VSYNC) @@ -4075,18 +4089,19 @@ static irqreturn_t bttv_irq(int irq, void *dev_id) audio_mute(btv, btv->mute); /* trigger automute */ if (astat & (BT848_INT_SCERR|BT848_INT_OCERR)) { - printk(KERN_INFO "bttv%d: %s%s @ %08x,",btv->c.nr, - (astat & BT848_INT_SCERR) ? "SCERR" : "", - (astat & BT848_INT_OCERR) ? "OCERR" : "", - btread(BT848_RISC_COUNT)); + pr_info("%d: %s%s @ %08x,", + btv->c.nr, + (astat & BT848_INT_SCERR) ? "SCERR" : "", + (astat & BT848_INT_OCERR) ? "OCERR" : "", + btread(BT848_RISC_COUNT)); bttv_print_irqbits(stat,astat); - printk("\n"); + pr_cont("\n"); if (bttv_debug) bttv_print_riscaddr(btv); } if (fdsr && astat & BT848_INT_FDSR) { - printk(KERN_INFO "bttv%d: FDSR @ %08x\n", - btv->c.nr,btread(BT848_RISC_COUNT)); + pr_info("%d: FDSR @ %08x\n", + btv->c.nr, btread(BT848_RISC_COUNT)); if (bttv_debug) bttv_print_riscaddr(btv); } @@ -4097,11 +4112,11 @@ static irqreturn_t bttv_irq(int irq, void *dev_id) if (count > 8 || !(astat & BT848_INT_GPINT)) { btwrite(0, BT848_INT_MASK); - printk(KERN_ERR - "bttv%d: IRQ lockup, cleared int mask [", btv->c.nr); + pr_err("%d: IRQ lockup, cleared int mask [", + btv->c.nr); } else { - printk(KERN_ERR - "bttv%d: IRQ lockup, clearing GPINT from int mask [", btv->c.nr); + pr_err("%d: IRQ lockup, clearing GPINT from int mask [", + btv->c.nr); btwrite(btread(BT848_INT_MASK) & (-1 ^ BT848_INT_GPINT), BT848_INT_MASK); @@ -4109,7 +4124,7 @@ static irqreturn_t bttv_irq(int irq, void *dev_id) bttv_print_irqbits(stat,astat); - printk("]\n"); + pr_cont("]\n"); } } btv->irq_total++; @@ -4171,7 +4186,7 @@ static void bttv_unregister_video(struct bttv *btv) static int __devinit bttv_register_video(struct bttv *btv) { if (no_overlay > 0) - printk("bttv: Overlay support disabled.\n"); + pr_notice("Overlay support disabled\n"); /* video */ btv->video_dev = vdev_init(btv, &bttv_video_template, "video"); @@ -4181,12 +4196,11 @@ static int __devinit bttv_register_video(struct bttv *btv) if (video_register_device(btv->video_dev, VFL_TYPE_GRABBER, video_nr[btv->c.nr]) < 0) goto err; - printk(KERN_INFO "bttv%d: registered device %s\n", - btv->c.nr, video_device_node_name(btv->video_dev)); + pr_info("%d: registered device %s\n", + btv->c.nr, video_device_node_name(btv->video_dev)); if (device_create_file(&btv->video_dev->dev, &dev_attr_card)<0) { - printk(KERN_ERR "bttv%d: device_create_file 'card' " - "failed\n", btv->c.nr); + pr_err("%d: device_create_file 'card' failed\n", btv->c.nr); goto err; } @@ -4198,8 +4212,8 @@ static int __devinit bttv_register_video(struct bttv *btv) if (video_register_device(btv->vbi_dev, VFL_TYPE_VBI, vbi_nr[btv->c.nr]) < 0) goto err; - printk(KERN_INFO "bttv%d: registered device %s\n", - btv->c.nr, video_device_node_name(btv->vbi_dev)); + pr_info("%d: registered device %s\n", + btv->c.nr, video_device_node_name(btv->vbi_dev)); if (!btv->has_radio) return 0; @@ -4210,8 +4224,8 @@ static int __devinit bttv_register_video(struct bttv *btv) if (video_register_device(btv->radio_dev, VFL_TYPE_RADIO, radio_nr[btv->c.nr]) < 0) goto err; - printk(KERN_INFO "bttv%d: registered device %s\n", - btv->c.nr, video_device_node_name(btv->radio_dev)); + pr_info("%d: registered device %s\n", + btv->c.nr, video_device_node_name(btv->radio_dev)); /* all done */ return 0; @@ -4244,10 +4258,10 @@ static int __devinit bttv_probe(struct pci_dev *dev, if (bttv_num == BTTV_MAX) return -ENOMEM; - printk(KERN_INFO "bttv: Bt8xx card found (%d).\n", bttv_num); + pr_info("Bt8xx card found (%d)\n", bttv_num); bttvs[bttv_num] = btv = kzalloc(sizeof(*btv), GFP_KERNEL); if (btv == NULL) { - printk(KERN_ERR "bttv: out of memory.\n"); + pr_err("out of memory\n"); return -ENOMEM; } btv->c.nr = bttv_num; @@ -4277,21 +4291,19 @@ static int __devinit bttv_probe(struct pci_dev *dev, btv->c.pci = dev; btv->id = dev->device; if (pci_enable_device(dev)) { - printk(KERN_WARNING "bttv%d: Can't enable device.\n", - btv->c.nr); + pr_warn("%d: Can't enable device\n", btv->c.nr); return -EIO; } if (pci_set_dma_mask(dev, DMA_BIT_MASK(32))) { - printk(KERN_WARNING "bttv%d: No suitable DMA available.\n", - btv->c.nr); + pr_warn("%d: No suitable DMA available\n", btv->c.nr); return -EIO; } if (!request_mem_region(pci_resource_start(dev,0), pci_resource_len(dev,0), btv->c.v4l2_dev.name)) { - printk(KERN_WARNING "bttv%d: can't request iomem (0x%llx).\n", - btv->c.nr, - (unsigned long long)pci_resource_start(dev,0)); + pr_warn("%d: can't request iomem (0x%llx)\n", + btv->c.nr, + (unsigned long long)pci_resource_start(dev, 0)); return -EBUSY; } pci_set_master(dev); @@ -4299,22 +4311,21 @@ static int __devinit bttv_probe(struct pci_dev *dev, result = v4l2_device_register(&dev->dev, &btv->c.v4l2_dev); if (result < 0) { - printk(KERN_WARNING "bttv%d: v4l2_device_register() failed\n", btv->c.nr); + pr_warn("%d: v4l2_device_register() failed\n", btv->c.nr); goto fail0; } btv->revision = dev->revision; pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat); - printk(KERN_INFO "bttv%d: Bt%d (rev %d) at %s, ", - bttv_num,btv->id, btv->revision, pci_name(dev)); - printk("irq: %d, latency: %d, mmio: 0x%llx\n", - btv->c.pci->irq, lat, - (unsigned long long)pci_resource_start(dev,0)); + pr_info("%d: Bt%d (rev %d) at %s, irq: %d, latency: %d, mmio: 0x%llx\n", + bttv_num, btv->id, btv->revision, pci_name(dev), + btv->c.pci->irq, lat, + (unsigned long long)pci_resource_start(dev, 0)); schedule(); btv->bt848_mmio = ioremap(pci_resource_start(dev, 0), 0x1000); if (NULL == btv->bt848_mmio) { - printk("bttv%d: ioremap() failed\n", btv->c.nr); + pr_err("%d: ioremap() failed\n", btv->c.nr); result = -EIO; goto fail1; } @@ -4327,8 +4338,8 @@ static int __devinit bttv_probe(struct pci_dev *dev, result = request_irq(btv->c.pci->irq, bttv_irq, IRQF_SHARED | IRQF_DISABLED, btv->c.v4l2_dev.name, (void *)btv); if (result < 0) { - printk(KERN_ERR "bttv%d: can't get IRQ %d\n", - bttv_num,btv->c.pci->irq); + pr_err("%d: can't get IRQ %d\n", + bttv_num, btv->c.pci->irq); goto fail1; } @@ -4433,7 +4444,7 @@ static void __devexit bttv_remove(struct pci_dev *pci_dev) struct bttv *btv = to_bttv(v4l2_dev); if (bttv_verbose) - printk("bttv%d: unloading\n",btv->c.nr); + pr_info("%d: unloading\n", btv->c.nr); if (bttv_tvcards[btv->c.type].has_dvb) flush_request_modules(btv); @@ -4481,7 +4492,7 @@ static int bttv_suspend(struct pci_dev *pci_dev, pm_message_t state) struct bttv_buffer_set idle; unsigned long flags; - dprintk("bttv%d: suspend %d\n", btv->c.nr, state.event); + dprintk("%d: suspend %d\n", btv->c.nr, state.event); /* stop dma + irqs */ spin_lock_irqsave(&btv->s_lock,flags); @@ -4517,14 +4528,13 @@ static int bttv_resume(struct pci_dev *pci_dev) unsigned long flags; int err; - dprintk("bttv%d: resume\n", btv->c.nr); + dprintk("%d: resume\n", btv->c.nr); /* restore pci state */ if (btv->state.disabled) { err=pci_enable_device(pci_dev); if (err) { - printk(KERN_WARNING "bttv%d: Can't enable device.\n", - btv->c.nr); + pr_warn("%d: Can't enable device\n", btv->c.nr); return err; } btv->state.disabled = 0; @@ -4532,8 +4542,7 @@ static int bttv_resume(struct pci_dev *pci_dev) err=pci_set_power_state(pci_dev, PCI_D0); if (err) { pci_disable_device(pci_dev); - printk(KERN_WARNING "bttv%d: Can't enable device.\n", - btv->c.nr); + pr_warn("%d: Can't enable device\n", btv->c.nr); btv->state.disabled = 1; return err; } @@ -4585,22 +4594,21 @@ static int __init bttv_init_module(void) bttv_num = 0; - printk(KERN_INFO "bttv: driver version %s loaded\n", - BTTV_VERSION); + pr_info("driver version %s loaded\n", BTTV_VERSION); if (gbuffers < 2 || gbuffers > VIDEO_MAX_FRAME) gbuffers = 2; if (gbufsize > BTTV_MAX_FBUF) gbufsize = BTTV_MAX_FBUF; gbufsize = (gbufsize + PAGE_SIZE - 1) & PAGE_MASK; if (bttv_verbose) - printk(KERN_INFO "bttv: using %d buffers with %dk (%d pages) each for capture\n", - gbuffers, gbufsize >> 10, gbufsize >> PAGE_SHIFT); + pr_info("using %d buffers with %dk (%d pages) each for capture\n", + gbuffers, gbufsize >> 10, gbufsize >> PAGE_SHIFT); bttv_check_chipset(); ret = bus_register(&bttv_sub_bus_type); if (ret < 0) { - printk(KERN_WARNING "bttv: bus_register error: %d\n", ret); + pr_warn("bus_register error: %d\n", ret); return ret; } ret = pci_register_driver(&bttv_pci_driver); diff --git a/drivers/media/video/bt8xx/bttv-gpio.c b/drivers/media/video/bt8xx/bttv-gpio.c index 13ce72c04b3..922e8233fd0 100644 --- a/drivers/media/video/bt8xx/bttv-gpio.c +++ b/drivers/media/video/bt8xx/bttv-gpio.c @@ -26,6 +26,8 @@ */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/init.h> #include <linux/delay.h> @@ -99,7 +101,7 @@ int bttv_sub_add_device(struct bttv_core *core, char *name) kfree(sub); return err; } - printk("bttv%d: add subdevice \"%s\"\n", core->nr, dev_name(&sub->dev)); + pr_info("%d: add subdevice \"%s\"\n", core->nr, dev_name(&sub->dev)); list_add_tail(&sub->list,&core->subs); return 0; } diff --git a/drivers/media/video/bt8xx/bttv-i2c.c b/drivers/media/video/bt8xx/bttv-i2c.c index d49b675045f..e3952af7e56 100644 --- a/drivers/media/video/bt8xx/bttv-i2c.c +++ b/drivers/media/video/bt8xx/bttv-i2c.c @@ -27,6 +27,8 @@ */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/init.h> #include <linux/delay.h> @@ -154,9 +156,7 @@ bttv_i2c_sendbytes(struct bttv *btv, const struct i2c_msg *msg, int last) if (retval == 0) goto eio; if (i2c_debug) { - printk(" <W %02x %02x", msg->addr << 1, msg->buf[0]); - if (!(xmit & BT878_I2C_NOSTOP)) - printk(" >\n"); + pr_cont(" <W %02x %02x", msg->addr << 1, msg->buf[0]); } for (cnt = 1; cnt < msg->len; cnt++ ) { @@ -170,19 +170,18 @@ bttv_i2c_sendbytes(struct bttv *btv, const struct i2c_msg *msg, int last) goto err; if (retval == 0) goto eio; - if (i2c_debug) { - printk(" %02x", msg->buf[cnt]); - if (!(xmit & BT878_I2C_NOSTOP)) - printk(" >\n"); - } + if (i2c_debug) + pr_cont(" %02x", msg->buf[cnt]); } + if (!(xmit & BT878_I2C_NOSTOP)) + pr_cont(">\n"); return msg->len; eio: retval = -EIO; err: if (i2c_debug) - printk(" ERR: %d\n",retval); + pr_cont(" ERR: %d\n",retval); return retval; } @@ -193,7 +192,7 @@ bttv_i2c_readbytes(struct bttv *btv, const struct i2c_msg *msg, int last) u32 cnt; int retval; - for(cnt = 0; cnt < msg->len; cnt++) { + for (cnt = 0; cnt < msg->len; cnt++) { xmit = (msg->addr << 25) | (1 << 24) | I2C_HW; if (cnt < msg->len-1) xmit |= BT848_I2C_W3B; @@ -201,6 +200,12 @@ bttv_i2c_readbytes(struct bttv *btv, const struct i2c_msg *msg, int last) xmit |= BT878_I2C_NOSTOP; if (cnt) xmit |= BT878_I2C_NOSTART; + + if (i2c_debug) { + if (!(xmit & BT878_I2C_NOSTART)) + pr_cont(" <R %02x", (msg->addr << 1) +1); + } + btwrite(xmit, BT848_I2C); retval = bttv_i2c_wait_done(btv); if (retval < 0) @@ -209,20 +214,20 @@ bttv_i2c_readbytes(struct bttv *btv, const struct i2c_msg *msg, int last) goto eio; msg->buf[cnt] = ((u32)btread(BT848_I2C) >> 8) & 0xff; if (i2c_debug) { - if (!(xmit & BT878_I2C_NOSTART)) - printk(" <R %02x", (msg->addr << 1) +1); - printk(" =%02x", msg->buf[cnt]); - if (!(xmit & BT878_I2C_NOSTOP)) - printk(" >\n"); + pr_cont(" =%02x", msg->buf[cnt]); } + if (i2c_debug && !(xmit & BT878_I2C_NOSTOP)) + pr_cont(" >\n"); } + + return msg->len; eio: retval = -EIO; err: if (i2c_debug) - printk(" ERR: %d\n",retval); + pr_cont(" ERR: %d\n",retval); return retval; } @@ -234,7 +239,8 @@ static int bttv_i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int int i; if (i2c_debug) - printk("bt-i2c:"); + pr_debug("bt-i2c:"); + btwrite(BT848_INT_I2CDONE|BT848_INT_RACK, BT848_INT_STAT); for (i = 0 ; i < num; i++) { if (msgs[i].flags & I2C_M_RD) { @@ -271,20 +277,20 @@ int bttv_I2CRead(struct bttv *btv, unsigned char addr, char *probe_for) if (0 != btv->i2c_rc) return -1; if (bttv_verbose && NULL != probe_for) - printk(KERN_INFO "bttv%d: i2c: checking for %s @ 0x%02x... ", - btv->c.nr,probe_for,addr); + pr_info("%d: i2c: checking for %s @ 0x%02x... ", + btv->c.nr, probe_for, addr); btv->i2c_client.addr = addr >> 1; if (1 != i2c_master_recv(&btv->i2c_client, &buffer, 1)) { if (NULL != probe_for) { if (bttv_verbose) - printk("not found\n"); + pr_cont("not found\n"); } else - printk(KERN_WARNING "bttv%d: i2c read 0x%x: error\n", - btv->c.nr,addr); + pr_warn("%d: i2c read 0x%x: error\n", + btv->c.nr, addr); return -1; } if (bttv_verbose && NULL != probe_for) - printk("found\n"); + pr_cont("found\n"); return buffer; } @@ -335,8 +341,8 @@ static void do_i2c_scan(char *name, struct i2c_client *c) rc = i2c_master_recv(c,&buf,0); if (rc < 0) continue; - printk("%s: i2c scan: found device @ 0x%x [%s]\n", - name, i << 1, i2c_devs[i] ? i2c_devs[i] : "???"); + pr_info("%s: i2c scan: found device @ 0x%x [%s]\n", + name, i << 1, i2c_devs[i] ? i2c_devs[i] : "???"); } } diff --git a/drivers/media/video/bt8xx/bttv-input.c b/drivers/media/video/bt8xx/bttv-input.c index 677d70c0e1c..ef4c7cd4198 100644 --- a/drivers/media/video/bt8xx/bttv-input.c +++ b/drivers/media/video/bt8xx/bttv-input.c @@ -18,6 +18,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/init.h> #include <linux/delay.h> @@ -36,9 +38,10 @@ static int ir_rc5_remote_gap = 885; module_param(ir_rc5_remote_gap, int, 0644); #undef dprintk -#define dprintk(arg...) do { \ - if (ir_debug >= 1) \ - printk(arg); \ +#define dprintk(fmt, ...) \ +do { \ + if (ir_debug >= 1) \ + pr_info(fmt, ##__VA_ARGS__); \ } while (0) #define DEVNAME "bttv-input" @@ -62,7 +65,7 @@ static void ir_handle_key(struct bttv *btv) /* extract data */ data = ir_extract_bits(gpio, ir->mask_keycode); - dprintk(KERN_INFO DEVNAME ": irq gpio=0x%x code=%d | %s%s%s\n", + dprintk("irq gpio=0x%x code=%d | %s%s%s\n", gpio, data, ir->polling ? "poll" : "irq", (gpio & ir->mask_keydown) ? " down" : "", @@ -96,7 +99,7 @@ static void ir_enltv_handle_key(struct bttv *btv) keyup = (gpio & ir->mask_keyup) ? 1 << 31 : 0; if ((ir->last_gpio & 0x7f) != data) { - dprintk(KERN_INFO DEVNAME ": gpio=0x%x code=%d | %s\n", + dprintk("gpio=0x%x code=%d | %s\n", gpio, data, (gpio & ir->mask_keyup) ? " up" : "up/down"); @@ -107,7 +110,7 @@ static void ir_enltv_handle_key(struct bttv *btv) if ((ir->last_gpio & 1 << 31) == keyup) return; - dprintk(KERN_INFO DEVNAME ":(cnt) gpio=0x%x code=%d | %s\n", + dprintk("(cnt) gpio=0x%x code=%d | %s\n", gpio, data, (gpio & ir->mask_keyup) ? " up" : "down"); @@ -177,13 +180,12 @@ static u32 bttv_rc5_decode(unsigned int code) rc5 |= 1; break; case 3: - dprintk(KERN_INFO DEVNAME ":rc5_decode(%x) bad code\n", + dprintk("rc5_decode(%x) bad code\n", org_code); return 0; } } - dprintk(KERN_INFO DEVNAME ":" - "code=%x, rc5=%x, start=%x, toggle=%x, address=%x, " + dprintk("code=%x, rc5=%x, start=%x, toggle=%x, address=%x, " "instr=%x\n", rc5, org_code, RC5_START(rc5), RC5_TOGGLE(rc5), RC5_ADDR(rc5), RC5_INSTR(rc5)); return rc5; @@ -212,20 +214,20 @@ static void bttv_rc5_timer_end(unsigned long data) /* Allow some timer jitter (RC5 is ~24ms anyway so this is ok) */ if (gap < 28000) { - dprintk(KERN_INFO DEVNAME ": spurious timer_end\n"); + dprintk("spurious timer_end\n"); return; } if (ir->last_bit < 20) { /* ignore spurious codes (caused by light/other remotes) */ - dprintk(KERN_INFO DEVNAME ": short code: %x\n", ir->code); + dprintk("short code: %x\n", ir->code); } else { ir->code = (ir->code << ir->shift_by) | 1; rc5 = bttv_rc5_decode(ir->code); /* two start bits? */ if (RC5_START(rc5) != ir->start) { - printk(KERN_INFO DEVNAME ":" + pr_info(DEVNAME ":" " rc5 start bits invalid: %u\n", RC5_START(rc5)); /* right address? */ @@ -235,8 +237,7 @@ static void bttv_rc5_timer_end(unsigned long data) /* Good code */ rc_keydown(ir->dev, instr, toggle); - dprintk(KERN_INFO DEVNAME ":" - " instruction %x, toggle %x\n", + dprintk("instruction %x, toggle %x\n", instr, toggle); } } @@ -265,7 +266,7 @@ static int bttv_rc5_irq(struct bttv *btv) tv.tv_usec - ir->base_time.tv_usec; } - dprintk(KERN_INFO DEVNAME ": RC5 IRQ: gap %d us for %s\n", + dprintk("RC5 IRQ: gap %d us for %s\n", gap, (gpio & 0x20) ? "mark" : "space"); /* remote IRQ? */ @@ -340,14 +341,14 @@ static int get_key_pv951(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) /* poll IR chip */ if (1 != i2c_master_recv(ir->c, &b, 1)) { - dprintk(KERN_INFO DEVNAME ": read error\n"); + dprintk("read error\n"); return -EIO; } /* ignore 0xaa */ if (b==0xaa) return 0; - dprintk(KERN_INFO DEVNAME ": key %02x\n", b); + dprintk("key %02x\n", b); /* * NOTE: @@ -517,7 +518,7 @@ int bttv_input_init(struct bttv *btv) break; } if (NULL == ir_codes) { - dprintk(KERN_INFO "Ooops: IR config error [card=%d]\n", btv->c.type); + dprintk("Ooops: IR config error [card=%d]\n", btv->c.type); err = -ENODEV; goto err_out_free; } diff --git a/drivers/media/video/bt8xx/bttv-risc.c b/drivers/media/video/bt8xx/bttv-risc.c index 9b57d091da4..82cc47d2e3f 100644 --- a/drivers/media/video/bt8xx/bttv-risc.c +++ b/drivers/media/video/bt8xx/bttv-risc.c @@ -24,6 +24,8 @@ */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/init.h> #include <linux/slab.h> @@ -473,8 +475,7 @@ bttv_set_dma(struct bttv *btv, int override) capctl |= (btv->cap_ctl & 0x0c) ? 0x0c : 0x00; /* vbi data */ capctl |= override; - d2printk(KERN_DEBUG - "bttv%d: capctl=%x lirq=%d top=%08Lx/%08Lx even=%08Lx/%08Lx\n", + d2printk("%d: capctl=%x lirq=%d top=%08llx/%08llx even=%08llx/%08llx\n", btv->c.nr,capctl,btv->loop_irq, btv->cvbi ? (unsigned long long)btv->cvbi->top.dma : 0, btv->curr.top ? (unsigned long long)btv->curr.top->top.dma : 0, @@ -517,8 +518,8 @@ bttv_risc_init_main(struct bttv *btv) if ((rc = btcx_riscmem_alloc(btv->c.pci,&btv->main,PAGE_SIZE)) < 0) return rc; - dprintk(KERN_DEBUG "bttv%d: risc main @ %08Lx\n", - btv->c.nr,(unsigned long long)btv->main.dma); + dprintk("%d: risc main @ %08llx\n", + btv->c.nr, (unsigned long long)btv->main.dma); btv->main.cpu[0] = cpu_to_le32(BT848_RISC_SYNC | BT848_RISC_RESYNC | BT848_FIFO_STATUS_VRE); @@ -557,12 +558,12 @@ bttv_risc_hook(struct bttv *btv, int slot, struct btcx_riscmem *risc, unsigned long next = btv->main.dma + ((slot+2) << 2); if (NULL == risc) { - d2printk(KERN_DEBUG "bttv%d: risc=%p slot[%d]=NULL\n", - btv->c.nr,risc,slot); + d2printk("%d: risc=%p slot[%d]=NULL\n", btv->c.nr, risc, slot); btv->main.cpu[slot+1] = cpu_to_le32(next); } else { - d2printk(KERN_DEBUG "bttv%d: risc=%p slot[%d]=%08Lx irq=%d\n", - btv->c.nr,risc,slot,(unsigned long long)risc->dma,irqflags); + d2printk("%d: risc=%p slot[%d]=%08llx irq=%d\n", + btv->c.nr, risc, slot, + (unsigned long long)risc->dma, irqflags); cmd = BT848_RISC_JUMP; if (irqflags) { cmd |= BT848_RISC_IRQ; @@ -708,8 +709,7 @@ bttv_buffer_risc(struct bttv *btv, struct bttv_buffer *buf) const struct bttv_tvnorm *tvnorm = bttv_tvnorms + buf->tvnorm; struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb); - dprintk(KERN_DEBUG - "bttv%d: buffer field: %s format: %s size: %dx%d\n", + dprintk("%d: buffer field: %s format: %s size: %dx%d\n", btv->c.nr, v4l2_field_names[buf->vb.field], buf->fmt->name, buf->vb.width, buf->vb.height); @@ -870,10 +870,9 @@ bttv_overlay_risc(struct bttv *btv, struct bttv_buffer *buf) { /* check interleave, bottom+top fields */ - dprintk(KERN_DEBUG - "bttv%d: overlay fields: %s format: %s size: %dx%d\n", + dprintk("%d: overlay fields: %s format: %s size: %dx%d\n", btv->c.nr, v4l2_field_names[buf->vb.field], - fmt->name,ov->w.width,ov->w.height); + fmt->name, ov->w.width, ov->w.height); /* calculate geometry */ bttv_calc_geo(btv,&buf->geo,ov->w.width,ov->w.height, diff --git a/drivers/media/video/bt8xx/bttv-vbi.c b/drivers/media/video/bt8xx/bttv-vbi.c index e79a402fa6c..b433267d9aa 100644 --- a/drivers/media/video/bt8xx/bttv-vbi.c +++ b/drivers/media/video/bt8xx/bttv-vbi.c @@ -23,6 +23,8 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/errno.h> #include <linux/fs.h> @@ -65,8 +67,11 @@ MODULE_PARM_DESC(vbi_debug,"vbi code debug messages, default is 0 (no)"); #ifdef dprintk # undef dprintk #endif -#define dprintk(fmt, arg...) if (vbi_debug) \ - printk(KERN_DEBUG "bttv%d/vbi: " fmt, btv->c.nr , ## arg) +#define dprintk(fmt, ...) \ +do { \ + if (vbi_debug) \ + pr_debug("%d: " fmt, btv->c.nr, ##__VA_ARGS__); \ +} while (0) #define IMAGE_SIZE(fmt) \ (((fmt)->count[0] + (fmt)->count[1]) * (fmt)->samples_per_line) diff --git a/drivers/media/video/bt8xx/bttvp.h b/drivers/media/video/bt8xx/bttvp.h index 318edf2830b..db943a8d580 100644 --- a/drivers/media/video/bt8xx/bttvp.h +++ b/drivers/media/video/bt8xx/bttvp.h @@ -310,9 +310,21 @@ extern unsigned int bttv_gpio; extern void bttv_gpio_tracking(struct bttv *btv, char *comment); extern int init_bttv_i2c(struct bttv *btv); -#define bttv_printk if (bttv_verbose) printk -#define dprintk if (bttv_debug >= 1) printk -#define d2printk if (bttv_debug >= 2) printk +#define dprintk(fmt, ...) \ +do { \ + if (bttv_debug >= 1) \ + pr_debug(fmt, ##__VA_ARGS__); \ +} while (0) +#define dprintk_cont(fmt, ...) \ +do { \ + if (bttv_debug >= 1) \ + pr_cont(fmt, ##__VA_ARGS__); \ +} while (0) +#define d2printk(fmt, ...) \ +do { \ + if (bttv_debug >= 2) \ + printk(fmt, ##__VA_ARGS__); \ +} while (0) #define BTTV_MAX_FBUF 0x208000 #define BTTV_TIMEOUT msecs_to_jiffies(500) /* 0.5 seconds */ diff --git a/drivers/media/video/cpia2/cpia2_usb.c b/drivers/media/video/cpia2/cpia2_usb.c index dc5b07a20f6..59c797c1527 100644 --- a/drivers/media/video/cpia2/cpia2_usb.c +++ b/drivers/media/video/cpia2/cpia2_usb.c @@ -31,6 +31,7 @@ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/usb.h> +#include <linux/module.h> #include "cpia2.h" diff --git a/drivers/media/video/cx18/Makefile b/drivers/media/video/cx18/Makefile index 2fadd9ded34..a86bab5893e 100644 --- a/drivers/media/video/cx18/Makefile +++ b/drivers/media/video/cx18/Makefile @@ -8,6 +8,6 @@ cx18-alsa-objs := cx18-alsa-main.o cx18-alsa-pcm.o obj-$(CONFIG_VIDEO_CX18) += cx18.o obj-$(CONFIG_VIDEO_CX18_ALSA) += cx18-alsa.o -EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core -EXTRA_CFLAGS += -Idrivers/media/dvb/frontends -EXTRA_CFLAGS += -Idrivers/media/common/tuners +ccflags-y += -Idrivers/media/dvb/dvb-core +ccflags-y += -Idrivers/media/dvb/frontends +ccflags-y += -Idrivers/media/common/tuners diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c index 9e2f870f425..c6ff32a6137 100644 --- a/drivers/media/video/cx18/cx18-driver.c +++ b/drivers/media/video/cx18/cx18-driver.c @@ -1085,6 +1085,8 @@ static int __devinit cx18_probe(struct pci_dev *pci_dev, setup.addr = ADDR_UNSET; setup.type = cx->options.tuner; setup.mode_mask = T_ANALOG_TV; /* matches TV tuners */ + if (cx->options.radio > 0) + setup.mode_mask |= T_RADIO; setup.tuner_callback = (setup.type == TUNER_XC2028) ? cx18_reset_tuner_gpio : NULL; cx18_call_all(cx, tuner, s_type_addr, &setup); diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h index 18342072306..b9a94fc5146 100644 --- a/drivers/media/video/cx18/cx18-driver.h +++ b/drivers/media/video/cx18/cx18-driver.h @@ -409,6 +409,7 @@ struct cx18_stream { /* Videobuf for YUV video */ u32 pixelformat; + u32 vb_bytes_per_frame; struct list_head vb_capture; /* video capture queue */ spinlock_t vb_lock; struct timer_list vb_timeout; @@ -430,10 +431,6 @@ struct cx18_open_id { u32 open_id; int type; struct cx18 *cx; - - struct videobuf_queue vbuf_q; - spinlock_t s_lock; /* Protect vbuf_q */ - enum v4l2_buf_type vb_type; }; static inline struct cx18_open_id *fh2id(struct v4l2_fh *fh) diff --git a/drivers/media/video/cx18/cx18-fileops.c b/drivers/media/video/cx18/cx18-fileops.c index 07411f34885..14cb961c22b 100644 --- a/drivers/media/video/cx18/cx18-fileops.c +++ b/drivers/media/video/cx18/cx18-fileops.c @@ -784,8 +784,6 @@ int cx18_v4l2_close(struct file *filp) cx18_release_stream(s); } else { cx18_stop_capture(id, 0); - if (id->type == CX18_ENC_STREAM_TYPE_YUV) - videobuf_mmap_free(&id->vbuf_q); } kfree(id); mutex_unlock(&cx->serialize_lock); diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c index afe0a29e720..66b1c15c354 100644 --- a/drivers/media/video/cx18/cx18-ioctl.c +++ b/drivers/media/video/cx18/cx18-ioctl.c @@ -160,12 +160,7 @@ static int cx18_g_fmt_vid_cap(struct file *file, void *fh, pixfmt->priv = 0; if (id->type == CX18_ENC_STREAM_TYPE_YUV) { pixfmt->pixelformat = s->pixelformat; - /* HM12 YUV size is (Y=(h*720) + UV=(h*(720/2))) - UYUV YUV size is (Y=(h*720) + UV=(h*(720))) */ - if (s->pixelformat == V4L2_PIX_FMT_HM12) - pixfmt->sizeimage = pixfmt->height * 720 * 3 / 2; - else - pixfmt->sizeimage = pixfmt->height * 720 * 2; + pixfmt->sizeimage = s->vb_bytes_per_frame; pixfmt->bytesperline = 720; } else { pixfmt->pixelformat = V4L2_PIX_FMT_MPEG; @@ -296,6 +291,12 @@ static int cx18_s_fmt_vid_cap(struct file *file, void *fh, return -EBUSY; s->pixelformat = fmt->fmt.pix.pixelformat; + /* HM12 YUV size is (Y=(h*720) + UV=(h*(720/2))) + UYUV YUV size is (Y=(h*720) + UV=(h*(720))) */ + if (s->pixelformat == V4L2_PIX_FMT_HM12) + s->vb_bytes_per_frame = h * 720 * 3 / 2; + else + s->vb_bytes_per_frame = h * 720 * 2; mbus_fmt.width = cx->cxhdl.width = w; mbus_fmt.height = cx->cxhdl.height = h; @@ -463,13 +464,16 @@ static int cx18_s_register(struct file *file, void *fh, static int cx18_querycap(struct file *file, void *fh, struct v4l2_capability *vcap) { - struct cx18 *cx = fh2id(fh)->cx; + struct cx18_open_id *id = fh2id(fh); + struct cx18 *cx = id->cx; strlcpy(vcap->driver, CX18_DRIVER_NAME, sizeof(vcap->driver)); strlcpy(vcap->card, cx->card_name, sizeof(vcap->card)); snprintf(vcap->bus_info, sizeof(vcap->bus_info), "PCI:%s", pci_name(cx->pci_dev)); vcap->capabilities = cx->v4l2_cap; /* capabilities */ + if (id->type == CX18_ENC_STREAM_TYPE_YUV) + vcap->capabilities |= V4L2_CAP_STREAMING; return 0; } diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c index c07191e09fc..0c7796e76ac 100644 --- a/drivers/media/video/cx18/cx18-mailbox.c +++ b/drivers/media/video/cx18/cx18-mailbox.c @@ -196,7 +196,7 @@ static void cx18_mdl_send_to_videobuf(struct cx18_stream *s, } /* If we've filled the buffer as per the callers res then dispatch it */ - if (vb_buf->bytes_used >= (vb_buf->vb.width * vb_buf->vb.height * 2)) { + if (vb_buf->bytes_used >= s->vb_bytes_per_frame) { dispatch = 1; vb_buf->bytes_used = 0; } diff --git a/drivers/media/video/cx18/cx18-mailbox.h b/drivers/media/video/cx18/cx18-mailbox.h index 05fe6bdbe06..b63fdfaac49 100644 --- a/drivers/media/video/cx18/cx18-mailbox.h +++ b/drivers/media/video/cx18/cx18-mailbox.h @@ -69,7 +69,7 @@ struct cx18_mailbox { /* Each command can have up to 6 arguments */ u32 args[MAX_MB_ARGUMENTS]; /* The return code can be one of the codes in the file cx23418.h. If the - command is completed successfuly, the error will be ERR_SYS_SUCCESS. + command is completed successfully, the error will be ERR_SYS_SUCCESS. If it is pending, the code is ERR_SYS_PENDING. If it failed, the error code would indicate the task from which the error originated and will be one of the errors in cx23418.h. In that case, the following diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c index 852f420fd27..638cca156b5 100644 --- a/drivers/media/video/cx18/cx18-streams.c +++ b/drivers/media/video/cx18/cx18-streams.c @@ -138,6 +138,12 @@ static int cx18_prepare_buffer(struct videobuf_queue *q, buf->tvnorm = cx->std; s->pixelformat = pixelformat; + /* HM12 YUV size is (Y=(h*720) + UV=(h*(720/2))) + UYUV YUV size is (Y=(h*720) + UV=(h*(720))) */ + if (s->pixelformat == V4L2_PIX_FMT_HM12) + s->vb_bytes_per_frame = height * 720 * 3 / 2; + else + s->vb_bytes_per_frame = height * 720 * 2; cx18_dma_free(q, s, buf); } @@ -154,6 +160,12 @@ static int cx18_prepare_buffer(struct videobuf_queue *q, buf->tvnorm = cx->std; s->pixelformat = pixelformat; + /* HM12 YUV size is (Y=(h*720) + UV=(h*(720/2))) + UYUV YUV size is (Y=(h*720) + UV=(h*(720))) */ + if (s->pixelformat == V4L2_PIX_FMT_HM12) + s->vb_bytes_per_frame = height * 720 * 3 / 2; + else + s->vb_bytes_per_frame = height * 720 * 2; rc = videobuf_iolock(q, &buf->vb, NULL); if (rc != 0) goto fail; @@ -287,6 +299,7 @@ static void cx18_stream_init(struct cx18 *cx, int type) /* Assume the previous pixel default */ s->pixelformat = V4L2_PIX_FMT_HM12; + s->vb_bytes_per_frame = cx->cxhdl.height * 720 * 3 / 2; } } diff --git a/drivers/media/video/cx231xx/Makefile b/drivers/media/video/cx231xx/Makefile index 2c248435544..b3348975c7c 100644 --- a/drivers/media/video/cx231xx/Makefile +++ b/drivers/media/video/cx231xx/Makefile @@ -8,9 +8,9 @@ obj-$(CONFIG_VIDEO_CX231XX) += cx231xx.o obj-$(CONFIG_VIDEO_CX231XX_ALSA) += cx231xx-alsa.o obj-$(CONFIG_VIDEO_CX231XX_DVB) += cx231xx-dvb.o -EXTRA_CFLAGS += -Idrivers/media/video -EXTRA_CFLAGS += -Idrivers/media/common/tuners -EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core -EXTRA_CFLAGS += -Idrivers/media/dvb/frontends -EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-usb +ccflags-y += -Idrivers/media/video +ccflags-y += -Idrivers/media/common/tuners +ccflags-y += -Idrivers/media/dvb/dvb-core +ccflags-y += -Idrivers/media/dvb/frontends +ccflags-y += -Idrivers/media/dvb/dvb-usb diff --git a/drivers/media/video/cx23885/Kconfig b/drivers/media/video/cx23885/Kconfig index caab1bfb79e..b391e9bda87 100644 --- a/drivers/media/video/cx23885/Kconfig +++ b/drivers/media/video/cx23885/Kconfig @@ -38,7 +38,7 @@ config VIDEO_CX23885 config MEDIA_ALTERA_CI tristate "Altera FPGA based CI module" depends on VIDEO_CX23885 && DVB_CORE - select STAPL_ALTERA + select ALTERA_STAPL ---help--- An Altera FPGA CI module for NetUP Dual DVB-T/C RF CI card. diff --git a/drivers/media/video/cx23885/Makefile b/drivers/media/video/cx23885/Makefile index 23293c7b6ac..f81f2796a0f 100644 --- a/drivers/media/video/cx23885/Makefile +++ b/drivers/media/video/cx23885/Makefile @@ -2,14 +2,14 @@ cx23885-objs := cx23885-cards.o cx23885-video.o cx23885-vbi.o \ cx23885-core.o cx23885-i2c.o cx23885-dvb.o cx23885-417.o \ cx23885-ioctl.o cx23885-ir.o cx23885-av.o cx23885-input.o \ cx23888-ir.o netup-init.o cimax2.o netup-eeprom.o \ - cx23885-f300.o + cx23885-f300.o cx23885-alsa.o obj-$(CONFIG_VIDEO_CX23885) += cx23885.o obj-$(CONFIG_MEDIA_ALTERA_CI) += altera-ci.o -EXTRA_CFLAGS += -Idrivers/media/video -EXTRA_CFLAGS += -Idrivers/media/common/tuners -EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core -EXTRA_CFLAGS += -Idrivers/media/dvb/frontends +ccflags-y += -Idrivers/media/video +ccflags-y += -Idrivers/media/common/tuners +ccflags-y += -Idrivers/media/dvb/dvb-core +ccflags-y += -Idrivers/media/dvb/frontends -EXTRA_CFLAGS += $(extra-cflags-y) $(extra-cflags-m) +ccflags-y += $(extra-cflags-y) $(extra-cflags-m) diff --git a/drivers/media/video/cx23885/cx23885-alsa.c b/drivers/media/video/cx23885/cx23885-alsa.c new file mode 100644 index 00000000000..795169237e7 --- /dev/null +++ b/drivers/media/video/cx23885/cx23885-alsa.c @@ -0,0 +1,535 @@ +/* + * + * Support for CX23885 analog audio capture + * + * (c) 2008 Mijhail Moreyra <mijhail.moreyra@gmail.com> + * Adapted from cx88-alsa.c + * (c) 2009 Steven Toth <stoth@kernellabs.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/vmalloc.h> +#include <linux/dma-mapping.h> +#include <linux/pci.h> + +#include <asm/delay.h> + +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/control.h> +#include <sound/initval.h> + +#include <sound/tlv.h> + + +#include "cx23885.h" +#include "cx23885-reg.h" + +#define AUDIO_SRAM_CHANNEL SRAM_CH07 + +#define dprintk(level, fmt, arg...) if (audio_debug >= level) \ + printk(KERN_INFO "%s: " fmt, chip->dev->name , ## arg) + +#define dprintk_core(level, fmt, arg...) if (audio_debug >= level) \ + printk(KERN_DEBUG "%s: " fmt, chip->dev->name , ## arg) + +/**************************************************************************** + Module global static vars + ****************************************************************************/ + +static unsigned int disable_analog_audio; +module_param(disable_analog_audio, int, 0644); +MODULE_PARM_DESC(disable_analog_audio, "disable analog audio ALSA driver"); + +static unsigned int audio_debug; +module_param(audio_debug, int, 0644); +MODULE_PARM_DESC(audio_debug, "enable debug messages [analog audio]"); + +/**************************************************************************** + Board specific funtions + ****************************************************************************/ + +/* Constants taken from cx88-reg.h */ +#define AUD_INT_DN_RISCI1 (1 << 0) +#define AUD_INT_UP_RISCI1 (1 << 1) +#define AUD_INT_RDS_DN_RISCI1 (1 << 2) +#define AUD_INT_DN_RISCI2 (1 << 4) /* yes, 3 is skipped */ +#define AUD_INT_UP_RISCI2 (1 << 5) +#define AUD_INT_RDS_DN_RISCI2 (1 << 6) +#define AUD_INT_DN_SYNC (1 << 12) +#define AUD_INT_UP_SYNC (1 << 13) +#define AUD_INT_RDS_DN_SYNC (1 << 14) +#define AUD_INT_OPC_ERR (1 << 16) +#define AUD_INT_BER_IRQ (1 << 20) +#define AUD_INT_MCHG_IRQ (1 << 21) +#define GP_COUNT_CONTROL_RESET 0x3 + +/* + * BOARD Specific: Sets audio DMA + */ + +static int cx23885_start_audio_dma(struct cx23885_audio_dev *chip) +{ + struct cx23885_audio_buffer *buf = chip->buf; + struct cx23885_dev *dev = chip->dev; + struct sram_channel *audio_ch = + &dev->sram_channels[AUDIO_SRAM_CHANNEL]; + + dprintk(1, "%s()\n", __func__); + + /* Make sure RISC/FIFO are off before changing FIFO/RISC settings */ + cx_clear(AUD_INT_DMA_CTL, 0x11); + + /* setup fifo + format - out channel */ + cx23885_sram_channel_setup(chip->dev, audio_ch, buf->bpl, + buf->risc.dma); + + /* sets bpl size */ + cx_write(AUD_INT_A_LNGTH, buf->bpl); + + /* This is required to get good audio (1 seems to be ok) */ + cx_write(AUD_INT_A_MODE, 1); + + /* reset counter */ + cx_write(AUD_INT_A_GPCNT_CTL, GP_COUNT_CONTROL_RESET); + atomic_set(&chip->count, 0); + + dprintk(1, "Start audio DMA, %d B/line, %d lines/FIFO, %d periods, %d " + "byte buffer\n", buf->bpl, cx_read(audio_ch->cmds_start+12)>>1, + chip->num_periods, buf->bpl * chip->num_periods); + + /* Enables corresponding bits at AUD_INT_STAT */ + cx_write(AUDIO_INT_INT_MSK, AUD_INT_OPC_ERR | AUD_INT_DN_SYNC | + AUD_INT_DN_RISCI1); + + /* Clean any pending interrupt bits already set */ + cx_write(AUDIO_INT_INT_STAT, ~0); + + /* enable audio irqs */ + cx_set(PCI_INT_MSK, chip->dev->pci_irqmask | PCI_MSK_AUD_INT); + + /* start dma */ + cx_set(DEV_CNTRL2, (1<<5)); /* Enables Risc Processor */ + cx_set(AUD_INT_DMA_CTL, 0x11); /* audio downstream FIFO and + RISC enable */ + if (audio_debug) + cx23885_sram_channel_dump(chip->dev, audio_ch); + + return 0; +} + +/* + * BOARD Specific: Resets audio DMA + */ +static int cx23885_stop_audio_dma(struct cx23885_audio_dev *chip) +{ + struct cx23885_dev *dev = chip->dev; + dprintk(1, "Stopping audio DMA\n"); + + /* stop dma */ + cx_clear(AUD_INT_DMA_CTL, 0x11); + + /* disable irqs */ + cx_clear(PCI_INT_MSK, PCI_MSK_AUD_INT); + cx_clear(AUDIO_INT_INT_MSK, AUD_INT_OPC_ERR | AUD_INT_DN_SYNC | + AUD_INT_DN_RISCI1); + + if (audio_debug) + cx23885_sram_channel_dump(chip->dev, + &dev->sram_channels[AUDIO_SRAM_CHANNEL]); + + return 0; +} + +/* + * BOARD Specific: Handles audio IRQ + */ +int cx23885_audio_irq(struct cx23885_dev *dev, u32 status, u32 mask) +{ + struct cx23885_audio_dev *chip = dev->audio_dev; + + if (0 == (status & mask)) + return 0; + + cx_write(AUDIO_INT_INT_STAT, status); + + /* risc op code error */ + if (status & AUD_INT_OPC_ERR) { + printk(KERN_WARNING "%s/1: Audio risc op code error\n", + dev->name); + cx_clear(AUD_INT_DMA_CTL, 0x11); + cx23885_sram_channel_dump(dev, + &dev->sram_channels[AUDIO_SRAM_CHANNEL]); + } + if (status & AUD_INT_DN_SYNC) { + dprintk(1, "Downstream sync error\n"); + cx_write(AUD_INT_A_GPCNT_CTL, GP_COUNT_CONTROL_RESET); + return 1; + } + /* risc1 downstream */ + if (status & AUD_INT_DN_RISCI1) { + atomic_set(&chip->count, cx_read(AUD_INT_A_GPCNT)); + snd_pcm_period_elapsed(chip->substream); + } + /* FIXME: Any other status should deserve a special handling? */ + + return 1; +} + +static int dsp_buffer_free(struct cx23885_audio_dev *chip) +{ + BUG_ON(!chip->dma_size); + + dprintk(2, "Freeing buffer\n"); + videobuf_dma_unmap(&chip->pci->dev, chip->dma_risc); + videobuf_dma_free(chip->dma_risc); + btcx_riscmem_free(chip->pci, &chip->buf->risc); + kfree(chip->buf); + + chip->dma_risc = NULL; + chip->dma_size = 0; + + return 0; +} + +/**************************************************************************** + ALSA PCM Interface + ****************************************************************************/ + +/* + * Digital hardware definition + */ +#define DEFAULT_FIFO_SIZE 4096 + +static struct snd_pcm_hardware snd_cx23885_digital_hw = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + /* Analog audio output will be full of clicks and pops if there + are not exactly four lines in the SRAM FIFO buffer. */ + .period_bytes_min = DEFAULT_FIFO_SIZE/4, + .period_bytes_max = DEFAULT_FIFO_SIZE/4, + .periods_min = 1, + .periods_max = 1024, + .buffer_bytes_max = (1024*1024), +}; + +/* + * audio pcm capture open callback + */ +static int snd_cx23885_pcm_open(struct snd_pcm_substream *substream) +{ + struct cx23885_audio_dev *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + int err; + + if (!chip) { + printk(KERN_ERR "BUG: cx23885 can't find device struct." + " Can't proceed with open\n"); + return -ENODEV; + } + + err = snd_pcm_hw_constraint_pow2(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIODS); + if (err < 0) + goto _error; + + chip->substream = substream; + + runtime->hw = snd_cx23885_digital_hw; + + if (chip->dev->sram_channels[AUDIO_SRAM_CHANNEL].fifo_size != + DEFAULT_FIFO_SIZE) { + unsigned int bpl = chip->dev-> + sram_channels[AUDIO_SRAM_CHANNEL].fifo_size / 4; + bpl &= ~7; /* must be multiple of 8 */ + runtime->hw.period_bytes_min = bpl; + runtime->hw.period_bytes_max = bpl; + } + + return 0; +_error: + dprintk(1, "Error opening PCM!\n"); + return err; +} + +/* + * audio close callback + */ +static int snd_cx23885_close(struct snd_pcm_substream *substream) +{ + return 0; +} + +/* + * hw_params callback + */ +static int snd_cx23885_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct cx23885_audio_dev *chip = snd_pcm_substream_chip(substream); + struct videobuf_dmabuf *dma; + + struct cx23885_audio_buffer *buf; + int ret; + + if (substream->runtime->dma_area) { + dsp_buffer_free(chip); + substream->runtime->dma_area = NULL; + } + + chip->period_size = params_period_bytes(hw_params); + chip->num_periods = params_periods(hw_params); + chip->dma_size = chip->period_size * params_periods(hw_params); + + BUG_ON(!chip->dma_size); + BUG_ON(chip->num_periods & (chip->num_periods-1)); + + buf = kzalloc(sizeof(*buf), GFP_KERNEL); + if (NULL == buf) + return -ENOMEM; + + buf->bpl = chip->period_size; + + dma = &buf->dma; + videobuf_dma_init(dma); + ret = videobuf_dma_init_kernel(dma, PCI_DMA_FROMDEVICE, + (PAGE_ALIGN(chip->dma_size) >> PAGE_SHIFT)); + if (ret < 0) + goto error; + + ret = videobuf_dma_map(&chip->pci->dev, dma); + if (ret < 0) + goto error; + + ret = cx23885_risc_databuffer(chip->pci, &buf->risc, dma->sglist, + chip->period_size, chip->num_periods, 1); + if (ret < 0) + goto error; + + /* Loop back to start of program */ + buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP|RISC_IRQ1|RISC_CNT_INC); + buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma); + buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */ + + chip->buf = buf; + chip->dma_risc = dma; + + substream->runtime->dma_area = chip->dma_risc->vaddr; + substream->runtime->dma_bytes = chip->dma_size; + substream->runtime->dma_addr = 0; + + return 0; + +error: + kfree(buf); + return ret; +} + +/* + * hw free callback + */ +static int snd_cx23885_hw_free(struct snd_pcm_substream *substream) +{ + + struct cx23885_audio_dev *chip = snd_pcm_substream_chip(substream); + + if (substream->runtime->dma_area) { + dsp_buffer_free(chip); + substream->runtime->dma_area = NULL; + } + + return 0; +} + +/* + * prepare callback + */ +static int snd_cx23885_prepare(struct snd_pcm_substream *substream) +{ + return 0; +} + +/* + * trigger callback + */ +static int snd_cx23885_card_trigger(struct snd_pcm_substream *substream, + int cmd) +{ + struct cx23885_audio_dev *chip = snd_pcm_substream_chip(substream); + int err; + + /* Local interrupts are already disabled by ALSA */ + spin_lock(&chip->lock); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + err = cx23885_start_audio_dma(chip); + break; + case SNDRV_PCM_TRIGGER_STOP: + err = cx23885_stop_audio_dma(chip); + break; + default: + err = -EINVAL; + break; + } + + spin_unlock(&chip->lock); + + return err; +} + +/* + * pointer callback + */ +static snd_pcm_uframes_t snd_cx23885_pointer( + struct snd_pcm_substream *substream) +{ + struct cx23885_audio_dev *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + u16 count; + + count = atomic_read(&chip->count); + + return runtime->period_size * (count & (runtime->periods-1)); +} + +/* + * page callback (needed for mmap) + */ +static struct page *snd_cx23885_page(struct snd_pcm_substream *substream, + unsigned long offset) +{ + void *pageptr = substream->runtime->dma_area + offset; + return vmalloc_to_page(pageptr); +} + +/* + * operators + */ +static struct snd_pcm_ops snd_cx23885_pcm_ops = { + .open = snd_cx23885_pcm_open, + .close = snd_cx23885_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_cx23885_hw_params, + .hw_free = snd_cx23885_hw_free, + .prepare = snd_cx23885_prepare, + .trigger = snd_cx23885_card_trigger, + .pointer = snd_cx23885_pointer, + .page = snd_cx23885_page, +}; + +/* + * create a PCM device + */ +static int snd_cx23885_pcm(struct cx23885_audio_dev *chip, int device, + char *name) +{ + int err; + struct snd_pcm *pcm; + + err = snd_pcm_new(chip->card, name, device, 0, 1, &pcm); + if (err < 0) + return err; + pcm->private_data = chip; + strcpy(pcm->name, name); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cx23885_pcm_ops); + + return 0; +} + +/**************************************************************************** + Basic Flow for Sound Devices + ****************************************************************************/ + +/* + * Alsa Constructor - Component probe + */ + +struct cx23885_audio_dev *cx23885_audio_register(struct cx23885_dev *dev) +{ + struct snd_card *card; + struct cx23885_audio_dev *chip; + int err; + + if (disable_analog_audio) + return NULL; + + if (dev->sram_channels[AUDIO_SRAM_CHANNEL].cmds_start == 0) { + printk(KERN_WARNING "%s(): Missing SRAM channel configuration " + "for analog TV Audio\n", __func__); + return NULL; + } + + err = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, + THIS_MODULE, sizeof(struct cx23885_audio_dev), &card); + if (err < 0) + goto error; + + chip = (struct cx23885_audio_dev *) card->private_data; + chip->dev = dev; + chip->pci = dev->pci; + chip->card = card; + spin_lock_init(&chip->lock); + + snd_card_set_dev(card, &dev->pci->dev); + + err = snd_cx23885_pcm(chip, 0, "CX23885 Digital"); + if (err < 0) + goto error; + + strcpy(card->driver, "CX23885"); + sprintf(card->shortname, "Conexant CX23885"); + sprintf(card->longname, "%s at %s", card->shortname, dev->name); + + err = snd_card_register(card); + if (err < 0) + goto error; + + dprintk(0, "registered ALSA audio device\n"); + + return chip; + +error: + snd_card_free(card); + printk(KERN_ERR "%s(): Failed to register analog " + "audio adapter\n", __func__); + + return NULL; +} + +/* + * ALSA destructor + */ +void cx23885_audio_unregister(struct cx23885_dev *dev) +{ + struct cx23885_audio_dev *chip = dev->audio_dev; + + snd_card_free(chip->card); +} diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c index 76b7563de39..c3cf08945e4 100644 --- a/drivers/media/video/cx23885/cx23885-cards.c +++ b/drivers/media/video/cx23885/cx23885-cards.c @@ -25,8 +25,8 @@ #include <linux/delay.h> #include <media/cx25840.h> #include <linux/firmware.h> +#include <misc/altera.h> -#include "../../../staging/altera-stapl/altera.h" #include "cx23885.h" #include "tuner-xc2028.h" #include "netup-eeprom.h" @@ -106,12 +106,14 @@ struct cx23885_board cx23885_boards[] = { .vmux = CX25840_VIN7_CH3 | CX25840_VIN5_CH2 | CX25840_VIN2_CH1, + .amux = CX25840_AUDIO8, .gpio0 = 0, }, { .type = CX23885_VMUX_COMPOSITE1, .vmux = CX25840_VIN7_CH3 | CX25840_VIN4_CH2 | CX25840_VIN6_CH1, + .amux = CX25840_AUDIO7, .gpio0 = 0, }, { .type = CX23885_VMUX_SVIDEO, @@ -119,6 +121,7 @@ struct cx23885_board cx23885_boards[] = { CX25840_VIN4_CH2 | CX25840_VIN8_CH1 | CX25840_SVIDEO_ON, + .amux = CX25840_AUDIO7, .gpio0 = 0, } }, }, @@ -153,7 +156,30 @@ struct cx23885_board cx23885_boards[] = { }, [CX23885_BOARD_HAUPPAUGE_HVR1500] = { .name = "Hauppauge WinTV-HVR1500", + .porta = CX23885_ANALOG_VIDEO, .portc = CX23885_MPEG_DVB, + .tuner_type = TUNER_XC2028, + .tuner_addr = 0x61, /* 0xc2 >> 1 */ + .input = {{ + .type = CX23885_VMUX_TELEVISION, + .vmux = CX25840_VIN7_CH3 | + CX25840_VIN5_CH2 | + CX25840_VIN2_CH1, + .gpio0 = 0, + }, { + .type = CX23885_VMUX_COMPOSITE1, + .vmux = CX25840_VIN7_CH3 | + CX25840_VIN4_CH2 | + CX25840_VIN6_CH1, + .gpio0 = 0, + }, { + .type = CX23885_VMUX_SVIDEO, + .vmux = CX25840_VIN7_CH3 | + CX25840_VIN4_CH2 | + CX25840_VIN8_CH1 | + CX25840_SVIDEO_ON, + .gpio0 = 0, + } }, }, [CX23885_BOARD_HAUPPAUGE_HVR1200] = { .name = "Hauppauge WinTV-HVR1200", @@ -387,6 +413,31 @@ struct cx23885_board cx23885_boards[] = { .vmux = CX25840_COMPOSITE1, } }, }, + [CX23885_BOARD_MPX885] = { + .name = "MPX-885", + .porta = CX23885_ANALOG_VIDEO, + .input = {{ + .type = CX23885_VMUX_COMPOSITE1, + .vmux = CX25840_COMPOSITE1, + .amux = CX25840_AUDIO6, + .gpio0 = 0, + }, { + .type = CX23885_VMUX_COMPOSITE2, + .vmux = CX25840_COMPOSITE2, + .amux = CX25840_AUDIO6, + .gpio0 = 0, + }, { + .type = CX23885_VMUX_COMPOSITE3, + .vmux = CX25840_COMPOSITE3, + .amux = CX25840_AUDIO7, + .gpio0 = 0, + }, { + .type = CX23885_VMUX_COMPOSITE4, + .vmux = CX25840_COMPOSITE4, + .amux = CX25840_AUDIO7, + .gpio0 = 0, + } }, + }, }; const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards); @@ -1415,6 +1466,8 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1290: case CX23885_BOARD_LEADTEK_WINFAST_PXTV1200: case CX23885_BOARD_GOTVIEW_X5_3D_HYBRID: + case CX23885_BOARD_HAUPPAUGE_HVR1500: + case CX23885_BOARD_MPX885: dev->sd_cx25840 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_bus[2].i2c_adap, "cx25840", 0x88 >> 1, NULL); diff --git a/drivers/media/video/cx23885/cx23885-core.c b/drivers/media/video/cx23885/cx23885-core.c index ee41a8882f5..40e68b22015 100644 --- a/drivers/media/video/cx23885/cx23885-core.c +++ b/drivers/media/video/cx23885/cx23885-core.c @@ -54,7 +54,7 @@ MODULE_PARM_DESC(card, "card type"); #define dprintk(level, fmt, arg...)\ do { if (debug >= level)\ - printk(KERN_DEBUG "%s/0: " fmt, dev->name, ## arg);\ + printk(KERN_DEBUG "%s: " fmt, dev->name, ## arg);\ } while (0) static unsigned int cx23885_devcount; @@ -155,12 +155,12 @@ static struct sram_channel cx23885_sram_channels[] = { .cnt2_reg = DMA5_CNT2, }, [SRAM_CH07] = { - .name = "ch7", - .cmds_start = 0x0, - .ctrl_start = 0x0, - .cdt = 0x0, - .fifo_start = 0x0, - .fifo_size = 0x0, + .name = "TV Audio", + .cmds_start = 0x10190, + .ctrl_start = 0x10480, + .cdt = 0x10a00, + .fifo_start = 0x7000, + .fifo_size = 0x1000, .ptr1_reg = DMA6_PTR1, .ptr2_reg = DMA6_PTR2, .cnt1_reg = DMA6_CNT1, @@ -1082,10 +1082,10 @@ static void cx23885_dev_unregister(struct cx23885_dev *dev) static __le32 *cx23885_risc_field(__le32 *rp, struct scatterlist *sglist, unsigned int offset, u32 sync_line, unsigned int bpl, unsigned int padding, - unsigned int lines) + unsigned int lines, unsigned int lpi) { struct scatterlist *sg; - unsigned int line, todo; + unsigned int line, todo, sol; /* sync instruction */ if (sync_line != NO_SYNC_LINE) @@ -1098,16 +1098,22 @@ static __le32 *cx23885_risc_field(__le32 *rp, struct scatterlist *sglist, offset -= sg_dma_len(sg); sg++; } + + if (lpi && line > 0 && !(line % lpi)) + sol = RISC_SOL | RISC_IRQ1 | RISC_CNT_INC; + else + sol = RISC_SOL; + if (bpl <= sg_dma_len(sg)-offset) { /* fits into current chunk */ - *(rp++) = cpu_to_le32(RISC_WRITE|RISC_SOL|RISC_EOL|bpl); + *(rp++) = cpu_to_le32(RISC_WRITE|sol|RISC_EOL|bpl); *(rp++) = cpu_to_le32(sg_dma_address(sg)+offset); *(rp++) = cpu_to_le32(0); /* bits 63-32 */ offset += bpl; } else { /* scanline needs to be split */ todo = bpl; - *(rp++) = cpu_to_le32(RISC_WRITE|RISC_SOL| + *(rp++) = cpu_to_le32(RISC_WRITE|sol| (sg_dma_len(sg)-offset)); *(rp++) = cpu_to_le32(sg_dma_address(sg)+offset); *(rp++) = cpu_to_le32(0); /* bits 63-32 */ @@ -1164,10 +1170,10 @@ int cx23885_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc, rp = risc->cpu; if (UNSET != top_offset) rp = cx23885_risc_field(rp, sglist, top_offset, 0, - bpl, padding, lines); + bpl, padding, lines, 0); if (UNSET != bottom_offset) rp = cx23885_risc_field(rp, sglist, bottom_offset, 0x200, - bpl, padding, lines); + bpl, padding, lines, 0); /* save pointer to jmp instruction address */ risc->jmp = rp; @@ -1175,11 +1181,11 @@ int cx23885_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc, return 0; } -static int cx23885_risc_databuffer(struct pci_dev *pci, +int cx23885_risc_databuffer(struct pci_dev *pci, struct btcx_riscmem *risc, struct scatterlist *sglist, unsigned int bpl, - unsigned int lines) + unsigned int lines, unsigned int lpi) { u32 instructions; __le32 *rp; @@ -1199,7 +1205,55 @@ static int cx23885_risc_databuffer(struct pci_dev *pci, /* write risc instructions */ rp = risc->cpu; - rp = cx23885_risc_field(rp, sglist, 0, NO_SYNC_LINE, bpl, 0, lines); + rp = cx23885_risc_field(rp, sglist, 0, NO_SYNC_LINE, + bpl, 0, lines, lpi); + + /* save pointer to jmp instruction address */ + risc->jmp = rp; + BUG_ON((risc->jmp - risc->cpu + 2) * sizeof(*risc->cpu) > risc->size); + return 0; +} + +int cx23885_risc_vbibuffer(struct pci_dev *pci, struct btcx_riscmem *risc, + struct scatterlist *sglist, unsigned int top_offset, + unsigned int bottom_offset, unsigned int bpl, + unsigned int padding, unsigned int lines) +{ + u32 instructions, fields; + __le32 *rp; + int rc; + + fields = 0; + if (UNSET != top_offset) + fields++; + if (UNSET != bottom_offset) + fields++; + + /* estimate risc mem: worst case is one write per page border + + one write per scan line + syncs + jump (all 2 dwords). Padding + can cause next bpl to start close to a page border. First DMA + region may be smaller than PAGE_SIZE */ + /* write and jump need and extra dword */ + instructions = fields * (1 + ((bpl + padding) * lines) + / PAGE_SIZE + lines); + instructions += 2; + rc = btcx_riscmem_alloc(pci, risc, instructions*12); + if (rc < 0) + return rc; + /* write risc instructions */ + rp = risc->cpu; + + /* Sync to line 6, so US CC line 21 will appear in line '12' + * in the userland vbi payload */ + if (UNSET != top_offset) + rp = cx23885_risc_field(rp, sglist, top_offset, 6, + bpl, padding, lines, 0); + + if (UNSET != bottom_offset) + rp = cx23885_risc_field(rp, sglist, bottom_offset, 0x207, + bpl, padding, lines, 0); + + /* save pointer to jmp instruction address */ risc->jmp = rp; @@ -1207,6 +1261,7 @@ static int cx23885_risc_databuffer(struct pci_dev *pci, return 0; } + int cx23885_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc, u32 reg, u32 mask, u32 value) { @@ -1517,7 +1572,7 @@ int cx23885_buf_prepare(struct videobuf_queue *q, struct cx23885_tsport *port, goto fail; cx23885_risc_databuffer(dev->pci, &buf->risc, videobuf_to_dma(&buf->vb)->sglist, - buf->vb.width, buf->vb.height); + buf->vb.width, buf->vb.height, 0); } buf->vb.state = VIDEOBUF_PREPARED; return 0; @@ -1741,15 +1796,19 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id) struct cx23885_tsport *ts2 = &dev->ts2; u32 pci_status, pci_mask; u32 vida_status, vida_mask; + u32 audint_status, audint_mask; u32 ts1_status, ts1_mask; u32 ts2_status, ts2_mask; int vida_count = 0, ts1_count = 0, ts2_count = 0, handled = 0; + int audint_count = 0; bool subdev_handled; pci_status = cx_read(PCI_INT_STAT); pci_mask = cx23885_irq_get_mask(dev); vida_status = cx_read(VID_A_INT_STAT); vida_mask = cx_read(VID_A_INT_MSK); + audint_status = cx_read(AUDIO_INT_INT_STAT); + audint_mask = cx_read(AUDIO_INT_INT_MSK); ts1_status = cx_read(VID_B_INT_STAT); ts1_mask = cx_read(VID_B_INT_MSK); ts2_status = cx_read(VID_C_INT_STAT); @@ -1759,12 +1818,15 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id) goto out; vida_count = cx_read(VID_A_GPCNT); + audint_count = cx_read(AUD_INT_A_GPCNT); ts1_count = cx_read(ts1->reg_gpcnt); ts2_count = cx_read(ts2->reg_gpcnt); dprintk(7, "pci_status: 0x%08x pci_mask: 0x%08x\n", pci_status, pci_mask); dprintk(7, "vida_status: 0x%08x vida_mask: 0x%08x count: 0x%x\n", vida_status, vida_mask, vida_count); + dprintk(7, "audint_status: 0x%08x audint_mask: 0x%08x count: 0x%x\n", + audint_status, audint_mask, audint_count); dprintk(7, "ts1_status: 0x%08x ts1_mask: 0x%08x count: 0x%x\n", ts1_status, ts1_mask, ts1_count); dprintk(7, "ts2_status: 0x%08x ts2_mask: 0x%08x count: 0x%x\n", @@ -1861,6 +1923,9 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id) if (vida_status) handled += cx23885_video_irq(dev, vida_status); + if (audint_status) + handled += cx23885_audio_irq(dev, audint_status, audint_mask); + if (pci_status & PCI_MSK_IR) { subdev_handled = false; v4l2_subdev_call(dev->sd_ir, core, interrupt_service_routine, diff --git a/drivers/media/video/cx23885/cx23885-dvb.c b/drivers/media/video/cx23885/cx23885-dvb.c index aa83f07b1b0..bcb45be44bb 100644 --- a/drivers/media/video/cx23885/cx23885-dvb.c +++ b/drivers/media/video/cx23885/cx23885-dvb.c @@ -844,7 +844,7 @@ static int dvb_register(struct cx23885_tsport *port) static struct xc2028_ctrl ctl = { .fname = XC3028L_DEFAULT_FIRMWARE, .max_len = 64, - .demod = 5000, + .demod = XC3028_FE_DIBCOM52, /* This is true for all demods with v36 firmware? */ .type = XC2028_D2633, diff --git a/drivers/media/video/cx23885/cx23885-i2c.c b/drivers/media/video/cx23885/cx23885-i2c.c index 307ff543c25..0ff7a9e98f3 100644 --- a/drivers/media/video/cx23885/cx23885-i2c.c +++ b/drivers/media/video/cx23885/cx23885-i2c.c @@ -287,6 +287,7 @@ static char *i2c_devs[128] = { [0x32 >> 1] = "cx24227", [0x88 >> 1] = "cx25837", [0x84 >> 1] = "tda8295", + [0x98 >> 1] = "flatiron", [0xa0 >> 1] = "eeprom", [0xc0 >> 1] = "tuner/mt2131/tda8275", [0xc2 >> 1] = "tuner/mt2131/tda8275/xc5000/xc3028", diff --git a/drivers/media/video/cx23885/cx23885-reg.h b/drivers/media/video/cx23885/cx23885-reg.h index c87ac682ebb..a99936e0cbc 100644 --- a/drivers/media/video/cx23885/cx23885-reg.h +++ b/drivers/media/video/cx23885/cx23885-reg.h @@ -203,6 +203,7 @@ Channel manager Data Structure entry = 20 DWORD #define SD2_BIAS_CTRL 0x0000000A #define AMP_BIAS_CTRL 0x0000000C #define CH_PWR_CTRL1 0x0000000E +#define FLD_CH_SEL (1 << 3) #define CH_PWR_CTRL2 0x0000000F #define DSM_STATUS1 0x00000010 #define DSM_STATUS2 0x00000011 @@ -271,7 +272,9 @@ Channel manager Data Structure entry = 20 DWORD #define VID_BC_MSK_OPC_ERR (1 << 16) #define VID_BC_MSK_SYNC (1 << 12) #define VID_BC_MSK_OF (1 << 8) +#define VID_BC_MSK_VBI_RISCI2 (1 << 5) #define VID_BC_MSK_RISCI2 (1 << 4) +#define VID_BC_MSK_VBI_RISCI1 (1 << 1) #define VID_BC_MSK_RISCI1 1 #define VID_C_INT_MSK 0x00040040 diff --git a/drivers/media/video/cx23885/cx23885-vbi.c b/drivers/media/video/cx23885/cx23885-vbi.c index c0b60382ad1..a1154f035bc 100644 --- a/drivers/media/video/cx23885/cx23885-vbi.c +++ b/drivers/media/video/cx23885/cx23885-vbi.c @@ -41,6 +41,12 @@ MODULE_PARM_DESC(vbi_debug, "enable debug messages [vbi]"); /* ------------------------------------------------------------------ */ +#define VBI_LINE_LENGTH 1440 +#define NTSC_VBI_START_LINE 10 /* line 10 - 21 */ +#define NTSC_VBI_END_LINE 21 +#define NTSC_VBI_LINES (NTSC_VBI_END_LINE - NTSC_VBI_START_LINE + 1) + + int cx23885_vbi_fmt(struct file *file, void *priv, struct v4l2_format *f) { @@ -49,43 +55,86 @@ int cx23885_vbi_fmt(struct file *file, void *priv, if (dev->tvnorm & V4L2_STD_525_60) { /* ntsc */ - f->fmt.vbi.sampling_rate = 28636363; + f->fmt.vbi.samples_per_line = VBI_LINE_LENGTH; + f->fmt.vbi.sampling_rate = 27000000; + f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; + f->fmt.vbi.offset = 0; + f->fmt.vbi.flags = 0; f->fmt.vbi.start[0] = 10; - f->fmt.vbi.start[1] = 273; - + f->fmt.vbi.count[0] = 17; + f->fmt.vbi.start[1] = 263 + 10 + 1; + f->fmt.vbi.count[1] = 17; } else if (dev->tvnorm & V4L2_STD_625_50) { /* pal */ f->fmt.vbi.sampling_rate = 35468950; f->fmt.vbi.start[0] = 7 - 1; f->fmt.vbi.start[1] = 319 - 1; } + return 0; } +/* We're given the Video Interrupt status register. + * The cx23885_video_irq() func has already validated + * the potential error bits, we just need to + * deal with vbi payload and return indication if + * we actually processed any payload. + */ +int cx23885_vbi_irq(struct cx23885_dev *dev, u32 status) +{ + u32 count; + int handled = 0; + + if (status & VID_BC_MSK_VBI_RISCI1) { + dprintk(1, "%s() VID_BC_MSK_VBI_RISCI1\n", __func__); + spin_lock(&dev->slock); + count = cx_read(VID_A_GPCNT); + cx23885_video_wakeup(dev, &dev->vbiq, count); + spin_unlock(&dev->slock); + handled++; + } + + if (status & VID_BC_MSK_VBI_RISCI2) { + dprintk(1, "%s() VID_BC_MSK_VBI_RISCI2\n", __func__); + dprintk(2, "stopper vbi\n"); + spin_lock(&dev->slock); + cx23885_restart_vbi_queue(dev, &dev->vbiq); + spin_unlock(&dev->slock); + handled++; + } + + return handled; +} + static int cx23885_start_vbi_dma(struct cx23885_dev *dev, struct cx23885_dmaqueue *q, struct cx23885_buffer *buf) { + dprintk(1, "%s()\n", __func__); + /* setup fifo + format */ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH02], buf->vb.width, buf->risc.dma); /* reset counter */ + cx_write(VID_A_GPCNT_CTL, 3); + cx_write(VID_A_VBI_CTRL, 3); + cx_write(VBI_A_GPCNT_CTL, 3); q->count = 1; - /* enable irqs */ + /* enable irq */ cx23885_irq_add_enable(dev, 0x01); cx_set(VID_A_INT_MSK, 0x000022); /* start dma */ cx_set(DEV_CNTRL2, (1<<5)); - cx_set(VID_A_DMA_CTL, 0x00000022); + cx_set(VID_A_DMA_CTL, 0x22); /* FIFO and RISC enable */ return 0; } -static int cx23885_restart_vbi_queue(struct cx23885_dev *dev, +int cx23885_restart_vbi_queue(struct cx23885_dev *dev, struct cx23885_dmaqueue *q) { struct cx23885_buffer *buf; @@ -102,7 +151,7 @@ static int cx23885_restart_vbi_queue(struct cx23885_dev *dev, buf = list_entry(item, struct cx23885_buffer, vb.queue); buf->count = q->count++; } - mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT); + mod_timer(&q->timeout, jiffies + (BUFFER_TIMEOUT / 30)); return 0; } @@ -113,8 +162,7 @@ void cx23885_vbi_timeout(unsigned long data) struct cx23885_buffer *buf; unsigned long flags; - cx23885_sram_channel_dump(dev, &dev->sram_channels[SRAM_CH02]); - + /* Stop the VBI engine */ cx_clear(VID_A_DMA_CTL, 0x22); spin_lock_irqsave(&dev->slock, flags); @@ -132,7 +180,7 @@ void cx23885_vbi_timeout(unsigned long data) } /* ------------------------------------------------------------------ */ -#define VBI_LINE_LENGTH 2048 +#define VBI_LINE_LENGTH 1440 #define VBI_LINE_COUNT 17 static int @@ -173,7 +221,7 @@ vbi_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, rc = videobuf_iolock(q, &buf->vb, NULL); if (0 != rc) goto fail; - cx23885_risc_buffer(dev->pci, &buf->risc, + cx23885_risc_vbibuffer(dev->pci, &buf->risc, dma->sglist, 0, buf->vb.width * buf->vb.height, buf->vb.width, 0, @@ -207,7 +255,7 @@ vbi_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) cx23885_start_vbi_dma(dev, q, buf); buf->vb.state = VIDEOBUF_ACTIVE; buf->count = q->count++; - mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT); + mod_timer(&q->timeout, jiffies + (BUFFER_TIMEOUT / 30)); dprintk(2, "[%p/%d] vbi_queue - first active\n", buf, buf->vb.i); diff --git a/drivers/media/video/cx23885/cx23885-video.c b/drivers/media/video/cx23885/cx23885-video.c index 896bb32dbf0..e730b926301 100644 --- a/drivers/media/video/cx23885/cx23885-video.c +++ b/drivers/media/video/cx23885/cx23885-video.c @@ -37,6 +37,8 @@ #include "cx23885-ioctl.h" #include "tuner-xc2028.h" +#include <media/cx25840.h> + MODULE_DESCRIPTION("v4l2 driver module for cx23885 based TV cards"); MODULE_AUTHOR("Steven Toth <stoth@linuxtv.org>"); MODULE_LICENSE("GPL"); @@ -69,14 +71,14 @@ MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes"); #define dprintk(level, fmt, arg...)\ do { if (video_debug >= level)\ - printk(KERN_DEBUG "%s/0: " fmt, dev->name, ## arg);\ + printk(KERN_DEBUG "%s: " fmt, dev->name, ## arg);\ } while (0) /* ------------------------------------------------------------------- */ /* static data */ #define FORMAT_FLAGS_PACKED 0x01 - +#if 0 static struct cx23885_fmt formats[] = { { .name = "8 bpp, gray", @@ -130,6 +132,23 @@ static struct cx23885_fmt formats[] = { .flags = FORMAT_FLAGS_PACKED, }, }; +#else +static struct cx23885_fmt formats[] = { + { +#if 0 + .name = "4:2:2, packed, UYVY", + .fourcc = V4L2_PIX_FMT_UYVY, + .depth = 16, + .flags = FORMAT_FLAGS_PACKED, + }, { +#endif + .name = "4:2:2, packed, YUYV", + .fourcc = V4L2_PIX_FMT_YUYV, + .depth = 16, + .flags = FORMAT_FLAGS_PACKED, + } +}; +#endif static struct cx23885_fmt *format_by_fourcc(unsigned int fourcc) { @@ -139,7 +158,12 @@ static struct cx23885_fmt *format_by_fourcc(unsigned int fourcc) if (formats[i].fourcc == fourcc) return formats+i; - printk(KERN_ERR "%s(0x%08x) NOT FOUND\n", __func__, fourcc); + printk(KERN_ERR "%s(%c%c%c%c) NOT FOUND\n", __func__, + (fourcc & 0xff), + ((fourcc >> 8) & 0xff), + ((fourcc >> 16) & 0xff), + ((fourcc >> 24) & 0xff) + ); return NULL; } @@ -171,7 +195,7 @@ static struct cx23885_ctrl cx23885_ctls[] = { .id = V4L2_CID_CONTRAST, .name = "Contrast", .minimum = 0, - .maximum = 0xff, + .maximum = 0x7f, .step = 1, .default_value = 0x3f, .type = V4L2_CTRL_TYPE_INTEGER, @@ -184,10 +208,10 @@ static struct cx23885_ctrl cx23885_ctls[] = { .v = { .id = V4L2_CID_HUE, .name = "Hue", - .minimum = 0, - .maximum = 0xff, + .minimum = -127, + .maximum = 128, .step = 1, - .default_value = 0x7f, + .default_value = 0x0, .type = V4L2_CTRL_TYPE_INTEGER, }, .off = 128, @@ -202,9 +226,9 @@ static struct cx23885_ctrl cx23885_ctls[] = { .id = V4L2_CID_SATURATION, .name = "Saturation", .minimum = 0, - .maximum = 0xff, + .maximum = 0x7f, .step = 1, - .default_value = 0x7f, + .default_value = 0x3f, .type = V4L2_CTRL_TYPE_INTEGER, }, .off = 0, @@ -258,8 +282,8 @@ static const u32 *ctrl_classes[] = { NULL }; -static void cx23885_video_wakeup(struct cx23885_dev *dev, - struct cx23885_dmaqueue *q, u32 count) +void cx23885_video_wakeup(struct cx23885_dev *dev, + struct cx23885_dmaqueue *q, u32 count) { struct cx23885_buffer *buf; int bc; @@ -393,6 +417,71 @@ static void res_free(struct cx23885_dev *dev, struct cx23885_fh *fh, mutex_unlock(&dev->lock); } +static int cx23885_flatiron_write(struct cx23885_dev *dev, u8 reg, u8 data) +{ + /* 8 bit registers, 8 bit values */ + u8 buf[] = { reg, data }; + + struct i2c_msg msg = { .addr = 0x98 >> 1, + .flags = 0, .buf = buf, .len = 2 }; + + return i2c_transfer(&dev->i2c_bus[2].i2c_adap, &msg, 1); +} + +static u8 cx23885_flatiron_read(struct cx23885_dev *dev, u8 reg) +{ + /* 8 bit registers, 8 bit values */ + int ret; + u8 b0[] = { reg }; + u8 b1[] = { 0 }; + + struct i2c_msg msg[] = { + { .addr = 0x98 >> 1, .flags = 0, .buf = b0, .len = 1 }, + { .addr = 0x98 >> 1, .flags = I2C_M_RD, .buf = b1, .len = 1 } + }; + + ret = i2c_transfer(&dev->i2c_bus[2].i2c_adap, &msg[0], 2); + if (ret != 2) + printk(KERN_ERR "%s() error\n", __func__); + + return b1[0]; +} + +static void cx23885_flatiron_dump(struct cx23885_dev *dev) +{ + int i; + dprintk(1, "Flatiron dump\n"); + for (i = 0; i < 0x24; i++) { + dprintk(1, "FI[%02x] = %02x\n", i, + cx23885_flatiron_read(dev, i)); + } +} + +static int cx23885_flatiron_mux(struct cx23885_dev *dev, int input) +{ + u8 val; + dprintk(1, "%s(input = %d)\n", __func__, input); + + if (input == 1) + val = cx23885_flatiron_read(dev, CH_PWR_CTRL1) & ~FLD_CH_SEL; + else if (input == 2) + val = cx23885_flatiron_read(dev, CH_PWR_CTRL1) | FLD_CH_SEL; + else + return -EINVAL; + + val |= 0x20; /* Enable clock to delta-sigma and dec filter */ + + cx23885_flatiron_write(dev, CH_PWR_CTRL1, val); + + /* Wake up */ + cx23885_flatiron_write(dev, CH_PWR_CTRL2, 0); + + if (video_debug) + cx23885_flatiron_dump(dev); + + return 0; +} + static int cx23885_video_mux(struct cx23885_dev *dev, unsigned int input) { dprintk(1, "%s() video_mux: %d [vmux=%d, gpio=0x%x,0x%x,0x%x,0x%x]\n", @@ -413,27 +502,59 @@ static int cx23885_video_mux(struct cx23885_dev *dev, unsigned int input) v4l2_subdev_call(dev->sd_cx25840, video, s_routing, INPUT(input)->vmux, 0, 0); + if ((dev->board == CX23885_BOARD_HAUPPAUGE_HVR1800) || + (dev->board == CX23885_BOARD_MPX885)) { + /* Configure audio routing */ + v4l2_subdev_call(dev->sd_cx25840, audio, s_routing, + INPUT(input)->amux, 0, 0); + + if (INPUT(input)->amux == CX25840_AUDIO7) + cx23885_flatiron_mux(dev, 1); + else if (INPUT(input)->amux == CX25840_AUDIO6) + cx23885_flatiron_mux(dev, 2); + } + return 0; } -/* ------------------------------------------------------------------ */ -static int cx23885_set_scale(struct cx23885_dev *dev, unsigned int width, - unsigned int height, enum v4l2_field field) +static int cx23885_audio_mux(struct cx23885_dev *dev, unsigned int input) { - dprintk(1, "%s()\n", __func__); + dprintk(1, "%s(input=%d)\n", __func__, input); + + /* The baseband video core of the cx23885 has two audio inputs. + * LR1 and LR2. In almost every single case so far only HVR1xxx + * cards we've only ever supported LR1. Time to support LR2, + * which is available via the optional white breakout header on + * the board. + * We'll use a could of existing enums in the card struct to allow + * devs to specify which baseband input they need, or just default + * to what we've always used. + */ + if (INPUT(input)->amux == CX25840_AUDIO7) + cx23885_flatiron_mux(dev, 1); + else if (INPUT(input)->amux == CX25840_AUDIO6) + cx23885_flatiron_mux(dev, 2); + else { + /* Not specifically defined, assume the default. */ + cx23885_flatiron_mux(dev, 1); + } + return 0; } +/* ------------------------------------------------------------------ */ static int cx23885_start_video_dma(struct cx23885_dev *dev, struct cx23885_dmaqueue *q, struct cx23885_buffer *buf) { dprintk(1, "%s()\n", __func__); + /* Stop the dma/fifo before we tamper with it's risc programs */ + cx_clear(VID_A_DMA_CTL, 0x11); + /* setup fifo + format */ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH01], buf->bpl, buf->risc.dma); - cx23885_set_scale(dev, buf->vb.width, buf->vb.height, buf->vb.field); /* reset counter */ cx_write(VID_A_GPCNT_CTL, 3); @@ -748,7 +869,7 @@ static int video_open(struct file *file) fh->type = type; fh->width = 320; fh->height = 240; - fh->fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24); + fh->fmt = format_by_fourcc(V4L2_PIX_FMT_YUYV); videobuf_queue_sg_init(&fh->vidq, &cx23885_video_qops, &dev->pci->dev, &dev->slock, @@ -757,6 +878,14 @@ static int video_open(struct file *file) sizeof(struct cx23885_buffer), fh, NULL); + videobuf_queue_sg_init(&fh->vbiq, &cx23885_vbi_qops, + &dev->pci->dev, &dev->slock, + V4L2_BUF_TYPE_VBI_CAPTURE, + V4L2_FIELD_SEQ_TB, + sizeof(struct cx23885_buffer), + fh, NULL); + + dprintk(1, "post videobuf_queue_init()\n"); return 0; @@ -884,8 +1013,9 @@ static int cx23885_get_control(struct cx23885_dev *dev, static int cx23885_set_control(struct cx23885_dev *dev, struct v4l2_control *ctl) { - dprintk(1, "%s() calling cx25840(VIDIOC_S_CTRL)" - " (disabled - no action)\n", __func__); + dprintk(1, "%s() calling cx25840(VIDIOC_S_CTRL)\n", __func__); + call_all(dev, core, s_ctrl, ctl); + return 0; } @@ -1059,13 +1189,22 @@ static int vidioc_streamon(struct file *file, void *priv, struct cx23885_dev *dev = fh->dev; dprintk(1, "%s()\n", __func__); - if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)) + if ((fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && + (fh->type != V4L2_BUF_TYPE_VBI_CAPTURE)) return -EINVAL; if (unlikely(i != fh->type)) return -EINVAL; if (unlikely(!res_get(dev, fh, get_resource(fh)))) return -EBUSY; + + /* Don't start VBI streaming unless vida streaming + * has already started. + */ + if ((fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) && + ((cx_read(VID_A_DMA_CTL) & 0x11) == 0)) + return -EINVAL; + return videobuf_streamon(get_queue(fh)); } @@ -1076,7 +1215,8 @@ static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) int err, res; dprintk(1, "%s()\n", __func__); - if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + if ((fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && + (fh->type != V4L2_BUF_TYPE_VBI_CAPTURE)) return -EINVAL; if (i != fh->type) return -EINVAL; @@ -1119,7 +1259,7 @@ static int cx23885_enum_input(struct cx23885_dev *dev, struct v4l2_input *i) dprintk(1, "%s()\n", __func__); n = i->index; - if (n >= 4) + if (n >= MAX_CX23885_INPUT) return -EINVAL; if (0 == INPUT(n)->type) @@ -1133,6 +1273,11 @@ static int cx23885_enum_input(struct cx23885_dev *dev, struct v4l2_input *i) i->type = V4L2_INPUT_TYPE_TUNER; i->std = CX23885_NORMS; } + + /* Two selectable audio inputs for non-tv inputs */ + if (INPUT(n)->type != CX23885_VMUX_TELEVISION) + i->audioset = 0x3; + return 0; } @@ -1159,13 +1304,20 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i) dprintk(1, "%s(%d)\n", __func__, i); - if (i >= 4) { + if (i >= MAX_CX23885_INPUT) { dprintk(1, "%s() -EINVAL\n", __func__); return -EINVAL; } + if (INPUT(i)->type == 0) + return -EINVAL; + mutex_lock(&dev->lock); cx23885_video_mux(dev, i); + + /* By default establish the default audio input for the card also */ + /* Caller is free to use VIDIOC_S_AUDIO to override afterwards */ + cx23885_audio_mux(dev, i); mutex_unlock(&dev->lock); return 0; } @@ -1185,6 +1337,64 @@ static int vidioc_log_status(struct file *file, void *priv) return 0; } +static int cx23885_query_audinput(struct file *file, void *priv, + struct v4l2_audio *i) +{ + struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; + static const char *iname[] = { + [0] = "Baseband L/R 1", + [1] = "Baseband L/R 2", + }; + unsigned int n; + dprintk(1, "%s()\n", __func__); + + n = i->index; + if (n >= 2) + return -EINVAL; + + memset(i, 0, sizeof(*i)); + i->index = n; + strcpy(i->name, iname[n]); + i->capability = V4L2_AUDCAP_STEREO; + i->mode = V4L2_AUDMODE_AVL; + return 0; + +} + +static int vidioc_enum_audinput(struct file *file, void *priv, + struct v4l2_audio *i) +{ + return cx23885_query_audinput(file, priv, i); +} + +static int vidioc_g_audinput(struct file *file, void *priv, + struct v4l2_audio *i) +{ + struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; + + i->index = dev->audinput; + dprintk(1, "%s(input=%d)\n", __func__, i->index); + + return cx23885_query_audinput(file, priv, i); +} + +static int vidioc_s_audinput(struct file *file, void *priv, + struct v4l2_audio *i) +{ + struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; + if (i->index >= 2) + return -EINVAL; + + dprintk(1, "%s(%d)\n", __func__, i->index); + + dev->audinput = i->index; + + /* Skip the audio defaults from the cards struct, caller wants + * directly touch the audio mux hardware. */ + cx23885_flatiron_mux(dev, dev->audinput + 1); + return 0; +} + static int vidioc_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *qctrl) { @@ -1221,10 +1431,8 @@ static int vidioc_g_tuner(struct file *file, void *priv, return -EINVAL; strcpy(t->name, "Television"); - t->type = V4L2_TUNER_ANALOG_TV; - t->capability = V4L2_TUNER_CAP_NORM; - t->rangehigh = 0xffffffffUL; - t->signal = 0xffff ; /* LOCKED */ + + call_all(dev, tuner, g_tuner, t); return 0; } @@ -1237,6 +1445,9 @@ static int vidioc_s_tuner(struct file *file, void *priv, return -EINVAL; if (0 != t->index) return -EINVAL; + /* Update the A/V core */ + call_all(dev, tuner, s_tuner, t); + return 0; } @@ -1302,10 +1513,6 @@ static void cx23885_vid_timeout(unsigned long data) struct cx23885_buffer *buf; unsigned long flags; - cx23885_sram_channel_dump(dev, &dev->sram_channels[SRAM_CH01]); - - cx_clear(VID_A_DMA_CTL, 0x11); - spin_lock_irqsave(&dev->slock, flags); while (!list_empty(&q->active)) { buf = list_entry(q->active.next, @@ -1313,7 +1520,7 @@ static void cx23885_vid_timeout(unsigned long data) list_del(&buf->vb.queue); buf->vb.state = VIDEOBUF_ERROR; wake_up(&buf->vb.done); - printk(KERN_ERR "%s/0: [%p/%d] timeout - dma=0x%08lx\n", + printk(KERN_ERR "%s: [%p/%d] timeout - dma=0x%08lx\n", dev->name, buf, buf->vb.i, (unsigned long)buf->risc.dma); } @@ -1329,27 +1536,43 @@ int cx23885_video_irq(struct cx23885_dev *dev, u32 status) mask = cx_read(VID_A_INT_MSK); if (0 == (status & mask)) return handled; + cx_write(VID_A_INT_STAT, status); - dprintk(2, "%s() status = 0x%08x\n", __func__, status); - /* risc op code error */ - if (status & (1 << 16)) { - printk(KERN_WARNING "%s/0: video risc op code error\n", - dev->name); - cx_clear(VID_A_DMA_CTL, 0x11); - cx23885_sram_channel_dump(dev, &dev->sram_channels[SRAM_CH01]); + /* risc op code error, fifo overflow or line sync detection error */ + if ((status & VID_BC_MSK_OPC_ERR) || + (status & VID_BC_MSK_SYNC) || + (status & VID_BC_MSK_OF)) { + + if (status & VID_BC_MSK_OPC_ERR) { + dprintk(7, " (VID_BC_MSK_OPC_ERR 0x%08x)\n", + VID_BC_MSK_OPC_ERR); + printk(KERN_WARNING "%s: video risc op code error\n", + dev->name); + cx23885_sram_channel_dump(dev, + &dev->sram_channels[SRAM_CH01]); + } + + if (status & VID_BC_MSK_SYNC) + dprintk(7, " (VID_BC_MSK_SYNC 0x%08x) " + "video lines miss-match\n", + VID_BC_MSK_SYNC); + + if (status & VID_BC_MSK_OF) + dprintk(7, " (VID_BC_MSK_OF 0x%08x) fifo overflow\n", + VID_BC_MSK_OF); + } - /* risc1 y */ - if (status & 0x01) { + /* Video */ + if (status & VID_BC_MSK_RISCI1) { spin_lock(&dev->slock); count = cx_read(VID_A_GPCNT); cx23885_video_wakeup(dev, &dev->vidq, count); spin_unlock(&dev->slock); handled++; } - /* risc2 y */ - if (status & 0x10) { + if (status & VID_BC_MSK_RISCI2) { dprintk(2, "stopper video\n"); spin_lock(&dev->slock); cx23885_restart_video_queue(dev, &dev->vidq); @@ -1357,6 +1580,9 @@ int cx23885_video_irq(struct cx23885_dev *dev, u32 status) handled++; } + /* Allow the VBI framework to process it's payload */ + handled += cx23885_vbi_irq(dev, status); + return handled; } @@ -1405,6 +1631,9 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_g_register = cx23885_g_register, .vidioc_s_register = cx23885_s_register, #endif + .vidioc_enumaudio = vidioc_enum_audinput, + .vidioc_g_audio = vidioc_g_audinput, + .vidioc_s_audio = vidioc_s_audinput, }; static struct video_device cx23885_vbi_template; @@ -1429,6 +1658,14 @@ void cx23885_video_unregister(struct cx23885_dev *dev) dprintk(1, "%s()\n", __func__); cx23885_irq_remove(dev, 0x01); + if (dev->vbi_dev) { + if (video_is_registered(dev->vbi_dev)) + video_unregister_device(dev->vbi_dev); + else + video_device_release(dev->vbi_dev); + dev->vbi_dev = NULL; + btcx_riscmem_free(dev->pci, &dev->vbiq.stopper); + } if (dev->video_dev) { if (video_is_registered(dev->video_dev)) video_unregister_device(dev->video_dev); @@ -1438,6 +1675,9 @@ void cx23885_video_unregister(struct cx23885_dev *dev) btcx_riscmem_free(dev->pci, &dev->vidq.stopper); } + + if (dev->audio_dev) + cx23885_audio_unregister(dev); } int cx23885_video_register(struct cx23885_dev *dev) @@ -1463,7 +1703,14 @@ int cx23885_video_register(struct cx23885_dev *dev) cx23885_risc_stopper(dev->pci, &dev->vidq.stopper, VID_A_DMA_CTL, 0x11, 0x00); - /* Don't enable VBI yet */ + /* init vbi dma queues */ + INIT_LIST_HEAD(&dev->vbiq.active); + INIT_LIST_HEAD(&dev->vbiq.queued); + dev->vbiq.timeout.function = cx23885_vbi_timeout; + dev->vbiq.timeout.data = (unsigned long)dev; + init_timer(&dev->vbiq.timeout); + cx23885_risc_stopper(dev->pci, &dev->vbiq.stopper, + VID_A_DMA_CTL, 0x22, 0x00); cx23885_irq_add_enable(dev, 0x01); @@ -1504,8 +1751,7 @@ int cx23885_video_register(struct cx23885_dev *dev) } } - - /* register v4l devices */ + /* register Video device */ dev->video_dev = cx23885_vdev_init(dev, dev->pci, &cx23885_video_template, "video"); err = video_register_device(dev->video_dev, VFL_TYPE_GRABBER, @@ -1515,13 +1761,31 @@ int cx23885_video_register(struct cx23885_dev *dev) dev->name); goto fail_unreg; } - printk(KERN_INFO "%s/0: registered device %s [v4l2]\n", + printk(KERN_INFO "%s: registered device %s [v4l2]\n", dev->name, video_device_node_name(dev->video_dev)); + + /* register VBI device */ + dev->vbi_dev = cx23885_vdev_init(dev, dev->pci, + &cx23885_vbi_template, "vbi"); + err = video_register_device(dev->vbi_dev, VFL_TYPE_VBI, + vbi_nr[dev->nr]); + if (err < 0) { + printk(KERN_INFO "%s: can't register vbi device\n", + dev->name); + goto fail_unreg; + } + printk(KERN_INFO "%s: registered device %s\n", + dev->name, video_device_node_name(dev->vbi_dev)); + + /* Register ALSA audio device */ + dev->audio_dev = cx23885_audio_register(dev); + /* initial device configuration */ mutex_lock(&dev->lock); cx23885_set_tvnorm(dev, dev->tvnorm); init_controls(dev); cx23885_video_mux(dev, 0); + cx23885_audio_mux(dev, 0); mutex_unlock(&dev->lock); return 0; diff --git a/drivers/media/video/cx23885/cx23885.h b/drivers/media/video/cx23885/cx23885.h index d86bc0b1317..b49036fe3ff 100644 --- a/drivers/media/video/cx23885/cx23885.h +++ b/drivers/media/video/cx23885/cx23885.h @@ -86,6 +86,7 @@ #define CX23885_BOARD_GOTVIEW_X5_3D_HYBRID 29 #define CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF 30 #define CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000 31 +#define CX23885_BOARD_MPX885 32 #define GPIO_0 0x00000001 #define GPIO_1 0x00000002 @@ -192,6 +193,7 @@ struct cx23885_buffer { struct cx23885_input { enum cx23885_itype type; unsigned int vmux; + unsigned int amux; u32 gpio0, gpio1, gpio2, gpio3; }; @@ -318,6 +320,34 @@ struct cx23885_kernel_ir { struct rc_dev *rc; }; +struct cx23885_audio_buffer { + unsigned int bpl; + struct btcx_riscmem risc; + struct videobuf_dmabuf dma; +}; + +struct cx23885_audio_dev { + struct cx23885_dev *dev; + + struct pci_dev *pci; + + struct snd_card *card; + + spinlock_t lock; + + atomic_t count; + + unsigned int dma_size; + unsigned int period_size; + unsigned int num_periods; + + struct videobuf_dmabuf *dma_risc; + + struct cx23885_audio_buffer *buf; + + struct snd_pcm_substream *substream; +}; + struct cx23885_dev { atomic_t refcount; struct v4l2_device v4l2_dev; @@ -362,6 +392,7 @@ struct cx23885_dev { /* Analog video */ u32 resources; unsigned int input; + unsigned int audinput; /* Selectable audio input */ u32 tvaudio; v4l2_std_id tvnorm; unsigned int tuner_type; @@ -400,6 +431,9 @@ struct cx23885_dev { atomic_t v4l_reader_count; struct cx23885_tvnorm encodernorm; + /* Analog raw audio */ + struct cx23885_audio_dev *audio_dev; + }; static inline struct cx23885_dev *to_cx23885(struct v4l2_device *v4l2_dev) @@ -478,6 +512,11 @@ extern int cx23885_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc, unsigned int top_offset, unsigned int bottom_offset, unsigned int bpl, unsigned int padding, unsigned int lines); +extern int cx23885_risc_vbibuffer(struct pci_dev *pci, + struct btcx_riscmem *risc, struct scatterlist *sglist, + unsigned int top_offset, unsigned int bottom_offset, + unsigned int bpl, unsigned int padding, unsigned int lines); + void cx23885_cancel_buffers(struct cx23885_tsport *port); extern int cx23885_restart_queue(struct cx23885_tsport *port, @@ -533,6 +572,8 @@ extern void cx23885_free_buffer(struct videobuf_queue *q, extern int cx23885_video_register(struct cx23885_dev *dev); extern void cx23885_video_unregister(struct cx23885_dev *dev); extern int cx23885_video_irq(struct cx23885_dev *dev, u32 status); +extern void cx23885_video_wakeup(struct cx23885_dev *dev, + struct cx23885_dmaqueue *q, u32 count); /* ----------------------------------------------------------- */ /* cx23885-vbi.c */ @@ -540,6 +581,9 @@ extern int cx23885_vbi_fmt(struct file *file, void *priv, struct v4l2_format *f); extern void cx23885_vbi_timeout(unsigned long data); extern struct videobuf_queue_ops cx23885_vbi_qops; +extern int cx23885_restart_vbi_queue(struct cx23885_dev *dev, + struct cx23885_dmaqueue *q); +extern int cx23885_vbi_irq(struct cx23885_dev *dev, u32 status); /* cx23885-i2c.c */ extern int cx23885_i2c_register(struct cx23885_i2c *bus); @@ -563,6 +607,18 @@ extern void mc417_gpio_set(struct cx23885_dev *dev, u32 mask); extern void mc417_gpio_clear(struct cx23885_dev *dev, u32 mask); extern void mc417_gpio_enable(struct cx23885_dev *dev, u32 mask, int asoutput); +/* ----------------------------------------------------------- */ +/* cx23885-alsa.c */ +extern struct cx23885_audio_dev *cx23885_audio_register( + struct cx23885_dev *dev); +extern void cx23885_audio_unregister(struct cx23885_dev *dev); +extern int cx23885_audio_irq(struct cx23885_dev *dev, u32 status, u32 mask); +extern int cx23885_risc_databuffer(struct pci_dev *pci, + struct btcx_riscmem *risc, + struct scatterlist *sglist, + unsigned int bpl, + unsigned int lines, + unsigned int lpi); /* ----------------------------------------------------------- */ /* tv norms */ diff --git a/drivers/media/video/cx23885/cx23888-ir.c b/drivers/media/video/cx23885/cx23888-ir.c index e37be6fcf67..bb1ce346425 100644 --- a/drivers/media/video/cx23885/cx23888-ir.c +++ b/drivers/media/video/cx23885/cx23888-ir.c @@ -673,7 +673,7 @@ static int cx23888_ir_rx_read(struct v4l2_subdev *sd, u8 *buf, size_t count, unsigned int i, n; union cx23888_ir_fifo_rec *p; - unsigned u, v; + unsigned u, v, w; n = count / sizeof(union cx23888_ir_fifo_rec) * sizeof(union cx23888_ir_fifo_rec); @@ -692,11 +692,12 @@ static int cx23888_ir_rx_read(struct v4l2_subdev *sd, u8 *buf, size_t count, if ((p->hw_fifo_data & FIFO_RXTX_RTO) == FIFO_RXTX_RTO) { /* Assume RTO was because of no IR light input */ u = 0; - v4l2_dbg(2, ir_888_debug, sd, "rx read: end of rx\n"); + w = 1; } else { u = (p->hw_fifo_data & FIFO_RXTX_LVL) ? 1 : 0; if (invert) u = u ? 0 : 1; + w = 0; } v = (unsigned) pulse_width_count_to_ns( @@ -707,9 +708,12 @@ static int cx23888_ir_rx_read(struct v4l2_subdev *sd, u8 *buf, size_t count, init_ir_raw_event(&p->ir_core_data); p->ir_core_data.pulse = u; p->ir_core_data.duration = v; + p->ir_core_data.timeout = w; - v4l2_dbg(2, ir_888_debug, sd, "rx read: %10u ns %s\n", - v, u ? "mark" : "space"); + v4l2_dbg(2, ir_888_debug, sd, "rx read: %10u ns %s %s\n", + v, u ? "mark" : "space", w ? "(timed out)" : ""); + if (w) + v4l2_dbg(2, ir_888_debug, sd, "rx read: end of rx\n"); } return 0; } diff --git a/drivers/media/video/cx25821/Kconfig b/drivers/media/video/cx25821/Kconfig new file mode 100644 index 00000000000..5f6b5421371 --- /dev/null +++ b/drivers/media/video/cx25821/Kconfig @@ -0,0 +1,34 @@ +config VIDEO_CX25821 + tristate "Conexant cx25821 support" + depends on DVB_CORE && VIDEO_DEV && PCI && I2C + select I2C_ALGOBIT + select VIDEO_BTCX + select VIDEO_TVEEPROM + depends on RC_CORE + select VIDEOBUF_DVB + select VIDEOBUF_DMA_SG + select VIDEO_CX25840 + select VIDEO_CX2341X + ---help--- + This is a video4linux driver for Conexant 25821 based + TV cards. + + To compile this driver as a module, choose M here: the + module will be called cx25821 + +config VIDEO_CX25821_ALSA + tristate "Conexant 25821 DMA audio support" + depends on VIDEO_CX25821 && SND && EXPERIMENTAL + select SND_PCM + ---help--- + This is a video4linux driver for direct (DMA) audio on + Conexant 25821 based capture cards using ALSA. + + It only works with boards with function 01 enabled. + To check if your board supports, use lspci -n. + If supported, you should see 14f1:8801 or 14f1:8811 + PCI device. + + To compile this driver as a module, choose M here: the + module will be called cx25821-alsa. + diff --git a/drivers/media/video/cx25821/Makefile b/drivers/media/video/cx25821/Makefile new file mode 100644 index 00000000000..aedde18c68f --- /dev/null +++ b/drivers/media/video/cx25821/Makefile @@ -0,0 +1,13 @@ +cx25821-y := cx25821-core.o cx25821-cards.o cx25821-i2c.o \ + cx25821-gpio.o cx25821-medusa-video.o \ + cx25821-video.o cx25821-video-upstream.o \ + cx25821-video-upstream-ch2.o \ + cx25821-audio-upstream.o + +obj-$(CONFIG_VIDEO_CX25821) += cx25821.o +obj-$(CONFIG_VIDEO_CX25821_ALSA) += cx25821-alsa.o + +ccflags-y := -Idrivers/media/video +ccflags-y += -Idrivers/media/common/tuners +ccflags-y += -Idrivers/media/dvb/dvb-core +ccflags-y += -Idrivers/media/dvb/frontends diff --git a/drivers/media/video/cx25821/cx25821-alsa.c b/drivers/media/video/cx25821/cx25821-alsa.c new file mode 100644 index 00000000000..09e99de5fd2 --- /dev/null +++ b/drivers/media/video/cx25821/cx25821-alsa.c @@ -0,0 +1,795 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> + * Based on SAA713x ALSA driver and CX88 driver + * + * 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, version 2 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/vmalloc.h> +#include <linux/dma-mapping.h> +#include <linux/pci.h> +#include <linux/slab.h> + +#include <linux/delay.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/control.h> +#include <sound/initval.h> +#include <sound/tlv.h> + +#include "cx25821.h" +#include "cx25821-reg.h" + +#define AUDIO_SRAM_CHANNEL SRAM_CH08 + +#define dprintk(level, fmt, arg...) \ +do { \ + if (debug >= level) \ + pr_info("%s/1: " fmt, chip->dev->name, ##arg); \ +} while (0) +#define dprintk_core(level, fmt, arg...) \ +do { \ + if (debug >= level) \ + printk(KERN_DEBUG "%s/1: " fmt, chip->dev->name, ##arg); \ +} while (0) + +/**************************************************************************** + Data type declarations - Can be moded to a header file later + ****************************************************************************/ + +static struct snd_card *snd_cx25821_cards[SNDRV_CARDS]; +static int devno; + +struct cx25821_audio_buffer { + unsigned int bpl; + struct btcx_riscmem risc; + struct videobuf_dmabuf dma; +}; + +struct cx25821_audio_dev { + struct cx25821_dev *dev; + struct cx25821_dmaqueue q; + + /* pci i/o */ + struct pci_dev *pci; + + /* audio controls */ + int irq; + + struct snd_card *card; + + unsigned long iobase; + spinlock_t reg_lock; + atomic_t count; + + unsigned int dma_size; + unsigned int period_size; + unsigned int num_periods; + + struct videobuf_dmabuf *dma_risc; + + struct cx25821_audio_buffer *buf; + + struct snd_pcm_substream *substream; +}; + + +/**************************************************************************** + Module global static vars + ****************************************************************************/ + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = { 1, [1 ... (SNDRV_CARDS - 1)] = 1 }; + +module_param_array(enable, bool, NULL, 0444); +MODULE_PARM_DESC(enable, "Enable cx25821 soundcard. default enabled."); + +module_param_array(index, int, NULL, 0444); +MODULE_PARM_DESC(index, "Index value for cx25821 capture interface(s)."); + +/**************************************************************************** + Module macros + ****************************************************************************/ + +MODULE_DESCRIPTION("ALSA driver module for cx25821 based capture cards"); +MODULE_AUTHOR("Hiep Huynh"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("{{Conexant,25821}"); /* "{{Conexant,23881}," */ + +static unsigned int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "enable debug messages"); + +/**************************************************************************** + Module specific funtions + ****************************************************************************/ +/* Constants taken from cx88-reg.h */ +#define AUD_INT_DN_RISCI1 (1 << 0) +#define AUD_INT_UP_RISCI1 (1 << 1) +#define AUD_INT_RDS_DN_RISCI1 (1 << 2) +#define AUD_INT_DN_RISCI2 (1 << 4) /* yes, 3 is skipped */ +#define AUD_INT_UP_RISCI2 (1 << 5) +#define AUD_INT_RDS_DN_RISCI2 (1 << 6) +#define AUD_INT_DN_SYNC (1 << 12) +#define AUD_INT_UP_SYNC (1 << 13) +#define AUD_INT_RDS_DN_SYNC (1 << 14) +#define AUD_INT_OPC_ERR (1 << 16) +#define AUD_INT_BER_IRQ (1 << 20) +#define AUD_INT_MCHG_IRQ (1 << 21) +#define GP_COUNT_CONTROL_RESET 0x3 + +#define PCI_MSK_AUD_EXT (1 << 4) +#define PCI_MSK_AUD_INT (1 << 3) +/* + * BOARD Specific: Sets audio DMA + */ + +static int _cx25821_start_audio_dma(struct cx25821_audio_dev *chip) +{ + struct cx25821_audio_buffer *buf = chip->buf; + struct cx25821_dev *dev = chip->dev; + struct sram_channel *audio_ch = + &cx25821_sram_channels[AUDIO_SRAM_CHANNEL]; + u32 tmp = 0; + + /* enable output on the GPIO 0 for the MCLK ADC (Audio) */ + cx25821_set_gpiopin_direction(chip->dev, 0, 0); + + /* Make sure RISC/FIFO are off before changing FIFO/RISC settings */ + cx_clear(AUD_INT_DMA_CTL, + FLD_AUD_DST_A_RISC_EN | FLD_AUD_DST_A_FIFO_EN); + + /* setup fifo + format - out channel */ + cx25821_sram_channel_setup_audio(chip->dev, audio_ch, buf->bpl, + buf->risc.dma); + + /* sets bpl size */ + cx_write(AUD_A_LNGTH, buf->bpl); + + /* reset counter */ + /* GP_COUNT_CONTROL_RESET = 0x3 */ + cx_write(AUD_A_GPCNT_CTL, GP_COUNT_CONTROL_RESET); + atomic_set(&chip->count, 0); + + /* Set the input mode to 16-bit */ + tmp = cx_read(AUD_A_CFG); + cx_write(AUD_A_CFG, + tmp | FLD_AUD_DST_PK_MODE | FLD_AUD_DST_ENABLE | + FLD_AUD_CLK_ENABLE); + + /* + pr_info("DEBUG: Start audio DMA, %d B/line, cmds_start(0x%x)= %d lines/FIFO, %d periods, %d byte buffer\n", + buf->bpl, audio_ch->cmds_start, + cx_read(audio_ch->cmds_start + 12)>>1, + chip->num_periods, buf->bpl * chip->num_periods); + */ + + /* Enables corresponding bits at AUD_INT_STAT */ + cx_write(AUD_A_INT_MSK, + FLD_AUD_DST_RISCI1 | FLD_AUD_DST_OF | FLD_AUD_DST_SYNC | + FLD_AUD_DST_OPC_ERR); + + /* Clean any pending interrupt bits already set */ + cx_write(AUD_A_INT_STAT, ~0); + + /* enable audio irqs */ + cx_set(PCI_INT_MSK, chip->dev->pci_irqmask | PCI_MSK_AUD_INT); + + /* Turn on audio downstream fifo and risc enable 0x101 */ + tmp = cx_read(AUD_INT_DMA_CTL); + cx_set(AUD_INT_DMA_CTL, + tmp | (FLD_AUD_DST_A_RISC_EN | FLD_AUD_DST_A_FIFO_EN)); + + mdelay(100); + return 0; +} + +/* + * BOARD Specific: Resets audio DMA + */ +static int _cx25821_stop_audio_dma(struct cx25821_audio_dev *chip) +{ + struct cx25821_dev *dev = chip->dev; + + /* stop dma */ + cx_clear(AUD_INT_DMA_CTL, + FLD_AUD_DST_A_RISC_EN | FLD_AUD_DST_A_FIFO_EN); + + /* disable irqs */ + cx_clear(PCI_INT_MSK, PCI_MSK_AUD_INT); + cx_clear(AUD_A_INT_MSK, + AUD_INT_OPC_ERR | AUD_INT_DN_SYNC | AUD_INT_DN_RISCI2 | + AUD_INT_DN_RISCI1); + + return 0; +} + +#define MAX_IRQ_LOOP 50 + +/* + * BOARD Specific: IRQ dma bits + */ +static char *cx25821_aud_irqs[32] = { + "dn_risci1", "up_risci1", "rds_dn_risc1", /* 0-2 */ + NULL, /* reserved */ + "dn_risci2", "up_risci2", "rds_dn_risc2", /* 4-6 */ + NULL, /* reserved */ + "dnf_of", "upf_uf", "rds_dnf_uf", /* 8-10 */ + NULL, /* reserved */ + "dn_sync", "up_sync", "rds_dn_sync", /* 12-14 */ + NULL, /* reserved */ + "opc_err", "par_err", "rip_err", /* 16-18 */ + "pci_abort", "ber_irq", "mchg_irq" /* 19-21 */ +}; + +/* + * BOARD Specific: Threats IRQ audio specific calls + */ +static void cx25821_aud_irq(struct cx25821_audio_dev *chip, u32 status, + u32 mask) +{ + struct cx25821_dev *dev = chip->dev; + + if (0 == (status & mask)) + return; + + cx_write(AUD_A_INT_STAT, status); + if (debug > 1 || (status & mask & ~0xff)) + cx25821_print_irqbits(dev->name, "irq aud", + cx25821_aud_irqs, + ARRAY_SIZE(cx25821_aud_irqs), status, + mask); + + /* risc op code error */ + if (status & AUD_INT_OPC_ERR) { + pr_warn("WARNING %s/1: Audio risc op code error\n", dev->name); + + cx_clear(AUD_INT_DMA_CTL, + FLD_AUD_DST_A_RISC_EN | FLD_AUD_DST_A_FIFO_EN); + cx25821_sram_channel_dump_audio(dev, + &cx25821_sram_channels + [AUDIO_SRAM_CHANNEL]); + } + if (status & AUD_INT_DN_SYNC) { + pr_warn("WARNING %s: Downstream sync error!\n", dev->name); + cx_write(AUD_A_GPCNT_CTL, GP_COUNT_CONTROL_RESET); + return; + } + + /* risc1 downstream */ + if (status & AUD_INT_DN_RISCI1) { + atomic_set(&chip->count, cx_read(AUD_A_GPCNT)); + snd_pcm_period_elapsed(chip->substream); + } +} + +/* + * BOARD Specific: Handles IRQ calls + */ +static irqreturn_t cx25821_irq(int irq, void *dev_id) +{ + struct cx25821_audio_dev *chip = dev_id; + struct cx25821_dev *dev = chip->dev; + u32 status, pci_status; + u32 audint_status, audint_mask; + int loop, handled = 0; + int audint_count = 0; + + audint_status = cx_read(AUD_A_INT_STAT); + audint_mask = cx_read(AUD_A_INT_MSK); + audint_count = cx_read(AUD_A_GPCNT); + status = cx_read(PCI_INT_STAT); + + for (loop = 0; loop < 1; loop++) { + status = cx_read(PCI_INT_STAT); + if (0 == status) { + status = cx_read(PCI_INT_STAT); + audint_status = cx_read(AUD_A_INT_STAT); + audint_mask = cx_read(AUD_A_INT_MSK); + + if (status) { + handled = 1; + cx_write(PCI_INT_STAT, status); + + cx25821_aud_irq(chip, audint_status, + audint_mask); + break; + } else + goto out; + } + + handled = 1; + cx_write(PCI_INT_STAT, status); + + cx25821_aud_irq(chip, audint_status, audint_mask); + } + + pci_status = cx_read(PCI_INT_STAT); + + if (handled) + cx_write(PCI_INT_STAT, pci_status); + +out: + return IRQ_RETVAL(handled); +} + +static int dsp_buffer_free(struct cx25821_audio_dev *chip) +{ + BUG_ON(!chip->dma_size); + + dprintk(2, "Freeing buffer\n"); + videobuf_dma_unmap(&chip->pci->dev, chip->dma_risc); + videobuf_dma_free(chip->dma_risc); + btcx_riscmem_free(chip->pci, &chip->buf->risc); + kfree(chip->buf); + + chip->dma_risc = NULL; + chip->dma_size = 0; + + return 0; +} + +/**************************************************************************** + ALSA PCM Interface + ****************************************************************************/ + +/* + * Digital hardware definition + */ +#define DEFAULT_FIFO_SIZE 384 +static struct snd_pcm_hardware snd_cx25821_digital_hw = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + /* Analog audio output will be full of clicks and pops if there + are not exactly four lines in the SRAM FIFO buffer. */ + .period_bytes_min = DEFAULT_FIFO_SIZE / 3, + .period_bytes_max = DEFAULT_FIFO_SIZE / 3, + .periods_min = 1, + .periods_max = AUDIO_LINE_SIZE, + /* 128 * 128 = 16384 = 1024 * 16 */ + .buffer_bytes_max = (AUDIO_LINE_SIZE * AUDIO_LINE_SIZE), +}; + +/* + * audio pcm capture open callback + */ +static int snd_cx25821_pcm_open(struct snd_pcm_substream *substream) +{ + struct cx25821_audio_dev *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + int err; + unsigned int bpl = 0; + + if (!chip) { + pr_err("DEBUG: cx25821 can't find device struct. Can't proceed with open\n"); + return -ENODEV; + } + + err = + snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_PERIODS); + if (err < 0) + goto _error; + + chip->substream = substream; + + runtime->hw = snd_cx25821_digital_hw; + + if (cx25821_sram_channels[AUDIO_SRAM_CHANNEL].fifo_size != + DEFAULT_FIFO_SIZE) { + /* since there are 3 audio Clusters */ + bpl = cx25821_sram_channels[AUDIO_SRAM_CHANNEL].fifo_size / 3; + bpl &= ~7; /* must be multiple of 8 */ + + if (bpl > AUDIO_LINE_SIZE) + bpl = AUDIO_LINE_SIZE; + + runtime->hw.period_bytes_min = bpl; + runtime->hw.period_bytes_max = bpl; + } + + return 0; +_error: + dprintk(1, "Error opening PCM!\n"); + return err; +} + +/* + * audio close callback + */ +static int snd_cx25821_close(struct snd_pcm_substream *substream) +{ + return 0; +} + +/* + * hw_params callback + */ +static int snd_cx25821_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct cx25821_audio_dev *chip = snd_pcm_substream_chip(substream); + struct videobuf_dmabuf *dma; + + struct cx25821_audio_buffer *buf; + int ret; + + if (substream->runtime->dma_area) { + dsp_buffer_free(chip); + substream->runtime->dma_area = NULL; + } + + chip->period_size = params_period_bytes(hw_params); + chip->num_periods = params_periods(hw_params); + chip->dma_size = chip->period_size * params_periods(hw_params); + + BUG_ON(!chip->dma_size); + BUG_ON(chip->num_periods & (chip->num_periods - 1)); + + buf = kzalloc(sizeof(*buf), GFP_KERNEL); + if (NULL == buf) + return -ENOMEM; + + if (chip->period_size > AUDIO_LINE_SIZE) + chip->period_size = AUDIO_LINE_SIZE; + + buf->bpl = chip->period_size; + + dma = &buf->dma; + videobuf_dma_init(dma); + ret = videobuf_dma_init_kernel(dma, PCI_DMA_FROMDEVICE, + (PAGE_ALIGN(chip->dma_size) >> + PAGE_SHIFT)); + if (ret < 0) + goto error; + + ret = videobuf_dma_map(&chip->pci->dev, dma); + if (ret < 0) + goto error; + + ret = + cx25821_risc_databuffer_audio(chip->pci, &buf->risc, dma->sglist, + chip->period_size, chip->num_periods, + 1); + if (ret < 0) { + pr_info("DEBUG: ERROR after cx25821_risc_databuffer_audio()\n"); + goto error; + } + + /* Loop back to start of program */ + buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); + buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma); + buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */ + + chip->buf = buf; + chip->dma_risc = dma; + + substream->runtime->dma_area = chip->dma_risc->vaddr; + substream->runtime->dma_bytes = chip->dma_size; + substream->runtime->dma_addr = 0; + + return 0; + +error: + kfree(buf); + return ret; +} + +/* + * hw free callback + */ +static int snd_cx25821_hw_free(struct snd_pcm_substream *substream) +{ + struct cx25821_audio_dev *chip = snd_pcm_substream_chip(substream); + + if (substream->runtime->dma_area) { + dsp_buffer_free(chip); + substream->runtime->dma_area = NULL; + } + + return 0; +} + +/* + * prepare callback + */ +static int snd_cx25821_prepare(struct snd_pcm_substream *substream) +{ + return 0; +} + +/* + * trigger callback + */ +static int snd_cx25821_card_trigger(struct snd_pcm_substream *substream, + int cmd) +{ + struct cx25821_audio_dev *chip = snd_pcm_substream_chip(substream); + int err = 0; + + /* Local interrupts are already disabled by ALSA */ + spin_lock(&chip->reg_lock); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + err = _cx25821_start_audio_dma(chip); + break; + case SNDRV_PCM_TRIGGER_STOP: + err = _cx25821_stop_audio_dma(chip); + break; + default: + err = -EINVAL; + break; + } + + spin_unlock(&chip->reg_lock); + + return err; +} + +/* + * pointer callback + */ +static snd_pcm_uframes_t snd_cx25821_pointer(struct snd_pcm_substream + *substream) +{ + struct cx25821_audio_dev *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + u16 count; + + count = atomic_read(&chip->count); + + return runtime->period_size * (count & (runtime->periods - 1)); +} + +/* + * page callback (needed for mmap) + */ +static struct page *snd_cx25821_page(struct snd_pcm_substream *substream, + unsigned long offset) +{ + void *pageptr = substream->runtime->dma_area + offset; + + return vmalloc_to_page(pageptr); +} + +/* + * operators + */ +static struct snd_pcm_ops snd_cx25821_pcm_ops = { + .open = snd_cx25821_pcm_open, + .close = snd_cx25821_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_cx25821_hw_params, + .hw_free = snd_cx25821_hw_free, + .prepare = snd_cx25821_prepare, + .trigger = snd_cx25821_card_trigger, + .pointer = snd_cx25821_pointer, + .page = snd_cx25821_page, +}; + +/* + * ALSA create a PCM device: Called when initializing the board. + * Sets up the name and hooks up the callbacks + */ +static int snd_cx25821_pcm(struct cx25821_audio_dev *chip, int device, + char *name) +{ + struct snd_pcm *pcm; + int err; + + err = snd_pcm_new(chip->card, name, device, 0, 1, &pcm); + if (err < 0) { + pr_info("ERROR: FAILED snd_pcm_new() in %s\n", __func__); + return err; + } + pcm->private_data = chip; + pcm->info_flags = 0; + strcpy(pcm->name, name); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cx25821_pcm_ops); + + return 0; +} + +/**************************************************************************** + Basic Flow for Sound Devices + ****************************************************************************/ + +/* + * PCI ID Table - 14f1:8801 and 14f1:8811 means function 1: Audio + * Only boards with eeprom and byte 1 at eeprom=1 have it + */ + +static DEFINE_PCI_DEVICE_TABLE(cx25821_audio_pci_tbl) = { + {0x14f1, 0x0920, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {0,} +}; + +MODULE_DEVICE_TABLE(pci, cx25821_audio_pci_tbl); + +/* + * Not used in the function snd_cx25821_dev_free so removing + * from the file. + */ +/* +static int snd_cx25821_free(struct cx25821_audio_dev *chip) +{ + if (chip->irq >= 0) + free_irq(chip->irq, chip); + + cx25821_dev_unregister(chip->dev); + pci_disable_device(chip->pci); + + return 0; +} +*/ + +/* + * Component Destructor + */ +static void snd_cx25821_dev_free(struct snd_card *card) +{ + struct cx25821_audio_dev *chip = card->private_data; + + /* snd_cx25821_free(chip); */ + snd_card_free(chip->card); +} + +/* + * Alsa Constructor - Component probe + */ +static int cx25821_audio_initdev(struct cx25821_dev *dev) +{ + struct snd_card *card; + struct cx25821_audio_dev *chip; + int err; + + if (devno >= SNDRV_CARDS) { + pr_info("DEBUG ERROR: devno >= SNDRV_CARDS %s\n", __func__); + return -ENODEV; + } + + if (!enable[devno]) { + ++devno; + pr_info("DEBUG ERROR: !enable[devno] %s\n", __func__); + return -ENOENT; + } + + err = snd_card_create(index[devno], id[devno], THIS_MODULE, + sizeof(struct cx25821_audio_dev), &card); + if (err < 0) { + pr_info("DEBUG ERROR: cannot create snd_card_new in %s\n", + __func__); + return err; + } + + strcpy(card->driver, "cx25821"); + + /* Card "creation" */ + card->private_free = snd_cx25821_dev_free; + chip = card->private_data; + spin_lock_init(&chip->reg_lock); + + chip->dev = dev; + chip->card = card; + chip->pci = dev->pci; + chip->iobase = pci_resource_start(dev->pci, 0); + + chip->irq = dev->pci->irq; + + err = request_irq(dev->pci->irq, cx25821_irq, + IRQF_SHARED, chip->dev->name, chip); + + if (err < 0) { + pr_err("ERROR %s: can't get IRQ %d for ALSA\n", + chip->dev->name, dev->pci->irq); + goto error; + } + + err = snd_cx25821_pcm(chip, 0, "cx25821 Digital"); + if (err < 0) { + pr_info("DEBUG ERROR: cannot create snd_cx25821_pcm %s\n", + __func__); + goto error; + } + + snd_card_set_dev(card, &chip->pci->dev); + + strcpy(card->shortname, "cx25821"); + sprintf(card->longname, "%s at 0x%lx irq %d", chip->dev->name, + chip->iobase, chip->irq); + strcpy(card->mixername, "CX25821"); + + pr_info("%s/%i: ALSA support for cx25821 boards\n", + card->driver, devno); + + err = snd_card_register(card); + if (err < 0) { + pr_info("DEBUG ERROR: cannot register sound card %s\n", + __func__); + goto error; + } + + snd_cx25821_cards[devno] = card; + + devno++; + return 0; + +error: + snd_card_free(card); + return err; +} + +/**************************************************************************** + LINUX MODULE INIT + ****************************************************************************/ +static void cx25821_audio_fini(void) +{ + snd_card_free(snd_cx25821_cards[0]); +} + +/* + * Module initializer + * + * Loops through present saa7134 cards, and assigns an ALSA device + * to each one + * + */ +static int cx25821_alsa_init(void) +{ + struct cx25821_dev *dev = NULL; + struct list_head *list; + + mutex_lock(&cx25821_devlist_mutex); + list_for_each(list, &cx25821_devlist) { + dev = list_entry(list, struct cx25821_dev, devlist); + cx25821_audio_initdev(dev); + } + mutex_unlock(&cx25821_devlist_mutex); + + if (dev == NULL) + pr_info("ERROR ALSA: no cx25821 cards found\n"); + + return 0; + +} + +late_initcall(cx25821_alsa_init); +module_exit(cx25821_audio_fini); + +/* ----------------------------------------------------------- */ +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/video/cx25821/cx25821-audio-upstream.c b/drivers/media/video/cx25821/cx25821-audio-upstream.c new file mode 100644 index 00000000000..c20d6dece15 --- /dev/null +++ b/drivers/media/video/cx25821/cx25821-audio-upstream.c @@ -0,0 +1,788 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <hiep.huynh@conexant.com>, <shu.lin@conexant.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "cx25821-video.h" +#include "cx25821-audio-upstream.h" + +#include <linux/fs.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/syscalls.h> +#include <linux/file.h> +#include <linux/fcntl.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/uaccess.h> + +MODULE_DESCRIPTION("v4l2 driver module for cx25821 based TV cards"); +MODULE_AUTHOR("Hiep Huynh <hiep.huynh@conexant.com>"); +MODULE_LICENSE("GPL"); + +static int _intr_msk = FLD_AUD_SRC_RISCI1 | FLD_AUD_SRC_OF | + FLD_AUD_SRC_SYNC | FLD_AUD_SRC_OPC_ERR; + +int cx25821_sram_channel_setup_upstream_audio(struct cx25821_dev *dev, + struct sram_channel *ch, + unsigned int bpl, u32 risc) +{ + unsigned int i, lines; + u32 cdt; + + if (ch->cmds_start == 0) { + cx_write(ch->ptr1_reg, 0); + cx_write(ch->ptr2_reg, 0); + cx_write(ch->cnt2_reg, 0); + cx_write(ch->cnt1_reg, 0); + return 0; + } + + bpl = (bpl + 7) & ~7; /* alignment */ + cdt = ch->cdt; + lines = ch->fifo_size / bpl; + + if (lines > 3) + lines = 3; + + BUG_ON(lines < 2); + + /* write CDT */ + for (i = 0; i < lines; i++) { + cx_write(cdt + 16 * i, ch->fifo_start + bpl * i); + cx_write(cdt + 16 * i + 4, 0); + cx_write(cdt + 16 * i + 8, 0); + cx_write(cdt + 16 * i + 12, 0); + } + + /* write CMDS */ + cx_write(ch->cmds_start + 0, risc); + + cx_write(ch->cmds_start + 4, 0); + cx_write(ch->cmds_start + 8, cdt); + cx_write(ch->cmds_start + 12, AUDIO_CDT_SIZE_QW); + cx_write(ch->cmds_start + 16, ch->ctrl_start); + + /* IQ size */ + cx_write(ch->cmds_start + 20, AUDIO_IQ_SIZE_DW); + + for (i = 24; i < 80; i += 4) + cx_write(ch->cmds_start + i, 0); + + /* fill registers */ + cx_write(ch->ptr1_reg, ch->fifo_start); + cx_write(ch->ptr2_reg, cdt); + cx_write(ch->cnt2_reg, AUDIO_CDT_SIZE_QW); + cx_write(ch->cnt1_reg, AUDIO_CLUSTER_SIZE_QW - 1); + + return 0; +} + +static __le32 *cx25821_risc_field_upstream_audio(struct cx25821_dev *dev, + __le32 *rp, + dma_addr_t databuf_phys_addr, + unsigned int bpl, + int fifo_enable) +{ + unsigned int line; + struct sram_channel *sram_ch = + dev->channels[dev->_audio_upstream_channel].sram_channels; + int offset = 0; + + /* scan lines */ + for (line = 0; line < LINES_PER_AUDIO_BUFFER; line++) { + *(rp++) = cpu_to_le32(RISC_READ | RISC_SOL | RISC_EOL | bpl); + *(rp++) = cpu_to_le32(databuf_phys_addr + offset); + *(rp++) = cpu_to_le32(0); /* bits 63-32 */ + + /* Check if we need to enable the FIFO + * after the first 3 lines. + * For the upstream audio channel, + * the risc engine will enable the FIFO */ + if (fifo_enable && line == 2) { + *(rp++) = RISC_WRITECR; + *(rp++) = sram_ch->dma_ctl; + *(rp++) = sram_ch->fld_aud_fifo_en; + *(rp++) = 0x00000020; + } + + offset += AUDIO_LINE_SIZE; + } + + return rp; +} + +int cx25821_risc_buffer_upstream_audio(struct cx25821_dev *dev, + struct pci_dev *pci, + unsigned int bpl, unsigned int lines) +{ + __le32 *rp; + int fifo_enable = 0; + int frame = 0, i = 0; + int frame_size = AUDIO_DATA_BUF_SZ; + int databuf_offset = 0; + int risc_flag = RISC_CNT_INC; + dma_addr_t risc_phys_jump_addr; + + /* Virtual address of Risc buffer program */ + rp = dev->_risc_virt_addr; + + /* sync instruction */ + *(rp++) = cpu_to_le32(RISC_RESYNC | AUDIO_SYNC_LINE); + + for (frame = 0; frame < NUM_AUDIO_FRAMES; frame++) { + databuf_offset = frame_size * frame; + + if (frame == 0) { + fifo_enable = 1; + risc_flag = RISC_CNT_RESET; + } else { + fifo_enable = 0; + risc_flag = RISC_CNT_INC; + } + + /* Calculate physical jump address */ + if ((frame + 1) == NUM_AUDIO_FRAMES) { + risc_phys_jump_addr = + dev->_risc_phys_start_addr + + RISC_SYNC_INSTRUCTION_SIZE; + } else { + risc_phys_jump_addr = + dev->_risc_phys_start_addr + + RISC_SYNC_INSTRUCTION_SIZE + + AUDIO_RISC_DMA_BUF_SIZE * (frame + 1); + } + + rp = cx25821_risc_field_upstream_audio(dev, rp, + dev-> + _audiodata_buf_phys_addr + + databuf_offset, bpl, + fifo_enable); + + if (USE_RISC_NOOP_AUDIO) { + for (i = 0; i < NUM_NO_OPS; i++) + *(rp++) = cpu_to_le32(RISC_NOOP); + } + + /* Loop to (Nth)FrameRISC or to Start of Risc program & + * generate IRQ */ + *(rp++) = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | risc_flag); + *(rp++) = cpu_to_le32(risc_phys_jump_addr); + *(rp++) = cpu_to_le32(0); + + /* Recalculate virtual address based on frame index */ + rp = dev->_risc_virt_addr + RISC_SYNC_INSTRUCTION_SIZE / 4 + + (AUDIO_RISC_DMA_BUF_SIZE * (frame + 1) / 4); + } + + return 0; +} + +void cx25821_free_memory_audio(struct cx25821_dev *dev) +{ + if (dev->_risc_virt_addr) { + pci_free_consistent(dev->pci, dev->_audiorisc_size, + dev->_risc_virt_addr, dev->_risc_phys_addr); + dev->_risc_virt_addr = NULL; + } + + if (dev->_audiodata_buf_virt_addr) { + pci_free_consistent(dev->pci, dev->_audiodata_buf_size, + dev->_audiodata_buf_virt_addr, + dev->_audiodata_buf_phys_addr); + dev->_audiodata_buf_virt_addr = NULL; + } +} + +void cx25821_stop_upstream_audio(struct cx25821_dev *dev) +{ + struct sram_channel *sram_ch = + dev->channels[AUDIO_UPSTREAM_SRAM_CHANNEL_B].sram_channels; + u32 tmp = 0; + + if (!dev->_audio_is_running) { + printk(KERN_DEBUG + pr_fmt("No audio file is currently running so return!\n")); + return; + } + /* Disable RISC interrupts */ + cx_write(sram_ch->int_msk, 0); + + /* Turn OFF risc and fifo enable in AUD_DMA_CNTRL */ + tmp = cx_read(sram_ch->dma_ctl); + cx_write(sram_ch->dma_ctl, + tmp & ~(sram_ch->fld_aud_fifo_en | sram_ch->fld_aud_risc_en)); + + /* Clear data buffer memory */ + if (dev->_audiodata_buf_virt_addr) + memset(dev->_audiodata_buf_virt_addr, 0, + dev->_audiodata_buf_size); + + dev->_audio_is_running = 0; + dev->_is_first_audio_frame = 0; + dev->_audioframe_count = 0; + dev->_audiofile_status = END_OF_FILE; + + kfree(dev->_irq_audio_queues); + dev->_irq_audio_queues = NULL; + + kfree(dev->_audiofilename); +} + +void cx25821_free_mem_upstream_audio(struct cx25821_dev *dev) +{ + if (dev->_audio_is_running) + cx25821_stop_upstream_audio(dev); + + cx25821_free_memory_audio(dev); +} + +int cx25821_get_audio_data(struct cx25821_dev *dev, + struct sram_channel *sram_ch) +{ + struct file *myfile; + int frame_index_temp = dev->_audioframe_index; + int i = 0; + int line_size = AUDIO_LINE_SIZE; + int frame_size = AUDIO_DATA_BUF_SZ; + int frame_offset = frame_size * frame_index_temp; + ssize_t vfs_read_retval = 0; + char mybuf[line_size]; + loff_t file_offset = dev->_audioframe_count * frame_size; + loff_t pos; + mm_segment_t old_fs; + + if (dev->_audiofile_status == END_OF_FILE) + return 0; + + myfile = filp_open(dev->_audiofilename, O_RDONLY | O_LARGEFILE, 0); + + if (IS_ERR(myfile)) { + const int open_errno = -PTR_ERR(myfile); + pr_err("%s(): ERROR opening file(%s) with errno = %d!\n", + __func__, dev->_audiofilename, open_errno); + return PTR_ERR(myfile); + } else { + if (!(myfile->f_op)) { + pr_err("%s(): File has no file operations registered!\n", + __func__); + filp_close(myfile, NULL); + return -EIO; + } + + if (!myfile->f_op->read) { + pr_err("%s(): File has no READ operations registered!\n", + __func__); + filp_close(myfile, NULL); + return -EIO; + } + + pos = myfile->f_pos; + old_fs = get_fs(); + set_fs(KERNEL_DS); + + for (i = 0; i < dev->_audio_lines_count; i++) { + pos = file_offset; + + vfs_read_retval = + vfs_read(myfile, mybuf, line_size, &pos); + + if (vfs_read_retval > 0 && vfs_read_retval == line_size + && dev->_audiodata_buf_virt_addr != NULL) { + memcpy((void *)(dev->_audiodata_buf_virt_addr + + frame_offset / 4), mybuf, + vfs_read_retval); + } + + file_offset += vfs_read_retval; + frame_offset += vfs_read_retval; + + if (vfs_read_retval < line_size) { + pr_info("Done: exit %s() since no more bytes to read from Audio file\n", + __func__); + break; + } + } + + if (i > 0) + dev->_audioframe_count++; + + dev->_audiofile_status = + (vfs_read_retval == line_size) ? IN_PROGRESS : END_OF_FILE; + + set_fs(old_fs); + filp_close(myfile, NULL); + } + + return 0; +} + +static void cx25821_audioups_handler(struct work_struct *work) +{ + struct cx25821_dev *dev = + container_of(work, struct cx25821_dev, _audio_work_entry); + + if (!dev) { + pr_err("ERROR %s(): since container_of(work_struct) FAILED!\n", + __func__); + return; + } + + cx25821_get_audio_data(dev, dev->channels[dev->_audio_upstream_channel]. + sram_channels); +} + +int cx25821_openfile_audio(struct cx25821_dev *dev, + struct sram_channel *sram_ch) +{ + struct file *myfile; + int i = 0, j = 0; + int line_size = AUDIO_LINE_SIZE; + ssize_t vfs_read_retval = 0; + char mybuf[line_size]; + loff_t pos; + loff_t offset = (unsigned long)0; + mm_segment_t old_fs; + + myfile = filp_open(dev->_audiofilename, O_RDONLY | O_LARGEFILE, 0); + + if (IS_ERR(myfile)) { + const int open_errno = -PTR_ERR(myfile); + pr_err("%s(): ERROR opening file(%s) with errno = %d!\n", + __func__, dev->_audiofilename, open_errno); + return PTR_ERR(myfile); + } else { + if (!(myfile->f_op)) { + pr_err("%s(): File has no file operations registered!\n", + __func__); + filp_close(myfile, NULL); + return -EIO; + } + + if (!myfile->f_op->read) { + pr_err("%s(): File has no READ operations registered!\n", + __func__); + filp_close(myfile, NULL); + return -EIO; + } + + pos = myfile->f_pos; + old_fs = get_fs(); + set_fs(KERNEL_DS); + + for (j = 0; j < NUM_AUDIO_FRAMES; j++) { + for (i = 0; i < dev->_audio_lines_count; i++) { + pos = offset; + + vfs_read_retval = + vfs_read(myfile, mybuf, line_size, &pos); + + if (vfs_read_retval > 0 + && vfs_read_retval == line_size + && dev->_audiodata_buf_virt_addr != NULL) { + memcpy((void *)(dev-> + _audiodata_buf_virt_addr + + offset / 4), mybuf, + vfs_read_retval); + } + + offset += vfs_read_retval; + + if (vfs_read_retval < line_size) { + pr_info("Done: exit %s() since no more bytes to read from Audio file\n", + __func__); + break; + } + } + + if (i > 0) + dev->_audioframe_count++; + + if (vfs_read_retval < line_size) + break; + } + + dev->_audiofile_status = + (vfs_read_retval == line_size) ? IN_PROGRESS : END_OF_FILE; + + set_fs(old_fs); + myfile->f_pos = 0; + filp_close(myfile, NULL); + } + + return 0; +} + +static int cx25821_audio_upstream_buffer_prepare(struct cx25821_dev *dev, + struct sram_channel *sram_ch, + int bpl) +{ + int ret = 0; + dma_addr_t dma_addr; + dma_addr_t data_dma_addr; + + cx25821_free_memory_audio(dev); + + dev->_risc_virt_addr = + pci_alloc_consistent(dev->pci, dev->audio_upstream_riscbuf_size, + &dma_addr); + dev->_risc_virt_start_addr = dev->_risc_virt_addr; + dev->_risc_phys_start_addr = dma_addr; + dev->_risc_phys_addr = dma_addr; + dev->_audiorisc_size = dev->audio_upstream_riscbuf_size; + + if (!dev->_risc_virt_addr) { + printk(KERN_DEBUG + pr_fmt("ERROR: pci_alloc_consistent() FAILED to allocate memory for RISC program! Returning\n")); + return -ENOMEM; + } + /* Clear out memory at address */ + memset(dev->_risc_virt_addr, 0, dev->_audiorisc_size); + + /* For Audio Data buffer allocation */ + dev->_audiodata_buf_virt_addr = + pci_alloc_consistent(dev->pci, dev->audio_upstream_databuf_size, + &data_dma_addr); + dev->_audiodata_buf_phys_addr = data_dma_addr; + dev->_audiodata_buf_size = dev->audio_upstream_databuf_size; + + if (!dev->_audiodata_buf_virt_addr) { + printk(KERN_DEBUG + pr_fmt("ERROR: pci_alloc_consistent() FAILED to allocate memory for data buffer! Returning\n")); + return -ENOMEM; + } + /* Clear out memory at address */ + memset(dev->_audiodata_buf_virt_addr, 0, dev->_audiodata_buf_size); + + ret = cx25821_openfile_audio(dev, sram_ch); + if (ret < 0) + return ret; + + /* Creating RISC programs */ + ret = + cx25821_risc_buffer_upstream_audio(dev, dev->pci, bpl, + dev->_audio_lines_count); + if (ret < 0) { + printk(KERN_DEBUG + pr_fmt("ERROR creating audio upstream RISC programs!\n")); + goto error; + } + + return 0; + +error: + return ret; +} + +int cx25821_audio_upstream_irq(struct cx25821_dev *dev, int chan_num, + u32 status) +{ + int i = 0; + u32 int_msk_tmp; + struct sram_channel *channel = dev->channels[chan_num].sram_channels; + dma_addr_t risc_phys_jump_addr; + __le32 *rp; + + if (status & FLD_AUD_SRC_RISCI1) { + /* Get interrupt_index of the program that interrupted */ + u32 prog_cnt = cx_read(channel->gpcnt); + + /* Since we've identified our IRQ, clear our bits from the + * interrupt mask and interrupt status registers */ + cx_write(channel->int_msk, 0); + cx_write(channel->int_stat, cx_read(channel->int_stat)); + + spin_lock(&dev->slock); + + while (prog_cnt != dev->_last_index_irq) { + /* Update _last_index_irq */ + if (dev->_last_index_irq < (NUMBER_OF_PROGRAMS - 1)) + dev->_last_index_irq++; + else + dev->_last_index_irq = 0; + + dev->_audioframe_index = dev->_last_index_irq; + + queue_work(dev->_irq_audio_queues, + &dev->_audio_work_entry); + } + + if (dev->_is_first_audio_frame) { + dev->_is_first_audio_frame = 0; + + if (dev->_risc_virt_start_addr != NULL) { + risc_phys_jump_addr = + dev->_risc_phys_start_addr + + RISC_SYNC_INSTRUCTION_SIZE + + AUDIO_RISC_DMA_BUF_SIZE; + + rp = cx25821_risc_field_upstream_audio(dev, + dev->_risc_virt_start_addr + 1, + dev->_audiodata_buf_phys_addr, + AUDIO_LINE_SIZE, FIFO_DISABLE); + + if (USE_RISC_NOOP_AUDIO) { + for (i = 0; i < NUM_NO_OPS; i++) { + *(rp++) = + cpu_to_le32(RISC_NOOP); + } + } + /* Jump to 2nd Audio Frame */ + *(rp++) = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | + RISC_CNT_RESET); + *(rp++) = cpu_to_le32(risc_phys_jump_addr); + *(rp++) = cpu_to_le32(0); + } + } + + spin_unlock(&dev->slock); + } else { + if (status & FLD_AUD_SRC_OF) + pr_warn("%s(): Audio Received Overflow Error Interrupt!\n", + __func__); + + if (status & FLD_AUD_SRC_SYNC) + pr_warn("%s(): Audio Received Sync Error Interrupt!\n", + __func__); + + if (status & FLD_AUD_SRC_OPC_ERR) + pr_warn("%s(): Audio Received OpCode Error Interrupt!\n", + __func__); + + /* Read and write back the interrupt status register to clear + * our bits */ + cx_write(channel->int_stat, cx_read(channel->int_stat)); + } + + if (dev->_audiofile_status == END_OF_FILE) { + pr_warn("EOF Channel Audio Framecount = %d\n", + dev->_audioframe_count); + return -1; + } + /* ElSE, set the interrupt mask register, re-enable irq. */ + int_msk_tmp = cx_read(channel->int_msk); + cx_write(channel->int_msk, int_msk_tmp |= _intr_msk); + + return 0; +} + +static irqreturn_t cx25821_upstream_irq_audio(int irq, void *dev_id) +{ + struct cx25821_dev *dev = dev_id; + u32 msk_stat, audio_status; + int handled = 0; + struct sram_channel *sram_ch; + + if (!dev) + return -1; + + sram_ch = dev->channels[dev->_audio_upstream_channel].sram_channels; + + msk_stat = cx_read(sram_ch->int_mstat); + audio_status = cx_read(sram_ch->int_stat); + + /* Only deal with our interrupt */ + if (audio_status) { + handled = cx25821_audio_upstream_irq(dev, + dev->_audio_upstream_channel, audio_status); + } + + if (handled < 0) + cx25821_stop_upstream_audio(dev); + else + handled += handled; + + return IRQ_RETVAL(handled); +} + +static void cx25821_wait_fifo_enable(struct cx25821_dev *dev, + struct sram_channel *sram_ch) +{ + int count = 0; + u32 tmp; + + do { + /* Wait 10 microsecond before checking to see if the FIFO is + * turned ON. */ + udelay(10); + + tmp = cx_read(sram_ch->dma_ctl); + + /* 10 millisecond timeout */ + if (count++ > 1000) { + pr_err("ERROR: %s() fifo is NOT turned on. Timeout!\n", + __func__); + return; + } + + } while (!(tmp & sram_ch->fld_aud_fifo_en)); + +} + +int cx25821_start_audio_dma_upstream(struct cx25821_dev *dev, + struct sram_channel *sram_ch) +{ + u32 tmp = 0; + int err = 0; + + /* Set the physical start address of the RISC program in the initial + * program counter(IPC) member of the CMDS. */ + cx_write(sram_ch->cmds_start + 0, dev->_risc_phys_addr); + /* Risc IPC High 64 bits 63-32 */ + cx_write(sram_ch->cmds_start + 4, 0); + + /* reset counter */ + cx_write(sram_ch->gpcnt_ctl, 3); + + /* Set the line length (It looks like we do not need to set the + * line length) */ + cx_write(sram_ch->aud_length, AUDIO_LINE_SIZE & FLD_AUD_DST_LN_LNGTH); + + /* Set the input mode to 16-bit */ + tmp = cx_read(sram_ch->aud_cfg); + tmp |= + FLD_AUD_SRC_ENABLE | FLD_AUD_DST_PK_MODE | FLD_AUD_CLK_ENABLE | + FLD_AUD_MASTER_MODE | FLD_AUD_CLK_SELECT_PLL_D | FLD_AUD_SONY_MODE; + cx_write(sram_ch->aud_cfg, tmp); + + /* Read and write back the interrupt status register to clear it */ + tmp = cx_read(sram_ch->int_stat); + cx_write(sram_ch->int_stat, tmp); + + /* Clear our bits from the interrupt status register. */ + cx_write(sram_ch->int_stat, _intr_msk); + + /* Set the interrupt mask register, enable irq. */ + cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | (1 << sram_ch->irq_bit)); + tmp = cx_read(sram_ch->int_msk); + cx_write(sram_ch->int_msk, tmp |= _intr_msk); + + err = + request_irq(dev->pci->irq, cx25821_upstream_irq_audio, + IRQF_SHARED, dev->name, dev); + if (err < 0) { + pr_err("%s: can't get upstream IRQ %d\n", + dev->name, dev->pci->irq); + goto fail_irq; + } + + /* Start the DMA engine */ + tmp = cx_read(sram_ch->dma_ctl); + cx_set(sram_ch->dma_ctl, tmp | sram_ch->fld_aud_risc_en); + + dev->_audio_is_running = 1; + dev->_is_first_audio_frame = 1; + + /* The fifo_en bit turns on by the first Risc program */ + cx25821_wait_fifo_enable(dev, sram_ch); + + return 0; + +fail_irq: + cx25821_dev_unregister(dev); + return err; +} + +int cx25821_audio_upstream_init(struct cx25821_dev *dev, int channel_select) +{ + struct sram_channel *sram_ch; + int retval = 0; + int err = 0; + int str_length = 0; + + if (dev->_audio_is_running) { + pr_warn("Audio Channel is still running so return!\n"); + return 0; + } + + dev->_audio_upstream_channel = channel_select; + sram_ch = dev->channels[channel_select].sram_channels; + + /* Work queue */ + INIT_WORK(&dev->_audio_work_entry, cx25821_audioups_handler); + dev->_irq_audio_queues = + create_singlethread_workqueue("cx25821_audioworkqueue"); + + if (!dev->_irq_audio_queues) { + printk(KERN_DEBUG + pr_fmt("ERROR: create_singlethread_workqueue() for Audio FAILED!\n")); + return -ENOMEM; + } + + dev->_last_index_irq = 0; + dev->_audio_is_running = 0; + dev->_audioframe_count = 0; + dev->_audiofile_status = RESET_STATUS; + dev->_audio_lines_count = LINES_PER_AUDIO_BUFFER; + _line_size = AUDIO_LINE_SIZE; + + if (dev->input_audiofilename) { + str_length = strlen(dev->input_audiofilename); + dev->_audiofilename = kmalloc(str_length + 1, GFP_KERNEL); + + if (!dev->_audiofilename) + goto error; + + memcpy(dev->_audiofilename, dev->input_audiofilename, + str_length + 1); + + /* Default if filename is empty string */ + if (strcmp(dev->input_audiofilename, "") == 0) + dev->_audiofilename = "/root/audioGOOD.wav"; + } else { + str_length = strlen(_defaultAudioName); + dev->_audiofilename = kmalloc(str_length + 1, GFP_KERNEL); + + if (!dev->_audiofilename) + goto error; + + memcpy(dev->_audiofilename, _defaultAudioName, str_length + 1); + } + + retval = cx25821_sram_channel_setup_upstream_audio(dev, sram_ch, + _line_size, 0); + + dev->audio_upstream_riscbuf_size = + AUDIO_RISC_DMA_BUF_SIZE * NUM_AUDIO_PROGS + + RISC_SYNC_INSTRUCTION_SIZE; + dev->audio_upstream_databuf_size = AUDIO_DATA_BUF_SZ * NUM_AUDIO_PROGS; + + /* Allocating buffers and prepare RISC program */ + retval = cx25821_audio_upstream_buffer_prepare(dev, sram_ch, + _line_size); + if (retval < 0) { + pr_err("%s: Failed to set up Audio upstream buffers!\n", + dev->name); + goto error; + } + /* Start RISC engine */ + cx25821_start_audio_dma_upstream(dev, sram_ch); + + return 0; + +error: + cx25821_dev_unregister(dev); + + return err; +} diff --git a/drivers/media/video/cx25821/cx25821-audio-upstream.h b/drivers/media/video/cx25821/cx25821-audio-upstream.h new file mode 100644 index 00000000000..af2ae7c5815 --- /dev/null +++ b/drivers/media/video/cx25821/cx25821-audio-upstream.h @@ -0,0 +1,62 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <hiep.huynh@conexant.com>, <shu.lin@conexant.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/mutex.h> +#include <linux/workqueue.h> + +#define NUM_AUDIO_PROGS 8 +#define NUM_AUDIO_FRAMES 8 +#define END_OF_FILE 0 +#define IN_PROGRESS 1 +#define RESET_STATUS -1 +#define FIFO_DISABLE 0 +#define FIFO_ENABLE 1 +#define NUM_NO_OPS 4 + +#define RISC_READ_INSTRUCTION_SIZE 12 +#define RISC_JUMP_INSTRUCTION_SIZE 12 +#define RISC_WRITECR_INSTRUCTION_SIZE 16 +#define RISC_SYNC_INSTRUCTION_SIZE 4 +#define DWORD_SIZE 4 +#define AUDIO_SYNC_LINE 4 + +#define LINES_PER_AUDIO_BUFFER 15 +#define AUDIO_LINE_SIZE 128 +#define AUDIO_DATA_BUF_SZ (AUDIO_LINE_SIZE * LINES_PER_AUDIO_BUFFER) + +#define USE_RISC_NOOP_AUDIO 1 + +#ifdef USE_RISC_NOOP_AUDIO +#define AUDIO_RISC_DMA_BUF_SIZE \ + (LINES_PER_AUDIO_BUFFER * RISC_READ_INSTRUCTION_SIZE + \ + RISC_WRITECR_INSTRUCTION_SIZE + NUM_NO_OPS * DWORD_SIZE + \ + RISC_JUMP_INSTRUCTION_SIZE) +#endif + +#ifndef USE_RISC_NOOP_AUDIO +#define AUDIO_RISC_DMA_BUF_SIZE \ + (LINES_PER_AUDIO_BUFFER * RISC_READ_INSTRUCTION_SIZE + \ + RISC_WRITECR_INSTRUCTION_SIZE + RISC_JUMP_INSTRUCTION_SIZE) +#endif + +static int _line_size; +char *_defaultAudioName = "/root/audioGOOD.wav"; diff --git a/drivers/media/video/cx25821/cx25821-audio.h b/drivers/media/video/cx25821/cx25821-audio.h new file mode 100644 index 00000000000..8eb55b7b88c --- /dev/null +++ b/drivers/media/video/cx25821/cx25821-audio.h @@ -0,0 +1,61 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __CX25821_AUDIO_H__ +#define __CX25821_AUDIO_H__ + +#define USE_RISC_NOOP 1 +#define LINES_PER_BUFFER 15 +#define AUDIO_LINE_SIZE 128 + +/* Number of buffer programs to use at once. */ +#define NUMBER_OF_PROGRAMS 8 + +/* + * Max size of the RISC program for a buffer. - worst case is 2 writes per line + * Space is also added for the 4 no-op instructions added on the end. + */ +#ifndef USE_RISC_NOOP +#define MAX_BUFFER_PROGRAM_SIZE \ + (2 * LINES_PER_BUFFER * RISC_WRITE_INSTRUCTION_SIZE + \ + RISC_WRITECR_INSTRUCTION_SIZE * 4) +#endif + +/* MAE 12 July 2005 Try to use NOOP RISC instruction instead */ +#ifdef USE_RISC_NOOP +#define MAX_BUFFER_PROGRAM_SIZE \ + (2 * LINES_PER_BUFFER * RISC_WRITE_INSTRUCTION_SIZE + \ + RISC_NOOP_INSTRUCTION_SIZE * 4) +#endif + +/* Sizes of various instructions in bytes. Used when adding instructions. */ +#define RISC_WRITE_INSTRUCTION_SIZE 12 +#define RISC_JUMP_INSTRUCTION_SIZE 12 +#define RISC_SKIP_INSTRUCTION_SIZE 4 +#define RISC_SYNC_INSTRUCTION_SIZE 4 +#define RISC_WRITECR_INSTRUCTION_SIZE 16 +#define RISC_NOOP_INSTRUCTION_SIZE 4 + +#define MAX_AUDIO_DMA_BUFFER_SIZE \ +(MAX_BUFFER_PROGRAM_SIZE * NUMBER_OF_PROGRAMS + RISC_SYNC_INSTRUCTION_SIZE) + +#endif diff --git a/drivers/media/video/cx25821/cx25821-biffuncs.h b/drivers/media/video/cx25821/cx25821-biffuncs.h new file mode 100644 index 00000000000..9326a7c729e --- /dev/null +++ b/drivers/media/video/cx25821/cx25821-biffuncs.h @@ -0,0 +1,45 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _BITFUNCS_H +#define _BITFUNCS_H + +#define SetBit(Bit) (1 << Bit) + +inline u8 getBit(u32 sample, u8 index) +{ + return (u8) ((sample >> index) & 1); +} + +inline u32 clearBitAtPos(u32 value, u8 bit) +{ + return value & ~(1 << bit); +} + +inline u32 setBitAtPos(u32 sample, u8 bit) +{ + sample |= (1 << bit); + return sample; + +} + +#endif diff --git a/drivers/media/video/cx25821/cx25821-cards.c b/drivers/media/video/cx25821/cx25821-cards.c new file mode 100644 index 00000000000..6ace60313b4 --- /dev/null +++ b/drivers/media/video/cx25821/cx25821-cards.c @@ -0,0 +1,72 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> + * Based on Steven Toth <stoth@linuxtv.org> cx23885 driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <media/cx25840.h> + +#include "cx25821.h" +#include "tuner-xc2028.h" + +/* board config info */ + +struct cx25821_board cx25821_boards[] = { + [UNKNOWN_BOARD] = { + .name = "UNKNOWN/GENERIC", + /* Ensure safe default for unknown boards */ + .clk_freq = 0, + }, + + [CX25821_BOARD] = { + .name = "CX25821", + .portb = CX25821_RAW, + .portc = CX25821_264, + .input[0].type = CX25821_VMUX_COMPOSITE, + }, + +}; + +const unsigned int cx25821_bcount = ARRAY_SIZE(cx25821_boards); + +struct cx25821_subid cx25821_subids[] = { + { + .subvendor = 0x14f1, + .subdevice = 0x0920, + .card = CX25821_BOARD, + }, +}; + +void cx25821_card_setup(struct cx25821_dev *dev) +{ + static u8 eeprom[256]; + + if (dev->i2c_bus[0].i2c_rc == 0) { + dev->i2c_bus[0].i2c_client.addr = 0xa0 >> 1; + tveeprom_read(&dev->i2c_bus[0].i2c_client, eeprom, + sizeof(eeprom)); + } +} diff --git a/drivers/media/video/cx25821/cx25821-core.c b/drivers/media/video/cx25821/cx25821-core.c new file mode 100644 index 00000000000..a7fa38f9594 --- /dev/null +++ b/drivers/media/video/cx25821/cx25821-core.c @@ -0,0 +1,1517 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> + * Based on Steven Toth <stoth@linuxtv.org> cx23885 driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/i2c.h> +#include <linux/slab.h> +#include "cx25821.h" +#include "cx25821-sram.h" +#include "cx25821-video.h" + +MODULE_DESCRIPTION("Driver for Athena cards"); +MODULE_AUTHOR("Shu Lin - Hiep Huynh"); +MODULE_LICENSE("GPL"); + +static unsigned int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "enable debug messages"); + +static unsigned int card[] = {[0 ... (CX25821_MAXBOARDS - 1)] = UNSET }; +module_param_array(card, int, NULL, 0444); +MODULE_PARM_DESC(card, "card type"); + +static unsigned int cx25821_devcount; + +DEFINE_MUTEX(cx25821_devlist_mutex); +EXPORT_SYMBOL(cx25821_devlist_mutex); +LIST_HEAD(cx25821_devlist); +EXPORT_SYMBOL(cx25821_devlist); + +struct sram_channel cx25821_sram_channels[] = { + [SRAM_CH00] = { + .i = SRAM_CH00, + .name = "VID A", + .cmds_start = VID_A_DOWN_CMDS, + .ctrl_start = VID_A_IQ, + .cdt = VID_A_CDT, + .fifo_start = VID_A_DOWN_CLUSTER_1, + .fifo_size = (VID_CLUSTER_SIZE << 2), + .ptr1_reg = DMA1_PTR1, + .ptr2_reg = DMA1_PTR2, + .cnt1_reg = DMA1_CNT1, + .cnt2_reg = DMA1_CNT2, + .int_msk = VID_A_INT_MSK, + .int_stat = VID_A_INT_STAT, + .int_mstat = VID_A_INT_MSTAT, + .dma_ctl = VID_DST_A_DMA_CTL, + .gpcnt_ctl = VID_DST_A_GPCNT_CTL, + .gpcnt = VID_DST_A_GPCNT, + .vip_ctl = VID_DST_A_VIP_CTL, + .pix_frmt = VID_DST_A_PIX_FRMT, + }, + + [SRAM_CH01] = { + .i = SRAM_CH01, + .name = "VID B", + .cmds_start = VID_B_DOWN_CMDS, + .ctrl_start = VID_B_IQ, + .cdt = VID_B_CDT, + .fifo_start = VID_B_DOWN_CLUSTER_1, + .fifo_size = (VID_CLUSTER_SIZE << 2), + .ptr1_reg = DMA2_PTR1, + .ptr2_reg = DMA2_PTR2, + .cnt1_reg = DMA2_CNT1, + .cnt2_reg = DMA2_CNT2, + .int_msk = VID_B_INT_MSK, + .int_stat = VID_B_INT_STAT, + .int_mstat = VID_B_INT_MSTAT, + .dma_ctl = VID_DST_B_DMA_CTL, + .gpcnt_ctl = VID_DST_B_GPCNT_CTL, + .gpcnt = VID_DST_B_GPCNT, + .vip_ctl = VID_DST_B_VIP_CTL, + .pix_frmt = VID_DST_B_PIX_FRMT, + }, + + [SRAM_CH02] = { + .i = SRAM_CH02, + .name = "VID C", + .cmds_start = VID_C_DOWN_CMDS, + .ctrl_start = VID_C_IQ, + .cdt = VID_C_CDT, + .fifo_start = VID_C_DOWN_CLUSTER_1, + .fifo_size = (VID_CLUSTER_SIZE << 2), + .ptr1_reg = DMA3_PTR1, + .ptr2_reg = DMA3_PTR2, + .cnt1_reg = DMA3_CNT1, + .cnt2_reg = DMA3_CNT2, + .int_msk = VID_C_INT_MSK, + .int_stat = VID_C_INT_STAT, + .int_mstat = VID_C_INT_MSTAT, + .dma_ctl = VID_DST_C_DMA_CTL, + .gpcnt_ctl = VID_DST_C_GPCNT_CTL, + .gpcnt = VID_DST_C_GPCNT, + .vip_ctl = VID_DST_C_VIP_CTL, + .pix_frmt = VID_DST_C_PIX_FRMT, + }, + + [SRAM_CH03] = { + .i = SRAM_CH03, + .name = "VID D", + .cmds_start = VID_D_DOWN_CMDS, + .ctrl_start = VID_D_IQ, + .cdt = VID_D_CDT, + .fifo_start = VID_D_DOWN_CLUSTER_1, + .fifo_size = (VID_CLUSTER_SIZE << 2), + .ptr1_reg = DMA4_PTR1, + .ptr2_reg = DMA4_PTR2, + .cnt1_reg = DMA4_CNT1, + .cnt2_reg = DMA4_CNT2, + .int_msk = VID_D_INT_MSK, + .int_stat = VID_D_INT_STAT, + .int_mstat = VID_D_INT_MSTAT, + .dma_ctl = VID_DST_D_DMA_CTL, + .gpcnt_ctl = VID_DST_D_GPCNT_CTL, + .gpcnt = VID_DST_D_GPCNT, + .vip_ctl = VID_DST_D_VIP_CTL, + .pix_frmt = VID_DST_D_PIX_FRMT, + }, + + [SRAM_CH04] = { + .i = SRAM_CH04, + .name = "VID E", + .cmds_start = VID_E_DOWN_CMDS, + .ctrl_start = VID_E_IQ, + .cdt = VID_E_CDT, + .fifo_start = VID_E_DOWN_CLUSTER_1, + .fifo_size = (VID_CLUSTER_SIZE << 2), + .ptr1_reg = DMA5_PTR1, + .ptr2_reg = DMA5_PTR2, + .cnt1_reg = DMA5_CNT1, + .cnt2_reg = DMA5_CNT2, + .int_msk = VID_E_INT_MSK, + .int_stat = VID_E_INT_STAT, + .int_mstat = VID_E_INT_MSTAT, + .dma_ctl = VID_DST_E_DMA_CTL, + .gpcnt_ctl = VID_DST_E_GPCNT_CTL, + .gpcnt = VID_DST_E_GPCNT, + .vip_ctl = VID_DST_E_VIP_CTL, + .pix_frmt = VID_DST_E_PIX_FRMT, + }, + + [SRAM_CH05] = { + .i = SRAM_CH05, + .name = "VID F", + .cmds_start = VID_F_DOWN_CMDS, + .ctrl_start = VID_F_IQ, + .cdt = VID_F_CDT, + .fifo_start = VID_F_DOWN_CLUSTER_1, + .fifo_size = (VID_CLUSTER_SIZE << 2), + .ptr1_reg = DMA6_PTR1, + .ptr2_reg = DMA6_PTR2, + .cnt1_reg = DMA6_CNT1, + .cnt2_reg = DMA6_CNT2, + .int_msk = VID_F_INT_MSK, + .int_stat = VID_F_INT_STAT, + .int_mstat = VID_F_INT_MSTAT, + .dma_ctl = VID_DST_F_DMA_CTL, + .gpcnt_ctl = VID_DST_F_GPCNT_CTL, + .gpcnt = VID_DST_F_GPCNT, + .vip_ctl = VID_DST_F_VIP_CTL, + .pix_frmt = VID_DST_F_PIX_FRMT, + }, + + [SRAM_CH06] = { + .i = SRAM_CH06, + .name = "VID G", + .cmds_start = VID_G_DOWN_CMDS, + .ctrl_start = VID_G_IQ, + .cdt = VID_G_CDT, + .fifo_start = VID_G_DOWN_CLUSTER_1, + .fifo_size = (VID_CLUSTER_SIZE << 2), + .ptr1_reg = DMA7_PTR1, + .ptr2_reg = DMA7_PTR2, + .cnt1_reg = DMA7_CNT1, + .cnt2_reg = DMA7_CNT2, + .int_msk = VID_G_INT_MSK, + .int_stat = VID_G_INT_STAT, + .int_mstat = VID_G_INT_MSTAT, + .dma_ctl = VID_DST_G_DMA_CTL, + .gpcnt_ctl = VID_DST_G_GPCNT_CTL, + .gpcnt = VID_DST_G_GPCNT, + .vip_ctl = VID_DST_G_VIP_CTL, + .pix_frmt = VID_DST_G_PIX_FRMT, + }, + + [SRAM_CH07] = { + .i = SRAM_CH07, + .name = "VID H", + .cmds_start = VID_H_DOWN_CMDS, + .ctrl_start = VID_H_IQ, + .cdt = VID_H_CDT, + .fifo_start = VID_H_DOWN_CLUSTER_1, + .fifo_size = (VID_CLUSTER_SIZE << 2), + .ptr1_reg = DMA8_PTR1, + .ptr2_reg = DMA8_PTR2, + .cnt1_reg = DMA8_CNT1, + .cnt2_reg = DMA8_CNT2, + .int_msk = VID_H_INT_MSK, + .int_stat = VID_H_INT_STAT, + .int_mstat = VID_H_INT_MSTAT, + .dma_ctl = VID_DST_H_DMA_CTL, + .gpcnt_ctl = VID_DST_H_GPCNT_CTL, + .gpcnt = VID_DST_H_GPCNT, + .vip_ctl = VID_DST_H_VIP_CTL, + .pix_frmt = VID_DST_H_PIX_FRMT, + }, + + [SRAM_CH08] = { + .name = "audio from", + .cmds_start = AUD_A_DOWN_CMDS, + .ctrl_start = AUD_A_IQ, + .cdt = AUD_A_CDT, + .fifo_start = AUD_A_DOWN_CLUSTER_1, + .fifo_size = AUDIO_CLUSTER_SIZE * 3, + .ptr1_reg = DMA17_PTR1, + .ptr2_reg = DMA17_PTR2, + .cnt1_reg = DMA17_CNT1, + .cnt2_reg = DMA17_CNT2, + }, + + [SRAM_CH09] = { + .i = SRAM_CH09, + .name = "VID Upstream I", + .cmds_start = VID_I_UP_CMDS, + .ctrl_start = VID_I_IQ, + .cdt = VID_I_CDT, + .fifo_start = VID_I_UP_CLUSTER_1, + .fifo_size = (VID_CLUSTER_SIZE << 2), + .ptr1_reg = DMA15_PTR1, + .ptr2_reg = DMA15_PTR2, + .cnt1_reg = DMA15_CNT1, + .cnt2_reg = DMA15_CNT2, + .int_msk = VID_I_INT_MSK, + .int_stat = VID_I_INT_STAT, + .int_mstat = VID_I_INT_MSTAT, + .dma_ctl = VID_SRC_I_DMA_CTL, + .gpcnt_ctl = VID_SRC_I_GPCNT_CTL, + .gpcnt = VID_SRC_I_GPCNT, + + .vid_fmt_ctl = VID_SRC_I_FMT_CTL, + .vid_active_ctl1 = VID_SRC_I_ACTIVE_CTL1, + .vid_active_ctl2 = VID_SRC_I_ACTIVE_CTL2, + .vid_cdt_size = VID_SRC_I_CDT_SZ, + .irq_bit = 8, + }, + + [SRAM_CH10] = { + .i = SRAM_CH10, + .name = "VID Upstream J", + .cmds_start = VID_J_UP_CMDS, + .ctrl_start = VID_J_IQ, + .cdt = VID_J_CDT, + .fifo_start = VID_J_UP_CLUSTER_1, + .fifo_size = (VID_CLUSTER_SIZE << 2), + .ptr1_reg = DMA16_PTR1, + .ptr2_reg = DMA16_PTR2, + .cnt1_reg = DMA16_CNT1, + .cnt2_reg = DMA16_CNT2, + .int_msk = VID_J_INT_MSK, + .int_stat = VID_J_INT_STAT, + .int_mstat = VID_J_INT_MSTAT, + .dma_ctl = VID_SRC_J_DMA_CTL, + .gpcnt_ctl = VID_SRC_J_GPCNT_CTL, + .gpcnt = VID_SRC_J_GPCNT, + + .vid_fmt_ctl = VID_SRC_J_FMT_CTL, + .vid_active_ctl1 = VID_SRC_J_ACTIVE_CTL1, + .vid_active_ctl2 = VID_SRC_J_ACTIVE_CTL2, + .vid_cdt_size = VID_SRC_J_CDT_SZ, + .irq_bit = 9, + }, + + [SRAM_CH11] = { + .i = SRAM_CH11, + .name = "Audio Upstream Channel B", + .cmds_start = AUD_B_UP_CMDS, + .ctrl_start = AUD_B_IQ, + .cdt = AUD_B_CDT, + .fifo_start = AUD_B_UP_CLUSTER_1, + .fifo_size = (AUDIO_CLUSTER_SIZE * 3), + .ptr1_reg = DMA22_PTR1, + .ptr2_reg = DMA22_PTR2, + .cnt1_reg = DMA22_CNT1, + .cnt2_reg = DMA22_CNT2, + .int_msk = AUD_B_INT_MSK, + .int_stat = AUD_B_INT_STAT, + .int_mstat = AUD_B_INT_MSTAT, + .dma_ctl = AUD_INT_DMA_CTL, + .gpcnt_ctl = AUD_B_GPCNT_CTL, + .gpcnt = AUD_B_GPCNT, + .aud_length = AUD_B_LNGTH, + .aud_cfg = AUD_B_CFG, + .fld_aud_fifo_en = FLD_AUD_SRC_B_FIFO_EN, + .fld_aud_risc_en = FLD_AUD_SRC_B_RISC_EN, + .irq_bit = 11, + }, +}; +EXPORT_SYMBOL(cx25821_sram_channels); + +struct sram_channel *channel0 = &cx25821_sram_channels[SRAM_CH00]; +struct sram_channel *channel1 = &cx25821_sram_channels[SRAM_CH01]; +struct sram_channel *channel2 = &cx25821_sram_channels[SRAM_CH02]; +struct sram_channel *channel3 = &cx25821_sram_channels[SRAM_CH03]; +struct sram_channel *channel4 = &cx25821_sram_channels[SRAM_CH04]; +struct sram_channel *channel5 = &cx25821_sram_channels[SRAM_CH05]; +struct sram_channel *channel6 = &cx25821_sram_channels[SRAM_CH06]; +struct sram_channel *channel7 = &cx25821_sram_channels[SRAM_CH07]; +struct sram_channel *channel9 = &cx25821_sram_channels[SRAM_CH09]; +struct sram_channel *channel10 = &cx25821_sram_channels[SRAM_CH10]; +struct sram_channel *channel11 = &cx25821_sram_channels[SRAM_CH11]; + +struct cx25821_dmaqueue mpegq; + +static int cx25821_risc_decode(u32 risc) +{ + static const char * const instr[16] = { + [RISC_SYNC >> 28] = "sync", + [RISC_WRITE >> 28] = "write", + [RISC_WRITEC >> 28] = "writec", + [RISC_READ >> 28] = "read", + [RISC_READC >> 28] = "readc", + [RISC_JUMP >> 28] = "jump", + [RISC_SKIP >> 28] = "skip", + [RISC_WRITERM >> 28] = "writerm", + [RISC_WRITECM >> 28] = "writecm", + [RISC_WRITECR >> 28] = "writecr", + }; + static const int incr[16] = { + [RISC_WRITE >> 28] = 3, + [RISC_JUMP >> 28] = 3, + [RISC_SKIP >> 28] = 1, + [RISC_SYNC >> 28] = 1, + [RISC_WRITERM >> 28] = 3, + [RISC_WRITECM >> 28] = 3, + [RISC_WRITECR >> 28] = 4, + }; + static const char * const bits[] = { + "12", "13", "14", "resync", + "cnt0", "cnt1", "18", "19", + "20", "21", "22", "23", + "irq1", "irq2", "eol", "sol", + }; + int i; + + pr_cont("0x%08x [ %s", + risc, instr[risc >> 28] ? instr[risc >> 28] : "INVALID"); + for (i = ARRAY_SIZE(bits) - 1; i >= 0; i--) { + if (risc & (1 << (i + 12))) + pr_cont(" %s", bits[i]); + } + pr_cont(" count=%d ]\n", risc & 0xfff); + return incr[risc >> 28] ? incr[risc >> 28] : 1; +} + +static inline int i2c_slave_did_ack(struct i2c_adapter *i2c_adap) +{ + struct cx25821_i2c *bus = i2c_adap->algo_data; + struct cx25821_dev *dev = bus->dev; + return cx_read(bus->reg_stat) & 0x01; +} + +void cx_i2c_read_print(struct cx25821_dev *dev, u32 reg, const char *reg_string) +{ + int tmp = 0; + u32 value = 0; + + value = cx25821_i2c_read(&dev->i2c_bus[0], reg, &tmp); +} + +static void cx25821_registers_init(struct cx25821_dev *dev) +{ + u32 tmp; + + /* enable RUN_RISC in Pecos */ + cx_write(DEV_CNTRL2, 0x20); + + /* Set the master PCI interrupt masks to enable video, audio, MBIF, + * and GPIO interrupts + * I2C interrupt masking is handled by the I2C objects themselves. */ + cx_write(PCI_INT_MSK, 0x2001FFFF); + + tmp = cx_read(RDR_TLCTL0); + tmp &= ~FLD_CFG_RCB_CK_EN; /* Clear the RCB_CK_EN bit */ + cx_write(RDR_TLCTL0, tmp); + + /* PLL-A setting for the Audio Master Clock */ + cx_write(PLL_A_INT_FRAC, 0x9807A58B); + + /* PLL_A_POST = 0x1C, PLL_A_OUT_TO_PIN = 0x1 */ + cx_write(PLL_A_POST_STAT_BIST, 0x8000019C); + + /* clear reset bit [31] */ + tmp = cx_read(PLL_A_INT_FRAC); + cx_write(PLL_A_INT_FRAC, tmp & 0x7FFFFFFF); + + /* PLL-B setting for Mobilygen Host Bus Interface */ + cx_write(PLL_B_INT_FRAC, 0x9883A86F); + + /* PLL_B_POST = 0xD, PLL_B_OUT_TO_PIN = 0x0 */ + cx_write(PLL_B_POST_STAT_BIST, 0x8000018D); + + /* clear reset bit [31] */ + tmp = cx_read(PLL_B_INT_FRAC); + cx_write(PLL_B_INT_FRAC, tmp & 0x7FFFFFFF); + + /* PLL-C setting for video upstream channel */ + cx_write(PLL_C_INT_FRAC, 0x96A0EA3F); + + /* PLL_C_POST = 0x3, PLL_C_OUT_TO_PIN = 0x0 */ + cx_write(PLL_C_POST_STAT_BIST, 0x80000103); + + /* clear reset bit [31] */ + tmp = cx_read(PLL_C_INT_FRAC); + cx_write(PLL_C_INT_FRAC, tmp & 0x7FFFFFFF); + + /* PLL-D setting for audio upstream channel */ + cx_write(PLL_D_INT_FRAC, 0x98757F5B); + + /* PLL_D_POST = 0x13, PLL_D_OUT_TO_PIN = 0x0 */ + cx_write(PLL_D_POST_STAT_BIST, 0x80000113); + + /* clear reset bit [31] */ + tmp = cx_read(PLL_D_INT_FRAC); + cx_write(PLL_D_INT_FRAC, tmp & 0x7FFFFFFF); + + /* This selects the PLL C clock source for the video upstream channel + * I and J */ + tmp = cx_read(VID_CH_CLK_SEL); + cx_write(VID_CH_CLK_SEL, (tmp & 0x00FFFFFF) | 0x24000000); + + /* 656/VIP SRC Upstream Channel I & J and 7 - Host Bus Interface for + * channel A-C + * select 656/VIP DST for downstream Channel A - C */ + tmp = cx_read(VID_CH_MODE_SEL); + /* cx_write( VID_CH_MODE_SEL, tmp | 0x1B0001FF); */ + cx_write(VID_CH_MODE_SEL, tmp & 0xFFFFFE00); + + /* enables 656 port I and J as output */ + tmp = cx_read(CLK_RST); + /* use external ALT_PLL_REF pin as its reference clock instead */ + tmp |= FLD_USE_ALT_PLL_REF; + cx_write(CLK_RST, tmp & ~(FLD_VID_I_CLK_NOE | FLD_VID_J_CLK_NOE)); + + mdelay(100); +} + +int cx25821_sram_channel_setup(struct cx25821_dev *dev, + struct sram_channel *ch, + unsigned int bpl, u32 risc) +{ + unsigned int i, lines; + u32 cdt; + + if (ch->cmds_start == 0) { + cx_write(ch->ptr1_reg, 0); + cx_write(ch->ptr2_reg, 0); + cx_write(ch->cnt2_reg, 0); + cx_write(ch->cnt1_reg, 0); + return 0; + } + + bpl = (bpl + 7) & ~7; /* alignment */ + cdt = ch->cdt; + lines = ch->fifo_size / bpl; + + if (lines > 4) + lines = 4; + + BUG_ON(lines < 2); + + cx_write(8 + 0, RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); + cx_write(8 + 4, 8); + cx_write(8 + 8, 0); + + /* write CDT */ + for (i = 0; i < lines; i++) { + cx_write(cdt + 16 * i, ch->fifo_start + bpl * i); + cx_write(cdt + 16 * i + 4, 0); + cx_write(cdt + 16 * i + 8, 0); + cx_write(cdt + 16 * i + 12, 0); + } + + /* init the first cdt buffer */ + for (i = 0; i < 128; i++) + cx_write(ch->fifo_start + 4 * i, i); + + /* write CMDS */ + if (ch->jumponly) + cx_write(ch->cmds_start + 0, 8); + else + cx_write(ch->cmds_start + 0, risc); + + cx_write(ch->cmds_start + 4, 0); /* 64 bits 63-32 */ + cx_write(ch->cmds_start + 8, cdt); + cx_write(ch->cmds_start + 12, (lines * 16) >> 3); + cx_write(ch->cmds_start + 16, ch->ctrl_start); + + if (ch->jumponly) + cx_write(ch->cmds_start + 20, 0x80000000 | (64 >> 2)); + else + cx_write(ch->cmds_start + 20, 64 >> 2); + + for (i = 24; i < 80; i += 4) + cx_write(ch->cmds_start + i, 0); + + /* fill registers */ + cx_write(ch->ptr1_reg, ch->fifo_start); + cx_write(ch->ptr2_reg, cdt); + cx_write(ch->cnt2_reg, (lines * 16) >> 3); + cx_write(ch->cnt1_reg, (bpl >> 3) - 1); + + return 0; +} +EXPORT_SYMBOL(cx25821_sram_channel_setup); + +int cx25821_sram_channel_setup_audio(struct cx25821_dev *dev, + struct sram_channel *ch, + unsigned int bpl, u32 risc) +{ + unsigned int i, lines; + u32 cdt; + + if (ch->cmds_start == 0) { + cx_write(ch->ptr1_reg, 0); + cx_write(ch->ptr2_reg, 0); + cx_write(ch->cnt2_reg, 0); + cx_write(ch->cnt1_reg, 0); + return 0; + } + + bpl = (bpl + 7) & ~7; /* alignment */ + cdt = ch->cdt; + lines = ch->fifo_size / bpl; + + if (lines > 3) + lines = 3; /* for AUDIO */ + + BUG_ON(lines < 2); + + cx_write(8 + 0, RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); + cx_write(8 + 4, 8); + cx_write(8 + 8, 0); + + /* write CDT */ + for (i = 0; i < lines; i++) { + cx_write(cdt + 16 * i, ch->fifo_start + bpl * i); + cx_write(cdt + 16 * i + 4, 0); + cx_write(cdt + 16 * i + 8, 0); + cx_write(cdt + 16 * i + 12, 0); + } + + /* write CMDS */ + if (ch->jumponly) + cx_write(ch->cmds_start + 0, 8); + else + cx_write(ch->cmds_start + 0, risc); + + cx_write(ch->cmds_start + 4, 0); /* 64 bits 63-32 */ + cx_write(ch->cmds_start + 8, cdt); + cx_write(ch->cmds_start + 12, (lines * 16) >> 3); + cx_write(ch->cmds_start + 16, ch->ctrl_start); + + /* IQ size */ + if (ch->jumponly) + cx_write(ch->cmds_start + 20, 0x80000000 | (64 >> 2)); + else + cx_write(ch->cmds_start + 20, 64 >> 2); + + /* zero out */ + for (i = 24; i < 80; i += 4) + cx_write(ch->cmds_start + i, 0); + + /* fill registers */ + cx_write(ch->ptr1_reg, ch->fifo_start); + cx_write(ch->ptr2_reg, cdt); + cx_write(ch->cnt2_reg, (lines * 16) >> 3); + cx_write(ch->cnt1_reg, (bpl >> 3) - 1); + + return 0; +} +EXPORT_SYMBOL(cx25821_sram_channel_setup_audio); + +void cx25821_sram_channel_dump(struct cx25821_dev *dev, struct sram_channel *ch) +{ + static char *name[] = { + "init risc lo", + "init risc hi", + "cdt base", + "cdt size", + "iq base", + "iq size", + "risc pc lo", + "risc pc hi", + "iq wr ptr", + "iq rd ptr", + "cdt current", + "pci target lo", + "pci target hi", + "line / byte", + }; + u32 risc; + unsigned int i, j, n; + + pr_warn("%s: %s - dma channel status dump\n", dev->name, ch->name); + for (i = 0; i < ARRAY_SIZE(name); i++) + pr_warn("cmds + 0x%2x: %-15s: 0x%08x\n", + i * 4, name[i], cx_read(ch->cmds_start + 4 * i)); + + j = i * 4; + for (i = 0; i < 4;) { + risc = cx_read(ch->cmds_start + 4 * (i + 14)); + pr_warn("cmds + 0x%2x: risc%d: ", j + i * 4, i); + i += cx25821_risc_decode(risc); + } + + for (i = 0; i < (64 >> 2); i += n) { + risc = cx_read(ch->ctrl_start + 4 * i); + /* No consideration for bits 63-32 */ + + pr_warn("ctrl + 0x%2x (0x%08x): iq %x: ", + i * 4, ch->ctrl_start + 4 * i, i); + n = cx25821_risc_decode(risc); + for (j = 1; j < n; j++) { + risc = cx_read(ch->ctrl_start + 4 * (i + j)); + pr_warn("ctrl + 0x%2x : iq %x: 0x%08x [ arg #%d ]\n", + 4 * (i + j), i + j, risc, j); + } + } + + pr_warn(" : fifo: 0x%08x -> 0x%x\n", + ch->fifo_start, ch->fifo_start + ch->fifo_size); + pr_warn(" : ctrl: 0x%08x -> 0x%x\n", + ch->ctrl_start, ch->ctrl_start + 6 * 16); + pr_warn(" : ptr1_reg: 0x%08x\n", + cx_read(ch->ptr1_reg)); + pr_warn(" : ptr2_reg: 0x%08x\n", + cx_read(ch->ptr2_reg)); + pr_warn(" : cnt1_reg: 0x%08x\n", + cx_read(ch->cnt1_reg)); + pr_warn(" : cnt2_reg: 0x%08x\n", + cx_read(ch->cnt2_reg)); +} +EXPORT_SYMBOL(cx25821_sram_channel_dump); + +void cx25821_sram_channel_dump_audio(struct cx25821_dev *dev, + struct sram_channel *ch) +{ + static const char * const name[] = { + "init risc lo", + "init risc hi", + "cdt base", + "cdt size", + "iq base", + "iq size", + "risc pc lo", + "risc pc hi", + "iq wr ptr", + "iq rd ptr", + "cdt current", + "pci target lo", + "pci target hi", + "line / byte", + }; + + u32 risc, value, tmp; + unsigned int i, j, n; + + pr_info("\n%s: %s - dma Audio channel status dump\n", + dev->name, ch->name); + + for (i = 0; i < ARRAY_SIZE(name); i++) + pr_info("%s: cmds + 0x%2x: %-15s: 0x%08x\n", + dev->name, i * 4, name[i], + cx_read(ch->cmds_start + 4 * i)); + + j = i * 4; + for (i = 0; i < 4;) { + risc = cx_read(ch->cmds_start + 4 * (i + 14)); + pr_warn("cmds + 0x%2x: risc%d: ", j + i * 4, i); + i += cx25821_risc_decode(risc); + } + + for (i = 0; i < (64 >> 2); i += n) { + risc = cx_read(ch->ctrl_start + 4 * i); + /* No consideration for bits 63-32 */ + + pr_warn("ctrl + 0x%2x (0x%08x): iq %x: ", + i * 4, ch->ctrl_start + 4 * i, i); + n = cx25821_risc_decode(risc); + + for (j = 1; j < n; j++) { + risc = cx_read(ch->ctrl_start + 4 * (i + j)); + pr_warn("ctrl + 0x%2x : iq %x: 0x%08x [ arg #%d ]\n", + 4 * (i + j), i + j, risc, j); + } + } + + pr_warn(" : fifo: 0x%08x -> 0x%x\n", + ch->fifo_start, ch->fifo_start + ch->fifo_size); + pr_warn(" : ctrl: 0x%08x -> 0x%x\n", + ch->ctrl_start, ch->ctrl_start + 6 * 16); + pr_warn(" : ptr1_reg: 0x%08x\n", + cx_read(ch->ptr1_reg)); + pr_warn(" : ptr2_reg: 0x%08x\n", + cx_read(ch->ptr2_reg)); + pr_warn(" : cnt1_reg: 0x%08x\n", + cx_read(ch->cnt1_reg)); + pr_warn(" : cnt2_reg: 0x%08x\n", + cx_read(ch->cnt2_reg)); + + for (i = 0; i < 4; i++) { + risc = cx_read(ch->cmds_start + 56 + (i * 4)); + pr_warn("instruction %d = 0x%x\n", i, risc); + } + + /* read data from the first cdt buffer */ + risc = cx_read(AUD_A_CDT); + pr_warn("\nread cdt loc=0x%x\n", risc); + for (i = 0; i < 8; i++) { + n = cx_read(risc + i * 4); + pr_cont("0x%x ", n); + } + pr_cont("\n\n"); + + value = cx_read(CLK_RST); + CX25821_INFO(" CLK_RST = 0x%x\n\n", value); + + value = cx_read(PLL_A_POST_STAT_BIST); + CX25821_INFO(" PLL_A_POST_STAT_BIST = 0x%x\n\n", value); + value = cx_read(PLL_A_INT_FRAC); + CX25821_INFO(" PLL_A_INT_FRAC = 0x%x\n\n", value); + + value = cx_read(PLL_B_POST_STAT_BIST); + CX25821_INFO(" PLL_B_POST_STAT_BIST = 0x%x\n\n", value); + value = cx_read(PLL_B_INT_FRAC); + CX25821_INFO(" PLL_B_INT_FRAC = 0x%x\n\n", value); + + value = cx_read(PLL_C_POST_STAT_BIST); + CX25821_INFO(" PLL_C_POST_STAT_BIST = 0x%x\n\n", value); + value = cx_read(PLL_C_INT_FRAC); + CX25821_INFO(" PLL_C_INT_FRAC = 0x%x\n\n", value); + + value = cx_read(PLL_D_POST_STAT_BIST); + CX25821_INFO(" PLL_D_POST_STAT_BIST = 0x%x\n\n", value); + value = cx_read(PLL_D_INT_FRAC); + CX25821_INFO(" PLL_D_INT_FRAC = 0x%x\n\n", value); + + value = cx25821_i2c_read(&dev->i2c_bus[0], AFE_AB_DIAG_CTRL, &tmp); + CX25821_INFO(" AFE_AB_DIAG_CTRL (0x10900090) = 0x%x\n\n", value); +} +EXPORT_SYMBOL(cx25821_sram_channel_dump_audio); + +static void cx25821_shutdown(struct cx25821_dev *dev) +{ + int i; + + /* disable RISC controller */ + cx_write(DEV_CNTRL2, 0); + + /* Disable Video A/B activity */ + for (i = 0; i < VID_CHANNEL_NUM; i++) { + cx_write(dev->channels[i].sram_channels->dma_ctl, 0); + cx_write(dev->channels[i].sram_channels->int_msk, 0); + } + + for (i = VID_UPSTREAM_SRAM_CHANNEL_I; + i <= VID_UPSTREAM_SRAM_CHANNEL_J; i++) { + cx_write(dev->channels[i].sram_channels->dma_ctl, 0); + cx_write(dev->channels[i].sram_channels->int_msk, 0); + } + + /* Disable Audio activity */ + cx_write(AUD_INT_DMA_CTL, 0); + + /* Disable Serial port */ + cx_write(UART_CTL, 0); + + /* Disable Interrupts */ + cx_write(PCI_INT_MSK, 0); + cx_write(AUD_A_INT_MSK, 0); +} + +void cx25821_set_pixel_format(struct cx25821_dev *dev, int channel_select, + u32 format) +{ + if (channel_select <= 7 && channel_select >= 0) { + cx_write(dev->channels[channel_select]. + sram_channels->pix_frmt, format); + dev->channels[channel_select].pixel_formats = format; + } +} + +static void cx25821_set_vip_mode(struct cx25821_dev *dev, + struct sram_channel *ch) +{ + cx_write(ch->pix_frmt, PIXEL_FRMT_422); + cx_write(ch->vip_ctl, PIXEL_ENGINE_VIP1); +} + +static void cx25821_initialize(struct cx25821_dev *dev) +{ + int i; + + dprintk(1, "%s()\n", __func__); + + cx25821_shutdown(dev); + cx_write(PCI_INT_STAT, 0xffffffff); + + for (i = 0; i < VID_CHANNEL_NUM; i++) + cx_write(dev->channels[i].sram_channels->int_stat, 0xffffffff); + + cx_write(AUD_A_INT_STAT, 0xffffffff); + cx_write(AUD_B_INT_STAT, 0xffffffff); + cx_write(AUD_C_INT_STAT, 0xffffffff); + cx_write(AUD_D_INT_STAT, 0xffffffff); + cx_write(AUD_E_INT_STAT, 0xffffffff); + + cx_write(CLK_DELAY, cx_read(CLK_DELAY) & 0x80000000); + cx_write(PAD_CTRL, 0x12); /* for I2C */ + cx25821_registers_init(dev); /* init Pecos registers */ + mdelay(100); + + for (i = 0; i < VID_CHANNEL_NUM; i++) { + cx25821_set_vip_mode(dev, dev->channels[i].sram_channels); + cx25821_sram_channel_setup(dev, dev->channels[i].sram_channels, + 1440, 0); + dev->channels[i].pixel_formats = PIXEL_FRMT_422; + dev->channels[i].use_cif_resolution = FALSE; + } + + /* Probably only affect Downstream */ + for (i = VID_UPSTREAM_SRAM_CHANNEL_I; + i <= VID_UPSTREAM_SRAM_CHANNEL_J; i++) { + cx25821_set_vip_mode(dev, dev->channels[i].sram_channels); + } + + cx25821_sram_channel_setup_audio(dev, + dev->channels[SRAM_CH08].sram_channels, + 128, 0); + + cx25821_gpio_init(dev); +} + +static int cx25821_get_resources(struct cx25821_dev *dev) +{ + if (request_mem_region + (pci_resource_start(dev->pci, 0), pci_resource_len(dev->pci, 0), + dev->name)) + return 0; + + pr_err("%s: can't get MMIO memory @ 0x%llx\n", + dev->name, (unsigned long long)pci_resource_start(dev->pci, 0)); + + return -EBUSY; +} + +static void cx25821_dev_checkrevision(struct cx25821_dev *dev) +{ + dev->hwrevision = cx_read(RDR_CFG2) & 0xff; + + pr_info("%s(): Hardware revision = 0x%02x\n", + __func__, dev->hwrevision); +} + +static void cx25821_iounmap(struct cx25821_dev *dev) +{ + if (dev == NULL) + return; + + /* Releasing IO memory */ + if (dev->lmmio != NULL) { + CX25821_INFO("Releasing lmmio.\n"); + iounmap(dev->lmmio); + dev->lmmio = NULL; + } +} + +static int cx25821_dev_setup(struct cx25821_dev *dev) +{ + int io_size = 0, i; + + pr_info("\n***********************************\n"); + pr_info("cx25821 set up\n"); + pr_info("***********************************\n\n"); + + mutex_init(&dev->lock); + + atomic_inc(&dev->refcount); + + dev->nr = ++cx25821_devcount; + sprintf(dev->name, "cx25821[%d]", dev->nr); + + mutex_lock(&cx25821_devlist_mutex); + list_add_tail(&dev->devlist, &cx25821_devlist); + mutex_unlock(&cx25821_devlist_mutex); + + strcpy(cx25821_boards[UNKNOWN_BOARD].name, "unknown"); + strcpy(cx25821_boards[CX25821_BOARD].name, "cx25821"); + + if (dev->pci->device != 0x8210) { + pr_info("%s(): Exiting. Incorrect Hardware device = 0x%02x\n", + __func__, dev->pci->device); + return -1; + } else { + pr_info("Athena Hardware device = 0x%02x\n", dev->pci->device); + } + + /* Apply a sensible clock frequency for the PCIe bridge */ + dev->clk_freq = 28000000; + for (i = 0; i < MAX_VID_CHANNEL_NUM; i++) + dev->channels[i].sram_channels = &cx25821_sram_channels[i]; + + if (dev->nr > 1) + CX25821_INFO("dev->nr > 1!"); + + /* board config */ + dev->board = 1; /* card[dev->nr]; */ + dev->_max_num_decoders = MAX_DECODERS; + + dev->pci_bus = dev->pci->bus->number; + dev->pci_slot = PCI_SLOT(dev->pci->devfn); + dev->pci_irqmask = 0x001f00; + + /* External Master 1 Bus */ + dev->i2c_bus[0].nr = 0; + dev->i2c_bus[0].dev = dev; + dev->i2c_bus[0].reg_stat = I2C1_STAT; + dev->i2c_bus[0].reg_ctrl = I2C1_CTRL; + dev->i2c_bus[0].reg_addr = I2C1_ADDR; + dev->i2c_bus[0].reg_rdata = I2C1_RDATA; + dev->i2c_bus[0].reg_wdata = I2C1_WDATA; + dev->i2c_bus[0].i2c_period = (0x07 << 24); /* 1.95MHz */ + + if (cx25821_get_resources(dev) < 0) { + pr_err("%s: No more PCIe resources for subsystem: %04x:%04x\n", + dev->name, dev->pci->subsystem_vendor, + dev->pci->subsystem_device); + + cx25821_devcount--; + return -EBUSY; + } + + /* PCIe stuff */ + dev->base_io_addr = pci_resource_start(dev->pci, 0); + io_size = pci_resource_len(dev->pci, 0); + + if (!dev->base_io_addr) { + CX25821_ERR("No PCI Memory resources, exiting!\n"); + return -ENODEV; + } + + dev->lmmio = ioremap(dev->base_io_addr, pci_resource_len(dev->pci, 0)); + + if (!dev->lmmio) { + CX25821_ERR + ("ioremap failed, maybe increasing __VMALLOC_RESERVE in page.h\n"); + cx25821_iounmap(dev); + return -ENOMEM; + } + + dev->bmmio = (u8 __iomem *) dev->lmmio; + + pr_info("%s: subsystem: %04x:%04x, board: %s [card=%d,%s]\n", + dev->name, dev->pci->subsystem_vendor, + dev->pci->subsystem_device, cx25821_boards[dev->board].name, + dev->board, card[dev->nr] == dev->board ? + "insmod option" : "autodetected"); + + /* init hardware */ + cx25821_initialize(dev); + + cx25821_i2c_register(&dev->i2c_bus[0]); +/* cx25821_i2c_register(&dev->i2c_bus[1]); + * cx25821_i2c_register(&dev->i2c_bus[2]); */ + + CX25821_INFO("i2c register! bus->i2c_rc = %d\n", + dev->i2c_bus[0].i2c_rc); + + cx25821_card_setup(dev); + + if (medusa_video_init(dev) < 0) + CX25821_ERR("%s(): Failed to initialize medusa!\n", __func__); + + cx25821_video_register(dev); + + /* register IOCTL device */ + dev->ioctl_dev = + cx25821_vdev_init(dev, dev->pci, &cx25821_videoioctl_template, + "video"); + + if (video_register_device + (dev->ioctl_dev, VFL_TYPE_GRABBER, VIDEO_IOCTL_CH) < 0) { + cx25821_videoioctl_unregister(dev); + pr_err("%s(): Failed to register video adapter for IOCTL, so unregistering videoioctl device\n", + __func__); + } + + cx25821_dev_checkrevision(dev); + CX25821_INFO("setup done!\n"); + + return 0; +} + +void cx25821_start_upstream_video_ch1(struct cx25821_dev *dev, + struct upstream_user_struct *up_data) +{ + dev->_isNTSC = !strcmp(dev->vid_stdname, "NTSC") ? 1 : 0; + + dev->tvnorm = !dev->_isNTSC ? V4L2_STD_PAL_BG : V4L2_STD_NTSC_M; + medusa_set_videostandard(dev); + + cx25821_vidupstream_init_ch1(dev, dev->channel_select, + dev->pixel_format); +} + +void cx25821_start_upstream_video_ch2(struct cx25821_dev *dev, + struct upstream_user_struct *up_data) +{ + dev->_isNTSC_ch2 = !strcmp(dev->vid_stdname_ch2, "NTSC") ? 1 : 0; + + dev->tvnorm = !dev->_isNTSC_ch2 ? V4L2_STD_PAL_BG : V4L2_STD_NTSC_M; + medusa_set_videostandard(dev); + + cx25821_vidupstream_init_ch2(dev, dev->channel_select_ch2, + dev->pixel_format_ch2); +} + +void cx25821_start_upstream_audio(struct cx25821_dev *dev, + struct upstream_user_struct *up_data) +{ + cx25821_audio_upstream_init(dev, AUDIO_UPSTREAM_SRAM_CHANNEL_B); +} + +void cx25821_dev_unregister(struct cx25821_dev *dev) +{ + int i; + + if (!dev->base_io_addr) + return; + + cx25821_free_mem_upstream_ch1(dev); + cx25821_free_mem_upstream_ch2(dev); + cx25821_free_mem_upstream_audio(dev); + + release_mem_region(dev->base_io_addr, pci_resource_len(dev->pci, 0)); + + if (!atomic_dec_and_test(&dev->refcount)) + return; + + for (i = 0; i < VID_CHANNEL_NUM; i++) + cx25821_video_unregister(dev, i); + + for (i = VID_UPSTREAM_SRAM_CHANNEL_I; + i <= AUDIO_UPSTREAM_SRAM_CHANNEL_B; i++) { + cx25821_video_unregister(dev, i); + } + + cx25821_videoioctl_unregister(dev); + + cx25821_i2c_unregister(&dev->i2c_bus[0]); + cx25821_iounmap(dev); +} +EXPORT_SYMBOL(cx25821_dev_unregister); + +static __le32 *cx25821_risc_field(__le32 * rp, struct scatterlist *sglist, + unsigned int offset, u32 sync_line, + unsigned int bpl, unsigned int padding, + unsigned int lines) +{ + struct scatterlist *sg; + unsigned int line, todo; + + /* sync instruction */ + if (sync_line != NO_SYNC_LINE) + *(rp++) = cpu_to_le32(RISC_RESYNC | sync_line); + + /* scan lines */ + sg = sglist; + for (line = 0; line < lines; line++) { + while (offset && offset >= sg_dma_len(sg)) { + offset -= sg_dma_len(sg); + sg++; + } + if (bpl <= sg_dma_len(sg) - offset) { + /* fits into current chunk */ + *(rp++) = + cpu_to_le32(RISC_WRITE | RISC_SOL | RISC_EOL | bpl); + *(rp++) = cpu_to_le32(sg_dma_address(sg) + offset); + *(rp++) = cpu_to_le32(0); /* bits 63-32 */ + offset += bpl; + } else { + /* scanline needs to be split */ + todo = bpl; + *(rp++) = + cpu_to_le32(RISC_WRITE | RISC_SOL | + (sg_dma_len(sg) - offset)); + *(rp++) = cpu_to_le32(sg_dma_address(sg) + offset); + *(rp++) = cpu_to_le32(0); /* bits 63-32 */ + todo -= (sg_dma_len(sg) - offset); + offset = 0; + sg++; + while (todo > sg_dma_len(sg)) { + *(rp++) = + cpu_to_le32(RISC_WRITE | sg_dma_len(sg)); + *(rp++) = cpu_to_le32(sg_dma_address(sg)); + *(rp++) = cpu_to_le32(0); /* bits 63-32 */ + todo -= sg_dma_len(sg); + sg++; + } + *(rp++) = cpu_to_le32(RISC_WRITE | RISC_EOL | todo); + *(rp++) = cpu_to_le32(sg_dma_address(sg)); + *(rp++) = cpu_to_le32(0); /* bits 63-32 */ + offset += todo; + } + + offset += padding; + } + + return rp; +} + +int cx25821_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc, + struct scatterlist *sglist, unsigned int top_offset, + unsigned int bottom_offset, unsigned int bpl, + unsigned int padding, unsigned int lines) +{ + u32 instructions; + u32 fields; + __le32 *rp; + int rc; + + fields = 0; + if (UNSET != top_offset) + fields++; + if (UNSET != bottom_offset) + fields++; + + /* estimate risc mem: worst case is one write per page border + + one write per scan line + syncs + jump (all 2 dwords). Padding + can cause next bpl to start close to a page border. First DMA + region may be smaller than PAGE_SIZE */ + /* write and jump need and extra dword */ + instructions = + fields * (1 + ((bpl + padding) * lines) / PAGE_SIZE + lines); + instructions += 2; + rc = btcx_riscmem_alloc(pci, risc, instructions * 12); + + if (rc < 0) + return rc; + + /* write risc instructions */ + rp = risc->cpu; + + if (UNSET != top_offset) { + rp = cx25821_risc_field(rp, sglist, top_offset, 0, bpl, padding, + lines); + } + + if (UNSET != bottom_offset) { + rp = cx25821_risc_field(rp, sglist, bottom_offset, 0x200, bpl, + padding, lines); + } + + /* save pointer to jmp instruction address */ + risc->jmp = rp; + BUG_ON((risc->jmp - risc->cpu + 2) * sizeof(*risc->cpu) > risc->size); + + return 0; +} + +static __le32 *cx25821_risc_field_audio(__le32 * rp, struct scatterlist *sglist, + unsigned int offset, u32 sync_line, + unsigned int bpl, unsigned int padding, + unsigned int lines, unsigned int lpi) +{ + struct scatterlist *sg; + unsigned int line, todo, sol; + + /* sync instruction */ + if (sync_line != NO_SYNC_LINE) + *(rp++) = cpu_to_le32(RISC_RESYNC | sync_line); + + /* scan lines */ + sg = sglist; + for (line = 0; line < lines; line++) { + while (offset && offset >= sg_dma_len(sg)) { + offset -= sg_dma_len(sg); + sg++; + } + + if (lpi && line > 0 && !(line % lpi)) + sol = RISC_SOL | RISC_IRQ1 | RISC_CNT_INC; + else + sol = RISC_SOL; + + if (bpl <= sg_dma_len(sg) - offset) { + /* fits into current chunk */ + *(rp++) = + cpu_to_le32(RISC_WRITE | sol | RISC_EOL | bpl); + *(rp++) = cpu_to_le32(sg_dma_address(sg) + offset); + *(rp++) = cpu_to_le32(0); /* bits 63-32 */ + offset += bpl; + } else { + /* scanline needs to be split */ + todo = bpl; + *(rp++) = cpu_to_le32(RISC_WRITE | sol | + (sg_dma_len(sg) - offset)); + *(rp++) = cpu_to_le32(sg_dma_address(sg) + offset); + *(rp++) = cpu_to_le32(0); /* bits 63-32 */ + todo -= (sg_dma_len(sg) - offset); + offset = 0; + sg++; + while (todo > sg_dma_len(sg)) { + *(rp++) = cpu_to_le32(RISC_WRITE | + sg_dma_len(sg)); + *(rp++) = cpu_to_le32(sg_dma_address(sg)); + *(rp++) = cpu_to_le32(0); /* bits 63-32 */ + todo -= sg_dma_len(sg); + sg++; + } + *(rp++) = cpu_to_le32(RISC_WRITE | RISC_EOL | todo); + *(rp++) = cpu_to_le32(sg_dma_address(sg)); + *(rp++) = cpu_to_le32(0); /* bits 63-32 */ + offset += todo; + } + offset += padding; + } + + return rp; +} + +int cx25821_risc_databuffer_audio(struct pci_dev *pci, + struct btcx_riscmem *risc, + struct scatterlist *sglist, + unsigned int bpl, + unsigned int lines, unsigned int lpi) +{ + u32 instructions; + __le32 *rp; + int rc; + + /* estimate risc mem: worst case is one write per page border + + one write per scan line + syncs + jump (all 2 dwords). Here + there is no padding and no sync. First DMA region may be smaller + than PAGE_SIZE */ + /* Jump and write need an extra dword */ + instructions = 1 + (bpl * lines) / PAGE_SIZE + lines; + instructions += 1; + + rc = btcx_riscmem_alloc(pci, risc, instructions * 12); + if (rc < 0) + return rc; + + /* write risc instructions */ + rp = risc->cpu; + rp = cx25821_risc_field_audio(rp, sglist, 0, NO_SYNC_LINE, bpl, 0, + lines, lpi); + + /* save pointer to jmp instruction address */ + risc->jmp = rp; + BUG_ON((risc->jmp - risc->cpu + 2) * sizeof(*risc->cpu) > risc->size); + return 0; +} +EXPORT_SYMBOL(cx25821_risc_databuffer_audio); + +int cx25821_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc, + u32 reg, u32 mask, u32 value) +{ + __le32 *rp; + int rc; + + rc = btcx_riscmem_alloc(pci, risc, 4 * 16); + + if (rc < 0) + return rc; + + /* write risc instructions */ + rp = risc->cpu; + + *(rp++) = cpu_to_le32(RISC_WRITECR | RISC_IRQ1); + *(rp++) = cpu_to_le32(reg); + *(rp++) = cpu_to_le32(value); + *(rp++) = cpu_to_le32(mask); + *(rp++) = cpu_to_le32(RISC_JUMP); + *(rp++) = cpu_to_le32(risc->dma); + *(rp++) = cpu_to_le32(0); /* bits 63-32 */ + return 0; +} + +void cx25821_free_buffer(struct videobuf_queue *q, struct cx25821_buffer *buf) +{ + struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb); + + BUG_ON(in_interrupt()); + videobuf_waiton(q, &buf->vb, 0, 0); + videobuf_dma_unmap(q->dev, dma); + videobuf_dma_free(dma); + btcx_riscmem_free(to_pci_dev(q->dev), &buf->risc); + buf->vb.state = VIDEOBUF_NEEDS_INIT; +} + +static irqreturn_t cx25821_irq(int irq, void *dev_id) +{ + struct cx25821_dev *dev = dev_id; + u32 pci_status, pci_mask; + u32 vid_status; + int i, handled = 0; + u32 mask[8] = { 1, 2, 4, 8, 16, 32, 64, 128 }; + + pci_status = cx_read(PCI_INT_STAT); + pci_mask = cx_read(PCI_INT_MSK); + + if (pci_status == 0) + goto out; + + for (i = 0; i < VID_CHANNEL_NUM; i++) { + if (pci_status & mask[i]) { + vid_status = cx_read(dev->channels[i]. + sram_channels->int_stat); + + if (vid_status) + handled += + cx25821_video_irq(dev, i, vid_status); + + cx_write(PCI_INT_STAT, mask[i]); + } + } + +out: + return IRQ_RETVAL(handled); +} + +void cx25821_print_irqbits(char *name, char *tag, char **strings, + int len, u32 bits, u32 mask) +{ + unsigned int i; + + printk(KERN_DEBUG pr_fmt("%s: %s [0x%x]"), name, tag, bits); + + for (i = 0; i < len; i++) { + if (!(bits & (1 << i))) + continue; + if (strings[i]) + pr_cont(" %s", strings[i]); + else + pr_cont(" %d", i); + if (!(mask & (1 << i))) + continue; + pr_cont("*"); + } + pr_cont("\n"); +} +EXPORT_SYMBOL(cx25821_print_irqbits); + +struct cx25821_dev *cx25821_dev_get(struct pci_dev *pci) +{ + struct cx25821_dev *dev = pci_get_drvdata(pci); + return dev; +} +EXPORT_SYMBOL(cx25821_dev_get); + +static int __devinit cx25821_initdev(struct pci_dev *pci_dev, + const struct pci_device_id *pci_id) +{ + struct cx25821_dev *dev; + int err = 0; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (NULL == dev) + return -ENOMEM; + + err = v4l2_device_register(&pci_dev->dev, &dev->v4l2_dev); + if (err < 0) + goto fail_free; + + /* pci init */ + dev->pci = pci_dev; + if (pci_enable_device(pci_dev)) { + err = -EIO; + + pr_info("pci enable failed!\n"); + + goto fail_unregister_device; + } + + pr_info("Athena pci enable !\n"); + + err = cx25821_dev_setup(dev); + if (err) { + if (err == -EBUSY) + goto fail_unregister_device; + else + goto fail_unregister_pci; + } + + /* print pci info */ + pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &dev->pci_rev); + pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &dev->pci_lat); + pr_info("%s/0: found at %s, rev: %d, irq: %d, latency: %d, mmio: 0x%llx\n", + dev->name, pci_name(pci_dev), dev->pci_rev, pci_dev->irq, + dev->pci_lat, (unsigned long long)dev->base_io_addr); + + pci_set_master(pci_dev); + if (!pci_dma_supported(pci_dev, 0xffffffff)) { + pr_err("%s/0: Oops: no 32bit PCI DMA ???\n", dev->name); + err = -EIO; + goto fail_irq; + } + + err = + request_irq(pci_dev->irq, cx25821_irq, IRQF_SHARED, + dev->name, dev); + + if (err < 0) { + pr_err("%s: can't get IRQ %d\n", dev->name, pci_dev->irq); + goto fail_irq; + } + + return 0; + +fail_irq: + pr_info("cx25821_initdev() can't get IRQ !\n"); + cx25821_dev_unregister(dev); + +fail_unregister_pci: + pci_disable_device(pci_dev); +fail_unregister_device: + v4l2_device_unregister(&dev->v4l2_dev); + +fail_free: + kfree(dev); + return err; +} + +static void __devexit cx25821_finidev(struct pci_dev *pci_dev) +{ + struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); + struct cx25821_dev *dev = get_cx25821(v4l2_dev); + + cx25821_shutdown(dev); + pci_disable_device(pci_dev); + + /* unregister stuff */ + if (pci_dev->irq) + free_irq(pci_dev->irq, dev); + + mutex_lock(&cx25821_devlist_mutex); + list_del(&dev->devlist); + mutex_unlock(&cx25821_devlist_mutex); + + cx25821_dev_unregister(dev); + v4l2_device_unregister(v4l2_dev); + kfree(dev); +} + +static DEFINE_PCI_DEVICE_TABLE(cx25821_pci_tbl) = { + { + /* CX25821 Athena */ + .vendor = 0x14f1, + .device = 0x8210, + .subvendor = 0x14f1, + .subdevice = 0x0920, + }, + { + /* --- end of list --- */ + } +}; + +MODULE_DEVICE_TABLE(pci, cx25821_pci_tbl); + +static struct pci_driver cx25821_pci_driver = { + .name = "cx25821", + .id_table = cx25821_pci_tbl, + .probe = cx25821_initdev, + .remove = __devexit_p(cx25821_finidev), + /* TODO */ + .suspend = NULL, + .resume = NULL, +}; + +static int __init cx25821_init(void) +{ + pr_info("driver version %d.%d.%d loaded\n", + (CX25821_VERSION_CODE >> 16) & 0xff, + (CX25821_VERSION_CODE >> 8) & 0xff, + CX25821_VERSION_CODE & 0xff); + return pci_register_driver(&cx25821_pci_driver); +} + +static void __exit cx25821_fini(void) +{ + pci_unregister_driver(&cx25821_pci_driver); +} + + +module_init(cx25821_init); +module_exit(cx25821_fini); diff --git a/drivers/media/video/cx25821/cx25821-gpio.c b/drivers/media/video/cx25821/cx25821-gpio.c new file mode 100644 index 00000000000..29e43b03c85 --- /dev/null +++ b/drivers/media/video/cx25821/cx25821-gpio.c @@ -0,0 +1,98 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "cx25821.h" + +/********************* GPIO stuffs *********************/ +void cx25821_set_gpiopin_direction(struct cx25821_dev *dev, + int pin_number, int pin_logic_value) +{ + int bit = pin_number; + u32 gpio_oe_reg = GPIO_LO_OE; + u32 gpio_register = 0; + u32 value = 0; + + /* Check for valid pinNumber */ + if (pin_number >= 47) + return; + + if (pin_number > 31) { + bit = pin_number - 31; + gpio_oe_reg = GPIO_HI_OE; + } + /* Here we will make sure that the GPIOs 0 and 1 are output. keep the + * rest as is */ + gpio_register = cx_read(gpio_oe_reg); + + if (pin_logic_value == 1) + value = gpio_register | Set_GPIO_Bit(bit); + else + value = gpio_register & Clear_GPIO_Bit(bit); + + cx_write(gpio_oe_reg, value); +} +EXPORT_SYMBOL(cx25821_set_gpiopin_direction); + +static void cx25821_set_gpiopin_logicvalue(struct cx25821_dev *dev, + int pin_number, int pin_logic_value) +{ + int bit = pin_number; + u32 gpio_reg = GPIO_LO; + u32 value = 0; + + /* Check for valid pinNumber */ + if (pin_number >= 47) + return; + + /* change to output direction */ + cx25821_set_gpiopin_direction(dev, pin_number, 0); + + if (pin_number > 31) { + bit = pin_number - 31; + gpio_reg = GPIO_HI; + } + + value = cx_read(gpio_reg); + + if (pin_logic_value == 0) + value &= Clear_GPIO_Bit(bit); + else + value |= Set_GPIO_Bit(bit); + + cx_write(gpio_reg, value); +} + +void cx25821_gpio_init(struct cx25821_dev *dev) +{ + if (dev == NULL) + return; + + switch (dev->board) { + case CX25821_BOARD_CONEXANT_ATHENA10: + default: + /* set GPIO 5 to select the path for Medusa/Athena */ + cx25821_set_gpiopin_logicvalue(dev, 5, 1); + mdelay(20); + break; + } + +} diff --git a/drivers/media/video/cx25821/cx25821-i2c.c b/drivers/media/video/cx25821/cx25821-i2c.c new file mode 100644 index 00000000000..4d3d0ce4078 --- /dev/null +++ b/drivers/media/video/cx25821/cx25821-i2c.c @@ -0,0 +1,425 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> + * Based on Steven Toth <stoth@linuxtv.org> cx23885 driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "cx25821.h" +#include <linux/i2c.h> + +static unsigned int i2c_debug; +module_param(i2c_debug, int, 0644); +MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]"); + +static unsigned int i2c_scan; +module_param(i2c_scan, int, 0444); +MODULE_PARM_DESC(i2c_scan, "scan i2c bus at insmod time"); + +#define dprintk(level, fmt, arg...) \ +do { \ + if (i2c_debug >= level) \ + printk(KERN_DEBUG "%s/0: " fmt, dev->name, ##arg); \ +} while (0) + +#define I2C_WAIT_DELAY 32 +#define I2C_WAIT_RETRY 64 + +#define I2C_EXTEND (1 << 3) +#define I2C_NOSTOP (1 << 4) + +static inline int i2c_slave_did_ack(struct i2c_adapter *i2c_adap) +{ + struct cx25821_i2c *bus = i2c_adap->algo_data; + struct cx25821_dev *dev = bus->dev; + return cx_read(bus->reg_stat) & 0x01; +} + +static inline int i2c_is_busy(struct i2c_adapter *i2c_adap) +{ + struct cx25821_i2c *bus = i2c_adap->algo_data; + struct cx25821_dev *dev = bus->dev; + return cx_read(bus->reg_stat) & 0x02 ? 1 : 0; +} + +static int i2c_wait_done(struct i2c_adapter *i2c_adap) +{ + int count; + + for (count = 0; count < I2C_WAIT_RETRY; count++) { + if (!i2c_is_busy(i2c_adap)) + break; + udelay(I2C_WAIT_DELAY); + } + + if (I2C_WAIT_RETRY == count) + return 0; + + return 1; +} + +static int i2c_sendbytes(struct i2c_adapter *i2c_adap, + const struct i2c_msg *msg, int joined_rlen) +{ + struct cx25821_i2c *bus = i2c_adap->algo_data; + struct cx25821_dev *dev = bus->dev; + u32 wdata, addr, ctrl; + int retval, cnt; + + if (joined_rlen) + dprintk(1, "%s(msg->wlen=%d, nextmsg->rlen=%d)\n", __func__, + msg->len, joined_rlen); + else + dprintk(1, "%s(msg->len=%d)\n", __func__, msg->len); + + /* Deal with i2c probe functions with zero payload */ + if (msg->len == 0) { + cx_write(bus->reg_addr, msg->addr << 25); + cx_write(bus->reg_ctrl, bus->i2c_period | (1 << 2)); + + if (!i2c_wait_done(i2c_adap)) + return -EIO; + + if (!i2c_slave_did_ack(i2c_adap)) + return -EIO; + + dprintk(1, "%s(): returns 0\n", __func__); + return 0; + } + + /* dev, reg + first byte */ + addr = (msg->addr << 25) | msg->buf[0]; + wdata = msg->buf[0]; + + ctrl = bus->i2c_period | (1 << 12) | (1 << 2); + + if (msg->len > 1) + ctrl |= I2C_NOSTOP | I2C_EXTEND; + else if (joined_rlen) + ctrl |= I2C_NOSTOP; + + cx_write(bus->reg_addr, addr); + cx_write(bus->reg_wdata, wdata); + cx_write(bus->reg_ctrl, ctrl); + + retval = i2c_wait_done(i2c_adap); + if (retval < 0) + goto err; + + if (retval == 0) + goto eio; + + if (i2c_debug) { + if (!(ctrl & I2C_NOSTOP)) + printk(" >\n"); + } + + for (cnt = 1; cnt < msg->len; cnt++) { + /* following bytes */ + wdata = msg->buf[cnt]; + ctrl = bus->i2c_period | (1 << 12) | (1 << 2); + + if (cnt < msg->len - 1) + ctrl |= I2C_NOSTOP | I2C_EXTEND; + else if (joined_rlen) + ctrl |= I2C_NOSTOP; + + cx_write(bus->reg_addr, addr); + cx_write(bus->reg_wdata, wdata); + cx_write(bus->reg_ctrl, ctrl); + + retval = i2c_wait_done(i2c_adap); + if (retval < 0) + goto err; + + if (retval == 0) + goto eio; + + if (i2c_debug) { + dprintk(1, " %02x", msg->buf[cnt]); + if (!(ctrl & I2C_NOSTOP)) + dprintk(1, " >\n"); + } + } + + return msg->len; + +eio: + retval = -EIO; +err: + if (i2c_debug) + pr_err(" ERR: %d\n", retval); + return retval; +} + +static int i2c_readbytes(struct i2c_adapter *i2c_adap, + const struct i2c_msg *msg, int joined) +{ + struct cx25821_i2c *bus = i2c_adap->algo_data; + struct cx25821_dev *dev = bus->dev; + u32 ctrl, cnt; + int retval; + + if (i2c_debug && !joined) + dprintk(1, "6-%s(msg->len=%d)\n", __func__, msg->len); + + /* Deal with i2c probe functions with zero payload */ + if (msg->len == 0) { + cx_write(bus->reg_addr, msg->addr << 25); + cx_write(bus->reg_ctrl, bus->i2c_period | (1 << 2) | 1); + if (!i2c_wait_done(i2c_adap)) + return -EIO; + if (!i2c_slave_did_ack(i2c_adap)) + return -EIO; + + dprintk(1, "%s(): returns 0\n", __func__); + return 0; + } + + if (i2c_debug) { + if (joined) + dprintk(1, " R"); + else + dprintk(1, " <R %02x", (msg->addr << 1) + 1); + } + + for (cnt = 0; cnt < msg->len; cnt++) { + + ctrl = bus->i2c_period | (1 << 12) | (1 << 2) | 1; + + if (cnt < msg->len - 1) + ctrl |= I2C_NOSTOP | I2C_EXTEND; + + cx_write(bus->reg_addr, msg->addr << 25); + cx_write(bus->reg_ctrl, ctrl); + + retval = i2c_wait_done(i2c_adap); + if (retval < 0) + goto err; + if (retval == 0) + goto eio; + msg->buf[cnt] = cx_read(bus->reg_rdata) & 0xff; + + if (i2c_debug) { + dprintk(1, " %02x", msg->buf[cnt]); + if (!(ctrl & I2C_NOSTOP)) + dprintk(1, " >\n"); + } + } + + return msg->len; +eio: + retval = -EIO; +err: + if (i2c_debug) + pr_err(" ERR: %d\n", retval); + return retval; +} + +static int i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num) +{ + struct cx25821_i2c *bus = i2c_adap->algo_data; + struct cx25821_dev *dev = bus->dev; + int i, retval = 0; + + dprintk(1, "%s(num = %d)\n", __func__, num); + + for (i = 0; i < num; i++) { + dprintk(1, "%s(num = %d) addr = 0x%02x len = 0x%x\n", + __func__, num, msgs[i].addr, msgs[i].len); + + if (msgs[i].flags & I2C_M_RD) { + /* read */ + retval = i2c_readbytes(i2c_adap, &msgs[i], 0); + } else if (i + 1 < num && (msgs[i + 1].flags & I2C_M_RD) && + msgs[i].addr == msgs[i + 1].addr) { + /* write then read from same address */ + retval = + i2c_sendbytes(i2c_adap, &msgs[i], msgs[i + 1].len); + + if (retval < 0) + goto err; + i++; + retval = i2c_readbytes(i2c_adap, &msgs[i], 1); + } else { + /* write */ + retval = i2c_sendbytes(i2c_adap, &msgs[i], 0); + } + + if (retval < 0) + goto err; + } + return num; + +err: + return retval; +} + + +static u32 cx25821_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_SMBUS_EMUL | + I2C_FUNC_I2C | + I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_READ_WORD_DATA | I2C_FUNC_SMBUS_WRITE_WORD_DATA; +} + +static struct i2c_algorithm cx25821_i2c_algo_template = { + .master_xfer = i2c_xfer, + .functionality = cx25821_functionality, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif +}; + +static struct i2c_adapter cx25821_i2c_adap_template = { + .name = "cx25821", + .owner = THIS_MODULE, + .algo = &cx25821_i2c_algo_template, +}; + +static struct i2c_client cx25821_i2c_client_template = { + .name = "cx25821 internal", +}; + +/* init + register i2c algo-bit adapter */ +int cx25821_i2c_register(struct cx25821_i2c *bus) +{ + struct cx25821_dev *dev = bus->dev; + + dprintk(1, "%s(bus = %d)\n", __func__, bus->nr); + + memcpy(&bus->i2c_adap, &cx25821_i2c_adap_template, + sizeof(bus->i2c_adap)); + memcpy(&bus->i2c_algo, &cx25821_i2c_algo_template, + sizeof(bus->i2c_algo)); + memcpy(&bus->i2c_client, &cx25821_i2c_client_template, + sizeof(bus->i2c_client)); + + bus->i2c_adap.dev.parent = &dev->pci->dev; + + strlcpy(bus->i2c_adap.name, bus->dev->name, sizeof(bus->i2c_adap.name)); + + bus->i2c_algo.data = bus; + bus->i2c_adap.algo_data = bus; + i2c_set_adapdata(&bus->i2c_adap, &dev->v4l2_dev); + i2c_add_adapter(&bus->i2c_adap); + + bus->i2c_client.adapter = &bus->i2c_adap; + + /* set up the I2c */ + bus->i2c_client.addr = (0x88 >> 1); + + return bus->i2c_rc; +} + +int cx25821_i2c_unregister(struct cx25821_i2c *bus) +{ + i2c_del_adapter(&bus->i2c_adap); + return 0; +} + +void cx25821_av_clk(struct cx25821_dev *dev, int enable) +{ + /* write 0 to bus 2 addr 0x144 via i2x_xfer() */ + char buffer[3]; + struct i2c_msg msg; + dprintk(1, "%s(enabled = %d)\n", __func__, enable); + + /* Register 0x144 */ + buffer[0] = 0x01; + buffer[1] = 0x44; + if (enable == 1) + buffer[2] = 0x05; + else + buffer[2] = 0x00; + + msg.addr = 0x44; + msg.flags = I2C_M_TEN; + msg.len = 3; + msg.buf = buffer; + + i2c_xfer(&dev->i2c_bus[0].i2c_adap, &msg, 1); +} + +int cx25821_i2c_read(struct cx25821_i2c *bus, u16 reg_addr, int *value) +{ + struct i2c_client *client = &bus->i2c_client; + int retval = 0; + int v = 0; + u8 addr[2] = { 0, 0 }; + u8 buf[4] = { 0, 0, 0, 0 }; + + struct i2c_msg msgs[2] = { + { + .addr = client->addr, + .flags = 0, + .len = 2, + .buf = addr, + }, { + .addr = client->addr, + .flags = I2C_M_RD, + .len = 4, + .buf = buf, + } + }; + + addr[0] = (reg_addr >> 8); + addr[1] = (reg_addr & 0xff); + msgs[0].addr = 0x44; + msgs[1].addr = 0x44; + + retval = i2c_xfer(client->adapter, msgs, 2); + + v = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + *value = v; + + return v; +} + +int cx25821_i2c_write(struct cx25821_i2c *bus, u16 reg_addr, int value) +{ + struct i2c_client *client = &bus->i2c_client; + int retval = 0; + u8 buf[6] = { 0, 0, 0, 0, 0, 0 }; + + struct i2c_msg msgs[1] = { + { + .addr = client->addr, + .flags = 0, + .len = 6, + .buf = buf, + } + }; + + buf[0] = reg_addr >> 8; + buf[1] = reg_addr & 0xff; + buf[5] = (value >> 24) & 0xff; + buf[4] = (value >> 16) & 0xff; + buf[3] = (value >> 8) & 0xff; + buf[2] = value & 0xff; + client->flags = 0; + msgs[0].addr = 0x44; + + retval = i2c_xfer(client->adapter, msgs, 1); + + return retval; +} diff --git a/drivers/media/video/cx25821/cx25821-medusa-defines.h b/drivers/media/video/cx25821/cx25821-medusa-defines.h new file mode 100644 index 00000000000..60d197f5755 --- /dev/null +++ b/drivers/media/video/cx25821/cx25821-medusa-defines.h @@ -0,0 +1,42 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _MEDUSA_DEF_H_ +#define _MEDUSA_DEF_H_ + +/* Video deocder that we supported */ +#define VDEC_A 0 +#define VDEC_B 1 +#define VDEC_C 2 +#define VDEC_D 3 +#define VDEC_E 4 +#define VDEC_F 5 +#define VDEC_G 6 +#define VDEC_H 7 + +/* end of display sequence */ +#define END_OF_SEQ 0xF; + +/* registry string size */ +#define MAX_REGISTRY_SZ 40; + +#endif diff --git a/drivers/media/video/cx25821/cx25821-medusa-reg.h b/drivers/media/video/cx25821/cx25821-medusa-reg.h new file mode 100644 index 00000000000..1c1c228352d --- /dev/null +++ b/drivers/media/video/cx25821/cx25821-medusa-reg.h @@ -0,0 +1,455 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __MEDUSA_REGISTERS__ +#define __MEDUSA_REGISTERS__ + +/* Serial Slave Registers */ +#define HOST_REGISTER1 0x0000 +#define HOST_REGISTER2 0x0001 + +/* Chip Configuration Registers */ +#define CHIP_CTRL 0x0100 +#define AFE_AB_CTRL 0x0104 +#define AFE_CD_CTRL 0x0108 +#define AFE_EF_CTRL 0x010C +#define AFE_GH_CTRL 0x0110 +#define DENC_AB_CTRL 0x0114 +#define BYP_AB_CTRL 0x0118 +#define MON_A_CTRL 0x011C +#define DISP_SEQ_A 0x0120 +#define DISP_SEQ_B 0x0124 +#define DISP_AB_CNT 0x0128 +#define DISP_CD_CNT 0x012C +#define DISP_EF_CNT 0x0130 +#define DISP_GH_CNT 0x0134 +#define DISP_IJ_CNT 0x0138 +#define PIN_OE_CTRL 0x013C +#define PIN_SPD_CTRL 0x0140 +#define PIN_SPD_CTRL2 0x0144 +#define IRQ_STAT_CTRL 0x0148 +#define POWER_CTRL_AB 0x014C +#define POWER_CTRL_CD 0x0150 +#define POWER_CTRL_EF 0x0154 +#define POWER_CTRL_GH 0x0158 +#define TUNE_CTRL 0x015C +#define BIAS_CTRL 0x0160 +#define AFE_AB_DIAG_CTRL 0x0164 +#define AFE_CD_DIAG_CTRL 0x0168 +#define AFE_EF_DIAG_CTRL 0x016C +#define AFE_GH_DIAG_CTRL 0x0170 +#define PLL_AB_DIAG_CTRL 0x0174 +#define PLL_CD_DIAG_CTRL 0x0178 +#define PLL_EF_DIAG_CTRL 0x017C +#define PLL_GH_DIAG_CTRL 0x0180 +#define TEST_CTRL 0x0184 +#define BIST_STAT 0x0188 +#define BIST_STAT2 0x018C +#define BIST_VID_PLL_AB_STAT 0x0190 +#define BIST_VID_PLL_CD_STAT 0x0194 +#define BIST_VID_PLL_EF_STAT 0x0198 +#define BIST_VID_PLL_GH_STAT 0x019C +#define DLL_DIAG_CTRL 0x01A0 +#define DEV_CH_ID_CTRL 0x01A4 +#define ABIST_CTRL_STATUS 0x01A8 +#define ABIST_FREQ 0x01AC +#define ABIST_GOERT_SHIFT 0x01B0 +#define ABIST_COEF12 0x01B4 +#define ABIST_COEF34 0x01B8 +#define ABIST_COEF56 0x01BC +#define ABIST_COEF7_SNR 0x01C0 +#define ABIST_ADC_CAL 0x01C4 +#define ABIST_BIN1_VGA0 0x01C8 +#define ABIST_BIN2_VGA1 0x01CC +#define ABIST_BIN3_VGA2 0x01D0 +#define ABIST_BIN4_VGA3 0x01D4 +#define ABIST_BIN5_VGA4 0x01D8 +#define ABIST_BIN6_VGA5 0x01DC +#define ABIST_BIN7_VGA6 0x0x1E0 +#define ABIST_CLAMP_A 0x0x1E4 +#define ABIST_CLAMP_B 0x0x1E8 +#define ABIST_CLAMP_C 0x01EC +#define ABIST_CLAMP_D 0x01F0 +#define ABIST_CLAMP_E 0x01F4 +#define ABIST_CLAMP_F 0x01F8 + +/* Digital Video Encoder A Registers */ +#define DENC_A_REG_1 0x0200 +#define DENC_A_REG_2 0x0204 +#define DENC_A_REG_3 0x0208 +#define DENC_A_REG_4 0x020C +#define DENC_A_REG_5 0x0210 +#define DENC_A_REG_6 0x0214 +#define DENC_A_REG_7 0x0218 +#define DENC_A_REG_8 0x021C + +/* Digital Video Encoder B Registers */ +#define DENC_B_REG_1 0x0300 +#define DENC_B_REG_2 0x0304 +#define DENC_B_REG_3 0x0308 +#define DENC_B_REG_4 0x030C +#define DENC_B_REG_5 0x0310 +#define DENC_B_REG_6 0x0314 +#define DENC_B_REG_7 0x0318 +#define DENC_B_REG_8 0x031C + +/* Video Decoder A Registers */ +#define MODE_CTRL 0x1000 +#define OUT_CTRL1 0x1004 +#define OUT_CTRL_NS 0x1008 +#define GEN_STAT 0x100C +#define INT_STAT_MASK 0x1010 +#define LUMA_CTRL 0x1014 +#define CHROMA_CTRL 0x1018 +#define CRUSH_CTRL 0x101C +#define HORIZ_TIM_CTRL 0x1020 +#define VERT_TIM_CTRL 0x1024 +#define MISC_TIM_CTRL 0x1028 +#define FIELD_COUNT 0x102C +#define HSCALE_CTRL 0x1030 +#define VSCALE_CTRL 0x1034 +#define MAN_VGA_CTRL 0x1038 +#define MAN_AGC_CTRL 0x103C +#define DFE_CTRL1 0x1040 +#define DFE_CTRL2 0x1044 +#define DFE_CTRL3 0x1048 +#define PLL_CTRL 0x104C +#define PLL_CTRL_FAST 0x1050 +#define HTL_CTRL 0x1054 +#define SRC_CFG 0x1058 +#define SC_STEP_SIZE 0x105C +#define SC_CONVERGE_CTRL 0x1060 +#define SC_LOOP_CTRL 0x1064 +#define COMB_2D_HFS_CFG 0x1068 +#define COMB_2D_HFD_CFG 0x106C +#define COMB_2D_LF_CFG 0x1070 +#define COMB_2D_BLEND 0x1074 +#define COMB_MISC_CTRL 0x1078 +#define COMB_FLAT_THRESH_CTRL 0x107C +#define COMB_TEST 0x1080 +#define BP_MISC_CTRL 0x1084 +#define VCR_DET_CTRL 0x1088 +#define NOISE_DET_CTRL 0x108C +#define COMB_FLAT_NOISE_CTRL 0x1090 +#define VERSION 0x11F8 +#define SOFT_RST_CTRL 0x11FC + +/* Video Decoder B Registers */ +#define VDEC_B_MODE_CTRL 0x1200 +#define VDEC_B_OUT_CTRL1 0x1204 +#define VDEC_B_OUT_CTRL_NS 0x1208 +#define VDEC_B_GEN_STAT 0x120C +#define VDEC_B_INT_STAT_MASK 0x1210 +#define VDEC_B_LUMA_CTRL 0x1214 +#define VDEC_B_CHROMA_CTRL 0x1218 +#define VDEC_B_CRUSH_CTRL 0x121C +#define VDEC_B_HORIZ_TIM_CTRL 0x1220 +#define VDEC_B_VERT_TIM_CTRL 0x1224 +#define VDEC_B_MISC_TIM_CTRL 0x1228 +#define VDEC_B_FIELD_COUNT 0x122C +#define VDEC_B_HSCALE_CTRL 0x1230 +#define VDEC_B_VSCALE_CTRL 0x1234 +#define VDEC_B_MAN_VGA_CTRL 0x1238 +#define VDEC_B_MAN_AGC_CTRL 0x123C +#define VDEC_B_DFE_CTRL1 0x1240 +#define VDEC_B_DFE_CTRL2 0x1244 +#define VDEC_B_DFE_CTRL3 0x1248 +#define VDEC_B_PLL_CTRL 0x124C +#define VDEC_B_PLL_CTRL_FAST 0x1250 +#define VDEC_B_HTL_CTRL 0x1254 +#define VDEC_B_SRC_CFG 0x1258 +#define VDEC_B_SC_STEP_SIZE 0x125C +#define VDEC_B_SC_CONVERGE_CTRL 0x1260 +#define VDEC_B_SC_LOOP_CTRL 0x1264 +#define VDEC_B_COMB_2D_HFS_CFG 0x1268 +#define VDEC_B_COMB_2D_HFD_CFG 0x126C +#define VDEC_B_COMB_2D_LF_CFG 0x1270 +#define VDEC_B_COMB_2D_BLEND 0x1274 +#define VDEC_B_COMB_MISC_CTRL 0x1278 +#define VDEC_B_COMB_FLAT_THRESH_CTRL 0x127C +#define VDEC_B_COMB_TEST 0x1280 +#define VDEC_B_BP_MISC_CTRL 0x1284 +#define VDEC_B_VCR_DET_CTRL 0x1288 +#define VDEC_B_NOISE_DET_CTRL 0x128C +#define VDEC_B_COMB_FLAT_NOISE_CTRL 0x1290 +#define VDEC_B_VERSION 0x13F8 +#define VDEC_B_SOFT_RST_CTRL 0x13FC + +/* Video Decoder C Registers */ +#define VDEC_C_MODE_CTRL 0x1400 +#define VDEC_C_OUT_CTRL1 0x1404 +#define VDEC_C_OUT_CTRL_NS 0x1408 +#define VDEC_C_GEN_STAT 0x140C +#define VDEC_C_INT_STAT_MASK 0x1410 +#define VDEC_C_LUMA_CTRL 0x1414 +#define VDEC_C_CHROMA_CTRL 0x1418 +#define VDEC_C_CRUSH_CTRL 0x141C +#define VDEC_C_HORIZ_TIM_CTRL 0x1420 +#define VDEC_C_VERT_TIM_CTRL 0x1424 +#define VDEC_C_MISC_TIM_CTRL 0x1428 +#define VDEC_C_FIELD_COUNT 0x142C +#define VDEC_C_HSCALE_CTRL 0x1430 +#define VDEC_C_VSCALE_CTRL 0x1434 +#define VDEC_C_MAN_VGA_CTRL 0x1438 +#define VDEC_C_MAN_AGC_CTRL 0x143C +#define VDEC_C_DFE_CTRL1 0x1440 +#define VDEC_C_DFE_CTRL2 0x1444 +#define VDEC_C_DFE_CTRL3 0x1448 +#define VDEC_C_PLL_CTRL 0x144C +#define VDEC_C_PLL_CTRL_FAST 0x1450 +#define VDEC_C_HTL_CTRL 0x1454 +#define VDEC_C_SRC_CFG 0x1458 +#define VDEC_C_SC_STEP_SIZE 0x145C +#define VDEC_C_SC_CONVERGE_CTRL 0x1460 +#define VDEC_C_SC_LOOP_CTRL 0x1464 +#define VDEC_C_COMB_2D_HFS_CFG 0x1468 +#define VDEC_C_COMB_2D_HFD_CFG 0x146C +#define VDEC_C_COMB_2D_LF_CFG 0x1470 +#define VDEC_C_COMB_2D_BLEND 0x1474 +#define VDEC_C_COMB_MISC_CTRL 0x1478 +#define VDEC_C_COMB_FLAT_THRESH_CTRL 0x147C +#define VDEC_C_COMB_TEST 0x1480 +#define VDEC_C_BP_MISC_CTRL 0x1484 +#define VDEC_C_VCR_DET_CTRL 0x1488 +#define VDEC_C_NOISE_DET_CTRL 0x148C +#define VDEC_C_COMB_FLAT_NOISE_CTRL 0x1490 +#define VDEC_C_VERSION 0x15F8 +#define VDEC_C_SOFT_RST_CTRL 0x15FC + +/* Video Decoder D Registers */ +#define VDEC_D_MODE_CTRL 0x1600 +#define VDEC_D_OUT_CTRL1 0x1604 +#define VDEC_D_OUT_CTRL_NS 0x1608 +#define VDEC_D_GEN_STAT 0x160C +#define VDEC_D_INT_STAT_MASK 0x1610 +#define VDEC_D_LUMA_CTRL 0x1614 +#define VDEC_D_CHROMA_CTRL 0x1618 +#define VDEC_D_CRUSH_CTRL 0x161C +#define VDEC_D_HORIZ_TIM_CTRL 0x1620 +#define VDEC_D_VERT_TIM_CTRL 0x1624 +#define VDEC_D_MISC_TIM_CTRL 0x1628 +#define VDEC_D_FIELD_COUNT 0x162C +#define VDEC_D_HSCALE_CTRL 0x1630 +#define VDEC_D_VSCALE_CTRL 0x1634 +#define VDEC_D_MAN_VGA_CTRL 0x1638 +#define VDEC_D_MAN_AGC_CTRL 0x163C +#define VDEC_D_DFE_CTRL1 0x1640 +#define VDEC_D_DFE_CTRL2 0x1644 +#define VDEC_D_DFE_CTRL3 0x1648 +#define VDEC_D_PLL_CTRL 0x164C +#define VDEC_D_PLL_CTRL_FAST 0x1650 +#define VDEC_D_HTL_CTRL 0x1654 +#define VDEC_D_SRC_CFG 0x1658 +#define VDEC_D_SC_STEP_SIZE 0x165C +#define VDEC_D_SC_CONVERGE_CTRL 0x1660 +#define VDEC_D_SC_LOOP_CTRL 0x1664 +#define VDEC_D_COMB_2D_HFS_CFG 0x1668 +#define VDEC_D_COMB_2D_HFD_CFG 0x166C +#define VDEC_D_COMB_2D_LF_CFG 0x1670 +#define VDEC_D_COMB_2D_BLEND 0x1674 +#define VDEC_D_COMB_MISC_CTRL 0x1678 +#define VDEC_D_COMB_FLAT_THRESH_CTRL 0x167C +#define VDEC_D_COMB_TEST 0x1680 +#define VDEC_D_BP_MISC_CTRL 0x1684 +#define VDEC_D_VCR_DET_CTRL 0x1688 +#define VDEC_D_NOISE_DET_CTRL 0x168C +#define VDEC_D_COMB_FLAT_NOISE_CTRL 0x1690 +#define VDEC_D_VERSION 0x17F8 +#define VDEC_D_SOFT_RST_CTRL 0x17FC + +/* Video Decoder E Registers */ +#define VDEC_E_MODE_CTRL 0x1800 +#define VDEC_E_OUT_CTRL1 0x1804 +#define VDEC_E_OUT_CTRL_NS 0x1808 +#define VDEC_E_GEN_STAT 0x180C +#define VDEC_E_INT_STAT_MASK 0x1810 +#define VDEC_E_LUMA_CTRL 0x1814 +#define VDEC_E_CHROMA_CTRL 0x1818 +#define VDEC_E_CRUSH_CTRL 0x181C +#define VDEC_E_HORIZ_TIM_CTRL 0x1820 +#define VDEC_E_VERT_TIM_CTRL 0x1824 +#define VDEC_E_MISC_TIM_CTRL 0x1828 +#define VDEC_E_FIELD_COUNT 0x182C +#define VDEC_E_HSCALE_CTRL 0x1830 +#define VDEC_E_VSCALE_CTRL 0x1834 +#define VDEC_E_MAN_VGA_CTRL 0x1838 +#define VDEC_E_MAN_AGC_CTRL 0x183C +#define VDEC_E_DFE_CTRL1 0x1840 +#define VDEC_E_DFE_CTRL2 0x1844 +#define VDEC_E_DFE_CTRL3 0x1848 +#define VDEC_E_PLL_CTRL 0x184C +#define VDEC_E_PLL_CTRL_FAST 0x1850 +#define VDEC_E_HTL_CTRL 0x1854 +#define VDEC_E_SRC_CFG 0x1858 +#define VDEC_E_SC_STEP_SIZE 0x185C +#define VDEC_E_SC_CONVERGE_CTRL 0x1860 +#define VDEC_E_SC_LOOP_CTRL 0x1864 +#define VDEC_E_COMB_2D_HFS_CFG 0x1868 +#define VDEC_E_COMB_2D_HFD_CFG 0x186C +#define VDEC_E_COMB_2D_LF_CFG 0x1870 +#define VDEC_E_COMB_2D_BLEND 0x1874 +#define VDEC_E_COMB_MISC_CTRL 0x1878 +#define VDEC_E_COMB_FLAT_THRESH_CTRL 0x187C +#define VDEC_E_COMB_TEST 0x1880 +#define VDEC_E_BP_MISC_CTRL 0x1884 +#define VDEC_E_VCR_DET_CTRL 0x1888 +#define VDEC_E_NOISE_DET_CTRL 0x188C +#define VDEC_E_COMB_FLAT_NOISE_CTRL 0x1890 +#define VDEC_E_VERSION 0x19F8 +#define VDEC_E_SOFT_RST_CTRL 0x19FC + +/* Video Decoder F Registers */ +#define VDEC_F_MODE_CTRL 0x1A00 +#define VDEC_F_OUT_CTRL1 0x1A04 +#define VDEC_F_OUT_CTRL_NS 0x1A08 +#define VDEC_F_GEN_STAT 0x1A0C +#define VDEC_F_INT_STAT_MASK 0x1A10 +#define VDEC_F_LUMA_CTRL 0x1A14 +#define VDEC_F_CHROMA_CTRL 0x1A18 +#define VDEC_F_CRUSH_CTRL 0x1A1C +#define VDEC_F_HORIZ_TIM_CTRL 0x1A20 +#define VDEC_F_VERT_TIM_CTRL 0x1A24 +#define VDEC_F_MISC_TIM_CTRL 0x1A28 +#define VDEC_F_FIELD_COUNT 0x1A2C +#define VDEC_F_HSCALE_CTRL 0x1A30 +#define VDEC_F_VSCALE_CTRL 0x1A34 +#define VDEC_F_MAN_VGA_CTRL 0x1A38 +#define VDEC_F_MAN_AGC_CTRL 0x1A3C +#define VDEC_F_DFE_CTRL1 0x1A40 +#define VDEC_F_DFE_CTRL2 0x1A44 +#define VDEC_F_DFE_CTRL3 0x1A48 +#define VDEC_F_PLL_CTRL 0x1A4C +#define VDEC_F_PLL_CTRL_FAST 0x1A50 +#define VDEC_F_HTL_CTRL 0x1A54 +#define VDEC_F_SRC_CFG 0x1A58 +#define VDEC_F_SC_STEP_SIZE 0x1A5C +#define VDEC_F_SC_CONVERGE_CTRL 0x1A60 +#define VDEC_F_SC_LOOP_CTRL 0x1A64 +#define VDEC_F_COMB_2D_HFS_CFG 0x1A68 +#define VDEC_F_COMB_2D_HFD_CFG 0x1A6C +#define VDEC_F_COMB_2D_LF_CFG 0x1A70 +#define VDEC_F_COMB_2D_BLEND 0x1A74 +#define VDEC_F_COMB_MISC_CTRL 0x1A78 +#define VDEC_F_COMB_FLAT_THRESH_CTRL 0x1A7C +#define VDEC_F_COMB_TEST 0x1A80 +#define VDEC_F_BP_MISC_CTRL 0x1A84 +#define VDEC_F_VCR_DET_CTRL 0x1A88 +#define VDEC_F_NOISE_DET_CTRL 0x1A8C +#define VDEC_F_COMB_FLAT_NOISE_CTRL 0x1A90 +#define VDEC_F_VERSION 0x1BF8 +#define VDEC_F_SOFT_RST_CTRL 0x1BFC + +/* Video Decoder G Registers */ +#define VDEC_G_MODE_CTRL 0x1C00 +#define VDEC_G_OUT_CTRL1 0x1C04 +#define VDEC_G_OUT_CTRL_NS 0x1C08 +#define VDEC_G_GEN_STAT 0x1C0C +#define VDEC_G_INT_STAT_MASK 0x1C10 +#define VDEC_G_LUMA_CTRL 0x1C14 +#define VDEC_G_CHROMA_CTRL 0x1C18 +#define VDEC_G_CRUSH_CTRL 0x1C1C +#define VDEC_G_HORIZ_TIM_CTRL 0x1C20 +#define VDEC_G_VERT_TIM_CTRL 0x1C24 +#define VDEC_G_MISC_TIM_CTRL 0x1C28 +#define VDEC_G_FIELD_COUNT 0x1C2C +#define VDEC_G_HSCALE_CTRL 0x1C30 +#define VDEC_G_VSCALE_CTRL 0x1C34 +#define VDEC_G_MAN_VGA_CTRL 0x1C38 +#define VDEC_G_MAN_AGC_CTRL 0x1C3C +#define VDEC_G_DFE_CTRL1 0x1C40 +#define VDEC_G_DFE_CTRL2 0x1C44 +#define VDEC_G_DFE_CTRL3 0x1C48 +#define VDEC_G_PLL_CTRL 0x1C4C +#define VDEC_G_PLL_CTRL_FAST 0x1C50 +#define VDEC_G_HTL_CTRL 0x1C54 +#define VDEC_G_SRC_CFG 0x1C58 +#define VDEC_G_SC_STEP_SIZE 0x1C5C +#define VDEC_G_SC_CONVERGE_CTRL 0x1C60 +#define VDEC_G_SC_LOOP_CTRL 0x1C64 +#define VDEC_G_COMB_2D_HFS_CFG 0x1C68 +#define VDEC_G_COMB_2D_HFD_CFG 0x1C6C +#define VDEC_G_COMB_2D_LF_CFG 0x1C70 +#define VDEC_G_COMB_2D_BLEND 0x1C74 +#define VDEC_G_COMB_MISC_CTRL 0x1C78 +#define VDEC_G_COMB_FLAT_THRESH_CTRL 0x1C7C +#define VDEC_G_COMB_TEST 0x1C80 +#define VDEC_G_BP_MISC_CTRL 0x1C84 +#define VDEC_G_VCR_DET_CTRL 0x1C88 +#define VDEC_G_NOISE_DET_CTRL 0x1C8C +#define VDEC_G_COMB_FLAT_NOISE_CTRL 0x1C90 +#define VDEC_G_VERSION 0x1DF8 +#define VDEC_G_SOFT_RST_CTRL 0x1DFC + +/* Video Decoder H Registers */ +#define VDEC_H_MODE_CTRL 0x1E00 +#define VDEC_H_OUT_CTRL1 0x1E04 +#define VDEC_H_OUT_CTRL_NS 0x1E08 +#define VDEC_H_GEN_STAT 0x1E0C +#define VDEC_H_INT_STAT_MASK 0x1E1E +#define VDEC_H_LUMA_CTRL 0x1E14 +#define VDEC_H_CHROMA_CTRL 0x1E18 +#define VDEC_H_CRUSH_CTRL 0x1E1C +#define VDEC_H_HORIZ_TIM_CTRL 0x1E20 +#define VDEC_H_VERT_TIM_CTRL 0x1E24 +#define VDEC_H_MISC_TIM_CTRL 0x1E28 +#define VDEC_H_FIELD_COUNT 0x1E2C +#define VDEC_H_HSCALE_CTRL 0x1E30 +#define VDEC_H_VSCALE_CTRL 0x1E34 +#define VDEC_H_MAN_VGA_CTRL 0x1E38 +#define VDEC_H_MAN_AGC_CTRL 0x1E3C +#define VDEC_H_DFE_CTRL1 0x1E40 +#define VDEC_H_DFE_CTRL2 0x1E44 +#define VDEC_H_DFE_CTRL3 0x1E48 +#define VDEC_H_PLL_CTRL 0x1E4C +#define VDEC_H_PLL_CTRL_FAST 0x1E50 +#define VDEC_H_HTL_CTRL 0x1E54 +#define VDEC_H_SRC_CFG 0x1E58 +#define VDEC_H_SC_STEP_SIZE 0x1E5C +#define VDEC_H_SC_CONVERGE_CTRL 0x1E60 +#define VDEC_H_SC_LOOP_CTRL 0x1E64 +#define VDEC_H_COMB_2D_HFS_CFG 0x1E68 +#define VDEC_H_COMB_2D_HFD_CFG 0x1E6C +#define VDEC_H_COMB_2D_LF_CFG 0x1E70 +#define VDEC_H_COMB_2D_BLEND 0x1E74 +#define VDEC_H_COMB_MISC_CTRL 0x1E78 +#define VDEC_H_COMB_FLAT_THRESH_CTRL 0x1E7C +#define VDEC_H_COMB_TEST 0x1E80 +#define VDEC_H_BP_MISC_CTRL 0x1E84 +#define VDEC_H_VCR_DET_CTRL 0x1E88 +#define VDEC_H_NOISE_DET_CTRL 0x1E8C +#define VDEC_H_COMB_FLAT_NOISE_CTRL 0x1E90 +#define VDEC_H_VERSION 0x1FF8 +#define VDEC_H_SOFT_RST_CTRL 0x1FFC + +/*****************************************************************************/ +/* LUMA_CTRL register fields */ +#define VDEC_A_BRITE_CTRL 0x1014 +#define VDEC_A_CNTRST_CTRL 0x1015 +#define VDEC_A_PEAK_SEL 0x1016 + +/*****************************************************************************/ +/* CHROMA_CTRL register fields */ +#define VDEC_A_USAT_CTRL 0x1018 +#define VDEC_A_VSAT_CTRL 0x1019 +#define VDEC_A_HUE_CTRL 0x101A + +#endif diff --git a/drivers/media/video/cx25821/cx25821-medusa-video.c b/drivers/media/video/cx25821/cx25821-medusa-video.c new file mode 100644 index 00000000000..fc780d0908d --- /dev/null +++ b/drivers/media/video/cx25821/cx25821-medusa-video.c @@ -0,0 +1,872 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "cx25821.h" +#include "cx25821-medusa-video.h" +#include "cx25821-biffuncs.h" + +/* + * medusa_enable_bluefield_output() + * + * Enable the generation of blue filed output if no video + * + */ +static void medusa_enable_bluefield_output(struct cx25821_dev *dev, int channel, + int enable) +{ + int ret_val = 1; + u32 value = 0; + u32 tmp = 0; + int out_ctrl = OUT_CTRL1; + int out_ctrl_ns = OUT_CTRL_NS; + + switch (channel) { + default: + case VDEC_A: + break; + case VDEC_B: + out_ctrl = VDEC_B_OUT_CTRL1; + out_ctrl_ns = VDEC_B_OUT_CTRL_NS; + break; + case VDEC_C: + out_ctrl = VDEC_C_OUT_CTRL1; + out_ctrl_ns = VDEC_C_OUT_CTRL_NS; + break; + case VDEC_D: + out_ctrl = VDEC_D_OUT_CTRL1; + out_ctrl_ns = VDEC_D_OUT_CTRL_NS; + break; + case VDEC_E: + out_ctrl = VDEC_E_OUT_CTRL1; + out_ctrl_ns = VDEC_E_OUT_CTRL_NS; + return; + case VDEC_F: + out_ctrl = VDEC_F_OUT_CTRL1; + out_ctrl_ns = VDEC_F_OUT_CTRL_NS; + return; + case VDEC_G: + out_ctrl = VDEC_G_OUT_CTRL1; + out_ctrl_ns = VDEC_G_OUT_CTRL_NS; + return; + case VDEC_H: + out_ctrl = VDEC_H_OUT_CTRL1; + out_ctrl_ns = VDEC_H_OUT_CTRL_NS; + return; + } + + value = cx25821_i2c_read(&dev->i2c_bus[0], out_ctrl, &tmp); + value &= 0xFFFFFF7F; /* clear BLUE_FIELD_EN */ + if (enable) + value |= 0x00000080; /* set BLUE_FIELD_EN */ + ret_val = cx25821_i2c_write(&dev->i2c_bus[0], out_ctrl, value); + + value = cx25821_i2c_read(&dev->i2c_bus[0], out_ctrl_ns, &tmp); + value &= 0xFFFFFF7F; + if (enable) + value |= 0x00000080; /* set BLUE_FIELD_EN */ + ret_val = cx25821_i2c_write(&dev->i2c_bus[0], out_ctrl_ns, value); +} + +static int medusa_initialize_ntsc(struct cx25821_dev *dev) +{ + int ret_val = 0; + int i = 0; + u32 value = 0; + u32 tmp = 0; + + mutex_lock(&dev->lock); + + for (i = 0; i < MAX_DECODERS; i++) { + /* set video format NTSC-M */ + value = + cx25821_i2c_read(&dev->i2c_bus[0], MODE_CTRL + (0x200 * i), + &tmp); + value &= 0xFFFFFFF0; + /* enable the fast locking mode bit[16] */ + value |= 0x10001; + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], MODE_CTRL + (0x200 * i), + value); + + /* resolution NTSC 720x480 */ + value = + cx25821_i2c_read(&dev->i2c_bus[0], + HORIZ_TIM_CTRL + (0x200 * i), &tmp); + value &= 0x00C00C00; + value |= 0x612D0074; + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], + HORIZ_TIM_CTRL + (0x200 * i), value); + + value = + cx25821_i2c_read(&dev->i2c_bus[0], + VERT_TIM_CTRL + (0x200 * i), &tmp); + value &= 0x00C00C00; + value |= 0x1C1E001A; /* vblank_cnt + 2 to get camera ID */ + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], + VERT_TIM_CTRL + (0x200 * i), value); + + /* chroma subcarrier step size */ + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], + SC_STEP_SIZE + (0x200 * i), 0x43E00000); + + /* enable VIP optional active */ + value = + cx25821_i2c_read(&dev->i2c_bus[0], + OUT_CTRL_NS + (0x200 * i), &tmp); + value &= 0xFFFBFFFF; + value |= 0x00040000; + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], + OUT_CTRL_NS + (0x200 * i), value); + + /* enable VIP optional active (VIP_OPT_AL) for direct output. */ + value = + cx25821_i2c_read(&dev->i2c_bus[0], OUT_CTRL1 + (0x200 * i), + &tmp); + value &= 0xFFFBFFFF; + value |= 0x00040000; + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], OUT_CTRL1 + (0x200 * i), + value); + + /* + * clear VPRES_VERT_EN bit, fixes the chroma run away problem + * when the input switching rate < 16 fields + */ + value = + cx25821_i2c_read(&dev->i2c_bus[0], + MISC_TIM_CTRL + (0x200 * i), &tmp); + /* disable special play detection */ + value = setBitAtPos(value, 14); + value = clearBitAtPos(value, 15); + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], + MISC_TIM_CTRL + (0x200 * i), value); + + /* set vbi_gate_en to 0 */ + value = + cx25821_i2c_read(&dev->i2c_bus[0], DFE_CTRL1 + (0x200 * i), + &tmp); + value = clearBitAtPos(value, 29); + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], DFE_CTRL1 + (0x200 * i), + value); + + /* Enable the generation of blue field output if no video */ + medusa_enable_bluefield_output(dev, i, 1); + } + + for (i = 0; i < MAX_ENCODERS; i++) { + /* NTSC hclock */ + value = + cx25821_i2c_read(&dev->i2c_bus[0], + DENC_A_REG_1 + (0x100 * i), &tmp); + value &= 0xF000FC00; + value |= 0x06B402D0; + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], + DENC_A_REG_1 + (0x100 * i), value); + + /* burst begin and burst end */ + value = + cx25821_i2c_read(&dev->i2c_bus[0], + DENC_A_REG_2 + (0x100 * i), &tmp); + value &= 0xFF000000; + value |= 0x007E9054; + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], + DENC_A_REG_2 + (0x100 * i), value); + + value = + cx25821_i2c_read(&dev->i2c_bus[0], + DENC_A_REG_3 + (0x100 * i), &tmp); + value &= 0xFC00FE00; + value |= 0x00EC00F0; + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], + DENC_A_REG_3 + (0x100 * i), value); + + /* set NTSC vblank, no phase alternation, 7.5 IRE pedestal */ + value = + cx25821_i2c_read(&dev->i2c_bus[0], + DENC_A_REG_4 + (0x100 * i), &tmp); + value &= 0x00FCFFFF; + value |= 0x13020000; + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], + DENC_A_REG_4 + (0x100 * i), value); + + value = + cx25821_i2c_read(&dev->i2c_bus[0], + DENC_A_REG_5 + (0x100 * i), &tmp); + value &= 0xFFFF0000; + value |= 0x0000E575; + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], + DENC_A_REG_5 + (0x100 * i), value); + + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], + DENC_A_REG_6 + (0x100 * i), 0x009A89C1); + + /* Subcarrier Increment */ + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], + DENC_A_REG_7 + (0x100 * i), 0x21F07C1F); + } + + /* set picture resolutions */ + /* 0 - 720 */ + ret_val = cx25821_i2c_write(&dev->i2c_bus[0], HSCALE_CTRL, 0x0); + /* 0 - 480 */ + ret_val = cx25821_i2c_write(&dev->i2c_bus[0], VSCALE_CTRL, 0x0); + + /* set Bypass input format to NTSC 525 lines */ + value = cx25821_i2c_read(&dev->i2c_bus[0], BYP_AB_CTRL, &tmp); + value |= 0x00080200; + ret_val = cx25821_i2c_write(&dev->i2c_bus[0], BYP_AB_CTRL, value); + + mutex_unlock(&dev->lock); + + return ret_val; +} + +static int medusa_PALCombInit(struct cx25821_dev *dev, int dec) +{ + int ret_val = -1; + u32 value = 0, tmp = 0; + + /* Setup for 2D threshold */ + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], COMB_2D_HFS_CFG + (0x200 * dec), + 0x20002861); + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], COMB_2D_HFD_CFG + (0x200 * dec), + 0x20002861); + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], COMB_2D_LF_CFG + (0x200 * dec), + 0x200A1023); + + /* Setup flat chroma and luma thresholds */ + value = + cx25821_i2c_read(&dev->i2c_bus[0], + COMB_FLAT_THRESH_CTRL + (0x200 * dec), &tmp); + value &= 0x06230000; + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], + COMB_FLAT_THRESH_CTRL + (0x200 * dec), value); + + /* set comb 2D blend */ + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], COMB_2D_BLEND + (0x200 * dec), + 0x210F0F0F); + + /* COMB MISC CONTROL */ + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], COMB_MISC_CTRL + (0x200 * dec), + 0x41120A7F); + + return ret_val; +} + +static int medusa_initialize_pal(struct cx25821_dev *dev) +{ + int ret_val = 0; + int i = 0; + u32 value = 0; + u32 tmp = 0; + + mutex_lock(&dev->lock); + + for (i = 0; i < MAX_DECODERS; i++) { + /* set video format PAL-BDGHI */ + value = + cx25821_i2c_read(&dev->i2c_bus[0], MODE_CTRL + (0x200 * i), + &tmp); + value &= 0xFFFFFFF0; + /* enable the fast locking mode bit[16] */ + value |= 0x10004; + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], MODE_CTRL + (0x200 * i), + value); + + /* resolution PAL 720x576 */ + value = + cx25821_i2c_read(&dev->i2c_bus[0], + HORIZ_TIM_CTRL + (0x200 * i), &tmp); + value &= 0x00C00C00; + value |= 0x632D007D; + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], + HORIZ_TIM_CTRL + (0x200 * i), value); + + /* vblank656_cnt=x26, vactive_cnt=240h, vblank_cnt=x24 */ + value = + cx25821_i2c_read(&dev->i2c_bus[0], + VERT_TIM_CTRL + (0x200 * i), &tmp); + value &= 0x00C00C00; + value |= 0x28240026; /* vblank_cnt + 2 to get camera ID */ + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], + VERT_TIM_CTRL + (0x200 * i), value); + + /* chroma subcarrier step size */ + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], + SC_STEP_SIZE + (0x200 * i), 0x5411E2D0); + + /* enable VIP optional active */ + value = + cx25821_i2c_read(&dev->i2c_bus[0], + OUT_CTRL_NS + (0x200 * i), &tmp); + value &= 0xFFFBFFFF; + value |= 0x00040000; + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], + OUT_CTRL_NS + (0x200 * i), value); + + /* enable VIP optional active (VIP_OPT_AL) for direct output. */ + value = + cx25821_i2c_read(&dev->i2c_bus[0], OUT_CTRL1 + (0x200 * i), + &tmp); + value &= 0xFFFBFFFF; + value |= 0x00040000; + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], OUT_CTRL1 + (0x200 * i), + value); + + /* + * clear VPRES_VERT_EN bit, fixes the chroma run away problem + * when the input switching rate < 16 fields + */ + value = + cx25821_i2c_read(&dev->i2c_bus[0], + MISC_TIM_CTRL + (0x200 * i), &tmp); + /* disable special play detection */ + value = setBitAtPos(value, 14); + value = clearBitAtPos(value, 15); + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], + MISC_TIM_CTRL + (0x200 * i), value); + + /* set vbi_gate_en to 0 */ + value = + cx25821_i2c_read(&dev->i2c_bus[0], DFE_CTRL1 + (0x200 * i), + &tmp); + value = clearBitAtPos(value, 29); + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], DFE_CTRL1 + (0x200 * i), + value); + + medusa_PALCombInit(dev, i); + + /* Enable the generation of blue field output if no video */ + medusa_enable_bluefield_output(dev, i, 1); + } + + for (i = 0; i < MAX_ENCODERS; i++) { + /* PAL hclock */ + value = + cx25821_i2c_read(&dev->i2c_bus[0], + DENC_A_REG_1 + (0x100 * i), &tmp); + value &= 0xF000FC00; + value |= 0x06C002D0; + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], + DENC_A_REG_1 + (0x100 * i), value); + + /* burst begin and burst end */ + value = + cx25821_i2c_read(&dev->i2c_bus[0], + DENC_A_REG_2 + (0x100 * i), &tmp); + value &= 0xFF000000; + value |= 0x007E9754; + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], + DENC_A_REG_2 + (0x100 * i), value); + + /* hblank and vactive */ + value = + cx25821_i2c_read(&dev->i2c_bus[0], + DENC_A_REG_3 + (0x100 * i), &tmp); + value &= 0xFC00FE00; + value |= 0x00FC0120; + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], + DENC_A_REG_3 + (0x100 * i), value); + + /* set PAL vblank, phase alternation, 0 IRE pedestal */ + value = + cx25821_i2c_read(&dev->i2c_bus[0], + DENC_A_REG_4 + (0x100 * i), &tmp); + value &= 0x00FCFFFF; + value |= 0x14010000; + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], + DENC_A_REG_4 + (0x100 * i), value); + + value = + cx25821_i2c_read(&dev->i2c_bus[0], + DENC_A_REG_5 + (0x100 * i), &tmp); + value &= 0xFFFF0000; + value |= 0x0000F078; + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], + DENC_A_REG_5 + (0x100 * i), value); + + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], + DENC_A_REG_6 + (0x100 * i), 0x00A493CF); + + /* Subcarrier Increment */ + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], + DENC_A_REG_7 + (0x100 * i), 0x2A098ACB); + } + + /* set picture resolutions */ + /* 0 - 720 */ + ret_val = cx25821_i2c_write(&dev->i2c_bus[0], HSCALE_CTRL, 0x0); + /* 0 - 576 */ + ret_val = cx25821_i2c_write(&dev->i2c_bus[0], VSCALE_CTRL, 0x0); + + /* set Bypass input format to PAL 625 lines */ + value = cx25821_i2c_read(&dev->i2c_bus[0], BYP_AB_CTRL, &tmp); + value &= 0xFFF7FDFF; + ret_val = cx25821_i2c_write(&dev->i2c_bus[0], BYP_AB_CTRL, value); + + mutex_unlock(&dev->lock); + + return ret_val; +} + +int medusa_set_videostandard(struct cx25821_dev *dev) +{ + int status = STATUS_SUCCESS; + u32 value = 0, tmp = 0; + + if (dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK) + status = medusa_initialize_pal(dev); + else + status = medusa_initialize_ntsc(dev); + + /* Enable DENC_A output */ + value = cx25821_i2c_read(&dev->i2c_bus[0], DENC_A_REG_4, &tmp); + value = setBitAtPos(value, 4); + status = cx25821_i2c_write(&dev->i2c_bus[0], DENC_A_REG_4, value); + + /* Enable DENC_B output */ + value = cx25821_i2c_read(&dev->i2c_bus[0], DENC_B_REG_4, &tmp); + value = setBitAtPos(value, 4); + status = cx25821_i2c_write(&dev->i2c_bus[0], DENC_B_REG_4, value); + + return status; +} + +void medusa_set_resolution(struct cx25821_dev *dev, int width, + int decoder_select) +{ + int decoder = 0; + int decoder_count = 0; + int ret_val = 0; + u32 hscale = 0x0; + u32 vscale = 0x0; + const int MAX_WIDTH = 720; + + mutex_lock(&dev->lock); + + /* validate the width - cannot be negative */ + if (width > MAX_WIDTH) { + pr_info("%s(): width %d > MAX_WIDTH %d ! resetting to MAX_WIDTH\n", + __func__, width, MAX_WIDTH); + width = MAX_WIDTH; + } + + if (decoder_select <= 7 && decoder_select >= 0) { + decoder = decoder_select; + decoder_count = decoder_select + 1; + } else { + decoder = 0; + decoder_count = _num_decoders; + } + + switch (width) { + case 320: + hscale = 0x13E34B; + vscale = 0x0; + break; + + case 352: + hscale = 0x10A273; + vscale = 0x0; + break; + + case 176: + hscale = 0x3115B2; + vscale = 0x1E00; + break; + + case 160: + hscale = 0x378D84; + vscale = 0x1E00; + break; + + default: /* 720 */ + hscale = 0x0; + vscale = 0x0; + break; + } + + for (; decoder < decoder_count; decoder++) { + /* write scaling values for each decoder */ + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], + HSCALE_CTRL + (0x200 * decoder), hscale); + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], + VSCALE_CTRL + (0x200 * decoder), vscale); + } + + mutex_unlock(&dev->lock); +} + +static void medusa_set_decoderduration(struct cx25821_dev *dev, int decoder, + int duration) +{ + int ret_val = 0; + u32 fld_cnt = 0; + u32 tmp = 0; + u32 disp_cnt_reg = DISP_AB_CNT; + + mutex_lock(&dev->lock); + + /* no support */ + if (decoder < VDEC_A && decoder > VDEC_H) { + mutex_unlock(&dev->lock); + return; + } + + switch (decoder) { + default: + break; + case VDEC_C: + case VDEC_D: + disp_cnt_reg = DISP_CD_CNT; + break; + case VDEC_E: + case VDEC_F: + disp_cnt_reg = DISP_EF_CNT; + break; + case VDEC_G: + case VDEC_H: + disp_cnt_reg = DISP_GH_CNT; + break; + } + + _display_field_cnt[decoder] = duration; + + /* update hardware */ + fld_cnt = cx25821_i2c_read(&dev->i2c_bus[0], disp_cnt_reg, &tmp); + + if (!(decoder % 2)) { /* EVEN decoder */ + fld_cnt &= 0xFFFF0000; + fld_cnt |= duration; + } else { + fld_cnt &= 0x0000FFFF; + fld_cnt |= ((u32) duration) << 16; + } + + ret_val = cx25821_i2c_write(&dev->i2c_bus[0], disp_cnt_reg, fld_cnt); + + mutex_unlock(&dev->lock); +} + +/* Map to Medusa register setting */ +static int mapM(int srcMin, + int srcMax, int srcVal, int dstMin, int dstMax, int *dstVal) +{ + int numerator; + int denominator; + int quotient; + + if ((srcMin == srcMax) || (srcVal < srcMin) || (srcVal > srcMax)) + return -1; + /* + * This is the overall expression used: + * *dstVal = + * (srcVal - srcMin)*(dstMax - dstMin) / (srcMax - srcMin) + dstMin; + * but we need to account for rounding so below we use the modulus + * operator to find the remainder and increment if necessary. + */ + numerator = (srcVal - srcMin) * (dstMax - dstMin); + denominator = srcMax - srcMin; + quotient = numerator / denominator; + + if (2 * (numerator % denominator) >= denominator) + quotient++; + + *dstVal = quotient + dstMin; + + return 0; +} + +static unsigned long convert_to_twos(long numeric, unsigned long bits_len) +{ + unsigned char temp; + + if (numeric >= 0) + return numeric; + else { + temp = ~(abs(numeric) & 0xFF); + temp += 1; + return temp; + } +} + +int medusa_set_brightness(struct cx25821_dev *dev, int brightness, int decoder) +{ + int ret_val = 0; + int value = 0; + u32 val = 0, tmp = 0; + + mutex_lock(&dev->lock); + if ((brightness > VIDEO_PROCAMP_MAX) + || (brightness < VIDEO_PROCAMP_MIN)) { + mutex_unlock(&dev->lock); + return -1; + } + ret_val = + mapM(VIDEO_PROCAMP_MIN, VIDEO_PROCAMP_MAX, brightness, + SIGNED_BYTE_MIN, SIGNED_BYTE_MAX, &value); + value = convert_to_twos(value, 8); + val = + cx25821_i2c_read(&dev->i2c_bus[0], + VDEC_A_BRITE_CTRL + (0x200 * decoder), &tmp); + val &= 0xFFFFFF00; + ret_val |= + cx25821_i2c_write(&dev->i2c_bus[0], + VDEC_A_BRITE_CTRL + (0x200 * decoder), + val | value); + mutex_unlock(&dev->lock); + return ret_val; +} + +int medusa_set_contrast(struct cx25821_dev *dev, int contrast, int decoder) +{ + int ret_val = 0; + int value = 0; + u32 val = 0, tmp = 0; + + mutex_lock(&dev->lock); + + if ((contrast > VIDEO_PROCAMP_MAX) || (contrast < VIDEO_PROCAMP_MIN)) { + mutex_unlock(&dev->lock); + return -1; + } + + ret_val = + mapM(VIDEO_PROCAMP_MIN, VIDEO_PROCAMP_MAX, contrast, + UNSIGNED_BYTE_MIN, UNSIGNED_BYTE_MAX, &value); + val = + cx25821_i2c_read(&dev->i2c_bus[0], + VDEC_A_CNTRST_CTRL + (0x200 * decoder), &tmp); + val &= 0xFFFFFF00; + ret_val |= + cx25821_i2c_write(&dev->i2c_bus[0], + VDEC_A_CNTRST_CTRL + (0x200 * decoder), + val | value); + + mutex_unlock(&dev->lock); + return ret_val; +} + +int medusa_set_hue(struct cx25821_dev *dev, int hue, int decoder) +{ + int ret_val = 0; + int value = 0; + u32 val = 0, tmp = 0; + + mutex_lock(&dev->lock); + + if ((hue > VIDEO_PROCAMP_MAX) || (hue < VIDEO_PROCAMP_MIN)) { + mutex_unlock(&dev->lock); + return -1; + } + + ret_val = + mapM(VIDEO_PROCAMP_MIN, VIDEO_PROCAMP_MAX, hue, SIGNED_BYTE_MIN, + SIGNED_BYTE_MAX, &value); + + value = convert_to_twos(value, 8); + val = + cx25821_i2c_read(&dev->i2c_bus[0], + VDEC_A_HUE_CTRL + (0x200 * decoder), &tmp); + val &= 0xFFFFFF00; + + ret_val |= + cx25821_i2c_write(&dev->i2c_bus[0], + VDEC_A_HUE_CTRL + (0x200 * decoder), val | value); + + mutex_unlock(&dev->lock); + return ret_val; +} + +int medusa_set_saturation(struct cx25821_dev *dev, int saturation, int decoder) +{ + int ret_val = 0; + int value = 0; + u32 val = 0, tmp = 0; + + mutex_lock(&dev->lock); + + if ((saturation > VIDEO_PROCAMP_MAX) + || (saturation < VIDEO_PROCAMP_MIN)) { + mutex_unlock(&dev->lock); + return -1; + } + + ret_val = + mapM(VIDEO_PROCAMP_MIN, VIDEO_PROCAMP_MAX, saturation, + UNSIGNED_BYTE_MIN, UNSIGNED_BYTE_MAX, &value); + + val = + cx25821_i2c_read(&dev->i2c_bus[0], + VDEC_A_USAT_CTRL + (0x200 * decoder), &tmp); + val &= 0xFFFFFF00; + ret_val |= + cx25821_i2c_write(&dev->i2c_bus[0], + VDEC_A_USAT_CTRL + (0x200 * decoder), + val | value); + + val = + cx25821_i2c_read(&dev->i2c_bus[0], + VDEC_A_VSAT_CTRL + (0x200 * decoder), &tmp); + val &= 0xFFFFFF00; + ret_val |= + cx25821_i2c_write(&dev->i2c_bus[0], + VDEC_A_VSAT_CTRL + (0x200 * decoder), + val | value); + + mutex_unlock(&dev->lock); + return ret_val; +} + +/* Program the display sequence and monitor output. */ + +int medusa_video_init(struct cx25821_dev *dev) +{ + u32 value = 0, tmp = 0; + int ret_val = 0; + int i = 0; + + mutex_lock(&dev->lock); + + _num_decoders = dev->_max_num_decoders; + + /* disable Auto source selection on all video decoders */ + value = cx25821_i2c_read(&dev->i2c_bus[0], MON_A_CTRL, &tmp); + value &= 0xFFFFF0FF; + ret_val = cx25821_i2c_write(&dev->i2c_bus[0], MON_A_CTRL, value); + + if (ret_val < 0) + goto error; + + /* Turn off Master source switch enable */ + value = cx25821_i2c_read(&dev->i2c_bus[0], MON_A_CTRL, &tmp); + value &= 0xFFFFFFDF; + ret_val = cx25821_i2c_write(&dev->i2c_bus[0], MON_A_CTRL, value); + + if (ret_val < 0) + goto error; + + mutex_unlock(&dev->lock); + + for (i = 0; i < _num_decoders; i++) + medusa_set_decoderduration(dev, i, _display_field_cnt[i]); + + mutex_lock(&dev->lock); + + /* Select monitor as DENC A input, power up the DAC */ + value = cx25821_i2c_read(&dev->i2c_bus[0], DENC_AB_CTRL, &tmp); + value &= 0xFF70FF70; + value |= 0x00090008; /* set en_active */ + ret_val = cx25821_i2c_write(&dev->i2c_bus[0], DENC_AB_CTRL, value); + + if (ret_val < 0) + goto error; + + /* enable input is VIP/656 */ + value = cx25821_i2c_read(&dev->i2c_bus[0], BYP_AB_CTRL, &tmp); + value |= 0x00040100; /* enable VIP */ + ret_val = cx25821_i2c_write(&dev->i2c_bus[0], BYP_AB_CTRL, value); + + if (ret_val < 0) + goto error; + + /* select AFE clock to output mode */ + value = cx25821_i2c_read(&dev->i2c_bus[0], AFE_AB_DIAG_CTRL, &tmp); + value &= 0x83FFFFFF; + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], AFE_AB_DIAG_CTRL, + value | 0x10000000); + + if (ret_val < 0) + goto error; + + /* Turn on all of the data out and control output pins. */ + value = cx25821_i2c_read(&dev->i2c_bus[0], PIN_OE_CTRL, &tmp); + value &= 0xFEF0FE00; + if (_num_decoders == MAX_DECODERS) { + /* + * Note: The octal board does not support control pins(bit16-19) + * These bits are ignored in the octal board. + * + * disable VDEC A-C port, default to Mobilygen Interface + */ + value |= 0x010001F8; + } else { + /* disable VDEC A-C port, default to Mobilygen Interface */ + value |= 0x010F0108; + } + + value |= 7; + ret_val = cx25821_i2c_write(&dev->i2c_bus[0], PIN_OE_CTRL, value); + + if (ret_val < 0) + goto error; + + + mutex_unlock(&dev->lock); + + ret_val = medusa_set_videostandard(dev); + + return ret_val; + +error: + mutex_unlock(&dev->lock); + return ret_val; +} diff --git a/drivers/media/video/cx25821/cx25821-medusa-video.h b/drivers/media/video/cx25821/cx25821-medusa-video.h new file mode 100644 index 00000000000..6175e096185 --- /dev/null +++ b/drivers/media/video/cx25821/cx25821-medusa-video.h @@ -0,0 +1,49 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _MEDUSA_VIDEO_H +#define _MEDUSA_VIDEO_H + +#include "cx25821-medusa-defines.h" + +/* Color control constants */ +#define VIDEO_PROCAMP_MIN 0 +#define VIDEO_PROCAMP_MAX 10000 +#define UNSIGNED_BYTE_MIN 0 +#define UNSIGNED_BYTE_MAX 0xFF +#define SIGNED_BYTE_MIN -128 +#define SIGNED_BYTE_MAX 127 + +/* Default video color settings */ +#define SHARPNESS_DEFAULT 50 +#define SATURATION_DEFAULT 5000 +#define BRIGHTNESS_DEFAULT 6200 +#define CONTRAST_DEFAULT 5000 +#define HUE_DEFAULT 5000 + +unsigned short _num_decoders; +unsigned short _num_cameras; + +unsigned int _video_standard; +int _display_field_cnt[MAX_DECODERS]; + +#endif diff --git a/drivers/media/video/cx25821/cx25821-reg.h b/drivers/media/video/cx25821/cx25821-reg.h new file mode 100644 index 00000000000..a3fc25a4dc0 --- /dev/null +++ b/drivers/media/video/cx25821/cx25821-reg.h @@ -0,0 +1,1592 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __CX25821_REGISTERS__ +#define __CX25821_REGISTERS__ + +/* Risc Instructions */ +#define RISC_CNT_INC 0x00010000 +#define RISC_CNT_RESET 0x00030000 +#define RISC_IRQ1 0x01000000 +#define RISC_IRQ2 0x02000000 +#define RISC_EOL 0x04000000 +#define RISC_SOL 0x08000000 +#define RISC_WRITE 0x10000000 +#define RISC_SKIP 0x20000000 +#define RISC_JUMP 0x70000000 +#define RISC_SYNC 0x80000000 +#define RISC_RESYNC 0x80008000 +#define RISC_READ 0x90000000 +#define RISC_WRITERM 0xB0000000 +#define RISC_WRITECM 0xC0000000 +#define RISC_WRITECR 0xD0000000 +#define RISC_WRITEC 0x50000000 +#define RISC_READC 0xA0000000 + +#define RISC_SYNC_ODD 0x00000000 +#define RISC_SYNC_EVEN 0x00000200 +#define RISC_SYNC_ODD_VBI 0x00000006 +#define RISC_SYNC_EVEN_VBI 0x00000207 +#define RISC_NOOP 0xF0000000 + +/***************************************************************************** +* ASB SRAM + *****************************************************************************/ +#define TX_SRAM 0x000000 /* Transmit SRAM */ + +/*****************************************************************************/ +#define RX_RAM 0x010000 /* Receive SRAM */ + +/***************************************************************************** +* Application Layer (AL) + *****************************************************************************/ +#define DEV_CNTRL2 0x040000 /* Device control */ +#define FLD_RUN_RISC 0x00000020 + +/* ***************************************************************************** */ +#define PCI_INT_MSK 0x040010 /* PCI interrupt mask */ +#define PCI_INT_STAT 0x040014 /* PCI interrupt status */ +#define PCI_INT_MSTAT 0x040018 /* PCI interrupt masked status */ +#define FLD_HAMMERHEAD_INT (1 << 27) +#define FLD_UART_INT (1 << 26) +#define FLD_IRQN_INT (1 << 25) +#define FLD_TM_INT (1 << 28) +#define FLD_I2C_3_RACK (1 << 27) +#define FLD_I2C_3_INT (1 << 26) +#define FLD_I2C_2_RACK (1 << 25) +#define FLD_I2C_2_INT (1 << 24) +#define FLD_I2C_1_RACK (1 << 23) +#define FLD_I2C_1_INT (1 << 22) + +#define FLD_APB_DMA_BERR_INT (1 << 21) +#define FLD_AL_WR_BERR_INT (1 << 20) +#define FLD_AL_RD_BERR_INT (1 << 19) +#define FLD_RISC_WR_BERR_INT (1 << 18) +#define FLD_RISC_RD_BERR_INT (1 << 17) + +#define FLD_VID_I_INT (1 << 8) +#define FLD_VID_H_INT (1 << 7) +#define FLD_VID_G_INT (1 << 6) +#define FLD_VID_F_INT (1 << 5) +#define FLD_VID_E_INT (1 << 4) +#define FLD_VID_D_INT (1 << 3) +#define FLD_VID_C_INT (1 << 2) +#define FLD_VID_B_INT (1 << 1) +#define FLD_VID_A_INT (1 << 0) + +/* ***************************************************************************** */ +#define VID_A_INT_MSK 0x040020 /* Video A interrupt mask */ +#define VID_A_INT_STAT 0x040024 /* Video A interrupt status */ +#define VID_A_INT_MSTAT 0x040028 /* Video A interrupt masked status */ +#define VID_A_INT_SSTAT 0x04002C /* Video A interrupt set status */ + +/* ***************************************************************************** */ +#define VID_B_INT_MSK 0x040030 /* Video B interrupt mask */ +#define VID_B_INT_STAT 0x040034 /* Video B interrupt status */ +#define VID_B_INT_MSTAT 0x040038 /* Video B interrupt masked status */ +#define VID_B_INT_SSTAT 0x04003C /* Video B interrupt set status */ + +/* ***************************************************************************** */ +#define VID_C_INT_MSK 0x040040 /* Video C interrupt mask */ +#define VID_C_INT_STAT 0x040044 /* Video C interrupt status */ +#define VID_C_INT_MSTAT 0x040048 /* Video C interrupt masked status */ +#define VID_C_INT_SSTAT 0x04004C /* Video C interrupt set status */ + +/* ***************************************************************************** */ +#define VID_D_INT_MSK 0x040050 /* Video D interrupt mask */ +#define VID_D_INT_STAT 0x040054 /* Video D interrupt status */ +#define VID_D_INT_MSTAT 0x040058 /* Video D interrupt masked status */ +#define VID_D_INT_SSTAT 0x04005C /* Video D interrupt set status */ + +/* ***************************************************************************** */ +#define VID_E_INT_MSK 0x040060 /* Video E interrupt mask */ +#define VID_E_INT_STAT 0x040064 /* Video E interrupt status */ +#define VID_E_INT_MSTAT 0x040068 /* Video E interrupt masked status */ +#define VID_E_INT_SSTAT 0x04006C /* Video E interrupt set status */ + +/* ***************************************************************************** */ +#define VID_F_INT_MSK 0x040070 /* Video F interrupt mask */ +#define VID_F_INT_STAT 0x040074 /* Video F interrupt status */ +#define VID_F_INT_MSTAT 0x040078 /* Video F interrupt masked status */ +#define VID_F_INT_SSTAT 0x04007C /* Video F interrupt set status */ + +/* ***************************************************************************** */ +#define VID_G_INT_MSK 0x040080 /* Video G interrupt mask */ +#define VID_G_INT_STAT 0x040084 /* Video G interrupt status */ +#define VID_G_INT_MSTAT 0x040088 /* Video G interrupt masked status */ +#define VID_G_INT_SSTAT 0x04008C /* Video G interrupt set status */ + +/* ***************************************************************************** */ +#define VID_H_INT_MSK 0x040090 /* Video H interrupt mask */ +#define VID_H_INT_STAT 0x040094 /* Video H interrupt status */ +#define VID_H_INT_MSTAT 0x040098 /* Video H interrupt masked status */ +#define VID_H_INT_SSTAT 0x04009C /* Video H interrupt set status */ + +/* ***************************************************************************** */ +#define VID_I_INT_MSK 0x0400A0 /* Video I interrupt mask */ +#define VID_I_INT_STAT 0x0400A4 /* Video I interrupt status */ +#define VID_I_INT_MSTAT 0x0400A8 /* Video I interrupt masked status */ +#define VID_I_INT_SSTAT 0x0400AC /* Video I interrupt set status */ + +/* ***************************************************************************** */ +#define VID_J_INT_MSK 0x0400B0 /* Video J interrupt mask */ +#define VID_J_INT_STAT 0x0400B4 /* Video J interrupt status */ +#define VID_J_INT_MSTAT 0x0400B8 /* Video J interrupt masked status */ +#define VID_J_INT_SSTAT 0x0400BC /* Video J interrupt set status */ + +#define FLD_VID_SRC_OPC_ERR 0x00020000 +#define FLD_VID_DST_OPC_ERR 0x00010000 +#define FLD_VID_SRC_SYNC 0x00002000 +#define FLD_VID_DST_SYNC 0x00001000 +#define FLD_VID_SRC_UF 0x00000200 +#define FLD_VID_DST_OF 0x00000100 +#define FLD_VID_SRC_RISC2 0x00000020 +#define FLD_VID_DST_RISC2 0x00000010 +#define FLD_VID_SRC_RISC1 0x00000002 +#define FLD_VID_DST_RISC1 0x00000001 +#define FLD_VID_SRC_ERRORS (FLD_VID_SRC_OPC_ERR | FLD_VID_SRC_SYNC | FLD_VID_SRC_UF) +#define FLD_VID_DST_ERRORS (FLD_VID_DST_OPC_ERR | FLD_VID_DST_SYNC | FLD_VID_DST_OF) + +/* ***************************************************************************** */ +#define AUD_A_INT_MSK 0x0400C0 /* Audio Int interrupt mask */ +#define AUD_A_INT_STAT 0x0400C4 /* Audio Int interrupt status */ +#define AUD_A_INT_MSTAT 0x0400C8 /* Audio Int interrupt masked status */ +#define AUD_A_INT_SSTAT 0x0400CC /* Audio Int interrupt set status */ + +/* ***************************************************************************** */ +#define AUD_B_INT_MSK 0x0400D0 /* Audio Int interrupt mask */ +#define AUD_B_INT_STAT 0x0400D4 /* Audio Int interrupt status */ +#define AUD_B_INT_MSTAT 0x0400D8 /* Audio Int interrupt masked status */ +#define AUD_B_INT_SSTAT 0x0400DC /* Audio Int interrupt set status */ + +/* ***************************************************************************** */ +#define AUD_C_INT_MSK 0x0400E0 /* Audio Int interrupt mask */ +#define AUD_C_INT_STAT 0x0400E4 /* Audio Int interrupt status */ +#define AUD_C_INT_MSTAT 0x0400E8 /* Audio Int interrupt masked status */ +#define AUD_C_INT_SSTAT 0x0400EC /* Audio Int interrupt set status */ + +/* ***************************************************************************** */ +#define AUD_D_INT_MSK 0x0400F0 /* Audio Int interrupt mask */ +#define AUD_D_INT_STAT 0x0400F4 /* Audio Int interrupt status */ +#define AUD_D_INT_MSTAT 0x0400F8 /* Audio Int interrupt masked status */ +#define AUD_D_INT_SSTAT 0x0400FC /* Audio Int interrupt set status */ + +/* ***************************************************************************** */ +#define AUD_E_INT_MSK 0x040100 /* Audio Int interrupt mask */ +#define AUD_E_INT_STAT 0x040104 /* Audio Int interrupt status */ +#define AUD_E_INT_MSTAT 0x040108 /* Audio Int interrupt masked status */ +#define AUD_E_INT_SSTAT 0x04010C /* Audio Int interrupt set status */ + +#define FLD_AUD_SRC_OPC_ERR 0x00020000 +#define FLD_AUD_DST_OPC_ERR 0x00010000 +#define FLD_AUD_SRC_SYNC 0x00002000 +#define FLD_AUD_DST_SYNC 0x00001000 +#define FLD_AUD_SRC_OF 0x00000200 +#define FLD_AUD_DST_OF 0x00000100 +#define FLD_AUD_SRC_RISCI2 0x00000020 +#define FLD_AUD_DST_RISCI2 0x00000010 +#define FLD_AUD_SRC_RISCI1 0x00000002 +#define FLD_AUD_DST_RISCI1 0x00000001 + +/* ***************************************************************************** */ +#define MBIF_A_INT_MSK 0x040110 /* MBIF Int interrupt mask */ +#define MBIF_A_INT_STAT 0x040114 /* MBIF Int interrupt status */ +#define MBIF_A_INT_MSTAT 0x040118 /* MBIF Int interrupt masked status */ +#define MBIF_A_INT_SSTAT 0x04011C /* MBIF Int interrupt set status */ + +/* ***************************************************************************** */ +#define MBIF_B_INT_MSK 0x040120 /* MBIF Int interrupt mask */ +#define MBIF_B_INT_STAT 0x040124 /* MBIF Int interrupt status */ +#define MBIF_B_INT_MSTAT 0x040128 /* MBIF Int interrupt masked status */ +#define MBIF_B_INT_SSTAT 0x04012C /* MBIF Int interrupt set status */ + +#define FLD_MBIF_DST_OPC_ERR 0x00010000 +#define FLD_MBIF_DST_SYNC 0x00001000 +#define FLD_MBIF_DST_OF 0x00000100 +#define FLD_MBIF_DST_RISCI2 0x00000010 +#define FLD_MBIF_DST_RISCI1 0x00000001 + +/* ***************************************************************************** */ +#define AUD_EXT_INT_MSK 0x040060 /* Audio Ext interrupt mask */ +#define AUD_EXT_INT_STAT 0x040064 /* Audio Ext interrupt status */ +#define AUD_EXT_INT_MSTAT 0x040068 /* Audio Ext interrupt masked status */ +#define AUD_EXT_INT_SSTAT 0x04006C /* Audio Ext interrupt set status */ +#define FLD_AUD_EXT_OPC_ERR 0x00010000 +#define FLD_AUD_EXT_SYNC 0x00001000 +#define FLD_AUD_EXT_OF 0x00000100 +#define FLD_AUD_EXT_RISCI2 0x00000010 +#define FLD_AUD_EXT_RISCI1 0x00000001 + +/* ***************************************************************************** */ +#define GPIO_LO 0x110010 /* Lower of GPIO pins [31:0] */ +#define GPIO_HI 0x110014 /* Upper WORD of GPIO pins [47:31] */ + +#define GPIO_LO_OE 0x110018 /* Lower of GPIO output enable [31:0] */ +#define GPIO_HI_OE 0x11001C /* Upper word of GPIO output enable [47:32] */ + +#define GPIO_LO_INT_MSK 0x11003C /* GPIO interrupt mask */ +#define GPIO_LO_INT_STAT 0x110044 /* GPIO interrupt status */ +#define GPIO_LO_INT_MSTAT 0x11004C /* GPIO interrupt masked status */ +#define GPIO_LO_ISM_SNS 0x110054 /* GPIO interrupt sensitivity */ +#define GPIO_LO_ISM_POL 0x11005C /* GPIO interrupt polarity */ + +#define GPIO_HI_INT_MSK 0x110040 /* GPIO interrupt mask */ +#define GPIO_HI_INT_STAT 0x110048 /* GPIO interrupt status */ +#define GPIO_HI_INT_MSTAT 0x110050 /* GPIO interrupt masked status */ +#define GPIO_HI_ISM_SNS 0x110058 /* GPIO interrupt sensitivity */ +#define GPIO_HI_ISM_POL 0x110060 /* GPIO interrupt polarity */ + +#define FLD_GPIO43_INT (1 << 11) +#define FLD_GPIO42_INT (1 << 10) +#define FLD_GPIO41_INT (1 << 9) +#define FLD_GPIO40_INT (1 << 8) + +#define FLD_GPIO9_INT (1 << 9) +#define FLD_GPIO8_INT (1 << 8) +#define FLD_GPIO7_INT (1 << 7) +#define FLD_GPIO6_INT (1 << 6) +#define FLD_GPIO5_INT (1 << 5) +#define FLD_GPIO4_INT (1 << 4) +#define FLD_GPIO3_INT (1 << 3) +#define FLD_GPIO2_INT (1 << 2) +#define FLD_GPIO1_INT (1 << 1) +#define FLD_GPIO0_INT (1 << 0) + +/* ***************************************************************************** */ +#define TC_REQ 0x040090 /* Rider PCI Express traFFic class request */ + +/* ***************************************************************************** */ +#define TC_REQ_SET 0x040094 /* Rider PCI Express traFFic class request set */ + +/* ***************************************************************************** */ +/* Rider */ +/* ***************************************************************************** */ + +/* PCI Compatible Header */ +/* ***************************************************************************** */ +#define RDR_CFG0 0x050000 +#define RDR_VENDOR_DEVICE_ID_CFG 0x050000 + +/* ***************************************************************************** */ +#define RDR_CFG1 0x050004 + +/* ***************************************************************************** */ +#define RDR_CFG2 0x050008 + +/* ***************************************************************************** */ +#define RDR_CFG3 0x05000C + +/* ***************************************************************************** */ +#define RDR_CFG4 0x050010 + +/* ***************************************************************************** */ +#define RDR_CFG5 0x050014 + +/* ***************************************************************************** */ +#define RDR_CFG6 0x050018 + +/* ***************************************************************************** */ +#define RDR_CFG7 0x05001C + +/* ***************************************************************************** */ +#define RDR_CFG8 0x050020 + +/* ***************************************************************************** */ +#define RDR_CFG9 0x050024 + +/* ***************************************************************************** */ +#define RDR_CFGA 0x050028 + +/* ***************************************************************************** */ +#define RDR_CFGB 0x05002C +#define RDR_SUSSYSTEM_ID_CFG 0x05002C + +/* ***************************************************************************** */ +#define RDR_CFGC 0x050030 + +/* ***************************************************************************** */ +#define RDR_CFGD 0x050034 + +/* ***************************************************************************** */ +#define RDR_CFGE 0x050038 + +/* ***************************************************************************** */ +#define RDR_CFGF 0x05003C + +/* ***************************************************************************** */ +/* PCI-Express Capabilities */ +/* ***************************************************************************** */ +#define RDR_PECAP 0x050040 + +/* ***************************************************************************** */ +#define RDR_PEDEVCAP 0x050044 + +/* ***************************************************************************** */ +#define RDR_PEDEVSC 0x050048 + +/* ***************************************************************************** */ +#define RDR_PELINKCAP 0x05004C + +/* ***************************************************************************** */ +#define RDR_PELINKSC 0x050050 + +/* ***************************************************************************** */ +#define RDR_PMICAP 0x050080 + +/* ***************************************************************************** */ +#define RDR_PMCSR 0x050084 + +/* ***************************************************************************** */ +#define RDR_VPDCAP 0x050090 + +/* ***************************************************************************** */ +#define RDR_VPDDATA 0x050094 + +/* ***************************************************************************** */ +#define RDR_MSICAP 0x0500A0 + +/* ***************************************************************************** */ +#define RDR_MSIARL 0x0500A4 + +/* ***************************************************************************** */ +#define RDR_MSIARU 0x0500A8 + +/* ***************************************************************************** */ +#define RDR_MSIDATA 0x0500AC + +/* ***************************************************************************** */ +/* PCI Express Extended Capabilities */ +/* ***************************************************************************** */ +#define RDR_AERXCAP 0x050100 + +/* ***************************************************************************** */ +#define RDR_AERUESTA 0x050104 + +/* ***************************************************************************** */ +#define RDR_AERUEMSK 0x050108 + +/* ***************************************************************************** */ +#define RDR_AERUESEV 0x05010C + +/* ***************************************************************************** */ +#define RDR_AERCESTA 0x050110 + +/* ***************************************************************************** */ +#define RDR_AERCEMSK 0x050114 + +/* ***************************************************************************** */ +#define RDR_AERCC 0x050118 + +/* ***************************************************************************** */ +#define RDR_AERHL0 0x05011C + +/* ***************************************************************************** */ +#define RDR_AERHL1 0x050120 + +/* ***************************************************************************** */ +#define RDR_AERHL2 0x050124 + +/* ***************************************************************************** */ +#define RDR_AERHL3 0x050128 + +/* ***************************************************************************** */ +#define RDR_VCXCAP 0x050200 + +/* ***************************************************************************** */ +#define RDR_VCCAP1 0x050204 + +/* ***************************************************************************** */ +#define RDR_VCCAP2 0x050208 + +/* ***************************************************************************** */ +#define RDR_VCSC 0x05020C + +/* ***************************************************************************** */ +#define RDR_VCR0_CAP 0x050210 + +/* ***************************************************************************** */ +#define RDR_VCR0_CTRL 0x050214 + +/* ***************************************************************************** */ +#define RDR_VCR0_STAT 0x050218 + +/* ***************************************************************************** */ +#define RDR_VCR1_CAP 0x05021C + +/* ***************************************************************************** */ +#define RDR_VCR1_CTRL 0x050220 + +/* ***************************************************************************** */ +#define RDR_VCR1_STAT 0x050224 + +/* ***************************************************************************** */ +#define RDR_VCR2_CAP 0x050228 + +/* ***************************************************************************** */ +#define RDR_VCR2_CTRL 0x05022C + +/* ***************************************************************************** */ +#define RDR_VCR2_STAT 0x050230 + +/* ***************************************************************************** */ +#define RDR_VCR3_CAP 0x050234 + +/* ***************************************************************************** */ +#define RDR_VCR3_CTRL 0x050238 + +/* ***************************************************************************** */ +#define RDR_VCR3_STAT 0x05023C + +/* ***************************************************************************** */ +#define RDR_VCARB0 0x050240 + +/* ***************************************************************************** */ +#define RDR_VCARB1 0x050244 + +/* ***************************************************************************** */ +#define RDR_VCARB2 0x050248 + +/* ***************************************************************************** */ +#define RDR_VCARB3 0x05024C + +/* ***************************************************************************** */ +#define RDR_VCARB4 0x050250 + +/* ***************************************************************************** */ +#define RDR_VCARB5 0x050254 + +/* ***************************************************************************** */ +#define RDR_VCARB6 0x050258 + +/* ***************************************************************************** */ +#define RDR_VCARB7 0x05025C + +/* ***************************************************************************** */ +#define RDR_RDRSTAT0 0x050300 + +/* ***************************************************************************** */ +#define RDR_RDRSTAT1 0x050304 + +/* ***************************************************************************** */ +#define RDR_RDRCTL0 0x050308 + +/* ***************************************************************************** */ +#define RDR_RDRCTL1 0x05030C + +/* ***************************************************************************** */ +/* Transaction Layer Registers */ +/* ***************************************************************************** */ +#define RDR_TLSTAT0 0x050310 + +/* ***************************************************************************** */ +#define RDR_TLSTAT1 0x050314 + +/* ***************************************************************************** */ +#define RDR_TLCTL0 0x050318 +#define FLD_CFG_UR_CPL_MODE 0x00000040 +#define FLD_CFG_CORR_ERR_QUITE 0x00000020 +#define FLD_CFG_RCB_CK_EN 0x00000010 +#define FLD_CFG_BNDRY_CK_EN 0x00000008 +#define FLD_CFG_BYTE_EN_CK_EN 0x00000004 +#define FLD_CFG_RELAX_ORDER_MSK 0x00000002 +#define FLD_CFG_TAG_ORDER_EN 0x00000001 + +/* ***************************************************************************** */ +#define RDR_TLCTL1 0x05031C + +/* ***************************************************************************** */ +#define RDR_REQRCAL 0x050320 + +/* ***************************************************************************** */ +#define RDR_REQRCAU 0x050324 + +/* ***************************************************************************** */ +#define RDR_REQEPA 0x050328 + +/* ***************************************************************************** */ +#define RDR_REQCTRL 0x05032C + +/* ***************************************************************************** */ +#define RDR_REQSTAT 0x050330 + +/* ***************************************************************************** */ +#define RDR_TL_TEST 0x050334 + +/* ***************************************************************************** */ +#define RDR_VCR01_CTL 0x050348 + +/* ***************************************************************************** */ +#define RDR_VCR23_CTL 0x05034C + +/* ***************************************************************************** */ +#define RDR_RX_VCR0_FC 0x050350 + +/* ***************************************************************************** */ +#define RDR_RX_VCR1_FC 0x050354 + +/* ***************************************************************************** */ +#define RDR_RX_VCR2_FC 0x050358 + +/* ***************************************************************************** */ +#define RDR_RX_VCR3_FC 0x05035C + +/* ***************************************************************************** */ +/* Data Link Layer Registers */ +/* ***************************************************************************** */ +#define RDR_DLLSTAT 0x050360 + +/* ***************************************************************************** */ +#define RDR_DLLCTRL 0x050364 + +/* ***************************************************************************** */ +#define RDR_REPLAYTO 0x050368 + +/* ***************************************************************************** */ +#define RDR_ACKLATTO 0x05036C + +/* ***************************************************************************** */ +/* MAC Layer Registers */ +/* ***************************************************************************** */ +#define RDR_MACSTAT0 0x050380 + +/* ***************************************************************************** */ +#define RDR_MACSTAT1 0x050384 + +/* ***************************************************************************** */ +#define RDR_MACCTRL0 0x050388 + +/* ***************************************************************************** */ +#define RDR_MACCTRL1 0x05038C + +/* ***************************************************************************** */ +#define RDR_MACCTRL2 0x050390 + +/* ***************************************************************************** */ +#define RDR_MAC_LB_DATA 0x050394 + +/* ***************************************************************************** */ +#define RDR_L0S_EXIT_LAT 0x050398 + +/* ***************************************************************************** */ +/* DMAC */ +/* ***************************************************************************** */ +#define DMA1_PTR1 0x100000 /* DMA Current Ptr : Ch#1 */ + +/* ***************************************************************************** */ +#define DMA2_PTR1 0x100004 /* DMA Current Ptr : Ch#2 */ + +/* ***************************************************************************** */ +#define DMA3_PTR1 0x100008 /* DMA Current Ptr : Ch#3 */ + +/* ***************************************************************************** */ +#define DMA4_PTR1 0x10000C /* DMA Current Ptr : Ch#4 */ + +/* ***************************************************************************** */ +#define DMA5_PTR1 0x100010 /* DMA Current Ptr : Ch#5 */ + +/* ***************************************************************************** */ +#define DMA6_PTR1 0x100014 /* DMA Current Ptr : Ch#6 */ + +/* ***************************************************************************** */ +#define DMA7_PTR1 0x100018 /* DMA Current Ptr : Ch#7 */ + +/* ***************************************************************************** */ +#define DMA8_PTR1 0x10001C /* DMA Current Ptr : Ch#8 */ + +/* ***************************************************************************** */ +#define DMA9_PTR1 0x100020 /* DMA Current Ptr : Ch#9 */ + +/* ***************************************************************************** */ +#define DMA10_PTR1 0x100024 /* DMA Current Ptr : Ch#10 */ + +/* ***************************************************************************** */ +#define DMA11_PTR1 0x100028 /* DMA Current Ptr : Ch#11 */ + +/* ***************************************************************************** */ +#define DMA12_PTR1 0x10002C /* DMA Current Ptr : Ch#12 */ + +/* ***************************************************************************** */ +#define DMA13_PTR1 0x100030 /* DMA Current Ptr : Ch#13 */ + +/* ***************************************************************************** */ +#define DMA14_PTR1 0x100034 /* DMA Current Ptr : Ch#14 */ + +/* ***************************************************************************** */ +#define DMA15_PTR1 0x100038 /* DMA Current Ptr : Ch#15 */ + +/* ***************************************************************************** */ +#define DMA16_PTR1 0x10003C /* DMA Current Ptr : Ch#16 */ + +/* ***************************************************************************** */ +#define DMA17_PTR1 0x100040 /* DMA Current Ptr : Ch#17 */ + +/* ***************************************************************************** */ +#define DMA18_PTR1 0x100044 /* DMA Current Ptr : Ch#18 */ + +/* ***************************************************************************** */ +#define DMA19_PTR1 0x100048 /* DMA Current Ptr : Ch#19 */ + +/* ***************************************************************************** */ +#define DMA20_PTR1 0x10004C /* DMA Current Ptr : Ch#20 */ + +/* ***************************************************************************** */ +#define DMA21_PTR1 0x100050 /* DMA Current Ptr : Ch#21 */ + +/* ***************************************************************************** */ +#define DMA22_PTR1 0x100054 /* DMA Current Ptr : Ch#22 */ + +/* ***************************************************************************** */ +#define DMA23_PTR1 0x100058 /* DMA Current Ptr : Ch#23 */ + +/* ***************************************************************************** */ +#define DMA24_PTR1 0x10005C /* DMA Current Ptr : Ch#24 */ + +/* ***************************************************************************** */ +#define DMA25_PTR1 0x100060 /* DMA Current Ptr : Ch#25 */ + +/* ***************************************************************************** */ +#define DMA26_PTR1 0x100064 /* DMA Current Ptr : Ch#26 */ + +/* ***************************************************************************** */ +#define DMA1_PTR2 0x100080 /* DMA Tab Ptr : Ch#1 */ + +/* ***************************************************************************** */ +#define DMA2_PTR2 0x100084 /* DMA Tab Ptr : Ch#2 */ + +/* ***************************************************************************** */ +#define DMA3_PTR2 0x100088 /* DMA Tab Ptr : Ch#3 */ + +/* ***************************************************************************** */ +#define DMA4_PTR2 0x10008C /* DMA Tab Ptr : Ch#4 */ + +/* ***************************************************************************** */ +#define DMA5_PTR2 0x100090 /* DMA Tab Ptr : Ch#5 */ + +/* ***************************************************************************** */ +#define DMA6_PTR2 0x100094 /* DMA Tab Ptr : Ch#6 */ + +/* ***************************************************************************** */ +#define DMA7_PTR2 0x100098 /* DMA Tab Ptr : Ch#7 */ + +/* ***************************************************************************** */ +#define DMA8_PTR2 0x10009C /* DMA Tab Ptr : Ch#8 */ + +/* ***************************************************************************** */ +#define DMA9_PTR2 0x1000A0 /* DMA Tab Ptr : Ch#9 */ + +/* ***************************************************************************** */ +#define DMA10_PTR2 0x1000A4 /* DMA Tab Ptr : Ch#10 */ + +/* ***************************************************************************** */ +#define DMA11_PTR2 0x1000A8 /* DMA Tab Ptr : Ch#11 */ + +/* ***************************************************************************** */ +#define DMA12_PTR2 0x1000AC /* DMA Tab Ptr : Ch#12 */ + +/* ***************************************************************************** */ +#define DMA13_PTR2 0x1000B0 /* DMA Tab Ptr : Ch#13 */ + +/* ***************************************************************************** */ +#define DMA14_PTR2 0x1000B4 /* DMA Tab Ptr : Ch#14 */ + +/* ***************************************************************************** */ +#define DMA15_PTR2 0x1000B8 /* DMA Tab Ptr : Ch#15 */ + +/* ***************************************************************************** */ +#define DMA16_PTR2 0x1000BC /* DMA Tab Ptr : Ch#16 */ + +/* ***************************************************************************** */ +#define DMA17_PTR2 0x1000C0 /* DMA Tab Ptr : Ch#17 */ + +/* ***************************************************************************** */ +#define DMA18_PTR2 0x1000C4 /* DMA Tab Ptr : Ch#18 */ + +/* ***************************************************************************** */ +#define DMA19_PTR2 0x1000C8 /* DMA Tab Ptr : Ch#19 */ + +/* ***************************************************************************** */ +#define DMA20_PTR2 0x1000CC /* DMA Tab Ptr : Ch#20 */ + +/* ***************************************************************************** */ +#define DMA21_PTR2 0x1000D0 /* DMA Tab Ptr : Ch#21 */ + +/* ***************************************************************************** */ +#define DMA22_PTR2 0x1000D4 /* DMA Tab Ptr : Ch#22 */ + +/* ***************************************************************************** */ +#define DMA23_PTR2 0x1000D8 /* DMA Tab Ptr : Ch#23 */ + +/* ***************************************************************************** */ +#define DMA24_PTR2 0x1000DC /* DMA Tab Ptr : Ch#24 */ + +/* ***************************************************************************** */ +#define DMA25_PTR2 0x1000E0 /* DMA Tab Ptr : Ch#25 */ + +/* ***************************************************************************** */ +#define DMA26_PTR2 0x1000E4 /* DMA Tab Ptr : Ch#26 */ + +/* ***************************************************************************** */ +#define DMA1_CNT1 0x100100 /* DMA BuFFer Size : Ch#1 */ + +/* ***************************************************************************** */ +#define DMA2_CNT1 0x100104 /* DMA BuFFer Size : Ch#2 */ + +/* ***************************************************************************** */ +#define DMA3_CNT1 0x100108 /* DMA BuFFer Size : Ch#3 */ + +/* ***************************************************************************** */ +#define DMA4_CNT1 0x10010C /* DMA BuFFer Size : Ch#4 */ + +/* ***************************************************************************** */ +#define DMA5_CNT1 0x100110 /* DMA BuFFer Size : Ch#5 */ + +/* ***************************************************************************** */ +#define DMA6_CNT1 0x100114 /* DMA BuFFer Size : Ch#6 */ + +/* ***************************************************************************** */ +#define DMA7_CNT1 0x100118 /* DMA BuFFer Size : Ch#7 */ + +/* ***************************************************************************** */ +#define DMA8_CNT1 0x10011C /* DMA BuFFer Size : Ch#8 */ + +/* ***************************************************************************** */ +#define DMA9_CNT1 0x100120 /* DMA BuFFer Size : Ch#9 */ + +/* ***************************************************************************** */ +#define DMA10_CNT1 0x100124 /* DMA BuFFer Size : Ch#10 */ + +/* ***************************************************************************** */ +#define DMA11_CNT1 0x100128 /* DMA BuFFer Size : Ch#11 */ + +/* ***************************************************************************** */ +#define DMA12_CNT1 0x10012C /* DMA BuFFer Size : Ch#12 */ + +/* ***************************************************************************** */ +#define DMA13_CNT1 0x100130 /* DMA BuFFer Size : Ch#13 */ + +/* ***************************************************************************** */ +#define DMA14_CNT1 0x100134 /* DMA BuFFer Size : Ch#14 */ + +/* ***************************************************************************** */ +#define DMA15_CNT1 0x100138 /* DMA BuFFer Size : Ch#15 */ + +/* ***************************************************************************** */ +#define DMA16_CNT1 0x10013C /* DMA BuFFer Size : Ch#16 */ + +/* ***************************************************************************** */ +#define DMA17_CNT1 0x100140 /* DMA BuFFer Size : Ch#17 */ + +/* ***************************************************************************** */ +#define DMA18_CNT1 0x100144 /* DMA BuFFer Size : Ch#18 */ + +/* ***************************************************************************** */ +#define DMA19_CNT1 0x100148 /* DMA BuFFer Size : Ch#19 */ + +/* ***************************************************************************** */ +#define DMA20_CNT1 0x10014C /* DMA BuFFer Size : Ch#20 */ + +/* ***************************************************************************** */ +#define DMA21_CNT1 0x100150 /* DMA BuFFer Size : Ch#21 */ + +/* ***************************************************************************** */ +#define DMA22_CNT1 0x100154 /* DMA BuFFer Size : Ch#22 */ + +/* ***************************************************************************** */ +#define DMA23_CNT1 0x100158 /* DMA BuFFer Size : Ch#23 */ + +/* ***************************************************************************** */ +#define DMA24_CNT1 0x10015C /* DMA BuFFer Size : Ch#24 */ + +/* ***************************************************************************** */ +#define DMA25_CNT1 0x100160 /* DMA BuFFer Size : Ch#25 */ + +/* ***************************************************************************** */ +#define DMA26_CNT1 0x100164 /* DMA BuFFer Size : Ch#26 */ + +/* ***************************************************************************** */ +#define DMA1_CNT2 0x100180 /* DMA Table Size : Ch#1 */ + +/* ***************************************************************************** */ +#define DMA2_CNT2 0x100184 /* DMA Table Size : Ch#2 */ + +/* ***************************************************************************** */ +#define DMA3_CNT2 0x100188 /* DMA Table Size : Ch#3 */ + +/* ***************************************************************************** */ +#define DMA4_CNT2 0x10018C /* DMA Table Size : Ch#4 */ + +/* ***************************************************************************** */ +#define DMA5_CNT2 0x100190 /* DMA Table Size : Ch#5 */ + +/* ***************************************************************************** */ +#define DMA6_CNT2 0x100194 /* DMA Table Size : Ch#6 */ + +/* ***************************************************************************** */ +#define DMA7_CNT2 0x100198 /* DMA Table Size : Ch#7 */ + +/* ***************************************************************************** */ +#define DMA8_CNT2 0x10019C /* DMA Table Size : Ch#8 */ + +/* ***************************************************************************** */ +#define DMA9_CNT2 0x1001A0 /* DMA Table Size : Ch#9 */ + +/* ***************************************************************************** */ +#define DMA10_CNT2 0x1001A4 /* DMA Table Size : Ch#10 */ + +/* ***************************************************************************** */ +#define DMA11_CNT2 0x1001A8 /* DMA Table Size : Ch#11 */ + +/* ***************************************************************************** */ +#define DMA12_CNT2 0x1001AC /* DMA Table Size : Ch#12 */ + +/* ***************************************************************************** */ +#define DMA13_CNT2 0x1001B0 /* DMA Table Size : Ch#13 */ + +/* ***************************************************************************** */ +#define DMA14_CNT2 0x1001B4 /* DMA Table Size : Ch#14 */ + +/* ***************************************************************************** */ +#define DMA15_CNT2 0x1001B8 /* DMA Table Size : Ch#15 */ + +/* ***************************************************************************** */ +#define DMA16_CNT2 0x1001BC /* DMA Table Size : Ch#16 */ + +/* ***************************************************************************** */ +#define DMA17_CNT2 0x1001C0 /* DMA Table Size : Ch#17 */ + +/* ***************************************************************************** */ +#define DMA18_CNT2 0x1001C4 /* DMA Table Size : Ch#18 */ + +/* ***************************************************************************** */ +#define DMA19_CNT2 0x1001C8 /* DMA Table Size : Ch#19 */ + +/* ***************************************************************************** */ +#define DMA20_CNT2 0x1001CC /* DMA Table Size : Ch#20 */ + +/* ***************************************************************************** */ +#define DMA21_CNT2 0x1001D0 /* DMA Table Size : Ch#21 */ + +/* ***************************************************************************** */ +#define DMA22_CNT2 0x1001D4 /* DMA Table Size : Ch#22 */ + +/* ***************************************************************************** */ +#define DMA23_CNT2 0x1001D8 /* DMA Table Size : Ch#23 */ + +/* ***************************************************************************** */ +#define DMA24_CNT2 0x1001DC /* DMA Table Size : Ch#24 */ + +/* ***************************************************************************** */ +#define DMA25_CNT2 0x1001E0 /* DMA Table Size : Ch#25 */ + +/* ***************************************************************************** */ +#define DMA26_CNT2 0x1001E4 /* DMA Table Size : Ch#26 */ + +/* ***************************************************************************** */ + /* ITG */ +/* ***************************************************************************** */ +#define TM_CNT_LDW 0x110000 /* Timer : Counter low */ + +/* ***************************************************************************** */ +#define TM_CNT_UW 0x110004 /* Timer : Counter high word */ + +/* ***************************************************************************** */ +#define TM_LMT_LDW 0x110008 /* Timer : Limit low */ + +/* ***************************************************************************** */ +#define TM_LMT_UW 0x11000C /* Timer : Limit high word */ + +/* ***************************************************************************** */ +#define GP0_IO 0x110010 /* GPIO output enables data I/O */ +#define FLD_GP_OE 0x00FF0000 /* GPIO: GP_OE output enable */ +#define FLD_GP_IN 0x0000FF00 /* GPIO: GP_IN status */ +#define FLD_GP_OUT 0x000000FF /* GPIO: GP_OUT control */ + +/* ***************************************************************************** */ +#define GPIO_ISM 0x110014 /* GPIO interrupt sensitivity mode */ +#define FLD_GP_ISM_SNS 0x00000070 +#define FLD_GP_ISM_POL 0x00000007 + +/* ***************************************************************************** */ +#define SOFT_RESET 0x11001C /* Output system reset reg */ +#define FLD_PECOS_SOFT_RESET 0x00000001 + +/* ***************************************************************************** */ +#define MC416_RWD 0x110020 /* MC416 GPIO[18:3] pin */ +#define MC416_OEN 0x110024 /* Output enable of GPIO[18:3] */ +#define MC416_CTL 0x110028 + +/* ***************************************************************************** */ +#define ALT_PIN_OUT_SEL 0x11002C /* Alternate GPIO output select */ + +#define FLD_ALT_GPIO_OUT_SEL 0xF0000000 +/* 0 Disabled <-- default */ +/* 1 GPIO[0] */ +/* 2 GPIO[10] */ +/* 3 VIP_656_DATA_VAL */ +/* 4 VIP_656_DATA[0] */ +/* 5 VIP_656_CLK */ +/* 6 VIP_656_DATA_EXT[1] */ +/* 7 VIP_656_DATA_EXT[0] */ +/* 8 ATT_IF */ + +#define FLD_AUX_PLL_CLK_ALT_SEL 0x0F000000 +/* 0 AUX_PLL_CLK<-- default */ +/* 1 GPIO[2] */ +/* 2 GPIO[10] */ +/* 3 VIP_656_DATA_VAL */ +/* 4 VIP_656_DATA[0] */ +/* 5 VIP_656_CLK */ +/* 6 VIP_656_DATA_EXT[1] */ +/* 7 VIP_656_DATA_EXT[0] */ + +#define FLD_IR_TX_ALT_SEL 0x00F00000 +/* 0 IR_TX <-- default */ +/* 1 GPIO[1] */ +/* 2 GPIO[10] */ +/* 3 VIP_656_DATA_VAL */ +/* 4 VIP_656_DATA[0] */ +/* 5 VIP_656_CLK */ +/* 6 VIP_656_DATA_EXT[1] */ +/* 7 VIP_656_DATA_EXT[0] */ + +#define FLD_IR_RX_ALT_SEL 0x000F0000 +/* 0 IR_RX <-- default */ +/* 1 GPIO[0] */ +/* 2 GPIO[10] */ +/* 3 VIP_656_DATA_VAL */ +/* 4 VIP_656_DATA[0] */ +/* 5 VIP_656_CLK */ +/* 6 VIP_656_DATA_EXT[1] */ +/* 7 VIP_656_DATA_EXT[0] */ + +#define FLD_GPIO10_ALT_SEL 0x0000F000 +/* 0 GPIO[10] <-- default */ +/* 1 GPIO[0] */ +/* 2 GPIO[10] */ +/* 3 VIP_656_DATA_VAL */ +/* 4 VIP_656_DATA[0] */ +/* 5 VIP_656_CLK */ +/* 6 VIP_656_DATA_EXT[1] */ +/* 7 VIP_656_DATA_EXT[0] */ + +#define FLD_GPIO2_ALT_SEL 0x00000F00 +/* 0 GPIO[2] <-- default */ +/* 1 GPIO[1] */ +/* 2 GPIO[10] */ +/* 3 VIP_656_DATA_VAL */ +/* 4 VIP_656_DATA[0] */ +/* 5 VIP_656_CLK */ +/* 6 VIP_656_DATA_EXT[1] */ +/* 7 VIP_656_DATA_EXT[0] */ + +#define FLD_GPIO1_ALT_SEL 0x000000F0 +/* 0 GPIO[1] <-- default */ +/* 1 GPIO[0] */ +/* 2 GPIO[10] */ +/* 3 VIP_656_DATA_VAL */ +/* 4 VIP_656_DATA[0] */ +/* 5 VIP_656_CLK */ +/* 6 VIP_656_DATA_EXT[1] */ +/* 7 VIP_656_DATA_EXT[0] */ + +#define FLD_GPIO0_ALT_SEL 0x0000000F +/* 0 GPIO[0] <-- default */ +/* 1 GPIO[1] */ +/* 2 GPIO[10] */ +/* 3 VIP_656_DATA_VAL */ +/* 4 VIP_656_DATA[0] */ +/* 5 VIP_656_CLK */ +/* 6 VIP_656_DATA_EXT[1] */ +/* 7 VIP_656_DATA_EXT[0] */ + +#define ALT_PIN_IN_SEL 0x110030 /* Alternate GPIO input select */ + +#define FLD_GPIO10_ALT_IN_SEL 0x0000F000 +/* 0 GPIO[10] <-- default */ +/* 1 IR_RX */ +/* 2 IR_TX */ +/* 3 AUX_PLL_CLK */ +/* 4 IF_ATT_SEL */ +/* 5 GPIO[0] */ +/* 6 GPIO[1] */ +/* 7 GPIO[2] */ + +#define FLD_GPIO2_ALT_IN_SEL 0x00000F00 +/* 0 GPIO[2] <-- default */ +/* 1 IR_RX */ +/* 2 IR_TX */ +/* 3 AUX_PLL_CLK */ +/* 4 IF_ATT_SEL */ + +#define FLD_GPIO1_ALT_IN_SEL 0x000000F0 +/* 0 GPIO[1] <-- default */ +/* 1 IR_RX */ +/* 2 IR_TX */ +/* 3 AUX_PLL_CLK */ +/* 4 IF_ATT_SEL */ + +#define FLD_GPIO0_ALT_IN_SEL 0x0000000F +/* 0 GPIO[0] <-- default */ +/* 1 IR_RX */ +/* 2 IR_TX */ +/* 3 AUX_PLL_CLK */ +/* 4 IF_ATT_SEL */ + +/* ***************************************************************************** */ +#define TEST_BUS_CTL1 0x110040 /* Test bus control register #1 */ + +/* ***************************************************************************** */ +#define TEST_BUS_CTL2 0x110044 /* Test bus control register #2 */ + +/* ***************************************************************************** */ +#define CLK_DELAY 0x110048 /* Clock delay */ +#define FLD_MOE_CLK_DIS 0x80000000 /* Disable MoE clock */ + +/* ***************************************************************************** */ +#define PAD_CTRL 0x110068 /* Pad drive strength control */ + +/* ***************************************************************************** */ +#define MBIST_CTRL 0x110050 /* SRAM memory built-in self test control */ + +/* ***************************************************************************** */ +#define MBIST_STAT 0x110054 /* SRAM memory built-in self test status */ + +/* ***************************************************************************** */ +/* PLL registers */ +/* ***************************************************************************** */ +#define PLL_A_INT_FRAC 0x110088 +#define PLL_A_POST_STAT_BIST 0x11008C +#define PLL_B_INT_FRAC 0x110090 +#define PLL_B_POST_STAT_BIST 0x110094 +#define PLL_C_INT_FRAC 0x110098 +#define PLL_C_POST_STAT_BIST 0x11009C +#define PLL_D_INT_FRAC 0x1100A0 +#define PLL_D_POST_STAT_BIST 0x1100A4 + +#define CLK_RST 0x11002C +#define FLD_VID_I_CLK_NOE 0x00001000 +#define FLD_VID_J_CLK_NOE 0x00002000 +#define FLD_USE_ALT_PLL_REF 0x00004000 + +#define VID_CH_MODE_SEL 0x110078 +#define VID_CH_CLK_SEL 0x11007C + +/* ***************************************************************************** */ +#define VBI_A_DMA 0x130008 /* VBI A DMA data port */ + +/* ***************************************************************************** */ +#define VID_A_VIP_CTL 0x130080 /* Video A VIP format control */ +#define FLD_VIP_MODE 0x00000001 + +/* ***************************************************************************** */ +#define VID_A_PIXEL_FRMT 0x130084 /* Video A pixel format */ +#define FLD_VID_A_GAMMA_DIS 0x00000008 +#define FLD_VID_A_FORMAT 0x00000007 +#define FLD_VID_A_GAMMA_FACTOR 0x00000010 + +/* ***************************************************************************** */ +#define VID_A_VBI_CTL 0x130088 /* Video A VBI miscellaneous control */ +#define FLD_VID_A_VIP_EXT 0x00000003 + +/* ***************************************************************************** */ +#define VID_B_DMA 0x130100 /* Video B DMA data port */ + +/* ***************************************************************************** */ +#define VBI_B_DMA 0x130108 /* VBI B DMA data port */ + +/* ***************************************************************************** */ +#define VID_B_SRC_SEL 0x130144 /* Video B source select */ +#define FLD_VID_B_SRC_SEL 0x00000000 + +/* ***************************************************************************** */ +#define VID_B_LNGTH 0x130150 /* Video B line length */ +#define FLD_VID_B_LN_LNGTH 0x00000FFF + +/* ***************************************************************************** */ +#define VID_B_VIP_CTL 0x130180 /* Video B VIP format control */ + +/* ***************************************************************************** */ +#define VID_B_PIXEL_FRMT 0x130184 /* Video B pixel format */ +#define FLD_VID_B_GAMMA_DIS 0x00000008 +#define FLD_VID_B_FORMAT 0x00000007 +#define FLD_VID_B_GAMMA_FACTOR 0x00000010 + +/* ***************************************************************************** */ +#define VID_C_DMA 0x130200 /* Video C DMA data port */ + +/* ***************************************************************************** */ +#define VID_C_LNGTH 0x130250 /* Video C line length */ +#define FLD_VID_C_LN_LNGTH 0x00000FFF + +/* ***************************************************************************** */ +/* Video Destination Channels */ +/* ***************************************************************************** */ + +#define VID_DST_A_GPCNT 0x130020 /* Video A general purpose counter */ +#define VID_DST_B_GPCNT 0x130120 /* Video B general purpose counter */ +#define VID_DST_C_GPCNT 0x130220 /* Video C general purpose counter */ +#define VID_DST_D_GPCNT 0x130320 /* Video D general purpose counter */ +#define VID_DST_E_GPCNT 0x130420 /* Video E general purpose counter */ +#define VID_DST_F_GPCNT 0x130520 /* Video F general purpose counter */ +#define VID_DST_G_GPCNT 0x130620 /* Video G general purpose counter */ +#define VID_DST_H_GPCNT 0x130720 /* Video H general purpose counter */ + +/* ***************************************************************************** */ + +#define VID_DST_A_GPCNT_CTL 0x130030 /* Video A general purpose control */ +#define VID_DST_B_GPCNT_CTL 0x130130 /* Video B general purpose control */ +#define VID_DST_C_GPCNT_CTL 0x130230 /* Video C general purpose control */ +#define VID_DST_D_GPCNT_CTL 0x130330 /* Video D general purpose control */ +#define VID_DST_E_GPCNT_CTL 0x130430 /* Video E general purpose control */ +#define VID_DST_F_GPCNT_CTL 0x130530 /* Video F general purpose control */ +#define VID_DST_G_GPCNT_CTL 0x130630 /* Video G general purpose control */ +#define VID_DST_H_GPCNT_CTL 0x130730 /* Video H general purpose control */ + +/* ***************************************************************************** */ + +#define VID_DST_A_DMA_CTL 0x130040 /* Video A DMA control */ +#define VID_DST_B_DMA_CTL 0x130140 /* Video B DMA control */ +#define VID_DST_C_DMA_CTL 0x130240 /* Video C DMA control */ +#define VID_DST_D_DMA_CTL 0x130340 /* Video D DMA control */ +#define VID_DST_E_DMA_CTL 0x130440 /* Video E DMA control */ +#define VID_DST_F_DMA_CTL 0x130540 /* Video F DMA control */ +#define VID_DST_G_DMA_CTL 0x130640 /* Video G DMA control */ +#define VID_DST_H_DMA_CTL 0x130740 /* Video H DMA control */ + +#define FLD_VID_RISC_EN 0x00000010 +#define FLD_VID_FIFO_EN 0x00000001 + +/* ***************************************************************************** */ + +#define VID_DST_A_VIP_CTL 0x130080 /* Video A VIP control */ +#define VID_DST_B_VIP_CTL 0x130180 /* Video B VIP control */ +#define VID_DST_C_VIP_CTL 0x130280 /* Video C VIP control */ +#define VID_DST_D_VIP_CTL 0x130380 /* Video D VIP control */ +#define VID_DST_E_VIP_CTL 0x130480 /* Video E VIP control */ +#define VID_DST_F_VIP_CTL 0x130580 /* Video F VIP control */ +#define VID_DST_G_VIP_CTL 0x130680 /* Video G VIP control */ +#define VID_DST_H_VIP_CTL 0x130780 /* Video H VIP control */ + +/* ***************************************************************************** */ + +#define VID_DST_A_PIX_FRMT 0x130084 /* Video A Pixel format */ +#define VID_DST_B_PIX_FRMT 0x130184 /* Video B Pixel format */ +#define VID_DST_C_PIX_FRMT 0x130284 /* Video C Pixel format */ +#define VID_DST_D_PIX_FRMT 0x130384 /* Video D Pixel format */ +#define VID_DST_E_PIX_FRMT 0x130484 /* Video E Pixel format */ +#define VID_DST_F_PIX_FRMT 0x130584 /* Video F Pixel format */ +#define VID_DST_G_PIX_FRMT 0x130684 /* Video G Pixel format */ +#define VID_DST_H_PIX_FRMT 0x130784 /* Video H Pixel format */ + +/* ***************************************************************************** */ +/* Video Source Channels */ +/* ***************************************************************************** */ + +#define VID_SRC_A_GPCNT_CTL 0x130804 /* Video A general purpose control */ +#define VID_SRC_B_GPCNT_CTL 0x130904 /* Video B general purpose control */ +#define VID_SRC_C_GPCNT_CTL 0x130A04 /* Video C general purpose control */ +#define VID_SRC_D_GPCNT_CTL 0x130B04 /* Video D general purpose control */ +#define VID_SRC_E_GPCNT_CTL 0x130C04 /* Video E general purpose control */ +#define VID_SRC_F_GPCNT_CTL 0x130D04 /* Video F general purpose control */ +#define VID_SRC_I_GPCNT_CTL 0x130E04 /* Video I general purpose control */ +#define VID_SRC_J_GPCNT_CTL 0x130F04 /* Video J general purpose control */ + +/* ***************************************************************************** */ + +#define VID_SRC_A_GPCNT 0x130808 /* Video A general purpose counter */ +#define VID_SRC_B_GPCNT 0x130908 /* Video B general purpose counter */ +#define VID_SRC_C_GPCNT 0x130A08 /* Video C general purpose counter */ +#define VID_SRC_D_GPCNT 0x130B08 /* Video D general purpose counter */ +#define VID_SRC_E_GPCNT 0x130C08 /* Video E general purpose counter */ +#define VID_SRC_F_GPCNT 0x130D08 /* Video F general purpose counter */ +#define VID_SRC_I_GPCNT 0x130E08 /* Video I general purpose counter */ +#define VID_SRC_J_GPCNT 0x130F08 /* Video J general purpose counter */ + +/* ***************************************************************************** */ + +#define VID_SRC_A_DMA_CTL 0x13080C /* Video A DMA control */ +#define VID_SRC_B_DMA_CTL 0x13090C /* Video B DMA control */ +#define VID_SRC_C_DMA_CTL 0x130A0C /* Video C DMA control */ +#define VID_SRC_D_DMA_CTL 0x130B0C /* Video D DMA control */ +#define VID_SRC_E_DMA_CTL 0x130C0C /* Video E DMA control */ +#define VID_SRC_F_DMA_CTL 0x130D0C /* Video F DMA control */ +#define VID_SRC_I_DMA_CTL 0x130E0C /* Video I DMA control */ +#define VID_SRC_J_DMA_CTL 0x130F0C /* Video J DMA control */ + +#define FLD_APB_RISC_EN 0x00000010 +#define FLD_APB_FIFO_EN 0x00000001 + +/* ***************************************************************************** */ + +#define VID_SRC_A_FMT_CTL 0x130810 /* Video A format control */ +#define VID_SRC_B_FMT_CTL 0x130910 /* Video B format control */ +#define VID_SRC_C_FMT_CTL 0x130A10 /* Video C format control */ +#define VID_SRC_D_FMT_CTL 0x130B10 /* Video D format control */ +#define VID_SRC_E_FMT_CTL 0x130C10 /* Video E format control */ +#define VID_SRC_F_FMT_CTL 0x130D10 /* Video F format control */ +#define VID_SRC_I_FMT_CTL 0x130E10 /* Video I format control */ +#define VID_SRC_J_FMT_CTL 0x130F10 /* Video J format control */ + +/* ***************************************************************************** */ + +#define VID_SRC_A_ACTIVE_CTL1 0x130814 /* Video A active control 1 */ +#define VID_SRC_B_ACTIVE_CTL1 0x130914 /* Video B active control 1 */ +#define VID_SRC_C_ACTIVE_CTL1 0x130A14 /* Video C active control 1 */ +#define VID_SRC_D_ACTIVE_CTL1 0x130B14 /* Video D active control 1 */ +#define VID_SRC_E_ACTIVE_CTL1 0x130C14 /* Video E active control 1 */ +#define VID_SRC_F_ACTIVE_CTL1 0x130D14 /* Video F active control 1 */ +#define VID_SRC_I_ACTIVE_CTL1 0x130E14 /* Video I active control 1 */ +#define VID_SRC_J_ACTIVE_CTL1 0x130F14 /* Video J active control 1 */ + +/* ***************************************************************************** */ + +#define VID_SRC_A_ACTIVE_CTL2 0x130818 /* Video A active control 2 */ +#define VID_SRC_B_ACTIVE_CTL2 0x130918 /* Video B active control 2 */ +#define VID_SRC_C_ACTIVE_CTL2 0x130A18 /* Video C active control 2 */ +#define VID_SRC_D_ACTIVE_CTL2 0x130B18 /* Video D active control 2 */ +#define VID_SRC_E_ACTIVE_CTL2 0x130C18 /* Video E active control 2 */ +#define VID_SRC_F_ACTIVE_CTL2 0x130D18 /* Video F active control 2 */ +#define VID_SRC_I_ACTIVE_CTL2 0x130E18 /* Video I active control 2 */ +#define VID_SRC_J_ACTIVE_CTL2 0x130F18 /* Video J active control 2 */ + +/* ***************************************************************************** */ + +#define VID_SRC_A_CDT_SZ 0x13081C /* Video A CDT size */ +#define VID_SRC_B_CDT_SZ 0x13091C /* Video B CDT size */ +#define VID_SRC_C_CDT_SZ 0x130A1C /* Video C CDT size */ +#define VID_SRC_D_CDT_SZ 0x130B1C /* Video D CDT size */ +#define VID_SRC_E_CDT_SZ 0x130C1C /* Video E CDT size */ +#define VID_SRC_F_CDT_SZ 0x130D1C /* Video F CDT size */ +#define VID_SRC_I_CDT_SZ 0x130E1C /* Video I CDT size */ +#define VID_SRC_J_CDT_SZ 0x130F1C /* Video J CDT size */ + +/* ***************************************************************************** */ +/* Audio I/F */ +/* ***************************************************************************** */ +#define AUD_DST_A_DMA 0x140000 /* Audio Int A DMA data port */ +#define AUD_SRC_A_DMA 0x140008 /* Audio Int A DMA data port */ + +#define AUD_A_GPCNT 0x140010 /* Audio Int A gp counter */ +#define FLD_AUD_A_GP_CNT 0x0000FFFF + +#define AUD_A_GPCNT_CTL 0x140014 /* Audio Int A gp control */ + +#define AUD_A_LNGTH 0x140018 /* Audio Int A line length */ + +#define AUD_A_CFG 0x14001C /* Audio Int A configuration */ + +/* ***************************************************************************** */ +#define AUD_DST_B_DMA 0x140100 /* Audio Int B DMA data port */ +#define AUD_SRC_B_DMA 0x140108 /* Audio Int B DMA data port */ + +#define AUD_B_GPCNT 0x140110 /* Audio Int B gp counter */ +#define FLD_AUD_B_GP_CNT 0x0000FFFF + +#define AUD_B_GPCNT_CTL 0x140114 /* Audio Int B gp control */ + +#define AUD_B_LNGTH 0x140118 /* Audio Int B line length */ + +#define AUD_B_CFG 0x14011C /* Audio Int B configuration */ + +/* ***************************************************************************** */ +#define AUD_DST_C_DMA 0x140200 /* Audio Int C DMA data port */ +#define AUD_SRC_C_DMA 0x140208 /* Audio Int C DMA data port */ + +#define AUD_C_GPCNT 0x140210 /* Audio Int C gp counter */ +#define FLD_AUD_C_GP_CNT 0x0000FFFF + +#define AUD_C_GPCNT_CTL 0x140214 /* Audio Int C gp control */ + +#define AUD_C_LNGTH 0x140218 /* Audio Int C line length */ + +#define AUD_C_CFG 0x14021C /* Audio Int C configuration */ + +/* ***************************************************************************** */ +#define AUD_DST_D_DMA 0x140300 /* Audio Int D DMA data port */ +#define AUD_SRC_D_DMA 0x140308 /* Audio Int D DMA data port */ + +#define AUD_D_GPCNT 0x140310 /* Audio Int D gp counter */ +#define FLD_AUD_D_GP_CNT 0x0000FFFF + +#define AUD_D_GPCNT_CTL 0x140314 /* Audio Int D gp control */ + +#define AUD_D_LNGTH 0x140318 /* Audio Int D line length */ + +#define AUD_D_CFG 0x14031C /* Audio Int D configuration */ + +/* ***************************************************************************** */ +#define AUD_SRC_E_DMA 0x140400 /* Audio Int E DMA data port */ + +#define AUD_E_GPCNT 0x140410 /* Audio Int E gp counter */ +#define FLD_AUD_E_GP_CNT 0x0000FFFF + +#define AUD_E_GPCNT_CTL 0x140414 /* Audio Int E gp control */ + +#define AUD_E_CFG 0x14041C /* Audio Int E configuration */ + +/* ***************************************************************************** */ + +#define FLD_AUD_DST_LN_LNGTH 0x00000FFF + +#define FLD_AUD_DST_PK_MODE 0x00004000 + +#define FLD_AUD_CLK_ENABLE 0x00000200 + +#define FLD_AUD_MASTER_MODE 0x00000002 + +#define FLD_AUD_SONY_MODE 0x00000001 + +#define FLD_AUD_CLK_SELECT_PLL_D 0x00001800 + +#define FLD_AUD_DST_ENABLE 0x00020000 + +#define FLD_AUD_SRC_ENABLE 0x00010000 + +/* ***************************************************************************** */ +#define AUD_INT_DMA_CTL 0x140500 /* Audio Int DMA control */ + +#define FLD_AUD_SRC_E_RISC_EN 0x00008000 +#define FLD_AUD_SRC_C_RISC_EN 0x00004000 +#define FLD_AUD_SRC_B_RISC_EN 0x00002000 +#define FLD_AUD_SRC_A_RISC_EN 0x00001000 + +#define FLD_AUD_DST_D_RISC_EN 0x00000800 +#define FLD_AUD_DST_C_RISC_EN 0x00000400 +#define FLD_AUD_DST_B_RISC_EN 0x00000200 +#define FLD_AUD_DST_A_RISC_EN 0x00000100 + +#define FLD_AUD_SRC_E_FIFO_EN 0x00000080 +#define FLD_AUD_SRC_C_FIFO_EN 0x00000040 +#define FLD_AUD_SRC_B_FIFO_EN 0x00000020 +#define FLD_AUD_SRC_A_FIFO_EN 0x00000010 + +#define FLD_AUD_DST_D_FIFO_EN 0x00000008 +#define FLD_AUD_DST_C_FIFO_EN 0x00000004 +#define FLD_AUD_DST_B_FIFO_EN 0x00000002 +#define FLD_AUD_DST_A_FIFO_EN 0x00000001 + +/* ***************************************************************************** */ +/* */ +/* Mobilygen Interface Registers */ +/* */ +/* ***************************************************************************** */ +/* Mobilygen Interface A */ +/* ***************************************************************************** */ +#define MB_IF_A_DMA 0x150000 /* MBIF A DMA data port */ +#define MB_IF_A_GPCN 0x150008 /* MBIF A GP counter */ +#define MB_IF_A_GPCN_CTRL 0x15000C +#define MB_IF_A_DMA_CTRL 0x150010 +#define MB_IF_A_LENGTH 0x150014 +#define MB_IF_A_HDMA_XFER_SZ 0x150018 +#define MB_IF_A_HCMD 0x15001C +#define MB_IF_A_HCONFIG 0x150020 +#define MB_IF_A_DATA_STRUCT_0 0x150024 +#define MB_IF_A_DATA_STRUCT_1 0x150028 +#define MB_IF_A_DATA_STRUCT_2 0x15002C +#define MB_IF_A_DATA_STRUCT_3 0x150030 +#define MB_IF_A_DATA_STRUCT_4 0x150034 +#define MB_IF_A_DATA_STRUCT_5 0x150038 +#define MB_IF_A_DATA_STRUCT_6 0x15003C +#define MB_IF_A_DATA_STRUCT_7 0x150040 +#define MB_IF_A_DATA_STRUCT_8 0x150044 +#define MB_IF_A_DATA_STRUCT_9 0x150048 +#define MB_IF_A_DATA_STRUCT_A 0x15004C +#define MB_IF_A_DATA_STRUCT_B 0x150050 +#define MB_IF_A_DATA_STRUCT_C 0x150054 +#define MB_IF_A_DATA_STRUCT_D 0x150058 +#define MB_IF_A_DATA_STRUCT_E 0x15005C +#define MB_IF_A_DATA_STRUCT_F 0x150060 +/* ***************************************************************************** */ +/* Mobilygen Interface B */ +/* ***************************************************************************** */ +#define MB_IF_B_DMA 0x160000 /* MBIF A DMA data port */ +#define MB_IF_B_GPCN 0x160008 /* MBIF A GP counter */ +#define MB_IF_B_GPCN_CTRL 0x16000C +#define MB_IF_B_DMA_CTRL 0x160010 +#define MB_IF_B_LENGTH 0x160014 +#define MB_IF_B_HDMA_XFER_SZ 0x160018 +#define MB_IF_B_HCMD 0x16001C +#define MB_IF_B_HCONFIG 0x160020 +#define MB_IF_B_DATA_STRUCT_0 0x160024 +#define MB_IF_B_DATA_STRUCT_1 0x160028 +#define MB_IF_B_DATA_STRUCT_2 0x16002C +#define MB_IF_B_DATA_STRUCT_3 0x160030 +#define MB_IF_B_DATA_STRUCT_4 0x160034 +#define MB_IF_B_DATA_STRUCT_5 0x160038 +#define MB_IF_B_DATA_STRUCT_6 0x16003C +#define MB_IF_B_DATA_STRUCT_7 0x160040 +#define MB_IF_B_DATA_STRUCT_8 0x160044 +#define MB_IF_B_DATA_STRUCT_9 0x160048 +#define MB_IF_B_DATA_STRUCT_A 0x16004C +#define MB_IF_B_DATA_STRUCT_B 0x160050 +#define MB_IF_B_DATA_STRUCT_C 0x160054 +#define MB_IF_B_DATA_STRUCT_D 0x160058 +#define MB_IF_B_DATA_STRUCT_E 0x16005C +#define MB_IF_B_DATA_STRUCT_F 0x160060 + +/* MB_DMA_CTRL */ +#define FLD_MB_IF_RISC_EN 0x00000010 +#define FLD_MB_IF_FIFO_EN 0x00000001 + +/* MB_LENGTH */ +#define FLD_MB_IF_LN_LNGTH 0x00000FFF + +/* MB_HCMD register */ +#define FLD_MB_HCMD_H_GO 0x80000000 +#define FLD_MB_HCMD_H_BUSY 0x40000000 +#define FLD_MB_HCMD_H_DMA_HOLD 0x10000000 +#define FLD_MB_HCMD_H_DMA_BUSY 0x08000000 +#define FLD_MB_HCMD_H_DMA_TYPE 0x04000000 +#define FLD_MB_HCMD_H_DMA_XACT 0x02000000 +#define FLD_MB_HCMD_H_RW_N 0x01000000 +#define FLD_MB_HCMD_H_ADDR 0x00FF0000 +#define FLD_MB_HCMD_H_DATA 0x0000FFFF + +/* ***************************************************************************** */ +/* I2C #1 */ +/* ***************************************************************************** */ +#define I2C1_ADDR 0x180000 /* I2C #1 address */ +#define FLD_I2C_DADDR 0xfe000000 /* RW [31:25] I2C Device Address */ + /* RO [24] reserved */ +/* ***************************************************************************** */ +#define FLD_I2C_SADDR 0x00FFFFFF /* RW [23:0] I2C Sub-address */ + +/* ***************************************************************************** */ +#define I2C1_WDATA 0x180004 /* I2C #1 write data */ +#define FLD_I2C_WDATA 0xFFFFFFFF /* RW [31:0] */ + +/* ***************************************************************************** */ +#define I2C1_CTRL 0x180008 /* I2C #1 control */ +#define FLD_I2C_PERIOD 0xFF000000 /* RW [31:24] */ +#define FLD_I2C_SCL_IN 0x00200000 /* RW [21] */ +#define FLD_I2C_SDA_IN 0x00100000 /* RW [20] */ + /* RO [19:18] reserved */ +#define FLD_I2C_SCL_OUT 0x00020000 /* RW [17] */ +#define FLD_I2C_SDA_OUT 0x00010000 /* RW [16] */ + /* RO [15] reserved */ +#define FLD_I2C_DATA_LEN 0x00007000 /* RW [14:12] */ +#define FLD_I2C_SADDR_INC 0x00000800 /* RW [11] */ + /* RO [10:9] reserved */ +#define FLD_I2C_SADDR_LEN 0x00000300 /* RW [9:8] */ + /* RO [7:6] reserved */ +#define FLD_I2C_SOFT 0x00000020 /* RW [5] */ +#define FLD_I2C_NOSTOP 0x00000010 /* RW [4] */ +#define FLD_I2C_EXTEND 0x00000008 /* RW [3] */ +#define FLD_I2C_SYNC 0x00000004 /* RW [2] */ +#define FLD_I2C_READ_SA 0x00000002 /* RW [1] */ +#define FLD_I2C_READ_WRN 0x00000001 /* RW [0] */ + +/* ***************************************************************************** */ +#define I2C1_RDATA 0x18000C /* I2C #1 read data */ +#define FLD_I2C_RDATA 0xFFFFFFFF /* RO [31:0] */ + +/* ***************************************************************************** */ +#define I2C1_STAT 0x180010 /* I2C #1 status */ +#define FLD_I2C_XFER_IN_PROG 0x00000002 /* RO [1] */ +#define FLD_I2C_RACK 0x00000001 /* RO [0] */ + +/* ***************************************************************************** */ +/* I2C #2 */ +/* ***************************************************************************** */ +#define I2C2_ADDR 0x190000 /* I2C #2 address */ + +/* ***************************************************************************** */ +#define I2C2_WDATA 0x190004 /* I2C #2 write data */ + +/* ***************************************************************************** */ +#define I2C2_CTRL 0x190008 /* I2C #2 control */ + +/* ***************************************************************************** */ +#define I2C2_RDATA 0x19000C /* I2C #2 read data */ + +/* ***************************************************************************** */ +#define I2C2_STAT 0x190010 /* I2C #2 status */ + +/* ***************************************************************************** */ +/* I2C #3 */ +/* ***************************************************************************** */ +#define I2C3_ADDR 0x1A0000 /* I2C #3 address */ + +/* ***************************************************************************** */ +#define I2C3_WDATA 0x1A0004 /* I2C #3 write data */ + +/* ***************************************************************************** */ +#define I2C3_CTRL 0x1A0008 /* I2C #3 control */ + +/* ***************************************************************************** */ +#define I2C3_RDATA 0x1A000C /* I2C #3 read data */ + +/* ***************************************************************************** */ +#define I2C3_STAT 0x1A0010 /* I2C #3 status */ + +/* ***************************************************************************** */ +/* UART */ +/* ***************************************************************************** */ +#define UART_CTL 0x1B0000 /* UART Control Register */ +#define FLD_LOOP_BACK_EN (1 << 7) /* RW field - default 0 */ +#define FLD_RX_TRG_SZ (3 << 2) /* RW field - default 0 */ +#define FLD_RX_EN (1 << 1) /* RW field - default 0 */ +#define FLD_TX_EN (1 << 0) /* RW field - default 0 */ + +/* ***************************************************************************** */ +#define UART_BRD 0x1B0004 /* UART Baud Rate Divisor */ +#define FLD_BRD 0x0000FFFF /* RW field - default 0x197 */ + +/* ***************************************************************************** */ +#define UART_DBUF 0x1B0008 /* UART Tx/Rx Data BuFFer */ +#define FLD_DB 0xFFFFFFFF /* RW field - default 0 */ + +/* ***************************************************************************** */ +#define UART_ISR 0x1B000C /* UART Interrupt Status */ +#define FLD_RXD_TIMEOUT_EN (1 << 7) /* RW field - default 0 */ +#define FLD_FRM_ERR_EN (1 << 6) /* RW field - default 0 */ +#define FLD_RXD_RDY_EN (1 << 5) /* RW field - default 0 */ +#define FLD_TXD_EMPTY_EN (1 << 4) /* RW field - default 0 */ +#define FLD_RXD_OVERFLOW (1 << 3) /* RW field - default 0 */ +#define FLD_FRM_ERR (1 << 2) /* RW field - default 0 */ +#define FLD_RXD_RDY (1 << 1) /* RW field - default 0 */ +#define FLD_TXD_EMPTY (1 << 0) /* RW field - default 0 */ + +/* ***************************************************************************** */ +#define UART_CNT 0x1B0010 /* UART Tx/Rx FIFO Byte Count */ +#define FLD_TXD_CNT (0x1F << 8) /* RW field - default 0 */ +#define FLD_RXD_CNT (0x1F << 0) /* RW field - default 0 */ + +/* ***************************************************************************** */ +/* Motion Detection */ +#define MD_CH0_GRID_BLOCK_YCNT 0x170014 +#define MD_CH1_GRID_BLOCK_YCNT 0x170094 +#define MD_CH2_GRID_BLOCK_YCNT 0x170114 +#define MD_CH3_GRID_BLOCK_YCNT 0x170194 +#define MD_CH4_GRID_BLOCK_YCNT 0x170214 +#define MD_CH5_GRID_BLOCK_YCNT 0x170294 +#define MD_CH6_GRID_BLOCK_YCNT 0x170314 +#define MD_CH7_GRID_BLOCK_YCNT 0x170394 + +#define PIXEL_FRMT_422 4 +#define PIXEL_FRMT_411 5 +#define PIXEL_FRMT_Y8 6 + +#define PIXEL_ENGINE_VIP1 0 +#define PIXEL_ENGINE_VIP2 1 + +#endif /* Athena_REGISTERS */ diff --git a/drivers/media/video/cx25821/cx25821-sram.h b/drivers/media/video/cx25821/cx25821-sram.h new file mode 100644 index 00000000000..5f05d153bc4 --- /dev/null +++ b/drivers/media/video/cx25821/cx25821-sram.h @@ -0,0 +1,261 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __ATHENA_SRAM_H__ +#define __ATHENA_SRAM_H__ + +/* #define RX_SRAM_START_SIZE = 0; // Start of reserved SRAM */ +#define VID_CMDS_SIZE 80 /* Video CMDS size in bytes */ +#define AUDIO_CMDS_SIZE 80 /* AUDIO CMDS size in bytes */ +#define MBIF_CMDS_SIZE 80 /* MBIF CMDS size in bytes */ + +/* #define RX_SRAM_POOL_START_SIZE = 0; // Start of useable RX SRAM for buffers */ +#define VID_IQ_SIZE 64 /* VID instruction queue size in bytes */ +#define MBIF_IQ_SIZE 64 +#define AUDIO_IQ_SIZE 64 /* AUD instruction queue size in bytes */ + +#define VID_CDT_SIZE 64 /* VID cluster descriptor table size in bytes */ +#define MBIF_CDT_SIZE 64 /* MBIF/HBI cluster descriptor table size in bytes */ +#define AUDIO_CDT_SIZE 48 /* AUD cluster descriptor table size in bytes */ + +/* #define RX_SRAM_POOL_FREE_SIZE = 16; // Start of available RX SRAM */ +/* #define RX_SRAM_END_SIZE = 0; // End of RX SRAM */ + +/* #define TX_SRAM_POOL_START_SIZE = 0; // Start of transmit pool SRAM */ +/* #define MSI_DATA_SIZE = 64; // Reserved (MSI Data, RISC working stora */ + +#define VID_CLUSTER_SIZE 1440 /* VID cluster data line */ +#define AUDIO_CLUSTER_SIZE 128 /* AUDIO cluster data line */ +#define MBIF_CLUSTER_SIZE 1440 /* MBIF/HBI cluster data line */ + +/* #define TX_SRAM_POOL_FREE_SIZE = 704; // Start of available TX SRAM */ +/* #define TX_SRAM_END_SIZE = 0; // End of TX SRAM */ + +/* Receive SRAM */ +#define RX_SRAM_START 0x10000 +#define VID_A_DOWN_CMDS 0x10000 +#define VID_B_DOWN_CMDS 0x10050 +#define VID_C_DOWN_CMDS 0x100A0 +#define VID_D_DOWN_CMDS 0x100F0 +#define VID_E_DOWN_CMDS 0x10140 +#define VID_F_DOWN_CMDS 0x10190 +#define VID_G_DOWN_CMDS 0x101E0 +#define VID_H_DOWN_CMDS 0x10230 +#define VID_A_UP_CMDS 0x10280 +#define VID_B_UP_CMDS 0x102D0 +#define VID_C_UP_CMDS 0x10320 +#define VID_D_UP_CMDS 0x10370 +#define VID_E_UP_CMDS 0x103C0 +#define VID_F_UP_CMDS 0x10410 +#define VID_I_UP_CMDS 0x10460 +#define VID_J_UP_CMDS 0x104B0 +#define AUD_A_DOWN_CMDS 0x10500 +#define AUD_B_DOWN_CMDS 0x10550 +#define AUD_C_DOWN_CMDS 0x105A0 +#define AUD_D_DOWN_CMDS 0x105F0 +#define AUD_A_UP_CMDS 0x10640 +#define AUD_B_UP_CMDS 0x10690 +#define AUD_C_UP_CMDS 0x106E0 +#define AUD_E_UP_CMDS 0x10730 +#define MBIF_A_DOWN_CMDS 0x10780 +#define MBIF_B_DOWN_CMDS 0x107D0 +#define DMA_SCRATCH_PAD 0x10820 /* Scratch pad area from 0x10820 to 0x10B40 */ + +/* #define RX_SRAM_POOL_START = 0x105B0; */ + +#define VID_A_IQ 0x11000 +#define VID_B_IQ 0x11040 +#define VID_C_IQ 0x11080 +#define VID_D_IQ 0x110C0 +#define VID_E_IQ 0x11100 +#define VID_F_IQ 0x11140 +#define VID_G_IQ 0x11180 +#define VID_H_IQ 0x111C0 +#define VID_I_IQ 0x11200 +#define VID_J_IQ 0x11240 +#define AUD_A_IQ 0x11280 +#define AUD_B_IQ 0x112C0 +#define AUD_C_IQ 0x11300 +#define AUD_D_IQ 0x11340 +#define AUD_E_IQ 0x11380 +#define MBIF_A_IQ 0x11000 +#define MBIF_B_IQ 0x110C0 + +#define VID_A_CDT 0x10C00 +#define VID_B_CDT 0x10C40 +#define VID_C_CDT 0x10C80 +#define VID_D_CDT 0x10CC0 +#define VID_E_CDT 0x10D00 +#define VID_F_CDT 0x10D40 +#define VID_G_CDT 0x10D80 +#define VID_H_CDT 0x10DC0 +#define VID_I_CDT 0x10E00 +#define VID_J_CDT 0x10E40 +#define AUD_A_CDT 0x10E80 +#define AUD_B_CDT 0x10EB0 +#define AUD_C_CDT 0x10EE0 +#define AUD_D_CDT 0x10F10 +#define AUD_E_CDT 0x10F40 +#define MBIF_A_CDT 0x10C00 +#define MBIF_B_CDT 0x10CC0 + +/* Cluster Buffer for RX */ +#define VID_A_UP_CLUSTER_1 0x11400 +#define VID_A_UP_CLUSTER_2 0x119A0 +#define VID_A_UP_CLUSTER_3 0x11F40 +#define VID_A_UP_CLUSTER_4 0x124E0 + +#define VID_B_UP_CLUSTER_1 0x12A80 +#define VID_B_UP_CLUSTER_2 0x13020 +#define VID_B_UP_CLUSTER_3 0x135C0 +#define VID_B_UP_CLUSTER_4 0x13B60 + +#define VID_C_UP_CLUSTER_1 0x14100 +#define VID_C_UP_CLUSTER_2 0x146A0 +#define VID_C_UP_CLUSTER_3 0x14C40 +#define VID_C_UP_CLUSTER_4 0x151E0 + +#define VID_D_UP_CLUSTER_1 0x15780 +#define VID_D_UP_CLUSTER_2 0x15D20 +#define VID_D_UP_CLUSTER_3 0x162C0 +#define VID_D_UP_CLUSTER_4 0x16860 + +#define VID_E_UP_CLUSTER_1 0x16E00 +#define VID_E_UP_CLUSTER_2 0x173A0 +#define VID_E_UP_CLUSTER_3 0x17940 +#define VID_E_UP_CLUSTER_4 0x17EE0 + +#define VID_F_UP_CLUSTER_1 0x18480 +#define VID_F_UP_CLUSTER_2 0x18A20 +#define VID_F_UP_CLUSTER_3 0x18FC0 +#define VID_F_UP_CLUSTER_4 0x19560 + +#define VID_I_UP_CLUSTER_1 0x19B00 +#define VID_I_UP_CLUSTER_2 0x1A0A0 +#define VID_I_UP_CLUSTER_3 0x1A640 +#define VID_I_UP_CLUSTER_4 0x1ABE0 + +#define VID_J_UP_CLUSTER_1 0x1B180 +#define VID_J_UP_CLUSTER_2 0x1B720 +#define VID_J_UP_CLUSTER_3 0x1BCC0 +#define VID_J_UP_CLUSTER_4 0x1C260 + +#define AUD_A_UP_CLUSTER_1 0x1C800 +#define AUD_A_UP_CLUSTER_2 0x1C880 +#define AUD_A_UP_CLUSTER_3 0x1C900 + +#define AUD_B_UP_CLUSTER_1 0x1C980 +#define AUD_B_UP_CLUSTER_2 0x1CA00 +#define AUD_B_UP_CLUSTER_3 0x1CA80 + +#define AUD_C_UP_CLUSTER_1 0x1CB00 +#define AUD_C_UP_CLUSTER_2 0x1CB80 +#define AUD_C_UP_CLUSTER_3 0x1CC00 + +#define AUD_E_UP_CLUSTER_1 0x1CC80 +#define AUD_E_UP_CLUSTER_2 0x1CD00 +#define AUD_E_UP_CLUSTER_3 0x1CD80 + +#define RX_SRAM_POOL_FREE 0x1CE00 +#define RX_SRAM_END 0x1D000 + +/* Free Receive SRAM 144 Bytes */ + +/* Transmit SRAM */ +#define TX_SRAM_POOL_START 0x00000 + +#define VID_A_DOWN_CLUSTER_1 0x00040 +#define VID_A_DOWN_CLUSTER_2 0x005E0 +#define VID_A_DOWN_CLUSTER_3 0x00B80 +#define VID_A_DOWN_CLUSTER_4 0x01120 + +#define VID_B_DOWN_CLUSTER_1 0x016C0 +#define VID_B_DOWN_CLUSTER_2 0x01C60 +#define VID_B_DOWN_CLUSTER_3 0x02200 +#define VID_B_DOWN_CLUSTER_4 0x027A0 + +#define VID_C_DOWN_CLUSTER_1 0x02D40 +#define VID_C_DOWN_CLUSTER_2 0x032E0 +#define VID_C_DOWN_CLUSTER_3 0x03880 +#define VID_C_DOWN_CLUSTER_4 0x03E20 + +#define VID_D_DOWN_CLUSTER_1 0x043C0 +#define VID_D_DOWN_CLUSTER_2 0x04960 +#define VID_D_DOWN_CLUSTER_3 0x04F00 +#define VID_D_DOWN_CLUSTER_4 0x054A0 + +#define VID_E_DOWN_CLUSTER_1 0x05a40 +#define VID_E_DOWN_CLUSTER_2 0x05FE0 +#define VID_E_DOWN_CLUSTER_3 0x06580 +#define VID_E_DOWN_CLUSTER_4 0x06B20 + +#define VID_F_DOWN_CLUSTER_1 0x070C0 +#define VID_F_DOWN_CLUSTER_2 0x07660 +#define VID_F_DOWN_CLUSTER_3 0x07C00 +#define VID_F_DOWN_CLUSTER_4 0x081A0 + +#define VID_G_DOWN_CLUSTER_1 0x08740 +#define VID_G_DOWN_CLUSTER_2 0x08CE0 +#define VID_G_DOWN_CLUSTER_3 0x09280 +#define VID_G_DOWN_CLUSTER_4 0x09820 + +#define VID_H_DOWN_CLUSTER_1 0x09DC0 +#define VID_H_DOWN_CLUSTER_2 0x0A360 +#define VID_H_DOWN_CLUSTER_3 0x0A900 +#define VID_H_DOWN_CLUSTER_4 0x0AEA0 + +#define AUD_A_DOWN_CLUSTER_1 0x0B500 +#define AUD_A_DOWN_CLUSTER_2 0x0B580 +#define AUD_A_DOWN_CLUSTER_3 0x0B600 + +#define AUD_B_DOWN_CLUSTER_1 0x0B680 +#define AUD_B_DOWN_CLUSTER_2 0x0B700 +#define AUD_B_DOWN_CLUSTER_3 0x0B780 + +#define AUD_C_DOWN_CLUSTER_1 0x0B800 +#define AUD_C_DOWN_CLUSTER_2 0x0B880 +#define AUD_C_DOWN_CLUSTER_3 0x0B900 + +#define AUD_D_DOWN_CLUSTER_1 0x0B980 +#define AUD_D_DOWN_CLUSTER_2 0x0BA00 +#define AUD_D_DOWN_CLUSTER_3 0x0BA80 + +#define TX_SRAM_POOL_FREE 0x0BB00 +#define TX_SRAM_END 0x0C000 + +#define BYTES_TO_DWORDS(bcount) ((bcount) >> 2) +#define BYTES_TO_QWORDS(bcount) ((bcount) >> 3) +#define BYTES_TO_OWORDS(bcount) ((bcount) >> 4) + +#define VID_IQ_SIZE_DW BYTES_TO_DWORDS(VID_IQ_SIZE) +#define VID_CDT_SIZE_QW BYTES_TO_QWORDS(VID_CDT_SIZE) +#define VID_CLUSTER_SIZE_OW BYTES_TO_OWORDS(VID_CLUSTER_SIZE) + +#define AUDIO_IQ_SIZE_DW BYTES_TO_DWORDS(AUDIO_IQ_SIZE) +#define AUDIO_CDT_SIZE_QW BYTES_TO_QWORDS(AUDIO_CDT_SIZE) +#define AUDIO_CLUSTER_SIZE_QW BYTES_TO_QWORDS(AUDIO_CLUSTER_SIZE) + +#define MBIF_IQ_SIZE_DW BYTES_TO_DWORDS(MBIF_IQ_SIZE) +#define MBIF_CDT_SIZE_QW BYTES_TO_QWORDS(MBIF_CDT_SIZE) +#define MBIF_CLUSTER_SIZE_OW BYTES_TO_OWORDS(MBIF_CLUSTER_SIZE) + +#endif diff --git a/drivers/media/video/cx25821/cx25821-video-upstream-ch2.c b/drivers/media/video/cx25821/cx25821-video-upstream-ch2.c new file mode 100644 index 00000000000..2a724ddfa53 --- /dev/null +++ b/drivers/media/video/cx25821/cx25821-video-upstream-ch2.c @@ -0,0 +1,823 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <hiep.huynh@conexant.com>, <shu.lin@conexant.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "cx25821-video.h" +#include "cx25821-video-upstream-ch2.h" + +#include <linux/fs.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/syscalls.h> +#include <linux/file.h> +#include <linux/fcntl.h> +#include <linux/slab.h> +#include <linux/uaccess.h> + +MODULE_DESCRIPTION("v4l2 driver module for cx25821 based TV cards"); +MODULE_AUTHOR("Hiep Huynh <hiep.huynh@conexant.com>"); +MODULE_LICENSE("GPL"); + +static int _intr_msk = FLD_VID_SRC_RISC1 | FLD_VID_SRC_UF | FLD_VID_SRC_SYNC | + FLD_VID_SRC_OPC_ERR; + +static __le32 *cx25821_update_riscprogram_ch2(struct cx25821_dev *dev, + __le32 *rp, unsigned int offset, + unsigned int bpl, u32 sync_line, + unsigned int lines, + int fifo_enable, int field_type) +{ + unsigned int line, i; + int dist_betwn_starts = bpl * 2; + + *(rp++) = cpu_to_le32(RISC_RESYNC | sync_line); + + if (USE_RISC_NOOP_VIDEO) { + for (i = 0; i < NUM_NO_OPS; i++) + *(rp++) = cpu_to_le32(RISC_NOOP); + } + + /* scan lines */ + for (line = 0; line < lines; line++) { + *(rp++) = cpu_to_le32(RISC_READ | RISC_SOL | RISC_EOL | bpl); + *(rp++) = cpu_to_le32(dev->_data_buf_phys_addr_ch2 + offset); + *(rp++) = cpu_to_le32(0); /* bits 63-32 */ + + if ((lines <= NTSC_FIELD_HEIGHT) + || (line < (NTSC_FIELD_HEIGHT - 1)) + || !(dev->_isNTSC_ch2)) { + offset += dist_betwn_starts; + } + } + + return rp; +} + +static __le32 *cx25821_risc_field_upstream_ch2(struct cx25821_dev *dev, + __le32 *rp, + dma_addr_t databuf_phys_addr, + unsigned int offset, + u32 sync_line, unsigned int bpl, + unsigned int lines, + int fifo_enable, int field_type) +{ + unsigned int line, i; + struct sram_channel *sram_ch = + dev->channels[dev->_channel2_upstream_select].sram_channels; + int dist_betwn_starts = bpl * 2; + + /* sync instruction */ + if (sync_line != NO_SYNC_LINE) + *(rp++) = cpu_to_le32(RISC_RESYNC | sync_line); + + if (USE_RISC_NOOP_VIDEO) { + for (i = 0; i < NUM_NO_OPS; i++) + *(rp++) = cpu_to_le32(RISC_NOOP); + } + + /* scan lines */ + for (line = 0; line < lines; line++) { + *(rp++) = cpu_to_le32(RISC_READ | RISC_SOL | RISC_EOL | bpl); + *(rp++) = cpu_to_le32(databuf_phys_addr + offset); + *(rp++) = cpu_to_le32(0); /* bits 63-32 */ + + if ((lines <= NTSC_FIELD_HEIGHT) + || (line < (NTSC_FIELD_HEIGHT - 1)) + || !(dev->_isNTSC_ch2)) { + offset += dist_betwn_starts; + } + + /* + check if we need to enable the FIFO after the first 4 lines + For the upstream video channel, the risc engine will enable + the FIFO. + */ + if (fifo_enable && line == 3) { + *(rp++) = RISC_WRITECR; + *(rp++) = sram_ch->dma_ctl; + *(rp++) = FLD_VID_FIFO_EN; + *(rp++) = 0x00000001; + } + } + + return rp; +} + +int cx25821_risc_buffer_upstream_ch2(struct cx25821_dev *dev, + struct pci_dev *pci, + unsigned int top_offset, unsigned int bpl, + unsigned int lines) +{ + __le32 *rp; + int fifo_enable = 0; + int singlefield_lines = lines >> 1; /*get line count for single field */ + int odd_num_lines = singlefield_lines; + int frame = 0; + int frame_size = 0; + int databuf_offset = 0; + int risc_program_size = 0; + int risc_flag = RISC_CNT_RESET; + unsigned int bottom_offset = bpl; + dma_addr_t risc_phys_jump_addr; + + if (dev->_isNTSC_ch2) { + odd_num_lines = singlefield_lines + 1; + risc_program_size = FRAME1_VID_PROG_SIZE; + if (bpl == Y411_LINE_SZ) + frame_size = FRAME_SIZE_NTSC_Y411; + else + frame_size = FRAME_SIZE_NTSC_Y422; + } else { + risc_program_size = PAL_VID_PROG_SIZE; + if (bpl == Y411_LINE_SZ) + frame_size = FRAME_SIZE_PAL_Y411; + else + frame_size = FRAME_SIZE_PAL_Y422; + } + + /* Virtual address of Risc buffer program */ + rp = dev->_dma_virt_addr_ch2; + + for (frame = 0; frame < NUM_FRAMES; frame++) { + databuf_offset = frame_size * frame; + + if (UNSET != top_offset) { + fifo_enable = (frame == 0) ? FIFO_ENABLE : FIFO_DISABLE; + rp = cx25821_risc_field_upstream_ch2(dev, rp, + dev->_data_buf_phys_addr_ch2 + databuf_offset, + top_offset, 0, bpl, odd_num_lines, fifo_enable, + ODD_FIELD); + } + + fifo_enable = FIFO_DISABLE; + + /* Even field */ + rp = cx25821_risc_field_upstream_ch2(dev, rp, + dev->_data_buf_phys_addr_ch2 + databuf_offset, + bottom_offset, 0x200, bpl, singlefield_lines, + fifo_enable, EVEN_FIELD); + + if (frame == 0) { + risc_flag = RISC_CNT_RESET; + risc_phys_jump_addr = dev->_dma_phys_start_addr_ch2 + + risc_program_size; + } else { + risc_flag = RISC_CNT_INC; + risc_phys_jump_addr = dev->_dma_phys_start_addr_ch2; + } + + /* + Loop to 2ndFrameRISC or to Start of + Risc program & generate IRQ + */ + *(rp++) = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | risc_flag); + *(rp++) = cpu_to_le32(risc_phys_jump_addr); + *(rp++) = cpu_to_le32(0); + } + + return 0; +} + +void cx25821_stop_upstream_video_ch2(struct cx25821_dev *dev) +{ + struct sram_channel *sram_ch = + dev->channels[VID_UPSTREAM_SRAM_CHANNEL_J].sram_channels; + u32 tmp = 0; + + if (!dev->_is_running_ch2) { + pr_info("No video file is currently running so return!\n"); + return; + } + /* Disable RISC interrupts */ + tmp = cx_read(sram_ch->int_msk); + cx_write(sram_ch->int_msk, tmp & ~_intr_msk); + + /* Turn OFF risc and fifo */ + tmp = cx_read(sram_ch->dma_ctl); + cx_write(sram_ch->dma_ctl, tmp & ~(FLD_VID_FIFO_EN | FLD_VID_RISC_EN)); + + /* Clear data buffer memory */ + if (dev->_data_buf_virt_addr_ch2) + memset(dev->_data_buf_virt_addr_ch2, 0, + dev->_data_buf_size_ch2); + + dev->_is_running_ch2 = 0; + dev->_is_first_frame_ch2 = 0; + dev->_frame_count_ch2 = 0; + dev->_file_status_ch2 = END_OF_FILE; + + kfree(dev->_irq_queues_ch2); + dev->_irq_queues_ch2 = NULL; + + kfree(dev->_filename_ch2); + + tmp = cx_read(VID_CH_MODE_SEL); + cx_write(VID_CH_MODE_SEL, tmp & 0xFFFFFE00); +} + +void cx25821_free_mem_upstream_ch2(struct cx25821_dev *dev) +{ + if (dev->_is_running_ch2) + cx25821_stop_upstream_video_ch2(dev); + + if (dev->_dma_virt_addr_ch2) { + pci_free_consistent(dev->pci, dev->_risc_size_ch2, + dev->_dma_virt_addr_ch2, + dev->_dma_phys_addr_ch2); + dev->_dma_virt_addr_ch2 = NULL; + } + + if (dev->_data_buf_virt_addr_ch2) { + pci_free_consistent(dev->pci, dev->_data_buf_size_ch2, + dev->_data_buf_virt_addr_ch2, + dev->_data_buf_phys_addr_ch2); + dev->_data_buf_virt_addr_ch2 = NULL; + } +} + +int cx25821_get_frame_ch2(struct cx25821_dev *dev, struct sram_channel *sram_ch) +{ + struct file *myfile; + int frame_index_temp = dev->_frame_index_ch2; + int i = 0; + int line_size = + (dev->_pixel_format_ch2 == + PIXEL_FRMT_411) ? Y411_LINE_SZ : Y422_LINE_SZ; + int frame_size = 0; + int frame_offset = 0; + ssize_t vfs_read_retval = 0; + char mybuf[line_size]; + loff_t file_offset; + loff_t pos; + mm_segment_t old_fs; + + if (dev->_file_status_ch2 == END_OF_FILE) + return 0; + + if (dev->_isNTSC_ch2) { + frame_size = + (line_size == + Y411_LINE_SZ) ? FRAME_SIZE_NTSC_Y411 : + FRAME_SIZE_NTSC_Y422; + } else { + frame_size = + (line_size == + Y411_LINE_SZ) ? FRAME_SIZE_PAL_Y411 : FRAME_SIZE_PAL_Y422; + } + + frame_offset = (frame_index_temp > 0) ? frame_size : 0; + file_offset = dev->_frame_count_ch2 * frame_size; + + myfile = filp_open(dev->_filename_ch2, O_RDONLY | O_LARGEFILE, 0); + if (IS_ERR(myfile)) { + const int open_errno = -PTR_ERR(myfile); + pr_err("%s(): ERROR opening file(%s) with errno = %d!\n", + __func__, dev->_filename_ch2, open_errno); + return PTR_ERR(myfile); + } else { + if (!(myfile->f_op)) { + pr_err("%s(): File has no file operations registered!\n", + __func__); + filp_close(myfile, NULL); + return -EIO; + } + + if (!myfile->f_op->read) { + pr_err("%s(): File has no READ operations registered!\n", + __func__); + filp_close(myfile, NULL); + return -EIO; + } + + pos = myfile->f_pos; + old_fs = get_fs(); + set_fs(KERNEL_DS); + + for (i = 0; i < dev->_lines_count_ch2; i++) { + pos = file_offset; + + vfs_read_retval = + vfs_read(myfile, mybuf, line_size, &pos); + + if (vfs_read_retval > 0 && vfs_read_retval == line_size + && dev->_data_buf_virt_addr_ch2 != NULL) { + memcpy((void *)(dev->_data_buf_virt_addr_ch2 + + frame_offset / 4), mybuf, + vfs_read_retval); + } + + file_offset += vfs_read_retval; + frame_offset += vfs_read_retval; + + if (vfs_read_retval < line_size) { + pr_info("Done: exit %s() since no more bytes to read from Video file\n", + __func__); + break; + } + } + + if (i > 0) + dev->_frame_count_ch2++; + + dev->_file_status_ch2 = + (vfs_read_retval == line_size) ? IN_PROGRESS : END_OF_FILE; + + set_fs(old_fs); + filp_close(myfile, NULL); + } + + return 0; +} + +static void cx25821_vidups_handler_ch2(struct work_struct *work) +{ + struct cx25821_dev *dev = + container_of(work, struct cx25821_dev, _irq_work_entry_ch2); + + if (!dev) { + pr_err("ERROR %s(): since container_of(work_struct) FAILED!\n", + __func__); + return; + } + + cx25821_get_frame_ch2(dev, + dev->channels[dev-> + _channel2_upstream_select].sram_channels); +} + +int cx25821_openfile_ch2(struct cx25821_dev *dev, struct sram_channel *sram_ch) +{ + struct file *myfile; + int i = 0, j = 0; + int line_size = + (dev->_pixel_format_ch2 == + PIXEL_FRMT_411) ? Y411_LINE_SZ : Y422_LINE_SZ; + ssize_t vfs_read_retval = 0; + char mybuf[line_size]; + loff_t pos; + loff_t offset = (unsigned long)0; + mm_segment_t old_fs; + + myfile = filp_open(dev->_filename_ch2, O_RDONLY | O_LARGEFILE, 0); + + if (IS_ERR(myfile)) { + const int open_errno = -PTR_ERR(myfile); + pr_err("%s(): ERROR opening file(%s) with errno = %d!\n", + __func__, dev->_filename_ch2, open_errno); + return PTR_ERR(myfile); + } else { + if (!(myfile->f_op)) { + pr_err("%s(): File has no file operations registered!\n", + __func__); + filp_close(myfile, NULL); + return -EIO; + } + + if (!myfile->f_op->read) { + pr_err("%s(): File has no READ operations registered! Returning\n", + __func__); + filp_close(myfile, NULL); + return -EIO; + } + + pos = myfile->f_pos; + old_fs = get_fs(); + set_fs(KERNEL_DS); + + for (j = 0; j < NUM_FRAMES; j++) { + for (i = 0; i < dev->_lines_count_ch2; i++) { + pos = offset; + + vfs_read_retval = + vfs_read(myfile, mybuf, line_size, &pos); + + if (vfs_read_retval > 0 + && vfs_read_retval == line_size + && dev->_data_buf_virt_addr_ch2 != NULL) { + memcpy((void *)(dev-> + _data_buf_virt_addr_ch2 + + offset / 4), mybuf, + vfs_read_retval); + } + + offset += vfs_read_retval; + + if (vfs_read_retval < line_size) { + pr_info("Done: exit %s() since no more bytes to read from Video file\n", + __func__); + break; + } + } + + if (i > 0) + dev->_frame_count_ch2++; + + if (vfs_read_retval < line_size) + break; + } + + dev->_file_status_ch2 = + (vfs_read_retval == line_size) ? IN_PROGRESS : END_OF_FILE; + + set_fs(old_fs); + myfile->f_pos = 0; + filp_close(myfile, NULL); + } + + return 0; +} + +static int cx25821_upstream_buffer_prepare_ch2(struct cx25821_dev *dev, + struct sram_channel *sram_ch, + int bpl) +{ + int ret = 0; + dma_addr_t dma_addr; + dma_addr_t data_dma_addr; + + if (dev->_dma_virt_addr_ch2 != NULL) { + pci_free_consistent(dev->pci, dev->upstream_riscbuf_size_ch2, + dev->_dma_virt_addr_ch2, + dev->_dma_phys_addr_ch2); + } + + dev->_dma_virt_addr_ch2 = + pci_alloc_consistent(dev->pci, dev->upstream_riscbuf_size_ch2, + &dma_addr); + dev->_dma_virt_start_addr_ch2 = dev->_dma_virt_addr_ch2; + dev->_dma_phys_start_addr_ch2 = dma_addr; + dev->_dma_phys_addr_ch2 = dma_addr; + dev->_risc_size_ch2 = dev->upstream_riscbuf_size_ch2; + + if (!dev->_dma_virt_addr_ch2) { + pr_err("FAILED to allocate memory for Risc buffer! Returning\n"); + return -ENOMEM; + } + + /* Iniitize at this address until n bytes to 0 */ + memset(dev->_dma_virt_addr_ch2, 0, dev->_risc_size_ch2); + + if (dev->_data_buf_virt_addr_ch2 != NULL) { + pci_free_consistent(dev->pci, dev->upstream_databuf_size_ch2, + dev->_data_buf_virt_addr_ch2, + dev->_data_buf_phys_addr_ch2); + } + /* For Video Data buffer allocation */ + dev->_data_buf_virt_addr_ch2 = + pci_alloc_consistent(dev->pci, dev->upstream_databuf_size_ch2, + &data_dma_addr); + dev->_data_buf_phys_addr_ch2 = data_dma_addr; + dev->_data_buf_size_ch2 = dev->upstream_databuf_size_ch2; + + if (!dev->_data_buf_virt_addr_ch2) { + pr_err("FAILED to allocate memory for data buffer! Returning\n"); + return -ENOMEM; + } + + /* Initialize at this address until n bytes to 0 */ + memset(dev->_data_buf_virt_addr_ch2, 0, dev->_data_buf_size_ch2); + + ret = cx25821_openfile_ch2(dev, sram_ch); + if (ret < 0) + return ret; + + /* Creating RISC programs */ + ret = cx25821_risc_buffer_upstream_ch2(dev, dev->pci, 0, bpl, + dev->_lines_count_ch2); + if (ret < 0) { + pr_info("Failed creating Video Upstream Risc programs!\n"); + goto error; + } + + return 0; + +error: + return ret; +} + +int cx25821_video_upstream_irq_ch2(struct cx25821_dev *dev, int chan_num, + u32 status) +{ + u32 int_msk_tmp; + struct sram_channel *channel = dev->channels[chan_num].sram_channels; + int singlefield_lines = NTSC_FIELD_HEIGHT; + int line_size_in_bytes = Y422_LINE_SZ; + int odd_risc_prog_size = 0; + dma_addr_t risc_phys_jump_addr; + __le32 *rp; + + if (status & FLD_VID_SRC_RISC1) { + /* We should only process one program per call */ + u32 prog_cnt = cx_read(channel->gpcnt); + + /* + * Since we've identified our IRQ, clear our bits from the + * interrupt mask and interrupt status registers + */ + int_msk_tmp = cx_read(channel->int_msk); + cx_write(channel->int_msk, int_msk_tmp & ~_intr_msk); + cx_write(channel->int_stat, _intr_msk); + + spin_lock(&dev->slock); + + dev->_frame_index_ch2 = prog_cnt; + + queue_work(dev->_irq_queues_ch2, &dev->_irq_work_entry_ch2); + + if (dev->_is_first_frame_ch2) { + dev->_is_first_frame_ch2 = 0; + + if (dev->_isNTSC_ch2) { + singlefield_lines += 1; + odd_risc_prog_size = ODD_FLD_NTSC_PROG_SIZE; + } else { + singlefield_lines = PAL_FIELD_HEIGHT; + odd_risc_prog_size = ODD_FLD_PAL_PROG_SIZE; + } + + if (dev->_dma_virt_start_addr_ch2 != NULL) { + if (dev->_pixel_format_ch2 == PIXEL_FRMT_411) + line_size_in_bytes = Y411_LINE_SZ; + else + line_size_in_bytes = Y422_LINE_SZ; + risc_phys_jump_addr = + dev->_dma_phys_start_addr_ch2 + + odd_risc_prog_size; + + rp = cx25821_update_riscprogram_ch2(dev, + dev->_dma_virt_start_addr_ch2, + TOP_OFFSET, line_size_in_bytes, + 0x0, singlefield_lines, + FIFO_DISABLE, ODD_FIELD); + + /* Jump to Even Risc program of 1st Frame */ + *(rp++) = cpu_to_le32(RISC_JUMP); + *(rp++) = cpu_to_le32(risc_phys_jump_addr); + *(rp++) = cpu_to_le32(0); + } + } + + spin_unlock(&dev->slock); + } + + if (dev->_file_status_ch2 == END_OF_FILE) { + pr_info("EOF Channel 2 Framecount = %d\n", + dev->_frame_count_ch2); + return -1; + } + /* ElSE, set the interrupt mask register, re-enable irq. */ + int_msk_tmp = cx_read(channel->int_msk); + cx_write(channel->int_msk, int_msk_tmp |= _intr_msk); + + return 0; +} + +static irqreturn_t cx25821_upstream_irq_ch2(int irq, void *dev_id) +{ + struct cx25821_dev *dev = dev_id; + u32 msk_stat, vid_status; + int handled = 0; + int channel_num = 0; + struct sram_channel *sram_ch; + + if (!dev) + return -1; + + channel_num = VID_UPSTREAM_SRAM_CHANNEL_J; + sram_ch = dev->channels[channel_num].sram_channels; + + msk_stat = cx_read(sram_ch->int_mstat); + vid_status = cx_read(sram_ch->int_stat); + + /* Only deal with our interrupt */ + if (vid_status) { + handled = + cx25821_video_upstream_irq_ch2(dev, channel_num, + vid_status); + } + + if (handled < 0) + cx25821_stop_upstream_video_ch2(dev); + else + handled += handled; + + return IRQ_RETVAL(handled); +} + +static void cx25821_set_pixelengine_ch2(struct cx25821_dev *dev, + struct sram_channel *ch, int pix_format) +{ + int width = WIDTH_D1; + int height = dev->_lines_count_ch2; + int num_lines, odd_num_lines; + u32 value; + int vip_mode = PIXEL_ENGINE_VIP1; + + value = ((pix_format & 0x3) << 12) | (vip_mode & 0x7); + value &= 0xFFFFFFEF; + value |= dev->_isNTSC_ch2 ? 0 : 0x10; + cx_write(ch->vid_fmt_ctl, value); + + /* + * set number of active pixels in each line. Default is 720 + * pixels in both NTSC and PAL format + */ + cx_write(ch->vid_active_ctl1, width); + + num_lines = (height / 2) & 0x3FF; + odd_num_lines = num_lines; + + if (dev->_isNTSC_ch2) + odd_num_lines += 1; + + value = (num_lines << 16) | odd_num_lines; + + /* set number of active lines in field 0 (top) and field 1 (bottom) */ + cx_write(ch->vid_active_ctl2, value); + + cx_write(ch->vid_cdt_size, VID_CDT_SIZE >> 3); +} + +int cx25821_start_video_dma_upstream_ch2(struct cx25821_dev *dev, + struct sram_channel *sram_ch) +{ + u32 tmp = 0; + int err = 0; + + /* + * 656/VIP SRC Upstream Channel I & J and 7 - Host Bus Interface + * for channel A-C + */ + tmp = cx_read(VID_CH_MODE_SEL); + cx_write(VID_CH_MODE_SEL, tmp | 0x1B0001FF); + + /* + * Set the physical start address of the RISC program in the initial + * program counter(IPC) member of the cmds. + */ + cx_write(sram_ch->cmds_start + 0, dev->_dma_phys_addr_ch2); + cx_write(sram_ch->cmds_start + 4, 0); /* Risc IPC High 64 bits 63-32 */ + + /* reset counter */ + cx_write(sram_ch->gpcnt_ctl, 3); + + /* Clear our bits from the interrupt status register. */ + cx_write(sram_ch->int_stat, _intr_msk); + + /* Set the interrupt mask register, enable irq. */ + cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | (1 << sram_ch->irq_bit)); + tmp = cx_read(sram_ch->int_msk); + cx_write(sram_ch->int_msk, tmp |= _intr_msk); + + err = + request_irq(dev->pci->irq, cx25821_upstream_irq_ch2, + IRQF_SHARED, dev->name, dev); + if (err < 0) { + pr_err("%s: can't get upstream IRQ %d\n", + dev->name, dev->pci->irq); + goto fail_irq; + } + /* Start the DMA engine */ + tmp = cx_read(sram_ch->dma_ctl); + cx_set(sram_ch->dma_ctl, tmp | FLD_VID_RISC_EN); + + dev->_is_running_ch2 = 1; + dev->_is_first_frame_ch2 = 1; + + return 0; + +fail_irq: + cx25821_dev_unregister(dev); + return err; +} + +int cx25821_vidupstream_init_ch2(struct cx25821_dev *dev, int channel_select, + int pixel_format) +{ + struct sram_channel *sram_ch; + u32 tmp; + int retval = 0; + int err = 0; + int data_frame_size = 0; + int risc_buffer_size = 0; + int str_length = 0; + + if (dev->_is_running_ch2) { + pr_info("Video Channel is still running so return!\n"); + return 0; + } + + dev->_channel2_upstream_select = channel_select; + sram_ch = dev->channels[channel_select].sram_channels; + + INIT_WORK(&dev->_irq_work_entry_ch2, cx25821_vidups_handler_ch2); + dev->_irq_queues_ch2 = + create_singlethread_workqueue("cx25821_workqueue2"); + + if (!dev->_irq_queues_ch2) { + pr_err("create_singlethread_workqueue() for Video FAILED!\n"); + return -ENOMEM; + } + /* + * 656/VIP SRC Upstream Channel I & J and 7 - + * Host Bus Interface for channel A-C + */ + tmp = cx_read(VID_CH_MODE_SEL); + cx_write(VID_CH_MODE_SEL, tmp | 0x1B0001FF); + + dev->_is_running_ch2 = 0; + dev->_frame_count_ch2 = 0; + dev->_file_status_ch2 = RESET_STATUS; + dev->_lines_count_ch2 = dev->_isNTSC_ch2 ? 480 : 576; + dev->_pixel_format_ch2 = pixel_format; + dev->_line_size_ch2 = + (dev->_pixel_format_ch2 == + PIXEL_FRMT_422) ? (WIDTH_D1 * 2) : (WIDTH_D1 * 3) / 2; + data_frame_size = dev->_isNTSC_ch2 ? NTSC_DATA_BUF_SZ : PAL_DATA_BUF_SZ; + risc_buffer_size = + dev->_isNTSC_ch2 ? NTSC_RISC_BUF_SIZE : PAL_RISC_BUF_SIZE; + + if (dev->input_filename_ch2) { + str_length = strlen(dev->input_filename_ch2); + dev->_filename_ch2 = kmalloc(str_length + 1, GFP_KERNEL); + + if (!dev->_filename_ch2) + goto error; + + memcpy(dev->_filename_ch2, dev->input_filename_ch2, + str_length + 1); + } else { + str_length = strlen(dev->_defaultname_ch2); + dev->_filename_ch2 = kmalloc(str_length + 1, GFP_KERNEL); + + if (!dev->_filename_ch2) + goto error; + + memcpy(dev->_filename_ch2, dev->_defaultname_ch2, + str_length + 1); + } + + /* Default if filename is empty string */ + if (strcmp(dev->input_filename_ch2, "") == 0) { + if (dev->_isNTSC_ch2) { + dev->_filename_ch2 = + (dev->_pixel_format_ch2 == + PIXEL_FRMT_411) ? "/root/vid411.yuv" : + "/root/vidtest.yuv"; + } else { + dev->_filename_ch2 = + (dev->_pixel_format_ch2 == + PIXEL_FRMT_411) ? "/root/pal411.yuv" : + "/root/pal422.yuv"; + } + } + + retval = cx25821_sram_channel_setup_upstream(dev, sram_ch, + dev->_line_size_ch2, 0); + + /* setup fifo + format */ + cx25821_set_pixelengine_ch2(dev, sram_ch, dev->_pixel_format_ch2); + + dev->upstream_riscbuf_size_ch2 = risc_buffer_size * 2; + dev->upstream_databuf_size_ch2 = data_frame_size * 2; + + /* Allocating buffers and prepare RISC program */ + retval = cx25821_upstream_buffer_prepare_ch2(dev, sram_ch, + dev->_line_size_ch2); + if (retval < 0) { + pr_err("%s: Failed to set up Video upstream buffers!\n", + dev->name); + goto error; + } + + cx25821_start_video_dma_upstream_ch2(dev, sram_ch); + + return 0; + +error: + cx25821_dev_unregister(dev); + + return err; +} diff --git a/drivers/media/video/cx25821/cx25821-video-upstream-ch2.h b/drivers/media/video/cx25821/cx25821-video-upstream-ch2.h new file mode 100644 index 00000000000..d42dab59b66 --- /dev/null +++ b/drivers/media/video/cx25821/cx25821-video-upstream-ch2.h @@ -0,0 +1,138 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <hiep.huynh@conexant.com>, <shu.lin@conexant.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/mutex.h> +#include <linux/workqueue.h> + +#define OPEN_FILE_1 0 +#define NUM_PROGS 8 +#define NUM_FRAMES 2 +#define ODD_FIELD 0 +#define EVEN_FIELD 1 +#define TOP_OFFSET 0 +#define FIFO_DISABLE 0 +#define FIFO_ENABLE 1 +#define TEST_FRAMES 5 +#define END_OF_FILE 0 +#define IN_PROGRESS 1 +#define RESET_STATUS -1 +#define NUM_NO_OPS 5 + +/* PAL and NTSC line sizes and number of lines. */ +#define WIDTH_D1 720 +#define NTSC_LINES_PER_FRAME 480 +#define PAL_LINES_PER_FRAME 576 +#define PAL_LINE_SZ 1440 +#define Y422_LINE_SZ 1440 +#define Y411_LINE_SZ 1080 +#define NTSC_FIELD_HEIGHT 240 +#define NTSC_ODD_FLD_LINES 241 +#define PAL_FIELD_HEIGHT 288 + +#define FRAME_SIZE_NTSC_Y422 (NTSC_LINES_PER_FRAME * Y422_LINE_SZ) +#define FRAME_SIZE_NTSC_Y411 (NTSC_LINES_PER_FRAME * Y411_LINE_SZ) +#define FRAME_SIZE_PAL_Y422 (PAL_LINES_PER_FRAME * Y422_LINE_SZ) +#define FRAME_SIZE_PAL_Y411 (PAL_LINES_PER_FRAME * Y411_LINE_SZ) + +#define NTSC_DATA_BUF_SZ (Y422_LINE_SZ * NTSC_LINES_PER_FRAME) +#define PAL_DATA_BUF_SZ (Y422_LINE_SZ * PAL_LINES_PER_FRAME) + +#define RISC_WRITECR_INSTRUCTION_SIZE 16 +#define RISC_SYNC_INSTRUCTION_SIZE 4 +#define JUMP_INSTRUCTION_SIZE 12 +#define MAXSIZE_NO_OPS 36 +#define DWORD_SIZE 4 + +#define USE_RISC_NOOP_VIDEO 1 + +#ifdef USE_RISC_NOOP_VIDEO +#define PAL_US_VID_PROG_SIZE \ + (PAL_FIELD_HEIGHT * 3 * DWORD_SIZE + \ + RISC_WRITECR_INSTRUCTION_SIZE + RISC_SYNC_INSTRUCTION_SIZE + \ + NUM_NO_OPS * DWORD_SIZE) + +#define PAL_RISC_BUF_SIZE (2 * PAL_US_VID_PROG_SIZE) + +#define PAL_VID_PROG_SIZE \ + ((PAL_FIELD_HEIGHT * 2) * 3 * DWORD_SIZE + \ + 2 * RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \ + JUMP_INSTRUCTION_SIZE + 2 * NUM_NO_OPS * DWORD_SIZE) + +#define ODD_FLD_PAL_PROG_SIZE \ + (PAL_FIELD_HEIGHT * 3 * DWORD_SIZE + \ + RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \ + NUM_NO_OPS * DWORD_SIZE) + +#define NTSC_US_VID_PROG_SIZE \ + ((NTSC_ODD_FLD_LINES + 1) * 3 * DWORD_SIZE + \ + RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE + \ + NUM_NO_OPS * DWORD_SIZE) + +#define NTSC_RISC_BUF_SIZE \ + (2 * (RISC_SYNC_INSTRUCTION_SIZE + NTSC_US_VID_PROG_SIZE)) + +#define FRAME1_VID_PROG_SIZE \ + ((NTSC_ODD_FLD_LINES + NTSC_FIELD_HEIGHT) * \ + 3 * DWORD_SIZE + 2 * RISC_SYNC_INSTRUCTION_SIZE + \ + RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE + \ + 2 * NUM_NO_OPS * DWORD_SIZE) + +#define ODD_FLD_NTSC_PROG_SIZE \ + (NTSC_ODD_FLD_LINES * 3 * DWORD_SIZE + \ + RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \ + NUM_NO_OPS * DWORD_SIZE) +#endif + +#ifndef USE_RISC_NOOP_VIDEO +#define PAL_US_VID_PROG_SIZE \ + ((PAL_FIELD_HEIGHT + 1) * 3 * DWORD_SIZE + \ + RISC_WRITECR_INSTRUCTION_SIZE) + +#define PAL_RISC_BUF_SIZE \ + (2 * (RISC_SYNC_INSTRUCTION_SIZE + PAL_US_VID_PROG_SIZE)) + +#define PAL_VID_PROG_SIZE \ + ((PAL_FIELD_HEIGHT * 2) * 3 * DWORD_SIZE + \ + 2 * RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \ + JUMP_INSTRUCTION_SIZE) + +#define ODD_FLD_PAL_PROG_SIZE \ + (PAL_FIELD_HEIGHT * 3 * DWORD_SIZE + \ + RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE) + +#define ODD_FLD_NTSC_PROG_SIZE \ + (NTSC_ODD_FLD_LINES * 3 * DWORD_SIZE + \ + RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE) + +#define NTSC_US_VID_PROG_SIZE \ + ((NTSC_ODD_FLD_LINES + 1) * 3 * DWORD_SIZE + \ + RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE) + +#define NTSC_RISC_BUF_SIZE \ + (2 * (RISC_SYNC_INSTRUCTION_SIZE + NTSC_US_VID_PROG_SIZE)) + +#define FRAME1_VID_PROG_SIZE \ + ((NTSC_ODD_FLD_LINES + NTSC_FIELD_HEIGHT) * \ + 3 * DWORD_SIZE + 2 * RISC_SYNC_INSTRUCTION_SIZE + \ + RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE) + +#endif diff --git a/drivers/media/video/cx25821/cx25821-video-upstream.c b/drivers/media/video/cx25821/cx25821-video-upstream.c new file mode 100644 index 00000000000..c0b80068f46 --- /dev/null +++ b/drivers/media/video/cx25821/cx25821-video-upstream.c @@ -0,0 +1,885 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <hiep.huynh@conexant.com>, <shu.lin@conexant.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "cx25821-video.h" +#include "cx25821-video-upstream.h" + +#include <linux/fs.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/syscalls.h> +#include <linux/file.h> +#include <linux/fcntl.h> +#include <linux/slab.h> +#include <linux/uaccess.h> + +MODULE_DESCRIPTION("v4l2 driver module for cx25821 based TV cards"); +MODULE_AUTHOR("Hiep Huynh <hiep.huynh@conexant.com>"); +MODULE_LICENSE("GPL"); + +static int _intr_msk = FLD_VID_SRC_RISC1 | FLD_VID_SRC_UF | FLD_VID_SRC_SYNC | + FLD_VID_SRC_OPC_ERR; + +int cx25821_sram_channel_setup_upstream(struct cx25821_dev *dev, + struct sram_channel *ch, + unsigned int bpl, u32 risc) +{ + unsigned int i, lines; + u32 cdt; + + if (ch->cmds_start == 0) { + cx_write(ch->ptr1_reg, 0); + cx_write(ch->ptr2_reg, 0); + cx_write(ch->cnt2_reg, 0); + cx_write(ch->cnt1_reg, 0); + return 0; + } + + bpl = (bpl + 7) & ~7; /* alignment */ + cdt = ch->cdt; + lines = ch->fifo_size / bpl; + + if (lines > 4) + lines = 4; + + BUG_ON(lines < 2); + + /* write CDT */ + for (i = 0; i < lines; i++) { + cx_write(cdt + 16 * i, ch->fifo_start + bpl * i); + cx_write(cdt + 16 * i + 4, 0); + cx_write(cdt + 16 * i + 8, 0); + cx_write(cdt + 16 * i + 12, 0); + } + + /* write CMDS */ + cx_write(ch->cmds_start + 0, risc); + + cx_write(ch->cmds_start + 4, 0); + cx_write(ch->cmds_start + 8, cdt); + cx_write(ch->cmds_start + 12, (lines * 16) >> 3); + cx_write(ch->cmds_start + 16, ch->ctrl_start); + + cx_write(ch->cmds_start + 20, VID_IQ_SIZE_DW); + + for (i = 24; i < 80; i += 4) + cx_write(ch->cmds_start + i, 0); + + /* fill registers */ + cx_write(ch->ptr1_reg, ch->fifo_start); + cx_write(ch->ptr2_reg, cdt); + cx_write(ch->cnt2_reg, (lines * 16) >> 3); + cx_write(ch->cnt1_reg, (bpl >> 3) - 1); + + return 0; +} + +static __le32 *cx25821_update_riscprogram(struct cx25821_dev *dev, + __le32 *rp, unsigned int offset, + unsigned int bpl, u32 sync_line, + unsigned int lines, int fifo_enable, + int field_type) +{ + unsigned int line, i; + int dist_betwn_starts = bpl * 2; + + *(rp++) = cpu_to_le32(RISC_RESYNC | sync_line); + + if (USE_RISC_NOOP_VIDEO) { + for (i = 0; i < NUM_NO_OPS; i++) + *(rp++) = cpu_to_le32(RISC_NOOP); + } + + /* scan lines */ + for (line = 0; line < lines; line++) { + *(rp++) = cpu_to_le32(RISC_READ | RISC_SOL | RISC_EOL | bpl); + *(rp++) = cpu_to_le32(dev->_data_buf_phys_addr + offset); + *(rp++) = cpu_to_le32(0); /* bits 63-32 */ + + if ((lines <= NTSC_FIELD_HEIGHT) + || (line < (NTSC_FIELD_HEIGHT - 1)) || !(dev->_isNTSC)) { + offset += dist_betwn_starts; + } + } + + return rp; +} + +static __le32 *cx25821_risc_field_upstream(struct cx25821_dev *dev, __le32 * rp, + dma_addr_t databuf_phys_addr, + unsigned int offset, u32 sync_line, + unsigned int bpl, unsigned int lines, + int fifo_enable, int field_type) +{ + unsigned int line, i; + struct sram_channel *sram_ch = + dev->channels[dev->_channel_upstream_select].sram_channels; + int dist_betwn_starts = bpl * 2; + + /* sync instruction */ + if (sync_line != NO_SYNC_LINE) + *(rp++) = cpu_to_le32(RISC_RESYNC | sync_line); + + if (USE_RISC_NOOP_VIDEO) { + for (i = 0; i < NUM_NO_OPS; i++) + *(rp++) = cpu_to_le32(RISC_NOOP); + } + + /* scan lines */ + for (line = 0; line < lines; line++) { + *(rp++) = cpu_to_le32(RISC_READ | RISC_SOL | RISC_EOL | bpl); + *(rp++) = cpu_to_le32(databuf_phys_addr + offset); + *(rp++) = cpu_to_le32(0); /* bits 63-32 */ + + if ((lines <= NTSC_FIELD_HEIGHT) + || (line < (NTSC_FIELD_HEIGHT - 1)) || !(dev->_isNTSC)) + /* to skip the other field line */ + offset += dist_betwn_starts; + + /* check if we need to enable the FIFO after the first 4 lines + * For the upstream video channel, the risc engine will enable + * the FIFO. */ + if (fifo_enable && line == 3) { + *(rp++) = RISC_WRITECR; + *(rp++) = sram_ch->dma_ctl; + *(rp++) = FLD_VID_FIFO_EN; + *(rp++) = 0x00000001; + } + } + + return rp; +} + +int cx25821_risc_buffer_upstream(struct cx25821_dev *dev, + struct pci_dev *pci, + unsigned int top_offset, + unsigned int bpl, unsigned int lines) +{ + __le32 *rp; + int fifo_enable = 0; + /* get line count for single field */ + int singlefield_lines = lines >> 1; + int odd_num_lines = singlefield_lines; + int frame = 0; + int frame_size = 0; + int databuf_offset = 0; + int risc_program_size = 0; + int risc_flag = RISC_CNT_RESET; + unsigned int bottom_offset = bpl; + dma_addr_t risc_phys_jump_addr; + + if (dev->_isNTSC) { + odd_num_lines = singlefield_lines + 1; + risc_program_size = FRAME1_VID_PROG_SIZE; + frame_size = + (bpl == + Y411_LINE_SZ) ? FRAME_SIZE_NTSC_Y411 : + FRAME_SIZE_NTSC_Y422; + } else { + risc_program_size = PAL_VID_PROG_SIZE; + frame_size = + (bpl == + Y411_LINE_SZ) ? FRAME_SIZE_PAL_Y411 : FRAME_SIZE_PAL_Y422; + } + + /* Virtual address of Risc buffer program */ + rp = dev->_dma_virt_addr; + + for (frame = 0; frame < NUM_FRAMES; frame++) { + databuf_offset = frame_size * frame; + + if (UNSET != top_offset) { + fifo_enable = (frame == 0) ? FIFO_ENABLE : FIFO_DISABLE; + rp = cx25821_risc_field_upstream(dev, rp, + dev-> + _data_buf_phys_addr + + databuf_offset, + top_offset, 0, bpl, + odd_num_lines, + fifo_enable, + ODD_FIELD); + } + + fifo_enable = FIFO_DISABLE; + + /* Even Field */ + rp = cx25821_risc_field_upstream(dev, rp, + dev->_data_buf_phys_addr + + databuf_offset, bottom_offset, + 0x200, bpl, singlefield_lines, + fifo_enable, EVEN_FIELD); + + if (frame == 0) { + risc_flag = RISC_CNT_RESET; + risc_phys_jump_addr = + dev->_dma_phys_start_addr + risc_program_size; + } else { + risc_phys_jump_addr = dev->_dma_phys_start_addr; + risc_flag = RISC_CNT_INC; + } + + /* Loop to 2ndFrameRISC or to Start of Risc + * program & generate IRQ + */ + *(rp++) = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | risc_flag); + *(rp++) = cpu_to_le32(risc_phys_jump_addr); + *(rp++) = cpu_to_le32(0); + } + + return 0; +} + +void cx25821_stop_upstream_video_ch1(struct cx25821_dev *dev) +{ + struct sram_channel *sram_ch = + dev->channels[VID_UPSTREAM_SRAM_CHANNEL_I].sram_channels; + u32 tmp = 0; + + if (!dev->_is_running) { + pr_info("No video file is currently running so return!\n"); + return; + } + /* Disable RISC interrupts */ + tmp = cx_read(sram_ch->int_msk); + cx_write(sram_ch->int_msk, tmp & ~_intr_msk); + + /* Turn OFF risc and fifo enable */ + tmp = cx_read(sram_ch->dma_ctl); + cx_write(sram_ch->dma_ctl, tmp & ~(FLD_VID_FIFO_EN | FLD_VID_RISC_EN)); + + /* Clear data buffer memory */ + if (dev->_data_buf_virt_addr) + memset(dev->_data_buf_virt_addr, 0, dev->_data_buf_size); + + dev->_is_running = 0; + dev->_is_first_frame = 0; + dev->_frame_count = 0; + dev->_file_status = END_OF_FILE; + + kfree(dev->_irq_queues); + dev->_irq_queues = NULL; + + kfree(dev->_filename); + + tmp = cx_read(VID_CH_MODE_SEL); + cx_write(VID_CH_MODE_SEL, tmp & 0xFFFFFE00); +} + +void cx25821_free_mem_upstream_ch1(struct cx25821_dev *dev) +{ + if (dev->_is_running) + cx25821_stop_upstream_video_ch1(dev); + + if (dev->_dma_virt_addr) { + pci_free_consistent(dev->pci, dev->_risc_size, + dev->_dma_virt_addr, dev->_dma_phys_addr); + dev->_dma_virt_addr = NULL; + } + + if (dev->_data_buf_virt_addr) { + pci_free_consistent(dev->pci, dev->_data_buf_size, + dev->_data_buf_virt_addr, + dev->_data_buf_phys_addr); + dev->_data_buf_virt_addr = NULL; + } +} + +int cx25821_get_frame(struct cx25821_dev *dev, struct sram_channel *sram_ch) +{ + struct file *myfile; + int frame_index_temp = dev->_frame_index; + int i = 0; + int line_size = + (dev->_pixel_format == + PIXEL_FRMT_411) ? Y411_LINE_SZ : Y422_LINE_SZ; + int frame_size = 0; + int frame_offset = 0; + ssize_t vfs_read_retval = 0; + char mybuf[line_size]; + loff_t file_offset; + loff_t pos; + mm_segment_t old_fs; + + if (dev->_file_status == END_OF_FILE) + return 0; + + if (dev->_isNTSC) { + frame_size = + (line_size == + Y411_LINE_SZ) ? FRAME_SIZE_NTSC_Y411 : + FRAME_SIZE_NTSC_Y422; + } else { + frame_size = + (line_size == + Y411_LINE_SZ) ? FRAME_SIZE_PAL_Y411 : FRAME_SIZE_PAL_Y422; + } + + frame_offset = (frame_index_temp > 0) ? frame_size : 0; + file_offset = dev->_frame_count * frame_size; + + myfile = filp_open(dev->_filename, O_RDONLY | O_LARGEFILE, 0); + + if (IS_ERR(myfile)) { + const int open_errno = -PTR_ERR(myfile); + pr_err("%s(): ERROR opening file(%s) with errno = %d!\n", + __func__, dev->_filename, open_errno); + return PTR_ERR(myfile); + } else { + if (!(myfile->f_op)) { + pr_err("%s(): File has no file operations registered!\n", + __func__); + filp_close(myfile, NULL); + return -EIO; + } + + if (!myfile->f_op->read) { + pr_err("%s(): File has no READ operations registered!\n", + __func__); + filp_close(myfile, NULL); + return -EIO; + } + + pos = myfile->f_pos; + old_fs = get_fs(); + set_fs(KERNEL_DS); + + for (i = 0; i < dev->_lines_count; i++) { + pos = file_offset; + + vfs_read_retval = + vfs_read(myfile, mybuf, line_size, &pos); + + if (vfs_read_retval > 0 && vfs_read_retval == line_size + && dev->_data_buf_virt_addr != NULL) { + memcpy((void *)(dev->_data_buf_virt_addr + + frame_offset / 4), mybuf, + vfs_read_retval); + } + + file_offset += vfs_read_retval; + frame_offset += vfs_read_retval; + + if (vfs_read_retval < line_size) { + pr_info("Done: exit %s() since no more bytes to read from Video file\n", + __func__); + break; + } + } + + if (i > 0) + dev->_frame_count++; + + dev->_file_status = + (vfs_read_retval == line_size) ? IN_PROGRESS : END_OF_FILE; + + set_fs(old_fs); + filp_close(myfile, NULL); + } + + return 0; +} + +static void cx25821_vidups_handler(struct work_struct *work) +{ + struct cx25821_dev *dev = + container_of(work, struct cx25821_dev, _irq_work_entry); + + if (!dev) { + pr_err("ERROR %s(): since container_of(work_struct) FAILED!\n", + __func__); + return; + } + + cx25821_get_frame(dev, + dev->channels[dev->_channel_upstream_select]. + sram_channels); +} + +int cx25821_openfile(struct cx25821_dev *dev, struct sram_channel *sram_ch) +{ + struct file *myfile; + int i = 0, j = 0; + int line_size = + (dev->_pixel_format == + PIXEL_FRMT_411) ? Y411_LINE_SZ : Y422_LINE_SZ; + ssize_t vfs_read_retval = 0; + char mybuf[line_size]; + loff_t pos; + loff_t offset = (unsigned long)0; + mm_segment_t old_fs; + + myfile = filp_open(dev->_filename, O_RDONLY | O_LARGEFILE, 0); + + if (IS_ERR(myfile)) { + const int open_errno = -PTR_ERR(myfile); + pr_err("%s(): ERROR opening file(%s) with errno = %d!\n", + __func__, dev->_filename, open_errno); + return PTR_ERR(myfile); + } else { + if (!(myfile->f_op)) { + pr_err("%s(): File has no file operations registered!\n", + __func__); + filp_close(myfile, NULL); + return -EIO; + } + + if (!myfile->f_op->read) { + pr_err("%s(): File has no READ operations registered! Returning\n", + __func__); + filp_close(myfile, NULL); + return -EIO; + } + + pos = myfile->f_pos; + old_fs = get_fs(); + set_fs(KERNEL_DS); + + for (j = 0; j < NUM_FRAMES; j++) { + for (i = 0; i < dev->_lines_count; i++) { + pos = offset; + + vfs_read_retval = + vfs_read(myfile, mybuf, line_size, &pos); + + if (vfs_read_retval > 0 + && vfs_read_retval == line_size + && dev->_data_buf_virt_addr != NULL) { + memcpy((void *)(dev-> + _data_buf_virt_addr + + offset / 4), mybuf, + vfs_read_retval); + } + + offset += vfs_read_retval; + + if (vfs_read_retval < line_size) { + pr_info("Done: exit %s() since no more bytes to read from Video file\n", + __func__); + break; + } + } + + if (i > 0) + dev->_frame_count++; + + if (vfs_read_retval < line_size) + break; + } + + dev->_file_status = + (vfs_read_retval == line_size) ? IN_PROGRESS : END_OF_FILE; + + set_fs(old_fs); + myfile->f_pos = 0; + filp_close(myfile, NULL); + } + + return 0; +} + +int cx25821_upstream_buffer_prepare(struct cx25821_dev *dev, + struct sram_channel *sram_ch, int bpl) +{ + int ret = 0; + dma_addr_t dma_addr; + dma_addr_t data_dma_addr; + + if (dev->_dma_virt_addr != NULL) { + pci_free_consistent(dev->pci, dev->upstream_riscbuf_size, + dev->_dma_virt_addr, dev->_dma_phys_addr); + } + + dev->_dma_virt_addr = + pci_alloc_consistent(dev->pci, dev->upstream_riscbuf_size, + &dma_addr); + dev->_dma_virt_start_addr = dev->_dma_virt_addr; + dev->_dma_phys_start_addr = dma_addr; + dev->_dma_phys_addr = dma_addr; + dev->_risc_size = dev->upstream_riscbuf_size; + + if (!dev->_dma_virt_addr) { + pr_err("FAILED to allocate memory for Risc buffer! Returning\n"); + return -ENOMEM; + } + + /* Clear memory at address */ + memset(dev->_dma_virt_addr, 0, dev->_risc_size); + + if (dev->_data_buf_virt_addr != NULL) { + pci_free_consistent(dev->pci, dev->upstream_databuf_size, + dev->_data_buf_virt_addr, + dev->_data_buf_phys_addr); + } + /* For Video Data buffer allocation */ + dev->_data_buf_virt_addr = + pci_alloc_consistent(dev->pci, dev->upstream_databuf_size, + &data_dma_addr); + dev->_data_buf_phys_addr = data_dma_addr; + dev->_data_buf_size = dev->upstream_databuf_size; + + if (!dev->_data_buf_virt_addr) { + pr_err("FAILED to allocate memory for data buffer! Returning\n"); + return -ENOMEM; + } + + /* Clear memory at address */ + memset(dev->_data_buf_virt_addr, 0, dev->_data_buf_size); + + ret = cx25821_openfile(dev, sram_ch); + if (ret < 0) + return ret; + + /* Create RISC programs */ + ret = + cx25821_risc_buffer_upstream(dev, dev->pci, 0, bpl, + dev->_lines_count); + if (ret < 0) { + pr_info("Failed creating Video Upstream Risc programs!\n"); + goto error; + } + + return 0; + +error: + return ret; +} + +int cx25821_video_upstream_irq(struct cx25821_dev *dev, int chan_num, + u32 status) +{ + u32 int_msk_tmp; + struct sram_channel *channel = dev->channels[chan_num].sram_channels; + int singlefield_lines = NTSC_FIELD_HEIGHT; + int line_size_in_bytes = Y422_LINE_SZ; + int odd_risc_prog_size = 0; + dma_addr_t risc_phys_jump_addr; + __le32 *rp; + + if (status & FLD_VID_SRC_RISC1) { + /* We should only process one program per call */ + u32 prog_cnt = cx_read(channel->gpcnt); + + /* Since we've identified our IRQ, clear our bits from the + * interrupt mask and interrupt status registers */ + int_msk_tmp = cx_read(channel->int_msk); + cx_write(channel->int_msk, int_msk_tmp & ~_intr_msk); + cx_write(channel->int_stat, _intr_msk); + + spin_lock(&dev->slock); + + dev->_frame_index = prog_cnt; + + queue_work(dev->_irq_queues, &dev->_irq_work_entry); + + if (dev->_is_first_frame) { + dev->_is_first_frame = 0; + + if (dev->_isNTSC) { + singlefield_lines += 1; + odd_risc_prog_size = ODD_FLD_NTSC_PROG_SIZE; + } else { + singlefield_lines = PAL_FIELD_HEIGHT; + odd_risc_prog_size = ODD_FLD_PAL_PROG_SIZE; + } + + if (dev->_dma_virt_start_addr != NULL) { + line_size_in_bytes = + (dev->_pixel_format == + PIXEL_FRMT_411) ? Y411_LINE_SZ : + Y422_LINE_SZ; + risc_phys_jump_addr = + dev->_dma_phys_start_addr + + odd_risc_prog_size; + + rp = cx25821_update_riscprogram(dev, + dev->_dma_virt_start_addr, TOP_OFFSET, + line_size_in_bytes, 0x0, + singlefield_lines, FIFO_DISABLE, + ODD_FIELD); + + /* Jump to Even Risc program of 1st Frame */ + *(rp++) = cpu_to_le32(RISC_JUMP); + *(rp++) = cpu_to_le32(risc_phys_jump_addr); + *(rp++) = cpu_to_le32(0); + } + } + + spin_unlock(&dev->slock); + } else { + if (status & FLD_VID_SRC_UF) + pr_err("%s(): Video Received Underflow Error Interrupt!\n", + __func__); + + if (status & FLD_VID_SRC_SYNC) + pr_err("%s(): Video Received Sync Error Interrupt!\n", + __func__); + + if (status & FLD_VID_SRC_OPC_ERR) + pr_err("%s(): Video Received OpCode Error Interrupt!\n", + __func__); + } + + if (dev->_file_status == END_OF_FILE) { + pr_err("EOF Channel 1 Framecount = %d\n", dev->_frame_count); + return -1; + } + /* ElSE, set the interrupt mask register, re-enable irq. */ + int_msk_tmp = cx_read(channel->int_msk); + cx_write(channel->int_msk, int_msk_tmp |= _intr_msk); + + return 0; +} + +static irqreturn_t cx25821_upstream_irq(int irq, void *dev_id) +{ + struct cx25821_dev *dev = dev_id; + u32 msk_stat, vid_status; + int handled = 0; + int channel_num = 0; + struct sram_channel *sram_ch; + + if (!dev) + return -1; + + channel_num = VID_UPSTREAM_SRAM_CHANNEL_I; + + sram_ch = dev->channels[channel_num].sram_channels; + + msk_stat = cx_read(sram_ch->int_mstat); + vid_status = cx_read(sram_ch->int_stat); + + /* Only deal with our interrupt */ + if (vid_status) { + handled = + cx25821_video_upstream_irq(dev, channel_num, vid_status); + } + + if (handled < 0) + cx25821_stop_upstream_video_ch1(dev); + else + handled += handled; + + return IRQ_RETVAL(handled); +} + +void cx25821_set_pixelengine(struct cx25821_dev *dev, struct sram_channel *ch, + int pix_format) +{ + int width = WIDTH_D1; + int height = dev->_lines_count; + int num_lines, odd_num_lines; + u32 value; + int vip_mode = OUTPUT_FRMT_656; + + value = ((pix_format & 0x3) << 12) | (vip_mode & 0x7); + value &= 0xFFFFFFEF; + value |= dev->_isNTSC ? 0 : 0x10; + cx_write(ch->vid_fmt_ctl, value); + + /* set number of active pixels in each line. + * Default is 720 pixels in both NTSC and PAL format */ + cx_write(ch->vid_active_ctl1, width); + + num_lines = (height / 2) & 0x3FF; + odd_num_lines = num_lines; + + if (dev->_isNTSC) + odd_num_lines += 1; + + value = (num_lines << 16) | odd_num_lines; + + /* set number of active lines in field 0 (top) and field 1 (bottom) */ + cx_write(ch->vid_active_ctl2, value); + + cx_write(ch->vid_cdt_size, VID_CDT_SIZE >> 3); +} + +int cx25821_start_video_dma_upstream(struct cx25821_dev *dev, + struct sram_channel *sram_ch) +{ + u32 tmp = 0; + int err = 0; + + /* 656/VIP SRC Upstream Channel I & J and 7 - Host Bus Interface for + * channel A-C + */ + tmp = cx_read(VID_CH_MODE_SEL); + cx_write(VID_CH_MODE_SEL, tmp | 0x1B0001FF); + + /* Set the physical start address of the RISC program in the initial + * program counter(IPC) member of the cmds. + */ + cx_write(sram_ch->cmds_start + 0, dev->_dma_phys_addr); + /* Risc IPC High 64 bits 63-32 */ + cx_write(sram_ch->cmds_start + 4, 0); + + /* reset counter */ + cx_write(sram_ch->gpcnt_ctl, 3); + + /* Clear our bits from the interrupt status register. */ + cx_write(sram_ch->int_stat, _intr_msk); + + /* Set the interrupt mask register, enable irq. */ + cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | (1 << sram_ch->irq_bit)); + tmp = cx_read(sram_ch->int_msk); + cx_write(sram_ch->int_msk, tmp |= _intr_msk); + + err = + request_irq(dev->pci->irq, cx25821_upstream_irq, + IRQF_SHARED, dev->name, dev); + if (err < 0) { + pr_err("%s: can't get upstream IRQ %d\n", + dev->name, dev->pci->irq); + goto fail_irq; + } + + /* Start the DMA engine */ + tmp = cx_read(sram_ch->dma_ctl); + cx_set(sram_ch->dma_ctl, tmp | FLD_VID_RISC_EN); + + dev->_is_running = 1; + dev->_is_first_frame = 1; + + return 0; + +fail_irq: + cx25821_dev_unregister(dev); + return err; +} + +int cx25821_vidupstream_init_ch1(struct cx25821_dev *dev, int channel_select, + int pixel_format) +{ + struct sram_channel *sram_ch; + u32 tmp; + int retval = 0; + int err = 0; + int data_frame_size = 0; + int risc_buffer_size = 0; + int str_length = 0; + + if (dev->_is_running) { + pr_info("Video Channel is still running so return!\n"); + return 0; + } + + dev->_channel_upstream_select = channel_select; + sram_ch = dev->channels[channel_select].sram_channels; + + INIT_WORK(&dev->_irq_work_entry, cx25821_vidups_handler); + dev->_irq_queues = create_singlethread_workqueue("cx25821_workqueue"); + + if (!dev->_irq_queues) { + pr_err("create_singlethread_workqueue() for Video FAILED!\n"); + return -ENOMEM; + } + /* 656/VIP SRC Upstream Channel I & J and 7 - Host Bus Interface for + * channel A-C + */ + tmp = cx_read(VID_CH_MODE_SEL); + cx_write(VID_CH_MODE_SEL, tmp | 0x1B0001FF); + + dev->_is_running = 0; + dev->_frame_count = 0; + dev->_file_status = RESET_STATUS; + dev->_lines_count = dev->_isNTSC ? 480 : 576; + dev->_pixel_format = pixel_format; + dev->_line_size = + (dev->_pixel_format == + PIXEL_FRMT_422) ? (WIDTH_D1 * 2) : (WIDTH_D1 * 3) / 2; + data_frame_size = dev->_isNTSC ? NTSC_DATA_BUF_SZ : PAL_DATA_BUF_SZ; + risc_buffer_size = + dev->_isNTSC ? NTSC_RISC_BUF_SIZE : PAL_RISC_BUF_SIZE; + + if (dev->input_filename) { + str_length = strlen(dev->input_filename); + dev->_filename = kmalloc(str_length + 1, GFP_KERNEL); + + if (!dev->_filename) + goto error; + + memcpy(dev->_filename, dev->input_filename, str_length + 1); + } else { + str_length = strlen(dev->_defaultname); + dev->_filename = kmalloc(str_length + 1, GFP_KERNEL); + + if (!dev->_filename) + goto error; + + memcpy(dev->_filename, dev->_defaultname, str_length + 1); + } + + /* Default if filename is empty string */ + if (strcmp(dev->input_filename, "") == 0) { + if (dev->_isNTSC) { + dev->_filename = + (dev->_pixel_format == + PIXEL_FRMT_411) ? "/root/vid411.yuv" : + "/root/vidtest.yuv"; + } else { + dev->_filename = + (dev->_pixel_format == + PIXEL_FRMT_411) ? "/root/pal411.yuv" : + "/root/pal422.yuv"; + } + } + + dev->_is_running = 0; + dev->_frame_count = 0; + dev->_file_status = RESET_STATUS; + dev->_lines_count = dev->_isNTSC ? 480 : 576; + dev->_pixel_format = pixel_format; + dev->_line_size = + (dev->_pixel_format == + PIXEL_FRMT_422) ? (WIDTH_D1 * 2) : (WIDTH_D1 * 3) / 2; + + retval = + cx25821_sram_channel_setup_upstream(dev, sram_ch, dev->_line_size, + 0); + + /* setup fifo + format */ + cx25821_set_pixelengine(dev, sram_ch, dev->_pixel_format); + + dev->upstream_riscbuf_size = risc_buffer_size * 2; + dev->upstream_databuf_size = data_frame_size * 2; + + /* Allocating buffers and prepare RISC program */ + retval = cx25821_upstream_buffer_prepare(dev, sram_ch, dev->_line_size); + if (retval < 0) { + pr_err("%s: Failed to set up Video upstream buffers!\n", + dev->name); + goto error; + } + + cx25821_start_video_dma_upstream(dev, sram_ch); + + return 0; + +error: + cx25821_dev_unregister(dev); + + return err; +} diff --git a/drivers/media/video/cx25821/cx25821-video-upstream.h b/drivers/media/video/cx25821/cx25821-video-upstream.h new file mode 100644 index 00000000000..268ec8aa6a6 --- /dev/null +++ b/drivers/media/video/cx25821/cx25821-video-upstream.h @@ -0,0 +1,139 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <hiep.huynh@conexant.com>, <shu.lin@conexant.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/mutex.h> +#include <linux/workqueue.h> + +#define OUTPUT_FRMT_656 0 +#define OPEN_FILE_1 0 +#define NUM_PROGS 8 +#define NUM_FRAMES 2 +#define ODD_FIELD 0 +#define EVEN_FIELD 1 +#define TOP_OFFSET 0 +#define FIFO_DISABLE 0 +#define FIFO_ENABLE 1 +#define TEST_FRAMES 5 +#define END_OF_FILE 0 +#define IN_PROGRESS 1 +#define RESET_STATUS -1 +#define NUM_NO_OPS 5 + +/* PAL and NTSC line sizes and number of lines. */ +#define WIDTH_D1 720 +#define NTSC_LINES_PER_FRAME 480 +#define PAL_LINES_PER_FRAME 576 +#define PAL_LINE_SZ 1440 +#define Y422_LINE_SZ 1440 +#define Y411_LINE_SZ 1080 +#define NTSC_FIELD_HEIGHT 240 +#define NTSC_ODD_FLD_LINES 241 +#define PAL_FIELD_HEIGHT 288 + +#define FRAME_SIZE_NTSC_Y422 (NTSC_LINES_PER_FRAME * Y422_LINE_SZ) +#define FRAME_SIZE_NTSC_Y411 (NTSC_LINES_PER_FRAME * Y411_LINE_SZ) +#define FRAME_SIZE_PAL_Y422 (PAL_LINES_PER_FRAME * Y422_LINE_SZ) +#define FRAME_SIZE_PAL_Y411 (PAL_LINES_PER_FRAME * Y411_LINE_SZ) + +#define NTSC_DATA_BUF_SZ (Y422_LINE_SZ * NTSC_LINES_PER_FRAME) +#define PAL_DATA_BUF_SZ (Y422_LINE_SZ * PAL_LINES_PER_FRAME) + +#define RISC_WRITECR_INSTRUCTION_SIZE 16 +#define RISC_SYNC_INSTRUCTION_SIZE 4 +#define JUMP_INSTRUCTION_SIZE 12 +#define MAXSIZE_NO_OPS 36 +#define DWORD_SIZE 4 + +#define USE_RISC_NOOP_VIDEO 1 + +#ifdef USE_RISC_NOOP_VIDEO +#define PAL_US_VID_PROG_SIZE \ + (PAL_FIELD_HEIGHT * 3 * DWORD_SIZE + \ + RISC_WRITECR_INSTRUCTION_SIZE + RISC_SYNC_INSTRUCTION_SIZE + \ + NUM_NO_OPS * DWORD_SIZE) + +#define PAL_RISC_BUF_SIZE (2 * PAL_US_VID_PROG_SIZE) + +#define PAL_VID_PROG_SIZE \ + ((PAL_FIELD_HEIGHT * 2) * 3 * DWORD_SIZE + \ + 2 * RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \ + JUMP_INSTRUCTION_SIZE + 2 * NUM_NO_OPS * DWORD_SIZE) + +#define ODD_FLD_PAL_PROG_SIZE \ + (PAL_FIELD_HEIGHT * 3 * DWORD_SIZE + \ + RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \ + NUM_NO_OPS * DWORD_SIZE) + +#define ODD_FLD_NTSC_PROG_SIZE \ + (NTSC_ODD_FLD_LINES * 3 * DWORD_SIZE + \ + RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \ + NUM_NO_OPS * DWORD_SIZE) + +#define NTSC_US_VID_PROG_SIZE \ + ((NTSC_ODD_FLD_LINES + 1) * 3 * DWORD_SIZE + \ + RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE + \ + NUM_NO_OPS * DWORD_SIZE) + +#define NTSC_RISC_BUF_SIZE \ + (2 * (RISC_SYNC_INSTRUCTION_SIZE + NTSC_US_VID_PROG_SIZE)) + +#define FRAME1_VID_PROG_SIZE \ + ((NTSC_ODD_FLD_LINES + NTSC_FIELD_HEIGHT) * 3 * DWORD_SIZE + \ + 2 * RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \ + JUMP_INSTRUCTION_SIZE + 2 * NUM_NO_OPS * DWORD_SIZE) + +#endif + +#ifndef USE_RISC_NOOP_VIDEO +#define PAL_US_VID_PROG_SIZE \ + (PAL_FIELD_HEIGHT * 3 * DWORD_SIZE + \ + RISC_WRITECR_INSTRUCTION_SIZE + RISC_SYNC_INSTRUCTION_SIZE + \ + JUMP_INSTRUCTION_SIZE) + +#define PAL_RISC_BUF_SIZE (2 * PAL_US_VID_PROG_SIZE) + +#define PAL_VID_PROG_SIZE \ + ((PAL_FIELD_HEIGHT * 2) * 3 * DWORD_SIZE + \ + 2 * RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \ + JUMP_INSTRUCTION_SIZE) + +#define ODD_FLD_PAL_PROG_SIZE \ + (PAL_FIELD_HEIGHT * 3 * DWORD_SIZE + \ + RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE) + +#define ODD_FLD_NTSC_PROG_SIZE \ + (NTSC_ODD_FLD_LINES * 3 * DWORD_SIZE + \ + RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE) + +#define NTSC_US_VID_PROG_SIZE \ + ((NTSC_ODD_FLD_LINES + 1) * 3 * DWORD_SIZE + \ + RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE) + +#define NTSC_RISC_BUF_SIZE \ + (2 * (RISC_SYNC_INSTRUCTION_SIZE + NTSC_US_VID_PROG_SIZE)) + +#define FRAME1_VID_PROG_SIZE \ + ((NTSC_ODD_FLD_LINES + NTSC_FIELD_HEIGHT) * 3 * DWORD_SIZE + \ + 2 * RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \ + JUMP_INSTRUCTION_SIZE) + +#endif diff --git a/drivers/media/video/cx25821/cx25821-video.c b/drivers/media/video/cx25821/cx25821-video.c new file mode 100644 index 00000000000..4d6907cda75 --- /dev/null +++ b/drivers/media/video/cx25821/cx25821-video.c @@ -0,0 +1,2012 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> + * Based on Steven Toth <stoth@linuxtv.org> cx23885 driver + * Parts adapted/taken from Eduardo Moscoso Rubino + * Copyright (C) 2009 Eduardo Moscoso Rubino <moscoso@TopoLogica.com> + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "cx25821-video.h" + +MODULE_DESCRIPTION("v4l2 driver module for cx25821 based TV cards"); +MODULE_AUTHOR("Hiep Huynh <hiep.huynh@conexant.com>"); +MODULE_LICENSE("GPL"); + +static unsigned int video_nr[] = {[0 ... (CX25821_MAXBOARDS - 1)] = UNSET }; +static unsigned int radio_nr[] = {[0 ... (CX25821_MAXBOARDS - 1)] = UNSET }; + +module_param_array(video_nr, int, NULL, 0444); +module_param_array(radio_nr, int, NULL, 0444); + +MODULE_PARM_DESC(video_nr, "video device numbers"); +MODULE_PARM_DESC(radio_nr, "radio device numbers"); + +static unsigned int video_debug = VIDEO_DEBUG; +module_param(video_debug, int, 0644); +MODULE_PARM_DESC(video_debug, "enable debug messages [video]"); + +static unsigned int irq_debug; +module_param(irq_debug, int, 0644); +MODULE_PARM_DESC(irq_debug, "enable debug messages [IRQ handler]"); + +unsigned int vid_limit = 16; +module_param(vid_limit, int, 0644); +MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes"); + +static void cx25821_init_controls(struct cx25821_dev *dev, int chan_num); + +static const struct v4l2_file_operations video_fops; +static const struct v4l2_ioctl_ops video_ioctl_ops; + +#define FORMAT_FLAGS_PACKED 0x01 + +struct cx25821_fmt formats[] = { + { + .name = "8 bpp, gray", + .fourcc = V4L2_PIX_FMT_GREY, + .depth = 8, + .flags = FORMAT_FLAGS_PACKED, + }, { + .name = "4:1:1, packed, Y41P", + .fourcc = V4L2_PIX_FMT_Y41P, + .depth = 12, + .flags = FORMAT_FLAGS_PACKED, + }, { + .name = "4:2:2, packed, YUYV", + .fourcc = V4L2_PIX_FMT_YUYV, + .depth = 16, + .flags = FORMAT_FLAGS_PACKED, + }, { + .name = "4:2:2, packed, UYVY", + .fourcc = V4L2_PIX_FMT_UYVY, + .depth = 16, + .flags = FORMAT_FLAGS_PACKED, + }, { + .name = "4:2:0, YUV", + .fourcc = V4L2_PIX_FMT_YUV420, + .depth = 12, + .flags = FORMAT_FLAGS_PACKED, + }, +}; + +int cx25821_get_format_size(void) +{ + return ARRAY_SIZE(formats); +} + +struct cx25821_fmt *cx25821_format_by_fourcc(unsigned int fourcc) +{ + unsigned int i; + + if (fourcc == V4L2_PIX_FMT_Y41P || fourcc == V4L2_PIX_FMT_YUV411P) + return formats + 1; + + for (i = 0; i < ARRAY_SIZE(formats); i++) + if (formats[i].fourcc == fourcc) + return formats + i; + + pr_err("%s(0x%08x) NOT FOUND\n", __func__, fourcc); + return NULL; +} + +void cx25821_dump_video_queue(struct cx25821_dev *dev, + struct cx25821_dmaqueue *q) +{ + struct cx25821_buffer *buf; + struct list_head *item; + dprintk(1, "%s()\n", __func__); + + if (!list_empty(&q->active)) { + list_for_each(item, &q->active) + buf = list_entry(item, struct cx25821_buffer, vb.queue); + } + + if (!list_empty(&q->queued)) { + list_for_each(item, &q->queued) + buf = list_entry(item, struct cx25821_buffer, vb.queue); + } + +} + +void cx25821_video_wakeup(struct cx25821_dev *dev, struct cx25821_dmaqueue *q, + u32 count) +{ + struct cx25821_buffer *buf; + int bc; + + for (bc = 0;; bc++) { + if (list_empty(&q->active)) { + dprintk(1, "bc=%d (=0: active empty)\n", bc); + break; + } + + buf = + list_entry(q->active.next, struct cx25821_buffer, vb.queue); + + /* count comes from the hw and it is 16bit wide -- + * this trick handles wrap-arounds correctly for + * up to 32767 buffers in flight... */ + if ((s16) (count - buf->count) < 0) + break; + + do_gettimeofday(&buf->vb.ts); + buf->vb.state = VIDEOBUF_DONE; + list_del(&buf->vb.queue); + wake_up(&buf->vb.done); + } + + if (list_empty(&q->active)) + del_timer(&q->timeout); + else + mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); + if (bc != 1) + pr_err("%s: %d buffers handled (should be 1)\n", __func__, bc); +} + +#ifdef TUNER_FLAG +int cx25821_set_tvnorm(struct cx25821_dev *dev, v4l2_std_id norm) +{ + dprintk(1, "%s(norm = 0x%08x) name: [%s]\n", + __func__, (unsigned int)norm, v4l2_norm_to_name(norm)); + + dev->tvnorm = norm; + + /* Tell the internal A/V decoder */ + cx25821_call_all(dev, core, s_std, norm); + + return 0; +} +#endif + +struct video_device *cx25821_vdev_init(struct cx25821_dev *dev, + struct pci_dev *pci, + struct video_device *template, + char *type) +{ + struct video_device *vfd; + dprintk(1, "%s()\n", __func__); + + vfd = video_device_alloc(); + if (NULL == vfd) + return NULL; + *vfd = *template; + vfd->v4l2_dev = &dev->v4l2_dev; + vfd->release = video_device_release; + snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", dev->name, type, + cx25821_boards[dev->board].name); + video_set_drvdata(vfd, dev); + return vfd; +} + +/* +static int cx25821_ctrl_query(struct v4l2_queryctrl *qctrl) +{ + int i; + + if (qctrl->id < V4L2_CID_BASE || qctrl->id >= V4L2_CID_LASTP1) + return -EINVAL; + for (i = 0; i < CX25821_CTLS; i++) + if (cx25821_ctls[i].v.id == qctrl->id) + break; + if (i == CX25821_CTLS) { + *qctrl = no_ctl; + return 0; + } + *qctrl = cx25821_ctls[i].v; + return 0; +} +*/ + +/* resource management */ +int cx25821_res_get(struct cx25821_dev *dev, struct cx25821_fh *fh, + unsigned int bit) +{ + dprintk(1, "%s()\n", __func__); + if (fh->resources & bit) + /* have it already allocated */ + return 1; + + /* is it free? */ + mutex_lock(&dev->lock); + if (dev->channels[fh->channel_id].resources & bit) { + /* no, someone else uses it */ + mutex_unlock(&dev->lock); + return 0; + } + /* it's free, grab it */ + fh->resources |= bit; + dev->channels[fh->channel_id].resources |= bit; + dprintk(1, "res: get %d\n", bit); + mutex_unlock(&dev->lock); + return 1; +} + +int cx25821_res_check(struct cx25821_fh *fh, unsigned int bit) +{ + return fh->resources & bit; +} + +int cx25821_res_locked(struct cx25821_fh *fh, unsigned int bit) +{ + return fh->dev->channels[fh->channel_id].resources & bit; +} + +void cx25821_res_free(struct cx25821_dev *dev, struct cx25821_fh *fh, + unsigned int bits) +{ + BUG_ON((fh->resources & bits) != bits); + dprintk(1, "%s()\n", __func__); + + mutex_lock(&dev->lock); + fh->resources &= ~bits; + dev->channels[fh->channel_id].resources &= ~bits; + dprintk(1, "res: put %d\n", bits); + mutex_unlock(&dev->lock); +} + +int cx25821_video_mux(struct cx25821_dev *dev, unsigned int input) +{ + struct v4l2_routing route; + memset(&route, 0, sizeof(route)); + + dprintk(1, "%s(): video_mux: %d [vmux=%d, gpio=0x%x,0x%x,0x%x,0x%x]\n", + __func__, input, INPUT(input)->vmux, INPUT(input)->gpio0, + INPUT(input)->gpio1, INPUT(input)->gpio2, INPUT(input)->gpio3); + dev->input = input; + + route.input = INPUT(input)->vmux; + + /* Tell the internal A/V decoder */ + cx25821_call_all(dev, video, s_routing, INPUT(input)->vmux, 0, 0); + + return 0; +} + +int cx25821_start_video_dma(struct cx25821_dev *dev, + struct cx25821_dmaqueue *q, + struct cx25821_buffer *buf, + struct sram_channel *channel) +{ + int tmp = 0; + + /* setup fifo + format */ + cx25821_sram_channel_setup(dev, channel, buf->bpl, buf->risc.dma); + + /* reset counter */ + cx_write(channel->gpcnt_ctl, 3); + q->count = 1; + + /* enable irq */ + cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | (1 << channel->i)); + cx_set(channel->int_msk, 0x11); + + /* start dma */ + cx_write(channel->dma_ctl, 0x11); /* FIFO and RISC enable */ + + /* make sure upstream setting if any is reversed */ + tmp = cx_read(VID_CH_MODE_SEL); + cx_write(VID_CH_MODE_SEL, tmp & 0xFFFFFE00); + + return 0; +} + +int cx25821_restart_video_queue(struct cx25821_dev *dev, + struct cx25821_dmaqueue *q, + struct sram_channel *channel) +{ + struct cx25821_buffer *buf, *prev; + struct list_head *item; + + if (!list_empty(&q->active)) { + buf = + list_entry(q->active.next, struct cx25821_buffer, vb.queue); + + cx25821_start_video_dma(dev, q, buf, channel); + + list_for_each(item, &q->active) { + buf = list_entry(item, struct cx25821_buffer, vb.queue); + buf->count = q->count++; + } + + mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); + return 0; + } + + prev = NULL; + for (;;) { + if (list_empty(&q->queued)) + return 0; + + buf = + list_entry(q->queued.next, struct cx25821_buffer, vb.queue); + + if (NULL == prev) { + list_move_tail(&buf->vb.queue, &q->active); + cx25821_start_video_dma(dev, q, buf, channel); + buf->vb.state = VIDEOBUF_ACTIVE; + buf->count = q->count++; + mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); + } else if (prev->vb.width == buf->vb.width && + prev->vb.height == buf->vb.height && + prev->fmt == buf->fmt) { + list_move_tail(&buf->vb.queue, &q->active); + buf->vb.state = VIDEOBUF_ACTIVE; + buf->count = q->count++; + prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); + prev->risc.jmp[2] = cpu_to_le32(0); /* Bits 63 - 32 */ + } else { + return 0; + } + prev = buf; + } +} + +void cx25821_vid_timeout(unsigned long data) +{ + struct cx25821_data *timeout_data = (struct cx25821_data *)data; + struct cx25821_dev *dev = timeout_data->dev; + struct sram_channel *channel = timeout_data->channel; + struct cx25821_dmaqueue *q = &dev->channels[channel->i].vidq; + struct cx25821_buffer *buf; + unsigned long flags; + + /* cx25821_sram_channel_dump(dev, channel); */ + cx_clear(channel->dma_ctl, 0x11); + + spin_lock_irqsave(&dev->slock, flags); + while (!list_empty(&q->active)) { + buf = + list_entry(q->active.next, struct cx25821_buffer, vb.queue); + list_del(&buf->vb.queue); + + buf->vb.state = VIDEOBUF_ERROR; + wake_up(&buf->vb.done); + } + + cx25821_restart_video_queue(dev, q, channel); + spin_unlock_irqrestore(&dev->slock, flags); +} + +int cx25821_video_irq(struct cx25821_dev *dev, int chan_num, u32 status) +{ + u32 count = 0; + int handled = 0; + u32 mask; + struct sram_channel *channel = dev->channels[chan_num].sram_channels; + + mask = cx_read(channel->int_msk); + if (0 == (status & mask)) + return handled; + + cx_write(channel->int_stat, status); + + /* risc op code error */ + if (status & (1 << 16)) { + pr_warn("%s, %s: video risc op code error\n", + dev->name, channel->name); + cx_clear(channel->dma_ctl, 0x11); + cx25821_sram_channel_dump(dev, channel); + } + + /* risc1 y */ + if (status & FLD_VID_DST_RISC1) { + spin_lock(&dev->slock); + count = cx_read(channel->gpcnt); + cx25821_video_wakeup(dev, &dev->channels[channel->i].vidq, + count); + spin_unlock(&dev->slock); + handled++; + } + + /* risc2 y */ + if (status & 0x10) { + dprintk(2, "stopper video\n"); + spin_lock(&dev->slock); + cx25821_restart_video_queue(dev, + &dev->channels[channel->i].vidq, channel); + spin_unlock(&dev->slock); + handled++; + } + return handled; +} + +void cx25821_videoioctl_unregister(struct cx25821_dev *dev) +{ + if (dev->ioctl_dev) { + if (video_is_registered(dev->ioctl_dev)) + video_unregister_device(dev->ioctl_dev); + else + video_device_release(dev->ioctl_dev); + + dev->ioctl_dev = NULL; + } +} + +void cx25821_video_unregister(struct cx25821_dev *dev, int chan_num) +{ + cx_clear(PCI_INT_MSK, 1); + + if (dev->channels[chan_num].video_dev) { + if (video_is_registered(dev->channels[chan_num].video_dev)) + video_unregister_device( + dev->channels[chan_num].video_dev); + else + video_device_release( + dev->channels[chan_num].video_dev); + + dev->channels[chan_num].video_dev = NULL; + + btcx_riscmem_free(dev->pci, + &dev->channels[chan_num].vidq.stopper); + + pr_warn("device %d released!\n", chan_num); + } + +} + +int cx25821_video_register(struct cx25821_dev *dev) +{ + int err; + int i; + + struct video_device cx25821_video_device = { + .name = "cx25821-video", + .fops = &video_fops, + .minor = -1, + .ioctl_ops = &video_ioctl_ops, + .tvnorms = CX25821_NORMS, + .current_norm = V4L2_STD_NTSC_M, + }; + + spin_lock_init(&dev->slock); + + for (i = 0; i < MAX_VID_CHANNEL_NUM - 1; ++i) { + cx25821_init_controls(dev, i); + + cx25821_risc_stopper(dev->pci, &dev->channels[i].vidq.stopper, + dev->channels[i].sram_channels->dma_ctl, + 0x11, 0); + + dev->channels[i].sram_channels = &cx25821_sram_channels[i]; + dev->channels[i].video_dev = NULL; + dev->channels[i].resources = 0; + + cx_write(dev->channels[i].sram_channels->int_stat, 0xffffffff); + + INIT_LIST_HEAD(&dev->channels[i].vidq.active); + INIT_LIST_HEAD(&dev->channels[i].vidq.queued); + + dev->channels[i].timeout_data.dev = dev; + dev->channels[i].timeout_data.channel = + &cx25821_sram_channels[i]; + dev->channels[i].vidq.timeout.function = + cx25821_vid_timeout; + dev->channels[i].vidq.timeout.data = + (unsigned long)&dev->channels[i].timeout_data; + init_timer(&dev->channels[i].vidq.timeout); + + /* register v4l devices */ + dev->channels[i].video_dev = cx25821_vdev_init(dev, + dev->pci, &cx25821_video_device, "video"); + + err = video_register_device(dev->channels[i].video_dev, + VFL_TYPE_GRABBER, video_nr[dev->nr]); + + if (err < 0) + goto fail_unreg; + + } + + /* set PCI interrupt */ + cx_set(PCI_INT_MSK, 0xff); + + /* initial device configuration */ + mutex_lock(&dev->lock); +#ifdef TUNER_FLAG + dev->tvnorm = cx25821_video_device.current_norm; + cx25821_set_tvnorm(dev, dev->tvnorm); +#endif + mutex_unlock(&dev->lock); + + + return 0; + +fail_unreg: + cx25821_video_unregister(dev, i); + return err; +} + +int cx25821_buffer_setup(struct videobuf_queue *q, unsigned int *count, + unsigned int *size) +{ + struct cx25821_fh *fh = q->priv_data; + + *size = fh->fmt->depth * fh->width * fh->height >> 3; + + if (0 == *count) + *count = 32; + + if (*size * *count > vid_limit * 1024 * 1024) + *count = (vid_limit * 1024 * 1024) / *size; + + return 0; +} + +int cx25821_buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct cx25821_fh *fh = q->priv_data; + struct cx25821_dev *dev = fh->dev; + struct cx25821_buffer *buf = + container_of(vb, struct cx25821_buffer, vb); + int rc, init_buffer = 0; + u32 line0_offset, line1_offset; + struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb); + int bpl_local = LINE_SIZE_D1; + int channel_opened = fh->channel_id; + + BUG_ON(NULL == fh->fmt); + if (fh->width < 48 || fh->width > 720 || + fh->height < 32 || fh->height > 576) + return -EINVAL; + + buf->vb.size = (fh->width * fh->height * fh->fmt->depth) >> 3; + + if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) + return -EINVAL; + + if (buf->fmt != fh->fmt || + buf->vb.width != fh->width || + buf->vb.height != fh->height || buf->vb.field != field) { + buf->fmt = fh->fmt; + buf->vb.width = fh->width; + buf->vb.height = fh->height; + buf->vb.field = field; + init_buffer = 1; + } + + if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { + init_buffer = 1; + rc = videobuf_iolock(q, &buf->vb, NULL); + if (0 != rc) { + printk(KERN_DEBUG pr_fmt("videobuf_iolock failed!\n")); + goto fail; + } + } + + dprintk(1, "init_buffer=%d\n", init_buffer); + + if (init_buffer) { + + channel_opened = dev->channel_opened; + if (channel_opened < 0 || channel_opened > 7) + channel_opened = 7; + + if (dev->channels[channel_opened].pixel_formats == + PIXEL_FRMT_411) + buf->bpl = (buf->fmt->depth * buf->vb.width) >> 3; + else + buf->bpl = (buf->fmt->depth >> 3) * (buf->vb.width); + + if (dev->channels[channel_opened].pixel_formats == + PIXEL_FRMT_411) { + bpl_local = buf->bpl; + } else { + bpl_local = buf->bpl; /* Default */ + + if (channel_opened >= 0 && channel_opened <= 7) { + if (dev->channels[channel_opened] + .use_cif_resolution) { + if (dev->tvnorm & V4L2_STD_PAL_BG + || dev->tvnorm & V4L2_STD_PAL_DK) + bpl_local = 352 << 1; + else + bpl_local = + dev->channels[channel_opened]. + cif_width << + 1; + } + } + } + + switch (buf->vb.field) { + case V4L2_FIELD_TOP: + cx25821_risc_buffer(dev->pci, &buf->risc, + dma->sglist, 0, UNSET, + buf->bpl, 0, buf->vb.height); + break; + case V4L2_FIELD_BOTTOM: + cx25821_risc_buffer(dev->pci, &buf->risc, + dma->sglist, UNSET, 0, + buf->bpl, 0, buf->vb.height); + break; + case V4L2_FIELD_INTERLACED: + /* All other formats are top field first */ + line0_offset = 0; + line1_offset = buf->bpl; + dprintk(1, "top field first\n"); + + cx25821_risc_buffer(dev->pci, &buf->risc, + dma->sglist, line0_offset, + bpl_local, bpl_local, bpl_local, + buf->vb.height >> 1); + break; + case V4L2_FIELD_SEQ_TB: + cx25821_risc_buffer(dev->pci, &buf->risc, + dma->sglist, + 0, buf->bpl * (buf->vb.height >> 1), + buf->bpl, 0, buf->vb.height >> 1); + break; + case V4L2_FIELD_SEQ_BT: + cx25821_risc_buffer(dev->pci, &buf->risc, + dma->sglist, + buf->bpl * (buf->vb.height >> 1), 0, + buf->bpl, 0, buf->vb.height >> 1); + break; + default: + BUG(); + } + } + + dprintk(2, "[%p/%d] buffer_prep - %dx%d %dbpp \"%s\" - dma=0x%08lx\n", + buf, buf->vb.i, fh->width, fh->height, fh->fmt->depth, + fh->fmt->name, (unsigned long)buf->risc.dma); + + buf->vb.state = VIDEOBUF_PREPARED; + + return 0; + +fail: + cx25821_free_buffer(q, buf); + return rc; +} + +void cx25821_buffer_release(struct videobuf_queue *q, + struct videobuf_buffer *vb) +{ + struct cx25821_buffer *buf = + container_of(vb, struct cx25821_buffer, vb); + + cx25821_free_buffer(q, buf); +} + +struct videobuf_queue *get_queue(struct cx25821_fh *fh) +{ + switch (fh->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + return &fh->vidq; + default: + BUG(); + return NULL; + } +} + +int cx25821_get_resource(struct cx25821_fh *fh, int resource) +{ + switch (fh->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + return resource; + default: + BUG(); + return 0; + } +} + +int cx25821_video_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct cx25821_fh *fh = file->private_data; + + return videobuf_mmap_mapper(get_queue(fh), vma); +} + + +static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct cx25821_buffer *buf = + container_of(vb, struct cx25821_buffer, vb); + struct cx25821_buffer *prev; + struct cx25821_fh *fh = vq->priv_data; + struct cx25821_dev *dev = fh->dev; + struct cx25821_dmaqueue *q = &dev->channels[fh->channel_id].vidq; + + /* add jump to stopper */ + buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); + buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma); + buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */ + + dprintk(2, "jmp to stopper (0x%x)\n", buf->risc.jmp[1]); + + if (!list_empty(&q->queued)) { + list_add_tail(&buf->vb.queue, &q->queued); + buf->vb.state = VIDEOBUF_QUEUED; + dprintk(2, "[%p/%d] buffer_queue - append to queued\n", buf, + buf->vb.i); + + } else if (list_empty(&q->active)) { + list_add_tail(&buf->vb.queue, &q->active); + cx25821_start_video_dma(dev, q, buf, + dev->channels[fh->channel_id].sram_channels); + buf->vb.state = VIDEOBUF_ACTIVE; + buf->count = q->count++; + mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); + dprintk(2, "[%p/%d] buffer_queue - first active, buf cnt = %d, q->count = %d\n", + buf, buf->vb.i, buf->count, q->count); + } else { + prev = list_entry(q->active.prev, struct cx25821_buffer, + vb.queue); + if (prev->vb.width == buf->vb.width + && prev->vb.height == buf->vb.height + && prev->fmt == buf->fmt) { + list_add_tail(&buf->vb.queue, &q->active); + buf->vb.state = VIDEOBUF_ACTIVE; + buf->count = q->count++; + prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); + + /* 64 bit bits 63-32 */ + prev->risc.jmp[2] = cpu_to_le32(0); + dprintk(2, "[%p/%d] buffer_queue - append to active, buf->count=%d\n", + buf, buf->vb.i, buf->count); + + } else { + list_add_tail(&buf->vb.queue, &q->queued); + buf->vb.state = VIDEOBUF_QUEUED; + dprintk(2, "[%p/%d] buffer_queue - first queued\n", buf, + buf->vb.i); + } + } + + if (list_empty(&q->active)) + dprintk(2, "active queue empty!\n"); +} + +static struct videobuf_queue_ops cx25821_video_qops = { + .buf_setup = cx25821_buffer_setup, + .buf_prepare = cx25821_buffer_prepare, + .buf_queue = buffer_queue, + .buf_release = cx25821_buffer_release, +}; + +static int video_open(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct cx25821_dev *h, *dev = video_drvdata(file); + struct cx25821_fh *fh; + struct list_head *list; + int minor = video_devdata(file)->minor; + enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + u32 pix_format; + int ch_id = 0; + int i; + + dprintk(1, "open dev=%s type=%s\n", video_device_node_name(vdev), + v4l2_type_names[type]); + + /* allocate + initialize per filehandle data */ + fh = kzalloc(sizeof(*fh), GFP_KERNEL); + if (NULL == fh) + return -ENOMEM; + + mutex_lock(&cx25821_devlist_mutex); + + list_for_each(list, &cx25821_devlist) + { + h = list_entry(list, struct cx25821_dev, devlist); + + for (i = 0; i < MAX_VID_CHANNEL_NUM; i++) { + if (h->channels[i].video_dev && + h->channels[i].video_dev->minor == minor) { + dev = h; + ch_id = i; + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + } + } + } + + if (NULL == dev) { + mutex_unlock(&cx25821_devlist_mutex); + kfree(fh); + return -ENODEV; + } + + file->private_data = fh; + fh->dev = dev; + fh->type = type; + fh->width = 720; + fh->channel_id = ch_id; + + if (dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK) + fh->height = 576; + else + fh->height = 480; + + dev->channel_opened = fh->channel_id; + if (dev->channels[ch_id].pixel_formats == PIXEL_FRMT_411) + pix_format = V4L2_PIX_FMT_Y41P; + else + pix_format = V4L2_PIX_FMT_YUYV; + fh->fmt = cx25821_format_by_fourcc(pix_format); + + v4l2_prio_open(&dev->channels[ch_id].prio, &fh->prio); + + videobuf_queue_sg_init(&fh->vidq, &cx25821_video_qops, + &dev->pci->dev, &dev->slock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_FIELD_INTERLACED, + sizeof(struct cx25821_buffer), fh, NULL); + + dprintk(1, "post videobuf_queue_init()\n"); + mutex_unlock(&cx25821_devlist_mutex); + + return 0; +} + +static ssize_t video_read(struct file *file, char __user * data, size_t count, + loff_t *ppos) +{ + struct cx25821_fh *fh = file->private_data; + + switch (fh->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + if (cx25821_res_locked(fh, RESOURCE_VIDEO0)) + return -EBUSY; + + return videobuf_read_one(&fh->vidq, data, count, ppos, + file->f_flags & O_NONBLOCK); + + default: + BUG(); + return 0; + } +} + +static unsigned int video_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct cx25821_fh *fh = file->private_data; + struct cx25821_buffer *buf; + + if (cx25821_res_check(fh, RESOURCE_VIDEO0)) { + /* streaming capture */ + if (list_empty(&fh->vidq.stream)) + return POLLERR; + buf = list_entry(fh->vidq.stream.next, + struct cx25821_buffer, vb.stream); + } else { + /* read() capture */ + buf = (struct cx25821_buffer *)fh->vidq.read_buf; + if (NULL == buf) + return POLLERR; + } + + poll_wait(file, &buf->vb.done, wait); + if (buf->vb.state == VIDEOBUF_DONE || buf->vb.state == VIDEOBUF_ERROR) { + if (buf->vb.state == VIDEOBUF_DONE) { + struct cx25821_dev *dev = fh->dev; + + if (dev && dev->channels[fh->channel_id] + .use_cif_resolution) { + u8 cam_id = *((char *)buf->vb.baddr + 3); + memcpy((char *)buf->vb.baddr, + (char *)buf->vb.baddr + (fh->width * 2), + (fh->width * 2)); + *((char *)buf->vb.baddr + 3) = cam_id; + } + } + + return POLLIN | POLLRDNORM; + } + + return 0; +} + +static int video_release(struct file *file) +{ + struct cx25821_fh *fh = file->private_data; + struct cx25821_dev *dev = fh->dev; + + /* stop the risc engine and fifo */ + cx_write(channel0->dma_ctl, 0); /* FIFO and RISC disable */ + + /* stop video capture */ + if (cx25821_res_check(fh, RESOURCE_VIDEO0)) { + videobuf_queue_cancel(&fh->vidq); + cx25821_res_free(dev, fh, RESOURCE_VIDEO0); + } + + if (fh->vidq.read_buf) { + cx25821_buffer_release(&fh->vidq, fh->vidq.read_buf); + kfree(fh->vidq.read_buf); + } + + videobuf_mmap_free(&fh->vidq); + + v4l2_prio_close(&dev->channels[fh->channel_id].prio, fh->prio); + file->private_data = NULL; + kfree(fh); + + return 0; +} + +static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = fh->dev; + + if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)) + return -EINVAL; + + if (unlikely(i != fh->type)) + return -EINVAL; + + if (unlikely(!cx25821_res_get(dev, fh, cx25821_get_resource(fh, + RESOURCE_VIDEO0)))) + return -EBUSY; + + return videobuf_streamon(get_queue(fh)); +} + +static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = fh->dev; + int err, res; + + if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (i != fh->type) + return -EINVAL; + + res = cx25821_get_resource(fh, RESOURCE_VIDEO0); + err = videobuf_streamoff(get_queue(fh)); + if (err < 0) + return err; + cx25821_res_free(dev, fh, res); + return 0; +} + +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + struct v4l2_mbus_framefmt mbus_fmt; + int err; + int pix_format = PIXEL_FRMT_422; + + if (fh) { + err = v4l2_prio_check(&dev->channels[fh->channel_id].prio, + fh->prio); + if (0 != err) + return err; + } + + dprintk(2, "%s()\n", __func__); + err = cx25821_vidioc_try_fmt_vid_cap(file, priv, f); + + if (0 != err) + return err; + + fh->fmt = cx25821_format_by_fourcc(f->fmt.pix.pixelformat); + fh->vidq.field = f->fmt.pix.field; + + /* check if width and height is valid based on set standard */ + if (cx25821_is_valid_width(f->fmt.pix.width, dev->tvnorm)) + fh->width = f->fmt.pix.width; + + if (cx25821_is_valid_height(f->fmt.pix.height, dev->tvnorm)) + fh->height = f->fmt.pix.height; + + if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_Y41P) + pix_format = PIXEL_FRMT_411; + else if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) + pix_format = PIXEL_FRMT_422; + else + return -EINVAL; + + cx25821_set_pixel_format(dev, SRAM_CH00, pix_format); + + /* check if cif resolution */ + if (fh->width == 320 || fh->width == 352) + dev->channels[fh->channel_id].use_cif_resolution = 1; + else + dev->channels[fh->channel_id].use_cif_resolution = 0; + + dev->channels[fh->channel_id].cif_width = fh->width; + medusa_set_resolution(dev, fh->width, SRAM_CH00); + + dprintk(2, "%s(): width=%d height=%d field=%d\n", __func__, fh->width, + fh->height, fh->vidq.field); + v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, V4L2_MBUS_FMT_FIXED); + cx25821_call_all(dev, video, s_mbus_fmt, &mbus_fmt); + + return 0; +} + +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + int ret_val = 0; + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + + ret_val = videobuf_dqbuf(get_queue(fh), p, file->f_flags & O_NONBLOCK); + + p->sequence = dev->channels[fh->channel_id].vidq.count; + + return ret_val; +} + +static int vidioc_log_status(struct file *file, void *priv) +{ + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + struct cx25821_fh *fh = priv; + char name[32 + 2]; + + struct sram_channel *sram_ch = dev->channels[fh->channel_id] + .sram_channels; + u32 tmp = 0; + + snprintf(name, sizeof(name), "%s/2", dev->name); + pr_info("%s/2: ============ START LOG STATUS ============\n", + dev->name); + cx25821_call_all(dev, core, log_status); + tmp = cx_read(sram_ch->dma_ctl); + pr_info("Video input 0 is %s\n", + (tmp & 0x11) ? "streaming" : "stopped"); + pr_info("%s/2: ============= END LOG STATUS =============\n", + dev->name); + return 0; +} + +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctl) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + int err; + + if (fh) { + err = v4l2_prio_check(&dev->channels[fh->channel_id].prio, + fh->prio); + if (0 != err) + return err; + } + + return cx25821_set_control(dev, ctl, fh->channel_id); +} + +/* VIDEO IOCTLS */ +int cx25821_vidioc_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cx25821_fh *fh = priv; + + f->fmt.pix.width = fh->width; + f->fmt.pix.height = fh->height; + f->fmt.pix.field = fh->vidq.field; + f->fmt.pix.pixelformat = fh->fmt->fourcc; + f->fmt.pix.bytesperline = (f->fmt.pix.width * fh->fmt->depth) >> 3; + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + + return 0; +} + +int cx25821_vidioc_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cx25821_fmt *fmt; + enum v4l2_field field; + unsigned int maxw, maxh; + + fmt = cx25821_format_by_fourcc(f->fmt.pix.pixelformat); + if (NULL == fmt) + return -EINVAL; + + field = f->fmt.pix.field; + maxw = 720; + maxh = 576; + + if (V4L2_FIELD_ANY == field) { + if (f->fmt.pix.height > maxh / 2) + field = V4L2_FIELD_INTERLACED; + else + field = V4L2_FIELD_TOP; + } + + switch (field) { + case V4L2_FIELD_TOP: + case V4L2_FIELD_BOTTOM: + maxh = maxh / 2; + break; + case V4L2_FIELD_INTERLACED: + break; + default: + return -EINVAL; + } + + f->fmt.pix.field = field; + if (f->fmt.pix.height < 32) + f->fmt.pix.height = 32; + if (f->fmt.pix.height > maxh) + f->fmt.pix.height = maxh; + if (f->fmt.pix.width < 48) + f->fmt.pix.width = 48; + if (f->fmt.pix.width > maxw) + f->fmt.pix.width = maxw; + f->fmt.pix.width &= ~0x03; + f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + + return 0; +} + +int cx25821_vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + + strcpy(cap->driver, "cx25821"); + strlcpy(cap->card, cx25821_boards[dev->board].name, sizeof(cap->card)); + sprintf(cap->bus_info, "PCIe:%s", pci_name(dev->pci)); + cap->version = CX25821_VERSION_CODE; + cap->capabilities = + V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; + if (UNSET != dev->tuner_type) + cap->capabilities |= V4L2_CAP_TUNER; + return 0; +} + +int cx25821_vidioc_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + if (unlikely(f->index >= ARRAY_SIZE(formats))) + return -EINVAL; + + strlcpy(f->description, formats[f->index].name, sizeof(f->description)); + f->pixelformat = formats[f->index].fourcc; + + return 0; +} + +int cx25821_vidioc_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *p) +{ + struct cx25821_fh *fh = priv; + return videobuf_reqbufs(get_queue(fh), p); +} + +int cx25821_vidioc_querybuf(struct file *file, void *priv, + struct v4l2_buffer *p) +{ + struct cx25821_fh *fh = priv; + return videobuf_querybuf(get_queue(fh), p); +} + +int cx25821_vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct cx25821_fh *fh = priv; + return videobuf_qbuf(get_queue(fh), p); +} + +int cx25821_vidioc_g_priority(struct file *file, void *f, enum v4l2_priority *p) +{ + struct cx25821_dev *dev = ((struct cx25821_fh *)f)->dev; + struct cx25821_fh *fh = f; + + *p = v4l2_prio_max(&dev->channels[fh->channel_id].prio); + + return 0; +} + +int cx25821_vidioc_s_priority(struct file *file, void *f, + enum v4l2_priority prio) +{ + struct cx25821_fh *fh = f; + struct cx25821_dev *dev = ((struct cx25821_fh *)f)->dev; + + return v4l2_prio_change(&dev->channels[fh->channel_id].prio, &fh->prio, + prio); +} + +#ifdef TUNER_FLAG +int cx25821_vidioc_s_std(struct file *file, void *priv, v4l2_std_id * tvnorms) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + int err; + + dprintk(1, "%s()\n", __func__); + + if (fh) { + err = v4l2_prio_check(&dev->channels[fh->channel_id].prio, + fh->prio); + if (0 != err) + return err; + } + + if (dev->tvnorm == *tvnorms) + return 0; + + mutex_lock(&dev->lock); + cx25821_set_tvnorm(dev, *tvnorms); + mutex_unlock(&dev->lock); + + medusa_set_videostandard(dev); + + return 0; +} +#endif + +int cx25821_enum_input(struct cx25821_dev *dev, struct v4l2_input *i) +{ + static const char * const iname[] = { + [CX25821_VMUX_COMPOSITE] = "Composite", + [CX25821_VMUX_SVIDEO] = "S-Video", + [CX25821_VMUX_DEBUG] = "for debug only", + }; + unsigned int n; + dprintk(1, "%s()\n", __func__); + + n = i->index; + if (n >= 2) + return -EINVAL; + + if (0 == INPUT(n)->type) + return -EINVAL; + + i->type = V4L2_INPUT_TYPE_CAMERA; + strcpy(i->name, iname[INPUT(n)->type]); + + i->std = CX25821_NORMS; + return 0; +} + +int cx25821_vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *i) +{ + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + dprintk(1, "%s()\n", __func__); + return cx25821_enum_input(dev, i); +} + +int cx25821_vidioc_g_input(struct file *file, void *priv, unsigned int *i) +{ + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + + *i = dev->input; + dprintk(1, "%s(): returns %d\n", __func__, *i); + return 0; +} + +int cx25821_vidioc_s_input(struct file *file, void *priv, unsigned int i) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + int err; + + dprintk(1, "%s(%d)\n", __func__, i); + + if (fh) { + err = v4l2_prio_check(&dev->channels[fh->channel_id].prio, + fh->prio); + if (0 != err) + return err; + } + + if (i >= CX25821_NR_INPUT) { + dprintk(1, "%s(): -EINVAL\n", __func__); + return -EINVAL; + } + + mutex_lock(&dev->lock); + cx25821_video_mux(dev, i); + mutex_unlock(&dev->lock); + return 0; +} + +#ifdef TUNER_FLAG +int cx25821_vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = fh->dev; + + f->frequency = dev->freq; + + cx25821_call_all(dev, tuner, g_frequency, f); + + return 0; +} + +int cx25821_set_freq(struct cx25821_dev *dev, struct v4l2_frequency *f) +{ + mutex_lock(&dev->lock); + dev->freq = f->frequency; + + cx25821_call_all(dev, tuner, s_frequency, f); + + /* When changing channels it is required to reset TVAUDIO */ + msleep(10); + + mutex_unlock(&dev->lock); + + return 0; +} + +int cx25821_vidioc_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev; + int err; + + if (fh) { + dev = fh->dev; + err = v4l2_prio_check(&dev->channels[fh->channel_id].prio, + fh->prio); + if (0 != err) + return err; + } else { + pr_err("Invalid fh pointer!\n"); + return -EINVAL; + } + + return cx25821_set_freq(dev, f); +} +#endif + +#ifdef CONFIG_VIDEO_ADV_DEBUG +int cx25821_vidioc_g_register(struct file *file, void *fh, + struct v4l2_dbg_register *reg) +{ + struct cx25821_dev *dev = ((struct cx25821_fh *)fh)->dev; + + if (!v4l2_chip_match_host(®->match)) + return -EINVAL; + + cx25821_call_all(dev, core, g_register, reg); + + return 0; +} + +int cx25821_vidioc_s_register(struct file *file, void *fh, + struct v4l2_dbg_register *reg) +{ + struct cx25821_dev *dev = ((struct cx25821_fh *)fh)->dev; + + if (!v4l2_chip_match_host(®->match)) + return -EINVAL; + + cx25821_call_all(dev, core, s_register, reg); + + return 0; +} + +#endif + +#ifdef TUNER_FLAG +int cx25821_vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) +{ + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + + if (unlikely(UNSET == dev->tuner_type)) + return -EINVAL; + if (0 != t->index) + return -EINVAL; + + strcpy(t->name, "Television"); + t->type = V4L2_TUNER_ANALOG_TV; + t->capability = V4L2_TUNER_CAP_NORM; + t->rangehigh = 0xffffffffUL; + + t->signal = 0xffff; /* LOCKED */ + return 0; +} + +int cx25821_vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *t) +{ + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + struct cx25821_fh *fh = priv; + int err; + + if (fh) { + err = v4l2_prio_check(&dev->channels[fh->channel_id].prio, + fh->prio); + if (0 != err) + return err; + } + + dprintk(1, "%s()\n", __func__); + if (UNSET == dev->tuner_type) + return -EINVAL; + if (0 != t->index) + return -EINVAL; + + return 0; +} + +#endif +/*****************************************************************************/ +static const struct v4l2_queryctrl no_ctl = { + .name = "42", + .flags = V4L2_CTRL_FLAG_DISABLED, +}; + +static struct v4l2_queryctrl cx25821_ctls[] = { + /* --- video --- */ + { + .id = V4L2_CID_BRIGHTNESS, + .name = "Brightness", + .minimum = 0, + .maximum = 10000, + .step = 1, + .default_value = 6200, + .type = V4L2_CTRL_TYPE_INTEGER, + }, { + .id = V4L2_CID_CONTRAST, + .name = "Contrast", + .minimum = 0, + .maximum = 10000, + .step = 1, + .default_value = 5000, + .type = V4L2_CTRL_TYPE_INTEGER, + }, { + .id = V4L2_CID_SATURATION, + .name = "Saturation", + .minimum = 0, + .maximum = 10000, + .step = 1, + .default_value = 5000, + .type = V4L2_CTRL_TYPE_INTEGER, + }, { + .id = V4L2_CID_HUE, + .name = "Hue", + .minimum = 0, + .maximum = 10000, + .step = 1, + .default_value = 5000, + .type = V4L2_CTRL_TYPE_INTEGER, + } +}; +static const int CX25821_CTLS = ARRAY_SIZE(cx25821_ctls); + +static int cx25821_ctrl_query(struct v4l2_queryctrl *qctrl) +{ + int i; + + if (qctrl->id < V4L2_CID_BASE || qctrl->id >= V4L2_CID_LASTP1) + return -EINVAL; + for (i = 0; i < CX25821_CTLS; i++) + if (cx25821_ctls[i].id == qctrl->id) + break; + if (i == CX25821_CTLS) { + *qctrl = no_ctl; + return 0; + } + *qctrl = cx25821_ctls[i]; + return 0; +} + +int cx25821_vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qctrl) +{ + return cx25821_ctrl_query(qctrl); +} + +/* ------------------------------------------------------------------ */ +/* VIDEO CTRL IOCTLS */ + +static const struct v4l2_queryctrl *ctrl_by_id(unsigned int id) +{ + unsigned int i; + + for (i = 0; i < CX25821_CTLS; i++) + if (cx25821_ctls[i].id == id) + return cx25821_ctls + i; + return NULL; +} + +int cx25821_vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctl) +{ + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + struct cx25821_fh *fh = priv; + + const struct v4l2_queryctrl *ctrl; + + ctrl = ctrl_by_id(ctl->id); + + if (NULL == ctrl) + return -EINVAL; + switch (ctl->id) { + case V4L2_CID_BRIGHTNESS: + ctl->value = dev->channels[fh->channel_id].ctl_bright; + break; + case V4L2_CID_HUE: + ctl->value = dev->channels[fh->channel_id].ctl_hue; + break; + case V4L2_CID_CONTRAST: + ctl->value = dev->channels[fh->channel_id].ctl_contrast; + break; + case V4L2_CID_SATURATION: + ctl->value = dev->channels[fh->channel_id].ctl_saturation; + break; + } + return 0; +} + +int cx25821_set_control(struct cx25821_dev *dev, + struct v4l2_control *ctl, int chan_num) +{ + int err; + const struct v4l2_queryctrl *ctrl; + + err = -EINVAL; + + ctrl = ctrl_by_id(ctl->id); + + if (NULL == ctrl) + return err; + + switch (ctrl->type) { + case V4L2_CTRL_TYPE_BOOLEAN: + case V4L2_CTRL_TYPE_MENU: + case V4L2_CTRL_TYPE_INTEGER: + if (ctl->value < ctrl->minimum) + ctl->value = ctrl->minimum; + if (ctl->value > ctrl->maximum) + ctl->value = ctrl->maximum; + break; + default: + /* nothing */ ; + } + + switch (ctl->id) { + case V4L2_CID_BRIGHTNESS: + dev->channels[chan_num].ctl_bright = ctl->value; + medusa_set_brightness(dev, ctl->value, chan_num); + break; + case V4L2_CID_HUE: + dev->channels[chan_num].ctl_hue = ctl->value; + medusa_set_hue(dev, ctl->value, chan_num); + break; + case V4L2_CID_CONTRAST: + dev->channels[chan_num].ctl_contrast = ctl->value; + medusa_set_contrast(dev, ctl->value, chan_num); + break; + case V4L2_CID_SATURATION: + dev->channels[chan_num].ctl_saturation = ctl->value; + medusa_set_saturation(dev, ctl->value, chan_num); + break; + } + + err = 0; + + return err; +} + +static void cx25821_init_controls(struct cx25821_dev *dev, int chan_num) +{ + struct v4l2_control ctrl; + int i; + for (i = 0; i < CX25821_CTLS; i++) { + ctrl.id = cx25821_ctls[i].id; + ctrl.value = cx25821_ctls[i].default_value; + + cx25821_set_control(dev, &ctrl, chan_num); + } +} + +int cx25821_vidioc_cropcap(struct file *file, void *priv, + struct v4l2_cropcap *cropcap) +{ + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + + if (cropcap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + cropcap->bounds.top = cropcap->bounds.left = 0; + cropcap->bounds.width = 720; + cropcap->bounds.height = dev->tvnorm == V4L2_STD_PAL_BG ? 576 : 480; + cropcap->pixelaspect.numerator = + dev->tvnorm == V4L2_STD_PAL_BG ? 59 : 10; + cropcap->pixelaspect.denominator = + dev->tvnorm == V4L2_STD_PAL_BG ? 54 : 11; + cropcap->defrect = cropcap->bounds; + return 0; +} + +int cx25821_vidioc_s_crop(struct file *file, void *priv, struct v4l2_crop *crop) +{ + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + struct cx25821_fh *fh = priv; + int err; + + if (fh) { + err = v4l2_prio_check(&dev->channels[fh->channel_id].prio, + fh->prio); + if (0 != err) + return err; + } + /* cx25821_vidioc_s_crop not supported */ + return -EINVAL; +} + +int cx25821_vidioc_g_crop(struct file *file, void *priv, struct v4l2_crop *crop) +{ + /* cx25821_vidioc_g_crop not supported */ + return -EINVAL; +} + +int cx25821_vidioc_querystd(struct file *file, void *priv, v4l2_std_id * norm) +{ + /* medusa does not support video standard sensing of current input */ + *norm = CX25821_NORMS; + + return 0; +} + +int cx25821_is_valid_width(u32 width, v4l2_std_id tvnorm) +{ + if (tvnorm == V4L2_STD_PAL_BG) { + if (width == 352 || width == 720) + return 1; + else + return 0; + } + + if (tvnorm == V4L2_STD_NTSC_M) { + if (width == 320 || width == 352 || width == 720) + return 1; + else + return 0; + } + return 0; +} + +int cx25821_is_valid_height(u32 height, v4l2_std_id tvnorm) +{ + if (tvnorm == V4L2_STD_PAL_BG) { + if (height == 576 || height == 288) + return 1; + else + return 0; + } + + if (tvnorm == V4L2_STD_NTSC_M) { + if (height == 480 || height == 240) + return 1; + else + return 0; + } + + return 0; +} + +static long video_ioctl_upstream9(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct cx25821_fh *fh = file->private_data; + struct cx25821_dev *dev = fh->dev; + int command = 0; + struct upstream_user_struct *data_from_user; + + data_from_user = (struct upstream_user_struct *)arg; + + if (!data_from_user) { + pr_err("%s(): Upstream data is INVALID. Returning\n", __func__); + return 0; + } + + command = data_from_user->command; + + if (command != UPSTREAM_START_VIDEO && command != UPSTREAM_STOP_VIDEO) + return 0; + + dev->input_filename = data_from_user->input_filename; + dev->input_audiofilename = data_from_user->input_filename; + dev->vid_stdname = data_from_user->vid_stdname; + dev->pixel_format = data_from_user->pixel_format; + dev->channel_select = data_from_user->channel_select; + dev->command = data_from_user->command; + + switch (command) { + case UPSTREAM_START_VIDEO: + cx25821_start_upstream_video_ch1(dev, data_from_user); + break; + + case UPSTREAM_STOP_VIDEO: + cx25821_stop_upstream_video_ch1(dev); + break; + } + + return 0; +} + +static long video_ioctl_upstream10(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct cx25821_fh *fh = file->private_data; + struct cx25821_dev *dev = fh->dev; + int command = 0; + struct upstream_user_struct *data_from_user; + + data_from_user = (struct upstream_user_struct *)arg; + + if (!data_from_user) { + pr_err("%s(): Upstream data is INVALID. Returning\n", __func__); + return 0; + } + + command = data_from_user->command; + + if (command != UPSTREAM_START_VIDEO && command != UPSTREAM_STOP_VIDEO) + return 0; + + dev->input_filename_ch2 = data_from_user->input_filename; + dev->input_audiofilename = data_from_user->input_filename; + dev->vid_stdname_ch2 = data_from_user->vid_stdname; + dev->pixel_format_ch2 = data_from_user->pixel_format; + dev->channel_select_ch2 = data_from_user->channel_select; + dev->command_ch2 = data_from_user->command; + + switch (command) { + case UPSTREAM_START_VIDEO: + cx25821_start_upstream_video_ch2(dev, data_from_user); + break; + + case UPSTREAM_STOP_VIDEO: + cx25821_stop_upstream_video_ch2(dev); + break; + } + + return 0; +} + +static long video_ioctl_upstream11(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct cx25821_fh *fh = file->private_data; + struct cx25821_dev *dev = fh->dev; + int command = 0; + struct upstream_user_struct *data_from_user; + + data_from_user = (struct upstream_user_struct *)arg; + + if (!data_from_user) { + pr_err("%s(): Upstream data is INVALID. Returning\n", __func__); + return 0; + } + + command = data_from_user->command; + + if (command != UPSTREAM_START_AUDIO && command != UPSTREAM_STOP_AUDIO) + return 0; + + dev->input_filename = data_from_user->input_filename; + dev->input_audiofilename = data_from_user->input_filename; + dev->vid_stdname = data_from_user->vid_stdname; + dev->pixel_format = data_from_user->pixel_format; + dev->channel_select = data_from_user->channel_select; + dev->command = data_from_user->command; + + switch (command) { + case UPSTREAM_START_AUDIO: + cx25821_start_upstream_audio(dev, data_from_user); + break; + + case UPSTREAM_STOP_AUDIO: + cx25821_stop_upstream_audio(dev); + break; + } + + return 0; +} + +static long video_ioctl_set(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct cx25821_fh *fh = file->private_data; + struct cx25821_dev *dev = fh->dev; + struct downstream_user_struct *data_from_user; + int command; + int width = 720; + int selected_channel = 0, pix_format = 0, i = 0; + int cif_enable = 0, cif_width = 0; + u32 value = 0; + + data_from_user = (struct downstream_user_struct *)arg; + + if (!data_from_user) { + pr_err("%s(): User data is INVALID. Returning\n", __func__); + return 0; + } + + command = data_from_user->command; + + if (command != SET_VIDEO_STD && command != SET_PIXEL_FORMAT + && command != ENABLE_CIF_RESOLUTION && command != REG_READ + && command != REG_WRITE && command != MEDUSA_READ + && command != MEDUSA_WRITE) { + return 0; + } + + switch (command) { + case SET_VIDEO_STD: + if (!strcmp(data_from_user->vid_stdname, "PAL")) + dev->tvnorm = V4L2_STD_PAL_BG; + else + dev->tvnorm = V4L2_STD_NTSC_M; + medusa_set_videostandard(dev); + break; + + case SET_PIXEL_FORMAT: + selected_channel = data_from_user->decoder_select; + pix_format = data_from_user->pixel_format; + + if (!(selected_channel <= 7 && selected_channel >= 0)) { + selected_channel -= 4; + selected_channel = selected_channel % 8; + } + + if (selected_channel >= 0) + cx25821_set_pixel_format(dev, selected_channel, + pix_format); + + break; + + case ENABLE_CIF_RESOLUTION: + selected_channel = data_from_user->decoder_select; + cif_enable = data_from_user->cif_resolution_enable; + cif_width = data_from_user->cif_width; + + if (cif_enable) { + if (dev->tvnorm & V4L2_STD_PAL_BG + || dev->tvnorm & V4L2_STD_PAL_DK) { + width = 352; + } else { + width = cif_width; + if (cif_width != 320 && cif_width != 352) + width = 320; + } + } + + if (!(selected_channel <= 7 && selected_channel >= 0)) { + selected_channel -= 4; + selected_channel = selected_channel % 8; + } + + if (selected_channel <= 7 && selected_channel >= 0) { + dev->channels[selected_channel]. + use_cif_resolution = cif_enable; + dev->channels[selected_channel].cif_width = width; + } else { + for (i = 0; i < VID_CHANNEL_NUM; i++) { + dev->channels[i].use_cif_resolution = + cif_enable; + dev->channels[i].cif_width = width; + } + } + + medusa_set_resolution(dev, width, selected_channel); + break; + case REG_READ: + data_from_user->reg_data = cx_read(data_from_user->reg_address); + break; + case REG_WRITE: + cx_write(data_from_user->reg_address, data_from_user->reg_data); + break; + case MEDUSA_READ: + value = cx25821_i2c_read(&dev->i2c_bus[0], + (u16) data_from_user->reg_address, + &data_from_user->reg_data); + break; + case MEDUSA_WRITE: + cx25821_i2c_write(&dev->i2c_bus[0], + (u16) data_from_user->reg_address, + data_from_user->reg_data); + break; + } + + return 0; +} + +static long cx25821_video_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + int ret = 0; + + struct cx25821_fh *fh = file->private_data; + + /* check to see if it's the video upstream */ + if (fh->channel_id == SRAM_CH09) { + ret = video_ioctl_upstream9(file, cmd, arg); + return ret; + } else if (fh->channel_id == SRAM_CH10) { + ret = video_ioctl_upstream10(file, cmd, arg); + return ret; + } else if (fh->channel_id == SRAM_CH11) { + ret = video_ioctl_upstream11(file, cmd, arg); + ret = video_ioctl_set(file, cmd, arg); + return ret; + } + + return video_ioctl2(file, cmd, arg); +} + +/* exported stuff */ +static const struct v4l2_file_operations video_fops = { + .owner = THIS_MODULE, + .open = video_open, + .release = video_release, + .read = video_read, + .poll = video_poll, + .mmap = cx25821_video_mmap, + .ioctl = cx25821_video_ioctl, +}; + +static const struct v4l2_ioctl_ops video_ioctl_ops = { + .vidioc_querycap = cx25821_vidioc_querycap, + .vidioc_enum_fmt_vid_cap = cx25821_vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = cx25821_vidioc_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = cx25821_vidioc_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, + .vidioc_reqbufs = cx25821_vidioc_reqbufs, + .vidioc_querybuf = cx25821_vidioc_querybuf, + .vidioc_qbuf = cx25821_vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, +#ifdef TUNER_FLAG + .vidioc_s_std = cx25821_vidioc_s_std, + .vidioc_querystd = cx25821_vidioc_querystd, +#endif + .vidioc_cropcap = cx25821_vidioc_cropcap, + .vidioc_s_crop = cx25821_vidioc_s_crop, + .vidioc_g_crop = cx25821_vidioc_g_crop, + .vidioc_enum_input = cx25821_vidioc_enum_input, + .vidioc_g_input = cx25821_vidioc_g_input, + .vidioc_s_input = cx25821_vidioc_s_input, + .vidioc_g_ctrl = cx25821_vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_queryctrl = cx25821_vidioc_queryctrl, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_log_status = vidioc_log_status, + .vidioc_g_priority = cx25821_vidioc_g_priority, + .vidioc_s_priority = cx25821_vidioc_s_priority, +#ifdef TUNER_FLAG + .vidioc_g_tuner = cx25821_vidioc_g_tuner, + .vidioc_s_tuner = cx25821_vidioc_s_tuner, + .vidioc_g_frequency = cx25821_vidioc_g_frequency, + .vidioc_s_frequency = cx25821_vidioc_s_frequency, +#endif +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = cx25821_vidioc_g_register, + .vidioc_s_register = cx25821_vidioc_s_register, +#endif +}; + +struct video_device cx25821_videoioctl_template = { + .name = "cx25821-videoioctl", + .fops = &video_fops, + .ioctl_ops = &video_ioctl_ops, + .tvnorms = CX25821_NORMS, + .current_norm = V4L2_STD_NTSC_M, +}; diff --git a/drivers/media/video/cx25821/cx25821-video.h b/drivers/media/video/cx25821/cx25821-video.h new file mode 100644 index 00000000000..d0d9538ca5b --- /dev/null +++ b/drivers/media/video/cx25821/cx25821-video.h @@ -0,0 +1,188 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> + * Based on Steven Toth <stoth@linuxtv.org> cx23885 driver + * + * 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 CX25821_VIDEO_H_ +#define CX25821_VIDEO_H_ + +#include <linux/init.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/kmod.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/kthread.h> +#include <asm/div64.h> + +#include "cx25821.h" +#include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> + +#define TUNER_FLAG + +#define VIDEO_DEBUG 0 + +#define dprintk(level, fmt, arg...) \ +do { \ + if (VIDEO_DEBUG >= level) \ + printk(KERN_DEBUG "%s/0: " fmt, dev->name, ##arg); \ +} while (0) + +/* For IOCTL to identify running upstream */ +#define UPSTREAM_START_VIDEO 700 +#define UPSTREAM_STOP_VIDEO 701 +#define UPSTREAM_START_AUDIO 702 +#define UPSTREAM_STOP_AUDIO 703 +#define UPSTREAM_DUMP_REGISTERS 702 +#define SET_VIDEO_STD 800 +#define SET_PIXEL_FORMAT 1000 +#define ENABLE_CIF_RESOLUTION 1001 + +#define REG_READ 900 +#define REG_WRITE 901 +#define MEDUSA_READ 910 +#define MEDUSA_WRITE 911 + +extern struct sram_channel *channel0; +extern struct sram_channel *channel1; +extern struct sram_channel *channel2; +extern struct sram_channel *channel3; +extern struct sram_channel *channel4; +extern struct sram_channel *channel5; +extern struct sram_channel *channel6; +extern struct sram_channel *channel7; +extern struct sram_channel *channel9; +extern struct sram_channel *channel10; +extern struct sram_channel *channel11; +extern struct video_device cx25821_videoioctl_template; +/* extern const u32 *ctrl_classes[]; */ + +extern unsigned int vid_limit; + +#define FORMAT_FLAGS_PACKED 0x01 +extern struct cx25821_fmt formats[]; +extern struct cx25821_fmt *cx25821_format_by_fourcc(unsigned int fourcc); +extern struct cx25821_data timeout_data[MAX_VID_CHANNEL_NUM]; + +extern void cx25821_dump_video_queue(struct cx25821_dev *dev, + struct cx25821_dmaqueue *q); +extern void cx25821_video_wakeup(struct cx25821_dev *dev, + struct cx25821_dmaqueue *q, u32 count); + +#ifdef TUNER_FLAG +extern int cx25821_set_tvnorm(struct cx25821_dev *dev, v4l2_std_id norm); +#endif + +extern int cx25821_res_get(struct cx25821_dev *dev, struct cx25821_fh *fh, + unsigned int bit); +extern int cx25821_res_check(struct cx25821_fh *fh, unsigned int bit); +extern int cx25821_res_locked(struct cx25821_fh *fh, unsigned int bit); +extern void cx25821_res_free(struct cx25821_dev *dev, struct cx25821_fh *fh, + unsigned int bits); +extern int cx25821_video_mux(struct cx25821_dev *dev, unsigned int input); +extern int cx25821_start_video_dma(struct cx25821_dev *dev, + struct cx25821_dmaqueue *q, + struct cx25821_buffer *buf, + struct sram_channel *channel); + +extern int cx25821_set_scale(struct cx25821_dev *dev, unsigned int width, + unsigned int height, enum v4l2_field field); +extern int cx25821_video_irq(struct cx25821_dev *dev, int chan_num, u32 status); +extern void cx25821_video_unregister(struct cx25821_dev *dev, int chan_num); +extern int cx25821_video_register(struct cx25821_dev *dev); +extern int cx25821_get_format_size(void); + +extern int cx25821_buffer_setup(struct videobuf_queue *q, unsigned int *count, + unsigned int *size); +extern int cx25821_buffer_prepare(struct videobuf_queue *q, + struct videobuf_buffer *vb, + enum v4l2_field field); +extern void cx25821_buffer_release(struct videobuf_queue *q, + struct videobuf_buffer *vb); +extern struct videobuf_queue *get_queue(struct cx25821_fh *fh); +extern int cx25821_get_resource(struct cx25821_fh *fh, int resource); +extern int cx25821_video_mmap(struct file *file, struct vm_area_struct *vma); +extern int cx25821_vidioc_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f); +extern int cx25821_vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap); +extern int cx25821_vidioc_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f); +extern int cx25821_vidioc_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *p); +extern int cx25821_vidioc_querybuf(struct file *file, void *priv, + struct v4l2_buffer *p); +extern int cx25821_vidioc_qbuf(struct file *file, void *priv, + struct v4l2_buffer *p); +extern int cx25821_vidioc_s_std(struct file *file, void *priv, + v4l2_std_id *tvnorms); +extern int cx25821_enum_input(struct cx25821_dev *dev, struct v4l2_input *i); +extern int cx25821_vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *i); +extern int cx25821_vidioc_g_input(struct file *file, void *priv, + unsigned int *i); +extern int cx25821_vidioc_s_input(struct file *file, void *priv, + unsigned int i); +extern int cx25821_vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctl); +extern int cx25821_vidioc_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f); +extern int cx25821_vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f); +extern int cx25821_set_freq(struct cx25821_dev *dev, struct v4l2_frequency *f); +extern int cx25821_vidioc_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *f); +extern int cx25821_vidioc_g_register(struct file *file, void *fh, + struct v4l2_dbg_register *reg); +extern int cx25821_vidioc_s_register(struct file *file, void *fh, + struct v4l2_dbg_register *reg); +extern int cx25821_vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *t); +extern int cx25821_vidioc_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *t); + +extern int cx25821_is_valid_width(u32 width, v4l2_std_id tvnorm); +extern int cx25821_is_valid_height(u32 height, v4l2_std_id tvnorm); + +extern int cx25821_vidioc_g_priority(struct file *file, void *f, + enum v4l2_priority *p); +extern int cx25821_vidioc_s_priority(struct file *file, void *f, + enum v4l2_priority prio); + +extern int cx25821_vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qctrl); +extern int cx25821_set_control(struct cx25821_dev *dev, + struct v4l2_control *ctrl, int chan_num); + +extern int cx25821_vidioc_cropcap(struct file *file, void *fh, + struct v4l2_cropcap *cropcap); +extern int cx25821_vidioc_s_crop(struct file *file, void *priv, + struct v4l2_crop *crop); +extern int cx25821_vidioc_g_crop(struct file *file, void *priv, + struct v4l2_crop *crop); + +extern int cx25821_vidioc_querystd(struct file *file, void *priv, + v4l2_std_id *norm); +#endif diff --git a/drivers/media/video/cx25821/cx25821.h b/drivers/media/video/cx25821/cx25821.h new file mode 100644 index 00000000000..2d2d0093282 --- /dev/null +++ b/drivers/media/video/cx25821/cx25821.h @@ -0,0 +1,617 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> + * Based on Steven Toth <stoth@linuxtv.org> cx23885 driver + * + * 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 CX25821_H_ +#define CX25821_H_ + +#include <linux/pci.h> +#include <linux/i2c.h> +#include <linux/i2c-algo-bit.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/sched.h> +#include <linux/kdev_t.h> + +#include <media/v4l2-common.h> +#include <media/v4l2-device.h> +#include <media/tuner.h> +#include <media/tveeprom.h> +#include <media/videobuf-dma-sg.h> +#include <media/videobuf-dvb.h> + +#include "btcx-risc.h" +#include "cx25821-reg.h" +#include "cx25821-medusa-reg.h" +#include "cx25821-sram.h" +#include "cx25821-audio.h" +#include "media/cx2341x.h" + +#include <linux/version.h> +#include <linux/mutex.h> + +#define CX25821_VERSION_CODE KERNEL_VERSION(0, 0, 106) + +#define UNSET (-1U) +#define NO_SYNC_LINE (-1U) + +#define CX25821_MAXBOARDS 2 + +#define TRUE 1 +#define FALSE 0 +#define LINE_SIZE_D1 1440 + +/* Number of decoders and encoders */ +#define MAX_DECODERS 8 +#define MAX_ENCODERS 2 +#define QUAD_DECODERS 4 +#define MAX_CAMERAS 16 + +/* Max number of inputs by card */ +#define MAX_CX25821_INPUT 8 +#define INPUT(nr) (&cx25821_boards[dev->board].input[nr]) +#define RESOURCE_VIDEO0 1 +#define RESOURCE_VIDEO1 2 +#define RESOURCE_VIDEO2 4 +#define RESOURCE_VIDEO3 8 +#define RESOURCE_VIDEO4 16 +#define RESOURCE_VIDEO5 32 +#define RESOURCE_VIDEO6 64 +#define RESOURCE_VIDEO7 128 +#define RESOURCE_VIDEO8 256 +#define RESOURCE_VIDEO9 512 +#define RESOURCE_VIDEO10 1024 +#define RESOURCE_VIDEO11 2048 +#define RESOURCE_VIDEO_IOCTL 4096 + +#define BUFFER_TIMEOUT (HZ) /* 0.5 seconds */ + +#define UNKNOWN_BOARD 0 +#define CX25821_BOARD 1 + +/* Currently supported by the driver */ +#define CX25821_NORMS (\ + V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_JP | V4L2_STD_NTSC_M_KR | \ + V4L2_STD_PAL_BG | V4L2_STD_PAL_DK | V4L2_STD_PAL_I | \ + V4L2_STD_PAL_M | V4L2_STD_PAL_N | V4L2_STD_PAL_H | \ + V4L2_STD_PAL_Nc) + +#define CX25821_BOARD_CONEXANT_ATHENA10 1 +#define MAX_VID_CHANNEL_NUM 12 +#define VID_CHANNEL_NUM 8 +#define CX25821_NR_INPUT 2 + +struct cx25821_fmt { + char *name; + u32 fourcc; /* v4l2 format id */ + int depth; + int flags; + u32 cxformat; +}; + +struct cx25821_ctrl { + struct v4l2_queryctrl v; + u32 off; + u32 reg; + u32 mask; + u32 shift; +}; + +struct cx25821_tvnorm { + char *name; + v4l2_std_id id; + u32 cxiformat; + u32 cxoformat; +}; + +struct cx25821_fh { + struct cx25821_dev *dev; + enum v4l2_buf_type type; + int radio; + u32 resources; + + enum v4l2_priority prio; + + /* video overlay */ + struct v4l2_window win; + struct v4l2_clip *clips; + unsigned int nclips; + + /* video capture */ + struct cx25821_fmt *fmt; + unsigned int width, height; + int channel_id; + + /* vbi capture */ + struct videobuf_queue vidq; + struct videobuf_queue vbiq; + + /* H264 Encoder specifics ONLY */ + struct videobuf_queue mpegq; + atomic_t v4l_reading; +}; + +enum cx25821_itype { + CX25821_VMUX_COMPOSITE = 1, + CX25821_VMUX_SVIDEO, + CX25821_VMUX_DEBUG, + CX25821_RADIO, +}; + +enum cx25821_src_sel_type { + CX25821_SRC_SEL_EXT_656_VIDEO = 0, + CX25821_SRC_SEL_PARALLEL_MPEG_VIDEO +}; + +/* buffer for one video frame */ +struct cx25821_buffer { + /* common v4l buffer stuff -- must be first */ + struct videobuf_buffer vb; + + /* cx25821 specific */ + unsigned int bpl; + struct btcx_riscmem risc; + struct cx25821_fmt *fmt; + u32 count; +}; + +struct cx25821_input { + enum cx25821_itype type; + unsigned int vmux; + u32 gpio0, gpio1, gpio2, gpio3; +}; + +enum port { + CX25821_UNDEFINED = 0, + CX25821_RAW, + CX25821_264 +}; + +struct cx25821_board { + char *name; + enum port porta; + enum port portb; + enum port portc; + unsigned int tuner_type; + unsigned int radio_type; + unsigned char tuner_addr; + unsigned char radio_addr; + + u32 clk_freq; + struct cx25821_input input[CX25821_NR_INPUT]; +}; + +struct cx25821_subid { + u16 subvendor; + u16 subdevice; + u32 card; +}; + +struct cx25821_i2c { + struct cx25821_dev *dev; + + int nr; + + /* i2c i/o */ + struct i2c_adapter i2c_adap; + struct i2c_algo_bit_data i2c_algo; + struct i2c_client i2c_client; + u32 i2c_rc; + + /* cx25821 registers used for raw addess */ + u32 i2c_period; + u32 reg_ctrl; + u32 reg_stat; + u32 reg_addr; + u32 reg_rdata; + u32 reg_wdata; +}; + +struct cx25821_dmaqueue { + struct list_head active; + struct list_head queued; + struct timer_list timeout; + struct btcx_riscmem stopper; + u32 count; +}; + +struct cx25821_data { + struct cx25821_dev *dev; + struct sram_channel *channel; +}; + +struct cx25821_channel { + struct v4l2_prio_state prio; + + int ctl_bright; + int ctl_contrast; + int ctl_hue; + int ctl_saturation; + struct cx25821_data timeout_data; + + struct video_device *video_dev; + struct cx25821_dmaqueue vidq; + + struct sram_channel *sram_channels; + + struct mutex lock; + int resources; + + int pixel_formats; + int use_cif_resolution; + int cif_width; +}; + +struct cx25821_dev { + struct list_head devlist; + atomic_t refcount; + struct v4l2_device v4l2_dev; + + /* pci stuff */ + struct pci_dev *pci; + unsigned char pci_rev, pci_lat; + int pci_bus, pci_slot; + u32 base_io_addr; + u32 __iomem *lmmio; + u8 __iomem *bmmio; + int pci_irqmask; + int hwrevision; + + u32 clk_freq; + + /* I2C adapters: Master 1 & 2 (External) & Master 3 (Internal only) */ + struct cx25821_i2c i2c_bus[3]; + + int nr; + struct mutex lock; + + struct cx25821_channel channels[MAX_VID_CHANNEL_NUM]; + + /* board details */ + unsigned int board; + char name[32]; + + /* Analog video */ + u32 resources; + unsigned int input; + u32 tvaudio; + v4l2_std_id tvnorm; + unsigned int tuner_type; + unsigned char tuner_addr; + unsigned int radio_type; + unsigned char radio_addr; + unsigned int has_radio; + unsigned int videc_type; + unsigned char videc_addr; + unsigned short _max_num_decoders; + + /* Analog Audio Upstream */ + int _audio_is_running; + int _audiopixel_format; + int _is_first_audio_frame; + int _audiofile_status; + int _audio_lines_count; + int _audioframe_count; + int _audio_upstream_channel; + int _last_index_irq; /* The last interrupt index processed. */ + + __le32 *_risc_audio_jmp_addr; + __le32 *_risc_virt_start_addr; + __le32 *_risc_virt_addr; + dma_addr_t _risc_phys_addr; + dma_addr_t _risc_phys_start_addr; + + unsigned int _audiorisc_size; + unsigned int _audiodata_buf_size; + __le32 *_audiodata_buf_virt_addr; + dma_addr_t _audiodata_buf_phys_addr; + char *_audiofilename; + + /* V4l */ + u32 freq; + struct video_device *vbi_dev; + struct video_device *radio_dev; + struct video_device *ioctl_dev; + + spinlock_t slock; + + /* Video Upstream */ + int _line_size; + int _prog_cnt; + int _pixel_format; + int _is_first_frame; + int _is_running; + int _file_status; + int _lines_count; + int _frame_count; + int _channel_upstream_select; + unsigned int _risc_size; + + __le32 *_dma_virt_start_addr; + __le32 *_dma_virt_addr; + dma_addr_t _dma_phys_addr; + dma_addr_t _dma_phys_start_addr; + + unsigned int _data_buf_size; + __le32 *_data_buf_virt_addr; + dma_addr_t _data_buf_phys_addr; + char *_filename; + char *_defaultname; + + int _line_size_ch2; + int _prog_cnt_ch2; + int _pixel_format_ch2; + int _is_first_frame_ch2; + int _is_running_ch2; + int _file_status_ch2; + int _lines_count_ch2; + int _frame_count_ch2; + int _channel2_upstream_select; + unsigned int _risc_size_ch2; + + __le32 *_dma_virt_start_addr_ch2; + __le32 *_dma_virt_addr_ch2; + dma_addr_t _dma_phys_addr_ch2; + dma_addr_t _dma_phys_start_addr_ch2; + + unsigned int _data_buf_size_ch2; + __le32 *_data_buf_virt_addr_ch2; + dma_addr_t _data_buf_phys_addr_ch2; + char *_filename_ch2; + char *_defaultname_ch2; + + /* MPEG Encoder ONLY settings */ + u32 cx23417_mailbox; + struct cx2341x_mpeg_params mpeg_params; + struct video_device *v4l_device; + atomic_t v4l_reader_count; + struct cx25821_tvnorm encodernorm; + + u32 upstream_riscbuf_size; + u32 upstream_databuf_size; + u32 upstream_riscbuf_size_ch2; + u32 upstream_databuf_size_ch2; + u32 audio_upstream_riscbuf_size; + u32 audio_upstream_databuf_size; + int _isNTSC; + int _frame_index; + int _audioframe_index; + struct workqueue_struct *_irq_queues; + struct work_struct _irq_work_entry; + struct workqueue_struct *_irq_queues_ch2; + struct work_struct _irq_work_entry_ch2; + struct workqueue_struct *_irq_audio_queues; + struct work_struct _audio_work_entry; + char *input_filename; + char *input_filename_ch2; + int _frame_index_ch2; + int _isNTSC_ch2; + char *vid_stdname_ch2; + int pixel_format_ch2; + int channel_select_ch2; + int command_ch2; + char *input_audiofilename; + char *vid_stdname; + int pixel_format; + int channel_select; + int command; + int channel_opened; +}; + +struct upstream_user_struct { + char *input_filename; + char *vid_stdname; + int pixel_format; + int channel_select; + int command; +}; + +struct downstream_user_struct { + char *vid_stdname; + int pixel_format; + int cif_resolution_enable; + int cif_width; + int decoder_select; + int command; + int reg_address; + int reg_data; +}; + +extern struct upstream_user_struct *up_data; + +static inline struct cx25821_dev *get_cx25821(struct v4l2_device *v4l2_dev) +{ + return container_of(v4l2_dev, struct cx25821_dev, v4l2_dev); +} + +#define cx25821_call_all(dev, o, f, args...) \ + v4l2_device_call_all(&dev->v4l2_dev, 0, o, f, ##args) + +extern struct list_head cx25821_devlist; +extern struct mutex cx25821_devlist_mutex; + +extern struct cx25821_board cx25821_boards[]; +extern struct cx25821_subid cx25821_subids[]; + +#define SRAM_CH00 0 /* Video A */ +#define SRAM_CH01 1 /* Video B */ +#define SRAM_CH02 2 /* Video C */ +#define SRAM_CH03 3 /* Video D */ +#define SRAM_CH04 4 /* Video E */ +#define SRAM_CH05 5 /* Video F */ +#define SRAM_CH06 6 /* Video G */ +#define SRAM_CH07 7 /* Video H */ + +#define SRAM_CH08 8 /* Audio A */ +#define SRAM_CH09 9 /* Video Upstream I */ +#define SRAM_CH10 10 /* Video Upstream J */ +#define SRAM_CH11 11 /* Audio Upstream AUD_CHANNEL_B */ + +#define VID_UPSTREAM_SRAM_CHANNEL_I SRAM_CH09 +#define VID_UPSTREAM_SRAM_CHANNEL_J SRAM_CH10 +#define AUDIO_UPSTREAM_SRAM_CHANNEL_B SRAM_CH11 +#define VIDEO_IOCTL_CH 11 + +struct sram_channel { + char *name; + u32 i; + u32 cmds_start; + u32 ctrl_start; + u32 cdt; + u32 fifo_start; + u32 fifo_size; + u32 ptr1_reg; + u32 ptr2_reg; + u32 cnt1_reg; + u32 cnt2_reg; + u32 int_msk; + u32 int_stat; + u32 int_mstat; + u32 dma_ctl; + u32 gpcnt_ctl; + u32 gpcnt; + u32 aud_length; + u32 aud_cfg; + u32 fld_aud_fifo_en; + u32 fld_aud_risc_en; + + /* For Upstream Video */ + u32 vid_fmt_ctl; + u32 vid_active_ctl1; + u32 vid_active_ctl2; + u32 vid_cdt_size; + + u32 vip_ctl; + u32 pix_frmt; + u32 jumponly; + u32 irq_bit; +}; +extern struct sram_channel cx25821_sram_channels[]; + +#define STATUS_SUCCESS 0 +#define STATUS_UNSUCCESSFUL -1 + +#define cx_read(reg) readl(dev->lmmio + ((reg)>>2)) +#define cx_write(reg, value) writel((value), dev->lmmio + ((reg)>>2)) + +#define cx_andor(reg, mask, value) \ + writel((readl(dev->lmmio+((reg)>>2)) & ~(mask)) |\ + ((value) & (mask)), dev->lmmio+((reg)>>2)) + +#define cx_set(reg, bit) cx_andor((reg), (bit), (bit)) +#define cx_clear(reg, bit) cx_andor((reg), (bit), 0) + +#define Set_GPIO_Bit(Bit) (1 << Bit) +#define Clear_GPIO_Bit(Bit) (~(1 << Bit)) + +#define CX25821_ERR(fmt, args...) \ + pr_err("(%d): " fmt, dev->board, ##args) +#define CX25821_WARN(fmt, args...) \ + pr_warn("(%d): " fmt, dev->board, ##args) +#define CX25821_INFO(fmt, args...) \ + pr_info("(%d): " fmt, dev->board, ##args) + +extern int cx25821_i2c_register(struct cx25821_i2c *bus); +extern void cx25821_card_setup(struct cx25821_dev *dev); +extern int cx25821_ir_init(struct cx25821_dev *dev); +extern int cx25821_i2c_read(struct cx25821_i2c *bus, u16 reg_addr, int *value); +extern int cx25821_i2c_write(struct cx25821_i2c *bus, u16 reg_addr, int value); +extern int cx25821_i2c_unregister(struct cx25821_i2c *bus); +extern void cx25821_gpio_init(struct cx25821_dev *dev); +extern void cx25821_set_gpiopin_direction(struct cx25821_dev *dev, + int pin_number, int pin_logic_value); + +extern int medusa_video_init(struct cx25821_dev *dev); +extern int medusa_set_videostandard(struct cx25821_dev *dev); +extern void medusa_set_resolution(struct cx25821_dev *dev, int width, + int decoder_select); +extern int medusa_set_brightness(struct cx25821_dev *dev, int brightness, + int decoder); +extern int medusa_set_contrast(struct cx25821_dev *dev, int contrast, + int decoder); +extern int medusa_set_hue(struct cx25821_dev *dev, int hue, int decoder); +extern int medusa_set_saturation(struct cx25821_dev *dev, int saturation, + int decoder); + +extern int cx25821_sram_channel_setup(struct cx25821_dev *dev, + struct sram_channel *ch, unsigned int bpl, + u32 risc); + +extern int cx25821_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc, + struct scatterlist *sglist, + unsigned int top_offset, + unsigned int bottom_offset, + unsigned int bpl, + unsigned int padding, unsigned int lines); +extern int cx25821_risc_databuffer_audio(struct pci_dev *pci, + struct btcx_riscmem *risc, + struct scatterlist *sglist, + unsigned int bpl, + unsigned int lines, unsigned int lpi); +extern void cx25821_free_buffer(struct videobuf_queue *q, + struct cx25821_buffer *buf); +extern int cx25821_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc, + u32 reg, u32 mask, u32 value); +extern void cx25821_sram_channel_dump(struct cx25821_dev *dev, + struct sram_channel *ch); +extern void cx25821_sram_channel_dump_audio(struct cx25821_dev *dev, + struct sram_channel *ch); + +extern struct cx25821_dev *cx25821_dev_get(struct pci_dev *pci); +extern void cx25821_print_irqbits(char *name, char *tag, char **strings, + int len, u32 bits, u32 mask); +extern void cx25821_dev_unregister(struct cx25821_dev *dev); +extern int cx25821_sram_channel_setup_audio(struct cx25821_dev *dev, + struct sram_channel *ch, + unsigned int bpl, u32 risc); + +extern int cx25821_vidupstream_init_ch1(struct cx25821_dev *dev, + int channel_select, int pixel_format); +extern int cx25821_vidupstream_init_ch2(struct cx25821_dev *dev, + int channel_select, int pixel_format); +extern int cx25821_audio_upstream_init(struct cx25821_dev *dev, + int channel_select); +extern void cx25821_free_mem_upstream_ch1(struct cx25821_dev *dev); +extern void cx25821_free_mem_upstream_ch2(struct cx25821_dev *dev); +extern void cx25821_free_mem_upstream_audio(struct cx25821_dev *dev); +extern void cx25821_start_upstream_video_ch1(struct cx25821_dev *dev, + struct upstream_user_struct + *up_data); +extern void cx25821_start_upstream_video_ch2(struct cx25821_dev *dev, + struct upstream_user_struct + *up_data); +extern void cx25821_start_upstream_audio(struct cx25821_dev *dev, + struct upstream_user_struct *up_data); +extern void cx25821_stop_upstream_video_ch1(struct cx25821_dev *dev); +extern void cx25821_stop_upstream_video_ch2(struct cx25821_dev *dev); +extern void cx25821_stop_upstream_audio(struct cx25821_dev *dev); +extern int cx25821_sram_channel_setup_upstream(struct cx25821_dev *dev, + struct sram_channel *ch, + unsigned int bpl, u32 risc); +extern void cx25821_set_pixel_format(struct cx25821_dev *dev, int channel, + u32 format); +extern void cx25821_videoioctl_unregister(struct cx25821_dev *dev); +extern struct video_device *cx25821_vdev_init(struct cx25821_dev *dev, + struct pci_dev *pci, + struct video_device *template, + char *type); +#endif diff --git a/drivers/media/video/cx25840/Makefile b/drivers/media/video/cx25840/Makefile index 2ee96d3973b..dc40dde2e0c 100644 --- a/drivers/media/video/cx25840/Makefile +++ b/drivers/media/video/cx25840/Makefile @@ -3,4 +3,4 @@ cx25840-objs := cx25840-core.o cx25840-audio.o cx25840-firmware.o \ obj-$(CONFIG_VIDEO_CX25840) += cx25840.o -EXTRA_CFLAGS += -Idrivers/media/video +ccflags-y += -Idrivers/media/video diff --git a/drivers/media/video/cx25840/cx25840-audio.c b/drivers/media/video/cx25840/cx25840-audio.c index 34b96c7cfd6..005f1109364 100644 --- a/drivers/media/video/cx25840/cx25840-audio.c +++ b/drivers/media/video/cx25840/cx25840-audio.c @@ -480,6 +480,7 @@ void cx25840_audio_set_path(struct i2c_client *client) static void set_volume(struct i2c_client *client, int volume) { + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); int vol; /* Convert the volume to msp3400 values (0-127) */ @@ -495,7 +496,14 @@ static void set_volume(struct i2c_client *client, int volume) } /* PATH1_VOLUME */ - cx25840_write(client, 0x8d4, 228 - (vol * 2)); + if (is_cx2388x(state)) { + /* for cx23885 volume doesn't work, + * the calculation always results in + * e4 regardless. + */ + cx25840_write(client, 0x8d4, volume); + } else + cx25840_write(client, 0x8d4, 228 - (vol * 2)); } static void set_balance(struct i2c_client *client, int balance) diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c index b7ee2ae7058..cd9976408ab 100644 --- a/drivers/media/video/cx25840/cx25840-core.c +++ b/drivers/media/video/cx25840/cx25840-core.c @@ -702,6 +702,13 @@ static void cx231xx_initialize(struct i2c_client *client) /* start microcontroller */ cx25840_and_or(client, 0x803, ~0x10, 0x10); + + /* CC raw enable */ + cx25840_write(client, 0x404, 0x0b); + + /* CC on */ + cx25840_write(client, 0x42f, 0x66); + cx25840_write4(client, 0x474, 0x1e1e601a); } /* ----------------------------------------------------------------------- */ @@ -1067,6 +1074,18 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp cx25840_write(client, 0x919, 0x01); } + if (is_cx2388x(state) && ((aud_input == CX25840_AUDIO7) || + (aud_input == CX25840_AUDIO6))) { + /* Configure audio from LR1 or LR2 input */ + cx25840_write4(client, 0x910, 0); + cx25840_write4(client, 0x8d0, 0x63073); + } else + if (is_cx2388x(state) && (aud_input == CX25840_AUDIO8)) { + /* Configure audio from tuner/sif input */ + cx25840_write4(client, 0x910, 0x12b000c9); + cx25840_write4(client, 0x8d0, 0x1f063870); + } + return 0; } diff --git a/drivers/media/video/cx25840/cx25840-ir.c b/drivers/media/video/cx25840/cx25840-ir.c index 7eb79af28aa..13c380ebb56 100644 --- a/drivers/media/video/cx25840/cx25840-ir.c +++ b/drivers/media/video/cx25840/cx25840-ir.c @@ -23,6 +23,7 @@ #include <linux/slab.h> #include <linux/kfifo.h> +#include <linux/module.h> #include <media/cx25840.h> #include <media/rc-core.h> @@ -668,7 +669,7 @@ static int cx25840_ir_rx_read(struct v4l2_subdev *sd, u8 *buf, size_t count, u16 divider; unsigned int i, n; union cx25840_ir_fifo_rec *p; - unsigned u, v; + unsigned u, v, w; if (ir_state == NULL) return -ENODEV; @@ -694,11 +695,12 @@ static int cx25840_ir_rx_read(struct v4l2_subdev *sd, u8 *buf, size_t count, if ((p->hw_fifo_data & FIFO_RXTX_RTO) == FIFO_RXTX_RTO) { /* Assume RTO was because of no IR light input */ u = 0; - v4l2_dbg(2, ir_debug, sd, "rx read: end of rx\n"); + w = 1; } else { u = (p->hw_fifo_data & FIFO_RXTX_LVL) ? 1 : 0; if (invert) u = u ? 0 : 1; + w = 0; } v = (unsigned) pulse_width_count_to_ns( @@ -709,9 +711,12 @@ static int cx25840_ir_rx_read(struct v4l2_subdev *sd, u8 *buf, size_t count, init_ir_raw_event(&p->ir_core_data); p->ir_core_data.pulse = u; p->ir_core_data.duration = v; + p->ir_core_data.timeout = w; - v4l2_dbg(2, ir_debug, sd, "rx read: %10u ns %s\n", - v, u ? "mark" : "space"); + v4l2_dbg(2, ir_debug, sd, "rx read: %10u ns %s %s\n", + v, u ? "mark" : "space", w ? "(timed out)" : ""); + if (w) + v4l2_dbg(2, ir_debug, sd, "rx read: end of rx\n"); } return 0; } diff --git a/drivers/media/video/cx88/Makefile b/drivers/media/video/cx88/Makefile index 5b7e26761f0..c1a2785ba24 100644 --- a/drivers/media/video/cx88/Makefile +++ b/drivers/media/video/cx88/Makefile @@ -10,7 +10,7 @@ obj-$(CONFIG_VIDEO_CX88_BLACKBIRD) += cx88-blackbird.o obj-$(CONFIG_VIDEO_CX88_DVB) += cx88-dvb.o obj-$(CONFIG_VIDEO_CX88_VP3054) += cx88-vp3054-i2c.o -EXTRA_CFLAGS += -Idrivers/media/video -EXTRA_CFLAGS += -Idrivers/media/common/tuners -EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core -EXTRA_CFLAGS += -Idrivers/media/dvb/frontends +ccflags-y += -Idrivers/media/video +ccflags-y += -Idrivers/media/common/tuners +ccflags-y += -Idrivers/media/dvb/dvb-core +ccflags-y += -Idrivers/media/dvb/frontends diff --git a/drivers/media/video/cx88/cx88-core.c b/drivers/media/video/cx88/cx88-core.c index fbcaa1c5b09..fbfdd806793 100644 --- a/drivers/media/video/cx88/cx88-core.c +++ b/drivers/media/video/cx88/cx88-core.c @@ -636,9 +636,6 @@ int cx88_reset(struct cx88_core *core) cx_write(MO_PCI_INTSTAT, 0xFFFFFFFF); // Clear PCI int cx_write(MO_INT1_STAT, 0xFFFFFFFF); // Clear RISC int - /* set default notch filter */ - cx_andor(MO_HTOTAL, 0x1800, (HLNotchFilter4xFsc << 11)); - /* Reset on-board parts */ cx_write(MO_SRST_IO, 0); msleep(10); diff --git a/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c index 60d28fdd779..921c56d115d 100644 --- a/drivers/media/video/cx88/cx88-video.c +++ b/drivers/media/video/cx88/cx88-video.c @@ -266,7 +266,7 @@ static const struct cx88_ctrl cx8800_ctls[] = { .id = V4L2_CID_BAND_STOP_FILTER, .name = "Notch filter", .minimum = 0, - .maximum = 3, + .maximum = 1, .step = 1, .default_value = 0x0, .type = V4L2_CTRL_TYPE_INTEGER, diff --git a/drivers/media/video/davinci/dm355_ccdc.c b/drivers/media/video/davinci/dm355_ccdc.c index c29ac88ffd7..bd443ee76ff 100644 --- a/drivers/media/video/davinci/dm355_ccdc.c +++ b/drivers/media/video/davinci/dm355_ccdc.c @@ -39,6 +39,7 @@ #include <linux/videodev2.h> #include <linux/clk.h> #include <linux/err.h> +#include <linux/module.h> #include <media/davinci/dm355_ccdc.h> #include <media/davinci/vpss.h> diff --git a/drivers/media/video/davinci/dm644x_ccdc.c b/drivers/media/video/davinci/dm644x_ccdc.c index c8b32c1c738..8051c295647 100644 --- a/drivers/media/video/davinci/dm644x_ccdc.c +++ b/drivers/media/video/davinci/dm644x_ccdc.c @@ -40,6 +40,7 @@ #include <linux/gfp.h> #include <linux/clk.h> #include <linux/err.h> +#include <linux/module.h> #include <media/davinci/dm644x_ccdc.h> #include <media/davinci/vpss.h> diff --git a/drivers/media/video/davinci/vpbe_display.c b/drivers/media/video/davinci/vpbe_display.c index 7f1d83a6d57..8588a86d9b4 100644 --- a/drivers/media/video/davinci/vpbe_display.c +++ b/drivers/media/video/davinci/vpbe_display.c @@ -43,7 +43,6 @@ static int debug; -#define VPBE_DISPLAY_SD_BUF_SIZE (720*576*2) #define VPBE_DEFAULT_NUM_BUFS 3 module_param(debug, int, 0644); diff --git a/drivers/media/video/davinci/vpbe_osd.c b/drivers/media/video/davinci/vpbe_osd.c index 5352884998f..ceccf430251 100644 --- a/drivers/media/video/davinci/vpbe_osd.c +++ b/drivers/media/video/davinci/vpbe_osd.c @@ -1162,7 +1162,7 @@ static int osd_probe(struct platform_device *pdev) goto free_mem; } osd->osd_base_phys = res->start; - osd->osd_size = res->end - res->start + 1; + osd->osd_size = resource_size(res); if (!request_mem_region(osd->osd_base_phys, osd->osd_size, MODULE_NAME)) { dev_err(osd->dev, "Unable to reserve OSD MMIO region\n"); diff --git a/drivers/media/video/em28xx/Kconfig b/drivers/media/video/em28xx/Kconfig index 281ee427c2a..f6f622e123b 100644 --- a/drivers/media/video/em28xx/Kconfig +++ b/drivers/media/video/em28xx/Kconfig @@ -41,6 +41,8 @@ config VIDEO_EM28XX_DVB select DVB_CXD2820R if !DVB_FE_CUSTOMISE select DVB_DRXK if !DVB_FE_CUSTOMISE select DVB_TDA18271C2DD if !DVB_FE_CUSTOMISE + select DVB_TDA10071 if !DVB_FE_CUSTOMISE + select DVB_A8293 if !DVB_FE_CUSTOMISE select VIDEOBUF_DVB ---help--- This adds support for DVB cards based on the diff --git a/drivers/media/video/em28xx/Makefile b/drivers/media/video/em28xx/Makefile index 38aaa004f57..2abdf76c520 100644 --- a/drivers/media/video/em28xx/Makefile +++ b/drivers/media/video/em28xx/Makefile @@ -9,8 +9,8 @@ obj-$(CONFIG_VIDEO_EM28XX) += em28xx.o obj-$(CONFIG_VIDEO_EM28XX_ALSA) += em28xx-alsa.o obj-$(CONFIG_VIDEO_EM28XX_DVB) += em28xx-dvb.o -EXTRA_CFLAGS += -Idrivers/media/video -EXTRA_CFLAGS += -Idrivers/media/common/tuners -EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core -EXTRA_CFLAGS += -Idrivers/media/dvb/frontends +ccflags-y += -Idrivers/media/video +ccflags-y += -Idrivers/media/common/tuners +ccflags-y += -Idrivers/media/dvb/dvb-core +ccflags-y += -Idrivers/media/dvb/frontends diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index 3e3959fee41..9b747c266af 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -60,7 +60,7 @@ static unsigned int card[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; module_param_array(card, int, NULL, 0444); MODULE_PARM_DESC(card, "card type"); -/* Bitmask marking allocated devices from 0 to EM28XX_MAXBOARDS */ +/* Bitmask marking allocated devices from 0 to EM28XX_MAXBOARDS - 1 */ static unsigned long em28xx_devused; struct em28xx_hash_table { @@ -317,6 +317,25 @@ static struct em28xx_reg_seq terratec_h5_digital[] = { }; #endif +/* 2013:024f PCTV DVB-S2 Stick 460e + * GPIO_0 - POWER_ON + * GPIO_1 - BOOST + * GPIO_2 - VUV_LNB (red LED) + * GPIO_3 - EXT_12V + * GPIO_4 - INT_DEM (DEMOD GPIO_0) + * GPIO_5 - INT_LNB + * GPIO_6 - RESET_DEM + * GPIO_7 - LED (green LED) + */ +static struct em28xx_reg_seq pctv_460e[] = { + {EM2874_R80_GPIO, 0x01, 0xff, 50}, + {0x0d, 0xff, 0xff, 50}, + {EM2874_R80_GPIO, 0x41, 0xff, 50}, /* GPIO_6=1 */ + {0x0d, 0x42, 0xff, 50}, + {EM2874_R80_GPIO, 0x61, 0xff, 50}, /* GPIO_5=1 */ + { -1, -1, -1, -1}, +}; + /* * Board definitions */ @@ -1810,6 +1829,17 @@ struct em28xx_board em28xx_boards[] = { .has_dvb = 1, .ir_codes = RC_MAP_PINNACLE_PCTV_HD, }, + /* 2013:024f PCTV DVB-S2 Stick 460e + * Empia EM28174, NXP TDA10071, Conexant CX24118A and Allegro A8293 */ + [EM28174_BOARD_PCTV_460E] = { + .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT | + EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ, + .name = "PCTV DVB-S2 Stick (460e)", + .tuner_type = TUNER_ABSENT, + .tuner_gpio = pctv_460e, + .has_dvb = 1, + .ir_codes = RC_MAP_PINNACLE_PCTV_HD, + }, }; const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards); @@ -1893,6 +1923,8 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM2860_BOARD_TERRATEC_AV350 }, { USB_DEVICE(0x0ccd, 0x0096), .driver_info = EM2860_BOARD_TERRATEC_GRABBY }, + { USB_DEVICE(0x0ccd, 0x10AF), + .driver_info = EM2860_BOARD_TERRATEC_GRABBY }, { USB_DEVICE(0x0fd9, 0x0033), .driver_info = EM2860_BOARD_ELGATO_VIDEO_CAPTURE}, { USB_DEVICE(0x185b, 0x2870), @@ -1941,6 +1973,8 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM2870_BOARD_KWORLD_A340 }, { USB_DEVICE(0x2013, 0x024f), .driver_info = EM28174_BOARD_PCTV_290E }, + { USB_DEVICE(0x2013, 0x024c), + .driver_info = EM28174_BOARD_PCTV_460E }, { }, }; MODULE_DEVICE_TABLE(usb, em28xx_id_table); @@ -2768,9 +2802,9 @@ static void flush_request_modules(struct em28xx *dev) #endif /* CONFIG_MODULES */ /* - * em28xx_realease_resources() + * em28xx_release_resources() * unregisters the v4l2,i2c and usb devices - * called when the device gets disconected or at module unload + * called when the device gets disconnected or at module unload */ void em28xx_release_resources(struct em28xx *dev) { @@ -2784,8 +2818,6 @@ void em28xx_release_resources(struct em28xx *dev) em28xx_release_analog_resources(dev); - em28xx_remove_from_devlist(dev); - em28xx_i2c_unregister(dev); v4l2_device_unregister(&dev->v4l2_dev); @@ -2793,7 +2825,7 @@ void em28xx_release_resources(struct em28xx *dev) usb_put_dev(dev->udev); /* Mark device as unused */ - em28xx_devused &= ~(1 << dev->devno); + clear_bit(dev->devno, &em28xx_devused); }; /* @@ -2806,7 +2838,6 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, { struct em28xx *dev = *devhandle; int retval; - int errCode; dev->udev = udev; mutex_init(&dev->ctrl_urb_lock); @@ -2883,10 +2914,9 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, } if (dev->is_audio_only) { - errCode = em28xx_audio_setup(dev); - if (errCode) + retval = em28xx_audio_setup(dev); + if (retval) return -ENODEV; - em28xx_add_into_devlist(dev); em28xx_init_extension(dev); return 0; @@ -2903,7 +2933,7 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, /* Resets I2C speed */ em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, dev->board.i2c_speed); if (retval < 0) { - em28xx_errdev("%s: em28xx_write_regs_req failed!" + em28xx_errdev("%s: em28xx_write_reg failed!" " retval [%d]\n", __func__, retval); return retval; @@ -2917,12 +2947,11 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, } /* register i2c bus */ - errCode = em28xx_i2c_register(dev); - if (errCode < 0) { - v4l2_device_unregister(&dev->v4l2_dev); - em28xx_errdev("%s: em28xx_i2c_register - errCode [%d]!\n", - __func__, errCode); - return errCode; + retval = em28xx_i2c_register(dev); + if (retval < 0) { + em28xx_errdev("%s: em28xx_i2c_register - error [%d]!\n", + __func__, retval); + goto unregister_dev; } /* @@ -2936,11 +2965,11 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, em28xx_card_setup(dev); /* Configure audio */ - errCode = em28xx_audio_setup(dev); - if (errCode < 0) { - v4l2_device_unregister(&dev->v4l2_dev); - em28xx_errdev("%s: Error while setting audio - errCode [%d]!\n", - __func__, errCode); + retval = em28xx_audio_setup(dev); + if (retval < 0) { + em28xx_errdev("%s: Error while setting audio - error [%d]!\n", + __func__, retval); + goto fail; } /* wake i2c devices */ @@ -2954,41 +2983,41 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, if (dev->board.has_msp34xx) { /* Send a reset to other chips via gpio */ - errCode = em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xf7); - if (errCode < 0) { - em28xx_errdev("%s: em28xx_write_regs_req - " - "msp34xx(1) failed! errCode [%d]\n", - __func__, errCode); - return errCode; + retval = em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xf7); + if (retval < 0) { + em28xx_errdev("%s: em28xx_write_reg - " + "msp34xx(1) failed! error [%d]\n", + __func__, retval); + goto fail; } msleep(3); - errCode = em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xff); - if (errCode < 0) { - em28xx_errdev("%s: em28xx_write_regs_req - " - "msp34xx(2) failed! errCode [%d]\n", - __func__, errCode); - return errCode; + retval = em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xff); + if (retval < 0) { + em28xx_errdev("%s: em28xx_write_reg - " + "msp34xx(2) failed! error [%d]\n", + __func__, retval); + goto fail; } msleep(3); } - em28xx_add_into_devlist(dev); - retval = em28xx_register_analog_devices(dev); if (retval < 0) { - em28xx_release_resources(dev); - goto fail_reg_devices; + goto fail; } - em28xx_init_extension(dev); - /* Save some power by putting tuner to sleep */ v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0); return 0; -fail_reg_devices: +fail: + em28xx_i2c_unregister(dev); + +unregister_dev: + v4l2_device_unregister(&dev->v4l2_dev); + return retval; } @@ -3015,8 +3044,16 @@ static int em28xx_usb_probe(struct usb_interface *interface, udev = usb_get_dev(interface_to_usbdev(interface)); /* Check to see next free device and mark as used */ - nr = find_first_zero_bit(&em28xx_devused, EM28XX_MAXBOARDS); - em28xx_devused |= 1<<nr; + do { + nr = find_first_zero_bit(&em28xx_devused, EM28XX_MAXBOARDS); + if (nr >= EM28XX_MAXBOARDS) { + /* No free device slots */ + printk(DRIVER_NAME ": Supports only %i em28xx boards.\n", + EM28XX_MAXBOARDS); + retval = -ENOMEM; + goto err_no_slot; + } + } while (test_and_set_bit(nr, &em28xx_devused)); /* Don't register audio interfaces */ if (interface->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) { @@ -3027,7 +3064,6 @@ static int em28xx_usb_probe(struct usb_interface *interface, ifnum, interface->altsetting[0].desc.bInterfaceClass); - em28xx_devused &= ~(1<<nr); retval = -ENODEV; goto err; } @@ -3076,7 +3112,6 @@ static int em28xx_usb_probe(struct usb_interface *interface, em28xx_err(DRIVER_NAME " This is an anciliary " "interface not used by the driver\n"); - em28xx_devused &= ~(1<<nr); retval = -ENODEV; goto err; } @@ -3132,29 +3167,19 @@ static int em28xx_usb_probe(struct usb_interface *interface, printk(DRIVER_NAME ": Device initialization failed.\n"); printk(DRIVER_NAME ": Device must be connected to a high-speed" " USB 2.0 port.\n"); - em28xx_devused &= ~(1<<nr); retval = -ENODEV; goto err; } - if (nr >= EM28XX_MAXBOARDS) { - printk(DRIVER_NAME ": Supports only %i em28xx boards.\n", - EM28XX_MAXBOARDS); - em28xx_devused &= ~(1<<nr); - retval = -ENOMEM; - goto err; - } - /* allocate memory for our device state and initialize it */ dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (dev == NULL) { em28xx_err(DRIVER_NAME ": out of memory!\n"); - em28xx_devused &= ~(1<<nr); retval = -ENOMEM; goto err; } - snprintf(dev->name, 29, "em28xx #%d", nr); + snprintf(dev->name, sizeof(dev->name), "em28xx #%d", nr); dev->devno = nr; dev->model = id->driver_info; dev->alt = -1; @@ -3177,7 +3202,6 @@ static int em28xx_usb_probe(struct usb_interface *interface, if (dev->alt_max_pkt_size == NULL) { em28xx_errdev("out of memory!\n"); - em28xx_devused &= ~(1<<nr); kfree(dev); retval = -ENOMEM; goto err; @@ -3204,8 +3228,8 @@ static int em28xx_usb_probe(struct usb_interface *interface, mutex_lock(&dev->lock); retval = em28xx_init_dev(&dev, udev, interface, nr); if (retval) { - em28xx_devused &= ~(1<<dev->devno); mutex_unlock(&dev->lock); + kfree(dev->alt_max_pkt_size); kfree(dev); goto err; } @@ -3217,15 +3241,26 @@ static int em28xx_usb_probe(struct usb_interface *interface, */ mutex_unlock(&dev->lock); + /* + * These extensions can be modules. If the modules are already + * loaded then we can initialise the device now, otherwise we + * will initialise it when the modules load instead. + */ + em28xx_init_extension(dev); + return 0; err: + clear_bit(nr, &em28xx_devused); + +err_no_slot: + usb_put_dev(udev); return retval; } /* * em28xx_usb_disconnect() - * called when the device gets diconencted + * called when the device gets disconnected * video device will be unregistered on v4l2_close in case it is still open */ static void em28xx_usb_disconnect(struct usb_interface *interface) @@ -3273,10 +3308,10 @@ static void em28xx_usb_disconnect(struct usb_interface *interface) em28xx_release_resources(dev); } - em28xx_close_extension(dev); - mutex_unlock(&dev->lock); + em28xx_close_extension(dev); + if (!dev->users) { kfree(dev->alt_max_pkt_size); kfree(dev); diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c index 57b1b5c6d88..804a4ab47ac 100644 --- a/drivers/media/video/em28xx/em28xx-core.c +++ b/drivers/media/video/em28xx/em28xx-core.c @@ -1184,25 +1184,6 @@ static LIST_HEAD(em28xx_devlist); static DEFINE_MUTEX(em28xx_devlist_mutex); /* - * em28xx_realease_resources() - * unregisters the v4l2,i2c and usb devices - * called when the device gets disconected or at module unload -*/ -void em28xx_remove_from_devlist(struct em28xx *dev) -{ - mutex_lock(&em28xx_devlist_mutex); - list_del(&dev->devlist); - mutex_unlock(&em28xx_devlist_mutex); -}; - -void em28xx_add_into_devlist(struct em28xx *dev) -{ - mutex_lock(&em28xx_devlist_mutex); - list_add_tail(&dev->devlist, &em28xx_devlist); - mutex_unlock(&em28xx_devlist_mutex); -}; - -/* * Extension interface */ @@ -1217,8 +1198,8 @@ int em28xx_register_extension(struct em28xx_ops *ops) list_for_each_entry(dev, &em28xx_devlist, devlist) { ops->init(dev); } - printk(KERN_INFO "Em28xx: Initialized (%s) extension\n", ops->name); mutex_unlock(&em28xx_devlist_mutex); + printk(KERN_INFO "Em28xx: Initialized (%s) extension\n", ops->name); return 0; } EXPORT_SYMBOL(em28xx_register_extension); @@ -1231,36 +1212,34 @@ void em28xx_unregister_extension(struct em28xx_ops *ops) list_for_each_entry(dev, &em28xx_devlist, devlist) { ops->fini(dev); } - printk(KERN_INFO "Em28xx: Removed (%s) extension\n", ops->name); list_del(&ops->next); mutex_unlock(&em28xx_devlist_mutex); + printk(KERN_INFO "Em28xx: Removed (%s) extension\n", ops->name); } EXPORT_SYMBOL(em28xx_unregister_extension); void em28xx_init_extension(struct em28xx *dev) { - struct em28xx_ops *ops = NULL; + const struct em28xx_ops *ops = NULL; mutex_lock(&em28xx_devlist_mutex); - if (!list_empty(&em28xx_extension_devlist)) { - list_for_each_entry(ops, &em28xx_extension_devlist, next) { - if (ops->init) - ops->init(dev); - } + list_add_tail(&dev->devlist, &em28xx_devlist); + list_for_each_entry(ops, &em28xx_extension_devlist, next) { + if (ops->init) + ops->init(dev); } mutex_unlock(&em28xx_devlist_mutex); } void em28xx_close_extension(struct em28xx *dev) { - struct em28xx_ops *ops = NULL; + const struct em28xx_ops *ops = NULL; mutex_lock(&em28xx_devlist_mutex); - if (!list_empty(&em28xx_extension_devlist)) { - list_for_each_entry(ops, &em28xx_extension_devlist, next) { - if (ops->fini) - ops->fini(dev); - } + list_for_each_entry(ops, &em28xx_extension_devlist, next) { + if (ops->fini) + ops->fini(dev); } + list_del(&dev->devlist); mutex_unlock(&em28xx_devlist_mutex); } diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c index e5916dee409..cef7a2d409c 100644 --- a/drivers/media/video/em28xx/em28xx-dvb.c +++ b/drivers/media/video/em28xx/em28xx-dvb.c @@ -42,6 +42,8 @@ #include "cxd2820r.h" #include "tda18271c2dd.h" #include "drxk.h" +#include "tda10071.h" +#include "a8293.h" MODULE_DESCRIPTION("driver for em28xx based DVB cards"); MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>"); @@ -122,7 +124,7 @@ static inline void print_err_status(struct em28xx *dev, } } -static inline int dvb_isoc_copy(struct em28xx *dev, struct urb *urb) +static inline int em28xx_dvb_isoc_copy(struct em28xx *dev, struct urb *urb) { int i; @@ -155,7 +157,7 @@ static inline int dvb_isoc_copy(struct em28xx *dev, struct urb *urb) return 0; } -static int start_streaming(struct em28xx_dvb *dvb) +static int em28xx_start_streaming(struct em28xx_dvb *dvb) { int rc; struct em28xx *dev = dvb->adapter.priv; @@ -175,10 +177,10 @@ static int start_streaming(struct em28xx_dvb *dvb) return em28xx_init_isoc(dev, EM28XX_DVB_MAX_PACKETS, EM28XX_DVB_NUM_BUFS, max_dvb_packet_size, - dvb_isoc_copy); + em28xx_dvb_isoc_copy); } -static int stop_streaming(struct em28xx_dvb *dvb) +static int em28xx_stop_streaming(struct em28xx_dvb *dvb) { struct em28xx *dev = dvb->adapter.priv; @@ -189,7 +191,7 @@ static int stop_streaming(struct em28xx_dvb *dvb) return 0; } -static int start_feed(struct dvb_demux_feed *feed) +static int em28xx_start_feed(struct dvb_demux_feed *feed) { struct dvb_demux *demux = feed->demux; struct em28xx_dvb *dvb = demux->priv; @@ -203,7 +205,7 @@ static int start_feed(struct dvb_demux_feed *feed) rc = dvb->nfeeds; if (dvb->nfeeds == 1) { - ret = start_streaming(dvb); + ret = em28xx_start_streaming(dvb); if (ret < 0) rc = ret; } @@ -212,7 +214,7 @@ static int start_feed(struct dvb_demux_feed *feed) return rc; } -static int stop_feed(struct dvb_demux_feed *feed) +static int em28xx_stop_feed(struct dvb_demux_feed *feed) { struct dvb_demux *demux = feed->demux; struct em28xx_dvb *dvb = demux->priv; @@ -222,7 +224,7 @@ static int stop_feed(struct dvb_demux_feed *feed) dvb->nfeeds--; if (0 == dvb->nfeeds) - err = stop_streaming(dvb); + err = em28xx_stop_streaming(dvb); mutex_unlock(&dvb->lock); return err; @@ -380,7 +382,7 @@ static void terratec_h5_init(struct em28xx *dev) em28xx_gpio_set(dev, terratec_h5_end); }; -static int mt352_terratec_xs_init(struct dvb_frontend *fe) +static int em28xx_mt352_terratec_xs_init(struct dvb_frontend *fe) { /* Values extracted from a USB trace of the Terratec Windows driver */ static u8 clock_config[] = { CLOCK_CTL, 0x38, 0x2c }; @@ -412,7 +414,7 @@ static struct mt352_config terratec_xs_mt352_cfg = { .demod_address = (0x1e >> 1), .no_tuner = 1, .if2 = 45600, - .demod_init = mt352_terratec_xs_init, + .demod_init = em28xx_mt352_terratec_xs_init, }; static struct tda10023_config em28xx_tda10023_config = { @@ -438,11 +440,25 @@ static struct cxd2820r_config em28xx_cxd2820r_config = { static struct tda18271_config em28xx_cxd2820r_tda18271_config = { .output_opt = TDA18271_OUTPUT_LT_OFF, + .gate = TDA18271_GATE_DIGITAL, +}; + +static const struct tda10071_config em28xx_tda10071_config = { + .i2c_address = 0x55, /* (0xaa >> 1) */ + .i2c_wr_max = 64, + .ts_mode = TDA10071_TS_SERIAL, + .spec_inv = 0, + .xtal = 40444000, /* 40.444 MHz */ + .pll_multiplier = 20, +}; + +static const struct a8293_config em28xx_a8293_config = { + .i2c_addr = 0x08, /* (0x10 >> 1) */ }; /* ------------------------------------------------------------------ */ -static int attach_xc3028(u8 addr, struct em28xx *dev) +static int em28xx_attach_xc3028(u8 addr, struct em28xx *dev) { struct dvb_frontend *fe; struct xc2028_config cfg; @@ -472,10 +488,8 @@ static int attach_xc3028(u8 addr, struct em28xx *dev) /* ------------------------------------------------------------------ */ -static int register_dvb(struct em28xx_dvb *dvb, - struct module *module, - struct em28xx *dev, - struct device *device) +static int em28xx_register_dvb(struct em28xx_dvb *dvb, struct module *module, + struct em28xx *dev, struct device *device) { int result; @@ -522,8 +536,8 @@ static int register_dvb(struct em28xx_dvb *dvb, dvb->demux.priv = dvb; dvb->demux.filternum = 256; dvb->demux.feednum = 256; - dvb->demux.start_feed = start_feed; - dvb->demux.stop_feed = stop_feed; + dvb->demux.start_feed = em28xx_start_feed; + dvb->demux.stop_feed = em28xx_stop_feed; result = dvb_dmx_init(&dvb->demux); if (result < 0) { @@ -591,7 +605,7 @@ fail_adapter: return result; } -static void unregister_dvb(struct em28xx_dvb *dvb) +static void em28xx_unregister_dvb(struct em28xx_dvb *dvb) { dvb_net_release(&dvb->net); dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); @@ -607,9 +621,9 @@ static void unregister_dvb(struct em28xx_dvb *dvb) dvb_unregister_adapter(&dvb->adapter); } -static int dvb_init(struct em28xx *dev) +static int em28xx_dvb_init(struct em28xx *dev) { - int result = 0; + int result = 0, mfe_shared = 0; struct em28xx_dvb *dvb; if (!dev->board.has_dvb) { @@ -648,7 +662,7 @@ static int dvb_init(struct em28xx *dev) dvb->fe[0] = dvb_attach(lgdt330x_attach, &em2880_lgdt3303_dev, &dev->i2c_adap); - if (attach_xc3028(0x61, dev) < 0) { + if (em28xx_attach_xc3028(0x61, dev) < 0) { result = -EINVAL; goto out_free; } @@ -657,7 +671,7 @@ static int dvb_init(struct em28xx *dev) dvb->fe[0] = dvb_attach(zl10353_attach, &em28xx_zl10353_with_xc3028, &dev->i2c_adap); - if (attach_xc3028(0x61, dev) < 0) { + if (em28xx_attach_xc3028(0x61, dev) < 0) { result = -EINVAL; goto out_free; } @@ -668,7 +682,7 @@ static int dvb_init(struct em28xx *dev) dvb->fe[0] = dvb_attach(zl10353_attach, &em28xx_zl10353_xc3028_no_i2c_gate, &dev->i2c_adap); - if (attach_xc3028(0x61, dev) < 0) { + if (em28xx_attach_xc3028(0x61, dev) < 0) { result = -EINVAL; goto out_free; } @@ -689,7 +703,7 @@ static int dvb_init(struct em28xx *dev) &dev->i2c_adap); } - if (attach_xc3028(0x61, dev) < 0) { + if (em28xx_attach_xc3028(0x61, dev) < 0) { result = -EINVAL; goto out_free; } @@ -699,7 +713,7 @@ static int dvb_init(struct em28xx *dev) dvb->fe[0] = dvb_attach(s5h1409_attach, &em28xx_s5h1409_with_xc3028, &dev->i2c_adap); - if (attach_xc3028(0x61, dev) < 0) { + if (em28xx_attach_xc3028(0x61, dev) < 0) { result = -EINVAL; goto out_free; } @@ -720,7 +734,7 @@ static int dvb_init(struct em28xx *dev) case EM2882_BOARD_PINNACLE_HYBRID_PRO_330E: dvb->fe[0] = dvb_attach(drxd_attach, &em28xx_drxd, NULL, &dev->i2c_adap, &dev->udev->dev); - if (attach_xc3028(0x61, dev) < 0) { + if (em28xx_attach_xc3028(0x61, dev) < 0) { result = -EINVAL; goto out_free; } @@ -753,11 +767,9 @@ static int dvb_init(struct em28xx *dev) dvb->fe[0] = dvb_attach(cxd2820r_attach, &em28xx_cxd2820r_config, &dev->i2c_adap, NULL); if (dvb->fe[0]) { - struct i2c_adapter *i2c_tuner; - i2c_tuner = cxd2820r_get_tuner_i2c_adapter(dvb->fe[0]); /* FE 0 attach tuner */ if (!dvb_attach(tda18271_attach, dvb->fe[0], 0x60, - i2c_tuner, &em28xx_cxd2820r_tda18271_config)) { + &dev->i2c_adap, &em28xx_cxd2820r_tda18271_config)) { dvb_frontend_detach(dvb->fe[0]); result = -EINVAL; goto out_free; @@ -768,10 +780,12 @@ static int dvb_init(struct em28xx *dev) dvb->fe[1]->id = 1; /* FE 1 attach tuner */ if (!dvb_attach(tda18271_attach, dvb->fe[1], 0x60, - i2c_tuner, &em28xx_cxd2820r_tda18271_config)) { + &dev->i2c_adap, &em28xx_cxd2820r_tda18271_config)) { dvb_frontend_detach(dvb->fe[1]); /* leave FE 0 still active */ } + + mfe_shared = 1; } break; case EM2884_BOARD_TERRATEC_H5: @@ -809,6 +823,16 @@ static int dvb_init(struct em28xx *dev) sizeof(dvb->fe[0]->ops.tuner_ops)); break; + case EM28174_BOARD_PCTV_460E: + /* attach demod */ + dvb->fe[0] = dvb_attach(tda10071_attach, + &em28xx_tda10071_config, &dev->i2c_adap); + + /* attach SEC */ + if (dvb->fe[0]) + dvb_attach(a8293_attach, dvb->fe[0], &dev->i2c_adap, + &em28xx_a8293_config); + break; default: em28xx_errdev("/2: The frontend of your DVB/ATSC card" " isn't supported yet\n"); @@ -823,11 +847,14 @@ static int dvb_init(struct em28xx *dev) dvb->fe[0]->callback = em28xx_tuner_callback; /* register everything */ - result = register_dvb(dvb, THIS_MODULE, dev, &dev->udev->dev); + result = em28xx_register_dvb(dvb, THIS_MODULE, dev, &dev->udev->dev); if (result < 0) goto out_free; + /* MFE lock */ + dvb->adapter.mfe_shared = mfe_shared; + em28xx_info("Successfully loaded em28xx-dvb\n"); ret: em28xx_set_mode(dev, EM28XX_SUSPEND); @@ -840,7 +867,14 @@ out_free: goto ret; } -static int dvb_fini(struct em28xx *dev) +static inline void prevent_sleep(struct dvb_frontend_ops *ops) +{ + ops->set_voltage = NULL; + ops->sleep = NULL; + ops->tuner_ops.sleep = NULL; +} + +static int em28xx_dvb_fini(struct em28xx *dev) { if (!dev->board.has_dvb) { /* This device does not support the extension */ @@ -848,8 +882,19 @@ static int dvb_fini(struct em28xx *dev) } if (dev->dvb) { - unregister_dvb(dev->dvb); - kfree(dev->dvb); + struct em28xx_dvb *dvb = dev->dvb; + + if (dev->state & DEV_DISCONNECTED) { + /* We cannot tell the device to sleep + * once it has been unplugged. */ + if (dvb->fe[0]) + prevent_sleep(&dvb->fe[0]->ops); + if (dvb->fe[1]) + prevent_sleep(&dvb->fe[1]->ops); + } + + em28xx_unregister_dvb(dvb); + kfree(dvb); dev->dvb = NULL; } @@ -859,8 +904,8 @@ static int dvb_fini(struct em28xx *dev) static struct em28xx_ops dvb_ops = { .id = EM28XX_DVB, .name = "Em28xx dvb Extension", - .init = dvb_init, - .fini = dvb_fini, + .init = em28xx_dvb_init, + .fini = em28xx_dvb_fini, }; static int __init em28xx_dvb_register(void) diff --git a/drivers/media/video/em28xx/em28xx-input.c b/drivers/media/video/em28xx/em28xx-input.c index 5d12b14282e..679da480428 100644 --- a/drivers/media/video/em28xx/em28xx-input.c +++ b/drivers/media/video/em28xx/em28xx-input.c @@ -463,11 +463,11 @@ int em28xx_ir_fini(struct em28xx *dev) if (!ir) return 0; - em28xx_ir_stop(ir->rc); - rc_unregister_device(ir->rc); - kfree(ir); + if (ir->rc) + rc_unregister_device(ir->rc); /* done */ + kfree(ir); dev->ir = NULL; return 0; } diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c index d176dc0394e..9b4557a2f6d 100644 --- a/drivers/media/video/em28xx/em28xx-video.c +++ b/drivers/media/video/em28xx/em28xx-video.c @@ -1156,6 +1156,21 @@ static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm) return 0; } +static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *norm) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + v4l2_device_call_all(&dev->v4l2_dev, 0, video, querystd, norm); + + return 0; +} + static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm) { struct em28xx_fh *fh = priv; @@ -1787,6 +1802,45 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, return 0; } +static int vidioc_enum_framesizes(struct file *file, void *priv, + struct v4l2_frmsizeenum *fsize) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + struct em28xx_fmt *fmt; + unsigned int maxw = norm_maxw(dev); + unsigned int maxh = norm_maxh(dev); + + fmt = format_by_fourcc(fsize->pixel_format); + if (!fmt) { + em28xx_videodbg("Fourcc format (%08x) invalid.\n", + fsize->pixel_format); + return -EINVAL; + } + + if (dev->board.is_em2800) { + if (fsize->index > 1) + return -EINVAL; + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = maxw / (1 + fsize->index); + fsize->discrete.height = maxh / (1 + fsize->index); + return 0; + } + + if (fsize->index != 0) + return -EINVAL; + + /* Report a continuous range */ + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + fsize->stepwise.min_width = 48; + fsize->stepwise.min_height = 32; + fsize->stepwise.max_width = maxw; + fsize->stepwise.max_height = maxh; + fsize->stepwise.step_width = 1; + fsize->stepwise.step_height = 1; + return 0; +} + /* Sliced VBI ioctls */ static int vidioc_g_fmt_sliced_vbi_cap(struct file *file, void *priv, struct v4l2_format *f) @@ -2200,6 +2254,7 @@ static int em28xx_v4l2_close(struct file *filp) free the remaining resources */ if (dev->state & DEV_DISCONNECTED) { em28xx_release_resources(dev); + kfree(dev->alt_max_pkt_size); kfree(dev); return 0; } @@ -2340,10 +2395,10 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, .vidioc_s_fmt_vbi_cap = vidioc_s_fmt_vbi_cap, + .vidioc_enum_framesizes = vidioc_enum_framesizes, .vidioc_g_audio = vidioc_g_audio, .vidioc_s_audio = vidioc_s_audio, .vidioc_cropcap = vidioc_cropcap, - .vidioc_g_fmt_sliced_vbi_cap = vidioc_g_fmt_sliced_vbi_cap, .vidioc_try_fmt_sliced_vbi_cap = vidioc_try_set_sliced_vbi_cap, .vidioc_s_fmt_sliced_vbi_cap = vidioc_try_set_sliced_vbi_cap, @@ -2353,6 +2408,7 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_qbuf = vidioc_qbuf, .vidioc_dqbuf = vidioc_dqbuf, .vidioc_g_std = vidioc_g_std, + .vidioc_querystd = vidioc_querystd, .vidioc_s_std = vidioc_s_std, .vidioc_g_parm = vidioc_g_parm, .vidioc_s_parm = vidioc_s_parm, diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h index d80658bf3da..2a2cb7ed001 100644 --- a/drivers/media/video/em28xx/em28xx.h +++ b/drivers/media/video/em28xx/em28xx.h @@ -120,6 +120,7 @@ #define EM2874_BOARD_LEADERSHIP_ISDBT 77 #define EM28174_BOARD_PCTV_290E 78 #define EM2884_BOARD_TERRATEC_H5 79 +#define EM28174_BOARD_PCTV_460E 80 /* Limits minimum and default number of buffers */ #define EM28XX_MIN_BUF 4 @@ -677,8 +678,6 @@ int em28xx_isoc_dvb_max_packetsize(struct em28xx *dev); int em28xx_set_mode(struct em28xx *dev, enum em28xx_mode set_mode); int em28xx_gpio_set(struct em28xx *dev, struct em28xx_reg_seq *gpio); void em28xx_wake_i2c(struct em28xx *dev); -void em28xx_remove_from_devlist(struct em28xx *dev); -void em28xx_add_into_devlist(struct em28xx *dev); int em28xx_register_extension(struct em28xx_ops *dev); void em28xx_unregister_extension(struct em28xx_ops *dev); void em28xx_init_extension(struct em28xx *dev); diff --git a/drivers/media/video/et61x251/et61x251.h b/drivers/media/video/et61x251/et61x251.h index 14bb907d650..337ded4a638 100644 --- a/drivers/media/video/et61x251/et61x251.h +++ b/drivers/media/video/et61x251/et61x251.h @@ -165,45 +165,49 @@ et61x251_attach_sensor(struct et61x251_device* cam, #undef DBG #undef KDBG #ifdef ET61X251_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:%s:%d] " fmt "\n", \ - __FILE__, __func__, __LINE__ , ## args); \ - } \ +#define DBG(level, fmt, ...) \ +do { \ + if (debug >= (level)) { \ + if ((level) == 1) \ + dev_err(&cam->usbdev->dev, fmt "\n", \ + ##__VA_ARGS__); \ + else if ((level) == 2) \ + dev_info(&cam->usbdev->dev, fmt "\n", \ + ##__VA_ARGS__); \ + else if ((level) >= 3) \ + dev_info(&cam->usbdev->dev, "[%s:%s:%d] " fmt "\n", \ + __FILE__, __func__, __LINE__, \ + ##__VA_ARGS__); \ + } \ } while (0) -# define KDBG(level, fmt, args...) \ -do { \ - if (debug >= (level)) { \ - if ((level) == 1 || (level) == 2) \ - pr_info("et61x251: " fmt "\n", ## args); \ - else if ((level) == 3) \ - pr_debug("sn9c102: [%s:%s:%d] " fmt "\n", __FILE__, \ - __func__, __LINE__ , ## args); \ - } \ +#define KDBG(level, fmt, ...) \ +do { \ + if (debug >= (level)) { \ + if ((level) == 1 || (level) == 2) \ + pr_info(fmt "\n", ##__VA_ARGS__); \ + else if ((level) == 3) \ + pr_debug("[%s:%s:%d] " fmt "\n", \ + __FILE__, __func__, __LINE__, \ + ##__VA_ARGS__); \ + } \ } while (0) -# define V4LDBG(level, name, cmd) \ -do { \ - if (debug >= (level)) \ - v4l_print_ioctl(name, cmd); \ +#define V4LDBG(level, name, cmd) \ +do { \ + if (debug >= (level)) \ + v4l_print_ioctl(name, cmd); \ } while (0) #else -# define DBG(level, fmt, args...) do {;} while(0) -# define KDBG(level, fmt, args...) do {;} while(0) -# define V4LDBG(level, name, cmd) do {;} while(0) +#define DBG(level, fmt, ...) do {;} while(0) +#define KDBG(level, fmt, ...) do {;} while(0) +#define V4LDBG(level, name, cmd) do {;} while(0) #endif #undef PDBG -#define PDBG(fmt, args...) \ -dev_info(&cam->usbdev->dev, "[%s:%s:%d] " fmt "\n", __FILE__, __func__, \ - __LINE__ , ## args) +#define PDBG(fmt, ...) \ + dev_info(&cam->usbdev->dev, "[%s:%s:%d] " fmt "\n", \ + __FILE__, __func__, __LINE__, ##__VA_ARGS__) #undef PDBGG -#define PDBGG(fmt, args...) do {;} while(0) /* placeholder */ +#define PDBGG(fmt, args...) do {;} while (0) /* placeholder */ #endif /* _ET61X251_H_ */ diff --git a/drivers/media/video/et61x251/et61x251_core.c b/drivers/media/video/et61x251/et61x251_core.c index 9a1e80a1e14..d3777c86e1d 100644 --- a/drivers/media/video/et61x251/et61x251_core.c +++ b/drivers/media/video/et61x251/et61x251_core.c @@ -18,6 +18,8 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * ***************************************************************************/ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/version.h> #include <linux/module.h> #include <linux/init.h> diff --git a/drivers/media/video/et61x251/et61x251_tas5130d1b.c b/drivers/media/video/et61x251/et61x251_tas5130d1b.c index 04b7fbb310a..ced2e167935 100644 --- a/drivers/media/video/et61x251/et61x251_tas5130d1b.c +++ b/drivers/media/video/et61x251/et61x251_tas5130d1b.c @@ -19,6 +19,8 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * ***************************************************************************/ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include "et61x251_sensor.h" diff --git a/drivers/media/video/gspca/Kconfig b/drivers/media/video/gspca/Kconfig index 43d9a20caeb..103af3fe5aa 100644 --- a/drivers/media/video/gspca/Kconfig +++ b/drivers/media/video/gspca/Kconfig @@ -356,6 +356,16 @@ config USB_GSPCA_T613 To compile this driver as a module, choose M here: the module will be called gspca_t613. +config USB_GSPCA_TOPRO + tristate "TOPRO USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the + TP6800 and TP6810 Topro chips. + + To compile this driver as a module, choose M here: the + module will be called gspca_topro. + config USB_GSPCA_TV8532 tristate "TV8532 USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA diff --git a/drivers/media/video/gspca/Makefile b/drivers/media/video/gspca/Makefile index d6364a86333..f345f494d0f 100644 --- a/drivers/media/video/gspca/Makefile +++ b/drivers/media/video/gspca/Makefile @@ -35,6 +35,7 @@ obj-$(CONFIG_USB_GSPCA_SUNPLUS) += gspca_sunplus.o obj-$(CONFIG_USB_GSPCA_STK014) += gspca_stk014.o obj-$(CONFIG_USB_GSPCA_STV0680) += gspca_stv0680.o obj-$(CONFIG_USB_GSPCA_T613) += gspca_t613.o +obj-$(CONFIG_USB_GSPCA_TOPRO) += gspca_topro.o obj-$(CONFIG_USB_GSPCA_TV8532) += gspca_tv8532.o obj-$(CONFIG_USB_GSPCA_VC032X) += gspca_vc032x.o obj-$(CONFIG_USB_GSPCA_VICAM) += gspca_vicam.o @@ -78,6 +79,7 @@ gspca_stk014-objs := stk014.o gspca_stv0680-objs := stv0680.o gspca_sunplus-objs := sunplus.o gspca_t613-objs := t613.o +gspca_topro-objs := topro.o gspca_tv8532-objs := tv8532.o gspca_vc032x-objs := vc032x.o gspca_vicam-objs := vicam.o diff --git a/drivers/media/video/gspca/benq.c b/drivers/media/video/gspca/benq.c index a09c4709d61..6ae26160b81 100644 --- a/drivers/media/video/gspca/benq.c +++ b/drivers/media/video/gspca/benq.c @@ -18,6 +18,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #define MODULE_NAME "benq" #include "gspca.h" @@ -62,7 +64,7 @@ static void reg_w(struct gspca_dev *gspca_dev, 0, 500); if (ret < 0) { - err("reg_w err %d", ret); + pr_err("reg_w err %d\n", ret); gspca_dev->usb_err = ret; } } @@ -84,20 +86,6 @@ static int sd_init(struct gspca_dev *gspca_dev) return 0; } -static int sd_isoc_init(struct gspca_dev *gspca_dev) -{ - int ret; - - ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, - gspca_dev->nbalt - 1); - if (ret < 0) { - err("usb_set_interface failed"); - return ret; - } -/* reg_w(gspca_dev, 0x0003, 0x0002); */ - return 0; -} - /* -- start the camera -- */ static int sd_start(struct gspca_dev *gspca_dev) { @@ -113,7 +101,7 @@ static int sd_start(struct gspca_dev *gspca_dev) for (n = 0; n < 4; n++) { urb = usb_alloc_urb(SD_NPKT, GFP_KERNEL); if (!urb) { - err("usb_alloc_urb failed"); + pr_err("usb_alloc_urb failed\n"); return -ENOMEM; } gspca_dev->urb[n] = urb; @@ -123,7 +111,7 @@ static int sd_start(struct gspca_dev *gspca_dev) &urb->transfer_dma); if (urb->transfer_buffer == NULL) { - err("usb_alloc_coherent failed"); + pr_err("usb_alloc_coherent failed\n"); return -ENOMEM; } urb->dev = gspca_dev->dev; @@ -181,7 +169,7 @@ static void sd_isoc_irq(struct urb *urb) if (gspca_dev->frozen) return; #endif - err("urb status: %d", urb->status); + pr_err("urb status: %d\n", urb->status); return; } @@ -209,7 +197,7 @@ static void sd_isoc_irq(struct urb *urb) if (st == 0) st = urb->iso_frame_desc[i].status; if (st) { - err("ISOC data error: [%d] status=%d", + pr_err("ISOC data error: [%d] status=%d\n", i, st); gspca_dev->last_packet_type = DISCARD_PACKET; continue; @@ -256,10 +244,10 @@ static void sd_isoc_irq(struct urb *urb) /* resubmit the URBs */ st = usb_submit_urb(urb0, GFP_ATOMIC); if (st < 0) - err("usb_submit_urb(0) ret %d", st); + pr_err("usb_submit_urb(0) ret %d\n", st); st = usb_submit_urb(urb, GFP_ATOMIC); if (st < 0) - err("usb_submit_urb() ret %d", st); + pr_err("usb_submit_urb() ret %d\n", st); } /* sub-driver description */ @@ -269,7 +257,6 @@ static const struct sd_desc sd_desc = { .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, .init = sd_init, - .isoc_init = sd_isoc_init, .start = sd_start, .stopN = sd_stopN, .pkt_scan = sd_pkt_scan, diff --git a/drivers/media/video/gspca/conex.c b/drivers/media/video/gspca/conex.c index 8b398493f96..4c56dbef6d9 100644 --- a/drivers/media/video/gspca/conex.c +++ b/drivers/media/video/gspca/conex.c @@ -19,6 +19,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #define MODULE_NAME "conex" #include "gspca.h" @@ -129,7 +131,7 @@ static void reg_r(struct gspca_dev *gspca_dev, #ifdef GSPCA_DEBUG if (len > USB_BUF_SZ) { - err("reg_r: buffer overflow"); + pr_err("reg_r: buffer overflow\n"); return; } #endif @@ -169,7 +171,7 @@ static void reg_w(struct gspca_dev *gspca_dev, #ifdef GSPCA_DEBUG if (len > USB_BUF_SZ) { - err("reg_w: buffer overflow"); + pr_err("reg_w: buffer overflow\n"); return; } PDEBUG(D_USBO, "reg write [%02x] = %02x..", index, *buffer); diff --git a/drivers/media/video/gspca/cpia1.c b/drivers/media/video/gspca/cpia1.c index f2a9451eea1..f9b86b2484b 100644 --- a/drivers/media/video/gspca/cpia1.c +++ b/drivers/media/video/gspca/cpia1.c @@ -26,6 +26,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #define MODULE_NAME "cpia1" #include <linux/input.h> @@ -550,8 +552,7 @@ retry: gspca_dev->usb_buf, databytes, 1000); if (ret < 0) - err("usb_control_msg %02x, error %d", command[1], - ret); + pr_err("usb_control_msg %02x, error %d\n", command[1], ret); if (ret == -EPIPE && retries > 0) { retries--; @@ -1279,7 +1280,7 @@ static void monitor_exposure(struct gspca_dev *gspca_dev) cmd[7] = 0; ret = cpia_usb_transferCmd(gspca_dev, cmd); if (ret) { - err("ReadVPRegs(30,4,9,8) - failed: %d", ret); + pr_err("ReadVPRegs(30,4,9,8) - failed: %d\n", ret); return; } exp_acc = gspca_dev->usb_buf[0]; diff --git a/drivers/media/video/gspca/etoms.c b/drivers/media/video/gspca/etoms.c index 4b2c483fce6..0357d6d461d 100644 --- a/drivers/media/video/gspca/etoms.c +++ b/drivers/media/video/gspca/etoms.c @@ -18,6 +18,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #define MODULE_NAME "etoms" #include "gspca.h" @@ -236,7 +238,7 @@ static void reg_r(struct gspca_dev *gspca_dev, #ifdef GSPCA_DEBUG if (len > USB_BUF_SZ) { - err("reg_r: buffer overflow"); + pr_err("reg_r: buffer overflow\n"); return; } #endif @@ -274,7 +276,7 @@ static void reg_w(struct gspca_dev *gspca_dev, #ifdef GSPCA_DEBUG if (len > USB_BUF_SZ) { - err("reg_w: buffer overflow"); + pr_err("reg_w: buffer overflow\n"); return; } PDEBUG(D_USBO, "reg write [%02x] = %02x..", index, *buffer); diff --git a/drivers/media/video/gspca/finepix.c b/drivers/media/video/gspca/finepix.c index 987b4b69d7a..ea48200fd3a 100644 --- a/drivers/media/video/gspca/finepix.c +++ b/drivers/media/video/gspca/finepix.c @@ -18,6 +18,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #define MODULE_NAME "finepix" #include "gspca.h" @@ -182,7 +184,7 @@ static int sd_start(struct gspca_dev *gspca_dev) /* Init the device */ ret = command(gspca_dev, 0); if (ret < 0) { - err("init failed %d", ret); + pr_err("init failed %d\n", ret); return ret; } @@ -194,14 +196,14 @@ static int sd_start(struct gspca_dev *gspca_dev) FPIX_MAX_TRANSFER, &len, FPIX_TIMEOUT); if (ret < 0) { - err("usb_bulk_msg failed %d", ret); + pr_err("usb_bulk_msg failed %d\n", ret); return ret; } /* Request a frame, but don't read it */ ret = command(gspca_dev, 1); if (ret < 0) { - err("frame request failed %d", ret); + pr_err("frame request failed %d\n", ret); return ret; } diff --git a/drivers/media/video/gspca/gl860/Makefile b/drivers/media/video/gspca/gl860/Makefile index 13c9403cc87..f511eccdfd9 100644 --- a/drivers/media/video/gspca/gl860/Makefile +++ b/drivers/media/video/gspca/gl860/Makefile @@ -6,5 +6,5 @@ gspca_gl860-objs := gl860.o \ gl860-ov9655.o \ gl860-mi2020.o -EXTRA_CFLAGS += -Idrivers/media/video/gspca +ccflags-y += -Idrivers/media/video/gspca diff --git a/drivers/media/video/gspca/gl860/gl860.c b/drivers/media/video/gspca/gl860/gl860.c index e8e071aa212..2ced3b73994 100644 --- a/drivers/media/video/gspca/gl860/gl860.c +++ b/drivers/media/video/gspca/gl860/gl860.c @@ -18,6 +18,9 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include "gspca.h" #include "gl860.h" @@ -572,9 +575,8 @@ int gl860_RTx(struct gspca_dev *gspca_dev, } if (r < 0) - err("ctrl transfer failed %4d " - "[p%02x r%d v%04x i%04x len%d]", - r, pref, req, val, index, len); + pr_err("ctrl transfer failed %4d [p%02x r%d v%04x i%04x len%d]\n", + r, pref, req, val, index, len); else if (len > 1 && r < len) PDEBUG(D_ERR, "short ctrl transfer %d/%d", r, len); diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index 5da4879f47f..881e04c7ffe 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -21,7 +21,9 @@ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#define MODULE_NAME "gspca" +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define GSPCA_VERSION "2.14.0" #include <linux/init.h> #include <linux/fs.h> @@ -50,12 +52,10 @@ #error "DEF_NURBS too big" #endif -#define DRIVER_VERSION_NUMBER "2.13.0" - MODULE_AUTHOR("Jean-François Moine <http://moinejf.free.fr>"); MODULE_DESCRIPTION("GSPCA USB Camera Driver"); MODULE_LICENSE("GPL"); -MODULE_VERSION(DRIVER_VERSION_NUMBER); +MODULE_VERSION(GSPCA_VERSION); #ifdef GSPCA_DEBUG int gspca_debug = D_ERR | D_PROBE; @@ -148,7 +148,7 @@ static void int_irq(struct urb *urb) if (ret == 0) { ret = usb_submit_urb(urb, GFP_ATOMIC); if (ret < 0) - err("Resubmit URB failed with error %i", ret); + pr_err("Resubmit URB failed with error %i\n", ret); } } @@ -177,8 +177,8 @@ static int gspca_input_connect(struct gspca_dev *dev) err = input_register_device(input_dev); if (err) { - err("Input device registration failed with error %i", - err); + pr_err("Input device registration failed with error %i\n", + err); input_dev->dev.parent = NULL; input_free_device(input_dev); } else { @@ -323,8 +323,8 @@ static void fill_frame(struct gspca_dev *gspca_dev, /* check the packet status and length */ st = urb->iso_frame_desc[i].status; if (st) { - err("ISOC data error: [%d] len=%d, status=%d", - i, len, st); + pr_err("ISOC data error: [%d] len=%d, status=%d\n", + i, len, st); gspca_dev->last_packet_type = DISCARD_PACKET; continue; } @@ -346,7 +346,7 @@ resubmit: /* resubmit the URB */ st = usb_submit_urb(urb, GFP_ATOMIC); if (st < 0) - err("usb_submit_urb() ret %d", st); + pr_err("usb_submit_urb() ret %d\n", st); } /* @@ -400,7 +400,7 @@ resubmit: if (gspca_dev->cam.bulk_nurbs != 0) { st = usb_submit_urb(urb, GFP_ATOMIC); if (st < 0) - err("usb_submit_urb() ret %d", st); + pr_err("usb_submit_urb() ret %d\n", st); } } @@ -464,7 +464,7 @@ void gspca_frame_add(struct gspca_dev *gspca_dev, } else { /* !! image is NULL only when last pkt is LAST or DISCARD if (gspca_dev->image == NULL) { - err("gspca_frame_add() image == NULL"); + pr_err("gspca_frame_add() image == NULL\n"); return; } */ @@ -497,19 +497,6 @@ void gspca_frame_add(struct gspca_dev *gspca_dev, } EXPORT_SYMBOL(gspca_frame_add); -static int gspca_is_compressed(__u32 format) -{ - switch (format) { - case V4L2_PIX_FMT_MJPEG: - case V4L2_PIX_FMT_JPEG: - case V4L2_PIX_FMT_SPCA561: - case V4L2_PIX_FMT_PAC207: - case V4L2_PIX_FMT_MR97310A: - return 1; - } - return 0; -} - static int frame_alloc(struct gspca_dev *gspca_dev, struct file *file, enum v4l2_memory memory, unsigned int count) { @@ -525,7 +512,7 @@ static int frame_alloc(struct gspca_dev *gspca_dev, struct file *file, count = GSPCA_MAX_FRAMES - 1; gspca_dev->frbuf = vmalloc_32(frsz * count); if (!gspca_dev->frbuf) { - err("frame alloc failed"); + pr_err("frame alloc failed\n"); return -ENOMEM; } gspca_dev->capt_file = file; @@ -597,7 +584,7 @@ static int gspca_set_alt0(struct gspca_dev *gspca_dev) return 0; ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, 0); if (ret < 0) - err("set alt 0 err %d", ret); + pr_err("set alt 0 err %d\n", ret); return ret; } @@ -640,53 +627,104 @@ static struct usb_host_endpoint *alt_xfer(struct usb_host_interface *alt, return NULL; } +/* compute the minimum bandwidth for the current transfer */ +static u32 which_bandwidth(struct gspca_dev *gspca_dev) +{ + u32 bandwidth; + int i; + + i = gspca_dev->curr_mode; + bandwidth = gspca_dev->cam.cam_mode[i].sizeimage; + + /* if the image is compressed, estimate the mean image size */ + if (bandwidth < gspca_dev->cam.cam_mode[i].width * + gspca_dev->cam.cam_mode[i].height) + bandwidth /= 3; + + /* estimate the frame rate */ + if (gspca_dev->sd_desc->get_streamparm) { + struct v4l2_streamparm parm; + + parm.parm.capture.timeperframe.denominator = 15; + gspca_dev->sd_desc->get_streamparm(gspca_dev, &parm); + bandwidth *= parm.parm.capture.timeperframe.denominator; + } else { + bandwidth *= 15; /* 15 fps */ + } + + PDEBUG(D_STREAM, "min bandwidth: %d", bandwidth); + return bandwidth; +} + +/* endpoint table */ +#define MAX_ALT 16 +struct ep_tb_s { + u32 alt; + u32 bandwidth; +}; + /* - * look for an input (isoc or bulk) endpoint - * - * The endpoint is defined by the subdriver. - * Use only the first isoc (some Zoran - 0x0572:0x0001 - have two such ep). - * This routine may be called many times when the bandwidth is too small - * (the bandwidth is checked on urb submit). + * build the table of the endpoints + * and compute the minimum bandwidth for the image transfer */ -static struct usb_host_endpoint *get_ep(struct gspca_dev *gspca_dev) +static int build_ep_tb(struct gspca_dev *gspca_dev, + struct usb_interface *intf, + int xfer, + struct ep_tb_s *ep_tb) { - struct usb_interface *intf; struct usb_host_endpoint *ep; - int xfer, i, ret; + int i, j, nbalt, psize, found; + u32 bandwidth, last_bw; - intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface); - ep = NULL; - xfer = gspca_dev->cam.bulk ? USB_ENDPOINT_XFER_BULK - : USB_ENDPOINT_XFER_ISOC; - i = gspca_dev->alt; /* previous alt setting */ - if (gspca_dev->cam.reverse_alts) { - while (++i < gspca_dev->nbalt) { - ep = alt_xfer(&intf->altsetting[i], xfer); - if (ep) - break; - } - } else { - while (--i >= 0) { - ep = alt_xfer(&intf->altsetting[i], xfer); - if (ep) - break; - } - } - if (ep == NULL) { - err("no transfer endpoint found"); - return NULL; - } - PDEBUG(D_STREAM, "use alt %d ep 0x%02x", - i, ep->desc.bEndpointAddress); - gspca_dev->alt = i; /* memorize the current alt setting */ - if (gspca_dev->nbalt > 1) { - ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, i); - if (ret < 0) { - err("set alt %d err %d", i, ret); - ep = NULL; + nbalt = intf->num_altsetting; + if (nbalt > MAX_ALT) + nbalt = MAX_ALT; /* fixme: should warn */ + + /* build the endpoint table */ + i = 0; + last_bw = 0; + for (;;) { + ep_tb->bandwidth = 2000 * 2000 * 120; + found = 0; + for (j = 0; j < nbalt; j++) { + ep = alt_xfer(&intf->altsetting[j], xfer); + if (ep == NULL) + continue; + psize = le16_to_cpu(ep->desc.wMaxPacketSize); + if (!gspca_dev->cam.bulk) /* isoc */ + psize = (psize & 0x07ff) * + (1 + ((psize >> 11) & 3)); + bandwidth = psize * ep->desc.bInterval * 1000; + if (gspca_dev->dev->speed == USB_SPEED_HIGH + || gspca_dev->dev->speed == USB_SPEED_SUPER) + bandwidth *= 8; + if (bandwidth <= last_bw) + continue; + if (bandwidth < ep_tb->bandwidth) { + ep_tb->bandwidth = bandwidth; + ep_tb->alt = j; + found = 1; + } } + if (!found) + break; + PDEBUG(D_STREAM, "alt %d bandwidth %d", + ep_tb->alt, ep_tb->bandwidth); + last_bw = ep_tb->bandwidth; + i++; + ep_tb++; + } + + /* get the requested bandwidth and start at the highest atlsetting */ + bandwidth = which_bandwidth(gspca_dev); + ep_tb--; + while (i > 1) { + ep_tb--; + if (ep_tb->bandwidth < bandwidth) + break; + i--; } - return ep; + return i; } /* @@ -731,7 +769,7 @@ static int create_urbs(struct gspca_dev *gspca_dev, for (n = 0; n < nurbs; n++) { urb = usb_alloc_urb(npkt, GFP_KERNEL); if (!urb) { - err("usb_alloc_urb failed"); + pr_err("usb_alloc_urb failed\n"); return -ENOMEM; } gspca_dev->urb[n] = urb; @@ -741,7 +779,7 @@ static int create_urbs(struct gspca_dev *gspca_dev, &urb->transfer_dma); if (urb->transfer_buffer == NULL) { - err("usb_alloc_coherent failed"); + pr_err("usb_alloc_coherent failed\n"); return -ENOMEM; } urb->dev = gspca_dev->dev; @@ -752,7 +790,10 @@ static int create_urbs(struct gspca_dev *gspca_dev, ep->desc.bEndpointAddress); urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; - urb->interval = ep->desc.bInterval; + if (gspca_dev->dev->speed == USB_SPEED_LOW) + urb->interval = ep->desc.bInterval; + else + urb->interval = 1 << (ep->desc.bInterval - 1); urb->complete = isoc_irq; urb->number_of_packets = npkt; for (i = 0; i < npkt; i++) { @@ -774,9 +815,11 @@ static int create_urbs(struct gspca_dev *gspca_dev, */ static int gspca_init_transfer(struct gspca_dev *gspca_dev) { + struct usb_interface *intf; struct usb_host_endpoint *ep; struct urb *urb; - int n, ret; + struct ep_tb_s ep_tb[MAX_ALT]; + int n, ret, xfer, alt, alt_idx; if (mutex_lock_interruptible(&gspca_dev->usb_lock)) return -ERESTARTSYS; @@ -794,30 +837,65 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) gspca_dev->usb_err = 0; - /* set the higher alternate setting and - * loop until urb submit succeeds */ - if (gspca_dev->cam.reverse_alts) - gspca_dev->alt = 0; - else - gspca_dev->alt = gspca_dev->nbalt; - + /* do the specific subdriver stuff before endpoint selection */ + gspca_dev->alt = 0; if (gspca_dev->sd_desc->isoc_init) { ret = gspca_dev->sd_desc->isoc_init(gspca_dev); if (ret < 0) goto unlock; } + intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface); + xfer = gspca_dev->cam.bulk ? USB_ENDPOINT_XFER_BULK + : USB_ENDPOINT_XFER_ISOC; - gspca_input_destroy_urb(gspca_dev); - ep = get_ep(gspca_dev); - if (ep == NULL) { - ret = -EIO; - goto out; + /* if the subdriver forced an altsetting, get the endpoint */ + if (gspca_dev->alt != 0) { + gspca_dev->alt--; /* (previous version compatibility) */ + ep = alt_xfer(&intf->altsetting[gspca_dev->alt], xfer); + if (ep == NULL) { + pr_err("bad altsetting %d\n", gspca_dev->alt); + ret = -EIO; + goto out; + } + ep_tb[0].alt = gspca_dev->alt; + alt_idx = 1; + } else { + + /* else, compute the minimum bandwidth + * and build the endpoint table */ + alt_idx = build_ep_tb(gspca_dev, intf, xfer, ep_tb); + if (alt_idx <= 0) { + pr_err("no transfer endpoint found\n"); + ret = -EIO; + goto unlock; + } } + + /* set the highest alternate setting and + * loop until urb submit succeeds */ + gspca_input_destroy_urb(gspca_dev); + + gspca_dev->alt = ep_tb[--alt_idx].alt; + alt = -1; for (;;) { + if (alt != gspca_dev->alt) { + alt = gspca_dev->alt; + if (gspca_dev->nbalt > 1) { + ret = usb_set_interface(gspca_dev->dev, + gspca_dev->iface, + alt); + if (ret < 0) { + if (ret == -ENOSPC) + goto retry; /*fixme: ugly*/ + pr_err("set alt %d err %d\n", alt, ret); + goto out; + } + } + } if (!gspca_dev->cam.no_urb_create) { - PDEBUG(D_STREAM, "init transfer alt %d", - gspca_dev->alt); - ret = create_urbs(gspca_dev, ep); + PDEBUG(D_STREAM, "init transfer alt %d", alt); + ret = create_urbs(gspca_dev, + alt_xfer(&intf->altsetting[alt], xfer)); if (ret < 0) { destroy_urbs(gspca_dev); goto out; @@ -851,29 +929,35 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) break; } if (ret >= 0) - break; + break; /* transfer is started */ + + /* something when wrong + * stop the webcam and free the transfer resources */ gspca_stream_off(gspca_dev); if (ret != -ENOSPC) { - err("usb_submit_urb alt %d err %d", - gspca_dev->alt, ret); + pr_err("usb_submit_urb alt %d err %d\n", + gspca_dev->alt, ret); goto out; } /* the bandwidth is not wide enough * negotiate or try a lower alternate setting */ +retry: PDEBUG(D_ERR|D_STREAM, - "bandwidth not wide enough - trying again"); + "alt %d - bandwidth not wide enough - trying again", + alt); msleep(20); /* wait for kill complete */ if (gspca_dev->sd_desc->isoc_nego) { ret = gspca_dev->sd_desc->isoc_nego(gspca_dev); if (ret < 0) goto out; } else { - ep = get_ep(gspca_dev); - if (ep == NULL) { + if (alt_idx <= 0) { + pr_err("no transfer endpoint found\n"); ret = -EIO; goto out; } + alt = ep_tb[--alt_idx].alt; } } out: @@ -1044,7 +1128,9 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, return -EINVAL; /* no more format */ fmtdesc->pixelformat = fmt_tb[index]; - if (gspca_is_compressed(fmt_tb[index])) + if (gspca_dev->cam.cam_mode[i].sizeimage < + gspca_dev->cam.cam_mode[i].width * + gspca_dev->cam.cam_mode[i].height) fmtdesc->flags = V4L2_FMT_FLAG_COMPRESSED; fmtdesc->description[0] = fmtdesc->pixelformat & 0xff; fmtdesc->description[1] = (fmtdesc->pixelformat >> 8) & 0xff; @@ -2195,19 +2281,20 @@ int gspca_dev_probe2(struct usb_interface *intf, struct usb_device *dev = interface_to_usbdev(intf); int ret; - PDEBUG(D_PROBE, "probing %04x:%04x", id->idVendor, id->idProduct); + pr_info("%s-" GSPCA_VERSION " probing %04x:%04x\n", + sd_desc->name, id->idVendor, id->idProduct); /* create the device */ if (dev_size < sizeof *gspca_dev) dev_size = sizeof *gspca_dev; gspca_dev = kzalloc(dev_size, GFP_KERNEL); if (!gspca_dev) { - err("couldn't kzalloc gspca struct"); + pr_err("couldn't kzalloc gspca struct\n"); return -ENOMEM; } gspca_dev->usb_buf = kmalloc(USB_BUF_SZ, GFP_KERNEL); if (!gspca_dev->usb_buf) { - err("out of memory"); + pr_err("out of memory\n"); ret = -ENOMEM; goto out; } @@ -2264,7 +2351,7 @@ int gspca_dev_probe2(struct usb_interface *intf, VFL_TYPE_GRABBER, -1); if (ret < 0) { - err("video_register_device err %d", ret); + pr_err("video_register_device err %d\n", ret); goto out; } @@ -2296,8 +2383,8 @@ int gspca_dev_probe(struct usb_interface *intf, /* we don't handle multi-config cameras */ if (dev->descriptor.bNumConfigurations != 1) { - err("%04x:%04x too many config", - id->idVendor, id->idProduct); + pr_err("%04x:%04x too many config\n", + id->idVendor, id->idProduct); return -ENODEV; } @@ -2480,7 +2567,7 @@ EXPORT_SYMBOL(gspca_auto_gain_n_exposure); /* -- module insert / remove -- */ static int __init gspca_init(void) { - info("v" DRIVER_VERSION_NUMBER " registered"); + pr_info("v" GSPCA_VERSION " registered\n"); return 0; } static void __exit gspca_exit(void) diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h index 49e2fcbe81f..e444f16e149 100644 --- a/drivers/media/video/gspca/gspca.h +++ b/drivers/media/video/gspca/gspca.h @@ -14,11 +14,12 @@ #ifdef GSPCA_DEBUG /* GSPCA our debug messages */ extern int gspca_debug; -#define PDEBUG(level, fmt, args...) \ - do {\ - if (gspca_debug & (level)) \ - printk(KERN_INFO MODULE_NAME ": " fmt "\n", ## args); \ - } while (0) +#define PDEBUG(level, fmt, ...) \ +do { \ + if (gspca_debug & (level)) \ + pr_info(fmt, ##__VA_ARGS__); \ +} while (0) + #define D_ERR 0x01 #define D_PROBE 0x02 #define D_CONF 0x04 @@ -29,17 +30,8 @@ extern int gspca_debug; #define D_USBO 0x00 #define D_V4L2 0x0100 #else -#define PDEBUG(level, fmt, args...) +#define PDEBUG(level, fmt, ...) #endif -#undef err -#define err(fmt, args...) \ - printk(KERN_ERR MODULE_NAME ": " fmt "\n", ## args) -#undef info -#define info(fmt, args...) \ - printk(KERN_INFO MODULE_NAME ": " fmt "\n", ## args) -#undef warn -#define warn(fmt, args...) \ - printk(KERN_WARNING MODULE_NAME ": " fmt "\n", ## args) #define GSPCA_MAX_FRAMES 16 /* maximum number of video frame buffers */ /* image transfers */ diff --git a/drivers/media/video/gspca/jeilinj.c b/drivers/media/video/gspca/jeilinj.c index 1bd9c4b542d..8e3dabe3007 100644 --- a/drivers/media/video/gspca/jeilinj.c +++ b/drivers/media/video/gspca/jeilinj.c @@ -24,6 +24,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #define MODULE_NAME "jeilinj" #include <linux/slab.h> @@ -113,8 +115,8 @@ static void jlj_write2(struct gspca_dev *gspca_dev, unsigned char *command) usb_sndbulkpipe(gspca_dev->dev, 3), gspca_dev->usb_buf, 2, NULL, 500); if (retval < 0) { - err("command write [%02x] error %d", - gspca_dev->usb_buf[0], retval); + pr_err("command write [%02x] error %d\n", + gspca_dev->usb_buf[0], retval); gspca_dev->usb_err = retval; } } @@ -131,8 +133,8 @@ static void jlj_read1(struct gspca_dev *gspca_dev, unsigned char response) gspca_dev->usb_buf, 1, NULL, 500); response = gspca_dev->usb_buf[0]; if (retval < 0) { - err("read command [%02x] error %d", - gspca_dev->usb_buf[0], retval); + pr_err("read command [%02x] error %d\n", + gspca_dev->usb_buf[0], retval); gspca_dev->usb_err = retval; } } @@ -403,13 +405,7 @@ static int sd_config(struct gspca_dev *gspca_dev, dev->type = id->driver_info; gspca_dev->cam.ctrls = dev->ctrls; dev->quality = QUALITY_DEF; - dev->ctrls[LIGHTFREQ].def = V4L2_CID_POWER_LINE_FREQUENCY_60HZ; - dev->ctrls[RED].def = RED_BALANCE_DEF; - dev->ctrls[GREEN].def = GREEN_BALANCE_DEF; - dev->ctrls[BLUE].def = BLUE_BALANCE_DEF; - PDEBUG(D_PROBE, - "JEILINJ camera detected" - " (vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); + cam->cam_mode = jlj_mode; cam->nmodes = ARRAY_SIZE(jlj_mode); cam->bulk = 1; @@ -422,7 +418,7 @@ static void sd_stopN(struct gspca_dev *gspca_dev) { int i; u8 *buf; - u8 stop_commands[][2] = { + static u8 stop_commands[][2] = { {0x71, 0x00}, {0x70, 0x09}, {0x71, 0x80}, diff --git a/drivers/media/video/gspca/kinect.c b/drivers/media/video/gspca/kinect.c index 26fc206f095..4fe51fda80f 100644 --- a/drivers/media/video/gspca/kinect.c +++ b/drivers/media/video/gspca/kinect.c @@ -24,6 +24,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #define MODULE_NAME "kinect" #include "gspca.h" @@ -34,11 +36,6 @@ MODULE_AUTHOR("Antonio Ospite <ospite@studenti.unina.it>"); MODULE_DESCRIPTION("GSPCA/Kinect Sensor Device USB Camera Driver"); MODULE_LICENSE("GPL"); -#ifdef GSPCA_DEBUG -int gspca_debug = D_ERR | D_PROBE | D_CONF | D_STREAM | D_FRAM | D_PACK | - D_USBI | D_USBO | D_V4L2; -#endif - struct pkt_hdr { uint8_t magic[2]; uint8_t pad; @@ -141,7 +138,7 @@ static int send_cmd(struct gspca_dev *gspca_dev, uint16_t cmd, void *cmdbuf, struct cam_hdr *rhdr = (void *)ibuf; if (cmd_len & 1 || cmd_len > (0x400 - sizeof(*chdr))) { - err("send_cmd: Invalid command length (0x%x)", cmd_len); + pr_err("send_cmd: Invalid command length (0x%x)\n", cmd_len); return -1; } @@ -157,7 +154,7 @@ static int send_cmd(struct gspca_dev *gspca_dev, uint16_t cmd, void *cmdbuf, PDEBUG(D_USBO, "Control cmd=%04x tag=%04x len=%04x: %d", cmd, sd->cam_tag, cmd_len, res); if (res < 0) { - err("send_cmd: Output control transfer failed (%d)", res); + pr_err("send_cmd: Output control transfer failed (%d)\n", res); return res; } @@ -166,33 +163,35 @@ static int send_cmd(struct gspca_dev *gspca_dev, uint16_t cmd, void *cmdbuf, } while (actual_len == 0); PDEBUG(D_USBO, "Control reply: %d", res); if (actual_len < sizeof(*rhdr)) { - err("send_cmd: Input control transfer failed (%d)", res); + pr_err("send_cmd: Input control transfer failed (%d)\n", res); return res; } actual_len -= sizeof(*rhdr); if (rhdr->magic[0] != 0x52 || rhdr->magic[1] != 0x42) { - err("send_cmd: Bad magic %02x %02x", rhdr->magic[0], - rhdr->magic[1]); + pr_err("send_cmd: Bad magic %02x %02x\n", + rhdr->magic[0], rhdr->magic[1]); return -1; } if (rhdr->cmd != chdr->cmd) { - err("send_cmd: Bad cmd %02x != %02x", rhdr->cmd, chdr->cmd); + pr_err("send_cmd: Bad cmd %02x != %02x\n", + rhdr->cmd, chdr->cmd); return -1; } if (rhdr->tag != chdr->tag) { - err("send_cmd: Bad tag %04x != %04x", rhdr->tag, chdr->tag); + pr_err("send_cmd: Bad tag %04x != %04x\n", + rhdr->tag, chdr->tag); return -1; } if (cpu_to_le16(rhdr->len) != (actual_len/2)) { - err("send_cmd: Bad len %04x != %04x", - cpu_to_le16(rhdr->len), (int)(actual_len/2)); + pr_err("send_cmd: Bad len %04x != %04x\n", + cpu_to_le16(rhdr->len), (int)(actual_len/2)); return -1; } if (actual_len > reply_len) { - warn("send_cmd: Data buffer is %d bytes long, but got %d bytes", - reply_len, actual_len); + pr_warn("send_cmd: Data buffer is %d bytes long, but got %d bytes\n", + reply_len, actual_len); memcpy(replybuf, ibuf+sizeof(*rhdr), reply_len); } else { memcpy(replybuf, ibuf+sizeof(*rhdr), actual_len); @@ -218,8 +217,8 @@ static int write_register(struct gspca_dev *gspca_dev, uint16_t reg, if (res < 0) return res; if (res != 2) { - warn("send_cmd returned %d [%04x %04x], 0000 expected", - res, reply[0], reply[1]); + pr_warn("send_cmd returned %d [%04x %04x], 0000 expected\n", + res, reply[0], reply[1]); } return 0; } @@ -353,8 +352,8 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, u8 *__data, int len) return; if (hdr->magic[0] != 'R' || hdr->magic[1] != 'B') { - warn("[Stream %02x] Invalid magic %02x%02x", sd->stream_flag, - hdr->magic[0], hdr->magic[1]); + pr_warn("[Stream %02x] Invalid magic %02x%02x\n", + sd->stream_flag, hdr->magic[0], hdr->magic[1]); return; } @@ -368,7 +367,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, u8 *__data, int len) gspca_frame_add(gspca_dev, LAST_PACKET, data, datalen); else - warn("Packet type not recognized..."); + pr_warn("Packet type not recognized...\n"); } /* sub-driver description */ diff --git a/drivers/media/video/gspca/konica.c b/drivers/media/video/gspca/konica.c index 5964691c0e9..f3f7fe0ec4b 100644 --- a/drivers/media/video/gspca/konica.c +++ b/drivers/media/video/gspca/konica.c @@ -28,6 +28,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #define MODULE_NAME "konica" #include <linux/input.h> @@ -200,7 +202,7 @@ static void reg_w(struct gspca_dev *gspca_dev, u16 value, u16 index) 0, 1000); if (ret < 0) { - err("reg_w err %d", ret); + pr_err("reg_w err %d\n", ret); gspca_dev->usb_err = ret; } } @@ -221,7 +223,7 @@ static void reg_r(struct gspca_dev *gspca_dev, u16 value, u16 index) 2, 1000); if (ret < 0) { - err("reg_w err %d", ret); + pr_err("reg_w err %d\n", ret); gspca_dev->usb_err = ret; } } @@ -284,7 +286,7 @@ static int sd_start(struct gspca_dev *gspca_dev) intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface); alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt); if (!alt) { - err("Couldn't get altsetting"); + pr_err("Couldn't get altsetting\n"); return -EIO; } @@ -315,7 +317,7 @@ static int sd_start(struct gspca_dev *gspca_dev) le16_to_cpu(alt->endpoint[i].desc.wMaxPacketSize); urb = usb_alloc_urb(SD_NPKT, GFP_KERNEL); if (!urb) { - err("usb_alloc_urb failed"); + pr_err("usb_alloc_urb failed\n"); return -ENOMEM; } gspca_dev->urb[n] = urb; @@ -324,7 +326,7 @@ static int sd_start(struct gspca_dev *gspca_dev) GFP_KERNEL, &urb->transfer_dma); if (urb->transfer_buffer == NULL) { - err("usb_buffer_alloc failed"); + pr_err("usb_buffer_alloc failed\n"); return -ENOMEM; } @@ -386,7 +388,7 @@ static void sd_isoc_irq(struct urb *urb) PDEBUG(D_ERR, "urb status: %d", urb->status); st = usb_submit_urb(urb, GFP_ATOMIC); if (st < 0) - err("resubmit urb error %d", st); + pr_err("resubmit urb error %d\n", st); return; } @@ -477,7 +479,7 @@ resubmit: } st = usb_submit_urb(status_urb, GFP_ATOMIC); if (st < 0) - err("usb_submit_urb(status_urb) ret %d", st); + pr_err("usb_submit_urb(status_urb) ret %d\n", st); } static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) diff --git a/drivers/media/video/gspca/m5602/Makefile b/drivers/media/video/gspca/m5602/Makefile index bf7a19a1e6d..7f52961f439 100644 --- a/drivers/media/video/gspca/m5602/Makefile +++ b/drivers/media/video/gspca/m5602/Makefile @@ -8,4 +8,4 @@ gspca_m5602-objs := m5602_core.o \ m5602_s5k83a.o \ m5602_s5k4aa.o -EXTRA_CFLAGS += -Idrivers/media/video/gspca +ccflags-y += -Idrivers/media/video/gspca diff --git a/drivers/media/video/gspca/m5602/m5602_core.c b/drivers/media/video/gspca/m5602/m5602_core.c index a7722b1aef9..67533e5582a 100644 --- a/drivers/media/video/gspca/m5602/m5602_core.c +++ b/drivers/media/video/gspca/m5602/m5602_core.c @@ -16,6 +16,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include "m5602_ov9650.h" #include "m5602_ov7660.h" #include "m5602_mt9m111.h" @@ -192,10 +194,9 @@ static void m5602_dump_bridge(struct sd *sd) for (i = 0; i < 0x80; i++) { unsigned char val = 0; m5602_read_bridge(sd, i, &val); - info("ALi m5602 address 0x%x contains 0x%x", i, val); + pr_info("ALi m5602 address 0x%x contains 0x%x\n", i, val); } - info("Warning: The ALi m5602 webcam probably won't work " - "until it's power cycled"); + pr_info("Warning: The ALi m5602 webcam probably won't work until it's power cycled\n"); } static int m5602_probe_sensor(struct sd *sd) @@ -231,7 +232,7 @@ static int m5602_probe_sensor(struct sd *sd) return 0; /* More sensor probe function goes here */ - info("Failed to find a sensor"); + pr_info("Failed to find a sensor\n"); sd->sensor = NULL; return -ENODEV; } diff --git a/drivers/media/video/gspca/m5602/m5602_mt9m111.c b/drivers/media/video/gspca/m5602/m5602_mt9m111.c index 0d605a52b92..6268aa24ec5 100644 --- a/drivers/media/video/gspca/m5602/m5602_mt9m111.c +++ b/drivers/media/video/gspca/m5602/m5602_mt9m111.c @@ -16,6 +16,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include "m5602_mt9m111.h" static int mt9m111_set_vflip(struct gspca_dev *gspca_dev, __s32 val); @@ -163,7 +165,7 @@ int mt9m111_probe(struct sd *sd) if (force_sensor) { if (force_sensor == MT9M111_SENSOR) { - info("Forcing a %s sensor", mt9m111.name); + pr_info("Forcing a %s sensor\n", mt9m111.name); goto sensor_found; } /* If we want to force another sensor, don't try to probe this @@ -191,7 +193,7 @@ int mt9m111_probe(struct sd *sd) return -ENODEV; if ((data[0] == 0x14) && (data[1] == 0x3a)) { - info("Detected a mt9m111 sensor"); + pr_info("Detected a mt9m111 sensor\n"); goto sensor_found; } @@ -612,34 +614,34 @@ static void mt9m111_dump_registers(struct sd *sd) { u8 address, value[2] = {0x00, 0x00}; - info("Dumping the mt9m111 register state"); + pr_info("Dumping the mt9m111 register state\n"); - info("Dumping the mt9m111 sensor core registers"); + pr_info("Dumping the mt9m111 sensor core registers\n"); value[1] = MT9M111_SENSOR_CORE; m5602_write_sensor(sd, MT9M111_PAGE_MAP, value, 2); for (address = 0; address < 0xff; address++) { m5602_read_sensor(sd, address, value, 2); - info("register 0x%x contains 0x%x%x", - address, value[0], value[1]); + pr_info("register 0x%x contains 0x%x%x\n", + address, value[0], value[1]); } - info("Dumping the mt9m111 color pipeline registers"); + pr_info("Dumping the mt9m111 color pipeline registers\n"); value[1] = MT9M111_COLORPIPE; m5602_write_sensor(sd, MT9M111_PAGE_MAP, value, 2); for (address = 0; address < 0xff; address++) { m5602_read_sensor(sd, address, value, 2); - info("register 0x%x contains 0x%x%x", - address, value[0], value[1]); + pr_info("register 0x%x contains 0x%x%x\n", + address, value[0], value[1]); } - info("Dumping the mt9m111 camera control registers"); + pr_info("Dumping the mt9m111 camera control registers\n"); value[1] = MT9M111_CAMERA_CONTROL; m5602_write_sensor(sd, MT9M111_PAGE_MAP, value, 2); for (address = 0; address < 0xff; address++) { m5602_read_sensor(sd, address, value, 2); - info("register 0x%x contains 0x%x%x", - address, value[0], value[1]); + pr_info("register 0x%x contains 0x%x%x\n", + address, value[0], value[1]); } - info("mt9m111 register state dump complete"); + pr_info("mt9m111 register state dump complete\n"); } diff --git a/drivers/media/video/gspca/m5602/m5602_ov7660.c b/drivers/media/video/gspca/m5602/m5602_ov7660.c index b12f60464b3..9a14835c128 100644 --- a/drivers/media/video/gspca/m5602/m5602_ov7660.c +++ b/drivers/media/video/gspca/m5602/m5602_ov7660.c @@ -16,6 +16,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include "m5602_ov7660.h" static int ov7660_get_gain(struct gspca_dev *gspca_dev, __s32 *val); @@ -149,7 +151,7 @@ int ov7660_probe(struct sd *sd) if (force_sensor) { if (force_sensor == OV7660_SENSOR) { - info("Forcing an %s sensor", ov7660.name); + pr_info("Forcing an %s sensor\n", ov7660.name); goto sensor_found; } /* If we want to force another sensor, @@ -180,10 +182,10 @@ int ov7660_probe(struct sd *sd) if (m5602_read_sensor(sd, OV7660_VER, &ver_id, 1)) return -ENODEV; - info("Sensor reported 0x%x%x", prod_id, ver_id); + pr_info("Sensor reported 0x%x%x\n", prod_id, ver_id); if ((prod_id == 0x76) && (ver_id == 0x60)) { - info("Detected a ov7660 sensor"); + pr_info("Detected a ov7660 sensor\n"); goto sensor_found; } return -ENODEV; @@ -457,17 +459,16 @@ static int ov7660_set_vflip(struct gspca_dev *gspca_dev, __s32 val) static void ov7660_dump_registers(struct sd *sd) { int address; - info("Dumping the ov7660 register state"); + pr_info("Dumping the ov7660 register state\n"); for (address = 0; address < 0xa9; address++) { u8 value; m5602_read_sensor(sd, address, &value, 1); - info("register 0x%x contains 0x%x", - address, value); + pr_info("register 0x%x contains 0x%x\n", address, value); } - info("ov7660 register state dump complete"); + pr_info("ov7660 register state dump complete\n"); - info("Probing for which registers that are read/write"); + pr_info("Probing for which registers that are read/write\n"); for (address = 0; address < 0xff; address++) { u8 old_value, ctrl_value; u8 test_value[2] = {0xff, 0xff}; @@ -477,9 +478,9 @@ static void ov7660_dump_registers(struct sd *sd) m5602_read_sensor(sd, address, &ctrl_value, 1); if (ctrl_value == test_value[0]) - info("register 0x%x is writeable", address); + pr_info("register 0x%x is writeable\n", address); else - info("register 0x%x is read only", address); + pr_info("register 0x%x is read only\n", address); /* Restore original value */ m5602_write_sensor(sd, address, &old_value, 1); diff --git a/drivers/media/video/gspca/m5602/m5602_ov9650.c b/drivers/media/video/gspca/m5602/m5602_ov9650.c index 703d48670a2..2114a8b90ec 100644 --- a/drivers/media/video/gspca/m5602/m5602_ov9650.c +++ b/drivers/media/video/gspca/m5602/m5602_ov9650.c @@ -16,6 +16,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include "m5602_ov9650.h" static int ov9650_set_exposure(struct gspca_dev *gspca_dev, __s32 val); @@ -299,7 +301,7 @@ int ov9650_probe(struct sd *sd) if (force_sensor) { if (force_sensor == OV9650_SENSOR) { - info("Forcing an %s sensor", ov9650.name); + pr_info("Forcing an %s sensor\n", ov9650.name); goto sensor_found; } /* If we want to force another sensor, @@ -330,7 +332,7 @@ int ov9650_probe(struct sd *sd) return -ENODEV; if ((prod_id == 0x96) && (ver_id == 0x52)) { - info("Detected an ov9650 sensor"); + pr_info("Detected an ov9650 sensor\n"); goto sensor_found; } return -ENODEV; @@ -850,17 +852,16 @@ static int ov9650_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val) static void ov9650_dump_registers(struct sd *sd) { int address; - info("Dumping the ov9650 register state"); + pr_info("Dumping the ov9650 register state\n"); for (address = 0; address < 0xa9; address++) { u8 value; m5602_read_sensor(sd, address, &value, 1); - info("register 0x%x contains 0x%x", - address, value); + pr_info("register 0x%x contains 0x%x\n", address, value); } - info("ov9650 register state dump complete"); + pr_info("ov9650 register state dump complete\n"); - info("Probing for which registers that are read/write"); + pr_info("Probing for which registers that are read/write\n"); for (address = 0; address < 0xff; address++) { u8 old_value, ctrl_value; u8 test_value[2] = {0xff, 0xff}; @@ -870,9 +871,9 @@ static void ov9650_dump_registers(struct sd *sd) m5602_read_sensor(sd, address, &ctrl_value, 1); if (ctrl_value == test_value[0]) - info("register 0x%x is writeable", address); + pr_info("register 0x%x is writeable\n", address); else - info("register 0x%x is read only", address); + pr_info("register 0x%x is read only\n", address); /* Restore original value */ m5602_write_sensor(sd, address, &old_value, 1); diff --git a/drivers/media/video/gspca/m5602/m5602_po1030.c b/drivers/media/video/gspca/m5602/m5602_po1030.c index 1febd34c2f0..b8771698cbc 100644 --- a/drivers/media/video/gspca/m5602/m5602_po1030.c +++ b/drivers/media/video/gspca/m5602/m5602_po1030.c @@ -16,6 +16,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include "m5602_po1030.h" static int po1030_get_exposure(struct gspca_dev *gspca_dev, __s32 *val); @@ -197,7 +199,7 @@ int po1030_probe(struct sd *sd) if (force_sensor) { if (force_sensor == PO1030_SENSOR) { - info("Forcing a %s sensor", po1030.name); + pr_info("Forcing a %s sensor\n", po1030.name); goto sensor_found; } /* If we want to force another sensor, don't try to probe this @@ -221,7 +223,7 @@ int po1030_probe(struct sd *sd) return -ENODEV; if (dev_id_h == 0x30) { - info("Detected a po1030 sensor"); + pr_info("Detected a po1030 sensor\n"); goto sensor_found; } return -ENODEV; @@ -267,7 +269,7 @@ int po1030_init(struct sd *sd) break; default: - info("Invalid stream command, exiting init"); + pr_info("Invalid stream command, exiting init\n"); return -EINVAL; } } @@ -733,16 +735,15 @@ static void po1030_dump_registers(struct sd *sd) int address; u8 value = 0; - info("Dumping the po1030 sensor core registers"); + pr_info("Dumping the po1030 sensor core registers\n"); for (address = 0; address < 0x7f; address++) { m5602_read_sensor(sd, address, &value, 1); - info("register 0x%x contains 0x%x", - address, value); + pr_info("register 0x%x contains 0x%x\n", address, value); } - info("po1030 register state dump complete"); + pr_info("po1030 register state dump complete\n"); - info("Probing for which registers that are read/write"); + pr_info("Probing for which registers that are read/write\n"); for (address = 0; address < 0xff; address++) { u8 old_value, ctrl_value; u8 test_value[2] = {0xff, 0xff}; @@ -752,9 +753,9 @@ static void po1030_dump_registers(struct sd *sd) m5602_read_sensor(sd, address, &ctrl_value, 1); if (ctrl_value == test_value[0]) - info("register 0x%x is writeable", address); + pr_info("register 0x%x is writeable\n", address); else - info("register 0x%x is read only", address); + pr_info("register 0x%x is read only\n", address); /* Restore original value */ m5602_write_sensor(sd, address, &old_value, 1); diff --git a/drivers/media/video/gspca/m5602/m5602_s5k4aa.c b/drivers/media/video/gspca/m5602/m5602_s5k4aa.c index d27280be985..cc8ec3f7e8d 100644 --- a/drivers/media/video/gspca/m5602/m5602_s5k4aa.c +++ b/drivers/media/video/gspca/m5602/m5602_s5k4aa.c @@ -16,6 +16,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include "m5602_s5k4aa.h" static int s5k4aa_get_exposure(struct gspca_dev *gspca_dev, __s32 *val); @@ -240,7 +242,7 @@ int s5k4aa_probe(struct sd *sd) if (force_sensor) { if (force_sensor == S5K4AA_SENSOR) { - info("Forcing a %s sensor", s5k4aa.name); + pr_info("Forcing a %s sensor\n", s5k4aa.name); goto sensor_found; } /* If we want to force another sensor, don't try to probe this @@ -276,7 +278,7 @@ int s5k4aa_probe(struct sd *sd) data, 2); break; default: - info("Invalid stream command, exiting init"); + pr_info("Invalid stream command, exiting init\n"); return -EINVAL; } } @@ -292,7 +294,7 @@ int s5k4aa_probe(struct sd *sd) if (memcmp(prod_id, expected_prod_id, sizeof(prod_id))) return -ENODEV; else - info("Detected a s5k4aa sensor"); + pr_info("Detected a s5k4aa sensor\n"); sensor_found: sensor_settings = kmalloc( @@ -347,7 +349,7 @@ int s5k4aa_start(struct sd *sd) break; default: - err("Invalid stream command, exiting init"); + pr_err("Invalid stream command, exiting init\n"); return -EINVAL; } } @@ -383,7 +385,7 @@ int s5k4aa_start(struct sd *sd) break; default: - err("Invalid stream command, exiting init"); + pr_err("Invalid stream command, exiting init\n"); return -EINVAL; } } @@ -447,7 +449,7 @@ int s5k4aa_init(struct sd *sd) init_s5k4aa[i][1], data, 2); break; default: - info("Invalid stream command, exiting init"); + pr_info("Invalid stream command, exiting init\n"); return -EINVAL; } } @@ -686,20 +688,21 @@ static void s5k4aa_dump_registers(struct sd *sd) m5602_read_sensor(sd, S5K4AA_PAGE_MAP, &old_page, 1); for (page = 0; page < 16; page++) { m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &page, 1); - info("Dumping the s5k4aa register state for page 0x%x", page); + pr_info("Dumping the s5k4aa register state for page 0x%x\n", + page); for (address = 0; address <= 0xff; address++) { u8 value = 0; m5602_read_sensor(sd, address, &value, 1); - info("register 0x%x contains 0x%x", - address, value); + pr_info("register 0x%x contains 0x%x\n", + address, value); } } - info("s5k4aa register state dump complete"); + pr_info("s5k4aa register state dump complete\n"); for (page = 0; page < 16; page++) { m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &page, 1); - info("Probing for which registers that are " - "read/write for page 0x%x", page); + pr_info("Probing for which registers that are read/write for page 0x%x\n", + page); for (address = 0; address <= 0xff; address++) { u8 old_value, ctrl_value, test_value = 0xff; @@ -708,14 +711,16 @@ static void s5k4aa_dump_registers(struct sd *sd) m5602_read_sensor(sd, address, &ctrl_value, 1); if (ctrl_value == test_value) - info("register 0x%x is writeable", address); + pr_info("register 0x%x is writeable\n", + address); else - info("register 0x%x is read only", address); + pr_info("register 0x%x is read only\n", + address); /* Restore original value */ m5602_write_sensor(sd, address, &old_value, 1); } } - info("Read/write register probing complete"); + pr_info("Read/write register probing complete\n"); m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &old_page, 1); } diff --git a/drivers/media/video/gspca/m5602/m5602_s5k83a.c b/drivers/media/video/gspca/m5602/m5602_s5k83a.c index fbd91545497..1de743a02b0 100644 --- a/drivers/media/video/gspca/m5602/m5602_s5k83a.c +++ b/drivers/media/video/gspca/m5602/m5602_s5k83a.c @@ -16,6 +16,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/kthread.h> #include "m5602_s5k83a.h" @@ -135,7 +137,7 @@ int s5k83a_probe(struct sd *sd) if (force_sensor) { if (force_sensor == S5K83A_SENSOR) { - info("Forcing a %s sensor", s5k83a.name); + pr_info("Forcing a %s sensor\n", s5k83a.name); goto sensor_found; } /* If we want to force another sensor, don't try to probe this @@ -168,7 +170,7 @@ int s5k83a_probe(struct sd *sd) if ((prod_id == 0xff) || (ver_id == 0xff)) return -ENODEV; else - info("Detected a s5k83a sensor"); + pr_info("Detected a s5k83a sensor\n"); sensor_found: sens_priv = kmalloc( @@ -227,7 +229,7 @@ int s5k83a_init(struct sd *sd) init_s5k83a[i][1], data, 2); break; default: - info("Invalid stream command, exiting init"); + pr_info("Invalid stream command, exiting init\n"); return -EINVAL; } } @@ -273,7 +275,7 @@ static int rotation_thread_function(void *data) s5k83a_get_rotation(sd, ®); if (previous_rotation != reg) { previous_rotation = reg; - info("Camera was flipped"); + pr_info("Camera was flipped\n"); s5k83a_get_vflip((struct gspca_dev *) sd, &vflip); s5k83a_get_hflip((struct gspca_dev *) sd, &hflip); @@ -566,20 +568,20 @@ static void s5k83a_dump_registers(struct sd *sd) for (page = 0; page < 16; page++) { m5602_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1); - info("Dumping the s5k83a register state for page 0x%x", page); + pr_info("Dumping the s5k83a register state for page 0x%x\n", + page); for (address = 0; address <= 0xff; address++) { u8 val = 0; m5602_read_sensor(sd, address, &val, 1); - info("register 0x%x contains 0x%x", - address, val); + pr_info("register 0x%x contains 0x%x\n", address, val); } } - info("s5k83a register state dump complete"); + pr_info("s5k83a register state dump complete\n"); for (page = 0; page < 16; page++) { m5602_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1); - info("Probing for which registers that are read/write " - "for page 0x%x", page); + pr_info("Probing for which registers that are read/write for page 0x%x\n", + page); for (address = 0; address <= 0xff; address++) { u8 old_val, ctrl_val, test_val = 0xff; @@ -588,14 +590,16 @@ static void s5k83a_dump_registers(struct sd *sd) m5602_read_sensor(sd, address, &ctrl_val, 1); if (ctrl_val == test_val) - info("register 0x%x is writeable", address); + pr_info("register 0x%x is writeable\n", + address); else - info("register 0x%x is read only", address); + pr_info("register 0x%x is read only\n", + address); /* Restore original val */ m5602_write_sensor(sd, address, &old_val, 1); } } - info("Read/write register probing complete"); + pr_info("Read/write register probing complete\n"); m5602_write_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1); } diff --git a/drivers/media/video/gspca/mars.c b/drivers/media/video/gspca/mars.c index 0196209a948..ef45fa57575 100644 --- a/drivers/media/video/gspca/mars.c +++ b/drivers/media/video/gspca/mars.c @@ -19,6 +19,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #define MODULE_NAME "mars" #include "gspca.h" @@ -178,8 +180,8 @@ static void reg_w(struct gspca_dev *gspca_dev, &alen, 500); /* timeout in milliseconds */ if (ret < 0) { - err("reg write [%02x] error %d", - gspca_dev->usb_buf[0], ret); + pr_err("reg write [%02x] error %d\n", + gspca_dev->usb_buf[0], ret); gspca_dev->usb_err = ret; } } diff --git a/drivers/media/video/gspca/mr97310a.c b/drivers/media/video/gspca/mr97310a.c index 97e50796743..473e813b680 100644 --- a/drivers/media/video/gspca/mr97310a.c +++ b/drivers/media/video/gspca/mr97310a.c @@ -40,6 +40,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #define MODULE_NAME "mr97310a" #include "gspca.h" @@ -267,7 +269,7 @@ static int mr_write(struct gspca_dev *gspca_dev, int len) usb_sndbulkpipe(gspca_dev->dev, 4), gspca_dev->usb_buf, len, NULL, 500); if (rc < 0) - err("reg write [%02x] error %d", + pr_err("reg write [%02x] error %d\n", gspca_dev->usb_buf[0], rc); return rc; } @@ -281,7 +283,7 @@ static int mr_read(struct gspca_dev *gspca_dev, int len) usb_rcvbulkpipe(gspca_dev->dev, 3), gspca_dev->usb_buf, len, NULL, 500); if (rc < 0) - err("reg read [%02x] error %d", + pr_err("reg read [%02x] error %d\n", gspca_dev->usb_buf[0], rc); return rc; } @@ -540,7 +542,7 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->sensor_type = 1; break; default: - err("Unknown CIF Sensor id : %02x", + pr_err("Unknown CIF Sensor id : %02x\n", gspca_dev->usb_buf[1]); return -ENODEV; } @@ -575,10 +577,10 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->sensor_type = 2; } else if ((gspca_dev->usb_buf[0] != 0x03) && (gspca_dev->usb_buf[0] != 0x04)) { - err("Unknown VGA Sensor id Byte 0: %02x", - gspca_dev->usb_buf[0]); - err("Defaults assumed, may not work"); - err("Please report this"); + pr_err("Unknown VGA Sensor id Byte 0: %02x\n", + gspca_dev->usb_buf[0]); + pr_err("Defaults assumed, may not work\n"); + pr_err("Please report this\n"); } /* Sakar Digital color needs to be adjusted. */ if ((gspca_dev->usb_buf[0] == 0x03) && @@ -595,10 +597,10 @@ static int sd_config(struct gspca_dev *gspca_dev, /* Nothing to do here. */ break; default: - err("Unknown VGA Sensor id Byte 1: %02x", - gspca_dev->usb_buf[1]); - err("Defaults assumed, may not work"); - err("Please report this"); + pr_err("Unknown VGA Sensor id Byte 1: %02x\n", + gspca_dev->usb_buf[1]); + pr_err("Defaults assumed, may not work\n"); + pr_err("Please report this\n"); } } PDEBUG(D_PROBE, "MR97310A VGA camera detected, sensor: %d", diff --git a/drivers/media/video/gspca/nw80x.c b/drivers/media/video/gspca/nw80x.c index 8e754fd4dc5..7681814e594 100644 --- a/drivers/media/video/gspca/nw80x.c +++ b/drivers/media/video/gspca/nw80x.c @@ -20,6 +20,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #define MODULE_NAME "nw80x" #include "gspca.h" @@ -1571,7 +1573,7 @@ static void reg_w(struct gspca_dev *gspca_dev, len, 500); if (ret < 0) { - err("reg_w err %d", ret); + pr_err("reg_w err %d\n", ret); gspca_dev->usb_err = ret; } } @@ -1592,7 +1594,7 @@ static void reg_r(struct gspca_dev *gspca_dev, 0x00, index, gspca_dev->usb_buf, len, 500); if (ret < 0) { - err("reg_r err %d", ret); + pr_err("reg_r err %d\n", ret); gspca_dev->usb_err = ret; return; } @@ -1802,7 +1804,8 @@ static int sd_config(struct gspca_dev *gspca_dev, } } if (webcam_chip[sd->webcam] != sd->bridge) { - err("Bad webcam type %d for NW80%d", sd->webcam, sd->bridge); + pr_err("Bad webcam type %d for NW80%d\n", + sd->webcam, sd->bridge); gspca_dev->usb_err = -ENODEV; return gspca_dev->usb_err; } diff --git a/drivers/media/video/gspca/ov519.c b/drivers/media/video/gspca/ov519.c index 0800433b209..6a01b35a947 100644 --- a/drivers/media/video/gspca/ov519.c +++ b/drivers/media/video/gspca/ov519.c @@ -36,6 +36,9 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #define MODULE_NAME "ov519" #include <linux/input.h> @@ -2171,7 +2174,7 @@ static void reg_w(struct sd *sd, u16 index, u16 value) sd->gspca_dev.usb_buf, 1, 500); leave: if (ret < 0) { - err("reg_w %02x failed %d", index, ret); + pr_err("reg_w %02x failed %d\n", index, ret); sd->gspca_dev.usb_err = ret; return; } @@ -2210,7 +2213,7 @@ static int reg_r(struct sd *sd, u16 index) PDEBUG(D_USBI, "GET %02x 0000 %04x %02x", req, index, ret); } else { - err("reg_r %02x failed %d", index, ret); + pr_err("reg_r %02x failed %d\n", index, ret); sd->gspca_dev.usb_err = ret; } @@ -2235,7 +2238,7 @@ static int reg_r8(struct sd *sd, if (ret >= 0) { ret = sd->gspca_dev.usb_buf[0]; } else { - err("reg_r8 %02x failed %d", index, ret); + pr_err("reg_r8 %02x failed %d\n", index, ret); sd->gspca_dev.usb_err = ret; } @@ -2288,7 +2291,7 @@ static void ov518_reg_w32(struct sd *sd, u16 index, u32 value, int n) 0, index, sd->gspca_dev.usb_buf, n, 500); if (ret < 0) { - err("reg_w32 %02x failed %d", index, ret); + pr_err("reg_w32 %02x failed %d\n", index, ret); sd->gspca_dev.usb_err = ret; } } @@ -2457,7 +2460,7 @@ static void ovfx2_i2c_w(struct sd *sd, u8 reg, u8 value) (u16) value, (u16) reg, NULL, 0, 500); if (ret < 0) { - err("ovfx2_i2c_w %02x failed %d", reg, ret); + pr_err("ovfx2_i2c_w %02x failed %d\n", reg, ret); sd->gspca_dev.usb_err = ret; } @@ -2481,7 +2484,7 @@ static int ovfx2_i2c_r(struct sd *sd, u8 reg) ret = sd->gspca_dev.usb_buf[0]; PDEBUG(D_USBI, "ovfx2_i2c_r %02x %02x", reg, ret); } else { - err("ovfx2_i2c_r %02x failed %d", reg, ret); + pr_err("ovfx2_i2c_r %02x failed %d\n", reg, ret); sd->gspca_dev.usb_err = ret; } @@ -2727,7 +2730,7 @@ static void ov_hires_configure(struct sd *sd) int high, low; if (sd->bridge != BRIDGE_OVFX2) { - err("error hires sensors only supported with ovfx2"); + pr_err("error hires sensors only supported with ovfx2\n"); return; } @@ -2762,7 +2765,7 @@ static void ov_hires_configure(struct sd *sd) } break; } - err("Error unknown sensor type: %02x%02x", high, low); + pr_err("Error unknown sensor type: %02x%02x\n", high, low); } /* This initializes the OV8110, OV8610 sensor. The OV8110 uses @@ -2783,7 +2786,7 @@ static void ov8xx0_configure(struct sd *sd) if ((rc & 3) == 1) sd->sensor = SEN_OV8610; else - err("Unknown image sensor version: %d", rc & 3); + pr_err("Unknown image sensor version: %d\n", rc & 3); } /* This initializes the OV7610, OV7620, or OV76BE sensor. The OV76BE uses @@ -2840,8 +2843,8 @@ static void ov7xx0_configure(struct sd *sd) if (high == 0x76) { switch (low) { case 0x30: - err("Sensor is an OV7630/OV7635"); - err("7630 is not supported by this driver"); + pr_err("Sensor is an OV7630/OV7635\n"); + pr_err("7630 is not supported by this driver\n"); return; case 0x40: PDEBUG(D_PROBE, "Sensor is an OV7645"); @@ -2858,7 +2861,6 @@ static void ov7xx0_configure(struct sd *sd) case 0x60: PDEBUG(D_PROBE, "Sensor is a OV7660"); sd->sensor = SEN_OV7660; - sd->invert_led = 0; break; default: PDEBUG(D_PROBE, "Unknown sensor: 0x76%x", low); @@ -2869,7 +2871,7 @@ static void ov7xx0_configure(struct sd *sd) sd->sensor = SEN_OV7620; } } else { - err("Unknown image sensor version: %d", rc & 3); + pr_err("Unknown image sensor version: %d\n", rc & 3); } } @@ -2892,8 +2894,7 @@ static void ov6xx0_configure(struct sd *sd) switch (rc) { case 0x00: sd->sensor = SEN_OV6630; - warn("WARNING: Sensor is an OV66308. Your camera may have"); - warn("been misdetected in previous driver versions."); + pr_warn("WARNING: Sensor is an OV66308. Your camera may have been misdetected in previous driver versions.\n"); break; case 0x01: sd->sensor = SEN_OV6620; @@ -2909,11 +2910,10 @@ static void ov6xx0_configure(struct sd *sd) break; case 0x90: sd->sensor = SEN_OV6630; - warn("WARNING: Sensor is an OV66307. Your camera may have"); - warn("been misdetected in previous driver versions."); + pr_warn("WARNING: Sensor is an OV66307. Your camera may have been misdetected in previous driver versions.\n"); break; default: - err("FATAL: Unknown sensor version: 0x%02x", rc); + pr_err("FATAL: Unknown sensor version: 0x%02x\n", rc); return; } @@ -3337,7 +3337,6 @@ static int sd_config(struct gspca_dev *gspca_dev, case BRIDGE_OV519: cam->cam_mode = ov519_vga_mode; cam->nmodes = ARRAY_SIZE(ov519_vga_mode); - sd->invert_led = !sd->invert_led; break; case BRIDGE_OVFX2: cam->cam_mode = ov519_vga_mode; @@ -3407,7 +3406,7 @@ static int sd_init(struct gspca_dev *gspca_dev) } else if (init_ov_sensor(sd, OV_HIRES_SID) >= 0) { ov_hires_configure(sd); } else { - err("Can't determine sensor slave IDs"); + pr_err("Can't determine sensor slave IDs\n"); goto error; } @@ -3592,7 +3591,7 @@ static void ov511_mode_init_regs(struct sd *sd) intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface); alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt); if (!alt) { - err("Couldn't get altsetting"); + pr_err("Couldn't get altsetting\n"); sd->gspca_dev.usb_err = -EIO; return; } @@ -3715,7 +3714,7 @@ static void ov518_mode_init_regs(struct sd *sd) intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface); alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt); if (!alt) { - err("Couldn't get altsetting"); + pr_err("Couldn't get altsetting\n"); sd->gspca_dev.usb_err = -EIO; return; } @@ -5005,24 +5004,24 @@ static const struct sd_desc sd_desc = { /* -- module initialisation -- */ static const struct usb_device_id device_table[] = { {USB_DEVICE(0x041e, 0x4003), .driver_info = BRIDGE_W9968CF }, - {USB_DEVICE(0x041e, 0x4052), .driver_info = BRIDGE_OV519 }, - {USB_DEVICE(0x041e, 0x405f), + {USB_DEVICE(0x041e, 0x4052), .driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED }, + {USB_DEVICE(0x041e, 0x405f), .driver_info = BRIDGE_OV519 }, {USB_DEVICE(0x041e, 0x4060), .driver_info = BRIDGE_OV519 }, {USB_DEVICE(0x041e, 0x4061), .driver_info = BRIDGE_OV519 }, - {USB_DEVICE(0x041e, 0x4064), - .driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED }, + {USB_DEVICE(0x041e, 0x4064), .driver_info = BRIDGE_OV519 }, {USB_DEVICE(0x041e, 0x4067), .driver_info = BRIDGE_OV519 }, - {USB_DEVICE(0x041e, 0x4068), + {USB_DEVICE(0x041e, 0x4068), .driver_info = BRIDGE_OV519 }, + {USB_DEVICE(0x045e, 0x028c), .driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED }, - {USB_DEVICE(0x045e, 0x028c), .driver_info = BRIDGE_OV519 }, {USB_DEVICE(0x054c, 0x0154), .driver_info = BRIDGE_OV519 }, - {USB_DEVICE(0x054c, 0x0155), - .driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED }, + {USB_DEVICE(0x054c, 0x0155), .driver_info = BRIDGE_OV519 }, {USB_DEVICE(0x05a9, 0x0511), .driver_info = BRIDGE_OV511 }, {USB_DEVICE(0x05a9, 0x0518), .driver_info = BRIDGE_OV518 }, - {USB_DEVICE(0x05a9, 0x0519), .driver_info = BRIDGE_OV519 }, - {USB_DEVICE(0x05a9, 0x0530), .driver_info = BRIDGE_OV519 }, + {USB_DEVICE(0x05a9, 0x0519), + .driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED }, + {USB_DEVICE(0x05a9, 0x0530), + .driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED }, {USB_DEVICE(0x05a9, 0x2800), .driver_info = BRIDGE_OVFX2 }, {USB_DEVICE(0x05a9, 0x4519), .driver_info = BRIDGE_OV519 }, {USB_DEVICE(0x05a9, 0x8519), .driver_info = BRIDGE_OV519 }, diff --git a/drivers/media/video/gspca/ov534.c b/drivers/media/video/gspca/ov534.c index 0c6369b7fe1..76907eced4a 100644 --- a/drivers/media/video/gspca/ov534.c +++ b/drivers/media/video/gspca/ov534.c @@ -28,6 +28,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #define MODULE_NAME "ov534" #include "gspca.h" @@ -775,7 +777,7 @@ static void ov534_reg_write(struct gspca_dev *gspca_dev, u16 reg, u8 val) USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0x00, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT); if (ret < 0) { - err("write failed %d", ret); + pr_err("write failed %d\n", ret); gspca_dev->usb_err = ret; } } @@ -794,7 +796,7 @@ static u8 ov534_reg_read(struct gspca_dev *gspca_dev, u16 reg) 0x00, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT); PDEBUG(D_USBI, "GET 01 0000 %04x %02x", reg, gspca_dev->usb_buf[0]); if (ret < 0) { - err("read failed %d", ret); + pr_err("read failed %d\n", ret); gspca_dev->usb_err = ret; } return gspca_dev->usb_buf[0]; @@ -858,7 +860,7 @@ static void sccb_reg_write(struct gspca_dev *gspca_dev, u8 reg, u8 val) ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_3); if (!sccb_check_status(gspca_dev)) { - err("sccb_reg_write failed"); + pr_err("sccb_reg_write failed\n"); gspca_dev->usb_err = -EIO; } } @@ -868,11 +870,11 @@ static u8 sccb_reg_read(struct gspca_dev *gspca_dev, u16 reg) ov534_reg_write(gspca_dev, OV534_REG_SUBADDR, reg); ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_2); if (!sccb_check_status(gspca_dev)) - err("sccb_reg_read failed 1"); + pr_err("sccb_reg_read failed 1\n"); ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_READ_2); if (!sccb_check_status(gspca_dev)) - err("sccb_reg_read failed 2"); + pr_err("sccb_reg_read failed 2\n"); return ov534_reg_read(gspca_dev, OV534_REG_READ); } diff --git a/drivers/media/video/gspca/ov534_9.c b/drivers/media/video/gspca/ov534_9.c index aaf5428c57f..b3b1ea60a84 100644 --- a/drivers/media/video/gspca/ov534_9.c +++ b/drivers/media/video/gspca/ov534_9.c @@ -1,7 +1,7 @@ /* - * ov534-ov965x gspca driver + * ov534-ov9xxx gspca driver * - * Copyright (C) 2009-2010 Jean-Francois Moine http://moinejf.free.fr + * Copyright (C) 2009-2011 Jean-Francois Moine http://moinejf.free.fr * Copyright (C) 2008 Antonio Ospite <ospite@studenti.unina.it> * Copyright (C) 2008 Jim Paris <jim@jtan.com> * @@ -24,6 +24,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #define MODULE_NAME "ov534_9" #include "gspca.h" @@ -45,39 +47,44 @@ MODULE_AUTHOR("Jean-Francois Moine <moinejf@free.fr>"); MODULE_DESCRIPTION("GSPCA/OV534_9 USB Camera Driver"); MODULE_LICENSE("GPL"); +/* controls */ +enum e_ctrl { + BRIGHTNESS, + CONTRAST, + AUTOGAIN, + EXPOSURE, + SHARPNESS, + SATUR, + LIGHTFREQ, + NCTRLS /* number of controls */ +}; + /* specific webcam descriptor */ struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ + struct gspca_ctrl ctrls[NCTRLS]; __u32 last_pts; u8 last_fid; - u8 brightness; - u8 contrast; - u8 autogain; - u8 exposure; - s8 sharpness; - u8 satur; - u8 freq; + u8 sensor; +}; +enum sensors { + SENSOR_OV965x, /* ov9657 */ + SENSOR_OV971x, /* ov9712 */ + NSENSORS }; /* V4L2 controls supported by the driver */ -static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_setsatur(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getsatur(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val); - -static const struct ctrl sd_ctrls[] = { - { /* 0 */ +static void setbrightness(struct gspca_dev *gspca_dev); +static void setcontrast(struct gspca_dev *gspca_dev); +static void setautogain(struct gspca_dev *gspca_dev); +static void setexposure(struct gspca_dev *gspca_dev); +static void setsharpness(struct gspca_dev *gspca_dev); +static void setsatur(struct gspca_dev *gspca_dev); +static void setlightfreq(struct gspca_dev *gspca_dev); + +static const struct ctrl sd_ctrls[NCTRLS] = { +[BRIGHTNESS] = { { .id = V4L2_CID_BRIGHTNESS, .type = V4L2_CTRL_TYPE_INTEGER, @@ -85,13 +92,11 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 15, .step = 1, -#define BRIGHTNESS_DEF 7 - .default_value = BRIGHTNESS_DEF, + .default_value = 7 }, - .set = sd_setbrightness, - .get = sd_getbrightness, + .set_control = setbrightness }, - { /* 1 */ +[CONTRAST] = { { .id = V4L2_CID_CONTRAST, .type = V4L2_CTRL_TYPE_INTEGER, @@ -99,13 +104,11 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 15, .step = 1, -#define CONTRAST_DEF 3 - .default_value = CONTRAST_DEF, + .default_value = 3 }, - .set = sd_setcontrast, - .get = sd_getcontrast, + .set_control = setcontrast }, - { /* 2 */ +[AUTOGAIN] = { { .id = V4L2_CID_AUTOGAIN, .type = V4L2_CTRL_TYPE_BOOLEAN, @@ -116,11 +119,9 @@ static const struct ctrl sd_ctrls[] = { #define AUTOGAIN_DEF 1 .default_value = AUTOGAIN_DEF, }, - .set = sd_setautogain, - .get = sd_getautogain, + .set_control = setautogain }, -#define EXPO_IDX 3 - { /* 3 */ +[EXPOSURE] = { { .id = V4L2_CID_EXPOSURE, .type = V4L2_CTRL_TYPE_INTEGER, @@ -128,13 +129,11 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 3, .step = 1, -#define EXPO_DEF 0 - .default_value = EXPO_DEF, + .default_value = 0 }, - .set = sd_setexposure, - .get = sd_getexposure, + .set_control = setexposure }, - { /* 4 */ +[SHARPNESS] = { { .id = V4L2_CID_SHARPNESS, .type = V4L2_CTRL_TYPE_INTEGER, @@ -142,13 +141,11 @@ static const struct ctrl sd_ctrls[] = { .minimum = -1, /* -1 = auto */ .maximum = 4, .step = 1, -#define SHARPNESS_DEF -1 - .default_value = SHARPNESS_DEF, + .default_value = -1 }, - .set = sd_setsharpness, - .get = sd_getsharpness, + .set_control = setsharpness }, - { /* 5 */ +[SATUR] = { { .id = V4L2_CID_SATURATION, .type = V4L2_CTRL_TYPE_INTEGER, @@ -156,13 +153,11 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 4, .step = 1, -#define SATUR_DEF 2 - .default_value = SATUR_DEF, + .default_value = 2 }, - .set = sd_setsatur, - .get = sd_getsatur, + .set_control = setsatur }, - { +[LIGHTFREQ] = { { .id = V4L2_CID_POWER_LINE_FREQUENCY, .type = V4L2_CTRL_TYPE_MENU, @@ -170,11 +165,9 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */ .step = 1, -#define FREQ_DEF 0 - .default_value = FREQ_DEF, + .default_value = 0 }, - .set = sd_setfreq, - .get = sd_getfreq, + .set_control = setlightfreq }, }; @@ -206,6 +199,14 @@ static const struct v4l2_pix_format ov965x_mode[] = { .colorspace = V4L2_COLORSPACE_JPEG}, }; +static const struct v4l2_pix_format ov971x_mode[] = { + {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480, + .colorspace = V4L2_COLORSPACE_SRGB + } +}; + static const u8 bridge_init[][2] = { {0x88, 0xf8}, {0x89, 0xff}, @@ -240,7 +241,7 @@ static const u8 bridge_init[][2] = { {0x94, 0x11}, }; -static const u8 sensor_init[][2] = { +static const u8 ov965x_init[][2] = { {0x12, 0x80}, /* com7 - SSCB reset */ {0x00, 0x00}, /* gain */ {0x01, 0x80}, /* blue */ @@ -450,7 +451,7 @@ static const u8 bridge_init_2[][2] = { {0x94, 0x11}, }; -static const u8 sensor_init_2[][2] = { +static const u8 ov965x_init_2[][2] = { {0x3b, 0xc4}, {0x1e, 0x04}, /* mvfp */ {0x13, 0xe0}, /* com8 */ @@ -492,7 +493,65 @@ static const u8 sensor_init_2[][2] = { {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */ }; -static const u8 sensor_start_1_vga[][2] = { /* same for qvga */ +static const u8 ov971x_init[][2] = { + {0x12, 0x80}, + {0x09, 0x10}, + {0x1e, 0x07}, + {0x5f, 0x18}, + {0x69, 0x04}, + {0x65, 0x2a}, + {0x68, 0x0a}, + {0x39, 0x28}, + {0x4d, 0x90}, + {0xc1, 0x80}, + {0x0c, 0x30}, + {0x6d, 0x02}, + {0x96, 0xf1}, + {0xbc, 0x68}, + {0x12, 0x00}, + {0x3b, 0x00}, + {0x97, 0x80}, + {0x17, 0x25}, + {0x18, 0xa2}, + {0x19, 0x01}, + {0x1a, 0xca}, + {0x03, 0x0a}, + {0x32, 0x07}, + {0x98, 0x40}, /*{0x98, 0x00},*/ + {0x99, 0xA0}, /*{0x99, 0x00},*/ + {0x9a, 0x01}, /*{0x9a, 0x00},*/ + {0x57, 0x00}, + {0x58, 0x78}, /*{0x58, 0xc8},*/ + {0x59, 0x50}, /*{0x59, 0xa0},*/ + {0x4c, 0x13}, + {0x4b, 0x36}, + {0x3d, 0x3c}, + {0x3e, 0x03}, + {0xbd, 0x50}, /*{0xbd, 0xa0},*/ + {0xbe, 0x78}, /*{0xbe, 0xc8},*/ + {0x4e, 0x55}, + {0x4f, 0x55}, + {0x50, 0x55}, + {0x51, 0x55}, + {0x24, 0x55}, + {0x25, 0x40}, + {0x26, 0xa1}, + {0x5c, 0x59}, + {0x5d, 0x00}, + {0x11, 0x00}, + {0x2a, 0x98}, + {0x2b, 0x06}, + {0x2d, 0x00}, + {0x2e, 0x00}, + {0x13, 0xa5}, + {0x14, 0x40}, + {0x4a, 0x00}, + {0x49, 0xce}, + {0x22, 0x03}, + {0x09, 0x00} +}; + +static const u8 ov965x_start_1_vga[][2] = { /* same for qvga */ {0x12, 0x62}, /* com7 - 30fps VGA YUV */ {0x36, 0xfa}, /* aref3 */ {0x69, 0x0a}, /* hv */ @@ -515,7 +574,7 @@ static const u8 sensor_start_1_vga[][2] = { /* same for qvga */ {0xc0, 0xaa}, }; -static const u8 sensor_start_1_svga[][2] = { +static const u8 ov965x_start_1_svga[][2] = { {0x12, 0x02}, /* com7 - YUYV - VGA 15 full resolution */ {0x36, 0xf8}, /* aref3 */ {0x69, 0x02}, /* hv */ @@ -537,7 +596,7 @@ static const u8 sensor_start_1_svga[][2] = { {0xc0, 0xe2}, }; -static const u8 sensor_start_1_xga[][2] = { +static const u8 ov965x_start_1_xga[][2] = { {0x12, 0x02}, /* com7 */ {0x36, 0xf8}, /* aref3 */ {0x69, 0x02}, /* hv */ @@ -560,7 +619,7 @@ static const u8 sensor_start_1_xga[][2] = { {0xc0, 0xe2}, }; -static const u8 sensor_start_1_sxga[][2] = { +static const u8 ov965x_start_1_sxga[][2] = { {0x12, 0x02}, /* com7 */ {0x36, 0xf8}, /* aref3 */ {0x69, 0x02}, /* hv */ @@ -709,7 +768,7 @@ static const u8 bridge_start_sxga[][2] = { {0x94, 0x11}, }; -static const u8 sensor_start_2_qvga[][2] = { +static const u8 ov965x_start_2_qvga[][2] = { {0x3b, 0xe4}, /* com11 - night mode 1/4 frame rate */ {0x1e, 0x04}, /* mvfp */ {0x13, 0xe0}, /* com8 */ @@ -727,7 +786,7 @@ static const u8 sensor_start_2_qvga[][2] = { {0x3a, 0x80}, /* tslb - yuyv */ }; -static const u8 sensor_start_2_vga[][2] = { +static const u8 ov965x_start_2_vga[][2] = { {0x3b, 0xc4}, /* com11 - night mode 1/4 frame rate */ {0x1e, 0x04}, /* mvfp */ {0x13, 0xe0}, /* com8 */ @@ -743,7 +802,7 @@ static const u8 sensor_start_2_vga[][2] = { {0x2d, 0x00}, /* advfl */ }; -static const u8 sensor_start_2_svga[][2] = { /* same for xga */ +static const u8 ov965x_start_2_svga[][2] = { /* same for xga */ {0x3b, 0xc4}, /* com11 - night mode 1/4 frame rate */ {0x1e, 0x04}, /* mvfp */ {0x13, 0xe0}, /* com8 */ @@ -757,7 +816,7 @@ static const u8 sensor_start_2_svga[][2] = { /* same for xga */ {0xa3, 0x41}, /* bd60 */ }; -static const u8 sensor_start_2_sxga[][2] = { +static const u8 ov965x_start_2_sxga[][2] = { {0x13, 0xe0}, /* com8 */ {0x00, 0x00}, {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */ @@ -785,7 +844,7 @@ static void reg_w_i(struct gspca_dev *gspca_dev, u16 reg, u8 val) USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0x00, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT); if (ret < 0) { - err("reg_w failed %d", ret); + pr_err("reg_w failed %d\n", ret); gspca_dev->usb_err = ret; } } @@ -810,7 +869,7 @@ static u8 reg_r(struct gspca_dev *gspca_dev, u16 reg) 0x00, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT); PDEBUG(D_USBI, "reg_r [%04x] -> %02x", reg, gspca_dev->usb_buf[0]); if (ret < 0) { - err("reg_r err %d", ret); + pr_err("reg_r err %d\n", ret); gspca_dev->usb_err = ret; } return gspca_dev->usb_buf[0]; @@ -848,7 +907,7 @@ static void sccb_write(struct gspca_dev *gspca_dev, u8 reg, u8 val) reg_w_i(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_3); if (!sccb_check_status(gspca_dev)) - err("sccb_write failed"); + pr_err("sccb_write failed\n"); } static u8 sccb_read(struct gspca_dev *gspca_dev, u16 reg) @@ -856,11 +915,11 @@ static u8 sccb_read(struct gspca_dev *gspca_dev, u16 reg) reg_w(gspca_dev, OV534_REG_SUBADDR, reg); reg_w(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_2); if (!sccb_check_status(gspca_dev)) - err("sccb_read failed 1"); + pr_err("sccb_read failed 1\n"); reg_w(gspca_dev, OV534_REG_OPERATION, OV534_OP_READ_2); if (!sccb_check_status(gspca_dev)) - err("sccb_read failed 2"); + pr_err("sccb_read failed 2\n"); return reg_r(gspca_dev, OV534_REG_READ); } @@ -922,7 +981,9 @@ static void setbrightness(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; u8 val; - val = sd->brightness; + if (gspca_dev->ctrl_dis & (1 << BRIGHTNESS)) + return; + val = sd->ctrls[BRIGHTNESS].val; if (val < 8) val = 15 - val; /* f .. 8 */ else @@ -935,8 +996,10 @@ static void setcontrast(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + if (gspca_dev->ctrl_dis & (1 << CONTRAST)) + return; sccb_write(gspca_dev, 0x56, /* cnst1 - contrast 1 ctrl coeff */ - sd->contrast << 4); + sd->ctrls[CONTRAST].val << 4); } static void setautogain(struct gspca_dev *gspca_dev) @@ -944,10 +1007,12 @@ static void setautogain(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; u8 val; + if (gspca_dev->ctrl_dis & (1 << AUTOGAIN)) + return; /*fixme: should adjust agc/awb/aec by different controls */ val = sccb_read(gspca_dev, 0x13); /* com8 */ sccb_write(gspca_dev, 0xff, 0x00); - if (sd->autogain) + if (sd->ctrls[AUTOGAIN].val) val |= 0x05; /* agc & aec */ else val &= 0xfa; @@ -960,8 +1025,10 @@ static void setexposure(struct gspca_dev *gspca_dev) u8 val; static const u8 expo[4] = {0x00, 0x25, 0x38, 0x5e}; + if (gspca_dev->ctrl_dis & (1 << EXPOSURE)) + return; sccb_write(gspca_dev, 0x10, /* aec[9:2] */ - expo[sd->exposure]); + expo[sd->ctrls[EXPOSURE].val]); val = sccb_read(gspca_dev, 0x13); /* com8 */ sccb_write(gspca_dev, 0xff, 0x00); @@ -977,7 +1044,9 @@ static void setsharpness(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; s8 val; - val = sd->sharpness; + if (gspca_dev->ctrl_dis & (1 << SHARPNESS)) + return; + val = sd->ctrls[SHARPNESS].val; if (val < 0) { /* auto */ val = sccb_read(gspca_dev, 0x42); /* com17 */ sccb_write(gspca_dev, 0xff, 0x00); @@ -1006,8 +1075,10 @@ static void setsatur(struct gspca_dev *gspca_dev) {0x48, 0x90} }; - val1 = matrix[sd->satur][0]; - val2 = matrix[sd->satur][1]; + if (gspca_dev->ctrl_dis & (1 << SATUR)) + return; + val1 = matrix[sd->ctrls[SATUR].val][0]; + val2 = matrix[sd->ctrls[SATUR].val][1]; val3 = val1 + val2; sccb_write(gspca_dev, 0x4f, val3); /* matrix coeff */ sccb_write(gspca_dev, 0x50, val3); @@ -1022,14 +1093,16 @@ static void setsatur(struct gspca_dev *gspca_dev) sccb_write(gspca_dev, 0x41, val1); } -static void setfreq(struct gspca_dev *gspca_dev) +static void setlightfreq(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; u8 val; + if (gspca_dev->ctrl_dis & (1 << LIGHTFREQ)) + return; val = sccb_read(gspca_dev, 0x13); /* com8 */ sccb_write(gspca_dev, 0xff, 0x00); - if (sd->freq == 0) { + if (sd->ctrls[LIGHTFREQ].val == 0) { sccb_write(gspca_dev, 0x13, val & 0xdf); return; } @@ -1037,7 +1110,7 @@ static void setfreq(struct gspca_dev *gspca_dev) val = sccb_read(gspca_dev, 0x42); /* com17 */ sccb_write(gspca_dev, 0xff, 0x00); - if (sd->freq == 1) + if (sd->ctrls[LIGHTFREQ].val == 1) val |= 0x01; else val &= 0xfe; @@ -1049,34 +1122,19 @@ static int sd_config(struct gspca_dev *gspca_dev, const struct usb_device_id *id) { struct sd *sd = (struct sd *) gspca_dev; - struct cam *cam; - - cam = &gspca_dev->cam; - cam->cam_mode = ov965x_mode; - cam->nmodes = ARRAY_SIZE(ov965x_mode); + gspca_dev->cam.ctrls = sd->ctrls; - sd->brightness = BRIGHTNESS_DEF; - sd->contrast = CONTRAST_DEF; #if AUTOGAIN_DEF != 0 - sd->autogain = AUTOGAIN_DEF; - gspca_dev->ctrl_inac |= (1 << EXPO_IDX); -#endif -#if EXPO_DEF != 0 - sd->exposure = EXPO_DEF; -#endif -#if SHARPNESS_DEF != 0 - sd->sharpness = SHARPNESS_DEF; + gspca_dev->ctrl_inac |= (1 << EXPOSURE); #endif - sd->satur = SATUR_DEF; - sd->freq = FREQ_DEF; - return 0; } /* this function is called at probe and resume time */ static int sd_init(struct gspca_dev *gspca_dev) { + struct sd *sd = (struct sd *) gspca_dev; u16 sensor_id; /* reset bridge */ @@ -1099,68 +1157,117 @@ static int sd_init(struct gspca_dev *gspca_dev) PDEBUG(D_PROBE, "Sensor ID: %04x", sensor_id); /* initialize */ - reg_w_array(gspca_dev, bridge_init, - ARRAY_SIZE(bridge_init)); - sccb_w_array(gspca_dev, sensor_init, - ARRAY_SIZE(sensor_init)); - reg_w_array(gspca_dev, bridge_init_2, - ARRAY_SIZE(bridge_init_2)); - sccb_w_array(gspca_dev, sensor_init_2, - ARRAY_SIZE(sensor_init_2)); - reg_w(gspca_dev, 0xe0, 0x00); - reg_w(gspca_dev, 0xe0, 0x01); - set_led(gspca_dev, 0); - reg_w(gspca_dev, 0xe0, 0x00); + if ((sensor_id & 0xfff0) == 0x9650) { + sd->sensor = SENSOR_OV965x; + + gspca_dev->cam.cam_mode = ov965x_mode; + gspca_dev->cam.nmodes = ARRAY_SIZE(ov965x_mode); + + reg_w_array(gspca_dev, bridge_init, + ARRAY_SIZE(bridge_init)); + sccb_w_array(gspca_dev, ov965x_init, + ARRAY_SIZE(ov965x_init)); + reg_w_array(gspca_dev, bridge_init_2, + ARRAY_SIZE(bridge_init_2)); + sccb_w_array(gspca_dev, ov965x_init_2, + ARRAY_SIZE(ov965x_init_2)); + reg_w(gspca_dev, 0xe0, 0x00); + reg_w(gspca_dev, 0xe0, 0x01); + set_led(gspca_dev, 0); + reg_w(gspca_dev, 0xe0, 0x00); + } else if ((sensor_id & 0xfff0) == 0x9710) { + const char *p; + int l; + + sd->sensor = SENSOR_OV971x; + + gspca_dev->cam.cam_mode = ov971x_mode; + gspca_dev->cam.nmodes = ARRAY_SIZE(ov971x_mode); + + /* no control yet */ + gspca_dev->ctrl_dis = (1 << NCTRLS) - 1; + + gspca_dev->cam.bulk = 1; + gspca_dev->cam.bulk_size = 16384; + gspca_dev->cam.bulk_nurbs = 2; + + sccb_w_array(gspca_dev, ov971x_init, + ARRAY_SIZE(ov971x_init)); + + /* set video format on bridge processor */ + /* access bridge processor's video format registers at: 0x00 */ + reg_w(gspca_dev, 0x1c, 0x00); + /*set register: 0x00 is 'RAW8', 0x40 is 'YUV422' (YUYV?)*/ + reg_w(gspca_dev, 0x1d, 0x00); + + /* Will W. specific stuff + * set VSYNC to + * output (0x1f) if first webcam + * input (0x17) if 2nd or 3rd webcam */ + p = video_device_node_name(&gspca_dev->vdev); + l = strlen(p) - 1; + if (p[l] == '0') + reg_w(gspca_dev, 0x56, 0x1f); + else + reg_w(gspca_dev, 0x56, 0x17); + } else { + err("Unknown sensor %04x", sensor_id); + return -EINVAL; + } return gspca_dev->usb_err; } static int sd_start(struct gspca_dev *gspca_dev) { + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->sensor == SENSOR_OV971x) + return gspca_dev->usb_err; switch (gspca_dev->curr_mode) { case QVGA_MODE: /* 320x240 */ - sccb_w_array(gspca_dev, sensor_start_1_vga, - ARRAY_SIZE(sensor_start_1_vga)); + sccb_w_array(gspca_dev, ov965x_start_1_vga, + ARRAY_SIZE(ov965x_start_1_vga)); reg_w_array(gspca_dev, bridge_start_qvga, ARRAY_SIZE(bridge_start_qvga)); - sccb_w_array(gspca_dev, sensor_start_2_qvga, - ARRAY_SIZE(sensor_start_2_qvga)); + sccb_w_array(gspca_dev, ov965x_start_2_qvga, + ARRAY_SIZE(ov965x_start_2_qvga)); break; case VGA_MODE: /* 640x480 */ - sccb_w_array(gspca_dev, sensor_start_1_vga, - ARRAY_SIZE(sensor_start_1_vga)); + sccb_w_array(gspca_dev, ov965x_start_1_vga, + ARRAY_SIZE(ov965x_start_1_vga)); reg_w_array(gspca_dev, bridge_start_vga, ARRAY_SIZE(bridge_start_vga)); - sccb_w_array(gspca_dev, sensor_start_2_vga, - ARRAY_SIZE(sensor_start_2_vga)); + sccb_w_array(gspca_dev, ov965x_start_2_vga, + ARRAY_SIZE(ov965x_start_2_vga)); break; case SVGA_MODE: /* 800x600 */ - sccb_w_array(gspca_dev, sensor_start_1_svga, - ARRAY_SIZE(sensor_start_1_svga)); + sccb_w_array(gspca_dev, ov965x_start_1_svga, + ARRAY_SIZE(ov965x_start_1_svga)); reg_w_array(gspca_dev, bridge_start_svga, ARRAY_SIZE(bridge_start_svga)); - sccb_w_array(gspca_dev, sensor_start_2_svga, - ARRAY_SIZE(sensor_start_2_svga)); + sccb_w_array(gspca_dev, ov965x_start_2_svga, + ARRAY_SIZE(ov965x_start_2_svga)); break; case XGA_MODE: /* 1024x768 */ - sccb_w_array(gspca_dev, sensor_start_1_xga, - ARRAY_SIZE(sensor_start_1_xga)); + sccb_w_array(gspca_dev, ov965x_start_1_xga, + ARRAY_SIZE(ov965x_start_1_xga)); reg_w_array(gspca_dev, bridge_start_xga, ARRAY_SIZE(bridge_start_xga)); - sccb_w_array(gspca_dev, sensor_start_2_svga, - ARRAY_SIZE(sensor_start_2_svga)); + sccb_w_array(gspca_dev, ov965x_start_2_svga, + ARRAY_SIZE(ov965x_start_2_svga)); break; default: /* case SXGA_MODE: * 1280x1024 */ - sccb_w_array(gspca_dev, sensor_start_1_sxga, - ARRAY_SIZE(sensor_start_1_sxga)); + sccb_w_array(gspca_dev, ov965x_start_1_sxga, + ARRAY_SIZE(ov965x_start_1_sxga)); reg_w_array(gspca_dev, bridge_start_sxga, ARRAY_SIZE(bridge_start_sxga)); - sccb_w_array(gspca_dev, sensor_start_2_sxga, - ARRAY_SIZE(sensor_start_2_sxga)); + sccb_w_array(gspca_dev, ov965x_start_2_sxga, + ARRAY_SIZE(ov965x_start_2_sxga)); break; } - setfreq(gspca_dev); + setlightfreq(gspca_dev); setautogain(gspca_dev); setbrightness(gspca_dev); setcontrast(gspca_dev); @@ -1198,9 +1305,11 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, __u32 this_pts; u8 this_fid; int remaining_len = len; + int payload_len; + payload_len = gspca_dev->cam.bulk ? 2048 : 2040; do { - len = min(remaining_len, 2040); + len = min(remaining_len, payload_len); /* Payloads are prefixed with a UVC-style header. We consider a frame to start when the FID toggles, or the PTS @@ -1262,138 +1371,6 @@ scan_next: } while (remaining_len > 0); } -/* controls */ -static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->brightness = val; - if (gspca_dev->streaming) - setbrightness(gspca_dev); - return gspca_dev->usb_err; -} - -static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->brightness; - return 0; -} - -static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->contrast = val; - if (gspca_dev->streaming) - setcontrast(gspca_dev); - return gspca_dev->usb_err; -} - -static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->contrast; - return 0; -} - -static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->autogain = val; - - if (gspca_dev->streaming) { - if (val) - gspca_dev->ctrl_inac |= (1 << EXPO_IDX); - else - gspca_dev->ctrl_inac &= ~(1 << EXPO_IDX); - setautogain(gspca_dev); - } - return gspca_dev->usb_err; -} - -static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->autogain; - return 0; -} - -static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->exposure = val; - if (gspca_dev->streaming) - setexposure(gspca_dev); - return gspca_dev->usb_err; -} - -static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->exposure; - return 0; -} - -static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->sharpness = val; - if (gspca_dev->streaming) - setsharpness(gspca_dev); - return gspca_dev->usb_err; -} - -static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->sharpness; - return 0; -} - -static int sd_setsatur(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->satur = val; - if (gspca_dev->streaming) - setsatur(gspca_dev); - return gspca_dev->usb_err; -} - -static int sd_getsatur(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->satur; - return 0; -} -static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->freq = val; - if (gspca_dev->streaming) - setfreq(gspca_dev); - return gspca_dev->usb_err; -} - -static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->freq; - return 0; -} - static int sd_querymenu(struct gspca_dev *gspca_dev, struct v4l2_querymenu *menu) { @@ -1419,7 +1396,7 @@ static int sd_querymenu(struct gspca_dev *gspca_dev, static const struct sd_desc sd_desc = { .name = MODULE_NAME, .ctrls = sd_ctrls, - .nctrls = ARRAY_SIZE(sd_ctrls), + .nctrls = NCTRLS, .config = sd_config, .init = sd_init, .start = sd_start, @@ -1430,6 +1407,7 @@ static const struct sd_desc sd_desc = { /* -- module initialisation -- */ static const struct usb_device_id device_table[] = { + {USB_DEVICE(0x05a9, 0x8065)}, {USB_DEVICE(0x06f8, 0x3003)}, {} }; diff --git a/drivers/media/video/gspca/pac207.c b/drivers/media/video/gspca/pac207.c index 81739a2f205..1600df152fd 100644 --- a/drivers/media/video/gspca/pac207.c +++ b/drivers/media/video/gspca/pac207.c @@ -23,6 +23,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #define MODULE_NAME "pac207" #include <linux/input.h> @@ -178,8 +180,8 @@ static int pac207_write_regs(struct gspca_dev *gspca_dev, u16 index, 0x00, index, gspca_dev->usb_buf, length, PAC207_CTRL_TIMEOUT); if (err < 0) - err("Failed to write registers to index 0x%04X, error %d)", - index, err); + pr_err("Failed to write registers to index 0x%04X, error %d\n", + index, err); return err; } @@ -194,8 +196,8 @@ static int pac207_write_reg(struct gspca_dev *gspca_dev, u16 index, u16 value) USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, value, index, NULL, 0, PAC207_CTRL_TIMEOUT); if (err) - err("Failed to write a register (index 0x%04X," - " value 0x%02X, error %d)", index, value, err); + pr_err("Failed to write a register (index 0x%04X, value 0x%02X, error %d)\n", + index, value, err); return err; } @@ -210,8 +212,8 @@ static int pac207_read_reg(struct gspca_dev *gspca_dev, u16 index) 0x00, index, gspca_dev->usb_buf, 1, PAC207_CTRL_TIMEOUT); if (res < 0) { - err("Failed to read a register (index 0x%04X, error %d)", - index, res); + pr_err("Failed to read a register (index 0x%04X, error %d)\n", + index, res); return res; } diff --git a/drivers/media/video/gspca/pac7302.c b/drivers/media/video/gspca/pac7302.c index 5615d7bd830..1c44f78ff9e 100644 --- a/drivers/media/video/gspca/pac7302.c +++ b/drivers/media/video/gspca/pac7302.c @@ -61,6 +61,8 @@ 3 | 0x21 | sethvflip() */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #define MODULE_NAME "pac7302" #include <linux/input.h> @@ -408,8 +410,8 @@ static void reg_w_buf(struct gspca_dev *gspca_dev, index, gspca_dev->usb_buf, len, 500); if (ret < 0) { - err("reg_w_buf failed index 0x%02x, error %d", - index, ret); + pr_err("reg_w_buf failed index 0x%02x, error %d\n", + index, ret); gspca_dev->usb_err = ret; } } @@ -431,8 +433,8 @@ static void reg_w(struct gspca_dev *gspca_dev, 0, index, gspca_dev->usb_buf, 1, 500); if (ret < 0) { - err("reg_w() failed index 0x%02x, value 0x%02x, error %d", - index, value, ret); + pr_err("reg_w() failed index 0x%02x, value 0x%02x, error %d\n", + index, value, ret); gspca_dev->usb_err = ret; } } @@ -466,9 +468,8 @@ static void reg_w_page(struct gspca_dev *gspca_dev, 0, index, gspca_dev->usb_buf, 1, 500); if (ret < 0) { - err("reg_w_page() failed index 0x%02x, " - "value 0x%02x, error %d", - index, page[index], ret); + pr_err("reg_w_page() failed index 0x%02x, value 0x%02x, error %d\n", + index, page[index], ret); gspca_dev->usb_err = ret; break; } diff --git a/drivers/media/video/gspca/pac7311.c b/drivers/media/video/gspca/pac7311.c index f8801b50e64..7509d05dc06 100644 --- a/drivers/media/video/gspca/pac7311.c +++ b/drivers/media/video/gspca/pac7311.c @@ -49,6 +49,8 @@ for max gain, 0x14 for minimal gain. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #define MODULE_NAME "pac7311" #include <linux/input.h> @@ -276,8 +278,8 @@ static void reg_w_buf(struct gspca_dev *gspca_dev, index, gspca_dev->usb_buf, len, 500); if (ret < 0) { - err("reg_w_buf() failed index 0x%02x, error %d", - index, ret); + pr_err("reg_w_buf() failed index 0x%02x, error %d\n", + index, ret); gspca_dev->usb_err = ret; } } @@ -299,8 +301,8 @@ static void reg_w(struct gspca_dev *gspca_dev, 0, index, gspca_dev->usb_buf, 1, 500); if (ret < 0) { - err("reg_w() failed index 0x%02x, value 0x%02x, error %d", - index, value, ret); + pr_err("reg_w() failed index 0x%02x, value 0x%02x, error %d\n", + index, value, ret); gspca_dev->usb_err = ret; } } @@ -334,9 +336,8 @@ static void reg_w_page(struct gspca_dev *gspca_dev, 0, index, gspca_dev->usb_buf, 1, 500); if (ret < 0) { - err("reg_w_page() failed index 0x%02x, " - "value 0x%02x, error %d", - index, page[index], ret); + pr_err("reg_w_page() failed index 0x%02x, value 0x%02x, error %d\n", + index, page[index], ret); gspca_dev->usb_err = ret; break; } diff --git a/drivers/media/video/gspca/se401.c b/drivers/media/video/gspca/se401.c index 4c283c24c75..3b71bbcd977 100644 --- a/drivers/media/video/gspca/se401.c +++ b/drivers/media/video/gspca/se401.c @@ -23,6 +23,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #define MODULE_NAME "se401" #define BULK_SIZE 4096 @@ -144,8 +146,8 @@ static void se401_write_req(struct gspca_dev *gspca_dev, u16 req, u16 value, value, 0, NULL, 0, 1000); if (err < 0) { if (!silent) - err("write req failed req %#04x val %#04x error %d", - req, value, err); + pr_err("write req failed req %#04x val %#04x error %d\n", + req, value, err); gspca_dev->usb_err = err; } } @@ -158,7 +160,7 @@ static void se401_read_req(struct gspca_dev *gspca_dev, u16 req, int silent) return; if (USB_BUF_SZ < READ_REQ_SIZE) { - err("USB_BUF_SZ too small!!"); + pr_err("USB_BUF_SZ too small!!\n"); gspca_dev->usb_err = -ENOBUFS; return; } @@ -169,7 +171,8 @@ static void se401_read_req(struct gspca_dev *gspca_dev, u16 req, int silent) 0, 0, gspca_dev->usb_buf, READ_REQ_SIZE, 1000); if (err < 0) { if (!silent) - err("read req failed req %#04x error %d", req, err); + pr_err("read req failed req %#04x error %d\n", + req, err); gspca_dev->usb_err = err; } } @@ -188,8 +191,8 @@ static void se401_set_feature(struct gspca_dev *gspca_dev, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, param, selector, NULL, 0, 1000); if (err < 0) { - err("set feature failed sel %#04x param %#04x error %d", - selector, param, err); + pr_err("set feature failed sel %#04x param %#04x error %d\n", + selector, param, err); gspca_dev->usb_err = err; } } @@ -202,7 +205,7 @@ static int se401_get_feature(struct gspca_dev *gspca_dev, u16 selector) return gspca_dev->usb_err; if (USB_BUF_SZ < 2) { - err("USB_BUF_SZ too small!!"); + pr_err("USB_BUF_SZ too small!!\n"); gspca_dev->usb_err = -ENOBUFS; return gspca_dev->usb_err; } @@ -213,7 +216,8 @@ static int se401_get_feature(struct gspca_dev *gspca_dev, u16 selector) USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, selector, gspca_dev->usb_buf, 2, 1000); if (err < 0) { - err("get feature failed sel %#04x error %d", selector, err); + pr_err("get feature failed sel %#04x error %d\n", + selector, err); gspca_dev->usb_err = err; return err; } @@ -300,21 +304,21 @@ static int sd_config(struct gspca_dev *gspca_dev, return gspca_dev->usb_err; if (cd[1] != 0x41) { - err("Wrong descriptor type"); + pr_err("Wrong descriptor type\n"); return -ENODEV; } if (!(cd[2] & SE401_FORMAT_BAYER)) { - err("Bayer format not supported!"); + pr_err("Bayer format not supported!\n"); return -ENODEV; } if (cd[3]) - info("ExtraFeatures: %d", cd[3]); + pr_info("ExtraFeatures: %d\n", cd[3]); n = cd[4] | (cd[5] << 8); if (n > MAX_MODES) { - err("Too many frame sizes"); + pr_err("Too many frame sizes\n"); return -ENODEV; } @@ -353,15 +357,16 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->fmts[i].pixelformat = V4L2_PIX_FMT_SBGGR8; sd->fmts[i].bytesperline = widths[i]; sd->fmts[i].sizeimage = widths[i] * heights[i]; - info("Frame size: %dx%d bayer", widths[i], heights[i]); + pr_info("Frame size: %dx%d bayer\n", + widths[i], heights[i]); } else { /* Found a match use janggu compression */ sd->fmts[i].pixelformat = V4L2_PIX_FMT_SE401; sd->fmts[i].bytesperline = 0; sd->fmts[i].sizeimage = widths[i] * heights[i] * 3; - info("Frame size: %dx%d 1/%dth janggu", - widths[i], heights[i], - sd->fmts[i].priv * sd->fmts[i].priv); + pr_info("Frame size: %dx%d 1/%dth janggu\n", + widths[i], heights[i], + sd->fmts[i].priv * sd->fmts[i].priv); } } @@ -571,11 +576,12 @@ static void sd_pkt_scan_janggu(struct gspca_dev *gspca_dev, u8 *data, int len) plen = ((bits + 47) >> 4) << 1; /* Sanity checks */ if (plen > 1024) { - err("invalid packet len %d restarting stream", plen); + pr_err("invalid packet len %d restarting stream\n", + plen); goto error; } if (info == 3) { - err("unknown frame info value restarting stream"); + pr_err("unknown frame info value restarting stream\n"); goto error; } @@ -599,8 +605,8 @@ static void sd_pkt_scan_janggu(struct gspca_dev *gspca_dev, u8 *data, int len) break; case 1: /* EOF */ if (sd->pixels_read != imagesize) { - err("frame size %d expected %d", - sd->pixels_read, imagesize); + pr_err("frame size %d expected %d\n", + sd->pixels_read, imagesize); goto error; } sd_complete_frame(gspca_dev, sd->packet, plen); diff --git a/drivers/media/video/gspca/sn9c2028.c b/drivers/media/video/gspca/sn9c2028.c index 4271f86dfe0..48aae3926a3 100644 --- a/drivers/media/video/gspca/sn9c2028.c +++ b/drivers/media/video/gspca/sn9c2028.c @@ -18,6 +18,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #define MODULE_NAME "sn9c2028" #include "gspca.h" @@ -75,8 +77,8 @@ static int sn9c2028_command(struct gspca_dev *gspca_dev, u8 *command) USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, 2, 0, gspca_dev->usb_buf, 6, 500); if (rc < 0) { - err("command write [%02x] error %d", - gspca_dev->usb_buf[0], rc); + pr_err("command write [%02x] error %d\n", + gspca_dev->usb_buf[0], rc); return rc; } @@ -93,7 +95,7 @@ static int sn9c2028_read1(struct gspca_dev *gspca_dev) USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, 1, 0, gspca_dev->usb_buf, 1, 500); if (rc != 1) { - err("read1 error %d", rc); + pr_err("read1 error %d\n", rc); return (rc < 0) ? rc : -EIO; } PDEBUG(D_USBI, "read1 response %02x", gspca_dev->usb_buf[0]); @@ -109,7 +111,7 @@ static int sn9c2028_read4(struct gspca_dev *gspca_dev, u8 *reading) USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, 4, 0, gspca_dev->usb_buf, 4, 500); if (rc != 4) { - err("read4 error %d", rc); + pr_err("read4 error %d\n", rc); return (rc < 0) ? rc : -EIO; } memcpy(reading, gspca_dev->usb_buf, 4); @@ -131,7 +133,7 @@ static int sn9c2028_long_command(struct gspca_dev *gspca_dev, u8 *command) for (i = 0; i < 256 && status < 2; i++) status = sn9c2028_read1(gspca_dev); if (status != 2) { - err("long command status read error %d", status); + pr_err("long command status read error %d\n", status); return (status < 0) ? status : -EIO; } @@ -638,7 +640,7 @@ static int sd_start(struct gspca_dev *gspca_dev) err_code = start_vivitar_cam(gspca_dev); break; default: - err("Starting unknown camera, please report this"); + pr_err("Starting unknown camera, please report this\n"); return -ENXIO; } diff --git a/drivers/media/video/gspca/sn9c20x.c b/drivers/media/video/gspca/sn9c20x.c index c431900cd29..86e07a139a1 100644 --- a/drivers/media/video/gspca/sn9c20x.c +++ b/drivers/media/video/gspca/sn9c20x.c @@ -18,6 +18,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/input.h> #include "gspca.h" @@ -1123,7 +1125,7 @@ static int reg_r(struct gspca_dev *gspca_dev, u16 reg, u16 length) length, 500); if (unlikely(result < 0 || result != length)) { - err("Read register failed 0x%02X", reg); + pr_err("Read register failed 0x%02X\n", reg); return -EIO; } return 0; @@ -1144,7 +1146,7 @@ static int reg_w(struct gspca_dev *gspca_dev, u16 reg, length, 500); if (unlikely(result < 0 || result != length)) { - err("Write register failed index 0x%02X", reg); + pr_err("Write register failed index 0x%02X\n", reg); return -EIO; } return 0; @@ -1275,14 +1277,14 @@ static int ov9650_init_sensor(struct gspca_dev *gspca_dev) return -EINVAL; if (id != 0x7fa2) { - err("sensor id for ov9650 doesn't match (0x%04x)", id); + pr_err("sensor id for ov9650 doesn't match (0x%04x)\n", id); return -ENODEV; } for (i = 0; i < ARRAY_SIZE(ov9650_init); i++) { if (i2c_w1(gspca_dev, ov9650_init[i].reg, ov9650_init[i].val) < 0) { - err("OV9650 sensor initialization failed"); + pr_err("OV9650 sensor initialization failed\n"); return -ENODEV; } } @@ -1299,7 +1301,7 @@ static int ov9655_init_sensor(struct gspca_dev *gspca_dev) for (i = 0; i < ARRAY_SIZE(ov9655_init); i++) { if (i2c_w1(gspca_dev, ov9655_init[i].reg, ov9655_init[i].val) < 0) { - err("OV9655 sensor initialization failed"); + pr_err("OV9655 sensor initialization failed\n"); return -ENODEV; } } @@ -1318,7 +1320,7 @@ static int soi968_init_sensor(struct gspca_dev *gspca_dev) for (i = 0; i < ARRAY_SIZE(soi968_init); i++) { if (i2c_w1(gspca_dev, soi968_init[i].reg, soi968_init[i].val) < 0) { - err("SOI968 sensor initialization failed"); + pr_err("SOI968 sensor initialization failed\n"); return -ENODEV; } } @@ -1338,7 +1340,7 @@ static int ov7660_init_sensor(struct gspca_dev *gspca_dev) for (i = 0; i < ARRAY_SIZE(ov7660_init); i++) { if (i2c_w1(gspca_dev, ov7660_init[i].reg, ov7660_init[i].val) < 0) { - err("OV7660 sensor initialization failed"); + pr_err("OV7660 sensor initialization failed\n"); return -ENODEV; } } @@ -1355,7 +1357,7 @@ static int ov7670_init_sensor(struct gspca_dev *gspca_dev) for (i = 0; i < ARRAY_SIZE(ov7670_init); i++) { if (i2c_w1(gspca_dev, ov7670_init[i].reg, ov7670_init[i].val) < 0) { - err("OV7670 sensor initialization failed"); + pr_err("OV7670 sensor initialization failed\n"); return -ENODEV; } } @@ -1379,14 +1381,14 @@ static int mt9v_init_sensor(struct gspca_dev *gspca_dev) for (i = 0; i < ARRAY_SIZE(mt9v011_init); i++) { if (i2c_w2(gspca_dev, mt9v011_init[i].reg, mt9v011_init[i].val) < 0) { - err("MT9V011 sensor initialization failed"); + pr_err("MT9V011 sensor initialization failed\n"); return -ENODEV; } } sd->hstart = 2; sd->vstart = 2; sd->sensor = SENSOR_MT9V011; - info("MT9V011 sensor detected"); + pr_info("MT9V011 sensor detected\n"); return 0; } @@ -1397,7 +1399,7 @@ static int mt9v_init_sensor(struct gspca_dev *gspca_dev) for (i = 0; i < ARRAY_SIZE(mt9v111_init); i++) { if (i2c_w2(gspca_dev, mt9v111_init[i].reg, mt9v111_init[i].val) < 0) { - err("MT9V111 sensor initialization failed"); + pr_err("MT9V111 sensor initialization failed\n"); return -ENODEV; } } @@ -1407,7 +1409,7 @@ static int mt9v_init_sensor(struct gspca_dev *gspca_dev) sd->hstart = 2; sd->vstart = 2; sd->sensor = SENSOR_MT9V111; - info("MT9V111 sensor detected"); + pr_info("MT9V111 sensor detected\n"); return 0; } @@ -1422,14 +1424,14 @@ static int mt9v_init_sensor(struct gspca_dev *gspca_dev) for (i = 0; i < ARRAY_SIZE(mt9v112_init); i++) { if (i2c_w2(gspca_dev, mt9v112_init[i].reg, mt9v112_init[i].val) < 0) { - err("MT9V112 sensor initialization failed"); + pr_err("MT9V112 sensor initialization failed\n"); return -ENODEV; } } sd->hstart = 6; sd->vstart = 2; sd->sensor = SENSOR_MT9V112; - info("MT9V112 sensor detected"); + pr_info("MT9V112 sensor detected\n"); return 0; } @@ -1443,7 +1445,7 @@ static int mt9m112_init_sensor(struct gspca_dev *gspca_dev) for (i = 0; i < ARRAY_SIZE(mt9m112_init); i++) { if (i2c_w2(gspca_dev, mt9m112_init[i].reg, mt9m112_init[i].val) < 0) { - err("MT9M112 sensor initialization failed"); + pr_err("MT9M112 sensor initialization failed\n"); return -ENODEV; } } @@ -1461,7 +1463,7 @@ static int mt9m111_init_sensor(struct gspca_dev *gspca_dev) for (i = 0; i < ARRAY_SIZE(mt9m111_init); i++) { if (i2c_w2(gspca_dev, mt9m111_init[i].reg, mt9m111_init[i].val) < 0) { - err("MT9M111 sensor initialization failed"); + pr_err("MT9M111 sensor initialization failed\n"); return -ENODEV; } } @@ -1485,20 +1487,20 @@ static int mt9m001_init_sensor(struct gspca_dev *gspca_dev) switch (id) { case 0x8411: case 0x8421: - info("MT9M001 color sensor detected"); + pr_info("MT9M001 color sensor detected\n"); break; case 0x8431: - info("MT9M001 mono sensor detected"); + pr_info("MT9M001 mono sensor detected\n"); break; default: - err("No MT9M001 chip detected, ID = %x\n", id); + pr_err("No MT9M001 chip detected, ID = %x\n\n", id); return -ENODEV; } for (i = 0; i < ARRAY_SIZE(mt9m001_init); i++) { if (i2c_w2(gspca_dev, mt9m001_init[i].reg, mt9m001_init[i].val) < 0) { - err("MT9M001 sensor initialization failed"); + pr_err("MT9M001 sensor initialization failed\n"); return -ENODEV; } } @@ -1517,7 +1519,7 @@ static int hv7131r_init_sensor(struct gspca_dev *gspca_dev) for (i = 0; i < ARRAY_SIZE(hv7131r_init); i++) { if (i2c_w1(gspca_dev, hv7131r_init[i].reg, hv7131r_init[i].val) < 0) { - err("HV7131R Sensor initialization failed"); + pr_err("HV7131R Sensor initialization failed\n"); return -ENODEV; } } @@ -2103,7 +2105,7 @@ static int sd_init(struct gspca_dev *gspca_dev) for (i = 0; i < ARRAY_SIZE(bridge_init); i++) { value = bridge_init[i][1]; if (reg_w(gspca_dev, bridge_init[i][0], &value, 1) < 0) { - err("Device initialization failed"); + pr_err("Device initialization failed\n"); return -ENODEV; } } @@ -2114,7 +2116,7 @@ static int sd_init(struct gspca_dev *gspca_dev) reg_w1(gspca_dev, 0x1006, 0x20); if (reg_w(gspca_dev, 0x10c0, i2c_init, 9) < 0) { - err("Device initialization failed"); + pr_err("Device initialization failed\n"); return -ENODEV; } @@ -2122,27 +2124,27 @@ static int sd_init(struct gspca_dev *gspca_dev) case SENSOR_OV9650: if (ov9650_init_sensor(gspca_dev) < 0) return -ENODEV; - info("OV9650 sensor detected"); + pr_info("OV9650 sensor detected\n"); break; case SENSOR_OV9655: if (ov9655_init_sensor(gspca_dev) < 0) return -ENODEV; - info("OV9655 sensor detected"); + pr_info("OV9655 sensor detected\n"); break; case SENSOR_SOI968: if (soi968_init_sensor(gspca_dev) < 0) return -ENODEV; - info("SOI968 sensor detected"); + pr_info("SOI968 sensor detected\n"); break; case SENSOR_OV7660: if (ov7660_init_sensor(gspca_dev) < 0) return -ENODEV; - info("OV7660 sensor detected"); + pr_info("OV7660 sensor detected\n"); break; case SENSOR_OV7670: if (ov7670_init_sensor(gspca_dev) < 0) return -ENODEV; - info("OV7670 sensor detected"); + pr_info("OV7670 sensor detected\n"); break; case SENSOR_MT9VPRB: if (mt9v_init_sensor(gspca_dev) < 0) @@ -2151,12 +2153,12 @@ static int sd_init(struct gspca_dev *gspca_dev) case SENSOR_MT9M111: if (mt9m111_init_sensor(gspca_dev) < 0) return -ENODEV; - info("MT9M111 sensor detected"); + pr_info("MT9M111 sensor detected\n"); break; case SENSOR_MT9M112: if (mt9m112_init_sensor(gspca_dev) < 0) return -ENODEV; - info("MT9M112 sensor detected"); + pr_info("MT9M112 sensor detected\n"); break; case SENSOR_MT9M001: if (mt9m001_init_sensor(gspca_dev) < 0) @@ -2165,10 +2167,10 @@ static int sd_init(struct gspca_dev *gspca_dev) case SENSOR_HV7131R: if (hv7131r_init_sensor(gspca_dev) < 0) return -ENODEV; - info("HV7131R sensor detected"); + pr_info("HV7131R sensor detected\n"); break; default: - info("Unsupported Sensor"); + pr_info("Unsupported Sensor\n"); return -ENODEV; } @@ -2263,19 +2265,19 @@ static int sd_start(struct gspca_dev *gspca_dev) switch (mode & SCALE_MASK) { case SCALE_1280x1024: scale = 0xc0; - info("Set 1280x1024"); + pr_info("Set 1280x1024\n"); break; case SCALE_640x480: scale = 0x80; - info("Set 640x480"); + pr_info("Set 640x480\n"); break; case SCALE_320x240: scale = 0x90; - info("Set 320x240"); + pr_info("Set 320x240\n"); break; case SCALE_160x120: scale = 0xa0; - info("Set 160x120"); + pr_info("Set 160x120\n"); break; } @@ -2513,7 +2515,7 @@ static const struct usb_device_id device_table[] = { {USB_DEVICE(0x0c45, 0x628f), SN9C20X(OV9650, 0x30, 0)}, {USB_DEVICE(0x0c45, 0x62a0), SN9C20X(OV7670, 0x21, 0)}, {USB_DEVICE(0x0c45, 0x62b0), SN9C20X(MT9VPRB, 0x00, 0)}, - {USB_DEVICE(0x0c45, 0x62b3), SN9C20X(OV9655, 0x30, 0)}, + {USB_DEVICE(0x0c45, 0x62b3), SN9C20X(OV9655, 0x30, LED_REVERSE)}, {USB_DEVICE(0x0c45, 0x62bb), SN9C20X(OV7660, 0x21, LED_REVERSE)}, {USB_DEVICE(0x0c45, 0x62bc), SN9C20X(HV7131R, 0x11, 0)}, {USB_DEVICE(0x045e, 0x00f4), SN9C20X(OV9650, 0x30, 0)}, diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c index 81b8a600783..c746bf19ca1 100644 --- a/drivers/media/video/gspca/sonixj.c +++ b/drivers/media/video/gspca/sonixj.c @@ -1,7 +1,7 @@ /* * Sonix sn9c102p sn9c105 sn9c120 (jpeg) subdriver * - * Copyright (C) 2009-2010 Jean-François Moine <http://moinejf.free.fr> + * Copyright (C) 2009-2011 Jean-François Moine <http://moinejf.free.fr> * Copyright (C) 2005 Michel Xhaard mxhaard@magic.fr * * This program is free software; you can redistribute it and/or modify @@ -19,6 +19,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #define MODULE_NAME "sonixj" #include <linux/input.h> @@ -136,7 +138,7 @@ static void setillum(struct gspca_dev *gspca_dev); static void setfreq(struct gspca_dev *gspca_dev); static const struct ctrl sd_ctrls[NCTRLS] = { -[BRIGHTNESS] = { +[BRIGHTNESS] = { { .id = V4L2_CID_BRIGHTNESS, .type = V4L2_CTRL_TYPE_INTEGER, @@ -157,7 +159,7 @@ static const struct ctrl sd_ctrls[NCTRLS] = { #define CONTRAST_MAX 127 .maximum = CONTRAST_MAX, .step = 1, - .default_value = 63, + .default_value = 20, }, .set_control = setcontrast }, @@ -737,7 +739,7 @@ static const u8 mi0360_sensor_init[][8] = { {0xd1, 0x5d, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xd1, 0x5d, 0x24, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xd1, 0x5d, 0x26, 0x00, 0x00, 0x00, 0x24, 0x10}, - {0xd1, 0x5d, 0x2f, 0xf7, 0xB0, 0x00, 0x04, 0x10}, + {0xd1, 0x5d, 0x2f, 0xf7, 0xb0, 0x00, 0x04, 0x10}, {0xd1, 0x5d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xd1, 0x5d, 0x33, 0x00, 0x00, 0x01, 0x00, 0x10}, {0xb1, 0x5d, 0x3d, 0x06, 0x8f, 0x00, 0x00, 0x10}, @@ -1277,7 +1279,7 @@ static const u8 soi768_sensor_param1[][8] = { /* global gain ? : 07 - change with 0x15 at the end */ {0xa1, 0x21, 0x10, 0x3f, 0x00, 0x00, 0x00, 0x10}, /* ???? : 063f */ {0xa1, 0x21, 0x04, 0x06, 0x00, 0x00, 0x00, 0x10}, - {0xb1, 0x21, 0x2d, 0x00, 0x02, 0x00, 0x00, 0x10}, + {0xb1, 0x21, 0x2d, 0x63, 0x03, 0x00, 0x00, 0x10}, /* exposure ? : 0200 - change with 0x1e at the end */ {} }; @@ -1395,7 +1397,7 @@ static void reg_r(struct gspca_dev *gspca_dev, return; #ifdef GSPCA_DEBUG if (len > USB_BUF_SZ) { - err("reg_r: buffer overflow"); + pr_err("reg_r: buffer overflow\n"); return; } #endif @@ -1408,7 +1410,7 @@ static void reg_r(struct gspca_dev *gspca_dev, 500); PDEBUG(D_USBI, "reg_r [%02x] -> %02x", value, gspca_dev->usb_buf[0]); if (ret < 0) { - err("reg_r err %d", ret); + pr_err("reg_r err %d\n", ret); gspca_dev->usb_err = ret; } } @@ -1432,7 +1434,7 @@ static void reg_w1(struct gspca_dev *gspca_dev, gspca_dev->usb_buf, 1, 500); if (ret < 0) { - err("reg_w1 err %d", ret); + pr_err("reg_w1 err %d\n", ret); gspca_dev->usb_err = ret; } } @@ -1449,7 +1451,7 @@ static void reg_w(struct gspca_dev *gspca_dev, value, buffer[0], buffer[1]); #ifdef GSPCA_DEBUG if (len > USB_BUF_SZ) { - err("reg_w: buffer overflow"); + pr_err("reg_w: buffer overflow\n"); return; } #endif @@ -1462,7 +1464,7 @@ static void reg_w(struct gspca_dev *gspca_dev, gspca_dev->usb_buf, len, 500); if (ret < 0) { - err("reg_w err %d", ret); + pr_err("reg_w err %d\n", ret); gspca_dev->usb_err = ret; } } @@ -1502,7 +1504,7 @@ static void i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val) gspca_dev->usb_buf, 8, 500); if (ret < 0) { - err("i2c_w1 err %d", ret); + pr_err("i2c_w1 err %d\n", ret); gspca_dev->usb_err = ret; } } @@ -1527,7 +1529,7 @@ static void i2c_w8(struct gspca_dev *gspca_dev, 500); msleep(2); if (ret < 0) { - err("i2c_w8 err %d", ret); + pr_err("i2c_w8 err %d\n", ret); gspca_dev->usb_err = ret; } } @@ -1591,7 +1593,7 @@ static void hv7131r_probe(struct gspca_dev *gspca_dev) PDEBUG(D_PROBE, "Sensor HV7131R found"); return; } - warn("Erroneous HV7131R ID 0x%02x 0x%02x 0x%02x", + pr_warn("Erroneous HV7131R ID 0x%02x 0x%02x 0x%02x\n", gspca_dev->usb_buf[0], gspca_dev->usb_buf[1], gspca_dev->usb_buf[2]); } @@ -1710,7 +1712,7 @@ static void ov7648_probe(struct gspca_dev *gspca_dev) sd->sensor = SENSOR_PO1030; return; } - err("Unknown sensor %04x", val); + pr_err("Unknown sensor %04x\n", val); } /* 0c45:6142 sensor may be po2030n, gc0305 or gc0307 */ @@ -1748,7 +1750,7 @@ static void po2030n_probe(struct gspca_dev *gspca_dev) PDEBUG(D_PROBE, "Sensor po2030n"); /* sd->sensor = SENSOR_PO2030N; */ } else { - err("Unknown sensor ID %04x", val); + pr_err("Unknown sensor ID %04x\n", val); } } @@ -2006,8 +2008,7 @@ static void setbrightness(struct gspca_dev *gspca_dev) case SENSOR_OM6802: expo = brightness << 2; sd->exposure = setexposure(gspca_dev, expo); - k2 = brightness >> 3; - break; + return; /* Y offset already set */ } reg_w1(gspca_dev, 0x96, k2); /* color matrix Y offset */ @@ -2019,13 +2020,13 @@ static void setcontrast(struct gspca_dev *gspca_dev) u8 k2; u8 contrast[6]; - k2 = sd->ctrls[CONTRAST].val * 0x30 / (CONTRAST_MAX + 1) - + 0x10; /* 10..40 */ + k2 = sd->ctrls[CONTRAST].val * 37 / (CONTRAST_MAX + 1) + + 37; /* 37..73 */ contrast[0] = (k2 + 1) / 2; /* red */ contrast[1] = 0; contrast[2] = k2; /* green */ contrast[3] = 0; - contrast[4] = (k2 + 1) / 5; /* blue */ + contrast[4] = k2 / 5; /* blue */ contrast[5] = 0; reg_w(gspca_dev, 0x84, contrast, sizeof contrast); } @@ -2386,7 +2387,7 @@ static int sd_start(struct gspca_dev *gspca_dev) reg_w1(gspca_dev, 0x01, 0x22); msleep(100); reg01 = SCL_SEL_OD | S_PDN_INV; - reg17 &= MCK_SIZE_MASK; + reg17 &= ~MCK_SIZE_MASK; reg17 |= 0x04; /* clock / 4 */ break; } @@ -2507,9 +2508,7 @@ static int sd_start(struct gspca_dev *gspca_dev) break; case SENSOR_HV7131R: case SENSOR_MI0360: - if (mode) - reg01 |= SYS_SEL_48M; /* 320x240: clk 48Mhz */ - else + if (!mode) reg01 &= ~SYS_SEL_48M; /* 640x480: clk 24Mhz */ reg17 &= ~MCK_SIZE_MASK; reg17 |= 0x01; /* clock / 1 */ @@ -2532,6 +2531,10 @@ static int sd_start(struct gspca_dev *gspca_dev) if (!mode) { /* if 640x480 */ reg17 &= ~MCK_SIZE_MASK; reg17 |= 0x04; /* clock / 4 */ + } else { + reg01 &= ~SYS_SEL_48M; /* clk 24Mz */ + reg17 &= ~MCK_SIZE_MASK; + reg17 |= 0x02; /* clock / 2 */ } break; case SENSOR_OV7630: diff --git a/drivers/media/video/gspca/spca1528.c b/drivers/media/video/gspca/spca1528.c index 76c006b2bc8..695673106e7 100644 --- a/drivers/media/video/gspca/spca1528.c +++ b/drivers/media/video/gspca/spca1528.c @@ -1,7 +1,7 @@ /* * spca1528 subdriver * - * Copyright (C) 2010 Jean-Francois Moine (http://moinejf.free.fr) + * Copyright (C) 2010-2011 Jean-Francois Moine (http://moinejf.free.fr) * * 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 @@ -18,6 +18,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #define MODULE_NAME "spca1528" #include "gspca.h" @@ -171,7 +173,7 @@ static void reg_r(struct gspca_dev *gspca_dev, PDEBUG(D_USBI, "GET %02x 0000 %04x %02x", req, index, gspca_dev->usb_buf[0]); if (ret < 0) { - err("reg_r err %d", ret); + pr_err("reg_r err %d\n", ret); gspca_dev->usb_err = ret; } } @@ -193,7 +195,7 @@ static void reg_w(struct gspca_dev *gspca_dev, value, index, NULL, 0, 500); if (ret < 0) { - err("reg_w err %d", ret); + pr_err("reg_w err %d\n", ret); gspca_dev->usb_err = ret; } } @@ -217,21 +219,23 @@ static void reg_wb(struct gspca_dev *gspca_dev, value, index, gspca_dev->usb_buf, 1, 500); if (ret < 0) { - err("reg_w err %d", ret); + pr_err("reg_w err %d\n", ret); gspca_dev->usb_err = ret; } } static void wait_status_0(struct gspca_dev *gspca_dev) { - int i; + int i, w; - i = 20; + i = 16; + w = 0; do { reg_r(gspca_dev, 0x21, 0x0000, 1); if (gspca_dev->usb_buf[0] == 0) return; - msleep(30); + w += 15; + msleep(w); } while (--i > 0); PDEBUG(D_ERR, "wait_status_0 timeout"); gspca_dev->usb_err = -ETIME; @@ -307,8 +311,6 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->color = COLOR_DEF; sd->sharpness = SHARPNESS_DEF; - gspca_dev->nbalt = 4; /* use alternate setting 3 */ - return 0; } @@ -347,8 +349,12 @@ static int sd_isoc_init(struct gspca_dev *gspca_dev) mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; reg_wb(gspca_dev, 0x25, 0x0000, 0x0004, mode); reg_r(gspca_dev, 0x25, 0x0004, 1); - reg_wb(gspca_dev, 0x27, 0x0000, 0x0000, 0x06); + reg_wb(gspca_dev, 0x27, 0x0000, 0x0000, 0x06); /* 420 */ reg_r(gspca_dev, 0x27, 0x0000, 1); + +/* not useful.. + gspca_dev->alt = 4; * use alternate setting 3 */ + return gspca_dev->usb_err; } @@ -361,8 +367,8 @@ static int sd_start(struct gspca_dev *gspca_dev) jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, 0x22); /* JPEG 411 */ - /* the JPEG quality seems to be 82% */ - jpeg_set_qual(sd->jpeg_hdr, 82); + /* the JPEG quality shall be 85% */ + jpeg_set_qual(sd->jpeg_hdr, 85); /* set the controls */ setbrightness(gspca_dev); @@ -377,7 +383,7 @@ static int sd_start(struct gspca_dev *gspca_dev) /* start the capture */ wait_status_0(gspca_dev); - reg_w(gspca_dev, 0x31, 0x0000, 0x0004); + reg_w(gspca_dev, 0x31, 0x0000, 0x0004); /* start request */ wait_status_1(gspca_dev); wait_status_0(gspca_dev); msleep(200); @@ -390,7 +396,7 @@ static void sd_stopN(struct gspca_dev *gspca_dev) { /* stop the capture */ wait_status_0(gspca_dev); - reg_w(gspca_dev, 0x31, 0x0000, 0x0000); + reg_w(gspca_dev, 0x31, 0x0000, 0x0000); /* stop request */ wait_status_1(gspca_dev); wait_status_0(gspca_dev); } diff --git a/drivers/media/video/gspca/spca500.c b/drivers/media/video/gspca/spca500.c index 3e76951e3c1..bb82c94ece1 100644 --- a/drivers/media/video/gspca/spca500.c +++ b/drivers/media/video/gspca/spca500.c @@ -19,6 +19,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #define MODULE_NAME "spca500" #include "gspca.h" @@ -396,7 +398,7 @@ static int reg_w(struct gspca_dev *gspca_dev, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, value, index, NULL, 0, 500); if (ret < 0) - err("reg write: error %d", ret); + pr_err("reg write: error %d\n", ret); return ret; } @@ -418,7 +420,7 @@ static int reg_r_12(struct gspca_dev *gspca_dev, gspca_dev->usb_buf, length, 500); /* timeout */ if (ret < 0) { - err("reg_r_12 err %d", ret); + pr_err("reg_r_12 err %d\n", ret); return ret; } return (gspca_dev->usb_buf[1] << 8) + gspca_dev->usb_buf[0]; diff --git a/drivers/media/video/gspca/spca501.c b/drivers/media/video/gspca/spca501.c index f7ef282cc60..7aaac72aee9 100644 --- a/drivers/media/video/gspca/spca501.c +++ b/drivers/media/video/gspca/spca501.c @@ -19,6 +19,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #define MODULE_NAME "spca501" #include "gspca.h" @@ -1852,7 +1854,7 @@ static int reg_write(struct usb_device *dev, PDEBUG(D_USBO, "reg write: 0x%02x 0x%02x 0x%02x", req, index, value); if (ret < 0) - err("reg write: error %d", ret); + pr_err("reg write: error %d\n", ret); return ret; } diff --git a/drivers/media/video/gspca/spca505.c b/drivers/media/video/gspca/spca505.c index e5bf865147d..16722dc6039 100644 --- a/drivers/media/video/gspca/spca505.c +++ b/drivers/media/video/gspca/spca505.c @@ -19,6 +19,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #define MODULE_NAME "spca505" #include "gspca.h" @@ -578,7 +580,7 @@ static int reg_write(struct usb_device *dev, PDEBUG(D_USBO, "reg write: 0x%02x,0x%02x:0x%02x, %d", req, index, value, ret); if (ret < 0) - err("reg write: error %d", ret); + pr_err("reg write: error %d\n", ret); return ret; } @@ -685,8 +687,8 @@ static int sd_start(struct gspca_dev *gspca_dev) return ret; } if (ret != 0x0101) { - err("After vector read returns 0x%04x should be 0x0101", - ret); + pr_err("After vector read returns 0x%04x should be 0x0101\n", + ret); } ret = reg_write(gspca_dev->dev, 0x06, 0x16, 0x0a); diff --git a/drivers/media/video/gspca/spca508.c b/drivers/media/video/gspca/spca508.c index 9d0b46027b9..a44fe3d2596 100644 --- a/drivers/media/video/gspca/spca508.c +++ b/drivers/media/video/gspca/spca508.c @@ -18,6 +18,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #define MODULE_NAME "spca508" #include "gspca.h" @@ -1275,7 +1277,7 @@ static int reg_write(struct usb_device *dev, PDEBUG(D_USBO, "reg write i:0x%04x = 0x%02x", index, value); if (ret < 0) - err("reg write: error %d", ret); + pr_err("reg write: error %d\n", ret); return ret; } @@ -1297,7 +1299,7 @@ static int reg_read(struct gspca_dev *gspca_dev, PDEBUG(D_USBI, "reg read i:%04x --> %02x", index, gspca_dev->usb_buf[0]); if (ret < 0) { - err("reg_read err %d", ret); + pr_err("reg_read err %d\n", ret); return ret; } return gspca_dev->usb_buf[0]; diff --git a/drivers/media/video/gspca/spca561.c b/drivers/media/video/gspca/spca561.c index e836e778dfb..c82fd53cef9 100644 --- a/drivers/media/video/gspca/spca561.c +++ b/drivers/media/video/gspca/spca561.c @@ -20,6 +20,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #define MODULE_NAME "spca561" #include <linux/input.h> @@ -315,7 +317,7 @@ static void reg_w_val(struct usb_device *dev, __u16 index, __u8 value) value, index, NULL, 0, 500); PDEBUG(D_USBO, "reg write: 0x%02x:0x%02x", index, value); if (ret < 0) - err("reg write: error %d", ret); + pr_err("reg write: error %d\n", ret); } static void write_vector(struct gspca_dev *gspca_dev, diff --git a/drivers/media/video/gspca/sq905.c b/drivers/media/video/gspca/sq905.c index 5ba96aff225..df805f79828 100644 --- a/drivers/media/video/gspca/sq905.c +++ b/drivers/media/video/gspca/sq905.c @@ -33,6 +33,8 @@ * drivers. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #define MODULE_NAME "sq905" #include <linux/workqueue.h> @@ -123,8 +125,7 @@ static int sq905_command(struct gspca_dev *gspca_dev, u16 index) SQ905_COMMAND, index, gspca_dev->usb_buf, 1, SQ905_CMD_TIMEOUT); if (ret < 0) { - err("%s: usb_control_msg failed (%d)", - __func__, ret); + pr_err("%s: usb_control_msg failed (%d)\n", __func__, ret); return ret; } @@ -135,8 +136,7 @@ static int sq905_command(struct gspca_dev *gspca_dev, u16 index) SQ905_PING, 0, gspca_dev->usb_buf, 1, SQ905_CMD_TIMEOUT); if (ret < 0) { - err("%s: usb_control_msg failed 2 (%d)", - __func__, ret); + pr_err("%s: usb_control_msg failed 2 (%d)\n", __func__, ret); return ret; } @@ -158,7 +158,7 @@ static int sq905_ack_frame(struct gspca_dev *gspca_dev) SQ905_READ_DONE, 0, gspca_dev->usb_buf, 1, SQ905_CMD_TIMEOUT); if (ret < 0) { - err("%s: usb_control_msg failed (%d)", __func__, ret); + pr_err("%s: usb_control_msg failed (%d)\n", __func__, ret); return ret; } @@ -186,7 +186,7 @@ sq905_read_data(struct gspca_dev *gspca_dev, u8 *data, int size, int need_lock) if (need_lock) mutex_unlock(&gspca_dev->usb_lock); if (ret < 0) { - err("%s: usb_control_msg failed (%d)", __func__, ret); + pr_err("%s: usb_control_msg failed (%d)\n", __func__, ret); return ret; } ret = usb_bulk_msg(gspca_dev->dev, @@ -195,8 +195,7 @@ sq905_read_data(struct gspca_dev *gspca_dev, u8 *data, int size, int need_lock) /* successful, it returns 0, otherwise negative */ if (ret < 0 || act_len != size) { - err("bulk read fail (%d) len %d/%d", - ret, act_len, size); + pr_err("bulk read fail (%d) len %d/%d\n", ret, act_len, size); return -EIO; } return 0; @@ -226,7 +225,7 @@ static void sq905_dostream(struct work_struct *work) buffer = kmalloc(SQ905_MAX_TRANSFER, GFP_KERNEL | GFP_DMA); if (!buffer) { - err("Couldn't allocate USB buffer"); + pr_err("Couldn't allocate USB buffer\n"); goto quit_stream; } diff --git a/drivers/media/video/gspca/sq905c.c b/drivers/media/video/gspca/sq905c.c index 457563b7a71..c2c056056e0 100644 --- a/drivers/media/video/gspca/sq905c.c +++ b/drivers/media/video/gspca/sq905c.c @@ -27,6 +27,8 @@ * and may contain code fragments from it. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #define MODULE_NAME "sq905c" #include <linux/workqueue.h> @@ -95,8 +97,7 @@ static int sq905c_command(struct gspca_dev *gspca_dev, u16 command, u16 index) command, index, NULL, 0, SQ905C_CMD_TIMEOUT); if (ret < 0) { - err("%s: usb_control_msg failed (%d)", - __func__, ret); + pr_err("%s: usb_control_msg failed (%d)\n", __func__, ret); return ret; } @@ -115,8 +116,7 @@ static int sq905c_read(struct gspca_dev *gspca_dev, u16 command, u16 index, command, index, gspca_dev->usb_buf, size, SQ905C_CMD_TIMEOUT); if (ret < 0) { - err("%s: usb_control_msg failed (%d)", - __func__, ret); + pr_err("%s: usb_control_msg failed (%d)\n", __func__, ret); return ret; } @@ -146,7 +146,7 @@ static void sq905c_dostream(struct work_struct *work) buffer = kmalloc(SQ905C_MAX_TRANSFER, GFP_KERNEL | GFP_DMA); if (!buffer) { - err("Couldn't allocate USB buffer"); + pr_err("Couldn't allocate USB buffer\n"); goto quit_stream; } diff --git a/drivers/media/video/gspca/sq930x.c b/drivers/media/video/gspca/sq930x.c index 8215d5dcd45..e4255b4905e 100644 --- a/drivers/media/video/gspca/sq930x.c +++ b/drivers/media/video/gspca/sq930x.c @@ -20,6 +20,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #define MODULE_NAME "sq930x" #include "gspca.h" @@ -468,7 +470,7 @@ static void reg_r(struct gspca_dev *gspca_dev, value, 0, gspca_dev->usb_buf, len, 500); if (ret < 0) { - err("reg_r %04x failed %d", value, ret); + pr_err("reg_r %04x failed %d\n", value, ret); gspca_dev->usb_err = ret; } } @@ -488,7 +490,7 @@ static void reg_w(struct gspca_dev *gspca_dev, u16 value, u16 index) 500); msleep(30); if (ret < 0) { - err("reg_w %04x %04x failed %d", value, index, ret); + pr_err("reg_w %04x %04x failed %d\n", value, index, ret); gspca_dev->usb_err = ret; } } @@ -511,7 +513,7 @@ static void reg_wb(struct gspca_dev *gspca_dev, u16 value, u16 index, 1000); msleep(30); if (ret < 0) { - err("reg_wb %04x %04x failed %d", value, index, ret); + pr_err("reg_wb %04x %04x failed %d\n", value, index, ret); gspca_dev->usb_err = ret; } } @@ -556,7 +558,7 @@ static void i2c_write(struct sd *sd, gspca_dev->usb_buf, buf - gspca_dev->usb_buf, 500); if (ret < 0) { - err("i2c_write failed %d", ret); + pr_err("i2c_write failed %d\n", ret); gspca_dev->usb_err = ret; } } @@ -575,7 +577,7 @@ static void ucbus_write(struct gspca_dev *gspca_dev, #ifdef GSPCA_DEBUG if ((batchsize - 1) * 3 > USB_BUF_SZ) { - err("Bug: usb_buf overflow"); + pr_err("Bug: usb_buf overflow\n"); gspca_dev->usb_err = -ENOMEM; return; } @@ -612,7 +614,7 @@ static void ucbus_write(struct gspca_dev *gspca_dev, gspca_dev->usb_buf, buf - gspca_dev->usb_buf, 500); if (ret < 0) { - err("ucbus_write failed %d", ret); + pr_err("ucbus_write failed %d\n", ret); gspca_dev->usb_err = ret; return; } @@ -688,7 +690,7 @@ static void cmos_probe(struct gspca_dev *gspca_dev) break; } if (i >= ARRAY_SIZE(probe_order)) { - err("Unknown sensor"); + pr_err("Unknown sensor\n"); gspca_dev->usb_err = -EINVAL; return; } @@ -696,7 +698,8 @@ static void cmos_probe(struct gspca_dev *gspca_dev) switch (sd->sensor) { case SENSOR_OV7660: case SENSOR_OV9630: - err("Sensor %s not yet treated", sensor_tb[sd->sensor].name); + pr_err("Sensor %s not yet treated\n", + sensor_tb[sd->sensor].name); gspca_dev->usb_err = -EINVAL; break; } @@ -1091,7 +1094,7 @@ static void sd_dq_callback(struct gspca_dev *gspca_dev) gspca_dev->cam.bulk_nurbs = 1; ret = usb_submit_urb(gspca_dev->urb[0], GFP_ATOMIC); if (ret < 0) - err("sd_dq_callback() err %d", ret); + pr_err("sd_dq_callback() err %d\n", ret); /* wait a little time, otherwise the webcam crashes */ msleep(100); diff --git a/drivers/media/video/gspca/stk014.c b/drivers/media/video/gspca/stk014.c index 763747700f1..42a7a28a6c8 100644 --- a/drivers/media/video/gspca/stk014.c +++ b/drivers/media/video/gspca/stk014.c @@ -18,6 +18,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #define MODULE_NAME "stk014" #include "gspca.h" @@ -137,7 +139,7 @@ static u8 reg_r(struct gspca_dev *gspca_dev, gspca_dev->usb_buf, 1, 500); if (ret < 0) { - err("reg_r err %d", ret); + pr_err("reg_r err %d\n", ret); gspca_dev->usb_err = ret; return 0; } @@ -162,7 +164,7 @@ static void reg_w(struct gspca_dev *gspca_dev, 0, 500); if (ret < 0) { - err("reg_w err %d", ret); + pr_err("reg_w err %d\n", ret); gspca_dev->usb_err = ret; } } @@ -192,7 +194,7 @@ static void rcv_val(struct gspca_dev *gspca_dev, &alen, 500); /* timeout in milliseconds */ if (ret < 0) { - err("rcv_val err %d", ret); + pr_err("rcv_val err %d\n", ret); gspca_dev->usb_err = ret; } } @@ -235,7 +237,7 @@ static void snd_val(struct gspca_dev *gspca_dev, &alen, 500); /* timeout in milliseconds */ if (ret < 0) { - err("snd_val err %d", ret); + pr_err("snd_val err %d\n", ret); gspca_dev->usb_err = ret; } else { if (ads == 0x003f08) { @@ -315,7 +317,7 @@ static int sd_init(struct gspca_dev *gspca_dev) ret = reg_r(gspca_dev, 0x0740); if (gspca_dev->usb_err >= 0) { if (ret != 0xff) { - err("init reg: 0x%02x", ret); + pr_err("init reg: 0x%02x\n", ret); gspca_dev->usb_err = -EIO; } } @@ -349,8 +351,8 @@ static int sd_start(struct gspca_dev *gspca_dev) gspca_dev->iface, gspca_dev->alt); if (ret < 0) { - err("set intf %d %d failed", - gspca_dev->iface, gspca_dev->alt); + pr_err("set intf %d %d failed\n", + gspca_dev->iface, gspca_dev->alt); gspca_dev->usb_err = ret; goto out; } diff --git a/drivers/media/video/gspca/stv0680.c b/drivers/media/video/gspca/stv0680.c index e2ef41cf72d..4dcc7e37f9f 100644 --- a/drivers/media/video/gspca/stv0680.c +++ b/drivers/media/video/gspca/stv0680.c @@ -27,6 +27,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #define MODULE_NAME "stv0680" #include "gspca.h" @@ -79,7 +81,7 @@ static int stv_sndctrl(struct gspca_dev *gspca_dev, int set, u8 req, u16 val, val, 0, gspca_dev->usb_buf, size, 500); if ((ret < 0) && (req != 0x0a)) - err("usb_control_msg error %i, request = 0x%x, error = %i", + pr_err("usb_control_msg error %i, request = 0x%x, error = %i\n", set, req, ret); return ret; @@ -236,7 +238,7 @@ static int sd_config(struct gspca_dev *gspca_dev, if (stv_sndctrl(gspca_dev, 2, 0x06, 0x0100, 0x12) != 0x12 || gspca_dev->usb_buf[8] != 0x53 || gspca_dev->usb_buf[9] != 0x05) { - err("Could not get descriptor 0100."); + pr_err("Could not get descriptor 0100\n"); return stv0680_handle_error(gspca_dev, -EIO); } diff --git a/drivers/media/video/gspca/stv06xx/Makefile b/drivers/media/video/gspca/stv06xx/Makefile index 2f3c3a606ce..5b318faf9aa 100644 --- a/drivers/media/video/gspca/stv06xx/Makefile +++ b/drivers/media/video/gspca/stv06xx/Makefile @@ -6,5 +6,5 @@ gspca_stv06xx-objs := stv06xx.o \ stv06xx_pb0100.o \ stv06xx_st6422.o -EXTRA_CFLAGS += -Idrivers/media/video/gspca +ccflags-y += -Idrivers/media/video/gspca diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.c b/drivers/media/video/gspca/stv06xx/stv06xx.c index abf1658fa33..b1fca7db101 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx.c +++ b/drivers/media/video/gspca/stv06xx/stv06xx.c @@ -27,6 +27,8 @@ * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/input.h> #include "stv06xx_sensor.h" @@ -189,7 +191,7 @@ int stv06xx_read_sensor(struct sd *sd, const u8 address, u16 *value) 0x04, 0x40, 0x1400, 0, buf, I2C_BUFFER_LENGTH, STV06XX_URB_MSG_TIMEOUT); if (err < 0) { - err("I2C: Read error writing address: %d", err); + pr_err("I2C: Read error writing address: %d\n", err); return err; } @@ -213,14 +215,14 @@ static void stv06xx_dump_bridge(struct sd *sd) int i; u8 data, buf; - info("Dumping all stv06xx bridge registers"); + pr_info("Dumping all stv06xx bridge registers\n"); for (i = 0x1400; i < 0x160f; i++) { stv06xx_read_bridge(sd, i, &data); - info("Read 0x%x from address 0x%x", data, i); + pr_info("Read 0x%x from address 0x%x\n", data, i); } - info("Testing stv06xx bridge registers for writability"); + pr_info("Testing stv06xx bridge registers for writability\n"); for (i = 0x1400; i < 0x160f; i++) { stv06xx_read_bridge(sd, i, &data); buf = data; @@ -228,12 +230,12 @@ static void stv06xx_dump_bridge(struct sd *sd) stv06xx_write_bridge(sd, i, 0xff); stv06xx_read_bridge(sd, i, &data); if (data == 0xff) - info("Register 0x%x is read/write", i); + pr_info("Register 0x%x is read/write\n", i); else if (data != buf) - info("Register 0x%x is read/write," - " but only partially", i); + pr_info("Register 0x%x is read/write, but only partially\n", + i); else - info("Register 0x%x is read-only", i); + pr_info("Register 0x%x is read-only\n", i); stv06xx_write_bridge(sd, i, buf); } diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.h b/drivers/media/video/gspca/stv06xx/stv06xx.h index e0f63c51f40..d270a5981af 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx.h +++ b/drivers/media/video/gspca/stv06xx/stv06xx.h @@ -37,6 +37,8 @@ #define STV_ISOC_ENDPOINT_ADDR 0x81 +#define STV_R 0x0509 + #define STV_REG23 0x0423 /* Control registers of the STV0600 ASIC */ @@ -61,7 +63,9 @@ /* Refers to the CIF 352x288 and QCIF 176x144 */ /* 1: 288 lines, 2: 144 lines */ -#define STV_Y_CTRL 0x15c3 +#define STV_Y_CTRL 0x15c3 + +#define STV_RESET 0x1620 /* 0xa: 352 columns, 0x6: 176 columns */ #define STV_X_CTRL 0x1680 diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c index b8156855f2b..a8698b7a756 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c +++ b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c @@ -28,6 +28,8 @@ * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include "stv06xx_hdcs.h" static const struct ctrl hdcs1x00_ctrl[] = { @@ -428,7 +430,7 @@ static int hdcs_probe_1x00(struct sd *sd) if (ret < 0 || sensor != 0x08) return -ENODEV; - info("HDCS-1000/1100 sensor detected"); + pr_info("HDCS-1000/1100 sensor detected\n"); sd->gspca_dev.cam.cam_mode = hdcs1x00_mode; sd->gspca_dev.cam.nmodes = ARRAY_SIZE(hdcs1x00_mode); @@ -487,7 +489,7 @@ static int hdcs_probe_1020(struct sd *sd) if (ret < 0 || sensor != 0x10) return -ENODEV; - info("HDCS-1020 sensor detected"); + pr_info("HDCS-1020 sensor detected\n"); sd->gspca_dev.cam.cam_mode = hdcs1020_mode; sd->gspca_dev.cam.nmodes = ARRAY_SIZE(hdcs1020_mode); @@ -601,11 +603,11 @@ static int hdcs_dump(struct sd *sd) { u16 reg, val; - info("Dumping sensor registers:"); + pr_info("Dumping sensor registers:\n"); for (reg = HDCS_IDENT; reg <= HDCS_ROWEXPH; reg++) { stv06xx_read_sensor(sd, reg, &val); - info("reg 0x%02x = 0x%02x", reg, val); + pr_info("reg 0x%02x = 0x%02x\n", reg, val); } return 0; } diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c b/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c index 75a5b9c2f15..26f14fc4a13 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c +++ b/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c @@ -44,6 +44,8 @@ * PB_CFILLIN = R5 = 0x0E (14 dec) : Sets the frame rate */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include "stv06xx_pb0100.h" static const struct ctrl pb0100_ctrl[] = { @@ -190,7 +192,7 @@ static int pb0100_probe(struct sd *sd) if (!sensor_settings) return -ENOMEM; - info("Photobit pb0100 sensor detected"); + pr_info("Photobit pb0100 sensor detected\n"); sd->gspca_dev.cam.cam_mode = pb0100_mode; sd->gspca_dev.cam.nmodes = ARRAY_SIZE(pb0100_mode); diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c b/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c index 8a456de4970..9940e035b3a 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c +++ b/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c @@ -26,6 +26,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include "stv06xx_st6422.h" /* controls */ @@ -136,7 +138,7 @@ static int st6422_probe(struct sd *sd) if (sd->bridge != BRIDGE_ST6422) return -ENODEV; - info("st6422 sensor detected"); + pr_info("st6422 sensor detected\n"); sensor_settings = kmalloc(sizeof *sensor_settings, GFP_KERNEL); if (!sensor_settings) diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c index f8398434c32..a5c69d9ebdd 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c +++ b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c @@ -27,6 +27,8 @@ * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include "stv06xx_vv6410.h" static struct v4l2_pix_format vv6410_mode[] = { @@ -112,7 +114,7 @@ static int vv6410_probe(struct sd *sd) return -ENODEV; if (data == 0x19) { - info("vv6410 sensor detected"); + pr_info("vv6410 sensor detected\n"); sensor_settings = kmalloc(ARRAY_SIZE(vv6410_ctrl) * sizeof(s32), GFP_KERNEL); @@ -138,18 +140,7 @@ static int vv6410_init(struct sd *sd) s32 *sensor_settings = sd->sensor_priv; for (i = 0; i < ARRAY_SIZE(stv_bridge_init); i++) { - /* if NULL then len contains single value */ - if (stv_bridge_init[i].data == NULL) { - err = stv06xx_write_bridge(sd, - stv_bridge_init[i].start, - stv_bridge_init[i].len); - } else { - int j; - for (j = 0; j < stv_bridge_init[i].len; j++) - err = stv06xx_write_bridge(sd, - stv_bridge_init[i].start + j, - stv_bridge_init[i].data[j]); - } + stv06xx_write_bridge(sd, stv_bridge_init[i].addr, stv_bridge_init[i].data); } if (err < 0) @@ -183,15 +174,6 @@ static int vv6410_start(struct sd *sd) struct cam *cam = &sd->gspca_dev.cam; u32 priv = cam->cam_mode[sd->gspca_dev.curr_mode].priv; - if (priv & VV6410_CROP_TO_QVGA) { - PDEBUG(D_CONF, "Cropping to QVGA"); - stv06xx_write_sensor(sd, VV6410_XENDH, 320 - 1); - stv06xx_write_sensor(sd, VV6410_YENDH, 240 - 1); - } else { - stv06xx_write_sensor(sd, VV6410_XENDH, 360 - 1); - stv06xx_write_sensor(sd, VV6410_YENDH, 294 - 1); - } - if (priv & VV6410_SUBSAMPLE) { PDEBUG(D_CONF, "Enabling subsampling"); stv06xx_write_bridge(sd, STV_Y_CTRL, 0x02); @@ -201,8 +183,8 @@ static int vv6410_start(struct sd *sd) } else { stv06xx_write_bridge(sd, STV_Y_CTRL, 0x01); stv06xx_write_bridge(sd, STV_X_CTRL, 0x0a); + stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x00); - stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x20); } /* Turn on LED */ @@ -242,11 +224,11 @@ static int vv6410_dump(struct sd *sd) u8 i; int err = 0; - info("Dumping all vv6410 sensor registers"); + pr_info("Dumping all vv6410 sensor registers\n"); for (i = 0; i < 0xff && !err; i++) { u16 data; err = stv06xx_read_sensor(sd, i, &data); - info("Register 0x%x contained 0x%x", i, data); + pr_info("Register 0x%x contained 0x%x\n", i, data); } return (err < 0) ? err : 0; } diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h index 7fe3587f5f7..a25b8873f2e 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h +++ b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h @@ -211,49 +211,49 @@ const struct stv06xx_sensor stv06xx_sensor_vv6410 = { /* If NULL, only single value to write, stored in len */ struct stv_init { - const u8 *data; - u16 start; - u8 len; -}; - -static const u8 x1500[] = { /* 0x1500 - 0x150f */ - 0x0b, 0xa7, 0xb7, 0x00, 0x00 -}; - -static const u8 x1536[] = { /* 0x1536 - 0x153b */ - 0x02, 0x00, 0x60, 0x01, 0x20, 0x01 + u16 addr; + u8 data; }; static const struct stv_init stv_bridge_init[] = { /* This reg is written twice. Some kind of reset? */ - {NULL, 0x1620, 0x80}, - {NULL, 0x1620, 0x00}, - {NULL, 0x1443, 0x00}, - {NULL, 0x1423, 0x04}, - {x1500, 0x1500, ARRAY_SIZE(x1500)}, - {x1536, 0x1536, ARRAY_SIZE(x1536)}, + {STV_RESET, 0x80}, + {STV_RESET, 0x00}, + {STV_SCAN_RATE, 0x00}, + {STV_I2C_FLUSH, 0x04}, + {STV_REG00, 0x0b}, + {STV_REG01, 0xa7}, + {STV_REG02, 0xb7}, + {STV_REG03, 0x00}, + {STV_REG04, 0x00}, + {0x1536, 0x02}, + {0x1537, 0x00}, + {0x1538, 0x60}, + {0x1539, 0x01}, + {0x153a, 0x20}, + {0x153b, 0x01}, }; static const u8 vv6410_sensor_init[][2] = { /* Setup registers */ - {VV6410_SETUP0, VV6410_SOFT_RESET}, - {VV6410_SETUP0, VV6410_LOW_POWER_MODE}, + {VV6410_SETUP0, VV6410_SOFT_RESET}, + {VV6410_SETUP0, VV6410_LOW_POWER_MODE}, /* Use shuffled read-out mode */ - {VV6410_SETUP1, BIT(6)}, - /* All modes to 1 */ - {VV6410_FGMODES, BIT(6) | BIT(4) | BIT(2) | BIT(0)}, - {VV6410_PINMAPPING, 0x00}, + {VV6410_SETUP1, BIT(6)}, + /* All modes to 1, FST, Fast QCK, Free running QCK, Free running LST, FST will qualify visible pixels */ + {VV6410_FGMODES, BIT(6) | BIT(4) | BIT(2) | BIT(0)}, + {VV6410_PINMAPPING, 0x00}, /* Pre-clock generator divide off */ - {VV6410_DATAFORMAT, BIT(7) | BIT(0)}, + {VV6410_DATAFORMAT, BIT(7) | BIT(0)}, - {VV6410_CLKDIV, VV6410_CLK_DIV_2}, + {VV6410_CLKDIV, VV6410_CLK_DIV_2}, /* System registers */ /* Enable voltage doubler */ - {VV6410_AS0, BIT(6) | BIT(4) | BIT(3) | BIT(2) | BIT(1)}, - {VV6410_AT0, 0x00}, + {VV6410_AS0, BIT(6) | BIT(4) | BIT(3) | BIT(2) | BIT(1)}, + {VV6410_AT0, 0x00}, /* Power up audio, differential */ - {VV6410_AT1, BIT(4)|BIT(0)}, + {VV6410_AT1, BIT(4) | BIT(0)}, }; #endif diff --git a/drivers/media/video/gspca/sunplus.c b/drivers/media/video/gspca/sunplus.c index 6ec23290218..c8909772435 100644 --- a/drivers/media/video/gspca/sunplus.c +++ b/drivers/media/video/gspca/sunplus.c @@ -19,6 +19,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #define MODULE_NAME "sunplus" #include "gspca.h" @@ -325,7 +327,7 @@ static void reg_r(struct gspca_dev *gspca_dev, #ifdef GSPCA_DEBUG if (len > USB_BUF_SZ) { - err("reg_r: buffer overflow"); + pr_err("reg_r: buffer overflow\n"); return; } #endif @@ -340,7 +342,7 @@ static void reg_r(struct gspca_dev *gspca_dev, len ? gspca_dev->usb_buf : NULL, len, 500); if (ret < 0) { - err("reg_r err %d", ret); + pr_err("reg_r err %d\n", ret); gspca_dev->usb_err = ret; } } @@ -365,7 +367,7 @@ static void reg_w_1(struct gspca_dev *gspca_dev, gspca_dev->usb_buf, 1, 500); if (ret < 0) { - err("reg_w_1 err %d", ret); + pr_err("reg_w_1 err %d\n", ret); gspca_dev->usb_err = ret; } } @@ -385,7 +387,7 @@ static void reg_w_riv(struct gspca_dev *gspca_dev, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, value, index, NULL, 0, 500); if (ret < 0) { - err("reg_w_riv err %d", ret); + pr_err("reg_w_riv err %d\n", ret); gspca_dev->usb_err = ret; return; } diff --git a/drivers/media/video/gspca/t613.c b/drivers/media/video/gspca/t613.c index d1d733b9359..90f0877eb59 100644 --- a/drivers/media/video/gspca/t613.c +++ b/drivers/media/video/gspca/t613.c @@ -26,6 +26,8 @@ * Costantino Leandro */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #define MODULE_NAME "t613" #include <linux/slab.h> @@ -572,7 +574,7 @@ static void reg_w_buf(struct gspca_dev *gspca_dev, tmpbuf = kmemdup(buffer, len, GFP_KERNEL); if (!tmpbuf) { - err("Out of memory"); + pr_err("Out of memory\n"); return; } usb_control_msg(gspca_dev->dev, @@ -598,7 +600,7 @@ static void reg_w_ixbuf(struct gspca_dev *gspca_dev, } else { p = tmpbuf = kmalloc(len * 2, GFP_KERNEL); if (!tmpbuf) { - err("Out of memory"); + pr_err("Out of memory\n"); return; } } @@ -652,7 +654,7 @@ static void om6802_sensor_init(struct gspca_dev *gspca_dev) } byte = reg_r(gspca_dev, 0x0063); if (byte != 0x17) { - err("Bad sensor reset %02x", byte); + pr_err("Bad sensor reset %02x\n", byte); /* continue? */ } @@ -890,7 +892,7 @@ static int sd_init(struct gspca_dev *gspca_dev) sd->sensor = SENSOR_OM6802; break; default: - err("unknown sensor %04x", sensor_id); + pr_err("unknown sensor %04x\n", sensor_id); return -EINVAL; } @@ -905,7 +907,7 @@ static int sd_init(struct gspca_dev *gspca_dev) break; /* OK */ } if (i < 0) { - err("Bad sensor reset %02x", test_byte); + pr_err("Bad sensor reset %02x\n", test_byte); return -EIO; } reg_w_buf(gspca_dev, n2, sizeof n2); diff --git a/drivers/media/video/gspca/topro.c b/drivers/media/video/gspca/topro.c new file mode 100644 index 00000000000..29596c59837 --- /dev/null +++ b/drivers/media/video/gspca/topro.c @@ -0,0 +1,4989 @@ +/* + * Topro TP6800/6810 webcam driver. + * + * Copyright (C) 2011 Jean-François Moine (http://moinejf.free.fr) + * Copyright (C) 2009 Anders Blomdell (anders.blomdell@control.lth.se) + * Copyright (C) 2008 Thomas Champagne (lafeuil@gmail.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; If not, see <http://www.gnu.org/licenses/>. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "gspca.h" + +MODULE_DESCRIPTION("Topro TP6800/6810 gspca webcam driver"); +MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>, " + "Anders Blomdell <anders.blomdell@control.lth.se>"); +MODULE_LICENSE("GPL"); + +static int force_sensor = -1; + +/* JPEG header */ +static const u8 jpeg_head[] = { + 0xff, 0xd8, /* jpeg */ + +/* quantization table quality 50% */ + 0xff, 0xdb, 0x00, 0x84, /* DQT */ +0, +#define JPEG_QT0_OFFSET 7 + 0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e, + 0x0d, 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28, + 0x1a, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25, + 0x1d, 0x28, 0x3a, 0x33, 0x3d, 0x3c, 0x39, 0x33, + 0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, 0x44, + 0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57, + 0x5f, 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71, + 0x79, 0x70, 0x64, 0x78, 0x5c, 0x65, 0x67, 0x63, +1, +#define JPEG_QT1_OFFSET 72 + 0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a, + 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + + /* Define Huffman table (thanks to Thomas Kaiser) */ + 0xff, 0xc4, 0x01, 0x5e, + 0x00, 0x00, 0x02, 0x03, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, + 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, + 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, + 0x07, 0x05, 0x04, 0x06, 0x01, 0x00, 0x00, 0x57, + 0x01, 0x02, 0x03, 0x00, 0x11, 0x04, 0x12, 0x21, + 0x31, 0x13, 0x41, 0x51, 0x61, 0x05, 0x22, 0x32, + 0x14, 0x71, 0x81, 0x91, 0x15, 0x23, 0x42, 0x52, + 0x62, 0xa1, 0xb1, 0x06, 0x33, 0x72, 0xc1, 0xd1, + 0x24, 0x43, 0x53, 0x82, 0x16, 0x34, 0x92, 0xa2, + 0xe1, 0xf1, 0xf0, 0x07, 0x08, 0x17, 0x18, 0x25, + 0x26, 0x27, 0x28, 0x35, 0x36, 0x37, 0x38, 0x44, + 0x45, 0x46, 0x47, 0x48, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x73, + 0x74, 0x75, 0x76, 0x77, 0x78, 0x83, 0x84, 0x85, + 0x86, 0x87, 0x88, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xb2, + 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xc2, 0xc3, + 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xd2, 0xd3, 0xd4, + 0xd5, 0xd6, 0xd7, 0xd8, 0xe2, 0xe3, 0xe4, 0xe5, + 0xe6, 0xe7, 0xe8, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, + 0xf7, 0xf8, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, + 0x05, 0x06, 0x07, 0x08, 0x09, 0x11, 0x00, 0x02, + 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, + 0x04, 0x06, 0x01, 0x00, 0x00, 0x57, 0x00, 0x01, + 0x11, 0x02, 0x21, 0x03, 0x12, 0x31, 0x41, 0x13, + 0x22, 0x51, 0x61, 0x04, 0x32, 0x71, 0x05, 0x14, + 0x23, 0x42, 0x33, 0x52, 0x81, 0x91, 0xa1, 0xb1, + 0xf0, 0x06, 0x15, 0xc1, 0xd1, 0xe1, 0x24, 0x43, + 0x62, 0xf1, 0x16, 0x25, 0x34, 0x53, 0x72, 0x82, + 0x92, 0x07, 0x08, 0x17, 0x18, 0x26, 0x27, 0x28, + 0x35, 0x36, 0x37, 0x38, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x54, 0x55, 0x56, 0x57, 0x58, 0x63, 0x64, + 0x65, 0x66, 0x67, 0x68, 0x73, 0x74, 0x75, 0x76, + 0x77, 0x78, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, + 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0xa2, 0xa3, + 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xb2, 0xb3, 0xb4, + 0xb5, 0xb6, 0xb7, 0xb8, 0xc2, 0xc3, 0xc4, 0xc5, + 0xc6, 0xc7, 0xc8, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, + 0xd7, 0xd8, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xff, 0xc0, 0x00, 0x11, /* SOF0 (start of frame 0 */ + 0x08, /* data precision */ +#define JPEG_HEIGHT_OFFSET 493 + 0x01, 0xe0, /* height */ + 0x02, 0x80, /* width */ + 0x03, /* component number */ + 0x01, + 0x21, /* samples Y = jpeg 422 */ + 0x00, /* quant Y */ + 0x02, 0x11, 0x01, /* samples CbCr - quant CbCr */ + 0x03, 0x11, 0x01, + + 0xff, 0xda, 0x00, 0x0c, /* SOS (start of scan) */ + 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00 +#define JPEG_HDR_SZ 521 +}; + +enum e_ctrl { + EXPOSURE, + QUALITY, + SHARPNESS, + RGAIN, + GAIN, + BGAIN, + GAMMA, + AUTOGAIN, + NCTRLS /* number of controls */ +}; + +#define AUTOGAIN_DEF 1 + +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + struct gspca_ctrl ctrls[NCTRLS]; + + u8 framerate; + u8 quality; /* webcam current JPEG quality (0..16) */ + s8 ag_cnt; /* autogain / start counter for tp6810 */ +#define AG_CNT_START 13 /* check gain every N frames */ + + u8 bridge; + u8 sensor; + + u8 jpeg_hdr[JPEG_HDR_SZ]; +}; + +enum bridges { + BRIDGE_TP6800, + BRIDGE_TP6810, +}; + +enum sensors { + SENSOR_CX0342, + SENSOR_SOI763A, /* ~= ov7630 / ov7648 */ + NSENSORS +}; + +static const struct v4l2_pix_format vga_mode[] = { + {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 4 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG}, + {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG} +}; + +/* + * JPEG quality + * index: webcam compression + * value: JPEG quality in % + */ +static const u8 jpeg_q[17] = { + 88, 77, 67, 57, 55, 55, 45, 45, 36, 36, 30, 30, 26, 26, 22, 22, 94 +}; + +#define BULK_OUT_SIZE 0x20 +#if BULK_OUT_SIZE > USB_BUF_SZ +#error "USB buffer too small" +#endif + +static const u8 rates[] = {30, 20, 15, 10, 7, 5}; +static const struct framerates framerates[] = { + { + .rates = rates, + .nrates = ARRAY_SIZE(rates) + }, + { + .rates = rates, + .nrates = ARRAY_SIZE(rates) + } +}; +static const u8 rates_6810[] = {30, 15, 10, 7, 5}; +static const struct framerates framerates_6810[] = { + { + .rates = rates_6810, + .nrates = ARRAY_SIZE(rates_6810) + }, + { + .rates = rates_6810, + .nrates = ARRAY_SIZE(rates_6810) + } +}; + +/* + * webcam quality in % + * the last value is the ultra fine quality + */ + +/* TP6800 register offsets */ +#define TP6800_R10_SIF_TYPE 0x10 +#define TP6800_R11_SIF_CONTROL 0x11 +#define TP6800_R12_SIF_ADDR_S 0x12 +#define TP6800_R13_SIF_TX_DATA 0x13 +#define TP6800_R14_SIF_RX_DATA 0x14 +#define TP6800_R15_GPIO_PU 0x15 +#define TP6800_R16_GPIO_PD 0x16 +#define TP6800_R17_GPIO_IO 0x17 +#define TP6800_R18_GPIO_DATA 0x18 +#define TP6800_R19_SIF_ADDR_S2 0x19 +#define TP6800_R1A_SIF_TX_DATA2 0x1a +#define TP6800_R1B_SIF_RX_DATA2 0x1b +#define TP6800_R21_ENDP_1_CTL 0x21 +#define TP6800_R2F_TIMING_CFG 0x2f +#define TP6800_R30_SENSOR_CFG 0x30 +#define TP6800_R31_PIXEL_START 0x31 +#define TP6800_R32_PIXEL_END_L 0x32 +#define TP6800_R33_PIXEL_END_H 0x33 +#define TP6800_R34_LINE_START 0x34 +#define TP6800_R35_LINE_END_L 0x35 +#define TP6800_R36_LINE_END_H 0x36 +#define TP6800_R37_FRONT_DARK_ST 0x37 +#define TP6800_R38_FRONT_DARK_END 0x38 +#define TP6800_R39_REAR_DARK_ST_L 0x39 +#define TP6800_R3A_REAR_DARK_ST_H 0x3a +#define TP6800_R3B_REAR_DARK_END_L 0x3b +#define TP6800_R3C_REAR_DARK_END_H 0x3c +#define TP6800_R3D_HORIZ_DARK_LINE_L 0x3d +#define TP6800_R3E_HORIZ_DARK_LINE_H 0x3e +#define TP6800_R3F_FRAME_RATE 0x3f +#define TP6800_R50 0x50 +#define TP6800_R51 0x51 +#define TP6800_R52 0x52 +#define TP6800_R53 0x53 +#define TP6800_R54_DARK_CFG 0x54 +#define TP6800_R55_GAMMA_R 0x55 +#define TP6800_R56_GAMMA_G 0x56 +#define TP6800_R57_GAMMA_B 0x57 +#define TP6800_R5C_EDGE_THRLD 0x5c +#define TP6800_R5D_DEMOSAIC_CFG 0x5d +#define TP6800_R78_FORMAT 0x78 +#define TP6800_R79_QUALITY 0x79 +#define TP6800_R7A_BLK_THRLD 0x7a + +/* CX0342 register offsets */ + +#define CX0342_SENSOR_ID 0x00 +#define CX0342_VERSION_NO 0x01 +#define CX0342_ORG_X_L 0x02 +#define CX0342_ORG_X_H 0x03 +#define CX0342_ORG_Y_L 0x04 +#define CX0342_ORG_Y_H 0x05 +#define CX0342_STOP_X_L 0x06 +#define CX0342_STOP_X_H 0x07 +#define CX0342_STOP_Y_L 0x08 +#define CX0342_STOP_Y_H 0x09 +#define CX0342_FRAME_WIDTH_L 0x0a +#define CX0342_FRAME_WIDTH_H 0x0b +#define CX0342_FRAME_HEIGH_L 0x0c +#define CX0342_FRAME_HEIGH_H 0x0d +#define CX0342_EXPO_LINE_L 0x10 +#define CX0342_EXPO_LINE_H 0x11 +#define CX0342_EXPO_CLK_L 0x12 +#define CX0342_EXPO_CLK_H 0x13 +#define CX0342_RAW_GRGAIN_L 0x14 +#define CX0342_RAW_GRGAIN_H 0x15 +#define CX0342_RAW_GBGAIN_L 0x16 +#define CX0342_RAW_GBGAIN_H 0x17 +#define CX0342_RAW_RGAIN_L 0x18 +#define CX0342_RAW_RGAIN_H 0x19 +#define CX0342_RAW_BGAIN_L 0x1a +#define CX0342_RAW_BGAIN_H 0x1b +#define CX0342_GLOBAL_GAIN 0x1c +#define CX0342_SYS_CTRL_0 0x20 +#define CX0342_SYS_CTRL_1 0x21 +#define CX0342_SYS_CTRL_2 0x22 +#define CX0342_BYPASS_MODE 0x23 +#define CX0342_SYS_CTRL_3 0x24 +#define CX0342_TIMING_EN 0x25 +#define CX0342_OUTPUT_CTRL 0x26 +#define CX0342_AUTO_ADC_CALIB 0x27 +#define CX0342_SYS_CTRL_4 0x28 +#define CX0342_ADCGN 0x30 +#define CX0342_SLPCR 0x31 +#define CX0342_SLPFN_LO 0x32 +#define CX0342_ADC_CTL 0x33 +#define CX0342_LVRST_BLBIAS 0x34 +#define CX0342_VTHSEL 0x35 +#define CX0342_RAMP_RIV 0x36 +#define CX0342_LDOSEL 0x37 +#define CX0342_CLOCK_GEN 0x40 +#define CX0342_SOFT_RESET 0x41 +#define CX0342_PLL 0x42 +#define CX0342_DR_ENH_PULSE_OFFSET_L 0x43 +#define CX0342_DR_ENH_PULSE_OFFSET_H 0x44 +#define CX0342_DR_ENH_PULSE_POS_L 0x45 +#define CX0342_DR_ENH_PULSE_POS_H 0x46 +#define CX0342_DR_ENH_PULSE_WIDTH 0x47 +#define CX0342_AS_CURRENT_CNT_L 0x48 +#define CX0342_AS_CURRENT_CNT_H 0x49 +#define CX0342_AS_PREVIOUS_CNT_L 0x4a +#define CX0342_AS_PREVIOUS_CNT_H 0x4b +#define CX0342_SPV_VALUE_L 0x4c +#define CX0342_SPV_VALUE_H 0x4d +#define CX0342_GPXLTHD_L 0x50 +#define CX0342_GPXLTHD_H 0x51 +#define CX0342_RBPXLTHD_L 0x52 +#define CX0342_RBPXLTHD_H 0x53 +#define CX0342_PLANETHD_L 0x54 +#define CX0342_PLANETHD_H 0x55 +#define CX0342_ROWDARK_TH 0x56 +#define CX0342_ROWDARK_TOL 0x57 +#define CX0342_RB_GAP_L 0x58 +#define CX0342_RB_GAP_H 0x59 +#define CX0342_G_GAP_L 0x5a +#define CX0342_G_GAP_H 0x5b +#define CX0342_AUTO_ROW_DARK 0x60 +#define CX0342_MANUAL_DARK_VALUE 0x61 +#define CX0342_GB_DARK_OFFSET 0x62 +#define CX0342_GR_DARK_OFFSET 0x63 +#define CX0342_RED_DARK_OFFSET 0x64 +#define CX0342_BLUE_DARK_OFFSET 0x65 +#define CX0342_DATA_SCALING_MULTI 0x66 +#define CX0342_AUTOD_Q_FRAME 0x67 +#define CX0342_AUTOD_ALLOW_VARI 0x68 +#define CX0342_AUTO_DARK_VALUE_L 0x69 +#define CX0342_AUTO_DARK_VALUE_H 0x6a +#define CX0342_IO_CTRL_0 0x70 +#define CX0342_IO_CTRL_1 0x71 +#define CX0342_IO_CTRL_2 0x72 +#define CX0342_IDLE_CTRL 0x73 +#define CX0342_TEST_MODE 0x74 +#define CX0342_FRAME_FIX_DATA_TEST 0x75 +#define CX0342_FRAME_CNT_TEST 0x76 +#define CX0342_RST_OVERFLOW_L 0x80 +#define CX0342_RST_OVERFLOW_H 0x81 +#define CX0342_RST_UNDERFLOW_L 0x82 +#define CX0342_RST_UNDERFLOW_H 0x83 +#define CX0342_DATA_OVERFLOW_L 0x84 +#define CX0342_DATA_OVERFLOW_H 0x85 +#define CX0342_DATA_UNDERFLOW_L 0x86 +#define CX0342_DATA_UNDERFLOW_H 0x87 +#define CX0342_CHANNEL_0_0_L_irst 0x90 +#define CX0342_CHANNEL_0_0_H_irst 0x91 +#define CX0342_CHANNEL_0_1_L_irst 0x92 +#define CX0342_CHANNEL_0_1_H_irst 0x93 +#define CX0342_CHANNEL_0_2_L_irst 0x94 +#define CX0342_CHANNEL_0_2_H_irst 0x95 +#define CX0342_CHANNEL_0_3_L_irst 0x96 +#define CX0342_CHANNEL_0_3_H_irst 0x97 +#define CX0342_CHANNEL_0_4_L_irst 0x98 +#define CX0342_CHANNEL_0_4_H_irst 0x99 +#define CX0342_CHANNEL_0_5_L_irst 0x9a +#define CX0342_CHANNEL_0_5_H_irst 0x9b +#define CX0342_CHANNEL_0_6_L_irst 0x9c +#define CX0342_CHANNEL_0_6_H_irst 0x9d +#define CX0342_CHANNEL_0_7_L_irst 0x9e +#define CX0342_CHANNEL_0_7_H_irst 0x9f +#define CX0342_CHANNEL_1_0_L_itx 0xa0 +#define CX0342_CHANNEL_1_0_H_itx 0xa1 +#define CX0342_CHANNEL_1_1_L_itx 0xa2 +#define CX0342_CHANNEL_1_1_H_itx 0xa3 +#define CX0342_CHANNEL_1_2_L_itx 0xa4 +#define CX0342_CHANNEL_1_2_H_itx 0xa5 +#define CX0342_CHANNEL_1_3_L_itx 0xa6 +#define CX0342_CHANNEL_1_3_H_itx 0xa7 +#define CX0342_CHANNEL_1_4_L_itx 0xa8 +#define CX0342_CHANNEL_1_4_H_itx 0xa9 +#define CX0342_CHANNEL_1_5_L_itx 0xaa +#define CX0342_CHANNEL_1_5_H_itx 0xab +#define CX0342_CHANNEL_1_6_L_itx 0xac +#define CX0342_CHANNEL_1_6_H_itx 0xad +#define CX0342_CHANNEL_1_7_L_itx 0xae +#define CX0342_CHANNEL_1_7_H_itx 0xaf +#define CX0342_CHANNEL_2_0_L_iwl 0xb0 +#define CX0342_CHANNEL_2_0_H_iwl 0xb1 +#define CX0342_CHANNEL_2_1_L_iwl 0xb2 +#define CX0342_CHANNEL_2_1_H_iwl 0xb3 +#define CX0342_CHANNEL_2_2_L_iwl 0xb4 +#define CX0342_CHANNEL_2_2_H_iwl 0xb5 +#define CX0342_CHANNEL_2_3_L_iwl 0xb6 +#define CX0342_CHANNEL_2_3_H_iwl 0xb7 +#define CX0342_CHANNEL_2_4_L_iwl 0xb8 +#define CX0342_CHANNEL_2_4_H_iwl 0xb9 +#define CX0342_CHANNEL_2_5_L_iwl 0xba +#define CX0342_CHANNEL_2_5_H_iwl 0xbb +#define CX0342_CHANNEL_2_6_L_iwl 0xbc +#define CX0342_CHANNEL_2_6_H_iwl 0xbd +#define CX0342_CHANNEL_2_7_L_iwl 0xbe +#define CX0342_CHANNEL_2_7_H_iwl 0xbf +#define CX0342_CHANNEL_3_0_L_ensp 0xc0 +#define CX0342_CHANNEL_3_0_H_ensp 0xc1 +#define CX0342_CHANNEL_3_1_L_ensp 0xc2 +#define CX0342_CHANNEL_3_1_H_ensp 0xc3 +#define CX0342_CHANNEL_3_2_L_ensp 0xc4 +#define CX0342_CHANNEL_3_2_H_ensp 0xc5 +#define CX0342_CHANNEL_3_3_L_ensp 0xc6 +#define CX0342_CHANNEL_3_3_H_ensp 0xc7 +#define CX0342_CHANNEL_3_4_L_ensp 0xc8 +#define CX0342_CHANNEL_3_4_H_ensp 0xc9 +#define CX0342_CHANNEL_3_5_L_ensp 0xca +#define CX0342_CHANNEL_3_5_H_ensp 0xcb +#define CX0342_CHANNEL_3_6_L_ensp 0xcc +#define CX0342_CHANNEL_3_6_H_ensp 0xcd +#define CX0342_CHANNEL_3_7_L_ensp 0xce +#define CX0342_CHANNEL_3_7_H_ensp 0xcf +#define CX0342_CHANNEL_4_0_L_sela 0xd0 +#define CX0342_CHANNEL_4_0_H_sela 0xd1 +#define CX0342_CHANNEL_4_1_L_sela 0xd2 +#define CX0342_CHANNEL_4_1_H_sela 0xd3 +#define CX0342_CHANNEL_5_0_L_intla 0xe0 +#define CX0342_CHANNEL_5_0_H_intla 0xe1 +#define CX0342_CHANNEL_5_1_L_intla 0xe2 +#define CX0342_CHANNEL_5_1_H_intla 0xe3 +#define CX0342_CHANNEL_5_2_L_intla 0xe4 +#define CX0342_CHANNEL_5_2_H_intla 0xe5 +#define CX0342_CHANNEL_5_3_L_intla 0xe6 +#define CX0342_CHANNEL_5_3_H_intla 0xe7 +#define CX0342_CHANNEL_6_0_L_xa_sel_pos 0xf0 +#define CX0342_CHANNEL_6_0_H_xa_sel_pos 0xf1 +#define CX0342_CHANNEL_7_1_L_cds_pos 0xf2 +#define CX0342_CHANNEL_7_1_H_cds_pos 0xf3 +#define CX0342_SENSOR_HEIGHT_L 0xfb +#define CX0342_SENSOR_HEIGHT_H 0xfc +#define CX0342_SENSOR_WIDTH_L 0xfd +#define CX0342_SENSOR_WIDTH_H 0xfe +#define CX0342_VSYNC_HSYNC_READ 0xff + +struct cmd { + u8 reg; + u8 val; +}; + +static const u8 DQT[17][130] = { + /* Define quantization table (thanks to Thomas Kaiser) */ + { /* Quality 0 */ + 0x00, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x01, + 0x04, 0x04, 0x04, 0x06, 0x05, 0x06, 0x0b, 0x06, + 0x06, 0x0b, 0x18, 0x10, 0x0e, 0x10, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + }, + { /* Quality 1 */ + 0x00, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x01, + 0x08, 0x09, 0x09, 0x0c, 0x0a, 0x0c, 0x17, 0x0d, + 0x0d, 0x17, 0x31, 0x21, 0x1c, 0x21, 0x31, 0x31, + 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, + 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, + 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, + 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, + 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, + 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, + }, + { /* Quality 2 */ + 0x00, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x06, 0x06, 0x06, 0x04, 0x04, 0x04, + 0x04, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x01, + 0x0c, 0x0d, 0x0d, 0x12, 0x0f, 0x12, 0x23, 0x13, + 0x13, 0x23, 0x4a, 0x31, 0x2a, 0x31, 0x4a, 0x4a, + 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, + 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, + 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, + 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, + 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, + 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, + }, + { /* Quality 3 */ + 0x00, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04, + 0x04, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x13, 0x13, 0x13, 0x13, 0x13, + 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, + 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, + 0x01, + 0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a, + 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + }, + { /* Quality 4 */ + 0x00, + 0x04, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x0a, 0x0a, 0x0a, 0x05, 0x05, 0x05, + 0x05, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x17, 0x17, 0x17, 0x17, 0x17, + 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, + 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, + 0x01, + 0x11, 0x16, 0x16, 0x1e, 0x1a, 0x1e, 0x3a, 0x20, + 0x20, 0x3a, 0x7b, 0x52, 0x46, 0x52, 0x7b, 0x7b, + 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, + 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, + 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, + 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, + 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, + 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, + }, + { /* Quality 5 */ + 0x00, + 0x04, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x0c, 0x0c, 0x0c, 0x06, 0x06, 0x06, + 0x06, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, + 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, + 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, + 0x01, + 0x11, 0x1b, 0x1b, 0x24, 0x1f, 0x24, 0x46, 0x27, + 0x27, 0x46, 0x94, 0x63, 0x54, 0x63, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + }, + { /* Quality 6 */ + 0x00, + 0x05, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x0e, 0x0e, 0x0e, 0x07, 0x07, 0x07, + 0x07, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x21, 0x21, 0x21, 0x21, 0x21, + 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, + 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, + 0x01, + 0x15, 0x1f, 0x1f, 0x2a, 0x24, 0x2a, 0x52, 0x2d, + 0x2d, 0x52, 0xad, 0x73, 0x62, 0x73, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + }, + { /* Quality 7 */ + 0x00, + 0x05, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x10, 0x10, 0x10, 0x08, 0x08, 0x08, + 0x08, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x26, 0x26, 0x26, 0x26, 0x26, + 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, + 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, + 0x01, + 0x15, 0x24, 0x24, 0x30, 0x2a, 0x30, 0x5e, 0x34, + 0x34, 0x5e, 0xc6, 0x84, 0x70, 0x84, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + }, + { /* Quality 8 */ + 0x00, + 0x06, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x14, 0x14, 0x14, 0x0a, 0x0a, 0x0a, + 0x0a, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, + 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, + 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, + 0x14, 0x14, 0x14, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, + 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, + 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, + 0x01, + 0x19, 0x2d, 0x2d, 0x3c, 0x34, 0x3c, 0x75, 0x41, + 0x41, 0x75, 0xf7, 0xa5, 0x8c, 0xa5, 0xf7, 0xf7, + 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, + 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, + 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, + 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, + 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, + 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, + }, + { /* Quality 9 */ + 0x00, + 0x06, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x18, 0x18, 0x18, 0x0c, 0x0c, 0x0c, + 0x0c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x01, + 0x19, 0x36, 0x36, 0x48, 0x3f, 0x48, 0x8d, 0x4e, + 0x4e, 0x8d, 0xff, 0xc6, 0xa8, 0xc6, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + { /* Quality 10 */ + 0x00, + 0x07, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x1c, 0x1c, 0x1c, 0x0e, 0x0e, 0x0e, + 0x0e, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, + 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, + 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, + 0x1c, 0x1c, 0x1c, 0x42, 0x42, 0x42, 0x42, 0x42, + 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, + 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, + 0x01, + 0x1d, 0x3f, 0x3f, 0x54, 0x49, 0x54, 0xa4, 0x5b, + 0x5b, 0xa4, 0xff, 0xe7, 0xc4, 0xe7, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + { /* Quality 11 */ + 0x00, + 0x07, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x20, 0x20, 0x20, 0x10, 0x10, 0x10, + 0x10, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, + 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, + 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, + 0x01, + 0x1d, 0x48, 0x48, 0x60, 0x54, 0x60, 0xbc, 0x68, + 0x68, 0xbc, 0xff, 0xff, 0xe0, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + { /* Quality 12 */ + 0x00, + 0x08, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, + 0x14, 0x14, 0x28, 0x28, 0x28, 0x14, 0x14, 0x14, + 0x14, 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, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, + 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, + 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, + 0x01, + 0x22, 0x5a, 0x5a, 0x78, 0x69, 0x78, 0xeb, 0x82, + 0x82, 0xeb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + { /* Quality 13 */ + 0x00, + 0x08, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x30, 0x30, 0x30, 0x18, 0x18, 0x18, + 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x72, 0x72, 0x72, 0x72, 0x72, + 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, + 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, + 0x01, + 0x22, 0x6c, 0x6c, 0x90, 0x7e, 0x90, 0xff, 0x9c, + 0x9c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + { /* Quality 14 */ + 0x00, + 0x0a, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, + 0x1c, 0x1c, 0x38, 0x38, 0x38, 0x1c, 0x1c, 0x1c, + 0x1c, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, + 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, + 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, + 0x38, 0x38, 0x38, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x01, + 0x2a, 0x7e, 0x7e, 0xa8, 0x93, 0xa8, 0xff, 0xb6, + 0xb6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + { /* Quality 15 */ + 0x00, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x40, 0x40, 0x40, 0x20, 0x20, 0x20, + 0x20, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x01, + 0x2a, 0x90, 0x90, 0xc0, 0xa8, 0xc0, 0xff, 0xd0, + 0xd0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + { /* Quality 16-31 */ + 0x00, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x01, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + } +}; + +static const struct cmd tp6810_cx_init_common[] = { + {0x1c, 0x00}, + {TP6800_R10_SIF_TYPE, 0x00}, + {0x4e, 0x00}, + {0x4f, 0x00}, + {TP6800_R50, 0xff}, + {TP6800_R51, 0x03}, + {0x00, 0x07}, + {TP6800_R79_QUALITY, 0x03}, + {TP6800_R2F_TIMING_CFG, 0x37}, + {TP6800_R30_SENSOR_CFG, 0x10}, + {TP6800_R21_ENDP_1_CTL, 0x00}, + {TP6800_R52, 0x40}, + {TP6800_R53, 0x40}, + {TP6800_R54_DARK_CFG, 0x40}, + {TP6800_R30_SENSOR_CFG, 0x18}, + {0x4b, 0x00}, + {TP6800_R3F_FRAME_RATE, 0x83}, + {TP6800_R79_QUALITY, 0x05}, + {TP6800_R21_ENDP_1_CTL, 0x00}, + {0x7c, 0x04}, + {0x25, 0x14}, + {0x26, 0x0f}, + {0x7b, 0x10}, +}; + +static const struct cmd tp6810_ov_init_common[] = { + {0x1c, 0x00}, + {TP6800_R10_SIF_TYPE, 0x00}, + {0x4e, 0x00}, + {0x4f, 0x00}, + {TP6800_R50, 0xff}, + {TP6800_R51, 0x03}, + {0x00, 0x07}, + {TP6800_R52, 0x40}, + {TP6800_R53, 0x40}, + {TP6800_R54_DARK_CFG, 0x40}, + {TP6800_R79_QUALITY, 0x03}, + {TP6800_R2F_TIMING_CFG, 0x17}, + {TP6800_R30_SENSOR_CFG, 0x18}, + {TP6800_R21_ENDP_1_CTL, 0x00}, + {TP6800_R3F_FRAME_RATE, 0x86}, + {0x25, 0x18}, + {0x26, 0x0f}, + {0x7b, 0x90}, +}; + +static const struct cmd tp6810_bridge_start[] = { + {0x59, 0x88}, + {0x5a, 0x0f}, + {0x5b, 0x4e}, + {TP6800_R5C_EDGE_THRLD, 0x63}, + {TP6800_R5D_DEMOSAIC_CFG, 0x00}, + {0x03, 0x7f}, + {0x04, 0x80}, + {0x06, 0x00}, + {0x00, 0x00}, +}; + +static const struct cmd tp6810_late_start[] = { + {0x7d, 0x01}, + {0xb0, 0x04}, + {0xb1, 0x04}, + {0xb2, 0x04}, + {0xb3, 0x04}, + {0xb4, 0x04}, + {0xb5, 0x04}, + {0xb6, 0x08}, + {0xb7, 0x08}, + {0xb8, 0x04}, + {0xb9, 0x04}, + {0xba, 0x04}, + {0xbb, 0x04}, + {0xbc, 0x04}, + {0xbd, 0x08}, + {0xbe, 0x08}, + {0xbf, 0x08}, + {0xc0, 0x04}, + {0xc1, 0x04}, + {0xc2, 0x08}, + {0xc3, 0x08}, + {0xc4, 0x08}, + {0xc5, 0x08}, + {0xc6, 0x08}, + {0xc7, 0x13}, + {0xc8, 0x04}, + {0xc9, 0x08}, + {0xca, 0x08}, + {0xcb, 0x08}, + {0xcc, 0x08}, + {0xcd, 0x08}, + {0xce, 0x13}, + {0xcf, 0x13}, + {0xd0, 0x08}, + {0xd1, 0x08}, + {0xd2, 0x08}, + {0xd3, 0x08}, + {0xd4, 0x08}, + {0xd5, 0x13}, + {0xd6, 0x13}, + {0xd7, 0x13}, + {0xd8, 0x08}, + {0xd9, 0x08}, + {0xda, 0x08}, + {0xdb, 0x08}, + {0xdc, 0x13}, + {0xdd, 0x13}, + {0xde, 0x13}, + {0xdf, 0x13}, + {0xe0, 0x08}, + {0xe1, 0x08}, + {0xe2, 0x08}, + {0xe3, 0x13}, + {0xe4, 0x13}, + {0xe5, 0x13}, + {0xe6, 0x13}, + {0xe7, 0x13}, + {0xe8, 0x08}, + {0xe9, 0x08}, + {0xea, 0x13}, + {0xeb, 0x13}, + {0xec, 0x13}, + {0xed, 0x13}, + {0xee, 0x13}, + {0xef, 0x13}, + {0x7d, 0x02}, + + /* later after isoc start */ + {0x7d, 0x08}, + {0x7d, 0x00}, +}; + +static const struct cmd cx0342_timing_seq[] = { + {CX0342_CHANNEL_0_1_L_irst, 0x20}, + {CX0342_CHANNEL_0_2_L_irst, 0x24}, + {CX0342_CHANNEL_0_2_H_irst, 0x00}, + {CX0342_CHANNEL_0_3_L_irst, 0x2f}, + {CX0342_CHANNEL_0_3_H_irst, 0x00}, + {CX0342_CHANNEL_1_0_L_itx, 0x02}, + {CX0342_CHANNEL_1_0_H_itx, 0x00}, + {CX0342_CHANNEL_1_1_L_itx, 0x20}, + {CX0342_CHANNEL_1_1_H_itx, 0x00}, + {CX0342_CHANNEL_1_2_L_itx, 0xe4}, + {CX0342_CHANNEL_1_2_H_itx, 0x00}, + {CX0342_CHANNEL_1_3_L_itx, 0xee}, + {CX0342_CHANNEL_1_3_H_itx, 0x00}, + {CX0342_CHANNEL_2_0_L_iwl, 0x30}, + {CX0342_CHANNEL_2_0_H_iwl, 0x00}, + {CX0342_CHANNEL_3_0_L_ensp, 0x34}, + {CX0342_CHANNEL_3_1_L_ensp, 0xe2}, + {CX0342_CHANNEL_3_1_H_ensp, 0x00}, + {CX0342_CHANNEL_3_2_L_ensp, 0xf6}, + {CX0342_CHANNEL_3_2_H_ensp, 0x00}, + {CX0342_CHANNEL_3_3_L_ensp, 0xf4}, + {CX0342_CHANNEL_3_3_H_ensp, 0x02}, + {CX0342_CHANNEL_4_0_L_sela, 0x26}, + {CX0342_CHANNEL_4_0_H_sela, 0x00}, + {CX0342_CHANNEL_4_1_L_sela, 0xe2}, + {CX0342_CHANNEL_4_1_H_sela, 0x00}, + {CX0342_CHANNEL_5_0_L_intla, 0x26}, + {CX0342_CHANNEL_5_1_L_intla, 0x29}, + {CX0342_CHANNEL_5_2_L_intla, 0xf0}, + {CX0342_CHANNEL_5_2_H_intla, 0x00}, + {CX0342_CHANNEL_5_3_L_intla, 0xf3}, + {CX0342_CHANNEL_5_3_H_intla, 0x00}, + {CX0342_CHANNEL_6_0_L_xa_sel_pos, 0x24}, + {CX0342_CHANNEL_7_1_L_cds_pos, 0x02}, + {CX0342_TIMING_EN, 0x01}, +}; + +/* define the JPEG header */ +static void jpeg_define(u8 *jpeg_hdr, + int height, + int width) +{ + memcpy(jpeg_hdr, jpeg_head, sizeof jpeg_head); + jpeg_hdr[JPEG_HEIGHT_OFFSET + 0] = height >> 8; + jpeg_hdr[JPEG_HEIGHT_OFFSET + 1] = height; + jpeg_hdr[JPEG_HEIGHT_OFFSET + 2] = width >> 8; + jpeg_hdr[JPEG_HEIGHT_OFFSET + 3] = width; +} + +/* set the JPEG quality for sensor soi763a */ +static void jpeg_set_qual(u8 *jpeg_hdr, + int quality) +{ + int i, sc; + + if (quality < 50) + sc = 5000 / quality; + else + sc = 200 - quality * 2; + for (i = 0; i < 64; i++) { + jpeg_hdr[JPEG_QT0_OFFSET + i] = + (jpeg_head[JPEG_QT0_OFFSET + i] * sc + 50) / 100; + jpeg_hdr[JPEG_QT1_OFFSET + i] = + (jpeg_head[JPEG_QT1_OFFSET + i] * sc + 50) / 100; + } +} + +static void reg_w(struct gspca_dev *gspca_dev, u8 index, u8 value) +{ + struct usb_device *dev = gspca_dev->dev; + int ret; + + if (gspca_dev->usb_err < 0) + return; + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + 0x0e, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, NULL, 0, 500); + if (ret < 0) { + pr_err("reg_w err %d\n", ret); + gspca_dev->usb_err = ret; + } +} + +/* the returned value is in gspca_dev->usb_buf */ +static void reg_r(struct gspca_dev *gspca_dev, u8 index) +{ + struct usb_device *dev = gspca_dev->dev; + int ret; + + if (gspca_dev->usb_err < 0) + return; + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + 0x0d, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, index, gspca_dev->usb_buf, 1, 500); + if (ret < 0) { + pr_err("reg_r err %d\n", ret); + gspca_dev->usb_err = ret; + } +} + +static void reg_w_buf(struct gspca_dev *gspca_dev, + const struct cmd *p, int l) +{ + do { + reg_w(gspca_dev, p->reg, p->val); + p++; + } while (--l > 0); +} + +static int i2c_w(struct gspca_dev *gspca_dev, u8 index, u8 value) +{ + struct sd *sd = (struct sd *) gspca_dev; + + reg_w(gspca_dev, TP6800_R11_SIF_CONTROL, 0x00); + reg_w(gspca_dev, TP6800_R19_SIF_ADDR_S2, index); + reg_w(gspca_dev, TP6800_R13_SIF_TX_DATA, value); + reg_w(gspca_dev, TP6800_R11_SIF_CONTROL, 0x01); + if (sd->bridge == BRIDGE_TP6800) + return 0; + msleep(5); + reg_r(gspca_dev, TP6800_R11_SIF_CONTROL); + if (gspca_dev->usb_buf[0] == 0) + return 0; + reg_w(gspca_dev, TP6800_R11_SIF_CONTROL, 0x00); + return -1; /* error */ +} + +static void i2c_w_buf(struct gspca_dev *gspca_dev, + const struct cmd *p, int l) +{ + do { + i2c_w(gspca_dev, p->reg, p->val); + p++; + } while (--l > 0); +} + +static int i2c_r(struct gspca_dev *gspca_dev, u8 index, int len) +{ + struct sd *sd = (struct sd *) gspca_dev; + int v; + + reg_w(gspca_dev, TP6800_R19_SIF_ADDR_S2, index); + reg_w(gspca_dev, TP6800_R11_SIF_CONTROL, 0x02); + msleep(5); + reg_r(gspca_dev, TP6800_R14_SIF_RX_DATA); + v = gspca_dev->usb_buf[0]; + if (sd->bridge == BRIDGE_TP6800) + return v; + if (len > 1) { + reg_r(gspca_dev, TP6800_R1B_SIF_RX_DATA2); + v |= (gspca_dev->usb_buf[0] << 8); + } + reg_r(gspca_dev, TP6800_R11_SIF_CONTROL); + if (gspca_dev->usb_buf[0] == 0) + return v; + reg_w(gspca_dev, TP6800_R11_SIF_CONTROL, 0x00); + return -1; +} + +static void bulk_w(struct gspca_dev *gspca_dev, + u8 tag, + const u8 *data, + int length) +{ + struct usb_device *dev = gspca_dev->dev; + int count, actual_count, ret; + + if (gspca_dev->usb_err < 0) + return; + for (;;) { + count = length > BULK_OUT_SIZE - 1 + ? BULK_OUT_SIZE - 1 : length; + gspca_dev->usb_buf[0] = tag; + memcpy(&gspca_dev->usb_buf[1], data, count); + ret = usb_bulk_msg(dev, + usb_sndbulkpipe(dev, 3), + gspca_dev->usb_buf, count + 1, + &actual_count, 500); + if (ret < 0) { + pr_err("bulk write error %d tag=%02x\n", + ret, tag); + gspca_dev->usb_err = ret; + return; + } + length -= count; + if (length <= 0) + break; + data += count; + } +} + +static int probe_6810(struct gspca_dev *gspca_dev) +{ + u8 gpio; + int ret; + + reg_r(gspca_dev, TP6800_R18_GPIO_DATA); + gpio = gspca_dev->usb_buf[0]; + reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio); + reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20); + reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio); + reg_w(gspca_dev, TP6800_R10_SIF_TYPE, 0x04); /* i2c 16 bits */ + reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x21); /* ov??? */ + reg_w(gspca_dev, TP6800_R1A_SIF_TX_DATA2, 0x00); + if (i2c_w(gspca_dev, 0x00, 0x00) >= 0) + return SENSOR_SOI763A; + + reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20); + reg_w(gspca_dev, TP6800_R10_SIF_TYPE, 0x00); /* i2c 8 bits */ + reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x7f); /* (unknown i2c) */ + if (i2c_w(gspca_dev, 0x00, 0x00) >= 0) + return -2; + + reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20); + reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio); + reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20); + reg_w(gspca_dev, TP6800_R10_SIF_TYPE, 0x00); /* i2c 8 bits */ + reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x11); /* tas??? / hv??? */ + ret = i2c_r(gspca_dev, 0x00, 1); + if (ret > 0) + return -3; + + reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20); + reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio); + reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20); + reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x6e); /* po??? */ + ret = i2c_r(gspca_dev, 0x00, 1); + if (ret > 0) + return -4; + + ret = i2c_r(gspca_dev, 0x01, 1); + if (ret > 0) + return -5; + + reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20); + reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio); + reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20); + reg_w(gspca_dev, TP6800_R10_SIF_TYPE, 0x04); /* i2c 16 bits */ + reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x5d); /* mi/mt??? */ + ret = i2c_r(gspca_dev, 0x00, 2); + if (ret > 0) + return -6; + + reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20); + reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio); + reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20); + reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x5c); /* mi/mt??? */ + ret = i2c_r(gspca_dev, 0x36, 2); + if (ret > 0) + return -7; + + reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio); + reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20); + reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio); + reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x61); /* (unknown i2c) */ + reg_w(gspca_dev, TP6800_R1A_SIF_TX_DATA2, 0x10); + if (i2c_w(gspca_dev, 0xff, 0x00) >= 0) + return -8; + + reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20); + reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio); + reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20); + reg_w(gspca_dev, TP6800_R10_SIF_TYPE, 0x00); /* i2c 8 bits */ + reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x20); /* cx0342 */ + ret = i2c_r(gspca_dev, 0x00, 1); + if (ret > 0) + return SENSOR_CX0342; + return -9; +} + +static void cx0342_6810_init(struct gspca_dev *gspca_dev) +{ + static const struct cmd reg_init_1[] = { + {TP6800_R2F_TIMING_CFG, 0x2f}, + {0x25, 0x02}, + {TP6800_R21_ENDP_1_CTL, 0x00}, + {TP6800_R3F_FRAME_RATE, 0x80}, + {TP6800_R2F_TIMING_CFG, 0x2f}, + {TP6800_R18_GPIO_DATA, 0xe1}, + {TP6800_R18_GPIO_DATA, 0xc1}, + {TP6800_R18_GPIO_DATA, 0xe1}, + {TP6800_R11_SIF_CONTROL, 0x00}, + }; + static const struct cmd reg_init_2[] = { + {TP6800_R78_FORMAT, 0x48}, + {TP6800_R11_SIF_CONTROL, 0x00}, + }; + static const struct cmd sensor_init[] = { + {CX0342_OUTPUT_CTRL, 0x07}, + {CX0342_BYPASS_MODE, 0x58}, + {CX0342_GPXLTHD_L, 0x28}, + {CX0342_RBPXLTHD_L, 0x28}, + {CX0342_PLANETHD_L, 0x50}, + {CX0342_PLANETHD_H, 0x03}, + {CX0342_RB_GAP_L, 0xff}, + {CX0342_RB_GAP_H, 0x07}, + {CX0342_G_GAP_L, 0xff}, + {CX0342_G_GAP_H, 0x07}, + {CX0342_RST_OVERFLOW_L, 0x5c}, + {CX0342_RST_OVERFLOW_H, 0x01}, + {CX0342_DATA_OVERFLOW_L, 0xfc}, + {CX0342_DATA_OVERFLOW_H, 0x03}, + {CX0342_DATA_UNDERFLOW_L, 0x00}, + {CX0342_DATA_UNDERFLOW_H, 0x00}, + {CX0342_SYS_CTRL_0, 0x40}, + {CX0342_GLOBAL_GAIN, 0x01}, + {CX0342_CLOCK_GEN, 0x00}, + {CX0342_SYS_CTRL_0, 0x02}, + {CX0342_IDLE_CTRL, 0x05}, + {CX0342_ADCGN, 0x00}, + {CX0342_ADC_CTL, 0x00}, + {CX0342_LVRST_BLBIAS, 0x01}, + {CX0342_VTHSEL, 0x0b}, + {CX0342_RAMP_RIV, 0x0b}, + {CX0342_LDOSEL, 0x07}, + {CX0342_SPV_VALUE_L, 0x40}, + {CX0342_SPV_VALUE_H, 0x02}, + + {CX0342_AUTO_ADC_CALIB, 0x81}, + {CX0342_TIMING_EN, 0x01}, + }; + + reg_w_buf(gspca_dev, reg_init_1, ARRAY_SIZE(reg_init_1)); + reg_w_buf(gspca_dev, tp6810_cx_init_common, + ARRAY_SIZE(tp6810_cx_init_common)); + reg_w_buf(gspca_dev, reg_init_2, ARRAY_SIZE(reg_init_2)); + + reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x20); /* cx0342 I2C addr */ + i2c_w_buf(gspca_dev, sensor_init, ARRAY_SIZE(sensor_init)); + i2c_w_buf(gspca_dev, cx0342_timing_seq, ARRAY_SIZE(cx0342_timing_seq)); +} + +static void soi763a_6810_init(struct gspca_dev *gspca_dev) +{ + static const struct cmd reg_init_1[] = { + {TP6800_R2F_TIMING_CFG, 0x2f}, + {TP6800_R18_GPIO_DATA, 0xe1}, + {0x25, 0x02}, + {TP6800_R21_ENDP_1_CTL, 0x00}, + {TP6800_R3F_FRAME_RATE, 0x80}, + {TP6800_R2F_TIMING_CFG, 0x2f}, + {TP6800_R18_GPIO_DATA, 0xc1}, + }; + static const struct cmd reg_init_2[] = { + {TP6800_R78_FORMAT, 0x54}, + }; + static const struct cmd sensor_init[] = { + {0x00, 0x00}, + {0x01, 0x80}, + {0x02, 0x80}, + {0x03, 0x90}, + {0x04, 0x20}, + {0x05, 0x20}, + {0x06, 0x80}, + {0x07, 0x00}, + {0x08, 0xff}, + {0x09, 0xff}, + {0x0a, 0x76}, /* 7630 = soi673a */ + {0x0b, 0x30}, + {0x0c, 0x20}, + {0x0d, 0x20}, + {0x0e, 0xff}, + {0x0f, 0xff}, + {0x10, 0x41}, + {0x15, 0x14}, + {0x11, 0x40}, + {0x12, 0x48}, + {0x13, 0x80}, + {0x14, 0x80}, + {0x16, 0x03}, + {0x28, 0xb0}, + {0x71, 0x20}, + {0x75, 0x8e}, + {0x17, 0x1b}, + {0x18, 0xbd}, + {0x19, 0x05}, + {0x1a, 0xf6}, + {0x1b, 0x04}, + {0x1c, 0x7f}, /* omnivision */ + {0x1d, 0xa2}, + {0x1e, 0x00}, + {0x1f, 0x00}, + {0x20, 0x45}, + {0x21, 0x80}, + {0x22, 0x80}, + {0x23, 0xee}, + {0x24, 0x50}, + {0x25, 0x7a}, + {0x26, 0xa0}, + {0x27, 0x9a}, + {0x29, 0x30}, + {0x2a, 0x80}, + {0x2b, 0x00}, + {0x2c, 0xac}, + {0x2d, 0x05}, + {0x2e, 0x80}, + {0x2f, 0x3c}, + {0x30, 0x22}, + {0x31, 0x00}, + {0x32, 0x86}, + {0x33, 0x08}, + {0x34, 0xff}, + {0x35, 0xff}, + {0x36, 0xff}, + {0x37, 0xff}, + {0x38, 0xff}, + {0x39, 0xff}, + {0x3a, 0xfe}, + {0x3b, 0xfe}, + {0x3c, 0xfe}, + {0x3d, 0xfe}, + {0x3e, 0xfe}, + {0x3f, 0x71}, + {0x40, 0xff}, + {0x41, 0xff}, + {0x42, 0xff}, + {0x43, 0xff}, + {0x44, 0xff}, + {0x45, 0xff}, + {0x46, 0xff}, + {0x47, 0xff}, + {0x48, 0xff}, + {0x49, 0xff}, + {0x4a, 0xfe}, + {0x4b, 0xff}, + {0x4c, 0x00}, + {0x4d, 0x00}, + {0x4e, 0xff}, + {0x4f, 0xff}, + {0x50, 0xff}, + {0x51, 0xff}, + {0x52, 0xff}, + {0x53, 0xff}, + {0x54, 0xff}, + {0x55, 0xff}, + {0x56, 0xff}, + {0x57, 0xff}, + {0x58, 0xff}, + {0x59, 0xff}, + {0x5a, 0xff}, + {0x5b, 0xfe}, + {0x5c, 0xff}, + {0x5d, 0x8f}, + {0x5e, 0xff}, + {0x5f, 0x8f}, + {0x60, 0xa2}, + {0x61, 0x4a}, + {0x62, 0xf3}, + {0x63, 0x75}, + {0x64, 0xf0}, + {0x65, 0x00}, + {0x66, 0x55}, + {0x67, 0x92}, + {0x68, 0xa0}, + {0x69, 0x4a}, + {0x6a, 0x22}, + {0x6b, 0x00}, + {0x6c, 0x33}, + {0x6d, 0x44}, + {0x6e, 0x22}, + {0x6f, 0x84}, + {0x70, 0x0b}, + {0x72, 0x10}, + {0x73, 0x50}, + {0x74, 0x21}, + {0x76, 0x00}, + {0x77, 0xa5}, + {0x78, 0x80}, + {0x79, 0x80}, + {0x7a, 0x80}, + {0x7b, 0xe2}, + {0x7c, 0x00}, + {0x7d, 0xf7}, + {0x7e, 0x00}, + {0x7f, 0x00}, + }; + + reg_w_buf(gspca_dev, reg_init_1, ARRAY_SIZE(reg_init_1)); + reg_w_buf(gspca_dev, tp6810_ov_init_common, + ARRAY_SIZE(tp6810_ov_init_common)); + reg_w_buf(gspca_dev, reg_init_2, ARRAY_SIZE(reg_init_2)); + + i2c_w(gspca_dev, 0x12, 0x80); /* sensor reset */ + msleep(10); + i2c_w_buf(gspca_dev, sensor_init, ARRAY_SIZE(sensor_init)); +} + +/* set the gain and exposure */ +static void setexposure(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->sensor == SENSOR_CX0342) { + int expo; + + expo = (sd->ctrls[EXPOSURE].val << 2) - 1; + i2c_w(gspca_dev, CX0342_EXPO_LINE_L, expo); + i2c_w(gspca_dev, CX0342_EXPO_LINE_H, expo >> 8); + if (sd->bridge == BRIDGE_TP6800) + i2c_w(gspca_dev, CX0342_RAW_GBGAIN_H, + sd->ctrls[GAIN].val >> 8); + i2c_w(gspca_dev, CX0342_RAW_GBGAIN_L, sd->ctrls[GAIN].val); + if (sd->bridge == BRIDGE_TP6800) + i2c_w(gspca_dev, CX0342_RAW_GRGAIN_H, + sd->ctrls[GAIN].val >> 8); + i2c_w(gspca_dev, CX0342_RAW_GRGAIN_L, sd->ctrls[GAIN].val); + if (sd->bridge == BRIDGE_TP6800) + i2c_w(gspca_dev, CX0342_RAW_BGAIN_H, + sd->ctrls[BGAIN].val >> 8); + i2c_w(gspca_dev, CX0342_RAW_BGAIN_L, sd->ctrls[BGAIN].val); + if (sd->bridge == BRIDGE_TP6800) + i2c_w(gspca_dev, CX0342_RAW_RGAIN_H, + sd->ctrls[RGAIN].val >> 8); + i2c_w(gspca_dev, CX0342_RAW_RGAIN_L, sd->ctrls[RGAIN].val); + i2c_w(gspca_dev, CX0342_SYS_CTRL_0, + sd->bridge == BRIDGE_TP6800 ? 0x80 : 0x81); + return; + } + + /* soi763a */ + i2c_w(gspca_dev, 0x10, /* AEC_H (exposure time) */ + sd->ctrls[EXPOSURE].val); +/* i2c_w(gspca_dev, 0x76, 0x02); * AEC_L ([1:0] */ + i2c_w(gspca_dev, 0x00, /* gain */ + sd->ctrls[GAIN].val); +} + +/* set the JPEG quantization tables */ +static void set_dqt(struct gspca_dev *gspca_dev, u8 q) +{ + struct sd *sd = (struct sd *) gspca_dev; + + /* update the jpeg quantization tables */ + PDEBUG(D_STREAM, "q %d -> %d", sd->quality, q); + sd->quality = q; + if (q > 16) + q = 16; + if (sd->sensor == SENSOR_SOI763A) + jpeg_set_qual(sd->jpeg_hdr, jpeg_q[q]); + else + memcpy(&sd->jpeg_hdr[JPEG_QT0_OFFSET - 1], + DQT[q], sizeof DQT[0]); +} + +/* set the JPEG compression quality factor */ +static void setquality(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u16 q; + + q = sd->ctrls[QUALITY].val; + if (q != 16) + q = 15 - q; + + reg_w(gspca_dev, TP6800_R7A_BLK_THRLD, 0x00); + reg_w(gspca_dev, TP6800_R79_QUALITY, 0x04); + reg_w(gspca_dev, TP6800_R79_QUALITY, q); + + /* auto quality */ + if (q == 15 && sd->bridge == BRIDGE_TP6810) { + msleep(4); + reg_w(gspca_dev, TP6800_R7A_BLK_THRLD, 0x19); + } +} + +static const u8 color_null[18] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +static const u8 color_gain[NSENSORS][18] = { +[SENSOR_CX0342] = + {0x4c, 0x00, 0xa9, 0x00, 0x31, 0x00, /* Y R/G/B (LE values) */ + 0xb6, 0x03, 0x6c, 0x03, 0xe0, 0x00, /* U R/G/B */ + 0xdf, 0x00, 0x46, 0x03, 0xdc, 0x03}, /* V R/G/B */ +[SENSOR_SOI763A] = + {0x4c, 0x00, 0x95, 0x00, 0x1d, 0x00, /* Y R/G/B (LE values) */ + 0xb6, 0x03, 0x6c, 0x03, 0xd7, 0x00, /* U R/G/B */ + 0xd5, 0x00, 0x46, 0x03, 0xdc, 0x03}, /* V R/G/B */ +}; + +static void setgamma(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int gamma; +#define NGAMMA 6 + static const u8 gamma_tb[NGAMMA][3][1024] = { + { /* gamma 0 - from tp6800 + soi763a */ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x03, 0x05, 0x07, 0x07, 0x08, 0x09, 0x09, + 0x0a, 0x0c, 0x0c, 0x0d, 0x0e, 0x0e, 0x10, 0x11, + 0x11, 0x12, 0x14, 0x14, 0x15, 0x16, 0x16, 0x17, + 0x17, 0x18, 0x1a, 0x1a, 0x1b, 0x1b, 0x1c, 0x1e, + 0x1e, 0x1f, 0x1f, 0x20, 0x20, 0x22, 0x23, 0x23, + 0x25, 0x25, 0x26, 0x26, 0x27, 0x27, 0x28, 0x28, + 0x29, 0x29, 0x2b, 0x2c, 0x2c, 0x2d, 0x2d, 0x2f, + 0x2f, 0x30, 0x30, 0x31, 0x31, 0x33, 0x33, 0x34, + 0x34, 0x34, 0x35, 0x35, 0x37, 0x37, 0x38, 0x38, + 0x39, 0x39, 0x3a, 0x3a, 0x3b, 0x3b, 0x3b, 0x3c, + 0x3c, 0x3d, 0x3d, 0x3f, 0x3f, 0x40, 0x40, 0x40, + 0x42, 0x42, 0x43, 0x43, 0x44, 0x44, 0x44, 0x45, + 0x45, 0x47, 0x47, 0x47, 0x48, 0x48, 0x49, 0x49, + 0x4a, 0x4a, 0x4a, 0x4b, 0x4b, 0x4b, 0x4c, 0x4c, + 0x4d, 0x4d, 0x4d, 0x4f, 0x4f, 0x50, 0x50, 0x50, + 0x52, 0x52, 0x52, 0x53, 0x53, 0x54, 0x54, 0x54, + 0x55, 0x55, 0x55, 0x56, 0x56, 0x58, 0x58, 0x58, + 0x59, 0x59, 0x59, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b, + 0x5b, 0x5c, 0x5c, 0x5c, 0x5e, 0x5e, 0x5e, 0x5f, + 0x5f, 0x5f, 0x60, 0x60, 0x60, 0x61, 0x61, 0x61, + 0x62, 0x62, 0x62, 0x63, 0x63, 0x63, 0x65, 0x65, + 0x65, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67, 0x68, + 0x68, 0x68, 0x69, 0x69, 0x69, 0x69, 0x6a, 0x6a, + 0x6a, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6e, + 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70, + 0x70, 0x71, 0x71, 0x71, 0x71, 0x73, 0x73, 0x73, + 0x74, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x77, + 0x77, 0x77, 0x77, 0x78, 0x78, 0x78, 0x79, 0x79, + 0x79, 0x79, 0x7a, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, + 0x7b, 0x7c, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d, + 0x7d, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80, 0x80, + 0x81, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x82, + 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, 0x86, + 0x86, 0x86, 0x86, 0x88, 0x88, 0x88, 0x88, 0x89, + 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8a, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8d, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f, 0x8f, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x91, 0x91, 0x91, 0x91, + 0x92, 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x93, + 0x94, 0x94, 0x94, 0x94, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x97, 0x97, 0x97, 0x97, 0x98, 0x98, 0x98, + 0x98, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xac, 0xad, + 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, + 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2, + 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3, + 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, + 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb8, + 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9, + 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba, 0xbc, 0xbc, + 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, + 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, + 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, + 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3, + 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5, + 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, 0xc9, 0xc9, + 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca, 0xca, + 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, + 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xce, + 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf, + 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, + 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3, + 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, + 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, + 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, + 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb, + 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdf, + 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, + 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, + 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, + 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, + 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, + 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, + 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb, + 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, + 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, + 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, + 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, + 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, + 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, + 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, + 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x03, 0x05, 0x07, 0x07, 0x08, 0x09, 0x09, + 0x0a, 0x0c, 0x0c, 0x0d, 0x0e, 0x0e, 0x10, 0x11, + 0x11, 0x12, 0x14, 0x14, 0x15, 0x16, 0x16, 0x17, + 0x17, 0x18, 0x1a, 0x1a, 0x1b, 0x1b, 0x1c, 0x1e, + 0x1e, 0x1f, 0x1f, 0x20, 0x20, 0x22, 0x23, 0x23, + 0x25, 0x25, 0x26, 0x26, 0x27, 0x27, 0x28, 0x28, + 0x29, 0x29, 0x2b, 0x2c, 0x2c, 0x2d, 0x2d, 0x2f, + 0x2f, 0x30, 0x30, 0x31, 0x31, 0x33, 0x33, 0x34, + 0x34, 0x34, 0x35, 0x35, 0x37, 0x37, 0x38, 0x38, + 0x39, 0x39, 0x3a, 0x3a, 0x3b, 0x3b, 0x3b, 0x3c, + 0x3c, 0x3d, 0x3d, 0x3f, 0x3f, 0x40, 0x40, 0x40, + 0x42, 0x42, 0x43, 0x43, 0x44, 0x44, 0x44, 0x45, + 0x45, 0x47, 0x47, 0x47, 0x48, 0x48, 0x49, 0x49, + 0x4a, 0x4a, 0x4a, 0x4b, 0x4b, 0x4b, 0x4c, 0x4c, + 0x4d, 0x4d, 0x4d, 0x4f, 0x4f, 0x50, 0x50, 0x50, + 0x52, 0x52, 0x52, 0x53, 0x53, 0x54, 0x54, 0x54, + 0x55, 0x55, 0x55, 0x56, 0x56, 0x58, 0x58, 0x58, + 0x59, 0x59, 0x59, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b, + 0x5b, 0x5c, 0x5c, 0x5c, 0x5e, 0x5e, 0x5e, 0x5f, + 0x5f, 0x5f, 0x60, 0x60, 0x60, 0x61, 0x61, 0x61, + 0x62, 0x62, 0x62, 0x63, 0x63, 0x63, 0x65, 0x65, + 0x65, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67, 0x68, + 0x68, 0x68, 0x69, 0x69, 0x69, 0x69, 0x6a, 0x6a, + 0x6a, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6e, + 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70, + 0x70, 0x71, 0x71, 0x71, 0x71, 0x73, 0x73, 0x73, + 0x74, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x77, + 0x77, 0x77, 0x77, 0x78, 0x78, 0x78, 0x79, 0x79, + 0x79, 0x79, 0x7a, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, + 0x7b, 0x7c, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d, + 0x7d, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80, 0x80, + 0x81, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x82, + 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, 0x86, + 0x86, 0x86, 0x86, 0x88, 0x88, 0x88, 0x88, 0x89, + 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8a, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8d, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f, 0x8f, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x91, 0x91, 0x91, 0x91, + 0x92, 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x93, + 0x94, 0x94, 0x94, 0x94, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x97, 0x97, 0x97, 0x97, 0x98, 0x98, 0x98, + 0x98, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xac, 0xad, + 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, + 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2, + 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3, + 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, + 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb8, + 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9, + 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba, 0xbc, 0xbc, + 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, + 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, + 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, + 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3, + 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5, + 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, 0xc9, 0xc9, + 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca, 0xca, + 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, + 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xce, + 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf, + 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, + 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3, + 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, + 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, + 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, + 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb, + 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdf, + 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, + 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, + 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, + 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, + 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, + 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, + 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb, + 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, + 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, + 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, + 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, + 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, + 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, + 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, + 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x03, 0x05, 0x07, 0x07, 0x08, 0x09, 0x09, + 0x0a, 0x0c, 0x0c, 0x0d, 0x0e, 0x0e, 0x10, 0x11, + 0x11, 0x12, 0x14, 0x14, 0x15, 0x16, 0x16, 0x17, + 0x17, 0x18, 0x1a, 0x1a, 0x1b, 0x1b, 0x1c, 0x1e, + 0x1e, 0x1f, 0x1f, 0x20, 0x20, 0x22, 0x23, 0x23, + 0x25, 0x25, 0x26, 0x26, 0x27, 0x27, 0x28, 0x28, + 0x29, 0x29, 0x2b, 0x2c, 0x2c, 0x2d, 0x2d, 0x2f, + 0x2f, 0x30, 0x30, 0x31, 0x31, 0x33, 0x33, 0x34, + 0x34, 0x34, 0x35, 0x35, 0x37, 0x37, 0x38, 0x38, + 0x39, 0x39, 0x3a, 0x3a, 0x3b, 0x3b, 0x3b, 0x3c, + 0x3c, 0x3d, 0x3d, 0x3f, 0x3f, 0x40, 0x40, 0x40, + 0x42, 0x42, 0x43, 0x43, 0x44, 0x44, 0x44, 0x45, + 0x45, 0x47, 0x47, 0x47, 0x48, 0x48, 0x49, 0x49, + 0x4a, 0x4a, 0x4a, 0x4b, 0x4b, 0x4b, 0x4c, 0x4c, + 0x4d, 0x4d, 0x4d, 0x4f, 0x4f, 0x50, 0x50, 0x50, + 0x52, 0x52, 0x52, 0x53, 0x53, 0x54, 0x54, 0x54, + 0x55, 0x55, 0x55, 0x56, 0x56, 0x58, 0x58, 0x58, + 0x59, 0x59, 0x59, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b, + 0x5b, 0x5c, 0x5c, 0x5c, 0x5e, 0x5e, 0x5e, 0x5f, + 0x5f, 0x5f, 0x60, 0x60, 0x60, 0x61, 0x61, 0x61, + 0x62, 0x62, 0x62, 0x63, 0x63, 0x63, 0x65, 0x65, + 0x65, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67, 0x68, + 0x68, 0x68, 0x69, 0x69, 0x69, 0x69, 0x6a, 0x6a, + 0x6a, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6e, + 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70, + 0x70, 0x71, 0x71, 0x71, 0x71, 0x73, 0x73, 0x73, + 0x74, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x76, + 0x77, 0x77, 0x77, 0x78, 0x78, 0x78, 0x79, 0x79, + 0x79, 0x79, 0x7a, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, + 0x7b, 0x7c, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d, + 0x7d, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80, 0x80, + 0x81, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x82, + 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, 0x86, + 0x86, 0x86, 0x86, 0x88, 0x88, 0x88, 0x88, 0x89, + 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8a, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8d, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f, 0x8f, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x91, 0x91, 0x91, 0x91, + 0x92, 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x93, + 0x94, 0x94, 0x94, 0x94, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x97, 0x97, 0x97, 0x97, 0x98, 0x98, 0x98, + 0x98, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xac, 0xad, + 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, + 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2, + 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3, + 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, + 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb8, + 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9, + 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba, 0xbc, 0xbc, + 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, + 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, + 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, + 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3, + 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5, + 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, 0xc9, 0xc9, + 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca, 0xca, + 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, + 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xce, + 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf, + 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, + 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3, + 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, + 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, + 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, + 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb, + 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdf, + 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, + 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, + 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, + 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, + 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, + 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, + 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb, + 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, + 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, + 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, + 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, + 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, + 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, + 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, + 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb} + }, + { /* gamma 1 - from tp6810 + soi763a */ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x03, 0x05, 0x07, 0x08, 0x09, 0x0a, + 0x0c, 0x0d, 0x0e, 0x10, 0x11, 0x12, 0x14, 0x15, + 0x16, 0x17, 0x18, 0x1a, 0x1a, 0x1b, 0x1c, 0x1e, + 0x1f, 0x20, 0x22, 0x22, 0x23, 0x25, 0x26, 0x27, + 0x27, 0x28, 0x29, 0x2b, 0x2b, 0x2c, 0x2d, 0x2f, + 0x2f, 0x30, 0x31, 0x33, 0x33, 0x34, 0x35, 0x35, + 0x37, 0x38, 0x38, 0x39, 0x3a, 0x3a, 0x3b, 0x3c, + 0x3c, 0x3d, 0x3f, 0x3f, 0x40, 0x42, 0x42, 0x43, + 0x43, 0x44, 0x45, 0x45, 0x47, 0x47, 0x48, 0x49, + 0x49, 0x4a, 0x4a, 0x4b, 0x4b, 0x4c, 0x4d, 0x4d, + 0x4f, 0x4f, 0x50, 0x50, 0x52, 0x52, 0x53, 0x53, + 0x54, 0x54, 0x55, 0x56, 0x56, 0x58, 0x58, 0x59, + 0x59, 0x5a, 0x5a, 0x5b, 0x5b, 0x5c, 0x5c, 0x5e, + 0x5e, 0x5e, 0x5f, 0x5f, 0x60, 0x60, 0x61, 0x61, + 0x62, 0x62, 0x63, 0x63, 0x65, 0x65, 0x65, 0x66, + 0x66, 0x67, 0x67, 0x68, 0x68, 0x69, 0x69, 0x69, + 0x6a, 0x6a, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6e, + 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70, 0x71, 0x71, + 0x73, 0x73, 0x73, 0x74, 0x74, 0x74, 0x75, 0x75, + 0x77, 0x77, 0x77, 0x78, 0x78, 0x79, 0x79, 0x79, + 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7c, 0x7c, 0x7c, + 0x7d, 0x7d, 0x7d, 0x7f, 0x7f, 0x80, 0x80, 0x80, + 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x84, 0x84, + 0x84, 0x85, 0x85, 0x85, 0x86, 0x86, 0x86, 0x88, + 0x88, 0x88, 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a, + 0x8b, 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, + 0x8e, 0x8f, 0x8f, 0x8f, 0x90, 0x90, 0x90, 0x91, + 0x91, 0x91, 0x92, 0x92, 0x92, 0x92, 0x93, 0x93, + 0x93, 0x94, 0x94, 0x94, 0x96, 0x96, 0x96, 0x97, + 0x97, 0x97, 0x97, 0x98, 0x98, 0x98, 0x99, 0x99, + 0x99, 0x9a, 0x9a, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9e, + 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, + 0xa3, 0xa4, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab, 0xab, + 0xac, 0xac, 0xac, 0xad, 0xad, 0xad, 0xad, 0xae, + 0xae, 0xae, 0xae, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, + 0xb0, 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2, + 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3, 0xb4, + 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb6, 0xb7, + 0xb7, 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb8, + 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xba, + 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, + 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, + 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0xc2, + 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, + 0xc4, 0xc4, 0xc4, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, + 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, + 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc, + 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, 0xce, + 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf, + 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, + 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, + 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, + 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, + 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xda, + 0xda, 0xda, 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, + 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, + 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, + 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, + 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, + 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, + 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, + 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, + 0xe9, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, + 0xec, 0xec, 0xec, 0xed, 0xed, 0xed, 0xed, 0xed, + 0xee, 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef, + 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, 0xf3, 0xf3, + 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, + 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, + 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, + 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, + 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, + 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, + 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, + 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, + 0x05, 0x07, 0x07, 0x08, 0x09, 0x0a, 0x0c, 0x0d, + 0x0e, 0x10, 0x10, 0x11, 0x12, 0x14, 0x15, 0x15, + 0x16, 0x17, 0x18, 0x1a, 0x1a, 0x1b, 0x1c, 0x1e, + 0x1e, 0x1f, 0x20, 0x20, 0x22, 0x23, 0x25, 0x25, + 0x26, 0x27, 0x27, 0x28, 0x29, 0x29, 0x2b, 0x2c, + 0x2c, 0x2d, 0x2d, 0x2f, 0x30, 0x30, 0x31, 0x31, + 0x33, 0x34, 0x34, 0x35, 0x35, 0x37, 0x38, 0x38, + 0x39, 0x39, 0x3a, 0x3a, 0x3b, 0x3b, 0x3c, 0x3d, + 0x3d, 0x3f, 0x3f, 0x40, 0x40, 0x42, 0x42, 0x43, + 0x43, 0x44, 0x44, 0x45, 0x45, 0x47, 0x47, 0x48, + 0x48, 0x49, 0x49, 0x4a, 0x4a, 0x4b, 0x4b, 0x4c, + 0x4c, 0x4d, 0x4d, 0x4d, 0x4f, 0x4f, 0x50, 0x50, + 0x52, 0x52, 0x53, 0x53, 0x53, 0x54, 0x54, 0x55, + 0x55, 0x56, 0x56, 0x56, 0x58, 0x58, 0x59, 0x59, + 0x5a, 0x5a, 0x5a, 0x5b, 0x5b, 0x5c, 0x5c, 0x5c, + 0x5e, 0x5e, 0x5f, 0x5f, 0x5f, 0x60, 0x60, 0x60, + 0x61, 0x61, 0x62, 0x62, 0x62, 0x63, 0x63, 0x65, + 0x65, 0x65, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67, + 0x68, 0x68, 0x69, 0x69, 0x69, 0x6a, 0x6a, 0x6a, + 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6e, 0x6e, + 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70, 0x70, 0x71, + 0x71, 0x71, 0x73, 0x73, 0x73, 0x74, 0x74, 0x74, + 0x75, 0x75, 0x75, 0x77, 0x77, 0x77, 0x78, 0x78, + 0x78, 0x79, 0x79, 0x79, 0x79, 0x7a, 0x7a, 0x7a, + 0x7b, 0x7b, 0x7b, 0x7c, 0x7c, 0x7c, 0x7c, 0x7d, + 0x7d, 0x7d, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80, + 0x80, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x82, + 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, 0x86, + 0x86, 0x86, 0x88, 0x88, 0x88, 0x88, 0x89, 0x89, + 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8d, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8f, 0x8f, 0x8f, 0x90, 0x90, 0x90, 0x90, + 0x91, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92, 0x92, + 0x93, 0x93, 0x93, 0x93, 0x94, 0x94, 0x94, 0x94, + 0x96, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97, 0x97, + 0x98, 0x98, 0x98, 0x98, 0x99, 0x99, 0x99, 0x99, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9c, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab, + 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xac, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xae, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, + 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2, + 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3, + 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, + 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb8, + 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9, + 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba, 0xbc, 0xbc, + 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, + 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, + 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, + 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, + 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, + 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, + 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, + 0xca, 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce, 0xce, 0xcf, + 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0, + 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, + 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, + 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, + 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, + 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, + 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb, 0xdb, + 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdf, + 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, + 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, + 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, + 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, + 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, + 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, + 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, + 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, + 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed, + 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, + 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, + 0xf1, 0xf1, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, + 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, + 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, + 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, + 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, + 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, + 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, + 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, + 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, + 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x02, 0x03, 0x05, 0x05, 0x07, + 0x08, 0x09, 0x0a, 0x0a, 0x0c, 0x0d, 0x0e, 0x0e, + 0x10, 0x11, 0x12, 0x12, 0x14, 0x15, 0x16, 0x16, + 0x17, 0x18, 0x18, 0x1a, 0x1b, 0x1b, 0x1c, 0x1e, + 0x1e, 0x1f, 0x1f, 0x20, 0x22, 0x22, 0x23, 0x23, + 0x25, 0x26, 0x26, 0x27, 0x27, 0x28, 0x29, 0x29, + 0x2b, 0x2b, 0x2c, 0x2c, 0x2d, 0x2d, 0x2f, 0x30, + 0x30, 0x31, 0x31, 0x33, 0x33, 0x34, 0x34, 0x35, + 0x35, 0x37, 0x37, 0x38, 0x38, 0x39, 0x39, 0x3a, + 0x3a, 0x3b, 0x3b, 0x3b, 0x3c, 0x3c, 0x3d, 0x3d, + 0x3f, 0x3f, 0x40, 0x40, 0x42, 0x42, 0x42, 0x43, + 0x43, 0x44, 0x44, 0x45, 0x45, 0x47, 0x47, 0x47, + 0x48, 0x48, 0x49, 0x49, 0x49, 0x4a, 0x4a, 0x4b, + 0x4b, 0x4b, 0x4c, 0x4c, 0x4d, 0x4d, 0x4d, 0x4f, + 0x4f, 0x50, 0x50, 0x50, 0x52, 0x52, 0x52, 0x53, + 0x53, 0x54, 0x54, 0x54, 0x55, 0x55, 0x55, 0x56, + 0x56, 0x58, 0x58, 0x58, 0x59, 0x59, 0x59, 0x5a, + 0x5a, 0x5a, 0x5b, 0x5b, 0x5b, 0x5c, 0x5c, 0x5c, + 0x5e, 0x5e, 0x5e, 0x5f, 0x5f, 0x5f, 0x60, 0x60, + 0x60, 0x61, 0x61, 0x61, 0x62, 0x62, 0x62, 0x63, + 0x63, 0x63, 0x65, 0x65, 0x65, 0x66, 0x66, 0x66, + 0x66, 0x67, 0x67, 0x67, 0x68, 0x68, 0x68, 0x69, + 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6a, 0x6c, 0x6c, + 0x6c, 0x6d, 0x6d, 0x6d, 0x6d, 0x6e, 0x6e, 0x6e, + 0x6f, 0x6f, 0x6f, 0x6f, 0x70, 0x70, 0x70, 0x71, + 0x71, 0x71, 0x71, 0x73, 0x73, 0x73, 0x74, 0x74, + 0x74, 0x74, 0x75, 0x75, 0x75, 0x75, 0x77, 0x77, + 0x77, 0x78, 0x78, 0x78, 0x78, 0x79, 0x79, 0x79, + 0x79, 0x7a, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7b, + 0x7b, 0x7c, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d, + 0x7d, 0x7f, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80, + 0x80, 0x81, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, + 0x82, 0x84, 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, + 0x85, 0x86, 0x86, 0x86, 0x86, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x89, 0x89, 0x89, 0x89, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8f, 0x8f, 0x8f, 0x8f, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x91, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x93, 0x93, 0x93, 0x93, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x98, 0x98, 0x98, + 0x98, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1, + 0xb1, 0xb1, 0xb1, 0xb1, 0xb2, 0xb2, 0xb2, 0xb2, + 0xb2, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb4, + 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb6, + 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, + 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, + 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba, + 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, + 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, + 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0xc2, + 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, + 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, + 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, + 0xc7, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, + 0xca, 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb, 0xcb, + 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce, + 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf, + 0xcf, 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, + 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, + 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, + 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd7, + 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, + 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, + 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb, + 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, + 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, + 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, + 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, + 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, + 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, + 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, + 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, + 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, + 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, + 0xec, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xee, + 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef, 0xef, + 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf1, + 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, 0xf3, 0xf3, 0xf3, + 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, + 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, + 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, + 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, + 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, + 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, + 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, + 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} + }, + { /* gamma 2 */ + {0x00, 0x01, 0x02, 0x05, 0x07, 0x08, 0x0a, 0x0c, + 0x0d, 0x0e, 0x10, 0x12, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x1a, 0x1b, 0x1c, 0x1e, 0x1f, 0x20, 0x22, + 0x23, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2b, 0x2c, + 0x2d, 0x2d, 0x2f, 0x30, 0x31, 0x33, 0x34, 0x34, + 0x35, 0x37, 0x38, 0x38, 0x39, 0x3a, 0x3b, 0x3b, + 0x3c, 0x3d, 0x3f, 0x3f, 0x40, 0x42, 0x42, 0x43, + 0x44, 0x44, 0x45, 0x47, 0x47, 0x48, 0x49, 0x49, + 0x4a, 0x4b, 0x4b, 0x4c, 0x4c, 0x4d, 0x4f, 0x4f, + 0x50, 0x50, 0x52, 0x53, 0x53, 0x54, 0x54, 0x55, + 0x55, 0x56, 0x56, 0x58, 0x58, 0x59, 0x5a, 0x5a, + 0x5b, 0x5b, 0x5c, 0x5c, 0x5e, 0x5e, 0x5f, 0x5f, + 0x60, 0x60, 0x61, 0x61, 0x62, 0x62, 0x63, 0x63, + 0x65, 0x65, 0x65, 0x66, 0x66, 0x67, 0x67, 0x68, + 0x68, 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6c, 0x6c, + 0x6d, 0x6d, 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x70, + 0x70, 0x70, 0x71, 0x71, 0x73, 0x73, 0x73, 0x74, + 0x74, 0x75, 0x75, 0x75, 0x77, 0x77, 0x78, 0x78, + 0x78, 0x79, 0x79, 0x79, 0x7a, 0x7a, 0x7b, 0x7b, + 0x7b, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7f, 0x7f, + 0x7f, 0x80, 0x80, 0x80, 0x81, 0x81, 0x81, 0x82, + 0x82, 0x82, 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, + 0x86, 0x86, 0x86, 0x88, 0x88, 0x88, 0x89, 0x89, + 0x89, 0x8a, 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, + 0x8f, 0x90, 0x90, 0x90, 0x91, 0x91, 0x91, 0x91, + 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x93, 0x94, + 0x94, 0x94, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97, + 0x97, 0x98, 0x98, 0x98, 0x98, 0x99, 0x99, 0x99, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e, + 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa0, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa2, 0xa3, + 0xa3, 0xa3, 0xa4, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, + 0xab, 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xae, + 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb0, + 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2, 0xb2, 0xb2, + 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3, 0xb4, 0xb4, + 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb6, 0xb7, + 0xb7, 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb8, + 0xb8, 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, + 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, + 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, + 0xbe, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, + 0xc0, 0xc0, 0xc0, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, + 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, + 0xc4, 0xc4, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, + 0xc7, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, + 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, + 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce, 0xce, + 0xce, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, + 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, 0xd1, + 0xd1, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, + 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, + 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, + 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, + 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, + 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde, + 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, + 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, + 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, + 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, + 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, + 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, + 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, + 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, + 0xe9, 0xe9, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, + 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed, 0xed, + 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, 0xee, + 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, + 0xf1, 0xf1, 0xf1, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, + 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, + 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, + 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, + 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, + 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, + 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb}, + {0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x05, + 0x07, 0x08, 0x09, 0x0a, 0x0d, 0x0e, 0x10, 0x11, + 0x12, 0x14, 0x15, 0x16, 0x16, 0x17, 0x18, 0x1a, + 0x1b, 0x1c, 0x1e, 0x1f, 0x20, 0x20, 0x22, 0x23, + 0x25, 0x26, 0x26, 0x27, 0x28, 0x29, 0x29, 0x2b, + 0x2c, 0x2d, 0x2d, 0x2f, 0x30, 0x30, 0x31, 0x33, + 0x33, 0x34, 0x35, 0x35, 0x37, 0x38, 0x38, 0x39, + 0x3a, 0x3a, 0x3b, 0x3b, 0x3c, 0x3d, 0x3d, 0x3f, + 0x3f, 0x40, 0x42, 0x42, 0x43, 0x43, 0x44, 0x44, + 0x45, 0x45, 0x47, 0x47, 0x48, 0x48, 0x49, 0x4a, + 0x4a, 0x4b, 0x4b, 0x4c, 0x4c, 0x4d, 0x4d, 0x4d, + 0x4f, 0x4f, 0x50, 0x50, 0x52, 0x52, 0x53, 0x53, + 0x54, 0x54, 0x55, 0x55, 0x56, 0x56, 0x56, 0x58, + 0x58, 0x59, 0x59, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b, + 0x5c, 0x5c, 0x5c, 0x5e, 0x5e, 0x5f, 0x5f, 0x5f, + 0x60, 0x60, 0x61, 0x61, 0x61, 0x62, 0x62, 0x63, + 0x63, 0x63, 0x65, 0x65, 0x65, 0x66, 0x66, 0x67, + 0x67, 0x67, 0x68, 0x68, 0x68, 0x69, 0x69, 0x69, + 0x6a, 0x6a, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, + 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70, + 0x70, 0x71, 0x71, 0x71, 0x73, 0x73, 0x73, 0x73, + 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x77, 0x77, + 0x77, 0x78, 0x78, 0x78, 0x79, 0x79, 0x79, 0x79, + 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7b, 0x7b, 0x7c, + 0x7c, 0x7c, 0x7d, 0x7d, 0x7d, 0x7d, 0x7f, 0x7f, + 0x7f, 0x80, 0x80, 0x80, 0x80, 0x81, 0x81, 0x81, + 0x82, 0x82, 0x82, 0x82, 0x84, 0x84, 0x84, 0x84, + 0x85, 0x85, 0x85, 0x85, 0x86, 0x86, 0x86, 0x88, + 0x88, 0x88, 0x88, 0x89, 0x89, 0x89, 0x89, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, 0x8b, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8e, 0x8f, + 0x8f, 0x8f, 0x8f, 0x90, 0x90, 0x90, 0x90, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92, 0x92, + 0x93, 0x93, 0x93, 0x93, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x96, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97, + 0x97, 0x98, 0x98, 0x98, 0x98, 0x98, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x9a, 0x9a, 0x9a, 0x9a, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xad, 0xad, 0xad, 0xad, 0xad, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb1, + 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2, 0xb2, 0xb2, + 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, + 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, + 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, + 0xb7, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, + 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xba, + 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, + 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, + 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, 0xbf, + 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, + 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, + 0xc3, 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, + 0xc4, 0xc4, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, + 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, 0xc9, 0xc9, + 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca, 0xca, + 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce, + 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, + 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, + 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, + 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, + 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, + 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, + 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, + 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, + 0xda, 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, + 0xdb, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdf, 0xdf, + 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, + 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, + 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, + 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4, + 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, + 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, + 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, + 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, + 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb, + 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, + 0xec, 0xec, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, + 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xef, + 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, + 0xf1, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, + 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, + 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, + 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, + 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, + 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, + 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb}, + {0x00, 0x00, 0x00, 0x01, 0x02, 0x05, 0x07, 0x08, + 0x09, 0x0a, 0x0c, 0x0e, 0x10, 0x11, 0x12, 0x14, + 0x15, 0x16, 0x17, 0x18, 0x1a, 0x1b, 0x1c, 0x1e, + 0x1f, 0x20, 0x20, 0x22, 0x23, 0x25, 0x26, 0x27, + 0x28, 0x28, 0x29, 0x2b, 0x2c, 0x2d, 0x2d, 0x2f, + 0x30, 0x31, 0x31, 0x33, 0x34, 0x35, 0x35, 0x37, + 0x38, 0x38, 0x39, 0x3a, 0x3a, 0x3b, 0x3c, 0x3c, + 0x3d, 0x3f, 0x3f, 0x40, 0x40, 0x42, 0x43, 0x43, + 0x44, 0x44, 0x45, 0x47, 0x47, 0x48, 0x48, 0x49, + 0x4a, 0x4a, 0x4b, 0x4b, 0x4c, 0x4c, 0x4d, 0x4d, + 0x4f, 0x4f, 0x50, 0x50, 0x52, 0x52, 0x53, 0x53, + 0x54, 0x54, 0x55, 0x55, 0x56, 0x56, 0x58, 0x58, + 0x59, 0x59, 0x5a, 0x5a, 0x5b, 0x5b, 0x5c, 0x5c, + 0x5c, 0x5e, 0x5e, 0x5f, 0x5f, 0x60, 0x60, 0x61, + 0x61, 0x61, 0x62, 0x62, 0x63, 0x63, 0x65, 0x65, + 0x65, 0x66, 0x66, 0x67, 0x67, 0x67, 0x68, 0x68, + 0x69, 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6c, 0x6c, + 0x6d, 0x6d, 0x6d, 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, + 0x70, 0x70, 0x70, 0x71, 0x71, 0x71, 0x73, 0x73, + 0x73, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x77, + 0x77, 0x78, 0x78, 0x78, 0x79, 0x79, 0x79, 0x7a, + 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7b, 0x7c, 0x7c, + 0x7c, 0x7d, 0x7d, 0x7d, 0x7f, 0x7f, 0x7f, 0x80, + 0x80, 0x80, 0x81, 0x81, 0x81, 0x81, 0x82, 0x82, + 0x82, 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, + 0x86, 0x86, 0x86, 0x88, 0x88, 0x88, 0x88, 0x89, + 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8a, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8f, 0x8f, 0x8f, 0x8f, 0x90, 0x90, 0x90, + 0x91, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92, 0x92, + 0x93, 0x93, 0x93, 0x93, 0x94, 0x94, 0x94, 0x94, + 0x96, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97, 0x97, + 0x98, 0x98, 0x98, 0x98, 0x99, 0x99, 0x99, 0x99, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9c, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, + 0xb0, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, + 0xb1, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, + 0xb3, 0xb3, 0xb3, 0xb4, 0xb3, 0xb4, 0xb4, 0xb4, + 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, + 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, + 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba, + 0xba, 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, + 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, + 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, + 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0xc2, + 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3, + 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, + 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, + 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, + 0xca, 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb, 0xcb, + 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce, + 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf, + 0xcf, 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, + 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, + 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, + 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, + 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, + 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, + 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, + 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, + 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, + 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, + 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, + 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, + 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, + 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, + 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, + 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, + 0xe8, 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, + 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, + 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed, 0xed, + 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, 0xee, + 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, + 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, 0xf3, 0xf3, 0xf3, + 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, + 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, + 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, + 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, + 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, + 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, + 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb} + }, + { /* gamma 3 - from tp6810 + cx0342 */ + {0x08, 0x09, 0x0c, 0x0d, 0x10, 0x11, 0x14, 0x15, + 0x17, 0x18, 0x1a, 0x1c, 0x1e, 0x1f, 0x20, 0x23, + 0x25, 0x26, 0x27, 0x28, 0x2b, 0x2c, 0x2d, 0x2f, + 0x30, 0x31, 0x33, 0x34, 0x35, 0x37, 0x38, 0x39, + 0x3a, 0x3b, 0x3c, 0x3d, 0x3f, 0x40, 0x42, 0x43, + 0x44, 0x45, 0x47, 0x48, 0x48, 0x49, 0x4a, 0x4b, + 0x4c, 0x4d, 0x4d, 0x4f, 0x50, 0x52, 0x53, 0x53, + 0x54, 0x55, 0x56, 0x56, 0x58, 0x59, 0x5a, 0x5a, + 0x5b, 0x5c, 0x5c, 0x5e, 0x5f, 0x5f, 0x60, 0x61, + 0x61, 0x62, 0x63, 0x63, 0x65, 0x66, 0x66, 0x67, + 0x68, 0x68, 0x69, 0x69, 0x6a, 0x6c, 0x6c, 0x6d, + 0x6d, 0x6e, 0x6f, 0x6f, 0x70, 0x70, 0x71, 0x73, + 0x73, 0x74, 0x74, 0x75, 0x75, 0x77, 0x77, 0x78, + 0x78, 0x79, 0x7a, 0x7a, 0x7b, 0x7b, 0x7c, 0x7c, + 0x7d, 0x7d, 0x7f, 0x7f, 0x80, 0x80, 0x81, 0x81, + 0x82, 0x82, 0x84, 0x84, 0x85, 0x85, 0x86, 0x86, + 0x86, 0x88, 0x88, 0x89, 0x89, 0x8a, 0x8a, 0x8b, + 0x8b, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8f, 0x8f, + 0x90, 0x90, 0x91, 0x91, 0x91, 0x92, 0x92, 0x93, + 0x93, 0x93, 0x94, 0x94, 0x96, 0x96, 0x97, 0x97, + 0x97, 0x98, 0x98, 0x99, 0x99, 0x99, 0x9a, 0x9a, + 0x9a, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, + 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, + 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, + 0xa4, 0xa5, 0xa5, 0xa5, 0xa6, 0xa6, 0xa8, 0xa8, + 0xa8, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab, 0xac, + 0xac, 0xac, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, + 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1, + 0xb1, 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, + 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, + 0xb7, 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, + 0xba, 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, + 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, + 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0xc2, 0xc2, + 0xc3, 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc5, + 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc7, + 0xc7, 0xc7, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, + 0xca, 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc, + 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, + 0xce, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0, + 0xd0, 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, + 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, + 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, + 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda, 0xda, + 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, + 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde, 0xdf, 0xdf, + 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, + 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, + 0xe3, 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, + 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, + 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, + 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, + 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, + 0xed, 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, + 0xee, 0xee, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, + 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, + 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, + 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, + 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, + 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, + 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, + 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, + 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0x03, 0x05, 0x07, 0x09, 0x0a, 0x0c, 0x0d, 0x10, + 0x11, 0x12, 0x14, 0x15, 0x17, 0x18, 0x1a, 0x1b, + 0x1c, 0x1e, 0x1f, 0x20, 0x22, 0x23, 0x25, 0x26, + 0x27, 0x28, 0x29, 0x2b, 0x2c, 0x2c, 0x2d, 0x2f, + 0x30, 0x31, 0x33, 0x33, 0x34, 0x35, 0x37, 0x38, + 0x38, 0x39, 0x3a, 0x3b, 0x3b, 0x3c, 0x3d, 0x3f, + 0x3f, 0x40, 0x42, 0x42, 0x43, 0x44, 0x45, 0x45, + 0x47, 0x47, 0x48, 0x49, 0x49, 0x4a, 0x4b, 0x4b, + 0x4c, 0x4d, 0x4d, 0x4f, 0x4f, 0x50, 0x52, 0x52, + 0x53, 0x53, 0x54, 0x54, 0x55, 0x55, 0x56, 0x58, + 0x58, 0x59, 0x59, 0x5a, 0x5a, 0x5b, 0x5b, 0x5c, + 0x5c, 0x5e, 0x5e, 0x5f, 0x5f, 0x60, 0x60, 0x61, + 0x61, 0x62, 0x62, 0x63, 0x63, 0x65, 0x65, 0x66, + 0x66, 0x67, 0x67, 0x67, 0x68, 0x68, 0x69, 0x69, + 0x6a, 0x6a, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6e, + 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70, 0x71, 0x71, + 0x71, 0x73, 0x73, 0x74, 0x74, 0x74, 0x75, 0x75, + 0x77, 0x77, 0x77, 0x78, 0x78, 0x79, 0x79, 0x79, + 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7b, 0x7c, 0x7c, + 0x7d, 0x7d, 0x7d, 0x7f, 0x7f, 0x7f, 0x80, 0x80, + 0x80, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x84, + 0x84, 0x84, 0x85, 0x85, 0x85, 0x86, 0x86, 0x86, + 0x88, 0x88, 0x88, 0x89, 0x89, 0x89, 0x8a, 0x8a, + 0x8a, 0x8b, 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f, 0x90, 0x90, + 0x90, 0x91, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92, + 0x93, 0x93, 0x93, 0x93, 0x94, 0x94, 0x94, 0x96, + 0x96, 0x96, 0x96, 0x97, 0x97, 0x97, 0x98, 0x98, + 0x98, 0x98, 0x99, 0x99, 0x99, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9d, 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, 0x9e, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab, + 0xab, 0xac, 0xac, 0xac, 0xac, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xae, 0xae, 0xae, 0xae, 0xaf, 0xaf, + 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb1, + 0xb1, 0xb1, 0xb1, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, + 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb4, 0xb4, 0xb4, + 0xb4, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, + 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, + 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xba, + 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, + 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, + 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, + 0xc0, 0xc0, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, + 0xc3, 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, + 0xc4, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, + 0xc7, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, + 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, + 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce, + 0xce, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, + 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, 0xd1, + 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, + 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, + 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, + 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, + 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, + 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, + 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, + 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, + 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, + 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, + 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, + 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, + 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, + 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, + 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb, + 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, + 0xec, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xee, + 0xee, 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef, + 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, + 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, + 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, + 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, + 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, + 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, + 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, + 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, + 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, + 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, + 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0x07, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, + 0x16, 0x17, 0x18, 0x1b, 0x1c, 0x1e, 0x1f, 0x20, + 0x23, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2b, 0x2d, + 0x2f, 0x30, 0x31, 0x33, 0x34, 0x35, 0x37, 0x38, + 0x39, 0x3a, 0x3b, 0x3b, 0x3c, 0x3d, 0x3f, 0x40, + 0x42, 0x43, 0x44, 0x44, 0x45, 0x47, 0x48, 0x49, + 0x4a, 0x4a, 0x4b, 0x4c, 0x4d, 0x4d, 0x4f, 0x50, + 0x52, 0x52, 0x53, 0x54, 0x55, 0x55, 0x56, 0x58, + 0x58, 0x59, 0x5a, 0x5b, 0x5b, 0x5c, 0x5e, 0x5e, + 0x5f, 0x5f, 0x60, 0x61, 0x61, 0x62, 0x63, 0x63, + 0x65, 0x65, 0x66, 0x67, 0x67, 0x68, 0x68, 0x69, + 0x6a, 0x6a, 0x6c, 0x6c, 0x6d, 0x6d, 0x6e, 0x6e, + 0x6f, 0x70, 0x70, 0x71, 0x71, 0x73, 0x73, 0x74, + 0x74, 0x75, 0x75, 0x77, 0x77, 0x78, 0x78, 0x79, + 0x79, 0x7a, 0x7a, 0x7b, 0x7b, 0x7c, 0x7c, 0x7d, + 0x7d, 0x7f, 0x7f, 0x80, 0x80, 0x81, 0x81, 0x81, + 0x82, 0x82, 0x84, 0x84, 0x85, 0x85, 0x86, 0x86, + 0x88, 0x88, 0x88, 0x89, 0x89, 0x8a, 0x8a, 0x8b, + 0x8b, 0x8b, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8f, + 0x8f, 0x90, 0x90, 0x90, 0x91, 0x91, 0x92, 0x92, + 0x92, 0x93, 0x93, 0x94, 0x94, 0x94, 0x96, 0x96, + 0x96, 0x97, 0x97, 0x98, 0x98, 0x98, 0x99, 0x99, + 0x99, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, + 0x9c, 0x9d, 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, 0xa0, + 0xa0, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa3, + 0xa3, 0xa3, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, + 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8, 0xa9, + 0xa9, 0xa9, 0xab, 0xab, 0xab, 0xac, 0xac, 0xac, + 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xaf, 0xaf, + 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, + 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb4, + 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, + 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, + 0xb9, 0xba, 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbc, + 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbf, + 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, + 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3, 0xc4, + 0xc4, 0xc4, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, 0xc9, + 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca, 0xcb, 0xcb, + 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce, 0xcf, + 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, + 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, + 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, + 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, + 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda, + 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, + 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde, + 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, + 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, + 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4, + 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, + 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, + 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, + 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb, 0xeb, + 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed, + 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, 0xee, + 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, + 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, + 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, + 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, + 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, + 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, + 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, + 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, + 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} + }, + { /* gamma 4 - from tp6800 + soi763a */ + {0x11, 0x14, 0x15, 0x17, 0x1a, 0x1b, 0x1e, 0x1f, + 0x22, 0x23, 0x25, 0x27, 0x28, 0x2b, 0x2c, 0x2d, + 0x2f, 0x31, 0x33, 0x34, 0x35, 0x38, 0x39, 0x3a, + 0x3b, 0x3c, 0x3d, 0x40, 0x42, 0x43, 0x44, 0x45, + 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4f, + 0x50, 0x52, 0x53, 0x53, 0x54, 0x55, 0x56, 0x58, + 0x59, 0x5a, 0x5b, 0x5b, 0x5c, 0x5e, 0x5f, 0x60, + 0x61, 0x61, 0x62, 0x63, 0x65, 0x65, 0x66, 0x67, + 0x68, 0x68, 0x69, 0x6a, 0x6c, 0x6c, 0x6d, 0x6e, + 0x6f, 0x6f, 0x70, 0x71, 0x71, 0x73, 0x74, 0x74, + 0x75, 0x77, 0x77, 0x78, 0x79, 0x79, 0x7a, 0x7a, + 0x7b, 0x7c, 0x7c, 0x7d, 0x7f, 0x7f, 0x80, 0x80, + 0x81, 0x81, 0x82, 0x84, 0x84, 0x85, 0x85, 0x86, + 0x86, 0x88, 0x89, 0x89, 0x8a, 0x8a, 0x8b, 0x8b, + 0x8d, 0x8d, 0x8e, 0x8e, 0x8f, 0x90, 0x90, 0x91, + 0x91, 0x92, 0x92, 0x93, 0x93, 0x94, 0x94, 0x96, + 0x96, 0x97, 0x97, 0x98, 0x98, 0x98, 0x99, 0x99, + 0x9a, 0x9a, 0x9b, 0x9b, 0x9c, 0x9c, 0x9d, 0x9d, + 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, 0xa2, + 0xa2, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, 0xa5, 0xa5, + 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa9, 0xa9, 0xab, + 0xab, 0xab, 0xac, 0xac, 0xad, 0xad, 0xad, 0xae, + 0xae, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb1, 0xb1, + 0xb1, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb4, 0xb4, + 0xb4, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, 0xb8, 0xb8, + 0xb8, 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xbc, + 0xbc, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbf, + 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc2, 0xc2, 0xc2, + 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5, + 0xc5, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc9, + 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb, + 0xcb, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xce, + 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, + 0xd0, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3, + 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, + 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, + 0xd9, 0xda, 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb, + 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde, 0xdf, + 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, + 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, + 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, + 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, + 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9, + 0xe9, 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, + 0xec, 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, + 0xee, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, 0xf3, 0xf3, + 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, + 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, + 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, + 0xf9, 0xf9, 0xfa, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, + 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb}, + {0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x11, 0x14, 0x15, + 0x16, 0x17, 0x1a, 0x1b, 0x1c, 0x1e, 0x1f, 0x20, + 0x23, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2b, 0x2c, + 0x2d, 0x2f, 0x30, 0x31, 0x33, 0x34, 0x34, 0x35, + 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3c, 0x3d, + 0x3f, 0x40, 0x42, 0x42, 0x43, 0x44, 0x45, 0x45, + 0x47, 0x48, 0x49, 0x49, 0x4a, 0x4b, 0x4b, 0x4c, + 0x4d, 0x4f, 0x4f, 0x50, 0x52, 0x52, 0x53, 0x54, + 0x54, 0x55, 0x55, 0x56, 0x58, 0x58, 0x59, 0x5a, + 0x5a, 0x5b, 0x5b, 0x5c, 0x5e, 0x5e, 0x5f, 0x5f, + 0x60, 0x60, 0x61, 0x61, 0x62, 0x63, 0x63, 0x65, + 0x65, 0x66, 0x66, 0x67, 0x67, 0x68, 0x68, 0x69, + 0x69, 0x6a, 0x6a, 0x6c, 0x6c, 0x6d, 0x6d, 0x6e, + 0x6e, 0x6f, 0x6f, 0x70, 0x70, 0x71, 0x71, 0x73, + 0x73, 0x74, 0x74, 0x74, 0x75, 0x75, 0x77, 0x77, + 0x78, 0x78, 0x79, 0x79, 0x79, 0x7a, 0x7a, 0x7b, + 0x7b, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7f, 0x7f, + 0x7f, 0x80, 0x80, 0x81, 0x81, 0x81, 0x82, 0x82, + 0x84, 0x84, 0x84, 0x85, 0x85, 0x86, 0x86, 0x86, + 0x88, 0x88, 0x88, 0x89, 0x89, 0x8a, 0x8a, 0x8a, + 0x8b, 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, + 0x8e, 0x8f, 0x8f, 0x90, 0x90, 0x90, 0x91, 0x91, + 0x91, 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x94, + 0x94, 0x94, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97, + 0x98, 0x98, 0x98, 0x98, 0x99, 0x99, 0x99, 0x9a, + 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9d, 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, + 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, 0xa4, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa8, 0xa8, 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, + 0xaa, 0xab, 0xab, 0xac, 0xac, 0xac, 0xad, 0xad, + 0xad, 0xad, 0xae, 0xae, 0xae, 0xae, 0xaf, 0xaf, + 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1, + 0xb1, 0xb1, 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, + 0xb3, 0xb3, 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, + 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, + 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba, + 0xba, 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, + 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, + 0xbf, 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, + 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, + 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5, + 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc7, + 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, 0xc9, 0xc9, 0xc9, + 0xca, 0xca, 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb, + 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce, + 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0, + 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, + 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, + 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, + 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, + 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda, + 0xda, 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, + 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, + 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, + 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, + 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, + 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, + 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, + 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, + 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, + 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, + 0xec, 0xec, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, + 0xee, 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef, + 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, + 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, + 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, + 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, + 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, + 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, + 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, + 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb}, + {0x0d, 0x10, 0x11, 0x14, 0x15, 0x17, 0x18, 0x1b, + 0x1c, 0x1e, 0x20, 0x22, 0x23, 0x26, 0x27, 0x28, + 0x29, 0x2b, 0x2d, 0x2f, 0x30, 0x31, 0x33, 0x34, + 0x35, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, + 0x3f, 0x40, 0x42, 0x43, 0x44, 0x45, 0x47, 0x48, + 0x49, 0x4a, 0x4b, 0x4b, 0x4c, 0x4d, 0x4f, 0x50, + 0x52, 0x52, 0x53, 0x54, 0x55, 0x56, 0x56, 0x58, + 0x59, 0x5a, 0x5a, 0x5b, 0x5c, 0x5e, 0x5e, 0x5f, + 0x60, 0x60, 0x61, 0x62, 0x62, 0x63, 0x65, 0x65, + 0x66, 0x67, 0x67, 0x68, 0x69, 0x69, 0x6a, 0x6c, + 0x6c, 0x6d, 0x6d, 0x6e, 0x6f, 0x6f, 0x70, 0x70, + 0x71, 0x73, 0x73, 0x74, 0x74, 0x75, 0x75, 0x77, + 0x78, 0x78, 0x79, 0x79, 0x7a, 0x7a, 0x7b, 0x7b, + 0x7c, 0x7c, 0x7d, 0x7d, 0x7f, 0x7f, 0x80, 0x80, + 0x81, 0x81, 0x82, 0x82, 0x84, 0x84, 0x85, 0x85, + 0x86, 0x86, 0x88, 0x88, 0x89, 0x89, 0x8a, 0x8a, + 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8f, + 0x8f, 0x90, 0x90, 0x91, 0x91, 0x91, 0x92, 0x92, + 0x93, 0x93, 0x94, 0x94, 0x94, 0x96, 0x96, 0x97, + 0x97, 0x98, 0x98, 0x98, 0x99, 0x99, 0x9a, 0x9a, + 0x9a, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, + 0x9d, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, + 0xa1, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, + 0xa4, 0xa5, 0xa5, 0xa5, 0xa6, 0xa6, 0xa8, 0xa8, + 0xa8, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab, 0xac, + 0xac, 0xac, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, + 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1, + 0xb1, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb4, + 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, + 0xb7, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xba, + 0xba, 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, + 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, + 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0xc2, 0xc2, 0xc3, + 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5, + 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, + 0xc7, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, + 0xca, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, + 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce, + 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, + 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3, + 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, + 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, 0xd8, + 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, + 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xde, 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf, + 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, + 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, + 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, + 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, + 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, + 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, + 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, + 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, + 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, + 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, + 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, + 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, + 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, + 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, + 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb} + }, + { /* gamma 5 */ + {0x16, 0x18, 0x19, 0x1b, 0x1d, 0x1e, 0x20, 0x21, + 0x23, 0x24, 0x25, 0x27, 0x28, 0x2a, 0x2b, 0x2c, + 0x2d, 0x2f, 0x30, 0x31, 0x32, 0x34, 0x35, 0x36, + 0x37, 0x38, 0x39, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, + 0x4f, 0x50, 0x51, 0x51, 0x52, 0x53, 0x54, 0x55, + 0x56, 0x56, 0x57, 0x58, 0x59, 0x59, 0x5a, 0x5b, + 0x5c, 0x5c, 0x5d, 0x5e, 0x5f, 0x5f, 0x60, 0x61, + 0x62, 0x62, 0x63, 0x64, 0x64, 0x65, 0x66, 0x66, + 0x67, 0x68, 0x68, 0x69, 0x6a, 0x6a, 0x6b, 0x6b, + 0x6c, 0x6d, 0x6d, 0x6e, 0x6f, 0x6f, 0x70, 0x70, + 0x71, 0x71, 0x72, 0x73, 0x73, 0x74, 0x74, 0x75, + 0x75, 0x76, 0x77, 0x77, 0x78, 0x78, 0x79, 0x79, + 0x7a, 0x7a, 0x7b, 0x7b, 0x7c, 0x7d, 0x7d, 0x7e, + 0x7e, 0x7f, 0x7f, 0x80, 0x80, 0x81, 0x81, 0x82, + 0x82, 0x83, 0x83, 0x84, 0x84, 0x84, 0x85, 0x85, + 0x86, 0x86, 0x87, 0x87, 0x88, 0x88, 0x89, 0x89, + 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, 0x8c, 0x8c, 0x8d, + 0x8d, 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x90, 0x90, + 0x91, 0x91, 0x91, 0x92, 0x92, 0x93, 0x93, 0x94, + 0x94, 0x94, 0x95, 0x95, 0x96, 0x96, 0x96, 0x97, + 0x97, 0x98, 0x98, 0x98, 0x99, 0x99, 0x9a, 0x9a, + 0x9a, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, 0x9d, 0x9d, + 0x9d, 0x9e, 0x9e, 0x9e, 0x9f, 0x9f, 0xa0, 0xa0, + 0xa0, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa3, + 0xa3, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa6, + 0xa6, 0xa6, 0xa7, 0xa7, 0xa7, 0xa8, 0xa8, 0xa8, + 0xa9, 0xa9, 0xa9, 0xaa, 0xaa, 0xaa, 0xab, 0xab, + 0xab, 0xac, 0xac, 0xac, 0xad, 0xad, 0xad, 0xae, + 0xae, 0xae, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, + 0xb0, 0xb1, 0xb1, 0xb1, 0xb2, 0xb2, 0xb2, 0xb3, + 0xb3, 0xb3, 0xb4, 0xb4, 0xb4, 0xb4, 0xb5, 0xb5, + 0xb5, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, + 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xba, 0xba, + 0xba, 0xba, 0xbb, 0xbb, 0xbb, 0xbc, 0xbc, 0xbc, + 0xbc, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, + 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc1, + 0xc1, 0xc1, 0xc1, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, + 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5, + 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, + 0xc7, 0xc8, 0xc8, 0xc8, 0xc8, 0xc9, 0xc9, 0xc9, + 0xc9, 0xca, 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb, + 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, + 0xcd, 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, + 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, + 0xd1, 0xd2, 0xd2, 0xd2, 0xd2, 0xd3, 0xd3, 0xd3, + 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd5, 0xd5, 0xd5, + 0xd5, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, + 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, + 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb, + 0xdb, 0xdb, 0xdb, 0xdc, 0xdc, 0xdc, 0xdc, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde, + 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, + 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, + 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4, + 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, + 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, + 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, + 0xe9, 0xe9, 0xea, 0xea, 0xea, 0xea, 0xea, 0xeb, + 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, + 0xed, 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, + 0xee, 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, + 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf3, 0xf3, 0xf3, + 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, + 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, + 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, + 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, + 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, + 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0x0f, 0x11, 0x12, 0x14, 0x15, 0x16, 0x18, 0x19, + 0x1a, 0x1b, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, + 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, + 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x31, 0x32, + 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x38, 0x39, + 0x3a, 0x3b, 0x3c, 0x3c, 0x3d, 0x3e, 0x3f, 0x3f, + 0x40, 0x41, 0x42, 0x42, 0x43, 0x44, 0x44, 0x45, + 0x46, 0x47, 0x47, 0x48, 0x49, 0x49, 0x4a, 0x4b, + 0x4b, 0x4c, 0x4c, 0x4d, 0x4e, 0x4e, 0x4f, 0x50, + 0x50, 0x51, 0x51, 0x52, 0x53, 0x53, 0x54, 0x54, + 0x55, 0x55, 0x56, 0x56, 0x57, 0x58, 0x58, 0x59, + 0x59, 0x5a, 0x5a, 0x5b, 0x5b, 0x5c, 0x5c, 0x5d, + 0x5d, 0x5e, 0x5e, 0x5f, 0x5f, 0x60, 0x60, 0x61, + 0x61, 0x62, 0x62, 0x63, 0x63, 0x64, 0x64, 0x65, + 0x65, 0x66, 0x66, 0x66, 0x67, 0x67, 0x68, 0x68, + 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6b, 0x6b, 0x6c, + 0x6c, 0x6d, 0x6d, 0x6d, 0x6e, 0x6e, 0x6f, 0x6f, + 0x6f, 0x70, 0x70, 0x71, 0x71, 0x71, 0x72, 0x72, + 0x73, 0x73, 0x73, 0x74, 0x74, 0x75, 0x75, 0x75, + 0x76, 0x76, 0x76, 0x77, 0x77, 0x78, 0x78, 0x78, + 0x79, 0x79, 0x79, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, + 0x7b, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d, 0x7e, 0x7e, + 0x7e, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80, 0x81, + 0x81, 0x81, 0x82, 0x82, 0x82, 0x83, 0x83, 0x83, + 0x84, 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x86, + 0x86, 0x86, 0x87, 0x87, 0x87, 0x88, 0x88, 0x88, + 0x88, 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8c, 0x8c, 0x8c, 0x8d, 0x8d, + 0x8d, 0x8e, 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f, + 0x90, 0x90, 0x90, 0x90, 0x91, 0x91, 0x91, 0x91, + 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x93, 0x94, + 0x94, 0x94, 0x94, 0x95, 0x95, 0x95, 0x96, 0x96, + 0x96, 0x96, 0x97, 0x97, 0x97, 0x97, 0x98, 0x98, + 0x98, 0x98, 0x99, 0x99, 0x99, 0x99, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9f, 0x9f, 0x9f, 0x9f, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa9, 0xa9, 0xa9, + 0xa9, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xab, 0xab, + 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xac, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xae, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, + 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2, + 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3, + 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb5, 0xb5, 0xb5, + 0xb5, 0xb5, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb7, + 0xb7, 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb8, + 0xb8, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba, + 0xba, 0xba, 0xba, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, + 0xbb, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, + 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, + 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, + 0xc0, 0xc0, 0xc0, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, + 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, + 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, + 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, + 0xc7, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc9, 0xc9, + 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca, + 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, + 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, + 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0, + 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, + 0xd1, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd3, + 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, + 0xd4, 0xd4, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, + 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, + 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, + 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xda, + 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb, 0xdb, 0xdb, + 0xdb, 0xdb, 0xdb, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, + 0xde, 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, + 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, + 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, + 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, + 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, + 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, + 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, + 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, + 0xe9, 0xe9, 0xe9, 0xe9, 0xea, 0xea, 0xea, 0xea, + 0xea, 0xea, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, + 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed, + 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, 0xee, + 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, + 0xf1, 0xf1, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, + 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, + 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, + 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, + 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, + 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, + 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, + 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, + 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0x13, 0x15, 0x16, 0x18, 0x19, 0x1b, 0x1c, 0x1e, + 0x1f, 0x20, 0x22, 0x23, 0x24, 0x26, 0x27, 0x28, + 0x29, 0x2a, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, + 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, + 0x42, 0x43, 0x44, 0x44, 0x45, 0x46, 0x47, 0x48, + 0x49, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4d, 0x4e, + 0x4f, 0x50, 0x50, 0x51, 0x52, 0x53, 0x53, 0x54, + 0x55, 0x55, 0x56, 0x57, 0x57, 0x58, 0x59, 0x59, + 0x5a, 0x5b, 0x5b, 0x5c, 0x5d, 0x5d, 0x5e, 0x5f, + 0x5f, 0x60, 0x60, 0x61, 0x62, 0x62, 0x63, 0x63, + 0x64, 0x65, 0x65, 0x66, 0x66, 0x67, 0x67, 0x68, + 0x69, 0x69, 0x6a, 0x6a, 0x6b, 0x6b, 0x6c, 0x6c, + 0x6d, 0x6d, 0x6e, 0x6e, 0x6f, 0x6f, 0x70, 0x70, + 0x71, 0x71, 0x72, 0x72, 0x73, 0x73, 0x74, 0x74, + 0x75, 0x75, 0x76, 0x76, 0x77, 0x77, 0x78, 0x78, + 0x79, 0x79, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7c, + 0x7c, 0x7d, 0x7d, 0x7e, 0x7e, 0x7e, 0x7f, 0x7f, + 0x80, 0x80, 0x81, 0x81, 0x81, 0x82, 0x82, 0x83, + 0x83, 0x84, 0x84, 0x84, 0x85, 0x85, 0x86, 0x86, + 0x86, 0x87, 0x87, 0x88, 0x88, 0x88, 0x89, 0x89, + 0x89, 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, 0x8c, 0x8c, + 0x8c, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, + 0x8f, 0x90, 0x90, 0x90, 0x91, 0x91, 0x92, 0x92, + 0x92, 0x93, 0x93, 0x93, 0x94, 0x94, 0x94, 0x95, + 0x95, 0x95, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97, + 0x98, 0x98, 0x98, 0x99, 0x99, 0x99, 0x9a, 0x9a, + 0x9a, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c, 0x9d, + 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, 0x9e, 0x9f, 0x9f, + 0x9f, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, + 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa6, 0xa6, 0xa6, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa8, 0xa8, 0xa8, 0xa9, + 0xa9, 0xa9, 0xa9, 0xaa, 0xaa, 0xaa, 0xab, 0xab, + 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xad, 0xad, + 0xad, 0xae, 0xae, 0xae, 0xae, 0xaf, 0xaf, 0xaf, + 0xaf, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, + 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3, + 0xb4, 0xb4, 0xb4, 0xb4, 0xb5, 0xb5, 0xb5, 0xb5, + 0xb6, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, + 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9, + 0xba, 0xba, 0xba, 0xba, 0xbb, 0xbb, 0xbb, 0xbb, + 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, + 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, 0xbf, + 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc1, 0xc1, 0xc1, + 0xc1, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, + 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5, + 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc7, + 0xc7, 0xc7, 0xc7, 0xc7, 0xc8, 0xc8, 0xc8, 0xc8, + 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, + 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc, + 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xce, + 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf, + 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, + 0xd1, 0xd1, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd3, + 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, + 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd6, 0xd6, 0xd6, + 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, + 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, + 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb, 0xdb, + 0xdb, 0xdb, 0xdb, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, + 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, + 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, + 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, + 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, + 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, + 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, + 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9, + 0xe9, 0xe9, 0xe9, 0xea, 0xea, 0xea, 0xea, 0xea, + 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, + 0xec, 0xec, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, + 0xee, 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef, + 0xef, 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf2, 0xf2, 0xf2, + 0xf2, 0xf2, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, + 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, + 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, + 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, + 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, + 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, + 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, + 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + }, + }; + + reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x00); + if (sd->bridge == BRIDGE_TP6810) + reg_w(gspca_dev, 0x02, 0x28); +/* msleep(50); */ + gamma = sd->ctrls[GAMMA].val; + bulk_w(gspca_dev, 0x00, gamma_tb[gamma][0], 1024); + bulk_w(gspca_dev, 0x01, gamma_tb[gamma][1], 1024); + bulk_w(gspca_dev, 0x02, gamma_tb[gamma][2], 1024); + if (sd->bridge == BRIDGE_TP6810) { + int i; + + reg_w(gspca_dev, 0x02, 0x2b); + reg_w(gspca_dev, 0x02, 0x28); + for (i = 0; i < 6; i++) + reg_w(gspca_dev, TP6800_R55_GAMMA_R, + gamma_tb[gamma][0][i]); + reg_w(gspca_dev, 0x02, 0x2b); + reg_w(gspca_dev, 0x02, 0x28); + for (i = 0; i < 6; i++) + reg_w(gspca_dev, TP6800_R56_GAMMA_G, + gamma_tb[gamma][1][i]); + reg_w(gspca_dev, 0x02, 0x2b); + reg_w(gspca_dev, 0x02, 0x28); + for (i = 0; i < 6; i++) + reg_w(gspca_dev, TP6800_R57_GAMMA_B, + gamma_tb[gamma][2][i]); + reg_w(gspca_dev, 0x02, 0x28); + } + reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x03); +/* msleep(50); */ +} + +static void setsharpness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 val; + + if (sd->bridge == BRIDGE_TP6800) { + val = sd->ctrls[SHARPNESS].val + | 0x08; /* grid compensation enable */ + if (gspca_dev->width == 640) + reg_w(gspca_dev, TP6800_R78_FORMAT, 0x00); /* vga */ + else + val |= 0x04; /* scaling down enable */ + reg_w(gspca_dev, TP6800_R5D_DEMOSAIC_CFG, val); + } else { + val = (sd->ctrls[SHARPNESS].val << 5) | 0x08; + reg_w(gspca_dev, 0x59, val); + } +} + +static void setautogain(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (gspca_dev->ctrl_dis & (1 << AUTOGAIN)) + return; + if (sd->ctrls[AUTOGAIN].val) { + sd->ag_cnt = AG_CNT_START; + gspca_dev->ctrl_inac |= (1 << EXPOSURE) | (1 << GAIN); + } else { + sd->ag_cnt = -1; + gspca_dev->ctrl_inac &= ~((1 << EXPOSURE) | (1 << GAIN)); + } +} + +/* set the resolution for sensor cx0342 */ +static void set_resolution(struct gspca_dev *gspca_dev) +{ + reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x00); + if (gspca_dev->width == 320) { + reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0x06); + msleep(100); + i2c_w(gspca_dev, CX0342_AUTO_ADC_CALIB, 0x01); + msleep(100); + reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x03); + reg_w(gspca_dev, TP6800_R78_FORMAT, 0x01); /* qvga */ + reg_w(gspca_dev, TP6800_R5D_DEMOSAIC_CFG, 0x0d); + i2c_w(gspca_dev, CX0342_EXPO_LINE_L, 0x37); + i2c_w(gspca_dev, CX0342_EXPO_LINE_H, 0x01); + } else { + reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0x05); + msleep(100); + i2c_w(gspca_dev, CX0342_AUTO_ADC_CALIB, 0x01); + msleep(100); + reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x03); + reg_w(gspca_dev, TP6800_R78_FORMAT, 0x00); /* vga */ + reg_w(gspca_dev, TP6800_R5D_DEMOSAIC_CFG, 0x09); + i2c_w(gspca_dev, CX0342_EXPO_LINE_L, 0xcf); + i2c_w(gspca_dev, CX0342_EXPO_LINE_H, 0x00); + } + i2c_w(gspca_dev, CX0342_SYS_CTRL_0, 0x01); + bulk_w(gspca_dev, 0x03, color_gain[SENSOR_CX0342], + ARRAY_SIZE(color_gain[0])); + setgamma(gspca_dev); + setquality(gspca_dev); +} + +/* convert the frame rate to a tp68x0 value */ +static int get_fr_idx(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i; + + if (sd->bridge == BRIDGE_TP6800) { + for (i = 0; i < ARRAY_SIZE(rates) - 1; i++) { + if (sd->framerate >= rates[i]) + break; + } + i = 6 - i; /* 1 = 5fps .. 6 = 30fps */ + + /* 640x480 * 30 fps does not work */ + if (i == 6 /* if 30 fps */ + && gspca_dev->width == 640) + i = 0x86; /* 15 fps */ + } else { + for (i = 0; i < ARRAY_SIZE(rates_6810) - 1; i++) { + if (sd->framerate >= rates_6810[i]) + break; + } + i = 7 - i; /* 3 = 5fps .. 7 = 30fps */ + + /* 640x480 * 30 fps does not work */ + if (i == 7 /* if 30 fps */ + && gspca_dev->width == 640) + i = 6; /* 15 fps */ + i |= 0x80; /* clock * 1 */ + } + return i; +} + +static void setframerate(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 fr_idx; + + fr_idx = get_fr_idx(gspca_dev); + + if (sd->bridge == BRIDGE_TP6810) { + reg_r(gspca_dev, 0x7b); + reg_w(gspca_dev, 0x7b, + sd->sensor == SENSOR_CX0342 ? 0x10 : 0x90); + if (sd->ctrls[EXPOSURE].val >= 128) + fr_idx = 0xf0; /* lower frame rate */ + } + + reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, fr_idx); + + if (sd->sensor == SENSOR_CX0342) + i2c_w(gspca_dev, CX0342_AUTO_ADC_CALIB, 0x01); +} + +static void setrgain(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int rgain; + + rgain = sd->ctrls[RGAIN].val; + i2c_w(gspca_dev, CX0342_RAW_RGAIN_H, rgain >> 8); + i2c_w(gspca_dev, CX0342_RAW_RGAIN_L, rgain); + i2c_w(gspca_dev, CX0342_SYS_CTRL_0, 0x80); +} + +static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->sensor == SENSOR_CX0342) { + sd->ctrls[BGAIN].val = sd->ctrls[BGAIN].val + * val / sd->ctrls[GAIN].val; + if (sd->ctrls[BGAIN].val > 4095) + sd->ctrls[BGAIN].val = 4095; + sd->ctrls[RGAIN].val = sd->ctrls[RGAIN].val + * val / sd->ctrls[GAIN].val; + if (sd->ctrls[RGAIN].val > 4095) + sd->ctrls[RGAIN].val = 4095; + } + sd->ctrls[GAIN].val = val; + if (gspca_dev->streaming) + setexposure(gspca_dev); + return gspca_dev->usb_err; +} + +static void setbgain(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int bgain; + + bgain = sd->ctrls[BGAIN].val; + i2c_w(gspca_dev, CX0342_RAW_BGAIN_H, bgain >> 8); + i2c_w(gspca_dev, CX0342_RAW_BGAIN_L, bgain); + i2c_w(gspca_dev, CX0342_SYS_CTRL_0, 0x80); +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->bridge = id->driver_info; + + gspca_dev->cam.cam_mode = vga_mode; + gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode); + gspca_dev->cam.mode_framerates = sd->bridge == BRIDGE_TP6800 ? + framerates : framerates_6810; + + sd->framerate = 30; /* default: 30 fps */ + gspca_dev->cam.ctrls = sd->ctrls; + return 0; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + static const struct cmd tp6800_preinit[] = { + {TP6800_R10_SIF_TYPE, 0x01}, /* sif */ + {TP6800_R11_SIF_CONTROL, 0x01}, + {TP6800_R15_GPIO_PU, 0x9f}, + {TP6800_R16_GPIO_PD, 0x9f}, + {TP6800_R17_GPIO_IO, 0x80}, + {TP6800_R18_GPIO_DATA, 0x40}, /* LED off */ + }; + static const struct cmd tp6810_preinit[] = { + {TP6800_R2F_TIMING_CFG, 0x2f}, + {TP6800_R15_GPIO_PU, 0x6f}, + {TP6800_R16_GPIO_PD, 0x40}, + {TP6800_R17_GPIO_IO, 0x9f}, + {TP6800_R18_GPIO_DATA, 0xc1}, /* LED off */ + }; + + if (sd->bridge == BRIDGE_TP6800) + reg_w_buf(gspca_dev, tp6800_preinit, + ARRAY_SIZE(tp6800_preinit)); + else + reg_w_buf(gspca_dev, tp6810_preinit, + ARRAY_SIZE(tp6810_preinit)); + msleep(15); + reg_r(gspca_dev, TP6800_R18_GPIO_DATA); + PDEBUG(D_PROBE, "gpio: %02x", gspca_dev->usb_buf[0]); +/* values: + * 0x80: snapshot button + * 0x40: LED + * 0x20: (bridge / sensor) reset for tp6810 ? + * 0x07: sensor type ? + */ + + /* guess the sensor type */ + if (force_sensor >= 0) { + sd->sensor = force_sensor; + } else { + if (sd->bridge == BRIDGE_TP6800) { +/*fixme: not sure this is working*/ + switch (gspca_dev->usb_buf[0] & 0x07) { + case 0: + sd->sensor = SENSOR_SOI763A; + break; + case 1: + sd->sensor = SENSOR_CX0342; + break; + } + } else { + int sensor; + + sensor = probe_6810(gspca_dev); + if (sensor < 0) { + pr_warn("Unknown sensor %d - forced to soi763a\n", + -sensor); + sensor = SENSOR_SOI763A; + } + sd->sensor = sensor; + } + } + if (sd->sensor == SENSOR_SOI763A) { + pr_info("Sensor soi763a\n"); + sd->ctrls[GAMMA].def = sd->bridge == BRIDGE_TP6800 ? 0 : 1; + sd->ctrls[GAIN].max = 15; + sd->ctrls[GAIN].def = 3; + gspca_dev->ctrl_dis = (1 << RGAIN) | (1 << BGAIN); + if (sd->bridge == BRIDGE_TP6810) { + soi763a_6810_init(gspca_dev); +#if AUTOGAIN_DEF + gspca_dev->ctrl_inac |= (1 << EXPOSURE) | (1 << GAIN); +#endif + } else { + gspca_dev->ctrl_dis |= (1 << AUTOGAIN); + } + } else { + pr_info("Sensor cx0342\n"); + if (sd->bridge == BRIDGE_TP6810) { + cx0342_6810_init(gspca_dev); +#if AUTOGAIN_DEF + gspca_dev->ctrl_inac |= (1 << EXPOSURE) | (1 << GAIN); +#endif + } else { + gspca_dev->ctrl_dis |= (1 << AUTOGAIN); + } + } + + if (sd->bridge == BRIDGE_TP6810) + sd->ctrls[QUALITY].def = 0; /* auto quality */ + set_dqt(gspca_dev, 0); + return 0; +} + +/* This function is called before choosing the alt setting */ +static int sd_isoc_init(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + static const struct cmd cx_sensor_init[] = { + {CX0342_AUTO_ADC_CALIB, 0x81}, + {CX0342_EXPO_LINE_L, 0x37}, + {CX0342_EXPO_LINE_H, 0x01}, + {CX0342_RAW_GRGAIN_L, 0x00}, + {CX0342_RAW_GBGAIN_L, 0x00}, + {CX0342_RAW_RGAIN_L, 0x00}, + {CX0342_RAW_BGAIN_L, 0x00}, + {CX0342_SYS_CTRL_0, 0x81}, + }; + static const struct cmd cx_bridge_init[] = { + {0x4d, 0x00}, + {0x4c, 0xff}, + {0x4e, 0xff}, + {0x4f, 0x00}, + }; + static const struct cmd ov_sensor_init[] = { + {0x10, 0x75}, /* exposure */ + {0x76, 0x03}, + {0x00, 0x00}, /* gain */ + }; + static const struct cmd ov_bridge_init[] = { + {0x7b, 0x90}, + {TP6800_R3F_FRAME_RATE, 0x87}, + }; + + if (sd->bridge == BRIDGE_TP6800) + return 0; + if (sd->sensor == SENSOR_CX0342) { + reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x20); + reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0x87); + i2c_w_buf(gspca_dev, cx_sensor_init, + ARRAY_SIZE(cx_sensor_init)); + reg_w_buf(gspca_dev, cx_bridge_init, + ARRAY_SIZE(cx_bridge_init)); + bulk_w(gspca_dev, 0x03, color_null, sizeof color_null); + reg_w(gspca_dev, 0x59, 0x40); + } else { + reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x21); + i2c_w_buf(gspca_dev, ov_sensor_init, + ARRAY_SIZE(ov_sensor_init)); + reg_r(gspca_dev, 0x7b); + reg_w_buf(gspca_dev, ov_bridge_init, + ARRAY_SIZE(ov_bridge_init)); + } + reg_w(gspca_dev, TP6800_R78_FORMAT, + gspca_dev->curr_mode ? 0x00 : 0x01); + return gspca_dev->usb_err; +} + +static void set_led(struct gspca_dev *gspca_dev, int on) +{ + u8 data; + + reg_r(gspca_dev, TP6800_R18_GPIO_DATA); + data = gspca_dev->usb_buf[0]; + if (on) + data &= ~0x40; + else + data |= 0x40; + reg_w(gspca_dev, TP6800_R18_GPIO_DATA, data); +} + +static void cx0342_6800_start(struct gspca_dev *gspca_dev) +{ + static const struct cmd reg_init[] = { +/*fixme: is this usefull?*/ + {TP6800_R17_GPIO_IO, 0x9f}, + {TP6800_R16_GPIO_PD, 0x40}, + {TP6800_R10_SIF_TYPE, 0x00}, /* i2c 8 bits */ + {TP6800_R50, 0x00}, + {TP6800_R51, 0x00}, + {TP6800_R52, 0xff}, + {TP6800_R53, 0x03}, + {TP6800_R54_DARK_CFG, 0x07}, + {TP6800_R5C_EDGE_THRLD, 0x40}, + {TP6800_R7A_BLK_THRLD, 0x40}, + {TP6800_R2F_TIMING_CFG, 0x17}, + {TP6800_R30_SENSOR_CFG, 0x18}, /* G1B..RG0 */ + {TP6800_R37_FRONT_DARK_ST, 0x00}, + {TP6800_R38_FRONT_DARK_END, 0x00}, + {TP6800_R39_REAR_DARK_ST_L, 0x00}, + {TP6800_R3A_REAR_DARK_ST_H, 0x00}, + {TP6800_R3B_REAR_DARK_END_L, 0x00}, + {TP6800_R3C_REAR_DARK_END_H, 0x00}, + {TP6800_R3D_HORIZ_DARK_LINE_L, 0x00}, + {TP6800_R3E_HORIZ_DARK_LINE_H, 0x00}, + {TP6800_R21_ENDP_1_CTL, 0x03}, + + {TP6800_R31_PIXEL_START, 0x0b}, + {TP6800_R32_PIXEL_END_L, 0x8a}, + {TP6800_R33_PIXEL_END_H, 0x02}, + {TP6800_R34_LINE_START, 0x0e}, + {TP6800_R35_LINE_END_L, 0xf4}, + {TP6800_R36_LINE_END_H, 0x01}, + {TP6800_R78_FORMAT, 0x00}, + {TP6800_R12_SIF_ADDR_S, 0x20}, /* cx0342 i2c addr */ + }; + static const struct cmd sensor_init[] = { + {CX0342_OUTPUT_CTRL, 0x07}, + {CX0342_BYPASS_MODE, 0x58}, + {CX0342_GPXLTHD_L, 0x16}, + {CX0342_RBPXLTHD_L, 0x16}, + {CX0342_PLANETHD_L, 0xc0}, + {CX0342_PLANETHD_H, 0x03}, + {CX0342_RB_GAP_L, 0xff}, + {CX0342_RB_GAP_H, 0x07}, + {CX0342_G_GAP_L, 0xff}, + {CX0342_G_GAP_H, 0x07}, + {CX0342_RST_OVERFLOW_L, 0x5c}, + {CX0342_RST_OVERFLOW_H, 0x01}, + {CX0342_DATA_OVERFLOW_L, 0xfc}, + {CX0342_DATA_OVERFLOW_H, 0x03}, + {CX0342_DATA_UNDERFLOW_L, 0x00}, + {CX0342_DATA_UNDERFLOW_H, 0x00}, + {CX0342_SYS_CTRL_0, 0x40}, + {CX0342_GLOBAL_GAIN, 0x01}, + {CX0342_CLOCK_GEN, 0x00}, + {CX0342_SYS_CTRL_0, 0x02}, + {CX0342_IDLE_CTRL, 0x05}, + {CX0342_ADCGN, 0x00}, + {CX0342_ADC_CTL, 0x00}, + {CX0342_LVRST_BLBIAS, 0x01}, + {CX0342_VTHSEL, 0x0b}, + {CX0342_RAMP_RIV, 0x0b}, + {CX0342_LDOSEL, 0x07}, + {CX0342_SPV_VALUE_L, 0x40}, + {CX0342_SPV_VALUE_H, 0x02}, + }; + + reg_w_buf(gspca_dev, reg_init, ARRAY_SIZE(reg_init)); + i2c_w_buf(gspca_dev, sensor_init, ARRAY_SIZE(sensor_init)); + i2c_w_buf(gspca_dev, cx0342_timing_seq, ARRAY_SIZE(cx0342_timing_seq)); + reg_w(gspca_dev, TP6800_R5C_EDGE_THRLD, 0x10); + reg_w(gspca_dev, TP6800_R54_DARK_CFG, 0x00); + i2c_w(gspca_dev, CX0342_EXPO_LINE_H, 0x00); + i2c_w(gspca_dev, CX0342_SYS_CTRL_0, 0x01); + setexposure(gspca_dev); + set_led(gspca_dev, 1); + set_resolution(gspca_dev); +} + +static void cx0342_6810_start(struct gspca_dev *gspca_dev) +{ + static const struct cmd sensor_init_2[] = { + {CX0342_EXPO_LINE_L, 0x6f}, + {CX0342_EXPO_LINE_H, 0x02}, + {CX0342_RAW_GRGAIN_L, 0x00}, + {CX0342_RAW_GBGAIN_L, 0x00}, + {CX0342_RAW_RGAIN_L, 0x00}, + {CX0342_RAW_BGAIN_L, 0x00}, + {CX0342_SYS_CTRL_0, 0x81}, + }; + static const struct cmd bridge_init_2[] = { + {0x4d, 0x00}, + {0x4c, 0xff}, + {0x4e, 0xff}, + {0x4f, 0x00}, + {TP6800_R7A_BLK_THRLD, 0x00}, + {TP6800_R79_QUALITY, 0x04}, + {TP6800_R79_QUALITY, 0x01}, + }; + static const struct cmd bridge_init_3[] = { + {TP6800_R31_PIXEL_START, 0x08}, + {TP6800_R32_PIXEL_END_L, 0x87}, + {TP6800_R33_PIXEL_END_H, 0x02}, + {TP6800_R34_LINE_START, 0x0e}, + {TP6800_R35_LINE_END_L, 0xf4}, + {TP6800_R36_LINE_END_H, 0x01}, + }; + static const struct cmd sensor_init_3[] = { + {CX0342_AUTO_ADC_CALIB, 0x81}, + {CX0342_EXPO_LINE_L, 0x6f}, + {CX0342_EXPO_LINE_H, 0x02}, + {CX0342_RAW_GRGAIN_L, 0x00}, + {CX0342_RAW_GBGAIN_L, 0x00}, + {CX0342_RAW_RGAIN_L, 0x00}, + {CX0342_RAW_BGAIN_L, 0x00}, + {CX0342_SYS_CTRL_0, 0x81}, + }; + static const struct cmd bridge_init_5[] = { + {0x4d, 0x00}, + {0x4c, 0xff}, + {0x4e, 0xff}, + {0x4f, 0x00}, + }; + static const struct cmd sensor_init_4[] = { + {CX0342_EXPO_LINE_L, 0xd3}, + {CX0342_EXPO_LINE_H, 0x01}, +/*fixme: gains, but 00..80 only*/ + {CX0342_RAW_GRGAIN_L, 0x40}, + {CX0342_RAW_GBGAIN_L, 0x40}, + {CX0342_RAW_RGAIN_L, 0x40}, + {CX0342_RAW_BGAIN_L, 0x40}, + {CX0342_SYS_CTRL_0, 0x81}, + }; + static const struct cmd sensor_init_5[] = { + {CX0342_IDLE_CTRL, 0x05}, + {CX0342_ADCGN, 0x00}, + {CX0342_ADC_CTL, 0x00}, + {CX0342_LVRST_BLBIAS, 0x01}, + {CX0342_VTHSEL, 0x0b}, + {CX0342_RAMP_RIV, 0x0b}, + {CX0342_LDOSEL, 0x07}, + {CX0342_SPV_VALUE_L, 0x40}, + {CX0342_SPV_VALUE_H, 0x02}, + {CX0342_AUTO_ADC_CALIB, 0x81}, + }; + + reg_w(gspca_dev, 0x22, gspca_dev->alt); + i2c_w_buf(gspca_dev, sensor_init_2, ARRAY_SIZE(sensor_init_2)); + reg_w_buf(gspca_dev, bridge_init_2, ARRAY_SIZE(bridge_init_2)); + reg_w_buf(gspca_dev, tp6810_cx_init_common, + ARRAY_SIZE(tp6810_cx_init_common)); + reg_w_buf(gspca_dev, bridge_init_3, ARRAY_SIZE(bridge_init_3)); + if (gspca_dev->curr_mode) { + reg_w(gspca_dev, 0x4a, 0x7f); + reg_w(gspca_dev, 0x07, 0x05); + reg_w(gspca_dev, TP6800_R78_FORMAT, 0x00); /* vga */ + } else { + reg_w(gspca_dev, 0x4a, 0xff); + reg_w(gspca_dev, 0x07, 0x85); + reg_w(gspca_dev, TP6800_R78_FORMAT, 0x01); /* qvga */ + } + setgamma(gspca_dev); + reg_w_buf(gspca_dev, tp6810_bridge_start, + ARRAY_SIZE(tp6810_bridge_start)); + setsharpness(gspca_dev); + bulk_w(gspca_dev, 0x03, color_gain[SENSOR_CX0342], + ARRAY_SIZE(color_gain[0])); + reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0x87); + i2c_w_buf(gspca_dev, sensor_init_3, ARRAY_SIZE(sensor_init_3)); + reg_w_buf(gspca_dev, bridge_init_5, ARRAY_SIZE(bridge_init_5)); + i2c_w_buf(gspca_dev, sensor_init_4, ARRAY_SIZE(sensor_init_4)); + reg_w_buf(gspca_dev, bridge_init_5, ARRAY_SIZE(bridge_init_5)); + i2c_w_buf(gspca_dev, sensor_init_5, ARRAY_SIZE(sensor_init_5)); + + set_led(gspca_dev, 1); +/* setquality(gspca_dev); */ +} + +static void soi763a_6800_start(struct gspca_dev *gspca_dev) +{ + static const struct cmd reg_init[] = { + {TP6800_R79_QUALITY, 0x04}, + {TP6800_R79_QUALITY, 0x01}, + {TP6800_R10_SIF_TYPE, 0x00}, /* i2c 8 bits */ + + {TP6800_R50, 0x00}, + {TP6800_R51, 0x00}, + {TP6800_R52, 0xff}, + {TP6800_R53, 0x03}, + {TP6800_R54_DARK_CFG, 0x07}, + {TP6800_R5C_EDGE_THRLD, 0x40}, + + {TP6800_R79_QUALITY, 0x03}, + {TP6800_R7A_BLK_THRLD, 0x40}, + + {TP6800_R2F_TIMING_CFG, 0x46}, + {TP6800_R30_SENSOR_CFG, 0x10}, /* BG1..G0R */ + {TP6800_R37_FRONT_DARK_ST, 0x00}, + {TP6800_R38_FRONT_DARK_END, 0x00}, + {TP6800_R39_REAR_DARK_ST_L, 0x00}, + {TP6800_R3A_REAR_DARK_ST_H, 0x00}, + {TP6800_R3B_REAR_DARK_END_L, 0x00}, + {TP6800_R3C_REAR_DARK_END_H, 0x00}, + {TP6800_R3D_HORIZ_DARK_LINE_L, 0x00}, + {TP6800_R3E_HORIZ_DARK_LINE_H, 0x00}, + {TP6800_R21_ENDP_1_CTL, 0x03}, + + {TP6800_R3F_FRAME_RATE, 0x04}, /* 15 fps */ + {TP6800_R5D_DEMOSAIC_CFG, 0x0e}, /* scale down - medium edge */ + + {TP6800_R31_PIXEL_START, 0x1b}, + {TP6800_R32_PIXEL_END_L, 0x9a}, + {TP6800_R33_PIXEL_END_H, 0x02}, + {TP6800_R34_LINE_START, 0x0f}, + {TP6800_R35_LINE_END_L, 0xf4}, + {TP6800_R36_LINE_END_H, 0x01}, + {TP6800_R78_FORMAT, 0x01}, /* qvga */ + {TP6800_R12_SIF_ADDR_S, 0x21}, /* soi763a i2c addr */ + {TP6800_R1A_SIF_TX_DATA2, 0x00}, + }; + static const struct cmd sensor_init[] = { + {0x12, 0x48}, /* mirror - RGB */ + {0x13, 0xa0}, /* clock - no AGC nor AEC */ + {0x03, 0xa4}, /* saturation */ + {0x04, 0x30}, /* hue */ + {0x05, 0x88}, /* contrast */ + {0x06, 0x60}, /* brightness */ + {0x10, 0x41}, /* AEC */ + {0x11, 0x40}, /* clock rate */ + {0x13, 0xa0}, + {0x14, 0x00}, /* 640x480 */ + {0x15, 0x14}, + {0x1f, 0x41}, + {0x20, 0x80}, + {0x23, 0xee}, + {0x24, 0x50}, + {0x25, 0x7a}, + {0x26, 0x00}, + {0x27, 0xe2}, + {0x28, 0xb0}, + {0x2a, 0x00}, + {0x2b, 0x00}, + {0x2d, 0x81}, + {0x2f, 0x9d}, + {0x60, 0x80}, + {0x61, 0x00}, + {0x62, 0x88}, + {0x63, 0x11}, + {0x64, 0x89}, + {0x65, 0x00}, + {0x67, 0x94}, + {0x68, 0x7a}, + {0x69, 0x0f}, + {0x6c, 0x80}, + {0x6d, 0x80}, + {0x6e, 0x80}, + {0x6f, 0xff}, + {0x71, 0x20}, + {0x74, 0x20}, + {0x75, 0x86}, + {0x77, 0xb5}, + {0x17, 0x18}, /* H href start */ + {0x18, 0xbf}, /* H href end */ + {0x19, 0x03}, /* V start */ + {0x1a, 0xf8}, /* V end */ + {0x01, 0x80}, /* blue gain */ + {0x02, 0x80}, /* red gain */ + }; + + reg_w_buf(gspca_dev, reg_init, ARRAY_SIZE(reg_init)); + + i2c_w(gspca_dev, 0x12, 0x80); /* sensor reset */ + msleep(10); + + i2c_w_buf(gspca_dev, sensor_init, ARRAY_SIZE(sensor_init)); + + reg_w(gspca_dev, TP6800_R5C_EDGE_THRLD, 0x10); + reg_w(gspca_dev, TP6800_R54_DARK_CFG, 0x00); + + setsharpness(gspca_dev); + + bulk_w(gspca_dev, 0x03, color_gain[SENSOR_SOI763A], + ARRAY_SIZE(color_gain[0])); + + set_led(gspca_dev, 1); + setexposure(gspca_dev); + setquality(gspca_dev); + setgamma(gspca_dev); +} + +static void soi763a_6810_start(struct gspca_dev *gspca_dev) +{ + static const struct cmd bridge_init_2[] = { + {TP6800_R7A_BLK_THRLD, 0x00}, + {TP6800_R79_QUALITY, 0x04}, + {TP6800_R79_QUALITY, 0x01}, + }; + static const struct cmd bridge_init_3[] = { + {TP6800_R31_PIXEL_START, 0x20}, + {TP6800_R32_PIXEL_END_L, 0x9f}, + {TP6800_R33_PIXEL_END_H, 0x02}, + {TP6800_R34_LINE_START, 0x13}, + {TP6800_R35_LINE_END_L, 0xf8}, + {TP6800_R36_LINE_END_H, 0x01}, + }; + static const struct cmd bridge_init_6[] = { + {0x08, 0xff}, + {0x09, 0xff}, + {0x0a, 0x5f}, + {0x0b, 0x80}, + }; + + reg_w(gspca_dev, 0x22, gspca_dev->alt); + bulk_w(gspca_dev, 0x03, color_null, sizeof color_null); + reg_w(gspca_dev, 0x59, 0x40); + setexposure(gspca_dev); + reg_w_buf(gspca_dev, bridge_init_2, ARRAY_SIZE(bridge_init_2)); + reg_w_buf(gspca_dev, tp6810_ov_init_common, + ARRAY_SIZE(tp6810_ov_init_common)); + reg_w_buf(gspca_dev, bridge_init_3, ARRAY_SIZE(bridge_init_3)); + if (gspca_dev->curr_mode) { + reg_w(gspca_dev, 0x4a, 0x7f); + reg_w(gspca_dev, 0x07, 0x05); + reg_w(gspca_dev, TP6800_R78_FORMAT, 0x00); /* vga */ + } else { + reg_w(gspca_dev, 0x4a, 0xff); + reg_w(gspca_dev, 0x07, 0x85); + reg_w(gspca_dev, TP6800_R78_FORMAT, 0x01); /* qvga */ + } + setgamma(gspca_dev); + reg_w_buf(gspca_dev, tp6810_bridge_start, + ARRAY_SIZE(tp6810_bridge_start)); + + if (gspca_dev->curr_mode) { + reg_w(gspca_dev, 0x4f, 0x00); + reg_w(gspca_dev, 0x4e, 0x7c); + } + + reg_w(gspca_dev, 0x00, 0x00); + + setsharpness(gspca_dev); + bulk_w(gspca_dev, 0x03, color_gain[SENSOR_SOI763A], + ARRAY_SIZE(color_gain[0])); + set_led(gspca_dev, 1); + reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0xf0); + setexposure(gspca_dev); + reg_w_buf(gspca_dev, bridge_init_6, ARRAY_SIZE(bridge_init_6)); +} + +/* -- start the camera -- */ +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width); + set_dqt(gspca_dev, sd->quality); + if (sd->bridge == BRIDGE_TP6800) { + if (sd->sensor == SENSOR_CX0342) + cx0342_6800_start(gspca_dev); + else + soi763a_6800_start(gspca_dev); + } else { + if (sd->sensor == SENSOR_CX0342) + cx0342_6810_start(gspca_dev); + else + soi763a_6810_start(gspca_dev); + reg_w_buf(gspca_dev, tp6810_late_start, + ARRAY_SIZE(tp6810_late_start)); + reg_w(gspca_dev, 0x80, 0x03); + reg_w(gspca_dev, 0x82, gspca_dev->curr_mode ? 0x0a : 0x0e); + + setexposure(gspca_dev); + setquality(gspca_dev); + setautogain(gspca_dev); + } + + setframerate(gspca_dev); + + return gspca_dev->usb_err; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->bridge == BRIDGE_TP6800) + reg_w(gspca_dev, TP6800_R2F_TIMING_CFG, 0x03); + set_led(gspca_dev, 0); + reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x00); +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, + int len) +{ + struct sd *sd = (struct sd *) gspca_dev; + + /* the start of frame contains: + * ff d8 + * ff fe + * width / 16 + * height / 8 + * quality + */ + if (sd->bridge == BRIDGE_TP6810) { + if (*data != 0x5a) { +/*fixme: don't discard the whole frame..*/ + if (*data == 0xaa || *data == 0x00) + return; + if (*data > 0xc0) { + PDEBUG(D_FRAM, "bad frame"); + gspca_dev->last_packet_type = DISCARD_PACKET; + return; + } + } + data++; + len--; + if (*data == 0xff && data[1] == 0xd8) { +/*fixme: there may be information in the 4 high bits*/ + if ((data[6] & 0x0f) != sd->quality) + set_dqt(gspca_dev, data[6] & 0x0f); + gspca_frame_add(gspca_dev, FIRST_PACKET, + sd->jpeg_hdr, JPEG_HDR_SZ); + gspca_frame_add(gspca_dev, INTER_PACKET, + data + 7, len - 7); + } else if (data[len - 2] == 0xff && data[len - 1] == 0xd9) { + gspca_frame_add(gspca_dev, LAST_PACKET, + data, len); + } else { + gspca_frame_add(gspca_dev, INTER_PACKET, + data, len); + } + return; + } + + switch (*data) { + case 0x55: + gspca_frame_add(gspca_dev, LAST_PACKET, data, 0); + + if (len < 8 + || data[1] != 0xff || data[2] != 0xd8 + || data[3] != 0xff || data[4] != 0xfe) { + + /* Have only seen this with corrupt frames */ + gspca_dev->last_packet_type = DISCARD_PACKET; + return; + } + if (data[7] != sd->quality) + set_dqt(gspca_dev, data[7]); + gspca_frame_add(gspca_dev, FIRST_PACKET, + sd->jpeg_hdr, JPEG_HDR_SZ); + gspca_frame_add(gspca_dev, INTER_PACKET, + data + 8, len - 8); + break; + case 0xaa: + gspca_dev->last_packet_type = DISCARD_PACKET; + break; + case 0xcc: + if (data[1] != 0xff || data[2] != 0xd8) + gspca_frame_add(gspca_dev, INTER_PACKET, + data + 1, len - 1); + else + gspca_dev->last_packet_type = DISCARD_PACKET; + break; + } +} + +/* -- do autogain -- */ +/* gain setting is done in setexposure() for tp6810 */ +static void setgain(struct gspca_dev *gspca_dev) {} +/* !! coarse_grained_expo_autogain is not used !! */ +#define exp_too_low_cnt bridge +#define exp_too_high_cnt sensor + +#include "autogain_functions.h" +static void sd_dq_callback(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret, alen; + int luma, expo; + + if (sd->ag_cnt < 0) + return; + if (--sd->ag_cnt > 5) + return; + switch (sd->ag_cnt) { +/* case 5: */ + default: + reg_w(gspca_dev, 0x7d, 0x00); + break; + case 4: + reg_w(gspca_dev, 0x27, 0xb0); + break; + case 3: + reg_w(gspca_dev, 0x0c, 0x01); + break; + case 2: + ret = usb_bulk_msg(gspca_dev->dev, + usb_rcvbulkpipe(gspca_dev->dev, 0x02), + gspca_dev->usb_buf, + 32, + &alen, + 500); + if (ret < 0) { + pr_err("bulk err %d\n", ret); + break; + } + /* values not used (unknown) */ + break; + case 1: + reg_w(gspca_dev, 0x27, 0xd0); + break; + case 0: + ret = usb_bulk_msg(gspca_dev->dev, + usb_rcvbulkpipe(gspca_dev->dev, 0x02), + gspca_dev->usb_buf, + 32, + &alen, + 500); + if (ret < 0) { + pr_err("bulk err %d\n", ret); + break; + } + luma = ((gspca_dev->usb_buf[8] << 8) + gspca_dev->usb_buf[7] + + (gspca_dev->usb_buf[11] << 8) + gspca_dev->usb_buf[10] + + (gspca_dev->usb_buf[14] << 8) + gspca_dev->usb_buf[13] + + (gspca_dev->usb_buf[17] << 8) + gspca_dev->usb_buf[16] + + (gspca_dev->usb_buf[20] << 8) + gspca_dev->usb_buf[19] + + (gspca_dev->usb_buf[23] << 8) + gspca_dev->usb_buf[22] + + (gspca_dev->usb_buf[26] << 8) + gspca_dev->usb_buf[25] + + (gspca_dev->usb_buf[29] << 8) + gspca_dev->usb_buf[28]) + / 8; + if (gspca_dev->width == 640) + luma /= 4; + reg_w(gspca_dev, 0x7d, 0x00); + + expo = sd->ctrls[EXPOSURE].val; + ret = auto_gain_n_exposure(gspca_dev, luma, + 60, /* desired luma */ + 6, /* dead zone */ + 2, /* gain knee */ + 70); /* expo knee */ + sd->ag_cnt = AG_CNT_START; + if (sd->bridge == BRIDGE_TP6810) { + if ((expo >= 128 && sd->ctrls[EXPOSURE].val < 128) + || (expo < 128 && sd->ctrls[EXPOSURE].val >= 128)) + setframerate(gspca_dev); + } + break; + } +} + +/* get stream parameters (framerate) */ +static void sd_get_streamparm(struct gspca_dev *gspca_dev, + struct v4l2_streamparm *parm) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct v4l2_captureparm *cp = &parm->parm.capture; + struct v4l2_fract *tpf = &cp->timeperframe; + int fr, i; + + cp->capability |= V4L2_CAP_TIMEPERFRAME; + tpf->numerator = 1; + i = get_fr_idx(gspca_dev); + if (i & 0x80) { + if (sd->bridge == BRIDGE_TP6800) + fr = rates[6 - (i & 0x07)]; + else + fr = rates_6810[7 - (i & 0x07)]; + } else { + fr = rates[6 - i]; + } + tpf->denominator = fr; +} + +/* set stream parameters (framerate) */ +static void sd_set_streamparm(struct gspca_dev *gspca_dev, + struct v4l2_streamparm *parm) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct v4l2_captureparm *cp = &parm->parm.capture; + struct v4l2_fract *tpf = &cp->timeperframe; + int fr, i; + + sd->framerate = tpf->denominator / tpf->numerator; + if (gspca_dev->streaming) + setframerate(gspca_dev); + + /* Return the actual framerate */ + i = get_fr_idx(gspca_dev); + if (i & 0x80) + fr = rates_6810[7 - (i & 0x07)]; + else + fr = rates[6 - i]; + tpf->numerator = 1; + tpf->denominator = fr; +} + +static int sd_set_jcomp(struct gspca_dev *gspca_dev, + struct v4l2_jpegcompression *jcomp) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->sensor == SENSOR_SOI763A) + jpeg_set_qual(sd->jpeg_hdr, jcomp->quality); +/* else + fixme: TODO +*/ + return gspca_dev->usb_err; +} + +static int sd_get_jcomp(struct gspca_dev *gspca_dev, + struct v4l2_jpegcompression *jcomp) +{ + struct sd *sd = (struct sd *) gspca_dev; + + memset(jcomp, 0, sizeof *jcomp); + jcomp->quality = jpeg_q[sd->quality]; + jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT + | V4L2_JPEG_MARKER_DQT; + return 0; +} + +static struct ctrl sd_ctrls[NCTRLS] = { +[EXPOSURE] = { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", + .minimum = 0x01, + .maximum = 0xdc, + .step = 1, + .default_value = 0x4e, + }, + .set_control = setexposure + }, +[QUALITY] = { + { + .id = V4L2_CID_PRIVATE_BASE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Compression quality", + .minimum = 0, + .maximum = 15, + .step = 1, + .default_value = 13, + }, + .set_control = setquality + }, +[RGAIN] = { + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Red balance", + .minimum = 0, + .maximum = 4095, + .step = 1, + .default_value = 256, + }, + .set_control = setrgain + }, +[GAIN] = { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain", + .minimum = 0, + .maximum = 4095, + .step = 1, + .default_value = 256, + }, + .set = sd_setgain + }, +[BGAIN] = { + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Blue balance", + .minimum = 0, + .maximum = 4095, + .step = 1, + .default_value = 256, + }, + .set_control = setbgain + }, +[SHARPNESS] = { + { + .id = V4L2_CID_SHARPNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Sharpness", + .minimum = 0, + .maximum = 3, + .step = 1, + .default_value = 2, + }, + .set_control = setsharpness + }, +[GAMMA] = { + { + .id = V4L2_CID_GAMMA, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gamma", + .minimum = 0, + .maximum = NGAMMA - 1, + .step = 1, + .default_value = 1, + }, + .set_control = setgamma + }, +[AUTOGAIN] = { + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto Gain", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = AUTOGAIN_DEF + }, + .set_control = setautogain + }, +}; + +static const struct sd_desc sd_desc = { + .name = KBUILD_MODNAME, + .ctrls = sd_ctrls, + .nctrls = NCTRLS, + .config = sd_config, + .init = sd_init, + .isoc_init = sd_isoc_init, + .start = sd_start, + .stopN = sd_stopN, + .pkt_scan = sd_pkt_scan, + .dq_callback = sd_dq_callback, + .get_streamparm = sd_get_streamparm, + .set_streamparm = sd_set_streamparm, + .get_jcomp = sd_get_jcomp, + .set_jcomp = sd_set_jcomp, +}; + +static const struct usb_device_id device_table[] = { + {USB_DEVICE(0x06a2, 0x0003), .driver_info = BRIDGE_TP6800}, + {USB_DEVICE(0x06a2, 0x6810), .driver_info = BRIDGE_TP6810}, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, device_table); + +static int sd_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + return gspca_dev_probe(interface, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = KBUILD_MODNAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, + .reset_resume = gspca_resume, +#endif +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + return usb_register(&sd_driver); +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); + +module_param(force_sensor, int, 0644); +MODULE_PARM_DESC(force_sensor, + "Force sensor. 0: cx0342, 1: soi763a"); diff --git a/drivers/media/video/gspca/vc032x.c b/drivers/media/video/gspca/vc032x.c index 6caed734a06..7ee2c8271dc 100644 --- a/drivers/media/video/gspca/vc032x.c +++ b/drivers/media/video/gspca/vc032x.c @@ -20,6 +20,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #define MODULE_NAME "vc032x" #include "gspca.h" @@ -3169,7 +3171,7 @@ static void reg_r_i(struct gspca_dev *gspca_dev, index, gspca_dev->usb_buf, len, 500); if (ret < 0) { - err("reg_r err %d", ret); + pr_err("reg_r err %d\n", ret); gspca_dev->usb_err = ret; } } @@ -3210,7 +3212,7 @@ static void reg_w_i(struct gspca_dev *gspca_dev, value, index, NULL, 0, 500); if (ret < 0) { - err("reg_w err %d", ret); + pr_err("reg_w err %d\n", ret); gspca_dev->usb_err = ret; } } @@ -3235,8 +3237,7 @@ static u16 read_sensor_register(struct gspca_dev *gspca_dev, reg_r(gspca_dev, 0xa1, 0xb33f, 1); if (!(gspca_dev->usb_buf[0] & 0x02)) { - err("I2c Bus Busy Wait %02x", - gspca_dev->usb_buf[0]); + pr_err("I2c Bus Busy Wait %02x\n", gspca_dev->usb_buf[0]); return 0; } reg_w(gspca_dev, 0xa0, address, 0xb33a); @@ -3349,7 +3350,7 @@ static void i2c_write(struct gspca_dev *gspca_dev, msleep(20); } while (--retry > 0); if (retry <= 0) - err("i2c_write timeout"); + pr_err("i2c_write timeout\n"); } static void put_tab_to_reg(struct gspca_dev *gspca_dev, @@ -3446,7 +3447,7 @@ static int sd_init(struct gspca_dev *gspca_dev) switch (sensor) { case -1: - err("Unknown sensor..."); + pr_err("Unknown sensor...\n"); return -EINVAL; case SENSOR_HV7131R: PDEBUG(D_PROBE, "Find Sensor HV7131R"); diff --git a/drivers/media/video/gspca/vicam.c b/drivers/media/video/gspca/vicam.c index 84dfbab923b..81dd4c99d02 100644 --- a/drivers/media/video/gspca/vicam.c +++ b/drivers/media/video/gspca/vicam.c @@ -26,6 +26,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #define MODULE_NAME "vicam" #define HEADER_SIZE 64 @@ -117,7 +119,7 @@ static int vicam_control_msg(struct gspca_dev *gspca_dev, u8 request, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, value, index, data, len, 1000); if (ret < 0) - err("control msg req %02X error %d", request, ret); + pr_err("control msg req %02X error %d\n", request, ret); return ret; } @@ -189,8 +191,8 @@ static int vicam_read_frame(struct gspca_dev *gspca_dev, u8 *data, int size) data, size, &act_len, 10000); /* successful, it returns 0, otherwise negative */ if (ret < 0 || act_len != size) { - err("bulk read fail (%d) len %d/%d", - ret, act_len, size); + pr_err("bulk read fail (%d) len %d/%d\n", + ret, act_len, size); return -EIO; } return 0; @@ -216,7 +218,7 @@ static void vicam_dostream(struct work_struct *work) HEADER_SIZE; buffer = kmalloc(frame_sz, GFP_KERNEL | GFP_DMA); if (!buffer) { - err("Couldn't allocate USB buffer"); + pr_err("Couldn't allocate USB buffer\n"); goto exit; } @@ -269,7 +271,7 @@ static int sd_init(struct gspca_dev *gspca_dev) ret = request_ihex_firmware(&fw, "vicam/firmware.fw", &gspca_dev->dev->dev); if (ret) { - err("Failed to load \"vicam/firmware.fw\": %d\n", ret); + pr_err("Failed to load \"vicam/firmware.fw\": %d\n", ret); return ret; } diff --git a/drivers/media/video/gspca/w996Xcf.c b/drivers/media/video/gspca/w996Xcf.c index 4a9e622e5e1..27d2cef0692 100644 --- a/drivers/media/video/gspca/w996Xcf.c +++ b/drivers/media/video/gspca/w996Xcf.c @@ -31,6 +31,8 @@ the sensor drivers to v4l2 sub drivers, and properly split of this driver from ov519.c */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #define W9968CF_I2C_BUS_DELAY 4 /* delay in us for I2C bit r/w operations */ #define Y_QUANTABLE (&sd->jpeg_hdr[JPEG_QT0_OFFSET]) @@ -81,7 +83,7 @@ static void w9968cf_write_fsb(struct sd *sd, u16* data) USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, value, 0x06, sd->gspca_dev.usb_buf, 6, 500); if (ret < 0) { - err("Write FSB registers failed (%d)", ret); + pr_err("Write FSB registers failed (%d)\n", ret); sd->gspca_dev.usb_err = ret; } } @@ -108,7 +110,7 @@ static void w9968cf_write_sb(struct sd *sd, u16 value) udelay(W9968CF_I2C_BUS_DELAY); if (ret < 0) { - err("Write SB reg [01] %04x failed", value); + pr_err("Write SB reg [01] %04x failed\n", value); sd->gspca_dev.usb_err = ret; } } @@ -135,7 +137,7 @@ static int w9968cf_read_sb(struct sd *sd) ret = sd->gspca_dev.usb_buf[0] | (sd->gspca_dev.usb_buf[1] << 8); } else { - err("Read SB reg [01] failed"); + pr_err("Read SB reg [01] failed\n"); sd->gspca_dev.usb_err = ret; } diff --git a/drivers/media/video/gspca/xirlink_cit.c b/drivers/media/video/gspca/xirlink_cit.c index c089a0f6f1d..3aed42acdb5 100644 --- a/drivers/media/video/gspca/xirlink_cit.c +++ b/drivers/media/video/gspca/xirlink_cit.c @@ -27,6 +27,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #define MODULE_NAME "xirlink-cit" #include <linux/input.h> @@ -800,8 +802,8 @@ static int cit_write_reg(struct gspca_dev *gspca_dev, u16 value, u16 index) USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT, value, index, NULL, 0, 1000); if (err < 0) - err("Failed to write a register (index 0x%04X," - " value 0x%02X, error %d)", index, value, err); + pr_err("Failed to write a register (index 0x%04X, value 0x%02X, error %d)\n", + index, value, err); return 0; } @@ -816,8 +818,8 @@ static int cit_read_reg(struct gspca_dev *gspca_dev, u16 index, int verbose) USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT, 0x00, index, buf, 8, 1000); if (res < 0) { - err("Failed to read a register (index 0x%04X, error %d)", - index, res); + pr_err("Failed to read a register (index 0x%04X, error %d)\n", + index, res); return res; } @@ -1587,7 +1589,7 @@ static int cit_get_packet_size(struct gspca_dev *gspca_dev) intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface); alt = usb_altnum_to_altsetting(intf, gspca_dev->alt); if (!alt) { - err("Couldn't get altsetting"); + pr_err("Couldn't get altsetting\n"); return -EIO; } @@ -2824,7 +2826,7 @@ static int sd_isoc_nego(struct gspca_dev *gspca_dev) ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, 1); if (ret < 0) - err("set alt 1 err %d", ret); + pr_err("set alt 1 err %d\n", ret); return ret; } diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c index 61cdd56a74a..30ea1e47949 100644 --- a/drivers/media/video/gspca/zc3xx.c +++ b/drivers/media/video/gspca/zc3xx.c @@ -19,6 +19,8 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #define MODULE_NAME "zc3xx" #include <linux/input.h> @@ -5666,7 +5668,7 @@ static u8 reg_r_i(struct gspca_dev *gspca_dev, index, gspca_dev->usb_buf, 1, 500); if (ret < 0) { - err("reg_r_i err %d", ret); + pr_err("reg_r_i err %d\n", ret); gspca_dev->usb_err = ret; return 0; } @@ -5698,7 +5700,7 @@ static void reg_w_i(struct gspca_dev *gspca_dev, value, index, NULL, 0, 500); if (ret < 0) { - err("reg_w_i err %d", ret); + pr_err("reg_w_i err %d\n", ret); gspca_dev->usb_err = ret; } } @@ -5724,7 +5726,7 @@ static u16 i2c_read(struct gspca_dev *gspca_dev, msleep(20); retbyte = reg_r_i(gspca_dev, 0x0091); /* read status */ if (retbyte != 0x00) - err("i2c_r status error %02x", retbyte); + pr_err("i2c_r status error %02x\n", retbyte); retval = reg_r_i(gspca_dev, 0x0095); /* read Lowbyte */ retval |= reg_r_i(gspca_dev, 0x0096) << 8; /* read Hightbyte */ PDEBUG(D_USBI, "i2c r [%02x] -> %04x (%02x)", @@ -5748,7 +5750,7 @@ static u8 i2c_write(struct gspca_dev *gspca_dev, msleep(1); retbyte = reg_r_i(gspca_dev, 0x0091); /* read status */ if (retbyte != 0x00) - err("i2c_w status error %02x", retbyte); + pr_err("i2c_w status error %02x\n", retbyte); PDEBUG(D_USBO, "i2c w [%02x] = %02x%02x (%02x)", reg, valH, valL, retbyte); return retbyte; @@ -6497,7 +6499,7 @@ static int sd_init(struct gspca_dev *gspca_dev) PDEBUG(D_PROBE, "Sensor GC0303"); break; default: - warn("Unknown sensor - set to TAS5130C"); + pr_warn("Unknown sensor - set to TAS5130C\n"); sd->sensor = SENSOR_TAS5130C; } break; @@ -6603,7 +6605,7 @@ static int sd_init(struct gspca_dev *gspca_dev) sd->sensor = SENSOR_OV7620; /* same sensor (?) */ break; default: - err("Unknown sensor %04x", sensor); + pr_err("Unknown sensor %04x\n", sensor); return -EINVAL; } } @@ -6970,6 +6972,7 @@ static const struct sd_desc sd_desc = { }; static const struct usb_device_id device_table[] = { + {USB_DEVICE(0x03f0, 0x1b07)}, {USB_DEVICE(0x041e, 0x041e)}, {USB_DEVICE(0x041e, 0x4017)}, {USB_DEVICE(0x041e, 0x401c), .driver_info = SENSOR_PAS106}, diff --git a/drivers/media/video/hdpvr/Makefile b/drivers/media/video/hdpvr/Makefile index 3baa9f613ca..52f057f24e3 100644 --- a/drivers/media/video/hdpvr/Makefile +++ b/drivers/media/video/hdpvr/Makefile @@ -2,6 +2,6 @@ hdpvr-objs := hdpvr-control.o hdpvr-core.o hdpvr-video.o hdpvr-i2c.o obj-$(CONFIG_VIDEO_HDPVR) += hdpvr.o -EXTRA_CFLAGS += -Idrivers/media/video +ccflags-y += -Idrivers/media/video -EXTRA_CFLAGS += $(extra-cflags-y) $(extra-cflags-m) +ccflags-y += $(extra-cflags-y) $(extra-cflags-m) diff --git a/drivers/media/video/hdpvr/hdpvr-i2c.c b/drivers/media/video/hdpvr/hdpvr-i2c.c index 2a1ac287591..82e819fa91c 100644 --- a/drivers/media/video/hdpvr/hdpvr-i2c.c +++ b/drivers/media/video/hdpvr/hdpvr-i2c.c @@ -17,6 +17,7 @@ #include <linux/i2c.h> #include <linux/slab.h> +#include <linux/export.h> #include "hdpvr.h" diff --git a/drivers/media/video/hexium_gemini.c b/drivers/media/video/hexium_gemini.c index cbc505a2fc2..a62322d5c0d 100644 --- a/drivers/media/video/hexium_gemini.c +++ b/drivers/media/video/hexium_gemini.c @@ -21,9 +21,12 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #define DEBUG_VARIABLE debug #include <media/saa7146_vv.h> +#include <linux/module.h> static int debug; module_param(debug, int, 0); @@ -175,13 +178,14 @@ static int hexium_init_done(struct saa7146_dev *dev) union i2c_smbus_data data; int i = 0; - DEB_D(("hexium_init_done called.\n")); + DEB_D("hexium_init_done called\n"); /* initialize the helper ics to useful values */ for (i = 0; i < sizeof(hexium_ks0127b); i++) { data.byte = hexium_ks0127b[i]; if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x6c, 0, I2C_SMBUS_WRITE, i, I2C_SMBUS_BYTE_DATA, &data)) { - printk("hexium_gemini: hexium_init_done() failed for address 0x%02x\n", i); + pr_err("hexium_init_done() failed for address 0x%02x\n", + i); } } @@ -192,7 +196,7 @@ static int hexium_set_input(struct hexium *hexium, int input) { union i2c_smbus_data data; - DEB_D((".\n")); + DEB_D("\n"); data.byte = hexium_input_select[input].byte; if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x6c, 0, I2C_SMBUS_WRITE, hexium_input_select[input].adr, I2C_SMBUS_BYTE_DATA, &data)) { @@ -207,12 +211,13 @@ static int hexium_set_standard(struct hexium *hexium, struct hexium_data *vdec) union i2c_smbus_data data; int i = 0; - DEB_D((".\n")); + DEB_D("\n"); while (vdec[i].adr != -1) { data.byte = vdec[i].byte; if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x6c, 0, I2C_SMBUS_WRITE, vdec[i].adr, I2C_SMBUS_BYTE_DATA, &data)) { - printk("hexium_init_done: hexium_set_standard() failed for address 0x%02x\n", i); + pr_err("hexium_init_done: hexium_set_standard() failed for address 0x%02x\n", + i); return -1; } i++; @@ -222,14 +227,14 @@ static int hexium_set_standard(struct hexium *hexium, struct hexium_data *vdec) static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i) { - DEB_EE(("VIDIOC_ENUMINPUT %d.\n", i->index)); + DEB_EE("VIDIOC_ENUMINPUT %d\n", i->index); if (i->index >= HEXIUM_INPUTS) return -EINVAL; memcpy(i, &hexium_inputs[i->index], sizeof(struct v4l2_input)); - DEB_D(("v4l2_ioctl: VIDIOC_ENUMINPUT %d.\n", i->index)); + DEB_D("v4l2_ioctl: VIDIOC_ENUMINPUT %d\n", i->index); return 0; } @@ -240,7 +245,7 @@ static int vidioc_g_input(struct file *file, void *fh, unsigned int *input) *input = hexium->cur_input; - DEB_D(("VIDIOC_G_INPUT: %d\n", *input)); + DEB_D("VIDIOC_G_INPUT: %d\n", *input); return 0; } @@ -249,7 +254,7 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int input) struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; struct hexium *hexium = (struct hexium *) dev->ext_priv; - DEB_EE(("VIDIOC_S_INPUT %d.\n", input)); + DEB_EE("VIDIOC_S_INPUT %d\n", input); if (input >= HEXIUM_INPUTS) return -EINVAL; @@ -270,7 +275,7 @@ static int vidioc_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl * for (i = HEXIUM_CONTROLS - 1; i >= 0; i--) { if (hexium_controls[i].id == qc->id) { *qc = hexium_controls[i]; - DEB_D(("VIDIOC_QUERYCTRL %d.\n", qc->id)); + DEB_D("VIDIOC_QUERYCTRL %d\n", qc->id); return 0; } } @@ -293,7 +298,7 @@ static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *vc) if (vc->id == V4L2_CID_PRIVATE_BASE) { vc->value = hexium->cur_bw; - DEB_D(("VIDIOC_G_CTRL BW:%d.\n", vc->value)); + DEB_D("VIDIOC_G_CTRL BW:%d\n", vc->value); return 0; } return -EINVAL; @@ -316,7 +321,7 @@ static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *vc) if (vc->id == V4L2_CID_PRIVATE_BASE) hexium->cur_bw = vc->value; - DEB_D(("VIDIOC_S_CTRL BW:%d.\n", hexium->cur_bw)); + DEB_D("VIDIOC_S_CTRL BW:%d\n", hexium->cur_bw); if (0 == hexium->cur_bw && V4L2_STD_PAL == hexium->cur_std) { hexium_set_standard(hexium, hexium_pal); @@ -351,14 +356,14 @@ static struct saa7146_ext_vv vv_data; /* this function only gets called when the probing was successful */ static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info) { - struct hexium *hexium = (struct hexium *) dev->ext_priv; + struct hexium *hexium; int ret; - DEB_EE((".\n")); + DEB_EE("\n"); hexium = kzalloc(sizeof(struct hexium), GFP_KERNEL); if (NULL == hexium) { - printk("hexium_gemini: not enough kernel memory in hexium_attach().\n"); + pr_err("not enough kernel memory in hexium_attach()\n"); return -ENOMEM; } dev->ext_priv = hexium; @@ -371,7 +376,7 @@ static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_d }; saa7146_i2c_adapter_prepare(dev, &hexium->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480); if (i2c_add_adapter(&hexium->i2c_adapter) < 0) { - DEB_S(("cannot register i2c-device. skipping.\n")); + DEB_S("cannot register i2c-device. skipping.\n"); kfree(hexium); return -EFAULT; } @@ -402,11 +407,11 @@ static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_d vv_data.ops.vidioc_s_input = vidioc_s_input; ret = saa7146_register_device(&hexium->video_dev, dev, "hexium gemini", VFL_TYPE_GRABBER); if (ret < 0) { - printk("hexium_gemini: cannot register capture v4l2 device. skipping.\n"); + pr_err("cannot register capture v4l2 device. skipping.\n"); return ret; } - printk("hexium_gemini: found 'hexium gemini' frame grabber-%d.\n", hexium_num); + pr_info("found 'hexium gemini' frame grabber-%d\n", hexium_num); hexium_num++; return 0; @@ -416,7 +421,7 @@ static int hexium_detach(struct saa7146_dev *dev) { struct hexium *hexium = (struct hexium *) dev->ext_priv; - DEB_EE(("dev:%p\n", dev)); + DEB_EE("dev:%p\n", dev); saa7146_unregister_device(&hexium->video_dev, dev); saa7146_vv_release(dev); @@ -508,7 +513,7 @@ static struct saa7146_extension hexium_extension = { static int __init hexium_init_module(void) { if (0 != saa7146_register_extension(&hexium_extension)) { - DEB_S(("failed to register extension.\n")); + DEB_S("failed to register extension\n"); return -ENODEV; } diff --git a/drivers/media/video/hexium_orion.c b/drivers/media/video/hexium_orion.c index 6ad7e1c8b92..23debc967d9 100644 --- a/drivers/media/video/hexium_orion.c +++ b/drivers/media/video/hexium_orion.c @@ -21,9 +21,12 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #define DEBUG_VARIABLE debug #include <media/saa7146_vv.h> +#include <linux/module.h> static int debug; module_param(debug, int, 0); @@ -209,7 +212,7 @@ static int hexium_probe(struct saa7146_dev *dev) union i2c_smbus_data data; int err = 0; - DEB_EE((".\n")); + DEB_EE("\n"); /* there are no hexium orion cards with revision 0 saa7146s */ if (0 == dev->revision) { @@ -218,7 +221,7 @@ static int hexium_probe(struct saa7146_dev *dev) hexium = kzalloc(sizeof(struct hexium), GFP_KERNEL); if (NULL == hexium) { - printk("hexium_orion: hexium_probe: not enough kernel memory.\n"); + pr_err("hexium_probe: not enough kernel memory\n"); return -ENOMEM; } @@ -234,7 +237,7 @@ static int hexium_probe(struct saa7146_dev *dev) }; saa7146_i2c_adapter_prepare(dev, &hexium->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480); if (i2c_add_adapter(&hexium->i2c_adapter) < 0) { - DEB_S(("cannot register i2c-device. skipping.\n")); + DEB_S("cannot register i2c-device. skipping.\n"); kfree(hexium); return -EFAULT; } @@ -248,7 +251,7 @@ static int hexium_probe(struct saa7146_dev *dev) /* detect newer Hexium Orion cards by subsystem ids */ if (0x17c8 == dev->pci->subsystem_vendor && 0x0101 == dev->pci->subsystem_device) { - printk("hexium_orion: device is a Hexium Orion w/ 1 SVHS + 3 BNC inputs.\n"); + pr_info("device is a Hexium Orion w/ 1 SVHS + 3 BNC inputs\n"); /* we store the pointer in our private data field */ dev->ext_priv = hexium; hexium->type = HEXIUM_ORION_1SVHS_3BNC; @@ -256,7 +259,7 @@ static int hexium_probe(struct saa7146_dev *dev) } if (0x17c8 == dev->pci->subsystem_vendor && 0x2101 == dev->pci->subsystem_device) { - printk("hexium_orion: device is a Hexium Orion w/ 4 BNC inputs.\n"); + pr_info("device is a Hexium Orion w/ 4 BNC inputs\n"); /* we store the pointer in our private data field */ dev->ext_priv = hexium; hexium->type = HEXIUM_ORION_4BNC; @@ -266,7 +269,7 @@ static int hexium_probe(struct saa7146_dev *dev) /* check if this is an old hexium Orion card by looking at a saa7110 at address 0x4e */ if (0 == (err = i2c_smbus_xfer(&hexium->i2c_adapter, 0x4e, 0, I2C_SMBUS_READ, 0x00, I2C_SMBUS_BYTE_DATA, &data))) { - printk("hexium_orion: device is a Hexium HV-PCI6/Orion (old).\n"); + pr_info("device is a Hexium HV-PCI6/Orion (old)\n"); /* we store the pointer in our private data field */ dev->ext_priv = hexium; hexium->type = HEXIUM_HV_PCI6_ORION; @@ -288,13 +291,13 @@ static int hexium_init_done(struct saa7146_dev *dev) union i2c_smbus_data data; int i = 0; - DEB_D(("hexium_init_done called.\n")); + DEB_D("hexium_init_done called\n"); /* initialize the helper ics to useful values */ for (i = 0; i < sizeof(hexium_saa7110); i++) { data.byte = hexium_saa7110[i]; if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x4e, 0, I2C_SMBUS_WRITE, i, I2C_SMBUS_BYTE_DATA, &data)) { - printk("hexium_orion: failed for address 0x%02x\n", i); + pr_err("failed for address 0x%02x\n", i); } } @@ -306,7 +309,7 @@ static int hexium_set_input(struct hexium *hexium, int input) union i2c_smbus_data data; int i = 0; - DEB_D((".\n")); + DEB_D("\n"); for (i = 0; i < 8; i++) { int adr = hexium_input_select[input].data[i].adr; @@ -314,7 +317,7 @@ static int hexium_set_input(struct hexium *hexium, int input) if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x4e, 0, I2C_SMBUS_WRITE, adr, I2C_SMBUS_BYTE_DATA, &data)) { return -1; } - printk("%d: 0x%02x => 0x%02x\n",input, adr,data.byte); + pr_debug("%d: 0x%02x => 0x%02x\n", input, adr, data.byte); } return 0; @@ -322,14 +325,14 @@ static int hexium_set_input(struct hexium *hexium, int input) static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i) { - DEB_EE(("VIDIOC_ENUMINPUT %d.\n", i->index)); + DEB_EE("VIDIOC_ENUMINPUT %d\n", i->index); if (i->index >= HEXIUM_INPUTS) return -EINVAL; memcpy(i, &hexium_inputs[i->index], sizeof(struct v4l2_input)); - DEB_D(("v4l2_ioctl: VIDIOC_ENUMINPUT %d.\n", i->index)); + DEB_D("v4l2_ioctl: VIDIOC_ENUMINPUT %d\n", i->index); return 0; } @@ -340,7 +343,7 @@ static int vidioc_g_input(struct file *file, void *fh, unsigned int *input) *input = hexium->cur_input; - DEB_D(("VIDIOC_G_INPUT: %d\n", *input)); + DEB_D("VIDIOC_G_INPUT: %d\n", *input); return 0; } @@ -365,18 +368,18 @@ static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_d { struct hexium *hexium = (struct hexium *) dev->ext_priv; - DEB_EE((".\n")); + DEB_EE("\n"); saa7146_vv_init(dev, &vv_data); vv_data.ops.vidioc_enum_input = vidioc_enum_input; vv_data.ops.vidioc_g_input = vidioc_g_input; vv_data.ops.vidioc_s_input = vidioc_s_input; if (0 != saa7146_register_device(&hexium->video_dev, dev, "hexium orion", VFL_TYPE_GRABBER)) { - printk("hexium_orion: cannot register capture v4l2 device. skipping.\n"); + pr_err("cannot register capture v4l2 device. skipping.\n"); return -1; } - printk("hexium_orion: found 'hexium orion' frame grabber-%d.\n", hexium_num); + pr_err("found 'hexium orion' frame grabber-%d\n", hexium_num); hexium_num++; /* the rest */ @@ -390,7 +393,7 @@ static int hexium_detach(struct saa7146_dev *dev) { struct hexium *hexium = (struct hexium *) dev->ext_priv; - DEB_EE(("dev:%p\n", dev)); + DEB_EE("dev:%p\n", dev); saa7146_unregister_device(&hexium->video_dev, dev); saa7146_vv_release(dev); @@ -479,7 +482,7 @@ static struct saa7146_extension extension = { static int __init hexium_init_module(void) { if (0 != saa7146_register_extension(&extension)) { - DEB_S(("failed to register extension.\n")); + DEB_S("failed to register extension\n"); return -ENODEV; } diff --git a/drivers/media/video/imx074.c b/drivers/media/video/imx074.c index 0382ea752e6..eec75bb5720 100644 --- a/drivers/media/video/imx074.c +++ b/drivers/media/video/imx074.c @@ -12,11 +12,12 @@ #include <linux/delay.h> #include <linux/i2c.h> +#include <linux/v4l2-mediabus.h> #include <linux/slab.h> #include <linux/videodev2.h> +#include <linux/module.h> #include <media/soc_camera.h> -#include <media/soc_mediabus.h> #include <media/v4l2-subdev.h> #include <media/v4l2-chip-ident.h> @@ -267,6 +268,17 @@ static int imx074_g_chip_ident(struct v4l2_subdev *sd, return 0; } +static int imx074_g_mbus_config(struct v4l2_subdev *sd, + struct v4l2_mbus_config *cfg) +{ + cfg->type = V4L2_MBUS_CSI2; + cfg->flags = V4L2_MBUS_CSI2_2_LANE | + V4L2_MBUS_CSI2_CHANNEL_0 | + V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; + + return 0; +} + static struct v4l2_subdev_video_ops imx074_subdev_video_ops = { .s_stream = imx074_s_stream, .s_mbus_fmt = imx074_s_fmt, @@ -275,6 +287,7 @@ static struct v4l2_subdev_video_ops imx074_subdev_video_ops = { .enum_mbus_fmt = imx074_enum_fmt, .g_crop = imx074_g_crop, .cropcap = imx074_cropcap, + .g_mbus_config = imx074_g_mbus_config, }; static struct v4l2_subdev_core_ops imx074_subdev_core_ops = { @@ -286,28 +299,7 @@ static struct v4l2_subdev_ops imx074_subdev_ops = { .video = &imx074_subdev_video_ops, }; -/* - * We have to provide soc-camera operations, but we don't have anything to say - * there. The MIPI CSI2 driver will provide .query_bus_param and .set_bus_param - */ -static unsigned long imx074_query_bus_param(struct soc_camera_device *icd) -{ - return 0; -} - -static int imx074_set_bus_param(struct soc_camera_device *icd, - unsigned long flags) -{ - return -EINVAL; -} - -static struct soc_camera_ops imx074_ops = { - .query_bus_param = imx074_query_bus_param, - .set_bus_param = imx074_set_bus_param, -}; - -static int imx074_video_probe(struct soc_camera_device *icd, - struct i2c_client *client) +static int imx074_video_probe(struct i2c_client *client) { int ret; u16 id; @@ -417,17 +409,10 @@ static int imx074_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct imx074 *priv; - struct soc_camera_device *icd = client->dev.platform_data; struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - struct soc_camera_link *icl; + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); int ret; - if (!icd) { - dev_err(&client->dev, "IMX074: missing soc-camera data!\n"); - return -EINVAL; - } - - icl = to_soc_camera_link(icd); if (!icl) { dev_err(&client->dev, "IMX074: missing platform data!\n"); return -EINVAL; @@ -445,12 +430,10 @@ static int imx074_probe(struct i2c_client *client, v4l2_i2c_subdev_init(&priv->subdev, client, &imx074_subdev_ops); - icd->ops = &imx074_ops; priv->fmt = &imx074_colour_fmts[0]; - ret = imx074_video_probe(icd, client); + ret = imx074_video_probe(client); if (ret < 0) { - icd->ops = NULL; kfree(priv); return ret; } @@ -461,10 +444,8 @@ static int imx074_probe(struct i2c_client *client, static int imx074_remove(struct i2c_client *client) { struct imx074 *priv = to_imx074(client); - struct soc_camera_device *icd = client->dev.platform_data; - struct soc_camera_link *icl = to_soc_camera_link(icd); + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); - icd->ops = NULL; if (icl->free_bus) icl->free_bus(icl); kfree(priv); diff --git a/drivers/media/video/ivtv/Makefile b/drivers/media/video/ivtv/Makefile index 26ce0d6eaee..71ab76a5ab2 100644 --- a/drivers/media/video/ivtv/Makefile +++ b/drivers/media/video/ivtv/Makefile @@ -7,8 +7,8 @@ ivtv-objs := ivtv-routing.o ivtv-cards.o ivtv-controls.o \ obj-$(CONFIG_VIDEO_IVTV) += ivtv.o obj-$(CONFIG_VIDEO_FB_IVTV) += ivtvfb.o -EXTRA_CFLAGS += -Idrivers/media/video -EXTRA_CFLAGS += -Idrivers/media/common/tuners -EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core -EXTRA_CFLAGS += -Idrivers/media/dvb/frontends +ccflags-y += -Idrivers/media/video +ccflags-y += -Idrivers/media/common/tuners +ccflags-y += -Idrivers/media/dvb/dvb-core +ccflags-y += -Idrivers/media/dvb/frontends diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c index 0fb75524484..41108a9a195 100644 --- a/drivers/media/video/ivtv/ivtv-driver.c +++ b/drivers/media/video/ivtv/ivtv-driver.c @@ -1180,6 +1180,8 @@ static int __devinit ivtv_probe(struct pci_dev *pdev, setup.addr = ADDR_UNSET; setup.type = itv->options.tuner; setup.mode_mask = T_ANALOG_TV; /* matches TV tuners */ + if (itv->options.radio > 0) + setup.mode_mask |= T_RADIO; setup.tuner_callback = (setup.type == TUNER_XC2028) ? ivtv_reset_tuner_gpio : NULL; ivtv_call_all(itv, tuner, s_type_addr, &setup); diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c index 3e5c090af11..ecafa697326 100644 --- a/drivers/media/video/ivtv/ivtv-ioctl.c +++ b/drivers/media/video/ivtv/ivtv-ioctl.c @@ -1203,9 +1203,7 @@ static int ivtv_g_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_sliced cap->service_lines[f][l] = set; } } - return 0; - } - if (cap->type == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) { + } else if (cap->type == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) { if (!(itv->v4l2_cap & V4L2_CAP_SLICED_VBI_OUTPUT)) return -EINVAL; if (itv->is_60hz) { @@ -1215,9 +1213,16 @@ static int ivtv_g_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_sliced cap->service_lines[0][23] = V4L2_SLICED_WSS_625; cap->service_lines[0][16] = V4L2_SLICED_VPS; } - return 0; + } else { + return -EINVAL; } - return -EINVAL; + + set = 0; + for (f = 0; f < 2; f++) + for (l = 0; l < 24; l++) + set |= cap->service_lines[f][l]; + cap->service_set = set; + return 0; } static int ivtv_g_enc_index(struct file *file, void *fh, struct v4l2_enc_idx *idx) diff --git a/drivers/media/video/m5mols/m5mols_core.c b/drivers/media/video/m5mols/m5mols_core.c index fb8e4a7a9dd..05ab3700647 100644 --- a/drivers/media/video/m5mols/m5mols_core.c +++ b/drivers/media/video/m5mols/m5mols_core.c @@ -21,6 +21,7 @@ #include <linux/gpio.h> #include <linux/regulator/consumer.h> #include <linux/videodev2.h> +#include <linux/module.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> #include <media/v4l2-subdev.h> @@ -936,7 +937,7 @@ static int __devinit m5mols_probe(struct i2c_client *client, return -EINVAL; } - if (!pdata->irq) { + if (!client->irq) { dev_err(&client->dev, "Interrupt not assigned\n"); return -EINVAL; } @@ -973,7 +974,7 @@ static int __devinit m5mols_probe(struct i2c_client *client, init_waitqueue_head(&info->irq_waitq); INIT_WORK(&info->work_irq, m5mols_irq_work); - ret = request_irq(pdata->irq, m5mols_irq_handler, + ret = request_irq(client->irq, m5mols_irq_handler, IRQF_TRIGGER_RISING, MODULE_NAME, sd); if (ret) { dev_err(&client->dev, "Interrupt request failed: %d\n", ret); @@ -998,7 +999,7 @@ static int __devexit m5mols_remove(struct i2c_client *client) struct m5mols_info *info = to_m5mols(sd); v4l2_device_unregister_subdev(sd); - free_irq(info->pdata->irq, sd); + free_irq(client->irq, sd); regulator_bulk_free(ARRAY_SIZE(supplies), supplies); gpio_free(info->pdata->gpio_reset); diff --git a/drivers/media/video/marvell-ccic/mcam-core.c b/drivers/media/video/marvell-ccic/mcam-core.c index 83c14514cd5..80ec64d2d6d 100644 --- a/drivers/media/video/marvell-ccic/mcam-core.c +++ b/drivers/media/video/marvell-ccic/mcam-core.c @@ -450,7 +450,7 @@ static void mcam_set_contig_buffer(struct mcam_camera *cam, int frame) buf = cam->vb_bufs[frame ^ 0x1]; cam->vb_bufs[frame] = buf; mcam_reg_write(cam, frame == 0 ? REG_Y0BAR : REG_Y1BAR, - vb2_dma_contig_plane_paddr(&buf->vb_buf, 0)); + vb2_dma_contig_plane_dma_addr(&buf->vb_buf, 0)); set_bit(CF_SINGLE_BUFFER, &cam->flags); singles++; return; @@ -461,7 +461,7 @@ static void mcam_set_contig_buffer(struct mcam_camera *cam, int frame) buf = list_first_entry(&cam->buffers, struct mcam_vb_buffer, queue); list_del_init(&buf->queue); mcam_reg_write(cam, frame == 0 ? REG_Y0BAR : REG_Y1BAR, - vb2_dma_contig_plane_paddr(&buf->vb_buf, 0)); + vb2_dma_contig_plane_dma_addr(&buf->vb_buf, 0)); cam->vb_bufs[frame] = buf; clear_bit(CF_SINGLE_BUFFER, &cam->flags); } @@ -883,8 +883,9 @@ static int mcam_read_setup(struct mcam_camera *cam) * Videobuf2 interface code. */ -static int mcam_vb_queue_setup(struct vb2_queue *vq, unsigned int *nbufs, - unsigned int *num_planes, unsigned long sizes[], +static int mcam_vb_queue_setup(struct vb2_queue *vq, + const struct v4l2_format *fmt, unsigned int *nbufs, + unsigned int *num_planes, unsigned int sizes[], void *alloc_ctxs[]) { struct mcam_camera *cam = vb2_get_drv_priv(vq); @@ -940,12 +941,14 @@ static void mcam_vb_wait_finish(struct vb2_queue *vq) /* * These need to be called with the mutex held from vb2 */ -static int mcam_vb_start_streaming(struct vb2_queue *vq) +static int mcam_vb_start_streaming(struct vb2_queue *vq, unsigned int count) { struct mcam_camera *cam = vb2_get_drv_priv(vq); - if (cam->state != S_IDLE) + if (cam->state != S_IDLE) { + INIT_LIST_HEAD(&cam->buffers); return -EINVAL; + } cam->sequence = 0; /* * Videobuf2 sneakily hoards all the buffers and won't diff --git a/drivers/media/video/marvell-ccic/mmp-driver.c b/drivers/media/video/marvell-ccic/mmp-driver.c index d6b76454137..fb0b124b35f 100644 --- a/drivers/media/video/marvell-ccic/mmp-driver.c +++ b/drivers/media/video/marvell-ccic/mmp-driver.c @@ -29,6 +29,7 @@ #include "mcam-core.h" +MODULE_ALIAS("platform:mmp-camera"); MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/mem2mem_testdev.c b/drivers/media/video/mem2mem_testdev.c index 166bf9349c1..12897e8a331 100644 --- a/drivers/media/video/mem2mem_testdev.c +++ b/drivers/media/video/mem2mem_testdev.c @@ -738,9 +738,10 @@ static const struct v4l2_ioctl_ops m2mtest_ioctl_ops = { * Queue operations */ -static int m2mtest_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, - unsigned int *nplanes, unsigned long sizes[], - void *alloc_ctxs[]) +static int m2mtest_queue_setup(struct vb2_queue *vq, + const struct v4l2_format *fmt, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) { struct m2mtest_ctx *ctx = vb2_get_drv_priv(vq); struct m2mtest_q_data *q_data; @@ -793,10 +794,24 @@ static void m2mtest_buf_queue(struct vb2_buffer *vb) v4l2_m2m_buf_queue(ctx->m2m_ctx, vb); } +static void m2mtest_wait_prepare(struct vb2_queue *q) +{ + struct m2mtest_ctx *ctx = vb2_get_drv_priv(q); + m2mtest_unlock(ctx); +} + +static void m2mtest_wait_finish(struct vb2_queue *q) +{ + struct m2mtest_ctx *ctx = vb2_get_drv_priv(q); + m2mtest_lock(ctx); +} + static struct vb2_ops m2mtest_qops = { .queue_setup = m2mtest_queue_setup, .buf_prepare = m2mtest_buf_prepare, .buf_queue = m2mtest_buf_queue, + .wait_prepare = m2mtest_wait_prepare, + .wait_finish = m2mtest_wait_finish, }; static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) diff --git a/drivers/media/video/msp3400-driver.c b/drivers/media/video/msp3400-driver.c index c43c81f5f97..d0f53885728 100644 --- a/drivers/media/video/msp3400-driver.c +++ b/drivers/media/video/msp3400-driver.c @@ -426,6 +426,20 @@ static int msp_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq) return 0; } +static int msp_querystd(struct v4l2_subdev *sd, v4l2_std_id *id) +{ + struct msp_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + *id &= state->detected_std; + + v4l_dbg(2, msp_debug, client, + "detected standard: %s(0x%08Lx)\n", + msp_standard_std_name(state->std), state->detected_std); + + return 0; +} + static int msp_s_std(struct v4l2_subdev *sd, v4l2_std_id id) { struct msp_state *state = to_state(sd); @@ -616,6 +630,10 @@ static const struct v4l2_subdev_core_ops msp_core_ops = { .s_std = msp_s_std, }; +static const struct v4l2_subdev_video_ops msp_video_ops = { + .querystd = msp_querystd, +}; + static const struct v4l2_subdev_tuner_ops msp_tuner_ops = { .s_frequency = msp_s_frequency, .g_tuner = msp_g_tuner, @@ -630,6 +648,7 @@ static const struct v4l2_subdev_audio_ops msp_audio_ops = { static const struct v4l2_subdev_ops msp_ops = { .core = &msp_core_ops, + .video = &msp_video_ops, .tuner = &msp_tuner_ops, .audio = &msp_audio_ops, }; @@ -664,6 +683,7 @@ static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id) v4l2_i2c_subdev_init(sd, client, &msp_ops); state->v4l2_std = V4L2_STD_NTSC; + state->detected_std = V4L2_STD_ALL; state->audmode = V4L2_TUNER_MODE_STEREO; state->input = -1; state->i2s_mode = 0; diff --git a/drivers/media/video/msp3400-driver.h b/drivers/media/video/msp3400-driver.h index 32a478e532f..831e8db4368 100644 --- a/drivers/media/video/msp3400-driver.h +++ b/drivers/media/video/msp3400-driver.h @@ -75,7 +75,7 @@ struct msp_state { int opmode; int std; int mode; - v4l2_std_id v4l2_std; + v4l2_std_id v4l2_std, detected_std; int nicam_on; int acb; int in_scart; diff --git a/drivers/media/video/msp3400-kthreads.c b/drivers/media/video/msp3400-kthreads.c index 80387e2c3ec..f8b51714f2f 100644 --- a/drivers/media/video/msp3400-kthreads.c +++ b/drivers/media/video/msp3400-kthreads.c @@ -37,29 +37,49 @@ static struct { int retval; int main, second; char *name; + v4l2_std_id std; } msp_stdlist[] = { - { 0x0000, 0, 0, "could not detect sound standard" }, - { 0x0001, 0, 0, "autodetect start" }, - { 0x0002, MSP_CARRIER(4.5), MSP_CARRIER(4.72), "4.5/4.72 M Dual FM-Stereo" }, - { 0x0003, MSP_CARRIER(5.5), MSP_CARRIER(5.7421875), "5.5/5.74 B/G Dual FM-Stereo" }, - { 0x0004, MSP_CARRIER(6.5), MSP_CARRIER(6.2578125), "6.5/6.25 D/K1 Dual FM-Stereo" }, - { 0x0005, MSP_CARRIER(6.5), MSP_CARRIER(6.7421875), "6.5/6.74 D/K2 Dual FM-Stereo" }, - { 0x0006, MSP_CARRIER(6.5), MSP_CARRIER(6.5), "6.5 D/K FM-Mono (HDEV3)" }, - { 0x0007, MSP_CARRIER(6.5), MSP_CARRIER(5.7421875), "6.5/5.74 D/K3 Dual FM-Stereo" }, - { 0x0008, MSP_CARRIER(5.5), MSP_CARRIER(5.85), "5.5/5.85 B/G NICAM FM" }, - { 0x0009, MSP_CARRIER(6.5), MSP_CARRIER(5.85), "6.5/5.85 L NICAM AM" }, - { 0x000a, MSP_CARRIER(6.0), MSP_CARRIER(6.55), "6.0/6.55 I NICAM FM" }, - { 0x000b, MSP_CARRIER(6.5), MSP_CARRIER(5.85), "6.5/5.85 D/K NICAM FM" }, - { 0x000c, MSP_CARRIER(6.5), MSP_CARRIER(5.85), "6.5/5.85 D/K NICAM FM (HDEV2)" }, - { 0x000d, MSP_CARRIER(6.5), MSP_CARRIER(5.85), "6.5/5.85 D/K NICAM FM (HDEV3)" }, - { 0x0020, MSP_CARRIER(4.5), MSP_CARRIER(4.5), "4.5 M BTSC-Stereo" }, - { 0x0021, MSP_CARRIER(4.5), MSP_CARRIER(4.5), "4.5 M BTSC-Mono + SAP" }, - { 0x0030, MSP_CARRIER(4.5), MSP_CARRIER(4.5), "4.5 M EIA-J Japan Stereo" }, - { 0x0040, MSP_CARRIER(10.7), MSP_CARRIER(10.7), "10.7 FM-Stereo Radio" }, - { 0x0050, MSP_CARRIER(6.5), MSP_CARRIER(6.5), "6.5 SAT-Mono" }, - { 0x0051, MSP_CARRIER(7.02), MSP_CARRIER(7.20), "7.02/7.20 SAT-Stereo" }, - { 0x0060, MSP_CARRIER(7.2), MSP_CARRIER(7.2), "7.2 SAT ADR" }, - { -1, 0, 0, NULL }, /* EOF */ + { 0x0000, 0, 0, "could not detect sound standard", V4L2_STD_ALL }, + { 0x0001, 0, 0, "autodetect start", V4L2_STD_ALL }, + { 0x0002, MSP_CARRIER(4.5), MSP_CARRIER(4.72), + "4.5/4.72 M Dual FM-Stereo", V4L2_STD_MN }, + { 0x0003, MSP_CARRIER(5.5), MSP_CARRIER(5.7421875), + "5.5/5.74 B/G Dual FM-Stereo", V4L2_STD_BG }, + { 0x0004, MSP_CARRIER(6.5), MSP_CARRIER(6.2578125), + "6.5/6.25 D/K1 Dual FM-Stereo", V4L2_STD_DK }, + { 0x0005, MSP_CARRIER(6.5), MSP_CARRIER(6.7421875), + "6.5/6.74 D/K2 Dual FM-Stereo", V4L2_STD_DK }, + { 0x0006, MSP_CARRIER(6.5), MSP_CARRIER(6.5), + "6.5 D/K FM-Mono (HDEV3)", V4L2_STD_DK }, + { 0x0007, MSP_CARRIER(6.5), MSP_CARRIER(5.7421875), + "6.5/5.74 D/K3 Dual FM-Stereo", V4L2_STD_DK }, + { 0x0008, MSP_CARRIER(5.5), MSP_CARRIER(5.85), + "5.5/5.85 B/G NICAM FM", V4L2_STD_BG }, + { 0x0009, MSP_CARRIER(6.5), MSP_CARRIER(5.85), + "6.5/5.85 L NICAM AM", V4L2_STD_L }, + { 0x000a, MSP_CARRIER(6.0), MSP_CARRIER(6.55), + "6.0/6.55 I NICAM FM", V4L2_STD_PAL_I }, + { 0x000b, MSP_CARRIER(6.5), MSP_CARRIER(5.85), + "6.5/5.85 D/K NICAM FM", V4L2_STD_DK }, + { 0x000c, MSP_CARRIER(6.5), MSP_CARRIER(5.85), + "6.5/5.85 D/K NICAM FM (HDEV2)", V4L2_STD_DK }, + { 0x000d, MSP_CARRIER(6.5), MSP_CARRIER(5.85), + "6.5/5.85 D/K NICAM FM (HDEV3)", V4L2_STD_DK }, + { 0x0020, MSP_CARRIER(4.5), MSP_CARRIER(4.5), + "4.5 M BTSC-Stereo", V4L2_STD_MTS }, + { 0x0021, MSP_CARRIER(4.5), MSP_CARRIER(4.5), + "4.5 M BTSC-Mono + SAP", V4L2_STD_MTS }, + { 0x0030, MSP_CARRIER(4.5), MSP_CARRIER(4.5), + "4.5 M EIA-J Japan Stereo", V4L2_STD_NTSC_M_JP }, + { 0x0040, MSP_CARRIER(10.7), MSP_CARRIER(10.7), + "10.7 FM-Stereo Radio", V4L2_STD_ALL }, + { 0x0050, MSP_CARRIER(6.5), MSP_CARRIER(6.5), + "6.5 SAT-Mono", V4L2_STD_ALL }, + { 0x0051, MSP_CARRIER(7.02), MSP_CARRIER(7.20), + "7.02/7.20 SAT-Stereo", V4L2_STD_ALL }, + { 0x0060, MSP_CARRIER(7.2), MSP_CARRIER(7.2), + "7.2 SAT ADR", V4L2_STD_ALL }, + { -1, 0, 0, NULL, 0 }, /* EOF */ }; static struct msp3400c_init_data_dem { @@ -156,6 +176,16 @@ const char *msp_standard_std_name(int std) return "unknown"; } +static v4l2_std_id msp_standard_std(int std) +{ + int i; + + for (i = 0; msp_stdlist[i].name != NULL; i++) + if (msp_stdlist[i].retval == std) + return msp_stdlist[i].std; + return V4L2_STD_ALL; +} + static void msp_set_source(struct i2c_client *client, u16 src) { struct msp_state *state = to_state(i2c_get_clientdata(client)); @@ -479,6 +509,7 @@ int msp3400c_thread(void *data) int count, max1, max2, val1, val2, val, i; v4l_dbg(1, msp_debug, client, "msp3400 daemon started\n"); + state->detected_std = V4L2_STD_ALL; set_freezable(); for (;;) { v4l_dbg(2, msp_debug, client, "msp3400 thread: sleep\n"); @@ -579,6 +610,7 @@ restart: state->main = msp3400c_carrier_detect_main[max1].cdo; switch (max1) { case 1: /* 5.5 */ + state->detected_std = V4L2_STD_BG | V4L2_STD_PAL_H; if (max2 == 0) { /* B/G FM-stereo */ state->second = msp3400c_carrier_detect_55[max2].cdo; @@ -596,6 +628,7 @@ restart: break; case 2: /* 6.0 */ /* PAL I NICAM */ + state->detected_std = V4L2_STD_PAL_I; state->second = MSP_CARRIER(6.552); msp3400c_set_mode(client, MSP_MODE_FM_NICAM2); state->nicam_on = 1; @@ -607,22 +640,26 @@ restart: state->second = msp3400c_carrier_detect_65[max2].cdo; msp3400c_set_mode(client, MSP_MODE_FM_TERRA); state->watch_stereo = 1; + state->detected_std = V4L2_STD_DK; } else if (max2 == 0 && (state->v4l2_std & V4L2_STD_SECAM)) { /* L NICAM or AM-mono */ state->second = msp3400c_carrier_detect_65[max2].cdo; msp3400c_set_mode(client, MSP_MODE_AM_NICAM); state->watch_stereo = 1; + state->detected_std = V4L2_STD_L; } else if (max2 == 0 && state->has_nicam) { /* D/K NICAM */ state->second = msp3400c_carrier_detect_65[max2].cdo; msp3400c_set_mode(client, MSP_MODE_FM_NICAM1); state->nicam_on = 1; state->watch_stereo = 1; + state->detected_std = V4L2_STD_DK; } else { goto no_second; } break; case 0: /* 4.5 */ + state->detected_std = V4L2_STD_MN; default: no_second: state->second = msp3400c_carrier_detect_main[max1].cdo; @@ -662,6 +699,7 @@ int msp3410d_thread(void *data) int val, i, std, count; v4l_dbg(1, msp_debug, client, "msp3410 daemon started\n"); + state->detected_std = V4L2_STD_ALL; set_freezable(); for (;;) { v4l_dbg(2, msp_debug, client, "msp3410 thread: sleep\n"); @@ -743,6 +781,8 @@ restart: msp_stdlist[8].name : "unknown", val); state->std = val = 0x0009; msp_write_dem(client, 0x20, val); + } else { + state->detected_std = msp_standard_std(state->std); } /* set stereo */ @@ -957,6 +997,7 @@ int msp34xxg_thread(void *data) int val, i; v4l_dbg(1, msp_debug, client, "msp34xxg daemon started\n"); + state->detected_std = V4L2_STD_ALL; set_freezable(); for (;;) { v4l_dbg(2, msp_debug, client, "msp34xxg thread: sleep\n"); @@ -1013,6 +1054,7 @@ unmute: v4l_dbg(1, msp_debug, client, "detected standard: %s (0x%04x)\n", msp_standard_std_name(state->std), state->std); + state->detected_std = msp_standard_std(state->std); if (state->std == 9) { /* AM NICAM mode */ diff --git a/drivers/media/video/mt9m001.c b/drivers/media/video/mt9m001.c index 4da9cca939c..e2b1029b16c 100644 --- a/drivers/media/video/mt9m001.c +++ b/drivers/media/video/mt9m001.c @@ -12,10 +12,13 @@ #include <linux/slab.h> #include <linux/i2c.h> #include <linux/log2.h> +#include <linux/module.h> +#include <media/soc_camera.h> +#include <media/soc_mediabus.h> #include <media/v4l2-subdev.h> #include <media/v4l2-chip-ident.h> -#include <media/soc_camera.h> +#include <media/v4l2-ctrls.h> /* * mt9m001 i2c address 0x5d @@ -84,15 +87,19 @@ static const struct mt9m001_datafmt mt9m001_monochrome_fmts[] = { struct mt9m001 { struct v4l2_subdev subdev; + struct v4l2_ctrl_handler hdl; + struct { + /* exposure/auto-exposure cluster */ + struct v4l2_ctrl *autoexposure; + struct v4l2_ctrl *exposure; + }; struct v4l2_rect rect; /* Sensor window */ const struct mt9m001_datafmt *fmt; const struct mt9m001_datafmt *fmts; int num_fmts; int model; /* V4L2_IDENT_MT9M001* codes from v4l2-chip-ident.h */ - unsigned int gain; - unsigned int exposure; + unsigned int total_h; unsigned short y_skip_top; /* Lines to skip at the top */ - unsigned char autoexposure; }; static struct mt9m001 *to_mt9m001(const struct i2c_client *client) @@ -165,54 +172,13 @@ static int mt9m001_s_stream(struct v4l2_subdev *sd, int enable) return 0; } -static int mt9m001_set_bus_param(struct soc_camera_device *icd, - unsigned long flags) -{ - struct soc_camera_link *icl = to_soc_camera_link(icd); - unsigned long width_flag = flags & SOCAM_DATAWIDTH_MASK; - - /* Only one width bit may be set */ - if (!is_power_of_2(width_flag)) - return -EINVAL; - - if (icl->set_bus_param) - return icl->set_bus_param(icl, width_flag); - - /* - * Without board specific bus width settings we only support the - * sensors native bus width - */ - if (width_flag == SOCAM_DATAWIDTH_10) - return 0; - - return -EINVAL; -} - -static unsigned long mt9m001_query_bus_param(struct soc_camera_device *icd) -{ - struct soc_camera_link *icl = to_soc_camera_link(icd); - /* MT9M001 has all capture_format parameters fixed */ - unsigned long flags = SOCAM_PCLK_SAMPLE_FALLING | - SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH | - SOCAM_DATA_ACTIVE_HIGH | SOCAM_MASTER; - - if (icl->query_bus_param) - flags |= icl->query_bus_param(icl) & SOCAM_DATAWIDTH_MASK; - else - flags |= SOCAM_DATAWIDTH_10; - - return soc_camera_apply_sensor_flags(icl, flags); -} - static int mt9m001_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct mt9m001 *mt9m001 = to_mt9m001(client); struct v4l2_rect rect = a->c; - struct soc_camera_device *icd = client->dev.platform_data; int ret; const u16 hblank = 9, vblank = 25; - unsigned int total_h; if (mt9m001->fmts == mt9m001_colour_fmts) /* @@ -231,7 +197,7 @@ static int mt9m001_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) soc_camera_limit_side(&rect.top, &rect.height, MT9M001_ROW_SKIP, MT9M001_MIN_HEIGHT, MT9M001_MAX_HEIGHT); - total_h = rect.height + mt9m001->y_skip_top + vblank; + mt9m001->total_h = rect.height + mt9m001->y_skip_top + vblank; /* Blanking and start values - default... */ ret = reg_write(client, MT9M001_HORIZONTAL_BLANKING, hblank); @@ -240,7 +206,7 @@ static int mt9m001_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) /* * The caller provides a supported format, as verified per - * call to icd->try_fmt() + * call to .try_mbus_fmt() */ if (!ret) ret = reg_write(client, MT9M001_COLUMN_START, rect.left); @@ -251,17 +217,8 @@ static int mt9m001_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) if (!ret) ret = reg_write(client, MT9M001_WINDOW_HEIGHT, rect.height + mt9m001->y_skip_top - 1); - if (!ret && mt9m001->autoexposure) { - ret = reg_write(client, MT9M001_SHUTTER_WIDTH, total_h); - if (!ret) { - const struct v4l2_queryctrl *qctrl = - soc_camera_find_qctrl(icd->ops, - V4L2_CID_EXPOSURE); - mt9m001->exposure = (524 + (total_h - 1) * - (qctrl->maximum - qctrl->minimum)) / - 1048 + qctrl->minimum; - } - } + if (!ret && v4l2_ctrl_g_ctrl(mt9m001->autoexposure) == V4L2_EXPOSURE_AUTO) + ret = reg_write(client, MT9M001_SHUTTER_WIDTH, mt9m001->total_h); if (!ret) mt9m001->rect = rect; @@ -421,107 +378,48 @@ static int mt9m001_s_register(struct v4l2_subdev *sd, } #endif -static const struct v4l2_queryctrl mt9m001_controls[] = { - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Flip Vertically", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gain", - .minimum = 0, - .maximum = 127, - .step = 1, - .default_value = 64, - .flags = V4L2_CTRL_FLAG_SLIDER, - }, { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Exposure", - .minimum = 1, - .maximum = 255, - .step = 1, - .default_value = 255, - .flags = V4L2_CTRL_FLAG_SLIDER, - }, { - .id = V4L2_CID_EXPOSURE_AUTO, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Automatic Exposure", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1, - } -}; - -static struct soc_camera_ops mt9m001_ops = { - .set_bus_param = mt9m001_set_bus_param, - .query_bus_param = mt9m001_query_bus_param, - .controls = mt9m001_controls, - .num_controls = ARRAY_SIZE(mt9m001_controls), -}; - -static int mt9m001_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +static int mt9m001_g_volatile_ctrl(struct v4l2_ctrl *ctrl) { - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9m001 *mt9m001 = to_mt9m001(client); - int data; + struct mt9m001 *mt9m001 = container_of(ctrl->handler, + struct mt9m001, hdl); + s32 min, max; switch (ctrl->id) { - case V4L2_CID_VFLIP: - data = reg_read(client, MT9M001_READ_OPTIONS2); - if (data < 0) - return -EIO; - ctrl->value = !!(data & 0x8000); - break; case V4L2_CID_EXPOSURE_AUTO: - ctrl->value = mt9m001->autoexposure; - break; - case V4L2_CID_GAIN: - ctrl->value = mt9m001->gain; - break; - case V4L2_CID_EXPOSURE: - ctrl->value = mt9m001->exposure; + min = mt9m001->exposure->minimum; + max = mt9m001->exposure->maximum; + mt9m001->exposure->val = + (524 + (mt9m001->total_h - 1) * (max - min)) / 1048 + min; break; } return 0; } -static int mt9m001_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +static int mt9m001_s_ctrl(struct v4l2_ctrl *ctrl) { + struct mt9m001 *mt9m001 = container_of(ctrl->handler, + struct mt9m001, hdl); + struct v4l2_subdev *sd = &mt9m001->subdev; struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9m001 *mt9m001 = to_mt9m001(client); - struct soc_camera_device *icd = client->dev.platform_data; - const struct v4l2_queryctrl *qctrl; + struct v4l2_ctrl *exp = mt9m001->exposure; int data; - qctrl = soc_camera_find_qctrl(&mt9m001_ops, ctrl->id); - - if (!qctrl) - return -EINVAL; - switch (ctrl->id) { case V4L2_CID_VFLIP: - if (ctrl->value) + if (ctrl->val) data = reg_set(client, MT9M001_READ_OPTIONS2, 0x8000); else data = reg_clear(client, MT9M001_READ_OPTIONS2, 0x8000); if (data < 0) return -EIO; - break; + return 0; + case V4L2_CID_GAIN: - if (ctrl->value > qctrl->maximum || ctrl->value < qctrl->minimum) - return -EINVAL; /* See Datasheet Table 7, Gain settings. */ - if (ctrl->value <= qctrl->default_value) { + if (ctrl->val <= ctrl->default_value) { /* Pack it into 0..1 step 0.125, register values 0..8 */ - unsigned long range = qctrl->default_value - qctrl->minimum; - data = ((ctrl->value - qctrl->minimum) * 8 + range / 2) / range; + unsigned long range = ctrl->default_value - ctrl->minimum; + data = ((ctrl->val - ctrl->minimum) * 8 + range / 2) / range; dev_dbg(&client->dev, "Setting gain %d\n", data); data = reg_write(client, MT9M001_GLOBAL_GAIN, data); @@ -530,8 +428,8 @@ static int mt9m001_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) } else { /* Pack it into 1.125..15 variable step, register values 9..67 */ /* We assume qctrl->maximum - qctrl->default_value - 1 > 0 */ - unsigned long range = qctrl->maximum - qctrl->default_value - 1; - unsigned long gain = ((ctrl->value - qctrl->default_value - 1) * + unsigned long range = ctrl->maximum - ctrl->default_value - 1; + unsigned long gain = ((ctrl->val - ctrl->default_value - 1) * 111 + range / 2) / range + 9; if (gain <= 32) @@ -547,66 +445,44 @@ static int mt9m001_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) if (data < 0) return -EIO; } + return 0; - /* Success */ - mt9m001->gain = ctrl->value; - break; - case V4L2_CID_EXPOSURE: - /* mt9m001 has maximum == default */ - if (ctrl->value > qctrl->maximum || ctrl->value < qctrl->minimum) - return -EINVAL; - else { - unsigned long range = qctrl->maximum - qctrl->minimum; - unsigned long shutter = ((ctrl->value - qctrl->minimum) * 1048 + + case V4L2_CID_EXPOSURE_AUTO: + if (ctrl->val == V4L2_EXPOSURE_MANUAL) { + unsigned long range = exp->maximum - exp->minimum; + unsigned long shutter = ((exp->val - exp->minimum) * 1048 + range / 2) / range + 1; dev_dbg(&client->dev, "Setting shutter width from %d to %lu\n", - reg_read(client, MT9M001_SHUTTER_WIDTH), - shutter); + reg_read(client, MT9M001_SHUTTER_WIDTH), shutter); if (reg_write(client, MT9M001_SHUTTER_WIDTH, shutter) < 0) return -EIO; - mt9m001->exposure = ctrl->value; - mt9m001->autoexposure = 0; - } - break; - case V4L2_CID_EXPOSURE_AUTO: - if (ctrl->value) { + } else { const u16 vblank = 25; - unsigned int total_h = mt9m001->rect.height + + + mt9m001->total_h = mt9m001->rect.height + mt9m001->y_skip_top + vblank; - if (reg_write(client, MT9M001_SHUTTER_WIDTH, - total_h) < 0) + if (reg_write(client, MT9M001_SHUTTER_WIDTH, mt9m001->total_h) < 0) return -EIO; - qctrl = soc_camera_find_qctrl(icd->ops, V4L2_CID_EXPOSURE); - mt9m001->exposure = (524 + (total_h - 1) * - (qctrl->maximum - qctrl->minimum)) / - 1048 + qctrl->minimum; - mt9m001->autoexposure = 1; - } else - mt9m001->autoexposure = 0; - break; + } + return 0; } - return 0; + return -EINVAL; } /* * Interface active, can use i2c. If it fails, it can indeed mean, that * this wasn't our capture interface, so, we wait for the right one */ -static int mt9m001_video_probe(struct soc_camera_device *icd, +static int mt9m001_video_probe(struct soc_camera_link *icl, struct i2c_client *client) { struct mt9m001 *mt9m001 = to_mt9m001(client); - struct soc_camera_link *icl = to_soc_camera_link(icd); s32 data; unsigned long flags; int ret; - /* We must have a parent by now. And it cannot be a wrong one. */ - BUG_ON(!icd->parent || - to_soc_camera_host(icd->parent)->nr != icd->iface); - /* Enable the chip */ data = reg_write(client, MT9M001_CHIP_ENABLE, 1); dev_dbg(&client->dev, "write: %d\n", data); @@ -661,18 +537,11 @@ static int mt9m001_video_probe(struct soc_camera_device *icd, dev_err(&client->dev, "Failed to initialise the camera\n"); /* mt9m001_init() has reset the chip, returning registers to defaults */ - mt9m001->gain = 64; - mt9m001->exposure = 255; - - return ret; + return v4l2_ctrl_handler_setup(&mt9m001->hdl); } -static void mt9m001_video_remove(struct soc_camera_device *icd) +static void mt9m001_video_remove(struct soc_camera_link *icl) { - struct soc_camera_link *icl = to_soc_camera_link(icd); - - dev_dbg(icd->pdev, "Video removed: %p, %p\n", - icd->parent, icd->vdev); if (icl->free_bus) icl->free_bus(icl); } @@ -687,9 +556,12 @@ static int mt9m001_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines) return 0; } +static const struct v4l2_ctrl_ops mt9m001_ctrl_ops = { + .g_volatile_ctrl = mt9m001_g_volatile_ctrl, + .s_ctrl = mt9m001_s_ctrl, +}; + static struct v4l2_subdev_core_ops mt9m001_subdev_core_ops = { - .g_ctrl = mt9m001_g_ctrl, - .s_ctrl = mt9m001_s_ctrl, .g_chip_ident = mt9m001_g_chip_ident, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = mt9m001_g_register, @@ -710,6 +582,40 @@ static int mt9m001_enum_fmt(struct v4l2_subdev *sd, unsigned int index, return 0; } +static int mt9m001_g_mbus_config(struct v4l2_subdev *sd, + struct v4l2_mbus_config *cfg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + + /* MT9M001 has all capture_format parameters fixed */ + cfg->flags = V4L2_MBUS_PCLK_SAMPLE_FALLING | + V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH | + V4L2_MBUS_DATA_ACTIVE_HIGH | V4L2_MBUS_MASTER; + cfg->type = V4L2_MBUS_PARALLEL; + cfg->flags = soc_camera_apply_board_flags(icl, cfg); + + return 0; +} + +static int mt9m001_s_mbus_config(struct v4l2_subdev *sd, + const struct v4l2_mbus_config *cfg) +{ + const struct i2c_client *client = v4l2_get_subdevdata(sd); + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct mt9m001 *mt9m001 = to_mt9m001(client); + unsigned int bps = soc_mbus_get_fmtdesc(mt9m001->fmt->code)->bits_per_sample; + + if (icl->set_bus_param) + return icl->set_bus_param(icl, 1 << (bps - 1)); + + /* + * Without board specific bus width settings we only support the + * sensors native bus width + */ + return bps == 10 ? 0 : -EINVAL; +} + static struct v4l2_subdev_video_ops mt9m001_subdev_video_ops = { .s_stream = mt9m001_s_stream, .s_mbus_fmt = mt9m001_s_fmt, @@ -719,6 +625,8 @@ static struct v4l2_subdev_video_ops mt9m001_subdev_video_ops = { .g_crop = mt9m001_g_crop, .cropcap = mt9m001_cropcap, .enum_mbus_fmt = mt9m001_enum_fmt, + .g_mbus_config = mt9m001_g_mbus_config, + .s_mbus_config = mt9m001_s_mbus_config, }; static struct v4l2_subdev_sensor_ops mt9m001_subdev_sensor_ops = { @@ -735,17 +643,10 @@ static int mt9m001_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct mt9m001 *mt9m001; - struct soc_camera_device *icd = client->dev.platform_data; struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - struct soc_camera_link *icl; + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); int ret; - if (!icd) { - dev_err(&client->dev, "MT9M001: missing soc-camera data!\n"); - return -EINVAL; - } - - icl = to_soc_camera_link(icd); if (!icl) { dev_err(&client->dev, "MT9M001 driver needs platform data\n"); return -EINVAL; @@ -762,25 +663,40 @@ static int mt9m001_probe(struct i2c_client *client, return -ENOMEM; v4l2_i2c_subdev_init(&mt9m001->subdev, client, &mt9m001_subdev_ops); + v4l2_ctrl_handler_init(&mt9m001->hdl, 4); + v4l2_ctrl_new_std(&mt9m001->hdl, &mt9m001_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(&mt9m001->hdl, &mt9m001_ctrl_ops, + V4L2_CID_GAIN, 0, 127, 1, 64); + mt9m001->exposure = v4l2_ctrl_new_std(&mt9m001->hdl, &mt9m001_ctrl_ops, + V4L2_CID_EXPOSURE, 1, 255, 1, 255); + /* + * Simulated autoexposure. If enabled, we calculate shutter width + * ourselves in the driver based on vertical blanking and frame width + */ + mt9m001->autoexposure = v4l2_ctrl_new_std_menu(&mt9m001->hdl, + &mt9m001_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0, + V4L2_EXPOSURE_AUTO); + mt9m001->subdev.ctrl_handler = &mt9m001->hdl; + if (mt9m001->hdl.error) { + int err = mt9m001->hdl.error; - /* Second stage probe - when a capture adapter is there */ - icd->ops = &mt9m001_ops; + kfree(mt9m001); + return err; + } + v4l2_ctrl_auto_cluster(2, &mt9m001->autoexposure, + V4L2_EXPOSURE_MANUAL, true); + /* Second stage probe - when a capture adapter is there */ mt9m001->y_skip_top = 0; mt9m001->rect.left = MT9M001_COLUMN_SKIP; mt9m001->rect.top = MT9M001_ROW_SKIP; mt9m001->rect.width = MT9M001_MAX_WIDTH; mt9m001->rect.height = MT9M001_MAX_HEIGHT; - /* - * Simulated autoexposure. If enabled, we calculate shutter width - * ourselves in the driver based on vertical blanking and frame width - */ - mt9m001->autoexposure = 1; - - ret = mt9m001_video_probe(icd, client); + ret = mt9m001_video_probe(icl, client); if (ret) { - icd->ops = NULL; + v4l2_ctrl_handler_free(&mt9m001->hdl); kfree(mt9m001); } @@ -790,10 +706,11 @@ static int mt9m001_probe(struct i2c_client *client, static int mt9m001_remove(struct i2c_client *client) { struct mt9m001 *mt9m001 = to_mt9m001(client); - struct soc_camera_device *icd = client->dev.platform_data; + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); - icd->ops = NULL; - mt9m001_video_remove(icd); + v4l2_device_unregister_subdev(&mt9m001->subdev); + v4l2_ctrl_handler_free(&mt9m001->hdl); + mt9m001_video_remove(icl); kfree(mt9m001); return 0; diff --git a/drivers/media/video/mt9m111.c b/drivers/media/video/mt9m111.c index a357aa889fc..cf2c0fb95f2 100644 --- a/drivers/media/video/mt9m111.c +++ b/drivers/media/video/mt9m111.c @@ -13,10 +13,13 @@ #include <linux/log2.h> #include <linux/gpio.h> #include <linux/delay.h> +#include <linux/v4l2-mediabus.h> +#include <linux/module.h> +#include <media/soc_camera.h> #include <media/v4l2-common.h> +#include <media/v4l2-ctrls.h> #include <media/v4l2-chip-ident.h> -#include <media/soc_camera.h> /* * MT9M111, MT9M112 and MT9M131: @@ -177,6 +180,8 @@ enum mt9m111_context { struct mt9m111 { struct v4l2_subdev subdev; + struct v4l2_ctrl_handler hdl; + struct v4l2_ctrl *gain; int model; /* V4L2_IDENT_MT9M111 or V4L2_IDENT_MT9M112 code * from v4l2-chip-ident.h */ enum mt9m111_context context; @@ -184,13 +189,9 @@ struct mt9m111 { struct mutex power_lock; /* lock to protect power_count */ int power_count; const struct mt9m111_datafmt *fmt; - unsigned int gain; - unsigned char autoexposure; + int lastpage; /* PageMap cache value */ unsigned char datawidth; unsigned int powered:1; - unsigned int hflip:1; - unsigned int vflip:1; - unsigned int autowhitebalance:1; }; static struct mt9m111 *to_mt9m111(const struct i2c_client *client) @@ -202,17 +203,17 @@ static int reg_page_map_set(struct i2c_client *client, const u16 reg) { int ret; u16 page; - static int lastpage = -1; /* PageMap cache value */ + struct mt9m111 *mt9m111 = to_mt9m111(client); page = (reg >> 8); - if (page == lastpage) + if (page == mt9m111->lastpage) return 0; if (page > 2) return -EINVAL; ret = i2c_smbus_write_word_data(client, MT9M111_PAGE_MAP, swab16(page)); if (!ret) - lastpage = page; + mt9m111->lastpage = page; return ret; } @@ -362,21 +363,6 @@ static int mt9m111_reset(struct mt9m111 *mt9m111) return ret; } -static unsigned long mt9m111_query_bus_param(struct soc_camera_device *icd) -{ - struct soc_camera_link *icl = to_soc_camera_link(icd); - unsigned long flags = SOCAM_MASTER | SOCAM_PCLK_SAMPLE_RISING | - SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH | - SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATAWIDTH_8; - - return soc_camera_apply_sensor_flags(icl, flags); -} - -static int mt9m111_set_bus_param(struct soc_camera_device *icd, unsigned long f) -{ - return 0; -} - static int mt9m111_make_rect(struct mt9m111 *mt9m111, struct v4l2_rect *rect) { @@ -659,50 +645,6 @@ static int mt9m111_s_register(struct v4l2_subdev *sd, } #endif -static const struct v4l2_queryctrl mt9m111_controls[] = { - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Flip Verticaly", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Flip Horizontaly", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, { /* gain = 1/32*val (=>gain=1 if val==32) */ - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gain", - .minimum = 0, - .maximum = 63 * 2 * 2, - .step = 1, - .default_value = 32, - .flags = V4L2_CTRL_FLAG_SLIDER, - }, { - .id = V4L2_CID_EXPOSURE_AUTO, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Auto Exposure", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1, - } -}; - -static struct soc_camera_ops mt9m111_ops = { - .query_bus_param = mt9m111_query_bus_param, - .set_bus_param = mt9m111_set_bus_param, - .controls = mt9m111_controls, - .num_controls = ARRAY_SIZE(mt9m111_controls), -}; - static int mt9m111_set_flip(struct mt9m111 *mt9m111, int flip, int mask) { struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev); @@ -743,7 +685,6 @@ static int mt9m111_set_global_gain(struct mt9m111 *mt9m111, int gain) if (gain > 63 * 2 * 2) return -EINVAL; - mt9m111->gain = gain; if ((gain >= 64 * 2) && (gain < 63 * 2 * 2)) val = (1 << 10) | (1 << 9) | (gain / 4); else if ((gain >= 64) && (gain < 64 * 2)) @@ -757,118 +698,47 @@ static int mt9m111_set_global_gain(struct mt9m111 *mt9m111, int gain) static int mt9m111_set_autoexposure(struct mt9m111 *mt9m111, int on) { struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev); - int ret; if (on) - ret = reg_set(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOEXPO_EN); - else - ret = reg_clear(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOEXPO_EN); - - if (!ret) - mt9m111->autoexposure = on; - - return ret; + return reg_set(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOEXPO_EN); + return reg_clear(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOEXPO_EN); } static int mt9m111_set_autowhitebalance(struct mt9m111 *mt9m111, int on) { struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev); - int ret; if (on) - ret = reg_set(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOWHITEBAL_EN); - else - ret = reg_clear(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOWHITEBAL_EN); - - if (!ret) - mt9m111->autowhitebalance = on; - - return ret; -} - -static int mt9m111_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev); - int data; - - switch (ctrl->id) { - case V4L2_CID_VFLIP: - if (mt9m111->context == HIGHPOWER) - data = reg_read(READ_MODE_B); - else - data = reg_read(READ_MODE_A); - - if (data < 0) - return -EIO; - ctrl->value = !!(data & MT9M111_RMB_MIRROR_ROWS); - break; - case V4L2_CID_HFLIP: - if (mt9m111->context == HIGHPOWER) - data = reg_read(READ_MODE_B); - else - data = reg_read(READ_MODE_A); - - if (data < 0) - return -EIO; - ctrl->value = !!(data & MT9M111_RMB_MIRROR_COLS); - break; - case V4L2_CID_GAIN: - data = mt9m111_get_global_gain(mt9m111); - if (data < 0) - return data; - ctrl->value = data; - break; - case V4L2_CID_EXPOSURE_AUTO: - ctrl->value = mt9m111->autoexposure; - break; - case V4L2_CID_AUTO_WHITE_BALANCE: - ctrl->value = mt9m111->autowhitebalance; - break; - } - return 0; + return reg_set(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOWHITEBAL_EN); + return reg_clear(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOWHITEBAL_EN); } -static int mt9m111_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +static int mt9m111_s_ctrl(struct v4l2_ctrl *ctrl) { - struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev); - const struct v4l2_queryctrl *qctrl; - int ret; - - qctrl = soc_camera_find_qctrl(&mt9m111_ops, ctrl->id); - if (!qctrl) - return -EINVAL; + struct mt9m111 *mt9m111 = container_of(ctrl->handler, + struct mt9m111, hdl); switch (ctrl->id) { case V4L2_CID_VFLIP: - mt9m111->vflip = ctrl->value; - ret = mt9m111_set_flip(mt9m111, ctrl->value, + return mt9m111_set_flip(mt9m111, ctrl->val, MT9M111_RMB_MIRROR_ROWS); - break; case V4L2_CID_HFLIP: - mt9m111->hflip = ctrl->value; - ret = mt9m111_set_flip(mt9m111, ctrl->value, + return mt9m111_set_flip(mt9m111, ctrl->val, MT9M111_RMB_MIRROR_COLS); - break; case V4L2_CID_GAIN: - ret = mt9m111_set_global_gain(mt9m111, ctrl->value); - break; + return mt9m111_set_global_gain(mt9m111, ctrl->val); case V4L2_CID_EXPOSURE_AUTO: - ret = mt9m111_set_autoexposure(mt9m111, ctrl->value); - break; + return mt9m111_set_autoexposure(mt9m111, ctrl->val); case V4L2_CID_AUTO_WHITE_BALANCE: - ret = mt9m111_set_autowhitebalance(mt9m111, ctrl->value); - break; - default: - ret = -EINVAL; + return mt9m111_set_autowhitebalance(mt9m111, ctrl->val); } - return ret; + return -EINVAL; } static int mt9m111_suspend(struct mt9m111 *mt9m111) { - mt9m111->gain = mt9m111_get_global_gain(mt9m111); + v4l2_ctrl_s_ctrl(mt9m111->gain, mt9m111_get_global_gain(mt9m111)); return 0; } @@ -878,11 +748,7 @@ static void mt9m111_restore_state(struct mt9m111 *mt9m111) mt9m111_set_context(mt9m111, mt9m111->context); mt9m111_set_pixfmt(mt9m111, mt9m111->fmt->code); mt9m111_setup_rect(mt9m111, &mt9m111->rect); - mt9m111_set_flip(mt9m111, mt9m111->hflip, MT9M111_RMB_MIRROR_COLS); - mt9m111_set_flip(mt9m111, mt9m111->vflip, MT9M111_RMB_MIRROR_ROWS); - mt9m111_set_global_gain(mt9m111, mt9m111->gain); - mt9m111_set_autoexposure(mt9m111, mt9m111->autoexposure); - mt9m111_set_autowhitebalance(mt9m111, mt9m111->autowhitebalance); + v4l2_ctrl_handler_setup(&mt9m111->hdl); } static int mt9m111_resume(struct mt9m111 *mt9m111) @@ -910,8 +776,6 @@ static int mt9m111_init(struct mt9m111 *mt9m111) ret = mt9m111_reset(mt9m111); if (!ret) ret = mt9m111_set_context(mt9m111, mt9m111->context); - if (!ret) - ret = mt9m111_set_autoexposure(mt9m111, mt9m111->autoexposure); if (ret) dev_err(&client->dev, "mt9m111 init failed: %d\n", ret); return ret; @@ -921,20 +785,12 @@ static int mt9m111_init(struct mt9m111 *mt9m111) * Interface active, can use i2c. If it fails, it can indeed mean, that * this wasn't our capture interface, so, we wait for the right one */ -static int mt9m111_video_probe(struct soc_camera_device *icd, - struct i2c_client *client) +static int mt9m111_video_probe(struct i2c_client *client) { struct mt9m111 *mt9m111 = to_mt9m111(client); s32 data; int ret; - /* We must have a parent by now. And it cannot be a wrong one. */ - BUG_ON(!icd->parent || - to_soc_camera_host(icd->parent)->nr != icd->iface); - - mt9m111->autoexposure = 1; - mt9m111->autowhitebalance = 1; - data = reg_read(CHIP_VERSION); switch (data) { @@ -948,17 +804,16 @@ static int mt9m111_video_probe(struct soc_camera_device *icd, dev_info(&client->dev, "Detected a MT9M112 chip ID %x\n", data); break; default: - ret = -ENODEV; dev_err(&client->dev, "No MT9M111/MT9M112/MT9M131 chip detected register read %x\n", data); - goto ei2c; + return -ENODEV; } ret = mt9m111_init(mt9m111); - -ei2c: - return ret; + if (ret) + return ret; + return v4l2_ctrl_handler_setup(&mt9m111->hdl); } static int mt9m111_s_power(struct v4l2_subdev *sd, int on) @@ -995,9 +850,11 @@ out: return ret; } +static const struct v4l2_ctrl_ops mt9m111_ctrl_ops = { + .s_ctrl = mt9m111_s_ctrl, +}; + static struct v4l2_subdev_core_ops mt9m111_subdev_core_ops = { - .g_ctrl = mt9m111_g_ctrl, - .s_ctrl = mt9m111_s_ctrl, .g_chip_ident = mt9m111_g_chip_ident, .s_power = mt9m111_s_power, #ifdef CONFIG_VIDEO_ADV_DEBUG @@ -1016,6 +873,21 @@ static int mt9m111_enum_fmt(struct v4l2_subdev *sd, unsigned int index, return 0; } +static int mt9m111_g_mbus_config(struct v4l2_subdev *sd, + struct v4l2_mbus_config *cfg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + + cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING | + V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH | + V4L2_MBUS_DATA_ACTIVE_HIGH; + cfg->type = V4L2_MBUS_PARALLEL; + cfg->flags = soc_camera_apply_board_flags(icl, cfg); + + return 0; +} + static struct v4l2_subdev_video_ops mt9m111_subdev_video_ops = { .s_mbus_fmt = mt9m111_s_fmt, .g_mbus_fmt = mt9m111_g_fmt, @@ -1024,6 +896,7 @@ static struct v4l2_subdev_video_ops mt9m111_subdev_video_ops = { .g_crop = mt9m111_g_crop, .cropcap = mt9m111_cropcap, .enum_mbus_fmt = mt9m111_enum_fmt, + .g_mbus_config = mt9m111_g_mbus_config, }; static struct v4l2_subdev_ops mt9m111_subdev_ops = { @@ -1035,17 +908,10 @@ static int mt9m111_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct mt9m111 *mt9m111; - struct soc_camera_device *icd = client->dev.platform_data; struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - struct soc_camera_link *icl; + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); int ret; - if (!icd) { - dev_err(&client->dev, "mt9m111: soc-camera data missing!\n"); - return -EINVAL; - } - - icl = to_soc_camera_link(icd); if (!icl) { dev_err(&client->dev, "mt9m111: driver needs platform data\n"); return -EINVAL; @@ -1062,19 +928,37 @@ static int mt9m111_probe(struct i2c_client *client, return -ENOMEM; v4l2_i2c_subdev_init(&mt9m111->subdev, client, &mt9m111_subdev_ops); + v4l2_ctrl_handler_init(&mt9m111->hdl, 5); + v4l2_ctrl_new_std(&mt9m111->hdl, &mt9m111_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(&mt9m111->hdl, &mt9m111_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(&mt9m111->hdl, &mt9m111_ctrl_ops, + V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1); + mt9m111->gain = v4l2_ctrl_new_std(&mt9m111->hdl, &mt9m111_ctrl_ops, + V4L2_CID_GAIN, 0, 63 * 2 * 2, 1, 32); + v4l2_ctrl_new_std_menu(&mt9m111->hdl, + &mt9m111_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0, + V4L2_EXPOSURE_AUTO); + mt9m111->subdev.ctrl_handler = &mt9m111->hdl; + if (mt9m111->hdl.error) { + int err = mt9m111->hdl.error; - /* Second stage probe - when a capture adapter is there */ - icd->ops = &mt9m111_ops; + kfree(mt9m111); + return err; + } + /* Second stage probe - when a capture adapter is there */ mt9m111->rect.left = MT9M111_MIN_DARK_COLS; mt9m111->rect.top = MT9M111_MIN_DARK_ROWS; mt9m111->rect.width = MT9M111_MAX_WIDTH; mt9m111->rect.height = MT9M111_MAX_HEIGHT; mt9m111->fmt = &mt9m111_colour_fmts[0]; + mt9m111->lastpage = -1; - ret = mt9m111_video_probe(icd, client); + ret = mt9m111_video_probe(client); if (ret) { - icd->ops = NULL; + v4l2_ctrl_handler_free(&mt9m111->hdl); kfree(mt9m111); } @@ -1084,9 +968,9 @@ static int mt9m111_probe(struct i2c_client *client, static int mt9m111_remove(struct i2c_client *client) { struct mt9m111 *mt9m111 = to_mt9m111(client); - struct soc_camera_device *icd = client->dev.platform_data; - icd->ops = NULL; + v4l2_device_unregister_subdev(&mt9m111->subdev); + v4l2_ctrl_handler_free(&mt9m111->hdl); kfree(mt9m111); return 0; diff --git a/drivers/media/video/mt9p031.c b/drivers/media/video/mt9p031.c new file mode 100644 index 00000000000..73c068993f0 --- /dev/null +++ b/drivers/media/video/mt9p031.c @@ -0,0 +1,964 @@ +/* + * Driver for MT9P031 CMOS Image Sensor from Aptina + * + * Copyright (C) 2011, Laurent Pinchart <laurent.pinchart@ideasonboard.com> + * Copyright (C) 2011, Javier Martin <javier.martin@vista-silicon.com> + * Copyright (C) 2011, Guennadi Liakhovetski <g.liakhovetski@gmx.de> + * + * Based on the MT9V032 driver and Bastian Hecht's code. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/log2.h> +#include <linux/pm.h> +#include <linux/slab.h> +#include <media/v4l2-subdev.h> +#include <linux/videodev2.h> + +#include <media/mt9p031.h> +#include <media/v4l2-chip-ident.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-subdev.h> + +#define MT9P031_PIXEL_ARRAY_WIDTH 2752 +#define MT9P031_PIXEL_ARRAY_HEIGHT 2004 + +#define MT9P031_CHIP_VERSION 0x00 +#define MT9P031_CHIP_VERSION_VALUE 0x1801 +#define MT9P031_ROW_START 0x01 +#define MT9P031_ROW_START_MIN 0 +#define MT9P031_ROW_START_MAX 2004 +#define MT9P031_ROW_START_DEF 54 +#define MT9P031_COLUMN_START 0x02 +#define MT9P031_COLUMN_START_MIN 0 +#define MT9P031_COLUMN_START_MAX 2750 +#define MT9P031_COLUMN_START_DEF 16 +#define MT9P031_WINDOW_HEIGHT 0x03 +#define MT9P031_WINDOW_HEIGHT_MIN 2 +#define MT9P031_WINDOW_HEIGHT_MAX 2006 +#define MT9P031_WINDOW_HEIGHT_DEF 1944 +#define MT9P031_WINDOW_WIDTH 0x04 +#define MT9P031_WINDOW_WIDTH_MIN 2 +#define MT9P031_WINDOW_WIDTH_MAX 2752 +#define MT9P031_WINDOW_WIDTH_DEF 2592 +#define MT9P031_HORIZONTAL_BLANK 0x05 +#define MT9P031_HORIZONTAL_BLANK_MIN 0 +#define MT9P031_HORIZONTAL_BLANK_MAX 4095 +#define MT9P031_VERTICAL_BLANK 0x06 +#define MT9P031_VERTICAL_BLANK_MIN 0 +#define MT9P031_VERTICAL_BLANK_MAX 4095 +#define MT9P031_VERTICAL_BLANK_DEF 25 +#define MT9P031_OUTPUT_CONTROL 0x07 +#define MT9P031_OUTPUT_CONTROL_CEN 2 +#define MT9P031_OUTPUT_CONTROL_SYN 1 +#define MT9P031_OUTPUT_CONTROL_DEF 0x1f82 +#define MT9P031_SHUTTER_WIDTH_UPPER 0x08 +#define MT9P031_SHUTTER_WIDTH_LOWER 0x09 +#define MT9P031_SHUTTER_WIDTH_MIN 1 +#define MT9P031_SHUTTER_WIDTH_MAX 1048575 +#define MT9P031_SHUTTER_WIDTH_DEF 1943 +#define MT9P031_PLL_CONTROL 0x10 +#define MT9P031_PLL_CONTROL_PWROFF 0x0050 +#define MT9P031_PLL_CONTROL_PWRON 0x0051 +#define MT9P031_PLL_CONTROL_USEPLL 0x0052 +#define MT9P031_PLL_CONFIG_1 0x11 +#define MT9P031_PLL_CONFIG_2 0x12 +#define MT9P031_PIXEL_CLOCK_CONTROL 0x0a +#define MT9P031_FRAME_RESTART 0x0b +#define MT9P031_SHUTTER_DELAY 0x0c +#define MT9P031_RST 0x0d +#define MT9P031_RST_ENABLE 1 +#define MT9P031_RST_DISABLE 0 +#define MT9P031_READ_MODE_1 0x1e +#define MT9P031_READ_MODE_2 0x20 +#define MT9P031_READ_MODE_2_ROW_MIR (1 << 15) +#define MT9P031_READ_MODE_2_COL_MIR (1 << 14) +#define MT9P031_READ_MODE_2_ROW_BLC (1 << 6) +#define MT9P031_ROW_ADDRESS_MODE 0x22 +#define MT9P031_COLUMN_ADDRESS_MODE 0x23 +#define MT9P031_GLOBAL_GAIN 0x35 +#define MT9P031_GLOBAL_GAIN_MIN 8 +#define MT9P031_GLOBAL_GAIN_MAX 1024 +#define MT9P031_GLOBAL_GAIN_DEF 8 +#define MT9P031_GLOBAL_GAIN_MULT (1 << 6) +#define MT9P031_ROW_BLACK_DEF_OFFSET 0x4b +#define MT9P031_TEST_PATTERN 0xa0 +#define MT9P031_TEST_PATTERN_SHIFT 3 +#define MT9P031_TEST_PATTERN_ENABLE (1 << 0) +#define MT9P031_TEST_PATTERN_DISABLE (0 << 0) +#define MT9P031_TEST_PATTERN_GREEN 0xa1 +#define MT9P031_TEST_PATTERN_RED 0xa2 +#define MT9P031_TEST_PATTERN_BLUE 0xa3 + +struct mt9p031_pll_divs { + u32 ext_freq; + u32 target_freq; + u8 m; + u8 n; + u8 p1; +}; + +struct mt9p031 { + struct v4l2_subdev subdev; + struct media_pad pad; + struct v4l2_rect crop; /* Sensor window */ + struct v4l2_mbus_framefmt format; + struct v4l2_ctrl_handler ctrls; + struct mt9p031_platform_data *pdata; + struct mutex power_lock; /* lock to protect power_count */ + int power_count; + u16 xskip; + u16 yskip; + + const struct mt9p031_pll_divs *pll; + + /* Registers cache */ + u16 output_control; + u16 mode2; +}; + +static struct mt9p031 *to_mt9p031(struct v4l2_subdev *sd) +{ + return container_of(sd, struct mt9p031, subdev); +} + +static int mt9p031_read(struct i2c_client *client, u8 reg) +{ + s32 data = i2c_smbus_read_word_data(client, reg); + return data < 0 ? data : be16_to_cpu(data); +} + +static int mt9p031_write(struct i2c_client *client, u8 reg, u16 data) +{ + return i2c_smbus_write_word_data(client, reg, cpu_to_be16(data)); +} + +static int mt9p031_set_output_control(struct mt9p031 *mt9p031, u16 clear, + u16 set) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); + u16 value = (mt9p031->output_control & ~clear) | set; + int ret; + + ret = mt9p031_write(client, MT9P031_OUTPUT_CONTROL, value); + if (ret < 0) + return ret; + + mt9p031->output_control = value; + return 0; +} + +static int mt9p031_set_mode2(struct mt9p031 *mt9p031, u16 clear, u16 set) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); + u16 value = (mt9p031->mode2 & ~clear) | set; + int ret; + + ret = mt9p031_write(client, MT9P031_READ_MODE_2, value); + if (ret < 0) + return ret; + + mt9p031->mode2 = value; + return 0; +} + +static int mt9p031_reset(struct mt9p031 *mt9p031) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); + int ret; + + /* Disable chip output, synchronous option update */ + ret = mt9p031_write(client, MT9P031_RST, MT9P031_RST_ENABLE); + if (ret < 0) + return ret; + ret = mt9p031_write(client, MT9P031_RST, MT9P031_RST_DISABLE); + if (ret < 0) + return ret; + + return mt9p031_set_output_control(mt9p031, MT9P031_OUTPUT_CONTROL_CEN, + 0); +} + +/* + * This static table uses ext_freq and vdd_io values to select suitable + * PLL dividers m, n and p1 which have been calculated as specifiec in p36 + * of Aptina's mt9p031 datasheet. New values should be added here. + */ +static const struct mt9p031_pll_divs mt9p031_divs[] = { + /* ext_freq target_freq m n p1 */ + {21000000, 48000000, 26, 2, 6} +}; + +static int mt9p031_pll_get_divs(struct mt9p031 *mt9p031) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); + int i; + + for (i = 0; i < ARRAY_SIZE(mt9p031_divs); i++) { + if (mt9p031_divs[i].ext_freq == mt9p031->pdata->ext_freq && + mt9p031_divs[i].target_freq == mt9p031->pdata->target_freq) { + mt9p031->pll = &mt9p031_divs[i]; + return 0; + } + } + + dev_err(&client->dev, "Couldn't find PLL dividers for ext_freq = %d, " + "target_freq = %d\n", mt9p031->pdata->ext_freq, + mt9p031->pdata->target_freq); + return -EINVAL; +} + +static int mt9p031_pll_enable(struct mt9p031 *mt9p031) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); + int ret; + + ret = mt9p031_write(client, MT9P031_PLL_CONTROL, + MT9P031_PLL_CONTROL_PWRON); + if (ret < 0) + return ret; + + ret = mt9p031_write(client, MT9P031_PLL_CONFIG_1, + (mt9p031->pll->m << 8) | (mt9p031->pll->n - 1)); + if (ret < 0) + return ret; + + ret = mt9p031_write(client, MT9P031_PLL_CONFIG_2, mt9p031->pll->p1 - 1); + if (ret < 0) + return ret; + + usleep_range(1000, 2000); + ret = mt9p031_write(client, MT9P031_PLL_CONTROL, + MT9P031_PLL_CONTROL_PWRON | + MT9P031_PLL_CONTROL_USEPLL); + return ret; +} + +static inline int mt9p031_pll_disable(struct mt9p031 *mt9p031) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); + + return mt9p031_write(client, MT9P031_PLL_CONTROL, + MT9P031_PLL_CONTROL_PWROFF); +} + +static int mt9p031_power_on(struct mt9p031 *mt9p031) +{ + /* Ensure RESET_BAR is low */ + if (mt9p031->pdata->reset) { + mt9p031->pdata->reset(&mt9p031->subdev, 1); + usleep_range(1000, 2000); + } + + /* Emable clock */ + if (mt9p031->pdata->set_xclk) + mt9p031->pdata->set_xclk(&mt9p031->subdev, + mt9p031->pdata->ext_freq); + + /* Now RESET_BAR must be high */ + if (mt9p031->pdata->reset) { + mt9p031->pdata->reset(&mt9p031->subdev, 0); + usleep_range(1000, 2000); + } + + return 0; +} + +static void mt9p031_power_off(struct mt9p031 *mt9p031) +{ + if (mt9p031->pdata->reset) { + mt9p031->pdata->reset(&mt9p031->subdev, 1); + usleep_range(1000, 2000); + } + + if (mt9p031->pdata->set_xclk) + mt9p031->pdata->set_xclk(&mt9p031->subdev, 0); +} + +static int __mt9p031_set_power(struct mt9p031 *mt9p031, bool on) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); + int ret; + + if (!on) { + mt9p031_power_off(mt9p031); + return 0; + } + + ret = mt9p031_power_on(mt9p031); + if (ret < 0) + return ret; + + ret = mt9p031_reset(mt9p031); + if (ret < 0) { + dev_err(&client->dev, "Failed to reset the camera\n"); + return ret; + } + + return v4l2_ctrl_handler_setup(&mt9p031->ctrls); +} + +/* ----------------------------------------------------------------------------- + * V4L2 subdev video operations + */ + +static int mt9p031_set_params(struct mt9p031 *mt9p031) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); + struct v4l2_mbus_framefmt *format = &mt9p031->format; + const struct v4l2_rect *crop = &mt9p031->crop; + unsigned int hblank; + unsigned int vblank; + unsigned int xskip; + unsigned int yskip; + unsigned int xbin; + unsigned int ybin; + int ret; + + /* Windows position and size. + * + * TODO: Make sure the start coordinates and window size match the + * skipping, binning and mirroring (see description of registers 2 and 4 + * in table 13, and Binning section on page 41). + */ + ret = mt9p031_write(client, MT9P031_COLUMN_START, crop->left); + if (ret < 0) + return ret; + ret = mt9p031_write(client, MT9P031_ROW_START, crop->top); + if (ret < 0) + return ret; + ret = mt9p031_write(client, MT9P031_WINDOW_WIDTH, crop->width - 1); + if (ret < 0) + return ret; + ret = mt9p031_write(client, MT9P031_WINDOW_HEIGHT, crop->height - 1); + if (ret < 0) + return ret; + + /* Row and column binning and skipping. Use the maximum binning value + * compatible with the skipping settings. + */ + xskip = DIV_ROUND_CLOSEST(crop->width, format->width); + yskip = DIV_ROUND_CLOSEST(crop->height, format->height); + xbin = 1 << (ffs(xskip) - 1); + ybin = 1 << (ffs(yskip) - 1); + + ret = mt9p031_write(client, MT9P031_COLUMN_ADDRESS_MODE, + ((xbin - 1) << 4) | (xskip - 1)); + if (ret < 0) + return ret; + ret = mt9p031_write(client, MT9P031_ROW_ADDRESS_MODE, + ((ybin - 1) << 4) | (yskip - 1)); + if (ret < 0) + return ret; + + /* Blanking - use minimum value for horizontal blanking and default + * value for vertical blanking. + */ + hblank = 346 * ybin + 64 + (80 >> max_t(unsigned int, xbin, 3)); + vblank = MT9P031_VERTICAL_BLANK_DEF; + + ret = mt9p031_write(client, MT9P031_HORIZONTAL_BLANK, hblank); + if (ret < 0) + return ret; + ret = mt9p031_write(client, MT9P031_VERTICAL_BLANK, vblank); + if (ret < 0) + return ret; + + return ret; +} + +static int mt9p031_s_stream(struct v4l2_subdev *subdev, int enable) +{ + struct mt9p031 *mt9p031 = to_mt9p031(subdev); + int ret; + + if (!enable) { + /* Stop sensor readout */ + ret = mt9p031_set_output_control(mt9p031, + MT9P031_OUTPUT_CONTROL_CEN, 0); + if (ret < 0) + return ret; + + return mt9p031_pll_disable(mt9p031); + } + + ret = mt9p031_set_params(mt9p031); + if (ret < 0) + return ret; + + /* Switch to master "normal" mode */ + ret = mt9p031_set_output_control(mt9p031, 0, + MT9P031_OUTPUT_CONTROL_CEN); + if (ret < 0) + return ret; + + return mt9p031_pll_enable(mt9p031); +} + +static int mt9p031_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct mt9p031 *mt9p031 = to_mt9p031(subdev); + + if (code->pad || code->index) + return -EINVAL; + + code->code = mt9p031->format.code; + return 0; +} + +static int mt9p031_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct mt9p031 *mt9p031 = to_mt9p031(subdev); + + if (fse->index >= 8 || fse->code != mt9p031->format.code) + return -EINVAL; + + fse->min_width = MT9P031_WINDOW_WIDTH_DEF + / min_t(unsigned int, 7, fse->index + 1); + fse->max_width = fse->min_width; + fse->min_height = MT9P031_WINDOW_HEIGHT_DEF / (fse->index + 1); + fse->max_height = fse->min_height; + + return 0; +} + +static struct v4l2_mbus_framefmt * +__mt9p031_get_pad_format(struct mt9p031 *mt9p031, struct v4l2_subdev_fh *fh, + unsigned int pad, u32 which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_format(fh, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &mt9p031->format; + default: + return NULL; + } +} + +static struct v4l2_rect * +__mt9p031_get_pad_crop(struct mt9p031 *mt9p031, struct v4l2_subdev_fh *fh, + unsigned int pad, u32 which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_crop(fh, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &mt9p031->crop; + default: + return NULL; + } +} + +static int mt9p031_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct mt9p031 *mt9p031 = to_mt9p031(subdev); + + fmt->format = *__mt9p031_get_pad_format(mt9p031, fh, fmt->pad, + fmt->which); + return 0; +} + +static int mt9p031_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *format) +{ + struct mt9p031 *mt9p031 = to_mt9p031(subdev); + struct v4l2_mbus_framefmt *__format; + struct v4l2_rect *__crop; + unsigned int width; + unsigned int height; + unsigned int hratio; + unsigned int vratio; + + __crop = __mt9p031_get_pad_crop(mt9p031, fh, format->pad, + 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 / 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); + + hratio = DIV_ROUND_CLOSEST(__crop->width, width); + vratio = DIV_ROUND_CLOSEST(__crop->height, height); + + __format = __mt9p031_get_pad_format(mt9p031, fh, format->pad, + format->which); + __format->width = __crop->width / hratio; + __format->height = __crop->height / vratio; + + format->format = *__format; + + return 0; +} + +static int mt9p031_get_crop(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_crop *crop) +{ + struct mt9p031 *mt9p031 = to_mt9p031(subdev); + + crop->rect = *__mt9p031_get_pad_crop(mt9p031, fh, crop->pad, + crop->which); + return 0; +} + +static int mt9p031_set_crop(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_crop *crop) +{ + struct mt9p031 *mt9p031 = to_mt9p031(subdev); + struct v4l2_mbus_framefmt *__format; + struct v4l2_rect *__crop; + struct v4l2_rect rect; + + /* Clamp the crop rectangle boundaries and align them to a multiple of 2 + * pixels to ensure a GRBG Bayer pattern. + */ + rect.left = clamp(ALIGN(crop->rect.left, 2), MT9P031_COLUMN_START_MIN, + 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); + + __crop = __mt9p031_get_pad_crop(mt9p031, fh, crop->pad, crop->which); + + if (rect.width != __crop->width || rect.height != __crop->height) { + /* Reset the output image size if the crop rectangle size has + * been modified. + */ + __format = __mt9p031_get_pad_format(mt9p031, fh, crop->pad, + crop->which); + __format->width = rect.width; + __format->height = rect.height; + } + + *__crop = rect; + crop->rect = rect; + + return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 subdev control operations + */ + +#define V4L2_CID_TEST_PATTERN (V4L2_CID_USER_BASE | 0x1001) + +static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mt9p031 *mt9p031 = + container_of(ctrl->handler, struct mt9p031, ctrls); + struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); + u16 data; + int ret; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + ret = mt9p031_write(client, MT9P031_SHUTTER_WIDTH_UPPER, + (ctrl->val >> 16) & 0xffff); + if (ret < 0) + return ret; + + return mt9p031_write(client, MT9P031_SHUTTER_WIDTH_LOWER, + ctrl->val & 0xffff); + + case V4L2_CID_GAIN: + /* Gain is controlled by 2 analog stages and a digital stage. + * Valid values for the 3 stages are + * + * Stage Min Max Step + * ------------------------------------------ + * First analog stage x1 x2 1 + * Second analog stage x1 x4 0.125 + * Digital stage x1 x16 0.125 + * + * To minimize noise, the gain stages should be used in the + * second analog stage, first analog stage, digital stage order. + * Gain from a previous stage should be pushed to its maximum + * value before the next stage is used. + */ + if (ctrl->val <= 32) { + data = ctrl->val; + } else if (ctrl->val <= 64) { + ctrl->val &= ~1; + data = (1 << 6) | (ctrl->val >> 1); + } else { + ctrl->val &= ~7; + data = ((ctrl->val - 64) << 5) | (1 << 6) | 32; + } + + return mt9p031_write(client, MT9P031_GLOBAL_GAIN, data); + + case V4L2_CID_HFLIP: + if (ctrl->val) + return mt9p031_set_mode2(mt9p031, + 0, MT9P031_READ_MODE_2_COL_MIR); + else + return mt9p031_set_mode2(mt9p031, + MT9P031_READ_MODE_2_COL_MIR, 0); + + case V4L2_CID_VFLIP: + if (ctrl->val) + return mt9p031_set_mode2(mt9p031, + 0, MT9P031_READ_MODE_2_ROW_MIR); + else + return mt9p031_set_mode2(mt9p031, + MT9P031_READ_MODE_2_ROW_MIR, 0); + + case V4L2_CID_TEST_PATTERN: + if (!ctrl->val) { + ret = mt9p031_set_mode2(mt9p031, + 0, MT9P031_READ_MODE_2_ROW_BLC); + if (ret < 0) + return ret; + + return mt9p031_write(client, MT9P031_TEST_PATTERN, + MT9P031_TEST_PATTERN_DISABLE); + } + + ret = mt9p031_write(client, MT9P031_TEST_PATTERN_GREEN, 0x05a0); + if (ret < 0) + return ret; + ret = mt9p031_write(client, MT9P031_TEST_PATTERN_RED, 0x0a50); + if (ret < 0) + return ret; + ret = mt9p031_write(client, MT9P031_TEST_PATTERN_BLUE, 0x0aa0); + if (ret < 0) + return ret; + + ret = mt9p031_set_mode2(mt9p031, MT9P031_READ_MODE_2_ROW_BLC, + 0); + if (ret < 0) + return ret; + ret = mt9p031_write(client, MT9P031_ROW_BLACK_DEF_OFFSET, 0); + if (ret < 0) + return ret; + + return mt9p031_write(client, MT9P031_TEST_PATTERN, + ((ctrl->val - 1) << MT9P031_TEST_PATTERN_SHIFT) + | MT9P031_TEST_PATTERN_ENABLE); + } + return 0; +} + +static struct v4l2_ctrl_ops mt9p031_ctrl_ops = { + .s_ctrl = mt9p031_s_ctrl, +}; + +static const char * const mt9p031_test_pattern_menu[] = { + "Disabled", + "Color Field", + "Horizontal Gradient", + "Vertical Gradient", + "Diagonal Gradient", + "Classic Test Pattern", + "Walking 1s", + "Monochrome Horizontal Bars", + "Monochrome Vertical Bars", + "Vertical Color Bars", +}; + +static const struct v4l2_ctrl_config mt9p031_ctrls[] = { + { + .ops = &mt9p031_ctrl_ops, + .id = V4L2_CID_TEST_PATTERN, + .type = V4L2_CTRL_TYPE_MENU, + .name = "Test Pattern", + .min = 0, + .max = ARRAY_SIZE(mt9p031_test_pattern_menu) - 1, + .step = 0, + .def = 0, + .flags = 0, + .menu_skip_mask = 0, + .qmenu = mt9p031_test_pattern_menu, + } +}; + +/* ----------------------------------------------------------------------------- + * V4L2 subdev core operations + */ + +static int mt9p031_set_power(struct v4l2_subdev *subdev, int on) +{ + struct mt9p031 *mt9p031 = to_mt9p031(subdev); + int ret = 0; + + mutex_lock(&mt9p031->power_lock); + + /* If the power count is modified from 0 to != 0 or from != 0 to 0, + * update the power state. + */ + if (mt9p031->power_count == !on) { + ret = __mt9p031_set_power(mt9p031, !!on); + if (ret < 0) + goto out; + } + + /* Update the power count. */ + mt9p031->power_count += on ? 1 : -1; + WARN_ON(mt9p031->power_count < 0); + +out: + mutex_unlock(&mt9p031->power_lock); + return ret; +} + +/* ----------------------------------------------------------------------------- + * V4L2 subdev internal operations + */ + +static int mt9p031_registered(struct v4l2_subdev *subdev) +{ + struct i2c_client *client = v4l2_get_subdevdata(subdev); + struct mt9p031 *mt9p031 = to_mt9p031(subdev); + s32 data; + int ret; + + ret = mt9p031_power_on(mt9p031); + if (ret < 0) { + dev_err(&client->dev, "MT9P031 power up failed\n"); + return ret; + } + + /* Read out the chip version register */ + data = mt9p031_read(client, MT9P031_CHIP_VERSION); + if (data != MT9P031_CHIP_VERSION_VALUE) { + dev_err(&client->dev, "MT9P031 not detected, wrong version " + "0x%04x\n", data); + return -ENODEV; + } + + mt9p031_power_off(mt9p031); + + dev_info(&client->dev, "MT9P031 detected at address 0x%02x\n", + client->addr); + + return ret; +} + +static int mt9p031_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) +{ + struct mt9p031 *mt9p031 = to_mt9p031(subdev); + struct v4l2_mbus_framefmt *format; + struct v4l2_rect *crop; + + crop = v4l2_subdev_get_try_crop(fh, 0); + crop->left = MT9P031_COLUMN_START_DEF; + crop->top = MT9P031_ROW_START_DEF; + crop->width = MT9P031_WINDOW_WIDTH_DEF; + crop->height = MT9P031_WINDOW_HEIGHT_DEF; + + format = v4l2_subdev_get_try_format(fh, 0); + + if (mt9p031->pdata->version == MT9P031_MONOCHROME_VERSION) + format->code = V4L2_MBUS_FMT_Y12_1X12; + else + format->code = V4L2_MBUS_FMT_SGRBG12_1X12; + + format->width = MT9P031_WINDOW_WIDTH_DEF; + format->height = MT9P031_WINDOW_HEIGHT_DEF; + format->field = V4L2_FIELD_NONE; + format->colorspace = V4L2_COLORSPACE_SRGB; + + mt9p031->xskip = 1; + mt9p031->yskip = 1; + return mt9p031_set_power(subdev, 1); +} + +static int mt9p031_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) +{ + return mt9p031_set_power(subdev, 0); +} + +static struct v4l2_subdev_core_ops mt9p031_subdev_core_ops = { + .s_power = mt9p031_set_power, +}; + +static struct v4l2_subdev_video_ops mt9p031_subdev_video_ops = { + .s_stream = mt9p031_s_stream, +}; + +static struct v4l2_subdev_pad_ops mt9p031_subdev_pad_ops = { + .enum_mbus_code = mt9p031_enum_mbus_code, + .enum_frame_size = mt9p031_enum_frame_size, + .get_fmt = mt9p031_get_format, + .set_fmt = mt9p031_set_format, + .get_crop = mt9p031_get_crop, + .set_crop = mt9p031_set_crop, +}; + +static struct v4l2_subdev_ops mt9p031_subdev_ops = { + .core = &mt9p031_subdev_core_ops, + .video = &mt9p031_subdev_video_ops, + .pad = &mt9p031_subdev_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops mt9p031_subdev_internal_ops = { + .registered = mt9p031_registered, + .open = mt9p031_open, + .close = mt9p031_close, +}; + +/* ----------------------------------------------------------------------------- + * Driver initialization and probing + */ + +static int mt9p031_probe(struct i2c_client *client, + const struct i2c_device_id *did) +{ + struct mt9p031_platform_data *pdata = client->dev.platform_data; + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct mt9p031 *mt9p031; + unsigned int i; + int ret; + + if (pdata == NULL) { + dev_err(&client->dev, "No platform data\n"); + return -EINVAL; + } + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) { + dev_warn(&client->dev, + "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); + return -EIO; + } + + mt9p031 = kzalloc(sizeof(*mt9p031), GFP_KERNEL); + if (mt9p031 == NULL) + return -ENOMEM; + + mt9p031->pdata = pdata; + mt9p031->output_control = MT9P031_OUTPUT_CONTROL_DEF; + mt9p031->mode2 = MT9P031_READ_MODE_2_ROW_BLC; + + v4l2_ctrl_handler_init(&mt9p031->ctrls, ARRAY_SIZE(mt9p031_ctrls) + 4); + + v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops, + V4L2_CID_EXPOSURE, MT9P031_SHUTTER_WIDTH_MIN, + MT9P031_SHUTTER_WIDTH_MAX, 1, + MT9P031_SHUTTER_WIDTH_DEF); + v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops, + V4L2_CID_GAIN, MT9P031_GLOBAL_GAIN_MIN, + MT9P031_GLOBAL_GAIN_MAX, 1, MT9P031_GLOBAL_GAIN_DEF); + v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + + for (i = 0; i < ARRAY_SIZE(mt9p031_ctrls); ++i) + v4l2_ctrl_new_custom(&mt9p031->ctrls, &mt9p031_ctrls[i], NULL); + + mt9p031->subdev.ctrl_handler = &mt9p031->ctrls; + + if (mt9p031->ctrls.error) + printk(KERN_INFO "%s: control initialization error %d\n", + __func__, mt9p031->ctrls.error); + + mutex_init(&mt9p031->power_lock); + v4l2_i2c_subdev_init(&mt9p031->subdev, client, &mt9p031_subdev_ops); + mt9p031->subdev.internal_ops = &mt9p031_subdev_internal_ops; + + mt9p031->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_init(&mt9p031->subdev.entity, 1, &mt9p031->pad, 0); + if (ret < 0) + goto done; + + mt9p031->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + mt9p031->crop.width = MT9P031_WINDOW_WIDTH_DEF; + mt9p031->crop.height = MT9P031_WINDOW_HEIGHT_DEF; + mt9p031->crop.left = MT9P031_COLUMN_START_DEF; + mt9p031->crop.top = MT9P031_ROW_START_DEF; + + if (mt9p031->pdata->version == MT9P031_MONOCHROME_VERSION) + mt9p031->format.code = V4L2_MBUS_FMT_Y12_1X12; + else + mt9p031->format.code = V4L2_MBUS_FMT_SGRBG12_1X12; + + mt9p031->format.width = MT9P031_WINDOW_WIDTH_DEF; + mt9p031->format.height = MT9P031_WINDOW_HEIGHT_DEF; + mt9p031->format.field = V4L2_FIELD_NONE; + mt9p031->format.colorspace = V4L2_COLORSPACE_SRGB; + + ret = mt9p031_pll_get_divs(mt9p031); + +done: + if (ret < 0) { + v4l2_ctrl_handler_free(&mt9p031->ctrls); + media_entity_cleanup(&mt9p031->subdev.entity); + kfree(mt9p031); + } + + return ret; +} + +static int mt9p031_remove(struct i2c_client *client) +{ + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct mt9p031 *mt9p031 = to_mt9p031(subdev); + + v4l2_ctrl_handler_free(&mt9p031->ctrls); + v4l2_device_unregister_subdev(subdev); + media_entity_cleanup(&subdev->entity); + kfree(mt9p031); + + return 0; +} + +static const struct i2c_device_id mt9p031_id[] = { + { "mt9p031", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mt9p031_id); + +static struct i2c_driver mt9p031_i2c_driver = { + .driver = { + .name = "mt9p031", + }, + .probe = mt9p031_probe, + .remove = mt9p031_remove, + .id_table = mt9p031_id, +}; + +static int __init mt9p031_mod_init(void) +{ + return i2c_add_driver(&mt9p031_i2c_driver); +} + +static void __exit mt9p031_mod_exit(void) +{ + i2c_del_driver(&mt9p031_i2c_driver); +} + +module_init(mt9p031_mod_init); +module_exit(mt9p031_mod_exit); + +MODULE_DESCRIPTION("Aptina MT9P031 Camera driver"); +MODULE_AUTHOR("Bastian Hecht <hechtb@gmail.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/video/mt9t001.c b/drivers/media/video/mt9t001.c new file mode 100644 index 00000000000..08074b8a273 --- /dev/null +++ b/drivers/media/video/mt9t001.c @@ -0,0 +1,836 @@ +/* + * Driver for MT9T001 CMOS Image Sensor from Aptina (Micron) + * + * Copyright (C) 2010-2011, Laurent Pinchart <laurent.pinchart@ideasonboard.com> + * + * Based on the MT9M001 driver, + * + * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/log2.h> +#include <linux/slab.h> +#include <linux/videodev2.h> +#include <linux/v4l2-mediabus.h> + +#include <media/mt9t001.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-subdev.h> + +#define MT9T001_PIXEL_ARRAY_HEIGHT 1568 +#define MT9T001_PIXEL_ARRAY_WIDTH 2112 + +#define MT9T001_CHIP_VERSION 0x00 +#define MT9T001_CHIP_ID 0x1621 +#define MT9T001_ROW_START 0x01 +#define MT9T001_ROW_START_MIN 0 +#define MT9T001_ROW_START_DEF 20 +#define MT9T001_ROW_START_MAX 1534 +#define MT9T001_COLUMN_START 0x02 +#define MT9T001_COLUMN_START_MIN 0 +#define MT9T001_COLUMN_START_DEF 32 +#define MT9T001_COLUMN_START_MAX 2046 +#define MT9T001_WINDOW_HEIGHT 0x03 +#define MT9T001_WINDOW_HEIGHT_MIN 1 +#define MT9T001_WINDOW_HEIGHT_DEF 1535 +#define MT9T001_WINDOW_HEIGHT_MAX 1567 +#define MT9T001_WINDOW_WIDTH 0x04 +#define MT9T001_WINDOW_WIDTH_MIN 1 +#define MT9T001_WINDOW_WIDTH_DEF 2047 +#define MT9T001_WINDOW_WIDTH_MAX 2111 +#define MT9T001_HORIZONTAL_BLANKING 0x05 +#define MT9T001_HORIZONTAL_BLANKING_MIN 21 +#define MT9T001_HORIZONTAL_BLANKING_MAX 1023 +#define MT9T001_VERTICAL_BLANKING 0x06 +#define MT9T001_VERTICAL_BLANKING_MIN 3 +#define MT9T001_VERTICAL_BLANKING_MAX 1023 +#define MT9T001_OUTPUT_CONTROL 0x07 +#define MT9T001_OUTPUT_CONTROL_SYNC (1 << 0) +#define MT9T001_OUTPUT_CONTROL_CHIP_ENABLE (1 << 1) +#define MT9T001_OUTPUT_CONTROL_TEST_DATA (1 << 6) +#define MT9T001_SHUTTER_WIDTH_HIGH 0x08 +#define MT9T001_SHUTTER_WIDTH_LOW 0x09 +#define MT9T001_SHUTTER_WIDTH_MIN 1 +#define MT9T001_SHUTTER_WIDTH_DEF 1561 +#define MT9T001_SHUTTER_WIDTH_MAX (1024 * 1024) +#define MT9T001_PIXEL_CLOCK 0x0a +#define MT9T001_PIXEL_CLOCK_INVERT (1 << 15) +#define MT9T001_PIXEL_CLOCK_SHIFT_MASK (7 << 8) +#define MT9T001_PIXEL_CLOCK_SHIFT_SHIFT 8 +#define MT9T001_PIXEL_CLOCK_DIVIDE_MASK (0x7f << 0) +#define MT9T001_FRAME_RESTART 0x0b +#define MT9T001_SHUTTER_DELAY 0x0c +#define MT9T001_SHUTTER_DELAY_MAX 2047 +#define MT9T001_RESET 0x0d +#define MT9T001_READ_MODE1 0x1e +#define MT9T001_READ_MODE_SNAPSHOT (1 << 8) +#define MT9T001_READ_MODE_STROBE_ENABLE (1 << 9) +#define MT9T001_READ_MODE_STROBE_WIDTH (1 << 10) +#define MT9T001_READ_MODE_STROBE_OVERRIDE (1 << 11) +#define MT9T001_READ_MODE2 0x20 +#define MT9T001_READ_MODE_BAD_FRAMES (1 << 0) +#define MT9T001_READ_MODE_LINE_VALID_CONTINUOUS (1 << 9) +#define MT9T001_READ_MODE_LINE_VALID_FRAME (1 << 10) +#define MT9T001_READ_MODE3 0x21 +#define MT9T001_READ_MODE_GLOBAL_RESET (1 << 0) +#define MT9T001_READ_MODE_GHST_CTL (1 << 1) +#define MT9T001_ROW_ADDRESS_MODE 0x22 +#define MT9T001_ROW_SKIP_MASK (7 << 0) +#define MT9T001_ROW_BIN_MASK (3 << 3) +#define MT9T001_ROW_BIN_SHIFT 3 +#define MT9T001_COLUMN_ADDRESS_MODE 0x23 +#define MT9T001_COLUMN_SKIP_MASK (7 << 0) +#define MT9T001_COLUMN_BIN_MASK (3 << 3) +#define MT9T001_COLUMN_BIN_SHIFT 3 +#define MT9T001_GREEN1_GAIN 0x2b +#define MT9T001_BLUE_GAIN 0x2c +#define MT9T001_RED_GAIN 0x2d +#define MT9T001_GREEN2_GAIN 0x2e +#define MT9T001_TEST_DATA 0x32 +#define MT9T001_GLOBAL_GAIN 0x35 +#define MT9T001_GLOBAL_GAIN_MIN 8 +#define MT9T001_GLOBAL_GAIN_MAX 1024 +#define MT9T001_BLACK_LEVEL 0x49 +#define MT9T001_ROW_BLACK_DEFAULT_OFFSET 0x4b +#define MT9T001_BLC_DELTA_THRESHOLDS 0x5d +#define MT9T001_CAL_THRESHOLDS 0x5f +#define MT9T001_GREEN1_OFFSET 0x60 +#define MT9T001_GREEN2_OFFSET 0x61 +#define MT9T001_BLACK_LEVEL_CALIBRATION 0x62 +#define MT9T001_BLACK_LEVEL_OVERRIDE (1 << 0) +#define MT9T001_BLACK_LEVEL_DISABLE_OFFSET (1 << 1) +#define MT9T001_BLACK_LEVEL_RECALCULATE (1 << 12) +#define MT9T001_BLACK_LEVEL_LOCK_RED_BLUE (1 << 13) +#define MT9T001_BLACK_LEVEL_LOCK_GREEN (1 << 14) +#define MT9T001_RED_OFFSET 0x63 +#define MT9T001_BLUE_OFFSET 0x64 + +struct mt9t001 { + struct v4l2_subdev subdev; + struct media_pad pad; + + struct v4l2_mbus_framefmt format; + struct v4l2_rect crop; + + struct v4l2_ctrl_handler ctrls; + struct v4l2_ctrl *gains[4]; + + u16 output_control; + u16 black_level; +}; + +static inline struct mt9t001 *to_mt9t001(struct v4l2_subdev *sd) +{ + return container_of(sd, struct mt9t001, subdev); +} + +static int mt9t001_read(struct i2c_client *client, u8 reg) +{ + s32 data = i2c_smbus_read_word_data(client, reg); + return data < 0 ? data : be16_to_cpu(data); +} + +static int mt9t001_write(struct i2c_client *client, u8 reg, u16 data) +{ + return i2c_smbus_write_word_data(client, reg, cpu_to_be16(data)); +} + +static int mt9t001_set_output_control(struct mt9t001 *mt9t001, u16 clear, + u16 set) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mt9t001->subdev); + u16 value = (mt9t001->output_control & ~clear) | set; + int ret; + + if (value == mt9t001->output_control) + return 0; + + ret = mt9t001_write(client, MT9T001_OUTPUT_CONTROL, value); + if (ret < 0) + return ret; + + mt9t001->output_control = value; + return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 subdev video operations + */ + +static struct v4l2_mbus_framefmt * +__mt9t001_get_pad_format(struct mt9t001 *mt9t001, struct v4l2_subdev_fh *fh, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_format(fh, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &mt9t001->format; + default: + return NULL; + } +} + +static struct v4l2_rect * +__mt9t001_get_pad_crop(struct mt9t001 *mt9t001, struct v4l2_subdev_fh *fh, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_crop(fh, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &mt9t001->crop; + default: + return NULL; + } +} + +static int mt9t001_s_stream(struct v4l2_subdev *subdev, int enable) +{ + const u16 mode = MT9T001_OUTPUT_CONTROL_CHIP_ENABLE; + struct i2c_client *client = v4l2_get_subdevdata(subdev); + struct mt9t001 *mt9t001 = to_mt9t001(subdev); + struct v4l2_mbus_framefmt *format = &mt9t001->format; + struct v4l2_rect *crop = &mt9t001->crop; + unsigned int hratio; + unsigned int vratio; + int ret; + + if (!enable) + return mt9t001_set_output_control(mt9t001, mode, 0); + + /* Configure the window size and row/column bin */ + hratio = DIV_ROUND_CLOSEST(crop->width, format->width); + vratio = DIV_ROUND_CLOSEST(crop->height, format->height); + + ret = mt9t001_write(client, MT9T001_ROW_ADDRESS_MODE, hratio - 1); + if (ret < 0) + return ret; + + ret = mt9t001_write(client, MT9T001_COLUMN_ADDRESS_MODE, vratio - 1); + if (ret < 0) + return ret; + + ret = mt9t001_write(client, MT9T001_COLUMN_START, crop->left); + if (ret < 0) + return ret; + + ret = mt9t001_write(client, MT9T001_ROW_START, crop->top); + if (ret < 0) + return ret; + + ret = mt9t001_write(client, MT9T001_WINDOW_WIDTH, crop->width - 1); + if (ret < 0) + return ret; + + ret = mt9t001_write(client, MT9T001_WINDOW_HEIGHT, crop->height - 1); + if (ret < 0) + return ret; + + /* Switch to master "normal" mode */ + return mt9t001_set_output_control(mt9t001, 0, mode); +} + +static int mt9t001_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index > 0) + return -EINVAL; + + code->code = V4L2_MBUS_FMT_SGRBG10_1X10; + return 0; +} + +static int mt9t001_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index >= 8 || fse->code != V4L2_MBUS_FMT_SGRBG10_1X10) + return -EINVAL; + + fse->min_width = (MT9T001_WINDOW_WIDTH_DEF + 1) / fse->index; + fse->max_width = fse->min_width; + fse->min_height = (MT9T001_WINDOW_HEIGHT_DEF + 1) / fse->index; + fse->max_height = fse->min_height; + + return 0; +} + +static int mt9t001_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *format) +{ + struct mt9t001 *mt9t001 = to_mt9t001(subdev); + + format->format = *__mt9t001_get_pad_format(mt9t001, fh, format->pad, + format->which); + return 0; +} + +static int mt9t001_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *format) +{ + struct mt9t001 *mt9t001 = to_mt9t001(subdev); + struct v4l2_mbus_framefmt *__format; + struct v4l2_rect *__crop; + unsigned int width; + unsigned int height; + unsigned int hratio; + unsigned int vratio; + + __crop = __mt9t001_get_pad_crop(mt9t001, fh, format->pad, + 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 / 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), + __crop->height); + + hratio = DIV_ROUND_CLOSEST(__crop->width, width); + vratio = DIV_ROUND_CLOSEST(__crop->height, height); + + __format = __mt9t001_get_pad_format(mt9t001, fh, format->pad, + format->which); + __format->width = __crop->width / hratio; + __format->height = __crop->height / vratio; + + format->format = *__format; + + return 0; +} + +static int mt9t001_get_crop(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_crop *crop) +{ + struct mt9t001 *mt9t001 = to_mt9t001(subdev); + + crop->rect = *__mt9t001_get_pad_crop(mt9t001, fh, crop->pad, + crop->which); + return 0; +} + +static int mt9t001_set_crop(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_crop *crop) +{ + struct mt9t001 *mt9t001 = to_mt9t001(subdev); + struct v4l2_mbus_framefmt *__format; + struct v4l2_rect *__crop; + struct v4l2_rect rect; + + /* Clamp the crop rectangle boundaries and align them to a multiple of 2 + * pixels. + */ + rect.left = clamp(ALIGN(crop->rect.left, 2), + MT9T001_COLUMN_START_MIN, + MT9T001_COLUMN_START_MAX); + 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); + + __crop = __mt9t001_get_pad_crop(mt9t001, fh, crop->pad, crop->which); + + if (rect.width != __crop->width || rect.height != __crop->height) { + /* Reset the output image size if the crop rectangle size has + * been modified. + */ + __format = __mt9t001_get_pad_format(mt9t001, fh, crop->pad, + crop->which); + __format->width = rect.width; + __format->height = rect.height; + } + + *__crop = rect; + crop->rect = rect; + + return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 subdev control operations + */ + +#define V4L2_CID_TEST_PATTERN (V4L2_CID_USER_BASE | 0x1001) +#define V4L2_CID_BLACK_LEVEL_AUTO (V4L2_CID_USER_BASE | 0x1002) +#define V4L2_CID_BLACK_LEVEL_OFFSET (V4L2_CID_USER_BASE | 0x1003) +#define V4L2_CID_BLACK_LEVEL_CALIBRATE (V4L2_CID_USER_BASE | 0x1004) + +#define V4L2_CID_GAIN_RED (V4L2_CTRL_CLASS_CAMERA | 0x1001) +#define V4L2_CID_GAIN_GREEN_RED (V4L2_CTRL_CLASS_CAMERA | 0x1002) +#define V4L2_CID_GAIN_GREEN_BLUE (V4L2_CTRL_CLASS_CAMERA | 0x1003) +#define V4L2_CID_GAIN_BLUE (V4L2_CTRL_CLASS_CAMERA | 0x1004) + +static u16 mt9t001_gain_value(s32 *gain) +{ + /* Gain is controlled by 2 analog stages and a digital stage. Valid + * values for the 3 stages are + * + * Stage Min Max Step + * ------------------------------------------ + * First analog stage x1 x2 1 + * Second analog stage x1 x4 0.125 + * Digital stage x1 x16 0.125 + * + * To minimize noise, the gain stages should be used in the second + * analog stage, first analog stage, digital stage order. Gain from a + * previous stage should be pushed to its maximum value before the next + * stage is used. + */ + if (*gain <= 32) + return *gain; + + if (*gain <= 64) { + *gain &= ~1; + return (1 << 6) | (*gain >> 1); + } + + *gain &= ~7; + return ((*gain - 64) << 5) | (1 << 6) | 32; +} + +static int mt9t001_ctrl_freeze(struct mt9t001 *mt9t001, bool freeze) +{ + return mt9t001_set_output_control(mt9t001, + freeze ? 0 : MT9T001_OUTPUT_CONTROL_SYNC, + freeze ? MT9T001_OUTPUT_CONTROL_SYNC : 0); +} + +static int mt9t001_s_ctrl(struct v4l2_ctrl *ctrl) +{ + static const u8 gains[4] = { + MT9T001_RED_GAIN, MT9T001_GREEN1_GAIN, + MT9T001_GREEN2_GAIN, MT9T001_BLUE_GAIN + }; + + struct mt9t001 *mt9t001 = + container_of(ctrl->handler, struct mt9t001, ctrls); + struct i2c_client *client = v4l2_get_subdevdata(&mt9t001->subdev); + unsigned int count; + unsigned int i; + u16 value; + int ret; + + switch (ctrl->id) { + case V4L2_CID_GAIN_RED: + case V4L2_CID_GAIN_GREEN_RED: + case V4L2_CID_GAIN_GREEN_BLUE: + case V4L2_CID_GAIN_BLUE: + + /* Disable control updates if more than one control has changed + * in the cluster. + */ + for (i = 0, count = 0; i < 4; ++i) { + struct v4l2_ctrl *gain = mt9t001->gains[i]; + + if (gain->val != gain->cur.val) + count++; + } + + if (count > 1) { + ret = mt9t001_ctrl_freeze(mt9t001, true); + if (ret < 0) + return ret; + } + + /* Update the gain controls. */ + for (i = 0; i < 4; ++i) { + struct v4l2_ctrl *gain = mt9t001->gains[i]; + + if (gain->val == gain->cur.val) + continue; + + value = mt9t001_gain_value(&gain->val); + ret = mt9t001_write(client, gains[i], value); + if (ret < 0) { + mt9t001_ctrl_freeze(mt9t001, false); + return ret; + } + } + + /* Enable control updates. */ + if (count > 1) { + ret = mt9t001_ctrl_freeze(mt9t001, false); + if (ret < 0) + return ret; + } + + break; + + case V4L2_CID_EXPOSURE: + ret = mt9t001_write(client, MT9T001_SHUTTER_WIDTH_LOW, + ctrl->val & 0xffff); + if (ret < 0) + return ret; + + return mt9t001_write(client, MT9T001_SHUTTER_WIDTH_HIGH, + ctrl->val >> 16); + + case V4L2_CID_TEST_PATTERN: + ret = mt9t001_set_output_control(mt9t001, + ctrl->val ? 0 : MT9T001_OUTPUT_CONTROL_TEST_DATA, + ctrl->val ? MT9T001_OUTPUT_CONTROL_TEST_DATA : 0); + if (ret < 0) + return ret; + + return mt9t001_write(client, MT9T001_TEST_DATA, ctrl->val << 2); + + case V4L2_CID_BLACK_LEVEL_AUTO: + value = ctrl->val ? 0 : MT9T001_BLACK_LEVEL_OVERRIDE; + ret = mt9t001_write(client, MT9T001_BLACK_LEVEL_CALIBRATION, + value); + if (ret < 0) + return ret; + + mt9t001->black_level = value; + break; + + case V4L2_CID_BLACK_LEVEL_OFFSET: + ret = mt9t001_write(client, MT9T001_GREEN1_OFFSET, ctrl->val); + if (ret < 0) + return ret; + + ret = mt9t001_write(client, MT9T001_GREEN2_OFFSET, ctrl->val); + if (ret < 0) + return ret; + + ret = mt9t001_write(client, MT9T001_RED_OFFSET, ctrl->val); + if (ret < 0) + return ret; + + return mt9t001_write(client, MT9T001_BLUE_OFFSET, ctrl->val); + + case V4L2_CID_BLACK_LEVEL_CALIBRATE: + return mt9t001_write(client, MT9T001_BLACK_LEVEL_CALIBRATION, + MT9T001_BLACK_LEVEL_RECALCULATE | + mt9t001->black_level); + } + + return 0; +} + +static struct v4l2_ctrl_ops mt9t001_ctrl_ops = { + .s_ctrl = mt9t001_s_ctrl, +}; + +static const struct v4l2_ctrl_config mt9t001_ctrls[] = { + { + .ops = &mt9t001_ctrl_ops, + .id = V4L2_CID_TEST_PATTERN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Test pattern", + .min = 0, + .max = 1023, + .step = 1, + .def = 0, + .flags = 0, + }, { + .ops = &mt9t001_ctrl_ops, + .id = V4L2_CID_BLACK_LEVEL_AUTO, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Black Level, Auto", + .min = 0, + .max = 1, + .step = 1, + .def = 1, + .flags = 0, + }, { + .ops = &mt9t001_ctrl_ops, + .id = V4L2_CID_BLACK_LEVEL_OFFSET, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Black Level, Offset", + .min = -256, + .max = 255, + .step = 1, + .def = 32, + .flags = 0, + }, { + .ops = &mt9t001_ctrl_ops, + .id = V4L2_CID_BLACK_LEVEL_CALIBRATE, + .type = V4L2_CTRL_TYPE_BUTTON, + .name = "Black Level, Calibrate", + .min = 0, + .max = 0, + .step = 0, + .def = 0, + .flags = V4L2_CTRL_FLAG_WRITE_ONLY, + }, +}; + +static const struct v4l2_ctrl_config mt9t001_gains[] = { + { + .ops = &mt9t001_ctrl_ops, + .id = V4L2_CID_GAIN_RED, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain, Red", + .min = MT9T001_GLOBAL_GAIN_MIN, + .max = MT9T001_GLOBAL_GAIN_MAX, + .step = 1, + .def = MT9T001_GLOBAL_GAIN_MIN, + .flags = 0, + }, { + .ops = &mt9t001_ctrl_ops, + .id = V4L2_CID_GAIN_GREEN_RED, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain, Green (R)", + .min = MT9T001_GLOBAL_GAIN_MIN, + .max = MT9T001_GLOBAL_GAIN_MAX, + .step = 1, + .def = MT9T001_GLOBAL_GAIN_MIN, + .flags = 0, + }, { + .ops = &mt9t001_ctrl_ops, + .id = V4L2_CID_GAIN_GREEN_BLUE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain, Green (B)", + .min = MT9T001_GLOBAL_GAIN_MIN, + .max = MT9T001_GLOBAL_GAIN_MAX, + .step = 1, + .def = MT9T001_GLOBAL_GAIN_MIN, + .flags = 0, + }, { + .ops = &mt9t001_ctrl_ops, + .id = V4L2_CID_GAIN_BLUE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain, Blue", + .min = MT9T001_GLOBAL_GAIN_MIN, + .max = MT9T001_GLOBAL_GAIN_MAX, + .step = 1, + .def = MT9T001_GLOBAL_GAIN_MIN, + .flags = 0, + }, +}; + +/* ----------------------------------------------------------------------------- + * V4L2 subdev internal operations + */ + +static int mt9t001_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) +{ + struct v4l2_mbus_framefmt *format; + struct v4l2_rect *crop; + + crop = v4l2_subdev_get_try_crop(fh, 0); + crop->left = MT9T001_COLUMN_START_DEF; + crop->top = MT9T001_ROW_START_DEF; + crop->width = MT9T001_WINDOW_WIDTH_DEF + 1; + crop->height = MT9T001_WINDOW_HEIGHT_DEF + 1; + + format = v4l2_subdev_get_try_format(fh, 0); + format->code = V4L2_MBUS_FMT_SGRBG10_1X10; + format->width = MT9T001_WINDOW_WIDTH_DEF + 1; + format->height = MT9T001_WINDOW_HEIGHT_DEF + 1; + format->field = V4L2_FIELD_NONE; + format->colorspace = V4L2_COLORSPACE_SRGB; + + return 0; +} + +static struct v4l2_subdev_video_ops mt9t001_subdev_video_ops = { + .s_stream = mt9t001_s_stream, +}; + +static struct v4l2_subdev_pad_ops mt9t001_subdev_pad_ops = { + .enum_mbus_code = mt9t001_enum_mbus_code, + .enum_frame_size = mt9t001_enum_frame_size, + .get_fmt = mt9t001_get_format, + .set_fmt = mt9t001_set_format, + .get_crop = mt9t001_get_crop, + .set_crop = mt9t001_set_crop, +}; + +static struct v4l2_subdev_ops mt9t001_subdev_ops = { + .video = &mt9t001_subdev_video_ops, + .pad = &mt9t001_subdev_pad_ops, +}; + +static struct v4l2_subdev_internal_ops mt9t001_subdev_internal_ops = { + .open = mt9t001_open, +}; + +static int mt9t001_video_probe(struct i2c_client *client) +{ + struct mt9t001_platform_data *pdata = client->dev.platform_data; + s32 data; + int ret; + + dev_info(&client->dev, "Probing MT9T001 at address 0x%02x\n", + client->addr); + + /* Reset the chip and stop data read out */ + ret = mt9t001_write(client, MT9T001_RESET, 1); + if (ret < 0) + return ret; + + ret = mt9t001_write(client, MT9T001_RESET, 0); + if (ret < 0) + return ret; + + ret = mt9t001_write(client, MT9T001_OUTPUT_CONTROL, 0); + if (ret < 0) + return ret; + + /* Configure the pixel clock polarity */ + if (pdata && pdata->clk_pol) { + ret = mt9t001_write(client, MT9T001_PIXEL_CLOCK, + MT9T001_PIXEL_CLOCK_INVERT); + if (ret < 0) + return ret; + } + + /* Read and check the sensor version */ + data = mt9t001_read(client, MT9T001_CHIP_VERSION); + if (data != MT9T001_CHIP_ID) { + dev_err(&client->dev, "MT9T001 not detected, wrong version " + "0x%04x\n", data); + return -ENODEV; + } + + dev_info(&client->dev, "MT9T001 detected at address 0x%02x\n", + client->addr); + + return ret; +} + +static int mt9t001_probe(struct i2c_client *client, + const struct i2c_device_id *did) +{ + struct mt9t001 *mt9t001; + unsigned int i; + int ret; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WORD_DATA)) { + dev_warn(&client->adapter->dev, + "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); + return -EIO; + } + + ret = mt9t001_video_probe(client); + if (ret < 0) + return ret; + + mt9t001 = kzalloc(sizeof(*mt9t001), GFP_KERNEL); + if (!mt9t001) + return -ENOMEM; + + v4l2_ctrl_handler_init(&mt9t001->ctrls, ARRAY_SIZE(mt9t001_ctrls) + + ARRAY_SIZE(mt9t001_gains) + 2); + + v4l2_ctrl_new_std(&mt9t001->ctrls, &mt9t001_ctrl_ops, + V4L2_CID_EXPOSURE, MT9T001_SHUTTER_WIDTH_MIN, + MT9T001_SHUTTER_WIDTH_MAX, 1, + MT9T001_SHUTTER_WIDTH_DEF); + v4l2_ctrl_new_std(&mt9t001->ctrls, &mt9t001_ctrl_ops, + V4L2_CID_BLACK_LEVEL, 1, 1, 1, 1); + + for (i = 0; i < ARRAY_SIZE(mt9t001_ctrls); ++i) + v4l2_ctrl_new_custom(&mt9t001->ctrls, &mt9t001_ctrls[i], NULL); + + for (i = 0; i < ARRAY_SIZE(mt9t001_gains); ++i) + mt9t001->gains[i] = v4l2_ctrl_new_custom(&mt9t001->ctrls, + &mt9t001_gains[i], NULL); + + v4l2_ctrl_cluster(ARRAY_SIZE(mt9t001_gains), mt9t001->gains); + + mt9t001->subdev.ctrl_handler = &mt9t001->ctrls; + + if (mt9t001->ctrls.error) { + printk(KERN_INFO "%s: control initialization error %d\n", + __func__, mt9t001->ctrls.error); + ret = -EINVAL; + goto done; + } + + mt9t001->crop.left = MT9T001_COLUMN_START_DEF; + mt9t001->crop.top = MT9T001_ROW_START_DEF; + mt9t001->crop.width = MT9T001_WINDOW_WIDTH_DEF + 1; + mt9t001->crop.height = MT9T001_WINDOW_HEIGHT_DEF + 1; + + mt9t001->format.code = V4L2_MBUS_FMT_SGRBG10_1X10; + mt9t001->format.width = MT9T001_WINDOW_WIDTH_DEF + 1; + mt9t001->format.height = MT9T001_WINDOW_HEIGHT_DEF + 1; + mt9t001->format.field = V4L2_FIELD_NONE; + mt9t001->format.colorspace = V4L2_COLORSPACE_SRGB; + + v4l2_i2c_subdev_init(&mt9t001->subdev, client, &mt9t001_subdev_ops); + mt9t001->subdev.internal_ops = &mt9t001_subdev_internal_ops; + mt9t001->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + mt9t001->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_init(&mt9t001->subdev.entity, 1, &mt9t001->pad, 0); + +done: + if (ret < 0) { + v4l2_ctrl_handler_free(&mt9t001->ctrls); + media_entity_cleanup(&mt9t001->subdev.entity); + kfree(mt9t001); + } + + return ret; +} + +static int mt9t001_remove(struct i2c_client *client) +{ + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct mt9t001 *mt9t001 = to_mt9t001(subdev); + + v4l2_ctrl_handler_free(&mt9t001->ctrls); + v4l2_device_unregister_subdev(subdev); + media_entity_cleanup(&subdev->entity); + kfree(mt9t001); + return 0; +} + +static const struct i2c_device_id mt9t001_id[] = { + { "mt9t001", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mt9t001_id); + +static struct i2c_driver mt9t001_driver = { + .driver = { + .name = "mt9t001", + }, + .probe = mt9t001_probe, + .remove = mt9t001_remove, + .id_table = mt9t001_id, +}; + +static int __init mt9t001_init(void) +{ + return i2c_add_driver(&mt9t001_driver); +} + +static void __exit mt9t001_exit(void) +{ + i2c_del_driver(&mt9t001_driver); +} + +module_init(mt9t001_init); +module_exit(mt9t001_exit); + +MODULE_DESCRIPTION("Aptina (Micron) MT9T001 Camera driver"); +MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/mt9t031.c b/drivers/media/video/mt9t031.c index 30547cc3f89..0e78477452f 100644 --- a/drivers/media/video/mt9t031.c +++ b/drivers/media/video/mt9t031.c @@ -13,11 +13,21 @@ #include <linux/log2.h> #include <linux/pm.h> #include <linux/slab.h> +#include <linux/v4l2-mediabus.h> #include <linux/videodev2.h> +#include <linux/module.h> #include <media/soc_camera.h> #include <media/v4l2-chip-ident.h> #include <media/v4l2-subdev.h> +#include <media/v4l2-ctrls.h> + +/* + * ATTENTION: this driver still cannot be used outside of the soc-camera + * framework because of its PM implementation, using the video_device node. + * If hardware becomes available for testing, alternative PM approaches shall + * be considered and tested. + */ /* * mt9t031 i2c address 0x5d @@ -57,21 +67,20 @@ #define MT9T031_COLUMN_SKIP 32 #define MT9T031_ROW_SKIP 20 -#define MT9T031_BUS_PARAM (SOCAM_PCLK_SAMPLE_RISING | \ - SOCAM_PCLK_SAMPLE_FALLING | SOCAM_HSYNC_ACTIVE_HIGH | \ - SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_DATA_ACTIVE_HIGH | \ - SOCAM_MASTER | SOCAM_DATAWIDTH_10) - struct mt9t031 { struct v4l2_subdev subdev; + struct v4l2_ctrl_handler hdl; + struct { + /* exposure/auto-exposure cluster */ + struct v4l2_ctrl *autoexposure; + struct v4l2_ctrl *exposure; + }; struct v4l2_rect rect; /* Sensor window */ int model; /* V4L2_IDENT_MT9T031* codes from v4l2-chip-ident.h */ u16 xskip; u16 yskip; - unsigned int gain; + unsigned int total_h; unsigned short y_skip_top; /* Lines to skip at the top */ - unsigned int exposure; - unsigned char autoexposure; }; static struct mt9t031 *to_mt9t031(const struct i2c_client *client) @@ -179,95 +188,6 @@ static int mt9t031_s_stream(struct v4l2_subdev *sd, int enable) return 0; } -static int mt9t031_set_bus_param(struct soc_camera_device *icd, - unsigned long flags) -{ - struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd)); - - /* The caller should have queried our parameters, check anyway */ - if (flags & ~MT9T031_BUS_PARAM) - return -EINVAL; - - if (flags & SOCAM_PCLK_SAMPLE_FALLING) - reg_clear(client, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000); - else - reg_set(client, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000); - - return 0; -} - -static unsigned long mt9t031_query_bus_param(struct soc_camera_device *icd) -{ - struct soc_camera_link *icl = to_soc_camera_link(icd); - - return soc_camera_apply_sensor_flags(icl, MT9T031_BUS_PARAM); -} - -enum { - MT9T031_CTRL_VFLIP, - MT9T031_CTRL_HFLIP, - MT9T031_CTRL_GAIN, - MT9T031_CTRL_EXPOSURE, - MT9T031_CTRL_EXPOSURE_AUTO, -}; - -static const struct v4l2_queryctrl mt9t031_controls[] = { - [MT9T031_CTRL_VFLIP] = { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Flip Vertically", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, - [MT9T031_CTRL_HFLIP] = { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Flip Horizontally", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, - [MT9T031_CTRL_GAIN] = { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gain", - .minimum = 0, - .maximum = 127, - .step = 1, - .default_value = 64, - .flags = V4L2_CTRL_FLAG_SLIDER, - }, - [MT9T031_CTRL_EXPOSURE] = { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Exposure", - .minimum = 1, - .maximum = 255, - .step = 1, - .default_value = 255, - .flags = V4L2_CTRL_FLAG_SLIDER, - }, - [MT9T031_CTRL_EXPOSURE_AUTO] = { - .id = V4L2_CID_EXPOSURE_AUTO, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Automatic Exposure", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1, - } -}; - -static struct soc_camera_ops mt9t031_ops = { - .set_bus_param = mt9t031_set_bus_param, - .query_bus_param = mt9t031_query_bus_param, - .controls = mt9t031_controls, - .num_controls = ARRAY_SIZE(mt9t031_controls), -}; - /* target must be _even_ */ static u16 mt9t031_skip(s32 *source, s32 target, s32 max) { @@ -353,7 +273,7 @@ static int mt9t031_set_params(struct i2c_client *client, /* * The caller provides a supported format, as guaranteed by - * icd->try_fmt_cap(), soc_camera_s_crop() and soc_camera_cropcap() + * .try_mbus_fmt(), soc_camera_s_crop() and soc_camera_cropcap() */ if (ret >= 0) ret = reg_write(client, MT9T031_COLUMN_START, rect->left); @@ -364,17 +284,10 @@ static int mt9t031_set_params(struct i2c_client *client, if (ret >= 0) ret = reg_write(client, MT9T031_WINDOW_HEIGHT, rect->height + mt9t031->y_skip_top - 1); - if (ret >= 0 && mt9t031->autoexposure) { - unsigned int total_h = rect->height + mt9t031->y_skip_top + vblank; - ret = set_shutter(client, total_h); - if (ret >= 0) { - const u32 shutter_max = MT9T031_MAX_HEIGHT + vblank; - const struct v4l2_queryctrl *qctrl = - &mt9t031_controls[MT9T031_CTRL_EXPOSURE]; - mt9t031->exposure = (shutter_max / 2 + (total_h - 1) * - (qctrl->maximum - qctrl->minimum)) / - shutter_max + qctrl->minimum; - } + if (ret >= 0 && v4l2_ctrl_g_ctrl(mt9t031->autoexposure) == V4L2_EXPOSURE_AUTO) { + mt9t031->total_h = rect->height + mt9t031->y_skip_top + vblank; + + ret = set_shutter(client, mt9t031->total_h); } /* Re-enable register update, commit all changes */ @@ -543,71 +456,57 @@ static int mt9t031_s_register(struct v4l2_subdev *sd, } #endif -static int mt9t031_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +static int mt9t031_g_volatile_ctrl(struct v4l2_ctrl *ctrl) { - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9t031 *mt9t031 = to_mt9t031(client); - int data; + struct mt9t031 *mt9t031 = container_of(ctrl->handler, + struct mt9t031, hdl); + const u32 shutter_max = MT9T031_MAX_HEIGHT + MT9T031_VERTICAL_BLANK; + s32 min, max; switch (ctrl->id) { - case V4L2_CID_VFLIP: - data = reg_read(client, MT9T031_READ_MODE_2); - if (data < 0) - return -EIO; - ctrl->value = !!(data & 0x8000); - break; - case V4L2_CID_HFLIP: - data = reg_read(client, MT9T031_READ_MODE_2); - if (data < 0) - return -EIO; - ctrl->value = !!(data & 0x4000); - break; case V4L2_CID_EXPOSURE_AUTO: - ctrl->value = mt9t031->autoexposure; - break; - case V4L2_CID_GAIN: - ctrl->value = mt9t031->gain; - break; - case V4L2_CID_EXPOSURE: - ctrl->value = mt9t031->exposure; + min = mt9t031->exposure->minimum; + max = mt9t031->exposure->maximum; + mt9t031->exposure->val = + (shutter_max / 2 + (mt9t031->total_h - 1) * (max - min)) + / shutter_max + min; break; } return 0; } -static int mt9t031_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +static int mt9t031_s_ctrl(struct v4l2_ctrl *ctrl) { + struct mt9t031 *mt9t031 = container_of(ctrl->handler, + struct mt9t031, hdl); + struct v4l2_subdev *sd = &mt9t031->subdev; struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9t031 *mt9t031 = to_mt9t031(client); - const struct v4l2_queryctrl *qctrl; + struct v4l2_ctrl *exp = mt9t031->exposure; int data; switch (ctrl->id) { case V4L2_CID_VFLIP: - if (ctrl->value) + if (ctrl->val) data = reg_set(client, MT9T031_READ_MODE_2, 0x8000); else data = reg_clear(client, MT9T031_READ_MODE_2, 0x8000); if (data < 0) return -EIO; - break; + return 0; case V4L2_CID_HFLIP: - if (ctrl->value) + if (ctrl->val) data = reg_set(client, MT9T031_READ_MODE_2, 0x4000); else data = reg_clear(client, MT9T031_READ_MODE_2, 0x4000); if (data < 0) return -EIO; - break; + return 0; case V4L2_CID_GAIN: - qctrl = &mt9t031_controls[MT9T031_CTRL_GAIN]; - if (ctrl->value > qctrl->maximum || ctrl->value < qctrl->minimum) - return -EINVAL; /* See Datasheet Table 7, Gain settings. */ - if (ctrl->value <= qctrl->default_value) { + if (ctrl->val <= ctrl->default_value) { /* Pack it into 0..1 step 0.125, register values 0..8 */ - unsigned long range = qctrl->default_value - qctrl->minimum; - data = ((ctrl->value - qctrl->minimum) * 8 + range / 2) / range; + unsigned long range = ctrl->default_value - ctrl->minimum; + data = ((ctrl->val - ctrl->minimum) * 8 + range / 2) / range; dev_dbg(&client->dev, "Setting gain %d\n", data); data = reg_write(client, MT9T031_GLOBAL_GAIN, data); @@ -616,9 +515,9 @@ static int mt9t031_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) } else { /* Pack it into 1.125..128 variable step, register values 9..0x7860 */ /* We assume qctrl->maximum - qctrl->default_value - 1 > 0 */ - unsigned long range = qctrl->maximum - qctrl->default_value - 1; + unsigned long range = ctrl->maximum - ctrl->default_value - 1; /* calculated gain: map 65..127 to 9..1024 step 0.125 */ - unsigned long gain = ((ctrl->value - qctrl->default_value - 1) * + unsigned long gain = ((ctrl->val - ctrl->default_value - 1) * 1015 + range / 2) / range + 9; if (gain <= 32) /* calculated gain 9..32 -> 9..32 */ @@ -635,19 +534,13 @@ static int mt9t031_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) if (data < 0) return -EIO; } + return 0; - /* Success */ - mt9t031->gain = ctrl->value; - break; - case V4L2_CID_EXPOSURE: - qctrl = &mt9t031_controls[MT9T031_CTRL_EXPOSURE]; - /* mt9t031 has maximum == default */ - if (ctrl->value > qctrl->maximum || ctrl->value < qctrl->minimum) - return -EINVAL; - else { - const unsigned long range = qctrl->maximum - qctrl->minimum; - const u32 shutter = ((ctrl->value - qctrl->minimum) * 1048 + - range / 2) / range + 1; + case V4L2_CID_EXPOSURE_AUTO: + if (ctrl->val == V4L2_EXPOSURE_MANUAL) { + unsigned int range = exp->maximum - exp->minimum; + unsigned int shutter = ((exp->val - exp->minimum) * 1048 + + range / 2) / range + 1; u32 old; get_shutter(client, &old); @@ -655,27 +548,15 @@ static int mt9t031_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) old, shutter); if (set_shutter(client, shutter) < 0) return -EIO; - mt9t031->exposure = ctrl->value; - mt9t031->autoexposure = 0; - } - break; - case V4L2_CID_EXPOSURE_AUTO: - if (ctrl->value) { + } else { const u16 vblank = MT9T031_VERTICAL_BLANK; - const u32 shutter_max = MT9T031_MAX_HEIGHT + vblank; - unsigned int total_h = mt9t031->rect.height + + mt9t031->total_h = mt9t031->rect.height + mt9t031->y_skip_top + vblank; - if (set_shutter(client, total_h) < 0) + if (set_shutter(client, mt9t031->total_h) < 0) return -EIO; - qctrl = &mt9t031_controls[MT9T031_CTRL_EXPOSURE]; - mt9t031->exposure = (shutter_max / 2 + (total_h - 1) * - (qctrl->maximum - qctrl->minimum)) / - shutter_max + qctrl->minimum; - mt9t031->autoexposure = 1; - } else - mt9t031->autoexposure = 0; - break; + } + return 0; default: return -EINVAL; } @@ -700,8 +581,7 @@ static int mt9t031_runtime_suspend(struct device *dev) static int mt9t031_runtime_resume(struct device *dev) { struct video_device *vdev = to_video_device(dev); - struct soc_camera_device *icd = dev_get_drvdata(vdev->parent); - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct v4l2_subdev *sd = soc_camera_vdev_to_subdev(vdev); struct i2c_client *client = v4l2_get_subdevdata(sd); struct mt9t031 *mt9t031 = to_mt9t031(client); @@ -734,6 +614,19 @@ static struct device_type mt9t031_dev_type = { .pm = &mt9t031_dev_pm_ops, }; +static int mt9t031_s_power(struct v4l2_subdev *sd, int on) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct video_device *vdev = soc_camera_i2c_to_vdev(client); + + if (on) + vdev->dev.type = &mt9t031_dev_type; + else + vdev->dev.type = NULL; + + return 0; +} + /* * Interface active, can use i2c. If it fails, it can indeed mean, that * this wasn't our capture interface, so, we wait for the right one @@ -741,7 +634,6 @@ static struct device_type mt9t031_dev_type = { static int mt9t031_video_probe(struct i2c_client *client) { struct mt9t031 *mt9t031 = to_mt9t031(client); - struct video_device *vdev = soc_camera_i2c_to_vdev(client); s32 data; int ret; @@ -768,11 +660,7 @@ static int mt9t031_video_probe(struct i2c_client *client) if (ret < 0) dev_err(&client->dev, "Failed to initialise the camera\n"); else - vdev->dev.type = &mt9t031_dev_type; - - /* mt9t031_idle() has reset the chip to default. */ - mt9t031->exposure = 255; - mt9t031->gain = 64; + v4l2_ctrl_handler_setup(&mt9t031->hdl); return ret; } @@ -787,10 +675,14 @@ static int mt9t031_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines) return 0; } +static const struct v4l2_ctrl_ops mt9t031_ctrl_ops = { + .g_volatile_ctrl = mt9t031_g_volatile_ctrl, + .s_ctrl = mt9t031_s_ctrl, +}; + static struct v4l2_subdev_core_ops mt9t031_subdev_core_ops = { - .g_ctrl = mt9t031_g_ctrl, - .s_ctrl = mt9t031_s_ctrl, .g_chip_ident = mt9t031_g_chip_ident, + .s_power = mt9t031_s_power, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = mt9t031_g_register, .s_register = mt9t031_s_register, @@ -807,6 +699,34 @@ static int mt9t031_enum_fmt(struct v4l2_subdev *sd, unsigned int index, return 0; } +static int mt9t031_g_mbus_config(struct v4l2_subdev *sd, + struct v4l2_mbus_config *cfg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + + cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING | + V4L2_MBUS_PCLK_SAMPLE_FALLING | V4L2_MBUS_HSYNC_ACTIVE_HIGH | + V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH; + cfg->type = V4L2_MBUS_PARALLEL; + cfg->flags = soc_camera_apply_board_flags(icl, cfg); + + return 0; +} + +static int mt9t031_s_mbus_config(struct v4l2_subdev *sd, + const struct v4l2_mbus_config *cfg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + + if (soc_camera_apply_board_flags(icl, cfg) & + V4L2_MBUS_PCLK_SAMPLE_FALLING) + return reg_clear(client, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000); + else + return reg_set(client, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000); +} + static struct v4l2_subdev_video_ops mt9t031_subdev_video_ops = { .s_stream = mt9t031_s_stream, .s_mbus_fmt = mt9t031_s_fmt, @@ -816,6 +736,8 @@ static struct v4l2_subdev_video_ops mt9t031_subdev_video_ops = { .g_crop = mt9t031_g_crop, .cropcap = mt9t031_cropcap, .enum_mbus_fmt = mt9t031_enum_fmt, + .g_mbus_config = mt9t031_g_mbus_config, + .s_mbus_config = mt9t031_s_mbus_config, }; static struct v4l2_subdev_sensor_ops mt9t031_subdev_sensor_ops = { @@ -832,18 +754,13 @@ static int mt9t031_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct mt9t031 *mt9t031; - struct soc_camera_device *icd = client->dev.platform_data; + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); int ret; - if (icd) { - struct soc_camera_link *icl = to_soc_camera_link(icd); - if (!icl) { - dev_err(&client->dev, "MT9T031 driver needs platform data\n"); - return -EINVAL; - } - - icd->ops = &mt9t031_ops; + if (!icl) { + dev_err(&client->dev, "MT9T031 driver needs platform data\n"); + return -EINVAL; } if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) { @@ -857,6 +774,33 @@ static int mt9t031_probe(struct i2c_client *client, return -ENOMEM; v4l2_i2c_subdev_init(&mt9t031->subdev, client, &mt9t031_subdev_ops); + v4l2_ctrl_handler_init(&mt9t031->hdl, 5); + v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops, + V4L2_CID_GAIN, 0, 127, 1, 64); + + /* + * Simulated autoexposure. If enabled, we calculate shutter width + * ourselves in the driver based on vertical blanking and frame width + */ + mt9t031->autoexposure = v4l2_ctrl_new_std_menu(&mt9t031->hdl, + &mt9t031_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0, + V4L2_EXPOSURE_AUTO); + mt9t031->exposure = v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops, + V4L2_CID_EXPOSURE, 1, 255, 1, 255); + + mt9t031->subdev.ctrl_handler = &mt9t031->hdl; + if (mt9t031->hdl.error) { + int err = mt9t031->hdl.error; + + kfree(mt9t031); + return err; + } + v4l2_ctrl_auto_cluster(2, &mt9t031->autoexposure, + V4L2_EXPOSURE_MANUAL, true); mt9t031->y_skip_top = 0; mt9t031->rect.left = MT9T031_COLUMN_SKIP; @@ -864,12 +808,6 @@ static int mt9t031_probe(struct i2c_client *client, mt9t031->rect.width = MT9T031_MAX_WIDTH; mt9t031->rect.height = MT9T031_MAX_HEIGHT; - /* - * Simulated autoexposure. If enabled, we calculate shutter width - * ourselves in the driver based on vertical blanking and frame width - */ - mt9t031->autoexposure = 1; - mt9t031->xskip = 1; mt9t031->yskip = 1; @@ -880,8 +818,7 @@ static int mt9t031_probe(struct i2c_client *client, mt9t031_disable(client); if (ret) { - if (icd) - icd->ops = NULL; + v4l2_ctrl_handler_free(&mt9t031->hdl); kfree(mt9t031); } @@ -891,10 +828,9 @@ static int mt9t031_probe(struct i2c_client *client, static int mt9t031_remove(struct i2c_client *client) { struct mt9t031 *mt9t031 = to_mt9t031(client); - struct soc_camera_device *icd = client->dev.platform_data; - if (icd) - icd->ops = NULL; + v4l2_device_unregister_subdev(&mt9t031->subdev); + v4l2_ctrl_handler_free(&mt9t031->hdl); kfree(mt9t031); return 0; diff --git a/drivers/media/video/mt9t112.c b/drivers/media/video/mt9t112.c index d2e0a50063a..32114a3c0ca 100644 --- a/drivers/media/video/mt9t112.c +++ b/drivers/media/video/mt9t112.c @@ -22,11 +22,11 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/slab.h> +#include <linux/v4l2-mediabus.h> #include <linux/videodev2.h> #include <media/mt9t112.h> #include <media/soc_camera.h> -#include <media/soc_mediabus.h> #include <media/v4l2-chip-ident.h> #include <media/v4l2-common.h> @@ -34,11 +34,7 @@ /* #define EXT_CLOCK 24000000 */ /************************************************************************ - - macro - - ************************************************************************/ /* * frame size @@ -80,17 +76,8 @@ #define VAR8(id, offset) _VAR(id, offset, 0x8000) /************************************************************************ - - struct - - ************************************************************************/ -struct mt9t112_frame_size { - u16 width; - u16 height; -}; - struct mt9t112_format { enum v4l2_mbus_pixelcode code; enum v4l2_colorspace colorspace; @@ -102,21 +89,17 @@ struct mt9t112_priv { struct v4l2_subdev subdev; struct mt9t112_camera_info *info; struct i2c_client *client; - struct soc_camera_device icd; - struct mt9t112_frame_size frame; + struct v4l2_rect frame; const struct mt9t112_format *format; int model; u32 flags; /* for flags */ -#define INIT_DONE (1<<0) +#define INIT_DONE (1 << 0) +#define PCLK_RISING (1 << 1) }; /************************************************************************ - - supported format - - ************************************************************************/ static const struct mt9t112_format mt9t112_cfmts[] = { @@ -154,11 +137,7 @@ static const struct mt9t112_format mt9t112_cfmts[] = { }; /************************************************************************ - - general function - - ************************************************************************/ static struct mt9t112_priv *to_mt9t112(const struct i2c_client *client) { @@ -326,50 +305,47 @@ static int mt9t112_clock_info(const struct i2c_client *client, u32 ext) n = (n >> 8) & 0x003f; enable = ((6000 > ext) || (54000 < ext)) ? "X" : ""; - dev_info(&client->dev, "EXTCLK : %10u K %s\n", ext, enable); + dev_dbg(&client->dev, "EXTCLK : %10u K %s\n", ext, enable); vco = 2 * m * ext / (n+1); enable = ((384000 > vco) || (768000 < vco)) ? "X" : ""; - dev_info(&client->dev, "VCO : %10u K %s\n", vco, enable); + dev_dbg(&client->dev, "VCO : %10u K %s\n", vco, enable); clk = vco / (p1+1) / (p2+1); enable = (96000 < clk) ? "X" : ""; - dev_info(&client->dev, "PIXCLK : %10u K %s\n", clk, enable); + dev_dbg(&client->dev, "PIXCLK : %10u K %s\n", clk, enable); clk = vco / (p3+1); enable = (768000 < clk) ? "X" : ""; - dev_info(&client->dev, "MIPICLK : %10u K %s\n", clk, enable); + dev_dbg(&client->dev, "MIPICLK : %10u K %s\n", clk, enable); clk = vco / (p6+1); enable = (96000 < clk) ? "X" : ""; - dev_info(&client->dev, "MCU CLK : %10u K %s\n", clk, enable); + dev_dbg(&client->dev, "MCU CLK : %10u K %s\n", clk, enable); clk = vco / (p5+1); enable = (54000 < clk) ? "X" : ""; - dev_info(&client->dev, "SOC CLK : %10u K %s\n", clk, enable); + dev_dbg(&client->dev, "SOC CLK : %10u K %s\n", clk, enable); clk = vco / (p4+1); enable = (70000 < clk) ? "X" : ""; - dev_info(&client->dev, "Sensor CLK : %10u K %s\n", clk, enable); + dev_dbg(&client->dev, "Sensor CLK : %10u K %s\n", clk, enable); clk = vco / (p7+1); - dev_info(&client->dev, "External sensor : %10u K\n", clk); + dev_dbg(&client->dev, "External sensor : %10u K\n", clk); clk = ext / (n+1); enable = ((2000 > clk) || (24000 < clk)) ? "X" : ""; - dev_info(&client->dev, "PFD : %10u K %s\n", clk, enable); + dev_dbg(&client->dev, "PFD : %10u K %s\n", clk, enable); return 0; } #endif -static void mt9t112_frame_check(u32 *width, u32 *height) +static void mt9t112_frame_check(u32 *width, u32 *height, u32 *left, u32 *top) { - if (*width > MAX_WIDTH) - *width = MAX_WIDTH; - - if (*height > MAX_HEIGHT) - *height = MAX_HEIGHT; + soc_camera_limit_side(left, width, 0, 0, MAX_WIDTH); + soc_camera_limit_side(top, height, 0, 0, MAX_HEIGHT); } static int mt9t112_set_a_frame_size(const struct i2c_client *client, @@ -758,48 +734,7 @@ static int mt9t112_init_camera(const struct i2c_client *client) } /************************************************************************ - - - soc_camera_ops - - -************************************************************************/ -static int mt9t112_set_bus_param(struct soc_camera_device *icd, - unsigned long flags) -{ - return 0; -} - -static unsigned long mt9t112_query_bus_param(struct soc_camera_device *icd) -{ - struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd)); - struct mt9t112_priv *priv = to_mt9t112(client); - struct soc_camera_link *icl = to_soc_camera_link(icd); - unsigned long flags = SOCAM_MASTER | SOCAM_VSYNC_ACTIVE_HIGH | - SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_DATA_ACTIVE_HIGH; - - flags |= (priv->info->flags & MT9T112_FLAG_PCLK_RISING_EDGE) ? - SOCAM_PCLK_SAMPLE_RISING : SOCAM_PCLK_SAMPLE_FALLING; - - if (priv->info->flags & MT9T112_FLAG_DATAWIDTH_8) - flags |= SOCAM_DATAWIDTH_8; - else - flags |= SOCAM_DATAWIDTH_10; - - return soc_camera_apply_sensor_flags(icl, flags); -} - -static struct soc_camera_ops mt9t112_ops = { - .set_bus_param = mt9t112_set_bus_param, - .query_bus_param = mt9t112_query_bus_param, -}; - -/************************************************************************ - - v4l2_subdev_core_ops - - ************************************************************************/ static int mt9t112_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *id) @@ -850,11 +785,7 @@ static struct v4l2_subdev_core_ops mt9t112_subdev_core_ops = { /************************************************************************ - - v4l2_subdev_video_ops - - ************************************************************************/ static int mt9t112_s_stream(struct v4l2_subdev *sd, int enable) { @@ -877,8 +808,7 @@ static int mt9t112_s_stream(struct v4l2_subdev *sd, int enable) } if (!(priv->flags & INIT_DONE)) { - u16 param = (MT9T112_FLAG_PCLK_RISING_EDGE & - priv->info->flags) ? 0x0001 : 0x0000; + u16 param = PCLK_RISING & priv->flags ? 0x0001 : 0x0000; ECHECKER(ret, mt9t112_init_camera(client)); @@ -910,19 +840,12 @@ static int mt9t112_s_stream(struct v4l2_subdev *sd, int enable) return ret; } -static int mt9t112_set_params(struct i2c_client *client, u32 width, u32 height, +static int mt9t112_set_params(struct mt9t112_priv *priv, + const struct v4l2_rect *rect, enum v4l2_mbus_pixelcode code) { - struct mt9t112_priv *priv = to_mt9t112(client); int i; - priv->format = NULL; - - /* - * frame size check - */ - mt9t112_frame_check(&width, &height); - /* * get color format */ @@ -933,8 +856,13 @@ static int mt9t112_set_params(struct i2c_client *client, u32 width, u32 height, if (i == ARRAY_SIZE(mt9t112_cfmts)) return -EINVAL; - priv->frame.width = (u16)width; - priv->frame.height = (u16)height; + priv->frame = *rect; + + /* + * frame size check + */ + mt9t112_frame_check(&priv->frame.width, &priv->frame.height, + &priv->frame.left, &priv->frame.top); priv->format = mt9t112_cfmts + i; @@ -945,9 +873,12 @@ static int mt9t112_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) { a->bounds.left = 0; a->bounds.top = 0; - a->bounds.width = VGA_WIDTH; - a->bounds.height = VGA_HEIGHT; - a->defrect = a->bounds; + a->bounds.width = MAX_WIDTH; + a->bounds.height = MAX_HEIGHT; + a->defrect.left = 0; + a->defrect.top = 0; + a->defrect.width = VGA_WIDTH; + a->defrect.height = VGA_HEIGHT; a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; a->pixelaspect.numerator = 1; a->pixelaspect.denominator = 1; @@ -957,11 +888,11 @@ static int mt9t112_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) static int mt9t112_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) { - a->c.left = 0; - a->c.top = 0; - a->c.width = VGA_WIDTH; - a->c.height = VGA_HEIGHT; - a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9t112_priv *priv = to_mt9t112(client); + + a->c = priv->frame; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; return 0; } @@ -969,10 +900,10 @@ static int mt9t112_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) static int mt9t112_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) { struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9t112_priv *priv = to_mt9t112(client); struct v4l2_rect *rect = &a->c; - return mt9t112_set_params(client, rect->width, rect->height, - V4L2_MBUS_FMT_UYVY8_2X8); + return mt9t112_set_params(priv, rect, priv->format->code); } static int mt9t112_g_fmt(struct v4l2_subdev *sd, @@ -981,16 +912,9 @@ static int mt9t112_g_fmt(struct v4l2_subdev *sd, struct i2c_client *client = v4l2_get_subdevdata(sd); struct mt9t112_priv *priv = to_mt9t112(client); - if (!priv->format) { - int ret = mt9t112_set_params(client, VGA_WIDTH, VGA_HEIGHT, - V4L2_MBUS_FMT_UYVY8_2X8); - if (ret < 0) - return ret; - } - mf->width = priv->frame.width; mf->height = priv->frame.height; - /* TODO: set colorspace */ + mf->colorspace = priv->format->colorspace; mf->code = priv->format->code; mf->field = V4L2_FIELD_NONE; @@ -1001,17 +925,42 @@ static int mt9t112_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) { struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9t112_priv *priv = to_mt9t112(client); + struct v4l2_rect rect = { + .width = mf->width, + .height = mf->height, + .left = priv->frame.left, + .top = priv->frame.top, + }; + int ret; + + ret = mt9t112_set_params(priv, &rect, mf->code); + + if (!ret) + mf->colorspace = priv->format->colorspace; - /* TODO: set colorspace */ - return mt9t112_set_params(client, mf->width, mf->height, mf->code); + return ret; } static int mt9t112_try_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) { - mt9t112_frame_check(&mf->width, &mf->height); + unsigned int top, left; + int i; + + for (i = 0; i < ARRAY_SIZE(mt9t112_cfmts); i++) + if (mt9t112_cfmts[i].code == mf->code) + break; + + if (i == ARRAY_SIZE(mt9t112_cfmts)) { + mf->code = V4L2_MBUS_FMT_UYVY8_2X8; + mf->colorspace = V4L2_COLORSPACE_JPEG; + } else { + mf->colorspace = mt9t112_cfmts[i].colorspace; + } + + mt9t112_frame_check(&mf->width, &mf->height, &left, &top); - /* TODO: set colorspace */ mf->field = V4L2_FIELD_NONE; return 0; @@ -1024,6 +973,35 @@ static int mt9t112_enum_fmt(struct v4l2_subdev *sd, unsigned int index, return -EINVAL; *code = mt9t112_cfmts[index].code; + + return 0; +} + +static int mt9t112_g_mbus_config(struct v4l2_subdev *sd, + struct v4l2_mbus_config *cfg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + + cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_VSYNC_ACTIVE_HIGH | + V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH | + V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING; + cfg->type = V4L2_MBUS_PARALLEL; + cfg->flags = soc_camera_apply_board_flags(icl, cfg); + + return 0; +} + +static int mt9t112_s_mbus_config(struct v4l2_subdev *sd, + const struct v4l2_mbus_config *cfg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct mt9t112_priv *priv = to_mt9t112(client); + + if (soc_camera_apply_board_flags(icl, cfg) & V4L2_MBUS_PCLK_SAMPLE_RISING) + priv->flags |= PCLK_RISING; + return 0; } @@ -1036,31 +1014,24 @@ static struct v4l2_subdev_video_ops mt9t112_subdev_video_ops = { .g_crop = mt9t112_g_crop, .s_crop = mt9t112_s_crop, .enum_mbus_fmt = mt9t112_enum_fmt, + .g_mbus_config = mt9t112_g_mbus_config, + .s_mbus_config = mt9t112_s_mbus_config, }; /************************************************************************ - - i2c driver - - ************************************************************************/ static struct v4l2_subdev_ops mt9t112_subdev_ops = { .core = &mt9t112_subdev_core_ops, .video = &mt9t112_subdev_video_ops, }; -static int mt9t112_camera_probe(struct soc_camera_device *icd, - struct i2c_client *client) +static int mt9t112_camera_probe(struct i2c_client *client) { struct mt9t112_priv *priv = to_mt9t112(client); const char *devname; int chipid; - /* We must have a parent by now. And it cannot be a wrong one. */ - BUG_ON(!icd->parent || - to_soc_camera_host(icd->parent)->nr != icd->iface); - /* * check and show chip ID */ @@ -1088,20 +1059,21 @@ static int mt9t112_camera_probe(struct soc_camera_device *icd, static int mt9t112_probe(struct i2c_client *client, const struct i2c_device_id *did) { - struct mt9t112_priv *priv; - struct soc_camera_device *icd = client->dev.platform_data; - struct soc_camera_link *icl; - int ret; + struct mt9t112_priv *priv; + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct v4l2_rect rect = { + .width = VGA_WIDTH, + .height = VGA_HEIGHT, + .left = (MAX_WIDTH - VGA_WIDTH) / 2, + .top = (MAX_HEIGHT - VGA_HEIGHT) / 2, + }; + int ret; - if (!icd) { - dev_err(&client->dev, "mt9t112: missing soc-camera data!\n"); + if (!icl || !icl->priv) { + dev_err(&client->dev, "mt9t112: missing platform data!\n"); return -EINVAL; } - icl = to_soc_camera_link(icd); - if (!icl || !icl->priv) - return -EINVAL; - priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -1110,13 +1082,12 @@ static int mt9t112_probe(struct i2c_client *client, v4l2_i2c_subdev_init(&priv->subdev, client, &mt9t112_subdev_ops); - icd->ops = &mt9t112_ops; - - ret = mt9t112_camera_probe(icd, client); - if (ret) { - icd->ops = NULL; + ret = mt9t112_camera_probe(client); + if (ret) kfree(priv); - } + + /* Cannot fail: using the default supported pixel code */ + mt9t112_set_params(priv, &rect, V4L2_MBUS_FMT_UYVY8_2X8); return ret; } @@ -1124,9 +1095,7 @@ static int mt9t112_probe(struct i2c_client *client, static int mt9t112_remove(struct i2c_client *client) { struct mt9t112_priv *priv = to_mt9t112(client); - struct soc_camera_device *icd = client->dev.platform_data; - icd->ops = NULL; kfree(priv); return 0; } @@ -1147,11 +1116,7 @@ static struct i2c_driver mt9t112_i2c_driver = { }; /************************************************************************ - - module function - - ************************************************************************/ static int __init mt9t112_module_init(void) { diff --git a/drivers/media/video/mt9v011.c b/drivers/media/video/mt9v011.c index 893a8b8f514..db74dd27c72 100644 --- a/drivers/media/video/mt9v011.c +++ b/drivers/media/video/mt9v011.c @@ -9,6 +9,7 @@ #include <linux/slab.h> #include <linux/videodev2.h> #include <linux/delay.h> +#include <linux/module.h> #include <asm/div64.h> #include <media/v4l2-device.h> #include <media/v4l2-chip-ident.h> diff --git a/drivers/media/video/mt9v022.c b/drivers/media/video/mt9v022.c index 51b0fccbfe7..690ee0d42ee 100644 --- a/drivers/media/video/mt9v022.c +++ b/drivers/media/video/mt9v022.c @@ -13,10 +13,13 @@ #include <linux/i2c.h> #include <linux/delay.h> #include <linux/log2.h> +#include <linux/module.h> +#include <media/soc_camera.h> +#include <media/soc_mediabus.h> #include <media/v4l2-subdev.h> #include <media/v4l2-chip-ident.h> -#include <media/soc_camera.h> +#include <media/v4l2-ctrls.h> /* * mt9v022 i2c address 0x48, 0x4c, 0x58, 0x5c @@ -100,6 +103,17 @@ static const struct mt9v022_datafmt mt9v022_monochrome_fmts[] = { struct mt9v022 { struct v4l2_subdev subdev; + struct v4l2_ctrl_handler hdl; + struct { + /* exposure/auto-exposure cluster */ + struct v4l2_ctrl *autoexposure; + struct v4l2_ctrl *exposure; + }; + struct { + /* gain/auto-gain cluster */ + struct v4l2_ctrl *autogain; + struct v4l2_ctrl *gain; + }; struct v4l2_rect rect; /* Sensor window */ const struct mt9v022_datafmt *fmt; const struct mt9v022_datafmt *fmts; @@ -178,6 +192,8 @@ static int mt9v022_init(struct i2c_client *client) ret = reg_clear(client, MT9V022_BLACK_LEVEL_CALIB_CTRL, 1); if (!ret) ret = reg_write(client, MT9V022_DIGITAL_TEST_PATTERN, 0); + if (!ret) + return v4l2_ctrl_handler_setup(&mt9v022->hdl); return ret; } @@ -199,78 +215,6 @@ static int mt9v022_s_stream(struct v4l2_subdev *sd, int enable) return 0; } -static int mt9v022_set_bus_param(struct soc_camera_device *icd, - unsigned long flags) -{ - struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd)); - struct mt9v022 *mt9v022 = to_mt9v022(client); - struct soc_camera_link *icl = to_soc_camera_link(icd); - unsigned int width_flag = flags & SOCAM_DATAWIDTH_MASK; - int ret; - u16 pixclk = 0; - - /* Only one width bit may be set */ - if (!is_power_of_2(width_flag)) - return -EINVAL; - - if (icl->set_bus_param) { - ret = icl->set_bus_param(icl, width_flag); - if (ret) - return ret; - } else { - /* - * Without board specific bus width settings we only support the - * sensors native bus width - */ - if (width_flag != SOCAM_DATAWIDTH_10) - return -EINVAL; - } - - flags = soc_camera_apply_sensor_flags(icl, flags); - - if (flags & SOCAM_PCLK_SAMPLE_FALLING) - pixclk |= 0x10; - - if (!(flags & SOCAM_HSYNC_ACTIVE_HIGH)) - pixclk |= 0x1; - - if (!(flags & SOCAM_VSYNC_ACTIVE_HIGH)) - pixclk |= 0x2; - - ret = reg_write(client, MT9V022_PIXCLK_FV_LV, pixclk); - if (ret < 0) - return ret; - - if (!(flags & SOCAM_MASTER)) - mt9v022->chip_control &= ~0x8; - - ret = reg_write(client, MT9V022_CHIP_CONTROL, mt9v022->chip_control); - if (ret < 0) - return ret; - - dev_dbg(&client->dev, "Calculated pixclk 0x%x, chip control 0x%x\n", - pixclk, mt9v022->chip_control); - - return 0; -} - -static unsigned long mt9v022_query_bus_param(struct soc_camera_device *icd) -{ - struct soc_camera_link *icl = to_soc_camera_link(icd); - unsigned int flags = SOCAM_MASTER | SOCAM_SLAVE | - SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING | - SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_LOW | - SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW | - SOCAM_DATA_ACTIVE_HIGH; - - if (icl->query_bus_param) - flags |= icl->query_bus_param(icl) & SOCAM_DATAWIDTH_MASK; - else - flags |= SOCAM_DATAWIDTH_10; - - return soc_camera_apply_sensor_flags(icl, flags); -} - static int mt9v022_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) { struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -389,7 +333,7 @@ static int mt9v022_s_fmt(struct v4l2_subdev *sd, /* * The caller provides a supported format, as verified per call to - * icd->try_fmt(), datawidth is from our supported format list + * .try_mbus_fmt(), datawidth is from our supported format list */ switch (mf->code) { case V4L2_MBUS_FMT_Y8_1X8: @@ -502,236 +446,131 @@ static int mt9v022_s_register(struct v4l2_subdev *sd, } #endif -static const struct v4l2_queryctrl mt9v022_controls[] = { - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Flip Vertically", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Flip Horizontally", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Analog Gain", - .minimum = 64, - .maximum = 127, - .step = 1, - .default_value = 64, - .flags = V4L2_CTRL_FLAG_SLIDER, - }, { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Exposure", - .minimum = 1, - .maximum = 255, - .step = 1, - .default_value = 255, - .flags = V4L2_CTRL_FLAG_SLIDER, - }, { - .id = V4L2_CID_AUTOGAIN, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Automatic Gain", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1, - }, { - .id = V4L2_CID_EXPOSURE_AUTO, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Automatic Exposure", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1, - } -}; - -static struct soc_camera_ops mt9v022_ops = { - .set_bus_param = mt9v022_set_bus_param, - .query_bus_param = mt9v022_query_bus_param, - .controls = mt9v022_controls, - .num_controls = ARRAY_SIZE(mt9v022_controls), -}; - -static int mt9v022_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +static int mt9v022_g_volatile_ctrl(struct v4l2_ctrl *ctrl) { + struct mt9v022 *mt9v022 = container_of(ctrl->handler, + struct mt9v022, hdl); + struct v4l2_subdev *sd = &mt9v022->subdev; struct i2c_client *client = v4l2_get_subdevdata(sd); - const struct v4l2_queryctrl *qctrl; + struct v4l2_ctrl *gain = mt9v022->gain; + struct v4l2_ctrl *exp = mt9v022->exposure; unsigned long range; int data; - qctrl = soc_camera_find_qctrl(&mt9v022_ops, ctrl->id); - switch (ctrl->id) { - case V4L2_CID_VFLIP: - data = reg_read(client, MT9V022_READ_MODE); - if (data < 0) - return -EIO; - ctrl->value = !!(data & 0x10); - break; - case V4L2_CID_HFLIP: - data = reg_read(client, MT9V022_READ_MODE); - if (data < 0) - return -EIO; - ctrl->value = !!(data & 0x20); - break; - case V4L2_CID_EXPOSURE_AUTO: - data = reg_read(client, MT9V022_AEC_AGC_ENABLE); - if (data < 0) - return -EIO; - ctrl->value = !!(data & 0x1); - break; case V4L2_CID_AUTOGAIN: - data = reg_read(client, MT9V022_AEC_AGC_ENABLE); - if (data < 0) - return -EIO; - ctrl->value = !!(data & 0x2); - break; - case V4L2_CID_GAIN: data = reg_read(client, MT9V022_ANALOG_GAIN); if (data < 0) return -EIO; - range = qctrl->maximum - qctrl->minimum; - ctrl->value = ((data - 16) * range + 24) / 48 + qctrl->minimum; - - break; - case V4L2_CID_EXPOSURE: + range = gain->maximum - gain->minimum; + gain->val = ((data - 16) * range + 24) / 48 + gain->minimum; + return 0; + case V4L2_CID_EXPOSURE_AUTO: data = reg_read(client, MT9V022_TOTAL_SHUTTER_WIDTH); if (data < 0) return -EIO; - range = qctrl->maximum - qctrl->minimum; - ctrl->value = ((data - 1) * range + 239) / 479 + qctrl->minimum; - - break; + range = exp->maximum - exp->minimum; + exp->val = ((data - 1) * range + 239) / 479 + exp->minimum; + return 0; } - return 0; + return -EINVAL; } -static int mt9v022_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +static int mt9v022_s_ctrl(struct v4l2_ctrl *ctrl) { - int data; + struct mt9v022 *mt9v022 = container_of(ctrl->handler, + struct mt9v022, hdl); + struct v4l2_subdev *sd = &mt9v022->subdev; struct i2c_client *client = v4l2_get_subdevdata(sd); - const struct v4l2_queryctrl *qctrl; - - qctrl = soc_camera_find_qctrl(&mt9v022_ops, ctrl->id); - if (!qctrl) - return -EINVAL; + int data; switch (ctrl->id) { case V4L2_CID_VFLIP: - if (ctrl->value) + if (ctrl->val) data = reg_set(client, MT9V022_READ_MODE, 0x10); else data = reg_clear(client, MT9V022_READ_MODE, 0x10); if (data < 0) return -EIO; - break; + return 0; case V4L2_CID_HFLIP: - if (ctrl->value) + if (ctrl->val) data = reg_set(client, MT9V022_READ_MODE, 0x20); else data = reg_clear(client, MT9V022_READ_MODE, 0x20); if (data < 0) return -EIO; - break; - case V4L2_CID_GAIN: - /* mt9v022 has minimum == default */ - if (ctrl->value > qctrl->maximum || ctrl->value < qctrl->minimum) - return -EINVAL; - else { - unsigned long range = qctrl->maximum - qctrl->minimum; + return 0; + case V4L2_CID_AUTOGAIN: + if (ctrl->val) { + if (reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x2) < 0) + return -EIO; + } else { + struct v4l2_ctrl *gain = mt9v022->gain; + /* mt9v022 has minimum == default */ + unsigned long range = gain->maximum - gain->minimum; /* Valid values 16 to 64, 32 to 64 must be even. */ - unsigned long gain = ((ctrl->value - qctrl->minimum) * + unsigned long gain_val = ((gain->val - gain->minimum) * 48 + range / 2) / range + 16; - if (gain >= 32) - gain &= ~1; + + if (gain_val >= 32) + gain_val &= ~1; + /* * The user wants to set gain manually, hope, she * knows, what she's doing... Switch AGC off. */ - if (reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x2) < 0) return -EIO; dev_dbg(&client->dev, "Setting gain from %d to %lu\n", - reg_read(client, MT9V022_ANALOG_GAIN), gain); - if (reg_write(client, MT9V022_ANALOG_GAIN, gain) < 0) + reg_read(client, MT9V022_ANALOG_GAIN), gain_val); + if (reg_write(client, MT9V022_ANALOG_GAIN, gain_val) < 0) return -EIO; } - break; - case V4L2_CID_EXPOSURE: - /* mt9v022 has maximum == default */ - if (ctrl->value > qctrl->maximum || ctrl->value < qctrl->minimum) - return -EINVAL; - else { - unsigned long range = qctrl->maximum - qctrl->minimum; - unsigned long shutter = ((ctrl->value - qctrl->minimum) * - 479 + range / 2) / range + 1; + return 0; + case V4L2_CID_EXPOSURE_AUTO: + if (ctrl->val == V4L2_EXPOSURE_AUTO) { + data = reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x1); + } else { + struct v4l2_ctrl *exp = mt9v022->exposure; + unsigned long range = exp->maximum - exp->minimum; + unsigned long shutter = ((exp->val - exp->minimum) * + 479 + range / 2) / range + 1; + /* * The user wants to set shutter width manually, hope, * she knows, what she's doing... Switch AEC off. */ - - if (reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x1) < 0) + data = reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x1); + if (data < 0) return -EIO; - dev_dbg(&client->dev, "Shutter width from %d to %lu\n", - reg_read(client, MT9V022_TOTAL_SHUTTER_WIDTH), - shutter); + reg_read(client, MT9V022_TOTAL_SHUTTER_WIDTH), + shutter); if (reg_write(client, MT9V022_TOTAL_SHUTTER_WIDTH, - shutter) < 0) + shutter) < 0) return -EIO; } - break; - case V4L2_CID_AUTOGAIN: - if (ctrl->value) - data = reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x2); - else - data = reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x2); - if (data < 0) - return -EIO; - break; - case V4L2_CID_EXPOSURE_AUTO: - if (ctrl->value) - data = reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x1); - else - data = reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x1); - if (data < 0) - return -EIO; - break; + return 0; } - return 0; + return -EINVAL; } /* * Interface active, can use i2c. If it fails, it can indeed mean, that * this wasn't our capture interface, so, we wait for the right one */ -static int mt9v022_video_probe(struct soc_camera_device *icd, - struct i2c_client *client) +static int mt9v022_video_probe(struct i2c_client *client) { struct mt9v022 *mt9v022 = to_mt9v022(client); - struct soc_camera_link *icl = to_soc_camera_link(icd); + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); s32 data; int ret; unsigned long flags; - /* We must have a parent by now. And it cannot be a wrong one. */ - BUG_ON(!icd->parent || - to_soc_camera_host(icd->parent)->nr != icd->iface); - /* Read out the chip version register */ data = reg_read(client, MT9V022_CHIP_VERSION); @@ -805,16 +644,6 @@ ei2c: return ret; } -static void mt9v022_video_remove(struct soc_camera_device *icd) -{ - struct soc_camera_link *icl = to_soc_camera_link(icd); - - dev_dbg(icd->pdev, "Video removed: %p, %p\n", - icd->parent, icd->vdev); - if (icl->free_bus) - icl->free_bus(icl); -} - static int mt9v022_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines) { struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -825,9 +654,12 @@ static int mt9v022_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines) return 0; } +static const struct v4l2_ctrl_ops mt9v022_ctrl_ops = { + .g_volatile_ctrl = mt9v022_g_volatile_ctrl, + .s_ctrl = mt9v022_s_ctrl, +}; + static struct v4l2_subdev_core_ops mt9v022_subdev_core_ops = { - .g_ctrl = mt9v022_g_ctrl, - .s_ctrl = mt9v022_s_ctrl, .g_chip_ident = mt9v022_g_chip_ident, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = mt9v022_g_register, @@ -848,6 +680,72 @@ static int mt9v022_enum_fmt(struct v4l2_subdev *sd, unsigned int index, return 0; } +static int mt9v022_g_mbus_config(struct v4l2_subdev *sd, + struct v4l2_mbus_config *cfg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + + cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_SLAVE | + V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | + V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW | + V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW | + V4L2_MBUS_DATA_ACTIVE_HIGH; + cfg->type = V4L2_MBUS_PARALLEL; + cfg->flags = soc_camera_apply_board_flags(icl, cfg); + + return 0; +} + +static int mt9v022_s_mbus_config(struct v4l2_subdev *sd, + const struct v4l2_mbus_config *cfg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct mt9v022 *mt9v022 = to_mt9v022(client); + unsigned long flags = soc_camera_apply_board_flags(icl, cfg); + unsigned int bps = soc_mbus_get_fmtdesc(mt9v022->fmt->code)->bits_per_sample; + int ret; + u16 pixclk = 0; + + if (icl->set_bus_param) { + ret = icl->set_bus_param(icl, 1 << (bps - 1)); + if (ret) + return ret; + } else if (bps != 10) { + /* + * Without board specific bus width settings we only support the + * sensors native bus width + */ + return -EINVAL; + } + + if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) + pixclk |= 0x10; + + if (!(flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH)) + pixclk |= 0x1; + + if (!(flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)) + pixclk |= 0x2; + + ret = reg_write(client, MT9V022_PIXCLK_FV_LV, pixclk); + if (ret < 0) + return ret; + + if (!(flags & V4L2_MBUS_MASTER)) + mt9v022->chip_control &= ~0x8; + + ret = reg_write(client, MT9V022_CHIP_CONTROL, mt9v022->chip_control); + if (ret < 0) + return ret; + + dev_dbg(&client->dev, "Calculated pixclk 0x%x, chip control 0x%x\n", + pixclk, mt9v022->chip_control); + + return 0; +} + static struct v4l2_subdev_video_ops mt9v022_subdev_video_ops = { .s_stream = mt9v022_s_stream, .s_mbus_fmt = mt9v022_s_fmt, @@ -857,6 +755,8 @@ static struct v4l2_subdev_video_ops mt9v022_subdev_video_ops = { .g_crop = mt9v022_g_crop, .cropcap = mt9v022_cropcap, .enum_mbus_fmt = mt9v022_enum_fmt, + .g_mbus_config = mt9v022_g_mbus_config, + .s_mbus_config = mt9v022_s_mbus_config, }; static struct v4l2_subdev_sensor_ops mt9v022_subdev_sensor_ops = { @@ -873,17 +773,10 @@ static int mt9v022_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct mt9v022 *mt9v022; - struct soc_camera_device *icd = client->dev.platform_data; + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - struct soc_camera_link *icl; int ret; - if (!icd) { - dev_err(&client->dev, "MT9V022: missing soc-camera data!\n"); - return -EINVAL; - } - - icl = to_soc_camera_link(icd); if (!icl) { dev_err(&client->dev, "MT9V022 driver needs platform data\n"); return -EINVAL; @@ -900,10 +793,39 @@ static int mt9v022_probe(struct i2c_client *client, return -ENOMEM; v4l2_i2c_subdev_init(&mt9v022->subdev, client, &mt9v022_subdev_ops); + v4l2_ctrl_handler_init(&mt9v022->hdl, 6); + v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + mt9v022->autogain = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, 1); + mt9v022->gain = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, + V4L2_CID_GAIN, 0, 127, 1, 64); + + /* + * Simulated autoexposure. If enabled, we calculate shutter width + * ourselves in the driver based on vertical blanking and frame width + */ + mt9v022->autoexposure = v4l2_ctrl_new_std_menu(&mt9v022->hdl, + &mt9v022_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0, + V4L2_EXPOSURE_AUTO); + mt9v022->exposure = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, + V4L2_CID_EXPOSURE, 1, 255, 1, 255); + + mt9v022->subdev.ctrl_handler = &mt9v022->hdl; + if (mt9v022->hdl.error) { + int err = mt9v022->hdl.error; + + kfree(mt9v022); + return err; + } + v4l2_ctrl_auto_cluster(2, &mt9v022->autoexposure, + V4L2_EXPOSURE_MANUAL, true); + v4l2_ctrl_auto_cluster(2, &mt9v022->autogain, 0, true); mt9v022->chip_control = MT9V022_CHIP_CONTROL_DEFAULT; - icd->ops = &mt9v022_ops; /* * MT9V022 _really_ corrupts the first read out line. * TODO: verify on i.MX31 @@ -914,9 +836,9 @@ static int mt9v022_probe(struct i2c_client *client, mt9v022->rect.width = MT9V022_MAX_WIDTH; mt9v022->rect.height = MT9V022_MAX_HEIGHT; - ret = mt9v022_video_probe(icd, client); + ret = mt9v022_video_probe(client); if (ret) { - icd->ops = NULL; + v4l2_ctrl_handler_free(&mt9v022->hdl); kfree(mt9v022); } @@ -926,10 +848,12 @@ static int mt9v022_probe(struct i2c_client *client, static int mt9v022_remove(struct i2c_client *client) { struct mt9v022 *mt9v022 = to_mt9v022(client); - struct soc_camera_device *icd = client->dev.platform_data; + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); - icd->ops = NULL; - mt9v022_video_remove(icd); + v4l2_device_unregister_subdev(&mt9v022->subdev); + if (icl->free_bus) + icl->free_bus(icl); + v4l2_ctrl_handler_free(&mt9v022->hdl); kfree(mt9v022); return 0; diff --git a/drivers/media/video/mt9v032.c b/drivers/media/video/mt9v032.c index c64e1dc4cb4..f080c162123 100644 --- a/drivers/media/video/mt9v032.c +++ b/drivers/media/video/mt9v032.c @@ -19,6 +19,7 @@ #include <linux/slab.h> #include <linux/videodev2.h> #include <linux/v4l2-mediabus.h> +#include <linux/module.h> #include <media/mt9v032.h> #include <media/v4l2-ctrls.h> diff --git a/drivers/media/video/mx1_camera.c b/drivers/media/video/mx1_camera.c index 087db12a3a6..18e94c7d2be 100644 --- a/drivers/media/video/mx1_camera.c +++ b/drivers/media/video/mx1_camera.c @@ -78,11 +78,10 @@ #define CSI_IRQ_MASK (CSISR_SFF_OR_INT | CSISR_RFF_OR_INT | \ CSISR_STATFF_INT | CSISR_RXFF_INT | CSISR_SOF_INT) -#define CSI_BUS_FLAGS (SOCAM_MASTER | SOCAM_HSYNC_ACTIVE_HIGH | \ - SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW | \ - SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING | \ - SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATA_ACTIVE_LOW | \ - SOCAM_DATAWIDTH_8) +#define CSI_BUS_FLAGS (V4L2_MBUS_MASTER | V4L2_MBUS_HSYNC_ACTIVE_HIGH | \ + V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW | \ + V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | \ + V4L2_MBUS_DATA_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_LOW) #define MAX_VIDEO_MEM 16 /* Video memory limit in megabytes */ @@ -490,59 +489,73 @@ static int mx1_camera_set_crop(struct soc_camera_device *icd, static int mx1_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) { + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx1_camera_dev *pcdev = ici->priv; - unsigned long camera_flags, common_flags; + struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,}; + unsigned long common_flags; unsigned int csicr1; int ret; - camera_flags = icd->ops->query_bus_param(icd); - /* MX1 supports only 8bit buswidth */ - common_flags = soc_camera_bus_param_compatible(camera_flags, - CSI_BUS_FLAGS); - if (!common_flags) - return -EINVAL; + ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg); + if (!ret) { + common_flags = soc_mbus_config_compatible(&cfg, CSI_BUS_FLAGS); + if (!common_flags) { + dev_warn(icd->parent, + "Flags incompatible: camera 0x%x, host 0x%x\n", + cfg.flags, CSI_BUS_FLAGS); + return -EINVAL; + } + } else if (ret != -ENOIOCTLCMD) { + return ret; + } else { + common_flags = CSI_BUS_FLAGS; + } /* Make choises, based on platform choice */ - if ((common_flags & SOCAM_VSYNC_ACTIVE_HIGH) && - (common_flags & SOCAM_VSYNC_ACTIVE_LOW)) { + if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) && + (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) { if (!pcdev->pdata || pcdev->pdata->flags & MX1_CAMERA_VSYNC_HIGH) - common_flags &= ~SOCAM_VSYNC_ACTIVE_LOW; + common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW; else - common_flags &= ~SOCAM_VSYNC_ACTIVE_HIGH; + common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH; } - if ((common_flags & SOCAM_PCLK_SAMPLE_RISING) && - (common_flags & SOCAM_PCLK_SAMPLE_FALLING)) { + if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) && + (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) { if (!pcdev->pdata || pcdev->pdata->flags & MX1_CAMERA_PCLK_RISING) - common_flags &= ~SOCAM_PCLK_SAMPLE_FALLING; + common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING; else - common_flags &= ~SOCAM_PCLK_SAMPLE_RISING; + common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING; } - if ((common_flags & SOCAM_DATA_ACTIVE_HIGH) && - (common_flags & SOCAM_DATA_ACTIVE_LOW)) { + if ((common_flags & V4L2_MBUS_DATA_ACTIVE_HIGH) && + (common_flags & V4L2_MBUS_DATA_ACTIVE_LOW)) { if (!pcdev->pdata || pcdev->pdata->flags & MX1_CAMERA_DATA_HIGH) - common_flags &= ~SOCAM_DATA_ACTIVE_LOW; + common_flags &= ~V4L2_MBUS_DATA_ACTIVE_LOW; else - common_flags &= ~SOCAM_DATA_ACTIVE_HIGH; + common_flags &= ~V4L2_MBUS_DATA_ACTIVE_HIGH; } - ret = icd->ops->set_bus_param(icd, common_flags); - if (ret < 0) + cfg.flags = common_flags; + ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg); + if (ret < 0 && ret != -ENOIOCTLCMD) { + dev_dbg(icd->parent, "camera s_mbus_config(0x%lx) returned %d\n", + common_flags, ret); return ret; + } csicr1 = __raw_readl(pcdev->base + CSICR1); - if (common_flags & SOCAM_PCLK_SAMPLE_RISING) + if (common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) csicr1 |= CSICR1_REDGE; - if (common_flags & SOCAM_VSYNC_ACTIVE_HIGH) + if (common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) csicr1 |= CSICR1_SOF_POL; - if (common_flags & SOCAM_DATA_ACTIVE_LOW) + if (common_flags & V4L2_MBUS_DATA_ACTIVE_LOW) csicr1 |= CSICR1_DATA_POL; __raw_writel(csicr1, pcdev->base + CSICR1); diff --git a/drivers/media/video/mx2_camera.c b/drivers/media/video/mx2_camera.c index ec2410c0c80..a803d9ea8fd 100644 --- a/drivers/media/video/mx2_camera.c +++ b/drivers/media/video/mx2_camera.c @@ -686,16 +686,15 @@ static void mx2_camera_init_videobuf(struct videobuf_queue *q, icd, &icd->video_lock); } -#define MX2_BUS_FLAGS (SOCAM_DATAWIDTH_8 | \ - SOCAM_MASTER | \ - SOCAM_VSYNC_ACTIVE_HIGH | \ - SOCAM_VSYNC_ACTIVE_LOW | \ - SOCAM_HSYNC_ACTIVE_HIGH | \ - SOCAM_HSYNC_ACTIVE_LOW | \ - SOCAM_PCLK_SAMPLE_RISING | \ - SOCAM_PCLK_SAMPLE_FALLING | \ - SOCAM_DATA_ACTIVE_HIGH | \ - SOCAM_DATA_ACTIVE_LOW) +#define MX2_BUS_FLAGS (V4L2_MBUS_MASTER | \ + V4L2_MBUS_VSYNC_ACTIVE_HIGH | \ + V4L2_MBUS_VSYNC_ACTIVE_LOW | \ + V4L2_MBUS_HSYNC_ACTIVE_HIGH | \ + V4L2_MBUS_HSYNC_ACTIVE_LOW | \ + V4L2_MBUS_PCLK_SAMPLE_RISING | \ + V4L2_MBUS_PCLK_SAMPLE_FALLING | \ + V4L2_MBUS_DATA_ACTIVE_HIGH | \ + V4L2_MBUS_DATA_ACTIVE_LOW) static int mx27_camera_emma_prp_reset(struct mx2_camera_dev *pcdev) { @@ -770,46 +769,59 @@ static void mx27_camera_emma_buf_init(struct soc_camera_device *icd, static int mx2_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) { - struct soc_camera_host *ici = - to_soc_camera_host(icd->parent); + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx2_camera_dev *pcdev = ici->priv; - unsigned long camera_flags, common_flags; - int ret = 0; + struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,}; + unsigned long common_flags; + int ret; int bytesperline; u32 csicr1 = pcdev->csicr1; - camera_flags = icd->ops->query_bus_param(icd); - - common_flags = soc_camera_bus_param_compatible(camera_flags, - MX2_BUS_FLAGS); - if (!common_flags) - return -EINVAL; + ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg); + if (!ret) { + common_flags = soc_mbus_config_compatible(&cfg, MX2_BUS_FLAGS); + if (!common_flags) { + dev_warn(icd->parent, + "Flags incompatible: camera 0x%x, host 0x%x\n", + cfg.flags, MX2_BUS_FLAGS); + return -EINVAL; + } + } else if (ret != -ENOIOCTLCMD) { + return ret; + } else { + common_flags = MX2_BUS_FLAGS; + } - if ((common_flags & SOCAM_HSYNC_ACTIVE_HIGH) && - (common_flags & SOCAM_HSYNC_ACTIVE_LOW)) { + if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) && + (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) { if (pcdev->platform_flags & MX2_CAMERA_HSYNC_HIGH) - common_flags &= ~SOCAM_HSYNC_ACTIVE_LOW; + common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW; else - common_flags &= ~SOCAM_HSYNC_ACTIVE_HIGH; + common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH; } - if ((common_flags & SOCAM_PCLK_SAMPLE_RISING) && - (common_flags & SOCAM_PCLK_SAMPLE_FALLING)) { + if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) && + (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) { if (pcdev->platform_flags & MX2_CAMERA_PCLK_SAMPLE_RISING) - common_flags &= ~SOCAM_PCLK_SAMPLE_FALLING; + common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING; else - common_flags &= ~SOCAM_PCLK_SAMPLE_RISING; + common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING; } - ret = icd->ops->set_bus_param(icd, common_flags); - if (ret < 0) + cfg.flags = common_flags; + ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg); + if (ret < 0 && ret != -ENOIOCTLCMD) { + dev_dbg(icd->parent, "camera s_mbus_config(0x%lx) returned %d\n", + common_flags, ret); return ret; + } - if (common_flags & SOCAM_PCLK_SAMPLE_RISING) + if (common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) csicr1 |= CSICR1_REDGE; - if (common_flags & SOCAM_VSYNC_ACTIVE_HIGH) + if (common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) csicr1 |= CSICR1_SOF_POL; - if (common_flags & SOCAM_HSYNC_ACTIVE_HIGH) + if (common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) csicr1 |= CSICR1_HSYNC_POL; if (pcdev->platform_flags & MX2_CAMERA_SWAP16) csicr1 |= CSICR1_SWAP16_EN; diff --git a/drivers/media/video/mx3_camera.c b/drivers/media/video/mx3_camera.c index c045b47803a..f96f92f00f9 100644 --- a/drivers/media/video/mx3_camera.c +++ b/drivers/media/video/mx3_camera.c @@ -109,10 +109,12 @@ struct mx3_camera_dev { unsigned long platform_flags; unsigned long mclk; + u16 width_flags; /* max 15 bits */ struct list_head capture; spinlock_t lock; /* Protects video buffer lists */ struct mx3_camera_buffer *active; + size_t buf_total; struct vb2_alloc_ctx *alloc_ctx; enum v4l2_field field; int sequence; @@ -190,79 +192,53 @@ static void mx3_cam_dma_done(void *arg) * Calculate the __buffer__ (not data) size and number of buffers. */ static int mx3_videobuf_setup(struct vb2_queue *vq, + const struct v4l2_format *fmt, unsigned int *count, unsigned int *num_planes, - unsigned long sizes[], void *alloc_ctxs[]) + unsigned int sizes[], void *alloc_ctxs[]) { struct soc_camera_device *icd = soc_camera_from_vb2q(vq); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx3_camera_dev *mx3_cam = ici->priv; - int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, - icd->current_fmt->host_fmt); - - if (bytes_per_line < 0) - return bytes_per_line; + int bytes_per_line; + unsigned int height; if (!mx3_cam->idmac_channel[0]) return -EINVAL; - *num_planes = 1; - - mx3_cam->sequence = 0; - sizes[0] = bytes_per_line * icd->user_height; - alloc_ctxs[0] = mx3_cam->alloc_ctx; - - if (!*count) - *count = 32; - - if (sizes[0] * *count > MAX_VIDEO_MEM * 1024 * 1024) - *count = MAX_VIDEO_MEM * 1024 * 1024 / sizes[0]; - - return 0; -} - -static int mx3_videobuf_prepare(struct vb2_buffer *vb) -{ - struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct mx3_camera_dev *mx3_cam = ici->priv; - struct idmac_channel *ichan = mx3_cam->idmac_channel[0]; - struct scatterlist *sg; - struct mx3_camera_buffer *buf; - size_t new_size; - int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, + if (fmt) { + const struct soc_camera_format_xlate *xlate = soc_camera_xlate_by_fourcc(icd, + fmt->fmt.pix.pixelformat); + if (!xlate) + return -EINVAL; + bytes_per_line = soc_mbus_bytes_per_line(fmt->fmt.pix.width, + xlate->host_fmt); + height = fmt->fmt.pix.height; + } else { + /* Called from VIDIOC_REQBUFS or in compatibility mode */ + bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, icd->current_fmt->host_fmt); - + height = icd->user_height; + } if (bytes_per_line < 0) return bytes_per_line; - buf = to_mx3_vb(vb); - sg = &buf->sg; + sizes[0] = bytes_per_line * height; - new_size = bytes_per_line * icd->user_height; - - if (vb2_plane_size(vb, 0) < new_size) { - dev_err(icd->parent, "Buffer too small (%lu < %zu)\n", - vb2_plane_size(vb, 0), new_size); - return -ENOBUFS; - } - - if (buf->state == CSI_BUF_NEEDS_INIT) { - sg_dma_address(sg) = vb2_dma_contig_plane_paddr(vb, 0); - sg_dma_len(sg) = new_size; + alloc_ctxs[0] = mx3_cam->alloc_ctx; - buf->txd = ichan->dma_chan.device->device_prep_slave_sg( - &ichan->dma_chan, sg, 1, DMA_FROM_DEVICE, - DMA_PREP_INTERRUPT); - if (!buf->txd) - return -EIO; + if (!vq->num_buffers) + mx3_cam->sequence = 0; - buf->txd->callback_param = buf->txd; - buf->txd->callback = mx3_cam_dma_done; + if (!*count) + *count = 2; - buf->state = CSI_BUF_PREPARED; - } + /* If *num_planes != 0, we have already verified *count. */ + if (!*num_planes && + sizes[0] * *count + mx3_cam->buf_total > MAX_VIDEO_MEM * 1024 * 1024) + *count = (MAX_VIDEO_MEM * 1024 * 1024 - mx3_cam->buf_total) / + sizes[0]; - vb2_set_plane_payload(vb, 0, new_size); + *num_planes = 1; return 0; } @@ -286,28 +262,58 @@ static void mx3_videobuf_queue(struct vb2_buffer *vb) struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx3_camera_dev *mx3_cam = ici->priv; struct mx3_camera_buffer *buf = to_mx3_vb(vb); - struct dma_async_tx_descriptor *txd = buf->txd; - struct idmac_channel *ichan = to_idmac_chan(txd->chan); + struct scatterlist *sg = &buf->sg; + struct dma_async_tx_descriptor *txd; + struct idmac_channel *ichan = mx3_cam->idmac_channel[0]; struct idmac_video_param *video = &ichan->params.video; - dma_cookie_t cookie; - u32 fourcc = icd->current_fmt->host_fmt->fourcc; + const struct soc_mbus_pixelfmt *host_fmt = icd->current_fmt->host_fmt; + int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, host_fmt); unsigned long flags; + dma_cookie_t cookie; + size_t new_size; + + BUG_ON(bytes_per_line <= 0); + + new_size = bytes_per_line * icd->user_height; + + if (vb2_plane_size(vb, 0) < new_size) { + dev_err(icd->parent, "Buffer #%d too small (%lu < %zu)\n", + vb->v4l2_buf.index, vb2_plane_size(vb, 0), new_size); + goto error; + } + + if (buf->state == CSI_BUF_NEEDS_INIT) { + sg_dma_address(sg) = vb2_dma_contig_plane_dma_addr(vb, 0); + sg_dma_len(sg) = new_size; + + txd = ichan->dma_chan.device->device_prep_slave_sg( + &ichan->dma_chan, sg, 1, DMA_FROM_DEVICE, + DMA_PREP_INTERRUPT); + if (!txd) + goto error; + + txd->callback_param = txd; + txd->callback = mx3_cam_dma_done; + + buf->state = CSI_BUF_PREPARED; + buf->txd = txd; + } else { + txd = buf->txd; + } + + vb2_set_plane_payload(vb, 0, new_size); /* This is the configuration of one sg-element */ - video->out_pixel_fmt = fourcc_to_ipu_pix(fourcc); + video->out_pixel_fmt = fourcc_to_ipu_pix(host_fmt->fourcc); if (video->out_pixel_fmt == IPU_PIX_FMT_GENERIC) { /* - * If the IPU DMA channel is configured to transport - * generic 8-bit data, we have to set up correctly the - * geometry parameters upon the current pixel format. - * So, since the DMA horizontal parameters are expressed - * in bytes not pixels, convert these in the right unit. + * If the IPU DMA channel is configured to transfer generic + * 8-bit data, we have to set up the geometry parameters + * correctly, according to the current pixel format. The DMA + * horizontal parameters in this case are expressed in bytes, + * not in pixels. */ - int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, - icd->current_fmt->host_fmt); - BUG_ON(bytes_per_line <= 0); - video->out_width = bytes_per_line; video->out_height = icd->user_height; video->out_stride = bytes_per_line; @@ -351,6 +357,7 @@ static void mx3_videobuf_queue(struct vb2_buffer *vb) mx3_cam->active = NULL; spin_unlock_irqrestore(&mx3_cam->lock, flags); +error: vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); } @@ -384,17 +391,24 @@ static void mx3_videobuf_release(struct vb2_buffer *vb) } spin_unlock_irqrestore(&mx3_cam->lock, flags); + + mx3_cam->buf_total -= vb2_plane_size(vb, 0); } static int mx3_videobuf_init(struct vb2_buffer *vb) { + struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct mx3_camera_dev *mx3_cam = ici->priv; struct mx3_camera_buffer *buf = to_mx3_vb(vb); + /* This is for locking debugging only */ INIT_LIST_HEAD(&buf->queue); sg_init_table(&buf->sg, 1); buf->state = CSI_BUF_NEEDS_INIT; - buf->txd = NULL; + + mx3_cam->buf_total += vb2_plane_size(vb, 0); return 0; } @@ -405,13 +419,12 @@ static int mx3_stop_streaming(struct vb2_queue *q) struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx3_camera_dev *mx3_cam = ici->priv; struct idmac_channel *ichan = mx3_cam->idmac_channel[0]; - struct dma_chan *chan; struct mx3_camera_buffer *buf, *tmp; unsigned long flags; if (ichan) { - chan = &ichan->dma_chan; - chan->device->device_control(chan, DMA_TERMINATE_ALL, 0); + struct dma_chan *chan = &ichan->dma_chan; + chan->device->device_control(chan, DMA_PAUSE, 0); } spin_lock_irqsave(&mx3_cam->lock, flags); @@ -419,8 +432,8 @@ static int mx3_stop_streaming(struct vb2_queue *q) mx3_cam->active = NULL; list_for_each_entry_safe(buf, tmp, &mx3_cam->capture, queue) { - buf->state = CSI_BUF_NEEDS_INIT; list_del_init(&buf->queue); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); } spin_unlock_irqrestore(&mx3_cam->lock, flags); @@ -430,7 +443,6 @@ static int mx3_stop_streaming(struct vb2_queue *q) static struct vb2_ops mx3_videobuf_ops = { .queue_setup = mx3_videobuf_setup, - .buf_prepare = mx3_videobuf_prepare, .buf_queue = mx3_videobuf_queue, .buf_cleanup = mx3_videobuf_release, .buf_init = mx3_videobuf_init, @@ -514,6 +526,7 @@ static int mx3_camera_add_device(struct soc_camera_device *icd) mx3_camera_activate(mx3_cam, icd); + mx3_cam->buf_total = 0; mx3_cam->icd = icd; dev_info(icd->parent, "MX3 Camera driver attached to camera %d\n", @@ -548,58 +561,27 @@ static int test_platform_param(struct mx3_camera_dev *mx3_cam, unsigned char buswidth, unsigned long *flags) { /* + * If requested data width is supported by the platform, use it or any + * possible lower value - i.MX31 is smart enough to shift bits + */ + if (buswidth > fls(mx3_cam->width_flags)) + return -EINVAL; + + /* * Platform specified synchronization and pixel clock polarities are * only a recommendation and are only used during probing. MX3x * camera interface only works in master mode, i.e., uses HSYNC and * VSYNC signals from the sensor */ - *flags = SOCAM_MASTER | - SOCAM_HSYNC_ACTIVE_HIGH | - SOCAM_HSYNC_ACTIVE_LOW | - SOCAM_VSYNC_ACTIVE_HIGH | - SOCAM_VSYNC_ACTIVE_LOW | - SOCAM_PCLK_SAMPLE_RISING | - SOCAM_PCLK_SAMPLE_FALLING | - SOCAM_DATA_ACTIVE_HIGH | - SOCAM_DATA_ACTIVE_LOW; - - /* - * If requested data width is supported by the platform, use it or any - * possible lower value - i.MX31 is smart enough to schift bits - */ - if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_15) - *flags |= SOCAM_DATAWIDTH_15 | SOCAM_DATAWIDTH_10 | - SOCAM_DATAWIDTH_8 | SOCAM_DATAWIDTH_4; - else if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_10) - *flags |= SOCAM_DATAWIDTH_10 | SOCAM_DATAWIDTH_8 | - SOCAM_DATAWIDTH_4; - else if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_8) - *flags |= SOCAM_DATAWIDTH_8 | SOCAM_DATAWIDTH_4; - else if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_4) - *flags |= SOCAM_DATAWIDTH_4; - - switch (buswidth) { - case 15: - if (!(*flags & SOCAM_DATAWIDTH_15)) - return -EINVAL; - break; - case 10: - if (!(*flags & SOCAM_DATAWIDTH_10)) - return -EINVAL; - break; - case 8: - if (!(*flags & SOCAM_DATAWIDTH_8)) - return -EINVAL; - break; - case 4: - if (!(*flags & SOCAM_DATAWIDTH_4)) - return -EINVAL; - break; - default: - dev_warn(mx3_cam->soc_host.v4l2_dev.dev, - "Unsupported bus width %d\n", buswidth); - return -EINVAL; - } + *flags = V4L2_MBUS_MASTER | + V4L2_MBUS_HSYNC_ACTIVE_HIGH | + V4L2_MBUS_HSYNC_ACTIVE_LOW | + V4L2_MBUS_VSYNC_ACTIVE_HIGH | + V4L2_MBUS_VSYNC_ACTIVE_LOW | + V4L2_MBUS_PCLK_SAMPLE_RISING | + V4L2_MBUS_PCLK_SAMPLE_FALLING | + V4L2_MBUS_DATA_ACTIVE_HIGH | + V4L2_MBUS_DATA_ACTIVE_LOW; return 0; } @@ -607,9 +589,11 @@ static int test_platform_param(struct mx3_camera_dev *mx3_cam, static int mx3_camera_try_bus_param(struct soc_camera_device *icd, const unsigned int depth) { + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx3_camera_dev *mx3_cam = ici->priv; - unsigned long bus_flags, camera_flags; + struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,}; + unsigned long bus_flags, common_flags; int ret = test_platform_param(mx3_cam, depth, &bus_flags); dev_dbg(icd->parent, "request bus width %d bit: %d\n", depth, ret); @@ -617,15 +601,21 @@ static int mx3_camera_try_bus_param(struct soc_camera_device *icd, if (ret < 0) return ret; - camera_flags = icd->ops->query_bus_param(icd); - - ret = soc_camera_bus_param_compatible(camera_flags, bus_flags); - if (ret < 0) - dev_warn(icd->parent, - "Flags incompatible: camera %lx, host %lx\n", - camera_flags, bus_flags); + ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg); + if (!ret) { + common_flags = soc_mbus_config_compatible(&cfg, + bus_flags); + if (!common_flags) { + dev_warn(icd->parent, + "Flags incompatible: camera 0x%x, host 0x%lx\n", + cfg.flags, bus_flags); + return -EINVAL; + } + } else if (ret != -ENOIOCTLCMD) { + return ret; + } - return ret; + return 0; } static bool chan_filter(struct dma_chan *chan, void *arg) @@ -994,9 +984,11 @@ static int mx3_camera_querycap(struct soc_camera_host *ici, static int mx3_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) { + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx3_camera_dev *mx3_cam = ici->priv; - unsigned long bus_flags, camera_flags, common_flags; + struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,}; + unsigned long bus_flags, common_flags; u32 dw, sens_conf; const struct soc_mbus_pixelfmt *fmt; int buswidth; @@ -1008,83 +1000,76 @@ static int mx3_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) if (!fmt) return -EINVAL; - buswidth = fmt->bits_per_sample; - ret = test_platform_param(mx3_cam, buswidth, &bus_flags); - xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); if (!xlate) { dev_warn(dev, "Format %x not found\n", pixfmt); return -EINVAL; } + buswidth = fmt->bits_per_sample; + ret = test_platform_param(mx3_cam, buswidth, &bus_flags); + dev_dbg(dev, "requested bus width %d bit: %d\n", buswidth, ret); if (ret < 0) return ret; - camera_flags = icd->ops->query_bus_param(icd); - - common_flags = soc_camera_bus_param_compatible(camera_flags, bus_flags); - dev_dbg(dev, "Flags cam: 0x%lx host: 0x%lx common: 0x%lx\n", - camera_flags, bus_flags, common_flags); - if (!common_flags) { - dev_dbg(dev, "no common flags"); - return -EINVAL; + ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg); + if (!ret) { + common_flags = soc_mbus_config_compatible(&cfg, + bus_flags); + if (!common_flags) { + dev_warn(icd->parent, + "Flags incompatible: camera 0x%x, host 0x%lx\n", + cfg.flags, bus_flags); + return -EINVAL; + } + } else if (ret != -ENOIOCTLCMD) { + return ret; + } else { + common_flags = bus_flags; } + dev_dbg(dev, "Flags cam: 0x%x host: 0x%lx common: 0x%lx\n", + cfg.flags, bus_flags, common_flags); + /* Make choices, based on platform preferences */ - if ((common_flags & SOCAM_HSYNC_ACTIVE_HIGH) && - (common_flags & SOCAM_HSYNC_ACTIVE_LOW)) { + if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) && + (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) { if (mx3_cam->platform_flags & MX3_CAMERA_HSP) - common_flags &= ~SOCAM_HSYNC_ACTIVE_HIGH; + common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH; else - common_flags &= ~SOCAM_HSYNC_ACTIVE_LOW; + common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW; } - if ((common_flags & SOCAM_VSYNC_ACTIVE_HIGH) && - (common_flags & SOCAM_VSYNC_ACTIVE_LOW)) { + if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) && + (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) { if (mx3_cam->platform_flags & MX3_CAMERA_VSP) - common_flags &= ~SOCAM_VSYNC_ACTIVE_HIGH; + common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH; else - common_flags &= ~SOCAM_VSYNC_ACTIVE_LOW; + common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW; } - if ((common_flags & SOCAM_DATA_ACTIVE_HIGH) && - (common_flags & SOCAM_DATA_ACTIVE_LOW)) { + if ((common_flags & V4L2_MBUS_DATA_ACTIVE_HIGH) && + (common_flags & V4L2_MBUS_DATA_ACTIVE_LOW)) { if (mx3_cam->platform_flags & MX3_CAMERA_DP) - common_flags &= ~SOCAM_DATA_ACTIVE_HIGH; + common_flags &= ~V4L2_MBUS_DATA_ACTIVE_HIGH; else - common_flags &= ~SOCAM_DATA_ACTIVE_LOW; + common_flags &= ~V4L2_MBUS_DATA_ACTIVE_LOW; } - if ((common_flags & SOCAM_PCLK_SAMPLE_RISING) && - (common_flags & SOCAM_PCLK_SAMPLE_FALLING)) { + if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) && + (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) { if (mx3_cam->platform_flags & MX3_CAMERA_PCP) - common_flags &= ~SOCAM_PCLK_SAMPLE_RISING; + common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING; else - common_flags &= ~SOCAM_PCLK_SAMPLE_FALLING; + common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING; } - /* - * Make the camera work in widest common mode, we'll take care of - * the rest - */ - if (common_flags & SOCAM_DATAWIDTH_15) - common_flags = (common_flags & ~SOCAM_DATAWIDTH_MASK) | - SOCAM_DATAWIDTH_15; - else if (common_flags & SOCAM_DATAWIDTH_10) - common_flags = (common_flags & ~SOCAM_DATAWIDTH_MASK) | - SOCAM_DATAWIDTH_10; - else if (common_flags & SOCAM_DATAWIDTH_8) - common_flags = (common_flags & ~SOCAM_DATAWIDTH_MASK) | - SOCAM_DATAWIDTH_8; - else - common_flags = (common_flags & ~SOCAM_DATAWIDTH_MASK) | - SOCAM_DATAWIDTH_4; - - ret = icd->ops->set_bus_param(icd, common_flags); - if (ret < 0) { - dev_dbg(dev, "camera set_bus_param(%lx) returned %d\n", + cfg.flags = common_flags; + ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg); + if (ret < 0 && ret != -ENOIOCTLCMD) { + dev_dbg(dev, "camera s_mbus_config(0x%lx) returned %d\n", common_flags, ret); return ret; } @@ -1108,13 +1093,13 @@ static int mx3_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) /* This has been set in mx3_camera_activate(), but we clear it above */ sens_conf |= CSI_SENS_CONF_DATA_FMT_BAYER; - if (common_flags & SOCAM_PCLK_SAMPLE_FALLING) + if (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) sens_conf |= 1 << CSI_SENS_CONF_PIX_CLK_POL_SHIFT; - if (common_flags & SOCAM_HSYNC_ACTIVE_LOW) + if (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) sens_conf |= 1 << CSI_SENS_CONF_HSYNC_POL_SHIFT; - if (common_flags & SOCAM_VSYNC_ACTIVE_LOW) + if (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) sens_conf |= 1 << CSI_SENS_CONF_VSYNC_POL_SHIFT; - if (common_flags & SOCAM_DATA_ACTIVE_LOW) + if (common_flags & V4L2_MBUS_DATA_ACTIVE_LOW) sens_conf |= 1 << CSI_SENS_CONF_DATA_POL_SHIFT; /* Just do what we're asked to do */ @@ -1199,6 +1184,14 @@ static int __devinit mx3_camera_probe(struct platform_device *pdev) "data widths, using default 8 bit\n"); mx3_cam->platform_flags |= MX3_CAMERA_DATAWIDTH_8; } + if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_4) + mx3_cam->width_flags = 1 << 3; + if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_8) + mx3_cam->width_flags |= 1 << 7; + if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_10) + mx3_cam->width_flags |= 1 << 9; + if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_15) + mx3_cam->width_flags |= 1 << 14; mx3_cam->mclk = mx3_cam->pdata->mclk_10khz * 10000; if (!mx3_cam->mclk) { @@ -1281,8 +1274,6 @@ static int __devexit mx3_camera_remove(struct platform_device *pdev) dmaengine_put(); - dev_info(&pdev->dev, "i.MX3x Camera driver unloaded\n"); - return 0; } diff --git a/drivers/media/video/mxb.c b/drivers/media/video/mxb.c index 0b385002350..2e413174843 100644 --- a/drivers/media/video/mxb.c +++ b/drivers/media/video/mxb.c @@ -21,12 +21,15 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #define DEBUG_VARIABLE debug #include <media/saa7146_vv.h> #include <media/tuner.h> #include <media/v4l2-common.h> #include <media/saa7115.h> +#include <linux/module.h> #include "mxb.h" #include "tea6415c.h" @@ -171,7 +174,7 @@ static int mxb_probe(struct saa7146_dev *dev) mxb = kzalloc(sizeof(struct mxb), GFP_KERNEL); if (mxb == NULL) { - DEB_D(("not enough kernel memory.\n")); + DEB_D("not enough kernel memory\n"); return -ENOMEM; } @@ -179,7 +182,7 @@ static int mxb_probe(struct saa7146_dev *dev) saa7146_i2c_adapter_prepare(dev, &mxb->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480); if (i2c_add_adapter(&mxb->i2c_adapter) < 0) { - DEB_S(("cannot register i2c-device. skipping.\n")); + DEB_S("cannot register i2c-device. skipping.\n"); kfree(mxb); return -EFAULT; } @@ -200,7 +203,7 @@ static int mxb_probe(struct saa7146_dev *dev) /* check if all devices are present */ if (!mxb->tea6420_1 || !mxb->tea6420_2 || !mxb->tea6415c || !mxb->tda9840 || !mxb->saa7111a || !mxb->tuner) { - printk("mxb: did not find all i2c devices. aborting\n"); + pr_err("did not find all i2c devices. aborting\n"); i2c_del_adapter(&mxb->i2c_adapter); kfree(mxb); return -ENODEV; @@ -346,11 +349,11 @@ static int mxb_init_done(struct saa7146_dev* dev) msg.buf = &mxb_saa7740_init[i].data[0]; err = i2c_transfer(&mxb->i2c_adapter, &msg, 1); if (err != 1) { - DEB_D(("failed to initialize 'sound arena module'.\n")); + DEB_D("failed to initialize 'sound arena module'\n"); goto err; } } - INFO(("'sound arena module' detected.\n")); + pr_info("'sound arena module' detected\n"); } err: /* the rest for saa7146: you should definitely set some basic values @@ -390,7 +393,7 @@ static int vidioc_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl * for (i = MAXCONTROLS - 1; i >= 0; i--) { if (mxb_controls[i].id == qc->id) { *qc = mxb_controls[i]; - DEB_D(("VIDIOC_QUERYCTRL %d.\n", qc->id)); + DEB_D("VIDIOC_QUERYCTRL %d\n", qc->id); return 0; } } @@ -413,11 +416,11 @@ static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *vc) if (vc->id == V4L2_CID_AUDIO_MUTE) { vc->value = mxb->cur_mute; - DEB_D(("VIDIOC_G_CTRL V4L2_CID_AUDIO_MUTE:%d.\n", vc->value)); + DEB_D("VIDIOC_G_CTRL V4L2_CID_AUDIO_MUTE:%d\n", vc->value); return 0; } - DEB_EE(("VIDIOC_G_CTRL V4L2_CID_AUDIO_MUTE:%d.\n", vc->value)); + DEB_EE("VIDIOC_G_CTRL V4L2_CID_AUDIO_MUTE:%d\n", vc->value); return 0; } @@ -440,14 +443,14 @@ static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *vc) /* switch the audio-source */ tea6420_route_line(mxb, vc->value ? 6 : video_audio_connect[mxb->cur_input]); - DEB_EE(("VIDIOC_S_CTRL, V4L2_CID_AUDIO_MUTE: %d.\n", vc->value)); + DEB_EE("VIDIOC_S_CTRL, V4L2_CID_AUDIO_MUTE: %d\n", vc->value); } return 0; } static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i) { - DEB_EE(("VIDIOC_ENUMINPUT %d.\n", i->index)); + DEB_EE("VIDIOC_ENUMINPUT %d\n", i->index); if (i->index >= MXB_INPUTS) return -EINVAL; memcpy(i, &mxb_inputs[i->index], sizeof(struct v4l2_input)); @@ -460,7 +463,7 @@ static int vidioc_g_input(struct file *file, void *fh, unsigned int *i) struct mxb *mxb = (struct mxb *)dev->ext_priv; *i = mxb->cur_input; - DEB_EE(("VIDIOC_G_INPUT %d.\n", *i)); + DEB_EE("VIDIOC_G_INPUT %d\n", *i); return 0; } @@ -471,7 +474,7 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int input) int err = 0; int i = 0; - DEB_EE(("VIDIOC_S_INPUT %d.\n", input)); + DEB_EE("VIDIOC_S_INPUT %d\n", input); if (input >= MXB_INPUTS) return -EINVAL; @@ -514,7 +517,7 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int input) /* switch video in saa7111a */ if (saa7111a_call(mxb, video, s_routing, i, SAA7111_FMT_CCIR, 0)) - printk(KERN_ERR "VIDIOC_S_INPUT: could not address saa7111a.\n"); + pr_err("VIDIOC_S_INPUT: could not address saa7111a\n"); /* switch the audio-source only if necessary */ if (0 == mxb->cur_mute) @@ -529,11 +532,12 @@ static int vidioc_g_tuner(struct file *file, void *fh, struct v4l2_tuner *t) struct mxb *mxb = (struct mxb *)dev->ext_priv; if (t->index) { - DEB_D(("VIDIOC_G_TUNER: channel %d does not have a tuner attached.\n", t->index)); + DEB_D("VIDIOC_G_TUNER: channel %d does not have a tuner attached\n", + t->index); return -EINVAL; } - DEB_EE(("VIDIOC_G_TUNER: %d\n", t->index)); + DEB_EE("VIDIOC_G_TUNER: %d\n", t->index); memset(t, 0, sizeof(*t)); strlcpy(t->name, "TV Tuner", sizeof(t->name)); @@ -550,7 +554,8 @@ static int vidioc_s_tuner(struct file *file, void *fh, struct v4l2_tuner *t) struct mxb *mxb = (struct mxb *)dev->ext_priv; if (t->index) { - DEB_D(("VIDIOC_S_TUNER: channel %d does not have a tuner attached.\n", t->index)); + DEB_D("VIDIOC_S_TUNER: channel %d does not have a tuner attached\n", + t->index); return -EINVAL; } @@ -564,14 +569,14 @@ static int vidioc_g_frequency(struct file *file, void *fh, struct v4l2_frequency struct mxb *mxb = (struct mxb *)dev->ext_priv; if (mxb->cur_input) { - DEB_D(("VIDIOC_G_FREQ: channel %d does not have a tuner!\n", - mxb->cur_input)); + DEB_D("VIDIOC_G_FREQ: channel %d does not have a tuner!\n", + mxb->cur_input); return -EINVAL; } *f = mxb->cur_freq; - DEB_EE(("VIDIOC_G_FREQ: freq:0x%08x.\n", mxb->cur_freq.frequency)); + DEB_EE("VIDIOC_G_FREQ: freq:0x%08x\n", mxb->cur_freq.frequency); return 0; } @@ -588,12 +593,13 @@ static int vidioc_s_frequency(struct file *file, void *fh, struct v4l2_frequency return -EINVAL; if (mxb->cur_input) { - DEB_D(("VIDIOC_S_FREQ: channel %d does not have a tuner!\n", mxb->cur_input)); + DEB_D("VIDIOC_S_FREQ: channel %d does not have a tuner!\n", + mxb->cur_input); return -EINVAL; } mxb->cur_freq = *f; - DEB_EE(("VIDIOC_S_FREQUENCY: freq:0x%08x.\n", mxb->cur_freq.frequency)); + DEB_EE("VIDIOC_S_FREQUENCY: freq:0x%08x\n", mxb->cur_freq.frequency); /* tune in desired frequency */ tuner_call(mxb, tuner, s_frequency, &mxb->cur_freq); @@ -612,18 +618,18 @@ static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a) struct mxb *mxb = (struct mxb *)dev->ext_priv; if (a->index > MXB_INPUTS) { - DEB_D(("VIDIOC_G_AUDIO %d out of range.\n", a->index)); + DEB_D("VIDIOC_G_AUDIO %d out of range\n", a->index); return -EINVAL; } - DEB_EE(("VIDIOC_G_AUDIO %d.\n", a->index)); + DEB_EE("VIDIOC_G_AUDIO %d\n", a->index); memcpy(a, &mxb_audios[video_audio_connect[mxb->cur_input]], sizeof(struct v4l2_audio)); return 0; } static int vidioc_s_audio(struct file *file, void *fh, struct v4l2_audio *a) { - DEB_D(("VIDIOC_S_AUDIO %d.\n", a->index)); + DEB_D("VIDIOC_S_AUDIO %d\n", a->index); return 0; } @@ -655,11 +661,11 @@ static long vidioc_default(struct file *file, void *fh, bool valid_prio, int i = *(int *)arg; if (i < 0 || i >= MXB_AUDIOS) { - DEB_D(("illegal argument to MXB_S_AUDIO_CD: i:%d.\n", i)); + DEB_D("invalid argument to MXB_S_AUDIO_CD: i:%d\n", i); return -EINVAL; } - DEB_EE(("MXB_S_AUDIO_CD: i:%d.\n", i)); + DEB_EE("MXB_S_AUDIO_CD: i:%d\n", i); tea6420_route_cd(mxb, i); return 0; @@ -669,17 +675,18 @@ static long vidioc_default(struct file *file, void *fh, bool valid_prio, int i = *(int *)arg; if (i < 0 || i >= MXB_AUDIOS) { - DEB_D(("illegal argument to MXB_S_AUDIO_LINE: i:%d.\n", i)); + DEB_D("invalid argument to MXB_S_AUDIO_LINE: i:%d\n", + i); return -EINVAL; } - DEB_EE(("MXB_S_AUDIO_LINE: i:%d.\n", i)); + DEB_EE("MXB_S_AUDIO_LINE: i:%d\n", i); tea6420_route_line(mxb, i); return 0; } default: /* - DEB2(printk("does not handle this ioctl.\n")); + DEB2(pr_err("does not handle this ioctl\n")); */ return -ENOIOCTLCMD; } @@ -693,7 +700,7 @@ static int mxb_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data { struct mxb *mxb; - DEB_EE(("dev:%p\n", dev)); + DEB_EE("dev:%p\n", dev); saa7146_vv_init(dev, &vv_data); if (mxb_probe(dev)) { @@ -720,7 +727,7 @@ static int mxb_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data #endif vv_data.ops.vidioc_default = vidioc_default; if (saa7146_register_device(&mxb->video_dev, dev, "mxb", VFL_TYPE_GRABBER)) { - ERR(("cannot register capture v4l2 device. skipping.\n")); + ERR("cannot register capture v4l2 device. skipping.\n"); saa7146_vv_release(dev); return -1; } @@ -728,11 +735,11 @@ static int mxb_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data /* initialization stuff (vbi) (only for revision > 0 and for extensions which want it)*/ if (MXB_BOARD_CAN_DO_VBI(dev)) { if (saa7146_register_device(&mxb->vbi_dev, dev, "mxb", VFL_TYPE_VBI)) { - ERR(("cannot register vbi v4l2 device. skipping.\n")); + ERR("cannot register vbi v4l2 device. skipping.\n"); } } - printk("mxb: found Multimedia eXtension Board #%d.\n", mxb_num); + pr_info("found Multimedia eXtension Board #%d\n", mxb_num); mxb_num++; mxb_init_done(dev); @@ -743,7 +750,7 @@ static int mxb_detach(struct saa7146_dev *dev) { struct mxb *mxb = (struct mxb *)dev->ext_priv; - DEB_EE(("dev:%p\n", dev)); + DEB_EE("dev:%p\n", dev); saa7146_unregister_device(&mxb->video_dev,dev); if (MXB_BOARD_CAN_DO_VBI(dev)) @@ -765,7 +772,7 @@ static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *standa if (V4L2_STD_PAL_I == standard->id) { v4l2_std_id std = V4L2_STD_PAL_I; - DEB_D(("VIDIOC_S_STD: setting mxb for PAL_I.\n")); + DEB_D("VIDIOC_S_STD: setting mxb for PAL_I\n"); /* set the 7146 gpio register -- I don't know what this does exactly */ saa7146_write(dev, GPIO_CTRL, 0x00404050); /* unset the 7111 gpio register -- I don't know what this does exactly */ @@ -774,7 +781,7 @@ static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *standa } else { v4l2_std_id std = V4L2_STD_PAL_BG; - DEB_D(("VIDIOC_S_STD: setting mxb for PAL/NTSC/SECAM.\n")); + DEB_D("VIDIOC_S_STD: setting mxb for PAL/NTSC/SECAM\n"); /* set the 7146 gpio register -- I don't know what this does exactly */ saa7146_write(dev, GPIO_CTRL, 0x00404050); /* set the 7111 gpio register -- I don't know what this does exactly */ @@ -852,7 +859,7 @@ static struct saa7146_extension extension = { static int __init mxb_init_module(void) { if (saa7146_register_extension(&extension)) { - DEB_S(("failed to register extension.\n")); + DEB_S("failed to register extension\n"); return -ENODEV; } diff --git a/drivers/media/video/noon010pc30.c b/drivers/media/video/noon010pc30.c index 35f722a88f7..50838bf8420 100644 --- a/drivers/media/video/noon010pc30.c +++ b/drivers/media/video/noon010pc30.c @@ -1,7 +1,7 @@ /* * Driver for SiliconFile NOON010PC30 CIF (1/11") Image Sensor with ISP * - * Copyright (C) 2010 Samsung Electronics + * Copyright (C) 2010 - 2011 Samsung Electronics Co., Ltd. * Contact: Sylwester Nawrocki, <s.nawrocki@samsung.com> * * Initial register configuration based on a driver authored by @@ -10,7 +10,7 @@ * 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 vergsion. + * (at your option) any later version. */ #include <linux/delay.h> @@ -21,6 +21,7 @@ #include <media/noon010pc30.h> #include <media/v4l2-chip-ident.h> #include <linux/videodev2.h> +#include <linux/module.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> #include <media/v4l2-mediabus.h> @@ -131,17 +132,23 @@ static const char * const noon010_supply_name[] = { struct noon010_info { struct v4l2_subdev sd; + struct media_pad pad; struct v4l2_ctrl_handler hdl; - const struct noon010pc30_platform_data *pdata; + struct regulator_bulk_data supply[NOON010_NUM_SUPPLIES]; + u32 gpio_nreset; + u32 gpio_nstby; + + /* Protects the struct members below */ + struct mutex lock; + const struct noon010_format *curr_fmt; const struct noon010_frmsize *curr_win; + unsigned int apply_new_cfg:1; + unsigned int streaming:1; unsigned int hflip:1; unsigned int vflip:1; unsigned int power:1; u8 i2c_reg_page; - struct regulator_bulk_data supply[NOON010_NUM_SUPPLIES]; - u32 gpio_nreset; - u32 gpio_nstby; }; struct i2c_regval { @@ -292,8 +299,10 @@ static int noon010_power_ctrl(struct v4l2_subdev *sd, bool reset, bool sleep) u8 reg = sleep ? 0xF1 : 0xF0; int ret = 0; - if (reset) + if (reset) { ret = cam_i2c_write(sd, POWER_CTRL_REG, reg | 0x02); + udelay(20); + } if (!ret) { ret = cam_i2c_write(sd, POWER_CTRL_REG, reg); if (reset && !ret) @@ -313,6 +322,7 @@ static int noon010_enable_autowhitebalance(struct v4l2_subdev *sd, int on) return ret; } +/* Called with struct noon010_info.lock mutex held */ static int noon010_set_flip(struct v4l2_subdev *sd, int hflip, int vflip) { struct noon010_info *info = to_noon010(sd); @@ -340,21 +350,18 @@ static int noon010_set_flip(struct v4l2_subdev *sd, int hflip, int vflip) static int noon010_set_params(struct v4l2_subdev *sd) { struct noon010_info *info = to_noon010(sd); - int ret; - if (!info->curr_win) - return -EINVAL; - - ret = cam_i2c_write(sd, VDO_CTL_REG(0), info->curr_win->vid_ctl1); - - if (!ret && info->curr_fmt) - ret = cam_i2c_write(sd, ISP_CTL_REG(0), - info->curr_fmt->ispctl1_reg); - return ret; + int ret = cam_i2c_write(sd, VDO_CTL_REG(0), + info->curr_win->vid_ctl1); + if (ret) + return ret; + return cam_i2c_write(sd, ISP_CTL_REG(0), + info->curr_fmt->ispctl1_reg); } /* Find nearest matching image pixel size. */ -static int noon010_try_frame_size(struct v4l2_mbus_framefmt *mf) +static int noon010_try_frame_size(struct v4l2_mbus_framefmt *mf, + const struct noon010_frmsize **size) { unsigned int min_err = ~0; int i = ARRAY_SIZE(noon010_sizes); @@ -374,11 +381,14 @@ static int noon010_try_frame_size(struct v4l2_mbus_framefmt *mf) if (match) { mf->width = match->width; mf->height = match->height; + if (size) + *size = match; return 0; } return -EINVAL; } +/* Called with info.lock mutex held */ static int power_enable(struct noon010_info *info) { int ret; @@ -419,6 +429,7 @@ static int power_enable(struct noon010_info *info) return 0; } +/* Called with info.lock mutex held */ static int power_disable(struct noon010_info *info) { int ret; @@ -448,147 +459,175 @@ static int power_disable(struct noon010_info *info) static int noon010_s_ctrl(struct v4l2_ctrl *ctrl) { struct v4l2_subdev *sd = to_sd(ctrl); + struct noon010_info *info = to_noon010(sd); + int ret = 0; v4l2_dbg(1, debug, sd, "%s: ctrl_id: %d, value: %d\n", __func__, ctrl->id, ctrl->val); + mutex_lock(&info->lock); + /* + * If the device is not powered up by the host driver do + * not apply any controls to H/W at this time. Instead + * the controls will be restored right after power-up. + */ + if (!info->power) + goto unlock; + switch (ctrl->id) { case V4L2_CID_AUTO_WHITE_BALANCE: - return noon010_enable_autowhitebalance(sd, ctrl->val); + ret = noon010_enable_autowhitebalance(sd, ctrl->val); + break; case V4L2_CID_BLUE_BALANCE: - return cam_i2c_write(sd, MWB_BGAIN_REG, ctrl->val); + ret = cam_i2c_write(sd, MWB_BGAIN_REG, ctrl->val); + break; case V4L2_CID_RED_BALANCE: - return cam_i2c_write(sd, MWB_RGAIN_REG, ctrl->val); + ret = cam_i2c_write(sd, MWB_RGAIN_REG, ctrl->val); + break; default: - return -EINVAL; + ret = -EINVAL; } +unlock: + mutex_unlock(&info->lock); + return ret; } -static int noon010_enum_fmt(struct v4l2_subdev *sd, unsigned int index, - enum v4l2_mbus_pixelcode *code) +static int noon010_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) { - if (!code || index >= ARRAY_SIZE(noon010_formats)) + if (code->index >= ARRAY_SIZE(noon010_formats)) return -EINVAL; - *code = noon010_formats[index].code; + code->code = noon010_formats[code->index].code; return 0; } -static int noon010_g_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) +static int noon010_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) { struct noon010_info *info = to_noon010(sd); - int ret; - - if (!mf) - return -EINVAL; + struct v4l2_mbus_framefmt *mf; - if (!info->curr_win || !info->curr_fmt) { - ret = noon010_set_params(sd); - if (ret) - return ret; + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + if (fh) { + mf = v4l2_subdev_get_try_format(fh, 0); + fmt->format = *mf; + } + return 0; } + mf = &fmt->format; - mf->width = info->curr_win->width; - mf->height = info->curr_win->height; - mf->code = info->curr_fmt->code; - mf->colorspace = info->curr_fmt->colorspace; - mf->field = V4L2_FIELD_NONE; + mutex_lock(&info->lock); + mf->width = info->curr_win->width; + mf->height = info->curr_win->height; + mf->code = info->curr_fmt->code; + mf->colorspace = info->curr_fmt->colorspace; + mf->field = V4L2_FIELD_NONE; + mutex_unlock(&info->lock); return 0; } /* Return nearest media bus frame format. */ -static const struct noon010_format *try_fmt(struct v4l2_subdev *sd, +static const struct noon010_format *noon010_try_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) { int i = ARRAY_SIZE(noon010_formats); - noon010_try_frame_size(mf); - - while (i--) + while (--i) if (mf->code == noon010_formats[i].code) break; - mf->code = noon010_formats[i].code; return &noon010_formats[i]; } -static int noon010_try_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) -{ - if (!sd || !mf) - return -EINVAL; - - try_fmt(sd, mf); - return 0; -} - -static int noon010_s_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) +static int noon010_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) { struct noon010_info *info = to_noon010(sd); + const struct noon010_frmsize *size = NULL; + const struct noon010_format *nf; + struct v4l2_mbus_framefmt *mf; + int ret = 0; - if (!sd || !mf) - return -EINVAL; - - info->curr_fmt = try_fmt(sd, mf); + nf = noon010_try_fmt(sd, &fmt->format); + noon010_try_frame_size(&fmt->format, &size); + fmt->format.colorspace = V4L2_COLORSPACE_JPEG; - return noon010_set_params(sd); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + if (fh) { + mf = v4l2_subdev_get_try_format(fh, 0); + *mf = fmt->format; + } + return 0; + } + mutex_lock(&info->lock); + if (!info->streaming) { + info->apply_new_cfg = 1; + info->curr_fmt = nf; + info->curr_win = size; + } else { + ret = -EBUSY; + } + mutex_unlock(&info->lock); + return ret; } +/* Called with struct noon010_info.lock mutex held */ static int noon010_base_config(struct v4l2_subdev *sd) { - struct noon010_info *info = to_noon010(sd); - int ret; - - ret = noon010_bulk_write_reg(sd, noon010_base_regs); - if (!ret) { - info->curr_fmt = &noon010_formats[0]; - info->curr_win = &noon010_sizes[0]; + int ret = noon010_bulk_write_reg(sd, noon010_base_regs); + if (!ret) ret = noon010_set_params(sd); - } if (!ret) ret = noon010_set_flip(sd, 1, 0); - if (!ret) - ret = noon010_power_ctrl(sd, false, false); - /* sync the handler and the registers state */ - v4l2_ctrl_handler_setup(&to_noon010(sd)->hdl); return ret; } static int noon010_s_power(struct v4l2_subdev *sd, int on) { struct noon010_info *info = to_noon010(sd); - const struct noon010pc30_platform_data *pdata = info->pdata; - int ret = 0; - - if (WARN(pdata == NULL, "No platform data!\n")) - return -ENOMEM; + int ret; + mutex_lock(&info->lock); if (on) { ret = power_enable(info); - if (ret) - return ret; - ret = noon010_base_config(sd); + if (!ret) + ret = noon010_base_config(sd); } else { noon010_power_ctrl(sd, false, true); ret = power_disable(info); - info->curr_win = NULL; - info->curr_fmt = NULL; } + mutex_unlock(&info->lock); + + /* Restore the controls state */ + if (!ret && on) + ret = v4l2_ctrl_handler_setup(&info->hdl); return ret; } -static int noon010_g_chip_ident(struct v4l2_subdev *sd, - struct v4l2_dbg_chip_ident *chip) +static int noon010_s_stream(struct v4l2_subdev *sd, int on) { - struct i2c_client *client = v4l2_get_subdevdata(sd); + struct noon010_info *info = to_noon010(sd); + int ret = 0; - return v4l2_chip_ident_i2c_client(client, chip, - V4L2_IDENT_NOON010PC30, 0); + mutex_lock(&info->lock); + if (!info->streaming != !on) { + ret = noon010_power_ctrl(sd, false, !on); + if (!ret) + info->streaming = on; + } + if (!ret && on && info->apply_new_cfg) { + ret = noon010_set_params(sd); + if (!ret) + info->apply_new_cfg = 0; + } + mutex_unlock(&info->lock); + return ret; } static int noon010_log_status(struct v4l2_subdev *sd) @@ -599,12 +638,27 @@ static int noon010_log_status(struct v4l2_subdev *sd) return 0; } +static int noon010_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct v4l2_mbus_framefmt *mf = v4l2_subdev_get_try_format(fh, 0); + + mf->width = noon010_sizes[0].width; + mf->height = noon010_sizes[0].height; + mf->code = noon010_formats[0].code; + mf->colorspace = V4L2_COLORSPACE_JPEG; + mf->field = V4L2_FIELD_NONE; + return 0; +} + +static const struct v4l2_subdev_internal_ops noon010_subdev_internal_ops = { + .open = noon010_open, +}; + static const struct v4l2_ctrl_ops noon010_ctrl_ops = { .s_ctrl = noon010_s_ctrl, }; static const struct v4l2_subdev_core_ops noon010_core_ops = { - .g_chip_ident = noon010_g_chip_ident, .s_power = noon010_s_power, .g_ctrl = v4l2_subdev_g_ctrl, .s_ctrl = v4l2_subdev_s_ctrl, @@ -616,15 +670,19 @@ static const struct v4l2_subdev_core_ops noon010_core_ops = { .log_status = noon010_log_status, }; -static const struct v4l2_subdev_video_ops noon010_video_ops = { - .g_mbus_fmt = noon010_g_fmt, - .s_mbus_fmt = noon010_s_fmt, - .try_mbus_fmt = noon010_try_fmt, - .enum_mbus_fmt = noon010_enum_fmt, +static struct v4l2_subdev_pad_ops noon010_pad_ops = { + .enum_mbus_code = noon010_enum_mbus_code, + .get_fmt = noon010_get_fmt, + .set_fmt = noon010_set_fmt, +}; + +static struct v4l2_subdev_video_ops noon010_video_ops = { + .s_stream = noon010_s_stream, }; static const struct v4l2_subdev_ops noon010_ops = { .core = &noon010_core_ops, + .pad = &noon010_pad_ops, .video = &noon010_video_ops, }; @@ -665,10 +723,14 @@ static int noon010_probe(struct i2c_client *client, if (!info) return -ENOMEM; + mutex_init(&info->lock); sd = &info->sd; strlcpy(sd->name, MODULE_NAME, sizeof(sd->name)); v4l2_i2c_subdev_init(sd, client, &noon010_ops); + sd->internal_ops = &noon010_subdev_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + v4l2_ctrl_handler_init(&info->hdl, 3); v4l2_ctrl_new_std(&info->hdl, &noon010_ctrl_ops, @@ -684,10 +746,11 @@ static int noon010_probe(struct i2c_client *client, if (ret) goto np_err; - info->pdata = client->dev.platform_data; info->i2c_reg_page = -1; info->gpio_nreset = -EINVAL; info->gpio_nstby = -EINVAL; + info->curr_fmt = &noon010_formats[0]; + info->curr_win = &noon010_sizes[0]; if (gpio_is_valid(pdata->gpio_nreset)) { ret = gpio_request(pdata->gpio_nreset, "NOON010PC30 NRST"); @@ -719,11 +782,17 @@ static int noon010_probe(struct i2c_client *client, if (ret) goto np_reg_err; + info->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; + ret = media_entity_init(&sd->entity, 1, &info->pad, 0); + if (ret < 0) + goto np_me_err; + ret = noon010_detect(client, info); if (!ret) return 0; - /* the sensor detection failed */ +np_me_err: regulator_bulk_free(NOON010_NUM_SUPPLIES, info->supply); np_reg_err: if (gpio_is_valid(info->gpio_nstby)) @@ -754,6 +823,7 @@ static int noon010_remove(struct i2c_client *client) if (gpio_is_valid(info->gpio_nstby)) gpio_free(info->gpio_nstby); + media_entity_cleanup(&sd->entity); kfree(info); return 0; } diff --git a/drivers/media/video/omap/omap_vout.c b/drivers/media/video/omap/omap_vout.c index b5ef3622244..9c5c19f142d 100644 --- a/drivers/media/video/omap/omap_vout.c +++ b/drivers/media/video/omap/omap_vout.c @@ -400,7 +400,6 @@ static int omapvid_setup_overlay(struct omap_vout_device *vout, ovl->get_overlay_info(ovl, &info); info.paddr = addr; - info.vaddr = NULL; info.width = cropwidth; info.height = cropheight; info.color_mode = vout->dss_mode; @@ -834,6 +833,15 @@ static void omap_vout_buffer_release(struct videobuf_queue *q, /* * File operations */ +static unsigned int omap_vout_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct omap_vout_device *vout = file->private_data; + struct videobuf_queue *q = &vout->vbq; + + return videobuf_poll_stream(file, q, wait); +} + static void omap_vout_vm_open(struct vm_area_struct *vma) { struct omap_vout_device *vout = vma->vm_private_data; @@ -1165,12 +1173,17 @@ static int vidioc_try_fmt_vid_overlay(struct file *file, void *fh, { int ret = 0; struct omap_vout_device *vout = fh; + struct omap_overlay *ovl; + struct omapvideo_info *ovid; struct v4l2_window *win = &f->fmt.win; + ovid = &vout->vid_info; + ovl = ovid->overlays[0]; + ret = omap_vout_try_window(&vout->fbuf, win); if (!ret) { - if (vout->vid == OMAP_VIDEO1) + if ((ovl->caps & OMAP_DSS_OVL_CAP_GLOBAL_ALPHA) == 0) win->global_alpha = 255; else win->global_alpha = f->fmt.win.global_alpha; @@ -1194,8 +1207,8 @@ static int vidioc_s_fmt_vid_overlay(struct file *file, void *fh, ret = omap_vout_new_window(&vout->crop, &vout->win, &vout->fbuf, win); if (!ret) { - /* Video1 plane does not support global alpha */ - if (ovl->id == OMAP_DSS_VIDEO1) + /* Video1 plane does not support global alpha on OMAP3 */ + if ((ovl->caps & OMAP_DSS_OVL_CAP_GLOBAL_ALPHA) == 0) vout->win.global_alpha = 255; else vout->win.global_alpha = f->fmt.win.global_alpha; @@ -1788,7 +1801,9 @@ static int vidioc_s_fbuf(struct file *file, void *fh, if (ovl->manager && ovl->manager->get_manager_info && ovl->manager->set_manager_info) { ovl->manager->get_manager_info(ovl->manager, &info); - info.alpha_enabled = enable; + /* enable this only if there is no zorder cap */ + if ((ovl->caps & OMAP_DSS_OVL_CAP_ZORDER) == 0) + info.partial_alpha_enabled = enable; if (ovl->manager->set_manager_info(ovl->manager, &info)) return -EINVAL; } @@ -1820,7 +1835,7 @@ static int vidioc_g_fbuf(struct file *file, void *fh, } if (ovl->manager && ovl->manager->get_manager_info) { ovl->manager->get_manager_info(ovl->manager, &info); - if (info.alpha_enabled) + if (info.partial_alpha_enabled) a->flags |= V4L2_FBUF_FLAG_LOCAL_ALPHA; } @@ -1855,6 +1870,7 @@ static const struct v4l2_ioctl_ops vout_ioctl_ops = { static const struct v4l2_file_operations omap_vout_fops = { .owner = THIS_MODULE, + .poll = omap_vout_poll, .unlocked_ioctl = video_ioctl2, .mmap = omap_vout_mmap, .open = omap_vout_open, @@ -2194,19 +2210,6 @@ static int __init omap_vout_probe(struct platform_device *pdev) "'%s' Display already enabled\n", def_display->name); } - /* set the update mode */ - if (def_display->caps & - OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) { - if (dssdrv->enable_te) - dssdrv->enable_te(def_display, 0); - if (dssdrv->set_update_mode) - dssdrv->set_update_mode(def_display, - OMAP_DSS_UPDATE_MANUAL); - } else { - if (dssdrv->set_update_mode) - dssdrv->set_update_mode(def_display, - OMAP_DSS_UPDATE_AUTO); - } } } diff --git a/drivers/media/video/omap1_camera.c b/drivers/media/video/omap1_camera.c index 8a947e603ac..e87ae2f634b 100644 --- a/drivers/media/video/omap1_camera.c +++ b/drivers/media/video/omap1_camera.c @@ -102,10 +102,10 @@ /* end of OMAP1 Camera Interface registers */ -#define SOCAM_BUS_FLAGS (SOCAM_MASTER | \ - SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH | \ - SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING | \ - SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATAWIDTH_8) +#define SOCAM_BUS_FLAGS (V4L2_MBUS_MASTER | \ + V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH | \ + V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | \ + V4L2_MBUS_DATA_ACTIVE_HIGH) #define FIFO_SIZE ((THRESHOLD_MASK >> THRESHOLD_SHIFT) + 1) @@ -1438,41 +1438,55 @@ static int omap1_cam_querycap(struct soc_camera_host *ici, static int omap1_cam_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) { + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); struct device *dev = icd->parent; struct soc_camera_host *ici = to_soc_camera_host(dev); struct omap1_cam_dev *pcdev = ici->priv; const struct soc_camera_format_xlate *xlate; const struct soc_mbus_pixelfmt *fmt; - unsigned long camera_flags, common_flags; + struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,}; + unsigned long common_flags; u32 ctrlclock, mode; int ret; - camera_flags = icd->ops->query_bus_param(icd); - - common_flags = soc_camera_bus_param_compatible(camera_flags, - SOCAM_BUS_FLAGS); - if (!common_flags) - return -EINVAL; + ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg); + if (!ret) { + common_flags = soc_mbus_config_compatible(&cfg, SOCAM_BUS_FLAGS); + if (!common_flags) { + dev_warn(dev, + "Flags incompatible: camera 0x%x, host 0x%x\n", + cfg.flags, SOCAM_BUS_FLAGS); + return -EINVAL; + } + } else if (ret != -ENOIOCTLCMD) { + return ret; + } else { + common_flags = SOCAM_BUS_FLAGS; + } /* Make choices, possibly based on platform configuration */ - if ((common_flags & SOCAM_PCLK_SAMPLE_RISING) && - (common_flags & SOCAM_PCLK_SAMPLE_FALLING)) { + if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) && + (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) { if (!pcdev->pdata || pcdev->pdata->flags & OMAP1_CAMERA_LCLK_RISING) - common_flags &= ~SOCAM_PCLK_SAMPLE_FALLING; + common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING; else - common_flags &= ~SOCAM_PCLK_SAMPLE_RISING; + common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING; } - ret = icd->ops->set_bus_param(icd, common_flags); - if (ret < 0) + cfg.flags = common_flags; + ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg); + if (ret < 0 && ret != -ENOIOCTLCMD) { + dev_dbg(dev, "camera s_mbus_config(0x%lx) returned %d\n", + common_flags, ret); return ret; + } ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK); if (ctrlclock & LCLK_EN) CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN); - if (common_flags & SOCAM_PCLK_SAMPLE_RISING) { + if (common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) { dev_dbg(dev, "CTRLCLOCK_REG |= POLCLK\n"); ctrlclock |= POLCLK; } else { @@ -1565,10 +1579,10 @@ static int __init omap1_cam_probe(struct platform_device *pdev) pcdev->clk = clk; pcdev->pdata = pdev->dev.platform_data; - pcdev->pflags = pcdev->pdata->flags; - - if (pcdev->pdata) + if (pcdev->pdata) { + pcdev->pflags = pcdev->pdata->flags; pcdev->camexclk = pcdev->pdata->camexclk_khz * 1000; + } switch (pcdev->camexclk) { case 6000000: @@ -1578,6 +1592,7 @@ static int __init omap1_cam_probe(struct platform_device *pdev) case 24000000: break; default: + /* pcdev->camexclk != 0 => pcdev->pdata != NULL */ dev_warn(&pdev->dev, "Incorrect sensor clock frequency %ld kHz, " "should be one of 0, 6, 8, 9.6, 12 or 24 MHz, " @@ -1585,8 +1600,7 @@ static int __init omap1_cam_probe(struct platform_device *pdev) pcdev->pdata->camexclk_khz); pcdev->camexclk = 0; case 0: - dev_info(&pdev->dev, - "Not providing sensor clock\n"); + dev_info(&pdev->dev, "Not providing sensor clock\n"); } INIT_LIST_HEAD(&pcdev->capture); @@ -1716,5 +1730,5 @@ MODULE_PARM_DESC(sg_mode, "videobuf mode, 0: dma-contig (default), 1: dma-sg"); MODULE_DESCRIPTION("OMAP1 Camera Interface driver"); MODULE_AUTHOR("Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>"); MODULE_LICENSE("GPL v2"); -MODULE_LICENSE(DRIVER_VERSION); +MODULE_VERSION(DRIVER_VERSION); MODULE_ALIAS("platform:" DRIVER_NAME); diff --git a/drivers/media/video/omap24xxcam.c b/drivers/media/video/omap24xxcam.c index eb97bff7116..45522e60318 100644 --- a/drivers/media/video/omap24xxcam.c +++ b/drivers/media/video/omap24xxcam.c @@ -36,6 +36,7 @@ #include <linux/io.h> #include <linux/slab.h> #include <linux/sched.h> +#include <linux/module.h> #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> diff --git a/drivers/media/video/omap3isp/Makefile b/drivers/media/video/omap3isp/Makefile index b1b344774ae..e8847e79e31 100644 --- a/drivers/media/video/omap3isp/Makefile +++ b/drivers/media/video/omap3isp/Makefile @@ -1,8 +1,6 @@ # Makefile for OMAP3 ISP driver -ifdef CONFIG_VIDEO_OMAP3_DEBUG -EXTRA_CFLAGS += -DDEBUG -endif +ccflags-$(CONFIG_VIDEO_OMAP3_DEBUG) += -DDEBUG omap3-isp-objs += \ isp.o ispqueue.o ispvideo.o \ diff --git a/drivers/media/video/omap3isp/isp.c b/drivers/media/video/omap3isp/isp.c index 5cea2bbd701..b818cacf420 100644 --- a/drivers/media/video/omap3isp/isp.c +++ b/drivers/media/video/omap3isp/isp.c @@ -80,6 +80,13 @@ #include "isph3a.h" #include "isphist.h" +/* + * this is provided as an interim solution until omap3isp doesn't need + * any omap-specific iommu API + */ +#define to_iommu(dev) \ + (struct omap_iommu *)platform_get_drvdata(to_platform_device(dev)) + static unsigned int autoidle; module_param(autoidle, int, 0444); MODULE_PARM_DESC(autoidle, "Enable OMAP3ISP AUTOIDLE support"); @@ -732,7 +739,7 @@ static int isp_pipeline_enable(struct isp_pipeline *pipe, struct media_pad *pad; struct v4l2_subdev *subdev; unsigned long flags; - int ret = 0; + int ret; spin_lock_irqsave(&pipe->lock, flags); pipe->state &= ~(ISP_PIPELINE_IDLE_INPUT | ISP_PIPELINE_IDLE_OUTPUT); @@ -756,7 +763,7 @@ static int isp_pipeline_enable(struct isp_pipeline *pipe, ret = v4l2_subdev_call(subdev, video, s_stream, mode); if (ret < 0 && ret != -ENOIOCTLCMD) - break; + return ret; if (subdev == &isp->isp_ccdc.subdev) { v4l2_subdev_call(&isp->isp_aewb.subdev, video, @@ -777,7 +784,7 @@ static int isp_pipeline_enable(struct isp_pipeline *pipe, if (pipe->do_propagation && mode == ISP_PIPELINE_STREAM_SINGLESHOT) atomic_inc(&pipe->frame_number); - return ret; + return 0; } static int isp_pipeline_wait_resizer(struct isp_device *isp) @@ -1108,7 +1115,7 @@ static void isp_save_ctx(struct isp_device *isp) { isp_save_context(isp, isp_reg_list); if (isp->iommu) - iommu_save_ctx(isp->iommu); + omap_iommu_save_ctx(isp->iommu); } /* @@ -1122,7 +1129,7 @@ static void isp_restore_ctx(struct isp_device *isp) { isp_restore_context(isp, isp_reg_list); if (isp->iommu) - iommu_restore_ctx(isp->iommu); + omap_iommu_restore_ctx(isp->iommu); omap3isp_ccdc_restore_context(isp); omap3isp_preview_restore_context(isp); } @@ -1697,6 +1704,7 @@ static int isp_register_entities(struct isp_device *isp) isp->media_dev.dev = isp->dev; strlcpy(isp->media_dev.model, "TI OMAP3 ISP", sizeof(isp->media_dev.model)); + isp->media_dev.hw_revision = isp->revision; isp->media_dev.link_notify = isp_pipeline_link_notify; ret = media_device_register(&isp->media_dev); if (ret < 0) { @@ -1975,7 +1983,8 @@ static int isp_remove(struct platform_device *pdev) isp_cleanup_modules(isp); omap3isp_get(isp); - iommu_put(isp->iommu); + iommu_detach_device(isp->domain, isp->iommu_dev); + iommu_domain_free(isp->domain); omap3isp_put(isp); free_irq(isp->irq_num, isp); @@ -2123,25 +2132,41 @@ static int isp_probe(struct platform_device *pdev) } /* IOMMU */ - isp->iommu = iommu_get("isp"); - if (IS_ERR_OR_NULL(isp->iommu)) { - isp->iommu = NULL; + isp->iommu_dev = omap_find_iommu_device("isp"); + if (!isp->iommu_dev) { + dev_err(isp->dev, "omap_find_iommu_device failed\n"); ret = -ENODEV; goto error_isp; } + /* to be removed once iommu migration is complete */ + isp->iommu = to_iommu(isp->iommu_dev); + + isp->domain = iommu_domain_alloc(pdev->dev.bus); + if (!isp->domain) { + dev_err(isp->dev, "can't alloc iommu domain\n"); + ret = -ENOMEM; + goto error_isp; + } + + ret = iommu_attach_device(isp->domain, isp->iommu_dev); + if (ret) { + dev_err(&pdev->dev, "can't attach iommu device: %d\n", ret); + goto free_domain; + } + /* Interrupt */ isp->irq_num = platform_get_irq(pdev, 0); if (isp->irq_num <= 0) { dev_err(isp->dev, "No IRQ resource\n"); ret = -ENODEV; - goto error_isp; + goto detach_dev; } if (request_irq(isp->irq_num, isp_isr, IRQF_SHARED, "OMAP3 ISP", isp)) { dev_err(isp->dev, "Unable to request IRQ\n"); ret = -EINVAL; - goto error_isp; + goto detach_dev; } /* Entities */ @@ -2162,8 +2187,11 @@ error_modules: isp_cleanup_modules(isp); error_irq: free_irq(isp->irq_num, isp); +detach_dev: + iommu_detach_device(isp->domain, isp->iommu_dev); +free_domain: + iommu_domain_free(isp->domain); error_isp: - iommu_put(isp->iommu); omap3isp_put(isp); error: isp_put_clocks(isp); @@ -2183,6 +2211,8 @@ error: regulator_put(isp->isp_csiphy2.vdd); regulator_put(isp->isp_csiphy1.vdd); platform_set_drvdata(pdev, NULL); + + mutex_destroy(&isp->isp_mutex); kfree(isp); return ret; diff --git a/drivers/media/video/omap3isp/isp.h b/drivers/media/video/omap3isp/isp.h index 529e582ef94..705946ef4d6 100644 --- a/drivers/media/video/omap3isp/isp.h +++ b/drivers/media/video/omap3isp/isp.h @@ -27,11 +27,13 @@ #ifndef OMAP3_ISP_CORE_H #define OMAP3_ISP_CORE_H +#include <media/omap3isp.h> #include <media/v4l2-device.h> #include <linux/device.h> #include <linux/io.h> #include <linux/platform_device.h> #include <linux/wait.h> +#include <linux/iommu.h> #include <plat/iommu.h> #include <plat/iovmm.h> @@ -94,14 +96,6 @@ enum isp_subclk_resource { OMAP3_ISP_SUBCLK_RESIZER = (1 << 4), }; -enum isp_interface_type { - ISP_INTERFACE_PARALLEL, - ISP_INTERFACE_CSI2A_PHY2, - ISP_INTERFACE_CCP2B_PHY1, - ISP_INTERFACE_CCP2B_PHY2, - ISP_INTERFACE_CSI2C_PHY1, -}; - /* ISP: OMAP 34xx ES 1.0 */ #define ISP_REVISION_1_0 0x10 /* ISP2: OMAP 34xx ES 2.0, 2.1 and 3.0 */ @@ -130,82 +124,6 @@ struct isp_reg { u32 val; }; -/** - * struct isp_parallel_platform_data - Parallel interface platform data - * @data_lane_shift: Data lane shifter - * 0 - CAMEXT[13:0] -> CAM[13:0] - * 1 - CAMEXT[13:2] -> CAM[11:0] - * 2 - CAMEXT[13:4] -> CAM[9:0] - * 3 - CAMEXT[13:6] -> CAM[7:0] - * @clk_pol: Pixel clock polarity - * 0 - Non Inverted, 1 - Inverted - * @hs_pol: Horizontal synchronization polarity - * 0 - Active high, 1 - Active low - * @vs_pol: Vertical synchronization polarity - * 0 - Active high, 1 - Active low - * @bridge: CCDC Bridge input control - * ISPCTRL_PAR_BRIDGE_DISABLE - Disable - * ISPCTRL_PAR_BRIDGE_LENDIAN - Little endian - * ISPCTRL_PAR_BRIDGE_BENDIAN - Big endian - */ -struct isp_parallel_platform_data { - unsigned int data_lane_shift:2; - unsigned int clk_pol:1; - unsigned int hs_pol:1; - unsigned int vs_pol:1; - unsigned int bridge:4; -}; - -/** - * struct isp_ccp2_platform_data - CCP2 interface platform data - * @strobe_clk_pol: Strobe/clock polarity - * 0 - Non Inverted, 1 - Inverted - * @crc: Enable the cyclic redundancy check - * @ccp2_mode: Enable CCP2 compatibility mode - * 0 - MIPI-CSI1 mode, 1 - CCP2 mode - * @phy_layer: Physical layer selection - * ISPCCP2_CTRL_PHY_SEL_CLOCK - Data/clock physical layer - * ISPCCP2_CTRL_PHY_SEL_STROBE - Data/strobe physical layer - * @vpclk_div: Video port output clock control - */ -struct isp_ccp2_platform_data { - unsigned int strobe_clk_pol:1; - unsigned int crc:1; - unsigned int ccp2_mode:1; - unsigned int phy_layer:1; - unsigned int vpclk_div:2; -}; - -/** - * struct isp_csi2_platform_data - CSI2 interface platform data - * @crc: Enable the cyclic redundancy check - * @vpclk_div: Video port output clock control - */ -struct isp_csi2_platform_data { - unsigned crc:1; - unsigned vpclk_div:2; -}; - -struct isp_subdev_i2c_board_info { - struct i2c_board_info *board_info; - int i2c_adapter_id; -}; - -struct isp_v4l2_subdevs_group { - struct isp_subdev_i2c_board_info *subdevs; - enum isp_interface_type interface; - union { - struct isp_parallel_platform_data parallel; - struct isp_ccp2_platform_data ccp2; - struct isp_csi2_platform_data csi2; - } bus; /* gcc < 4.6.0 chokes on anonymous union initializers */ -}; - -struct isp_platform_data { - struct isp_v4l2_subdevs_group *subdevs; - void (*set_constraints)(struct isp_device *isp, bool enable); -}; - struct isp_platform_callback { u32 (*set_xclk)(struct isp_device *isp, u32 xclk, u8 xclksel); int (*csiphy_config)(struct isp_csiphy *phy, @@ -294,7 +212,9 @@ struct isp_device { unsigned int sbl_resources; unsigned int subclk_resources; - struct iommu *iommu; + struct omap_iommu *iommu; + struct iommu_domain *domain; + struct device *iommu_dev; struct isp_platform_callback platform_cb; }; diff --git a/drivers/media/video/omap3isp/ispccdc.c b/drivers/media/video/omap3isp/ispccdc.c index 9d3459de04b..b0b0fa5a357 100644 --- a/drivers/media/video/omap3isp/ispccdc.c +++ b/drivers/media/video/omap3isp/ispccdc.c @@ -31,6 +31,7 @@ #include <linux/dma-mapping.h> #include <linux/mm.h> #include <linux/sched.h> +#include <linux/slab.h> #include <media/v4l2-event.h> #include "isp.h" @@ -365,7 +366,7 @@ static void ccdc_lsc_free_request(struct isp_ccdc_device *ccdc, dma_unmap_sg(isp->dev, req->iovm->sgt->sgl, req->iovm->sgt->nents, DMA_TO_DEVICE); if (req->table) - iommu_vfree(isp->iommu, req->table); + omap_iommu_vfree(isp->domain, isp->iommu, req->table); kfree(req); } @@ -437,15 +438,15 @@ static int ccdc_lsc_config(struct isp_ccdc_device *ccdc, req->enable = 1; - req->table = iommu_vmalloc(isp->iommu, 0, req->config.size, - IOMMU_FLAG); + req->table = omap_iommu_vmalloc(isp->domain, isp->iommu, 0, + req->config.size, IOMMU_FLAG); if (IS_ERR_VALUE(req->table)) { req->table = 0; ret = -ENOMEM; goto done; } - req->iovm = find_iovm_area(isp->iommu, req->table); + req->iovm = omap_find_iovm_area(isp->iommu, req->table); if (req->iovm == NULL) { ret = -ENOMEM; goto done; @@ -461,7 +462,7 @@ static int ccdc_lsc_config(struct isp_ccdc_device *ccdc, dma_sync_sg_for_cpu(isp->dev, req->iovm->sgt->sgl, req->iovm->sgt->nents, DMA_TO_DEVICE); - table = da_to_va(isp->iommu, req->table); + table = omap_da_to_va(isp->iommu, req->table); if (copy_from_user(table, config->lsc, req->config.size)) { ret = -EFAULT; goto done; @@ -730,18 +731,19 @@ static int ccdc_config(struct isp_ccdc_device *ccdc, /* * table_new must be 64-bytes aligned, but it's - * already done by iommu_vmalloc(). + * already done by omap_iommu_vmalloc(). */ size = ccdc->fpc.fpnum * 4; - table_new = iommu_vmalloc(isp->iommu, 0, size, - IOMMU_FLAG); + table_new = omap_iommu_vmalloc(isp->domain, isp->iommu, + 0, size, IOMMU_FLAG); if (IS_ERR_VALUE(table_new)) return -ENOMEM; - if (copy_from_user(da_to_va(isp->iommu, table_new), + if (copy_from_user(omap_da_to_va(isp->iommu, table_new), (__force void __user *) ccdc->fpc.fpcaddr, size)) { - iommu_vfree(isp->iommu, table_new); + omap_iommu_vfree(isp->domain, isp->iommu, + table_new); return -EFAULT; } @@ -751,7 +753,7 @@ static int ccdc_config(struct isp_ccdc_device *ccdc, ccdc_configure_fpc(ccdc); if (table_old != 0) - iommu_vfree(isp->iommu, table_old); + omap_iommu_vfree(isp->domain, isp->iommu, table_old); } return ccdc_lsc_config(ccdc, ccdc_struct); @@ -1404,11 +1406,14 @@ static int __ccdc_handle_stopping(struct isp_ccdc_device *ccdc, u32 event) static void ccdc_hs_vs_isr(struct isp_ccdc_device *ccdc) { + struct isp_pipeline *pipe = + to_isp_pipeline(&ccdc->video_out.video.entity); struct video_device *vdev = &ccdc->subdev.devnode; struct v4l2_event event; memset(&event, 0, sizeof(event)); - event.type = V4L2_EVENT_OMAP3ISP_HS_VS; + event.type = V4L2_EVENT_FRAME_SYNC; + event.u.frame_sync.frame_sequence = atomic_read(&pipe->frame_number); v4l2_event_queue(vdev, &event); } @@ -1690,7 +1695,11 @@ static long ccdc_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) static int ccdc_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, struct v4l2_event_subscription *sub) { - if (sub->type != V4L2_EVENT_OMAP3ISP_HS_VS) + if (sub->type != V4L2_EVENT_FRAME_SYNC) + return -EINVAL; + + /* line number is zero at frame start */ + if (sub->id != 0) return -EINVAL; return v4l2_event_subscribe(fh, sub, OMAP3ISP_CCDC_NEVENTS); @@ -1827,7 +1836,7 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, * callers to request an output size bigger than the input size * up to the nearest multiple of 16. */ - fmt->width = clamp_t(u32, width, 32, (fmt->width + 15) & ~15); + fmt->width = clamp_t(u32, width, 32, fmt->width + 15); fmt->width &= ~15; fmt->height = clamp_t(u32, height, 32, fmt->height); break; @@ -2143,6 +2152,37 @@ static const struct media_entity_operations ccdc_media_ops = { .link_setup = ccdc_link_setup, }; +void omap3isp_ccdc_unregister_entities(struct isp_ccdc_device *ccdc) +{ + v4l2_device_unregister_subdev(&ccdc->subdev); + omap3isp_video_unregister(&ccdc->video_out); +} + +int omap3isp_ccdc_register_entities(struct isp_ccdc_device *ccdc, + struct v4l2_device *vdev) +{ + int ret; + + /* Register the subdev and video node. */ + ret = v4l2_device_register_subdev(vdev, &ccdc->subdev); + if (ret < 0) + goto error; + + ret = omap3isp_video_register(&ccdc->video_out, vdev); + if (ret < 0) + goto error; + + return 0; + +error: + omap3isp_ccdc_unregister_entities(ccdc); + return ret; +} + +/* ----------------------------------------------------------------------------- + * ISP CCDC initialisation and cleanup + */ + /* * ccdc_init_entities - Initialize V4L2 subdev and media entity * @ccdc: ISP CCDC module @@ -2184,50 +2224,23 @@ static int ccdc_init_entities(struct isp_ccdc_device *ccdc) ret = omap3isp_video_init(&ccdc->video_out, "CCDC"); if (ret < 0) - return ret; + goto error_video; /* Connect the CCDC subdev to the video node. */ ret = media_entity_create_link(&ccdc->subdev.entity, CCDC_PAD_SOURCE_OF, &ccdc->video_out.video.entity, 0, 0); if (ret < 0) - return ret; - - return 0; -} - -void omap3isp_ccdc_unregister_entities(struct isp_ccdc_device *ccdc) -{ - media_entity_cleanup(&ccdc->subdev.entity); - - v4l2_device_unregister_subdev(&ccdc->subdev); - omap3isp_video_unregister(&ccdc->video_out); -} - -int omap3isp_ccdc_register_entities(struct isp_ccdc_device *ccdc, - struct v4l2_device *vdev) -{ - int ret; - - /* Register the subdev and video node. */ - ret = v4l2_device_register_subdev(vdev, &ccdc->subdev); - if (ret < 0) - goto error; - - ret = omap3isp_video_register(&ccdc->video_out, vdev); - if (ret < 0) - goto error; + goto error_link; return 0; -error: - omap3isp_ccdc_unregister_entities(ccdc); +error_link: + omap3isp_video_cleanup(&ccdc->video_out); +error_video: + media_entity_cleanup(me); return ret; } -/* ----------------------------------------------------------------------------- - * ISP CCDC initialisation and cleanup - */ - /* * omap3isp_ccdc_init - CCDC module initialization. * @dev: Device pointer specific to the OMAP3 ISP. @@ -2239,6 +2252,7 @@ error: int omap3isp_ccdc_init(struct isp_device *isp) { struct isp_ccdc_device *ccdc = &isp->isp_ccdc; + int ret; spin_lock_init(&ccdc->lock); init_waitqueue_head(&ccdc->wait); @@ -2267,7 +2281,13 @@ int omap3isp_ccdc_init(struct isp_device *isp) ccdc->update = OMAP3ISP_CCDC_BLCLAMP; ccdc_apply_controls(ccdc); - return ccdc_init_entities(ccdc); + ret = ccdc_init_entities(ccdc); + if (ret < 0) { + mutex_destroy(&ccdc->ioctl_lock); + return ret; + } + + return 0; } /* @@ -2278,6 +2298,9 @@ void omap3isp_ccdc_cleanup(struct isp_device *isp) { struct isp_ccdc_device *ccdc = &isp->isp_ccdc; + omap3isp_video_cleanup(&ccdc->video_out); + media_entity_cleanup(&ccdc->subdev.entity); + /* Free LSC requests. As the CCDC is stopped there's no active request, * so only the pending request and the free queue need to be handled. */ @@ -2286,5 +2309,7 @@ void omap3isp_ccdc_cleanup(struct isp_device *isp) ccdc_lsc_free_queue(ccdc, &ccdc->lsc.free_queue); if (ccdc->fpc.fpcaddr != 0) - iommu_vfree(isp->iommu, ccdc->fpc.fpcaddr); + omap_iommu_vfree(isp->domain, isp->iommu, ccdc->fpc.fpcaddr); + + mutex_destroy(&ccdc->ioctl_lock); } diff --git a/drivers/media/video/omap3isp/ispccp2.c b/drivers/media/video/omap3isp/ispccp2.c index ec9e395f333..904ca8c8b17 100644 --- a/drivers/media/video/omap3isp/ispccp2.c +++ b/drivers/media/video/omap3isp/ispccp2.c @@ -243,9 +243,9 @@ static int ccp2_phyif_config(struct isp_ccp2_device *ccp2, val = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL); if (!(val & ISPCCP2_CTRL_MODE)) { - if (pdata->ccp2_mode) + if (pdata->ccp2_mode == ISP_CCP2_MODE_CCP2) dev_warn(isp->dev, "OMAP3 CCP2 bus not available\n"); - if (pdata->phy_layer == ISPCCP2_CTRL_PHY_SEL_STROBE) + if (pdata->phy_layer == ISP_CCP2_PHY_DATA_STROBE) /* Strobe mode requires CCP2 */ return -EIO; } @@ -1032,6 +1032,48 @@ static const struct media_entity_operations ccp2_media_ops = { }; /* + * omap3isp_ccp2_unregister_entities - Unregister media entities: subdev + * @ccp2: Pointer to ISP CCP2 device + */ +void omap3isp_ccp2_unregister_entities(struct isp_ccp2_device *ccp2) +{ + v4l2_device_unregister_subdev(&ccp2->subdev); + omap3isp_video_unregister(&ccp2->video_in); +} + +/* + * omap3isp_ccp2_register_entities - Register the subdev media entity + * @ccp2: Pointer to ISP CCP2 device + * @vdev: Pointer to v4l device + * return negative error code or zero on success + */ + +int omap3isp_ccp2_register_entities(struct isp_ccp2_device *ccp2, + struct v4l2_device *vdev) +{ + int ret; + + /* Register the subdev and video nodes. */ + ret = v4l2_device_register_subdev(vdev, &ccp2->subdev); + if (ret < 0) + goto error; + + ret = omap3isp_video_register(&ccp2->video_in, vdev); + if (ret < 0) + goto error; + + return 0; + +error: + omap3isp_ccp2_unregister_entities(ccp2); + return ret; +} + +/* ----------------------------------------------------------------------------- + * ISP ccp2 initialisation and cleanup + */ + +/* * ccp2_init_entities - Initialize ccp2 subdev and media entity. * @ccp2: Pointer to ISP CCP2 device * return negative error code or zero on success @@ -1083,72 +1125,23 @@ static int ccp2_init_entities(struct isp_ccp2_device *ccp2) ret = omap3isp_video_init(&ccp2->video_in, "CCP2"); if (ret < 0) - return ret; + goto error_video; /* Connect the video node to the ccp2 subdev. */ ret = media_entity_create_link(&ccp2->video_in.video.entity, 0, &ccp2->subdev.entity, CCP2_PAD_SINK, 0); if (ret < 0) - return ret; + goto error_link; return 0; -} -/* - * omap3isp_ccp2_unregister_entities - Unregister media entities: subdev - * @ccp2: Pointer to ISP CCP2 device - */ -void omap3isp_ccp2_unregister_entities(struct isp_ccp2_device *ccp2) -{ +error_link: + omap3isp_video_cleanup(&ccp2->video_in); +error_video: media_entity_cleanup(&ccp2->subdev.entity); - - v4l2_device_unregister_subdev(&ccp2->subdev); - omap3isp_video_unregister(&ccp2->video_in); -} - -/* - * omap3isp_ccp2_register_entities - Register the subdev media entity - * @ccp2: Pointer to ISP CCP2 device - * @vdev: Pointer to v4l device - * return negative error code or zero on success - */ - -int omap3isp_ccp2_register_entities(struct isp_ccp2_device *ccp2, - struct v4l2_device *vdev) -{ - int ret; - - /* Register the subdev and video nodes. */ - ret = v4l2_device_register_subdev(vdev, &ccp2->subdev); - if (ret < 0) - goto error; - - ret = omap3isp_video_register(&ccp2->video_in, vdev); - if (ret < 0) - goto error; - - return 0; - -error: - omap3isp_ccp2_unregister_entities(ccp2); return ret; } -/* ----------------------------------------------------------------------------- - * ISP ccp2 initialisation and cleanup - */ - -/* - * omap3isp_ccp2_cleanup - CCP2 un-initialization - * @isp : Pointer to ISP device - */ -void omap3isp_ccp2_cleanup(struct isp_device *isp) -{ - struct isp_ccp2_device *ccp2 = &isp->isp_ccp2; - - regulator_put(ccp2->vdds_csib); -} - /* * omap3isp_ccp2_init - CCP2 initialization. * @isp : Pointer to ISP device @@ -1184,13 +1177,25 @@ int omap3isp_ccp2_init(struct isp_device *isp) } ret = ccp2_init_entities(ccp2); - if (ret < 0) - goto out; + if (ret < 0) { + regulator_put(ccp2->vdds_csib); + return ret; + } ccp2_reset(ccp2); -out: - if (ret) - omap3isp_ccp2_cleanup(isp); + return 0; +} - return ret; +/* + * omap3isp_ccp2_cleanup - CCP2 un-initialization + * @isp : Pointer to ISP device + */ +void omap3isp_ccp2_cleanup(struct isp_device *isp) +{ + struct isp_ccp2_device *ccp2 = &isp->isp_ccp2; + + omap3isp_video_cleanup(&ccp2->video_in); + media_entity_cleanup(&ccp2->subdev.entity); + + regulator_put(ccp2->vdds_csib); } diff --git a/drivers/media/video/omap3isp/ispcsi2.c b/drivers/media/video/omap3isp/ispcsi2.c index 69161a682b3..0c5f1cb9d99 100644 --- a/drivers/media/video/omap3isp/ispcsi2.c +++ b/drivers/media/video/omap3isp/ispcsi2.c @@ -1187,6 +1187,37 @@ static const struct media_entity_operations csi2_media_ops = { .link_setup = csi2_link_setup, }; +void omap3isp_csi2_unregister_entities(struct isp_csi2_device *csi2) +{ + v4l2_device_unregister_subdev(&csi2->subdev); + omap3isp_video_unregister(&csi2->video_out); +} + +int omap3isp_csi2_register_entities(struct isp_csi2_device *csi2, + struct v4l2_device *vdev) +{ + int ret; + + /* Register the subdev and video nodes. */ + ret = v4l2_device_register_subdev(vdev, &csi2->subdev); + if (ret < 0) + goto error; + + ret = omap3isp_video_register(&csi2->video_out, vdev); + if (ret < 0) + goto error; + + return 0; + +error: + omap3isp_csi2_unregister_entities(csi2); + return ret; +} + +/* ----------------------------------------------------------------------------- + * ISP CSI2 initialisation and cleanup + */ + /* * csi2_init_entities - Initialize subdev and media entity. * @csi2: Pointer to csi2 structure. @@ -1228,57 +1259,23 @@ static int csi2_init_entities(struct isp_csi2_device *csi2) ret = omap3isp_video_init(&csi2->video_out, "CSI2a"); if (ret < 0) - return ret; + goto error_video; /* Connect the CSI2 subdev to the video node. */ ret = media_entity_create_link(&csi2->subdev.entity, CSI2_PAD_SOURCE, &csi2->video_out.video.entity, 0, 0); if (ret < 0) - return ret; + goto error_link; return 0; -} -void omap3isp_csi2_unregister_entities(struct isp_csi2_device *csi2) -{ +error_link: + omap3isp_video_cleanup(&csi2->video_out); +error_video: media_entity_cleanup(&csi2->subdev.entity); - - v4l2_device_unregister_subdev(&csi2->subdev); - omap3isp_video_unregister(&csi2->video_out); -} - -int omap3isp_csi2_register_entities(struct isp_csi2_device *csi2, - struct v4l2_device *vdev) -{ - int ret; - - /* Register the subdev and video nodes. */ - ret = v4l2_device_register_subdev(vdev, &csi2->subdev); - if (ret < 0) - goto error; - - ret = omap3isp_video_register(&csi2->video_out, vdev); - if (ret < 0) - goto error; - - return 0; - -error: - omap3isp_csi2_unregister_entities(csi2); return ret; } -/* ----------------------------------------------------------------------------- - * ISP CSI2 initialisation and cleanup - */ - -/* - * omap3isp_csi2_cleanup - Routine for module driver cleanup - */ -void omap3isp_csi2_cleanup(struct isp_device *isp) -{ -} - /* * omap3isp_csi2_init - Routine for module driver init */ @@ -1298,7 +1295,7 @@ int omap3isp_csi2_init(struct isp_device *isp) ret = csi2_init_entities(csi2a); if (ret < 0) - goto fail; + return ret; if (isp->revision == ISP_REVISION_15_0) { csi2c->isp = isp; @@ -1311,7 +1308,15 @@ int omap3isp_csi2_init(struct isp_device *isp) } return 0; -fail: - omap3isp_csi2_cleanup(isp); - return ret; +} + +/* + * omap3isp_csi2_cleanup - Routine for module driver cleanup + */ +void omap3isp_csi2_cleanup(struct isp_device *isp) +{ + struct isp_csi2_device *csi2a = &isp->isp_csi2a; + + omap3isp_video_cleanup(&csi2a->video_out); + media_entity_cleanup(&csi2a->subdev.entity); } diff --git a/drivers/media/video/omap3isp/isph3a_aewb.c b/drivers/media/video/omap3isp/isph3a_aewb.c index 8068cefd8d8..a3c76bf1817 100644 --- a/drivers/media/video/omap3isp/isph3a_aewb.c +++ b/drivers/media/video/omap3isp/isph3a_aewb.c @@ -370,5 +370,5 @@ void omap3isp_h3a_aewb_cleanup(struct isp_device *isp) { kfree(isp->isp_aewb.priv); kfree(isp->isp_aewb.recover_priv); - omap3isp_stat_free(&isp->isp_aewb); + omap3isp_stat_cleanup(&isp->isp_aewb); } diff --git a/drivers/media/video/omap3isp/isph3a_af.c b/drivers/media/video/omap3isp/isph3a_af.c index ba54d0acdec..58e0bc41489 100644 --- a/drivers/media/video/omap3isp/isph3a_af.c +++ b/drivers/media/video/omap3isp/isph3a_af.c @@ -425,5 +425,5 @@ void omap3isp_h3a_af_cleanup(struct isp_device *isp) { kfree(isp->isp_af.priv); kfree(isp->isp_af.recover_priv); - omap3isp_stat_free(&isp->isp_af); + omap3isp_stat_cleanup(&isp->isp_af); } diff --git a/drivers/media/video/omap3isp/isphist.c b/drivers/media/video/omap3isp/isphist.c index 1743856b30d..1163907bcdd 100644 --- a/drivers/media/video/omap3isp/isphist.c +++ b/drivers/media/video/omap3isp/isphist.c @@ -516,5 +516,5 @@ void omap3isp_hist_cleanup(struct isp_device *isp) if (HIST_USING_DMA(&isp->isp_hist)) omap_free_dma(isp->isp_hist.dma_ch); kfree(isp->isp_hist.priv); - omap3isp_stat_free(&isp->isp_hist); + omap3isp_stat_cleanup(&isp->isp_hist); } diff --git a/drivers/media/video/omap3isp/isppreview.c b/drivers/media/video/omap3isp/isppreview.c index aba537af87e..ccb876fe023 100644 --- a/drivers/media/video/omap3isp/isppreview.c +++ b/drivers/media/video/omap3isp/isppreview.c @@ -76,9 +76,51 @@ static struct omap3isp_prev_csc flr_prev_csc = { #define DEF_DETECT_CORRECT_VAL 0xe -#define PREV_MIN_WIDTH 64 -#define PREV_MIN_HEIGHT 8 -#define PREV_MAX_HEIGHT 16384 +/* + * Margins and image size limits. + * + * The preview engine crops several rows and columns internally depending on + * which filters are enabled. To avoid format changes when the filters are + * enabled or disabled (which would prevent them from being turned on or off + * during streaming), the driver assumes all the filters are enabled when + * computing sink crop and source format limits. + * + * If a filter is disabled, additional cropping is automatically added at the + * preview engine input by the driver to avoid overflow at line and frame end. + * This is completely transparent for applications. + * + * Median filter 4 pixels + * Noise filter, + * Faulty pixels correction 4 pixels, 4 lines + * CFA filter 4 pixels, 4 lines in Bayer mode + * 2 lines in other modes + * Color suppression 2 pixels + * or luma enhancement + * ------------------------------------------------------------- + * Maximum total 14 pixels, 8 lines + * + * The color suppression and luma enhancement filters are applied after bayer to + * YUV conversion. They thus can crop one pixel on the left and one pixel on the + * right side of the image without changing the color pattern. When both those + * filters are disabled, the driver must crop the two pixels on the same side of + * the image to avoid changing the bayer pattern. The left margin is thus set to + * 8 pixels and the right margin to 6 pixels. + */ + +#define PREV_MARGIN_LEFT 8 +#define PREV_MARGIN_RIGHT 6 +#define PREV_MARGIN_TOP 4 +#define PREV_MARGIN_BOTTOM 4 + +#define PREV_MIN_IN_WIDTH 64 +#define PREV_MIN_IN_HEIGHT 8 +#define PREV_MAX_IN_HEIGHT 16384 + +#define PREV_MIN_OUT_WIDTH 0 +#define PREV_MIN_OUT_HEIGHT 0 +#define PREV_MAX_OUT_WIDTH 1280 +#define PREV_MAX_OUT_WIDTH_ES2 3300 +#define PREV_MAX_OUT_WIDTH_3630 4096 /* * Coeficient Tables for the submodules in Preview. @@ -979,52 +1021,36 @@ static void preview_config_averager(struct isp_prev_device *prev, u8 average) * enabled when reporting source pad formats to userspace. If this assumption is * not true, rows and columns must be manually cropped at the preview engine * input to avoid overflows at the end of lines and frames. + * + * See the explanation at the PREV_MARGIN_* definitions for more details. */ static void preview_config_input_size(struct isp_prev_device *prev) { struct isp_device *isp = to_isp_device(prev); struct prev_params *params = &prev->params; - struct v4l2_mbus_framefmt *format = &prev->formats[PREV_PAD_SINK]; - unsigned int sph = 0; - unsigned int eph = format->width - 1; - unsigned int slv = 0; - unsigned int elv = format->height - 1; - - if (prev->input == PREVIEW_INPUT_CCDC) { - sph += 2; - eph -= 2; + unsigned int sph = prev->crop.left; + unsigned int eph = prev->crop.left + prev->crop.width - 1; + unsigned int slv = prev->crop.top; + unsigned int elv = prev->crop.top + prev->crop.height - 1; + + if (params->features & PREV_CFA) { + sph -= 2; + eph += 2; + slv -= 2; + elv += 2; } - - /* - * Median filter 4 pixels - * Noise filter 4 pixels, 4 lines - * or faulty pixels correction - * CFA filter 4 pixels, 4 lines in Bayer mode - * 2 lines in other modes - * Color suppression 2 pixels - * or luma enhancement - * ------------------------------------------------------------- - * Maximum total 14 pixels, 8 lines - */ - - if (!(params->features & PREV_CFA)) { - sph += 2; - eph -= 2; - slv += 2; - elv -= 2; + if (params->features & (PREV_DEFECT_COR | PREV_NOISE_FILTER)) { + sph -= 2; + eph += 2; + slv -= 2; + elv += 2; } - if (!(params->features & (PREV_DEFECT_COR | PREV_NOISE_FILTER))) { - sph += 2; - eph -= 2; - slv += 2; - elv -= 2; + if (params->features & PREV_HORZ_MEDIAN_FILTER) { + sph -= 2; + eph += 2; } - if (!(params->features & PREV_HORZ_MEDIAN_FILTER)) { - sph += 2; - eph -= 2; - } - if (!(params->features & (PREV_CHROMA_SUPPRESS | PREV_LUMA_ENHANCE))) - sph += 2; + if (params->features & (PREV_CHROMA_SUPPRESS | PREV_LUMA_ENHANCE)) + sph -= 2; isp_reg_writel(isp, (sph << ISPPRV_HORZ_INFO_SPH_SHIFT) | eph, OMAP3_ISP_IOMEM_PREV, ISPPRV_HORZ_INFO); @@ -1228,7 +1254,6 @@ static void preview_init_params(struct isp_prev_device *prev) /* Init values */ params->contrast = ISPPRV_CONTRAST_DEF * ISPPRV_CONTRAST_UNITS; params->brightness = ISPPRV_BRIGHT_DEF * ISPPRV_BRIGHT_UNITS; - params->average = NO_AVE; params->cfa.format = OMAP3ISP_CFAFMT_BAYER; memcpy(params->cfa.table, cfa_coef_table, sizeof(params->cfa.table)); @@ -1281,14 +1306,14 @@ static unsigned int preview_max_out_width(struct isp_prev_device *prev) switch (isp->revision) { case ISP_REVISION_1_0: - return ISPPRV_MAXOUTPUT_WIDTH; + return PREV_MAX_OUT_WIDTH; case ISP_REVISION_2_0: default: - return ISPPRV_MAXOUTPUT_WIDTH_ES2; + return PREV_MAX_OUT_WIDTH_ES2; case ISP_REVISION_15_0: - return ISPPRV_MAXOUTPUT_WIDTH_3630; + return PREV_MAX_OUT_WIDTH_3630; } } @@ -1296,8 +1321,6 @@ static void preview_configure(struct isp_prev_device *prev) { struct isp_device *isp = to_isp_device(prev); struct v4l2_mbus_framefmt *format; - unsigned int max_out_width; - unsigned int format_avg; preview_setup_hw(prev); @@ -1335,10 +1358,7 @@ static void preview_configure(struct isp_prev_device *prev) preview_config_outlineoffset(prev, ALIGN(format->width, 0x10) * 2); - max_out_width = preview_max_out_width(prev); - - format_avg = fls(DIV_ROUND_UP(format->width, max_out_width) - 1); - preview_config_averager(prev, format_avg); + preview_config_averager(prev, 0); preview_config_ycpos(prev, format->code); } @@ -1597,6 +1617,16 @@ __preview_get_format(struct isp_prev_device *prev, struct v4l2_subdev_fh *fh, return &prev->formats[pad]; } +static struct v4l2_rect * +__preview_get_crop(struct isp_prev_device *prev, struct v4l2_subdev_fh *fh, + enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_crop(fh, PREV_PAD_SINK); + else + return &prev->crop; +} + /* previewer format descriptions */ static const unsigned int preview_input_fmts[] = { V4L2_MBUS_FMT_SGRBG10_1X10, @@ -1611,24 +1641,25 @@ static const unsigned int preview_output_fmts[] = { }; /* - * preview_try_format - Handle try format by pad subdev method - * @prev: ISP preview device - * @fh : V4L2 subdev file handle - * @pad: pad num - * @fmt: pointer to v4l2 format structure + * preview_try_format - Validate a format + * @prev: ISP preview engine + * @fh: V4L2 subdev file handle + * @pad: pad number + * @fmt: format to be validated + * @which: try/active format selector + * + * Validate and adjust the given format for the given pad based on the preview + * engine limits and the format and crop rectangles on other pads. */ static void preview_try_format(struct isp_prev_device *prev, struct v4l2_subdev_fh *fh, unsigned int pad, struct v4l2_mbus_framefmt *fmt, enum v4l2_subdev_format_whence which) { - struct v4l2_mbus_framefmt *format; - unsigned int max_out_width; enum v4l2_mbus_pixelcode pixelcode; + struct v4l2_rect *crop; unsigned int i; - max_out_width = preview_max_out_width(prev); - switch (pad) { case PREV_PAD_SINK: /* When reading data from the CCDC, the input size has already @@ -1641,10 +1672,11 @@ static void preview_try_format(struct isp_prev_device *prev, * filter array interpolation. */ if (prev->input == PREVIEW_INPUT_MEMORY) { - fmt->width = clamp_t(u32, fmt->width, PREV_MIN_WIDTH, - max_out_width * 8); - fmt->height = clamp_t(u32, fmt->height, PREV_MIN_HEIGHT, - PREV_MAX_HEIGHT); + fmt->width = clamp_t(u32, fmt->width, PREV_MIN_IN_WIDTH, + preview_max_out_width(prev)); + fmt->height = clamp_t(u32, fmt->height, + PREV_MIN_IN_HEIGHT, + PREV_MAX_IN_HEIGHT); } fmt->colorspace = V4L2_COLORSPACE_SRGB; @@ -1661,15 +1693,8 @@ static void preview_try_format(struct isp_prev_device *prev, case PREV_PAD_SOURCE: pixelcode = fmt->code; - format = __preview_get_format(prev, fh, PREV_PAD_SINK, which); - memcpy(fmt, format, sizeof(*fmt)); + *fmt = *__preview_get_format(prev, fh, PREV_PAD_SINK, which); - /* The preview module output size is configurable through the - * input interface (horizontal and vertical cropping) and the - * averager (horizontal scaling by 1/1, 1/2, 1/4 or 1/8). In - * spite of this, hardcode the output size to the biggest - * possible value for simplicity reasons. - */ switch (pixelcode) { case V4L2_MBUS_FMT_YUYV8_1X16: case V4L2_MBUS_FMT_UYVY8_1X16: @@ -1681,31 +1706,14 @@ static void preview_try_format(struct isp_prev_device *prev, break; } - /* The TRM states (12.1.4.7.1.2) that 2 pixels must be cropped - * from the left and right sides when the input source is the - * CCDC. This seems not to be needed in practice, investigation - * is required. - */ - if (prev->input == PREVIEW_INPUT_CCDC) - fmt->width -= 4; - - /* The preview module can output a maximum of 3312 pixels - * horizontally due to fixed memory-line sizes. Compute the - * horizontal averaging factor accordingly. Note that the limit - * applies to the noise filter and CFA interpolation blocks, so - * it doesn't take cropping by further blocks into account. - * - * ES 1.0 hardware revision is limited to 1280 pixels - * horizontally. - */ - fmt->width >>= fls(DIV_ROUND_UP(fmt->width, max_out_width) - 1); - - /* Assume that all blocks are enabled and crop pixels and lines - * accordingly. See preview_config_input_size() for more - * information. + /* The preview module output size is configurable through the + * averager (horizontal scaling by 1/1, 1/2, 1/4 or 1/8). This + * is not supported yet, hardcode the output size to the crop + * rectangle size. */ - fmt->width -= 14; - fmt->height -= 8; + crop = __preview_get_crop(prev, fh, which); + fmt->width = crop->width; + fmt->height = crop->height; fmt->colorspace = V4L2_COLORSPACE_JPEG; break; @@ -1715,6 +1723,49 @@ static void preview_try_format(struct isp_prev_device *prev, } /* + * preview_try_crop - Validate a crop rectangle + * @prev: ISP preview engine + * @sink: format on the sink pad + * @crop: crop rectangle to be validated + * + * The preview engine crops lines and columns for its internal operation, + * depending on which filters are enabled. Enforce minimum crop margins to + * handle that transparently for userspace. + * + * See the explanation at the PREV_MARGIN_* definitions for more details. + */ +static void preview_try_crop(struct isp_prev_device *prev, + const struct v4l2_mbus_framefmt *sink, + struct v4l2_rect *crop) +{ + unsigned int left = PREV_MARGIN_LEFT; + unsigned int right = sink->width - PREV_MARGIN_RIGHT; + unsigned int top = PREV_MARGIN_TOP; + unsigned int bottom = sink->height - PREV_MARGIN_BOTTOM; + + /* When processing data on-the-fly from the CCDC, at least 2 pixels must + * be cropped from the left and right sides of the image. As we don't + * know which filters will be enabled, increase the left and right + * margins by two. + */ + if (prev->input == PREVIEW_INPUT_CCDC) { + left += 2; + right -= 2; + } + + /* Restrict left/top to even values to keep the Bayer pattern. */ + crop->left &= ~1; + crop->top &= ~1; + + crop->left = clamp_t(u32, crop->left, left, right - PREV_MIN_OUT_WIDTH); + crop->top = clamp_t(u32, crop->top, top, bottom - PREV_MIN_OUT_HEIGHT); + crop->width = clamp_t(u32, crop->width, PREV_MIN_OUT_WIDTH, + right - crop->left); + crop->height = clamp_t(u32, crop->height, PREV_MIN_OUT_HEIGHT, + bottom - crop->top); +} + +/* * preview_enum_mbus_code - Handle pixel format enumeration * @sd : pointer to v4l2 subdev structure * @fh : V4L2 subdev file handle @@ -1776,6 +1827,60 @@ static int preview_enum_frame_size(struct v4l2_subdev *sd, } /* + * preview_get_crop - Retrieve the crop rectangle on a pad + * @sd: ISP preview V4L2 subdevice + * @fh: V4L2 subdev file handle + * @crop: crop rectangle + * + * Return 0 on success or a negative error code otherwise. + */ +static int preview_get_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_crop *crop) +{ + struct isp_prev_device *prev = v4l2_get_subdevdata(sd); + + /* Cropping is only supported on the sink pad. */ + if (crop->pad != PREV_PAD_SINK) + return -EINVAL; + + crop->rect = *__preview_get_crop(prev, fh, crop->which); + return 0; +} + +/* + * preview_set_crop - Retrieve the crop rectangle on a pad + * @sd: ISP preview V4L2 subdevice + * @fh: V4L2 subdev file handle + * @crop: crop rectangle + * + * Return 0 on success or a negative error code otherwise. + */ +static int preview_set_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_crop *crop) +{ + struct isp_prev_device *prev = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + /* Cropping is only supported on the sink pad. */ + if (crop->pad != PREV_PAD_SINK) + return -EINVAL; + + /* The crop rectangle can't be changed while streaming. */ + if (prev->state != ISP_PIPELINE_STREAM_STOPPED) + return -EBUSY; + + format = __preview_get_format(prev, fh, PREV_PAD_SINK, crop->which); + preview_try_crop(prev, format, &crop->rect); + *__preview_get_crop(prev, fh, crop->which) = crop->rect; + + /* Update the source format. */ + format = __preview_get_format(prev, fh, PREV_PAD_SOURCE, crop->which); + preview_try_format(prev, fh, PREV_PAD_SOURCE, format, crop->which); + + return 0; +} + +/* * preview_get_format - Handle get format by pads subdev method * @sd : pointer to v4l2 subdev structure * @fh : V4L2 subdev file handle @@ -1808,6 +1913,7 @@ static int preview_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, { struct isp_prev_device *prev = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; + struct v4l2_rect *crop; format = __preview_get_format(prev, fh, fmt->pad, fmt->which); if (format == NULL) @@ -1818,9 +1924,18 @@ static int preview_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, /* Propagate the format from sink to source */ if (fmt->pad == PREV_PAD_SINK) { + /* Reset the crop rectangle. */ + crop = __preview_get_crop(prev, fh, fmt->which); + crop->left = 0; + crop->top = 0; + crop->width = fmt->format.width; + crop->height = fmt->format.height; + + preview_try_crop(prev, &fmt->format, crop); + + /* Update the source format. */ format = __preview_get_format(prev, fh, PREV_PAD_SOURCE, fmt->which); - *format = fmt->format; preview_try_format(prev, fh, PREV_PAD_SOURCE, format, fmt->which); } @@ -1869,6 +1984,8 @@ static const struct v4l2_subdev_pad_ops preview_v4l2_pad_ops = { .enum_frame_size = preview_enum_frame_size, .get_fmt = preview_get_format, .set_fmt = preview_set_format, + .get_crop = preview_get_crop, + .set_crop = preview_set_crop, }; /* subdev operations */ @@ -1966,8 +2083,44 @@ static const struct media_entity_operations preview_media_ops = { .link_setup = preview_link_setup, }; +void omap3isp_preview_unregister_entities(struct isp_prev_device *prev) +{ + v4l2_device_unregister_subdev(&prev->subdev); + omap3isp_video_unregister(&prev->video_in); + omap3isp_video_unregister(&prev->video_out); +} + +int omap3isp_preview_register_entities(struct isp_prev_device *prev, + struct v4l2_device *vdev) +{ + int ret; + + /* Register the subdev and video nodes. */ + ret = v4l2_device_register_subdev(vdev, &prev->subdev); + if (ret < 0) + goto error; + + ret = omap3isp_video_register(&prev->video_in, vdev); + if (ret < 0) + goto error; + + ret = omap3isp_video_register(&prev->video_out, vdev); + if (ret < 0) + goto error; + + return 0; + +error: + omap3isp_preview_unregister_entities(prev); + return ret; +} + +/* ----------------------------------------------------------------------------- + * ISP previewer initialisation and cleanup + */ + /* - * review_init_entities - Initialize subdev and media entity. + * preview_init_entities - Initialize subdev and media entity. * @prev : Pointer to preview structure * return -ENOMEM or zero on success */ @@ -2024,69 +2177,34 @@ static int preview_init_entities(struct isp_prev_device *prev) ret = omap3isp_video_init(&prev->video_in, "preview"); if (ret < 0) - return ret; + goto error_video_in; ret = omap3isp_video_init(&prev->video_out, "preview"); if (ret < 0) - return ret; + goto error_video_out; /* Connect the video nodes to the previewer subdev. */ ret = media_entity_create_link(&prev->video_in.video.entity, 0, &prev->subdev.entity, PREV_PAD_SINK, 0); if (ret < 0) - return ret; + goto error_link; ret = media_entity_create_link(&prev->subdev.entity, PREV_PAD_SOURCE, &prev->video_out.video.entity, 0, 0); if (ret < 0) - return ret; + goto error_link; return 0; -} -void omap3isp_preview_unregister_entities(struct isp_prev_device *prev) -{ +error_link: + omap3isp_video_cleanup(&prev->video_out); +error_video_out: + omap3isp_video_cleanup(&prev->video_in); +error_video_in: media_entity_cleanup(&prev->subdev.entity); - - v4l2_device_unregister_subdev(&prev->subdev); - v4l2_ctrl_handler_free(&prev->ctrls); - omap3isp_video_unregister(&prev->video_in); - omap3isp_video_unregister(&prev->video_out); -} - -int omap3isp_preview_register_entities(struct isp_prev_device *prev, - struct v4l2_device *vdev) -{ - int ret; - - /* Register the subdev and video nodes. */ - ret = v4l2_device_register_subdev(vdev, &prev->subdev); - if (ret < 0) - goto error; - - ret = omap3isp_video_register(&prev->video_in, vdev); - if (ret < 0) - goto error; - - ret = omap3isp_video_register(&prev->video_out, vdev); - if (ret < 0) - goto error; - - return 0; - -error: - omap3isp_preview_unregister_entities(prev); return ret; } -/* ----------------------------------------------------------------------------- - * ISP previewer initialisation and cleanup - */ - -void omap3isp_preview_cleanup(struct isp_device *isp) -{ -} - /* * isp_preview_init - Previewer initialization. * @dev : Pointer to ISP device @@ -2095,19 +2213,20 @@ void omap3isp_preview_cleanup(struct isp_device *isp) int omap3isp_preview_init(struct isp_device *isp) { struct isp_prev_device *prev = &isp->isp_prev; - int ret; spin_lock_init(&prev->lock); init_waitqueue_head(&prev->wait); preview_init_params(prev); - ret = preview_init_entities(prev); - if (ret < 0) - goto out; + return preview_init_entities(prev); +} -out: - if (ret) - omap3isp_preview_cleanup(isp); +void omap3isp_preview_cleanup(struct isp_device *isp) +{ + struct isp_prev_device *prev = &isp->isp_prev; - return ret; + v4l2_ctrl_handler_free(&prev->ctrls); + omap3isp_video_cleanup(&prev->video_in); + omap3isp_video_cleanup(&prev->video_out); + media_entity_cleanup(&prev->subdev.entity); } diff --git a/drivers/media/video/omap3isp/isppreview.h b/drivers/media/video/omap3isp/isppreview.h index fa943bd05c7..f54e775c2df 100644 --- a/drivers/media/video/omap3isp/isppreview.h +++ b/drivers/media/video/omap3isp/isppreview.h @@ -45,11 +45,6 @@ #define ISPPRV_CONTRAST_HIGH 0xFF #define ISPPRV_CONTRAST_UNITS 0x1 -#define NO_AVE 0x0 -#define AVE_2_PIX 0x1 -#define AVE_4_PIX 0x2 -#define AVE_8_PIX 0x3 - /* Features list */ #define PREV_LUMA_ENHANCE OMAP3ISP_PREV_LUMAENH #define PREV_INVERSE_ALAW OMAP3ISP_PREV_INVALAW @@ -106,7 +101,6 @@ enum preview_ycpos_mode { * @rgb2ycbcr: RGB to ycbcr parameters. * @hmed: Horizontal median filter. * @yclimit: YC limits parameters. - * @average: Downsampling rate for averager. * @contrast: Contrast. * @brightness: Brightness. */ @@ -124,7 +118,6 @@ struct prev_params { struct omap3isp_prev_csc rgb2ycbcr; struct omap3isp_prev_hmed hmed; struct omap3isp_prev_yclimit yclimit; - u8 average; u8 contrast; u8 brightness; }; @@ -159,6 +152,7 @@ struct isptables_update { * @subdev: V4L2 subdevice * @pads: Media entity pads * @formats: Active formats at the subdev pad + * @crop: Active crop rectangle * @input: Module currently connected to the input pad * @output: Bitmask of the active output * @video_in: Input video entity @@ -177,6 +171,7 @@ struct isp_prev_device { struct v4l2_subdev subdev; struct media_pad pads[PREV_PADS_NUM]; struct v4l2_mbus_framefmt formats[PREV_PADS_NUM]; + struct v4l2_rect crop; struct v4l2_ctrl_handler ctrls; diff --git a/drivers/media/video/omap3isp/ispqueue.c b/drivers/media/video/omap3isp/ispqueue.c index 9c317148205..9bebb1e57aa 100644 --- a/drivers/media/video/omap3isp/ispqueue.c +++ b/drivers/media/video/omap3isp/ispqueue.c @@ -868,6 +868,10 @@ int omap3isp_video_queue_qbuf(struct isp_video_queue *queue, goto done; if (vbuf->memory == V4L2_MEMORY_USERPTR && + vbuf->length < buf->vbuf.length) + goto done; + + if (vbuf->memory == V4L2_MEMORY_USERPTR && vbuf->m.userptr != buf->vbuf.m.userptr) { isp_video_buffer_cleanup(buf); buf->vbuf.m.userptr = vbuf->m.userptr; diff --git a/drivers/media/video/omap3isp/ispreg.h b/drivers/media/video/omap3isp/ispreg.h index 69f6af6f6b9..084ea77d65a 100644 --- a/drivers/media/video/omap3isp/ispreg.h +++ b/drivers/media/video/omap3isp/ispreg.h @@ -402,9 +402,6 @@ #define ISPPRV_YENH_TABLE_ADDR 0x1000 #define ISPPRV_CFA_TABLE_ADDR 0x1400 -#define ISPPRV_MAXOUTPUT_WIDTH 1280 -#define ISPPRV_MAXOUTPUT_WIDTH_ES2 3300 -#define ISPPRV_MAXOUTPUT_WIDTH_3630 4096 #define ISPRSZ_MIN_OUTPUT 64 #define ISPRSZ_MAX_OUTPUT 3312 diff --git a/drivers/media/video/omap3isp/ispresizer.c b/drivers/media/video/omap3isp/ispresizer.c index 0bb0f8cd36f..50e593bfcfa 100644 --- a/drivers/media/video/omap3isp/ispresizer.c +++ b/drivers/media/video/omap3isp/ispresizer.c @@ -1608,6 +1608,42 @@ static const struct media_entity_operations resizer_media_ops = { .link_setup = resizer_link_setup, }; +void omap3isp_resizer_unregister_entities(struct isp_res_device *res) +{ + v4l2_device_unregister_subdev(&res->subdev); + omap3isp_video_unregister(&res->video_in); + omap3isp_video_unregister(&res->video_out); +} + +int omap3isp_resizer_register_entities(struct isp_res_device *res, + struct v4l2_device *vdev) +{ + int ret; + + /* Register the subdev and video nodes. */ + ret = v4l2_device_register_subdev(vdev, &res->subdev); + if (ret < 0) + goto error; + + ret = omap3isp_video_register(&res->video_in, vdev); + if (ret < 0) + goto error; + + ret = omap3isp_video_register(&res->video_out, vdev); + if (ret < 0) + goto error; + + return 0; + +error: + omap3isp_resizer_unregister_entities(res); + return ret; +} + +/* ----------------------------------------------------------------------------- + * ISP resizer initialization and cleanup + */ + /* * resizer_init_entities - Initialize resizer subdev and media entity. * @res : Pointer to resizer device structure @@ -1652,68 +1688,34 @@ static int resizer_init_entities(struct isp_res_device *res) ret = omap3isp_video_init(&res->video_in, "resizer"); if (ret < 0) - return ret; + goto error_video_in; ret = omap3isp_video_init(&res->video_out, "resizer"); if (ret < 0) - return ret; + goto error_video_out; /* Connect the video nodes to the resizer subdev. */ ret = media_entity_create_link(&res->video_in.video.entity, 0, &res->subdev.entity, RESZ_PAD_SINK, 0); if (ret < 0) - return ret; + goto error_link; ret = media_entity_create_link(&res->subdev.entity, RESZ_PAD_SOURCE, &res->video_out.video.entity, 0, 0); if (ret < 0) - return ret; + goto error_link; return 0; -} -void omap3isp_resizer_unregister_entities(struct isp_res_device *res) -{ +error_link: + omap3isp_video_cleanup(&res->video_out); +error_video_out: + omap3isp_video_cleanup(&res->video_in); +error_video_in: media_entity_cleanup(&res->subdev.entity); - - v4l2_device_unregister_subdev(&res->subdev); - omap3isp_video_unregister(&res->video_in); - omap3isp_video_unregister(&res->video_out); -} - -int omap3isp_resizer_register_entities(struct isp_res_device *res, - struct v4l2_device *vdev) -{ - int ret; - - /* Register the subdev and video nodes. */ - ret = v4l2_device_register_subdev(vdev, &res->subdev); - if (ret < 0) - goto error; - - ret = omap3isp_video_register(&res->video_in, vdev); - if (ret < 0) - goto error; - - ret = omap3isp_video_register(&res->video_out, vdev); - if (ret < 0) - goto error; - - return 0; - -error: - omap3isp_resizer_unregister_entities(res); return ret; } -/* ----------------------------------------------------------------------------- - * ISP resizer initialization and cleanup - */ - -void omap3isp_resizer_cleanup(struct isp_device *isp) -{ -} - /* * isp_resizer_init - Resizer initialization. * @isp : Pointer to ISP device @@ -1722,17 +1724,17 @@ void omap3isp_resizer_cleanup(struct isp_device *isp) int omap3isp_resizer_init(struct isp_device *isp) { struct isp_res_device *res = &isp->isp_res; - int ret; init_waitqueue_head(&res->wait); atomic_set(&res->stopping, 0); - ret = resizer_init_entities(res); - if (ret < 0) - goto out; + return resizer_init_entities(res); +} -out: - if (ret) - omap3isp_resizer_cleanup(isp); +void omap3isp_resizer_cleanup(struct isp_device *isp) +{ + struct isp_res_device *res = &isp->isp_res; - return ret; + omap3isp_video_cleanup(&res->video_in); + omap3isp_video_cleanup(&res->video_out); + media_entity_cleanup(&res->subdev.entity); } diff --git a/drivers/media/video/omap3isp/ispstat.c b/drivers/media/video/omap3isp/ispstat.c index 808065948ac..68d539456c5 100644 --- a/drivers/media/video/omap3isp/ispstat.c +++ b/drivers/media/video/omap3isp/ispstat.c @@ -366,7 +366,8 @@ static void isp_stat_bufs_free(struct ispstat *stat) dma_unmap_sg(isp->dev, buf->iovm->sgt->sgl, buf->iovm->sgt->nents, DMA_FROM_DEVICE); - iommu_vfree(isp->iommu, buf->iommu_addr); + omap_iommu_vfree(isp->domain, isp->iommu, + buf->iommu_addr); } else { if (!buf->virt_addr) continue; @@ -399,8 +400,8 @@ static int isp_stat_bufs_alloc_iommu(struct ispstat *stat, unsigned int size) struct iovm_struct *iovm; WARN_ON(buf->dma_addr); - buf->iommu_addr = iommu_vmalloc(isp->iommu, 0, size, - IOMMU_FLAG); + buf->iommu_addr = omap_iommu_vmalloc(isp->domain, isp->iommu, 0, + size, IOMMU_FLAG); if (IS_ERR((void *)buf->iommu_addr)) { dev_err(stat->isp->dev, "%s: Can't acquire memory for " @@ -409,7 +410,7 @@ static int isp_stat_bufs_alloc_iommu(struct ispstat *stat, unsigned int size) return -ENOMEM; } - iovm = find_iovm_area(isp->iommu, buf->iommu_addr); + iovm = omap_find_iovm_area(isp->iommu, buf->iommu_addr); if (!iovm || !dma_map_sg(isp->dev, iovm->sgt->sgl, iovm->sgt->nents, DMA_FROM_DEVICE)) { @@ -418,7 +419,7 @@ static int isp_stat_bufs_alloc_iommu(struct ispstat *stat, unsigned int size) } buf->iovm = iovm; - buf->virt_addr = da_to_va(stat->isp->iommu, + buf->virt_addr = omap_da_to_va(stat->isp->iommu, (u32)buf->iommu_addr); buf->empty = 1; dev_dbg(stat->isp->dev, "%s: buffer[%d] allocated." @@ -1022,24 +1023,6 @@ void omap3isp_stat_dma_isr(struct ispstat *stat) __stat_isr(stat, 1); } -static int isp_stat_init_entities(struct ispstat *stat, const char *name, - const struct v4l2_subdev_ops *sd_ops) -{ - struct v4l2_subdev *subdev = &stat->subdev; - struct media_entity *me = &subdev->entity; - - v4l2_subdev_init(subdev, sd_ops); - snprintf(subdev->name, V4L2_SUBDEV_NAME_SIZE, "OMAP3 ISP %s", name); - subdev->grp_id = 1 << 16; /* group ID for isp subdevs */ - subdev->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE; - v4l2_set_subdevdata(subdev, stat); - - stat->pad.flags = MEDIA_PAD_FL_SINK; - me->ops = NULL; - - return media_entity_init(me, 1, &stat->pad, 0); -} - int omap3isp_stat_subscribe_event(struct v4l2_subdev *subdev, struct v4l2_fh *fh, struct v4l2_event_subscription *sub) @@ -1061,7 +1044,6 @@ int omap3isp_stat_unsubscribe_event(struct v4l2_subdev *subdev, void omap3isp_stat_unregister_entities(struct ispstat *stat) { - media_entity_cleanup(&stat->subdev.entity); v4l2_device_unregister_subdev(&stat->subdev); } @@ -1071,21 +1053,50 @@ int omap3isp_stat_register_entities(struct ispstat *stat, return v4l2_device_register_subdev(vdev, &stat->subdev); } +static int isp_stat_init_entities(struct ispstat *stat, const char *name, + const struct v4l2_subdev_ops *sd_ops) +{ + struct v4l2_subdev *subdev = &stat->subdev; + struct media_entity *me = &subdev->entity; + + v4l2_subdev_init(subdev, sd_ops); + snprintf(subdev->name, V4L2_SUBDEV_NAME_SIZE, "OMAP3 ISP %s", name); + subdev->grp_id = 1 << 16; /* group ID for isp subdevs */ + subdev->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE; + v4l2_set_subdevdata(subdev, stat); + + stat->pad.flags = MEDIA_PAD_FL_SINK; + me->ops = NULL; + + return media_entity_init(me, 1, &stat->pad, 0); +} + int omap3isp_stat_init(struct ispstat *stat, const char *name, const struct v4l2_subdev_ops *sd_ops) { + int ret; + stat->buf = kcalloc(STAT_MAX_BUFS, sizeof(*stat->buf), GFP_KERNEL); if (!stat->buf) return -ENOMEM; + isp_stat_buf_clear(stat); mutex_init(&stat->ioctl_lock); atomic_set(&stat->buf_err, 0); - return isp_stat_init_entities(stat, name, sd_ops); + ret = isp_stat_init_entities(stat, name, sd_ops); + if (ret < 0) { + mutex_destroy(&stat->ioctl_lock); + kfree(stat->buf); + } + + return ret; } -void omap3isp_stat_free(struct ispstat *stat) +void omap3isp_stat_cleanup(struct ispstat *stat) { + media_entity_cleanup(&stat->subdev.entity); + mutex_destroy(&stat->ioctl_lock); isp_stat_bufs_free(stat); kfree(stat->buf); } diff --git a/drivers/media/video/omap3isp/ispstat.h b/drivers/media/video/omap3isp/ispstat.h index d86da94fa50..9b7c8654dc8 100644 --- a/drivers/media/video/omap3isp/ispstat.h +++ b/drivers/media/video/omap3isp/ispstat.h @@ -144,7 +144,7 @@ int omap3isp_stat_request_statistics(struct ispstat *stat, struct omap3isp_stat_data *data); int omap3isp_stat_init(struct ispstat *stat, const char *name, const struct v4l2_subdev_ops *sd_ops); -void omap3isp_stat_free(struct ispstat *stat); +void omap3isp_stat_cleanup(struct ispstat *stat); int omap3isp_stat_subscribe_event(struct v4l2_subdev *subdev, struct v4l2_fh *fh, struct v4l2_event_subscription *sub); diff --git a/drivers/media/video/omap3isp/ispvideo.c b/drivers/media/video/omap3isp/ispvideo.c index fd965adfd59..d1000723c5a 100644 --- a/drivers/media/video/omap3isp/ispvideo.c +++ b/drivers/media/video/omap3isp/ispvideo.c @@ -278,7 +278,8 @@ isp_video_far_end(struct isp_video *video) * limits reported by every block in the pipeline. * * Return 0 if all formats match, or -EPIPE if at least one link is found with - * different formats on its two ends. + * different formats on its two ends or if the pipeline doesn't start with a + * video source (either a subdev with no input pad, or a non-subdev entity). */ static int isp_video_validate_pipeline(struct isp_pipeline *pipe) { @@ -329,10 +330,15 @@ static int isp_video_validate_pipeline(struct isp_pipeline *pipe) * in the middle of it. */ shifter_link = subdev == &isp->isp_ccdc.subdev; - /* Retrieve the source format */ + /* Retrieve the source format. Return an error if no source + * entity can be found, and stop checking the pipeline if the + * source entity isn't a subdev. + */ pad = media_entity_remote_source(pad); - if (pad == NULL || - media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + if (pad == NULL) + return -EPIPE; + + if (media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) break; subdev = media_entity_to_v4l2_subdev(pad->entity); @@ -446,7 +452,7 @@ ispmmu_vmap(struct isp_device *isp, const struct scatterlist *sglist, int sglen) sgt->nents = sglen; sgt->orig_nents = sglen; - da = iommu_vmap(isp->iommu, 0, sgt, IOMMU_FLAG); + da = omap_iommu_vmap(isp->domain, isp->iommu, 0, sgt, IOMMU_FLAG); if (IS_ERR_VALUE(da)) kfree(sgt); @@ -462,7 +468,7 @@ static void ispmmu_vunmap(struct isp_device *isp, dma_addr_t da) { struct sg_table *sgt; - sgt = iommu_vunmap(isp->iommu, (u32)da); + sgt = omap_iommu_vunmap(isp->domain, isp->iommu, (u32)da); kfree(sgt); } @@ -1050,6 +1056,14 @@ error: if (video->isp->pdata->set_constraints) video->isp->pdata->set_constraints(video->isp, false); media_entity_pipeline_stop(&video->video.entity); + /* The DMA queue must be emptied here, otherwise CCDC interrupts + * that will get triggered the next time the CCDC is powered up + * will try to access buffers that might have been freed but + * still present in the DMA queue. This can easily get triggered + * if the above omap3isp_pipeline_set_stream() call fails on a + * system with a free-running sensor. + */ + INIT_LIST_HEAD(&video->dmaqueue); video->queue = NULL; } @@ -1311,6 +1325,13 @@ int omap3isp_video_init(struct isp_video *video, const char *name) return 0; } +void omap3isp_video_cleanup(struct isp_video *video) +{ + media_entity_cleanup(&video->video.entity); + mutex_destroy(&video->stream_lock); + mutex_destroy(&video->mutex); +} + int omap3isp_video_register(struct isp_video *video, struct v4l2_device *vdev) { int ret; @@ -1327,8 +1348,6 @@ int omap3isp_video_register(struct isp_video *video, struct v4l2_device *vdev) void omap3isp_video_unregister(struct isp_video *video) { - if (video_is_registered(&video->video)) { - media_entity_cleanup(&video->video.entity); + if (video_is_registered(&video->video)) video_unregister_device(&video->video); - } } diff --git a/drivers/media/video/omap3isp/ispvideo.h b/drivers/media/video/omap3isp/ispvideo.h index 53160aa24e6..08cbfa144e6 100644 --- a/drivers/media/video/omap3isp/ispvideo.h +++ b/drivers/media/video/omap3isp/ispvideo.h @@ -190,6 +190,7 @@ struct isp_video_fh { container_of(q, struct isp_video_fh, queue) int omap3isp_video_init(struct isp_video *video, const char *name); +void omap3isp_video_cleanup(struct isp_video *video); int omap3isp_video_register(struct isp_video *video, struct v4l2_device *vdev); void omap3isp_video_unregister(struct isp_video *video); diff --git a/drivers/media/video/ov2640.c b/drivers/media/video/ov2640.c index 9ce2fa037b9..b5247cb64fd 100644 --- a/drivers/media/video/ov2640.c +++ b/drivers/media/video/ov2640.c @@ -18,11 +18,13 @@ #include <linux/i2c.h> #include <linux/slab.h> #include <linux/delay.h> +#include <linux/v4l2-mediabus.h> #include <linux/videodev2.h> + +#include <media/soc_camera.h> #include <media/v4l2-chip-ident.h> #include <media/v4l2-subdev.h> -#include <media/soc_camera.h> -#include <media/soc_mediabus.h> +#include <media/v4l2-ctrls.h> #define VAL_SET(x, mask, rshift, lshift) \ ((((x) >> rshift) & mask) << lshift) @@ -299,12 +301,10 @@ struct ov2640_win_size { struct ov2640_priv { struct v4l2_subdev subdev; - struct ov2640_camera_info *info; + struct v4l2_ctrl_handler hdl; enum v4l2_mbus_pixelcode cfmt_code; const struct ov2640_win_size *win; int model; - u16 flag_vflip:1; - u16 flag_hflip:1; }; /* @@ -610,29 +610,6 @@ static enum v4l2_mbus_pixelcode ov2640_codes[] = { }; /* - * Supported controls - */ -static const struct v4l2_queryctrl ov2640_controls[] = { - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Flip Vertically", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Flip Horizontally", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, -}; - -/* * General functions */ static struct ov2640_priv *to_ov2640(const struct i2c_client *client) @@ -701,81 +678,23 @@ static int ov2640_s_stream(struct v4l2_subdev *sd, int enable) return 0; } -static int ov2640_set_bus_param(struct soc_camera_device *icd, - unsigned long flags) -{ - struct soc_camera_link *icl = to_soc_camera_link(icd); - unsigned long width_flag = flags & SOCAM_DATAWIDTH_MASK; - - /* Only one width bit may be set */ - if (!is_power_of_2(width_flag)) - return -EINVAL; - - if (icl->set_bus_param) - return icl->set_bus_param(icl, width_flag); - - /* - * Without board specific bus width settings we support only the - * sensors native bus width witch are tested working - */ - if (width_flag & (SOCAM_DATAWIDTH_10 | SOCAM_DATAWIDTH_8)) - return 0; - - return 0; -} - -static unsigned long ov2640_query_bus_param(struct soc_camera_device *icd) -{ - struct soc_camera_link *icl = to_soc_camera_link(icd); - unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER | - SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH | - SOCAM_DATA_ACTIVE_HIGH; - - if (icl->query_bus_param) - flags |= icl->query_bus_param(icl) & SOCAM_DATAWIDTH_MASK; - else - flags |= SOCAM_DATAWIDTH_10; - - return soc_camera_apply_sensor_flags(icl, flags); -} - -static int ov2640_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +static int ov2640_s_ctrl(struct v4l2_ctrl *ctrl) { + struct v4l2_subdev *sd = + &container_of(ctrl->handler, struct ov2640_priv, hdl)->subdev; struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ov2640_priv *priv = to_ov2640(client); - - switch (ctrl->id) { - case V4L2_CID_VFLIP: - ctrl->value = priv->flag_vflip; - break; - case V4L2_CID_HFLIP: - ctrl->value = priv->flag_hflip; - break; - } - return 0; -} - -static int ov2640_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ov2640_priv *priv = to_ov2640(client); - int ret = 0; u8 val; switch (ctrl->id) { case V4L2_CID_VFLIP: - val = ctrl->value ? REG04_VFLIP_IMG : 0x00; - priv->flag_vflip = ctrl->value ? 1 : 0; - ret = ov2640_mask_set(client, REG04, REG04_VFLIP_IMG, val); - break; + val = ctrl->val ? REG04_VFLIP_IMG : 0x00; + return ov2640_mask_set(client, REG04, REG04_VFLIP_IMG, val); case V4L2_CID_HFLIP: - val = ctrl->value ? REG04_HFLIP_IMG : 0x00; - priv->flag_hflip = ctrl->value ? 1 : 0; - ret = ov2640_mask_set(client, REG04, REG04_HFLIP_IMG, val); - break; + val = ctrl->val ? REG04_HFLIP_IMG : 0x00; + return ov2640_mask_set(client, REG04, REG04_HFLIP_IMG, val); } - return ret; + return -EINVAL; } static int ov2640_g_chip_ident(struct v4l2_subdev *sd, @@ -1023,18 +942,13 @@ static int ov2640_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) return 0; } -static int ov2640_video_probe(struct soc_camera_device *icd, - struct i2c_client *client) +static int ov2640_video_probe(struct i2c_client *client) { struct ov2640_priv *priv = to_ov2640(client); u8 pid, ver, midh, midl; const char *devname; int ret; - /* We must have a parent by now. And it cannot be a wrong one. */ - BUG_ON(!icd->parent || - to_soc_camera_host(icd->parent)->nr != icd->iface); - /* * check and show product ID and manufacturer ID */ @@ -1060,22 +974,17 @@ static int ov2640_video_probe(struct soc_camera_device *icd, "%s Product ID %0x:%0x Manufacturer ID %x:%x\n", devname, pid, ver, midh, midl); - return 0; + return v4l2_ctrl_handler_setup(&priv->hdl); err: return ret; } -static struct soc_camera_ops ov2640_ops = { - .set_bus_param = ov2640_set_bus_param, - .query_bus_param = ov2640_query_bus_param, - .controls = ov2640_controls, - .num_controls = ARRAY_SIZE(ov2640_controls), +static const struct v4l2_ctrl_ops ov2640_ctrl_ops = { + .s_ctrl = ov2640_s_ctrl, }; static struct v4l2_subdev_core_ops ov2640_subdev_core_ops = { - .g_ctrl = ov2640_g_ctrl, - .s_ctrl = ov2640_s_ctrl, .g_chip_ident = ov2640_g_chip_ident, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = ov2640_g_register, @@ -1083,6 +992,21 @@ static struct v4l2_subdev_core_ops ov2640_subdev_core_ops = { #endif }; +static int ov2640_g_mbus_config(struct v4l2_subdev *sd, + struct v4l2_mbus_config *cfg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + + cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER | + V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH | + V4L2_MBUS_DATA_ACTIVE_HIGH; + cfg->type = V4L2_MBUS_PARALLEL; + cfg->flags = soc_camera_apply_board_flags(icl, cfg); + + return 0; +} + static struct v4l2_subdev_video_ops ov2640_subdev_video_ops = { .s_stream = ov2640_s_stream, .g_mbus_fmt = ov2640_g_fmt, @@ -1091,6 +1015,7 @@ static struct v4l2_subdev_video_ops ov2640_subdev_video_ops = { .cropcap = ov2640_cropcap, .g_crop = ov2640_g_crop, .enum_mbus_fmt = ov2640_enum_fmt, + .g_mbus_config = ov2640_g_mbus_config, }; static struct v4l2_subdev_ops ov2640_subdev_ops = { @@ -1104,18 +1029,11 @@ static struct v4l2_subdev_ops ov2640_subdev_ops = { static int ov2640_probe(struct i2c_client *client, const struct i2c_device_id *did) { - struct ov2640_priv *priv; - struct soc_camera_device *icd = client->dev.platform_data; - struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - struct soc_camera_link *icl; - int ret; - - if (!icd) { - dev_err(&adapter->dev, "OV2640: missing soc-camera data!\n"); - return -EINVAL; - } + struct ov2640_priv *priv; + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + int ret; - icl = to_soc_camera_link(icd); if (!icl) { dev_err(&adapter->dev, "OV2640: Missing platform_data for driver\n"); @@ -1135,15 +1053,23 @@ static int ov2640_probe(struct i2c_client *client, return -ENOMEM; } - priv->info = icl->priv; - v4l2_i2c_subdev_init(&priv->subdev, client, &ov2640_subdev_ops); + v4l2_ctrl_handler_init(&priv->hdl, 2); + v4l2_ctrl_new_std(&priv->hdl, &ov2640_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(&priv->hdl, &ov2640_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + priv->subdev.ctrl_handler = &priv->hdl; + if (priv->hdl.error) { + int err = priv->hdl.error; - icd->ops = &ov2640_ops; + kfree(priv); + return err; + } - ret = ov2640_video_probe(icd, client); + ret = ov2640_video_probe(client); if (ret) { - icd->ops = NULL; + v4l2_ctrl_handler_free(&priv->hdl); kfree(priv); } else { dev_info(&adapter->dev, "OV2640 Probed\n"); @@ -1155,9 +1081,9 @@ static int ov2640_probe(struct i2c_client *client, static int ov2640_remove(struct i2c_client *client) { struct ov2640_priv *priv = to_ov2640(client); - struct soc_camera_device *icd = client->dev.platform_data; - icd->ops = NULL; + v4l2_device_unregister_subdev(&priv->subdev); + v4l2_ctrl_handler_free(&priv->hdl); kfree(priv); return 0; } diff --git a/drivers/media/video/ov5642.c b/drivers/media/video/ov5642.c index 349a4ad3ccc..bb37ec80f27 100644 --- a/drivers/media/video/ov5642.c +++ b/drivers/media/video/ov5642.c @@ -14,14 +14,16 @@ * published by the Free Software Foundation. */ +#include <linux/bitops.h> #include <linux/delay.h> #include <linux/i2c.h> +#include <linux/kernel.h> #include <linux/slab.h> #include <linux/videodev2.h> #include <linux/module.h> +#include <linux/v4l2-mediabus.h> #include <media/soc_camera.h> -#include <media/soc_mediabus.h> #include <media/v4l2-chip-ident.h> #include <media/v4l2-subdev.h> @@ -35,7 +37,7 @@ #define REG_WINDOW_START_Y_LOW 0x3803 #define REG_WINDOW_WIDTH_HIGH 0x3804 #define REG_WINDOW_WIDTH_LOW 0x3805 -#define REG_WINDOW_HEIGHT_HIGH 0x3806 +#define REG_WINDOW_HEIGHT_HIGH 0x3806 #define REG_WINDOW_HEIGHT_LOW 0x3807 #define REG_OUT_WIDTH_HIGH 0x3808 #define REG_OUT_WIDTH_LOW 0x3809 @@ -45,19 +47,44 @@ #define REG_OUT_TOTAL_WIDTH_LOW 0x380d #define REG_OUT_TOTAL_HEIGHT_HIGH 0x380e #define REG_OUT_TOTAL_HEIGHT_LOW 0x380f +#define REG_OUTPUT_FORMAT 0x4300 +#define REG_ISP_CTRL_01 0x5001 +#define REG_AVG_WINDOW_END_X_HIGH 0x5682 +#define REG_AVG_WINDOW_END_X_LOW 0x5683 +#define REG_AVG_WINDOW_END_Y_HIGH 0x5686 +#define REG_AVG_WINDOW_END_Y_LOW 0x5687 + +/* active pixel array size */ +#define OV5642_SENSOR_SIZE_X 2592 +#define OV5642_SENSOR_SIZE_Y 1944 /* - * define standard resolution. - * Works currently only for up to 720 lines - * eg. 320x240, 640x480, 800x600, 1280x720, 2048x720 + * About OV5642 resolution, cropping and binning: + * This sensor supports it all, at least in the feature description. + * Unfortunately, no combination of appropriate registers settings could make + * the chip work the intended way. As it works with predefined register lists, + * some undocumented registers are presumably changed there to achieve their + * goals. + * This driver currently only works for resolutions up to 720 lines with a + * 1:1 scale. Hopefully these restrictions will be removed in the future. */ +#define OV5642_MAX_WIDTH OV5642_SENSOR_SIZE_X +#define OV5642_MAX_HEIGHT 720 -#define OV5642_WIDTH 1280 -#define OV5642_HEIGHT 720 -#define OV5642_TOTAL_WIDTH 3200 -#define OV5642_TOTAL_HEIGHT 2000 -#define OV5642_SENSOR_SIZE_X 2592 -#define OV5642_SENSOR_SIZE_Y 1944 +/* default sizes */ +#define OV5642_DEFAULT_WIDTH 1280 +#define OV5642_DEFAULT_HEIGHT OV5642_MAX_HEIGHT + +/* minimum extra blanking */ +#define BLANKING_EXTRA_WIDTH 500 +#define BLANKING_EXTRA_HEIGHT 20 + +/* + * the sensor's autoexposure is buggy when setting total_height low. + * It tries to expose longer than 1 frame period without taking care of it + * and this leads to weird output. So we set 1000 lines as minimum. + */ +#define BLANKING_MIN_HEIGHT 1000 struct regval_list { u16 reg_num; @@ -582,6 +609,11 @@ struct ov5642_datafmt { struct ov5642 { struct v4l2_subdev subdev; const struct ov5642_datafmt *fmt; + struct v4l2_rect crop_rect; + + /* blanking information */ + int total_width; + int total_height; }; static const struct ov5642_datafmt ov5642_colour_fmts[] = { @@ -642,6 +674,21 @@ static int reg_write(struct i2c_client *client, u16 reg, u8 val) return 0; } + +/* + * convenience function to write 16 bit register values that are split up + * into two consecutive high and low parts + */ +static int reg_write16(struct i2c_client *client, u16 reg, u16 val16) +{ + int ret; + + ret = reg_write(client, reg, val16 >> 8); + if (ret) + return ret; + return reg_write(client, reg + 1, val16 & 0x00ff); +} + #ifdef CONFIG_VIDEO_ADV_DEBUG static int ov5642_get_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) { @@ -685,58 +732,55 @@ static int ov5642_write_array(struct i2c_client *client, return 0; } -static int ov5642_set_resolution(struct i2c_client *client) +static int ov5642_set_resolution(struct v4l2_subdev *sd) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov5642 *priv = to_ov5642(client); + int width = priv->crop_rect.width; + int height = priv->crop_rect.height; + int total_width = priv->total_width; + int total_height = priv->total_height; + int start_x = (OV5642_SENSOR_SIZE_X - width) / 2; + int start_y = (OV5642_SENSOR_SIZE_Y - height) / 2; int ret; - u8 start_x_high = ((OV5642_SENSOR_SIZE_X - OV5642_WIDTH) / 2) >> 8; - u8 start_x_low = ((OV5642_SENSOR_SIZE_X - OV5642_WIDTH) / 2) & 0xff; - u8 start_y_high = ((OV5642_SENSOR_SIZE_Y - OV5642_HEIGHT) / 2) >> 8; - u8 start_y_low = ((OV5642_SENSOR_SIZE_Y - OV5642_HEIGHT) / 2) & 0xff; - - u8 width_high = OV5642_WIDTH >> 8; - u8 width_low = OV5642_WIDTH & 0xff; - u8 height_high = OV5642_HEIGHT >> 8; - u8 height_low = OV5642_HEIGHT & 0xff; - - u8 total_width_high = OV5642_TOTAL_WIDTH >> 8; - u8 total_width_low = OV5642_TOTAL_WIDTH & 0xff; - u8 total_height_high = OV5642_TOTAL_HEIGHT >> 8; - u8 total_height_low = OV5642_TOTAL_HEIGHT & 0xff; - - ret = reg_write(client, REG_WINDOW_START_X_HIGH, start_x_high); - if (!ret) - ret = reg_write(client, REG_WINDOW_START_X_LOW, start_x_low); - if (!ret) - ret = reg_write(client, REG_WINDOW_START_Y_HIGH, start_y_high); - if (!ret) - ret = reg_write(client, REG_WINDOW_START_Y_LOW, start_y_low); + /* + * This should set the starting point for cropping. + * Doesn't work so far. + */ + ret = reg_write16(client, REG_WINDOW_START_X_HIGH, start_x); if (!ret) - ret = reg_write(client, REG_WINDOW_WIDTH_HIGH, width_high); - if (!ret) - ret = reg_write(client, REG_WINDOW_WIDTH_LOW , width_low); - if (!ret) - ret = reg_write(client, REG_WINDOW_HEIGHT_HIGH, height_high); - if (!ret) - ret = reg_write(client, REG_WINDOW_HEIGHT_LOW, height_low); + ret = reg_write16(client, REG_WINDOW_START_Y_HIGH, start_y); + if (!ret) { + priv->crop_rect.left = start_x; + priv->crop_rect.top = start_y; + } if (!ret) - ret = reg_write(client, REG_OUT_WIDTH_HIGH, width_high); + ret = reg_write16(client, REG_WINDOW_WIDTH_HIGH, width); if (!ret) - ret = reg_write(client, REG_OUT_WIDTH_LOW , width_low); - if (!ret) - ret = reg_write(client, REG_OUT_HEIGHT_HIGH, height_high); + ret = reg_write16(client, REG_WINDOW_HEIGHT_HIGH, height); + if (ret) + return ret; + priv->crop_rect.width = width; + priv->crop_rect.height = height; + + /* Set the output window size. Only 1:1 scale is supported so far. */ + ret = reg_write16(client, REG_OUT_WIDTH_HIGH, width); if (!ret) - ret = reg_write(client, REG_OUT_HEIGHT_LOW, height_low); + ret = reg_write16(client, REG_OUT_HEIGHT_HIGH, height); + /* Total width = output size + blanking */ if (!ret) - ret = reg_write(client, REG_OUT_TOTAL_WIDTH_HIGH, total_width_high); + ret = reg_write16(client, REG_OUT_TOTAL_WIDTH_HIGH, total_width); if (!ret) - ret = reg_write(client, REG_OUT_TOTAL_WIDTH_LOW, total_width_low); + ret = reg_write16(client, REG_OUT_TOTAL_HEIGHT_HIGH, total_height); + + /* Sets the window for AWB calculations */ if (!ret) - ret = reg_write(client, REG_OUT_TOTAL_HEIGHT_HIGH, total_height_high); + ret = reg_write16(client, REG_AVG_WINDOW_END_X_HIGH, width); if (!ret) - ret = reg_write(client, REG_OUT_TOTAL_HEIGHT_LOW, total_height_low); + ret = reg_write16(client, REG_AVG_WINDOW_END_Y_HIGH, height); return ret; } @@ -744,18 +788,18 @@ static int ov5642_set_resolution(struct i2c_client *client) static int ov5642_try_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov5642 *priv = to_ov5642(client); const struct ov5642_datafmt *fmt = ov5642_find_datafmt(mf->code); - dev_dbg(sd->v4l2_dev->dev, "%s(%u) width: %u heigth: %u\n", - __func__, mf->code, mf->width, mf->height); + mf->width = priv->crop_rect.width; + mf->height = priv->crop_rect.height; if (!fmt) { mf->code = ov5642_colour_fmts[0].code; mf->colorspace = ov5642_colour_fmts[0].colorspace; } - mf->width = OV5642_WIDTH; - mf->height = OV5642_HEIGHT; mf->field = V4L2_FIELD_NONE; return 0; @@ -767,20 +811,13 @@ static int ov5642_s_fmt(struct v4l2_subdev *sd, struct i2c_client *client = v4l2_get_subdevdata(sd); struct ov5642 *priv = to_ov5642(client); - dev_dbg(sd->v4l2_dev->dev, "%s(%u)\n", __func__, mf->code); - /* MIPI CSI could have changed the format, double-check */ if (!ov5642_find_datafmt(mf->code)) return -EINVAL; ov5642_try_fmt(sd, mf); - priv->fmt = ov5642_find_datafmt(mf->code); - ov5642_write_array(client, ov5642_default_regs_init); - ov5642_set_resolution(client); - ov5642_write_array(client, ov5642_default_regs_finalise); - return 0; } @@ -794,8 +831,8 @@ static int ov5642_g_fmt(struct v4l2_subdev *sd, mf->code = fmt->code; mf->colorspace = fmt->colorspace; - mf->width = OV5642_WIDTH; - mf->height = OV5642_HEIGHT; + mf->width = priv->crop_rect.width; + mf->height = priv->crop_rect.height; mf->field = V4L2_FIELD_NONE; return 0; @@ -828,15 +865,44 @@ static int ov5642_g_chip_ident(struct v4l2_subdev *sd, return 0; } +static int ov5642_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov5642 *priv = to_ov5642(client); + struct v4l2_rect *rect = &a->c; + int ret; + + v4l_bound_align_image(&rect->width, 48, OV5642_MAX_WIDTH, 1, + &rect->height, 32, OV5642_MAX_HEIGHT, 1, 0); + + priv->crop_rect.width = rect->width; + priv->crop_rect.height = rect->height; + priv->total_width = rect->width + BLANKING_EXTRA_WIDTH; + priv->total_height = max_t(int, rect->height + + BLANKING_EXTRA_HEIGHT, + BLANKING_MIN_HEIGHT); + priv->crop_rect.width = rect->width; + priv->crop_rect.height = rect->height; + + ret = ov5642_write_array(client, ov5642_default_regs_init); + if (!ret) + ret = ov5642_set_resolution(sd); + if (!ret) + ret = ov5642_write_array(client, ov5642_default_regs_finalise); + + return ret; +} + static int ov5642_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov5642 *priv = to_ov5642(client); struct v4l2_rect *rect = &a->c; - a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - rect->top = 0; - rect->left = 0; - rect->width = OV5642_WIDTH; - rect->height = OV5642_HEIGHT; + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + *rect = priv->crop_rect; return 0; } @@ -845,8 +911,8 @@ static int ov5642_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) { a->bounds.left = 0; a->bounds.top = 0; - a->bounds.width = OV5642_WIDTH; - a->bounds.height = OV5642_HEIGHT; + a->bounds.width = OV5642_MAX_WIDTH; + a->bounds.height = OV5642_MAX_HEIGHT; a->defrect = a->bounds; a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; a->pixelaspect.numerator = 1; @@ -855,16 +921,47 @@ static int ov5642_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) return 0; } +static int ov5642_g_mbus_config(struct v4l2_subdev *sd, + struct v4l2_mbus_config *cfg) +{ + cfg->type = V4L2_MBUS_CSI2; + cfg->flags = V4L2_MBUS_CSI2_2_LANE | V4L2_MBUS_CSI2_CHANNEL_0 | + V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; + + return 0; +} + +static int ov5642_s_power(struct v4l2_subdev *sd, int on) +{ + struct i2c_client *client; + int ret; + + if (!on) + return 0; + + client = v4l2_get_subdevdata(sd); + ret = ov5642_write_array(client, ov5642_default_regs_init); + if (!ret) + ret = ov5642_set_resolution(sd); + if (!ret) + ret = ov5642_write_array(client, ov5642_default_regs_finalise); + + return ret; +} + static struct v4l2_subdev_video_ops ov5642_subdev_video_ops = { .s_mbus_fmt = ov5642_s_fmt, .g_mbus_fmt = ov5642_g_fmt, .try_mbus_fmt = ov5642_try_fmt, .enum_mbus_fmt = ov5642_enum_fmt, + .s_crop = ov5642_s_crop, .g_crop = ov5642_g_crop, .cropcap = ov5642_cropcap, + .g_mbus_config = ov5642_g_mbus_config, }; static struct v4l2_subdev_core_ops ov5642_subdev_core_ops = { + .s_power = ov5642_s_power, .g_chip_ident = ov5642_g_chip_ident, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = ov5642_get_register, @@ -877,28 +974,7 @@ static struct v4l2_subdev_ops ov5642_subdev_ops = { .video = &ov5642_subdev_video_ops, }; -/* - * We have to provide soc-camera operations, but we don't have anything to say - * there. The MIPI CSI2 driver will provide .query_bus_param and .set_bus_param - */ -static unsigned long soc_ov5642_query_bus_param(struct soc_camera_device *icd) -{ - return 0; -} - -static int soc_ov5642_set_bus_param(struct soc_camera_device *icd, - unsigned long flags) -{ - return -EINVAL; -} - -static struct soc_camera_ops soc_ov5642_ops = { - .query_bus_param = soc_ov5642_query_bus_param, - .set_bus_param = soc_ov5642_set_bus_param, -}; - -static int ov5642_video_probe(struct soc_camera_device *icd, - struct i2c_client *client) +static int ov5642_video_probe(struct i2c_client *client) { int ret; u8 id_high, id_low; @@ -929,16 +1005,9 @@ static int ov5642_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct ov5642 *priv; - struct soc_camera_device *icd = client->dev.platform_data; - struct soc_camera_link *icl; + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); int ret; - if (!icd) { - dev_err(&client->dev, "OV5642: missing soc-camera data!\n"); - return -EINVAL; - } - - icl = to_soc_camera_link(icd); if (!icl) { dev_err(&client->dev, "OV5642: missing platform data!\n"); return -EINVAL; @@ -950,17 +1019,24 @@ static int ov5642_probe(struct i2c_client *client, v4l2_i2c_subdev_init(&priv->subdev, client, &ov5642_subdev_ops); - icd->ops = &soc_ov5642_ops; - priv->fmt = &ov5642_colour_fmts[0]; + priv->fmt = &ov5642_colour_fmts[0]; + + priv->crop_rect.width = OV5642_DEFAULT_WIDTH; + priv->crop_rect.height = OV5642_DEFAULT_HEIGHT; + priv->crop_rect.left = (OV5642_MAX_WIDTH - OV5642_DEFAULT_WIDTH) / 2; + priv->crop_rect.top = (OV5642_MAX_HEIGHT - OV5642_DEFAULT_HEIGHT) / 2; + priv->crop_rect.width = OV5642_DEFAULT_WIDTH; + priv->crop_rect.height = OV5642_DEFAULT_HEIGHT; + priv->total_width = OV5642_DEFAULT_WIDTH + BLANKING_EXTRA_WIDTH; + priv->total_height = BLANKING_MIN_HEIGHT; - ret = ov5642_video_probe(icd, client); + ret = ov5642_video_probe(client); if (ret < 0) goto error; return 0; error: - icd->ops = NULL; kfree(priv); return ret; } @@ -968,10 +1044,8 @@ error: static int ov5642_remove(struct i2c_client *client) { struct ov5642 *priv = to_ov5642(client); - struct soc_camera_device *icd = client->dev.platform_data; - struct soc_camera_link *icl = to_soc_camera_link(icd); + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); - icd->ops = NULL; if (icl->free_bus) icl->free_bus(icl); kfree(priv); diff --git a/drivers/media/video/ov6650.c b/drivers/media/video/ov6650.c index 456d9ad9ae5..9f2d26b1d4c 100644 --- a/drivers/media/video/ov6650.c +++ b/drivers/media/video/ov6650.c @@ -28,10 +28,12 @@ #include <linux/delay.h> #include <linux/i2c.h> #include <linux/slab.h> +#include <linux/v4l2-mediabus.h> +#include <linux/module.h> #include <media/soc_camera.h> #include <media/v4l2-chip-ident.h> - +#include <media/v4l2-ctrls.h> /* Register definitions */ #define REG_GAIN 0x00 /* range 00 - 3F */ @@ -177,20 +179,23 @@ struct ov6650_reg { struct ov6650 { struct v4l2_subdev subdev; - - int gain; - int blue; - int red; - int saturation; - int hue; - int brightness; - int exposure; - int gamma; - int aec; - bool vflip; - bool hflip; - bool awb; - bool agc; + struct v4l2_ctrl_handler hdl; + struct { + /* exposure/autoexposure cluster */ + struct v4l2_ctrl *autoexposure; + struct v4l2_ctrl *exposure; + }; + struct { + /* gain/autogain cluster */ + struct v4l2_ctrl *autogain; + struct v4l2_ctrl *gain; + }; + struct { + /* blue/red/autowhitebalance cluster */ + struct v4l2_ctrl *autowb; + struct v4l2_ctrl *blue; + struct v4l2_ctrl *red; + }; bool half_scale; /* scale down output by 2 */ struct v4l2_rect rect; /* sensor cropping window */ unsigned long pclk_limit; /* from host */ @@ -210,126 +215,6 @@ static enum v4l2_mbus_pixelcode ov6650_codes[] = { V4L2_MBUS_FMT_Y8_1X8, }; -static const struct v4l2_queryctrl ov6650_controls[] = { - { - .id = V4L2_CID_AUTOGAIN, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "AGC", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1, - }, - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gain", - .minimum = 0, - .maximum = 0x3f, - .step = 1, - .default_value = DEF_GAIN, - }, - { - .id = V4L2_CID_AUTO_WHITE_BALANCE, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "AWB", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1, - }, - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Blue", - .minimum = 0, - .maximum = 0xff, - .step = 1, - .default_value = DEF_BLUE, - }, - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Red", - .minimum = 0, - .maximum = 0xff, - .step = 1, - .default_value = DEF_RED, - }, - { - .id = V4L2_CID_SATURATION, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Saturation", - .minimum = 0, - .maximum = 0xf, - .step = 1, - .default_value = 0x8, - }, - { - .id = V4L2_CID_HUE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Hue", - .minimum = 0, - .maximum = HUE_MASK, - .step = 1, - .default_value = DEF_HUE, - }, - { - .id = V4L2_CID_BRIGHTNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Brightness", - .minimum = 0, - .maximum = 0xff, - .step = 1, - .default_value = 0x80, - }, - { - .id = V4L2_CID_EXPOSURE_AUTO, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "AEC", - .minimum = 0, - .maximum = 3, - .step = 1, - .default_value = 0, - }, - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Exposure", - .minimum = 0, - .maximum = 0xff, - .step = 1, - .default_value = DEF_AECH, - }, - { - .id = V4L2_CID_GAMMA, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gamma", - .minimum = 0, - .maximum = 0xff, - .step = 1, - .default_value = 0x12, - }, - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Flip Vertically", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, - { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Flip Horizontally", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, -}; - /* read a register */ static int ov6650_reg_read(struct i2c_client *client, u8 reg, u8 *val) { @@ -419,213 +304,90 @@ static int ov6650_s_stream(struct v4l2_subdev *sd, int enable) return 0; } -/* Alter bus settings on camera side */ -static int ov6650_set_bus_param(struct soc_camera_device *icd, - unsigned long flags) -{ - struct soc_camera_link *icl = to_soc_camera_link(icd); - struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd)); - int ret; - - flags = soc_camera_apply_sensor_flags(icl, flags); - - if (flags & SOCAM_PCLK_SAMPLE_RISING) - ret = ov6650_reg_rmw(client, REG_COMJ, COMJ_PCLK_RISING, 0); - else - ret = ov6650_reg_rmw(client, REG_COMJ, 0, COMJ_PCLK_RISING); - if (ret) - return ret; - - if (flags & SOCAM_HSYNC_ACTIVE_LOW) - ret = ov6650_reg_rmw(client, REG_COMF, COMF_HREF_LOW, 0); - else - ret = ov6650_reg_rmw(client, REG_COMF, 0, COMF_HREF_LOW); - if (ret) - return ret; - - if (flags & SOCAM_VSYNC_ACTIVE_HIGH) - ret = ov6650_reg_rmw(client, REG_COMJ, COMJ_VSYNC_HIGH, 0); - else - ret = ov6650_reg_rmw(client, REG_COMJ, 0, COMJ_VSYNC_HIGH); - - return ret; -} - -/* Request bus settings on camera side */ -static unsigned long ov6650_query_bus_param(struct soc_camera_device *icd) -{ - struct soc_camera_link *icl = to_soc_camera_link(icd); - - unsigned long flags = SOCAM_MASTER | - SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING | - SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_LOW | - SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW | - SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATAWIDTH_8; - - return soc_camera_apply_sensor_flags(icl, flags); -} - /* Get status of additional camera capabilities */ -static int ov6650_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +static int ov6550_g_volatile_ctrl(struct v4l2_ctrl *ctrl) { + struct ov6650 *priv = container_of(ctrl->handler, struct ov6650, hdl); + struct v4l2_subdev *sd = &priv->subdev; struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ov6650 *priv = to_ov6650(client); - uint8_t reg; - int ret = 0; + uint8_t reg, reg2; + int ret; switch (ctrl->id) { case V4L2_CID_AUTOGAIN: - ctrl->value = priv->agc; - break; - case V4L2_CID_GAIN: - if (priv->agc) { - ret = ov6650_reg_read(client, REG_GAIN, ®); - ctrl->value = reg; - } else { - ctrl->value = priv->gain; - } - break; + ret = ov6650_reg_read(client, REG_GAIN, ®); + if (!ret) + priv->gain->val = reg; + return ret; case V4L2_CID_AUTO_WHITE_BALANCE: - ctrl->value = priv->awb; - break; - case V4L2_CID_BLUE_BALANCE: - if (priv->awb) { - ret = ov6650_reg_read(client, REG_BLUE, ®); - ctrl->value = reg; - } else { - ctrl->value = priv->blue; - } - break; - case V4L2_CID_RED_BALANCE: - if (priv->awb) { - ret = ov6650_reg_read(client, REG_RED, ®); - ctrl->value = reg; - } else { - ctrl->value = priv->red; + ret = ov6650_reg_read(client, REG_BLUE, ®); + if (!ret) + ret = ov6650_reg_read(client, REG_RED, ®2); + if (!ret) { + priv->blue->val = reg; + priv->red->val = reg2; } - break; - case V4L2_CID_SATURATION: - ctrl->value = priv->saturation; - break; - case V4L2_CID_HUE: - ctrl->value = priv->hue; - break; - case V4L2_CID_BRIGHTNESS: - ctrl->value = priv->brightness; - break; + return ret; case V4L2_CID_EXPOSURE_AUTO: - ctrl->value = priv->aec; - break; - case V4L2_CID_EXPOSURE: - if (priv->aec) { - ret = ov6650_reg_read(client, REG_AECH, ®); - ctrl->value = reg; - } else { - ctrl->value = priv->exposure; - } - break; - case V4L2_CID_GAMMA: - ctrl->value = priv->gamma; - break; - case V4L2_CID_VFLIP: - ctrl->value = priv->vflip; - break; - case V4L2_CID_HFLIP: - ctrl->value = priv->hflip; - break; + ret = ov6650_reg_read(client, REG_AECH, ®); + if (!ret) + priv->exposure->val = reg; + return ret; } - return ret; + return -EINVAL; } /* Set status of additional camera capabilities */ -static int ov6650_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +static int ov6550_s_ctrl(struct v4l2_ctrl *ctrl) { + struct ov6650 *priv = container_of(ctrl->handler, struct ov6650, hdl); + struct v4l2_subdev *sd = &priv->subdev; struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ov6650 *priv = to_ov6650(client); - int ret = 0; + int ret; switch (ctrl->id) { case V4L2_CID_AUTOGAIN: ret = ov6650_reg_rmw(client, REG_COMB, - ctrl->value ? COMB_AGC : 0, COMB_AGC); - if (!ret) - priv->agc = ctrl->value; - break; - case V4L2_CID_GAIN: - ret = ov6650_reg_write(client, REG_GAIN, ctrl->value); - if (!ret) - priv->gain = ctrl->value; - break; + ctrl->val ? COMB_AGC : 0, COMB_AGC); + if (!ret && !ctrl->val) + ret = ov6650_reg_write(client, REG_GAIN, priv->gain->val); + return ret; case V4L2_CID_AUTO_WHITE_BALANCE: ret = ov6650_reg_rmw(client, REG_COMB, - ctrl->value ? COMB_AWB : 0, COMB_AWB); - if (!ret) - priv->awb = ctrl->value; - break; - case V4L2_CID_BLUE_BALANCE: - ret = ov6650_reg_write(client, REG_BLUE, ctrl->value); - if (!ret) - priv->blue = ctrl->value; - break; - case V4L2_CID_RED_BALANCE: - ret = ov6650_reg_write(client, REG_RED, ctrl->value); - if (!ret) - priv->red = ctrl->value; - break; + ctrl->val ? COMB_AWB : 0, COMB_AWB); + if (!ret && !ctrl->val) { + ret = ov6650_reg_write(client, REG_BLUE, priv->blue->val); + if (!ret) + ret = ov6650_reg_write(client, REG_RED, + priv->red->val); + } + return ret; case V4L2_CID_SATURATION: - ret = ov6650_reg_rmw(client, REG_SAT, SET_SAT(ctrl->value), + return ov6650_reg_rmw(client, REG_SAT, SET_SAT(ctrl->val), SAT_MASK); - if (!ret) - priv->saturation = ctrl->value; - break; case V4L2_CID_HUE: - ret = ov6650_reg_rmw(client, REG_HUE, SET_HUE(ctrl->value), + return ov6650_reg_rmw(client, REG_HUE, SET_HUE(ctrl->val), HUE_MASK); - if (!ret) - priv->hue = ctrl->value; - break; case V4L2_CID_BRIGHTNESS: - ret = ov6650_reg_write(client, REG_BRT, ctrl->value); - if (!ret) - priv->brightness = ctrl->value; - break; + return ov6650_reg_write(client, REG_BRT, ctrl->val); case V4L2_CID_EXPOSURE_AUTO: - switch (ctrl->value) { - case V4L2_EXPOSURE_AUTO: - ret = ov6650_reg_rmw(client, REG_COMB, COMB_AEC, 0); - break; - default: - ret = ov6650_reg_rmw(client, REG_COMB, 0, COMB_AEC); - break; - } - if (!ret) - priv->aec = ctrl->value; - break; - case V4L2_CID_EXPOSURE: - ret = ov6650_reg_write(client, REG_AECH, ctrl->value); - if (!ret) - priv->exposure = ctrl->value; - break; + ret = ov6650_reg_rmw(client, REG_COMB, ctrl->val == + V4L2_EXPOSURE_AUTO ? COMB_AEC : 0, COMB_AEC); + if (!ret && ctrl->val == V4L2_EXPOSURE_MANUAL) + ret = ov6650_reg_write(client, REG_AECH, + priv->exposure->val); + return ret; case V4L2_CID_GAMMA: - ret = ov6650_reg_write(client, REG_GAM1, ctrl->value); - if (!ret) - priv->gamma = ctrl->value; - break; + return ov6650_reg_write(client, REG_GAM1, ctrl->val); case V4L2_CID_VFLIP: - ret = ov6650_reg_rmw(client, REG_COMB, - ctrl->value ? COMB_FLIP_V : 0, COMB_FLIP_V); - if (!ret) - priv->vflip = ctrl->value; - break; + return ov6650_reg_rmw(client, REG_COMB, + ctrl->val ? COMB_FLIP_V : 0, COMB_FLIP_V); case V4L2_CID_HFLIP: - ret = ov6650_reg_rmw(client, REG_COMB, - ctrl->value ? COMB_FLIP_H : 0, COMB_FLIP_H); - if (!ret) - priv->hflip = ctrl->value; - break; + return ov6650_reg_rmw(client, REG_COMB, + ctrl->val ? COMB_FLIP_H : 0, COMB_FLIP_H); } - return ret; + return -EINVAL; } /* Get chip identification */ @@ -778,7 +540,7 @@ static u8 to_clkrc(struct v4l2_fract *timeperframe, static int ov6650_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_device *icd = client->dev.platform_data; + struct soc_camera_device *icd = (struct soc_camera_device *)sd->grp_id; struct soc_camera_sense *sense = icd->sense; struct ov6650 *priv = to_ov6650(client); bool half_scale = !is_unscaled_ok(mf->width, mf->height, &priv->rect); @@ -1057,8 +819,7 @@ static int ov6650_prog_dflt(struct i2c_client *client) return ret; } -static int ov6650_video_probe(struct soc_camera_device *icd, - struct i2c_client *client) +static int ov6650_video_probe(struct i2c_client *client) { u8 pidh, pidl, midh, midl; int ret = 0; @@ -1094,16 +855,12 @@ static int ov6650_video_probe(struct soc_camera_device *icd, return ret; } -static struct soc_camera_ops ov6650_ops = { - .set_bus_param = ov6650_set_bus_param, - .query_bus_param = ov6650_query_bus_param, - .controls = ov6650_controls, - .num_controls = ARRAY_SIZE(ov6650_controls), +static const struct v4l2_ctrl_ops ov6550_ctrl_ops = { + .g_volatile_ctrl = ov6550_g_volatile_ctrl, + .s_ctrl = ov6550_s_ctrl, }; static struct v4l2_subdev_core_ops ov6650_core_ops = { - .g_ctrl = ov6650_g_ctrl, - .s_ctrl = ov6650_s_ctrl, .g_chip_ident = ov6650_g_chip_ident, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = ov6650_get_register, @@ -1111,6 +868,55 @@ static struct v4l2_subdev_core_ops ov6650_core_ops = { #endif }; +/* Request bus settings on camera side */ +static int ov6650_g_mbus_config(struct v4l2_subdev *sd, + struct v4l2_mbus_config *cfg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + + cfg->flags = V4L2_MBUS_MASTER | + V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | + V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW | + V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW | + V4L2_MBUS_DATA_ACTIVE_HIGH; + cfg->type = V4L2_MBUS_PARALLEL; + cfg->flags = soc_camera_apply_board_flags(icl, cfg); + + return 0; +} + +/* Alter bus settings on camera side */ +static int ov6650_s_mbus_config(struct v4l2_subdev *sd, + const struct v4l2_mbus_config *cfg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + unsigned long flags = soc_camera_apply_board_flags(icl, cfg); + int ret; + + if (flags & V4L2_MBUS_PCLK_SAMPLE_RISING) + ret = ov6650_reg_rmw(client, REG_COMJ, COMJ_PCLK_RISING, 0); + else + ret = ov6650_reg_rmw(client, REG_COMJ, 0, COMJ_PCLK_RISING); + if (ret) + return ret; + + if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) + ret = ov6650_reg_rmw(client, REG_COMF, COMF_HREF_LOW, 0); + else + ret = ov6650_reg_rmw(client, REG_COMF, 0, COMF_HREF_LOW); + if (ret) + return ret; + + if (flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) + ret = ov6650_reg_rmw(client, REG_COMJ, COMJ_VSYNC_HIGH, 0); + else + ret = ov6650_reg_rmw(client, REG_COMJ, 0, COMJ_VSYNC_HIGH); + + return ret; +} + static struct v4l2_subdev_video_ops ov6650_video_ops = { .s_stream = ov6650_s_stream, .g_mbus_fmt = ov6650_g_fmt, @@ -1122,6 +928,8 @@ static struct v4l2_subdev_video_ops ov6650_video_ops = { .s_crop = ov6650_s_crop, .g_parm = ov6650_g_parm, .s_parm = ov6650_s_parm, + .g_mbus_config = ov6650_g_mbus_config, + .s_mbus_config = ov6650_s_mbus_config, }; static struct v4l2_subdev_ops ov6650_subdev_ops = { @@ -1136,16 +944,9 @@ static int ov6650_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct ov6650 *priv; - struct soc_camera_device *icd = client->dev.platform_data; - struct soc_camera_link *icl; + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); int ret; - if (!icd) { - dev_err(&client->dev, "Missing soc-camera data!\n"); - return -EINVAL; - } - - icl = to_soc_camera_link(icd); if (!icl) { dev_err(&client->dev, "Missing platform_data for driver\n"); return -EINVAL; @@ -1159,8 +960,46 @@ static int ov6650_probe(struct i2c_client *client, } v4l2_i2c_subdev_init(&priv->subdev, client, &ov6650_subdev_ops); + v4l2_ctrl_handler_init(&priv->hdl, 13); + v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + priv->autogain = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, 1); + priv->gain = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops, + V4L2_CID_GAIN, 0, 0x3f, 1, DEF_GAIN); + priv->autowb = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops, + V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1); + priv->blue = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops, + V4L2_CID_BLUE_BALANCE, 0, 0xff, 1, DEF_BLUE); + priv->red = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops, + V4L2_CID_RED_BALANCE, 0, 0xff, 1, DEF_RED); + v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops, + V4L2_CID_SATURATION, 0, 0xf, 1, 0x8); + v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops, + V4L2_CID_HUE, 0, HUE_MASK, 1, DEF_HUE); + v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 0xff, 1, 0x80); + priv->autoexposure = v4l2_ctrl_new_std_menu(&priv->hdl, + &ov6550_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, + V4L2_EXPOSURE_MANUAL, 0, V4L2_EXPOSURE_AUTO); + priv->exposure = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops, + V4L2_CID_EXPOSURE, 0, 0xff, 1, DEF_AECH); + v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops, + V4L2_CID_GAMMA, 0, 0xff, 1, 0x12); + + priv->subdev.ctrl_handler = &priv->hdl; + if (priv->hdl.error) { + int err = priv->hdl.error; - icd->ops = &ov6650_ops; + kfree(priv); + return err; + } + v4l2_ctrl_auto_cluster(2, &priv->autogain, 0, true); + v4l2_ctrl_auto_cluster(3, &priv->autowb, 0, true); + v4l2_ctrl_auto_cluster(2, &priv->autoexposure, + V4L2_EXPOSURE_MANUAL, true); priv->rect.left = DEF_HSTRT << 1; priv->rect.top = DEF_VSTRT << 1; @@ -1170,10 +1009,12 @@ static int ov6650_probe(struct i2c_client *client, priv->code = V4L2_MBUS_FMT_YUYV8_2X8; priv->colorspace = V4L2_COLORSPACE_JPEG; - ret = ov6650_video_probe(icd, client); + ret = ov6650_video_probe(client); + if (!ret) + ret = v4l2_ctrl_handler_setup(&priv->hdl); if (ret) { - icd->ops = NULL; + v4l2_ctrl_handler_free(&priv->hdl); kfree(priv); } @@ -1184,6 +1025,8 @@ static int ov6650_remove(struct i2c_client *client) { struct ov6650 *priv = to_ov6650(client); + v4l2_device_unregister_subdev(&priv->subdev); + v4l2_ctrl_handler_free(&priv->hdl); kfree(priv); return 0; } diff --git a/drivers/media/video/ov772x.c b/drivers/media/video/ov772x.c index 397870f076c..9f6ce3d8a29 100644 --- a/drivers/media/video/ov772x.c +++ b/drivers/media/video/ov772x.c @@ -20,12 +20,14 @@ #include <linux/i2c.h> #include <linux/slab.h> #include <linux/delay.h> +#include <linux/v4l2-mediabus.h> #include <linux/videodev2.h> + +#include <media/ov772x.h> +#include <media/soc_camera.h> +#include <media/v4l2-ctrls.h> #include <media/v4l2-chip-ident.h> #include <media/v4l2-subdev.h> -#include <media/soc_camera.h> -#include <media/soc_mediabus.h> -#include <media/ov772x.h> /* * register offset @@ -400,6 +402,7 @@ struct ov772x_win_size { struct ov772x_priv { struct v4l2_subdev subdev; + struct v4l2_ctrl_handler hdl; struct ov772x_camera_info *info; const struct ov772x_color_format *cfmt; const struct ov772x_win_size *win; @@ -517,36 +520,6 @@ static const struct ov772x_win_size ov772x_win_qvga = { .regs = ov772x_qvga_regs, }; -static const struct v4l2_queryctrl ov772x_controls[] = { - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Flip Vertically", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, - { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Flip Horizontally", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, - { - .id = V4L2_CID_BAND_STOP_FILTER, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Band-stop filter", - .minimum = 0, - .maximum = 256, - .step = 1, - .default_value = 0, - }, -}; - /* * general function */ @@ -620,75 +593,30 @@ static int ov772x_s_stream(struct v4l2_subdev *sd, int enable) return 0; } -static int ov772x_set_bus_param(struct soc_camera_device *icd, - unsigned long flags) -{ - return 0; -} - -static unsigned long ov772x_query_bus_param(struct soc_camera_device *icd) -{ - struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd)); - struct ov772x_priv *priv = i2c_get_clientdata(client); - struct soc_camera_link *icl = to_soc_camera_link(icd); - unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER | - SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH | - SOCAM_DATA_ACTIVE_HIGH; - - if (priv->info->flags & OV772X_FLAG_8BIT) - flags |= SOCAM_DATAWIDTH_8; - else - flags |= SOCAM_DATAWIDTH_10; - - return soc_camera_apply_sensor_flags(icl, flags); -} - -static int ov772x_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - struct ov772x_priv *priv = container_of(sd, struct ov772x_priv, subdev); - - switch (ctrl->id) { - case V4L2_CID_VFLIP: - ctrl->value = priv->flag_vflip; - break; - case V4L2_CID_HFLIP: - ctrl->value = priv->flag_hflip; - break; - case V4L2_CID_BAND_STOP_FILTER: - ctrl->value = priv->band_filter; - break; - } - return 0; -} - -static int ov772x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +static int ov772x_s_ctrl(struct v4l2_ctrl *ctrl) { + struct ov772x_priv *priv = container_of(ctrl->handler, + struct ov772x_priv, hdl); + struct v4l2_subdev *sd = &priv->subdev; struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ov772x_priv *priv = container_of(sd, struct ov772x_priv, subdev); int ret = 0; u8 val; switch (ctrl->id) { case V4L2_CID_VFLIP: - val = ctrl->value ? VFLIP_IMG : 0x00; - priv->flag_vflip = ctrl->value; + val = ctrl->val ? VFLIP_IMG : 0x00; + priv->flag_vflip = ctrl->val; if (priv->info->flags & OV772X_FLAG_VFLIP) val ^= VFLIP_IMG; - ret = ov772x_mask_set(client, COM3, VFLIP_IMG, val); - break; + return ov772x_mask_set(client, COM3, VFLIP_IMG, val); case V4L2_CID_HFLIP: - val = ctrl->value ? HFLIP_IMG : 0x00; - priv->flag_hflip = ctrl->value; + val = ctrl->val ? HFLIP_IMG : 0x00; + priv->flag_hflip = ctrl->val; if (priv->info->flags & OV772X_FLAG_HFLIP) val ^= HFLIP_IMG; - ret = ov772x_mask_set(client, COM3, HFLIP_IMG, val); - break; + return ov772x_mask_set(client, COM3, HFLIP_IMG, val); case V4L2_CID_BAND_STOP_FILTER: - if ((unsigned)ctrl->value > 256) - ctrl->value = 256; - if (ctrl->value == priv->band_filter) - break; - if (!ctrl->value) { + if (!ctrl->val) { /* Switch the filter off, it is on now */ ret = ov772x_mask_set(client, BDBASE, 0xff, 0xff); if (!ret) @@ -696,7 +624,7 @@ static int ov772x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) BNDF_ON_OFF, 0); } else { /* Switch the filter on, set AEC low limit */ - val = 256 - ctrl->value; + val = 256 - ctrl->val; ret = ov772x_mask_set(client, COM8, BNDF_ON_OFF, BNDF_ON_OFF); if (!ret) @@ -704,11 +632,11 @@ static int ov772x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) 0xff, val); } if (!ret) - priv->band_filter = ctrl->value; - break; + priv->band_filter = ctrl->val; + return ret; } - return ret; + return -EINVAL; } static int ov772x_g_chip_ident(struct v4l2_subdev *sd, @@ -822,13 +750,13 @@ static int ov772x_set_params(struct i2c_client *client, u32 *width, u32 *height, goto ov772x_set_fmt_error; ret = ov772x_mask_set(client, - EDGE_TRSHLD, EDGE_THRESHOLD_MASK, + EDGE_TRSHLD, OV772X_EDGE_THRESHOLD_MASK, priv->info->edgectrl.threshold); if (ret < 0) goto ov772x_set_fmt_error; ret = ov772x_mask_set(client, - EDGE_STRNGT, EDGE_STRENGTH_MASK, + EDGE_STRNGT, OV772X_EDGE_STRENGTH_MASK, priv->info->edgectrl.strength); if (ret < 0) goto ov772x_set_fmt_error; @@ -840,13 +768,13 @@ static int ov772x_set_params(struct i2c_client *client, u32 *width, u32 *height, * set upper and lower limit */ ret = ov772x_mask_set(client, - EDGE_UPPER, EDGE_UPPER_MASK, + EDGE_UPPER, OV772X_EDGE_UPPER_MASK, priv->info->edgectrl.upper); if (ret < 0) goto ov772x_set_fmt_error; ret = ov772x_mask_set(client, - EDGE_LOWER, EDGE_LOWER_MASK, + EDGE_LOWER, OV772X_EDGE_LOWER_MASK, priv->info->edgectrl.lower); if (ret < 0) goto ov772x_set_fmt_error; @@ -1025,17 +953,12 @@ static int ov772x_try_fmt(struct v4l2_subdev *sd, return 0; } -static int ov772x_video_probe(struct soc_camera_device *icd, - struct i2c_client *client) +static int ov772x_video_probe(struct i2c_client *client) { struct ov772x_priv *priv = to_ov772x(client); u8 pid, ver; const char *devname; - /* We must have a parent by now. And it cannot be a wrong one. */ - BUG_ON(!icd->parent || - to_soc_camera_host(icd->parent)->nr != icd->iface); - /* * check and show product ID and manufacturer ID */ @@ -1064,20 +987,14 @@ static int ov772x_video_probe(struct soc_camera_device *icd, ver, i2c_smbus_read_byte_data(client, MIDH), i2c_smbus_read_byte_data(client, MIDL)); - - return 0; + return v4l2_ctrl_handler_setup(&priv->hdl); } -static struct soc_camera_ops ov772x_ops = { - .set_bus_param = ov772x_set_bus_param, - .query_bus_param = ov772x_query_bus_param, - .controls = ov772x_controls, - .num_controls = ARRAY_SIZE(ov772x_controls), +static const struct v4l2_ctrl_ops ov772x_ctrl_ops = { + .s_ctrl = ov772x_s_ctrl, }; static struct v4l2_subdev_core_ops ov772x_subdev_core_ops = { - .g_ctrl = ov772x_g_ctrl, - .s_ctrl = ov772x_s_ctrl, .g_chip_ident = ov772x_g_chip_ident, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = ov772x_g_register, @@ -1095,6 +1012,21 @@ static int ov772x_enum_fmt(struct v4l2_subdev *sd, unsigned int index, return 0; } +static int ov772x_g_mbus_config(struct v4l2_subdev *sd, + struct v4l2_mbus_config *cfg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + + cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER | + V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH | + V4L2_MBUS_DATA_ACTIVE_HIGH; + cfg->type = V4L2_MBUS_PARALLEL; + cfg->flags = soc_camera_apply_board_flags(icl, cfg); + + return 0; +} + static struct v4l2_subdev_video_ops ov772x_subdev_video_ops = { .s_stream = ov772x_s_stream, .g_mbus_fmt = ov772x_g_fmt, @@ -1103,6 +1035,7 @@ static struct v4l2_subdev_video_ops ov772x_subdev_video_ops = { .cropcap = ov772x_cropcap, .g_crop = ov772x_g_crop, .enum_mbus_fmt = ov772x_enum_fmt, + .g_mbus_config = ov772x_g_mbus_config, }; static struct v4l2_subdev_ops ov772x_subdev_ops = { @@ -1117,20 +1050,15 @@ static struct v4l2_subdev_ops ov772x_subdev_ops = { static int ov772x_probe(struct i2c_client *client, const struct i2c_device_id *did) { - struct ov772x_priv *priv; - struct soc_camera_device *icd = client->dev.platform_data; - struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - struct soc_camera_link *icl; - int ret; - - if (!icd) { - dev_err(&client->dev, "OV772X: missing soc-camera data!\n"); - return -EINVAL; - } + struct ov772x_priv *priv; + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + int ret; - icl = to_soc_camera_link(icd); - if (!icl || !icl->priv) + if (!icl || !icl->priv) { + dev_err(&client->dev, "OV772X: missing platform data!\n"); return -EINVAL; + } if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { dev_err(&adapter->dev, @@ -1146,12 +1074,24 @@ static int ov772x_probe(struct i2c_client *client, priv->info = icl->priv; v4l2_i2c_subdev_init(&priv->subdev, client, &ov772x_subdev_ops); + v4l2_ctrl_handler_init(&priv->hdl, 3); + v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops, + V4L2_CID_BAND_STOP_FILTER, 0, 256, 1, 0); + priv->subdev.ctrl_handler = &priv->hdl; + if (priv->hdl.error) { + int err = priv->hdl.error; - icd->ops = &ov772x_ops; + kfree(priv); + return err; + } - ret = ov772x_video_probe(icd, client); + ret = ov772x_video_probe(client); if (ret) { - icd->ops = NULL; + v4l2_ctrl_handler_free(&priv->hdl); kfree(priv); } @@ -1161,9 +1101,9 @@ static int ov772x_probe(struct i2c_client *client, static int ov772x_remove(struct i2c_client *client) { struct ov772x_priv *priv = to_ov772x(client); - struct soc_camera_device *icd = client->dev.platform_data; - icd->ops = NULL; + v4l2_device_unregister_subdev(&priv->subdev); + v4l2_ctrl_handler_free(&priv->hdl); kfree(priv); return 0; } diff --git a/drivers/media/video/ov9640.c b/drivers/media/video/ov9640.c index 3681a6ff081..a4f99797eb5 100644 --- a/drivers/media/video/ov9640.c +++ b/drivers/media/video/ov9640.c @@ -24,10 +24,13 @@ #include <linux/i2c.h> #include <linux/slab.h> #include <linux/delay.h> +#include <linux/v4l2-mediabus.h> #include <linux/videodev2.h> + +#include <media/soc_camera.h> #include <media/v4l2-chip-ident.h> #include <media/v4l2-common.h> -#include <media/soc_camera.h> +#include <media/v4l2-ctrls.h> #include "ov9640.h" @@ -162,27 +165,6 @@ static enum v4l2_mbus_pixelcode ov9640_codes[] = { V4L2_MBUS_FMT_RGB565_2X8_LE, }; -static const struct v4l2_queryctrl ov9640_controls[] = { - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Flip Vertically", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, - { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Flip Horizontally", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, -}; - /* read a register */ static int ov9640_reg_read(struct i2c_client *client, u8 reg, u8 *val) { @@ -284,75 +266,25 @@ static int ov9640_s_stream(struct v4l2_subdev *sd, int enable) return 0; } -/* Alter bus settings on camera side */ -static int ov9640_set_bus_param(struct soc_camera_device *icd, - unsigned long flags) -{ - return 0; -} - -/* Request bus settings on camera side */ -static unsigned long ov9640_query_bus_param(struct soc_camera_device *icd) -{ - struct soc_camera_link *icl = to_soc_camera_link(icd); - - /* - * REVISIT: the camera probably can do 10 bit transfers, but I don't - * have those pins connected on my hardware. - */ - unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER | - SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH | - SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATAWIDTH_8; - - return soc_camera_apply_sensor_flags(icl, flags); -} - -/* Get status of additional camera capabilities */ -static int ov9640_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - struct ov9640_priv *priv = to_ov9640_sensor(sd); - - switch (ctrl->id) { - case V4L2_CID_VFLIP: - ctrl->value = priv->flag_vflip; - break; - case V4L2_CID_HFLIP: - ctrl->value = priv->flag_hflip; - break; - } - return 0; -} - /* Set status of additional camera capabilities */ -static int ov9640_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +static int ov9640_s_ctrl(struct v4l2_ctrl *ctrl) { - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ov9640_priv *priv = to_ov9640_sensor(sd); - - int ret = 0; + struct ov9640_priv *priv = container_of(ctrl->handler, struct ov9640_priv, hdl); + struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev); switch (ctrl->id) { case V4L2_CID_VFLIP: - priv->flag_vflip = ctrl->value; - if (ctrl->value) - ret = ov9640_reg_rmw(client, OV9640_MVFP, + if (ctrl->val) + return ov9640_reg_rmw(client, OV9640_MVFP, OV9640_MVFP_V, 0); - else - ret = ov9640_reg_rmw(client, OV9640_MVFP, - 0, OV9640_MVFP_V); - break; + return ov9640_reg_rmw(client, OV9640_MVFP, 0, OV9640_MVFP_V); case V4L2_CID_HFLIP: - priv->flag_hflip = ctrl->value; - if (ctrl->value) - ret = ov9640_reg_rmw(client, OV9640_MVFP, + if (ctrl->val) + return ov9640_reg_rmw(client, OV9640_MVFP, OV9640_MVFP_H, 0); - else - ret = ov9640_reg_rmw(client, OV9640_MVFP, - 0, OV9640_MVFP_H); - break; + return ov9640_reg_rmw(client, OV9640_MVFP, 0, OV9640_MVFP_H); } - - return ret; + return -EINVAL; } /* Get chip identification */ @@ -646,10 +578,7 @@ static int ov9640_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) return 0; } - - -static int ov9640_video_probe(struct soc_camera_device *icd, - struct i2c_client *client) +static int ov9640_video_probe(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); struct ov9640_priv *priv = to_ov9640_sensor(sd); @@ -657,29 +586,19 @@ static int ov9640_video_probe(struct soc_camera_device *icd, const char *devname; int ret = 0; - /* We must have a parent by now. And it cannot be a wrong one. */ - BUG_ON(!icd->parent || - to_soc_camera_host(icd->parent)->nr != icd->iface); - /* * check and show product ID and manufacturer ID */ ret = ov9640_reg_read(client, OV9640_PID, &pid); + if (!ret) + ret = ov9640_reg_read(client, OV9640_VER, &ver); + if (!ret) + ret = ov9640_reg_read(client, OV9640_MIDH, &midh); + if (!ret) + ret = ov9640_reg_read(client, OV9640_MIDL, &midl); if (ret) - goto err; - - ret = ov9640_reg_read(client, OV9640_VER, &ver); - if (ret) - goto err; - - ret = ov9640_reg_read(client, OV9640_MIDH, &midh); - if (ret) - goto err; - - ret = ov9640_reg_read(client, OV9640_MIDL, &midl); - if (ret) - goto err; + return ret; switch (VERSION(pid, ver)) { case OV9640_V2: @@ -693,27 +612,20 @@ static int ov9640_video_probe(struct soc_camera_device *icd, break; default: dev_err(&client->dev, "Product ID error %x:%x\n", pid, ver); - ret = -ENODEV; - goto err; + return -ENODEV; } dev_info(&client->dev, "%s Product ID %0x:%0x Manufacturer ID %x:%x\n", devname, pid, ver, midh, midl); -err: - return ret; + return v4l2_ctrl_handler_setup(&priv->hdl); } -static struct soc_camera_ops ov9640_ops = { - .set_bus_param = ov9640_set_bus_param, - .query_bus_param = ov9640_query_bus_param, - .controls = ov9640_controls, - .num_controls = ARRAY_SIZE(ov9640_controls), +static const struct v4l2_ctrl_ops ov9640_ctrl_ops = { + .s_ctrl = ov9640_s_ctrl, }; static struct v4l2_subdev_core_ops ov9640_core_ops = { - .g_ctrl = ov9640_g_ctrl, - .s_ctrl = ov9640_s_ctrl, .g_chip_ident = ov9640_g_chip_ident, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = ov9640_get_register, @@ -722,6 +634,22 @@ static struct v4l2_subdev_core_ops ov9640_core_ops = { }; +/* Request bus settings on camera side */ +static int ov9640_g_mbus_config(struct v4l2_subdev *sd, + struct v4l2_mbus_config *cfg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + + cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER | + V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH | + V4L2_MBUS_DATA_ACTIVE_HIGH; + cfg->type = V4L2_MBUS_PARALLEL; + cfg->flags = soc_camera_apply_board_flags(icl, cfg); + + return 0; +} + static struct v4l2_subdev_video_ops ov9640_video_ops = { .s_stream = ov9640_s_stream, .s_mbus_fmt = ov9640_s_fmt, @@ -729,7 +657,7 @@ static struct v4l2_subdev_video_ops ov9640_video_ops = { .enum_mbus_fmt = ov9640_enum_fmt, .cropcap = ov9640_cropcap, .g_crop = ov9640_g_crop, - + .g_mbus_config = ov9640_g_mbus_config, }; static struct v4l2_subdev_ops ov9640_subdev_ops = { @@ -744,16 +672,9 @@ static int ov9640_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct ov9640_priv *priv; - struct soc_camera_device *icd = client->dev.platform_data; - struct soc_camera_link *icl; + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); int ret; - if (!icd) { - dev_err(&client->dev, "Missing soc-camera data!\n"); - return -EINVAL; - } - - icl = to_soc_camera_link(icd); if (!icl) { dev_err(&client->dev, "Missing platform_data for driver\n"); return -EINVAL; @@ -768,12 +689,23 @@ static int ov9640_probe(struct i2c_client *client, v4l2_i2c_subdev_init(&priv->subdev, client, &ov9640_subdev_ops); - icd->ops = &ov9640_ops; + v4l2_ctrl_handler_init(&priv->hdl, 2); + v4l2_ctrl_new_std(&priv->hdl, &ov9640_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(&priv->hdl, &ov9640_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + priv->subdev.ctrl_handler = &priv->hdl; + if (priv->hdl.error) { + int err = priv->hdl.error; + + kfree(priv); + return err; + } - ret = ov9640_video_probe(icd, client); + ret = ov9640_video_probe(client); if (ret) { - icd->ops = NULL; + v4l2_ctrl_handler_free(&priv->hdl); kfree(priv); } @@ -785,6 +717,8 @@ static int ov9640_remove(struct i2c_client *client) struct v4l2_subdev *sd = i2c_get_clientdata(client); struct ov9640_priv *priv = to_ov9640_sensor(sd); + v4l2_device_unregister_subdev(&priv->subdev); + v4l2_ctrl_handler_free(&priv->hdl); kfree(priv); return 0; } diff --git a/drivers/media/video/ov9640.h b/drivers/media/video/ov9640.h index f8a51b70792..6b33a972c83 100644 --- a/drivers/media/video/ov9640.h +++ b/drivers/media/video/ov9640.h @@ -198,12 +198,10 @@ struct ov9640_reg { struct ov9640_priv { struct v4l2_subdev subdev; + struct v4l2_ctrl_handler hdl; int model; int revision; - - bool flag_vflip; - bool flag_hflip; }; #endif /* __DRIVERS_MEDIA_VIDEO_OV9640_H__ */ diff --git a/drivers/media/video/ov9740.c b/drivers/media/video/ov9740.c index edd1ffcca30..d9a9f7174f7 100644 --- a/drivers/media/video/ov9740.c +++ b/drivers/media/video/ov9740.c @@ -14,8 +14,11 @@ #include <linux/module.h> #include <linux/i2c.h> #include <linux/slab.h> -#include <media/v4l2-chip-ident.h> +#include <linux/v4l2-mediabus.h> + #include <media/soc_camera.h> +#include <media/v4l2-chip-ident.h> +#include <media/v4l2-ctrls.h> #define to_ov9740(sd) container_of(sd, struct ov9740_priv, subdev) @@ -192,6 +195,7 @@ struct ov9740_reg { struct ov9740_priv { struct v4l2_subdev subdev; + struct v4l2_ctrl_handler hdl; int ident; u16 model; @@ -392,27 +396,6 @@ static enum v4l2_mbus_pixelcode ov9740_codes[] = { V4L2_MBUS_FMT_YUYV8_2X8, }; -static const struct v4l2_queryctrl ov9740_controls[] = { - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Flip Vertically", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, - { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Flip Horizontally", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, -}; - /* read a register */ static int ov9740_reg_read(struct i2c_client *client, u16 reg, u8 *val) { @@ -560,25 +543,6 @@ static int ov9740_s_stream(struct v4l2_subdev *sd, int enable) return ret; } -/* Alter bus settings on camera side */ -static int ov9740_set_bus_param(struct soc_camera_device *icd, - unsigned long flags) -{ - return 0; -} - -/* Request bus settings on camera side */ -static unsigned long ov9740_query_bus_param(struct soc_camera_device *icd) -{ - struct soc_camera_link *icl = to_soc_camera_link(icd); - - unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER | - SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH | - SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATAWIDTH_8; - - return soc_camera_apply_sensor_flags(icl, flags); -} - /* select nearest higher resolution for capture */ static void ov9740_res_roundup(u32 *width, u32 *height) { @@ -788,36 +752,18 @@ static int ov9740_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) return 0; } -/* Get status of additional camera capabilities */ -static int ov9740_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - struct ov9740_priv *priv = to_ov9740(sd); - - switch (ctrl->id) { - case V4L2_CID_VFLIP: - ctrl->value = priv->flag_vflip; - break; - case V4L2_CID_HFLIP: - ctrl->value = priv->flag_hflip; - break; - default: - return -EINVAL; - } - - return 0; -} - /* Set status of additional camera capabilities */ -static int ov9740_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +static int ov9740_s_ctrl(struct v4l2_ctrl *ctrl) { - struct ov9740_priv *priv = to_ov9740(sd); + struct ov9740_priv *priv = + container_of(ctrl->handler, struct ov9740_priv, hdl); switch (ctrl->id) { case V4L2_CID_VFLIP: - priv->flag_vflip = ctrl->value; + priv->flag_vflip = ctrl->val; break; case V4L2_CID_HFLIP: - priv->flag_hflip = ctrl->value; + priv->flag_hflip = ctrl->val; break; default: return -EINVAL; @@ -890,18 +836,13 @@ static int ov9740_set_register(struct v4l2_subdev *sd, } #endif -static int ov9740_video_probe(struct soc_camera_device *icd, - struct i2c_client *client) +static int ov9740_video_probe(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); struct ov9740_priv *priv = to_ov9740(sd); u8 modelhi, modello; int ret; - /* We must have a parent by now. And it cannot be a wrong one. */ - BUG_ON(!icd->parent || - to_soc_camera_host(icd->parent)->nr != icd->iface); - /* * check and show product ID and manufacturer ID */ @@ -942,25 +883,33 @@ err: return ret; } -static struct soc_camera_ops ov9740_ops = { - .set_bus_param = ov9740_set_bus_param, - .query_bus_param = ov9740_query_bus_param, - .controls = ov9740_controls, - .num_controls = ARRAY_SIZE(ov9740_controls), -}; +/* Request bus settings on camera side */ +static int ov9740_g_mbus_config(struct v4l2_subdev *sd, + struct v4l2_mbus_config *cfg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + + cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER | + V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH | + V4L2_MBUS_DATA_ACTIVE_HIGH; + cfg->type = V4L2_MBUS_PARALLEL; + cfg->flags = soc_camera_apply_board_flags(icl, cfg); + + return 0; +} static struct v4l2_subdev_video_ops ov9740_video_ops = { - .s_stream = ov9740_s_stream, - .s_mbus_fmt = ov9740_s_fmt, - .try_mbus_fmt = ov9740_try_fmt, - .enum_mbus_fmt = ov9740_enum_fmt, - .cropcap = ov9740_cropcap, - .g_crop = ov9740_g_crop, + .s_stream = ov9740_s_stream, + .s_mbus_fmt = ov9740_s_fmt, + .try_mbus_fmt = ov9740_try_fmt, + .enum_mbus_fmt = ov9740_enum_fmt, + .cropcap = ov9740_cropcap, + .g_crop = ov9740_g_crop, + .g_mbus_config = ov9740_g_mbus_config, }; static struct v4l2_subdev_core_ops ov9740_core_ops = { - .g_ctrl = ov9740_g_ctrl, - .s_ctrl = ov9740_s_ctrl, .g_chip_ident = ov9740_g_chip_ident, .s_power = ov9740_s_power, #ifdef CONFIG_VIDEO_ADV_DEBUG @@ -974,6 +923,10 @@ static struct v4l2_subdev_ops ov9740_subdev_ops = { .video = &ov9740_video_ops, }; +static const struct v4l2_ctrl_ops ov9740_ctrl_ops = { + .s_ctrl = ov9740_s_ctrl, +}; + /* * i2c_driver function */ @@ -981,16 +934,9 @@ static int ov9740_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct ov9740_priv *priv; - struct soc_camera_device *icd = client->dev.platform_data; - struct soc_camera_link *icl; + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); int ret; - if (!icd) { - dev_err(&client->dev, "Missing soc-camera data!\n"); - return -EINVAL; - } - - icl = to_soc_camera_link(icd); if (!icl) { dev_err(&client->dev, "Missing platform_data for driver\n"); return -EINVAL; @@ -1003,12 +949,24 @@ static int ov9740_probe(struct i2c_client *client, } v4l2_i2c_subdev_init(&priv->subdev, client, &ov9740_subdev_ops); + v4l2_ctrl_handler_init(&priv->hdl, 13); + v4l2_ctrl_new_std(&priv->hdl, &ov9740_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(&priv->hdl, &ov9740_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + priv->subdev.ctrl_handler = &priv->hdl; + if (priv->hdl.error) { + int err = priv->hdl.error; - icd->ops = &ov9740_ops; + kfree(priv); + return err; + } - ret = ov9740_video_probe(icd, client); + ret = ov9740_video_probe(client); + if (!ret) + ret = v4l2_ctrl_handler_setup(&priv->hdl); if (ret < 0) { - icd->ops = NULL; + v4l2_ctrl_handler_free(&priv->hdl); kfree(priv); } @@ -1019,8 +977,9 @@ static int ov9740_remove(struct i2c_client *client) { struct ov9740_priv *priv = i2c_get_clientdata(client); + v4l2_device_unregister_subdev(&priv->subdev); + v4l2_ctrl_handler_free(&priv->hdl); kfree(priv); - return 0; } diff --git a/drivers/media/video/pvrusb2/Makefile b/drivers/media/video/pvrusb2/Makefile index de2fc14f043..c17f37d964a 100644 --- a/drivers/media/video/pvrusb2/Makefile +++ b/drivers/media/video/pvrusb2/Makefile @@ -16,7 +16,7 @@ pvrusb2-objs := pvrusb2-i2c-core.o \ obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2.o -EXTRA_CFLAGS += -Idrivers/media/video -EXTRA_CFLAGS += -Idrivers/media/common/tuners -EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core -EXTRA_CFLAGS += -Idrivers/media/dvb/frontends +ccflags-y += -Idrivers/media/video +ccflags-y += -Idrivers/media/common/tuners +ccflags-y += -Idrivers/media/dvb/dvb-core +ccflags-y += -Idrivers/media/dvb/frontends diff --git a/drivers/media/video/pvrusb2/pvrusb2-devattr.c b/drivers/media/video/pvrusb2/pvrusb2-devattr.c index e799331389b..c6da8f77e1a 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-devattr.c +++ b/drivers/media/video/pvrusb2/pvrusb2-devattr.c @@ -28,6 +28,7 @@ pvr2_device_desc structures. #include "pvrusb2-devattr.h" #include <linux/usb.h> +#include <linux/module.h> /* This is needed in order to pull in tuner type ids... */ #include <linux/i2c.h> #include <media/tuner.h> diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index e98d3821279..122b45760f0 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -21,6 +21,7 @@ #include <linux/errno.h> #include <linux/string.h> #include <linux/slab.h> +#include <linux/module.h> #include <linux/firmware.h> #include <linux/videodev2.h> #include <media/v4l2-common.h> @@ -2993,6 +2994,13 @@ static void pvr2_subdev_set_control(struct pvr2_hdw *hdw, int id, pvr2_subdev_set_control(hdw, id, #lab, (hdw)->lab##_val); \ } +int pvr2_hdw_get_detected_std(struct pvr2_hdw *hdw, v4l2_std_id *std) +{ + v4l2_device_call_all(&hdw->v4l2_dev, 0, + video, querystd, std); + return 0; +} + /* Execute whatever commands are required to update the state of all the sub-devices so that they match our current control values. */ static void pvr2_subdev_update(struct pvr2_hdw *hdw) diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/drivers/media/video/pvrusb2/pvrusb2-hdw.h index d7753ae9ff4..66546580b17 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.h +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.h @@ -214,6 +214,9 @@ struct pvr2_stream *pvr2_hdw_get_video_stream(struct pvr2_hdw *); int pvr2_hdw_get_stdenum_value(struct pvr2_hdw *hdw,struct v4l2_standard *std, unsigned int idx); +/* Get the detected video standard */ +int pvr2_hdw_get_detected_std(struct pvr2_hdw *hdw, v4l2_std_id *std); + /* Enable / disable retrieval of CPU firmware or prom contents. This must be enabled before pvr2_hdw_cpufw_get() will function. Note that doing this may prevent the device from running (and leaving this mode may diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c index e72d5103e77..885ce11f222 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c +++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c @@ -19,6 +19,7 @@ */ #include <linux/i2c.h> +#include <linux/module.h> #include <media/ir-kbd-i2c.h> #include "pvrusb2-i2c-core.h" #include "pvrusb2-hdw-internal.h" diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index e27f8ab7696..6d666174dbb 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -29,6 +29,7 @@ #include "pvrusb2-v4l2.h" #include "pvrusb2-ioread.h" #include <linux/videodev2.h> +#include <linux/module.h> #include <media/v4l2-dev.h> #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> @@ -227,6 +228,14 @@ static long pvr2_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) break; } + case VIDIOC_QUERYSTD: + { + v4l2_std_id *std = arg; + *std = V4L2_STD_ALL; + ret = pvr2_hdw_get_detected_std(hdw, std); + break; + } + case VIDIOC_G_STD: { int val = 0; diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c index 51ca3589b1b..01ff643e682 100644 --- a/drivers/media/video/pwc/pwc-if.c +++ b/drivers/media/video/pwc/pwc-if.c @@ -744,9 +744,9 @@ static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma) /***************************************************************************/ /* Videobuf2 operations */ -static int queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, - unsigned int *nplanes, unsigned long sizes[], - void *alloc_ctxs[]) +static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) { struct pwc_device *pdev = vb2_get_drv_priv(vq); @@ -816,7 +816,7 @@ static void buffer_queue(struct vb2_buffer *vb) spin_unlock_irqrestore(&pdev->queued_bufs_lock, flags); } -static int start_streaming(struct vb2_queue *vq) +static int start_streaming(struct vb2_queue *vq, unsigned int count) { struct pwc_device *pdev = vb2_get_drv_priv(vq); diff --git a/drivers/media/video/pwc/pwc-v4l.c b/drivers/media/video/pwc/pwc-v4l.c index e9a0e94b999..a10ff6b64ac 100644 --- a/drivers/media/video/pwc/pwc-v4l.c +++ b/drivers/media/video/pwc/pwc-v4l.c @@ -83,6 +83,7 @@ static const struct v4l2_ctrl_config pwc_contour_cfg = { .id = PWC_CID_CUSTOM(contour), .type = V4L2_CTRL_TYPE_INTEGER, .name = "Contour", + .flags = V4L2_CTRL_FLAG_SLIDER, .min = 0, .max = 63, .step = 1, @@ -206,8 +207,7 @@ int pwc_init_controls(struct pwc_device *pdev) pdev->blue_balance = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, V4L2_CID_BLUE_BALANCE, 0, 255, 1, def); - v4l2_ctrl_auto_cluster(3, &pdev->auto_white_balance, awb_manual, - pdev->auto_white_balance->cur.val == awb_auto); + v4l2_ctrl_auto_cluster(3, &pdev->auto_white_balance, awb_manual, true); /* autogain, gain */ r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, AGC_MODE_FORMATTER, &def); @@ -331,14 +331,14 @@ int pwc_init_controls(struct pwc_device *pdev) pdev->restore_user = v4l2_ctrl_new_custom(hdl, &pwc_restore_user_cfg, NULL); if (pdev->restore_user) - pdev->restore_user->flags = V4L2_CTRL_FLAG_UPDATE; + pdev->restore_user->flags |= V4L2_CTRL_FLAG_UPDATE; pdev->restore_factory = v4l2_ctrl_new_custom(hdl, &pwc_restore_factory_cfg, NULL); if (pdev->restore_factory) - pdev->restore_factory->flags = V4L2_CTRL_FLAG_UPDATE; + pdev->restore_factory->flags |= V4L2_CTRL_FLAG_UPDATE; - if (!pdev->features & FEATURE_MOTOR_PANTILT) + if (!(pdev->features & FEATURE_MOTOR_PANTILT)) return hdl->error; /* Motor pan / tilt / reset */ @@ -563,8 +563,10 @@ static int pwc_g_volatile_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { case V4L2_CID_AUTO_WHITE_BALANCE: - if (pdev->color_bal_valid && time_before(jiffies, - pdev->last_color_bal_update + HZ / 4)) { + if (pdev->color_bal_valid && + (pdev->auto_white_balance->val != awb_auto || + time_before(jiffies, + pdev->last_color_bal_update + HZ / 4))) { pdev->red_balance->val = pdev->last_red_balance; pdev->blue_balance->val = pdev->last_blue_balance; break; @@ -630,7 +632,7 @@ leave: static int pwc_set_awb(struct pwc_device *pdev) { - int ret = 0; + int ret; if (pdev->auto_white_balance->is_new) { ret = pwc_set_u8_ctrl(pdev, SET_CHROM_CTL, @@ -639,52 +641,34 @@ static int pwc_set_awb(struct pwc_device *pdev) if (ret) return ret; - /* Update val when coming from auto or going to a preset */ - if (pdev->red_balance->is_volatile || - pdev->auto_white_balance->val == awb_indoor || - pdev->auto_white_balance->val == awb_outdoor || - pdev->auto_white_balance->val == awb_fl) { - if (!pdev->red_balance->is_new) - pwc_get_u8_ctrl(pdev, GET_STATUS_CTL, - READ_RED_GAIN_FORMATTER, - &pdev->red_balance->val); - if (!pdev->blue_balance->is_new) - pwc_get_u8_ctrl(pdev, GET_STATUS_CTL, - READ_BLUE_GAIN_FORMATTER, - &pdev->blue_balance->val); - } - if (pdev->auto_white_balance->val == awb_auto) { - pdev->red_balance->is_volatile = true; - pdev->blue_balance->is_volatile = true; + if (pdev->auto_white_balance->val != awb_manual) pdev->color_bal_valid = false; /* Force cache update */ - } else { - pdev->red_balance->is_volatile = false; - pdev->blue_balance->is_volatile = false; - } } + if (pdev->auto_white_balance->val != awb_manual) + return 0; - if (ret == 0 && pdev->red_balance->is_new) { - if (pdev->auto_white_balance->val != awb_manual) - return -EBUSY; + if (pdev->red_balance->is_new) { ret = pwc_set_u8_ctrl(pdev, SET_CHROM_CTL, PRESET_MANUAL_RED_GAIN_FORMATTER, pdev->red_balance->val); + if (ret) + return ret; } - if (ret == 0 && pdev->blue_balance->is_new) { - if (pdev->auto_white_balance->val != awb_manual) - return -EBUSY; + if (pdev->blue_balance->is_new) { ret = pwc_set_u8_ctrl(pdev, SET_CHROM_CTL, PRESET_MANUAL_BLUE_GAIN_FORMATTER, pdev->blue_balance->val); + if (ret) + return ret; } - return ret; + return 0; } /* For CODEC2 models which have separate autogain and auto exposure */ static int pwc_set_autogain(struct pwc_device *pdev) { - int ret = 0; + int ret; if (pdev->autogain->is_new) { ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, @@ -692,27 +676,28 @@ static int pwc_set_autogain(struct pwc_device *pdev) pdev->autogain->val ? 0 : 0xff); if (ret) return ret; + if (pdev->autogain->val) pdev->gain_valid = false; /* Force cache update */ - else if (!pdev->gain->is_new) - pwc_get_u8_ctrl(pdev, GET_STATUS_CTL, - READ_AGC_FORMATTER, - &pdev->gain->val); } - if (ret == 0 && pdev->gain->is_new) { - if (pdev->autogain->val) - return -EBUSY; + + if (pdev->autogain->val) + return 0; + + if (pdev->gain->is_new) { ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, PRESET_AGC_FORMATTER, pdev->gain->val); + if (ret) + return ret; } - return ret; + return 0; } /* For CODEC2 models which have separate autogain and auto exposure */ static int pwc_set_exposure_auto(struct pwc_device *pdev) { - int ret = 0; + int ret; int is_auto = pdev->exposure_auto->val == V4L2_EXPOSURE_AUTO; if (pdev->exposure_auto->is_new) { @@ -721,27 +706,28 @@ static int pwc_set_exposure_auto(struct pwc_device *pdev) is_auto ? 0 : 0xff); if (ret) return ret; + if (is_auto) pdev->exposure_valid = false; /* Force cache update */ - else if (!pdev->exposure->is_new) - pwc_get_u16_ctrl(pdev, GET_STATUS_CTL, - READ_SHUTTER_FORMATTER, - &pdev->exposure->val); } - if (ret == 0 && pdev->exposure->is_new) { - if (is_auto) - return -EBUSY; + + if (is_auto) + return 0; + + if (pdev->exposure->is_new) { ret = pwc_set_u16_ctrl(pdev, SET_LUM_CTL, PRESET_SHUTTER_FORMATTER, pdev->exposure->val); + if (ret) + return ret; } - return ret; + return 0; } /* For CODEC3 models which have autogain controlling both gain and exposure */ static int pwc_set_autogain_expo(struct pwc_device *pdev) { - int ret = 0; + int ret; if (pdev->autogain->is_new) { ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, @@ -749,35 +735,32 @@ static int pwc_set_autogain_expo(struct pwc_device *pdev) pdev->autogain->val ? 0 : 0xff); if (ret) return ret; + if (pdev->autogain->val) { pdev->gain_valid = false; /* Force cache update */ pdev->exposure_valid = false; /* Force cache update */ - } else { - if (!pdev->gain->is_new) - pwc_get_u8_ctrl(pdev, GET_STATUS_CTL, - READ_AGC_FORMATTER, - &pdev->gain->val); - if (!pdev->exposure->is_new) - pwc_get_u16_ctrl(pdev, GET_STATUS_CTL, - READ_SHUTTER_FORMATTER, - &pdev->exposure->val); } } - if (ret == 0 && pdev->gain->is_new) { - if (pdev->autogain->val) - return -EBUSY; + + if (pdev->autogain->val) + return 0; + + if (pdev->gain->is_new) { ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, PRESET_AGC_FORMATTER, pdev->gain->val); + if (ret) + return ret; } - if (ret == 0 && pdev->exposure->is_new) { - if (pdev->autogain->val) - return -EBUSY; + + if (pdev->exposure->is_new) { ret = pwc_set_u16_ctrl(pdev, SET_LUM_CTL, PRESET_SHUTTER_FORMATTER, pdev->exposure->val); + if (ret) + return ret; } - return ret; + return 0; } static int pwc_set_motor(struct pwc_device *pdev) @@ -878,10 +861,6 @@ static int pwc_s_ctrl(struct v4l2_ctrl *ctrl) pdev->autocontour->val ? 0 : 0xff); } if (ret == 0 && pdev->contour->is_new) { - if (pdev->autocontour->val) { - ret = -EBUSY; - break; - } ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, PRESET_CONTOUR_FORMATTER, pdev->contour->val); @@ -1099,6 +1078,14 @@ static int pwc_enum_frameintervals(struct file *file, void *fh, return 0; } +static int pwc_log_status(struct file *file, void *priv) +{ + struct pwc_device *pdev = video_drvdata(file); + + v4l2_ctrl_handler_log_status(&pdev->ctrl_handler, PWC_NAME); + return 0; +} + static long pwc_default(struct file *file, void *fh, bool valid_prio, int cmd, void *arg) { @@ -1122,6 +1109,7 @@ const struct v4l2_ioctl_ops pwc_ioctl_ops = { .vidioc_dqbuf = pwc_dqbuf, .vidioc_streamon = pwc_streamon, .vidioc_streamoff = pwc_streamoff, + .vidioc_log_status = pwc_log_status, .vidioc_enum_framesizes = pwc_enum_framesizes, .vidioc_enum_frameintervals = pwc_enum_frameintervals, .vidioc_default = pwc_default, diff --git a/drivers/media/video/pxa_camera.c b/drivers/media/video/pxa_camera.c index d07df22a5ec..79fb22c89ae 100644 --- a/drivers/media/video/pxa_camera.c +++ b/drivers/media/video/pxa_camera.c @@ -214,6 +214,7 @@ struct pxa_camera_dev { unsigned long ciclk; unsigned long mclk; u32 mclk_divisor; + u16 width_flags; /* max 10 bits */ struct list_head capture; @@ -1020,37 +1021,20 @@ static int test_platform_param(struct pxa_camera_dev *pcdev, * quick capture interface supports both. */ *flags = (pcdev->platform_flags & PXA_CAMERA_MASTER ? - SOCAM_MASTER : SOCAM_SLAVE) | - SOCAM_HSYNC_ACTIVE_HIGH | - SOCAM_HSYNC_ACTIVE_LOW | - SOCAM_VSYNC_ACTIVE_HIGH | - SOCAM_VSYNC_ACTIVE_LOW | - SOCAM_DATA_ACTIVE_HIGH | - SOCAM_PCLK_SAMPLE_RISING | - SOCAM_PCLK_SAMPLE_FALLING; + V4L2_MBUS_MASTER : V4L2_MBUS_SLAVE) | + V4L2_MBUS_HSYNC_ACTIVE_HIGH | + V4L2_MBUS_HSYNC_ACTIVE_LOW | + V4L2_MBUS_VSYNC_ACTIVE_HIGH | + V4L2_MBUS_VSYNC_ACTIVE_LOW | + V4L2_MBUS_DATA_ACTIVE_HIGH | + V4L2_MBUS_PCLK_SAMPLE_RISING | + V4L2_MBUS_PCLK_SAMPLE_FALLING; /* If requested data width is supported by the platform, use it */ - switch (buswidth) { - case 10: - if (!(pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_10)) - return -EINVAL; - *flags |= SOCAM_DATAWIDTH_10; - break; - case 9: - if (!(pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_9)) - return -EINVAL; - *flags |= SOCAM_DATAWIDTH_9; - break; - case 8: - if (!(pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_8)) - return -EINVAL; - *flags |= SOCAM_DATAWIDTH_8; - break; - default: - return -EINVAL; - } + if ((1 << (buswidth - 1)) & pcdev->width_flags) + return 0; - return 0; + return -EINVAL; } static void pxa_camera_setup_cicr(struct soc_camera_device *icd, @@ -1070,12 +1054,12 @@ static void pxa_camera_setup_cicr(struct soc_camera_device *icd, * Datawidth is now guaranteed to be equal to one of the three values. * We fix bit-per-pixel equal to data-width... */ - switch (flags & SOCAM_DATAWIDTH_MASK) { - case SOCAM_DATAWIDTH_10: + switch (icd->current_fmt->host_fmt->bits_per_sample) { + case 10: dw = 4; bpp = 0x40; break; - case SOCAM_DATAWIDTH_9: + case 9: dw = 3; bpp = 0x20; break; @@ -1084,7 +1068,7 @@ static void pxa_camera_setup_cicr(struct soc_camera_device *icd, * Actually it can only be 8 now, * default is just to silence compiler warnings */ - case SOCAM_DATAWIDTH_8: + case 8: dw = 2; bpp = 0; } @@ -1093,11 +1077,11 @@ static void pxa_camera_setup_cicr(struct soc_camera_device *icd, cicr4 |= CICR4_PCLK_EN; if (pcdev->platform_flags & PXA_CAMERA_MCLK_EN) cicr4 |= CICR4_MCLK_EN; - if (flags & SOCAM_PCLK_SAMPLE_FALLING) + if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) cicr4 |= CICR4_PCP; - if (flags & SOCAM_HSYNC_ACTIVE_LOW) + if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) cicr4 |= CICR4_HSP; - if (flags & SOCAM_VSYNC_ACTIVE_LOW) + if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) cicr4 |= CICR4_VSP; cicr0 = __raw_readl(pcdev->base + CICR0); @@ -1151,9 +1135,11 @@ static void pxa_camera_setup_cicr(struct soc_camera_device *icd, static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) { + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct pxa_camera_dev *pcdev = ici->priv; - unsigned long bus_flags, camera_flags, common_flags; + struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,}; + unsigned long bus_flags, common_flags; int ret; struct pxa_cam *cam = icd->host_priv; @@ -1162,44 +1148,58 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) if (ret < 0) return ret; - camera_flags = icd->ops->query_bus_param(icd); - - common_flags = soc_camera_bus_param_compatible(camera_flags, bus_flags); - if (!common_flags) - return -EINVAL; + ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg); + if (!ret) { + common_flags = soc_mbus_config_compatible(&cfg, + bus_flags); + if (!common_flags) { + dev_warn(icd->parent, + "Flags incompatible: camera 0x%x, host 0x%lx\n", + cfg.flags, bus_flags); + return -EINVAL; + } + } else if (ret != -ENOIOCTLCMD) { + return ret; + } else { + common_flags = bus_flags; + } pcdev->channels = 1; /* Make choises, based on platform preferences */ - if ((common_flags & SOCAM_HSYNC_ACTIVE_HIGH) && - (common_flags & SOCAM_HSYNC_ACTIVE_LOW)) { + if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) && + (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) { if (pcdev->platform_flags & PXA_CAMERA_HSP) - common_flags &= ~SOCAM_HSYNC_ACTIVE_HIGH; + common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH; else - common_flags &= ~SOCAM_HSYNC_ACTIVE_LOW; + common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW; } - if ((common_flags & SOCAM_VSYNC_ACTIVE_HIGH) && - (common_flags & SOCAM_VSYNC_ACTIVE_LOW)) { + if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) && + (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) { if (pcdev->platform_flags & PXA_CAMERA_VSP) - common_flags &= ~SOCAM_VSYNC_ACTIVE_HIGH; + common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH; else - common_flags &= ~SOCAM_VSYNC_ACTIVE_LOW; + common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW; } - if ((common_flags & SOCAM_PCLK_SAMPLE_RISING) && - (common_flags & SOCAM_PCLK_SAMPLE_FALLING)) { + if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) && + (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) { if (pcdev->platform_flags & PXA_CAMERA_PCP) - common_flags &= ~SOCAM_PCLK_SAMPLE_RISING; + common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING; else - common_flags &= ~SOCAM_PCLK_SAMPLE_FALLING; + common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING; } - cam->flags = common_flags; - - ret = icd->ops->set_bus_param(icd, common_flags); - if (ret < 0) + cfg.flags = common_flags; + ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg); + if (ret < 0 && ret != -ENOIOCTLCMD) { + dev_dbg(icd->parent, "camera s_mbus_config(0x%lx) returned %d\n", + common_flags, ret); return ret; + } + + cam->flags = common_flags; pxa_camera_setup_cicr(icd, common_flags, pixfmt); @@ -1209,17 +1209,31 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) static int pxa_camera_try_bus_param(struct soc_camera_device *icd, unsigned char buswidth) { + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct pxa_camera_dev *pcdev = ici->priv; - unsigned long bus_flags, camera_flags; + struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,}; + unsigned long bus_flags, common_flags; int ret = test_platform_param(pcdev, buswidth, &bus_flags); if (ret < 0) return ret; - camera_flags = icd->ops->query_bus_param(icd); + ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg); + if (!ret) { + common_flags = soc_mbus_config_compatible(&cfg, + bus_flags); + if (!common_flags) { + dev_warn(icd->parent, + "Flags incompatible: camera 0x%x, host 0x%lx\n", + cfg.flags, bus_flags); + return -EINVAL; + } + } else if (ret == -ENOIOCTLCMD) { + ret = 0; + } - return soc_camera_bus_param_compatible(camera_flags, bus_flags) ? 0 : -EINVAL; + return ret; } static const struct soc_mbus_pixelfmt pxa_camera_formats[] = { @@ -1687,6 +1701,12 @@ static int __devinit pxa_camera_probe(struct platform_device *pdev) "data widths, using default 10 bit\n"); pcdev->platform_flags |= PXA_CAMERA_DATAWIDTH_10; } + if (pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_8) + pcdev->width_flags = 1 << 7; + if (pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_9) + pcdev->width_flags |= 1 << 8; + if (pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_10) + pcdev->width_flags |= 1 << 9; pcdev->mclk = pcdev->pdata->mclk_10khz * 10000; if (!pcdev->mclk) { dev_warn(&pdev->dev, diff --git a/drivers/media/video/rj54n1cb0c.c b/drivers/media/video/rj54n1cb0c.c index 847ccc067e8..9937386a3ba 100644 --- a/drivers/media/video/rj54n1cb0c.c +++ b/drivers/media/video/rj54n1cb0c.c @@ -11,13 +11,15 @@ #include <linux/delay.h> #include <linux/i2c.h> #include <linux/slab.h> +#include <linux/v4l2-mediabus.h> #include <linux/videodev2.h> +#include <linux/module.h> #include <media/rj54n1cb0c.h> #include <media/soc_camera.h> -#include <media/soc_mediabus.h> #include <media/v4l2-subdev.h> #include <media/v4l2-chip-ident.h> +#include <media/v4l2-ctrls.h> #define RJ54N1_DEV_CODE 0x0400 #define RJ54N1_DEV_CODE2 0x0401 @@ -148,6 +150,7 @@ struct rj54n1_clock_div { struct rj54n1 { struct v4l2_subdev subdev; + struct v4l2_ctrl_handler hdl; struct rj54n1_clock_div clk_div; const struct rj54n1_datafmt *fmt; struct v4l2_rect rect; /* Sensor window */ @@ -499,31 +502,6 @@ static int rj54n1_s_stream(struct v4l2_subdev *sd, int enable) return reg_set(client, RJ54N1_STILL_CONTROL, (!enable) << 7, 0x80); } -static int rj54n1_set_bus_param(struct soc_camera_device *icd, - unsigned long flags) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - /* Figures 2.5-1 to 2.5-3 - default falling pixclk edge */ - - if (flags & SOCAM_PCLK_SAMPLE_RISING) - return reg_write(client, RJ54N1_OUT_SIGPO, 1 << 4); - else - return reg_write(client, RJ54N1_OUT_SIGPO, 0); -} - -static unsigned long rj54n1_query_bus_param(struct soc_camera_device *icd) -{ - struct soc_camera_link *icl = to_soc_camera_link(icd); - const unsigned long flags = - SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING | - SOCAM_MASTER | SOCAM_DATAWIDTH_8 | - SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH | - SOCAM_DATA_ACTIVE_HIGH; - - return soc_camera_apply_sensor_flags(icl, flags); -} - static int rj54n1_set_rect(struct i2c_client *client, u16 reg_x, u16 reg_y, u16 reg_xy, u32 width, u32 height) @@ -1202,134 +1180,51 @@ static int rj54n1_s_register(struct v4l2_subdev *sd, } #endif -static const struct v4l2_queryctrl rj54n1_controls[] = { - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Flip Vertically", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Flip Horizontally", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gain", - .minimum = 0, - .maximum = 127, - .step = 1, - .default_value = 66, - .flags = V4L2_CTRL_FLAG_SLIDER, - }, { - .id = V4L2_CID_AUTO_WHITE_BALANCE, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Auto white balance", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1, - }, -}; - -static struct soc_camera_ops rj54n1_ops = { - .set_bus_param = rj54n1_set_bus_param, - .query_bus_param = rj54n1_query_bus_param, - .controls = rj54n1_controls, - .num_controls = ARRAY_SIZE(rj54n1_controls), -}; - -static int rj54n1_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +static int rj54n1_s_ctrl(struct v4l2_ctrl *ctrl) { + struct rj54n1 *rj54n1 = container_of(ctrl->handler, struct rj54n1, hdl); + struct v4l2_subdev *sd = &rj54n1->subdev; struct i2c_client *client = v4l2_get_subdevdata(sd); - struct rj54n1 *rj54n1 = to_rj54n1(client); int data; switch (ctrl->id) { case V4L2_CID_VFLIP: - data = reg_read(client, RJ54N1_MIRROR_STILL_MODE); - if (data < 0) - return -EIO; - ctrl->value = !(data & 1); - break; - case V4L2_CID_HFLIP: - data = reg_read(client, RJ54N1_MIRROR_STILL_MODE); - if (data < 0) - return -EIO; - ctrl->value = !(data & 2); - break; - case V4L2_CID_GAIN: - data = reg_read(client, RJ54N1_Y_GAIN); - if (data < 0) - return -EIO; - - ctrl->value = data / 2; - break; - case V4L2_CID_AUTO_WHITE_BALANCE: - ctrl->value = rj54n1->auto_wb; - break; - } - - return 0; -} - -static int rj54n1_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - int data; - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct rj54n1 *rj54n1 = to_rj54n1(client); - const struct v4l2_queryctrl *qctrl; - - qctrl = soc_camera_find_qctrl(&rj54n1_ops, ctrl->id); - if (!qctrl) - return -EINVAL; - - switch (ctrl->id) { - case V4L2_CID_VFLIP: - if (ctrl->value) + if (ctrl->val) data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 0, 1); else data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 1, 1); if (data < 0) return -EIO; - break; + return 0; case V4L2_CID_HFLIP: - if (ctrl->value) + if (ctrl->val) data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 0, 2); else data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 2, 2); if (data < 0) return -EIO; - break; + return 0; case V4L2_CID_GAIN: - if (ctrl->value > qctrl->maximum || - ctrl->value < qctrl->minimum) - return -EINVAL; - else if (reg_write(client, RJ54N1_Y_GAIN, ctrl->value * 2) < 0) + if (reg_write(client, RJ54N1_Y_GAIN, ctrl->val * 2) < 0) return -EIO; - break; + return 0; case V4L2_CID_AUTO_WHITE_BALANCE: /* Auto WB area - whole image */ - if (reg_set(client, RJ54N1_WB_SEL_WEIGHT_I, ctrl->value << 7, + if (reg_set(client, RJ54N1_WB_SEL_WEIGHT_I, ctrl->val << 7, 0x80) < 0) return -EIO; - rj54n1->auto_wb = ctrl->value; - break; + rj54n1->auto_wb = ctrl->val; + return 0; } - return 0; + return -EINVAL; } +static const struct v4l2_ctrl_ops rj54n1_ctrl_ops = { + .s_ctrl = rj54n1_s_ctrl, +}; + static struct v4l2_subdev_core_ops rj54n1_subdev_core_ops = { - .g_ctrl = rj54n1_g_ctrl, - .s_ctrl = rj54n1_s_ctrl, .g_chip_ident = rj54n1_g_chip_ident, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = rj54n1_g_register, @@ -1337,6 +1232,36 @@ static struct v4l2_subdev_core_ops rj54n1_subdev_core_ops = { #endif }; +static int rj54n1_g_mbus_config(struct v4l2_subdev *sd, + struct v4l2_mbus_config *cfg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + + cfg->flags = + V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | + V4L2_MBUS_MASTER | V4L2_MBUS_DATA_ACTIVE_HIGH | + V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH; + cfg->type = V4L2_MBUS_PARALLEL; + cfg->flags = soc_camera_apply_board_flags(icl, cfg); + + return 0; +} + +static int rj54n1_s_mbus_config(struct v4l2_subdev *sd, + const struct v4l2_mbus_config *cfg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + + /* Figures 2.5-1 to 2.5-3 - default falling pixclk edge */ + if (soc_camera_apply_board_flags(icl, cfg) & + V4L2_MBUS_PCLK_SAMPLE_RISING) + return reg_write(client, RJ54N1_OUT_SIGPO, 1 << 4); + else + return reg_write(client, RJ54N1_OUT_SIGPO, 0); +} + static struct v4l2_subdev_video_ops rj54n1_subdev_video_ops = { .s_stream = rj54n1_s_stream, .s_mbus_fmt = rj54n1_s_fmt, @@ -1346,6 +1271,8 @@ static struct v4l2_subdev_video_ops rj54n1_subdev_video_ops = { .g_crop = rj54n1_g_crop, .s_crop = rj54n1_s_crop, .cropcap = rj54n1_cropcap, + .g_mbus_config = rj54n1_g_mbus_config, + .s_mbus_config = rj54n1_s_mbus_config, }; static struct v4l2_subdev_ops rj54n1_subdev_ops = { @@ -1357,17 +1284,12 @@ static struct v4l2_subdev_ops rj54n1_subdev_ops = { * Interface active, can use i2c. If it fails, it can indeed mean, that * this wasn't our capture interface, so, we wait for the right one */ -static int rj54n1_video_probe(struct soc_camera_device *icd, - struct i2c_client *client, +static int rj54n1_video_probe(struct i2c_client *client, struct rj54n1_pdata *priv) { int data1, data2; int ret; - /* We must have a parent by now. And it cannot be a wrong one. */ - BUG_ON(!icd->parent || - to_soc_camera_host(icd->parent)->nr != icd->iface); - /* Read out the chip version register */ data1 = reg_read(client, RJ54N1_DEV_CODE); data2 = reg_read(client, RJ54N1_DEV_CODE2); @@ -1395,18 +1317,11 @@ static int rj54n1_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct rj54n1 *rj54n1; - struct soc_camera_device *icd = client->dev.platform_data; + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - struct soc_camera_link *icl; struct rj54n1_pdata *rj54n1_priv; int ret; - if (!icd) { - dev_err(&client->dev, "RJ54N1CB0C: missing soc-camera data!\n"); - return -EINVAL; - } - - icl = to_soc_camera_link(icd); if (!icl || !icl->priv) { dev_err(&client->dev, "RJ54N1CB0C: missing platform data!\n"); return -EINVAL; @@ -1425,8 +1340,22 @@ static int rj54n1_probe(struct i2c_client *client, return -ENOMEM; v4l2_i2c_subdev_init(&rj54n1->subdev, client, &rj54n1_subdev_ops); + v4l2_ctrl_handler_init(&rj54n1->hdl, 4); + v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops, + V4L2_CID_GAIN, 0, 127, 1, 66); + v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops, + V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1); + rj54n1->subdev.ctrl_handler = &rj54n1->hdl; + if (rj54n1->hdl.error) { + int err = rj54n1->hdl.error; - icd->ops = &rj54n1_ops; + kfree(rj54n1); + return err; + } rj54n1->clk_div = clk_div; rj54n1->rect.left = RJ54N1_COLUMN_SKIP; @@ -1440,25 +1369,24 @@ static int rj54n1_probe(struct i2c_client *client, rj54n1->tgclk_mhz = (rj54n1_priv->mclk_freq / PLL_L * PLL_N) / (clk_div.ratio_tg + 1) / (clk_div.ratio_t + 1); - ret = rj54n1_video_probe(icd, client, rj54n1_priv); + ret = rj54n1_video_probe(client, rj54n1_priv); if (ret < 0) { - icd->ops = NULL; + v4l2_ctrl_handler_free(&rj54n1->hdl); kfree(rj54n1); return ret; } - - return ret; + return v4l2_ctrl_handler_setup(&rj54n1->hdl); } static int rj54n1_remove(struct i2c_client *client) { struct rj54n1 *rj54n1 = to_rj54n1(client); - struct soc_camera_device *icd = client->dev.platform_data; - struct soc_camera_link *icl = to_soc_camera_link(icd); + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); - icd->ops = NULL; + v4l2_device_unregister_subdev(&rj54n1->subdev); if (icl->free_bus) icl->free_bus(icl); + v4l2_ctrl_handler_free(&rj54n1->hdl); kfree(rj54n1); return 0; diff --git a/drivers/media/video/s5k6aa.c b/drivers/media/video/s5k6aa.c new file mode 100644 index 00000000000..0df7f2a4181 --- /dev/null +++ b/drivers/media/video/s5k6aa.c @@ -0,0 +1,1681 @@ +/* + * Driver for Samsung S5K6AAFX SXGA 1/6" 1.3M CMOS Image Sensor + * with embedded SoC ISP. + * + * Copyright (C) 2011, Samsung Electronics Co., Ltd. + * Sylwester Nawrocki <s.nawrocki@samsung.com> + * + * Based on a driver authored by Dongsoo Nathaniel Kim. + * Copyright (C) 2009, Dongsoo Nathaniel Kim <dongsoo45.kim@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/media.h> +#include <linux/module.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> + +#include <media/media-entity.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-subdev.h> +#include <media/v4l2-mediabus.h> +#include <media/s5k6aa.h> + +static int debug; +module_param(debug, int, 0644); + +#define DRIVER_NAME "S5K6AA" + +/* The token to indicate array termination */ +#define S5K6AA_TERM 0xffff +#define S5K6AA_OUT_WIDTH_DEF 640 +#define S5K6AA_OUT_HEIGHT_DEF 480 +#define S5K6AA_WIN_WIDTH_MAX 1280 +#define S5K6AA_WIN_HEIGHT_MAX 1024 +#define S5K6AA_WIN_WIDTH_MIN 8 +#define S5K6AA_WIN_HEIGHT_MIN 8 + +/* + * H/W register Interface (0xD0000000 - 0xD0000FFF) + */ +#define AHB_MSB_ADDR_PTR 0xfcfc +#define GEN_REG_OFFSH 0xd000 +#define REG_CMDWR_ADDRH 0x0028 +#define REG_CMDWR_ADDRL 0x002a +#define REG_CMDRD_ADDRH 0x002c +#define REG_CMDRD_ADDRL 0x002e +#define REG_CMDBUF0_ADDR 0x0f12 +#define REG_CMDBUF1_ADDR 0x0f10 + +/* + * Host S/W Register interface (0x70000000 - 0x70002000) + * The value of the two most significant address bytes is 0x7000, + * (HOST_SWIF_OFFS_H). The register addresses below specify 2 LSBs. + */ +#define HOST_SWIF_OFFSH 0x7000 + +/* Initialization parameters */ +/* Master clock frequency in KHz */ +#define REG_I_INCLK_FREQ_L 0x01b8 +#define REG_I_INCLK_FREQ_H 0x01ba +#define MIN_MCLK_FREQ_KHZ 6000U +#define MAX_MCLK_FREQ_KHZ 27000U +#define REG_I_USE_NPVI_CLOCKS 0x01c6 +#define REG_I_USE_NMIPI_CLOCKS 0x01c8 + +/* Clock configurations, n = 0..2. REG_I_* frequency unit is 4 kHz. */ +#define REG_I_OPCLK_4KHZ(n) ((n) * 6 + 0x01cc) +#define REG_I_MIN_OUTRATE_4KHZ(n) ((n) * 6 + 0x01ce) +#define REG_I_MAX_OUTRATE_4KHZ(n) ((n) * 6 + 0x01d0) +#define SYS_PLL_OUT_FREQ (48000000 / 4000) +#define PCLK_FREQ_MIN (24000000 / 4000) +#define PCLK_FREQ_MAX (48000000 / 4000) +#define REG_I_INIT_PARAMS_UPDATED 0x01e0 +#define REG_I_ERROR_INFO 0x01e2 + +/* General purpose parameters */ +#define REG_USER_BRIGHTNESS 0x01e4 +#define REG_USER_CONTRAST 0x01e6 +#define REG_USER_SATURATION 0x01e8 +#define REG_USER_SHARPBLUR 0x01ea + +#define REG_G_SPEC_EFFECTS 0x01ee +#define REG_G_ENABLE_PREV 0x01f0 +#define REG_G_ENABLE_PREV_CHG 0x01f2 +#define REG_G_NEW_CFG_SYNC 0x01f8 +#define REG_G_PREVZOOM_IN_WIDTH 0x020a +#define REG_G_PREVZOOM_IN_HEIGHT 0x020c +#define REG_G_PREVZOOM_IN_XOFFS 0x020e +#define REG_G_PREVZOOM_IN_YOFFS 0x0210 +#define REG_G_INPUTS_CHANGE_REQ 0x021a +#define REG_G_ACTIVE_PREV_CFG 0x021c +#define REG_G_PREV_CFG_CHG 0x021e +#define REG_G_PREV_OPEN_AFTER_CH 0x0220 +#define REG_G_PREV_CFG_ERROR 0x0222 + +/* Preview control section. n = 0...4. */ +#define PREG(n, x) ((n) * 0x26 + x) +#define REG_P_OUT_WIDTH(n) PREG(n, 0x0242) +#define REG_P_OUT_HEIGHT(n) PREG(n, 0x0244) +#define REG_P_FMT(n) PREG(n, 0x0246) +#define REG_P_MAX_OUT_RATE(n) PREG(n, 0x0248) +#define REG_P_MIN_OUT_RATE(n) PREG(n, 0x024a) +#define REG_P_PVI_MASK(n) PREG(n, 0x024c) +#define REG_P_CLK_INDEX(n) PREG(n, 0x024e) +#define REG_P_FR_RATE_TYPE(n) PREG(n, 0x0250) +#define FR_RATE_DYNAMIC 0 +#define FR_RATE_FIXED 1 +#define FR_RATE_FIXED_ACCURATE 2 +#define REG_P_FR_RATE_Q_TYPE(n) PREG(n, 0x0252) +#define FR_RATE_Q_BEST_FRRATE 1 /* Binning enabled */ +#define FR_RATE_Q_BEST_QUALITY 2 /* Binning disabled */ +/* Frame period in 0.1 ms units */ +#define REG_P_MAX_FR_TIME(n) PREG(n, 0x0254) +#define REG_P_MIN_FR_TIME(n) PREG(n, 0x0256) +/* Conversion to REG_P_[MAX/MIN]_FR_TIME value; __t: time in us */ +#define US_TO_FR_TIME(__t) ((__t) / 100) +#define S5K6AA_MIN_FR_TIME 33300 /* us */ +#define S5K6AA_MAX_FR_TIME 650000 /* us */ +#define S5K6AA_MAX_HIGHRES_FR_TIME 666 /* x100 us */ +/* The below 5 registers are for "device correction" values */ +#define REG_P_COLORTEMP(n) PREG(n, 0x025e) +#define REG_P_PREV_MIRROR(n) PREG(n, 0x0262) + +/* Extended image property controls */ +/* Exposure time in 10 us units */ +#define REG_SF_USR_EXPOSURE_L 0x03c6 +#define REG_SF_USR_EXPOSURE_H 0x03c8 +#define REG_SF_USR_EXPOSURE_CHG 0x03ca +#define REG_SF_USR_TOT_GAIN 0x03cc +#define REG_SF_USR_TOT_GAIN_CHG 0x03ce +#define REG_SF_RGAIN 0x03d0 +#define REG_SF_RGAIN_CHG 0x03d2 +#define REG_SF_GGAIN 0x03d4 +#define REG_SF_GGAIN_CHG 0x03d6 +#define REG_SF_BGAIN 0x03d8 +#define REG_SF_BGAIN_CHG 0x03da +#define REG_SF_FLICKER_QUANT 0x03dc +#define REG_SF_FLICKER_QUANT_CHG 0x03de + +/* Output interface (parallel/MIPI) setup */ +#define REG_OIF_EN_MIPI_LANES 0x03fa +#define REG_OIF_EN_PACKETS 0x03fc +#define REG_OIF_CFG_CHG 0x03fe + +/* Auto-algorithms enable mask */ +#define REG_DBG_AUTOALG_EN 0x0400 +#define AALG_ALL_EN_MASK (1 << 0) +#define AALG_AE_EN_MASK (1 << 1) +#define AALG_DIVLEI_EN_MASK (1 << 2) +#define AALG_WB_EN_MASK (1 << 3) +#define AALG_FLICKER_EN_MASK (1 << 5) +#define AALG_FIT_EN_MASK (1 << 6) +#define AALG_WRHW_EN_MASK (1 << 7) + +/* Firmware revision information */ +#define REG_FW_APIVER 0x012e +#define S5K6AAFX_FW_APIVER 0x0001 +#define REG_FW_REVISION 0x0130 + +/* For now we use only one user configuration register set */ +#define S5K6AA_MAX_PRESETS 1 + +static const char * const s5k6aa_supply_names[] = { + "vdd_core", /* Digital core supply 1.5V (1.4V to 1.6V) */ + "vdda", /* Analog power supply 2.8V (2.6V to 3.0V) */ + "vdd_reg", /* Regulator input power 1.8V (1.7V to 1.9V) + or 2.8V (2.6V to 3.0) */ + "vddio", /* I/O supply 1.8V (1.65V to 1.95V) + or 2.8V (2.5V to 3.1V) */ +}; +#define S5K6AA_NUM_SUPPLIES ARRAY_SIZE(s5k6aa_supply_names) + +enum s5k6aa_gpio_id { + STBY, + RST, + GPIO_NUM, +}; + +struct s5k6aa_regval { + u16 addr; + u16 val; +}; + +struct s5k6aa_pixfmt { + enum v4l2_mbus_pixelcode code; + u32 colorspace; + /* REG_P_FMT(x) register value */ + u16 reg_p_fmt; +}; + +struct s5k6aa_preset { + /* output pixel format and resolution */ + struct v4l2_mbus_framefmt mbus_fmt; + u8 clk_id; + u8 index; +}; + +struct s5k6aa_ctrls { + struct v4l2_ctrl_handler handler; + /* Auto / manual white balance cluster */ + struct v4l2_ctrl *awb; + struct v4l2_ctrl *gain_red; + struct v4l2_ctrl *gain_blue; + struct v4l2_ctrl *gain_green; + /* Mirror cluster */ + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; + /* Auto exposure / manual exposure and gain cluster */ + struct v4l2_ctrl *auto_exp; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *gain; +}; + +struct s5k6aa_interval { + u16 reg_fr_time; + struct v4l2_fract interval; + /* Maximum rectangle for the interval */ + struct v4l2_frmsize_discrete size; +}; + +struct s5k6aa { + struct v4l2_subdev sd; + struct media_pad pad; + + enum v4l2_mbus_type bus_type; + u8 mipi_lanes; + + int (*s_power)(int enable); + struct regulator_bulk_data supplies[S5K6AA_NUM_SUPPLIES]; + struct s5k6aa_gpio gpio[GPIO_NUM]; + + /* external master clock frequency */ + unsigned long mclk_frequency; + /* ISP internal master clock frequency */ + u16 clk_fop; + /* output pixel clock frequency range */ + u16 pclk_fmin; + u16 pclk_fmax; + + unsigned int inv_hflip:1; + unsigned int inv_vflip:1; + + /* protects the struct members below */ + struct mutex lock; + + /* sensor matrix scan window */ + struct v4l2_rect ccd_rect; + + struct s5k6aa_ctrls ctrls; + struct s5k6aa_preset presets[S5K6AA_MAX_PRESETS]; + struct s5k6aa_preset *preset; + const struct s5k6aa_interval *fiv; + + unsigned int streaming:1; + unsigned int apply_cfg:1; + unsigned int apply_crop:1; + unsigned int power; +}; + +static struct s5k6aa_regval s5k6aa_analog_config[] = { + /* Analog settings */ + { 0x112a, 0x0000 }, { 0x1132, 0x0000 }, + { 0x113e, 0x0000 }, { 0x115c, 0x0000 }, + { 0x1164, 0x0000 }, { 0x1174, 0x0000 }, + { 0x1178, 0x0000 }, { 0x077a, 0x0000 }, + { 0x077c, 0x0000 }, { 0x077e, 0x0000 }, + { 0x0780, 0x0000 }, { 0x0782, 0x0000 }, + { 0x0784, 0x0000 }, { 0x0786, 0x0000 }, + { 0x0788, 0x0000 }, { 0x07a2, 0x0000 }, + { 0x07a4, 0x0000 }, { 0x07a6, 0x0000 }, + { 0x07a8, 0x0000 }, { 0x07b6, 0x0000 }, + { 0x07b8, 0x0002 }, { 0x07ba, 0x0004 }, + { 0x07bc, 0x0004 }, { 0x07be, 0x0005 }, + { 0x07c0, 0x0005 }, { S5K6AA_TERM, 0 }, +}; + +/* TODO: Add RGB888 and Bayer format */ +static const struct s5k6aa_pixfmt s5k6aa_formats[] = { + { V4L2_MBUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_JPEG, 5 }, + /* range 16-240 */ + { V4L2_MBUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_REC709, 6 }, + { V4L2_MBUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_JPEG, 0 }, +}; + +static const struct s5k6aa_interval s5k6aa_intervals[] = { + { 1000, {10000, 1000000}, {1280, 1024} }, /* 10 fps */ + { 666, {15000, 1000000}, {1280, 1024} }, /* 15 fps */ + { 500, {20000, 1000000}, {1280, 720} }, /* 20 fps */ + { 400, {25000, 1000000}, {640, 480} }, /* 25 fps */ + { 333, {33300, 1000000}, {640, 480} }, /* 30 fps */ +}; + +#define S5K6AA_INTERVAL_DEF_INDEX 1 /* 15 fps */ + +static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct s5k6aa, ctrls.handler)->sd; +} + +static inline struct s5k6aa *to_s5k6aa(struct v4l2_subdev *sd) +{ + return container_of(sd, struct s5k6aa, sd); +} + +/* Set initial values for all preview presets */ +static void s5k6aa_presets_data_init(struct s5k6aa *s5k6aa) +{ + struct s5k6aa_preset *preset = &s5k6aa->presets[0]; + int i; + + for (i = 0; i < S5K6AA_MAX_PRESETS; i++) { + preset->mbus_fmt.width = S5K6AA_OUT_WIDTH_DEF; + preset->mbus_fmt.height = S5K6AA_OUT_HEIGHT_DEF; + preset->mbus_fmt.code = s5k6aa_formats[0].code; + preset->index = i; + preset->clk_id = 0; + preset++; + } + + s5k6aa->fiv = &s5k6aa_intervals[S5K6AA_INTERVAL_DEF_INDEX]; + s5k6aa->preset = &s5k6aa->presets[0]; +} + +static int s5k6aa_i2c_read(struct i2c_client *client, u16 addr, u16 *val) +{ + u8 wbuf[2] = {addr >> 8, addr & 0xFF}; + struct i2c_msg msg[2]; + u8 rbuf[2]; + int ret; + + msg[0].addr = client->addr; + msg[0].flags = 0; + msg[0].len = 2; + msg[0].buf = wbuf; + + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = 2; + msg[1].buf = rbuf; + + ret = i2c_transfer(client->adapter, msg, 2); + *val = be16_to_cpu(*((u16 *)rbuf)); + + v4l2_dbg(3, debug, client, "i2c_read: 0x%04X : 0x%04x\n", addr, *val); + + return ret == 2 ? 0 : ret; +} + +static int s5k6aa_i2c_write(struct i2c_client *client, u16 addr, u16 val) +{ + u8 buf[4] = {addr >> 8, addr & 0xFF, val >> 8, val & 0xFF}; + + int ret = i2c_master_send(client, buf, 4); + v4l2_dbg(3, debug, client, "i2c_write: 0x%04X : 0x%04x\n", addr, val); + + return ret == 4 ? 0 : ret; +} + +/* The command register write, assumes Command_Wr_addH = 0x7000. */ +static int s5k6aa_write(struct i2c_client *c, u16 addr, u16 val) +{ + int ret = s5k6aa_i2c_write(c, REG_CMDWR_ADDRL, addr); + if (ret) + return ret; + return s5k6aa_i2c_write(c, REG_CMDBUF0_ADDR, val); +} + +/* The command register read, assumes Command_Rd_addH = 0x7000. */ +static int s5k6aa_read(struct i2c_client *client, u16 addr, u16 *val) +{ + int ret = s5k6aa_i2c_write(client, REG_CMDRD_ADDRL, addr); + if (ret) + return ret; + return s5k6aa_i2c_read(client, REG_CMDBUF0_ADDR, val); +} + +static int s5k6aa_write_array(struct v4l2_subdev *sd, + const struct s5k6aa_regval *msg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u16 addr_incr = 0; + int ret = 0; + + while (msg->addr != S5K6AA_TERM) { + if (addr_incr != 2) + ret = s5k6aa_i2c_write(client, REG_CMDWR_ADDRL, + msg->addr); + if (ret) + break; + ret = s5k6aa_i2c_write(client, REG_CMDBUF0_ADDR, msg->val); + if (ret) + break; + /* Assume that msg->addr is always less than 0xfffc */ + addr_incr = (msg + 1)->addr - msg->addr; + msg++; + } + + return ret; +} + +/* Configure the AHB high address bytes for GTG registers access */ +static int s5k6aa_set_ahb_address(struct i2c_client *client) +{ + int ret = s5k6aa_i2c_write(client, AHB_MSB_ADDR_PTR, GEN_REG_OFFSH); + if (ret) + return ret; + ret = s5k6aa_i2c_write(client, REG_CMDRD_ADDRH, HOST_SWIF_OFFSH); + if (ret) + return ret; + return s5k6aa_i2c_write(client, REG_CMDWR_ADDRH, HOST_SWIF_OFFSH); +} + +/** + * s5k6aa_configure_pixel_clock - apply ISP main clock/PLL configuration + * + * Configure the internal ISP PLL for the required output frequency. + * Locking: called with s5k6aa.lock mutex held. + */ +static int s5k6aa_configure_pixel_clocks(struct s5k6aa *s5k6aa) +{ + struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd); + unsigned long fmclk = s5k6aa->mclk_frequency / 1000; + u16 status; + int ret; + + if (WARN(fmclk < MIN_MCLK_FREQ_KHZ || fmclk > MAX_MCLK_FREQ_KHZ, + "Invalid clock frequency: %ld\n", fmclk)) + return -EINVAL; + + s5k6aa->pclk_fmin = PCLK_FREQ_MIN; + s5k6aa->pclk_fmax = PCLK_FREQ_MAX; + s5k6aa->clk_fop = SYS_PLL_OUT_FREQ; + + /* External input clock frequency in kHz */ + ret = s5k6aa_write(c, REG_I_INCLK_FREQ_H, fmclk >> 16); + if (!ret) + ret = s5k6aa_write(c, REG_I_INCLK_FREQ_L, fmclk & 0xFFFF); + if (!ret) + ret = s5k6aa_write(c, REG_I_USE_NPVI_CLOCKS, 1); + /* Internal PLL frequency */ + if (!ret) + ret = s5k6aa_write(c, REG_I_OPCLK_4KHZ(0), s5k6aa->clk_fop); + if (!ret) + ret = s5k6aa_write(c, REG_I_MIN_OUTRATE_4KHZ(0), + s5k6aa->pclk_fmin); + if (!ret) + ret = s5k6aa_write(c, REG_I_MAX_OUTRATE_4KHZ(0), + s5k6aa->pclk_fmax); + if (!ret) + ret = s5k6aa_write(c, REG_I_INIT_PARAMS_UPDATED, 1); + if (!ret) + ret = s5k6aa_read(c, REG_I_ERROR_INFO, &status); + + return ret ? ret : (status ? -EINVAL : 0); +} + +/* Set horizontal and vertical image flipping */ +static int s5k6aa_set_mirror(struct s5k6aa *s5k6aa, int horiz_flip) +{ + struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); + int index = s5k6aa->preset->index; + + unsigned int vflip = s5k6aa->ctrls.vflip->val ^ s5k6aa->inv_vflip; + unsigned int flip = (horiz_flip ^ s5k6aa->inv_hflip) | (vflip << 1); + + return s5k6aa_write(client, REG_P_PREV_MIRROR(index), flip); +} + +/* Configure auto/manual white balance and R/G/B gains */ +static int s5k6aa_set_awb(struct s5k6aa *s5k6aa, int awb) +{ + struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd); + struct s5k6aa_ctrls *ctrls = &s5k6aa->ctrls; + u16 reg; + + int ret = s5k6aa_read(c, REG_DBG_AUTOALG_EN, ®); + + if (!ret && !awb) { + ret = s5k6aa_write(c, REG_SF_RGAIN, ctrls->gain_red->val); + if (!ret) + ret = s5k6aa_write(c, REG_SF_RGAIN_CHG, 1); + if (ret) + return ret; + + ret = s5k6aa_write(c, REG_SF_GGAIN, ctrls->gain_green->val); + if (!ret) + ret = s5k6aa_write(c, REG_SF_GGAIN_CHG, 1); + if (ret) + return ret; + + ret = s5k6aa_write(c, REG_SF_BGAIN, ctrls->gain_blue->val); + if (!ret) + ret = s5k6aa_write(c, REG_SF_BGAIN_CHG, 1); + } + if (!ret) { + reg = awb ? reg | AALG_WB_EN_MASK : reg & ~AALG_WB_EN_MASK; + ret = s5k6aa_write(c, REG_DBG_AUTOALG_EN, reg); + } + + return ret; +} + +/* Program FW with exposure time, 'exposure' in us units */ +static int s5k6aa_set_user_exposure(struct i2c_client *client, int exposure) +{ + unsigned int time = exposure / 10; + + int ret = s5k6aa_write(client, REG_SF_USR_EXPOSURE_L, time & 0xffff); + if (!ret) + ret = s5k6aa_write(client, REG_SF_USR_EXPOSURE_H, time >> 16); + if (ret) + return ret; + return s5k6aa_write(client, REG_SF_USR_EXPOSURE_CHG, 1); +} + +static int s5k6aa_set_user_gain(struct i2c_client *client, int gain) +{ + int ret = s5k6aa_write(client, REG_SF_USR_TOT_GAIN, gain); + if (ret) + return ret; + return s5k6aa_write(client, REG_SF_USR_TOT_GAIN_CHG, 1); +} + +/* Set auto/manual exposure and total gain */ +static int s5k6aa_set_auto_exposure(struct s5k6aa *s5k6aa, int value) +{ + struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd); + unsigned int exp_time = s5k6aa->ctrls.exposure->val; + u16 auto_alg; + + int ret = s5k6aa_read(c, REG_DBG_AUTOALG_EN, &auto_alg); + if (ret) + return ret; + + v4l2_dbg(1, debug, c, "man_exp: %d, auto_exp: %d, a_alg: 0x%x\n", + exp_time, value, auto_alg); + + if (value == V4L2_EXPOSURE_AUTO) { + auto_alg |= AALG_AE_EN_MASK | AALG_DIVLEI_EN_MASK; + } else { + ret = s5k6aa_set_user_exposure(c, exp_time); + if (ret) + return ret; + ret = s5k6aa_set_user_gain(c, s5k6aa->ctrls.gain->val); + if (ret) + return ret; + auto_alg &= ~(AALG_AE_EN_MASK | AALG_DIVLEI_EN_MASK); + } + + return s5k6aa_write(c, REG_DBG_AUTOALG_EN, auto_alg); +} + +static int s5k6aa_set_anti_flicker(struct s5k6aa *s5k6aa, int value) +{ + struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); + u16 auto_alg; + int ret; + + ret = s5k6aa_read(client, REG_DBG_AUTOALG_EN, &auto_alg); + if (ret) + return ret; + + if (value == V4L2_CID_POWER_LINE_FREQUENCY_AUTO) { + auto_alg |= AALG_FLICKER_EN_MASK; + } else { + auto_alg &= ~AALG_FLICKER_EN_MASK; + /* The V4L2_CID_LINE_FREQUENCY control values match + * the register values */ + ret = s5k6aa_write(client, REG_SF_FLICKER_QUANT, value); + if (ret) + return ret; + ret = s5k6aa_write(client, REG_SF_FLICKER_QUANT_CHG, 1); + if (ret) + return ret; + } + + return s5k6aa_write(client, REG_DBG_AUTOALG_EN, auto_alg); +} + +static int s5k6aa_set_colorfx(struct s5k6aa *s5k6aa, int val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); + static const struct v4l2_control colorfx[] = { + { V4L2_COLORFX_NONE, 0 }, + { V4L2_COLORFX_BW, 1 }, + { V4L2_COLORFX_NEGATIVE, 2 }, + { V4L2_COLORFX_SEPIA, 3 }, + { V4L2_COLORFX_SKY_BLUE, 4 }, + { V4L2_COLORFX_SKETCH, 5 }, + }; + int i; + + for (i = 0; i < ARRAY_SIZE(colorfx); i++) { + if (colorfx[i].id == val) + return s5k6aa_write(client, REG_G_SPEC_EFFECTS, + colorfx[i].value); + } + return -EINVAL; +} + +static int s5k6aa_preview_config_status(struct i2c_client *client) +{ + u16 error = 0; + int ret = s5k6aa_read(client, REG_G_PREV_CFG_ERROR, &error); + + v4l2_dbg(1, debug, client, "error: 0x%x (%d)\n", error, ret); + return ret ? ret : (error ? -EINVAL : 0); +} + +static int s5k6aa_get_pixfmt_index(struct s5k6aa *s5k6aa, + struct v4l2_mbus_framefmt *mf) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(s5k6aa_formats); i++) + if (mf->colorspace == s5k6aa_formats[i].colorspace && + mf->code == s5k6aa_formats[i].code) + return i; + return 0; +} + +static int s5k6aa_set_output_framefmt(struct s5k6aa *s5k6aa, + struct s5k6aa_preset *preset) +{ + struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); + int fmt_index = s5k6aa_get_pixfmt_index(s5k6aa, &preset->mbus_fmt); + int ret; + + ret = s5k6aa_write(client, REG_P_OUT_WIDTH(preset->index), + preset->mbus_fmt.width); + if (!ret) + ret = s5k6aa_write(client, REG_P_OUT_HEIGHT(preset->index), + preset->mbus_fmt.height); + if (!ret) + ret = s5k6aa_write(client, REG_P_FMT(preset->index), + s5k6aa_formats[fmt_index].reg_p_fmt); + return ret; +} + +static int s5k6aa_set_input_params(struct s5k6aa *s5k6aa) +{ + struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd); + struct v4l2_rect *r = &s5k6aa->ccd_rect; + int ret; + + ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_WIDTH, r->width); + if (!ret) + ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_HEIGHT, r->height); + if (!ret) + ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_XOFFS, r->left); + if (!ret) + ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_YOFFS, r->top); + if (!ret) + ret = s5k6aa_write(c, REG_G_INPUTS_CHANGE_REQ, 1); + if (!ret) + s5k6aa->apply_crop = 0; + + return ret; +} + +/** + * s5k6aa_configure_video_bus - configure the video output interface + * @bus_type: video bus type: parallel or MIPI-CSI + * @nlanes: number of MIPI lanes to be used (MIPI-CSI only) + * + * Note: Only parallel bus operation has been tested. + */ +static int s5k6aa_configure_video_bus(struct s5k6aa *s5k6aa, + enum v4l2_mbus_type bus_type, int nlanes) +{ + struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); + u16 cfg = 0; + int ret; + + /* + * TODO: The sensor is supposed to support BT.601 and BT.656 + * but there is nothing indicating how to switch between both + * in the datasheet. For now default BT.601 interface is assumed. + */ + if (bus_type == V4L2_MBUS_CSI2) + cfg = nlanes; + else if (bus_type != V4L2_MBUS_PARALLEL) + return -EINVAL; + + ret = s5k6aa_write(client, REG_OIF_EN_MIPI_LANES, cfg); + if (ret) + return ret; + return s5k6aa_write(client, REG_OIF_CFG_CHG, 1); +} + +/* This function should be called when switching to new user configuration set*/ +static int s5k6aa_new_config_sync(struct i2c_client *client, int timeout, + int cid) +{ + unsigned long end = jiffies + msecs_to_jiffies(timeout); + u16 reg = 1; + int ret; + + ret = s5k6aa_write(client, REG_G_ACTIVE_PREV_CFG, cid); + if (!ret) + ret = s5k6aa_write(client, REG_G_PREV_CFG_CHG, 1); + if (!ret) + ret = s5k6aa_write(client, REG_G_NEW_CFG_SYNC, 1); + if (timeout == 0) + return ret; + + while (ret >= 0 && time_is_after_jiffies(end)) { + ret = s5k6aa_read(client, REG_G_NEW_CFG_SYNC, ®); + if (!reg) + return 0; + usleep_range(1000, 5000); + } + return ret ? ret : -ETIMEDOUT; +} + +/** + * s5k6aa_set_prev_config - write user preview register set + * + * Configure output resolution and color fromat, pixel clock + * frequency range, device frame rate type and frame period range. + */ +static int s5k6aa_set_prev_config(struct s5k6aa *s5k6aa, + struct s5k6aa_preset *preset) +{ + struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); + int idx = preset->index; + u16 frame_rate_q; + int ret; + + if (s5k6aa->fiv->reg_fr_time >= S5K6AA_MAX_HIGHRES_FR_TIME) + frame_rate_q = FR_RATE_Q_BEST_FRRATE; + else + frame_rate_q = FR_RATE_Q_BEST_QUALITY; + + ret = s5k6aa_set_output_framefmt(s5k6aa, preset); + if (!ret) + ret = s5k6aa_write(client, REG_P_MAX_OUT_RATE(idx), + s5k6aa->pclk_fmax); + if (!ret) + ret = s5k6aa_write(client, REG_P_MIN_OUT_RATE(idx), + s5k6aa->pclk_fmin); + if (!ret) + ret = s5k6aa_write(client, REG_P_CLK_INDEX(idx), + preset->clk_id); + if (!ret) + ret = s5k6aa_write(client, REG_P_FR_RATE_TYPE(idx), + FR_RATE_DYNAMIC); + if (!ret) + ret = s5k6aa_write(client, REG_P_FR_RATE_Q_TYPE(idx), + frame_rate_q); + if (!ret) + ret = s5k6aa_write(client, REG_P_MAX_FR_TIME(idx), + s5k6aa->fiv->reg_fr_time + 33); + if (!ret) + ret = s5k6aa_write(client, REG_P_MIN_FR_TIME(idx), + s5k6aa->fiv->reg_fr_time - 33); + if (!ret) + ret = s5k6aa_new_config_sync(client, 250, idx); + if (!ret) + ret = s5k6aa_preview_config_status(client); + if (!ret) + s5k6aa->apply_cfg = 0; + + v4l2_dbg(1, debug, client, "Frame interval: %d +/- 3.3ms. (%d)\n", + s5k6aa->fiv->reg_fr_time, ret); + return ret; +} + +/** + * s5k6aa_initialize_isp - basic ISP MCU initialization + * + * Configure AHB addresses for registers read/write; configure PLLs for + * required output pixel clock. The ISP power supply needs to be already + * enabled, with an optional H/W reset. + * Locking: called with s5k6aa.lock mutex held. + */ +static int s5k6aa_initialize_isp(struct v4l2_subdev *sd) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct s5k6aa *s5k6aa = to_s5k6aa(sd); + int ret; + + s5k6aa->apply_crop = 1; + s5k6aa->apply_cfg = 1; + msleep(100); + + ret = s5k6aa_set_ahb_address(client); + if (ret) + return ret; + ret = s5k6aa_configure_video_bus(s5k6aa, s5k6aa->bus_type, + s5k6aa->mipi_lanes); + if (ret) + return ret; + ret = s5k6aa_write_array(sd, s5k6aa_analog_config); + if (ret) + return ret; + msleep(20); + + return s5k6aa_configure_pixel_clocks(s5k6aa); +} + +static int s5k6aa_gpio_set_value(struct s5k6aa *priv, int id, u32 val) +{ + if (!gpio_is_valid(priv->gpio[id].gpio)) + return 0; + gpio_set_value(priv->gpio[id].gpio, !!val); + return 1; +} + +static int s5k6aa_gpio_assert(struct s5k6aa *priv, int id) +{ + return s5k6aa_gpio_set_value(priv, id, priv->gpio[id].level); +} + +static int s5k6aa_gpio_deassert(struct s5k6aa *priv, int id) +{ + return s5k6aa_gpio_set_value(priv, id, !priv->gpio[id].level); +} + +static int __s5k6aa_power_on(struct s5k6aa *s5k6aa) +{ + int ret; + + ret = regulator_bulk_enable(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies); + if (ret) + return ret; + if (s5k6aa_gpio_deassert(s5k6aa, STBY)) + usleep_range(150, 200); + + if (s5k6aa->s_power) + ret = s5k6aa->s_power(1); + usleep_range(4000, 4000); + + if (s5k6aa_gpio_deassert(s5k6aa, RST)) + msleep(20); + + return ret; +} + +static int __s5k6aa_power_off(struct s5k6aa *s5k6aa) +{ + int ret; + + if (s5k6aa_gpio_assert(s5k6aa, RST)) + usleep_range(100, 150); + + if (s5k6aa->s_power) { + ret = s5k6aa->s_power(0); + if (ret) + return ret; + } + if (s5k6aa_gpio_assert(s5k6aa, STBY)) + usleep_range(50, 100); + s5k6aa->streaming = 0; + + return regulator_bulk_disable(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies); +} + +/* + * V4L2 subdev core and video operations + */ +static int s5k6aa_set_power(struct v4l2_subdev *sd, int on) +{ + struct s5k6aa *s5k6aa = to_s5k6aa(sd); + int ret = 0; + + mutex_lock(&s5k6aa->lock); + + if (!on == s5k6aa->power) { + if (on) { + ret = __s5k6aa_power_on(s5k6aa); + if (!ret) + ret = s5k6aa_initialize_isp(sd); + } else { + ret = __s5k6aa_power_off(s5k6aa); + } + + if (!ret) + s5k6aa->power += on ? 1 : -1; + } + + mutex_unlock(&s5k6aa->lock); + + if (!on || ret || s5k6aa->power != 1) + return ret; + + return v4l2_ctrl_handler_setup(sd->ctrl_handler); +} + +static int __s5k6aa_stream(struct s5k6aa *s5k6aa, int enable) +{ + struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); + int ret = 0; + + ret = s5k6aa_write(client, REG_G_ENABLE_PREV, enable); + if (!ret) + ret = s5k6aa_write(client, REG_G_ENABLE_PREV_CHG, 1); + if (!ret) + s5k6aa->streaming = enable; + + return ret; +} + +static int s5k6aa_s_stream(struct v4l2_subdev *sd, int on) +{ + struct s5k6aa *s5k6aa = to_s5k6aa(sd); + int ret = 0; + + mutex_lock(&s5k6aa->lock); + + if (s5k6aa->streaming == !on) { + if (!ret && s5k6aa->apply_cfg) + ret = s5k6aa_set_prev_config(s5k6aa, s5k6aa->preset); + if (s5k6aa->apply_crop) + ret = s5k6aa_set_input_params(s5k6aa); + if (!ret) + ret = __s5k6aa_stream(s5k6aa, !!on); + } + mutex_unlock(&s5k6aa->lock); + + return ret; +} + +static int s5k6aa_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct s5k6aa *s5k6aa = to_s5k6aa(sd); + + mutex_lock(&s5k6aa->lock); + fi->interval = s5k6aa->fiv->interval; + mutex_unlock(&s5k6aa->lock); + + return 0; +} + +static int __s5k6aa_set_frame_interval(struct s5k6aa *s5k6aa, + struct v4l2_subdev_frame_interval *fi) +{ + struct v4l2_mbus_framefmt *mbus_fmt = &s5k6aa->preset->mbus_fmt; + const struct s5k6aa_interval *fiv = &s5k6aa_intervals[0]; + unsigned int err, min_err = UINT_MAX; + unsigned int i, fr_time; + + if (fi->interval.denominator == 0) + return -EINVAL; + + fr_time = fi->interval.numerator * 10000 / fi->interval.denominator; + + for (i = 0; i < ARRAY_SIZE(s5k6aa_intervals); i++) { + const struct s5k6aa_interval *iv = &s5k6aa_intervals[i]; + + if (mbus_fmt->width > iv->size.width || + mbus_fmt->height > iv->size.height) + continue; + + err = abs(iv->reg_fr_time - fr_time); + if (err < min_err) { + fiv = iv; + min_err = err; + } + } + s5k6aa->fiv = fiv; + + v4l2_dbg(1, debug, &s5k6aa->sd, "Changed frame interval to %d us\n", + fiv->reg_fr_time * 100); + return 0; +} + +static int s5k6aa_s_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct s5k6aa *s5k6aa = to_s5k6aa(sd); + int ret; + + v4l2_dbg(1, debug, sd, "Setting %d/%d frame interval\n", + fi->interval.numerator, fi->interval.denominator); + + mutex_lock(&s5k6aa->lock); + ret = __s5k6aa_set_frame_interval(s5k6aa, fi); + s5k6aa->apply_cfg = 1; + + mutex_unlock(&s5k6aa->lock); + return ret; +} + +/* + * V4L2 subdev pad level and video operations + */ +static int s5k6aa_enum_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_interval_enum *fie) +{ + struct s5k6aa *s5k6aa = to_s5k6aa(sd); + const struct s5k6aa_interval *fi; + int ret = 0; + + if (fie->index > ARRAY_SIZE(s5k6aa_intervals)) + return -EINVAL; + + v4l_bound_align_image(&fie->width, S5K6AA_WIN_WIDTH_MIN, + S5K6AA_WIN_WIDTH_MAX, 1, + &fie->height, S5K6AA_WIN_HEIGHT_MIN, + S5K6AA_WIN_HEIGHT_MAX, 1, 0); + + mutex_lock(&s5k6aa->lock); + fi = &s5k6aa_intervals[fie->index]; + if (fie->width > fi->size.width || fie->height > fi->size.height) + ret = -EINVAL; + else + fie->interval = fi->interval; + mutex_unlock(&s5k6aa->lock); + + return ret; +} + +static int s5k6aa_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index >= ARRAY_SIZE(s5k6aa_formats)) + return -EINVAL; + + code->code = s5k6aa_formats[code->index].code; + return 0; +} + +static int s5k6aa_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + int i = ARRAY_SIZE(s5k6aa_formats); + + if (fse->index > 0) + return -EINVAL; + + while (--i) + if (fse->code == s5k6aa_formats[i].code) + break; + + fse->code = s5k6aa_formats[i].code; + fse->min_width = S5K6AA_WIN_WIDTH_MIN; + fse->max_width = S5K6AA_WIN_WIDTH_MAX; + fse->max_height = S5K6AA_WIN_HEIGHT_MIN; + fse->min_height = S5K6AA_WIN_HEIGHT_MAX; + + return 0; +} + +static struct v4l2_rect * +__s5k6aa_get_crop_rect(struct s5k6aa *s5k6aa, struct v4l2_subdev_fh *fh, + enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_ACTIVE) + return &s5k6aa->ccd_rect; + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_crop(fh, 0); + + return NULL; +} + +static void s5k6aa_try_format(struct s5k6aa *s5k6aa, + struct v4l2_mbus_framefmt *mf) +{ + unsigned int index; + + v4l_bound_align_image(&mf->width, S5K6AA_WIN_WIDTH_MIN, + S5K6AA_WIN_WIDTH_MAX, 1, + &mf->height, S5K6AA_WIN_HEIGHT_MIN, + S5K6AA_WIN_HEIGHT_MAX, 1, 0); + + if (mf->colorspace != V4L2_COLORSPACE_JPEG && + mf->colorspace != V4L2_COLORSPACE_REC709) + mf->colorspace = V4L2_COLORSPACE_JPEG; + + index = s5k6aa_get_pixfmt_index(s5k6aa, mf); + + mf->colorspace = s5k6aa_formats[index].colorspace; + mf->code = s5k6aa_formats[index].code; + mf->field = V4L2_FIELD_NONE; +} + +static int s5k6aa_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct s5k6aa *s5k6aa = to_s5k6aa(sd); + struct v4l2_mbus_framefmt *mf; + + memset(fmt->reserved, 0, sizeof(fmt->reserved)); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + mf = v4l2_subdev_get_try_format(fh, 0); + fmt->format = *mf; + return 0; + } + + mutex_lock(&s5k6aa->lock); + fmt->format = s5k6aa->preset->mbus_fmt; + mutex_unlock(&s5k6aa->lock); + + return 0; +} + +static int s5k6aa_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct s5k6aa *s5k6aa = to_s5k6aa(sd); + struct s5k6aa_preset *preset = s5k6aa->preset; + struct v4l2_mbus_framefmt *mf; + struct v4l2_rect *crop; + int ret = 0; + + mutex_lock(&s5k6aa->lock); + s5k6aa_try_format(s5k6aa, &fmt->format); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + mf = v4l2_subdev_get_try_format(fh, fmt->pad); + crop = v4l2_subdev_get_try_crop(fh, 0); + } else { + if (s5k6aa->streaming) { + ret = -EBUSY; + } else { + mf = &preset->mbus_fmt; + crop = &s5k6aa->ccd_rect; + s5k6aa->apply_cfg = 1; + } + } + + if (ret == 0) { + struct v4l2_subdev_frame_interval fiv = { + .interval = {0, 1} + }; + + *mf = fmt->format; + /* + * Make sure the crop window is valid, i.e. its size is + * greater than the output window, as the ISP supports + * only down-scaling. + */ + crop->width = clamp_t(unsigned int, crop->width, mf->width, + S5K6AA_WIN_WIDTH_MAX); + crop->height = clamp_t(unsigned int, crop->height, mf->height, + S5K6AA_WIN_HEIGHT_MAX); + crop->left = clamp_t(unsigned int, crop->left, 0, + S5K6AA_WIN_WIDTH_MAX - crop->width); + crop->top = clamp_t(unsigned int, crop->top, 0, + S5K6AA_WIN_HEIGHT_MAX - crop->height); + + /* Reset to minimum possible frame interval */ + ret = __s5k6aa_set_frame_interval(s5k6aa, &fiv); + } + mutex_unlock(&s5k6aa->lock); + + return ret; +} + +static int s5k6aa_get_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_crop *crop) +{ + struct s5k6aa *s5k6aa = to_s5k6aa(sd); + struct v4l2_rect *rect; + + memset(crop->reserved, 0, sizeof(crop->reserved)); + mutex_lock(&s5k6aa->lock); + + rect = __s5k6aa_get_crop_rect(s5k6aa, fh, crop->which); + if (rect) + crop->rect = *rect; + + mutex_unlock(&s5k6aa->lock); + + v4l2_dbg(1, debug, sd, "Current crop rectangle: (%d,%d)/%dx%d\n", + rect->left, rect->top, rect->width, rect->height); + + return 0; +} + +static int s5k6aa_set_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_crop *crop) +{ + struct s5k6aa *s5k6aa = to_s5k6aa(sd); + struct v4l2_mbus_framefmt *mf; + unsigned int max_x, max_y; + struct v4l2_rect *crop_r; + + mutex_lock(&s5k6aa->lock); + crop_r = __s5k6aa_get_crop_rect(s5k6aa, fh, crop->which); + + if (crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + mf = &s5k6aa->preset->mbus_fmt; + s5k6aa->apply_crop = 1; + } else { + mf = v4l2_subdev_get_try_format(fh, 0); + } + v4l_bound_align_image(&crop->rect.width, mf->width, + S5K6AA_WIN_WIDTH_MAX, 1, + &crop->rect.height, mf->height, + S5K6AA_WIN_HEIGHT_MAX, 1, 0); + + max_x = (S5K6AA_WIN_WIDTH_MAX - crop->rect.width) & ~1; + max_y = (S5K6AA_WIN_HEIGHT_MAX - crop->rect.height) & ~1; + + crop->rect.left = clamp_t(unsigned int, crop->rect.left, 0, max_x); + crop->rect.top = clamp_t(unsigned int, crop->rect.top, 0, max_y); + + *crop_r = crop->rect; + + mutex_unlock(&s5k6aa->lock); + + v4l2_dbg(1, debug, sd, "Set crop rectangle: (%d,%d)/%dx%d\n", + crop_r->left, crop_r->top, crop_r->width, crop_r->height); + + return 0; +} + +static const struct v4l2_subdev_pad_ops s5k6aa_pad_ops = { + .enum_mbus_code = s5k6aa_enum_mbus_code, + .enum_frame_size = s5k6aa_enum_frame_size, + .enum_frame_interval = s5k6aa_enum_frame_interval, + .get_fmt = s5k6aa_get_fmt, + .set_fmt = s5k6aa_set_fmt, + .get_crop = s5k6aa_get_crop, + .set_crop = s5k6aa_set_crop, +}; + +static const struct v4l2_subdev_video_ops s5k6aa_video_ops = { + .g_frame_interval = s5k6aa_g_frame_interval, + .s_frame_interval = s5k6aa_s_frame_interval, + .s_stream = s5k6aa_s_stream, +}; + +/* + * V4L2 subdev controls + */ + +static int s5k6aa_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = ctrl_to_sd(ctrl); + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct s5k6aa *s5k6aa = to_s5k6aa(sd); + int idx, err = 0; + + v4l2_dbg(1, debug, sd, "ctrl: 0x%x, value: %d\n", ctrl->id, ctrl->val); + + mutex_lock(&s5k6aa->lock); + /* + * If the device is not powered up by the host driver do + * not apply any controls to H/W at this time. Instead + * the controls will be restored right after power-up. + */ + if (s5k6aa->power == 0) + goto unlock; + idx = s5k6aa->preset->index; + + switch (ctrl->id) { + case V4L2_CID_AUTO_WHITE_BALANCE: + err = s5k6aa_set_awb(s5k6aa, ctrl->val); + break; + + case V4L2_CID_BRIGHTNESS: + err = s5k6aa_write(client, REG_USER_BRIGHTNESS, ctrl->val); + break; + + case V4L2_CID_COLORFX: + err = s5k6aa_set_colorfx(s5k6aa, ctrl->val); + break; + + case V4L2_CID_CONTRAST: + err = s5k6aa_write(client, REG_USER_CONTRAST, ctrl->val); + break; + + case V4L2_CID_EXPOSURE_AUTO: + err = s5k6aa_set_auto_exposure(s5k6aa, ctrl->val); + break; + + case V4L2_CID_HFLIP: + err = s5k6aa_set_mirror(s5k6aa, ctrl->val); + if (err) + break; + err = s5k6aa_write(client, REG_G_PREV_CFG_CHG, 1); + break; + + case V4L2_CID_POWER_LINE_FREQUENCY: + err = s5k6aa_set_anti_flicker(s5k6aa, ctrl->val); + break; + + case V4L2_CID_SATURATION: + err = s5k6aa_write(client, REG_USER_SATURATION, ctrl->val); + break; + + case V4L2_CID_SHARPNESS: + err = s5k6aa_write(client, REG_USER_SHARPBLUR, ctrl->val); + break; + + case V4L2_CID_WHITE_BALANCE_TEMPERATURE: + err = s5k6aa_write(client, REG_P_COLORTEMP(idx), ctrl->val); + if (err) + break; + err = s5k6aa_write(client, REG_G_PREV_CFG_CHG, 1); + break; + } +unlock: + mutex_unlock(&s5k6aa->lock); + return err; +} + +static const struct v4l2_ctrl_ops s5k6aa_ctrl_ops = { + .s_ctrl = s5k6aa_s_ctrl, +}; + +static int s5k6aa_log_status(struct v4l2_subdev *sd) +{ + v4l2_ctrl_handler_log_status(sd->ctrl_handler, sd->name); + return 0; +} + +#define V4L2_CID_RED_GAIN (V4L2_CTRL_CLASS_CAMERA | 0x1001) +#define V4L2_CID_GREEN_GAIN (V4L2_CTRL_CLASS_CAMERA | 0x1002) +#define V4L2_CID_BLUE_GAIN (V4L2_CTRL_CLASS_CAMERA | 0x1003) + +static const struct v4l2_ctrl_config s5k6aa_ctrls[] = { + { + .ops = &s5k6aa_ctrl_ops, + .id = V4L2_CID_RED_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain, Red", + .min = 0, + .max = 256, + .def = 127, + .step = 1, + }, { + .ops = &s5k6aa_ctrl_ops, + .id = V4L2_CID_GREEN_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain, Green", + .min = 0, + .max = 256, + .def = 127, + .step = 1, + }, { + .ops = &s5k6aa_ctrl_ops, + .id = V4L2_CID_BLUE_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain, Blue", + .min = 0, + .max = 256, + .def = 127, + .step = 1, + }, +}; + +static int s5k6aa_initialize_ctrls(struct s5k6aa *s5k6aa) +{ + const struct v4l2_ctrl_ops *ops = &s5k6aa_ctrl_ops; + struct s5k6aa_ctrls *ctrls = &s5k6aa->ctrls; + struct v4l2_ctrl_handler *hdl = &ctrls->handler; + + int ret = v4l2_ctrl_handler_init(hdl, 16); + if (ret) + return ret; + /* Auto white balance cluster */ + ctrls->awb = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTO_WHITE_BALANCE, + 0, 1, 1, 1); + ctrls->gain_red = v4l2_ctrl_new_custom(hdl, &s5k6aa_ctrls[0], NULL); + ctrls->gain_green = v4l2_ctrl_new_custom(hdl, &s5k6aa_ctrls[1], NULL); + ctrls->gain_blue = v4l2_ctrl_new_custom(hdl, &s5k6aa_ctrls[2], NULL); + v4l2_ctrl_auto_cluster(4, &ctrls->awb, 0, false); + + ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, 0, 1, 1, 0); + ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_cluster(2, &ctrls->hflip); + + ctrls->auto_exp = v4l2_ctrl_new_std_menu(hdl, ops, + V4L2_CID_EXPOSURE_AUTO, + V4L2_EXPOSURE_MANUAL, 0, V4L2_EXPOSURE_AUTO); + /* Exposure time: x 1 us */ + ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE, + 0, 6000000U, 1, 100000U); + /* Total gain: 256 <=> 1x */ + ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN, + 0, 256, 1, 256); + v4l2_ctrl_auto_cluster(3, &ctrls->auto_exp, 0, false); + + v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_POWER_LINE_FREQUENCY, + V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0, + V4L2_CID_POWER_LINE_FREQUENCY_AUTO); + + v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_COLORFX, + V4L2_COLORFX_SKY_BLUE, ~0x6f, V4L2_COLORFX_NONE); + + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_WHITE_BALANCE_TEMPERATURE, + 0, 256, 1, 0); + + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SATURATION, -127, 127, 1, 0); + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS, -127, 127, 1, 0); + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -127, 127, 1, 0); + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SHARPNESS, -127, 127, 1, 0); + + if (hdl->error) { + ret = hdl->error; + v4l2_ctrl_handler_free(hdl); + return ret; + } + + s5k6aa->sd.ctrl_handler = hdl; + return 0; +} + +/* + * V4L2 subdev internal operations + */ +static int s5k6aa_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0); + struct v4l2_rect *crop = v4l2_subdev_get_try_crop(fh, 0); + + format->colorspace = s5k6aa_formats[0].colorspace; + format->code = s5k6aa_formats[0].code; + format->width = S5K6AA_OUT_WIDTH_DEF; + format->height = S5K6AA_OUT_HEIGHT_DEF; + format->field = V4L2_FIELD_NONE; + + crop->width = S5K6AA_WIN_WIDTH_MAX; + crop->height = S5K6AA_WIN_HEIGHT_MAX; + crop->left = 0; + crop->top = 0; + + return 0; +} + +int s5k6aa_check_fw_revision(struct s5k6aa *s5k6aa) +{ + struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); + u16 api_ver = 0, fw_rev = 0; + + int ret = s5k6aa_set_ahb_address(client); + + if (!ret) + ret = s5k6aa_read(client, REG_FW_APIVER, &api_ver); + if (!ret) + ret = s5k6aa_read(client, REG_FW_REVISION, &fw_rev); + if (ret) { + v4l2_err(&s5k6aa->sd, "FW revision check failed!\n"); + return ret; + } + + v4l2_info(&s5k6aa->sd, "FW API ver.: 0x%X, FW rev.: 0x%X\n", + api_ver, fw_rev); + + return api_ver == S5K6AAFX_FW_APIVER ? 0 : -ENODEV; +} + +static int s5k6aa_registered(struct v4l2_subdev *sd) +{ + struct s5k6aa *s5k6aa = to_s5k6aa(sd); + int ret; + + mutex_lock(&s5k6aa->lock); + ret = __s5k6aa_power_on(s5k6aa); + if (!ret) { + msleep(100); + ret = s5k6aa_check_fw_revision(s5k6aa); + __s5k6aa_power_off(s5k6aa); + } + mutex_unlock(&s5k6aa->lock); + + return ret; +} + +static const struct v4l2_subdev_internal_ops s5k6aa_subdev_internal_ops = { + .registered = s5k6aa_registered, + .open = s5k6aa_open, +}; + +static const struct v4l2_subdev_core_ops s5k6aa_core_ops = { + .s_power = s5k6aa_set_power, + .log_status = s5k6aa_log_status, +}; + +static const struct v4l2_subdev_ops s5k6aa_subdev_ops = { + .core = &s5k6aa_core_ops, + .pad = &s5k6aa_pad_ops, + .video = &s5k6aa_video_ops, +}; + +/* + * GPIO setup + */ +static int s5k6aa_configure_gpio(int nr, int val, const char *name) +{ + unsigned long flags = val ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW; + int ret; + + if (!gpio_is_valid(nr)) + return 0; + ret = gpio_request_one(nr, flags, name); + if (!ret) + gpio_export(nr, 0); + return ret; +} + +static void s5k6aa_free_gpios(struct s5k6aa *s5k6aa) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(s5k6aa->gpio); i++) { + if (!gpio_is_valid(s5k6aa->gpio[i].gpio)) + continue; + gpio_free(s5k6aa->gpio[i].gpio); + s5k6aa->gpio[i].gpio = -EINVAL; + } +} + +static int s5k6aa_configure_gpios(struct s5k6aa *s5k6aa, + const struct s5k6aa_platform_data *pdata) +{ + const struct s5k6aa_gpio *gpio = &pdata->gpio_stby; + int ret; + + s5k6aa->gpio[STBY].gpio = -EINVAL; + s5k6aa->gpio[RST].gpio = -EINVAL; + + ret = s5k6aa_configure_gpio(gpio->gpio, gpio->level, "S5K6AA_STBY"); + if (ret) { + s5k6aa_free_gpios(s5k6aa); + return ret; + } + s5k6aa->gpio[STBY] = *gpio; + if (gpio_is_valid(gpio->gpio)) + gpio_set_value(gpio->gpio, 0); + + gpio = &pdata->gpio_reset; + ret = s5k6aa_configure_gpio(gpio->gpio, gpio->level, "S5K6AA_RST"); + if (ret) { + s5k6aa_free_gpios(s5k6aa); + return ret; + } + s5k6aa->gpio[RST] = *gpio; + if (gpio_is_valid(gpio->gpio)) + gpio_set_value(gpio->gpio, 0); + + return 0; +} + +static int s5k6aa_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + const struct s5k6aa_platform_data *pdata = client->dev.platform_data; + struct v4l2_subdev *sd; + struct s5k6aa *s5k6aa; + int i, ret; + + if (pdata == NULL) { + dev_err(&client->dev, "Platform data not specified\n"); + return -EINVAL; + } + + if (pdata->mclk_frequency == 0) { + dev_err(&client->dev, "MCLK frequency not specified\n"); + return -EINVAL; + } + + s5k6aa = kzalloc(sizeof(*s5k6aa), GFP_KERNEL); + if (!s5k6aa) + return -ENOMEM; + + mutex_init(&s5k6aa->lock); + + s5k6aa->mclk_frequency = pdata->mclk_frequency; + s5k6aa->bus_type = pdata->bus_type; + s5k6aa->mipi_lanes = pdata->nlanes; + s5k6aa->s_power = pdata->set_power; + s5k6aa->inv_hflip = pdata->horiz_flip; + s5k6aa->inv_vflip = pdata->vert_flip; + + sd = &s5k6aa->sd; + strlcpy(sd->name, DRIVER_NAME, sizeof(sd->name)); + v4l2_i2c_subdev_init(sd, client, &s5k6aa_subdev_ops); + + sd->internal_ops = &s5k6aa_subdev_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + s5k6aa->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; + ret = media_entity_init(&sd->entity, 1, &s5k6aa->pad, 0); + if (ret) + goto out_err1; + + ret = s5k6aa_configure_gpios(s5k6aa, pdata); + if (ret) + goto out_err2; + + for (i = 0; i < S5K6AA_NUM_SUPPLIES; i++) + s5k6aa->supplies[i].supply = s5k6aa_supply_names[i]; + + ret = regulator_bulk_get(&client->dev, S5K6AA_NUM_SUPPLIES, + s5k6aa->supplies); + if (ret) { + dev_err(&client->dev, "Failed to get regulators\n"); + goto out_err3; + } + + ret = s5k6aa_initialize_ctrls(s5k6aa); + if (ret) + goto out_err4; + + s5k6aa_presets_data_init(s5k6aa); + + s5k6aa->ccd_rect.width = S5K6AA_WIN_WIDTH_MAX; + s5k6aa->ccd_rect.height = S5K6AA_WIN_HEIGHT_MAX; + s5k6aa->ccd_rect.left = 0; + s5k6aa->ccd_rect.top = 0; + + return 0; + +out_err4: + regulator_bulk_free(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies); +out_err3: + s5k6aa_free_gpios(s5k6aa); +out_err2: + media_entity_cleanup(&s5k6aa->sd.entity); +out_err1: + kfree(s5k6aa); + return ret; +} + +static int s5k6aa_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct s5k6aa *s5k6aa = to_s5k6aa(sd); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(sd->ctrl_handler); + media_entity_cleanup(&sd->entity); + regulator_bulk_free(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies); + s5k6aa_free_gpios(s5k6aa); + kfree(s5k6aa); + + return 0; +} + +static const struct i2c_device_id s5k6aa_id[] = { + { DRIVER_NAME, 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, s5k6aa_id); + + +static struct i2c_driver s5k6aa_i2c_driver = { + .driver = { + .name = DRIVER_NAME + }, + .probe = s5k6aa_probe, + .remove = s5k6aa_remove, + .id_table = s5k6aa_id, +}; + +static int __init s5k6aa_init(void) +{ + return i2c_add_driver(&s5k6aa_i2c_driver); +} + +static void __exit s5k6aa_exit(void) +{ + i2c_del_driver(&s5k6aa_i2c_driver); +} + +module_init(s5k6aa_init); +module_exit(s5k6aa_exit); + +MODULE_DESCRIPTION("Samsung S5K6AA(FX) SXGA camera driver"); +MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/s5p-fimc/Makefile b/drivers/media/video/s5p-fimc/Makefile index df6954ab1d9..33dec7f890e 100644 --- a/drivers/media/video/s5p-fimc/Makefile +++ b/drivers/media/video/s5p-fimc/Makefile @@ -1,4 +1,4 @@ -s5p-fimc-objs := fimc-core.o fimc-reg.o fimc-capture.o +s5p-fimc-objs := fimc-core.o fimc-reg.o fimc-capture.o fimc-mdevice.o s5p-csis-objs := mipi-csis.o obj-$(CONFIG_VIDEO_S5P_MIPI_CSIS) += s5p-csis.o diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c index 0d730e55605..c8d91b0cd9b 100644 --- a/drivers/media/video/s5p-fimc/fimc-capture.c +++ b/drivers/media/video/s5p-fimc/fimc-capture.c @@ -16,11 +16,9 @@ #include <linux/bug.h> #include <linux/interrupt.h> #include <linux/device.h> -#include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <linux/list.h> #include <linux/slab.h> -#include <linux/clk.h> -#include <linux/i2c.h> #include <linux/videodev2.h> #include <media/v4l2-device.h> @@ -29,135 +27,88 @@ #include <media/videobuf2-core.h> #include <media/videobuf2-dma-contig.h> +#include "fimc-mdevice.h" #include "fimc-core.h" -static struct v4l2_subdev *fimc_subdev_register(struct fimc_dev *fimc, - struct s5p_fimc_isp_info *isp_info) +static int fimc_init_capture(struct fimc_dev *fimc) { - struct i2c_adapter *i2c_adap; - struct fimc_vid_cap *vid_cap = &fimc->vid_cap; - struct v4l2_subdev *sd = NULL; - - i2c_adap = i2c_get_adapter(isp_info->i2c_bus_num); - if (!i2c_adap) - return ERR_PTR(-ENOMEM); - - sd = v4l2_i2c_new_subdev_board(&vid_cap->v4l2_dev, i2c_adap, - isp_info->board_info, NULL); - if (!sd) { - v4l2_err(&vid_cap->v4l2_dev, "failed to acquire subdev\n"); - return NULL; - } + struct fimc_ctx *ctx = fimc->vid_cap.ctx; + struct fimc_sensor_info *sensor; + unsigned long flags; + int ret = 0; - v4l2_info(&vid_cap->v4l2_dev, "subdevice %s registered successfuly\n", - isp_info->board_info->type); + if (fimc->pipeline.sensor == NULL || ctx == NULL) + return -ENXIO; + if (ctx->s_frame.fmt == NULL) + return -EINVAL; - return sd; -} + sensor = v4l2_get_subdev_hostdata(fimc->pipeline.sensor); -static void fimc_subdev_unregister(struct fimc_dev *fimc) -{ - struct fimc_vid_cap *vid_cap = &fimc->vid_cap; - struct i2c_client *client; + spin_lock_irqsave(&fimc->slock, flags); + fimc_prepare_dma_offset(ctx, &ctx->d_frame); + fimc_set_yuv_order(ctx); - if (vid_cap->input_index < 0) - return; /* Subdevice already released or not registered. */ + fimc_hw_set_camera_polarity(fimc, sensor->pdata); + fimc_hw_set_camera_type(fimc, sensor->pdata); + fimc_hw_set_camera_source(fimc, sensor->pdata); + fimc_hw_set_camera_offset(fimc, &ctx->s_frame); - if (vid_cap->sd) { - v4l2_device_unregister_subdev(vid_cap->sd); - client = v4l2_get_subdevdata(vid_cap->sd); - i2c_unregister_device(client); - i2c_put_adapter(client->adapter); - vid_cap->sd = NULL; + ret = fimc_set_scaler_info(ctx); + if (!ret) { + fimc_hw_set_input_path(ctx); + fimc_hw_set_prescaler(ctx); + fimc_hw_set_mainscaler(ctx); + fimc_hw_set_target_format(ctx); + fimc_hw_set_rotation(ctx); + fimc_hw_set_effect(ctx, false); + fimc_hw_set_output_path(ctx); + fimc_hw_set_out_dma(ctx); + clear_bit(ST_CAPT_APPLY_CFG, &fimc->state); } - - vid_cap->input_index = -1; + spin_unlock_irqrestore(&fimc->slock, flags); + return ret; } -/** - * fimc_subdev_attach - attach v4l2_subdev to camera host interface - * - * @fimc: FIMC device information - * @index: index to the array of available subdevices, - * -1 for full array search or non negative value - * to select specific subdevice - */ -static int fimc_subdev_attach(struct fimc_dev *fimc, int index) +static int fimc_capture_state_cleanup(struct fimc_dev *fimc, bool suspend) { - struct fimc_vid_cap *vid_cap = &fimc->vid_cap; - struct s5p_platform_fimc *pdata = fimc->pdata; - struct s5p_fimc_isp_info *isp_info; - struct v4l2_subdev *sd; - int i; - - for (i = 0; i < pdata->num_clients; ++i) { - isp_info = &pdata->isp_info[i]; + struct fimc_vid_cap *cap = &fimc->vid_cap; + struct fimc_vid_buffer *buf; + unsigned long flags; + bool streaming; - if (index >= 0 && i != index) - continue; + spin_lock_irqsave(&fimc->slock, flags); + streaming = fimc->state & (1 << ST_CAPT_ISP_STREAM); - sd = fimc_subdev_register(fimc, isp_info); - if (!IS_ERR_OR_NULL(sd)) { - vid_cap->sd = sd; - vid_cap->input_index = i; + fimc->state &= ~(1 << ST_CAPT_RUN | 1 << ST_CAPT_SHUT | + 1 << ST_CAPT_STREAM | 1 << ST_CAPT_ISP_STREAM); + if (!suspend) + fimc->state &= ~(1 << ST_CAPT_PEND | 1 << ST_CAPT_SUSPENDED); - return 0; - } + /* Release unused buffers */ + while (!suspend && !list_empty(&cap->pending_buf_q)) { + buf = fimc_pending_queue_pop(cap); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); } + /* If suspending put unused buffers onto pending queue */ + while (!list_empty(&cap->active_buf_q)) { + buf = fimc_active_queue_pop(cap); + if (suspend) + fimc_pending_queue_add(cap, buf); + else + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + } + set_bit(ST_CAPT_SUSPENDED, &fimc->state); + spin_unlock_irqrestore(&fimc->slock, flags); - vid_cap->input_index = -1; - vid_cap->sd = NULL; - v4l2_err(&vid_cap->v4l2_dev, "fimc%d: sensor attach failed\n", - fimc->id); - return -ENODEV; -} - -static int fimc_isp_subdev_init(struct fimc_dev *fimc, unsigned int index) -{ - struct s5p_fimc_isp_info *isp_info; - struct s5p_platform_fimc *pdata = fimc->pdata; - int ret; - - if (index >= pdata->num_clients) - return -EINVAL; - - isp_info = &pdata->isp_info[index]; - - if (isp_info->clk_frequency) - clk_set_rate(fimc->clock[CLK_CAM], isp_info->clk_frequency); - - ret = clk_enable(fimc->clock[CLK_CAM]); - if (ret) - return ret; - - ret = fimc_subdev_attach(fimc, index); - if (ret) - return ret; - - ret = fimc_hw_set_camera_polarity(fimc, isp_info); - if (ret) - return ret; - - ret = v4l2_subdev_call(fimc->vid_cap.sd, core, s_power, 1); - if (!ret) - return ret; - - /* enabling power failed so unregister subdev */ - fimc_subdev_unregister(fimc); - - v4l2_err(&fimc->vid_cap.v4l2_dev, "ISP initialization failed: %d\n", - ret); - - return ret; + if (streaming) + return fimc_pipeline_s_stream(fimc, 0); + else + return 0; } -static int fimc_stop_capture(struct fimc_dev *fimc) +static int fimc_stop_capture(struct fimc_dev *fimc, bool suspend) { unsigned long flags; - struct fimc_vid_cap *cap; - struct fimc_vid_buffer *buf; - - cap = &fimc->vid_cap; if (!fimc_capture_active(fimc)) return 0; @@ -169,81 +120,73 @@ static int fimc_stop_capture(struct fimc_dev *fimc) wait_event_timeout(fimc->irq_queue, !test_bit(ST_CAPT_SHUT, &fimc->state), - FIMC_SHUTDOWN_TIMEOUT); - - v4l2_subdev_call(cap->sd, video, s_stream, 0); + (2*HZ/10)); /* 200 ms */ - spin_lock_irqsave(&fimc->slock, flags); - fimc->state &= ~(1 << ST_CAPT_RUN | 1 << ST_CAPT_PEND | - 1 << ST_CAPT_SHUT | 1 << ST_CAPT_STREAM); + return fimc_capture_state_cleanup(fimc, suspend); +} - fimc->vid_cap.active_buf_cnt = 0; +/** + * fimc_capture_config_update - apply the camera interface configuration + * + * To be called from within the interrupt handler with fimc.slock + * spinlock held. It updates the camera pixel crop, rotation and + * image flip in H/W. + */ +int fimc_capture_config_update(struct fimc_ctx *ctx) +{ + struct fimc_dev *fimc = ctx->fimc_dev; + int ret; - /* Release buffers that were enqueued in the driver by videobuf2. */ - while (!list_empty(&cap->pending_buf_q)) { - buf = pending_queue_pop(cap); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); - } + if (test_bit(ST_CAPT_APPLY_CFG, &fimc->state)) + return 0; - while (!list_empty(&cap->active_buf_q)) { - buf = active_queue_pop(cap); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + spin_lock(&ctx->slock); + fimc_hw_set_camera_offset(fimc, &ctx->s_frame); + ret = fimc_set_scaler_info(ctx); + if (ret == 0) { + fimc_hw_set_prescaler(ctx); + fimc_hw_set_mainscaler(ctx); + fimc_hw_set_target_format(ctx); + fimc_hw_set_rotation(ctx); + fimc_prepare_dma_offset(ctx, &ctx->d_frame); + fimc_hw_set_out_dma(ctx); + set_bit(ST_CAPT_APPLY_CFG, &fimc->state); } - - spin_unlock_irqrestore(&fimc->slock, flags); - - dbg("state: 0x%lx", fimc->state); - return 0; + spin_unlock(&ctx->slock); + return ret; } -static int start_streaming(struct vb2_queue *q) +static int start_streaming(struct vb2_queue *q, unsigned int count) { struct fimc_ctx *ctx = q->drv_priv; struct fimc_dev *fimc = ctx->fimc_dev; - struct s5p_fimc_isp_info *isp_info; + struct fimc_vid_cap *vid_cap = &fimc->vid_cap; + int min_bufs; int ret; fimc_hw_reset(fimc); + vid_cap->frame_count = 0; - ret = v4l2_subdev_call(fimc->vid_cap.sd, video, s_stream, 1); - if (ret && ret != -ENOIOCTLCMD) - return ret; - - ret = fimc_prepare_config(ctx, ctx->state); + ret = fimc_init_capture(fimc); if (ret) - return ret; - - isp_info = &fimc->pdata->isp_info[fimc->vid_cap.input_index]; - fimc_hw_set_camera_type(fimc, isp_info); - fimc_hw_set_camera_source(fimc, isp_info); - fimc_hw_set_camera_offset(fimc, &ctx->s_frame); + goto error; - if (ctx->state & FIMC_PARAMS) { - ret = fimc_set_scaler_info(ctx); - if (ret) { - err("Scaler setup error"); - return ret; - } - fimc_hw_set_input_path(ctx); - fimc_hw_set_prescaler(ctx); - fimc_hw_set_mainscaler(ctx); - fimc_hw_set_target_format(ctx); - fimc_hw_set_rotation(ctx); - fimc_hw_set_effect(ctx); - } + set_bit(ST_CAPT_PEND, &fimc->state); - fimc_hw_set_output_path(ctx); - fimc_hw_set_out_dma(ctx); + min_bufs = fimc->vid_cap.reqbufs_count > 1 ? 2 : 1; - INIT_LIST_HEAD(&fimc->vid_cap.pending_buf_q); - INIT_LIST_HEAD(&fimc->vid_cap.active_buf_q); - fimc->vid_cap.active_buf_cnt = 0; - fimc->vid_cap.frame_count = 0; - fimc->vid_cap.buf_index = 0; + if (vid_cap->active_buf_cnt >= min_bufs && + !test_and_set_bit(ST_CAPT_STREAM, &fimc->state)) { + fimc_activate_capture(ctx); - set_bit(ST_CAPT_PEND, &fimc->state); + if (!test_and_set_bit(ST_CAPT_ISP_STREAM, &fimc->state)) + fimc_pipeline_s_stream(fimc, 1); + } return 0; +error: + fimc_capture_state_cleanup(fimc, false); + return ret; } static int stop_streaming(struct vb2_queue *q) @@ -254,7 +197,46 @@ static int stop_streaming(struct vb2_queue *q) if (!fimc_capture_active(fimc)) return -EINVAL; - return fimc_stop_capture(fimc); + return fimc_stop_capture(fimc, false); +} + +int fimc_capture_suspend(struct fimc_dev *fimc) +{ + bool suspend = fimc_capture_busy(fimc); + + int ret = fimc_stop_capture(fimc, suspend); + if (ret) + return ret; + return fimc_pipeline_shutdown(fimc); +} + +static void buffer_queue(struct vb2_buffer *vb); + +int fimc_capture_resume(struct fimc_dev *fimc) +{ + struct fimc_vid_cap *vid_cap = &fimc->vid_cap; + struct fimc_vid_buffer *buf; + int i; + + if (!test_and_clear_bit(ST_CAPT_SUSPENDED, &fimc->state)) + return 0; + + INIT_LIST_HEAD(&fimc->vid_cap.active_buf_q); + vid_cap->buf_index = 0; + fimc_pipeline_initialize(fimc, &fimc->vid_cap.vfd->entity, + false); + fimc_init_capture(fimc); + + clear_bit(ST_CAPT_SUSPENDED, &fimc->state); + + for (i = 0; i < vid_cap->reqbufs_count; i++) { + if (list_empty(&vid_cap->pending_buf_q)) + break; + buf = fimc_pending_queue_pop(vid_cap); + buffer_queue(&buf->vb); + } + return 0; + } static unsigned int get_plane_size(struct fimc_frame *fr, unsigned int plane) @@ -264,9 +246,9 @@ static unsigned int get_plane_size(struct fimc_frame *fr, unsigned int plane) return fr->f_width * fr->f_height * fr->fmt->depth[plane] / 8; } -static int queue_setup(struct vb2_queue *vq, unsigned int *num_buffers, - unsigned int *num_planes, unsigned long sizes[], - void *allocators[]) +static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *pfmt, + unsigned int *num_buffers, unsigned int *num_planes, + unsigned int sizes[], void *allocators[]) { struct fimc_ctx *ctx = vq->drv_priv; struct fimc_fmt *fmt = ctx->d_frame.fmt; @@ -289,21 +271,20 @@ static int buffer_prepare(struct vb2_buffer *vb) { struct vb2_queue *vq = vb->vb2_queue; struct fimc_ctx *ctx = vq->drv_priv; - struct v4l2_device *v4l2_dev = &ctx->fimc_dev->m2m.v4l2_dev; int i; - if (!ctx->d_frame.fmt || vq->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + if (ctx->d_frame.fmt == NULL) return -EINVAL; for (i = 0; i < ctx->d_frame.fmt->memplanes; i++) { - unsigned long size = get_plane_size(&ctx->d_frame, i); + unsigned long size = ctx->d_frame.payload[i]; if (vb2_plane_size(vb, i) < size) { - v4l2_err(v4l2_dev, "User buffer too small (%ld < %ld)\n", + v4l2_err(ctx->fimc_dev->vid_cap.vfd, + "User buffer too small (%ld < %ld)\n", vb2_plane_size(vb, i), size); return -EINVAL; } - vb2_set_plane_payload(vb, i, size); } @@ -312,10 +293,10 @@ static int buffer_prepare(struct vb2_buffer *vb) static void buffer_queue(struct vb2_buffer *vb) { - struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); - struct fimc_dev *fimc = ctx->fimc_dev; struct fimc_vid_buffer *buf = container_of(vb, struct fimc_vid_buffer, vb); + struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct fimc_dev *fimc = ctx->fimc_dev; struct fimc_vid_cap *vid_cap = &fimc->vid_cap; unsigned long flags; int min_bufs; @@ -323,15 +304,16 @@ static void buffer_queue(struct vb2_buffer *vb) spin_lock_irqsave(&fimc->slock, flags); fimc_prepare_addr(ctx, &buf->vb, &ctx->d_frame, &buf->paddr); - if (!test_bit(ST_CAPT_STREAM, &fimc->state) - && vid_cap->active_buf_cnt < FIMC_MAX_OUT_BUFS) { + if (!test_bit(ST_CAPT_SUSPENDED, &fimc->state) && + !test_bit(ST_CAPT_STREAM, &fimc->state) && + vid_cap->active_buf_cnt < FIMC_MAX_OUT_BUFS) { /* Setup the buffer directly for processing. */ int buf_id = (vid_cap->reqbufs_count == 1) ? -1 : vid_cap->buf_index; fimc_hw_set_output_addr(fimc, &buf->paddr, buf_id); buf->index = vid_cap->buf_index; - active_queue_add(vid_cap, buf); + fimc_active_queue_add(vid_cap, buf); if (++vid_cap->buf_index >= FIMC_MAX_OUT_BUFS) vid_cap->buf_index = 0; @@ -341,10 +323,17 @@ static void buffer_queue(struct vb2_buffer *vb) min_bufs = vid_cap->reqbufs_count > 1 ? 2 : 1; - if (vid_cap->active_buf_cnt >= min_bufs && - !test_and_set_bit(ST_CAPT_STREAM, &fimc->state)) + + if (vb2_is_streaming(&vid_cap->vbq) && + vid_cap->active_buf_cnt >= min_bufs && + !test_and_set_bit(ST_CAPT_STREAM, &fimc->state)) { fimc_activate_capture(ctx); + spin_unlock_irqrestore(&fimc->slock, flags); + if (!test_and_set_bit(ST_CAPT_ISP_STREAM, &fimc->state)) + fimc_pipeline_s_stream(fimc, 1); + return; + } spin_unlock_irqrestore(&fimc->slock, flags); } @@ -370,10 +359,40 @@ static struct vb2_ops fimc_capture_qops = { .stop_streaming = stop_streaming, }; +/** + * fimc_capture_ctrls_create - initialize the control handler + * Initialize the capture video node control handler and fill it + * with the FIMC controls. Inherit any sensor's controls if the + * 'user_subdev_api' flag is false (default behaviour). + * This function need to be called with the graph mutex held. + */ +int fimc_capture_ctrls_create(struct fimc_dev *fimc) +{ + struct fimc_vid_cap *vid_cap = &fimc->vid_cap; + int ret; + + if (WARN_ON(vid_cap->ctx == NULL)) + return -ENXIO; + if (vid_cap->ctx->ctrls_rdy) + return 0; + + ret = fimc_ctrls_create(vid_cap->ctx); + if (ret || vid_cap->user_subdev_api) + return ret; + + return v4l2_ctrl_add_handler(&vid_cap->ctx->ctrl_handler, + fimc->pipeline.sensor->ctrl_handler); +} + +static int fimc_capture_set_default_format(struct fimc_dev *fimc); + static int fimc_capture_open(struct file *file) { struct fimc_dev *fimc = video_drvdata(file); - int ret = 0; + int ret = v4l2_fh_open(file); + + if (ret) + return ret; dbg("pid: %d, state: 0x%lx", task_pid_nr(current), fimc->state); @@ -381,17 +400,27 @@ static int fimc_capture_open(struct file *file) if (fimc_m2m_active(fimc)) return -EBUSY; + set_bit(ST_CAPT_BUSY, &fimc->state); + pm_runtime_get_sync(&fimc->pdev->dev); + if (++fimc->vid_cap.refcnt == 1) { - ret = fimc_isp_subdev_init(fimc, 0); - if (ret) { + ret = fimc_pipeline_initialize(fimc, + &fimc->vid_cap.vfd->entity, true); + if (ret < 0) { + dev_err(&fimc->pdev->dev, + "Video pipeline initialization failed\n"); + pm_runtime_put_sync(&fimc->pdev->dev); fimc->vid_cap.refcnt--; - return -EIO; + v4l2_fh_release(file); + clear_bit(ST_CAPT_BUSY, &fimc->state); + return ret; } - } - - file->private_data = fimc->vid_cap.ctx; + ret = fimc_capture_ctrls_create(fimc); - return 0; + if (!ret && !fimc->vid_cap.user_subdev_api) + ret = fimc_capture_set_default_format(fimc); + } + return ret; } static int fimc_capture_close(struct file *file) @@ -401,37 +430,36 @@ static int fimc_capture_close(struct file *file) dbg("pid: %d, state: 0x%lx", task_pid_nr(current), fimc->state); if (--fimc->vid_cap.refcnt == 0) { - fimc_stop_capture(fimc); - vb2_queue_release(&fimc->vid_cap.vbq); + clear_bit(ST_CAPT_BUSY, &fimc->state); + fimc_stop_capture(fimc, false); + fimc_pipeline_shutdown(fimc); + clear_bit(ST_CAPT_SUSPENDED, &fimc->state); + } - v4l2_err(&fimc->vid_cap.v4l2_dev, "releasing ISP\n"); + pm_runtime_put(&fimc->pdev->dev); - v4l2_subdev_call(fimc->vid_cap.sd, core, s_power, 0); - clk_disable(fimc->clock[CLK_CAM]); - fimc_subdev_unregister(fimc); + if (fimc->vid_cap.refcnt == 0) { + vb2_queue_release(&fimc->vid_cap.vbq); + fimc_ctrls_delete(fimc->vid_cap.ctx); } - - return 0; + return v4l2_fh_release(file); } static unsigned int fimc_capture_poll(struct file *file, struct poll_table_struct *wait) { - struct fimc_ctx *ctx = file->private_data; - struct fimc_dev *fimc = ctx->fimc_dev; + struct fimc_dev *fimc = video_drvdata(file); return vb2_poll(&fimc->vid_cap.vbq, file, wait); } static int fimc_capture_mmap(struct file *file, struct vm_area_struct *vma) { - struct fimc_ctx *ctx = file->private_data; - struct fimc_dev *fimc = ctx->fimc_dev; + struct fimc_dev *fimc = video_drvdata(file); return vb2_mmap(&fimc->vid_cap.vbq, vma); } -/* video device file operations */ static const struct v4l2_file_operations fimc_capture_fops = { .owner = THIS_MODULE, .open = fimc_capture_open, @@ -441,263 +469,553 @@ static const struct v4l2_file_operations fimc_capture_fops = { .mmap = fimc_capture_mmap, }; +/* + * Format and crop negotiation helpers + */ + +static struct fimc_fmt *fimc_capture_try_format(struct fimc_ctx *ctx, + u32 *width, u32 *height, + u32 *code, u32 *fourcc, int pad) +{ + bool rotation = ctx->rotation == 90 || ctx->rotation == 270; + struct fimc_dev *fimc = ctx->fimc_dev; + struct samsung_fimc_variant *var = fimc->variant; + struct fimc_pix_limit *pl = var->pix_limit; + struct fimc_frame *dst = &ctx->d_frame; + u32 depth, min_w, max_w, min_h, align_h = 3; + u32 mask = FMT_FLAGS_CAM; + struct fimc_fmt *ffmt; + + /* Color conversion from/to JPEG is not supported */ + if (code && ctx->s_frame.fmt && pad == FIMC_SD_PAD_SOURCE && + fimc_fmt_is_jpeg(ctx->s_frame.fmt->color)) + *code = V4L2_MBUS_FMT_JPEG_1X8; + + if (fourcc && *fourcc != V4L2_PIX_FMT_JPEG && pad != FIMC_SD_PAD_SINK) + mask |= FMT_FLAGS_M2M; + + ffmt = fimc_find_format(fourcc, code, mask, 0); + if (WARN_ON(!ffmt)) + return NULL; + if (code) + *code = ffmt->mbus_code; + if (fourcc) + *fourcc = ffmt->fourcc; + + if (pad == FIMC_SD_PAD_SINK) { + max_w = fimc_fmt_is_jpeg(ffmt->color) ? + pl->scaler_dis_w : pl->scaler_en_w; + /* Apply the camera input interface pixel constraints */ + v4l_bound_align_image(width, max_t(u32, *width, 32), max_w, 4, + height, max_t(u32, *height, 32), + FIMC_CAMIF_MAX_HEIGHT, + fimc_fmt_is_jpeg(ffmt->color) ? 3 : 1, + 0); + return ffmt; + } + /* Can't scale or crop in transparent (JPEG) transfer mode */ + if (fimc_fmt_is_jpeg(ffmt->color)) { + *width = ctx->s_frame.f_width; + *height = ctx->s_frame.f_height; + return ffmt; + } + /* Apply the scaler and the output DMA constraints */ + max_w = rotation ? pl->out_rot_en_w : pl->out_rot_dis_w; + min_w = ctx->state & FIMC_DST_CROP ? dst->width : var->min_out_pixsize; + min_h = ctx->state & FIMC_DST_CROP ? dst->height : var->min_out_pixsize; + if (fimc->id == 1 && var->pix_hoff) + align_h = fimc_fmt_is_rgb(ffmt->color) ? 0 : 1; + + depth = fimc_get_format_depth(ffmt); + v4l_bound_align_image(width, min_w, max_w, + ffs(var->min_out_pixsize) - 1, + height, min_h, FIMC_CAMIF_MAX_HEIGHT, + align_h, + 64/(ALIGN(depth, 8))); + + dbg("pad%d: code: 0x%x, %dx%d. dst fmt: %dx%d", + pad, code ? *code : 0, *width, *height, + dst->f_width, dst->f_height); + + return ffmt; +} + +static void fimc_capture_try_crop(struct fimc_ctx *ctx, struct v4l2_rect *r, + int pad) +{ + bool rotate = ctx->rotation == 90 || ctx->rotation == 270; + struct fimc_dev *fimc = ctx->fimc_dev; + struct samsung_fimc_variant *var = fimc->variant; + struct fimc_pix_limit *pl = var->pix_limit; + struct fimc_frame *sink = &ctx->s_frame; + u32 max_w, max_h, min_w = 0, min_h = 0, min_sz; + u32 align_sz = 0, align_h = 4; + u32 max_sc_h, max_sc_v; + + /* In JPEG transparent transfer mode cropping is not supported */ + if (fimc_fmt_is_jpeg(ctx->d_frame.fmt->color)) { + r->width = sink->f_width; + r->height = sink->f_height; + r->left = r->top = 0; + return; + } + if (pad == FIMC_SD_PAD_SOURCE) { + if (ctx->rotation != 90 && ctx->rotation != 270) + align_h = 1; + max_sc_h = min(SCALER_MAX_HRATIO, 1 << (ffs(sink->width) - 3)); + max_sc_v = min(SCALER_MAX_VRATIO, 1 << (ffs(sink->height) - 1)); + min_sz = var->min_out_pixsize; + } else { + u32 depth = fimc_get_format_depth(sink->fmt); + align_sz = 64/ALIGN(depth, 8); + min_sz = var->min_inp_pixsize; + min_w = min_h = min_sz; + max_sc_h = max_sc_v = 1; + } + /* + * For the crop rectangle at source pad the following constraints + * must be met: + * - it must fit in the sink pad format rectangle (f_width/f_height); + * - maximum downscaling ratio is 64; + * - maximum crop size depends if the rotator is used or not; + * - the sink pad format width/height must be 4 multiple of the + * prescaler ratios determined by sink pad size and source pad crop, + * the prescaler ratio is returned by fimc_get_scaler_factor(). + */ + max_w = min_t(u32, + rotate ? pl->out_rot_en_w : pl->out_rot_dis_w, + rotate ? sink->f_height : sink->f_width); + max_h = min_t(u32, FIMC_CAMIF_MAX_HEIGHT, sink->f_height); + if (pad == FIMC_SD_PAD_SOURCE) { + min_w = min_t(u32, max_w, sink->f_width / max_sc_h); + min_h = min_t(u32, max_h, sink->f_height / max_sc_v); + if (rotate) { + swap(max_sc_h, max_sc_v); + swap(min_w, min_h); + } + } + v4l_bound_align_image(&r->width, min_w, max_w, ffs(min_sz) - 1, + &r->height, min_h, max_h, align_h, + align_sz); + /* Adjust left/top if cropping rectangle is out of bounds */ + r->left = clamp_t(u32, r->left, 0, sink->f_width - r->width); + r->top = clamp_t(u32, r->top, 0, sink->f_height - r->height); + r->left = round_down(r->left, var->hor_offs_align); + + dbg("pad%d: (%d,%d)/%dx%d, sink fmt: %dx%d", + pad, r->left, r->top, r->width, r->height, + sink->f_width, sink->f_height); +} + +/* + * The video node ioctl operations + */ static int fimc_vidioc_querycap_capture(struct file *file, void *priv, struct v4l2_capability *cap) { - struct fimc_ctx *ctx = file->private_data; - struct fimc_dev *fimc = ctx->fimc_dev; + struct fimc_dev *fimc = video_drvdata(file); strncpy(cap->driver, fimc->pdev->name, sizeof(cap->driver) - 1); strncpy(cap->card, fimc->pdev->name, sizeof(cap->card) - 1); cap->bus_info[0] = 0; - cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_VIDEO_CAPTURE_MPLANE; + cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE_MPLANE; return 0; } -/* Synchronize formats of the camera interface input and attached sensor. */ -static int sync_capture_fmt(struct fimc_ctx *ctx) +static int fimc_cap_enum_fmt_mplane(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct fimc_fmt *fmt; + + fmt = fimc_find_format(NULL, NULL, FMT_FLAGS_CAM | FMT_FLAGS_M2M, + f->index); + if (!fmt) + return -EINVAL; + strncpy(f->description, fmt->name, sizeof(f->description) - 1); + f->pixelformat = fmt->fourcc; + if (fmt->fourcc == V4L2_MBUS_FMT_JPEG_1X8) + f->flags |= V4L2_FMT_FLAG_COMPRESSED; + return 0; +} + +/** + * fimc_pipeline_try_format - negotiate and/or set formats at pipeline + * elements + * @ctx: FIMC capture context + * @tfmt: media bus format to try/set on subdevs + * @fmt_id: fimc pixel format id corresponding to returned @tfmt (output) + * @set: true to set format on subdevs, false to try only + */ +static int fimc_pipeline_try_format(struct fimc_ctx *ctx, + struct v4l2_mbus_framefmt *tfmt, + struct fimc_fmt **fmt_id, + bool set) { - struct fimc_frame *frame = &ctx->s_frame; struct fimc_dev *fimc = ctx->fimc_dev; - struct v4l2_mbus_framefmt *fmt = &fimc->vid_cap.fmt; - int ret; + struct v4l2_subdev *sd = fimc->pipeline.sensor; + struct v4l2_subdev *csis = fimc->pipeline.csis; + struct v4l2_subdev_format sfmt; + struct v4l2_mbus_framefmt *mf = &sfmt.format; + struct fimc_fmt *ffmt = NULL; + int ret, i = 0; + + if (WARN_ON(!sd || !tfmt)) + return -EINVAL; - fmt->width = ctx->d_frame.o_width; - fmt->height = ctx->d_frame.o_height; + memset(&sfmt, 0, sizeof(sfmt)); + sfmt.format = *tfmt; + + sfmt.which = set ? V4L2_SUBDEV_FORMAT_ACTIVE : V4L2_SUBDEV_FORMAT_TRY; + while (1) { + ffmt = fimc_find_format(NULL, mf->code != 0 ? &mf->code : NULL, + FMT_FLAGS_CAM, i++); + if (ffmt == NULL) { + /* + * Notify user-space if common pixel code for + * host and sensor does not exist. + */ + return -EINVAL; + } + mf->code = tfmt->code = ffmt->mbus_code; - ret = v4l2_subdev_call(fimc->vid_cap.sd, video, s_mbus_fmt, fmt); - if (ret == -ENOIOCTLCMD) { - err("s_mbus_fmt failed"); - return ret; - } - dbg("w: %d, h: %d, code= %d", fmt->width, fmt->height, fmt->code); + ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &sfmt); + if (ret) + return ret; + if (mf->code != tfmt->code) { + mf->code = 0; + continue; + } + if (mf->width != tfmt->width || mf->width != tfmt->width) { + u32 fcc = ffmt->fourcc; + tfmt->width = mf->width; + tfmt->height = mf->height; + ffmt = fimc_capture_try_format(ctx, + &tfmt->width, &tfmt->height, + NULL, &fcc, FIMC_SD_PAD_SOURCE); + if (ffmt && ffmt->mbus_code) + mf->code = ffmt->mbus_code; + if (mf->width != tfmt->width || mf->width != tfmt->width) + continue; + tfmt->code = mf->code; + } + if (csis) + ret = v4l2_subdev_call(csis, pad, set_fmt, NULL, &sfmt); - frame->fmt = find_mbus_format(fmt, FMT_FLAGS_CAM); - if (!frame->fmt) { - err("fimc source format not found\n"); - return -EINVAL; + if (mf->code == tfmt->code && + mf->width == tfmt->width && mf->width == tfmt->width) + break; } - frame->f_width = fmt->width; - frame->f_height = fmt->height; - frame->width = fmt->width; - frame->height = fmt->height; - frame->o_width = fmt->width; - frame->o_height = fmt->height; - frame->offs_h = 0; - frame->offs_v = 0; + if (fmt_id && ffmt) + *fmt_id = ffmt; + *tfmt = *mf; + dbg("code: 0x%x, %dx%d, %p", mf->code, mf->width, mf->height, ffmt); return 0; } -static int fimc_cap_s_fmt_mplane(struct file *file, void *priv, +static int fimc_cap_g_fmt_mplane(struct file *file, void *fh, struct v4l2_format *f) { - struct fimc_ctx *ctx = priv; - struct fimc_dev *fimc = ctx->fimc_dev; - struct fimc_frame *frame; - struct v4l2_pix_format_mplane *pix; - int ret; - int i; + struct fimc_dev *fimc = video_drvdata(file); + struct fimc_ctx *ctx = fimc->vid_cap.ctx; if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) return -EINVAL; - ret = fimc_vidioc_try_fmt_mplane(file, priv, f); - if (ret) - return ret; - - if (vb2_is_busy(&fimc->vid_cap.vbq) || fimc_capture_active(fimc)) - return -EBUSY; + return fimc_fill_format(&ctx->d_frame, f); +} - frame = &ctx->d_frame; +static int fimc_cap_try_fmt_mplane(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; + struct fimc_dev *fimc = video_drvdata(file); + struct fimc_ctx *ctx = fimc->vid_cap.ctx; + struct v4l2_mbus_framefmt mf; + struct fimc_fmt *ffmt = NULL; - pix = &f->fmt.pix_mp; - frame->fmt = find_format(f, FMT_FLAGS_M2M | FMT_FLAGS_CAM); - if (!frame->fmt) { - err("fimc target format not found\n"); + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) return -EINVAL; + + if (pix->pixelformat == V4L2_PIX_FMT_JPEG) { + fimc_capture_try_format(ctx, &pix->width, &pix->height, + NULL, &pix->pixelformat, + FIMC_SD_PAD_SINK); + ctx->s_frame.f_width = pix->width; + ctx->s_frame.f_height = pix->height; } + ffmt = fimc_capture_try_format(ctx, &pix->width, &pix->height, + NULL, &pix->pixelformat, + FIMC_SD_PAD_SOURCE); + if (!ffmt) + return -EINVAL; - for (i = 0; i < frame->fmt->colplanes; i++) { - frame->payload[i] = - (pix->width * pix->height * frame->fmt->depth[i]) >> 3; + if (!fimc->vid_cap.user_subdev_api) { + mf.width = pix->width; + mf.height = pix->height; + mf.code = ffmt->mbus_code; + fimc_md_graph_lock(fimc); + fimc_pipeline_try_format(ctx, &mf, &ffmt, false); + fimc_md_graph_unlock(fimc); + + pix->width = mf.width; + pix->height = mf.height; + if (ffmt) + pix->pixelformat = ffmt->fourcc; } - /* Output DMA frame pixel size and offsets. */ - frame->f_width = pix->plane_fmt[0].bytesperline * 8 - / frame->fmt->depth[0]; - frame->f_height = pix->height; - frame->width = pix->width; - frame->height = pix->height; - frame->o_width = pix->width; - frame->o_height = pix->height; - frame->offs_h = 0; - frame->offs_v = 0; + fimc_adjust_mplane_format(ffmt, pix->width, pix->height, pix); + return 0; +} - ctx->state |= (FIMC_PARAMS | FIMC_DST_FMT); +static void fimc_capture_mark_jpeg_xfer(struct fimc_ctx *ctx, bool jpeg) +{ + ctx->scaler.enabled = !jpeg; + fimc_ctrls_activate(ctx, !jpeg); - ret = sync_capture_fmt(ctx); - return ret; + if (jpeg) + set_bit(ST_CAPT_JPEG, &ctx->fimc_dev->state); + else + clear_bit(ST_CAPT_JPEG, &ctx->fimc_dev->state); } -static int fimc_cap_enum_input(struct file *file, void *priv, - struct v4l2_input *i) +static int fimc_capture_set_format(struct fimc_dev *fimc, struct v4l2_format *f) { - struct fimc_ctx *ctx = priv; - struct s5p_platform_fimc *pldata = ctx->fimc_dev->pdata; - struct s5p_fimc_isp_info *isp_info; + struct fimc_ctx *ctx = fimc->vid_cap.ctx; + struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; + struct v4l2_mbus_framefmt *mf = &fimc->vid_cap.mf; + struct fimc_frame *ff = &ctx->d_frame; + struct fimc_fmt *s_fmt = NULL; + int ret, i; - if (i->index >= pldata->num_clients) + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) return -EINVAL; + if (vb2_is_busy(&fimc->vid_cap.vbq)) + return -EBUSY; - isp_info = &pldata->isp_info[i->index]; + /* Pre-configure format at camera interface input, for JPEG only */ + if (pix->pixelformat == V4L2_PIX_FMT_JPEG) { + fimc_capture_try_format(ctx, &pix->width, &pix->height, + NULL, &pix->pixelformat, + FIMC_SD_PAD_SINK); + ctx->s_frame.f_width = pix->width; + ctx->s_frame.f_height = pix->height; + } + /* Try the format at the scaler and the DMA output */ + ff->fmt = fimc_capture_try_format(ctx, &pix->width, &pix->height, + NULL, &pix->pixelformat, + FIMC_SD_PAD_SOURCE); + if (!ff->fmt) + return -EINVAL; + /* Try to match format at the host and the sensor */ + if (!fimc->vid_cap.user_subdev_api) { + mf->code = ff->fmt->mbus_code; + mf->width = pix->width; + mf->height = pix->height; + + fimc_md_graph_lock(fimc); + ret = fimc_pipeline_try_format(ctx, mf, &s_fmt, true); + fimc_md_graph_unlock(fimc); + if (ret) + return ret; + pix->width = mf->width; + pix->height = mf->height; + } + fimc_adjust_mplane_format(ff->fmt, pix->width, pix->height, pix); + for (i = 0; i < ff->fmt->colplanes; i++) + ff->payload[i] = + (pix->width * pix->height * ff->fmt->depth[i]) / 8; + + set_frame_bounds(ff, pix->width, pix->height); + /* Reset the composition rectangle if not yet configured */ + if (!(ctx->state & FIMC_DST_CROP)) + set_frame_crop(ff, 0, 0, pix->width, pix->height); + + fimc_capture_mark_jpeg_xfer(ctx, fimc_fmt_is_jpeg(ff->fmt->color)); + + /* Reset cropping and set format at the camera interface input */ + if (!fimc->vid_cap.user_subdev_api) { + ctx->s_frame.fmt = s_fmt; + set_frame_bounds(&ctx->s_frame, pix->width, pix->height); + set_frame_crop(&ctx->s_frame, 0, 0, pix->width, pix->height); + } - i->type = V4L2_INPUT_TYPE_CAMERA; - strncpy(i->name, isp_info->board_info->type, 32); - return 0; + return ret; } -static int fimc_cap_s_input(struct file *file, void *priv, - unsigned int i) +static int fimc_cap_s_fmt_mplane(struct file *file, void *priv, + struct v4l2_format *f) { - struct fimc_ctx *ctx = priv; - struct fimc_dev *fimc = ctx->fimc_dev; - struct s5p_platform_fimc *pdata = fimc->pdata; - - if (fimc_capture_active(ctx->fimc_dev)) - return -EBUSY; + struct fimc_dev *fimc = video_drvdata(file); - if (i >= pdata->num_clients) - return -EINVAL; + return fimc_capture_set_format(fimc, f); +} +static int fimc_cap_enum_input(struct file *file, void *priv, + struct v4l2_input *i) +{ + struct fimc_dev *fimc = video_drvdata(file); + struct v4l2_subdev *sd = fimc->pipeline.sensor; - if (fimc->vid_cap.sd) { - int ret = v4l2_subdev_call(fimc->vid_cap.sd, core, s_power, 0); - if (ret) - err("s_power failed: %d", ret); + if (i->index != 0) + return -EINVAL; - clk_disable(fimc->clock[CLK_CAM]); - } + i->type = V4L2_INPUT_TYPE_CAMERA; + if (sd) + strlcpy(i->name, sd->name, sizeof(i->name)); + return 0; +} - /* Release the attached sensor subdevice. */ - fimc_subdev_unregister(fimc); +static int fimc_cap_s_input(struct file *file, void *priv, unsigned int i) +{ + return i == 0 ? i : -EINVAL; +} - return fimc_isp_subdev_init(fimc, i); +static int fimc_cap_g_input(struct file *file, void *priv, unsigned int *i) +{ + *i = 0; + return 0; } -static int fimc_cap_g_input(struct file *file, void *priv, - unsigned int *i) +/** + * fimc_pipeline_validate - check for formats inconsistencies + * between source and sink pad of each link + * + * Return 0 if all formats match or -EPIPE otherwise. + */ +static int fimc_pipeline_validate(struct fimc_dev *fimc) { - struct fimc_ctx *ctx = priv; - struct fimc_vid_cap *cap = &ctx->fimc_dev->vid_cap; + struct v4l2_subdev_format sink_fmt, src_fmt; + struct fimc_vid_cap *vid_cap = &fimc->vid_cap; + struct v4l2_subdev *sd; + struct media_pad *pad; + int ret; - *i = cap->input_index; + /* Start with the video capture node pad */ + pad = media_entity_remote_source(&vid_cap->vd_pad); + if (pad == NULL) + return -EPIPE; + /* FIMC.{N} subdevice */ + sd = media_entity_to_v4l2_subdev(pad->entity); + + while (1) { + /* Retrieve format at the sink pad */ + pad = &sd->entity.pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + break; + /* Don't call FIMC subdev operation to avoid nested locking */ + if (sd == fimc->vid_cap.subdev) { + struct fimc_frame *ff = &vid_cap->ctx->s_frame; + sink_fmt.format.width = ff->f_width; + sink_fmt.format.height = ff->f_height; + sink_fmt.format.code = ff->fmt ? ff->fmt->mbus_code : 0; + } else { + sink_fmt.pad = pad->index; + sink_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &sink_fmt); + if (ret < 0 && ret != -ENOIOCTLCMD) + return -EPIPE; + } + /* Retrieve format at the source pad */ + pad = media_entity_remote_source(pad); + if (pad == NULL || + media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + break; + + sd = media_entity_to_v4l2_subdev(pad->entity); + src_fmt.pad = pad->index; + src_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &src_fmt); + if (ret < 0 && ret != -ENOIOCTLCMD) + return -EPIPE; + + if (src_fmt.format.width != sink_fmt.format.width || + src_fmt.format.height != sink_fmt.format.height || + src_fmt.format.code != sink_fmt.format.code) + return -EPIPE; + } return 0; } static int fimc_cap_streamon(struct file *file, void *priv, enum v4l2_buf_type type) { - struct fimc_ctx *ctx = priv; - struct fimc_dev *fimc = ctx->fimc_dev; + struct fimc_dev *fimc = video_drvdata(file); + struct fimc_pipeline *p = &fimc->pipeline; + int ret; - if (fimc_capture_active(fimc) || !fimc->vid_cap.sd) + if (fimc_capture_active(fimc)) return -EBUSY; - if (!(ctx->state & FIMC_DST_FMT)) { - v4l2_err(&fimc->vid_cap.v4l2_dev, "Format is not set\n"); - return -EINVAL; - } + media_entity_pipeline_start(&p->sensor->entity, p->pipe); + if (fimc->vid_cap.user_subdev_api) { + ret = fimc_pipeline_validate(fimc); + if (ret) + return ret; + } return vb2_streamon(&fimc->vid_cap.vbq, type); } static int fimc_cap_streamoff(struct file *file, void *priv, enum v4l2_buf_type type) { - struct fimc_ctx *ctx = priv; - struct fimc_dev *fimc = ctx->fimc_dev; + struct fimc_dev *fimc = video_drvdata(file); + struct v4l2_subdev *sd = fimc->pipeline.sensor; + int ret; - return vb2_streamoff(&fimc->vid_cap.vbq, type); + ret = vb2_streamoff(&fimc->vid_cap.vbq, type); + if (ret == 0) + media_entity_pipeline_stop(&sd->entity); + return ret; } static int fimc_cap_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *reqbufs) { - struct fimc_ctx *ctx = priv; - struct fimc_vid_cap *cap = &ctx->fimc_dev->vid_cap; - int ret; - + struct fimc_dev *fimc = video_drvdata(file); + int ret = vb2_reqbufs(&fimc->vid_cap.vbq, reqbufs); - ret = vb2_reqbufs(&cap->vbq, reqbufs); if (!ret) - cap->reqbufs_count = reqbufs->count; - + fimc->vid_cap.reqbufs_count = reqbufs->count; return ret; } static int fimc_cap_querybuf(struct file *file, void *priv, struct v4l2_buffer *buf) { - struct fimc_ctx *ctx = priv; - struct fimc_vid_cap *cap = &ctx->fimc_dev->vid_cap; + struct fimc_dev *fimc = video_drvdata(file); - return vb2_querybuf(&cap->vbq, buf); + return vb2_querybuf(&fimc->vid_cap.vbq, buf); } static int fimc_cap_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) { - struct fimc_ctx *ctx = priv; - struct fimc_vid_cap *cap = &ctx->fimc_dev->vid_cap; - return vb2_qbuf(&cap->vbq, buf); + struct fimc_dev *fimc = video_drvdata(file); + + return vb2_qbuf(&fimc->vid_cap.vbq, buf); } static int fimc_cap_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) { - struct fimc_ctx *ctx = priv; - return vb2_dqbuf(&ctx->fimc_dev->vid_cap.vbq, buf, - file->f_flags & O_NONBLOCK); -} - -static int fimc_cap_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct fimc_ctx *ctx = priv; - int ret = -EINVAL; + struct fimc_dev *fimc = video_drvdata(file); - /* Allow any controls but 90/270 rotation while streaming */ - if (!fimc_capture_active(ctx->fimc_dev) || - ctrl->id != V4L2_CID_ROTATE || - (ctrl->value != 90 && ctrl->value != 270)) { - ret = check_ctrl_val(ctx, ctrl); - if (!ret) { - ret = fimc_s_ctrl(ctx, ctrl); - if (!ret) - ctx->state |= FIMC_PARAMS; - } - } - if (ret == -EINVAL) - ret = v4l2_subdev_call(ctx->fimc_dev->vid_cap.sd, - core, s_ctrl, ctrl); - return ret; + return vb2_dqbuf(&fimc->vid_cap.vbq, buf, file->f_flags & O_NONBLOCK); } static int fimc_cap_cropcap(struct file *file, void *fh, struct v4l2_cropcap *cr) { - struct fimc_frame *f; - struct fimc_ctx *ctx = fh; + struct fimc_dev *fimc = video_drvdata(file); + struct fimc_frame *f = &fimc->vid_cap.ctx->s_frame; if (cr->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) return -EINVAL; - f = &ctx->s_frame; - cr->bounds.left = 0; cr->bounds.top = 0; cr->bounds.width = f->o_width; @@ -709,10 +1027,8 @@ static int fimc_cap_cropcap(struct file *file, void *fh, static int fimc_cap_g_crop(struct file *file, void *fh, struct v4l2_crop *cr) { - struct fimc_frame *f; - struct fimc_ctx *ctx = file->private_data; - - f = &ctx->s_frame; + struct fimc_dev *fimc = video_drvdata(file); + struct fimc_frame *f = &fimc->vid_cap.ctx->s_frame; cr->c.left = f->offs_h; cr->c.top = f->offs_v; @@ -722,53 +1038,31 @@ static int fimc_cap_g_crop(struct file *file, void *fh, struct v4l2_crop *cr) return 0; } -static int fimc_cap_s_crop(struct file *file, void *fh, - struct v4l2_crop *cr) +static int fimc_cap_s_crop(struct file *file, void *fh, struct v4l2_crop *cr) { - struct fimc_frame *f; - struct fimc_ctx *ctx = file->private_data; - struct fimc_dev *fimc = ctx->fimc_dev; - int ret = -EINVAL; - - if (fimc_capture_active(fimc)) - return -EBUSY; - - ret = fimc_try_crop(ctx, cr); - if (ret) - return ret; - - if (!(ctx->state & FIMC_DST_FMT)) { - v4l2_err(&fimc->vid_cap.v4l2_dev, - "Capture color format not set\n"); - return -EINVAL; /* TODO: make sure this is the right value */ - } + struct fimc_dev *fimc = video_drvdata(file); + struct fimc_ctx *ctx = fimc->vid_cap.ctx; + struct fimc_frame *ff; + unsigned long flags; - f = &ctx->s_frame; - /* Check for the pixel scaling ratio when cropping input image. */ - ret = fimc_check_scaler_ratio(cr->c.width, cr->c.height, - ctx->d_frame.width, ctx->d_frame.height, - ctx->rotation); - if (ret) { - v4l2_err(&fimc->vid_cap.v4l2_dev, "Out of the scaler range\n"); - return ret; - } + fimc_capture_try_crop(ctx, &cr->c, FIMC_SD_PAD_SINK); + ff = &ctx->s_frame; - f->offs_h = cr->c.left; - f->offs_v = cr->c.top; - f->width = cr->c.width; - f->height = cr->c.height; + spin_lock_irqsave(&fimc->slock, flags); + set_frame_crop(ff, cr->c.left, cr->c.top, cr->c.width, cr->c.height); + set_bit(ST_CAPT_APPLY_CFG, &fimc->state); + spin_unlock_irqrestore(&fimc->slock, flags); return 0; } - static const struct v4l2_ioctl_ops fimc_capture_ioctl_ops = { .vidioc_querycap = fimc_vidioc_querycap_capture, - .vidioc_enum_fmt_vid_cap_mplane = fimc_vidioc_enum_fmt_mplane, - .vidioc_try_fmt_vid_cap_mplane = fimc_vidioc_try_fmt_mplane, + .vidioc_enum_fmt_vid_cap_mplane = fimc_cap_enum_fmt_mplane, + .vidioc_try_fmt_vid_cap_mplane = fimc_cap_try_fmt_mplane, .vidioc_s_fmt_vid_cap_mplane = fimc_cap_s_fmt_mplane, - .vidioc_g_fmt_vid_cap_mplane = fimc_vidioc_g_fmt_mplane, + .vidioc_g_fmt_vid_cap_mplane = fimc_cap_g_fmt_mplane, .vidioc_reqbufs = fimc_cap_reqbufs, .vidioc_querybuf = fimc_cap_querybuf, @@ -779,10 +1073,6 @@ static const struct v4l2_ioctl_ops fimc_capture_ioctl_ops = { .vidioc_streamon = fimc_cap_streamon, .vidioc_streamoff = fimc_cap_streamoff, - .vidioc_queryctrl = fimc_vidioc_queryctrl, - .vidioc_g_ctrl = fimc_vidioc_g_ctrl, - .vidioc_s_ctrl = fimc_cap_s_ctrl, - .vidioc_g_crop = fimc_cap_g_crop, .vidioc_s_crop = fimc_cap_s_crop, .vidioc_cropcap = fimc_cap_cropcap, @@ -792,17 +1082,328 @@ static const struct v4l2_ioctl_ops fimc_capture_ioctl_ops = { .vidioc_g_input = fimc_cap_g_input, }; +/* Capture subdev media entity operations */ +static int fimc_link_setup(struct media_entity *entity, + const struct media_pad *local, + const struct media_pad *remote, u32 flags) +{ + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); + struct fimc_dev *fimc = v4l2_get_subdevdata(sd); + + if (media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + return -EINVAL; + + if (WARN_ON(fimc == NULL)) + return 0; + + dbg("%s --> %s, flags: 0x%x. input: 0x%x", + local->entity->name, remote->entity->name, flags, + fimc->vid_cap.input); + + if (flags & MEDIA_LNK_FL_ENABLED) { + if (fimc->vid_cap.input != 0) + return -EBUSY; + fimc->vid_cap.input = sd->grp_id; + return 0; + } + + fimc->vid_cap.input = 0; + return 0; +} + +static const struct media_entity_operations fimc_sd_media_ops = { + .link_setup = fimc_link_setup, +}; + +/** + * fimc_sensor_notify - v4l2_device notification from a sensor subdev + * @sd: pointer to a subdev generating the notification + * @notification: the notification type, must be S5P_FIMC_TX_END_NOTIFY + * @arg: pointer to an u32 type integer that stores the frame payload value + * + * The End Of Frame notification sent by sensor subdev in its still capture + * mode. If there is only a single VSYNC generated by the sensor at the + * beginning of a frame transmission, FIMC does not issue the LastIrq + * (end of frame) interrupt. And this notification is used to complete the + * frame capture and returning a buffer to user-space. Subdev drivers should + * call this notification from their last 'End of frame capture' interrupt. + */ +void fimc_sensor_notify(struct v4l2_subdev *sd, unsigned int notification, + void *arg) +{ + struct fimc_sensor_info *sensor; + struct fimc_vid_buffer *buf; + struct fimc_md *fmd; + struct fimc_dev *fimc; + unsigned long flags; + + if (sd == NULL) + return; + + sensor = v4l2_get_subdev_hostdata(sd); + fmd = entity_to_fimc_mdev(&sd->entity); + + spin_lock_irqsave(&fmd->slock, flags); + fimc = sensor ? sensor->host : NULL; + + if (fimc && arg && notification == S5P_FIMC_TX_END_NOTIFY && + test_bit(ST_CAPT_PEND, &fimc->state)) { + unsigned long irq_flags; + spin_lock_irqsave(&fimc->slock, irq_flags); + if (!list_empty(&fimc->vid_cap.active_buf_q)) { + buf = list_entry(fimc->vid_cap.active_buf_q.next, + struct fimc_vid_buffer, list); + vb2_set_plane_payload(&buf->vb, 0, *((u32 *)arg)); + } + fimc_capture_irq_handler(fimc, true); + fimc_deactivate_capture(fimc); + spin_unlock_irqrestore(&fimc->slock, irq_flags); + } + spin_unlock_irqrestore(&fmd->slock, flags); +} + +static int fimc_subdev_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct fimc_fmt *fmt; + + fmt = fimc_find_format(NULL, NULL, FMT_FLAGS_CAM, code->index); + if (!fmt) + return -EINVAL; + code->code = fmt->mbus_code; + return 0; +} + +static int fimc_subdev_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct fimc_dev *fimc = v4l2_get_subdevdata(sd); + struct fimc_ctx *ctx = fimc->vid_cap.ctx; + struct v4l2_mbus_framefmt *mf; + struct fimc_frame *ff; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + mf = v4l2_subdev_get_try_format(fh, fmt->pad); + fmt->format = *mf; + return 0; + } + mf = &fmt->format; + mf->colorspace = V4L2_COLORSPACE_JPEG; + ff = fmt->pad == FIMC_SD_PAD_SINK ? &ctx->s_frame : &ctx->d_frame; + + mutex_lock(&fimc->lock); + /* The pixel code is same on both input and output pad */ + if (!WARN_ON(ctx->s_frame.fmt == NULL)) + mf->code = ctx->s_frame.fmt->mbus_code; + mf->width = ff->f_width; + mf->height = ff->f_height; + mutex_unlock(&fimc->lock); + + return 0; +} + +static int fimc_subdev_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct fimc_dev *fimc = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *mf = &fmt->format; + struct fimc_ctx *ctx = fimc->vid_cap.ctx; + struct fimc_frame *ff; + struct fimc_fmt *ffmt; + + dbg("pad%d: code: 0x%x, %dx%d", + fmt->pad, mf->code, mf->width, mf->height); + + if (fmt->pad == FIMC_SD_PAD_SOURCE && + vb2_is_busy(&fimc->vid_cap.vbq)) + return -EBUSY; + + mutex_lock(&fimc->lock); + ffmt = fimc_capture_try_format(ctx, &mf->width, &mf->height, + &mf->code, NULL, fmt->pad); + mutex_unlock(&fimc->lock); + mf->colorspace = V4L2_COLORSPACE_JPEG; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + mf = v4l2_subdev_get_try_format(fh, fmt->pad); + *mf = fmt->format; + return 0; + } + fimc_capture_mark_jpeg_xfer(ctx, fimc_fmt_is_jpeg(ffmt->color)); + + ff = fmt->pad == FIMC_SD_PAD_SINK ? + &ctx->s_frame : &ctx->d_frame; + + mutex_lock(&fimc->lock); + set_frame_bounds(ff, mf->width, mf->height); + ff->fmt = ffmt; + + /* Reset the crop rectangle if required. */ + if (!(fmt->pad == FIMC_SD_PAD_SOURCE && (ctx->state & FIMC_DST_CROP))) + set_frame_crop(ff, 0, 0, mf->width, mf->height); + + if (fmt->pad == FIMC_SD_PAD_SINK) + ctx->state &= ~FIMC_DST_CROP; + mutex_unlock(&fimc->lock); + return 0; +} + +static int fimc_subdev_get_crop(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_crop *crop) +{ + struct fimc_dev *fimc = v4l2_get_subdevdata(sd); + struct fimc_ctx *ctx = fimc->vid_cap.ctx; + struct v4l2_rect *r = &crop->rect; + struct fimc_frame *ff; + + if (crop->which == V4L2_SUBDEV_FORMAT_TRY) { + crop->rect = *v4l2_subdev_get_try_crop(fh, crop->pad); + return 0; + } + ff = crop->pad == FIMC_SD_PAD_SINK ? + &ctx->s_frame : &ctx->d_frame; + + mutex_lock(&fimc->lock); + r->left = ff->offs_h; + r->top = ff->offs_v; + r->width = ff->width; + r->height = ff->height; + mutex_unlock(&fimc->lock); + + dbg("ff:%p, pad%d: l:%d, t:%d, %dx%d, f_w: %d, f_h: %d", + ff, crop->pad, r->left, r->top, r->width, r->height, + ff->f_width, ff->f_height); + + return 0; +} + +static int fimc_subdev_set_crop(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_crop *crop) +{ + struct fimc_dev *fimc = v4l2_get_subdevdata(sd); + struct fimc_ctx *ctx = fimc->vid_cap.ctx; + struct v4l2_rect *r = &crop->rect; + struct fimc_frame *ff; + unsigned long flags; + + dbg("(%d,%d)/%dx%d", r->left, r->top, r->width, r->height); + + ff = crop->pad == FIMC_SD_PAD_SOURCE ? + &ctx->d_frame : &ctx->s_frame; + + mutex_lock(&fimc->lock); + fimc_capture_try_crop(ctx, r, crop->pad); + + if (crop->which == V4L2_SUBDEV_FORMAT_TRY) { + mutex_lock(&fimc->lock); + *v4l2_subdev_get_try_crop(fh, crop->pad) = *r; + return 0; + } + spin_lock_irqsave(&fimc->slock, flags); + set_frame_crop(ff, r->left, r->top, r->width, r->height); + if (crop->pad == FIMC_SD_PAD_SOURCE) + ctx->state |= FIMC_DST_CROP; + + set_bit(ST_CAPT_APPLY_CFG, &fimc->state); + spin_unlock_irqrestore(&fimc->slock, flags); + + dbg("pad%d: (%d,%d)/%dx%d", crop->pad, r->left, r->top, + r->width, r->height); + + mutex_unlock(&fimc->lock); + return 0; +} + +static struct v4l2_subdev_pad_ops fimc_subdev_pad_ops = { + .enum_mbus_code = fimc_subdev_enum_mbus_code, + .get_fmt = fimc_subdev_get_fmt, + .set_fmt = fimc_subdev_set_fmt, + .get_crop = fimc_subdev_get_crop, + .set_crop = fimc_subdev_set_crop, +}; + +static struct v4l2_subdev_ops fimc_subdev_ops = { + .pad = &fimc_subdev_pad_ops, +}; + +static int fimc_create_capture_subdev(struct fimc_dev *fimc, + struct v4l2_device *v4l2_dev) +{ + struct v4l2_subdev *sd; + int ret; + + sd = kzalloc(sizeof(*sd), GFP_KERNEL); + if (!sd) + return -ENOMEM; + + v4l2_subdev_init(sd, &fimc_subdev_ops); + sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE; + snprintf(sd->name, sizeof(sd->name), "FIMC.%d", fimc->pdev->id); + + fimc->vid_cap.sd_pads[FIMC_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + fimc->vid_cap.sd_pads[FIMC_SD_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_init(&sd->entity, FIMC_SD_PADS_NUM, + fimc->vid_cap.sd_pads, 0); + if (ret) + goto me_err; + ret = v4l2_device_register_subdev(v4l2_dev, sd); + if (ret) + goto sd_err; + + fimc->vid_cap.subdev = sd; + v4l2_set_subdevdata(sd, fimc); + sd->entity.ops = &fimc_sd_media_ops; + return 0; +sd_err: + media_entity_cleanup(&sd->entity); +me_err: + kfree(sd); + return ret; +} + +static void fimc_destroy_capture_subdev(struct fimc_dev *fimc) +{ + struct v4l2_subdev *sd = fimc->vid_cap.subdev; + + if (!sd) + return; + media_entity_cleanup(&sd->entity); + v4l2_device_unregister_subdev(sd); + kfree(sd); + sd = NULL; +} + +/* Set default format at the sensor and host interface */ +static int fimc_capture_set_default_format(struct fimc_dev *fimc) +{ + struct v4l2_format fmt = { + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, + .fmt.pix_mp = { + .width = 640, + .height = 480, + .pixelformat = V4L2_PIX_FMT_YUYV, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_JPEG, + }, + }; + + return fimc_capture_set_format(fimc, &fmt); +} + /* fimc->lock must be already initialized */ -int fimc_register_capture_device(struct fimc_dev *fimc) +int fimc_register_capture_device(struct fimc_dev *fimc, + struct v4l2_device *v4l2_dev) { - struct v4l2_device *v4l2_dev = &fimc->vid_cap.v4l2_dev; struct video_device *vfd; struct fimc_vid_cap *vid_cap; struct fimc_ctx *ctx; - struct v4l2_format f; - struct fimc_frame *fr; struct vb2_queue *q; - int ret; + int ret = -ENOMEM; ctx = kzalloc(sizeof *ctx, GFP_KERNEL); if (!ctx) @@ -812,33 +1413,21 @@ int fimc_register_capture_device(struct fimc_dev *fimc) ctx->in_path = FIMC_CAMERA; ctx->out_path = FIMC_DMA; ctx->state = FIMC_CTX_CAP; - - /* Default format of the output frames */ - f.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB32; - fr = &ctx->d_frame; - fr->fmt = find_format(&f, FMT_FLAGS_M2M); - fr->width = fr->f_width = fr->o_width = 640; - fr->height = fr->f_height = fr->o_height = 480; - - if (!v4l2_dev->name[0]) - snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), - "%s.capture", dev_name(&fimc->pdev->dev)); - - ret = v4l2_device_register(NULL, v4l2_dev); - if (ret) - goto err_info; + ctx->s_frame.fmt = fimc_find_format(NULL, NULL, FMT_FLAGS_CAM, 0); + ctx->d_frame.fmt = fimc_find_format(NULL, NULL, FMT_FLAGS_CAM, 0); vfd = video_device_alloc(); if (!vfd) { v4l2_err(v4l2_dev, "Failed to allocate video device\n"); - goto err_v4l2_reg; + goto err_vd_alloc; } - snprintf(vfd->name, sizeof(vfd->name), "%s:cap", + snprintf(vfd->name, sizeof(vfd->name), "%s.capture", dev_name(&fimc->pdev->dev)); vfd->fops = &fimc_capture_fops; vfd->ioctl_ops = &fimc_capture_ioctl_ops; + vfd->v4l2_dev = v4l2_dev; vfd->minor = -1; vfd->release = video_device_release; vfd->lock = &fimc->lock; @@ -849,8 +1438,6 @@ int fimc_register_capture_device(struct fimc_dev *fimc) vid_cap->active_buf_cnt = 0; vid_cap->reqbufs_count = 0; vid_cap->refcnt = 0; - /* Default color format for image sensor */ - vid_cap->fmt.code = V4L2_MBUS_FMT_YUYV8_2X8; INIT_LIST_HEAD(&vid_cap->pending_buf_q); INIT_LIST_HEAD(&vid_cap->active_buf_q); @@ -868,34 +1455,37 @@ int fimc_register_capture_device(struct fimc_dev *fimc) vb2_queue_init(q); - ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1); - if (ret) { - v4l2_err(v4l2_dev, "Failed to register video device\n"); - goto err_vd_reg; - } - - v4l2_info(v4l2_dev, - "FIMC capture driver registered as /dev/video%d\n", - vfd->num); + fimc->vid_cap.vd_pad.flags = MEDIA_PAD_FL_SINK; + ret = media_entity_init(&vfd->entity, 1, &fimc->vid_cap.vd_pad, 0); + if (ret) + goto err_ent; + ret = fimc_create_capture_subdev(fimc, v4l2_dev); + if (ret) + goto err_sd_reg; + vfd->ctrl_handler = &ctx->ctrl_handler; return 0; -err_vd_reg: +err_sd_reg: + media_entity_cleanup(&vfd->entity); +err_ent: video_device_release(vfd); -err_v4l2_reg: - v4l2_device_unregister(v4l2_dev); -err_info: +err_vd_alloc: kfree(ctx); - dev_err(&fimc->pdev->dev, "failed to install\n"); return ret; } void fimc_unregister_capture_device(struct fimc_dev *fimc) { - struct fimc_vid_cap *capture = &fimc->vid_cap; - - if (capture->vfd) - video_unregister_device(capture->vfd); + struct video_device *vfd = fimc->vid_cap.vfd; - kfree(capture->ctx); + if (vfd) { + media_entity_cleanup(&vfd->entity); + /* Can also be called if video device was + not registered */ + video_unregister_device(vfd); + } + fimc_destroy_capture_subdev(fimc); + kfree(fimc->vid_cap.ctx); + fimc->vid_cap.ctx = NULL; } diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c index aa550666cc0..19ca6db38b2 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.c +++ b/drivers/media/video/s5p-fimc/fimc-core.c @@ -18,6 +18,7 @@ #include <linux/interrupt.h> #include <linux/device.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <linux/list.h> #include <linux/io.h> #include <linux/slab.h> @@ -27,9 +28,10 @@ #include <media/videobuf2-dma-contig.h> #include "fimc-core.h" +#include "fimc-mdevice.h" static char *fimc_clocks[MAX_FIMC_CLOCKS] = { - "sclk_fimc", "fimc", "sclk_cam" + "sclk_fimc", "fimc" }; static struct fimc_fmt fimc_formats[] = { @@ -157,59 +159,28 @@ static struct fimc_fmt fimc_formats[] = { .memplanes = 2, .colplanes = 2, .flags = FMT_FLAGS_M2M, - }, -}; - -static struct v4l2_queryctrl fimc_ctrls[] = { - { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Horizontal flip", - .minimum = 0, - .maximum = 1, - .default_value = 0, }, { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Vertical flip", - .minimum = 0, - .maximum = 1, - .default_value = 0, - }, { - .id = V4L2_CID_ROTATE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Rotation (CCW)", - .minimum = 0, - .maximum = 270, - .step = 90, - .default_value = 0, + .name = "JPEG encoded data", + .fourcc = V4L2_PIX_FMT_JPEG, + .color = S5P_FIMC_JPEG, + .depth = { 8 }, + .memplanes = 1, + .colplanes = 1, + .mbus_code = V4L2_MBUS_FMT_JPEG_1X8, + .flags = FMT_FLAGS_CAM, }, }; - -static struct v4l2_queryctrl *get_ctrl(int id) +int fimc_check_scaler_ratio(struct fimc_ctx *ctx, int sw, int sh, + int dw, int dh, int rotation) { - int i; - - for (i = 0; i < ARRAY_SIZE(fimc_ctrls); ++i) - if (id == fimc_ctrls[i].id) - return &fimc_ctrls[i]; - return NULL; -} + if (rotation == 90 || rotation == 270) + swap(dw, dh); -int fimc_check_scaler_ratio(int sw, int sh, int dw, int dh, int rot) -{ - int tx, ty; - - if (rot == 90 || rot == 270) { - ty = dw; - tx = dh; - } else { - tx = dw; - ty = dh; - } + if (!ctx->scaler.enabled) + return (sw == dw && sh == dh) ? 0 : -EINVAL; - if ((sw >= SCALER_MAX_HRATIO * tx) || (sh >= SCALER_MAX_VRATIO * ty)) + if ((sw >= SCALER_MAX_HRATIO * dw) || (sh >= SCALER_MAX_VRATIO * dh)) return -EINVAL; return 0; @@ -235,10 +206,11 @@ static int fimc_get_scaler_factor(u32 src, u32 tar, u32 *ratio, u32 *shift) int fimc_set_scaler_info(struct fimc_ctx *ctx) { + struct samsung_fimc_variant *variant = ctx->fimc_dev->variant; + struct device *dev = &ctx->fimc_dev->pdev->dev; struct fimc_scaler *sc = &ctx->scaler; struct fimc_frame *s_frame = &ctx->s_frame; struct fimc_frame *d_frame = &ctx->d_frame; - struct samsung_fimc_variant *variant = ctx->fimc_dev->variant; int tx, ty, sx, sy; int ret; @@ -250,15 +222,14 @@ int fimc_set_scaler_info(struct fimc_ctx *ctx) ty = d_frame->height; } if (tx <= 0 || ty <= 0) { - v4l2_err(&ctx->fimc_dev->m2m.v4l2_dev, - "invalid target size: %d x %d", tx, ty); + dev_err(dev, "Invalid target size: %dx%d", tx, ty); return -EINVAL; } sx = s_frame->width; sy = s_frame->height; if (sx <= 0 || sy <= 0) { - err("invalid source size: %d x %d", sx, sy); + dev_err(dev, "Invalid source size: %dx%d", sx, sy); return -EINVAL; } sc->real_width = sx; @@ -301,7 +272,6 @@ int fimc_set_scaler_info(struct fimc_ctx *ctx) static void fimc_m2m_job_finish(struct fimc_ctx *ctx, int vb_state) { struct vb2_buffer *src_vb, *dst_vb; - struct fimc_dev *fimc = ctx->fimc_dev; if (!ctx || !ctx->m2m_ctx) return; @@ -312,54 +282,68 @@ static void fimc_m2m_job_finish(struct fimc_ctx *ctx, int vb_state) if (src_vb && dst_vb) { v4l2_m2m_buf_done(src_vb, vb_state); v4l2_m2m_buf_done(dst_vb, vb_state); - v4l2_m2m_job_finish(fimc->m2m.m2m_dev, ctx->m2m_ctx); + v4l2_m2m_job_finish(ctx->fimc_dev->m2m.m2m_dev, + ctx->m2m_ctx); } } /* Complete the transaction which has been scheduled for execution. */ -static void fimc_m2m_shutdown(struct fimc_ctx *ctx) +static int fimc_m2m_shutdown(struct fimc_ctx *ctx) { struct fimc_dev *fimc = ctx->fimc_dev; int ret; if (!fimc_m2m_pending(fimc)) - return; + return 0; fimc_ctx_state_lock_set(FIMC_CTX_SHUT, ctx); ret = wait_event_timeout(fimc->irq_queue, !fimc_ctx_state_is_set(FIMC_CTX_SHUT, ctx), FIMC_SHUTDOWN_TIMEOUT); - /* - * In case of a timeout the buffers are not released in the interrupt - * handler so return them here with the error flag set, if there are - * any on the queue. - */ - if (ret == 0) - fimc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR); + + return ret == 0 ? -ETIMEDOUT : ret; +} + +static int start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct fimc_ctx *ctx = q->drv_priv; + int ret; + + ret = pm_runtime_get_sync(&ctx->fimc_dev->pdev->dev); + return ret > 0 ? 0 : ret; } static int stop_streaming(struct vb2_queue *q) { struct fimc_ctx *ctx = q->drv_priv; + int ret; - fimc_m2m_shutdown(ctx); + ret = fimc_m2m_shutdown(ctx); + if (ret == -ETIMEDOUT) + fimc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR); + pm_runtime_put(&ctx->fimc_dev->pdev->dev); return 0; } -static void fimc_capture_irq_handler(struct fimc_dev *fimc) +void fimc_capture_irq_handler(struct fimc_dev *fimc, bool final) { struct fimc_vid_cap *cap = &fimc->vid_cap; struct fimc_vid_buffer *v_buf; struct timeval *tv; struct timespec ts; + if (test_and_clear_bit(ST_CAPT_SHUT, &fimc->state)) { + wake_up(&fimc->irq_queue); + return; + } + if (!list_empty(&cap->active_buf_q) && - test_bit(ST_CAPT_RUN, &fimc->state)) { + test_bit(ST_CAPT_RUN, &fimc->state) && final) { ktime_get_real_ts(&ts); - v_buf = active_queue_pop(cap); + v_buf = fimc_active_queue_pop(cap); tv = &v_buf->vb.v4l2_buf.timestamp; tv->tv_sec = ts.tv_sec; @@ -369,19 +353,14 @@ static void fimc_capture_irq_handler(struct fimc_dev *fimc) vb2_buffer_done(&v_buf->vb, VB2_BUF_STATE_DONE); } - if (test_and_clear_bit(ST_CAPT_SHUT, &fimc->state)) { - wake_up(&fimc->irq_queue); - return; - } - if (!list_empty(&cap->pending_buf_q)) { - v_buf = pending_queue_pop(cap); + v_buf = fimc_pending_queue_pop(cap); fimc_hw_set_output_addr(fimc, &v_buf->paddr, cap->buf_index); v_buf->index = cap->buf_index; /* Move the buffer to the capture active queue */ - active_queue_add(cap, v_buf); + fimc_active_queue_add(cap, v_buf); dbg("next frame: %d, done frame: %d", fimc_hw_get_frame_index(fimc), v_buf->index); @@ -391,7 +370,8 @@ static void fimc_capture_irq_handler(struct fimc_dev *fimc) } if (cap->active_buf_cnt == 0) { - clear_bit(ST_CAPT_RUN, &fimc->state); + if (final) + clear_bit(ST_CAPT_RUN, &fimc->state); if (++cap->buf_index >= FIMC_MAX_OUT_BUFS) cap->buf_index = 0; @@ -399,11 +379,13 @@ static void fimc_capture_irq_handler(struct fimc_dev *fimc) set_bit(ST_CAPT_RUN, &fimc->state); } + fimc_capture_config_update(cap->ctx); + dbg("frame: %d, active_buf_cnt: %d", fimc_hw_get_frame_index(fimc), cap->active_buf_cnt); } -static irqreturn_t fimc_isr(int irq, void *priv) +static irqreturn_t fimc_irq_handler(int irq, void *priv) { struct fimc_dev *fimc = priv; struct fimc_vid_cap *cap = &fimc->vid_cap; @@ -411,9 +393,17 @@ static irqreturn_t fimc_isr(int irq, void *priv) fimc_hw_clear_irq(fimc); + spin_lock(&fimc->slock); + if (test_and_clear_bit(ST_M2M_PEND, &fimc->state)) { + if (test_and_clear_bit(ST_M2M_SUSPENDING, &fimc->state)) { + set_bit(ST_M2M_SUSPENDED, &fimc->state); + wake_up(&fimc->irq_queue); + goto out; + } ctx = v4l2_m2m_get_curr_priv(fimc->m2m.m2m_dev); if (ctx != NULL) { + spin_unlock(&fimc->slock); fimc_m2m_job_finish(ctx, VB2_BUF_STATE_DONE); spin_lock(&ctx->slock); @@ -423,21 +413,16 @@ static irqreturn_t fimc_isr(int irq, void *priv) } spin_unlock(&ctx->slock); } - return IRQ_HANDLED; - } - - spin_lock(&fimc->slock); - - if (test_bit(ST_CAPT_PEND, &fimc->state)) { - fimc_capture_irq_handler(fimc); - + } else if (test_bit(ST_CAPT_PEND, &fimc->state)) { + fimc_capture_irq_handler(fimc, + !test_bit(ST_CAPT_JPEG, &fimc->state)); if (cap->active_buf_cnt == 1) { fimc_deactivate_capture(fimc); clear_bit(ST_CAPT_STREAM, &fimc->state); } } - +out: spin_unlock(&fimc->slock); return IRQ_HANDLED; } @@ -457,7 +442,7 @@ int fimc_prepare_addr(struct fimc_ctx *ctx, struct vb2_buffer *vb, dbg("memplanes= %d, colplanes= %d, pix_size= %d", frame->fmt->memplanes, frame->fmt->colplanes, pix_size); - paddr->y = vb2_dma_contig_plane_paddr(vb, 0); + paddr->y = vb2_dma_contig_plane_dma_addr(vb, 0); if (frame->fmt->memplanes == 1) { switch (frame->fmt->colplanes) { @@ -485,10 +470,10 @@ int fimc_prepare_addr(struct fimc_ctx *ctx, struct vb2_buffer *vb, } } else { if (frame->fmt->memplanes >= 2) - paddr->cb = vb2_dma_contig_plane_paddr(vb, 1); + paddr->cb = vb2_dma_contig_plane_dma_addr(vb, 1); if (frame->fmt->memplanes == 3) - paddr->cr = vb2_dma_contig_plane_paddr(vb, 2); + paddr->cr = vb2_dma_contig_plane_dma_addr(vb, 2); } dbg("PHYS_ADDR: y= 0x%X cb= 0x%X cr= 0x%X ret= %d", @@ -498,7 +483,7 @@ int fimc_prepare_addr(struct fimc_ctx *ctx, struct vb2_buffer *vb, } /* Set order for 1 and 2 plane YCBCR 4:2:2 formats. */ -static void fimc_set_yuv_order(struct fimc_ctx *ctx) +void fimc_set_yuv_order(struct fimc_ctx *ctx) { /* The one only mode supported in SoC. */ ctx->in_order_2p = S5P_FIMC_LSB_CRCB; @@ -540,7 +525,7 @@ static void fimc_set_yuv_order(struct fimc_ctx *ctx) dbg("ctx->out_order_1p= %d", ctx->out_order_1p); } -static void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f) +void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f) { struct samsung_fimc_variant *variant = ctx->fimc_dev->variant; u32 i, depth = 0; @@ -606,9 +591,6 @@ int fimc_prepare_config(struct fimc_ctx *ctx, u32 flags) fimc_set_yuv_order(ctx); } - /* Input DMA mode is not allowed when the scaler is disabled. */ - ctx->scaler.enabled = 1; - if (flags & FIMC_SRC_ADDR) { vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx); ret = fimc_prepare_addr(ctx, vb, s_frame, &s_frame->paddr); @@ -635,10 +617,10 @@ static void fimc_dma_run(void *priv) return; fimc = ctx->fimc_dev; - - spin_lock_irqsave(&ctx->slock, flags); + spin_lock_irqsave(&fimc->slock, flags); set_bit(ST_M2M_PEND, &fimc->state); + spin_lock(&ctx->slock); ctx->state |= (FIMC_SRC_ADDR | FIMC_DST_ADDR); ret = fimc_prepare_config(ctx, ctx->state); if (ret) @@ -649,8 +631,6 @@ static void fimc_dma_run(void *priv) ctx->state |= FIMC_PARAMS; fimc->m2m.ctx = ctx; } - - spin_lock(&fimc->slock); fimc_hw_set_input_addr(fimc, &ctx->s_frame.paddr); if (ctx->state & FIMC_PARAMS) { @@ -665,7 +645,7 @@ static void fimc_dma_run(void *priv) fimc_hw_set_mainscaler(ctx); fimc_hw_set_target_format(ctx); fimc_hw_set_rotation(ctx); - fimc_hw_set_effect(ctx); + fimc_hw_set_effect(ctx, false); } fimc_hw_set_output_path(ctx); @@ -680,10 +660,9 @@ static void fimc_dma_run(void *priv) ctx->state &= (FIMC_CTX_M2M | FIMC_CTX_CAP | FIMC_SRC_FMT | FIMC_DST_FMT); fimc_hw_activate_input_dma(fimc, true); - spin_unlock(&fimc->slock); - dma_unlock: - spin_unlock_irqrestore(&ctx->slock, flags); + spin_unlock(&ctx->slock); + spin_unlock_irqrestore(&fimc->slock, flags); } static void fimc_job_abort(void *priv) @@ -691,9 +670,9 @@ static void fimc_job_abort(void *priv) fimc_m2m_shutdown(priv); } -static int fimc_queue_setup(struct vb2_queue *vq, unsigned int *num_buffers, - unsigned int *num_planes, unsigned long sizes[], - void *allocators[]) +static int fimc_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, + unsigned int *num_buffers, unsigned int *num_planes, + unsigned int sizes[], void *allocators[]) { struct fimc_ctx *ctx = vb2_get_drv_priv(vq); struct fimc_frame *f; @@ -762,146 +741,296 @@ static struct vb2_ops fimc_qops = { .wait_prepare = fimc_unlock, .wait_finish = fimc_lock, .stop_streaming = stop_streaming, + .start_streaming = start_streaming, }; -static int fimc_m2m_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) +/* + * V4L2 controls handling + */ +#define ctrl_to_ctx(__ctrl) \ + container_of((__ctrl)->handler, struct fimc_ctx, ctrl_handler) + +static int fimc_s_ctrl(struct v4l2_ctrl *ctrl) { - struct fimc_ctx *ctx = file->private_data; + struct fimc_ctx *ctx = ctrl_to_ctx(ctrl); + struct fimc_dev *fimc = ctx->fimc_dev; + struct samsung_fimc_variant *variant = fimc->variant; + unsigned long flags; + int ret = 0; + + if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE) + return 0; + + switch (ctrl->id) { + case V4L2_CID_HFLIP: + spin_lock_irqsave(&ctx->slock, flags); + ctx->hflip = ctrl->val; + break; + + case V4L2_CID_VFLIP: + spin_lock_irqsave(&ctx->slock, flags); + ctx->vflip = ctrl->val; + break; + + case V4L2_CID_ROTATE: + if (fimc_capture_pending(fimc) || + fimc_ctx_state_is_set(FIMC_DST_FMT | FIMC_SRC_FMT, ctx)) { + ret = fimc_check_scaler_ratio(ctx, ctx->s_frame.width, + ctx->s_frame.height, ctx->d_frame.width, + ctx->d_frame.height, ctrl->val); + } + if (ret) { + v4l2_err(fimc->m2m.vfd, "Out of scaler range\n"); + return -EINVAL; + } + if ((ctrl->val == 90 || ctrl->val == 270) && + !variant->has_out_rot) + return -EINVAL; + spin_lock_irqsave(&ctx->slock, flags); + ctx->rotation = ctrl->val; + break; + + default: + v4l2_err(fimc->v4l2_dev, "Invalid control: 0x%X\n", ctrl->id); + return -EINVAL; + } + ctx->state |= FIMC_PARAMS; + set_bit(ST_CAPT_APPLY_CFG, &fimc->state); + spin_unlock_irqrestore(&ctx->slock, flags); + return 0; +} + +static const struct v4l2_ctrl_ops fimc_ctrl_ops = { + .s_ctrl = fimc_s_ctrl, +}; + +int fimc_ctrls_create(struct fimc_ctx *ctx) +{ + if (ctx->ctrls_rdy) + return 0; + v4l2_ctrl_handler_init(&ctx->ctrl_handler, 3); + + ctx->ctrl_rotate = v4l2_ctrl_new_std(&ctx->ctrl_handler, &fimc_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + ctx->ctrl_hflip = v4l2_ctrl_new_std(&ctx->ctrl_handler, &fimc_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + ctx->ctrl_vflip = v4l2_ctrl_new_std(&ctx->ctrl_handler, &fimc_ctrl_ops, + V4L2_CID_ROTATE, 0, 270, 90, 0); + ctx->ctrls_rdy = ctx->ctrl_handler.error == 0; + + return ctx->ctrl_handler.error; +} + +void fimc_ctrls_delete(struct fimc_ctx *ctx) +{ + if (ctx->ctrls_rdy) { + v4l2_ctrl_handler_free(&ctx->ctrl_handler); + ctx->ctrls_rdy = false; + } +} + +void fimc_ctrls_activate(struct fimc_ctx *ctx, bool active) +{ + if (!ctx->ctrls_rdy) + return; + + mutex_lock(&ctx->ctrl_handler.lock); + v4l2_ctrl_activate(ctx->ctrl_rotate, active); + v4l2_ctrl_activate(ctx->ctrl_hflip, active); + v4l2_ctrl_activate(ctx->ctrl_vflip, active); + + if (active) { + ctx->rotation = ctx->ctrl_rotate->val; + ctx->hflip = ctx->ctrl_hflip->val; + ctx->vflip = ctx->ctrl_vflip->val; + } else { + ctx->rotation = 0; + ctx->hflip = 0; + ctx->vflip = 0; + } + mutex_unlock(&ctx->ctrl_handler.lock); +} + +/* + * V4L2 ioctl handlers + */ +static int fimc_m2m_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + struct fimc_ctx *ctx = fh_to_ctx(fh); struct fimc_dev *fimc = ctx->fimc_dev; strncpy(cap->driver, fimc->pdev->name, sizeof(cap->driver) - 1); strncpy(cap->card, fimc->pdev->name, sizeof(cap->card) - 1); cap->bus_info[0] = 0; cap->capabilities = V4L2_CAP_STREAMING | - V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE; return 0; } -int fimc_vidioc_enum_fmt_mplane(struct file *file, void *priv, - struct v4l2_fmtdesc *f) +static int fimc_m2m_enum_fmt_mplane(struct file *file, void *priv, + struct v4l2_fmtdesc *f) { struct fimc_fmt *fmt; - if (f->index >= ARRAY_SIZE(fimc_formats)) + fmt = fimc_find_format(NULL, NULL, FMT_FLAGS_M2M, f->index); + if (!fmt) return -EINVAL; - fmt = &fimc_formats[f->index]; strncpy(f->description, fmt->name, sizeof(f->description) - 1); f->pixelformat = fmt->fourcc; - return 0; } -int fimc_vidioc_g_fmt_mplane(struct file *file, void *priv, - struct v4l2_format *f) +int fimc_fill_format(struct fimc_frame *frame, struct v4l2_format *f) { - struct fimc_ctx *ctx = priv; - struct fimc_frame *frame; - struct v4l2_pix_format_mplane *pixm; + struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp; int i; - frame = ctx_get_frame(ctx, f->type); - if (IS_ERR(frame)) - return PTR_ERR(frame); - - pixm = &f->fmt.pix_mp; - - pixm->width = frame->width; - pixm->height = frame->height; - pixm->field = V4L2_FIELD_NONE; - pixm->pixelformat = frame->fmt->fourcc; - pixm->colorspace = V4L2_COLORSPACE_JPEG; - pixm->num_planes = frame->fmt->memplanes; + pixm->width = frame->o_width; + pixm->height = frame->o_height; + pixm->field = V4L2_FIELD_NONE; + pixm->pixelformat = frame->fmt->fourcc; + pixm->colorspace = V4L2_COLORSPACE_JPEG; + pixm->num_planes = frame->fmt->memplanes; for (i = 0; i < pixm->num_planes; ++i) { - int bpl = frame->o_width; - + int bpl = frame->f_width; if (frame->fmt->colplanes == 1) /* packed formats */ bpl = (bpl * frame->fmt->depth[0]) / 8; - pixm->plane_fmt[i].bytesperline = bpl; - pixm->plane_fmt[i].sizeimage = (frame->o_width * frame->o_height * frame->fmt->depth[i]) / 8; } - return 0; } -struct fimc_fmt *find_format(struct v4l2_format *f, unsigned int mask) +void fimc_fill_frame(struct fimc_frame *frame, struct v4l2_format *f) { - struct fimc_fmt *fmt; - unsigned int i; + struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp; + + frame->f_width = pixm->plane_fmt[0].bytesperline; + if (frame->fmt->colplanes == 1) + frame->f_width = (frame->f_width * 8) / frame->fmt->depth[0]; + frame->f_height = pixm->height; + frame->width = pixm->width; + frame->height = pixm->height; + frame->o_width = pixm->width; + frame->o_height = pixm->height; + frame->offs_h = 0; + frame->offs_v = 0; +} - for (i = 0; i < ARRAY_SIZE(fimc_formats); ++i) { - fmt = &fimc_formats[i]; - if (fmt->fourcc == f->fmt.pix_mp.pixelformat && - (fmt->flags & mask)) - break; +/** + * fimc_adjust_mplane_format - adjust bytesperline/sizeimage for each plane + * @fmt: fimc pixel format description (input) + * @width: requested pixel width + * @height: requested pixel height + * @pix: multi-plane format to adjust + */ +void fimc_adjust_mplane_format(struct fimc_fmt *fmt, u32 width, u32 height, + struct v4l2_pix_format_mplane *pix) +{ + u32 bytesperline = 0; + int i; + + pix->colorspace = V4L2_COLORSPACE_JPEG; + pix->field = V4L2_FIELD_NONE; + pix->num_planes = fmt->memplanes; + pix->height = height; + pix->width = width; + + for (i = 0; i < pix->num_planes; ++i) { + u32 bpl = pix->plane_fmt[i].bytesperline; + u32 *sizeimage = &pix->plane_fmt[i].sizeimage; + + if (fmt->colplanes > 1 && (bpl == 0 || bpl < pix->width)) + bpl = pix->width; /* Planar */ + + if (fmt->colplanes == 1 && /* Packed */ + (bpl == 0 || ((bpl * 8) / fmt->depth[i]) < pix->width)) + bpl = (pix->width * fmt->depth[0]) / 8; + + if (i == 0) /* Same bytesperline for each plane. */ + bytesperline = bpl; + + pix->plane_fmt[i].bytesperline = bytesperline; + *sizeimage = (pix->width * pix->height * fmt->depth[i]) / 8; } +} - return (i == ARRAY_SIZE(fimc_formats)) ? NULL : fmt; +static int fimc_m2m_g_fmt_mplane(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct fimc_ctx *ctx = fh_to_ctx(fh); + struct fimc_frame *frame = ctx_get_frame(ctx, f->type); + + if (IS_ERR(frame)) + return PTR_ERR(frame); + + return fimc_fill_format(frame, f); } -struct fimc_fmt *find_mbus_format(struct v4l2_mbus_framefmt *f, - unsigned int mask) +/** + * fimc_find_format - lookup fimc color format by fourcc or media bus format + * @pixelformat: fourcc to match, ignored if null + * @mbus_code: media bus code to match, ignored if null + * @mask: the color flags to match + * @index: offset in the fimc_formats array, ignored if negative + */ +struct fimc_fmt *fimc_find_format(u32 *pixelformat, u32 *mbus_code, + unsigned int mask, int index) { - struct fimc_fmt *fmt; + struct fimc_fmt *fmt, *def_fmt = NULL; unsigned int i; + int id = 0; + + if (index >= ARRAY_SIZE(fimc_formats)) + return NULL; for (i = 0; i < ARRAY_SIZE(fimc_formats); ++i) { fmt = &fimc_formats[i]; - if (fmt->mbus_code == f->code && (fmt->flags & mask)) - break; + if (!(fmt->flags & mask)) + continue; + if (pixelformat && fmt->fourcc == *pixelformat) + return fmt; + if (mbus_code && fmt->mbus_code == *mbus_code) + return fmt; + if (index == id) + def_fmt = fmt; + id++; } - - return (i == ARRAY_SIZE(fimc_formats)) ? NULL : fmt; + return def_fmt; } - -int fimc_vidioc_try_fmt_mplane(struct file *file, void *priv, - struct v4l2_format *f) +static int fimc_try_fmt_mplane(struct fimc_ctx *ctx, struct v4l2_format *f) { - struct fimc_ctx *ctx = priv; struct fimc_dev *fimc = ctx->fimc_dev; struct samsung_fimc_variant *variant = fimc->variant; struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; struct fimc_fmt *fmt; - u32 max_width, mod_x, mod_y, mask; - int i, is_output = 0; - + u32 max_w, mod_x, mod_y; - if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { - if (fimc_ctx_state_is_set(FIMC_CTX_CAP, ctx)) - return -EINVAL; - is_output = 1; - } else if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + if (!IS_M2M(f->type)) return -EINVAL; - } dbg("w: %d, h: %d", pix->width, pix->height); - mask = is_output ? FMT_FLAGS_M2M : FMT_FLAGS_M2M | FMT_FLAGS_CAM; - fmt = find_format(f, mask); - if (!fmt) { - v4l2_err(&fimc->m2m.v4l2_dev, "Fourcc format (0x%X) invalid.\n", - pix->pixelformat); + fmt = fimc_find_format(&pix->pixelformat, NULL, FMT_FLAGS_M2M, 0); + if (WARN(fmt == NULL, "Pixel format lookup failed")) return -EINVAL; - } if (pix->field == V4L2_FIELD_ANY) pix->field = V4L2_FIELD_NONE; - else if (V4L2_FIELD_NONE != pix->field) + else if (pix->field != V4L2_FIELD_NONE) return -EINVAL; - if (is_output) { - max_width = variant->pix_limit->scaler_dis_w; + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + max_w = variant->pix_limit->scaler_dis_w; mod_x = ffs(variant->min_inp_pixsize) - 1; } else { - max_width = variant->pix_limit->out_rot_dis_w; + max_w = variant->pix_limit->out_rot_dis_w; mod_x = ffs(variant->min_out_pixsize) - 1; } @@ -914,70 +1043,52 @@ int fimc_vidioc_try_fmt_mplane(struct file *file, void *priv, else mod_y = mod_x; } + dbg("mod_x: %d, mod_y: %d, max_w: %d", mod_x, mod_y, max_w); - dbg("mod_x: %d, mod_y: %d, max_w: %d", mod_x, mod_y, max_width); - - v4l_bound_align_image(&pix->width, 16, max_width, mod_x, + v4l_bound_align_image(&pix->width, 16, max_w, mod_x, &pix->height, 8, variant->pix_limit->scaler_dis_w, mod_y, 0); - pix->num_planes = fmt->memplanes; - pix->colorspace = V4L2_COLORSPACE_JPEG; - - - for (i = 0; i < pix->num_planes; ++i) { - u32 bpl = pix->plane_fmt[i].bytesperline; - u32 *sizeimage = &pix->plane_fmt[i].sizeimage; - - if (fmt->colplanes > 1 && (bpl == 0 || bpl < pix->width)) - bpl = pix->width; /* Planar */ - - if (fmt->colplanes == 1 && /* Packed */ - (bpl == 0 || ((bpl * 8) / fmt->depth[i]) < pix->width)) - bpl = (pix->width * fmt->depth[0]) / 8; - - if (i == 0) /* Same bytesperline for each plane. */ - mod_x = bpl; + fimc_adjust_mplane_format(fmt, pix->width, pix->height, &f->fmt.pix_mp); + return 0; +} - pix->plane_fmt[i].bytesperline = mod_x; - *sizeimage = (pix->width * pix->height * fmt->depth[i]) / 8; - } +static int fimc_m2m_try_fmt_mplane(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct fimc_ctx *ctx = fh_to_ctx(fh); - return 0; + return fimc_try_fmt_mplane(ctx, f); } -static int fimc_m2m_s_fmt_mplane(struct file *file, void *priv, +static int fimc_m2m_s_fmt_mplane(struct file *file, void *fh, struct v4l2_format *f) { - struct fimc_ctx *ctx = priv; + struct fimc_ctx *ctx = fh_to_ctx(fh); struct fimc_dev *fimc = ctx->fimc_dev; struct vb2_queue *vq; struct fimc_frame *frame; struct v4l2_pix_format_mplane *pix; int i, ret = 0; - ret = fimc_vidioc_try_fmt_mplane(file, priv, f); + ret = fimc_try_fmt_mplane(ctx, f); if (ret) return ret; vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); if (vb2_is_busy(vq)) { - v4l2_err(&fimc->m2m.v4l2_dev, "queue (%d) busy\n", f->type); + v4l2_err(fimc->m2m.vfd, "queue (%d) busy\n", f->type); return -EBUSY; } - if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) frame = &ctx->s_frame; - } else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + else frame = &ctx->d_frame; - } else { - v4l2_err(&fimc->m2m.v4l2_dev, - "Wrong buffer/video queue type (%d)\n", f->type); - return -EINVAL; - } pix = &f->fmt.pix_mp; - frame->fmt = find_format(f, FMT_FLAGS_M2M); + frame->fmt = fimc_find_format(&pix->pixelformat, NULL, + FMT_FLAGS_M2M, 0); if (!frame->fmt) return -EINVAL; @@ -986,15 +1097,9 @@ static int fimc_m2m_s_fmt_mplane(struct file *file, void *priv, (pix->width * pix->height * frame->fmt->depth[i]) / 8; } - frame->f_width = pix->plane_fmt[0].bytesperline * 8 / - frame->fmt->depth[0]; - frame->f_height = pix->height; - frame->width = pix->width; - frame->height = pix->height; - frame->o_width = pix->width; - frame->o_height = pix->height; - frame->offs_h = 0; - frame->offs_v = 0; + fimc_fill_frame(frame, f); + + ctx->scaler.enabled = 1; if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) fimc_ctx_state_lock_set(FIMC_PARAMS | FIMC_DST_FMT, ctx); @@ -1006,39 +1111,42 @@ static int fimc_m2m_s_fmt_mplane(struct file *file, void *priv, return 0; } -static int fimc_m2m_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *reqbufs) +static int fimc_m2m_reqbufs(struct file *file, void *fh, + struct v4l2_requestbuffers *reqbufs) { - struct fimc_ctx *ctx = priv; + struct fimc_ctx *ctx = fh_to_ctx(fh); + return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs); } -static int fimc_m2m_querybuf(struct file *file, void *priv, - struct v4l2_buffer *buf) +static int fimc_m2m_querybuf(struct file *file, void *fh, + struct v4l2_buffer *buf) { - struct fimc_ctx *ctx = priv; + struct fimc_ctx *ctx = fh_to_ctx(fh); + return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf); } -static int fimc_m2m_qbuf(struct file *file, void *priv, - struct v4l2_buffer *buf) +static int fimc_m2m_qbuf(struct file *file, void *fh, + struct v4l2_buffer *buf) { - struct fimc_ctx *ctx = priv; + struct fimc_ctx *ctx = fh_to_ctx(fh); return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf); } -static int fimc_m2m_dqbuf(struct file *file, void *priv, - struct v4l2_buffer *buf) +static int fimc_m2m_dqbuf(struct file *file, void *fh, + struct v4l2_buffer *buf) { - struct fimc_ctx *ctx = priv; + struct fimc_ctx *ctx = fh_to_ctx(fh); + return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf); } -static int fimc_m2m_streamon(struct file *file, void *priv, - enum v4l2_buf_type type) +static int fimc_m2m_streamon(struct file *file, void *fh, + enum v4l2_buf_type type) { - struct fimc_ctx *ctx = priv; + struct fimc_ctx *ctx = fh_to_ctx(fh); /* The source and target color format need to be set */ if (V4L2_TYPE_IS_OUTPUT(type)) { @@ -1051,149 +1159,19 @@ static int fimc_m2m_streamon(struct file *file, void *priv, return v4l2_m2m_streamon(file, ctx->m2m_ctx, type); } -static int fimc_m2m_streamoff(struct file *file, void *priv, +static int fimc_m2m_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) { - struct fimc_ctx *ctx = priv; - return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type); -} - -int fimc_vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) -{ - struct fimc_ctx *ctx = priv; - struct v4l2_queryctrl *c; - int ret = -EINVAL; - - c = get_ctrl(qc->id); - if (c) { - *qc = *c; - return 0; - } - - if (fimc_ctx_state_is_set(FIMC_CTX_CAP, ctx)) { - return v4l2_subdev_call(ctx->fimc_dev->vid_cap.sd, - core, queryctrl, qc); - } - return ret; -} - -int fimc_vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct fimc_ctx *ctx = priv; - struct fimc_dev *fimc = ctx->fimc_dev; + struct fimc_ctx *ctx = fh_to_ctx(fh); - switch (ctrl->id) { - case V4L2_CID_HFLIP: - ctrl->value = (FLIP_X_AXIS & ctx->flip) ? 1 : 0; - break; - case V4L2_CID_VFLIP: - ctrl->value = (FLIP_Y_AXIS & ctx->flip) ? 1 : 0; - break; - case V4L2_CID_ROTATE: - ctrl->value = ctx->rotation; - break; - default: - if (fimc_ctx_state_is_set(FIMC_CTX_CAP, ctx)) { - return v4l2_subdev_call(fimc->vid_cap.sd, core, - g_ctrl, ctrl); - } else { - v4l2_err(&fimc->m2m.v4l2_dev, "Invalid control\n"); - return -EINVAL; - } - } - dbg("ctrl->value= %d", ctrl->value); - - return 0; -} - -int check_ctrl_val(struct fimc_ctx *ctx, struct v4l2_control *ctrl) -{ - struct v4l2_queryctrl *c; - c = get_ctrl(ctrl->id); - if (!c) - return -EINVAL; - - if (ctrl->value < c->minimum || ctrl->value > c->maximum - || (c->step != 0 && ctrl->value % c->step != 0)) { - v4l2_err(&ctx->fimc_dev->m2m.v4l2_dev, - "Invalid control value\n"); - return -ERANGE; - } - - return 0; -} - -int fimc_s_ctrl(struct fimc_ctx *ctx, struct v4l2_control *ctrl) -{ - struct samsung_fimc_variant *variant = ctx->fimc_dev->variant; - struct fimc_dev *fimc = ctx->fimc_dev; - int ret = 0; - - switch (ctrl->id) { - case V4L2_CID_HFLIP: - if (ctrl->value) - ctx->flip |= FLIP_X_AXIS; - else - ctx->flip &= ~FLIP_X_AXIS; - break; - - case V4L2_CID_VFLIP: - if (ctrl->value) - ctx->flip |= FLIP_Y_AXIS; - else - ctx->flip &= ~FLIP_Y_AXIS; - break; - - case V4L2_CID_ROTATE: - if (fimc_ctx_state_is_set(FIMC_DST_FMT | FIMC_SRC_FMT, ctx)) { - ret = fimc_check_scaler_ratio(ctx->s_frame.width, - ctx->s_frame.height, ctx->d_frame.width, - ctx->d_frame.height, ctrl->value); - } - - if (ret) { - v4l2_err(&fimc->m2m.v4l2_dev, "Out of scaler range\n"); - return -EINVAL; - } - - /* Check for the output rotator availability */ - if ((ctrl->value == 90 || ctrl->value == 270) && - (ctx->in_path == FIMC_DMA && !variant->has_out_rot)) - return -EINVAL; - ctx->rotation = ctrl->value; - break; - - default: - v4l2_err(&fimc->m2m.v4l2_dev, "Invalid control\n"); - return -EINVAL; - } - - fimc_ctx_state_lock_set(FIMC_PARAMS, ctx); - - return 0; -} - -static int fimc_m2m_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct fimc_ctx *ctx = priv; - int ret = 0; - - ret = check_ctrl_val(ctx, ctrl); - if (ret) - return ret; - - ret = fimc_s_ctrl(ctx, ctrl); - return 0; + return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type); } static int fimc_m2m_cropcap(struct file *file, void *fh, - struct v4l2_cropcap *cr) + struct v4l2_cropcap *cr) { + struct fimc_ctx *ctx = fh_to_ctx(fh); struct fimc_frame *frame; - struct fimc_ctx *ctx = fh; frame = ctx_get_frame(ctx, cr->type); if (IS_ERR(frame)) @@ -1201,8 +1179,8 @@ static int fimc_m2m_cropcap(struct file *file, void *fh, cr->bounds.left = 0; cr->bounds.top = 0; - cr->bounds.width = frame->f_width; - cr->bounds.height = frame->f_height; + cr->bounds.width = frame->o_width; + cr->bounds.height = frame->o_height; cr->defrect = cr->bounds; return 0; @@ -1210,8 +1188,8 @@ static int fimc_m2m_cropcap(struct file *file, void *fh, static int fimc_m2m_g_crop(struct file *file, void *fh, struct v4l2_crop *cr) { + struct fimc_ctx *ctx = fh_to_ctx(fh); struct fimc_frame *frame; - struct fimc_ctx *ctx = file->private_data; frame = ctx_get_frame(ctx, cr->type); if (IS_ERR(frame)) @@ -1225,26 +1203,21 @@ static int fimc_m2m_g_crop(struct file *file, void *fh, struct v4l2_crop *cr) return 0; } -int fimc_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr) +static int fimc_m2m_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr) { struct fimc_dev *fimc = ctx->fimc_dev; struct fimc_frame *f; u32 min_size, halign, depth = 0; - bool is_capture_ctx; int i; if (cr->c.top < 0 || cr->c.left < 0) { - v4l2_err(&fimc->m2m.v4l2_dev, + v4l2_err(fimc->m2m.vfd, "doesn't support negative values for top & left\n"); return -EINVAL; } - - is_capture_ctx = fimc_ctx_state_is_set(FIMC_CTX_CAP, ctx); - if (cr->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) - f = is_capture_ctx ? &ctx->s_frame : &ctx->d_frame; - else if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE && - !is_capture_ctx) + f = &ctx->d_frame; + else if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) f = &ctx->s_frame; else return -EINVAL; @@ -1253,15 +1226,10 @@ int fimc_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr) fimc->variant->min_inp_pixsize : fimc->variant->min_out_pixsize; /* Get pixel alignment constraints. */ - if (is_capture_ctx) { - min_size = 16; - halign = 4; - } else { - if (fimc->id == 1 && fimc->variant->pix_hoff) - halign = fimc_fmt_is_rgb(f->fmt->color) ? 0 : 1; - else - halign = ffs(min_size) - 1; - } + if (fimc->id == 1 && fimc->variant->pix_hoff) + halign = fimc_fmt_is_rgb(f->fmt->color) ? 0 : 1; + else + halign = ffs(min_size) - 1; for (i = 0; i < f->fmt->colplanes; i++) depth += f->fmt->depth[i]; @@ -1278,7 +1246,7 @@ int fimc_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr) cr->c.top = f->o_height - cr->c.height; cr->c.left = round_down(cr->c.left, min_size); - cr->c.top = round_down(cr->c.top, is_capture_ctx ? 16 : 8); + cr->c.top = round_down(cr->c.top, fimc->variant->hor_offs_align); dbg("l:%d, t:%d, w:%d, h:%d, f_w: %d, f_h: %d", cr->c.left, cr->c.top, cr->c.width, cr->c.height, @@ -1289,12 +1257,12 @@ int fimc_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr) static int fimc_m2m_s_crop(struct file *file, void *fh, struct v4l2_crop *cr) { - struct fimc_ctx *ctx = file->private_data; + struct fimc_ctx *ctx = fh_to_ctx(fh); struct fimc_dev *fimc = ctx->fimc_dev; struct fimc_frame *f; int ret; - ret = fimc_try_crop(ctx, cr); + ret = fimc_m2m_try_crop(ctx, cr); if (ret) return ret; @@ -1304,18 +1272,16 @@ static int fimc_m2m_s_crop(struct file *file, void *fh, struct v4l2_crop *cr) /* Check to see if scaling ratio is within supported range */ if (fimc_ctx_state_is_set(FIMC_DST_FMT | FIMC_SRC_FMT, ctx)) { if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { - ret = fimc_check_scaler_ratio(cr->c.width, cr->c.height, - ctx->d_frame.width, - ctx->d_frame.height, - ctx->rotation); + ret = fimc_check_scaler_ratio(ctx, cr->c.width, + cr->c.height, ctx->d_frame.width, + ctx->d_frame.height, ctx->rotation); } else { - ret = fimc_check_scaler_ratio(ctx->s_frame.width, - ctx->s_frame.height, - cr->c.width, cr->c.height, - ctx->rotation); + ret = fimc_check_scaler_ratio(ctx, ctx->s_frame.width, + ctx->s_frame.height, cr->c.width, + cr->c.height, ctx->rotation); } if (ret) { - v4l2_err(&fimc->m2m.v4l2_dev, "Out of scaler range\n"); + v4l2_err(fimc->m2m.vfd, "Out of scaler range\n"); return -EINVAL; } } @@ -1333,14 +1299,14 @@ static int fimc_m2m_s_crop(struct file *file, void *fh, struct v4l2_crop *cr) static const struct v4l2_ioctl_ops fimc_m2m_ioctl_ops = { .vidioc_querycap = fimc_m2m_querycap, - .vidioc_enum_fmt_vid_cap_mplane = fimc_vidioc_enum_fmt_mplane, - .vidioc_enum_fmt_vid_out_mplane = fimc_vidioc_enum_fmt_mplane, + .vidioc_enum_fmt_vid_cap_mplane = fimc_m2m_enum_fmt_mplane, + .vidioc_enum_fmt_vid_out_mplane = fimc_m2m_enum_fmt_mplane, - .vidioc_g_fmt_vid_cap_mplane = fimc_vidioc_g_fmt_mplane, - .vidioc_g_fmt_vid_out_mplane = fimc_vidioc_g_fmt_mplane, + .vidioc_g_fmt_vid_cap_mplane = fimc_m2m_g_fmt_mplane, + .vidioc_g_fmt_vid_out_mplane = fimc_m2m_g_fmt_mplane, - .vidioc_try_fmt_vid_cap_mplane = fimc_vidioc_try_fmt_mplane, - .vidioc_try_fmt_vid_out_mplane = fimc_vidioc_try_fmt_mplane, + .vidioc_try_fmt_vid_cap_mplane = fimc_m2m_try_fmt_mplane, + .vidioc_try_fmt_vid_out_mplane = fimc_m2m_try_fmt_mplane, .vidioc_s_fmt_vid_cap_mplane = fimc_m2m_s_fmt_mplane, .vidioc_s_fmt_vid_out_mplane = fimc_m2m_s_fmt_mplane, @@ -1354,10 +1320,6 @@ static const struct v4l2_ioctl_ops fimc_m2m_ioctl_ops = { .vidioc_streamon = fimc_m2m_streamon, .vidioc_streamoff = fimc_m2m_streamoff, - .vidioc_queryctrl = fimc_vidioc_queryctrl, - .vidioc_g_ctrl = fimc_vidioc_g_ctrl, - .vidioc_s_ctrl = fimc_m2m_s_ctrl, - .vidioc_g_crop = fimc_m2m_g_crop, .vidioc_s_crop = fimc_m2m_s_crop, .vidioc_cropcap = fimc_m2m_cropcap @@ -1396,7 +1358,8 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, static int fimc_m2m_open(struct file *file) { struct fimc_dev *fimc = video_drvdata(file); - struct fimc_ctx *ctx = NULL; + struct fimc_ctx *ctx; + int ret; dbg("pid: %d, state: 0x%lx, refcnt: %d", task_pid_nr(current), fimc->state, fimc->vid_cap.refcnt); @@ -1408,19 +1371,24 @@ static int fimc_m2m_open(struct file *file) if (fimc->vid_cap.refcnt > 0) return -EBUSY; - fimc->m2m.refcnt++; - set_bit(ST_OUTDMA_RUN, &fimc->state); - ctx = kzalloc(sizeof *ctx, GFP_KERNEL); if (!ctx) return -ENOMEM; + v4l2_fh_init(&ctx->fh, fimc->m2m.vfd); + ret = fimc_ctrls_create(ctx); + if (ret) + goto error_fh; + + /* Use separate control handler per file handle */ + ctx->fh.ctrl_handler = &ctx->ctrl_handler; + file->private_data = &ctx->fh; + v4l2_fh_add(&ctx->fh); - file->private_data = ctx; ctx->fimc_dev = fimc; /* Default color format */ ctx->s_frame.fmt = &fimc_formats[0]; ctx->d_frame.fmt = &fimc_formats[0]; - /* Setup the device context for mem2mem mode. */ + /* Setup the device context for memory-to-memory mode */ ctx->state = FIMC_CTX_M2M; ctx->flags = 0; ctx->in_path = FIMC_DMA; @@ -1429,34 +1397,46 @@ static int fimc_m2m_open(struct file *file) ctx->m2m_ctx = v4l2_m2m_ctx_init(fimc->m2m.m2m_dev, ctx, queue_init); if (IS_ERR(ctx->m2m_ctx)) { - int err = PTR_ERR(ctx->m2m_ctx); - kfree(ctx); - return err; + ret = PTR_ERR(ctx->m2m_ctx); + goto error_c; } + if (fimc->m2m.refcnt++ == 0) + set_bit(ST_M2M_RUN, &fimc->state); return 0; + +error_c: + fimc_ctrls_delete(ctx); +error_fh: + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); + kfree(ctx); + return ret; } static int fimc_m2m_release(struct file *file) { - struct fimc_ctx *ctx = file->private_data; + struct fimc_ctx *ctx = fh_to_ctx(file->private_data); struct fimc_dev *fimc = ctx->fimc_dev; dbg("pid: %d, state: 0x%lx, refcnt= %d", task_pid_nr(current), fimc->state, fimc->m2m.refcnt); v4l2_m2m_ctx_release(ctx->m2m_ctx); - kfree(ctx); - if (--fimc->m2m.refcnt <= 0) - clear_bit(ST_OUTDMA_RUN, &fimc->state); + fimc_ctrls_delete(ctx); + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); + if (--fimc->m2m.refcnt <= 0) + clear_bit(ST_M2M_RUN, &fimc->state); + kfree(ctx); return 0; } static unsigned int fimc_m2m_poll(struct file *file, - struct poll_table_struct *wait) + struct poll_table_struct *wait) { - struct fimc_ctx *ctx = file->private_data; + struct fimc_ctx *ctx = fh_to_ctx(file->private_data); return v4l2_m2m_poll(file, ctx->m2m_ctx, wait); } @@ -1464,7 +1444,7 @@ static unsigned int fimc_m2m_poll(struct file *file, static int fimc_m2m_mmap(struct file *file, struct vm_area_struct *vma) { - struct fimc_ctx *ctx = file->private_data; + struct fimc_ctx *ctx = fh_to_ctx(file->private_data); return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma); } @@ -1483,92 +1463,73 @@ static struct v4l2_m2m_ops m2m_ops = { .job_abort = fimc_job_abort, }; -static int fimc_register_m2m_device(struct fimc_dev *fimc) +int fimc_register_m2m_device(struct fimc_dev *fimc, + struct v4l2_device *v4l2_dev) { struct video_device *vfd; struct platform_device *pdev; - struct v4l2_device *v4l2_dev; int ret = 0; if (!fimc) return -ENODEV; pdev = fimc->pdev; - v4l2_dev = &fimc->m2m.v4l2_dev; - - /* set name if it is empty */ - if (!v4l2_dev->name[0]) - snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), - "%s.m2m", dev_name(&pdev->dev)); - - ret = v4l2_device_register(&pdev->dev, v4l2_dev); - if (ret) - goto err_m2m_r1; + fimc->v4l2_dev = v4l2_dev; vfd = video_device_alloc(); if (!vfd) { v4l2_err(v4l2_dev, "Failed to allocate video device\n"); - goto err_m2m_r1; + return -ENOMEM; } vfd->fops = &fimc_m2m_fops; vfd->ioctl_ops = &fimc_m2m_ioctl_ops; + vfd->v4l2_dev = v4l2_dev; vfd->minor = -1; vfd->release = video_device_release; vfd->lock = &fimc->lock; - snprintf(vfd->name, sizeof(vfd->name), "%s:m2m", dev_name(&pdev->dev)); - + snprintf(vfd->name, sizeof(vfd->name), "%s.m2m", dev_name(&pdev->dev)); video_set_drvdata(vfd, fimc); - platform_set_drvdata(pdev, fimc); fimc->m2m.vfd = vfd; fimc->m2m.m2m_dev = v4l2_m2m_init(&m2m_ops); if (IS_ERR(fimc->m2m.m2m_dev)) { v4l2_err(v4l2_dev, "failed to initialize v4l2-m2m device\n"); ret = PTR_ERR(fimc->m2m.m2m_dev); - goto err_m2m_r2; - } - - ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1); - if (ret) { - v4l2_err(v4l2_dev, - "%s(): failed to register video device\n", __func__); - goto err_m2m_r3; + goto err_init; } - v4l2_info(v4l2_dev, - "FIMC m2m driver registered as /dev/video%d\n", vfd->num); - return 0; + ret = media_entity_init(&vfd->entity, 0, NULL, 0); + if (!ret) + return 0; -err_m2m_r3: v4l2_m2m_release(fimc->m2m.m2m_dev); -err_m2m_r2: +err_init: video_device_release(fimc->m2m.vfd); -err_m2m_r1: - v4l2_device_unregister(v4l2_dev); - return ret; } -static void fimc_unregister_m2m_device(struct fimc_dev *fimc) +void fimc_unregister_m2m_device(struct fimc_dev *fimc) { - if (fimc) { + if (!fimc) + return; + + if (fimc->m2m.m2m_dev) v4l2_m2m_release(fimc->m2m.m2m_dev); + if (fimc->m2m.vfd) { + media_entity_cleanup(&fimc->m2m.vfd->entity); + /* Can also be called if video device wasn't registered */ video_unregister_device(fimc->m2m.vfd); - - v4l2_device_unregister(&fimc->m2m.v4l2_dev); } } -static void fimc_clk_release(struct fimc_dev *fimc) +static void fimc_clk_put(struct fimc_dev *fimc) { int i; for (i = 0; i < fimc->num_clocks; i++) { - if (fimc->clock[i]) { - clk_disable(fimc->clock[i]); + if (fimc->clock[i]) clk_put(fimc->clock[i]); - } } } @@ -1577,15 +1538,50 @@ static int fimc_clk_get(struct fimc_dev *fimc) int i; for (i = 0; i < fimc->num_clocks; i++) { fimc->clock[i] = clk_get(&fimc->pdev->dev, fimc_clocks[i]); - - if (!IS_ERR_OR_NULL(fimc->clock[i])) { - clk_enable(fimc->clock[i]); + if (!IS_ERR_OR_NULL(fimc->clock[i])) continue; - } dev_err(&fimc->pdev->dev, "failed to get fimc clock: %s\n", fimc_clocks[i]); return -ENXIO; } + + return 0; +} + +static int fimc_m2m_suspend(struct fimc_dev *fimc) +{ + unsigned long flags; + int timeout; + + spin_lock_irqsave(&fimc->slock, flags); + if (!fimc_m2m_pending(fimc)) { + spin_unlock_irqrestore(&fimc->slock, flags); + return 0; + } + clear_bit(ST_M2M_SUSPENDED, &fimc->state); + set_bit(ST_M2M_SUSPENDING, &fimc->state); + spin_unlock_irqrestore(&fimc->slock, flags); + + timeout = wait_event_timeout(fimc->irq_queue, + test_bit(ST_M2M_SUSPENDED, &fimc->state), + FIMC_SHUTDOWN_TIMEOUT); + + clear_bit(ST_M2M_SUSPENDING, &fimc->state); + return timeout == 0 ? -EAGAIN : 0; +} + +static int fimc_m2m_resume(struct fimc_dev *fimc) +{ + unsigned long flags; + + spin_lock_irqsave(&fimc->slock, flags); + /* Clear for full H/W setup in first run after resume */ + fimc->m2m.ctx = NULL; + spin_unlock_irqrestore(&fimc->slock, flags); + + if (test_and_clear_bit(ST_M2M_SUSPENDED, &fimc->state)) + fimc_m2m_job_finish(fimc->m2m.ctx, + VB2_BUF_STATE_ERROR); return 0; } @@ -1596,7 +1592,6 @@ static int fimc_probe(struct platform_device *pdev) struct samsung_fimc_driverdata *drv_data; struct s5p_platform_fimc *pdata; int ret = 0; - int cap_input_index = -1; dev_dbg(&pdev->dev, "%s():\n", __func__); @@ -1614,15 +1609,16 @@ static int fimc_probe(struct platform_device *pdev) return -ENOMEM; fimc->id = pdev->id; + fimc->variant = drv_data->variant[fimc->id]; fimc->pdev = pdev; pdata = pdev->dev.platform_data; fimc->pdata = pdata; - fimc->state = ST_IDLE; + + set_bit(ST_LPM, &fimc->state); init_waitqueue_head(&fimc->irq_queue); spin_lock_init(&fimc->slock); - mutex_init(&fimc->lock); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -1647,71 +1643,51 @@ static int fimc_probe(struct platform_device *pdev) goto err_req_region; } - fimc->num_clocks = MAX_FIMC_CLOCKS - 1; - - /* Check if a video capture node needs to be registered. */ - if (pdata && pdata->num_clients > 0) { - cap_input_index = 0; - fimc->num_clocks++; - } - - ret = fimc_clk_get(fimc); - if (ret) - goto err_regs_unmap; - clk_set_rate(fimc->clock[CLK_BUS], drv_data->lclk_frequency); - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!res) { dev_err(&pdev->dev, "failed to get IRQ resource\n"); ret = -ENXIO; - goto err_clk; + goto err_regs_unmap; } fimc->irq = res->start; - fimc_hw_reset(fimc); + fimc->num_clocks = MAX_FIMC_CLOCKS; + ret = fimc_clk_get(fimc); + if (ret) + goto err_regs_unmap; + clk_set_rate(fimc->clock[CLK_BUS], drv_data->lclk_frequency); + clk_enable(fimc->clock[CLK_BUS]); - ret = request_irq(fimc->irq, fimc_isr, 0, pdev->name, fimc); + platform_set_drvdata(pdev, fimc); + + ret = request_irq(fimc->irq, fimc_irq_handler, 0, pdev->name, fimc); if (ret) { dev_err(&pdev->dev, "failed to install irq (%d)\n", ret); goto err_clk; } + pm_runtime_enable(&pdev->dev); + ret = pm_runtime_get_sync(&pdev->dev); + if (ret < 0) + goto err_irq; /* Initialize contiguous memory allocator */ - fimc->alloc_ctx = vb2_dma_contig_init_ctx(&fimc->pdev->dev); + fimc->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); if (IS_ERR(fimc->alloc_ctx)) { ret = PTR_ERR(fimc->alloc_ctx); - goto err_irq; + goto err_pm; } - ret = fimc_register_m2m_device(fimc); - if (ret) - goto err_irq; - - /* At least one camera sensor is required to register capture node */ - if (cap_input_index >= 0) { - ret = fimc_register_capture_device(fimc); - if (ret) - goto err_m2m; - clk_disable(fimc->clock[CLK_CAM]); - } - /* - * Exclude the additional output DMA address registers by masking - * them out on HW revisions that provide extended capabilites. - */ - if (fimc->variant->out_buf_count > 4) - fimc_hw_set_dma_seq(fimc, 0xF); - - dev_dbg(&pdev->dev, "%s(): fimc-%d registered successfully\n", - __func__, fimc->id); + dev_dbg(&pdev->dev, "FIMC.%d registered successfully\n", fimc->id); + pm_runtime_put(&pdev->dev); return 0; -err_m2m: - fimc_unregister_m2m_device(fimc); +err_pm: + pm_runtime_put(&pdev->dev); err_irq: free_irq(fimc->irq, fimc); err_clk: - fimc_clk_release(fimc); + fimc_clk_put(fimc); err_regs_unmap: iounmap(fimc->regs); err_req_region: @@ -1719,31 +1695,105 @@ err_req_region: kfree(fimc->regs_res); err_info: kfree(fimc); + return ret; +} +static int fimc_runtime_resume(struct device *dev) +{ + struct fimc_dev *fimc = dev_get_drvdata(dev); + + dbg("fimc%d: state: 0x%lx", fimc->id, fimc->state); + + /* Enable clocks and perform basic initalization */ + clk_enable(fimc->clock[CLK_GATE]); + fimc_hw_reset(fimc); + if (fimc->variant->out_buf_count > 4) + fimc_hw_set_dma_seq(fimc, 0xF); + + /* Resume the capture or mem-to-mem device */ + if (fimc_capture_busy(fimc)) + return fimc_capture_resume(fimc); + else if (fimc_m2m_pending(fimc)) + return fimc_m2m_resume(fimc); + return 0; +} + +static int fimc_runtime_suspend(struct device *dev) +{ + struct fimc_dev *fimc = dev_get_drvdata(dev); + int ret = 0; + + if (fimc_capture_busy(fimc)) + ret = fimc_capture_suspend(fimc); + else + ret = fimc_m2m_suspend(fimc); + if (!ret) + clk_disable(fimc->clock[CLK_GATE]); + + dbg("fimc%d: state: 0x%lx", fimc->id, fimc->state); return ret; } -static int __devexit fimc_remove(struct platform_device *pdev) +#ifdef CONFIG_PM_SLEEP +static int fimc_resume(struct device *dev) { - struct fimc_dev *fimc = - (struct fimc_dev *)platform_get_drvdata(pdev); + struct fimc_dev *fimc = dev_get_drvdata(dev); + unsigned long flags; - free_irq(fimc->irq, fimc); + dbg("fimc%d: state: 0x%lx", fimc->id, fimc->state); + + /* Do not resume if the device was idle before system suspend */ + spin_lock_irqsave(&fimc->slock, flags); + if (!test_and_clear_bit(ST_LPM, &fimc->state) || + (!fimc_m2m_active(fimc) && !fimc_capture_busy(fimc))) { + spin_unlock_irqrestore(&fimc->slock, flags); + return 0; + } fimc_hw_reset(fimc); + if (fimc->variant->out_buf_count > 4) + fimc_hw_set_dma_seq(fimc, 0xF); + spin_unlock_irqrestore(&fimc->slock, flags); + + if (fimc_capture_busy(fimc)) + return fimc_capture_resume(fimc); - fimc_unregister_m2m_device(fimc); - fimc_unregister_capture_device(fimc); + return fimc_m2m_resume(fimc); +} + +static int fimc_suspend(struct device *dev) +{ + struct fimc_dev *fimc = dev_get_drvdata(dev); + + dbg("fimc%d: state: 0x%lx", fimc->id, fimc->state); - fimc_clk_release(fimc); + if (test_and_set_bit(ST_LPM, &fimc->state)) + return 0; + if (fimc_capture_busy(fimc)) + return fimc_capture_suspend(fimc); + + return fimc_m2m_suspend(fimc); +} +#endif /* CONFIG_PM_SLEEP */ + +static int __devexit fimc_remove(struct platform_device *pdev) +{ + struct fimc_dev *fimc = platform_get_drvdata(pdev); + + pm_runtime_disable(&pdev->dev); + fimc_runtime_suspend(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); vb2_dma_contig_cleanup_ctx(fimc->alloc_ctx); + clk_disable(fimc->clock[CLK_BUS]); + fimc_clk_put(fimc); + free_irq(fimc->irq, fimc); iounmap(fimc->regs); release_resource(fimc->regs_res); kfree(fimc->regs_res); kfree(fimc); - dev_info(&pdev->dev, "%s driver unloaded\n", pdev->name); + dev_info(&pdev->dev, "driver unloaded\n"); return 0; } @@ -1786,6 +1836,7 @@ static struct fimc_pix_limit s5p_pix_limit[4] = { static struct samsung_fimc_variant fimc0_variant_s5p = { .has_inp_rot = 1, .has_out_rot = 1, + .has_cam_if = 1, .min_inp_pixsize = 16, .min_out_pixsize = 16, .hor_offs_align = 8, @@ -1794,6 +1845,7 @@ static struct samsung_fimc_variant fimc0_variant_s5p = { }; static struct samsung_fimc_variant fimc2_variant_s5p = { + .has_cam_if = 1, .min_inp_pixsize = 16, .min_out_pixsize = 16, .hor_offs_align = 8, @@ -1805,6 +1857,7 @@ static struct samsung_fimc_variant fimc0_variant_s5pv210 = { .pix_hoff = 1, .has_inp_rot = 1, .has_out_rot = 1, + .has_cam_if = 1, .min_inp_pixsize = 16, .min_out_pixsize = 16, .hor_offs_align = 8, @@ -1816,6 +1869,7 @@ static struct samsung_fimc_variant fimc1_variant_s5pv210 = { .pix_hoff = 1, .has_inp_rot = 1, .has_out_rot = 1, + .has_cam_if = 1, .has_mainscaler_ext = 1, .min_inp_pixsize = 16, .min_out_pixsize = 16, @@ -1825,6 +1879,7 @@ static struct samsung_fimc_variant fimc1_variant_s5pv210 = { }; static struct samsung_fimc_variant fimc2_variant_s5pv210 = { + .has_cam_if = 1, .pix_hoff = 1, .min_inp_pixsize = 16, .min_out_pixsize = 16, @@ -1837,22 +1892,24 @@ static struct samsung_fimc_variant fimc0_variant_exynos4 = { .pix_hoff = 1, .has_inp_rot = 1, .has_out_rot = 1, + .has_cam_if = 1, .has_cistatus2 = 1, .has_mainscaler_ext = 1, .min_inp_pixsize = 16, .min_out_pixsize = 16, - .hor_offs_align = 1, + .hor_offs_align = 2, .out_buf_count = 32, .pix_limit = &s5p_pix_limit[1], }; -static struct samsung_fimc_variant fimc2_variant_exynos4 = { +static struct samsung_fimc_variant fimc3_variant_exynos4 = { .pix_hoff = 1, + .has_cam_if = 1, .has_cistatus2 = 1, .has_mainscaler_ext = 1, .min_inp_pixsize = 16, .min_out_pixsize = 16, - .hor_offs_align = 1, + .hor_offs_align = 2, .out_buf_count = 32, .pix_limit = &s5p_pix_limit[3], }; @@ -1885,7 +1942,7 @@ static struct samsung_fimc_driverdata fimc_drvdata_exynos4 = { [0] = &fimc0_variant_exynos4, [1] = &fimc0_variant_exynos4, [2] = &fimc0_variant_exynos4, - [3] = &fimc2_variant_exynos4, + [3] = &fimc3_variant_exynos4, }, .num_entities = 4, .lclk_frequency = 166000000UL, @@ -1906,33 +1963,28 @@ static struct platform_device_id fimc_driver_ids[] = { }; MODULE_DEVICE_TABLE(platform, fimc_driver_ids); +static const struct dev_pm_ops fimc_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(fimc_suspend, fimc_resume) + SET_RUNTIME_PM_OPS(fimc_runtime_suspend, fimc_runtime_resume, NULL) +}; + static struct platform_driver fimc_driver = { .probe = fimc_probe, - .remove = __devexit_p(fimc_remove), + .remove = __devexit_p(fimc_remove), .id_table = fimc_driver_ids, .driver = { - .name = MODULE_NAME, + .name = FIMC_MODULE_NAME, .owner = THIS_MODULE, + .pm = &fimc_pm_ops, } }; -static int __init fimc_init(void) +int __init fimc_register_driver(void) { - int ret = platform_driver_register(&fimc_driver); - if (ret) - err("platform_driver_register failed: %d\n", ret); - return ret; + return platform_driver_probe(&fimc_driver, fimc_probe); } -static void __exit fimc_exit(void) +void __exit fimc_unregister_driver(void) { platform_driver_unregister(&fimc_driver); } - -module_init(fimc_init); -module_exit(fimc_exit); - -MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>"); -MODULE_DESCRIPTION("S5P FIMC camera host interface/video postprocessor driver"); -MODULE_LICENSE("GPL"); -MODULE_VERSION("1.0.1"); diff --git a/drivers/media/video/s5p-fimc/fimc-core.h b/drivers/media/video/s5p-fimc/fimc-core.h index 1f70772daaf..a6936dad5b1 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.h +++ b/drivers/media/video/s5p-fimc/fimc-core.h @@ -11,12 +11,16 @@ /*#define DEBUG*/ +#include <linux/platform_device.h> #include <linux/sched.h> #include <linux/spinlock.h> #include <linux/types.h> #include <linux/videodev2.h> #include <linux/io.h> + +#include <media/media-entity.h> #include <media/videobuf2-core.h> +#include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> #include <media/v4l2-mem2mem.h> #include <media/v4l2-mediabus.h> @@ -32,38 +36,46 @@ /* Time to wait for next frame VSYNC interrupt while stopping operation. */ #define FIMC_SHUTDOWN_TIMEOUT ((100*HZ)/1000) -#define MAX_FIMC_CLOCKS 3 -#define MODULE_NAME "s5p-fimc" +#define MAX_FIMC_CLOCKS 2 +#define FIMC_MODULE_NAME "s5p-fimc" #define FIMC_MAX_DEVS 4 #define FIMC_MAX_OUT_BUFS 4 #define SCALER_MAX_HRATIO 64 #define SCALER_MAX_VRATIO 64 #define DMA_MIN_SIZE 8 +#define FIMC_CAMIF_MAX_HEIGHT 0x2000 /* indices to the clocks array */ enum { CLK_BUS, CLK_GATE, - CLK_CAM, }; enum fimc_dev_flags { - /* for m2m node */ - ST_IDLE, - ST_OUTDMA_RUN, + ST_LPM, + /* m2m node */ + ST_M2M_RUN, ST_M2M_PEND, - /* for capture node */ + ST_M2M_SUSPENDING, + ST_M2M_SUSPENDED, + /* capture node */ ST_CAPT_PEND, ST_CAPT_RUN, ST_CAPT_STREAM, + ST_CAPT_ISP_STREAM, + ST_CAPT_SUSPENDED, ST_CAPT_SHUT, + ST_CAPT_BUSY, + ST_CAPT_APPLY_CFG, + ST_CAPT_JPEG, }; -#define fimc_m2m_active(dev) test_bit(ST_OUTDMA_RUN, &(dev)->state) +#define fimc_m2m_active(dev) test_bit(ST_M2M_RUN, &(dev)->state) #define fimc_m2m_pending(dev) test_bit(ST_M2M_PEND, &(dev)->state) #define fimc_capture_running(dev) test_bit(ST_CAPT_RUN, &(dev)->state) #define fimc_capture_pending(dev) test_bit(ST_CAPT_PEND, &(dev)->state) +#define fimc_capture_busy(dev) test_bit(ST_CAPT_BUSY, &(dev)->state) enum fimc_datapath { FIMC_CAMERA, @@ -83,9 +95,14 @@ enum fimc_color_fmt { S5P_FIMC_CBYCRY422, S5P_FIMC_CRYCBY422, S5P_FIMC_YCBCR444_LOCAL, + S5P_FIMC_JPEG = 0x40, }; -#define fimc_fmt_is_rgb(x) ((x) & 0x10) +#define fimc_fmt_is_rgb(x) (!!((x) & 0x10)) +#define fimc_fmt_is_jpeg(x) (!!((x) & 0x40)) + +#define IS_M2M(__strt) ((__strt) == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE || \ + __strt == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) /* Cb/Cr chrominance components order for 2 plane Y/CbCr 4:2:2 formats. */ #define S5P_FIMC_LSB_CRCB S5P_CIOCTRL_ORDER422_2P_LSB_CRCB @@ -104,9 +121,10 @@ enum fimc_color_fmt { #define FIMC_DST_ADDR (1 << 2) #define FIMC_SRC_FMT (1 << 3) #define FIMC_DST_FMT (1 << 4) -#define FIMC_CTX_M2M (1 << 5) -#define FIMC_CTX_CAP (1 << 6) -#define FIMC_CTX_SHUT (1 << 7) +#define FIMC_DST_CROP (1 << 5) +#define FIMC_CTX_M2M (1 << 16) +#define FIMC_CTX_CAP (1 << 17) +#define FIMC_CTX_SHUT (1 << 18) /* Image conversion flags */ #define FIMC_IN_DMA_ACCESS_TILED (1 << 0) @@ -122,11 +140,6 @@ enum fimc_color_fmt { /* Y (16 ~ 235), Cb/Cr (16 ~ 240) */ #define FIMC_COLOR_RANGE_NARROW (1 << 3) -#define FLIP_NONE 0 -#define FLIP_X_AXIS 1 -#define FLIP_Y_AXIS 2 -#define FLIP_XY_AXIS (FLIP_X_AXIS | FLIP_Y_AXIS) - /** * struct fimc_fmt - the driver's internal color format data * @mbus_code: Media Bus pixel code, -1 if not applicable @@ -275,26 +288,29 @@ struct fimc_frame { /** * struct fimc_m2m_device - v4l2 memory-to-memory device data * @vfd: the video device node for v4l2 m2m mode - * @v4l2_dev: v4l2 device for m2m mode * @m2m_dev: v4l2 memory-to-memory device data * @ctx: hardware context data * @refcnt: the reference counter */ struct fimc_m2m_device { struct video_device *vfd; - struct v4l2_device v4l2_dev; struct v4l2_m2m_dev *m2m_dev; struct fimc_ctx *ctx; int refcnt; }; +#define FIMC_SD_PAD_SINK 0 +#define FIMC_SD_PAD_SOURCE 1 +#define FIMC_SD_PADS_NUM 2 + /** * struct fimc_vid_cap - camera capture device information * @ctx: hardware context data * @vfd: video device node for camera capture mode - * @v4l2_dev: v4l2_device struct to manage subdevs - * @sd: pointer to camera sensor subdevice currently in use - * @fmt: Media Bus format configured at selected image sensor + * @subdev: subdev exposing the FIMC processing block + * @vd_pad: fimc video capture node pad + * @sd_pads: fimc video processing block pads + * @mf: media bus format at the FIMC camera input (and the scaler output) pad * @pending_buf_q: the pending buffer queue head * @active_buf_q: the queue head of buffers scheduled in hardware * @vbq: the capture am video buffer queue @@ -304,14 +320,17 @@ struct fimc_m2m_device { * @reqbufs_count: the number of buffers requested in REQBUFS ioctl * @input_index: input (camera sensor) index * @refcnt: driver's private reference counter + * @input: capture input type, grp_id of the attached subdev + * @user_subdev_api: true if subdevs are not configured by the host driver */ struct fimc_vid_cap { struct fimc_ctx *ctx; struct vb2_alloc_ctx *alloc_ctx; struct video_device *vfd; - struct v4l2_device v4l2_dev; - struct v4l2_subdev *sd;; - struct v4l2_mbus_framefmt fmt; + struct v4l2_subdev *subdev; + struct media_pad vd_pad; + struct v4l2_mbus_framefmt mf; + struct media_pad sd_pads[FIMC_SD_PADS_NUM]; struct list_head pending_buf_q; struct list_head active_buf_q; struct vb2_queue vbq; @@ -321,6 +340,8 @@ struct fimc_vid_cap { unsigned int reqbufs_count; int input_index; int refcnt; + u32 input; + bool user_subdev_api; }; /** @@ -351,6 +372,7 @@ struct fimc_pix_limit { * @has_cistatus2: 1 if CISTATUS2 register is present in this IP revision * @has_mainscaler_ext: 1 if extended mainscaler ratios in CIEXTEN register * are present in this IP revision + * @has_cam_if: set if this instance has a camera input interface * @pix_limit: pixel size constraints for the scaler * @min_inp_pixsize: minimum input pixel size * @min_out_pixsize: minimum output pixel size @@ -363,6 +385,7 @@ struct samsung_fimc_variant { unsigned int has_out_rot:1; unsigned int has_cistatus2:1; unsigned int has_mainscaler_ext:1; + unsigned int has_cam_if:1; struct fimc_pix_limit *pix_limit; u16 min_inp_pixsize; u16 min_out_pixsize; @@ -383,6 +406,12 @@ struct samsung_fimc_driverdata { int num_entities; }; +struct fimc_pipeline { + struct media_pipeline *pipe; + struct v4l2_subdev *sensor; + struct v4l2_subdev *csis; +}; + struct fimc_ctx; /** @@ -399,10 +428,12 @@ struct fimc_ctx; * @regs_res: the resource claimed for IO registers * @irq: FIMC interrupt number * @irq_queue: interrupt handler waitqueue + * @v4l2_dev: root v4l2_device * @m2m: memory-to-memory V4L2 device information * @vid_cap: camera capture device information * @state: flags used to synchronize m2m and capture mode operation * @alloc_ctx: videobuf2 memory allocator context + * @pipeline: fimc video capture pipeline data structure */ struct fimc_dev { spinlock_t slock; @@ -417,10 +448,12 @@ struct fimc_dev { struct resource *regs_res; int irq; wait_queue_head_t irq_queue; + struct v4l2_device *v4l2_dev; struct fimc_m2m_device m2m; struct fimc_vid_cap vid_cap; unsigned long state; struct vb2_alloc_ctx *alloc_ctx; + struct fimc_pipeline pipeline; }; /** @@ -437,11 +470,18 @@ struct fimc_dev { * @scaler: image scaler properties * @effect: image effect * @rotation: image clockwise rotation in degrees - * @flip: image flip mode + * @hflip: indicates image horizontal flip if set + * @vflip: indicates image vertical flip if set * @flags: additional flags for image conversion * @state: flags to keep track of user configuration * @fimc_dev: the FIMC device this context applies to * @m2m_ctx: memory-to-memory device context + * @fh: v4l2 file handle + * @ctrl_handler: v4l2 controls handler + * @ctrl_rotate image rotation control + * @ctrl_hflip horizontal flip control + * @ctrl_vflip vartical flip control + * @ctrls_rdy: true if the control handler is initialized */ struct fimc_ctx { spinlock_t slock; @@ -456,13 +496,49 @@ struct fimc_ctx { struct fimc_scaler scaler; struct fimc_effect effect; int rotation; - u32 flip; + unsigned int hflip:1; + unsigned int vflip:1; u32 flags; u32 state; struct fimc_dev *fimc_dev; struct v4l2_m2m_ctx *m2m_ctx; + struct v4l2_fh fh; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *ctrl_rotate; + struct v4l2_ctrl *ctrl_hflip; + struct v4l2_ctrl *ctrl_vflip; + bool ctrls_rdy; }; +#define fh_to_ctx(__fh) container_of(__fh, struct fimc_ctx, fh) + +static inline void set_frame_bounds(struct fimc_frame *f, u32 width, u32 height) +{ + f->o_width = width; + f->o_height = height; + f->f_width = width; + f->f_height = height; +} + +static inline void set_frame_crop(struct fimc_frame *f, + u32 left, u32 top, u32 width, u32 height) +{ + f->offs_h = left; + f->offs_v = top; + f->width = width; + f->height = height; +} + +static inline u32 fimc_get_format_depth(struct fimc_fmt *ff) +{ + u32 i, depth = 0; + + if (ff != NULL) + for (i = 0; i < ff->colplanes; i++) + depth += ff->depth[i]; + return depth; +} + static inline bool fimc_capture_active(struct fimc_dev *fimc) { unsigned long flags; @@ -561,7 +637,7 @@ static inline struct fimc_frame *ctx_get_frame(struct fimc_ctx *ctx, } else if (V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE == type) { frame = &ctx->d_frame; } else { - v4l2_err(&ctx->fimc_dev->m2m.v4l2_dev, + v4l2_err(ctx->fimc_dev->v4l2_dev, "Wrong buffer/video queue type (%d)\n", type); return ERR_PTR(-EINVAL); } @@ -595,7 +671,7 @@ void fimc_hw_en_irq(struct fimc_dev *fimc, int enable); void fimc_hw_set_prescaler(struct fimc_ctx *ctx); void fimc_hw_set_mainscaler(struct fimc_ctx *ctx); void fimc_hw_en_capture(struct fimc_ctx *ctx); -void fimc_hw_set_effect(struct fimc_ctx *ctx); +void fimc_hw_set_effect(struct fimc_ctx *ctx, bool active); void fimc_hw_set_in_dma(struct fimc_ctx *ctx); void fimc_hw_set_input_path(struct fimc_ctx *ctx); void fimc_hw_set_output_path(struct fimc_ctx *ctx); @@ -614,36 +690,45 @@ int fimc_hw_set_camera_type(struct fimc_dev *fimc, /* fimc-core.c */ int fimc_vidioc_enum_fmt_mplane(struct file *file, void *priv, struct v4l2_fmtdesc *f); -int fimc_vidioc_g_fmt_mplane(struct file *file, void *priv, - struct v4l2_format *f); -int fimc_vidioc_try_fmt_mplane(struct file *file, void *priv, - struct v4l2_format *f); -int fimc_vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc); -int fimc_vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl); - -int fimc_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr); -int check_ctrl_val(struct fimc_ctx *ctx, struct v4l2_control *ctrl); -int fimc_s_ctrl(struct fimc_ctx *ctx, struct v4l2_control *ctrl); - -struct fimc_fmt *find_format(struct v4l2_format *f, unsigned int mask); -struct fimc_fmt *find_mbus_format(struct v4l2_mbus_framefmt *f, - unsigned int mask); - -int fimc_check_scaler_ratio(int sw, int sh, int dw, int dh, int rot); +int fimc_ctrls_create(struct fimc_ctx *ctx); +void fimc_ctrls_delete(struct fimc_ctx *ctx); +void fimc_ctrls_activate(struct fimc_ctx *ctx, bool active); +int fimc_fill_format(struct fimc_frame *frame, struct v4l2_format *f); +void fimc_adjust_mplane_format(struct fimc_fmt *fmt, u32 width, u32 height, + struct v4l2_pix_format_mplane *pix); +struct fimc_fmt *fimc_find_format(u32 *pixelformat, u32 *mbus_code, + unsigned int mask, int index); + +int fimc_check_scaler_ratio(struct fimc_ctx *ctx, int sw, int sh, + int dw, int dh, int rotation); int fimc_set_scaler_info(struct fimc_ctx *ctx); int fimc_prepare_config(struct fimc_ctx *ctx, u32 flags); int fimc_prepare_addr(struct fimc_ctx *ctx, struct vb2_buffer *vb, struct fimc_frame *frame, struct fimc_addr *paddr); +void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f); +void fimc_set_yuv_order(struct fimc_ctx *ctx); +void fimc_fill_frame(struct fimc_frame *frame, struct v4l2_format *f); +void fimc_capture_irq_handler(struct fimc_dev *fimc, bool done); + +int fimc_register_m2m_device(struct fimc_dev *fimc, + struct v4l2_device *v4l2_dev); +void fimc_unregister_m2m_device(struct fimc_dev *fimc); +int fimc_register_driver(void); +void fimc_unregister_driver(void); /* -----------------------------------------------------*/ /* fimc-capture.c */ -int fimc_register_capture_device(struct fimc_dev *fimc); +int fimc_register_capture_device(struct fimc_dev *fimc, + struct v4l2_device *v4l2_dev); void fimc_unregister_capture_device(struct fimc_dev *fimc); -int fimc_sensor_sd_init(struct fimc_dev *fimc, int index); +int fimc_capture_ctrls_create(struct fimc_dev *fimc); int fimc_vid_cap_buf_queue(struct fimc_dev *fimc, struct fimc_vid_buffer *fimc_vb); +void fimc_sensor_notify(struct v4l2_subdev *sd, unsigned int notification, + void *arg); +int fimc_capture_suspend(struct fimc_dev *fimc); +int fimc_capture_resume(struct fimc_dev *fimc); +int fimc_capture_config_update(struct fimc_ctx *ctx); /* Locking: the caller holds fimc->slock */ static inline void fimc_activate_capture(struct fimc_ctx *ctx) @@ -661,22 +746,27 @@ static inline void fimc_deactivate_capture(struct fimc_dev *fimc) } /* - * Add buf to the capture active buffers queue. - * Locking: Need to be called with fimc_dev::slock held. + * Buffer list manipulation functions. Must be called with fimc.slock held. */ -static inline void active_queue_add(struct fimc_vid_cap *vid_cap, - struct fimc_vid_buffer *buf) + +/** + * fimc_active_queue_add - add buffer to the capture active buffers queue + * @buf: buffer to add to the active buffers list + */ +static inline void fimc_active_queue_add(struct fimc_vid_cap *vid_cap, + struct fimc_vid_buffer *buf) { list_add_tail(&buf->list, &vid_cap->active_buf_q); vid_cap->active_buf_cnt++; } -/* - * Pop a video buffer from the capture active buffers queue - * Locking: Need to be called with fimc_dev::slock held. +/** + * fimc_active_queue_pop - pop buffer from the capture active buffers queue + * + * The caller must assure the active_buf_q list is not empty. */ -static inline struct fimc_vid_buffer * -active_queue_pop(struct fimc_vid_cap *vid_cap) +static inline struct fimc_vid_buffer *fimc_active_queue_pop( + struct fimc_vid_cap *vid_cap) { struct fimc_vid_buffer *buf; buf = list_entry(vid_cap->active_buf_q.next, @@ -686,16 +776,23 @@ active_queue_pop(struct fimc_vid_cap *vid_cap) return buf; } -/* Add video buffer to the capture pending buffers queue */ +/** + * fimc_pending_queue_add - add buffer to the capture pending buffers queue + * @buf: buffer to add to the pending buffers list + */ static inline void fimc_pending_queue_add(struct fimc_vid_cap *vid_cap, struct fimc_vid_buffer *buf) { list_add_tail(&buf->list, &vid_cap->pending_buf_q); } -/* Add video buffer to the capture pending buffers queue */ -static inline struct fimc_vid_buffer * -pending_queue_pop(struct fimc_vid_cap *vid_cap) +/** + * fimc_pending_queue_pop - pop buffer from the capture pending buffers queue + * + * The caller must assure the pending_buf_q list is not empty. + */ +static inline struct fimc_vid_buffer *fimc_pending_queue_pop( + struct fimc_vid_cap *vid_cap) { struct fimc_vid_buffer *buf; buf = list_entry(vid_cap->pending_buf_q.next, diff --git a/drivers/media/video/s5p-fimc/fimc-mdevice.c b/drivers/media/video/s5p-fimc/fimc-mdevice.c new file mode 100644 index 00000000000..cc337b1de91 --- /dev/null +++ b/drivers/media/video/s5p-fimc/fimc-mdevice.c @@ -0,0 +1,858 @@ +/* + * S5P/EXYNOS4 SoC series camera host interface media device driver + * + * Copyright (C) 2011 Samsung Electronics Co., Ltd. + * Contact: Sylwester Nawrocki, <s.nawrocki@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation, either version 2 of the License, + * or (at your option) any later version. + */ + +#include <linux/bug.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/i2c.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/types.h> +#include <linux/slab.h> +#include <linux/version.h> +#include <media/v4l2-ctrls.h> +#include <media/media-device.h> + +#include "fimc-core.h" +#include "fimc-mdevice.h" +#include "mipi-csis.h" + +static int __fimc_md_set_camclk(struct fimc_md *fmd, + struct fimc_sensor_info *s_info, + bool on); +/** + * fimc_pipeline_prepare - update pipeline information with subdevice pointers + * @fimc: fimc device terminating the pipeline + * + * Caller holds the graph mutex. + */ +void fimc_pipeline_prepare(struct fimc_dev *fimc, struct media_entity *me) +{ + struct media_entity_graph graph; + struct v4l2_subdev *sd; + + media_entity_graph_walk_start(&graph, me); + + while ((me = media_entity_graph_walk_next(&graph))) { + if (media_entity_type(me) != MEDIA_ENT_T_V4L2_SUBDEV) + continue; + sd = media_entity_to_v4l2_subdev(me); + + if (sd->grp_id == SENSOR_GROUP_ID) + fimc->pipeline.sensor = sd; + else if (sd->grp_id == CSIS_GROUP_ID) + fimc->pipeline.csis = sd; + } +} + +/** + * __subdev_set_power - change power state of a single subdev + * @sd: subdevice to change power state for + * @on: 1 to enable power or 0 to disable + * + * Return result of s_power subdev operation or -ENXIO if sd argument + * is NULL. Return 0 if the subdevice does not implement s_power. + */ +static int __subdev_set_power(struct v4l2_subdev *sd, int on) +{ + int *use_count; + int ret; + + if (sd == NULL) + return -ENXIO; + + use_count = &sd->entity.use_count; + if (on && (*use_count)++ > 0) + return 0; + else if (!on && (*use_count == 0 || --(*use_count) > 0)) + return 0; + ret = v4l2_subdev_call(sd, core, s_power, on); + + return ret != -ENOIOCTLCMD ? ret : 0; +} + +/** + * fimc_pipeline_s_power - change power state of all pipeline subdevs + * @fimc: fimc device terminating the pipeline + * @state: 1 to enable power or 0 for power down + * + * Need to be called with the graph mutex held. + */ +int fimc_pipeline_s_power(struct fimc_dev *fimc, int state) +{ + int ret = 0; + + if (fimc->pipeline.sensor == NULL) + return -ENXIO; + + if (state) { + ret = __subdev_set_power(fimc->pipeline.csis, 1); + if (ret && ret != -ENXIO) + return ret; + return __subdev_set_power(fimc->pipeline.sensor, 1); + } + + ret = __subdev_set_power(fimc->pipeline.sensor, 0); + if (ret) + return ret; + ret = __subdev_set_power(fimc->pipeline.csis, 0); + + return ret == -ENXIO ? 0 : ret; +} + +/** + * __fimc_pipeline_initialize - update the pipeline information, enable power + * of all pipeline subdevs and the sensor clock + * @me: media entity to start graph walk with + * @prep: true to acquire sensor (and csis) subdevs + * + * This function must be called with the graph mutex held. + */ +static int __fimc_pipeline_initialize(struct fimc_dev *fimc, + struct media_entity *me, bool prep) +{ + int ret; + + if (prep) + fimc_pipeline_prepare(fimc, me); + if (fimc->pipeline.sensor == NULL) + return -EINVAL; + ret = fimc_md_set_camclk(fimc->pipeline.sensor, true); + if (ret) + return ret; + return fimc_pipeline_s_power(fimc, 1); +} + +int fimc_pipeline_initialize(struct fimc_dev *fimc, struct media_entity *me, + bool prep) +{ + int ret; + + mutex_lock(&me->parent->graph_mutex); + ret = __fimc_pipeline_initialize(fimc, me, prep); + mutex_unlock(&me->parent->graph_mutex); + + return ret; +} + +/** + * __fimc_pipeline_shutdown - disable the sensor clock and pipeline power + * @fimc: fimc device terminating the pipeline + * + * Disable power of all subdevs in the pipeline and turn off the external + * sensor clock. + * Called with the graph mutex held. + */ +int __fimc_pipeline_shutdown(struct fimc_dev *fimc) +{ + int ret = 0; + + if (fimc->pipeline.sensor) { + ret = fimc_pipeline_s_power(fimc, 0); + fimc_md_set_camclk(fimc->pipeline.sensor, false); + } + return ret == -ENXIO ? 0 : ret; +} + +int fimc_pipeline_shutdown(struct fimc_dev *fimc) +{ + struct media_entity *me = &fimc->vid_cap.vfd->entity; + int ret; + + mutex_lock(&me->parent->graph_mutex); + ret = __fimc_pipeline_shutdown(fimc); + mutex_unlock(&me->parent->graph_mutex); + + return ret; +} + +/** + * fimc_pipeline_s_stream - invoke s_stream on pipeline subdevs + * @fimc: fimc device terminating the pipeline + * @on: passed as the s_stream call argument + */ +int fimc_pipeline_s_stream(struct fimc_dev *fimc, int on) +{ + struct fimc_pipeline *p = &fimc->pipeline; + int ret = 0; + + if (p->sensor == NULL) + return -ENODEV; + + if ((on && p->csis) || !on) + ret = v4l2_subdev_call(on ? p->csis : p->sensor, + video, s_stream, on); + if (ret < 0 && ret != -ENOIOCTLCMD) + return ret; + if ((!on && p->csis) || on) + ret = v4l2_subdev_call(on ? p->sensor : p->csis, + video, s_stream, on); + return ret == -ENOIOCTLCMD ? 0 : ret; +} + +/* + * Sensor subdevice helper functions + */ +static struct v4l2_subdev *fimc_md_register_sensor(struct fimc_md *fmd, + struct fimc_sensor_info *s_info) +{ + struct i2c_adapter *adapter; + struct v4l2_subdev *sd = NULL; + + if (!s_info || !fmd) + return NULL; + + adapter = i2c_get_adapter(s_info->pdata->i2c_bus_num); + if (!adapter) + return NULL; + sd = v4l2_i2c_new_subdev_board(&fmd->v4l2_dev, adapter, + s_info->pdata->board_info, NULL); + if (IS_ERR_OR_NULL(sd)) { + v4l2_err(&fmd->v4l2_dev, "Failed to acquire subdev\n"); + return NULL; + } + v4l2_set_subdev_hostdata(sd, s_info); + sd->grp_id = SENSOR_GROUP_ID; + + v4l2_info(&fmd->v4l2_dev, "Registered sensor subdevice %s\n", + s_info->pdata->board_info->type); + return sd; +} + +static void fimc_md_unregister_sensor(struct v4l2_subdev *sd) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!client) + return; + v4l2_device_unregister_subdev(sd); + i2c_unregister_device(client); + i2c_put_adapter(client->adapter); +} + +static int fimc_md_register_sensor_entities(struct fimc_md *fmd) +{ + struct s5p_platform_fimc *pdata = fmd->pdev->dev.platform_data; + struct fimc_dev *fd = NULL; + int num_clients, ret, i; + + /* + * Runtime resume one of the FIMC entities to make sure + * the sclk_cam clocks are not globally disabled. + */ + for (i = 0; !fd && i < ARRAY_SIZE(fmd->fimc); i++) + if (fmd->fimc[i]) + fd = fmd->fimc[i]; + if (!fd) + return -ENXIO; + ret = pm_runtime_get_sync(&fd->pdev->dev); + if (ret < 0) + return ret; + + WARN_ON(pdata->num_clients > ARRAY_SIZE(fmd->sensor)); + num_clients = min_t(u32, pdata->num_clients, ARRAY_SIZE(fmd->sensor)); + + fmd->num_sensors = num_clients; + for (i = 0; i < num_clients; i++) { + fmd->sensor[i].pdata = &pdata->isp_info[i]; + ret = __fimc_md_set_camclk(fmd, &fmd->sensor[i], true); + if (ret) + break; + fmd->sensor[i].subdev = + fimc_md_register_sensor(fmd, &fmd->sensor[i]); + ret = __fimc_md_set_camclk(fmd, &fmd->sensor[i], false); + if (ret) + break; + } + pm_runtime_put(&fd->pdev->dev); + return ret; +} + +/* + * MIPI CSIS and FIMC platform devices registration. + */ +static int fimc_register_callback(struct device *dev, void *p) +{ + struct fimc_dev *fimc = dev_get_drvdata(dev); + struct fimc_md *fmd = p; + int ret; + + if (!fimc || !fimc->pdev) + return 0; + if (fimc->pdev->id < 0 || fimc->pdev->id >= FIMC_MAX_DEVS) + return 0; + + fmd->fimc[fimc->pdev->id] = fimc; + ret = fimc_register_m2m_device(fimc, &fmd->v4l2_dev); + if (ret) + return ret; + ret = fimc_register_capture_device(fimc, &fmd->v4l2_dev); + if (!ret) + fimc->vid_cap.user_subdev_api = fmd->user_subdev_api; + return ret; +} + +static int csis_register_callback(struct device *dev, void *p) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct platform_device *pdev; + struct fimc_md *fmd = p; + int id, ret; + + if (!sd) + return 0; + pdev = v4l2_get_subdevdata(sd); + if (!pdev || pdev->id < 0 || pdev->id >= CSIS_MAX_ENTITIES) + return 0; + v4l2_info(sd, "csis%d sd: %s\n", pdev->id, sd->name); + + id = pdev->id < 0 ? 0 : pdev->id; + fmd->csis[id].sd = sd; + sd->grp_id = CSIS_GROUP_ID; + ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd); + if (ret) + v4l2_err(&fmd->v4l2_dev, + "Failed to register CSIS subdevice: %d\n", ret); + return ret; +} + +/** + * fimc_md_register_platform_entities - register FIMC and CSIS media entities + */ +static int fimc_md_register_platform_entities(struct fimc_md *fmd) +{ + struct device_driver *driver; + int ret; + + driver = driver_find(FIMC_MODULE_NAME, &platform_bus_type); + if (!driver) + return -ENODEV; + ret = driver_for_each_device(driver, NULL, fmd, + fimc_register_callback); + put_driver(driver); + if (ret) + return ret; + + driver = driver_find(CSIS_DRIVER_NAME, &platform_bus_type); + if (driver) { + ret = driver_for_each_device(driver, NULL, fmd, + csis_register_callback); + put_driver(driver); + } + return ret; +} + +static void fimc_md_unregister_entities(struct fimc_md *fmd) +{ + int i; + + for (i = 0; i < FIMC_MAX_DEVS; i++) { + if (fmd->fimc[i] == NULL) + continue; + fimc_unregister_m2m_device(fmd->fimc[i]); + fimc_unregister_capture_device(fmd->fimc[i]); + fmd->fimc[i] = NULL; + } + for (i = 0; i < CSIS_MAX_ENTITIES; i++) { + if (fmd->csis[i].sd == NULL) + continue; + v4l2_device_unregister_subdev(fmd->csis[i].sd); + fmd->csis[i].sd = NULL; + } + for (i = 0; i < fmd->num_sensors; i++) { + if (fmd->sensor[i].subdev == NULL) + continue; + fimc_md_unregister_sensor(fmd->sensor[i].subdev); + fmd->sensor[i].subdev = NULL; + } +} + +static int fimc_md_register_video_nodes(struct fimc_md *fmd) +{ + int i, ret = 0; + + for (i = 0; i < FIMC_MAX_DEVS && !ret; i++) { + if (!fmd->fimc[i]) + continue; + + if (fmd->fimc[i]->m2m.vfd) + ret = video_register_device(fmd->fimc[i]->m2m.vfd, + VFL_TYPE_GRABBER, -1); + if (ret) + break; + if (fmd->fimc[i]->vid_cap.vfd) + ret = video_register_device(fmd->fimc[i]->vid_cap.vfd, + VFL_TYPE_GRABBER, -1); + } + + return ret; +} + +/** + * __fimc_md_create_fimc_links - create links to all FIMC entities + * @fmd: fimc media device + * @source: the source entity to create links to all fimc entities from + * @sensor: sensor subdev linked to FIMC[fimc_id] entity, may be null + * @pad: the source entity pad index + * @fimc_id: index of the fimc device for which link should be enabled + */ +static int __fimc_md_create_fimc_links(struct fimc_md *fmd, + struct media_entity *source, + struct v4l2_subdev *sensor, + int pad, int fimc_id) +{ + struct fimc_sensor_info *s_info; + struct media_entity *sink; + unsigned int flags; + int ret, i; + + for (i = 0; i < FIMC_MAX_DEVS; i++) { + if (!fmd->fimc[i]) + break; + /* + * Some FIMC variants are not fitted with camera capture + * interface. Skip creating a link from sensor for those. + */ + if (sensor->grp_id == SENSOR_GROUP_ID && + !fmd->fimc[i]->variant->has_cam_if) + continue; + + flags = (i == fimc_id) ? MEDIA_LNK_FL_ENABLED : 0; + sink = &fmd->fimc[i]->vid_cap.subdev->entity; + ret = media_entity_create_link(source, pad, sink, + FIMC_SD_PAD_SINK, flags); + if (ret) + return ret; + + /* Notify FIMC capture subdev entity */ + ret = media_entity_call(sink, link_setup, &sink->pads[0], + &source->pads[pad], flags); + if (ret) + break; + + v4l2_info(&fmd->v4l2_dev, "created link [%s] %c> [%s]", + source->name, flags ? '=' : '-', sink->name); + + if (flags == 0) + continue; + s_info = v4l2_get_subdev_hostdata(sensor); + if (!WARN_ON(s_info == NULL)) { + unsigned long irq_flags; + spin_lock_irqsave(&fmd->slock, irq_flags); + s_info->host = fmd->fimc[i]; + spin_unlock_irqrestore(&fmd->slock, irq_flags); + } + } + return 0; +} + +/** + * fimc_md_create_links - create default links between registered entities + * + * Parallel interface sensor entities are connected directly to FIMC capture + * entities. The sensors using MIPI CSIS bus are connected through immutable + * link with CSI receiver entity specified by mux_id. Any registered CSIS + * entity has a link to each registered FIMC capture entity. Enabled links + * are created by default between each subsequent registered sensor and + * subsequent FIMC capture entity. The number of default active links is + * determined by the number of available sensors or FIMC entities, + * whichever is less. + */ +static int fimc_md_create_links(struct fimc_md *fmd) +{ + struct v4l2_subdev *sensor, *csis; + struct s5p_fimc_isp_info *pdata; + struct fimc_sensor_info *s_info; + struct media_entity *source, *sink; + int i, pad, fimc_id = 0; + int ret = 0; + u32 flags; + + for (i = 0; i < fmd->num_sensors; i++) { + if (fmd->sensor[i].subdev == NULL) + continue; + + sensor = fmd->sensor[i].subdev; + s_info = v4l2_get_subdev_hostdata(sensor); + if (!s_info || !s_info->pdata) + continue; + + source = NULL; + pdata = s_info->pdata; + + switch (pdata->bus_type) { + case FIMC_MIPI_CSI2: + if (WARN(pdata->mux_id >= CSIS_MAX_ENTITIES, + "Wrong CSI channel id: %d\n", pdata->mux_id)) + return -EINVAL; + + csis = fmd->csis[pdata->mux_id].sd; + if (WARN(csis == NULL, + "MIPI-CSI interface specified " + "but s5p-csis module is not loaded!\n")) + continue; + + ret = media_entity_create_link(&sensor->entity, 0, + &csis->entity, CSIS_PAD_SINK, + MEDIA_LNK_FL_IMMUTABLE | + MEDIA_LNK_FL_ENABLED); + if (ret) + return ret; + + v4l2_info(&fmd->v4l2_dev, "created link [%s] => [%s]", + sensor->entity.name, csis->entity.name); + + source = &csis->entity; + pad = CSIS_PAD_SOURCE; + break; + + case FIMC_ITU_601...FIMC_ITU_656: + source = &sensor->entity; + pad = 0; + break; + + default: + v4l2_err(&fmd->v4l2_dev, "Wrong bus_type: %x\n", + pdata->bus_type); + return -EINVAL; + } + if (source == NULL) + continue; + + ret = __fimc_md_create_fimc_links(fmd, source, sensor, pad, + fimc_id++); + } + /* Create immutable links between each FIMC's subdev and video node */ + flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED; + for (i = 0; i < FIMC_MAX_DEVS; i++) { + if (!fmd->fimc[i]) + continue; + source = &fmd->fimc[i]->vid_cap.subdev->entity; + sink = &fmd->fimc[i]->vid_cap.vfd->entity; + ret = media_entity_create_link(source, FIMC_SD_PAD_SOURCE, + sink, 0, flags); + if (ret) + break; + } + + return ret; +} + +/* + * The peripheral sensor clock management. + */ +static int fimc_md_get_clocks(struct fimc_md *fmd) +{ + char clk_name[32]; + struct clk *clock; + int i; + + for (i = 0; i < FIMC_MAX_CAMCLKS; i++) { + snprintf(clk_name, sizeof(clk_name), "sclk_cam%u", i); + clock = clk_get(NULL, clk_name); + if (IS_ERR_OR_NULL(clock)) { + v4l2_err(&fmd->v4l2_dev, "Failed to get clock: %s", + clk_name); + return -ENXIO; + } + fmd->camclk[i].clock = clock; + } + return 0; +} + +static void fimc_md_put_clocks(struct fimc_md *fmd) +{ + int i = FIMC_MAX_CAMCLKS; + + while (--i >= 0) { + if (IS_ERR_OR_NULL(fmd->camclk[i].clock)) + continue; + clk_put(fmd->camclk[i].clock); + fmd->camclk[i].clock = NULL; + } +} + +static int __fimc_md_set_camclk(struct fimc_md *fmd, + struct fimc_sensor_info *s_info, + bool on) +{ + struct s5p_fimc_isp_info *pdata = s_info->pdata; + struct fimc_camclk_info *camclk; + int ret = 0; + + if (WARN_ON(pdata->clk_id >= FIMC_MAX_CAMCLKS) || fmd == NULL) + return -EINVAL; + + if (s_info->clk_on == on) + return 0; + camclk = &fmd->camclk[pdata->clk_id]; + + dbg("camclk %d, f: %lu, clk: %p, on: %d", + pdata->clk_id, pdata->clk_frequency, camclk, on); + + if (on) { + if (camclk->use_count > 0 && + camclk->frequency != pdata->clk_frequency) + return -EINVAL; + + if (camclk->use_count++ == 0) { + clk_set_rate(camclk->clock, pdata->clk_frequency); + camclk->frequency = pdata->clk_frequency; + ret = clk_enable(camclk->clock); + } + s_info->clk_on = 1; + dbg("Enabled camclk %d: f: %lu", pdata->clk_id, + clk_get_rate(camclk->clock)); + + return ret; + } + + if (WARN_ON(camclk->use_count == 0)) + return 0; + + if (--camclk->use_count == 0) { + clk_disable(camclk->clock); + s_info->clk_on = 0; + dbg("Disabled camclk %d", pdata->clk_id); + } + return ret; +} + +/** + * fimc_md_set_camclk - peripheral sensor clock setup + * @sd: sensor subdev to configure sclk_cam clock for + * @on: 1 to enable or 0 to disable the clock + * + * There are 2 separate clock outputs available in the SoC for external + * image processors. These clocks are shared between all registered FIMC + * devices to which sensors can be attached, either directly or through + * the MIPI CSI receiver. The clock is allowed here to be used by + * multiple sensors concurrently if they use same frequency. + * The per sensor subdev clk_on attribute helps to synchronize accesses + * to the sclk_cam clocks from the video and media device nodes. + * This function should only be called when the graph mutex is held. + */ +int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on) +{ + struct fimc_sensor_info *s_info = v4l2_get_subdev_hostdata(sd); + struct fimc_md *fmd = entity_to_fimc_mdev(&sd->entity); + + return __fimc_md_set_camclk(fmd, s_info, on); +} + +static int fimc_md_link_notify(struct media_pad *source, + struct media_pad *sink, u32 flags) +{ + struct v4l2_subdev *sd; + struct fimc_dev *fimc; + int ret = 0; + + if (media_entity_type(sink->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + return 0; + + sd = media_entity_to_v4l2_subdev(sink->entity); + fimc = v4l2_get_subdevdata(sd); + + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + ret = __fimc_pipeline_shutdown(fimc); + fimc->pipeline.sensor = NULL; + fimc->pipeline.csis = NULL; + + mutex_lock(&fimc->lock); + fimc_ctrls_delete(fimc->vid_cap.ctx); + mutex_unlock(&fimc->lock); + return ret; + } + /* + * Link activation. Enable power of pipeline elements only if the + * pipeline is already in use, i.e. its video node is opened. + * Recreate the controls destroyed during the link deactivation. + */ + mutex_lock(&fimc->lock); + if (fimc->vid_cap.refcnt > 0) { + ret = __fimc_pipeline_initialize(fimc, source->entity, true); + if (!ret) + ret = fimc_capture_ctrls_create(fimc); + } + mutex_unlock(&fimc->lock); + + return ret ? -EPIPE : ret; +} + +static ssize_t fimc_md_sysfs_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct fimc_md *fmd = platform_get_drvdata(pdev); + + if (fmd->user_subdev_api) + return strlcpy(buf, "Sub-device API (sub-dev)\n", PAGE_SIZE); + + return strlcpy(buf, "V4L2 video node only API (vid-dev)\n", PAGE_SIZE); +} + +static ssize_t fimc_md_sysfs_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct fimc_md *fmd = platform_get_drvdata(pdev); + bool subdev_api; + int i; + + if (!strcmp(buf, "vid-dev\n")) + subdev_api = false; + else if (!strcmp(buf, "sub-dev\n")) + subdev_api = true; + else + return count; + + fmd->user_subdev_api = subdev_api; + for (i = 0; i < FIMC_MAX_DEVS; i++) + if (fmd->fimc[i]) + fmd->fimc[i]->vid_cap.user_subdev_api = subdev_api; + return count; +} +/* + * This device attribute is to select video pipeline configuration method. + * There are following valid values: + * vid-dev - for V4L2 video node API only, subdevice will be configured + * by the host driver. + * sub-dev - for media controller API, subdevs must be configured in user + * space before starting streaming. + */ +static DEVICE_ATTR(subdev_conf_mode, S_IWUSR | S_IRUGO, + fimc_md_sysfs_show, fimc_md_sysfs_store); + +static int __devinit fimc_md_probe(struct platform_device *pdev) +{ + struct v4l2_device *v4l2_dev; + struct fimc_md *fmd; + int ret; + + if (WARN(!pdev->dev.platform_data, "Platform data not specified!\n")) + return -EINVAL; + + fmd = kzalloc(sizeof(struct fimc_md), GFP_KERNEL); + if (!fmd) + return -ENOMEM; + + spin_lock_init(&fmd->slock); + fmd->pdev = pdev; + + strlcpy(fmd->media_dev.model, "SAMSUNG S5P FIMC", + sizeof(fmd->media_dev.model)); + fmd->media_dev.link_notify = fimc_md_link_notify; + fmd->media_dev.dev = &pdev->dev; + + v4l2_dev = &fmd->v4l2_dev; + v4l2_dev->mdev = &fmd->media_dev; + v4l2_dev->notify = fimc_sensor_notify; + snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "%s", + dev_name(&pdev->dev)); + + ret = v4l2_device_register(&pdev->dev, &fmd->v4l2_dev); + if (ret < 0) { + v4l2_err(v4l2_dev, "Failed to register v4l2_device: %d\n", ret); + goto err1; + } + ret = media_device_register(&fmd->media_dev); + if (ret < 0) { + v4l2_err(v4l2_dev, "Failed to register media device: %d\n", ret); + goto err2; + } + ret = fimc_md_get_clocks(fmd); + if (ret) + goto err3; + + fmd->user_subdev_api = false; + ret = fimc_md_register_platform_entities(fmd); + if (ret) + goto err3; + + ret = fimc_md_register_sensor_entities(fmd); + if (ret) + goto err3; + ret = fimc_md_create_links(fmd); + if (ret) + goto err3; + ret = v4l2_device_register_subdev_nodes(&fmd->v4l2_dev); + if (ret) + goto err3; + ret = fimc_md_register_video_nodes(fmd); + if (ret) + goto err3; + + ret = device_create_file(&pdev->dev, &dev_attr_subdev_conf_mode); + if (!ret) { + platform_set_drvdata(pdev, fmd); + return 0; + } +err3: + media_device_unregister(&fmd->media_dev); + fimc_md_put_clocks(fmd); + fimc_md_unregister_entities(fmd); +err2: + v4l2_device_unregister(&fmd->v4l2_dev); +err1: + kfree(fmd); + return ret; +} + +static int __devexit fimc_md_remove(struct platform_device *pdev) +{ + struct fimc_md *fmd = platform_get_drvdata(pdev); + + if (!fmd) + return 0; + device_remove_file(&pdev->dev, &dev_attr_subdev_conf_mode); + fimc_md_unregister_entities(fmd); + media_device_unregister(&fmd->media_dev); + fimc_md_put_clocks(fmd); + kfree(fmd); + return 0; +} + +static struct platform_driver fimc_md_driver = { + .probe = fimc_md_probe, + .remove = __devexit_p(fimc_md_remove), + .driver = { + .name = "s5p-fimc-md", + .owner = THIS_MODULE, + } +}; + +int __init fimc_md_init(void) +{ + int ret; + request_module("s5p-csis"); + ret = fimc_register_driver(); + if (ret) + return ret; + return platform_driver_register(&fimc_md_driver); +} +void __exit fimc_md_exit(void) +{ + platform_driver_unregister(&fimc_md_driver); + fimc_unregister_driver(); +} + +module_init(fimc_md_init); +module_exit(fimc_md_exit); + +MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>"); +MODULE_DESCRIPTION("S5P FIMC camera host interface/video postprocessor driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("2.0.1"); diff --git a/drivers/media/video/s5p-fimc/fimc-mdevice.h b/drivers/media/video/s5p-fimc/fimc-mdevice.h new file mode 100644 index 00000000000..da3780823e7 --- /dev/null +++ b/drivers/media/video/s5p-fimc/fimc-mdevice.h @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2011 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef FIMC_MDEVICE_H_ +#define FIMC_MDEVICE_H_ + +#include <linux/clk.h> +#include <linux/platform_device.h> +#include <linux/mutex.h> +#include <media/media-device.h> +#include <media/media-entity.h> +#include <media/v4l2-device.h> +#include <media/v4l2-subdev.h> + +#include "fimc-core.h" +#include "mipi-csis.h" + +/* Group IDs of sensor, MIPI CSIS and the writeback subdevs. */ +#define SENSOR_GROUP_ID (1 << 8) +#define CSIS_GROUP_ID (1 << 9) +#define WRITEBACK_GROUP_ID (1 << 10) + +#define FIMC_MAX_SENSORS 8 +#define FIMC_MAX_CAMCLKS 2 + +struct fimc_csis_info { + struct v4l2_subdev *sd; + int id; +}; + +struct fimc_camclk_info { + struct clk *clock; + int use_count; + unsigned long frequency; +}; + +/** + * struct fimc_sensor_info - image data source subdev information + * @pdata: sensor's atrributes passed as media device's platform data + * @subdev: image sensor v4l2 subdev + * @host: fimc device the sensor is currently linked to + * @clk_on: sclk_cam clock's state associated with this subdev + * + * This data structure applies to image sensor and the writeback subdevs. + */ +struct fimc_sensor_info { + struct s5p_fimc_isp_info *pdata; + struct v4l2_subdev *subdev; + struct fimc_dev *host; + bool clk_on; +}; + +/** + * struct fimc_md - fimc media device information + * @csis: MIPI CSIS subdevs data + * @sensor: array of registered sensor subdevs + * @num_sensors: actual number of registered sensors + * @camclk: external sensor clock information + * @fimc: array of registered fimc devices + * @media_dev: top level media device + * @v4l2_dev: top level v4l2_device holding up the subdevs + * @pdev: platform device this media device is hooked up into + * @user_subdev_api: true if subdevs are not configured by the host driver + * @slock: spinlock protecting @sensor array + */ +struct fimc_md { + struct fimc_csis_info csis[CSIS_MAX_ENTITIES]; + struct fimc_sensor_info sensor[FIMC_MAX_SENSORS]; + int num_sensors; + struct fimc_camclk_info camclk[FIMC_MAX_CAMCLKS]; + struct fimc_dev *fimc[FIMC_MAX_DEVS]; + struct media_device media_dev; + struct v4l2_device v4l2_dev; + struct platform_device *pdev; + bool user_subdev_api; + spinlock_t slock; +}; + +#define is_subdev_pad(pad) (pad == NULL || \ + media_entity_type(pad->entity) == MEDIA_ENT_T_V4L2_SUBDEV) + +#define me_subtype(me) \ + ((me->type) & (MEDIA_ENT_TYPE_MASK | MEDIA_ENT_SUBTYPE_MASK)) + +#define subdev_has_devnode(__sd) (__sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE) + +static inline struct fimc_md *entity_to_fimc_mdev(struct media_entity *me) +{ + return me->parent == NULL ? NULL : + container_of(me->parent, struct fimc_md, media_dev); +} + +static inline void fimc_md_graph_lock(struct fimc_dev *fimc) +{ + BUG_ON(fimc->vid_cap.vfd == NULL); + mutex_lock(&fimc->vid_cap.vfd->entity.parent->graph_mutex); +} + +static inline void fimc_md_graph_unlock(struct fimc_dev *fimc) +{ + BUG_ON(fimc->vid_cap.vfd == NULL); + mutex_unlock(&fimc->vid_cap.vfd->entity.parent->graph_mutex); +} + +int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on); +void fimc_pipeline_prepare(struct fimc_dev *fimc, struct media_entity *me); +int fimc_pipeline_initialize(struct fimc_dev *fimc, struct media_entity *me, + bool resume); +int fimc_pipeline_shutdown(struct fimc_dev *fimc); +int fimc_pipeline_s_power(struct fimc_dev *fimc, int state); +int fimc_pipeline_s_stream(struct fimc_dev *fimc, int state); + +#endif diff --git a/drivers/media/video/s5p-fimc/fimc-reg.c b/drivers/media/video/s5p-fimc/fimc-reg.c index 4893b2d91d8..20e664e3416 100644 --- a/drivers/media/video/s5p-fimc/fimc-reg.c +++ b/drivers/media/video/s5p-fimc/fimc-reg.c @@ -30,7 +30,7 @@ void fimc_hw_reset(struct fimc_dev *dev) cfg = readl(dev->regs + S5P_CIGCTRL); cfg |= (S5P_CIGCTRL_SWRST | S5P_CIGCTRL_IRQ_LEVEL); writel(cfg, dev->regs + S5P_CIGCTRL); - udelay(1000); + udelay(10); cfg = readl(dev->regs + S5P_CIGCTRL); cfg &= ~S5P_CIGCTRL_SWRST; @@ -41,19 +41,11 @@ static u32 fimc_hw_get_in_flip(struct fimc_ctx *ctx) { u32 flip = S5P_MSCTRL_FLIP_NORMAL; - switch (ctx->flip) { - case FLIP_X_AXIS: + if (ctx->hflip) flip = S5P_MSCTRL_FLIP_X_MIRROR; - break; - case FLIP_Y_AXIS: + if (ctx->vflip) flip = S5P_MSCTRL_FLIP_Y_MIRROR; - break; - case FLIP_XY_AXIS: - flip = S5P_MSCTRL_FLIP_180; - break; - default: - break; - } + if (ctx->rotation <= 90) return flip; @@ -64,19 +56,11 @@ static u32 fimc_hw_get_target_flip(struct fimc_ctx *ctx) { u32 flip = S5P_CITRGFMT_FLIP_NORMAL; - switch (ctx->flip) { - case FLIP_X_AXIS: - flip = S5P_CITRGFMT_FLIP_X_MIRROR; - break; - case FLIP_Y_AXIS: - flip = S5P_CITRGFMT_FLIP_Y_MIRROR; - break; - case FLIP_XY_AXIS: - flip = S5P_CITRGFMT_FLIP_180; - break; - default: - break; - } + if (ctx->hflip) + flip |= S5P_CITRGFMT_FLIP_X_MIRROR; + if (ctx->vflip) + flip |= S5P_CITRGFMT_FLIP_Y_MIRROR; + if (ctx->rotation <= 90) return flip; @@ -368,17 +352,19 @@ void fimc_hw_en_capture(struct fimc_ctx *ctx) writel(cfg | S5P_CIIMGCPT_IMGCPTEN, dev->regs + S5P_CIIMGCPT); } -void fimc_hw_set_effect(struct fimc_ctx *ctx) +void fimc_hw_set_effect(struct fimc_ctx *ctx, bool active) { struct fimc_dev *dev = ctx->fimc_dev; struct fimc_effect *effect = &ctx->effect; - u32 cfg = (S5P_CIIMGEFF_IE_ENABLE | S5P_CIIMGEFF_IE_SC_AFTER); - - cfg |= effect->type; + u32 cfg = 0; - if (effect->type == S5P_FIMC_EFFECT_ARBITRARY) { - cfg |= S5P_CIIMGEFF_PAT_CB(effect->pat_cb); - cfg |= S5P_CIIMGEFF_PAT_CR(effect->pat_cr); + if (active) { + cfg |= S5P_CIIMGEFF_IE_SC_AFTER | S5P_CIIMGEFF_IE_ENABLE; + cfg |= effect->type; + if (effect->type == S5P_FIMC_EFFECT_ARBITRARY) { + cfg |= S5P_CIIMGEFF_PAT_CB(effect->pat_cb); + cfg |= S5P_CIIMGEFF_PAT_CR(effect->pat_cr); + } } writel(cfg, dev->regs + S5P_CIIMGEFF); @@ -547,20 +533,24 @@ int fimc_hw_set_camera_polarity(struct fimc_dev *fimc, u32 cfg = readl(fimc->regs + S5P_CIGCTRL); cfg &= ~(S5P_CIGCTRL_INVPOLPCLK | S5P_CIGCTRL_INVPOLVSYNC | - S5P_CIGCTRL_INVPOLHREF | S5P_CIGCTRL_INVPOLHSYNC); + S5P_CIGCTRL_INVPOLHREF | S5P_CIGCTRL_INVPOLHSYNC | + S5P_CIGCTRL_INVPOLFIELD); - if (cam->flags & FIMC_CLK_INV_PCLK) + if (cam->flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) cfg |= S5P_CIGCTRL_INVPOLPCLK; - if (cam->flags & FIMC_CLK_INV_VSYNC) + if (cam->flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) cfg |= S5P_CIGCTRL_INVPOLVSYNC; - if (cam->flags & FIMC_CLK_INV_HREF) + if (cam->flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) cfg |= S5P_CIGCTRL_INVPOLHREF; - if (cam->flags & FIMC_CLK_INV_HSYNC) + if (cam->flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) cfg |= S5P_CIGCTRL_INVPOLHSYNC; + if (cam->flags & V4L2_MBUS_FIELD_EVEN_LOW) + cfg |= S5P_CIGCTRL_INVPOLFIELD; + writel(cfg, fimc->regs + S5P_CIGCTRL); return 0; @@ -588,7 +578,7 @@ int fimc_hw_set_camera_source(struct fimc_dev *fimc, if (cam->bus_type == FIMC_ITU_601 || cam->bus_type == FIMC_ITU_656) { for (i = 0; i < ARRAY_SIZE(pix_desc); i++) { - if (fimc->vid_cap.fmt.code == pix_desc[i].pixelcode) { + if (fimc->vid_cap.mf.code == pix_desc[i].pixelcode) { cfg = pix_desc[i].cisrcfmt; bus_width = pix_desc[i].bus_width; break; @@ -596,9 +586,9 @@ int fimc_hw_set_camera_source(struct fimc_dev *fimc, } if (i == ARRAY_SIZE(pix_desc)) { - v4l2_err(&fimc->vid_cap.v4l2_dev, + v4l2_err(fimc->vid_cap.vfd, "Camera color format not supported: %d\n", - fimc->vid_cap.fmt.code); + fimc->vid_cap.mf.code); return -EINVAL; } @@ -608,6 +598,9 @@ int fimc_hw_set_camera_source(struct fimc_dev *fimc, else if (bus_width == 16) cfg |= S5P_CISRCFMT_ITU601_16BIT; } /* else defaults to ITU-R BT.656 8-bit */ + } else if (cam->bus_type == FIMC_MIPI_CSI2) { + if (fimc_fmt_is_jpeg(f->fmt->color)) + cfg |= S5P_CISRCFMT_ITU601_8BIT; } cfg |= S5P_CISRCFMT_HSIZE(f->o_width) | S5P_CISRCFMT_VSIZE(f->o_height); @@ -649,7 +642,7 @@ int fimc_hw_set_camera_type(struct fimc_dev *fimc, /* Select ITU B interface, disable Writeback path and test pattern. */ cfg &= ~(S5P_CIGCTRL_TESTPAT_MASK | S5P_CIGCTRL_SELCAM_ITU_A | S5P_CIGCTRL_SELCAM_MIPI | S5P_CIGCTRL_CAMIF_SELWB | - S5P_CIGCTRL_SELCAM_MIPI_A); + S5P_CIGCTRL_SELCAM_MIPI_A | S5P_CIGCTRL_CAM_JPEG); if (cam->bus_type == FIMC_MIPI_CSI2) { cfg |= S5P_CIGCTRL_SELCAM_MIPI; @@ -658,11 +651,18 @@ int fimc_hw_set_camera_type(struct fimc_dev *fimc, cfg |= S5P_CIGCTRL_SELCAM_MIPI_A; /* TODO: add remaining supported formats. */ - if (vid_cap->fmt.code == V4L2_MBUS_FMT_VYUY8_2X8) { + switch (vid_cap->mf.code) { + case V4L2_MBUS_FMT_VYUY8_2X8: tmp = S5P_CSIIMGFMT_YCBCR422_8BIT; - } else { - err("camera image format not supported: %d", - vid_cap->fmt.code); + break; + case V4L2_MBUS_FMT_JPEG_1X8: + tmp = S5P_CSIIMGFMT_USER(1); + cfg |= S5P_CIGCTRL_CAM_JPEG; + break; + default: + v4l2_err(fimc->vid_cap.vfd, + "Not supported camera pixel format: %d", + vid_cap->mf.code); return -EINVAL; } tmp |= (cam->csi_data_align == 32) << 8; diff --git a/drivers/media/video/s5p-fimc/mipi-csis.c b/drivers/media/video/s5p-fimc/mipi-csis.c index ef056d6605c..59d79bc2f58 100644 --- a/drivers/media/video/s5p-fimc/mipi-csis.c +++ b/drivers/media/video/s5p-fimc/mipi-csis.c @@ -81,6 +81,12 @@ static char *csi_clock_name[] = { }; #define NUM_CSIS_CLOCKS ARRAY_SIZE(csi_clock_name) +static const char * const csis_supply_name[] = { + "vdd11", /* 1.1V or 1.2V (s5pc100) MIPI CSI suppply */ + "vdd18", /* VDD 1.8V and MIPI CSI PLL supply */ +}; +#define CSIS_NUM_SUPPLIES ARRAY_SIZE(csis_supply_name) + enum { ST_POWERED = 1, ST_STREAMING = 2, @@ -109,9 +115,9 @@ struct csis_state { struct platform_device *pdev; struct resource *regs_res; void __iomem *regs; + struct regulator_bulk_data supplies[CSIS_NUM_SUPPLIES]; struct clk *clock[NUM_CSIS_CLOCKS]; int irq; - struct regulator *supply; u32 flags; const struct csis_pix_format *csis_fmt; struct v4l2_mbus_framefmt format; @@ -460,6 +466,7 @@ static int __devinit s5pcsis_probe(struct platform_device *pdev) struct resource *regs_res; struct csis_state *state; int ret = -ENOMEM; + int i; state = kzalloc(sizeof(*state), GFP_KERNEL); if (!state) @@ -519,14 +526,13 @@ static int __devinit s5pcsis_probe(struct platform_device *pdev) goto e_clkput; } - if (!pdata->fixed_phy_vdd) { - state->supply = regulator_get(&pdev->dev, "vdd"); - if (IS_ERR(state->supply)) { - ret = PTR_ERR(state->supply); - state->supply = NULL; - goto e_clkput; - } - } + for (i = 0; i < CSIS_NUM_SUPPLIES; i++) + state->supplies[i].supply = csis_supply_name[i]; + + ret = regulator_bulk_get(&pdev->dev, CSIS_NUM_SUPPLIES, + state->supplies); + if (ret) + goto e_clkput; ret = request_irq(state->irq, s5pcsis_irq_handler, 0, dev_name(&pdev->dev), state); @@ -553,7 +559,6 @@ static int __devinit s5pcsis_probe(struct platform_device *pdev) /* .. and a pointer to the subdev. */ platform_set_drvdata(pdev, &state->sd); - state->flags = ST_SUSPENDED; pm_runtime_enable(&pdev->dev); return 0; @@ -561,8 +566,7 @@ static int __devinit s5pcsis_probe(struct platform_device *pdev) e_irqfree: free_irq(state->irq, state); e_regput: - if (state->supply) - regulator_put(state->supply); + regulator_bulk_free(CSIS_NUM_SUPPLIES, state->supplies); e_clkput: clk_disable(state->clock[CSIS_CLK_MUX]); s5pcsis_clk_put(state); @@ -575,7 +579,7 @@ e_free: return ret; } -static int s5pcsis_suspend(struct device *dev) +static int s5pcsis_pm_suspend(struct device *dev, bool runtime) { struct s5p_platform_mipi_csis *pdata = dev->platform_data; struct platform_device *pdev = to_platform_device(dev); @@ -592,21 +596,21 @@ static int s5pcsis_suspend(struct device *dev) ret = pdata->phy_enable(state->pdev, false); if (ret) goto unlock; - if (state->supply) { - ret = regulator_disable(state->supply); - if (ret) - goto unlock; - } + ret = regulator_bulk_disable(CSIS_NUM_SUPPLIES, + state->supplies); + if (ret) + goto unlock; clk_disable(state->clock[CSIS_CLK_GATE]); state->flags &= ~ST_POWERED; + if (!runtime) + state->flags |= ST_SUSPENDED; } - state->flags |= ST_SUSPENDED; unlock: mutex_unlock(&state->lock); return ret ? -EAGAIN : 0; } -static int s5pcsis_resume(struct device *dev) +static int s5pcsis_pm_resume(struct device *dev, bool runtime) { struct s5p_platform_mipi_csis *pdata = dev->platform_data; struct platform_device *pdev = to_platform_device(dev); @@ -618,20 +622,20 @@ static int s5pcsis_resume(struct device *dev) __func__, state->flags); mutex_lock(&state->lock); - if (!(state->flags & ST_SUSPENDED)) + if (!runtime && !(state->flags & ST_SUSPENDED)) goto unlock; if (!(state->flags & ST_POWERED)) { - if (state->supply) - ret = regulator_enable(state->supply); + ret = regulator_bulk_enable(CSIS_NUM_SUPPLIES, + state->supplies); if (ret) goto unlock; - ret = pdata->phy_enable(state->pdev, true); if (!ret) { state->flags |= ST_POWERED; - } else if (state->supply) { - regulator_disable(state->supply); + } else { + regulator_bulk_disable(CSIS_NUM_SUPPLIES, + state->supplies); goto unlock; } clk_enable(state->clock[CSIS_CLK_GATE]); @@ -646,24 +650,26 @@ static int s5pcsis_resume(struct device *dev) } #ifdef CONFIG_PM_SLEEP -static int s5pcsis_pm_suspend(struct device *dev) +static int s5pcsis_suspend(struct device *dev) { - return s5pcsis_suspend(dev); + return s5pcsis_pm_suspend(dev, false); } -static int s5pcsis_pm_resume(struct device *dev) +static int s5pcsis_resume(struct device *dev) { - int ret; - - ret = s5pcsis_resume(dev); + return s5pcsis_pm_resume(dev, false); +} +#endif - if (!ret) { - pm_runtime_disable(dev); - ret = pm_runtime_set_active(dev); - pm_runtime_enable(dev); - } +#ifdef CONFIG_PM_RUNTIME +static int s5pcsis_runtime_suspend(struct device *dev) +{ + return s5pcsis_pm_suspend(dev, true); +} - return ret; +static int s5pcsis_runtime_resume(struct device *dev) +{ + return s5pcsis_pm_resume(dev, true); } #endif @@ -679,8 +685,7 @@ static int __devexit s5pcsis_remove(struct platform_device *pdev) pm_runtime_set_suspended(&pdev->dev); s5pcsis_clk_put(state); - if (state->supply) - regulator_put(state->supply); + regulator_bulk_free(CSIS_NUM_SUPPLIES, state->supplies); media_entity_cleanup(&state->sd.entity); free_irq(state->irq, state); @@ -692,8 +697,9 @@ static int __devexit s5pcsis_remove(struct platform_device *pdev) } static const struct dev_pm_ops s5pcsis_pm_ops = { - SET_RUNTIME_PM_OPS(s5pcsis_suspend, s5pcsis_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(s5pcsis_pm_suspend, s5pcsis_pm_resume) + SET_RUNTIME_PM_OPS(s5pcsis_runtime_suspend, s5pcsis_runtime_resume, + NULL) + SET_SYSTEM_SLEEP_PM_OPS(s5pcsis_suspend, s5pcsis_resume) }; static struct platform_driver s5pcsis_driver = { diff --git a/drivers/media/video/s5p-fimc/regs-fimc.h b/drivers/media/video/s5p-fimc/regs-fimc.h index 0fea3e635d7..c8e3b94bd91 100644 --- a/drivers/media/video/s5p-fimc/regs-fimc.h +++ b/drivers/media/video/s5p-fimc/regs-fimc.h @@ -54,12 +54,14 @@ #define S5P_CIGCTRL_IRQ_CLR (1 << 19) #define S5P_CIGCTRL_IRQ_ENABLE (1 << 16) #define S5P_CIGCTRL_SHDW_DISABLE (1 << 12) +#define S5P_CIGCTRL_CAM_JPEG (1 << 8) #define S5P_CIGCTRL_SELCAM_MIPI_A (1 << 7) #define S5P_CIGCTRL_CAMIF_SELWB (1 << 6) /* 0 - ITU601; 1 - ITU709 */ #define S5P_CIGCTRL_CSC_ITU601_709 (1 << 5) #define S5P_CIGCTRL_INVPOLHSYNC (1 << 4) #define S5P_CIGCTRL_SELCAM_MIPI (1 << 3) +#define S5P_CIGCTRL_INVPOLFIELD (1 << 1) #define S5P_CIGCTRL_INTERLACE (1 << 0) /* Window offset 2 */ @@ -184,7 +186,6 @@ /* Image effect */ #define S5P_CIIMGEFF 0xd0 -#define S5P_CIIMGEFF_IE_DISABLE (0 << 30) #define S5P_CIIMGEFF_IE_ENABLE (1 << 30) #define S5P_CIIMGEFF_IE_SC_BEFORE (0 << 29) #define S5P_CIIMGEFF_IE_SC_AFTER (1 << 29) @@ -286,10 +287,8 @@ #define S5P_CSIIMGFMT_RAW8 0x2a #define S5P_CSIIMGFMT_RAW10 0x2b #define S5P_CSIIMGFMT_RAW12 0x2c -#define S5P_CSIIMGFMT_USER1 0x30 -#define S5P_CSIIMGFMT_USER2 0x31 -#define S5P_CSIIMGFMT_USER3 0x32 -#define S5P_CSIIMGFMT_USER4 0x33 +/* User defined formats. x = 0...16. */ +#define S5P_CSIIMGFMT_USER(x) (0x30 + x - 1) /* Output frame buffer sequence mask */ #define S5P_CIFCNTSEQ 0x1FC diff --git a/drivers/media/video/s5p-mfc/s5p_mfc.c b/drivers/media/video/s5p-mfc/s5p_mfc.c index 7dc7eab58b3..8be8b54eb74 100644 --- a/drivers/media/video/s5p-mfc/s5p_mfc.c +++ b/drivers/media/video/s5p-mfc/s5p_mfc.c @@ -202,7 +202,7 @@ static void s5p_mfc_handle_frame_copy_time(struct s5p_mfc_ctx *ctx) appropraite flags */ src_buf = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list); list_for_each_entry(dst_buf, &ctx->dst_queue, list) { - if (vb2_dma_contig_plane_paddr(dst_buf->b, 0) == dec_y_addr) { + if (vb2_dma_contig_plane_dma_addr(dst_buf->b, 0) == dec_y_addr) { memcpy(&dst_buf->b->v4l2_buf.timecode, &src_buf->b->v4l2_buf.timecode, sizeof(struct v4l2_timecode)); @@ -248,7 +248,7 @@ static void s5p_mfc_handle_frame_new(struct s5p_mfc_ctx *ctx, unsigned int err) * check which videobuf does it correspond to */ list_for_each_entry(dst_buf, &ctx->dst_queue, list) { /* Check if this is the buffer we're looking for */ - if (vb2_dma_contig_plane_paddr(dst_buf->b, 0) == dspl_y_addr) { + if (vb2_dma_contig_plane_dma_addr(dst_buf->b, 0) == dspl_y_addr) { list_del(&dst_buf->list); ctx->dst_queue_cnt--; dst_buf->b->v4l2_buf.sequence = ctx->sequence; @@ -940,9 +940,8 @@ static int match_child(struct device *dev, void *data) return !strcmp(dev_name(dev), (char *)data); } - /* MFC probe function */ -static int __devinit s5p_mfc_probe(struct platform_device *pdev) +static int s5p_mfc_probe(struct platform_device *pdev) { struct s5p_mfc_dev *dev; struct video_device *vfd; @@ -1236,7 +1235,7 @@ static const struct dev_pm_ops s5p_mfc_pm_ops = { NULL) }; -static struct platform_driver s5p_mfc_pdrv = { +static struct platform_driver s5p_mfc_driver = { .probe = s5p_mfc_probe, .remove = __devexit_p(s5p_mfc_remove), .driver = { @@ -1254,15 +1253,15 @@ static int __init s5p_mfc_init(void) int ret; pr_info("%s", banner); - ret = platform_driver_register(&s5p_mfc_pdrv); + ret = platform_driver_register(&s5p_mfc_driver); if (ret) pr_err("Platform device registration failed.\n"); return ret; } -static void __devexit s5p_mfc_exit(void) +static void __exit s5p_mfc_exit(void) { - platform_driver_unregister(&s5p_mfc_pdrv); + platform_driver_unregister(&s5p_mfc_driver); } module_init(s5p_mfc_init); diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c b/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c index 5f4da80051b..f2481a85e0a 100644 --- a/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c +++ b/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c @@ -38,7 +38,7 @@ int s5p_mfc_alloc_and_load_firmware(struct s5p_mfc_dev *dev) * into kernel. */ mfc_debug_enter(); err = request_firmware((const struct firmware **)&fw_blob, - "s5pc110-mfc.fw", dev->v4l2_dev.dev); + "s5p-mfc.fw", dev->v4l2_dev.dev); if (err != 0) { mfc_err("Firmware is not present in the /lib/firmware directory nor compiled in kernel\n"); return -EINVAL; @@ -116,7 +116,7 @@ int s5p_mfc_reload_firmware(struct s5p_mfc_dev *dev) * into kernel. */ mfc_debug_enter(); err = request_firmware((const struct firmware **)&fw_blob, - "s5pc110-mfc.fw", dev->v4l2_dev.dev); + "s5p-mfc.fw", dev->v4l2_dev.dev); if (err != 0) { mfc_err("Firmware is not present in the /lib/firmware directory nor compiled in kernel\n"); return -EINVAL; diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_dec.c b/drivers/media/video/s5p-mfc/s5p_mfc_dec.c index b2c5052a9c4..844a4d7797b 100644 --- a/drivers/media/video/s5p-mfc/s5p_mfc_dec.c +++ b/drivers/media/video/s5p-mfc/s5p_mfc_dec.c @@ -165,7 +165,7 @@ static struct mfc_control controls[] = { .maximum = 32, .step = 1, .default_value = 1, - .is_volatile = 1, + .flags = V4L2_CTRL_FLAG_VOLATILE, }, }; @@ -220,8 +220,8 @@ static int vidioc_querycap(struct file *file, void *priv, strncpy(cap->card, dev->plat_dev->name, sizeof(cap->card) - 1); cap->bus_info[0] = 0; cap->version = KERNEL_VERSION(1, 0, 0); - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT - | V4L2_CAP_STREAMING; + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE_MPLANE | + V4L2_CAP_VIDEO_OUTPUT_MPLANE | V4L2_CAP_STREAMING; return 0; } @@ -744,9 +744,10 @@ static const struct v4l2_ioctl_ops s5p_mfc_dec_ioctl_ops = { .vidioc_g_crop = vidioc_g_crop, }; -static int s5p_mfc_queue_setup(struct vb2_queue *vq, unsigned int *buf_count, - unsigned int *plane_count, unsigned long psize[], - void *allocators[]) +static int s5p_mfc_queue_setup(struct vb2_queue *vq, + const struct v4l2_format *fmt, unsigned int *buf_count, + unsigned int *plane_count, unsigned int psize[], + void *allocators[]) { struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv); @@ -824,7 +825,7 @@ static int s5p_mfc_buf_init(struct vb2_buffer *vb) return 0; for (i = 0; i <= ctx->src_fmt->num_planes ; i++) { if (IS_ERR_OR_NULL(ERR_PTR( - vb2_dma_contig_plane_paddr(vb, i)))) { + vb2_dma_contig_plane_dma_addr(vb, i)))) { mfc_err("Plane mem not allocated\n"); return -EINVAL; } @@ -837,13 +838,13 @@ static int s5p_mfc_buf_init(struct vb2_buffer *vb) i = vb->v4l2_buf.index; ctx->dst_bufs[i].b = vb; ctx->dst_bufs[i].cookie.raw.luma = - vb2_dma_contig_plane_paddr(vb, 0); + vb2_dma_contig_plane_dma_addr(vb, 0); ctx->dst_bufs[i].cookie.raw.chroma = - vb2_dma_contig_plane_paddr(vb, 1); + vb2_dma_contig_plane_dma_addr(vb, 1); ctx->dst_bufs_cnt++; } else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { if (IS_ERR_OR_NULL(ERR_PTR( - vb2_dma_contig_plane_paddr(vb, 0)))) { + vb2_dma_contig_plane_dma_addr(vb, 0)))) { mfc_err("Plane memory not allocated\n"); return -EINVAL; } @@ -855,7 +856,7 @@ static int s5p_mfc_buf_init(struct vb2_buffer *vb) i = vb->v4l2_buf.index; ctx->src_bufs[i].b = vb; ctx->src_bufs[i].cookie.stream = - vb2_dma_contig_plane_paddr(vb, 0); + vb2_dma_contig_plane_dma_addr(vb, 0); ctx->src_bufs_cnt++; } else { mfc_err("s5p_mfc_buf_init: unknown queue type\n"); @@ -864,7 +865,7 @@ static int s5p_mfc_buf_init(struct vb2_buffer *vb) return 0; } -static int s5p_mfc_start_streaming(struct vb2_queue *q) +static int s5p_mfc_start_streaming(struct vb2_queue *q, unsigned int count) { struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv); struct s5p_mfc_dev *dev = ctx->dev; @@ -1020,7 +1021,7 @@ int s5p_mfc_dec_ctrls_setup(struct s5p_mfc_ctx *ctx) return ctx->ctrl_handler.error; } if (controls[i].is_volatile && ctx->ctrls[i]) - ctx->ctrls[i]->is_volatile = 1; + ctx->ctrls[i]->flags |= V4L2_CTRL_FLAG_VOLATILE; } return 0; } diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_enc.c b/drivers/media/video/s5p-mfc/s5p_mfc_enc.c index fee094a14f4..1e8cdb77d4b 100644 --- a/drivers/media/video/s5p-mfc/s5p_mfc_enc.c +++ b/drivers/media/video/s5p-mfc/s5p_mfc_enc.c @@ -599,8 +599,8 @@ static void cleanup_ref_queue(struct s5p_mfc_ctx *ctx) while (!list_empty(&ctx->ref_queue)) { mb_entry = list_entry((&ctx->ref_queue)->next, struct s5p_mfc_buf, list); - mb_y_addr = vb2_dma_contig_plane_paddr(mb_entry->b, 0); - mb_c_addr = vb2_dma_contig_plane_paddr(mb_entry->b, 1); + mb_y_addr = vb2_dma_contig_plane_dma_addr(mb_entry->b, 0); + mb_c_addr = vb2_dma_contig_plane_dma_addr(mb_entry->b, 1); list_del(&mb_entry->list); ctx->ref_queue_cnt--; list_add_tail(&mb_entry->list, &ctx->src_queue); @@ -622,7 +622,7 @@ static int enc_pre_seq_start(struct s5p_mfc_ctx *ctx) spin_lock_irqsave(&dev->irqlock, flags); dst_mb = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf, list); - dst_addr = vb2_dma_contig_plane_paddr(dst_mb->b, 0); + dst_addr = vb2_dma_contig_plane_dma_addr(dst_mb->b, 0); dst_size = vb2_plane_size(dst_mb->b, 0); s5p_mfc_set_enc_stream_buffer(ctx, dst_addr, dst_size); spin_unlock_irqrestore(&dev->irqlock, flags); @@ -668,14 +668,14 @@ static int enc_pre_frame_start(struct s5p_mfc_ctx *ctx) spin_lock_irqsave(&dev->irqlock, flags); src_mb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list); - src_y_addr = vb2_dma_contig_plane_paddr(src_mb->b, 0); - src_c_addr = vb2_dma_contig_plane_paddr(src_mb->b, 1); + src_y_addr = vb2_dma_contig_plane_dma_addr(src_mb->b, 0); + src_c_addr = vb2_dma_contig_plane_dma_addr(src_mb->b, 1); s5p_mfc_set_enc_frame_buffer(ctx, src_y_addr, src_c_addr); spin_unlock_irqrestore(&dev->irqlock, flags); spin_lock_irqsave(&dev->irqlock, flags); dst_mb = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf, list); - dst_addr = vb2_dma_contig_plane_paddr(dst_mb->b, 0); + dst_addr = vb2_dma_contig_plane_dma_addr(dst_mb->b, 0); dst_size = vb2_plane_size(dst_mb->b, 0); s5p_mfc_set_enc_stream_buffer(ctx, dst_addr, dst_size); spin_unlock_irqrestore(&dev->irqlock, flags); @@ -703,8 +703,8 @@ static int enc_post_frame_start(struct s5p_mfc_ctx *ctx) if (slice_type >= 0) { s5p_mfc_get_enc_frame_buffer(ctx, &enc_y_addr, &enc_c_addr); list_for_each_entry(mb_entry, &ctx->src_queue, list) { - mb_y_addr = vb2_dma_contig_plane_paddr(mb_entry->b, 0); - mb_c_addr = vb2_dma_contig_plane_paddr(mb_entry->b, 1); + mb_y_addr = vb2_dma_contig_plane_dma_addr(mb_entry->b, 0); + mb_c_addr = vb2_dma_contig_plane_dma_addr(mb_entry->b, 1); if ((enc_y_addr == mb_y_addr) && (enc_c_addr == mb_c_addr)) { list_del(&mb_entry->list); @@ -715,8 +715,8 @@ static int enc_post_frame_start(struct s5p_mfc_ctx *ctx) } } list_for_each_entry(mb_entry, &ctx->ref_queue, list) { - mb_y_addr = vb2_dma_contig_plane_paddr(mb_entry->b, 0); - mb_c_addr = vb2_dma_contig_plane_paddr(mb_entry->b, 1); + mb_y_addr = vb2_dma_contig_plane_dma_addr(mb_entry->b, 0); + mb_c_addr = vb2_dma_contig_plane_dma_addr(mb_entry->b, 1); if ((enc_y_addr == mb_y_addr) && (enc_c_addr == mb_c_addr)) { list_del(&mb_entry->list); @@ -785,8 +785,8 @@ static int vidioc_querycap(struct file *file, void *priv, strncpy(cap->card, dev->plat_dev->name, sizeof(cap->card) - 1); cap->bus_info[0] = 0; cap->version = KERNEL_VERSION(1, 0, 0); - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE - | V4L2_CAP_VIDEO_OUTPUT + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE_MPLANE + | V4L2_CAP_VIDEO_OUTPUT_MPLANE | V4L2_CAP_STREAMING; return 0; } @@ -1501,20 +1501,21 @@ static int check_vb_with_fmt(struct s5p_mfc_fmt *fmt, struct vb2_buffer *vb) return -EINVAL; } for (i = 0; i < fmt->num_planes; i++) { - if (!vb2_dma_contig_plane_paddr(vb, i)) { + if (!vb2_dma_contig_plane_dma_addr(vb, i)) { mfc_err("failed to get plane cookie\n"); return -EINVAL; } mfc_debug(2, "index: %d, plane[%d] cookie: 0x%08zx", vb->v4l2_buf.index, i, - vb2_dma_contig_plane_paddr(vb, i)); + vb2_dma_contig_plane_dma_addr(vb, i)); } return 0; } static int s5p_mfc_queue_setup(struct vb2_queue *vq, - unsigned int *buf_count, unsigned int *plane_count, - unsigned long psize[], void *allocators[]) + const struct v4l2_format *fmt, + unsigned int *buf_count, unsigned int *plane_count, + unsigned int psize[], void *allocators[]) { struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv); @@ -1584,7 +1585,7 @@ static int s5p_mfc_buf_init(struct vb2_buffer *vb) i = vb->v4l2_buf.index; ctx->dst_bufs[i].b = vb; ctx->dst_bufs[i].cookie.stream = - vb2_dma_contig_plane_paddr(vb, 0); + vb2_dma_contig_plane_dma_addr(vb, 0); ctx->dst_bufs_cnt++; } else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { ret = check_vb_with_fmt(ctx->src_fmt, vb); @@ -1593,9 +1594,9 @@ static int s5p_mfc_buf_init(struct vb2_buffer *vb) i = vb->v4l2_buf.index; ctx->src_bufs[i].b = vb; ctx->src_bufs[i].cookie.raw.luma = - vb2_dma_contig_plane_paddr(vb, 0); + vb2_dma_contig_plane_dma_addr(vb, 0); ctx->src_bufs[i].cookie.raw.chroma = - vb2_dma_contig_plane_paddr(vb, 1); + vb2_dma_contig_plane_dma_addr(vb, 1); ctx->src_bufs_cnt++; } else { mfc_err("inavlid queue type: %d\n", vq->type); @@ -1640,7 +1641,7 @@ static int s5p_mfc_buf_prepare(struct vb2_buffer *vb) return 0; } -static int s5p_mfc_start_streaming(struct vb2_queue *q) +static int s5p_mfc_start_streaming(struct vb2_queue *q, unsigned int count) { struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv); struct s5p_mfc_dev *dev = ctx->dev; @@ -1814,7 +1815,7 @@ int s5p_mfc_enc_ctrls_setup(struct s5p_mfc_ctx *ctx) return ctx->ctrl_handler.error; } if (controls[i].is_volatile && ctx->ctrls[i]) - ctx->ctrls[i]->is_volatile = 1; + ctx->ctrls[i]->flags |= V4L2_CTRL_FLAG_VOLATILE; } return 0; } diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_opr.c b/drivers/media/video/s5p-mfc/s5p_mfc_opr.c index 7b239168c19..e08b21c50eb 100644 --- a/drivers/media/video/s5p-mfc/s5p_mfc_opr.c +++ b/drivers/media/video/s5p-mfc/s5p_mfc_opr.c @@ -1135,7 +1135,7 @@ static int s5p_mfc_run_dec_frame(struct s5p_mfc_ctx *ctx, int last_frame) temp_vb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list); temp_vb->used = 1; s5p_mfc_set_dec_stream_buffer(ctx, - vb2_dma_contig_plane_paddr(temp_vb->b, 0), ctx->consumed_stream, + vb2_dma_contig_plane_dma_addr(temp_vb->b, 0), ctx->consumed_stream, temp_vb->b->v4l2_planes[0].bytesused); spin_unlock_irqrestore(&dev->irqlock, flags); index = temp_vb->b->v4l2_buf.index; @@ -1172,12 +1172,12 @@ static int s5p_mfc_run_enc_frame(struct s5p_mfc_ctx *ctx) } src_mb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list); src_mb->used = 1; - src_y_addr = vb2_dma_contig_plane_paddr(src_mb->b, 0); - src_c_addr = vb2_dma_contig_plane_paddr(src_mb->b, 1); + src_y_addr = vb2_dma_contig_plane_dma_addr(src_mb->b, 0); + src_c_addr = vb2_dma_contig_plane_dma_addr(src_mb->b, 1); s5p_mfc_set_enc_frame_buffer(ctx, src_y_addr, src_c_addr); dst_mb = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf, list); dst_mb->used = 1; - dst_addr = vb2_dma_contig_plane_paddr(dst_mb->b, 0); + dst_addr = vb2_dma_contig_plane_dma_addr(dst_mb->b, 0); dst_size = vb2_plane_size(dst_mb->b, 0); s5p_mfc_set_enc_stream_buffer(ctx, dst_addr, dst_size); spin_unlock_irqrestore(&dev->irqlock, flags); @@ -1200,7 +1200,7 @@ static void s5p_mfc_run_init_dec(struct s5p_mfc_ctx *ctx) s5p_mfc_set_dec_desc_buffer(ctx); mfc_debug(2, "Header size: %d\n", temp_vb->b->v4l2_planes[0].bytesused); s5p_mfc_set_dec_stream_buffer(ctx, - vb2_dma_contig_plane_paddr(temp_vb->b, 0), + vb2_dma_contig_plane_dma_addr(temp_vb->b, 0), 0, temp_vb->b->v4l2_planes[0].bytesused); spin_unlock_irqrestore(&dev->irqlock, flags); dev->curr_ctx = ctx->num; @@ -1219,7 +1219,7 @@ static void s5p_mfc_run_init_enc(struct s5p_mfc_ctx *ctx) s5p_mfc_set_enc_ref_buffer(ctx); spin_lock_irqsave(&dev->irqlock, flags); dst_mb = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf, list); - dst_addr = vb2_dma_contig_plane_paddr(dst_mb->b, 0); + dst_addr = vb2_dma_contig_plane_dma_addr(dst_mb->b, 0); dst_size = vb2_plane_size(dst_mb->b, 0); s5p_mfc_set_enc_stream_buffer(ctx, dst_addr, dst_size); spin_unlock_irqrestore(&dev->irqlock, flags); @@ -1255,7 +1255,7 @@ static int s5p_mfc_run_init_dec_buffers(struct s5p_mfc_ctx *ctx) temp_vb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list); mfc_debug(2, "Header size: %d\n", temp_vb->b->v4l2_planes[0].bytesused); s5p_mfc_set_dec_stream_buffer(ctx, - vb2_dma_contig_plane_paddr(temp_vb->b, 0), + vb2_dma_contig_plane_dma_addr(temp_vb->b, 0), 0, temp_vb->b->v4l2_planes[0].bytesused); spin_unlock_irqrestore(&dev->irqlock, flags); dev->curr_ctx = ctx->num; diff --git a/drivers/media/video/s5p-tv/Kconfig b/drivers/media/video/s5p-tv/Kconfig index 9c37dee7bc5..f2a09779ec8 100644 --- a/drivers/media/video/s5p-tv/Kconfig +++ b/drivers/media/video/s5p-tv/Kconfig @@ -8,7 +8,7 @@ config VIDEO_SAMSUNG_S5P_TV bool "Samsung TV driver for S5P platform (experimental)" - depends on PLAT_S5P + depends on PLAT_S5P && PM_RUNTIME depends on EXPERIMENTAL default n ---help--- diff --git a/drivers/media/video/s5p-tv/hdmi_drv.c b/drivers/media/video/s5p-tv/hdmi_drv.c index 06d6663f459..0279e6e89fe 100644 --- a/drivers/media/video/s5p-tv/hdmi_drv.c +++ b/drivers/media/video/s5p-tv/hdmi_drv.c @@ -210,20 +210,17 @@ static void hdmi_reg_init(struct hdmi_device *hdev) /* enable HPD interrupts */ hdmi_write_mask(hdev, HDMI_INTC_CON, ~0, HDMI_INTC_EN_GLOBAL | HDMI_INTC_EN_HPD_PLUG | HDMI_INTC_EN_HPD_UNPLUG); - /* choose HDMI mode */ + /* choose DVI mode */ hdmi_write_mask(hdev, HDMI_MODE_SEL, - HDMI_MODE_HDMI_EN, HDMI_MODE_MASK); + HDMI_MODE_DVI_EN, HDMI_MODE_MASK); + hdmi_write_mask(hdev, HDMI_CON_2, ~0, + HDMI_DVI_PERAMBLE_EN | HDMI_DVI_BAND_EN); /* disable bluescreen */ hdmi_write_mask(hdev, HDMI_CON_0, 0, HDMI_BLUE_SCR_EN); /* choose bluescreen (fecal) color */ hdmi_writeb(hdev, HDMI_BLUE_SCREEN_0, 0x12); hdmi_writeb(hdev, HDMI_BLUE_SCREEN_1, 0x34); hdmi_writeb(hdev, HDMI_BLUE_SCREEN_2, 0x56); - /* enable AVI packet every vsync, fixes purple line problem */ - hdmi_writeb(hdev, HDMI_AVI_CON, 0x02); - /* force YUV444, look to CEA-861-D, table 7 for more detail */ - hdmi_writeb(hdev, HDMI_AVI_BYTE(0), 2 << 5); - hdmi_write_mask(hdev, HDMI_CON_1, 2, 3 << 5); } static void hdmi_timing_apply(struct hdmi_device *hdev, @@ -443,6 +440,7 @@ static const struct hdmi_preset_conf hdmi_conf_480p = { .height = 480, .code = V4L2_MBUS_FMT_FIXED, /* means RGB888 */ .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_SRGB, }, }; @@ -475,6 +473,7 @@ static const struct hdmi_preset_conf hdmi_conf_720p60 = { .height = 720, .code = V4L2_MBUS_FMT_FIXED, /* means RGB888 */ .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_SRGB, }, }; @@ -507,6 +506,7 @@ static const struct hdmi_preset_conf hdmi_conf_1080p50 = { .height = 1080, .code = V4L2_MBUS_FMT_FIXED, /* means RGB888 */ .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_SRGB, }, }; @@ -539,6 +539,7 @@ static const struct hdmi_preset_conf hdmi_conf_1080p60 = { .height = 1080, .code = V4L2_MBUS_FMT_FIXED, /* means RGB888 */ .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_SRGB, }, }; diff --git a/drivers/media/video/s5p-tv/mixer.h b/drivers/media/video/s5p-tv/mixer.h index e2242243f63..51ad59b3035 100644 --- a/drivers/media/video/s5p-tv/mixer.h +++ b/drivers/media/video/s5p-tv/mixer.h @@ -111,8 +111,6 @@ struct mxr_buffer { enum mxr_layer_state { /** layers is not shown */ MXR_LAYER_IDLE = 0, - /** state between STREAMON and hardware start */ - MXR_LAYER_STREAMING_START, /** layer is shown */ MXR_LAYER_STREAMING, /** state before STREAMOFF is finished */ diff --git a/drivers/media/video/s5p-tv/mixer_grp_layer.c b/drivers/media/video/s5p-tv/mixer_grp_layer.c index 58f0ba49580..de8270c2b6e 100644 --- a/drivers/media/video/s5p-tv/mixer_grp_layer.c +++ b/drivers/media/video/s5p-tv/mixer_grp_layer.c @@ -86,7 +86,7 @@ static void mxr_graph_buffer_set(struct mxr_layer *layer, dma_addr_t addr = 0; if (buf) - addr = vb2_dma_contig_plane_paddr(&buf->vb, 0); + addr = vb2_dma_contig_plane_dma_addr(&buf->vb, 0); mxr_reg_graph_buffer(layer->mdev, layer->idx, addr); } diff --git a/drivers/media/video/s5p-tv/mixer_reg.c b/drivers/media/video/s5p-tv/mixer_reg.c index 38dac672aa1..4800a3cbb29 100644 --- a/drivers/media/video/s5p-tv/mixer_reg.c +++ b/drivers/media/video/s5p-tv/mixer_reg.c @@ -90,7 +90,7 @@ void mxr_reg_reset(struct mxr_device *mdev) mxr_vsync_set_update(mdev, MXR_DISABLE); /* set output in RGB888 mode */ - mxr_write(mdev, MXR_CFG, MXR_CFG_OUT_YUV444); + mxr_write(mdev, MXR_CFG, MXR_CFG_OUT_RGB888); /* 16 beat burst in DMA */ mxr_write_mask(mdev, MXR_STATUS, MXR_STATUS_16_BURST, @@ -376,6 +376,12 @@ void mxr_reg_set_mbus_fmt(struct mxr_device *mdev, spin_lock_irqsave(&mdev->reg_slock, flags); mxr_vsync_set_update(mdev, MXR_DISABLE); + /* selecting colorspace accepted by output */ + if (fmt->colorspace == V4L2_COLORSPACE_JPEG) + val |= MXR_CFG_OUT_YUV444; + else + val |= MXR_CFG_OUT_RGB888; + /* choosing between interlace and progressive mode */ if (fmt->field == V4L2_FIELD_INTERLACED) val |= MXR_CFG_SCAN_INTERLACE; @@ -394,7 +400,8 @@ void mxr_reg_set_mbus_fmt(struct mxr_device *mdev, else WARN(1, "unrecognized mbus height %u!\n", fmt->height); - mxr_write_mask(mdev, MXR_CFG, val, MXR_CFG_SCAN_MASK); + mxr_write_mask(mdev, MXR_CFG, val, MXR_CFG_SCAN_MASK | + MXR_CFG_OUT_MASK); val = (fmt->field == V4L2_FIELD_INTERLACED) ? ~0 : 0; vp_write_mask(mdev, VP_MODE, val, diff --git a/drivers/media/video/s5p-tv/mixer_video.c b/drivers/media/video/s5p-tv/mixer_video.c index 43ac22f35bc..e16d3a4bc1d 100644 --- a/drivers/media/video/s5p-tv/mixer_video.c +++ b/drivers/media/video/s5p-tv/mixer_video.c @@ -727,8 +727,8 @@ static const struct v4l2_file_operations mxr_fops = { .unlocked_ioctl = video_ioctl2, }; -static int queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, - unsigned int *nplanes, unsigned long sizes[], +static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *pfmt, + unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) { struct mxr_layer *layer = vb2_get_drv_priv(vq); @@ -764,19 +764,10 @@ static void buf_queue(struct vb2_buffer *vb) struct mxr_layer *layer = vb2_get_drv_priv(vb->vb2_queue); struct mxr_device *mdev = layer->mdev; unsigned long flags; - int must_start = 0; spin_lock_irqsave(&layer->enq_slock, flags); - if (layer->state == MXR_LAYER_STREAMING_START) { - layer->state = MXR_LAYER_STREAMING; - must_start = 1; - } list_add_tail(&buffer->list, &layer->enq_list); spin_unlock_irqrestore(&layer->enq_slock, flags); - if (must_start) { - layer->ops.stream_set(layer, MXR_ENABLE); - mxr_streamer_get(mdev); - } mxr_dbg(mdev, "queuing buffer\n"); } @@ -797,13 +788,19 @@ static void wait_unlock(struct vb2_queue *vq) mutex_unlock(&layer->mutex); } -static int start_streaming(struct vb2_queue *vq) +static int start_streaming(struct vb2_queue *vq, unsigned int count) { struct mxr_layer *layer = vb2_get_drv_priv(vq); struct mxr_device *mdev = layer->mdev; unsigned long flags; mxr_dbg(mdev, "%s\n", __func__); + + if (count == 0) { + mxr_dbg(mdev, "no output buffers queued\n"); + return -EINVAL; + } + /* block any changes in output configuration */ mxr_output_get(mdev); @@ -814,9 +811,12 @@ static int start_streaming(struct vb2_queue *vq) layer->ops.format_set(layer); /* enabling layer in hardware */ spin_lock_irqsave(&layer->enq_slock, flags); - layer->state = MXR_LAYER_STREAMING_START; + layer->state = MXR_LAYER_STREAMING; spin_unlock_irqrestore(&layer->enq_slock, flags); + layer->ops.stream_set(layer, MXR_ENABLE); + mxr_streamer_get(mdev); + return 0; } diff --git a/drivers/media/video/s5p-tv/mixer_vp_layer.c b/drivers/media/video/s5p-tv/mixer_vp_layer.c index 6950ed8ac1a..f3bb2e34cb5 100644 --- a/drivers/media/video/s5p-tv/mixer_vp_layer.c +++ b/drivers/media/video/s5p-tv/mixer_vp_layer.c @@ -97,9 +97,9 @@ static void mxr_vp_buffer_set(struct mxr_layer *layer, mxr_reg_vp_buffer(layer->mdev, luma_addr, chroma_addr); return; } - luma_addr[0] = vb2_dma_contig_plane_paddr(&buf->vb, 0); + luma_addr[0] = vb2_dma_contig_plane_dma_addr(&buf->vb, 0); if (layer->fmt->num_subframes == 2) { - chroma_addr[0] = vb2_dma_contig_plane_paddr(&buf->vb, 1); + chroma_addr[0] = vb2_dma_contig_plane_dma_addr(&buf->vb, 1); } else { /* FIXME: mxr_get_plane_size compute integer division, * which is slow and should not be performed in interrupt */ diff --git a/drivers/media/video/s5p-tv/regs-hdmi.h b/drivers/media/video/s5p-tv/regs-hdmi.h index ac93ad6f2bc..33247d13e4c 100644 --- a/drivers/media/video/s5p-tv/regs-hdmi.h +++ b/drivers/media/video/s5p-tv/regs-hdmi.h @@ -127,6 +127,10 @@ #define HDMI_BLUE_SCR_EN (1 << 5) #define HDMI_EN (1 << 0) +/* HDMI_CON_2 */ +#define HDMI_DVI_PERAMBLE_EN (1 << 5) +#define HDMI_DVI_BAND_EN (1 << 1) + /* HDMI_PHY_STATUS */ #define HDMI_PHY_STATUS_READY (1 << 0) diff --git a/drivers/media/video/s5p-tv/regs-mixer.h b/drivers/media/video/s5p-tv/regs-mixer.h index 3c8442609c1..158abb43d0a 100644 --- a/drivers/media/video/s5p-tv/regs-mixer.h +++ b/drivers/media/video/s5p-tv/regs-mixer.h @@ -67,6 +67,7 @@ /* bits for MXR_CFG */ #define MXR_CFG_OUT_YUV444 (0 << 8) #define MXR_CFG_OUT_RGB888 (1 << 8) +#define MXR_CFG_OUT_MASK (1 << 8) #define MXR_CFG_DST_SDO (0 << 7) #define MXR_CFG_DST_HDMI (1 << 7) #define MXR_CFG_DST_MASK (1 << 7) diff --git a/drivers/media/video/s5p-tv/sdo_drv.c b/drivers/media/video/s5p-tv/sdo_drv.c index 4dddd6bd635..8cec67ef48c 100644 --- a/drivers/media/video/s5p-tv/sdo_drv.c +++ b/drivers/media/video/s5p-tv/sdo_drv.c @@ -170,6 +170,7 @@ static int sdo_g_mbus_fmt(struct v4l2_subdev *sd, fmt->height = sdev->fmt->height; fmt->code = V4L2_MBUS_FMT_FIXED; fmt->field = V4L2_FIELD_INTERLACED; + fmt->colorspace = V4L2_COLORSPACE_JPEG; return 0; } diff --git a/drivers/media/video/saa7115.c b/drivers/media/video/saa7115.c index f2ae405c74a..5cfdbc78b91 100644 --- a/drivers/media/video/saa7115.c +++ b/drivers/media/video/saa7115.c @@ -793,7 +793,6 @@ static int saa711x_s_ctrl(struct v4l2_ctrl *ctrl) saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, state->gain->val); else saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, state->gain->val | 0x80); - v4l2_ctrl_activate(state->gain, !state->agc->val); break; default: @@ -1345,35 +1344,57 @@ static int saa711x_g_vbi_data(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_dat static int saa711x_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) { struct saa711x_state *state = to_state(sd); - int reg1e; + int reg1f, reg1e; - *std = V4L2_STD_ALL; - if (state->ident != V4L2_IDENT_SAA7115) { - int reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC); + /* + * The V4L2 core already initializes std with all supported + * Standards. All driver needs to do is to mask it, to remove + * standards that don't apply from the mask + */ - if (reg1f & 0x20) - *std = V4L2_STD_525_60; - else - *std = V4L2_STD_625_50; + reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC); + v4l2_dbg(1, debug, sd, "Status byte 2 (0x1f)=0x%02x\n", reg1f); - return 0; - } + /* horizontal/vertical not locked */ + if (reg1f & 0x40) + goto ret; + + if (reg1f & 0x20) + *std &= V4L2_STD_525_60; + else + *std &= V4L2_STD_625_50; + + if (state->ident != V4L2_IDENT_SAA7115) + goto ret; reg1e = saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC); switch (reg1e & 0x03) { case 1: - *std = V4L2_STD_NTSC; + *std &= V4L2_STD_NTSC; break; case 2: - *std = V4L2_STD_PAL; + /* + * V4L2_STD_PAL just cover the european PAL standards. + * This is wrong, as the device could also be using an + * other PAL standard. + */ + *std &= V4L2_STD_PAL | V4L2_STD_PAL_N | V4L2_STD_PAL_Nc | + V4L2_STD_PAL_M | V4L2_STD_PAL_60; break; case 3: - *std = V4L2_STD_SECAM; + *std &= V4L2_STD_SECAM; break; default: + /* Can't detect anything */ break; } + + v4l2_dbg(1, debug, sd, "Status byte 1 (0x1e)=0x%02x\n", reg1e); + +ret: + v4l2_dbg(1, debug, sd, "detected std mask = %08Lx\n", *std); + return 0; } @@ -1601,7 +1622,6 @@ static int saa711x_probe(struct i2c_client *client, V4L2_CID_CHROMA_AGC, 0, 1, 1, 1); state->gain = v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops, V4L2_CID_CHROMA_GAIN, 0, 127, 1, 40); - state->gain->is_volatile = 1; sd->ctrl_handler = hdl; if (hdl->error) { int err = hdl->error; @@ -1610,8 +1630,7 @@ static int saa711x_probe(struct i2c_client *client, kfree(state); return err; } - state->agc->flags |= V4L2_CTRL_FLAG_UPDATE; - v4l2_ctrl_cluster(2, &state->agc); + v4l2_ctrl_auto_cluster(2, &state->agc, 0, true); state->input = -1; state->output = SAA7115_IPORT_ON; diff --git a/drivers/media/video/saa7134/Makefile b/drivers/media/video/saa7134/Makefile index 8a5ff4d3cf1..a646ccf5169 100644 --- a/drivers/media/video/saa7134/Makefile +++ b/drivers/media/video/saa7134/Makefile @@ -10,7 +10,7 @@ obj-$(CONFIG_VIDEO_SAA7134_ALSA) += saa7134-alsa.o obj-$(CONFIG_VIDEO_SAA7134_DVB) += saa7134-dvb.o -EXTRA_CFLAGS += -Idrivers/media/video -EXTRA_CFLAGS += -Idrivers/media/common/tuners -EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core -EXTRA_CFLAGS += -Idrivers/media/dvb/frontends +ccflags-y += -Idrivers/media/video +ccflags-y += -Idrivers/media/common/tuners +ccflags-y += -Idrivers/media/dvb/dvb-core +ccflags-y += -Idrivers/media/dvb/frontends diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index bc8d6bba8ee..9b550687213 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -843,10 +843,10 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev); int saa7134_ir_start(struct saa7134_dev *dev); void saa7134_ir_stop(struct saa7134_dev *dev); #else -#define saa7134_input_init1(dev) (0) -#define saa7134_input_fini(dev) (0) -#define saa7134_input_irq(dev) (0) -#define saa7134_probe_i2c_ir(dev) (0) -#define saa7134_ir_start(dev) (0) -#define saa7134_ir_stop(dev) (0) +#define saa7134_input_init1(dev) ((void)0) +#define saa7134_input_fini(dev) ((void)0) +#define saa7134_input_irq(dev) ((void)0) +#define saa7134_probe_i2c_ir(dev) ((void)0) +#define saa7134_ir_start(dev) ((void)0) +#define saa7134_ir_stop(dev) ((void)0) #endif diff --git a/drivers/media/video/saa7164/Makefile b/drivers/media/video/saa7164/Makefile index 6303a8e60ea..ecd5811dc48 100644 --- a/drivers/media/video/saa7164/Makefile +++ b/drivers/media/video/saa7164/Makefile @@ -4,9 +4,9 @@ saa7164-objs := saa7164-cards.o saa7164-core.o saa7164-i2c.o saa7164-dvb.o \ obj-$(CONFIG_VIDEO_SAA7164) += saa7164.o -EXTRA_CFLAGS += -Idrivers/media/video -EXTRA_CFLAGS += -Idrivers/media/common/tuners -EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core -EXTRA_CFLAGS += -Idrivers/media/dvb/frontends +ccflags-y += -Idrivers/media/video +ccflags-y += -Idrivers/media/common/tuners +ccflags-y += -Idrivers/media/dvb/dvb-core +ccflags-y += -Idrivers/media/dvb/frontends -EXTRA_CFLAGS += $(extra-cflags-y) $(extra-cflags-m) +ccflags-y += $(extra-cflags-y) $(extra-cflags-m) diff --git a/drivers/media/video/saa7164/saa7164-cards.c b/drivers/media/video/saa7164/saa7164-cards.c index 69822a4e727..971591d6450 100644 --- a/drivers/media/video/saa7164/saa7164-cards.c +++ b/drivers/media/video/saa7164/saa7164-cards.c @@ -203,6 +203,66 @@ struct saa7164_board saa7164_boards[] = { .i2c_reg_len = REGLEN_8bit, } }, }, + [SAA7164_BOARD_HAUPPAUGE_HVR2200_4] = { + .name = "Hauppauge WinTV-HVR2200", + .porta = SAA7164_MPEG_DVB, + .portb = SAA7164_MPEG_DVB, + .portc = SAA7164_MPEG_ENCODER, + .portd = SAA7164_MPEG_ENCODER, + .porte = SAA7164_MPEG_VBI, + .portf = SAA7164_MPEG_VBI, + .chiprev = SAA7164_CHIP_REV3, + .unit = {{ + .id = 0x1d, + .type = SAA7164_UNIT_EEPROM, + .name = "4K EEPROM", + .i2c_bus_nr = SAA7164_I2C_BUS_0, + .i2c_bus_addr = 0xa0 >> 1, + .i2c_reg_len = REGLEN_8bit, + }, { + .id = 0x04, + .type = SAA7164_UNIT_TUNER, + .name = "TDA18271-1", + .i2c_bus_nr = SAA7164_I2C_BUS_1, + .i2c_bus_addr = 0xc0 >> 1, + .i2c_reg_len = REGLEN_8bit, + }, { + .id = 0x05, + .type = SAA7164_UNIT_ANALOG_DEMODULATOR, + .name = "TDA8290-1", + .i2c_bus_nr = SAA7164_I2C_BUS_1, + .i2c_bus_addr = 0x84 >> 1, + .i2c_reg_len = REGLEN_8bit, + }, { + .id = 0x1b, + .type = SAA7164_UNIT_TUNER, + .name = "TDA18271-2", + .i2c_bus_nr = SAA7164_I2C_BUS_2, + .i2c_bus_addr = 0xc0 >> 1, + .i2c_reg_len = REGLEN_8bit, + }, { + .id = 0x1c, + .type = SAA7164_UNIT_ANALOG_DEMODULATOR, + .name = "TDA8290-2", + .i2c_bus_nr = SAA7164_I2C_BUS_2, + .i2c_bus_addr = 0x84 >> 1, + .i2c_reg_len = REGLEN_8bit, + }, { + .id = 0x1e, + .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, + .name = "TDA10048-1", + .i2c_bus_nr = SAA7164_I2C_BUS_1, + .i2c_bus_addr = 0x10 >> 1, + .i2c_reg_len = REGLEN_8bit, + }, { + .id = 0x1f, + .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, + .name = "TDA10048-2", + .i2c_bus_nr = SAA7164_I2C_BUS_2, + .i2c_bus_addr = 0x12 >> 1, + .i2c_reg_len = REGLEN_8bit, + } }, + }, [SAA7164_BOARD_HAUPPAUGE_HVR2250] = { .name = "Hauppauge WinTV-HVR2250", .porta = SAA7164_MPEG_DVB, @@ -387,6 +447,62 @@ struct saa7164_board saa7164_boards[] = { .i2c_reg_len = REGLEN_8bit, } }, }, + [SAA7164_BOARD_HAUPPAUGE_HVR2200_5] = { + .name = "Hauppauge WinTV-HVR2200", + .porta = SAA7164_MPEG_DVB, + .portb = SAA7164_MPEG_DVB, + .chiprev = SAA7164_CHIP_REV3, + .unit = {{ + .id = 0x23, + .type = SAA7164_UNIT_EEPROM, + .name = "4K EEPROM", + .i2c_bus_nr = SAA7164_I2C_BUS_0, + .i2c_bus_addr = 0xa0 >> 1, + .i2c_reg_len = REGLEN_8bit, + }, { + .id = 0x04, + .type = SAA7164_UNIT_TUNER, + .name = "TDA18271-1", + .i2c_bus_nr = SAA7164_I2C_BUS_1, + .i2c_bus_addr = 0xc0 >> 1, + .i2c_reg_len = REGLEN_8bit, + }, { + .id = 0x05, + .type = SAA7164_UNIT_ANALOG_DEMODULATOR, + .name = "TDA8290-1", + .i2c_bus_nr = SAA7164_I2C_BUS_1, + .i2c_bus_addr = 0x84 >> 1, + .i2c_reg_len = REGLEN_8bit, + }, { + .id = 0x21, + .type = SAA7164_UNIT_TUNER, + .name = "TDA18271-2", + .i2c_bus_nr = SAA7164_I2C_BUS_2, + .i2c_bus_addr = 0xc0 >> 1, + .i2c_reg_len = REGLEN_8bit, + }, { + .id = 0x22, + .type = SAA7164_UNIT_ANALOG_DEMODULATOR, + .name = "TDA8290-2", + .i2c_bus_nr = SAA7164_I2C_BUS_2, + .i2c_bus_addr = 0x84 >> 1, + .i2c_reg_len = REGLEN_8bit, + }, { + .id = 0x24, + .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, + .name = "TDA10048-1", + .i2c_bus_nr = SAA7164_I2C_BUS_1, + .i2c_bus_addr = 0x10 >> 1, + .i2c_reg_len = REGLEN_8bit, + }, { + .id = 0x25, + .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, + .name = "TDA10048-2", + .i2c_bus_nr = SAA7164_I2C_BUS_2, + .i2c_bus_addr = 0x12 >> 1, + .i2c_reg_len = REGLEN_8bit, + } }, + }, }; const unsigned int saa7164_bcount = ARRAY_SIZE(saa7164_boards); @@ -426,6 +542,14 @@ struct saa7164_subid saa7164_subids[] = { .subvendor = 0x0070, .subdevice = 0x8851, .card = SAA7164_BOARD_HAUPPAUGE_HVR2250_2, + }, { + .subvendor = 0x0070, + .subdevice = 0x8940, + .card = SAA7164_BOARD_HAUPPAUGE_HVR2200_4, + }, { + .subvendor = 0x0070, + .subdevice = 0x8953, + .card = SAA7164_BOARD_HAUPPAUGE_HVR2200_5, }, }; const unsigned int saa7164_idcount = ARRAY_SIZE(saa7164_subids); @@ -469,6 +593,8 @@ void saa7164_gpio_setup(struct saa7164_dev *dev) case SAA7164_BOARD_HAUPPAUGE_HVR2200: case SAA7164_BOARD_HAUPPAUGE_HVR2200_2: case SAA7164_BOARD_HAUPPAUGE_HVR2200_3: + case SAA7164_BOARD_HAUPPAUGE_HVR2200_4: + case SAA7164_BOARD_HAUPPAUGE_HVR2200_5: case SAA7164_BOARD_HAUPPAUGE_HVR2250: case SAA7164_BOARD_HAUPPAUGE_HVR2250_2: case SAA7164_BOARD_HAUPPAUGE_HVR2250_3: @@ -549,6 +675,8 @@ void saa7164_card_setup(struct saa7164_dev *dev) case SAA7164_BOARD_HAUPPAUGE_HVR2200: case SAA7164_BOARD_HAUPPAUGE_HVR2200_2: case SAA7164_BOARD_HAUPPAUGE_HVR2200_3: + case SAA7164_BOARD_HAUPPAUGE_HVR2200_4: + case SAA7164_BOARD_HAUPPAUGE_HVR2200_5: case SAA7164_BOARD_HAUPPAUGE_HVR2250: case SAA7164_BOARD_HAUPPAUGE_HVR2250_2: case SAA7164_BOARD_HAUPPAUGE_HVR2250_3: diff --git a/drivers/media/video/saa7164/saa7164-dvb.c b/drivers/media/video/saa7164/saa7164-dvb.c index f65eab63ca8..5c5cc3ebf9b 100644 --- a/drivers/media/video/saa7164/saa7164-dvb.c +++ b/drivers/media/video/saa7164/saa7164-dvb.c @@ -475,6 +475,8 @@ int saa7164_dvb_register(struct saa7164_port *port) case SAA7164_BOARD_HAUPPAUGE_HVR2200: case SAA7164_BOARD_HAUPPAUGE_HVR2200_2: case SAA7164_BOARD_HAUPPAUGE_HVR2200_3: + case SAA7164_BOARD_HAUPPAUGE_HVR2200_4: + case SAA7164_BOARD_HAUPPAUGE_HVR2200_5: i2c_bus = &dev->i2c_bus[port->nr + 1]; switch (port->nr) { case 0: diff --git a/drivers/media/video/saa7164/saa7164.h b/drivers/media/video/saa7164/saa7164.h index 6678bf1e781..742b34103b5 100644 --- a/drivers/media/video/saa7164/saa7164.h +++ b/drivers/media/video/saa7164/saa7164.h @@ -82,6 +82,8 @@ #define SAA7164_BOARD_HAUPPAUGE_HVR2200_3 6 #define SAA7164_BOARD_HAUPPAUGE_HVR2250_2 7 #define SAA7164_BOARD_HAUPPAUGE_HVR2250_3 8 +#define SAA7164_BOARD_HAUPPAUGE_HVR2200_4 9 +#define SAA7164_BOARD_HAUPPAUGE_HVR2200_5 10 #define SAA7164_MAX_UNITS 8 #define SAA7164_TS_NUMBER_OF_LINES 312 diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c index e54089802b6..f390682629c 100644 --- a/drivers/media/video/sh_mobile_ceu_camera.c +++ b/drivers/media/video/sh_mobile_ceu_camera.c @@ -90,7 +90,6 @@ struct sh_mobile_ceu_buffer { struct vb2_buffer vb; /* v4l buffer must be first */ struct list_head queue; - enum v4l2_mbus_pixelcode code; }; struct sh_mobile_ceu_dev { @@ -100,7 +99,8 @@ struct sh_mobile_ceu_dev { unsigned int irq; void __iomem *base; - unsigned long video_limit; + size_t video_limit; + size_t buf_total; spinlock_t lock; /* Protects video buffer lists */ struct list_head capture; @@ -121,7 +121,7 @@ struct sh_mobile_ceu_dev { }; struct sh_mobile_ceu_cam { - /* CEU offsets within scaled by the CEU camera output */ + /* CEU offsets within the camera output, before the CEU scaler */ unsigned int ceu_left; unsigned int ceu_top; /* Client output, as seen by the CEU */ @@ -144,30 +144,6 @@ static struct sh_mobile_ceu_buffer *to_ceu_vb(struct vb2_buffer *vb) return container_of(vb, struct sh_mobile_ceu_buffer, vb); } -static unsigned long make_bus_param(struct sh_mobile_ceu_dev *pcdev) -{ - unsigned long flags; - - flags = SOCAM_MASTER | - SOCAM_PCLK_SAMPLE_RISING | - SOCAM_HSYNC_ACTIVE_HIGH | - SOCAM_HSYNC_ACTIVE_LOW | - SOCAM_VSYNC_ACTIVE_HIGH | - SOCAM_VSYNC_ACTIVE_LOW | - SOCAM_DATA_ACTIVE_HIGH; - - if (pcdev->pdata->flags & SH_CEU_FLAG_USE_8BIT_BUS) - flags |= SOCAM_DATAWIDTH_8; - - if (pcdev->pdata->flags & SH_CEU_FLAG_USE_16BIT_BUS) - flags |= SOCAM_DATAWIDTH_16; - - if (flags & SOCAM_DATAWIDTH_MASK) - return flags; - - return 0; -} - static void ceu_write(struct sh_mobile_ceu_dev *priv, unsigned long reg_offs, u32 data) { @@ -216,34 +192,62 @@ static int sh_mobile_ceu_soft_reset(struct sh_mobile_ceu_dev *pcdev) /* * Videobuf operations */ + +/* + * .queue_setup() is called to check, whether the driver can accept the + * requested number of buffers and to fill in plane sizes + * for the current frame format if required + */ static int sh_mobile_ceu_videobuf_setup(struct vb2_queue *vq, + const struct v4l2_format *fmt, unsigned int *count, unsigned int *num_planes, - unsigned long sizes[], void *alloc_ctxs[]) + unsigned int sizes[], void *alloc_ctxs[]) { struct soc_camera_device *icd = container_of(vq, struct soc_camera_device, vb2_vidq); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct sh_mobile_ceu_dev *pcdev = ici->priv; - int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, - icd->current_fmt->host_fmt); + int bytes_per_line; + unsigned int height; + if (fmt) { + const struct soc_camera_format_xlate *xlate = soc_camera_xlate_by_fourcc(icd, + fmt->fmt.pix.pixelformat); + if (!xlate) + return -EINVAL; + bytes_per_line = soc_mbus_bytes_per_line(fmt->fmt.pix.width, + xlate->host_fmt); + height = fmt->fmt.pix.height; + } else { + /* Called from VIDIOC_REQBUFS or in compatibility mode */ + bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, + icd->current_fmt->host_fmt); + height = icd->user_height; + } if (bytes_per_line < 0) return bytes_per_line; - *num_planes = 1; + sizes[0] = bytes_per_line * height; - pcdev->sequence = 0; - sizes[0] = bytes_per_line * icd->user_height; alloc_ctxs[0] = pcdev->alloc_ctx; + if (!vq->num_buffers) + pcdev->sequence = 0; + if (!*count) *count = 2; - if (pcdev->video_limit) { - if (PAGE_ALIGN(sizes[0]) * *count > pcdev->video_limit) - *count = pcdev->video_limit / PAGE_ALIGN(sizes[0]); + /* If *num_planes != 0, we have already verified *count. */ + if (pcdev->video_limit && !*num_planes) { + size_t size = PAGE_ALIGN(sizes[0]) * *count; + + if (size + pcdev->buf_total > pcdev->video_limit) + *count = (pcdev->video_limit - pcdev->buf_total) / + PAGE_ALIGN(sizes[0]); } - dev_dbg(icd->parent, "count=%d, size=%lu\n", *count, sizes[0]); + *num_planes = 1; + + dev_dbg(icd->parent, "count=%d, size=%u\n", *count, sizes[0]); return 0; } @@ -267,6 +271,7 @@ static int sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev) unsigned long top1, top2; unsigned long bottom1, bottom2; u32 status; + bool planar; int ret = 0; /* @@ -312,19 +317,31 @@ static int sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev) bottom2 = CDBCR; } - phys_addr_top = vb2_dma_contig_plane_paddr(pcdev->active, 0); - - ceu_write(pcdev, top1, phys_addr_top); - if (V4L2_FIELD_NONE != pcdev->field) { - phys_addr_bottom = phys_addr_top + icd->user_width; - ceu_write(pcdev, bottom1, phys_addr_bottom); - } + phys_addr_top = vb2_dma_contig_plane_dma_addr(pcdev->active, 0); switch (icd->current_fmt->host_fmt->fourcc) { case V4L2_PIX_FMT_NV12: case V4L2_PIX_FMT_NV21: case V4L2_PIX_FMT_NV16: case V4L2_PIX_FMT_NV61: + planar = true; + break; + default: + planar = false; + } + + ceu_write(pcdev, top1, phys_addr_top); + if (V4L2_FIELD_NONE != pcdev->field) { + if (planar) + phys_addr_bottom = phys_addr_top + icd->user_width; + else + phys_addr_bottom = phys_addr_top + + soc_mbus_bytes_per_line(icd->user_width, + icd->current_fmt->host_fmt); + ceu_write(pcdev, bottom1, phys_addr_bottom); + } + + if (planar) { phys_addr_top += icd->user_width * icd->user_height; ceu_write(pcdev, top2, phys_addr_top); @@ -341,23 +358,40 @@ static int sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev) static int sh_mobile_ceu_videobuf_prepare(struct vb2_buffer *vb) { + struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vb); + + /* Added list head initialization on alloc */ + WARN(!list_empty(&buf->queue), "Buffer %p on queue!\n", vb); + + return 0; +} + +static void sh_mobile_ceu_videobuf_queue(struct vb2_buffer *vb) +{ struct soc_camera_device *icd = container_of(vb->vb2_queue, struct soc_camera_device, vb2_vidq); - struct sh_mobile_ceu_buffer *buf; + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct sh_mobile_ceu_dev *pcdev = ici->priv; + struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vb); + unsigned long size; int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, icd->current_fmt->host_fmt); - unsigned long size; if (bytes_per_line < 0) - return bytes_per_line; + goto error; - buf = to_ceu_vb(vb); + size = icd->user_height * bytes_per_line; + + if (vb2_plane_size(vb, 0) < size) { + dev_err(icd->parent, "Buffer #%d too small (%lu < %lu)\n", + vb->v4l2_buf.index, vb2_plane_size(vb, 0), size); + goto error; + } + + vb2_set_plane_payload(vb, 0, size); dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__, vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0)); - /* Added list head initialization on alloc */ - WARN(!list_empty(&buf->queue), "Buffer %p on queue!\n", vb); - #ifdef DEBUG /* * This can be useful if you want to see if we actually fill @@ -367,31 +401,6 @@ static int sh_mobile_ceu_videobuf_prepare(struct vb2_buffer *vb) memset(vb2_plane_vaddr(vb, 0), 0xaa, vb2_get_plane_payload(vb, 0)); #endif - BUG_ON(NULL == icd->current_fmt); - - size = icd->user_height * bytes_per_line; - - if (vb2_plane_size(vb, 0) < size) { - dev_err(icd->parent, "Buffer too small (%lu < %lu)\n", - vb2_plane_size(vb, 0), size); - return -ENOBUFS; - } - - vb2_set_plane_payload(vb, 0, size); - - return 0; -} - -static void sh_mobile_ceu_videobuf_queue(struct vb2_buffer *vb) -{ - struct soc_camera_device *icd = container_of(vb->vb2_queue, struct soc_camera_device, vb2_vidq); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct sh_mobile_ceu_dev *pcdev = ici->priv; - struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vb); - - dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__, - vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0)); - spin_lock_irq(&pcdev->lock); list_add_tail(&buf->queue, &pcdev->capture); @@ -405,6 +414,11 @@ static void sh_mobile_ceu_videobuf_queue(struct vb2_buffer *vb) sh_mobile_ceu_capture(pcdev); } spin_unlock_irq(&pcdev->lock); + + return; + +error: + vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); } static void sh_mobile_ceu_videobuf_release(struct vb2_buffer *vb) @@ -429,11 +443,23 @@ static void sh_mobile_ceu_videobuf_release(struct vb2_buffer *vb) if (buf->queue.next) list_del_init(&buf->queue); + pcdev->buf_total -= PAGE_ALIGN(vb2_plane_size(vb, 0)); + dev_dbg(icd->parent, "%s() %zu bytes buffers\n", __func__, + pcdev->buf_total); + spin_unlock_irq(&pcdev->lock); } static int sh_mobile_ceu_videobuf_init(struct vb2_buffer *vb) { + struct soc_camera_device *icd = container_of(vb->vb2_queue, struct soc_camera_device, vb2_vidq); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct sh_mobile_ceu_dev *pcdev = ici->priv; + + pcdev->buf_total += PAGE_ALIGN(vb2_plane_size(vb, 0)); + dev_dbg(icd->parent, "%s() %zu bytes buffers\n", __func__, + pcdev->buf_total); + /* This is for locking debugging only */ INIT_LIST_HEAD(&to_ceu_vb(vb)->queue); return 0; @@ -535,19 +561,29 @@ static int sh_mobile_ceu_add_device(struct soc_camera_device *icd) pm_runtime_get_sync(ici->v4l2_dev.dev); + pcdev->buf_total = 0; + ret = sh_mobile_ceu_soft_reset(pcdev); csi2_sd = find_csi2(pcdev); + if (csi2_sd) + csi2_sd->grp_id = (long)icd; ret = v4l2_subdev_call(csi2_sd, core, s_power, 1); - if (ret != -ENODEV && ret != -ENOIOCTLCMD && ret < 0) { + if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) { pm_runtime_put_sync(ici->v4l2_dev.dev); - } else { - pcdev->icd = icd; - ret = 0; + return ret; } - return ret; + /* + * -ENODEV is special: either csi2_sd == NULL or the CSI-2 driver + * has not found this soc-camera device among its clients + */ + if (ret == -ENODEV && csi2_sd) + csi2_sd->grp_id = 0; + pcdev->icd = icd; + + return 0; } /* Called with .video_lock held */ @@ -560,6 +596,8 @@ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd) BUG_ON(icd != pcdev->icd); v4l2_subdev_call(csi2_sd, core, s_power, 0); + if (csi2_sd) + csi2_sd->grp_id = 0; /* disable capture, disable interrupts */ ceu_write(pcdev, CEIER, 0); sh_mobile_ceu_soft_reset(pcdev); @@ -628,22 +666,22 @@ static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd) left_offset = cam->ceu_left; top_offset = cam->ceu_top; - /* CEU cropping (CFSZR) is applied _after_ the scaling filter (CFLCR) */ + WARN_ON(icd->user_width & 3 || icd->user_height & 3); + + width = icd->user_width; + if (pcdev->image_mode) { in_width = cam->width; if (!pcdev->is_16bit) { in_width *= 2; left_offset *= 2; } - width = icd->user_width; - cdwdr_width = icd->user_width; + cdwdr_width = width; } else { - int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, + int bytes_per_line = soc_mbus_bytes_per_line(width, icd->current_fmt->host_fmt); unsigned int w_factor; - width = icd->user_width; - switch (icd->current_fmt->host_fmt->packing) { case SOC_MBUS_PACKING_2X8_PADHI: w_factor = 2; @@ -653,10 +691,10 @@ static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd) } in_width = cam->width * w_factor; - left_offset = left_offset * w_factor; + left_offset *= w_factor; if (bytes_per_line < 0) - cdwdr_width = icd->user_width; + cdwdr_width = width; else cdwdr_width = bytes_per_line; } @@ -664,7 +702,7 @@ static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd) height = icd->user_height; in_height = cam->height; if (V4L2_FIELD_NONE != pcdev->field) { - height /= 2; + height = (height / 2) & ~3; in_height /= 2; top_offset /= 2; cdwdr_width *= 2; @@ -686,6 +724,7 @@ static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd) ceu_write(pcdev, CAMOR, camor); ceu_write(pcdev, CAPWR, (in_height << 16) | in_width); + /* CFSZR clipping is applied _after_ the scaling filter (CFLCR) */ ceu_write(pcdev, CFSZR, (height << 16) | width); ceu_write(pcdev, CDWDR, cdwdr_width); } @@ -723,66 +762,93 @@ static void capture_restore(struct sh_mobile_ceu_dev *pcdev, u32 capsr) ceu_write(pcdev, CAPSR, capsr); } +/* Find the bus subdevice driver, e.g., CSI2 */ +static struct v4l2_subdev *find_bus_subdev(struct sh_mobile_ceu_dev *pcdev, + struct soc_camera_device *icd) +{ + if (pcdev->csi2_pdev) { + struct v4l2_subdev *csi2_sd = find_csi2(pcdev); + if (csi2_sd && csi2_sd->grp_id == (u32)icd) + return csi2_sd; + } + + return soc_camera_to_subdev(icd); +} + +#define CEU_BUS_FLAGS (V4L2_MBUS_MASTER | \ + V4L2_MBUS_PCLK_SAMPLE_RISING | \ + V4L2_MBUS_HSYNC_ACTIVE_HIGH | \ + V4L2_MBUS_HSYNC_ACTIVE_LOW | \ + V4L2_MBUS_VSYNC_ACTIVE_HIGH | \ + V4L2_MBUS_VSYNC_ACTIVE_LOW | \ + V4L2_MBUS_DATA_ACTIVE_HIGH) + /* Capture is not running, no interrupts, no locking needed */ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) { struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct sh_mobile_ceu_dev *pcdev = ici->priv; - int ret; - unsigned long camera_flags, common_flags, value; - int yuv_lineskip; + struct v4l2_subdev *sd = find_bus_subdev(pcdev, icd); struct sh_mobile_ceu_cam *cam = icd->host_priv; + struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,}; + unsigned long value, common_flags = CEU_BUS_FLAGS; u32 capsr = capture_save_reset(pcdev); + unsigned int yuv_lineskip; + int ret; - camera_flags = icd->ops->query_bus_param(icd); - common_flags = soc_camera_bus_param_compatible(camera_flags, - make_bus_param(pcdev)); - if (!common_flags) - return -EINVAL; + /* + * If the client doesn't implement g_mbus_config, we just use our + * platform data + */ + ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg); + if (!ret) { + common_flags = soc_mbus_config_compatible(&cfg, + common_flags); + if (!common_flags) + return -EINVAL; + } else if (ret != -ENOIOCTLCMD) { + return ret; + } /* Make choises, based on platform preferences */ - if ((common_flags & SOCAM_HSYNC_ACTIVE_HIGH) && - (common_flags & SOCAM_HSYNC_ACTIVE_LOW)) { + if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) && + (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) { if (pcdev->pdata->flags & SH_CEU_FLAG_HSYNC_LOW) - common_flags &= ~SOCAM_HSYNC_ACTIVE_HIGH; + common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH; else - common_flags &= ~SOCAM_HSYNC_ACTIVE_LOW; + common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW; } - if ((common_flags & SOCAM_VSYNC_ACTIVE_HIGH) && - (common_flags & SOCAM_VSYNC_ACTIVE_LOW)) { + if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) && + (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) { if (pcdev->pdata->flags & SH_CEU_FLAG_VSYNC_LOW) - common_flags &= ~SOCAM_VSYNC_ACTIVE_HIGH; + common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH; else - common_flags &= ~SOCAM_VSYNC_ACTIVE_LOW; + common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW; } - ret = icd->ops->set_bus_param(icd, common_flags); - if (ret < 0) + cfg.flags = common_flags; + ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg); + if (ret < 0 && ret != -ENOIOCTLCMD) return ret; - switch (common_flags & SOCAM_DATAWIDTH_MASK) { - case SOCAM_DATAWIDTH_8: - pcdev->is_16bit = 0; - break; - case SOCAM_DATAWIDTH_16: + if (icd->current_fmt->host_fmt->bits_per_sample > 8) pcdev->is_16bit = 1; - break; - default: - return -EINVAL; - } + else + pcdev->is_16bit = 0; ceu_write(pcdev, CRCNTR, 0); ceu_write(pcdev, CRCMPR, 0); value = 0x00000010; /* data fetch by default */ - yuv_lineskip = 0; + yuv_lineskip = 0x10; switch (icd->current_fmt->host_fmt->fourcc) { case V4L2_PIX_FMT_NV12: case V4L2_PIX_FMT_NV21: - yuv_lineskip = 1; /* skip for NV12/21, no skip for NV16/61 */ + /* convert 4:2:2 -> 4:2:0 */ + yuv_lineskip = 0; /* skip for NV12/21, no skip for NV16/61 */ /* fall-through */ case V4L2_PIX_FMT_NV16: case V4L2_PIX_FMT_NV61: @@ -808,8 +874,8 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd, icd->current_fmt->host_fmt->fourcc == V4L2_PIX_FMT_NV61) value ^= 0x00000100; /* swap U, V to change from NV1x->NVx1 */ - value |= common_flags & SOCAM_VSYNC_ACTIVE_LOW ? 1 << 1 : 0; - value |= common_flags & SOCAM_HSYNC_ACTIVE_LOW ? 1 << 0 : 0; + value |= common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW ? 1 << 1 : 0; + value |= common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW ? 1 << 0 : 0; value |= pcdev->is_16bit ? 1 << 12 : 0; /* CSI2 mode */ @@ -852,9 +918,7 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd, * using 7 we swap the data bytes to match the incoming order: * D0, D1, D2, D3, D4, D5, D6, D7 */ - value = 0x00000017; - if (yuv_lineskip) - value &= ~0x00000010; /* convert 4:2:2 -> 4:2:0 */ + value = 0x00000007 | yuv_lineskip; ceu_write(pcdev, CDOCR, value); ceu_write(pcdev, CFWCR, 0); /* keep "datafetch firewall" disabled */ @@ -875,13 +939,19 @@ static int sh_mobile_ceu_try_bus_param(struct soc_camera_device *icd, { struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct sh_mobile_ceu_dev *pcdev = ici->priv; - unsigned long camera_flags, common_flags; + struct v4l2_subdev *sd = find_bus_subdev(pcdev, icd); + unsigned long common_flags = CEU_BUS_FLAGS; + struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,}; + int ret; - camera_flags = icd->ops->query_bus_param(icd); - common_flags = soc_camera_bus_param_compatible(camera_flags, - make_bus_param(pcdev)); - if (!common_flags || buswidth > 16 || - (buswidth > 8 && !(common_flags & SOCAM_DATAWIDTH_16))) + ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg); + if (!ret) + common_flags = soc_mbus_config_compatible(&cfg, + common_flags); + else if (ret != -ENOIOCTLCMD) + return ret; + + if (!common_flags || buswidth > 16) return -EINVAL; return 0; @@ -891,26 +961,26 @@ static const struct soc_mbus_pixelfmt sh_mobile_ceu_formats[] = { { .fourcc = V4L2_PIX_FMT_NV12, .name = "NV12", - .bits_per_sample = 12, - .packing = SOC_MBUS_PACKING_NONE, + .bits_per_sample = 8, + .packing = SOC_MBUS_PACKING_1_5X8, .order = SOC_MBUS_ORDER_LE, }, { .fourcc = V4L2_PIX_FMT_NV21, .name = "NV21", - .bits_per_sample = 12, - .packing = SOC_MBUS_PACKING_NONE, + .bits_per_sample = 8, + .packing = SOC_MBUS_PACKING_1_5X8, .order = SOC_MBUS_ORDER_LE, }, { .fourcc = V4L2_PIX_FMT_NV16, .name = "NV16", - .bits_per_sample = 16, - .packing = SOC_MBUS_PACKING_NONE, + .bits_per_sample = 8, + .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_LE, }, { .fourcc = V4L2_PIX_FMT_NV61, .name = "NV61", - .bits_per_sample = 16, - .packing = SOC_MBUS_PACKING_NONE, + .bits_per_sample = 8, + .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_LE, }, }; @@ -920,6 +990,8 @@ static bool sh_mobile_ceu_packing_supported(const struct soc_mbus_pixelfmt *fmt) { return fmt->packing == SOC_MBUS_PACKING_NONE || (fmt->bits_per_sample == 8 && + fmt->packing == SOC_MBUS_PACKING_1_5X8) || + (fmt->bits_per_sample == 8 && fmt->packing == SOC_MBUS_PACKING_2X8_PADHI) || (fmt->bits_per_sample > 8 && fmt->packing == SOC_MBUS_PACKING_EXTEND16); @@ -927,6 +999,38 @@ static bool sh_mobile_ceu_packing_supported(const struct soc_mbus_pixelfmt *fmt) static int client_g_rect(struct v4l2_subdev *sd, struct v4l2_rect *rect); +static struct soc_camera_device *ctrl_to_icd(struct v4l2_ctrl *ctrl) +{ + return container_of(ctrl->handler, struct soc_camera_device, + ctrl_handler); +} + +static int sh_mobile_ceu_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct soc_camera_device *icd = ctrl_to_icd(ctrl); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct sh_mobile_ceu_dev *pcdev = ici->priv; + + switch (ctrl->id) { + case V4L2_CID_SHARPNESS: + switch (icd->current_fmt->host_fmt->fourcc) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV61: + ceu_write(pcdev, CLFCR, !ctrl->val); + return 0; + } + break; + } + + return -EINVAL; +} + +static const struct v4l2_ctrl_ops sh_mobile_ceu_ctrl_ops = { + .s_ctrl = sh_mobile_ceu_s_ctrl, +}; + static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int idx, struct soc_camera_format_xlate *xlate) { @@ -952,6 +1056,7 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int } if (!pcdev->pdata->csi2) { + /* Are there any restrictions in the CSI-2 case? */ ret = sh_mobile_ceu_try_bus_param(icd, fmt->bits_per_sample); if (ret < 0) return 0; @@ -962,6 +1067,12 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int struct v4l2_rect rect; int shift = 0; + /* Add our control */ + v4l2_ctrl_new_std(&icd->ctrl_handler, &sh_mobile_ceu_ctrl_ops, + V4L2_CID_SHARPNESS, 0, 1, 1, 0); + if (icd->ctrl_handler.error) + return icd->ctrl_handler.error; + /* FIXME: subwindow is lost between close / open */ /* Cache current client geometry */ @@ -1004,9 +1115,6 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int cam->width = mf.width; cam->height = mf.height; - cam->width = mf.width; - cam->height = mf.height; - icd->host_priv = cam; } else { cam = icd->host_priv; @@ -1278,6 +1386,7 @@ static int client_s_fmt(struct soc_camera_device *icd, unsigned int width = mf->width, height = mf->height, tmp_w, tmp_h; unsigned int max_width, max_height; struct v4l2_cropcap cap; + bool ceu_1to1; int ret; ret = v4l2_device_call_until_err(sd->v4l2_dev, (long)icd, video, @@ -1287,7 +1396,14 @@ static int client_s_fmt(struct soc_camera_device *icd, dev_geo(dev, "camera scaled to %ux%u\n", mf->width, mf->height); - if ((width == mf->width && height == mf->height) || !ceu_can_scale) + if (width == mf->width && height == mf->height) { + /* Perfect! The client has done it all. */ + ceu_1to1 = true; + goto update_cache; + } + + ceu_1to1 = false; + if (!ceu_can_scale) goto update_cache; cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; @@ -1327,7 +1443,10 @@ update_cache: if (ret < 0) return ret; - update_subrect(cam); + if (ceu_1to1) + cam->subrect = cam->rect; + else + update_subrect(cam); return 0; } @@ -1414,7 +1533,10 @@ static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd, capsr = capture_save_reset(pcdev); dev_dbg(dev, "CAPSR 0x%x, CFLCR 0x%x\n", capsr, pcdev->cflcr); - /* 1. - 2. Apply iterative camera S_CROP for new input window. */ + /* + * 1. - 2. Apply iterative camera S_CROP for new input window, read back + * actual camera rectangle. + */ ret = client_s_crop(icd, a, &cam_crop); if (ret < 0) return ret; @@ -1498,8 +1620,9 @@ static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd, ceu_write(pcdev, CFLCR, cflcr); } - icd->user_width = out_width; - icd->user_height = out_height; + icd->user_width = out_width & ~3; + icd->user_height = out_height & ~3; + /* Offsets are applied at the CEU scaling filter input */ cam->ceu_left = scale_down(rect->left - cam_rect->left, scale_cam_h) & ~1; cam->ceu_top = scale_down(rect->top - cam_rect->top, scale_cam_v) & ~1; @@ -1538,7 +1661,7 @@ static int sh_mobile_ceu_get_crop(struct soc_camera_device *icd, * CEU crop, mapped backed onto the client input (subrect). */ static void calculate_client_output(struct soc_camera_device *icd, - struct v4l2_pix_format *pix, struct v4l2_mbus_framefmt *mf) + const struct v4l2_pix_format *pix, struct v4l2_mbus_framefmt *mf) { struct sh_mobile_ceu_cam *cam = icd->host_priv; struct device *dev = icd->parent; @@ -1574,8 +1697,8 @@ static void calculate_client_output(struct soc_camera_device *icd, dev_geo(dev, "3: scales %u:%u\n", scale_h, scale_v); /* - * 4. Calculate client output window by applying combined scales to real - * input window. + * 4. Calculate desired client output window by applying combined scales + * to client (real) input window. */ mf->width = scale_down(cam->rect.width, scale_h); mf->height = scale_down(cam->rect.height, scale_v); @@ -1600,8 +1723,6 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd, bool image_mode; enum v4l2_field field; - dev_geo(dev, "S_FMT(pix=0x%x, %ux%u)\n", pixfmt, pix->width, pix->height); - switch (pix->field) { default: pix->field = V4L2_FIELD_NONE; @@ -1622,8 +1743,8 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd, return -EINVAL; } - /* 1.-4. Calculate client output geometry */ - calculate_client_output(icd, &f->fmt.pix, &mf); + /* 1.-4. Calculate desired client output geometry */ + calculate_client_output(icd, pix, &mf); mf.field = pix->field; mf.colorspace = pix->colorspace; mf.code = xlate->code; @@ -1639,6 +1760,9 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd, image_mode = false; } + dev_geo(dev, "S_FMT(pix=0x%x, fld 0x%x, code 0x%x, %ux%u)\n", pixfmt, mf.field, mf.code, + pix->width, pix->height); + dev_geo(dev, "4: request camera output %ux%u\n", mf.width, mf.height); /* 5. - 9. */ @@ -1700,6 +1824,10 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd, pcdev->field = field; pcdev->image_mode = image_mode; + /* CFSZR requirement */ + pix->width &= ~3; + pix->height &= ~3; + return 0; } @@ -1725,7 +1853,8 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, /* FIXME: calculate using depth and bus width */ - v4l_bound_align_image(&pix->width, 2, 2560, 1, + /* CFSZR requires height and width to be 4-pixel aligned */ + v4l_bound_align_image(&pix->width, 2, 2560, 2, &pix->height, 4, 1920, 2, 0); width = pix->width; @@ -1778,6 +1907,9 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, pix->height = height; } + pix->width &= ~3; + pix->height &= ~3; + dev_geo(icd->parent, "%s(): return %d, fmt 0x%x, %ux%u\n", __func__, ret, pix->pixelformat, pix->width, pix->height); @@ -1824,8 +1956,8 @@ static int sh_mobile_ceu_set_livecrop(struct soc_camera_device *icd, out_height != f.fmt.pix.height)) ret = -EINVAL; if (!ret) { - icd->user_width = out_width; - icd->user_height = out_height; + icd->user_width = out_width & ~3; + icd->user_height = out_height & ~3; ret = sh_mobile_ceu_set_bus_param(icd, icd->current_fmt->host_fmt->fourcc); } @@ -1869,55 +2001,6 @@ static int sh_mobile_ceu_init_videobuf(struct vb2_queue *q, return vb2_queue_init(q); } -static int sh_mobile_ceu_get_ctrl(struct soc_camera_device *icd, - struct v4l2_control *ctrl) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct sh_mobile_ceu_dev *pcdev = ici->priv; - u32 val; - - switch (ctrl->id) { - case V4L2_CID_SHARPNESS: - val = ceu_read(pcdev, CLFCR); - ctrl->value = val ^ 1; - return 0; - } - return -ENOIOCTLCMD; -} - -static int sh_mobile_ceu_set_ctrl(struct soc_camera_device *icd, - struct v4l2_control *ctrl) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct sh_mobile_ceu_dev *pcdev = ici->priv; - - switch (ctrl->id) { - case V4L2_CID_SHARPNESS: - switch (icd->current_fmt->host_fmt->fourcc) { - case V4L2_PIX_FMT_NV12: - case V4L2_PIX_FMT_NV21: - case V4L2_PIX_FMT_NV16: - case V4L2_PIX_FMT_NV61: - ceu_write(pcdev, CLFCR, !ctrl->value); - return 0; - } - return -EINVAL; - } - return -ENOIOCTLCMD; -} - -static const struct v4l2_queryctrl sh_mobile_ceu_controls[] = { - { - .id = V4L2_CID_SHARPNESS, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Low-pass filter", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, -}; - static struct soc_camera_host_ops sh_mobile_ceu_host_ops = { .owner = THIS_MODULE, .add = sh_mobile_ceu_add_device, @@ -1929,14 +2012,10 @@ static struct soc_camera_host_ops sh_mobile_ceu_host_ops = { .set_livecrop = sh_mobile_ceu_set_livecrop, .set_fmt = sh_mobile_ceu_set_fmt, .try_fmt = sh_mobile_ceu_try_fmt, - .set_ctrl = sh_mobile_ceu_set_ctrl, - .get_ctrl = sh_mobile_ceu_get_ctrl, .poll = sh_mobile_ceu_poll, .querycap = sh_mobile_ceu_querycap, .set_bus_param = sh_mobile_ceu_set_bus_param, .init_videobuf2 = sh_mobile_ceu_init_videobuf, - .controls = sh_mobile_ceu_controls, - .num_controls = ARRAY_SIZE(sh_mobile_ceu_controls), }; struct bus_wait { diff --git a/drivers/media/video/sh_mobile_csi2.c b/drivers/media/video/sh_mobile_csi2.c index 2893a0134c7..ea4f0473ed3 100644 --- a/drivers/media/video/sh_mobile_csi2.c +++ b/drivers/media/video/sh_mobile_csi2.c @@ -15,10 +15,12 @@ #include <linux/pm_runtime.h> #include <linux/slab.h> #include <linux/videodev2.h> +#include <linux/module.h> #include <media/sh_mobile_ceu.h> #include <media/sh_mobile_csi2.h> #include <media/soc_camera.h> +#include <media/soc_mediabus.h> #include <media/v4l2-common.h> #include <media/v4l2-dev.h> #include <media/v4l2-device.h> @@ -35,11 +37,10 @@ struct sh_csi2 { struct v4l2_subdev subdev; struct list_head list; unsigned int irq; + unsigned long mipi_flags; void __iomem *base; struct platform_device *pdev; struct sh_csi2_client_config *client; - unsigned long (*query_bus_param)(struct soc_camera_device *); - int (*set_bus_param)(struct soc_camera_device *, unsigned long); }; static int sh_csi2_try_fmt(struct v4l2_subdev *sd, @@ -127,9 +128,34 @@ static int sh_csi2_s_fmt(struct v4l2_subdev *sd, return 0; } +static int sh_csi2_g_mbus_config(struct v4l2_subdev *sd, + struct v4l2_mbus_config *cfg) +{ + cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | + V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH | + V4L2_MBUS_MASTER | V4L2_MBUS_DATA_ACTIVE_HIGH; + cfg->type = V4L2_MBUS_PARALLEL; + + return 0; +} + +static int sh_csi2_s_mbus_config(struct v4l2_subdev *sd, + const struct v4l2_mbus_config *cfg) +{ + struct sh_csi2 *priv = container_of(sd, struct sh_csi2, subdev); + struct soc_camera_device *icd = (struct soc_camera_device *)sd->grp_id; + struct v4l2_subdev *client_sd = soc_camera_to_subdev(icd); + struct v4l2_mbus_config client_cfg = {.type = V4L2_MBUS_CSI2, + .flags = priv->mipi_flags}; + + return v4l2_subdev_call(client_sd, video, s_mbus_config, &client_cfg); +} + static struct v4l2_subdev_video_ops sh_csi2_subdev_video_ops = { .s_mbus_fmt = sh_csi2_s_fmt, .try_mbus_fmt = sh_csi2_try_fmt, + .g_mbus_config = sh_csi2_g_mbus_config, + .s_mbus_config = sh_csi2_s_mbus_config, }; static void sh_csi2_hwinit(struct sh_csi2 *priv) @@ -144,11 +170,21 @@ static void sh_csi2_hwinit(struct sh_csi2 *priv) udelay(5); iowrite32(0x00000000, priv->base + SH_CSI2_SRST); - if (priv->client->lanes & 3) - tmp |= priv->client->lanes & 3; - else - /* Default - both lanes */ - tmp |= 3; + switch (pdata->type) { + case SH_CSI2C: + if (priv->client->lanes == 1) + tmp |= 1; + else + /* Default - both lanes */ + tmp |= 3; + break; + case SH_CSI2I: + if (!priv->client->lanes || priv->client->lanes > 4) + /* Default - all 4 lanes */ + tmp |= 0xf; + else + tmp |= (1 << priv->client->lanes) - 1; + } if (priv->client->phy == SH_CSI2_PHY_MAIN) tmp |= 0x8000; @@ -163,38 +199,18 @@ static void sh_csi2_hwinit(struct sh_csi2 *priv) iowrite32(tmp, priv->base + SH_CSI2_CHKSUM); } -static int sh_csi2_set_bus_param(struct soc_camera_device *icd, - unsigned long flags) -{ - return 0; -} - -static unsigned long sh_csi2_query_bus_param(struct soc_camera_device *icd) -{ - struct soc_camera_link *icl = to_soc_camera_link(icd); - const unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | - SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH | - SOCAM_MASTER | SOCAM_DATAWIDTH_8 | SOCAM_DATA_ACTIVE_HIGH; - - return soc_camera_apply_sensor_flags(icl, flags); -} - static int sh_csi2_client_connect(struct sh_csi2 *priv) { struct sh_csi2_pdata *pdata = priv->pdev->dev.platform_data; - struct v4l2_subdev *sd, *csi2_sd = &priv->subdev; - struct soc_camera_device *icd = NULL; + struct soc_camera_device *icd = (struct soc_camera_device *)priv->subdev.grp_id; + struct v4l2_subdev *client_sd = soc_camera_to_subdev(icd); struct device *dev = v4l2_get_subdevdata(&priv->subdev); - int i; + struct v4l2_mbus_config cfg; + unsigned long common_flags, csi2_flags; + int i, ret; - v4l2_device_for_each_subdev(sd, csi2_sd->v4l2_dev) - if (sd->grp_id) { - icd = (struct soc_camera_device *)sd->grp_id; - break; - } - - if (!icd) - return -EINVAL; + if (priv->client) + return -EBUSY; for (i = 0; i < pdata->num_clients; i++) if (&pdata->clients[i].pdev->dev == icd->pdev) @@ -205,14 +221,41 @@ static int sh_csi2_client_connect(struct sh_csi2 *priv) if (i == pdata->num_clients) return -ENODEV; - priv->client = pdata->clients + i; + /* Check if we can support this camera */ + csi2_flags = V4L2_MBUS_CSI2_CONTINUOUS_CLOCK | V4L2_MBUS_CSI2_1_LANE; + + switch (pdata->type) { + case SH_CSI2C: + if (pdata->clients[i].lanes != 1) + csi2_flags |= V4L2_MBUS_CSI2_2_LANE; + break; + case SH_CSI2I: + switch (pdata->clients[i].lanes) { + default: + csi2_flags |= V4L2_MBUS_CSI2_4_LANE; + case 3: + csi2_flags |= V4L2_MBUS_CSI2_3_LANE; + case 2: + csi2_flags |= V4L2_MBUS_CSI2_2_LANE; + } + } - priv->set_bus_param = icd->ops->set_bus_param; - priv->query_bus_param = icd->ops->query_bus_param; - icd->ops->set_bus_param = sh_csi2_set_bus_param; - icd->ops->query_bus_param = sh_csi2_query_bus_param; + cfg.type = V4L2_MBUS_CSI2; + ret = v4l2_subdev_call(client_sd, video, g_mbus_config, &cfg); + if (ret == -ENOIOCTLCMD) + common_flags = csi2_flags; + else if (!ret) + common_flags = soc_mbus_config_compatible(&cfg, + csi2_flags); + else + common_flags = 0; - csi2_sd->grp_id = (long)icd; + if (!common_flags) + return -EINVAL; + + /* All good: camera MIPI configuration supported */ + priv->mipi_flags = common_flags; + priv->client = pdata->clients + i; pm_runtime_get_sync(dev); @@ -223,16 +266,10 @@ static int sh_csi2_client_connect(struct sh_csi2 *priv) static void sh_csi2_client_disconnect(struct sh_csi2 *priv) { - struct soc_camera_device *icd = (struct soc_camera_device *)priv->subdev.grp_id; + if (!priv->client) + return; priv->client = NULL; - priv->subdev.grp_id = 0; - - /* Driver is about to be unbound */ - icd->ops->set_bus_param = priv->set_bus_param; - icd->ops->query_bus_param = priv->query_bus_param; - priv->set_bus_param = NULL; - priv->query_bus_param = NULL; pm_runtime_put(v4l2_get_subdevdata(&priv->subdev)); } diff --git a/drivers/media/video/sh_vou.c b/drivers/media/video/sh_vou.c index 6a729879d89..9644bd861ab 100644 --- a/drivers/media/video/sh_vou.c +++ b/drivers/media/video/sh_vou.c @@ -20,6 +20,7 @@ #include <linux/pm_runtime.h> #include <linux/slab.h> #include <linux/videodev2.h> +#include <linux/module.h> #include <media/sh_vou.h> #include <media/v4l2-common.h> diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index 5bdfe7e16bc..b72580c3895 100644 --- a/drivers/media/video/soc_camera.c +++ b/drivers/media/video/soc_camera.c @@ -50,49 +50,65 @@ static LIST_HEAD(hosts); static LIST_HEAD(devices); static DEFINE_MUTEX(list_lock); /* Protects the list of hosts */ -static int soc_camera_power_set(struct soc_camera_device *icd, - struct soc_camera_link *icl, - int power_on) +static int soc_camera_power_on(struct soc_camera_device *icd, + struct soc_camera_link *icl) { - int ret; - - if (power_on) { - ret = regulator_bulk_enable(icl->num_regulators, - icl->regulators); - if (ret < 0) { - dev_err(icd->pdev, "Cannot enable regulators\n"); - return ret; - } + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + int ret = regulator_bulk_enable(icl->num_regulators, + icl->regulators); + if (ret < 0) { + dev_err(icd->pdev, "Cannot enable regulators\n"); + return ret; + } - if (icl->power) - ret = icl->power(icd->pdev, power_on); + if (icl->power) { + ret = icl->power(icd->pdev, 1); if (ret < 0) { dev_err(icd->pdev, "Platform failed to power-on the camera.\n"); - - regulator_bulk_disable(icl->num_regulators, - icl->regulators); - return ret; + goto elinkpwr; } - } else { - ret = 0; - if (icl->power) - ret = icl->power(icd->pdev, 0); + } + + ret = v4l2_subdev_call(sd, core, s_power, 1); + if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) + goto esdpwr; + + return 0; + +esdpwr: + if (icl->power) + icl->power(icd->pdev, 0); +elinkpwr: + regulator_bulk_disable(icl->num_regulators, + icl->regulators); + return ret; +} + +static int soc_camera_power_off(struct soc_camera_device *icd, + struct soc_camera_link *icl) +{ + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + int ret = v4l2_subdev_call(sd, core, s_power, 0); + + if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) + return ret; + + if (icl->power) { + ret = icl->power(icd->pdev, 0); if (ret < 0) { dev_err(icd->pdev, "Platform failed to power-off the camera.\n"); return ret; } - - ret = regulator_bulk_disable(icl->num_regulators, - icl->regulators); - if (ret < 0) { - dev_err(icd->pdev, "Cannot disable regulators\n"); - return ret; - } } - return 0; + ret = regulator_bulk_disable(icl->num_regulators, + icl->regulators); + if (ret < 0) + dev_err(icd->pdev, "Cannot disable regulators\n"); + + return ret; } const struct soc_camera_format_xlate *soc_camera_xlate_by_fourcc( @@ -108,38 +124,38 @@ const struct soc_camera_format_xlate *soc_camera_xlate_by_fourcc( EXPORT_SYMBOL(soc_camera_xlate_by_fourcc); /** - * soc_camera_apply_sensor_flags() - apply platform SOCAM_SENSOR_INVERT_* flags + * soc_camera_apply_board_flags() - apply platform SOCAM_SENSOR_INVERT_* flags * @icl: camera platform parameters - * @flags: flags to be inverted according to platform configuration + * @cfg: media bus configuration * @return: resulting flags */ -unsigned long soc_camera_apply_sensor_flags(struct soc_camera_link *icl, - unsigned long flags) +unsigned long soc_camera_apply_board_flags(struct soc_camera_link *icl, + const struct v4l2_mbus_config *cfg) { - unsigned long f; + unsigned long f, flags = cfg->flags; /* If only one of the two polarities is supported, switch to the opposite */ if (icl->flags & SOCAM_SENSOR_INVERT_HSYNC) { - f = flags & (SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_LOW); - if (f == SOCAM_HSYNC_ACTIVE_HIGH || f == SOCAM_HSYNC_ACTIVE_LOW) - flags ^= SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_LOW; + f = flags & (V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW); + if (f == V4L2_MBUS_HSYNC_ACTIVE_HIGH || f == V4L2_MBUS_HSYNC_ACTIVE_LOW) + flags ^= V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW; } if (icl->flags & SOCAM_SENSOR_INVERT_VSYNC) { - f = flags & (SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW); - if (f == SOCAM_VSYNC_ACTIVE_HIGH || f == SOCAM_VSYNC_ACTIVE_LOW) - flags ^= SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW; + f = flags & (V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW); + if (f == V4L2_MBUS_VSYNC_ACTIVE_HIGH || f == V4L2_MBUS_VSYNC_ACTIVE_LOW) + flags ^= V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW; } if (icl->flags & SOCAM_SENSOR_INVERT_PCLK) { - f = flags & (SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING); - if (f == SOCAM_PCLK_SAMPLE_RISING || f == SOCAM_PCLK_SAMPLE_FALLING) - flags ^= SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING; + f = flags & (V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING); + if (f == V4L2_MBUS_PCLK_SAMPLE_RISING || f == V4L2_MBUS_PCLK_SAMPLE_FALLING) + flags ^= V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING; } return flags; } -EXPORT_SYMBOL(soc_camera_apply_sensor_flags); +EXPORT_SYMBOL(soc_camera_apply_board_flags); #define pixfmtstr(x) (x) & 0xff, ((x) >> 8) & 0xff, ((x) >> 16) & 0xff, \ ((x) >> 24) & 0xff @@ -233,6 +249,14 @@ static int soc_camera_s_std(struct file *file, void *priv, v4l2_std_id *a) return v4l2_subdev_call(sd, core, s_std, *a); } +static int soc_camera_g_std(struct file *file, void *priv, v4l2_std_id *a) +{ + struct soc_camera_device *icd = file->private_data; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + + return v4l2_subdev_call(sd, core, g_std, a); +} + static int soc_camera_enum_fsizes(struct file *file, void *fh, struct v4l2_frmsizeenum *fsize) { @@ -318,6 +342,32 @@ static int soc_camera_dqbuf(struct file *file, void *priv, return vb2_dqbuf(&icd->vb2_vidq, p, file->f_flags & O_NONBLOCK); } +static int soc_camera_create_bufs(struct file *file, void *priv, + struct v4l2_create_buffers *create) +{ + struct soc_camera_device *icd = file->private_data; + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + + /* videobuf2 only */ + if (ici->ops->init_videobuf) + return -EINVAL; + else + return vb2_create_bufs(&icd->vb2_vidq, create); +} + +static int soc_camera_prepare_buf(struct file *file, void *priv, + struct v4l2_buffer *b) +{ + struct soc_camera_device *icd = file->private_data; + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + + /* videobuf2 only */ + if (ici->ops->init_videobuf) + return -EINVAL; + else + return vb2_prepare_buf(&icd->vb2_vidq, b); +} + /* Always entered with .video_lock held */ static int soc_camera_init_user_formats(struct soc_camera_device *icd) { @@ -448,7 +498,7 @@ static int soc_camera_open(struct file *file) struct soc_camera_host *ici; int ret; - if (!icd->ops) + if (!to_soc_camera_control(icd)) /* No device driver attached */ return -ENODEV; @@ -476,7 +526,7 @@ static int soc_camera_open(struct file *file) }, }; - ret = soc_camera_power_set(icd, icl, 1); + ret = soc_camera_power_on(icd, icl); if (ret < 0) goto epower; @@ -512,6 +562,7 @@ static int soc_camera_open(struct file *file) if (ret < 0) goto einitvb; } + v4l2_ctrl_handler_setup(&icd->ctrl_handler); } file->private_data = icd; @@ -529,7 +580,7 @@ esfmt: eresume: ici->ops->remove(icd); eiciadd: - soc_camera_power_set(icd, icl, 0); + soc_camera_power_off(icd, icl); epower: icd->use_count--; module_put(ici->ops->owner); @@ -553,7 +604,7 @@ static int soc_camera_close(struct file *file) if (ici->ops->init_videobuf2) vb2_queue_release(&icd->vb2_vidq); - soc_camera_power_set(icd, icl, 0); + soc_camera_power_off(icd, icl); } if (icd->streamer == file) @@ -781,75 +832,6 @@ static int soc_camera_streamoff(struct file *file, void *priv, return 0; } -static int soc_camera_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) -{ - struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - int i; - - WARN_ON(priv != file->private_data); - - if (!qc->id) - return -EINVAL; - - /* First check host controls */ - for (i = 0; i < ici->ops->num_controls; i++) - if (qc->id == ici->ops->controls[i].id) { - memcpy(qc, &(ici->ops->controls[i]), - sizeof(*qc)); - return 0; - } - - /* Then device controls */ - for (i = 0; i < icd->ops->num_controls; i++) - if (qc->id == icd->ops->controls[i].id) { - memcpy(qc, &(icd->ops->controls[i]), - sizeof(*qc)); - return 0; - } - - return -EINVAL; -} - -static int soc_camera_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - int ret; - - WARN_ON(priv != file->private_data); - - if (ici->ops->get_ctrl) { - ret = ici->ops->get_ctrl(icd, ctrl); - if (ret != -ENOIOCTLCMD) - return ret; - } - - return v4l2_subdev_call(sd, core, g_ctrl, ctrl); -} - -static int soc_camera_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - int ret; - - WARN_ON(priv != file->private_data); - - if (ici->ops->set_ctrl) { - ret = ici->ops->set_ctrl(icd, ctrl); - if (ret != -ENOIOCTLCMD) - return ret; - } - - return v4l2_subdev_call(sd, core, s_ctrl, ctrl); -} - static int soc_camera_cropcap(struct file *file, void *fh, struct v4l2_cropcap *a) { @@ -1003,7 +985,7 @@ static int soc_camera_init_i2c(struct soc_camera_device *icd, goto ei2cga; } - icl->board_info->platform_data = icd; + icl->board_info->platform_data = icl; subdev = v4l2_i2c_new_subdev_board(&ici->v4l2_dev, adap, icl->board_info, NULL); @@ -1052,12 +1034,29 @@ static int soc_camera_probe(struct soc_camera_device *icd) dev_info(icd->pdev, "Probing %s\n", dev_name(icd->pdev)); + /* + * Currently the subdev with the largest number of controls (13) is + * ov6550. So let's pick 16 as a hint for the control handler. Note + * that this is a hint only: too large and you waste some memory, too + * small and there is a (very) small performance hit when looking up + * controls in the internal hash. + */ + ret = v4l2_ctrl_handler_init(&icd->ctrl_handler, 16); + if (ret < 0) + return ret; + ret = regulator_bulk_get(icd->pdev, icl->num_regulators, icl->regulators); if (ret < 0) goto ereg; - ret = soc_camera_power_set(icd, icl, 1); + /* + * This will not yet call v4l2_subdev_core_ops::s_power(1), because the + * subdevice has not been initialised yet. We'll have to call it once + * again after initialisation, even though it shouldn't be needed, we + * don't do any IO here. + */ + ret = soc_camera_power_on(icd, icl); if (ret < 0) goto epower; @@ -1098,6 +1097,7 @@ static int soc_camera_probe(struct soc_camera_device *icd) if (!control || !control->driver || !dev_get_drvdata(control) || !try_module_get(control->driver->owner)) { icl->del_device(icd); + ret = -ENODEV; goto enodrv; } } @@ -1105,6 +1105,9 @@ static int soc_camera_probe(struct soc_camera_device *icd) sd = soc_camera_to_subdev(icd); sd->grp_id = (long)icd; + if (v4l2_ctrl_add_handler(&icd->ctrl_handler, sd->ctrl_handler)) + goto ectrl; + /* At this point client .probe() should have run already */ ret = soc_camera_init_user_formats(icd); if (ret < 0) @@ -1123,6 +1126,10 @@ static int soc_camera_probe(struct soc_camera_device *icd) if (ret < 0) goto evidstart; + ret = v4l2_subdev_call(sd, core, s_power, 1); + if (ret < 0 && ret != -ENOIOCTLCMD) + goto esdpwr; + /* Try to improve our guess of a reasonable window format */ if (!v4l2_subdev_call(sd, video, g_mbus_fmt, &mf)) { icd->user_width = mf.width; @@ -1133,16 +1140,19 @@ static int soc_camera_probe(struct soc_camera_device *icd) ici->ops->remove(icd); - soc_camera_power_set(icd, icl, 0); + soc_camera_power_off(icd, icl); mutex_unlock(&icd->video_lock); return 0; +esdpwr: + video_unregister_device(icd->vdev); evidstart: mutex_unlock(&icd->video_lock); soc_camera_free_user_formats(icd); eiufmt: +ectrl: if (icl->board_info) { soc_camera_free_i2c(icd); } else { @@ -1152,13 +1162,15 @@ eiufmt: enodrv: eadddev: video_device_release(icd->vdev); + icd->vdev = NULL; evdc: ici->ops->remove(icd); eadd: - soc_camera_power_set(icd, icl, 0); + soc_camera_power_off(icd, icl); epower: regulator_bulk_free(icl->num_regulators, icl->regulators); ereg: + v4l2_ctrl_handler_free(&icd->ctrl_handler); return ret; } @@ -1173,6 +1185,7 @@ static int soc_camera_remove(struct soc_camera_device *icd) BUG_ON(!icd->parent); + v4l2_ctrl_handler_free(&icd->ctrl_handler); if (vdev) { video_unregister_device(vdev); icd->vdev = NULL; @@ -1363,24 +1376,24 @@ static int soc_camera_device_register(struct soc_camera_device *icd) static const struct v4l2_ioctl_ops soc_camera_ioctl_ops = { .vidioc_querycap = soc_camera_querycap, + .vidioc_try_fmt_vid_cap = soc_camera_try_fmt_vid_cap, .vidioc_g_fmt_vid_cap = soc_camera_g_fmt_vid_cap, - .vidioc_enum_fmt_vid_cap = soc_camera_enum_fmt_vid_cap, .vidioc_s_fmt_vid_cap = soc_camera_s_fmt_vid_cap, + .vidioc_enum_fmt_vid_cap = soc_camera_enum_fmt_vid_cap, .vidioc_enum_input = soc_camera_enum_input, .vidioc_g_input = soc_camera_g_input, .vidioc_s_input = soc_camera_s_input, .vidioc_s_std = soc_camera_s_std, + .vidioc_g_std = soc_camera_g_std, .vidioc_enum_framesizes = soc_camera_enum_fsizes, .vidioc_reqbufs = soc_camera_reqbufs, - .vidioc_try_fmt_vid_cap = soc_camera_try_fmt_vid_cap, .vidioc_querybuf = soc_camera_querybuf, .vidioc_qbuf = soc_camera_qbuf, .vidioc_dqbuf = soc_camera_dqbuf, + .vidioc_create_bufs = soc_camera_create_bufs, + .vidioc_prepare_buf = soc_camera_prepare_buf, .vidioc_streamon = soc_camera_streamon, .vidioc_streamoff = soc_camera_streamoff, - .vidioc_queryctrl = soc_camera_queryctrl, - .vidioc_g_ctrl = soc_camera_g_ctrl, - .vidioc_s_ctrl = soc_camera_s_ctrl, .vidioc_cropcap = soc_camera_cropcap, .vidioc_g_crop = soc_camera_g_crop, .vidioc_s_crop = soc_camera_s_crop, @@ -1409,6 +1422,7 @@ static int video_dev_create(struct soc_camera_device *icd) vdev->ioctl_ops = &soc_camera_ioctl_ops; vdev->release = video_device_release; vdev->tvnorms = V4L2_STD_UNKNOWN; + vdev->ctrl_handler = &icd->ctrl_handler; vdev->lock = &icd->video_lock; icd->vdev = vdev; @@ -1427,11 +1441,6 @@ static int soc_camera_video_start(struct soc_camera_device *icd) if (!icd->parent) return -ENODEV; - if (!icd->ops || - !icd->ops->query_bus_param || - !icd->ops->set_bus_param) - return -EINVAL; - ret = video_register_device(icd->vdev, VFL_TYPE_GRABBER, -1); if (ret < 0) { dev_err(icd->pdev, "video_register_device failed: %d\n", ret); diff --git a/drivers/media/video/soc_camera_platform.c b/drivers/media/video/soc_camera_platform.c index 8069cd6bc5e..4402a8a74f7 100644 --- a/drivers/media/video/soc_camera_platform.c +++ b/drivers/media/video/soc_camera_platform.c @@ -30,32 +30,12 @@ static struct soc_camera_platform_priv *get_priv(struct platform_device *pdev) return container_of(subdev, struct soc_camera_platform_priv, subdev); } -static struct soc_camera_platform_info *get_info(struct soc_camera_device *icd) -{ - struct platform_device *pdev = - to_platform_device(to_soc_camera_control(icd)); - return pdev->dev.platform_data; -} - static int soc_camera_platform_s_stream(struct v4l2_subdev *sd, int enable) { struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd); return p->set_capture(p, enable); } -static int soc_camera_platform_set_bus_param(struct soc_camera_device *icd, - unsigned long flags) -{ - return 0; -} - -static unsigned long -soc_camera_platform_query_bus_param(struct soc_camera_device *icd) -{ - struct soc_camera_platform_info *p = get_info(icd); - return p->bus_param; -} - static int soc_camera_platform_fill_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) { @@ -115,6 +95,17 @@ static int soc_camera_platform_cropcap(struct v4l2_subdev *sd, return 0; } +static int soc_camera_platform_g_mbus_config(struct v4l2_subdev *sd, + struct v4l2_mbus_config *cfg) +{ + struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd); + + cfg->flags = p->mbus_param; + cfg->type = p->mbus_type; + + return 0; +} + static struct v4l2_subdev_video_ops platform_subdev_video_ops = { .s_stream = soc_camera_platform_s_stream, .enum_mbus_fmt = soc_camera_platform_enum_fmt, @@ -123,6 +114,7 @@ static struct v4l2_subdev_video_ops platform_subdev_video_ops = { .try_mbus_fmt = soc_camera_platform_fill_fmt, .g_mbus_fmt = soc_camera_platform_fill_fmt, .s_mbus_fmt = soc_camera_platform_fill_fmt, + .g_mbus_config = soc_camera_platform_g_mbus_config, }; static struct v4l2_subdev_ops platform_subdev_ops = { @@ -130,11 +122,6 @@ static struct v4l2_subdev_ops platform_subdev_ops = { .video = &platform_subdev_video_ops, }; -static struct soc_camera_ops soc_camera_platform_ops = { - .set_bus_param = soc_camera_platform_set_bus_param, - .query_bus_param = soc_camera_platform_query_bus_param, -}; - static int soc_camera_platform_probe(struct platform_device *pdev) { struct soc_camera_host *ici; @@ -163,8 +150,6 @@ static int soc_camera_platform_probe(struct platform_device *pdev) /* Set the control device reference */ icd->control = &pdev->dev; - icd->ops = &soc_camera_platform_ops; - ici = to_soc_camera_host(icd->parent); v4l2_subdev_init(&priv->subdev, &platform_subdev_ops); @@ -178,7 +163,6 @@ static int soc_camera_platform_probe(struct platform_device *pdev) return ret; evdrs: - icd->ops = NULL; platform_set_drvdata(pdev, NULL); kfree(priv); return ret; @@ -187,11 +171,10 @@ evdrs: static int soc_camera_platform_remove(struct platform_device *pdev) { struct soc_camera_platform_priv *priv = get_priv(pdev); - struct soc_camera_platform_info *p = pdev->dev.platform_data; - struct soc_camera_device *icd = p->icd; + struct soc_camera_platform_info *p = v4l2_get_subdevdata(&priv->subdev); + p->icd->control = NULL; v4l2_device_unregister_subdev(&priv->subdev); - icd->ops = NULL; platform_set_drvdata(pdev, NULL); kfree(priv); return 0; diff --git a/drivers/media/video/soc_mediabus.c b/drivers/media/video/soc_mediabus.c index bea7c9cf4f8..cf7f2194ded 100644 --- a/drivers/media/video/soc_mediabus.c +++ b/drivers/media/video/soc_mediabus.c @@ -383,6 +383,39 @@ const struct soc_mbus_pixelfmt *soc_mbus_get_fmtdesc( } EXPORT_SYMBOL(soc_mbus_get_fmtdesc); +unsigned int soc_mbus_config_compatible(const struct v4l2_mbus_config *cfg, + unsigned int flags) +{ + unsigned long common_flags; + bool hsync = true, vsync = true, pclk, data, mode; + bool mipi_lanes, mipi_clock; + + common_flags = cfg->flags & flags; + + switch (cfg->type) { + case V4L2_MBUS_PARALLEL: + hsync = common_flags & (V4L2_MBUS_HSYNC_ACTIVE_HIGH | + V4L2_MBUS_HSYNC_ACTIVE_LOW); + vsync = common_flags & (V4L2_MBUS_VSYNC_ACTIVE_HIGH | + V4L2_MBUS_VSYNC_ACTIVE_LOW); + case V4L2_MBUS_BT656: + pclk = common_flags & (V4L2_MBUS_PCLK_SAMPLE_RISING | + V4L2_MBUS_PCLK_SAMPLE_FALLING); + data = common_flags & (V4L2_MBUS_DATA_ACTIVE_HIGH | + V4L2_MBUS_DATA_ACTIVE_LOW); + mode = common_flags & (V4L2_MBUS_MASTER | V4L2_MBUS_SLAVE); + return (!hsync || !vsync || !pclk || !data || !mode) ? + 0 : common_flags; + case V4L2_MBUS_CSI2: + mipi_lanes = common_flags & V4L2_MBUS_CSI2_LANES; + mipi_clock = common_flags & (V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK | + V4L2_MBUS_CSI2_CONTINUOUS_CLOCK); + return (!mipi_lanes || !mipi_clock) ? 0 : common_flags; + } + return 0; +} +EXPORT_SYMBOL(soc_mbus_config_compatible); + static int __init soc_mbus_init(void) { return 0; diff --git a/drivers/media/video/sr030pc30.c b/drivers/media/video/sr030pc30.c index 8afb0e8a2e0..d1b07aceaf9 100644 --- a/drivers/media/video/sr030pc30.c +++ b/drivers/media/video/sr030pc30.c @@ -19,6 +19,7 @@ #include <linux/i2c.h> #include <linux/delay.h> #include <linux/slab.h> +#include <linux/module.h> #include <media/v4l2-device.h> #include <media/v4l2-subdev.h> #include <media/v4l2-mediabus.h> @@ -714,11 +715,6 @@ static int sr030pc30_base_config(struct v4l2_subdev *sd) return ret; } -static int sr030pc30_s_stream(struct v4l2_subdev *sd, int enable) -{ - return 0; -} - static int sr030pc30_s_power(struct v4l2_subdev *sd, int on) { struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -764,7 +760,6 @@ static const struct v4l2_subdev_core_ops sr030pc30_core_ops = { }; static const struct v4l2_subdev_video_ops sr030pc30_video_ops = { - .s_stream = sr030pc30_s_stream, .g_mbus_fmt = sr030pc30_g_fmt, .s_mbus_fmt = sr030pc30_s_fmt, .try_mbus_fmt = sr030pc30_try_fmt, diff --git a/drivers/media/video/stk-webcam.c b/drivers/media/video/stk-webcam.c index d1a2cefbf55..cbc105f975d 100644 --- a/drivers/media/video/stk-webcam.c +++ b/drivers/media/video/stk-webcam.c @@ -55,6 +55,8 @@ MODULE_AUTHOR("Jaime Velasco Juan <jsagarribay@gmail.com> and Nicolas VIVIEN"); MODULE_DESCRIPTION("Syntek DC1125 webcam driver"); +/* bool for webcam LED management */ +int first_init = 1; /* Some cameras have audio interfaces, we aren't interested in those */ static struct usb_device_id stkwebcam_table[] = { @@ -518,7 +520,7 @@ static int stk_prepare_sio_buffers(struct stk_camera *dev, unsigned n_sbufs) return -ENOMEM; for (i = 0; i < n_sbufs; i++) { if (stk_setup_siobuf(dev, i)) - return (dev->n_sbufs > 1)? 0 : -ENOMEM; + return (dev->n_sbufs > 1 ? 0 : -ENOMEM); dev->n_sbufs = i+1; } } @@ -558,9 +560,14 @@ static int v4l_stk_open(struct file *fp) vdev = video_devdata(fp); dev = vdev_to_camera(vdev); - if (dev == NULL || !is_present(dev)) { + if (dev == NULL || !is_present(dev)) return -ENXIO; - } + + if (!first_init) + stk_camera_write_reg(dev, 0x0, 0x24); + else + first_init = 0; + fp->private_data = dev; usb_autopm_get_interface(dev->interface); @@ -574,10 +581,12 @@ static int v4l_stk_release(struct file *fp) if (dev->owner == fp) { stk_stop_stream(dev); stk_free_buffers(dev); + stk_camera_write_reg(dev, 0x0, 0x49); /* turn off the LED */ + unset_initialised(dev); dev->owner = NULL; } - if(is_present(dev)) + if (is_present(dev)) usb_autopm_put_interface(dev->interface); return 0; @@ -654,7 +663,7 @@ static unsigned int v4l_stk_poll(struct file *fp, poll_table *wait) return POLLERR; if (!list_empty(&dev->sio_full)) - return (POLLIN | POLLRDNORM); + return POLLIN | POLLRDNORM; return 0; } @@ -891,9 +900,9 @@ static int stk_vidioc_g_fmt_vid_cap(struct file *filp, struct stk_camera *dev = priv; int i; - for (i = 0; i < ARRAY_SIZE(stk_sizes) - && stk_sizes[i].m != dev->vsettings.mode; - i++); + for (i = 0; i < ARRAY_SIZE(stk_sizes) && + stk_sizes[i].m != dev->vsettings.mode; i++) + ; if (i == ARRAY_SIZE(stk_sizes)) { STK_ERROR("ERROR: mode invalid\n"); return -EINVAL; @@ -1305,9 +1314,8 @@ static int stk_camera_probe(struct usb_interface *interface, usb_set_intfdata(interface, dev); err = stk_register_video_device(dev); - if (err) { + if (err) goto error; - } return 0; @@ -1350,6 +1358,7 @@ static int stk_camera_resume(struct usb_interface *intf) return 0; unset_initialised(dev); stk_initialise(dev); + stk_camera_write_reg(dev, 0x0, 0x49); stk_setup_format(dev); if (is_streaming(dev)) stk_start_stream(dev); diff --git a/drivers/media/video/tcm825x.c b/drivers/media/video/tcm825x.c index b6ee1bd342d..462caa44ae0 100644 --- a/drivers/media/video/tcm825x.c +++ b/drivers/media/video/tcm825x.c @@ -27,6 +27,7 @@ */ #include <linux/i2c.h> +#include <linux/module.h> #include <media/v4l2-int-device.h> #include "tcm825x.h" diff --git a/drivers/media/video/timblogiw.c b/drivers/media/video/timblogiw.c index 84cd1b65b76..a0895bf0748 100644 --- a/drivers/media/video/timblogiw.c +++ b/drivers/media/video/timblogiw.c @@ -27,6 +27,7 @@ #include <linux/interrupt.h> #include <linux/list.h> #include <linux/i2c.h> +#include <linux/module.h> #include <media/v4l2-ioctl.h> #include <media/v4l2-device.h> #include <media/videobuf-dma-contig.h> diff --git a/drivers/media/video/tlg2300/Makefile b/drivers/media/video/tlg2300/Makefile index 81bb7fdd1e3..ea09b9af2d3 100644 --- a/drivers/media/video/tlg2300/Makefile +++ b/drivers/media/video/tlg2300/Makefile @@ -2,8 +2,8 @@ poseidon-objs := pd-video.o pd-alsa.o pd-dvb.o pd-radio.o pd-main.o obj-$(CONFIG_VIDEO_TLG2300) += poseidon.o -EXTRA_CFLAGS += -Idrivers/media/video -EXTRA_CFLAGS += -Idrivers/media/common/tuners -EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core -EXTRA_CFLAGS += -Idrivers/media/dvb/frontends +ccflags-y += -Idrivers/media/video +ccflags-y += -Idrivers/media/common/tuners +ccflags-y += -Idrivers/media/dvb/dvb-core +ccflags-y += -Idrivers/media/dvb/frontends diff --git a/drivers/media/video/tm6000/Kconfig b/drivers/media/video/tm6000/Kconfig new file mode 100644 index 00000000000..114eec8a630 --- /dev/null +++ b/drivers/media/video/tm6000/Kconfig @@ -0,0 +1,33 @@ +config VIDEO_TM6000 + tristate "TV Master TM5600/6000/6010 driver" + depends on VIDEO_DEV && I2C && INPUT && RC_CORE && USB && EXPERIMENTAL + select VIDEO_TUNER + select MEDIA_TUNER_XC2028 + select MEDIA_TUNER_XC5000 + select VIDEOBUF_VMALLOC + help + Support for TM5600/TM6000/TM6010 USB Device + + Since these cards have no MPEG decoder onboard, they transmit + only compressed MPEG data over the usb bus, so you need + an external software decoder to watch TV on your computer. + + Say Y if you own such a device and want to use it. + +config VIDEO_TM6000_ALSA + tristate "TV Master TM5600/6000/6010 audio support" + depends on VIDEO_TM6000 && SND && EXPERIMENTAL + select SND_PCM + ---help--- + This is a video4linux driver for direct (DMA) audio for + TM5600/TM6000/TM6010 USB Devices. + + To compile this driver as a module, choose M here: the + module will be called tm6000-alsa. + +config VIDEO_TM6000_DVB + tristate "DVB Support for tm6000 based TV cards" + depends on VIDEO_TM6000 && DVB_CORE && USB && EXPERIMENTAL + select DVB_ZL10353 + ---help--- + This adds support for DVB cards based on the tm5600/tm6000 chip. diff --git a/drivers/media/video/tm6000/Makefile b/drivers/media/video/tm6000/Makefile new file mode 100644 index 00000000000..395515b4a88 --- /dev/null +++ b/drivers/media/video/tm6000/Makefile @@ -0,0 +1,15 @@ +tm6000-y := tm6000-cards.o \ + tm6000-core.o \ + tm6000-i2c.o \ + tm6000-video.o \ + tm6000-stds.o \ + tm6000-input.o + +obj-$(CONFIG_VIDEO_TM6000) += tm6000.o +obj-$(CONFIG_VIDEO_TM6000_ALSA) += tm6000-alsa.o +obj-$(CONFIG_VIDEO_TM6000_DVB) += tm6000-dvb.o + +ccflags-y := -Idrivers/media/video +ccflags-y += -Idrivers/media/common/tuners +ccflags-y += -Idrivers/media/dvb/dvb-core +ccflags-y += -Idrivers/media/dvb/frontends diff --git a/drivers/media/video/tm6000/tm6000-alsa.c b/drivers/media/video/tm6000/tm6000-alsa.c new file mode 100644 index 00000000000..7d675c72fd4 --- /dev/null +++ b/drivers/media/video/tm6000/tm6000-alsa.c @@ -0,0 +1,513 @@ +/* + * + * Support for audio capture for tm5600/6000/6010 + * (c) 2007-2008 Mauro Carvalho Chehab <mchehab@redhat.com> + * + * Based on cx88-alsa.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/usb.h> +#include <linux/slab.h> +#include <linux/vmalloc.h> + +#include <linux/delay.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/control.h> +#include <sound/initval.h> + + +#include "tm6000.h" +#include "tm6000-regs.h" + +#undef dprintk + +#define dprintk(level, fmt, arg...) do { \ + if (debug >= level) \ + printk(KERN_INFO "%s/1: " fmt, chip->core->name , ## arg); \ + } while (0) + +/**************************************************************************** + Module global static vars + ****************************************************************************/ + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ + +static int enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 1}; + +module_param_array(enable, bool, NULL, 0444); +MODULE_PARM_DESC(enable, "Enable tm6000x soundcard. default enabled."); + +module_param_array(index, int, NULL, 0444); +MODULE_PARM_DESC(index, "Index value for tm6000x capture interface(s)."); + + +/**************************************************************************** + Module macros + ****************************************************************************/ + +MODULE_DESCRIPTION("ALSA driver module for tm5600/tm6000/tm6010 based TV cards"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("{{Trident,tm5600}," + "{{Trident,tm6000}," + "{{Trident,tm6010}"); +static unsigned int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "enable debug messages"); + +/**************************************************************************** + Module specific funtions + ****************************************************************************/ + +/* + * BOARD Specific: Sets audio DMA + */ + +static int _tm6000_start_audio_dma(struct snd_tm6000_card *chip) +{ + struct tm6000_core *core = chip->core; + + dprintk(1, "Starting audio DMA\n"); + + /* Enables audio */ + tm6000_set_reg_mask(core, TM6010_REQ07_RCC_ACTIVE_IF, 0x40, 0x40); + + tm6000_set_audio_bitrate(core, 48000); + + return 0; +} + +/* + * BOARD Specific: Resets audio DMA + */ +static int _tm6000_stop_audio_dma(struct snd_tm6000_card *chip) +{ + struct tm6000_core *core = chip->core; + + dprintk(1, "Stopping audio DMA\n"); + + /* Disables audio */ + tm6000_set_reg_mask(core, TM6010_REQ07_RCC_ACTIVE_IF, 0x00, 0x40); + + return 0; +} + +static void dsp_buffer_free(struct snd_pcm_substream *substream) +{ + struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); + + dprintk(2, "Freeing buffer\n"); + + vfree(substream->runtime->dma_area); + substream->runtime->dma_area = NULL; + substream->runtime->dma_bytes = 0; +} + +static int dsp_buffer_alloc(struct snd_pcm_substream *substream, int size) +{ + struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); + + dprintk(2, "Allocating buffer\n"); + + if (substream->runtime->dma_area) { + if (substream->runtime->dma_bytes > size) + return 0; + + dsp_buffer_free(substream); + } + + substream->runtime->dma_area = vmalloc(size); + if (!substream->runtime->dma_area) + return -ENOMEM; + + substream->runtime->dma_bytes = size; + + return 0; +} + + +/**************************************************************************** + ALSA PCM Interface + ****************************************************************************/ + +/* + * Digital hardware definition + */ +#define DEFAULT_FIFO_SIZE 4096 + +static struct snd_pcm_hardware snd_tm6000_digital_hw = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .period_bytes_min = 64, + .period_bytes_max = 12544, + .periods_min = 1, + .periods_max = 98, + .buffer_bytes_max = 62720 * 8, +}; + +/* + * audio pcm capture open callback + */ +static int snd_tm6000_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + int err; + + err = snd_pcm_hw_constraint_pow2(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIODS); + if (err < 0) + goto _error; + + chip->substream = substream; + + runtime->hw = snd_tm6000_digital_hw; + + return 0; +_error: + dprintk(1, "Error opening PCM!\n"); + return err; +} + +/* + * audio close callback + */ +static int snd_tm6000_close(struct snd_pcm_substream *substream) +{ + struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); + struct tm6000_core *core = chip->core; + + if (atomic_read(&core->stream_started) > 0) { + atomic_set(&core->stream_started, 0); + schedule_work(&core->wq_trigger); + } + + return 0; +} + +static int tm6000_fillbuf(struct tm6000_core *core, char *buf, int size) +{ + struct snd_tm6000_card *chip = core->adev; + struct snd_pcm_substream *substream = chip->substream; + struct snd_pcm_runtime *runtime; + int period_elapsed = 0; + unsigned int stride, buf_pos; + int length; + + if (atomic_read(&core->stream_started) == 0) + return 0; + + if (!size || !substream) { + dprintk(1, "substream was NULL\n"); + return -EINVAL; + } + + runtime = substream->runtime; + if (!runtime || !runtime->dma_area) { + dprintk(1, "runtime was NULL\n"); + return -EINVAL; + } + + buf_pos = chip->buf_pos; + stride = runtime->frame_bits >> 3; + + if (stride == 0) { + dprintk(1, "stride is zero\n"); + return -EINVAL; + } + + length = size / stride; + if (length == 0) { + dprintk(1, "%s: length was zero\n", __func__); + return -EINVAL; + } + + dprintk(1, "Copying %d bytes at %p[%d] - buf size=%d x %d\n", size, + runtime->dma_area, buf_pos, + (unsigned int)runtime->buffer_size, stride); + + if (buf_pos + length >= runtime->buffer_size) { + unsigned int cnt = runtime->buffer_size - buf_pos; + memcpy(runtime->dma_area + buf_pos * stride, buf, cnt * stride); + memcpy(runtime->dma_area, buf + cnt * stride, + length * stride - cnt * stride); + } else + memcpy(runtime->dma_area + buf_pos * stride, buf, + length * stride); + + snd_pcm_stream_lock(substream); + + chip->buf_pos += length; + if (chip->buf_pos >= runtime->buffer_size) + chip->buf_pos -= runtime->buffer_size; + + chip->period_pos += length; + if (chip->period_pos >= runtime->period_size) { + chip->period_pos -= runtime->period_size; + period_elapsed = 1; + } + + snd_pcm_stream_unlock(substream); + + if (period_elapsed) + snd_pcm_period_elapsed(substream); + + return 0; +} + +/* + * hw_params callback + */ +static int snd_tm6000_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + int size, rc; + + size = params_period_bytes(hw_params) * params_periods(hw_params); + + rc = dsp_buffer_alloc(substream, size); + if (rc < 0) + return rc; + + return 0; +} + +/* + * hw free callback + */ +static int snd_tm6000_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); + struct tm6000_core *core = chip->core; + + if (atomic_read(&core->stream_started) > 0) { + atomic_set(&core->stream_started, 0); + schedule_work(&core->wq_trigger); + } + + dsp_buffer_free(substream); + return 0; +} + +/* + * prepare callback + */ +static int snd_tm6000_prepare(struct snd_pcm_substream *substream) +{ + struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); + + chip->buf_pos = 0; + chip->period_pos = 0; + + return 0; +} + + +/* + * trigger callback + */ +static void audio_trigger(struct work_struct *work) +{ + struct tm6000_core *core = container_of(work, struct tm6000_core, + wq_trigger); + struct snd_tm6000_card *chip = core->adev; + + if (atomic_read(&core->stream_started)) { + dprintk(1, "starting capture"); + _tm6000_start_audio_dma(chip); + } else { + dprintk(1, "stopping capture"); + _tm6000_stop_audio_dma(chip); + } +} + +static int snd_tm6000_card_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); + struct tm6000_core *core = chip->core; + int err = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + atomic_set(&core->stream_started, 1); + break; + case SNDRV_PCM_TRIGGER_STOP: + atomic_set(&core->stream_started, 0); + break; + default: + err = -EINVAL; + break; + } + schedule_work(&core->wq_trigger); + + return err; +} +/* + * pointer callback + */ +static snd_pcm_uframes_t snd_tm6000_pointer(struct snd_pcm_substream *substream) +{ + struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); + + return chip->buf_pos; +} + +/* + * operators + */ +static struct snd_pcm_ops snd_tm6000_pcm_ops = { + .open = snd_tm6000_pcm_open, + .close = snd_tm6000_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_tm6000_hw_params, + .hw_free = snd_tm6000_hw_free, + .prepare = snd_tm6000_prepare, + .trigger = snd_tm6000_card_trigger, + .pointer = snd_tm6000_pointer, +}; + +/* + * create a PCM device + */ + +/* FIXME: Control interface - How to control volume/mute? */ + +/**************************************************************************** + Basic Flow for Sound Devices + ****************************************************************************/ + +/* + * Alsa Constructor - Component probe + */ +static int tm6000_audio_init(struct tm6000_core *dev) +{ + struct snd_card *card; + struct snd_tm6000_card *chip; + int rc; + static int devnr; + char component[14]; + struct snd_pcm *pcm; + + if (!dev) + return 0; + + if (devnr >= SNDRV_CARDS) + return -ENODEV; + + if (!enable[devnr]) + return -ENOENT; + + rc = snd_card_create(index[devnr], "tm6000", THIS_MODULE, 0, &card); + if (rc < 0) { + snd_printk(KERN_ERR "cannot create card instance %d\n", devnr); + return rc; + } + strcpy(card->driver, "tm6000-alsa"); + strcpy(card->shortname, "TM5600/60x0"); + sprintf(card->longname, "TM5600/60x0 Audio at bus %d device %d", + dev->udev->bus->busnum, dev->udev->devnum); + + sprintf(component, "USB%04x:%04x", + le16_to_cpu(dev->udev->descriptor.idVendor), + le16_to_cpu(dev->udev->descriptor.idProduct)); + snd_component_add(card, component); + snd_card_set_dev(card, &dev->udev->dev); + + chip = kzalloc(sizeof(struct snd_tm6000_card), GFP_KERNEL); + if (!chip) { + rc = -ENOMEM; + goto error; + } + + chip->core = dev; + chip->card = card; + dev->adev = chip; + spin_lock_init(&chip->reg_lock); + + rc = snd_pcm_new(card, "TM6000 Audio", 0, 0, 1, &pcm); + if (rc < 0) + goto error_chip; + + pcm->info_flags = 0; + pcm->private_data = chip; + strcpy(pcm->name, "Trident TM5600/60x0"); + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_tm6000_pcm_ops); + + INIT_WORK(&dev->wq_trigger, audio_trigger); + rc = snd_card_register(card); + if (rc < 0) + goto error_chip; + + dprintk(1, "Registered audio driver for %s\n", card->longname); + + return 0; + +error_chip: + kfree(chip); + dev->adev = NULL; +error: + snd_card_free(card); + return rc; +} + +static int tm6000_audio_fini(struct tm6000_core *dev) +{ + struct snd_tm6000_card *chip = dev->adev; + + if (!dev) + return 0; + + if (!chip) + return 0; + + if (!chip->card) + return 0; + + snd_card_free(chip->card); + chip->card = NULL; + kfree(chip); + dev->adev = NULL; + + return 0; +} + +static struct tm6000_ops audio_ops = { + .type = TM6000_AUDIO, + .name = "TM6000 Audio Extension", + .init = tm6000_audio_init, + .fini = tm6000_audio_fini, + .fillbuf = tm6000_fillbuf, +}; + +static int __init tm6000_alsa_register(void) +{ + return tm6000_register_extension(&audio_ops); +} + +static void __exit tm6000_alsa_unregister(void) +{ + tm6000_unregister_extension(&audio_ops); +} + +module_init(tm6000_alsa_register); +module_exit(tm6000_alsa_unregister); diff --git a/drivers/media/video/tm6000/tm6000-cards.c b/drivers/media/video/tm6000/tm6000-cards.c new file mode 100644 index 00000000000..ec2578a0fdf --- /dev/null +++ b/drivers/media/video/tm6000/tm6000-cards.c @@ -0,0 +1,1402 @@ +/* + * tm6000-cards.c - driver for TM5600/TM6000/TM6010 USB video capture devices + * + * Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org> + * + * 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 version 2 + * + * 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 <linux/init.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/usb.h> +#include <linux/slab.h> +#include <media/v4l2-common.h> +#include <media/tuner.h> +#include <media/tvaudio.h> +#include <media/i2c-addr.h> +#include <media/rc-map.h> + +#include "tm6000.h" +#include "tm6000-regs.h" +#include "tuner-xc2028.h" +#include "xc5000.h" + +#define TM6000_BOARD_UNKNOWN 0 +#define TM5600_BOARD_GENERIC 1 +#define TM6000_BOARD_GENERIC 2 +#define TM6010_BOARD_GENERIC 3 +#define TM5600_BOARD_10MOONS_UT821 4 +#define TM5600_BOARD_10MOONS_UT330 5 +#define TM6000_BOARD_ADSTECH_DUAL_TV 6 +#define TM6000_BOARD_FREECOM_AND_SIMILAR 7 +#define TM6000_BOARD_ADSTECH_MINI_DUAL_TV 8 +#define TM6010_BOARD_HAUPPAUGE_900H 9 +#define TM6010_BOARD_BEHOLD_WANDER 10 +#define TM6010_BOARD_BEHOLD_VOYAGER 11 +#define TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE 12 +#define TM6010_BOARD_TWINHAN_TU501 13 +#define TM6010_BOARD_BEHOLD_WANDER_LITE 14 +#define TM6010_BOARD_BEHOLD_VOYAGER_LITE 15 +#define TM5600_BOARD_TERRATEC_GRABSTER 16 + +#define is_generic(model) ((model == TM6000_BOARD_UNKNOWN) || \ + (model == TM5600_BOARD_GENERIC) || \ + (model == TM6000_BOARD_GENERIC) || \ + (model == TM6010_BOARD_GENERIC)) + +#define TM6000_MAXBOARDS 16 +static unsigned int card[] = {[0 ... (TM6000_MAXBOARDS - 1)] = UNSET }; + +module_param_array(card, int, NULL, 0444); + +static unsigned long tm6000_devused; + + +struct tm6000_board { + char *name; + char eename[16]; /* EEPROM name */ + unsigned eename_size; /* size of EEPROM name */ + unsigned eename_pos; /* Position where it appears at ROM */ + + struct tm6000_capabilities caps; + + enum tm6000_devtype type; /* variant of the chipset */ + int tuner_type; /* type of the tuner */ + int tuner_addr; /* tuner address */ + int demod_addr; /* demodulator address */ + + struct tm6000_gpio gpio; + + struct tm6000_input vinput[3]; + struct tm6000_input rinput; + + char *ir_codes; +}; + +static struct tm6000_board tm6000_boards[] = { + [TM6000_BOARD_UNKNOWN] = { + .name = "Unknown tm6000 video grabber", + .caps = { + .has_tuner = 1, + .has_eeprom = 1, + }, + .gpio = { + .tuner_reset = TM6000_GPIO_1, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_ADC1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + }, + [TM5600_BOARD_GENERIC] = { + .name = "Generic tm5600 board", + .type = TM5600, + .tuner_type = TUNER_XC2028, + .tuner_addr = 0xc2 >> 1, + .caps = { + .has_tuner = 1, + .has_eeprom = 1, + }, + .gpio = { + .tuner_reset = TM6000_GPIO_1, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_ADC1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + }, + [TM6000_BOARD_GENERIC] = { + .name = "Generic tm6000 board", + .tuner_type = TUNER_XC2028, + .tuner_addr = 0xc2 >> 1, + .caps = { + .has_tuner = 1, + .has_eeprom = 1, + }, + .gpio = { + .tuner_reset = TM6000_GPIO_1, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_ADC1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + }, + [TM6010_BOARD_GENERIC] = { + .name = "Generic tm6010 board", + .type = TM6010, + .tuner_type = TUNER_XC2028, + .tuner_addr = 0xc2 >> 1, + .demod_addr = 0x1e >> 1, + .caps = { + .has_tuner = 1, + .has_dvb = 1, + .has_zl10353 = 1, + .has_eeprom = 1, + .has_remote = 1, + }, + .gpio = { + .tuner_reset = TM6010_GPIO_2, + .tuner_on = TM6010_GPIO_3, + .demod_reset = TM6010_GPIO_1, + .demod_on = TM6010_GPIO_4, + .power_led = TM6010_GPIO_7, + .dvb_led = TM6010_GPIO_5, + .ir = TM6010_GPIO_0, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_SIF1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + }, + [TM5600_BOARD_10MOONS_UT821] = { + .name = "10Moons UT 821", + .tuner_type = TUNER_XC2028, + .eename = { '1', '0', 'M', 'O', 'O', 'N', 'S', '5', '6', '0', '0', 0xff, 0x45, 0x5b}, + .eename_size = 14, + .eename_pos = 0x14, + .type = TM5600, + .tuner_addr = 0xc2 >> 1, + .caps = { + .has_tuner = 1, + .has_eeprom = 1, + }, + .gpio = { + .tuner_reset = TM6000_GPIO_1, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_ADC1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + }, + [TM5600_BOARD_10MOONS_UT330] = { + .name = "10Moons UT 330", + .tuner_type = TUNER_PHILIPS_FQ1216AME_MK4, + .tuner_addr = 0xc8 >> 1, + .caps = { + .has_tuner = 1, + .has_dvb = 0, + .has_zl10353 = 0, + .has_eeprom = 1, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_ADC1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + }, + [TM6000_BOARD_ADSTECH_DUAL_TV] = { + .name = "ADSTECH Dual TV USB", + .tuner_type = TUNER_XC2028, + .tuner_addr = 0xc8 >> 1, + .caps = { + .has_tuner = 1, + .has_tda9874 = 1, + .has_dvb = 1, + .has_zl10353 = 1, + .has_eeprom = 1, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_ADC1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + }, + [TM6000_BOARD_FREECOM_AND_SIMILAR] = { + .name = "Freecom Hybrid Stick / Moka DVB-T Receiver Dual", + .tuner_type = TUNER_XC2028, /* has a XC3028 */ + .tuner_addr = 0xc2 >> 1, + .demod_addr = 0x1e >> 1, + .caps = { + .has_tuner = 1, + .has_dvb = 1, + .has_zl10353 = 1, + .has_eeprom = 0, + .has_remote = 1, + }, + .gpio = { + .tuner_reset = TM6000_GPIO_4, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_ADC1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + }, + [TM6000_BOARD_ADSTECH_MINI_DUAL_TV] = { + .name = "ADSTECH Mini Dual TV USB", + .tuner_type = TUNER_XC2028, /* has a XC3028 */ + .tuner_addr = 0xc8 >> 1, + .demod_addr = 0x1e >> 1, + .caps = { + .has_tuner = 1, + .has_dvb = 1, + .has_zl10353 = 1, + .has_eeprom = 0, + }, + .gpio = { + .tuner_reset = TM6000_GPIO_4, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_ADC1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + }, + [TM6010_BOARD_HAUPPAUGE_900H] = { + .name = "Hauppauge WinTV HVR-900H / WinTV USB2-Stick", + .eename = { 'H', 0, 'V', 0, 'R', 0, '9', 0, '0', 0, '0', 0, 'H', 0 }, + .eename_size = 14, + .eename_pos = 0x42, + .tuner_type = TUNER_XC2028, /* has a XC3028 */ + .tuner_addr = 0xc2 >> 1, + .demod_addr = 0x1e >> 1, + .type = TM6010, + .caps = { + .has_tuner = 1, + .has_dvb = 1, + .has_zl10353 = 1, + .has_eeprom = 1, + .has_remote = 1, + }, + .gpio = { + .tuner_reset = TM6010_GPIO_2, + .tuner_on = TM6010_GPIO_3, + .demod_reset = TM6010_GPIO_1, + .demod_on = TM6010_GPIO_4, + .power_led = TM6010_GPIO_7, + .dvb_led = TM6010_GPIO_5, + .ir = TM6010_GPIO_0, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_SIF1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + }, + [TM6010_BOARD_BEHOLD_WANDER] = { + .name = "Beholder Wander DVB-T/TV/FM USB2.0", + .tuner_type = TUNER_XC5000, + .tuner_addr = 0xc2 >> 1, + .demod_addr = 0x1e >> 1, + .type = TM6010, + .caps = { + .has_tuner = 1, + .has_dvb = 1, + .has_zl10353 = 1, + .has_eeprom = 1, + .has_remote = 1, + .has_radio = 1, + }, + .gpio = { + .tuner_reset = TM6010_GPIO_0, + .demod_reset = TM6010_GPIO_1, + .power_led = TM6010_GPIO_6, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_SIF1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + .rinput = { + .type = TM6000_INPUT_RADIO, + .amux = TM6000_AMUX_ADC1, + }, + }, + [TM6010_BOARD_BEHOLD_VOYAGER] = { + .name = "Beholder Voyager TV/FM USB2.0", + .tuner_type = TUNER_XC5000, + .tuner_addr = 0xc2 >> 1, + .type = TM6010, + .caps = { + .has_tuner = 1, + .has_dvb = 0, + .has_zl10353 = 0, + .has_eeprom = 1, + .has_remote = 1, + .has_radio = 1, + }, + .gpio = { + .tuner_reset = TM6010_GPIO_0, + .power_led = TM6010_GPIO_6, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_SIF1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + .rinput = { + .type = TM6000_INPUT_RADIO, + .amux = TM6000_AMUX_ADC1, + }, + }, + [TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE] = { + .name = "Terratec Cinergy Hybrid XE / Cinergy Hybrid-Stick", + .tuner_type = TUNER_XC2028, /* has a XC3028 */ + .tuner_addr = 0xc2 >> 1, + .demod_addr = 0x1e >> 1, + .type = TM6010, + .caps = { + .has_tuner = 1, + .has_dvb = 1, + .has_zl10353 = 1, + .has_eeprom = 1, + .has_remote = 1, + .has_radio = 1, + }, + .gpio = { + .tuner_reset = TM6010_GPIO_2, + .tuner_on = TM6010_GPIO_3, + .demod_reset = TM6010_GPIO_1, + .demod_on = TM6010_GPIO_4, + .power_led = TM6010_GPIO_7, + .dvb_led = TM6010_GPIO_5, + .ir = TM6010_GPIO_0, + }, + .ir_codes = RC_MAP_NEC_TERRATEC_CINERGY_XS, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_SIF1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + .rinput = { + .type = TM6000_INPUT_RADIO, + .amux = TM6000_AMUX_SIF1, + }, + }, + [TM5600_BOARD_TERRATEC_GRABSTER] = { + .name = "Terratec Grabster AV 150/250 MX", + .type = TM5600, + .tuner_type = TUNER_ABSENT, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_ADC1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + }, + [TM6010_BOARD_TWINHAN_TU501] = { + .name = "Twinhan TU501(704D1)", + .tuner_type = TUNER_XC2028, /* has a XC3028 */ + .tuner_addr = 0xc2 >> 1, + .demod_addr = 0x1e >> 1, + .type = TM6010, + .caps = { + .has_tuner = 1, + .has_dvb = 1, + .has_zl10353 = 1, + .has_eeprom = 1, + .has_remote = 1, + }, + .gpio = { + .tuner_reset = TM6010_GPIO_2, + .tuner_on = TM6010_GPIO_3, + .demod_reset = TM6010_GPIO_1, + .demod_on = TM6010_GPIO_4, + .power_led = TM6010_GPIO_7, + .dvb_led = TM6010_GPIO_5, + .ir = TM6010_GPIO_0, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_SIF1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + }, + [TM6010_BOARD_BEHOLD_WANDER_LITE] = { + .name = "Beholder Wander Lite DVB-T/TV/FM USB2.0", + .tuner_type = TUNER_XC5000, + .tuner_addr = 0xc2 >> 1, + .demod_addr = 0x1e >> 1, + .type = TM6010, + .caps = { + .has_tuner = 1, + .has_dvb = 1, + .has_zl10353 = 1, + .has_eeprom = 1, + .has_remote = 0, + .has_radio = 1, + }, + .gpio = { + .tuner_reset = TM6010_GPIO_0, + .demod_reset = TM6010_GPIO_1, + .power_led = TM6010_GPIO_6, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_SIF1, + }, + }, + .rinput = { + .type = TM6000_INPUT_RADIO, + .amux = TM6000_AMUX_ADC1, + }, + }, + [TM6010_BOARD_BEHOLD_VOYAGER_LITE] = { + .name = "Beholder Voyager Lite TV/FM USB2.0", + .tuner_type = TUNER_XC5000, + .tuner_addr = 0xc2 >> 1, + .type = TM6010, + .caps = { + .has_tuner = 1, + .has_dvb = 0, + .has_zl10353 = 0, + .has_eeprom = 1, + .has_remote = 0, + .has_radio = 1, + }, + .gpio = { + .tuner_reset = TM6010_GPIO_0, + .power_led = TM6010_GPIO_6, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_SIF1, + }, + }, + .rinput = { + .type = TM6000_INPUT_RADIO, + .amux = TM6000_AMUX_ADC1, + }, + }, +}; + +/* table of devices that work with this driver */ +static struct usb_device_id tm6000_id_table[] = { + { USB_DEVICE(0x6000, 0x0001), .driver_info = TM5600_BOARD_GENERIC }, + { USB_DEVICE(0x6000, 0x0002), .driver_info = TM6010_BOARD_GENERIC }, + { USB_DEVICE(0x06e1, 0xf332), .driver_info = TM6000_BOARD_ADSTECH_DUAL_TV }, + { USB_DEVICE(0x14aa, 0x0620), .driver_info = TM6000_BOARD_FREECOM_AND_SIMILAR }, + { USB_DEVICE(0x06e1, 0xb339), .driver_info = TM6000_BOARD_ADSTECH_MINI_DUAL_TV }, + { USB_DEVICE(0x2040, 0x6600), .driver_info = TM6010_BOARD_HAUPPAUGE_900H }, + { USB_DEVICE(0x2040, 0x6601), .driver_info = TM6010_BOARD_HAUPPAUGE_900H }, + { USB_DEVICE(0x2040, 0x6610), .driver_info = TM6010_BOARD_HAUPPAUGE_900H }, + { USB_DEVICE(0x2040, 0x6611), .driver_info = TM6010_BOARD_HAUPPAUGE_900H }, + { USB_DEVICE(0x6000, 0xdec0), .driver_info = TM6010_BOARD_BEHOLD_WANDER }, + { USB_DEVICE(0x6000, 0xdec1), .driver_info = TM6010_BOARD_BEHOLD_VOYAGER }, + { USB_DEVICE(0x0ccd, 0x0086), .driver_info = TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE }, + { USB_DEVICE(0x0ccd, 0x00A5), .driver_info = TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE }, + { USB_DEVICE(0x0ccd, 0x0079), .driver_info = TM5600_BOARD_TERRATEC_GRABSTER }, + { USB_DEVICE(0x13d3, 0x3240), .driver_info = TM6010_BOARD_TWINHAN_TU501 }, + { USB_DEVICE(0x13d3, 0x3241), .driver_info = TM6010_BOARD_TWINHAN_TU501 }, + { USB_DEVICE(0x13d3, 0x3243), .driver_info = TM6010_BOARD_TWINHAN_TU501 }, + { USB_DEVICE(0x13d3, 0x3264), .driver_info = TM6010_BOARD_TWINHAN_TU501 }, + { USB_DEVICE(0x6000, 0xdec2), .driver_info = TM6010_BOARD_BEHOLD_WANDER_LITE }, + { USB_DEVICE(0x6000, 0xdec3), .driver_info = TM6010_BOARD_BEHOLD_VOYAGER_LITE }, + { } +}; + +/* Control power led for show some activity */ +void tm6000_flash_led(struct tm6000_core *dev, u8 state) +{ + /* Power LED unconfigured */ + if (!dev->gpio.power_led) + return; + + /* ON Power LED */ + if (state) { + switch (dev->model) { + case TM6010_BOARD_HAUPPAUGE_900H: + case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE: + case TM6010_BOARD_TWINHAN_TU501: + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.power_led, 0x00); + break; + case TM6010_BOARD_BEHOLD_WANDER: + case TM6010_BOARD_BEHOLD_VOYAGER: + case TM6010_BOARD_BEHOLD_WANDER_LITE: + case TM6010_BOARD_BEHOLD_VOYAGER_LITE: + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.power_led, 0x01); + break; + } + } + /* OFF Power LED */ + else { + switch (dev->model) { + case TM6010_BOARD_HAUPPAUGE_900H: + case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE: + case TM6010_BOARD_TWINHAN_TU501: + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.power_led, 0x01); + break; + case TM6010_BOARD_BEHOLD_WANDER: + case TM6010_BOARD_BEHOLD_VOYAGER: + case TM6010_BOARD_BEHOLD_WANDER_LITE: + case TM6010_BOARD_BEHOLD_VOYAGER_LITE: + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.power_led, 0x00); + break; + } + } +} + +/* Tuner callback to provide the proper gpio changes needed for xc5000 */ +int tm6000_xc5000_callback(void *ptr, int component, int command, int arg) +{ + int rc = 0; + struct tm6000_core *dev = ptr; + + if (dev->tuner_type != TUNER_XC5000) + return 0; + + switch (command) { + case XC5000_TUNER_RESET: + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x01); + msleep(15); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x00); + msleep(15); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x01); + break; + } + return rc; +} +EXPORT_SYMBOL_GPL(tm6000_xc5000_callback); + +/* Tuner callback to provide the proper gpio changes needed for xc2028 */ + +int tm6000_tuner_callback(void *ptr, int component, int command, int arg) +{ + int rc = 0; + struct tm6000_core *dev = ptr; + + if (dev->tuner_type != TUNER_XC2028) + return 0; + + switch (command) { + case XC2028_RESET_CLK: + tm6000_ir_wait(dev, 0); + + tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, + 0x02, arg); + msleep(10); + rc = tm6000_i2c_reset(dev, 10); + break; + case XC2028_TUNER_RESET: + /* Reset codes during load firmware */ + switch (arg) { + case 0: + /* newer tuner can faster reset */ + switch (dev->model) { + case TM5600_BOARD_10MOONS_UT821: + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x01); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + 0x300, 0x01); + msleep(10); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x00); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + 0x300, 0x00); + msleep(10); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x01); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + 0x300, 0x01); + break; + case TM6010_BOARD_HAUPPAUGE_900H: + case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE: + case TM6010_BOARD_TWINHAN_TU501: + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x01); + msleep(60); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x00); + msleep(75); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x01); + msleep(60); + break; + default: + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x00); + msleep(130); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x01); + msleep(130); + break; + } + + tm6000_ir_wait(dev, 1); + break; + case 1: + tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, + 0x02, 0x01); + msleep(10); + break; + case 2: + rc = tm6000_i2c_reset(dev, 100); + break; + } + break; + case XC2028_I2C_FLUSH: + tm6000_set_reg(dev, REQ_50_SET_START, 0, 0); + tm6000_set_reg(dev, REQ_51_SET_STOP, 0, 0); + break; + } + return rc; +} +EXPORT_SYMBOL_GPL(tm6000_tuner_callback); + +int tm6000_cards_setup(struct tm6000_core *dev) +{ + /* + * Board-specific initialization sequence. Handles all GPIO + * initialization sequences that are board-specific. + * Up to now, all found devices use GPIO1 and GPIO4 at the same way. + * Probably, they're all based on some reference device. Due to that, + * there's a common routine at the end to handle those GPIO's. Devices + * that use different pinups or init sequences can just return at + * the board-specific session. + */ + switch (dev->model) { + case TM6010_BOARD_HAUPPAUGE_900H: + case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE: + case TM6010_BOARD_TWINHAN_TU501: + case TM6010_BOARD_GENERIC: + /* Turn xceive 3028 on */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.tuner_on, 0x01); + msleep(15); + /* Turn zarlink zl10353 on */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_on, 0x00); + msleep(15); + /* Reset zarlink zl10353 */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x00); + msleep(50); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x01); + msleep(15); + /* Turn zarlink zl10353 off */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_on, 0x01); + msleep(15); + /* ir ? */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.ir, 0x01); + msleep(15); + /* Power led on (blue) */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x00); + msleep(15); + /* DVB led off (orange) */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.dvb_led, 0x01); + msleep(15); + /* Turn zarlink zl10353 on */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_on, 0x00); + msleep(15); + break; + case TM6010_BOARD_BEHOLD_WANDER: + case TM6010_BOARD_BEHOLD_WANDER_LITE: + /* Power led on (blue) */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x01); + msleep(15); + /* Reset zarlink zl10353 */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x00); + msleep(50); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x01); + msleep(15); + break; + case TM6010_BOARD_BEHOLD_VOYAGER: + case TM6010_BOARD_BEHOLD_VOYAGER_LITE: + /* Power led on (blue) */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x01); + msleep(15); + break; + default: + break; + } + + /* + * Default initialization. Most of the devices seem to use GPIO1 + * and GPIO4.on the same way, so, this handles the common sequence + * used by most devices. + * If a device uses a different sequence or different GPIO pins for + * reset, just add the code at the board-specific part + */ + + if (dev->gpio.tuner_reset) { + int rc; + int i; + + for (i = 0; i < 2; i++) { + rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x00); + if (rc < 0) { + printk(KERN_ERR "Error %i doing tuner reset\n", rc); + return rc; + } + + msleep(10); /* Just to be conservative */ + rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x01); + if (rc < 0) { + printk(KERN_ERR "Error %i doing tuner reset\n", rc); + return rc; + } + } + } else { + printk(KERN_ERR "Tuner reset is not configured\n"); + return -1; + } + + msleep(50); + + return 0; +}; + +static void tm6000_config_tuner(struct tm6000_core *dev) +{ + struct tuner_setup tun_setup; + + /* Load tuner module */ + v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, + "tuner", dev->tuner_addr, NULL); + + memset(&tun_setup, 0, sizeof(tun_setup)); + tun_setup.type = dev->tuner_type; + tun_setup.addr = dev->tuner_addr; + + tun_setup.mode_mask = 0; + if (dev->caps.has_tuner) + tun_setup.mode_mask |= (T_ANALOG_TV | T_RADIO); + + switch (dev->tuner_type) { + case TUNER_XC2028: + tun_setup.tuner_callback = tm6000_tuner_callback; + break; + case TUNER_XC5000: + tun_setup.tuner_callback = tm6000_xc5000_callback; + break; + } + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, &tun_setup); + + switch (dev->tuner_type) { + case TUNER_XC2028: { + struct v4l2_priv_tun_config xc2028_cfg; + struct xc2028_ctrl ctl; + + memset(&xc2028_cfg, 0, sizeof(xc2028_cfg)); + memset(&ctl, 0, sizeof(ctl)); + + ctl.demod = XC3028_FE_ZARLINK456; + + xc2028_cfg.tuner = TUNER_XC2028; + xc2028_cfg.priv = &ctl; + + switch (dev->model) { + case TM6010_BOARD_HAUPPAUGE_900H: + case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE: + case TM6010_BOARD_TWINHAN_TU501: + ctl.fname = "xc3028L-v36.fw"; + break; + default: + if (dev->dev_type == TM6010) + ctl.fname = "xc3028-v27.fw"; + else + ctl.fname = "xc3028-v24.fw"; + } + + printk(KERN_INFO "Setting firmware parameters for xc2028\n"); + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config, + &xc2028_cfg); + + } + break; + case TUNER_XC5000: + { + struct v4l2_priv_tun_config xc5000_cfg; + struct xc5000_config ctl = { + .i2c_address = dev->tuner_addr, + .if_khz = 4570, + .radio_input = XC5000_RADIO_FM1_MONO, + }; + + xc5000_cfg.tuner = TUNER_XC5000; + xc5000_cfg.priv = &ctl; + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config, + &xc5000_cfg); + } + break; + default: + printk(KERN_INFO "Unknown tuner type. Tuner is not configured.\n"); + break; + } +} + +static int fill_board_specific_data(struct tm6000_core *dev) +{ + int rc; + + dev->dev_type = tm6000_boards[dev->model].type; + dev->tuner_type = tm6000_boards[dev->model].tuner_type; + dev->tuner_addr = tm6000_boards[dev->model].tuner_addr; + + dev->gpio = tm6000_boards[dev->model].gpio; + + dev->ir_codes = tm6000_boards[dev->model].ir_codes; + + dev->demod_addr = tm6000_boards[dev->model].demod_addr; + + dev->caps = tm6000_boards[dev->model].caps; + + dev->vinput[0] = tm6000_boards[dev->model].vinput[0]; + dev->vinput[1] = tm6000_boards[dev->model].vinput[1]; + dev->vinput[2] = tm6000_boards[dev->model].vinput[2]; + dev->rinput = tm6000_boards[dev->model].rinput; + + /* setup per-model quirks */ + switch (dev->model) { + case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE: + dev->quirks |= TM6000_QUIRK_NO_USB_DELAY; + break; + + default: + break; + } + + /* initialize hardware */ + rc = tm6000_init(dev); + if (rc < 0) + return rc; + + return v4l2_device_register(&dev->udev->dev, &dev->v4l2_dev); +} + + +static void use_alternative_detection_method(struct tm6000_core *dev) +{ + int i, model = -1; + + if (!dev->eedata_size) + return; + + for (i = 0; i < ARRAY_SIZE(tm6000_boards); i++) { + if (!tm6000_boards[i].eename_size) + continue; + if (dev->eedata_size < tm6000_boards[i].eename_pos + + tm6000_boards[i].eename_size) + continue; + + if (!memcmp(&dev->eedata[tm6000_boards[i].eename_pos], + tm6000_boards[i].eename, + tm6000_boards[i].eename_size)) { + model = i; + break; + } + } + if (model < 0) { + printk(KERN_INFO "Device has eeprom but is currently unknown\n"); + return; + } + + dev->model = model; + + printk(KERN_INFO "Device identified via eeprom as %s (type = %d)\n", + tm6000_boards[model].name, model); +} + +static int tm6000_init_dev(struct tm6000_core *dev) +{ + struct v4l2_frequency f; + int rc = 0; + + mutex_init(&dev->lock); + mutex_lock(&dev->lock); + + if (!is_generic(dev->model)) { + rc = fill_board_specific_data(dev); + if (rc < 0) + goto err; + + /* register i2c bus */ + rc = tm6000_i2c_register(dev); + if (rc < 0) + goto err; + } else { + /* register i2c bus */ + rc = tm6000_i2c_register(dev); + if (rc < 0) + goto err; + + use_alternative_detection_method(dev); + + rc = fill_board_specific_data(dev); + if (rc < 0) + goto err; + } + + /* Default values for STD and resolutions */ + dev->width = 720; + dev->height = 480; + dev->norm = V4L2_STD_PAL_M; + + /* Configure tuner */ + tm6000_config_tuner(dev); + + /* Set video standard */ + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm); + + /* Set tuner frequency - also loads firmware on xc2028/xc3028 */ + f.tuner = 0; + f.type = V4L2_TUNER_ANALOG_TV; + f.frequency = 3092; /* 193.25 MHz */ + dev->freq = f.frequency; + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f); + + if (dev->caps.has_tda9874) + v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, + "tvaudio", I2C_ADDR_TDA9874, NULL); + + /* register and initialize V4L2 */ + rc = tm6000_v4l2_register(dev); + if (rc < 0) + goto err; + + tm6000_add_into_devlist(dev); + tm6000_init_extension(dev); + + tm6000_ir_init(dev); + + mutex_unlock(&dev->lock); + return 0; + +err: + mutex_unlock(&dev->lock); + return rc; +} + +/* high bandwidth multiplier, as encoded in highspeed endpoint descriptors */ +#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03)) + +static void get_max_endpoint(struct usb_device *udev, + struct usb_host_interface *alt, + char *msgtype, + struct usb_host_endpoint *curr_e, + struct tm6000_endpoint *tm_ep) +{ + u16 tmp = le16_to_cpu(curr_e->desc.wMaxPacketSize); + unsigned int size = tmp & 0x7ff; + + if (udev->speed == USB_SPEED_HIGH) + size = size * hb_mult(tmp); + + if (size > tm_ep->maxsize) { + tm_ep->endp = curr_e; + tm_ep->maxsize = size; + tm_ep->bInterfaceNumber = alt->desc.bInterfaceNumber; + tm_ep->bAlternateSetting = alt->desc.bAlternateSetting; + + printk(KERN_INFO "tm6000: %s endpoint: 0x%02x (max size=%u bytes)\n", + msgtype, curr_e->desc.bEndpointAddress, + size); + } +} + +/* + * tm6000_usb_probe() + * checks for supported devices + */ +static int tm6000_usb_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct usb_device *usbdev; + struct tm6000_core *dev = NULL; + int i, rc = 0; + int nr = 0; + char *speed; + + usbdev = usb_get_dev(interface_to_usbdev(interface)); + + /* Selects the proper interface */ + rc = usb_set_interface(usbdev, 0, 1); + if (rc < 0) + goto err; + + /* Check to see next free device and mark as used */ + nr = find_first_zero_bit(&tm6000_devused, TM6000_MAXBOARDS); + if (nr >= TM6000_MAXBOARDS) { + printk(KERN_ERR "tm6000: Supports only %i tm60xx boards.\n", TM6000_MAXBOARDS); + usb_put_dev(usbdev); + return -ENOMEM; + } + + /* Create and initialize dev struct */ + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (dev == NULL) { + printk(KERN_ERR "tm6000" ": out of memory!\n"); + usb_put_dev(usbdev); + return -ENOMEM; + } + spin_lock_init(&dev->slock); + mutex_init(&dev->usb_lock); + + /* Increment usage count */ + set_bit(nr, &tm6000_devused); + snprintf(dev->name, 29, "tm6000 #%d", nr); + + dev->model = id->driver_info; + if (card[nr] < ARRAY_SIZE(tm6000_boards)) + dev->model = card[nr]; + + dev->udev = usbdev; + dev->devno = nr; + + switch (usbdev->speed) { + case USB_SPEED_LOW: + speed = "1.5"; + break; + case USB_SPEED_UNKNOWN: + case USB_SPEED_FULL: + speed = "12"; + break; + case USB_SPEED_HIGH: + speed = "480"; + break; + default: + speed = "unknown"; + } + + /* Get endpoints */ + for (i = 0; i < interface->num_altsetting; i++) { + int ep; + + for (ep = 0; ep < interface->altsetting[i].desc.bNumEndpoints; ep++) { + struct usb_host_endpoint *e; + int dir_out; + + e = &interface->altsetting[i].endpoint[ep]; + + dir_out = ((e->desc.bEndpointAddress & + USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT); + + printk(KERN_INFO "tm6000: alt %d, interface %i, class %i\n", + i, + interface->altsetting[i].desc.bInterfaceNumber, + interface->altsetting[i].desc.bInterfaceClass); + + switch (e->desc.bmAttributes) { + case USB_ENDPOINT_XFER_BULK: + if (!dir_out) { + get_max_endpoint(usbdev, + &interface->altsetting[i], + "Bulk IN", e, + &dev->bulk_in); + } else { + get_max_endpoint(usbdev, + &interface->altsetting[i], + "Bulk OUT", e, + &dev->bulk_out); + } + break; + case USB_ENDPOINT_XFER_ISOC: + if (!dir_out) { + get_max_endpoint(usbdev, + &interface->altsetting[i], + "ISOC IN", e, + &dev->isoc_in); + } else { + get_max_endpoint(usbdev, + &interface->altsetting[i], + "ISOC OUT", e, + &dev->isoc_out); + } + break; + case USB_ENDPOINT_XFER_INT: + if (!dir_out) { + get_max_endpoint(usbdev, + &interface->altsetting[i], + "INT IN", e, + &dev->int_in); + } else { + get_max_endpoint(usbdev, + &interface->altsetting[i], + "INT OUT", e, + &dev->int_out); + } + break; + } + } + } + + + printk(KERN_INFO "tm6000: New video device @ %s Mbps (%04x:%04x, ifnum %d)\n", + speed, + le16_to_cpu(dev->udev->descriptor.idVendor), + le16_to_cpu(dev->udev->descriptor.idProduct), + interface->altsetting->desc.bInterfaceNumber); + +/* check if the the device has the iso in endpoint at the correct place */ + if (!dev->isoc_in.endp) { + printk(KERN_ERR "tm6000: probing error: no IN ISOC endpoint!\n"); + rc = -ENODEV; + + goto err; + } + + /* save our data pointer in this interface device */ + usb_set_intfdata(interface, dev); + + printk(KERN_INFO "tm6000: Found %s\n", tm6000_boards[dev->model].name); + + rc = tm6000_init_dev(dev); + if (rc < 0) + goto err; + + return 0; + +err: + printk(KERN_ERR "tm6000: Error %d while registering\n", rc); + + clear_bit(nr, &tm6000_devused); + usb_put_dev(usbdev); + + kfree(dev); + return rc; +} + +/* + * tm6000_usb_disconnect() + * called when the device gets diconencted + * video device will be unregistered on v4l2_close in case it is still open + */ +static void tm6000_usb_disconnect(struct usb_interface *interface) +{ + struct tm6000_core *dev = usb_get_intfdata(interface); + usb_set_intfdata(interface, NULL); + + if (!dev) + return; + + printk(KERN_INFO "tm6000: disconnecting %s\n", dev->name); + + tm6000_ir_fini(dev); + + if (dev->gpio.power_led) { + switch (dev->model) { + case TM6010_BOARD_HAUPPAUGE_900H: + case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE: + case TM6010_BOARD_TWINHAN_TU501: + /* Power led off */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.power_led, 0x01); + msleep(15); + break; + case TM6010_BOARD_BEHOLD_WANDER: + case TM6010_BOARD_BEHOLD_VOYAGER: + case TM6010_BOARD_BEHOLD_WANDER_LITE: + case TM6010_BOARD_BEHOLD_VOYAGER_LITE: + /* Power led off */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.power_led, 0x00); + msleep(15); + break; + } + } + tm6000_v4l2_unregister(dev); + + tm6000_i2c_unregister(dev); + + v4l2_device_unregister(&dev->v4l2_dev); + + dev->state |= DEV_DISCONNECTED; + + usb_put_dev(dev->udev); + + tm6000_close_extension(dev); + tm6000_remove_from_devlist(dev); + + clear_bit(dev->devno, &tm6000_devused); + kfree(dev); +} + +static struct usb_driver tm6000_usb_driver = { + .name = "tm6000", + .probe = tm6000_usb_probe, + .disconnect = tm6000_usb_disconnect, + .id_table = tm6000_id_table, +}; + +static int __init tm6000_module_init(void) +{ + int result; + + printk(KERN_INFO "tm6000" " v4l2 driver version %d.%d.%d loaded\n", + (TM6000_VERSION >> 16) & 0xff, + (TM6000_VERSION >> 8) & 0xff, TM6000_VERSION & 0xff); + + /* register this driver with the USB subsystem */ + result = usb_register(&tm6000_usb_driver); + if (result) + printk(KERN_ERR "tm6000" + " usb_register failed. Error number %d.\n", result); + + return result; +} + +static void __exit tm6000_module_exit(void) +{ + /* deregister at USB subsystem */ + usb_deregister(&tm6000_usb_driver); +} + +module_init(tm6000_module_init); +module_exit(tm6000_module_exit); + +MODULE_DESCRIPTION("Trident TVMaster TM5600/TM6000/TM6010 USB2 adapter"); +MODULE_AUTHOR("Mauro Carvalho Chehab"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/tm6000/tm6000-core.c b/drivers/media/video/tm6000/tm6000-core.c new file mode 100644 index 00000000000..9783616a0da --- /dev/null +++ b/drivers/media/video/tm6000/tm6000-core.c @@ -0,0 +1,965 @@ +/* + * tm6000-core.c - driver for TM5600/TM6000/TM6010 USB video capture devices + * + * Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org> + * + * Copyright (C) 2007 Michel Ludwig <michel.ludwig@gmail.com> + * - DVB-T support + * + * 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 version 2 + * + * 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 <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/usb.h> +#include <linux/i2c.h> +#include "tm6000.h" +#include "tm6000-regs.h" +#include <media/v4l2-common.h> +#include <media/tuner.h> + +#define USB_TIMEOUT (5 * HZ) /* ms */ + +int tm6000_read_write_usb(struct tm6000_core *dev, u8 req_type, u8 req, + u16 value, u16 index, u8 *buf, u16 len) +{ + int ret, i; + unsigned int pipe; + u8 *data = NULL; + + mutex_lock(&dev->usb_lock); + + if (len) + data = kzalloc(len, GFP_KERNEL); + + if (req_type & USB_DIR_IN) + pipe = usb_rcvctrlpipe(dev->udev, 0); + else { + pipe = usb_sndctrlpipe(dev->udev, 0); + memcpy(data, buf, len); + } + + if (tm6000_debug & V4L2_DEBUG_I2C) { + printk(KERN_DEBUG "(dev %p, pipe %08x): ", dev->udev, pipe); + + printk(KERN_CONT "%s: %02x %02x %02x %02x %02x %02x %02x %02x ", + (req_type & USB_DIR_IN) ? " IN" : "OUT", + req_type, req, value&0xff, value>>8, index&0xff, + index>>8, len&0xff, len>>8); + + if (!(req_type & USB_DIR_IN)) { + printk(KERN_CONT ">>> "); + for (i = 0; i < len; i++) + printk(KERN_CONT " %02x", buf[i]); + printk(KERN_CONT "\n"); + } + } + + ret = usb_control_msg(dev->udev, pipe, req, req_type, value, index, + data, len, USB_TIMEOUT); + + if (req_type & USB_DIR_IN) + memcpy(buf, data, len); + + if (tm6000_debug & V4L2_DEBUG_I2C) { + if (ret < 0) { + if (req_type & USB_DIR_IN) + printk(KERN_DEBUG "<<< (len=%d)\n", len); + + printk(KERN_CONT "%s: Error #%d\n", __func__, ret); + } else if (req_type & USB_DIR_IN) { + printk(KERN_CONT "<<< "); + for (i = 0; i < len; i++) + printk(KERN_CONT " %02x", buf[i]); + printk(KERN_CONT "\n"); + } + } + + kfree(data); + msleep(5); + + mutex_unlock(&dev->usb_lock); + return ret; +} + +int tm6000_set_reg(struct tm6000_core *dev, u8 req, u16 value, u16 index) +{ + return + tm6000_read_write_usb(dev, USB_DIR_OUT | USB_TYPE_VENDOR, + req, value, index, NULL, 0); +} +EXPORT_SYMBOL_GPL(tm6000_set_reg); + +int tm6000_get_reg(struct tm6000_core *dev, u8 req, u16 value, u16 index) +{ + int rc; + u8 buf[1]; + + rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR, req, + value, index, buf, 1); + + if (rc < 0) + return rc; + + return *buf; +} +EXPORT_SYMBOL_GPL(tm6000_get_reg); + +int tm6000_set_reg_mask(struct tm6000_core *dev, u8 req, u16 value, + u16 index, u16 mask) +{ + int rc; + u8 buf[1]; + u8 new_index; + + rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR, req, + value, index, buf, 1); + + if (rc < 0) + return rc; + + new_index = (buf[0] & ~mask) | (index & mask); + + if (new_index == index) + return 0; + + return tm6000_read_write_usb(dev, USB_DIR_OUT | USB_TYPE_VENDOR, + req, value, new_index, NULL, 0); +} +EXPORT_SYMBOL_GPL(tm6000_set_reg_mask); + +int tm6000_get_reg16(struct tm6000_core *dev, u8 req, u16 value, u16 index) +{ + int rc; + u8 buf[2]; + + rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR, req, + value, index, buf, 2); + + if (rc < 0) + return rc; + + return buf[1]|buf[0]<<8; +} + +int tm6000_get_reg32(struct tm6000_core *dev, u8 req, u16 value, u16 index) +{ + int rc; + u8 buf[4]; + + rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR, req, + value, index, buf, 4); + + if (rc < 0) + return rc; + + return buf[3] | buf[2] << 8 | buf[1] << 16 | buf[0] << 24; +} + +int tm6000_i2c_reset(struct tm6000_core *dev, u16 tsleep) +{ + int rc; + + rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, TM6000_GPIO_CLK, 0); + if (rc < 0) + return rc; + + msleep(tsleep); + + rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, TM6000_GPIO_CLK, 1); + msleep(tsleep); + + return rc; +} + +void tm6000_set_fourcc_format(struct tm6000_core *dev) +{ + if (dev->dev_type == TM6010) { + int val; + + val = tm6000_get_reg(dev, TM6010_REQ07_RCC_ACTIVE_IF, 0) & 0xfc; + if (dev->fourcc == V4L2_PIX_FMT_UYVY) + tm6000_set_reg(dev, TM6010_REQ07_RCC_ACTIVE_IF, val); + else + tm6000_set_reg(dev, TM6010_REQ07_RCC_ACTIVE_IF, val | 1); + } else { + if (dev->fourcc == V4L2_PIX_FMT_UYVY) + tm6000_set_reg(dev, TM6010_REQ07_RC1_TRESHOLD, 0xd0); + else + tm6000_set_reg(dev, TM6010_REQ07_RC1_TRESHOLD, 0x90); + } +} + +static void tm6000_set_vbi(struct tm6000_core *dev) +{ + /* + * FIXME: + * VBI lines and start/end are different between 60Hz and 50Hz + * So, it is very likely that we need to change the config to + * something that takes it into account, doing something different + * if (dev->norm & V4L2_STD_525_60) + */ + + if (dev->dev_type == TM6010) { + tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x01); + tm6000_set_reg(dev, TM6010_REQ07_R41_TELETEXT_VBI_CODE1, 0x27); + tm6000_set_reg(dev, TM6010_REQ07_R42_VBI_DATA_HIGH_LEVEL, 0x55); + tm6000_set_reg(dev, TM6010_REQ07_R43_VBI_DATA_TYPE_LINE7, 0x66); + tm6000_set_reg(dev, TM6010_REQ07_R44_VBI_DATA_TYPE_LINE8, 0x66); + tm6000_set_reg(dev, TM6010_REQ07_R45_VBI_DATA_TYPE_LINE9, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R46_VBI_DATA_TYPE_LINE10, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R47_VBI_DATA_TYPE_LINE11, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R48_VBI_DATA_TYPE_LINE12, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R49_VBI_DATA_TYPE_LINE13, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R4A_VBI_DATA_TYPE_LINE14, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R4B_VBI_DATA_TYPE_LINE15, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R4C_VBI_DATA_TYPE_LINE16, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R4D_VBI_DATA_TYPE_LINE17, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R4E_VBI_DATA_TYPE_LINE18, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R4F_VBI_DATA_TYPE_LINE19, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R50_VBI_DATA_TYPE_LINE20, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R51_VBI_DATA_TYPE_LINE21, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R52_VBI_DATA_TYPE_LINE22, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R53_VBI_DATA_TYPE_LINE23, 0x00); + tm6000_set_reg(dev, + TM6010_REQ07_R54_VBI_DATA_TYPE_RLINES, 0x00); + tm6000_set_reg(dev, + TM6010_REQ07_R55_VBI_LOOP_FILTER_GAIN, 0x01); + tm6000_set_reg(dev, + TM6010_REQ07_R56_VBI_LOOP_FILTER_I_GAIN, 0x00); + tm6000_set_reg(dev, + TM6010_REQ07_R57_VBI_LOOP_FILTER_P_GAIN, 0x02); + tm6000_set_reg(dev, TM6010_REQ07_R58_VBI_CAPTION_DTO1, 0x35); + tm6000_set_reg(dev, TM6010_REQ07_R59_VBI_CAPTION_DTO0, 0xa0); + tm6000_set_reg(dev, TM6010_REQ07_R5A_VBI_TELETEXT_DTO1, 0x11); + tm6000_set_reg(dev, TM6010_REQ07_R5B_VBI_TELETEXT_DTO0, 0x4c); + tm6000_set_reg(dev, TM6010_REQ07_R40_TELETEXT_VBI_CODE0, 0x01); + tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x00); + } +} + +int tm6000_init_analog_mode(struct tm6000_core *dev) +{ + struct v4l2_frequency f; + + if (dev->dev_type == TM6010) { + u8 active = TM6010_REQ07_RCC_ACTIVE_IF_AUDIO_ENABLE; + + if (!dev->radio) + active |= TM6010_REQ07_RCC_ACTIVE_IF_VIDEO_ENABLE; + + /* Enable video and audio */ + tm6000_set_reg_mask(dev, TM6010_REQ07_RCC_ACTIVE_IF, + active, 0x60); + /* Disable TS input */ + tm6000_set_reg_mask(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, + 0x00, 0x40); + } else { + /* Enables soft reset */ + tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x01); + + if (dev->scaler) + /* Disable Hfilter and Enable TS Drop err */ + tm6000_set_reg(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0x20); + else /* Enable Hfilter and disable TS Drop err */ + tm6000_set_reg(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0x80); + + tm6000_set_reg(dev, TM6010_REQ07_RC3_HSTART1, 0x88); + tm6000_set_reg(dev, TM6000_REQ07_RDA_CLK_SEL, 0x23); + tm6000_set_reg(dev, TM6010_REQ07_RD1_ADDR_FOR_REQ1, 0xc0); + tm6000_set_reg(dev, TM6010_REQ07_RD2_ADDR_FOR_REQ2, 0xd8); + tm6000_set_reg(dev, TM6010_REQ07_RD6_ENDP_REQ1_REQ2, 0x06); + tm6000_set_reg(dev, TM6000_REQ07_RDF_PWDOWN_ACLK, 0x1f); + + /* AP Software reset */ + tm6000_set_reg(dev, TM6010_REQ07_RFF_SOFT_RESET, 0x08); + tm6000_set_reg(dev, TM6010_REQ07_RFF_SOFT_RESET, 0x00); + + tm6000_set_fourcc_format(dev); + + /* Disables soft reset */ + tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x00); + } + msleep(20); + + /* Tuner firmware can now be loaded */ + + /* + * FIXME: This is a hack! xc3028 "sleeps" when no channel is detected + * for more than a few seconds. Not sure why, as this behavior does + * not happen on other devices with xc3028. So, I suspect that it + * is yet another bug at tm6000. After start sleeping, decoding + * doesn't start automatically. Instead, it requires some + * I2C commands to wake it up. As we want to have image at the + * beginning, we needed to add this hack. The better would be to + * discover some way to make tm6000 to wake up without this hack. + */ + f.frequency = dev->freq; + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f); + + msleep(100); + tm6000_set_standard(dev); + tm6000_set_vbi(dev); + tm6000_set_audio_bitrate(dev, 48000); + + /* switch dvb led off */ + if (dev->gpio.dvb_led) { + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.dvb_led, 0x01); + } + + return 0; +} + +int tm6000_init_digital_mode(struct tm6000_core *dev) +{ + if (dev->dev_type == TM6010) { + /* Disable video and audio */ + tm6000_set_reg_mask(dev, TM6010_REQ07_RCC_ACTIVE_IF, + 0x00, 0x60); + /* Enable TS input */ + tm6000_set_reg_mask(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, + 0x40, 0x40); + /* all power down, but not the digital data port */ + tm6000_set_reg(dev, TM6010_REQ07_RFE_POWER_DOWN, 0x28); + tm6000_set_reg(dev, TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xfc); + tm6000_set_reg(dev, TM6010_REQ08_RE6_POWER_DOWN_CTRL2, 0xff); + } else { + tm6000_set_reg(dev, TM6010_REQ07_RFF_SOFT_RESET, 0x08); + tm6000_set_reg(dev, TM6010_REQ07_RFF_SOFT_RESET, 0x00); + tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x01); + tm6000_set_reg(dev, TM6000_REQ07_RDF_PWDOWN_ACLK, 0x08); + tm6000_set_reg(dev, TM6000_REQ07_RE2_VADC_STATUS_CTL, 0x0c); + tm6000_set_reg(dev, TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0xff); + tm6000_set_reg(dev, TM6000_REQ07_REB_VADC_AADC_MODE, 0xd8); + tm6000_set_reg(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0x40); + tm6000_set_reg(dev, TM6010_REQ07_RC1_TRESHOLD, 0xd0); + tm6000_set_reg(dev, TM6010_REQ07_RC3_HSTART1, 0x09); + tm6000_set_reg(dev, TM6000_REQ07_RDA_CLK_SEL, 0x37); + tm6000_set_reg(dev, TM6010_REQ07_RD1_ADDR_FOR_REQ1, 0xd8); + tm6000_set_reg(dev, TM6010_REQ07_RD2_ADDR_FOR_REQ2, 0xc0); + tm6000_set_reg(dev, TM6010_REQ07_RD6_ENDP_REQ1_REQ2, 0x60); + + tm6000_set_reg(dev, TM6000_REQ07_RE2_VADC_STATUS_CTL, 0x0c); + tm6000_set_reg(dev, TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0xff); + tm6000_set_reg(dev, TM6000_REQ07_REB_VADC_AADC_MODE, 0x08); + msleep(50); + + tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 0x0020, 0x00); + msleep(50); + tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 0x0020, 0x01); + msleep(50); + tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 0x0020, 0x00); + msleep(100); + } + + /* switch dvb led on */ + if (dev->gpio.dvb_led) { + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.dvb_led, 0x00); + } + + return 0; +} +EXPORT_SYMBOL(tm6000_init_digital_mode); + +struct reg_init { + u8 req; + u8 reg; + u8 val; +}; + +/* The meaning of those initializations are unknown */ +static struct reg_init tm6000_init_tab[] = { + /* REG VALUE */ + { TM6000_REQ07_RDF_PWDOWN_ACLK, 0x1f }, + { TM6010_REQ07_RFF_SOFT_RESET, 0x08 }, + { TM6010_REQ07_RFF_SOFT_RESET, 0x00 }, + { TM6010_REQ07_RD5_POWERSAVE, 0x4f }, + { TM6000_REQ07_RDA_CLK_SEL, 0x23 }, + { TM6000_REQ07_RDB_OUT_SEL, 0x08 }, + { TM6000_REQ07_RE2_VADC_STATUS_CTL, 0x00 }, + { TM6000_REQ07_RE3_VADC_INP_LPF_SEL1, 0x10 }, + { TM6000_REQ07_RE5_VADC_INP_LPF_SEL2, 0x00 }, + { TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0x00 }, + { TM6000_REQ07_REB_VADC_AADC_MODE, 0x64 }, /* 48000 bits/sample, external input */ + { TM6000_REQ07_REE_VADC_CTRL_SEL_CONTROL, 0xc2 }, + + { TM6010_REQ07_R3F_RESET, 0x01 }, /* Start of soft reset */ + { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x00 }, + { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x07 }, + { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, + { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x00 }, + { TM6010_REQ07_R05_NOISE_THRESHOLD, 0x64 }, + { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x01 }, + { TM6010_REQ07_R08_LUMA_CONTRAST_ADJ, 0x82 }, + { TM6010_REQ07_R09_LUMA_BRIGHTNESS_ADJ, 0x36 }, + { TM6010_REQ07_R0A_CHROMA_SATURATION_ADJ, 0x50 }, + { TM6010_REQ07_R0C_CHROMA_AGC_CONTROL, 0x6a }, + { TM6010_REQ07_R11_AGC_PEAK_CONTROL, 0xc9 }, + { TM6010_REQ07_R12_AGC_GATE_STARTH, 0x07 }, + { TM6010_REQ07_R13_AGC_GATE_STARTL, 0x3b }, + { TM6010_REQ07_R14_AGC_GATE_WIDTH, 0x47 }, + { TM6010_REQ07_R15_AGC_BP_DELAY, 0x6f }, + { TM6010_REQ07_R17_HLOOP_MAXSTATE, 0xcd }, + { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e }, + { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x8b }, + { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xa2 }, + { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe9 }, + { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, + { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, + { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, + { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, + { TM6010_REQ07_R20_HSYNC_RISING_EDGE_TIME, 0x3c }, + { TM6010_REQ07_R21_HSYNC_PHASE_OFFSET, 0x3c }, + { TM6010_REQ07_R2D_CHROMA_BURST_END, 0x48 }, + { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 }, + { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22 }, + { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61 }, + { TM6010_REQ07_R32_VSYNC_HLOCK_MIN, 0x74 }, + { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x1c }, + { TM6010_REQ07_R34_VSYNC_AGC_MIN, 0x74 }, + { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, + { TM6010_REQ07_R36_VSYNC_VBI_MIN, 0x7a }, + { TM6010_REQ07_R37_VSYNC_VBI_MAX, 0x26 }, + { TM6010_REQ07_R38_VSYNC_THRESHOLD, 0x40 }, + { TM6010_REQ07_R39_VSYNC_TIME_CONSTANT, 0x0a }, + { TM6010_REQ07_R42_VBI_DATA_HIGH_LEVEL, 0x55 }, + { TM6010_REQ07_R51_VBI_DATA_TYPE_LINE21, 0x11 }, + { TM6010_REQ07_R55_VBI_LOOP_FILTER_GAIN, 0x01 }, + { TM6010_REQ07_R57_VBI_LOOP_FILTER_P_GAIN, 0x02 }, + { TM6010_REQ07_R58_VBI_CAPTION_DTO1, 0x35 }, + { TM6010_REQ07_R59_VBI_CAPTION_DTO0, 0xa0 }, + { TM6010_REQ07_R80_COMB_FILTER_TRESHOLD, 0x15 }, + { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42 }, + { TM6010_REQ07_RC1_TRESHOLD, 0xd0 }, + { TM6010_REQ07_RC3_HSTART1, 0x88 }, + { TM6010_REQ07_R3F_RESET, 0x00 }, /* End of the soft reset */ + { TM6010_REQ05_R18_IMASK7, 0x00 }, +}; + +static struct reg_init tm6010_init_tab[] = { + { TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0x00 }, + { TM6010_REQ07_RC4_HSTART0, 0xa0 }, + { TM6010_REQ07_RC6_HEND0, 0x40 }, + { TM6010_REQ07_RCA_VEND0, 0x31 }, + { TM6010_REQ07_RCC_ACTIVE_IF, 0xe1 }, + { TM6010_REQ07_RE0_DVIDEO_SOURCE, 0x03 }, + { TM6010_REQ07_RFE_POWER_DOWN, 0x7f }, + + { TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf0 }, + { TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf4 }, + { TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf8 }, + { TM6010_REQ08_RE6_POWER_DOWN_CTRL2, 0x00 }, + { TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf2 }, + { TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xf0 }, + { TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2 }, + { TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, 0x60 }, + { TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfc }, + + { TM6010_REQ07_R3F_RESET, 0x01 }, + { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x00 }, + { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x07 }, + { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, + { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x00 }, + { TM6010_REQ07_R05_NOISE_THRESHOLD, 0x64 }, + { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x01 }, + { TM6010_REQ07_R08_LUMA_CONTRAST_ADJ, 0x82 }, + { TM6010_REQ07_R09_LUMA_BRIGHTNESS_ADJ, 0x36 }, + { TM6010_REQ07_R0A_CHROMA_SATURATION_ADJ, 0x50 }, + { TM6010_REQ07_R0C_CHROMA_AGC_CONTROL, 0x6a }, + { TM6010_REQ07_R11_AGC_PEAK_CONTROL, 0xc9 }, + { TM6010_REQ07_R12_AGC_GATE_STARTH, 0x07 }, + { TM6010_REQ07_R13_AGC_GATE_STARTL, 0x3b }, + { TM6010_REQ07_R14_AGC_GATE_WIDTH, 0x47 }, + { TM6010_REQ07_R15_AGC_BP_DELAY, 0x6f }, + { TM6010_REQ07_R17_HLOOP_MAXSTATE, 0xcd }, + { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e }, + { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x8b }, + { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xa2 }, + { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe9 }, + { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, + { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, + { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, + { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, + { TM6010_REQ07_R20_HSYNC_RISING_EDGE_TIME, 0x3c }, + { TM6010_REQ07_R21_HSYNC_PHASE_OFFSET, 0x3c }, + { TM6010_REQ07_R2D_CHROMA_BURST_END, 0x48 }, + { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 }, + { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22 }, + { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61 }, + { TM6010_REQ07_R32_VSYNC_HLOCK_MIN, 0x74 }, + { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x1c }, + { TM6010_REQ07_R34_VSYNC_AGC_MIN, 0x74 }, + { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, + { TM6010_REQ07_R36_VSYNC_VBI_MIN, 0x7a }, + { TM6010_REQ07_R37_VSYNC_VBI_MAX, 0x26 }, + { TM6010_REQ07_R38_VSYNC_THRESHOLD, 0x40 }, + { TM6010_REQ07_R39_VSYNC_TIME_CONSTANT, 0x0a }, + { TM6010_REQ07_R42_VBI_DATA_HIGH_LEVEL, 0x55 }, + { TM6010_REQ07_R51_VBI_DATA_TYPE_LINE21, 0x11 }, + { TM6010_REQ07_R55_VBI_LOOP_FILTER_GAIN, 0x01 }, + { TM6010_REQ07_R57_VBI_LOOP_FILTER_P_GAIN, 0x02 }, + { TM6010_REQ07_R58_VBI_CAPTION_DTO1, 0x35 }, + { TM6010_REQ07_R59_VBI_CAPTION_DTO0, 0xa0 }, + { TM6010_REQ07_R80_COMB_FILTER_TRESHOLD, 0x15 }, + { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42 }, + { TM6010_REQ07_RC1_TRESHOLD, 0xd0 }, + { TM6010_REQ07_RC3_HSTART1, 0x88 }, + { TM6010_REQ07_R3F_RESET, 0x00 }, + + { TM6010_REQ05_R18_IMASK7, 0x00 }, + + { TM6010_REQ07_RD8_IR_LEADER1, 0xaa }, + { TM6010_REQ07_RD8_IR_LEADER0, 0x30 }, + { TM6010_REQ07_RD8_IR_PULSE_CNT1, 0x20 }, + { TM6010_REQ07_RD8_IR_PULSE_CNT0, 0xd0 }, + { REQ_04_EN_DISABLE_MCU_INT, 0x02, 0x00 }, + { TM6010_REQ07_RD8_IR, 0x2f }, + + /* set remote wakeup key:any key wakeup */ + { TM6010_REQ07_RE5_REMOTE_WAKEUP, 0xfe }, + { TM6010_REQ07_RD8_IR_WAKEUP_SEL, 0xff }, +}; + +int tm6000_init(struct tm6000_core *dev) +{ + int board, rc = 0, i, size; + struct reg_init *tab; + + /* Check board revision */ + board = tm6000_get_reg32(dev, REQ_40_GET_VERSION, 0, 0); + if (board >= 0) { + switch (board & 0xff) { + case 0xf3: + printk(KERN_INFO "Found tm6000\n"); + if (dev->dev_type != TM6000) + dev->dev_type = TM6000; + break; + case 0xf4: + printk(KERN_INFO "Found tm6010\n"); + if (dev->dev_type != TM6010) + dev->dev_type = TM6010; + break; + default: + printk(KERN_INFO "Unknown board version = 0x%08x\n", board); + } + } else + printk(KERN_ERR "Error %i while retrieving board version\n", board); + + if (dev->dev_type == TM6010) { + tab = tm6010_init_tab; + size = ARRAY_SIZE(tm6010_init_tab); + } else { + tab = tm6000_init_tab; + size = ARRAY_SIZE(tm6000_init_tab); + } + + /* Load board's initialization table */ + for (i = 0; i < size; i++) { + rc = tm6000_set_reg(dev, tab[i].req, tab[i].reg, tab[i].val); + if (rc < 0) { + printk(KERN_ERR "Error %i while setting req %d, " + "reg %d to value %d\n", rc, + tab[i].req, tab[i].reg, tab[i].val); + return rc; + } + } + + msleep(5); /* Just to be conservative */ + + rc = tm6000_cards_setup(dev); + + return rc; +} + +int tm6000_reset(struct tm6000_core *dev) +{ + int pipe; + int err; + + msleep(500); + + err = usb_set_interface(dev->udev, dev->isoc_in.bInterfaceNumber, 0); + if (err < 0) { + tm6000_err("failed to select interface %d, alt. setting 0\n", + dev->isoc_in.bInterfaceNumber); + return err; + } + + err = usb_reset_configuration(dev->udev); + if (err < 0) { + tm6000_err("failed to reset configuration\n"); + return err; + } + + if ((dev->quirks & TM6000_QUIRK_NO_USB_DELAY) == 0) + msleep(5); + + /* + * Not all devices have int_in defined + */ + if (!dev->int_in.endp) + return 0; + + err = usb_set_interface(dev->udev, dev->isoc_in.bInterfaceNumber, 2); + if (err < 0) { + tm6000_err("failed to select interface %d, alt. setting 2\n", + dev->isoc_in.bInterfaceNumber); + return err; + } + + msleep(5); + + pipe = usb_rcvintpipe(dev->udev, + dev->int_in.endp->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); + + err = usb_clear_halt(dev->udev, pipe); + if (err < 0) { + tm6000_err("usb_clear_halt failed: %d\n", err); + return err; + } + + return 0; +} + +int tm6000_set_audio_bitrate(struct tm6000_core *dev, int bitrate) +{ + int val = 0; + u8 areg_f0 = 0x60; /* ADC MCLK = 250 Fs */ + u8 areg_0a = 0x91; /* SIF 48KHz */ + + switch (bitrate) { + case 48000: + areg_f0 = 0x60; /* ADC MCLK = 250 Fs */ + areg_0a = 0x91; /* SIF 48KHz */ + dev->audio_bitrate = bitrate; + break; + case 32000: + areg_f0 = 0x00; /* ADC MCLK = 375 Fs */ + areg_0a = 0x90; /* SIF 32KHz */ + dev->audio_bitrate = bitrate; + break; + default: + return -EINVAL; + } + + + /* enable I2S, if we use sif or external I2S device */ + if (dev->dev_type == TM6010) { + val = tm6000_set_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD, areg_0a); + if (val < 0) + return val; + + val = tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, + areg_f0, 0xf0); + if (val < 0) + return val; + } else { + val = tm6000_set_reg_mask(dev, TM6000_REQ07_REB_VADC_AADC_MODE, + areg_f0, 0xf0); + if (val < 0) + return val; + } + return 0; +} +EXPORT_SYMBOL_GPL(tm6000_set_audio_bitrate); + +int tm6000_set_audio_rinput(struct tm6000_core *dev) +{ + if (dev->dev_type == TM6010) { + /* Audio crossbar setting, default SIF1 */ + u8 areg_f0; + + switch (dev->rinput.amux) { + case TM6000_AMUX_SIF1: + case TM6000_AMUX_SIF2: + areg_f0 = 0x03; + break; + case TM6000_AMUX_ADC1: + areg_f0 = 0x00; + break; + case TM6000_AMUX_ADC2: + areg_f0 = 0x08; + break; + case TM6000_AMUX_I2S: + areg_f0 = 0x04; + break; + default: + printk(KERN_INFO "%s: audio input dosn't support\n", + dev->name); + return 0; + break; + } + /* Set audio input crossbar */ + tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, + areg_f0, 0x0f); + } else { + u8 areg_eb; + /* Audio setting, default LINE1 */ + switch (dev->rinput.amux) { + case TM6000_AMUX_ADC1: + areg_eb = 0x00; + break; + case TM6000_AMUX_ADC2: + areg_eb = 0x04; + break; + default: + printk(KERN_INFO "%s: audio input dosn't support\n", + dev->name); + return 0; + break; + } + /* Set audio input */ + tm6000_set_reg_mask(dev, TM6000_REQ07_REB_VADC_AADC_MODE, + areg_eb, 0x0f); + } + return 0; +} + +static void tm6010_set_mute_sif(struct tm6000_core *dev, u8 mute) +{ + u8 mute_reg = 0; + + if (mute) + mute_reg = 0x08; + + tm6000_set_reg_mask(dev, TM6010_REQ08_R0A_A_I2S_MOD, mute_reg, 0x08); +} + +static void tm6010_set_mute_adc(struct tm6000_core *dev, u8 mute) +{ + u8 mute_reg = 0; + + if (mute) + mute_reg = 0x20; + + if (dev->dev_type == TM6010) { + tm6000_set_reg_mask(dev, TM6010_REQ08_RF2_LEFT_CHANNEL_VOL, + mute_reg, 0x20); + tm6000_set_reg_mask(dev, TM6010_REQ08_RF3_RIGHT_CHANNEL_VOL, + mute_reg, 0x20); + } else { + tm6000_set_reg_mask(dev, TM6000_REQ07_REC_VADC_AADC_LVOL, + mute_reg, 0x20); + tm6000_set_reg_mask(dev, TM6000_REQ07_RED_VADC_AADC_RVOL, + mute_reg, 0x20); + } +} + +int tm6000_tvaudio_set_mute(struct tm6000_core *dev, u8 mute) +{ + enum tm6000_mux mux; + + if (dev->radio) + mux = dev->rinput.amux; + else + mux = dev->vinput[dev->input].amux; + + switch (mux) { + case TM6000_AMUX_SIF1: + case TM6000_AMUX_SIF2: + if (dev->dev_type == TM6010) + tm6010_set_mute_sif(dev, mute); + else { + printk(KERN_INFO "ERROR: TM5600 and TM6000 don't has" + " SIF audio inputs. Please check the %s" + " configuration.\n", dev->name); + return -EINVAL; + } + break; + case TM6000_AMUX_ADC1: + case TM6000_AMUX_ADC2: + tm6010_set_mute_adc(dev, mute); + break; + default: + return -EINVAL; + break; + } + return 0; +} + +static void tm6010_set_volume_sif(struct tm6000_core *dev, int vol) +{ + u8 vol_reg; + + vol_reg = vol & 0x0F; + + if (vol < 0) + vol_reg |= 0x40; + + tm6000_set_reg(dev, TM6010_REQ08_R07_A_LEFT_VOL, vol_reg); + tm6000_set_reg(dev, TM6010_REQ08_R08_A_RIGHT_VOL, vol_reg); +} + +static void tm6010_set_volume_adc(struct tm6000_core *dev, int vol) +{ + u8 vol_reg; + + vol_reg = (vol + 0x10) & 0x1f; + + if (dev->dev_type == TM6010) { + tm6000_set_reg(dev, TM6010_REQ08_RF2_LEFT_CHANNEL_VOL, vol_reg); + tm6000_set_reg(dev, TM6010_REQ08_RF3_RIGHT_CHANNEL_VOL, vol_reg); + } else { + tm6000_set_reg(dev, TM6000_REQ07_REC_VADC_AADC_LVOL, vol_reg); + tm6000_set_reg(dev, TM6000_REQ07_RED_VADC_AADC_RVOL, vol_reg); + } +} + +void tm6000_set_volume(struct tm6000_core *dev, int vol) +{ + enum tm6000_mux mux; + + if (dev->radio) { + mux = dev->rinput.amux; + vol += 8; /* Offset to 0 dB */ + } else + mux = dev->vinput[dev->input].amux; + + switch (mux) { + case TM6000_AMUX_SIF1: + case TM6000_AMUX_SIF2: + if (dev->dev_type == TM6010) + tm6010_set_volume_sif(dev, vol); + else + printk(KERN_INFO "ERROR: TM5600 and TM6000 don't has" + " SIF audio inputs. Please check the %s" + " configuration.\n", dev->name); + break; + case TM6000_AMUX_ADC1: + case TM6000_AMUX_ADC2: + tm6010_set_volume_adc(dev, vol); + break; + default: + break; + } +} + +static LIST_HEAD(tm6000_devlist); +static DEFINE_MUTEX(tm6000_devlist_mutex); + +/* + * tm6000_realease_resource() + */ + +void tm6000_remove_from_devlist(struct tm6000_core *dev) +{ + mutex_lock(&tm6000_devlist_mutex); + list_del(&dev->devlist); + mutex_unlock(&tm6000_devlist_mutex); +}; + +void tm6000_add_into_devlist(struct tm6000_core *dev) +{ + mutex_lock(&tm6000_devlist_mutex); + list_add_tail(&dev->devlist, &tm6000_devlist); + mutex_unlock(&tm6000_devlist_mutex); +}; + +/* + * Extension interface + */ + +static LIST_HEAD(tm6000_extension_devlist); + +int tm6000_call_fillbuf(struct tm6000_core *dev, enum tm6000_ops_type type, + char *buf, int size) +{ + struct tm6000_ops *ops = NULL; + + /* FIXME: tm6000_extension_devlist_lock should be a spinlock */ + + if (!list_empty(&tm6000_extension_devlist)) { + list_for_each_entry(ops, &tm6000_extension_devlist, next) { + if (ops->fillbuf && ops->type == type) + ops->fillbuf(dev, buf, size); + } + } + + return 0; +} + +int tm6000_register_extension(struct tm6000_ops *ops) +{ + struct tm6000_core *dev = NULL; + + mutex_lock(&tm6000_devlist_mutex); + list_add_tail(&ops->next, &tm6000_extension_devlist); + list_for_each_entry(dev, &tm6000_devlist, devlist) { + ops->init(dev); + printk(KERN_INFO "%s: Initialized (%s) extension\n", + dev->name, ops->name); + } + mutex_unlock(&tm6000_devlist_mutex); + return 0; +} +EXPORT_SYMBOL(tm6000_register_extension); + +void tm6000_unregister_extension(struct tm6000_ops *ops) +{ + struct tm6000_core *dev = NULL; + + mutex_lock(&tm6000_devlist_mutex); + list_for_each_entry(dev, &tm6000_devlist, devlist) + ops->fini(dev); + + printk(KERN_INFO "tm6000: Remove (%s) extension\n", ops->name); + list_del(&ops->next); + mutex_unlock(&tm6000_devlist_mutex); +} +EXPORT_SYMBOL(tm6000_unregister_extension); + +void tm6000_init_extension(struct tm6000_core *dev) +{ + struct tm6000_ops *ops = NULL; + + mutex_lock(&tm6000_devlist_mutex); + if (!list_empty(&tm6000_extension_devlist)) { + list_for_each_entry(ops, &tm6000_extension_devlist, next) { + if (ops->init) + ops->init(dev); + } + } + mutex_unlock(&tm6000_devlist_mutex); +} + +void tm6000_close_extension(struct tm6000_core *dev) +{ + struct tm6000_ops *ops = NULL; + + mutex_lock(&tm6000_devlist_mutex); + if (!list_empty(&tm6000_extension_devlist)) { + list_for_each_entry(ops, &tm6000_extension_devlist, next) { + if (ops->fini) + ops->fini(dev); + } + } + mutex_unlock(&tm6000_devlist_mutex); +} diff --git a/drivers/media/video/tm6000/tm6000-dvb.c b/drivers/media/video/tm6000/tm6000-dvb.c new file mode 100644 index 00000000000..5e6c129a4be --- /dev/null +++ b/drivers/media/video/tm6000/tm6000-dvb.c @@ -0,0 +1,452 @@ +/* + * tm6000-dvb.c - dvb-t support for TM5600/TM6000/TM6010 USB video capture devices + * + * Copyright (C) 2007 Michel Ludwig <michel.ludwig@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation version 2 + * + * 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 <linux/kernel.h> +#include <linux/slab.h> +#include <linux/usb.h> + +#include "tm6000.h" +#include "tm6000-regs.h" + +#include "zl10353.h" + +#include <media/tuner.h> + +#include "tuner-xc2028.h" +#include "xc5000.h" + +MODULE_DESCRIPTION("DVB driver extension module for tm5600/6000/6010 based TV cards"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); +MODULE_LICENSE("GPL"); + +MODULE_SUPPORTED_DEVICE("{{Trident, tm5600}," + "{{Trident, tm6000}," + "{{Trident, tm6010}"); + +static int debug; + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "enable debug message"); + +static inline void print_err_status(struct tm6000_core *dev, + int packet, int status) +{ + char *errmsg = "Unknown"; + + switch (status) { + case -ENOENT: + errmsg = "unlinked synchronuously"; + break; + case -ECONNRESET: + errmsg = "unlinked asynchronuously"; + break; + case -ENOSR: + errmsg = "Buffer error (overrun)"; + break; + case -EPIPE: + errmsg = "Stalled (device not responding)"; + break; + case -EOVERFLOW: + errmsg = "Babble (bad cable?)"; + break; + case -EPROTO: + errmsg = "Bit-stuff error (bad cable?)"; + break; + case -EILSEQ: + errmsg = "CRC/Timeout (could be anything)"; + break; + case -ETIME: + errmsg = "Device does not respond"; + break; + } + if (packet < 0) { + dprintk(dev, 1, "URB status %d [%s].\n", + status, errmsg); + } else { + dprintk(dev, 1, "URB packet %d, status %d [%s].\n", + packet, status, errmsg); + } +} + +static void tm6000_urb_received(struct urb *urb) +{ + int ret; + struct tm6000_core *dev = urb->context; + + if (urb->status != 0) + print_err_status(dev, 0, urb->status); + else if (urb->actual_length > 0) + dvb_dmx_swfilter(&dev->dvb->demux, urb->transfer_buffer, + urb->actual_length); + + if (dev->dvb->streams > 0) { + ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret < 0) { + printk(KERN_ERR "tm6000: error %s\n", __func__); + kfree(urb->transfer_buffer); + usb_free_urb(urb); + } + } +} + +static int tm6000_start_stream(struct tm6000_core *dev) +{ + int ret; + unsigned int pipe, size; + struct tm6000_dvb *dvb = dev->dvb; + + printk(KERN_INFO "tm6000: got start stream request %s\n", __func__); + + if (dev->mode != TM6000_MODE_DIGITAL) { + tm6000_init_digital_mode(dev); + dev->mode = TM6000_MODE_DIGITAL; + } + + dvb->bulk_urb = usb_alloc_urb(0, GFP_KERNEL); + if (dvb->bulk_urb == NULL) { + printk(KERN_ERR "tm6000: couldn't allocate urb\n"); + return -ENOMEM; + } + + pipe = usb_rcvbulkpipe(dev->udev, dev->bulk_in.endp->desc.bEndpointAddress + & USB_ENDPOINT_NUMBER_MASK); + + size = usb_maxpacket(dev->udev, pipe, usb_pipeout(pipe)); + size = size * 15; /* 512 x 8 or 12 or 15 */ + + dvb->bulk_urb->transfer_buffer = kzalloc(size, GFP_KERNEL); + if (dvb->bulk_urb->transfer_buffer == NULL) { + usb_free_urb(dvb->bulk_urb); + printk(KERN_ERR "tm6000: couldn't allocate transfer buffer!\n"); + return -ENOMEM; + } + + usb_fill_bulk_urb(dvb->bulk_urb, dev->udev, pipe, + dvb->bulk_urb->transfer_buffer, + size, + tm6000_urb_received, dev); + + ret = usb_clear_halt(dev->udev, pipe); + if (ret < 0) { + printk(KERN_ERR "tm6000: error %i in %s during pipe reset\n", + ret, __func__); + return ret; + } else + printk(KERN_ERR "tm6000: pipe resetted\n"); + +/* mutex_lock(&tm6000_driver.open_close_mutex); */ + ret = usb_submit_urb(dvb->bulk_urb, GFP_KERNEL); + +/* mutex_unlock(&tm6000_driver.open_close_mutex); */ + if (ret) { + printk(KERN_ERR "tm6000: submit of urb failed (error=%i)\n", + ret); + + kfree(dvb->bulk_urb->transfer_buffer); + usb_free_urb(dvb->bulk_urb); + return ret; + } + + return 0; +} + +static void tm6000_stop_stream(struct tm6000_core *dev) +{ + struct tm6000_dvb *dvb = dev->dvb; + + if (dvb->bulk_urb) { + printk(KERN_INFO "urb killing\n"); + usb_kill_urb(dvb->bulk_urb); + printk(KERN_INFO "urb buffer free\n"); + kfree(dvb->bulk_urb->transfer_buffer); + usb_free_urb(dvb->bulk_urb); + dvb->bulk_urb = NULL; + } +} + +static int tm6000_start_feed(struct dvb_demux_feed *feed) +{ + struct dvb_demux *demux = feed->demux; + struct tm6000_core *dev = demux->priv; + struct tm6000_dvb *dvb = dev->dvb; + printk(KERN_INFO "tm6000: got start feed request %s\n", __func__); + + mutex_lock(&dvb->mutex); + if (dvb->streams == 0) { + dvb->streams = 1; +/* mutex_init(&tm6000_dev->streming_mutex); */ + tm6000_start_stream(dev); + } else + ++(dvb->streams); + mutex_unlock(&dvb->mutex); + + return 0; +} + +static int tm6000_stop_feed(struct dvb_demux_feed *feed) +{ + struct dvb_demux *demux = feed->demux; + struct tm6000_core *dev = demux->priv; + struct tm6000_dvb *dvb = dev->dvb; + + printk(KERN_INFO "tm6000: got stop feed request %s\n", __func__); + + mutex_lock(&dvb->mutex); + + printk(KERN_INFO "stream %#x\n", dvb->streams); + --(dvb->streams); + if (dvb->streams == 0) { + printk(KERN_INFO "stop stream\n"); + tm6000_stop_stream(dev); +/* mutex_destroy(&tm6000_dev->streaming_mutex); */ + } + mutex_unlock(&dvb->mutex); +/* mutex_destroy(&tm6000_dev->streaming_mutex); */ + + return 0; +} + +static int tm6000_dvb_attach_frontend(struct tm6000_core *dev) +{ + struct tm6000_dvb *dvb = dev->dvb; + + if (dev->caps.has_zl10353) { + struct zl10353_config config = { + .demod_address = dev->demod_addr, + .no_tuner = 1, + .parallel_ts = 1, + .if2 = 45700, + .disable_i2c_gate_ctrl = 1, + }; + + dvb->frontend = dvb_attach(zl10353_attach, &config, + &dev->i2c_adap); + } else { + printk(KERN_ERR "tm6000: no frontend defined for the device!\n"); + return -1; + } + + return (!dvb->frontend) ? -1 : 0; +} + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +static int register_dvb(struct tm6000_core *dev) +{ + int ret = -1; + struct tm6000_dvb *dvb = dev->dvb; + + mutex_init(&dvb->mutex); + + dvb->streams = 0; + + /* attach the frontend */ + ret = tm6000_dvb_attach_frontend(dev); + if (ret < 0) { + printk(KERN_ERR "tm6000: couldn't attach the frontend!\n"); + goto err; + } + + ret = dvb_register_adapter(&dvb->adapter, "Trident TVMaster 6000 DVB-T", + THIS_MODULE, &dev->udev->dev, adapter_nr); + dvb->adapter.priv = dev; + + if (dvb->frontend) { + switch (dev->tuner_type) { + case TUNER_XC2028: { + struct xc2028_config cfg = { + .i2c_adap = &dev->i2c_adap, + .i2c_addr = dev->tuner_addr, + }; + + dvb->frontend->callback = tm6000_tuner_callback; + ret = dvb_register_frontend(&dvb->adapter, dvb->frontend); + if (ret < 0) { + printk(KERN_ERR + "tm6000: couldn't register frontend\n"); + goto adapter_err; + } + + if (!dvb_attach(xc2028_attach, dvb->frontend, &cfg)) { + printk(KERN_ERR "tm6000: couldn't register " + "frontend (xc3028)\n"); + ret = -EINVAL; + goto frontend_err; + } + printk(KERN_INFO "tm6000: XC2028/3028 asked to be " + "attached to frontend!\n"); + break; + } + case TUNER_XC5000: { + struct xc5000_config cfg = { + .i2c_address = dev->tuner_addr, + }; + + dvb->frontend->callback = tm6000_xc5000_callback; + ret = dvb_register_frontend(&dvb->adapter, dvb->frontend); + if (ret < 0) { + printk(KERN_ERR + "tm6000: couldn't register frontend\n"); + goto adapter_err; + } + + if (!dvb_attach(xc5000_attach, dvb->frontend, &dev->i2c_adap, &cfg)) { + printk(KERN_ERR "tm6000: couldn't register " + "frontend (xc5000)\n"); + ret = -EINVAL; + goto frontend_err; + } + printk(KERN_INFO "tm6000: XC5000 asked to be " + "attached to frontend!\n"); + break; + } + } + } else + printk(KERN_ERR "tm6000: no frontend found\n"); + + dvb->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING + | DMX_MEMORY_BASED_FILTERING; + dvb->demux.priv = dev; + dvb->demux.filternum = 8; + dvb->demux.feednum = 8; + dvb->demux.start_feed = tm6000_start_feed; + dvb->demux.stop_feed = tm6000_stop_feed; + dvb->demux.write_to_decoder = NULL; + ret = dvb_dmx_init(&dvb->demux); + if (ret < 0) { + printk(KERN_ERR "tm6000: dvb_dmx_init failed (errno = %d)\n", ret); + goto frontend_err; + } + + dvb->dmxdev.filternum = dev->dvb->demux.filternum; + dvb->dmxdev.demux = &dev->dvb->demux.dmx; + dvb->dmxdev.capabilities = 0; + + ret = dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter); + if (ret < 0) { + printk(KERN_ERR "tm6000: dvb_dmxdev_init failed (errno = %d)\n", ret); + goto dvb_dmx_err; + } + + return 0; + +dvb_dmx_err: + dvb_dmx_release(&dvb->demux); +frontend_err: + if (dvb->frontend) { + dvb_frontend_detach(dvb->frontend); + dvb_unregister_frontend(dvb->frontend); + } +adapter_err: + dvb_unregister_adapter(&dvb->adapter); +err: + return ret; +} + +static void unregister_dvb(struct tm6000_core *dev) +{ + struct tm6000_dvb *dvb = dev->dvb; + + if (dvb->bulk_urb != NULL) { + struct urb *bulk_urb = dvb->bulk_urb; + + kfree(bulk_urb->transfer_buffer); + bulk_urb->transfer_buffer = NULL; + usb_unlink_urb(bulk_urb); + usb_free_urb(bulk_urb); + } + +/* mutex_lock(&tm6000_driver.open_close_mutex); */ + if (dvb->frontend) { + dvb_frontend_detach(dvb->frontend); + dvb_unregister_frontend(dvb->frontend); + } + + dvb_dmxdev_release(&dvb->dmxdev); + dvb_dmx_release(&dvb->demux); + dvb_unregister_adapter(&dvb->adapter); + mutex_destroy(&dvb->mutex); +/* mutex_unlock(&tm6000_driver.open_close_mutex); */ +} + +static int dvb_init(struct tm6000_core *dev) +{ + struct tm6000_dvb *dvb; + int rc; + + if (!dev) + return 0; + + if (!dev->caps.has_dvb) + return 0; + + dvb = kzalloc(sizeof(struct tm6000_dvb), GFP_KERNEL); + if (!dvb) { + printk(KERN_INFO "Cannot allocate memory\n"); + return -ENOMEM; + } + + dev->dvb = dvb; + + rc = register_dvb(dev); + if (rc < 0) { + kfree(dvb); + dev->dvb = NULL; + return 0; + } + + return 0; +} + +static int dvb_fini(struct tm6000_core *dev) +{ + if (!dev) + return 0; + + if (!dev->caps.has_dvb) + return 0; + + if (dev->dvb) { + unregister_dvb(dev); + kfree(dev->dvb); + dev->dvb = NULL; + } + + return 0; +} + +static struct tm6000_ops dvb_ops = { + .type = TM6000_DVB, + .name = "TM6000 dvb Extension", + .init = dvb_init, + .fini = dvb_fini, +}; + +static int __init tm6000_dvb_register(void) +{ + return tm6000_register_extension(&dvb_ops); +} + +static void __exit tm6000_dvb_unregister(void) +{ + tm6000_unregister_extension(&dvb_ops); +} + +module_init(tm6000_dvb_register); +module_exit(tm6000_dvb_unregister); diff --git a/drivers/media/video/tm6000/tm6000-i2c.c b/drivers/media/video/tm6000/tm6000-i2c.c new file mode 100644 index 00000000000..0290bbf00c3 --- /dev/null +++ b/drivers/media/video/tm6000/tm6000-i2c.c @@ -0,0 +1,340 @@ +/* + * tm6000-i2c.c - driver for TM5600/TM6000/TM6010 USB video capture devices + * + * Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org> + * + * Copyright (C) 2007 Michel Ludwig <michel.ludwig@gmail.com> + * - Fix SMBus Read Byte command + * + * 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 version 2 + * + * 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 <linux/module.h> +#include <linux/kernel.h> +#include <linux/usb.h> +#include <linux/i2c.h> + +#include "tm6000.h" +#include "tm6000-regs.h" +#include <media/v4l2-common.h> +#include <media/tuner.h> +#include "tuner-xc2028.h" + + +/* ----------------------------------------------------------- */ + +static unsigned int i2c_debug; +module_param(i2c_debug, int, 0644); +MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]"); + +#define i2c_dprintk(lvl, fmt, args...) if (i2c_debug >= lvl) do { \ + printk(KERN_DEBUG "%s at %s: " fmt, \ + dev->name, __func__, ##args); } while (0) + +static int tm6000_i2c_send_regs(struct tm6000_core *dev, unsigned char addr, + __u8 reg, char *buf, int len) +{ + int rc; + unsigned int tsleep; + unsigned int i2c_packet_limit = 16; + + if (dev->dev_type == TM6010) + i2c_packet_limit = 64; + + if (!buf) + return -1; + + if (len < 1 || len > i2c_packet_limit) { + printk(KERN_ERR "Incorrect length of i2c packet = %d, limit set to %d\n", + len, i2c_packet_limit); + return -1; + } + + /* capture mutex */ + rc = tm6000_read_write_usb(dev, USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, REQ_16_SET_GET_I2C_WR1_RDN, + addr | reg << 8, 0, buf, len); + + if (rc < 0) { + /* release mutex */ + return rc; + } + + /* Calculate delay time, 14000us for 64 bytes */ + tsleep = ((len * 200) + 200 + 1000) / 1000; + msleep(tsleep); + + /* release mutex */ + return rc; +} + +/* Generic read - doesn't work fine with 16bit registers */ +static int tm6000_i2c_recv_regs(struct tm6000_core *dev, unsigned char addr, + __u8 reg, char *buf, int len) +{ + int rc; + u8 b[2]; + unsigned int i2c_packet_limit = 16; + + if (dev->dev_type == TM6010) + i2c_packet_limit = 64; + + if (!buf) + return -1; + + if (len < 1 || len > i2c_packet_limit) { + printk(KERN_ERR "Incorrect length of i2c packet = %d, limit set to %d\n", + len, i2c_packet_limit); + return -1; + } + + /* capture mutex */ + if ((dev->caps.has_zl10353) && (dev->demod_addr << 1 == addr) && (reg % 2 == 0)) { + /* + * Workaround an I2C bug when reading from zl10353 + */ + reg -= 1; + len += 1; + + rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + REQ_16_SET_GET_I2C_WR1_RDN, addr | reg << 8, 0, b, len); + + *buf = b[1]; + } else { + rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + REQ_16_SET_GET_I2C_WR1_RDN, addr | reg << 8, 0, buf, len); + } + + /* release mutex */ + return rc; +} + +/* + * read from a 16bit register + * for example xc2028, xc3028 or xc3028L + */ +static int tm6000_i2c_recv_regs16(struct tm6000_core *dev, unsigned char addr, + __u16 reg, char *buf, int len) +{ + int rc; + unsigned char ureg; + + if (!buf || len != 2) + return -1; + + /* capture mutex */ + if (dev->dev_type == TM6010) { + ureg = reg & 0xFF; + rc = tm6000_read_write_usb(dev, USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, REQ_16_SET_GET_I2C_WR1_RDN, + addr | (reg & 0xFF00), 0, &ureg, 1); + + if (rc < 0) { + /* release mutex */ + return rc; + } + + msleep(1400 / 1000); + rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, REQ_35_AFTEK_TUNER_READ, + reg, 0, buf, len); + } else { + rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, REQ_14_SET_GET_I2C_WR2_RDN, + addr, reg, buf, len); + } + + /* release mutex */ + return rc; +} + +static int tm6000_i2c_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg msgs[], int num) +{ + struct tm6000_core *dev = i2c_adap->algo_data; + int addr, rc, i, byte; + + if (num <= 0) + return 0; + for (i = 0; i < num; i++) { + addr = (msgs[i].addr << 1) & 0xff; + i2c_dprintk(2, "%s %s addr=0x%x len=%d:", + (msgs[i].flags & I2C_M_RD) ? "read" : "write", + i == num - 1 ? "stop" : "nonstop", addr, msgs[i].len); + if (msgs[i].flags & I2C_M_RD) { + /* read request without preceding register selection */ + /* + * The TM6000 only supports a read transaction + * immediately after a 1 or 2 byte write to select + * a register. We cannot fulfil this request. + */ + i2c_dprintk(2, " read without preceding write not" + " supported"); + rc = -EOPNOTSUPP; + goto err; + } else if (i + 1 < num && msgs[i].len <= 2 && + (msgs[i + 1].flags & I2C_M_RD) && + msgs[i].addr == msgs[i + 1].addr) { + /* 1 or 2 byte write followed by a read */ + if (i2c_debug >= 2) + for (byte = 0; byte < msgs[i].len; byte++) + printk(KERN_CONT " %02x", msgs[i].buf[byte]); + i2c_dprintk(2, "; joined to read %s len=%d:", + i == num - 2 ? "stop" : "nonstop", + msgs[i + 1].len); + + if (msgs[i].len == 2) { + rc = tm6000_i2c_recv_regs16(dev, addr, + msgs[i].buf[0] << 8 | msgs[i].buf[1], + msgs[i + 1].buf, msgs[i + 1].len); + } else { + rc = tm6000_i2c_recv_regs(dev, addr, msgs[i].buf[0], + msgs[i + 1].buf, msgs[i + 1].len); + } + + i++; + + if (addr == dev->tuner_addr << 1) { + tm6000_set_reg(dev, REQ_50_SET_START, 0, 0); + tm6000_set_reg(dev, REQ_51_SET_STOP, 0, 0); + } + if (i2c_debug >= 2) + for (byte = 0; byte < msgs[i].len; byte++) + printk(KERN_CONT " %02x", msgs[i].buf[byte]); + } else { + /* write bytes */ + if (i2c_debug >= 2) + for (byte = 0; byte < msgs[i].len; byte++) + printk(KERN_CONT " %02x", msgs[i].buf[byte]); + rc = tm6000_i2c_send_regs(dev, addr, msgs[i].buf[0], + msgs[i].buf + 1, msgs[i].len - 1); + } + if (i2c_debug >= 2) + printk(KERN_CONT "\n"); + if (rc < 0) + goto err; + } + + return num; +err: + i2c_dprintk(2, " ERROR: %i\n", rc); + return rc; +} + +static int tm6000_i2c_eeprom(struct tm6000_core *dev) +{ + int i, rc; + unsigned char *p = dev->eedata; + unsigned char bytes[17]; + + dev->i2c_client.addr = 0xa0 >> 1; + dev->eedata_size = 0; + + bytes[16] = '\0'; + for (i = 0; i < sizeof(dev->eedata); ) { + *p = i; + rc = tm6000_i2c_recv_regs(dev, 0xa0, i, p, 1); + if (rc < 1) { + if (p == dev->eedata) + goto noeeprom; + else { + printk(KERN_WARNING + "%s: i2c eeprom read error (err=%d)\n", + dev->name, rc); + } + return -EINVAL; + } + dev->eedata_size++; + p++; + if (0 == (i % 16)) + printk(KERN_INFO "%s: i2c eeprom %02x:", dev->name, i); + printk(KERN_CONT " %02x", dev->eedata[i]); + if ((dev->eedata[i] >= ' ') && (dev->eedata[i] <= 'z')) + bytes[i%16] = dev->eedata[i]; + else + bytes[i%16] = '.'; + + i++; + + if (0 == (i % 16)) { + bytes[16] = '\0'; + printk(KERN_CONT " %s\n", bytes); + } + } + if (0 != (i%16)) { + bytes[i%16] = '\0'; + for (i %= 16; i < 16; i++) + printk(KERN_CONT " "); + printk(KERN_CONT " %s\n", bytes); + } + + return 0; + +noeeprom: + printk(KERN_INFO "%s: Huh, no eeprom present (err=%d)?\n", + dev->name, rc); + return -EINVAL; +} + +/* ----------------------------------------------------------- */ + +/* + * functionality() + */ +static u32 functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm tm6000_algo = { + .master_xfer = tm6000_i2c_xfer, + .functionality = functionality, +}; + +/* ----------------------------------------------------------- */ + +/* + * tm6000_i2c_register() + * register i2c bus + */ +int tm6000_i2c_register(struct tm6000_core *dev) +{ + int rc; + + dev->i2c_adap.owner = THIS_MODULE; + dev->i2c_adap.algo = &tm6000_algo; + dev->i2c_adap.dev.parent = &dev->udev->dev; + strlcpy(dev->i2c_adap.name, dev->name, sizeof(dev->i2c_adap.name)); + dev->i2c_adap.algo_data = dev; + i2c_set_adapdata(&dev->i2c_adap, &dev->v4l2_dev); + rc = i2c_add_adapter(&dev->i2c_adap); + if (rc) + return rc; + + dev->i2c_client.adapter = &dev->i2c_adap; + strlcpy(dev->i2c_client.name, "tm6000 internal", I2C_NAME_SIZE); + tm6000_i2c_eeprom(dev); + + return 0; +} + +/* + * tm6000_i2c_unregister() + * unregister i2c_bus + */ +int tm6000_i2c_unregister(struct tm6000_core *dev) +{ + i2c_del_adapter(&dev->i2c_adap); + return 0; +} diff --git a/drivers/media/video/tm6000/tm6000-input.c b/drivers/media/video/tm6000/tm6000-input.c new file mode 100644 index 00000000000..405d12729d0 --- /dev/null +++ b/drivers/media/video/tm6000/tm6000-input.c @@ -0,0 +1,459 @@ +/* + * tm6000-input.c - driver for TM5600/TM6000/TM6010 USB video capture devices + * + * Copyright (C) 2010 Stefan Ringel <stefan.ringel@arcor.de> + * + * 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 version 2 + * + * 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 <linux/module.h> +#include <linux/init.h> +#include <linux/delay.h> + +#include <linux/input.h> +#include <linux/usb.h> + +#include <media/rc-core.h> + +#include "tm6000.h" +#include "tm6000-regs.h" + +static unsigned int ir_debug; +module_param(ir_debug, int, 0644); +MODULE_PARM_DESC(ir_debug, "enable debug message [IR]"); + +static unsigned int enable_ir = 1; +module_param(enable_ir, int, 0644); +MODULE_PARM_DESC(enable_ir, "enable ir (default is enable)"); + +/* number of 50ms for ON-OFF-ON power led */ +/* show IR activity */ +#define PWLED_OFF 2 + +#undef dprintk + +#define dprintk(fmt, arg...) \ + if (ir_debug) { \ + printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg); \ + } + +struct tm6000_ir_poll_result { + u16 rc_data; +}; + +struct tm6000_IR { + struct tm6000_core *dev; + struct rc_dev *rc; + char name[32]; + char phys[32]; + + /* poll expernal decoder */ + int polling; + struct delayed_work work; + u8 wait:1; + u8 key:1; + u8 pwled:1; + u8 pwledcnt; + u16 key_addr; + struct urb *int_urb; + u8 *urb_data; + + int (*get_key) (struct tm6000_IR *, struct tm6000_ir_poll_result *); + + /* IR device properties */ + u64 rc_type; +}; + + +void tm6000_ir_wait(struct tm6000_core *dev, u8 state) +{ + struct tm6000_IR *ir = dev->ir; + + if (!dev->ir) + return; + + if (state) + ir->wait = 1; + else + ir->wait = 0; +} + + +static int tm6000_ir_config(struct tm6000_IR *ir) +{ + struct tm6000_core *dev = ir->dev; + u8 buf[10]; + int rc; + + switch (ir->rc_type) { + case RC_TYPE_NEC: + /* Setup IR decoder for NEC standard 12MHz system clock */ + /* IR_LEADER_CNT = 0.9ms */ + tm6000_set_reg(dev, TM6010_REQ07_RD8_IR_LEADER1, 0xaa); + tm6000_set_reg(dev, TM6010_REQ07_RD8_IR_LEADER0, 0x30); + /* IR_PULSE_CNT = 0.7ms */ + tm6000_set_reg(dev, TM6010_REQ07_RD8_IR_PULSE_CNT1, 0x20); + tm6000_set_reg(dev, TM6010_REQ07_RD8_IR_PULSE_CNT0, 0xd0); + /* Remote WAKEUP = enable */ + tm6000_set_reg(dev, TM6010_REQ07_RE5_REMOTE_WAKEUP, 0xfe); + /* IR_WKUP_SEL = Low byte in decoded IR data */ + tm6000_set_reg(dev, TM6010_REQ07_RD8_IR_WAKEUP_SEL, 0xff); + /* IR_WKU_ADD code */ + tm6000_set_reg(dev, TM6010_REQ07_RD8_IR_WAKEUP_ADD, 0xff); + tm6000_flash_led(dev, 0); + msleep(100); + tm6000_flash_led(dev, 1); + break; + default: + /* hack */ + buf[0] = 0xff; + buf[1] = 0xff; + buf[2] = 0xf2; + buf[3] = 0x2b; + buf[4] = 0x20; + buf[5] = 0x35; + buf[6] = 0x60; + buf[7] = 0x04; + buf[8] = 0xc0; + buf[9] = 0x08; + + rc = tm6000_read_write_usb(dev, USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, REQ_00_SET_IR_VALUE, 0, 0, buf, 0x0a); + msleep(100); + + if (rc < 0) { + printk(KERN_INFO "IR configuration failed"); + return rc; + } + break; + } + + return 0; +} + +static void tm6000_ir_urb_received(struct urb *urb) +{ + struct tm6000_core *dev = urb->context; + struct tm6000_IR *ir = dev->ir; + int rc; + + if (urb->status != 0) + printk(KERN_INFO "not ready\n"); + else if (urb->actual_length > 0) { + memcpy(ir->urb_data, urb->transfer_buffer, urb->actual_length); + + dprintk("data %02x %02x %02x %02x\n", ir->urb_data[0], + ir->urb_data[1], ir->urb_data[2], ir->urb_data[3]); + + ir->key = 1; + } + + rc = usb_submit_urb(urb, GFP_ATOMIC); +} + +static int default_polling_getkey(struct tm6000_IR *ir, + struct tm6000_ir_poll_result *poll_result) +{ + struct tm6000_core *dev = ir->dev; + int rc; + u8 buf[2]; + + if (ir->wait && !&dev->int_in) + return 0; + + if (&dev->int_in) { + switch (ir->rc_type) { + case RC_TYPE_RC5: + poll_result->rc_data = ir->urb_data[0]; + break; + case RC_TYPE_NEC: + if (ir->urb_data[1] == ((ir->key_addr >> 8) & 0xff)) { + poll_result->rc_data = ir->urb_data[0] + | ir->urb_data[1] << 8; + } + break; + default: + poll_result->rc_data = ir->urb_data[0] + | ir->urb_data[1] << 8; + break; + } + } else { + tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 2, 0); + msleep(10); + tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 2, 1); + msleep(10); + + if (ir->rc_type == RC_TYPE_RC5) { + rc = tm6000_read_write_usb(dev, USB_DIR_IN | + USB_TYPE_VENDOR | USB_RECIP_DEVICE, + REQ_02_GET_IR_CODE, 0, 0, buf, 1); + + msleep(10); + + dprintk("read data=%02x\n", buf[0]); + if (rc < 0) + return rc; + + poll_result->rc_data = buf[0]; + } else { + rc = tm6000_read_write_usb(dev, USB_DIR_IN | + USB_TYPE_VENDOR | USB_RECIP_DEVICE, + REQ_02_GET_IR_CODE, 0, 0, buf, 2); + + msleep(10); + + dprintk("read data=%04x\n", buf[0] | buf[1] << 8); + if (rc < 0) + return rc; + + poll_result->rc_data = buf[0] | buf[1] << 8; + } + if ((poll_result->rc_data & 0x00ff) != 0xff) + ir->key = 1; + } + return 0; +} + +static void tm6000_ir_handle_key(struct tm6000_IR *ir) +{ + struct tm6000_core *dev = ir->dev; + int result; + struct tm6000_ir_poll_result poll_result; + + /* read the registers containing the IR status */ + result = ir->get_key(ir, &poll_result); + if (result < 0) { + printk(KERN_INFO "ir->get_key() failed %d\n", result); + return; + } + + dprintk("ir->get_key result data=%04x\n", poll_result.rc_data); + + if (ir->pwled) { + if (ir->pwledcnt >= PWLED_OFF) { + ir->pwled = 0; + ir->pwledcnt = 0; + tm6000_flash_led(dev, 1); + } else + ir->pwledcnt += 1; + } + + if (ir->key) { + rc_keydown(ir->rc, poll_result.rc_data, 0); + ir->key = 0; + ir->pwled = 1; + ir->pwledcnt = 0; + tm6000_flash_led(dev, 0); + } + return; +} + +static void tm6000_ir_work(struct work_struct *work) +{ + struct tm6000_IR *ir = container_of(work, struct tm6000_IR, work.work); + + tm6000_ir_handle_key(ir); + schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling)); +} + +static int tm6000_ir_start(struct rc_dev *rc) +{ + struct tm6000_IR *ir = rc->priv; + + INIT_DELAYED_WORK(&ir->work, tm6000_ir_work); + schedule_delayed_work(&ir->work, 0); + + return 0; +} + +static void tm6000_ir_stop(struct rc_dev *rc) +{ + struct tm6000_IR *ir = rc->priv; + + cancel_delayed_work_sync(&ir->work); +} + +static int tm6000_ir_change_protocol(struct rc_dev *rc, u64 rc_type) +{ + struct tm6000_IR *ir = rc->priv; + + if (!ir) + return 0; + + if ((rc->rc_map.scan) && (rc_type == RC_TYPE_NEC)) + ir->key_addr = ((rc->rc_map.scan[0].scancode >> 8) & 0xffff); + + ir->get_key = default_polling_getkey; + ir->rc_type = rc_type; + + tm6000_ir_config(ir); + /* TODO */ + return 0; +} + +int tm6000_ir_int_start(struct tm6000_core *dev) +{ + struct tm6000_IR *ir = dev->ir; + int pipe, size; + int err = -ENOMEM; + + + if (!ir) + return -ENODEV; + + ir->int_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!ir->int_urb) + return -ENOMEM; + + pipe = usb_rcvintpipe(dev->udev, + dev->int_in.endp->desc.bEndpointAddress + & USB_ENDPOINT_NUMBER_MASK); + + size = usb_maxpacket(dev->udev, pipe, usb_pipeout(pipe)); + dprintk("IR max size: %d\n", size); + + ir->int_urb->transfer_buffer = kzalloc(size, GFP_KERNEL); + if (ir->int_urb->transfer_buffer == NULL) { + usb_free_urb(ir->int_urb); + return err; + } + dprintk("int interval: %d\n", dev->int_in.endp->desc.bInterval); + usb_fill_int_urb(ir->int_urb, dev->udev, pipe, + ir->int_urb->transfer_buffer, size, + tm6000_ir_urb_received, dev, + dev->int_in.endp->desc.bInterval); + err = usb_submit_urb(ir->int_urb, GFP_KERNEL); + if (err) { + kfree(ir->int_urb->transfer_buffer); + usb_free_urb(ir->int_urb); + return err; + } + ir->urb_data = kzalloc(size, GFP_KERNEL); + + return 0; +} + +void tm6000_ir_int_stop(struct tm6000_core *dev) +{ + struct tm6000_IR *ir = dev->ir; + + if (!ir) + return; + + usb_kill_urb(ir->int_urb); + kfree(ir->int_urb->transfer_buffer); + usb_free_urb(ir->int_urb); + ir->int_urb = NULL; + kfree(ir->urb_data); + ir->urb_data = NULL; +} + +int tm6000_ir_init(struct tm6000_core *dev) +{ + struct tm6000_IR *ir; + struct rc_dev *rc; + int err = -ENOMEM; + + if (!enable_ir) + return -ENODEV; + + if (!dev->caps.has_remote) + return 0; + + if (!dev->ir_codes) + return 0; + + ir = kzalloc(sizeof(*ir), GFP_KERNEL); + rc = rc_allocate_device(); + if (!ir || !rc) + goto out; + + /* record handles to ourself */ + ir->dev = dev; + dev->ir = ir; + ir->rc = rc; + + /* input einrichten */ + rc->allowed_protos = RC_TYPE_RC5 | RC_TYPE_NEC; + rc->priv = ir; + rc->change_protocol = tm6000_ir_change_protocol; + rc->open = tm6000_ir_start; + rc->close = tm6000_ir_stop; + rc->driver_type = RC_DRIVER_SCANCODE; + + ir->polling = 50; + ir->pwled = 0; + ir->pwledcnt = 0; + + + snprintf(ir->name, sizeof(ir->name), "tm5600/60x0 IR (%s)", + dev->name); + + usb_make_path(dev->udev, ir->phys, sizeof(ir->phys)); + strlcat(ir->phys, "/input0", sizeof(ir->phys)); + + tm6000_ir_change_protocol(rc, RC_TYPE_UNKNOWN); + + rc->input_name = ir->name; + rc->input_phys = ir->phys; + rc->input_id.bustype = BUS_USB; + rc->input_id.version = 1; + rc->input_id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor); + rc->input_id.product = le16_to_cpu(dev->udev->descriptor.idProduct); + rc->map_name = dev->ir_codes; + rc->driver_name = "tm6000"; + rc->dev.parent = &dev->udev->dev; + + if (&dev->int_in) { + dprintk("IR over int\n"); + + err = tm6000_ir_int_start(dev); + + if (err) + goto out; + } + + /* ir register */ + err = rc_register_device(rc); + if (err) + goto out; + + return 0; + +out: + dev->ir = NULL; + rc_free_device(rc); + kfree(ir); + return err; +} + +int tm6000_ir_fini(struct tm6000_core *dev) +{ + struct tm6000_IR *ir = dev->ir; + + /* skip detach on non attached board */ + + if (!ir) + return 0; + + rc_unregister_device(ir->rc); + + if (ir->int_urb) + tm6000_ir_int_stop(dev); + + kfree(ir); + dev->ir = NULL; + + return 0; +} diff --git a/drivers/media/video/tm6000/tm6000-regs.h b/drivers/media/video/tm6000/tm6000-regs.h new file mode 100644 index 00000000000..7f491b6de93 --- /dev/null +++ b/drivers/media/video/tm6000/tm6000-regs.h @@ -0,0 +1,600 @@ +/* + * tm6000-regs.h - driver for TM5600/TM6000/TM6010 USB video capture devices + * + * Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org> + * + * 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 version 2 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * Define TV Master TM5600/TM6000/TM6010 Request codes + */ +#define REQ_00_SET_IR_VALUE 0 +#define REQ_01_SET_WAKEUP_IRCODE 1 +#define REQ_02_GET_IR_CODE 2 +#define REQ_03_SET_GET_MCU_PIN 3 +#define REQ_04_EN_DISABLE_MCU_INT 4 +#define REQ_05_SET_GET_USBREG 5 + /* Write: RegNum, Value, 0 */ + /* Read : RegNum, Value, 1, RegStatus */ +#define REQ_06_SET_GET_USBREG_BIT 6 +#define REQ_07_SET_GET_AVREG 7 + /* Write: RegNum, Value, 0 */ + /* Read : RegNum, Value, 1, RegStatus */ +#define REQ_08_SET_GET_AVREG_BIT 8 +#define REQ_09_SET_GET_TUNER_FQ 9 +#define REQ_10_SET_TUNER_SYSTEM 10 +#define REQ_11_SET_EEPROM_ADDR 11 +#define REQ_12_SET_GET_EEPROMBYTE 12 +#define REQ_13_GET_EEPROM_SEQREAD 13 +#define REQ_14_SET_GET_I2C_WR2_RDN 14 +#define REQ_15_SET_GET_I2CBYTE 15 + /* Write: Subaddr, Slave Addr, value, 0 */ + /* Read : Subaddr, Slave Addr, value, 1 */ +#define REQ_16_SET_GET_I2C_WR1_RDN 16 + /* Subaddr, Slave Addr, 0, length */ +#define REQ_17_SET_GET_I2CFP 17 + /* Write: Slave Addr, register, value */ + /* Read : Slave Addr, register, 2, data */ +#define REQ_20_DATA_TRANSFER 20 +#define REQ_30_I2C_WRITE 30 +#define REQ_31_I2C_READ 31 +#define REQ_35_AFTEK_TUNER_READ 35 +#define REQ_40_GET_VERSION 40 +#define REQ_50_SET_START 50 +#define REQ_51_SET_STOP 51 +#define REQ_52_TRANSMIT_DATA 52 +#define REQ_53_SPI_INITIAL 53 +#define REQ_54_SPI_SETSTART 54 +#define REQ_55_SPI_INOUTDATA 55 +#define REQ_56_SPI_SETSTOP 56 + +/* + * Define TV Master TM5600/TM6000/TM6010 GPIO lines + */ + +#define TM6000_GPIO_CLK 0x101 +#define TM6000_GPIO_DATA 0x100 + +#define TM6000_GPIO_1 0x102 +#define TM6000_GPIO_2 0x103 +#define TM6000_GPIO_3 0x104 +#define TM6000_GPIO_4 0x300 +#define TM6000_GPIO_5 0x301 +#define TM6000_GPIO_6 0x304 +#define TM6000_GPIO_7 0x305 + +/* tm6010 defines GPIO with different values */ +#define TM6010_GPIO_0 0x0102 +#define TM6010_GPIO_1 0x0103 +#define TM6010_GPIO_2 0x0104 +#define TM6010_GPIO_3 0x0105 +#define TM6010_GPIO_4 0x0106 +#define TM6010_GPIO_5 0x0107 +#define TM6010_GPIO_6 0x0300 +#define TM6010_GPIO_7 0x0301 +#define TM6010_GPIO_9 0x0305 +/* + * Define TV Master TM5600/TM6000/TM6010 URB message codes and length + */ + +enum { + TM6000_URB_MSG_VIDEO = 1, + TM6000_URB_MSG_AUDIO, + TM6000_URB_MSG_VBI, + TM6000_URB_MSG_PTS, + TM6000_URB_MSG_ERR, +}; + +/* Define specific TM6000 Video decoder registers */ +#define TM6000_REQ07_RD8_TEST_SEL 0x07, 0xd8 +#define TM6000_REQ07_RD9_A_SIM_SEL 0x07, 0xd9 +#define TM6000_REQ07_RDA_CLK_SEL 0x07, 0xda +#define TM6000_REQ07_RDB_OUT_SEL 0x07, 0xdb +#define TM6000_REQ07_RDC_NSEL_I2S 0x07, 0xdc +#define TM6000_REQ07_RDD_GPIO2_MDRV 0x07, 0xdd +#define TM6000_REQ07_RDE_GPIO1_MDRV 0x07, 0xde +#define TM6000_REQ07_RDF_PWDOWN_ACLK 0x07, 0xdf +#define TM6000_REQ07_RE0_VADC_REF_CTL 0x07, 0xe0 +#define TM6000_REQ07_RE1_VADC_DACLIMP 0x07, 0xe1 +#define TM6000_REQ07_RE2_VADC_STATUS_CTL 0x07, 0xe2 +#define TM6000_REQ07_RE3_VADC_INP_LPF_SEL1 0x07, 0xe3 +#define TM6000_REQ07_RE4_VADC_TARGET1 0x07, 0xe4 +#define TM6000_REQ07_RE5_VADC_INP_LPF_SEL2 0x07, 0xe5 +#define TM6000_REQ07_RE6_VADC_TARGET2 0x07, 0xe6 +#define TM6000_REQ07_RE7_VADC_AGAIN_CTL 0x07, 0xe7 +#define TM6000_REQ07_RE8_VADC_PWDOWN_CTL 0x07, 0xe8 +#define TM6000_REQ07_RE9_VADC_INPUT_CTL1 0x07, 0xe9 +#define TM6000_REQ07_REA_VADC_INPUT_CTL2 0x07, 0xea +#define TM6000_REQ07_REB_VADC_AADC_MODE 0x07, 0xeb +#define TM6000_REQ07_REC_VADC_AADC_LVOL 0x07, 0xec +#define TM6000_REQ07_RED_VADC_AADC_RVOL 0x07, 0xed +#define TM6000_REQ07_REE_VADC_CTRL_SEL_CONTROL 0x07, 0xee +#define TM6000_REQ07_REF_VADC_GAIN_MAP_CTL 0x07, 0xef +#define TM6000_REQ07_RFD_BIST_ERR_VST_LOW 0x07, 0xfd +#define TM6000_REQ07_RFE_BIST_ERR_VST_HIGH 0x07, 0xfe + +/* Define TM6000/TM6010 Video decoder registers */ +#define TM6010_REQ07_R00_VIDEO_CONTROL0 0x07, 0x00 +#define TM6010_REQ07_R01_VIDEO_CONTROL1 0x07, 0x01 +#define TM6010_REQ07_R02_VIDEO_CONTROL2 0x07, 0x02 +#define TM6010_REQ07_R03_YC_SEP_CONTROL 0x07, 0x03 +#define TM6010_REQ07_R04_LUMA_HAGC_CONTROL 0x07, 0x04 +#define TM6010_REQ07_R05_NOISE_THRESHOLD 0x07, 0x05 +#define TM6010_REQ07_R06_AGC_GATE_THRESHOLD 0x07, 0x06 +#define TM6010_REQ07_R07_OUTPUT_CONTROL 0x07, 0x07 +#define TM6010_REQ07_R08_LUMA_CONTRAST_ADJ 0x07, 0x08 +#define TM6010_REQ07_R09_LUMA_BRIGHTNESS_ADJ 0x07, 0x09 +#define TM6010_REQ07_R0A_CHROMA_SATURATION_ADJ 0x07, 0x0a +#define TM6010_REQ07_R0B_CHROMA_HUE_PHASE_ADJ 0x07, 0x0b +#define TM6010_REQ07_R0C_CHROMA_AGC_CONTROL 0x07, 0x0c +#define TM6010_REQ07_R0D_CHROMA_KILL_LEVEL 0x07, 0x0d +#define TM6010_REQ07_R0F_CHROMA_AUTO_POSITION 0x07, 0x0f +#define TM6010_REQ07_R10_AGC_PEAK_NOMINAL 0x07, 0x10 +#define TM6010_REQ07_R11_AGC_PEAK_CONTROL 0x07, 0x11 +#define TM6010_REQ07_R12_AGC_GATE_STARTH 0x07, 0x12 +#define TM6010_REQ07_R13_AGC_GATE_STARTL 0x07, 0x13 +#define TM6010_REQ07_R14_AGC_GATE_WIDTH 0x07, 0x14 +#define TM6010_REQ07_R15_AGC_BP_DELAY 0x07, 0x15 +#define TM6010_REQ07_R16_LOCK_COUNT 0x07, 0x16 +#define TM6010_REQ07_R17_HLOOP_MAXSTATE 0x07, 0x17 +#define TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3 0x07, 0x18 +#define TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2 0x07, 0x19 +#define TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1 0x07, 0x1a +#define TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0 0x07, 0x1b +#define TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3 0x07, 0x1c +#define TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2 0x07, 0x1d +#define TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1 0x07, 0x1e +#define TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0 0x07, 0x1f +#define TM6010_REQ07_R20_HSYNC_RISING_EDGE_TIME 0x07, 0x20 +#define TM6010_REQ07_R21_HSYNC_PHASE_OFFSET 0x07, 0x21 +#define TM6010_REQ07_R22_HSYNC_PLL_START_TIME 0x07, 0x22 +#define TM6010_REQ07_R23_HSYNC_PLL_END_TIME 0x07, 0x23 +#define TM6010_REQ07_R24_HSYNC_TIP_START_TIME 0x07, 0x24 +#define TM6010_REQ07_R25_HSYNC_TIP_END_TIME 0x07, 0x25 +#define TM6010_REQ07_R26_HSYNC_RISING_EDGE_START 0x07, 0x26 +#define TM6010_REQ07_R27_HSYNC_RISING_EDGE_END 0x07, 0x27 +#define TM6010_REQ07_R28_BACKPORCH_START 0x07, 0x28 +#define TM6010_REQ07_R29_BACKPORCH_END 0x07, 0x29 +#define TM6010_REQ07_R2A_HSYNC_FILTER_START 0x07, 0x2a +#define TM6010_REQ07_R2B_HSYNC_FILTER_END 0x07, 0x2b +#define TM6010_REQ07_R2C_CHROMA_BURST_START 0x07, 0x2c +#define TM6010_REQ07_R2D_CHROMA_BURST_END 0x07, 0x2d +#define TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART 0x07, 0x2e +#define TM6010_REQ07_R2F_ACTIVE_VIDEO_HWIDTH 0x07, 0x2f +#define TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART 0x07, 0x30 +#define TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT 0x07, 0x31 +#define TM6010_REQ07_R32_VSYNC_HLOCK_MIN 0x07, 0x32 +#define TM6010_REQ07_R33_VSYNC_HLOCK_MAX 0x07, 0x33 +#define TM6010_REQ07_R34_VSYNC_AGC_MIN 0x07, 0x34 +#define TM6010_REQ07_R35_VSYNC_AGC_MAX 0x07, 0x35 +#define TM6010_REQ07_R36_VSYNC_VBI_MIN 0x07, 0x36 +#define TM6010_REQ07_R37_VSYNC_VBI_MAX 0x07, 0x37 +#define TM6010_REQ07_R38_VSYNC_THRESHOLD 0x07, 0x38 +#define TM6010_REQ07_R39_VSYNC_TIME_CONSTANT 0x07, 0x39 +#define TM6010_REQ07_R3A_STATUS1 0x07, 0x3a +#define TM6010_REQ07_R3B_STATUS2 0x07, 0x3b +#define TM6010_REQ07_R3C_STATUS3 0x07, 0x3c +#define TM6010_REQ07_R3F_RESET 0x07, 0x3f +#define TM6010_REQ07_R40_TELETEXT_VBI_CODE0 0x07, 0x40 +#define TM6010_REQ07_R41_TELETEXT_VBI_CODE1 0x07, 0x41 +#define TM6010_REQ07_R42_VBI_DATA_HIGH_LEVEL 0x07, 0x42 +#define TM6010_REQ07_R43_VBI_DATA_TYPE_LINE7 0x07, 0x43 +#define TM6010_REQ07_R44_VBI_DATA_TYPE_LINE8 0x07, 0x44 +#define TM6010_REQ07_R45_VBI_DATA_TYPE_LINE9 0x07, 0x45 +#define TM6010_REQ07_R46_VBI_DATA_TYPE_LINE10 0x07, 0x46 +#define TM6010_REQ07_R47_VBI_DATA_TYPE_LINE11 0x07, 0x47 +#define TM6010_REQ07_R48_VBI_DATA_TYPE_LINE12 0x07, 0x48 +#define TM6010_REQ07_R49_VBI_DATA_TYPE_LINE13 0x07, 0x49 +#define TM6010_REQ07_R4A_VBI_DATA_TYPE_LINE14 0x07, 0x4a +#define TM6010_REQ07_R4B_VBI_DATA_TYPE_LINE15 0x07, 0x4b +#define TM6010_REQ07_R4C_VBI_DATA_TYPE_LINE16 0x07, 0x4c +#define TM6010_REQ07_R4D_VBI_DATA_TYPE_LINE17 0x07, 0x4d +#define TM6010_REQ07_R4E_VBI_DATA_TYPE_LINE18 0x07, 0x4e +#define TM6010_REQ07_R4F_VBI_DATA_TYPE_LINE19 0x07, 0x4f +#define TM6010_REQ07_R50_VBI_DATA_TYPE_LINE20 0x07, 0x50 +#define TM6010_REQ07_R51_VBI_DATA_TYPE_LINE21 0x07, 0x51 +#define TM6010_REQ07_R52_VBI_DATA_TYPE_LINE22 0x07, 0x52 +#define TM6010_REQ07_R53_VBI_DATA_TYPE_LINE23 0x07, 0x53 +#define TM6010_REQ07_R54_VBI_DATA_TYPE_RLINES 0x07, 0x54 +#define TM6010_REQ07_R55_VBI_LOOP_FILTER_GAIN 0x07, 0x55 +#define TM6010_REQ07_R56_VBI_LOOP_FILTER_I_GAIN 0x07, 0x56 +#define TM6010_REQ07_R57_VBI_LOOP_FILTER_P_GAIN 0x07, 0x57 +#define TM6010_REQ07_R58_VBI_CAPTION_DTO1 0x07, 0x58 +#define TM6010_REQ07_R59_VBI_CAPTION_DTO0 0x07, 0x59 +#define TM6010_REQ07_R5A_VBI_TELETEXT_DTO1 0x07, 0x5a +#define TM6010_REQ07_R5B_VBI_TELETEXT_DTO0 0x07, 0x5b +#define TM6010_REQ07_R5C_VBI_WSS625_DTO1 0x07, 0x5c +#define TM6010_REQ07_R5D_VBI_WSS625_DTO0 0x07, 0x5d +#define TM6010_REQ07_R5E_VBI_CAPTION_FRAME_START 0x07, 0x5e +#define TM6010_REQ07_R5F_VBI_WSS625_FRAME_START 0x07, 0x5f +#define TM6010_REQ07_R60_TELETEXT_FRAME_START 0x07, 0x60 +#define TM6010_REQ07_R61_VBI_CCDATA1 0x07, 0x61 +#define TM6010_REQ07_R62_VBI_CCDATA2 0x07, 0x62 +#define TM6010_REQ07_R63_VBI_WSS625_DATA1 0x07, 0x63 +#define TM6010_REQ07_R64_VBI_WSS625_DATA2 0x07, 0x64 +#define TM6010_REQ07_R65_VBI_DATA_STATUS 0x07, 0x65 +#define TM6010_REQ07_R66_VBI_CAPTION_START 0x07, 0x66 +#define TM6010_REQ07_R67_VBI_WSS625_START 0x07, 0x67 +#define TM6010_REQ07_R68_VBI_TELETEXT_START 0x07, 0x68 +#define TM6010_REQ07_R70_HSYNC_DTO_INC_STATUS3 0x07, 0x70 +#define TM6010_REQ07_R71_HSYNC_DTO_INC_STATUS2 0x07, 0x71 +#define TM6010_REQ07_R72_HSYNC_DTO_INC_STATUS1 0x07, 0x72 +#define TM6010_REQ07_R73_HSYNC_DTO_INC_STATUS0 0x07, 0x73 +#define TM6010_REQ07_R74_CHROMA_DTO_INC_STATUS3 0x07, 0x74 +#define TM6010_REQ07_R75_CHROMA_DTO_INC_STATUS2 0x07, 0x75 +#define TM6010_REQ07_R76_CHROMA_DTO_INC_STATUS1 0x07, 0x76 +#define TM6010_REQ07_R77_CHROMA_DTO_INC_STATUS0 0x07, 0x77 +#define TM6010_REQ07_R78_AGC_AGAIN_STATUS 0x07, 0x78 +#define TM6010_REQ07_R79_AGC_DGAIN_STATUS 0x07, 0x79 +#define TM6010_REQ07_R7A_CHROMA_MAG_STATUS 0x07, 0x7a +#define TM6010_REQ07_R7B_CHROMA_GAIN_STATUS1 0x07, 0x7b +#define TM6010_REQ07_R7C_CHROMA_GAIN_STATUS0 0x07, 0x7c +#define TM6010_REQ07_R7D_CORDIC_FREQ_STATUS 0x07, 0x7d +#define TM6010_REQ07_R7F_STATUS_NOISE 0x07, 0x7f +#define TM6010_REQ07_R80_COMB_FILTER_TRESHOLD 0x07, 0x80 +#define TM6010_REQ07_R82_COMB_FILTER_CONFIG 0x07, 0x82 +#define TM6010_REQ07_R83_CHROMA_LOCK_CONFIG 0x07, 0x83 +#define TM6010_REQ07_R84_NOISE_NTSC_C 0x07, 0x84 +#define TM6010_REQ07_R85_NOISE_PAL_C 0x07, 0x85 +#define TM6010_REQ07_R86_NOISE_PHASE_C 0x07, 0x86 +#define TM6010_REQ07_R87_NOISE_PHASE_Y 0x07, 0x87 +#define TM6010_REQ07_R8A_CHROMA_LOOPFILTER_STATE 0x07, 0x8a +#define TM6010_REQ07_R8B_CHROMA_HRESAMPLER 0x07, 0x8b +#define TM6010_REQ07_R8D_CPUMP_DELAY_ADJ 0x07, 0x8d +#define TM6010_REQ07_R8E_CPUMP_ADJ 0x07, 0x8e +#define TM6010_REQ07_R8F_CPUMP_DELAY 0x07, 0x8f + +/* Define TM6000/TM6010 Miscellaneous registers */ +#define TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE 0x07, 0xc0 +#define TM6010_REQ07_RC1_TRESHOLD 0x07, 0xc1 +#define TM6010_REQ07_RC2_HSYNC_WIDTH 0x07, 0xc2 +#define TM6010_REQ07_RC3_HSTART1 0x07, 0xc3 +#define TM6010_REQ07_RC4_HSTART0 0x07, 0xc4 +#define TM6010_REQ07_RC5_HEND1 0x07, 0xc5 +#define TM6010_REQ07_RC6_HEND0 0x07, 0xc6 +#define TM6010_REQ07_RC7_VSTART1 0x07, 0xc7 +#define TM6010_REQ07_RC8_VSTART0 0x07, 0xc8 +#define TM6010_REQ07_RC9_VEND1 0x07, 0xc9 +#define TM6010_REQ07_RCA_VEND0 0x07, 0xca +#define TM6010_REQ07_RCB_DELAY 0x07, 0xcb +/* ONLY for TM6010 */ +#define TM6010_REQ07_RCC_ACTIVE_IF 0x07, 0xcc +#define TM6010_REQ07_RCC_ACTIVE_IF_VIDEO_ENABLE (1 << 5) +#define TM6010_REQ07_RCC_ACTIVE_IF_AUDIO_ENABLE (1 << 6) +#define TM6010_REQ07_RD0_USB_PERIPHERY_CONTROL 0x07, 0xd0 +#define TM6010_REQ07_RD1_ADDR_FOR_REQ1 0x07, 0xd1 +#define TM6010_REQ07_RD2_ADDR_FOR_REQ2 0x07, 0xd2 +#define TM6010_REQ07_RD3_ADDR_FOR_REQ3 0x07, 0xd3 +#define TM6010_REQ07_RD4_ADDR_FOR_REQ4 0x07, 0xd4 +#define TM6010_REQ07_RD5_POWERSAVE 0x07, 0xd5 +#define TM6010_REQ07_RD6_ENDP_REQ1_REQ2 0x07, 0xd6 +#define TM6010_REQ07_RD7_ENDP_REQ3_REQ4 0x07, 0xd7 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RD8_IR 0x07, 0xd8 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RD8_IR_BSIZE 0x07, 0xd9 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RD8_IR_WAKEUP_SEL 0x07, 0xda +/* ONLY for TM6010 */ +#define TM6010_REQ07_RD8_IR_WAKEUP_ADD 0x07, 0xdb +/* ONLY for TM6010 */ +#define TM6010_REQ07_RD8_IR_LEADER1 0x07, 0xdc +/* ONLY for TM6010 */ +#define TM6010_REQ07_RD8_IR_LEADER0 0x07, 0xdd +/* ONLY for TM6010 */ +#define TM6010_REQ07_RD8_IR_PULSE_CNT1 0x07, 0xde +/* ONLY for TM6010 */ +#define TM6010_REQ07_RD8_IR_PULSE_CNT0 0x07, 0xdf +/* ONLY for TM6010 */ +#define TM6010_REQ07_RE0_DVIDEO_SOURCE 0x07, 0xe0 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RE0_DVIDEO_SOURCE_IF 0x07, 0xe1 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RE2_OUT_SEL2 0x07, 0xe2 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RE3_OUT_SEL1 0x07, 0xe3 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RE4_OUT_SEL0 0x07, 0xe4 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RE5_REMOTE_WAKEUP 0x07, 0xe5 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RE7_PUB_GPIO 0x07, 0xe7 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RE8_TYPESEL_MOS_I2S 0x07, 0xe8 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RE9_TYPESEL_MOS_TS 0x07, 0xe9 +/* ONLY for TM6010 */ +#define TM6010_REQ07_REA_TYPESEL_MOS_CCIR 0x07, 0xea +/* ONLY for TM6010 */ +#define TM6010_REQ07_RF0_BIST_CRC_RESULT0 0x07, 0xf0 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RF1_BIST_CRC_RESULT1 0x07, 0xf1 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RF2_BIST_CRC_RESULT2 0x07, 0xf2 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RF3_BIST_CRC_RESULT3 0x07, 0xf3 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RF4_BIST_ERR_VST2 0x07, 0xf4 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RF5_BIST_ERR_VST1 0x07, 0xf5 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RF6_BIST_ERR_VST0 0x07, 0xf6 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RF7_BIST 0x07, 0xf7 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RFE_POWER_DOWN 0x07, 0xfe +#define TM6010_REQ07_RFF_SOFT_RESET 0x07, 0xff + +/* Define TM6000/TM6010 USB registers */ +#define TM6010_REQ05_R00_MAIN_CTRL 0x05, 0x00 +#define TM6010_REQ05_R01_DEVADDR 0x05, 0x01 +#define TM6010_REQ05_R02_TEST 0x05, 0x02 +#define TM6010_REQ05_R04_SOFN0 0x05, 0x04 +#define TM6010_REQ05_R05_SOFN1 0x05, 0x05 +#define TM6010_REQ05_R06_SOFTM0 0x05, 0x06 +#define TM6010_REQ05_R07_SOFTM1 0x05, 0x07 +#define TM6010_REQ05_R08_PHY_TEST 0x05, 0x08 +#define TM6010_REQ05_R09_VCTL 0x05, 0x09 +#define TM6010_REQ05_R0A_VSTA 0x05, 0x0a +#define TM6010_REQ05_R0B_CX_CFG 0x05, 0x0b +#define TM6010_REQ05_R0C_ENDP0_REG0 0x05, 0x0c +#define TM6010_REQ05_R10_GMASK 0x05, 0x10 +#define TM6010_REQ05_R11_IMASK0 0x05, 0x11 +#define TM6010_REQ05_R12_IMASK1 0x05, 0x12 +#define TM6010_REQ05_R13_IMASK2 0x05, 0x13 +#define TM6010_REQ05_R14_IMASK3 0x05, 0x14 +#define TM6010_REQ05_R15_IMASK4 0x05, 0x15 +#define TM6010_REQ05_R16_IMASK5 0x05, 0x16 +#define TM6010_REQ05_R17_IMASK6 0x05, 0x17 +#define TM6010_REQ05_R18_IMASK7 0x05, 0x18 +#define TM6010_REQ05_R19_ZEROP0 0x05, 0x19 +#define TM6010_REQ05_R1A_ZEROP1 0x05, 0x1a +#define TM6010_REQ05_R1C_FIFO_EMP0 0x05, 0x1c +#define TM6010_REQ05_R1D_FIFO_EMP1 0x05, 0x1d +#define TM6010_REQ05_R20_IRQ_GROUP 0x05, 0x20 +#define TM6010_REQ05_R21_IRQ_SOURCE0 0x05, 0x21 +#define TM6010_REQ05_R22_IRQ_SOURCE1 0x05, 0x22 +#define TM6010_REQ05_R23_IRQ_SOURCE2 0x05, 0x23 +#define TM6010_REQ05_R24_IRQ_SOURCE3 0x05, 0x24 +#define TM6010_REQ05_R25_IRQ_SOURCE4 0x05, 0x25 +#define TM6010_REQ05_R26_IRQ_SOURCE5 0x05, 0x26 +#define TM6010_REQ05_R27_IRQ_SOURCE6 0x05, 0x27 +#define TM6010_REQ05_R28_IRQ_SOURCE7 0x05, 0x28 +#define TM6010_REQ05_R29_SEQ_ERR0 0x05, 0x29 +#define TM6010_REQ05_R2A_SEQ_ERR1 0x05, 0x2a +#define TM6010_REQ05_R2B_SEQ_ABORT0 0x05, 0x2b +#define TM6010_REQ05_R2C_SEQ_ABORT1 0x05, 0x2c +#define TM6010_REQ05_R2D_TX_ZERO0 0x05, 0x2d +#define TM6010_REQ05_R2E_TX_ZERO1 0x05, 0x2e +#define TM6010_REQ05_R2F_IDLE_CNT 0x05, 0x2f +#define TM6010_REQ05_R30_FNO_P1 0x05, 0x30 +#define TM6010_REQ05_R31_FNO_P2 0x05, 0x31 +#define TM6010_REQ05_R32_FNO_P3 0x05, 0x32 +#define TM6010_REQ05_R33_FNO_P4 0x05, 0x33 +#define TM6010_REQ05_R34_FNO_P5 0x05, 0x34 +#define TM6010_REQ05_R35_FNO_P6 0x05, 0x35 +#define TM6010_REQ05_R36_FNO_P7 0x05, 0x36 +#define TM6010_REQ05_R37_FNO_P8 0x05, 0x37 +#define TM6010_REQ05_R38_FNO_P9 0x05, 0x38 +#define TM6010_REQ05_R30_FNO_P10 0x05, 0x39 +#define TM6010_REQ05_R30_FNO_P11 0x05, 0x3a +#define TM6010_REQ05_R30_FNO_P12 0x05, 0x3b +#define TM6010_REQ05_R30_FNO_P13 0x05, 0x3c +#define TM6010_REQ05_R30_FNO_P14 0x05, 0x3d +#define TM6010_REQ05_R30_FNO_P15 0x05, 0x3e +#define TM6010_REQ05_R40_IN_MAXPS_LOW1 0x05, 0x40 +#define TM6010_REQ05_R41_IN_MAXPS_HIGH1 0x05, 0x41 +#define TM6010_REQ05_R42_IN_MAXPS_LOW2 0x05, 0x42 +#define TM6010_REQ05_R43_IN_MAXPS_HIGH2 0x05, 0x43 +#define TM6010_REQ05_R44_IN_MAXPS_LOW3 0x05, 0x44 +#define TM6010_REQ05_R45_IN_MAXPS_HIGH3 0x05, 0x45 +#define TM6010_REQ05_R46_IN_MAXPS_LOW4 0x05, 0x46 +#define TM6010_REQ05_R47_IN_MAXPS_HIGH4 0x05, 0x47 +#define TM6010_REQ05_R48_IN_MAXPS_LOW5 0x05, 0x48 +#define TM6010_REQ05_R49_IN_MAXPS_HIGH5 0x05, 0x49 +#define TM6010_REQ05_R4A_IN_MAXPS_LOW6 0x05, 0x4a +#define TM6010_REQ05_R4B_IN_MAXPS_HIGH6 0x05, 0x4b +#define TM6010_REQ05_R4C_IN_MAXPS_LOW7 0x05, 0x4c +#define TM6010_REQ05_R4D_IN_MAXPS_HIGH7 0x05, 0x4d +#define TM6010_REQ05_R4E_IN_MAXPS_LOW8 0x05, 0x4e +#define TM6010_REQ05_R4F_IN_MAXPS_HIGH8 0x05, 0x4f +#define TM6010_REQ05_R50_IN_MAXPS_LOW9 0x05, 0x50 +#define TM6010_REQ05_R51_IN_MAXPS_HIGH9 0x05, 0x51 +#define TM6010_REQ05_R40_IN_MAXPS_LOW10 0x05, 0x52 +#define TM6010_REQ05_R41_IN_MAXPS_HIGH10 0x05, 0x53 +#define TM6010_REQ05_R40_IN_MAXPS_LOW11 0x05, 0x54 +#define TM6010_REQ05_R41_IN_MAXPS_HIGH11 0x05, 0x55 +#define TM6010_REQ05_R40_IN_MAXPS_LOW12 0x05, 0x56 +#define TM6010_REQ05_R41_IN_MAXPS_HIGH12 0x05, 0x57 +#define TM6010_REQ05_R40_IN_MAXPS_LOW13 0x05, 0x58 +#define TM6010_REQ05_R41_IN_MAXPS_HIGH13 0x05, 0x59 +#define TM6010_REQ05_R40_IN_MAXPS_LOW14 0x05, 0x5a +#define TM6010_REQ05_R41_IN_MAXPS_HIGH14 0x05, 0x5b +#define TM6010_REQ05_R40_IN_MAXPS_LOW15 0x05, 0x5c +#define TM6010_REQ05_R41_IN_MAXPS_HIGH15 0x05, 0x5d +#define TM6010_REQ05_R60_OUT_MAXPS_LOW1 0x05, 0x60 +#define TM6010_REQ05_R61_OUT_MAXPS_HIGH1 0x05, 0x61 +#define TM6010_REQ05_R62_OUT_MAXPS_LOW2 0x05, 0x62 +#define TM6010_REQ05_R63_OUT_MAXPS_HIGH2 0x05, 0x63 +#define TM6010_REQ05_R64_OUT_MAXPS_LOW3 0x05, 0x64 +#define TM6010_REQ05_R65_OUT_MAXPS_HIGH3 0x05, 0x65 +#define TM6010_REQ05_R66_OUT_MAXPS_LOW4 0x05, 0x66 +#define TM6010_REQ05_R67_OUT_MAXPS_HIGH4 0x05, 0x67 +#define TM6010_REQ05_R68_OUT_MAXPS_LOW5 0x05, 0x68 +#define TM6010_REQ05_R69_OUT_MAXPS_HIGH5 0x05, 0x69 +#define TM6010_REQ05_R6A_OUT_MAXPS_LOW6 0x05, 0x6a +#define TM6010_REQ05_R6B_OUT_MAXPS_HIGH6 0x05, 0x6b +#define TM6010_REQ05_R6C_OUT_MAXPS_LOW7 0x05, 0x6c +#define TM6010_REQ05_R6D_OUT_MAXPS_HIGH7 0x05, 0x6d +#define TM6010_REQ05_R6E_OUT_MAXPS_LOW8 0x05, 0x6e +#define TM6010_REQ05_R6F_OUT_MAXPS_HIGH8 0x05, 0x6f +#define TM6010_REQ05_R70_OUT_MAXPS_LOW9 0x05, 0x70 +#define TM6010_REQ05_R71_OUT_MAXPS_HIGH9 0x05, 0x71 +#define TM6010_REQ05_R60_OUT_MAXPS_LOW10 0x05, 0x72 +#define TM6010_REQ05_R61_OUT_MAXPS_HIGH10 0x05, 0x73 +#define TM6010_REQ05_R60_OUT_MAXPS_LOW11 0x05, 0x74 +#define TM6010_REQ05_R61_OUT_MAXPS_HIGH11 0x05, 0x75 +#define TM6010_REQ05_R60_OUT_MAXPS_LOW12 0x05, 0x76 +#define TM6010_REQ05_R61_OUT_MAXPS_HIGH12 0x05, 0x77 +#define TM6010_REQ05_R60_OUT_MAXPS_LOW13 0x05, 0x78 +#define TM6010_REQ05_R61_OUT_MAXPS_HIGH13 0x05, 0x79 +#define TM6010_REQ05_R60_OUT_MAXPS_LOW14 0x05, 0x7a +#define TM6010_REQ05_R61_OUT_MAXPS_HIGH14 0x05, 0x7b +#define TM6010_REQ05_R60_OUT_MAXPS_LOW15 0x05, 0x7c +#define TM6010_REQ05_R61_OUT_MAXPS_HIGH15 0x05, 0x7d +#define TM6010_REQ05_R80_FIFO0 0x05, 0x80 +#define TM6010_REQ05_R81_FIFO1 0x05, 0x81 +#define TM6010_REQ05_R82_FIFO2 0x05, 0x82 +#define TM6010_REQ05_R83_FIFO3 0x05, 0x83 +#define TM6010_REQ05_R84_FIFO4 0x05, 0x84 +#define TM6010_REQ05_R85_FIFO5 0x05, 0x85 +#define TM6010_REQ05_R86_FIFO6 0x05, 0x86 +#define TM6010_REQ05_R87_FIFO7 0x05, 0x87 +#define TM6010_REQ05_R88_FIFO8 0x05, 0x88 +#define TM6010_REQ05_R89_FIFO9 0x05, 0x89 +#define TM6010_REQ05_R81_FIFO10 0x05, 0x8a +#define TM6010_REQ05_R81_FIFO11 0x05, 0x8b +#define TM6010_REQ05_R81_FIFO12 0x05, 0x8c +#define TM6010_REQ05_R81_FIFO13 0x05, 0x8d +#define TM6010_REQ05_R81_FIFO14 0x05, 0x8e +#define TM6010_REQ05_R81_FIFO15 0x05, 0x8f +#define TM6010_REQ05_R90_CFG_FIFO0 0x05, 0x90 +#define TM6010_REQ05_R91_CFG_FIFO1 0x05, 0x91 +#define TM6010_REQ05_R92_CFG_FIFO2 0x05, 0x92 +#define TM6010_REQ05_R93_CFG_FIFO3 0x05, 0x93 +#define TM6010_REQ05_R94_CFG_FIFO4 0x05, 0x94 +#define TM6010_REQ05_R95_CFG_FIFO5 0x05, 0x95 +#define TM6010_REQ05_R96_CFG_FIFO6 0x05, 0x96 +#define TM6010_REQ05_R97_CFG_FIFO7 0x05, 0x97 +#define TM6010_REQ05_R98_CFG_FIFO8 0x05, 0x98 +#define TM6010_REQ05_R99_CFG_FIFO9 0x05, 0x99 +#define TM6010_REQ05_R91_CFG_FIFO10 0x05, 0x9a +#define TM6010_REQ05_R91_CFG_FIFO11 0x05, 0x9b +#define TM6010_REQ05_R91_CFG_FIFO12 0x05, 0x9c +#define TM6010_REQ05_R91_CFG_FIFO13 0x05, 0x9d +#define TM6010_REQ05_R91_CFG_FIFO14 0x05, 0x9e +#define TM6010_REQ05_R91_CFG_FIFO15 0x05, 0x9f +#define TM6010_REQ05_RA0_CTL_FIFO0 0x05, 0xa0 +#define TM6010_REQ05_RA1_CTL_FIFO1 0x05, 0xa1 +#define TM6010_REQ05_RA2_CTL_FIFO2 0x05, 0xa2 +#define TM6010_REQ05_RA3_CTL_FIFO3 0x05, 0xa3 +#define TM6010_REQ05_RA4_CTL_FIFO4 0x05, 0xa4 +#define TM6010_REQ05_RA5_CTL_FIFO5 0x05, 0xa5 +#define TM6010_REQ05_RA6_CTL_FIFO6 0x05, 0xa6 +#define TM6010_REQ05_RA7_CTL_FIFO7 0x05, 0xa7 +#define TM6010_REQ05_RA8_CTL_FIFO8 0x05, 0xa8 +#define TM6010_REQ05_RA9_CTL_FIFO9 0x05, 0xa9 +#define TM6010_REQ05_RA1_CTL_FIFO10 0x05, 0xaa +#define TM6010_REQ05_RA1_CTL_FIFO11 0x05, 0xab +#define TM6010_REQ05_RA1_CTL_FIFO12 0x05, 0xac +#define TM6010_REQ05_RA1_CTL_FIFO13 0x05, 0xad +#define TM6010_REQ05_RA1_CTL_FIFO14 0x05, 0xae +#define TM6010_REQ05_RA1_CTL_FIFO15 0x05, 0xaf +#define TM6010_REQ05_RB0_BC_LOW_FIFO0 0x05, 0xb0 +#define TM6010_REQ05_RB1_BC_LOW_FIFO1 0x05, 0xb1 +#define TM6010_REQ05_RB2_BC_LOW_FIFO2 0x05, 0xb2 +#define TM6010_REQ05_RB3_BC_LOW_FIFO3 0x05, 0xb3 +#define TM6010_REQ05_RB4_BC_LOW_FIFO4 0x05, 0xb4 +#define TM6010_REQ05_RB5_BC_LOW_FIFO5 0x05, 0xb5 +#define TM6010_REQ05_RB6_BC_LOW_FIFO6 0x05, 0xb6 +#define TM6010_REQ05_RB7_BC_LOW_FIFO7 0x05, 0xb7 +#define TM6010_REQ05_RB8_BC_LOW_FIFO8 0x05, 0xb8 +#define TM6010_REQ05_RB9_BC_LOW_FIFO9 0x05, 0xb9 +#define TM6010_REQ05_RB1_BC_LOW_FIFO10 0x05, 0xba +#define TM6010_REQ05_RB1_BC_LOW_FIFO11 0x05, 0xbb +#define TM6010_REQ05_RB1_BC_LOW_FIFO12 0x05, 0xbc +#define TM6010_REQ05_RB1_BC_LOW_FIFO13 0x05, 0xbd +#define TM6010_REQ05_RB1_BC_LOW_FIFO14 0x05, 0xbe +#define TM6010_REQ05_RB1_BC_LOW_FIFO15 0x05, 0xbf +#define TM6010_REQ05_RC0_DATA_FIFO0 0x05, 0xc0 +#define TM6010_REQ05_RC4_DATA_FIFO1 0x05, 0xc4 +#define TM6010_REQ05_RC8_DATA_FIFO2 0x05, 0xc8 +#define TM6010_REQ05_RCC_DATA_FIFO3 0x05, 0xcc +#define TM6010_REQ05_RD0_DATA_FIFO4 0x05, 0xd0 +#define TM6010_REQ05_RD4_DATA_FIFO5 0x05, 0xd4 +#define TM6010_REQ05_RD8_DATA_FIFO6 0x05, 0xd8 +#define TM6010_REQ05_RDC_DATA_FIFO7 0x05, 0xdc +#define TM6010_REQ05_RE0_DATA_FIFO8 0x05, 0xe0 +#define TM6010_REQ05_RE4_DATA_FIFO9 0x05, 0xe4 +#define TM6010_REQ05_RC4_DATA_FIFO10 0x05, 0xe8 +#define TM6010_REQ05_RC4_DATA_FIFO11 0x05, 0xec +#define TM6010_REQ05_RC4_DATA_FIFO12 0x05, 0xf0 +#define TM6010_REQ05_RC4_DATA_FIFO13 0x05, 0xf4 +#define TM6010_REQ05_RC4_DATA_FIFO14 0x05, 0xf8 +#define TM6010_REQ05_RC4_DATA_FIFO15 0x05, 0xfc + +/* Define TM6010 Audio decoder registers */ +/* This core available only in TM6010 */ +#define TM6010_REQ08_R00_A_VERSION 0x08, 0x00 +#define TM6010_REQ08_R01_A_INIT 0x08, 0x01 +#define TM6010_REQ08_R02_A_FIX_GAIN_CTRL 0x08, 0x02 +#define TM6010_REQ08_R03_A_AUTO_GAIN_CTRL 0x08, 0x03 +#define TM6010_REQ08_R04_A_SIF_AMP_CTRL 0x08, 0x04 +#define TM6010_REQ08_R05_A_STANDARD_MOD 0x08, 0x05 +#define TM6010_REQ08_R06_A_SOUND_MOD 0x08, 0x06 +#define TM6010_REQ08_R07_A_LEFT_VOL 0x08, 0x07 +#define TM6010_REQ08_R08_A_RIGHT_VOL 0x08, 0x08 +#define TM6010_REQ08_R09_A_MAIN_VOL 0x08, 0x09 +#define TM6010_REQ08_R0A_A_I2S_MOD 0x08, 0x0a +#define TM6010_REQ08_R0B_A_ASD_THRES1 0x08, 0x0b +#define TM6010_REQ08_R0C_A_ASD_THRES2 0x08, 0x0c +#define TM6010_REQ08_R0D_A_AMD_THRES 0x08, 0x0d +#define TM6010_REQ08_R0E_A_MONO_THRES1 0x08, 0x0e +#define TM6010_REQ08_R0F_A_MONO_THRES2 0x08, 0x0f +#define TM6010_REQ08_R10_A_MUTE_THRES1 0x08, 0x10 +#define TM6010_REQ08_R11_A_MUTE_THRES2 0x08, 0x11 +#define TM6010_REQ08_R12_A_AGC_U 0x08, 0x12 +#define TM6010_REQ08_R13_A_AGC_ERR_T 0x08, 0x13 +#define TM6010_REQ08_R14_A_AGC_GAIN_INIT 0x08, 0x14 +#define TM6010_REQ08_R15_A_AGC_STEP_THR 0x08, 0x15 +#define TM6010_REQ08_R16_A_AGC_GAIN_MAX 0x08, 0x16 +#define TM6010_REQ08_R17_A_AGC_GAIN_MIN 0x08, 0x17 +#define TM6010_REQ08_R18_A_TR_CTRL 0x08, 0x18 +#define TM6010_REQ08_R19_A_FH_2FH_GAIN 0x08, 0x19 +#define TM6010_REQ08_R1A_A_NICAM_SER_MAX 0x08, 0x1a +#define TM6010_REQ08_R1B_A_NICAM_SER_MIN 0x08, 0x1b +#define TM6010_REQ08_R1E_A_GAIN_DEEMPH_OUT 0x08, 0x1e +#define TM6010_REQ08_R1F_A_TEST_INTF_SEL 0x08, 0x1f +#define TM6010_REQ08_R20_A_TEST_PIN_SEL 0x08, 0x20 +#define TM6010_REQ08_R21_A_AGC_ERR 0x08, 0x21 +#define TM6010_REQ08_R22_A_AGC_GAIN 0x08, 0x22 +#define TM6010_REQ08_R23_A_NICAM_INFO 0x08, 0x23 +#define TM6010_REQ08_R24_A_SER 0x08, 0x24 +#define TM6010_REQ08_R25_A_C1_AMP 0x08, 0x25 +#define TM6010_REQ08_R26_A_C2_AMP 0x08, 0x26 +#define TM6010_REQ08_R27_A_NOISE_AMP 0x08, 0x27 +#define TM6010_REQ08_R28_A_AUDIO_MODE_RES 0x08, 0x28 + +/* Define TM6010 Video ADC registers */ +#define TM6010_REQ08_RE0_ADC_REF 0x08, 0xe0 +#define TM6010_REQ08_RE1_DAC_CLMP 0x08, 0xe1 +#define TM6010_REQ08_RE2_POWER_DOWN_CTRL1 0x08, 0xe2 +#define TM6010_REQ08_RE3_ADC_IN1_SEL 0x08, 0xe3 +#define TM6010_REQ08_RE4_ADC_IN2_SEL 0x08, 0xe4 +#define TM6010_REQ08_RE5_GAIN_PARAM 0x08, 0xe5 +#define TM6010_REQ08_RE6_POWER_DOWN_CTRL2 0x08, 0xe6 +#define TM6010_REQ08_RE7_REG_GAIN_Y 0x08, 0xe7 +#define TM6010_REQ08_RE8_REG_GAIN_C 0x08, 0xe8 +#define TM6010_REQ08_RE9_BIAS_CTRL 0x08, 0xe9 +#define TM6010_REQ08_REA_BUFF_DRV_CTRL 0x08, 0xea +#define TM6010_REQ08_REB_SIF_GAIN_CTRL 0x08, 0xeb +#define TM6010_REQ08_REC_REVERSE_YC_CTRL 0x08, 0xec +#define TM6010_REQ08_RED_GAIN_SEL 0x08, 0xed + +/* Define TM6010 Audio ADC registers */ +#define TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG 0x08, 0xf0 +#define TM6010_REQ08_RF1_AADC_POWER_DOWN 0x08, 0xf1 +#define TM6010_REQ08_RF2_LEFT_CHANNEL_VOL 0x08, 0xf2 +#define TM6010_REQ08_RF3_RIGHT_CHANNEL_VOL 0x08, 0xf3 diff --git a/drivers/media/video/tm6000/tm6000-stds.c b/drivers/media/video/tm6000/tm6000-stds.c new file mode 100644 index 00000000000..9a4145dc3d8 --- /dev/null +++ b/drivers/media/video/tm6000/tm6000-stds.c @@ -0,0 +1,659 @@ +/* + * tm6000-stds.c - driver for TM5600/TM6000/TM6010 USB video capture devices + * + * Copyright (C) 2007 Mauro Carvalho Chehab <mchehab@redhat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation version 2 + * + * 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 <linux/module.h> +#include <linux/kernel.h> +#include "tm6000.h" +#include "tm6000-regs.h" + +static unsigned int tm6010_a_mode; +module_param(tm6010_a_mode, int, 0644); +MODULE_PARM_DESC(tm6010_a_mode, "set tm6010 sif audio mode"); + +struct tm6000_reg_settings { + unsigned char req; + unsigned char reg; + unsigned char value; +}; + + +struct tm6000_std_settings { + v4l2_std_id id; + struct tm6000_reg_settings *common; +}; + +static struct tm6000_reg_settings composite_pal_m[] = { + { TM6010_REQ07_R3F_RESET, 0x01 }, + { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x04 }, + { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e }, + { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, + { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x00 }, + { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 }, + { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e }, + { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x83 }, + { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x0a }, + { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe0 }, + { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, + { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, + { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, + { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, + { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 }, + { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x20 }, + { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61 }, + { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c }, + { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, + { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52 }, + { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f }, + { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc }, + { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, + { TM6010_REQ07_R3F_RESET, 0x00 }, + { 0, 0, 0 } +}; + +static struct tm6000_reg_settings composite_pal_nc[] = { + { TM6010_REQ07_R3F_RESET, 0x01 }, + { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x36 }, + { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e }, + { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, + { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x02 }, + { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 }, + { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e }, + { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x91 }, + { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x1f }, + { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0x0c }, + { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, + { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, + { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, + { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, + { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c }, + { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2c }, + { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1 }, + { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c }, + { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, + { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52 }, + { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f }, + { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc }, + { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, + { TM6010_REQ07_R3F_RESET, 0x00 }, + { 0, 0, 0 } +}; + +static struct tm6000_reg_settings composite_pal[] = { + { TM6010_REQ07_R3F_RESET, 0x01 }, + { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x32 }, + { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e }, + { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, + { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x02 }, + { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 }, + { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x25 }, + { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0xd5 }, + { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x63 }, + { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0x50 }, + { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, + { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, + { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, + { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, + { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c }, + { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2c }, + { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1 }, + { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c }, + { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, + { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52 }, + { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f }, + { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc }, + { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, + { TM6010_REQ07_R3F_RESET, 0x00 }, + { 0, 0, 0 } +}; + +static struct tm6000_reg_settings composite_secam[] = { + { TM6010_REQ07_R3F_RESET, 0x01 }, + { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x38 }, + { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e }, + { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, + { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x02 }, + { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 }, + { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x24 }, + { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x92 }, + { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xe8 }, + { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xed }, + { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, + { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, + { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, + { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, + { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c }, + { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2c }, + { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1 }, + { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x2c }, + { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x18 }, + { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42 }, + { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0xff }, + { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, + { TM6010_REQ07_R3F_RESET, 0x00 }, + { 0, 0, 0 } +}; + +static struct tm6000_reg_settings composite_ntsc[] = { + { TM6010_REQ07_R3F_RESET, 0x01 }, + { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x00 }, + { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0f }, + { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, + { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x00 }, + { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 }, + { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e }, + { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x8b }, + { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xa2 }, + { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe9 }, + { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, + { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, + { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, + { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, + { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 }, + { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22 }, + { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61 }, + { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x1c }, + { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, + { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42 }, + { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f }, + { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdd }, + { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, + { TM6010_REQ07_R3F_RESET, 0x00 }, + { 0, 0, 0 } +}; + +static struct tm6000_std_settings composite_stds[] = { + { .id = V4L2_STD_PAL_M, .common = composite_pal_m, }, + { .id = V4L2_STD_PAL_Nc, .common = composite_pal_nc, }, + { .id = V4L2_STD_PAL, .common = composite_pal, }, + { .id = V4L2_STD_SECAM, .common = composite_secam, }, + { .id = V4L2_STD_NTSC, .common = composite_ntsc, }, +}; + +static struct tm6000_reg_settings svideo_pal_m[] = { + { TM6010_REQ07_R3F_RESET, 0x01 }, + { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x05 }, + { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e }, + { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, + { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x04 }, + { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 }, + { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e }, + { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x83 }, + { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x0a }, + { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe0 }, + { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, + { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, + { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, + { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, + { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 }, + { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22 }, + { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61 }, + { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c }, + { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, + { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52 }, + { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f }, + { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc }, + { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, + { TM6010_REQ07_R3F_RESET, 0x00 }, + { 0, 0, 0 } +}; + +static struct tm6000_reg_settings svideo_pal_nc[] = { + { TM6010_REQ07_R3F_RESET, 0x01 }, + { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x37 }, + { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e }, + { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, + { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x04 }, + { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 }, + { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e }, + { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x91 }, + { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x1f }, + { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0x0c }, + { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, + { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, + { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, + { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, + { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 }, + { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22 }, + { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1 }, + { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c }, + { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, + { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52 }, + { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f }, + { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc }, + { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, + { TM6010_REQ07_R3F_RESET, 0x00 }, + { 0, 0, 0 } +}; + +static struct tm6000_reg_settings svideo_pal[] = { + { TM6010_REQ07_R3F_RESET, 0x01 }, + { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x33 }, + { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e }, + { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, + { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x04 }, + { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x30 }, + { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x25 }, + { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0xd5 }, + { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x63 }, + { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0x50 }, + { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, + { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, + { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, + { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, + { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c }, + { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2a }, + { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1 }, + { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c }, + { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, + { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52 }, + { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f }, + { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc }, + { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, + { TM6010_REQ07_R3F_RESET, 0x00 }, + { 0, 0, 0 } +}; + +static struct tm6000_reg_settings svideo_secam[] = { + { TM6010_REQ07_R3F_RESET, 0x01 }, + { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x39 }, + { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e }, + { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, + { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x03 }, + { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 }, + { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x24 }, + { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x92 }, + { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xe8 }, + { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xed }, + { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, + { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, + { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, + { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, + { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c }, + { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2a }, + { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1 }, + { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x2c }, + { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x18 }, + { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42 }, + { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0xff }, + { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, + { TM6010_REQ07_R3F_RESET, 0x00 }, + { 0, 0, 0 } +}; + +static struct tm6000_reg_settings svideo_ntsc[] = { + { TM6010_REQ07_R3F_RESET, 0x01 }, + { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x01 }, + { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0f }, + { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, + { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x03 }, + { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x30 }, + { TM6010_REQ07_R17_HLOOP_MAXSTATE, 0x8b }, + { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e }, + { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x8b }, + { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xa2 }, + { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe9 }, + { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, + { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, + { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, + { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, + { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 }, + { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22 }, + { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61 }, + { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x1c }, + { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, + { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42 }, + { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f }, + { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdd }, + { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, + { TM6010_REQ07_R3F_RESET, 0x00 }, + { 0, 0, 0 } +}; + +static struct tm6000_std_settings svideo_stds[] = { + { .id = V4L2_STD_PAL_M, .common = svideo_pal_m, }, + { .id = V4L2_STD_PAL_Nc, .common = svideo_pal_nc, }, + { .id = V4L2_STD_PAL, .common = svideo_pal, }, + { .id = V4L2_STD_SECAM, .common = svideo_secam, }, + { .id = V4L2_STD_NTSC, .common = svideo_ntsc, }, +}; + +static int tm6000_set_audio_std(struct tm6000_core *dev) +{ + uint8_t areg_02 = 0x04; /* GC1 Fixed gain 0dB */ + uint8_t areg_05 = 0x01; /* Auto 4.5 = M Japan, Auto 6.5 = DK */ + uint8_t areg_06 = 0x02; /* Auto de-emphasis, mannual channel mode */ + uint8_t nicam_flag = 0; /* No NICAM */ + + if (dev->radio) { + tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x00); + tm6000_set_reg(dev, TM6010_REQ08_R02_A_FIX_GAIN_CTRL, 0x04); + tm6000_set_reg(dev, TM6010_REQ08_R03_A_AUTO_GAIN_CTRL, 0x00); + tm6000_set_reg(dev, TM6010_REQ08_R04_A_SIF_AMP_CTRL, 0x80); + tm6000_set_reg(dev, TM6010_REQ08_R05_A_STANDARD_MOD, 0x0c); + /* set mono or stereo */ + if (dev->amode == V4L2_TUNER_MODE_MONO) + tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD, 0x00); + else if (dev->amode == V4L2_TUNER_MODE_STEREO) + tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD, 0x02); + tm6000_set_reg(dev, TM6010_REQ08_R09_A_MAIN_VOL, 0x18); + tm6000_set_reg(dev, TM6010_REQ08_R0C_A_ASD_THRES2, 0x0a); + tm6000_set_reg(dev, TM6010_REQ08_R0D_A_AMD_THRES, 0x40); + tm6000_set_reg(dev, TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfe); + tm6000_set_reg(dev, TM6010_REQ08_R1E_A_GAIN_DEEMPH_OUT, 0x13); + tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x80); + tm6000_set_reg(dev, TM6010_REQ07_RFE_POWER_DOWN, 0xff); + return 0; + } + + switch (tm6010_a_mode) { + /* auto */ + case 0: + switch (dev->norm) { + case V4L2_STD_NTSC_M_KR: + areg_05 |= 0x00; + break; + case V4L2_STD_NTSC_M_JP: + areg_05 |= 0x40; + break; + case V4L2_STD_NTSC_M: + case V4L2_STD_PAL_M: + case V4L2_STD_PAL_N: + areg_05 |= 0x20; + break; + case V4L2_STD_PAL_Nc: + areg_05 |= 0x60; + break; + case V4L2_STD_SECAM_L: + areg_05 |= 0x00; + break; + case V4L2_STD_DK: + areg_05 |= 0x10; + break; + } + break; + /* A2 */ + case 1: + switch (dev->norm) { + case V4L2_STD_B: + case V4L2_STD_GH: + areg_05 = 0x05; + break; + case V4L2_STD_DK: + areg_05 = 0x09; + break; + } + break; + /* NICAM */ + case 2: + switch (dev->norm) { + case V4L2_STD_B: + case V4L2_STD_GH: + areg_05 = 0x07; + break; + case V4L2_STD_DK: + areg_05 = 0x06; + break; + case V4L2_STD_PAL_I: + areg_05 = 0x08; + break; + case V4L2_STD_SECAM_L: + areg_05 = 0x0a; + areg_02 = 0x02; + break; + } + nicam_flag = 1; + break; + /* other */ + case 3: + switch (dev->norm) { + /* DK3_A2 */ + case V4L2_STD_DK: + areg_05 = 0x0b; + break; + /* Korea */ + case V4L2_STD_NTSC_M_KR: + areg_05 = 0x04; + break; + /* EIAJ */ + case V4L2_STD_NTSC_M_JP: + areg_05 = 0x03; + break; + default: + areg_05 = 0x02; + break; + } + break; + } + + tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x00); + tm6000_set_reg(dev, TM6010_REQ08_R02_A_FIX_GAIN_CTRL, areg_02); + tm6000_set_reg(dev, TM6010_REQ08_R03_A_AUTO_GAIN_CTRL, 0x00); + tm6000_set_reg(dev, TM6010_REQ08_R04_A_SIF_AMP_CTRL, 0xa0); + tm6000_set_reg(dev, TM6010_REQ08_R05_A_STANDARD_MOD, areg_05); + tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD, areg_06); + tm6000_set_reg(dev, TM6010_REQ08_R07_A_LEFT_VOL, 0x00); + tm6000_set_reg(dev, TM6010_REQ08_R08_A_RIGHT_VOL, 0x00); + tm6000_set_reg(dev, TM6010_REQ08_R09_A_MAIN_VOL, 0x08); + tm6000_set_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD, 0x91); + tm6000_set_reg(dev, TM6010_REQ08_R0B_A_ASD_THRES1, 0x20); + tm6000_set_reg(dev, TM6010_REQ08_R0C_A_ASD_THRES2, 0x12); + tm6000_set_reg(dev, TM6010_REQ08_R0D_A_AMD_THRES, 0x20); + tm6000_set_reg(dev, TM6010_REQ08_R0E_A_MONO_THRES1, 0xf0); + tm6000_set_reg(dev, TM6010_REQ08_R0F_A_MONO_THRES2, 0x80); + tm6000_set_reg(dev, TM6010_REQ08_R10_A_MUTE_THRES1, 0xc0); + tm6000_set_reg(dev, TM6010_REQ08_R11_A_MUTE_THRES2, 0x80); + tm6000_set_reg(dev, TM6010_REQ08_R12_A_AGC_U, 0x12); + tm6000_set_reg(dev, TM6010_REQ08_R13_A_AGC_ERR_T, 0xfe); + tm6000_set_reg(dev, TM6010_REQ08_R14_A_AGC_GAIN_INIT, 0x20); + tm6000_set_reg(dev, TM6010_REQ08_R15_A_AGC_STEP_THR, 0x14); + tm6000_set_reg(dev, TM6010_REQ08_R16_A_AGC_GAIN_MAX, 0xfe); + tm6000_set_reg(dev, TM6010_REQ08_R17_A_AGC_GAIN_MIN, 0x01); + tm6000_set_reg(dev, TM6010_REQ08_R18_A_TR_CTRL, 0xa0); + tm6000_set_reg(dev, TM6010_REQ08_R19_A_FH_2FH_GAIN, 0x32); + tm6000_set_reg(dev, TM6010_REQ08_R1A_A_NICAM_SER_MAX, 0x64); + tm6000_set_reg(dev, TM6010_REQ08_R1B_A_NICAM_SER_MIN, 0x20); + tm6000_set_reg(dev, REQ_08_SET_GET_AVREG_BIT, 0x1c, 0x00); + tm6000_set_reg(dev, REQ_08_SET_GET_AVREG_BIT, 0x1d, 0x00); + tm6000_set_reg(dev, TM6010_REQ08_R1E_A_GAIN_DEEMPH_OUT, 0x13); + tm6000_set_reg(dev, TM6010_REQ08_R1F_A_TEST_INTF_SEL, 0x00); + tm6000_set_reg(dev, TM6010_REQ08_R20_A_TEST_PIN_SEL, 0x00); + tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x80); + + return 0; +} + +void tm6000_get_std_res(struct tm6000_core *dev) +{ + /* Currently, those are the only supported resoltions */ + if (dev->norm & V4L2_STD_525_60) + dev->height = 480; + else + dev->height = 576; + + dev->width = 720; +} + +static int tm6000_load_std(struct tm6000_core *dev, struct tm6000_reg_settings *set) +{ + int i, rc; + + /* Load board's initialization table */ + for (i = 0; set[i].req; i++) { + rc = tm6000_set_reg(dev, set[i].req, set[i].reg, set[i].value); + if (rc < 0) { + printk(KERN_ERR "Error %i while setting " + "req %d, reg %d to value %d\n", + rc, set[i].req, set[i].reg, set[i].value); + return rc; + } + } + + return 0; +} + +int tm6000_set_standard(struct tm6000_core *dev) +{ + struct tm6000_input *input; + int i, rc = 0; + u8 reg_07_fe = 0x8a; + u8 reg_08_f1 = 0xfc; + u8 reg_08_e2 = 0xf0; + u8 reg_08_e6 = 0x0f; + + tm6000_get_std_res(dev); + + if (!dev->radio) + input = &dev->vinput[dev->input]; + else + input = &dev->rinput; + + if (dev->dev_type == TM6010) { + switch (input->vmux) { + case TM6000_VMUX_VIDEO_A: + tm6000_set_reg(dev, TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf4); + tm6000_set_reg(dev, TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf1); + tm6000_set_reg(dev, TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xe0); + tm6000_set_reg(dev, TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2); + tm6000_set_reg(dev, TM6010_REQ08_RED_GAIN_SEL, 0xe8); + reg_07_fe |= 0x01; + break; + case TM6000_VMUX_VIDEO_B: + tm6000_set_reg(dev, TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf8); + tm6000_set_reg(dev, TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf1); + tm6000_set_reg(dev, TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xe0); + tm6000_set_reg(dev, TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2); + tm6000_set_reg(dev, TM6010_REQ08_RED_GAIN_SEL, 0xe8); + reg_07_fe |= 0x01; + break; + case TM6000_VMUX_VIDEO_AB: + tm6000_set_reg(dev, TM6010_REQ08_RE3_ADC_IN1_SEL, 0xfc); + tm6000_set_reg(dev, TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf8); + reg_08_e6 = 0x00; + tm6000_set_reg(dev, TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf2); + tm6000_set_reg(dev, TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xf0); + tm6000_set_reg(dev, TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2); + tm6000_set_reg(dev, TM6010_REQ08_RED_GAIN_SEL, 0xe0); + break; + default: + break; + } + switch (input->amux) { + case TM6000_AMUX_ADC1: + tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, + 0x00, 0x0f); + break; + case TM6000_AMUX_ADC2: + tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, + 0x08, 0x0f); + break; + case TM6000_AMUX_SIF1: + reg_08_e2 |= 0x02; + reg_08_e6 = 0x08; + reg_07_fe |= 0x40; + reg_08_f1 |= 0x02; + tm6000_set_reg(dev, TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf3); + tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, + 0x02, 0x0f); + break; + case TM6000_AMUX_SIF2: + reg_08_e2 |= 0x02; + reg_08_e6 = 0x08; + reg_07_fe |= 0x40; + reg_08_f1 |= 0x02; + tm6000_set_reg(dev, TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf7); + tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, + 0x02, 0x0f); + break; + default: + break; + } + tm6000_set_reg(dev, TM6010_REQ08_RE2_POWER_DOWN_CTRL1, reg_08_e2); + tm6000_set_reg(dev, TM6010_REQ08_RE6_POWER_DOWN_CTRL2, reg_08_e6); + tm6000_set_reg(dev, TM6010_REQ08_RF1_AADC_POWER_DOWN, reg_08_f1); + tm6000_set_reg(dev, TM6010_REQ07_RFE_POWER_DOWN, reg_07_fe); + } else { + switch (input->vmux) { + case TM6000_VMUX_VIDEO_A: + tm6000_set_reg(dev, TM6000_REQ07_RE3_VADC_INP_LPF_SEL1, 0x10); + tm6000_set_reg(dev, TM6000_REQ07_RE5_VADC_INP_LPF_SEL2, 0x00); + tm6000_set_reg(dev, TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0x0f); + tm6000_set_reg(dev, + REQ_03_SET_GET_MCU_PIN, input->v_gpio, 0); + break; + case TM6000_VMUX_VIDEO_B: + tm6000_set_reg(dev, TM6000_REQ07_RE3_VADC_INP_LPF_SEL1, 0x00); + tm6000_set_reg(dev, TM6000_REQ07_RE5_VADC_INP_LPF_SEL2, 0x00); + tm6000_set_reg(dev, TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0x0f); + tm6000_set_reg(dev, + REQ_03_SET_GET_MCU_PIN, input->v_gpio, 0); + break; + case TM6000_VMUX_VIDEO_AB: + tm6000_set_reg(dev, TM6000_REQ07_RE3_VADC_INP_LPF_SEL1, 0x10); + tm6000_set_reg(dev, TM6000_REQ07_RE5_VADC_INP_LPF_SEL2, 0x10); + tm6000_set_reg(dev, TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0x00); + tm6000_set_reg(dev, + REQ_03_SET_GET_MCU_PIN, input->v_gpio, 1); + break; + default: + break; + } + switch (input->amux) { + case TM6000_AMUX_ADC1: + tm6000_set_reg_mask(dev, + TM6000_REQ07_REB_VADC_AADC_MODE, 0x00, 0x0f); + break; + case TM6000_AMUX_ADC2: + tm6000_set_reg_mask(dev, + TM6000_REQ07_REB_VADC_AADC_MODE, 0x04, 0x0f); + break; + default: + break; + } + } + if (input->type == TM6000_INPUT_SVIDEO) { + for (i = 0; i < ARRAY_SIZE(svideo_stds); i++) { + if (dev->norm & svideo_stds[i].id) { + rc = tm6000_load_std(dev, svideo_stds[i].common); + goto ret; + } + } + return -EINVAL; + } else { + for (i = 0; i < ARRAY_SIZE(composite_stds); i++) { + if (dev->norm & composite_stds[i].id) { + rc = tm6000_load_std(dev, composite_stds[i].common); + goto ret; + } + } + return -EINVAL; + } + +ret: + if (rc < 0) + return rc; + + if ((dev->dev_type == TM6010) && + ((input->amux == TM6000_AMUX_SIF1) || + (input->amux == TM6000_AMUX_SIF2))) + tm6000_set_audio_std(dev); + + msleep(40); + + return 0; +} diff --git a/drivers/media/video/tm6000/tm6000-usb-isoc.h b/drivers/media/video/tm6000/tm6000-usb-isoc.h new file mode 100644 index 00000000000..99d15a55aa0 --- /dev/null +++ b/drivers/media/video/tm6000/tm6000-usb-isoc.h @@ -0,0 +1,50 @@ +/* + * tm6000-buf.c - driver for TM5600/TM6000/TM6010 USB video capture devices + * + * Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org> + * + * 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 version 2 + * + * 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 <linux/videodev2.h> + +#define TM6000_URB_MSG_LEN 180 + +struct usb_isoc_ctl { + /* max packet size of isoc transaction */ + int max_pkt_size; + + /* number of allocated urbs */ + int num_bufs; + + /* urb for isoc transfers */ + struct urb **urb; + + /* transfer buffers for isoc transfer */ + char **transfer_buffer; + + /* Last buffer command and region */ + u8 cmd; + int pos, size, pktsize; + + /* Last field: ODD or EVEN? */ + int vfield, field; + + /* Stores incomplete commands */ + u32 tmp_buf; + int tmp_buf_len; + + /* Stores already requested buffers */ + struct tm6000_buffer *buf; +}; diff --git a/drivers/media/video/tm6000/tm6000-video.c b/drivers/media/video/tm6000/tm6000-video.c new file mode 100644 index 00000000000..1e5ace0b5d1 --- /dev/null +++ b/drivers/media/video/tm6000/tm6000-video.c @@ -0,0 +1,1813 @@ +/* + * tm6000-video.c - driver for TM5600/TM6000/TM6010 USB video capture devices + * + * Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org> + * + * Copyright (C) 2007 Michel Ludwig <michel.ludwig@gmail.com> + * - Fixed module load/unload + * + * 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 version 2 + * + * 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 <linux/module.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/mm.h> +#include <linux/ioport.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/random.h> +#include <linux/usb.h> +#include <linux/videodev2.h> +#include <media/v4l2-ioctl.h> +#include <media/tuner.h> +#include <linux/interrupt.h> +#include <linux/kthread.h> +#include <linux/highmem.h> +#include <linux/freezer.h> + +#include "tm6000-regs.h" +#include "tm6000.h" + +#define BUFFER_TIMEOUT msecs_to_jiffies(2000) /* 2 seconds */ + +/* Limits minimum and default number of buffers */ +#define TM6000_MIN_BUF 4 +#define TM6000_DEF_BUF 8 + +#define TM6000_MAX_ISO_PACKETS 46 /* Max number of ISO packets */ + +/* Declare static vars that will be used as parameters */ +static unsigned int vid_limit = 16; /* Video memory limit, in Mb */ +static int video_nr = -1; /* /dev/videoN, -1 for autodetect */ +static int radio_nr = -1; /* /dev/radioN, -1 for autodetect */ + +/* Debug level */ +int tm6000_debug; +EXPORT_SYMBOL_GPL(tm6000_debug); + +static const struct v4l2_queryctrl no_ctrl = { + .name = "42", + .flags = V4L2_CTRL_FLAG_DISABLED, +}; + +/* supported controls */ +static struct v4l2_queryctrl tm6000_qctrl[] = { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 54, + .flags = 0, + }, { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 255, + .step = 0x1, + .default_value = 119, + .flags = 0, + }, { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Saturation", + .minimum = 0, + .maximum = 255, + .step = 0x1, + .default_value = 112, + .flags = 0, + }, { + .id = V4L2_CID_HUE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Hue", + .minimum = -128, + .maximum = 127, + .step = 0x1, + .default_value = 0, + .flags = 0, + }, + /* --- audio --- */ + { + .id = V4L2_CID_AUDIO_MUTE, + .name = "Mute", + .minimum = 0, + .maximum = 1, + .type = V4L2_CTRL_TYPE_BOOLEAN, + }, { + .id = V4L2_CID_AUDIO_VOLUME, + .name = "Volume", + .minimum = -15, + .maximum = 15, + .step = 1, + .default_value = 0, + .type = V4L2_CTRL_TYPE_INTEGER, + } +}; + +static const unsigned int CTRLS = ARRAY_SIZE(tm6000_qctrl); +static int qctl_regs[ARRAY_SIZE(tm6000_qctrl)]; + +static struct tm6000_fmt format[] = { + { + .name = "4:2:2, packed, YVY2", + .fourcc = V4L2_PIX_FMT_YUYV, + .depth = 16, + }, { + .name = "4:2:2, packed, UYVY", + .fourcc = V4L2_PIX_FMT_UYVY, + .depth = 16, + }, { + .name = "A/V + VBI mux packet", + .fourcc = V4L2_PIX_FMT_TM6000, + .depth = 16, + } +}; + +static const struct v4l2_queryctrl *ctrl_by_id(unsigned int id) +{ + unsigned int i; + + for (i = 0; i < CTRLS; i++) + if (tm6000_qctrl[i].id == id) + return tm6000_qctrl+i; + return NULL; +} + +/* ------------------------------------------------------------------ + * DMA and thread functions + * ------------------------------------------------------------------ + */ + +#define norm_maxw(a) 720 +#define norm_maxh(a) 576 + +#define norm_minw(a) norm_maxw(a) +#define norm_minh(a) norm_maxh(a) + +/* + * video-buf generic routine to get the next available buffer + */ +static inline void get_next_buf(struct tm6000_dmaqueue *dma_q, + struct tm6000_buffer **buf) +{ + struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq); + char *outp; + + if (list_empty(&dma_q->active)) { + dprintk(dev, V4L2_DEBUG_QUEUE, "No active queue to serve\n"); + *buf = NULL; + return; + } + + *buf = list_entry(dma_q->active.next, + struct tm6000_buffer, vb.queue); + + /* Cleans up buffer - Useful for testing for frame/URB loss */ + outp = videobuf_to_vmalloc(&(*buf)->vb); + + return; +} + +/* + * Announces that a buffer were filled and request the next + */ +static inline void buffer_filled(struct tm6000_core *dev, + struct tm6000_dmaqueue *dma_q, + struct tm6000_buffer *buf) +{ + /* Advice that buffer was filled */ + dprintk(dev, V4L2_DEBUG_ISOC, "[%p/%d] wakeup\n", buf, buf->vb.i); + buf->vb.state = VIDEOBUF_DONE; + buf->vb.field_count++; + do_gettimeofday(&buf->vb.ts); + + list_del(&buf->vb.queue); + wake_up(&buf->vb.done); +} + +/* + * Identify the tm5600/6000 buffer header type and properly handles + */ +static int copy_streams(u8 *data, unsigned long len, + struct urb *urb) +{ + struct tm6000_dmaqueue *dma_q = urb->context; + struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq); + u8 *ptr = data, *endp = data+len, c; + unsigned long header = 0; + int rc = 0; + unsigned int cmd, cpysize, pktsize, size, field, block, line, pos = 0; + struct tm6000_buffer *vbuf = NULL; + char *voutp = NULL; + unsigned int linewidth; + + if (!dev->radio) { + /* get video buffer */ + get_next_buf(dma_q, &vbuf); + + if (!vbuf) + return rc; + voutp = videobuf_to_vmalloc(&vbuf->vb); + + if (!voutp) + return 0; + } + + for (ptr = data; ptr < endp;) { + if (!dev->isoc_ctl.cmd) { + /* Header */ + if (dev->isoc_ctl.tmp_buf_len > 0) { + /* from last urb or packet */ + header = dev->isoc_ctl.tmp_buf; + if (4 - dev->isoc_ctl.tmp_buf_len > 0) { + memcpy((u8 *)&header + + dev->isoc_ctl.tmp_buf_len, + ptr, + 4 - dev->isoc_ctl.tmp_buf_len); + ptr += 4 - dev->isoc_ctl.tmp_buf_len; + } + dev->isoc_ctl.tmp_buf_len = 0; + } else { + if (ptr + 3 >= endp) { + /* have incomplete header */ + dev->isoc_ctl.tmp_buf_len = endp - ptr; + memcpy(&dev->isoc_ctl.tmp_buf, ptr, + dev->isoc_ctl.tmp_buf_len); + return rc; + } + /* Seek for sync */ + for (; ptr < endp - 3; ptr++) { + if (*(ptr + 3) == 0x47) + break; + } + /* Get message header */ + header = *(unsigned long *)ptr; + ptr += 4; + } + + /* split the header fields */ + c = (header >> 24) & 0xff; + size = ((header & 0x7e) << 1); + if (size > 0) + size -= 4; + block = (header >> 7) & 0xf; + field = (header >> 11) & 0x1; + line = (header >> 12) & 0x1ff; + cmd = (header >> 21) & 0x7; + /* Validates haeder fields */ + if (size > TM6000_URB_MSG_LEN) + size = TM6000_URB_MSG_LEN; + pktsize = TM6000_URB_MSG_LEN; + /* + * calculate position in buffer and change the buffer + */ + switch (cmd) { + case TM6000_URB_MSG_VIDEO: + if (!dev->radio) { + if ((dev->isoc_ctl.vfield != field) && + (field == 1)) { + /* + * Announces that a new buffer + * were filled + */ + buffer_filled(dev, dma_q, vbuf); + dprintk(dev, V4L2_DEBUG_ISOC, + "new buffer filled\n"); + get_next_buf(dma_q, &vbuf); + if (!vbuf) + return rc; + voutp = videobuf_to_vmalloc(&vbuf->vb); + if (!voutp) + return rc; + memset(voutp, 0, vbuf->vb.size); + } + linewidth = vbuf->vb.width << 1; + pos = ((line << 1) - field - 1) * + linewidth + block * TM6000_URB_MSG_LEN; + /* Don't allow to write out of the buffer */ + if (pos + size > vbuf->vb.size) + cmd = TM6000_URB_MSG_ERR; + dev->isoc_ctl.vfield = field; + } + break; + case TM6000_URB_MSG_VBI: + break; + case TM6000_URB_MSG_AUDIO: + case TM6000_URB_MSG_PTS: + size = pktsize; /* Size is always 180 bytes */ + break; + } + } else { + /* Continue the last copy */ + cmd = dev->isoc_ctl.cmd; + size = dev->isoc_ctl.size; + pos = dev->isoc_ctl.pos; + pktsize = dev->isoc_ctl.pktsize; + field = dev->isoc_ctl.field; + } + cpysize = (endp - ptr > size) ? size : endp - ptr; + if (cpysize) { + /* copy data in different buffers */ + switch (cmd) { + case TM6000_URB_MSG_VIDEO: + /* Fills video buffer */ + if (vbuf) + memcpy(&voutp[pos], ptr, cpysize); + break; + case TM6000_URB_MSG_AUDIO: { + int i; + for (i = 0; i < cpysize; i += 2) + swab16s((u16 *)(ptr + i)); + + tm6000_call_fillbuf(dev, TM6000_AUDIO, ptr, cpysize); + break; + } + case TM6000_URB_MSG_VBI: + /* Need some code to copy vbi buffer */ + break; + case TM6000_URB_MSG_PTS: { + /* Need some code to copy pts */ + u32 pts; + pts = *(u32 *)ptr; + dprintk(dev, V4L2_DEBUG_ISOC, "field %d, PTS %x", + field, pts); + break; + } + } + } + if (ptr + pktsize > endp) { + /* + * End of URB packet, but cmd processing is not + * complete. Preserve the state for a next packet + */ + dev->isoc_ctl.pos = pos + cpysize; + dev->isoc_ctl.size = size - cpysize; + dev->isoc_ctl.cmd = cmd; + dev->isoc_ctl.field = field; + dev->isoc_ctl.pktsize = pktsize - (endp - ptr); + ptr += endp - ptr; + } else { + dev->isoc_ctl.cmd = 0; + ptr += pktsize; + } + } + return 0; +} + +/* + * Identify the tm5600/6000 buffer header type and properly handles + */ +static int copy_multiplexed(u8 *ptr, unsigned long len, + struct urb *urb) +{ + struct tm6000_dmaqueue *dma_q = urb->context; + struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq); + unsigned int pos = dev->isoc_ctl.pos, cpysize; + int rc = 1; + struct tm6000_buffer *buf; + char *outp = NULL; + + get_next_buf(dma_q, &buf); + if (buf) + outp = videobuf_to_vmalloc(&buf->vb); + + if (!outp) + return 0; + + while (len > 0) { + cpysize = min(len, buf->vb.size-pos); + memcpy(&outp[pos], ptr, cpysize); + pos += cpysize; + ptr += cpysize; + len -= cpysize; + if (pos >= buf->vb.size) { + pos = 0; + /* Announces that a new buffer were filled */ + buffer_filled(dev, dma_q, buf); + dprintk(dev, V4L2_DEBUG_ISOC, "new buffer filled\n"); + get_next_buf(dma_q, &buf); + if (!buf) + break; + outp = videobuf_to_vmalloc(&(buf->vb)); + if (!outp) + return rc; + pos = 0; + } + } + + dev->isoc_ctl.pos = pos; + return rc; +} + +static inline void print_err_status(struct tm6000_core *dev, + int packet, int status) +{ + char *errmsg = "Unknown"; + + switch (status) { + case -ENOENT: + errmsg = "unlinked synchronuously"; + break; + case -ECONNRESET: + errmsg = "unlinked asynchronuously"; + break; + case -ENOSR: + errmsg = "Buffer error (overrun)"; + break; + case -EPIPE: + errmsg = "Stalled (device not responding)"; + break; + case -EOVERFLOW: + errmsg = "Babble (bad cable?)"; + break; + case -EPROTO: + errmsg = "Bit-stuff error (bad cable?)"; + break; + case -EILSEQ: + errmsg = "CRC/Timeout (could be anything)"; + break; + case -ETIME: + errmsg = "Device does not respond"; + break; + } + if (packet < 0) { + dprintk(dev, V4L2_DEBUG_QUEUE, "URB status %d [%s].\n", + status, errmsg); + } else { + dprintk(dev, V4L2_DEBUG_QUEUE, "URB packet %d, status %d [%s].\n", + packet, status, errmsg); + } +} + + +/* + * Controls the isoc copy of each urb packet + */ +static inline int tm6000_isoc_copy(struct urb *urb) +{ + struct tm6000_dmaqueue *dma_q = urb->context; + struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq); + int i, len = 0, rc = 1, status; + char *p; + + if (urb->status < 0) { + print_err_status(dev, -1, urb->status); + return 0; + } + + for (i = 0; i < urb->number_of_packets; i++) { + status = urb->iso_frame_desc[i].status; + + if (status < 0) { + print_err_status(dev, i, status); + continue; + } + + len = urb->iso_frame_desc[i].actual_length; + + if (len > 0) { + p = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + if (!urb->iso_frame_desc[i].status) { + if ((dev->fourcc) == V4L2_PIX_FMT_TM6000) { + rc = copy_multiplexed(p, len, urb); + if (rc <= 0) + return rc; + } else { + copy_streams(p, len, urb); + } + } + } + } + return rc; +} + +/* ------------------------------------------------------------------ + * URB control + * ------------------------------------------------------------------ + */ + +/* + * IRQ callback, called by URB callback + */ +static void tm6000_irq_callback(struct urb *urb) +{ + struct tm6000_dmaqueue *dma_q = urb->context; + struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq); + int i; + + switch (urb->status) { + case 0: + case -ETIMEDOUT: + break; + + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + return; + + default: + tm6000_err("urb completion error %d.\n", urb->status); + break; + } + + spin_lock(&dev->slock); + tm6000_isoc_copy(urb); + spin_unlock(&dev->slock); + + /* Reset urb buffers */ + for (i = 0; i < urb->number_of_packets; i++) { + urb->iso_frame_desc[i].status = 0; + urb->iso_frame_desc[i].actual_length = 0; + } + + urb->status = usb_submit_urb(urb, GFP_ATOMIC); + if (urb->status) + tm6000_err("urb resubmit failed (error=%i)\n", + urb->status); +} + +/* + * Stop and Deallocate URBs + */ +static void tm6000_uninit_isoc(struct tm6000_core *dev) +{ + struct urb *urb; + int i; + + dev->isoc_ctl.buf = NULL; + for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { + urb = dev->isoc_ctl.urb[i]; + if (urb) { + usb_kill_urb(urb); + usb_unlink_urb(urb); + if (dev->isoc_ctl.transfer_buffer[i]) { + usb_free_coherent(dev->udev, + urb->transfer_buffer_length, + dev->isoc_ctl.transfer_buffer[i], + urb->transfer_dma); + } + usb_free_urb(urb); + dev->isoc_ctl.urb[i] = NULL; + } + dev->isoc_ctl.transfer_buffer[i] = NULL; + } + + kfree(dev->isoc_ctl.urb); + kfree(dev->isoc_ctl.transfer_buffer); + + dev->isoc_ctl.urb = NULL; + dev->isoc_ctl.transfer_buffer = NULL; + dev->isoc_ctl.num_bufs = 0; +} + +/* + * Allocate URBs and start IRQ + */ +static int tm6000_prepare_isoc(struct tm6000_core *dev) +{ + struct tm6000_dmaqueue *dma_q = &dev->vidq; + int i, j, sb_size, pipe, size, max_packets, num_bufs = 8; + struct urb *urb; + + /* De-allocates all pending stuff */ + tm6000_uninit_isoc(dev); + /* Stop interrupt USB pipe */ + tm6000_ir_int_stop(dev); + + usb_set_interface(dev->udev, + dev->isoc_in.bInterfaceNumber, + dev->isoc_in.bAlternateSetting); + + /* Start interrupt USB pipe */ + tm6000_ir_int_start(dev); + + pipe = usb_rcvisocpipe(dev->udev, + dev->isoc_in.endp->desc.bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK); + + size = usb_maxpacket(dev->udev, pipe, usb_pipeout(pipe)); + + if (size > dev->isoc_in.maxsize) + size = dev->isoc_in.maxsize; + + dev->isoc_ctl.max_pkt_size = size; + + max_packets = TM6000_MAX_ISO_PACKETS; + sb_size = max_packets * size; + + dev->isoc_ctl.num_bufs = num_bufs; + + dev->isoc_ctl.urb = kmalloc(sizeof(void *)*num_bufs, GFP_KERNEL); + if (!dev->isoc_ctl.urb) { + tm6000_err("cannot alloc memory for usb buffers\n"); + return -ENOMEM; + } + + dev->isoc_ctl.transfer_buffer = kmalloc(sizeof(void *)*num_bufs, + GFP_KERNEL); + if (!dev->isoc_ctl.transfer_buffer) { + tm6000_err("cannot allocate memory for usbtransfer\n"); + kfree(dev->isoc_ctl.urb); + return -ENOMEM; + } + + dprintk(dev, V4L2_DEBUG_QUEUE, "Allocating %d x %d packets" + " (%d bytes) of %d bytes each to handle %u size\n", + max_packets, num_bufs, sb_size, + dev->isoc_in.maxsize, size); + + /* allocate urbs and transfer buffers */ + for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { + urb = usb_alloc_urb(max_packets, GFP_KERNEL); + if (!urb) { + tm6000_err("cannot alloc isoc_ctl.urb %i\n", i); + tm6000_uninit_isoc(dev); + usb_free_urb(urb); + return -ENOMEM; + } + dev->isoc_ctl.urb[i] = urb; + + dev->isoc_ctl.transfer_buffer[i] = usb_alloc_coherent(dev->udev, + sb_size, GFP_KERNEL, &urb->transfer_dma); + if (!dev->isoc_ctl.transfer_buffer[i]) { + tm6000_err("unable to allocate %i bytes for transfer" + " buffer %i%s\n", + sb_size, i, + in_interrupt() ? " while in int" : ""); + tm6000_uninit_isoc(dev); + return -ENOMEM; + } + memset(dev->isoc_ctl.transfer_buffer[i], 0, sb_size); + + usb_fill_bulk_urb(urb, dev->udev, pipe, + dev->isoc_ctl.transfer_buffer[i], sb_size, + tm6000_irq_callback, dma_q); + urb->interval = dev->isoc_in.endp->desc.bInterval; + urb->number_of_packets = max_packets; + urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; + + for (j = 0; j < max_packets; j++) { + urb->iso_frame_desc[j].offset = size * j; + urb->iso_frame_desc[j].length = size; + } + } + + return 0; +} + +static int tm6000_start_thread(struct tm6000_core *dev) +{ + struct tm6000_dmaqueue *dma_q = &dev->vidq; + int i; + + dma_q->frame = 0; + dma_q->ini_jiffies = jiffies; + + init_waitqueue_head(&dma_q->wq); + + /* submit urbs and enables IRQ */ + for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { + int rc = usb_submit_urb(dev->isoc_ctl.urb[i], GFP_ATOMIC); + if (rc) { + tm6000_err("submit of urb %i failed (error=%i)\n", i, + rc); + tm6000_uninit_isoc(dev); + return rc; + } + } + + return 0; +} + +/* ------------------------------------------------------------------ + * Videobuf operations + * ------------------------------------------------------------------ + */ + +static int +buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size) +{ + struct tm6000_fh *fh = vq->priv_data; + + *size = fh->fmt->depth * fh->width * fh->height >> 3; + if (0 == *count) + *count = TM6000_DEF_BUF; + + if (*count < TM6000_MIN_BUF) + *count = TM6000_MIN_BUF; + + while (*size * *count > vid_limit * 1024 * 1024) + (*count)--; + + return 0; +} + +static void free_buffer(struct videobuf_queue *vq, struct tm6000_buffer *buf) +{ + struct tm6000_fh *fh = vq->priv_data; + struct tm6000_core *dev = fh->dev; + unsigned long flags; + + if (in_interrupt()) + BUG(); + + /* We used to wait for the buffer to finish here, but this didn't work + because, as we were keeping the state as VIDEOBUF_QUEUED, + videobuf_queue_cancel marked it as finished for us. + (Also, it could wedge forever if the hardware was misconfigured.) + + This should be safe; by the time we get here, the buffer isn't + queued anymore. If we ever start marking the buffers as + VIDEOBUF_ACTIVE, it won't be, though. + */ + spin_lock_irqsave(&dev->slock, flags); + if (dev->isoc_ctl.buf == buf) + dev->isoc_ctl.buf = NULL; + spin_unlock_irqrestore(&dev->slock, flags); + + videobuf_vmalloc_free(&buf->vb); + buf->vb.state = VIDEOBUF_NEEDS_INIT; +} + +static int +buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct tm6000_fh *fh = vq->priv_data; + struct tm6000_buffer *buf = container_of(vb, struct tm6000_buffer, vb); + struct tm6000_core *dev = fh->dev; + int rc = 0; + + BUG_ON(NULL == fh->fmt); + + + /* FIXME: It assumes depth=2 */ + /* The only currently supported format is 16 bits/pixel */ + buf->vb.size = fh->fmt->depth*fh->width*fh->height >> 3; + if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) + return -EINVAL; + + if (buf->fmt != fh->fmt || + buf->vb.width != fh->width || + buf->vb.height != fh->height || + buf->vb.field != field) { + buf->fmt = fh->fmt; + buf->vb.width = fh->width; + buf->vb.height = fh->height; + buf->vb.field = field; + buf->vb.state = VIDEOBUF_NEEDS_INIT; + } + + if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { + rc = videobuf_iolock(vq, &buf->vb, NULL); + if (rc != 0) + goto fail; + } + + if (!dev->isoc_ctl.num_bufs) { + rc = tm6000_prepare_isoc(dev); + if (rc < 0) + goto fail; + + rc = tm6000_start_thread(dev); + if (rc < 0) + goto fail; + + } + + buf->vb.state = VIDEOBUF_PREPARED; + return 0; + +fail: + free_buffer(vq, buf); + return rc; +} + +static void +buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct tm6000_buffer *buf = container_of(vb, struct tm6000_buffer, vb); + struct tm6000_fh *fh = vq->priv_data; + struct tm6000_core *dev = fh->dev; + struct tm6000_dmaqueue *vidq = &dev->vidq; + + buf->vb.state = VIDEOBUF_QUEUED; + list_add_tail(&buf->vb.queue, &vidq->active); +} + +static void buffer_release(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct tm6000_buffer *buf = container_of(vb, struct tm6000_buffer, vb); + + free_buffer(vq, buf); +} + +static struct videobuf_queue_ops tm6000_video_qops = { + .buf_setup = buffer_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .buf_release = buffer_release, +}; + +/* ------------------------------------------------------------------ + * IOCTL handling + * ------------------------------------------------------------------ + */ + +static bool is_res_read(struct tm6000_core *dev, struct tm6000_fh *fh) +{ + /* Is the current fh handling it? if so, that's OK */ + if (dev->resources == fh && dev->is_res_read) + return true; + + return false; +} + +static bool is_res_streaming(struct tm6000_core *dev, struct tm6000_fh *fh) +{ + /* Is the current fh handling it? if so, that's OK */ + if (dev->resources == fh) + return true; + + return false; +} + +static bool res_get(struct tm6000_core *dev, struct tm6000_fh *fh, + bool is_res_read) +{ + /* Is the current fh handling it? if so, that's OK */ + if (dev->resources == fh && dev->is_res_read == is_res_read) + return true; + + /* is it free? */ + if (dev->resources) + return false; + + /* grab it */ + dev->resources = fh; + dev->is_res_read = is_res_read; + dprintk(dev, V4L2_DEBUG_RES_LOCK, "res: get\n"); + return true; +} + +static void res_free(struct tm6000_core *dev, struct tm6000_fh *fh) +{ + /* Is the current fh handling it? if so, that's OK */ + if (dev->resources != fh) + return; + + dev->resources = NULL; + dprintk(dev, V4L2_DEBUG_RES_LOCK, "res: put\n"); +} + +/* ------------------------------------------------------------------ + * IOCTL vidioc handling + * ------------------------------------------------------------------ + */ +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct tm6000_core *dev = ((struct tm6000_fh *)priv)->dev; + + strlcpy(cap->driver, "tm6000", sizeof(cap->driver)); + strlcpy(cap->card, "Trident TVMaster TM5600/6000/6010", sizeof(cap->card)); + cap->version = TM6000_VERSION; + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_STREAMING | + V4L2_CAP_AUDIO | + V4L2_CAP_READWRITE; + + if (dev->tuner_type != TUNER_ABSENT) + cap->capabilities |= V4L2_CAP_TUNER; + + return 0; +} + +static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + if (unlikely(f->index >= ARRAY_SIZE(format))) + return -EINVAL; + + strlcpy(f->description, format[f->index].name, sizeof(f->description)); + f->pixelformat = format[f->index].fourcc; + return 0; +} + +static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct tm6000_fh *fh = priv; + + f->fmt.pix.width = fh->width; + f->fmt.pix.height = fh->height; + f->fmt.pix.field = fh->vb_vidq.field; + f->fmt.pix.pixelformat = fh->fmt->fourcc; + f->fmt.pix.bytesperline = + (f->fmt.pix.width * fh->fmt->depth) >> 3; + f->fmt.pix.sizeimage = + f->fmt.pix.height * f->fmt.pix.bytesperline; + + return 0; +} + +static struct tm6000_fmt *format_by_fourcc(unsigned int fourcc) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(format); i++) + if (format[i].fourcc == fourcc) + return format+i; + return NULL; +} + +static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct tm6000_core *dev = ((struct tm6000_fh *)priv)->dev; + struct tm6000_fmt *fmt; + enum v4l2_field field; + + fmt = format_by_fourcc(f->fmt.pix.pixelformat); + if (NULL == fmt) { + dprintk(dev, V4L2_DEBUG_IOCTL_ARG, "Fourcc format (0x%08x)" + " invalid.\n", f->fmt.pix.pixelformat); + return -EINVAL; + } + + field = f->fmt.pix.field; + + if (field == V4L2_FIELD_ANY) + field = V4L2_FIELD_SEQ_TB; + else if (V4L2_FIELD_INTERLACED != field) { + dprintk(dev, V4L2_DEBUG_IOCTL_ARG, "Field type invalid.\n"); + return -EINVAL; + } + + tm6000_get_std_res(dev); + + f->fmt.pix.width = dev->width; + f->fmt.pix.height = dev->height; + + f->fmt.pix.width &= ~0x01; + + f->fmt.pix.field = field; + + f->fmt.pix.bytesperline = + (f->fmt.pix.width * fmt->depth) >> 3; + f->fmt.pix.sizeimage = + f->fmt.pix.height * f->fmt.pix.bytesperline; + + return 0; +} + +/*FIXME: This seems to be generic enough to be at videodev2 */ +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + int ret = vidioc_try_fmt_vid_cap(file, fh, f); + if (ret < 0) + return ret; + + fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); + fh->width = f->fmt.pix.width; + fh->height = f->fmt.pix.height; + fh->vb_vidq.field = f->fmt.pix.field; + fh->type = f->type; + + dev->fourcc = f->fmt.pix.pixelformat; + + tm6000_set_fourcc_format(dev); + + return 0; +} + +static int vidioc_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *p) +{ + struct tm6000_fh *fh = priv; + + return videobuf_reqbufs(&fh->vb_vidq, p); +} + +static int vidioc_querybuf(struct file *file, void *priv, + struct v4l2_buffer *p) +{ + struct tm6000_fh *fh = priv; + + return videobuf_querybuf(&fh->vb_vidq, p); +} + +static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct tm6000_fh *fh = priv; + + return videobuf_qbuf(&fh->vb_vidq, p); +} + +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct tm6000_fh *fh = priv; + + return videobuf_dqbuf(&fh->vb_vidq, p, + file->f_flags & O_NONBLOCK); +} + +static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + + if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (i != fh->type) + return -EINVAL; + + if (!res_get(dev, fh, false)) + return -EBUSY; + return videobuf_streamon(&fh->vb_vidq); +} + +static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + + if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (i != fh->type) + return -EINVAL; + + videobuf_streamoff(&fh->vb_vidq); + res_free(dev, fh); + + return 0; +} + +static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm) +{ + int rc = 0; + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + + dev->norm = *norm; + rc = tm6000_init_analog_mode(dev); + + fh->width = dev->width; + fh->height = dev->height; + + if (rc < 0) + return rc; + + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm); + + return 0; +} + +static const char *iname[] = { + [TM6000_INPUT_TV] = "Television", + [TM6000_INPUT_COMPOSITE1] = "Composite 1", + [TM6000_INPUT_COMPOSITE2] = "Composite 2", + [TM6000_INPUT_SVIDEO] = "S-Video", +}; + +static int vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *i) +{ + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + unsigned int n; + + n = i->index; + if (n >= 3) + return -EINVAL; + + if (!dev->vinput[n].type) + return -EINVAL; + + i->index = n; + + if (dev->vinput[n].type == TM6000_INPUT_TV) + i->type = V4L2_INPUT_TYPE_TUNER; + else + i->type = V4L2_INPUT_TYPE_CAMERA; + + strcpy(i->name, iname[dev->vinput[n].type]); + + i->std = TM6000_STD; + + return 0; +} + +static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) +{ + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + + *i = dev->input; + + return 0; +} + +static int vidioc_s_input(struct file *file, void *priv, unsigned int i) +{ + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + int rc = 0; + + if (i >= 3) + return -EINVAL; + if (!dev->vinput[i].type) + return -EINVAL; + + dev->input = i; + + rc = vidioc_s_std(file, priv, &dev->vfd->current_norm); + + return rc; +} + +/* --- controls ---------------------------------------------- */ +static int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(tm6000_qctrl); i++) + if (qc->id && qc->id == tm6000_qctrl[i].id) { + memcpy(qc, &(tm6000_qctrl[i]), + sizeof(*qc)); + return 0; + } + + return -EINVAL; +} + +static int vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + int val; + + /* FIXME: Probably, those won't work! Maybe we need shadow regs */ + switch (ctrl->id) { + case V4L2_CID_CONTRAST: + val = tm6000_get_reg(dev, TM6010_REQ07_R08_LUMA_CONTRAST_ADJ, 0); + break; + case V4L2_CID_BRIGHTNESS: + val = tm6000_get_reg(dev, TM6010_REQ07_R09_LUMA_BRIGHTNESS_ADJ, 0); + return 0; + case V4L2_CID_SATURATION: + val = tm6000_get_reg(dev, TM6010_REQ07_R0A_CHROMA_SATURATION_ADJ, 0); + return 0; + case V4L2_CID_HUE: + val = tm6000_get_reg(dev, TM6010_REQ07_R0B_CHROMA_HUE_PHASE_ADJ, 0); + return 0; + case V4L2_CID_AUDIO_MUTE: + val = dev->ctl_mute; + return 0; + case V4L2_CID_AUDIO_VOLUME: + val = dev->ctl_volume; + return 0; + default: + return -EINVAL; + } + + if (val < 0) + return val; + + ctrl->value = val; + + return 0; +} +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + u8 val = ctrl->value; + + switch (ctrl->id) { + case V4L2_CID_CONTRAST: + tm6000_set_reg(dev, TM6010_REQ07_R08_LUMA_CONTRAST_ADJ, val); + return 0; + case V4L2_CID_BRIGHTNESS: + tm6000_set_reg(dev, TM6010_REQ07_R09_LUMA_BRIGHTNESS_ADJ, val); + return 0; + case V4L2_CID_SATURATION: + tm6000_set_reg(dev, TM6010_REQ07_R0A_CHROMA_SATURATION_ADJ, val); + return 0; + case V4L2_CID_HUE: + tm6000_set_reg(dev, TM6010_REQ07_R0B_CHROMA_HUE_PHASE_ADJ, val); + return 0; + case V4L2_CID_AUDIO_MUTE: + dev->ctl_mute = val; + tm6000_tvaudio_set_mute(dev, val); + return 0; + case V4L2_CID_AUDIO_VOLUME: + dev->ctl_volume = val; + tm6000_set_volume(dev, val); + return 0; + } + return -EINVAL; +} + +static int vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + + if (unlikely(UNSET == dev->tuner_type)) + return -EINVAL; + if (0 != t->index) + return -EINVAL; + + strcpy(t->name, "Television"); + t->type = V4L2_TUNER_ANALOG_TV; + t->capability = V4L2_TUNER_CAP_NORM; + t->rangehigh = 0xffffffffUL; + t->rxsubchans = V4L2_TUNER_SUB_STEREO; + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t); + + t->audmode = dev->amode; + + return 0; +} + +static int vidioc_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + + if (UNSET == dev->tuner_type) + return -EINVAL; + if (0 != t->index) + return -EINVAL; + + dev->amode = t->audmode; + dprintk(dev, 3, "audio mode: %x\n", t->audmode); + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t); + + return 0; +} + +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + + if (unlikely(UNSET == dev->tuner_type)) + return -EINVAL; + + f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; + f->frequency = dev->freq; + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_frequency, f); + + return 0; +} + +static int vidioc_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + + if (unlikely(UNSET == dev->tuner_type)) + return -EINVAL; + if (unlikely(f->tuner != 0)) + return -EINVAL; + if (0 == fh->radio && V4L2_TUNER_ANALOG_TV != f->type) + return -EINVAL; + if (1 == fh->radio && V4L2_TUNER_RADIO != f->type) + return -EINVAL; + + dev->freq = f->frequency; + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, f); + + return 0; +} + +static int radio_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct tm6000_fh *fh = file->private_data; + struct tm6000_core *dev = fh->dev; + + strcpy(cap->driver, "tm6000"); + strlcpy(cap->card, dev->name, sizeof(dev->name)); + sprintf(cap->bus_info, "USB%04x:%04x", + le16_to_cpu(dev->udev->descriptor.idVendor), + le16_to_cpu(dev->udev->descriptor.idProduct)); + cap->version = dev->dev_type; + cap->capabilities = V4L2_CAP_TUNER | + V4L2_CAP_AUDIO | + V4L2_CAP_RADIO | + V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; + + return 0; +} + +static int radio_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct tm6000_fh *fh = file->private_data; + struct tm6000_core *dev = fh->dev; + + if (0 != t->index) + return -EINVAL; + + memset(t, 0, sizeof(*t)); + strcpy(t->name, "Radio"); + t->type = V4L2_TUNER_RADIO; + t->rxsubchans = V4L2_TUNER_SUB_STEREO; + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t); + + return 0; +} + +static int radio_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct tm6000_fh *fh = file->private_data; + struct tm6000_core *dev = fh->dev; + + if (0 != t->index) + return -EINVAL; + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t); + + return 0; +} + +static int radio_enum_input(struct file *file, void *priv, + struct v4l2_input *i) +{ + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + + if (i->index != 0) + return -EINVAL; + + if (!dev->rinput.type) + return -EINVAL; + + strcpy(i->name, "Radio"); + i->type = V4L2_INPUT_TYPE_TUNER; + + return 0; +} + +static int radio_g_input(struct file *filp, void *priv, unsigned int *i) +{ + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + + if (dev->input != 5) + return -EINVAL; + + *i = dev->input - 5; + + return 0; +} + +static int radio_g_audio(struct file *file, void *priv, + struct v4l2_audio *a) +{ + memset(a, 0, sizeof(*a)); + strcpy(a->name, "Radio"); + return 0; +} + +static int radio_s_audio(struct file *file, void *priv, + struct v4l2_audio *a) +{ + return 0; +} + +static int radio_s_input(struct file *filp, void *priv, unsigned int i) +{ + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + + if (i) + return -EINVAL; + + if (!dev->rinput.type) + return -EINVAL; + + dev->input = i + 5; + + return 0; +} + +static int radio_s_std(struct file *file, void *fh, v4l2_std_id *norm) +{ + return 0; +} + +static int radio_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *c) +{ + const struct v4l2_queryctrl *ctrl; + + if (c->id < V4L2_CID_BASE || + c->id >= V4L2_CID_LASTP1) + return -EINVAL; + if (c->id == V4L2_CID_AUDIO_MUTE) { + ctrl = ctrl_by_id(c->id); + *c = *ctrl; + } else + *c = no_ctrl; + + return 0; +} + +/* ------------------------------------------------------------------ + File operations for the device + ------------------------------------------------------------------*/ + +static int tm6000_open(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct tm6000_core *dev = video_drvdata(file); + struct tm6000_fh *fh; + enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + int i, rc; + int radio = 0; + + dprintk(dev, V4L2_DEBUG_OPEN, "tm6000: open called (dev=%s)\n", + video_device_node_name(vdev)); + + switch (vdev->vfl_type) { + case VFL_TYPE_GRABBER: + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + break; + case VFL_TYPE_VBI: + type = V4L2_BUF_TYPE_VBI_CAPTURE; + break; + case VFL_TYPE_RADIO: + radio = 1; + break; + } + + /* If more than one user, mutex should be added */ + dev->users++; + + dprintk(dev, V4L2_DEBUG_OPEN, "open dev=%s type=%s users=%d\n", + video_device_node_name(vdev), v4l2_type_names[type], + dev->users); + + /* allocate + initialize per filehandle data */ + fh = kzalloc(sizeof(*fh), GFP_KERNEL); + if (NULL == fh) { + dev->users--; + return -ENOMEM; + } + + file->private_data = fh; + fh->dev = dev; + fh->radio = radio; + dev->radio = radio; + fh->type = type; + dev->fourcc = format[0].fourcc; + + fh->fmt = format_by_fourcc(dev->fourcc); + + tm6000_get_std_res(dev); + + fh->width = dev->width; + fh->height = dev->height; + + dprintk(dev, V4L2_DEBUG_OPEN, "Open: fh=0x%08lx, dev=0x%08lx, " + "dev->vidq=0x%08lx\n", + (unsigned long)fh, (unsigned long)dev, + (unsigned long)&dev->vidq); + dprintk(dev, V4L2_DEBUG_OPEN, "Open: list_empty " + "queued=%d\n", list_empty(&dev->vidq.queued)); + dprintk(dev, V4L2_DEBUG_OPEN, "Open: list_empty " + "active=%d\n", list_empty(&dev->vidq.active)); + + /* initialize hardware on analog mode */ + rc = tm6000_init_analog_mode(dev); + if (rc < 0) + return rc; + + if (dev->mode != TM6000_MODE_ANALOG) { + /* Put all controls at a sane state */ + for (i = 0; i < ARRAY_SIZE(tm6000_qctrl); i++) + qctl_regs[i] = tm6000_qctrl[i].default_value; + + dev->mode = TM6000_MODE_ANALOG; + } + + if (!fh->radio) { + videobuf_queue_vmalloc_init(&fh->vb_vidq, &tm6000_video_qops, + NULL, &dev->slock, + fh->type, + V4L2_FIELD_INTERLACED, + sizeof(struct tm6000_buffer), fh, &dev->lock); + } else { + dprintk(dev, V4L2_DEBUG_OPEN, "video_open: setting radio device\n"); + dev->input = 5; + tm6000_set_audio_rinput(dev); + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_radio); + tm6000_prepare_isoc(dev); + tm6000_start_thread(dev); + } + + return 0; +} + +static ssize_t +tm6000_read(struct file *file, char __user *data, size_t count, loff_t *pos) +{ + struct tm6000_fh *fh = file->private_data; + + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + if (!res_get(fh->dev, fh, true)) + return -EBUSY; + + return videobuf_read_stream(&fh->vb_vidq, data, count, pos, 0, + file->f_flags & O_NONBLOCK); + } + return 0; +} + +static unsigned int +tm6000_poll(struct file *file, struct poll_table_struct *wait) +{ + struct tm6000_fh *fh = file->private_data; + struct tm6000_buffer *buf; + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type) + return POLLERR; + + if (!!is_res_streaming(fh->dev, fh)) + return POLLERR; + + if (!is_res_read(fh->dev, fh)) { + /* streaming capture */ + if (list_empty(&fh->vb_vidq.stream)) + return POLLERR; + buf = list_entry(fh->vb_vidq.stream.next, struct tm6000_buffer, vb.stream); + } else { + /* read() capture */ + return videobuf_poll_stream(file, &fh->vb_vidq, wait); + } + poll_wait(file, &buf->vb.done, wait); + if (buf->vb.state == VIDEOBUF_DONE || + buf->vb.state == VIDEOBUF_ERROR) + return POLLIN | POLLRDNORM; + return 0; +} + +static int tm6000_release(struct file *file) +{ + struct tm6000_fh *fh = file->private_data; + struct tm6000_core *dev = fh->dev; + struct video_device *vdev = video_devdata(file); + + dprintk(dev, V4L2_DEBUG_OPEN, "tm6000: close called (dev=%s, users=%d)\n", + video_device_node_name(vdev), dev->users); + + dev->users--; + + res_free(dev, fh); + + if (!dev->users) { + int err; + + tm6000_uninit_isoc(dev); + + if (!fh->radio) + videobuf_mmap_free(&fh->vb_vidq); + + err = tm6000_reset(dev); + if (err < 0) + dev_err(&vdev->dev, "reset failed: %d\n", err); + } + + kfree(fh); + + return 0; +} + +static int tm6000_mmap(struct file *file, struct vm_area_struct * vma) +{ + struct tm6000_fh *fh = file->private_data; + + return videobuf_mmap_mapper(&fh->vb_vidq, vma); +} + +static struct v4l2_file_operations tm6000_fops = { + .owner = THIS_MODULE, + .open = tm6000_open, + .release = tm6000_release, + .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */ + .read = tm6000_read, + .poll = tm6000_poll, + .mmap = tm6000_mmap, +}; + +static const struct v4l2_ioctl_ops video_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, + .vidioc_s_std = vidioc_s_std, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, +}; + +static struct video_device tm6000_template = { + .name = "tm6000", + .fops = &tm6000_fops, + .ioctl_ops = &video_ioctl_ops, + .release = video_device_release, + .tvnorms = TM6000_STD, + .current_norm = V4L2_STD_NTSC_M, +}; + +static const struct v4l2_file_operations radio_fops = { + .owner = THIS_MODULE, + .open = tm6000_open, + .release = tm6000_release, + .unlocked_ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops radio_ioctl_ops = { + .vidioc_querycap = radio_querycap, + .vidioc_g_tuner = radio_g_tuner, + .vidioc_enum_input = radio_enum_input, + .vidioc_g_audio = radio_g_audio, + .vidioc_s_tuner = radio_s_tuner, + .vidioc_s_audio = radio_s_audio, + .vidioc_s_input = radio_s_input, + .vidioc_s_std = radio_s_std, + .vidioc_queryctrl = radio_queryctrl, + .vidioc_g_input = radio_g_input, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, +}; + +static struct video_device tm6000_radio_template = { + .name = "tm6000", + .fops = &radio_fops, + .ioctl_ops = &radio_ioctl_ops, +}; + +/* ----------------------------------------------------------------- + * Initialization and module stuff + * ------------------------------------------------------------------ + */ + +static struct video_device *vdev_init(struct tm6000_core *dev, + const struct video_device + *template, const char *type_name) +{ + struct video_device *vfd; + + vfd = video_device_alloc(); + if (NULL == vfd) + return NULL; + + *vfd = *template; + vfd->v4l2_dev = &dev->v4l2_dev; + vfd->release = video_device_release; + vfd->debug = tm6000_debug; + vfd->lock = &dev->lock; + + snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name); + + video_set_drvdata(vfd, dev); + return vfd; +} + +int tm6000_v4l2_register(struct tm6000_core *dev) +{ + int ret = -1; + + dev->vfd = vdev_init(dev, &tm6000_template, "video"); + + if (!dev->vfd) { + printk(KERN_INFO "%s: can't register video device\n", + dev->name); + return -ENOMEM; + } + + /* init video dma queues */ + INIT_LIST_HEAD(&dev->vidq.active); + INIT_LIST_HEAD(&dev->vidq.queued); + + ret = video_register_device(dev->vfd, VFL_TYPE_GRABBER, video_nr); + + if (ret < 0) { + printk(KERN_INFO "%s: can't register video device\n", + dev->name); + return ret; + } + + printk(KERN_INFO "%s: registered device %s\n", + dev->name, video_device_node_name(dev->vfd)); + + if (dev->caps.has_radio) { + dev->radio_dev = vdev_init(dev, &tm6000_radio_template, + "radio"); + if (!dev->radio_dev) { + printk(KERN_INFO "%s: can't register radio device\n", + dev->name); + return ret; /* FIXME release resource */ + } + + ret = video_register_device(dev->radio_dev, VFL_TYPE_RADIO, + radio_nr); + if (ret < 0) { + printk(KERN_INFO "%s: can't register radio device\n", + dev->name); + return ret; /* FIXME release resource */ + } + + printk(KERN_INFO "%s: registered device %s\n", + dev->name, video_device_node_name(dev->radio_dev)); + } + + printk(KERN_INFO "Trident TVMaster TM5600/TM6000/TM6010 USB2 board (Load status: %d)\n", ret); + return ret; +} + +int tm6000_v4l2_unregister(struct tm6000_core *dev) +{ + video_unregister_device(dev->vfd); + + if (dev->radio_dev) { + if (video_is_registered(dev->radio_dev)) + video_unregister_device(dev->radio_dev); + else + video_device_release(dev->radio_dev); + dev->radio_dev = NULL; + } + + return 0; +} + +int tm6000_v4l2_exit(void) +{ + return 0; +} + +module_param(video_nr, int, 0); +MODULE_PARM_DESC(video_nr, "Allow changing video device number"); + +module_param_named(debug, tm6000_debug, int, 0444); +MODULE_PARM_DESC(debug, "activates debug info"); + +module_param(vid_limit, int, 0644); +MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes"); + diff --git a/drivers/media/video/tm6000/tm6000.h b/drivers/media/video/tm6000/tm6000.h new file mode 100644 index 00000000000..2777e514eff --- /dev/null +++ b/drivers/media/video/tm6000/tm6000.h @@ -0,0 +1,398 @@ +/* + * tm6000.h - driver for TM5600/TM6000/TM6010 USB video capture devices + * + * Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org> + * + * Copyright (C) 2007 Michel Ludwig <michel.ludwig@gmail.com> + * - DVB-T support + * + * 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 version 2 + * + * 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 <linux/videodev2.h> +#include <media/v4l2-common.h> +#include <media/videobuf-vmalloc.h> +#include "tm6000-usb-isoc.h" +#include <linux/i2c.h> +#include <linux/mutex.h> +#include <media/v4l2-device.h> + +#include <linux/dvb/frontend.h> +#include "dvb_demux.h" +#include "dvb_frontend.h" +#include "dmxdev.h" + +#define TM6000_VERSION KERNEL_VERSION(0, 0, 2) + +/* Inputs */ +enum tm6000_itype { + TM6000_INPUT_TV = 1, + TM6000_INPUT_COMPOSITE1, + TM6000_INPUT_COMPOSITE2, + TM6000_INPUT_SVIDEO, + TM6000_INPUT_DVB, + TM6000_INPUT_RADIO, +}; + +enum tm6000_mux { + TM6000_VMUX_VIDEO_A = 1, + TM6000_VMUX_VIDEO_B, + TM6000_VMUX_VIDEO_AB, + TM6000_AMUX_ADC1, + TM6000_AMUX_ADC2, + TM6000_AMUX_SIF1, + TM6000_AMUX_SIF2, + TM6000_AMUX_I2S, +}; + +enum tm6000_devtype { + TM6000 = 0, + TM5600, + TM6010, +}; + +struct tm6000_input { + enum tm6000_itype type; + enum tm6000_mux vmux; + enum tm6000_mux amux; + unsigned int v_gpio; + unsigned int a_gpio; +}; + +/* ------------------------------------------------------------------ + * Basic structures + * ------------------------------------------------------------------ + */ + +struct tm6000_fmt { + char *name; + u32 fourcc; /* v4l2 format id */ + int depth; +}; + +/* buffer for one video frame */ +struct tm6000_buffer { + /* common v4l buffer stuff -- must be first */ + struct videobuf_buffer vb; + + struct tm6000_fmt *fmt; +}; + +struct tm6000_dmaqueue { + struct list_head active; + struct list_head queued; + + /* thread for generating video stream*/ + struct task_struct *kthread; + wait_queue_head_t wq; + /* Counters to control fps rate */ + int frame; + int ini_jiffies; +}; + +/* device states */ +enum tm6000_core_state { + DEV_INITIALIZED = 0x01, + DEV_DISCONNECTED = 0x02, + DEV_MISCONFIGURED = 0x04, +}; + +/* io methods */ +enum tm6000_io_method { + IO_NONE, + IO_READ, + IO_MMAP, +}; + +enum tm6000_mode { + TM6000_MODE_UNKNOWN = 0, + TM6000_MODE_ANALOG, + TM6000_MODE_DIGITAL, +}; + +struct tm6000_gpio { + int tuner_reset; + int tuner_on; + int demod_reset; + int demod_on; + int power_led; + int dvb_led; + int ir; +}; + +struct tm6000_capabilities { + unsigned int has_tuner:1; + unsigned int has_tda9874:1; + unsigned int has_dvb:1; + unsigned int has_zl10353:1; + unsigned int has_eeprom:1; + unsigned int has_remote:1; + unsigned int has_radio:1; +}; + +struct tm6000_dvb { + struct dvb_adapter adapter; + struct dvb_demux demux; + struct dvb_frontend *frontend; + struct dmxdev dmxdev; + unsigned int streams; + struct urb *bulk_urb; + struct mutex mutex; +}; + +struct snd_tm6000_card { + struct snd_card *card; + spinlock_t reg_lock; + struct tm6000_core *core; + struct snd_pcm_substream *substream; + + /* temporary data for buffer fill processing */ + unsigned buf_pos; + unsigned period_pos; +}; + +struct tm6000_endpoint { + struct usb_host_endpoint *endp; + __u8 bInterfaceNumber; + __u8 bAlternateSetting; + unsigned maxsize; +}; + +#define TM6000_QUIRK_NO_USB_DELAY (1 << 0) + +struct tm6000_core { + /* generic device properties */ + char name[30]; /* name (including minor) of the device */ + int model; /* index in the device_data struct */ + int devno; /* marks the number of this device */ + enum tm6000_devtype dev_type; /* type of device */ + unsigned char eedata[256]; /* Eeprom data */ + unsigned eedata_size; /* Size of the eeprom info */ + + v4l2_std_id norm; /* Current norm */ + int width, height; /* Selected resolution */ + + enum tm6000_core_state state; + + /* Device Capabilities*/ + struct tm6000_capabilities caps; + + /* Tuner configuration */ + int tuner_type; /* type of the tuner */ + int tuner_addr; /* tuner address */ + + struct tm6000_gpio gpio; + + char *ir_codes; + + __u8 radio; + + /* Demodulator configuration */ + int demod_addr; /* demodulator address */ + + int audio_bitrate; + /* i2c i/o */ + struct i2c_adapter i2c_adap; + struct i2c_client i2c_client; + + + /* extension */ + struct list_head devlist; + + /* video for linux */ + int users; + + /* various device info */ + struct tm6000_fh *resources; /* Points to fh that is streaming */ + bool is_res_read; + + struct video_device *vfd; + struct video_device *radio_dev; + struct tm6000_dmaqueue vidq; + struct v4l2_device v4l2_dev; + + int input; + struct tm6000_input vinput[3]; /* video input */ + struct tm6000_input rinput; /* radio input */ + + int freq; + unsigned int fourcc; + + enum tm6000_mode mode; + + int ctl_mute; /* audio */ + int ctl_volume; + int amode; + + /* DVB-T support */ + struct tm6000_dvb *dvb; + + /* audio support */ + struct snd_tm6000_card *adev; + struct work_struct wq_trigger; /* Trigger to start/stop audio for alsa module */ + atomic_t stream_started; /* stream should be running if true */ + + struct tm6000_IR *ir; + + /* locks */ + struct mutex lock; + struct mutex usb_lock; + + /* usb transfer */ + struct usb_device *udev; /* the usb device */ + + struct tm6000_endpoint bulk_in, bulk_out, isoc_in, isoc_out; + struct tm6000_endpoint int_in, int_out; + + /* scaler!=0 if scaler is active*/ + int scaler; + + /* Isoc control struct */ + struct usb_isoc_ctl isoc_ctl; + + spinlock_t slock; + + unsigned long quirks; +}; + +enum tm6000_ops_type { + TM6000_AUDIO = 0x10, + TM6000_DVB = 0x20, +}; + +struct tm6000_ops { + struct list_head next; + char *name; + enum tm6000_ops_type type; + int (*init)(struct tm6000_core *); + int (*fini)(struct tm6000_core *); + int (*fillbuf)(struct tm6000_core *, char *buf, int size); +}; + +struct tm6000_fh { + struct tm6000_core *dev; + unsigned int radio; + + /* video capture */ + struct tm6000_fmt *fmt; + unsigned int width, height; + struct videobuf_queue vb_vidq; + + enum v4l2_buf_type type; +}; + +#define TM6000_STD (V4L2_STD_PAL|V4L2_STD_PAL_N|V4L2_STD_PAL_Nc| \ + V4L2_STD_PAL_M|V4L2_STD_PAL_60|V4L2_STD_NTSC_M| \ + V4L2_STD_NTSC_M_JP|V4L2_STD_SECAM) + +/* In tm6000-cards.c */ + +int tm6000_tuner_callback(void *ptr, int component, int command, int arg); +int tm6000_xc5000_callback(void *ptr, int component, int command, int arg); +int tm6000_cards_setup(struct tm6000_core *dev); +void tm6000_flash_led(struct tm6000_core *dev, u8 state); + +/* In tm6000-core.c */ + +int tm6000_read_write_usb(struct tm6000_core *dev, u8 reqtype, u8 req, + u16 value, u16 index, u8 *buf, u16 len); +int tm6000_get_reg(struct tm6000_core *dev, u8 req, u16 value, u16 index); +int tm6000_get_reg16(struct tm6000_core *dev, u8 req, u16 value, u16 index); +int tm6000_get_reg32(struct tm6000_core *dev, u8 req, u16 value, u16 index); +int tm6000_set_reg(struct tm6000_core *dev, u8 req, u16 value, u16 index); +int tm6000_set_reg_mask(struct tm6000_core *dev, u8 req, u16 value, + u16 index, u16 mask); +int tm6000_i2c_reset(struct tm6000_core *dev, u16 tsleep); +int tm6000_init(struct tm6000_core *dev); +int tm6000_reset(struct tm6000_core *dev); + +int tm6000_init_analog_mode(struct tm6000_core *dev); +int tm6000_init_digital_mode(struct tm6000_core *dev); +int tm6000_set_audio_bitrate(struct tm6000_core *dev, int bitrate); +int tm6000_set_audio_rinput(struct tm6000_core *dev); +int tm6000_tvaudio_set_mute(struct tm6000_core *dev, u8 mute); +void tm6000_set_volume(struct tm6000_core *dev, int vol); + +int tm6000_v4l2_register(struct tm6000_core *dev); +int tm6000_v4l2_unregister(struct tm6000_core *dev); +int tm6000_v4l2_exit(void); +void tm6000_set_fourcc_format(struct tm6000_core *dev); + +void tm6000_remove_from_devlist(struct tm6000_core *dev); +void tm6000_add_into_devlist(struct tm6000_core *dev); +int tm6000_register_extension(struct tm6000_ops *ops); +void tm6000_unregister_extension(struct tm6000_ops *ops); +void tm6000_init_extension(struct tm6000_core *dev); +void tm6000_close_extension(struct tm6000_core *dev); +int tm6000_call_fillbuf(struct tm6000_core *dev, enum tm6000_ops_type type, + char *buf, int size); + + +/* In tm6000-stds.c */ +void tm6000_get_std_res(struct tm6000_core *dev); +int tm6000_set_standard(struct tm6000_core *dev); + +/* In tm6000-i2c.c */ +int tm6000_i2c_register(struct tm6000_core *dev); +int tm6000_i2c_unregister(struct tm6000_core *dev); + +/* In tm6000-queue.c */ + +int tm6000_v4l2_mmap(struct file *filp, struct vm_area_struct *vma); + +int tm6000_vidioc_streamon(struct file *file, void *priv, + enum v4l2_buf_type i); +int tm6000_vidioc_streamoff(struct file *file, void *priv, + enum v4l2_buf_type i); +int tm6000_vidioc_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *rb); +int tm6000_vidioc_querybuf(struct file *file, void *priv, + struct v4l2_buffer *b); +int tm6000_vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b); +int tm6000_vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b); +ssize_t tm6000_v4l2_read(struct file *filp, char __user * buf, size_t count, + loff_t *f_pos); +unsigned int tm6000_v4l2_poll(struct file *file, + struct poll_table_struct *wait); +int tm6000_queue_init(struct tm6000_core *dev); + +/* In tm6000-alsa.c */ +/*int tm6000_audio_init(struct tm6000_core *dev, int idx);*/ + +/* In tm6000-input.c */ +int tm6000_ir_init(struct tm6000_core *dev); +int tm6000_ir_fini(struct tm6000_core *dev); +void tm6000_ir_wait(struct tm6000_core *dev, u8 state); +int tm6000_ir_int_start(struct tm6000_core *dev); +void tm6000_ir_int_stop(struct tm6000_core *dev); + +/* Debug stuff */ + +extern int tm6000_debug; + +#define dprintk(dev, level, fmt, arg...) do {\ + if (tm6000_debug & level) \ + printk(KERN_INFO "(%lu) %s %s :"fmt, jiffies, \ + dev->name, __func__ , ##arg); } while (0) + +#define V4L2_DEBUG_REG 0x0004 +#define V4L2_DEBUG_I2C 0x0008 +#define V4L2_DEBUG_QUEUE 0x0010 +#define V4L2_DEBUG_ISOC 0x0020 +#define V4L2_DEBUG_RES_LOCK 0x0040 /* Resource locking */ +#define V4L2_DEBUG_OPEN 0x0080 /* video open/close debug */ + +#define tm6000_err(fmt, arg...) do {\ + printk(KERN_ERR "tm6000 %s :"fmt, \ + __func__ , ##arg); } while (0) diff --git a/drivers/media/video/tvaudio.c b/drivers/media/video/tvaudio.c index c46a3bb9585..f22dbef9b95 100644 --- a/drivers/media/video/tvaudio.c +++ b/drivers/media/video/tvaudio.c @@ -1695,14 +1695,17 @@ static int tvaudio_s_ctrl(struct v4l2_subdev *sd, case V4L2_CID_AUDIO_BALANCE: { int volume, balance; + if (!(desc->flags & CHIP_HAS_VOLUME)) break; - volume = max(chip->left,chip->right); + volume = max(chip->left, chip->right); balance = ctrl->value; + chip->left = (min(65536 - balance, 32768) * volume) / 32768; + chip->right = (min(balance, volume * (__u16)32768)) / 32768; - chip_write(chip,desc->leftreg,desc->volfunc(chip->left)); - chip_write(chip,desc->rightreg,desc->volfunc(chip->right)); + chip_write(chip, desc->leftreg, desc->volfunc(chip->left)); + chip_write(chip, desc->rightreg, desc->volfunc(chip->right)); return 0; } diff --git a/drivers/media/video/tvp514x.c b/drivers/media/video/tvp514x.c index 9b3e828b077..926f0393115 100644 --- a/drivers/media/video/tvp514x.c +++ b/drivers/media/video/tvp514x.c @@ -32,6 +32,7 @@ #include <linux/slab.h> #include <linux/delay.h> #include <linux/videodev2.h> +#include <linux/module.h> #include <media/v4l2-device.h> #include <media/v4l2-common.h> diff --git a/drivers/media/video/tvp5150.c b/drivers/media/video/tvp5150.c index e927d25e0d3..6abaa16ae13 100644 --- a/drivers/media/video/tvp5150.c +++ b/drivers/media/video/tvp5150.c @@ -9,6 +9,7 @@ #include <linux/slab.h> #include <linux/videodev2.h> #include <linux/delay.h> +#include <linux/module.h> #include <media/v4l2-device.h> #include <media/tvp5150.h> #include <media/v4l2-chip-ident.h> diff --git a/drivers/media/video/tvp5150_reg.h b/drivers/media/video/tvp5150_reg.h index 4240043c0b2..25a99494491 100644 --- a/drivers/media/video/tvp5150_reg.h +++ b/drivers/media/video/tvp5150_reg.h @@ -45,7 +45,22 @@ /* Reserved 1Fh-27h */ -#define TVP5150_VIDEO_STD 0x28 /* Video standard */ +#define VIDEO_STD_MASK (0x07 >> 1) +#define TVP5150_VIDEO_STD 0x28 /* Video standard */ +#define VIDEO_STD_AUTO_SWITCH_BIT 0x00 +#define VIDEO_STD_NTSC_MJ_BIT 0x02 +#define VIDEO_STD_PAL_BDGHIN_BIT 0x04 +#define VIDEO_STD_PAL_M_BIT 0x06 +#define VIDEO_STD_PAL_COMBINATION_N_BIT 0x08 +#define VIDEO_STD_NTSC_4_43_BIT 0x0a +#define VIDEO_STD_SECAM_BIT 0x0c + +#define VIDEO_STD_NTSC_MJ_BIT_AS 0x01 +#define VIDEO_STD_PAL_BDGHIN_BIT_AS 0x03 +#define VIDEO_STD_PAL_M_BIT_AS 0x05 +#define VIDEO_STD_PAL_COMBINATION_N_BIT_AS 0x07 +#define VIDEO_STD_NTSC_4_43_BIT_AS 0x09 +#define VIDEO_STD_SECAM_BIT_AS 0x0b /* Reserved 29h-2bh */ diff --git a/drivers/media/video/tvp7002.c b/drivers/media/video/tvp7002.c index b799851bf3d..7875e80cb2f 100644 --- a/drivers/media/video/tvp7002.c +++ b/drivers/media/video/tvp7002.c @@ -28,6 +28,7 @@ #include <linux/i2c.h> #include <linux/slab.h> #include <linux/videodev2.h> +#include <linux/module.h> #include <media/tvp7002.h> #include <media/v4l2-device.h> #include <media/v4l2-chip-ident.h> @@ -128,7 +129,7 @@ static const struct i2c_reg_value tvp7002_init_default[] = { { TVP7002_ADC_SETUP, 0x50, TVP7002_WRITE }, { TVP7002_COARSE_CLAMP_CTL, 0x00, TVP7002_WRITE }, { TVP7002_SOG_CLAMP, 0x80, TVP7002_WRITE }, - { TVP7002_RGB_COARSE_CLAMP_CTL, 0x00, TVP7002_WRITE }, + { TVP7002_RGB_COARSE_CLAMP_CTL, 0x8c, TVP7002_WRITE }, { TVP7002_SOG_COARSE_CLAMP_CTL, 0x04, TVP7002_WRITE }, { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE }, { 0x32, 0x18, TVP7002_RESERVED }, @@ -182,7 +183,6 @@ static const struct i2c_reg_value tvp7002_parms_480P[] = { { TVP7002_HPLL_FDBK_DIV_MSBS, 0x35, TVP7002_WRITE }, { TVP7002_HPLL_FDBK_DIV_LSBS, 0xa0, TVP7002_WRITE }, { TVP7002_HPLL_CRTL, 0x02, TVP7002_WRITE }, - { TVP7002_HPLL_PHASE_SEL, 0x14, TVP7002_WRITE }, { TVP7002_AVID_START_PIXEL_LSBS, 0x91, TVP7002_WRITE }, { TVP7002_AVID_START_PIXEL_MSBS, 0x00, TVP7002_WRITE }, { TVP7002_AVID_STOP_PIXEL_LSBS, 0x0B, TVP7002_WRITE }, @@ -204,7 +204,6 @@ static const struct i2c_reg_value tvp7002_parms_576P[] = { { TVP7002_HPLL_FDBK_DIV_MSBS, 0x36, TVP7002_WRITE }, { TVP7002_HPLL_FDBK_DIV_LSBS, 0x00, TVP7002_WRITE }, { TVP7002_HPLL_CRTL, 0x18, TVP7002_WRITE }, - { TVP7002_HPLL_PHASE_SEL, 0x14, TVP7002_WRITE }, { TVP7002_AVID_START_PIXEL_LSBS, 0x9B, TVP7002_WRITE }, { TVP7002_AVID_START_PIXEL_MSBS, 0x00, TVP7002_WRITE }, { TVP7002_AVID_STOP_PIXEL_LSBS, 0x0F, TVP7002_WRITE }, @@ -226,7 +225,6 @@ static const struct i2c_reg_value tvp7002_parms_1080I60[] = { { TVP7002_HPLL_FDBK_DIV_MSBS, 0x89, TVP7002_WRITE }, { TVP7002_HPLL_FDBK_DIV_LSBS, 0x80, TVP7002_WRITE }, { TVP7002_HPLL_CRTL, 0x98, TVP7002_WRITE }, - { TVP7002_HPLL_PHASE_SEL, 0x14, TVP7002_WRITE }, { TVP7002_AVID_START_PIXEL_LSBS, 0x06, TVP7002_WRITE }, { TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE }, { TVP7002_AVID_STOP_PIXEL_LSBS, 0x8a, TVP7002_WRITE }, @@ -248,7 +246,6 @@ static const struct i2c_reg_value tvp7002_parms_1080P60[] = { { TVP7002_HPLL_FDBK_DIV_MSBS, 0x89, TVP7002_WRITE }, { TVP7002_HPLL_FDBK_DIV_LSBS, 0x80, TVP7002_WRITE }, { TVP7002_HPLL_CRTL, 0xE0, TVP7002_WRITE }, - { TVP7002_HPLL_PHASE_SEL, 0x14, TVP7002_WRITE }, { TVP7002_AVID_START_PIXEL_LSBS, 0x06, TVP7002_WRITE }, { TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE }, { TVP7002_AVID_STOP_PIXEL_LSBS, 0x8a, TVP7002_WRITE }, @@ -270,7 +267,6 @@ static const struct i2c_reg_value tvp7002_parms_1080I50[] = { { TVP7002_HPLL_FDBK_DIV_MSBS, 0xa5, TVP7002_WRITE }, { TVP7002_HPLL_FDBK_DIV_LSBS, 0x00, TVP7002_WRITE }, { TVP7002_HPLL_CRTL, 0x98, TVP7002_WRITE }, - { TVP7002_HPLL_PHASE_SEL, 0x14, TVP7002_WRITE }, { TVP7002_AVID_START_PIXEL_LSBS, 0x06, TVP7002_WRITE }, { TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE }, { TVP7002_AVID_STOP_PIXEL_LSBS, 0x8a, TVP7002_WRITE }, @@ -292,7 +288,6 @@ static const struct i2c_reg_value tvp7002_parms_720P60[] = { { TVP7002_HPLL_FDBK_DIV_MSBS, 0x67, TVP7002_WRITE }, { TVP7002_HPLL_FDBK_DIV_LSBS, 0x20, TVP7002_WRITE }, { TVP7002_HPLL_CRTL, 0xa0, TVP7002_WRITE }, - { TVP7002_HPLL_PHASE_SEL, 0x16, TVP7002_WRITE }, { TVP7002_AVID_START_PIXEL_LSBS, 0x47, TVP7002_WRITE }, { TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE }, { TVP7002_AVID_STOP_PIXEL_LSBS, 0x4B, TVP7002_WRITE }, @@ -314,7 +309,6 @@ static const struct i2c_reg_value tvp7002_parms_720P50[] = { { TVP7002_HPLL_FDBK_DIV_MSBS, 0x7b, TVP7002_WRITE }, { TVP7002_HPLL_FDBK_DIV_LSBS, 0xc0, TVP7002_WRITE }, { TVP7002_HPLL_CRTL, 0x98, TVP7002_WRITE }, - { TVP7002_HPLL_PHASE_SEL, 0x16, TVP7002_WRITE }, { TVP7002_AVID_START_PIXEL_LSBS, 0x47, TVP7002_WRITE }, { TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE }, { TVP7002_AVID_STOP_PIXEL_LSBS, 0x4B, TVP7002_WRITE }, @@ -687,6 +681,9 @@ static int tvp7002_query_dv_preset(struct v4l2_subdev *sd, u8 cpl_msb; int index; + /* Return invalid preset if no active input is detected */ + qpreset->preset = V4L2_DV_INVALID; + device = to_tvp7002(sd); /* Read standards from device registers */ @@ -720,8 +717,6 @@ static int tvp7002_query_dv_preset(struct v4l2_subdev *sd, if (index == NUM_PRESETS) { v4l2_dbg(1, debug, sd, "detection failed: lpf = %x, cpl = %x\n", lpfr, cpln); - /* Could not detect a signal, so return the 'invalid' preset */ - qpreset->preset = V4L2_DV_INVALID; return 0; } diff --git a/drivers/media/video/tw9910.c b/drivers/media/video/tw9910.c index 742482e3001..a514fa61116 100644 --- a/drivers/media/video/tw9910.c +++ b/drivers/media/video/tw9910.c @@ -22,11 +22,13 @@ #include <linux/slab.h> #include <linux/kernel.h> #include <linux/delay.h> +#include <linux/v4l2-mediabus.h> #include <linux/videodev2.h> -#include <media/v4l2-chip-ident.h> -#include <media/v4l2-subdev.h> + #include <media/soc_camera.h> #include <media/tw9910.h> +#include <media/v4l2-chip-ident.h> +#include <media/v4l2-subdev.h> #define GET_ID(val) ((val & 0xF8) >> 3) #define GET_REV(val) (val & 0x07) @@ -203,6 +205,10 @@ #define RTSEL_FIELD 0x06 /* 0110 = FIELD */ #define RTSEL_RTCO 0x07 /* 0111 = RTCO ( Real Time Control ) */ +/* HSYNC start and end are constant for now */ +#define HSYNC_START 0x0260 +#define HSYNC_END 0x0300 + /* * structure */ @@ -220,22 +226,11 @@ struct tw9910_scale_ctrl { u16 vscale; }; -struct tw9910_cropping_ctrl { - u16 vdelay; - u16 vactive; - u16 hdelay; - u16 hactive; -}; - -struct tw9910_hsync_ctrl { - u16 start; - u16 end; -}; - struct tw9910_priv { struct v4l2_subdev subdev; struct tw9910_video_info *info; const struct tw9910_scale_ctrl *scale; + v4l2_std_id norm; u32 revision; }; @@ -329,11 +324,6 @@ static const struct tw9910_scale_ctrl tw9910_pal_scales[] = { }, }; -static const struct tw9910_hsync_ctrl tw9910_hsync_ctrl = { - .start = 0x0260, - .end = 0x0300, -}; - /* * general function */ @@ -378,21 +368,20 @@ static int tw9910_set_scale(struct i2c_client *client, return ret; } -static int tw9910_set_hsync(struct i2c_client *client, - const struct tw9910_hsync_ctrl *hsync) +static int tw9910_set_hsync(struct i2c_client *client) { struct tw9910_priv *priv = to_tw9910(client); int ret; /* bit 10 - 3 */ ret = i2c_smbus_write_byte_data(client, HSBEGIN, - (hsync->start & 0x07F8) >> 3); + (HSYNC_START & 0x07F8) >> 3); if (ret < 0) return ret; /* bit 10 - 3 */ ret = i2c_smbus_write_byte_data(client, HSEND, - (hsync->end & 0x07F8) >> 3); + (HSYNC_END & 0x07F8) >> 3); if (ret < 0) return ret; @@ -400,8 +389,8 @@ static int tw9910_set_hsync(struct i2c_client *client, /* bit 2 - 0 */ if (1 == priv->revision) ret = tw9910_mask_set(client, HSLOWCTL, 0x77, - (hsync->start & 0x0007) << 4 | - (hsync->end & 0x0007)); + (HSYNC_START & 0x0007) << 4 | + (HSYNC_END & 0x0007)); return ret; } @@ -433,12 +422,11 @@ static int tw9910_power(struct i2c_client *client, int enable) return tw9910_mask_set(client, ACNTL2, ACNTL2_PDN_MASK, acntl2); } -static const struct tw9910_scale_ctrl* -tw9910_select_norm(struct soc_camera_device *icd, u32 width, u32 height) +static const struct tw9910_scale_ctrl *tw9910_select_norm(v4l2_std_id norm, + u32 width, u32 height) { const struct tw9910_scale_ctrl *scale; const struct tw9910_scale_ctrl *ret = NULL; - v4l2_std_id norm = icd->vdev->current_norm; __u32 diff = 0xffffffff, tmp; int size, i; @@ -465,7 +453,7 @@ tw9910_select_norm(struct soc_camera_device *icd, u32 width, u32 height) } /* - * soc_camera_ops function + * subdevice operations */ static int tw9910_s_stream(struct v4l2_subdev *sd, int enable) { @@ -507,49 +495,27 @@ static int tw9910_s_stream(struct v4l2_subdev *sd, int enable) return tw9910_power(client, enable); } -static int tw9910_set_bus_param(struct soc_camera_device *icd, - unsigned long flags) +static int tw9910_g_std(struct v4l2_subdev *sd, v4l2_std_id *norm) { - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); struct i2c_client *client = v4l2_get_subdevdata(sd); - u8 val = VSSL_VVALID | HSSL_DVALID; + struct tw9910_priv *priv = to_tw9910(client); - /* - * set OUTCTR1 - * - * We use VVALID and DVALID signals to control VSYNC and HSYNC - * outputs, in this mode their polarity is inverted. - */ - if (flags & SOCAM_HSYNC_ACTIVE_LOW) - val |= HSP_HI; + *norm = priv->norm; - if (flags & SOCAM_VSYNC_ACTIVE_LOW) - val |= VSP_HI; - - return i2c_smbus_write_byte_data(client, OUTCTR1, val); + return 0; } -static unsigned long tw9910_query_bus_param(struct soc_camera_device *icd) +static int tw9910_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) { - struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd)); + struct i2c_client *client = v4l2_get_subdevdata(sd); struct tw9910_priv *priv = to_tw9910(client); - struct soc_camera_link *icl = to_soc_camera_link(icd); - unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER | - SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH | - SOCAM_VSYNC_ACTIVE_LOW | SOCAM_HSYNC_ACTIVE_LOW | - SOCAM_DATA_ACTIVE_HIGH | priv->info->buswidth; - return soc_camera_apply_sensor_flags(icl, flags); -} - -static int tw9910_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) -{ - int ret = -EINVAL; + if (!(norm & (V4L2_STD_NTSC | V4L2_STD_PAL))) + return -EINVAL; - if (norm & (V4L2_STD_NTSC | V4L2_STD_PAL)) - ret = 0; + priv->norm = norm; - return ret; + return 0; } static int tw9910_g_chip_ident(struct v4l2_subdev *sd, @@ -600,19 +566,17 @@ static int tw9910_s_register(struct v4l2_subdev *sd, } #endif -static int tw9910_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +static int tw9910_set_frame(struct v4l2_subdev *sd, u32 *width, u32 *height) { - struct v4l2_rect *rect = &a->c; struct i2c_client *client = v4l2_get_subdevdata(sd); struct tw9910_priv *priv = to_tw9910(client); - struct soc_camera_device *icd = client->dev.platform_data; - int ret = -EINVAL; - u8 val; + int ret = -EINVAL; + u8 val; /* * select suitable norm */ - priv->scale = tw9910_select_norm(icd, rect->width, rect->height); + priv->scale = tw9910_select_norm(priv->norm, *width, *height); if (!priv->scale) goto tw9910_set_fmt_error; @@ -670,14 +634,12 @@ static int tw9910_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) /* * set hsync */ - ret = tw9910_set_hsync(client, &tw9910_hsync_ctrl); + ret = tw9910_set_hsync(client); if (ret < 0) goto tw9910_set_fmt_error; - rect->width = priv->scale->width; - rect->height = priv->scale->height; - rect->left = 0; - rect->top = 0; + *width = priv->scale->width; + *height = priv->scale->height; return ret; @@ -694,25 +656,15 @@ static int tw9910_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) struct i2c_client *client = v4l2_get_subdevdata(sd); struct tw9910_priv *priv = to_tw9910(client); - if (!priv->scale) { - int ret; - struct v4l2_crop crop = { - .c = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - }; - ret = tw9910_s_crop(sd, &crop); - if (ret < 0) - return ret; - } - a->c.left = 0; a->c.top = 0; - a->c.width = priv->scale->width; - a->c.height = priv->scale->height; + if (priv->norm & V4L2_STD_NTSC) { + a->c.width = 640; + a->c.height = 480; + } else { + a->c.width = 768; + a->c.height = 576; + } a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; return 0; @@ -720,14 +672,19 @@ static int tw9910_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) static int tw9910_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct tw9910_priv *priv = to_tw9910(client); + a->bounds.left = 0; a->bounds.top = 0; - a->bounds.width = 768; - a->bounds.height = 576; - a->defrect.left = 0; - a->defrect.top = 0; - a->defrect.width = 640; - a->defrect.height = 480; + if (priv->norm & V4L2_STD_NTSC) { + a->bounds.width = 640; + a->bounds.height = 480; + } else { + a->bounds.width = 768; + a->bounds.height = 576; + } + a->defrect = a->bounds; a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; a->pixelaspect.numerator = 1; a->pixelaspect.denominator = 1; @@ -743,15 +700,8 @@ static int tw9910_g_fmt(struct v4l2_subdev *sd, if (!priv->scale) { int ret; - struct v4l2_crop crop = { - .c = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - }; - ret = tw9910_s_crop(sd, &crop); + u32 width = 640, height = 480; + ret = tw9910_set_frame(sd, &width, &height); if (ret < 0) return ret; } @@ -768,17 +718,7 @@ static int tw9910_g_fmt(struct v4l2_subdev *sd, static int tw9910_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) { - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct tw9910_priv *priv = to_tw9910(client); - /* See tw9910_s_crop() - no proper cropping support */ - struct v4l2_crop a = { - .c = { - .left = 0, - .top = 0, - .width = mf->width, - .height = mf->height, - }, - }; + u32 width = mf->width, height = mf->height; int ret; WARN_ON(mf->field != V4L2_FIELD_ANY && @@ -792,10 +732,10 @@ static int tw9910_s_fmt(struct v4l2_subdev *sd, mf->colorspace = V4L2_COLORSPACE_JPEG; - ret = tw9910_s_crop(sd, &a); + ret = tw9910_set_frame(sd, &width, &height); if (!ret) { - mf->width = priv->scale->width; - mf->height = priv->scale->height; + mf->width = width; + mf->height = height; } return ret; } @@ -804,7 +744,7 @@ static int tw9910_try_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_device *icd = client->dev.platform_data; + struct tw9910_priv *priv = to_tw9910(client); const struct tw9910_scale_ctrl *scale; if (V4L2_FIELD_ANY == mf->field) { @@ -820,7 +760,7 @@ static int tw9910_try_fmt(struct v4l2_subdev *sd, /* * select suitable norm */ - scale = tw9910_select_norm(icd, mf->width, mf->height); + scale = tw9910_select_norm(priv->norm, mf->width, mf->height); if (!scale) return -EINVAL; @@ -830,16 +770,11 @@ static int tw9910_try_fmt(struct v4l2_subdev *sd, return 0; } -static int tw9910_video_probe(struct soc_camera_device *icd, - struct i2c_client *client) +static int tw9910_video_probe(struct i2c_client *client) { struct tw9910_priv *priv = to_tw9910(client); s32 id; - /* We must have a parent by now. And it cannot be a wrong one. */ - BUG_ON(!icd->parent || - to_soc_camera_host(icd->parent)->nr != icd->iface); - /* * tw9910 only use 8 or 16 bit bus width */ @@ -868,20 +803,15 @@ static int tw9910_video_probe(struct soc_camera_device *icd, dev_info(&client->dev, "tw9910 Product ID %0x:%0x\n", id, priv->revision); - icd->vdev->tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL; - icd->vdev->current_norm = V4L2_STD_NTSC; + priv->norm = V4L2_STD_NTSC; return 0; } -static struct soc_camera_ops tw9910_ops = { - .set_bus_param = tw9910_set_bus_param, - .query_bus_param = tw9910_query_bus_param, -}; - static struct v4l2_subdev_core_ops tw9910_subdev_core_ops = { .g_chip_ident = tw9910_g_chip_ident, .s_std = tw9910_s_std, + .g_std = tw9910_g_std, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = tw9910_g_register, .s_register = tw9910_s_register, @@ -898,6 +828,45 @@ static int tw9910_enum_fmt(struct v4l2_subdev *sd, unsigned int index, return 0; } +static int tw9910_g_mbus_config(struct v4l2_subdev *sd, + struct v4l2_mbus_config *cfg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + + cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER | + V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW | + V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW | + V4L2_MBUS_DATA_ACTIVE_HIGH; + cfg->type = V4L2_MBUS_PARALLEL; + cfg->flags = soc_camera_apply_board_flags(icl, cfg); + + return 0; +} + +static int tw9910_s_mbus_config(struct v4l2_subdev *sd, + const struct v4l2_mbus_config *cfg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + u8 val = VSSL_VVALID | HSSL_DVALID; + unsigned long flags = soc_camera_apply_board_flags(icl, cfg); + + /* + * set OUTCTR1 + * + * We use VVALID and DVALID signals to control VSYNC and HSYNC + * outputs, in this mode their polarity is inverted. + */ + if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) + val |= HSP_HI; + + if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) + val |= VSP_HI; + + return i2c_smbus_write_byte_data(client, OUTCTR1, val); +} + static struct v4l2_subdev_video_ops tw9910_subdev_video_ops = { .s_stream = tw9910_s_stream, .g_mbus_fmt = tw9910_g_fmt, @@ -905,8 +874,9 @@ static struct v4l2_subdev_video_ops tw9910_subdev_video_ops = { .try_mbus_fmt = tw9910_try_fmt, .cropcap = tw9910_cropcap, .g_crop = tw9910_g_crop, - .s_crop = tw9910_s_crop, .enum_mbus_fmt = tw9910_enum_fmt, + .g_mbus_config = tw9910_g_mbus_config, + .s_mbus_config = tw9910_s_mbus_config, }; static struct v4l2_subdev_ops tw9910_subdev_ops = { @@ -922,23 +892,18 @@ static int tw9910_probe(struct i2c_client *client, const struct i2c_device_id *did) { - struct tw9910_priv *priv; - struct tw9910_video_info *info; - struct soc_camera_device *icd = client->dev.platform_data; - struct i2c_adapter *adapter = + struct tw9910_priv *priv; + struct tw9910_video_info *info; + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - struct soc_camera_link *icl; - int ret; + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + int ret; - if (!icd) { - dev_err(&client->dev, "TW9910: missing soc-camera data!\n"); + if (!icl || !icl->priv) { + dev_err(&client->dev, "TW9910: missing platform data!\n"); return -EINVAL; } - icl = to_soc_camera_link(icd); - if (!icl || !icl->priv) - return -EINVAL; - info = icl->priv; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { @@ -956,14 +921,9 @@ static int tw9910_probe(struct i2c_client *client, v4l2_i2c_subdev_init(&priv->subdev, client, &tw9910_subdev_ops); - icd->ops = &tw9910_ops; - icd->iface = icl->bus_id; - - ret = tw9910_video_probe(icd, client); - if (ret) { - icd->ops = NULL; + ret = tw9910_video_probe(client); + if (ret) kfree(priv); - } return ret; } @@ -971,9 +931,7 @@ static int tw9910_probe(struct i2c_client *client, static int tw9910_remove(struct i2c_client *client) { struct tw9910_priv *priv = to_tw9910(client); - struct soc_camera_device *icd = client->dev.platform_data; - icd->ops = NULL; kfree(priv); return 0; } diff --git a/drivers/media/video/usbvision/Makefile b/drivers/media/video/usbvision/Makefile index 33871875094..aea1e3b5f06 100644 --- a/drivers/media/video/usbvision/Makefile +++ b/drivers/media/video/usbvision/Makefile @@ -2,5 +2,5 @@ usbvision-objs := usbvision-core.o usbvision-video.o usbvision-i2c.o usbvision- obj-$(CONFIG_VIDEO_USBVISION) += usbvision.o -EXTRA_CFLAGS += -Idrivers/media/video -EXTRA_CFLAGS += -Idrivers/media/common/tuners +ccflags-y += -Idrivers/media/video +ccflags-y += -Idrivers/media/common/tuners diff --git a/drivers/media/video/usbvision/usbvision-cards.c b/drivers/media/video/usbvision/usbvision-cards.c index 8f5266157f1..3103d0d020e 100644 --- a/drivers/media/video/usbvision/usbvision-cards.c +++ b/drivers/media/video/usbvision/usbvision-cards.c @@ -24,6 +24,7 @@ #include <linux/list.h> +#include <linux/module.h> #include <media/v4l2-dev.h> #include <media/tuner.h> #include "usbvision.h" diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c index 10c2364f3e8..254d3268884 100644 --- a/drivers/media/video/uvc/uvc_ctrl.c +++ b/drivers/media/video/uvc/uvc_ctrl.c @@ -1016,7 +1016,8 @@ int uvc_query_v4l2_menu(struct uvc_video_chain *chain, menu_info = &mapping->menu_info[query_menu->index]; - if (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES) { + if (mapping->data_type == UVC_CTRL_DATA_TYPE_BITMASK && + (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES)) { s32 bitmap; if (!ctrl->cached) { @@ -1225,7 +1226,8 @@ int uvc_ctrl_set(struct uvc_video_chain *chain, /* Valid menu indices are reported by the GET_RES request for * UVC controls that support it. */ - if (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES) { + if (mapping->data_type == UVC_CTRL_DATA_TYPE_BITMASK && + (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES)) { if (!ctrl->cached) { ret = uvc_ctrl_populate_cache(chain, ctrl); if (ret < 0) diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c index d29f9c2d085..656d4c9e3b9 100644 --- a/drivers/media/video/uvc/uvc_driver.c +++ b/drivers/media/video/uvc/uvc_driver.c @@ -114,6 +114,11 @@ static struct uvc_format_desc uvc_fmts[] = { .guid = UVC_GUID_FORMAT_RGBP, .fcc = V4L2_PIX_FMT_RGB565, }, + { + .name = "H.264", + .guid = UVC_GUID_FORMAT_H264, + .fcc = V4L2_PIX_FMT_H264, + }, }; /* ------------------------------------------------------------------------ @@ -1961,7 +1966,7 @@ static int __uvc_resume(struct usb_interface *intf, int reset) list_for_each_entry(stream, &dev->streams, list) { if (stream->intf == intf) - return uvc_video_resume(stream); + return uvc_video_resume(stream, reset); } uvc_trace(UVC_TRACE_SUSPEND, "Resume: video streaming USB interface " @@ -2331,6 +2336,14 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_PROBE_DEF }, + /* The Imaging Source USB CCD cameras */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x199e, + .idProduct = 0x8102, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0 }, /* Bodelin ProScopeHR */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_DEV_HI diff --git a/drivers/media/video/uvc/uvc_entity.c b/drivers/media/video/uvc/uvc_entity.c index 48fea373c25..29e239911d0 100644 --- a/drivers/media/video/uvc/uvc_entity.c +++ b/drivers/media/video/uvc/uvc_entity.c @@ -49,7 +49,7 @@ static int uvc_mc_register_entity(struct uvc_video_chain *chain, if (remote == NULL) return -EINVAL; - source = (UVC_ENTITY_TYPE(remote) != UVC_TT_STREAMING) + source = (UVC_ENTITY_TYPE(remote) == UVC_TT_STREAMING) ? (remote->vdev ? &remote->vdev->entity : NULL) : &remote->subdev.entity; if (source == NULL) diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c index ea71d5f1f6d..dadf11f704d 100644 --- a/drivers/media/video/uvc/uvc_v4l2.c +++ b/drivers/media/video/uvc/uvc_v4l2.c @@ -32,7 +32,7 @@ * UVC ioctls */ static int uvc_ioctl_ctrl_map(struct uvc_video_chain *chain, - struct uvc_xu_control_mapping *xmap, int old) + struct uvc_xu_control_mapping *xmap) { struct uvc_control_mapping *map; unsigned int size; @@ -58,13 +58,6 @@ static int uvc_ioctl_ctrl_map(struct uvc_video_chain *chain, break; case V4L2_CTRL_TYPE_MENU: - if (old) { - uvc_trace(UVC_TRACE_CONTROL, "V4L2_CTRL_TYPE_MENU not " - "supported for UVCIOC_CTRL_MAP_OLD.\n"); - ret = -EINVAL; - goto done; - } - size = xmap->menu_count * sizeof(*map->menu_info); map->menu_info = kmalloc(size, GFP_KERNEL); if (map->menu_info == NULL) { @@ -538,20 +531,6 @@ static int uvc_v4l2_release(struct file *file) return 0; } -static void uvc_v4l2_ioctl_warn(void) -{ - static int warned; - - if (warned) - return; - - uvc_printk(KERN_INFO, "Deprecated UVCIOC_CTRL_{ADD,MAP_OLD,GET,SET} " - "ioctls will be removed in 2.6.42.\n"); - uvc_printk(KERN_INFO, "See http://www.ideasonboard.org/uvc/upgrade/ " - "for upgrade instructions.\n"); - warned = 1; -} - static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct video_device *vdev = video_devdata(file); @@ -1032,37 +1011,8 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) uvc_trace(UVC_TRACE_IOCTL, "Unsupported ioctl 0x%08x\n", cmd); return -EINVAL; - /* Dynamic controls. UVCIOC_CTRL_ADD, UVCIOC_CTRL_MAP_OLD, - * UVCIOC_CTRL_GET and UVCIOC_CTRL_SET are deprecated and scheduled for - * removal in 2.6.42. - */ - case __UVCIOC_CTRL_ADD: - uvc_v4l2_ioctl_warn(); - return -EEXIST; - - case __UVCIOC_CTRL_MAP_OLD: - uvc_v4l2_ioctl_warn(); - case __UVCIOC_CTRL_MAP: case UVCIOC_CTRL_MAP: - return uvc_ioctl_ctrl_map(chain, arg, - cmd == __UVCIOC_CTRL_MAP_OLD); - - case __UVCIOC_CTRL_GET: - case __UVCIOC_CTRL_SET: - { - struct uvc_xu_control *xctrl = arg; - struct uvc_xu_control_query xqry = { - .unit = xctrl->unit, - .selector = xctrl->selector, - .query = cmd == __UVCIOC_CTRL_GET - ? UVC_GET_CUR : UVC_SET_CUR, - .size = xctrl->size, - .data = xctrl->data, - }; - - uvc_v4l2_ioctl_warn(); - return uvc_xu_ctrl_query(chain, &xqry); - } + return uvc_ioctl_ctrl_map(chain, arg); case UVCIOC_CTRL_QUERY: return uvc_xu_ctrl_query(chain, arg); diff --git a/drivers/media/video/uvc/uvc_video.c b/drivers/media/video/uvc/uvc_video.c index 8244167c891..b015e8e5e8b 100644 --- a/drivers/media/video/uvc/uvc_video.c +++ b/drivers/media/video/uvc/uvc_video.c @@ -790,8 +790,12 @@ static void uvc_free_urb_buffers(struct uvc_streaming *stream) for (i = 0; i < UVC_URBS; ++i) { if (stream->urb_buffer[i]) { +#ifndef CONFIG_DMA_NONCOHERENT usb_free_coherent(stream->dev->udev, stream->urb_size, stream->urb_buffer[i], stream->urb_dma[i]); +#else + kfree(stream->urb_buffer[i]); +#endif stream->urb_buffer[i] = NULL; } } @@ -831,9 +835,14 @@ static int uvc_alloc_urb_buffers(struct uvc_streaming *stream, for (; npackets > 1; npackets /= 2) { for (i = 0; i < UVC_URBS; ++i) { stream->urb_size = psize * npackets; +#ifndef CONFIG_DMA_NONCOHERENT stream->urb_buffer[i] = usb_alloc_coherent( stream->dev->udev, stream->urb_size, gfp_flags | __GFP_NOWARN, &stream->urb_dma[i]); +#else + stream->urb_buffer[i] = + kmalloc(stream->urb_size, gfp_flags | __GFP_NOWARN); +#endif if (!stream->urb_buffer[i]) { uvc_free_urb_buffers(stream); break; @@ -908,10 +917,14 @@ static int uvc_init_video_isoc(struct uvc_streaming *stream, urb->context = stream; urb->pipe = usb_rcvisocpipe(stream->dev->udev, ep->desc.bEndpointAddress); +#ifndef CONFIG_DMA_NONCOHERENT urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; + urb->transfer_dma = stream->urb_dma[i]; +#else + urb->transfer_flags = URB_ISO_ASAP; +#endif urb->interval = ep->desc.bInterval; urb->transfer_buffer = stream->urb_buffer[i]; - urb->transfer_dma = stream->urb_dma[i]; urb->complete = uvc_video_complete; urb->number_of_packets = npackets; urb->transfer_buffer_length = size; @@ -969,8 +982,10 @@ static int uvc_init_video_bulk(struct uvc_streaming *stream, usb_fill_bulk_urb(urb, stream->dev->udev, pipe, stream->urb_buffer[i], size, uvc_video_complete, stream); +#ifndef CONFIG_DMA_NONCOHERENT urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP; urb->transfer_dma = stream->urb_dma[i]; +#endif stream->urb[i] = urb; } @@ -1104,10 +1119,18 @@ int uvc_video_suspend(struct uvc_streaming *stream) * buffers, making sure userspace applications are notified of the problem * instead of waiting forever. */ -int uvc_video_resume(struct uvc_streaming *stream) +int uvc_video_resume(struct uvc_streaming *stream, int reset) { int ret; + /* If the bus has been reset on resume, set the alternate setting to 0. + * This should be the default value, but some devices crash or otherwise + * misbehave if they don't receive a SET_INTERFACE request before any + * other video control request. + */ + if (reset) + usb_set_interface(stream->dev->udev, stream->intfnum, 0); + stream->frozen = 0; ret = uvc_commit_video(stream, &stream->ctrl); diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h index df32a43ca86..4c1392ebcd4 100644 --- a/drivers/media/video/uvc/uvcvideo.h +++ b/drivers/media/video/uvc/uvcvideo.h @@ -1,106 +1,16 @@ #ifndef _USB_VIDEO_H_ #define _USB_VIDEO_H_ -#include <linux/kernel.h> -#include <linux/videodev2.h> - -#ifndef __KERNEL__ -/* - * This header provides binary compatibility with applications using the private - * uvcvideo API. This API is deprecated and will be removed in 2.6.42. - * Applications should be recompiled against the public linux/uvcvideo.h header. - */ -#warn "The uvcvideo.h header is deprecated, use linux/uvcvideo.h instead." - -/* - * Dynamic controls - */ - -/* Data types for UVC control data */ -#define UVC_CTRL_DATA_TYPE_RAW 0 -#define UVC_CTRL_DATA_TYPE_SIGNED 1 -#define UVC_CTRL_DATA_TYPE_UNSIGNED 2 -#define UVC_CTRL_DATA_TYPE_BOOLEAN 3 -#define UVC_CTRL_DATA_TYPE_ENUM 4 -#define UVC_CTRL_DATA_TYPE_BITMASK 5 - -/* Control flags */ -#define UVC_CONTROL_SET_CUR (1 << 0) -#define UVC_CONTROL_GET_CUR (1 << 1) -#define UVC_CONTROL_GET_MIN (1 << 2) -#define UVC_CONTROL_GET_MAX (1 << 3) -#define UVC_CONTROL_GET_RES (1 << 4) -#define UVC_CONTROL_GET_DEF (1 << 5) -#define UVC_CONTROL_RESTORE (1 << 6) -#define UVC_CONTROL_AUTO_UPDATE (1 << 7) - -#define UVC_CONTROL_GET_RANGE (UVC_CONTROL_GET_CUR | UVC_CONTROL_GET_MIN | \ - UVC_CONTROL_GET_MAX | UVC_CONTROL_GET_RES | \ - UVC_CONTROL_GET_DEF) - -struct uvc_menu_info { - __u32 value; - __u8 name[32]; -}; - -struct uvc_xu_control_mapping { - __u32 id; - __u8 name[32]; - __u8 entity[16]; - __u8 selector; - - __u8 size; - __u8 offset; - __u32 v4l2_type; - __u32 data_type; - - struct uvc_menu_info __user *menu_info; - __u32 menu_count; - - __u32 reserved[4]; -}; - -#endif - -struct uvc_xu_control_info { - __u8 entity[16]; - __u8 index; - __u8 selector; - __u16 size; - __u32 flags; -}; - -struct uvc_xu_control_mapping_old { - __u8 reserved[64]; -}; - -struct uvc_xu_control { - __u8 unit; - __u8 selector; - __u16 size; - __u8 __user *data; -}; - #ifndef __KERNEL__ -#define UVCIOC_CTRL_ADD _IOW('U', 1, struct uvc_xu_control_info) -#define UVCIOC_CTRL_MAP_OLD _IOWR('U', 2, struct uvc_xu_control_mapping_old) -#define UVCIOC_CTRL_MAP _IOWR('U', 2, struct uvc_xu_control_mapping) -#define UVCIOC_CTRL_GET _IOWR('U', 3, struct uvc_xu_control) -#define UVCIOC_CTRL_SET _IOW('U', 4, struct uvc_xu_control) -#else -#define __UVCIOC_CTRL_ADD _IOW('U', 1, struct uvc_xu_control_info) -#define __UVCIOC_CTRL_MAP_OLD _IOWR('U', 2, struct uvc_xu_control_mapping_old) -#define __UVCIOC_CTRL_MAP _IOWR('U', 2, struct uvc_xu_control_mapping) -#define __UVCIOC_CTRL_GET _IOWR('U', 3, struct uvc_xu_control) -#define __UVCIOC_CTRL_SET _IOW('U', 4, struct uvc_xu_control) -#endif - -#ifdef __KERNEL__ +#error "The uvcvideo.h header is deprecated, use linux/uvcvideo.h instead." +#endif /* __KERNEL__ */ +#include <linux/kernel.h> #include <linux/poll.h> #include <linux/usb.h> #include <linux/usb/video.h> #include <linux/uvcvideo.h> +#include <linux/videodev2.h> #include <media/media-device.h> #include <media/v4l2-device.h> @@ -179,6 +89,10 @@ struct uvc_xu_control { { 'M', '4', '2', '0', 0x00, 0x00, 0x10, 0x00, \ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} +#define UVC_GUID_FORMAT_H264 \ + { 'H', '2', '6', '4', 0x00, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} + /* ------------------------------------------------------------------------ * Driver specific constants. */ @@ -638,7 +552,7 @@ extern void uvc_mc_cleanup_entity(struct uvc_entity *entity); /* Video */ extern int uvc_video_init(struct uvc_streaming *stream); extern int uvc_video_suspend(struct uvc_streaming *stream); -extern int uvc_video_resume(struct uvc_streaming *stream); +extern int uvc_video_resume(struct uvc_streaming *stream, int reset); extern int uvc_video_enable(struct uvc_streaming *stream, int enable); extern int uvc_probe_video(struct uvc_streaming *stream, struct uvc_streaming_control *probe); @@ -698,6 +612,4 @@ extern struct usb_host_endpoint *uvc_find_endpoint( void uvc_video_decode_isight(struct urb *urb, struct uvc_streaming *stream, struct uvc_buffer *buf); -#endif /* __KERNEL__ */ - #endif diff --git a/drivers/media/video/v4l2-compat-ioctl32.c b/drivers/media/video/v4l2-compat-ioctl32.c index 61979b70f38..c68531b8827 100644 --- a/drivers/media/video/v4l2-compat-ioctl32.c +++ b/drivers/media/video/v4l2-compat-ioctl32.c @@ -159,11 +159,25 @@ struct v4l2_format32 { } fmt; }; -static int get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up) +/** + * struct v4l2_create_buffers32 - VIDIOC_CREATE_BUFS32 argument + * @index: on return, index of the first created buffer + * @count: entry: number of requested buffers, + * return: number of created buffers + * @memory: buffer memory type + * @format: frame format, for which buffers are requested + * @reserved: future extensions + */ +struct v4l2_create_buffers32 { + __u32 index; + __u32 count; + enum v4l2_memory memory; + struct v4l2_format32 format; + __u32 reserved[8]; +}; + +static int __get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up) { - if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_format32)) || - get_user(kp->type, &up->type)) - return -EFAULT; switch (kp->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: case V4L2_BUF_TYPE_VIDEO_OUTPUT: @@ -192,11 +206,24 @@ static int get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user } } -static int put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up) +static int get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up) +{ + if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_format32)) || + get_user(kp->type, &up->type)) + return -EFAULT; + return __get_v4l2_format32(kp, up); +} + +static int get_v4l2_create32(struct v4l2_create_buffers *kp, struct v4l2_create_buffers32 __user *up) +{ + if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_create_buffers32)) || + copy_from_user(kp, up, offsetof(struct v4l2_create_buffers32, format.fmt))) + return -EFAULT; + return __get_v4l2_format32(&kp->format, &up->format); +} + +static int __put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up) { - if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_format32)) || - put_user(kp->type, &up->type)) - return -EFAULT; switch (kp->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: case V4L2_BUF_TYPE_VIDEO_OUTPUT: @@ -225,6 +252,22 @@ static int put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user } } +static int put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up) +{ + if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_format32)) || + put_user(kp->type, &up->type)) + return -EFAULT; + return __put_v4l2_format32(kp, up); +} + +static int put_v4l2_create32(struct v4l2_create_buffers *kp, struct v4l2_create_buffers32 __user *up) +{ + if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_create_buffers32)) || + copy_to_user(up, kp, offsetof(struct v4l2_create_buffers32, format.fmt))) + return -EFAULT; + return __put_v4l2_format32(&kp->format, &up->format); +} + struct v4l2_standard32 { __u32 index; __u32 id[2]; /* __u64 would get the alignment wrong */ @@ -702,6 +745,8 @@ static int put_v4l2_event32(struct v4l2_event *kp, struct v4l2_event32 __user *u #define VIDIOC_S_EXT_CTRLS32 _IOWR('V', 72, struct v4l2_ext_controls32) #define VIDIOC_TRY_EXT_CTRLS32 _IOWR('V', 73, struct v4l2_ext_controls32) #define VIDIOC_DQEVENT32 _IOR ('V', 89, struct v4l2_event32) +#define VIDIOC_CREATE_BUFS32 _IOWR('V', 92, struct v4l2_create_buffers32) +#define VIDIOC_PREPARE_BUF32 _IOWR('V', 93, struct v4l2_buffer32) #define VIDIOC_OVERLAY32 _IOW ('V', 14, s32) #define VIDIOC_STREAMON32 _IOW ('V', 18, s32) @@ -721,6 +766,7 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar struct v4l2_standard v2s; struct v4l2_ext_controls v2ecs; struct v4l2_event v2ev; + struct v4l2_create_buffers v2crt; unsigned long vx; int vi; } karg; @@ -751,6 +797,8 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar case VIDIOC_S_INPUT32: cmd = VIDIOC_S_INPUT; break; case VIDIOC_G_OUTPUT32: cmd = VIDIOC_G_OUTPUT; break; case VIDIOC_S_OUTPUT32: cmd = VIDIOC_S_OUTPUT; break; + case VIDIOC_CREATE_BUFS32: cmd = VIDIOC_CREATE_BUFS; break; + case VIDIOC_PREPARE_BUF32: cmd = VIDIOC_PREPARE_BUF; break; } switch (cmd) { @@ -775,6 +823,12 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar compatible_arg = 0; break; + case VIDIOC_CREATE_BUFS: + err = get_v4l2_create32(&karg.v2crt, up); + compatible_arg = 0; + break; + + case VIDIOC_PREPARE_BUF: case VIDIOC_QUERYBUF: case VIDIOC_QBUF: case VIDIOC_DQBUF: @@ -860,6 +914,10 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar err = put_v4l2_format32(&karg.v2f, up); break; + case VIDIOC_CREATE_BUFS: + err = put_v4l2_create32(&karg.v2crt, up); + break; + case VIDIOC_QUERYBUF: case VIDIOC_QBUF: case VIDIOC_DQBUF: @@ -959,6 +1017,8 @@ long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg) case VIDIOC_DQEVENT32: case VIDIOC_SUBSCRIBE_EVENT: case VIDIOC_UNSUBSCRIBE_EVENT: + case VIDIOC_CREATE_BUFS32: + case VIDIOC_PREPARE_BUF32: ret = do_video_ioctl(file, cmd, arg); break; diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c index 06b6014d4fb..0f415dade05 100644 --- a/drivers/media/video/v4l2-ctrls.c +++ b/drivers/media/video/v4l2-ctrls.c @@ -20,6 +20,7 @@ #include <linux/ctype.h> #include <linux/slab.h> +#include <linux/export.h> #include <media/v4l2-ioctl.h> #include <media/v4l2-device.h> #include <media/v4l2-ctrls.h> @@ -43,7 +44,7 @@ struct v4l2_ctrl_helper { }; /* Small helper function to determine if the autocluster is set to manual - mode. In that case the is_volatile flag should be ignored. */ + mode. */ static bool is_cur_manual(const struct v4l2_ctrl *master) { return master->is_auto && master->cur.val == master->manual_mode_value; @@ -210,6 +211,7 @@ const char * const *v4l2_ctrl_get_menu(u32 id) "Disabled", "50 Hz", "60 Hz", + "Auto", NULL }; static const char * const camera_exposure_auto[] = { @@ -819,8 +821,8 @@ static void send_event(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, u32 changes) fill_event(&ev, ctrl, changes); list_for_each_entry(sev, &ctrl->ev_subs, node) - if (sev->fh && (sev->fh != fh || - (sev->flags & V4L2_EVENT_SUB_FL_ALLOW_FEEDBACK))) + if (sev->fh != fh || + (sev->flags & V4L2_EVENT_SUB_FL_ALLOW_FEEDBACK)) v4l2_event_queue_fh(sev->fh, &ev); } @@ -937,9 +939,15 @@ static void new_to_cur(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, break; } if (update_inactive) { - ctrl->flags &= ~V4L2_CTRL_FLAG_INACTIVE; - if (!is_cur_manual(ctrl->cluster[0])) + /* Note: update_inactive can only be true for auto clusters. */ + ctrl->flags &= + ~(V4L2_CTRL_FLAG_INACTIVE | V4L2_CTRL_FLAG_VOLATILE); + if (!is_cur_manual(ctrl->cluster[0])) { ctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; + if (ctrl->cluster[0]->has_volatiles) + ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; + } + fh = NULL; } if (changed || update_inactive) { /* If a control was changed that was not one of the controls @@ -1394,10 +1402,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl, type, min, max, is_menu ? cfg->menu_skip_mask : step, def, flags, qmenu, priv); - if (ctrl) { + if (ctrl) ctrl->is_private = cfg->is_private; - ctrl->is_volatile = cfg->is_volatile; - } return ctrl; } EXPORT_SYMBOL(v4l2_ctrl_new_custom); @@ -1491,6 +1497,7 @@ EXPORT_SYMBOL(v4l2_ctrl_add_handler); /* Cluster controls */ void v4l2_ctrl_cluster(unsigned ncontrols, struct v4l2_ctrl **controls) { + bool has_volatiles = false; int i; /* The first control is the master control and it must not be NULL */ @@ -1500,8 +1507,11 @@ void v4l2_ctrl_cluster(unsigned ncontrols, struct v4l2_ctrl **controls) if (controls[i]) { controls[i]->cluster = controls; controls[i]->ncontrols = ncontrols; + if (controls[i]->flags & V4L2_CTRL_FLAG_VOLATILE) + has_volatiles = true; } } + controls[0]->has_volatiles = has_volatiles; } EXPORT_SYMBOL(v4l2_ctrl_cluster); @@ -1509,22 +1519,25 @@ void v4l2_ctrl_auto_cluster(unsigned ncontrols, struct v4l2_ctrl **controls, u8 manual_val, bool set_volatile) { struct v4l2_ctrl *master = controls[0]; - u32 flag; + u32 flag = 0; int i; v4l2_ctrl_cluster(ncontrols, controls); WARN_ON(ncontrols <= 1); WARN_ON(manual_val < master->minimum || manual_val > master->maximum); + WARN_ON(set_volatile && !has_op(master, g_volatile_ctrl)); master->is_auto = true; + master->has_volatiles = set_volatile; master->manual_mode_value = manual_val; master->flags |= V4L2_CTRL_FLAG_UPDATE; - flag = is_cur_manual(master) ? 0 : V4L2_CTRL_FLAG_INACTIVE; + + if (!is_cur_manual(master)) + flag = V4L2_CTRL_FLAG_INACTIVE | + (set_volatile ? V4L2_CTRL_FLAG_VOLATILE : 0); for (i = 1; i < ncontrols; i++) - if (controls[i]) { - controls[i]->is_volatile = set_volatile; + if (controls[i]) controls[i]->flags |= flag; - } } EXPORT_SYMBOL(v4l2_ctrl_auto_cluster); @@ -1579,9 +1592,6 @@ EXPORT_SYMBOL(v4l2_ctrl_grab); static void log_ctrl(const struct v4l2_ctrl *ctrl, const char *prefix, const char *colon) { - int fl_inact = ctrl->flags & V4L2_CTRL_FLAG_INACTIVE; - int fl_grabbed = ctrl->flags & V4L2_CTRL_FLAG_GRABBED; - if (ctrl->flags & (V4L2_CTRL_FLAG_DISABLED | V4L2_CTRL_FLAG_WRITE_ONLY)) return; if (ctrl->type == V4L2_CTRL_TYPE_CTRL_CLASS) @@ -1612,14 +1622,17 @@ static void log_ctrl(const struct v4l2_ctrl *ctrl, printk(KERN_CONT "unknown type %d", ctrl->type); break; } - if (fl_inact && fl_grabbed) - printk(KERN_CONT " (inactive, grabbed)\n"); - else if (fl_inact) - printk(KERN_CONT " (inactive)\n"); - else if (fl_grabbed) - printk(KERN_CONT " (grabbed)\n"); - else - printk(KERN_CONT "\n"); + if (ctrl->flags & (V4L2_CTRL_FLAG_INACTIVE | + V4L2_CTRL_FLAG_GRABBED | + V4L2_CTRL_FLAG_VOLATILE)) { + if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE) + printk(KERN_CONT " inactive"); + if (ctrl->flags & V4L2_CTRL_FLAG_GRABBED) + printk(KERN_CONT " grabbed"); + if (ctrl->flags & V4L2_CTRL_FLAG_VOLATILE) + printk(KERN_CONT " volatile"); + } + printk(KERN_CONT "\n"); } /* Log all controls owned by the handler */ @@ -1959,7 +1972,8 @@ int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs v4l2_ctrl_lock(master); /* g_volatile_ctrl will update the new control values */ - if (has_op(master, g_volatile_ctrl) && !is_cur_manual(master)) { + if ((master->flags & V4L2_CTRL_FLAG_VOLATILE) || + (master->has_volatiles && !is_cur_manual(master))) { for (j = 0; j < master->ncontrols; j++) cur_to_new(master->cluster[j]); ret = call_op(master, g_volatile_ctrl); @@ -2004,7 +2018,7 @@ static int get_ctrl(struct v4l2_ctrl *ctrl, s32 *val) v4l2_ctrl_lock(master); /* g_volatile_ctrl will update the current control values */ - if (ctrl->is_volatile && !is_cur_manual(master)) { + if (ctrl->flags & V4L2_CTRL_FLAG_VOLATILE) { for (i = 0; i < master->ncontrols; i++) cur_to_new(master->cluster[i]); ret = call_op(master, g_volatile_ctrl); @@ -2120,6 +2134,20 @@ static int validate_ctrls(struct v4l2_ext_controls *cs, return 0; } +/* Obtain the current volatile values of an autocluster and mark them + as new. */ +static void update_from_auto_cluster(struct v4l2_ctrl *master) +{ + int i; + + for (i = 0; i < master->ncontrols; i++) + cur_to_new(master->cluster[i]); + if (!call_op(master, g_volatile_ctrl)) + for (i = 1; i < master->ncontrols; i++) + if (master->cluster[i]) + master->cluster[i]->is_new = 1; +} + /* Try or try-and-set controls */ static int try_set_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs, @@ -2165,6 +2193,31 @@ static int try_set_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl, if (master->cluster[j]) master->cluster[j]->is_new = 0; + /* For volatile autoclusters that are currently in auto mode + we need to discover if it will be set to manual mode. + If so, then we have to copy the current volatile values + first since those will become the new manual values (which + may be overwritten by explicit new values from this set + of controls). */ + if (master->is_auto && master->has_volatiles && + !is_cur_manual(master)) { + /* Pick an initial non-manual value */ + s32 new_auto_val = master->manual_mode_value + 1; + u32 tmp_idx = idx; + + do { + /* Check if the auto control is part of the + list, and remember the new value. */ + if (helpers[tmp_idx].ctrl == master) + new_auto_val = cs->controls[tmp_idx].value; + tmp_idx = helpers[tmp_idx].next; + } while (tmp_idx); + /* If the new value == the manual value, then copy + the current volatile values. */ + if (new_auto_val == master->manual_mode_value) + update_from_auto_cluster(master); + } + /* Copy the new caller-supplied control values. user_to_new() sets 'is_new' to 1. */ do { @@ -2235,6 +2288,12 @@ static int set_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, s32 *val) if (master->cluster[i]) master->cluster[i]->is_new = 0; + /* For autoclusters with volatiles that are switched from auto to + manual mode we have to update the current volatile values since + those will become the initial manual values after such a switch. */ + if (master->is_auto && master->has_volatiles && ctrl == master && + !is_cur_manual(master) && *val == master->manual_mode_value) + update_from_auto_cluster(master); ctrl->val = *val; ctrl->is_new = 1; ret = try_or_set_cluster(fh, master, true); diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c index 06f14008b34..a5c9ed128b9 100644 --- a/drivers/media/video/v4l2-dev.c +++ b/drivers/media/video/v4l2-dev.c @@ -173,6 +173,17 @@ static void v4l2_device_release(struct device *cd) media_device_unregister_entity(&vdev->entity); #endif + /* Do not call v4l2_device_put if there is no release callback set. + * Drivers that have no v4l2_device release callback might free the + * v4l2_dev instance in the video_device release callback below, so we + * must perform this check here. + * + * TODO: In the long run all drivers that use v4l2_device should use the + * v4l2_device release callback. This check will then be unnecessary. + */ + if (v4l2_dev && v4l2_dev->release == NULL) + v4l2_dev = NULL; + /* Release video_device and perform other cleanups as needed. */ vdev->release(vdev); diff --git a/drivers/media/video/v4l2-device.c b/drivers/media/video/v4l2-device.c index c72856c4143..0edd618b9dd 100644 --- a/drivers/media/video/v4l2-device.c +++ b/drivers/media/video/v4l2-device.c @@ -20,7 +20,9 @@ #include <linux/types.h> #include <linux/ioctl.h> +#include <linux/module.h> #include <linux/i2c.h> +#include <linux/slab.h> #if defined(CONFIG_SPI) #include <linux/spi/spi.h> #endif @@ -38,6 +40,7 @@ int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev) mutex_init(&v4l2_dev->ioctl_lock); v4l2_prio_init(&v4l2_dev->prio); kref_init(&v4l2_dev->ref); + get_device(dev); v4l2_dev->dev = dev; if (dev == NULL) { /* If dev == NULL, then name must be filled in by the caller */ @@ -93,6 +96,7 @@ void v4l2_device_disconnect(struct v4l2_device *v4l2_dev) if (dev_get_drvdata(v4l2_dev->dev) == v4l2_dev) dev_set_drvdata(v4l2_dev->dev, NULL); + put_device(v4l2_dev->dev); v4l2_dev->dev = NULL; } EXPORT_SYMBOL_GPL(v4l2_device_disconnect); @@ -191,6 +195,13 @@ int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev, } EXPORT_SYMBOL_GPL(v4l2_device_register_subdev); +static void v4l2_device_release_subdev_node(struct video_device *vdev) +{ + struct v4l2_subdev *sd = video_get_drvdata(vdev); + sd->devnode = NULL; + kfree(vdev); +} + int v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev) { struct video_device *vdev; @@ -204,22 +215,40 @@ int v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev) if (!(sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE)) continue; - vdev = &sd->devnode; + vdev = kzalloc(sizeof(*vdev), GFP_KERNEL); + if (!vdev) { + err = -ENOMEM; + goto clean_up; + } + + video_set_drvdata(vdev, sd); strlcpy(vdev->name, sd->name, sizeof(vdev->name)); vdev->v4l2_dev = v4l2_dev; vdev->fops = &v4l2_subdev_fops; - vdev->release = video_device_release_empty; + vdev->release = v4l2_device_release_subdev_node; vdev->ctrl_handler = sd->ctrl_handler; err = __video_register_device(vdev, VFL_TYPE_SUBDEV, -1, 1, sd->owner); - if (err < 0) - return err; + if (err < 0) { + kfree(vdev); + goto clean_up; + } #if defined(CONFIG_MEDIA_CONTROLLER) sd->entity.v4l.major = VIDEO_MAJOR; sd->entity.v4l.minor = vdev->minor; #endif + sd->devnode = vdev; } return 0; + +clean_up: + list_for_each_entry(sd, &v4l2_dev->subdevs, list) { + if (!sd->devnode) + break; + video_unregister_device(sd->devnode); + } + + return err; } EXPORT_SYMBOL_GPL(v4l2_device_register_subdev_nodes); @@ -245,7 +274,7 @@ void v4l2_device_unregister_subdev(struct v4l2_subdev *sd) if (v4l2_dev->mdev) media_device_unregister_entity(&sd->entity); #endif - video_unregister_device(&sd->devnode); + video_unregister_device(sd->devnode); module_put(sd->owner); } EXPORT_SYMBOL_GPL(v4l2_device_unregister_subdev); diff --git a/drivers/media/video/v4l2-event.c b/drivers/media/video/v4l2-event.c index 53b190cf225..c26ad963714 100644 --- a/drivers/media/video/v4l2-event.c +++ b/drivers/media/video/v4l2-event.c @@ -29,6 +29,7 @@ #include <linux/sched.h> #include <linux/slab.h> +#include <linux/export.h> static unsigned sev_pos(const struct v4l2_subscribed_event *sev, unsigned idx) { @@ -215,6 +216,9 @@ int v4l2_event_subscribe(struct v4l2_fh *fh, unsigned long flags; unsigned i; + if (sub->type == V4L2_EVENT_ALL) + return -EINVAL; + if (elems < 1) elems = 1; if (sub->type == V4L2_EVENT_CTRL) { @@ -282,6 +286,7 @@ int v4l2_event_unsubscribe(struct v4l2_fh *fh, { struct v4l2_subscribed_event *sev; unsigned long flags; + int i; if (sub->type == V4L2_EVENT_ALL) { v4l2_event_unsubscribe_all(fh); @@ -292,8 +297,12 @@ int v4l2_event_unsubscribe(struct v4l2_fh *fh, sev = v4l2_event_subscribed(fh, sub->type, sub->id); if (sev != NULL) { + /* Remove any pending events for this subscription */ + for (i = 0; i < sev->in_use; i++) { + list_del(&sev->events[sev_pos(sev, i)].list); + fh->navailable--; + } list_del(&sev->list); - sev->fh = NULL; } spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); diff --git a/drivers/media/video/v4l2-fh.c b/drivers/media/video/v4l2-fh.c index 122822d2b8b..9e3fc040ea2 100644 --- a/drivers/media/video/v4l2-fh.c +++ b/drivers/media/video/v4l2-fh.c @@ -24,6 +24,7 @@ #include <linux/bitops.h> #include <linux/slab.h> +#include <linux/export.h> #include <media/v4l2-dev.h> #include <media/v4l2-fh.h> #include <media/v4l2-event.h> diff --git a/drivers/media/video/v4l2-int-device.c b/drivers/media/video/v4l2-int-device.c index a935bae538e..f4473494af7 100644 --- a/drivers/media/video/v4l2-int-device.c +++ b/drivers/media/video/v4l2-int-device.c @@ -26,6 +26,7 @@ #include <linux/list.h> #include <linux/sort.h> #include <linux/string.h> +#include <linux/module.h> #include <media/v4l2-int-device.h> diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c index 002ce136344..e1da8fc9dd2 100644 --- a/drivers/media/video/v4l2-ioctl.c +++ b/drivers/media/video/v4l2-ioctl.c @@ -55,6 +55,19 @@ memset((u8 *)(p) + offsetof(typeof(*(p)), field) + sizeof((p)->field), \ 0, sizeof(*(p)) - offsetof(typeof(*(p)), field) - sizeof((p)->field)) +#define have_fmt_ops(foo) ( \ + ops->vidioc_##foo##_fmt_vid_cap || \ + ops->vidioc_##foo##_fmt_vid_out || \ + ops->vidioc_##foo##_fmt_vid_cap_mplane || \ + ops->vidioc_##foo##_fmt_vid_out_mplane || \ + ops->vidioc_##foo##_fmt_vid_overlay || \ + ops->vidioc_##foo##_fmt_vbi_cap || \ + ops->vidioc_##foo##_fmt_vid_out_overlay || \ + ops->vidioc_##foo##_fmt_vbi_out || \ + ops->vidioc_##foo##_fmt_sliced_vbi_cap || \ + ops->vidioc_##foo##_fmt_sliced_vbi_out || \ + ops->vidioc_##foo##_fmt_type_private) + struct std_descr { v4l2_std_id std; const char *descr; @@ -260,6 +273,8 @@ static const char *v4l2_ioctls[] = { [_IOC_NR(VIDIOC_DQEVENT)] = "VIDIOC_DQEVENT", [_IOC_NR(VIDIOC_SUBSCRIBE_EVENT)] = "VIDIOC_SUBSCRIBE_EVENT", [_IOC_NR(VIDIOC_UNSUBSCRIBE_EVENT)] = "VIDIOC_UNSUBSCRIBE_EVENT", + [_IOC_NR(VIDIOC_CREATE_BUFS)] = "VIDIOC_CREATE_BUFS", + [_IOC_NR(VIDIOC_PREPARE_BUF)] = "VIDIOC_PREPARE_BUF", }; #define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls) @@ -477,63 +492,6 @@ static int check_fmt(const struct v4l2_ioctl_ops *ops, enum v4l2_buf_type type) return -EINVAL; } -/** - * fmt_sp_to_mp() - Convert a single-plane format to its multi-planar 1-plane - * equivalent - */ -static int fmt_sp_to_mp(const struct v4l2_format *f_sp, - struct v4l2_format *f_mp) -{ - struct v4l2_pix_format_mplane *pix_mp = &f_mp->fmt.pix_mp; - const struct v4l2_pix_format *pix = &f_sp->fmt.pix; - - if (f_sp->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - f_mp->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; - else if (f_sp->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) - f_mp->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; - else - return -EINVAL; - - pix_mp->width = pix->width; - pix_mp->height = pix->height; - pix_mp->pixelformat = pix->pixelformat; - pix_mp->field = pix->field; - pix_mp->colorspace = pix->colorspace; - pix_mp->num_planes = 1; - pix_mp->plane_fmt[0].sizeimage = pix->sizeimage; - pix_mp->plane_fmt[0].bytesperline = pix->bytesperline; - - return 0; -} - -/** - * fmt_mp_to_sp() - Convert a multi-planar 1-plane format to its single-planar - * equivalent - */ -static int fmt_mp_to_sp(const struct v4l2_format *f_mp, - struct v4l2_format *f_sp) -{ - const struct v4l2_pix_format_mplane *pix_mp = &f_mp->fmt.pix_mp; - struct v4l2_pix_format *pix = &f_sp->fmt.pix; - - if (f_mp->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) - f_sp->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - else if (f_mp->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) - f_sp->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; - else - return -EINVAL; - - pix->width = pix_mp->width; - pix->height = pix_mp->height; - pix->pixelformat = pix_mp->pixelformat; - pix->field = pix_mp->field; - pix->colorspace = pix_mp->colorspace; - pix->sizeimage = pix_mp->plane_fmt[0].sizeimage; - pix->bytesperline = pix_mp->plane_fmt[0].bytesperline; - - return 0; -} - static long __video_do_ioctl(struct file *file, unsigned int cmd, void *arg) { @@ -541,8 +499,8 @@ static long __video_do_ioctl(struct file *file, const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops; void *fh = file->private_data; struct v4l2_fh *vfh = NULL; - struct v4l2_format f_copy; int use_fh_prio = 0; + long ret_prio = 0; long ret = -ENOTTY; if (ops == NULL) { @@ -562,39 +520,8 @@ static long __video_do_ioctl(struct file *file, use_fh_prio = test_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags); } - if (use_fh_prio) { - switch (cmd) { - case VIDIOC_S_CTRL: - case VIDIOC_S_STD: - case VIDIOC_S_INPUT: - case VIDIOC_S_OUTPUT: - case VIDIOC_S_TUNER: - case VIDIOC_S_FREQUENCY: - case VIDIOC_S_FMT: - case VIDIOC_S_CROP: - case VIDIOC_S_AUDIO: - case VIDIOC_S_AUDOUT: - case VIDIOC_S_EXT_CTRLS: - case VIDIOC_S_FBUF: - case VIDIOC_S_PRIORITY: - case VIDIOC_S_DV_PRESET: - case VIDIOC_S_DV_TIMINGS: - case VIDIOC_S_JPEGCOMP: - case VIDIOC_S_MODULATOR: - case VIDIOC_S_PARM: - case VIDIOC_S_HW_FREQ_SEEK: - case VIDIOC_ENCODER_CMD: - case VIDIOC_OVERLAY: - case VIDIOC_REQBUFS: - case VIDIOC_STREAMON: - case VIDIOC_STREAMOFF: - ret = v4l2_prio_check(vfd->prio, vfh->prio); - if (ret) - goto exit_prio; - ret = -EINVAL; - break; - } - } + if (use_fh_prio) + ret_prio = v4l2_prio_check(vfd->prio, vfh->prio); switch (cmd) { @@ -638,12 +565,14 @@ static long __video_do_ioctl(struct file *file, enum v4l2_priority *p = arg; if (!ops->vidioc_s_priority && !use_fh_prio) - break; + break; dbgarg(cmd, "setting priority to %d\n", *p); if (ops->vidioc_s_priority) ret = ops->vidioc_s_priority(file, fh, *p); else - ret = v4l2_prio_change(&vfd->v4l2_dev->prio, &vfh->prio, *p); + ret = ret_prio ? ret_prio : + v4l2_prio_change(&vfd->v4l2_dev->prio, + &vfh->prio, *p); break; } @@ -654,37 +583,37 @@ static long __video_do_ioctl(struct file *file, switch (f->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (ops->vidioc_enum_fmt_vid_cap) + if (likely(ops->vidioc_enum_fmt_vid_cap)) ret = ops->vidioc_enum_fmt_vid_cap(file, fh, f); break; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - if (ops->vidioc_enum_fmt_vid_cap_mplane) + if (likely(ops->vidioc_enum_fmt_vid_cap_mplane)) ret = ops->vidioc_enum_fmt_vid_cap_mplane(file, fh, f); break; case V4L2_BUF_TYPE_VIDEO_OVERLAY: - if (ops->vidioc_enum_fmt_vid_overlay) + if (likely(ops->vidioc_enum_fmt_vid_overlay)) ret = ops->vidioc_enum_fmt_vid_overlay(file, fh, f); break; case V4L2_BUF_TYPE_VIDEO_OUTPUT: - if (ops->vidioc_enum_fmt_vid_out) + if (likely(ops->vidioc_enum_fmt_vid_out)) ret = ops->vidioc_enum_fmt_vid_out(file, fh, f); break; case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - if (ops->vidioc_enum_fmt_vid_out_mplane) + if (likely(ops->vidioc_enum_fmt_vid_out_mplane)) ret = ops->vidioc_enum_fmt_vid_out_mplane(file, fh, f); break; case V4L2_BUF_TYPE_PRIVATE: - if (ops->vidioc_enum_fmt_type_private) + if (likely(ops->vidioc_enum_fmt_type_private)) ret = ops->vidioc_enum_fmt_type_private(file, fh, f); break; default: break; } - if (!ret) + if (likely (!ret)) dbgarg(cmd, "index=%d, type=%d, flags=%d, " "pixelformat=%c%c%c%c, description='%s'\n", f->index, f->type, f->flags, @@ -693,6 +622,14 @@ static long __video_do_ioctl(struct file *file, (f->pixelformat >> 16) & 0xff, (f->pixelformat >> 24) & 0xff, f->description); + else if (ret == -ENOTTY && + (ops->vidioc_enum_fmt_vid_cap || + ops->vidioc_enum_fmt_vid_out || + ops->vidioc_enum_fmt_vid_cap_mplane || + ops->vidioc_enum_fmt_vid_out_mplane || + ops->vidioc_enum_fmt_vid_overlay || + ops->vidioc_enum_fmt_type_private)) + ret = -EINVAL; break; } case VIDIOC_G_FMT: @@ -704,119 +641,67 @@ static long __video_do_ioctl(struct file *file, switch (f->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (ops->vidioc_g_fmt_vid_cap) { + if (ops->vidioc_g_fmt_vid_cap) ret = ops->vidioc_g_fmt_vid_cap(file, fh, f); - } else if (ops->vidioc_g_fmt_vid_cap_mplane) { - if (fmt_sp_to_mp(f, &f_copy)) - break; - ret = ops->vidioc_g_fmt_vid_cap_mplane(file, fh, - &f_copy); - if (ret) - break; - - /* Driver is currently in multi-planar format, - * we can't return it in single-planar API*/ - if (f_copy.fmt.pix_mp.num_planes > 1) { - ret = -EBUSY; - break; - } - - ret = fmt_mp_to_sp(&f_copy, f); - } if (!ret) v4l_print_pix_fmt(vfd, &f->fmt.pix); break; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - if (ops->vidioc_g_fmt_vid_cap_mplane) { + if (ops->vidioc_g_fmt_vid_cap_mplane) ret = ops->vidioc_g_fmt_vid_cap_mplane(file, fh, f); - } else if (ops->vidioc_g_fmt_vid_cap) { - if (fmt_mp_to_sp(f, &f_copy)) - break; - ret = ops->vidioc_g_fmt_vid_cap(file, - fh, &f_copy); - if (ret) - break; - - ret = fmt_sp_to_mp(&f_copy, f); - } if (!ret) v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp); break; case V4L2_BUF_TYPE_VIDEO_OVERLAY: - if (ops->vidioc_g_fmt_vid_overlay) + if (likely(ops->vidioc_g_fmt_vid_overlay)) ret = ops->vidioc_g_fmt_vid_overlay(file, fh, f); break; case V4L2_BUF_TYPE_VIDEO_OUTPUT: - if (ops->vidioc_g_fmt_vid_out) { + if (ops->vidioc_g_fmt_vid_out) ret = ops->vidioc_g_fmt_vid_out(file, fh, f); - } else if (ops->vidioc_g_fmt_vid_out_mplane) { - if (fmt_sp_to_mp(f, &f_copy)) - break; - ret = ops->vidioc_g_fmt_vid_out_mplane(file, fh, - &f_copy); - if (ret) - break; - - /* Driver is currently in multi-planar format, - * we can't return it in single-planar API*/ - if (f_copy.fmt.pix_mp.num_planes > 1) { - ret = -EBUSY; - break; - } - - ret = fmt_mp_to_sp(&f_copy, f); - } if (!ret) v4l_print_pix_fmt(vfd, &f->fmt.pix); break; case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - if (ops->vidioc_g_fmt_vid_out_mplane) { + if (ops->vidioc_g_fmt_vid_out_mplane) ret = ops->vidioc_g_fmt_vid_out_mplane(file, fh, f); - } else if (ops->vidioc_g_fmt_vid_out) { - if (fmt_mp_to_sp(f, &f_copy)) - break; - ret = ops->vidioc_g_fmt_vid_out(file, - fh, &f_copy); - if (ret) - break; - - ret = fmt_sp_to_mp(&f_copy, f); - } if (!ret) v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp); break; case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: - if (ops->vidioc_g_fmt_vid_out_overlay) + if (likely(ops->vidioc_g_fmt_vid_out_overlay)) ret = ops->vidioc_g_fmt_vid_out_overlay(file, fh, f); break; case V4L2_BUF_TYPE_VBI_CAPTURE: - if (ops->vidioc_g_fmt_vbi_cap) + if (likely(ops->vidioc_g_fmt_vbi_cap)) ret = ops->vidioc_g_fmt_vbi_cap(file, fh, f); break; case V4L2_BUF_TYPE_VBI_OUTPUT: - if (ops->vidioc_g_fmt_vbi_out) + if (likely(ops->vidioc_g_fmt_vbi_out)) ret = ops->vidioc_g_fmt_vbi_out(file, fh, f); break; case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: - if (ops->vidioc_g_fmt_sliced_vbi_cap) + if (likely(ops->vidioc_g_fmt_sliced_vbi_cap)) ret = ops->vidioc_g_fmt_sliced_vbi_cap(file, fh, f); break; case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: - if (ops->vidioc_g_fmt_sliced_vbi_out) + if (likely(ops->vidioc_g_fmt_sliced_vbi_out)) ret = ops->vidioc_g_fmt_sliced_vbi_out(file, fh, f); break; case V4L2_BUF_TYPE_PRIVATE: - if (ops->vidioc_g_fmt_type_private) + if (likely(ops->vidioc_g_fmt_type_private)) ret = ops->vidioc_g_fmt_type_private(file, fh, f); break; } + if (unlikely(ret == -ENOTTY && have_fmt_ops(g))) + ret = -EINVAL; break; } @@ -824,6 +709,14 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_format *f = (struct v4l2_format *)arg; + if (!have_fmt_ops(s)) + break; + if (ret_prio) { + ret = ret_prio; + break; + } + ret = -EINVAL; + /* FIXME: Should be one dump per type */ dbgarg(cmd, "type=%s\n", prt_names(f->type, v4l2_type_names)); @@ -831,44 +724,15 @@ static long __video_do_ioctl(struct file *file, case V4L2_BUF_TYPE_VIDEO_CAPTURE: CLEAR_AFTER_FIELD(f, fmt.pix); v4l_print_pix_fmt(vfd, &f->fmt.pix); - if (ops->vidioc_s_fmt_vid_cap) { + if (ops->vidioc_s_fmt_vid_cap) ret = ops->vidioc_s_fmt_vid_cap(file, fh, f); - } else if (ops->vidioc_s_fmt_vid_cap_mplane) { - if (fmt_sp_to_mp(f, &f_copy)) - break; - ret = ops->vidioc_s_fmt_vid_cap_mplane(file, fh, - &f_copy); - if (ret) - break; - - if (f_copy.fmt.pix_mp.num_planes > 1) { - /* Drivers shouldn't adjust from 1-plane - * to more than 1-plane formats */ - ret = -EBUSY; - WARN_ON(1); - break; - } - - ret = fmt_mp_to_sp(&f_copy, f); - } break; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: CLEAR_AFTER_FIELD(f, fmt.pix_mp); v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp); - if (ops->vidioc_s_fmt_vid_cap_mplane) { + if (ops->vidioc_s_fmt_vid_cap_mplane) ret = ops->vidioc_s_fmt_vid_cap_mplane(file, fh, f); - } else if (ops->vidioc_s_fmt_vid_cap && - f->fmt.pix_mp.num_planes == 1) { - if (fmt_mp_to_sp(f, &f_copy)) - break; - ret = ops->vidioc_s_fmt_vid_cap(file, - fh, &f_copy); - if (ret) - break; - - ret = fmt_sp_to_mp(&f_copy, f); - } break; case V4L2_BUF_TYPE_VIDEO_OVERLAY: CLEAR_AFTER_FIELD(f, fmt.win); @@ -879,44 +743,15 @@ static long __video_do_ioctl(struct file *file, case V4L2_BUF_TYPE_VIDEO_OUTPUT: CLEAR_AFTER_FIELD(f, fmt.pix); v4l_print_pix_fmt(vfd, &f->fmt.pix); - if (ops->vidioc_s_fmt_vid_out) { + if (ops->vidioc_s_fmt_vid_out) ret = ops->vidioc_s_fmt_vid_out(file, fh, f); - } else if (ops->vidioc_s_fmt_vid_out_mplane) { - if (fmt_sp_to_mp(f, &f_copy)) - break; - ret = ops->vidioc_s_fmt_vid_out_mplane(file, fh, - &f_copy); - if (ret) - break; - - if (f_copy.fmt.pix_mp.num_planes > 1) { - /* Drivers shouldn't adjust from 1-plane - * to more than 1-plane formats */ - ret = -EBUSY; - WARN_ON(1); - break; - } - - ret = fmt_mp_to_sp(&f_copy, f); - } break; case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: CLEAR_AFTER_FIELD(f, fmt.pix_mp); v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp); - if (ops->vidioc_s_fmt_vid_out_mplane) { + if (ops->vidioc_s_fmt_vid_out_mplane) ret = ops->vidioc_s_fmt_vid_out_mplane(file, fh, f); - } else if (ops->vidioc_s_fmt_vid_out && - f->fmt.pix_mp.num_planes == 1) { - if (fmt_mp_to_sp(f, &f_copy)) - break; - ret = ops->vidioc_s_fmt_vid_out(file, - fh, &f_copy); - if (ret) - break; - - ret = fmt_mp_to_sp(&f_copy, f); - } break; case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: CLEAR_AFTER_FIELD(f, fmt.win); @@ -926,29 +761,30 @@ static long __video_do_ioctl(struct file *file, break; case V4L2_BUF_TYPE_VBI_CAPTURE: CLEAR_AFTER_FIELD(f, fmt.vbi); - if (ops->vidioc_s_fmt_vbi_cap) + if (likely(ops->vidioc_s_fmt_vbi_cap)) ret = ops->vidioc_s_fmt_vbi_cap(file, fh, f); break; case V4L2_BUF_TYPE_VBI_OUTPUT: CLEAR_AFTER_FIELD(f, fmt.vbi); - if (ops->vidioc_s_fmt_vbi_out) + if (likely(ops->vidioc_s_fmt_vbi_out)) ret = ops->vidioc_s_fmt_vbi_out(file, fh, f); break; case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: CLEAR_AFTER_FIELD(f, fmt.sliced); - if (ops->vidioc_s_fmt_sliced_vbi_cap) + if (likely(ops->vidioc_s_fmt_sliced_vbi_cap)) ret = ops->vidioc_s_fmt_sliced_vbi_cap(file, fh, f); break; case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: CLEAR_AFTER_FIELD(f, fmt.sliced); - if (ops->vidioc_s_fmt_sliced_vbi_out) + if (likely(ops->vidioc_s_fmt_sliced_vbi_out)) ret = ops->vidioc_s_fmt_sliced_vbi_out(file, fh, f); + break; case V4L2_BUF_TYPE_PRIVATE: /* CLEAR_AFTER_FIELD(f, fmt.raw_data); <- does nothing */ - if (ops->vidioc_s_fmt_type_private) + if (likely(ops->vidioc_s_fmt_type_private)) ret = ops->vidioc_s_fmt_type_private(file, fh, f); break; @@ -965,132 +801,77 @@ static long __video_do_ioctl(struct file *file, switch (f->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: CLEAR_AFTER_FIELD(f, fmt.pix); - if (ops->vidioc_try_fmt_vid_cap) { + if (ops->vidioc_try_fmt_vid_cap) ret = ops->vidioc_try_fmt_vid_cap(file, fh, f); - } else if (ops->vidioc_try_fmt_vid_cap_mplane) { - if (fmt_sp_to_mp(f, &f_copy)) - break; - ret = ops->vidioc_try_fmt_vid_cap_mplane(file, - fh, &f_copy); - if (ret) - break; - - if (f_copy.fmt.pix_mp.num_planes > 1) { - /* Drivers shouldn't adjust from 1-plane - * to more than 1-plane formats */ - ret = -EBUSY; - WARN_ON(1); - break; - } - ret = fmt_mp_to_sp(&f_copy, f); - } if (!ret) v4l_print_pix_fmt(vfd, &f->fmt.pix); break; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: CLEAR_AFTER_FIELD(f, fmt.pix_mp); - if (ops->vidioc_try_fmt_vid_cap_mplane) { + if (ops->vidioc_try_fmt_vid_cap_mplane) ret = ops->vidioc_try_fmt_vid_cap_mplane(file, fh, f); - } else if (ops->vidioc_try_fmt_vid_cap && - f->fmt.pix_mp.num_planes == 1) { - if (fmt_mp_to_sp(f, &f_copy)) - break; - ret = ops->vidioc_try_fmt_vid_cap(file, - fh, &f_copy); - if (ret) - break; - - ret = fmt_sp_to_mp(&f_copy, f); - } if (!ret) v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp); break; case V4L2_BUF_TYPE_VIDEO_OVERLAY: CLEAR_AFTER_FIELD(f, fmt.win); - if (ops->vidioc_try_fmt_vid_overlay) + if (likely(ops->vidioc_try_fmt_vid_overlay)) ret = ops->vidioc_try_fmt_vid_overlay(file, fh, f); break; case V4L2_BUF_TYPE_VIDEO_OUTPUT: CLEAR_AFTER_FIELD(f, fmt.pix); - if (ops->vidioc_try_fmt_vid_out) { + if (ops->vidioc_try_fmt_vid_out) ret = ops->vidioc_try_fmt_vid_out(file, fh, f); - } else if (ops->vidioc_try_fmt_vid_out_mplane) { - if (fmt_sp_to_mp(f, &f_copy)) - break; - ret = ops->vidioc_try_fmt_vid_out_mplane(file, - fh, &f_copy); - if (ret) - break; - - if (f_copy.fmt.pix_mp.num_planes > 1) { - /* Drivers shouldn't adjust from 1-plane - * to more than 1-plane formats */ - ret = -EBUSY; - WARN_ON(1); - break; - } - ret = fmt_mp_to_sp(&f_copy, f); - } if (!ret) v4l_print_pix_fmt(vfd, &f->fmt.pix); break; case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: CLEAR_AFTER_FIELD(f, fmt.pix_mp); - if (ops->vidioc_try_fmt_vid_out_mplane) { + if (ops->vidioc_try_fmt_vid_out_mplane) ret = ops->vidioc_try_fmt_vid_out_mplane(file, fh, f); - } else if (ops->vidioc_try_fmt_vid_out && - f->fmt.pix_mp.num_planes == 1) { - if (fmt_mp_to_sp(f, &f_copy)) - break; - ret = ops->vidioc_try_fmt_vid_out(file, - fh, &f_copy); - if (ret) - break; - - ret = fmt_sp_to_mp(&f_copy, f); - } if (!ret) v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp); break; case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: CLEAR_AFTER_FIELD(f, fmt.win); - if (ops->vidioc_try_fmt_vid_out_overlay) + if (likely(ops->vidioc_try_fmt_vid_out_overlay)) ret = ops->vidioc_try_fmt_vid_out_overlay(file, fh, f); break; case V4L2_BUF_TYPE_VBI_CAPTURE: CLEAR_AFTER_FIELD(f, fmt.vbi); - if (ops->vidioc_try_fmt_vbi_cap) + if (likely(ops->vidioc_try_fmt_vbi_cap)) ret = ops->vidioc_try_fmt_vbi_cap(file, fh, f); break; case V4L2_BUF_TYPE_VBI_OUTPUT: CLEAR_AFTER_FIELD(f, fmt.vbi); - if (ops->vidioc_try_fmt_vbi_out) + if (likely(ops->vidioc_try_fmt_vbi_out)) ret = ops->vidioc_try_fmt_vbi_out(file, fh, f); break; case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: CLEAR_AFTER_FIELD(f, fmt.sliced); - if (ops->vidioc_try_fmt_sliced_vbi_cap) + if (likely(ops->vidioc_try_fmt_sliced_vbi_cap)) ret = ops->vidioc_try_fmt_sliced_vbi_cap(file, fh, f); break; case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: CLEAR_AFTER_FIELD(f, fmt.sliced); - if (ops->vidioc_try_fmt_sliced_vbi_out) + if (likely(ops->vidioc_try_fmt_sliced_vbi_out)) ret = ops->vidioc_try_fmt_sliced_vbi_out(file, fh, f); break; case V4L2_BUF_TYPE_PRIVATE: /* CLEAR_AFTER_FIELD(f, fmt.raw_data); <- does nothing */ - if (ops->vidioc_try_fmt_type_private) + if (likely(ops->vidioc_try_fmt_type_private)) ret = ops->vidioc_try_fmt_type_private(file, fh, f); break; } - + if (unlikely(ret == -ENOTTY && have_fmt_ops(try))) + ret = -EINVAL; break; } /* FIXME: Those buf reqs could be handled here, @@ -1103,6 +884,10 @@ static long __video_do_ioctl(struct file *file, if (!ops->vidioc_reqbufs) break; + if (ret_prio) { + ret = ret_prio; + break; + } ret = check_fmt(ops, p->type); if (ret) break; @@ -1168,6 +953,10 @@ static long __video_do_ioctl(struct file *file, if (!ops->vidioc_overlay) break; + if (ret_prio) { + ret = ret_prio; + break; + } dbgarg(cmd, "value=%d\n", *i); ret = ops->vidioc_overlay(file, fh, *i); break; @@ -1193,6 +982,10 @@ static long __video_do_ioctl(struct file *file, if (!ops->vidioc_s_fbuf) break; + if (ret_prio) { + ret = ret_prio; + break; + } dbgarg(cmd, "capability=0x%x, flags=%d, base=0x%08lx\n", p->capability, p->flags, (unsigned long)p->base); v4l_print_pix_fmt(vfd, &p->fmt); @@ -1205,6 +998,10 @@ static long __video_do_ioctl(struct file *file, if (!ops->vidioc_streamon) break; + if (ret_prio) { + ret = ret_prio; + break; + } dbgarg(cmd, "type=%s\n", prt_names(i, v4l2_type_names)); ret = ops->vidioc_streamon(file, fh, i); break; @@ -1215,6 +1012,10 @@ static long __video_do_ioctl(struct file *file, if (!ops->vidioc_streamoff) break; + if (ret_prio) { + ret = ret_prio; + break; + } dbgarg(cmd, "type=%s\n", prt_names(i, v4l2_type_names)); ret = ops->vidioc_streamoff(file, fh, i); break; @@ -1227,6 +1028,10 @@ static long __video_do_ioctl(struct file *file, unsigned int index = p->index, i, j = 0; const char *descr = ""; + if (id == 0) + break; + ret = -EINVAL; + /* Return norm array in a canonical way */ for (i = 0; i <= index && id; i++) { /* last std value in the standards array is 0, so this @@ -1262,16 +1067,15 @@ static long __video_do_ioctl(struct file *file, { v4l2_std_id *id = arg; - ret = 0; /* Calls the specific handler */ if (ops->vidioc_g_std) ret = ops->vidioc_g_std(file, fh, id); - else if (vfd->current_norm) + else if (vfd->current_norm) { + ret = 0; *id = vfd->current_norm; - else - ret = -EINVAL; + } - if (!ret) + if (likely(!ret)) dbgarg(cmd, "std=0x%08Lx\n", (long long unsigned)*id); break; } @@ -1281,15 +1085,20 @@ static long __video_do_ioctl(struct file *file, dbgarg(cmd, "std=%08Lx\n", (long long unsigned)*id); + if (!ops->vidioc_s_std) + break; + + if (ret_prio) { + ret = ret_prio; + break; + } + ret = -EINVAL; norm = (*id) & vfd->tvnorms; if (vfd->tvnorms && !norm) /* Check if std is supported */ break; /* Calls the specific handler */ - if (ops->vidioc_s_std) - ret = ops->vidioc_s_std(file, fh, &norm); - else - ret = -EINVAL; + ret = ops->vidioc_s_std(file, fh, &norm); /* Updates standard information */ if (ret >= 0) @@ -1302,6 +1111,14 @@ static long __video_do_ioctl(struct file *file, if (!ops->vidioc_querystd) break; + /* + * If nothing detected, it should return all supported + * Drivers just need to mask the std argument, in order + * to remove the standards that don't apply from the mask. + * This means that tuners, audio and video decoders can join + * their efforts to improve the standards detection + */ + *p = vfd->tvnorms; ret = ops->vidioc_querystd(file, fh, arg); if (!ret) dbgarg(cmd, "detected std=%08Lx\n", @@ -1358,6 +1175,10 @@ static long __video_do_ioctl(struct file *file, if (!ops->vidioc_s_input) break; + if (ret_prio) { + ret = ret_prio; + break; + } dbgarg(cmd, "value=%d\n", *i); ret = ops->vidioc_s_input(file, fh, *i); break; @@ -1410,6 +1231,10 @@ static long __video_do_ioctl(struct file *file, if (!ops->vidioc_s_output) break; + if (ret_prio) { + ret = ret_prio; + break; + } dbgarg(cmd, "value=%d\n", *i); ret = ops->vidioc_s_output(file, fh, *i); break; @@ -1479,6 +1304,10 @@ static long __video_do_ioctl(struct file *file, if (!(vfh && vfh->ctrl_handler) && !vfd->ctrl_handler && !ops->vidioc_s_ctrl && !ops->vidioc_s_ext_ctrls) break; + if (ret_prio) { + ret = ret_prio; + break; + } dbgarg(cmd, "id=0x%x, value=%d\n", p->id, p->value); @@ -1504,6 +1333,8 @@ static long __video_do_ioctl(struct file *file, ctrl.value = p->value; if (check_ext_ctrls(&ctrls, 1)) ret = ops->vidioc_s_ext_ctrls(file, fh, &ctrls); + else + ret = -EINVAL; break; } case VIDIOC_G_EXT_CTRLS: @@ -1515,8 +1346,10 @@ static long __video_do_ioctl(struct file *file, ret = v4l2_g_ext_ctrls(vfh->ctrl_handler, p); else if (vfd->ctrl_handler) ret = v4l2_g_ext_ctrls(vfd->ctrl_handler, p); - else if (ops->vidioc_g_ext_ctrls && check_ext_ctrls(p, 0)) - ret = ops->vidioc_g_ext_ctrls(file, fh, p); + else if (ops->vidioc_g_ext_ctrls) + ret = check_ext_ctrls(p, 0) ? + ops->vidioc_g_ext_ctrls(file, fh, p) : + -EINVAL; else break; v4l_print_ext_ctrls(cmd, vfd, p, !ret); @@ -1530,6 +1363,10 @@ static long __video_do_ioctl(struct file *file, if (!(vfh && vfh->ctrl_handler) && !vfd->ctrl_handler && !ops->vidioc_s_ext_ctrls) break; + if (ret_prio) { + ret = ret_prio; + break; + } v4l_print_ext_ctrls(cmd, vfd, p, 1); if (vfh && vfh->ctrl_handler) ret = v4l2_s_ext_ctrls(vfh, vfh->ctrl_handler, p); @@ -1537,6 +1374,8 @@ static long __video_do_ioctl(struct file *file, ret = v4l2_s_ext_ctrls(NULL, vfd->ctrl_handler, p); else if (check_ext_ctrls(p, 0)) ret = ops->vidioc_s_ext_ctrls(file, fh, p); + else + ret = -EINVAL; break; } case VIDIOC_TRY_EXT_CTRLS: @@ -1554,6 +1393,8 @@ static long __video_do_ioctl(struct file *file, ret = v4l2_try_ext_ctrls(vfd->ctrl_handler, p); else if (check_ext_ctrls(p, 0)) ret = ops->vidioc_try_ext_ctrls(file, fh, p); + else + ret = -EINVAL; break; } case VIDIOC_QUERYMENU: @@ -1614,6 +1455,10 @@ static long __video_do_ioctl(struct file *file, if (!ops->vidioc_s_audio) break; + if (ret_prio) { + ret = ret_prio; + break; + } dbgarg(cmd, "index=%d, name=%s, capability=0x%x, " "mode=0x%x\n", p->index, p->name, p->capability, p->mode); @@ -1654,6 +1499,10 @@ static long __video_do_ioctl(struct file *file, if (!ops->vidioc_s_audout) break; + if (ret_prio) { + ret = ret_prio; + break; + } dbgarg(cmd, "index=%d, name=%s, capability=%d, " "mode=%d\n", p->index, p->name, p->capability, p->mode); @@ -1683,6 +1532,10 @@ static long __video_do_ioctl(struct file *file, if (!ops->vidioc_s_modulator) break; + if (ret_prio) { + ret = ret_prio; + break; + } dbgarg(cmd, "index=%d, name=%s, capability=%d, " "rangelow=%d, rangehigh=%d, txsubchans=%d\n", p->index, p->name, p->capability, p->rangelow, @@ -1709,6 +1562,10 @@ static long __video_do_ioctl(struct file *file, if (!ops->vidioc_s_crop) break; + if (ret_prio) { + ret = ret_prio; + break; + } dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names)); dbgrect(vfd, "", &p->c); ret = ops->vidioc_s_crop(file, fh, p); @@ -1752,11 +1609,15 @@ static long __video_do_ioctl(struct file *file, if (!ops->vidioc_g_jpegcomp) break; + if (ret_prio) { + ret = ret_prio; + break; + } dbgarg(cmd, "quality=%d, APPn=%d, APP_len=%d, " "COM_len=%d, jpeg_markers=%d\n", p->quality, p->APPn, p->APP_len, p->COM_len, p->jpeg_markers); - ret = ops->vidioc_s_jpegcomp(file, fh, p); + ret = ops->vidioc_s_jpegcomp(file, fh, p); break; } case VIDIOC_G_ENC_INDEX: @@ -1777,6 +1638,10 @@ static long __video_do_ioctl(struct file *file, if (!ops->vidioc_encoder_cmd) break; + if (ret_prio) { + ret = ret_prio; + break; + } ret = ops->vidioc_encoder_cmd(file, fh, p); if (!ret) dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags); @@ -1797,6 +1662,8 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_streamparm *p = arg; + if (!ops->vidioc_g_parm && !vfd->current_norm) + break; if (ops->vidioc_g_parm) { ret = check_fmt(ops, p->type); if (ret) @@ -1805,14 +1672,13 @@ static long __video_do_ioctl(struct file *file, } else { v4l2_std_id std = vfd->current_norm; + ret = -EINVAL; if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) break; ret = 0; if (ops->vidioc_g_std) ret = ops->vidioc_g_std(file, fh, &std); - else if (std == 0) - ret = -EINVAL; if (ret == 0) v4l2_video_std_frame_period(std, &p->parm.capture.timeperframe); @@ -1827,6 +1693,10 @@ static long __video_do_ioctl(struct file *file, if (!ops->vidioc_s_parm) break; + if (ret_prio) { + ret = ret_prio; + break; + } ret = check_fmt(ops, p->type); if (ret) break; @@ -1862,6 +1732,10 @@ static long __video_do_ioctl(struct file *file, if (!ops->vidioc_s_tuner) break; + if (ret_prio) { + ret = ret_prio; + break; + } p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; dbgarg(cmd, "index=%d, name=%s, type=%d, " @@ -1896,6 +1770,10 @@ static long __video_do_ioctl(struct file *file, if (!ops->vidioc_s_frequency) break; + if (ret_prio) { + ret = ret_prio; + break; + } dbgarg(cmd, "tuner=%d, type=%d, frequency=%d\n", p->tuner, p->type, p->frequency); ret = ops->vidioc_s_frequency(file, fh, p); @@ -1970,6 +1848,10 @@ static long __video_do_ioctl(struct file *file, if (!ops->vidioc_s_hw_freq_seek) break; + if (ret_prio) { + ret = ret_prio; + break; + } type = (vfd->vfl_type == VFL_TYPE_RADIO) ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; dbgarg(cmd, @@ -2074,6 +1956,10 @@ static long __video_do_ioctl(struct file *file, if (!ops->vidioc_s_dv_preset) break; + if (ret_prio) { + ret = ret_prio; + break; + } dbgarg(cmd, "preset=%d\n", p->preset); ret = ops->vidioc_s_dv_preset(file, fh, p); @@ -2109,6 +1995,10 @@ static long __video_do_ioctl(struct file *file, if (!ops->vidioc_s_dv_timings) break; + if (ret_prio) { + ret = ret_prio; + break; + } switch (p->type) { case V4L2_DV_BT_656_1120: @@ -2216,20 +2106,47 @@ static long __video_do_ioctl(struct file *file, dbgarg(cmd, "type=0x%8.8x", sub->type); break; } - default: + case VIDIOC_CREATE_BUFS: { - bool valid_prio = true; + struct v4l2_create_buffers *create = arg; - if (!ops->vidioc_default) + if (!ops->vidioc_create_bufs) + break; + if (ret_prio) { + ret = ret_prio; + break; + } + ret = check_fmt(ops, create->format.type); + if (ret) break; - if (use_fh_prio) - valid_prio = v4l2_prio_check(vfd->prio, vfh->prio) >= 0; - ret = ops->vidioc_default(file, fh, valid_prio, cmd, arg); + + ret = ops->vidioc_create_bufs(file, fh, create); + + dbgarg(cmd, "count=%d @ %d\n", create->count, create->index); break; } + case VIDIOC_PREPARE_BUF: + { + struct v4l2_buffer *b = arg; + + if (!ops->vidioc_prepare_buf) + break; + ret = check_fmt(ops, b->type); + if (ret) + break; + + ret = ops->vidioc_prepare_buf(file, fh, b); + + dbgarg(cmd, "index=%d", b->index); + break; + } + default: + if (!ops->vidioc_default) + break; + ret = ops->vidioc_default(file, fh, ret_prio >= 0, cmd, arg); + break; } /* switch */ -exit_prio: if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) { if (ret < 0) { v4l_print_ioctl(vfd->name, cmd); diff --git a/drivers/media/video/v4l2-mem2mem.c b/drivers/media/video/v4l2-mem2mem.c index 3b15bf5892a..975d0fa938c 100644 --- a/drivers/media/video/v4l2-mem2mem.c +++ b/drivers/media/video/v4l2-mem2mem.c @@ -97,11 +97,12 @@ void *v4l2_m2m_next_buf(struct v4l2_m2m_queue_ctx *q_ctx) spin_lock_irqsave(&q_ctx->rdy_spinlock, flags); - if (list_empty(&q_ctx->rdy_queue)) - goto end; + if (list_empty(&q_ctx->rdy_queue)) { + spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags); + return NULL; + } b = list_entry(q_ctx->rdy_queue.next, struct v4l2_m2m_buffer, list); -end: spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags); return &b->vb; } @@ -117,12 +118,13 @@ void *v4l2_m2m_buf_remove(struct v4l2_m2m_queue_ctx *q_ctx) unsigned long flags; spin_lock_irqsave(&q_ctx->rdy_spinlock, flags); - if (!list_empty(&q_ctx->rdy_queue)) { - b = list_entry(q_ctx->rdy_queue.next, struct v4l2_m2m_buffer, - list); - list_del(&b->list); - q_ctx->num_rdy--; + if (list_empty(&q_ctx->rdy_queue)) { + spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags); + return NULL; } + b = list_entry(q_ctx->rdy_queue.next, struct v4l2_m2m_buffer, list); + list_del(&b->list); + q_ctx->num_rdy--; spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags); return &b->vb; diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c index b7967c9dc4a..65ade5f03c2 100644 --- a/drivers/media/video/v4l2-subdev.c +++ b/drivers/media/video/v4l2-subdev.c @@ -24,6 +24,7 @@ #include <linux/slab.h> #include <linux/types.h> #include <linux/videodev2.h> +#include <linux/export.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> @@ -173,6 +174,25 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) case VIDIOC_UNSUBSCRIBE_EVENT: return v4l2_subdev_call(sd, core, unsubscribe_event, vfh, arg); + +#ifdef CONFIG_VIDEO_ADV_DEBUG + case VIDIOC_DBG_G_REGISTER: + { + struct v4l2_dbg_register *p = arg; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + return v4l2_subdev_call(sd, core, g_register, p); + } + case VIDIOC_DBG_S_REGISTER: + { + struct v4l2_dbg_register *p = arg; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + return v4l2_subdev_call(sd, core, s_register, p); + } +#endif #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) case VIDIOC_SUBDEV_G_FMT: { struct v4l2_subdev_format *format = arg; diff --git a/drivers/media/video/via-camera.c b/drivers/media/video/via-camera.c index 85d3048c1d6..cbf13d09b4a 100644 --- a/drivers/media/video/via-camera.c +++ b/drivers/media/video/via-camera.c @@ -21,7 +21,7 @@ #include <media/videobuf-dma-sg.h> #include <linux/delay.h> #include <linux/dma-mapping.h> -#include <linux/pm_qos_params.h> +#include <linux/pm_qos.h> #include <linux/via-core.h> #include <linux/via-gpio.h> #include <linux/via_i2c.h> @@ -69,7 +69,7 @@ struct via_camera { struct mutex lock; enum viacam_opstate opstate; unsigned long flags; - struct pm_qos_request_list qos_request; + struct pm_qos_request qos_request; /* * GPIO info for power/reset management */ @@ -1332,6 +1332,8 @@ static __devinit bool viacam_serial_is_enabled(void) struct pci_bus *pbus = pci_find_bus(0, 0); u8 cbyte; + if (!pbus) + return false; pci_bus_read_config_byte(pbus, VIACAM_SERIAL_DEVFN, VIACAM_SERIAL_CREG, &cbyte); if ((cbyte & VIACAM_SERIAL_BIT) == 0) diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c index 3015e600094..95a3f5e82ae 100644 --- a/drivers/media/video/videobuf2-core.c +++ b/drivers/media/video/videobuf2-core.c @@ -38,13 +38,13 @@ module_param(debug, int, 0644); (((q)->ops->op) ? ((q)->ops->op(args)) : 0) #define V4L2_BUFFER_STATE_FLAGS (V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED | \ - V4L2_BUF_FLAG_DONE | V4L2_BUF_FLAG_ERROR) + V4L2_BUF_FLAG_DONE | V4L2_BUF_FLAG_ERROR | \ + V4L2_BUF_FLAG_PREPARED) /** * __vb2_buf_mem_alloc() - allocate video memory for the given buffer */ -static int __vb2_buf_mem_alloc(struct vb2_buffer *vb, - unsigned long *plane_sizes) +static int __vb2_buf_mem_alloc(struct vb2_buffer *vb) { struct vb2_queue *q = vb->vb2_queue; void *mem_priv; @@ -53,13 +53,13 @@ static int __vb2_buf_mem_alloc(struct vb2_buffer *vb, /* Allocate memory for all planes in this buffer */ for (plane = 0; plane < vb->num_planes; ++plane) { mem_priv = call_memop(q, plane, alloc, q->alloc_ctx[plane], - plane_sizes[plane]); + q->plane_sizes[plane]); if (IS_ERR_OR_NULL(mem_priv)) goto free; /* Associate allocator private data with this plane */ vb->planes[plane].mem_priv = mem_priv; - vb->v4l2_planes[plane].length = plane_sizes[plane]; + vb->v4l2_planes[plane].length = q->plane_sizes[plane]; } return 0; @@ -110,18 +110,28 @@ static void __vb2_buf_userptr_put(struct vb2_buffer *vb) * __setup_offsets() - setup unique offsets ("cookies") for every plane in * every buffer on the queue */ -static void __setup_offsets(struct vb2_queue *q) +static void __setup_offsets(struct vb2_queue *q, unsigned int n) { unsigned int buffer, plane; struct vb2_buffer *vb; - unsigned long off = 0; + unsigned long off; - for (buffer = 0; buffer < q->num_buffers; ++buffer) { + if (q->num_buffers) { + struct v4l2_plane *p; + vb = q->bufs[q->num_buffers - 1]; + p = &vb->v4l2_planes[vb->num_planes - 1]; + off = PAGE_ALIGN(p->m.mem_offset + p->length); + } else { + off = 0; + } + + for (buffer = q->num_buffers; buffer < q->num_buffers + n; ++buffer) { vb = q->bufs[buffer]; if (!vb) continue; for (plane = 0; plane < vb->num_planes; ++plane) { + vb->v4l2_planes[plane].length = q->plane_sizes[plane]; vb->v4l2_planes[plane].m.mem_offset = off; dprintk(3, "Buffer %d, plane %d offset 0x%08lx\n", @@ -141,8 +151,7 @@ static void __setup_offsets(struct vb2_queue *q) * Returns the number of buffers successfully allocated. */ static int __vb2_queue_alloc(struct vb2_queue *q, enum v4l2_memory memory, - unsigned int num_buffers, unsigned int num_planes, - unsigned long plane_sizes[]) + unsigned int num_buffers, unsigned int num_planes) { unsigned int buffer; struct vb2_buffer *vb; @@ -163,13 +172,13 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum v4l2_memory memory, vb->state = VB2_BUF_STATE_DEQUEUED; vb->vb2_queue = q; vb->num_planes = num_planes; - vb->v4l2_buf.index = buffer; + vb->v4l2_buf.index = q->num_buffers + buffer; vb->v4l2_buf.type = q->type; vb->v4l2_buf.memory = memory; /* Allocate video buffer memory for the MMAP type */ if (memory == V4L2_MEMORY_MMAP) { - ret = __vb2_buf_mem_alloc(vb, plane_sizes); + ret = __vb2_buf_mem_alloc(vb); if (ret) { dprintk(1, "Failed allocating memory for " "buffer %d\n", buffer); @@ -191,15 +200,13 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum v4l2_memory memory, } } - q->bufs[buffer] = vb; + q->bufs[q->num_buffers + buffer] = vb; } - q->num_buffers = buffer; - - __setup_offsets(q); + __setup_offsets(q, buffer); dprintk(1, "Allocated %d buffers, %d plane(s) each\n", - q->num_buffers, num_planes); + buffer, num_planes); return buffer; } @@ -207,12 +214,13 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum v4l2_memory memory, /** * __vb2_free_mem() - release all video buffer memory for a given queue */ -static void __vb2_free_mem(struct vb2_queue *q) +static void __vb2_free_mem(struct vb2_queue *q, unsigned int buffers) { unsigned int buffer; struct vb2_buffer *vb; - for (buffer = 0; buffer < q->num_buffers; ++buffer) { + for (buffer = q->num_buffers - buffers; buffer < q->num_buffers; + ++buffer) { vb = q->bufs[buffer]; if (!vb) continue; @@ -226,17 +234,18 @@ static void __vb2_free_mem(struct vb2_queue *q) } /** - * __vb2_queue_free() - free the queue - video memory and related information - * and return the queue to an uninitialized state. Might be called even if the - * queue has already been freed. + * __vb2_queue_free() - free buffers at the end of the queue - video memory and + * related information, if no buffers are left return the queue to an + * uninitialized state. Might be called even if the queue has already been freed. */ -static void __vb2_queue_free(struct vb2_queue *q) +static void __vb2_queue_free(struct vb2_queue *q, unsigned int buffers) { unsigned int buffer; /* Call driver-provided cleanup function for each buffer, if provided */ if (q->ops->buf_cleanup) { - for (buffer = 0; buffer < q->num_buffers; ++buffer) { + for (buffer = q->num_buffers - buffers; buffer < q->num_buffers; + ++buffer) { if (NULL == q->bufs[buffer]) continue; q->ops->buf_cleanup(q->bufs[buffer]); @@ -244,23 +253,26 @@ static void __vb2_queue_free(struct vb2_queue *q) } /* Release video buffer memory */ - __vb2_free_mem(q); + __vb2_free_mem(q, buffers); /* Free videobuf buffers */ - for (buffer = 0; buffer < q->num_buffers; ++buffer) { + for (buffer = q->num_buffers - buffers; buffer < q->num_buffers; + ++buffer) { kfree(q->bufs[buffer]); q->bufs[buffer] = NULL; } - q->num_buffers = 0; - q->memory = 0; + q->num_buffers -= buffers; + if (!q->num_buffers) + q->memory = 0; + INIT_LIST_HEAD(&q->queued_list); } /** * __verify_planes_array() - verify that the planes array passed in struct * v4l2_buffer from userspace can be safely used */ -static int __verify_planes_array(struct vb2_buffer *vb, struct v4l2_buffer *b) +static int __verify_planes_array(struct vb2_buffer *vb, const struct v4l2_buffer *b) { /* Is memory for copying plane information present? */ if (NULL == b->m.planes) { @@ -279,13 +291,48 @@ static int __verify_planes_array(struct vb2_buffer *vb, struct v4l2_buffer *b) } /** + * __buffer_in_use() - return true if the buffer is in use and + * the queue cannot be freed (by the means of REQBUFS(0)) call + */ +static bool __buffer_in_use(struct vb2_queue *q, struct vb2_buffer *vb) +{ + unsigned int plane; + for (plane = 0; plane < vb->num_planes; ++plane) { + void *mem_priv = vb->planes[plane].mem_priv; + /* + * If num_users() has not been provided, call_memop + * will return 0, apparently nobody cares about this + * case anyway. If num_users() returns more than 1, + * we are not the only user of the plane's memory. + */ + if (mem_priv && call_memop(q, plane, num_users, mem_priv) > 1) + return true; + } + return false; +} + +/** + * __buffers_in_use() - return true if any buffers on the queue are in use and + * the queue cannot be freed (by the means of REQBUFS(0)) call + */ +static bool __buffers_in_use(struct vb2_queue *q) +{ + unsigned int buffer; + for (buffer = 0; buffer < q->num_buffers; ++buffer) { + if (__buffer_in_use(q, q->bufs[buffer])) + return true; + } + return false; +} + +/** * __fill_v4l2_buffer() - fill in a struct v4l2_buffer with information to be * returned to userspace */ static int __fill_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b) { struct vb2_queue *q = vb->vb2_queue; - int ret = 0; + int ret; /* Copy back data such as timestamp, flags, input, etc. */ memcpy(b, &vb->v4l2_buf, offsetof(struct v4l2_buffer, m)); @@ -332,15 +379,18 @@ static int __fill_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b) case VB2_BUF_STATE_DONE: b->flags |= V4L2_BUF_FLAG_DONE; break; + case VB2_BUF_STATE_PREPARED: + b->flags |= V4L2_BUF_FLAG_PREPARED; + break; case VB2_BUF_STATE_DEQUEUED: /* nothing */ break; } - if (vb->num_planes_mapped == vb->num_planes) + if (__buffer_in_use(q, vb)) b->flags |= V4L2_BUF_FLAG_MAPPED; - return ret; + return 0; } /** @@ -402,33 +452,6 @@ static int __verify_mmap_ops(struct vb2_queue *q) } /** - * __buffers_in_use() - return true if any buffers on the queue are in use and - * the queue cannot be freed (by the means of REQBUFS(0)) call - */ -static bool __buffers_in_use(struct vb2_queue *q) -{ - unsigned int buffer, plane; - struct vb2_buffer *vb; - - for (buffer = 0; buffer < q->num_buffers; ++buffer) { - vb = q->bufs[buffer]; - for (plane = 0; plane < vb->num_planes; ++plane) { - /* - * If num_users() has not been provided, call_memop - * will return 0, apparently nobody cares about this - * case anyway. If num_users() returns more than 1, - * we are not the only user of the plane's memory. - */ - if (call_memop(q, plane, num_users, - vb->planes[plane].mem_priv) > 1) - return true; - } - } - - return false; -} - -/** * vb2_reqbufs() - Initiate streaming * @q: videobuf2 queue * @req: struct passed from userspace to vidioc_reqbufs handler in driver @@ -453,8 +476,7 @@ static bool __buffers_in_use(struct vb2_queue *q) */ int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req) { - unsigned int num_buffers, num_planes; - unsigned long plane_sizes[VIDEO_MAX_PLANES]; + unsigned int num_buffers, allocated_buffers, num_planes = 0; int ret = 0; if (q->fileio) { @@ -502,7 +524,7 @@ int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req) return -EBUSY; } - __vb2_queue_free(q); + __vb2_queue_free(q, q->num_buffers); /* * In case of REQBUFS(0) return immediately without calling @@ -516,7 +538,7 @@ int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req) * Make sure the requested values and current defaults are sane. */ num_buffers = min_t(unsigned int, req->count, VIDEO_MAX_FRAME); - memset(plane_sizes, 0, sizeof(plane_sizes)); + memset(q->plane_sizes, 0, sizeof(q->plane_sizes)); memset(q->alloc_ctx, 0, sizeof(q->alloc_ctx)); q->memory = req->memory; @@ -524,57 +546,180 @@ int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req) * Ask the driver how many buffers and planes per buffer it requires. * Driver also sets the size and allocator context for each plane. */ - ret = call_qop(q, queue_setup, q, &num_buffers, &num_planes, - plane_sizes, q->alloc_ctx); + ret = call_qop(q, queue_setup, q, NULL, &num_buffers, &num_planes, + q->plane_sizes, q->alloc_ctx); if (ret) return ret; /* Finally, allocate buffers and video memory */ - ret = __vb2_queue_alloc(q, req->memory, num_buffers, num_planes, - plane_sizes); + ret = __vb2_queue_alloc(q, req->memory, num_buffers, num_planes); if (ret == 0) { dprintk(1, "Memory allocation failed\n"); return -ENOMEM; } + allocated_buffers = ret; + /* * Check if driver can handle the allocated number of buffers. */ - if (ret < num_buffers) { - unsigned int orig_num_buffers; + if (allocated_buffers < num_buffers) { + num_buffers = allocated_buffers; - orig_num_buffers = num_buffers = ret; - ret = call_qop(q, queue_setup, q, &num_buffers, &num_planes, - plane_sizes, q->alloc_ctx); - if (ret) - goto free_mem; + ret = call_qop(q, queue_setup, q, NULL, &num_buffers, + &num_planes, q->plane_sizes, q->alloc_ctx); - if (orig_num_buffers < num_buffers) { + if (!ret && allocated_buffers < num_buffers) ret = -ENOMEM; - goto free_mem; - } /* - * Ok, driver accepted smaller number of buffers. + * Either the driver has accepted a smaller number of buffers, + * or .queue_setup() returned an error */ - ret = num_buffers; + } + + q->num_buffers = allocated_buffers; + + if (ret < 0) { + __vb2_queue_free(q, allocated_buffers); + return ret; } /* * Return the number of successfully allocated buffers * to the userspace. */ - req->count = ret; + req->count = allocated_buffers; return 0; - -free_mem: - __vb2_queue_free(q); - return ret; } EXPORT_SYMBOL_GPL(vb2_reqbufs); /** + * vb2_create_bufs() - Allocate buffers and any required auxiliary structs + * @q: videobuf2 queue + * @create: creation parameters, passed from userspace to vidioc_create_bufs + * handler in driver + * + * Should be called from vidioc_create_bufs ioctl handler of a driver. + * This function: + * 1) verifies parameter sanity + * 2) calls the .queue_setup() queue operation + * 3) performs any necessary memory allocations + * + * The return values from this function are intended to be directly returned + * from vidioc_create_bufs handler in driver. + */ +int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create) +{ + unsigned int num_planes = 0, num_buffers, allocated_buffers; + int ret = 0; + + if (q->fileio) { + dprintk(1, "%s(): file io in progress\n", __func__); + return -EBUSY; + } + + if (create->memory != V4L2_MEMORY_MMAP + && create->memory != V4L2_MEMORY_USERPTR) { + dprintk(1, "%s(): unsupported memory type\n", __func__); + return -EINVAL; + } + + if (create->format.type != q->type) { + dprintk(1, "%s(): requested type is incorrect\n", __func__); + return -EINVAL; + } + + /* + * Make sure all the required memory ops for given memory type + * are available. + */ + if (create->memory == V4L2_MEMORY_MMAP && __verify_mmap_ops(q)) { + dprintk(1, "%s(): MMAP for current setup unsupported\n", __func__); + return -EINVAL; + } + + if (create->memory == V4L2_MEMORY_USERPTR && __verify_userptr_ops(q)) { + dprintk(1, "%s(): USERPTR for current setup unsupported\n", __func__); + return -EINVAL; + } + + if (q->num_buffers == VIDEO_MAX_FRAME) { + dprintk(1, "%s(): maximum number of buffers already allocated\n", + __func__); + return -ENOBUFS; + } + + create->index = q->num_buffers; + + if (!q->num_buffers) { + memset(q->plane_sizes, 0, sizeof(q->plane_sizes)); + memset(q->alloc_ctx, 0, sizeof(q->alloc_ctx)); + q->memory = create->memory; + } + + num_buffers = min(create->count, VIDEO_MAX_FRAME - q->num_buffers); + + /* + * Ask the driver, whether the requested number of buffers, planes per + * buffer and their sizes are acceptable + */ + ret = call_qop(q, queue_setup, q, &create->format, &num_buffers, + &num_planes, q->plane_sizes, q->alloc_ctx); + if (ret) + return ret; + + /* Finally, allocate buffers and video memory */ + ret = __vb2_queue_alloc(q, create->memory, num_buffers, + num_planes); + if (ret < 0) { + dprintk(1, "Memory allocation failed with error: %d\n", ret); + return ret; + } + + allocated_buffers = ret; + + /* + * Check if driver can handle the so far allocated number of buffers. + */ + if (ret < num_buffers) { + num_buffers = ret; + + /* + * q->num_buffers contains the total number of buffers, that the + * queue driver has set up + */ + ret = call_qop(q, queue_setup, q, &create->format, &num_buffers, + &num_planes, q->plane_sizes, q->alloc_ctx); + + if (!ret && allocated_buffers < num_buffers) + ret = -ENOMEM; + + /* + * Either the driver has accepted a smaller number of buffers, + * or .queue_setup() returned an error + */ + } + + q->num_buffers += allocated_buffers; + + if (ret < 0) { + __vb2_queue_free(q, allocated_buffers); + return ret; + } + + /* + * Return the number of successfully allocated buffers + * to the userspace. + */ + create->count = allocated_buffers; + + return 0; +} +EXPORT_SYMBOL_GPL(vb2_create_bufs); + +/** * vb2_plane_vaddr() - Return a kernel virtual address of a given plane * @vb: vb2_buffer to which the plane in question belongs to * @plane_no: plane number for which the address is to be returned @@ -658,7 +803,7 @@ EXPORT_SYMBOL_GPL(vb2_buffer_done); * __fill_vb2_buffer() - fill a vb2_buffer with information provided in * a v4l2_buffer by the userspace */ -static int __fill_vb2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b, +static int __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *b, struct v4l2_plane *v4l2_planes) { unsigned int plane; @@ -722,7 +867,7 @@ static int __fill_vb2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b, /** * __qbuf_userptr() - handle qbuf of a USERPTR buffer */ -static int __qbuf_userptr(struct vb2_buffer *vb, struct v4l2_buffer *b) +static int __qbuf_userptr(struct vb2_buffer *vb, const struct v4l2_buffer *b) { struct v4l2_plane planes[VIDEO_MAX_PLANES]; struct vb2_queue *q = vb->vb2_queue; @@ -745,12 +890,20 @@ static int __qbuf_userptr(struct vb2_buffer *vb, struct v4l2_buffer *b) dprintk(3, "qbuf: userspace address for plane %d changed, " "reacquiring memory\n", plane); + /* Check if the provided plane buffer is large enough */ + if (planes[plane].length < q->plane_sizes[plane]) { + ret = -EINVAL; + goto err; + } + /* Release previously acquired memory if present */ if (vb->planes[plane].mem_priv) call_memop(q, plane, put_userptr, vb->planes[plane].mem_priv); vb->planes[plane].mem_priv = NULL; + vb->v4l2_planes[plane].m.userptr = 0; + vb->v4l2_planes[plane].length = 0; /* Acquire each plane's memory */ if (q->mem_ops->get_userptr) { @@ -788,10 +941,13 @@ static int __qbuf_userptr(struct vb2_buffer *vb, struct v4l2_buffer *b) return 0; err: /* In case of errors, release planes that were already acquired */ - for (; plane > 0; --plane) { - call_memop(q, plane, put_userptr, - vb->planes[plane - 1].mem_priv); - vb->planes[plane - 1].mem_priv = NULL; + for (plane = 0; plane < vb->num_planes; ++plane) { + if (vb->planes[plane].mem_priv) + call_memop(q, plane, put_userptr, + vb->planes[plane].mem_priv); + vb->planes[plane].mem_priv = NULL; + vb->v4l2_planes[plane].m.userptr = 0; + vb->v4l2_planes[plane].length = 0; } return ret; @@ -800,7 +956,7 @@ err: /** * __qbuf_mmap() - handle qbuf of an MMAP buffer */ -static int __qbuf_mmap(struct vb2_buffer *vb, struct v4l2_buffer *b) +static int __qbuf_mmap(struct vb2_buffer *vb, const struct v4l2_buffer *b) { return __fill_vb2_buffer(vb, b, vb->v4l2_planes); } @@ -817,6 +973,95 @@ static void __enqueue_in_driver(struct vb2_buffer *vb) q->ops->buf_queue(vb); } +static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b) +{ + struct vb2_queue *q = vb->vb2_queue; + int ret; + + switch (q->memory) { + case V4L2_MEMORY_MMAP: + ret = __qbuf_mmap(vb, b); + break; + case V4L2_MEMORY_USERPTR: + ret = __qbuf_userptr(vb, b); + break; + default: + WARN(1, "Invalid queue type\n"); + ret = -EINVAL; + } + + if (!ret) + ret = call_qop(q, buf_prepare, vb); + if (ret) + dprintk(1, "qbuf: buffer preparation failed: %d\n", ret); + else + vb->state = VB2_BUF_STATE_PREPARED; + + return ret; +} + +/** + * vb2_prepare_buf() - Pass ownership of a buffer from userspace to the kernel + * @q: videobuf2 queue + * @b: buffer structure passed from userspace to vidioc_prepare_buf + * handler in driver + * + * Should be called from vidioc_prepare_buf ioctl handler of a driver. + * This function: + * 1) verifies the passed buffer, + * 2) calls buf_prepare callback in the driver (if provided), in which + * driver-specific buffer initialization can be performed, + * + * The return values from this function are intended to be directly returned + * from vidioc_prepare_buf handler in driver. + */ +int vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b) +{ + struct vb2_buffer *vb; + int ret; + + if (q->fileio) { + dprintk(1, "%s(): file io in progress\n", __func__); + return -EBUSY; + } + + if (b->type != q->type) { + dprintk(1, "%s(): invalid buffer type\n", __func__); + return -EINVAL; + } + + if (b->index >= q->num_buffers) { + dprintk(1, "%s(): buffer index out of range\n", __func__); + return -EINVAL; + } + + vb = q->bufs[b->index]; + if (NULL == vb) { + /* Should never happen */ + dprintk(1, "%s(): buffer is NULL\n", __func__); + return -EINVAL; + } + + if (b->memory != q->memory) { + dprintk(1, "%s(): invalid memory type\n", __func__); + return -EINVAL; + } + + if (vb->state != VB2_BUF_STATE_DEQUEUED) { + dprintk(1, "%s(): invalid buffer state %d\n", __func__, vb->state); + return -EINVAL; + } + + ret = __buf_prepare(vb, b); + if (ret < 0) + return ret; + + __fill_v4l2_buffer(vb, b); + + return 0; +} +EXPORT_SYMBOL_GPL(vb2_prepare_buf); + /** * vb2_qbuf() - Queue a buffer from userspace * @q: videobuf2 queue @@ -826,8 +1071,8 @@ static void __enqueue_in_driver(struct vb2_buffer *vb) * Should be called from vidioc_qbuf ioctl handler of a driver. * This function: * 1) verifies the passed buffer, - * 2) calls buf_prepare callback in the driver (if provided), in which - * driver-specific buffer initialization can be performed, + * 2) if necessary, calls buf_prepare callback in the driver (if provided), in + * which driver-specific buffer initialization can be performed, * 3) if streaming is on, queues the buffer in driver by the means of buf_queue * callback for processing. * @@ -837,7 +1082,7 @@ static void __enqueue_in_driver(struct vb2_buffer *vb) int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b) { struct vb2_buffer *vb; - int ret = 0; + int ret; if (q->fileio) { dprintk(1, "qbuf: file io in progress\n"); @@ -866,29 +1111,18 @@ int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b) return -EINVAL; } - if (vb->state != VB2_BUF_STATE_DEQUEUED) { + switch (vb->state) { + case VB2_BUF_STATE_DEQUEUED: + ret = __buf_prepare(vb, b); + if (ret) + return ret; + case VB2_BUF_STATE_PREPARED: + break; + default: dprintk(1, "qbuf: buffer already in use\n"); return -EINVAL; } - if (q->memory == V4L2_MEMORY_MMAP) - ret = __qbuf_mmap(vb, b); - else if (q->memory == V4L2_MEMORY_USERPTR) - ret = __qbuf_userptr(vb, b); - else { - WARN(1, "Invalid queue type\n"); - return -EINVAL; - } - - if (ret) - return ret; - - ret = call_qop(q, buf_prepare, vb); - if (ret) { - dprintk(1, "qbuf: buffer preparation failed\n"); - return ret; - } - /* * Add to the queued buffers list, a buffer will stay on it until * dequeued in dqbuf. @@ -903,6 +1137,9 @@ int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b) if (q->streaming) __enqueue_in_driver(vb); + /* Fill buffer information for the userspace */ + __fill_v4l2_buffer(vb, b); + dprintk(1, "qbuf of buffer %d succeeded\n", vb->v4l2_buf.index); return 0; } @@ -1096,6 +1333,43 @@ int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking) EXPORT_SYMBOL_GPL(vb2_dqbuf); /** + * __vb2_queue_cancel() - cancel and stop (pause) streaming + * + * Removes all queued buffers from driver's queue and all buffers queued by + * userspace from videobuf's queue. Returns to state after reqbufs. + */ +static void __vb2_queue_cancel(struct vb2_queue *q) +{ + unsigned int i; + + /* + * Tell driver to stop all transactions and release all queued + * buffers. + */ + if (q->streaming) + call_qop(q, stop_streaming, q); + q->streaming = 0; + + /* + * Remove all buffers from videobuf's list... + */ + INIT_LIST_HEAD(&q->queued_list); + /* + * ...and done list; userspace will not receive any buffers it + * has not already dequeued before initiating cancel. + */ + INIT_LIST_HEAD(&q->done_list); + atomic_set(&q->queued_count, 0); + wake_up_all(&q->done_wq); + + /* + * Reinitialize all buffers for next use. + */ + for (i = 0; i < q->num_buffers; ++i) + q->bufs[i]->state = VB2_BUF_STATE_DEQUEUED; +} + +/** * vb2_streamon - start streaming * @q: videobuf2 queue * @type: type argument passed from userspace to vidioc_streamon handler @@ -1103,7 +1377,7 @@ EXPORT_SYMBOL_GPL(vb2_dqbuf); * Should be called from vidioc_streamon handler of a driver. * This function: * 1) verifies current state - * 2) starts streaming and passes any previously queued buffers to the driver + * 2) passes any previously queued buffers to the driver and starts streaming * * The return values from this function are intended to be directly returned * from vidioc_streamon handler in the driver. @@ -1129,75 +1403,29 @@ int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type) } /* - * Cannot start streaming on an OUTPUT device if no buffers have - * been queued yet. + * If any buffers were queued before streamon, + * we can now pass them to driver for processing. */ - if (V4L2_TYPE_IS_OUTPUT(q->type)) { - if (list_empty(&q->queued_list)) { - dprintk(1, "streamon: no output buffers queued\n"); - return -EINVAL; - } - } + list_for_each_entry(vb, &q->queued_list, queued_entry) + __enqueue_in_driver(vb); /* * Let driver notice that streaming state has been enabled. */ - ret = call_qop(q, start_streaming, q); + ret = call_qop(q, start_streaming, q, atomic_read(&q->queued_count)); if (ret) { dprintk(1, "streamon: driver refused to start streaming\n"); + __vb2_queue_cancel(q); return ret; } q->streaming = 1; - /* - * If any buffers were queued before streamon, - * we can now pass them to driver for processing. - */ - list_for_each_entry(vb, &q->queued_list, queued_entry) - __enqueue_in_driver(vb); - dprintk(3, "Streamon successful\n"); return 0; } EXPORT_SYMBOL_GPL(vb2_streamon); -/** - * __vb2_queue_cancel() - cancel and stop (pause) streaming - * - * Removes all queued buffers from driver's queue and all buffers queued by - * userspace from videobuf's queue. Returns to state after reqbufs. - */ -static void __vb2_queue_cancel(struct vb2_queue *q) -{ - unsigned int i; - - /* - * Tell driver to stop all transactions and release all queued - * buffers. - */ - if (q->streaming) - call_qop(q, stop_streaming, q); - q->streaming = 0; - - /* - * Remove all buffers from videobuf's list... - */ - INIT_LIST_HEAD(&q->queued_list); - /* - * ...and done list; userspace will not receive any buffers it - * has not already dequeued before initiating cancel. - */ - INIT_LIST_HEAD(&q->done_list); - atomic_set(&q->queued_count, 0); - wake_up_all(&q->done_wq); - - /* - * Reinitialize all buffers for next use. - */ - for (i = 0; i < q->num_buffers; ++i) - q->bufs[i]->state = VB2_BUF_STATE_DEQUEUED; -} /** * vb2_streamoff - stop streaming @@ -1336,14 +1564,42 @@ int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma) if (ret) return ret; - vb_plane->mapped = 1; - vb->num_planes_mapped++; - dprintk(3, "Buffer %d, plane %d successfully mapped\n", buffer, plane); return 0; } EXPORT_SYMBOL_GPL(vb2_mmap); +#ifndef CONFIG_MMU +unsigned long vb2_get_unmapped_area(struct vb2_queue *q, + unsigned long addr, + unsigned long len, + unsigned long pgoff, + unsigned long flags) +{ + unsigned long off = pgoff << PAGE_SHIFT; + struct vb2_buffer *vb; + unsigned int buffer, plane; + int ret; + + if (q->memory != V4L2_MEMORY_MMAP) { + dprintk(1, "Queue is not currently set up for mmap\n"); + return -EINVAL; + } + + /* + * Find the plane corresponding to the offset passed by userspace. + */ + ret = __find_plane_by_offset(q, off, &buffer, &plane); + if (ret) + return ret; + + vb = q->bufs[buffer]; + + return (unsigned long)vb2_plane_vaddr(vb, plane); +} +EXPORT_SYMBOL_GPL(vb2_get_unmapped_area); +#endif + static int __vb2_init_fileio(struct vb2_queue *q, int read); static int __vb2_cleanup_fileio(struct vb2_queue *q); @@ -1461,7 +1717,7 @@ void vb2_queue_release(struct vb2_queue *q) { __vb2_cleanup_fileio(q); __vb2_queue_cancel(q); - __vb2_queue_free(q); + __vb2_queue_free(q, q->num_buffers); } EXPORT_SYMBOL_GPL(vb2_queue_release); diff --git a/drivers/media/video/videobuf2-dma-contig.c b/drivers/media/video/videobuf2-dma-contig.c index a790a5f8c06..f17ad98fcc5 100644 --- a/drivers/media/video/videobuf2-dma-contig.c +++ b/drivers/media/video/videobuf2-dma-contig.c @@ -24,7 +24,7 @@ struct vb2_dc_conf { struct vb2_dc_buf { struct vb2_dc_conf *conf; void *vaddr; - dma_addr_t paddr; + dma_addr_t dma_addr; unsigned long size; struct vm_area_struct *vma; atomic_t refcount; @@ -42,7 +42,7 @@ static void *vb2_dma_contig_alloc(void *alloc_ctx, unsigned long size) if (!buf) return ERR_PTR(-ENOMEM); - buf->vaddr = dma_alloc_coherent(conf->dev, size, &buf->paddr, + buf->vaddr = dma_alloc_coherent(conf->dev, size, &buf->dma_addr, GFP_KERNEL); if (!buf->vaddr) { dev_err(conf->dev, "dma_alloc_coherent of size %ld failed\n", @@ -69,7 +69,7 @@ static void vb2_dma_contig_put(void *buf_priv) if (atomic_dec_and_test(&buf->refcount)) { dma_free_coherent(buf->conf->dev, buf->size, buf->vaddr, - buf->paddr); + buf->dma_addr); kfree(buf); } } @@ -78,7 +78,7 @@ static void *vb2_dma_contig_cookie(void *buf_priv) { struct vb2_dc_buf *buf = buf_priv; - return &buf->paddr; + return &buf->dma_addr; } static void *vb2_dma_contig_vaddr(void *buf_priv) @@ -106,7 +106,7 @@ static int vb2_dma_contig_mmap(void *buf_priv, struct vm_area_struct *vma) return -EINVAL; } - return vb2_mmap_pfn_range(vma, buf->paddr, buf->size, + return vb2_mmap_pfn_range(vma, buf->dma_addr, buf->size, &vb2_common_vm_ops, &buf->handler); } @@ -115,14 +115,14 @@ static void *vb2_dma_contig_get_userptr(void *alloc_ctx, unsigned long vaddr, { struct vb2_dc_buf *buf; struct vm_area_struct *vma; - dma_addr_t paddr = 0; + dma_addr_t dma_addr = 0; int ret; buf = kzalloc(sizeof *buf, GFP_KERNEL); if (!buf) return ERR_PTR(-ENOMEM); - ret = vb2_get_contig_userptr(vaddr, size, &vma, &paddr); + ret = vb2_get_contig_userptr(vaddr, size, &vma, &dma_addr); if (ret) { printk(KERN_ERR "Failed acquiring VMA for vaddr 0x%08lx\n", vaddr); @@ -131,7 +131,7 @@ static void *vb2_dma_contig_get_userptr(void *alloc_ctx, unsigned long vaddr, } buf->size = size; - buf->paddr = paddr; + buf->dma_addr = dma_addr; buf->vma = vma; return buf; diff --git a/drivers/media/video/videobuf2-dma-sg.c b/drivers/media/video/videobuf2-dma-sg.c index 065f468faf8..3bad8b105fe 100644 --- a/drivers/media/video/videobuf2-dma-sg.c +++ b/drivers/media/video/videobuf2-dma-sg.c @@ -75,12 +75,6 @@ static void *vb2_dma_sg_alloc(void *alloc_ctx, unsigned long size) printk(KERN_DEBUG "%s: Allocated buffer of %d pages\n", __func__, buf->sg_desc.num_pages); - - if (!buf->vaddr) - buf->vaddr = vm_map_ram(buf->pages, - buf->sg_desc.num_pages, - -1, - PAGE_KERNEL); return buf; fail_pages_alloc: diff --git a/drivers/media/video/videobuf2-memops.c b/drivers/media/video/videobuf2-memops.c index 569eeb3dfd5..71a7a78c3fc 100644 --- a/drivers/media/video/videobuf2-memops.c +++ b/drivers/media/video/videobuf2-memops.c @@ -68,12 +68,12 @@ void vb2_put_vma(struct vm_area_struct *vma) if (!vma) return; - if (vma->vm_file) - fput(vma->vm_file); - if (vma->vm_ops && vma->vm_ops->close) vma->vm_ops->close(vma); + if (vma->vm_file) + fput(vma->vm_file); + kfree(vma); } EXPORT_SYMBOL_GPL(vb2_put_vma); diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c index a848bd2af97..7d754fbcccb 100644 --- a/drivers/media/video/vivi.c +++ b/drivers/media/video/vivi.c @@ -650,9 +650,9 @@ static void vivi_stop_generating(struct vivi_dev *dev) /* ------------------------------------------------------------------ Videobuf operations ------------------------------------------------------------------*/ -static int queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, - unsigned int *nplanes, unsigned long sizes[], - void *alloc_ctxs[]) +static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) { struct vivi_dev *dev = vb2_get_drv_priv(vq); unsigned long size; @@ -766,7 +766,7 @@ static void buffer_queue(struct vb2_buffer *vb) spin_unlock_irqrestore(&dev->slock, flags); } -static int start_streaming(struct vb2_queue *vq) +static int start_streaming(struct vb2_queue *vq, unsigned int count) { struct vivi_dev *dev = vb2_get_drv_priv(vq); dprintk(dev, 1, "%s\n", __func__); @@ -852,6 +852,11 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, (f->fmt.pix.width * dev->fmt->depth) >> 3; f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + if (dev->fmt->fourcc == V4L2_PIX_FMT_YUYV || + dev->fmt->fourcc == V4L2_PIX_FMT_UYVY) + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + else + f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; return 0; } @@ -885,6 +890,11 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, (f->fmt.pix.width * fmt->depth) >> 3; f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + if (fmt->fourcc == V4L2_PIX_FMT_YUYV || + fmt->fourcc == V4L2_PIX_FMT_UYVY) + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + else + f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; return 0; } @@ -948,6 +958,14 @@ static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) return vb2_streamoff(&dev->vb_vidq, i); } +static int vidioc_log_status(struct file *file, void *priv) +{ + struct vivi_dev *dev = video_drvdata(file); + + v4l2_ctrl_handler_log_status(&dev->ctrl_handler, dev->v4l2_dev.name); + return 0; +} + static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *i) { return 0; @@ -1191,6 +1209,7 @@ static const struct v4l2_ioctl_ops vivi_ioctl_ops = { .vidioc_s_input = vidioc_s_input, .vidioc_streamon = vidioc_streamon, .vidioc_streamoff = vidioc_streamoff, + .vidioc_log_status = vidioc_log_status, .vidioc_subscribe_event = vidioc_subscribe_event, .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; diff --git a/drivers/media/video/vpx3220.c b/drivers/media/video/vpx3220.c index ca372eb911d..e5cad6ff64a 100644 --- a/drivers/media/video/vpx3220.c +++ b/drivers/media/video/vpx3220.c @@ -331,7 +331,7 @@ static int vpx3220_status(struct v4l2_subdev *sd, u32 *pstatus, v4l2_std_id *pst if (pstd) *pstd = std; if (pstatus) - *pstatus = status; + *pstatus = res; return 0; } diff --git a/drivers/media/video/zr364xx.c b/drivers/media/video/zr364xx.c index c492846c1c5..e78cf94f491 100644 --- a/drivers/media/video/zr364xx.c +++ b/drivers/media/video/zr364xx.c @@ -1638,6 +1638,9 @@ static int zr364xx_probe(struct usb_interface *intf, if (!cam->read_endpoint) { dev_err(&intf->dev, "Could not find bulk-in endpoint\n"); + video_device_release(cam->vdev); + kfree(cam); + cam = NULL; return -ENOMEM; } |