diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-09-06 03:21:14 +0200 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-09-06 03:21:14 +0200 |
commit | 9cfcc658da9693f65e7224e8329e40ada2f3c699 (patch) | |
tree | 44fb518eee069733f3f895177899815e7c89e5b0 /drivers/media/pci | |
parent | Merge branch 'mailbox-for-next' of git://git.linaro.org/landing-teams/working... (diff) | |
parent | [media] c8sectpfe: Remove select on undefined LIBELF_32 (diff) | |
download | linux-9cfcc658da9693f65e7224e8329e40ada2f3c699.tar.xz linux-9cfcc658da9693f65e7224e8329e40ada2f3c699.zip |
Merge tag 'media/v4.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media
Pull media updates from Mauro Carvalho Chehab:
- new DVB frontend drivers: ascot2e, cxd2841er, horus3a, lnbh25
- new HDMI capture driver: tc358743
- new driver for NetUP DVB new boards (netup_unidvb)
- IR support for DVBSky cards (smipcie-ir)
- Coda driver has gain macroblock tiling support
- Renesas R-Car gains JPEG codec driver
- new DVB platform driver for STi boards: c8sectpfe
- added documentation for the media core kABI to device-drivers DocBook
- lots of driver fixups, cleanups and improvements
* tag 'media/v4.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (297 commits)
[media] c8sectpfe: Remove select on undefined LIBELF_32
[media] i2c: fix platform_no_drv_owner.cocci warnings
[media] cx231xx: Use wake_up_interruptible() instead of wake_up_interruptible_nr()
[media] tc358743: only queue subdev notifications if devnode is set
[media] tc358743: add missing Kconfig dependency/select
[media] c8sectpfe: Use %pad to print 'dma_addr_t'
[media] DocBook media: Fix typo "the the" in xml files
[media] tc358743: make reset gpio optional
[media] tc358743: set direction of reset gpio using devm_gpiod_get
[media] dvbdev: document most of the functions/data structs
[media] dvb_frontend.h: document the struct dvb_frontend
[media] dvb-frontend.h: document struct dtv_frontend_properties
[media] dvb-frontend.h: document struct dvb_frontend_ops
[media] dvb: Use DVBFE_ALGO_HW where applicable
[media] dvb_frontend.h: document struct analog_demod_ops
[media] dvb_frontend.h: Document struct dvb_tuner_ops
[media] Docbook: Document struct analog_parameters
[media] dvb_frontend.h: get rid of dvbfe_modcod
[media] add documentation for struct dvb_tuner_info
[media] dvb_frontend: document dvb_frontend_tune_settings
...
Diffstat (limited to 'drivers/media/pci')
33 files changed, 2450 insertions, 414 deletions
diff --git a/drivers/media/pci/Kconfig b/drivers/media/pci/Kconfig index f318ae9bb57a..48a611bc3e18 100644 --- a/drivers/media/pci/Kconfig +++ b/drivers/media/pci/Kconfig @@ -11,16 +11,16 @@ if MEDIA_PCI_SUPPORT if MEDIA_CAMERA_SUPPORT comment "Media capture support" source "drivers/media/pci/meye/Kconfig" +source "drivers/media/pci/solo6x10/Kconfig" source "drivers/media/pci/sta2x11/Kconfig" +source "drivers/media/pci/tw68/Kconfig" +source "drivers/media/pci/zoran/Kconfig" endif if MEDIA_ANALOG_TV_SUPPORT comment "Media capture/analog TV support" source "drivers/media/pci/ivtv/Kconfig" -source "drivers/media/pci/zoran/Kconfig" source "drivers/media/pci/saa7146/Kconfig" -source "drivers/media/pci/solo6x10/Kconfig" -source "drivers/media/pci/tw68/Kconfig" source "drivers/media/pci/dt3155/Kconfig" endif @@ -49,6 +49,7 @@ source "drivers/media/pci/mantis/Kconfig" source "drivers/media/pci/ngene/Kconfig" source "drivers/media/pci/ddbridge/Kconfig" source "drivers/media/pci/smipcie/Kconfig" +source "drivers/media/pci/netup_unidvb/Kconfig" endif endif #MEDIA_PCI_SUPPORT diff --git a/drivers/media/pci/Makefile b/drivers/media/pci/Makefile index 23ce53bd47c3..5f8aacb8b9b8 100644 --- a/drivers/media/pci/Makefile +++ b/drivers/media/pci/Makefile @@ -12,7 +12,8 @@ obj-y += ttpci/ \ ngene/ \ ddbridge/ \ saa7146/ \ - smipcie/ + smipcie/ \ + netup_unidvb/ obj-$(CONFIG_VIDEO_IVTV) += ivtv/ obj-$(CONFIG_VIDEO_ZORAN) += zoran/ diff --git a/drivers/media/pci/bt8xx/btcx-risc.c b/drivers/media/pci/bt8xx/btcx-risc.c index 00f0880b6d66..57c7f58c3af2 100644 --- a/drivers/media/pci/bt8xx/btcx-risc.c +++ b/drivers/media/pci/bt8xx/btcx-risc.c @@ -160,7 +160,6 @@ btcx_align(struct v4l2_rect *win, struct v4l2_clip *clips, unsigned int n, int m void btcx_sort_clips(struct v4l2_clip *clips, unsigned int nclips) { - struct v4l2_clip swap; int i,j,n; if (nclips < 2) @@ -168,9 +167,7 @@ btcx_sort_clips(struct v4l2_clip *clips, unsigned int nclips) for (i = nclips-2; i >= 0; i--) { for (n = 0, j = 0; j <= i; j++) { if (clips[j].c.left > clips[j+1].c.left) { - swap = clips[j]; - clips[j] = clips[j+1]; - clips[j+1] = swap; + swap(clips[j], clips[j + 1]); n++; } } diff --git a/drivers/media/pci/bt8xx/bttv-input.c b/drivers/media/pci/bt8xx/bttv-input.c index 67c8d6b2c335..a75c53da224a 100644 --- a/drivers/media/pci/bt8xx/bttv-input.c +++ b/drivers/media/pci/bt8xx/bttv-input.c @@ -194,21 +194,18 @@ static u32 bttv_rc5_decode(unsigned int code) static void bttv_rc5_timer_end(unsigned long data) { struct bttv_ir *ir = (struct bttv_ir *)data; - struct timeval tv; + ktime_t tv; u32 gap, rc5, scancode; u8 toggle, command, system; /* get time */ - do_gettimeofday(&tv); + tv = ktime_get(); + gap = ktime_to_us(ktime_sub(tv, ir->base_time)); /* avoid overflow with gap >1s */ - if (tv.tv_sec - ir->base_time.tv_sec > 1) { + if (gap > USEC_PER_SEC) { gap = 200000; - } else { - gap = 1000000 * (tv.tv_sec - ir->base_time.tv_sec) + - tv.tv_usec - ir->base_time.tv_usec; } - /* signal we're ready to start a new code */ ir->active = false; @@ -249,7 +246,7 @@ static void bttv_rc5_timer_end(unsigned long data) static int bttv_rc5_irq(struct bttv *btv) { struct bttv_ir *ir = btv->remote; - struct timeval tv; + ktime_t tv; u32 gpio; u32 gap; unsigned long current_jiffies; @@ -259,14 +256,12 @@ static int bttv_rc5_irq(struct bttv *btv) /* get time of bit */ current_jiffies = jiffies; - do_gettimeofday(&tv); + tv = ktime_get(); + gap = ktime_to_us(ktime_sub(tv, ir->base_time)); /* avoid overflow with gap >1s */ - if (tv.tv_sec - ir->base_time.tv_sec > 1) { + if (gap > USEC_PER_SEC) { gap = 200000; - } else { - gap = 1000000 * (tv.tv_sec - ir->base_time.tv_sec) + - tv.tv_usec - ir->base_time.tv_usec; } dprintk("RC5 IRQ: gap %d us for %s\n", diff --git a/drivers/media/pci/bt8xx/bttvp.h b/drivers/media/pci/bt8xx/bttvp.h index a444cfb35c0b..31bf79d3b0d2 100644 --- a/drivers/media/pci/bt8xx/bttvp.h +++ b/drivers/media/pci/bt8xx/bttvp.h @@ -140,7 +140,7 @@ struct bttv_ir { bool rc5_gpio; /* Is RC5 legacy GPIO enabled? */ u32 last_bit; /* last raw bit seen */ u32 code; /* raw code under construction */ - struct timeval base_time; /* time of last seen code */ + ktime_t base_time; /* time of last seen code */ bool active; /* building raw code */ }; diff --git a/drivers/media/pci/cobalt/Kconfig b/drivers/media/pci/cobalt/Kconfig index 6a1c0089bb62..1f88ccc174da 100644 --- a/drivers/media/pci/cobalt/Kconfig +++ b/drivers/media/pci/cobalt/Kconfig @@ -1,7 +1,8 @@ config VIDEO_COBALT tristate "Cisco Cobalt support" depends on VIDEO_V4L2 && I2C && MEDIA_CONTROLLER - depends on PCI_MSI && MTD_COMPLEX_MAPPINGS && GPIOLIB + depends on PCI_MSI && MTD_COMPLEX_MAPPINGS + depends on GPIOLIB || COMPILE_TEST depends on SND select I2C_ALGOBIT select VIDEO_ADV7604 diff --git a/drivers/media/pci/cobalt/cobalt-driver.c b/drivers/media/pci/cobalt/cobalt-driver.c index b994b8efdc99..8fed61ec712e 100644 --- a/drivers/media/pci/cobalt/cobalt-driver.c +++ b/drivers/media/pci/cobalt/cobalt-driver.c @@ -339,15 +339,16 @@ static int cobalt_setup_pci(struct cobalt *cobalt, struct pci_dev *pci_dev, } if (pcie_link_get_lanes(cobalt) != 8) { - cobalt_err("PCI Express link width is not 8 lanes (%d)\n", + cobalt_warn("PCI Express link width is %d lanes.\n", pcie_link_get_lanes(cobalt)); if (pcie_bus_link_get_lanes(cobalt) < 8) - cobalt_err("The current slot only supports %d lanes, at least 8 are needed\n", + cobalt_warn("The current slot only supports %d lanes, for best performance 8 are needed\n", pcie_bus_link_get_lanes(cobalt)); - else + if (pcie_link_get_lanes(cobalt) != pcie_bus_link_get_lanes(cobalt)) { cobalt_err("The card is most likely not seated correctly in the PCIe slot\n"); - ret = -EIO; - goto err_disable; + ret = -EIO; + goto err_disable; + } } if (pci_set_dma_mask(pci_dev, DMA_BIT_MASK(64))) { diff --git a/drivers/media/pci/cobalt/cobalt-v4l2.c b/drivers/media/pci/cobalt/cobalt-v4l2.c index b40c2d141b58..9756fd3e8af5 100644 --- a/drivers/media/pci/cobalt/cobalt-v4l2.c +++ b/drivers/media/pci/cobalt/cobalt-v4l2.c @@ -28,6 +28,7 @@ #include <media/v4l2-ctrls.h> #include <media/v4l2-event.h> +#include <media/v4l2-dv-timings.h> #include <media/adv7604.h> #include <media/adv7842.h> @@ -641,13 +642,17 @@ static int cobalt_s_dv_timings(struct file *file, void *priv_fh, struct cobalt_stream *s = video_drvdata(file); int err; - if (vb2_is_busy(&s->q)) - return -EBUSY; - if (s->input == 1) { *timings = cea1080p60; return 0; } + + if (v4l2_match_dv_timings(timings, &s->timings, 0)) + return 0; + + if (vb2_is_busy(&s->q)) + return -EBUSY; + err = v4l2_subdev_call(s->sd, video, s_dv_timings, timings); if (!err) { diff --git a/drivers/media/pci/ivtv/ivtv-gpio.c b/drivers/media/pci/ivtv/ivtv-gpio.c index af52def700cc..f752f3993687 100644 --- a/drivers/media/pci/ivtv/ivtv-gpio.c +++ b/drivers/media/pci/ivtv/ivtv-gpio.c @@ -313,13 +313,6 @@ static const struct v4l2_ctrl_ops gpio_ctrl_ops = { static const struct v4l2_subdev_core_ops subdev_core_ops = { .log_status = subdev_log_status, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, }; static const struct v4l2_subdev_tuner_ops subdev_tuner_ops = { diff --git a/drivers/media/pci/mantis/mantis_dma.c b/drivers/media/pci/mantis/mantis_dma.c index 87990ece5848..2ce310b0a022 100644 --- a/drivers/media/pci/mantis/mantis_dma.c +++ b/drivers/media/pci/mantis/mantis_dma.c @@ -140,12 +140,10 @@ int mantis_dma_init(struct mantis_pci *mantis) /* Stop RISC Engine */ mmwrite(0, MANTIS_DMA_CTL); - goto err; + return err; } return 0; -err: - return err; } EXPORT_SYMBOL_GPL(mantis_dma_init); diff --git a/drivers/media/pci/netup_unidvb/Kconfig b/drivers/media/pci/netup_unidvb/Kconfig new file mode 100644 index 000000000000..f277b0b10c2d --- /dev/null +++ b/drivers/media/pci/netup_unidvb/Kconfig @@ -0,0 +1,12 @@ +config DVB_NETUP_UNIDVB + tristate "NetUP Universal DVB card support" + depends on DVB_CORE && VIDEO_DEV && PCI && I2C && SPI_MASTER + select VIDEOBUF2_DVB + select VIDEOBUF2_VMALLOC + select DVB_HORUS3A if MEDIA_SUBDRV_AUTOSELECT + select DVB_ASCOT2E if MEDIA_SUBDRV_AUTOSELECT + select DVB_LNBH25 if MEDIA_SUBDRV_AUTOSELECT + select DVB_CXD2841ER if MEDIA_SUBDRV_AUTOSELECT + ---help--- + Support for NetUP PCI express Universal DVB card. + diff --git a/drivers/media/pci/netup_unidvb/Makefile b/drivers/media/pci/netup_unidvb/Makefile new file mode 100644 index 000000000000..ee6ae0501eae --- /dev/null +++ b/drivers/media/pci/netup_unidvb/Makefile @@ -0,0 +1,9 @@ +netup-unidvb-objs += netup_unidvb_core.o +netup-unidvb-objs += netup_unidvb_i2c.o +netup-unidvb-objs += netup_unidvb_ci.o +netup-unidvb-objs += netup_unidvb_spi.o + +obj-$(CONFIG_DVB_NETUP_UNIDVB) += netup-unidvb.o + +ccflags-y += -Idrivers/media/dvb-core +ccflags-y += -Idrivers/media/dvb-frontends diff --git a/drivers/media/pci/netup_unidvb/netup_unidvb.h b/drivers/media/pci/netup_unidvb/netup_unidvb.h new file mode 100644 index 000000000000..fa951102d7fb --- /dev/null +++ b/drivers/media/pci/netup_unidvb/netup_unidvb.h @@ -0,0 +1,133 @@ +/* + * netup_unidvb.h + * + * Data type definitions for NetUP Universal Dual DVB-CI + * + * Copyright (C) 2014 NetUP Inc. + * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru> + * Copyright (C) 2014 Abylay Ospan <aospan@netup.ru> + * + * 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. + */ + +#include <linux/pci.h> +#include <linux/i2c.h> +#include <linux/workqueue.h> +#include <media/v4l2-common.h> +#include <media/v4l2-device.h> +#include <media/videobuf2-dvb.h> +#include <dvb_ca_en50221.h> + +#define NETUP_UNIDVB_NAME "netup_unidvb" +#define NETUP_UNIDVB_VERSION "0.0.1" +#define NETUP_VENDOR_ID 0x1b55 +#define NETUP_PCI_DEV_REVISION 0x2 + +/* IRQ-related regisers */ +#define REG_ISR 0x4890 +#define REG_ISR_MASKED 0x4892 +#define REG_IMASK_SET 0x4894 +#define REG_IMASK_CLEAR 0x4896 +/* REG_ISR register bits */ +#define NETUP_UNIDVB_IRQ_SPI (1 << 0) +#define NETUP_UNIDVB_IRQ_I2C0 (1 << 1) +#define NETUP_UNIDVB_IRQ_I2C1 (1 << 2) +#define NETUP_UNIDVB_IRQ_FRA0 (1 << 4) +#define NETUP_UNIDVB_IRQ_FRA1 (1 << 5) +#define NETUP_UNIDVB_IRQ_FRB0 (1 << 6) +#define NETUP_UNIDVB_IRQ_FRB1 (1 << 7) +#define NETUP_UNIDVB_IRQ_DMA1 (1 << 8) +#define NETUP_UNIDVB_IRQ_DMA2 (1 << 9) +#define NETUP_UNIDVB_IRQ_CI (1 << 10) +#define NETUP_UNIDVB_IRQ_CAM0 (1 << 11) +#define NETUP_UNIDVB_IRQ_CAM1 (1 << 12) + +struct netup_dma { + u8 num; + spinlock_t lock; + struct netup_unidvb_dev *ndev; + struct netup_dma_regs *regs; + u32 ring_buffer_size; + u8 *addr_virt; + dma_addr_t addr_phys; + u64 addr_last; + u32 high_addr; + u32 data_offset; + u32 data_size; + struct list_head free_buffers; + struct work_struct work; + struct timer_list timeout; +}; + +enum netup_i2c_state { + STATE_DONE, + STATE_WAIT, + STATE_WANT_READ, + STATE_WANT_WRITE, + STATE_ERROR +}; + +struct netup_i2c_regs; + +struct netup_i2c { + spinlock_t lock; + wait_queue_head_t wq; + struct i2c_adapter adap; + struct netup_unidvb_dev *dev; + struct netup_i2c_regs *regs; + struct i2c_msg *msg; + enum netup_i2c_state state; + u32 xmit_size; +}; + +struct netup_ci_state { + struct dvb_ca_en50221 ca; + u8 __iomem *membase8_config; + u8 __iomem *membase8_io; + struct netup_unidvb_dev *dev; + int status; + int nr; +}; + +struct netup_spi; + +struct netup_unidvb_dev { + struct pci_dev *pci_dev; + int pci_bus; + int pci_slot; + int pci_func; + int board_num; + int old_fw; + u32 __iomem *lmmio0; + u8 __iomem *bmmio0; + u32 __iomem *lmmio1; + u8 __iomem *bmmio1; + u8 *dma_virt; + dma_addr_t dma_phys; + u32 dma_size; + struct vb2_dvb_frontends frontends[2]; + struct netup_i2c i2c[2]; + struct workqueue_struct *wq; + struct netup_dma dma[2]; + struct netup_ci_state ci[2]; + struct netup_spi *spi; +}; + +int netup_i2c_register(struct netup_unidvb_dev *ndev); +void netup_i2c_unregister(struct netup_unidvb_dev *ndev); +irqreturn_t netup_ci_interrupt(struct netup_unidvb_dev *ndev); +irqreturn_t netup_i2c_interrupt(struct netup_i2c *i2c); +irqreturn_t netup_spi_interrupt(struct netup_spi *spi); +int netup_unidvb_ci_register(struct netup_unidvb_dev *dev, + int num, struct pci_dev *pci_dev); +void netup_unidvb_ci_unregister(struct netup_unidvb_dev *dev, int num); +int netup_spi_init(struct netup_unidvb_dev *ndev); +void netup_spi_release(struct netup_unidvb_dev *ndev); diff --git a/drivers/media/pci/netup_unidvb/netup_unidvb_ci.c b/drivers/media/pci/netup_unidvb/netup_unidvb_ci.c new file mode 100644 index 000000000000..751b51b03593 --- /dev/null +++ b/drivers/media/pci/netup_unidvb/netup_unidvb_ci.c @@ -0,0 +1,248 @@ +/* + * netup_unidvb_ci.c + * + * DVB CAM support for NetUP Universal Dual DVB-CI + * + * Copyright (C) 2014 NetUP Inc. + * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru> + * Copyright (C) 2014 Abylay Ospan <aospan@netup.ru> + * + * 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. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/kmod.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include "netup_unidvb.h" + +/* CI slot 0 base address */ +#define CAM0_CONFIG 0x0 +#define CAM0_IO 0x8000 +#define CAM0_MEM 0x10000 +#define CAM0_SZ 32 +/* CI slot 1 base address */ +#define CAM1_CONFIG 0x20000 +#define CAM1_IO 0x28000 +#define CAM1_MEM 0x30000 +#define CAM1_SZ 32 +/* ctrlstat registers */ +#define CAM_CTRLSTAT_READ_SET 0x4980 +#define CAM_CTRLSTAT_CLR 0x4982 +/* register bits */ +#define BIT_CAM_STCHG (1<<0) +#define BIT_CAM_PRESENT (1<<1) +#define BIT_CAM_RESET (1<<2) +#define BIT_CAM_BYPASS (1<<3) +#define BIT_CAM_READY (1<<4) +#define BIT_CAM_ERROR (1<<5) +#define BIT_CAM_OVERCURR (1<<6) +/* BIT_CAM_BYPASS bit shift for SLOT 1 */ +#define CAM1_SHIFT 8 + +irqreturn_t netup_ci_interrupt(struct netup_unidvb_dev *ndev) +{ + writew(0x101, ndev->bmmio0 + CAM_CTRLSTAT_CLR); + return IRQ_HANDLED; +} + +static int netup_unidvb_ci_slot_ts_ctl(struct dvb_ca_en50221 *en50221, + int slot) +{ + struct netup_ci_state *state = en50221->data; + struct netup_unidvb_dev *dev = state->dev; + u16 shift = (state->nr == 1) ? CAM1_SHIFT : 0; + + dev_dbg(&dev->pci_dev->dev, "%s(): CAM_CTRLSTAT=0x%x\n", + __func__, readw(dev->bmmio0 + CAM_CTRLSTAT_READ_SET)); + if (slot != 0) + return -EINVAL; + /* pass data to CAM module */ + writew(BIT_CAM_BYPASS << shift, dev->bmmio0 + CAM_CTRLSTAT_CLR); + dev_dbg(&dev->pci_dev->dev, "%s(): CAM_CTRLSTAT=0x%x done\n", + __func__, readw(dev->bmmio0 + CAM_CTRLSTAT_READ_SET)); + return 0; +} + +static int netup_unidvb_ci_slot_shutdown(struct dvb_ca_en50221 *en50221, + int slot) +{ + struct netup_ci_state *state = en50221->data; + struct netup_unidvb_dev *dev = state->dev; + + dev_dbg(&dev->pci_dev->dev, "%s()\n", __func__); + return 0; +} + +static int netup_unidvb_ci_slot_reset(struct dvb_ca_en50221 *en50221, + int slot) +{ + struct netup_ci_state *state = en50221->data; + struct netup_unidvb_dev *dev = state->dev; + unsigned long timeout = 0; + u16 shift = (state->nr == 1) ? CAM1_SHIFT : 0; + u16 ci_stat = 0; + int reset_counter = 3; + + dev_dbg(&dev->pci_dev->dev, "%s(): CAM_CTRLSTAT_READ_SET=0x%x\n", + __func__, readw(dev->bmmio0 + CAM_CTRLSTAT_READ_SET)); +reset: + timeout = jiffies + msecs_to_jiffies(5000); + /* start reset */ + writew(BIT_CAM_RESET << shift, dev->bmmio0 + CAM_CTRLSTAT_READ_SET); + dev_dbg(&dev->pci_dev->dev, "%s(): waiting for reset\n", __func__); + /* wait until reset done */ + while (time_before(jiffies, timeout)) { + ci_stat = readw(dev->bmmio0 + CAM_CTRLSTAT_READ_SET); + if (ci_stat & (BIT_CAM_READY << shift)) + break; + udelay(1000); + } + if (!(ci_stat & (BIT_CAM_READY << shift)) && reset_counter > 0) { + dev_dbg(&dev->pci_dev->dev, + "%s(): CAMP reset timeout! Will try again..\n", + __func__); + reset_counter--; + goto reset; + } + return 0; +} + +static int netup_unidvb_poll_ci_slot_status(struct dvb_ca_en50221 *en50221, + int slot, int open) +{ + struct netup_ci_state *state = en50221->data; + struct netup_unidvb_dev *dev = state->dev; + u16 shift = (state->nr == 1) ? CAM1_SHIFT : 0; + u16 ci_stat = 0; + + dev_dbg(&dev->pci_dev->dev, "%s(): CAM_CTRLSTAT_READ_SET=0x%x\n", + __func__, readw(dev->bmmio0 + CAM_CTRLSTAT_READ_SET)); + ci_stat = readw(dev->bmmio0 + CAM_CTRLSTAT_READ_SET); + if (ci_stat & (BIT_CAM_READY << shift)) { + state->status = DVB_CA_EN50221_POLL_CAM_PRESENT | + DVB_CA_EN50221_POLL_CAM_READY; + } else if (ci_stat & (BIT_CAM_PRESENT << shift)) { + state->status = DVB_CA_EN50221_POLL_CAM_PRESENT; + } else { + state->status = 0; + } + return state->status; +} + +static int netup_unidvb_ci_read_attribute_mem(struct dvb_ca_en50221 *en50221, + int slot, int addr) +{ + struct netup_ci_state *state = en50221->data; + struct netup_unidvb_dev *dev = state->dev; + u8 val = state->membase8_config[addr]; + + dev_dbg(&dev->pci_dev->dev, + "%s(): addr=0x%x val=0x%x\n", __func__, addr, val); + return val; +} + +static int netup_unidvb_ci_write_attribute_mem(struct dvb_ca_en50221 *en50221, + int slot, int addr, u8 data) +{ + struct netup_ci_state *state = en50221->data; + struct netup_unidvb_dev *dev = state->dev; + + dev_dbg(&dev->pci_dev->dev, + "%s(): addr=0x%x data=0x%x\n", __func__, addr, data); + state->membase8_config[addr] = data; + return 0; +} + +static int netup_unidvb_ci_read_cam_ctl(struct dvb_ca_en50221 *en50221, + int slot, u8 addr) +{ + struct netup_ci_state *state = en50221->data; + struct netup_unidvb_dev *dev = state->dev; + u8 val = state->membase8_io[addr]; + + dev_dbg(&dev->pci_dev->dev, + "%s(): addr=0x%x val=0x%x\n", __func__, addr, val); + return val; +} + +static int netup_unidvb_ci_write_cam_ctl(struct dvb_ca_en50221 *en50221, + int slot, u8 addr, u8 data) +{ + struct netup_ci_state *state = en50221->data; + struct netup_unidvb_dev *dev = state->dev; + + dev_dbg(&dev->pci_dev->dev, + "%s(): addr=0x%x data=0x%x\n", __func__, addr, data); + state->membase8_io[addr] = data; + return 0; +} + +int netup_unidvb_ci_register(struct netup_unidvb_dev *dev, + int num, struct pci_dev *pci_dev) +{ + int result; + struct netup_ci_state *state; + + if (num < 0 || num > 1) { + dev_err(&pci_dev->dev, "%s(): invalid CI adapter %d\n", + __func__, num); + return -EINVAL; + } + state = &dev->ci[num]; + state->nr = num; + state->membase8_config = dev->bmmio1 + + ((num == 0) ? CAM0_CONFIG : CAM1_CONFIG); + state->membase8_io = dev->bmmio1 + + ((num == 0) ? CAM0_IO : CAM1_IO); + state->dev = dev; + state->ca.owner = THIS_MODULE; + state->ca.read_attribute_mem = netup_unidvb_ci_read_attribute_mem; + state->ca.write_attribute_mem = netup_unidvb_ci_write_attribute_mem; + state->ca.read_cam_control = netup_unidvb_ci_read_cam_ctl; + state->ca.write_cam_control = netup_unidvb_ci_write_cam_ctl; + state->ca.slot_reset = netup_unidvb_ci_slot_reset; + state->ca.slot_shutdown = netup_unidvb_ci_slot_shutdown; + state->ca.slot_ts_enable = netup_unidvb_ci_slot_ts_ctl; + state->ca.poll_slot_status = netup_unidvb_poll_ci_slot_status; + state->ca.data = state; + result = dvb_ca_en50221_init(&dev->frontends[num].adapter, + &state->ca, 0, 1); + if (result < 0) { + dev_err(&pci_dev->dev, + "%s(): dvb_ca_en50221_init result %d\n", + __func__, result); + return result; + } + writew(NETUP_UNIDVB_IRQ_CI, (u16 *)(dev->bmmio0 + REG_IMASK_SET)); + dev_info(&pci_dev->dev, + "%s(): CI adapter %d init done\n", __func__, num); + return 0; +} + +void netup_unidvb_ci_unregister(struct netup_unidvb_dev *dev, int num) +{ + struct netup_ci_state *state; + + dev_dbg(&dev->pci_dev->dev, "%s()\n", __func__); + if (num < 0 || num > 1) { + dev_err(&dev->pci_dev->dev, "%s(): invalid CI adapter %d\n", + __func__, num); + return; + } + state = &dev->ci[num]; + dvb_ca_en50221_release(&state->ca); +} + diff --git a/drivers/media/pci/netup_unidvb/netup_unidvb_core.c b/drivers/media/pci/netup_unidvb/netup_unidvb_core.c new file mode 100644 index 000000000000..6d8bf6277647 --- /dev/null +++ b/drivers/media/pci/netup_unidvb/netup_unidvb_core.c @@ -0,0 +1,1001 @@ +/* + * netup_unidvb_core.c + * + * Main module for NetUP Universal Dual DVB-CI + * + * Copyright (C) 2014 NetUP Inc. + * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru> + * Copyright (C) 2014 Abylay Ospan <aospan@netup.ru> + * + * 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. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/kmod.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/list.h> +#include <media/videobuf2-vmalloc.h> + +#include "netup_unidvb.h" +#include "cxd2841er.h" +#include "horus3a.h" +#include "ascot2e.h" +#include "lnbh25.h" + +static int spi_enable; +module_param(spi_enable, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + +MODULE_DESCRIPTION("Driver for NetUP Dual Universal DVB CI PCIe card"); +MODULE_AUTHOR("info@netup.ru"); +MODULE_VERSION(NETUP_UNIDVB_VERSION); +MODULE_LICENSE("GPL"); + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +/* Avalon-MM PCI-E registers */ +#define AVL_PCIE_IENR 0x50 +#define AVL_PCIE_ISR 0x40 +#define AVL_IRQ_ENABLE 0x80 +#define AVL_IRQ_ASSERTED 0x80 +/* GPIO registers */ +#define GPIO_REG_IO 0x4880 +#define GPIO_REG_IO_TOGGLE 0x4882 +#define GPIO_REG_IO_SET 0x4884 +#define GPIO_REG_IO_CLEAR 0x4886 +/* GPIO bits */ +#define GPIO_FEA_RESET (1 << 0) +#define GPIO_FEB_RESET (1 << 1) +#define GPIO_RFA_CTL (1 << 2) +#define GPIO_RFB_CTL (1 << 3) +#define GPIO_FEA_TU_RESET (1 << 4) +#define GPIO_FEB_TU_RESET (1 << 5) +/* DMA base address */ +#define NETUP_DMA0_ADDR 0x4900 +#define NETUP_DMA1_ADDR 0x4940 +/* 8 DMA blocks * 128 packets * 188 bytes*/ +#define NETUP_DMA_BLOCKS_COUNT 8 +#define NETUP_DMA_PACKETS_COUNT 128 +/* DMA status bits */ +#define BIT_DMA_RUN 1 +#define BIT_DMA_ERROR 2 +#define BIT_DMA_IRQ 0x200 + +/** + * struct netup_dma_regs - the map of DMA module registers + * @ctrlstat_set: Control register, write to set control bits + * @ctrlstat_clear: Control register, write to clear control bits + * @start_addr_lo: DMA ring buffer start address, lower part + * @start_addr_hi: DMA ring buffer start address, higher part + * @size: DMA ring buffer size register + Bits [0-7]: DMA packet size, 188 bytes + Bits [16-23]: packets count in block, 128 packets + Bits [24-31]: blocks count, 8 blocks + * @timeout: DMA timeout in units of 8ns + For example, value of 375000000 equals to 3 sec + * @curr_addr_lo: Current ring buffer head address, lower part + * @curr_addr_hi: Current ring buffer head address, higher part + * @stat_pkt_received: Statistic register, not tested + * @stat_pkt_accepted: Statistic register, not tested + * @stat_pkt_overruns: Statistic register, not tested + * @stat_pkt_underruns: Statistic register, not tested + * @stat_fifo_overruns: Statistic register, not tested + */ +struct netup_dma_regs { + __le32 ctrlstat_set; + __le32 ctrlstat_clear; + __le32 start_addr_lo; + __le32 start_addr_hi; + __le32 size; + __le32 timeout; + __le32 curr_addr_lo; + __le32 curr_addr_hi; + __le32 stat_pkt_received; + __le32 stat_pkt_accepted; + __le32 stat_pkt_overruns; + __le32 stat_pkt_underruns; + __le32 stat_fifo_overruns; +} __packed __aligned(1); + +struct netup_unidvb_buffer { + struct vb2_buffer vb; + struct list_head list; + u32 size; +}; + +static int netup_unidvb_tuner_ctrl(void *priv, int is_dvb_tc); +static void netup_unidvb_queue_cleanup(struct netup_dma *dma); + +static struct cxd2841er_config demod_config = { + .i2c_addr = 0xc8 +}; + +static struct horus3a_config horus3a_conf = { + .i2c_address = 0xc0, + .xtal_freq_mhz = 16, + .set_tuner_callback = netup_unidvb_tuner_ctrl +}; + +static struct ascot2e_config ascot2e_conf = { + .i2c_address = 0xc2, + .set_tuner_callback = netup_unidvb_tuner_ctrl +}; + +static struct lnbh25_config lnbh25_conf = { + .i2c_address = 0x10, + .data2_config = LNBH25_TEN | LNBH25_EXTM +}; + +static int netup_unidvb_tuner_ctrl(void *priv, int is_dvb_tc) +{ + u8 reg, mask; + struct netup_dma *dma = priv; + struct netup_unidvb_dev *ndev; + + if (!priv) + return -EINVAL; + ndev = dma->ndev; + dev_dbg(&ndev->pci_dev->dev, "%s(): num %d is_dvb_tc %d\n", + __func__, dma->num, is_dvb_tc); + reg = readb(ndev->bmmio0 + GPIO_REG_IO); + mask = (dma->num == 0) ? GPIO_RFA_CTL : GPIO_RFB_CTL; + if (!is_dvb_tc) + reg |= mask; + else + reg &= ~mask; + writeb(reg, ndev->bmmio0 + GPIO_REG_IO); + return 0; +} + +static void netup_unidvb_dev_enable(struct netup_unidvb_dev *ndev) +{ + u16 gpio_reg; + + /* enable PCI-E interrupts */ + writel(AVL_IRQ_ENABLE, ndev->bmmio0 + AVL_PCIE_IENR); + /* unreset frontends bits[0:1] */ + writeb(0x00, ndev->bmmio0 + GPIO_REG_IO); + msleep(100); + gpio_reg = + GPIO_FEA_RESET | GPIO_FEB_RESET | + GPIO_FEA_TU_RESET | GPIO_FEB_TU_RESET | + GPIO_RFA_CTL | GPIO_RFB_CTL; + writeb(gpio_reg, ndev->bmmio0 + GPIO_REG_IO); + dev_dbg(&ndev->pci_dev->dev, + "%s(): AVL_PCIE_IENR 0x%x GPIO_REG_IO 0x%x\n", + __func__, readl(ndev->bmmio0 + AVL_PCIE_IENR), + (int)readb(ndev->bmmio0 + GPIO_REG_IO)); + +} + +static void netup_unidvb_dma_enable(struct netup_dma *dma, int enable) +{ + u32 irq_mask = (dma->num == 0 ? + NETUP_UNIDVB_IRQ_DMA1 : NETUP_UNIDVB_IRQ_DMA2); + + dev_dbg(&dma->ndev->pci_dev->dev, + "%s(): DMA%d enable %d\n", __func__, dma->num, enable); + if (enable) { + writel(BIT_DMA_RUN, &dma->regs->ctrlstat_set); + writew(irq_mask, + (u16 *)(dma->ndev->bmmio0 + REG_IMASK_SET)); + } else { + writel(BIT_DMA_RUN, &dma->regs->ctrlstat_clear); + writew(irq_mask, + (u16 *)(dma->ndev->bmmio0 + REG_IMASK_CLEAR)); + } +} + +static irqreturn_t netup_dma_interrupt(struct netup_dma *dma) +{ + u64 addr_curr; + u32 size; + unsigned long flags; + struct device *dev = &dma->ndev->pci_dev->dev; + + spin_lock_irqsave(&dma->lock, flags); + addr_curr = ((u64)readl(&dma->regs->curr_addr_hi) << 32) | + (u64)readl(&dma->regs->curr_addr_lo) | dma->high_addr; + /* clear IRQ */ + writel(BIT_DMA_IRQ, &dma->regs->ctrlstat_clear); + /* sanity check */ + if (addr_curr < dma->addr_phys || + addr_curr > dma->addr_phys + dma->ring_buffer_size) { + if (addr_curr != 0) { + dev_err(dev, + "%s(): addr 0x%llx not from 0x%llx:0x%llx\n", + __func__, addr_curr, (u64)dma->addr_phys, + (u64)(dma->addr_phys + dma->ring_buffer_size)); + } + goto irq_handled; + } + size = (addr_curr >= dma->addr_last) ? + (u32)(addr_curr - dma->addr_last) : + (u32)(dma->ring_buffer_size - (dma->addr_last - addr_curr)); + if (dma->data_size != 0) { + printk_ratelimited("%s(): lost interrupt, data size %d\n", + __func__, dma->data_size); + dma->data_size += size; + } + if (dma->data_size == 0 || dma->data_size > dma->ring_buffer_size) { + dma->data_size = size; + dma->data_offset = (u32)(dma->addr_last - dma->addr_phys); + } + dma->addr_last = addr_curr; + queue_work(dma->ndev->wq, &dma->work); +irq_handled: + spin_unlock_irqrestore(&dma->lock, flags); + return IRQ_HANDLED; +} + +static irqreturn_t netup_unidvb_isr(int irq, void *dev_id) +{ + struct pci_dev *pci_dev = (struct pci_dev *)dev_id; + struct netup_unidvb_dev *ndev = pci_get_drvdata(pci_dev); + u32 reg40, reg_isr; + irqreturn_t iret = IRQ_NONE; + + /* disable interrupts */ + writel(0, ndev->bmmio0 + AVL_PCIE_IENR); + /* check IRQ source */ + reg40 = readl(ndev->bmmio0 + AVL_PCIE_ISR); + if ((reg40 & AVL_IRQ_ASSERTED) != 0) { + /* IRQ is being signaled */ + reg_isr = readw(ndev->bmmio0 + REG_ISR); + if (reg_isr & NETUP_UNIDVB_IRQ_I2C0) { + iret = netup_i2c_interrupt(&ndev->i2c[0]); + } else if (reg_isr & NETUP_UNIDVB_IRQ_I2C1) { + iret = netup_i2c_interrupt(&ndev->i2c[1]); + } else if (reg_isr & NETUP_UNIDVB_IRQ_SPI) { + iret = netup_spi_interrupt(ndev->spi); + } else if (reg_isr & NETUP_UNIDVB_IRQ_DMA1) { + iret = netup_dma_interrupt(&ndev->dma[0]); + } else if (reg_isr & NETUP_UNIDVB_IRQ_DMA2) { + iret = netup_dma_interrupt(&ndev->dma[1]); + } else if (reg_isr & NETUP_UNIDVB_IRQ_CI) { + iret = netup_ci_interrupt(ndev); + } else { + dev_err(&pci_dev->dev, + "%s(): unknown interrupt 0x%x\n", + __func__, reg_isr); + } + } + /* re-enable interrupts */ + writel(AVL_IRQ_ENABLE, ndev->bmmio0 + AVL_PCIE_IENR); + return iret; +} + +static int netup_unidvb_queue_setup(struct vb2_queue *vq, + const struct v4l2_format *fmt, + unsigned int *nbuffers, + unsigned int *nplanes, + unsigned int sizes[], + void *alloc_ctxs[]) +{ + struct netup_dma *dma = vb2_get_drv_priv(vq); + + dev_dbg(&dma->ndev->pci_dev->dev, "%s()\n", __func__); + + *nplanes = 1; + if (vq->num_buffers + *nbuffers < VIDEO_MAX_FRAME) + *nbuffers = VIDEO_MAX_FRAME - vq->num_buffers; + sizes[0] = PAGE_ALIGN(NETUP_DMA_PACKETS_COUNT * 188); + dev_dbg(&dma->ndev->pci_dev->dev, "%s() nbuffers=%d sizes[0]=%d\n", + __func__, *nbuffers, sizes[0]); + return 0; +} + +static int netup_unidvb_buf_prepare(struct vb2_buffer *vb) +{ + struct netup_dma *dma = vb2_get_drv_priv(vb->vb2_queue); + struct netup_unidvb_buffer *buf = container_of(vb, + struct netup_unidvb_buffer, vb); + + dev_dbg(&dma->ndev->pci_dev->dev, "%s(): buf 0x%p\n", __func__, buf); + buf->size = 0; + return 0; +} + +static void netup_unidvb_buf_queue(struct vb2_buffer *vb) +{ + unsigned long flags; + struct netup_dma *dma = vb2_get_drv_priv(vb->vb2_queue); + struct netup_unidvb_buffer *buf = container_of(vb, + struct netup_unidvb_buffer, vb); + + dev_dbg(&dma->ndev->pci_dev->dev, "%s(): %p\n", __func__, buf); + spin_lock_irqsave(&dma->lock, flags); + list_add_tail(&buf->list, &dma->free_buffers); + spin_unlock_irqrestore(&dma->lock, flags); + mod_timer(&dma->timeout, jiffies + msecs_to_jiffies(1000)); +} + +static int netup_unidvb_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct netup_dma *dma = vb2_get_drv_priv(q); + + dev_dbg(&dma->ndev->pci_dev->dev, "%s()\n", __func__); + netup_unidvb_dma_enable(dma, 1); + return 0; +} + +static void netup_unidvb_stop_streaming(struct vb2_queue *q) +{ + struct netup_dma *dma = vb2_get_drv_priv(q); + + dev_dbg(&dma->ndev->pci_dev->dev, "%s()\n", __func__); + netup_unidvb_dma_enable(dma, 0); + netup_unidvb_queue_cleanup(dma); +} + +static struct vb2_ops dvb_qops = { + .queue_setup = netup_unidvb_queue_setup, + .buf_prepare = netup_unidvb_buf_prepare, + .buf_queue = netup_unidvb_buf_queue, + .start_streaming = netup_unidvb_start_streaming, + .stop_streaming = netup_unidvb_stop_streaming, +}; + +static int netup_unidvb_queue_init(struct netup_dma *dma, + struct vb2_queue *vb_queue) +{ + int res; + + /* Init videobuf2 queue structure */ + vb_queue->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + vb_queue->io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ; + vb_queue->drv_priv = dma; + vb_queue->buf_struct_size = sizeof(struct netup_unidvb_buffer); + vb_queue->ops = &dvb_qops; + vb_queue->mem_ops = &vb2_vmalloc_memops; + vb_queue->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + res = vb2_queue_init(vb_queue); + if (res != 0) { + dev_err(&dma->ndev->pci_dev->dev, + "%s(): vb2_queue_init failed (%d)\n", __func__, res); + } + return res; +} + +static int netup_unidvb_dvb_init(struct netup_unidvb_dev *ndev, + int num) +{ + struct vb2_dvb_frontend *fe0, *fe1, *fe2; + + if (num < 0 || num > 1) { + dev_dbg(&ndev->pci_dev->dev, + "%s(): unable to init DVB bus %d\n", __func__, num); + return -ENODEV; + } + mutex_init(&ndev->frontends[num].lock); + INIT_LIST_HEAD(&ndev->frontends[num].felist); + if (vb2_dvb_alloc_frontend(&ndev->frontends[num], 1) == NULL || + vb2_dvb_alloc_frontend( + &ndev->frontends[num], 2) == NULL || + vb2_dvb_alloc_frontend( + &ndev->frontends[num], 3) == NULL) { + dev_dbg(&ndev->pci_dev->dev, + "%s(): unable to to alllocate vb2_dvb_frontend\n", + __func__); + return -ENOMEM; + } + fe0 = vb2_dvb_get_frontend(&ndev->frontends[num], 1); + fe1 = vb2_dvb_get_frontend(&ndev->frontends[num], 2); + fe2 = vb2_dvb_get_frontend(&ndev->frontends[num], 3); + if (fe0 == NULL || fe1 == NULL || fe2 == NULL) { + dev_dbg(&ndev->pci_dev->dev, + "%s(): frontends has not been allocated\n", __func__); + return -EINVAL; + } + netup_unidvb_queue_init(&ndev->dma[num], &fe0->dvb.dvbq); + netup_unidvb_queue_init(&ndev->dma[num], &fe1->dvb.dvbq); + netup_unidvb_queue_init(&ndev->dma[num], &fe2->dvb.dvbq); + fe0->dvb.name = "netup_fe0"; + fe1->dvb.name = "netup_fe1"; + fe2->dvb.name = "netup_fe2"; + fe0->dvb.frontend = dvb_attach(cxd2841er_attach_s, + &demod_config, &ndev->i2c[num].adap); + if (fe0->dvb.frontend == NULL) { + dev_dbg(&ndev->pci_dev->dev, + "%s(): unable to attach DVB-S/S2 frontend\n", + __func__); + goto frontend_detach; + } + horus3a_conf.set_tuner_priv = &ndev->dma[num]; + if (!dvb_attach(horus3a_attach, fe0->dvb.frontend, + &horus3a_conf, &ndev->i2c[num].adap)) { + dev_dbg(&ndev->pci_dev->dev, + "%s(): unable to attach DVB-S/S2 tuner frontend\n", + __func__); + goto frontend_detach; + } + if (!dvb_attach(lnbh25_attach, fe0->dvb.frontend, + &lnbh25_conf, &ndev->i2c[num].adap)) { + dev_dbg(&ndev->pci_dev->dev, + "%s(): unable to attach SEC frontend\n", __func__); + goto frontend_detach; + } + /* DVB-T/T2 frontend */ + fe1->dvb.frontend = dvb_attach(cxd2841er_attach_t, + &demod_config, &ndev->i2c[num].adap); + if (fe1->dvb.frontend == NULL) { + dev_dbg(&ndev->pci_dev->dev, + "%s(): unable to attach DVB-T frontend\n", __func__); + goto frontend_detach; + } + fe1->dvb.frontend->id = 1; + ascot2e_conf.set_tuner_priv = &ndev->dma[num]; + if (!dvb_attach(ascot2e_attach, fe1->dvb.frontend, + &ascot2e_conf, &ndev->i2c[num].adap)) { + dev_dbg(&ndev->pci_dev->dev, + "%s(): unable to attach DVB-T tuner frontend\n", + __func__); + goto frontend_detach; + } + /* DVB-C/C2 frontend */ + fe2->dvb.frontend = dvb_attach(cxd2841er_attach_c, + &demod_config, &ndev->i2c[num].adap); + if (fe2->dvb.frontend == NULL) { + dev_dbg(&ndev->pci_dev->dev, + "%s(): unable to attach DVB-C frontend\n", __func__); + goto frontend_detach; + } + fe2->dvb.frontend->id = 2; + if (!dvb_attach(ascot2e_attach, fe2->dvb.frontend, + &ascot2e_conf, &ndev->i2c[num].adap)) { + dev_dbg(&ndev->pci_dev->dev, + "%s(): unable to attach DVB-T/C tuner frontend\n", + __func__); + goto frontend_detach; + } + + if (vb2_dvb_register_bus(&ndev->frontends[num], + THIS_MODULE, NULL, + &ndev->pci_dev->dev, adapter_nr, 1)) { + dev_dbg(&ndev->pci_dev->dev, + "%s(): unable to register DVB bus %d\n", + __func__, num); + goto frontend_detach; + } + dev_info(&ndev->pci_dev->dev, "DVB init done, num=%d\n", num); + return 0; +frontend_detach: + vb2_dvb_dealloc_frontends(&ndev->frontends[num]); + return -EINVAL; +} + +static void netup_unidvb_dvb_fini(struct netup_unidvb_dev *ndev, int num) +{ + if (num < 0 || num > 1) { + dev_err(&ndev->pci_dev->dev, + "%s(): unable to unregister DVB bus %d\n", + __func__, num); + return; + } + vb2_dvb_unregister_bus(&ndev->frontends[num]); + dev_info(&ndev->pci_dev->dev, + "%s(): DVB bus %d unregistered\n", __func__, num); +} + +static int netup_unidvb_dvb_setup(struct netup_unidvb_dev *ndev) +{ + int res; + + res = netup_unidvb_dvb_init(ndev, 0); + if (res) + return res; + res = netup_unidvb_dvb_init(ndev, 1); + if (res) { + netup_unidvb_dvb_fini(ndev, 0); + return res; + } + return 0; +} + +static int netup_unidvb_ring_copy(struct netup_dma *dma, + struct netup_unidvb_buffer *buf) +{ + u32 copy_bytes, ring_bytes; + u32 buff_bytes = NETUP_DMA_PACKETS_COUNT * 188 - buf->size; + u8 *p = vb2_plane_vaddr(&buf->vb, 0); + struct netup_unidvb_dev *ndev = dma->ndev; + + if (p == NULL) { + dev_err(&ndev->pci_dev->dev, + "%s(): buffer is NULL\n", __func__); + return -EINVAL; + } + p += buf->size; + if (dma->data_offset + dma->data_size > dma->ring_buffer_size) { + ring_bytes = dma->ring_buffer_size - dma->data_offset; + copy_bytes = (ring_bytes > buff_bytes) ? + buff_bytes : ring_bytes; + memcpy_fromio(p, dma->addr_virt + dma->data_offset, copy_bytes); + p += copy_bytes; + buf->size += copy_bytes; + buff_bytes -= copy_bytes; + dma->data_size -= copy_bytes; + dma->data_offset += copy_bytes; + if (dma->data_offset == dma->ring_buffer_size) + dma->data_offset = 0; + } + if (buff_bytes > 0) { + ring_bytes = dma->data_size; + copy_bytes = (ring_bytes > buff_bytes) ? + buff_bytes : ring_bytes; + memcpy_fromio(p, dma->addr_virt + dma->data_offset, copy_bytes); + buf->size += copy_bytes; + dma->data_size -= copy_bytes; + dma->data_offset += copy_bytes; + if (dma->data_offset == dma->ring_buffer_size) + dma->data_offset = 0; + } + return 0; +} + +static void netup_unidvb_dma_worker(struct work_struct *work) +{ + struct netup_dma *dma = container_of(work, struct netup_dma, work); + struct netup_unidvb_dev *ndev = dma->ndev; + struct netup_unidvb_buffer *buf; + unsigned long flags; + + spin_lock_irqsave(&dma->lock, flags); + if (dma->data_size == 0) { + dev_dbg(&ndev->pci_dev->dev, + "%s(): data_size == 0\n", __func__); + goto work_done; + } + while (dma->data_size > 0) { + if (list_empty(&dma->free_buffers)) { + dev_dbg(&ndev->pci_dev->dev, + "%s(): no free buffers\n", __func__); + goto work_done; + } + buf = list_first_entry(&dma->free_buffers, + struct netup_unidvb_buffer, list); + if (buf->size >= NETUP_DMA_PACKETS_COUNT * 188) { + dev_dbg(&ndev->pci_dev->dev, + "%s(): buffer overflow, size %d\n", + __func__, buf->size); + goto work_done; + } + if (netup_unidvb_ring_copy(dma, buf)) + goto work_done; + if (buf->size == NETUP_DMA_PACKETS_COUNT * 188) { + list_del(&buf->list); + dev_dbg(&ndev->pci_dev->dev, + "%s(): buffer %p done, size %d\n", + __func__, buf, buf->size); + v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); + vb2_set_plane_payload(&buf->vb, 0, buf->size); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE); + } + } +work_done: + dma->data_size = 0; + spin_unlock_irqrestore(&dma->lock, flags); +} + +static void netup_unidvb_queue_cleanup(struct netup_dma *dma) +{ + struct netup_unidvb_buffer *buf; + unsigned long flags; + + spin_lock_irqsave(&dma->lock, flags); + while (!list_empty(&dma->free_buffers)) { + buf = list_first_entry(&dma->free_buffers, + struct netup_unidvb_buffer, list); + list_del(&buf->list); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + } + spin_unlock_irqrestore(&dma->lock, flags); +} + +static void netup_unidvb_dma_timeout(unsigned long data) +{ + struct netup_dma *dma = (struct netup_dma *)data; + struct netup_unidvb_dev *ndev = dma->ndev; + + dev_dbg(&ndev->pci_dev->dev, "%s()\n", __func__); + netup_unidvb_queue_cleanup(dma); +} + +static int netup_unidvb_dma_init(struct netup_unidvb_dev *ndev, int num) +{ + struct netup_dma *dma; + struct device *dev = &ndev->pci_dev->dev; + + if (num < 0 || num > 1) { + dev_err(dev, "%s(): unable to register DMA%d\n", + __func__, num); + return -ENODEV; + } + dma = &ndev->dma[num]; + dev_info(dev, "%s(): starting DMA%d\n", __func__, num); + dma->num = num; + dma->ndev = ndev; + spin_lock_init(&dma->lock); + INIT_WORK(&dma->work, netup_unidvb_dma_worker); + INIT_LIST_HEAD(&dma->free_buffers); + dma->timeout.function = netup_unidvb_dma_timeout; + dma->timeout.data = (unsigned long)dma; + init_timer(&dma->timeout); + dma->ring_buffer_size = ndev->dma_size / 2; + dma->addr_virt = ndev->dma_virt + dma->ring_buffer_size * num; + dma->addr_phys = (dma_addr_t)((u64)ndev->dma_phys + + dma->ring_buffer_size * num); + dev_info(dev, "%s(): DMA%d buffer virt/phys 0x%p/0x%llx size %d\n", + __func__, num, dma->addr_virt, + (unsigned long long)dma->addr_phys, + dma->ring_buffer_size); + memset_io(dma->addr_virt, 0, dma->ring_buffer_size); + dma->addr_last = dma->addr_phys; + dma->high_addr = (u32)(dma->addr_phys & 0xC0000000); + dma->regs = (struct netup_dma_regs *)(num == 0 ? + ndev->bmmio0 + NETUP_DMA0_ADDR : + ndev->bmmio0 + NETUP_DMA1_ADDR); + writel((NETUP_DMA_BLOCKS_COUNT << 24) | + (NETUP_DMA_PACKETS_COUNT << 8) | 188, &dma->regs->size); + writel((u32)(dma->addr_phys & 0x3FFFFFFF), &dma->regs->start_addr_lo); + writel(0, &dma->regs->start_addr_hi); + writel(dma->high_addr, ndev->bmmio0 + 0x1000); + writel(375000000, &dma->regs->timeout); + msleep(1000); + writel(BIT_DMA_IRQ, &dma->regs->ctrlstat_clear); + return 0; +} + +static void netup_unidvb_dma_fini(struct netup_unidvb_dev *ndev, int num) +{ + struct netup_dma *dma; + + if (num < 0 || num > 1) + return; + dev_dbg(&ndev->pci_dev->dev, "%s(): num %d\n", __func__, num); + dma = &ndev->dma[num]; + netup_unidvb_dma_enable(dma, 0); + msleep(50); + cancel_work_sync(&dma->work); + del_timer(&dma->timeout); +} + +static int netup_unidvb_dma_setup(struct netup_unidvb_dev *ndev) +{ + int res; + + res = netup_unidvb_dma_init(ndev, 0); + if (res) + return res; + res = netup_unidvb_dma_init(ndev, 1); + if (res) { + netup_unidvb_dma_fini(ndev, 0); + return res; + } + netup_unidvb_dma_enable(&ndev->dma[0], 0); + netup_unidvb_dma_enable(&ndev->dma[1], 0); + return 0; +} + +static int netup_unidvb_ci_setup(struct netup_unidvb_dev *ndev, + struct pci_dev *pci_dev) +{ + int res; + + writew(NETUP_UNIDVB_IRQ_CI, ndev->bmmio0 + REG_IMASK_SET); + res = netup_unidvb_ci_register(ndev, 0, pci_dev); + if (res) + return res; + res = netup_unidvb_ci_register(ndev, 1, pci_dev); + if (res) + netup_unidvb_ci_unregister(ndev, 0); + return res; +} + +static int netup_unidvb_request_mmio(struct pci_dev *pci_dev) +{ + if (!request_mem_region(pci_resource_start(pci_dev, 0), + pci_resource_len(pci_dev, 0), NETUP_UNIDVB_NAME)) { + dev_err(&pci_dev->dev, + "%s(): unable to request MMIO bar 0 at 0x%llx\n", + __func__, + (unsigned long long)pci_resource_start(pci_dev, 0)); + return -EBUSY; + } + if (!request_mem_region(pci_resource_start(pci_dev, 1), + pci_resource_len(pci_dev, 1), NETUP_UNIDVB_NAME)) { + dev_err(&pci_dev->dev, + "%s(): unable to request MMIO bar 1 at 0x%llx\n", + __func__, + (unsigned long long)pci_resource_start(pci_dev, 1)); + release_mem_region(pci_resource_start(pci_dev, 0), + pci_resource_len(pci_dev, 0)); + return -EBUSY; + } + return 0; +} + +static int netup_unidvb_request_modules(struct device *dev) +{ + static const char * const modules[] = { + "lnbh25", "ascot2e", "horus3a", "cxd2841er", NULL + }; + const char * const *curr_mod = modules; + int err; + + while (*curr_mod != NULL) { + err = request_module(*curr_mod); + if (err) { + dev_warn(dev, "request_module(%s) failed: %d\n", + *curr_mod, err); + } + ++curr_mod; + } + return 0; +} + +static int netup_unidvb_initdev(struct pci_dev *pci_dev, + const struct pci_device_id *pci_id) +{ + u8 board_revision; + u16 board_vendor; + struct netup_unidvb_dev *ndev; + int old_firmware = 0; + + netup_unidvb_request_modules(&pci_dev->dev); + + /* Check card revision */ + if (pci_dev->revision != NETUP_PCI_DEV_REVISION) { + dev_err(&pci_dev->dev, + "netup_unidvb: expected card revision %d, got %d\n", + NETUP_PCI_DEV_REVISION, pci_dev->revision); + dev_err(&pci_dev->dev, + "Please upgrade firmware!\n"); + dev_err(&pci_dev->dev, + "Instructions on http://www.netup.tv\n"); + old_firmware = 1; + spi_enable = 1; + } + + /* allocate device context */ + ndev = kzalloc(sizeof(*ndev), GFP_KERNEL); + + if (!ndev) + goto dev_alloc_err; + memset(ndev, 0, sizeof(*ndev)); + ndev->old_fw = old_firmware; + ndev->wq = create_singlethread_workqueue(NETUP_UNIDVB_NAME); + if (!ndev->wq) { + dev_err(&pci_dev->dev, + "%s(): unable to create workqueue\n", __func__); + goto wq_create_err; + } + ndev->pci_dev = pci_dev; + ndev->pci_bus = pci_dev->bus->number; + ndev->pci_slot = PCI_SLOT(pci_dev->devfn); + ndev->pci_func = PCI_FUNC(pci_dev->devfn); + ndev->board_num = ndev->pci_bus*10 + ndev->pci_slot; + pci_set_drvdata(pci_dev, ndev); + /* PCI init */ + dev_info(&pci_dev->dev, "%s(): PCI device (%d). Bus:0x%x Slot:0x%x\n", + __func__, ndev->board_num, ndev->pci_bus, ndev->pci_slot); + + if (pci_enable_device(pci_dev)) { + dev_err(&pci_dev->dev, "%s(): pci_enable_device failed\n", + __func__); + goto pci_enable_err; + } + /* read PCI info */ + pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &board_revision); + pci_read_config_word(pci_dev, PCI_VENDOR_ID, &board_vendor); + if (board_vendor != NETUP_VENDOR_ID) { + dev_err(&pci_dev->dev, "%s(): unknown board vendor 0x%x", + __func__, board_vendor); + goto pci_detect_err; + } + dev_info(&pci_dev->dev, + "%s(): board vendor 0x%x, revision 0x%x\n", + __func__, board_vendor, board_revision); + pci_set_master(pci_dev); + if (!pci_dma_supported(pci_dev, 0xffffffff)) { + dev_err(&pci_dev->dev, + "%s(): 32bit PCI DMA is not supported\n", __func__); + goto pci_detect_err; + } + dev_info(&pci_dev->dev, "%s(): using 32bit PCI DMA\n", __func__); + /* Clear "no snoop" and "relaxed ordering" bits, use default MRRS. */ + pcie_capability_clear_and_set_word(pci_dev, PCI_EXP_DEVCTL, + PCI_EXP_DEVCTL_READRQ | PCI_EXP_DEVCTL_RELAX_EN | + PCI_EXP_DEVCTL_NOSNOOP_EN, 0); + /* Adjust PCIe completion timeout. */ + pcie_capability_clear_and_set_word(pci_dev, + PCI_EXP_DEVCTL2, 0xf, 0x2); + + if (netup_unidvb_request_mmio(pci_dev)) { + dev_err(&pci_dev->dev, + "%s(): unable to request MMIO regions\n", __func__); + goto pci_detect_err; + } + ndev->lmmio0 = ioremap(pci_resource_start(pci_dev, 0), + pci_resource_len(pci_dev, 0)); + if (!ndev->lmmio0) { + dev_err(&pci_dev->dev, + "%s(): unable to remap MMIO bar 0\n", __func__); + goto pci_bar0_error; + } + ndev->lmmio1 = ioremap(pci_resource_start(pci_dev, 1), + pci_resource_len(pci_dev, 1)); + if (!ndev->lmmio1) { + dev_err(&pci_dev->dev, + "%s(): unable to remap MMIO bar 1\n", __func__); + goto pci_bar1_error; + } + ndev->bmmio0 = (u8 __iomem *)ndev->lmmio0; + ndev->bmmio1 = (u8 __iomem *)ndev->lmmio1; + dev_info(&pci_dev->dev, + "%s(): PCI MMIO at 0x%p (%d); 0x%p (%d); IRQ %d", + __func__, + ndev->lmmio0, (u32)pci_resource_len(pci_dev, 0), + ndev->lmmio1, (u32)pci_resource_len(pci_dev, 1), + pci_dev->irq); + if (request_irq(pci_dev->irq, netup_unidvb_isr, IRQF_SHARED, + "netup_unidvb", pci_dev) < 0) { + dev_err(&pci_dev->dev, + "%s(): can't get IRQ %d\n", __func__, pci_dev->irq); + goto irq_request_err; + } + ndev->dma_size = 2 * 188 * + NETUP_DMA_BLOCKS_COUNT * NETUP_DMA_PACKETS_COUNT; + ndev->dma_virt = dma_alloc_coherent(&pci_dev->dev, + ndev->dma_size, &ndev->dma_phys, GFP_KERNEL); + if (!ndev->dma_virt) { + dev_err(&pci_dev->dev, "%s(): unable to allocate DMA buffer\n", + __func__); + goto dma_alloc_err; + } + netup_unidvb_dev_enable(ndev); + if (spi_enable && netup_spi_init(ndev)) { + dev_warn(&pci_dev->dev, + "netup_unidvb: SPI flash setup failed\n"); + goto spi_setup_err; + } + if (old_firmware) { + dev_err(&pci_dev->dev, + "netup_unidvb: card initialization was incomplete\n"); + return 0; + } + if (netup_i2c_register(ndev)) { + dev_err(&pci_dev->dev, "netup_unidvb: I2C setup failed\n"); + goto i2c_setup_err; + } + /* enable I2C IRQs */ + writew(NETUP_UNIDVB_IRQ_I2C0 | NETUP_UNIDVB_IRQ_I2C1, + ndev->bmmio0 + REG_IMASK_SET); + usleep_range(5000, 10000); + if (netup_unidvb_dvb_setup(ndev)) { + dev_err(&pci_dev->dev, "netup_unidvb: DVB setup failed\n"); + goto dvb_setup_err; + } + if (netup_unidvb_ci_setup(ndev, pci_dev)) { + dev_err(&pci_dev->dev, "netup_unidvb: CI setup failed\n"); + goto ci_setup_err; + } + if (netup_unidvb_dma_setup(ndev)) { + dev_err(&pci_dev->dev, "netup_unidvb: DMA setup failed\n"); + goto dma_setup_err; + } + dev_info(&pci_dev->dev, + "netup_unidvb: device has been initialized\n"); + return 0; +dma_setup_err: + netup_unidvb_ci_unregister(ndev, 0); + netup_unidvb_ci_unregister(ndev, 1); +ci_setup_err: + netup_unidvb_dvb_fini(ndev, 0); + netup_unidvb_dvb_fini(ndev, 1); +dvb_setup_err: + netup_i2c_unregister(ndev); +i2c_setup_err: + if (ndev->spi) + netup_spi_release(ndev); +spi_setup_err: + dma_free_coherent(&pci_dev->dev, ndev->dma_size, + ndev->dma_virt, ndev->dma_phys); +dma_alloc_err: + free_irq(pci_dev->irq, pci_dev); +irq_request_err: + iounmap(ndev->lmmio1); +pci_bar1_error: + iounmap(ndev->lmmio0); +pci_bar0_error: + release_mem_region(pci_resource_start(pci_dev, 0), + pci_resource_len(pci_dev, 0)); + release_mem_region(pci_resource_start(pci_dev, 1), + pci_resource_len(pci_dev, 1)); +pci_detect_err: + pci_disable_device(pci_dev); +pci_enable_err: + pci_set_drvdata(pci_dev, NULL); + destroy_workqueue(ndev->wq); +wq_create_err: + kfree(ndev); +dev_alloc_err: + dev_err(&pci_dev->dev, + "%s(): failed to initizalize device\n", __func__); + return -EIO; +} + +static void netup_unidvb_finidev(struct pci_dev *pci_dev) +{ + struct netup_unidvb_dev *ndev = pci_get_drvdata(pci_dev); + + dev_info(&pci_dev->dev, "%s(): trying to stop device\n", __func__); + if (!ndev->old_fw) { + netup_unidvb_dma_fini(ndev, 0); + netup_unidvb_dma_fini(ndev, 1); + netup_unidvb_ci_unregister(ndev, 0); + netup_unidvb_ci_unregister(ndev, 1); + netup_unidvb_dvb_fini(ndev, 0); + netup_unidvb_dvb_fini(ndev, 1); + netup_i2c_unregister(ndev); + } + if (ndev->spi) + netup_spi_release(ndev); + writew(0xffff, ndev->bmmio0 + REG_IMASK_CLEAR); + dma_free_coherent(&ndev->pci_dev->dev, ndev->dma_size, + ndev->dma_virt, ndev->dma_phys); + free_irq(pci_dev->irq, pci_dev); + iounmap(ndev->lmmio0); + iounmap(ndev->lmmio1); + release_mem_region(pci_resource_start(pci_dev, 0), + pci_resource_len(pci_dev, 0)); + release_mem_region(pci_resource_start(pci_dev, 1), + pci_resource_len(pci_dev, 1)); + pci_disable_device(pci_dev); + pci_set_drvdata(pci_dev, NULL); + destroy_workqueue(ndev->wq); + kfree(ndev); + dev_info(&pci_dev->dev, + "%s(): device has been successfully stopped\n", __func__); +} + + +static struct pci_device_id netup_unidvb_pci_tbl[] = { + { PCI_DEVICE(0x1b55, 0x18f6) }, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, netup_unidvb_pci_tbl); + +static struct pci_driver netup_unidvb_pci_driver = { + .name = "netup_unidvb", + .id_table = netup_unidvb_pci_tbl, + .probe = netup_unidvb_initdev, + .remove = netup_unidvb_finidev, + .suspend = NULL, + .resume = NULL, +}; + +static int __init netup_unidvb_init(void) +{ + return pci_register_driver(&netup_unidvb_pci_driver); +} + +static void __exit netup_unidvb_fini(void) +{ + pci_unregister_driver(&netup_unidvb_pci_driver); +} + +module_init(netup_unidvb_init); +module_exit(netup_unidvb_fini); diff --git a/drivers/media/pci/netup_unidvb/netup_unidvb_i2c.c b/drivers/media/pci/netup_unidvb/netup_unidvb_i2c.c new file mode 100644 index 000000000000..eaaa2d0a5fba --- /dev/null +++ b/drivers/media/pci/netup_unidvb/netup_unidvb_i2c.c @@ -0,0 +1,381 @@ +/* + * netup_unidvb_i2c.c + * + * Internal I2C bus driver for NetUP Universal Dual DVB-CI + * + * Copyright (C) 2014 NetUP Inc. + * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru> + * Copyright (C) 2014 Abylay Ospan <aospan@netup.ru> + * + * 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. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> +#include "netup_unidvb.h" + +#define NETUP_I2C_BUS0_ADDR 0x4800 +#define NETUP_I2C_BUS1_ADDR 0x4840 +#define NETUP_I2C_TIMEOUT 1000 + +/* twi_ctrl0_stat reg bits */ +#define TWI_IRQEN_COMPL 0x1 +#define TWI_IRQEN_ANACK 0x2 +#define TWI_IRQEN_DNACK 0x4 +#define TWI_IRQ_COMPL (TWI_IRQEN_COMPL << 8) +#define TWI_IRQ_ANACK (TWI_IRQEN_ANACK << 8) +#define TWI_IRQ_DNACK (TWI_IRQEN_DNACK << 8) +#define TWI_IRQ_TX 0x800 +#define TWI_IRQ_RX 0x1000 +#define TWI_IRQEN (TWI_IRQEN_COMPL | TWI_IRQEN_ANACK | TWI_IRQEN_DNACK) +/* twi_addr_ctrl1 reg bits*/ +#define TWI_TRANSFER 0x100 +#define TWI_NOSTOP 0x200 +#define TWI_SOFT_RESET 0x2000 +/* twi_clkdiv reg value */ +#define TWI_CLKDIV 156 +/* fifo_stat_ctrl reg bits */ +#define FIFO_IRQEN 0x8000 +#define FIFO_RESET 0x4000 +/* FIFO size */ +#define FIFO_SIZE 16 + +struct netup_i2c_fifo_regs { + union { + __u8 data8; + __le16 data16; + __le32 data32; + }; + __u8 padding[4]; + __le16 stat_ctrl; +} __packed __aligned(1); + +struct netup_i2c_regs { + __le16 clkdiv; + __le16 twi_ctrl0_stat; + __le16 twi_addr_ctrl1; + __le16 length; + __u8 padding1[8]; + struct netup_i2c_fifo_regs tx_fifo; + __u8 padding2[6]; + struct netup_i2c_fifo_regs rx_fifo; +} __packed __aligned(1); + +irqreturn_t netup_i2c_interrupt(struct netup_i2c *i2c) +{ + u16 reg, tmp; + unsigned long flags; + irqreturn_t iret = IRQ_HANDLED; + + spin_lock_irqsave(&i2c->lock, flags); + reg = readw(&i2c->regs->twi_ctrl0_stat); + writew(reg & ~TWI_IRQEN, &i2c->regs->twi_ctrl0_stat); + dev_dbg(i2c->adap.dev.parent, + "%s(): twi_ctrl0_state 0x%x\n", __func__, reg); + if ((reg & TWI_IRQEN_COMPL) != 0 && (reg & TWI_IRQ_COMPL)) { + dev_dbg(i2c->adap.dev.parent, + "%s(): TWI_IRQEN_COMPL\n", __func__); + i2c->state = STATE_DONE; + goto irq_ok; + } + if ((reg & TWI_IRQEN_ANACK) != 0 && (reg & TWI_IRQ_ANACK)) { + dev_dbg(i2c->adap.dev.parent, + "%s(): TWI_IRQEN_ANACK\n", __func__); + i2c->state = STATE_ERROR; + goto irq_ok; + } + if ((reg & TWI_IRQEN_DNACK) != 0 && (reg & TWI_IRQ_DNACK)) { + dev_dbg(i2c->adap.dev.parent, + "%s(): TWI_IRQEN_DNACK\n", __func__); + i2c->state = STATE_ERROR; + goto irq_ok; + } + if ((reg & TWI_IRQ_RX) != 0) { + tmp = readw(&i2c->regs->rx_fifo.stat_ctrl); + writew(tmp & ~FIFO_IRQEN, &i2c->regs->rx_fifo.stat_ctrl); + i2c->state = STATE_WANT_READ; + dev_dbg(i2c->adap.dev.parent, + "%s(): want read\n", __func__); + goto irq_ok; + } + if ((reg & TWI_IRQ_TX) != 0) { + tmp = readw(&i2c->regs->tx_fifo.stat_ctrl); + writew(tmp & ~FIFO_IRQEN, &i2c->regs->tx_fifo.stat_ctrl); + i2c->state = STATE_WANT_WRITE; + dev_dbg(i2c->adap.dev.parent, + "%s(): want write\n", __func__); + goto irq_ok; + } + dev_warn(&i2c->adap.dev, "%s(): not mine interrupt\n", __func__); + iret = IRQ_NONE; +irq_ok: + spin_unlock_irqrestore(&i2c->lock, flags); + if (iret == IRQ_HANDLED) + wake_up(&i2c->wq); + return iret; +} + +static void netup_i2c_reset(struct netup_i2c *i2c) +{ + dev_dbg(i2c->adap.dev.parent, "%s()\n", __func__); + i2c->state = STATE_DONE; + writew(TWI_SOFT_RESET, &i2c->regs->twi_addr_ctrl1); + writew(TWI_CLKDIV, &i2c->regs->clkdiv); + writew(FIFO_RESET, &i2c->regs->tx_fifo.stat_ctrl); + writew(FIFO_RESET, &i2c->regs->rx_fifo.stat_ctrl); + writew(0x800, &i2c->regs->tx_fifo.stat_ctrl); + writew(0x800, &i2c->regs->rx_fifo.stat_ctrl); +} + +static void netup_i2c_fifo_tx(struct netup_i2c *i2c) +{ + u8 data; + u32 fifo_space = FIFO_SIZE - + (readw(&i2c->regs->tx_fifo.stat_ctrl) & 0x3f); + u32 msg_length = i2c->msg->len - i2c->xmit_size; + + msg_length = (msg_length < fifo_space ? msg_length : fifo_space); + while (msg_length--) { + data = i2c->msg->buf[i2c->xmit_size++]; + writeb(data, &i2c->regs->tx_fifo.data8); + dev_dbg(i2c->adap.dev.parent, + "%s(): write 0x%02x\n", __func__, data); + } + if (i2c->xmit_size < i2c->msg->len) { + dev_dbg(i2c->adap.dev.parent, + "%s(): TX IRQ enabled\n", __func__); + writew(readw(&i2c->regs->tx_fifo.stat_ctrl) | FIFO_IRQEN, + &i2c->regs->tx_fifo.stat_ctrl); + } +} + +static void netup_i2c_fifo_rx(struct netup_i2c *i2c) +{ + u8 data; + u32 fifo_size = readw(&i2c->regs->rx_fifo.stat_ctrl) & 0x3f; + + dev_dbg(i2c->adap.dev.parent, + "%s(): RX fifo size %d\n", __func__, fifo_size); + while (fifo_size--) { + data = readb(&i2c->regs->rx_fifo.data8); + if ((i2c->msg->flags & I2C_M_RD) != 0 && + i2c->xmit_size < i2c->msg->len) { + i2c->msg->buf[i2c->xmit_size++] = data; + dev_dbg(i2c->adap.dev.parent, + "%s(): read 0x%02x\n", __func__, data); + } + } + if (i2c->xmit_size < i2c->msg->len) { + dev_dbg(i2c->adap.dev.parent, + "%s(): RX IRQ enabled\n", __func__); + writew(readw(&i2c->regs->rx_fifo.stat_ctrl) | FIFO_IRQEN, + &i2c->regs->rx_fifo.stat_ctrl); + } +} + +static void netup_i2c_start_xfer(struct netup_i2c *i2c) +{ + u16 rdflag = ((i2c->msg->flags & I2C_M_RD) ? 1 : 0); + u16 reg = readw(&i2c->regs->twi_ctrl0_stat); + + writew(TWI_IRQEN | reg, &i2c->regs->twi_ctrl0_stat); + writew(i2c->msg->len, &i2c->regs->length); + writew(TWI_TRANSFER | (i2c->msg->addr << 1) | rdflag, + &i2c->regs->twi_addr_ctrl1); + dev_dbg(i2c->adap.dev.parent, + "%s(): length %d twi_addr_ctrl1 0x%x twi_ctrl0_stat 0x%x\n", + __func__, readw(&i2c->regs->length), + readw(&i2c->regs->twi_addr_ctrl1), + readw(&i2c->regs->twi_ctrl0_stat)); + i2c->state = STATE_WAIT; + i2c->xmit_size = 0; + if (!rdflag) + netup_i2c_fifo_tx(i2c); + else + writew(FIFO_IRQEN | readw(&i2c->regs->rx_fifo.stat_ctrl), + &i2c->regs->rx_fifo.stat_ctrl); +} + +static int netup_i2c_xfer(struct i2c_adapter *adap, + struct i2c_msg *msgs, int num) +{ + unsigned long flags; + int i, trans_done, res = num; + struct netup_i2c *i2c = i2c_get_adapdata(adap); + u16 reg; + + if (num <= 0) { + dev_dbg(i2c->adap.dev.parent, + "%s(): num == %d\n", __func__, num); + return -EINVAL; + } + spin_lock_irqsave(&i2c->lock, flags); + if (i2c->state != STATE_DONE) { + dev_dbg(i2c->adap.dev.parent, + "%s(): i2c->state == %d, resetting I2C\n", + __func__, i2c->state); + netup_i2c_reset(i2c); + } + dev_dbg(i2c->adap.dev.parent, "%s() num %d\n", __func__, num); + for (i = 0; i < num; i++) { + i2c->msg = &msgs[i]; + netup_i2c_start_xfer(i2c); + trans_done = 0; + while (!trans_done) { + spin_unlock_irqrestore(&i2c->lock, flags); + if (wait_event_timeout(i2c->wq, + i2c->state != STATE_WAIT, + msecs_to_jiffies(NETUP_I2C_TIMEOUT))) { + spin_lock_irqsave(&i2c->lock, flags); + switch (i2c->state) { + case STATE_WANT_READ: + netup_i2c_fifo_rx(i2c); + break; + case STATE_WANT_WRITE: + netup_i2c_fifo_tx(i2c); + break; + case STATE_DONE: + if ((i2c->msg->flags & I2C_M_RD) != 0 && + i2c->xmit_size != i2c->msg->len) + netup_i2c_fifo_rx(i2c); + dev_dbg(i2c->adap.dev.parent, + "%s(): msg %d OK\n", + __func__, i); + trans_done = 1; + break; + case STATE_ERROR: + res = -EIO; + dev_dbg(i2c->adap.dev.parent, + "%s(): error state\n", + __func__); + goto done; + default: + dev_dbg(i2c->adap.dev.parent, + "%s(): invalid state %d\n", + __func__, i2c->state); + res = -EINVAL; + goto done; + } + if (!trans_done) { + i2c->state = STATE_WAIT; + reg = readw( + &i2c->regs->twi_ctrl0_stat); + writew(TWI_IRQEN | reg, + &i2c->regs->twi_ctrl0_stat); + } + spin_unlock_irqrestore(&i2c->lock, flags); + } else { + spin_lock_irqsave(&i2c->lock, flags); + dev_dbg(i2c->adap.dev.parent, + "%s(): wait timeout\n", __func__); + res = -ETIMEDOUT; + goto done; + } + spin_lock_irqsave(&i2c->lock, flags); + } + } +done: + spin_unlock_irqrestore(&i2c->lock, flags); + dev_dbg(i2c->adap.dev.parent, "%s(): result %d\n", __func__, res); + return res; +} + +static u32 netup_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm netup_i2c_algorithm = { + .master_xfer = netup_i2c_xfer, + .functionality = netup_i2c_func, +}; + +static struct i2c_adapter netup_i2c_adapter = { + .owner = THIS_MODULE, + .name = NETUP_UNIDVB_NAME, + .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, + .algo = &netup_i2c_algorithm, +}; + +static int netup_i2c_init(struct netup_unidvb_dev *ndev, int bus_num) +{ + int ret; + struct netup_i2c *i2c; + + if (bus_num < 0 || bus_num > 1) { + dev_err(&ndev->pci_dev->dev, + "%s(): invalid bus_num %d\n", __func__, bus_num); + return -EINVAL; + } + i2c = &ndev->i2c[bus_num]; + spin_lock_init(&i2c->lock); + init_waitqueue_head(&i2c->wq); + i2c->regs = (struct netup_i2c_regs *)(ndev->bmmio0 + + (bus_num == 0 ? NETUP_I2C_BUS0_ADDR : NETUP_I2C_BUS1_ADDR)); + netup_i2c_reset(i2c); + i2c->adap = netup_i2c_adapter; + i2c->adap.dev.parent = &ndev->pci_dev->dev; + i2c_set_adapdata(&i2c->adap, i2c); + ret = i2c_add_adapter(&i2c->adap); + if (ret) { + dev_err(&ndev->pci_dev->dev, + "%s(): failed to add I2C adapter\n", __func__); + return ret; + } + dev_info(&ndev->pci_dev->dev, + "%s(): registered I2C bus %d at 0x%x\n", + __func__, + bus_num, (bus_num == 0 ? + NETUP_I2C_BUS0_ADDR : + NETUP_I2C_BUS1_ADDR)); + return 0; +} + +static void netup_i2c_remove(struct netup_unidvb_dev *ndev, int bus_num) +{ + struct netup_i2c *i2c; + + if (bus_num < 0 || bus_num > 1) { + dev_err(&ndev->pci_dev->dev, + "%s(): invalid bus number %d\n", __func__, bus_num); + return; + } + i2c = &ndev->i2c[bus_num]; + netup_i2c_reset(i2c); + /* remove adapter */ + i2c_del_adapter(&i2c->adap); + dev_info(&ndev->pci_dev->dev, + "netup_i2c_remove: unregistered I2C bus %d\n", bus_num); +} + +int netup_i2c_register(struct netup_unidvb_dev *ndev) +{ + int ret; + + ret = netup_i2c_init(ndev, 0); + if (ret) + return ret; + ret = netup_i2c_init(ndev, 1); + if (ret) { + netup_i2c_remove(ndev, 0); + return ret; + } + return 0; +} + +void netup_i2c_unregister(struct netup_unidvb_dev *ndev) +{ + netup_i2c_remove(ndev, 0); + netup_i2c_remove(ndev, 1); +} + diff --git a/drivers/media/pci/netup_unidvb/netup_unidvb_spi.c b/drivers/media/pci/netup_unidvb/netup_unidvb_spi.c new file mode 100644 index 000000000000..f55b3276f28d --- /dev/null +++ b/drivers/media/pci/netup_unidvb/netup_unidvb_spi.c @@ -0,0 +1,252 @@ +/* + * netup_unidvb_spi.c + * + * Internal SPI driver for NetUP Universal Dual DVB-CI + * + * Copyright (C) 2014 NetUP Inc. + * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru> + * Copyright (C) 2014 Abylay Ospan <aospan@netup.ru> + * + * 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. + */ + +#include "netup_unidvb.h" +#include <linux/spi/spi.h> +#include <linux/spi/flash.h> +#include <linux/mtd/partitions.h> +#include <mtd/mtd-abi.h> + +#define NETUP_SPI_CTRL_IRQ 0x1000 +#define NETUP_SPI_CTRL_IMASK 0x2000 +#define NETUP_SPI_CTRL_START 0x8000 +#define NETUP_SPI_CTRL_LAST_CS 0x4000 + +#define NETUP_SPI_TIMEOUT 6000 + +enum netup_spi_state { + SPI_STATE_START, + SPI_STATE_DONE, +}; + +struct netup_spi_regs { + __u8 data[1024]; + __le16 control_stat; + __le16 clock_divider; +} __packed __aligned(1); + +struct netup_spi { + struct device *dev; + struct spi_master *master; + struct netup_spi_regs *regs; + u8 __iomem *mmio; + spinlock_t lock; + wait_queue_head_t waitq; + enum netup_spi_state state; +}; + +static char netup_spi_name[64] = "fpga"; + +static struct mtd_partition netup_spi_flash_partitions = { + .name = netup_spi_name, + .size = 0x1000000, /* 16MB */ + .offset = 0, + .mask_flags = MTD_CAP_ROM +}; + +static struct flash_platform_data spi_flash_data = { + .name = "netup0_m25p128", + .parts = &netup_spi_flash_partitions, + .nr_parts = 1, +}; + +static struct spi_board_info netup_spi_board = { + .modalias = "m25p128", + .max_speed_hz = 11000000, + .chip_select = 0, + .mode = SPI_MODE_0, + .platform_data = &spi_flash_data, +}; + +irqreturn_t netup_spi_interrupt(struct netup_spi *spi) +{ + u16 reg; + unsigned long flags; + + if (!spi) { + dev_dbg(&spi->master->dev, + "%s(): SPI not initialized\n", __func__); + return IRQ_NONE; + } + spin_lock_irqsave(&spi->lock, flags); + reg = readw(&spi->regs->control_stat); + if (!(reg & NETUP_SPI_CTRL_IRQ)) { + spin_unlock_irqrestore(&spi->lock, flags); + dev_dbg(&spi->master->dev, + "%s(): not mine interrupt\n", __func__); + return IRQ_NONE; + } + writew(reg | NETUP_SPI_CTRL_IRQ, &spi->regs->control_stat); + reg = readw(&spi->regs->control_stat); + writew(reg & ~NETUP_SPI_CTRL_IMASK, &spi->regs->control_stat); + spi->state = SPI_STATE_DONE; + wake_up(&spi->waitq); + spin_unlock_irqrestore(&spi->lock, flags); + dev_dbg(&spi->master->dev, + "%s(): SPI interrupt handled\n", __func__); + return IRQ_HANDLED; +} + +static int netup_spi_transfer(struct spi_master *master, + struct spi_message *msg) +{ + struct netup_spi *spi = spi_master_get_devdata(master); + struct spi_transfer *t; + int result = 0; + u32 tr_size; + + /* reset CS */ + writew(NETUP_SPI_CTRL_LAST_CS, &spi->regs->control_stat); + writew(0, &spi->regs->control_stat); + list_for_each_entry(t, &msg->transfers, transfer_list) { + tr_size = t->len; + while (tr_size) { + u32 frag_offset = t->len - tr_size; + u32 frag_size = (tr_size > sizeof(spi->regs->data)) ? + sizeof(spi->regs->data) : tr_size; + int frag_last = 0; + + if (list_is_last(&t->transfer_list, + &msg->transfers) && + frag_offset + frag_size == t->len) { + frag_last = 1; + } + if (t->tx_buf) { + memcpy_toio(spi->regs->data, + t->tx_buf + frag_offset, + frag_size); + } else { + memset_io(spi->regs->data, + 0, frag_size); + } + spi->state = SPI_STATE_START; + writew((frag_size & 0x3ff) | + NETUP_SPI_CTRL_IMASK | + NETUP_SPI_CTRL_START | + (frag_last ? NETUP_SPI_CTRL_LAST_CS : 0), + &spi->regs->control_stat); + dev_dbg(&spi->master->dev, + "%s(): control_stat 0x%04x\n", + __func__, readw(&spi->regs->control_stat)); + wait_event_timeout(spi->waitq, + spi->state != SPI_STATE_START, + msecs_to_jiffies(NETUP_SPI_TIMEOUT)); + if (spi->state == SPI_STATE_DONE) { + if (t->rx_buf) { + memcpy_fromio(t->rx_buf + frag_offset, + spi->regs->data, frag_size); + } + } else { + if (spi->state == SPI_STATE_START) { + dev_dbg(&spi->master->dev, + "%s(): transfer timeout\n", + __func__); + } else { + dev_dbg(&spi->master->dev, + "%s(): invalid state %d\n", + __func__, spi->state); + } + result = -EIO; + goto done; + } + tr_size -= frag_size; + msg->actual_length += frag_size; + } + } +done: + msg->status = result; + spi_finalize_current_message(master); + return result; +} + +static int netup_spi_setup(struct spi_device *spi) +{ + return 0; +} + +int netup_spi_init(struct netup_unidvb_dev *ndev) +{ + struct spi_master *master; + struct netup_spi *nspi; + + master = spi_alloc_master(&ndev->pci_dev->dev, + sizeof(struct netup_spi)); + if (!master) { + dev_err(&ndev->pci_dev->dev, + "%s(): unable to alloc SPI master\n", __func__); + return -EINVAL; + } + nspi = spi_master_get_devdata(master); + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST; + master->bus_num = -1; + master->num_chipselect = 1; + master->transfer_one_message = netup_spi_transfer; + master->setup = netup_spi_setup; + spin_lock_init(&nspi->lock); + init_waitqueue_head(&nspi->waitq); + nspi->master = master; + nspi->regs = (struct netup_spi_regs *)(ndev->bmmio0 + 0x4000); + writew(2, &nspi->regs->clock_divider); + writew(NETUP_UNIDVB_IRQ_SPI, ndev->bmmio0 + REG_IMASK_SET); + ndev->spi = nspi; + if (spi_register_master(master)) { + ndev->spi = NULL; + dev_err(&ndev->pci_dev->dev, + "%s(): unable to register SPI bus\n", __func__); + return -EINVAL; + } + snprintf(netup_spi_name, + sizeof(netup_spi_name), + "fpga_%02x:%02x.%01x", + ndev->pci_bus, + ndev->pci_slot, + ndev->pci_func); + if (!spi_new_device(master, &netup_spi_board)) { + ndev->spi = NULL; + dev_err(&ndev->pci_dev->dev, + "%s(): unable to create SPI device\n", __func__); + return -EINVAL; + } + dev_dbg(&ndev->pci_dev->dev, "%s(): SPI init OK\n", __func__); + return 0; +} + +void netup_spi_release(struct netup_unidvb_dev *ndev) +{ + u16 reg; + unsigned long flags; + struct netup_spi *spi = ndev->spi; + + if (!spi) { + dev_dbg(&spi->master->dev, + "%s(): SPI not initialized\n", __func__); + return; + } + spin_lock_irqsave(&spi->lock, flags); + reg = readw(&spi->regs->control_stat); + writew(reg | NETUP_SPI_CTRL_IRQ, &spi->regs->control_stat); + reg = readw(&spi->regs->control_stat); + writew(reg & ~NETUP_SPI_CTRL_IMASK, &spi->regs->control_stat); + spin_unlock_irqrestore(&spi->lock, flags); + spi_unregister_master(spi->master); + ndev->spi = NULL; +} + + diff --git a/drivers/media/pci/smipcie/Kconfig b/drivers/media/pci/smipcie/Kconfig index 21a1583dbd8f..c11c772830c9 100644 --- a/drivers/media/pci/smipcie/Kconfig +++ b/drivers/media/pci/smipcie/Kconfig @@ -7,6 +7,7 @@ config DVB_SMIPCIE select DVB_TS2020 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_M88RS6000T if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_SI2157 if MEDIA_SUBDRV_AUTOSELECT + depends on RC_CORE help Support for cards with SMI PCIe bridge: - DVBSky S950 V3 diff --git a/drivers/media/pci/smipcie/Makefile b/drivers/media/pci/smipcie/Makefile index be55481a6e95..013bc3fe4294 100644 --- a/drivers/media/pci/smipcie/Makefile +++ b/drivers/media/pci/smipcie/Makefile @@ -1,3 +1,6 @@ + +smipcie-objs := smipcie-main.o smipcie-ir.o + obj-$(CONFIG_DVB_SMIPCIE) += smipcie.o ccflags-y += -Idrivers/media/tuners diff --git a/drivers/media/pci/smipcie/smipcie-ir.c b/drivers/media/pci/smipcie/smipcie-ir.c new file mode 100644 index 000000000000..d018673c71f6 --- /dev/null +++ b/drivers/media/pci/smipcie/smipcie-ir.c @@ -0,0 +1,232 @@ +/* + * SMI PCIe driver for DVBSky cards. + * + * Copyright (C) 2014 Max nibble <nibble.max@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "smipcie.h" + +static void smi_ir_enableInterrupt(struct smi_rc *ir) +{ + struct smi_dev *dev = ir->dev; + + smi_write(MSI_INT_ENA_SET, IR_X_INT); +} + +static void smi_ir_disableInterrupt(struct smi_rc *ir) +{ + struct smi_dev *dev = ir->dev; + + smi_write(MSI_INT_ENA_CLR, IR_X_INT); +} + +static void smi_ir_clearInterrupt(struct smi_rc *ir) +{ + struct smi_dev *dev = ir->dev; + + smi_write(MSI_INT_STATUS_CLR, IR_X_INT); +} + +static void smi_ir_stop(struct smi_rc *ir) +{ + struct smi_dev *dev = ir->dev; + + smi_ir_disableInterrupt(ir); + smi_clear(IR_Init_Reg, 0x80); +} + +#define BITS_PER_COMMAND 14 +#define GROUPS_PER_BIT 2 +#define IR_RC5_MIN_BIT 36 +#define IR_RC5_MAX_BIT 52 +static u32 smi_decode_rc5(u8 *pData, u8 size) +{ + u8 index, current_bit, bit_count; + u8 group_array[BITS_PER_COMMAND * GROUPS_PER_BIT + 4]; + u8 group_index = 0; + u32 command = 0xFFFFFFFF; + + group_array[group_index++] = 1; + + for (index = 0; index < size; index++) { + + current_bit = (pData[index] & 0x80) ? 1 : 0; + bit_count = pData[index] & 0x7f; + + if ((current_bit == 1) && (bit_count >= 2*IR_RC5_MAX_BIT + 1)) { + goto process_code; + } else if ((bit_count >= IR_RC5_MIN_BIT) && + (bit_count <= IR_RC5_MAX_BIT)) { + group_array[group_index++] = current_bit; + } else if ((bit_count > IR_RC5_MAX_BIT) && + (bit_count <= 2*IR_RC5_MAX_BIT)) { + group_array[group_index++] = current_bit; + group_array[group_index++] = current_bit; + } else { + goto invalid_timing; + } + if (group_index >= BITS_PER_COMMAND*GROUPS_PER_BIT) + goto process_code; + + if ((group_index == BITS_PER_COMMAND*GROUPS_PER_BIT - 1) + && (group_array[group_index-1] == 0)) { + group_array[group_index++] = 1; + goto process_code; + } + } + +process_code: + if (group_index == (BITS_PER_COMMAND*GROUPS_PER_BIT-1)) + group_array[group_index++] = 1; + + if (group_index == BITS_PER_COMMAND*GROUPS_PER_BIT) { + command = 0; + for (index = 0; index < (BITS_PER_COMMAND*GROUPS_PER_BIT); + index = index + 2) { + if ((group_array[index] == 1) && + (group_array[index+1] == 0)) { + command |= (1 << (BITS_PER_COMMAND - + (index/2) - 1)); + } else if ((group_array[index] == 0) && + (group_array[index+1] == 1)) { + /* */ + } else { + command = 0xFFFFFFFF; + goto invalid_timing; + } + } + } + +invalid_timing: + return command; +} + +static void smi_ir_decode(struct work_struct *work) +{ + struct smi_rc *ir = container_of(work, struct smi_rc, work); + struct smi_dev *dev = ir->dev; + struct rc_dev *rc_dev = ir->rc_dev; + u32 dwIRControl, dwIRData, dwIRCode, scancode; + u8 index, ucIRCount, readLoop, rc5_command, rc5_system, toggle; + + dwIRControl = smi_read(IR_Init_Reg); + if (dwIRControl & rbIRVld) { + ucIRCount = (u8) smi_read(IR_Data_Cnt); + + if (ucIRCount < 4) + goto end_ir_decode; + + readLoop = ucIRCount/4; + if (ucIRCount % 4) + readLoop += 1; + for (index = 0; index < readLoop; index++) { + dwIRData = smi_read(IR_DATA_BUFFER_BASE + (index*4)); + + ir->irData[index*4 + 0] = (u8)(dwIRData); + ir->irData[index*4 + 1] = (u8)(dwIRData >> 8); + ir->irData[index*4 + 2] = (u8)(dwIRData >> 16); + ir->irData[index*4 + 3] = (u8)(dwIRData >> 24); + } + dwIRCode = smi_decode_rc5(ir->irData, ucIRCount); + + if (dwIRCode != 0xFFFFFFFF) { + rc5_command = dwIRCode & 0x3F; + rc5_system = (dwIRCode & 0x7C0) >> 6; + toggle = (dwIRCode & 0x800) ? 1 : 0; + scancode = rc5_system << 8 | rc5_command; + rc_keydown(rc_dev, RC_TYPE_RC5, scancode, toggle); + } + } +end_ir_decode: + smi_set(IR_Init_Reg, 0x04); + smi_ir_enableInterrupt(ir); +} + +/* ir functions call by main driver.*/ +int smi_ir_irq(struct smi_rc *ir, u32 int_status) +{ + int handled = 0; + + if (int_status & IR_X_INT) { + smi_ir_disableInterrupt(ir); + smi_ir_clearInterrupt(ir); + schedule_work(&ir->work); + handled = 1; + } + return handled; +} + +void smi_ir_start(struct smi_rc *ir) +{ + struct smi_dev *dev = ir->dev; + + smi_write(IR_Idle_Cnt_Low, 0x00140070); + msleep(20); + smi_set(IR_Init_Reg, 0x90); + + smi_ir_enableInterrupt(ir); +} + +int smi_ir_init(struct smi_dev *dev) +{ + int ret; + struct rc_dev *rc_dev; + struct smi_rc *ir = &dev->ir; + + rc_dev = rc_allocate_device(); + if (!rc_dev) + return -ENOMEM; + + /* init input device */ + snprintf(ir->input_name, sizeof(ir->input_name), "IR (%s)", + dev->info->name); + snprintf(ir->input_phys, sizeof(ir->input_phys), "pci-%s/ir0", + pci_name(dev->pci_dev)); + + rc_dev->driver_name = "SMI_PCIe"; + rc_dev->input_phys = ir->input_phys; + rc_dev->input_name = ir->input_name; + rc_dev->input_id.bustype = BUS_PCI; + rc_dev->input_id.version = 1; + rc_dev->input_id.vendor = dev->pci_dev->subsystem_vendor; + rc_dev->input_id.product = dev->pci_dev->subsystem_device; + rc_dev->dev.parent = &dev->pci_dev->dev; + + rc_dev->driver_type = RC_DRIVER_SCANCODE; + rc_dev->map_name = RC_MAP_DVBSKY; + + ir->rc_dev = rc_dev; + ir->dev = dev; + + INIT_WORK(&ir->work, smi_ir_decode); + smi_ir_disableInterrupt(ir); + + ret = rc_register_device(rc_dev); + if (ret) + goto ir_err; + + return 0; +ir_err: + rc_free_device(rc_dev); + return ret; +} + +void smi_ir_exit(struct smi_dev *dev) +{ + struct smi_rc *ir = &dev->ir; + struct rc_dev *rc_dev = ir->rc_dev; + + smi_ir_stop(ir); + rc_unregister_device(rc_dev); + ir->rc_dev = NULL; +} diff --git a/drivers/media/pci/smipcie/smipcie.c b/drivers/media/pci/smipcie/smipcie-main.c index 143fd7899ecd..b039a229b7d2 100644 --- a/drivers/media/pci/smipcie/smipcie.c +++ b/drivers/media/pci/smipcie/smipcie-main.c @@ -468,6 +468,7 @@ static irqreturn_t smi_irq_handler(int irq, void *dev_id) struct smi_dev *dev = dev_id; struct smi_port *port0 = &dev->ts_port[0]; struct smi_port *port1 = &dev->ts_port[1]; + struct smi_rc *ir = &dev->ir; int handled = 0; u32 intr_status = smi_read(MSI_INT_STATUS); @@ -480,6 +481,9 @@ static irqreturn_t smi_irq_handler(int irq, void *dev_id) if (dev->info->ts_1) handled += smi_port_irq(port1, intr_status); + /* ir interrupt.*/ + handled += smi_ir_irq(ir, intr_status); + return IRQ_RETVAL(handled); } @@ -993,6 +997,10 @@ static int smi_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto err_del_port0_attach; } + ret = smi_ir_init(dev); + if (ret < 0) + goto err_del_port1_attach; + #ifdef CONFIG_PCI_MSI /* to do msi interrupt.???*/ if (pci_msi_enabled()) ret = pci_enable_msi(dev->pci_dev); @@ -1003,10 +1011,13 @@ static int smi_probe(struct pci_dev *pdev, const struct pci_device_id *id) ret = request_irq(dev->pci_dev->irq, smi_irq_handler, IRQF_SHARED, "SMI_PCIE", dev); if (ret < 0) - goto err_del_port1_attach; + goto err_del_ir; + smi_ir_start(&dev->ir); return 0; +err_del_ir: + smi_ir_exit(dev); err_del_port1_attach: if (dev->info->ts_1) smi_port_detach(&dev->ts_port[1]); @@ -1039,6 +1050,7 @@ static void smi_remove(struct pci_dev *pdev) if (dev->info->ts_0) smi_port_detach(&dev->ts_port[0]); + smi_ir_exit(dev); smi_i2c_exit(dev); iounmap(dev->lmmio); pci_set_drvdata(pdev, NULL); diff --git a/drivers/media/pci/smipcie/smipcie.h b/drivers/media/pci/smipcie/smipcie.h index 10cdf20f4839..68cdda28fd98 100644 --- a/drivers/media/pci/smipcie/smipcie.h +++ b/drivers/media/pci/smipcie/smipcie.h @@ -234,6 +234,17 @@ struct smi_cfg_info { int fe_1; }; +struct smi_rc { + struct smi_dev *dev; + struct rc_dev *rc_dev; + char input_phys[64]; + char input_name[64]; + struct work_struct work; + u8 irData[256]; + + int users; +}; + struct smi_port { struct smi_dev *dev; int idx; @@ -284,6 +295,9 @@ struct smi_dev { /* i2c */ struct i2c_adapter i2c_bus[2]; struct i2c_algo_bit_data i2c_bit[2]; + + /* ir */ + struct smi_rc ir; }; #define smi_read(reg) readl(dev->lmmio + ((reg)>>2)) @@ -296,4 +310,9 @@ struct smi_dev { #define smi_set(reg, bit) smi_andor((reg), (bit), (bit)) #define smi_clear(reg, bit) smi_andor((reg), (bit), 0) +int smi_ir_irq(struct smi_rc *ir, u32 int_status); +void smi_ir_start(struct smi_rc *ir); +void smi_ir_exit(struct smi_dev *dev); +int smi_ir_init(struct smi_dev *dev); + #endif /* #ifndef _SMI_PCIE_H_ */ diff --git a/drivers/media/pci/solo6x10/solo6x10-core.c b/drivers/media/pci/solo6x10/solo6x10-core.c index 570d119ea18b..f50d07229236 100644 --- a/drivers/media/pci/solo6x10/solo6x10-core.c +++ b/drivers/media/pci/solo6x10/solo6x10-core.c @@ -134,23 +134,11 @@ static irqreturn_t solo_isr(int irq, void *data) static void free_solo_dev(struct solo_dev *solo_dev) { - struct pci_dev *pdev; - - if (!solo_dev) - return; + struct pci_dev *pdev = solo_dev->pdev; 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); @@ -164,9 +152,8 @@ static void free_solo_dev(struct solo_dev *solo_dev) /* Now cleanup the PCI device */ solo_irq_off(solo_dev, ~0); + free_irq(pdev->irq, solo_dev); pci_iounmap(pdev, solo_dev->reg_base); - if (pdev->irq) - free_irq(pdev->irq, solo_dev); } pci_release_regions(pdev); @@ -483,7 +470,6 @@ static int solo_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) 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; diff --git a/drivers/media/pci/solo6x10/solo6x10-g723.c b/drivers/media/pci/solo6x10/solo6x10-g723.c index 7ddc76709caa..4a37a1c51c48 100644 --- a/drivers/media/pci/solo6x10/solo6x10-g723.c +++ b/drivers/media/pci/solo6x10/solo6x10-g723.c @@ -48,10 +48,8 @@ /* 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 G723_MAX_BUFFER (G723_PERIOD_BYTES * PERIODS_MAX) +#define PERIODS G723_FDMA_PAGES #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; @@ -130,11 +128,11 @@ static const struct snd_pcm_hardware snd_solo_pcm_hw = { .rate_max = SAMPLERATE, .channels_min = 1, .channels_max = 1, - .buffer_bytes_max = G723_MAX_BUFFER, + .buffer_bytes_max = G723_PERIOD_BYTES * PERIODS, .period_bytes_min = G723_PERIOD_BYTES, .period_bytes_max = G723_PERIOD_BYTES, - .periods_min = PERIODS_MIN, - .periods_max = PERIODS_MAX, + .periods_min = PERIODS, + .periods_max = PERIODS, }; static int snd_solo_pcm_open(struct snd_pcm_substream *ss) @@ -340,7 +338,8 @@ 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), - G723_MAX_BUFFER, G723_MAX_BUFFER); + G723_PERIOD_BYTES * PERIODS, + G723_PERIOD_BYTES * PERIODS); if (ret < 0) return ret; diff --git a/drivers/media/pci/solo6x10/solo6x10.h b/drivers/media/pci/solo6x10/solo6x10.h index 1ca54b08b3aa..27423d7f5410 100644 --- a/drivers/media/pci/solo6x10/solo6x10.h +++ b/drivers/media/pci/solo6x10/solo6x10.h @@ -199,7 +199,6 @@ struct solo_dev { int nr_ext; u32 irq_mask; u32 motion_mask; - spinlock_t reg_io_lock; struct v4l2_device v4l2_dev; /* tw28xx accounting */ @@ -281,36 +280,13 @@ struct solo_dev { static inline u32 solo_reg_read(struct solo_dev *solo_dev, int reg) { - unsigned long flags; - u32 ret; - u16 val; - - spin_lock_irqsave(&solo_dev->reg_io_lock, flags); - - ret = readl(solo_dev->reg_base + reg); - rmb(); - pci_read_config_word(solo_dev->pdev, PCI_STATUS, &val); - rmb(); - - spin_unlock_irqrestore(&solo_dev->reg_io_lock, flags); - - return ret; + return readl(solo_dev->reg_base + reg); } static inline void solo_reg_write(struct solo_dev *solo_dev, int reg, u32 data) { - unsigned long flags; - u16 val; - - spin_lock_irqsave(&solo_dev->reg_io_lock, flags); - writel(data, solo_dev->reg_base + reg); - wmb(); - pci_read_config_word(solo_dev->pdev, PCI_STATUS, &val); - rmb(); - - spin_unlock_irqrestore(&solo_dev->reg_io_lock, flags); } static inline void solo_irq_on(struct solo_dev *dev, u32 mask) diff --git a/drivers/media/pci/ttpci/budget-av.c b/drivers/media/pci/ttpci/budget-av.c index 54c9910256f8..3e469d4e0c87 100644 --- a/drivers/media/pci/ttpci/budget-av.c +++ b/drivers/media/pci/ttpci/budget-av.c @@ -1508,7 +1508,7 @@ static int budget_av_attach(struct saa7146_dev *dev, struct saa7146_pci_extensio if (i2c_readregs(&budget_av->budget.i2c_adap, 0xa0, 0x30, mac, 6)) { pr_err("KNC1-%d: Could not read MAC from KNC1 card\n", budget_av->budget.dvb_adapter.num); - memset(mac, 0, 6); + eth_zero_addr(mac); } else { pr_info("KNC1-%d: MAC addr = %pM\n", budget_av->budget.dvb_adapter.num, mac); diff --git a/drivers/media/pci/ttpci/ttpci-eeprom.c b/drivers/media/pci/ttpci/ttpci-eeprom.c index 32d43156c548..079ee098b7e3 100644 --- a/drivers/media/pci/ttpci/ttpci-eeprom.c +++ b/drivers/media/pci/ttpci/ttpci-eeprom.c @@ -36,6 +36,7 @@ #include <linux/module.h> #include <linux/string.h> #include <linux/i2c.h> +#include <linux/etherdevice.h> #include "ttpci-eeprom.h" @@ -145,7 +146,7 @@ int ttpci_eeprom_parse_mac(struct i2c_adapter *adapter, u8 *proposed_mac) if (ret != 0) { /* Will only be -ENODEV */ dprintk("Couldn't read from EEPROM: not there?\n"); - memset(proposed_mac, 0, 6); + eth_zero_addr(proposed_mac); return ret; } @@ -157,14 +158,12 @@ int ttpci_eeprom_parse_mac(struct i2c_adapter *adapter, u8 *proposed_mac) dprintk( "%.2x:", encodedMAC[i]); } dprintk("%.2x\n", encodedMAC[19]); - memset(proposed_mac, 0, 6); + eth_zero_addr(proposed_mac); return ret; } memcpy(proposed_mac, decodedMAC, 6); - dprintk("adapter has MAC addr = %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", - decodedMAC[0], decodedMAC[1], decodedMAC[2], - decodedMAC[3], decodedMAC[4], decodedMAC[5]); + dprintk("adapter has MAC addr = %pM\n", decodedMAC); return 0; } diff --git a/drivers/media/pci/tw68/tw68-core.c b/drivers/media/pci/tw68/tw68-core.c index c135165a8b26..04706cc9b818 100644 --- a/drivers/media/pci/tw68/tw68-core.c +++ b/drivers/media/pci/tw68/tw68-core.c @@ -37,6 +37,7 @@ #include <linux/delay.h> #include <linux/mutex.h> #include <linux/dma-mapping.h> +#include <linux/pci_ids.h> #include <linux/pm.h> #include <media/v4l2-dev.h> @@ -70,13 +71,13 @@ static atomic_t tw68_instance = ATOMIC_INIT(0); * added under vendor 0x1797 (Techwell Inc.) as subsystem IDs. */ static const struct pci_device_id tw68_pci_tbl[] = { - {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_6800)}, - {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_6801)}, - {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_6804)}, - {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_6816_1)}, - {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_6816_2)}, - {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_6816_3)}, - {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_6816_4)}, + {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_TECHWELL_6800)}, + {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_TECHWELL_6801)}, + {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_TECHWELL_6804)}, + {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_TECHWELL_6816_1)}, + {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_TECHWELL_6816_2)}, + {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_TECHWELL_6816_3)}, + {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_TECHWELL_6816_4)}, {0,} }; @@ -263,15 +264,15 @@ static int tw68_initdev(struct pci_dev *pci_dev, } switch (pci_id->device) { - case PCI_DEVICE_ID_6800: /* TW6800 */ + case PCI_DEVICE_ID_TECHWELL_6800: /* TW6800 */ dev->vdecoder = TW6800; dev->board_virqmask = TW68_VID_INTS; break; - case PCI_DEVICE_ID_6801: /* Video decoder for TW6802 */ + case PCI_DEVICE_ID_TECHWELL_6801: /* Video decoder for TW6802 */ dev->vdecoder = TW6801; dev->board_virqmask = TW68_VID_INTS | TW68_VID_INTSX; break; - case PCI_DEVICE_ID_6804: /* Video decoder for TW6804 */ + case PCI_DEVICE_ID_TECHWELL_6804: /* Video decoder for TW6804 */ dev->vdecoder = TW6804; dev->board_virqmask = TW68_VID_INTS | TW68_VID_INTSX; break; diff --git a/drivers/media/pci/tw68/tw68.h b/drivers/media/pci/tw68/tw68.h index 93f2335e004b..ef51e4d48866 100644 --- a/drivers/media/pci/tw68/tw68.h +++ b/drivers/media/pci/tw68/tw68.h @@ -42,22 +42,6 @@ #define UNSET (-1U) -/* system vendor and device ID's */ -#define PCI_VENDOR_ID_TECHWELL 0x1797 -#define PCI_DEVICE_ID_6800 0x6800 -#define PCI_DEVICE_ID_6801 0x6801 -#define PCI_DEVICE_ID_AUDIO2 0x6802 -#define PCI_DEVICE_ID_TS3 0x6803 -#define PCI_DEVICE_ID_6804 0x6804 -#define PCI_DEVICE_ID_AUDIO5 0x6805 -#define PCI_DEVICE_ID_TS6 0x6806 - -/* tw6816 based cards */ -#define PCI_DEVICE_ID_6816_1 0x6810 -#define PCI_DEVICE_ID_6816_2 0x6811 -#define PCI_DEVICE_ID_6816_3 0x6812 -#define PCI_DEVICE_ID_6816_4 0x6813 - #define TW68_NORMS ( \ V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM | \ V4L2_STD_PAL_M | V4L2_STD_PAL_Nc | V4L2_STD_PAL_60) diff --git a/drivers/media/pci/zoran/zoran.h b/drivers/media/pci/zoran/zoran.h index 5e040085c2ff..4e7db8939c2b 100644 --- a/drivers/media/pci/zoran/zoran.h +++ b/drivers/media/pci/zoran/zoran.h @@ -32,6 +32,8 @@ #define _BUZ_H_ #include <media/v4l2-device.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-fh.h> struct zoran_sync { unsigned long frame; /* number of buffer that has been free'd */ @@ -216,6 +218,7 @@ struct zoran; /* zoran_fh contains per-open() settings */ struct zoran_fh { + struct v4l2_fh fh; struct zoran *zr; enum zoran_map_mode map_mode; /* Flag which bufferset will map by next mmap() */ @@ -268,6 +271,7 @@ struct card_info { struct zoran { struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler hdl; struct video_device *video_dev; struct i2c_adapter i2c_adapter; /* */ @@ -280,8 +284,7 @@ struct zoran { struct videocodec *codec; /* video codec */ struct videocodec *vfe; /* video front end */ - struct mutex resource_lock; /* prevent evil stuff */ - struct mutex other_lock; /* please merge with above */ + struct mutex lock; /* file ops serialize lock */ u8 initialized; /* flag if zoran has been correctly initialized */ int user; /* number of current users */ diff --git a/drivers/media/pci/zoran/zoran_card.c b/drivers/media/pci/zoran/zoran_card.c index cec5b7553f28..1136d92af642 100644 --- a/drivers/media/pci/zoran/zoran_card.c +++ b/drivers/media/pci/zoran/zoran_card.c @@ -1049,8 +1049,9 @@ static int zr36057_init (struct zoran *zr) /* * Now add the template and register the device unit. */ - memcpy(zr->video_dev, &zoran_template, sizeof(zoran_template)); + *zr->video_dev = zoran_template; zr->video_dev->v4l2_dev = &zr->v4l2_dev; + zr->video_dev->lock = &zr->lock; strcpy(zr->video_dev->name, ZR_DEVNAME(zr)); /* It's not a mem2mem device, but you can both capture and output from one and the same device. This should really be split up into two @@ -1116,6 +1117,7 @@ static void zoran_remove(struct pci_dev *pdev) pci_disable_device(zr->pci_dev); video_unregister_device(zr->video_dev); exit_free: + v4l2_ctrl_handler_free(&zr->hdl); v4l2_device_unregister(&zr->v4l2_dev); kfree(zr); } @@ -1219,9 +1221,11 @@ static int zoran_probe(struct pci_dev *pdev, const struct pci_device_id *ent) zr->pci_dev = pdev; zr->id = nr; snprintf(ZR_DEVNAME(zr), sizeof(ZR_DEVNAME(zr)), "MJPEG[%u]", zr->id); + if (v4l2_ctrl_handler_init(&zr->hdl, 10)) + goto zr_unreg; + zr->v4l2_dev.ctrl_handler = &zr->hdl; spin_lock_init(&zr->spinlock); - mutex_init(&zr->resource_lock); - mutex_init(&zr->other_lock); + mutex_init(&zr->lock); if (pci_enable_device(pdev)) goto zr_unreg; zr->revision = zr->pci_dev->revision; @@ -1443,6 +1447,7 @@ zr_free_irq: zr_unmap: iounmap(zr->zr36057_mem); zr_unreg: + v4l2_ctrl_handler_free(&zr->hdl); v4l2_device_unregister(&zr->v4l2_dev); zr_free_mem: kfree(zr); diff --git a/drivers/media/pci/zoran/zoran_device.c b/drivers/media/pci/zoran/zoran_device.c index 40119b3c52c1..4d47ddac97dc 100644 --- a/drivers/media/pci/zoran/zoran_device.c +++ b/drivers/media/pci/zoran/zoran_device.c @@ -31,6 +31,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/vmalloc.h> +#include <linux/ktime.h> #include <linux/interrupt.h> #include <linux/proc_fs.h> @@ -181,20 +182,11 @@ dump_guests (struct zoran *zr) } } -static inline unsigned long -get_time (void) -{ - struct timeval tv; - - do_gettimeofday(&tv); - return (1000000 * tv.tv_sec + tv.tv_usec); -} - void detect_guest_activity (struct zoran *zr) { int timeout, i, j, res, guest[8], guest0[8], change[8][3]; - unsigned long t0, t1; + ktime_t t0, t1; dump_guests(zr); printk(KERN_INFO "%s: Detecting guests activity, please wait...\n", @@ -205,15 +197,15 @@ detect_guest_activity (struct zoran *zr) timeout = 0; j = 0; - t0 = get_time(); + t0 = ktime_get(); while (timeout < 10000) { udelay(10); timeout++; for (i = 1; (i < 8) && (j < 8); i++) { res = post_office_read(zr, i, 0); if (res != guest[i]) { - t1 = get_time(); - change[j][0] = (t1 - t0); + t1 = ktime_get(); + change[j][0] = ktime_to_us(ktime_sub(t1, t0)); t0 = t1; change[j][1] = i; change[j][2] = res; diff --git a/drivers/media/pci/zoran/zoran_driver.c b/drivers/media/pci/zoran/zoran_driver.c index 2b25d31c46f6..80caa70c6360 100644 --- a/drivers/media/pci/zoran/zoran_driver.c +++ b/drivers/media/pci/zoran/zoran_driver.c @@ -61,6 +61,7 @@ #include <linux/videodev2.h> #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> +#include <media/v4l2-event.h> #include "videocodec.h" #include <asm/byteorder.h> @@ -592,10 +593,14 @@ static int v4l_sync(struct zoran_fh *fh, int frame) return -EPROTO; } + mutex_unlock(&zr->lock); /* wait on this buffer to get ready */ if (!wait_event_interruptible_timeout(zr->v4l_capq, - (zr->v4l_buffers.buffer[frame].state != BUZ_STATE_PEND), 10*HZ)) + (zr->v4l_buffers.buffer[frame].state != BUZ_STATE_PEND), 10*HZ)) { + mutex_lock(&zr->lock); return -ETIME; + } + mutex_lock(&zr->lock); if (signal_pending(current)) return -ERESTARTSYS; @@ -783,6 +788,7 @@ static int jpg_sync(struct zoran_fh *fh, struct zoran_sync *bs) ZR_DEVNAME(zr), __func__); return -EINVAL; } + mutex_unlock(&zr->lock); if (!wait_event_interruptible_timeout(zr->jpg_capq, (zr->jpg_que_tail != zr->jpg_dma_tail || zr->jpg_dma_tail == zr->jpg_dma_head), @@ -793,6 +799,7 @@ static int jpg_sync(struct zoran_fh *fh, struct zoran_sync *bs) udelay(1); zr->codec->control(zr->codec, CODEC_G_STATUS, sizeof(isr), &isr); + mutex_lock(&zr->lock); dprintk(1, KERN_ERR "%s: %s - timeout: codec isr=0x%02x\n", @@ -801,6 +808,7 @@ static int jpg_sync(struct zoran_fh *fh, struct zoran_sync *bs) return -ETIME; } + mutex_lock(&zr->lock); if (signal_pending(current)) return -ERESTARTSYS; @@ -911,7 +919,7 @@ static int zoran_open(struct file *file) dprintk(2, KERN_INFO "%s: %s(%s, pid=[%d]), users(-)=%d\n", ZR_DEVNAME(zr), __func__, current->comm, task_pid_nr(current), zr->user + 1); - mutex_lock(&zr->other_lock); + mutex_lock(&zr->lock); if (zr->user >= 2048) { dprintk(1, KERN_ERR "%s: too many users (%d) on device\n", @@ -930,6 +938,8 @@ static int zoran_open(struct file *file) res = -ENOMEM; goto fail_unlock; } + v4l2_fh_init(&fh->fh, video_devdata(file)); + /* used to be BUZ_MAX_WIDTH/HEIGHT, but that gives overflows * on norm-change! */ fh->overlay_mask = @@ -946,8 +956,6 @@ static int zoran_open(struct file *file) if (zr->user++ == 0) first_open = 1; - /*mutex_unlock(&zr->resource_lock);*/ - /* default setup - TODO: look at flags */ if (first_open) { /* First device open */ zr36057_restart(zr); @@ -961,14 +969,15 @@ static int zoran_open(struct file *file) file->private_data = fh; fh->zr = zr; zoran_open_init_session(fh); - mutex_unlock(&zr->other_lock); + v4l2_fh_add(&fh->fh); + mutex_unlock(&zr->lock); return 0; fail_fh: kfree(fh); fail_unlock: - mutex_unlock(&zr->other_lock); + mutex_unlock(&zr->lock); dprintk(2, KERN_INFO "%s: open failed (%d), users(-)=%d\n", ZR_DEVNAME(zr), res, zr->user); @@ -987,7 +996,7 @@ zoran_close(struct file *file) /* kernel locks (fs/device.c), so don't do that ourselves * (prevents deadlocks) */ - mutex_lock(&zr->other_lock); + mutex_lock(&zr->lock); zoran_close_end_session(fh); @@ -1021,9 +1030,10 @@ zoran_close(struct file *file) encoder_call(zr, video, s_routing, 2, 0, 0); } } - mutex_unlock(&zr->other_lock); + mutex_unlock(&zr->lock); - file->private_data = NULL; + v4l2_fh_del(&fh->fh); + v4l2_fh_exit(&fh->fh); kfree(fh->overlay_mask); kfree(fh); @@ -1032,29 +1042,6 @@ zoran_close(struct file *file) return 0; } - -static ssize_t -zoran_read (struct file *file, - char __user *data, - size_t count, - loff_t *ppos) -{ - /* we simply don't support read() (yet)... */ - - return -EINVAL; -} - -static ssize_t -zoran_write (struct file *file, - const char __user *data, - size_t count, - loff_t *ppos) -{ - /* ...and the same goes for write() */ - - return -EINVAL; -} - static int setup_fbuffer(struct zoran_fh *fh, void *base, const struct zoran_format *fmt, @@ -1523,7 +1510,6 @@ static int zoran_querycap(struct file *file, void *__fh, struct v4l2_capability struct zoran_fh *fh = __fh; struct zoran *zr = fh->zr; - memset(cap, 0, sizeof(*cap)); strncpy(cap->card, ZR_DEVNAME(zr), sizeof(cap->card)-1); strncpy(cap->driver, "zoran", sizeof(cap->driver)-1); snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI:%s", @@ -1583,9 +1569,6 @@ static int zoran_g_fmt_vid_out(struct file *file, void *__fh, struct v4l2_format *fmt) { struct zoran_fh *fh = __fh; - struct zoran *zr = fh->zr; - - mutex_lock(&zr->resource_lock); fmt->fmt.pix.width = fh->jpg_settings.img_width / fh->jpg_settings.HorDcm; fmt->fmt.pix.height = fh->jpg_settings.img_height * 2 / @@ -1601,7 +1584,6 @@ static int zoran_g_fmt_vid_out(struct file *file, void *__fh, fmt->fmt.pix.bytesperline = 0; fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - mutex_unlock(&zr->resource_lock); return 0; } @@ -1614,7 +1596,6 @@ static int zoran_g_fmt_vid_cap(struct file *file, void *__fh, if (fh->map_mode != ZORAN_MAP_MODE_RAW) return zoran_g_fmt_vid_out(file, fh, fmt); - mutex_lock(&zr->resource_lock); fmt->fmt.pix.width = fh->v4l_settings.width; fmt->fmt.pix.height = fh->v4l_settings.height; fmt->fmt.pix.sizeimage = fh->v4l_settings.bytesperline * @@ -1626,7 +1607,6 @@ static int zoran_g_fmt_vid_cap(struct file *file, void *__fh, fmt->fmt.pix.field = V4L2_FIELD_INTERLACED; else fmt->fmt.pix.field = V4L2_FIELD_TOP; - mutex_unlock(&zr->resource_lock); return 0; } @@ -1636,8 +1616,6 @@ static int zoran_g_fmt_vid_overlay(struct file *file, void *__fh, struct zoran_fh *fh = __fh; struct zoran *zr = fh->zr; - mutex_lock(&zr->resource_lock); - fmt->fmt.win.w.left = fh->overlay_settings.x; fmt->fmt.win.w.top = fh->overlay_settings.y; fmt->fmt.win.w.width = fh->overlay_settings.width; @@ -1647,7 +1625,6 @@ static int zoran_g_fmt_vid_overlay(struct file *file, void *__fh, else fmt->fmt.win.field = V4L2_FIELD_TOP; - mutex_unlock(&zr->resource_lock); return 0; } @@ -1657,8 +1634,6 @@ static int zoran_try_fmt_vid_overlay(struct file *file, void *__fh, struct zoran_fh *fh = __fh; struct zoran *zr = fh->zr; - mutex_lock(&zr->resource_lock); - if (fmt->fmt.win.w.width > BUZ_MAX_WIDTH) fmt->fmt.win.w.width = BUZ_MAX_WIDTH; if (fmt->fmt.win.w.width < BUZ_MIN_WIDTH) @@ -1668,7 +1643,6 @@ static int zoran_try_fmt_vid_overlay(struct file *file, void *__fh, if (fmt->fmt.win.w.height < BUZ_MIN_HEIGHT) fmt->fmt.win.w.height = BUZ_MIN_HEIGHT; - mutex_unlock(&zr->resource_lock); return 0; } @@ -1683,7 +1657,6 @@ static int zoran_try_fmt_vid_out(struct file *file, void *__fh, if (fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG) return -EINVAL; - mutex_lock(&zr->resource_lock); settings = fh->jpg_settings; /* we actually need to set 'real' parameters now */ @@ -1718,7 +1691,7 @@ static int zoran_try_fmt_vid_out(struct file *file, void *__fh, /* check */ res = zoran_check_jpg_settings(zr, &settings, 1); if (res) - goto tryfmt_unlock_and_return; + return res; /* tell the user what we actually did */ fmt->fmt.pix.width = settings.img_width / settings.HorDcm; @@ -1734,8 +1707,6 @@ static int zoran_try_fmt_vid_out(struct file *file, void *__fh, fmt->fmt.pix.sizeimage = zoran_v4l2_calc_bufsize(&settings); fmt->fmt.pix.bytesperline = 0; fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; -tryfmt_unlock_and_return: - mutex_unlock(&zr->resource_lock); return res; } @@ -1750,23 +1721,17 @@ static int zoran_try_fmt_vid_cap(struct file *file, void *__fh, if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG) return zoran_try_fmt_vid_out(file, fh, fmt); - mutex_lock(&zr->resource_lock); - for (i = 0; i < NUM_FORMATS; i++) if (zoran_formats[i].fourcc == fmt->fmt.pix.pixelformat) break; - if (i == NUM_FORMATS) { - mutex_unlock(&zr->resource_lock); + if (i == NUM_FORMATS) return -EINVAL; - } bpp = DIV_ROUND_UP(zoran_formats[i].depth, 8); v4l_bound_align_image( &fmt->fmt.pix.width, BUZ_MIN_WIDTH, BUZ_MAX_WIDTH, bpp == 2 ? 1 : 2, &fmt->fmt.pix.height, BUZ_MIN_HEIGHT, BUZ_MAX_HEIGHT, 0, 0); - mutex_unlock(&zr->resource_lock); - return 0; } @@ -1774,7 +1739,6 @@ static int zoran_s_fmt_vid_overlay(struct file *file, void *__fh, struct v4l2_format *fmt) { struct zoran_fh *fh = __fh; - struct zoran *zr = fh->zr; int res; dprintk(3, "x=%d, y=%d, w=%d, h=%d, cnt=%d, map=0x%p\n", @@ -1783,12 +1747,10 @@ static int zoran_s_fmt_vid_overlay(struct file *file, void *__fh, fmt->fmt.win.w.height, fmt->fmt.win.clipcount, fmt->fmt.win.bitmap); - mutex_lock(&zr->resource_lock); res = setup_window(fh, fmt->fmt.win.w.left, fmt->fmt.win.w.top, fmt->fmt.win.w.width, fmt->fmt.win.w.height, (struct v4l2_clip __user *)fmt->fmt.win.clips, fmt->fmt.win.clipcount, fmt->fmt.win.bitmap); - mutex_unlock(&zr->resource_lock); return res; } @@ -1808,13 +1770,11 @@ static int zoran_s_fmt_vid_out(struct file *file, void *__fh, if (fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG) return -EINVAL; - mutex_lock(&zr->resource_lock); - if (fh->buffers.allocated) { dprintk(1, KERN_ERR "%s: VIDIOC_S_FMT - cannot change capture mode\n", ZR_DEVNAME(zr)); res = -EBUSY; - goto sfmtjpg_unlock_and_return; + return res; } settings = fh->jpg_settings; @@ -1851,7 +1811,7 @@ static int zoran_s_fmt_vid_out(struct file *file, void *__fh, /* check */ res = zoran_check_jpg_settings(zr, &settings, 0); if (res) - goto sfmtjpg_unlock_and_return; + return res; /* it's ok, so set them */ fh->jpg_settings = settings; @@ -1872,9 +1832,6 @@ static int zoran_s_fmt_vid_out(struct file *file, void *__fh, fmt->fmt.pix.bytesperline = 0; fmt->fmt.pix.sizeimage = fh->buffers.buffer_size; fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - -sfmtjpg_unlock_and_return: - mutex_unlock(&zr->resource_lock); return res; } @@ -1898,14 +1855,12 @@ static int zoran_s_fmt_vid_cap(struct file *file, void *__fh, return -EINVAL; } - mutex_lock(&zr->resource_lock); - if ((fh->map_mode != ZORAN_MAP_MODE_RAW && fh->buffers.allocated) || fh->buffers.active != ZORAN_FREE) { dprintk(1, KERN_ERR "%s: VIDIOC_S_FMT - cannot change capture mode\n", ZR_DEVNAME(zr)); res = -EBUSY; - goto sfmtv4l_unlock_and_return; + return res; } if (fmt->fmt.pix.height > BUZ_MAX_HEIGHT) fmt->fmt.pix.height = BUZ_MAX_HEIGHT; @@ -1917,7 +1872,7 @@ static int zoran_s_fmt_vid_cap(struct file *file, void *__fh, res = zoran_v4l_set_format(fh, fmt->fmt.pix.width, fmt->fmt.pix.height, &zoran_formats[i]); if (res) - goto sfmtv4l_unlock_and_return; + return res; /* tell the user the results/missing stuff */ fmt->fmt.pix.bytesperline = fh->v4l_settings.bytesperline; @@ -1927,9 +1882,6 @@ static int zoran_s_fmt_vid_cap(struct file *file, void *__fh, fmt->fmt.pix.field = V4L2_FIELD_INTERLACED; else fmt->fmt.pix.field = V4L2_FIELD_TOP; - -sfmtv4l_unlock_and_return: - mutex_unlock(&zr->resource_lock); return res; } @@ -1940,14 +1892,12 @@ static int zoran_g_fbuf(struct file *file, void *__fh, struct zoran *zr = fh->zr; memset(fb, 0, sizeof(*fb)); - mutex_lock(&zr->resource_lock); fb->base = zr->vbuf_base; fb->fmt.width = zr->vbuf_width; fb->fmt.height = zr->vbuf_height; if (zr->overlay_settings.format) fb->fmt.pixelformat = fh->overlay_settings.format->fourcc; fb->fmt.bytesperline = zr->vbuf_bytesperline; - mutex_unlock(&zr->resource_lock); fb->fmt.colorspace = V4L2_COLORSPACE_SRGB; fb->fmt.field = V4L2_FIELD_INTERLACED; fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING; @@ -1973,10 +1923,8 @@ static int zoran_s_fbuf(struct file *file, void *__fh, return -EINVAL; } - mutex_lock(&zr->resource_lock); res = setup_fbuffer(fh, fb->base, &zoran_formats[i], fb->fmt.width, fb->fmt.height, fb->fmt.bytesperline); - mutex_unlock(&zr->resource_lock); return res; } @@ -1984,12 +1932,9 @@ static int zoran_s_fbuf(struct file *file, void *__fh, static int zoran_overlay(struct file *file, void *__fh, unsigned int on) { struct zoran_fh *fh = __fh; - struct zoran *zr = fh->zr; int res; - mutex_lock(&zr->resource_lock); res = setup_overlay(fh, on); - mutex_unlock(&zr->resource_lock); return res; } @@ -2013,14 +1958,13 @@ static int zoran_reqbufs(struct file *file, void *__fh, struct v4l2_requestbuffe if (req->count == 0) return zoran_streamoff(file, fh, req->type); - mutex_lock(&zr->resource_lock); if (fh->buffers.allocated) { dprintk(2, KERN_ERR "%s: VIDIOC_REQBUFS - buffers already allocated\n", ZR_DEVNAME(zr)); res = -EBUSY; - goto v4l2reqbuf_unlock_and_return; + return res; } if (fh->map_mode == ZORAN_MAP_MODE_RAW && @@ -2037,7 +1981,7 @@ static int zoran_reqbufs(struct file *file, void *__fh, struct v4l2_requestbuffe if (v4l_fbuffer_alloc(fh)) { res = -ENOMEM; - goto v4l2reqbuf_unlock_and_return; + return res; } } else if (fh->map_mode == ZORAN_MAP_MODE_JPG_REC || fh->map_mode == ZORAN_MAP_MODE_JPG_PLAY) { @@ -2054,7 +1998,7 @@ static int zoran_reqbufs(struct file *file, void *__fh, struct v4l2_requestbuffe if (jpg_fbuffer_alloc(fh)) { res = -ENOMEM; - goto v4l2reqbuf_unlock_and_return; + return res; } } else { dprintk(1, @@ -2062,23 +2006,17 @@ static int zoran_reqbufs(struct file *file, void *__fh, struct v4l2_requestbuffe "%s: VIDIOC_REQBUFS - unknown type %d\n", ZR_DEVNAME(zr), req->type); res = -EINVAL; - goto v4l2reqbuf_unlock_and_return; + return res; } -v4l2reqbuf_unlock_and_return: - mutex_unlock(&zr->resource_lock); - return res; } static int zoran_querybuf(struct file *file, void *__fh, struct v4l2_buffer *buf) { struct zoran_fh *fh = __fh; - struct zoran *zr = fh->zr; int res; - mutex_lock(&zr->resource_lock); res = zoran_v4l2_buffer_status(fh, buf, buf->index); - mutex_unlock(&zr->resource_lock); return res; } @@ -2089,8 +2027,6 @@ static int zoran_qbuf(struct file *file, void *__fh, struct v4l2_buffer *buf) struct zoran *zr = fh->zr; int res = 0, codec_mode, buf_type; - mutex_lock(&zr->resource_lock); - switch (fh->map_mode) { case ZORAN_MAP_MODE_RAW: if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { @@ -2098,12 +2034,12 @@ static int zoran_qbuf(struct file *file, void *__fh, struct v4l2_buffer *buf) "%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n", ZR_DEVNAME(zr), buf->type, fh->map_mode); res = -EINVAL; - goto qbuf_unlock_and_return; + return res; } res = zoran_v4l_queue_frame(fh, buf->index); if (res) - goto qbuf_unlock_and_return; + return res; if (!zr->v4l_memgrab_active && fh->buffers.active == ZORAN_LOCKED) zr36057_set_memgrab(zr, 1); break; @@ -2123,12 +2059,12 @@ static int zoran_qbuf(struct file *file, void *__fh, struct v4l2_buffer *buf) "%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n", ZR_DEVNAME(zr), buf->type, fh->map_mode); res = -EINVAL; - goto qbuf_unlock_and_return; + return res; } res = zoran_jpg_queue_frame(fh, buf->index, codec_mode); if (res != 0) - goto qbuf_unlock_and_return; + return res; if (zr->codec_mode == BUZ_MODE_IDLE && fh->buffers.active == ZORAN_LOCKED) zr36057_enable_jpg(zr, codec_mode); @@ -2142,9 +2078,6 @@ static int zoran_qbuf(struct file *file, void *__fh, struct v4l2_buffer *buf) res = -EINVAL; break; } -qbuf_unlock_and_return: - mutex_unlock(&zr->resource_lock); - return res; } @@ -2154,8 +2087,6 @@ static int zoran_dqbuf(struct file *file, void *__fh, struct v4l2_buffer *buf) struct zoran *zr = fh->zr; int res = 0, buf_type, num = -1; /* compiler borks here (?) */ - mutex_lock(&zr->resource_lock); - switch (fh->map_mode) { case ZORAN_MAP_MODE_RAW: if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { @@ -2163,18 +2094,18 @@ static int zoran_dqbuf(struct file *file, void *__fh, struct v4l2_buffer *buf) "%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n", ZR_DEVNAME(zr), buf->type, fh->map_mode); res = -EINVAL; - goto dqbuf_unlock_and_return; + return res; } num = zr->v4l_pend[zr->v4l_sync_tail & V4L_MASK_FRAME]; if (file->f_flags & O_NONBLOCK && zr->v4l_buffers.buffer[num].state != BUZ_STATE_DONE) { res = -EAGAIN; - goto dqbuf_unlock_and_return; + return res; } res = v4l_sync(fh, num); if (res) - goto dqbuf_unlock_and_return; + return res; zr->v4l_sync_tail++; res = zoran_v4l2_buffer_status(fh, buf, num); break; @@ -2194,7 +2125,7 @@ static int zoran_dqbuf(struct file *file, void *__fh, struct v4l2_buffer *buf) "%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n", ZR_DEVNAME(zr), buf->type, fh->map_mode); res = -EINVAL; - goto dqbuf_unlock_and_return; + return res; } num = zr->jpg_pend[zr->jpg_que_tail & BUZ_MASK_FRAME]; @@ -2202,12 +2133,12 @@ static int zoran_dqbuf(struct file *file, void *__fh, struct v4l2_buffer *buf) if (file->f_flags & O_NONBLOCK && zr->jpg_buffers.buffer[num].state != BUZ_STATE_DONE) { res = -EAGAIN; - goto dqbuf_unlock_and_return; + return res; } bs.frame = 0; /* suppress compiler warning */ res = jpg_sync(fh, &bs); if (res) - goto dqbuf_unlock_and_return; + return res; res = zoran_v4l2_buffer_status(fh, buf, bs.frame); break; } @@ -2219,9 +2150,6 @@ static int zoran_dqbuf(struct file *file, void *__fh, struct v4l2_buffer *buf) res = -EINVAL; break; } -dqbuf_unlock_and_return: - mutex_unlock(&zr->resource_lock); - return res; } @@ -2231,14 +2159,12 @@ static int zoran_streamon(struct file *file, void *__fh, enum v4l2_buf_type type struct zoran *zr = fh->zr; int res = 0; - mutex_lock(&zr->resource_lock); - switch (fh->map_mode) { case ZORAN_MAP_MODE_RAW: /* raw capture */ if (zr->v4l_buffers.active != ZORAN_ACTIVE || fh->buffers.active != ZORAN_ACTIVE) { res = -EBUSY; - goto strmon_unlock_and_return; + return res; } zr->v4l_buffers.active = fh->buffers.active = ZORAN_LOCKED; @@ -2257,7 +2183,7 @@ static int zoran_streamon(struct file *file, void *__fh, enum v4l2_buf_type type if (zr->jpg_buffers.active != ZORAN_ACTIVE || fh->buffers.active != ZORAN_ACTIVE) { res = -EBUSY; - goto strmon_unlock_and_return; + return res; } zr->jpg_buffers.active = fh->buffers.active = ZORAN_LOCKED; @@ -2276,9 +2202,6 @@ static int zoran_streamon(struct file *file, void *__fh, enum v4l2_buf_type type res = -EINVAL; break; } -strmon_unlock_and_return: - mutex_unlock(&zr->resource_lock); - return res; } @@ -2289,17 +2212,15 @@ static int zoran_streamoff(struct file *file, void *__fh, enum v4l2_buf_type typ int i, res = 0; unsigned long flags; - mutex_lock(&zr->resource_lock); - switch (fh->map_mode) { case ZORAN_MAP_MODE_RAW: /* raw capture */ if (fh->buffers.active == ZORAN_FREE && zr->v4l_buffers.active != ZORAN_FREE) { res = -EPERM; /* stay off other's settings! */ - goto strmoff_unlock_and_return; + return res; } if (zr->v4l_buffers.active == ZORAN_FREE) - goto strmoff_unlock_and_return; + return res; spin_lock_irqsave(&zr->spinlock, flags); /* unload capture */ @@ -2327,17 +2248,17 @@ static int zoran_streamoff(struct file *file, void *__fh, enum v4l2_buf_type typ if (fh->buffers.active == ZORAN_FREE && zr->jpg_buffers.active != ZORAN_FREE) { res = -EPERM; /* stay off other's settings! */ - goto strmoff_unlock_and_return; + return res; } if (zr->jpg_buffers.active == ZORAN_FREE) - goto strmoff_unlock_and_return; + return res; res = jpg_qbuf(fh, -1, (fh->map_mode == ZORAN_MAP_MODE_JPG_REC) ? BUZ_MODE_MOTION_COMPRESS : BUZ_MODE_MOTION_DECOMPRESS); if (res) - goto strmoff_unlock_and_return; + return res; break; default: dprintk(1, KERN_ERR @@ -2346,70 +2267,14 @@ static int zoran_streamoff(struct file *file, void *__fh, enum v4l2_buf_type typ res = -EINVAL; break; } -strmoff_unlock_and_return: - mutex_unlock(&zr->resource_lock); - return res; } - -static int zoran_queryctrl(struct file *file, void *__fh, - struct v4l2_queryctrl *ctrl) -{ - struct zoran_fh *fh = __fh; - struct zoran *zr = fh->zr; - - /* we only support hue/saturation/contrast/brightness */ - if (ctrl->id < V4L2_CID_BRIGHTNESS || - ctrl->id > V4L2_CID_HUE) - return -EINVAL; - - decoder_call(zr, core, queryctrl, ctrl); - - return 0; -} - -static int zoran_g_ctrl(struct file *file, void *__fh, struct v4l2_control *ctrl) -{ - struct zoran_fh *fh = __fh; - struct zoran *zr = fh->zr; - - /* we only support hue/saturation/contrast/brightness */ - if (ctrl->id < V4L2_CID_BRIGHTNESS || - ctrl->id > V4L2_CID_HUE) - return -EINVAL; - - mutex_lock(&zr->resource_lock); - decoder_call(zr, core, g_ctrl, ctrl); - mutex_unlock(&zr->resource_lock); - - return 0; -} - -static int zoran_s_ctrl(struct file *file, void *__fh, struct v4l2_control *ctrl) -{ - struct zoran_fh *fh = __fh; - struct zoran *zr = fh->zr; - - /* we only support hue/saturation/contrast/brightness */ - if (ctrl->id < V4L2_CID_BRIGHTNESS || - ctrl->id > V4L2_CID_HUE) - return -EINVAL; - - mutex_lock(&zr->resource_lock); - decoder_call(zr, core, s_ctrl, ctrl); - mutex_unlock(&zr->resource_lock); - - return 0; -} - static int zoran_g_std(struct file *file, void *__fh, v4l2_std_id *std) { struct zoran_fh *fh = __fh; struct zoran *zr = fh->zr; - mutex_lock(&zr->resource_lock); *std = zr->norm; - mutex_unlock(&zr->resource_lock); return 0; } @@ -2419,14 +2284,11 @@ static int zoran_s_std(struct file *file, void *__fh, v4l2_std_id std) struct zoran *zr = fh->zr; int res = 0; - mutex_lock(&zr->resource_lock); res = zoran_set_norm(zr, std); if (res) - goto sstd_unlock_and_return; + return res; res = wait_grab_pending(zr); -sstd_unlock_and_return: - mutex_unlock(&zr->resource_lock); return res; } @@ -2445,9 +2307,7 @@ static int zoran_enum_input(struct file *file, void *__fh, inp->std = V4L2_STD_ALL; /* Get status of video decoder */ - mutex_lock(&zr->resource_lock); decoder_call(zr, video, g_input_status, &inp->status); - mutex_unlock(&zr->resource_lock); return 0; } @@ -2456,9 +2316,7 @@ static int zoran_g_input(struct file *file, void *__fh, unsigned int *input) struct zoran_fh *fh = __fh; struct zoran *zr = fh->zr; - mutex_lock(&zr->resource_lock); *input = zr->input; - mutex_unlock(&zr->resource_lock); return 0; } @@ -2469,15 +2327,12 @@ static int zoran_s_input(struct file *file, void *__fh, unsigned int input) struct zoran *zr = fh->zr; int res; - mutex_lock(&zr->resource_lock); res = zoran_set_input(zr, input); if (res) - goto sinput_unlock_and_return; + return res; /* Make sure the changes come into effect */ res = wait_grab_pending(zr); -sinput_unlock_and_return: - mutex_unlock(&zr->resource_lock); return res; } @@ -2520,8 +2375,6 @@ static int zoran_cropcap(struct file *file, void *__fh, memset(cropcap, 0, sizeof(*cropcap)); cropcap->type = type; - mutex_lock(&zr->resource_lock); - if (cropcap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && (cropcap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || fh->map_mode == ZORAN_MAP_MODE_RAW)) { @@ -2529,7 +2382,7 @@ static int zoran_cropcap(struct file *file, void *__fh, "%s: VIDIOC_CROPCAP - subcapture only supported for compressed capture\n", ZR_DEVNAME(zr)); res = -EINVAL; - goto cropcap_unlock_and_return; + return res; } cropcap->bounds.top = cropcap->bounds.left = 0; @@ -2538,8 +2391,6 @@ static int zoran_cropcap(struct file *file, void *__fh, cropcap->defrect.top = cropcap->defrect.left = 0; cropcap->defrect.width = BUZ_MIN_WIDTH; cropcap->defrect.height = BUZ_MIN_HEIGHT; -cropcap_unlock_and_return: - mutex_unlock(&zr->resource_lock); return res; } @@ -2552,8 +2403,6 @@ static int zoran_g_crop(struct file *file, void *__fh, struct v4l2_crop *crop) memset(crop, 0, sizeof(*crop)); crop->type = type; - mutex_lock(&zr->resource_lock); - if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || fh->map_mode == ZORAN_MAP_MODE_RAW)) { @@ -2562,17 +2411,13 @@ static int zoran_g_crop(struct file *file, void *__fh, struct v4l2_crop *crop) "%s: VIDIOC_G_CROP - subcapture only supported for compressed capture\n", ZR_DEVNAME(zr)); res = -EINVAL; - goto gcrop_unlock_and_return; + return res; } crop->c.top = fh->jpg_settings.img_y; crop->c.left = fh->jpg_settings.img_x; crop->c.width = fh->jpg_settings.img_width; crop->c.height = fh->jpg_settings.img_height; - -gcrop_unlock_and_return: - mutex_unlock(&zr->resource_lock); - return res; } @@ -2585,14 +2430,12 @@ static int zoran_s_crop(struct file *file, void *__fh, const struct v4l2_crop *c settings = fh->jpg_settings; - mutex_lock(&zr->resource_lock); - if (fh->buffers.allocated) { dprintk(1, KERN_ERR "%s: VIDIOC_S_CROP - cannot change settings while active\n", ZR_DEVNAME(zr)); res = -EBUSY; - goto scrop_unlock_and_return; + return res; } if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && @@ -2602,7 +2445,7 @@ static int zoran_s_crop(struct file *file, void *__fh, const struct v4l2_crop *c "%s: VIDIOC_G_CROP - subcapture only supported for compressed capture\n", ZR_DEVNAME(zr)); res = -EINVAL; - goto scrop_unlock_and_return; + return res; } /* move into a form that we understand */ @@ -2614,13 +2457,10 @@ static int zoran_s_crop(struct file *file, void *__fh, const struct v4l2_crop *c /* check validity */ res = zoran_check_jpg_settings(zr, &settings, 0); if (res) - goto scrop_unlock_and_return; + return res; /* accept */ fh->jpg_settings = settings; - -scrop_unlock_and_return: - mutex_unlock(&zr->resource_lock); return res; } @@ -2628,11 +2468,8 @@ static int zoran_g_jpegcomp(struct file *file, void *__fh, struct v4l2_jpegcompression *params) { struct zoran_fh *fh = __fh; - struct zoran *zr = fh->zr; memset(params, 0, sizeof(*params)); - mutex_lock(&zr->resource_lock); - params->quality = fh->jpg_settings.jpg_comp.quality; params->APPn = fh->jpg_settings.jpg_comp.APPn; memcpy(params->APP_data, @@ -2646,8 +2483,6 @@ static int zoran_g_jpegcomp(struct file *file, void *__fh, params->jpeg_markers = fh->jpg_settings.jpg_comp.jpeg_markers; - mutex_unlock(&zr->resource_lock); - return 0; } @@ -2663,26 +2498,21 @@ static int zoran_s_jpegcomp(struct file *file, void *__fh, settings.jpg_comp = *params; - mutex_lock(&zr->resource_lock); - if (fh->buffers.active != ZORAN_FREE) { dprintk(1, KERN_WARNING "%s: VIDIOC_S_JPEGCOMP called while in playback/capture mode\n", ZR_DEVNAME(zr)); res = -EBUSY; - goto sjpegc_unlock_and_return; + return res; } res = zoran_check_jpg_settings(zr, &settings, 0); if (res) - goto sjpegc_unlock_and_return; + return res; if (!fh->buffers.allocated) fh->buffers.buffer_size = zoran_v4l2_calc_bufsize(&fh->jpg_settings); fh->jpg_settings.jpg_comp = settings.jpg_comp; -sjpegc_unlock_and_return: - mutex_unlock(&zr->resource_lock); - return res; } @@ -2692,7 +2522,8 @@ zoran_poll (struct file *file, { struct zoran_fh *fh = file->private_data; struct zoran *zr = fh->zr; - int res = 0, frame; + int res = v4l2_ctrl_poll(file, wait); + int frame; unsigned long flags; /* we should check whether buffers are ready to be synced on @@ -2703,8 +2534,6 @@ zoran_poll (struct file *file, * if no buffers queued or so, return POLLNVAL */ - mutex_lock(&zr->resource_lock); - switch (fh->map_mode) { case ZORAN_MAP_MODE_RAW: poll_wait(file, &zr->v4l_capq, wait); @@ -2722,7 +2551,7 @@ zoran_poll (struct file *file, if (fh->buffers.active != ZORAN_FREE && /* Buffer ready to DQBUF? */ zr->v4l_buffers.buffer[frame].state == BUZ_STATE_DONE) - res = POLLIN | POLLRDNORM; + res |= POLLIN | POLLRDNORM; spin_unlock_irqrestore(&zr->spinlock, flags); break; @@ -2743,9 +2572,9 @@ zoran_poll (struct file *file, if (fh->buffers.active != ZORAN_FREE && zr->jpg_buffers.buffer[frame].state == BUZ_STATE_DONE) { if (fh->map_mode == ZORAN_MAP_MODE_JPG_REC) - res = POLLIN | POLLRDNORM; + res |= POLLIN | POLLRDNORM; else - res = POLLOUT | POLLWRNORM; + res |= POLLOUT | POLLWRNORM; } spin_unlock_irqrestore(&zr->spinlock, flags); @@ -2756,11 +2585,9 @@ zoran_poll (struct file *file, KERN_ERR "%s: %s - internal error, unknown map_mode=%d\n", ZR_DEVNAME(zr), __func__, fh->map_mode); - res = POLLNVAL; + res |= POLLERR; } - mutex_unlock(&zr->resource_lock); - return res; } @@ -2792,9 +2619,6 @@ zoran_vm_close (struct vm_area_struct *vma) struct zoran *zr = fh->zr; int i; - if (!atomic_dec_and_mutex_lock(&map->count, &zr->resource_lock)) - return; - dprintk(3, KERN_INFO "%s: %s - munmap(%s)\n", ZR_DEVNAME(zr), __func__, mode_name(fh->map_mode)); @@ -2807,7 +2631,6 @@ zoran_vm_close (struct vm_area_struct *vma) /* Any buffers still mapped? */ for (i = 0; i < fh->buffers.num_buffers; i++) { if (fh->buffers.buffer[i].map) { - mutex_unlock(&zr->resource_lock); return; } } @@ -2815,7 +2638,6 @@ zoran_vm_close (struct vm_area_struct *vma) dprintk(3, KERN_INFO "%s: %s - free %s buffers\n", ZR_DEVNAME(zr), __func__, mode_name(fh->map_mode)); - if (fh->map_mode == ZORAN_MAP_MODE_RAW) { if (fh->buffers.active != ZORAN_FREE) { unsigned long flags; @@ -2835,8 +2657,6 @@ zoran_vm_close (struct vm_area_struct *vma) } jpg_fbuffer_free(fh); } - - mutex_unlock(&zr->resource_lock); } static const struct vm_operations_struct zoran_vm_ops = { @@ -2872,15 +2692,13 @@ zoran_mmap (struct file *file, return -EINVAL; } - mutex_lock(&zr->resource_lock); - if (!fh->buffers.allocated) { dprintk(1, KERN_ERR "%s: %s(%s) - buffers not yet allocated\n", ZR_DEVNAME(zr), __func__, mode_name(fh->map_mode)); res = -ENOMEM; - goto mmap_unlock_and_return; + return res; } first = offset / fh->buffers.buffer_size; @@ -2896,7 +2714,7 @@ zoran_mmap (struct file *file, fh->buffers.buffer_size, fh->buffers.num_buffers); res = -EINVAL; - goto mmap_unlock_and_return; + return res; } /* Check if any buffers are already mapped */ @@ -2907,7 +2725,7 @@ zoran_mmap (struct file *file, "%s: %s(%s) - buffer %d already mapped\n", ZR_DEVNAME(zr), __func__, mode_name(fh->map_mode), i); res = -EBUSY; - goto mmap_unlock_and_return; + return res; } } @@ -2915,7 +2733,7 @@ zoran_mmap (struct file *file, map = kmalloc(sizeof(struct zoran_mapping), GFP_KERNEL); if (!map) { res = -ENOMEM; - goto mmap_unlock_and_return; + return res; } map->fh = fh; atomic_set(&map->count, 1); @@ -2937,7 +2755,7 @@ zoran_mmap (struct file *file, "%s: %s(V4L) - remap_pfn_range failed\n", ZR_DEVNAME(zr), __func__); res = -EAGAIN; - goto mmap_unlock_and_return; + return res; } size -= todo; start += todo; @@ -2969,7 +2787,7 @@ zoran_mmap (struct file *file, "%s: %s(V4L) - remap_pfn_range failed\n", ZR_DEVNAME(zr), __func__); res = -EAGAIN; - goto mmap_unlock_and_return; + return res; } size -= todo; start += todo; @@ -2985,10 +2803,6 @@ zoran_mmap (struct file *file, } } - -mmap_unlock_and_return: - mutex_unlock(&zr->resource_lock); - return res; } @@ -3028,33 +2842,15 @@ static const struct v4l2_ioctl_ops zoran_ioctl_ops = { .vidioc_try_fmt_vid_cap = zoran_try_fmt_vid_cap, .vidioc_try_fmt_vid_out = zoran_try_fmt_vid_out, .vidioc_try_fmt_vid_overlay = zoran_try_fmt_vid_overlay, - .vidioc_queryctrl = zoran_queryctrl, - .vidioc_s_ctrl = zoran_s_ctrl, - .vidioc_g_ctrl = zoran_g_ctrl, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; -/* please use zr->resource_lock consistently and kill this wrapper */ -static long zoran_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - struct zoran_fh *fh = file->private_data; - struct zoran *zr = fh->zr; - int ret; - - mutex_lock(&zr->other_lock); - ret = video_ioctl2(file, cmd, arg); - mutex_unlock(&zr->other_lock); - - return ret; -} - static const struct v4l2_file_operations zoran_fops = { .owner = THIS_MODULE, .open = zoran_open, .release = zoran_close, - .unlocked_ioctl = zoran_ioctl, - .read = zoran_read, - .write = zoran_write, + .unlocked_ioctl = video_ioctl2, .mmap = zoran_mmap, .poll = zoran_poll, }; |