diff options
Diffstat (limited to 'drivers/staging/media')
58 files changed, 5750 insertions, 8744 deletions
diff --git a/drivers/staging/media/as102/Makefile b/drivers/staging/media/as102/Makefile index d8dfb757f1e2..8916d8a909bc 100644 --- a/drivers/staging/media/as102/Makefile +++ b/drivers/staging/media/as102/Makefile @@ -3,4 +3,4 @@ dvb-as102-objs := as102_drv.o as102_fw.o as10x_cmd.o as10x_cmd_stream.o \ obj-$(CONFIG_DVB_AS102) += dvb-as102.o -EXTRA_CFLAGS += -Idrivers/media/dvb-core +ccflags-y += -Idrivers/media/dvb-core diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipe.c b/drivers/staging/media/davinci_vpfe/dm365_ipipe.c index 92853539cc0a..05673ed45ce4 100644 --- a/drivers/staging/media/davinci_vpfe/dm365_ipipe.c +++ b/drivers/staging/media/davinci_vpfe/dm365_ipipe.c @@ -1859,5 +1859,5 @@ void vpfe_ipipe_cleanup(struct vpfe_ipipe_device *ipipe, iounmap(ipipe->isp5_base_addr); res = platform_get_resource(pdev, IORESOURCE_MEM, 4); if (res) - release_mem_region(res->start, res->end - res->start + 1); + release_mem_region(res->start, resource_size(res)); } diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c b/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c index c8cae51d3d00..b2f4ef84f3db 100644 --- a/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c +++ b/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c @@ -1065,7 +1065,6 @@ vpfe_ipipeif_cleanup(struct vpfe_ipipeif_device *ipipeif, iounmap(ipipeif->ipipeif_base_addr); res = platform_get_resource(pdev, IORESOURCE_MEM, 3); if (res) - release_mem_region(res->start, - res->end - res->start + 1); + release_mem_region(res->start, resource_size(res)); } diff --git a/drivers/staging/media/davinci_vpfe/dm365_isif.c b/drivers/staging/media/davinci_vpfe/dm365_isif.c index 6d4a93c556e4..5829360f74c9 100644 --- a/drivers/staging/media/davinci_vpfe/dm365_isif.c +++ b/drivers/staging/media/davinci_vpfe/dm365_isif.c @@ -1953,7 +1953,7 @@ static void isif_remove(struct vpfe_isif_device *isif, res = platform_get_resource(pdev, IORESOURCE_MEM, i); if (res) release_mem_region(res->start, - res->end - res->start + 1); + resource_size(res)); i++; } } @@ -2003,7 +2003,7 @@ int vpfe_isif_init(struct vpfe_isif_device *isif, struct platform_device *pdev) status = -ENOENT; goto fail_nobase_res; } - res_len = res->end - res->start + 1; + res_len = resource_size(res); res = request_mem_region(res->start, res_len, res->name); if (!res) { status = -EBUSY; diff --git a/drivers/staging/media/davinci_vpfe/dm365_resizer.c b/drivers/staging/media/davinci_vpfe/dm365_resizer.c index 9cb0262b9b33..126f84c4cb64 100644 --- a/drivers/staging/media/davinci_vpfe/dm365_resizer.c +++ b/drivers/staging/media/davinci_vpfe/dm365_resizer.c @@ -1995,5 +1995,5 @@ vpfe_resizer_cleanup(struct vpfe_resizer_device *vpfe_rsz, res = platform_get_resource(pdev, IORESOURCE_MEM, 5); if (res) release_mem_region(res->start, - res->end - res->start + 1); + resource_size(res)); } diff --git a/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c b/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c index 7b351717bcbb..b88e1ddce229 100644 --- a/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c +++ b/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c @@ -243,7 +243,7 @@ static int vpfe_enable_clock(struct vpfe_device *vpfe_dev) vpfe_dev->clks[i] = clk_get(vpfe_dev->pdev, vpfe_cfg->clocks[i]); - if (vpfe_dev->clks[i] == NULL) { + if (IS_ERR(vpfe_dev->clks[i])) { v4l2_err(vpfe_dev->pdev->driver, "Failed to get clock %s\n", vpfe_cfg->clocks[i]); @@ -264,7 +264,7 @@ static int vpfe_enable_clock(struct vpfe_device *vpfe_dev) return 0; out: for (i = 0; i < vpfe_cfg->num_clocks; i++) - if (vpfe_dev->clks[i]) { + if (!IS_ERR(vpfe_dev->clks[i])) { clk_disable_unprepare(vpfe_dev->clks[i]); clk_put(vpfe_dev->clks[i]); } @@ -719,22 +719,4 @@ static struct platform_driver vpfe_driver = { .remove = vpfe_remove, }; -/** - * vpfe_init : This function registers device driver - */ -static __init int vpfe_init(void) -{ - /* Register driver to the kernel */ - return platform_driver_register(&vpfe_driver); -} - -/** - * vpfe_cleanup : This function un-registers device driver - */ -static void vpfe_cleanup(void) -{ - platform_driver_unregister(&vpfe_driver); -} - -module_init(vpfe_init); -module_exit(vpfe_cleanup); +module_platform_driver(vpfe_driver); diff --git a/drivers/staging/media/davinci_vpfe/vpfe_video.c b/drivers/staging/media/davinci_vpfe/vpfe_video.c index c91d356c13df..ba913f1d955b 100644 --- a/drivers/staging/media/davinci_vpfe/vpfe_video.c +++ b/drivers/staging/media/davinci_vpfe/vpfe_video.c @@ -924,7 +924,7 @@ static int vpfe_querystd(struct file *file, void *priv, v4l2_std_id *std_id) * * Return 0 on success, error code otherwise */ -static int vpfe_s_std(struct file *file, void *priv, v4l2_std_id *std_id) +static int vpfe_s_std(struct file *file, void *priv, v4l2_std_id std_id) { struct vpfe_video_device *video = video_drvdata(file); struct vpfe_device *vpfe_dev = video->vpfe_dev; @@ -945,13 +945,13 @@ static int vpfe_s_std(struct file *file, void *priv, v4l2_std_id *std_id) goto unlock_out; } ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, - core, s_std, *std_id); + core, s_std, std_id); if (ret < 0) { v4l2_err(&vpfe_dev->v4l2_dev, "Failed to set standard\n"); video->stdid = V4L2_STD_UNKNOWN; goto unlock_out; } - video->stdid = *std_id; + video->stdid = std_id; unlock_out: mutex_unlock(&video->lock); return ret; @@ -1016,12 +1016,12 @@ vpfe_query_dv_timings(struct file *file, void *fh, } /* - * vpfe_s_dv_timings() - set dv_preset on external subdev + * vpfe_s_dv_timings() - set dv_timings on external subdev * @file: file pointer * @priv: void pointer * @timings: pointer to v4l2_dv_timings structure * - * set dv_timings pointed by preset on external subdev through + * set dv_timings pointed by timings on external subdev through * v4l2_device_call_until_err, this configures amplifier also * * Return 0 on success, error code otherwise @@ -1042,12 +1042,12 @@ vpfe_s_dv_timings(struct file *file, void *fh, } /* - * vpfe_g_dv_timings() - get dv_preset which is set on external subdev + * vpfe_g_dv_timings() - get dv_timings which is set on external subdev * @file: file pointer * @priv: void pointer * @timings: pointer to v4l2_dv_timings structure * - * get dv_preset which is set on external subdev through + * get dv_timings which is set on external subdev through * v4l2_subdev_call * * Return 0 on success, error code otherwise @@ -1423,7 +1423,7 @@ static int vpfe_dqbuf(struct file *file, void *priv, } /* - * vpfe_streamon() - get dv_preset which is set on external subdev + * vpfe_streamon() - start streaming * @file: file pointer * @priv: void pointer * @buf_type: enum v4l2_buf_type @@ -1472,7 +1472,7 @@ static int vpfe_streamon(struct file *file, void *priv, } /* - * vpfe_streamoff() - get dv_preset which is set on external subdev + * vpfe_streamoff() - stop streaming * @file: file pointer * @priv: void pointer * @buf_type: enum v4l2_buf_type diff --git a/drivers/staging/media/dt3155v4l/dt3155v4l.c b/drivers/staging/media/dt3155v4l/dt3155v4l.c index e33b7f55d84e..c32e0acde4f4 100644 --- a/drivers/staging/media/dt3155v4l/dt3155v4l.c +++ b/drivers/staging/media/dt3155v4l/dt3155v4l.c @@ -26,6 +26,7 @@ #include <linux/slab.h> #include <media/v4l2-dev.h> #include <media/v4l2-ioctl.h> +#include <media/v4l2-common.h> #include <media/videobuf2-dma-contig.h> #include "dt3155v4l.h" @@ -341,7 +342,7 @@ dt3155_irq_handler_even(int irq, void *dev_id) spin_lock(&ipd->lock); if (ipd->curr_buf) { - do_gettimeofday(&ipd->curr_buf->v4l2_buf.timestamp); + v4l2_get_timestamp(&ipd->curr_buf->v4l2_buf.timestamp); ipd->curr_buf->v4l2_buf.sequence = (ipd->field_count) >> 1; vb2_buffer_done(ipd->curr_buf, VB2_BUF_STATE_DONE); } @@ -390,6 +391,7 @@ dt3155_open(struct file *filp) goto err_alloc_queue; } pd->q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + pd->q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; pd->q->io_modes = VB2_READ | VB2_MMAP; pd->q->ops = &q_ops; pd->q->mem_ops = &vb2_dma_contig_memops; @@ -398,7 +400,7 @@ dt3155_open(struct file *filp) pd->field_count = 0; ret = vb2_queue_init(pd->q); if (ret < 0) - return ret; + goto err_request_irq; INIT_LIST_HEAD(&pd->dmaq); spin_lock_init(&pd->lock); /* disable all irqs, clear all irq flags */ @@ -410,6 +412,7 @@ dt3155_open(struct file *filp) goto err_request_irq; } pd->users++; + mutex_unlock(&pd->mux); return 0; /* success */ err_request_irq: kfree(pd->q); @@ -612,9 +615,9 @@ dt3155_ioc_g_std(struct file *filp, void *p, v4l2_std_id *norm) } static int -dt3155_ioc_s_std(struct file *filp, void *p, v4l2_std_id *norm) +dt3155_ioc_s_std(struct file *filp, void *p, v4l2_std_id norm) { - if (*norm & DT3155_CURRENT_NORM) + if (norm & DT3155_CURRENT_NORM) return 0; return -EINVAL; } diff --git a/drivers/staging/media/go7007/Kconfig b/drivers/staging/media/go7007/Kconfig index 7dfb2815b9ec..04bd0fba7b1f 100644 --- a/drivers/staging/media/go7007/Kconfig +++ b/drivers/staging/media/go7007/Kconfig @@ -1,20 +1,25 @@ config VIDEO_GO7007 tristate "WIS GO7007 MPEG encoder support" - depends on VIDEO_DEV && PCI && I2C - depends on SND - select VIDEOBUF_DMA_SG - depends on RC_CORE + depends on VIDEO_DEV && I2C + depends on SND && USB + select VIDEOBUF2_VMALLOC select VIDEO_TUNER - select VIDEO_TVEEPROM + select CYPRESS_FIRMWARE select SND_PCM - select CRC32 + select VIDEO_SONY_BTF_MPX if MEDIA_SUBDRV_AUTOSELECT + select VIDEO_SAA711X if MEDIA_SUBDRV_AUTOSELECT + select VIDEO_TW2804 if MEDIA_SUBDRV_AUTOSELECT + select VIDEO_TW9903 if MEDIA_SUBDRV_AUTOSELECT + select VIDEO_TW9906 if MEDIA_SUBDRV_AUTOSELECT + select VIDEO_OV7640 if MEDIA_SUBDRV_AUTOSELECT + select VIDEO_UDA1342 if MEDIA_SUBDRV_AUTOSELECT default N ---help--- This is a video4linux driver for the WIS GO7007 MPEG encoder chip. To compile this driver as a module, choose M here: the - module will be called go7007 + module will be called go7007. config VIDEO_GO7007_USB tristate "WIS GO7007 USB support" @@ -25,85 +30,25 @@ config VIDEO_GO7007_USB encoder chip over USB. To compile this driver as a module, choose M here: the - module will be called go7007-usb + module will be called go7007-usb. -config VIDEO_GO7007_USB_S2250_BOARD - tristate "Sensoray 2250/2251 support" - depends on VIDEO_GO7007_USB && DVB_USB - default N - ---help--- - This is a video4linux driver for the Sensoray 2250/2251 device. - - To compile this driver as a module, choose M here: the - module will be called s2250 - -config VIDEO_GO7007_OV7640 - tristate "OV7640 subdev support" - depends on VIDEO_GO7007 - default N - ---help--- - This is a video4linux driver for the OV7640 sub-device. - - To compile this driver as a module, choose M here: the - module will be called wis-ov7640 - -config VIDEO_GO7007_SAA7113 - tristate "SAA7113 subdev support" - depends on VIDEO_GO7007 - default N - ---help--- - This is a video4linux driver for the SAA7113 sub-device. - - To compile this driver as a module, choose M here: the - module will be called wis-saa7113 - -config VIDEO_GO7007_SAA7115 - tristate "SAA7115 subdev support" - depends on VIDEO_GO7007 - default N - ---help--- - This is a video4linux driver for the SAA7115 sub-device. - - To compile this driver as a module, choose M here: the - module will be called wis-saa7115 - -config VIDEO_GO7007_TW9903 - tristate "TW9903 subdev support" - depends on VIDEO_GO7007 - default N - ---help--- - This is a video4linux driver for the TW9903 sub-device. - - To compile this driver as a module, choose M here: the - module will be called wis-tw9903 - -config VIDEO_GO7007_UDA1342 - tristate "UDA1342 subdev support" - depends on VIDEO_GO7007 - default N - ---help--- - This is a video4linux driver for the UDA1342 sub-device. - - To compile this driver as a module, choose M here: the - module will be called wis-uda1342 - -config VIDEO_GO7007_SONY_TUNER - tristate "Sony tuner subdev support" +config VIDEO_GO7007_LOADER + tristate "WIS GO7007 Loader support" depends on VIDEO_GO7007 - default N + default y ---help--- - This is a video4linux driver for the Sony Tuner sub-device. + This is a go7007 firmware loader driver for the WIS GO7007 + MPEG encoder chip over USB. To compile this driver as a module, choose M here: the - module will be called wis-sony-tuner + module will be called go7007-loader. -config VIDEO_GO7007_TW2804 - tristate "TW2804 subdev support" - depends on VIDEO_GO7007 +config VIDEO_GO7007_USB_S2250_BOARD + tristate "Sensoray 2250/2251 support" + depends on VIDEO_GO7007_USB && USB default N ---help--- - This is a video4linux driver for the TW2804 sub-device. + This is a video4linux driver for the Sensoray 2250/2251 device. To compile this driver as a module, choose M here: the - module will be called wis-tw2804 - + module will be called s2250. diff --git a/drivers/staging/media/go7007/Makefile b/drivers/staging/media/go7007/Makefile index 3fdbef5306a0..9c6ad4a263ec 100644 --- a/drivers/staging/media/go7007/Makefile +++ b/drivers/staging/media/go7007/Makefile @@ -1,18 +1,7 @@ -#obj-m += go7007.o go7007-usb.o snd-go7007.o wis-saa7115.o wis-tw9903.o \ - wis-uda1342.o wis-sony-tuner.o wis-saa7113.o wis-ov7640.o \ - wis-tw2804.o - - obj-$(CONFIG_VIDEO_GO7007) += go7007.o obj-$(CONFIG_VIDEO_GO7007_USB) += go7007-usb.o -obj-$(CONFIG_VIDEO_GO7007_USB_S2250_BOARD) += s2250.o s2250-loader.o -obj-$(CONFIG_VIDEO_GO7007_SAA7113) += wis-saa7113.o -obj-$(CONFIG_VIDEO_GO7007_OV7640) += wis-ov7640.o -obj-$(CONFIG_VIDEO_GO7007_SAA7115) += wis-saa7115.o -obj-$(CONFIG_VIDEO_GO7007_TW9903) += wis-tw9903.o -obj-$(CONFIG_VIDEO_GO7007_UDA1342) += wis-uda1342.o -obj-$(CONFIG_VIDEO_GO7007_SONY_TUNER) += wis-sony-tuner.o -obj-$(CONFIG_VIDEO_GO7007_TW2804) += wis-tw2804.o +obj-$(CONFIG_VIDEO_GO7007_LOADER) += go7007-loader.o +obj-$(CONFIG_VIDEO_GO7007_USB_S2250_BOARD) += s2250.o go7007-y := go7007-v4l2.o go7007-driver.o go7007-i2c.o go7007-fw.o \ snd-go7007.o @@ -21,10 +10,6 @@ s2250-y := s2250-board.o # Uncomment when the saa7134 patches get into upstream #obj-$(CONFIG_VIDEO_SAA7134) += saa7134-go7007.o -#ccflags-$(CONFIG_VIDEO_SAA7134:m=y) += -Idrivers/media/video/saa7134 -DSAA7134_MPEG_GO7007=3 - -# S2250 needs cypress ezusb loader from dvb-usb -ccflags-$(CONFIG_VIDEO_GO7007_USB_S2250_BOARD:m=y) += -Idrivers/media/usb/dvb-usb +#ccflags-$(CONFIG_VIDEO_SAA7134:m=y) += -Idrivers/media/pci/saa7134 -ccflags-y += -Idrivers/media/dvb-frontends -ccflags-y += -Idrivers/media/dvb-core +ccflags-$(CONFIG_VIDEO_GO7007_LOADER:m=y) += -Idrivers/media/common diff --git a/drivers/staging/media/go7007/README b/drivers/staging/media/go7007/README index aeba1324a9c5..3af0d9062811 100644 --- a/drivers/staging/media/go7007/README +++ b/drivers/staging/media/go7007/README @@ -1,11 +1,137 @@ Todo: - - checkpatch.pl cleanups - - sparse cleanups - - lots of little modules, should be merged together - and added to the build. - - testing? - - handle churn in v4l layer. + - create an API for motion detection + - let s2250-board use i2c subdevs as well instead of hardcoding + support for the i2c devices. + - when the driver is moved out of staging, support for saa7134-go7007 + should be added to the saa7134 driver. The patch for that is + included below. -Please send patches to Greg Kroah-Hartman <greg@linuxfoundation.org> and Cc: Ross -Cohen <rcohen@snurgle.org> as well. +Patch for saa7134: +diff --git a/drivers/media/pci/saa7134/saa7134-cards.c b/drivers/media/pci/saa7134/saa7134-cards.c +index dc68cf1..9a53794 100644 +--- a/drivers/media/pci/saa7134/saa7134-cards.c ++++ b/drivers/media/pci/saa7134/saa7134-cards.c +@@ -5790,6 +5790,29 @@ struct saa7134_board saa7134_boards[] = { + .gpio = 0x6010000, + } }, + }, ++ [SAA7134_BOARD_WIS_VOYAGER] = { ++ .name = "WIS Voyager or compatible", ++ .audio_clock = 0x00200000, ++ .tuner_type = TUNER_PHILIPS_TDA8290, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .mpeg = SAA7134_MPEG_GO7007, ++ .inputs = { { ++ .name = name_comp1, ++ .vmux = 0, ++ .amux = LINE2, ++ }, { ++ .name = name_tv, ++ .vmux = 3, ++ .amux = TV, ++ .tv = 1, ++ }, { ++ .name = name_svideo, ++ .vmux = 6, ++ .amux = LINE1, ++ } }, ++ }, + + }; + +@@ -7037,6 +7060,12 @@ struct pci_device_id saa7134_pci_tbl[] = { + .subdevice = 0x0911, + .driver_data = SAA7134_BOARD_SENSORAY811_911, + }, { ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x1905, /* WIS */ ++ .subdevice = 0x7007, ++ .driver_data = SAA7134_BOARD_WIS_VOYAGER, ++ }, { + /* --- boards without eeprom + subsystem ID --- */ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, +diff --git a/drivers/media/pci/saa7134/saa7134-core.c b/drivers/media/pci/saa7134/saa7134-core.c +index 8fd24e7..0a849ea 100644 +--- a/drivers/media/pci/saa7134/saa7134-core.c ++++ b/drivers/media/pci/saa7134/saa7134-core.c +@@ -156,6 +156,8 @@ static void request_module_async(struct work_struct *work){ + request_module("saa7134-empress"); + if (card_is_dvb(dev)) + request_module("saa7134-dvb"); ++ if (card_is_go7007(dev)) ++ request_module("saa7134-go7007"); + if (alsa) { + if (dev->pci->device != PCI_DEVICE_ID_PHILIPS_SAA7130) + request_module("saa7134-alsa"); +@@ -557,8 +559,12 @@ static irqreturn_t saa7134_irq(int irq, void *dev_id) + saa7134_irq_vbi_done(dev,status); + + if ((report & SAA7134_IRQ_REPORT_DONE_RA2) && +- card_has_mpeg(dev)) +- saa7134_irq_ts_done(dev,status); ++ card_has_mpeg(dev)) { ++ if (dev->mops->irq_ts_done != NULL) ++ dev->mops->irq_ts_done(dev, status); ++ else ++ saa7134_irq_ts_done(dev, status); ++ } + + if (report & SAA7134_IRQ_REPORT_GPIO16) { + switch (dev->has_remote) { +diff --git a/drivers/media/pci/saa7134/saa7134.h b/drivers/media/pci/saa7134/saa7134.h +index 62169dd..5fad39a 100644 +--- a/drivers/media/pci/saa7134/saa7134.h ++++ b/drivers/media/pci/saa7134/saa7134.h +@@ -334,6 +334,7 @@ struct saa7134_card_ir { + #define SAA7134_BOARD_KWORLD_PC150U 189 + #define SAA7134_BOARD_ASUSTeK_PS3_100 190 + #define SAA7134_BOARD_HAWELL_HW_9004V1 191 ++#define SAA7134_BOARD_WIS_VOYAGER 192 + + #define SAA7134_MAXBOARDS 32 + #define SAA7134_INPUT_MAX 8 +@@ -364,6 +365,7 @@ enum saa7134_mpeg_type { + SAA7134_MPEG_UNUSED, + SAA7134_MPEG_EMPRESS, + SAA7134_MPEG_DVB, ++ SAA7134_MPEG_GO7007, + }; + + enum saa7134_mpeg_ts_type { +@@ -403,6 +405,7 @@ struct saa7134_board { + #define card_has_radio(dev) (NULL != saa7134_boards[dev->board].radio.name) + #define card_is_empress(dev) (SAA7134_MPEG_EMPRESS == saa7134_boards[dev->board].mpeg) + #define card_is_dvb(dev) (SAA7134_MPEG_DVB == saa7134_boards[dev->board].mpeg) ++#define card_is_go7007(dev) (SAA7134_MPEG_GO7007 == saa7134_boards[dev->board].mpeg) + #define card_has_mpeg(dev) (SAA7134_MPEG_UNUSED != saa7134_boards[dev->board].mpeg) + #define card(dev) (saa7134_boards[dev->board]) + #define card_in(dev,n) (saa7134_boards[dev->board].inputs[n]) +@@ -535,6 +538,8 @@ struct saa7134_mpeg_ops { + int (*init)(struct saa7134_dev *dev); + int (*fini)(struct saa7134_dev *dev); + void (*signal_change)(struct saa7134_dev *dev); ++ void (*irq_ts_done)(struct saa7134_dev *dev, ++ unsigned long status); + }; + + /* global device status */ +diff --git a/drivers/staging/media/go7007/Makefile b/drivers/staging/media/go7007/Makefile +index 9c6ad4a..1b23689 100644 +--- a/drivers/staging/media/go7007/Makefile ++++ b/drivers/staging/media/go7007/Makefile +@@ -8,8 +8,7 @@ go7007-y := go7007-v4l2.o go7007-driver.o go7007-i2c.o go7007-fw.o \ + + s2250-y := s2250-board.o + +-# Uncomment when the saa7134 patches get into upstream +-#obj-$(CONFIG_VIDEO_SAA7134) += saa7134-go7007.o +-#ccflags-$(CONFIG_VIDEO_SAA7134:m=y) += -Idrivers/media/pci/saa7134 ++obj-$(CONFIG_VIDEO_SAA7134) += saa7134-go7007.o ++ccflags-$(CONFIG_VIDEO_SAA7134:m=y) += -Idrivers/media/pci/saa7134 + + ccflags-$(CONFIG_VIDEO_GO7007_LOADER:m=y) += -Idrivers/media/common diff --git a/drivers/staging/media/go7007/go7007-driver.c b/drivers/staging/media/go7007/go7007-driver.c index 66950916df23..a5ca99d7c0b2 100644 --- a/drivers/staging/media/go7007/go7007-driver.c +++ b/drivers/staging/media/go7007/go7007-driver.c @@ -35,7 +35,6 @@ #include <media/v4l2-common.h> #include "go7007-priv.h" -#include "wis-i2c.h" /* * Wait for an interrupt to be delivered from the GO7007SB and return @@ -91,43 +90,43 @@ EXPORT_SYMBOL(go7007_read_addr); static int go7007_load_encoder(struct go7007 *go) { const struct firmware *fw_entry; - char fw_name[] = "go7007fw.bin"; + char fw_name[] = "go7007/go7007fw.bin"; void *bounce; int fw_len, rv = 0; u16 intr_val, intr_data; - if (request_firmware(&fw_entry, fw_name, go->dev)) { - v4l2_err(go, "unable to load firmware from file " - "\"%s\"\n", fw_name); - return -1; - } - if (fw_entry->size < 16 || memcmp(fw_entry->data, "WISGO7007FW", 11)) { - v4l2_err(go, "file \"%s\" does not appear to be " - "go7007 firmware\n", fw_name); - release_firmware(fw_entry); - return -1; - } - fw_len = fw_entry->size - 16; - bounce = kmemdup(fw_entry->data + 16, fw_len, GFP_KERNEL); - if (bounce == NULL) { - v4l2_err(go, "unable to allocate %d bytes for " - "firmware transfer\n", fw_len); + if (go->boot_fw == NULL) { + if (request_firmware(&fw_entry, fw_name, go->dev)) { + v4l2_err(go, "unable to load firmware from file \"%s\"\n", fw_name); + return -1; + } + if (fw_entry->size < 16 || memcmp(fw_entry->data, "WISGO7007FW", 11)) { + v4l2_err(go, "file \"%s\" does not appear to be go7007 firmware\n", fw_name); + release_firmware(fw_entry); + return -1; + } + fw_len = fw_entry->size - 16; + bounce = kmemdup(fw_entry->data + 16, fw_len, GFP_KERNEL); + if (bounce == NULL) { + v4l2_err(go, "unable to allocate %d bytes for firmware transfer\n", fw_len); + release_firmware(fw_entry); + return -1; + } release_firmware(fw_entry); - return -1; + go->boot_fw_len = fw_len; + go->boot_fw = bounce; } - release_firmware(fw_entry); if (go7007_interface_reset(go) < 0 || - go7007_send_firmware(go, bounce, fw_len) < 0 || - go7007_read_interrupt(go, &intr_val, &intr_data) < 0 || + go7007_send_firmware(go, go->boot_fw, go->boot_fw_len) < 0 || + go7007_read_interrupt(go, &intr_val, &intr_data) < 0 || (intr_val & ~0x1) != 0x5a5a) { v4l2_err(go, "error transferring firmware\n"); rv = -1; } - kfree(bounce); return rv; } -MODULE_FIRMWARE("go7007fw.bin"); +MODULE_FIRMWARE("go7007/go7007fw.bin"); /* * Boot the encoder and register the I2C adapter if requested. Do the @@ -167,15 +166,24 @@ static int go7007_init_encoder(struct go7007 *go) go7007_write_addr(go, 0x1000, 0x0811); go7007_write_addr(go, 0x1000, 0x0c11); } - if (go->board_id == GO7007_BOARDID_MATRIX_REV) { + switch (go->board_id) { + case GO7007_BOARDID_MATRIX_REV: /* Set GPIO pin 0 to be an output (audio clock control) */ go7007_write_addr(go, 0x3c82, 0x0001); go7007_write_addr(go, 0x3c80, 0x00fe); - } - if (go->board_id == GO7007_BOARDID_ADLINK_MPG24) { + break; + case GO7007_BOARDID_ADLINK_MPG24: /* set GPIO5 to be an output, currently low */ go7007_write_addr(go, 0x3c82, 0x0000); go7007_write_addr(go, 0x3c80, 0x00df); + break; + case GO7007_BOARDID_ADS_USBAV_709: + /* GPIO pin 0: audio clock control */ + /* pin 2: TW9906 reset */ + /* pin 3: capture LED */ + go7007_write_addr(go, 0x3c82, 0x000d); + go7007_write_addr(go, 0x3c80, 0x00f2); + break; } return 0; } @@ -196,18 +204,54 @@ int go7007_reset_encoder(struct go7007 *go) /* * Attempt to instantiate an I2C client by ID, probably loading a module. */ -static int init_i2c_module(struct i2c_adapter *adapter, const char *type, - int addr) +static int init_i2c_module(struct i2c_adapter *adapter, const struct go_i2c *const i2c) { struct go7007 *go = i2c_get_adapdata(adapter); struct v4l2_device *v4l2_dev = &go->v4l2_dev; - - if (v4l2_i2c_new_subdev(v4l2_dev, adapter, type, addr, NULL)) + struct v4l2_subdev *sd; + struct i2c_board_info info; + + memset(&info, 0, sizeof(info)); + strlcpy(info.type, i2c->type, sizeof(info.type)); + info.addr = i2c->addr; + info.flags = i2c->flags; + + sd = v4l2_i2c_new_subdev_board(v4l2_dev, adapter, &info, NULL); + if (sd) { + if (i2c->is_video) + go->sd_video = sd; + if (i2c->is_audio) + go->sd_audio = sd; return 0; + } - dev_info(&adapter->dev, - "go7007: probing for module i2c:%s failed\n", type); - return -1; + printk(KERN_INFO "go7007: probing for module i2c:%s failed\n", i2c->type); + return -EINVAL; +} + +/* + * Detach and unregister the encoder. The go7007 struct won't be freed + * until v4l2 finishes releasing its resources and all associated fds are + * closed by applications. + */ +static void go7007_remove(struct v4l2_device *v4l2_dev) +{ + struct go7007 *go = container_of(v4l2_dev, struct go7007, v4l2_dev); + + v4l2_device_unregister(v4l2_dev); + if (go->hpi_ops->release) + go->hpi_ops->release(go); + if (go->i2c_adapter_online) { + if (i2c_del_adapter(&go->i2c_adapter) == 0) + go->i2c_adapter_online = 0; + else + v4l2_err(&go->v4l2_dev, + "error removing I2C adapter!\n"); + } + + kfree(go->boot_fw); + go7007_v4l2_remove(go); + kfree(go); } /* @@ -218,38 +262,63 @@ static int init_i2c_module(struct i2c_adapter *adapter, const char *type, * * Must NOT be called with the hw_lock held. */ -int go7007_register_encoder(struct go7007 *go) +int go7007_register_encoder(struct go7007 *go, unsigned num_i2c_devs) { int i, ret; dev_info(go->dev, "go7007: registering new %s\n", go->name); + go->v4l2_dev.release = go7007_remove; + ret = v4l2_device_register(go->dev, &go->v4l2_dev); + if (ret < 0) + return ret; + mutex_lock(&go->hw_lock); ret = go7007_init_encoder(go); mutex_unlock(&go->hw_lock); if (ret < 0) - return -1; + return ret; - /* v4l2 init must happen before i2c subdevs */ - ret = go7007_v4l2_init(go); + ret = go7007_v4l2_ctrl_init(go); if (ret < 0) return ret; if (!go->i2c_adapter_online && go->board_info->flags & GO7007_BOARD_USE_ONBOARD_I2C) { - if (go7007_i2c_init(go) < 0) - return -1; + ret = go7007_i2c_init(go); + if (ret < 0) + return ret; go->i2c_adapter_online = 1; } if (go->i2c_adapter_online) { - for (i = 0; i < go->board_info->num_i2c_devs; ++i) - init_i2c_module(&go->i2c_adapter, - go->board_info->i2c_devs[i].type, - go->board_info->i2c_devs[i].addr); + if (go->board_id == GO7007_BOARDID_ADS_USBAV_709) { + /* Reset the TW9906 */ + go7007_write_addr(go, 0x3c82, 0x0009); + msleep(50); + go7007_write_addr(go, 0x3c82, 0x000d); + } + for (i = 0; i < num_i2c_devs; ++i) + init_i2c_module(&go->i2c_adapter, &go->board_info->i2c_devs[i]); + + if (go->tuner_type >= 0) { + struct tuner_setup setup = { + .addr = ADDR_UNSET, + .type = go->tuner_type, + .mode_mask = T_ANALOG_TV, + }; + + v4l2_device_call_all(&go->v4l2_dev, 0, tuner, + s_type_addr, &setup); + } if (go->board_id == GO7007_BOARDID_ADLINK_MPG24) - i2c_clients_command(&go->i2c_adapter, - DECODER_SET_CHANNEL, &go->channel_number); + v4l2_subdev_call(go->sd_video, video, s_routing, + 0, 0, go->channel_number + 1); } + + ret = go7007_v4l2_init(go); + if (ret < 0) + return ret; + if (go->board_info->flags & GO7007_BOARD_HAS_AUDIO) { go->audio_enabled = 1; go7007_snd_init(go); @@ -309,48 +378,54 @@ start_error: /* * Store a byte in the current video buffer, if there is one. */ -static inline void store_byte(struct go7007_buffer *gobuf, u8 byte) +static inline void store_byte(struct go7007_buffer *vb, u8 byte) { - if (gobuf != NULL && gobuf->bytesused < GO7007_BUF_SIZE) { - unsigned int pgidx = gobuf->offset >> PAGE_SHIFT; - unsigned int pgoff = gobuf->offset & ~PAGE_MASK; + if (vb && vb->vb.v4l2_planes[0].bytesused < GO7007_BUF_SIZE) { + u8 *ptr = vb2_plane_vaddr(&vb->vb, 0); - *((u8 *)page_address(gobuf->pages[pgidx]) + pgoff) = byte; - ++gobuf->offset; - ++gobuf->bytesused; + ptr[vb->vb.v4l2_planes[0].bytesused++] = byte; } } /* * Deliver the last video buffer and get a new one to start writing to. */ -static void frame_boundary(struct go7007 *go) +static struct go7007_buffer *frame_boundary(struct go7007 *go, struct go7007_buffer *vb) { - struct go7007_buffer *gobuf; + struct go7007_buffer *vb_tmp = NULL; + u32 *bytesused = &vb->vb.v4l2_planes[0].bytesused; int i; - if (go->active_buf) { - if (go->active_buf->modet_active) { - if (go->active_buf->bytesused + 216 < GO7007_BUF_SIZE) { + if (vb) { + if (vb->modet_active) { + if (*bytesused + 216 < GO7007_BUF_SIZE) { for (i = 0; i < 216; ++i) - store_byte(go->active_buf, - go->active_map[i]); - go->active_buf->bytesused -= 216; + store_byte(vb, go->active_map[i]); + *bytesused -= 216; } else - go->active_buf->modet_active = 0; + vb->modet_active = 0; } - go->active_buf->state = BUF_STATE_DONE; - wake_up_interruptible(&go->frame_waitq); - go->active_buf = NULL; + vb->vb.v4l2_buf.sequence = go->next_seq++; + v4l2_get_timestamp(&vb->vb.v4l2_buf.timestamp); + vb_tmp = vb; + spin_lock(&go->spinlock); + list_del(&vb->list); + if (list_empty(&go->vidq_active)) + vb = NULL; + else + vb = list_first_entry(&go->vidq_active, struct go7007_buffer, list); + go->active_buf = vb; + spin_unlock(&go->spinlock); + vb2_buffer_done(&vb_tmp->vb, VB2_BUF_STATE_DONE); + return vb; } - list_for_each_entry(gobuf, &go->stream, stream) - if (gobuf->state == BUF_STATE_QUEUED) { - gobuf->seq = go->next_seq; - do_gettimeofday(&gobuf->timestamp); - go->active_buf = gobuf; - break; - } - ++go->next_seq; + spin_lock(&go->spinlock); + if (!list_empty(&go->vidq_active)) + vb = go->active_buf = + list_first_entry(&go->vidq_active, struct go7007_buffer, list); + spin_unlock(&go->spinlock); + go->next_seq++; + return vb; } static void write_bitmap_word(struct go7007 *go) @@ -374,30 +449,30 @@ static void write_bitmap_word(struct go7007 *go) */ void go7007_parse_video_stream(struct go7007 *go, u8 *buf, int length) { - int i, seq_start_code = -1, frame_start_code = -1; - - spin_lock(&go->spinlock); + struct go7007_buffer *vb = go->active_buf; + int i, seq_start_code = -1, gop_start_code = -1, frame_start_code = -1; switch (go->format) { - case GO7007_FORMAT_MPEG4: + case V4L2_PIX_FMT_MPEG4: seq_start_code = 0xB0; + gop_start_code = 0xB3; frame_start_code = 0xB6; break; - case GO7007_FORMAT_MPEG1: - case GO7007_FORMAT_MPEG2: + case V4L2_PIX_FMT_MPEG1: + case V4L2_PIX_FMT_MPEG2: seq_start_code = 0xB3; + gop_start_code = 0xB8; frame_start_code = 0x00; break; } for (i = 0; i < length; ++i) { - if (go->active_buf != NULL && - go->active_buf->bytesused >= GO7007_BUF_SIZE - 3) { + if (vb && vb->vb.v4l2_planes[0].bytesused >= GO7007_BUF_SIZE - 3) { v4l2_info(&go->v4l2_dev, "dropping oversized frame\n"); - go->active_buf->offset -= go->active_buf->bytesused; - go->active_buf->bytesused = 0; - go->active_buf->modet_active = 0; - go->active_buf = NULL; + vb->vb.v4l2_planes[0].bytesused = 0; + vb->frame_offset = 0; + vb->modet_active = 0; + vb = go->active_buf = NULL; } switch (go->state) { @@ -410,7 +485,7 @@ void go7007_parse_video_stream(struct go7007 *go, u8 *buf, int length) go->state = STATE_FF; break; default: - store_byte(go->active_buf, buf[i]); + store_byte(vb, buf[i]); break; } break; @@ -420,12 +495,12 @@ void go7007_parse_video_stream(struct go7007 *go, u8 *buf, int length) go->state = STATE_00_00; break; case 0xFF: - store_byte(go->active_buf, 0x00); + store_byte(vb, 0x00); go->state = STATE_FF; break; default: - store_byte(go->active_buf, 0x00); - store_byte(go->active_buf, buf[i]); + store_byte(vb, 0x00); + store_byte(vb, buf[i]); go->state = STATE_DATA; break; } @@ -433,21 +508,21 @@ void go7007_parse_video_stream(struct go7007 *go, u8 *buf, int length) case STATE_00_00: switch (buf[i]) { case 0x00: - store_byte(go->active_buf, 0x00); + store_byte(vb, 0x00); /* go->state remains STATE_00_00 */ break; case 0x01: go->state = STATE_00_00_01; break; case 0xFF: - store_byte(go->active_buf, 0x00); - store_byte(go->active_buf, 0x00); + store_byte(vb, 0x00); + store_byte(vb, 0x00); go->state = STATE_FF; break; default: - store_byte(go->active_buf, 0x00); - store_byte(go->active_buf, 0x00); - store_byte(go->active_buf, buf[i]); + store_byte(vb, 0x00); + store_byte(vb, 0x00); + store_byte(vb, buf[i]); go->state = STATE_DATA; break; } @@ -455,31 +530,26 @@ void go7007_parse_video_stream(struct go7007 *go, u8 *buf, int length) case STATE_00_00_01: if (buf[i] == 0xF8 && go->modet_enable == 0) { /* MODET start code, but MODET not enabled */ - store_byte(go->active_buf, 0x00); - store_byte(go->active_buf, 0x00); - store_byte(go->active_buf, 0x01); - store_byte(go->active_buf, 0xF8); + store_byte(vb, 0x00); + store_byte(vb, 0x00); + store_byte(vb, 0x01); + store_byte(vb, 0xF8); go->state = STATE_DATA; break; } /* If this is the start of a new MPEG frame, * get a new buffer */ - if ((go->format == GO7007_FORMAT_MPEG1 || - go->format == GO7007_FORMAT_MPEG2 || - go->format == GO7007_FORMAT_MPEG4) && - (buf[i] == seq_start_code || - buf[i] == 0xB8 || /* GOP code */ - buf[i] == frame_start_code)) { - if (go->active_buf == NULL || go->seen_frame) - frame_boundary(go); - if (buf[i] == frame_start_code) { - if (go->active_buf != NULL) - go->active_buf->frame_offset = - go->active_buf->offset; - go->seen_frame = 1; - } else { - go->seen_frame = 0; - } + if ((go->format == V4L2_PIX_FMT_MPEG1 || + go->format == V4L2_PIX_FMT_MPEG2 || + go->format == V4L2_PIX_FMT_MPEG4) && + (buf[i] == seq_start_code || + buf[i] == gop_start_code || + buf[i] == frame_start_code)) { + if (vb == NULL || go->seen_frame) + vb = frame_boundary(go, vb); + go->seen_frame = buf[i] == frame_start_code; + if (vb && go->seen_frame) + vb->frame_offset = vb->vb.v4l2_planes[0].bytesused; } /* Handle any special chunk types, or just write the * start code to the (potentially new) buffer */ @@ -498,16 +568,16 @@ void go7007_parse_video_stream(struct go7007 *go, u8 *buf, int length) go->state = STATE_MODET_MAP; break; case 0xFF: /* Potential JPEG start code */ - store_byte(go->active_buf, 0x00); - store_byte(go->active_buf, 0x00); - store_byte(go->active_buf, 0x01); + store_byte(vb, 0x00); + store_byte(vb, 0x00); + store_byte(vb, 0x01); go->state = STATE_FF; break; default: - store_byte(go->active_buf, 0x00); - store_byte(go->active_buf, 0x00); - store_byte(go->active_buf, 0x01); - store_byte(go->active_buf, buf[i]); + store_byte(vb, 0x00); + store_byte(vb, 0x00); + store_byte(vb, 0x01); + store_byte(vb, buf[i]); go->state = STATE_DATA; break; } @@ -515,20 +585,20 @@ void go7007_parse_video_stream(struct go7007 *go, u8 *buf, int length) case STATE_FF: switch (buf[i]) { case 0x00: - store_byte(go->active_buf, 0xFF); + store_byte(vb, 0xFF); go->state = STATE_00; break; case 0xFF: - store_byte(go->active_buf, 0xFF); + store_byte(vb, 0xFF); /* go->state remains STATE_FF */ break; case 0xD8: - if (go->format == GO7007_FORMAT_MJPEG) - frame_boundary(go); + if (go->format == V4L2_PIX_FMT_MJPEG) + vb = frame_boundary(go, vb); /* fall through */ default: - store_byte(go->active_buf, 0xFF); - store_byte(go->active_buf, buf[i]); + store_byte(vb, 0xFF); + store_byte(vb, buf[i]); go->state = STATE_DATA; break; } @@ -551,8 +621,8 @@ void go7007_parse_video_stream(struct go7007 *go, u8 *buf, int length) write_bitmap_word(go); } else go->modet_word = buf[i] << 8; - } else if (go->parse_length == 207 && go->active_buf) { - go->active_buf->modet_active = buf[i]; + } else if (go->parse_length == 207 && vb) { + vb->modet_active = buf[i]; } if (++go->parse_length == 208) go->state = STATE_DATA; @@ -563,15 +633,14 @@ void go7007_parse_video_stream(struct go7007 *go, u8 *buf, int length) break; } } - - spin_unlock(&go->spinlock); } EXPORT_SYMBOL(go7007_parse_video_stream); /* * Allocate a new go7007 struct. Used by the hardware-specific probe. */ -struct go7007 *go7007_alloc(struct go7007_board_info *board, struct device *dev) +struct go7007 *go7007_alloc(const struct go7007_board_info *board, + struct device *dev) { struct go7007 *go; int i; @@ -588,33 +657,17 @@ struct go7007 *go7007_alloc(struct go7007_board_info *board, struct device *dev) mutex_init(&go->hw_lock); init_waitqueue_head(&go->frame_waitq); spin_lock_init(&go->spinlock); - go->video_dev = NULL; - go->ref_count = 0; go->status = STATUS_INIT; memset(&go->i2c_adapter, 0, sizeof(go->i2c_adapter)); go->i2c_adapter_online = 0; go->interrupt_available = 0; init_waitqueue_head(&go->interrupt_waitq); - go->in_use = 0; go->input = 0; - if (board->sensor_flags & GO7007_SENSOR_TV) { - go->standard = GO7007_STD_NTSC; - go->width = 720; - go->height = 480; - go->sensor_framerate = 30000; - } else { - go->standard = GO7007_STD_OTHER; - go->width = board->sensor_width; - go->height = board->sensor_height; - go->sensor_framerate = board->sensor_framerate; - } - go->encoder_v_offset = board->sensor_v_offset; - go->encoder_h_offset = board->sensor_h_offset; + go7007_update_board(go); go->encoder_h_halve = 0; go->encoder_v_halve = 0; go->encoder_subsample = 0; - go->streaming = 0; - go->format = GO7007_FORMAT_MJPEG; + go->format = V4L2_PIX_FMT_MJPEG; go->bitrate = 1500000; go->fps_scale = 1; go->pali = 0; @@ -633,31 +686,30 @@ struct go7007 *go7007_alloc(struct go7007_board_info *board, struct device *dev) go->modet_map[i] = 0; go->audio_deliver = NULL; go->audio_enabled = 0; - INIT_LIST_HEAD(&go->stream); return go; } EXPORT_SYMBOL(go7007_alloc); -/* - * Detach and unregister the encoder. The go7007 struct won't be freed - * until v4l2 finishes releasing its resources and all associated fds are - * closed by applications. - */ -void go7007_remove(struct go7007 *go) +void go7007_update_board(struct go7007 *go) { - if (go->i2c_adapter_online) { - if (i2c_del_adapter(&go->i2c_adapter) == 0) - go->i2c_adapter_online = 0; - else - v4l2_err(&go->v4l2_dev, - "error removing I2C adapter!\n"); - } + const struct go7007_board_info *board = go->board_info; - if (go->audio_enabled) - go7007_snd_remove(go); - go7007_v4l2_remove(go); + if (board->sensor_flags & GO7007_SENSOR_TV) { + go->standard = GO7007_STD_NTSC; + go->std = V4L2_STD_NTSC_M; + go->width = 720; + go->height = 480; + go->sensor_framerate = 30000; + } else { + go->standard = GO7007_STD_OTHER; + go->width = board->sensor_width; + go->height = board->sensor_height; + go->sensor_framerate = board->sensor_framerate; + } + go->encoder_v_offset = board->sensor_v_offset; + go->encoder_h_offset = board->sensor_h_offset; } -EXPORT_SYMBOL(go7007_remove); +EXPORT_SYMBOL(go7007_update_board); MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/go7007/go7007-fw.c b/drivers/staging/media/go7007/go7007-fw.c index a5ede1c109d0..c2d0e58afc34 100644 --- a/drivers/staging/media/go7007/go7007-fw.c +++ b/drivers/staging/media/go7007/go7007-fw.c @@ -36,6 +36,8 @@ #include "go7007-priv.h" +#define GO7007_FW_NAME "go7007/go7007tv.bin" + /* Constants used in the source firmware image to describe code segments */ #define FLAG_MODE_MJPEG (1) @@ -455,9 +457,9 @@ static int mpeg1_frame_header(struct go7007 *go, unsigned char *buf, CODE_ADD(c, frame == PFRAME ? 0x2 : 0x3, 13); CODE_ADD(c, 0xffff, 16); - CODE_ADD(c, go->format == GO7007_FORMAT_MPEG2 ? 0x7 : 0x4, 4); + CODE_ADD(c, go->format == V4L2_PIX_FMT_MPEG2 ? 0x7 : 0x4, 4); if (frame != PFRAME) - CODE_ADD(c, go->format == GO7007_FORMAT_MPEG2 ? 0x7 : 0x4, 4); + CODE_ADD(c, go->format == V4L2_PIX_FMT_MPEG2 ? 0x7 : 0x4, 4); else CODE_ADD(c, 0, 4); /* Is this supposed to be here?? */ CODE_ADD(c, 0, 3); /* What is this?? */ @@ -466,7 +468,7 @@ static int mpeg1_frame_header(struct go7007 *go, unsigned char *buf, if (j != 8) CODE_ADD(c, 0, j); - if (go->format == GO7007_FORMAT_MPEG2) { + if (go->format == V4L2_PIX_FMT_MPEG2) { CODE_ADD(c, 0x1, 24); CODE_ADD(c, 0xb5, 8); CODE_ADD(c, 0x844, 12); @@ -537,7 +539,7 @@ static int mpeg1_sequence_header(struct go7007 *go, unsigned char *buf, int ext) int i, aspect_ratio, picture_rate; CODE_GEN(c, buf + 6); - if (go->format == GO7007_FORMAT_MPEG1) { + if (go->format == V4L2_PIX_FMT_MPEG1) { switch (go->aspect_ratio) { case GO7007_RATIO_4_3: aspect_ratio = go->standard == GO7007_STD_NTSC ? 3 : 2; @@ -587,9 +589,9 @@ static int mpeg1_sequence_header(struct go7007 *go, unsigned char *buf, int ext) CODE_ADD(c, go->height, 12); CODE_ADD(c, aspect_ratio, 4); CODE_ADD(c, picture_rate, 4); - CODE_ADD(c, go->format == GO7007_FORMAT_MPEG2 ? 20000 : 0x3ffff, 18); + CODE_ADD(c, go->format == V4L2_PIX_FMT_MPEG2 ? 20000 : 0x3ffff, 18); CODE_ADD(c, 1, 1); - CODE_ADD(c, go->format == GO7007_FORMAT_MPEG2 ? 112 : 20, 10); + CODE_ADD(c, go->format == V4L2_PIX_FMT_MPEG2 ? 112 : 20, 10); CODE_ADD(c, 0, 3); /* Byte-align with zeros */ @@ -597,7 +599,7 @@ static int mpeg1_sequence_header(struct go7007 *go, unsigned char *buf, int ext) if (i != 8) CODE_ADD(c, 0, i); - if (go->format == GO7007_FORMAT_MPEG2) { + if (go->format == V4L2_PIX_FMT_MPEG2) { CODE_ADD(c, 0x1, 24); CODE_ADD(c, 0xb5, 8); CODE_ADD(c, 0x148, 12); @@ -930,10 +932,10 @@ static int brctrl_to_package(struct go7007 *go, __le16 *code, int space, int *framelen) { int converge_speed = 0; - int lambda = (go->format == GO7007_FORMAT_MJPEG || go->dvd_mode) ? + int lambda = (go->format == V4L2_PIX_FMT_MJPEG || go->dvd_mode) ? 100 : 0; int peak_rate = 6 * go->bitrate / 5; - int vbv_buffer = go->format == GO7007_FORMAT_MJPEG ? + int vbv_buffer = go->format == V4L2_PIX_FMT_MJPEG ? go->bitrate : (go->dvd_mode ? 900000 : peak_rate); int fps = go->sensor_framerate / go->fps_scale; @@ -1096,10 +1098,10 @@ static int config_package(struct go7007 *go, __le16 *code, int space) 0xc003, 0x28b4, 0xc004, 0x3c5a, 0xdc05, 0x2a77, - 0xc6c3, go->format == GO7007_FORMAT_MPEG4 ? 0 : - (go->format == GO7007_FORMAT_H263 ? 0 : 1), - 0xc680, go->format == GO7007_FORMAT_MPEG4 ? 0xf1 : - (go->format == GO7007_FORMAT_H263 ? 0x61 : + 0xc6c3, go->format == V4L2_PIX_FMT_MPEG4 ? 0 : + (go->format == V4L2_PIX_FMT_H263 ? 0 : 1), + 0xc680, go->format == V4L2_PIX_FMT_MPEG4 ? 0xf1 : + (go->format == V4L2_PIX_FMT_H263 ? 0x61 : 0xd3), 0xc780, 0x0140, 0xe009, 0x0001, @@ -1123,15 +1125,15 @@ static int config_package(struct go7007 *go, __le16 *code, int space) (!go->interlace_coding) ? 0x0008 : 0x0009, 0xc404, go->interlace_coding ? 0x44 : - (go->format == GO7007_FORMAT_MPEG4 ? 0x11 : - (go->format == GO7007_FORMAT_MPEG1 ? 0x02 : - (go->format == GO7007_FORMAT_MPEG2 ? 0x04 : - (go->format == GO7007_FORMAT_H263 ? 0x08 : + (go->format == V4L2_PIX_FMT_MPEG4 ? 0x11 : + (go->format == V4L2_PIX_FMT_MPEG1 ? 0x02 : + (go->format == V4L2_PIX_FMT_MPEG2 ? 0x04 : + (go->format == V4L2_PIX_FMT_H263 ? 0x08 : 0x20)))), - 0xbf0a, (go->format == GO7007_FORMAT_MPEG4 ? 8 : - (go->format == GO7007_FORMAT_MPEG1 ? 1 : - (go->format == GO7007_FORMAT_MPEG2 ? 2 : - (go->format == GO7007_FORMAT_H263 ? 4 : 16)))) | + 0xbf0a, (go->format == V4L2_PIX_FMT_MPEG4 ? 8 : + (go->format == V4L2_PIX_FMT_MPEG1 ? 1 : + (go->format == V4L2_PIX_FMT_MPEG2 ? 2 : + (go->format == V4L2_PIX_FMT_H263 ? 4 : 16)))) | ((go->repeat_seqhead ? 1 : 0) << 6) | ((go->dvd_mode ? 1 : 0) << 9) | ((go->gop_header_enable ? 1 : 0) << 10), @@ -1348,19 +1350,19 @@ static int final_package(struct go7007 *go, __le16 *code, int space) 0x41, go->ipb ? 0xd4c : 0x36b, (rows << 8) | (go->width >> 4), - go->format == GO7007_FORMAT_MPEG4 ? 0x0404 : 0, + go->format == V4L2_PIX_FMT_MPEG4 ? 0x0404 : 0, (1 << 15) | ((go->interlace_coding ? 1 : 0) << 13) | ((go->closed_gop ? 1 : 0) << 12) | - ((go->format == GO7007_FORMAT_MPEG4 ? 1 : 0) << 11) | + ((go->format == V4L2_PIX_FMT_MPEG4 ? 1 : 0) << 11) | /* (1 << 9) | */ ((go->ipb ? 3 : 0) << 7) | ((go->modet_enable ? 1 : 0) << 2) | ((go->dvd_mode ? 1 : 0) << 1) | 1, - (go->format == GO7007_FORMAT_MPEG1 ? 0x89a0 : - (go->format == GO7007_FORMAT_MPEG2 ? 0x89a0 : - (go->format == GO7007_FORMAT_MJPEG ? 0x89a0 : - (go->format == GO7007_FORMAT_MPEG4 ? 0x8920 : - (go->format == GO7007_FORMAT_H263 ? 0x8920 : 0))))), + (go->format == V4L2_PIX_FMT_MPEG1 ? 0x89a0 : + (go->format == V4L2_PIX_FMT_MPEG2 ? 0x89a0 : + (go->format == V4L2_PIX_FMT_MJPEG ? 0x89a0 : + (go->format == V4L2_PIX_FMT_MPEG4 ? 0x8920 : + (go->format == V4L2_PIX_FMT_H263 ? 0x8920 : 0))))), go->ipb ? 0x1f15 : 0x1f0b, go->ipb ? 0x0015 : 0x000b, go->ipb ? 0xa800 : 0x5800, @@ -1503,13 +1505,13 @@ static int do_special(struct go7007 *go, u16 type, __le16 *code, int space, switch (type) { case SPECIAL_FRM_HEAD: switch (go->format) { - case GO7007_FORMAT_MJPEG: + case V4L2_PIX_FMT_MJPEG: return gen_mjpeghdr_to_package(go, code, space); - case GO7007_FORMAT_MPEG1: - case GO7007_FORMAT_MPEG2: + case V4L2_PIX_FMT_MPEG1: + case V4L2_PIX_FMT_MPEG2: return gen_mpeg1hdr_to_package(go, code, space, framelen); - case GO7007_FORMAT_MPEG4: + case V4L2_PIX_FMT_MPEG4: return gen_mpeg4hdr_to_package(go, code, space, framelen); } @@ -1519,11 +1521,11 @@ static int do_special(struct go7007 *go, u16 type, __le16 *code, int space, return config_package(go, code, space); case SPECIAL_SEQHEAD: switch (go->format) { - case GO7007_FORMAT_MPEG1: - case GO7007_FORMAT_MPEG2: + case V4L2_PIX_FMT_MPEG1: + case V4L2_PIX_FMT_MPEG2: return seqhead_to_package(go, code, space, mpeg1_sequence_header); - case GO7007_FORMAT_MPEG4: + case V4L2_PIX_FMT_MPEG4: return seqhead_to_package(go, code, space, mpeg4_sequence_header); default: @@ -1553,25 +1555,25 @@ int go7007_construct_fw_image(struct go7007 *go, u8 **fw, int *fwlen) int ret; switch (go->format) { - case GO7007_FORMAT_MJPEG: + case V4L2_PIX_FMT_MJPEG: mode_flag = FLAG_MODE_MJPEG; break; - case GO7007_FORMAT_MPEG1: + case V4L2_PIX_FMT_MPEG1: mode_flag = FLAG_MODE_MPEG1; break; - case GO7007_FORMAT_MPEG2: + case V4L2_PIX_FMT_MPEG2: mode_flag = FLAG_MODE_MPEG2; break; - case GO7007_FORMAT_MPEG4: + case V4L2_PIX_FMT_MPEG4: mode_flag = FLAG_MODE_MPEG4; break; default: return -1; } - if (request_firmware(&fw_entry, go->board_info->firmware, go->dev)) { + if (request_firmware(&fw_entry, GO7007_FW_NAME, go->dev)) { dev_err(go->dev, "unable to load firmware from file \"%s\"\n", - go->board_info->firmware); + GO7007_FW_NAME); return -1; } code = kzalloc(codespace * 2, GFP_KERNEL); @@ -1586,7 +1588,7 @@ int go7007_construct_fw_image(struct go7007 *go, u8 **fw, int *fwlen) if (chunk_len + 2 > srclen) { dev_err(go->dev, "firmware file \"%s\" appears to be corrupted\n", - go->board_info->firmware); + GO7007_FW_NAME); goto fw_failed; } if (chunk_flags & mode_flag) { @@ -1622,3 +1624,5 @@ fw_failed: release_firmware(fw_entry); return -1; } + +MODULE_FIRMWARE(GO7007_FW_NAME); diff --git a/drivers/staging/media/go7007/go7007-i2c.c b/drivers/staging/media/go7007/go7007-i2c.c index 39456a36b2c6..74f25e03c326 100644 --- a/drivers/staging/media/go7007/go7007-i2c.c +++ b/drivers/staging/media/go7007/go7007-i2c.c @@ -28,7 +28,6 @@ #include <linux/uaccess.h> #include "go7007-priv.h" -#include "wis-i2c.h" /********************* Driver for on-board I2C adapter *********************/ @@ -52,11 +51,11 @@ static DEFINE_MUTEX(adlink_mpg24_i2c_lock); static int go7007_i2c_xfer(struct go7007 *go, u16 addr, int read, u16 command, int flags, u8 *data) { - int i, ret = -1; + int i, ret = -EIO; u16 val; if (go->status == STATUS_SHUTDOWN) - return -1; + return -ENODEV; #ifdef GO7007_I2C_DEBUG if (read) @@ -146,7 +145,7 @@ static int go7007_smbus_xfer(struct i2c_adapter *adapter, u16 addr, struct go7007 *go = i2c_get_adapdata(adapter); if (size != I2C_SMBUS_BYTE_DATA) - return -1; + return -EIO; return go7007_i2c_xfer(go, addr, read_write == I2C_SMBUS_READ, command, flags & I2C_CLIENT_SCCB ? 0x10 : 0x00, &data->byte); } @@ -170,26 +169,26 @@ static int go7007_i2c_master_xfer(struct i2c_adapter *adapter, (msgs[i].flags & I2C_M_RD) || !(msgs[i + 1].flags & I2C_M_RD) || msgs[i + 1].len != 1) - return -1; + return -EIO; if (go7007_i2c_xfer(go, msgs[i].addr, 1, (msgs[i].buf[0] << 8) | msgs[i].buf[1], 0x01, &msgs[i + 1].buf[0]) < 0) - return -1; + return -EIO; ++i; } else if (msgs[i].len == 3) { if (msgs[i].flags & I2C_M_RD) - return -1; + return -EIO; if (msgs[i].len != 3) - return -1; + return -EIO; if (go7007_i2c_xfer(go, msgs[i].addr, 0, (msgs[i].buf[0] << 8) | msgs[i].buf[1], 0x01, &msgs[i].buf[2]) < 0) - return -1; + return -EIO; } else - return -1; + return -EIO; } - return 0; + return num; } static u32 go7007_functionality(struct i2c_adapter *adapter) diff --git a/drivers/staging/media/go7007/go7007-loader.c b/drivers/staging/media/go7007/go7007-loader.c new file mode 100644 index 000000000000..f846ad5819dc --- /dev/null +++ b/drivers/staging/media/go7007/go7007-loader.c @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2008 Sensoray Company Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License (Version 2) as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/usb.h> +#include <linux/firmware.h> +#include <cypress_firmware.h> + +struct fw_config { + u16 vendor; + u16 product; + const char * const fw_name1; + const char * const fw_name2; +}; + +struct fw_config fw_configs[] = { + { 0x1943, 0xa250, "go7007/s2250-1.fw", "go7007/s2250-2.fw" }, + { 0x093b, 0xa002, "go7007/px-m402u.fw", NULL }, + { 0x093b, 0xa004, "go7007/px-tv402u.fw", NULL }, + { 0x0eb1, 0x6666, "go7007/lr192.fw", NULL }, + { 0x0eb1, 0x6668, "go7007/wis-startrek.fw", NULL }, + { 0, 0, NULL, NULL } +}; +MODULE_FIRMWARE("go7007/s2250-1.fw"); +MODULE_FIRMWARE("go7007/s2250-2.fw"); +MODULE_FIRMWARE("go7007/px-m402u.fw"); +MODULE_FIRMWARE("go7007/px-tv402u.fw"); +MODULE_FIRMWARE("go7007/lr192.fw"); +MODULE_FIRMWARE("go7007/wis-startrek.fw"); + +static int go7007_loader_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct usb_device *usbdev; + const struct firmware *fw; + u16 vendor, product; + const char *fw1, *fw2; + int ret; + int i; + + usbdev = usb_get_dev(interface_to_usbdev(interface)); + if (!usbdev) + goto failed2; + + if (usbdev->descriptor.bNumConfigurations != 1) { + dev_err(&interface->dev, "can't handle multiple config\n"); + return -ENODEV; + } + + vendor = le16_to_cpu(usbdev->descriptor.idVendor); + product = le16_to_cpu(usbdev->descriptor.idProduct); + + for (i = 0; fw_configs[i].fw_name1; i++) + if (fw_configs[i].vendor == vendor && + fw_configs[i].product == product) + break; + + /* Should never happen */ + if (fw_configs[i].fw_name1 == NULL) + goto failed2; + + fw1 = fw_configs[i].fw_name1; + fw2 = fw_configs[i].fw_name2; + + dev_info(&interface->dev, "loading firmware %s\n", fw1); + + if (request_firmware(&fw, fw1, &usbdev->dev)) { + dev_err(&interface->dev, + "unable to load firmware from file \"%s\"\n", fw1); + goto failed2; + } + ret = cypress_load_firmware(usbdev, fw, CYPRESS_FX2); + release_firmware(fw); + if (0 != ret) { + dev_err(&interface->dev, "loader download failed\n"); + goto failed2; + } + + if (fw2 == NULL) + return 0; + + if (request_firmware(&fw, fw2, &usbdev->dev)) { + dev_err(&interface->dev, + "unable to load firmware from file \"%s\"\n", fw2); + goto failed2; + } + ret = cypress_load_firmware(usbdev, fw, CYPRESS_FX2); + release_firmware(fw); + if (0 != ret) { + dev_err(&interface->dev, "firmware download failed\n"); + goto failed2; + } + return 0; + +failed2: + dev_err(&interface->dev, "probe failed\n"); + return -ENODEV; +} + +static void go7007_loader_disconnect(struct usb_interface *interface) +{ + dev_info(&interface->dev, "disconnect\n"); + usb_set_intfdata(interface, NULL); +} + +static const struct usb_device_id go7007_loader_ids[] = { + { USB_DEVICE(0x1943, 0xa250) }, + { USB_DEVICE(0x093b, 0xa002) }, + { USB_DEVICE(0x093b, 0xa004) }, + { USB_DEVICE(0x0eb1, 0x6666) }, + { USB_DEVICE(0x0eb1, 0x6668) }, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, go7007_loader_ids); + +static struct usb_driver go7007_loader_driver = { + .name = "go7007-loader", + .probe = go7007_loader_probe, + .disconnect = go7007_loader_disconnect, + .id_table = go7007_loader_ids, +}; + +module_usb_driver(go7007_loader_driver); + +MODULE_AUTHOR(""); +MODULE_DESCRIPTION("firmware loader for go7007-usb"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/go7007/go7007-priv.h b/drivers/staging/media/go7007/go7007-priv.h index b58c394c6555..6e16af720504 100644 --- a/drivers/staging/media/go7007/go7007-priv.h +++ b/drivers/staging/media/go7007/go7007-priv.h @@ -22,6 +22,9 @@ */ #include <media/v4l2-device.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-fh.h> +#include <media/videobuf2-core.h> struct go7007; @@ -34,15 +37,13 @@ struct go7007; #define GO7007_BOARDID_XMEN_II 5 #define GO7007_BOARDID_XMEN_III 6 #define GO7007_BOARDID_MATRIX_REV 7 -#define GO7007_BOARDID_PX_M402U 16 -#define GO7007_BOARDID_PX_TV402U_ANY 17 /* need to check tuner model */ -#define GO7007_BOARDID_PX_TV402U_NA 18 /* detected NTSC tuner */ -#define GO7007_BOARDID_PX_TV402U_EU 19 /* detected PAL tuner */ -#define GO7007_BOARDID_PX_TV402U_JP 20 /* detected NTSC-J tuner */ -#define GO7007_BOARDID_LIFEVIEW_LR192 21 /* TV Walker Ultra */ -#define GO7007_BOARDID_ENDURA 22 -#define GO7007_BOARDID_ADLINK_MPG24 23 -#define GO7007_BOARDID_SENSORAY_2250 24 /* Sensoray 2250/2251 */ +#define GO7007_BOARDID_PX_M402U 8 +#define GO7007_BOARDID_PX_TV402U 9 +#define GO7007_BOARDID_LIFEVIEW_LR192 10 /* TV Walker Ultra */ +#define GO7007_BOARDID_ENDURA 11 +#define GO7007_BOARDID_ADLINK_MPG24 12 +#define GO7007_BOARDID_SENSORAY_2250 13 /* Sensoray 2250/2251 */ +#define GO7007_BOARDID_ADS_USBAV_709 14 /* Various characteristics of each board */ #define GO7007_BOARD_HAS_AUDIO (1<<0) @@ -61,6 +62,7 @@ struct go7007; #define GO7007_SENSOR_TV (1<<7) #define GO7007_SENSOR_VBI (1<<8) #define GO7007_SENSOR_SCALING (1<<9) +#define GO7007_SENSOR_SAA7115 (1<<10) /* Characteristics of audio sensor devices */ #define GO7007_AUDIO_I2S_MODE_1 (1) @@ -74,7 +76,6 @@ struct go7007; #define GO7007_AUDIO_OKI_MODE (1<<17) struct go7007_board_info { - char *firmware; unsigned int flags; int hpi_buffer_cap; unsigned int sensor_flags; @@ -88,17 +89,25 @@ struct go7007_board_info { int audio_bclk_div; int audio_main_div; int num_i2c_devs; - struct { + struct go_i2c { const char *type; - int id; + unsigned int is_video:1; + unsigned int is_audio:1; int addr; - } i2c_devs[4]; + u32 flags; + } i2c_devs[5]; int num_inputs; struct { int video_input; - int audio_input; + int audio_index; char *name; } inputs[4]; + int video_config; + int num_aud_inputs; + struct { + int audio_input; + char *name; + } aud_inputs[3]; }; struct go7007_hpi_ops { @@ -109,6 +118,7 @@ struct go7007_hpi_ops { int (*stream_stop)(struct go7007 *go); int (*send_firmware)(struct go7007 *go, u8 *data, int len); int (*send_command)(struct go7007 *go, unsigned int cmd, void *arg); + void (*release)(struct go7007 *go); }; /* The video buffer size must be a multiple of PAGE_SIZE */ @@ -116,35 +126,12 @@ struct go7007_hpi_ops { #define GO7007_BUF_SIZE (GO7007_BUF_PAGES << PAGE_SHIFT) struct go7007_buffer { - struct go7007 *go; /* Reverse reference for VMA ops */ - int index; /* Reverse reference for DQBUF */ - enum { BUF_STATE_IDLE, BUF_STATE_QUEUED, BUF_STATE_DONE } state; - u32 seq; - struct timeval timestamp; - struct list_head stream; - struct page *pages[GO7007_BUF_PAGES + 1]; /* extra for straddling */ - unsigned long user_addr; - unsigned int page_count; - unsigned int offset; - unsigned int bytesused; + struct vb2_buffer vb; + struct list_head list; unsigned int frame_offset; u32 modet_active; - int mapped; }; -struct go7007_file { - struct go7007 *go; - struct mutex lock; - int buf_count; - struct go7007_buffer *bufs; -}; - -#define GO7007_FORMAT_MJPEG 0 -#define GO7007_FORMAT_MPEG4 1 -#define GO7007_FORMAT_MPEG1 2 -#define GO7007_FORMAT_MPEG2 3 -#define GO7007_FORMAT_H263 4 - #define GO7007_RATIO_1_1 0 #define GO7007_RATIO_4_3 1 #define GO7007_RATIO_16_9 2 @@ -163,24 +150,38 @@ enum go7007_parser_state { struct go7007 { struct device *dev; - struct go7007_board_info *board_info; + u8 bus_info[32]; + const struct go7007_board_info *board_info; unsigned int board_id; int tuner_type; int channel_number; /* for multi-channel boards like Adlink PCI-MPG24 */ char name[64]; - struct video_device *video_dev; + struct video_device vdev; + void *boot_fw; + unsigned boot_fw_len; struct v4l2_device v4l2_dev; - int ref_count; + struct v4l2_ctrl_handler hdl; + struct v4l2_ctrl *mpeg_video_encoding; + struct v4l2_ctrl *mpeg_video_gop_size; + struct v4l2_ctrl *mpeg_video_gop_closure; + struct v4l2_ctrl *mpeg_video_bitrate; + struct v4l2_ctrl *mpeg_video_aspect_ratio; + struct v4l2_ctrl *mpeg_video_b_frames; + struct v4l2_ctrl *mpeg_video_rep_seqheader; enum { STATUS_INIT, STATUS_ONLINE, STATUS_SHUTDOWN } status; spinlock_t spinlock; struct mutex hw_lock; - int streaming; - int in_use; + struct mutex serialize_lock; int audio_enabled; + struct v4l2_subdev *sd_video; + struct v4l2_subdev *sd_audio; + u8 usb_buf[16]; /* Video input */ int input; + int aud_input; enum { GO7007_STD_NTSC, GO7007_STD_PAL, GO7007_STD_OTHER } standard; + v4l2_std_id std; int sensor_framerate; int width; int height; @@ -191,7 +192,7 @@ struct go7007 { unsigned int encoder_subsample:1; /* Encoder config */ - int format; + u32 format; int bitrate; int fps_scale; int pali; @@ -217,14 +218,16 @@ struct go7007 { unsigned char active_map[216]; /* Video streaming */ - struct go7007_buffer *active_buf; + struct mutex queue_lock; + struct vb2_queue vidq; enum go7007_parser_state state; int parse_length; u16 modet_word; int seen_frame; u32 next_seq; - struct list_head stream; + struct list_head vidq_active; wait_queue_head_t frame_waitq; + struct go7007_buffer *active_buf; /* Audio streaming */ void (*audio_deliver)(struct go7007 *go, u8 *buf, int length); @@ -267,12 +270,12 @@ int go7007_read_addr(struct go7007 *go, u16 addr, u16 *data); int go7007_read_interrupt(struct go7007 *go, u16 *value, u16 *data); int go7007_boot_encoder(struct go7007 *go, int init_i2c); int go7007_reset_encoder(struct go7007 *go); -int go7007_register_encoder(struct go7007 *go); +int go7007_register_encoder(struct go7007 *go, unsigned num_i2c_devs); int go7007_start_encoder(struct go7007 *go); void go7007_parse_video_stream(struct go7007 *go, u8 *buf, int length); -struct go7007 *go7007_alloc(struct go7007_board_info *board, +struct go7007 *go7007_alloc(const struct go7007_board_info *board, struct device *dev); -void go7007_remove(struct go7007 *go); +void go7007_update_board(struct go7007 *go); /* go7007-fw.c */ int go7007_construct_fw_image(struct go7007 *go, u8 **fw, int *fwlen); @@ -283,6 +286,7 @@ int go7007_i2c_remove(struct go7007 *go); /* go7007-v4l2.c */ int go7007_v4l2_init(struct go7007 *go); +int go7007_v4l2_ctrl_init(struct go7007 *go); void go7007_v4l2_remove(struct go7007 *go); /* snd-go7007.c */ diff --git a/drivers/staging/media/go7007/go7007-usb.c b/drivers/staging/media/go7007/go7007-usb.c index 9dbf5ecd05a2..50066e01a6ed 100644 --- a/drivers/staging/media/go7007/go7007-usb.c +++ b/drivers/staging/media/go7007/go7007-usb.c @@ -26,10 +26,11 @@ #include <linux/usb.h> #include <linux/i2c.h> #include <asm/byteorder.h> -#include <media/tvaudio.h> +#include <media/saa7115.h> +#include <media/tuner.h> +#include <media/uda1342.h> #include "go7007-priv.h" -#include "wis-i2c.h" static unsigned int assume_endura; module_param(assume_endura, int, 0644); @@ -62,7 +63,7 @@ struct go7007_usb_board { }; struct go7007_usb { - struct go7007_usb_board *board; + const struct go7007_usb_board *board; struct mutex i2c_lock; struct usb_device *usbdev; struct urb *video_urbs[8]; @@ -72,10 +73,9 @@ struct go7007_usb { /*********************** Product specification data ***********************/ -static struct go7007_usb_board board_matrix_ii = { +static const struct go7007_usb_board board_matrix_ii = { .flags = GO7007_USB_EZUSB, .main_info = { - .firmware = "go7007tv.bin", .flags = GO7007_BOARD_HAS_AUDIO | GO7007_BOARD_USE_ONBOARD_I2C, .audio_flags = GO7007_AUDIO_I2S_MODE_1 | @@ -87,14 +87,15 @@ static struct go7007_usb_board board_matrix_ii = { .sensor_flags = GO7007_SENSOR_656 | GO7007_SENSOR_VALID_ENABLE | GO7007_SENSOR_TV | + GO7007_SENSOR_SAA7115 | GO7007_SENSOR_VBI | GO7007_SENSOR_SCALING, .num_i2c_devs = 1, .i2c_devs = { { - .type = "wis_saa7115", - .id = I2C_DRIVERID_WIS_SAA7115, + .type = "saa7115", .addr = 0x20, + .is_video = 1, }, }, .num_inputs = 2, @@ -108,13 +109,13 @@ static struct go7007_usb_board board_matrix_ii = { .name = "S-Video", }, }, + .video_config = SAA7115_IDQ_IS_DEFAULT, }, }; -static struct go7007_usb_board board_matrix_reload = { +static const struct go7007_usb_board board_matrix_reload = { .flags = GO7007_USB_EZUSB, .main_info = { - .firmware = "go7007tv.bin", .flags = GO7007_BOARD_HAS_AUDIO | GO7007_BOARD_USE_ONBOARD_I2C, .audio_flags = GO7007_AUDIO_I2S_MODE_1 | @@ -129,9 +130,9 @@ static struct go7007_usb_board board_matrix_reload = { .num_i2c_devs = 1, .i2c_devs = { { - .type = "wis_saa7113", - .id = I2C_DRIVERID_WIS_SAA7113, + .type = "saa7113", .addr = 0x25, + .is_video = 1, }, }, .num_inputs = 2, @@ -145,18 +146,19 @@ static struct go7007_usb_board board_matrix_reload = { .name = "S-Video", }, }, + .video_config = SAA7115_IDQ_IS_DEFAULT, }, }; -static struct go7007_usb_board board_star_trek = { +static const struct go7007_usb_board board_star_trek = { .flags = GO7007_USB_EZUSB | GO7007_USB_EZUSB_I2C, .main_info = { - .firmware = "go7007tv.bin", .flags = GO7007_BOARD_HAS_AUDIO, /* | GO7007_BOARD_HAS_TUNER, */ .sensor_flags = GO7007_SENSOR_656 | GO7007_SENSOR_VALID_ENABLE | GO7007_SENSOR_TV | + GO7007_SENSOR_SAA7115 | GO7007_SENSOR_VBI | GO7007_SENSOR_SCALING, .audio_flags = GO7007_AUDIO_I2S_MODE_1 | @@ -167,42 +169,43 @@ static struct go7007_usb_board board_star_trek = { .num_i2c_devs = 1, .i2c_devs = { { - .type = "wis_saa7115", - .id = I2C_DRIVERID_WIS_SAA7115, + .type = "saa7115", .addr = 0x20, + .is_video = 1, }, }, .num_inputs = 2, .inputs = { + /* { + * .video_input = 3, + * .audio_index = AUDIO_TUNER, + * .name = "Tuner", + * }, + */ { .video_input = 1, - /* .audio_input = AUDIO_EXTERN, */ + /* .audio_index = AUDIO_EXTERN, */ .name = "Composite", }, { .video_input = 8, - /* .audio_input = AUDIO_EXTERN, */ + /* .audio_index = AUDIO_EXTERN, */ .name = "S-Video", }, - /* { - * .video_input = 3, - * .audio_input = AUDIO_TUNER, - * .name = "Tuner", - * }, - */ }, + .video_config = SAA7115_IDQ_IS_DEFAULT, }, }; -static struct go7007_usb_board board_px_tv402u = { +static const struct go7007_usb_board board_px_tv402u = { .flags = GO7007_USB_EZUSB | GO7007_USB_EZUSB_I2C, .main_info = { - .firmware = "go7007tv.bin", .flags = GO7007_BOARD_HAS_AUDIO | GO7007_BOARD_HAS_TUNER, .sensor_flags = GO7007_SENSOR_656 | GO7007_SENSOR_VALID_ENABLE | GO7007_SENSOR_TV | + GO7007_SENSOR_SAA7115 | GO7007_SENSOR_VBI | GO7007_SENSOR_SCALING, .audio_flags = GO7007_AUDIO_I2S_MODE_1 | @@ -210,49 +213,67 @@ static struct go7007_usb_board board_px_tv402u = { .audio_bclk_div = 8, .audio_main_div = 2, .hpi_buffer_cap = 7, - .num_i2c_devs = 3, + .num_i2c_devs = 5, .i2c_devs = { { - .type = "wis_saa7115", - .id = I2C_DRIVERID_WIS_SAA7115, + .type = "saa7115", .addr = 0x20, + .is_video = 1, }, { - .type = "wis_uda1342", - .id = I2C_DRIVERID_WIS_UDA1342, + .type = "uda1342", .addr = 0x1a, + .is_audio = 1, }, { - .type = "wis_sony_tuner", - .id = I2C_DRIVERID_WIS_SONY_TUNER, + .type = "tuner", .addr = 0x60, }, + { + .type = "tuner", + .addr = 0x43, + }, + { + .type = "sony-btf-mpx", + .addr = 0x44, + }, }, .num_inputs = 3, .inputs = { { + .video_input = 3, + .audio_index = 0, + .name = "Tuner", + }, + { .video_input = 1, - .audio_input = TVAUDIO_INPUT_EXTERN, + .audio_index = 1, .name = "Composite", }, { .video_input = 8, - .audio_input = TVAUDIO_INPUT_EXTERN, + .audio_index = 1, .name = "S-Video", }, + }, + .video_config = SAA7115_IDQ_IS_DEFAULT, + .num_aud_inputs = 2, + .aud_inputs = { { - .video_input = 3, - .audio_input = TVAUDIO_INPUT_TUNER, + .audio_input = UDA1342_IN2, .name = "Tuner", }, + { + .audio_input = UDA1342_IN1, + .name = "Line In", + }, }, }, }; -static struct go7007_usb_board board_xmen = { +static const struct go7007_usb_board board_xmen = { .flags = 0, .main_info = { - .firmware = "go7007tv.bin", .flags = GO7007_BOARD_USE_ONBOARD_I2C, .hpi_buffer_cap = 0, .sensor_flags = GO7007_SENSOR_VREF_POLAR, @@ -271,8 +292,7 @@ static struct go7007_usb_board board_xmen = { .num_i2c_devs = 1, .i2c_devs = { { - .type = "wis_ov7640", - .id = I2C_DRIVERID_WIS_OV7640, + .type = "ov7640", .addr = 0x21, }, }, @@ -285,10 +305,9 @@ static struct go7007_usb_board board_xmen = { }, }; -static struct go7007_usb_board board_matrix_revolution = { +static const struct go7007_usb_board board_matrix_revolution = { .flags = GO7007_USB_EZUSB, .main_info = { - .firmware = "go7007tv.bin", .flags = GO7007_BOARD_HAS_AUDIO | GO7007_BOARD_USE_ONBOARD_I2C, .audio_flags = GO7007_AUDIO_I2S_MODE_1 | @@ -304,8 +323,8 @@ static struct go7007_usb_board board_matrix_revolution = { .num_i2c_devs = 1, .i2c_devs = { { - .type = "wis_tw9903", - .id = I2C_DRIVERID_WIS_TW9903, + .type = "tw9903", + .is_video = 1, .addr = 0x44, }, }, @@ -323,10 +342,9 @@ static struct go7007_usb_board board_matrix_revolution = { }, }; -static struct go7007_usb_board board_lifeview_lr192 = { +static const struct go7007_usb_board board_lifeview_lr192 = { .flags = GO7007_USB_EZUSB, .main_info = { - .firmware = "go7007tv.bin", .flags = GO7007_BOARD_HAS_AUDIO | GO7007_BOARD_USE_ONBOARD_I2C, .audio_flags = GO7007_AUDIO_I2S_MODE_1 | @@ -351,10 +369,9 @@ static struct go7007_usb_board board_lifeview_lr192 = { }, }; -static struct go7007_usb_board board_endura = { +static const struct go7007_usb_board board_endura = { .flags = 0, .main_info = { - .firmware = "go7007tv.bin", .flags = 0, .audio_flags = GO7007_AUDIO_I2S_MODE_1 | GO7007_AUDIO_I2S_MASTER | @@ -376,10 +393,9 @@ static struct go7007_usb_board board_endura = { }, }; -static struct go7007_usb_board board_adlink_mpg24 = { +static const struct go7007_usb_board board_adlink_mpg24 = { .flags = 0, .main_info = { - .firmware = "go7007tv.bin", .flags = GO7007_BOARD_USE_ONBOARD_I2C, .audio_flags = GO7007_AUDIO_I2S_MODE_1 | GO7007_AUDIO_I2S_MASTER | @@ -394,9 +410,10 @@ static struct go7007_usb_board board_adlink_mpg24 = { .num_i2c_devs = 1, .i2c_devs = { { - .type = "wis_tw2804", - .id = I2C_DRIVERID_WIS_TW2804, + .type = "tw2804", .addr = 0x00, /* yes, really */ + .flags = I2C_CLIENT_TEN, + .is_video = 1, }, }, .num_inputs = 1, @@ -408,10 +425,9 @@ static struct go7007_usb_board board_adlink_mpg24 = { }, }; -static struct go7007_usb_board board_sensoray_2250 = { +static const struct go7007_usb_board board_sensoray_2250 = { .flags = GO7007_USB_EZUSB | GO7007_USB_EZUSB_I2C, .main_info = { - .firmware = "go7007tv.bin", .audio_flags = GO7007_AUDIO_I2S_MODE_1 | GO7007_AUDIO_I2S_MASTER | GO7007_AUDIO_WORD_16, @@ -426,8 +442,9 @@ static struct go7007_usb_board board_sensoray_2250 = { .i2c_devs = { { .type = "s2250", - .id = I2C_DRIVERID_S2250, .addr = 0x43, + .is_video = 1, + .is_audio = 1, }, }, .num_inputs = 2, @@ -441,10 +458,60 @@ static struct go7007_usb_board board_sensoray_2250 = { .name = "S-Video", }, }, + .num_aud_inputs = 3, + .aud_inputs = { + { + .audio_input = 0, + .name = "Line In", + }, + { + .audio_input = 1, + .name = "Mic", + }, + { + .audio_input = 2, + .name = "Mic Boost", + }, + }, }, }; -MODULE_FIRMWARE("go7007tv.bin"); +static const struct go7007_usb_board board_ads_usbav_709 = { + .flags = GO7007_USB_EZUSB, + .main_info = { + .flags = GO7007_BOARD_HAS_AUDIO | + GO7007_BOARD_USE_ONBOARD_I2C, + .audio_flags = GO7007_AUDIO_I2S_MODE_1 | + GO7007_AUDIO_I2S_MASTER | + GO7007_AUDIO_WORD_16, + .audio_rate = 48000, + .audio_bclk_div = 8, + .audio_main_div = 2, + .hpi_buffer_cap = 7, + .sensor_flags = GO7007_SENSOR_656 | + GO7007_SENSOR_TV | + GO7007_SENSOR_VBI, + .num_i2c_devs = 1, + .i2c_devs = { + { + .type = "tw9906", + .is_video = 1, + .addr = 0x44, + }, + }, + .num_inputs = 2, + .inputs = { + { + .video_input = 0, + .name = "Composite", + }, + { + .video_input = 10, + .name = "S-Video", + }, + }, + }, +}; static const struct usb_device_id go7007_usb_id_table[] = { { @@ -529,7 +596,7 @@ static const struct usb_device_id go7007_usb_id_table[] = { .idProduct = 0xa104, /* Product ID of TV402U */ .bcdDevice_lo = 0x1, .bcdDevice_hi = 0x1, - .driver_info = (kernel_ulong_t)GO7007_BOARDID_PX_TV402U_ANY, + .driver_info = (kernel_ulong_t)GO7007_BOARDID_PX_TV402U, }, { .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION, @@ -547,6 +614,14 @@ static const struct usb_device_id go7007_usb_id_table[] = { .bcdDevice_hi = 0x1, .driver_info = (kernel_ulong_t)GO7007_BOARDID_SENSORAY_2250, }, + { + .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION, + .idVendor = 0x06e1, /* Vendor ID of ADS Technologies */ + .idProduct = 0x0709, /* Product ID of DVD Xpress DX2 */ + .bcdDevice_lo = 0x204, + .bcdDevice_hi = 0x204, + .driver_info = (kernel_ulong_t)GO7007_BOARDID_ADS_USBAV_709, + }, { } /* Terminating entry */ }; @@ -578,6 +653,8 @@ static int go7007_usb_interface_reset(struct go7007 *go) struct go7007_usb *usb = go->hpi_context; u16 intr_val, intr_data; + if (go->status == STATUS_SHUTDOWN) + return -1; /* Reset encoder */ if (go7007_write_interrupt(go, 0x0001, 0x0001) < 0) return -1; @@ -613,7 +690,7 @@ static int go7007_usb_ezusb_write_interrupt(struct go7007 *go, { struct go7007_usb *usb = go->hpi_context; int i, r; - u16 status_reg; + u16 status_reg = 0; int timeout = 500; #ifdef GO7007_USB_DEBUG @@ -625,15 +702,17 @@ static int go7007_usb_ezusb_write_interrupt(struct go7007 *go, r = usb_control_msg(usb->usbdev, usb_rcvctrlpipe(usb->usbdev, 0), 0x14, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, - 0, HPI_STATUS_ADDR, &status_reg, + 0, HPI_STATUS_ADDR, go->usb_buf, sizeof(status_reg), timeout); if (r < 0) - goto write_int_error; - __le16_to_cpus(&status_reg); + break; + status_reg = le16_to_cpu(*((u16 *)go->usb_buf)); if (!(status_reg & 0x0010)) break; msleep(10); } + if (r < 0) + goto write_int_error; if (i == 100) { printk(KERN_ERR "go7007-usb: device is hung, status reg = 0x%04x\n", @@ -661,7 +740,6 @@ static int go7007_usb_onboard_write_interrupt(struct go7007 *go, int addr, int data) { struct go7007_usb *usb = go->hpi_context; - u8 *tbuf; int r; int timeout = 500; @@ -670,17 +748,14 @@ static int go7007_usb_onboard_write_interrupt(struct go7007 *go, "go7007-usb: WriteInterrupt: %04x %04x\n", addr, data); #endif - tbuf = kzalloc(8, GFP_KERNEL); - if (tbuf == NULL) - return -ENOMEM; - tbuf[0] = data & 0xff; - tbuf[1] = data >> 8; - tbuf[2] = addr & 0xff; - tbuf[3] = addr >> 8; + go->usb_buf[0] = data & 0xff; + go->usb_buf[1] = data >> 8; + go->usb_buf[2] = addr & 0xff; + go->usb_buf[3] = addr >> 8; + go->usb_buf[4] = go->usb_buf[5] = go->usb_buf[6] = go->usb_buf[7] = 0; r = usb_control_msg(usb->usbdev, usb_sndctrlpipe(usb->usbdev, 2), 0x00, USB_TYPE_VENDOR | USB_RECIP_ENDPOINT, 0x55aa, - 0xf0f0, tbuf, 8, timeout); - kfree(tbuf); + 0xf0f0, go->usb_buf, 8, timeout); if (r < 0) { printk(KERN_ERR "go7007-usb: error in WriteInterrupt: %d\n", r); return r; @@ -738,7 +813,7 @@ static void go7007_usb_read_video_pipe_complete(struct urb *urb) struct go7007 *go = (struct go7007 *)urb->context; int r, status = urb->status; - if (!go->streaming) { + if (!vb2_is_streaming(&go->vidq)) { wake_up_interruptible(&go->frame_waitq); return; } @@ -762,7 +837,7 @@ static void go7007_usb_read_audio_pipe_complete(struct urb *urb) struct go7007 *go = (struct go7007 *)urb->context; int r, status = urb->status; - if (!go->streaming) + if (!vb2_is_streaming(&go->vidq)) return; if (status) { printk(KERN_ERR "go7007-usb: error in audio pipe: %d\n", @@ -849,6 +924,37 @@ static int go7007_usb_send_firmware(struct go7007 *go, u8 *data, int len) &transferred, timeout); } +static void go7007_usb_release(struct go7007 *go) +{ + struct go7007_usb *usb = go->hpi_context; + struct urb *vurb, *aurb; + int i; + + if (usb->intr_urb) { + usb_kill_urb(usb->intr_urb); + kfree(usb->intr_urb->transfer_buffer); + usb_free_urb(usb->intr_urb); + } + + /* Free USB-related structs */ + for (i = 0; i < 8; ++i) { + vurb = usb->video_urbs[i]; + if (vurb) { + usb_kill_urb(vurb); + kfree(vurb->transfer_buffer); + usb_free_urb(vurb); + } + aurb = usb->audio_urbs[i]; + if (aurb) { + usb_kill_urb(aurb); + kfree(aurb->transfer_buffer); + usb_free_urb(aurb); + } + } + + kfree(go->hpi_context); +} + static struct go7007_hpi_ops go7007_usb_ezusb_hpi_ops = { .interface_reset = go7007_usb_interface_reset, .write_interrupt = go7007_usb_ezusb_write_interrupt, @@ -856,6 +962,7 @@ static struct go7007_hpi_ops go7007_usb_ezusb_hpi_ops = { .stream_start = go7007_usb_stream_start, .stream_stop = go7007_usb_stream_stop, .send_firmware = go7007_usb_send_firmware, + .release = go7007_usb_release, }; static struct go7007_hpi_ops go7007_usb_onboard_hpi_ops = { @@ -865,6 +972,7 @@ static struct go7007_hpi_ops go7007_usb_onboard_hpi_ops = { .stream_start = go7007_usb_stream_start, .stream_stop = go7007_usb_stream_stop, .send_firmware = go7007_usb_send_firmware, + .release = go7007_usb_release, }; /********************* Driver for EZ-USB I2C adapter *********************/ @@ -874,12 +982,12 @@ static int go7007_usb_i2c_master_xfer(struct i2c_adapter *adapter, { struct go7007 *go = i2c_get_adapdata(adapter); struct go7007_usb *usb = go->hpi_context; - u8 buf[16]; + u8 *buf = go->usb_buf; int buf_len, i; - int ret = -1; + int ret = -EIO; if (go->status == STATUS_SHUTDOWN) - return -1; + return -ENODEV; mutex_lock(&usb->i2c_lock); @@ -929,14 +1037,14 @@ static int go7007_usb_i2c_master_xfer(struct i2c_adapter *adapter, buf, buf_len, 0) < 0) goto i2c_done; if (msgs[i].flags & I2C_M_RD) { - memset(buf, 0, sizeof(buf)); + memset(buf, 0, msgs[i].len + 1); if (go7007_usb_vendor_request(go, 0x25, 0, 0, buf, msgs[i].len + 1, 1) < 0) goto i2c_done; memcpy(msgs[i].buf, buf + 1, msgs[i].len); } } - ret = 0; + ret = num; i2c_done: mutex_unlock(&usb->i2c_lock); @@ -968,8 +1076,9 @@ static int go7007_usb_probe(struct usb_interface *intf, { struct go7007 *go; struct go7007_usb *usb; - struct go7007_usb_board *board; + const struct go7007_usb_board *board; struct usb_device *usbdev = interface_to_usbdev(intf); + unsigned num_i2c_devs; char *name; int video_pipe, i, v_urb_len; @@ -1008,7 +1117,7 @@ static int go7007_usb_probe(struct usb_interface *intf, name = "Plextor PX-M402U"; board = &board_matrix_ii; break; - case GO7007_BOARDID_PX_TV402U_ANY: + case GO7007_BOARDID_PX_TV402U: name = "Plextor PX-TV402U (unknown tuner)"; board = &board_px_tv402u; break; @@ -1024,29 +1133,29 @@ static int go7007_usb_probe(struct usb_interface *intf, name = "Sensoray 2250/2251"; board = &board_sensoray_2250; break; + case GO7007_BOARDID_ADS_USBAV_709: + name = "ADS Tech DVD Xpress DX2"; + board = &board_ads_usbav_709; + break; default: printk(KERN_ERR "go7007-usb: unknown board ID %d!\n", (unsigned int)id->driver_info); return 0; } - usb = kzalloc(sizeof(struct go7007_usb), GFP_KERNEL); - if (usb == NULL) + go = go7007_alloc(&board->main_info, &intf->dev); + if (go == NULL) return -ENOMEM; - /* Allocate the URB and buffer for receiving incoming interrupts */ - usb->intr_urb = usb_alloc_urb(0, GFP_KERNEL); - if (usb->intr_urb == NULL) - goto allocfail; - usb->intr_urb->transfer_buffer = kmalloc(2*sizeof(u16), GFP_KERNEL); - if (usb->intr_urb->transfer_buffer == NULL) - goto allocfail; + usb = kzalloc(sizeof(struct go7007_usb), GFP_KERNEL); + if (usb == NULL) { + kfree(go); + return -ENOMEM; + } - go = go7007_alloc(&board->main_info, &intf->dev); - if (go == NULL) - goto allocfail; usb->board = board; usb->usbdev = usbdev; + usb_make_path(usbdev, go->bus_info, sizeof(go->bus_info)); go->board_id = id->driver_info; strncpy(go->name, name, sizeof(go->name)); if (board->flags & GO7007_USB_EZUSB) @@ -1054,6 +1163,15 @@ static int go7007_usb_probe(struct usb_interface *intf, else go->hpi_ops = &go7007_usb_onboard_hpi_ops; go->hpi_context = usb; + + /* Allocate the URB and buffer for receiving incoming interrupts */ + usb->intr_urb = usb_alloc_urb(0, GFP_KERNEL); + if (usb->intr_urb == NULL) + goto allocfail; + usb->intr_urb->transfer_buffer = kmalloc(2*sizeof(u16), GFP_KERNEL); + if (usb->intr_urb->transfer_buffer == NULL) + goto allocfail; + if (go->board_id == GO7007_BOARDID_SENSORAY_2250) usb_fill_bulk_urb(usb->intr_urb, usb->usbdev, usb_rcvbulkpipe(usb->usbdev, 4), @@ -1069,7 +1187,7 @@ static int go7007_usb_probe(struct usb_interface *intf, /* Boot the GO7007 */ if (go7007_boot_encoder(go, go->board_info->flags & GO7007_BOARD_USE_ONBOARD_I2C) < 0) - goto initfail; + goto allocfail; /* Register the EZ-USB I2C adapter, if we're using it */ if (board->flags & GO7007_USB_EZUSB_I2C) { @@ -1081,7 +1199,7 @@ static int go7007_usb_probe(struct usb_interface *intf, if (i2c_add_adapter(&go->i2c_adapter) < 0) { printk(KERN_ERR "go7007-usb: error: i2c_add_adapter failed\n"); - goto initfail; + goto allocfail; } go->i2c_adapter_online = 1; } @@ -1121,34 +1239,36 @@ static int go7007_usb_probe(struct usb_interface *intf, "Adlink PCI-MPG24, channel #%d", channel); } + go7007_update_board(go); } } - /* Probe the tuner model on the TV402U */ - if (go->board_id == GO7007_BOARDID_PX_TV402U_ANY) { - u8 data[3]; + num_i2c_devs = go->board_info->num_i2c_devs; + /* Probe the tuner model on the TV402U */ + if (go->board_id == GO7007_BOARDID_PX_TV402U) { /* Board strapping indicates tuner model */ - if (go7007_usb_vendor_request(go, 0x41, 0, 0, data, 3, 1) < 0) { + if (go7007_usb_vendor_request(go, 0x41, 0, 0, go->usb_buf, 3, 1) < 0) { printk(KERN_ERR "go7007-usb: GPIO read failed!\n"); - goto initfail; + goto allocfail; } - switch (data[0] >> 6) { + switch (go->usb_buf[0] >> 6) { case 1: - go->board_id = GO7007_BOARDID_PX_TV402U_EU; go->tuner_type = TUNER_SONY_BTF_PG472Z; + go->std = V4L2_STD_PAL; strncpy(go->name, "Plextor PX-TV402U-EU", sizeof(go->name)); break; case 2: - go->board_id = GO7007_BOARDID_PX_TV402U_JP; go->tuner_type = TUNER_SONY_BTF_PK467Z; + go->std = V4L2_STD_NTSC_M_JP; + num_i2c_devs -= 2; strncpy(go->name, "Plextor PX-TV402U-JP", sizeof(go->name)); break; case 3: - go->board_id = GO7007_BOARDID_PX_TV402U_NA; go->tuner_type = TUNER_SONY_BTF_PB463Z; + num_i2c_devs -= 2; strncpy(go->name, "Plextor PX-TV402U-NA", sizeof(go->name)); break; @@ -1162,7 +1282,7 @@ static int go7007_usb_probe(struct usb_interface *intf, if (go7007_usb_vendor_request(go, 0x40, 0x7f02, 0, NULL, 0, 0) < 0) { printk(KERN_ERR "go7007-usb: GPIO write failed!\n"); - goto initfail; + goto allocfail; } } @@ -1176,11 +1296,6 @@ static int go7007_usb_probe(struct usb_interface *intf, "port will result in stream corruption, even " "at low bitrates!\n"); - /* Do any final GO7007 initialization, then register the - * V4L2 and ALSA interfaces */ - if (go7007_register_encoder(go) < 0) - goto initfail; - /* Allocate the URBs and buffers for receiving the video stream */ if (board->flags & GO7007_USB_EZUSB) { v_urb_len = 1024; @@ -1192,80 +1307,65 @@ static int go7007_usb_probe(struct usb_interface *intf, for (i = 0; i < 8; ++i) { usb->video_urbs[i] = usb_alloc_urb(0, GFP_KERNEL); if (usb->video_urbs[i] == NULL) - goto initfail; + goto allocfail; usb->video_urbs[i]->transfer_buffer = kmalloc(v_urb_len, GFP_KERNEL); if (usb->video_urbs[i]->transfer_buffer == NULL) - goto initfail; + goto allocfail; usb_fill_bulk_urb(usb->video_urbs[i], usb->usbdev, video_pipe, usb->video_urbs[i]->transfer_buffer, v_urb_len, go7007_usb_read_video_pipe_complete, go); } /* Allocate the URBs and buffers for receiving the audio stream */ - if ((board->flags & GO7007_USB_EZUSB) && go->audio_enabled) + if ((board->flags & GO7007_USB_EZUSB) && + (board->flags & GO7007_BOARD_HAS_AUDIO)) { for (i = 0; i < 8; ++i) { usb->audio_urbs[i] = usb_alloc_urb(0, GFP_KERNEL); if (usb->audio_urbs[i] == NULL) - goto initfail; + goto allocfail; usb->audio_urbs[i]->transfer_buffer = kmalloc(4096, GFP_KERNEL); if (usb->audio_urbs[i]->transfer_buffer == NULL) - goto initfail; + goto allocfail; usb_fill_bulk_urb(usb->audio_urbs[i], usb->usbdev, usb_rcvbulkpipe(usb->usbdev, 8), usb->audio_urbs[i]->transfer_buffer, 4096, go7007_usb_read_audio_pipe_complete, go); } + } + /* Do any final GO7007 initialization, then register the + * V4L2 and ALSA interfaces */ + if (go7007_register_encoder(go, num_i2c_devs) < 0) + goto allocfail; go->status = STATUS_ONLINE; return 0; -initfail: - go->status = STATUS_SHUTDOWN; - return 0; - allocfail: - if (usb->intr_urb) { - kfree(usb->intr_urb->transfer_buffer); - usb_free_urb(usb->intr_urb); - } - kfree(usb); + go7007_usb_release(go); + kfree(go); return -ENOMEM; } static void go7007_usb_disconnect(struct usb_interface *intf) { struct go7007 *go = to_go7007(usb_get_intfdata(intf)); - struct go7007_usb *usb = go->hpi_context; - struct urb *vurb, *aurb; - int i; - usb_kill_urb(usb->intr_urb); + mutex_lock(&go->queue_lock); + mutex_lock(&go->serialize_lock); - /* Free USB-related structs */ - for (i = 0; i < 8; ++i) { - vurb = usb->video_urbs[i]; - if (vurb) { - usb_kill_urb(vurb); - kfree(vurb->transfer_buffer); - usb_free_urb(vurb); - } - aurb = usb->audio_urbs[i]; - if (aurb) { - usb_kill_urb(aurb); - kfree(aurb->transfer_buffer); - usb_free_urb(aurb); - } - } - kfree(usb->intr_urb->transfer_buffer); - usb_free_urb(usb->intr_urb); - - kfree(go->hpi_context); + if (go->audio_enabled) + go7007_snd_remove(go); - go7007_remove(go); go->status = STATUS_SHUTDOWN; + v4l2_device_disconnect(&go->v4l2_dev); + video_unregister_device(&go->vdev); + mutex_unlock(&go->serialize_lock); + mutex_unlock(&go->queue_lock); + + v4l2_device_put(&go->v4l2_dev); } static struct usb_driver go7007_usb_driver = { diff --git a/drivers/staging/media/go7007/go7007-v4l2.c b/drivers/staging/media/go7007/go7007-v4l2.c index cb9fe33050c7..50eb69a8ef07 100644 --- a/drivers/staging/media/go7007/go7007-v4l2.c +++ b/drivers/staging/media/go7007/go7007-v4l2.c @@ -17,7 +17,6 @@ #include <linux/module.h> #include <linux/init.h> -#include <linux/version.h> #include <linux/delay.h> #include <linux/sched.h> #include <linux/spinlock.h> @@ -27,115 +26,45 @@ #include <linux/time.h> #include <linux/vmalloc.h> #include <linux/pagemap.h> +#include <linux/i2c.h> +#include <linux/mutex.h> +#include <linux/uaccess.h> #include <linux/videodev2.h> #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> #include <media/v4l2-subdev.h> -#include <linux/i2c.h> -#include <linux/mutex.h> -#include <linux/uaccess.h> +#include <media/v4l2-event.h> +#include <media/videobuf2-vmalloc.h> +#include <media/saa7115.h> #include "go7007.h" #include "go7007-priv.h" -#include "wis-i2c.h" - -/* Temporary defines until accepted in v4l-dvb */ -#ifndef V4L2_MPEG_STREAM_TYPE_MPEG_ELEM -#define V4L2_MPEG_STREAM_TYPE_MPEG_ELEM 6 /* MPEG elementary stream */ -#endif -#ifndef V4L2_MPEG_VIDEO_ENCODING_MPEG_4 -#define V4L2_MPEG_VIDEO_ENCODING_MPEG_4 3 -#endif #define call_all(dev, o, f, args...) \ v4l2_device_call_until_err(dev, 0, o, f, ##args) -static void deactivate_buffer(struct go7007_buffer *gobuf) -{ - int i; - - if (gobuf->state != BUF_STATE_IDLE) { - list_del(&gobuf->stream); - gobuf->state = BUF_STATE_IDLE; - } - if (gobuf->page_count > 0) { - for (i = 0; i < gobuf->page_count; ++i) - page_cache_release(gobuf->pages[i]); - gobuf->page_count = 0; - } -} - -static void abort_queued(struct go7007 *go) -{ - struct go7007_buffer *gobuf, *next; - - list_for_each_entry_safe(gobuf, next, &go->stream, stream) { - deactivate_buffer(gobuf); - } -} - -static int go7007_streamoff(struct go7007 *go) -{ - unsigned long flags; - - mutex_lock(&go->hw_lock); - if (go->streaming) { - go->streaming = 0; - go7007_stream_stop(go); - spin_lock_irqsave(&go->spinlock, flags); - abort_queued(go); - spin_unlock_irqrestore(&go->spinlock, flags); - go7007_reset_encoder(go); - } - mutex_unlock(&go->hw_lock); - return 0; -} - -static int go7007_open(struct file *file) -{ - struct go7007 *go = video_get_drvdata(video_devdata(file)); - struct go7007_file *gofh; - - if (go->status != STATUS_ONLINE) - return -EBUSY; - gofh = kzalloc(sizeof(struct go7007_file), GFP_KERNEL); - if (gofh == NULL) - return -ENOMEM; - ++go->ref_count; - gofh->go = go; - mutex_init(&gofh->lock); - gofh->buf_count = 0; - file->private_data = gofh; - return 0; -} - -static int go7007_release(struct file *file) +static bool valid_pixelformat(u32 pixelformat) { - struct go7007_file *gofh = file->private_data; - struct go7007 *go = gofh->go; - - if (gofh->buf_count > 0) { - go7007_streamoff(go); - go->in_use = 0; - kfree(gofh->bufs); - gofh->buf_count = 0; + switch (pixelformat) { + case V4L2_PIX_FMT_MJPEG: + case V4L2_PIX_FMT_MPEG1: + case V4L2_PIX_FMT_MPEG2: + case V4L2_PIX_FMT_MPEG4: + return true; + default: + return false; } - kfree(gofh); - if (--go->ref_count == 0) - kfree(go); - file->private_data = NULL; - return 0; } -static u32 get_frame_type_flag(struct go7007_buffer *gobuf, int format) +static u32 get_frame_type_flag(struct go7007_buffer *vb, int format) { - u8 *f = page_address(gobuf->pages[0]); + u8 *ptr = vb2_plane_vaddr(&vb->vb, 0); switch (format) { - case GO7007_FORMAT_MJPEG: + case V4L2_PIX_FMT_MJPEG: return V4L2_BUF_FLAG_KEYFRAME; - case GO7007_FORMAT_MPEG4: - switch ((f[gobuf->frame_offset + 4] >> 6) & 0x3) { + case V4L2_PIX_FMT_MPEG4: + switch ((ptr[vb->frame_offset + 4] >> 6) & 0x3) { case 0: return V4L2_BUF_FLAG_KEYFRAME; case 1: @@ -145,9 +74,9 @@ static u32 get_frame_type_flag(struct go7007_buffer *gobuf, int format) default: return 0; } - case GO7007_FORMAT_MPEG1: - case GO7007_FORMAT_MPEG2: - switch ((f[gobuf->frame_offset + 5] >> 3) & 0x7) { + case V4L2_PIX_FMT_MPEG1: + case V4L2_PIX_FMT_MPEG2: + switch ((ptr[vb->frame_offset + 5] >> 3) & 0x7) { case 1: return V4L2_BUF_FLAG_KEYFRAME; case 2: @@ -162,30 +91,111 @@ static u32 get_frame_type_flag(struct go7007_buffer *gobuf, int format) return 0; } -static int set_capture_size(struct go7007 *go, struct v4l2_format *fmt, int try) +static void get_resolution(struct go7007 *go, int *width, int *height) { - int sensor_height = 0, sensor_width = 0; - int width, height, i; - - if (fmt != NULL && fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG && - fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_MPEG && - fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_MPEG4) - return -EINVAL; - switch (go->standard) { case GO7007_STD_NTSC: - sensor_width = 720; - sensor_height = 480; + *width = 720; + *height = 480; break; case GO7007_STD_PAL: - sensor_width = 720; - sensor_height = 576; + *width = 720; + *height = 576; break; case GO7007_STD_OTHER: - sensor_width = go->board_info->sensor_width; - sensor_height = go->board_info->sensor_height; + default: + *width = go->board_info->sensor_width; + *height = go->board_info->sensor_height; + break; + } +} + +static void set_formatting(struct go7007 *go) +{ + if (go->format == V4L2_PIX_FMT_MJPEG) { + go->pali = 0; + go->aspect_ratio = GO7007_RATIO_1_1; + go->gop_size = 0; + go->ipb = 0; + go->closed_gop = 0; + go->repeat_seqhead = 0; + go->seq_header_enable = 0; + go->gop_header_enable = 0; + go->dvd_mode = 0; + return; + } + + switch (go->format) { + case V4L2_PIX_FMT_MPEG1: + go->pali = 0; + break; + default: + case V4L2_PIX_FMT_MPEG2: + go->pali = 0x48; + break; + case V4L2_PIX_FMT_MPEG4: + /* For future reference: this is the list of MPEG4 + * profiles that are available, although they are + * untested: + * + * Profile pali + * -------------- ---- + * PROFILE_S_L0 0x08 + * PROFILE_S_L1 0x01 + * PROFILE_S_L2 0x02 + * PROFILE_S_L3 0x03 + * PROFILE_ARTS_L1 0x91 + * PROFILE_ARTS_L2 0x92 + * PROFILE_ARTS_L3 0x93 + * PROFILE_ARTS_L4 0x94 + * PROFILE_AS_L0 0xf0 + * PROFILE_AS_L1 0xf1 + * PROFILE_AS_L2 0xf2 + * PROFILE_AS_L3 0xf3 + * PROFILE_AS_L4 0xf4 + * PROFILE_AS_L5 0xf5 + */ + go->pali = 0xf5; + break; + } + go->gop_size = v4l2_ctrl_g_ctrl(go->mpeg_video_gop_size); + go->closed_gop = v4l2_ctrl_g_ctrl(go->mpeg_video_gop_closure); + go->ipb = v4l2_ctrl_g_ctrl(go->mpeg_video_b_frames) != 0; + go->bitrate = v4l2_ctrl_g_ctrl(go->mpeg_video_bitrate); + go->repeat_seqhead = v4l2_ctrl_g_ctrl(go->mpeg_video_rep_seqheader); + go->gop_header_enable = 1; + go->dvd_mode = 0; + if (go->format == V4L2_PIX_FMT_MPEG2) + go->dvd_mode = + go->bitrate == 9800000 && + go->gop_size == 15 && + go->ipb == 0 && + go->repeat_seqhead == 1 && + go->closed_gop; + + switch (v4l2_ctrl_g_ctrl(go->mpeg_video_aspect_ratio)) { + default: + case V4L2_MPEG_VIDEO_ASPECT_1x1: + go->aspect_ratio = GO7007_RATIO_1_1; + break; + case V4L2_MPEG_VIDEO_ASPECT_4x3: + go->aspect_ratio = GO7007_RATIO_4_3; + break; + case V4L2_MPEG_VIDEO_ASPECT_16x9: + go->aspect_ratio = GO7007_RATIO_16_9; break; } +} + +static int set_capture_size(struct go7007 *go, struct v4l2_format *fmt, int try) +{ + int sensor_height = 0, sensor_width = 0; + int width, height, i; + + if (fmt != NULL && !valid_pixelformat(fmt->fmt.pix.pixelformat)) + return -EINVAL; + + get_resolution(go, &sensor_width, &sensor_height); if (fmt == NULL) { width = sensor_width; @@ -205,13 +215,12 @@ static int set_capture_size(struct go7007 *go, struct v4l2_format *fmt, int try) else height = fmt->fmt.pix.height & ~0x0f; } else { - int requested_size = fmt->fmt.pix.width * fmt->fmt.pix.height; - int sensor_size = sensor_width * sensor_height; + width = fmt->fmt.pix.width; - if (64 * requested_size < 9 * sensor_size) { + if (width <= sensor_width / 4) { width = sensor_width / 4; height = sensor_height / 4; - } else if (64 * requested_size < 36 * sensor_size) { + } else if (width <= sensor_width / 2) { width = sensor_width / 2; height = sensor_height / 2; } else { @@ -233,12 +242,14 @@ static int set_capture_size(struct go7007 *go, struct v4l2_format *fmt, int try) fmt->fmt.pix.field = V4L2_FIELD_NONE; fmt->fmt.pix.bytesperline = 0; fmt->fmt.pix.sizeimage = GO7007_BUF_SIZE; - fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; /* ?? */ + fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; } if (try) return 0; + if (fmt) + go->format = fmt->fmt.pix.pixelformat; go->width = width; go->height = height; go->encoder_h_offset = go->board_info->sensor_h_offset; @@ -252,18 +263,11 @@ static int set_capture_size(struct go7007 *go, struct v4l2_format *fmt, int try) struct v4l2_mbus_framefmt mbus_fmt; mbus_fmt.code = V4L2_MBUS_FMT_FIXED; - if (fmt != NULL) - mbus_fmt.width = fmt->fmt.pix.width; - else - mbus_fmt.width = width; - - if (height > sensor_height / 2) { - mbus_fmt.height = height / 2; - go->encoder_v_halve = 0; - } else { - mbus_fmt.height = height; - go->encoder_v_halve = 1; - } + mbus_fmt.width = fmt ? fmt->fmt.pix.width : width; + mbus_fmt.height = height; + go->encoder_h_halve = 0; + go->encoder_v_halve = 0; + go->encoder_subsample = 0; call_all(&go->v4l2_dev, video, s_mbus_fmt, &mbus_fmt); } else { if (width <= sensor_width / 4) { @@ -280,55 +284,6 @@ static int set_capture_size(struct go7007 *go, struct v4l2_format *fmt, int try) go->encoder_subsample = 0; } } - - if (fmt == NULL) - return 0; - - switch (fmt->fmt.pix.pixelformat) { - case V4L2_PIX_FMT_MPEG: - if (go->format == GO7007_FORMAT_MPEG1 || - go->format == GO7007_FORMAT_MPEG2 || - go->format == GO7007_FORMAT_MPEG4) - break; - go->format = GO7007_FORMAT_MPEG1; - go->pali = 0; - go->aspect_ratio = GO7007_RATIO_1_1; - go->gop_size = go->sensor_framerate / 1000; - go->ipb = 0; - go->closed_gop = 1; - go->repeat_seqhead = 1; - go->seq_header_enable = 1; - go->gop_header_enable = 1; - go->dvd_mode = 0; - break; - /* Backwards compatibility only! */ - case V4L2_PIX_FMT_MPEG4: - if (go->format == GO7007_FORMAT_MPEG4) - break; - go->format = GO7007_FORMAT_MPEG4; - go->pali = 0xf5; - go->aspect_ratio = GO7007_RATIO_1_1; - go->gop_size = go->sensor_framerate / 1000; - go->ipb = 0; - go->closed_gop = 1; - go->repeat_seqhead = 1; - go->seq_header_enable = 1; - go->gop_header_enable = 1; - go->dvd_mode = 0; - break; - case V4L2_PIX_FMT_MJPEG: - go->format = GO7007_FORMAT_MJPEG; - go->pali = 0; - go->aspect_ratio = GO7007_RATIO_1_1; - go->gop_size = 0; - go->ipb = 0; - go->closed_gop = 0; - go->repeat_seqhead = 0; - go->seq_header_enable = 0; - go->gop_header_enable = 0; - go->dvd_mode = 0; - break; - } return 0; } @@ -390,230 +345,23 @@ static int clip_to_modet_map(struct go7007 *go, int region, } #endif -static int mpeg_query_ctrl(struct v4l2_queryctrl *ctrl) -{ - static const u32 mpeg_ctrls[] = { - V4L2_CID_MPEG_CLASS, - V4L2_CID_MPEG_STREAM_TYPE, - V4L2_CID_MPEG_VIDEO_ENCODING, - V4L2_CID_MPEG_VIDEO_ASPECT, - V4L2_CID_MPEG_VIDEO_GOP_SIZE, - V4L2_CID_MPEG_VIDEO_GOP_CLOSURE, - V4L2_CID_MPEG_VIDEO_BITRATE, - 0 - }; - static const u32 *ctrl_classes[] = { - mpeg_ctrls, - NULL - }; - - ctrl->id = v4l2_ctrl_next(ctrl_classes, ctrl->id); - - switch (ctrl->id) { - case V4L2_CID_MPEG_CLASS: - return v4l2_ctrl_query_fill(ctrl, 0, 0, 0, 0); - case V4L2_CID_MPEG_STREAM_TYPE: - return v4l2_ctrl_query_fill(ctrl, - V4L2_MPEG_STREAM_TYPE_MPEG2_DVD, - V4L2_MPEG_STREAM_TYPE_MPEG_ELEM, 1, - V4L2_MPEG_STREAM_TYPE_MPEG_ELEM); - case V4L2_CID_MPEG_VIDEO_ENCODING: - return v4l2_ctrl_query_fill(ctrl, - V4L2_MPEG_VIDEO_ENCODING_MPEG_1, - V4L2_MPEG_VIDEO_ENCODING_MPEG_4, 1, - V4L2_MPEG_VIDEO_ENCODING_MPEG_2); - case V4L2_CID_MPEG_VIDEO_ASPECT: - return v4l2_ctrl_query_fill(ctrl, - V4L2_MPEG_VIDEO_ASPECT_1x1, - V4L2_MPEG_VIDEO_ASPECT_16x9, 1, - V4L2_MPEG_VIDEO_ASPECT_1x1); - case V4L2_CID_MPEG_VIDEO_GOP_SIZE: - return v4l2_ctrl_query_fill(ctrl, 0, 34, 1, 15); - case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: - return v4l2_ctrl_query_fill(ctrl, 0, 1, 1, 0); - case V4L2_CID_MPEG_VIDEO_BITRATE: - return v4l2_ctrl_query_fill(ctrl, - 64000, - 10000000, 1, - 1500000); - default: - return -EINVAL; - } - return 0; -} - -static int mpeg_s_ctrl(struct v4l2_control *ctrl, struct go7007 *go) -{ - /* pretty sure we can't change any of these while streaming */ - if (go->streaming) - return -EBUSY; - - switch (ctrl->id) { - case V4L2_CID_MPEG_STREAM_TYPE: - switch (ctrl->value) { - case V4L2_MPEG_STREAM_TYPE_MPEG2_DVD: - go->format = GO7007_FORMAT_MPEG2; - go->bitrate = 9800000; - go->gop_size = 15; - go->pali = 0x48; - go->closed_gop = 1; - go->repeat_seqhead = 0; - go->seq_header_enable = 1; - go->gop_header_enable = 1; - go->dvd_mode = 1; - break; - case V4L2_MPEG_STREAM_TYPE_MPEG_ELEM: - /* todo: */ - break; - default: - return -EINVAL; - } - break; - case V4L2_CID_MPEG_VIDEO_ENCODING: - switch (ctrl->value) { - case V4L2_MPEG_VIDEO_ENCODING_MPEG_1: - go->format = GO7007_FORMAT_MPEG1; - go->pali = 0; - break; - case V4L2_MPEG_VIDEO_ENCODING_MPEG_2: - go->format = GO7007_FORMAT_MPEG2; - /*if (mpeg->pali >> 24 == 2) - go->pali = mpeg->pali & 0xff; - else*/ - go->pali = 0x48; - break; - case V4L2_MPEG_VIDEO_ENCODING_MPEG_4: - go->format = GO7007_FORMAT_MPEG4; - /*if (mpeg->pali >> 24 == 4) - go->pali = mpeg->pali & 0xff; - else*/ - go->pali = 0xf5; - break; - default: - return -EINVAL; - } - go->gop_header_enable = - /*mpeg->flags & GO7007_MPEG_OMIT_GOP_HEADER - ? 0 :*/ 1; - /*if (mpeg->flags & GO7007_MPEG_REPEAT_SEQHEADER) - go->repeat_seqhead = 1; - else*/ - go->repeat_seqhead = 0; - go->dvd_mode = 0; - break; - case V4L2_CID_MPEG_VIDEO_ASPECT: - if (go->format == GO7007_FORMAT_MJPEG) - return -EINVAL; - switch (ctrl->value) { - case V4L2_MPEG_VIDEO_ASPECT_1x1: - go->aspect_ratio = GO7007_RATIO_1_1; - break; - case V4L2_MPEG_VIDEO_ASPECT_4x3: - go->aspect_ratio = GO7007_RATIO_4_3; - break; - case V4L2_MPEG_VIDEO_ASPECT_16x9: - go->aspect_ratio = GO7007_RATIO_16_9; - break; - case V4L2_MPEG_VIDEO_ASPECT_221x100: - default: - return -EINVAL; - } - break; - case V4L2_CID_MPEG_VIDEO_GOP_SIZE: - if (ctrl->value < 0 || ctrl->value > 34) - return -EINVAL; - go->gop_size = ctrl->value; - break; - case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: - if (ctrl->value != 0 && ctrl->value != 1) - return -EINVAL; - go->closed_gop = ctrl->value; - break; - case V4L2_CID_MPEG_VIDEO_BITRATE: - /* Upper bound is kind of arbitrary here */ - if (ctrl->value < 64000 || ctrl->value > 10000000) - return -EINVAL; - go->bitrate = ctrl->value; - break; - default: - return -EINVAL; - } - return 0; -} - -static int mpeg_g_ctrl(struct v4l2_control *ctrl, struct go7007 *go) -{ - switch (ctrl->id) { - case V4L2_CID_MPEG_STREAM_TYPE: - if (go->dvd_mode) - ctrl->value = V4L2_MPEG_STREAM_TYPE_MPEG2_DVD; - else - ctrl->value = V4L2_MPEG_STREAM_TYPE_MPEG_ELEM; - break; - case V4L2_CID_MPEG_VIDEO_ENCODING: - switch (go->format) { - case GO7007_FORMAT_MPEG1: - ctrl->value = V4L2_MPEG_VIDEO_ENCODING_MPEG_1; - break; - case GO7007_FORMAT_MPEG2: - ctrl->value = V4L2_MPEG_VIDEO_ENCODING_MPEG_2; - break; - case GO7007_FORMAT_MPEG4: - ctrl->value = V4L2_MPEG_VIDEO_ENCODING_MPEG_4; - break; - default: - return -EINVAL; - } - break; - case V4L2_CID_MPEG_VIDEO_ASPECT: - switch (go->aspect_ratio) { - case GO7007_RATIO_1_1: - ctrl->value = V4L2_MPEG_VIDEO_ASPECT_1x1; - break; - case GO7007_RATIO_4_3: - ctrl->value = V4L2_MPEG_VIDEO_ASPECT_4x3; - break; - case GO7007_RATIO_16_9: - ctrl->value = V4L2_MPEG_VIDEO_ASPECT_16x9; - break; - default: - return -EINVAL; - } - break; - case V4L2_CID_MPEG_VIDEO_GOP_SIZE: - ctrl->value = go->gop_size; - break; - case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: - ctrl->value = go->closed_gop; - break; - case V4L2_CID_MPEG_VIDEO_BITRATE: - ctrl->value = go->bitrate; - break; - default: - return -EINVAL; - } - return 0; -} - static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { - struct go7007 *go = ((struct go7007_file *) priv)->go; + struct go7007 *go = video_drvdata(file); strlcpy(cap->driver, "go7007", sizeof(cap->driver)); strlcpy(cap->card, go->name, sizeof(cap->card)); -#if 0 - strlcpy(cap->bus_info, dev_name(&dev->udev->dev), sizeof(cap->bus_info)); -#endif - - cap->version = KERNEL_VERSION(0, 9, 8); + strlcpy(cap->bus_info, go->bus_info, sizeof(cap->bus_info)); - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_STREAMING; /* | V4L2_CAP_AUDIO; */ + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; + if (go->board_info->num_aud_inputs) + cap->device_caps |= V4L2_CAP_AUDIO; if (go->board_info->flags & GO7007_BOARD_HAS_TUNER) - cap->capabilities |= V4L2_CAP_TUNER; - + cap->device_caps |= V4L2_CAP_TUNER; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -625,11 +373,19 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, switch (fmt->index) { case 0: fmt->pixelformat = V4L2_PIX_FMT_MJPEG; - desc = "Motion-JPEG"; + desc = "Motion JPEG"; break; case 1: - fmt->pixelformat = V4L2_PIX_FMT_MPEG; - desc = "MPEG1/MPEG2/MPEG4"; + fmt->pixelformat = V4L2_PIX_FMT_MPEG1; + desc = "MPEG-1 ES"; + break; + case 2: + fmt->pixelformat = V4L2_PIX_FMT_MPEG2; + desc = "MPEG-2 ES"; + break; + case 3: + fmt->pixelformat = V4L2_PIX_FMT_MPEG4; + desc = "MPEG-4 ES"; break; default: return -EINVAL; @@ -645,13 +401,12 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *fmt) { - struct go7007 *go = ((struct go7007_file *) priv)->go; + struct go7007 *go = video_drvdata(file); fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fmt->fmt.pix.width = go->width; fmt->fmt.pix.height = go->height; - fmt->fmt.pix.pixelformat = (go->format == GO7007_FORMAT_MJPEG) ? - V4L2_PIX_FMT_MJPEG : V4L2_PIX_FMT_MPEG; + fmt->fmt.pix.pixelformat = go->format; fmt->fmt.pix.field = V4L2_FIELD_NONE; fmt->fmt.pix.bytesperline = 0; fmt->fmt.pix.sizeimage = GO7007_BUF_SIZE; @@ -663,7 +418,7 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *fmt) { - struct go7007 *go = ((struct go7007_file *) priv)->go; + struct go7007 *go = video_drvdata(file); return set_capture_size(go, fmt, 1); } @@ -671,348 +426,137 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *fmt) { - struct go7007 *go = ((struct go7007_file *) priv)->go; + struct go7007 *go = video_drvdata(file); - if (go->streaming) + if (vb2_is_busy(&go->vidq)) return -EBUSY; return set_capture_size(go, fmt, 0); } -static int vidioc_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *req) +static int go7007_queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt, + unsigned int *num_buffers, unsigned int *num_planes, + unsigned int sizes[], void *alloc_ctxs[]) { - struct go7007_file *gofh = priv; - struct go7007 *go = gofh->go; - int retval = -EBUSY; - unsigned int count, i; - - if (go->streaming) - return retval; - - if (req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - req->memory != V4L2_MEMORY_MMAP) - return -EINVAL; - - mutex_lock(&gofh->lock); - for (i = 0; i < gofh->buf_count; ++i) - if (gofh->bufs[i].mapped > 0) - goto unlock_and_return; - - mutex_lock(&go->hw_lock); - if (go->in_use > 0 && gofh->buf_count == 0) { - mutex_unlock(&go->hw_lock); - goto unlock_and_return; - } + sizes[0] = GO7007_BUF_SIZE; + *num_planes = 1; - if (gofh->buf_count > 0) - kfree(gofh->bufs); - - retval = -ENOMEM; - count = req->count; - if (count > 0) { - if (count < 2) - count = 2; - if (count > 32) - count = 32; - - gofh->bufs = kcalloc(count, sizeof(struct go7007_buffer), - GFP_KERNEL); - - if (!gofh->bufs) { - mutex_unlock(&go->hw_lock); - goto unlock_and_return; - } - - for (i = 0; i < count; ++i) { - gofh->bufs[i].go = go; - gofh->bufs[i].index = i; - gofh->bufs[i].state = BUF_STATE_IDLE; - gofh->bufs[i].mapped = 0; - } - - go->in_use = 1; - } else { - go->in_use = 0; - } - - gofh->buf_count = count; - mutex_unlock(&go->hw_lock); - mutex_unlock(&gofh->lock); - - memset(req, 0, sizeof(*req)); - - req->count = count; - req->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - req->memory = V4L2_MEMORY_MMAP; - - return 0; - -unlock_and_return: - mutex_unlock(&gofh->lock); - return retval; -} - -static int vidioc_querybuf(struct file *file, void *priv, - struct v4l2_buffer *buf) -{ - struct go7007_file *gofh = priv; - int retval = -EINVAL; - unsigned int index; - - if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return retval; - - index = buf->index; - - mutex_lock(&gofh->lock); - if (index >= gofh->buf_count) - goto unlock_and_return; - - memset(buf, 0, sizeof(*buf)); - buf->index = index; - buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - switch (gofh->bufs[index].state) { - case BUF_STATE_QUEUED: - buf->flags = V4L2_BUF_FLAG_QUEUED; - break; - case BUF_STATE_DONE: - buf->flags = V4L2_BUF_FLAG_DONE; - break; - default: - buf->flags = 0; - } - - if (gofh->bufs[index].mapped) - buf->flags |= V4L2_BUF_FLAG_MAPPED; - buf->memory = V4L2_MEMORY_MMAP; - buf->m.offset = index * GO7007_BUF_SIZE; - buf->length = GO7007_BUF_SIZE; - mutex_unlock(&gofh->lock); + if (*num_buffers < 2) + *num_buffers = 2; return 0; - -unlock_and_return: - mutex_unlock(&gofh->lock); - return retval; } -static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) +static void go7007_buf_queue(struct vb2_buffer *vb) { - struct go7007_file *gofh = priv; - struct go7007 *go = gofh->go; - struct go7007_buffer *gobuf; + struct vb2_queue *vq = vb->vb2_queue; + struct go7007 *go = vb2_get_drv_priv(vq); + struct go7007_buffer *go7007_vb = + container_of(vb, struct go7007_buffer, vb); unsigned long flags; - int retval = -EINVAL; - int ret; - if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - buf->memory != V4L2_MEMORY_MMAP) - return retval; - - mutex_lock(&gofh->lock); - if (buf->index >= gofh->buf_count) - goto unlock_and_return; - - gobuf = &gofh->bufs[buf->index]; - if (!gobuf->mapped) - goto unlock_and_return; - - retval = -EBUSY; - if (gobuf->state != BUF_STATE_IDLE) - goto unlock_and_return; - - /* offset will be 0 until we really support USERPTR streaming */ - gobuf->offset = gobuf->user_addr & ~PAGE_MASK; - gobuf->bytesused = 0; - gobuf->frame_offset = 0; - gobuf->modet_active = 0; - if (gobuf->offset > 0) - gobuf->page_count = GO7007_BUF_PAGES + 1; - else - gobuf->page_count = GO7007_BUF_PAGES; - - retval = -ENOMEM; - down_read(¤t->mm->mmap_sem); - ret = get_user_pages(current, current->mm, - gobuf->user_addr & PAGE_MASK, gobuf->page_count, - 1, 1, gobuf->pages, NULL); - up_read(¤t->mm->mmap_sem); - - if (ret != gobuf->page_count) { - int i; - for (i = 0; i < ret; ++i) - page_cache_release(gobuf->pages[i]); - gobuf->page_count = 0; - goto unlock_and_return; - } - - gobuf->state = BUF_STATE_QUEUED; spin_lock_irqsave(&go->spinlock, flags); - list_add_tail(&gobuf->stream, &go->stream); + list_add_tail(&go7007_vb->list, &go->vidq_active); spin_unlock_irqrestore(&go->spinlock, flags); - mutex_unlock(&gofh->lock); +} - return 0; +static int go7007_buf_prepare(struct vb2_buffer *vb) +{ + struct go7007_buffer *go7007_vb = + container_of(vb, struct go7007_buffer, vb); -unlock_and_return: - mutex_unlock(&gofh->lock); - return retval; + go7007_vb->modet_active = 0; + go7007_vb->frame_offset = 0; + vb->v4l2_planes[0].bytesused = 0; + return 0; } - -static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) +static int go7007_buf_finish(struct vb2_buffer *vb) { - struct go7007_file *gofh = priv; - struct go7007 *go = gofh->go; - struct go7007_buffer *gobuf; - int retval = -EINVAL; - unsigned long flags; - u32 frame_type_flag; - DEFINE_WAIT(wait); - - if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return retval; - if (buf->memory != V4L2_MEMORY_MMAP) - return retval; - - mutex_lock(&gofh->lock); - if (list_empty(&go->stream)) - goto unlock_and_return; - gobuf = list_entry(go->stream.next, - struct go7007_buffer, stream); - - retval = -EAGAIN; - if (gobuf->state != BUF_STATE_DONE && - !(file->f_flags & O_NONBLOCK)) { - for (;;) { - prepare_to_wait(&go->frame_waitq, &wait, - TASK_INTERRUPTIBLE); - if (gobuf->state == BUF_STATE_DONE) - break; - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } - schedule(); - } - finish_wait(&go->frame_waitq, &wait); - } - if (gobuf->state != BUF_STATE_DONE) - goto unlock_and_return; - - spin_lock_irqsave(&go->spinlock, flags); - deactivate_buffer(gobuf); - spin_unlock_irqrestore(&go->spinlock, flags); - frame_type_flag = get_frame_type_flag(gobuf, go->format); - gobuf->state = BUF_STATE_IDLE; - - memset(buf, 0, sizeof(*buf)); - buf->index = gobuf->index; - buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - buf->bytesused = gobuf->bytesused; - buf->flags = V4L2_BUF_FLAG_MAPPED | frame_type_flag; + struct vb2_queue *vq = vb->vb2_queue; + struct go7007 *go = vb2_get_drv_priv(vq); + struct go7007_buffer *go7007_vb = + container_of(vb, struct go7007_buffer, vb); + u32 frame_type_flag = get_frame_type_flag(go7007_vb, go->format); + struct v4l2_buffer *buf = &vb->v4l2_buf; + + buf->flags &= ~(V4L2_BUF_FLAG_KEYFRAME | V4L2_BUF_FLAG_BFRAME | + V4L2_BUF_FLAG_PFRAME); + buf->flags |= frame_type_flag; buf->field = V4L2_FIELD_NONE; - buf->timestamp = gobuf->timestamp; - buf->sequence = gobuf->seq; - buf->memory = V4L2_MEMORY_MMAP; - buf->m.offset = gobuf->index * GO7007_BUF_SIZE; - buf->length = GO7007_BUF_SIZE; - buf->reserved = gobuf->modet_active; - - mutex_unlock(&gofh->lock); return 0; - -unlock_and_return: - mutex_unlock(&gofh->lock); - return retval; } -static int vidioc_streamon(struct file *file, void *priv, - enum v4l2_buf_type type) +static int go7007_start_streaming(struct vb2_queue *q, unsigned int count) { - struct go7007_file *gofh = priv; - struct go7007 *go = gofh->go; - int retval = 0; - - if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; + struct go7007 *go = vb2_get_drv_priv(q); + int ret; - mutex_lock(&gofh->lock); + set_formatting(go); mutex_lock(&go->hw_lock); - - if (!go->streaming) { - go->streaming = 1; - go->next_seq = 0; - go->active_buf = NULL; - if (go7007_start_encoder(go) < 0) - retval = -EIO; - else - retval = 0; - } + go->next_seq = 0; + go->active_buf = NULL; + q->streaming = 1; + if (go7007_start_encoder(go) < 0) + ret = -EIO; + else + ret = 0; mutex_unlock(&go->hw_lock); - mutex_unlock(&gofh->lock); + if (ret) { + q->streaming = 0; + return ret; + } call_all(&go->v4l2_dev, video, s_stream, 1); - - return retval; + v4l2_ctrl_grab(go->mpeg_video_gop_size, true); + v4l2_ctrl_grab(go->mpeg_video_gop_closure, true); + v4l2_ctrl_grab(go->mpeg_video_bitrate, true); + v4l2_ctrl_grab(go->mpeg_video_aspect_ratio, true); + /* Turn on Capture LED */ + if (go->board_id == GO7007_BOARDID_ADS_USBAV_709) + go7007_write_addr(go, 0x3c82, 0x0005); + return ret; } -static int vidioc_streamoff(struct file *file, void *priv, - enum v4l2_buf_type type) +static int go7007_stop_streaming(struct vb2_queue *q) { - struct go7007_file *gofh = priv; - struct go7007 *go = gofh->go; + struct go7007 *go = vb2_get_drv_priv(q); + unsigned long flags; - if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - mutex_lock(&gofh->lock); - go7007_streamoff(go); - mutex_unlock(&gofh->lock); + q->streaming = 0; + go7007_stream_stop(go); + mutex_lock(&go->hw_lock); + go7007_reset_encoder(go); + mutex_unlock(&go->hw_lock); call_all(&go->v4l2_dev, video, s_stream, 0); + spin_lock_irqsave(&go->spinlock, flags); + INIT_LIST_HEAD(&go->vidq_active); + spin_unlock_irqrestore(&go->spinlock, flags); + v4l2_ctrl_grab(go->mpeg_video_gop_size, false); + v4l2_ctrl_grab(go->mpeg_video_gop_closure, false); + v4l2_ctrl_grab(go->mpeg_video_bitrate, false); + v4l2_ctrl_grab(go->mpeg_video_aspect_ratio, false); + /* Turn on Capture LED */ + if (go->board_id == GO7007_BOARDID_ADS_USBAV_709) + go7007_write_addr(go, 0x3c82, 0x000d); return 0; } -static int vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *query) -{ - struct go7007 *go = ((struct go7007_file *) priv)->go; - int id = query->id; - - if (0 == call_all(&go->v4l2_dev, core, queryctrl, query)) - return 0; - - query->id = id; - return mpeg_query_ctrl(query); -} - -static int vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct go7007 *go = ((struct go7007_file *) priv)->go; - - if (0 == call_all(&go->v4l2_dev, core, g_ctrl, ctrl)) - return 0; - - return mpeg_g_ctrl(ctrl, go); -} - -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct go7007 *go = ((struct go7007_file *) priv)->go; - - if (0 == call_all(&go->v4l2_dev, core, s_ctrl, ctrl)) - return 0; - - return mpeg_s_ctrl(ctrl, go); -} +static struct vb2_ops go7007_video_qops = { + .queue_setup = go7007_queue_setup, + .buf_queue = go7007_buf_queue, + .buf_prepare = go7007_buf_prepare, + .buf_finish = go7007_buf_finish, + .start_streaming = go7007_start_streaming, + .stop_streaming = go7007_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; static int vidioc_g_parm(struct file *filp, void *priv, struct v4l2_streamparm *parm) { - struct go7007 *go = ((struct go7007_file *) priv)->go; + struct go7007 *go = video_drvdata(filp); struct v4l2_fract timeperframe = { .numerator = 1001 * go->fps_scale, .denominator = go->sensor_framerate, @@ -1021,7 +565,8 @@ static int vidioc_g_parm(struct file *filp, void *priv, if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - parm->parm.capture.capability |= V4L2_CAP_TIMEPERFRAME; + parm->parm.capture.readbuffers = 2; + parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; parm->parm.capture.timeperframe = timeperframe; return 0; @@ -1030,13 +575,11 @@ static int vidioc_g_parm(struct file *filp, void *priv, static int vidioc_s_parm(struct file *filp, void *priv, struct v4l2_streamparm *parm) { - struct go7007 *go = ((struct go7007_file *) priv)->go; + struct go7007 *go = video_drvdata(filp); unsigned int n, d; if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - if (parm->parm.capture.capturemode != 0) - return -EINVAL; n = go->sensor_framerate * parm->parm.capture.timeperframe.numerator; @@ -1046,7 +589,7 @@ static int vidioc_s_parm(struct file *filp, void *priv, else go->fps_scale = 1; - return 0; + return vidioc_g_parm(filp, priv, parm); } /* VIDIOC_ENUMSTD on go7007 were used for enumerating the supported fps and @@ -1062,121 +605,96 @@ static int vidioc_s_parm(struct file *filp, void *priv, static int vidioc_enum_framesizes(struct file *filp, void *priv, struct v4l2_frmsizeenum *fsize) { - struct go7007 *go = ((struct go7007_file *) priv)->go; + struct go7007 *go = video_drvdata(filp); + int width, height; - /* Return -EINVAL, if it is a TV board */ - if ((go->board_info->flags & GO7007_BOARD_HAS_TUNER) || - (go->board_info->sensor_flags & GO7007_SENSOR_TV)) + if (fsize->index > 2) return -EINVAL; - if (fsize->index > 0) + if (!valid_pixelformat(fsize->pixel_format)) return -EINVAL; + get_resolution(go, &width, &height); fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; - fsize->discrete.width = go->board_info->sensor_width; - fsize->discrete.height = go->board_info->sensor_height; - + fsize->discrete.width = (width >> fsize->index) & ~0xf; + fsize->discrete.height = (height >> fsize->index) & ~0xf; return 0; } static int vidioc_enum_frameintervals(struct file *filp, void *priv, struct v4l2_frmivalenum *fival) { - struct go7007 *go = ((struct go7007_file *) priv)->go; + struct go7007 *go = video_drvdata(filp); + int width, height; + int i; - /* Return -EINVAL, if it is a TV board */ - if ((go->board_info->flags & GO7007_BOARD_HAS_TUNER) || - (go->board_info->sensor_flags & GO7007_SENSOR_TV)) + if (fival->index > 4) return -EINVAL; - if (fival->index > 0) + if (!valid_pixelformat(fival->pixel_format)) return -EINVAL; + if (!(go->board_info->sensor_flags & GO7007_SENSOR_SCALING)) { + get_resolution(go, &width, &height); + for (i = 0; i <= 2; i++) + if (fival->width == ((width >> i) & ~0xf) && + fival->height == ((height >> i) & ~0xf)) + break; + if (i > 2) + return -EINVAL; + } fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; - fival->discrete.numerator = 1001; - fival->discrete.denominator = go->board_info->sensor_framerate; - + fival->discrete.numerator = 1001 * (fival->index + 1); + fival->discrete.denominator = go->sensor_framerate; return 0; } static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *std) { - struct go7007 *go = ((struct go7007_file *) priv)->go; + struct go7007 *go = video_drvdata(file); - switch (go->standard) { - case GO7007_STD_NTSC: - *std = V4L2_STD_NTSC; - break; - case GO7007_STD_PAL: - *std = V4L2_STD_PAL; - break; - default: - return -EINVAL; + *std = go->std; + return 0; +} + +static int go7007_s_std(struct go7007 *go) +{ + if (go->std & V4L2_STD_625_50) { + go->standard = GO7007_STD_PAL; + go->sensor_framerate = 25025; + } else { + go->standard = GO7007_STD_NTSC; + go->sensor_framerate = 30000; } + call_all(&go->v4l2_dev, core, s_std, go->std); + set_capture_size(go, NULL, 0); return 0; } -static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *std) +static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id std) { - struct go7007 *go = ((struct go7007_file *) priv)->go; + struct go7007 *go = video_drvdata(file); - if (go->streaming) + if (vb2_is_busy(&go->vidq)) return -EBUSY; - if (!(go->board_info->sensor_flags & GO7007_SENSOR_TV) && *std != 0) - return -EINVAL; - - if (*std == 0) - return -EINVAL; - - if ((go->board_info->flags & GO7007_BOARD_HAS_TUNER) && - go->input == go->board_info->num_inputs - 1) { - if (!go->i2c_adapter_online) - return -EIO; - if (call_all(&go->v4l2_dev, core, s_std, *std) < 0) - return -EINVAL; - } - - if (*std & V4L2_STD_NTSC) { - go->standard = GO7007_STD_NTSC; - go->sensor_framerate = 30000; - } else if (*std & V4L2_STD_PAL) { - go->standard = GO7007_STD_PAL; - go->sensor_framerate = 25025; - } else if (*std & V4L2_STD_SECAM) { - go->standard = GO7007_STD_PAL; - go->sensor_framerate = 25025; - } else - return -EINVAL; - - call_all(&go->v4l2_dev, core, s_std, *std); - set_capture_size(go, NULL, 0); + go->std = std; - return 0; + return go7007_s_std(go); } static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *std) { - struct go7007 *go = ((struct go7007_file *) priv)->go; - - if ((go->board_info->flags & GO7007_BOARD_HAS_TUNER) && - go->input == go->board_info->num_inputs - 1) { - if (!go->i2c_adapter_online) - return -EIO; - return call_all(&go->v4l2_dev, video, querystd, std); - } else if (go->board_info->sensor_flags & GO7007_SENSOR_TV) - *std = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM; - else - *std = 0; + struct go7007 *go = video_drvdata(file); - return 0; + return call_all(&go->v4l2_dev, video, querystd, std); } static int vidioc_enum_input(struct file *file, void *priv, struct v4l2_input *inp) { - struct go7007 *go = ((struct go7007_file *) priv)->go; + struct go7007 *go = video_drvdata(file); if (inp->index >= go->board_info->num_inputs) return -EINVAL; @@ -1184,18 +702,20 @@ static int vidioc_enum_input(struct file *file, void *priv, strncpy(inp->name, go->board_info->inputs[inp->index].name, sizeof(inp->name)); - /* If this board has a tuner, it will be the last input */ + /* If this board has a tuner, it will be the first input */ if ((go->board_info->flags & GO7007_BOARD_HAS_TUNER) && - inp->index == go->board_info->num_inputs - 1) + inp->index == 0) inp->type = V4L2_INPUT_TYPE_TUNER; else inp->type = V4L2_INPUT_TYPE_CAMERA; - inp->audioset = 0; + if (go->board_info->num_aud_inputs) + inp->audioset = (1 << go->board_info->num_aud_inputs) - 1; + else + inp->audioset = 0; inp->tuner = 0; if (go->board_info->sensor_flags & GO7007_SENSOR_TV) - inp->std = V4L2_STD_NTSC | V4L2_STD_PAL | - V4L2_STD_SECAM; + inp->std = video_devdata(file)->tvnorms; else inp->std = 0; @@ -1205,203 +725,128 @@ static int vidioc_enum_input(struct file *file, void *priv, static int vidioc_g_input(struct file *file, void *priv, unsigned int *input) { - struct go7007 *go = ((struct go7007_file *) priv)->go; + struct go7007 *go = video_drvdata(file); *input = go->input; return 0; } -static int vidioc_s_input(struct file *file, void *priv, unsigned int input) +static int vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *a) { - struct go7007 *go = ((struct go7007_file *) priv)->go; + struct go7007 *go = video_drvdata(file); - if (input >= go->board_info->num_inputs) + if (a->index >= go->board_info->num_aud_inputs) return -EINVAL; - if (go->streaming) - return -EBUSY; + strlcpy(a->name, go->board_info->aud_inputs[a->index].name, sizeof(a->name)); + a->capability = V4L2_AUDCAP_STEREO; + return 0; +} - go->input = input; +static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a) +{ + struct go7007 *go = video_drvdata(file); - return call_all(&go->v4l2_dev, video, s_routing, input, 0, 0); + a->index = go->aud_input; + strlcpy(a->name, go->board_info->aud_inputs[go->aud_input].name, sizeof(a->name)); + a->capability = V4L2_AUDCAP_STEREO; + return 0; } -static int vidioc_g_tuner(struct file *file, void *priv, - struct v4l2_tuner *t) +static int vidioc_s_audio(struct file *file, void *fh, const struct v4l2_audio *a) { - struct go7007 *go = ((struct go7007_file *) priv)->go; + struct go7007 *go = video_drvdata(file); - if (!(go->board_info->flags & GO7007_BOARD_HAS_TUNER)) - return -EINVAL; - if (t->index != 0) + if (a->index >= go->board_info->num_aud_inputs) return -EINVAL; - if (!go->i2c_adapter_online) - return -EIO; - - return call_all(&go->v4l2_dev, tuner, g_tuner, t); + go->aud_input = a->index; + v4l2_subdev_call(go->sd_audio, audio, s_routing, + go->board_info->aud_inputs[go->aud_input].audio_input, 0, 0); + return 0; } -static int vidioc_s_tuner(struct file *file, void *priv, - struct v4l2_tuner *t) +static void go7007_s_input(struct go7007 *go) { - struct go7007 *go = ((struct go7007_file *) priv)->go; + unsigned int input = go->input; - if (!(go->board_info->flags & GO7007_BOARD_HAS_TUNER)) - return -EINVAL; - if (t->index != 0) - return -EINVAL; - if (!go->i2c_adapter_online) - return -EIO; - - switch (go->board_id) { - case GO7007_BOARDID_PX_TV402U_NA: - case GO7007_BOARDID_PX_TV402U_JP: - /* No selectable options currently */ - if (t->audmode != V4L2_TUNER_MODE_STEREO) - return -EINVAL; - break; - } + v4l2_subdev_call(go->sd_video, video, s_routing, + go->board_info->inputs[input].video_input, 0, + go->board_info->video_config); + if (go->board_info->num_aud_inputs) { + int aud_input = go->board_info->inputs[input].audio_index; - return call_all(&go->v4l2_dev, tuner, s_tuner, t); + v4l2_subdev_call(go->sd_audio, audio, s_routing, + go->board_info->aud_inputs[aud_input].audio_input, 0, 0); + go->aud_input = aud_input; + } } -static int vidioc_g_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) +static int vidioc_s_input(struct file *file, void *priv, unsigned int input) { - struct go7007 *go = ((struct go7007_file *) priv)->go; + struct go7007 *go = video_drvdata(file); - if (!(go->board_info->flags & GO7007_BOARD_HAS_TUNER)) + if (input >= go->board_info->num_inputs) return -EINVAL; - if (!go->i2c_adapter_online) - return -EIO; + if (vb2_is_busy(&go->vidq)) + return -EBUSY; - f->type = V4L2_TUNER_ANALOG_TV; + go->input = input; + go7007_s_input(go); - return call_all(&go->v4l2_dev, tuner, g_frequency, f); + return 0; } -static int vidioc_s_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) +static int vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *t) { - struct go7007 *go = ((struct go7007_file *) priv)->go; + struct go7007 *go = video_drvdata(file); - if (!(go->board_info->flags & GO7007_BOARD_HAS_TUNER)) + if (t->index != 0) return -EINVAL; - if (!go->i2c_adapter_online) - return -EIO; - return call_all(&go->v4l2_dev, tuner, s_frequency, f); + strlcpy(t->name, "Tuner", sizeof(t->name)); + return call_all(&go->v4l2_dev, tuner, g_tuner, t); } -static int vidioc_cropcap(struct file *file, void *priv, - struct v4l2_cropcap *cropcap) +static int vidioc_s_tuner(struct file *file, void *priv, + const struct v4l2_tuner *t) { - struct go7007 *go = ((struct go7007_file *) priv)->go; + struct go7007 *go = video_drvdata(file); - if (cropcap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + if (t->index != 0) return -EINVAL; - /* These specify the raw input of the sensor */ - switch (go->standard) { - case GO7007_STD_NTSC: - cropcap->bounds.top = 0; - cropcap->bounds.left = 0; - cropcap->bounds.width = 720; - cropcap->bounds.height = 480; - cropcap->defrect.top = 0; - cropcap->defrect.left = 0; - cropcap->defrect.width = 720; - cropcap->defrect.height = 480; - break; - case GO7007_STD_PAL: - cropcap->bounds.top = 0; - cropcap->bounds.left = 0; - cropcap->bounds.width = 720; - cropcap->bounds.height = 576; - cropcap->defrect.top = 0; - cropcap->defrect.left = 0; - cropcap->defrect.width = 720; - cropcap->defrect.height = 576; - break; - case GO7007_STD_OTHER: - cropcap->bounds.top = 0; - cropcap->bounds.left = 0; - cropcap->bounds.width = go->board_info->sensor_width; - cropcap->bounds.height = go->board_info->sensor_height; - cropcap->defrect.top = 0; - cropcap->defrect.left = 0; - cropcap->defrect.width = go->board_info->sensor_width; - cropcap->defrect.height = go->board_info->sensor_height; - break; - } - - return 0; + return call_all(&go->v4l2_dev, tuner, s_tuner, t); } -static int vidioc_g_crop(struct file *file, void *priv, struct v4l2_crop *crop) +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) { - struct go7007 *go = ((struct go7007_file *) priv)->go; + struct go7007 *go = video_drvdata(file); - if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + if (f->tuner) return -EINVAL; - crop->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - /* These specify the raw input of the sensor */ - switch (go->standard) { - case GO7007_STD_NTSC: - crop->c.top = 0; - crop->c.left = 0; - crop->c.width = 720; - crop->c.height = 480; - break; - case GO7007_STD_PAL: - crop->c.top = 0; - crop->c.left = 0; - crop->c.width = 720; - crop->c.height = 576; - break; - case GO7007_STD_OTHER: - crop->c.top = 0; - crop->c.left = 0; - crop->c.width = go->board_info->sensor_width; - crop->c.height = go->board_info->sensor_height; - break; - } - - return 0; + return call_all(&go->v4l2_dev, tuner, g_frequency, f); } -/* FIXME: vidioc_s_crop is not really implemented!!! - */ -static int vidioc_s_crop(struct file *file, void *priv, const struct v4l2_crop *crop) +static int vidioc_s_frequency(struct file *file, void *priv, + const struct v4l2_frequency *f) { - if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - return 0; -} + struct go7007 *go = video_drvdata(file); -static int vidioc_g_jpegcomp(struct file *file, void *priv, - struct v4l2_jpegcompression *params) -{ - memset(params, 0, sizeof(*params)); - params->quality = 50; /* ?? */ - params->jpeg_markers = V4L2_JPEG_MARKER_DHT | - V4L2_JPEG_MARKER_DQT; + if (f->tuner) + return -EINVAL; - return 0; + return call_all(&go->v4l2_dev, tuner, s_frequency, f); } -static int vidioc_s_jpegcomp(struct file *file, void *priv, - const struct v4l2_jpegcompression *params) +static int vidioc_log_status(struct file *file, void *priv) { - if (params->quality != 50 || - params->jpeg_markers != (V4L2_JPEG_MARKER_DHT | - V4L2_JPEG_MARKER_DQT)) - return -EINVAL; + struct go7007 *go = video_drvdata(file); - return 0; + v4l2_ctrl_log_status(file, priv); + return call_all(&go->v4l2_dev, core, log_status); } /* FIXME: @@ -1412,180 +857,6 @@ static int vidioc_s_jpegcomp(struct file *file, void *priv, */ #if 0 - /* Temporary ioctls for controlling compression characteristics */ - case GO7007IOC_S_BITRATE: - { - int *bitrate = arg; - - if (go->streaming) - return -EINVAL; - /* Upper bound is kind of arbitrary here */ - if (*bitrate < 64000 || *bitrate > 10000000) - return -EINVAL; - go->bitrate = *bitrate; - return 0; - } - case GO7007IOC_G_BITRATE: - { - int *bitrate = arg; - - *bitrate = go->bitrate; - return 0; - } - case GO7007IOC_S_COMP_PARAMS: - { - struct go7007_comp_params *comp = arg; - - if (go->format == GO7007_FORMAT_MJPEG) - return -EINVAL; - if (comp->gop_size > 0) - go->gop_size = comp->gop_size; - else - go->gop_size = go->sensor_framerate / 1000; - if (go->gop_size != 15) - go->dvd_mode = 0; - /*go->ipb = comp->max_b_frames > 0;*/ /* completely untested */ - if (go->board_info->sensor_flags & GO7007_SENSOR_TV) { - switch (comp->aspect_ratio) { - case GO7007_ASPECT_RATIO_4_3_NTSC: - case GO7007_ASPECT_RATIO_4_3_PAL: - go->aspect_ratio = GO7007_RATIO_4_3; - break; - case GO7007_ASPECT_RATIO_16_9_NTSC: - case GO7007_ASPECT_RATIO_16_9_PAL: - go->aspect_ratio = GO7007_RATIO_16_9; - break; - default: - go->aspect_ratio = GO7007_RATIO_1_1; - break; - } - } - if (comp->flags & GO7007_COMP_OMIT_SEQ_HEADER) { - go->dvd_mode = 0; - go->seq_header_enable = 0; - } else { - go->seq_header_enable = 1; - } - /* fall-through */ - } - case GO7007IOC_G_COMP_PARAMS: - { - struct go7007_comp_params *comp = arg; - - if (go->format == GO7007_FORMAT_MJPEG) - return -EINVAL; - memset(comp, 0, sizeof(*comp)); - comp->gop_size = go->gop_size; - comp->max_b_frames = go->ipb ? 2 : 0; - switch (go->aspect_ratio) { - case GO7007_RATIO_4_3: - if (go->standard == GO7007_STD_NTSC) - comp->aspect_ratio = - GO7007_ASPECT_RATIO_4_3_NTSC; - else - comp->aspect_ratio = - GO7007_ASPECT_RATIO_4_3_PAL; - break; - case GO7007_RATIO_16_9: - if (go->standard == GO7007_STD_NTSC) - comp->aspect_ratio = - GO7007_ASPECT_RATIO_16_9_NTSC; - else - comp->aspect_ratio = - GO7007_ASPECT_RATIO_16_9_PAL; - break; - default: - comp->aspect_ratio = GO7007_ASPECT_RATIO_1_1; - break; - } - if (go->closed_gop) - comp->flags |= GO7007_COMP_CLOSED_GOP; - if (!go->seq_header_enable) - comp->flags |= GO7007_COMP_OMIT_SEQ_HEADER; - return 0; - } - case GO7007IOC_S_MPEG_PARAMS: - { - struct go7007_mpeg_params *mpeg = arg; - - if (go->format != GO7007_FORMAT_MPEG1 && - go->format != GO7007_FORMAT_MPEG2 && - go->format != GO7007_FORMAT_MPEG4) - return -EINVAL; - - if (mpeg->flags & GO7007_MPEG_FORCE_DVD_MODE) { - go->format = GO7007_FORMAT_MPEG2; - go->bitrate = 9800000; - go->gop_size = 15; - go->pali = 0x48; - go->closed_gop = 1; - go->repeat_seqhead = 0; - go->seq_header_enable = 1; - go->gop_header_enable = 1; - go->dvd_mode = 1; - } else { - switch (mpeg->mpeg_video_standard) { - case GO7007_MPEG_VIDEO_MPEG1: - go->format = GO7007_FORMAT_MPEG1; - go->pali = 0; - break; - case GO7007_MPEG_VIDEO_MPEG2: - go->format = GO7007_FORMAT_MPEG2; - if (mpeg->pali >> 24 == 2) - go->pali = mpeg->pali & 0xff; - else - go->pali = 0x48; - break; - case GO7007_MPEG_VIDEO_MPEG4: - go->format = GO7007_FORMAT_MPEG4; - if (mpeg->pali >> 24 == 4) - go->pali = mpeg->pali & 0xff; - else - go->pali = 0xf5; - break; - default: - return -EINVAL; - } - go->gop_header_enable = - mpeg->flags & GO7007_MPEG_OMIT_GOP_HEADER - ? 0 : 1; - if (mpeg->flags & GO7007_MPEG_REPEAT_SEQHEADER) - go->repeat_seqhead = 1; - else - go->repeat_seqhead = 0; - go->dvd_mode = 0; - } - /* fall-through */ - } - case GO7007IOC_G_MPEG_PARAMS: - { - struct go7007_mpeg_params *mpeg = arg; - - memset(mpeg, 0, sizeof(*mpeg)); - switch (go->format) { - case GO7007_FORMAT_MPEG1: - mpeg->mpeg_video_standard = GO7007_MPEG_VIDEO_MPEG1; - mpeg->pali = 0; - break; - case GO7007_FORMAT_MPEG2: - mpeg->mpeg_video_standard = GO7007_MPEG_VIDEO_MPEG2; - mpeg->pali = GO7007_MPEG_PROFILE(2, go->pali); - break; - case GO7007_FORMAT_MPEG4: - mpeg->mpeg_video_standard = GO7007_MPEG_VIDEO_MPEG4; - mpeg->pali = GO7007_MPEG_PROFILE(4, go->pali); - break; - default: - return -EINVAL; - } - if (!go->gop_header_enable) - mpeg->flags |= GO7007_MPEG_OMIT_GOP_HEADER; - if (go->repeat_seqhead) - mpeg->flags |= GO7007_MPEG_REPEAT_SEQHEADER; - if (go->dvd_mode) - mpeg->flags |= GO7007_MPEG_FORCE_DVD_MODE; - return 0; - } case GO7007IOC_S_MD_PARAMS: { struct go7007_md_params *mdp = arg; @@ -1604,25 +875,6 @@ static int vidioc_s_jpegcomp(struct file *file, void *priv, go->modet[mdp->region].enable = 0; /* fall-through */ } - case GO7007IOC_G_MD_PARAMS: - { - struct go7007_md_params *mdp = arg; - int region = mdp->region; - - if (mdp->region > 3) - return -EINVAL; - memset(mdp, 0, sizeof(struct go7007_md_params)); - mdp->region = region; - if (!go->modet[region].enable) - return 0; - mdp->pixel_threshold = - (go->modet[region].pixel_threshold << 1) + 1; - mdp->motion_threshold = - (go->modet[region].motion_threshold << 1) + 1; - mdp->trigger = - (go->modet[region].mb_threshold << 1) + 1; - return 0; - } case GO7007IOC_S_MD_REGION: { struct go7007_md_region *region = arg; @@ -1633,116 +885,14 @@ static int vidioc_s_jpegcomp(struct file *file, void *priv, } #endif -static ssize_t go7007_read(struct file *file, char __user *data, - size_t count, loff_t *ppos) -{ - return -EINVAL; -} - -static void go7007_vm_open(struct vm_area_struct *vma) -{ - struct go7007_buffer *gobuf = vma->vm_private_data; - - ++gobuf->mapped; -} - -static void go7007_vm_close(struct vm_area_struct *vma) -{ - struct go7007_buffer *gobuf = vma->vm_private_data; - unsigned long flags; - - if (--gobuf->mapped == 0) { - spin_lock_irqsave(&gobuf->go->spinlock, flags); - deactivate_buffer(gobuf); - spin_unlock_irqrestore(&gobuf->go->spinlock, flags); - } -} - -/* Copied from videobuf-dma-sg.c */ -static int go7007_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) -{ - struct page *page; - - page = alloc_page(GFP_USER | __GFP_DMA32); - if (!page) - return VM_FAULT_OOM; - clear_user_highpage(page, (unsigned long)vmf->virtual_address); - vmf->page = page; - return 0; -} - -static struct vm_operations_struct go7007_vm_ops = { - .open = go7007_vm_open, - .close = go7007_vm_close, - .fault = go7007_vm_fault, -}; - -static int go7007_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct go7007_file *gofh = file->private_data; - unsigned int index; - - if (gofh->go->status != STATUS_ONLINE) - return -EIO; - if (!(vma->vm_flags & VM_SHARED)) - return -EINVAL; /* only support VM_SHARED mapping */ - if (vma->vm_end - vma->vm_start != GO7007_BUF_SIZE) - return -EINVAL; /* must map exactly one full buffer */ - mutex_lock(&gofh->lock); - index = vma->vm_pgoff / GO7007_BUF_PAGES; - if (index >= gofh->buf_count) { - mutex_unlock(&gofh->lock); - return -EINVAL; /* trying to map beyond requested buffers */ - } - if (index * GO7007_BUF_PAGES != vma->vm_pgoff) { - mutex_unlock(&gofh->lock); - return -EINVAL; /* offset is not aligned on buffer boundary */ - } - if (gofh->bufs[index].mapped > 0) { - mutex_unlock(&gofh->lock); - return -EBUSY; - } - gofh->bufs[index].mapped = 1; - gofh->bufs[index].user_addr = vma->vm_start; - vma->vm_ops = &go7007_vm_ops; - vma->vm_flags |= VM_DONTEXPAND; - vma->vm_flags &= ~VM_IO; - vma->vm_private_data = &gofh->bufs[index]; - mutex_unlock(&gofh->lock); - return 0; -} - -static unsigned int go7007_poll(struct file *file, poll_table *wait) -{ - struct go7007_file *gofh = file->private_data; - struct go7007_buffer *gobuf; - - if (list_empty(&gofh->go->stream)) - return POLLERR; - gobuf = list_entry(gofh->go->stream.next, struct go7007_buffer, stream); - poll_wait(file, &gofh->go->frame_waitq, wait); - if (gobuf->state == BUF_STATE_DONE) - return POLLIN | POLLRDNORM; - return 0; -} - -static void go7007_vfl_release(struct video_device *vfd) -{ - struct go7007 *go = video_get_drvdata(vfd); - - video_device_release(vfd); - if (--go->ref_count == 0) - kfree(go); -} - static struct v4l2_file_operations go7007_fops = { .owner = THIS_MODULE, - .open = go7007_open, - .release = go7007_release, - .ioctl = video_ioctl2, - .read = go7007_read, - .mmap = go7007_mmap, - .poll = go7007_poll, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .unlocked_ioctl = video_ioctl2, + .read = vb2_fop_read, + .mmap = vb2_fop_mmap, + .poll = vb2_fop_poll, }; static const struct v4l2_ioctl_ops video_ioctl_ops = { @@ -1751,21 +901,21 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .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_reqbufs = vidioc_reqbufs, - .vidioc_querybuf = vidioc_querybuf, - .vidioc_qbuf = vidioc_qbuf, - .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, .vidioc_g_std = vidioc_g_std, .vidioc_s_std = vidioc_s_std, .vidioc_querystd = vidioc_querystd, .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_streamon = vidioc_streamon, - .vidioc_streamoff = vidioc_streamoff, + .vidioc_enumaudio = vidioc_enumaudio, + .vidioc_g_audio = vidioc_g_audio, + .vidioc_s_audio = vidioc_s_audio, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, .vidioc_g_tuner = vidioc_g_tuner, .vidioc_s_tuner = vidioc_s_tuner, .vidioc_g_frequency = vidioc_g_frequency, @@ -1774,66 +924,129 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_s_parm = vidioc_s_parm, .vidioc_enum_framesizes = vidioc_enum_framesizes, .vidioc_enum_frameintervals = vidioc_enum_frameintervals, - .vidioc_cropcap = vidioc_cropcap, - .vidioc_g_crop = vidioc_g_crop, - .vidioc_s_crop = vidioc_s_crop, - .vidioc_g_jpegcomp = vidioc_g_jpegcomp, - .vidioc_s_jpegcomp = vidioc_s_jpegcomp, + .vidioc_log_status = vidioc_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; static struct video_device go7007_template = { .name = "go7007", .fops = &go7007_fops, - .release = go7007_vfl_release, + .release = video_device_release_empty, .ioctl_ops = &video_ioctl_ops, .tvnorms = V4L2_STD_ALL, - .current_norm = V4L2_STD_NTSC, }; +int go7007_v4l2_ctrl_init(struct go7007 *go) +{ + struct v4l2_ctrl_handler *hdl = &go->hdl; + struct v4l2_ctrl *ctrl; + + v4l2_ctrl_handler_init(hdl, 13); + go->mpeg_video_gop_size = v4l2_ctrl_new_std(hdl, NULL, + V4L2_CID_MPEG_VIDEO_GOP_SIZE, 0, 34, 1, 15); + go->mpeg_video_gop_closure = v4l2_ctrl_new_std(hdl, NULL, + V4L2_CID_MPEG_VIDEO_GOP_CLOSURE, 0, 1, 1, 1); + go->mpeg_video_bitrate = v4l2_ctrl_new_std(hdl, NULL, + V4L2_CID_MPEG_VIDEO_BITRATE, + 64000, 10000000, 1, 9800000); + go->mpeg_video_b_frames = v4l2_ctrl_new_std(hdl, NULL, + V4L2_CID_MPEG_VIDEO_B_FRAMES, 0, 2, 2, 0); + go->mpeg_video_rep_seqheader = v4l2_ctrl_new_std(hdl, NULL, + V4L2_CID_MPEG_VIDEO_REPEAT_SEQ_HEADER, 0, 1, 1, 1); + + go->mpeg_video_aspect_ratio = v4l2_ctrl_new_std_menu(hdl, NULL, + V4L2_CID_MPEG_VIDEO_ASPECT, + V4L2_MPEG_VIDEO_ASPECT_16x9, 0, + V4L2_MPEG_VIDEO_ASPECT_1x1); + ctrl = v4l2_ctrl_new_std(hdl, NULL, + V4L2_CID_JPEG_ACTIVE_MARKER, 0, + V4L2_JPEG_ACTIVE_MARKER_DQT | V4L2_JPEG_ACTIVE_MARKER_DHT, 0, + V4L2_JPEG_ACTIVE_MARKER_DQT | V4L2_JPEG_ACTIVE_MARKER_DHT); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + if (hdl->error) { + int rv = hdl->error; + + v4l2_err(&go->v4l2_dev, "Could not register controls\n"); + return rv; + } + go->v4l2_dev.ctrl_handler = hdl; + return 0; +} + int go7007_v4l2_init(struct go7007 *go) { + struct video_device *vdev = &go->vdev; int rv; - go->video_dev = video_device_alloc(); - if (go->video_dev == NULL) - return -ENOMEM; - *go->video_dev = go7007_template; - go->video_dev->parent = go->dev; - rv = video_register_device(go->video_dev, VFL_TYPE_GRABBER, -1); - if (rv < 0) { - video_device_release(go->video_dev); - go->video_dev = NULL; + mutex_init(&go->serialize_lock); + mutex_init(&go->queue_lock); + + INIT_LIST_HEAD(&go->vidq_active); + go->vidq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + go->vidq.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ; + go->vidq.ops = &go7007_video_qops; + go->vidq.mem_ops = &vb2_vmalloc_memops; + go->vidq.drv_priv = go; + go->vidq.buf_struct_size = sizeof(struct go7007_buffer); + go->vidq.timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + go->vidq.lock = &go->queue_lock; + rv = vb2_queue_init(&go->vidq); + if (rv) return rv; + *vdev = go7007_template; + vdev->lock = &go->serialize_lock; + vdev->queue = &go->vidq; + set_bit(V4L2_FL_USE_FH_PRIO, &vdev->flags); + video_set_drvdata(vdev, go); + vdev->v4l2_dev = &go->v4l2_dev; + if (!v4l2_device_has_op(&go->v4l2_dev, video, querystd)) + v4l2_disable_ioctl(vdev, VIDIOC_QUERYSTD); + if (!(go->board_info->flags & GO7007_BOARD_HAS_TUNER)) { + v4l2_disable_ioctl(vdev, VIDIOC_S_FREQUENCY); + v4l2_disable_ioctl(vdev, VIDIOC_G_FREQUENCY); + v4l2_disable_ioctl(vdev, VIDIOC_S_TUNER); + v4l2_disable_ioctl(vdev, VIDIOC_G_TUNER); + } else { + struct v4l2_frequency f = { + .type = V4L2_TUNER_ANALOG_TV, + .frequency = 980, + }; + + call_all(&go->v4l2_dev, tuner, s_frequency, &f); } - rv = v4l2_device_register(go->dev, &go->v4l2_dev); - if (rv < 0) { - video_device_release(go->video_dev); - go->video_dev = NULL; - return rv; + if (!(go->board_info->sensor_flags & GO7007_SENSOR_TV)) { + v4l2_disable_ioctl(vdev, VIDIOC_G_STD); + v4l2_disable_ioctl(vdev, VIDIOC_S_STD); + vdev->tvnorms = 0; + } + if (go->board_info->sensor_flags & GO7007_SENSOR_SCALING) + v4l2_disable_ioctl(vdev, VIDIOC_ENUM_FRAMESIZES); + if (go->board_info->num_aud_inputs == 0) { + v4l2_disable_ioctl(vdev, VIDIOC_G_AUDIO); + v4l2_disable_ioctl(vdev, VIDIOC_S_AUDIO); + v4l2_disable_ioctl(vdev, VIDIOC_ENUMAUDIO); } - video_set_drvdata(go->video_dev, go); - ++go->ref_count; + /* Setup correct crystal frequency on this board */ + if (go->board_info->sensor_flags & GO7007_SENSOR_SAA7115) + v4l2_subdev_call(go->sd_video, video, s_crystal_freq, + SAA7115_FREQ_24_576_MHZ, + SAA7115_FREQ_FL_APLL | SAA7115_FREQ_FL_UCGC | + SAA7115_FREQ_FL_DOUBLE_ASCLK); + go7007_s_input(go); + if (go->board_info->sensor_flags & GO7007_SENSOR_TV) + go7007_s_std(go); + rv = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + if (rv < 0) + return rv; dev_info(go->dev, "registered device %s [v4l2]\n", - video_device_node_name(go->video_dev)); + video_device_node_name(vdev)); return 0; } void go7007_v4l2_remove(struct go7007 *go) { - unsigned long flags; - - mutex_lock(&go->hw_lock); - if (go->streaming) { - go->streaming = 0; - go7007_stream_stop(go); - spin_lock_irqsave(&go->spinlock, flags); - abort_queued(go); - spin_unlock_irqrestore(&go->spinlock, flags); - } - mutex_unlock(&go->hw_lock); - if (go->video_dev) - video_unregister_device(go->video_dev); - if (go->status != STATUS_SHUTDOWN) - v4l2_device_unregister(&go->v4l2_dev); + v4l2_ctrl_handler_free(&go->hdl); } diff --git a/drivers/staging/media/go7007/go7007.h b/drivers/staging/media/go7007/go7007.h index 7399c915a934..54b989738982 100644 --- a/drivers/staging/media/go7007/go7007.h +++ b/drivers/staging/media/go7007/go7007.h @@ -17,72 +17,6 @@ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/* DEPRECATED -- use V4L2_PIX_FMT_MPEG and then call GO7007IOC_S_MPEG_PARAMS - * to select between MPEG1, MPEG2, and MPEG4 */ -#define V4L2_PIX_FMT_MPEG4 v4l2_fourcc('M', 'P', 'G', '4') /* MPEG4 */ - -/* These will be replaced with a better interface - * soon, so don't get too attached to them */ -#define GO7007IOC_S_BITRATE _IOW('V', BASE_VIDIOC_PRIVATE + 0, int) -#define GO7007IOC_G_BITRATE _IOR('V', BASE_VIDIOC_PRIVATE + 1, int) - -enum go7007_aspect_ratio { - GO7007_ASPECT_RATIO_1_1 = 0, - GO7007_ASPECT_RATIO_4_3_NTSC = 1, - GO7007_ASPECT_RATIO_4_3_PAL = 2, - GO7007_ASPECT_RATIO_16_9_NTSC = 3, - GO7007_ASPECT_RATIO_16_9_PAL = 4, -}; - -/* Used to set generic compression parameters */ -struct go7007_comp_params { - __u32 gop_size; - __u32 max_b_frames; - enum go7007_aspect_ratio aspect_ratio; - __u32 flags; - __u32 reserved[8]; -}; - -#define GO7007_COMP_CLOSED_GOP 0x00000001 -#define GO7007_COMP_OMIT_SEQ_HEADER 0x00000002 - -enum go7007_mpeg_video_standard { - GO7007_MPEG_VIDEO_MPEG1 = 0, - GO7007_MPEG_VIDEO_MPEG2 = 1, - GO7007_MPEG_VIDEO_MPEG4 = 2, -}; - -/* Used to set parameters for V4L2_PIX_FMT_MPEG format */ -struct go7007_mpeg_params { - enum go7007_mpeg_video_standard mpeg_video_standard; - __u32 flags; - __u32 pali; - __u32 reserved[8]; -}; - -#define GO7007_MPEG_FORCE_DVD_MODE 0x00000001 -#define GO7007_MPEG_OMIT_GOP_HEADER 0x00000002 -#define GO7007_MPEG_REPEAT_SEQHEADER 0x00000004 - -#define GO7007_MPEG_PROFILE(format, pali) (((format)<<24)|(pali)) - -#define GO7007_MPEG2_PROFILE_MAIN_MAIN GO7007_MPEG_PROFILE(2, 0x48) - -#define GO7007_MPEG4_PROFILE_S_L0 GO7007_MPEG_PROFILE(4, 0x08) -#define GO7007_MPEG4_PROFILE_S_L1 GO7007_MPEG_PROFILE(4, 0x01) -#define GO7007_MPEG4_PROFILE_S_L2 GO7007_MPEG_PROFILE(4, 0x02) -#define GO7007_MPEG4_PROFILE_S_L3 GO7007_MPEG_PROFILE(4, 0x03) -#define GO7007_MPEG4_PROFILE_ARTS_L1 GO7007_MPEG_PROFILE(4, 0x91) -#define GO7007_MPEG4_PROFILE_ARTS_L2 GO7007_MPEG_PROFILE(4, 0x92) -#define GO7007_MPEG4_PROFILE_ARTS_L3 GO7007_MPEG_PROFILE(4, 0x93) -#define GO7007_MPEG4_PROFILE_ARTS_L4 GO7007_MPEG_PROFILE(4, 0x94) -#define GO7007_MPEG4_PROFILE_AS_L0 GO7007_MPEG_PROFILE(4, 0xf0) -#define GO7007_MPEG4_PROFILE_AS_L1 GO7007_MPEG_PROFILE(4, 0xf1) -#define GO7007_MPEG4_PROFILE_AS_L2 GO7007_MPEG_PROFILE(4, 0xf2) -#define GO7007_MPEG4_PROFILE_AS_L3 GO7007_MPEG_PROFILE(4, 0xf3) -#define GO7007_MPEG4_PROFILE_AS_L4 GO7007_MPEG_PROFILE(4, 0xf4) -#define GO7007_MPEG4_PROFILE_AS_L5 GO7007_MPEG_PROFILE(4, 0xf5) - struct go7007_md_params { __u16 region; __u16 trigger; @@ -98,14 +32,6 @@ struct go7007_md_region { __u32 reserved[8]; }; -#define GO7007IOC_S_MPEG_PARAMS _IOWR('V', BASE_VIDIOC_PRIVATE + 2, \ - struct go7007_mpeg_params) -#define GO7007IOC_G_MPEG_PARAMS _IOR('V', BASE_VIDIOC_PRIVATE + 3, \ - struct go7007_mpeg_params) -#define GO7007IOC_S_COMP_PARAMS _IOWR('V', BASE_VIDIOC_PRIVATE + 4, \ - struct go7007_comp_params) -#define GO7007IOC_G_COMP_PARAMS _IOR('V', BASE_VIDIOC_PRIVATE + 5, \ - struct go7007_comp_params) #define GO7007IOC_S_MD_PARAMS _IOWR('V', BASE_VIDIOC_PRIVATE + 6, \ struct go7007_md_params) #define GO7007IOC_G_MD_PARAMS _IOR('V', BASE_VIDIOC_PRIVATE + 7, \ diff --git a/drivers/staging/media/go7007/s2250-board.c b/drivers/staging/media/go7007/s2250-board.c index 37400bfa6ccb..beaa98b9c85a 100644 --- a/drivers/staging/media/go7007/s2250-board.c +++ b/drivers/staging/media/go7007/s2250-board.c @@ -29,6 +29,13 @@ MODULE_DESCRIPTION("Sensoray 2250/2251 i2c v4l2 subdev driver"); MODULE_LICENSE("GPL v2"); +/* + * Note: this board has two i2c devices: a vpx3226f and a tlv320aic23b. + * Due to the unusual way these are accessed on this device we do not + * reuse the i2c drivers, but instead they are implemented in this + * driver. It would be nice to improve on this, though. + */ + #define TLV320_ADDRESS 0x34 #define VPX322_ADDR_ANALOGCONTROL1 0x02 #define VPX322_ADDR_BRIGHTNESS0 0x0127 @@ -116,6 +123,7 @@ static u16 vid_regs_fp_pal[] = { struct s2250 { struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; v4l2_std_id std; int input; int brightness; @@ -353,127 +361,48 @@ static int s2250_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) u16 vidsource; vidsource = (state->input == 1) ? 0x040 : 0x020; - switch (norm) { - case V4L2_STD_NTSC: - write_regs_fp(client, vid_regs_fp); - write_reg_fp(client, 0x20, vidsource | 1); - break; - case V4L2_STD_PAL: + if (norm & V4L2_STD_625_50) { write_regs_fp(client, vid_regs_fp); write_regs_fp(client, vid_regs_fp_pal); write_reg_fp(client, 0x20, vidsource); - break; - default: - return -EINVAL; + } else { + write_regs_fp(client, vid_regs_fp); + write_reg_fp(client, 0x20, vidsource | 1); } state->std = norm; return 0; } -static int s2250_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *query) +static int s2250_s_ctrl(struct v4l2_ctrl *ctrl) { - switch (query->id) { - case V4L2_CID_BRIGHTNESS: - return v4l2_ctrl_query_fill(query, 0, 100, 1, 50); - case V4L2_CID_CONTRAST: - return v4l2_ctrl_query_fill(query, 0, 100, 1, 50); - case V4L2_CID_SATURATION: - return v4l2_ctrl_query_fill(query, 0, 100, 1, 50); - case V4L2_CID_HUE: - return v4l2_ctrl_query_fill(query, -50, 50, 1, 0); - default: - return -EINVAL; - } - return 0; -} - -static int s2250_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - struct s2250 *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - int value1; + struct s2250 *state = container_of(ctrl->handler, struct s2250, hdl); + struct i2c_client *client = v4l2_get_subdevdata(&state->sd); u16 oldvalue; switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: - if (ctrl->value > 100) - state->brightness = 100; - else if (ctrl->value < 0) - state->brightness = 0; - else - state->brightness = ctrl->value; - value1 = (state->brightness - 50) * 255 / 100; read_reg_fp(client, VPX322_ADDR_BRIGHTNESS0, &oldvalue); write_reg_fp(client, VPX322_ADDR_BRIGHTNESS0, - value1 | (oldvalue & ~0xff)); + ctrl->val | (oldvalue & ~0xff)); read_reg_fp(client, VPX322_ADDR_BRIGHTNESS1, &oldvalue); write_reg_fp(client, VPX322_ADDR_BRIGHTNESS1, - value1 | (oldvalue & ~0xff)); + ctrl->val | (oldvalue & ~0xff)); write_reg_fp(client, 0x140, 0x60); break; case V4L2_CID_CONTRAST: - if (ctrl->value > 100) - state->contrast = 100; - else if (ctrl->value < 0) - state->contrast = 0; - else - state->contrast = ctrl->value; - value1 = state->contrast * 0x40 / 100; - if (value1 > 0x3f) - value1 = 0x3f; /* max */ read_reg_fp(client, VPX322_ADDR_CONTRAST0, &oldvalue); write_reg_fp(client, VPX322_ADDR_CONTRAST0, - value1 | (oldvalue & ~0x3f)); + ctrl->val | (oldvalue & ~0x3f)); read_reg_fp(client, VPX322_ADDR_CONTRAST1, &oldvalue); write_reg_fp(client, VPX322_ADDR_CONTRAST1, - value1 | (oldvalue & ~0x3f)); + ctrl->val | (oldvalue & ~0x3f)); write_reg_fp(client, 0x140, 0x60); break; case V4L2_CID_SATURATION: - if (ctrl->value > 100) - state->saturation = 100; - else if (ctrl->value < 0) - state->saturation = 0; - else - state->saturation = ctrl->value; - value1 = state->saturation * 4140 / 100; - if (value1 > 4094) - value1 = 4094; - write_reg_fp(client, VPX322_ADDR_SAT, value1); - break; - case V4L2_CID_HUE: - if (ctrl->value > 50) - state->hue = 50; - else if (ctrl->value < -50) - state->hue = -50; - else - state->hue = ctrl->value; - /* clamp the hue range */ - value1 = state->hue * 280 / 50; - write_reg_fp(client, VPX322_ADDR_HUE, value1); - break; - default: - return -EINVAL; - } - return 0; -} - -static int s2250_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - struct s2250 *state = to_state(sd); - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - ctrl->value = state->brightness; - break; - case V4L2_CID_CONTRAST: - ctrl->value = state->contrast; - break; - case V4L2_CID_SATURATION: - ctrl->value = state->saturation; + write_reg_fp(client, VPX322_ADDR_SAT, ctrl->val); break; case V4L2_CID_HUE: - ctrl->value = state->hue; + write_reg_fp(client, VPX322_ADDR_HUE, ctrl->val); break; default: return -EINVAL; @@ -531,24 +460,21 @@ static int s2250_log_status(struct v4l2_subdev *sd) v4l2_info(sd, "Input: %s\n", state->input == 0 ? "Composite" : state->input == 1 ? "S-video" : "error"); - v4l2_info(sd, "Brightness: %d\n", state->brightness); - v4l2_info(sd, "Contrast: %d\n", state->contrast); - v4l2_info(sd, "Saturation: %d\n", state->saturation); - v4l2_info(sd, "Hue: %d\n", state->hue); v4l2_info(sd, "Audio input: %s\n", state->audio_input == 0 ? "Line In" : state->audio_input == 1 ? "Mic" : state->audio_input == 2 ? "Mic Boost" : "error"); - return 0; + return v4l2_ctrl_subdev_log_status(sd); } /* --------------------------------------------------------------------------*/ +static const struct v4l2_ctrl_ops s2250_ctrl_ops = { + .s_ctrl = s2250_s_ctrl, +}; + static const struct v4l2_subdev_core_ops s2250_core_ops = { .log_status = s2250_log_status, - .g_ctrl = s2250_g_ctrl, - .s_ctrl = s2250_s_ctrl, - .queryctrl = s2250_queryctrl, .s_std = s2250_s_std, }; @@ -584,7 +510,7 @@ static int s2250_probe(struct i2c_client *client, if (audio == NULL) return -ENOMEM; - state = kmalloc(sizeof(struct s2250), GFP_KERNEL); + state = kzalloc(sizeof(struct s2250), GFP_KERNEL); if (state == NULL) { i2c_unregister_device(audio); return -ENOMEM; @@ -596,6 +522,24 @@ static int s2250_probe(struct i2c_client *client, v4l2_info(sd, "initializing %s at address 0x%x on %s\n", "Sensoray 2250/2251", client->addr, client->adapter->name); + v4l2_ctrl_handler_init(&state->hdl, 4); + v4l2_ctrl_new_std(&state->hdl, &s2250_ctrl_ops, + V4L2_CID_BRIGHTNESS, -128, 127, 1, 0); + v4l2_ctrl_new_std(&state->hdl, &s2250_ctrl_ops, + V4L2_CID_CONTRAST, 0, 0x3f, 1, 0x32); + v4l2_ctrl_new_std(&state->hdl, &s2250_ctrl_ops, + V4L2_CID_SATURATION, 0, 4094, 1, 2070); + v4l2_ctrl_new_std(&state->hdl, &s2250_ctrl_ops, + V4L2_CID_HUE, -512, 511, 1, 0); + sd->ctrl_handler = &state->hdl; + if (state->hdl.error) { + int err = state->hdl.error; + + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + return err; + } + state->std = V4L2_STD_NTSC; state->brightness = 50; state->contrast = 50; @@ -606,22 +550,16 @@ static int s2250_probe(struct i2c_client *client, /* initialize the audio */ if (write_regs(audio, aud_regs) < 0) { dev_err(&client->dev, "error initializing audio\n"); - i2c_unregister_device(audio); - kfree(state); - return 0; + goto fail; } if (write_regs(client, vid_regs) < 0) { dev_err(&client->dev, "error initializing decoder\n"); - i2c_unregister_device(audio); - kfree(state); - return 0; + goto fail; } if (write_regs_fp(client, vid_regs_fp) < 0) { dev_err(&client->dev, "error initializing decoder\n"); - i2c_unregister_device(audio); - kfree(state); - return 0; + goto fail; } /* set default channel */ /* composite */ @@ -657,14 +595,21 @@ static int s2250_probe(struct i2c_client *client, v4l2_info(sd, "initialized successfully\n"); return 0; + +fail: + i2c_unregister_device(audio); + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + return -EIO; } static int s2250_remove(struct i2c_client *client) { - struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct s2250 *state = to_state(i2c_get_clientdata(client)); - v4l2_device_unregister_subdev(sd); - kfree(to_state(sd)); + v4l2_device_unregister_subdev(&state->sd); + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); return 0; } diff --git a/drivers/staging/media/go7007/s2250-loader.c b/drivers/staging/media/go7007/s2250-loader.c deleted file mode 100644 index 72e5175fe7e3..000000000000 --- a/drivers/staging/media/go7007/s2250-loader.c +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright (C) 2008 Sensoray Company Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License (Version 2) as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. - */ - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/usb.h> -#include <dvb-usb.h> - -#define S2250_LOADER_FIRMWARE "s2250_loader.fw" -#define S2250_FIRMWARE "s2250.fw" - -typedef struct device_extension_s { - struct kref kref; - int minor; - struct usb_device *usbdev; -} device_extension_t, *pdevice_extension_t; - -#define USB_s2250loader_MAJOR 240 -#define USB_s2250loader_MINOR_BASE 0 -#define MAX_DEVICES 256 - -static pdevice_extension_t s2250_dev_table[MAX_DEVICES]; -static DEFINE_MUTEX(s2250_dev_table_mutex); - -#define to_s2250loader_dev_common(d) container_of(d, device_extension_t, kref) -static void s2250loader_delete(struct kref *kref) -{ - pdevice_extension_t s = to_s2250loader_dev_common(kref); - s2250_dev_table[s->minor] = NULL; - kfree(s); -} - -static int s2250loader_probe(struct usb_interface *interface, - const struct usb_device_id *id) -{ - struct usb_device *usbdev; - int minor, ret; - pdevice_extension_t s = NULL; - const struct firmware *fw; - - usbdev = usb_get_dev(interface_to_usbdev(interface)); - if (!usbdev) { - dev_err(&interface->dev, "Enter s2250loader_probe failed\n"); - return -1; - } - dev_info(&interface->dev, "Enter s2250loader_probe 2.6 kernel\n"); - dev_info(&interface->dev, "vendor id 0x%x, device id 0x%x devnum:%d\n", - usbdev->descriptor.idVendor, usbdev->descriptor.idProduct, - usbdev->devnum); - - if (usbdev->descriptor.bNumConfigurations != 1) { - dev_err(&interface->dev, "can't handle multiple config\n"); - return -1; - } - mutex_lock(&s2250_dev_table_mutex); - - for (minor = 0; minor < MAX_DEVICES; minor++) { - if (s2250_dev_table[minor] == NULL) - break; - } - - if (minor < 0 || minor >= MAX_DEVICES) { - dev_err(&interface->dev, "Invalid minor: %d\n", minor); - goto failed; - } - - /* Allocate dev data structure */ - s = kmalloc(sizeof(device_extension_t), GFP_KERNEL); - if (s == NULL) - goto failed; - - s2250_dev_table[minor] = s; - - dev_info(&interface->dev, - "s2250loader_probe: Device %d on Bus %d Minor %d\n", - usbdev->devnum, usbdev->bus->busnum, minor); - - memset(s, 0, sizeof(device_extension_t)); - s->usbdev = usbdev; - dev_info(&interface->dev, "loading 2250 loader\n"); - - kref_init(&(s->kref)); - - mutex_unlock(&s2250_dev_table_mutex); - - if (request_firmware(&fw, S2250_LOADER_FIRMWARE, &usbdev->dev)) { - dev_err(&interface->dev, - "s2250: unable to load firmware from file \"%s\"\n", - S2250_LOADER_FIRMWARE); - goto failed2; - } - ret = usb_cypress_load_firmware(usbdev, fw, CYPRESS_FX2); - release_firmware(fw); - if (0 != ret) { - dev_err(&interface->dev, "loader download failed\n"); - goto failed2; - } - - if (request_firmware(&fw, S2250_FIRMWARE, &usbdev->dev)) { - dev_err(&interface->dev, - "s2250: unable to load firmware from file \"%s\"\n", - S2250_FIRMWARE); - goto failed2; - } - ret = usb_cypress_load_firmware(usbdev, fw, CYPRESS_FX2); - release_firmware(fw); - if (0 != ret) { - dev_err(&interface->dev, "firmware_s2250 download failed\n"); - goto failed2; - } - - usb_set_intfdata(interface, s); - return 0; - -failed: - mutex_unlock(&s2250_dev_table_mutex); -failed2: - if (s) - kref_put(&(s->kref), s2250loader_delete); - - dev_err(&interface->dev, "probe failed\n"); - return -1; -} - -static void s2250loader_disconnect(struct usb_interface *interface) -{ - pdevice_extension_t s; - dev_info(&interface->dev, "s2250: disconnect\n"); - s = usb_get_intfdata(interface); - usb_set_intfdata(interface, NULL); - kref_put(&(s->kref), s2250loader_delete); -} - -static const struct usb_device_id s2250loader_ids[] = { - {USB_DEVICE(0x1943, 0xa250)}, - {} /* Terminating entry */ -}; - -MODULE_DEVICE_TABLE(usb, s2250loader_ids); - -static struct usb_driver s2250loader_driver = { - .name = "s2250-loader", - .probe = s2250loader_probe, - .disconnect = s2250loader_disconnect, - .id_table = s2250loader_ids, -}; - -module_usb_driver(s2250loader_driver); - -MODULE_AUTHOR(""); -MODULE_DESCRIPTION("firmware loader for Sensoray 2250/2251"); -MODULE_LICENSE("GPL v2"); -MODULE_FIRMWARE(S2250_LOADER_FIRMWARE); -MODULE_FIRMWARE(S2250_FIRMWARE); diff --git a/drivers/staging/media/go7007/s2250-loader.h b/drivers/staging/media/go7007/s2250-loader.h deleted file mode 100644 index b7c301af16cc..000000000000 --- a/drivers/staging/media/go7007/s2250-loader.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2005-2006 Micronas USA Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License (Version 2) as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. - */ - -#ifndef _S2250_LOADER_H_ -#define _S2250_LOADER_H_ - -extern int s2250loader_init(void); -extern void s2250loader_cleanup(void); - -#endif diff --git a/drivers/staging/media/go7007/saa7134-go7007.c b/drivers/staging/media/go7007/saa7134-go7007.c index cf7c34a99459..d80b235d72ee 100644 --- a/drivers/staging/media/go7007/saa7134-go7007.c +++ b/drivers/staging/media/go7007/saa7134-go7007.c @@ -28,12 +28,15 @@ #include <linux/i2c.h> #include <asm/byteorder.h> #include <media/v4l2-common.h> +#include <media/v4l2-device.h> +#include <media/v4l2-subdev.h> -#include "saa7134-reg.h" #include "saa7134.h" +#include "saa7134-reg.h" +#include "go7007.h" #include "go7007-priv.h" -#define GO7007_HPI_DEBUG +/*#define GO7007_HPI_DEBUG*/ enum hpi_address { HPI_ADDR_VIDEO_BUFFER = 0xe4, @@ -57,6 +60,7 @@ enum gpio_command { }; struct saa7134_go7007 { + struct v4l2_subdev sd; struct saa7134_dev *dev; u8 *top; u8 *bottom; @@ -64,8 +68,12 @@ struct saa7134_go7007 { dma_addr_t bottom_dma; }; -static struct go7007_board_info board_voyager = { - .firmware = "go7007tv.bin", +static inline struct saa7134_go7007 *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct saa7134_go7007, sd); +} + +static const struct go7007_board_info board_voyager = { .flags = 0, .sensor_flags = GO7007_SENSOR_656 | GO7007_SENSOR_VALID_ENABLE | @@ -84,7 +92,6 @@ static struct go7007_board_info board_voyager = { }, }, }; -MODULE_FIRMWARE("go7007tv.bin"); /********************* Driver for GPIO HPI interface *********************/ @@ -236,7 +243,7 @@ static void saa7134_go7007_irq_ts_done(struct saa7134_dev *dev, struct go7007 *go = video_get_drvdata(dev->empress_dev); struct saa7134_go7007 *saa = go->hpi_context; - if (!go->streaming) + if (!vb2_is_streaming(&go->vidq)) return; if (0 != (status & 0x000f0000)) printk(KERN_DEBUG "saa7134-go7007: irq: lost %ld\n", @@ -261,12 +268,12 @@ static int saa7134_go7007_stream_start(struct go7007 *go) saa->top_dma = dma_map_page(&dev->pci->dev, virt_to_page(saa->top), 0, PAGE_SIZE, DMA_FROM_DEVICE); - if (!saa->top_dma) + if (dma_mapping_error(&dev->pci->dev, saa->top_dma)) return -ENOMEM; saa->bottom_dma = dma_map_page(&dev->pci->dev, virt_to_page(saa->bottom), 0, PAGE_SIZE, DMA_FROM_DEVICE); - if (!saa->bottom_dma) { + if (dma_mapping_error(&dev->pci->dev, saa->bottom_dma)) { dma_unmap_page(&dev->pci->dev, saa->top_dma, PAGE_SIZE, DMA_FROM_DEVICE); return -ENOMEM; @@ -380,47 +387,6 @@ static int saa7134_go7007_send_firmware(struct go7007 *go, u8 *data, int len) return 0; } -static int saa7134_go7007_send_command(struct go7007 *go, unsigned int cmd, - void *arg) -{ - struct saa7134_go7007 *saa = go->hpi_context; - struct saa7134_dev *dev = saa->dev; - - switch (cmd) { - case VIDIOC_S_STD: - { - v4l2_std_id *std = arg; - return saa7134_s_std_internal(dev, NULL, std); - } - case VIDIOC_G_STD: - { - v4l2_std_id *std = arg; - *std = dev->tvnorm->id; - return 0; - } - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *ctrl = arg; - if (V4L2_CTRL_ID2CLASS(ctrl->id) == V4L2_CTRL_CLASS_USER) - return saa7134_queryctrl(NULL, NULL, ctrl); - } - case VIDIOC_G_CTRL: - { - struct v4l2_control *ctrl = arg; - if (V4L2_CTRL_ID2CLASS(ctrl->id) == V4L2_CTRL_CLASS_USER) - return saa7134_g_ctrl_internal(dev, NULL, ctrl); - } - case VIDIOC_S_CTRL: - { - struct v4l2_control *ctrl = arg; - if (V4L2_CTRL_ID2CLASS(ctrl->id) == V4L2_CTRL_CLASS_USER) - return saa7134_s_ctrl_internal(dev, NULL, ctrl); - } - } - return -EINVAL; - -} - static struct go7007_hpi_ops saa7134_go7007_hpi_ops = { .interface_reset = saa7134_go7007_interface_reset, .write_interrupt = saa7134_go7007_write_interrupt, @@ -428,21 +394,88 @@ static struct go7007_hpi_ops saa7134_go7007_hpi_ops = { .stream_start = saa7134_go7007_stream_start, .stream_stop = saa7134_go7007_stream_stop, .send_firmware = saa7134_go7007_send_firmware, - .send_command = saa7134_go7007_send_command, +}; +MODULE_FIRMWARE("go7007/go7007tv.bin"); + +/* --------------------------------------------------------------------------*/ + +static int saa7134_go7007_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) +{ + struct saa7134_go7007 *saa = to_state(sd); + struct saa7134_dev *dev = saa->dev; + + return saa7134_s_std_internal(dev, NULL, norm); +} + +static int saa7134_go7007_queryctrl(struct v4l2_subdev *sd, + struct v4l2_queryctrl *query) +{ + return saa7134_queryctrl(NULL, NULL, query); +} +static int saa7134_go7007_s_ctrl(struct v4l2_subdev *sd, + struct v4l2_control *ctrl) +{ + struct saa7134_go7007 *saa = to_state(sd); + struct saa7134_dev *dev = saa->dev; + return saa7134_s_ctrl_internal(dev, NULL, ctrl); +} + +static int saa7134_go7007_g_ctrl(struct v4l2_subdev *sd, + struct v4l2_control *ctrl) +{ + struct saa7134_go7007 *saa = to_state(sd); + struct saa7134_dev *dev = saa->dev; + return saa7134_g_ctrl_internal(dev, NULL, ctrl); +} + +/* --------------------------------------------------------------------------*/ + +static const struct v4l2_subdev_core_ops saa7134_go7007_core_ops = { + .g_ctrl = saa7134_go7007_g_ctrl, + .s_ctrl = saa7134_go7007_s_ctrl, + .queryctrl = saa7134_go7007_queryctrl, + .s_std = saa7134_go7007_s_std, +}; + +static const struct v4l2_subdev_ops saa7134_go7007_sd_ops = { + .core = &saa7134_go7007_core_ops, }; +/* --------------------------------------------------------------------------*/ + + /********************* Add/remove functions *********************/ static int saa7134_go7007_init(struct saa7134_dev *dev) { struct go7007 *go; struct saa7134_go7007 *saa; + struct v4l2_subdev *sd; printk(KERN_DEBUG "saa7134-go7007: probing new SAA713X board\n"); + go = go7007_alloc(&board_voyager, &dev->pci->dev); + if (go == NULL) + return -ENOMEM; + saa = kzalloc(sizeof(struct saa7134_go7007), GFP_KERNEL); - if (saa == NULL) + if (saa == NULL) { + kfree(go); return -ENOMEM; + } + + go->board_id = GO7007_BOARDID_PCI_VOYAGER; + snprintf(go->bus_info, sizeof(go->bus_info), "PCI:%s", pci_name(dev->pci)); + strlcpy(go->name, saa7134_boards[dev->board].name, sizeof(go->name)); + go->hpi_ops = &saa7134_go7007_hpi_ops; + go->hpi_context = saa; + saa->dev = dev; + + /* Init the subdevice interface */ + sd = &saa->sd; + v4l2_subdev_init(sd, &saa7134_go7007_sd_ops); + v4l2_set_subdevdata(sd, saa); + strncpy(sd->name, "saa7134-go7007", sizeof(sd->name)); /* Allocate a couple pages for receiving the compressed stream */ saa->top = (u8 *)get_zeroed_page(GFP_KERNEL); @@ -452,32 +485,23 @@ static int saa7134_go7007_init(struct saa7134_dev *dev) if (!saa->bottom) goto allocfail; - go = go7007_alloc(&board_voyager, &dev->pci->dev); - if (go == NULL) - goto allocfail; - go->board_id = GO7007_BOARDID_PCI_VOYAGER; - strncpy(go->name, saa7134_boards[dev->board].name, sizeof(go->name)); - go->hpi_ops = &saa7134_go7007_hpi_ops; - go->hpi_context = saa; - saa->dev = dev; - /* Boot the GO7007 */ if (go7007_boot_encoder(go, go->board_info->flags & GO7007_BOARD_USE_ONBOARD_I2C) < 0) - goto initfail; + goto allocfail; /* Do any final GO7007 initialization, then register the * V4L2 and ALSA interfaces */ - if (go7007_register_encoder(go) < 0) - goto initfail; - dev->empress_dev = go->video_dev; - video_set_drvdata(dev->empress_dev, go); + if (go7007_register_encoder(go, go->board_info->num_i2c_devs) < 0) + goto allocfail; - go->status = STATUS_ONLINE; - return 0; + /* Register the subdevice interface with the go7007 device */ + if (v4l2_device_register_subdev(&go->v4l2_dev, sd) < 0) + printk(KERN_INFO "saa7134-go7007: register subdev failed\n"); -initfail: - go->status = STATUS_SHUTDOWN; + dev->empress_dev = &go->vdev; + + go->status = STATUS_ONLINE; return 0; allocfail: @@ -486,6 +510,7 @@ allocfail: if (saa->bottom) free_page((unsigned long)saa->bottom); kfree(saa); + kfree(go); return -ENOMEM; } @@ -498,12 +523,18 @@ static int saa7134_go7007_fini(struct saa7134_dev *dev) return 0; go = video_get_drvdata(dev->empress_dev); + if (go->audio_enabled) + go7007_snd_remove(go); + saa = go->hpi_context; go->status = STATUS_SHUTDOWN; free_page((unsigned long)saa->top); free_page((unsigned long)saa->bottom); + v4l2_device_unregister_subdev(&saa->sd); kfree(saa); - go7007_remove(go); + video_unregister_device(&go->vdev); + + v4l2_device_put(&go->v4l2_dev); dev->empress_dev = NULL; return 0; diff --git a/drivers/staging/media/go7007/snd-go7007.c b/drivers/staging/media/go7007/snd-go7007.c index 5af29ff68bfd..4be0fa40a39a 100644 --- a/drivers/staging/media/go7007/snd-go7007.c +++ b/drivers/staging/media/go7007/snd-go7007.c @@ -221,8 +221,6 @@ static int go7007_snd_free(struct snd_device *device) kfree(go->snd_context); go->snd_context = NULL; - if (--go->ref_count == 0) - kfree(go); return 0; } @@ -267,9 +265,9 @@ int go7007_snd_init(struct go7007 *go) kfree(gosnd); return ret; } - strncpy(gosnd->card->driver, "go7007", sizeof(gosnd->card->driver)); - strncpy(gosnd->card->shortname, go->name, sizeof(gosnd->card->driver)); - strncpy(gosnd->card->longname, gosnd->card->shortname, + strlcpy(gosnd->card->driver, "go7007", sizeof(gosnd->card->driver)); + strlcpy(gosnd->card->shortname, go->name, sizeof(gosnd->card->driver)); + strlcpy(gosnd->card->longname, gosnd->card->shortname, sizeof(gosnd->card->longname)); gosnd->pcm->private_data = go; @@ -285,8 +283,8 @@ int go7007_snd_init(struct go7007 *go) gosnd->substream = NULL; go->snd_context = gosnd; + v4l2_device_get(&go->v4l2_dev); ++dev; - ++go->ref_count; return 0; } @@ -298,6 +296,7 @@ int go7007_snd_remove(struct go7007 *go) snd_card_disconnect(gosnd->card); snd_card_free_when_closed(gosnd->card); + v4l2_device_put(&go->v4l2_dev); return 0; } EXPORT_SYMBOL(go7007_snd_remove); diff --git a/drivers/staging/media/go7007/wis-i2c.h b/drivers/staging/media/go7007/wis-i2c.h deleted file mode 100644 index 6d09c06c8560..000000000000 --- a/drivers/staging/media/go7007/wis-i2c.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2005-2006 Micronas USA Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License (Version 2) as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. - */ - -/* Temporary I2C IDs -- these need to be replaced with real registered IDs */ -#define I2C_DRIVERID_WIS_SAA7115 0xf0f0 -#define I2C_DRIVERID_WIS_UDA1342 0xf0f1 -#define I2C_DRIVERID_WIS_SONY_TUNER 0xf0f2 -#define I2C_DRIVERID_WIS_TW9903 0xf0f3 -#define I2C_DRIVERID_WIS_SAA7113 0xf0f4 -#define I2C_DRIVERID_WIS_OV7640 0xf0f5 -#define I2C_DRIVERID_WIS_TW2804 0xf0f6 -#define I2C_DRIVERID_S2250 0xf0f7 - -/* Definitions for new video decoder commands */ - -struct video_decoder_resolution { - unsigned int width; - unsigned int height; -}; - -#define DECODER_SET_RESOLUTION _IOW('d', 200, struct video_decoder_resolution) -#define DECODER_SET_CHANNEL _IOW('d', 201, int) - -/* Sony tuner types */ - -#define TUNER_SONY_BTF_PG472Z 200 -#define TUNER_SONY_BTF_PK467Z 201 -#define TUNER_SONY_BTF_PB463Z 202 diff --git a/drivers/staging/media/go7007/wis-ov7640.c b/drivers/staging/media/go7007/wis-ov7640.c deleted file mode 100644 index 9f01657f884a..000000000000 --- a/drivers/staging/media/go7007/wis-ov7640.c +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (C) 2005-2006 Micronas USA Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License (Version 2) as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. - */ - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/i2c.h> -#include <linux/videodev2.h> - -#include "wis-i2c.h" - -struct wis_ov7640 { - int brightness; - int contrast; - int saturation; - int hue; -}; - -static u8 initial_registers[] = { - 0x12, 0x80, - 0x12, 0x54, - 0x14, 0x24, - 0x15, 0x01, - 0x28, 0x20, - 0x75, 0x82, - 0xFF, 0xFF, /* Terminator (reg 0xFF is unused) */ -}; - -static int write_regs(struct i2c_client *client, u8 *regs) -{ - int i; - - for (i = 0; regs[i] != 0xFF; i += 2) - if (i2c_smbus_write_byte_data(client, regs[i], regs[i + 1]) < 0) - return -1; - return 0; -} - -static int wis_ov7640_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct i2c_adapter *adapter = client->adapter; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -ENODEV; - - client->flags = I2C_CLIENT_SCCB; - - dev_dbg(&client->dev, - "wis-ov7640: initializing OV7640 at address %d on %s\n", - client->addr, adapter->name); - - if (write_regs(client, initial_registers) < 0) { - dev_err(&client->dev, "wis-ov7640: error initializing OV7640\n"); - return -ENODEV; - } - - return 0; -} - -static int wis_ov7640_remove(struct i2c_client *client) -{ - return 0; -} - -static const struct i2c_device_id wis_ov7640_id[] = { - { "wis_ov7640", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, wis_ov7640_id); - -static struct i2c_driver wis_ov7640_driver = { - .driver = { - .name = "WIS OV7640 I2C driver", - }, - .probe = wis_ov7640_probe, - .remove = wis_ov7640_remove, - .id_table = wis_ov7640_id, -}; - -module_i2c_driver(wis_ov7640_driver); - -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/go7007/wis-saa7113.c b/drivers/staging/media/go7007/wis-saa7113.c deleted file mode 100644 index 891cde713a47..000000000000 --- a/drivers/staging/media/go7007/wis-saa7113.c +++ /dev/null @@ -1,324 +0,0 @@ -/* - * Copyright (C) 2005-2006 Micronas USA Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License (Version 2) as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. - */ - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/i2c.h> -#include <linux/videodev2.h> -#include <linux/ioctl.h> -#include <linux/slab.h> - -#include "wis-i2c.h" - -struct wis_saa7113 { - int norm; - int brightness; - int contrast; - int saturation; - int hue; -}; - -static u8 initial_registers[] = { - 0x01, 0x08, - 0x02, 0xc0, - 0x03, 0x33, - 0x04, 0x00, - 0x05, 0x00, - 0x06, 0xe9, - 0x07, 0x0d, - 0x08, 0xd8, - 0x09, 0x40, - 0x0a, 0x80, - 0x0b, 0x47, - 0x0c, 0x40, - 0x0d, 0x00, - 0x0e, 0x01, - 0x0f, 0x2a, - 0x10, 0x40, - 0x11, 0x0c, - 0x12, 0xfe, - 0x13, 0x00, - 0x14, 0x00, - 0x15, 0x04, - 0x16, 0x00, - 0x17, 0x00, - 0x18, 0x00, - 0x19, 0x00, - 0x1a, 0x00, - 0x1b, 0x00, - 0x1c, 0x00, - 0x1d, 0x00, - 0x1e, 0x00, - 0x1f, 0xc8, - 0x40, 0x00, - 0x41, 0xff, - 0x42, 0xff, - 0x43, 0xff, - 0x44, 0xff, - 0x45, 0xff, - 0x46, 0xff, - 0x47, 0xff, - 0x48, 0xff, - 0x49, 0xff, - 0x4a, 0xff, - 0x4b, 0xff, - 0x4c, 0xff, - 0x4d, 0xff, - 0x4e, 0xff, - 0x4f, 0xff, - 0x50, 0xff, - 0x51, 0xff, - 0x52, 0xff, - 0x53, 0xff, - 0x54, 0xff, - 0x55, 0xff, - 0x56, 0xff, - 0x57, 0xff, - 0x58, 0x00, - 0x59, 0x54, - 0x5a, 0x07, - 0x5b, 0x83, - 0x5c, 0x00, - 0x5d, 0x00, - 0x5e, 0x00, - 0x5f, 0x00, - 0x60, 0x00, - 0x61, 0x00, - 0x00, 0x00, /* Terminator (reg 0x00 is read-only) */ -}; - -static int write_reg(struct i2c_client *client, u8 reg, u8 value) -{ - return i2c_smbus_write_byte_data(client, reg, value); -} - -static int write_regs(struct i2c_client *client, u8 *regs) -{ - int i; - - for (i = 0; regs[i] != 0x00; i += 2) - if (i2c_smbus_write_byte_data(client, regs[i], regs[i + 1]) < 0) - return -1; - return 0; -} - -static int wis_saa7113_command(struct i2c_client *client, - unsigned int cmd, void *arg) -{ - struct wis_saa7113 *dec = i2c_get_clientdata(client); - - switch (cmd) { - case VIDIOC_S_INPUT: - { - int *input = arg; - - i2c_smbus_write_byte_data(client, 0x02, 0xC0 | *input); - i2c_smbus_write_byte_data(client, 0x09, - *input < 6 ? 0x40 : 0x80); - break; - } - case VIDIOC_S_STD: - { - v4l2_std_id *input = arg; - dec->norm = *input; - if (dec->norm & V4L2_STD_NTSC) { - write_reg(client, 0x0e, 0x01); - write_reg(client, 0x10, 0x40); - } else if (dec->norm & V4L2_STD_PAL) { - write_reg(client, 0x0e, 0x01); - write_reg(client, 0x10, 0x48); - } else if (dec->norm & V4L2_STD_SECAM) { - write_reg(client, 0x0e, 0x50); - write_reg(client, 0x10, 0x48); - } - break; - } - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *ctrl = arg; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - ctrl->type = V4L2_CTRL_TYPE_INTEGER; - strncpy(ctrl->name, "Brightness", sizeof(ctrl->name)); - ctrl->minimum = 0; - ctrl->maximum = 255; - ctrl->step = 1; - ctrl->default_value = 128; - ctrl->flags = 0; - break; - case V4L2_CID_CONTRAST: - ctrl->type = V4L2_CTRL_TYPE_INTEGER; - strncpy(ctrl->name, "Contrast", sizeof(ctrl->name)); - ctrl->minimum = 0; - ctrl->maximum = 127; - ctrl->step = 1; - ctrl->default_value = 71; - ctrl->flags = 0; - break; - case V4L2_CID_SATURATION: - ctrl->type = V4L2_CTRL_TYPE_INTEGER; - strncpy(ctrl->name, "Saturation", sizeof(ctrl->name)); - ctrl->minimum = 0; - ctrl->maximum = 127; - ctrl->step = 1; - ctrl->default_value = 64; - ctrl->flags = 0; - break; - case V4L2_CID_HUE: - ctrl->type = V4L2_CTRL_TYPE_INTEGER; - strncpy(ctrl->name, "Hue", sizeof(ctrl->name)); - ctrl->minimum = -128; - ctrl->maximum = 127; - ctrl->step = 1; - ctrl->default_value = 0; - ctrl->flags = 0; - break; - } - break; - } - case VIDIOC_S_CTRL: - { - struct v4l2_control *ctrl = arg; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - if (ctrl->value > 255) - dec->brightness = 255; - else if (ctrl->value < 0) - dec->brightness = 0; - else - dec->brightness = ctrl->value; - write_reg(client, 0x0a, dec->brightness); - break; - case V4L2_CID_CONTRAST: - if (ctrl->value > 127) - dec->contrast = 127; - else if (ctrl->value < 0) - dec->contrast = 0; - else - dec->contrast = ctrl->value; - write_reg(client, 0x0b, dec->contrast); - break; - case V4L2_CID_SATURATION: - if (ctrl->value > 127) - dec->saturation = 127; - else if (ctrl->value < 0) - dec->saturation = 0; - else - dec->saturation = ctrl->value; - write_reg(client, 0x0c, dec->saturation); - break; - case V4L2_CID_HUE: - if (ctrl->value > 127) - dec->hue = 127; - else if (ctrl->value < -128) - dec->hue = -128; - else - dec->hue = ctrl->value; - write_reg(client, 0x0d, dec->hue); - break; - } - break; - } - case VIDIOC_G_CTRL: - { - struct v4l2_control *ctrl = arg; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - ctrl->value = dec->brightness; - break; - case V4L2_CID_CONTRAST: - ctrl->value = dec->contrast; - break; - case V4L2_CID_SATURATION: - ctrl->value = dec->saturation; - break; - case V4L2_CID_HUE: - ctrl->value = dec->hue; - break; - } - break; - } - default: - break; - } - return 0; -} - -static int wis_saa7113_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct i2c_adapter *adapter = client->adapter; - struct wis_saa7113 *dec; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -ENODEV; - - dec = kmalloc(sizeof(struct wis_saa7113), GFP_KERNEL); - if (dec == NULL) - return -ENOMEM; - - dec->norm = V4L2_STD_NTSC; - dec->brightness = 128; - dec->contrast = 71; - dec->saturation = 64; - dec->hue = 0; - i2c_set_clientdata(client, dec); - - dev_dbg(&client->dev, - "wis-saa7113: initializing SAA7113 at address %d on %s\n", - client->addr, adapter->name); - - if (write_regs(client, initial_registers) < 0) { - dev_err(&client->dev, - "wis-saa7113: error initializing SAA7113\n"); - kfree(dec); - return -ENODEV; - } - - return 0; -} - -static int wis_saa7113_remove(struct i2c_client *client) -{ - struct wis_saa7113 *dec = i2c_get_clientdata(client); - - kfree(dec); - return 0; -} - -static const struct i2c_device_id wis_saa7113_id[] = { - { "wis_saa7113", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, wis_saa7113_id); - -static struct i2c_driver wis_saa7113_driver = { - .driver = { - .name = "WIS SAA7113 I2C driver", - }, - .probe = wis_saa7113_probe, - .remove = wis_saa7113_remove, - .command = wis_saa7113_command, - .id_table = wis_saa7113_id, -}; - -module_i2c_driver(wis_saa7113_driver); - -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/go7007/wis-saa7115.c b/drivers/staging/media/go7007/wis-saa7115.c deleted file mode 100644 index fa86acd3fdf0..000000000000 --- a/drivers/staging/media/go7007/wis-saa7115.c +++ /dev/null @@ -1,457 +0,0 @@ -/* - * Copyright (C) 2005-2006 Micronas USA Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License (Version 2) as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. - */ - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/i2c.h> -#include <linux/videodev2.h> -#include <linux/ioctl.h> -#include <linux/slab.h> - -#include "wis-i2c.h" - -struct wis_saa7115 { - int norm; - int brightness; - int contrast; - int saturation; - int hue; -}; - -static u8 initial_registers[] = { - 0x01, 0x08, - 0x02, 0xc0, - 0x03, 0x20, - 0x04, 0x80, - 0x05, 0x80, - 0x06, 0xeb, - 0x07, 0xe0, - 0x08, 0xf0, /* always toggle FID */ - 0x09, 0x40, - 0x0a, 0x80, - 0x0b, 0x40, - 0x0c, 0x40, - 0x0d, 0x00, - 0x0e, 0x03, - 0x0f, 0x2a, - 0x10, 0x0e, - 0x11, 0x00, - 0x12, 0x8d, - 0x13, 0x00, - 0x14, 0x00, - 0x15, 0x11, - 0x16, 0x01, - 0x17, 0xda, - 0x18, 0x40, - 0x19, 0x80, - 0x1a, 0x00, - 0x1b, 0x42, - 0x1c, 0xa9, - 0x30, 0x66, - 0x31, 0x90, - 0x32, 0x01, - 0x34, 0x00, - 0x35, 0x00, - 0x36, 0x20, - 0x38, 0x03, - 0x39, 0x20, - 0x3a, 0x88, - 0x40, 0x00, - 0x41, 0xff, - 0x42, 0xff, - 0x43, 0xff, - 0x44, 0xff, - 0x45, 0xff, - 0x46, 0xff, - 0x47, 0xff, - 0x48, 0xff, - 0x49, 0xff, - 0x4a, 0xff, - 0x4b, 0xff, - 0x4c, 0xff, - 0x4d, 0xff, - 0x4e, 0xff, - 0x4f, 0xff, - 0x50, 0xff, - 0x51, 0xff, - 0x52, 0xff, - 0x53, 0xff, - 0x54, 0xf4 /*0xff*/, - 0x55, 0xff, - 0x56, 0xff, - 0x57, 0xff, - 0x58, 0x40, - 0x59, 0x47, - 0x5a, 0x06 /*0x03*/, - 0x5b, 0x83, - 0x5d, 0x06, - 0x5e, 0x00, - 0x80, 0x30, /* window defined scaler operation, task A and B enabled */ - 0x81, 0x03, /* use scaler datapath generated V */ - 0x83, 0x00, - 0x84, 0x00, - 0x85, 0x00, - 0x86, 0x45, - 0x87, 0x31, - 0x88, 0xc0, - 0x90, 0x02, /* task A process top field */ - 0x91, 0x08, - 0x92, 0x09, - 0x93, 0x80, - 0x94, 0x06, - 0x95, 0x00, - 0x96, 0xc0, - 0x97, 0x02, - 0x98, 0x12, - 0x99, 0x00, - 0x9a, 0xf2, - 0x9b, 0x00, - 0x9c, 0xd0, - 0x9d, 0x02, - 0x9e, 0xf2, - 0x9f, 0x00, - 0xa0, 0x01, - 0xa1, 0x01, - 0xa2, 0x01, - 0xa4, 0x80, - 0xa5, 0x40, - 0xa6, 0x40, - 0xa8, 0x00, - 0xa9, 0x04, - 0xaa, 0x00, - 0xac, 0x00, - 0xad, 0x02, - 0xae, 0x00, - 0xb0, 0x00, - 0xb1, 0x04, - 0xb2, 0x00, - 0xb3, 0x04, - 0xb4, 0x00, - 0xb8, 0x00, - 0xbc, 0x00, - 0xc0, 0x03, /* task B process bottom field */ - 0xc1, 0x08, - 0xc2, 0x09, - 0xc3, 0x80, - 0xc4, 0x06, - 0xc5, 0x00, - 0xc6, 0xc0, - 0xc7, 0x02, - 0xc8, 0x12, - 0xc9, 0x00, - 0xca, 0xf2, - 0xcb, 0x00, - 0xcc, 0xd0, - 0xcd, 0x02, - 0xce, 0xf2, - 0xcf, 0x00, - 0xd0, 0x01, - 0xd1, 0x01, - 0xd2, 0x01, - 0xd4, 0x80, - 0xd5, 0x40, - 0xd6, 0x40, - 0xd8, 0x00, - 0xd9, 0x04, - 0xda, 0x00, - 0xdc, 0x00, - 0xdd, 0x02, - 0xde, 0x00, - 0xe0, 0x00, - 0xe1, 0x04, - 0xe2, 0x00, - 0xe3, 0x04, - 0xe4, 0x00, - 0xe8, 0x00, - 0x88, 0xf0, /* End of original static list */ - 0x00, 0x00, /* Terminator (reg 0x00 is read-only) */ -}; - -static int write_reg(struct i2c_client *client, u8 reg, u8 value) -{ - return i2c_smbus_write_byte_data(client, reg, value); -} - -static int write_regs(struct i2c_client *client, u8 *regs) -{ - int i; - - for (i = 0; regs[i] != 0x00; i += 2) - if (i2c_smbus_write_byte_data(client, regs[i], regs[i + 1]) < 0) - return -1; - return 0; -} - -static int wis_saa7115_command(struct i2c_client *client, - unsigned int cmd, void *arg) -{ - struct wis_saa7115 *dec = i2c_get_clientdata(client); - - switch (cmd) { - case VIDIOC_S_INPUT: - { - int *input = arg; - - i2c_smbus_write_byte_data(client, 0x02, 0xC0 | *input); - i2c_smbus_write_byte_data(client, 0x09, - *input < 6 ? 0x40 : 0xC0); - break; - } - case DECODER_SET_RESOLUTION: - { - struct video_decoder_resolution *res = arg; - /* Course-grained scaler */ - int h_integer_scaler = res->width < 704 ? 704 / res->width : 1; - /* Fine-grained scaler to take care of remainder */ - int h_scaling_increment = (704 / h_integer_scaler) * - 1024 / res->width; - /* Fine-grained scaler only */ - int v_scaling_increment = (dec->norm & V4L2_STD_NTSC ? - 240 : 288) * 1024 / res->height; - u8 regs[] = { - 0x88, 0xc0, - 0x9c, res->width & 0xff, - 0x9d, res->width >> 8, - 0x9e, res->height & 0xff, - 0x9f, res->height >> 8, - 0xa0, h_integer_scaler, - 0xa1, 1, - 0xa2, 1, - 0xa8, h_scaling_increment & 0xff, - 0xa9, h_scaling_increment >> 8, - 0xac, (h_scaling_increment / 2) & 0xff, - 0xad, (h_scaling_increment / 2) >> 8, - 0xb0, v_scaling_increment & 0xff, - 0xb1, v_scaling_increment >> 8, - 0xb2, v_scaling_increment & 0xff, - 0xb3, v_scaling_increment >> 8, - 0xcc, res->width & 0xff, - 0xcd, res->width >> 8, - 0xce, res->height & 0xff, - 0xcf, res->height >> 8, - 0xd0, h_integer_scaler, - 0xd1, 1, - 0xd2, 1, - 0xd8, h_scaling_increment & 0xff, - 0xd9, h_scaling_increment >> 8, - 0xdc, (h_scaling_increment / 2) & 0xff, - 0xdd, (h_scaling_increment / 2) >> 8, - 0xe0, v_scaling_increment & 0xff, - 0xe1, v_scaling_increment >> 8, - 0xe2, v_scaling_increment & 0xff, - 0xe3, v_scaling_increment >> 8, - 0x88, 0xf0, - 0, 0, - }; - write_regs(client, regs); - break; - } - case VIDIOC_S_STD: - { - v4l2_std_id *input = arg; - u8 regs[] = { - 0x88, 0xc0, - 0x98, *input & V4L2_STD_NTSC ? 0x12 : 0x16, - 0x9a, *input & V4L2_STD_NTSC ? 0xf2 : 0x20, - 0x9b, *input & V4L2_STD_NTSC ? 0x00 : 0x01, - 0xc8, *input & V4L2_STD_NTSC ? 0x12 : 0x16, - 0xca, *input & V4L2_STD_NTSC ? 0xf2 : 0x20, - 0xcb, *input & V4L2_STD_NTSC ? 0x00 : 0x01, - 0x88, 0xf0, - 0x30, *input & V4L2_STD_NTSC ? 0x66 : 0x00, - 0x31, *input & V4L2_STD_NTSC ? 0x90 : 0xe0, - 0, 0, - }; - write_regs(client, regs); - dec->norm = *input; - break; - } - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *ctrl = arg; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - ctrl->type = V4L2_CTRL_TYPE_INTEGER; - strncpy(ctrl->name, "Brightness", sizeof(ctrl->name)); - ctrl->minimum = 0; - ctrl->maximum = 255; - ctrl->step = 1; - ctrl->default_value = 128; - ctrl->flags = 0; - break; - case V4L2_CID_CONTRAST: - ctrl->type = V4L2_CTRL_TYPE_INTEGER; - strncpy(ctrl->name, "Contrast", sizeof(ctrl->name)); - ctrl->minimum = 0; - ctrl->maximum = 127; - ctrl->step = 1; - ctrl->default_value = 64; - ctrl->flags = 0; - break; - case V4L2_CID_SATURATION: - ctrl->type = V4L2_CTRL_TYPE_INTEGER; - strncpy(ctrl->name, "Saturation", sizeof(ctrl->name)); - ctrl->minimum = 0; - ctrl->maximum = 127; - ctrl->step = 1; - ctrl->default_value = 64; - ctrl->flags = 0; - break; - case V4L2_CID_HUE: - ctrl->type = V4L2_CTRL_TYPE_INTEGER; - strncpy(ctrl->name, "Hue", sizeof(ctrl->name)); - ctrl->minimum = -128; - ctrl->maximum = 127; - ctrl->step = 1; - ctrl->default_value = 0; - ctrl->flags = 0; - break; - } - break; - } - case VIDIOC_S_CTRL: - { - struct v4l2_control *ctrl = arg; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - if (ctrl->value > 255) - dec->brightness = 255; - else if (ctrl->value < 0) - dec->brightness = 0; - else - dec->brightness = ctrl->value; - write_reg(client, 0x0a, dec->brightness); - break; - case V4L2_CID_CONTRAST: - if (ctrl->value > 127) - dec->contrast = 127; - else if (ctrl->value < 0) - dec->contrast = 0; - else - dec->contrast = ctrl->value; - write_reg(client, 0x0b, dec->contrast); - break; - case V4L2_CID_SATURATION: - if (ctrl->value > 127) - dec->saturation = 127; - else if (ctrl->value < 0) - dec->saturation = 0; - else - dec->saturation = ctrl->value; - write_reg(client, 0x0c, dec->saturation); - break; - case V4L2_CID_HUE: - if (ctrl->value > 127) - dec->hue = 127; - else if (ctrl->value < -128) - dec->hue = -128; - else - dec->hue = ctrl->value; - write_reg(client, 0x0d, dec->hue); - break; - } - break; - } - case VIDIOC_G_CTRL: - { - struct v4l2_control *ctrl = arg; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - ctrl->value = dec->brightness; - break; - case V4L2_CID_CONTRAST: - ctrl->value = dec->contrast; - break; - case V4L2_CID_SATURATION: - ctrl->value = dec->saturation; - break; - case V4L2_CID_HUE: - ctrl->value = dec->hue; - break; - } - break; - } - default: - break; - } - return 0; -} - -static int wis_saa7115_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct i2c_adapter *adapter = client->adapter; - struct wis_saa7115 *dec; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -ENODEV; - - dec = kmalloc(sizeof(struct wis_saa7115), GFP_KERNEL); - if (dec == NULL) - return -ENOMEM; - - dec->norm = V4L2_STD_NTSC; - dec->brightness = 128; - dec->contrast = 64; - dec->saturation = 64; - dec->hue = 0; - i2c_set_clientdata(client, dec); - - dev_dbg(&client->dev, - "wis-saa7115: initializing SAA7115 at address %d on %s\n", - client->addr, adapter->name); - - if (write_regs(client, initial_registers) < 0) { - dev_err(&client->dev, - "wis-saa7115: error initializing SAA7115\n"); - kfree(dec); - return -ENODEV; - } - - return 0; -} - -static int wis_saa7115_remove(struct i2c_client *client) -{ - struct wis_saa7115 *dec = i2c_get_clientdata(client); - - kfree(dec); - return 0; -} - -static const struct i2c_device_id wis_saa7115_id[] = { - { "wis_saa7115", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, wis_saa7115_id); - -static struct i2c_driver wis_saa7115_driver = { - .driver = { - .name = "WIS SAA7115 I2C driver", - }, - .probe = wis_saa7115_probe, - .remove = wis_saa7115_remove, - .command = wis_saa7115_command, - .id_table = wis_saa7115_id, -}; - -module_i2c_driver(wis_saa7115_driver); - -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/go7007/wis-sony-tuner.c b/drivers/staging/media/go7007/wis-sony-tuner.c deleted file mode 100644 index 5d7ff8c81d6d..000000000000 --- a/drivers/staging/media/go7007/wis-sony-tuner.c +++ /dev/null @@ -1,707 +0,0 @@ -/* - * Copyright (C) 2005-2006 Micronas USA Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License (Version 2) as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. - */ - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/i2c.h> -#include <linux/videodev2.h> -#include <linux/slab.h> -#include <media/tuner.h> -#include <media/v4l2-common.h> -#include <media/v4l2-ioctl.h> - -#include "wis-i2c.h" - -/* #define MPX_DEBUG */ - -/* AS(IF/MPX) pin: LOW HIGH/OPEN - * IF/MPX address: 0x42/0x40 0x43/0x44 - */ -#define IF_I2C_ADDR 0x43 -#define MPX_I2C_ADDR 0x44 - -static v4l2_std_id force_band; -static char force_band_str[] = "-"; -module_param_string(force_band, force_band_str, sizeof(force_band_str), 0644); -static int force_mpx_mode = -1; -module_param(force_mpx_mode, int, 0644); - -/* Store tuner info in the same format as tuner.c, so maybe we can put the - * Sony tuner support in there. */ -struct sony_tunertype { - char *name; - unsigned char Vendor; /* unused here */ - unsigned char Type; /* unused here */ - - unsigned short thresh1; /* band switch VHF_LO <=> VHF_HI */ - unsigned short thresh2; /* band switch VHF_HI <=> UHF */ - unsigned char VHF_L; - unsigned char VHF_H; - unsigned char UHF; - unsigned char config; - unsigned short IFPCoff; -}; - -/* This array is indexed by (tuner_type - 200) */ -static struct sony_tunertype sony_tuners[] = { - { "Sony PAL+SECAM (BTF-PG472Z)", 0, 0, - 16*144.25, 16*427.25, 0x01, 0x02, 0x04, 0xc6, 623}, - { "Sony NTSC_JP (BTF-PK467Z)", 0, 0, - 16*220.25, 16*467.25, 0x01, 0x02, 0x04, 0xc6, 940}, - { "Sony NTSC (BTF-PB463Z)", 0, 0, - 16*130.25, 16*364.25, 0x01, 0x02, 0x04, 0xc6, 732}, -}; - -struct wis_sony_tuner { - int type; - v4l2_std_id std; - unsigned int freq; - int mpxmode; - u32 audmode; -}; - -/* Basically the same as default_set_tv_freq() in tuner.c */ -static int set_freq(struct i2c_client *client, int freq) -{ - struct wis_sony_tuner *t = i2c_get_clientdata(client); - char *band_name; - int n; - int band_select; - struct sony_tunertype *tun; - u8 buffer[4]; - - tun = &sony_tuners[t->type - 200]; - if (freq < tun->thresh1) { - band_name = "VHF_L"; - band_select = tun->VHF_L; - } else if (freq < tun->thresh2) { - band_name = "VHF_H"; - band_select = tun->VHF_H; - } else { - band_name = "UHF"; - band_select = tun->UHF; - } - dev_dbg(&client->dev, "tuning to frequency %d.%04d (%s)\n", - freq / 16, (freq % 16) * 625, band_name); - n = freq + tun->IFPCoff; - - buffer[0] = n >> 8; - buffer[1] = n & 0xff; - buffer[2] = tun->config; - buffer[3] = band_select; - i2c_master_send(client, buffer, 4); - - return 0; -} - -static int mpx_write(struct i2c_client *client, int dev, int addr, int val) -{ - u8 buffer[5]; - struct i2c_msg msg; - - buffer[0] = dev; - buffer[1] = addr >> 8; - buffer[2] = addr & 0xff; - buffer[3] = val >> 8; - buffer[4] = val & 0xff; - msg.addr = MPX_I2C_ADDR; - msg.flags = 0; - msg.len = 5; - msg.buf = buffer; - i2c_transfer(client->adapter, &msg, 1); - return 0; -} - -/* - * MPX register values for the BTF-PG472Z: - * - * FM_ NICAM_ SCART_ - * MODUS SOURCE ACB PRESCAL PRESCAL PRESCAL SYSTEM VOLUME - * 10/0030 12/0008 12/0013 12/000E 12/0010 12/0000 10/0020 12/0000 - * --------------------------------------------------------------- - * Auto 1003 0020 0100 2603 5000 XXXX 0001 7500 - * - * B/G - * Mono 1003 0020 0100 2603 5000 XXXX 0003 7500 - * A2 1003 0020 0100 2601 5000 XXXX 0003 7500 - * NICAM 1003 0120 0100 2603 5000 XXXX 0008 7500 - * - * I - * Mono 1003 0020 0100 2603 7900 XXXX 000A 7500 - * NICAM 1003 0120 0100 2603 7900 XXXX 000A 7500 - * - * D/K - * Mono 1003 0020 0100 2603 5000 XXXX 0004 7500 - * A2-1 1003 0020 0100 2601 5000 XXXX 0004 7500 - * A2-2 1003 0020 0100 2601 5000 XXXX 0005 7500 - * A2-3 1003 0020 0100 2601 5000 XXXX 0007 7500 - * NICAM 1003 0120 0100 2603 5000 XXXX 000B 7500 - * - * L/L' - * Mono 0003 0200 0100 7C03 5000 2200 0009 7500 - * NICAM 0003 0120 0100 7C03 5000 XXXX 0009 7500 - * - * M - * Mono 1003 0200 0100 2B03 5000 2B00 0002 7500 - * - * For Asia, replace the 0x26XX in FM_PRESCALE with 0x14XX. - * - * Bilingual selection in A2/NICAM: - * - * High byte of SOURCE Left chan Right chan - * 0x01 MAIN SUB - * 0x03 MAIN MAIN - * 0x04 SUB SUB - * - * Force mono in NICAM by setting the high byte of SOURCE to 0x02 (L/L') or - * 0x00 (all other bands). Force mono in A2 with FMONO_A2: - * - * FMONO_A2 - * 10/0022 - * -------- - * Forced mono ON 07F0 - * Forced mono OFF 0190 - */ - -static struct { - enum { AUD_MONO, AUD_A2, AUD_NICAM, AUD_NICAM_L } audio_mode; - u16 modus; - u16 source; - u16 acb; - u16 fm_prescale; - u16 nicam_prescale; - u16 scart_prescale; - u16 system; - u16 volume; -} mpx_audio_modes[] = { - /* Auto */ { AUD_MONO, 0x1003, 0x0020, 0x0100, 0x2603, - 0x5000, 0x0000, 0x0001, 0x7500 }, - /* B/G Mono */ { AUD_MONO, 0x1003, 0x0020, 0x0100, 0x2603, - 0x5000, 0x0000, 0x0003, 0x7500 }, - /* B/G A2 */ { AUD_A2, 0x1003, 0x0020, 0x0100, 0x2601, - 0x5000, 0x0000, 0x0003, 0x7500 }, - /* B/G NICAM */ { AUD_NICAM, 0x1003, 0x0120, 0x0100, 0x2603, - 0x5000, 0x0000, 0x0008, 0x7500 }, - /* I Mono */ { AUD_MONO, 0x1003, 0x0020, 0x0100, 0x2603, - 0x7900, 0x0000, 0x000A, 0x7500 }, - /* I NICAM */ { AUD_NICAM, 0x1003, 0x0120, 0x0100, 0x2603, - 0x7900, 0x0000, 0x000A, 0x7500 }, - /* D/K Mono */ { AUD_MONO, 0x1003, 0x0020, 0x0100, 0x2603, - 0x5000, 0x0000, 0x0004, 0x7500 }, - /* D/K A2-1 */ { AUD_A2, 0x1003, 0x0020, 0x0100, 0x2601, - 0x5000, 0x0000, 0x0004, 0x7500 }, - /* D/K A2-2 */ { AUD_A2, 0x1003, 0x0020, 0x0100, 0x2601, - 0x5000, 0x0000, 0x0005, 0x7500 }, - /* D/K A2-3 */ { AUD_A2, 0x1003, 0x0020, 0x0100, 0x2601, - 0x5000, 0x0000, 0x0007, 0x7500 }, - /* D/K NICAM */ { AUD_NICAM, 0x1003, 0x0120, 0x0100, 0x2603, - 0x5000, 0x0000, 0x000B, 0x7500 }, - /* L/L' Mono */ { AUD_MONO, 0x0003, 0x0200, 0x0100, 0x7C03, - 0x5000, 0x2200, 0x0009, 0x7500 }, - /* L/L' NICAM */{ AUD_NICAM_L, 0x0003, 0x0120, 0x0100, 0x7C03, - 0x5000, 0x0000, 0x0009, 0x7500 }, -}; - -#define MPX_NUM_MODES ARRAY_SIZE(mpx_audio_modes) - -static int mpx_setup(struct i2c_client *client) -{ - struct wis_sony_tuner *t = i2c_get_clientdata(client); - u16 source = 0; - u8 buffer[3]; - struct i2c_msg msg; - - /* reset MPX */ - buffer[0] = 0x00; - buffer[1] = 0x80; - buffer[2] = 0x00; - msg.addr = MPX_I2C_ADDR; - msg.flags = 0; - msg.len = 3; - msg.buf = buffer; - i2c_transfer(client->adapter, &msg, 1); - buffer[1] = 0x00; - i2c_transfer(client->adapter, &msg, 1); - - if (mpx_audio_modes[t->mpxmode].audio_mode != AUD_MONO) { - switch (t->audmode) { - case V4L2_TUNER_MODE_MONO: - switch (mpx_audio_modes[t->mpxmode].audio_mode) { - case AUD_A2: - source = mpx_audio_modes[t->mpxmode].source; - break; - case AUD_NICAM: - source = 0x0000; - break; - case AUD_NICAM_L: - source = 0x0200; - break; - default: - break; - } - break; - case V4L2_TUNER_MODE_STEREO: - source = mpx_audio_modes[t->mpxmode].source; - break; - case V4L2_TUNER_MODE_LANG1: - source = 0x0300; - break; - case V4L2_TUNER_MODE_LANG2: - source = 0x0400; - break; - } - source |= mpx_audio_modes[t->mpxmode].source & 0x00ff; - } else - source = mpx_audio_modes[t->mpxmode].source; - - mpx_write(client, 0x10, 0x0030, mpx_audio_modes[t->mpxmode].modus); - mpx_write(client, 0x12, 0x0008, source); - mpx_write(client, 0x12, 0x0013, mpx_audio_modes[t->mpxmode].acb); - mpx_write(client, 0x12, 0x000e, - mpx_audio_modes[t->mpxmode].fm_prescale); - mpx_write(client, 0x12, 0x0010, - mpx_audio_modes[t->mpxmode].nicam_prescale); - mpx_write(client, 0x12, 0x000d, - mpx_audio_modes[t->mpxmode].scart_prescale); - mpx_write(client, 0x10, 0x0020, mpx_audio_modes[t->mpxmode].system); - mpx_write(client, 0x12, 0x0000, mpx_audio_modes[t->mpxmode].volume); - if (mpx_audio_modes[t->mpxmode].audio_mode == AUD_A2) - mpx_write(client, 0x10, 0x0022, - t->audmode == V4L2_TUNER_MODE_MONO ? 0x07f0 : 0x0190); - -#ifdef MPX_DEBUG - { - u8 buf1[3], buf2[2]; - struct i2c_msg msgs[2]; - - dev_dbg(&client->dev, - "MPX registers: %04x %04x %04x %04x %04x %04x %04x %04x\n", - mpx_audio_modes[t->mpxmode].modus, - source, - mpx_audio_modes[t->mpxmode].acb, - mpx_audio_modes[t->mpxmode].fm_prescale, - mpx_audio_modes[t->mpxmode].nicam_prescale, - mpx_audio_modes[t->mpxmode].scart_prescale, - mpx_audio_modes[t->mpxmode].system, - mpx_audio_modes[t->mpxmode].volume); - buf1[0] = 0x11; - buf1[1] = 0x00; - buf1[2] = 0x7e; - msgs[0].addr = MPX_I2C_ADDR; - msgs[0].flags = 0; - msgs[0].len = 3; - msgs[0].buf = buf1; - msgs[1].addr = MPX_I2C_ADDR; - msgs[1].flags = I2C_M_RD; - msgs[1].len = 2; - msgs[1].buf = buf2; - i2c_transfer(client->adapter, msgs, 2); - dev_dbg(&client->dev, "MPX system: %02x%02x\n", - buf2[0], buf2[1]); - buf1[0] = 0x11; - buf1[1] = 0x02; - buf1[2] = 0x00; - i2c_transfer(client->adapter, msgs, 2); - dev_dbg(&client->dev, "MPX status: %02x%02x\n", - buf2[0], buf2[1]); - } -#endif - return 0; -} - -/* - * IF configuration values for the BTF-PG472Z: - * - * B/G: 0x94 0x70 0x49 - * I: 0x14 0x70 0x4a - * D/K: 0x14 0x70 0x4b - * L: 0x04 0x70 0x4b - * L': 0x44 0x70 0x53 - * M: 0x50 0x30 0x4c - */ - -static int set_if(struct i2c_client *client) -{ - struct wis_sony_tuner *t = i2c_get_clientdata(client); - u8 buffer[4]; - struct i2c_msg msg; - int default_mpx_mode = 0; - - /* configure IF */ - buffer[0] = 0; - if (t->std & V4L2_STD_PAL_BG) { - buffer[1] = 0x94; - buffer[2] = 0x70; - buffer[3] = 0x49; - default_mpx_mode = 1; - } else if (t->std & V4L2_STD_PAL_I) { - buffer[1] = 0x14; - buffer[2] = 0x70; - buffer[3] = 0x4a; - default_mpx_mode = 4; - } else if (t->std & V4L2_STD_PAL_DK) { - buffer[1] = 0x14; - buffer[2] = 0x70; - buffer[3] = 0x4b; - default_mpx_mode = 6; - } else if (t->std & V4L2_STD_SECAM_L) { - buffer[1] = 0x04; - buffer[2] = 0x70; - buffer[3] = 0x4b; - default_mpx_mode = 11; - } - msg.addr = IF_I2C_ADDR; - msg.flags = 0; - msg.len = 4; - msg.buf = buffer; - i2c_transfer(client->adapter, &msg, 1); - - /* Select MPX mode if not forced by the user */ - if (force_mpx_mode >= 0 && force_mpx_mode < MPX_NUM_MODES) - t->mpxmode = force_mpx_mode; - else - t->mpxmode = default_mpx_mode; - dev_dbg(&client->dev, "setting MPX to mode %d\n", t->mpxmode); - mpx_setup(client); - - return 0; -} - -static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) -{ - struct wis_sony_tuner *t = i2c_get_clientdata(client); - - switch (cmd) { -#if 0 -#ifdef TUNER_SET_TYPE_ADDR - case TUNER_SET_TYPE_ADDR: - { - struct tuner_setup *tun_setup = arg; - int *type = &tun_setup->type; -#else - case TUNER_SET_TYPE: - { - int *type = arg; -#endif - - if (t->type >= 0) { - if (t->type != *type) - dev_err(&client->dev, - "type already set to %d, ignoring request for %d\n", - t->type, *type); - break; - } - t->type = *type; - switch (t->type) { - case TUNER_SONY_BTF_PG472Z: - switch (force_band_str[0]) { - case 'b': - case 'B': - case 'g': - case 'G': - dev_info(&client->dev, - "forcing tuner to PAL-B/G bands\n"); - force_band = V4L2_STD_PAL_BG; - break; - case 'i': - case 'I': - dev_info(&client->dev, - "forcing tuner to PAL-I band\n"); - force_band = V4L2_STD_PAL_I; - break; - case 'd': - case 'D': - case 'k': - case 'K': - dev_info(&client->dev, - "forcing tuner to PAL-D/K bands\n"); - force_band = V4L2_STD_PAL_I; - break; - case 'l': - case 'L': - dev_info(&client->dev, - "forcing tuner to SECAM-L band\n"); - force_band = V4L2_STD_SECAM_L; - break; - default: - force_band = 0; - break; - } - if (force_band) - t->std = force_band; - else - t->std = V4L2_STD_PAL_BG; - set_if(client); - break; - case TUNER_SONY_BTF_PK467Z: - t->std = V4L2_STD_NTSC_M_JP; - break; - case TUNER_SONY_BTF_PB463Z: - t->std = V4L2_STD_NTSC_M; - break; - default: - dev_err(&client->dev, - "tuner type %d is not supported by this module\n", - *type); - break; - } - if (type >= 0) - dev_info(&clinet->dev, - "type set to %d (%s)\n", - t->type, sony_tuners[t->type - 200].name); - break; - } -#endif - case VIDIOC_G_FREQUENCY: - { - struct v4l2_frequency *f = arg; - - f->frequency = t->freq; - break; - } - case VIDIOC_S_FREQUENCY: - { - struct v4l2_frequency *f = arg; - - t->freq = f->frequency; - set_freq(client, t->freq); - break; - } - case VIDIOC_ENUMSTD: - { - struct v4l2_standard *std = arg; - - switch (t->type) { - case TUNER_SONY_BTF_PG472Z: - switch (std->index) { - case 0: - v4l2_video_std_construct(std, - V4L2_STD_PAL_BG, "PAL-B/G"); - break; - case 1: - v4l2_video_std_construct(std, - V4L2_STD_PAL_I, "PAL-I"); - break; - case 2: - v4l2_video_std_construct(std, - V4L2_STD_PAL_DK, "PAL-D/K"); - break; - case 3: - v4l2_video_std_construct(std, - V4L2_STD_SECAM_L, "SECAM-L"); - break; - default: - std->id = 0; /* hack to indicate EINVAL */ - break; - } - break; - case TUNER_SONY_BTF_PK467Z: - if (std->index != 0) { - std->id = 0; /* hack to indicate EINVAL */ - break; - } - v4l2_video_std_construct(std, - V4L2_STD_NTSC_M_JP, "NTSC-J"); - break; - case TUNER_SONY_BTF_PB463Z: - if (std->index != 0) { - std->id = 0; /* hack to indicate EINVAL */ - break; - } - v4l2_video_std_construct(std, V4L2_STD_NTSC_M, "NTSC"); - break; - } - break; - } - case VIDIOC_G_STD: - { - v4l2_std_id *std = arg; - - *std = t->std; - break; - } - case VIDIOC_S_STD: - { - v4l2_std_id *std = arg; - v4l2_std_id old = t->std; - - switch (t->type) { - case TUNER_SONY_BTF_PG472Z: - if (force_band && (*std & force_band) != *std && - *std != V4L2_STD_PAL && - *std != V4L2_STD_SECAM) { - dev_dbg(&client->dev, - "ignoring requested TV standard in favor of force_band value\n"); - t->std = force_band; - } else if (*std & V4L2_STD_PAL_BG) { /* default */ - t->std = V4L2_STD_PAL_BG; - } else if (*std & V4L2_STD_PAL_I) { - t->std = V4L2_STD_PAL_I; - } else if (*std & V4L2_STD_PAL_DK) { - t->std = V4L2_STD_PAL_DK; - } else if (*std & V4L2_STD_SECAM_L) { - t->std = V4L2_STD_SECAM_L; - } else { - dev_err(&client->dev, - "TV standard not supported\n"); - *std = 0; /* hack to indicate EINVAL */ - break; - } - if (old != t->std) - set_if(client); - break; - case TUNER_SONY_BTF_PK467Z: - if (!(*std & V4L2_STD_NTSC_M_JP)) { - dev_err(&client->dev, - "TV standard not supported\n"); - *std = 0; /* hack to indicate EINVAL */ - } - break; - case TUNER_SONY_BTF_PB463Z: - if (!(*std & V4L2_STD_NTSC_M)) { - dev_err(&client->dev, - "TV standard not supported\n"); - *std = 0; /* hack to indicate EINVAL */ - } - break; - } - break; - } - case VIDIOC_QUERYSTD: - { - v4l2_std_id *std = arg; - - switch (t->type) { - case TUNER_SONY_BTF_PG472Z: - if (force_band) - *std = force_band; - else - *std = V4L2_STD_PAL_BG | V4L2_STD_PAL_I | - V4L2_STD_PAL_DK | V4L2_STD_SECAM_L; - break; - case TUNER_SONY_BTF_PK467Z: - *std = V4L2_STD_NTSC_M_JP; - break; - case TUNER_SONY_BTF_PB463Z: - *std = V4L2_STD_NTSC_M; - break; - } - break; - } - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *tun = arg; - - memset(tun, 0, sizeof(*tun)); - strcpy(tun->name, "Television"); - tun->type = V4L2_TUNER_ANALOG_TV; - tun->rangelow = 0UL; /* does anything use these? */ - tun->rangehigh = 0xffffffffUL; - switch (t->type) { - case TUNER_SONY_BTF_PG472Z: - tun->capability = V4L2_TUNER_CAP_NORM | - V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 | - V4L2_TUNER_CAP_LANG2; - tun->rxsubchans = V4L2_TUNER_SUB_MONO | - V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_LANG1 | - V4L2_TUNER_SUB_LANG2; - break; - case TUNER_SONY_BTF_PK467Z: - case TUNER_SONY_BTF_PB463Z: - tun->capability = V4L2_TUNER_CAP_STEREO; - tun->rxsubchans = V4L2_TUNER_SUB_MONO | - V4L2_TUNER_SUB_STEREO; - break; - } - tun->audmode = t->audmode; - return 0; - } - case VIDIOC_S_TUNER: - { - struct v4l2_tuner *tun = arg; - - switch (t->type) { - case TUNER_SONY_BTF_PG472Z: - if (tun->audmode != t->audmode) { - t->audmode = tun->audmode; - mpx_setup(client); - } - break; - case TUNER_SONY_BTF_PK467Z: - case TUNER_SONY_BTF_PB463Z: - break; - } - return 0; - } - default: - break; - } - return 0; -} - -static int wis_sony_tuner_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct i2c_adapter *adapter = client->adapter; - struct wis_sony_tuner *t; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) - return -ENODEV; - - t = kmalloc(sizeof(struct wis_sony_tuner), GFP_KERNEL); - if (t == NULL) - return -ENOMEM; - - t->type = -1; - t->freq = 0; - t->mpxmode = 0; - t->audmode = V4L2_TUNER_MODE_STEREO; - i2c_set_clientdata(client, t); - - dev_dbg(&client->dev, "initializing tuner at address %d on %s\n", - client->addr, adapter->name); - - return 0; -} - -static int wis_sony_tuner_remove(struct i2c_client *client) -{ - struct wis_sony_tuner *t = i2c_get_clientdata(client); - - kfree(t); - return 0; -} - -static const struct i2c_device_id wis_sony_tuner_id[] = { - { "wis_sony_tuner", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, wis_sony_tuner_id); - -static struct i2c_driver wis_sony_tuner_driver = { - .driver = { - .name = "WIS Sony TV Tuner I2C driver", - }, - .probe = wis_sony_tuner_probe, - .remove = wis_sony_tuner_remove, - .command = tuner_command, - .id_table = wis_sony_tuner_id, -}; - -module_i2c_driver(wis_sony_tuner_driver); - -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/go7007/wis-tw2804.c b/drivers/staging/media/go7007/wis-tw2804.c deleted file mode 100644 index 290fd8c7bfef..000000000000 --- a/drivers/staging/media/go7007/wis-tw2804.c +++ /dev/null @@ -1,348 +0,0 @@ -/* - * Copyright (C) 2005-2006 Micronas USA Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License (Version 2) as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. - */ - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/i2c.h> -#include <linux/videodev2.h> -#include <linux/ioctl.h> -#include <linux/slab.h> - -#include "wis-i2c.h" - -struct wis_tw2804 { - int channel; - int norm; - int brightness; - int contrast; - int saturation; - int hue; -}; - -static u8 global_registers[] = { - 0x39, 0x00, - 0x3a, 0xff, - 0x3b, 0x84, - 0x3c, 0x80, - 0x3d, 0x80, - 0x3e, 0x82, - 0x3f, 0x82, - 0xff, 0xff, /* Terminator (reg 0xff does not exist) */ -}; - -static u8 channel_registers[] = { - 0x01, 0xc4, - 0x02, 0xa5, - 0x03, 0x20, - 0x04, 0xd0, - 0x05, 0x20, - 0x06, 0xd0, - 0x07, 0x88, - 0x08, 0x20, - 0x09, 0x07, - 0x0a, 0xf0, - 0x0b, 0x07, - 0x0c, 0xf0, - 0x0d, 0x40, - 0x0e, 0xd2, - 0x0f, 0x80, - 0x10, 0x80, - 0x11, 0x80, - 0x12, 0x80, - 0x13, 0x1f, - 0x14, 0x00, - 0x15, 0x00, - 0x16, 0x00, - 0x17, 0x00, - 0x18, 0xff, - 0x19, 0xff, - 0x1a, 0xff, - 0x1b, 0xff, - 0x1c, 0xff, - 0x1d, 0xff, - 0x1e, 0xff, - 0x1f, 0xff, - 0x20, 0x07, - 0x21, 0x07, - 0x22, 0x00, - 0x23, 0x91, - 0x24, 0x51, - 0x25, 0x03, - 0x26, 0x00, - 0x27, 0x00, - 0x28, 0x00, - 0x29, 0x00, - 0x2a, 0x00, - 0x2b, 0x00, - 0x2c, 0x00, - 0x2d, 0x00, - 0x2e, 0x00, - 0x2f, 0x00, - 0x30, 0x00, - 0x31, 0x00, - 0x32, 0x00, - 0x33, 0x00, - 0x34, 0x00, - 0x35, 0x00, - 0x36, 0x00, - 0x37, 0x00, - 0xff, 0xff, /* Terminator (reg 0xff does not exist) */ -}; - -static int write_reg(struct i2c_client *client, u8 reg, u8 value, int channel) -{ - return i2c_smbus_write_byte_data(client, reg | (channel << 6), value); -} - -static int write_regs(struct i2c_client *client, u8 *regs, int channel) -{ - int i; - - for (i = 0; regs[i] != 0xff; i += 2) - if (i2c_smbus_write_byte_data(client, - regs[i] | (channel << 6), regs[i + 1]) < 0) - return -1; - return 0; -} - -static int wis_tw2804_command(struct i2c_client *client, - unsigned int cmd, void *arg) -{ - struct wis_tw2804 *dec = i2c_get_clientdata(client); - - if (cmd == DECODER_SET_CHANNEL) { - int *input = arg; - - if (*input < 0 || *input > 3) { - dev_err(&client->dev, - "channel %d is not between 0 and 3!\n", *input); - return 0; - } - dec->channel = *input; - dev_dbg(&client->dev, "initializing TW2804 channel %d\n", - dec->channel); - if (dec->channel == 0 && - write_regs(client, global_registers, 0) < 0) { - dev_err(&client->dev, - "error initializing TW2804 global registers\n"); - return 0; - } - if (write_regs(client, channel_registers, dec->channel) < 0) { - dev_err(&client->dev, - "error initializing TW2804 channel %d\n", - dec->channel); - return 0; - } - return 0; - } - - if (dec->channel < 0) { - dev_dbg(&client->dev, - "ignoring command %08x until channel number is set\n", - cmd); - return 0; - } - - switch (cmd) { - case VIDIOC_S_STD: - { - v4l2_std_id *input = arg; - u8 regs[] = { - 0x01, *input & V4L2_STD_NTSC ? 0xc4 : 0x84, - 0x09, *input & V4L2_STD_NTSC ? 0x07 : 0x04, - 0x0a, *input & V4L2_STD_NTSC ? 0xf0 : 0x20, - 0x0b, *input & V4L2_STD_NTSC ? 0x07 : 0x04, - 0x0c, *input & V4L2_STD_NTSC ? 0xf0 : 0x20, - 0x0d, *input & V4L2_STD_NTSC ? 0x40 : 0x4a, - 0x16, *input & V4L2_STD_NTSC ? 0x00 : 0x40, - 0x17, *input & V4L2_STD_NTSC ? 0x00 : 0x40, - 0x20, *input & V4L2_STD_NTSC ? 0x07 : 0x0f, - 0x21, *input & V4L2_STD_NTSC ? 0x07 : 0x0f, - 0xff, 0xff, - }; - write_regs(client, regs, dec->channel); - dec->norm = *input; - break; - } - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *ctrl = arg; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - ctrl->type = V4L2_CTRL_TYPE_INTEGER; - strncpy(ctrl->name, "Brightness", sizeof(ctrl->name)); - ctrl->minimum = 0; - ctrl->maximum = 255; - ctrl->step = 1; - ctrl->default_value = 128; - ctrl->flags = 0; - break; - case V4L2_CID_CONTRAST: - ctrl->type = V4L2_CTRL_TYPE_INTEGER; - strncpy(ctrl->name, "Contrast", sizeof(ctrl->name)); - ctrl->minimum = 0; - ctrl->maximum = 255; - ctrl->step = 1; - ctrl->default_value = 128; - ctrl->flags = 0; - break; - case V4L2_CID_SATURATION: - ctrl->type = V4L2_CTRL_TYPE_INTEGER; - strncpy(ctrl->name, "Saturation", sizeof(ctrl->name)); - ctrl->minimum = 0; - ctrl->maximum = 255; - ctrl->step = 1; - ctrl->default_value = 128; - ctrl->flags = 0; - break; - case V4L2_CID_HUE: - ctrl->type = V4L2_CTRL_TYPE_INTEGER; - strncpy(ctrl->name, "Hue", sizeof(ctrl->name)); - ctrl->minimum = 0; - ctrl->maximum = 255; - ctrl->step = 1; - ctrl->default_value = 128; - ctrl->flags = 0; - break; - } - break; - } - case VIDIOC_S_CTRL: - { - struct v4l2_control *ctrl = arg; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - if (ctrl->value > 255) - dec->brightness = 255; - else if (ctrl->value < 0) - dec->brightness = 0; - else - dec->brightness = ctrl->value; - write_reg(client, 0x12, dec->brightness, dec->channel); - break; - case V4L2_CID_CONTRAST: - if (ctrl->value > 255) - dec->contrast = 255; - else if (ctrl->value < 0) - dec->contrast = 0; - else - dec->contrast = ctrl->value; - write_reg(client, 0x11, dec->contrast, dec->channel); - break; - case V4L2_CID_SATURATION: - if (ctrl->value > 255) - dec->saturation = 255; - else if (ctrl->value < 0) - dec->saturation = 0; - else - dec->saturation = ctrl->value; - write_reg(client, 0x10, dec->saturation, dec->channel); - break; - case V4L2_CID_HUE: - if (ctrl->value > 255) - dec->hue = 255; - else if (ctrl->value < 0) - dec->hue = 0; - else - dec->hue = ctrl->value; - write_reg(client, 0x0f, dec->hue, dec->channel); - break; - } - break; - } - case VIDIOC_G_CTRL: - { - struct v4l2_control *ctrl = arg; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - ctrl->value = dec->brightness; - break; - case V4L2_CID_CONTRAST: - ctrl->value = dec->contrast; - break; - case V4L2_CID_SATURATION: - ctrl->value = dec->saturation; - break; - case V4L2_CID_HUE: - ctrl->value = dec->hue; - break; - } - break; - } - default: - break; - } - return 0; -} - -static int wis_tw2804_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct i2c_adapter *adapter = client->adapter; - struct wis_tw2804 *dec; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -ENODEV; - - dec = kmalloc(sizeof(struct wis_tw2804), GFP_KERNEL); - if (dec == NULL) - return -ENOMEM; - - dec->channel = -1; - dec->norm = V4L2_STD_NTSC; - dec->brightness = 128; - dec->contrast = 128; - dec->saturation = 128; - dec->hue = 128; - i2c_set_clientdata(client, dec); - - dev_dbg(&client->dev, "creating TW2804 at address %d on %s\n", - client->addr, adapter->name); - - return 0; -} - -static int wis_tw2804_remove(struct i2c_client *client) -{ - struct wis_tw2804 *dec = i2c_get_clientdata(client); - - kfree(dec); - return 0; -} - -static const struct i2c_device_id wis_tw2804_id[] = { - { "wis_tw2804", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, wis_tw2804_id); - -static struct i2c_driver wis_tw2804_driver = { - .driver = { - .name = "WIS TW2804 I2C driver", - }, - .probe = wis_tw2804_probe, - .remove = wis_tw2804_remove, - .command = wis_tw2804_command, - .id_table = wis_tw2804_id, -}; - -module_i2c_driver(wis_tw2804_driver); - -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/go7007/wis-tw9903.c b/drivers/staging/media/go7007/wis-tw9903.c deleted file mode 100644 index 684ca37f0382..000000000000 --- a/drivers/staging/media/go7007/wis-tw9903.c +++ /dev/null @@ -1,328 +0,0 @@ -/* - * Copyright (C) 2005-2006 Micronas USA Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License (Version 2) as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. - */ - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/i2c.h> -#include <linux/videodev2.h> -#include <linux/ioctl.h> -#include <linux/slab.h> - -#include "wis-i2c.h" - -struct wis_tw9903 { - int norm; - int brightness; - int contrast; - int hue; -}; - -static u8 initial_registers[] = { - 0x02, 0x44, /* input 1, composite */ - 0x03, 0x92, /* correct digital format */ - 0x04, 0x00, - 0x05, 0x80, /* or 0x00 for PAL */ - 0x06, 0x40, /* second internal current reference */ - 0x07, 0x02, /* window */ - 0x08, 0x14, /* window */ - 0x09, 0xf0, /* window */ - 0x0a, 0x81, /* window */ - 0x0b, 0xd0, /* window */ - 0x0c, 0x8c, - 0x0d, 0x00, /* scaling */ - 0x0e, 0x11, /* scaling */ - 0x0f, 0x00, /* scaling */ - 0x10, 0x00, /* brightness */ - 0x11, 0x60, /* contrast */ - 0x12, 0x01, /* sharpness */ - 0x13, 0x7f, /* U gain */ - 0x14, 0x5a, /* V gain */ - 0x15, 0x00, /* hue */ - 0x16, 0xc3, /* sharpness */ - 0x18, 0x00, - 0x19, 0x58, /* vbi */ - 0x1a, 0x80, - 0x1c, 0x0f, /* video norm */ - 0x1d, 0x7f, /* video norm */ - 0x20, 0xa0, /* clamping gain (working 0x50) */ - 0x21, 0x22, - 0x22, 0xf0, - 0x23, 0xfe, - 0x24, 0x3c, - 0x25, 0x38, - 0x26, 0x44, - 0x27, 0x20, - 0x28, 0x00, - 0x29, 0x15, - 0x2a, 0xa0, - 0x2b, 0x44, - 0x2c, 0x37, - 0x2d, 0x00, - 0x2e, 0xa5, /* burst PLL control (working: a9) */ - 0x2f, 0xe0, /* 0xea is blue test frame -- 0xe0 for normal */ - 0x31, 0x00, - 0x33, 0x22, - 0x34, 0x11, - 0x35, 0x35, - 0x3b, 0x05, - 0x06, 0xc0, /* reset device */ - 0x00, 0x00, /* Terminator (reg 0x00 is read-only) */ -}; - -static int write_reg(struct i2c_client *client, u8 reg, u8 value) -{ - return i2c_smbus_write_byte_data(client, reg, value); -} - -static int write_regs(struct i2c_client *client, u8 *regs) -{ - int i; - - for (i = 0; regs[i] != 0x00; i += 2) - if (i2c_smbus_write_byte_data(client, regs[i], regs[i + 1]) < 0) - return -1; - return 0; -} - -static int wis_tw9903_command(struct i2c_client *client, - unsigned int cmd, void *arg) -{ - struct wis_tw9903 *dec = i2c_get_clientdata(client); - - switch (cmd) { - case VIDIOC_S_INPUT: - { - int *input = arg; - - i2c_smbus_write_byte_data(client, 0x02, 0x40 | (*input << 1)); - break; - } -#if 0 - /* The scaler on this thing seems to be horribly broken */ - case DECODER_SET_RESOLUTION: - { - struct video_decoder_resolution *res = arg; - /*int hscale = 256 * 720 / res->width;*/ - int hscale = 256 * 720 / (res->width - (res->width > 704 ? 0 : 8)); - int vscale = 256 * (dec->norm & V4L2_STD_NTSC ? 240 : 288) - / res->height; - u8 regs[] = { - 0x0d, vscale & 0xff, - 0x0f, hscale & 0xff, - 0x0e, ((vscale & 0xf00) >> 4) | ((hscale & 0xf00) >> 8), - 0x06, 0xc0, /* reset device */ - 0, 0, - }; - dev_dbg(&client->dev, "vscale is %04x, hscale is %04x\n", - vscale, hscale); - /*write_regs(client, regs);*/ - break; - } -#endif - case VIDIOC_S_STD: - { - v4l2_std_id *input = arg; - u8 regs[] = { - 0x05, *input & V4L2_STD_NTSC ? 0x80 : 0x00, - 0x07, *input & V4L2_STD_NTSC ? 0x02 : 0x12, - 0x08, *input & V4L2_STD_NTSC ? 0x14 : 0x18, - 0x09, *input & V4L2_STD_NTSC ? 0xf0 : 0x20, - 0, 0, - }; - write_regs(client, regs); - dec->norm = *input; - break; - } - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *ctrl = arg; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - ctrl->type = V4L2_CTRL_TYPE_INTEGER; - strncpy(ctrl->name, "Brightness", sizeof(ctrl->name)); - ctrl->minimum = -128; - ctrl->maximum = 127; - ctrl->step = 1; - ctrl->default_value = 0x00; - ctrl->flags = 0; - break; - case V4L2_CID_CONTRAST: - ctrl->type = V4L2_CTRL_TYPE_INTEGER; - strncpy(ctrl->name, "Contrast", sizeof(ctrl->name)); - ctrl->minimum = 0; - ctrl->maximum = 255; - ctrl->step = 1; - ctrl->default_value = 0x60; - ctrl->flags = 0; - break; -#if 0 - /* I don't understand how the Chroma Gain registers work... */ - case V4L2_CID_SATURATION: - ctrl->type = V4L2_CTRL_TYPE_INTEGER; - strncpy(ctrl->name, "Saturation", sizeof(ctrl->name)); - ctrl->minimum = 0; - ctrl->maximum = 127; - ctrl->step = 1; - ctrl->default_value = 64; - ctrl->flags = 0; - break; -#endif - case V4L2_CID_HUE: - ctrl->type = V4L2_CTRL_TYPE_INTEGER; - strncpy(ctrl->name, "Hue", sizeof(ctrl->name)); - ctrl->minimum = -128; - ctrl->maximum = 127; - ctrl->step = 1; - ctrl->default_value = 0; - ctrl->flags = 0; - break; - } - break; - } - case VIDIOC_S_CTRL: - { - struct v4l2_control *ctrl = arg; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - if (ctrl->value > 127) - dec->brightness = 127; - else if (ctrl->value < -128) - dec->brightness = -128; - else - dec->brightness = ctrl->value; - write_reg(client, 0x10, dec->brightness); - break; - case V4L2_CID_CONTRAST: - if (ctrl->value > 255) - dec->contrast = 255; - else if (ctrl->value < 0) - dec->contrast = 0; - else - dec->contrast = ctrl->value; - write_reg(client, 0x11, dec->contrast); - break; -#if 0 - case V4L2_CID_SATURATION: - if (ctrl->value > 127) - dec->saturation = 127; - else if (ctrl->value < 0) - dec->saturation = 0; - else - dec->saturation = ctrl->value; - /*write_reg(client, 0x0c, dec->saturation);*/ - break; -#endif - case V4L2_CID_HUE: - if (ctrl->value > 127) - dec->hue = 127; - else if (ctrl->value < -128) - dec->hue = -128; - else - dec->hue = ctrl->value; - write_reg(client, 0x15, dec->hue); - break; - } - break; - } - case VIDIOC_G_CTRL: - { - struct v4l2_control *ctrl = arg; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - ctrl->value = dec->brightness; - break; - case V4L2_CID_CONTRAST: - ctrl->value = dec->contrast; - break; -#if 0 - case V4L2_CID_SATURATION: - ctrl->value = dec->saturation; - break; -#endif - case V4L2_CID_HUE: - ctrl->value = dec->hue; - break; - } - break; - } - default: - break; - } - return 0; -} - -static int wis_tw9903_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct i2c_adapter *adapter = client->adapter; - struct wis_tw9903 *dec; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -ENODEV; - - dec = kmalloc(sizeof(struct wis_tw9903), GFP_KERNEL); - if (dec == NULL) - return -ENOMEM; - - dec->norm = V4L2_STD_NTSC; - dec->brightness = 0; - dec->contrast = 0x60; - dec->hue = 0; - i2c_set_clientdata(client, dec); - - dev_dbg(&client->dev, "initializing TW9903 at address %d on %s\n", - client->addr, adapter->name); - - if (write_regs(client, initial_registers) < 0) { - dev_err(&client->dev, "error initializing TW9903\n"); - kfree(dec); - return -ENODEV; - } - - return 0; -} - -static int wis_tw9903_remove(struct i2c_client *client) -{ - struct wis_tw9903 *dec = i2c_get_clientdata(client); - - kfree(dec); - return 0; -} - -static const struct i2c_device_id wis_tw9903_id[] = { - { "wis_tw9903", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, wis_tw9903_id); - -static struct i2c_driver wis_tw9903_driver = { - .driver = { - .name = "WIS TW9903 I2C driver", - }, - .probe = wis_tw9903_probe, - .remove = wis_tw9903_remove, - .command = wis_tw9903_command, - .id_table = wis_tw9903_id, -}; - -module_i2c_driver(wis_tw9903_driver); - -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/go7007/wis-uda1342.c b/drivers/staging/media/go7007/wis-uda1342.c deleted file mode 100644 index 582ea120a531..000000000000 --- a/drivers/staging/media/go7007/wis-uda1342.c +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (C) 2005-2006 Micronas USA Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License (Version 2) as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. - */ - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/i2c.h> -#include <linux/videodev2.h> -#include <media/tvaudio.h> -#include <media/v4l2-common.h> - -#include "wis-i2c.h" - -static int write_reg(struct i2c_client *client, int reg, int value) -{ - /* UDA1342 wants MSB first, but SMBus sends LSB first */ - i2c_smbus_write_word_data(client, reg, swab16(value)); - return 0; -} - -static int wis_uda1342_command(struct i2c_client *client, - unsigned int cmd, void *arg) -{ - switch (cmd) { - case VIDIOC_S_AUDIO: - { - int *inp = arg; - - switch (*inp) { - case TVAUDIO_INPUT_TUNER: - write_reg(client, 0x00, 0x1441); /* select input 2 */ - break; - case TVAUDIO_INPUT_EXTERN: - write_reg(client, 0x00, 0x1241); /* select input 1 */ - break; - default: - dev_err(&client->dev, "input %d not supported\n", - *inp); - break; - } - break; - } - default: - break; - } - return 0; -} - -static int wis_uda1342_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct i2c_adapter *adapter = client->adapter; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) - return -ENODEV; - - dev_dbg(&client->dev, "initializing UDA1342 at address %d on %s\n", - client->addr, adapter->name); - - write_reg(client, 0x00, 0x8000); /* reset registers */ - write_reg(client, 0x00, 0x1241); /* select input 1 */ - - return 0; -} - -static int wis_uda1342_remove(struct i2c_client *client) -{ - return 0; -} - -static const struct i2c_device_id wis_uda1342_id[] = { - { "wis_uda1342", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, wis_uda1342_id); - -static struct i2c_driver wis_uda1342_driver = { - .driver = { - .name = "WIS UDA1342 I2C driver", - }, - .probe = wis_uda1342_probe, - .remove = wis_uda1342_remove, - .command = wis_uda1342_command, - .id_table = wis_uda1342_id, -}; - -module_i2c_driver(wis_uda1342_driver); - -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/lirc/lirc_sir.c b/drivers/staging/media/lirc/lirc_sir.c index 63a554c36f75..f781c532b3c4 100644 --- a/drivers/staging/media/lirc/lirc_sir.c +++ b/drivers/staging/media/lirc/lirc_sir.c @@ -787,12 +787,6 @@ static int init_hardware(void) spin_lock_irqsave(&hardware_lock, flags); /* reset UART */ #ifdef LIRC_ON_SA1100 -#ifdef CONFIG_SA1100_BITSY - if (machine_is_bitsy()) { - pr_info("Power on IR module\n"); - set_bitsy_egpio(EGPIO_BITSY_IR_ON); - } -#endif #ifdef CONFIG_SA1100_COLLIE sa1100_irda_set_power_collie(3); /* power on */ #endif @@ -942,10 +936,6 @@ static void drop_hardware(void) Ser2UTCR3 = sr.utcr3; Ser2HSCR0 = sr.hscr0; -#ifdef CONFIG_SA1100_BITSY - if (machine_is_bitsy()) - clr_bitsy_egpio(EGPIO_BITSY_IR_ON); -#endif #ifdef CONFIG_SA1100_COLLIE sa1100_irda_set_power_collie(0); /* power off */ #endif diff --git a/drivers/staging/media/solo6x10/Kconfig b/drivers/staging/media/solo6x10/Kconfig index 63352de5eabf..ec32776ff547 100644 --- a/drivers/staging/media/solo6x10/Kconfig +++ b/drivers/staging/media/solo6x10/Kconfig @@ -1,7 +1,8 @@ config SOLO6X10 tristate "Softlogic 6x10 MPEG codec cards" depends on PCI && VIDEO_DEV && SND && I2C - select VIDEOBUF_DMA_SG + select VIDEOBUF2_DMA_SG + select VIDEOBUF2_DMA_CONTIG select SND_PCM ---help--- This driver supports the Softlogic based MPEG-4 and h.264 codec diff --git a/drivers/staging/media/solo6x10/Makefile b/drivers/staging/media/solo6x10/Makefile index 337e38c3a0f0..7aae118947b2 100644 --- a/drivers/staging/media/solo6x10/Makefile +++ b/drivers/staging/media/solo6x10/Makefile @@ -1,3 +1,5 @@ -solo6x10-y := core.o i2c.o p2m.o v4l2.o tw28.o gpio.o disp.o enc.o v4l2-enc.o g723.o +solo6x10-y := solo6x10-core.o solo6x10-i2c.o solo6x10-p2m.o solo6x10-v4l2.o \ + solo6x10-tw28.o solo6x10-gpio.o solo6x10-disp.o solo6x10-enc.o \ + solo6x10-v4l2-enc.o solo6x10-g723.o solo6x10-eeprom.o obj-$(CONFIG_SOLO6X10) += solo6x10.o diff --git a/drivers/staging/media/solo6x10/TODO b/drivers/staging/media/solo6x10/TODO index 539f739fe9e6..7b8db75b1acb 100644 --- a/drivers/staging/media/solo6x10/TODO +++ b/drivers/staging/media/solo6x10/TODO @@ -1,24 +1,15 @@ -TODO (staging => main): - - * Motion detection flags need to be moved to v4l2 - * Some private CIDs need to be moved to v4l2 - -TODO (general): - - * encoder on/off controls - * mpeg cid bitrate mode (vbr/cbr) - * mpeg cid bitrate/bitrate-peak - * mpeg encode of user data - * mpeg decode of user data - * switch between 4 frames/irq to 1 when using mjpeg (and then back - when not) - * implement a CID control for motion areas/thresholds - * implement CID controls for mozaic areas - * allow for higher level of interval (for < 1 fps) - * sound: - - implement playback via external sound jack - - implement loopback of external sound jack with incoming audio? - - implement pause/resume - -Plase send patches to Mauro Carvalho Chehab <mchehab@redhat.com> and Cc Ben Collins -<bcollins@bluecherry.net> +- batch up desc requests for more efficient use of p2m? +- encoder on/off controls +- mpeg cid bitrate mode (vbr/cbr) +- mpeg cid bitrate/bitrate-peak +- mpeg encode of user data +- mpeg decode of user data +- implement CID controls for mozaic areas + +- sound + - implement playback via external sound jack + - implement loopback of external sound jack with incoming audio? + - implement pause/resume (make use of in bc-server) + +Please send patches to the linux media list <linux-media@vger.kernel.org> and +Cc Ismael Luceno <ismael.luceno@corp.bluecherry.net>. diff --git a/drivers/staging/media/solo6x10/core.c b/drivers/staging/media/solo6x10/core.c deleted file mode 100644 index fd83d6d028bf..000000000000 --- a/drivers/staging/media/solo6x10/core.c +++ /dev/null @@ -1,321 +0,0 @@ -/* - * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com - * Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/pci.h> -#include <linux/interrupt.h> -#include <linux/slab.h> -#include <linux/videodev2.h> -#include "solo6x10.h" -#include "tw28.h" - -MODULE_DESCRIPTION("Softlogic 6x10 MP4/H.264 Encoder/Decoder V4L2/ALSA Driver"); -MODULE_AUTHOR("Ben Collins <bcollins@bluecherry.net>"); -MODULE_VERSION(SOLO6X10_VERSION); -MODULE_LICENSE("GPL"); - -void solo_irq_on(struct solo_dev *solo_dev, u32 mask) -{ - solo_dev->irq_mask |= mask; - solo_reg_write(solo_dev, SOLO_IRQ_ENABLE, solo_dev->irq_mask); -} - -void solo_irq_off(struct solo_dev *solo_dev, u32 mask) -{ - solo_dev->irq_mask &= ~mask; - solo_reg_write(solo_dev, SOLO_IRQ_ENABLE, solo_dev->irq_mask); -} - -/* XXX We should check the return value of the sub-device ISR's */ -static irqreturn_t solo_isr(int irq, void *data) -{ - struct solo_dev *solo_dev = data; - u32 status; - int i; - - status = solo_reg_read(solo_dev, SOLO_IRQ_STAT); - if (!status) - return IRQ_NONE; - - if (status & ~solo_dev->irq_mask) { - solo_reg_write(solo_dev, SOLO_IRQ_STAT, - status & ~solo_dev->irq_mask); - status &= solo_dev->irq_mask; - } - - if (status & SOLO_IRQ_PCI_ERR) { - u32 err = solo_reg_read(solo_dev, SOLO_PCI_ERR); - solo_p2m_error_isr(solo_dev, err); - solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_PCI_ERR); - } - - for (i = 0; i < SOLO_NR_P2M; i++) - if (status & SOLO_IRQ_P2M(i)) - solo_p2m_isr(solo_dev, i); - - if (status & SOLO_IRQ_IIC) - solo_i2c_isr(solo_dev); - - if (status & SOLO_IRQ_VIDEO_IN) - solo_video_in_isr(solo_dev); - - /* Call this first so enc gets detected flag set */ - if (status & SOLO_IRQ_MOTION) - solo_motion_isr(solo_dev); - - if (status & SOLO_IRQ_ENCODER) - solo_enc_v4l2_isr(solo_dev); - - if (status & SOLO_IRQ_G723) - solo_g723_isr(solo_dev); - - return IRQ_HANDLED; -} - -static void free_solo_dev(struct solo_dev *solo_dev) -{ - struct pci_dev *pdev; - - if (!solo_dev) - return; - - pdev = solo_dev->pdev; - - /* If we never initialized the PCI device, then nothing else - * below here needs cleanup */ - if (!pdev) { - kfree(solo_dev); - return; - } - - /* Bring down the sub-devices first */ - solo_g723_exit(solo_dev); - solo_enc_v4l2_exit(solo_dev); - solo_enc_exit(solo_dev); - solo_v4l2_exit(solo_dev); - solo_disp_exit(solo_dev); - solo_gpio_exit(solo_dev); - solo_p2m_exit(solo_dev); - solo_i2c_exit(solo_dev); - - /* Now cleanup the PCI device */ - if (solo_dev->reg_base) { - solo_irq_off(solo_dev, ~0); - pci_iounmap(pdev, solo_dev->reg_base); - free_irq(pdev->irq, solo_dev); - } - - pci_release_regions(pdev); - pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); - - kfree(solo_dev); -} - -static int solo_pci_probe(struct pci_dev *pdev, - const struct pci_device_id *id) -{ - struct solo_dev *solo_dev; - int ret; - int sdram; - u8 chip_id; - u32 reg; - - solo_dev = kzalloc(sizeof(*solo_dev), GFP_KERNEL); - if (solo_dev == NULL) - return -ENOMEM; - - solo_dev->pdev = pdev; - spin_lock_init(&solo_dev->reg_io_lock); - pci_set_drvdata(pdev, solo_dev); - - ret = pci_enable_device(pdev); - if (ret) - goto fail_probe; - - pci_set_master(pdev); - - ret = pci_request_regions(pdev, SOLO6X10_NAME); - if (ret) - goto fail_probe; - - solo_dev->reg_base = pci_ioremap_bar(pdev, 0); - if (solo_dev->reg_base == NULL) { - ret = -ENOMEM; - goto fail_probe; - } - - chip_id = solo_reg_read(solo_dev, SOLO_CHIP_OPTION) & - SOLO_CHIP_ID_MASK; - switch (chip_id) { - case 7: - solo_dev->nr_chans = 16; - solo_dev->nr_ext = 5; - break; - case 6: - solo_dev->nr_chans = 8; - solo_dev->nr_ext = 2; - break; - default: - dev_warn(&pdev->dev, "Invalid chip_id 0x%02x, " - "defaulting to 4 channels\n", - chip_id); - case 5: - solo_dev->nr_chans = 4; - solo_dev->nr_ext = 1; - } - - solo_dev->flags = id->driver_data; - - /* Disable all interrupts to start */ - solo_irq_off(solo_dev, ~0); - - reg = SOLO_SYS_CFG_SDRAM64BIT; - /* Initial global settings */ - if (!(solo_dev->flags & FLAGS_6110)) - reg |= SOLO6010_SYS_CFG_INPUTDIV(25) | - SOLO6010_SYS_CFG_FEEDBACKDIV((SOLO_CLOCK_MHZ * 2) - 2) | - SOLO6010_SYS_CFG_OUTDIV(3); - solo_reg_write(solo_dev, SOLO_SYS_CFG, reg); - - if (solo_dev->flags & FLAGS_6110) { - u32 sys_clock_MHz = SOLO_CLOCK_MHZ; - u32 pll_DIVQ; - u32 pll_DIVF; - - if (sys_clock_MHz < 125) { - pll_DIVQ = 3; - pll_DIVF = (sys_clock_MHz * 4) / 3; - } else { - pll_DIVQ = 2; - pll_DIVF = (sys_clock_MHz * 2) / 3; - } - - solo_reg_write(solo_dev, SOLO6110_PLL_CONFIG, - SOLO6110_PLL_RANGE_5_10MHZ | - SOLO6110_PLL_DIVR(9) | - SOLO6110_PLL_DIVQ_EXP(pll_DIVQ) | - SOLO6110_PLL_DIVF(pll_DIVF) | SOLO6110_PLL_FSEN); - mdelay(1); /* PLL Locking time (1ms) */ - - solo_reg_write(solo_dev, SOLO_DMA_CTRL1, 3 << 8); /* ? */ - } else - solo_reg_write(solo_dev, SOLO_DMA_CTRL1, 1 << 8); /* ? */ - - solo_reg_write(solo_dev, SOLO_TIMER_CLOCK_NUM, SOLO_CLOCK_MHZ - 1); - - /* PLL locking time of 1ms */ - mdelay(1); - - ret = request_irq(pdev->irq, solo_isr, IRQF_SHARED, SOLO6X10_NAME, - solo_dev); - if (ret) - goto fail_probe; - - /* Handle this from the start */ - solo_irq_on(solo_dev, SOLO_IRQ_PCI_ERR); - - ret = solo_i2c_init(solo_dev); - if (ret) - goto fail_probe; - - /* Setup the DMA engine */ - sdram = (solo_dev->nr_chans >= 8) ? 2 : 1; - solo_reg_write(solo_dev, SOLO_DMA_CTRL, - SOLO_DMA_CTRL_REFRESH_CYCLE(1) | - SOLO_DMA_CTRL_SDRAM_SIZE(sdram) | - SOLO_DMA_CTRL_SDRAM_CLK_INVERT | - SOLO_DMA_CTRL_READ_CLK_SELECT | - SOLO_DMA_CTRL_LATENCY(1)); - - ret = solo_p2m_init(solo_dev); - if (ret) - goto fail_probe; - - ret = solo_disp_init(solo_dev); - if (ret) - goto fail_probe; - - ret = solo_gpio_init(solo_dev); - if (ret) - goto fail_probe; - - ret = solo_tw28_init(solo_dev); - if (ret) - goto fail_probe; - - ret = solo_v4l2_init(solo_dev); - if (ret) - goto fail_probe; - - ret = solo_enc_init(solo_dev); - if (ret) - goto fail_probe; - - ret = solo_enc_v4l2_init(solo_dev); - if (ret) - goto fail_probe; - - ret = solo_g723_init(solo_dev); - if (ret) - goto fail_probe; - - return 0; - -fail_probe: - free_solo_dev(solo_dev); - return ret; -} - -static void solo_pci_remove(struct pci_dev *pdev) -{ - struct solo_dev *solo_dev = pci_get_drvdata(pdev); - - free_solo_dev(solo_dev); -} - -static struct pci_device_id solo_id_table[] = { - /* 6010 based cards */ - {PCI_DEVICE(PCI_VENDOR_ID_SOFTLOGIC, PCI_DEVICE_ID_SOLO6010)}, - {PCI_DEVICE(PCI_VENDOR_ID_SOFTLOGIC, PCI_DEVICE_ID_SOLO6110), - .driver_data = FLAGS_6110}, - {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_4)}, - {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_9)}, - {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_16)}, - {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_SOLO_4)}, - {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_SOLO_9)}, - {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_SOLO_16)}, - /* 6110 based cards */ - {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_6110_4)}, - {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_6110_8)}, - {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_6110_16)}, - {0,} -}; - -MODULE_DEVICE_TABLE(pci, solo_id_table); - -static struct pci_driver solo_pci_driver = { - .name = SOLO6X10_NAME, - .id_table = solo_id_table, - .probe = solo_pci_probe, - .remove = solo_pci_remove, -}; - -module_pci_driver(solo_pci_driver); diff --git a/drivers/staging/media/solo6x10/offsets.h b/drivers/staging/media/solo6x10/offsets.h deleted file mode 100644 index 3d7e569f1cf8..000000000000 --- a/drivers/staging/media/solo6x10/offsets.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com - * Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#ifndef __SOLO6X10_OFFSETS_H -#define __SOLO6X10_OFFSETS_H - -/* Offsets and sizes of the external address */ -#define SOLO_DISP_EXT_ADDR 0x00000000 -#define SOLO_DISP_EXT_SIZE 0x00480000 - -#define SOLO_DEC2LIVE_EXT_ADDR (SOLO_DISP_EXT_ADDR + SOLO_DISP_EXT_SIZE) -#define SOLO_DEC2LIVE_EXT_SIZE 0x00240000 - -#define SOLO_OSG_EXT_ADDR (SOLO_DEC2LIVE_EXT_ADDR + SOLO_DEC2LIVE_EXT_SIZE) -#define SOLO_OSG_EXT_SIZE 0x00120000 - -#define SOLO_EOSD_EXT_ADDR (SOLO_OSG_EXT_ADDR + SOLO_OSG_EXT_SIZE) -#define SOLO_EOSD_EXT_SIZE 0x00010000 - -#define SOLO_MOTION_EXT_ADDR(__solo) (SOLO_EOSD_EXT_ADDR + \ - (SOLO_EOSD_EXT_SIZE * __solo->nr_chans)) -#define SOLO_MOTION_EXT_SIZE 0x00080000 - -#define SOLO_G723_EXT_ADDR(__solo) \ - (SOLO_MOTION_EXT_ADDR(__solo) + SOLO_MOTION_EXT_SIZE) -#define SOLO_G723_EXT_SIZE 0x00010000 - -#define SOLO_CAP_EXT_ADDR(__solo) \ - (SOLO_G723_EXT_ADDR(__solo) + SOLO_G723_EXT_SIZE) -#define SOLO_CAP_EXT_MAX_PAGE (18 + 15) -#define SOLO_CAP_EXT_SIZE (SOLO_CAP_EXT_MAX_PAGE * 65536) - -/* This +1 is very important -- Why?! -- BenC */ -#define SOLO_EREF_EXT_ADDR(__solo) \ - (SOLO_CAP_EXT_ADDR(__solo) + \ - (SOLO_CAP_EXT_SIZE * (__solo->nr_chans + 1))) -#define SOLO_EREF_EXT_SIZE 0x00140000 - -#define SOLO_MP4E_EXT_ADDR(__solo) \ - (SOLO_EREF_EXT_ADDR(__solo) + \ - (SOLO_EREF_EXT_SIZE * __solo->nr_chans)) -#define SOLO_MP4E_EXT_SIZE(__solo) (0x00080000 * __solo->nr_chans) - -#define SOLO_DREF_EXT_ADDR(__solo) \ - (SOLO_MP4E_EXT_ADDR(__solo) + SOLO_MP4E_EXT_SIZE(__solo)) -#define SOLO_DREF_EXT_SIZE 0x00140000 - -#define SOLO_MP4D_EXT_ADDR(__solo) \ - (SOLO_DREF_EXT_ADDR(__solo) + \ - (SOLO_DREF_EXT_SIZE * __solo->nr_chans)) -#define SOLO_MP4D_EXT_SIZE 0x00080000 - -#define SOLO_JPEG_EXT_ADDR(__solo) \ - (SOLO_MP4D_EXT_ADDR(__solo) + \ - (SOLO_MP4D_EXT_SIZE * __solo->nr_chans)) -#define SOLO_JPEG_EXT_SIZE(__solo) (0x00080000 * __solo->nr_chans) - -#endif /* __SOLO6X10_OFFSETS_H */ diff --git a/drivers/staging/media/solo6x10/osd-font.h b/drivers/staging/media/solo6x10/osd-font.h deleted file mode 100644 index 591e0e82e0e8..000000000000 --- a/drivers/staging/media/solo6x10/osd-font.h +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com - * Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#ifndef __SOLO6X10_OSD_FONT_H -#define __SOLO6X10_OSD_FONT_H - -static const unsigned int solo_osd_font[] = { - 0x00000000, 0x0000c0c8, 0xccfefe0c, 0x08000000, - 0x00000000, 0x10103838, 0x7c7cfefe, 0x00000000, /* 0 */ - 0x00000000, 0xfefe7c7c, 0x38381010, 0x10000000, - 0x00000000, 0x7c82fefe, 0xfefefe7c, 0x00000000, - 0x00000000, 0x00001038, 0x10000000, 0x00000000, - 0x00000000, 0x0010387c, 0xfe7c3810, 0x00000000, - 0x00000000, 0x00384444, 0x44380000, 0x00000000, - 0x00000000, 0x38448282, 0x82443800, 0x00000000, - 0x00000000, 0x007c7c7c, 0x7c7c0000, 0x00000000, - 0x00000000, 0x6c6c6c6c, 0x6c6c6c6c, 0x00000000, - 0x00000000, 0x061e7efe, 0xfe7e1e06, 0x00000000, - 0x00000000, 0xc0f0fcfe, 0xfefcf0c0, 0x00000000, - 0x00000000, 0xc6cedefe, 0xfedecec6, 0x00000000, - 0x00000000, 0xc6e6f6fe, 0xfef6e6c6, 0x00000000, - 0x00000000, 0x12367efe, 0xfe7e3612, 0x00000000, - 0x00000000, 0x90d8fcfe, 0xfefcd890, 0x00000000, - 0x00000038, 0x7cc692ba, 0x92c67c38, 0x00000000, - 0x00000038, 0x7cc6aa92, 0xaac67c38, 0x00000000, - 0x00000038, 0x7830107c, 0xbaa8680c, 0x00000000, - 0x00000038, 0x3c18127c, 0xb8382c60, 0x00000000, - 0x00000044, 0xaa6c8254, 0x38eec67c, 0x00000000, - 0x00000082, 0x44288244, 0x38c6827c, 0x00000000, - 0x00000038, 0x444444fe, 0xfeeec6fe, 0x00000000, - 0x00000018, 0x78187818, 0x3c7e7e3c, 0x00000000, - 0x00000000, 0x3854929a, 0x82443800, 0x00000000, - 0x00000000, 0x00c0c8cc, 0xfefe0c08, 0x00000000, - 0x0000e0a0, 0xe040e00e, 0x8a0ea40e, 0x00000000, - 0x0000e0a0, 0xe040e00e, 0x0a8e440e, 0x00000000, - 0x0000007c, 0x82829292, 0x929282fe, 0x00000000, - 0x000000f8, 0xfc046494, 0x946404fc, 0x00000000, - 0x0000003f, 0x7f404c52, 0x524c407f, 0x00000000, - 0x0000007c, 0x82ba82ba, 0x82ba82fe, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x183c3c3c, 0x18180018, 0x18000000, /* 32 ! */ - 0x00000066, 0x66240000, 0x00000000, 0x00000000, - 0x00000000, 0x6c6cfe6c, 0x6c6cfe6c, 0x6c000000, /* 34 " # */ - 0x00001010, 0x7cd6d616, 0x7cd0d6d6, 0x7c101000, - 0x00000000, 0x0086c660, 0x30180cc6, 0xc2000000, /* 36 $ % */ - 0x00000000, 0x386c6c38, 0xdc766666, 0xdc000000, - 0x0000000c, 0x0c0c0600, 0x00000000, 0x00000000, /* 38 & ' */ - 0x00000000, 0x30180c0c, 0x0c0c0c18, 0x30000000, - 0x00000000, 0x0c183030, 0x30303018, 0x0c000000, /* 40 ( ) */ - 0x00000000, 0x0000663c, 0xff3c6600, 0x00000000, - 0x00000000, 0x00001818, 0x7e181800, 0x00000000, /* 42 * + */ - 0x00000000, 0x00000000, 0x00000e0e, 0x0c060000, - 0x00000000, 0x00000000, 0x7e000000, 0x00000000, /* 44 , - */ - 0x00000000, 0x00000000, 0x00000006, 0x06000000, - 0x00000000, 0x80c06030, 0x180c0602, 0x00000000, /* 46 . / */ - 0x0000007c, 0xc6e6f6de, 0xcec6c67c, 0x00000000, - 0x00000030, 0x383c3030, 0x303030fc, 0x00000000, /* 48 0 1 */ - 0x0000007c, 0xc6c06030, 0x180cc6fe, 0x00000000, - 0x0000007c, 0xc6c0c07c, 0xc0c0c67c, 0x00000000, /* 50 2 3 */ - 0x00000060, 0x70786c66, 0xfe6060f0, 0x00000000, - 0x000000fe, 0x0606067e, 0xc0c0c67c, 0x00000000, /* 52 4 5 */ - 0x00000038, 0x0c06067e, 0xc6c6c67c, 0x00000000, - 0x000000fe, 0xc6c06030, 0x18181818, 0x00000000, /* 54 6 7 */ - 0x0000007c, 0xc6c6c67c, 0xc6c6c67c, 0x00000000, - 0x0000007c, 0xc6c6c6fc, 0xc0c06038, 0x00000000, /* 56 8 9 */ - 0x00000000, 0x18180000, 0x00181800, 0x00000000, - 0x00000000, 0x18180000, 0x0018180c, 0x00000000, /* 58 : ; */ - 0x00000060, 0x30180c06, 0x0c183060, 0x00000000, - 0x00000000, 0x007e0000, 0x007e0000, 0x00000000, - 0x00000006, 0x0c183060, 0x30180c06, 0x00000000, - 0x0000007c, 0xc6c66030, 0x30003030, 0x00000000, - 0x0000007c, 0xc6f6d6d6, 0x7606067c, 0x00000000, - 0x00000010, 0x386cc6c6, 0xfec6c6c6, 0x00000000, /* 64 @ A */ - 0x0000007e, 0xc6c6c67e, 0xc6c6c67e, 0x00000000, - 0x00000078, 0xcc060606, 0x0606cc78, 0x00000000, /* 66 */ - 0x0000003e, 0x66c6c6c6, 0xc6c6663e, 0x00000000, - 0x000000fe, 0x0606063e, 0x060606fe, 0x00000000, /* 68 */ - 0x000000fe, 0x0606063e, 0x06060606, 0x00000000, - 0x00000078, 0xcc060606, 0xf6c6ccb8, 0x00000000, /* 70 */ - 0x000000c6, 0xc6c6c6fe, 0xc6c6c6c6, 0x00000000, - 0x0000003c, 0x18181818, 0x1818183c, 0x00000000, /* 72 */ - 0x00000060, 0x60606060, 0x6066663c, 0x00000000, - 0x000000c6, 0xc666361e, 0x3666c6c6, 0x00000000, /* 74 */ - 0x00000006, 0x06060606, 0x060606fe, 0x00000000, - 0x000000c6, 0xeefed6c6, 0xc6c6c6c6, 0x00000000, /* 76 */ - 0x000000c6, 0xcedefef6, 0xe6c6c6c6, 0x00000000, - 0x00000038, 0x6cc6c6c6, 0xc6c66c38, 0x00000000, /* 78 */ - 0x0000007e, 0xc6c6c67e, 0x06060606, 0x00000000, - 0x00000038, 0x6cc6c6c6, 0xc6d67c38, 0x60000000, /* 80 */ - 0x0000007e, 0xc6c6c67e, 0x66c6c6c6, 0x00000000, - 0x0000007c, 0xc6c60c38, 0x60c6c67c, 0x00000000, /* 82 */ - 0x0000007e, 0x18181818, 0x18181818, 0x00000000, - 0x000000c6, 0xc6c6c6c6, 0xc6c6c67c, 0x00000000, /* 84 */ - 0x000000c6, 0xc6c6c6c6, 0xc66c3810, 0x00000000, - 0x000000c6, 0xc6c6c6c6, 0xd6d6fe6c, 0x00000000, /* 86 */ - 0x000000c6, 0xc6c66c38, 0x6cc6c6c6, 0x00000000, - 0x00000066, 0x66666666, 0x3c181818, 0x00000000, /* 88 */ - 0x000000fe, 0xc0603018, 0x0c0606fe, 0x00000000, - 0x0000003c, 0x0c0c0c0c, 0x0c0c0c3c, 0x00000000, /* 90 */ - 0x00000002, 0x060c1830, 0x60c08000, 0x00000000, - 0x0000003c, 0x30303030, 0x3030303c, 0x00000000, /* 92 */ - 0x00001038, 0x6cc60000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00fe0000, - 0x00001818, 0x30000000, 0x00000000, 0x00000000, - 0x00000000, 0x00003c60, 0x7c66667c, 0x00000000, - 0x0000000c, 0x0c0c7ccc, 0xcccccc7c, 0x00000000, - 0x00000000, 0x00007cc6, 0x0606c67c, 0x00000000, - 0x00000060, 0x60607c66, 0x6666667c, 0x00000000, - 0x00000000, 0x00007cc6, 0xfe06c67c, 0x00000000, - 0x00000078, 0x0c0c0c3e, 0x0c0c0c0c, 0x00000000, - 0x00000000, 0x00007c66, 0x6666667c, 0x60603e00, - 0x0000000c, 0x0c0c7ccc, 0xcccccccc, 0x00000000, - 0x00000030, 0x30003830, 0x30303078, 0x00000000, - 0x00000030, 0x30003c30, 0x30303030, 0x30301f00, - 0x0000000c, 0x0c0ccc6c, 0x3c6ccccc, 0x00000000, - 0x00000030, 0x30303030, 0x30303030, 0x00000000, - 0x00000000, 0x000066fe, 0xd6d6d6d6, 0x00000000, - 0x00000000, 0x000078cc, 0xcccccccc, 0x00000000, - 0x00000000, 0x00007cc6, 0xc6c6c67c, 0x00000000, - 0x00000000, 0x00007ccc, 0xcccccc7c, 0x0c0c0c00, - 0x00000000, 0x00007c66, 0x6666667c, 0x60606000, - 0x00000000, 0x000076dc, 0x0c0c0c0c, 0x00000000, - 0x00000000, 0x00007cc6, 0x1c70c67c, 0x00000000, - 0x00000000, 0x1818fe18, 0x18181870, 0x00000000, - 0x00000000, 0x00006666, 0x6666663c, 0x00000000, - 0x00000000, 0x0000c6c6, 0xc66c3810, 0x00000000, - 0x00000000, 0x0000c6d6, 0xd6d6fe6c, 0x00000000, - 0x00000000, 0x0000c66c, 0x38386cc6, 0x00000000, - 0x00000000, 0x00006666, 0x6666667c, 0x60603e00, - 0x00000000, 0x0000fe60, 0x30180cfe, 0x00000000, - 0x00000070, 0x1818180e, 0x18181870, 0x00000000, - 0x00000018, 0x18181800, 0x18181818, 0x00000000, - 0x0000000e, 0x18181870, 0x1818180e, 0x00000000, - 0x000000dc, 0x76000000, 0x00000000, 0x00000000, - 0x00000000, 0x0010386c, 0xc6c6fe00, 0x00000000 -}; - -#endif /* __SOLO6X10_OSD_FONT_H */ diff --git a/drivers/staging/media/solo6x10/p2m.c b/drivers/staging/media/solo6x10/p2m.c deleted file mode 100644 index 58ab61b1f1d9..000000000000 --- a/drivers/staging/media/solo6x10/p2m.c +++ /dev/null @@ -1,306 +0,0 @@ -/* - * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com - * Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/scatterlist.h> -#include "solo6x10.h" - -/* #define SOLO_TEST_P2M */ - -int solo_p2m_dma(struct solo_dev *solo_dev, u8 id, int wr, - void *sys_addr, u32 ext_addr, u32 size) -{ - dma_addr_t dma_addr; - int ret; - - WARN_ON(!size); - BUG_ON(id >= SOLO_NR_P2M); - - if (!size) - return -EINVAL; - - dma_addr = pci_map_single(solo_dev->pdev, sys_addr, size, - wr ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); - - ret = solo_p2m_dma_t(solo_dev, id, wr, dma_addr, ext_addr, size); - - pci_unmap_single(solo_dev->pdev, dma_addr, size, - wr ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); - - return ret; -} - -int solo_p2m_dma_t(struct solo_dev *solo_dev, u8 id, int wr, - dma_addr_t dma_addr, u32 ext_addr, u32 size) -{ - struct p2m_desc *desc = kzalloc(sizeof(*desc) * 2, GFP_DMA); - int ret; - - if (desc == NULL) - return -ENOMEM; - - solo_p2m_push_desc(&desc[1], wr, dma_addr, ext_addr, size, 0, 0); - ret = solo_p2m_dma_desc(solo_dev, id, desc, 2); - kfree(desc); - - return ret; -} - -void solo_p2m_push_desc(struct p2m_desc *desc, int wr, dma_addr_t dma_addr, - u32 ext_addr, u32 size, int repeat, u32 ext_size) -{ - desc->ta = cpu_to_le32(dma_addr); - desc->fa = cpu_to_le32(ext_addr); - - desc->ext = cpu_to_le32(SOLO_P2M_COPY_SIZE(size >> 2)); - desc->ctrl = cpu_to_le32(SOLO_P2M_BURST_SIZE(SOLO_P2M_BURST_256) | - (wr ? SOLO_P2M_WRITE : 0) | SOLO_P2M_TRANS_ON); - - /* Ext size only matters when we're repeating */ - if (repeat) { - desc->ext |= cpu_to_le32(SOLO_P2M_EXT_INC(ext_size >> 2)); - desc->ctrl |= cpu_to_le32(SOLO_P2M_PCI_INC(size >> 2) | - SOLO_P2M_REPEAT(repeat)); - } -} - -int solo_p2m_dma_desc(struct solo_dev *solo_dev, u8 id, - struct p2m_desc *desc, int desc_count) -{ - struct solo_p2m_dev *p2m_dev; - unsigned int timeout; - int ret = 0; - u32 config = 0; - dma_addr_t desc_dma = 0; - - BUG_ON(id >= SOLO_NR_P2M); - BUG_ON(!desc_count || desc_count > SOLO_NR_P2M_DESC); - - p2m_dev = &solo_dev->p2m_dev[id]; - - mutex_lock(&p2m_dev->mutex); - - solo_reg_write(solo_dev, SOLO_P2M_CONTROL(id), 0); - - INIT_COMPLETION(p2m_dev->completion); - p2m_dev->error = 0; - - /* Enable the descriptors */ - config = solo_reg_read(solo_dev, SOLO_P2M_CONFIG(id)); - desc_dma = pci_map_single(solo_dev->pdev, desc, - desc_count * sizeof(*desc), - PCI_DMA_TODEVICE); - solo_reg_write(solo_dev, SOLO_P2M_DES_ADR(id), desc_dma); - solo_reg_write(solo_dev, SOLO_P2M_DESC_ID(id), desc_count - 1); - solo_reg_write(solo_dev, SOLO_P2M_CONFIG(id), config | - SOLO_P2M_DESC_MODE); - - /* Should have all descriptors completed from one interrupt */ - timeout = wait_for_completion_timeout(&p2m_dev->completion, HZ); - - solo_reg_write(solo_dev, SOLO_P2M_CONTROL(id), 0); - - /* Reset back to non-descriptor mode */ - solo_reg_write(solo_dev, SOLO_P2M_CONFIG(id), config); - solo_reg_write(solo_dev, SOLO_P2M_DESC_ID(id), 0); - solo_reg_write(solo_dev, SOLO_P2M_DES_ADR(id), 0); - pci_unmap_single(solo_dev->pdev, desc_dma, - desc_count * sizeof(*desc), - PCI_DMA_TODEVICE); - - if (p2m_dev->error) - ret = -EIO; - else if (timeout == 0) - ret = -EAGAIN; - - mutex_unlock(&p2m_dev->mutex); - - WARN_ON_ONCE(ret); - - return ret; -} - -int solo_p2m_dma_sg(struct solo_dev *solo_dev, u8 id, - struct p2m_desc *pdesc, int wr, - struct scatterlist *sg, u32 sg_off, - u32 ext_addr, u32 size) -{ - int i; - int idx; - - BUG_ON(id >= SOLO_NR_P2M); - - if (WARN_ON_ONCE(!size)) - return -EINVAL; - - memset(pdesc, 0, sizeof(*pdesc)); - - /* Should rewrite this to handle > SOLO_NR_P2M_DESC transactions */ - for (i = 0, idx = 1; idx < SOLO_NR_P2M_DESC && sg && size > 0; - i++, sg = sg_next(sg)) { - struct p2m_desc *desc = &pdesc[idx]; - u32 sg_len = sg_dma_len(sg); - u32 len; - - if (sg_off >= sg_len) { - sg_off -= sg_len; - continue; - } - - sg_len -= sg_off; - len = min(sg_len, size); - - solo_p2m_push_desc(desc, wr, sg_dma_address(sg) + sg_off, - ext_addr, len, 0, 0); - - size -= len; - ext_addr += len; - idx++; - - sg_off = 0; - } - - WARN_ON_ONCE(size || i >= SOLO_NR_P2M_DESC); - - return solo_p2m_dma_desc(solo_dev, id, pdesc, idx); -} - -#ifdef SOLO_TEST_P2M - -#define P2M_TEST_CHAR 0xbe - -static unsigned long long p2m_test(struct solo_dev *solo_dev, u8 id, - u32 base, int size) -{ - u8 *wr_buf; - u8 *rd_buf; - int i; - unsigned long long err_cnt = 0; - - wr_buf = kmalloc(size, GFP_KERNEL); - if (!wr_buf) { - printk(SOLO6X10_NAME ": Failed to malloc for p2m_test\n"); - return size; - } - - rd_buf = kmalloc(size, GFP_KERNEL); - if (!rd_buf) { - printk(SOLO6X10_NAME ": Failed to malloc for p2m_test\n"); - kfree(wr_buf); - return size; - } - - memset(wr_buf, P2M_TEST_CHAR, size); - memset(rd_buf, P2M_TEST_CHAR + 1, size); - - solo_p2m_dma(solo_dev, id, 1, wr_buf, base, size); - solo_p2m_dma(solo_dev, id, 0, rd_buf, base, size); - - for (i = 0; i < size; i++) - if (wr_buf[i] != rd_buf[i]) - err_cnt++; - - kfree(wr_buf); - kfree(rd_buf); - - return err_cnt; -} - -#define TEST_CHUNK_SIZE (8 * 1024) - -static void run_p2m_test(struct solo_dev *solo_dev) -{ - unsigned long long errs = 0; - u32 size = SOLO_JPEG_EXT_ADDR(solo_dev) + SOLO_JPEG_EXT_SIZE(solo_dev); - int i, d; - - dev_warn(&solo_dev->pdev->dev, "Testing %u bytes of external ram\n", - size); - - for (i = 0; i < size; i += TEST_CHUNK_SIZE) - for (d = 0; d < 4; d++) - errs += p2m_test(solo_dev, d, i, TEST_CHUNK_SIZE); - - dev_warn(&solo_dev->pdev->dev, "Found %llu errors during p2m test\n", - errs); - - return; -} -#else -#define run_p2m_test(__solo) do {} while (0) -#endif - -void solo_p2m_isr(struct solo_dev *solo_dev, int id) -{ - struct solo_p2m_dev *p2m_dev = &solo_dev->p2m_dev[id]; - - solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_P2M(id)); - - complete(&p2m_dev->completion); -} - -void solo_p2m_error_isr(struct solo_dev *solo_dev, u32 status) -{ - struct solo_p2m_dev *p2m_dev; - int i; - - if (!(status & SOLO_PCI_ERR_P2M)) - return; - - for (i = 0; i < SOLO_NR_P2M; i++) { - p2m_dev = &solo_dev->p2m_dev[i]; - p2m_dev->error = 1; - solo_reg_write(solo_dev, SOLO_P2M_CONTROL(i), 0); - complete(&p2m_dev->completion); - } -} - -void solo_p2m_exit(struct solo_dev *solo_dev) -{ - int i; - - for (i = 0; i < SOLO_NR_P2M; i++) - solo_irq_off(solo_dev, SOLO_IRQ_P2M(i)); -} - -int solo_p2m_init(struct solo_dev *solo_dev) -{ - struct solo_p2m_dev *p2m_dev; - int i; - - for (i = 0; i < SOLO_NR_P2M; i++) { - p2m_dev = &solo_dev->p2m_dev[i]; - - mutex_init(&p2m_dev->mutex); - init_completion(&p2m_dev->completion); - - solo_reg_write(solo_dev, SOLO_P2M_CONTROL(i), 0); - solo_reg_write(solo_dev, SOLO_P2M_CONFIG(i), - SOLO_P2M_CSC_16BIT_565 | - SOLO_P2M_DMA_INTERVAL(3) | - SOLO_P2M_DESC_INTR_OPT | - SOLO_P2M_PCI_MASTER_MODE); - solo_irq_on(solo_dev, SOLO_IRQ_P2M(i)); - } - - run_p2m_test(solo_dev); - - return 0; -} diff --git a/drivers/staging/media/solo6x10/solo6x10-core.c b/drivers/staging/media/solo6x10/solo6x10-core.c new file mode 100644 index 000000000000..36750205d23f --- /dev/null +++ b/drivers/staging/media/solo6x10/solo6x10-core.c @@ -0,0 +1,709 @@ +/* + * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com> + * + * Original author: + * Ben Collins <bcollins@ubuntu.com> + * + * Additional work by: + * John Brooks <john.brooks@bluecherry.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/interrupt.h> +#include <linux/videodev2.h> +#include <linux/delay.h> +#include <linux/sysfs.h> +#include <linux/ktime.h> +#include <linux/slab.h> + +#include "solo6x10.h" +#include "solo6x10-tw28.h" + +MODULE_DESCRIPTION("Softlogic 6x10 MPEG4/H.264/G.723 CODEC V4L2/ALSA Driver"); +MODULE_AUTHOR("Bluecherry <maintainers@bluecherrydvr.com>"); +MODULE_VERSION(SOLO6X10_VERSION); +MODULE_LICENSE("GPL"); + +unsigned video_nr = -1; +module_param(video_nr, uint, 0644); +MODULE_PARM_DESC(video_nr, "videoX start number, -1 is autodetect (default)"); + +static int full_eeprom; /* default is only top 64B */ +module_param(full_eeprom, uint, 0644); +MODULE_PARM_DESC(full_eeprom, "Allow access to full 128B EEPROM (dangerous)"); + + +static void solo_set_time(struct solo_dev *solo_dev) +{ + struct timespec ts; + + ktime_get_ts(&ts); + + solo_reg_write(solo_dev, SOLO_TIMER_SEC, ts.tv_sec); + solo_reg_write(solo_dev, SOLO_TIMER_USEC, ts.tv_nsec / NSEC_PER_USEC); +} + +static void solo_timer_sync(struct solo_dev *solo_dev) +{ + u32 sec, usec; + struct timespec ts; + long diff; + + if (solo_dev->type != SOLO_DEV_6110) + return; + + if (++solo_dev->time_sync < 60) + return; + + solo_dev->time_sync = 0; + + sec = solo_reg_read(solo_dev, SOLO_TIMER_SEC); + usec = solo_reg_read(solo_dev, SOLO_TIMER_USEC); + + ktime_get_ts(&ts); + + diff = (long)ts.tv_sec - (long)sec; + diff = (diff * 1000000) + + ((long)(ts.tv_nsec / NSEC_PER_USEC) - (long)usec); + + if (diff > 1000 || diff < -1000) { + solo_set_time(solo_dev); + } else if (diff) { + long usec_lsb = solo_dev->usec_lsb; + + usec_lsb -= diff / 4; + if (usec_lsb < 0) + usec_lsb = 0; + else if (usec_lsb > 255) + usec_lsb = 255; + + solo_dev->usec_lsb = usec_lsb; + solo_reg_write(solo_dev, SOLO_TIMER_USEC_LSB, + solo_dev->usec_lsb); + } +} + +static irqreturn_t solo_isr(int irq, void *data) +{ + struct solo_dev *solo_dev = data; + u32 status; + int i; + + status = solo_reg_read(solo_dev, SOLO_IRQ_STAT); + if (!status) + return IRQ_NONE; + + if (status & ~solo_dev->irq_mask) { + solo_reg_write(solo_dev, SOLO_IRQ_STAT, + status & ~solo_dev->irq_mask); + status &= solo_dev->irq_mask; + } + + if (status & SOLO_IRQ_PCI_ERR) + solo_p2m_error_isr(solo_dev); + + for (i = 0; i < SOLO_NR_P2M; i++) + if (status & SOLO_IRQ_P2M(i)) + solo_p2m_isr(solo_dev, i); + + if (status & SOLO_IRQ_IIC) + solo_i2c_isr(solo_dev); + + if (status & SOLO_IRQ_VIDEO_IN) { + solo_video_in_isr(solo_dev); + solo_timer_sync(solo_dev); + } + + if (status & SOLO_IRQ_ENCODER) + solo_enc_v4l2_isr(solo_dev); + + if (status & SOLO_IRQ_G723) + solo_g723_isr(solo_dev); + + /* Clear all interrupts handled */ + solo_reg_write(solo_dev, SOLO_IRQ_STAT, status); + + return IRQ_HANDLED; +} + +static void free_solo_dev(struct solo_dev *solo_dev) +{ + struct pci_dev *pdev; + + if (!solo_dev) + return; + + if (solo_dev->dev.parent) + device_unregister(&solo_dev->dev); + + pdev = solo_dev->pdev; + + /* If we never initialized the PCI device, then nothing else + * below here needs cleanup */ + if (!pdev) { + kfree(solo_dev); + return; + } + + if (solo_dev->reg_base) { + /* Bring down the sub-devices first */ + solo_g723_exit(solo_dev); + solo_enc_v4l2_exit(solo_dev); + solo_enc_exit(solo_dev); + solo_v4l2_exit(solo_dev); + solo_disp_exit(solo_dev); + solo_gpio_exit(solo_dev); + solo_p2m_exit(solo_dev); + solo_i2c_exit(solo_dev); + + /* Now cleanup the PCI device */ + solo_irq_off(solo_dev, ~0); + pci_iounmap(pdev, solo_dev->reg_base); + if (pdev->irq) + free_irq(pdev->irq, solo_dev); + } + + pci_release_regions(pdev); + pci_disable_device(pdev); + v4l2_device_unregister(&solo_dev->v4l2_dev); + pci_set_drvdata(pdev, NULL); + + kfree(solo_dev); +} + +static ssize_t eeprom_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct solo_dev *solo_dev = + container_of(dev, struct solo_dev, dev); + unsigned short *p = (unsigned short *)buf; + int i; + + if (count & 0x1) + dev_warn(dev, "EEPROM Write not aligned (truncating)\n"); + + if (!full_eeprom && count > 64) { + dev_warn(dev, "EEPROM Write truncated to 64 bytes\n"); + count = 64; + } else if (full_eeprom && count > 128) { + dev_warn(dev, "EEPROM Write truncated to 128 bytes\n"); + count = 128; + } + + solo_eeprom_ewen(solo_dev, 1); + + for (i = full_eeprom ? 0 : 32; i < min((int)(full_eeprom ? 64 : 32), + (int)(count / 2)); i++) + solo_eeprom_write(solo_dev, i, cpu_to_be16(p[i])); + + solo_eeprom_ewen(solo_dev, 0); + + return count; +} + +static ssize_t eeprom_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct solo_dev *solo_dev = + container_of(dev, struct solo_dev, dev); + unsigned short *p = (unsigned short *)buf; + int count = (full_eeprom ? 128 : 64); + int i; + + for (i = (full_eeprom ? 0 : 32); i < (count / 2); i++) + p[i] = be16_to_cpu(solo_eeprom_read(solo_dev, i)); + + return count; +} + +static ssize_t p2m_timeouts_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct solo_dev *solo_dev = + container_of(dev, struct solo_dev, dev); + + return sprintf(buf, "%d\n", solo_dev->p2m_timeouts); +} + +static ssize_t sdram_size_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct solo_dev *solo_dev = + container_of(dev, struct solo_dev, dev); + + return sprintf(buf, "%dMegs\n", solo_dev->sdram_size >> 20); +} + +static ssize_t tw28xx_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct solo_dev *solo_dev = + container_of(dev, struct solo_dev, dev); + + return sprintf(buf, "tw2815[%d] tw2864[%d] tw2865[%d]\n", + hweight32(solo_dev->tw2815), + hweight32(solo_dev->tw2864), + hweight32(solo_dev->tw2865)); +} + +static ssize_t input_map_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct solo_dev *solo_dev = + container_of(dev, struct solo_dev, dev); + unsigned int val; + char *out = buf; + + val = solo_reg_read(solo_dev, SOLO_VI_CH_SWITCH_0); + out += sprintf(out, "Channel 0 => Input %d\n", val & 0x1f); + out += sprintf(out, "Channel 1 => Input %d\n", (val >> 5) & 0x1f); + out += sprintf(out, "Channel 2 => Input %d\n", (val >> 10) & 0x1f); + out += sprintf(out, "Channel 3 => Input %d\n", (val >> 15) & 0x1f); + out += sprintf(out, "Channel 4 => Input %d\n", (val >> 20) & 0x1f); + out += sprintf(out, "Channel 5 => Input %d\n", (val >> 25) & 0x1f); + + val = solo_reg_read(solo_dev, SOLO_VI_CH_SWITCH_1); + out += sprintf(out, "Channel 6 => Input %d\n", val & 0x1f); + out += sprintf(out, "Channel 7 => Input %d\n", (val >> 5) & 0x1f); + out += sprintf(out, "Channel 8 => Input %d\n", (val >> 10) & 0x1f); + out += sprintf(out, "Channel 9 => Input %d\n", (val >> 15) & 0x1f); + out += sprintf(out, "Channel 10 => Input %d\n", (val >> 20) & 0x1f); + out += sprintf(out, "Channel 11 => Input %d\n", (val >> 25) & 0x1f); + + val = solo_reg_read(solo_dev, SOLO_VI_CH_SWITCH_2); + out += sprintf(out, "Channel 12 => Input %d\n", val & 0x1f); + out += sprintf(out, "Channel 13 => Input %d\n", (val >> 5) & 0x1f); + out += sprintf(out, "Channel 14 => Input %d\n", (val >> 10) & 0x1f); + out += sprintf(out, "Channel 15 => Input %d\n", (val >> 15) & 0x1f); + out += sprintf(out, "Spot Output => Input %d\n", (val >> 20) & 0x1f); + + return out - buf; +} + +static ssize_t p2m_timeout_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct solo_dev *solo_dev = + container_of(dev, struct solo_dev, dev); + unsigned long ms; + + int ret = kstrtoul(buf, 10, &ms); + if (ret < 0 || ms > 200) + return -EINVAL; + solo_dev->p2m_jiffies = msecs_to_jiffies(ms); + + return count; +} + +static ssize_t p2m_timeout_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct solo_dev *solo_dev = + container_of(dev, struct solo_dev, dev); + + return sprintf(buf, "%ums\n", jiffies_to_msecs(solo_dev->p2m_jiffies)); +} + +static ssize_t intervals_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct solo_dev *solo_dev = + container_of(dev, struct solo_dev, dev); + char *out = buf; + int fps = solo_dev->fps; + int i; + + for (i = 0; i < solo_dev->nr_chans; i++) { + out += sprintf(out, "Channel %d: %d/%d (0x%08x)\n", + i, solo_dev->v4l2_enc[i]->interval, fps, + solo_reg_read(solo_dev, SOLO_CAP_CH_INTV(i))); + } + + return out - buf; +} + +static ssize_t sdram_offsets_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct solo_dev *solo_dev = + container_of(dev, struct solo_dev, dev); + char *out = buf; + + out += sprintf(out, "DISP: 0x%08x @ 0x%08x\n", + SOLO_DISP_EXT_ADDR, + SOLO_DISP_EXT_SIZE); + + out += sprintf(out, "EOSD: 0x%08x @ 0x%08x (0x%08x * %d)\n", + SOLO_EOSD_EXT_ADDR, + SOLO_EOSD_EXT_AREA(solo_dev), + SOLO_EOSD_EXT_SIZE(solo_dev), + SOLO_EOSD_EXT_AREA(solo_dev) / + SOLO_EOSD_EXT_SIZE(solo_dev)); + + out += sprintf(out, "MOTI: 0x%08x @ 0x%08x\n", + SOLO_MOTION_EXT_ADDR(solo_dev), + SOLO_MOTION_EXT_SIZE); + + out += sprintf(out, "G723: 0x%08x @ 0x%08x\n", + SOLO_G723_EXT_ADDR(solo_dev), + SOLO_G723_EXT_SIZE); + + out += sprintf(out, "CAPT: 0x%08x @ 0x%08x (0x%08x * %d)\n", + SOLO_CAP_EXT_ADDR(solo_dev), + SOLO_CAP_EXT_SIZE(solo_dev), + SOLO_CAP_PAGE_SIZE, + SOLO_CAP_EXT_SIZE(solo_dev) / SOLO_CAP_PAGE_SIZE); + + out += sprintf(out, "EREF: 0x%08x @ 0x%08x (0x%08x * %d)\n", + SOLO_EREF_EXT_ADDR(solo_dev), + SOLO_EREF_EXT_AREA(solo_dev), + SOLO_EREF_EXT_SIZE, + SOLO_EREF_EXT_AREA(solo_dev) / SOLO_EREF_EXT_SIZE); + + out += sprintf(out, "MPEG: 0x%08x @ 0x%08x\n", + SOLO_MP4E_EXT_ADDR(solo_dev), + SOLO_MP4E_EXT_SIZE(solo_dev)); + + out += sprintf(out, "JPEG: 0x%08x @ 0x%08x\n", + SOLO_JPEG_EXT_ADDR(solo_dev), + SOLO_JPEG_EXT_SIZE(solo_dev)); + + return out - buf; +} + +static ssize_t sdram_show(struct file *file, struct kobject *kobj, + struct bin_attribute *a, char *buf, + loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct solo_dev *solo_dev = + container_of(dev, struct solo_dev, dev); + const int size = solo_dev->sdram_size; + + if (off >= size) + return 0; + + if (off + count > size) + count = size - off; + + if (solo_p2m_dma(solo_dev, 0, buf, off, count, 0, 0)) + return -EIO; + + return count; +} + +static const struct device_attribute solo_dev_attrs[] = { + __ATTR(eeprom, 0640, eeprom_show, eeprom_store), + __ATTR(p2m_timeout, 0644, p2m_timeout_show, p2m_timeout_store), + __ATTR_RO(p2m_timeouts), + __ATTR_RO(sdram_size), + __ATTR_RO(tw28xx), + __ATTR_RO(input_map), + __ATTR_RO(intervals), + __ATTR_RO(sdram_offsets), +}; + +static void solo_device_release(struct device *dev) +{ + /* Do nothing */ +} + +static int solo_sysfs_init(struct solo_dev *solo_dev) +{ + struct bin_attribute *sdram_attr = &solo_dev->sdram_attr; + struct device *dev = &solo_dev->dev; + const char *driver; + int i; + + if (solo_dev->type == SOLO_DEV_6110) + driver = "solo6110"; + else + driver = "solo6010"; + + dev->release = solo_device_release; + dev->parent = &solo_dev->pdev->dev; + set_dev_node(dev, dev_to_node(&solo_dev->pdev->dev)); + dev_set_name(dev, "%s-%d-%d", driver, solo_dev->vfd->num, + solo_dev->nr_chans); + + if (device_register(dev)) { + dev->parent = NULL; + return -ENOMEM; + } + + for (i = 0; i < ARRAY_SIZE(solo_dev_attrs); i++) { + if (device_create_file(dev, &solo_dev_attrs[i])) { + device_unregister(dev); + return -ENOMEM; + } + } + + sysfs_attr_init(&sdram_attr->attr); + sdram_attr->attr.name = "sdram"; + sdram_attr->attr.mode = 0440; + sdram_attr->read = sdram_show; + sdram_attr->size = solo_dev->sdram_size; + + if (device_create_bin_file(dev, sdram_attr)) { + device_unregister(dev); + return -ENOMEM; + } + + return 0; +} + +static int solo_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct solo_dev *solo_dev; + int ret; + u8 chip_id; + + solo_dev = kzalloc(sizeof(*solo_dev), GFP_KERNEL); + if (solo_dev == NULL) + return -ENOMEM; + + if (id->driver_data == SOLO_DEV_6010) + dev_info(&pdev->dev, "Probing Softlogic 6010\n"); + else + dev_info(&pdev->dev, "Probing Softlogic 6110\n"); + + solo_dev->type = id->driver_data; + solo_dev->pdev = pdev; + spin_lock_init(&solo_dev->reg_io_lock); + ret = v4l2_device_register(&pdev->dev, &solo_dev->v4l2_dev); + if (ret) + goto fail_probe; + + /* Only for during init */ + solo_dev->p2m_jiffies = msecs_to_jiffies(100); + + ret = pci_enable_device(pdev); + if (ret) + goto fail_probe; + + pci_set_master(pdev); + + /* RETRY/TRDY Timeout disabled */ + pci_write_config_byte(pdev, 0x40, 0x00); + pci_write_config_byte(pdev, 0x41, 0x00); + + ret = pci_request_regions(pdev, SOLO6X10_NAME); + if (ret) + goto fail_probe; + + solo_dev->reg_base = pci_ioremap_bar(pdev, 0); + if (solo_dev->reg_base == NULL) { + ret = -ENOMEM; + goto fail_probe; + } + + chip_id = solo_reg_read(solo_dev, SOLO_CHIP_OPTION) & + SOLO_CHIP_ID_MASK; + switch (chip_id) { + case 7: + solo_dev->nr_chans = 16; + solo_dev->nr_ext = 5; + break; + case 6: + solo_dev->nr_chans = 8; + solo_dev->nr_ext = 2; + break; + default: + dev_warn(&pdev->dev, "Invalid chip_id 0x%02x, assuming 4 ch\n", + chip_id); + case 5: + solo_dev->nr_chans = 4; + solo_dev->nr_ext = 1; + } + + /* Disable all interrupts to start */ + solo_irq_off(solo_dev, ~0); + + /* Initial global settings */ + if (solo_dev->type == SOLO_DEV_6010) { + solo_dev->clock_mhz = 108; + solo_dev->sys_config = SOLO_SYS_CFG_SDRAM64BIT + | SOLO_SYS_CFG_INPUTDIV(25) + | SOLO_SYS_CFG_FEEDBACKDIV(solo_dev->clock_mhz * 2 - 2) + | SOLO_SYS_CFG_OUTDIV(3); + solo_reg_write(solo_dev, SOLO_SYS_CFG, solo_dev->sys_config); + } else { + u32 divq, divf; + + solo_dev->clock_mhz = 135; + + if (solo_dev->clock_mhz < 125) { + divq = 3; + divf = (solo_dev->clock_mhz * 4) / 3 - 1; + } else { + divq = 2; + divf = (solo_dev->clock_mhz * 2) / 3 - 1; + } + + solo_reg_write(solo_dev, SOLO_PLL_CONFIG, + (1 << 20) | /* PLL_RANGE */ + (8 << 15) | /* PLL_DIVR */ + (divq << 12) | + (divf << 4) | + (1 << 1) /* PLL_FSEN */); + + solo_dev->sys_config = SOLO_SYS_CFG_SDRAM64BIT; + } + + solo_reg_write(solo_dev, SOLO_SYS_CFG, solo_dev->sys_config); + solo_reg_write(solo_dev, SOLO_TIMER_CLOCK_NUM, + solo_dev->clock_mhz - 1); + + /* PLL locking time of 1ms */ + mdelay(1); + + ret = request_irq(pdev->irq, solo_isr, IRQF_SHARED, SOLO6X10_NAME, + solo_dev); + if (ret) + goto fail_probe; + + /* Handle this from the start */ + solo_irq_on(solo_dev, SOLO_IRQ_PCI_ERR); + + ret = solo_i2c_init(solo_dev); + if (ret) + goto fail_probe; + + /* Setup the DMA engine */ + solo_reg_write(solo_dev, SOLO_DMA_CTRL, + SOLO_DMA_CTRL_REFRESH_CYCLE(1) | + SOLO_DMA_CTRL_SDRAM_SIZE(2) | + SOLO_DMA_CTRL_SDRAM_CLK_INVERT | + SOLO_DMA_CTRL_READ_CLK_SELECT | + SOLO_DMA_CTRL_LATENCY(1)); + + /* Undocumented crap */ + solo_reg_write(solo_dev, SOLO_DMA_CTRL1, + solo_dev->type == SOLO_DEV_6010 ? 0x100 : 0x300); + + if (solo_dev->type != SOLO_DEV_6010) { + solo_dev->usec_lsb = 0x3f; + solo_set_time(solo_dev); + } + + /* Disable watchdog */ + solo_reg_write(solo_dev, SOLO_WATCHDOG, 0); + + /* Initialize sub components */ + + ret = solo_p2m_init(solo_dev); + if (ret) + goto fail_probe; + + ret = solo_disp_init(solo_dev); + if (ret) + goto fail_probe; + + ret = solo_gpio_init(solo_dev); + if (ret) + goto fail_probe; + + ret = solo_tw28_init(solo_dev); + if (ret) + goto fail_probe; + + ret = solo_v4l2_init(solo_dev, video_nr); + if (ret) + goto fail_probe; + + ret = solo_enc_init(solo_dev); + if (ret) + goto fail_probe; + + ret = solo_enc_v4l2_init(solo_dev, video_nr); + if (ret) + goto fail_probe; + + ret = solo_g723_init(solo_dev); + if (ret) + goto fail_probe; + + ret = solo_sysfs_init(solo_dev); + if (ret) + goto fail_probe; + + /* Now that init is over, set this lower */ + solo_dev->p2m_jiffies = msecs_to_jiffies(20); + + return 0; + +fail_probe: + free_solo_dev(solo_dev); + return ret; +} + +static void solo_pci_remove(struct pci_dev *pdev) +{ + struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev); + struct solo_dev *solo_dev = container_of(v4l2_dev, struct solo_dev, v4l2_dev); + + free_solo_dev(solo_dev); +} + +static DEFINE_PCI_DEVICE_TABLE(solo_id_table) = { + /* 6010 based cards */ + { PCI_DEVICE(PCI_VENDOR_ID_SOFTLOGIC, PCI_DEVICE_ID_SOLO6010), + .driver_data = SOLO_DEV_6010 }, + { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_4), + .driver_data = SOLO_DEV_6010 }, + { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_9), + .driver_data = SOLO_DEV_6010 }, + { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_16), + .driver_data = SOLO_DEV_6010 }, + { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_SOLO_4), + .driver_data = SOLO_DEV_6010 }, + { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_SOLO_9), + .driver_data = SOLO_DEV_6010 }, + { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_SOLO_16), + .driver_data = SOLO_DEV_6010 }, + /* 6110 based cards */ + { PCI_DEVICE(PCI_VENDOR_ID_SOFTLOGIC, PCI_DEVICE_ID_SOLO6110), + .driver_data = SOLO_DEV_6110 }, + { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_6110_4), + .driver_data = SOLO_DEV_6110 }, + { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_6110_8), + .driver_data = SOLO_DEV_6110 }, + { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_6110_16), + .driver_data = SOLO_DEV_6110 }, + {0,} +}; + +MODULE_DEVICE_TABLE(pci, solo_id_table); + +static struct pci_driver solo_pci_driver = { + .name = SOLO6X10_NAME, + .id_table = solo_id_table, + .probe = solo_pci_probe, + .remove = solo_pci_remove, +}; + +module_pci_driver(solo_pci_driver); diff --git a/drivers/staging/media/solo6x10/disp.c b/drivers/staging/media/solo6x10/solo6x10-disp.c index 884c0eb757c4..32d9953bc36e 100644 --- a/drivers/staging/media/solo6x10/disp.c +++ b/drivers/staging/media/solo6x10/solo6x10-disp.c @@ -1,6 +1,11 @@ /* - * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com - * Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net> + * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com> + * + * Original author: + * Ben Collins <bcollins@ubuntu.com> + * + * Additional work by: + * John Brooks <john.brooks@bluecherry.net> * * 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 @@ -21,6 +26,7 @@ #include <linux/module.h> #include <linux/videodev2.h> #include <media/v4l2-ioctl.h> + #include "solo6x10.h" #define SOLO_VCLK_DELAY 3 @@ -30,12 +36,8 @@ #define SOLO_MOT_THRESH_H 64 #define SOLO_MOT_THRESH_SIZE 8192 #define SOLO_MOT_THRESH_REAL (SOLO_MOT_THRESH_W * SOLO_MOT_THRESH_H) -#define SOLO_MOT_FLAG_SIZE 512 -#define SOLO_MOT_FLAG_AREA (SOLO_MOT_FLAG_SIZE * 32) - -static unsigned video_type; -module_param(video_type, uint, 0644); -MODULE_PARM_DESC(video_type, "video_type (0 = NTSC/Default, 1 = PAL)"); +#define SOLO_MOT_FLAG_SIZE 1024 +#define SOLO_MOT_FLAG_AREA (SOLO_MOT_FLAG_SIZE * 16) static void solo_vin_config(struct solo_dev *solo_dev) { @@ -73,7 +75,12 @@ static void solo_vin_config(struct solo_dev *solo_dev) solo_reg_write(solo_dev, SOLO_VI_CH_FORMAT, SOLO_VI_FD_SEL_MASK(0) | SOLO_VI_PROG_MASK(0)); - solo_reg_write(solo_dev, SOLO_VI_FMT_CFG, 0); + /* On 6110, initialize mozaic darkness stength */ + if (solo_dev->type == SOLO_DEV_6010) + solo_reg_write(solo_dev, SOLO_VI_FMT_CFG, 0); + else + solo_reg_write(solo_dev, SOLO_VI_FMT_CFG, 16 << 22); + solo_reg_write(solo_dev, SOLO_VI_PAGE_SW, 2); if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) { @@ -97,21 +104,30 @@ static void solo_vin_config(struct solo_dev *solo_dev) SOLO_VI_PB_HSTOP(16 + 720)); } -static void solo_disp_config(struct solo_dev *solo_dev) +static void solo_vout_config_cursor(struct solo_dev *dev) +{ + int i; + + /* Load (blank) cursor bitmap mask (2bpp) */ + for (i = 0; i < 20; i++) + solo_reg_write(dev, SOLO_VO_CURSOR_MASK(i), 0); + + solo_reg_write(dev, SOLO_VO_CURSOR_POS, 0); + + solo_reg_write(dev, SOLO_VO_CURSOR_CLR, + (0x80 << 24) | (0x80 << 16) | (0x10 << 8) | 0x80); + solo_reg_write(dev, SOLO_VO_CURSOR_CLR2, (0xe0 << 8) | 0x80); +} + +static void solo_vout_config(struct solo_dev *solo_dev) { solo_dev->vout_hstart = 6; solo_dev->vout_vstart = 8; - solo_reg_write(solo_dev, SOLO_VO_BORDER_LINE_COLOR, - (0xa0 << 24) | (0x88 << 16) | (0xa0 << 8) | 0x88); - solo_reg_write(solo_dev, SOLO_VO_BORDER_FILL_COLOR, - (0x10 << 24) | (0x8f << 16) | (0x10 << 8) | 0x8f); - solo_reg_write(solo_dev, SOLO_VO_BKG_COLOR, - (16 << 24) | (128 << 16) | (16 << 8) | 128); - solo_reg_write(solo_dev, SOLO_VO_FMT_ENC, solo_dev->video_type | SOLO_VO_USER_COLOR_SET_NAV | + SOLO_VO_USER_COLOR_SET_NAH | SOLO_VO_NA_COLOR_Y(0) | SOLO_VO_NA_COLOR_CB(0) | SOLO_VO_NA_COLOR_CR(0)); @@ -130,19 +146,31 @@ static void solo_disp_config(struct solo_dev *solo_dev) SOLO_VO_H_LEN(solo_dev->video_hsize) | SOLO_VO_V_LEN(solo_dev->video_vsize)); - solo_reg_write(solo_dev, SOLO_VI_WIN_SW, 5); + /* Border & background colors */ + solo_reg_write(solo_dev, SOLO_VO_BORDER_LINE_COLOR, + (0xa0 << 24) | (0x88 << 16) | (0xa0 << 8) | 0x88); + solo_reg_write(solo_dev, SOLO_VO_BORDER_FILL_COLOR, + (0x10 << 24) | (0x8f << 16) | (0x10 << 8) | 0x8f); + solo_reg_write(solo_dev, SOLO_VO_BKG_COLOR, + (16 << 24) | (128 << 16) | (16 << 8) | 128); + + solo_reg_write(solo_dev, SOLO_VO_DISP_ERASE, SOLO_VO_DISP_ERASE_ON); + + solo_reg_write(solo_dev, SOLO_VI_WIN_SW, 0); + + solo_reg_write(solo_dev, SOLO_VO_ZOOM_CTRL, 0); + solo_reg_write(solo_dev, SOLO_VO_FREEZE_CTRL, 0); solo_reg_write(solo_dev, SOLO_VO_DISP_CTRL, SOLO_VO_DISP_ON | SOLO_VO_DISP_ERASE_COUNT(8) | SOLO_VO_DISP_BASE(SOLO_DISP_EXT_ADDR)); - solo_reg_write(solo_dev, SOLO_VO_DISP_ERASE, SOLO_VO_DISP_ERASE_ON); - /* Enable channels we support */ - solo_reg_write(solo_dev, SOLO_VI_CH_ENA, (1 << solo_dev->nr_chans) - 1); + solo_vout_config_cursor(solo_dev); - /* Disable the watchdog */ - solo_reg_write(solo_dev, SOLO_WATCHDOG, 0); + /* Enable channels we support */ + solo_reg_write(solo_dev, SOLO_VI_CH_ENA, + (1 << solo_dev->nr_chans) - 1); } static int solo_dma_vin_region(struct solo_dev *solo_dev, u32 off, @@ -153,29 +181,48 @@ static int solo_dma_vin_region(struct solo_dev *solo_dev, u32 off, int ret = 0; for (i = 0; i < sizeof(buf) >> 1; i++) - buf[i] = val; + buf[i] = cpu_to_le16(val); for (i = 0; i < reg_size; i += sizeof(buf)) - ret |= solo_p2m_dma(solo_dev, SOLO_P2M_DMA_ID_VIN, 1, buf, + ret |= solo_p2m_dma(solo_dev, 1, buf, SOLO_MOTION_EXT_ADDR(solo_dev) + off + i, - sizeof(buf)); + sizeof(buf), 0, 0); return ret; } -void solo_set_motion_threshold(struct solo_dev *solo_dev, u8 ch, u16 val) +int solo_set_motion_threshold(struct solo_dev *solo_dev, u8 ch, u16 val) { if (ch > solo_dev->nr_chans) - return; + return -EINVAL; - solo_dma_vin_region(solo_dev, SOLO_MOT_FLAG_AREA + - (ch * SOLO_MOT_THRESH_SIZE * 2), - val, SOLO_MOT_THRESH_REAL); + return solo_dma_vin_region(solo_dev, SOLO_MOT_FLAG_AREA + + (ch * SOLO_MOT_THRESH_SIZE * 2), + val, SOLO_MOT_THRESH_SIZE); +} + +int solo_set_motion_block(struct solo_dev *solo_dev, u8 ch, + const struct solo_motion_thresholds *thresholds) +{ + u32 off = SOLO_MOT_FLAG_AREA + ch * SOLO_MOT_THRESH_SIZE * 2; + u16 buf[64]; + int x, y; + int ret = 0; + + memset(buf, 0, sizeof(buf)); + for (y = 0; y < SOLO_MOTION_SZ; y++) { + for (x = 0; x < SOLO_MOTION_SZ; x++) + buf[x] = cpu_to_le16(thresholds->thresholds[y][x]); + ret |= solo_p2m_dma(solo_dev, 1, buf, + SOLO_MOTION_EXT_ADDR(solo_dev) + off + y * sizeof(buf), + sizeof(buf), 0, 0); + } + return ret; } /* First 8k is motion flag (512 bytes * 16). Following that is an 8k+8k * threshold and working table for each channel. Atleast that's what the - * spec says. However, this code (take from rdk) has some mystery 8k + * spec says. However, this code (taken from rdk) has some mystery 8k * block right after the flag area, before the first thresh table. */ static void solo_motion_config(struct solo_dev *solo_dev) { @@ -188,9 +235,9 @@ static void solo_motion_config(struct solo_dev *solo_dev) /* Clear working cache table */ solo_dma_vin_region(solo_dev, SOLO_MOT_FLAG_AREA + - SOLO_MOT_THRESH_SIZE + - (i * SOLO_MOT_THRESH_SIZE * 2), - 0x0000, SOLO_MOT_THRESH_REAL); + (i * SOLO_MOT_THRESH_SIZE * 2) + + SOLO_MOT_THRESH_SIZE, 0x0000, + SOLO_MOT_THRESH_SIZE); /* Set default threshold table */ solo_set_motion_threshold(solo_dev, i, SOLO_DEF_MOT_THRESH); @@ -202,8 +249,8 @@ static void solo_motion_config(struct solo_dev *solo_dev) solo_reg_write(solo_dev, SOLO_VI_MOT_CTRL, SOLO_VI_MOTION_FRAME_COUNT(3) | SOLO_VI_MOTION_SAMPLE_LENGTH(solo_dev->video_hsize / 16) - | /* SOLO_VI_MOTION_INTR_START_STOP | */ - SOLO_VI_MOTION_SAMPLE_COUNT(10)); + /* | SOLO_VI_MOTION_INTR_START_STOP */ + | SOLO_VI_MOTION_SAMPLE_COUNT(10)); solo_reg_write(solo_dev, SOLO_VI_MOTION_BORDER, 0); solo_reg_write(solo_dev, SOLO_VI_MOTION_BAR, 0); @@ -214,19 +261,17 @@ int solo_disp_init(struct solo_dev *solo_dev) int i; solo_dev->video_hsize = 704; - if (video_type == 0) { - solo_dev->video_type = SOLO_VO_FMT_TYPE_NTSC; + if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) { solo_dev->video_vsize = 240; solo_dev->fps = 30; } else { - solo_dev->video_type = SOLO_VO_FMT_TYPE_PAL; solo_dev->video_vsize = 288; solo_dev->fps = 25; } solo_vin_config(solo_dev); solo_motion_config(solo_dev); - solo_disp_config(solo_dev); + solo_vout_config(solo_dev); for (i = 0; i < solo_dev->nr_chans; i++) solo_reg_write(solo_dev, SOLO_VI_WIN_ON(i), 1); @@ -238,8 +283,6 @@ void solo_disp_exit(struct solo_dev *solo_dev) { int i; - solo_irq_off(solo_dev, SOLO_IRQ_MOTION); - solo_reg_write(solo_dev, SOLO_VO_DISP_CTRL, 0); solo_reg_write(solo_dev, SOLO_VO_ZOOM_CTRL, 0); solo_reg_write(solo_dev, SOLO_VO_FREEZE_CTRL, 0); diff --git a/drivers/staging/media/solo6x10/solo6x10-eeprom.c b/drivers/staging/media/solo6x10/solo6x10-eeprom.c new file mode 100644 index 000000000000..9d1c9bb53d6b --- /dev/null +++ b/drivers/staging/media/solo6x10/solo6x10-eeprom.c @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com> + * + * Original author: + * Ben Collins <bcollins@ubuntu.com> + * + * Additional work by: + * John Brooks <john.brooks@bluecherry.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <linux/kernel.h> +#include <linux/delay.h> + +#include "solo6x10.h" + +/* Control */ +#define EE_SHIFT_CLK 0x04 +#define EE_CS 0x08 +#define EE_DATA_WRITE 0x02 +#define EE_DATA_READ 0x01 +#define EE_ENB (0x80 | EE_CS) + +#define eeprom_delay() udelay(100) +#if 0 +#define eeprom_delay() solo_reg_read(solo_dev, SOLO_EEPROM_CTRL) +#define eeprom_delay() ({ \ + int i, ret; \ + udelay(100); \ + for (i = ret = 0; i < 1000 && !ret; i++) \ + ret = solo_eeprom_reg_read(solo_dev); \ +}) +#endif +#define ADDR_LEN 6 + +/* Commands */ +#define EE_EWEN_CMD 4 +#define EE_EWDS_CMD 4 +#define EE_WRITE_CMD 5 +#define EE_READ_CMD 6 +#define EE_ERASE_CMD 7 + +static unsigned int solo_eeprom_reg_read(struct solo_dev *solo_dev) +{ + return solo_reg_read(solo_dev, SOLO_EEPROM_CTRL) & EE_DATA_READ; +} + +static void solo_eeprom_reg_write(struct solo_dev *solo_dev, u32 data) +{ + solo_reg_write(solo_dev, SOLO_EEPROM_CTRL, data); + eeprom_delay(); +} + +static void solo_eeprom_cmd(struct solo_dev *solo_dev, int cmd) +{ + int i; + + solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ACCESS_EN); + solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE); + + for (i = 4 + ADDR_LEN; i >= 0; i--) { + int dataval = (cmd & (1 << i)) ? EE_DATA_WRITE : 0; + + solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE | dataval); + solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE | + EE_SHIFT_CLK | dataval); + } + + solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE); +} + +unsigned int solo_eeprom_ewen(struct solo_dev *solo_dev, int w_en) +{ + int ewen_cmd = (w_en ? 0x3f : 0) | (EE_EWEN_CMD << ADDR_LEN); + unsigned int retval = 0; + int i; + + solo_eeprom_cmd(solo_dev, ewen_cmd); + + for (i = 0; i < 16; i++) { + solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE | + EE_SHIFT_CLK); + retval = (retval << 1) | solo_eeprom_reg_read(solo_dev); + solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE); + retval = (retval << 1) | solo_eeprom_reg_read(solo_dev); + } + + solo_eeprom_reg_write(solo_dev, ~EE_CS); + retval = (retval << 1) | solo_eeprom_reg_read(solo_dev); + + return retval; +} + +unsigned short solo_eeprom_read(struct solo_dev *solo_dev, int loc) +{ + int read_cmd = loc | (EE_READ_CMD << ADDR_LEN); + unsigned short retval = 0; + int i; + + solo_eeprom_cmd(solo_dev, read_cmd); + + for (i = 0; i < 16; i++) { + solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE | + EE_SHIFT_CLK); + retval = (retval << 1) | solo_eeprom_reg_read(solo_dev); + solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE); + } + + solo_eeprom_reg_write(solo_dev, ~EE_CS); + + return retval; +} + +int solo_eeprom_write(struct solo_dev *solo_dev, int loc, + unsigned short data) +{ + int write_cmd = loc | (EE_WRITE_CMD << ADDR_LEN); + unsigned int retval; + int i; + + solo_eeprom_cmd(solo_dev, write_cmd); + + for (i = 15; i >= 0; i--) { + unsigned int dataval = (data >> i) & 1; + + solo_eeprom_reg_write(solo_dev, EE_ENB); + solo_eeprom_reg_write(solo_dev, + EE_ENB | (dataval << 1) | EE_SHIFT_CLK); + } + + solo_eeprom_reg_write(solo_dev, EE_ENB); + solo_eeprom_reg_write(solo_dev, ~EE_CS); + solo_eeprom_reg_write(solo_dev, EE_ENB); + + for (i = retval = 0; i < 10000 && !retval; i++) + retval = solo_eeprom_reg_read(solo_dev); + + solo_eeprom_reg_write(solo_dev, ~EE_CS); + + return !retval; +} diff --git a/drivers/staging/media/solo6x10/enc.c b/drivers/staging/media/solo6x10/solo6x10-enc.c index de502599bb19..94d5735abf85 100644 --- a/drivers/staging/media/solo6x10/enc.c +++ b/drivers/staging/media/solo6x10/solo6x10-enc.c @@ -1,6 +1,11 @@ /* - * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com - * Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net> + * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com> + * + * Original author: + * Ben Collins <bcollins@ubuntu.com> + * + * Additional work by: + * John Brooks <john.brooks@bluecherry.net> * * 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,30 +23,41 @@ */ #include <linux/kernel.h> +#include <linux/font.h> +#include <linux/bitrev.h> #include <linux/slab.h> -#include "solo6x10.h" -#include "osd-font.h" -#define CAPTURE_MAX_BANDWIDTH 32 /* D1 4channel (D1 == 4) */ -#define OSG_BUFFER_SIZE 1024 +#include "solo6x10.h" #define VI_PROG_HSIZE (1280 - 16) #define VI_PROG_VSIZE (1024 - 16) +#define IRQ_LEVEL 2 + static void solo_capture_config(struct solo_dev *solo_dev) { - int i, j; unsigned long height; unsigned long width; - unsigned char *buf; + void *buf; + int i; solo_reg_write(solo_dev, SOLO_CAP_BASE, - SOLO_CAP_MAX_PAGE(SOLO_CAP_EXT_MAX_PAGE * - solo_dev->nr_chans) | - SOLO_CAP_BASE_ADDR(SOLO_CAP_EXT_ADDR(solo_dev) >> 16)); - solo_reg_write(solo_dev, SOLO_CAP_BTW, - (1 << 17) | SOLO_CAP_PROG_BANDWIDTH(2) | - SOLO_CAP_MAX_BANDWIDTH(CAPTURE_MAX_BANDWIDTH)); + SOLO_CAP_MAX_PAGE((SOLO_CAP_EXT_SIZE(solo_dev) + - SOLO_CAP_PAGE_SIZE) >> 16) + | SOLO_CAP_BASE_ADDR(SOLO_CAP_EXT_ADDR(solo_dev) >> 16)); + + /* XXX: Undocumented bits at b17 and b24 */ + if (solo_dev->type == SOLO_DEV_6110) { + /* NOTE: Ref driver has (62 << 24) here as well, but it causes + * wacked out frame timing on 4-port 6110. */ + solo_reg_write(solo_dev, SOLO_CAP_BTW, + (1 << 17) | SOLO_CAP_PROG_BANDWIDTH(2) | + SOLO_CAP_MAX_BANDWIDTH(36)); + } else { + solo_reg_write(solo_dev, SOLO_CAP_BTW, + (1 << 17) | SOLO_CAP_PROG_BANDWIDTH(2) | + SOLO_CAP_MAX_BANDWIDTH(32)); + } /* Set scale 1, 9 dimension */ width = solo_dev->video_hsize; @@ -96,115 +112,212 @@ static void solo_capture_config(struct solo_dev *solo_dev) solo_reg_write(solo_dev, SOLO_VE_OSD_BASE, SOLO_EOSD_EXT_ADDR >> 16); solo_reg_write(solo_dev, SOLO_VE_OSD_CLR, 0xF0 << 16 | 0x80 << 8 | 0x80); - solo_reg_write(solo_dev, SOLO_VE_OSD_OPT, 0); + + if (solo_dev->type == SOLO_DEV_6010) + solo_reg_write(solo_dev, SOLO_VE_OSD_OPT, + SOLO_VE_OSD_H_SHADOW | SOLO_VE_OSD_V_SHADOW); + else + solo_reg_write(solo_dev, SOLO_VE_OSD_OPT, SOLO_VE_OSD_V_DOUBLE + | SOLO_VE_OSD_H_SHADOW | SOLO_VE_OSD_V_SHADOW); /* Clear OSG buffer */ - buf = kzalloc(OSG_BUFFER_SIZE, GFP_KERNEL); + buf = kzalloc(SOLO_EOSD_EXT_SIZE(solo_dev), GFP_KERNEL); if (!buf) return; for (i = 0; i < solo_dev->nr_chans; i++) { - for (j = 0; j < SOLO_EOSD_EXT_SIZE; j += OSG_BUFFER_SIZE) { - solo_p2m_dma(solo_dev, SOLO_P2M_DMA_ID_MP4E, 1, buf, - SOLO_EOSD_EXT_ADDR + - (i * SOLO_EOSD_EXT_SIZE) + j, - OSG_BUFFER_SIZE); - } + solo_p2m_dma(solo_dev, 1, buf, + SOLO_EOSD_EXT_ADDR + + (SOLO_EOSD_EXT_SIZE(solo_dev) * i), + SOLO_EOSD_EXT_SIZE(solo_dev), 0, 0); } kfree(buf); } +/* Should be called with enable_lock held */ int solo_osd_print(struct solo_enc_dev *solo_enc) { struct solo_dev *solo_dev = solo_enc->solo_dev; - char *str = solo_enc->osd_text; - u8 *buf; + unsigned char *str = solo_enc->osd_text; + u8 *buf = solo_enc->osd_buf; u32 reg = solo_reg_read(solo_dev, SOLO_VE_OSD_CH); - int len = strlen(str); + const struct font_desc *vga = find_font("VGA8x16"); + const unsigned char *vga_data; + int len; int i, j; - int x = 1, y = 1; + + if (WARN_ON_ONCE(!vga)) + return -ENODEV; + + len = strlen(str); if (len == 0) { + /* Disable OSD on this channel */ reg &= ~(1 << solo_enc->ch); solo_reg_write(solo_dev, SOLO_VE_OSD_CH, reg); return 0; } - buf = kzalloc(SOLO_EOSD_EXT_SIZE, GFP_KERNEL); - if (!buf) - return -ENOMEM; + memset(buf, 0, SOLO_EOSD_EXT_SIZE_MAX); + vga_data = (const unsigned char *)vga->data; for (i = 0; i < len; i++) { + unsigned char c = str[i]; + for (j = 0; j < 16; j++) { - buf[(j*2) + (i%2) + ((x + (i/2)) * 32) + (y * 2048)] = - (solo_osd_font[(str[i] * 4) + (j / 4)] - >> ((3 - (j % 4)) * 8)) & 0xff; + buf[(j * 2) + (i % 2) + (i / 2 * 32)] = + bitrev8(vga_data[(c * 16) + j]); } } - solo_p2m_dma(solo_dev, 0, 1, buf, SOLO_EOSD_EXT_ADDR + - (solo_enc->ch * SOLO_EOSD_EXT_SIZE), SOLO_EOSD_EXT_SIZE); + solo_p2m_dma(solo_dev, 1, buf, + SOLO_EOSD_EXT_ADDR + + (solo_enc->ch * SOLO_EOSD_EXT_SIZE(solo_dev)), + SOLO_EOSD_EXT_SIZE(solo_dev), 0, 0); + + /* Enable OSD on this channel */ reg |= (1 << solo_enc->ch); solo_reg_write(solo_dev, SOLO_VE_OSD_CH, reg); - kfree(buf); - return 0; } +/** + * Set channel Quality Profile (0-3). + */ +void solo_s_jpeg_qp(struct solo_dev *solo_dev, unsigned int ch, + unsigned int qp) +{ + unsigned long flags; + unsigned int idx, reg; + + if ((ch > 31) || (qp > 3)) + return; + + if (solo_dev->type == SOLO_DEV_6010) + return; + + if (ch < 16) { + idx = 0; + reg = SOLO_VE_JPEG_QP_CH_L; + } else { + ch -= 16; + idx = 1; + reg = SOLO_VE_JPEG_QP_CH_H; + } + ch *= 2; + + spin_lock_irqsave(&solo_dev->jpeg_qp_lock, flags); + + solo_dev->jpeg_qp[idx] &= ~(3 << ch); + solo_dev->jpeg_qp[idx] |= (qp & 3) << ch; + + solo_reg_write(solo_dev, reg, solo_dev->jpeg_qp[idx]); + + spin_unlock_irqrestore(&solo_dev->jpeg_qp_lock, flags); +} + +int solo_g_jpeg_qp(struct solo_dev *solo_dev, unsigned int ch) +{ + int idx; + + if (solo_dev->type == SOLO_DEV_6010) + return 2; + + if (WARN_ON_ONCE(ch > 31)) + return 2; + + if (ch < 16) { + idx = 0; + } else { + ch -= 16; + idx = 1; + } + ch *= 2; + + return (solo_dev->jpeg_qp[idx] >> ch) & 3; +} + +#define SOLO_QP_INIT 0xaaaaaaaa + static void solo_jpeg_config(struct solo_dev *solo_dev) { - u32 reg; - if (solo_dev->flags & FLAGS_6110) - reg = (4 << 24) | (3 << 16) | (2 << 8) | (1 << 0); - else - reg = (2 << 24) | (2 << 16) | (2 << 8) | (2 << 0); - solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_TBL, reg); - solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_CH_L, 0); - solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_CH_H, 0); + if (solo_dev->type == SOLO_DEV_6010) { + solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_TBL, + (2 << 24) | (2 << 16) | (2 << 8) | 2); + } else { + solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_TBL, + (4 << 24) | (3 << 16) | (2 << 8) | 1); + } + + spin_lock_init(&solo_dev->jpeg_qp_lock); + + /* Initialize Quality Profile for all channels */ + solo_dev->jpeg_qp[0] = solo_dev->jpeg_qp[1] = SOLO_QP_INIT; + solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_CH_L, SOLO_QP_INIT); + solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_CH_H, SOLO_QP_INIT); + solo_reg_write(solo_dev, SOLO_VE_JPEG_CFG, (SOLO_JPEG_EXT_SIZE(solo_dev) & 0xffff0000) | ((SOLO_JPEG_EXT_ADDR(solo_dev) >> 16) & 0x0000ffff)); solo_reg_write(solo_dev, SOLO_VE_JPEG_CTRL, 0xffffffff); - /* que limit, samp limit, pos limit */ - solo_reg_write(solo_dev, 0x0688, (0 << 16) | (30 << 8) | 60); + if (solo_dev->type == SOLO_DEV_6110) { + solo_reg_write(solo_dev, SOLO_VE_JPEG_CFG1, + (0 << 16) | (30 << 8) | 60); + } } static void solo_mp4e_config(struct solo_dev *solo_dev) { int i; - u32 reg; + u32 cfg; - /* We can only use VE_INTR_CTRL(0) if we want to support mjpeg */ solo_reg_write(solo_dev, SOLO_VE_CFG0, - SOLO_VE_INTR_CTRL(0) | + SOLO_VE_INTR_CTRL(IRQ_LEVEL) | SOLO_VE_BLOCK_SIZE(SOLO_MP4E_EXT_SIZE(solo_dev) >> 16) | SOLO_VE_BLOCK_BASE(SOLO_MP4E_EXT_ADDR(solo_dev) >> 16)); - solo_reg_write(solo_dev, SOLO_VE_CFG1, - SOLO_VE_INSERT_INDEX | SOLO_VE_MOTION_MODE(0)); + + cfg = SOLO_VE_BYTE_ALIGN(2) | SOLO_VE_INSERT_INDEX + | SOLO_VE_MOTION_MODE(0); + if (solo_dev->type != SOLO_DEV_6010) { + cfg |= SOLO_VE_MPEG_SIZE_H( + (SOLO_MP4E_EXT_SIZE(solo_dev) >> 24) & 0x0f); + cfg |= SOLO_VE_JPEG_SIZE_H( + (SOLO_JPEG_EXT_SIZE(solo_dev) >> 24) & 0x0f); + } + solo_reg_write(solo_dev, SOLO_VE_CFG1, cfg); solo_reg_write(solo_dev, SOLO_VE_WMRK_POLY, 0); solo_reg_write(solo_dev, SOLO_VE_VMRK_INIT_KEY, 0); solo_reg_write(solo_dev, SOLO_VE_WMRK_STRL, 0); + if (solo_dev->type == SOLO_DEV_6110) + solo_reg_write(solo_dev, SOLO_VE_WMRK_ENABLE, 0); solo_reg_write(solo_dev, SOLO_VE_ENCRYP_POLY, 0); solo_reg_write(solo_dev, SOLO_VE_ENCRYP_INIT, 0); - reg = SOLO_VE_LITTLE_ENDIAN | SOLO_COMP_ATTR_FCODE(1) | - SOLO_COMP_TIME_INC(0) | SOLO_COMP_TIME_WIDTH(15); - if (solo_dev->flags & FLAGS_6110) - reg |= SOLO_DCT_INTERVAL(10); - else - reg |= SOLO_DCT_INTERVAL(36 / 4); - solo_reg_write(solo_dev, SOLO_VE_ATTR, reg); + solo_reg_write(solo_dev, SOLO_VE_ATTR, + SOLO_VE_LITTLE_ENDIAN | + SOLO_COMP_ATTR_FCODE(1) | + SOLO_COMP_TIME_INC(0) | + SOLO_COMP_TIME_WIDTH(15) | + SOLO_DCT_INTERVAL(solo_dev->type == SOLO_DEV_6010 ? 9 : 10)); - for (i = 0; i < solo_dev->nr_chans; i++) + for (i = 0; i < solo_dev->nr_chans; i++) { solo_reg_write(solo_dev, SOLO_VE_CH_REF_BASE(i), (SOLO_EREF_EXT_ADDR(solo_dev) + (i * SOLO_EREF_EXT_SIZE)) >> 16); + solo_reg_write(solo_dev, SOLO_VE_CH_REF_BASE_E(i), + (SOLO_EREF_EXT_ADDR(solo_dev) + + ((i + 16) * SOLO_EREF_EXT_SIZE)) >> 16); + } - if (solo_dev->flags & FLAGS_6110) - solo_reg_write(solo_dev, 0x0634, 0x00040008); /* ? */ + if (solo_dev->type == SOLO_DEV_6110) { + solo_reg_write(solo_dev, SOLO_VE_COMPT_MOT, 0x00040008); + } else { + for (i = 0; i < solo_dev->nr_chans; i++) + solo_reg_write(solo_dev, SOLO_VE_CH_MOT(i), 0x100); + } } int solo_enc_init(struct solo_dev *solo_dev) @@ -220,8 +333,6 @@ int solo_enc_init(struct solo_dev *solo_dev) solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(i), 0); } - solo_irq_on(solo_dev, SOLO_IRQ_ENCODER); - return 0; } @@ -229,8 +340,6 @@ void solo_enc_exit(struct solo_dev *solo_dev) { int i; - solo_irq_off(solo_dev, SOLO_IRQ_ENCODER); - for (i = 0; i < solo_dev->nr_chans; i++) { solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(i), 0); solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(i), 0); diff --git a/drivers/staging/media/solo6x10/g723.c b/drivers/staging/media/solo6x10/solo6x10-g723.c index 2cd0de28a633..1db18c7972a0 100644 --- a/drivers/staging/media/solo6x10/g723.c +++ b/drivers/staging/media/solo6x10/solo6x10-g723.c @@ -1,6 +1,11 @@ /* - * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com - * Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net> + * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com> + * + * Original author: + * Ben Collins <bcollins@ubuntu.com> + * + * Additional work by: + * John Brooks <john.brooks@bluecherry.net> * * 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 @@ -21,17 +26,18 @@ #include <linux/mempool.h> #include <linux/poll.h> #include <linux/kthread.h> -#include <linux/slab.h> #include <linux/freezer.h> -#include <linux/export.h> +#include <linux/module.h> +#include <linux/slab.h> + #include <sound/core.h> #include <sound/initval.h> #include <sound/pcm.h> #include <sound/control.h> + #include "solo6x10.h" -#include "tw28.h" +#include "solo6x10-tw28.h" -#define G723_INTR_ORDER 0 #define G723_FDMA_PAGES 32 #define G723_PERIOD_BYTES 48 #define G723_PERIOD_BLOCK 1024 @@ -46,36 +52,40 @@ /* The solo writes to 1k byte pages, 32 pages, in the dma. Each 1k page * is broken down to 20 * 48 byte regions (one for each channel possible) * with the rest of the page being dummy data. */ -#define MAX_BUFFER (G723_PERIOD_BYTES * PERIODS_MAX) -#define IRQ_PAGES 4 /* 0 - 4 */ -#define PERIODS_MIN (1 << IRQ_PAGES) +#define G723_MAX_BUFFER (G723_PERIOD_BYTES * PERIODS_MAX) +#define G723_INTR_ORDER 4 /* 0 - 4 */ +#define PERIODS_MIN (1 << G723_INTR_ORDER) #define PERIODS_MAX G723_FDMA_PAGES struct solo_snd_pcm { - int on; - spinlock_t lock; - struct solo_dev *solo_dev; - unsigned char g723_buf[G723_PERIOD_BYTES]; + int on; + spinlock_t lock; + struct solo_dev *solo_dev; + unsigned char *g723_buf; + dma_addr_t g723_dma; }; static void solo_g723_config(struct solo_dev *solo_dev) { int clk_div; - clk_div = SOLO_CLOCK_MHZ / (SAMPLERATE * (BITRATE * 2) * 2); + clk_div = (solo_dev->clock_mhz * 1000000) + / (SAMPLERATE * (BITRATE * 2) * 2); solo_reg_write(solo_dev, SOLO_AUDIO_SAMPLE, - SOLO_AUDIO_BITRATE(BITRATE) | - SOLO_AUDIO_CLK_DIV(clk_div)); + SOLO_AUDIO_BITRATE(BITRATE) + | SOLO_AUDIO_CLK_DIV(clk_div)); solo_reg_write(solo_dev, SOLO_AUDIO_FDMA_INTR, - SOLO_AUDIO_FDMA_INTERVAL(IRQ_PAGES) | - SOLO_AUDIO_INTR_ORDER(G723_INTR_ORDER) | - SOLO_AUDIO_FDMA_BASE(SOLO_G723_EXT_ADDR(solo_dev) >> 16)); + SOLO_AUDIO_FDMA_INTERVAL(1) + | SOLO_AUDIO_INTR_ORDER(G723_INTR_ORDER) + | SOLO_AUDIO_FDMA_BASE(SOLO_G723_EXT_ADDR(solo_dev) >> 16)); solo_reg_write(solo_dev, SOLO_AUDIO_CONTROL, - SOLO_AUDIO_ENABLE | SOLO_AUDIO_I2S_MODE | - SOLO_AUDIO_I2S_MULTI(3) | SOLO_AUDIO_MODE(OUTMODE_MASK)); + SOLO_AUDIO_ENABLE + | SOLO_AUDIO_I2S_MODE + | SOLO_AUDIO_I2S_MULTI(3) + | SOLO_AUDIO_MODE(OUTMODE_MASK)); } void solo_g723_isr(struct solo_dev *solo_dev) @@ -85,8 +95,6 @@ void solo_g723_isr(struct solo_dev *solo_dev) struct snd_pcm_substream *ss; struct solo_snd_pcm *solo_pcm; - solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_G723); - for (ss = pstr->substream; ss != NULL; ss = ss->next) { if (snd_pcm_substream_chip(ss) == NULL) continue; @@ -115,18 +123,18 @@ static int snd_solo_hw_free(struct snd_pcm_substream *ss) return snd_pcm_lib_free_pages(ss); } -static struct snd_pcm_hardware snd_solo_pcm_hw = { +static const struct snd_pcm_hardware snd_solo_pcm_hw = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID), .formats = SNDRV_PCM_FMTBIT_U8, .rates = SNDRV_PCM_RATE_8000, - .rate_min = 8000, - .rate_max = 8000, + .rate_min = SAMPLERATE, + .rate_max = SAMPLERATE, .channels_min = 1, .channels_max = 1, - .buffer_bytes_max = MAX_BUFFER, + .buffer_bytes_max = G723_MAX_BUFFER, .period_bytes_min = G723_PERIOD_BYTES, .period_bytes_max = G723_PERIOD_BYTES, .periods_min = PERIODS_MIN, @@ -140,7 +148,13 @@ static int snd_solo_pcm_open(struct snd_pcm_substream *ss) solo_pcm = kzalloc(sizeof(*solo_pcm), GFP_KERNEL); if (solo_pcm == NULL) - return -ENOMEM; + goto oom; + + solo_pcm->g723_buf = pci_alloc_consistent(solo_dev->pdev, + G723_PERIOD_BYTES, + &solo_pcm->g723_dma); + if (solo_pcm->g723_buf == NULL) + goto oom; spin_lock_init(&solo_pcm->lock); solo_pcm->solo_dev = solo_dev; @@ -149,6 +163,10 @@ static int snd_solo_pcm_open(struct snd_pcm_substream *ss) snd_pcm_substream_chip(ss) = solo_pcm; return 0; + +oom: + kfree(solo_pcm); + return -ENOMEM; } static int snd_solo_pcm_close(struct snd_pcm_substream *ss) @@ -156,6 +174,8 @@ static int snd_solo_pcm_close(struct snd_pcm_substream *ss) struct solo_snd_pcm *solo_pcm = snd_pcm_substream_chip(ss); snd_pcm_substream_chip(ss) = solo_pcm->solo_dev; + pci_free_consistent(solo_pcm->solo_dev->pdev, G723_PERIOD_BYTES, + solo_pcm->g723_buf, solo_pcm->g723_dma); kfree(solo_pcm); return 0; @@ -220,12 +240,11 @@ static int snd_solo_pcm_copy(struct snd_pcm_substream *ss, int channel, for (i = 0; i < (count / G723_FRAMES_PER_PAGE); i++) { int page = (pos / G723_FRAMES_PER_PAGE) + i; - err = solo_p2m_dma(solo_dev, SOLO_P2M_DMA_ID_G723E, 0, - solo_pcm->g723_buf, - SOLO_G723_EXT_ADDR(solo_dev) + - (page * G723_PERIOD_BLOCK) + - (ss->number * G723_PERIOD_BYTES), - G723_PERIOD_BYTES); + err = solo_p2m_dma_t(solo_dev, 0, solo_pcm->g723_dma, + SOLO_G723_EXT_ADDR(solo_dev) + + (page * G723_PERIOD_BLOCK) + + (ss->number * G723_PERIOD_BYTES), + G723_PERIOD_BYTES, 0, 0); if (err) return err; @@ -325,7 +344,7 @@ static int solo_snd_pcm_init(struct solo_dev *solo_dev) ret = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, snd_dma_continuous_data(GFP_KERNEL), - MAX_BUFFER, MAX_BUFFER); + G723_MAX_BUFFER, G723_MAX_BUFFER); if (ret < 0) return ret; @@ -368,6 +387,7 @@ int solo_g723_init(struct solo_dev *solo_dev) strcpy(card->mixername, "SOLO-6x10"); kctl = snd_solo_capture_volume; kctl.count = solo_dev->nr_chans; + ret = snd_ctl_add(card, snd_ctl_new1(&kctl, solo_dev)); if (ret < 0) return ret; @@ -393,8 +413,12 @@ snd_error: void solo_g723_exit(struct solo_dev *solo_dev) { + if (!solo_dev->snd_card) + return; + solo_reg_write(solo_dev, SOLO_AUDIO_CONTROL, 0); solo_irq_off(solo_dev, SOLO_IRQ_G723); snd_card_free(solo_dev->snd_card); + solo_dev->snd_card = NULL; } diff --git a/drivers/staging/media/solo6x10/gpio.c b/drivers/staging/media/solo6x10/solo6x10-gpio.c index 0925e6f33a99..73276dc92875 100644 --- a/drivers/staging/media/solo6x10/gpio.c +++ b/drivers/staging/media/solo6x10/solo6x10-gpio.c @@ -1,6 +1,11 @@ /* - * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com - * Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net> + * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com> + * + * Original author: + * Ben Collins <bcollins@ubuntu.com> + * + * Additional work by: + * John Brooks <john.brooks@bluecherry.net> * * 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 @@ -19,7 +24,9 @@ #include <linux/kernel.h> #include <linux/fs.h> -#include <asm/uaccess.h> +#include <linux/delay.h> +#include <linux/uaccess.h> + #include "solo6x10.h" static void solo_gpio_mode(struct solo_dev *solo_dev, diff --git a/drivers/staging/media/solo6x10/i2c.c b/drivers/staging/media/solo6x10/solo6x10-i2c.c index 398070a3d293..01aa417c9258 100644 --- a/drivers/staging/media/solo6x10/i2c.c +++ b/drivers/staging/media/solo6x10/solo6x10-i2c.c @@ -1,6 +1,11 @@ /* - * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com - * Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net> + * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com> + * + * Original author: + * Ben Collins <bcollins@ubuntu.com> + * + * Additional work by: + * John Brooks <john.brooks@bluecherry.net> * * 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 @@ -26,6 +31,7 @@ * thread context, ACK the interrupt, and move on. -- BenC */ #include <linux/kernel.h> + #include "solo6x10.h" u8 solo_i2c_readbyte(struct solo_dev *solo_dev, int id, u8 addr, u8 off) @@ -173,10 +179,9 @@ int solo_i2c_isr(struct solo_dev *solo_dev) u32 status = solo_reg_read(solo_dev, SOLO_IIC_CTRL); int ret = -EINVAL; - solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_IIC); - if (status & (SOLO_IIC_STATE_TRNS | SOLO_IIC_STATE_SIG_ERR) || - solo_dev->i2c_id < 0) { + if (CHK_FLAGS(status, SOLO_IIC_STATE_TRNS | SOLO_IIC_STATE_SIG_ERR) + || solo_dev->i2c_id < 0) { solo_i2c_stop(solo_dev); return -ENXIO; } @@ -239,7 +244,8 @@ static int solo_i2c_master_xfer(struct i2c_adapter *adap, timeout = HZ / 2; for (;;) { - prepare_to_wait(&solo_dev->i2c_wait, &wait, TASK_INTERRUPTIBLE); + prepare_to_wait(&solo_dev->i2c_wait, &wait, + TASK_INTERRUPTIBLE); if (solo_dev->i2c_state == IIC_STATE_STOP) break; @@ -267,7 +273,7 @@ static u32 solo_i2c_functionality(struct i2c_adapter *adap) return I2C_FUNC_I2C; } -static struct i2c_algorithm solo_i2c_algo = { +static const struct i2c_algorithm solo_i2c_algo = { .master_xfer = solo_i2c_master_xfer, .functionality = solo_i2c_functionality, }; @@ -288,7 +294,8 @@ int solo_i2c_init(struct solo_dev *solo_dev) for (i = 0; i < SOLO_I2C_ADAPTERS; i++) { struct i2c_adapter *adap = &solo_dev->i2c_adap[i]; - snprintf(adap->name, I2C_NAME_SIZE, "%s I2C %d", SOLO6X10_NAME, i); + snprintf(adap->name, I2C_NAME_SIZE, "%s I2C %d", + SOLO6X10_NAME, i); adap->algo = &solo_i2c_algo; adap->algo_data = solo_dev; adap->retries = 1; @@ -311,9 +318,6 @@ int solo_i2c_init(struct solo_dev *solo_dev) return ret; } - dev_info(&solo_dev->pdev->dev, "Enabled %d i2c adapters\n", - SOLO_I2C_ADAPTERS); - return 0; } diff --git a/drivers/staging/media/solo6x10/solo6x10-jpeg.h b/drivers/staging/media/solo6x10/solo6x10-jpeg.h index 50defec318cc..c5218ceeabca 100644 --- a/drivers/staging/media/solo6x10/solo6x10-jpeg.h +++ b/drivers/staging/media/solo6x10/solo6x10-jpeg.h @@ -1,6 +1,11 @@ /* - * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com - * Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net> + * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com> + * + * Original author: + * Ben Collins <bcollins@ubuntu.com> + * + * Additional work by: + * John Brooks <john.brooks@bluecherry.net> * * 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 @@ -20,7 +25,7 @@ #ifndef __SOLO6X10_JPEG_H #define __SOLO6X10_JPEG_H -static unsigned char jpeg_header[] = { +static const unsigned char jpeg_header[] = { 0xff, 0xd8, 0xff, 0xfe, 0x00, 0x0d, 0x42, 0x6c, 0x75, 0x65, 0x63, 0x68, 0x65, 0x72, 0x72, 0x79, 0x20, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x20, 0x16, @@ -102,4 +107,87 @@ static unsigned char jpeg_header[] = { /* This is the byte marker for the start of SOF0: 0xffc0 marker */ #define SOF0_START 575 +/* This is the byte marker for the start of the DQT */ +#define DQT_START 17 +#define DQT_LEN 138 +const unsigned char jpeg_dqt[4][DQT_LEN] = { + { + 0xff, 0xdb, 0x00, 0x43, 0x00, + 0x08, 0x06, 0x06, 0x07, 0x06, 0x05, 0x08, 0x07, + 0x07, 0x07, 0x09, 0x09, 0x08, 0x0a, 0x0c, 0x14, + 0x0d, 0x0c, 0x0b, 0x0b, 0x0c, 0x19, 0x12, 0x13, + 0x0f, 0x14, 0x1d, 0x1a, 0x1f, 0x1e, 0x1d, 0x1a, + 0x1c, 0x1c, 0x20, 0x24, 0x2e, 0x27, 0x20, 0x22, + 0x2c, 0x23, 0x1c, 0x1c, 0x28, 0x37, 0x29, 0x2c, + 0x30, 0x31, 0x34, 0x34, 0x34, 0x1f, 0x27, 0x39, + 0x3d, 0x38, 0x32, 0x3c, 0x2e, 0x33, 0x34, 0x32, + 0xff, 0xdb, 0x00, 0x43, 0x01, + 0x09, 0x09, 0x09, 0x0c, 0x0b, 0x0c, 0x18, 0x0d, + 0x0d, 0x18, 0x32, 0x21, 0x1c, 0x21, 0x32, 0x32, + 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, + 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, + 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, + 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, + 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, + 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32 + }, { + 0xff, 0xdb, 0x00, 0x43, 0x00, + 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, + 0xff, 0xdb, 0x00, 0x43, 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 + }, { + 0xff, 0xdb, 0x00, 0x43, 0x00, + 0x20, 0x16, 0x18, 0x1c, 0x18, 0x14, 0x20, 0x1c, + 0x1a, 0x1c, 0x24, 0x22, 0x20, 0x26, 0x30, 0x50, + 0x34, 0x30, 0x2c, 0x2c, 0x30, 0x62, 0x46, 0x4a, + 0x3a, 0x50, 0x74, 0x66, 0x7a, 0x78, 0x72, 0x66, + 0x70, 0x6e, 0x80, 0x90, 0xb8, 0x9c, 0x80, 0x88, + 0xae, 0x8a, 0x6e, 0x70, 0xa0, 0xda, 0xa2, 0xae, + 0xbe, 0xc4, 0xce, 0xd0, 0xce, 0x7c, 0x9a, 0xe2, + 0xf2, 0xe0, 0xc8, 0xf0, 0xb8, 0xca, 0xce, 0xc6, + 0xff, 0xdb, 0x00, 0x43, 0x01, + 0x22, 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 + }, { + 0xff, 0xdb, 0x00, 0x43, 0x00, + 0x30, 0x21, 0x24, 0x2a, 0x24, 0x1e, 0x30, 0x2a, + 0x27, 0x2a, 0x36, 0x33, 0x30, 0x39, 0x48, 0x78, + 0x4e, 0x48, 0x42, 0x42, 0x48, 0x93, 0x69, 0x6f, + 0x57, 0x78, 0xae, 0x99, 0xb7, 0xb4, 0xab, 0x99, + 0xa8, 0xa5, 0xc0, 0xd8, 0xff, 0xea, 0xc0, 0xcc, + 0xff, 0xcf, 0xa5, 0xa8, 0xf0, 0xff, 0xf3, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xba, 0xe7, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xdb, 0x00, 0x43, 0x01, + 0x33, 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 + } +}; + #endif /* __SOLO6X10_JPEG_H */ diff --git a/drivers/staging/media/solo6x10/solo6x10-offsets.h b/drivers/staging/media/solo6x10/solo6x10-offsets.h new file mode 100644 index 000000000000..f005dca501f1 --- /dev/null +++ b/drivers/staging/media/solo6x10/solo6x10-offsets.h @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com> + * + * Original author: + * Ben Collins <bcollins@ubuntu.com> + * + * Additional work by: + * John Brooks <john.brooks@bluecherry.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __SOLO6X10_OFFSETS_H +#define __SOLO6X10_OFFSETS_H + +#define SOLO_DISP_EXT_ADDR 0x00000000 +#define SOLO_DISP_EXT_SIZE 0x00480000 + +#define SOLO_EOSD_EXT_ADDR \ + (SOLO_DISP_EXT_ADDR + SOLO_DISP_EXT_SIZE) +#define SOLO_EOSD_EXT_SIZE(__solo) \ + (__solo->type == SOLO_DEV_6010 ? 0x10000 : 0x20000) +#define SOLO_EOSD_EXT_SIZE_MAX 0x20000 +#define SOLO_EOSD_EXT_AREA(__solo) \ + (SOLO_EOSD_EXT_SIZE(__solo) * 32) + +#define SOLO_MOTION_EXT_ADDR(__solo) \ + (SOLO_EOSD_EXT_ADDR + SOLO_EOSD_EXT_AREA(__solo)) +#define SOLO_MOTION_EXT_SIZE 0x00080000 + +#define SOLO_G723_EXT_ADDR(__solo) \ + (SOLO_MOTION_EXT_ADDR(__solo) + SOLO_MOTION_EXT_SIZE) +#define SOLO_G723_EXT_SIZE 0x00010000 + +#define SOLO_CAP_EXT_ADDR(__solo) \ + (SOLO_G723_EXT_ADDR(__solo) + SOLO_G723_EXT_SIZE) + +/* 18 is the maximum number of pages required for PAL@D1, the largest frame + * possible */ +#define SOLO_CAP_PAGE_SIZE (18 << 16) + +/* Always allow the encoder enough for 16 channels, even if we have less. The + * exception is if we have card with only 32Megs of memory. */ +#define SOLO_CAP_EXT_SIZE(__solo) \ + ((((__solo->sdram_size <= (32 << 20)) ? 4 : 16) + 1) \ + * SOLO_CAP_PAGE_SIZE) + +#define SOLO_EREF_EXT_ADDR(__solo) \ + (SOLO_CAP_EXT_ADDR(__solo) + SOLO_CAP_EXT_SIZE(__solo)) +#define SOLO_EREF_EXT_SIZE 0x00140000 +#define SOLO_EREF_EXT_AREA(__solo) \ + (SOLO_EREF_EXT_SIZE * __solo->nr_chans * 2) + +#define __SOLO_JPEG_MIN_SIZE(__solo) (__solo->nr_chans * 0x00080000) + +#define SOLO_MP4E_EXT_ADDR(__solo) \ + (SOLO_EREF_EXT_ADDR(__solo) + SOLO_EREF_EXT_AREA(__solo)) +#define SOLO_MP4E_EXT_SIZE(__solo) \ + max((__solo->nr_chans * 0x00080000), \ + min(((__solo->sdram_size - SOLO_MP4E_EXT_ADDR(__solo)) - \ + __SOLO_JPEG_MIN_SIZE(__solo)), 0x00ff0000)) + +#define __SOLO_JPEG_MIN_SIZE(__solo) (__solo->nr_chans * 0x00080000) +#define SOLO_JPEG_EXT_ADDR(__solo) \ + (SOLO_MP4E_EXT_ADDR(__solo) + SOLO_MP4E_EXT_SIZE(__solo)) +#define SOLO_JPEG_EXT_SIZE(__solo) \ + max(__SOLO_JPEG_MIN_SIZE(__solo), \ + min((__solo->sdram_size - SOLO_JPEG_EXT_ADDR(__solo)), 0x00ff0000)) + +#define SOLO_SDRAM_END(__solo) \ + (SOLO_JPEG_EXT_ADDR(__solo) + SOLO_JPEG_EXT_SIZE(__solo)) + +#endif /* __SOLO6X10_OFFSETS_H */ diff --git a/drivers/staging/media/solo6x10/solo6x10-p2m.c b/drivers/staging/media/solo6x10/solo6x10-p2m.c new file mode 100644 index 000000000000..333594189b81 --- /dev/null +++ b/drivers/staging/media/solo6x10/solo6x10-p2m.c @@ -0,0 +1,333 @@ +/* + * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com> + * + * Original author: + * Ben Collins <bcollins@ubuntu.com> + * + * Additional work by: + * John Brooks <john.brooks@bluecherry.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> + +#include "solo6x10.h" + +static int multi_p2m; +module_param(multi_p2m, uint, 0644); +MODULE_PARM_DESC(multi_p2m, + "Use multiple P2M DMA channels (default: no, 6010-only)"); + +static int desc_mode; +module_param(desc_mode, uint, 0644); +MODULE_PARM_DESC(desc_mode, + "Allow use of descriptor mode DMA (default: no, 6010-only)"); + +int solo_p2m_dma(struct solo_dev *solo_dev, int wr, + void *sys_addr, u32 ext_addr, u32 size, + int repeat, u32 ext_size) +{ + dma_addr_t dma_addr; + int ret; + + if (WARN_ON_ONCE((unsigned long)sys_addr & 0x03)) + return -EINVAL; + if (WARN_ON_ONCE(!size)) + return -EINVAL; + + dma_addr = pci_map_single(solo_dev->pdev, sys_addr, size, + wr ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); + if (pci_dma_mapping_error(solo_dev->pdev, dma_addr)) + return -ENOMEM; + + ret = solo_p2m_dma_t(solo_dev, wr, dma_addr, ext_addr, size, + repeat, ext_size); + + pci_unmap_single(solo_dev->pdev, dma_addr, size, + wr ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); + + return ret; +} + +/* Mutex must be held for p2m_id before calling this!! */ +int solo_p2m_dma_desc(struct solo_dev *solo_dev, + struct solo_p2m_desc *desc, dma_addr_t desc_dma, + int desc_cnt) +{ + struct solo_p2m_dev *p2m_dev; + unsigned int timeout; + unsigned int config = 0; + int ret = 0; + int p2m_id = 0; + + /* Get next ID. According to Softlogic, 6110 has problems on !=0 P2M */ + if (solo_dev->type != SOLO_DEV_6110 && multi_p2m) { + p2m_id = atomic_inc_return(&solo_dev->p2m_count) % SOLO_NR_P2M; + if (p2m_id < 0) + p2m_id = -p2m_id; + } + + p2m_dev = &solo_dev->p2m_dev[p2m_id]; + + if (mutex_lock_interruptible(&p2m_dev->mutex)) + return -EINTR; + + INIT_COMPLETION(p2m_dev->completion); + p2m_dev->error = 0; + + if (desc_cnt > 1 && solo_dev->type != SOLO_DEV_6110 && desc_mode) { + /* For 6010 with more than one desc, we can do a one-shot */ + p2m_dev->desc_count = p2m_dev->desc_idx = 0; + config = solo_reg_read(solo_dev, SOLO_P2M_CONFIG(p2m_id)); + + solo_reg_write(solo_dev, SOLO_P2M_DES_ADR(p2m_id), desc_dma); + solo_reg_write(solo_dev, SOLO_P2M_DESC_ID(p2m_id), desc_cnt); + solo_reg_write(solo_dev, SOLO_P2M_CONFIG(p2m_id), config | + SOLO_P2M_DESC_MODE); + } else { + /* For single descriptors and 6110, we need to run each desc */ + p2m_dev->desc_count = desc_cnt; + p2m_dev->desc_idx = 1; + p2m_dev->descs = desc; + + solo_reg_write(solo_dev, SOLO_P2M_TAR_ADR(p2m_id), + desc[1].dma_addr); + solo_reg_write(solo_dev, SOLO_P2M_EXT_ADR(p2m_id), + desc[1].ext_addr); + solo_reg_write(solo_dev, SOLO_P2M_EXT_CFG(p2m_id), + desc[1].cfg); + solo_reg_write(solo_dev, SOLO_P2M_CONTROL(p2m_id), + desc[1].ctrl); + } + + timeout = wait_for_completion_timeout(&p2m_dev->completion, + solo_dev->p2m_jiffies); + + if (WARN_ON_ONCE(p2m_dev->error)) + ret = -EIO; + else if (timeout == 0) { + solo_dev->p2m_timeouts++; + ret = -EAGAIN; + } + + solo_reg_write(solo_dev, SOLO_P2M_CONTROL(p2m_id), 0); + + /* Don't write here for the no_desc_mode case, because config is 0. + * We can't test no_desc_mode again, it might race. */ + if (desc_cnt > 1 && solo_dev->type != SOLO_DEV_6110 && config) + solo_reg_write(solo_dev, SOLO_P2M_CONFIG(p2m_id), config); + + mutex_unlock(&p2m_dev->mutex); + + return ret; +} + +void solo_p2m_fill_desc(struct solo_p2m_desc *desc, int wr, + dma_addr_t dma_addr, u32 ext_addr, u32 size, + int repeat, u32 ext_size) +{ + WARN_ON_ONCE(dma_addr & 0x03); + WARN_ON_ONCE(!size); + + desc->cfg = SOLO_P2M_COPY_SIZE(size >> 2); + desc->ctrl = SOLO_P2M_BURST_SIZE(SOLO_P2M_BURST_256) | + (wr ? SOLO_P2M_WRITE : 0) | SOLO_P2M_TRANS_ON; + + if (repeat) { + desc->cfg |= SOLO_P2M_EXT_INC(ext_size >> 2); + desc->ctrl |= SOLO_P2M_PCI_INC(size >> 2) | + SOLO_P2M_REPEAT(repeat); + } + + desc->dma_addr = dma_addr; + desc->ext_addr = ext_addr; +} + +int solo_p2m_dma_t(struct solo_dev *solo_dev, int wr, + dma_addr_t dma_addr, u32 ext_addr, u32 size, + int repeat, u32 ext_size) +{ + struct solo_p2m_desc desc[2]; + + solo_p2m_fill_desc(&desc[1], wr, dma_addr, ext_addr, size, repeat, + ext_size); + + /* No need for desc_dma since we know it is a single-shot */ + return solo_p2m_dma_desc(solo_dev, desc, 0, 1); +} + +void solo_p2m_isr(struct solo_dev *solo_dev, int id) +{ + struct solo_p2m_dev *p2m_dev = &solo_dev->p2m_dev[id]; + struct solo_p2m_desc *desc; + + if (p2m_dev->desc_count <= p2m_dev->desc_idx) { + complete(&p2m_dev->completion); + return; + } + + /* Setup next descriptor */ + p2m_dev->desc_idx++; + desc = &p2m_dev->descs[p2m_dev->desc_idx]; + + solo_reg_write(solo_dev, SOLO_P2M_CONTROL(id), 0); + solo_reg_write(solo_dev, SOLO_P2M_TAR_ADR(id), desc->dma_addr); + solo_reg_write(solo_dev, SOLO_P2M_EXT_ADR(id), desc->ext_addr); + solo_reg_write(solo_dev, SOLO_P2M_EXT_CFG(id), desc->cfg); + solo_reg_write(solo_dev, SOLO_P2M_CONTROL(id), desc->ctrl); +} + +void solo_p2m_error_isr(struct solo_dev *solo_dev) +{ + unsigned int err = solo_reg_read(solo_dev, SOLO_PCI_ERR); + struct solo_p2m_dev *p2m_dev; + int i; + + if (!(err & (SOLO_PCI_ERR_P2M | SOLO_PCI_ERR_P2M_DESC))) + return; + + for (i = 0; i < SOLO_NR_P2M; i++) { + p2m_dev = &solo_dev->p2m_dev[i]; + p2m_dev->error = 1; + solo_reg_write(solo_dev, SOLO_P2M_CONTROL(i), 0); + complete(&p2m_dev->completion); + } +} + +void solo_p2m_exit(struct solo_dev *solo_dev) +{ + int i; + + for (i = 0; i < SOLO_NR_P2M; i++) + solo_irq_off(solo_dev, SOLO_IRQ_P2M(i)); +} + +static int solo_p2m_test(struct solo_dev *solo_dev, int base, int size) +{ + u32 *wr_buf; + u32 *rd_buf; + int i; + int ret = -EIO; + int order = get_order(size); + + wr_buf = (u32 *)__get_free_pages(GFP_KERNEL, order); + if (wr_buf == NULL) + return -1; + + rd_buf = (u32 *)__get_free_pages(GFP_KERNEL, order); + if (rd_buf == NULL) { + free_pages((unsigned long)wr_buf, order); + return -1; + } + + for (i = 0; i < (size >> 3); i++) + *(wr_buf + i) = (i << 16) | (i + 1); + + for (i = (size >> 3); i < (size >> 2); i++) + *(wr_buf + i) = ~((i << 16) | (i + 1)); + + memset(rd_buf, 0x55, size); + + if (solo_p2m_dma(solo_dev, 1, wr_buf, base, size, 0, 0)) + goto test_fail; + + if (solo_p2m_dma(solo_dev, 0, rd_buf, base, size, 0, 0)) + goto test_fail; + + for (i = 0; i < (size >> 2); i++) { + if (*(wr_buf + i) != *(rd_buf + i)) + goto test_fail; + } + + ret = 0; + +test_fail: + free_pages((unsigned long)wr_buf, order); + free_pages((unsigned long)rd_buf, order); + + return ret; +} + +int solo_p2m_init(struct solo_dev *solo_dev) +{ + struct solo_p2m_dev *p2m_dev; + int i; + + for (i = 0; i < SOLO_NR_P2M; i++) { + p2m_dev = &solo_dev->p2m_dev[i]; + + mutex_init(&p2m_dev->mutex); + init_completion(&p2m_dev->completion); + + solo_reg_write(solo_dev, SOLO_P2M_CONTROL(i), 0); + solo_reg_write(solo_dev, SOLO_P2M_CONFIG(i), + SOLO_P2M_CSC_16BIT_565 | + SOLO_P2M_DESC_INTR_OPT | + SOLO_P2M_DMA_INTERVAL(0) | + SOLO_P2M_PCI_MASTER_MODE); + solo_irq_on(solo_dev, SOLO_IRQ_P2M(i)); + } + + /* Find correct SDRAM size */ + for (solo_dev->sdram_size = 0, i = 2; i >= 0; i--) { + solo_reg_write(solo_dev, SOLO_DMA_CTRL, + SOLO_DMA_CTRL_REFRESH_CYCLE(1) | + SOLO_DMA_CTRL_SDRAM_SIZE(i) | + SOLO_DMA_CTRL_SDRAM_CLK_INVERT | + SOLO_DMA_CTRL_READ_CLK_SELECT | + SOLO_DMA_CTRL_LATENCY(1)); + + solo_reg_write(solo_dev, SOLO_SYS_CFG, solo_dev->sys_config | + SOLO_SYS_CFG_RESET); + solo_reg_write(solo_dev, SOLO_SYS_CFG, solo_dev->sys_config); + + switch (i) { + case 2: + if (solo_p2m_test(solo_dev, 0x07ff0000, 0x00010000) || + solo_p2m_test(solo_dev, 0x05ff0000, 0x00010000)) + continue; + break; + + case 1: + if (solo_p2m_test(solo_dev, 0x03ff0000, 0x00010000)) + continue; + break; + + default: + if (solo_p2m_test(solo_dev, 0x01ff0000, 0x00010000)) + continue; + } + + solo_dev->sdram_size = (32 << 20) << i; + break; + } + + if (!solo_dev->sdram_size) { + dev_err(&solo_dev->pdev->dev, "Error detecting SDRAM size\n"); + return -EIO; + } + + if (SOLO_SDRAM_END(solo_dev) > solo_dev->sdram_size) { + dev_err(&solo_dev->pdev->dev, + "SDRAM is not large enough (%u < %u)\n", + solo_dev->sdram_size, SOLO_SDRAM_END(solo_dev)); + return -EIO; + } + + return 0; +} diff --git a/drivers/staging/media/solo6x10/registers.h b/drivers/staging/media/solo6x10/solo6x10-regs.h index aca544472c93..428f6c951180 100644 --- a/drivers/staging/media/solo6x10/registers.h +++ b/drivers/staging/media/solo6x10/solo6x10-regs.h @@ -1,6 +1,11 @@ /* - * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com - * Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net> + * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com> + * + * Original author: + * Ben Collins <bcollins@ubuntu.com> + * + * Additional work by: + * John Brooks <john.brooks@bluecherry.net> * * 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 @@ -20,20 +25,20 @@ #ifndef __SOLO6X10_REGISTERS_H #define __SOLO6X10_REGISTERS_H -#include "offsets.h" +#include "solo6x10-offsets.h" -/* Global 6X10 system configuration */ +/* Global 6010 system configuration */ #define SOLO_SYS_CFG 0x0000 -#define SOLO6010_SYS_CFG_FOUT_EN 0x00000001 /* 6010 only */ -#define SOLO6010_SYS_CFG_PLL_BYPASS 0x00000002 /* 6010 only */ -#define SOLO6010_SYS_CFG_PLL_PWDN 0x00000004 /* 6010 only */ -#define SOLO6010_SYS_CFG_OUTDIV(__n) (((__n) & 0x003) << 3) /* 6010 only */ -#define SOLO6010_SYS_CFG_FEEDBACKDIV(__n) (((__n) & 0x1ff) << 5) /* 6010 only */ -#define SOLO6010_SYS_CFG_INPUTDIV(__n) (((__n) & 0x01f) << 14) /* 6010 only */ +#define SOLO_SYS_CFG_FOUT_EN 0x00000001 +#define SOLO_SYS_CFG_PLL_BYPASS 0x00000002 +#define SOLO_SYS_CFG_PLL_PWDN 0x00000004 +#define SOLO_SYS_CFG_OUTDIV(__n) (((__n) & 0x003) << 3) +#define SOLO_SYS_CFG_FEEDBACKDIV(__n) (((__n) & 0x1ff) << 5) +#define SOLO_SYS_CFG_INPUTDIV(__n) (((__n) & 0x01f) << 14) #define SOLO_SYS_CFG_CLOCK_DIV 0x00080000 #define SOLO_SYS_CFG_NCLK_DELAY(__n) (((__n) & 0x003) << 24) #define SOLO_SYS_CFG_PCLK_DELAY(__n) (((__n) & 0x00f) << 26) -#define SOLO_SYS_CFG_SDRAM64BIT 0x40000000 /* 6110: must be set */ +#define SOLO_SYS_CFG_SDRAM64BIT 0x40000000 #define SOLO_SYS_CFG_RESET 0x80000000 #define SOLO_DMA_CTRL 0x0004 @@ -45,7 +50,9 @@ #define SOLO_DMA_CTRL_READ_DATA_SELECT (1<<3) #define SOLO_DMA_CTRL_READ_CLK_SELECT (1<<2) #define SOLO_DMA_CTRL_LATENCY(n) ((n)<<0) -#define SOLO_DMA_CTRL1 0x0008 + +/* Some things we set in this are undocumented. Why Softlogic?!?! */ +#define SOLO_DMA_CTRL1 0x0008 #define SOLO_SYS_VCLK 0x000C #define SOLO_VCLK_INVERT (1<<22) @@ -61,7 +68,7 @@ #define SOLO_VCLK_VIN0001_DELAY(n) ((n)<<0) #define SOLO_IRQ_STAT 0x0010 -#define SOLO_IRQ_ENABLE 0x0014 +#define SOLO_IRQ_MASK 0x0014 #define SOLO_IRQ_P2M(n) (1<<((n)+17)) #define SOLO_IRQ_GPIO (1<<16) #define SOLO_IRQ_VIDEO_LOSS (1<<15) @@ -82,22 +89,7 @@ #define SOLO_CHIP_OPTION 0x001C #define SOLO_CHIP_ID_MASK 0x00000007 -#define SOLO6110_PLL_CONFIG 0x0020 -#define SOLO6110_PLL_RANGE_BYPASS (0 << 20) -#define SOLO6110_PLL_RANGE_5_10MHZ (1 << 20) -#define SOLO6110_PLL_RANGE_8_16MHZ (2 << 20) -#define SOLO6110_PLL_RANGE_13_26MHZ (3 << 20) -#define SOLO6110_PLL_RANGE_21_42MHZ (4 << 20) -#define SOLO6110_PLL_RANGE_34_68MHZ (5 << 20) -#define SOLO6110_PLL_RANGE_54_108MHZ (6 << 20) -#define SOLO6110_PLL_RANGE_88_200MHZ (7 << 20) -#define SOLO6110_PLL_DIVR(x) (((x) - 1) << 15) -#define SOLO6110_PLL_DIVQ_EXP(x) ((x) << 12) -#define SOLO6110_PLL_DIVF(x) (((x) - 1) << 4) -#define SOLO6110_PLL_RESET (1 << 3) -#define SOLO6110_PLL_BYPASS (1 << 2) -#define SOLO6110_PLL_FSEN (1 << 1) -#define SOLO6110_PLL_FB (1 << 0) +#define SOLO_PLL_CONFIG 0x0020 /* 6110 Only */ #define SOLO_EEPROM_CTRL 0x0060 #define SOLO_EEPROM_ACCESS_EN (1<<7) @@ -105,7 +97,7 @@ #define SOLO_EEPROM_CLK (1<<2) #define SOLO_EEPROM_DO (1<<1) #define SOLO_EEPROM_DI (1<<0) -#define SOLO_EEPROM_ENABLE (EEPROM_ACCESS_EN | EEPROM_CS) +#define SOLO_EEPROM_ENABLE (SOLO_EEPROM_ACCESS_EN | SOLO_EEPROM_CS) #define SOLO_PCI_ERR 0x0070 #define SOLO_PCI_ERR_FATAL 0x00000001 @@ -274,8 +266,8 @@ #define SOLO_VO_FI_CHANGE (1<<20) #define SOLO_VO_USER_COLOR_SET_VSYNC (1<<19) #define SOLO_VO_USER_COLOR_SET_HSYNC (1<<18) -#define SOLO_VO_USER_COLOR_SET_NAV (1<<17) -#define SOLO_VO_USER_COLOR_SET_NAH (1<<16) +#define SOLO_VO_USER_COLOR_SET_NAH (1<<17) +#define SOLO_VO_USER_COLOR_SET_NAV (1<<16) #define SOLO_VO_NA_COLOR_Y(Y) ((Y)<<8) #define SOLO_VO_NA_COLOR_CB(CB) (((CB)/16)<<4) #define SOLO_VO_NA_COLOR_CR(CR) (((CR)/16)<<0) @@ -401,12 +393,13 @@ #define SOLO_VE_BLOCK_BASE(n) ((n)<<0) #define SOLO_VE_CFG1 0x0614 -#define SOLO6110_VE_MPEG_SIZE_H(n) ((n)<<28) /* 6110 only */ -#define SOLO6010_VE_BYTE_ALIGN(n) ((n)<<24) /* 6010 only */ -#define SOLO6110_VE_JPEG_SIZE_H(n) ((n)<<20) /* 6110 only */ +#define SOLO_VE_BYTE_ALIGN(n) ((n)<<24) #define SOLO_VE_INSERT_INDEX (1<<18) #define SOLO_VE_MOTION_MODE(n) ((n)<<16) #define SOLO_VE_MOTION_BASE(n) ((n)<<0) +#define SOLO_VE_MPEG_SIZE_H(n) ((n)<<28) /* 6110 Only */ +#define SOLO_VE_JPEG_SIZE_H(n) ((n)<<20) /* 6110 Only */ +#define SOLO_VE_INSERT_INDEX_JPEG (1<<19) /* 6110 Only */ #define SOLO_VE_WMRK_POLY 0x061C #define SOLO_VE_VMRK_INIT_KEY 0x0620 @@ -420,6 +413,7 @@ #define SOLO_COMP_TIME_INC(n) ((n)<<25) #define SOLO_COMP_TIME_WIDTH(n) ((n)<<21) #define SOLO_DCT_INTERVAL(n) ((n)<<16) +#define SOLO_VE_COMPT_MOT 0x0634 /* 6110 Only */ #define SOLO_VE_STATE(n) (0x0640+((n)*4)) @@ -428,14 +422,21 @@ #define SOLO_VE_JPEG_QP_CH_H 0x0678 #define SOLO_VE_JPEG_CFG 0x067C #define SOLO_VE_JPEG_CTRL 0x0680 - +#define SOLO_VE_CODE_ENCRYPT 0x0684 /* 6110 Only */ +#define SOLO_VE_JPEG_CFG1 0x0688 /* 6110 Only */ +#define SOLO_VE_WMRK_ENABLE 0x068C /* 6110 Only */ #define SOLO_VE_OSD_CH 0x0690 #define SOLO_VE_OSD_BASE 0x0694 #define SOLO_VE_OSD_CLR 0x0698 #define SOLO_VE_OSD_OPT 0x069C +#define SOLO_VE_OSD_V_DOUBLE (1<<16) /* 6110 Only */ +#define SOLO_VE_OSD_H_SHADOW (1<<15) +#define SOLO_VE_OSD_V_SHADOW (1<<14) +#define SOLO_VE_OSD_H_OFFSET(n) ((n & 0x7f)<<7) +#define SOLO_VE_OSD_V_OFFSET(n) (n & 0x7f) #define SOLO_VE_CH_INTL(ch) (0x0700+((ch)*4)) -#define SOLO6010_VE_CH_MOT(ch) (0x0740+((ch)*4)) /* 6010 only */ +#define SOLO_VE_CH_MOT(ch) (0x0740+((ch)*4)) #define SOLO_VE_CH_QP(ch) (0x0780+((ch)*4)) #define SOLO_VE_CH_QP_E(ch) (0x07C0+((ch)*4)) #define SOLO_VE_CH_GOP(ch) (0x0800+((ch)*4)) @@ -447,7 +448,7 @@ #define SOLO_VE_JPEG_QUE(n) (0x0A04+((n)*8)) #define SOLO_VD_CFG0 0x0900 -#define SOLO6010_VD_CFG_NO_WRITE_NO_WINDOW (1<<24) /* 6010 only */ +#define SOLO_VD_CFG_NO_WRITE_NO_WINDOW (1<<24) #define SOLO_VD_CFG_BUSY_WIAT_CODE (1<<23) #define SOLO_VD_CFG_BUSY_WIAT_REF (1<<22) #define SOLO_VD_CFG_BUSY_WIAT_RES (1<<21) @@ -599,9 +600,9 @@ #define SOLO_UART_RX_DATA_POP (1<<8) #define SOLO_TIMER_CLOCK_NUM 0x0be0 -#define SOLO_TIMER_WATCHDOG 0x0be4 #define SOLO_TIMER_USEC 0x0be8 #define SOLO_TIMER_SEC 0x0bec +#define SOLO_TIMER_USEC_LSB 0x0d20 /* 6110 Only */ #define SOLO_AUDIO_CONTROL 0x0D00 #define SOLO_AUDIO_ENABLE (1<<31) @@ -629,9 +630,10 @@ #define SOLO_AUDIO_EVOL(ch, value) ((value)<<((ch)%10)) #define SOLO_AUDIO_STA 0x0D14 - -#define SOLO_WATCHDOG 0x0BE4 -#define WATCHDOG_STAT(status) (status<<8) -#define WATCHDOG_TIME(sec) (sec&0xff) +/* + * Watchdog configuration + */ +#define SOLO_WATCHDOG 0x0be4 +#define SOLO_WATCHDOG_SET(status, sec) (status << 8 | (sec & 0xff)) #endif /* __SOLO6X10_REGISTERS_H */ diff --git a/drivers/staging/media/solo6x10/tw28.c b/drivers/staging/media/solo6x10/solo6x10-tw28.c index db56b42c56c6..ad00e2b60323 100644 --- a/drivers/staging/media/solo6x10/tw28.c +++ b/drivers/staging/media/solo6x10/solo6x10-tw28.c @@ -1,6 +1,11 @@ /* - * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com - * Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net> + * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com> + * + * Original author: + * Ben Collins <bcollins@ubuntu.com> + * + * Additional work by: + * John Brooks <john.brooks@bluecherry.net> * * 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,12 +23,12 @@ */ #include <linux/kernel.h> +#include <linux/delay.h> + #include "solo6x10.h" -#include "tw28.h" +#include "solo6x10-tw28.h" -/* XXX: Some of these values are masked into an 8-bit regs, and shifted - * around for other 8-bit regs. What are the magic bits in these values? */ -#define DEFAULT_HDELAY_NTSC (32 - 4) +#define DEFAULT_HDELAY_NTSC (32 - 8) #define DEFAULT_HACTIVE_NTSC (720 + 16) #define DEFAULT_VDELAY_NTSC (7 - 2) #define DEFAULT_VACTIVE_NTSC (240 + 4) @@ -33,15 +38,16 @@ #define DEFAULT_VDELAY_PAL (6) #define DEFAULT_VACTIVE_PAL (312-DEFAULT_VDELAY_PAL) -static u8 tbl_tw2864_template[] = { - 0x00, 0x00, 0x80, 0x10, 0x80, 0x80, 0x00, 0x02, /* 0x00 */ - 0x12, 0xf5, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0x00, 0x80, 0x10, 0x80, 0x80, 0x00, 0x02, /* 0x10 */ - 0x12, 0xf5, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0x00, 0x80, 0x10, 0x80, 0x80, 0x00, 0x02, /* 0x20 */ - 0x12, 0xf5, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0x00, 0x80, 0x10, 0x80, 0x80, 0x00, 0x02, /* 0x30 */ - 0x12, 0xf5, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, + +static const u8 tbl_tw2864_ntsc_template[] = { + 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x00 */ + 0x12, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x10 */ + 0x12, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x20 */ + 0x12, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x30 */ + 0x12, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */ @@ -61,14 +67,49 @@ static u8 tbl_tw2864_template[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */ 0x00, 0x00, 0x55, 0x00, 0xb1, 0xe4, 0x40, 0x00, 0x77, 0x77, 0x01, 0x13, 0x57, 0x9b, 0xdf, 0x20, /* 0xd0 */ - 0x64, 0xa8, 0xec, 0xd1, 0x0f, 0x11, 0x11, 0x81, - 0x10, 0xe0, 0xbb, 0xbb, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */ + 0x64, 0xa8, 0xec, 0xc1, 0x0f, 0x11, 0x11, 0x81, + 0x00, 0xe0, 0xbb, 0xbb, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */ 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, 0x83, 0xb5, 0x09, 0x78, 0x85, 0x00, 0x01, 0x20, /* 0xf0 */ 0x64, 0x11, 0x40, 0xaf, 0xff, 0x00, 0x00, 0x00, }; -static u8 tbl_tw2865_ntsc_template[] = { +static const u8 tbl_tw2864_pal_template[] = { + 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x00 */ + 0x18, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x01, 0x7f, + 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x10 */ + 0x18, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x01, 0x7f, + 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x20 */ + 0x18, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x01, 0x7f, + 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x30 */ + 0x18, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x01, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA3, 0x00, + 0x00, 0x02, 0x00, 0xcc, 0x00, 0x80, 0x44, 0x50, /* 0x80 */ + 0x22, 0x01, 0xd8, 0xbc, 0xb8, 0x44, 0x38, 0x00, + 0x00, 0x78, 0x72, 0x3e, 0x14, 0xa5, 0xe4, 0x05, /* 0x90 */ + 0x00, 0x28, 0x44, 0x44, 0xa0, 0x90, 0x5a, 0x01, + 0x0a, 0x0a, 0x0a, 0x0a, 0x1a, 0x1a, 0x1a, 0x1a, /* 0xa0 */ + 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, 0x44, + 0x44, 0x0a, 0x00, 0xff, 0xef, 0xef, 0xef, 0xef, /* 0xb0 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */ + 0x00, 0x00, 0x55, 0x00, 0xb1, 0xe4, 0x40, 0x00, + 0x77, 0x77, 0x01, 0x13, 0x57, 0x9b, 0xdf, 0x20, /* 0xd0 */ + 0x64, 0xa8, 0xec, 0xc1, 0x0f, 0x11, 0x11, 0x81, + 0x00, 0xe0, 0xbb, 0xbb, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */ + 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, + 0x83, 0xb5, 0x09, 0x00, 0xa0, 0x00, 0x01, 0x20, /* 0xf0 */ + 0x64, 0x11, 0x40, 0xaf, 0xff, 0x00, 0x00, 0x00, +}; + +static const u8 tbl_tw2865_ntsc_template[] = { 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x00 */ 0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x10 */ @@ -103,7 +144,7 @@ static u8 tbl_tw2865_ntsc_template[] = { 0x64, 0x51, 0x40, 0xaf, 0xFF, 0xF0, 0x00, 0xC0, }; -static u8 tbl_tw2865_pal_template[] = { +static const u8 tbl_tw2865_pal_template[] = { 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x00 */ 0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f, 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x10 */ @@ -180,8 +221,8 @@ static void tw_write_and_verify(struct solo_dev *solo_dev, u8 addr, u8 off, msleep_interruptible(1); } -/* printk("solo6x10/tw28: Error writing register: %02x->%02x [%02x]\n", - addr, off, val); */ +/* printk("solo6x10/tw28: Error writing register: %02x->%02x [%02x]\n", */ +/* addr, off, val); */ } static int tw2865_setup(struct solo_dev *solo_dev, u8 dev_addr) @@ -216,16 +257,17 @@ static int tw2865_setup(struct solo_dev *solo_dev, u8 dev_addr) for (i = 0; i < 0xff; i++) { /* Skip read only registers */ - if (i >= 0xb8 && i <= 0xc1) - continue; - if ((i & ~0x30) == 0x00 || - (i & ~0x30) == 0x0c || - (i & ~0x30) == 0x0d) - continue; - if (i >= 0xc4 && i <= 0xc7) + switch (i) { + case 0xb8 ... 0xc1: + case 0xc4 ... 0xc7: + case 0xfd: continue; - if (i == 0xfd) + } + switch (i & ~0x30) { + case 0x00: + case 0x0c ... 0x0d: continue; + } tw_write_and_verify(solo_dev, dev_addr, i, tbl_tw2865_common[i]); @@ -236,11 +278,15 @@ static int tw2865_setup(struct solo_dev *solo_dev, u8 dev_addr) static int tw2864_setup(struct solo_dev *solo_dev, u8 dev_addr) { - u8 tbl_tw2864_common[sizeof(tbl_tw2864_template)]; + u8 tbl_tw2864_common[256]; int i; - memcpy(tbl_tw2864_common, tbl_tw2864_template, - sizeof(tbl_tw2864_common)); + if (solo_dev->video_type == SOLO_VO_FMT_TYPE_PAL) + memcpy(tbl_tw2864_common, tbl_tw2864_pal_template, + sizeof(tbl_tw2864_common)); + else + memcpy(tbl_tw2864_common, tbl_tw2864_ntsc_template, + sizeof(tbl_tw2864_common)); if (solo_dev->tw2865 == 0) { /* IRQ Mode */ @@ -285,33 +331,19 @@ static int tw2864_setup(struct solo_dev *solo_dev, u8 dev_addr) } } - /* NTSC or PAL */ - if (solo_dev->video_type == SOLO_VO_FMT_TYPE_PAL) { - for (i = 0; i < 4; i++) { - tbl_tw2864_common[0x07 | (i << 4)] |= 0x10; - tbl_tw2864_common[0x08 | (i << 4)] |= 0x06; - tbl_tw2864_common[0x0a | (i << 4)] |= 0x08; - tbl_tw2864_common[0x0b | (i << 4)] |= 0x13; - tbl_tw2864_common[0x0e | (i << 4)] |= 0x01; - } - tbl_tw2864_common[0x9d] = 0x90; - tbl_tw2864_common[0xf3] = 0x00; - tbl_tw2864_common[0xf4] = 0xa0; - } - for (i = 0; i < 0xff; i++) { /* Skip read only registers */ - if (i >= 0xb8 && i <= 0xc1) + switch (i) { + case 0xb8 ... 0xc1: + case 0xfd: continue; - if ((i & ~0x30) == 0x00 || - (i & ~0x30) == 0x0c || - (i & ~0x30) == 0x0d) - continue; - if (i == 0x74 || i == 0x77 || i == 0x78 || - i == 0x79 || i == 0x7a) - continue; - if (i == 0xfd) + } + switch (i & ~0x30) { + case 0x00: + case 0x0c: + case 0x0d: continue; + } tw_write_and_verify(solo_dev, dev_addr, i, tbl_tw2864_common[i]); @@ -544,8 +576,10 @@ int solo_tw28_init(struct solo_dev *solo_dev) int i; u8 value; - /* Detect techwell chip type */ - for (i = 0; i < TW_NUM_CHIP; i++) { + solo_dev->tw28_cnt = 0; + + /* Detect techwell chip type(s) */ + for (i = 0; i < solo_dev->nr_chans / 4; i++) { value = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, TW_CHIP_OFFSET_ADDR(i), 0xFF); @@ -560,7 +594,8 @@ int solo_tw28_init(struct solo_dev *solo_dev) break; default: value = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, - TW_CHIP_OFFSET_ADDR(i), 0x59); + TW_CHIP_OFFSET_ADDR(i), + 0x59); if ((value >> 3) == 0x04) { solo_dev->tw2815 |= 1 << i; solo_dev->tw28_cnt++; @@ -568,8 +603,11 @@ int solo_tw28_init(struct solo_dev *solo_dev) } } - if (!solo_dev->tw28_cnt) + if (solo_dev->tw28_cnt != (solo_dev->nr_chans >> 2)) { + dev_err(&solo_dev->pdev->dev, + "Could not initialize any techwell chips\n"); return -EINVAL; + } saa7128_setup(solo_dev); @@ -582,17 +620,6 @@ int solo_tw28_init(struct solo_dev *solo_dev) tw2815_setup(solo_dev, TW_CHIP_OFFSET_ADDR(i)); } - dev_info(&solo_dev->pdev->dev, "Initialized %d tw28xx chip%s:", - solo_dev->tw28_cnt, solo_dev->tw28_cnt == 1 ? "" : "s"); - - if (solo_dev->tw2865) - printk(" tw2865[%d]", hweight32(solo_dev->tw2865)); - if (solo_dev->tw2864) - printk(" tw2864[%d]", hweight32(solo_dev->tw2864)); - if (solo_dev->tw2815) - printk(" tw2815[%d]", hweight32(solo_dev->tw2815)); - printk("\n"); - return 0; } @@ -610,7 +637,7 @@ int tw28_get_video_status(struct solo_dev *solo_dev, u8 ch) chip_num = ch / 4; ch %= 4; - val = tw_readbyte(solo_dev, chip_num, TW286X_AV_STAT_ADDR, + val = tw_readbyte(solo_dev, chip_num, TW286x_AV_STAT_ADDR, TW_AV_STAT_ADDR) & 0x0f; return val & (1 << ch) ? 1 : 0; @@ -626,7 +653,7 @@ u16 tw28_get_audio_status(struct solo_dev *solo_dev) int i; for (i = 0; i < solo_dev->tw28_cnt; i++) { - val = (tw_readbyte(solo_dev, i, TW286X_AV_STAT_ADDR, + val = (tw_readbyte(solo_dev, i, TW286x_AV_STAT_ADDR, TW_AV_STAT_ADDR) & 0xf0) >> 4; status |= val << (i * 4); } @@ -635,7 +662,13 @@ u16 tw28_get_audio_status(struct solo_dev *solo_dev) } #endif -int tw28_set_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch, s32 val) +bool tw28_has_sharpness(struct solo_dev *solo_dev, u8 ch) +{ + return is_tw286x(solo_dev, ch / 4); +} + +int tw28_set_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch, + s32 val) { char sval; u8 chip_num; @@ -650,8 +683,6 @@ int tw28_set_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch, s32 val) switch (ctrl) { case V4L2_CID_SHARPNESS: /* Only 286x has sharpness */ - if (val > 0x0f || val < 0) - return -ERANGE; if (is_tw286x(solo_dev, chip_num)) { u8 v = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, TW_CHIP_OFFSET_ADDR(chip_num), @@ -661,8 +692,9 @@ int tw28_set_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch, s32 val) solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, TW_CHIP_OFFSET_ADDR(chip_num), TW286x_SHARPNESS(chip_num), v); - } else if (val != 0) - return -ERANGE; + } else { + return -EINVAL; + } break; case V4L2_CID_HUE: @@ -676,6 +708,7 @@ int tw28_set_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch, s32 val) break; case V4L2_CID_SATURATION: + /* 286x chips have a U and V component for saturation */ if (is_tw286x(solo_dev, chip_num)) { solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, TW_CHIP_OFFSET_ADDR(chip_num), diff --git a/drivers/staging/media/solo6x10/tw28.h b/drivers/staging/media/solo6x10/solo6x10-tw28.h index a44a03afbd30..1a02c87d4cf0 100644 --- a/drivers/staging/media/solo6x10/tw28.h +++ b/drivers/staging/media/solo6x10/solo6x10-tw28.h @@ -1,6 +1,11 @@ /* - * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com - * Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net> + * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com> + * + * Original author: + * Ben Collins <bcollins@ubuntu.com> + * + * Additional work by: + * John Brooks <john.brooks@bluecherry.net> * * 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 @@ -36,7 +41,7 @@ #define TW_AUDIO_INPUT_GAIN_ADDR(n) (0x60 + ((n > 1) ? 1 : 0)) /* tw286x */ -#define TW286X_AV_STAT_ADDR 0xfd +#define TW286x_AV_STAT_ADDR 0xfd #define TW286x_HUE_ADDR(n) (0x06 | ((n) << 4)) #define TW286x_SATURATIONU_ADDR(n) (0x04 | ((n) << 4)) #define TW286x_SATURATIONV_ADDR(n) (0x05 | ((n) << 4)) @@ -50,6 +55,7 @@ int solo_tw28_init(struct solo_dev *solo_dev); int tw28_set_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch, s32 val); int tw28_get_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch, s32 *val); +bool tw28_has_sharpness(struct solo_dev *solo_dev, u8 ch); u8 tw28_get_audio_gain(struct solo_dev *solo_dev, u8 ch); void tw28_set_audio_gain(struct solo_dev *solo_dev, u8 ch, u8 val); diff --git a/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c b/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c new file mode 100644 index 000000000000..98e2902afd74 --- /dev/null +++ b/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c @@ -0,0 +1,1385 @@ +/* + * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com> + * + * Original author: + * Ben Collins <bcollins@ubuntu.com> + * + * Additional work by: + * John Brooks <john.brooks@bluecherry.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/kthread.h> +#include <linux/freezer.h> + +#include <media/v4l2-ioctl.h> +#include <media/v4l2-common.h> +#include <media/v4l2-event.h> +#include <media/videobuf2-dma-sg.h> + +#include "solo6x10.h" +#include "solo6x10-tw28.h" +#include "solo6x10-jpeg.h" + +#define MIN_VID_BUFFERS 2 +#define FRAME_BUF_SIZE (196 * 1024) +#define MP4_QS 16 +#define DMA_ALIGN 4096 + +/* 6010 M4V */ +static unsigned char vop_6010_ntsc_d1[] = { + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x20, + 0x02, 0x48, 0x1d, 0xc0, 0x00, 0x40, 0x00, 0x40, + 0x00, 0x40, 0x00, 0x80, 0x00, 0x97, 0x53, 0x04, + 0x1f, 0x4c, 0x58, 0x10, 0xf0, 0x71, 0x18, 0x3f, +}; + +static unsigned char vop_6010_ntsc_cif[] = { + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x20, + 0x02, 0x48, 0x1d, 0xc0, 0x00, 0x40, 0x00, 0x40, + 0x00, 0x40, 0x00, 0x80, 0x00, 0x97, 0x53, 0x04, + 0x1f, 0x4c, 0x2c, 0x10, 0x78, 0x51, 0x18, 0x3f, +}; + +static unsigned char vop_6010_pal_d1[] = { + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x20, + 0x02, 0x48, 0x15, 0xc0, 0x00, 0x40, 0x00, 0x40, + 0x00, 0x40, 0x00, 0x80, 0x00, 0x97, 0x53, 0x04, + 0x1f, 0x4c, 0x58, 0x11, 0x20, 0x71, 0x18, 0x3f, +}; + +static unsigned char vop_6010_pal_cif[] = { + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x20, + 0x02, 0x48, 0x15, 0xc0, 0x00, 0x40, 0x00, 0x40, + 0x00, 0x40, 0x00, 0x80, 0x00, 0x97, 0x53, 0x04, + 0x1f, 0x4c, 0x2c, 0x10, 0x90, 0x51, 0x18, 0x3f, +}; + +/* 6110 h.264 */ +static unsigned char vop_6110_ntsc_d1[] = { + 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x00, 0x1e, + 0x9a, 0x74, 0x05, 0x81, 0xec, 0x80, 0x00, 0x00, + 0x00, 0x01, 0x68, 0xce, 0x32, 0x28, 0x00, 0x00, +}; + +static unsigned char vop_6110_ntsc_cif[] = { + 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x00, 0x1e, + 0x9a, 0x74, 0x0b, 0x0f, 0xc8, 0x00, 0x00, 0x00, + 0x01, 0x68, 0xce, 0x32, 0x28, 0x00, 0x00, 0x00, +}; + +static unsigned char vop_6110_pal_d1[] = { + 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x00, 0x1e, + 0x9a, 0x74, 0x05, 0x80, 0x93, 0x20, 0x00, 0x00, + 0x00, 0x01, 0x68, 0xce, 0x32, 0x28, 0x00, 0x00, +}; + +static unsigned char vop_6110_pal_cif[] = { + 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x00, 0x1e, + 0x9a, 0x74, 0x0b, 0x04, 0xb2, 0x00, 0x00, 0x00, + 0x01, 0x68, 0xce, 0x32, 0x28, 0x00, 0x00, 0x00, +}; + +struct vop_header { + /* VE_STATUS0 */ + u32 mpeg_size:20, sad_motion_flag:1, video_motion_flag:1, vop_type:2, + channel:5, source_fl:1, interlace:1, progressive:1; + + /* VE_STATUS1 */ + u32 vsize:8, hsize:8, last_queue:4, nop0:8, scale:4; + + /* VE_STATUS2 */ + u32 mpeg_off; + + /* VE_STATUS3 */ + u32 jpeg_off; + + /* VE_STATUS4 */ + u32 jpeg_size:20, interval:10, nop1:2; + + /* VE_STATUS5/6 */ + u32 sec, usec; + + /* VE_STATUS7/8/9 */ + u32 nop2[3]; + + /* VE_STATUS10 */ + u32 mpeg_size_alt:20, nop3:12; + + u32 end_nops[5]; +} __packed; + +struct solo_enc_buf { + enum solo_enc_types type; + struct vop_header *vh; + int motion; +}; + +static int solo_is_motion_on(struct solo_enc_dev *solo_enc) +{ + struct solo_dev *solo_dev = solo_enc->solo_dev; + + return (solo_dev->motion_mask >> solo_enc->ch) & 1; +} + +static int solo_motion_detected(struct solo_enc_dev *solo_enc) +{ + struct solo_dev *solo_dev = solo_enc->solo_dev; + unsigned long flags; + u32 ch_mask = 1 << solo_enc->ch; + int ret = 0; + + spin_lock_irqsave(&solo_enc->motion_lock, flags); + if (solo_reg_read(solo_dev, SOLO_VI_MOT_STATUS) & ch_mask) { + solo_reg_write(solo_dev, SOLO_VI_MOT_CLEAR, ch_mask); + ret = 1; + } + spin_unlock_irqrestore(&solo_enc->motion_lock, flags); + + return ret; +} + +static void solo_motion_toggle(struct solo_enc_dev *solo_enc, int on) +{ + struct solo_dev *solo_dev = solo_enc->solo_dev; + u32 mask = 1 << solo_enc->ch; + unsigned long flags; + + spin_lock_irqsave(&solo_enc->motion_lock, flags); + + if (on) + solo_dev->motion_mask |= mask; + else + solo_dev->motion_mask &= ~mask; + + solo_reg_write(solo_dev, SOLO_VI_MOT_CLEAR, mask); + + solo_reg_write(solo_dev, SOLO_VI_MOT_ADR, + SOLO_VI_MOTION_EN(solo_dev->motion_mask) | + (SOLO_MOTION_EXT_ADDR(solo_dev) >> 16)); + + spin_unlock_irqrestore(&solo_enc->motion_lock, flags); +} + +void solo_update_mode(struct solo_enc_dev *solo_enc) +{ + struct solo_dev *solo_dev = solo_enc->solo_dev; + int vop_len; + unsigned char *vop; + + solo_enc->interlaced = (solo_enc->mode & 0x08) ? 1 : 0; + solo_enc->bw_weight = max(solo_dev->fps / solo_enc->interval, 1); + + if (solo_enc->mode == SOLO_ENC_MODE_CIF) { + solo_enc->width = solo_dev->video_hsize >> 1; + solo_enc->height = solo_dev->video_vsize; + if (solo_dev->type == SOLO_DEV_6110) { + if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) { + vop = vop_6110_ntsc_cif; + vop_len = sizeof(vop_6110_ntsc_cif); + } else { + vop = vop_6110_pal_cif; + vop_len = sizeof(vop_6110_pal_cif); + } + } else { + if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) { + vop = vop_6010_ntsc_cif; + vop_len = sizeof(vop_6010_ntsc_cif); + } else { + vop = vop_6010_pal_cif; + vop_len = sizeof(vop_6010_pal_cif); + } + } + } else { + solo_enc->width = solo_dev->video_hsize; + solo_enc->height = solo_dev->video_vsize << 1; + solo_enc->bw_weight <<= 2; + if (solo_dev->type == SOLO_DEV_6110) { + if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) { + vop = vop_6110_ntsc_d1; + vop_len = sizeof(vop_6110_ntsc_d1); + } else { + vop = vop_6110_pal_d1; + vop_len = sizeof(vop_6110_pal_d1); + } + } else { + if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) { + vop = vop_6010_ntsc_d1; + vop_len = sizeof(vop_6010_ntsc_d1); + } else { + vop = vop_6010_pal_d1; + vop_len = sizeof(vop_6010_pal_d1); + } + } + } + + memcpy(solo_enc->vop, vop, vop_len); + + /* Some fixups for 6010/M4V */ + if (solo_dev->type == SOLO_DEV_6010) { + u16 fps = solo_dev->fps * 1000; + u16 interval = solo_enc->interval * 1000; + + vop = solo_enc->vop; + + /* Frame rate and interval */ + vop[22] = fps >> 4; + vop[23] = ((fps << 4) & 0xf0) | 0x0c + | ((interval >> 13) & 0x3); + vop[24] = (interval >> 5) & 0xff; + vop[25] = ((interval << 3) & 0xf8) | 0x04; + } + + solo_enc->vop_len = vop_len; + + /* Now handle the jpeg header */ + vop = solo_enc->jpeg_header; + vop[SOF0_START + 5] = 0xff & (solo_enc->height >> 8); + vop[SOF0_START + 6] = 0xff & solo_enc->height; + vop[SOF0_START + 7] = 0xff & (solo_enc->width >> 8); + vop[SOF0_START + 8] = 0xff & solo_enc->width; + + memcpy(vop + DQT_START, + jpeg_dqt[solo_g_jpeg_qp(solo_dev, solo_enc->ch)], DQT_LEN); +} + +static int solo_enc_on(struct solo_enc_dev *solo_enc) +{ + u8 ch = solo_enc->ch; + struct solo_dev *solo_dev = solo_enc->solo_dev; + u8 interval; + + solo_update_mode(solo_enc); + + /* Make sure to do a bandwidth check */ + if (solo_enc->bw_weight > solo_dev->enc_bw_remain) + return -EBUSY; + solo_enc->sequence = 0; + solo_dev->enc_bw_remain -= solo_enc->bw_weight; + + if (solo_enc->type == SOLO_ENC_TYPE_EXT) + solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(ch), 1); + + /* Disable all encoding for this channel */ + solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(ch), 0); + + /* Common for both std and ext encoding */ + solo_reg_write(solo_dev, SOLO_VE_CH_INTL(ch), + solo_enc->interlaced ? 1 : 0); + + if (solo_enc->interlaced) + interval = solo_enc->interval - 1; + else + interval = solo_enc->interval; + + /* Standard encoding only */ + solo_reg_write(solo_dev, SOLO_VE_CH_GOP(ch), solo_enc->gop); + solo_reg_write(solo_dev, SOLO_VE_CH_QP(ch), solo_enc->qp); + solo_reg_write(solo_dev, SOLO_CAP_CH_INTV(ch), interval); + + /* Extended encoding only */ + solo_reg_write(solo_dev, SOLO_VE_CH_GOP_E(ch), solo_enc->gop); + solo_reg_write(solo_dev, SOLO_VE_CH_QP_E(ch), solo_enc->qp); + solo_reg_write(solo_dev, SOLO_CAP_CH_INTV_E(ch), interval); + + /* Enables the standard encoder */ + solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(ch), solo_enc->mode); + + return 0; +} + +static void solo_enc_off(struct solo_enc_dev *solo_enc) +{ + struct solo_dev *solo_dev = solo_enc->solo_dev; + + solo_dev->enc_bw_remain += solo_enc->bw_weight; + + solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(solo_enc->ch), 0); + solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(solo_enc->ch), 0); +} + +static int enc_get_mpeg_dma(struct solo_dev *solo_dev, dma_addr_t dma, + unsigned int off, unsigned int size) +{ + int ret; + + if (off > SOLO_MP4E_EXT_SIZE(solo_dev)) + return -EINVAL; + + /* Single shot */ + if (off + size <= SOLO_MP4E_EXT_SIZE(solo_dev)) { + return solo_p2m_dma_t(solo_dev, 0, dma, + SOLO_MP4E_EXT_ADDR(solo_dev) + off, size, + 0, 0); + } + + /* Buffer wrap */ + ret = solo_p2m_dma_t(solo_dev, 0, dma, + SOLO_MP4E_EXT_ADDR(solo_dev) + off, + SOLO_MP4E_EXT_SIZE(solo_dev) - off, 0, 0); + + if (!ret) { + ret = solo_p2m_dma_t(solo_dev, 0, + dma + SOLO_MP4E_EXT_SIZE(solo_dev) - off, + SOLO_MP4E_EXT_ADDR(solo_dev), + size + off - SOLO_MP4E_EXT_SIZE(solo_dev), 0, 0); + } + + return ret; +} + +/* Build a descriptor queue out of an SG list and send it to the P2M for + * processing. */ +static int solo_send_desc(struct solo_enc_dev *solo_enc, int skip, + struct vb2_dma_sg_desc *vbuf, int off, int size, + unsigned int base, unsigned int base_size) +{ + struct solo_dev *solo_dev = solo_enc->solo_dev; + struct scatterlist *sg; + int i; + int ret; + + if (WARN_ON_ONCE(size > FRAME_BUF_SIZE)) + return -EINVAL; + + solo_enc->desc_count = 1; + + for_each_sg(vbuf->sglist, sg, vbuf->num_pages, i) { + struct solo_p2m_desc *desc; + dma_addr_t dma; + int len; + int left = base_size - off; + + desc = &solo_enc->desc_items[solo_enc->desc_count++]; + dma = sg_dma_address(sg); + len = sg_dma_len(sg); + + /* We assume this is smaller than the scatter size */ + BUG_ON(skip >= len); + if (skip) { + len -= skip; + dma += skip; + size -= skip; + skip = 0; + } + + len = min(len, size); + + if (len <= left) { + /* Single descriptor */ + solo_p2m_fill_desc(desc, 0, dma, base + off, + len, 0, 0); + } else { + /* Buffer wrap */ + /* XXX: Do these as separate DMA requests, to avoid + timeout errors triggered by awkwardly sized + descriptors. See + <https://github.com/bluecherrydvr/solo6x10/issues/8> + */ + ret = solo_p2m_dma_t(solo_dev, 0, dma, base + off, + left, 0, 0); + if (ret) + return ret; + + ret = solo_p2m_dma_t(solo_dev, 0, dma + left, base, + len - left, 0, 0); + if (ret) + return ret; + + solo_enc->desc_count--; + } + + size -= len; + if (size <= 0) + break; + + off += len; + if (off >= base_size) + off -= base_size; + + /* Because we may use two descriptors per loop */ + if (solo_enc->desc_count >= (solo_enc->desc_nelts - 1)) { + ret = solo_p2m_dma_desc(solo_dev, solo_enc->desc_items, + solo_enc->desc_dma, + solo_enc->desc_count - 1); + if (ret) + return ret; + solo_enc->desc_count = 1; + } + } + + if (solo_enc->desc_count <= 1) + return 0; + + return solo_p2m_dma_desc(solo_dev, solo_enc->desc_items, solo_enc->desc_dma, + solo_enc->desc_count - 1); +} + +static int solo_fill_jpeg(struct solo_enc_dev *solo_enc, + struct vb2_buffer *vb, struct vop_header *vh) +{ + struct solo_dev *solo_dev = solo_enc->solo_dev; + struct vb2_dma_sg_desc *vbuf = vb2_dma_sg_plane_desc(vb, 0); + int frame_size; + int ret; + + vb->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME; + + if (vb2_plane_size(vb, 0) < vh->jpeg_size + solo_enc->jpeg_len) + return -EIO; + + sg_copy_from_buffer(vbuf->sglist, vbuf->num_pages, + solo_enc->jpeg_header, + solo_enc->jpeg_len); + + frame_size = (vh->jpeg_size + solo_enc->jpeg_len + (DMA_ALIGN - 1)) + & ~(DMA_ALIGN - 1); + vb2_set_plane_payload(vb, 0, vh->jpeg_size + solo_enc->jpeg_len); + + dma_map_sg(&solo_dev->pdev->dev, vbuf->sglist, vbuf->num_pages, + DMA_FROM_DEVICE); + ret = solo_send_desc(solo_enc, solo_enc->jpeg_len, vbuf, vh->jpeg_off, + frame_size, SOLO_JPEG_EXT_ADDR(solo_dev), + SOLO_JPEG_EXT_SIZE(solo_dev)); + dma_unmap_sg(&solo_dev->pdev->dev, vbuf->sglist, vbuf->num_pages, + DMA_FROM_DEVICE); + return ret; +} + +static int solo_fill_mpeg(struct solo_enc_dev *solo_enc, + struct vb2_buffer *vb, struct vop_header *vh) +{ + struct solo_dev *solo_dev = solo_enc->solo_dev; + struct vb2_dma_sg_desc *vbuf = vb2_dma_sg_plane_desc(vb, 0); + int frame_off, frame_size; + int skip = 0; + int ret; + + if (vb2_plane_size(vb, 0) < vh->mpeg_size) + return -EIO; + + /* If this is a key frame, add extra header */ + if (!vh->vop_type) { + sg_copy_from_buffer(vbuf->sglist, vbuf->num_pages, + solo_enc->vop, + solo_enc->vop_len); + + skip = solo_enc->vop_len; + + vb->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME; + vb2_set_plane_payload(vb, 0, vh->mpeg_size + solo_enc->vop_len); + } else { + vb->v4l2_buf.flags |= V4L2_BUF_FLAG_PFRAME; + vb2_set_plane_payload(vb, 0, vh->mpeg_size); + } + + /* Now get the actual mpeg payload */ + frame_off = (vh->mpeg_off + sizeof(*vh)) + % SOLO_MP4E_EXT_SIZE(solo_dev); + frame_size = (vh->mpeg_size + skip + (DMA_ALIGN - 1)) + & ~(DMA_ALIGN - 1); + + dma_map_sg(&solo_dev->pdev->dev, vbuf->sglist, vbuf->num_pages, + DMA_FROM_DEVICE); + ret = solo_send_desc(solo_enc, skip, vbuf, frame_off, frame_size, + SOLO_MP4E_EXT_ADDR(solo_dev), + SOLO_MP4E_EXT_SIZE(solo_dev)); + dma_unmap_sg(&solo_dev->pdev->dev, vbuf->sglist, vbuf->num_pages, + DMA_FROM_DEVICE); + return ret; +} + +static int solo_enc_fillbuf(struct solo_enc_dev *solo_enc, + struct vb2_buffer *vb, struct solo_enc_buf *enc_buf) +{ + struct vop_header *vh = enc_buf->vh; + int ret; + + /* Check for motion flags */ + vb->v4l2_buf.flags &= ~(V4L2_BUF_FLAG_MOTION_ON | + V4L2_BUF_FLAG_MOTION_DETECTED); + if (solo_is_motion_on(solo_enc)) { + vb->v4l2_buf.flags |= V4L2_BUF_FLAG_MOTION_ON; + if (enc_buf->motion) + vb->v4l2_buf.flags |= V4L2_BUF_FLAG_MOTION_DETECTED; + } + + switch (solo_enc->fmt) { + case V4L2_PIX_FMT_MPEG4: + case V4L2_PIX_FMT_H264: + ret = solo_fill_mpeg(solo_enc, vb, vh); + break; + default: /* V4L2_PIX_FMT_MJPEG */ + ret = solo_fill_jpeg(solo_enc, vb, vh); + break; + } + + if (!ret) { + vb->v4l2_buf.sequence = solo_enc->sequence++; + vb->v4l2_buf.timestamp.tv_sec = vh->sec; + vb->v4l2_buf.timestamp.tv_usec = vh->usec; + } + + vb2_buffer_done(vb, ret ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); + + return ret; +} + +static void solo_enc_handle_one(struct solo_enc_dev *solo_enc, + struct solo_enc_buf *enc_buf) +{ + struct solo_vb2_buf *vb; + unsigned long flags; + + mutex_lock(&solo_enc->lock); + if (solo_enc->type != enc_buf->type) + goto unlock; + + spin_lock_irqsave(&solo_enc->av_lock, flags); + if (list_empty(&solo_enc->vidq_active)) { + spin_unlock_irqrestore(&solo_enc->av_lock, flags); + goto unlock; + } + vb = list_first_entry(&solo_enc->vidq_active, struct solo_vb2_buf, list); + list_del(&vb->list); + spin_unlock_irqrestore(&solo_enc->av_lock, flags); + + solo_enc_fillbuf(solo_enc, &vb->vb, enc_buf); +unlock: + mutex_unlock(&solo_enc->lock); +} + +void solo_enc_v4l2_isr(struct solo_dev *solo_dev) +{ + wake_up_interruptible_all(&solo_dev->ring_thread_wait); +} + +static void solo_handle_ring(struct solo_dev *solo_dev) +{ + for (;;) { + struct solo_enc_dev *solo_enc; + struct solo_enc_buf enc_buf; + u32 mpeg_current, off; + u8 ch; + u8 cur_q; + + /* Check if the hardware has any new ones in the queue */ + cur_q = solo_reg_read(solo_dev, SOLO_VE_STATE(11)) & 0xff; + if (cur_q == solo_dev->enc_idx) + break; + + mpeg_current = solo_reg_read(solo_dev, + SOLO_VE_MPEG4_QUE(solo_dev->enc_idx)); + solo_dev->enc_idx = (solo_dev->enc_idx + 1) % MP4_QS; + + ch = (mpeg_current >> 24) & 0x1f; + off = mpeg_current & 0x00ffffff; + + if (ch >= SOLO_MAX_CHANNELS) { + ch -= SOLO_MAX_CHANNELS; + enc_buf.type = SOLO_ENC_TYPE_EXT; + } else + enc_buf.type = SOLO_ENC_TYPE_STD; + + solo_enc = solo_dev->v4l2_enc[ch]; + if (solo_enc == NULL) { + dev_err(&solo_dev->pdev->dev, + "Got spurious packet for channel %d\n", ch); + continue; + } + + /* FAIL... */ + if (enc_get_mpeg_dma(solo_dev, solo_dev->vh_dma, off, + sizeof(struct vop_header))) + continue; + + enc_buf.vh = (struct vop_header *)solo_dev->vh_buf; + enc_buf.vh->mpeg_off -= SOLO_MP4E_EXT_ADDR(solo_dev); + enc_buf.vh->jpeg_off -= SOLO_JPEG_EXT_ADDR(solo_dev); + + /* Sanity check */ + if (enc_buf.vh->mpeg_off != off) + continue; + + if (solo_motion_detected(solo_enc)) + enc_buf.motion = 1; + else + enc_buf.motion = 0; + + solo_enc_handle_one(solo_enc, &enc_buf); + } +} + +static int solo_ring_thread(void *data) +{ + struct solo_dev *solo_dev = data; + DECLARE_WAITQUEUE(wait, current); + + set_freezable(); + add_wait_queue(&solo_dev->ring_thread_wait, &wait); + + for (;;) { + long timeout = schedule_timeout_interruptible(HZ); + if (timeout == -ERESTARTSYS || kthread_should_stop()) + break; + solo_irq_off(solo_dev, SOLO_IRQ_ENCODER); + solo_handle_ring(solo_dev); + solo_irq_on(solo_dev, SOLO_IRQ_ENCODER); + try_to_freeze(); + } + + remove_wait_queue(&solo_dev->ring_thread_wait, &wait); + + return 0; +} + +static int solo_enc_queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt, + unsigned int *num_buffers, unsigned int *num_planes, + unsigned int sizes[], void *alloc_ctxs[]) +{ + sizes[0] = FRAME_BUF_SIZE; + *num_planes = 1; + + if (*num_buffers < MIN_VID_BUFFERS) + *num_buffers = MIN_VID_BUFFERS; + + return 0; +} + +static void solo_enc_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_queue *vq = vb->vb2_queue; + struct solo_enc_dev *solo_enc = vb2_get_drv_priv(vq); + struct solo_vb2_buf *solo_vb = + container_of(vb, struct solo_vb2_buf, vb); + + spin_lock(&solo_enc->av_lock); + list_add_tail(&solo_vb->list, &solo_enc->vidq_active); + spin_unlock(&solo_enc->av_lock); +} + +static int solo_ring_start(struct solo_dev *solo_dev) +{ + solo_dev->ring_thread = kthread_run(solo_ring_thread, solo_dev, + SOLO6X10_NAME "_ring"); + if (IS_ERR(solo_dev->ring_thread)) { + int err = PTR_ERR(solo_dev->ring_thread); + solo_dev->ring_thread = NULL; + return err; + } + + solo_irq_on(solo_dev, SOLO_IRQ_ENCODER); + + return 0; +} + +static void solo_ring_stop(struct solo_dev *solo_dev) +{ + if (solo_dev->ring_thread) { + kthread_stop(solo_dev->ring_thread); + solo_dev->ring_thread = NULL; + } + + solo_irq_off(solo_dev, SOLO_IRQ_ENCODER); +} + +static int solo_enc_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct solo_enc_dev *solo_enc = vb2_get_drv_priv(q); + int ret; + + ret = solo_enc_on(solo_enc); + if (ret) + return ret; + return solo_ring_start(solo_enc->solo_dev); +} + +static int solo_enc_stop_streaming(struct vb2_queue *q) +{ + struct solo_enc_dev *solo_enc = vb2_get_drv_priv(q); + + solo_enc_off(solo_enc); + INIT_LIST_HEAD(&solo_enc->vidq_active); + solo_ring_stop(solo_enc->solo_dev); + return 0; +} + +static struct vb2_ops solo_enc_video_qops = { + .queue_setup = solo_enc_queue_setup, + .buf_queue = solo_enc_buf_queue, + .start_streaming = solo_enc_start_streaming, + .stop_streaming = solo_enc_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static int solo_enc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct solo_enc_dev *solo_enc = video_drvdata(file); + struct solo_dev *solo_dev = solo_enc->solo_dev; + + strcpy(cap->driver, SOLO6X10_NAME); + snprintf(cap->card, sizeof(cap->card), "Softlogic 6x10 Enc %d", + solo_enc->ch); + snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI:%s", + pci_name(solo_dev->pdev)); + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + return 0; +} + +static int solo_enc_enum_input(struct file *file, void *priv, + struct v4l2_input *input) +{ + struct solo_enc_dev *solo_enc = video_drvdata(file); + struct solo_dev *solo_dev = solo_enc->solo_dev; + + if (input->index) + return -EINVAL; + + snprintf(input->name, sizeof(input->name), "Encoder %d", + solo_enc->ch + 1); + input->type = V4L2_INPUT_TYPE_CAMERA; + input->std = solo_enc->vfd->tvnorms; + + if (!tw28_get_video_status(solo_dev, solo_enc->ch)) + input->status = V4L2_IN_ST_NO_SIGNAL; + + return 0; +} + +static int solo_enc_set_input(struct file *file, void *priv, + unsigned int index) +{ + if (index) + return -EINVAL; + + return 0; +} + +static int solo_enc_get_input(struct file *file, void *priv, + unsigned int *index) +{ + *index = 0; + + return 0; +} + +static int solo_enc_enum_fmt_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct solo_enc_dev *solo_enc = video_drvdata(file); + int dev_type = solo_enc->solo_dev->type; + + switch (f->index) { + case 0: + switch (dev_type) { + case SOLO_DEV_6010: + f->pixelformat = V4L2_PIX_FMT_MPEG4; + strcpy(f->description, "MPEG-4 part 2"); + break; + case SOLO_DEV_6110: + f->pixelformat = V4L2_PIX_FMT_H264; + strcpy(f->description, "H.264"); + break; + } + break; + case 1: + f->pixelformat = V4L2_PIX_FMT_MJPEG; + strcpy(f->description, "MJPEG"); + break; + default: + return -EINVAL; + } + + f->flags = V4L2_FMT_FLAG_COMPRESSED; + + return 0; +} + +static inline int solo_valid_pixfmt(u32 pixfmt, int dev_type) +{ + return (pixfmt == V4L2_PIX_FMT_H264 && dev_type == SOLO_DEV_6110) + || (pixfmt == V4L2_PIX_FMT_MPEG4 && dev_type == SOLO_DEV_6010) + || pixfmt == V4L2_PIX_FMT_MJPEG ? 0 : -EINVAL; +} + +static int solo_enc_try_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct solo_enc_dev *solo_enc = video_drvdata(file); + struct solo_dev *solo_dev = solo_enc->solo_dev; + struct v4l2_pix_format *pix = &f->fmt.pix; + + if (solo_valid_pixfmt(pix->pixelformat, solo_dev->type)) + return -EINVAL; + + if (pix->width < solo_dev->video_hsize || + pix->height < solo_dev->video_vsize << 1) { + /* Default to CIF 1/2 size */ + pix->width = solo_dev->video_hsize >> 1; + pix->height = solo_dev->video_vsize; + } else { + /* Full frame */ + pix->width = solo_dev->video_hsize; + pix->height = solo_dev->video_vsize << 1; + } + + switch (pix->field) { + case V4L2_FIELD_NONE: + case V4L2_FIELD_INTERLACED: + break; + case V4L2_FIELD_ANY: + default: + pix->field = V4L2_FIELD_INTERLACED; + break; + } + + /* Just set these */ + pix->colorspace = V4L2_COLORSPACE_SMPTE170M; + pix->sizeimage = FRAME_BUF_SIZE; + pix->bytesperline = 0; + pix->priv = 0; + + return 0; +} + +static int solo_enc_set_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct solo_enc_dev *solo_enc = video_drvdata(file); + struct solo_dev *solo_dev = solo_enc->solo_dev; + struct v4l2_pix_format *pix = &f->fmt.pix; + int ret; + + if (vb2_is_busy(&solo_enc->vidq)) + return -EBUSY; + + ret = solo_enc_try_fmt_cap(file, priv, f); + if (ret) + return ret; + + if (pix->width == solo_dev->video_hsize) + solo_enc->mode = SOLO_ENC_MODE_D1; + else + solo_enc->mode = SOLO_ENC_MODE_CIF; + + /* This does not change the encoder at all */ + solo_enc->fmt = pix->pixelformat; + + /* + * More information is needed about these 'extended' types. As far + * as I can tell these are basically additional video streams with + * different MPEG encoding attributes that can run in parallel with + * the main stream. If so, then this should be implemented as a + * second video node. Abusing priv like this is certainly not the + * right approach. + if (pix->priv) + solo_enc->type = SOLO_ENC_TYPE_EXT; + */ + solo_update_mode(solo_enc); + return 0; +} + +static int solo_enc_get_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct solo_enc_dev *solo_enc = video_drvdata(file); + struct v4l2_pix_format *pix = &f->fmt.pix; + + pix->width = solo_enc->width; + pix->height = solo_enc->height; + pix->pixelformat = solo_enc->fmt; + pix->field = solo_enc->interlaced ? V4L2_FIELD_INTERLACED : + V4L2_FIELD_NONE; + pix->sizeimage = FRAME_BUF_SIZE; + pix->colorspace = V4L2_COLORSPACE_SMPTE170M; + pix->priv = 0; + + return 0; +} + +static int solo_enc_g_std(struct file *file, void *priv, v4l2_std_id *i) +{ + struct solo_enc_dev *solo_enc = video_drvdata(file); + struct solo_dev *solo_dev = solo_enc->solo_dev; + + if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) + *i = V4L2_STD_NTSC_M; + else + *i = V4L2_STD_PAL; + return 0; +} + +static int solo_enc_s_std(struct file *file, void *priv, v4l2_std_id std) +{ + struct solo_enc_dev *solo_enc = video_drvdata(file); + + return solo_set_video_type(solo_enc->solo_dev, std & V4L2_STD_PAL); +} + +static int solo_enum_framesizes(struct file *file, void *priv, + struct v4l2_frmsizeenum *fsize) +{ + struct solo_enc_dev *solo_enc = video_drvdata(file); + struct solo_dev *solo_dev = solo_enc->solo_dev; + + if (solo_valid_pixfmt(fsize->pixel_format, solo_dev->type)) + return -EINVAL; + + switch (fsize->index) { + case 0: + fsize->discrete.width = solo_dev->video_hsize >> 1; + fsize->discrete.height = solo_dev->video_vsize; + break; + case 1: + fsize->discrete.width = solo_dev->video_hsize; + fsize->discrete.height = solo_dev->video_vsize << 1; + break; + default: + return -EINVAL; + } + + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + + return 0; +} + +static int solo_enum_frameintervals(struct file *file, void *priv, + struct v4l2_frmivalenum *fintv) +{ + struct solo_enc_dev *solo_enc = video_drvdata(file); + struct solo_dev *solo_dev = solo_enc->solo_dev; + + if (solo_valid_pixfmt(fintv->pixel_format, solo_dev->type)) + return -EINVAL; + if (fintv->index) + return -EINVAL; + if ((fintv->width != solo_dev->video_hsize >> 1 || + fintv->height != solo_dev->video_vsize) && + (fintv->width != solo_dev->video_hsize || + fintv->height != solo_dev->video_vsize << 1)) + return -EINVAL; + + fintv->type = V4L2_FRMIVAL_TYPE_STEPWISE; + + fintv->stepwise.min.numerator = 1; + fintv->stepwise.min.denominator = solo_dev->fps; + + fintv->stepwise.max.numerator = 15; + fintv->stepwise.max.denominator = solo_dev->fps; + + fintv->stepwise.step.numerator = 1; + fintv->stepwise.step.denominator = solo_dev->fps; + + return 0; +} + +static int solo_g_parm(struct file *file, void *priv, + struct v4l2_streamparm *sp) +{ + struct solo_enc_dev *solo_enc = video_drvdata(file); + struct solo_dev *solo_dev = solo_enc->solo_dev; + struct v4l2_captureparm *cp = &sp->parm.capture; + + cp->capability = V4L2_CAP_TIMEPERFRAME; + cp->timeperframe.numerator = solo_enc->interval; + cp->timeperframe.denominator = solo_dev->fps; + cp->capturemode = 0; + /* XXX: Shouldn't we be able to get/set this from videobuf? */ + cp->readbuffers = 2; + + return 0; +} + +static int solo_s_parm(struct file *file, void *priv, + struct v4l2_streamparm *sp) +{ + struct solo_enc_dev *solo_enc = video_drvdata(file); + struct solo_dev *solo_dev = solo_enc->solo_dev; + struct v4l2_captureparm *cp = &sp->parm.capture; + + if (vb2_is_streaming(&solo_enc->vidq)) + return -EBUSY; + + if ((cp->timeperframe.numerator == 0) || + (cp->timeperframe.denominator == 0)) { + /* reset framerate */ + cp->timeperframe.numerator = 1; + cp->timeperframe.denominator = solo_dev->fps; + } + + if (cp->timeperframe.denominator != solo_dev->fps) + cp->timeperframe.denominator = solo_dev->fps; + + if (cp->timeperframe.numerator > 15) + cp->timeperframe.numerator = 15; + + solo_enc->interval = cp->timeperframe.numerator; + + cp->capability = V4L2_CAP_TIMEPERFRAME; + cp->readbuffers = 2; + + solo_update_mode(solo_enc); + return 0; +} + +static long solo_enc_default(struct file *file, void *fh, + bool valid_prio, unsigned int cmd, void *arg) +{ + struct solo_enc_dev *solo_enc = video_drvdata(file); + struct solo_dev *solo_dev = solo_enc->solo_dev; + struct solo_motion_thresholds *thresholds = arg; + + switch (cmd) { + case SOLO_IOC_G_MOTION_THRESHOLDS: + *thresholds = solo_enc->motion_thresholds; + return 0; + + case SOLO_IOC_S_MOTION_THRESHOLDS: + if (!valid_prio) + return -EBUSY; + solo_enc->motion_thresholds = *thresholds; + if (solo_enc->motion_enabled && !solo_enc->motion_global) + return solo_set_motion_block(solo_dev, solo_enc->ch, + &solo_enc->motion_thresholds); + return 0; + default: + return -ENOTTY; + } +} + +static int solo_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct solo_enc_dev *solo_enc = + container_of(ctrl->handler, struct solo_enc_dev, hdl); + struct solo_dev *solo_dev = solo_enc->solo_dev; + int err; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + case V4L2_CID_CONTRAST: + case V4L2_CID_SATURATION: + case V4L2_CID_HUE: + case V4L2_CID_SHARPNESS: + return tw28_set_ctrl_val(solo_dev, ctrl->id, solo_enc->ch, + ctrl->val); + case V4L2_CID_MPEG_VIDEO_GOP_SIZE: + solo_enc->gop = ctrl->val; + return 0; + case V4L2_CID_MOTION_THRESHOLD: + solo_enc->motion_thresh = ctrl->val; + if (!solo_enc->motion_global || !solo_enc->motion_enabled) + return 0; + return solo_set_motion_threshold(solo_dev, solo_enc->ch, ctrl->val); + case V4L2_CID_MOTION_MODE: + solo_enc->motion_global = ctrl->val == 1; + solo_enc->motion_enabled = ctrl->val > 0; + if (ctrl->val) { + if (solo_enc->motion_global) + solo_set_motion_threshold(solo_dev, solo_enc->ch, + solo_enc->motion_thresh); + else + solo_set_motion_block(solo_dev, solo_enc->ch, + &solo_enc->motion_thresholds); + } + solo_motion_toggle(solo_enc, ctrl->val); + return 0; + case V4L2_CID_OSD_TEXT: + strcpy(solo_enc->osd_text, ctrl->string); + err = solo_osd_print(solo_enc); + return err; + default: + return -EINVAL; + } + + return 0; +} + +static const struct v4l2_file_operations solo_enc_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, + .unlocked_ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops solo_enc_ioctl_ops = { + .vidioc_querycap = solo_enc_querycap, + .vidioc_s_std = solo_enc_s_std, + .vidioc_g_std = solo_enc_g_std, + /* Input callbacks */ + .vidioc_enum_input = solo_enc_enum_input, + .vidioc_s_input = solo_enc_set_input, + .vidioc_g_input = solo_enc_get_input, + /* Video capture format callbacks */ + .vidioc_enum_fmt_vid_cap = solo_enc_enum_fmt_cap, + .vidioc_try_fmt_vid_cap = solo_enc_try_fmt_cap, + .vidioc_s_fmt_vid_cap = solo_enc_set_fmt_cap, + .vidioc_g_fmt_vid_cap = solo_enc_get_fmt_cap, + /* Streaming I/O */ + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + /* Frame size and interval */ + .vidioc_enum_framesizes = solo_enum_framesizes, + .vidioc_enum_frameintervals = solo_enum_frameintervals, + /* Video capture parameters */ + .vidioc_s_parm = solo_s_parm, + .vidioc_g_parm = solo_g_parm, + /* Logging and events */ + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, + .vidioc_default = solo_enc_default, +}; + +static const struct video_device solo_enc_template = { + .name = SOLO6X10_NAME, + .fops = &solo_enc_fops, + .ioctl_ops = &solo_enc_ioctl_ops, + .minor = -1, + .release = video_device_release, + .tvnorms = V4L2_STD_NTSC_M | V4L2_STD_PAL, +}; + +static const struct v4l2_ctrl_ops solo_ctrl_ops = { + .s_ctrl = solo_s_ctrl, +}; + +static const struct v4l2_ctrl_config solo_motion_threshold_ctrl = { + .ops = &solo_ctrl_ops, + .id = V4L2_CID_MOTION_THRESHOLD, + .name = "Motion Detection Threshold", + .type = V4L2_CTRL_TYPE_INTEGER, + .max = 0xffff, + .def = SOLO_DEF_MOT_THRESH, + .step = 1, + .flags = V4L2_CTRL_FLAG_SLIDER, +}; + +static const char * const solo_motion_mode_menu[] = { + "Disabled", + "Global Threshold", + "Regional Threshold", + NULL +}; + +static const struct v4l2_ctrl_config solo_motion_enable_ctrl = { + .ops = &solo_ctrl_ops, + .id = V4L2_CID_MOTION_MODE, + .name = "Motion Detection Mode", + .type = V4L2_CTRL_TYPE_MENU, + .qmenu = solo_motion_mode_menu, + .max = 2, +}; + +static const struct v4l2_ctrl_config solo_osd_text_ctrl = { + .ops = &solo_ctrl_ops, + .id = V4L2_CID_OSD_TEXT, + .name = "OSD Text", + .type = V4L2_CTRL_TYPE_STRING, + .max = OSD_TEXT_MAX, + .step = 1, +}; + +static struct solo_enc_dev *solo_enc_alloc(struct solo_dev *solo_dev, + u8 ch, unsigned nr) +{ + struct solo_enc_dev *solo_enc; + struct v4l2_ctrl_handler *hdl; + int ret; + int x, y; + + solo_enc = kzalloc(sizeof(*solo_enc), GFP_KERNEL); + if (!solo_enc) + return ERR_PTR(-ENOMEM); + + hdl = &solo_enc->hdl; + v4l2_ctrl_handler_init(hdl, 10); + v4l2_ctrl_new_std(hdl, &solo_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); + v4l2_ctrl_new_std(hdl, &solo_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, 128); + v4l2_ctrl_new_std(hdl, &solo_ctrl_ops, + V4L2_CID_SATURATION, 0, 255, 1, 128); + v4l2_ctrl_new_std(hdl, &solo_ctrl_ops, + V4L2_CID_HUE, 0, 255, 1, 128); + if (tw28_has_sharpness(solo_dev, ch)) + v4l2_ctrl_new_std(hdl, &solo_ctrl_ops, + V4L2_CID_SHARPNESS, 0, 15, 1, 0); + v4l2_ctrl_new_std(hdl, &solo_ctrl_ops, + V4L2_CID_MPEG_VIDEO_GOP_SIZE, 1, 255, 1, solo_dev->fps); + v4l2_ctrl_new_custom(hdl, &solo_motion_threshold_ctrl, NULL); + v4l2_ctrl_new_custom(hdl, &solo_motion_enable_ctrl, NULL); + v4l2_ctrl_new_custom(hdl, &solo_osd_text_ctrl, NULL); + if (hdl->error) { + ret = hdl->error; + goto hdl_free; + } + + solo_enc->solo_dev = solo_dev; + solo_enc->ch = ch; + mutex_init(&solo_enc->lock); + spin_lock_init(&solo_enc->av_lock); + INIT_LIST_HEAD(&solo_enc->vidq_active); + solo_enc->fmt = (solo_dev->type == SOLO_DEV_6010) ? + V4L2_PIX_FMT_MPEG4 : V4L2_PIX_FMT_H264; + solo_enc->type = SOLO_ENC_TYPE_STD; + + solo_enc->qp = SOLO_DEFAULT_QP; + solo_enc->gop = solo_dev->fps; + solo_enc->interval = 1; + solo_enc->mode = SOLO_ENC_MODE_CIF; + solo_enc->motion_global = true; + solo_enc->motion_thresh = SOLO_DEF_MOT_THRESH; + for (y = 0; y < SOLO_MOTION_SZ; y++) + for (x = 0; x < SOLO_MOTION_SZ; x++) + solo_enc->motion_thresholds.thresholds[y][x] = + SOLO_DEF_MOT_THRESH; + + solo_enc->vidq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + solo_enc->vidq.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ; + solo_enc->vidq.ops = &solo_enc_video_qops; + solo_enc->vidq.mem_ops = &vb2_dma_sg_memops; + solo_enc->vidq.drv_priv = solo_enc; + solo_enc->vidq.gfp_flags = __GFP_DMA32; + solo_enc->vidq.timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + solo_enc->vidq.buf_struct_size = sizeof(struct solo_vb2_buf); + solo_enc->vidq.lock = &solo_enc->lock; + ret = vb2_queue_init(&solo_enc->vidq); + if (ret) + goto hdl_free; + solo_update_mode(solo_enc); + + spin_lock_init(&solo_enc->motion_lock); + + /* Initialize this per encoder */ + solo_enc->jpeg_len = sizeof(jpeg_header); + memcpy(solo_enc->jpeg_header, jpeg_header, solo_enc->jpeg_len); + + solo_enc->desc_nelts = 32; + solo_enc->desc_items = pci_alloc_consistent(solo_dev->pdev, + sizeof(struct solo_p2m_desc) * + solo_enc->desc_nelts, &solo_enc->desc_dma); + ret = -ENOMEM; + if (solo_enc->desc_items == NULL) + goto hdl_free; + + solo_enc->vfd = video_device_alloc(); + if (!solo_enc->vfd) + goto pci_free; + + *solo_enc->vfd = solo_enc_template; + solo_enc->vfd->v4l2_dev = &solo_dev->v4l2_dev; + solo_enc->vfd->ctrl_handler = hdl; + solo_enc->vfd->queue = &solo_enc->vidq; + solo_enc->vfd->lock = &solo_enc->lock; + set_bit(V4L2_FL_USE_FH_PRIO, &solo_enc->vfd->flags); + video_set_drvdata(solo_enc->vfd, solo_enc); + ret = video_register_device(solo_enc->vfd, VFL_TYPE_GRABBER, nr); + if (ret < 0) + goto vdev_release; + + snprintf(solo_enc->vfd->name, sizeof(solo_enc->vfd->name), + "%s-enc (%i/%i)", SOLO6X10_NAME, solo_dev->vfd->num, + solo_enc->vfd->num); + + return solo_enc; + +vdev_release: + video_device_release(solo_enc->vfd); +pci_free: + pci_free_consistent(solo_enc->solo_dev->pdev, + sizeof(struct solo_p2m_desc) * solo_enc->desc_nelts, + solo_enc->desc_items, solo_enc->desc_dma); +hdl_free: + v4l2_ctrl_handler_free(hdl); + kfree(solo_enc); + return ERR_PTR(ret); +} + +static void solo_enc_free(struct solo_enc_dev *solo_enc) +{ + if (solo_enc == NULL) + return; + + video_unregister_device(solo_enc->vfd); + v4l2_ctrl_handler_free(&solo_enc->hdl); + kfree(solo_enc); +} + +int solo_enc_v4l2_init(struct solo_dev *solo_dev, unsigned nr) +{ + int i; + + init_waitqueue_head(&solo_dev->ring_thread_wait); + + solo_dev->vh_size = sizeof(struct vop_header); + solo_dev->vh_buf = pci_alloc_consistent(solo_dev->pdev, + solo_dev->vh_size, + &solo_dev->vh_dma); + if (solo_dev->vh_buf == NULL) + return -ENOMEM; + + for (i = 0; i < solo_dev->nr_chans; i++) { + solo_dev->v4l2_enc[i] = solo_enc_alloc(solo_dev, i, nr); + if (IS_ERR(solo_dev->v4l2_enc[i])) + break; + } + + if (i != solo_dev->nr_chans) { + int ret = PTR_ERR(solo_dev->v4l2_enc[i]); + while (i--) + solo_enc_free(solo_dev->v4l2_enc[i]); + pci_free_consistent(solo_dev->pdev, solo_dev->vh_size, + solo_dev->vh_buf, solo_dev->vh_dma); + solo_dev->vh_buf = NULL; + return ret; + } + + if (solo_dev->type == SOLO_DEV_6010) + solo_dev->enc_bw_remain = solo_dev->fps * 4 * 4; + else + solo_dev->enc_bw_remain = solo_dev->fps * 4 * 5; + + dev_info(&solo_dev->pdev->dev, "Encoders as /dev/video%d-%d\n", + solo_dev->v4l2_enc[0]->vfd->num, + solo_dev->v4l2_enc[solo_dev->nr_chans - 1]->vfd->num); + + return 0; +} + +void solo_enc_v4l2_exit(struct solo_dev *solo_dev) +{ + int i; + + for (i = 0; i < solo_dev->nr_chans; i++) + solo_enc_free(solo_dev->v4l2_enc[i]); + + if (solo_dev->vh_buf) + pci_free_consistent(solo_dev->pdev, solo_dev->vh_size, + solo_dev->vh_buf, solo_dev->vh_dma); +} diff --git a/drivers/staging/media/solo6x10/solo6x10-v4l2.c b/drivers/staging/media/solo6x10/solo6x10-v4l2.c new file mode 100644 index 000000000000..7b26de3488da --- /dev/null +++ b/drivers/staging/media/solo6x10/solo6x10-v4l2.c @@ -0,0 +1,734 @@ +/* + * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com> + * + * Original author: + * Ben Collins <bcollins@ubuntu.com> + * + * Additional work by: + * John Brooks <john.brooks@bluecherry.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/kthread.h> +#include <linux/freezer.h> + +#include <media/v4l2-ioctl.h> +#include <media/v4l2-common.h> +#include <media/v4l2-event.h> +#include <media/videobuf2-dma-contig.h> + +#include "solo6x10.h" +#include "solo6x10-tw28.h" + +/* Image size is two fields, SOLO_HW_BPL is one horizontal line in hardware */ +#define SOLO_HW_BPL 2048 +#define solo_vlines(__solo) (__solo->video_vsize * 2) +#define solo_image_size(__solo) (solo_bytesperline(__solo) * \ + solo_vlines(__solo)) +#define solo_bytesperline(__solo) (__solo->video_hsize * 2) + +#define MIN_VID_BUFFERS 2 + +static inline void erase_on(struct solo_dev *solo_dev) +{ + solo_reg_write(solo_dev, SOLO_VO_DISP_ERASE, SOLO_VO_DISP_ERASE_ON); + solo_dev->erasing = 1; + solo_dev->frame_blank = 0; +} + +static inline int erase_off(struct solo_dev *solo_dev) +{ + if (!solo_dev->erasing) + return 0; + + /* First time around, assert erase off */ + if (!solo_dev->frame_blank) + solo_reg_write(solo_dev, SOLO_VO_DISP_ERASE, 0); + /* Keep the erasing flag on for 8 frames minimum */ + if (solo_dev->frame_blank++ >= 8) + solo_dev->erasing = 0; + + return 1; +} + +void solo_video_in_isr(struct solo_dev *solo_dev) +{ + wake_up_interruptible_all(&solo_dev->disp_thread_wait); +} + +static void solo_win_setup(struct solo_dev *solo_dev, u8 ch, + int sx, int sy, int ex, int ey, int scale) +{ + if (ch >= solo_dev->nr_chans) + return; + + /* Here, we just keep window/channel the same */ + solo_reg_write(solo_dev, SOLO_VI_WIN_CTRL0(ch), + SOLO_VI_WIN_CHANNEL(ch) | + SOLO_VI_WIN_SX(sx) | + SOLO_VI_WIN_EX(ex) | + SOLO_VI_WIN_SCALE(scale)); + + solo_reg_write(solo_dev, SOLO_VI_WIN_CTRL1(ch), + SOLO_VI_WIN_SY(sy) | + SOLO_VI_WIN_EY(ey)); +} + +static int solo_v4l2_ch_ext_4up(struct solo_dev *solo_dev, u8 idx, int on) +{ + u8 ch = idx * 4; + + if (ch >= solo_dev->nr_chans) + return -EINVAL; + + if (!on) { + u8 i; + for (i = ch; i < ch + 4; i++) + solo_win_setup(solo_dev, i, solo_dev->video_hsize, + solo_vlines(solo_dev), + solo_dev->video_hsize, + solo_vlines(solo_dev), 0); + return 0; + } + + /* Row 1 */ + solo_win_setup(solo_dev, ch, 0, 0, solo_dev->video_hsize / 2, + solo_vlines(solo_dev) / 2, 3); + solo_win_setup(solo_dev, ch + 1, solo_dev->video_hsize / 2, 0, + solo_dev->video_hsize, solo_vlines(solo_dev) / 2, 3); + /* Row 2 */ + solo_win_setup(solo_dev, ch + 2, 0, solo_vlines(solo_dev) / 2, + solo_dev->video_hsize / 2, solo_vlines(solo_dev), 3); + solo_win_setup(solo_dev, ch + 3, solo_dev->video_hsize / 2, + solo_vlines(solo_dev) / 2, solo_dev->video_hsize, + solo_vlines(solo_dev), 3); + + return 0; +} + +static int solo_v4l2_ch_ext_16up(struct solo_dev *solo_dev, int on) +{ + int sy, ysize, hsize, i; + + if (!on) { + for (i = 0; i < 16; i++) + solo_win_setup(solo_dev, i, solo_dev->video_hsize, + solo_vlines(solo_dev), + solo_dev->video_hsize, + solo_vlines(solo_dev), 0); + return 0; + } + + ysize = solo_vlines(solo_dev) / 4; + hsize = solo_dev->video_hsize / 4; + + for (sy = 0, i = 0; i < 4; i++, sy += ysize) { + solo_win_setup(solo_dev, i * 4, 0, sy, hsize, + sy + ysize, 5); + solo_win_setup(solo_dev, (i * 4) + 1, hsize, sy, + hsize * 2, sy + ysize, 5); + solo_win_setup(solo_dev, (i * 4) + 2, hsize * 2, sy, + hsize * 3, sy + ysize, 5); + solo_win_setup(solo_dev, (i * 4) + 3, hsize * 3, sy, + solo_dev->video_hsize, sy + ysize, 5); + } + + return 0; +} + +static int solo_v4l2_ch(struct solo_dev *solo_dev, u8 ch, int on) +{ + u8 ext_ch; + + if (ch < solo_dev->nr_chans) { + solo_win_setup(solo_dev, ch, on ? 0 : solo_dev->video_hsize, + on ? 0 : solo_vlines(solo_dev), + solo_dev->video_hsize, solo_vlines(solo_dev), + on ? 1 : 0); + return 0; + } + + if (ch >= solo_dev->nr_chans + solo_dev->nr_ext) + return -EINVAL; + + ext_ch = ch - solo_dev->nr_chans; + + /* 4up's first */ + if (ext_ch < 4) + return solo_v4l2_ch_ext_4up(solo_dev, ext_ch, on); + + /* Remaining case is 16up for 16-port */ + return solo_v4l2_ch_ext_16up(solo_dev, on); +} + +static int solo_v4l2_set_ch(struct solo_dev *solo_dev, u8 ch) +{ + if (ch >= solo_dev->nr_chans + solo_dev->nr_ext) + return -EINVAL; + + erase_on(solo_dev); + + solo_v4l2_ch(solo_dev, solo_dev->cur_disp_ch, 0); + solo_v4l2_ch(solo_dev, ch, 1); + + solo_dev->cur_disp_ch = ch; + + return 0; +} + +static void solo_fillbuf(struct solo_dev *solo_dev, + struct vb2_buffer *vb) +{ + dma_addr_t vbuf; + unsigned int fdma_addr; + int error = -1; + int i; + + vbuf = vb2_dma_contig_plane_dma_addr(vb, 0); + if (!vbuf) + goto finish_buf; + + if (erase_off(solo_dev)) { + void *p = vb2_plane_vaddr(vb, 0); + int image_size = solo_image_size(solo_dev); + for (i = 0; i < image_size; i += 2) { + ((u8 *)p)[i] = 0x80; + ((u8 *)p)[i + 1] = 0x00; + } + error = 0; + } else { + fdma_addr = SOLO_DISP_EXT_ADDR + (solo_dev->old_write * + (SOLO_HW_BPL * solo_vlines(solo_dev))); + + error = solo_p2m_dma_t(solo_dev, 0, vbuf, fdma_addr, + solo_bytesperline(solo_dev), + solo_vlines(solo_dev), SOLO_HW_BPL); + } + +finish_buf: + if (!error) { + vb2_set_plane_payload(vb, 0, + solo_vlines(solo_dev) * solo_bytesperline(solo_dev)); + vb->v4l2_buf.sequence = solo_dev->sequence++; + v4l2_get_timestamp(&vb->v4l2_buf.timestamp); + } + + vb2_buffer_done(vb, error ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); +} + +static void solo_thread_try(struct solo_dev *solo_dev) +{ + struct solo_vb2_buf *vb; + + /* Only "break" from this loop if slock is held, otherwise + * just return. */ + for (;;) { + unsigned int cur_write; + + cur_write = SOLO_VI_STATUS0_PAGE( + solo_reg_read(solo_dev, SOLO_VI_STATUS0)); + if (cur_write == solo_dev->old_write) + return; + + spin_lock(&solo_dev->slock); + + if (list_empty(&solo_dev->vidq_active)) + break; + + vb = list_first_entry(&solo_dev->vidq_active, struct solo_vb2_buf, + list); + + solo_dev->old_write = cur_write; + list_del(&vb->list); + + spin_unlock(&solo_dev->slock); + + solo_fillbuf(solo_dev, &vb->vb); + } + + assert_spin_locked(&solo_dev->slock); + spin_unlock(&solo_dev->slock); +} + +static int solo_thread(void *data) +{ + struct solo_dev *solo_dev = data; + DECLARE_WAITQUEUE(wait, current); + + set_freezable(); + add_wait_queue(&solo_dev->disp_thread_wait, &wait); + + for (;;) { + long timeout = schedule_timeout_interruptible(HZ); + if (timeout == -ERESTARTSYS || kthread_should_stop()) + break; + solo_thread_try(solo_dev); + try_to_freeze(); + } + + remove_wait_queue(&solo_dev->disp_thread_wait, &wait); + + return 0; +} + +static int solo_start_thread(struct solo_dev *solo_dev) +{ + int ret = 0; + + solo_dev->kthread = kthread_run(solo_thread, solo_dev, SOLO6X10_NAME "_disp"); + + if (IS_ERR(solo_dev->kthread)) { + ret = PTR_ERR(solo_dev->kthread); + solo_dev->kthread = NULL; + return ret; + } + solo_irq_on(solo_dev, SOLO_IRQ_VIDEO_IN); + + return ret; +} + +static void solo_stop_thread(struct solo_dev *solo_dev) +{ + if (!solo_dev->kthread) + return; + + solo_irq_off(solo_dev, SOLO_IRQ_VIDEO_IN); + kthread_stop(solo_dev->kthread); + solo_dev->kthread = NULL; +} + +static int solo_queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt, + unsigned int *num_buffers, unsigned int *num_planes, + unsigned int sizes[], void *alloc_ctxs[]) +{ + struct solo_dev *solo_dev = vb2_get_drv_priv(q); + + sizes[0] = solo_image_size(solo_dev); + alloc_ctxs[0] = solo_dev->alloc_ctx; + *num_planes = 1; + + if (*num_buffers < MIN_VID_BUFFERS) + *num_buffers = MIN_VID_BUFFERS; + + return 0; +} + +static int solo_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct solo_dev *solo_dev = vb2_get_drv_priv(q); + + solo_dev->sequence = 0; + return solo_start_thread(solo_dev); +} + +static int solo_stop_streaming(struct vb2_queue *q) +{ + struct solo_dev *solo_dev = vb2_get_drv_priv(q); + + solo_stop_thread(solo_dev); + INIT_LIST_HEAD(&solo_dev->vidq_active); + return 0; +} + +static void solo_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_queue *vq = vb->vb2_queue; + struct solo_dev *solo_dev = vb2_get_drv_priv(vq); + struct solo_vb2_buf *solo_vb = + container_of(vb, struct solo_vb2_buf, vb); + + spin_lock(&solo_dev->slock); + list_add_tail(&solo_vb->list, &solo_dev->vidq_active); + spin_unlock(&solo_dev->slock); + wake_up_interruptible(&solo_dev->disp_thread_wait); +} + +static const struct vb2_ops solo_video_qops = { + .queue_setup = solo_queue_setup, + .buf_queue = solo_buf_queue, + .start_streaming = solo_start_streaming, + .stop_streaming = solo_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static int solo_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct solo_dev *solo_dev = video_drvdata(file); + + strcpy(cap->driver, SOLO6X10_NAME); + strcpy(cap->card, "Softlogic 6x10"); + snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI:%s", + pci_name(solo_dev->pdev)); + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + return 0; +} + +static int solo_enum_ext_input(struct solo_dev *solo_dev, + struct v4l2_input *input) +{ + static const char * const dispnames_1[] = { "4UP" }; + static const char * const dispnames_2[] = { "4UP-1", "4UP-2" }; + static const char * const dispnames_5[] = { + "4UP-1", "4UP-2", "4UP-3", "4UP-4", "16UP" + }; + const char * const *dispnames; + + if (input->index >= (solo_dev->nr_chans + solo_dev->nr_ext)) + return -EINVAL; + + if (solo_dev->nr_ext == 5) + dispnames = dispnames_5; + else if (solo_dev->nr_ext == 2) + dispnames = dispnames_2; + else + dispnames = dispnames_1; + + snprintf(input->name, sizeof(input->name), "Multi %s", + dispnames[input->index - solo_dev->nr_chans]); + + return 0; +} + +static int solo_enum_input(struct file *file, void *priv, + struct v4l2_input *input) +{ + struct solo_dev *solo_dev = video_drvdata(file); + + if (input->index >= solo_dev->nr_chans) { + int ret = solo_enum_ext_input(solo_dev, input); + if (ret < 0) + return ret; + } else { + snprintf(input->name, sizeof(input->name), "Camera %d", + input->index + 1); + + /* We can only check this for normal inputs */ + if (!tw28_get_video_status(solo_dev, input->index)) + input->status = V4L2_IN_ST_NO_SIGNAL; + } + + input->type = V4L2_INPUT_TYPE_CAMERA; + input->std = solo_dev->vfd->tvnorms; + return 0; +} + +static int solo_set_input(struct file *file, void *priv, unsigned int index) +{ + struct solo_dev *solo_dev = video_drvdata(file); + int ret = solo_v4l2_set_ch(solo_dev, index); + + if (!ret) { + while (erase_off(solo_dev)) + /* Do nothing */; + } + + return ret; +} + +static int solo_get_input(struct file *file, void *priv, unsigned int *index) +{ + struct solo_dev *solo_dev = video_drvdata(file); + + *index = solo_dev->cur_disp_ch; + + return 0; +} + +static int solo_enum_fmt_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + if (f->index) + return -EINVAL; + + f->pixelformat = V4L2_PIX_FMT_UYVY; + strlcpy(f->description, "UYUV 4:2:2 Packed", sizeof(f->description)); + + return 0; +} + +static int solo_try_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct solo_dev *solo_dev = video_drvdata(file); + struct v4l2_pix_format *pix = &f->fmt.pix; + int image_size = solo_image_size(solo_dev); + + if (pix->pixelformat != V4L2_PIX_FMT_UYVY) + return -EINVAL; + + pix->width = solo_dev->video_hsize; + pix->height = solo_vlines(solo_dev); + pix->sizeimage = image_size; + pix->field = V4L2_FIELD_INTERLACED; + pix->pixelformat = V4L2_PIX_FMT_UYVY; + pix->colorspace = V4L2_COLORSPACE_SMPTE170M; + pix->priv = 0; + return 0; +} + +static int solo_set_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct solo_dev *solo_dev = video_drvdata(file); + + if (vb2_is_busy(&solo_dev->vidq)) + return -EBUSY; + + /* For right now, if it doesn't match our running config, + * then fail */ + return solo_try_fmt_cap(file, priv, f); +} + +static int solo_get_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct solo_dev *solo_dev = video_drvdata(file); + struct v4l2_pix_format *pix = &f->fmt.pix; + + pix->width = solo_dev->video_hsize; + pix->height = solo_vlines(solo_dev); + pix->pixelformat = V4L2_PIX_FMT_UYVY; + pix->field = V4L2_FIELD_INTERLACED; + pix->sizeimage = solo_image_size(solo_dev); + pix->colorspace = V4L2_COLORSPACE_SMPTE170M; + pix->bytesperline = solo_bytesperline(solo_dev); + pix->priv = 0; + + return 0; +} + +static int solo_g_std(struct file *file, void *priv, v4l2_std_id *i) +{ + struct solo_dev *solo_dev = video_drvdata(file); + + if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) + *i = V4L2_STD_NTSC_M; + else + *i = V4L2_STD_PAL; + return 0; +} + +int solo_set_video_type(struct solo_dev *solo_dev, bool type) +{ + int i; + + /* Make sure all video nodes are idle */ + if (vb2_is_busy(&solo_dev->vidq)) + return -EBUSY; + for (i = 0; i < solo_dev->nr_chans; i++) + if (vb2_is_busy(&solo_dev->v4l2_enc[i]->vidq)) + return -EBUSY; + solo_dev->video_type = type; + /* Reconfigure for the new standard */ + solo_disp_init(solo_dev); + solo_enc_init(solo_dev); + solo_tw28_init(solo_dev); + for (i = 0; i < solo_dev->nr_chans; i++) + solo_update_mode(solo_dev->v4l2_enc[i]); + return solo_v4l2_set_ch(solo_dev, solo_dev->cur_disp_ch); +} + +static int solo_s_std(struct file *file, void *priv, v4l2_std_id std) +{ + struct solo_dev *solo_dev = video_drvdata(file); + + return solo_set_video_type(solo_dev, std & V4L2_STD_PAL); +} + +static int solo_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct solo_dev *solo_dev = + container_of(ctrl->handler, struct solo_dev, disp_hdl); + + switch (ctrl->id) { + case V4L2_CID_MOTION_TRACE: + if (ctrl->val) { + solo_reg_write(solo_dev, SOLO_VI_MOTION_BORDER, + SOLO_VI_MOTION_Y_ADD | + SOLO_VI_MOTION_Y_VALUE(0x20) | + SOLO_VI_MOTION_CB_VALUE(0x10) | + SOLO_VI_MOTION_CR_VALUE(0x10)); + solo_reg_write(solo_dev, SOLO_VI_MOTION_BAR, + SOLO_VI_MOTION_CR_ADD | + SOLO_VI_MOTION_Y_VALUE(0x10) | + SOLO_VI_MOTION_CB_VALUE(0x80) | + SOLO_VI_MOTION_CR_VALUE(0x10)); + } else { + solo_reg_write(solo_dev, SOLO_VI_MOTION_BORDER, 0); + solo_reg_write(solo_dev, SOLO_VI_MOTION_BAR, 0); + } + return 0; + default: + break; + } + return -EINVAL; +} + +static const struct v4l2_file_operations solo_v4l2_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, + .unlocked_ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops solo_v4l2_ioctl_ops = { + .vidioc_querycap = solo_querycap, + .vidioc_s_std = solo_s_std, + .vidioc_g_std = solo_g_std, + /* Input callbacks */ + .vidioc_enum_input = solo_enum_input, + .vidioc_s_input = solo_set_input, + .vidioc_g_input = solo_get_input, + /* Video capture format callbacks */ + .vidioc_enum_fmt_vid_cap = solo_enum_fmt_cap, + .vidioc_try_fmt_vid_cap = solo_try_fmt_cap, + .vidioc_s_fmt_vid_cap = solo_set_fmt_cap, + .vidioc_g_fmt_vid_cap = solo_get_fmt_cap, + /* Streaming I/O */ + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + /* Logging and events */ + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static struct video_device solo_v4l2_template = { + .name = SOLO6X10_NAME, + .fops = &solo_v4l2_fops, + .ioctl_ops = &solo_v4l2_ioctl_ops, + .minor = -1, + .release = video_device_release, + .tvnorms = V4L2_STD_NTSC_M | V4L2_STD_PAL, +}; + +static const struct v4l2_ctrl_ops solo_ctrl_ops = { + .s_ctrl = solo_s_ctrl, +}; + +static const struct v4l2_ctrl_config solo_motion_trace_ctrl = { + .ops = &solo_ctrl_ops, + .id = V4L2_CID_MOTION_TRACE, + .name = "Motion Detection Trace", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .max = 1, + .step = 1, +}; + +int solo_v4l2_init(struct solo_dev *solo_dev, unsigned nr) +{ + int ret; + int i; + + init_waitqueue_head(&solo_dev->disp_thread_wait); + spin_lock_init(&solo_dev->slock); + mutex_init(&solo_dev->lock); + INIT_LIST_HEAD(&solo_dev->vidq_active); + + solo_dev->vfd = video_device_alloc(); + if (!solo_dev->vfd) + return -ENOMEM; + + *solo_dev->vfd = solo_v4l2_template; + solo_dev->vfd->v4l2_dev = &solo_dev->v4l2_dev; + solo_dev->vfd->queue = &solo_dev->vidq; + solo_dev->vfd->lock = &solo_dev->lock; + v4l2_ctrl_handler_init(&solo_dev->disp_hdl, 1); + v4l2_ctrl_new_custom(&solo_dev->disp_hdl, &solo_motion_trace_ctrl, NULL); + if (solo_dev->disp_hdl.error) { + ret = solo_dev->disp_hdl.error; + goto fail; + } + solo_dev->vfd->ctrl_handler = &solo_dev->disp_hdl; + set_bit(V4L2_FL_USE_FH_PRIO, &solo_dev->vfd->flags); + + video_set_drvdata(solo_dev->vfd, solo_dev); + + solo_dev->vidq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + solo_dev->vidq.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ; + solo_dev->vidq.ops = &solo_video_qops; + solo_dev->vidq.mem_ops = &vb2_dma_contig_memops; + solo_dev->vidq.drv_priv = solo_dev; + solo_dev->vidq.timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + solo_dev->vidq.gfp_flags = __GFP_DMA32; + solo_dev->vidq.buf_struct_size = sizeof(struct solo_vb2_buf); + solo_dev->vidq.lock = &solo_dev->lock; + ret = vb2_queue_init(&solo_dev->vidq); + if (ret < 0) + goto fail; + + solo_dev->alloc_ctx = vb2_dma_contig_init_ctx(&solo_dev->pdev->dev); + if (IS_ERR(solo_dev->alloc_ctx)) { + dev_err(&solo_dev->pdev->dev, "Can't allocate buffer context"); + return PTR_ERR(solo_dev->alloc_ctx); + } + + /* Cycle all the channels and clear */ + for (i = 0; i < solo_dev->nr_chans; i++) { + solo_v4l2_set_ch(solo_dev, i); + while (erase_off(solo_dev)) + /* Do nothing */; + } + + /* Set the default display channel */ + solo_v4l2_set_ch(solo_dev, 0); + while (erase_off(solo_dev)) + /* Do nothing */; + + ret = video_register_device(solo_dev->vfd, VFL_TYPE_GRABBER, nr); + if (ret < 0) + goto fail; + + snprintf(solo_dev->vfd->name, sizeof(solo_dev->vfd->name), "%s (%i)", + SOLO6X10_NAME, solo_dev->vfd->num); + + dev_info(&solo_dev->pdev->dev, "Display as /dev/video%d with " + "%d inputs (%d extended)\n", solo_dev->vfd->num, + solo_dev->nr_chans, solo_dev->nr_ext); + + return 0; + +fail: + video_device_release(solo_dev->vfd); + vb2_dma_contig_cleanup_ctx(solo_dev->alloc_ctx); + v4l2_ctrl_handler_free(&solo_dev->disp_hdl); + solo_dev->vfd = NULL; + return ret; +} + +void solo_v4l2_exit(struct solo_dev *solo_dev) +{ + if (solo_dev->vfd == NULL) + return; + + video_unregister_device(solo_dev->vfd); + vb2_dma_contig_cleanup_ctx(solo_dev->alloc_ctx); + v4l2_ctrl_handler_free(&solo_dev->disp_hdl); + solo_dev->vfd = NULL; +} diff --git a/drivers/staging/media/solo6x10/solo6x10.h b/drivers/staging/media/solo6x10/solo6x10.h index abee7213202f..6f91d2e34b2a 100644 --- a/drivers/staging/media/solo6x10/solo6x10.h +++ b/drivers/staging/media/solo6x10/solo6x10.h @@ -1,6 +1,11 @@ /* - * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com - * Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net> + * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com> + * + * Original author: + * Ben Collins <bcollins@ubuntu.com> + * + * Additional work by: + * John Brooks <john.brooks@bluecherry.net> * * 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 @@ -20,21 +25,23 @@ #ifndef __SOLO6X10_H #define __SOLO6X10_H -#include <linux/version.h> #include <linux/pci.h> #include <linux/i2c.h> -#include <linux/semaphore.h> #include <linux/mutex.h> #include <linux/list.h> #include <linux/wait.h> -#include <linux/delay.h> -#include <linux/slab.h> -#include <asm/io.h> +#include <linux/stringify.h> +#include <linux/io.h> #include <linux/atomic.h> +#include <linux/slab.h> #include <linux/videodev2.h> + #include <media/v4l2-dev.h> -#include <media/videobuf-core.h> -#include "registers.h" +#include <media/v4l2-device.h> +#include <media/v4l2-ctrls.h> +#include <media/videobuf2-core.h> + +#include "solo6x10-regs.h" #ifndef PCI_VENDOR_ID_SOFTLOGIC #define PCI_VENDOR_ID_SOFTLOGIC 0x9413 @@ -58,19 +65,15 @@ #define PCI_DEVICE_ID_BC_6110_16 0x5310 #endif /* Bluecherry */ +/* Used in pci_device_id, and solo_dev->type */ +#define SOLO_DEV_6010 0 +#define SOLO_DEV_6110 1 + #define SOLO6X10_NAME "solo6x10" #define SOLO_MAX_CHANNELS 16 -/* Make sure these two match */ -#define SOLO6X10_VERSION "2.1.0" -#define SOLO6X10_VER_MAJOR 2 -#define SOLO6X10_VER_MINOR 0 -#define SOLO6X10_VER_SUB 0 -#define SOLO6X10_VER_NUM \ - KERNEL_VERSION(SOLO6X10_VER_MAJOR, SOLO6X10_VER_MINOR, SOLO6X10_VER_SUB) - -#define FLAGS_6110 1 +#define SOLO6X10_VERSION "3.0.0" /* * The SOLO6x10 actually has 8 i2c channels, but we only use 2. @@ -84,16 +87,7 @@ /* DMA Engine setup */ #define SOLO_NR_P2M 4 #define SOLO_NR_P2M_DESC 256 -/* MPEG and JPEG share the same interrupt and locks so they must be together - * in the same dma channel. */ -#define SOLO_P2M_DMA_ID_MP4E 0 -#define SOLO_P2M_DMA_ID_JPEG 0 -#define SOLO_P2M_DMA_ID_MP4D 1 -#define SOLO_P2M_DMA_ID_G723D 1 -#define SOLO_P2M_DMA_ID_DISP 2 -#define SOLO_P2M_DMA_ID_OSG 2 -#define SOLO_P2M_DMA_ID_G723E 3 -#define SOLO_P2M_DMA_ID_VIN 3 +#define SOLO_P2M_DESC_SIZE (SOLO_NR_P2M_DESC * 16) /* Encoder standard modes */ #define SOLO_ENC_MODE_CIF 2 @@ -103,23 +97,37 @@ #define SOLO_DEFAULT_GOP 30 #define SOLO_DEFAULT_QP 3 -/* There is 8MB memory available for solo to buffer MPEG4 frames. - * This gives us 512 * 16kbyte queues. */ -#define SOLO_NR_RING_BUFS 512 - -#define SOLO_CLOCK_MHZ 108 - #ifndef V4L2_BUF_FLAG_MOTION_ON -#define V4L2_BUF_FLAG_MOTION_ON 0x0400 -#define V4L2_BUF_FLAG_MOTION_DETECTED 0x0800 -#endif -#ifndef V4L2_CID_MOTION_ENABLE -#define PRIVATE_CIDS -#define V4L2_CID_MOTION_ENABLE (V4L2_CID_PRIVATE_BASE+0) -#define V4L2_CID_MOTION_THRESHOLD (V4L2_CID_PRIVATE_BASE+1) -#define V4L2_CID_MOTION_TRACE (V4L2_CID_PRIVATE_BASE+2) +#define V4L2_BUF_FLAG_MOTION_ON 0x10000 +#define V4L2_BUF_FLAG_MOTION_DETECTED 0x20000 #endif +#define SOLO_CID_CUSTOM_BASE (V4L2_CID_USER_BASE | 0xf000) +#define V4L2_CID_MOTION_MODE (SOLO_CID_CUSTOM_BASE+0) +#define V4L2_CID_MOTION_THRESHOLD (SOLO_CID_CUSTOM_BASE+1) +#define V4L2_CID_MOTION_TRACE (SOLO_CID_CUSTOM_BASE+2) +#define V4L2_CID_OSD_TEXT (SOLO_CID_CUSTOM_BASE+3) + +/* + * Motion thresholds are in a table of 64x64 samples, with + * each sample representing 16x16 pixels of the source. In + * effect, 44x30 samples are used for NTSC, and 44x36 for PAL. + * The 5th sample on the 10th row is (10*64)+5 = 645. + * + * Using a 64x64 array will result in a problem on some architectures like + * the powerpc where the size of the argument is limited to 13 bits. + * Since both PAL and NTSC do not use the full table anyway I've chosen + * to limit the array to 45x45 (45*16 = 720, which is the maximum PAL/NTSC + * width). + */ +#define SOLO_MOTION_SZ (45) +struct solo_motion_thresholds { + __u16 thresholds[SOLO_MOTION_SZ][SOLO_MOTION_SZ]; +}; + +#define SOLO_IOC_G_MOTION_THRESHOLDS _IOR('V', BASE_VIDIOC_PRIVATE+0, struct solo_motion_thresholds) +#define SOLO_IOC_S_MOTION_THRESHOLDS _IOW('V', BASE_VIDIOC_PRIVATE+1, struct solo_motion_thresholds) + enum SOLO_I2C_STATE { IIC_STATE_IDLE, IIC_STATE_START, @@ -128,20 +136,29 @@ enum SOLO_I2C_STATE { IIC_STATE_STOP }; -struct p2m_desc { - u32 ctrl; - u32 ext; - u32 ta; - u32 fa; +/* Defined in Table 4-16, Page 68-69 of the 6010 Datasheet */ +struct solo_p2m_desc { + u32 ctrl; + u32 cfg; + u32 dma_addr; + u32 ext_addr; }; struct solo_p2m_dev { struct mutex mutex; struct completion completion; + int desc_count; + int desc_idx; + struct solo_p2m_desc *descs; int error; }; -#define OSD_TEXT_MAX 30 +#define OSD_TEXT_MAX 44 + +struct solo_vb2_buf { + struct vb2_buffer vb; + struct list_head list; +}; enum solo_enc_types { SOLO_ENC_TYPE_STD, @@ -149,46 +166,61 @@ enum solo_enc_types { }; struct solo_enc_dev { - struct solo_dev *solo_dev; + struct solo_dev *solo_dev; /* V4L2 Items */ + struct v4l2_ctrl_handler hdl; struct video_device *vfd; /* General accounting */ - wait_queue_head_t thread_wait; - spinlock_t lock; - atomic_t readers; + struct mutex lock; + spinlock_t motion_lock; u8 ch; u8 mode, gop, qp, interlaced, interval; - u8 reset_gop; u8 bw_weight; - u8 motion_detected; u16 motion_thresh; + struct solo_motion_thresholds motion_thresholds; + bool motion_global; + bool motion_enabled; u16 width; u16 height; + + /* OSD buffers */ char osd_text[OSD_TEXT_MAX + 1]; -}; + u8 osd_buf[SOLO_EOSD_EXT_SIZE_MAX] + __aligned(4); -struct solo_enc_buf { - u8 vop; - u8 ch; + /* VOP stuff */ + unsigned char vop[64]; + int vop_len; + unsigned char jpeg_header[1024]; + int jpeg_len; + + u32 fmt; enum solo_enc_types type; - u32 off; - u32 size; - u32 jpeg_off; - u32 jpeg_size; - struct timeval ts; + u32 sequence; + struct vb2_queue vidq; + struct list_head vidq_active; + int desc_count; + int desc_nelts; + struct solo_p2m_desc *desc_items; + dma_addr_t desc_dma; + spinlock_t av_lock; }; /* The SOLO6x10 PCI Device */ struct solo_dev { /* General stuff */ struct pci_dev *pdev; + int type; + unsigned int time_sync; + unsigned int usec_lsb; + unsigned int clock_mhz; u8 __iomem *reg_base; int nr_chans; int nr_ext; - u32 flags; u32 irq_mask; u32 motion_mask; spinlock_t reg_io_lock; + struct v4l2_device v4l2_dev; /* tw28xx accounting */ u8 tw2865, tw2864, tw2815; @@ -206,6 +238,9 @@ struct solo_dev { /* P2M DMA Engine */ struct solo_p2m_dev p2m_dev[SOLO_NR_P2M]; + atomic_t p2m_count; + int p2m_jiffies; + unsigned int p2m_timeouts; /* V4L2 Display items */ struct video_device *vfd; @@ -213,15 +248,13 @@ struct solo_dev { unsigned int frame_blank; u8 cur_disp_ch; wait_queue_head_t disp_thread_wait; + struct v4l2_ctrl_handler disp_hdl; /* V4L2 Encoder items */ struct solo_enc_dev *v4l2_enc[SOLO_MAX_CHANNELS]; u16 enc_bw_remain; /* IDX into hw mp4 encoder */ u8 enc_idx; - /* Our software ring of enc buf references */ - u16 enc_wr_idx; - struct solo_enc_buf enc_buf[SOLO_NR_RING_BUFS]; /* Current video settings */ u32 video_type; @@ -230,11 +263,40 @@ struct solo_dev { u16 vin_hstart, vin_vstart; u8 fps; + /* JPEG Qp setting */ + spinlock_t jpeg_qp_lock; + u32 jpeg_qp[2]; + /* Audio components */ struct snd_card *snd_card; struct snd_pcm *snd_pcm; atomic_t snd_users; int g723_hw_idx; + + /* sysfs stuffs */ + struct device dev; + int sdram_size; + struct bin_attribute sdram_attr; + unsigned int sys_config; + + /* Ring thread */ + struct task_struct *ring_thread; + wait_queue_head_t ring_thread_wait; + + /* VOP_HEADER handling */ + void *vh_buf; + dma_addr_t vh_dma; + int vh_size; + + /* Buffer handling */ + struct vb2_queue vidq; + struct vb2_alloc_ctx *alloc_ctx; + u32 sequence; + struct task_struct *kthread; + struct mutex lock; + spinlock_t slock; + int old_write; + struct list_head vidq_active; }; static inline u32 solo_reg_read(struct solo_dev *solo_dev, int reg) @@ -255,7 +317,8 @@ static inline u32 solo_reg_read(struct solo_dev *solo_dev, int reg) return ret; } -static inline void solo_reg_write(struct solo_dev *solo_dev, int reg, u32 data) +static inline void solo_reg_write(struct solo_dev *solo_dev, int reg, + u32 data) { unsigned long flags; u16 val; @@ -270,10 +333,19 @@ static inline void solo_reg_write(struct solo_dev *solo_dev, int reg, u32 data) spin_unlock_irqrestore(&solo_dev->reg_io_lock, flags); } -void solo_irq_on(struct solo_dev *solo_dev, u32 mask); -void solo_irq_off(struct solo_dev *solo_dev, u32 mask); +static inline void solo_irq_on(struct solo_dev *dev, u32 mask) +{ + dev->irq_mask |= mask; + solo_reg_write(dev, SOLO_IRQ_MASK, dev->irq_mask); +} + +static inline void solo_irq_off(struct solo_dev *dev, u32 mask) +{ + dev->irq_mask &= ~mask; + solo_reg_write(dev, SOLO_IRQ_MASK, dev->irq_mask); +} -/* Init/exit routeines for subsystems */ +/* Init/exit routines for subsystems */ int solo_disp_init(struct solo_dev *solo_dev); void solo_disp_exit(struct solo_dev *solo_dev); @@ -286,13 +358,13 @@ void solo_i2c_exit(struct solo_dev *solo_dev); int solo_p2m_init(struct solo_dev *solo_dev); void solo_p2m_exit(struct solo_dev *solo_dev); -int solo_v4l2_init(struct solo_dev *solo_dev); +int solo_v4l2_init(struct solo_dev *solo_dev, unsigned nr); void solo_v4l2_exit(struct solo_dev *solo_dev); int solo_enc_init(struct solo_dev *solo_dev); void solo_enc_exit(struct solo_dev *solo_dev); -int solo_enc_v4l2_init(struct solo_dev *solo_dev); +int solo_enc_v4l2_init(struct solo_dev *solo_dev, unsigned nr); void solo_enc_v4l2_exit(struct solo_dev *solo_dev); int solo_g723_init(struct solo_dev *solo_dev); @@ -301,7 +373,7 @@ void solo_g723_exit(struct solo_dev *solo_dev); /* ISR's */ int solo_i2c_isr(struct solo_dev *solo_dev); void solo_p2m_isr(struct solo_dev *solo_dev, int id); -void solo_p2m_error_isr(struct solo_dev *solo_dev, u32 status); +void solo_p2m_error_isr(struct solo_dev *solo_dev); void solo_enc_v4l2_isr(struct solo_dev *solo_dev); void solo_g723_isr(struct solo_dev *solo_dev); void solo_motion_isr(struct solo_dev *solo_dev); @@ -313,24 +385,43 @@ void solo_i2c_writebyte(struct solo_dev *solo_dev, int id, u8 addr, u8 off, u8 data); /* P2M DMA */ -int solo_p2m_dma_t(struct solo_dev *solo_dev, u8 id, int wr, - dma_addr_t dma_addr, u32 ext_addr, u32 size); -int solo_p2m_dma(struct solo_dev *solo_dev, u8 id, int wr, - void *sys_addr, u32 ext_addr, u32 size); -int solo_p2m_dma_sg(struct solo_dev *solo_dev, u8 id, - struct p2m_desc *pdesc, int wr, - struct scatterlist *sglist, u32 sg_off, - u32 ext_addr, u32 size); -void solo_p2m_push_desc(struct p2m_desc *desc, int wr, dma_addr_t dma_addr, - u32 ext_addr, u32 size, int repeat, u32 ext_size); -int solo_p2m_dma_desc(struct solo_dev *solo_dev, u8 id, - struct p2m_desc *desc, int desc_count); +int solo_p2m_dma_t(struct solo_dev *solo_dev, int wr, + dma_addr_t dma_addr, u32 ext_addr, u32 size, + int repeat, u32 ext_size); +int solo_p2m_dma(struct solo_dev *solo_dev, int wr, + void *sys_addr, u32 ext_addr, u32 size, + int repeat, u32 ext_size); +void solo_p2m_fill_desc(struct solo_p2m_desc *desc, int wr, + dma_addr_t dma_addr, u32 ext_addr, u32 size, + int repeat, u32 ext_size); +int solo_p2m_dma_desc(struct solo_dev *solo_dev, + struct solo_p2m_desc *desc, dma_addr_t desc_dma, + int desc_cnt); + +/* Global s_std ioctl */ +int solo_set_video_type(struct solo_dev *solo_dev, bool type); +void solo_update_mode(struct solo_enc_dev *solo_enc); /* Set the threshold for motion detection */ -void solo_set_motion_threshold(struct solo_dev *solo_dev, u8 ch, u16 val); +int solo_set_motion_threshold(struct solo_dev *solo_dev, u8 ch, u16 val); +int solo_set_motion_block(struct solo_dev *solo_dev, u8 ch, + const struct solo_motion_thresholds *thresholds); #define SOLO_DEF_MOT_THRESH 0x0300 /* Write text on OSD */ int solo_osd_print(struct solo_enc_dev *solo_enc); +/* EEPROM commands */ +unsigned int solo_eeprom_ewen(struct solo_dev *solo_dev, int w_en); +unsigned short solo_eeprom_read(struct solo_dev *solo_dev, int loc); +int solo_eeprom_write(struct solo_dev *solo_dev, int loc, + unsigned short data); + +/* JPEG Qp functions */ +void solo_s_jpeg_qp(struct solo_dev *solo_dev, unsigned int ch, + unsigned int qp); +int solo_g_jpeg_qp(struct solo_dev *solo_dev, unsigned int ch); + +#define CHK_FLAGS(v, flags) (((v) & (flags)) == (flags)) + #endif /* __SOLO6X10_H */ diff --git a/drivers/staging/media/solo6x10/v4l2-enc.c b/drivers/staging/media/solo6x10/v4l2-enc.c deleted file mode 100644 index 4977e869d5b7..000000000000 --- a/drivers/staging/media/solo6x10/v4l2-enc.c +++ /dev/null @@ -1,1829 +0,0 @@ -/* - * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com - * Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/kthread.h> -#include <linux/freezer.h> -#include <media/v4l2-ioctl.h> -#include <media/v4l2-common.h> -#include <media/videobuf-dma-sg.h> -#include "solo6x10.h" -#include "tw28.h" -#include "solo6x10-jpeg.h" - -#define MIN_VID_BUFFERS 4 -#define FRAME_BUF_SIZE (128 * 1024) -#define MP4_QS 16 - -static int solo_enc_thread(void *data); - -extern unsigned video_nr; - -struct solo_enc_fh { - struct solo_enc_dev *enc; - u32 fmt; - u16 rd_idx; - u8 enc_on; - enum solo_enc_types type; - struct videobuf_queue vidq; - struct list_head vidq_active; - struct task_struct *kthread; - struct p2m_desc desc[SOLO_NR_P2M_DESC]; -}; - -static const u32 solo_user_ctrls[] = { - V4L2_CID_BRIGHTNESS, - V4L2_CID_CONTRAST, - V4L2_CID_SATURATION, - V4L2_CID_HUE, - V4L2_CID_SHARPNESS, - 0 -}; - -static const u32 solo_mpeg_ctrls[] = { - V4L2_CID_MPEG_VIDEO_ENCODING, - V4L2_CID_MPEG_VIDEO_GOP_SIZE, - 0 -}; - -static const u32 solo_private_ctrls[] = { - V4L2_CID_MOTION_ENABLE, - V4L2_CID_MOTION_THRESHOLD, - 0 -}; - -static const u32 solo_fmtx_ctrls[] = { - V4L2_CID_RDS_TX_RADIO_TEXT, - 0 -}; - -static const u32 *solo_ctrl_classes[] = { - solo_user_ctrls, - solo_mpeg_ctrls, - solo_fmtx_ctrls, - solo_private_ctrls, - NULL -}; - -static int solo_is_motion_on(struct solo_enc_dev *solo_enc) -{ - struct solo_dev *solo_dev = solo_enc->solo_dev; - u8 ch = solo_enc->ch; - - if (solo_dev->motion_mask & (1 << ch)) - return 1; - return 0; -} - -static void solo_motion_toggle(struct solo_enc_dev *solo_enc, int on) -{ - struct solo_dev *solo_dev = solo_enc->solo_dev; - u8 ch = solo_enc->ch; - - spin_lock(&solo_enc->lock); - - if (on) - solo_dev->motion_mask |= (1 << ch); - else - solo_dev->motion_mask &= ~(1 << ch); - - /* Do this regardless of if we are turning on or off */ - solo_reg_write(solo_enc->solo_dev, SOLO_VI_MOT_CLEAR, - 1 << solo_enc->ch); - solo_enc->motion_detected = 0; - - solo_reg_write(solo_dev, SOLO_VI_MOT_ADR, - SOLO_VI_MOTION_EN(solo_dev->motion_mask) | - (SOLO_MOTION_EXT_ADDR(solo_dev) >> 16)); - - if (solo_dev->motion_mask) - solo_irq_on(solo_dev, SOLO_IRQ_MOTION); - else - solo_irq_off(solo_dev, SOLO_IRQ_MOTION); - - spin_unlock(&solo_enc->lock); -} - -/* Should be called with solo_enc->lock held */ -static void solo_update_mode(struct solo_enc_dev *solo_enc) -{ - struct solo_dev *solo_dev = solo_enc->solo_dev; - - assert_spin_locked(&solo_enc->lock); - - solo_enc->interlaced = (solo_enc->mode & 0x08) ? 1 : 0; - solo_enc->bw_weight = max(solo_dev->fps / solo_enc->interval, 1); - - switch (solo_enc->mode) { - case SOLO_ENC_MODE_CIF: - solo_enc->width = solo_dev->video_hsize >> 1; - solo_enc->height = solo_dev->video_vsize; - break; - case SOLO_ENC_MODE_D1: - solo_enc->width = solo_dev->video_hsize; - solo_enc->height = solo_dev->video_vsize << 1; - solo_enc->bw_weight <<= 2; - break; - default: - WARN(1, "mode is unknown\n"); - } -} - -/* Should be called with solo_enc->lock held */ -static int solo_enc_on(struct solo_enc_fh *fh) -{ - struct solo_enc_dev *solo_enc = fh->enc; - u8 ch = solo_enc->ch; - struct solo_dev *solo_dev = solo_enc->solo_dev; - u8 interval; - - assert_spin_locked(&solo_enc->lock); - - if (fh->enc_on) - return 0; - - solo_update_mode(solo_enc); - - /* Make sure to bw check on first reader */ - if (!atomic_read(&solo_enc->readers)) { - if (solo_enc->bw_weight > solo_dev->enc_bw_remain) - return -EBUSY; - else - solo_dev->enc_bw_remain -= solo_enc->bw_weight; - } - - fh->enc_on = 1; - fh->rd_idx = solo_enc->solo_dev->enc_wr_idx; - - if (fh->type == SOLO_ENC_TYPE_EXT) - solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(ch), 1); - - if (atomic_inc_return(&solo_enc->readers) > 1) - return 0; - - /* Disable all encoding for this channel */ - solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(ch), 0); - - /* Common for both std and ext encoding */ - solo_reg_write(solo_dev, SOLO_VE_CH_INTL(ch), - solo_enc->interlaced ? 1 : 0); - - if (solo_enc->interlaced) - interval = solo_enc->interval - 1; - else - interval = solo_enc->interval; - - /* Standard encoding only */ - solo_reg_write(solo_dev, SOLO_VE_CH_GOP(ch), solo_enc->gop); - solo_reg_write(solo_dev, SOLO_VE_CH_QP(ch), solo_enc->qp); - solo_reg_write(solo_dev, SOLO_CAP_CH_INTV(ch), interval); - - /* Extended encoding only */ - solo_reg_write(solo_dev, SOLO_VE_CH_GOP_E(ch), solo_enc->gop); - solo_reg_write(solo_dev, SOLO_VE_CH_QP_E(ch), solo_enc->qp); - solo_reg_write(solo_dev, SOLO_CAP_CH_INTV_E(ch), interval); - - /* Enables the standard encoder */ - solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(ch), solo_enc->mode); - - /* Settle down Beavis... */ - mdelay(10); - - return 0; -} - -static void solo_enc_off(struct solo_enc_fh *fh) -{ - struct solo_enc_dev *solo_enc = fh->enc; - struct solo_dev *solo_dev = solo_enc->solo_dev; - - if (!fh->enc_on) - return; - - if (fh->kthread) { - kthread_stop(fh->kthread); - fh->kthread = NULL; - } - - solo_dev->enc_bw_remain += solo_enc->bw_weight; - fh->enc_on = 0; - - if (atomic_dec_return(&solo_enc->readers) > 0) - return; - - solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(solo_enc->ch), 0); - solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(solo_enc->ch), 0); -} - -static int solo_start_fh_thread(struct solo_enc_fh *fh) -{ - struct solo_enc_dev *solo_enc = fh->enc; - - fh->kthread = kthread_run(solo_enc_thread, fh, SOLO6X10_NAME "_enc"); - - /* Oops, we had a problem */ - if (IS_ERR(fh->kthread)) { - spin_lock(&solo_enc->lock); - solo_enc_off(fh); - spin_unlock(&solo_enc->lock); - - return PTR_ERR(fh->kthread); - } - - return 0; -} - -static void enc_reset_gop(struct solo_dev *solo_dev, u8 ch) -{ - BUG_ON(ch >= solo_dev->nr_chans); - solo_reg_write(solo_dev, SOLO_VE_CH_GOP(ch), 1); - solo_dev->v4l2_enc[ch]->reset_gop = 1; -} - -static int enc_gop_reset(struct solo_dev *solo_dev, u8 ch, u8 vop) -{ - BUG_ON(ch >= solo_dev->nr_chans); - if (!solo_dev->v4l2_enc[ch]->reset_gop) - return 0; - if (vop) - return 1; - solo_dev->v4l2_enc[ch]->reset_gop = 0; - solo_reg_write(solo_dev, SOLO_VE_CH_GOP(ch), - solo_dev->v4l2_enc[ch]->gop); - return 0; -} - -static void enc_write_sg(struct scatterlist *sglist, void *buf, int size) -{ - struct scatterlist *sg; - u8 *src = buf; - - for (sg = sglist; sg && size > 0; sg = sg_next(sg)) { - u8 *p = sg_virt(sg); - size_t len = sg_dma_len(sg); - int i; - - for (i = 0; i < len && size; i++) - p[i] = *(src++); - } -} - -static int enc_get_mpeg_dma_sg(struct solo_dev *solo_dev, - struct p2m_desc *desc, - struct scatterlist *sglist, int skip, - unsigned int off, unsigned int size) -{ - int ret; - - if (off > SOLO_MP4E_EXT_SIZE(solo_dev)) - return -EINVAL; - - if (off + size <= SOLO_MP4E_EXT_SIZE(solo_dev)) { - return solo_p2m_dma_sg(solo_dev, SOLO_P2M_DMA_ID_MP4E, - desc, 0, sglist, skip, - SOLO_MP4E_EXT_ADDR(solo_dev) + off, size); - } - - /* Buffer wrap */ - ret = solo_p2m_dma_sg(solo_dev, SOLO_P2M_DMA_ID_MP4E, desc, 0, - sglist, skip, SOLO_MP4E_EXT_ADDR(solo_dev) + off, - SOLO_MP4E_EXT_SIZE(solo_dev) - off); - - ret |= solo_p2m_dma_sg(solo_dev, SOLO_P2M_DMA_ID_MP4E, desc, 0, - sglist, skip + SOLO_MP4E_EXT_SIZE(solo_dev) - off, - SOLO_MP4E_EXT_ADDR(solo_dev), - size + off - SOLO_MP4E_EXT_SIZE(solo_dev)); - - return ret; -} - -static int enc_get_mpeg_dma_t(struct solo_dev *solo_dev, - dma_addr_t buf, unsigned int off, - unsigned int size) -{ - int ret; - - if (off > SOLO_MP4E_EXT_SIZE(solo_dev)) - return -EINVAL; - - if (off + size <= SOLO_MP4E_EXT_SIZE(solo_dev)) { - return solo_p2m_dma_t(solo_dev, SOLO_P2M_DMA_ID_MP4E, 0, buf, - SOLO_MP4E_EXT_ADDR(solo_dev) + off, size); - } - - /* Buffer wrap */ - ret = solo_p2m_dma_t(solo_dev, SOLO_P2M_DMA_ID_MP4E, 0, buf, - SOLO_MP4E_EXT_ADDR(solo_dev) + off, - SOLO_MP4E_EXT_SIZE(solo_dev) - off); - - ret |= solo_p2m_dma_t(solo_dev, SOLO_P2M_DMA_ID_MP4E, 0, - buf + SOLO_MP4E_EXT_SIZE(solo_dev) - off, - SOLO_MP4E_EXT_ADDR(solo_dev), - size + off - SOLO_MP4E_EXT_SIZE(solo_dev)); - - return ret; -} - -static int enc_get_mpeg_dma(struct solo_dev *solo_dev, void *buf, - unsigned int off, unsigned int size) -{ - int ret; - - dma_addr_t dma_addr = pci_map_single(solo_dev->pdev, buf, size, - PCI_DMA_FROMDEVICE); - ret = enc_get_mpeg_dma_t(solo_dev, dma_addr, off, size); - pci_unmap_single(solo_dev->pdev, dma_addr, size, PCI_DMA_FROMDEVICE); - - return ret; -} - -static int enc_get_jpeg_dma_sg(struct solo_dev *solo_dev, - struct p2m_desc *desc, - struct scatterlist *sglist, int skip, - unsigned int off, unsigned int size) -{ - int ret; - - if (off > SOLO_JPEG_EXT_SIZE(solo_dev)) - return -EINVAL; - - if (off + size <= SOLO_JPEG_EXT_SIZE(solo_dev)) { - return solo_p2m_dma_sg(solo_dev, SOLO_P2M_DMA_ID_JPEG, - desc, 0, sglist, skip, - SOLO_JPEG_EXT_ADDR(solo_dev) + off, size); - } - - /* Buffer wrap */ - ret = solo_p2m_dma_sg(solo_dev, SOLO_P2M_DMA_ID_JPEG, desc, 0, - sglist, skip, SOLO_JPEG_EXT_ADDR(solo_dev) + off, - SOLO_JPEG_EXT_SIZE(solo_dev) - off); - - ret |= solo_p2m_dma_sg(solo_dev, SOLO_P2M_DMA_ID_JPEG, desc, 0, - sglist, skip + SOLO_JPEG_EXT_SIZE(solo_dev) - off, - SOLO_JPEG_EXT_ADDR(solo_dev), - size + off - SOLO_JPEG_EXT_SIZE(solo_dev)); - - return ret; -} - -/* Returns true of __chk is within the first __range bytes of __off */ -#define OFF_IN_RANGE(__off, __range, __chk) \ - ((__off <= __chk) && ((__off + __range) >= __chk)) - -static void solo_jpeg_header(struct solo_enc_dev *solo_enc, - struct videobuf_dmabuf *vbuf) -{ - struct scatterlist *sg; - void *src = jpeg_header; - size_t copied = 0; - size_t to_copy = sizeof(jpeg_header); - - for (sg = vbuf->sglist; sg && copied < to_copy; sg = sg_next(sg)) { - size_t this_copy = min(sg_dma_len(sg), - (unsigned int)(to_copy - copied)); - u8 *p = sg_virt(sg); - - memcpy(p, src + copied, this_copy); - - if (OFF_IN_RANGE(copied, this_copy, SOF0_START + 5)) - p[(SOF0_START + 5) - copied] = - 0xff & (solo_enc->height >> 8); - if (OFF_IN_RANGE(copied, this_copy, SOF0_START + 6)) - p[(SOF0_START + 6) - copied] = 0xff & solo_enc->height; - if (OFF_IN_RANGE(copied, this_copy, SOF0_START + 7)) - p[(SOF0_START + 7) - copied] = - 0xff & (solo_enc->width >> 8); - if (OFF_IN_RANGE(copied, this_copy, SOF0_START + 8)) - p[(SOF0_START + 8) - copied] = 0xff & solo_enc->width; - - copied += this_copy; - } -} - -static int solo_fill_jpeg(struct solo_enc_fh *fh, struct solo_enc_buf *enc_buf, - struct videobuf_buffer *vb, - struct videobuf_dmabuf *vbuf) -{ - struct solo_dev *solo_dev = fh->enc->solo_dev; - int size = enc_buf->jpeg_size; - - /* Copy the header first (direct write) */ - solo_jpeg_header(fh->enc, vbuf); - - vb->size = size + sizeof(jpeg_header); - - /* Grab the jpeg frame */ - return enc_get_jpeg_dma_sg(solo_dev, fh->desc, vbuf->sglist, - sizeof(jpeg_header), - enc_buf->jpeg_off, size); -} - -static inline int vop_interlaced(__le32 *vh) -{ - return (__le32_to_cpu(vh[0]) >> 30) & 1; -} - -static inline u32 vop_size(__le32 *vh) -{ - return __le32_to_cpu(vh[0]) & 0xFFFFF; -} - -static inline u8 vop_hsize(__le32 *vh) -{ - return (__le32_to_cpu(vh[1]) >> 8) & 0xFF; -} - -static inline u8 vop_vsize(__le32 *vh) -{ - return __le32_to_cpu(vh[1]) & 0xFF; -} - -/* must be called with *bits % 8 = 0 */ -static void write_bytes(u8 **out, unsigned *bits, const u8 *src, unsigned count) -{ - memcpy(*out, src, count); - *out += count; - *bits += count * 8; -} - -static void write_bits(u8 **out, unsigned *bits, u32 value, unsigned count) -{ - - value <<= 32 - count; // shift to the right - - while (count--) { - **out <<= 1; - **out |= !!(value & (1 << 31)); /* MSB */ - value <<= 1; - if (++(*bits) % 8 == 0) - (*out)++; - } -} - -static void write_ue(u8 **out, unsigned *bits, unsigned value) /* H.264 only */ -{ - uint32_t max = 0, cnt = 0; - - while (value > max) { - max = (max + 2) * 2 - 2; - cnt++; - } - write_bits(out, bits, 1, cnt + 1); - write_bits(out, bits, ~(max - value), cnt); -} - -static void write_se(u8 **out, unsigned *bits, int value) /* H.264 only */ -{ - if (value <= 0) - write_ue(out, bits, -value * 2); - else - write_ue(out, bits, value * 2 - 1); -} - -static void write_mpeg4_end(u8 **out, unsigned *bits) -{ - write_bits(out, bits, 0, 1); - /* align on 32-bit boundary */ - if (*bits % 32) - write_bits(out, bits, 0xFFFFFFFF, 32 - *bits % 32); -} - -static void write_h264_end(u8 **out, unsigned *bits, int align) -{ - write_bits(out, bits, 1, 1); - while ((*bits) % 8) - write_bits(out, bits, 0, 1); - if (align) - while ((*bits) % 32) - write_bits(out, bits, 0, 1); -} - -static void mpeg4_write_vol(u8 **out, struct solo_dev *solo_dev, - __le32 *vh, unsigned fps, unsigned interval) -{ - static const u8 hdr[] = { - 0, 0, 1, 0x00 /* video_object_start_code */, - 0, 0, 1, 0x20 /* video_object_layer_start_code */ - }; - unsigned bits = 0; - unsigned width = vop_hsize(vh) << 4; - unsigned height = vop_vsize(vh) << 4; - unsigned interlaced = vop_interlaced(vh); - - write_bytes(out, &bits, hdr, sizeof(hdr)); - write_bits(out, &bits, 0, 1); /* random_accessible_vol */ - write_bits(out, &bits, 0x04, 8); /* video_object_type_indication: main */ - write_bits(out, &bits, 1, 1); /* is_object_layer_identifier */ - write_bits(out, &bits, 2, 4); /* video_object_layer_verid: table V2-39 */ - write_bits(out, &bits, 0, 3); /* video_object_layer_priority */ - if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) - write_bits(out, &bits, 3, 4); /* aspect_ratio_info, assuming 4:3 */ - else - write_bits(out, &bits, 2, 4); - write_bits(out, &bits, 1, 1); /* vol_control_parameters */ - write_bits(out, &bits, 1, 2); /* chroma_format: 4:2:0 */ - write_bits(out, &bits, 1, 1); /* low_delay */ - write_bits(out, &bits, 0, 1); /* vbv_parameters */ - write_bits(out, &bits, 0, 2); /* video_object_layer_shape: rectangular */ - write_bits(out, &bits, 1, 1); /* marker_bit */ - write_bits(out, &bits, fps, 16); /* vop_time_increment_resolution */ - write_bits(out, &bits, 1, 1); /* marker_bit */ - write_bits(out, &bits, 1, 1); /* fixed_vop_rate */ - write_bits(out, &bits, interval, 15); /* fixed_vop_time_increment */ - write_bits(out, &bits, 1, 1); /* marker_bit */ - write_bits(out, &bits, width, 13); /* video_object_layer_width */ - write_bits(out, &bits, 1, 1); /* marker_bit */ - write_bits(out, &bits, height, 13); /* video_object_layer_height */ - write_bits(out, &bits, 1, 1); /* marker_bit */ - write_bits(out, &bits, interlaced, 1); /* interlaced */ - write_bits(out, &bits, 1, 1); /* obmc_disable */ - write_bits(out, &bits, 0, 2); /* sprite_enable */ - write_bits(out, &bits, 0, 1); /* not_8_bit */ - write_bits(out, &bits, 1, 0); /* quant_type */ - write_bits(out, &bits, 0, 1); /* load_intra_quant_mat */ - write_bits(out, &bits, 0, 1); /* load_nonintra_quant_mat */ - write_bits(out, &bits, 0, 1); /* quarter_sample */ - write_bits(out, &bits, 1, 1); /* complexity_estimation_disable */ - write_bits(out, &bits, 1, 1); /* resync_marker_disable */ - write_bits(out, &bits, 0, 1); /* data_partitioned */ - write_bits(out, &bits, 0, 1); /* newpred_enable */ - write_bits(out, &bits, 0, 1); /* reduced_resolution_vop_enable */ - write_bits(out, &bits, 0, 1); /* scalability */ - write_mpeg4_end(out, &bits); -} - -static void h264_write_vol(u8 **out, struct solo_dev *solo_dev, __le32 *vh) -{ - static const u8 sps[] = { - 0, 0, 0, 1 /* start code */, 0x67, 66 /* profile_idc */, - 0 /* constraints */, 30 /* level_idc */ - }; - static const u8 pps[] = { - 0, 0, 0, 1 /* start code */, 0x68 - }; - - unsigned bits = 0; - unsigned mbs_w = vop_hsize(vh); - unsigned mbs_h = vop_vsize(vh); - - write_bytes(out, &bits, sps, sizeof(sps)); - write_ue(out, &bits, 0); /* seq_parameter_set_id */ - write_ue(out, &bits, 5); /* log2_max_frame_num_minus4 */ - write_ue(out, &bits, 0); /* pic_order_cnt_type */ - write_ue(out, &bits, 6); /* log2_max_pic_order_cnt_lsb_minus4 */ - write_ue(out, &bits, 1); /* max_num_ref_frames */ - write_bits(out, &bits, 0, 1); /* gaps_in_frame_num_value_allowed_flag */ - write_ue(out, &bits, mbs_w - 1); /* pic_width_in_mbs_minus1 */ - write_ue(out, &bits, mbs_h - 1); /* pic_height_in_map_units_minus1 */ - write_bits(out, &bits, 1, 1); /* frame_mbs_only_flag */ - write_bits(out, &bits, 1, 1); /* direct_8x8_frame_field_flag */ - write_bits(out, &bits, 0, 1); /* frame_cropping_flag */ - write_bits(out, &bits, 0, 1); /* vui_parameters_present_flag */ - write_h264_end(out, &bits, 0); - - write_bytes(out, &bits, pps, sizeof(pps)); - write_ue(out, &bits, 0); /* pic_parameter_set_id */ - write_ue(out, &bits, 0); /* seq_parameter_set_id */ - write_bits(out, &bits, 0, 1); /* entropy_coding_mode_flag */ - write_bits(out, &bits, 0, 1); /* bottom_field_pic_order_in_frame_present_flag */ - write_ue(out, &bits, 0); /* num_slice_groups_minus1 */ - write_ue(out, &bits, 0); /* num_ref_idx_l0_default_active_minus1 */ - write_ue(out, &bits, 0); /* num_ref_idx_l1_default_active_minus1 */ - write_bits(out, &bits, 0, 1); /* weighted_pred_flag */ - write_bits(out, &bits, 0, 2); /* weighted_bipred_idc */ - write_se(out, &bits, 0); /* pic_init_qp_minus26 */ - write_se(out, &bits, 0); /* pic_init_qs_minus26 */ - write_se(out, &bits, 2); /* chroma_qp_index_offset */ - write_bits(out, &bits, 0, 1); /* deblocking_filter_control_present_flag */ - write_bits(out, &bits, 1, 1); /* constrained_intra_pred_flag */ - write_bits(out, &bits, 0, 1); /* redundant_pic_cnt_present_flag */ - write_h264_end(out, &bits, 1); -} - -static int solo_fill_mpeg(struct solo_enc_fh *fh, struct solo_enc_buf *enc_buf, - struct videobuf_buffer *vb, - struct videobuf_dmabuf *vbuf) -{ - struct solo_enc_dev *solo_enc = fh->enc; - struct solo_dev *solo_dev = solo_enc->solo_dev; - -#define VH_WORDS 16 -#define MAX_VOL_HEADER_LENGTH 64 - - __le32 vh[VH_WORDS]; - int ret; - int frame_size, frame_off; - int skip = 0; - - if (WARN_ON_ONCE(enc_buf->size <= sizeof(vh))) - return -EINVAL; - - /* First get the hardware vop header (not real mpeg) */ - ret = enc_get_mpeg_dma(solo_dev, vh, enc_buf->off, sizeof(vh)); - if (WARN_ON_ONCE(ret)) - return ret; - - if (WARN_ON_ONCE(vop_size(vh) > enc_buf->size)) - return -EINVAL; - - vb->width = vop_hsize(vh) << 4; - vb->height = vop_vsize(vh) << 4; - vb->size = vop_size(vh); - - /* If this is a key frame, add extra m4v header */ - if (!enc_buf->vop) { - u8 header[MAX_VOL_HEADER_LENGTH], *out = header; - - if (solo_dev->flags & FLAGS_6110) - h264_write_vol(&out, solo_dev, vh); - else - mpeg4_write_vol(&out, solo_dev, vh, - solo_dev->fps * 1000, - solo_enc->interval * 1000); - skip = out - header; - enc_write_sg(vbuf->sglist, header, skip); - /* Adjust the dma buffer past this header */ - vb->size += skip; - } - - /* Now get the actual mpeg payload */ - frame_off = (enc_buf->off + sizeof(vh)) % SOLO_MP4E_EXT_SIZE(solo_dev); - frame_size = enc_buf->size - sizeof(vh); - - ret = enc_get_mpeg_dma_sg(solo_dev, fh->desc, vbuf->sglist, - skip, frame_off, frame_size); - WARN_ON_ONCE(ret); - - return ret; -} - -static void solo_enc_fillbuf(struct solo_enc_fh *fh, - struct videobuf_buffer *vb) -{ - struct solo_enc_dev *solo_enc = fh->enc; - struct solo_dev *solo_dev = solo_enc->solo_dev; - struct solo_enc_buf *enc_buf = NULL; - struct videobuf_dmabuf *vbuf; - int ret; - int error = 1; - u16 idx = fh->rd_idx; - - while (idx != solo_dev->enc_wr_idx) { - struct solo_enc_buf *ebuf = &solo_dev->enc_buf[idx]; - - idx = (idx + 1) % SOLO_NR_RING_BUFS; - - if (ebuf->ch != solo_enc->ch) - continue; - - if (fh->fmt == V4L2_PIX_FMT_MPEG) { - if (fh->type == ebuf->type) { - enc_buf = ebuf; - break; - } - } else { - /* For mjpeg, keep reading to the newest frame */ - enc_buf = ebuf; - } - } - - fh->rd_idx = idx; - - if (WARN_ON_ONCE(!enc_buf)) - goto buf_err; - - if ((fh->fmt == V4L2_PIX_FMT_MPEG && - vb->bsize < enc_buf->size) || - (fh->fmt == V4L2_PIX_FMT_MJPEG && - vb->bsize < (enc_buf->jpeg_size + sizeof(jpeg_header)))) { - WARN_ON_ONCE(1); - goto buf_err; - } - - vbuf = videobuf_to_dma(vb); - if (WARN_ON_ONCE(!vbuf)) - goto buf_err; - - if (fh->fmt == V4L2_PIX_FMT_MPEG) - ret = solo_fill_mpeg(fh, enc_buf, vb, vbuf); - else - ret = solo_fill_jpeg(fh, enc_buf, vb, vbuf); - - if (!ret) - error = 0; - -buf_err: - if (error) { - vb->state = VIDEOBUF_ERROR; - } else { - vb->field_count++; - vb->ts = enc_buf->ts; - vb->state = VIDEOBUF_DONE; - } - - wake_up(&vb->done); - - return; -} - -static void solo_enc_thread_try(struct solo_enc_fh *fh) -{ - struct solo_enc_dev *solo_enc = fh->enc; - struct solo_dev *solo_dev = solo_enc->solo_dev; - struct videobuf_buffer *vb; - - for (;;) { - spin_lock(&solo_enc->lock); - - if (fh->rd_idx == solo_dev->enc_wr_idx) - break; - - if (list_empty(&fh->vidq_active)) - break; - - vb = list_first_entry(&fh->vidq_active, - struct videobuf_buffer, queue); - - if (!waitqueue_active(&vb->done)) - break; - - list_del(&vb->queue); - - spin_unlock(&solo_enc->lock); - - solo_enc_fillbuf(fh, vb); - } - - assert_spin_locked(&solo_enc->lock); - spin_unlock(&solo_enc->lock); -} - -static int solo_enc_thread(void *data) -{ - struct solo_enc_fh *fh = data; - struct solo_enc_dev *solo_enc = fh->enc; - DECLARE_WAITQUEUE(wait, current); - - set_freezable(); - add_wait_queue(&solo_enc->thread_wait, &wait); - - for (;;) { - long timeout = schedule_timeout_interruptible(HZ); - if (timeout == -ERESTARTSYS || kthread_should_stop()) - break; - solo_enc_thread_try(fh); - try_to_freeze(); - } - - remove_wait_queue(&solo_enc->thread_wait, &wait); - - return 0; -} - -void solo_motion_isr(struct solo_dev *solo_dev) -{ - u32 status; - int i; - - solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_MOTION); - - status = solo_reg_read(solo_dev, SOLO_VI_MOT_STATUS); - - for (i = 0; i < solo_dev->nr_chans; i++) { - struct solo_enc_dev *solo_enc = solo_dev->v4l2_enc[i]; - - BUG_ON(solo_enc == NULL); - - if (solo_enc->motion_detected) - continue; - if (!(status & (1 << i))) - continue; - - solo_enc->motion_detected = 1; - } -} - -void solo_enc_v4l2_isr(struct solo_dev *solo_dev) -{ - struct solo_enc_buf *enc_buf; - u32 mpeg_current, mpeg_next, mpeg_size; - u32 jpeg_current, jpeg_next, jpeg_size; - u32 reg_mpeg_size; - u8 cur_q, vop_type; - u8 ch; - enum solo_enc_types enc_type; - - solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_ENCODER); - - cur_q = ((solo_reg_read(solo_dev, SOLO_VE_STATE(11)) & 0xF) + 1) % MP4_QS; - - reg_mpeg_size = ((solo_reg_read(solo_dev, SOLO_VE_STATE(0)) & 0xFFFFF) + 64 + 8) & ~7; - - while (solo_dev->enc_idx != cur_q) { - mpeg_current = solo_reg_read(solo_dev, - SOLO_VE_MPEG4_QUE(solo_dev->enc_idx)); - jpeg_current = solo_reg_read(solo_dev, - SOLO_VE_JPEG_QUE(solo_dev->enc_idx)); - solo_dev->enc_idx = (solo_dev->enc_idx + 1) % MP4_QS; - mpeg_next = solo_reg_read(solo_dev, - SOLO_VE_MPEG4_QUE(solo_dev->enc_idx)); - jpeg_next = solo_reg_read(solo_dev, - SOLO_VE_JPEG_QUE(solo_dev->enc_idx)); - - ch = (mpeg_current >> 24) & 0x1f; - if (ch >= SOLO_MAX_CHANNELS) { - ch -= SOLO_MAX_CHANNELS; - enc_type = SOLO_ENC_TYPE_EXT; - } else - enc_type = SOLO_ENC_TYPE_STD; - - vop_type = (mpeg_current >> 29) & 3; - - mpeg_current &= 0x00ffffff; - mpeg_next &= 0x00ffffff; - jpeg_current &= 0x00ffffff; - jpeg_next &= 0x00ffffff; - - mpeg_size = (SOLO_MP4E_EXT_SIZE(solo_dev) + - mpeg_next - mpeg_current) % - SOLO_MP4E_EXT_SIZE(solo_dev); - - jpeg_size = (SOLO_JPEG_EXT_SIZE(solo_dev) + - jpeg_next - jpeg_current) % - SOLO_JPEG_EXT_SIZE(solo_dev); - - /* XXX I think this means we had a ring overflow? */ - if (mpeg_current > mpeg_next && mpeg_size != reg_mpeg_size) { - enc_reset_gop(solo_dev, ch); - continue; - } - - /* When resetting the GOP, skip frames until I-frame */ - if (enc_gop_reset(solo_dev, ch, vop_type)) - continue; - - enc_buf = &solo_dev->enc_buf[solo_dev->enc_wr_idx]; - - enc_buf->vop = vop_type; - enc_buf->ch = ch; - enc_buf->off = mpeg_current; - enc_buf->size = mpeg_size; - enc_buf->jpeg_off = jpeg_current; - enc_buf->jpeg_size = jpeg_size; - enc_buf->type = enc_type; - - do_gettimeofday(&enc_buf->ts); - - solo_dev->enc_wr_idx = (solo_dev->enc_wr_idx + 1) % - SOLO_NR_RING_BUFS; - - wake_up_interruptible(&solo_dev->v4l2_enc[ch]->thread_wait); - } - - return; -} - -static int solo_enc_buf_setup(struct videobuf_queue *vq, unsigned int *count, - unsigned int *size) -{ - *size = FRAME_BUF_SIZE; - - if (*count < MIN_VID_BUFFERS) - *count = MIN_VID_BUFFERS; - - return 0; -} - -static int solo_enc_buf_prepare(struct videobuf_queue *vq, - struct videobuf_buffer *vb, - enum v4l2_field field) -{ - struct solo_enc_fh *fh = vq->priv_data; - struct solo_enc_dev *solo_enc = fh->enc; - - vb->size = FRAME_BUF_SIZE; - if (vb->baddr != 0 && vb->bsize < vb->size) - return -EINVAL; - - /* These properties only change when queue is idle */ - vb->width = solo_enc->width; - vb->height = solo_enc->height; - vb->field = field; - - if (vb->state == VIDEOBUF_NEEDS_INIT) { - int rc = videobuf_iolock(vq, vb, NULL); - if (rc < 0) { - struct videobuf_dmabuf *dma = videobuf_to_dma(vb); - videobuf_dma_unmap(vq->dev, dma); - videobuf_dma_free(dma); - vb->state = VIDEOBUF_NEEDS_INIT; - return rc; - } - } - vb->state = VIDEOBUF_PREPARED; - - return 0; -} - -static void solo_enc_buf_queue(struct videobuf_queue *vq, - struct videobuf_buffer *vb) -{ - struct solo_enc_fh *fh = vq->priv_data; - - vb->state = VIDEOBUF_QUEUED; - list_add_tail(&vb->queue, &fh->vidq_active); - wake_up_interruptible(&fh->enc->thread_wait); -} - -static void solo_enc_buf_release(struct videobuf_queue *vq, - struct videobuf_buffer *vb) -{ - struct videobuf_dmabuf *dma = videobuf_to_dma(vb); - - videobuf_dma_unmap(vq->dev, dma); - videobuf_dma_free(dma); - vb->state = VIDEOBUF_NEEDS_INIT; -} - -static struct videobuf_queue_ops solo_enc_video_qops = { - .buf_setup = solo_enc_buf_setup, - .buf_prepare = solo_enc_buf_prepare, - .buf_queue = solo_enc_buf_queue, - .buf_release = solo_enc_buf_release, -}; - -static unsigned int solo_enc_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct solo_enc_fh *fh = file->private_data; - - return videobuf_poll_stream(file, &fh->vidq, wait); -} - -static int solo_enc_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct solo_enc_fh *fh = file->private_data; - - return videobuf_mmap_mapper(&fh->vidq, vma); -} - -static int solo_enc_open(struct file *file) -{ - struct solo_enc_dev *solo_enc = video_drvdata(file); - struct solo_enc_fh *fh; - - fh = kzalloc(sizeof(*fh), GFP_KERNEL); - if (fh == NULL) - return -ENOMEM; - - fh->enc = solo_enc; - file->private_data = fh; - INIT_LIST_HEAD(&fh->vidq_active); - fh->fmt = V4L2_PIX_FMT_MPEG; - fh->type = SOLO_ENC_TYPE_STD; - - videobuf_queue_sg_init(&fh->vidq, &solo_enc_video_qops, - &solo_enc->solo_dev->pdev->dev, - &solo_enc->lock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, - V4L2_FIELD_INTERLACED, - sizeof(struct videobuf_buffer), fh, NULL); - - return 0; -} - -static ssize_t solo_enc_read(struct file *file, char __user *data, - size_t count, loff_t *ppos) -{ - struct solo_enc_fh *fh = file->private_data; - struct solo_enc_dev *solo_enc = fh->enc; - - /* Make sure the encoder is on */ - if (!fh->enc_on) { - int ret; - - spin_lock(&solo_enc->lock); - ret = solo_enc_on(fh); - spin_unlock(&solo_enc->lock); - if (ret) - return ret; - - ret = solo_start_fh_thread(fh); - if (ret) - return ret; - } - - return videobuf_read_stream(&fh->vidq, data, count, ppos, 0, - file->f_flags & O_NONBLOCK); -} - -static int solo_enc_release(struct file *file) -{ - struct solo_enc_fh *fh = file->private_data; - struct solo_enc_dev *solo_enc = fh->enc; - - videobuf_stop(&fh->vidq); - videobuf_mmap_free(&fh->vidq); - - spin_lock(&solo_enc->lock); - solo_enc_off(fh); - spin_unlock(&solo_enc->lock); - - kfree(fh); - - return 0; -} - -static int solo_enc_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct solo_enc_fh *fh = priv; - struct solo_enc_dev *solo_enc = fh->enc; - struct solo_dev *solo_dev = solo_enc->solo_dev; - - strcpy(cap->driver, SOLO6X10_NAME); - snprintf(cap->card, sizeof(cap->card), "Softlogic 6x10 Enc %d", - solo_enc->ch); - snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI %s", - pci_name(solo_dev->pdev)); - cap->version = SOLO6X10_VER_NUM; - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING; - return 0; -} - -static int solo_enc_enum_input(struct file *file, void *priv, - struct v4l2_input *input) -{ - struct solo_enc_fh *fh = priv; - struct solo_enc_dev *solo_enc = fh->enc; - struct solo_dev *solo_dev = solo_enc->solo_dev; - - if (input->index) - return -EINVAL; - - snprintf(input->name, sizeof(input->name), "Encoder %d", - solo_enc->ch + 1); - input->type = V4L2_INPUT_TYPE_CAMERA; - - if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) - input->std = V4L2_STD_NTSC_M; - else - input->std = V4L2_STD_PAL_B; - - if (!tw28_get_video_status(solo_dev, solo_enc->ch)) - input->status = V4L2_IN_ST_NO_SIGNAL; - - return 0; -} - -static int solo_enc_set_input(struct file *file, void *priv, unsigned int index) -{ - if (index) - return -EINVAL; - - return 0; -} - -static int solo_enc_get_input(struct file *file, void *priv, - unsigned int *index) -{ - *index = 0; - - return 0; -} - -static int solo_enc_enum_fmt_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - switch (f->index) { - case 0: - f->pixelformat = V4L2_PIX_FMT_MPEG; - strcpy(f->description, "MPEG-4 AVC"); - break; - case 1: - f->pixelformat = V4L2_PIX_FMT_MJPEG; - strcpy(f->description, "MJPEG"); - break; - default: - return -EINVAL; - } - - f->flags = V4L2_FMT_FLAG_COMPRESSED; - - return 0; -} - -static int solo_enc_try_fmt_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct solo_enc_fh *fh = priv; - struct solo_enc_dev *solo_enc = fh->enc; - struct solo_dev *solo_dev = solo_enc->solo_dev; - struct v4l2_pix_format *pix = &f->fmt.pix; - - if (pix->pixelformat != V4L2_PIX_FMT_MPEG && - pix->pixelformat != V4L2_PIX_FMT_MJPEG) - return -EINVAL; - - /* We cannot change width/height in mid read */ - if (atomic_read(&solo_enc->readers) > 0) { - if (pix->width != solo_enc->width || - pix->height != solo_enc->height) - return -EBUSY; - } - - if (pix->width < solo_dev->video_hsize || - pix->height < solo_dev->video_vsize << 1) { - /* Default to CIF 1/2 size */ - pix->width = solo_dev->video_hsize >> 1; - pix->height = solo_dev->video_vsize; - } else { - /* Full frame */ - pix->width = solo_dev->video_hsize; - pix->height = solo_dev->video_vsize << 1; - } - - if (pix->field == V4L2_FIELD_ANY) - pix->field = V4L2_FIELD_INTERLACED; - else if (pix->field != V4L2_FIELD_INTERLACED) - pix->field = V4L2_FIELD_INTERLACED; - - /* Just set these */ - pix->colorspace = V4L2_COLORSPACE_SMPTE170M; - pix->sizeimage = FRAME_BUF_SIZE; - - return 0; -} - -static int solo_enc_set_fmt_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct solo_enc_fh *fh = priv; - struct solo_enc_dev *solo_enc = fh->enc; - struct solo_dev *solo_dev = solo_enc->solo_dev; - struct v4l2_pix_format *pix = &f->fmt.pix; - int ret; - - spin_lock(&solo_enc->lock); - - ret = solo_enc_try_fmt_cap(file, priv, f); - if (ret) { - spin_unlock(&solo_enc->lock); - return ret; - } - - if (pix->width == solo_dev->video_hsize) - solo_enc->mode = SOLO_ENC_MODE_D1; - else - solo_enc->mode = SOLO_ENC_MODE_CIF; - - /* This does not change the encoder at all */ - fh->fmt = pix->pixelformat; - - if (pix->priv) - fh->type = SOLO_ENC_TYPE_EXT; - ret = solo_enc_on(fh); - - spin_unlock(&solo_enc->lock); - - if (ret) - return ret; - - return solo_start_fh_thread(fh); -} - -static int solo_enc_get_fmt_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct solo_enc_fh *fh = priv; - struct solo_enc_dev *solo_enc = fh->enc; - struct v4l2_pix_format *pix = &f->fmt.pix; - - pix->width = solo_enc->width; - pix->height = solo_enc->height; - pix->pixelformat = fh->fmt; - pix->field = solo_enc->interlaced ? V4L2_FIELD_INTERLACED : - V4L2_FIELD_NONE; - pix->sizeimage = FRAME_BUF_SIZE; - pix->colorspace = V4L2_COLORSPACE_SMPTE170M; - - return 0; -} - -static int solo_enc_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *req) -{ - struct solo_enc_fh *fh = priv; - - return videobuf_reqbufs(&fh->vidq, req); -} - -static int solo_enc_querybuf(struct file *file, void *priv, - struct v4l2_buffer *buf) -{ - struct solo_enc_fh *fh = priv; - - return videobuf_querybuf(&fh->vidq, buf); -} - -static int solo_enc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) -{ - struct solo_enc_fh *fh = priv; - - return videobuf_qbuf(&fh->vidq, buf); -} - -static int solo_enc_dqbuf(struct file *file, void *priv, - struct v4l2_buffer *buf) -{ - struct solo_enc_fh *fh = priv; - struct solo_enc_dev *solo_enc = fh->enc; - int ret; - - /* Make sure the encoder is on */ - if (!fh->enc_on) { - spin_lock(&solo_enc->lock); - ret = solo_enc_on(fh); - spin_unlock(&solo_enc->lock); - if (ret) - return ret; - - ret = solo_start_fh_thread(fh); - if (ret) - return ret; - } - - ret = videobuf_dqbuf(&fh->vidq, buf, file->f_flags & O_NONBLOCK); - if (ret) - return ret; - - /* Signal motion detection */ - if (solo_is_motion_on(solo_enc)) { - buf->flags |= V4L2_BUF_FLAG_MOTION_ON; - if (solo_enc->motion_detected) { - buf->flags |= V4L2_BUF_FLAG_MOTION_DETECTED; - solo_reg_write(solo_enc->solo_dev, SOLO_VI_MOT_CLEAR, - 1 << solo_enc->ch); - solo_enc->motion_detected = 0; - } - } - - /* Check for key frame on mpeg data */ - if (fh->fmt == V4L2_PIX_FMT_MPEG) { - struct videobuf_dmabuf *vbuf = - videobuf_to_dma(fh->vidq.bufs[buf->index]); - - if (vbuf) { - u8 *p = sg_virt(vbuf->sglist); - if (p[3] == 0x00) - buf->flags |= V4L2_BUF_FLAG_KEYFRAME; - else - buf->flags |= V4L2_BUF_FLAG_PFRAME; - } - } - - return 0; -} - -static int solo_enc_streamon(struct file *file, void *priv, - enum v4l2_buf_type i) -{ - struct solo_enc_fh *fh = priv; - - if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - return videobuf_streamon(&fh->vidq); -} - -static int solo_enc_streamoff(struct file *file, void *priv, - enum v4l2_buf_type i) -{ - struct solo_enc_fh *fh = priv; - - if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - return videobuf_streamoff(&fh->vidq); -} - -static int solo_enc_s_std(struct file *file, void *priv, v4l2_std_id *i) -{ - return 0; -} - -static int solo_enum_framesizes(struct file *file, void *priv, - struct v4l2_frmsizeenum *fsize) -{ - struct solo_enc_fh *fh = priv; - struct solo_dev *solo_dev = fh->enc->solo_dev; - - if (fsize->pixel_format != V4L2_PIX_FMT_MPEG) - return -EINVAL; - - switch (fsize->index) { - case 0: - fsize->discrete.width = solo_dev->video_hsize >> 1; - fsize->discrete.height = solo_dev->video_vsize; - break; - case 1: - fsize->discrete.width = solo_dev->video_hsize; - fsize->discrete.height = solo_dev->video_vsize << 1; - break; - default: - return -EINVAL; - } - - fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; - - return 0; -} - -static int solo_enum_frameintervals(struct file *file, void *priv, - struct v4l2_frmivalenum *fintv) -{ - struct solo_enc_fh *fh = priv; - struct solo_dev *solo_dev = fh->enc->solo_dev; - - if (fintv->pixel_format != V4L2_PIX_FMT_MPEG || fintv->index) - return -EINVAL; - - fintv->type = V4L2_FRMIVAL_TYPE_STEPWISE; - - fintv->stepwise.min.numerator = solo_dev->fps; - fintv->stepwise.min.denominator = 1; - - fintv->stepwise.max.numerator = solo_dev->fps; - fintv->stepwise.max.denominator = 15; - - fintv->stepwise.step.numerator = 1; - fintv->stepwise.step.denominator = 1; - - return 0; -} - -static int solo_g_parm(struct file *file, void *priv, - struct v4l2_streamparm *sp) -{ - struct solo_enc_fh *fh = priv; - struct solo_enc_dev *solo_enc = fh->enc; - struct solo_dev *solo_dev = solo_enc->solo_dev; - struct v4l2_captureparm *cp = &sp->parm.capture; - - cp->capability = V4L2_CAP_TIMEPERFRAME; - cp->timeperframe.numerator = solo_enc->interval; - cp->timeperframe.denominator = solo_dev->fps; - cp->capturemode = 0; - /* XXX: Shouldn't we be able to get/set this from videobuf? */ - cp->readbuffers = 2; - - return 0; -} - -static int solo_s_parm(struct file *file, void *priv, - struct v4l2_streamparm *sp) -{ - struct solo_enc_fh *fh = priv; - struct solo_enc_dev *solo_enc = fh->enc; - struct solo_dev *solo_dev = solo_enc->solo_dev; - struct v4l2_captureparm *cp = &sp->parm.capture; - - spin_lock(&solo_enc->lock); - - if (atomic_read(&solo_enc->readers) > 0) { - spin_unlock(&solo_enc->lock); - return -EBUSY; - } - - if ((cp->timeperframe.numerator == 0) || - (cp->timeperframe.denominator == 0)) { - /* reset framerate */ - cp->timeperframe.numerator = 1; - cp->timeperframe.denominator = solo_dev->fps; - } - - if (cp->timeperframe.denominator != solo_dev->fps) - cp->timeperframe.denominator = solo_dev->fps; - - if (cp->timeperframe.numerator > 15) - cp->timeperframe.numerator = 15; - - solo_enc->interval = cp->timeperframe.numerator; - - cp->capability = V4L2_CAP_TIMEPERFRAME; - - solo_enc->gop = max(solo_dev->fps / solo_enc->interval, 1); - solo_update_mode(solo_enc); - - spin_unlock(&solo_enc->lock); - - return 0; -} - -static int solo_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) -{ - struct solo_enc_fh *fh = priv; - struct solo_enc_dev *solo_enc = fh->enc; - struct solo_dev *solo_dev = solo_enc->solo_dev; - - qc->id = v4l2_ctrl_next(solo_ctrl_classes, qc->id); - if (!qc->id) - return -EINVAL; - - switch (qc->id) { - case V4L2_CID_BRIGHTNESS: - case V4L2_CID_CONTRAST: - case V4L2_CID_SATURATION: - case V4L2_CID_HUE: - return v4l2_ctrl_query_fill(qc, 0x00, 0xff, 1, 0x80); - case V4L2_CID_SHARPNESS: - return v4l2_ctrl_query_fill(qc, 0x00, 0x0f, 1, 0x00); - case V4L2_CID_MPEG_VIDEO_ENCODING: - return v4l2_ctrl_query_fill( - qc, V4L2_MPEG_VIDEO_ENCODING_MPEG_1, - V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC, 1, - V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC); - case V4L2_CID_MPEG_VIDEO_GOP_SIZE: - return v4l2_ctrl_query_fill(qc, 1, 255, 1, solo_dev->fps); -#ifdef PRIVATE_CIDS - case V4L2_CID_MOTION_THRESHOLD: - qc->flags |= V4L2_CTRL_FLAG_SLIDER; - qc->type = V4L2_CTRL_TYPE_INTEGER; - qc->minimum = 0; - qc->maximum = 0xffff; - qc->step = 1; - qc->default_value = SOLO_DEF_MOT_THRESH; - strlcpy(qc->name, "Motion Detection Threshold", - sizeof(qc->name)); - return 0; - case V4L2_CID_MOTION_ENABLE: - qc->type = V4L2_CTRL_TYPE_BOOLEAN; - qc->minimum = 0; - qc->maximum = qc->step = 1; - qc->default_value = 0; - strlcpy(qc->name, "Motion Detection Enable", sizeof(qc->name)); - return 0; -#else - case V4L2_CID_MOTION_THRESHOLD: - return v4l2_ctrl_query_fill(qc, 0, 0xffff, 1, - SOLO_DEF_MOT_THRESH); - case V4L2_CID_MOTION_ENABLE: - return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); -#endif - case V4L2_CID_RDS_TX_RADIO_TEXT: - qc->type = V4L2_CTRL_TYPE_STRING; - qc->minimum = 0; - qc->maximum = OSD_TEXT_MAX; - qc->step = 1; - qc->default_value = 0; - strlcpy(qc->name, "OSD Text", sizeof(qc->name)); - return 0; - } - - return -EINVAL; -} - -static int solo_querymenu(struct file *file, void *priv, - struct v4l2_querymenu *qmenu) -{ - struct v4l2_queryctrl qctrl; - int err; - - qctrl.id = qmenu->id; - err = solo_queryctrl(file, priv, &qctrl); - if (err) - return err; - - return v4l2_ctrl_query_menu(qmenu, &qctrl, NULL); -} - -static int solo_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct solo_enc_fh *fh = priv; - struct solo_enc_dev *solo_enc = fh->enc; - struct solo_dev *solo_dev = solo_enc->solo_dev; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - case V4L2_CID_CONTRAST: - case V4L2_CID_SATURATION: - case V4L2_CID_HUE: - case V4L2_CID_SHARPNESS: - return tw28_get_ctrl_val(solo_dev, ctrl->id, solo_enc->ch, - &ctrl->value); - case V4L2_CID_MPEG_VIDEO_ENCODING: - ctrl->value = V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC; - break; - case V4L2_CID_MPEG_VIDEO_GOP_SIZE: - ctrl->value = solo_enc->gop; - break; - case V4L2_CID_MOTION_THRESHOLD: - ctrl->value = solo_enc->motion_thresh; - break; - case V4L2_CID_MOTION_ENABLE: - ctrl->value = solo_is_motion_on(solo_enc); - break; - default: - return -EINVAL; - } - - return 0; -} - -static int solo_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct solo_enc_fh *fh = priv; - struct solo_enc_dev *solo_enc = fh->enc; - struct solo_dev *solo_dev = solo_enc->solo_dev; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - case V4L2_CID_CONTRAST: - case V4L2_CID_SATURATION: - case V4L2_CID_HUE: - case V4L2_CID_SHARPNESS: - return tw28_set_ctrl_val(solo_dev, ctrl->id, solo_enc->ch, - ctrl->value); - case V4L2_CID_MPEG_VIDEO_ENCODING: - if (ctrl->value != V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC) - return -ERANGE; - break; - case V4L2_CID_MPEG_VIDEO_GOP_SIZE: - if (ctrl->value < 1 || ctrl->value > 255) - return -ERANGE; - solo_enc->gop = ctrl->value; - solo_reg_write(solo_dev, SOLO_VE_CH_GOP(solo_enc->ch), - solo_enc->gop); - solo_reg_write(solo_dev, SOLO_VE_CH_GOP_E(solo_enc->ch), - solo_enc->gop); - break; - case V4L2_CID_MOTION_THRESHOLD: - /* TODO accept value on lower 16-bits and use high - * 16-bits to assign the value to a specific block */ - if (ctrl->value < 0 || ctrl->value > 0xffff) - return -ERANGE; - solo_enc->motion_thresh = ctrl->value; - solo_set_motion_threshold(solo_dev, solo_enc->ch, ctrl->value); - break; - case V4L2_CID_MOTION_ENABLE: - solo_motion_toggle(solo_enc, ctrl->value); - break; - default: - return -EINVAL; - } - - return 0; -} - -static int solo_s_ext_ctrls(struct file *file, void *priv, - struct v4l2_ext_controls *ctrls) -{ - struct solo_enc_fh *fh = priv; - struct solo_enc_dev *solo_enc = fh->enc; - int i; - - for (i = 0; i < ctrls->count; i++) { - struct v4l2_ext_control *ctrl = (ctrls->controls + i); - int err; - - switch (ctrl->id) { - case V4L2_CID_RDS_TX_RADIO_TEXT: - if (ctrl->size - 1 > OSD_TEXT_MAX) - err = -ERANGE; - else { - err = copy_from_user(solo_enc->osd_text, - ctrl->string, - OSD_TEXT_MAX); - solo_enc->osd_text[OSD_TEXT_MAX] = '\0'; - if (!err) - err = solo_osd_print(solo_enc); - else - err = -EFAULT; - } - break; - default: - err = -EINVAL; - } - - if (err < 0) { - ctrls->error_idx = i; - return err; - } - } - - return 0; -} - -static int solo_g_ext_ctrls(struct file *file, void *priv, - struct v4l2_ext_controls *ctrls) -{ - struct solo_enc_fh *fh = priv; - struct solo_enc_dev *solo_enc = fh->enc; - int i; - - for (i = 0; i < ctrls->count; i++) { - struct v4l2_ext_control *ctrl = (ctrls->controls + i); - int err; - - switch (ctrl->id) { - case V4L2_CID_RDS_TX_RADIO_TEXT: - if (ctrl->size < OSD_TEXT_MAX) { - ctrl->size = OSD_TEXT_MAX; - err = -ENOSPC; - } else { - err = copy_to_user(ctrl->string, - solo_enc->osd_text, - OSD_TEXT_MAX); - if (err) - err = -EFAULT; - } - break; - default: - err = -EINVAL; - } - - if (err < 0) { - ctrls->error_idx = i; - return err; - } - } - - return 0; -} - -static const struct v4l2_file_operations solo_enc_fops = { - .owner = THIS_MODULE, - .open = solo_enc_open, - .release = solo_enc_release, - .read = solo_enc_read, - .poll = solo_enc_poll, - .mmap = solo_enc_mmap, - .ioctl = video_ioctl2, -}; - -static const struct v4l2_ioctl_ops solo_enc_ioctl_ops = { - .vidioc_querycap = solo_enc_querycap, - .vidioc_s_std = solo_enc_s_std, - /* Input callbacks */ - .vidioc_enum_input = solo_enc_enum_input, - .vidioc_s_input = solo_enc_set_input, - .vidioc_g_input = solo_enc_get_input, - /* Video capture format callbacks */ - .vidioc_enum_fmt_vid_cap = solo_enc_enum_fmt_cap, - .vidioc_try_fmt_vid_cap = solo_enc_try_fmt_cap, - .vidioc_s_fmt_vid_cap = solo_enc_set_fmt_cap, - .vidioc_g_fmt_vid_cap = solo_enc_get_fmt_cap, - /* Streaming I/O */ - .vidioc_reqbufs = solo_enc_reqbufs, - .vidioc_querybuf = solo_enc_querybuf, - .vidioc_qbuf = solo_enc_qbuf, - .vidioc_dqbuf = solo_enc_dqbuf, - .vidioc_streamon = solo_enc_streamon, - .vidioc_streamoff = solo_enc_streamoff, - /* Frame size and interval */ - .vidioc_enum_framesizes = solo_enum_framesizes, - .vidioc_enum_frameintervals = solo_enum_frameintervals, - /* Video capture parameters */ - .vidioc_s_parm = solo_s_parm, - .vidioc_g_parm = solo_g_parm, - /* Controls */ - .vidioc_queryctrl = solo_queryctrl, - .vidioc_querymenu = solo_querymenu, - .vidioc_g_ctrl = solo_g_ctrl, - .vidioc_s_ctrl = solo_s_ctrl, - .vidioc_g_ext_ctrls = solo_g_ext_ctrls, - .vidioc_s_ext_ctrls = solo_s_ext_ctrls, -}; - -static struct video_device solo_enc_template = { - .name = SOLO6X10_NAME, - .fops = &solo_enc_fops, - .ioctl_ops = &solo_enc_ioctl_ops, - .minor = -1, - .release = video_device_release, - - .tvnorms = V4L2_STD_NTSC_M | V4L2_STD_PAL_B, - .current_norm = V4L2_STD_NTSC_M, -}; - -static struct solo_enc_dev *solo_enc_alloc(struct solo_dev *solo_dev, u8 ch) -{ - struct solo_enc_dev *solo_enc; - int ret; - - solo_enc = kzalloc(sizeof(*solo_enc), GFP_KERNEL); - if (!solo_enc) - return ERR_PTR(-ENOMEM); - - solo_enc->vfd = video_device_alloc(); - if (!solo_enc->vfd) { - kfree(solo_enc); - return ERR_PTR(-ENOMEM); - } - - solo_enc->solo_dev = solo_dev; - solo_enc->ch = ch; - - *solo_enc->vfd = solo_enc_template; - solo_enc->vfd->parent = &solo_dev->pdev->dev; - ret = video_register_device(solo_enc->vfd, VFL_TYPE_GRABBER, - video_nr); - if (ret < 0) { - video_device_release(solo_enc->vfd); - kfree(solo_enc); - return ERR_PTR(ret); - } - - video_set_drvdata(solo_enc->vfd, solo_enc); - - snprintf(solo_enc->vfd->name, sizeof(solo_enc->vfd->name), - "%s-enc (%i/%i)", SOLO6X10_NAME, solo_dev->vfd->num, - solo_enc->vfd->num); - - if (video_nr != -1) - video_nr++; - - spin_lock_init(&solo_enc->lock); - init_waitqueue_head(&solo_enc->thread_wait); - atomic_set(&solo_enc->readers, 0); - - solo_enc->qp = SOLO_DEFAULT_QP; - solo_enc->gop = solo_dev->fps; - solo_enc->interval = 1; - solo_enc->mode = SOLO_ENC_MODE_CIF; - solo_enc->motion_thresh = SOLO_DEF_MOT_THRESH; - - spin_lock(&solo_enc->lock); - solo_update_mode(solo_enc); - spin_unlock(&solo_enc->lock); - - return solo_enc; -} - -static void solo_enc_free(struct solo_enc_dev *solo_enc) -{ - if (solo_enc == NULL) - return; - - video_unregister_device(solo_enc->vfd); - kfree(solo_enc); -} - -int solo_enc_v4l2_init(struct solo_dev *solo_dev) -{ - int i; - - for (i = 0; i < solo_dev->nr_chans; i++) { - solo_dev->v4l2_enc[i] = solo_enc_alloc(solo_dev, i); - if (IS_ERR(solo_dev->v4l2_enc[i])) - break; - } - - if (i != solo_dev->nr_chans) { - int ret = PTR_ERR(solo_dev->v4l2_enc[i]); - while (i--) - solo_enc_free(solo_dev->v4l2_enc[i]); - return ret; - } - - /* D1@MAX-FPS * 4 */ - solo_dev->enc_bw_remain = solo_dev->fps * 4 * 4; - - dev_info(&solo_dev->pdev->dev, "Encoders as /dev/video%d-%d\n", - solo_dev->v4l2_enc[0]->vfd->num, - solo_dev->v4l2_enc[solo_dev->nr_chans - 1]->vfd->num); - - return 0; -} - -void solo_enc_v4l2_exit(struct solo_dev *solo_dev) -{ - int i; - - solo_irq_off(solo_dev, SOLO_IRQ_MOTION); - - for (i = 0; i < solo_dev->nr_chans; i++) - solo_enc_free(solo_dev->v4l2_enc[i]); -} diff --git a/drivers/staging/media/solo6x10/v4l2.c b/drivers/staging/media/solo6x10/v4l2.c deleted file mode 100644 index ca774cc57539..000000000000 --- a/drivers/staging/media/solo6x10/v4l2.c +++ /dev/null @@ -1,961 +0,0 @@ -/* - * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com - * Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/kthread.h> -#include <linux/freezer.h> -#include <media/v4l2-ioctl.h> -#include <media/v4l2-common.h> -#include <media/videobuf-dma-sg.h> -#include "solo6x10.h" -#include "tw28.h" - -#define SOLO_HW_BPL 2048 -#define SOLO_DISP_PIX_FIELD V4L2_FIELD_INTERLACED - -/* Image size is two fields, SOLO_HW_BPL is one horizontal line */ -#define solo_vlines(__solo) (__solo->video_vsize * 2) -#define solo_image_size(__solo) (solo_bytesperline(__solo) * \ - solo_vlines(__solo)) -#define solo_bytesperline(__solo) (__solo->video_hsize * 2) - -#define MIN_VID_BUFFERS 4 - -/* Simple file handle */ -struct solo_filehandle { - struct solo_dev *solo_dev; - struct videobuf_queue vidq; - struct task_struct *kthread; - spinlock_t slock; - int old_write; - struct list_head vidq_active; - struct p2m_desc desc[SOLO_NR_P2M_DESC]; - int desc_idx; -}; - -unsigned video_nr = -1; -module_param(video_nr, uint, 0644); -MODULE_PARM_DESC(video_nr, "videoX start number, -1 is autodetect (default)"); - -static void erase_on(struct solo_dev *solo_dev) -{ - solo_reg_write(solo_dev, SOLO_VO_DISP_ERASE, SOLO_VO_DISP_ERASE_ON); - solo_dev->erasing = 1; - solo_dev->frame_blank = 0; -} - -static int erase_off(struct solo_dev *solo_dev) -{ - if (!solo_dev->erasing) - return 0; - - /* First time around, assert erase off */ - if (!solo_dev->frame_blank) - solo_reg_write(solo_dev, SOLO_VO_DISP_ERASE, 0); - /* Keep the erasing flag on for 8 frames minimum */ - if (solo_dev->frame_blank++ >= 8) - solo_dev->erasing = 0; - - return 1; -} - -void solo_video_in_isr(struct solo_dev *solo_dev) -{ - solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_VIDEO_IN); - wake_up_interruptible(&solo_dev->disp_thread_wait); -} - -static void solo_win_setup(struct solo_dev *solo_dev, u8 ch, - int sx, int sy, int ex, int ey, int scale) -{ - if (ch >= solo_dev->nr_chans) - return; - - /* Here, we just keep window/channel the same */ - solo_reg_write(solo_dev, SOLO_VI_WIN_CTRL0(ch), - SOLO_VI_WIN_CHANNEL(ch) | - SOLO_VI_WIN_SX(sx) | - SOLO_VI_WIN_EX(ex) | - SOLO_VI_WIN_SCALE(scale)); - - solo_reg_write(solo_dev, SOLO_VI_WIN_CTRL1(ch), - SOLO_VI_WIN_SY(sy) | - SOLO_VI_WIN_EY(ey)); -} - -static int solo_v4l2_ch_ext_4up(struct solo_dev *solo_dev, u8 idx, int on) -{ - u8 ch = idx * 4; - - if (ch >= solo_dev->nr_chans) - return -EINVAL; - - if (!on) { - u8 i; - for (i = ch; i < ch + 4; i++) - solo_win_setup(solo_dev, i, solo_dev->video_hsize, - solo_vlines(solo_dev), - solo_dev->video_hsize, - solo_vlines(solo_dev), 0); - return 0; - } - - /* Row 1 */ - solo_win_setup(solo_dev, ch, 0, 0, solo_dev->video_hsize / 2, - solo_vlines(solo_dev) / 2, 3); - solo_win_setup(solo_dev, ch + 1, solo_dev->video_hsize / 2, 0, - solo_dev->video_hsize, solo_vlines(solo_dev) / 2, 3); - /* Row 2 */ - solo_win_setup(solo_dev, ch + 2, 0, solo_vlines(solo_dev) / 2, - solo_dev->video_hsize / 2, solo_vlines(solo_dev), 3); - solo_win_setup(solo_dev, ch + 3, solo_dev->video_hsize / 2, - solo_vlines(solo_dev) / 2, solo_dev->video_hsize, - solo_vlines(solo_dev), 3); - - return 0; -} - -static int solo_v4l2_ch_ext_16up(struct solo_dev *solo_dev, int on) -{ - int sy, ysize, hsize, i; - - if (!on) { - for (i = 0; i < 16; i++) - solo_win_setup(solo_dev, i, solo_dev->video_hsize, - solo_vlines(solo_dev), - solo_dev->video_hsize, - solo_vlines(solo_dev), 0); - return 0; - } - - ysize = solo_vlines(solo_dev) / 4; - hsize = solo_dev->video_hsize / 4; - - for (sy = 0, i = 0; i < 4; i++, sy += ysize) { - solo_win_setup(solo_dev, i * 4, 0, sy, hsize, - sy + ysize, 5); - solo_win_setup(solo_dev, (i * 4) + 1, hsize, sy, - hsize * 2, sy + ysize, 5); - solo_win_setup(solo_dev, (i * 4) + 2, hsize * 2, sy, - hsize * 3, sy + ysize, 5); - solo_win_setup(solo_dev, (i * 4) + 3, hsize * 3, sy, - solo_dev->video_hsize, sy + ysize, 5); - } - - return 0; -} - -static int solo_v4l2_ch(struct solo_dev *solo_dev, u8 ch, int on) -{ - u8 ext_ch; - - if (ch < solo_dev->nr_chans) { - solo_win_setup(solo_dev, ch, on ? 0 : solo_dev->video_hsize, - on ? 0 : solo_vlines(solo_dev), - solo_dev->video_hsize, solo_vlines(solo_dev), - on ? 1 : 0); - return 0; - } - - if (ch >= solo_dev->nr_chans + solo_dev->nr_ext) - return -EINVAL; - - ext_ch = ch - solo_dev->nr_chans; - - /* 4up's first */ - if (ext_ch < 4) - return solo_v4l2_ch_ext_4up(solo_dev, ext_ch, on); - - /* Remaining case is 16up for 16-port */ - return solo_v4l2_ch_ext_16up(solo_dev, on); -} - -static int solo_v4l2_set_ch(struct solo_dev *solo_dev, u8 ch) -{ - if (ch >= solo_dev->nr_chans + solo_dev->nr_ext) - return -EINVAL; - - erase_on(solo_dev); - - solo_v4l2_ch(solo_dev, solo_dev->cur_disp_ch, 0); - solo_v4l2_ch(solo_dev, ch, 1); - - solo_dev->cur_disp_ch = ch; - - return 0; -} - -static void disp_reset_desc(struct solo_filehandle *fh) -{ - /* We use desc mode, which ignores desc 0 */ - memset(fh->desc, 0, sizeof(*fh->desc)); - fh->desc_idx = 1; -} - -static int disp_flush_descs(struct solo_filehandle *fh) -{ - int ret; - - if (!fh->desc_idx) - return 0; - - ret = solo_p2m_dma_desc(fh->solo_dev, SOLO_P2M_DMA_ID_DISP, - fh->desc, fh->desc_idx); - disp_reset_desc(fh); - - return ret; -} - -static int disp_push_desc(struct solo_filehandle *fh, dma_addr_t dma_addr, - u32 ext_addr, int size, int repeat, int ext_size) -{ - if (fh->desc_idx >= SOLO_NR_P2M_DESC) { - int ret = disp_flush_descs(fh); - if (ret) - return ret; - } - - solo_p2m_push_desc(&fh->desc[fh->desc_idx], 0, dma_addr, ext_addr, - size, repeat, ext_size); - fh->desc_idx++; - - return 0; -} - -static void solo_fillbuf(struct solo_filehandle *fh, - struct videobuf_buffer *vb) -{ - struct solo_dev *solo_dev = fh->solo_dev; - struct videobuf_dmabuf *vbuf; - unsigned int fdma_addr; - int error = 1; - int i; - struct scatterlist *sg; - dma_addr_t sg_dma; - int sg_size_left; - - vbuf = videobuf_to_dma(vb); - if (!vbuf) - goto finish_buf; - - if (erase_off(solo_dev)) { - int i; - - /* Just blit to the entire sg list, ignoring size */ - for_each_sg(vbuf->sglist, sg, vbuf->sglen, i) { - void *p = sg_virt(sg); - size_t len = sg_dma_len(sg); - - for (i = 0; i < len; i += 2) { - ((u8 *)p)[i] = 0x80; - ((u8 *)p)[i + 1] = 0x00; - } - } - - error = 0; - goto finish_buf; - } - - disp_reset_desc(fh); - sg = vbuf->sglist; - sg_dma = sg_dma_address(sg); - sg_size_left = sg_dma_len(sg); - - fdma_addr = SOLO_DISP_EXT_ADDR + (fh->old_write * - (SOLO_HW_BPL * solo_vlines(solo_dev))); - - for (i = 0; i < solo_vlines(solo_dev); i++) { - int line_len = solo_bytesperline(solo_dev); - int lines; - - if (!sg_size_left) { - sg = sg_next(sg); - if (sg == NULL) - goto finish_buf; - sg_dma = sg_dma_address(sg); - sg_size_left = sg_dma_len(sg); - } - - /* No room for an entire line, so chunk it up */ - if (sg_size_left < line_len) { - int this_addr = fdma_addr; - - while (line_len > 0) { - int this_write; - - if (!sg_size_left) { - sg = sg_next(sg); - if (sg == NULL) - goto finish_buf; - sg_dma = sg_dma_address(sg); - sg_size_left = sg_dma_len(sg); - } - - this_write = min(sg_size_left, line_len); - - if (disp_push_desc(fh, sg_dma, this_addr, - this_write, 0, 0)) - goto finish_buf; - - line_len -= this_write; - sg_size_left -= this_write; - sg_dma += this_write; - this_addr += this_write; - } - - fdma_addr += SOLO_HW_BPL; - continue; - } - - /* Shove as many lines into a repeating descriptor as possible */ - lines = min(sg_size_left / line_len, - solo_vlines(solo_dev) - i); - - if (disp_push_desc(fh, sg_dma, fdma_addr, line_len, - lines - 1, SOLO_HW_BPL)) - goto finish_buf; - - i += lines - 1; - fdma_addr += SOLO_HW_BPL * lines; - sg_dma += lines * line_len; - sg_size_left -= lines * line_len; - } - - error = disp_flush_descs(fh); - -finish_buf: - if (error) { - vb->state = VIDEOBUF_ERROR; - } else { - vb->size = solo_vlines(solo_dev) * solo_bytesperline(solo_dev); - vb->state = VIDEOBUF_DONE; - vb->field_count++; - do_gettimeofday(&vb->ts); - } - - wake_up(&vb->done); - - return; -} - -static void solo_thread_try(struct solo_filehandle *fh) -{ - struct videobuf_buffer *vb; - unsigned int cur_write; - - for (;;) { - spin_lock(&fh->slock); - - if (list_empty(&fh->vidq_active)) - break; - - vb = list_first_entry(&fh->vidq_active, struct videobuf_buffer, - queue); - - if (!waitqueue_active(&vb->done)) - break; - - cur_write = SOLO_VI_STATUS0_PAGE(solo_reg_read(fh->solo_dev, - SOLO_VI_STATUS0)); - if (cur_write == fh->old_write) - break; - - fh->old_write = cur_write; - list_del(&vb->queue); - - spin_unlock(&fh->slock); - - solo_fillbuf(fh, vb); - } - - assert_spin_locked(&fh->slock); - spin_unlock(&fh->slock); -} - -static int solo_thread(void *data) -{ - struct solo_filehandle *fh = data; - struct solo_dev *solo_dev = fh->solo_dev; - DECLARE_WAITQUEUE(wait, current); - - set_freezable(); - add_wait_queue(&solo_dev->disp_thread_wait, &wait); - - for (;;) { - long timeout = schedule_timeout_interruptible(HZ); - if (timeout == -ERESTARTSYS || kthread_should_stop()) - break; - solo_thread_try(fh); - try_to_freeze(); - } - - remove_wait_queue(&solo_dev->disp_thread_wait, &wait); - - return 0; -} - -static int solo_start_thread(struct solo_filehandle *fh) -{ - fh->kthread = kthread_run(solo_thread, fh, SOLO6X10_NAME "_disp"); - - return PTR_RET(fh->kthread); -} - -static void solo_stop_thread(struct solo_filehandle *fh) -{ - if (fh->kthread) { - kthread_stop(fh->kthread); - fh->kthread = NULL; - } -} - -static int solo_buf_setup(struct videobuf_queue *vq, unsigned int *count, - unsigned int *size) -{ - struct solo_filehandle *fh = vq->priv_data; - struct solo_dev *solo_dev = fh->solo_dev; - - *size = solo_image_size(solo_dev); - - if (*count < MIN_VID_BUFFERS) - *count = MIN_VID_BUFFERS; - - return 0; -} - -static int solo_buf_prepare(struct videobuf_queue *vq, - struct videobuf_buffer *vb, enum v4l2_field field) -{ - struct solo_filehandle *fh = vq->priv_data; - struct solo_dev *solo_dev = fh->solo_dev; - - vb->size = solo_image_size(solo_dev); - if (vb->baddr != 0 && vb->bsize < vb->size) - return -EINVAL; - - /* XXX: These properties only change when queue is idle */ - vb->width = solo_dev->video_hsize; - vb->height = solo_vlines(solo_dev); - vb->bytesperline = solo_bytesperline(solo_dev); - vb->field = field; - - if (vb->state == VIDEOBUF_NEEDS_INIT) { - int rc = videobuf_iolock(vq, vb, NULL); - if (rc < 0) { - struct videobuf_dmabuf *dma = videobuf_to_dma(vb); - videobuf_dma_unmap(vq->dev, dma); - videobuf_dma_free(dma); - vb->state = VIDEOBUF_NEEDS_INIT; - return rc; - } - } - vb->state = VIDEOBUF_PREPARED; - - return 0; -} - -static void solo_buf_queue(struct videobuf_queue *vq, - struct videobuf_buffer *vb) -{ - struct solo_filehandle *fh = vq->priv_data; - struct solo_dev *solo_dev = fh->solo_dev; - - vb->state = VIDEOBUF_QUEUED; - list_add_tail(&vb->queue, &fh->vidq_active); - wake_up_interruptible(&solo_dev->disp_thread_wait); -} - -static void solo_buf_release(struct videobuf_queue *vq, - struct videobuf_buffer *vb) -{ - struct videobuf_dmabuf *dma = videobuf_to_dma(vb); - - videobuf_dma_unmap(vq->dev, dma); - videobuf_dma_free(dma); - vb->state = VIDEOBUF_NEEDS_INIT; -} - -static struct videobuf_queue_ops solo_video_qops = { - .buf_setup = solo_buf_setup, - .buf_prepare = solo_buf_prepare, - .buf_queue = solo_buf_queue, - .buf_release = solo_buf_release, -}; - -static unsigned int solo_v4l2_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct solo_filehandle *fh = file->private_data; - - return videobuf_poll_stream(file, &fh->vidq, wait); -} - -static int solo_v4l2_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct solo_filehandle *fh = file->private_data; - - return videobuf_mmap_mapper(&fh->vidq, vma); -} - -static int solo_v4l2_open(struct file *file) -{ - struct solo_dev *solo_dev = video_drvdata(file); - struct solo_filehandle *fh; - int ret; - - fh = kzalloc(sizeof(*fh), GFP_KERNEL); - if (fh == NULL) - return -ENOMEM; - - spin_lock_init(&fh->slock); - INIT_LIST_HEAD(&fh->vidq_active); - fh->solo_dev = solo_dev; - file->private_data = fh; - - ret = solo_start_thread(fh); - if (ret) { - kfree(fh); - return ret; - } - - videobuf_queue_sg_init(&fh->vidq, &solo_video_qops, - &solo_dev->pdev->dev, &fh->slock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, - SOLO_DISP_PIX_FIELD, - sizeof(struct videobuf_buffer), fh, NULL); - - return 0; -} - -static ssize_t solo_v4l2_read(struct file *file, char __user *data, - size_t count, loff_t *ppos) -{ - struct solo_filehandle *fh = file->private_data; - - return videobuf_read_stream(&fh->vidq, data, count, ppos, 0, - file->f_flags & O_NONBLOCK); -} - -static int solo_v4l2_release(struct file *file) -{ - struct solo_filehandle *fh = file->private_data; - - videobuf_stop(&fh->vidq); - videobuf_mmap_free(&fh->vidq); - solo_stop_thread(fh); - kfree(fh); - - return 0; -} - -static int solo_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct solo_filehandle *fh = priv; - struct solo_dev *solo_dev = fh->solo_dev; - - strcpy(cap->driver, SOLO6X10_NAME); - strcpy(cap->card, "Softlogic 6x10"); - snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI %s", - pci_name(solo_dev->pdev)); - cap->version = SOLO6X10_VER_NUM; - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING; - return 0; -} - -static int solo_enum_ext_input(struct solo_dev *solo_dev, - struct v4l2_input *input) -{ - static const char *dispnames_1[] = { "4UP" }; - static const char *dispnames_2[] = { "4UP-1", "4UP-2" }; - static const char *dispnames_5[] = { - "4UP-1", "4UP-2", "4UP-3", "4UP-4", "16UP" - }; - const char **dispnames; - - if (input->index >= (solo_dev->nr_chans + solo_dev->nr_ext)) - return -EINVAL; - - if (solo_dev->nr_ext == 5) - dispnames = dispnames_5; - else if (solo_dev->nr_ext == 2) - dispnames = dispnames_2; - else - dispnames = dispnames_1; - - snprintf(input->name, sizeof(input->name), "Multi %s", - dispnames[input->index - solo_dev->nr_chans]); - - return 0; -} - -static int solo_enum_input(struct file *file, void *priv, - struct v4l2_input *input) -{ - struct solo_filehandle *fh = priv; - struct solo_dev *solo_dev = fh->solo_dev; - - if (input->index >= solo_dev->nr_chans) { - int ret = solo_enum_ext_input(solo_dev, input); - if (ret < 0) - return ret; - } else { - snprintf(input->name, sizeof(input->name), "Camera %d", - input->index + 1); - - /* We can only check this for normal inputs */ - if (!tw28_get_video_status(solo_dev, input->index)) - input->status = V4L2_IN_ST_NO_SIGNAL; - } - - input->type = V4L2_INPUT_TYPE_CAMERA; - - if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) - input->std = V4L2_STD_NTSC_M; - else - input->std = V4L2_STD_PAL_B; - - return 0; -} - -static int solo_set_input(struct file *file, void *priv, unsigned int index) -{ - struct solo_filehandle *fh = priv; - - return solo_v4l2_set_ch(fh->solo_dev, index); -} - -static int solo_get_input(struct file *file, void *priv, unsigned int *index) -{ - struct solo_filehandle *fh = priv; - - *index = fh->solo_dev->cur_disp_ch; - - return 0; -} - -static int solo_enum_fmt_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - if (f->index) - return -EINVAL; - - f->pixelformat = V4L2_PIX_FMT_UYVY; - strlcpy(f->description, "UYUV 4:2:2 Packed", sizeof(f->description)); - - return 0; -} - -static int solo_try_fmt_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct solo_filehandle *fh = priv; - struct solo_dev *solo_dev = fh->solo_dev; - struct v4l2_pix_format *pix = &f->fmt.pix; - int image_size = solo_image_size(solo_dev); - - /* Check supported sizes */ - if (pix->width != solo_dev->video_hsize) - pix->width = solo_dev->video_hsize; - if (pix->height != solo_vlines(solo_dev)) - pix->height = solo_vlines(solo_dev); - if (pix->sizeimage != image_size) - pix->sizeimage = image_size; - - /* Check formats */ - if (pix->field == V4L2_FIELD_ANY) - pix->field = SOLO_DISP_PIX_FIELD; - - if (pix->pixelformat != V4L2_PIX_FMT_UYVY || - pix->field != SOLO_DISP_PIX_FIELD || - pix->colorspace != V4L2_COLORSPACE_SMPTE170M) - return -EINVAL; - - return 0; -} - -static int solo_set_fmt_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct solo_filehandle *fh = priv; - - if (videobuf_queue_is_busy(&fh->vidq)) - return -EBUSY; - - /* For right now, if it doesn't match our running config, - * then fail */ - return solo_try_fmt_cap(file, priv, f); -} - -static int solo_get_fmt_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct solo_filehandle *fh = priv; - struct solo_dev *solo_dev = fh->solo_dev; - struct v4l2_pix_format *pix = &f->fmt.pix; - - pix->width = solo_dev->video_hsize; - pix->height = solo_vlines(solo_dev); - pix->pixelformat = V4L2_PIX_FMT_UYVY; - pix->field = SOLO_DISP_PIX_FIELD; - pix->sizeimage = solo_image_size(solo_dev); - pix->colorspace = V4L2_COLORSPACE_SMPTE170M; - pix->bytesperline = solo_bytesperline(solo_dev); - - return 0; -} - -static int solo_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *req) -{ - struct solo_filehandle *fh = priv; - - return videobuf_reqbufs(&fh->vidq, req); -} - -static int solo_querybuf(struct file *file, void *priv, struct v4l2_buffer *buf) -{ - struct solo_filehandle *fh = priv; - - return videobuf_querybuf(&fh->vidq, buf); -} - -static int solo_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) -{ - struct solo_filehandle *fh = priv; - - return videobuf_qbuf(&fh->vidq, buf); -} - -static int solo_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) -{ - struct solo_filehandle *fh = priv; - - return videobuf_dqbuf(&fh->vidq, buf, file->f_flags & O_NONBLOCK); -} - -static int solo_streamon(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct solo_filehandle *fh = priv; - - if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - return videobuf_streamon(&fh->vidq); -} - -static int solo_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct solo_filehandle *fh = priv; - - if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - return videobuf_streamoff(&fh->vidq); -} - -static int solo_s_std(struct file *file, void *priv, v4l2_std_id *i) -{ - return 0; -} - -static const u32 solo_motion_ctrls[] = { - V4L2_CID_MOTION_TRACE, - 0 -}; - -static const u32 *solo_ctrl_classes[] = { - solo_motion_ctrls, - NULL -}; - -static int solo_disp_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) -{ - qc->id = v4l2_ctrl_next(solo_ctrl_classes, qc->id); - if (!qc->id) - return -EINVAL; - - switch (qc->id) { -#ifdef PRIVATE_CIDS - case V4L2_CID_MOTION_TRACE: - qc->type = V4L2_CTRL_TYPE_BOOLEAN; - qc->minimum = 0; - qc->maximum = qc->step = 1; - qc->default_value = 0; - strlcpy(qc->name, "Motion Detection Trace", sizeof(qc->name)); - return 0; -#else - case V4L2_CID_MOTION_TRACE: - return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); -#endif - } - return -EINVAL; -} - -static int solo_disp_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct solo_filehandle *fh = priv; - struct solo_dev *solo_dev = fh->solo_dev; - - switch (ctrl->id) { - case V4L2_CID_MOTION_TRACE: - ctrl->value = solo_reg_read(solo_dev, SOLO_VI_MOTION_BAR) - ? 1 : 0; - return 0; - } - return -EINVAL; -} - -static int solo_disp_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct solo_filehandle *fh = priv; - struct solo_dev *solo_dev = fh->solo_dev; - - switch (ctrl->id) { - case V4L2_CID_MOTION_TRACE: - if (ctrl->value) { - solo_reg_write(solo_dev, SOLO_VI_MOTION_BORDER, - SOLO_VI_MOTION_Y_ADD | - SOLO_VI_MOTION_Y_VALUE(0x20) | - SOLO_VI_MOTION_CB_VALUE(0x10) | - SOLO_VI_MOTION_CR_VALUE(0x10)); - solo_reg_write(solo_dev, SOLO_VI_MOTION_BAR, - SOLO_VI_MOTION_CR_ADD | - SOLO_VI_MOTION_Y_VALUE(0x10) | - SOLO_VI_MOTION_CB_VALUE(0x80) | - SOLO_VI_MOTION_CR_VALUE(0x10)); - } else { - solo_reg_write(solo_dev, SOLO_VI_MOTION_BORDER, 0); - solo_reg_write(solo_dev, SOLO_VI_MOTION_BAR, 0); - } - return 0; - } - return -EINVAL; -} - -static const struct v4l2_file_operations solo_v4l2_fops = { - .owner = THIS_MODULE, - .open = solo_v4l2_open, - .release = solo_v4l2_release, - .read = solo_v4l2_read, - .poll = solo_v4l2_poll, - .mmap = solo_v4l2_mmap, - .ioctl = video_ioctl2, -}; - -static const struct v4l2_ioctl_ops solo_v4l2_ioctl_ops = { - .vidioc_querycap = solo_querycap, - .vidioc_s_std = solo_s_std, - /* Input callbacks */ - .vidioc_enum_input = solo_enum_input, - .vidioc_s_input = solo_set_input, - .vidioc_g_input = solo_get_input, - /* Video capture format callbacks */ - .vidioc_enum_fmt_vid_cap = solo_enum_fmt_cap, - .vidioc_try_fmt_vid_cap = solo_try_fmt_cap, - .vidioc_s_fmt_vid_cap = solo_set_fmt_cap, - .vidioc_g_fmt_vid_cap = solo_get_fmt_cap, - /* Streaming I/O */ - .vidioc_reqbufs = solo_reqbufs, - .vidioc_querybuf = solo_querybuf, - .vidioc_qbuf = solo_qbuf, - .vidioc_dqbuf = solo_dqbuf, - .vidioc_streamon = solo_streamon, - .vidioc_streamoff = solo_streamoff, - /* Controls */ - .vidioc_queryctrl = solo_disp_queryctrl, - .vidioc_g_ctrl = solo_disp_g_ctrl, - .vidioc_s_ctrl = solo_disp_s_ctrl, -}; - -static struct video_device solo_v4l2_template = { - .name = SOLO6X10_NAME, - .fops = &solo_v4l2_fops, - .ioctl_ops = &solo_v4l2_ioctl_ops, - .minor = -1, - .release = video_device_release, - - .tvnorms = V4L2_STD_NTSC_M | V4L2_STD_PAL_B, - .current_norm = V4L2_STD_NTSC_M, -}; - -int solo_v4l2_init(struct solo_dev *solo_dev) -{ - int ret; - int i; - - init_waitqueue_head(&solo_dev->disp_thread_wait); - - solo_dev->vfd = video_device_alloc(); - if (!solo_dev->vfd) - return -ENOMEM; - - *solo_dev->vfd = solo_v4l2_template; - solo_dev->vfd->parent = &solo_dev->pdev->dev; - - ret = video_register_device(solo_dev->vfd, VFL_TYPE_GRABBER, video_nr); - if (ret < 0) { - video_device_release(solo_dev->vfd); - solo_dev->vfd = NULL; - return ret; - } - - video_set_drvdata(solo_dev->vfd, solo_dev); - - snprintf(solo_dev->vfd->name, sizeof(solo_dev->vfd->name), "%s (%i)", - SOLO6X10_NAME, solo_dev->vfd->num); - - if (video_nr != -1) - video_nr++; - - dev_info(&solo_dev->pdev->dev, "Display as /dev/video%d with " - "%d inputs (%d extended)\n", solo_dev->vfd->num, - solo_dev->nr_chans, solo_dev->nr_ext); - - /* Cycle all the channels and clear */ - for (i = 0; i < solo_dev->nr_chans; i++) { - solo_v4l2_set_ch(solo_dev, i); - while (erase_off(solo_dev)) - ;/* Do nothing */ - } - - /* Set the default display channel */ - solo_v4l2_set_ch(solo_dev, 0); - while (erase_off(solo_dev)) - ;/* Do nothing */ - - solo_irq_on(solo_dev, SOLO_IRQ_VIDEO_IN); - - return 0; -} - -void solo_v4l2_exit(struct solo_dev *solo_dev) -{ - solo_irq_off(solo_dev, SOLO_IRQ_VIDEO_IN); - if (solo_dev->vfd) { - video_unregister_device(solo_dev->vfd); - solo_dev->vfd = NULL; - } -} |