From e3e30f63389a319ca45161b07eb74e60f1e7ea20 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 8 Jun 2015 06:53:59 -0300 Subject: [media] stk1160: fix sequence handling Fix the sequence counter: we're counting frames, not fields. Also remove the unused 'field' field. That would only be needed if this driver would support V4L2_FIELD_ALTERNATE. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/stk1160/stk1160-v4l.c | 2 ++ drivers/media/usb/stk1160/stk1160-video.c | 4 +--- drivers/media/usb/stk1160/stk1160.h | 3 +-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/media/usb/stk1160/stk1160-v4l.c b/drivers/media/usb/stk1160/stk1160-v4l.c index 4d313ed4c32e..7291cca58704 100644 --- a/drivers/media/usb/stk1160/stk1160-v4l.c +++ b/drivers/media/usb/stk1160/stk1160-v4l.c @@ -194,6 +194,8 @@ static int stk1160_start_streaming(struct stk1160 *dev) /* Start saa711x */ v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 1); + dev->sequence = 0; + /* Start stk1160 */ stk1160_write_reg(dev, STK1160_DCTRL, 0xb3); stk1160_write_reg(dev, STK1160_DCTRL+3, 0x00); diff --git a/drivers/media/usb/stk1160/stk1160-video.c b/drivers/media/usb/stk1160/stk1160-video.c index 39f1aae209bc..940c3eaea507 100644 --- a/drivers/media/usb/stk1160/stk1160-video.c +++ b/drivers/media/usb/stk1160/stk1160-video.c @@ -96,9 +96,7 @@ void stk1160_buffer_done(struct stk1160 *dev) { struct stk1160_buffer *buf = dev->isoc_ctl.buf; - dev->field_count++; - - buf->vb.v4l2_buf.sequence = dev->field_count >> 1; + buf->vb.v4l2_buf.sequence = dev->sequence++; buf->vb.v4l2_buf.field = V4L2_FIELD_INTERLACED; buf->vb.v4l2_buf.bytesused = buf->bytesused; v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); diff --git a/drivers/media/usb/stk1160/stk1160.h b/drivers/media/usb/stk1160/stk1160.h index abdea484c998..3922a6cabde2 100644 --- a/drivers/media/usb/stk1160/stk1160.h +++ b/drivers/media/usb/stk1160/stk1160.h @@ -151,8 +151,7 @@ struct stk1160 { v4l2_std_id norm; /* current norm */ struct stk1160_fmt *fmt; /* selected format */ - unsigned int field_count; /* not sure ??? */ - enum v4l2_field field; /* also not sure :/ */ + unsigned int sequence; /* i2c i/o */ struct i2c_adapter i2c_adap; -- cgit v1.2.3 From 9d1b1f61a49c66145ebbf2844124987118e65632 Mon Sep 17 00:00:00 2001 From: Krzysztof Hałasa Date: Mon, 8 Jun 2015 10:35:05 -0300 Subject: [media] SOLO6x10: Fix G.723 minimum audio period count The period count is fixed, don't confuse ALSA. Signed-off-by: Krzysztof Ha?asa Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/solo6x10/solo6x10-g723.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) 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; -- cgit v1.2.3 From dd43a6278a9dbda46f56782b54a7a59216a87d23 Mon Sep 17 00:00:00 2001 From: Krzysztof Hałasa Date: Mon, 8 Jun 2015 10:37:15 -0300 Subject: [media] SOLO6x10: unmap registers only after free_irq() Fixes a panic on ARM. Diagnosis by Russell King. Signed-off-by: Krzysztof Ha?asa Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/solo6x10/solo6x10-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/pci/solo6x10/solo6x10-core.c b/drivers/media/pci/solo6x10/solo6x10-core.c index 570d119ea18b..84627e6f7377 100644 --- a/drivers/media/pci/solo6x10/solo6x10-core.c +++ b/drivers/media/pci/solo6x10/solo6x10-core.c @@ -164,9 +164,9 @@ static void free_solo_dev(struct solo_dev *solo_dev) /* Now cleanup the PCI device */ solo_irq_off(solo_dev, ~0); - pci_iounmap(pdev, solo_dev->reg_base); if (pdev->irq) free_irq(pdev->irq, solo_dev); + pci_iounmap(pdev, solo_dev->reg_base); } pci_release_regions(pdev); -- cgit v1.2.3 From e1ceb25a1569ce5b61b9c496dd32d038ba8cb936 Mon Sep 17 00:00:00 2001 From: Krzysztof Hałasa Date: Mon, 8 Jun 2015 10:42:24 -0300 Subject: [media] SOLO6x10: remove unneeded register locking and barriers readl() and writel() are atomic, we don't need the spin lock. Also, flushing posted write buffer isn't required. Especially on read :-) Signed-off-by: Krzysztof Ha?asa Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/solo6x10/solo6x10-core.c | 1 - drivers/media/pci/solo6x10/solo6x10.h | 26 +------------------------- 2 files changed, 1 insertion(+), 26 deletions(-) diff --git a/drivers/media/pci/solo6x10/solo6x10-core.c b/drivers/media/pci/solo6x10/solo6x10-core.c index 84627e6f7377..9c948b1fc1d8 100644 --- a/drivers/media/pci/solo6x10/solo6x10-core.c +++ b/drivers/media/pci/solo6x10/solo6x10-core.c @@ -483,7 +483,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.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) -- cgit v1.2.3 From d9b8252202a4cc60a6c5d4fb237d2bd99680e00f Mon Sep 17 00:00:00 2001 From: Krzysztof Hałasa Date: Mon, 8 Jun 2015 10:50:22 -0300 Subject: [media] SOLO6x10: Remove dead code solo_dev and pdev cannot be NULL here. It doesn't matter if we initialized the PCI device or not. Signed-off-by: Krzysztof Ha?asa Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/solo6x10/solo6x10-core.c | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/drivers/media/pci/solo6x10/solo6x10-core.c b/drivers/media/pci/solo6x10/solo6x10-core.c index 9c948b1fc1d8..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,8 +152,7 @@ static void free_solo_dev(struct solo_dev *solo_dev) /* Now cleanup the PCI device */ solo_irq_off(solo_dev, ~0); - if (pdev->irq) - free_irq(pdev->irq, solo_dev); + free_irq(pdev->irq, solo_dev); pci_iounmap(pdev, solo_dev->reg_base); } -- cgit v1.2.3 From e6cf0c409b40349f46427f9fc56f6ce4ec640a5d Mon Sep 17 00:00:00 2001 From: Jan Roemisch Date: Tue, 9 Jun 2015 08:44:33 -0300 Subject: [media] radio-bcm2048: Fix region selection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch fixes region selection for lower bottom_frequency in BCM2048 FM receiver. It also removes "Japan wide band" region since this is impossible to do just like that. Signed-off-by: Jan Roemisch Acked-by: Pali Rohár Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/bcm2048/radio-bcm2048.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/drivers/staging/media/bcm2048/radio-bcm2048.c b/drivers/staging/media/bcm2048/radio-bcm2048.c index 8bc68e2b4052..fb55e5941445 100644 --- a/drivers/staging/media/bcm2048/radio-bcm2048.c +++ b/drivers/staging/media/bcm2048/radio-bcm2048.c @@ -342,14 +342,6 @@ static struct region_info region_configs[] = { .deemphasis = 50, .region = 3, }, - /* Japan wide band */ - { - .channel_spacing = 10, - .bottom_frequency = 76000, - .top_frequency = 108000, - .deemphasis = 50, - .region = 4, - }, }; /* @@ -741,6 +733,18 @@ static int bcm2048_set_region(struct bcm2048_device *bdev, u8 region) mutex_lock(&bdev->mutex); bdev->region_info = region_configs[region]; + + if (region_configs[region].bottom_frequency < 87500) + bdev->cache_fm_ctrl |= BCM2048_BAND_SELECT; + else + bdev->cache_fm_ctrl &= ~BCM2048_BAND_SELECT; + + err = bcm2048_send_command(bdev, BCM2048_I2C_FM_CTRL, + bdev->cache_fm_ctrl); + if (err) { + mutex_unlock(&bdev->mutex); + goto done; + } mutex_unlock(&bdev->mutex); if (bdev->frequency < region_configs[region].bottom_frequency || -- cgit v1.2.3 From cfcffe397fae05d6193bbb0dc87a7148323bf3fb Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 9 Jun 2015 10:54:53 -0300 Subject: [media] rc/Kconfig: fix indentation problem The RC_ST and IR_SUNXI entries have weird indentation, and the RC_ST entry is actually malformed. Fix it. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/Kconfig | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig index ddfab256b9a5..b6e13116c6f5 100644 --- a/drivers/media/rc/Kconfig +++ b/drivers/media/rc/Kconfig @@ -371,21 +371,21 @@ config RC_ST tristate "ST remote control receiver" depends on RC_CORE depends on ARCH_STI || COMPILE_TEST - help - Say Y here if you want support for ST remote control driver - which allows both IR and UHF RX. - The driver passes raw pulse and space information to the LIRC decoder. + ---help--- + Say Y here if you want support for ST remote control driver + which allows both IR and UHF RX. + The driver passes raw pulse and space information to the LIRC decoder. - If you're not sure, select N here. + If you're not sure, select N here. config IR_SUNXI - tristate "SUNXI IR remote control" - depends on RC_CORE - depends on ARCH_SUNXI || COMPILE_TEST - ---help--- - Say Y if you want to use sunXi internal IR Controller - - To compile this driver as a module, choose M here: the module will - be called sunxi-ir. + tristate "SUNXI IR remote control" + depends on RC_CORE + depends on ARCH_SUNXI || COMPILE_TEST + ---help--- + Say Y if you want to use sunXi internal IR Controller + + To compile this driver as a module, choose M here: the module will + be called sunxi-ir. endif #RC_DEVICES -- cgit v1.2.3 From fd7524294ccaa6168593042121e3bbd26a529fd5 Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Wed, 10 Jun 2015 13:32:33 -0300 Subject: [media] v4l2-dv-timings: use swap() in v4l2_calc_aspect_ratio() Use kernel.h macro definition. Thanks to Julia Lawall for Coccinelle scripting support. Signed-off-by: Fabian Frederick Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-dv-timings.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-dv-timings.c b/drivers/media/v4l2-core/v4l2-dv-timings.c index 04dc71e3ebf0..eefad4f17497 100644 --- a/drivers/media/v4l2-core/v4l2-dv-timings.c +++ b/drivers/media/v4l2-core/v4l2-dv-timings.c @@ -665,7 +665,6 @@ EXPORT_SYMBOL_GPL(v4l2_detect_gtf); struct v4l2_fract v4l2_calc_aspect_ratio(u8 hor_landscape, u8 vert_portrait) { struct v4l2_fract aspect = { 16, 9 }; - u32 tmp; u8 ratio; /* Nothing filled in, fallback to 16:9 */ @@ -697,9 +696,7 @@ struct v4l2_fract v4l2_calc_aspect_ratio(u8 hor_landscape, u8 vert_portrait) if (hor_landscape) return aspect; /* The aspect ratio is for portrait, so swap numerator and denominator */ - tmp = aspect.denominator; - aspect.denominator = aspect.numerator; - aspect.numerator = tmp; + swap(aspect.denominator, aspect.numerator); return aspect; } EXPORT_SYMBOL_GPL(v4l2_calc_aspect_ratio); -- cgit v1.2.3 From 453f847ef2e445a7b1fe6cbe35d36a06c60f58a5 Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Wed, 10 Jun 2015 13:32:39 -0300 Subject: [media] wl128x: use swap() in fm_rdsparse_swapbytes() Use kernel.h macro definition. Thanks to Julia Lawall for Coccinelle scripting support. Signed-off-by: Fabian Frederick Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/wl128x/fmdrv_common.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/media/radio/wl128x/fmdrv_common.c b/drivers/media/radio/wl128x/fmdrv_common.c index 704397f3c106..ebc73b034249 100644 --- a/drivers/media/radio/wl128x/fmdrv_common.c +++ b/drivers/media/radio/wl128x/fmdrv_common.c @@ -689,7 +689,6 @@ static void fm_rx_update_af_cache(struct fmdev *fmdev, u8 af) static void fm_rdsparse_swapbytes(struct fmdev *fmdev, struct fm_rdsdata_format *rds_format) { - u8 byte1; u8 index = 0; u8 *rds_buff; @@ -701,9 +700,7 @@ static void fm_rdsparse_swapbytes(struct fmdev *fmdev, if (fmdev->asci_id != 0x6350) { rds_buff = &rds_format->data.groupdatabuff.buff[0]; while (index + 1 < FM_RX_RDS_INFO_FIELD_MAX) { - byte1 = rds_buff[index]; - rds_buff[index] = rds_buff[index + 1]; - rds_buff[index + 1] = byte1; + swap(rds_buff[index], rds_buff[index + 1]); index += 2; } } -- cgit v1.2.3 From 2bb00da1484073342a1f3e9f846ac0ad8dc6314d Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Wed, 10 Jun 2015 13:32:41 -0300 Subject: [media] saa7146: use swap() in sort_and_eliminate() Use kernel.h macro definition. Thanks to Julia Lawall for Coccinelle scripting support. Signed-off-by: Fabian Frederick Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/saa7146/saa7146_hlp.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/media/common/saa7146/saa7146_hlp.c b/drivers/media/common/saa7146/saa7146_hlp.c index be746d1aee9a..3dc6a838ca6f 100644 --- a/drivers/media/common/saa7146/saa7146_hlp.c +++ b/drivers/media/common/saa7146/saa7146_hlp.c @@ -307,7 +307,7 @@ static int calculate_v_scale_registers(struct saa7146_dev *dev, enum v4l2_field /* simple bubble-sort algorithm with duplicate elimination */ static int sort_and_eliminate(u32* values, int* count) { - int low = 0, high = 0, top = 0, temp = 0; + int low = 0, high = 0, top = 0; int cur = 0, next = 0; /* sanity checks */ @@ -318,11 +318,8 @@ static int sort_and_eliminate(u32* values, int* count) /* bubble sort the first @count items of the array @values */ for( top = *count; top > 0; top--) { for( low = 0, high = 1; high < top; low++, high++) { - if( values[low] > values[high] ) { - temp = values[low]; - values[low] = values[high]; - values[high] = temp; - } + if( values[low] > values[high] ) + swap(values[low], values[high]); } } -- cgit v1.2.3 From f47c183c20d845af9d747fc1e5b7dbd9c3871e05 Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Wed, 10 Jun 2015 13:32:50 -0300 Subject: [media] saa6588: use swap() in saa6588_i2c_poll() Use kernel.h macro definition. Thanks to Julia Lawall for Coccinelle scripting support. Signed-off-by: Fabian Frederick Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/saa6588.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/media/i2c/saa6588.c b/drivers/media/i2c/saa6588.c index 2960b5a8362a..2240e0a67db1 100644 --- a/drivers/media/i2c/saa6588.c +++ b/drivers/media/i2c/saa6588.c @@ -301,9 +301,7 @@ static void saa6588_i2c_poll(struct saa6588 *s) first and the last of the 3 bytes block. */ - tmp = tmpbuf[2]; - tmpbuf[2] = tmpbuf[0]; - tmpbuf[0] = tmp; + swap(tmpbuf[2], tmpbuf[0]); /* Map 'Invalid block E' to 'Invalid Block' */ if (blocknum == 6) -- cgit v1.2.3 From 30533ad10b7c3d7c05b757382aee28458167ab75 Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Wed, 10 Jun 2015 13:33:45 -0300 Subject: [media] btcx-risc: use swap() in btcx_sort_clips() Use kernel.h macro definition. Thanks to Julia Lawall for Coccinelle scripting support. Signed-off-by: Fabian Frederick Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/bt8xx/btcx-risc.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) 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++; } } -- cgit v1.2.3 From 5fea1bb703c360f323c62f6d34b5e947d0590e80 Mon Sep 17 00:00:00 2001 From: Prashant Laddha Date: Wed, 10 Jun 2015 13:51:42 -0300 Subject: [media] v4l2-dv-timings: add support for reduced blanking v2 Added support for reduced blanking version 2 (RB v2) in cvt timings. Standard specifies a fixed vsync pulse of 8 lines to indicate RB v2 timings. Vertical back porch is fixed at 6 lines and vertical front porch is remainder of vertical blanking time. For RB v2, horizontal blanking is fixed at 80 pixels. Horizontal sync is fixed at 32. All horizontal timing counts (active pixels, front, back porches) can be specified upto a precision of 1. RB v2 allows for non standard aspect ratios. In RB v2 vsync does not indicate aspect ratio. In absence of aspect ratio v4l2_detect_cvt() cannot calculate image width from image height. Hence extending the v4l2_detect_cvt() to pass image width in case of RB v2. Signed-off-by: Prashant Laddha Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv7604.c | 2 +- drivers/media/i2c/adv7842.c | 2 +- drivers/media/platform/vivid/vivid-vid-cap.c | 2 +- drivers/media/v4l2-core/v4l2-dv-timings.c | 80 ++++++++++++++++++++-------- include/media/v4l2-dv-timings.h | 6 ++- 5 files changed, 67 insertions(+), 25 deletions(-) diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index 808360fd6539..60630d62e663 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -1328,7 +1328,7 @@ static int stdi2dv_timings(struct v4l2_subdev *sd, } } - if (v4l2_detect_cvt(stdi->lcf + 1, hfreq, stdi->lcvs, + if (v4l2_detect_cvt(stdi->lcf + 1, hfreq, stdi->lcvs, 0, (stdi->hs_pol == '+' ? V4L2_DV_HSYNC_POS_POL : 0) | (stdi->vs_pol == '+' ? V4L2_DV_VSYNC_POS_POL : 0), false, timings)) diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index 4cf79b2422d4..aa0d1a04543b 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -1442,7 +1442,7 @@ static int stdi2dv_timings(struct v4l2_subdev *sd, } } - if (v4l2_detect_cvt(stdi->lcf + 1, hfreq, stdi->lcvs, + if (v4l2_detect_cvt(stdi->lcf + 1, hfreq, stdi->lcvs, 0, (stdi->hs_pol == '+' ? V4L2_DV_HSYNC_POS_POL : 0) | (stdi->vs_pol == '+' ? V4L2_DV_VSYNC_POS_POL : 0), false, timings)) diff --git a/drivers/media/platform/vivid/vivid-vid-cap.c b/drivers/media/platform/vivid/vivid-vid-cap.c index c4268d1b2f82..ed0b8788a66f 100644 --- a/drivers/media/platform/vivid/vivid-vid-cap.c +++ b/drivers/media/platform/vivid/vivid-vid-cap.c @@ -1627,7 +1627,7 @@ static bool valid_cvt_gtf_timings(struct v4l2_dv_timings *timings) h_freq = (u32)bt->pixelclock / total_h_pixel; if (bt->standards == 0 || (bt->standards & V4L2_DV_BT_STD_CVT)) { - if (v4l2_detect_cvt(total_v_lines, h_freq, bt->vsync, + if (v4l2_detect_cvt(total_v_lines, h_freq, bt->vsync, bt->width, bt->polarities, bt->interlaced, timings)) return true; } diff --git a/drivers/media/v4l2-core/v4l2-dv-timings.c b/drivers/media/v4l2-core/v4l2-dv-timings.c index eefad4f17497..ddaad3d7f1dd 100644 --- a/drivers/media/v4l2-core/v4l2-dv-timings.c +++ b/drivers/media/v4l2-core/v4l2-dv-timings.c @@ -316,6 +316,7 @@ EXPORT_SYMBOL_GPL(v4l2_print_dv_timings); */ #define CVT_PXL_CLK_GRAN 250000 /* pixel clock granularity */ +#define CVT_PXL_CLK_GRAN_RB_V2 1000 /* granularity for reduced blanking v2*/ /* Normal blanking */ #define CVT_MIN_V_BPORCH 7 /* lines */ @@ -335,15 +336,22 @@ EXPORT_SYMBOL_GPL(v4l2_print_dv_timings); /* Reduced Blanking */ #define CVT_RB_MIN_V_BPORCH 7 /* lines */ #define CVT_RB_V_FPORCH 3 /* lines */ -#define CVT_RB_MIN_V_BLANK 460 /* us */ +#define CVT_RB_MIN_V_BLANK 460 /* us */ #define CVT_RB_H_SYNC 32 /* pixels */ -#define CVT_RB_H_BPORCH 80 /* pixels */ #define CVT_RB_H_BLANK 160 /* pixels */ +/* Reduce blanking Version 2 */ +#define CVT_RB_V2_H_BLANK 80 /* pixels */ +#define CVT_RB_MIN_V_FPORCH 3 /* lines */ +#define CVT_RB_V2_MIN_V_FPORCH 1 /* lines */ +#define CVT_RB_V_BPORCH 6 /* lines */ /** v4l2_detect_cvt - detect if the given timings follow the CVT standard * @frame_height - the total height of the frame (including blanking) in lines. * @hfreq - the horizontal frequency in Hz. * @vsync - the height of the vertical sync in lines. + * @active_width - active width of image (does not include blanking). This + * information is needed only in case of version 2 of reduced blanking. + * In other cases, this parameter does not have any effect on timings. * @polarities - the horizontal and vertical polarities (same as struct * v4l2_bt_timings polarities). * @interlaced - if this flag is true, it indicates interlaced format @@ -352,20 +360,22 @@ EXPORT_SYMBOL_GPL(v4l2_print_dv_timings); * This function will attempt to detect if the given values correspond to a * valid CVT format. If so, then it will return true, and fmt will be filled * in with the found CVT timings. - * - * TODO: VESA defined a new version 2 of their reduced blanking - * formula. Support for that is currently missing in this CVT - * detection function. */ -bool v4l2_detect_cvt(unsigned frame_height, unsigned hfreq, unsigned vsync, - u32 polarities, bool interlaced, struct v4l2_dv_timings *fmt) +bool v4l2_detect_cvt(unsigned frame_height, + unsigned hfreq, + unsigned vsync, + unsigned active_width, + u32 polarities, + bool interlaced, + struct v4l2_dv_timings *fmt) { int v_fp, v_bp, h_fp, h_bp, hsync; int frame_width, image_height, image_width; bool reduced_blanking; + bool rb_v2 = false; unsigned pix_clk; - if (vsync < 4 || vsync > 7) + if (vsync < 4 || vsync > 8) return false; if (polarities == V4L2_DV_VSYNC_POS_POL) @@ -375,17 +385,35 @@ bool v4l2_detect_cvt(unsigned frame_height, unsigned hfreq, unsigned vsync, else return false; + if (reduced_blanking && vsync == 8) + rb_v2 = true; + + if (rb_v2 && active_width == 0) + return false; + + if (!rb_v2 && vsync > 7) + return false; + if (hfreq == 0) return false; /* Vertical */ if (reduced_blanking) { - v_fp = CVT_RB_V_FPORCH; - v_bp = (CVT_RB_MIN_V_BLANK * hfreq) / 1000000 + 1; - v_bp -= vsync + v_fp; - - if (v_bp < CVT_RB_MIN_V_BPORCH) - v_bp = CVT_RB_MIN_V_BPORCH; + if (rb_v2) { + v_bp = CVT_RB_V_BPORCH; + v_fp = (CVT_RB_MIN_V_BLANK * hfreq) / 1000000 + 1; + v_fp -= vsync + v_bp; + + if (v_fp < CVT_RB_V2_MIN_V_FPORCH) + v_fp = CVT_RB_V2_MIN_V_FPORCH; + } else { + v_fp = CVT_RB_V_FPORCH; + v_bp = (CVT_RB_MIN_V_BLANK * hfreq) / 1000000 + 1; + v_bp -= vsync + v_fp; + + if (v_bp < CVT_RB_MIN_V_BPORCH) + v_bp = CVT_RB_MIN_V_BPORCH; + } } else { v_fp = CVT_MIN_V_PORCH_RND; v_bp = (CVT_MIN_VSYNC_BP * hfreq) / 1000000 + 1 - vsync; @@ -422,22 +450,32 @@ bool v4l2_detect_cvt(unsigned frame_height, unsigned hfreq, unsigned vsync, else return false; break; + case 8: + image_width = active_width; + break; default: return false; } - image_width = image_width & ~7; + if (!rb_v2) + image_width = image_width & ~7; /* Horizontal */ if (reduced_blanking) { - pix_clk = (image_width + CVT_RB_H_BLANK) * hfreq; - pix_clk = (pix_clk / CVT_PXL_CLK_GRAN) * CVT_PXL_CLK_GRAN; + int h_blank; + int clk_gran; + + h_blank = rb_v2 ? CVT_RB_V2_H_BLANK : CVT_RB_H_BLANK; + clk_gran = rb_v2 ? CVT_PXL_CLK_GRAN_RB_V2 : CVT_PXL_CLK_GRAN; - h_bp = CVT_RB_H_BPORCH; + pix_clk = (image_width + h_blank) * hfreq; + pix_clk = (pix_clk / clk_gran) * clk_gran; + + h_bp = h_blank / 2; hsync = CVT_RB_H_SYNC; - h_fp = CVT_RB_H_BLANK - h_bp - hsync; + h_fp = h_blank - h_bp - hsync; - frame_width = image_width + CVT_RB_H_BLANK; + frame_width = image_width + h_blank; } else { unsigned ideal_duty_cycle_per_myriad = 100 * CVT_C_PRIME - (CVT_M_PRIME * 100000) / hfreq; diff --git a/include/media/v4l2-dv-timings.h b/include/media/v4l2-dv-timings.h index eecd3102a618..e18a653549cd 100644 --- a/include/media/v4l2-dv-timings.h +++ b/include/media/v4l2-dv-timings.h @@ -115,6 +115,9 @@ void v4l2_print_dv_timings(const char *dev_prefix, const char *prefix, * @frame_height - the total height of the frame (including blanking) in lines. * @hfreq - the horizontal frequency in Hz. * @vsync - the height of the vertical sync in lines. + * @active_width - active width of image (does not include blanking). This + * information is needed only in case of version 2 of reduced blanking. + * In other cases, this parameter does not have any effect on timings. * @polarities - the horizontal and vertical polarities (same as struct * v4l2_bt_timings polarities). * @interlaced - if this flag is true, it indicates interlaced format @@ -125,7 +128,8 @@ void v4l2_print_dv_timings(const char *dev_prefix, const char *prefix, * in with the found CVT timings. */ bool v4l2_detect_cvt(unsigned frame_height, unsigned hfreq, unsigned vsync, - u32 polarities, bool interlaced, struct v4l2_dv_timings *fmt); + unsigned active_width, u32 polarities, bool interlaced, + struct v4l2_dv_timings *fmt); /** v4l2_detect_gtf - detect if the given timings follow the GTF standard * @frame_height - the total height of the frame (including blanking) in lines. -- cgit v1.2.3 From 1b3b384177b2de010833fefffac6af3d6ffbdfed Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 12 Jun 2015 03:52:54 -0300 Subject: [media] v4l2-dv-timings: log if the timing is reduced blanking V2 The last CVT standard introduced reduced blanking version 2 which is signaled by a vsync of 8. Log this. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-dv-timings.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/v4l2-core/v4l2-dv-timings.c b/drivers/media/v4l2-core/v4l2-dv-timings.c index ddaad3d7f1dd..2c7b9fdfa0af 100644 --- a/drivers/media/v4l2-core/v4l2-dv-timings.c +++ b/drivers/media/v4l2-core/v4l2-dv-timings.c @@ -290,9 +290,11 @@ void v4l2_print_dv_timings(const char *dev_prefix, const char *prefix, (bt->polarities & V4L2_DV_VSYNC_POS_POL) ? "+" : "-", bt->il_vsync, bt->il_vbackporch); pr_info("%s: pixelclock: %llu\n", dev_prefix, bt->pixelclock); - pr_info("%s: flags (0x%x):%s%s%s%s%s\n", dev_prefix, bt->flags, + pr_info("%s: flags (0x%x):%s%s%s%s%s%s\n", dev_prefix, bt->flags, (bt->flags & V4L2_DV_FL_REDUCED_BLANKING) ? " REDUCED_BLANKING" : "", + ((bt->flags & V4L2_DV_FL_REDUCED_BLANKING) && + bt->vsync == 8) ? " (V2)" : "", (bt->flags & V4L2_DV_FL_CAN_REDUCE_FPS) ? " CAN_REDUCE_FPS" : "", (bt->flags & V4L2_DV_FL_REDUCED_FPS) ? -- cgit v1.2.3 From 9aee1ae3312daf0de4c9c614680d06d557133317 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Sat, 16 May 2015 15:11:40 -0300 Subject: [media] media: uapi: vsp1: Use __u32 instead of u32 Don't use the kernel types in uapi headers. Signed-off-by: Joe Perches Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- include/uapi/linux/vsp1.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/uapi/linux/vsp1.h b/include/uapi/linux/vsp1.h index e18858f6e865..9a823696d816 100644 --- a/include/uapi/linux/vsp1.h +++ b/include/uapi/linux/vsp1.h @@ -28,7 +28,7 @@ _IOWR('V', BASE_VIDIOC_PRIVATE + 1, struct vsp1_lut_config) struct vsp1_lut_config { - u32 lut[256]; + __u32 lut[256]; }; #endif /* __VSP1_USER_H__ */ -- cgit v1.2.3 From 5d479386983c5f1bb1aff4f88a027b6143f88a39 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Tue, 19 May 2015 20:08:05 -0300 Subject: [media] v4l: omap3isp: Fix async notifier registration order The async notifier was registered before the v4l2_device was registered and before the notifier callbacks were set. This could lead to missing the bound() and complete() callbacks and to attempting to spin_lock() and uninitialised spin lock. Also fix unregistering the async notifier in the case of an error --- the function may not fail anymore after the notifier is registered. Fixes: da7f3843d2c7 ("[media] omap3isp: Add support for the Device Tree") Signed-off-by: Sakari Ailus Reviewed-by: Sebastian Reichel Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/omap3isp/isp.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c index 18d0a871747f..d1334bb1fe37 100644 --- a/drivers/media/platform/omap3isp/isp.c +++ b/drivers/media/platform/omap3isp/isp.c @@ -2000,10 +2000,8 @@ static int isp_register_entities(struct isp_device *isp) ret = v4l2_device_register_subdev_nodes(&isp->v4l2_dev); done: - if (ret < 0) { + if (ret < 0) isp_unregister_entities(isp); - v4l2_async_notifier_unregister(&isp->notifier); - } return ret; } @@ -2423,10 +2421,6 @@ static int isp_probe(struct platform_device *pdev) ret = isp_of_parse_nodes(&pdev->dev, &isp->notifier); if (ret < 0) return ret; - ret = v4l2_async_notifier_register(&isp->v4l2_dev, - &isp->notifier); - if (ret) - return ret; } else { isp->pdata = pdev->dev.platform_data; isp->syscon = syscon_regmap_lookup_by_pdevname("syscon.0"); @@ -2557,18 +2551,27 @@ static int isp_probe(struct platform_device *pdev) if (ret < 0) goto error_iommu; - isp->notifier.bound = isp_subdev_notifier_bound; - isp->notifier.complete = isp_subdev_notifier_complete; - ret = isp_register_entities(isp); if (ret < 0) goto error_modules; + if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) { + isp->notifier.bound = isp_subdev_notifier_bound; + isp->notifier.complete = isp_subdev_notifier_complete; + + ret = v4l2_async_notifier_register(&isp->v4l2_dev, + &isp->notifier); + if (ret) + goto error_register_entities; + } + isp_core_init(isp, 1); omap3isp_put(isp); return 0; +error_register_entities: + isp_unregister_entities(isp); error_modules: isp_cleanup_modules(isp); error_iommu: -- cgit v1.2.3 From 9d39f05490115bf145e5ea03c0b7ec9d3d015b01 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 12 Jun 2015 20:06:23 -0300 Subject: [media] v4l: omap3isp: Fix sub-device power management code Commit 813f5c0ac5cc ("media: Change media device link_notify behaviour") modified the media controller link setup notification API and updated the OMAP3 ISP driver accordingly. As a side effect it introduced a bug by turning power on after setting the link instead of before. This results in sub-devices not being powered down in some cases when they should be. Fix it. Fixes: 813f5c0ac5cc [media] media: Change media device link_notify behaviour Signed-off-by: Sakari Ailus Cc: stable@vger.kernel.org # since v3.10 Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/omap3isp/isp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c index d1334bb1fe37..12be830d704f 100644 --- a/drivers/media/platform/omap3isp/isp.c +++ b/drivers/media/platform/omap3isp/isp.c @@ -829,14 +829,14 @@ static int isp_pipeline_link_notify(struct media_link *link, u32 flags, int ret; if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH && - !(link->flags & MEDIA_LNK_FL_ENABLED)) { + !(flags & MEDIA_LNK_FL_ENABLED)) { /* Powering off entities is assumed to never fail. */ isp_pipeline_pm_power(source, -sink_use); isp_pipeline_pm_power(sink, -source_use); return 0; } - if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH && + if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH && (flags & MEDIA_LNK_FL_ENABLED)) { ret = isp_pipeline_pm_power(source, sink_use); -- cgit v1.2.3 From 44f4294d5ee58ba3f9dcc0da4cc545c21328eb10 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 12 Apr 2015 09:09:05 -0300 Subject: [media] v4l: omap4iss: Enable driver compilation as a module Now that the driver doesn't use the non-exported omap4_ctrl_pad_readl and omap4_ctrl_pad_writel functions nothing prevents it from being compiled as a module anymore. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/omap4iss/Kconfig | 2 +- drivers/staging/media/omap4iss/TODO | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/staging/media/omap4iss/Kconfig b/drivers/staging/media/omap4iss/Kconfig index 072dac04a750..8d4e3bd1bfe1 100644 --- a/drivers/staging/media/omap4iss/Kconfig +++ b/drivers/staging/media/omap4iss/Kconfig @@ -1,5 +1,5 @@ config VIDEO_OMAP4 - bool "OMAP 4 Camera support" + tristate "OMAP 4 Camera support" depends on VIDEO_V4L2=y && VIDEO_V4L2_SUBDEV_API && I2C=y && ARCH_OMAP4 depends on HAS_DMA select MFD_SYSCON diff --git a/drivers/staging/media/omap4iss/TODO b/drivers/staging/media/omap4iss/TODO index fcde88860a2c..4d220ef82653 100644 --- a/drivers/staging/media/omap4iss/TODO +++ b/drivers/staging/media/omap4iss/TODO @@ -1,4 +1,3 @@ -* Make the driver compile as a module * Fix FIFO/buffer overflows and underflows * Replace dummy resizer code with a real implementation * Fix checkpatch errors and warnings -- cgit v1.2.3 From 408131b85509902b100f7045484c7377a40be99d Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 12 Apr 2015 09:09:05 -0300 Subject: [media] v4l: omap4iss: Remove video node crop support Cropping should be configured on the pipeline subdev nodes, not through the video nodes. Remove crop support on all video nodes. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/omap4iss/iss_video.c | 73 ------------------------------ 1 file changed, 73 deletions(-) diff --git a/drivers/staging/media/omap4iss/iss_video.c b/drivers/staging/media/omap4iss/iss_video.c index 85c54fedddda..40405d8710a6 100644 --- a/drivers/staging/media/omap4iss/iss_video.c +++ b/drivers/staging/media/omap4iss/iss_video.c @@ -639,76 +639,6 @@ iss_video_try_format(struct file *file, void *fh, struct v4l2_format *format) return 0; } -static int -iss_video_cropcap(struct file *file, void *fh, struct v4l2_cropcap *cropcap) -{ - struct iss_video *video = video_drvdata(file); - struct v4l2_subdev *subdev; - int ret; - - subdev = iss_video_remote_subdev(video, NULL); - if (subdev == NULL) - return -EINVAL; - - mutex_lock(&video->mutex); - ret = v4l2_subdev_call(subdev, video, cropcap, cropcap); - mutex_unlock(&video->mutex); - - return ret == -ENOIOCTLCMD ? -ENOTTY : ret; -} - -static int -iss_video_get_crop(struct file *file, void *fh, struct v4l2_crop *crop) -{ - struct iss_video *video = video_drvdata(file); - struct v4l2_subdev_format format; - struct v4l2_subdev *subdev; - u32 pad; - int ret; - - subdev = iss_video_remote_subdev(video, &pad); - if (subdev == NULL) - return -EINVAL; - - /* Try the get crop operation first and fallback to get format if not - * implemented. - */ - ret = v4l2_subdev_call(subdev, video, g_crop, crop); - if (ret != -ENOIOCTLCMD) - return ret; - - format.pad = pad; - format.which = V4L2_SUBDEV_FORMAT_ACTIVE; - ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &format); - if (ret < 0) - return ret == -ENOIOCTLCMD ? -ENOTTY : ret; - - crop->c.left = 0; - crop->c.top = 0; - crop->c.width = format.format.width; - crop->c.height = format.format.height; - - return 0; -} - -static int -iss_video_set_crop(struct file *file, void *fh, const struct v4l2_crop *crop) -{ - struct iss_video *video = video_drvdata(file); - struct v4l2_subdev *subdev; - int ret; - - subdev = iss_video_remote_subdev(video, NULL); - if (subdev == NULL) - return -EINVAL; - - mutex_lock(&video->mutex); - ret = v4l2_subdev_call(subdev, video, s_crop, crop); - mutex_unlock(&video->mutex); - - return ret == -ENOIOCTLCMD ? -ENOTTY : ret; -} - static int iss_video_get_param(struct file *file, void *fh, struct v4l2_streamparm *a) { @@ -1018,9 +948,6 @@ static const struct v4l2_ioctl_ops iss_video_ioctl_ops = { .vidioc_g_fmt_vid_out = iss_video_get_format, .vidioc_s_fmt_vid_out = iss_video_set_format, .vidioc_try_fmt_vid_out = iss_video_try_format, - .vidioc_cropcap = iss_video_cropcap, - .vidioc_g_crop = iss_video_get_crop, - .vidioc_s_crop = iss_video_set_crop, .vidioc_g_parm = iss_video_get_param, .vidioc_s_parm = iss_video_set_param, .vidioc_reqbufs = iss_video_reqbufs, -- cgit v1.2.3 From 8c0378354709058bba3319bb58fd04bd09e1eaa6 Mon Sep 17 00:00:00 2001 From: Josh Wu Date: Tue, 26 May 2015 06:54:45 -0300 Subject: [media] atmel-isi: disable ISI even if it has codec request In current code, stop_streaming() will just return if ISI is still working in the codec. But this is incorrect, we need to disable ISI even it is working on the codec, otherwise stop_streaming() will not work as we expected. Signed-off-by: Josh Wu Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/atmel-isi.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/media/platform/soc_camera/atmel-isi.c b/drivers/media/platform/soc_camera/atmel-isi.c index 287902681164..22270225b202 100644 --- a/drivers/media/platform/soc_camera/atmel-isi.c +++ b/drivers/media/platform/soc_camera/atmel-isi.c @@ -431,11 +431,9 @@ static void stop_streaming(struct vb2_queue *vq) time_before(jiffies, timeout)) msleep(1); - if (time_after(jiffies, timeout)) { + if (time_after(jiffies, timeout)) dev_err(icd->parent, "Timeout waiting for finishing codec request\n"); - return; - } /* Disable interrupts */ isi_writel(isi, ISI_INTDIS, -- cgit v1.2.3 From f3745a3af521d403d4c174e4bad0986e11f4d2f1 Mon Sep 17 00:00:00 2001 From: Josh Wu Date: Tue, 26 May 2015 06:54:46 -0300 Subject: [media] atmel-isi: add runtime pm support The runtime pm resume/suspend will enable/disable pclk (ISI peripheral clock). We have to call runtime_pm_get_sync()/runtime_pm_put() when we need to access ISI registers. In atmel_isi_probe(), remove the isi disable code as at that moment ISI peripheral clock is not enable yet. Besides, clock_start()/clock_stop() is used to control the mclk, not the ISI peripheral clock. So move this to start[stop]_streaming() function. Signed-off-by: Josh Wu Acked-by: Laurent Pinchart Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/atmel-isi.c | 55 +++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 8 deletions(-) diff --git a/drivers/media/platform/soc_camera/atmel-isi.c b/drivers/media/platform/soc_camera/atmel-isi.c index 22270225b202..0ea360acea81 100644 --- a/drivers/media/platform/soc_camera/atmel-isi.c +++ b/drivers/media/platform/soc_camera/atmel-isi.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -386,10 +387,13 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count) struct atmel_isi *isi = ici->priv; int ret; + pm_runtime_get_sync(ici->v4l2_dev.dev); + /* Reset ISI */ ret = atmel_isi_wait_status(isi, WAIT_ISI_RESET); if (ret < 0) { dev_err(icd->parent, "Reset ISI timed out\n"); + pm_runtime_put(ici->v4l2_dev.dev); return ret; } /* Disable all interrupts */ @@ -443,6 +447,8 @@ static void stop_streaming(struct vb2_queue *vq) ret = atmel_isi_wait_status(isi, WAIT_ISI_DISABLE); if (ret < 0) dev_err(icd->parent, "Disable ISI timed out\n"); + + pm_runtime_put(ici->v4l2_dev.dev); } static struct vb2_ops isi_video_qops = { @@ -514,7 +520,13 @@ static int isi_camera_set_fmt(struct soc_camera_device *icd, if (mf->code != xlate->code) return -EINVAL; + /* Enable PM and peripheral clock before operate isi registers */ + pm_runtime_get_sync(ici->v4l2_dev.dev); + ret = configure_geometry(isi, pix->width, pix->height, xlate->code); + + pm_runtime_put(ici->v4l2_dev.dev); + if (ret < 0) return ret; @@ -734,14 +746,9 @@ static int isi_camera_clock_start(struct soc_camera_host *ici) struct atmel_isi *isi = ici->priv; int ret; - ret = clk_prepare_enable(isi->pclk); - if (ret) - return ret; - if (!IS_ERR(isi->mck)) { ret = clk_prepare_enable(isi->mck); if (ret) { - clk_disable_unprepare(isi->pclk); return ret; } } @@ -756,7 +763,6 @@ static void isi_camera_clock_stop(struct soc_camera_host *ici) if (!IS_ERR(isi->mck)) clk_disable_unprepare(isi->mck); - clk_disable_unprepare(isi->pclk); } static unsigned int isi_camera_poll(struct file *file, poll_table *pt) @@ -853,9 +859,14 @@ static int isi_camera_set_bus_param(struct soc_camera_device *icd) cfg1 |= ISI_CFG1_THMASK_BEATS_16; + /* Enable PM and peripheral clock before operate isi registers */ + pm_runtime_get_sync(ici->v4l2_dev.dev); + isi_writel(isi, ISI_CTRL, ISI_CTRL_DIS); isi_writel(isi, ISI_CFG1, cfg1); + pm_runtime_put(ici->v4l2_dev.dev); + return 0; } @@ -887,6 +898,7 @@ static int atmel_isi_remove(struct platform_device *pdev) sizeof(struct fbd) * MAX_BUFFER_NUM, isi->p_fb_descriptors, isi->fb_descriptors_phys); + pm_runtime_disable(&pdev->dev); return 0; } @@ -1025,8 +1037,6 @@ static int atmel_isi_probe(struct platform_device *pdev) if (isi->pdata.data_width_flags & ISI_DATAWIDTH_10) isi->width_flags |= 1 << 9; - isi_writel(isi, ISI_CTRL, ISI_CTRL_DIS); - irq = platform_get_irq(pdev, 0); if (IS_ERR_VALUE(irq)) { ret = irq; @@ -1047,6 +1057,9 @@ static int atmel_isi_probe(struct platform_device *pdev) soc_host->v4l2_dev.dev = &pdev->dev; soc_host->nr = pdev->id; + pm_suspend_ignore_children(&pdev->dev, true); + pm_runtime_enable(&pdev->dev); + if (isi->pdata.asd_sizes) { soc_host->asd = isi->pdata.asd; soc_host->asd_sizes = isi->pdata.asd_sizes; @@ -1060,6 +1073,7 @@ static int atmel_isi_probe(struct platform_device *pdev) return 0; err_register_soc_camera_host: + pm_runtime_disable(&pdev->dev); err_req_irq: err_ioremap: vb2_dma_contig_cleanup_ctx(isi->alloc_ctx); @@ -1072,6 +1086,30 @@ err_alloc_ctx: return ret; } +static int atmel_isi_runtime_suspend(struct device *dev) +{ + struct soc_camera_host *soc_host = to_soc_camera_host(dev); + struct atmel_isi *isi = container_of(soc_host, + struct atmel_isi, soc_host); + + clk_disable_unprepare(isi->pclk); + + return 0; +} +static int atmel_isi_runtime_resume(struct device *dev) +{ + struct soc_camera_host *soc_host = to_soc_camera_host(dev); + struct atmel_isi *isi = container_of(soc_host, + struct atmel_isi, soc_host); + + return clk_prepare_enable(isi->pclk); +} + +static const struct dev_pm_ops atmel_isi_dev_pm_ops = { + SET_RUNTIME_PM_OPS(atmel_isi_runtime_suspend, + atmel_isi_runtime_resume, NULL) +}; + static const struct of_device_id atmel_isi_of_match[] = { { .compatible = "atmel,at91sam9g45-isi" }, { } @@ -1083,6 +1121,7 @@ static struct platform_driver atmel_isi_driver = { .driver = { .name = "atmel_isi", .of_match_table = of_match_ptr(atmel_isi_of_match), + .pm = &atmel_isi_dev_pm_ops, }, }; -- cgit v1.2.3 From 4a99362da734dfdcca5035dfa15b9f3708f1f8a4 Mon Sep 17 00:00:00 2001 From: Josh Wu Date: Tue, 26 May 2015 06:54:47 -0300 Subject: [media] atmel-isi: remove mck backward compatibility code The master clock should be handled by sensor itself. Signed-off-by: Josh Wu Acked-by: Laurent Pinchart Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/atmel-isi.c | 46 --------------------------- 1 file changed, 46 deletions(-) diff --git a/drivers/media/platform/soc_camera/atmel-isi.c b/drivers/media/platform/soc_camera/atmel-isi.c index 0ea360acea81..90701726a06a 100644 --- a/drivers/media/platform/soc_camera/atmel-isi.c +++ b/drivers/media/platform/soc_camera/atmel-isi.c @@ -35,7 +35,6 @@ #define VID_LIMIT_BYTES (16 * 1024 * 1024) #define MIN_FRAME_RATE 15 #define FRAME_INTERVAL_MILLI_SEC (1000 / MIN_FRAME_RATE) -#define ISI_DEFAULT_MCLK_FREQ 25000000 /* Frame buffer descriptor */ struct fbd { @@ -83,8 +82,6 @@ struct atmel_isi { struct completion complete; /* ISI peripherial clock */ struct clk *pclk; - /* ISI_MCK, feed to camera sensor to generate pixel clock */ - struct clk *mck; unsigned int irq; struct isi_platform_data pdata; @@ -740,31 +737,6 @@ static void isi_camera_remove_device(struct soc_camera_device *icd) icd->devnum); } -/* Called with .host_lock held */ -static int isi_camera_clock_start(struct soc_camera_host *ici) -{ - struct atmel_isi *isi = ici->priv; - int ret; - - if (!IS_ERR(isi->mck)) { - ret = clk_prepare_enable(isi->mck); - if (ret) { - return ret; - } - } - - return 0; -} - -/* Called with .host_lock held */ -static void isi_camera_clock_stop(struct soc_camera_host *ici) -{ - struct atmel_isi *isi = ici->priv; - - if (!IS_ERR(isi->mck)) - clk_disable_unprepare(isi->mck); -} - static unsigned int isi_camera_poll(struct file *file, poll_table *pt) { struct soc_camera_device *icd = file->private_data; @@ -874,8 +846,6 @@ static struct soc_camera_host_ops isi_soc_camera_host_ops = { .owner = THIS_MODULE, .add = isi_camera_add_device, .remove = isi_camera_remove_device, - .clock_start = isi_camera_clock_start, - .clock_stop = isi_camera_clock_stop, .set_fmt = isi_camera_set_fmt, .try_fmt = isi_camera_try_fmt, .get_formats = isi_camera_get_formats, @@ -912,7 +882,6 @@ static int atmel_isi_probe_dt(struct atmel_isi *isi, /* Default settings for ISI */ isi->pdata.full_mode = 1; - isi->pdata.mck_hz = ISI_DEFAULT_MCLK_FREQ; isi->pdata.frate = ISI_CFG1_FRATE_CAPTURE_ALL; np = of_graph_get_next_endpoint(np, NULL); @@ -988,21 +957,6 @@ static int atmel_isi_probe(struct platform_device *pdev) INIT_LIST_HEAD(&isi->video_buffer_list); INIT_LIST_HEAD(&isi->dma_desc_head); - /* ISI_MCK is the sensor master clock. It should be handled by the - * sensor driver directly, as the ISI has no use for that clock. Make - * the clock optional here while platforms transition to the correct - * model. - */ - isi->mck = devm_clk_get(dev, "isi_mck"); - if (!IS_ERR(isi->mck)) { - /* Set ISI_MCK's frequency, it should be faster than pixel - * clock. - */ - ret = clk_set_rate(isi->mck, isi->pdata.mck_hz); - if (ret < 0) - return ret; - } - isi->p_fb_descriptors = dma_alloc_coherent(&pdev->dev, sizeof(struct fbd) * MAX_BUFFER_NUM, &isi->fb_descriptors_phys, -- cgit v1.2.3 From 4366dfef377034e97d5b35677b7a1ebb1f1ce6dc Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Wed, 10 Jun 2015 10:38:29 -0300 Subject: [media] media/v4l2-ctrls: Code cleanout validate_new() We can simplify the code removing the if(). v4l2_ctr_new sets ctrls->elems to 1 when !ctrl->is_ptr. Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index e3a3468002e6..b6b7dcc1b77d 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1678,21 +1678,6 @@ static int validate_new(const struct v4l2_ctrl *ctrl, union v4l2_ctrl_ptr p_new) unsigned idx; int err = 0; - if (!ctrl->is_ptr) { - switch (ctrl->type) { - case V4L2_CTRL_TYPE_INTEGER: - case V4L2_CTRL_TYPE_INTEGER_MENU: - case V4L2_CTRL_TYPE_MENU: - case V4L2_CTRL_TYPE_BITMASK: - case V4L2_CTRL_TYPE_BOOLEAN: - case V4L2_CTRL_TYPE_BUTTON: - case V4L2_CTRL_TYPE_CTRL_CLASS: - case V4L2_CTRL_TYPE_INTEGER64: - return ctrl->type_ops->validate(ctrl, 0, p_new); - default: - break; - } - } for (idx = 0; !err && idx < ctrl->elems; idx++) err = ctrl->type_ops->validate(ctrl, idx, p_new); return err; -- cgit v1.2.3 From 227da85e775065c81e222f443e8379aba4d3f668 Mon Sep 17 00:00:00 2001 From: Prashant Laddha Date: Fri, 12 Jun 2015 08:48:10 -0300 Subject: [media] v4l2-dv-timings: print refresh rate with better precision In many cases, refresh rate is not exact integer. In such cases, fraction was lost and it used to print, say, 59 in case of 59.94. Now, capturing the fraction up to 2 decimal places. Signed-off-by: Prashant Laddha Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-dv-timings.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-dv-timings.c b/drivers/media/v4l2-core/v4l2-dv-timings.c index 2c7b9fdfa0af..6a83d6191684 100644 --- a/drivers/media/v4l2-core/v4l2-dv-timings.c +++ b/drivers/media/v4l2-core/v4l2-dv-timings.c @@ -256,6 +256,7 @@ void v4l2_print_dv_timings(const char *dev_prefix, const char *prefix, { const struct v4l2_bt_timings *bt = &t->bt; u32 htot, vtot; + u32 fps; if (t->type != V4L2_DV_BT_656_1120) return; @@ -265,13 +266,15 @@ void v4l2_print_dv_timings(const char *dev_prefix, const char *prefix, if (bt->interlaced) vtot /= 2; + fps = (htot * vtot) > 0 ? div_u64((100 * (u64)bt->pixelclock), + (htot * vtot)) : 0; + if (prefix == NULL) prefix = ""; - pr_info("%s: %s%ux%u%s%u (%ux%u)\n", dev_prefix, prefix, + pr_info("%s: %s%ux%u%s%u.%u (%ux%u)\n", dev_prefix, prefix, bt->width, bt->height, bt->interlaced ? "i" : "p", - (htot * vtot) > 0 ? ((u32)bt->pixelclock / (htot * vtot)) : 0, - htot, vtot); + fps / 100, fps % 100, htot, vtot); if (!detailed) return; -- cgit v1.2.3 From 747d481df3b9b6ef46ccb233d74c8d93ec4819e6 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Fri, 12 Jun 2015 13:31:07 -0300 Subject: [media] media/i2c/adv7343: Remove compat control ops They are no longer used in old non-control-framework bridge drivers. Reported-by: Hans Verkuil Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv7343.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/media/i2c/adv7343.c b/drivers/media/i2c/adv7343.c index 7c50833e7d17..d27283135490 100644 --- a/drivers/media/i2c/adv7343.c +++ b/drivers/media/i2c/adv7343.c @@ -319,13 +319,6 @@ static const struct v4l2_ctrl_ops adv7343_ctrl_ops = { static const struct v4l2_subdev_core_ops adv7343_core_ops = { .log_status = adv7343_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 int adv7343_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) -- cgit v1.2.3 From 260faaa490d29cd2ad3cad54fbda0ede406c818e Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Fri, 12 Jun 2015 13:31:08 -0300 Subject: [media] media/i2c/adv7393: Remove compat control ops They are no longer used in old non-control-framework bridge drivers. Reported-by: Hans Verkuil Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv7393.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/media/i2c/adv7393.c b/drivers/media/i2c/adv7393.c index 558f19154eb9..0215f95c2245 100644 --- a/drivers/media/i2c/adv7393.c +++ b/drivers/media/i2c/adv7393.c @@ -306,13 +306,6 @@ static const struct v4l2_ctrl_ops adv7393_ctrl_ops = { static const struct v4l2_subdev_core_ops adv7393_core_ops = { .log_status = adv7393_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 int adv7393_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) -- cgit v1.2.3 From 6ceea1f7d021492ed13609fa1cf401484dae0e0d Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Fri, 12 Jun 2015 13:31:09 -0300 Subject: [media] media/i2c/cs5345: Remove compat control ops They are no longer used in old non-control-framework bridge drivers. Reported-by: Hans Verkuil Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/cs5345.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/media/i2c/cs5345.c b/drivers/media/i2c/cs5345.c index 34b76a9e7515..8cebf9cc8007 100644 --- a/drivers/media/i2c/cs5345.c +++ b/drivers/media/i2c/cs5345.c @@ -132,13 +132,6 @@ static const struct v4l2_ctrl_ops cs5345_ctrl_ops = { static const struct v4l2_subdev_core_ops cs5345_core_ops = { .log_status = cs5345_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, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = cs5345_g_register, .s_register = cs5345_s_register, -- cgit v1.2.3 From 3cfa008e3cafa90684f84abbfb995cec437a61ea Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Fri, 12 Jun 2015 13:31:10 -0300 Subject: [media] media/i2c/saa717x: Remove compat control ops They are no longer used in old non-control-framework bridge drivers. Reported-by: Hans Verkuil Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/saa717x.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/media/i2c/saa717x.c b/drivers/media/i2c/saa717x.c index 7d517361e419..c6ba19cf1aa5 100644 --- a/drivers/media/i2c/saa717x.c +++ b/drivers/media/i2c/saa717x.c @@ -1204,13 +1204,6 @@ static const struct v4l2_subdev_core_ops saa717x_core_ops = { .g_register = saa717x_g_register, .s_register = saa717x_s_register, #endif - .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, .log_status = saa717x_log_status, }; -- cgit v1.2.3 From fc1a33fed0274efefbfc5f463a69efbaee4d989f Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Fri, 12 Jun 2015 13:31:12 -0300 Subject: [media] media/i2c/tda7432: Remove compat control ops They are no longer used in old non-control-framework bridge drivers. Reported-by: Hans Verkuil Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/tda7432.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/media/i2c/tda7432.c b/drivers/media/i2c/tda7432.c index cf93021a6500..d3834a4c48da 100644 --- a/drivers/media/i2c/tda7432.c +++ b/drivers/media/i2c/tda7432.c @@ -331,13 +331,6 @@ static const struct v4l2_ctrl_ops tda7432_ctrl_ops = { static const struct v4l2_subdev_core_ops tda7432_core_ops = { .log_status = tda7432_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_ops tda7432_ops = { -- cgit v1.2.3 From 8b198c6b8b65ef39c3652972c36a43e04b4482dd Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Fri, 12 Jun 2015 13:31:13 -0300 Subject: [media] media/i2c/tlv320aic23: Remove compat control ops They are no longer used in old non-control-framework bridge drivers. Reported-by: Hans Verkuil Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/tlv320aic23b.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/media/i2c/tlv320aic23b.c b/drivers/media/i2c/tlv320aic23b.c index ef87f7b09ea2..0370dd89f1fc 100644 --- a/drivers/media/i2c/tlv320aic23b.c +++ b/drivers/media/i2c/tlv320aic23b.c @@ -122,13 +122,6 @@ static const struct v4l2_ctrl_ops tlv320aic23b_ctrl_ops = { static const struct v4l2_subdev_core_ops tlv320aic23b_core_ops = { .log_status = tlv320aic23b_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_audio_ops tlv320aic23b_audio_ops = { -- cgit v1.2.3 From e5b40d2e11c3e2ed60ecb70313765d48c7786448 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Fri, 12 Jun 2015 13:31:14 -0300 Subject: [media] media/i2c/tvp514x: Remove compat control ops They are no longer used in old non-control-framework bridge drivers. Reported-by: Hans Verkuil Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/tvp514x.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/drivers/media/i2c/tvp514x.c b/drivers/media/i2c/tvp514x.c index 24e47279e30c..a93985a9b070 100644 --- a/drivers/media/i2c/tvp514x.c +++ b/drivers/media/i2c/tvp514x.c @@ -957,16 +957,6 @@ static int tvp514x_set_pad_format(struct v4l2_subdev *sd, return 0; } -static const struct v4l2_subdev_core_ops tvp514x_core_ops = { - .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_video_ops tvp514x_video_ops = { .s_std = tvp514x_s_std, .s_routing = tvp514x_s_routing, @@ -983,7 +973,6 @@ static const struct v4l2_subdev_pad_ops tvp514x_pad_ops = { }; static const struct v4l2_subdev_ops tvp514x_ops = { - .core = &tvp514x_core_ops, .video = &tvp514x_video_ops, .pad = &tvp514x_pad_ops, }; -- cgit v1.2.3 From f5f24bc3267b3651e8bb026a5669dfe6855ffe63 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Fri, 12 Jun 2015 13:31:15 -0300 Subject: [media] media/i2c/tvp7002: Remove compat control ops They are no longer used in old non-control-framework bridge drivers. Reported-by: Hans Verkuil Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/tvp7002.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/media/i2c/tvp7002.c b/drivers/media/i2c/tvp7002.c index 05077cffd235..f617d8b745ee 100644 --- a/drivers/media/i2c/tvp7002.c +++ b/drivers/media/i2c/tvp7002.c @@ -861,13 +861,6 @@ tvp7002_set_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cf /* V4L2 core operation handlers */ static const struct v4l2_subdev_core_ops tvp7002_core_ops = { .log_status = tvp7002_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, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = tvp7002_g_register, .s_register = tvp7002_s_register, -- cgit v1.2.3 From c2527967c5795815d3422f147af767293b3806eb Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Fri, 12 Jun 2015 13:31:16 -0300 Subject: [media] i2c/wm8739: Remove compat control ops They are no longer used in old non-control-framework bridge drivers. Reported-by: Hans Verkuil Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/wm8739.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/media/i2c/wm8739.c b/drivers/media/i2c/wm8739.c index 3be73f6a40e9..534b0e560317 100644 --- a/drivers/media/i2c/wm8739.c +++ b/drivers/media/i2c/wm8739.c @@ -176,13 +176,6 @@ static const struct v4l2_ctrl_ops wm8739_ctrl_ops = { static const struct v4l2_subdev_core_ops wm8739_core_ops = { .log_status = wm8739_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_audio_ops wm8739_audio_ops = { -- cgit v1.2.3 From 59617e74e20dac9523e34f5461162b19347dacf8 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Fri, 12 Jun 2015 13:31:17 -0300 Subject: [media] pci/ivtv/ivtv-gpio: Remove compat control ops They are no longer used in old non-control-framework bridge drivers. Reported-by: Hans Verkuil Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/ivtv/ivtv-gpio.c | 7 ------- 1 file changed, 7 deletions(-) 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 = { -- cgit v1.2.3 From 387a69243890a51be023aef12d0fdcae343f43f2 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Fri, 12 Jun 2015 13:31:18 -0300 Subject: [media] media/radio/saa7706h: Remove compat control ops They are no longer used in old non-control-framework bridge drivers. Reported-by: Hans Verkuil Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/saa7706h.c | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/drivers/media/radio/saa7706h.c b/drivers/media/radio/saa7706h.c index ec805b09c608..183e92719140 100644 --- a/drivers/media/radio/saa7706h.c +++ b/drivers/media/radio/saa7706h.c @@ -336,19 +336,7 @@ static const struct v4l2_ctrl_ops saa7706h_ctrl_ops = { .s_ctrl = saa7706h_s_ctrl, }; -static const struct v4l2_subdev_core_ops saa7706h_core_ops = { - .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_ops saa7706h_ops = { - .core = &saa7706h_core_ops, -}; +static const struct v4l2_subdev_ops empty_ops = {}; /* * Generic i2c probe @@ -373,7 +361,7 @@ static int saa7706h_probe(struct i2c_client *client, if (state == NULL) return -ENOMEM; sd = &state->sd; - v4l2_i2c_subdev_init(sd, client, &saa7706h_ops); + v4l2_i2c_subdev_init(sd, client, &empty_ops); v4l2_ctrl_handler_init(&state->hdl, 4); v4l2_ctrl_new_std(&state->hdl, &saa7706h_ctrl_ops, -- cgit v1.2.3 From 0a2a89c4acbf2ede9cdcc51d7113091978079b7b Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 4 Jun 2015 05:52:26 -0300 Subject: [media] gspca: sn9c2028: remove an unneeded condition We already know status is negative because of the earlier check so there is no need to check again. Signed-off-by: Dan Carpenter Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/sn9c2028.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/gspca/sn9c2028.c b/drivers/media/usb/gspca/sn9c2028.c index c75b7388a85c..4f2050a5ec94 100644 --- a/drivers/media/usb/gspca/sn9c2028.c +++ b/drivers/media/usb/gspca/sn9c2028.c @@ -140,7 +140,7 @@ static int sn9c2028_long_command(struct gspca_dev *gspca_dev, u8 *command) status = sn9c2028_read1(gspca_dev); if (status < 0) { pr_err("long command status read error %d\n", status); - return (status < 0) ? status : -EIO; + return status; } memset(reading, 0, 4); -- cgit v1.2.3 From 63f2f417526fc54191f2b813f72dc1d5322bede8 Mon Sep 17 00:00:00 2001 From: Nicholas Mc Guire Date: Sun, 7 Jun 2015 11:34:40 -0300 Subject: [media] gscpa_m5602: use msecs_to_jiffies for conversions API compliance scanning with coccinelle flagged: ./drivers/media/usb/gspca/m5602/m5602_s5k83a.c:180:9-25: WARNING: timeout (100) seems HZ dependent Numeric constants passed to schedule_timeout() make the effective timeout HZ dependent which makes little sense in a polling loop for the cameras rotation state. Fixed up by converting the constant to jiffies with msecs_to_jiffies() Signed-off-by: Nicholas Mc Guire Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/m5602/m5602_s5k83a.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/gspca/m5602/m5602_s5k83a.c b/drivers/media/usb/gspca/m5602/m5602_s5k83a.c index 7cbc3a00bda8..bf6b215438e3 100644 --- a/drivers/media/usb/gspca/m5602/m5602_s5k83a.c +++ b/drivers/media/usb/gspca/m5602/m5602_s5k83a.c @@ -177,7 +177,7 @@ static int rotation_thread_function(void *data) __s32 vflip, hflip; set_current_state(TASK_INTERRUPTIBLE); - while (!schedule_timeout(100)) { + while (!schedule_timeout(msecs_to_jiffies(100))) { if (mutex_lock_interruptible(&sd->gspca_dev.usb_lock)) break; -- cgit v1.2.3 From 03b36e4dcf422a10da8b67bce2ed00b34ec58aac Mon Sep 17 00:00:00 2001 From: Nobuhiro Iwamatsu Date: Wed, 28 Jan 2015 22:53:53 -0200 Subject: [media] v4l: vsp1: Fix VI6_WPF_SZCLIP_SIZE_MASK macro Clipping size bit of VI6_WPFn _HSZCLIP and VI6_WPFn _VSZCLIP register are from 0 bit to 11 bit. But VI6_WPF_SZCLIP_SIZE_MASK is set to 0x1FFF, this will mask until the reserve bits. This fixes size for VI6_WPF_SZCLIP_SIZE_MASK. Signed-off-by: Nobuhiro Iwamatsu Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_regs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/vsp1/vsp1_regs.h b/drivers/media/platform/vsp1/vsp1_regs.h index da3c573e1efc..f61e1096f9fc 100644 --- a/drivers/media/platform/vsp1/vsp1_regs.h +++ b/drivers/media/platform/vsp1/vsp1_regs.h @@ -238,7 +238,7 @@ #define VI6_WPF_SZCLIP_EN (1 << 28) #define VI6_WPF_SZCLIP_OFST_MASK (0xff << 16) #define VI6_WPF_SZCLIP_OFST_SHIFT 16 -#define VI6_WPF_SZCLIP_SIZE_MASK (0x1fff << 0) +#define VI6_WPF_SZCLIP_SIZE_MASK (0xfff << 0) #define VI6_WPF_SZCLIP_SIZE_SHIFT 0 #define VI6_WPF_OUTFMT 0x100c -- cgit v1.2.3 From 1aa7890324b497f96f07c20673fae58f26fabfe7 Mon Sep 17 00:00:00 2001 From: Nobuhiro Iwamatsu Date: Wed, 28 Jan 2015 22:53:54 -0200 Subject: [media] v4l: vsp1: Fix VI6_DPR_ROUTE_FP_MASK macro FP bit of VI6_DPR_mod_ROUTE register is 6bit. But VI6_DPR_ROUTE_FP_MASK is set to 0xFF, this will mask until the reserve bit. This fixes size for VI6_DPR_ROUTE_FP_MASK. Signed-off-by: Nobuhiro Iwamatsu Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_regs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/vsp1/vsp1_regs.h b/drivers/media/platform/vsp1/vsp1_regs.h index f61e1096f9fc..4177f98968d6 100644 --- a/drivers/media/platform/vsp1/vsp1_regs.h +++ b/drivers/media/platform/vsp1/vsp1_regs.h @@ -306,7 +306,7 @@ #define VI6_DPR_BRU_ROUTE 0x204c #define VI6_DPR_ROUTE_FXA_MASK (0xff << 8) #define VI6_DPR_ROUTE_FXA_SHIFT 16 -#define VI6_DPR_ROUTE_FP_MASK (0xff << 8) +#define VI6_DPR_ROUTE_FP_MASK (0x3f << 8) #define VI6_DPR_ROUTE_FP_SHIFT 8 #define VI6_DPR_ROUTE_RT_MASK (0x3f << 0) #define VI6_DPR_ROUTE_RT_SHIFT 0 -- cgit v1.2.3 From 45008ee9295b3ae96d7413ab91871907a671ca82 Mon Sep 17 00:00:00 2001 From: Nobuhiro Iwamatsu Date: Wed, 28 Jan 2015 22:53:55 -0200 Subject: [media] v4l: vsp1: Fix VI6_DPR_ROUTE_FXA_MASK macro FXA bit of VI6_DPR_mod_ROUTE register starts from 16bit. But VI6_DPR_ROUTE_FXA_MASK is set to become start from 8bit. This fixes shift size for VI6_DPR_ROUTE_FXA_MASK. Signed-off-by: Nobuhiro Iwamatsu Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_regs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/vsp1/vsp1_regs.h b/drivers/media/platform/vsp1/vsp1_regs.h index 4177f98968d6..25b48738b147 100644 --- a/drivers/media/platform/vsp1/vsp1_regs.h +++ b/drivers/media/platform/vsp1/vsp1_regs.h @@ -304,7 +304,7 @@ #define VI6_DPR_HST_ROUTE 0x2044 #define VI6_DPR_HSI_ROUTE 0x2048 #define VI6_DPR_BRU_ROUTE 0x204c -#define VI6_DPR_ROUTE_FXA_MASK (0xff << 8) +#define VI6_DPR_ROUTE_FXA_MASK (0xff << 16) #define VI6_DPR_ROUTE_FXA_SHIFT 16 #define VI6_DPR_ROUTE_FP_MASK (0x3f << 8) #define VI6_DPR_ROUTE_FP_SHIFT 8 -- cgit v1.2.3 From 139c92866e34bfa4897e644b36147fc86cc7a7a1 Mon Sep 17 00:00:00 2001 From: Sei Fumizono Date: Sun, 15 Mar 2015 11:33:07 -0300 Subject: [media] v4l: vsp1: Fix Suspend-to-RAM Fix Suspend-to-RAM so that VSP1 driver continues to work after resuming. In detail, - Fix the judgment of ref count in resuming. - Add stopping VSP1 during suspend. [Refactor the suspend and resume code to lower suspend delay] Signed-off-by: Sei Fumizono Signed-off-by: Yoshifumi Hosoya Signed-off-by: Yoshihiro Kaneko Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_drv.c | 13 ++++-- drivers/media/platform/vsp1/vsp1_video.c | 70 +++++++++++++++++++++++++++++++- drivers/media/platform/vsp1/vsp1_video.h | 5 ++- 3 files changed, 83 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c index 913485a90e97..4e61886384e3 100644 --- a/drivers/media/platform/vsp1/vsp1_drv.c +++ b/drivers/media/platform/vsp1/vsp1_drv.c @@ -1,7 +1,7 @@ /* * vsp1_drv.c -- R-Car VSP1 Driver * - * Copyright (C) 2013-2014 Renesas Electronics Corporation + * Copyright (C) 2013-2015 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) * @@ -403,7 +403,10 @@ static int vsp1_pm_suspend(struct device *dev) if (vsp1->ref_count == 0) return 0; + vsp1_pipelines_suspend(vsp1); + clk_disable_unprepare(vsp1->clock); + return 0; } @@ -413,10 +416,14 @@ static int vsp1_pm_resume(struct device *dev) WARN_ON(mutex_is_locked(&vsp1->lock)); - if (vsp1->ref_count) + if (vsp1->ref_count == 0) return 0; - return clk_prepare_enable(vsp1->clock); + clk_prepare_enable(vsp1->clock); + + vsp1_pipelines_resume(vsp1); + + return 0; } #endif diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index d91f19a9e1c1..c4b06214b6c1 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -1,7 +1,7 @@ /* * vsp1_video.c -- R-Car VSP1 Video Node * - * Copyright (C) 2013-2014 Renesas Electronics Corporation + * Copyright (C) 2013-2015 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) * @@ -703,6 +703,74 @@ void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe, } } +void vsp1_pipelines_suspend(struct vsp1_device *vsp1) +{ + unsigned long flags; + unsigned int i; + int ret; + + /* To avoid increasing the system suspend time needlessly, loop over the + * pipelines twice, first to set them all to the stopping state, and then + * to wait for the stop to complete. + */ + for (i = 0; i < vsp1->pdata.wpf_count; ++i) { + struct vsp1_rwpf *wpf = vsp1->wpf[i]; + struct vsp1_pipeline *pipe; + + if (wpf == NULL) + continue; + + pipe = to_vsp1_pipeline(&wpf->entity.subdev.entity); + if (pipe == NULL) + continue; + + spin_lock_irqsave(&pipe->irqlock, flags); + if (pipe->state == VSP1_PIPELINE_RUNNING) + pipe->state = VSP1_PIPELINE_STOPPING; + spin_unlock_irqrestore(&pipe->irqlock, flags); + } + + for (i = 0; i < vsp1->pdata.wpf_count; ++i) { + struct vsp1_rwpf *wpf = vsp1->wpf[i]; + struct vsp1_pipeline *pipe; + + if (wpf == NULL) + continue; + + pipe = to_vsp1_pipeline(&wpf->entity.subdev.entity); + if (pipe == NULL) + continue; + + ret = wait_event_timeout(pipe->wq, + pipe->state == VSP1_PIPELINE_STOPPED, + msecs_to_jiffies(500)); + if (ret == 0) + dev_warn(vsp1->dev, "pipeline %u stop timeout\n", + wpf->entity.index); + } +} + +void vsp1_pipelines_resume(struct vsp1_device *vsp1) +{ + unsigned int i; + + /* Resume pipeline all running pipelines. */ + for (i = 0; i < vsp1->pdata.wpf_count; ++i) { + struct vsp1_rwpf *wpf = vsp1->wpf[i]; + struct vsp1_pipeline *pipe; + + if (wpf == NULL) + continue; + + pipe = to_vsp1_pipeline(&wpf->entity.subdev.entity); + if (pipe == NULL) + continue; + + if (vsp1_pipeline_ready(pipe)) + vsp1_pipeline_run(pipe); + } +} + /* ----------------------------------------------------------------------------- * videobuf2 Queue Operations */ diff --git a/drivers/media/platform/vsp1/vsp1_video.h b/drivers/media/platform/vsp1/vsp1_video.h index fd2851a82e00..0887a4d2742c 100644 --- a/drivers/media/platform/vsp1/vsp1_video.h +++ b/drivers/media/platform/vsp1/vsp1_video.h @@ -1,7 +1,7 @@ /* * vsp1_video.h -- R-Car VSP1 Video Node * - * Copyright (C) 2013-2014 Renesas Electronics Corporation + * Copyright (C) 2013-2015 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) * @@ -149,4 +149,7 @@ void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe, struct vsp1_entity *input, unsigned int alpha); +void vsp1_pipelines_suspend(struct vsp1_device *vsp1); +void vsp1_pipelines_resume(struct vsp1_device *vsp1); + #endif /* __VSP1_VIDEO_H__ */ -- cgit v1.2.3 From 1c991fee30c72ff49bb96558d5f1c14a60230677 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 29 Apr 2015 16:54:39 -0300 Subject: [media] v4l: vsp1: Fix race condition when stopping pipeline When stopping the pipeline the driver waits for the pipeline state to be set to VSP1_PIPELINE_STOPPED but fails to lock the pipe irqlock to read the state variable protected by the lock. Fix it. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_video.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index c4b06214b6c1..c1b5a09b8331 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -514,6 +514,18 @@ static void vsp1_pipeline_run(struct vsp1_pipeline *pipe) pipe->buffers_ready = 0; } +bool vsp1_pipeline_stopped(struct vsp1_pipeline *pipe) +{ + unsigned long flags; + bool stopped; + + spin_lock_irqsave(&pipe->irqlock, flags); + stopped = pipe->state == VSP1_PIPELINE_STOPPED, + spin_unlock_irqrestore(&pipe->irqlock, flags); + + return stopped; +} + static int vsp1_pipeline_stop(struct vsp1_pipeline *pipe) { struct vsp1_entity *entity; @@ -525,7 +537,7 @@ static int vsp1_pipeline_stop(struct vsp1_pipeline *pipe) pipe->state = VSP1_PIPELINE_STOPPING; spin_unlock_irqrestore(&pipe->irqlock, flags); - ret = wait_event_timeout(pipe->wq, pipe->state == VSP1_PIPELINE_STOPPED, + ret = wait_event_timeout(pipe->wq, vsp1_pipeline_stopped(pipe), msecs_to_jiffies(500)); ret = ret == 0 ? -ETIMEDOUT : 0; @@ -741,8 +753,7 @@ void vsp1_pipelines_suspend(struct vsp1_device *vsp1) if (pipe == NULL) continue; - ret = wait_event_timeout(pipe->wq, - pipe->state == VSP1_PIPELINE_STOPPED, + ret = wait_event_timeout(pipe->wq, vsp1_pipeline_stopped(pipe), msecs_to_jiffies(500)); if (ret == 0) dev_warn(vsp1->dev, "pipeline %u stop timeout\n", -- cgit v1.2.3 From 85a0638b7855dfb00dc9b66bc2fdd4276d7dc87c Mon Sep 17 00:00:00 2001 From: Damian Hobson-Garcia Date: Thu, 28 May 2015 09:59:39 -0300 Subject: [media] v4l: vsp1: Align crop rectangle to even boundary for YUV formats Make sure that there are valid values in the crop rectangle to ensure that the color plane doesn't get shifted when cropping. Since there is no distinction between 12bit and 16bit YUV formats in at the subdev level, use the more restrictive 12bit limits for all YUV formats. Signed-off-by: Damian Hobson-Garcia Signed-off-by: Yoshihiro Kaneko Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_rwpf.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.c b/drivers/media/platform/vsp1/vsp1_rwpf.c index fa71f4695e16..9688c219b30e 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.c +++ b/drivers/media/platform/vsp1/vsp1_rwpf.c @@ -197,6 +197,17 @@ int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, */ format = vsp1_entity_get_pad_format(&rwpf->entity, cfg, RWPF_PAD_SINK, sel->which); + + /* Restrict the crop rectangle coordinates to multiples of 2 to avoid + * shifting the color plane. + */ + if (format->code == MEDIA_BUS_FMT_AYUV8_1X32) { + sel->r.left = ALIGN(sel->r.left, 2); + sel->r.top = ALIGN(sel->r.top, 2); + sel->r.width = round_down(sel->r.width, 2); + sel->r.height = round_down(sel->r.height, 2); + } + sel->r.left = min_t(unsigned int, sel->r.left, format->width - 2); sel->r.top = min_t(unsigned int, sel->r.top, format->height - 2); if (rwpf->entity.type == VSP1_ENTITY_WPF) { -- cgit v1.2.3 From e36f1b19e80dfd89d56a5af8f4b20d2fc170c3fe Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 7 Jun 2015 05:57:55 -0300 Subject: [media] clock-sh7724.c: fix sh-vou clock identifier Bitrot has set in for this driver and the sh-vou clock was never enabled, since the clock name in clock-sh7724.c was wrong. It should be sh-vou, not sh-vou.0. Signed-off-by: Hans Verkuil Thanks-to: Geert Uytterhoeven Cc: Magnus Damm Signed-off-by: Mauro Carvalho Chehab --- arch/sh/kernel/cpu/sh4a/clock-sh7724.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7724.c b/arch/sh/kernel/cpu/sh4a/clock-sh7724.c index c187b9579c21..f27c618de527 100644 --- a/arch/sh/kernel/cpu/sh4a/clock-sh7724.c +++ b/arch/sh/kernel/cpu/sh4a/clock-sh7724.c @@ -343,7 +343,7 @@ static struct clk_lookup lookups[] = { CLKDEV_CON_ID("2ddmac0", &mstp_clks[HWBLK_2DDMAC]), CLKDEV_DEV_ID("sh_fsi.0", &mstp_clks[HWBLK_SPU]), CLKDEV_CON_ID("jpu0", &mstp_clks[HWBLK_JPU]), - CLKDEV_DEV_ID("sh-vou.0", &mstp_clks[HWBLK_VOU]), + CLKDEV_DEV_ID("sh-vou", &mstp_clks[HWBLK_VOU]), CLKDEV_CON_ID("beu0", &mstp_clks[HWBLK_BEU0]), CLKDEV_DEV_ID("sh_mobile_ceu.0", &mstp_clks[HWBLK_CEU0]), CLKDEV_CON_ID("veu0", &mstp_clks[HWBLK_VEU0]), -- cgit v1.2.3 From 41bdc3cf81c4d0f0dfe09f06ff203dd59d422f37 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 3 Jul 2015 08:35:51 -0300 Subject: [media] vsp1: declar vsp1_pipeline_stopped() as static drivers/media/platform/vsp1/vsp1_video.c:517:6: warning: no previous prototype for 'vsp1_pipeline_stopped' [-Wmissing-prototypes] bool vsp1_pipeline_stopped(struct vsp1_pipeline *pipe) ^ Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_video.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index c1b5a09b8331..770e08dc03f1 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -514,7 +514,7 @@ static void vsp1_pipeline_run(struct vsp1_pipeline *pipe) pipe->buffers_ready = 0; } -bool vsp1_pipeline_stopped(struct vsp1_pipeline *pipe) +static bool vsp1_pipeline_stopped(struct vsp1_pipeline *pipe) { unsigned long flags; bool stopped; -- cgit v1.2.3 From 4690271ce14cd4cf4796e0b6631978d151abe8e4 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 7 Jun 2015 05:57:56 -0300 Subject: [media] sh-vou: use resource managed calls Simplify the sh-vou clean up by using devm_* were possible. Signed-off-by: Hans Verkuil Cc: Magnus Damm Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sh_vou.c | 43 ++++++++--------------------------------- 1 file changed, 8 insertions(+), 35 deletions(-) diff --git a/drivers/media/platform/sh_vou.c b/drivers/media/platform/sh_vou.c index 8b799bae01b8..801d5ef4dc28 100644 --- a/drivers/media/platform/sh_vou.c +++ b/drivers/media/platform/sh_vou.c @@ -1300,7 +1300,7 @@ static int sh_vou_probe(struct platform_device *pdev) struct i2c_adapter *i2c_adap; struct video_device *vdev; struct sh_vou_device *vou_dev; - struct resource *reg_res, *region; + struct resource *reg_res; struct v4l2_subdev *subdev; int irq, ret; @@ -1312,7 +1312,7 @@ static int sh_vou_probe(struct platform_device *pdev) return -ENODEV; } - vou_dev = kzalloc(sizeof(*vou_dev), GFP_KERNEL); + vou_dev = devm_kzalloc(&pdev->dev, sizeof(*vou_dev), GFP_KERNEL); if (!vou_dev) return -ENOMEM; @@ -1340,28 +1340,18 @@ static int sh_vou_probe(struct platform_device *pdev) pix->sizeimage = VOU_MAX_IMAGE_WIDTH * 2 * 480; pix->colorspace = V4L2_COLORSPACE_SMPTE170M; - region = request_mem_region(reg_res->start, resource_size(reg_res), - pdev->name); - if (!region) { - dev_err(&pdev->dev, "VOU region already claimed\n"); - ret = -EBUSY; - goto ereqmemreg; - } - - vou_dev->base = ioremap(reg_res->start, resource_size(reg_res)); - if (!vou_dev->base) { - ret = -ENOMEM; - goto emap; - } + vou_dev->base = devm_ioremap_resource(&pdev->dev, reg_res); + if (IS_ERR(vou_dev->base)) + return PTR_ERR(vou_dev->base); - ret = request_irq(irq, sh_vou_isr, 0, "vou", vou_dev); + ret = devm_request_irq(&pdev->dev, irq, sh_vou_isr, 0, "vou", vou_dev); if (ret < 0) - goto ereqirq; + return ret; ret = v4l2_device_register(&pdev->dev, &vou_dev->v4l2_dev); if (ret < 0) { dev_err(&pdev->dev, "Error registering v4l2 device\n"); - goto ev4l2devreg; + return ret; } vdev = &vou_dev->vdev; @@ -1407,39 +1397,22 @@ ereset: ei2cgadap: pm_runtime_disable(&pdev->dev); v4l2_device_unregister(&vou_dev->v4l2_dev); -ev4l2devreg: - free_irq(irq, vou_dev); -ereqirq: - iounmap(vou_dev->base); -emap: - release_mem_region(reg_res->start, resource_size(reg_res)); -ereqmemreg: - kfree(vou_dev); return ret; } static int sh_vou_remove(struct platform_device *pdev) { - int irq = platform_get_irq(pdev, 0); struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev); struct sh_vou_device *vou_dev = container_of(v4l2_dev, struct sh_vou_device, v4l2_dev); struct v4l2_subdev *sd = list_entry(v4l2_dev->subdevs.next, struct v4l2_subdev, list); struct i2c_client *client = v4l2_get_subdevdata(sd); - struct resource *reg_res; - if (irq > 0) - free_irq(irq, vou_dev); pm_runtime_disable(&pdev->dev); video_unregister_device(&vou_dev->vdev); i2c_put_adapter(client->adapter); v4l2_device_unregister(&vou_dev->v4l2_dev); - iounmap(vou_dev->base); - reg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (reg_res) - release_mem_region(reg_res->start, resource_size(reg_res)); - kfree(vou_dev); return 0; } -- cgit v1.2.3 From d8046ee09f843cb314a660f589a334bce84a7efa Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 7 Jun 2015 05:57:57 -0300 Subject: [media] sh-vou: fix querycap support Fix v4l2-compliance errors due to empty driver and bus_info fields. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sh_vou.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/platform/sh_vou.c b/drivers/media/platform/sh_vou.c index 801d5ef4dc28..d7a72a9ba90b 100644 --- a/drivers/media/platform/sh_vou.c +++ b/drivers/media/platform/sh_vou.c @@ -396,6 +396,8 @@ static int sh_vou_querycap(struct file *file, void *priv, dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); strlcpy(cap->card, "SuperH VOU", sizeof(cap->card)); + strlcpy(cap->driver, "sh-vou", sizeof(cap->driver)); + strlcpy(cap->bus_info, "platform:sh-vou", sizeof(cap->bus_info)); cap->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; -- cgit v1.2.3 From c5f98085bf3f1d645aaf76eac7c9ca0e2ab684c6 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 7 Jun 2015 05:57:58 -0300 Subject: [media] sh-vou: use v4l2_fh This allows us to drop the use_count and you get free G/S_PRIORITY support. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sh_vou.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/drivers/media/platform/sh_vou.c b/drivers/media/platform/sh_vou.c index d7a72a9ba90b..4994b7ba9236 100644 --- a/drivers/media/platform/sh_vou.c +++ b/drivers/media/platform/sh_vou.c @@ -63,7 +63,6 @@ enum sh_vou_status { struct sh_vou_device { struct v4l2_device v4l2_dev; struct video_device vdev; - atomic_t use_count; struct sh_vou_pdata *pdata; spinlock_t lock; void __iomem *base; @@ -79,6 +78,7 @@ struct sh_vou_device { }; struct sh_vou_file { + struct v4l2_fh fh; struct videobuf_queue vbq; }; @@ -1173,20 +1173,24 @@ static int sh_vou_open(struct file *file) dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); + v4l2_fh_init(&vou_file->fh, &vou_dev->vdev); if (mutex_lock_interruptible(&vou_dev->fop_lock)) { kfree(vou_file); return -ERESTARTSYS; } - if (atomic_inc_return(&vou_dev->use_count) == 1) { + v4l2_fh_add(&vou_file->fh); + if (v4l2_fh_is_singular(&vou_file->fh)) { int ret; + /* First open */ vou_dev->status = SH_VOU_INITIALISING; pm_runtime_get_sync(vou_dev->v4l2_dev.dev); ret = sh_vou_hw_init(vou_dev); if (ret < 0) { - atomic_dec(&vou_dev->use_count); pm_runtime_put(vou_dev->v4l2_dev.dev); vou_dev->status = SH_VOU_IDLE; + v4l2_fh_del(&vou_file->fh); + v4l2_fh_exit(&vou_file->fh); mutex_unlock(&vou_dev->fop_lock); kfree(vou_file); return ret; @@ -1213,14 +1217,16 @@ static int sh_vou_release(struct file *file) dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); - if (!atomic_dec_return(&vou_dev->use_count)) { - mutex_lock(&vou_dev->fop_lock); + mutex_lock(&vou_dev->fop_lock); + if (v4l2_fh_is_singular(&vou_file->fh)) { /* Last close */ vou_dev->status = SH_VOU_IDLE; sh_vou_reg_a_set(vou_dev, VOUER, 0, 0x101); pm_runtime_put(vou_dev->v4l2_dev.dev); - mutex_unlock(&vou_dev->fop_lock); } + v4l2_fh_del(&vou_file->fh); + v4l2_fh_exit(&vou_file->fh); + mutex_unlock(&vou_dev->fop_lock); file->private_data = NULL; kfree(vou_file); @@ -1321,7 +1327,6 @@ static int sh_vou_probe(struct platform_device *pdev) INIT_LIST_HEAD(&vou_dev->queue); spin_lock_init(&vou_dev->lock); mutex_init(&vou_dev->fop_lock); - atomic_set(&vou_dev->use_count, 0); vou_dev->pdata = vou_pdata; vou_dev->status = SH_VOU_IDLE; -- cgit v1.2.3 From 4de00d0efe0a7af8e2ef591effa2a559b7cc38a3 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 7 Jun 2015 05:57:59 -0300 Subject: [media] sh-vou: support compulsory G/S/ENUM_OUTPUT ioctls Video output drivers must support these ioctls. Otherwise applications cannot deduce that these outputs exist and what capabilities they have. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sh_vou.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/drivers/media/platform/sh_vou.c b/drivers/media/platform/sh_vou.c index 4994b7ba9236..d9a4502b4a58 100644 --- a/drivers/media/platform/sh_vou.c +++ b/drivers/media/platform/sh_vou.c @@ -872,6 +872,30 @@ static int sh_vou_streamoff(struct file *file, void *priv, return 0; } +static int sh_vou_enum_output(struct file *file, void *fh, + struct v4l2_output *a) +{ + struct sh_vou_device *vou_dev = video_drvdata(file); + + if (a->index) + return -EINVAL; + strlcpy(a->name, "Video Out", sizeof(a->name)); + a->type = V4L2_OUTPUT_TYPE_ANALOG; + a->std = vou_dev->vdev.tvnorms; + return 0; +} + +int sh_vou_g_output(struct file *file, void *fh, unsigned int *i) +{ + *i = 0; + return 0; +} + +int sh_vou_s_output(struct file *file, void *fh, unsigned int i) +{ + return i ? -EINVAL : 0; +} + static u32 sh_vou_ntsc_mode(enum sh_vou_bus_fmt bus_fmt) { switch (bus_fmt) { @@ -1276,6 +1300,9 @@ static const struct v4l2_ioctl_ops sh_vou_ioctl_ops = { .vidioc_dqbuf = sh_vou_dqbuf, .vidioc_streamon = sh_vou_streamon, .vidioc_streamoff = sh_vou_streamoff, + .vidioc_g_output = sh_vou_g_output, + .vidioc_s_output = sh_vou_s_output, + .vidioc_enum_output = sh_vou_enum_output, .vidioc_s_std = sh_vou_s_std, .vidioc_g_std = sh_vou_g_std, .vidioc_cropcap = sh_vou_cropcap, -- cgit v1.2.3 From 22df2e7a390f6cb31c6a5b0d8051d0ec83769ed1 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 7 Jun 2015 05:58:00 -0300 Subject: [media] sh-vou: fix incorrect initial pixelformat It was set to a format that wasn't supported. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sh_vou.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/sh_vou.c b/drivers/media/platform/sh_vou.c index d9a4502b4a58..262c244fcc39 100644 --- a/drivers/media/platform/sh_vou.c +++ b/drivers/media/platform/sh_vou.c @@ -1368,7 +1368,7 @@ static int sh_vou_probe(struct platform_device *pdev) rect->height = 480; pix->width = VOU_MAX_IMAGE_WIDTH; pix->height = 480; - pix->pixelformat = V4L2_PIX_FMT_YVYU; + pix->pixelformat = V4L2_PIX_FMT_NV16; pix->field = V4L2_FIELD_NONE; pix->bytesperline = VOU_MAX_IMAGE_WIDTH * 2; pix->sizeimage = VOU_MAX_IMAGE_WIDTH * 2 * 480; -- cgit v1.2.3 From 61fbacc115731d2b0435f959f94ff7eb8c3c1059 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 7 Jun 2015 05:58:01 -0300 Subject: [media] sh-vou: replace g/s_crop/cropcap by g/s_selection Implement g/s_selection. The v4l2 core will emulate g/s_crop and cropcap on top of g/s_selection. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sh_vou.c | 71 +++++++++++++++-------------------------- 1 file changed, 25 insertions(+), 46 deletions(-) diff --git a/drivers/media/platform/sh_vou.c b/drivers/media/platform/sh_vou.c index 262c244fcc39..9479c4429e97 100644 --- a/drivers/media/platform/sh_vou.c +++ b/drivers/media/platform/sh_vou.c @@ -949,24 +949,36 @@ static int sh_vou_g_std(struct file *file, void *priv, v4l2_std_id *std) return 0; } -static int sh_vou_g_crop(struct file *file, void *fh, struct v4l2_crop *a) +static int sh_vou_g_selection(struct file *file, void *fh, + struct v4l2_selection *sel) { struct sh_vou_device *vou_dev = video_drvdata(file); - dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); - - a->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; - a->c = vou_dev->rect; - + if (sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + switch (sel->target) { + case V4L2_SEL_TGT_COMPOSE: + sel->r = vou_dev->rect; + break; + case V4L2_SEL_TGT_COMPOSE_DEFAULT: + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = VOU_MAX_IMAGE_WIDTH; + sel->r.height = VOU_MAX_IMAGE_HEIGHT; + break; + default: + return -EINVAL; + } return 0; } /* Assume a dull encoder, do all the work ourselves. */ -static int sh_vou_s_crop(struct file *file, void *fh, const struct v4l2_crop *a) +static int sh_vou_s_selection(struct file *file, void *fh, + struct v4l2_selection *sel) { - struct v4l2_crop a_writable = *a; + struct v4l2_rect *rect = &sel->r; struct sh_vou_device *vou_dev = video_drvdata(file); - struct v4l2_rect *rect = &a_writable.c; struct v4l2_crop sd_crop = {.type = V4L2_BUF_TYPE_VIDEO_OUTPUT}; struct v4l2_pix_format *pix = &vou_dev->pix; struct sh_vou_geometry geo; @@ -980,10 +992,8 @@ static int sh_vou_s_crop(struct file *file, void *fh, const struct v4l2_crop *a) unsigned int img_height_max; int ret; - dev_dbg(vou_dev->v4l2_dev.dev, "%s(): %ux%u@%u:%u\n", __func__, - rect->width, rect->height, rect->left, rect->top); - - if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + if (sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT || + sel->target != V4L2_SEL_TGT_COMPOSE) return -EINVAL; if (vou_dev->std & V4L2_STD_525_60) @@ -1047,36 +1057,6 @@ static int sh_vou_s_crop(struct file *file, void *fh, const struct v4l2_crop *a) return 0; } -/* - * Total field: NTSC 858 x 2 * 262/263, PAL 864 x 2 * 312/313, default rectangle - * is the initial register values, height takes the interlaced format into - * account. The actual image can only go up to 720 x 2 * 240, So, VOUVPR can - * actually only meaningfully contain values <= 720 and <= 240 respectively, and - * not <= 864 and <= 312. - */ -static int sh_vou_cropcap(struct file *file, void *priv, - struct v4l2_cropcap *a) -{ - struct sh_vou_device *vou_dev = video_drvdata(file); - - dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); - - a->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; - a->bounds.left = 0; - a->bounds.top = 0; - a->bounds.width = VOU_MAX_IMAGE_WIDTH; - a->bounds.height = VOU_MAX_IMAGE_HEIGHT; - /* Default = max, set VOUDPR = 0, which is not hardware default */ - a->defrect.left = 0; - a->defrect.top = 0; - a->defrect.width = VOU_MAX_IMAGE_WIDTH; - a->defrect.height = VOU_MAX_IMAGE_HEIGHT; - a->pixelaspect.numerator = 1; - a->pixelaspect.denominator = 1; - - return 0; -} - static irqreturn_t sh_vou_isr(int irq, void *dev_id) { struct sh_vou_device *vou_dev = dev_id; @@ -1305,9 +1285,8 @@ static const struct v4l2_ioctl_ops sh_vou_ioctl_ops = { .vidioc_enum_output = sh_vou_enum_output, .vidioc_s_std = sh_vou_s_std, .vidioc_g_std = sh_vou_g_std, - .vidioc_cropcap = sh_vou_cropcap, - .vidioc_g_crop = sh_vou_g_crop, - .vidioc_s_crop = sh_vou_s_crop, + .vidioc_g_selection = sh_vou_g_selection, + .vidioc_s_selection = sh_vou_s_selection, }; static const struct v4l2_file_operations sh_vou_fops = { -- cgit v1.2.3 From 66853ec4e1d096efe406383adc219285e5516af4 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 7 Jun 2015 05:58:03 -0300 Subject: [media] sh-vou: let sh_vou_s_fmt_vid_out call sh_vou_try_fmt_vid_out This ensures that both do the same checks, and simplifies s_fmt_vid_out a bit. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sh_vou.c | 86 +++++++++++++++++++---------------------- 1 file changed, 40 insertions(+), 46 deletions(-) diff --git a/drivers/media/platform/sh_vou.c b/drivers/media/platform/sh_vou.c index 9479c4429e97..079910d7062f 100644 --- a/drivers/media/platform/sh_vou.c +++ b/drivers/media/platform/sh_vou.c @@ -673,34 +673,19 @@ static void vou_adjust_output(struct sh_vou_geometry *geo, v4l2_std_id std) vou_scale_v_num[idx_v], vou_scale_v_den[idx_v], best); } -static int sh_vou_s_fmt_vid_out(struct file *file, void *priv, - struct v4l2_format *fmt) +static int sh_vou_try_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *fmt) { struct sh_vou_device *vou_dev = video_drvdata(file); struct v4l2_pix_format *pix = &fmt->fmt.pix; unsigned int img_height_max; int pix_idx; - struct sh_vou_geometry geo; - struct v4l2_subdev_format format = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - /* Revisit: is this the correct code? */ - .format.code = MEDIA_BUS_FMT_YUYV8_2X8, - .format.field = V4L2_FIELD_INTERLACED, - .format.colorspace = V4L2_COLORSPACE_SMPTE170M, - }; - struct v4l2_mbus_framefmt *mbfmt = &format.format; - int ret; - - dev_dbg(vou_dev->v4l2_dev.dev, "%s(): %ux%u -> %ux%u\n", __func__, - vou_dev->rect.width, vou_dev->rect.height, - pix->width, pix->height); - if (pix->field == V4L2_FIELD_ANY) - pix->field = V4L2_FIELD_NONE; + dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); - if (fmt->type != V4L2_BUF_TYPE_VIDEO_OUTPUT || - pix->field != V4L2_FIELD_NONE) - return -EINVAL; + pix->field = V4L2_FIELD_INTERLACED; + pix->colorspace = V4L2_COLORSPACE_SMPTE170M; + pix->ycbcr_enc = pix->quantization = 0; for (pix_idx = 0; pix_idx < ARRAY_SIZE(vou_fmt); pix_idx++) if (vou_fmt[pix_idx].pfmt == pix->pixelformat) @@ -714,9 +699,37 @@ static int sh_vou_s_fmt_vid_out(struct file *file, void *priv, else img_height_max = 576; - /* Image width must be a multiple of 4 */ v4l_bound_align_image(&pix->width, 0, VOU_MAX_IMAGE_WIDTH, 2, &pix->height, 0, img_height_max, 1, 0); + pix->bytesperline = pix->width * 2; + + return 0; +} + +static int sh_vou_s_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct sh_vou_device *vou_dev = video_drvdata(file); + struct v4l2_pix_format *pix = &fmt->fmt.pix; + unsigned int img_height_max; + struct sh_vou_geometry geo; + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + /* Revisit: is this the correct code? */ + .format.code = MEDIA_BUS_FMT_YUYV8_2X8, + .format.field = V4L2_FIELD_INTERLACED, + .format.colorspace = V4L2_COLORSPACE_SMPTE170M, + }; + struct v4l2_mbus_framefmt *mbfmt = &format.format; + int ret = sh_vou_try_fmt_vid_out(file, priv, fmt); + int pix_idx; + + if (ret) + return ret; + + for (pix_idx = 0; pix_idx < ARRAY_SIZE(vou_fmt); pix_idx++) + if (vou_fmt[pix_idx].pfmt == pix->pixelformat) + break; geo.in_width = pix->width; geo.in_height = pix->height; @@ -735,6 +748,11 @@ static int sh_vou_s_fmt_vid_out(struct file *file, void *priv, dev_dbg(vou_dev->v4l2_dev.dev, "%s(): %ux%u -> %ux%u\n", __func__, geo.output.width, geo.output.height, mbfmt->width, mbfmt->height); + if (vou_dev->std & V4L2_STD_525_60) + img_height_max = 480; + else + img_height_max = 576; + /* Sanity checks */ if ((unsigned)mbfmt->width > VOU_MAX_IMAGE_WIDTH || (unsigned)mbfmt->height > img_height_max || @@ -767,30 +785,6 @@ static int sh_vou_s_fmt_vid_out(struct file *file, void *priv, return 0; } -static int sh_vou_try_fmt_vid_out(struct file *file, void *priv, - struct v4l2_format *fmt) -{ - struct sh_vou_device *vou_dev = video_drvdata(file); - struct v4l2_pix_format *pix = &fmt->fmt.pix; - int i; - - dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); - - fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; - pix->field = V4L2_FIELD_NONE; - - v4l_bound_align_image(&pix->width, 0, VOU_MAX_IMAGE_WIDTH, 1, - &pix->height, 0, VOU_MAX_IMAGE_HEIGHT, 1, 0); - - for (i = 0; i < ARRAY_SIZE(vou_fmt); i++) - if (vou_fmt[i].pfmt == pix->pixelformat) - return 0; - - pix->pixelformat = vou_fmt[0].pfmt; - - return 0; -} - static int sh_vou_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *req) { -- cgit v1.2.3 From 5c3edcb225d6690000c2563c573984042747b28d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 7 Jun 2015 05:58:04 -0300 Subject: [media] sh-vou: fix bytesperline The bytesperline values were wrong for planar formats where bytesperline is the line length for the first plane. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sh_vou.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/sh_vou.c b/drivers/media/platform/sh_vou.c index 079910d7062f..6cf80835e8ba 100644 --- a/drivers/media/platform/sh_vou.c +++ b/drivers/media/platform/sh_vou.c @@ -133,6 +133,7 @@ struct sh_vou_fmt { u32 pfmt; char *desc; unsigned char bpp; + unsigned char bpl; unsigned char rgb; unsigned char yf; unsigned char pkf; @@ -143,6 +144,7 @@ static struct sh_vou_fmt vou_fmt[] = { { .pfmt = V4L2_PIX_FMT_NV12, .bpp = 12, + .bpl = 1, .desc = "YVU420 planar", .yf = 0, .rgb = 0, @@ -150,6 +152,7 @@ static struct sh_vou_fmt vou_fmt[] = { { .pfmt = V4L2_PIX_FMT_NV16, .bpp = 16, + .bpl = 1, .desc = "YVYU planar", .yf = 1, .rgb = 0, @@ -157,6 +160,7 @@ static struct sh_vou_fmt vou_fmt[] = { { .pfmt = V4L2_PIX_FMT_RGB24, .bpp = 24, + .bpl = 3, .desc = "RGB24", .pkf = 2, .rgb = 1, @@ -164,6 +168,7 @@ static struct sh_vou_fmt vou_fmt[] = { { .pfmt = V4L2_PIX_FMT_RGB565, .bpp = 16, + .bpl = 2, .desc = "RGB565", .pkf = 3, .rgb = 1, @@ -171,6 +176,7 @@ static struct sh_vou_fmt vou_fmt[] = { { .pfmt = V4L2_PIX_FMT_RGB565X, .bpp = 16, + .bpl = 2, .desc = "RGB565 byteswapped", .pkf = 3, .rgb = 1, @@ -701,7 +707,8 @@ static int sh_vou_try_fmt_vid_out(struct file *file, void *priv, v4l_bound_align_image(&pix->width, 0, VOU_MAX_IMAGE_WIDTH, 2, &pix->height, 0, img_height_max, 1, 0); - pix->bytesperline = pix->width * 2; + pix->bytesperline = pix->width * vou_fmt[pix_idx].bpl; + pix->sizeimage = pix->height * ((pix->width * vou_fmt[pix_idx].bpp) >> 3); return 0; } @@ -1343,7 +1350,7 @@ static int sh_vou_probe(struct platform_device *pdev) pix->height = 480; pix->pixelformat = V4L2_PIX_FMT_NV16; pix->field = V4L2_FIELD_NONE; - pix->bytesperline = VOU_MAX_IMAGE_WIDTH * 2; + pix->bytesperline = VOU_MAX_IMAGE_WIDTH; pix->sizeimage = VOU_MAX_IMAGE_WIDTH * 2 * 480; pix->colorspace = V4L2_COLORSPACE_SMPTE170M; -- cgit v1.2.3 From 57af3ad59d953f300a1fcb143e72d024aff73550 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 7 Jun 2015 05:58:05 -0300 Subject: [media] sh-vou: convert to vb2 This converts this driver to videobuf2. As usual it is a big and hard to review patch, but this is always a big-bang change. It has been tested with my Renesas board. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sh_vou.c | 590 +++++++++++++++++----------------------- 1 file changed, 250 insertions(+), 340 deletions(-) diff --git a/drivers/media/platform/sh_vou.c b/drivers/media/platform/sh_vou.c index 6cf80835e8ba..da8ea6a76327 100644 --- a/drivers/media/platform/sh_vou.c +++ b/drivers/media/platform/sh_vou.c @@ -27,7 +27,7 @@ #include #include #include -#include +#include /* Mirror addresses are not available for all registers */ #define VOUER 0 @@ -57,8 +57,19 @@ enum sh_vou_status { SH_VOU_RUNNING, }; +#define VOU_MIN_IMAGE_WIDTH 16 #define VOU_MAX_IMAGE_WIDTH 720 -#define VOU_MAX_IMAGE_HEIGHT 576 +#define VOU_MIN_IMAGE_HEIGHT 16 + +struct sh_vou_buffer { + struct vb2_buffer vb; + struct list_head list; +}; + +static inline struct sh_vou_buffer *to_sh_vou_buffer(struct vb2_buffer *vb2) +{ + return container_of(vb2, struct sh_vou_buffer, vb); +} struct sh_vou_device { struct v4l2_device v4l2_dev; @@ -69,19 +80,17 @@ struct sh_vou_device { /* State information */ struct v4l2_pix_format pix; struct v4l2_rect rect; - struct list_head queue; + struct list_head buf_list; v4l2_std_id std; int pix_idx; - struct videobuf_buffer *active; + struct vb2_queue queue; + struct vb2_alloc_ctx *alloc_ctx; + struct sh_vou_buffer *active; enum sh_vou_status status; + unsigned sequence; struct mutex fop_lock; }; -struct sh_vou_file { - struct v4l2_fh fh; - struct videobuf_queue vbq; -}; - /* Register access routines for sides A, B and mirror addresses */ static void sh_vou_reg_a_write(struct sh_vou_device *vou_dev, unsigned int reg, u32 value) @@ -184,11 +193,11 @@ static struct sh_vou_fmt vou_fmt[] = { }; static void sh_vou_schedule_next(struct sh_vou_device *vou_dev, - struct videobuf_buffer *vb) + struct vb2_buffer *vb) { dma_addr_t addr1, addr2; - addr1 = videobuf_to_dma_contig(vb); + addr1 = vb2_dma_contig_plane_dma_addr(vb, 0); switch (vou_dev->pix.pixelformat) { case V4L2_PIX_FMT_NV12: case V4L2_PIX_FMT_NV16: @@ -202,8 +211,7 @@ static void sh_vou_schedule_next(struct sh_vou_device *vou_dev, sh_vou_reg_m_write(vou_dev, VOUAD2R, addr2); } -static void sh_vou_stream_start(struct sh_vou_device *vou_dev, - struct videobuf_buffer *vb) +static void sh_vou_stream_config(struct sh_vou_device *vou_dev) { unsigned int row_coeff; #ifdef __LITTLE_ENDIAN @@ -230,167 +238,136 @@ static void sh_vou_stream_start(struct sh_vou_device *vou_dev, sh_vou_reg_a_write(vou_dev, VOUSWR, dataswap); sh_vou_reg_ab_write(vou_dev, VOUAIR, vou_dev->pix.width * row_coeff); - sh_vou_schedule_next(vou_dev, vb); -} - -static void free_buffer(struct videobuf_queue *vq, struct videobuf_buffer *vb) -{ - BUG_ON(in_interrupt()); - - /* Wait until this buffer is no longer in STATE_QUEUED or STATE_ACTIVE */ - videobuf_waiton(vq, vb, 0, 0); - videobuf_dma_contig_free(vq, vb); - vb->state = VIDEOBUF_NEEDS_INIT; } /* Locking: caller holds fop_lock mutex */ -static int sh_vou_buf_setup(struct videobuf_queue *vq, unsigned int *count, - unsigned int *size) +static int sh_vou_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) { - struct video_device *vdev = vq->priv_data; - struct sh_vou_device *vou_dev = video_get_drvdata(vdev); - - *size = vou_fmt[vou_dev->pix_idx].bpp * vou_dev->pix.width * - vou_dev->pix.height / 8; - - if (*count < 2) - *count = 2; - - /* Taking into account maximum frame size, *count will stay >= 2 */ - if (PAGE_ALIGN(*size) * *count > 4 * 1024 * 1024) - *count = 4 * 1024 * 1024 / PAGE_ALIGN(*size); + struct sh_vou_device *vou_dev = vb2_get_drv_priv(vq); + struct v4l2_pix_format *pix = &vou_dev->pix; + int bytes_per_line = vou_fmt[vou_dev->pix_idx].bpp * pix->width / 8; - dev_dbg(vou_dev->v4l2_dev.dev, "%s(): count=%d, size=%d\n", __func__, - *count, *size); + dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); + if (fmt && fmt->fmt.pix.sizeimage < pix->height * bytes_per_line) + return -EINVAL; + *nplanes = 1; + sizes[0] = fmt ? fmt->fmt.pix.sizeimage : pix->height * bytes_per_line; + alloc_ctxs[0] = vou_dev->alloc_ctx; return 0; } -/* Locking: caller holds fop_lock mutex */ -static int sh_vou_buf_prepare(struct videobuf_queue *vq, - struct videobuf_buffer *vb, - enum v4l2_field field) +static int sh_vou_buf_prepare(struct vb2_buffer *vb) { - struct video_device *vdev = vq->priv_data; - struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + struct sh_vou_device *vou_dev = vb2_get_drv_priv(vb->vb2_queue); struct v4l2_pix_format *pix = &vou_dev->pix; - int bytes_per_line = vou_fmt[vou_dev->pix_idx].bpp * pix->width / 8; - int ret; + unsigned bytes_per_line = vou_fmt[vou_dev->pix_idx].bpp * pix->width / 8; + unsigned size = pix->height * bytes_per_line; dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); - if (vb->width != pix->width || - vb->height != pix->height || - vb->field != pix->field) { - vb->width = pix->width; - vb->height = pix->height; - vb->field = field; - if (vb->state != VIDEOBUF_NEEDS_INIT) - free_buffer(vq, vb); - } - - vb->size = vb->height * bytes_per_line; - if (vb->baddr && vb->bsize < vb->size) { + if (vb2_plane_size(vb, 0) < size) { /* User buffer too small */ - dev_warn(vq->dev, "User buffer too small: [%zu] @ %lx\n", - vb->bsize, vb->baddr); + dev_warn(vou_dev->v4l2_dev.dev, "buffer too small (%lu < %u)\n", + vb2_plane_size(vb, 0), size); return -EINVAL; } - if (vb->state == VIDEOBUF_NEEDS_INIT) { - ret = videobuf_iolock(vq, vb, NULL); - if (ret < 0) { - dev_warn(vq->dev, "IOLOCK buf-type %d: %d\n", - vb->memory, ret); - return ret; - } - vb->state = VIDEOBUF_PREPARED; - } - - dev_dbg(vou_dev->v4l2_dev.dev, - "%s(): fmt #%d, %u bytes per line, phys %pad, type %d, state %d\n", - __func__, vou_dev->pix_idx, bytes_per_line, - ({ dma_addr_t addr = videobuf_to_dma_contig(vb); &addr; }), - vb->memory, vb->state); - + vb2_set_plane_payload(vb, 0, size); return 0; } /* Locking: caller holds fop_lock mutex and vq->irqlock spinlock */ -static void sh_vou_buf_queue(struct videobuf_queue *vq, - struct videobuf_buffer *vb) +static void sh_vou_buf_queue(struct vb2_buffer *vb) { - struct video_device *vdev = vq->priv_data; - struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + struct sh_vou_device *vou_dev = vb2_get_drv_priv(vb->vb2_queue); + struct sh_vou_buffer *shbuf = to_sh_vou_buffer(vb); + unsigned long flags; - dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); + spin_lock_irqsave(&vou_dev->lock, flags); + list_add_tail(&shbuf->list, &vou_dev->buf_list); + spin_unlock_irqrestore(&vou_dev->lock, flags); +} - vb->state = VIDEOBUF_QUEUED; - list_add_tail(&vb->queue, &vou_dev->queue); - - if (vou_dev->status == SH_VOU_RUNNING) { - return; - } else if (!vou_dev->active) { - vou_dev->active = vb; - /* Start from side A: we use mirror addresses, so, set B */ - sh_vou_reg_a_write(vou_dev, VOURPR, 1); - dev_dbg(vou_dev->v4l2_dev.dev, "%s: first buffer status 0x%x\n", - __func__, sh_vou_reg_a_read(vou_dev, VOUSTR)); - sh_vou_schedule_next(vou_dev, vb); - /* Only activate VOU after the second buffer */ - } else if (vou_dev->active->queue.next == &vb->queue) { - /* Second buffer - initialise register side B */ - sh_vou_reg_a_write(vou_dev, VOURPR, 0); - sh_vou_stream_start(vou_dev, vb); - - /* Register side switching with frame VSYNC */ - sh_vou_reg_a_write(vou_dev, VOURCR, 5); - dev_dbg(vou_dev->v4l2_dev.dev, "%s: second buffer status 0x%x\n", - __func__, sh_vou_reg_a_read(vou_dev, VOUSTR)); - - /* Enable End-of-Frame (VSYNC) interrupts */ - sh_vou_reg_a_write(vou_dev, VOUIR, 0x10004); - /* Two buffers on the queue - activate the hardware */ - - vou_dev->status = SH_VOU_RUNNING; - sh_vou_reg_a_write(vou_dev, VOUER, 0x107); +static int sh_vou_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct sh_vou_device *vou_dev = vb2_get_drv_priv(vq); + struct sh_vou_buffer *buf, *node; + int ret; + + vou_dev->sequence = 0; + ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, + video, s_stream, 1); + if (ret < 0 && ret != -ENOIOCTLCMD) { + list_for_each_entry_safe(buf, node, &vou_dev->buf_list, list) { + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED); + list_del(&buf->list); + } + vou_dev->active = NULL; + return ret; } + + buf = list_entry(vou_dev->buf_list.next, struct sh_vou_buffer, list); + + vou_dev->active = buf; + + /* Start from side A: we use mirror addresses, so, set B */ + sh_vou_reg_a_write(vou_dev, VOURPR, 1); + dev_dbg(vou_dev->v4l2_dev.dev, "%s: first buffer status 0x%x\n", + __func__, sh_vou_reg_a_read(vou_dev, VOUSTR)); + sh_vou_schedule_next(vou_dev, &buf->vb); + + buf = list_entry(buf->list.next, struct sh_vou_buffer, list); + + /* Second buffer - initialise register side B */ + sh_vou_reg_a_write(vou_dev, VOURPR, 0); + sh_vou_schedule_next(vou_dev, &buf->vb); + + /* Register side switching with frame VSYNC */ + sh_vou_reg_a_write(vou_dev, VOURCR, 5); + + sh_vou_stream_config(vou_dev); + /* Enable End-of-Frame (VSYNC) interrupts */ + sh_vou_reg_a_write(vou_dev, VOUIR, 0x10004); + + /* Two buffers on the queue - activate the hardware */ + vou_dev->status = SH_VOU_RUNNING; + sh_vou_reg_a_write(vou_dev, VOUER, 0x107); + return 0; } -static void sh_vou_buf_release(struct videobuf_queue *vq, - struct videobuf_buffer *vb) +static void sh_vou_stop_streaming(struct vb2_queue *vq) { - struct video_device *vdev = vq->priv_data; - struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + struct sh_vou_device *vou_dev = vb2_get_drv_priv(vq); + struct sh_vou_buffer *buf, *node; unsigned long flags; - dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); - + v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, + video, s_stream, 0); + /* disable output */ + sh_vou_reg_a_set(vou_dev, VOUER, 0, 1); + /* ...but the current frame will complete */ + sh_vou_reg_a_set(vou_dev, VOUIR, 0, 0x30000); + msleep(50); spin_lock_irqsave(&vou_dev->lock, flags); - - if (vou_dev->active == vb) { - /* disable output */ - sh_vou_reg_a_set(vou_dev, VOUER, 0, 1); - /* ...but the current frame will complete */ - sh_vou_reg_a_set(vou_dev, VOUIR, 0, 0x30000); - vou_dev->active = NULL; + list_for_each_entry_safe(buf, node, &vou_dev->buf_list, list) { + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + list_del(&buf->list); } - - if ((vb->state == VIDEOBUF_ACTIVE || vb->state == VIDEOBUF_QUEUED)) { - vb->state = VIDEOBUF_ERROR; - list_del(&vb->queue); - } - + vou_dev->active = NULL; spin_unlock_irqrestore(&vou_dev->lock, flags); - - free_buffer(vq, vb); } -static struct videobuf_queue_ops sh_vou_video_qops = { - .buf_setup = sh_vou_buf_setup, - .buf_prepare = sh_vou_buf_prepare, - .buf_queue = sh_vou_buf_queue, - .buf_release = sh_vou_buf_release, +static struct vb2_ops sh_vou_qops = { + .queue_setup = sh_vou_queue_setup, + .buf_prepare = sh_vou_buf_prepare, + .buf_queue = sh_vou_buf_queue, + .start_streaming = sh_vou_start_streaming, + .stop_streaming = sh_vou_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, }; /* Video IOCTLs */ @@ -404,7 +381,8 @@ static int sh_vou_querycap(struct file *file, void *priv, strlcpy(cap->card, "SuperH VOU", sizeof(cap->card)); strlcpy(cap->driver, "sh-vou", sizeof(cap->driver)); strlcpy(cap->bus_info, "platform:sh-vou", sizeof(cap->bus_info)); - cap->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; + cap->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -548,8 +526,10 @@ static void vou_adjust_input(struct sh_vou_geometry *geo, v4l2_std_id std) img_height_max = 576; /* Image width must be a multiple of 4 */ - v4l_bound_align_image(&geo->in_width, 0, VOU_MAX_IMAGE_WIDTH, 2, - &geo->in_height, 0, img_height_max, 1, 0); + v4l_bound_align_image(&geo->in_width, + VOU_MIN_IMAGE_WIDTH, VOU_MAX_IMAGE_WIDTH, 2, + &geo->in_height, + VOU_MIN_IMAGE_HEIGHT, img_height_max, 1, 0); /* Select scales to come as close as possible to the output image */ for (i = ARRAY_SIZE(vou_scale_h_num) - 1; i >= 0; i--) { @@ -705,19 +685,19 @@ static int sh_vou_try_fmt_vid_out(struct file *file, void *priv, else img_height_max = 576; - v4l_bound_align_image(&pix->width, 0, VOU_MAX_IMAGE_WIDTH, 2, - &pix->height, 0, img_height_max, 1, 0); + v4l_bound_align_image(&pix->width, + VOU_MIN_IMAGE_WIDTH, VOU_MAX_IMAGE_WIDTH, 2, + &pix->height, + VOU_MIN_IMAGE_HEIGHT, img_height_max, 1, 0); pix->bytesperline = pix->width * vou_fmt[pix_idx].bpl; pix->sizeimage = pix->height * ((pix->width * vou_fmt[pix_idx].bpp) >> 3); return 0; } -static int sh_vou_s_fmt_vid_out(struct file *file, void *priv, - struct v4l2_format *fmt) +static int sh_vou_set_fmt_vid_out(struct sh_vou_device *vou_dev, + struct v4l2_pix_format *pix) { - struct sh_vou_device *vou_dev = video_drvdata(file); - struct v4l2_pix_format *pix = &fmt->fmt.pix; unsigned int img_height_max; struct sh_vou_geometry geo; struct v4l2_subdev_format format = { @@ -728,11 +708,11 @@ static int sh_vou_s_fmt_vid_out(struct file *file, void *priv, .format.colorspace = V4L2_COLORSPACE_SMPTE170M, }; struct v4l2_mbus_framefmt *mbfmt = &format.format; - int ret = sh_vou_try_fmt_vid_out(file, priv, fmt); int pix_idx; + int ret; - if (ret) - return ret; + if (vb2_is_busy(&vou_dev->queue)) + return -EBUSY; for (pix_idx = 0; pix_idx < ARRAY_SIZE(vou_fmt); pix_idx++) if (vou_fmt[pix_idx].pfmt == pix->pixelformat) @@ -792,85 +772,15 @@ static int sh_vou_s_fmt_vid_out(struct file *file, void *priv, return 0; } -static int sh_vou_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *req) -{ - struct sh_vou_device *vou_dev = video_drvdata(file); - struct sh_vou_file *vou_file = priv; - - dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); - - if (req->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) - return -EINVAL; - - return videobuf_reqbufs(&vou_file->vbq, req); -} - -static int sh_vou_querybuf(struct file *file, void *priv, - struct v4l2_buffer *b) -{ - struct sh_vou_device *vou_dev = video_drvdata(file); - struct sh_vou_file *vou_file = priv; - - dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); - - return videobuf_querybuf(&vou_file->vbq, b); -} - -static int sh_vou_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) -{ - struct sh_vou_device *vou_dev = video_drvdata(file); - struct sh_vou_file *vou_file = priv; - - dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); - - return videobuf_qbuf(&vou_file->vbq, b); -} - -static int sh_vou_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) -{ - struct sh_vou_device *vou_dev = video_drvdata(file); - struct sh_vou_file *vou_file = priv; - - dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); - - return videobuf_dqbuf(&vou_file->vbq, b, file->f_flags & O_NONBLOCK); -} - -static int sh_vou_streamon(struct file *file, void *priv, - enum v4l2_buf_type buftype) +static int sh_vou_s_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *fmt) { struct sh_vou_device *vou_dev = video_drvdata(file); - struct sh_vou_file *vou_file = priv; - int ret; - - dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); + int ret = sh_vou_try_fmt_vid_out(file, priv, fmt); - ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, - video, s_stream, 1); - if (ret < 0 && ret != -ENOIOCTLCMD) + if (ret) return ret; - - /* This calls our .buf_queue() (== sh_vou_buf_queue) */ - return videobuf_streamon(&vou_file->vbq); -} - -static int sh_vou_streamoff(struct file *file, void *priv, - enum v4l2_buf_type buftype) -{ - struct sh_vou_device *vou_dev = video_drvdata(file); - struct sh_vou_file *vou_file = priv; - - dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); - - /* - * This calls buf_release from host driver's videobuf_queue_ops for all - * remaining buffers. When the last buffer is freed, stop streaming - */ - videobuf_streamoff(&vou_file->vbq); - v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video, s_stream, 0); - - return 0; + return sh_vou_set_fmt_vid_out(vou_dev, &fmt->fmt.pix); } static int sh_vou_enum_output(struct file *file, void *fh, @@ -919,8 +829,11 @@ static int sh_vou_s_std(struct file *file, void *priv, v4l2_std_id std_id) dev_dbg(vou_dev->v4l2_dev.dev, "%s(): 0x%llx\n", __func__, std_id); - if (std_id & ~vou_dev->vdev.tvnorms) - return -EINVAL; + if (std_id == vou_dev->std) + return 0; + + if (vb2_is_busy(&vou_dev->queue)) + return -EBUSY; ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video, s_std_output, std_id); @@ -928,13 +841,25 @@ static int sh_vou_s_std(struct file *file, void *priv, v4l2_std_id std_id) if (ret < 0 && ret != -ENOIOCTLCMD) return ret; - if (std_id & V4L2_STD_525_60) + vou_dev->rect.top = vou_dev->rect.left = 0; + vou_dev->rect.width = VOU_MAX_IMAGE_WIDTH; + if (std_id & V4L2_STD_525_60) { sh_vou_reg_ab_set(vou_dev, VOUCR, sh_vou_ntsc_mode(vou_dev->pdata->bus_fmt) << 29, 7 << 29); - else + vou_dev->rect.height = 480; + } else { sh_vou_reg_ab_set(vou_dev, VOUCR, 5 << 29, 7 << 29); + vou_dev->rect.height = 576; + } + vou_dev->pix.width = vou_dev->rect.width; + vou_dev->pix.height = vou_dev->rect.height; + vou_dev->pix.bytesperline = + vou_dev->pix.width * vou_fmt[vou_dev->pix_idx].bpl; + vou_dev->pix.sizeimage = vou_dev->pix.height * + ((vou_dev->pix.width * vou_fmt[vou_dev->pix_idx].bpp) >> 3); vou_dev->std = std_id; + sh_vou_set_fmt_vid_out(vou_dev, &vou_dev->pix); return 0; } @@ -966,7 +891,10 @@ static int sh_vou_g_selection(struct file *file, void *fh, sel->r.left = 0; sel->r.top = 0; sel->r.width = VOU_MAX_IMAGE_WIDTH; - sel->r.height = VOU_MAX_IMAGE_HEIGHT; + if (vou_dev->std & V4L2_STD_525_60) + sel->r.height = 480; + else + sel->r.height = 576; break; default: return -EINVAL; @@ -997,13 +925,18 @@ static int sh_vou_s_selection(struct file *file, void *fh, sel->target != V4L2_SEL_TGT_COMPOSE) return -EINVAL; + if (vb2_is_busy(&vou_dev->queue)) + return -EBUSY; + if (vou_dev->std & V4L2_STD_525_60) img_height_max = 480; else img_height_max = 576; - v4l_bound_align_image(&rect->width, 0, VOU_MAX_IMAGE_WIDTH, 1, - &rect->height, 0, img_height_max, 1, 0); + v4l_bound_align_image(&rect->width, + VOU_MIN_IMAGE_WIDTH, VOU_MAX_IMAGE_WIDTH, 1, + &rect->height, + VOU_MIN_IMAGE_HEIGHT, img_height_max, 1, 0); if (rect->width + rect->left > VOU_MAX_IMAGE_WIDTH) rect->left = VOU_MAX_IMAGE_WIDTH - rect->width; @@ -1062,7 +995,7 @@ static irqreturn_t sh_vou_isr(int irq, void *dev_id) { struct sh_vou_device *vou_dev = dev_id; static unsigned long j; - struct videobuf_buffer *vb; + struct sh_vou_buffer *vb; static int cnt; u32 irq_status = sh_vou_reg_a_read(vou_dev, VOUIR), masked; u32 vou_status = sh_vou_reg_a_read(vou_dev, VOUSTR); @@ -1075,7 +1008,7 @@ static irqreturn_t sh_vou_isr(int irq, void *dev_id) } spin_lock(&vou_dev->lock); - if (!vou_dev->active || list_empty(&vou_dev->queue)) { + if (!vou_dev->active || list_empty(&vou_dev->buf_list)) { if (printk_timed_ratelimit(&j, 500)) dev_warn(vou_dev->v4l2_dev.dev, "IRQ without active buffer: %x!\n", irq_status); @@ -1097,33 +1030,30 @@ static irqreturn_t sh_vou_isr(int irq, void *dev_id) sh_vou_reg_a_write(vou_dev, VOUIR, masked); vb = vou_dev->active; - list_del(&vb->queue); - - vb->state = VIDEOBUF_DONE; - v4l2_get_timestamp(&vb->ts); - vb->field_count++; - wake_up(&vb->done); - - if (list_empty(&vou_dev->queue)) { - /* Stop VOU */ - dev_dbg(vou_dev->v4l2_dev.dev, "%s: queue empty after %d\n", - __func__, cnt); - sh_vou_reg_a_set(vou_dev, VOUER, 0, 1); - vou_dev->active = NULL; - vou_dev->status = SH_VOU_INITIALISING; - /* Disable End-of-Frame (VSYNC) interrupts */ - sh_vou_reg_a_set(vou_dev, VOUIR, 0, 0x30000); + if (list_is_singular(&vb->list)) { + /* Keep cycling while no next buffer is available */ + sh_vou_schedule_next(vou_dev, &vb->vb); spin_unlock(&vou_dev->lock); return IRQ_HANDLED; } - vou_dev->active = list_entry(vou_dev->queue.next, - struct videobuf_buffer, queue); + list_del(&vb->list); + + v4l2_get_timestamp(&vb->vb.v4l2_buf.timestamp); + vb->vb.v4l2_buf.sequence = vou_dev->sequence++; + vb->vb.v4l2_buf.field = V4L2_FIELD_INTERLACED; + vb2_buffer_done(&vb->vb, VB2_BUF_STATE_DONE); + + vou_dev->active = list_entry(vou_dev->buf_list.next, + struct sh_vou_buffer, list); - if (vou_dev->active->queue.next != &vou_dev->queue) { - struct videobuf_buffer *new = list_entry(vou_dev->active->queue.next, - struct videobuf_buffer, queue); - sh_vou_schedule_next(vou_dev, new); + if (list_is_singular(&vou_dev->buf_list)) { + /* Keep cycling while no next buffer is available */ + sh_vou_schedule_next(vou_dev, &vou_dev->active->vb); + } else { + struct sh_vou_buffer *new = list_entry(vou_dev->active->list.next, + struct sh_vou_buffer, list); + sh_vou_schedule_next(vou_dev, &new->vb); } spin_unlock(&vou_dev->lock); @@ -1163,6 +1093,8 @@ static int sh_vou_hw_init(struct sh_vou_device *vou_dev) /* Default - fixed HSYNC length, can be made configurable is required */ sh_vou_reg_ab_write(vou_dev, VOUMSR, 0x800000); + sh_vou_set_fmt_vid_out(vou_dev, &vou_dev->pix); + return 0; } @@ -1170,104 +1102,49 @@ static int sh_vou_hw_init(struct sh_vou_device *vou_dev) static int sh_vou_open(struct file *file) { struct sh_vou_device *vou_dev = video_drvdata(file); - struct sh_vou_file *vou_file = kzalloc(sizeof(struct sh_vou_file), - GFP_KERNEL); - - if (!vou_file) - return -ENOMEM; - - dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); + int err; - v4l2_fh_init(&vou_file->fh, &vou_dev->vdev); - if (mutex_lock_interruptible(&vou_dev->fop_lock)) { - kfree(vou_file); + if (mutex_lock_interruptible(&vou_dev->fop_lock)) return -ERESTARTSYS; - } - v4l2_fh_add(&vou_file->fh); - if (v4l2_fh_is_singular(&vou_file->fh)) { - int ret; + err = v4l2_fh_open(file); + if (err) + goto done_open; + if (v4l2_fh_is_singular_file(file) && + vou_dev->status == SH_VOU_INITIALISING) { /* First open */ - vou_dev->status = SH_VOU_INITIALISING; pm_runtime_get_sync(vou_dev->v4l2_dev.dev); - ret = sh_vou_hw_init(vou_dev); - if (ret < 0) { + err = sh_vou_hw_init(vou_dev); + if (err < 0) { pm_runtime_put(vou_dev->v4l2_dev.dev); + v4l2_fh_release(file); + } else { vou_dev->status = SH_VOU_IDLE; - v4l2_fh_del(&vou_file->fh); - v4l2_fh_exit(&vou_file->fh); - mutex_unlock(&vou_dev->fop_lock); - kfree(vou_file); - return ret; } } - - videobuf_queue_dma_contig_init(&vou_file->vbq, &sh_vou_video_qops, - vou_dev->v4l2_dev.dev, &vou_dev->lock, - V4L2_BUF_TYPE_VIDEO_OUTPUT, - V4L2_FIELD_NONE, - sizeof(struct videobuf_buffer), - &vou_dev->vdev, &vou_dev->fop_lock); +done_open: mutex_unlock(&vou_dev->fop_lock); - - file->private_data = vou_file; - - return 0; + return err; } static int sh_vou_release(struct file *file) { struct sh_vou_device *vou_dev = video_drvdata(file); - struct sh_vou_file *vou_file = file->private_data; - - dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); + bool is_last; mutex_lock(&vou_dev->fop_lock); - if (v4l2_fh_is_singular(&vou_file->fh)) { + is_last = v4l2_fh_is_singular_file(file); + _vb2_fop_release(file, NULL); + if (is_last) { /* Last close */ - vou_dev->status = SH_VOU_IDLE; + vou_dev->status = SH_VOU_INITIALISING; sh_vou_reg_a_set(vou_dev, VOUER, 0, 0x101); pm_runtime_put(vou_dev->v4l2_dev.dev); } - v4l2_fh_del(&vou_file->fh); - v4l2_fh_exit(&vou_file->fh); mutex_unlock(&vou_dev->fop_lock); - - file->private_data = NULL; - kfree(vou_file); - return 0; } -static int sh_vou_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct sh_vou_device *vou_dev = video_drvdata(file); - struct sh_vou_file *vou_file = file->private_data; - int ret; - - dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); - - if (mutex_lock_interruptible(&vou_dev->fop_lock)) - return -ERESTARTSYS; - ret = videobuf_mmap_mapper(&vou_file->vbq, vma); - mutex_unlock(&vou_dev->fop_lock); - return ret; -} - -static unsigned int sh_vou_poll(struct file *file, poll_table *wait) -{ - struct sh_vou_device *vou_dev = video_drvdata(file); - struct sh_vou_file *vou_file = file->private_data; - unsigned int res; - - dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); - - mutex_lock(&vou_dev->fop_lock); - res = videobuf_poll_stream(file, &vou_file->vbq, wait); - mutex_unlock(&vou_dev->fop_lock); - return res; -} - /* sh_vou display ioctl operations */ static const struct v4l2_ioctl_ops sh_vou_ioctl_ops = { .vidioc_querycap = sh_vou_querycap, @@ -1275,12 +1152,15 @@ static const struct v4l2_ioctl_ops sh_vou_ioctl_ops = { .vidioc_g_fmt_vid_out = sh_vou_g_fmt_vid_out, .vidioc_s_fmt_vid_out = sh_vou_s_fmt_vid_out, .vidioc_try_fmt_vid_out = sh_vou_try_fmt_vid_out, - .vidioc_reqbufs = sh_vou_reqbufs, - .vidioc_querybuf = sh_vou_querybuf, - .vidioc_qbuf = sh_vou_qbuf, - .vidioc_dqbuf = sh_vou_dqbuf, - .vidioc_streamon = sh_vou_streamon, - .vidioc_streamoff = sh_vou_streamoff, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_expbuf = vb2_ioctl_expbuf, .vidioc_g_output = sh_vou_g_output, .vidioc_s_output = sh_vou_s_output, .vidioc_enum_output = sh_vou_enum_output, @@ -1295,8 +1175,9 @@ static const struct v4l2_file_operations sh_vou_fops = { .open = sh_vou_open, .release = sh_vou_release, .unlocked_ioctl = video_ioctl2, - .mmap = sh_vou_mmap, - .poll = sh_vou_poll, + .mmap = vb2_fop_mmap, + .poll = vb2_fop_poll, + .write = vb2_fop_write, }; static const struct video_device sh_vou_video_template = { @@ -1317,6 +1198,7 @@ static int sh_vou_probe(struct platform_device *pdev) struct sh_vou_device *vou_dev; struct resource *reg_res; struct v4l2_subdev *subdev; + struct vb2_queue *q; int irq, ret; reg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -1331,11 +1213,12 @@ static int sh_vou_probe(struct platform_device *pdev) if (!vou_dev) return -ENOMEM; - INIT_LIST_HEAD(&vou_dev->queue); + INIT_LIST_HEAD(&vou_dev->buf_list); spin_lock_init(&vou_dev->lock); mutex_init(&vou_dev->fop_lock); vou_dev->pdata = vou_pdata; - vou_dev->status = SH_VOU_IDLE; + vou_dev->status = SH_VOU_INITIALISING; + vou_dev->pix_idx = 1; rect = &vou_dev->rect; pix = &vou_dev->pix; @@ -1349,7 +1232,7 @@ static int sh_vou_probe(struct platform_device *pdev) pix->width = VOU_MAX_IMAGE_WIDTH; pix->height = 480; pix->pixelformat = V4L2_PIX_FMT_NV16; - pix->field = V4L2_FIELD_NONE; + pix->field = V4L2_FIELD_INTERLACED; pix->bytesperline = VOU_MAX_IMAGE_WIDTH; pix->sizeimage = VOU_MAX_IMAGE_WIDTH * 2 * 480; pix->colorspace = V4L2_COLORSPACE_SMPTE170M; @@ -1378,6 +1261,30 @@ static int sh_vou_probe(struct platform_device *pdev) video_set_drvdata(vdev, vou_dev); + /* Initialize the vb2 queue */ + q = &vou_dev->queue; + q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_WRITE; + q->drv_priv = vou_dev; + q->buf_struct_size = sizeof(struct sh_vou_buffer); + q->ops = &sh_vou_qops; + q->mem_ops = &vb2_dma_contig_memops; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->min_buffers_needed = 2; + q->lock = &vou_dev->fop_lock; + ret = vb2_queue_init(q); + if (ret) + goto einitctx; + + vou_dev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); + if (IS_ERR(vou_dev->alloc_ctx)) { + dev_err(&pdev->dev, "Can't allocate buffer context"); + ret = PTR_ERR(vou_dev->alloc_ctx); + goto einitctx; + } + vdev->queue = q; + INIT_LIST_HEAD(&vou_dev->buf_list); + pm_runtime_enable(&pdev->dev); pm_runtime_resume(&pdev->dev); @@ -1409,6 +1316,8 @@ ei2cnd: ereset: i2c_put_adapter(i2c_adap); ei2cgadap: + vb2_dma_contig_cleanup_ctx(vou_dev->alloc_ctx); +einitctx: pm_runtime_disable(&pdev->dev); v4l2_device_unregister(&vou_dev->v4l2_dev); return ret; @@ -1426,6 +1335,7 @@ static int sh_vou_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); video_unregister_device(&vou_dev->vdev); i2c_put_adapter(client->adapter); + vb2_dma_contig_cleanup_ctx(vou_dev->alloc_ctx); v4l2_device_unregister(&vou_dev->v4l2_dev); return 0; } -- cgit v1.2.3 From 0b3474f0f61b21fc7f4d2a203cbcc2599a4d3aef Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 8 Jun 2015 03:20:15 -0300 Subject: [media] sh-vou: add support for log_status Dump the VOU registers in log_status. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sh_vou.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/drivers/media/platform/sh_vou.c b/drivers/media/platform/sh_vou.c index da8ea6a76327..e2b2afa0a73d 100644 --- a/drivers/media/platform/sh_vou.c +++ b/drivers/media/platform/sh_vou.c @@ -875,6 +875,33 @@ static int sh_vou_g_std(struct file *file, void *priv, v4l2_std_id *std) return 0; } +static int sh_vou_log_status(struct file *file, void *priv) +{ + struct sh_vou_device *vou_dev = video_drvdata(file); + + pr_info("VOUER: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUER)); + pr_info("VOUCR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUCR)); + pr_info("VOUSTR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUSTR)); + pr_info("VOUVCR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUVCR)); + pr_info("VOUISR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUISR)); + pr_info("VOUBCR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUBCR)); + pr_info("VOUDPR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUDPR)); + pr_info("VOUDSR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUDSR)); + pr_info("VOUVPR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUVPR)); + pr_info("VOUIR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUIR)); + pr_info("VOUSRR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUSRR)); + pr_info("VOUMSR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUMSR)); + pr_info("VOUHIR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUHIR)); + pr_info("VOUDFR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUDFR)); + pr_info("VOUAD1R: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUAD1R)); + pr_info("VOUAD2R: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUAD2R)); + pr_info("VOUAIR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUAIR)); + pr_info("VOUSWR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUSWR)); + pr_info("VOURCR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOURCR)); + pr_info("VOURPR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOURPR)); + return 0; +} + static int sh_vou_g_selection(struct file *file, void *fh, struct v4l2_selection *sel) { @@ -1168,6 +1195,7 @@ static const struct v4l2_ioctl_ops sh_vou_ioctl_ops = { .vidioc_g_std = sh_vou_g_std, .vidioc_g_selection = sh_vou_g_selection, .vidioc_s_selection = sh_vou_s_selection, + .vidioc_log_status = sh_vou_log_status, }; static const struct v4l2_file_operations sh_vou_fops = { -- cgit v1.2.3 From 4c34cc5e0f99ced4c7a11d49007bf7a90e2fae7a Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Fri, 12 Jun 2015 13:31:11 -0300 Subject: [media] media/i2c/sr030pc30: Remove compat control ops They are no longer used in old non-control-framework bridge drivers. Reported-by: Hans Verkuil Signed-off-by: Ricardo Ribalda Delgado Acked-by: Sylwester Nawrocki Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/sr030pc30.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/media/i2c/sr030pc30.c b/drivers/media/i2c/sr030pc30.c index b62b6ddc4356..229dc76c44a5 100644 --- a/drivers/media/i2c/sr030pc30.c +++ b/drivers/media/i2c/sr030pc30.c @@ -636,13 +636,6 @@ static const struct v4l2_ctrl_ops sr030pc30_ctrl_ops = { static const struct v4l2_subdev_core_ops sr030pc30_core_ops = { .s_power = sr030pc30_s_power, - .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_pad_ops sr030pc30_pad_ops = { -- cgit v1.2.3 From c046707ff99912878ba63db5f2f2e2916ca961ce Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 15 Jun 2015 08:33:41 -0300 Subject: [media] DocBook/media: fix bad spacing in VIDIOC_EXPBUF The VIDIOC_EXPBUF documentation had spurious spaces that made it irritating to read. Fix this. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/vidioc-expbuf.xml | 38 +++++++++++------------ 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/Documentation/DocBook/media/v4l/vidioc-expbuf.xml b/Documentation/DocBook/media/v4l/vidioc-expbuf.xml index a78c9207422f..0ae0b6a915d0 100644 --- a/Documentation/DocBook/media/v4l/vidioc-expbuf.xml +++ b/Documentation/DocBook/media/v4l/vidioc-expbuf.xml @@ -62,28 +62,28 @@ buffer as a DMABUF file at any time after buffers have been allocated with the &VIDIOC-REQBUFS; ioctl. To export a buffer, applications fill &v4l2-exportbuffer;. The - type field is set to the same buffer type as was -previously used with &v4l2-requestbuffers; type . -Applications must also set the index field. Valid +type field is set to the same buffer type as was +previously used with &v4l2-requestbuffers; type. +Applications must also set the index field. Valid index numbers range from zero to the number of buffers allocated with -&VIDIOC-REQBUFS; (&v4l2-requestbuffers; count ) -minus one. For the multi-planar API, applications set the plane - field to the index of the plane to be exported. Valid planes +&VIDIOC-REQBUFS; (&v4l2-requestbuffers; count) +minus one. For the multi-planar API, applications set the plane +field to the index of the plane to be exported. Valid planes range from zero to the maximal number of valid planes for the currently active -format. For the single-planar API, applications must set plane - to zero. Additional flags may be posted in the -flags field. Refer to a manual for open() for details. +format. For the single-planar API, applications must set plane +to zero. Additional flags may be posted in the flags +field. Refer to a manual for open() for details. Currently only O_CLOEXEC, O_RDONLY, O_WRONLY, and O_RDWR are supported. All other fields must be set to zero. In the case of multi-planar API, every plane is exported separately using -multiple VIDIOC_EXPBUF calls. +multiple VIDIOC_EXPBUF calls. - After calling VIDIOC_EXPBUF the fd - field will be set by a driver. This is a DMABUF file +After calling VIDIOC_EXPBUF the fd +field will be set by a driver. This is a DMABUF file descriptor. The application may pass it to other DMABUF-aware devices. Refer to DMABUF importing for details about importing DMABUF files into V4L2 nodes. It is recommended to close a DMABUF file when it -is no longer used to allow the associated memory to be reclaimed. +is no longer used to allow the associated memory to be reclaimed. @@ -170,9 +170,9 @@ multi-planar API. Otherwise this value must be set to zero. __u32 flags - Flags for the newly created file, currently only -O_CLOEXEC , O_RDONLY, O_WRONLY -, and O_RDWR are supported, refer to the manual + Flags for the newly created file, currently only +O_CLOEXEC, O_RDONLY, O_WRONLY, +and O_RDWR are supported, refer to the manual of open() for more details. @@ -200,9 +200,9 @@ set the array to zero. EINVAL A queue is not in MMAP mode or DMABUF exporting is not -supported or flags or type - or index or plane - fields are invalid. +supported or flags or type +or index or plane fields +are invalid. -- cgit v1.2.3 From 250121d348645675f5ade7e7ebe3fc6b46a9a2c0 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Wed, 3 Jun 2015 10:59:50 -0300 Subject: [media] media: adv7180: add of match table Add a proper of match id for use when the device is being bound via device tree, to avoid having to use the i2c old-style binding of the device. Signed-off-by: Ben Dooks Signed-off-by: William.Towle Reviewed-by: Rob Taylor Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv7180.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c index a493c0b0b5fe..09a96df170d1 100644 --- a/drivers/media/i2c/adv7180.c +++ b/drivers/media/i2c/adv7180.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -1324,11 +1325,21 @@ static SIMPLE_DEV_PM_OPS(adv7180_pm_ops, adv7180_suspend, adv7180_resume); #define ADV7180_PM_OPS NULL #endif +#ifdef CONFIG_OF +static const struct of_device_id adv7180_of_id[] = { + { .compatible = "adi,adv7180", }, + { }, +}; + +MODULE_DEVICE_TABLE(of, adv7180_of_id); +#endif + static struct i2c_driver adv7180_driver = { .driver = { .owner = THIS_MODULE, .name = KBUILD_MODNAME, .pm = ADV7180_PM_OPS, + .of_match_table = of_match_ptr(adv7180_of_id), }, .probe = adv7180_probe, .remove = adv7180_remove, -- cgit v1.2.3 From f862f57dae5f0555552256b67f5bfd523f97a736 Mon Sep 17 00:00:00 2001 From: Pablo Anton Date: Fri, 19 Jun 2015 10:23:06 -0300 Subject: [media] media: i2c: ADV7604: Migrate to regmap This is a preliminary patch in order to add support for ALSA. It replaces all current i2c access with regmap. Signed-off-by: Pablo Anton Signed-off-by: Jean-Michel Hautbois Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv7604.c | 351 ++++++++++++++++++++++++++++++++------------ 1 file changed, 256 insertions(+), 95 deletions(-) diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index 60630d62e663..8e39bf60cb16 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include @@ -188,6 +189,9 @@ struct adv76xx_state { /* i2c clients */ struct i2c_client *i2c_clients[ADV76XX_PAGE_MAX]; + /* Regmaps */ + struct regmap *regmap[ADV76XX_PAGE_MAX]; + /* controls */ struct v4l2_ctrl *detect_tx_5v_ctrl; struct v4l2_ctrl *analog_sampling_phase_ctrl; @@ -373,66 +377,39 @@ static inline unsigned vtotal(const struct v4l2_bt_timings *t) /* ----------------------------------------------------------------------- */ -static s32 adv_smbus_read_byte_data_check(struct i2c_client *client, - u8 command, bool check) -{ - union i2c_smbus_data data; - - if (!i2c_smbus_xfer(client->adapter, client->addr, client->flags, - I2C_SMBUS_READ, command, - I2C_SMBUS_BYTE_DATA, &data)) - return data.byte; - if (check) - v4l_err(client, "error reading %02x, %02x\n", - client->addr, command); - return -EIO; -} - -static s32 adv_smbus_read_byte_data(struct adv76xx_state *state, - enum adv76xx_page page, u8 command) +static int adv76xx_read_check(struct adv76xx_state *state, + int client_page, u8 reg) { - return adv_smbus_read_byte_data_check(state->i2c_clients[page], - command, true); -} - -static s32 adv_smbus_write_byte_data(struct adv76xx_state *state, - enum adv76xx_page page, u8 command, - u8 value) -{ - struct i2c_client *client = state->i2c_clients[page]; - union i2c_smbus_data data; + struct i2c_client *client = state->i2c_clients[client_page]; int err; - int i; + unsigned int val; - data.byte = value; - for (i = 0; i < 3; i++) { - err = i2c_smbus_xfer(client->adapter, client->addr, - client->flags, - I2C_SMBUS_WRITE, command, - I2C_SMBUS_BYTE_DATA, &data); - if (!err) - break; + err = regmap_read(state->regmap[client_page], reg, &val); + + if (err) { + v4l_err(client, "error reading %02x, %02x\n", + client->addr, reg); + return err; } - if (err < 0) - v4l_err(client, "error writing %02x, %02x, %02x\n", - client->addr, command, value); - return err; + return val; } -static s32 adv_smbus_write_i2c_block_data(struct adv76xx_state *state, - enum adv76xx_page page, u8 command, - unsigned length, const u8 *values) +/* adv76xx_write_block(): Write raw data with a maximum of I2C_SMBUS_BLOCK_MAX + * size to one or more registers. + * + * A value of zero will be returned on success, a negative errno will + * be returned in error cases. + */ +static int adv76xx_write_block(struct adv76xx_state *state, int client_page, + unsigned int init_reg, const void *val, + size_t val_len) { - struct i2c_client *client = state->i2c_clients[page]; - union i2c_smbus_data data; + struct regmap *regmap = state->regmap[client_page]; + + if (val_len > I2C_SMBUS_BLOCK_MAX) + val_len = I2C_SMBUS_BLOCK_MAX; - if (length > I2C_SMBUS_BLOCK_MAX) - length = I2C_SMBUS_BLOCK_MAX; - data.block[0] = length; - memcpy(data.block + 1, values, length); - return i2c_smbus_xfer(client->adapter, client->addr, client->flags, - I2C_SMBUS_WRITE, command, - I2C_SMBUS_I2C_BLOCK_DATA, &data); + return regmap_raw_write(regmap, init_reg, val, val_len); } /* ----------------------------------------------------------------------- */ @@ -441,14 +418,14 @@ static inline int io_read(struct v4l2_subdev *sd, u8 reg) { struct adv76xx_state *state = to_state(sd); - return adv_smbus_read_byte_data(state, ADV76XX_PAGE_IO, reg); + return adv76xx_read_check(state, ADV76XX_PAGE_IO, reg); } static inline int io_write(struct v4l2_subdev *sd, u8 reg, u8 val) { struct adv76xx_state *state = to_state(sd); - return adv_smbus_write_byte_data(state, ADV76XX_PAGE_IO, reg, val); + return regmap_write(state->regmap[ADV76XX_PAGE_IO], reg, val); } static inline int io_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) @@ -460,71 +437,70 @@ static inline int avlink_read(struct v4l2_subdev *sd, u8 reg) { struct adv76xx_state *state = to_state(sd); - return adv_smbus_read_byte_data(state, ADV7604_PAGE_AVLINK, reg); + return adv76xx_read_check(state, ADV7604_PAGE_AVLINK, reg); } static inline int avlink_write(struct v4l2_subdev *sd, u8 reg, u8 val) { struct adv76xx_state *state = to_state(sd); - return adv_smbus_write_byte_data(state, ADV7604_PAGE_AVLINK, reg, val); + return regmap_write(state->regmap[ADV7604_PAGE_AVLINK], reg, val); } static inline int cec_read(struct v4l2_subdev *sd, u8 reg) { struct adv76xx_state *state = to_state(sd); - return adv_smbus_read_byte_data(state, ADV76XX_PAGE_CEC, reg); + return adv76xx_read_check(state, ADV76XX_PAGE_CEC, reg); } static inline int cec_write(struct v4l2_subdev *sd, u8 reg, u8 val) { struct adv76xx_state *state = to_state(sd); - return adv_smbus_write_byte_data(state, ADV76XX_PAGE_CEC, reg, val); + return regmap_write(state->regmap[ADV76XX_PAGE_CEC], reg, val); } static inline int infoframe_read(struct v4l2_subdev *sd, u8 reg) { struct adv76xx_state *state = to_state(sd); - return adv_smbus_read_byte_data(state, ADV76XX_PAGE_INFOFRAME, reg); + return adv76xx_read_check(state, ADV76XX_PAGE_INFOFRAME, reg); } static inline int infoframe_write(struct v4l2_subdev *sd, u8 reg, u8 val) { struct adv76xx_state *state = to_state(sd); - return adv_smbus_write_byte_data(state, ADV76XX_PAGE_INFOFRAME, - reg, val); + return regmap_write(state->regmap[ADV76XX_PAGE_INFOFRAME], reg, val); } static inline int afe_read(struct v4l2_subdev *sd, u8 reg) { struct adv76xx_state *state = to_state(sd); - return adv_smbus_read_byte_data(state, ADV76XX_PAGE_AFE, reg); + return adv76xx_read_check(state, ADV76XX_PAGE_AFE, reg); } static inline int afe_write(struct v4l2_subdev *sd, u8 reg, u8 val) { struct adv76xx_state *state = to_state(sd); - return adv_smbus_write_byte_data(state, ADV76XX_PAGE_AFE, reg, val); + return regmap_write(state->regmap[ADV76XX_PAGE_AFE], reg, val); } static inline int rep_read(struct v4l2_subdev *sd, u8 reg) { struct adv76xx_state *state = to_state(sd); - return adv_smbus_read_byte_data(state, ADV76XX_PAGE_REP, reg); + return adv76xx_read_check(state, ADV76XX_PAGE_REP, reg); } static inline int rep_write(struct v4l2_subdev *sd, u8 reg, u8 val) { struct adv76xx_state *state = to_state(sd); - return adv_smbus_write_byte_data(state, ADV76XX_PAGE_REP, reg, val); + return regmap_write(state->regmap[ADV76XX_PAGE_REP], reg, val); } static inline int rep_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) @@ -536,28 +512,37 @@ static inline int edid_read(struct v4l2_subdev *sd, u8 reg) { struct adv76xx_state *state = to_state(sd); - return adv_smbus_read_byte_data(state, ADV76XX_PAGE_EDID, reg); + return adv76xx_read_check(state, ADV76XX_PAGE_EDID, reg); } static inline int edid_write(struct v4l2_subdev *sd, u8 reg, u8 val) { struct adv76xx_state *state = to_state(sd); - return adv_smbus_write_byte_data(state, ADV76XX_PAGE_EDID, reg, val); + return regmap_write(state->regmap[ADV76XX_PAGE_EDID], reg, val); } static inline int edid_write_block(struct v4l2_subdev *sd, - unsigned len, const u8 *val) + unsigned int total_len, const u8 *val) { struct adv76xx_state *state = to_state(sd); int err = 0; - int i; + int i = 0; + int len = 0; - v4l2_dbg(2, debug, sd, "%s: write EDID block (%d byte)\n", __func__, len); + v4l2_dbg(2, debug, sd, "%s: write EDID block (%d byte)\n", + __func__, total_len); + + while (!err && i < total_len) { + len = (total_len - i) > I2C_SMBUS_BLOCK_MAX ? + I2C_SMBUS_BLOCK_MAX : + (total_len - i); + + err = adv76xx_write_block(state, ADV76XX_PAGE_EDID, + i, val + i, len); + i += len; + } - for (i = 0; !err && i < len; i += I2C_SMBUS_BLOCK_MAX) - err = adv_smbus_write_i2c_block_data(state, ADV76XX_PAGE_EDID, - i, I2C_SMBUS_BLOCK_MAX, val + i); return err; } @@ -587,7 +572,7 @@ static inline int hdmi_read(struct v4l2_subdev *sd, u8 reg) { struct adv76xx_state *state = to_state(sd); - return adv_smbus_read_byte_data(state, ADV76XX_PAGE_HDMI, reg); + return adv76xx_read_check(state, ADV76XX_PAGE_HDMI, reg); } static u16 hdmi_read16(struct v4l2_subdev *sd, u8 reg, u16 mask) @@ -599,7 +584,7 @@ static inline int hdmi_write(struct v4l2_subdev *sd, u8 reg, u8 val) { struct adv76xx_state *state = to_state(sd); - return adv_smbus_write_byte_data(state, ADV76XX_PAGE_HDMI, reg, val); + return regmap_write(state->regmap[ADV76XX_PAGE_HDMI], reg, val); } static inline int hdmi_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) @@ -611,14 +596,14 @@ static inline int test_write(struct v4l2_subdev *sd, u8 reg, u8 val) { struct adv76xx_state *state = to_state(sd); - return adv_smbus_write_byte_data(state, ADV76XX_PAGE_TEST, reg, val); + return regmap_write(state->regmap[ADV76XX_PAGE_TEST], reg, val); } static inline int cp_read(struct v4l2_subdev *sd, u8 reg) { struct adv76xx_state *state = to_state(sd); - return adv_smbus_read_byte_data(state, ADV76XX_PAGE_CP, reg); + return adv76xx_read_check(state, ADV76XX_PAGE_CP, reg); } static u16 cp_read16(struct v4l2_subdev *sd, u8 reg, u16 mask) @@ -630,7 +615,7 @@ static inline int cp_write(struct v4l2_subdev *sd, u8 reg, u8 val) { struct adv76xx_state *state = to_state(sd); - return adv_smbus_write_byte_data(state, ADV76XX_PAGE_CP, reg, val); + return regmap_write(state->regmap[ADV76XX_PAGE_CP], reg, val); } static inline int cp_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) @@ -642,14 +627,14 @@ static inline int vdp_read(struct v4l2_subdev *sd, u8 reg) { struct adv76xx_state *state = to_state(sd); - return adv_smbus_read_byte_data(state, ADV7604_PAGE_VDP, reg); + return adv76xx_read_check(state, ADV7604_PAGE_VDP, reg); } static inline int vdp_write(struct v4l2_subdev *sd, u8 reg, u8 val) { struct adv76xx_state *state = to_state(sd); - return adv_smbus_write_byte_data(state, ADV7604_PAGE_VDP, reg, val); + return regmap_write(state->regmap[ADV7604_PAGE_VDP], reg, val); } #define ADV76XX_REG(page, offset) (((page) << 8) | (offset)) @@ -660,13 +645,16 @@ static int adv76xx_read_reg(struct v4l2_subdev *sd, unsigned int reg) { struct adv76xx_state *state = to_state(sd); unsigned int page = reg >> 8; + unsigned int val; + int err; if (!(BIT(page) & state->info->page_mask)) return -EINVAL; reg &= 0xff; + err = regmap_read(state->regmap[page], reg, &val); - return adv_smbus_read_byte_data(state, page, reg); + return err ? err : val; } #endif @@ -680,7 +668,7 @@ static int adv76xx_write_reg(struct v4l2_subdev *sd, unsigned int reg, u8 val) reg &= 0xff; - return adv_smbus_write_byte_data(state, page, reg, val); + return regmap_write(state->regmap[page], reg, val); } static void adv76xx_write_reg_seq(struct v4l2_subdev *sd, @@ -976,8 +964,8 @@ static void configure_custom_video_timings(struct v4l2_subdev *sd, /* Should only be set in auto-graphics mode [REF_02, p. 91-92] */ /* setup PLL_DIV_MAN_EN and PLL_DIV_RATIO */ /* IO-map reg. 0x16 and 0x17 should be written in sequence */ - if (adv_smbus_write_i2c_block_data(state, ADV76XX_PAGE_IO, - 0x16, 2, pll)) + if (regmap_raw_write(state->regmap[ADV76XX_PAGE_IO], + 0x16, pll, 2)) v4l2_err(sd, "writing to reg 0x16 and 0x17 failed\n"); /* active video - horizontal timing */ @@ -1028,8 +1016,8 @@ static void adv76xx_set_offset(struct v4l2_subdev *sd, bool auto_offset, u16 off offset_buf[3] = offset_c & 0x0ff; /* Registers must be written in this order with no i2c access in between */ - if (adv_smbus_write_i2c_block_data(state, ADV76XX_PAGE_CP, - 0x77, 4, offset_buf)) + if (regmap_raw_write(state->regmap[ADV76XX_PAGE_CP], + 0x77, offset_buf, 4)) v4l2_err(sd, "%s: i2c error writing to CP reg 0x77, 0x78, 0x79, 0x7a\n", __func__); } @@ -1058,8 +1046,8 @@ static void adv76xx_set_gain(struct v4l2_subdev *sd, bool auto_gain, u16 gain_a, gain_buf[3] = ((gain_c & 0x0ff)); /* Registers must be written in this order with no i2c access in between */ - if (adv_smbus_write_i2c_block_data(state, ADV76XX_PAGE_CP, - 0x73, 4, gain_buf)) + if (regmap_raw_write(state->regmap[ADV76XX_PAGE_CP], + 0x73, gain_buf, 4)) v4l2_err(sd, "%s: i2c error writing to CP reg 0x73, 0x74, 0x75, 0x76\n", __func__); } @@ -2762,6 +2750,148 @@ static int adv76xx_parse_dt(struct adv76xx_state *state) return 0; } +static const struct regmap_config adv76xx_regmap_cnf[] = { + { + .name = "io", + .reg_bits = 8, + .val_bits = 8, + + .max_register = 0xff, + .cache_type = REGCACHE_NONE, + }, + { + .name = "avlink", + .reg_bits = 8, + .val_bits = 8, + + .max_register = 0xff, + .cache_type = REGCACHE_NONE, + }, + { + .name = "cec", + .reg_bits = 8, + .val_bits = 8, + + .max_register = 0xff, + .cache_type = REGCACHE_NONE, + }, + { + .name = "infoframe", + .reg_bits = 8, + .val_bits = 8, + + .max_register = 0xff, + .cache_type = REGCACHE_NONE, + }, + { + .name = "esdp", + .reg_bits = 8, + .val_bits = 8, + + .max_register = 0xff, + .cache_type = REGCACHE_NONE, + }, + { + .name = "epp", + .reg_bits = 8, + .val_bits = 8, + + .max_register = 0xff, + .cache_type = REGCACHE_NONE, + }, + { + .name = "afe", + .reg_bits = 8, + .val_bits = 8, + + .max_register = 0xff, + .cache_type = REGCACHE_NONE, + }, + { + .name = "rep", + .reg_bits = 8, + .val_bits = 8, + + .max_register = 0xff, + .cache_type = REGCACHE_NONE, + }, + { + .name = "edid", + .reg_bits = 8, + .val_bits = 8, + + .max_register = 0xff, + .cache_type = REGCACHE_NONE, + }, + + { + .name = "hdmi", + .reg_bits = 8, + .val_bits = 8, + + .max_register = 0xff, + .cache_type = REGCACHE_NONE, + }, + { + .name = "test", + .reg_bits = 8, + .val_bits = 8, + + .max_register = 0xff, + .cache_type = REGCACHE_NONE, + }, + { + .name = "cp", + .reg_bits = 8, + .val_bits = 8, + + .max_register = 0xff, + .cache_type = REGCACHE_NONE, + }, + { + .name = "vdp", + .reg_bits = 8, + .val_bits = 8, + + .max_register = 0xff, + .cache_type = REGCACHE_NONE, + }, +}; + +static int configure_regmap(struct adv76xx_state *state, int region) +{ + int err; + + if (!state->i2c_clients[region]) + return -ENODEV; + + state->regmap[region] = + devm_regmap_init_i2c(state->i2c_clients[region], + &adv76xx_regmap_cnf[region]); + + if (IS_ERR(state->regmap[region])) { + err = PTR_ERR(state->regmap[region]); + v4l_err(state->i2c_clients[region], + "Error initializing regmap %d with error %d\n", + region, err); + return -EINVAL; + } + + return 0; +} + +static int configure_regmaps(struct adv76xx_state *state) +{ + int i, err; + + for (i = ADV7604_PAGE_AVLINK ; i < ADV76XX_PAGE_MAX; i++) { + err = configure_regmap(state, i); + if (err && (err != -ENODEV)) + return err; + } + return 0; +} + static int adv76xx_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -2771,7 +2901,7 @@ static int adv76xx_probe(struct i2c_client *client, struct v4l2_ctrl_handler *hdl; struct v4l2_subdev *sd; unsigned int i; - u16 val; + unsigned int val, val2; int err; /* Check if the adapter supports the needed features */ @@ -2835,23 +2965,49 @@ static int adv76xx_probe(struct i2c_client *client, client->addr); sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + /* Configure IO Regmap region */ + err = configure_regmap(state, ADV76XX_PAGE_IO); + + if (err) { + v4l2_err(sd, "Error configuring IO regmap region\n"); + return -ENODEV; + } + /* * Verify that the chip is present. On ADV7604 the RD_INFO register only * identifies the revision, while on ADV7611 it identifies the model as * well. Use the HDMI slave address on ADV7604 and RD_INFO on ADV7611. */ if (state->info->type == ADV7604) { - val = adv_smbus_read_byte_data_check(client, 0xfb, false); + err = regmap_read(state->regmap[ADV76XX_PAGE_IO], 0xfb, &val); + if (err) { + v4l2_err(sd, "Error %d reading IO Regmap\n", err); + return -ENODEV; + } if (val != 0x68) { - v4l2_info(sd, "not an adv7604 on address 0x%x\n", + v4l2_err(sd, "not an adv7604 on address 0x%x\n", client->addr << 1); return -ENODEV; } } else { - val = (adv_smbus_read_byte_data_check(client, 0xea, false) << 8) - | (adv_smbus_read_byte_data_check(client, 0xeb, false) << 0); - if (val != 0x2051) { - v4l2_info(sd, "not an adv7611 on address 0x%x\n", + err = regmap_read(state->regmap[ADV76XX_PAGE_IO], + 0xea, + &val); + if (err) { + v4l2_err(sd, "Error %d reading IO Regmap\n", err); + return -ENODEV; + } + val2 = val << 8; + err = regmap_read(state->regmap[ADV76XX_PAGE_IO], + 0xeb, + &val); + if (err) { + v4l2_err(sd, "Error %d reading IO Regmap\n", err); + return -ENODEV; + } + val2 |= val; + if (val2 != 0x2051) { + v4l2_err(sd, "not an adv7611 on address 0x%x\n", client->addr << 1); return -ENODEV; } @@ -2941,6 +3097,11 @@ static int adv76xx_probe(struct i2c_client *client, if (err) goto err_work_queues; + /* Configure regmaps */ + err = configure_regmaps(state); + if (err) + goto err_entity; + err = adv76xx_core_init(sd); if (err) goto err_entity; -- cgit v1.2.3 From 8331d30bf0ccf179c3d03d968c9ae1c8f06eafc4 Mon Sep 17 00:00:00 2001 From: William Towle Date: Wed, 3 Jun 2015 10:59:51 -0300 Subject: [media] media: adv7604: chip info and formats for ADV7612 Add support for the ADV7612 chip as implemented on Renesas' Lager board to adv7604.c, including lists for formats/colourspace/timing selection and an IRQ handler. Signed-off-by: William Towle Signed-off-by: Hans Verkuil [hans.verkuil@cisco.com: fix merge conflicts due to regmap patch] Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv7604.c | 91 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 87 insertions(+), 4 deletions(-) diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index 8e39bf60cb16..c8fefeab0513 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -82,6 +82,7 @@ MODULE_LICENSE("GPL"); enum adv76xx_type { ADV7604, ADV7611, + ADV7612, }; struct adv76xx_reg_seq { @@ -754,6 +755,23 @@ static const struct adv76xx_format_info adv7611_formats[] = { ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_12BIT }, }; +static const struct adv76xx_format_info adv7612_formats[] = { + { MEDIA_BUS_FMT_RGB888_1X24, ADV76XX_OP_CH_SEL_RGB, true, false, + ADV76XX_OP_MODE_SEL_SDR_444 | ADV76XX_OP_FORMAT_SEL_8BIT }, + { MEDIA_BUS_FMT_YUYV8_2X8, ADV76XX_OP_CH_SEL_RGB, false, false, + ADV76XX_OP_MODE_SEL_SDR_422 | ADV76XX_OP_FORMAT_SEL_8BIT }, + { MEDIA_BUS_FMT_YVYU8_2X8, ADV76XX_OP_CH_SEL_RGB, false, true, + ADV76XX_OP_MODE_SEL_SDR_422 | ADV76XX_OP_FORMAT_SEL_8BIT }, + { MEDIA_BUS_FMT_UYVY8_1X16, ADV76XX_OP_CH_SEL_RBG, false, false, + ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT }, + { MEDIA_BUS_FMT_VYUY8_1X16, ADV76XX_OP_CH_SEL_RBG, false, true, + ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT }, + { MEDIA_BUS_FMT_YUYV8_1X16, ADV76XX_OP_CH_SEL_RGB, false, false, + ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT }, + { MEDIA_BUS_FMT_YVYU8_1X16, ADV76XX_OP_CH_SEL_RGB, false, true, + ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT }, +}; + static const struct adv76xx_format_info * adv76xx_format_info(struct adv76xx_state *state, u32 code) { @@ -2498,6 +2516,11 @@ static void adv7611_setup_irqs(struct v4l2_subdev *sd) io_write(sd, 0x41, 0xd0); /* STDI irq for any change, disable INT2 */ } +static void adv7612_setup_irqs(struct v4l2_subdev *sd) +{ + io_write(sd, 0x41, 0xd0); /* disable INT2 */ +} + static void adv76xx_unregister_clients(struct adv76xx_state *state) { unsigned int i; @@ -2585,6 +2608,19 @@ static const struct adv76xx_reg_seq adv7611_recommended_settings_hdmi[] = { { ADV76XX_REG_SEQ_TERM, 0 }, }; +static const struct adv76xx_reg_seq adv7612_recommended_settings_hdmi[] = { + { ADV76XX_REG(ADV76XX_PAGE_CP, 0x6c), 0x00 }, + { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x9b), 0x03 }, + { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x6f), 0x08 }, + { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x85), 0x1f }, + { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x87), 0x70 }, + { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x57), 0xda }, + { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x58), 0x01 }, + { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x03), 0x98 }, + { ADV76XX_REG(ADV76XX_PAGE_HDMI, 0x4c), 0x44 }, + { ADV76XX_REG_SEQ_TERM, 0 }, +}; + static const struct adv76xx_chip_info adv76xx_chip_info[] = { [ADV7604] = { .type = ADV7604, @@ -2673,17 +2709,59 @@ static const struct adv76xx_chip_info adv76xx_chip_info[] = { .field1_vsync_mask = 0x3fff, .field1_vbackporch_mask = 0x3fff, }, + [ADV7612] = { + .type = ADV7612, + .has_afe = false, + .max_port = ADV7604_PAD_HDMI_PORT_B, + .num_dv_ports = 2, + .edid_enable_reg = 0x74, + .edid_status_reg = 0x76, + .lcf_reg = 0xa3, + .tdms_lock_mask = 0x43, + .cable_det_mask = 0x01, + .fmt_change_digital_mask = 0x03, + .formats = adv7612_formats, + .nformats = ARRAY_SIZE(adv7612_formats), + .set_termination = adv7611_set_termination, + .setup_irqs = adv7612_setup_irqs, + .read_hdmi_pixelclock = adv7611_read_hdmi_pixelclock, + .read_cable_det = adv7611_read_cable_det, + .recommended_settings = { + [1] = adv7612_recommended_settings_hdmi, + }, + .num_recommended_settings = { + [1] = ARRAY_SIZE(adv7612_recommended_settings_hdmi), + }, + .page_mask = BIT(ADV76XX_PAGE_IO) | BIT(ADV76XX_PAGE_CEC) | + BIT(ADV76XX_PAGE_INFOFRAME) | BIT(ADV76XX_PAGE_AFE) | + BIT(ADV76XX_PAGE_REP) | BIT(ADV76XX_PAGE_EDID) | + BIT(ADV76XX_PAGE_HDMI) | BIT(ADV76XX_PAGE_CP), + .linewidth_mask = 0x1fff, + .field0_height_mask = 0x1fff, + .field1_height_mask = 0x1fff, + .hfrontporch_mask = 0x1fff, + .hsync_mask = 0x1fff, + .hbackporch_mask = 0x1fff, + .field0_vfrontporch_mask = 0x3fff, + .field0_vsync_mask = 0x3fff, + .field0_vbackporch_mask = 0x3fff, + .field1_vfrontporch_mask = 0x3fff, + .field1_vsync_mask = 0x3fff, + .field1_vbackporch_mask = 0x3fff, + }, }; static const struct i2c_device_id adv76xx_i2c_id[] = { { "adv7604", (kernel_ulong_t)&adv76xx_chip_info[ADV7604] }, { "adv7611", (kernel_ulong_t)&adv76xx_chip_info[ADV7611] }, + { "adv7612", (kernel_ulong_t)&adv76xx_chip_info[ADV7612] }, { } }; MODULE_DEVICE_TABLE(i2c, adv76xx_i2c_id); static const struct of_device_id adv76xx_of_id[] __maybe_unused = { { .compatible = "adi,adv7611", .data = &adv76xx_chip_info[ADV7611] }, + { .compatible = "adi,adv7612", .data = &adv76xx_chip_info[ADV7612] }, { } }; MODULE_DEVICE_TABLE(of, adv76xx_of_id); @@ -2978,7 +3056,8 @@ static int adv76xx_probe(struct i2c_client *client, * identifies the revision, while on ADV7611 it identifies the model as * well. Use the HDMI slave address on ADV7604 and RD_INFO on ADV7611. */ - if (state->info->type == ADV7604) { + switch (state->info->type) { + case ADV7604: err = regmap_read(state->regmap[ADV76XX_PAGE_IO], 0xfb, &val); if (err) { v4l2_err(sd, "Error %d reading IO Regmap\n", err); @@ -2989,7 +3068,9 @@ static int adv76xx_probe(struct i2c_client *client, client->addr << 1); return -ENODEV; } - } else { + break; + case ADV7611: + case ADV7612: err = regmap_read(state->regmap[ADV76XX_PAGE_IO], 0xea, &val); @@ -3006,11 +3087,13 @@ static int adv76xx_probe(struct i2c_client *client, return -ENODEV; } val2 |= val; - if (val2 != 0x2051) { - v4l2_err(sd, "not an adv7611 on address 0x%x\n", + if ((state->info->type == ADV7611 && val != 0x2051) || + (state->info->type == ADV7612 && val != 0x2041)) { + v4l2_err(sd, "not an adv761x on address 0x%x\n", client->addr << 1); return -ENODEV; } + break; } /* control handlers */ -- cgit v1.2.3 From 6a219f15a86812a226d197aa93b2806e9cecda7c Mon Sep 17 00:00:00 2001 From: Ian Molton Date: Wed, 3 Jun 2015 10:59:52 -0300 Subject: [media] media: adv7604: document support for ADV7612 dual HDMI input decoder This documentation accompanies the patch adding support for the ADV7612 dual HDMI decoder / repeater chip. Signed-off-by: Ian Molton Reviewed-by: William Towle Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../devicetree/bindings/media/i2c/adv7604.txt | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/Documentation/devicetree/bindings/media/i2c/adv7604.txt b/Documentation/devicetree/bindings/media/i2c/adv7604.txt index c27cede3bd68..7eafdbc055f9 100644 --- a/Documentation/devicetree/bindings/media/i2c/adv7604.txt +++ b/Documentation/devicetree/bindings/media/i2c/adv7604.txt @@ -1,15 +1,17 @@ -* Analog Devices ADV7604/11 video decoder with HDMI receiver +* Analog Devices ADV7604/11/12 video decoder with HDMI receiver -The ADV7604 and ADV7611 are multiformat video decoders with an integrated HDMI -receiver. The ADV7604 has four multiplexed HDMI inputs and one analog input, -and the ADV7611 has one HDMI input and no analog input. +The ADV7604 and ADV7611/12 are multiformat video decoders with an integrated +HDMI receiver. The ADV7604 has four multiplexed HDMI inputs and one analog +input, and the ADV7611 has one HDMI input and no analog input. The 7612 is +similar to the 7611 but has 2 HDMI inputs. -These device tree bindings support the ADV7611 only at the moment. +These device tree bindings support the ADV7611/12 only at the moment. Required Properties: - compatible: Must contain one of the following - "adi,adv7611" for the ADV7611 + - "adi,adv7612" for the ADV7612 - reg: I2C slave address @@ -22,10 +24,10 @@ port, in accordance with the video interface bindings defined in Documentation/devicetree/bindings/media/video-interfaces.txt. The port nodes are numbered as follows. - Port ADV7611 + Port ADV7611 ADV7612 ------------------------------------------------------------ - HDMI 0 - Digital output 1 + HDMI 0 0, 1 + Digital output 1 2 The digital output port node must contain at least one endpoint. -- cgit v1.2.3 From bf9c82278c348eb6b72496de6a3e5269aadb6b34 Mon Sep 17 00:00:00 2001 From: Ian Molton Date: Wed, 3 Jun 2015 10:59:53 -0300 Subject: [media] media: adv7604: ability to read default input port from DT Adds support to the adv7604 driver for specifying the default input port in the Device tree. If no value is provided, the driver will be unable to select an input without help from userspace. Tested-by: William Towle Signed-off-by: Ian Molton Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/devicetree/bindings/media/i2c/adv7604.txt | 3 +++ drivers/media/i2c/adv7604.c | 8 +++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/media/i2c/adv7604.txt b/Documentation/devicetree/bindings/media/i2c/adv7604.txt index 7eafdbc055f9..8337f75c75da 100644 --- a/Documentation/devicetree/bindings/media/i2c/adv7604.txt +++ b/Documentation/devicetree/bindings/media/i2c/adv7604.txt @@ -47,6 +47,7 @@ Optional Endpoint Properties: If none of hsync-active, vsync-active and pclk-sample is specified the endpoint will use embedded BT.656 synchronization. + - default-input: Select which input is selected after reset. Example: @@ -60,6 +61,8 @@ Example: #address-cells = <1>; #size-cells = <0>; + default-input = <0>; + port@0 { reg = <0>; }; diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index c8fefeab0513..21b549a8dc74 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -2772,6 +2772,7 @@ static int adv76xx_parse_dt(struct adv76xx_state *state) struct device_node *endpoint; struct device_node *np; unsigned int flags; + u32 v; np = state->i2c_clients[ADV76XX_PAGE_IO]->dev.of_node; @@ -2781,6 +2782,12 @@ static int adv76xx_parse_dt(struct adv76xx_state *state) return -EINVAL; v4l2_of_parse_endpoint(endpoint, &bus_cfg); + + if (!of_property_read_u32(endpoint, "default-input", &v)) + state->pdata.default_input = v; + else + state->pdata.default_input = -1; + of_node_put(endpoint); flags = bus_cfg.bus.parallel.flags; @@ -2819,7 +2826,6 @@ static int adv76xx_parse_dt(struct adv76xx_state *state) /* Hardcode the remaining platform data fields. */ state->pdata.disable_pwrdnb = 0; state->pdata.disable_cable_det_rst = 0; - state->pdata.default_input = -1; state->pdata.blank_data = 1; state->pdata.alt_data_sat = 1; state->pdata.op_format_mode_sel = ADV7604_OP_FORMAT_MODE0; -- cgit v1.2.3 From 2a1e91a1595f4dec14ab16d63c6348d8fa159911 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 3 Jul 2015 09:12:54 -0300 Subject: [media] sh_vou: declare static functions as such drivers/media/platform/sh_vou.c:799:5: warning: no previous prototype for 'sh_vou_g_output' [-Wmissing-prototypes] int sh_vou_g_output(struct file *file, void *fh, unsigned int *i) ^ drivers/media/platform/sh_vou.c:805:5: warning: no previous prototype for 'sh_vou_s_output' [-Wmissing-prototypes] int sh_vou_s_output(struct file *file, void *fh, unsigned int i) ^ Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sh_vou.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/sh_vou.c b/drivers/media/platform/sh_vou.c index e2b2afa0a73d..fe5c8ab06bd5 100644 --- a/drivers/media/platform/sh_vou.c +++ b/drivers/media/platform/sh_vou.c @@ -796,13 +796,13 @@ static int sh_vou_enum_output(struct file *file, void *fh, return 0; } -int sh_vou_g_output(struct file *file, void *fh, unsigned int *i) +static int sh_vou_g_output(struct file *file, void *fh, unsigned int *i) { *i = 0; return 0; } -int sh_vou_s_output(struct file *file, void *fh, unsigned int i) +static int sh_vou_s_output(struct file *file, void *fh, unsigned int i) { return i ? -EINVAL : 0; } -- cgit v1.2.3 From a66b0c41ad277ae62a3ae6ac430a71882f899557 Mon Sep 17 00:00:00 2001 From: David Härdeman Date: Tue, 19 May 2015 19:03:12 -0300 Subject: [media] rc-core: fix remove uevent generation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The input_dev is already gone when the rc device is being unregistered so checking for its presence only means that no remove uevent will be generated. Cc: stable@kernel.org Signed-off-by: David Härdeman Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/rc-main.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index 9d015db65280..84d142bdb7a2 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -1193,9 +1193,6 @@ static int rc_dev_uevent(struct device *device, struct kobj_uevent_env *env) { struct rc_dev *dev = to_rc_dev(device); - if (!dev || !dev->input_dev) - return -ENODEV; - if (dev->rc_map.name) ADD_HOTPLUG_VAR("NAME=%s", dev->rc_map.name); if (dev->driver_name) -- cgit v1.2.3 From fcb13097867757d360d5226d36ed3ffe849dc3ae Mon Sep 17 00:00:00 2001 From: David Härdeman Date: Tue, 19 May 2015 19:03:17 -0300 Subject: [media] rc-core: use an IDA rather than a bitmap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch changes rc-core to use the kernel facilities that are already available for handling unique numbers instead of rolling its own bitmap stuff. Signed-off-by: David Härdeman Tested-by: Stefan Lippers-Hollmann Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/rc-ir-raw.c | 2 +- drivers/media/rc/rc-main.c | 40 ++++++++++++++++++++-------------------- include/media/rc-core.h | 4 ++-- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/drivers/media/rc/rc-ir-raw.c b/drivers/media/rc/rc-ir-raw.c index b9e4645c731c..1068f2ba56c6 100644 --- a/drivers/media/rc/rc-ir-raw.c +++ b/drivers/media/rc/rc-ir-raw.c @@ -406,7 +406,7 @@ int ir_raw_event_register(struct rc_dev *dev) spin_lock_init(&dev->raw->lock); dev->raw->thread = kthread_run(ir_raw_event_thread, dev->raw, - "rc%ld", dev->devno); + "rc%u", dev->minor); if (IS_ERR(dev->raw->thread)) { rc = PTR_ERR(dev->raw->thread); diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index 84d142bdb7a2..20914edd5a10 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -18,17 +18,15 @@ #include #include #include +#include #include #include #include "rc-core-priv.h" -/* Bitmap to store allocated device numbers from 0 to IRRCV_NUM_DEVICES - 1 */ -#define IRRCV_NUM_DEVICES 256 -static DECLARE_BITMAP(ir_core_dev_number, IRRCV_NUM_DEVICES); - /* Sizes are in bytes, 256 bytes allows for 32 entries on x64 */ #define IR_TAB_MIN_SIZE 256 #define IR_TAB_MAX_SIZE 8192 +#define RC_DEV_MAX 256 /* FIXME: IR_KEYPRESS_TIMEOUT should be protocol specific */ #define IR_KEYPRESS_TIMEOUT 250 @@ -38,6 +36,9 @@ static LIST_HEAD(rc_map_list); static DEFINE_SPINLOCK(rc_map_lock); static struct led_trigger *led_feedback; +/* Used to keep track of rc devices */ +static DEFINE_IDA(rc_ida); + static struct rc_map_list *seek_rc_map(const char *name) { struct rc_map_list *map = NULL; @@ -1311,7 +1312,9 @@ int rc_register_device(struct rc_dev *dev) static bool raw_init = false; /* raw decoders loaded? */ struct rc_map *rc_map; const char *path; - int rc, devno, attr = 0; + int attr = 0; + int minor; + int rc; if (!dev || !dev->map_name) return -EINVAL; @@ -1331,13 +1334,13 @@ int rc_register_device(struct rc_dev *dev) if (dev->close) dev->input_dev->close = ir_close; - do { - devno = find_first_zero_bit(ir_core_dev_number, - IRRCV_NUM_DEVICES); - /* No free device slots */ - if (devno >= IRRCV_NUM_DEVICES) - return -ENOMEM; - } while (test_and_set_bit(devno, ir_core_dev_number)); + minor = ida_simple_get(&rc_ida, 0, RC_DEV_MAX, GFP_KERNEL); + if (minor < 0) + return minor; + + dev->minor = minor; + dev_set_name(&dev->dev, "rc%u", dev->minor); + dev_set_drvdata(&dev->dev, dev); dev->dev.groups = dev->sysfs_groups; dev->sysfs_groups[attr++] = &rc_dev_protocol_attr_grp; @@ -1357,9 +1360,6 @@ int rc_register_device(struct rc_dev *dev) */ mutex_lock(&dev->lock); - dev->devno = devno; - dev_set_name(&dev->dev, "rc%ld", dev->devno); - dev_set_drvdata(&dev->dev, dev); rc = device_add(&dev->dev); if (rc) goto out_unlock; @@ -1435,8 +1435,8 @@ int rc_register_device(struct rc_dev *dev) mutex_unlock(&dev->lock); - IR_dprintk(1, "Registered rc%ld (driver: %s, remote: %s, mode %s)\n", - dev->devno, + IR_dprintk(1, "Registered rc%u (driver: %s, remote: %s, mode %s)\n", + dev->minor, dev->driver_name ? dev->driver_name : "unknown", rc_map->name ? rc_map->name : "unknown", dev->driver_type == RC_DRIVER_IR_RAW ? "raw" : "cooked"); @@ -1455,7 +1455,7 @@ out_dev: device_del(&dev->dev); out_unlock: mutex_unlock(&dev->lock); - clear_bit(dev->devno, ir_core_dev_number); + ida_simple_remove(&rc_ida, minor); return rc; } EXPORT_SYMBOL_GPL(rc_register_device); @@ -1467,8 +1467,6 @@ void rc_unregister_device(struct rc_dev *dev) del_timer_sync(&dev->timer_keyup); - clear_bit(dev->devno, ir_core_dev_number); - if (dev->driver_type == RC_DRIVER_IR_RAW) ir_raw_event_unregister(dev); @@ -1481,6 +1479,8 @@ void rc_unregister_device(struct rc_dev *dev) device_del(&dev->dev); + ida_simple_remove(&rc_ida, dev->minor); + rc_free_device(dev); } diff --git a/include/media/rc-core.h b/include/media/rc-core.h index 45534da57759..5642fbea886e 100644 --- a/include/media/rc-core.h +++ b/include/media/rc-core.h @@ -69,7 +69,7 @@ enum rc_filter_type { * @rc_map: current scan/key table * @lock: used to ensure we've filled in all protocol details before * anyone can call show_protocols or store_protocols - * @devno: unique remote control device number + * @minor: unique minor remote control device number * @raw: additional data for raw pulse/space devices * @input_dev: the input child device used to communicate events to userspace * @driver_type: specifies if protocol decoding is done in hardware or software @@ -131,7 +131,7 @@ struct rc_dev { const char *map_name; struct rc_map rc_map; struct mutex lock; - unsigned long devno; + unsigned int minor; struct ir_raw_event_ctrl *raw; struct input_dev *input_dev; enum rc_driver_type driver_type; -- cgit v1.2.3 From 275ddb40bcf686d210d86c6718e42425a6a0bc76 Mon Sep 17 00:00:00 2001 From: David Härdeman Date: Tue, 19 May 2015 19:03:22 -0300 Subject: [media] rc-core: remove the LIRC "protocol" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The LIRC protocol was always a bad fit and if we're ever going to expose protocol numbers in a user-space API, it'd be better to get rid of the LIRC "protocol" first. The sysfs API is kept backwards compatible by always listing the lirc protocol as present and enabled. Signed-off-by: David Härdeman Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/ir-lirc-codec.c | 5 +---- drivers/media/rc/keymaps/rc-lirc.c | 2 +- drivers/media/rc/rc-main.c | 14 +++++++++----- include/media/rc-map.h | 38 ++++++++++++++++++-------------------- 4 files changed, 29 insertions(+), 30 deletions(-) diff --git a/drivers/media/rc/ir-lirc-codec.c b/drivers/media/rc/ir-lirc-codec.c index 98893a8332c7..a32659fcd266 100644 --- a/drivers/media/rc/ir-lirc-codec.c +++ b/drivers/media/rc/ir-lirc-codec.c @@ -35,9 +35,6 @@ static int ir_lirc_decode(struct rc_dev *dev, struct ir_raw_event ev) struct lirc_codec *lirc = &dev->raw->lirc; int sample; - if (!(dev->enabled_protocols & RC_BIT_LIRC)) - return 0; - if (!dev->raw->lirc.drv || !dev->raw->lirc.drv->rbuf) return -EINVAL; @@ -424,7 +421,7 @@ static int ir_lirc_unregister(struct rc_dev *dev) } static struct ir_raw_handler lirc_handler = { - .protocols = RC_BIT_LIRC, + .protocols = 0, .decode = ir_lirc_decode, .raw_register = ir_lirc_register, .raw_unregister = ir_lirc_unregister, diff --git a/drivers/media/rc/keymaps/rc-lirc.c b/drivers/media/rc/keymaps/rc-lirc.c index fbf08fa6f46e..e172f5db5803 100644 --- a/drivers/media/rc/keymaps/rc-lirc.c +++ b/drivers/media/rc/keymaps/rc-lirc.c @@ -20,7 +20,7 @@ static struct rc_map_list lirc_map = { .map = { .scan = lirc, .size = ARRAY_SIZE(lirc), - .rc_type = RC_TYPE_LIRC, + .rc_type = RC_TYPE_OTHER, .name = RC_MAP_LIRC, } }; diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index 20914edd5a10..c80816561181 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -800,7 +800,6 @@ static struct { { RC_BIT_SANYO, "sanyo" }, { RC_BIT_SHARP, "sharp" }, { RC_BIT_MCE_KBD, "mce_kbd" }, - { RC_BIT_LIRC, "lirc" }, { RC_BIT_XMP, "xmp" }, }; @@ -885,6 +884,9 @@ static ssize_t show_protocols(struct device *device, allowed &= ~proto_names[i].type; } + if (dev->driver_type == RC_DRIVER_IR_RAW) + tmp += sprintf(tmp, "[lirc] "); + if (tmp != buf) tmp--; *tmp = '\n'; @@ -936,8 +938,12 @@ static int parse_protocol_change(u64 *protocols, const char *buf) } if (i == ARRAY_SIZE(proto_names)) { - IR_dprintk(1, "Unknown protocol: '%s'\n", tmp); - return -EINVAL; + if (!strcasecmp(tmp, "lirc")) + mask = 0; + else { + IR_dprintk(1, "Unknown protocol: '%s'\n", tmp); + return -EINVAL; + } } count++; @@ -1425,8 +1431,6 @@ int rc_register_device(struct rc_dev *dev) if (dev->change_protocol) { u64 rc_type = (1ll << rc_map->rc_type); - if (dev->driver_type == RC_DRIVER_IR_RAW) - rc_type |= RC_BIT_LIRC; rc = dev->change_protocol(dev, &rc_type); if (rc < 0) goto out_raw; diff --git a/include/media/rc-map.h b/include/media/rc-map.h index 27763d5bd261..7c4bbc4dfab4 100644 --- a/include/media/rc-map.h +++ b/include/media/rc-map.h @@ -14,30 +14,28 @@ enum rc_type { RC_TYPE_UNKNOWN = 0, /* Protocol not known */ RC_TYPE_OTHER = 1, /* Protocol known but proprietary */ - RC_TYPE_LIRC = 2, /* Pass raw IR to lirc userspace */ - RC_TYPE_RC5 = 3, /* Philips RC5 protocol */ - RC_TYPE_RC5X = 4, /* Philips RC5x protocol */ - RC_TYPE_RC5_SZ = 5, /* StreamZap variant of RC5 */ - RC_TYPE_JVC = 6, /* JVC protocol */ - RC_TYPE_SONY12 = 7, /* Sony 12 bit protocol */ - RC_TYPE_SONY15 = 8, /* Sony 15 bit protocol */ - RC_TYPE_SONY20 = 9, /* Sony 20 bit protocol */ - RC_TYPE_NEC = 10, /* NEC protocol */ - RC_TYPE_SANYO = 11, /* Sanyo protocol */ - RC_TYPE_MCE_KBD = 12, /* RC6-ish MCE keyboard/mouse */ - RC_TYPE_RC6_0 = 13, /* Philips RC6-0-16 protocol */ - RC_TYPE_RC6_6A_20 = 14, /* Philips RC6-6A-20 protocol */ - RC_TYPE_RC6_6A_24 = 15, /* Philips RC6-6A-24 protocol */ - RC_TYPE_RC6_6A_32 = 16, /* Philips RC6-6A-32 protocol */ - RC_TYPE_RC6_MCE = 17, /* MCE (Philips RC6-6A-32 subtype) protocol */ - RC_TYPE_SHARP = 18, /* Sharp protocol */ - RC_TYPE_XMP = 19, /* XMP protocol */ + RC_TYPE_RC5 = 2, /* Philips RC5 protocol */ + RC_TYPE_RC5X = 3, /* Philips RC5x protocol */ + RC_TYPE_RC5_SZ = 4, /* StreamZap variant of RC5 */ + RC_TYPE_JVC = 5, /* JVC protocol */ + RC_TYPE_SONY12 = 6, /* Sony 12 bit protocol */ + RC_TYPE_SONY15 = 7, /* Sony 15 bit protocol */ + RC_TYPE_SONY20 = 8, /* Sony 20 bit protocol */ + RC_TYPE_NEC = 9, /* NEC protocol */ + RC_TYPE_SANYO = 10, /* Sanyo protocol */ + RC_TYPE_MCE_KBD = 11, /* RC6-ish MCE keyboard/mouse */ + RC_TYPE_RC6_0 = 12, /* Philips RC6-0-16 protocol */ + RC_TYPE_RC6_6A_20 = 13, /* Philips RC6-6A-20 protocol */ + RC_TYPE_RC6_6A_24 = 14, /* Philips RC6-6A-24 protocol */ + RC_TYPE_RC6_6A_32 = 15, /* Philips RC6-6A-32 protocol */ + RC_TYPE_RC6_MCE = 16, /* MCE (Philips RC6-6A-32 subtype) protocol */ + RC_TYPE_SHARP = 17, /* Sharp protocol */ + RC_TYPE_XMP = 18, /* XMP protocol */ }; #define RC_BIT_NONE 0 #define RC_BIT_UNKNOWN (1 << RC_TYPE_UNKNOWN) #define RC_BIT_OTHER (1 << RC_TYPE_OTHER) -#define RC_BIT_LIRC (1 << RC_TYPE_LIRC) #define RC_BIT_RC5 (1 << RC_TYPE_RC5) #define RC_BIT_RC5X (1 << RC_TYPE_RC5X) #define RC_BIT_RC5_SZ (1 << RC_TYPE_RC5_SZ) @@ -56,7 +54,7 @@ enum rc_type { #define RC_BIT_SHARP (1 << RC_TYPE_SHARP) #define RC_BIT_XMP (1 << RC_TYPE_XMP) -#define RC_BIT_ALL (RC_BIT_UNKNOWN | RC_BIT_OTHER | RC_BIT_LIRC | \ +#define RC_BIT_ALL (RC_BIT_UNKNOWN | RC_BIT_OTHER | \ RC_BIT_RC5 | RC_BIT_RC5X | RC_BIT_RC5_SZ | \ RC_BIT_JVC | \ RC_BIT_SONY12 | RC_BIT_SONY15 | RC_BIT_SONY20 | \ -- cgit v1.2.3 From f459aec2bc81a46b674901424295f8ffe5e29ad0 Mon Sep 17 00:00:00 2001 From: David Härdeman Date: Tue, 19 May 2015 19:03:27 -0300 Subject: [media] lmedm04: NEC scancode cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This changes the keymap back to the state before commit 616a4b83 and changes the driver to use full NEC32 scancodes following the instructions provided by Malcolm Priestley . Signed-off-by: David Härdeman Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/keymaps/rc-lme2510.c | 132 ++++++++++++++++----------------- drivers/media/usb/dvb-usb-v2/lmedm04.c | 21 +++--- 2 files changed, 77 insertions(+), 76 deletions(-) diff --git a/drivers/media/rc/keymaps/rc-lme2510.c b/drivers/media/rc/keymaps/rc-lme2510.c index 51f18bb50a37..2b0027c41332 100644 --- a/drivers/media/rc/keymaps/rc-lme2510.c +++ b/drivers/media/rc/keymaps/rc-lme2510.c @@ -15,74 +15,74 @@ static struct rc_map_table lme2510_rc[] = { /* Type 1 - 26 buttons */ - { 0x10ed45, KEY_0 }, - { 0x10ed5f, KEY_1 }, - { 0x10ed50, KEY_2 }, - { 0x10ed5d, KEY_3 }, - { 0x10ed41, KEY_4 }, - { 0x10ed0a, KEY_5 }, - { 0x10ed42, KEY_6 }, - { 0x10ed47, KEY_7 }, - { 0x10ed49, KEY_8 }, - { 0x10ed05, KEY_9 }, - { 0x10ed43, KEY_POWER }, - { 0x10ed46, KEY_SUBTITLE }, - { 0x10ed06, KEY_PAUSE }, - { 0x10ed03, KEY_MEDIA_REPEAT}, - { 0x10ed02, KEY_PAUSE }, - { 0x10ed5e, KEY_VOLUMEUP }, - { 0x10ed5c, KEY_VOLUMEDOWN }, - { 0x10ed09, KEY_CHANNELUP }, - { 0x10ed1a, KEY_CHANNELDOWN }, - { 0x10ed1e, KEY_PLAY }, - { 0x10ed1b, KEY_ZOOM }, - { 0x10ed59, KEY_MUTE }, - { 0x10ed5a, KEY_TV }, - { 0x10ed18, KEY_RECORD }, - { 0x10ed07, KEY_EPG }, - { 0x10ed01, KEY_STOP }, + { 0xef12ba45, KEY_0 }, + { 0xef12a05f, KEY_1 }, + { 0xef12af50, KEY_2 }, + { 0xef12a25d, KEY_3 }, + { 0xef12be41, KEY_4 }, + { 0xef12f50a, KEY_5 }, + { 0xef12bd42, KEY_6 }, + { 0xef12b847, KEY_7 }, + { 0xef12b649, KEY_8 }, + { 0xef12fa05, KEY_9 }, + { 0xef12bc43, KEY_POWER }, + { 0xef12b946, KEY_SUBTITLE }, + { 0xef12f906, KEY_PAUSE }, + { 0xef12fc03, KEY_MEDIA_REPEAT}, + { 0xef12fd02, KEY_PAUSE }, + { 0xef12a15e, KEY_VOLUMEUP }, + { 0xef12a35c, KEY_VOLUMEDOWN }, + { 0xef12f609, KEY_CHANNELUP }, + { 0xef12e51a, KEY_CHANNELDOWN }, + { 0xef12e11e, KEY_PLAY }, + { 0xef12e41b, KEY_ZOOM }, + { 0xef12a659, KEY_MUTE }, + { 0xef12a55a, KEY_TV }, + { 0xef12e718, KEY_RECORD }, + { 0xef12f807, KEY_EPG }, + { 0xef12fe01, KEY_STOP }, /* Type 2 - 20 buttons */ - { 0xbf15, KEY_0 }, - { 0xbf08, KEY_1 }, - { 0xbf09, KEY_2 }, - { 0xbf0a, KEY_3 }, - { 0xbf0c, KEY_4 }, - { 0xbf0d, KEY_5 }, - { 0xbf0e, KEY_6 }, - { 0xbf10, KEY_7 }, - { 0xbf11, KEY_8 }, - { 0xbf12, KEY_9 }, - { 0xbf00, KEY_POWER }, - { 0xbf04, KEY_MEDIA_REPEAT}, /* Recall */ - { 0xbf1a, KEY_PAUSE }, /* Timeshift */ - { 0xbf02, KEY_VOLUMEUP }, /* 2 x -/+ Keys not marked */ - { 0xbf06, KEY_VOLUMEDOWN }, /* Volume defined as right hand*/ - { 0xbf01, KEY_CHANNELUP }, - { 0xbf05, KEY_CHANNELDOWN }, - { 0xbf14, KEY_ZOOM }, - { 0xbf18, KEY_RECORD }, - { 0xbf16, KEY_STOP }, + { 0xff40ea15, KEY_0 }, + { 0xff40f708, KEY_1 }, + { 0xff40f609, KEY_2 }, + { 0xff40f50a, KEY_3 }, + { 0xff40f30c, KEY_4 }, + { 0xff40f20d, KEY_5 }, + { 0xff40f10e, KEY_6 }, + { 0xff40ef10, KEY_7 }, + { 0xff40ee11, KEY_8 }, + { 0xff40ed12, KEY_9 }, + { 0xff40ff00, KEY_POWER }, + { 0xff40fb04, KEY_MEDIA_REPEAT}, /* Recall */ + { 0xff40e51a, KEY_PAUSE }, /* Timeshift */ + { 0xff40fd02, KEY_VOLUMEUP }, /* 2 x -/+ Keys not marked */ + { 0xff40f906, KEY_VOLUMEDOWN }, /* Volume defined as right hand*/ + { 0xff40fe01, KEY_CHANNELUP }, + { 0xff40fa05, KEY_CHANNELDOWN }, + { 0xff40eb14, KEY_ZOOM }, + { 0xff40e718, KEY_RECORD }, + { 0xff40e916, KEY_STOP }, /* Type 3 - 20 buttons */ - { 0x1c, KEY_0 }, - { 0x07, KEY_1 }, - { 0x15, KEY_2 }, - { 0x09, KEY_3 }, - { 0x16, KEY_4 }, - { 0x19, KEY_5 }, - { 0x0d, KEY_6 }, - { 0x0c, KEY_7 }, - { 0x18, KEY_8 }, - { 0x5e, KEY_9 }, - { 0x45, KEY_POWER }, - { 0x44, KEY_MEDIA_REPEAT}, /* Recall */ - { 0x4a, KEY_PAUSE }, /* Timeshift */ - { 0x47, KEY_VOLUMEUP }, /* 2 x -/+ Keys not marked */ - { 0x43, KEY_VOLUMEDOWN }, /* Volume defined as right hand*/ - { 0x46, KEY_CHANNELUP }, - { 0x40, KEY_CHANNELDOWN }, - { 0x08, KEY_ZOOM }, - { 0x42, KEY_RECORD }, - { 0x5a, KEY_STOP }, + { 0xff00e31c, KEY_0 }, + { 0xff00f807, KEY_1 }, + { 0xff00ea15, KEY_2 }, + { 0xff00f609, KEY_3 }, + { 0xff00e916, KEY_4 }, + { 0xff00e619, KEY_5 }, + { 0xff00f20d, KEY_6 }, + { 0xff00f30c, KEY_7 }, + { 0xff00e718, KEY_8 }, + { 0xff00a15e, KEY_9 }, + { 0xff00ba45, KEY_POWER }, + { 0xff00bb44, KEY_MEDIA_REPEAT}, /* Recall */ + { 0xff00b54a, KEY_PAUSE }, /* Timeshift */ + { 0xff00b847, KEY_VOLUMEUP }, /* 2 x -/+ Keys not marked */ + { 0xff00bc43, KEY_VOLUMEDOWN }, /* Volume defined as right hand*/ + { 0xff00b946, KEY_CHANNELUP }, + { 0xff00bf40, KEY_CHANNELDOWN }, + { 0xff00f708, KEY_ZOOM }, + { 0xff00bd42, KEY_RECORD }, + { 0xff00a55a, KEY_STOP }, }; static struct rc_map_list lme2510_map = { diff --git a/drivers/media/usb/dvb-usb-v2/lmedm04.c b/drivers/media/usb/dvb-usb-v2/lmedm04.c index 4cc55b3a0558..3721ee63b8fb 100644 --- a/drivers/media/usb/dvb-usb-v2/lmedm04.c +++ b/drivers/media/usb/dvb-usb-v2/lmedm04.c @@ -348,15 +348,16 @@ static void lme2510_int_response(struct urb *lme_urb) switch (ibuf[0]) { case 0xaa: debug_data_snipet(1, "INT Remote data snipet", ibuf); - if ((ibuf[4] + ibuf[5]) == 0xff) { - key = RC_SCANCODE_NECX((ibuf[2] ^ 0xff) << 8 | - (ibuf[3] > 0) ? (ibuf[3] ^ 0xff) : 0, - ibuf[5]); - deb_info(1, "INT Key =%08x", key); - if (adap_to_d(adap)->rc_dev != NULL) - rc_keydown(adap_to_d(adap)->rc_dev, - RC_TYPE_NEC, key, 0); - } + if (!adap_to_d(adap)->rc_dev) + break; + + key = RC_SCANCODE_NEC32(ibuf[2] << 24 | + ibuf[3] << 16 | + ibuf[4] << 8 | + ibuf[5]); + + deb_info(1, "INT Key = 0x%08x", key); + rc_keydown(adap_to_d(adap)->rc_dev, RC_TYPE_NEC, key, 0); break; case 0xbb: switch (st->tuner_config) { @@ -1344,7 +1345,7 @@ module_usb_driver(lme2510_driver); MODULE_AUTHOR("Malcolm Priestley "); MODULE_DESCRIPTION("LME2510(C) DVB-S USB2.0"); -MODULE_VERSION("2.06"); +MODULE_VERSION("2.07"); MODULE_LICENSE("GPL"); MODULE_FIRMWARE(LME2510_C_S7395); MODULE_FIRMWARE(LME2510_C_LG); -- cgit v1.2.3 From 8783b9c50400c6279d7c3b716637b98e83d3c933 Mon Sep 17 00:00:00 2001 From: Nibble Max Date: Mon, 29 Jun 2015 11:09:42 -0300 Subject: [media] SMI PCIe IR driver for DVBSky cards Ported from the manufacturer's source tree, available from http://dvbsky.net/download/linux/media_build-bst-150211.tar.gz This is the second patch after a public review. [mchehab@osg.samsung.com: fix inconsistent identing warning] Signed-off-by: Dirk Nehring Reviewd-by: Nibble Max Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/smipcie/Kconfig | 1 + drivers/media/pci/smipcie/Makefile | 3 + drivers/media/pci/smipcie/smipcie-ir.c | 232 +++++++ drivers/media/pci/smipcie/smipcie-main.c | 1114 ++++++++++++++++++++++++++++++ drivers/media/pci/smipcie/smipcie.c | 1102 ----------------------------- drivers/media/pci/smipcie/smipcie.h | 19 + 6 files changed, 1369 insertions(+), 1102 deletions(-) create mode 100644 drivers/media/pci/smipcie/smipcie-ir.c create mode 100644 drivers/media/pci/smipcie/smipcie-main.c delete mode 100644 drivers/media/pci/smipcie/smipcie.c 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 + * + * 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-main.c b/drivers/media/pci/smipcie/smipcie-main.c new file mode 100644 index 000000000000..b039a229b7d2 --- /dev/null +++ b/drivers/media/pci/smipcie/smipcie-main.c @@ -0,0 +1,1114 @@ +/* + * SMI PCIe driver for DVBSky cards. + * + * Copyright (C) 2014 Max nibble + * + * 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" +#include "m88ds3103.h" +#include "ts2020.h" +#include "m88rs6000t.h" +#include "si2168.h" +#include "si2157.h" + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +static int smi_hw_init(struct smi_dev *dev) +{ + u32 port_mux, port_ctrl, int_stat; + + /* set port mux.*/ + port_mux = smi_read(MUX_MODE_CTRL); + port_mux &= ~(rbPaMSMask); + port_mux |= rbPaMSDtvNoGpio; + port_mux &= ~(rbPbMSMask); + port_mux |= rbPbMSDtvNoGpio; + port_mux &= ~(0x0f0000); + port_mux |= 0x50000; + smi_write(MUX_MODE_CTRL, port_mux); + + /* set DTV register.*/ + /* Port A */ + port_ctrl = smi_read(VIDEO_CTRL_STATUS_A); + port_ctrl &= ~0x01; + smi_write(VIDEO_CTRL_STATUS_A, port_ctrl); + port_ctrl = smi_read(MPEG2_CTRL_A); + port_ctrl &= ~0x40; + port_ctrl |= 0x80; + smi_write(MPEG2_CTRL_A, port_ctrl); + /* Port B */ + port_ctrl = smi_read(VIDEO_CTRL_STATUS_B); + port_ctrl &= ~0x01; + smi_write(VIDEO_CTRL_STATUS_B, port_ctrl); + port_ctrl = smi_read(MPEG2_CTRL_B); + port_ctrl &= ~0x40; + port_ctrl |= 0x80; + smi_write(MPEG2_CTRL_B, port_ctrl); + + /* disable and clear interrupt.*/ + smi_write(MSI_INT_ENA_CLR, ALL_INT); + int_stat = smi_read(MSI_INT_STATUS); + smi_write(MSI_INT_STATUS_CLR, int_stat); + + /* reset demod.*/ + smi_clear(PERIPHERAL_CTRL, 0x0303); + msleep(50); + smi_set(PERIPHERAL_CTRL, 0x0101); + return 0; +} + +/* i2c bit bus.*/ +static void smi_i2c_cfg(struct smi_dev *dev, u32 sw_ctl) +{ + u32 dwCtrl; + + dwCtrl = smi_read(sw_ctl); + dwCtrl &= ~0x18; /* disable output.*/ + dwCtrl |= 0x21; /* reset and software mode.*/ + dwCtrl &= ~0xff00; + dwCtrl |= 0x6400; + smi_write(sw_ctl, dwCtrl); + msleep(20); + dwCtrl = smi_read(sw_ctl); + dwCtrl &= ~0x20; + smi_write(sw_ctl, dwCtrl); +} + +static void smi_i2c_setsda(struct smi_dev *dev, int state, u32 sw_ctl) +{ + if (state) { + /* set as input.*/ + smi_clear(sw_ctl, SW_I2C_MSK_DAT_EN); + } else { + smi_clear(sw_ctl, SW_I2C_MSK_DAT_OUT); + /* set as output.*/ + smi_set(sw_ctl, SW_I2C_MSK_DAT_EN); + } +} + +static void smi_i2c_setscl(void *data, int state, u32 sw_ctl) +{ + struct smi_dev *dev = data; + + if (state) { + /* set as input.*/ + smi_clear(sw_ctl, SW_I2C_MSK_CLK_EN); + } else { + smi_clear(sw_ctl, SW_I2C_MSK_CLK_OUT); + /* set as output.*/ + smi_set(sw_ctl, SW_I2C_MSK_CLK_EN); + } +} + +static int smi_i2c_getsda(void *data, u32 sw_ctl) +{ + struct smi_dev *dev = data; + /* set as input.*/ + smi_clear(sw_ctl, SW_I2C_MSK_DAT_EN); + udelay(1); + return (smi_read(sw_ctl) & SW_I2C_MSK_DAT_IN) ? 1 : 0; +} + +static int smi_i2c_getscl(void *data, u32 sw_ctl) +{ + struct smi_dev *dev = data; + /* set as input.*/ + smi_clear(sw_ctl, SW_I2C_MSK_CLK_EN); + udelay(1); + return (smi_read(sw_ctl) & SW_I2C_MSK_CLK_IN) ? 1 : 0; +} +/* i2c 0.*/ +static void smi_i2c0_setsda(void *data, int state) +{ + struct smi_dev *dev = data; + + smi_i2c_setsda(dev, state, I2C_A_SW_CTL); +} + +static void smi_i2c0_setscl(void *data, int state) +{ + struct smi_dev *dev = data; + + smi_i2c_setscl(dev, state, I2C_A_SW_CTL); +} + +static int smi_i2c0_getsda(void *data) +{ + struct smi_dev *dev = data; + + return smi_i2c_getsda(dev, I2C_A_SW_CTL); +} + +static int smi_i2c0_getscl(void *data) +{ + struct smi_dev *dev = data; + + return smi_i2c_getscl(dev, I2C_A_SW_CTL); +} +/* i2c 1.*/ +static void smi_i2c1_setsda(void *data, int state) +{ + struct smi_dev *dev = data; + + smi_i2c_setsda(dev, state, I2C_B_SW_CTL); +} + +static void smi_i2c1_setscl(void *data, int state) +{ + struct smi_dev *dev = data; + + smi_i2c_setscl(dev, state, I2C_B_SW_CTL); +} + +static int smi_i2c1_getsda(void *data) +{ + struct smi_dev *dev = data; + + return smi_i2c_getsda(dev, I2C_B_SW_CTL); +} + +static int smi_i2c1_getscl(void *data) +{ + struct smi_dev *dev = data; + + return smi_i2c_getscl(dev, I2C_B_SW_CTL); +} + +static int smi_i2c_init(struct smi_dev *dev) +{ + int ret; + + /* i2c bus 0 */ + smi_i2c_cfg(dev, I2C_A_SW_CTL); + i2c_set_adapdata(&dev->i2c_bus[0], dev); + strcpy(dev->i2c_bus[0].name, "SMI-I2C0"); + dev->i2c_bus[0].owner = THIS_MODULE; + dev->i2c_bus[0].dev.parent = &dev->pci_dev->dev; + dev->i2c_bus[0].algo_data = &dev->i2c_bit[0]; + dev->i2c_bit[0].data = dev; + dev->i2c_bit[0].setsda = smi_i2c0_setsda; + dev->i2c_bit[0].setscl = smi_i2c0_setscl; + dev->i2c_bit[0].getsda = smi_i2c0_getsda; + dev->i2c_bit[0].getscl = smi_i2c0_getscl; + dev->i2c_bit[0].udelay = 12; + dev->i2c_bit[0].timeout = 10; + /* Raise SCL and SDA */ + smi_i2c0_setsda(dev, 1); + smi_i2c0_setscl(dev, 1); + + ret = i2c_bit_add_bus(&dev->i2c_bus[0]); + if (ret < 0) + return ret; + + /* i2c bus 1 */ + smi_i2c_cfg(dev, I2C_B_SW_CTL); + i2c_set_adapdata(&dev->i2c_bus[1], dev); + strcpy(dev->i2c_bus[1].name, "SMI-I2C1"); + dev->i2c_bus[1].owner = THIS_MODULE; + dev->i2c_bus[1].dev.parent = &dev->pci_dev->dev; + dev->i2c_bus[1].algo_data = &dev->i2c_bit[1]; + dev->i2c_bit[1].data = dev; + dev->i2c_bit[1].setsda = smi_i2c1_setsda; + dev->i2c_bit[1].setscl = smi_i2c1_setscl; + dev->i2c_bit[1].getsda = smi_i2c1_getsda; + dev->i2c_bit[1].getscl = smi_i2c1_getscl; + dev->i2c_bit[1].udelay = 12; + dev->i2c_bit[1].timeout = 10; + /* Raise SCL and SDA */ + smi_i2c1_setsda(dev, 1); + smi_i2c1_setscl(dev, 1); + + ret = i2c_bit_add_bus(&dev->i2c_bus[1]); + if (ret < 0) + i2c_del_adapter(&dev->i2c_bus[0]); + + return ret; +} + +static void smi_i2c_exit(struct smi_dev *dev) +{ + i2c_del_adapter(&dev->i2c_bus[0]); + i2c_del_adapter(&dev->i2c_bus[1]); +} + +static int smi_read_eeprom(struct i2c_adapter *i2c, u16 reg, u8 *data, u16 size) +{ + int ret; + u8 b0[2] = { (reg >> 8) & 0xff, reg & 0xff }; + + struct i2c_msg msg[] = { + { .addr = 0x50, .flags = 0, + .buf = b0, .len = 2 }, + { .addr = 0x50, .flags = I2C_M_RD, + .buf = data, .len = size } + }; + + ret = i2c_transfer(i2c, msg, 2); + + if (ret != 2) { + dev_err(&i2c->dev, "%s: reg=0x%x (error=%d)\n", + __func__, reg, ret); + return ret; + } + return ret; +} + +/* ts port interrupt operations */ +static void smi_port_disableInterrupt(struct smi_port *port) +{ + struct smi_dev *dev = port->dev; + + smi_write(MSI_INT_ENA_CLR, + (port->_dmaInterruptCH0 | port->_dmaInterruptCH1)); +} + +static void smi_port_enableInterrupt(struct smi_port *port) +{ + struct smi_dev *dev = port->dev; + + smi_write(MSI_INT_ENA_SET, + (port->_dmaInterruptCH0 | port->_dmaInterruptCH1)); +} + +static void smi_port_clearInterrupt(struct smi_port *port) +{ + struct smi_dev *dev = port->dev; + + smi_write(MSI_INT_STATUS_CLR, + (port->_dmaInterruptCH0 | port->_dmaInterruptCH1)); +} + +/* tasklet handler: DMA data to dmx.*/ +static void smi_dma_xfer(unsigned long data) +{ + struct smi_port *port = (struct smi_port *) data; + struct smi_dev *dev = port->dev; + u32 intr_status, finishedData, dmaManagement; + u8 dmaChan0State, dmaChan1State; + + intr_status = port->_int_status; + dmaManagement = smi_read(port->DMA_MANAGEMENT); + dmaChan0State = (u8)((dmaManagement & 0x00000030) >> 4); + dmaChan1State = (u8)((dmaManagement & 0x00300000) >> 20); + + /* CH-0 DMA interrupt.*/ + if ((intr_status & port->_dmaInterruptCH0) && (dmaChan0State == 0x01)) { + dev_dbg(&dev->pci_dev->dev, + "Port[%d]-DMA CH0 engine complete successful !\n", + port->idx); + finishedData = smi_read(port->DMA_CHAN0_TRANS_STATE); + finishedData &= 0x003FFFFF; + /* value of DMA_PORT0_CHAN0_TRANS_STATE register [21:0] + * indicate dma total transfer length and + * zero of [21:0] indicate dma total transfer length + * equal to 0x400000 (4MB)*/ + if (finishedData == 0) + finishedData = 0x00400000; + if (finishedData != SMI_TS_DMA_BUF_SIZE) { + dev_dbg(&dev->pci_dev->dev, + "DMA CH0 engine complete length mismatched, finish data=%d !\n", + finishedData); + } + dvb_dmx_swfilter_packets(&port->demux, + port->cpu_addr[0], (finishedData / 188)); + /*dvb_dmx_swfilter(&port->demux, + port->cpu_addr[0], finishedData);*/ + } + /* CH-1 DMA interrupt.*/ + if ((intr_status & port->_dmaInterruptCH1) && (dmaChan1State == 0x01)) { + dev_dbg(&dev->pci_dev->dev, + "Port[%d]-DMA CH1 engine complete successful !\n", + port->idx); + finishedData = smi_read(port->DMA_CHAN1_TRANS_STATE); + finishedData &= 0x003FFFFF; + /* value of DMA_PORT0_CHAN0_TRANS_STATE register [21:0] + * indicate dma total transfer length and + * zero of [21:0] indicate dma total transfer length + * equal to 0x400000 (4MB)*/ + if (finishedData == 0) + finishedData = 0x00400000; + if (finishedData != SMI_TS_DMA_BUF_SIZE) { + dev_dbg(&dev->pci_dev->dev, + "DMA CH1 engine complete length mismatched, finish data=%d !\n", + finishedData); + } + dvb_dmx_swfilter_packets(&port->demux, + port->cpu_addr[1], (finishedData / 188)); + /*dvb_dmx_swfilter(&port->demux, + port->cpu_addr[1], finishedData);*/ + } + /* restart DMA.*/ + if (intr_status & port->_dmaInterruptCH0) + dmaManagement |= 0x00000002; + if (intr_status & port->_dmaInterruptCH1) + dmaManagement |= 0x00020000; + smi_write(port->DMA_MANAGEMENT, dmaManagement); + /* Re-enable interrupts */ + smi_port_enableInterrupt(port); +} + +static void smi_port_dma_free(struct smi_port *port) +{ + if (port->cpu_addr[0]) { + pci_free_consistent(port->dev->pci_dev, SMI_TS_DMA_BUF_SIZE, + port->cpu_addr[0], port->dma_addr[0]); + port->cpu_addr[0] = NULL; + } + if (port->cpu_addr[1]) { + pci_free_consistent(port->dev->pci_dev, SMI_TS_DMA_BUF_SIZE, + port->cpu_addr[1], port->dma_addr[1]); + port->cpu_addr[1] = NULL; + } +} + +static int smi_port_init(struct smi_port *port, int dmaChanUsed) +{ + dev_dbg(&port->dev->pci_dev->dev, + "%s, port %d, dmaused %d\n", __func__, port->idx, dmaChanUsed); + port->enable = 0; + if (port->idx == 0) { + /* Port A */ + port->_dmaInterruptCH0 = dmaChanUsed & 0x01; + port->_dmaInterruptCH1 = dmaChanUsed & 0x02; + + port->DMA_CHAN0_ADDR_LOW = DMA_PORTA_CHAN0_ADDR_LOW; + port->DMA_CHAN0_ADDR_HI = DMA_PORTA_CHAN0_ADDR_HI; + port->DMA_CHAN0_TRANS_STATE = DMA_PORTA_CHAN0_TRANS_STATE; + port->DMA_CHAN0_CONTROL = DMA_PORTA_CHAN0_CONTROL; + port->DMA_CHAN1_ADDR_LOW = DMA_PORTA_CHAN1_ADDR_LOW; + port->DMA_CHAN1_ADDR_HI = DMA_PORTA_CHAN1_ADDR_HI; + port->DMA_CHAN1_TRANS_STATE = DMA_PORTA_CHAN1_TRANS_STATE; + port->DMA_CHAN1_CONTROL = DMA_PORTA_CHAN1_CONTROL; + port->DMA_MANAGEMENT = DMA_PORTA_MANAGEMENT; + } else { + /* Port B */ + port->_dmaInterruptCH0 = (dmaChanUsed << 2) & 0x04; + port->_dmaInterruptCH1 = (dmaChanUsed << 2) & 0x08; + + port->DMA_CHAN0_ADDR_LOW = DMA_PORTB_CHAN0_ADDR_LOW; + port->DMA_CHAN0_ADDR_HI = DMA_PORTB_CHAN0_ADDR_HI; + port->DMA_CHAN0_TRANS_STATE = DMA_PORTB_CHAN0_TRANS_STATE; + port->DMA_CHAN0_CONTROL = DMA_PORTB_CHAN0_CONTROL; + port->DMA_CHAN1_ADDR_LOW = DMA_PORTB_CHAN1_ADDR_LOW; + port->DMA_CHAN1_ADDR_HI = DMA_PORTB_CHAN1_ADDR_HI; + port->DMA_CHAN1_TRANS_STATE = DMA_PORTB_CHAN1_TRANS_STATE; + port->DMA_CHAN1_CONTROL = DMA_PORTB_CHAN1_CONTROL; + port->DMA_MANAGEMENT = DMA_PORTB_MANAGEMENT; + } + + if (port->_dmaInterruptCH0) { + port->cpu_addr[0] = pci_alloc_consistent(port->dev->pci_dev, + SMI_TS_DMA_BUF_SIZE, + &port->dma_addr[0]); + if (!port->cpu_addr[0]) { + dev_err(&port->dev->pci_dev->dev, + "Port[%d] DMA CH0 memory allocation failed!\n", + port->idx); + goto err; + } + } + + if (port->_dmaInterruptCH1) { + port->cpu_addr[1] = pci_alloc_consistent(port->dev->pci_dev, + SMI_TS_DMA_BUF_SIZE, + &port->dma_addr[1]); + if (!port->cpu_addr[1]) { + dev_err(&port->dev->pci_dev->dev, + "Port[%d] DMA CH1 memory allocation failed!\n", + port->idx); + goto err; + } + } + + smi_port_disableInterrupt(port); + tasklet_init(&port->tasklet, smi_dma_xfer, (unsigned long)port); + tasklet_disable(&port->tasklet); + port->enable = 1; + return 0; +err: + smi_port_dma_free(port); + return -ENOMEM; +} + +static void smi_port_exit(struct smi_port *port) +{ + smi_port_disableInterrupt(port); + tasklet_kill(&port->tasklet); + smi_port_dma_free(port); + port->enable = 0; +} + +static int smi_port_irq(struct smi_port *port, u32 int_status) +{ + u32 port_req_irq = port->_dmaInterruptCH0 | port->_dmaInterruptCH1; + int handled = 0; + + if (int_status & port_req_irq) { + smi_port_disableInterrupt(port); + port->_int_status = int_status; + smi_port_clearInterrupt(port); + tasklet_schedule(&port->tasklet); + handled = 1; + } + return handled; +} + +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); + + /* ts0 interrupt.*/ + if (dev->info->ts_0) + handled += smi_port_irq(port0, intr_status); + + /* ts1 interrupt.*/ + 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); +} + +static struct i2c_client *smi_add_i2c_client(struct i2c_adapter *adapter, + struct i2c_board_info *info) +{ + struct i2c_client *client; + + request_module(info->type); + client = i2c_new_device(adapter, info); + if (client == NULL || client->dev.driver == NULL) + goto err_add_i2c_client; + + if (!try_module_get(client->dev.driver->owner)) { + i2c_unregister_device(client); + goto err_add_i2c_client; + } + return client; + +err_add_i2c_client: + client = NULL; + return client; +} + +static void smi_del_i2c_client(struct i2c_client *client) +{ + module_put(client->dev.driver->owner); + i2c_unregister_device(client); +} + +static const struct m88ds3103_config smi_dvbsky_m88ds3103_cfg = { + .i2c_addr = 0x68, + .clock = 27000000, + .i2c_wr_max = 33, + .clock_out = 0, + .ts_mode = M88DS3103_TS_PARALLEL, + .ts_clk = 16000, + .ts_clk_pol = 1, + .agc = 0x99, + .lnb_hv_pol = 0, + .lnb_en_pol = 1, +}; + +static int smi_dvbsky_m88ds3103_fe_attach(struct smi_port *port) +{ + int ret = 0; + struct smi_dev *dev = port->dev; + struct i2c_adapter *i2c; + /* tuner I2C module */ + struct i2c_adapter *tuner_i2c_adapter; + struct i2c_client *tuner_client; + struct i2c_board_info tuner_info; + struct ts2020_config ts2020_config = {}; + memset(&tuner_info, 0, sizeof(struct i2c_board_info)); + i2c = (port->idx == 0) ? &dev->i2c_bus[0] : &dev->i2c_bus[1]; + + /* attach demod */ + port->fe = dvb_attach(m88ds3103_attach, + &smi_dvbsky_m88ds3103_cfg, i2c, &tuner_i2c_adapter); + if (!port->fe) { + ret = -ENODEV; + return ret; + } + /* attach tuner */ + ts2020_config.fe = port->fe; + strlcpy(tuner_info.type, "ts2020", I2C_NAME_SIZE); + tuner_info.addr = 0x60; + tuner_info.platform_data = &ts2020_config; + tuner_client = smi_add_i2c_client(tuner_i2c_adapter, &tuner_info); + if (!tuner_client) { + ret = -ENODEV; + goto err_tuner_i2c_device; + } + + /* delegate signal strength measurement to tuner */ + port->fe->ops.read_signal_strength = + port->fe->ops.tuner_ops.get_rf_strength; + + port->i2c_client_tuner = tuner_client; + return ret; + +err_tuner_i2c_device: + dvb_frontend_detach(port->fe); + return ret; +} + +static const struct m88ds3103_config smi_dvbsky_m88rs6000_cfg = { + .i2c_addr = 0x69, + .clock = 27000000, + .i2c_wr_max = 33, + .ts_mode = M88DS3103_TS_PARALLEL, + .ts_clk = 16000, + .ts_clk_pol = 1, + .agc = 0x99, + .lnb_hv_pol = 0, + .lnb_en_pol = 1, +}; + +static int smi_dvbsky_m88rs6000_fe_attach(struct smi_port *port) +{ + int ret = 0; + struct smi_dev *dev = port->dev; + struct i2c_adapter *i2c; + /* tuner I2C module */ + struct i2c_adapter *tuner_i2c_adapter; + struct i2c_client *tuner_client; + struct i2c_board_info tuner_info; + struct m88rs6000t_config m88rs6000t_config; + + memset(&tuner_info, 0, sizeof(struct i2c_board_info)); + i2c = (port->idx == 0) ? &dev->i2c_bus[0] : &dev->i2c_bus[1]; + + /* attach demod */ + port->fe = dvb_attach(m88ds3103_attach, + &smi_dvbsky_m88rs6000_cfg, i2c, &tuner_i2c_adapter); + if (!port->fe) { + ret = -ENODEV; + return ret; + } + /* attach tuner */ + m88rs6000t_config.fe = port->fe; + strlcpy(tuner_info.type, "m88rs6000t", I2C_NAME_SIZE); + tuner_info.addr = 0x21; + tuner_info.platform_data = &m88rs6000t_config; + tuner_client = smi_add_i2c_client(tuner_i2c_adapter, &tuner_info); + if (!tuner_client) { + ret = -ENODEV; + goto err_tuner_i2c_device; + } + + /* delegate signal strength measurement to tuner */ + port->fe->ops.read_signal_strength = + port->fe->ops.tuner_ops.get_rf_strength; + + port->i2c_client_tuner = tuner_client; + return ret; + +err_tuner_i2c_device: + dvb_frontend_detach(port->fe); + return ret; +} + +static int smi_dvbsky_sit2_fe_attach(struct smi_port *port) +{ + int ret = 0; + struct smi_dev *dev = port->dev; + struct i2c_adapter *i2c; + struct i2c_adapter *tuner_i2c_adapter; + struct i2c_client *client_tuner, *client_demod; + struct i2c_board_info client_info; + struct si2168_config si2168_config; + struct si2157_config si2157_config; + + /* select i2c bus */ + i2c = (port->idx == 0) ? &dev->i2c_bus[0] : &dev->i2c_bus[1]; + + /* attach demod */ + memset(&si2168_config, 0, sizeof(si2168_config)); + si2168_config.i2c_adapter = &tuner_i2c_adapter; + si2168_config.fe = &port->fe; + si2168_config.ts_mode = SI2168_TS_PARALLEL; + + memset(&client_info, 0, sizeof(struct i2c_board_info)); + strlcpy(client_info.type, "si2168", I2C_NAME_SIZE); + client_info.addr = 0x64; + client_info.platform_data = &si2168_config; + + client_demod = smi_add_i2c_client(i2c, &client_info); + if (!client_demod) { + ret = -ENODEV; + return ret; + } + port->i2c_client_demod = client_demod; + + /* attach tuner */ + memset(&si2157_config, 0, sizeof(si2157_config)); + si2157_config.fe = port->fe; + si2157_config.if_port = 1; + + memset(&client_info, 0, sizeof(struct i2c_board_info)); + strlcpy(client_info.type, "si2157", I2C_NAME_SIZE); + client_info.addr = 0x60; + client_info.platform_data = &si2157_config; + + client_tuner = smi_add_i2c_client(tuner_i2c_adapter, &client_info); + if (!client_tuner) { + smi_del_i2c_client(port->i2c_client_demod); + port->i2c_client_demod = NULL; + ret = -ENODEV; + return ret; + } + port->i2c_client_tuner = client_tuner; + return ret; +} + +static int smi_fe_init(struct smi_port *port) +{ + int ret = 0; + struct smi_dev *dev = port->dev; + struct dvb_adapter *adap = &port->dvb_adapter; + u8 mac_ee[16]; + + dev_dbg(&port->dev->pci_dev->dev, + "%s: port %d, fe_type = %d\n", + __func__, port->idx, port->fe_type); + switch (port->fe_type) { + case DVBSKY_FE_M88DS3103: + ret = smi_dvbsky_m88ds3103_fe_attach(port); + break; + case DVBSKY_FE_M88RS6000: + ret = smi_dvbsky_m88rs6000_fe_attach(port); + break; + case DVBSKY_FE_SIT2: + ret = smi_dvbsky_sit2_fe_attach(port); + break; + } + if (ret < 0) + return ret; + + /* register dvb frontend */ + ret = dvb_register_frontend(adap, port->fe); + if (ret < 0) { + if (port->i2c_client_tuner) + smi_del_i2c_client(port->i2c_client_tuner); + if (port->i2c_client_demod) + smi_del_i2c_client(port->i2c_client_demod); + dvb_frontend_detach(port->fe); + return ret; + } + /* init MAC.*/ + ret = smi_read_eeprom(&dev->i2c_bus[0], 0xc0, mac_ee, 16); + dev_info(&port->dev->pci_dev->dev, + "DVBSky SMI PCIe MAC= %pM\n", mac_ee + (port->idx)*8); + memcpy(adap->proposed_mac, mac_ee + (port->idx)*8, 6); + return ret; +} + +static void smi_fe_exit(struct smi_port *port) +{ + dvb_unregister_frontend(port->fe); + /* remove I2C demod and tuner */ + if (port->i2c_client_tuner) + smi_del_i2c_client(port->i2c_client_tuner); + if (port->i2c_client_demod) + smi_del_i2c_client(port->i2c_client_demod); + dvb_frontend_detach(port->fe); +} + +static int my_dvb_dmx_ts_card_init(struct dvb_demux *dvbdemux, char *id, + int (*start_feed)(struct dvb_demux_feed *), + int (*stop_feed)(struct dvb_demux_feed *), + void *priv) +{ + dvbdemux->priv = priv; + + dvbdemux->filternum = 256; + dvbdemux->feednum = 256; + dvbdemux->start_feed = start_feed; + dvbdemux->stop_feed = stop_feed; + dvbdemux->write_to_decoder = NULL; + dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | + DMX_SECTION_FILTERING | + DMX_MEMORY_BASED_FILTERING); + return dvb_dmx_init(dvbdemux); +} + +static int my_dvb_dmxdev_ts_card_init(struct dmxdev *dmxdev, + struct dvb_demux *dvbdemux, + struct dmx_frontend *hw_frontend, + struct dmx_frontend *mem_frontend, + struct dvb_adapter *dvb_adapter) +{ + int ret; + + dmxdev->filternum = 256; + dmxdev->demux = &dvbdemux->dmx; + dmxdev->capabilities = 0; + ret = dvb_dmxdev_init(dmxdev, dvb_adapter); + if (ret < 0) + return ret; + + hw_frontend->source = DMX_FRONTEND_0; + dvbdemux->dmx.add_frontend(&dvbdemux->dmx, hw_frontend); + mem_frontend->source = DMX_MEMORY_FE; + dvbdemux->dmx.add_frontend(&dvbdemux->dmx, mem_frontend); + return dvbdemux->dmx.connect_frontend(&dvbdemux->dmx, hw_frontend); +} + +static u32 smi_config_DMA(struct smi_port *port) +{ + struct smi_dev *dev = port->dev; + u32 totalLength = 0, dmaMemPtrLow, dmaMemPtrHi, dmaCtlReg; + u8 chanLatencyTimer = 0, dmaChanEnable = 1, dmaTransStart = 1; + u32 dmaManagement = 0, tlpTransUnit = DMA_TRANS_UNIT_188; + u8 tlpTc = 0, tlpTd = 1, tlpEp = 0, tlpAttr = 0; + u64 mem; + + dmaManagement = smi_read(port->DMA_MANAGEMENT); + /* Setup Channel-0 */ + if (port->_dmaInterruptCH0) { + totalLength = SMI_TS_DMA_BUF_SIZE; + mem = port->dma_addr[0]; + dmaMemPtrLow = mem & 0xffffffff; + dmaMemPtrHi = mem >> 32; + dmaCtlReg = (totalLength) | (tlpTransUnit << 22) | (tlpTc << 25) + | (tlpTd << 28) | (tlpEp << 29) | (tlpAttr << 30); + dmaManagement |= dmaChanEnable | (dmaTransStart << 1) + | (chanLatencyTimer << 8); + /* write DMA register, start DMA engine */ + smi_write(port->DMA_CHAN0_ADDR_LOW, dmaMemPtrLow); + smi_write(port->DMA_CHAN0_ADDR_HI, dmaMemPtrHi); + smi_write(port->DMA_CHAN0_CONTROL, dmaCtlReg); + } + /* Setup Channel-1 */ + if (port->_dmaInterruptCH1) { + totalLength = SMI_TS_DMA_BUF_SIZE; + mem = port->dma_addr[1]; + dmaMemPtrLow = mem & 0xffffffff; + dmaMemPtrHi = mem >> 32; + dmaCtlReg = (totalLength) | (tlpTransUnit << 22) | (tlpTc << 25) + | (tlpTd << 28) | (tlpEp << 29) | (tlpAttr << 30); + dmaManagement |= (dmaChanEnable << 16) | (dmaTransStart << 17) + | (chanLatencyTimer << 24); + /* write DMA register, start DMA engine */ + smi_write(port->DMA_CHAN1_ADDR_LOW, dmaMemPtrLow); + smi_write(port->DMA_CHAN1_ADDR_HI, dmaMemPtrHi); + smi_write(port->DMA_CHAN1_CONTROL, dmaCtlReg); + } + return dmaManagement; +} + +static int smi_start_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + struct dvb_demux *dvbdmx = dvbdmxfeed->demux; + struct smi_port *port = dvbdmx->priv; + struct smi_dev *dev = port->dev; + u32 dmaManagement; + + if (port->users++ == 0) { + dmaManagement = smi_config_DMA(port); + smi_port_clearInterrupt(port); + smi_port_enableInterrupt(port); + smi_write(port->DMA_MANAGEMENT, dmaManagement); + tasklet_enable(&port->tasklet); + } + return port->users; +} + +static int smi_stop_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + struct dvb_demux *dvbdmx = dvbdmxfeed->demux; + struct smi_port *port = dvbdmx->priv; + struct smi_dev *dev = port->dev; + + if (--port->users) + return port->users; + + tasklet_disable(&port->tasklet); + smi_port_disableInterrupt(port); + smi_clear(port->DMA_MANAGEMENT, 0x30003); + return 0; +} + +static int smi_dvb_init(struct smi_port *port) +{ + int ret; + struct dvb_adapter *adap = &port->dvb_adapter; + struct dvb_demux *dvbdemux = &port->demux; + + dev_dbg(&port->dev->pci_dev->dev, + "%s, port %d\n", __func__, port->idx); + + ret = dvb_register_adapter(adap, "SMI_DVB", THIS_MODULE, + &port->dev->pci_dev->dev, + adapter_nr); + if (ret < 0) { + dev_err(&port->dev->pci_dev->dev, "Fail to register DVB adapter.\n"); + return ret; + } + ret = my_dvb_dmx_ts_card_init(dvbdemux, "SW demux", + smi_start_feed, + smi_stop_feed, port); + if (ret < 0) + goto err_del_dvb_register_adapter; + + ret = my_dvb_dmxdev_ts_card_init(&port->dmxdev, &port->demux, + &port->hw_frontend, + &port->mem_frontend, adap); + if (ret < 0) + goto err_del_dvb_dmx; + + ret = dvb_net_init(adap, &port->dvbnet, port->dmxdev.demux); + if (ret < 0) + goto err_del_dvb_dmxdev; + return 0; +err_del_dvb_dmxdev: + dvbdemux->dmx.close(&dvbdemux->dmx); + dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &port->hw_frontend); + dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &port->mem_frontend); + dvb_dmxdev_release(&port->dmxdev); +err_del_dvb_dmx: + dvb_dmx_release(&port->demux); +err_del_dvb_register_adapter: + dvb_unregister_adapter(&port->dvb_adapter); + return ret; +} + +static void smi_dvb_exit(struct smi_port *port) +{ + struct dvb_demux *dvbdemux = &port->demux; + + dvb_net_release(&port->dvbnet); + + dvbdemux->dmx.close(&dvbdemux->dmx); + dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &port->hw_frontend); + dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &port->mem_frontend); + dvb_dmxdev_release(&port->dmxdev); + dvb_dmx_release(&port->demux); + + dvb_unregister_adapter(&port->dvb_adapter); +} + +static int smi_port_attach(struct smi_dev *dev, + struct smi_port *port, int index) +{ + int ret, dmachs; + + port->dev = dev; + port->idx = index; + port->fe_type = (index == 0) ? dev->info->fe_0 : dev->info->fe_1; + dmachs = (index == 0) ? dev->info->ts_0 : dev->info->ts_1; + /* port init.*/ + ret = smi_port_init(port, dmachs); + if (ret < 0) + return ret; + /* dvb init.*/ + ret = smi_dvb_init(port); + if (ret < 0) + goto err_del_port_init; + /* fe init.*/ + ret = smi_fe_init(port); + if (ret < 0) + goto err_del_dvb_init; + return 0; +err_del_dvb_init: + smi_dvb_exit(port); +err_del_port_init: + smi_port_exit(port); + return ret; +} + +static void smi_port_detach(struct smi_port *port) +{ + smi_fe_exit(port); + smi_dvb_exit(port); + smi_port_exit(port); +} + +static int smi_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct smi_dev *dev; + int ret = -ENOMEM; + + if (pci_enable_device(pdev) < 0) + return -ENODEV; + + dev = kzalloc(sizeof(struct smi_dev), GFP_KERNEL); + if (!dev) { + ret = -ENOMEM; + goto err_pci_disable_device; + } + + dev->pci_dev = pdev; + pci_set_drvdata(pdev, dev); + dev->info = (struct smi_cfg_info *) id->driver_data; + dev_info(&dev->pci_dev->dev, + "card detected: %s\n", dev->info->name); + + dev->nr = dev->info->type; + dev->lmmio = ioremap(pci_resource_start(dev->pci_dev, 0), + pci_resource_len(dev->pci_dev, 0)); + if (!dev->lmmio) { + ret = -ENOMEM; + goto err_kfree; + } + + /* should we set to 32bit DMA? */ + ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + if (ret < 0) + goto err_pci_iounmap; + + pci_set_master(pdev); + + ret = smi_hw_init(dev); + if (ret < 0) + goto err_pci_iounmap; + + ret = smi_i2c_init(dev); + if (ret < 0) + goto err_pci_iounmap; + + if (dev->info->ts_0) { + ret = smi_port_attach(dev, &dev->ts_port[0], 0); + if (ret < 0) + goto err_del_i2c_adaptor; + } + + if (dev->info->ts_1) { + ret = smi_port_attach(dev, &dev->ts_port[1], 1); + if (ret < 0) + 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); + if (ret) + dev_info(&dev->pci_dev->dev, "MSI not available.\n"); +#endif + + ret = request_irq(dev->pci_dev->irq, smi_irq_handler, + IRQF_SHARED, "SMI_PCIE", dev); + if (ret < 0) + 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]); +err_del_port0_attach: + if (dev->info->ts_0) + smi_port_detach(&dev->ts_port[0]); +err_del_i2c_adaptor: + smi_i2c_exit(dev); +err_pci_iounmap: + iounmap(dev->lmmio); +err_kfree: + pci_set_drvdata(pdev, NULL); + kfree(dev); +err_pci_disable_device: + pci_disable_device(pdev); + return ret; +} + +static void smi_remove(struct pci_dev *pdev) +{ + struct smi_dev *dev = pci_get_drvdata(pdev); + + smi_write(MSI_INT_ENA_CLR, ALL_INT); + free_irq(dev->pci_dev->irq, dev); +#ifdef CONFIG_PCI_MSI + pci_disable_msi(dev->pci_dev); +#endif + if (dev->info->ts_1) + smi_port_detach(&dev->ts_port[1]); + 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); + pci_disable_device(pdev); + kfree(dev); +} + +/* DVBSky cards */ +static struct smi_cfg_info dvbsky_s950_cfg = { + .type = SMI_DVBSKY_S950, + .name = "DVBSky S950 V3", + .ts_0 = SMI_TS_NULL, + .ts_1 = SMI_TS_DMA_BOTH, + .fe_0 = DVBSKY_FE_NULL, + .fe_1 = DVBSKY_FE_M88DS3103, +}; + +static struct smi_cfg_info dvbsky_s952_cfg = { + .type = SMI_DVBSKY_S952, + .name = "DVBSky S952 V3", + .ts_0 = SMI_TS_DMA_BOTH, + .ts_1 = SMI_TS_DMA_BOTH, + .fe_0 = DVBSKY_FE_M88RS6000, + .fe_1 = DVBSKY_FE_M88RS6000, +}; + +static struct smi_cfg_info dvbsky_t9580_cfg = { + .type = SMI_DVBSKY_T9580, + .name = "DVBSky T9580 V3", + .ts_0 = SMI_TS_DMA_BOTH, + .ts_1 = SMI_TS_DMA_BOTH, + .fe_0 = DVBSKY_FE_SIT2, + .fe_1 = DVBSKY_FE_M88DS3103, +}; + +/* PCI IDs */ +#define SMI_ID(_subvend, _subdev, _driverdata) { \ + .vendor = SMI_VID, .device = SMI_PID, \ + .subvendor = _subvend, .subdevice = _subdev, \ + .driver_data = (unsigned long)&_driverdata } + +static const struct pci_device_id smi_id_table[] = { + SMI_ID(0x4254, 0x0550, dvbsky_s950_cfg), + SMI_ID(0x4254, 0x0552, dvbsky_s952_cfg), + SMI_ID(0x4254, 0x5580, dvbsky_t9580_cfg), + {0} +}; +MODULE_DEVICE_TABLE(pci, smi_id_table); + +static struct pci_driver smipcie_driver = { + .name = "SMI PCIe driver", + .id_table = smi_id_table, + .probe = smi_probe, + .remove = smi_remove, +}; + +module_pci_driver(smipcie_driver); + +MODULE_AUTHOR("Max nibble "); +MODULE_DESCRIPTION("SMI PCIe driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/pci/smipcie/smipcie.c b/drivers/media/pci/smipcie/smipcie.c deleted file mode 100644 index 143fd7899ecd..000000000000 --- a/drivers/media/pci/smipcie/smipcie.c +++ /dev/null @@ -1,1102 +0,0 @@ -/* - * SMI PCIe driver for DVBSky cards. - * - * Copyright (C) 2014 Max nibble - * - * 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" -#include "m88ds3103.h" -#include "ts2020.h" -#include "m88rs6000t.h" -#include "si2168.h" -#include "si2157.h" - -DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); - -static int smi_hw_init(struct smi_dev *dev) -{ - u32 port_mux, port_ctrl, int_stat; - - /* set port mux.*/ - port_mux = smi_read(MUX_MODE_CTRL); - port_mux &= ~(rbPaMSMask); - port_mux |= rbPaMSDtvNoGpio; - port_mux &= ~(rbPbMSMask); - port_mux |= rbPbMSDtvNoGpio; - port_mux &= ~(0x0f0000); - port_mux |= 0x50000; - smi_write(MUX_MODE_CTRL, port_mux); - - /* set DTV register.*/ - /* Port A */ - port_ctrl = smi_read(VIDEO_CTRL_STATUS_A); - port_ctrl &= ~0x01; - smi_write(VIDEO_CTRL_STATUS_A, port_ctrl); - port_ctrl = smi_read(MPEG2_CTRL_A); - port_ctrl &= ~0x40; - port_ctrl |= 0x80; - smi_write(MPEG2_CTRL_A, port_ctrl); - /* Port B */ - port_ctrl = smi_read(VIDEO_CTRL_STATUS_B); - port_ctrl &= ~0x01; - smi_write(VIDEO_CTRL_STATUS_B, port_ctrl); - port_ctrl = smi_read(MPEG2_CTRL_B); - port_ctrl &= ~0x40; - port_ctrl |= 0x80; - smi_write(MPEG2_CTRL_B, port_ctrl); - - /* disable and clear interrupt.*/ - smi_write(MSI_INT_ENA_CLR, ALL_INT); - int_stat = smi_read(MSI_INT_STATUS); - smi_write(MSI_INT_STATUS_CLR, int_stat); - - /* reset demod.*/ - smi_clear(PERIPHERAL_CTRL, 0x0303); - msleep(50); - smi_set(PERIPHERAL_CTRL, 0x0101); - return 0; -} - -/* i2c bit bus.*/ -static void smi_i2c_cfg(struct smi_dev *dev, u32 sw_ctl) -{ - u32 dwCtrl; - - dwCtrl = smi_read(sw_ctl); - dwCtrl &= ~0x18; /* disable output.*/ - dwCtrl |= 0x21; /* reset and software mode.*/ - dwCtrl &= ~0xff00; - dwCtrl |= 0x6400; - smi_write(sw_ctl, dwCtrl); - msleep(20); - dwCtrl = smi_read(sw_ctl); - dwCtrl &= ~0x20; - smi_write(sw_ctl, dwCtrl); -} - -static void smi_i2c_setsda(struct smi_dev *dev, int state, u32 sw_ctl) -{ - if (state) { - /* set as input.*/ - smi_clear(sw_ctl, SW_I2C_MSK_DAT_EN); - } else { - smi_clear(sw_ctl, SW_I2C_MSK_DAT_OUT); - /* set as output.*/ - smi_set(sw_ctl, SW_I2C_MSK_DAT_EN); - } -} - -static void smi_i2c_setscl(void *data, int state, u32 sw_ctl) -{ - struct smi_dev *dev = data; - - if (state) { - /* set as input.*/ - smi_clear(sw_ctl, SW_I2C_MSK_CLK_EN); - } else { - smi_clear(sw_ctl, SW_I2C_MSK_CLK_OUT); - /* set as output.*/ - smi_set(sw_ctl, SW_I2C_MSK_CLK_EN); - } -} - -static int smi_i2c_getsda(void *data, u32 sw_ctl) -{ - struct smi_dev *dev = data; - /* set as input.*/ - smi_clear(sw_ctl, SW_I2C_MSK_DAT_EN); - udelay(1); - return (smi_read(sw_ctl) & SW_I2C_MSK_DAT_IN) ? 1 : 0; -} - -static int smi_i2c_getscl(void *data, u32 sw_ctl) -{ - struct smi_dev *dev = data; - /* set as input.*/ - smi_clear(sw_ctl, SW_I2C_MSK_CLK_EN); - udelay(1); - return (smi_read(sw_ctl) & SW_I2C_MSK_CLK_IN) ? 1 : 0; -} -/* i2c 0.*/ -static void smi_i2c0_setsda(void *data, int state) -{ - struct smi_dev *dev = data; - - smi_i2c_setsda(dev, state, I2C_A_SW_CTL); -} - -static void smi_i2c0_setscl(void *data, int state) -{ - struct smi_dev *dev = data; - - smi_i2c_setscl(dev, state, I2C_A_SW_CTL); -} - -static int smi_i2c0_getsda(void *data) -{ - struct smi_dev *dev = data; - - return smi_i2c_getsda(dev, I2C_A_SW_CTL); -} - -static int smi_i2c0_getscl(void *data) -{ - struct smi_dev *dev = data; - - return smi_i2c_getscl(dev, I2C_A_SW_CTL); -} -/* i2c 1.*/ -static void smi_i2c1_setsda(void *data, int state) -{ - struct smi_dev *dev = data; - - smi_i2c_setsda(dev, state, I2C_B_SW_CTL); -} - -static void smi_i2c1_setscl(void *data, int state) -{ - struct smi_dev *dev = data; - - smi_i2c_setscl(dev, state, I2C_B_SW_CTL); -} - -static int smi_i2c1_getsda(void *data) -{ - struct smi_dev *dev = data; - - return smi_i2c_getsda(dev, I2C_B_SW_CTL); -} - -static int smi_i2c1_getscl(void *data) -{ - struct smi_dev *dev = data; - - return smi_i2c_getscl(dev, I2C_B_SW_CTL); -} - -static int smi_i2c_init(struct smi_dev *dev) -{ - int ret; - - /* i2c bus 0 */ - smi_i2c_cfg(dev, I2C_A_SW_CTL); - i2c_set_adapdata(&dev->i2c_bus[0], dev); - strcpy(dev->i2c_bus[0].name, "SMI-I2C0"); - dev->i2c_bus[0].owner = THIS_MODULE; - dev->i2c_bus[0].dev.parent = &dev->pci_dev->dev; - dev->i2c_bus[0].algo_data = &dev->i2c_bit[0]; - dev->i2c_bit[0].data = dev; - dev->i2c_bit[0].setsda = smi_i2c0_setsda; - dev->i2c_bit[0].setscl = smi_i2c0_setscl; - dev->i2c_bit[0].getsda = smi_i2c0_getsda; - dev->i2c_bit[0].getscl = smi_i2c0_getscl; - dev->i2c_bit[0].udelay = 12; - dev->i2c_bit[0].timeout = 10; - /* Raise SCL and SDA */ - smi_i2c0_setsda(dev, 1); - smi_i2c0_setscl(dev, 1); - - ret = i2c_bit_add_bus(&dev->i2c_bus[0]); - if (ret < 0) - return ret; - - /* i2c bus 1 */ - smi_i2c_cfg(dev, I2C_B_SW_CTL); - i2c_set_adapdata(&dev->i2c_bus[1], dev); - strcpy(dev->i2c_bus[1].name, "SMI-I2C1"); - dev->i2c_bus[1].owner = THIS_MODULE; - dev->i2c_bus[1].dev.parent = &dev->pci_dev->dev; - dev->i2c_bus[1].algo_data = &dev->i2c_bit[1]; - dev->i2c_bit[1].data = dev; - dev->i2c_bit[1].setsda = smi_i2c1_setsda; - dev->i2c_bit[1].setscl = smi_i2c1_setscl; - dev->i2c_bit[1].getsda = smi_i2c1_getsda; - dev->i2c_bit[1].getscl = smi_i2c1_getscl; - dev->i2c_bit[1].udelay = 12; - dev->i2c_bit[1].timeout = 10; - /* Raise SCL and SDA */ - smi_i2c1_setsda(dev, 1); - smi_i2c1_setscl(dev, 1); - - ret = i2c_bit_add_bus(&dev->i2c_bus[1]); - if (ret < 0) - i2c_del_adapter(&dev->i2c_bus[0]); - - return ret; -} - -static void smi_i2c_exit(struct smi_dev *dev) -{ - i2c_del_adapter(&dev->i2c_bus[0]); - i2c_del_adapter(&dev->i2c_bus[1]); -} - -static int smi_read_eeprom(struct i2c_adapter *i2c, u16 reg, u8 *data, u16 size) -{ - int ret; - u8 b0[2] = { (reg >> 8) & 0xff, reg & 0xff }; - - struct i2c_msg msg[] = { - { .addr = 0x50, .flags = 0, - .buf = b0, .len = 2 }, - { .addr = 0x50, .flags = I2C_M_RD, - .buf = data, .len = size } - }; - - ret = i2c_transfer(i2c, msg, 2); - - if (ret != 2) { - dev_err(&i2c->dev, "%s: reg=0x%x (error=%d)\n", - __func__, reg, ret); - return ret; - } - return ret; -} - -/* ts port interrupt operations */ -static void smi_port_disableInterrupt(struct smi_port *port) -{ - struct smi_dev *dev = port->dev; - - smi_write(MSI_INT_ENA_CLR, - (port->_dmaInterruptCH0 | port->_dmaInterruptCH1)); -} - -static void smi_port_enableInterrupt(struct smi_port *port) -{ - struct smi_dev *dev = port->dev; - - smi_write(MSI_INT_ENA_SET, - (port->_dmaInterruptCH0 | port->_dmaInterruptCH1)); -} - -static void smi_port_clearInterrupt(struct smi_port *port) -{ - struct smi_dev *dev = port->dev; - - smi_write(MSI_INT_STATUS_CLR, - (port->_dmaInterruptCH0 | port->_dmaInterruptCH1)); -} - -/* tasklet handler: DMA data to dmx.*/ -static void smi_dma_xfer(unsigned long data) -{ - struct smi_port *port = (struct smi_port *) data; - struct smi_dev *dev = port->dev; - u32 intr_status, finishedData, dmaManagement; - u8 dmaChan0State, dmaChan1State; - - intr_status = port->_int_status; - dmaManagement = smi_read(port->DMA_MANAGEMENT); - dmaChan0State = (u8)((dmaManagement & 0x00000030) >> 4); - dmaChan1State = (u8)((dmaManagement & 0x00300000) >> 20); - - /* CH-0 DMA interrupt.*/ - if ((intr_status & port->_dmaInterruptCH0) && (dmaChan0State == 0x01)) { - dev_dbg(&dev->pci_dev->dev, - "Port[%d]-DMA CH0 engine complete successful !\n", - port->idx); - finishedData = smi_read(port->DMA_CHAN0_TRANS_STATE); - finishedData &= 0x003FFFFF; - /* value of DMA_PORT0_CHAN0_TRANS_STATE register [21:0] - * indicate dma total transfer length and - * zero of [21:0] indicate dma total transfer length - * equal to 0x400000 (4MB)*/ - if (finishedData == 0) - finishedData = 0x00400000; - if (finishedData != SMI_TS_DMA_BUF_SIZE) { - dev_dbg(&dev->pci_dev->dev, - "DMA CH0 engine complete length mismatched, finish data=%d !\n", - finishedData); - } - dvb_dmx_swfilter_packets(&port->demux, - port->cpu_addr[0], (finishedData / 188)); - /*dvb_dmx_swfilter(&port->demux, - port->cpu_addr[0], finishedData);*/ - } - /* CH-1 DMA interrupt.*/ - if ((intr_status & port->_dmaInterruptCH1) && (dmaChan1State == 0x01)) { - dev_dbg(&dev->pci_dev->dev, - "Port[%d]-DMA CH1 engine complete successful !\n", - port->idx); - finishedData = smi_read(port->DMA_CHAN1_TRANS_STATE); - finishedData &= 0x003FFFFF; - /* value of DMA_PORT0_CHAN0_TRANS_STATE register [21:0] - * indicate dma total transfer length and - * zero of [21:0] indicate dma total transfer length - * equal to 0x400000 (4MB)*/ - if (finishedData == 0) - finishedData = 0x00400000; - if (finishedData != SMI_TS_DMA_BUF_SIZE) { - dev_dbg(&dev->pci_dev->dev, - "DMA CH1 engine complete length mismatched, finish data=%d !\n", - finishedData); - } - dvb_dmx_swfilter_packets(&port->demux, - port->cpu_addr[1], (finishedData / 188)); - /*dvb_dmx_swfilter(&port->demux, - port->cpu_addr[1], finishedData);*/ - } - /* restart DMA.*/ - if (intr_status & port->_dmaInterruptCH0) - dmaManagement |= 0x00000002; - if (intr_status & port->_dmaInterruptCH1) - dmaManagement |= 0x00020000; - smi_write(port->DMA_MANAGEMENT, dmaManagement); - /* Re-enable interrupts */ - smi_port_enableInterrupt(port); -} - -static void smi_port_dma_free(struct smi_port *port) -{ - if (port->cpu_addr[0]) { - pci_free_consistent(port->dev->pci_dev, SMI_TS_DMA_BUF_SIZE, - port->cpu_addr[0], port->dma_addr[0]); - port->cpu_addr[0] = NULL; - } - if (port->cpu_addr[1]) { - pci_free_consistent(port->dev->pci_dev, SMI_TS_DMA_BUF_SIZE, - port->cpu_addr[1], port->dma_addr[1]); - port->cpu_addr[1] = NULL; - } -} - -static int smi_port_init(struct smi_port *port, int dmaChanUsed) -{ - dev_dbg(&port->dev->pci_dev->dev, - "%s, port %d, dmaused %d\n", __func__, port->idx, dmaChanUsed); - port->enable = 0; - if (port->idx == 0) { - /* Port A */ - port->_dmaInterruptCH0 = dmaChanUsed & 0x01; - port->_dmaInterruptCH1 = dmaChanUsed & 0x02; - - port->DMA_CHAN0_ADDR_LOW = DMA_PORTA_CHAN0_ADDR_LOW; - port->DMA_CHAN0_ADDR_HI = DMA_PORTA_CHAN0_ADDR_HI; - port->DMA_CHAN0_TRANS_STATE = DMA_PORTA_CHAN0_TRANS_STATE; - port->DMA_CHAN0_CONTROL = DMA_PORTA_CHAN0_CONTROL; - port->DMA_CHAN1_ADDR_LOW = DMA_PORTA_CHAN1_ADDR_LOW; - port->DMA_CHAN1_ADDR_HI = DMA_PORTA_CHAN1_ADDR_HI; - port->DMA_CHAN1_TRANS_STATE = DMA_PORTA_CHAN1_TRANS_STATE; - port->DMA_CHAN1_CONTROL = DMA_PORTA_CHAN1_CONTROL; - port->DMA_MANAGEMENT = DMA_PORTA_MANAGEMENT; - } else { - /* Port B */ - port->_dmaInterruptCH0 = (dmaChanUsed << 2) & 0x04; - port->_dmaInterruptCH1 = (dmaChanUsed << 2) & 0x08; - - port->DMA_CHAN0_ADDR_LOW = DMA_PORTB_CHAN0_ADDR_LOW; - port->DMA_CHAN0_ADDR_HI = DMA_PORTB_CHAN0_ADDR_HI; - port->DMA_CHAN0_TRANS_STATE = DMA_PORTB_CHAN0_TRANS_STATE; - port->DMA_CHAN0_CONTROL = DMA_PORTB_CHAN0_CONTROL; - port->DMA_CHAN1_ADDR_LOW = DMA_PORTB_CHAN1_ADDR_LOW; - port->DMA_CHAN1_ADDR_HI = DMA_PORTB_CHAN1_ADDR_HI; - port->DMA_CHAN1_TRANS_STATE = DMA_PORTB_CHAN1_TRANS_STATE; - port->DMA_CHAN1_CONTROL = DMA_PORTB_CHAN1_CONTROL; - port->DMA_MANAGEMENT = DMA_PORTB_MANAGEMENT; - } - - if (port->_dmaInterruptCH0) { - port->cpu_addr[0] = pci_alloc_consistent(port->dev->pci_dev, - SMI_TS_DMA_BUF_SIZE, - &port->dma_addr[0]); - if (!port->cpu_addr[0]) { - dev_err(&port->dev->pci_dev->dev, - "Port[%d] DMA CH0 memory allocation failed!\n", - port->idx); - goto err; - } - } - - if (port->_dmaInterruptCH1) { - port->cpu_addr[1] = pci_alloc_consistent(port->dev->pci_dev, - SMI_TS_DMA_BUF_SIZE, - &port->dma_addr[1]); - if (!port->cpu_addr[1]) { - dev_err(&port->dev->pci_dev->dev, - "Port[%d] DMA CH1 memory allocation failed!\n", - port->idx); - goto err; - } - } - - smi_port_disableInterrupt(port); - tasklet_init(&port->tasklet, smi_dma_xfer, (unsigned long)port); - tasklet_disable(&port->tasklet); - port->enable = 1; - return 0; -err: - smi_port_dma_free(port); - return -ENOMEM; -} - -static void smi_port_exit(struct smi_port *port) -{ - smi_port_disableInterrupt(port); - tasklet_kill(&port->tasklet); - smi_port_dma_free(port); - port->enable = 0; -} - -static int smi_port_irq(struct smi_port *port, u32 int_status) -{ - u32 port_req_irq = port->_dmaInterruptCH0 | port->_dmaInterruptCH1; - int handled = 0; - - if (int_status & port_req_irq) { - smi_port_disableInterrupt(port); - port->_int_status = int_status; - smi_port_clearInterrupt(port); - tasklet_schedule(&port->tasklet); - handled = 1; - } - return handled; -} - -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]; - int handled = 0; - - u32 intr_status = smi_read(MSI_INT_STATUS); - - /* ts0 interrupt.*/ - if (dev->info->ts_0) - handled += smi_port_irq(port0, intr_status); - - /* ts1 interrupt.*/ - if (dev->info->ts_1) - handled += smi_port_irq(port1, intr_status); - - return IRQ_RETVAL(handled); -} - -static struct i2c_client *smi_add_i2c_client(struct i2c_adapter *adapter, - struct i2c_board_info *info) -{ - struct i2c_client *client; - - request_module(info->type); - client = i2c_new_device(adapter, info); - if (client == NULL || client->dev.driver == NULL) - goto err_add_i2c_client; - - if (!try_module_get(client->dev.driver->owner)) { - i2c_unregister_device(client); - goto err_add_i2c_client; - } - return client; - -err_add_i2c_client: - client = NULL; - return client; -} - -static void smi_del_i2c_client(struct i2c_client *client) -{ - module_put(client->dev.driver->owner); - i2c_unregister_device(client); -} - -static const struct m88ds3103_config smi_dvbsky_m88ds3103_cfg = { - .i2c_addr = 0x68, - .clock = 27000000, - .i2c_wr_max = 33, - .clock_out = 0, - .ts_mode = M88DS3103_TS_PARALLEL, - .ts_clk = 16000, - .ts_clk_pol = 1, - .agc = 0x99, - .lnb_hv_pol = 0, - .lnb_en_pol = 1, -}; - -static int smi_dvbsky_m88ds3103_fe_attach(struct smi_port *port) -{ - int ret = 0; - struct smi_dev *dev = port->dev; - struct i2c_adapter *i2c; - /* tuner I2C module */ - struct i2c_adapter *tuner_i2c_adapter; - struct i2c_client *tuner_client; - struct i2c_board_info tuner_info; - struct ts2020_config ts2020_config = {}; - memset(&tuner_info, 0, sizeof(struct i2c_board_info)); - i2c = (port->idx == 0) ? &dev->i2c_bus[0] : &dev->i2c_bus[1]; - - /* attach demod */ - port->fe = dvb_attach(m88ds3103_attach, - &smi_dvbsky_m88ds3103_cfg, i2c, &tuner_i2c_adapter); - if (!port->fe) { - ret = -ENODEV; - return ret; - } - /* attach tuner */ - ts2020_config.fe = port->fe; - strlcpy(tuner_info.type, "ts2020", I2C_NAME_SIZE); - tuner_info.addr = 0x60; - tuner_info.platform_data = &ts2020_config; - tuner_client = smi_add_i2c_client(tuner_i2c_adapter, &tuner_info); - if (!tuner_client) { - ret = -ENODEV; - goto err_tuner_i2c_device; - } - - /* delegate signal strength measurement to tuner */ - port->fe->ops.read_signal_strength = - port->fe->ops.tuner_ops.get_rf_strength; - - port->i2c_client_tuner = tuner_client; - return ret; - -err_tuner_i2c_device: - dvb_frontend_detach(port->fe); - return ret; -} - -static const struct m88ds3103_config smi_dvbsky_m88rs6000_cfg = { - .i2c_addr = 0x69, - .clock = 27000000, - .i2c_wr_max = 33, - .ts_mode = M88DS3103_TS_PARALLEL, - .ts_clk = 16000, - .ts_clk_pol = 1, - .agc = 0x99, - .lnb_hv_pol = 0, - .lnb_en_pol = 1, -}; - -static int smi_dvbsky_m88rs6000_fe_attach(struct smi_port *port) -{ - int ret = 0; - struct smi_dev *dev = port->dev; - struct i2c_adapter *i2c; - /* tuner I2C module */ - struct i2c_adapter *tuner_i2c_adapter; - struct i2c_client *tuner_client; - struct i2c_board_info tuner_info; - struct m88rs6000t_config m88rs6000t_config; - - memset(&tuner_info, 0, sizeof(struct i2c_board_info)); - i2c = (port->idx == 0) ? &dev->i2c_bus[0] : &dev->i2c_bus[1]; - - /* attach demod */ - port->fe = dvb_attach(m88ds3103_attach, - &smi_dvbsky_m88rs6000_cfg, i2c, &tuner_i2c_adapter); - if (!port->fe) { - ret = -ENODEV; - return ret; - } - /* attach tuner */ - m88rs6000t_config.fe = port->fe; - strlcpy(tuner_info.type, "m88rs6000t", I2C_NAME_SIZE); - tuner_info.addr = 0x21; - tuner_info.platform_data = &m88rs6000t_config; - tuner_client = smi_add_i2c_client(tuner_i2c_adapter, &tuner_info); - if (!tuner_client) { - ret = -ENODEV; - goto err_tuner_i2c_device; - } - - /* delegate signal strength measurement to tuner */ - port->fe->ops.read_signal_strength = - port->fe->ops.tuner_ops.get_rf_strength; - - port->i2c_client_tuner = tuner_client; - return ret; - -err_tuner_i2c_device: - dvb_frontend_detach(port->fe); - return ret; -} - -static int smi_dvbsky_sit2_fe_attach(struct smi_port *port) -{ - int ret = 0; - struct smi_dev *dev = port->dev; - struct i2c_adapter *i2c; - struct i2c_adapter *tuner_i2c_adapter; - struct i2c_client *client_tuner, *client_demod; - struct i2c_board_info client_info; - struct si2168_config si2168_config; - struct si2157_config si2157_config; - - /* select i2c bus */ - i2c = (port->idx == 0) ? &dev->i2c_bus[0] : &dev->i2c_bus[1]; - - /* attach demod */ - memset(&si2168_config, 0, sizeof(si2168_config)); - si2168_config.i2c_adapter = &tuner_i2c_adapter; - si2168_config.fe = &port->fe; - si2168_config.ts_mode = SI2168_TS_PARALLEL; - - memset(&client_info, 0, sizeof(struct i2c_board_info)); - strlcpy(client_info.type, "si2168", I2C_NAME_SIZE); - client_info.addr = 0x64; - client_info.platform_data = &si2168_config; - - client_demod = smi_add_i2c_client(i2c, &client_info); - if (!client_demod) { - ret = -ENODEV; - return ret; - } - port->i2c_client_demod = client_demod; - - /* attach tuner */ - memset(&si2157_config, 0, sizeof(si2157_config)); - si2157_config.fe = port->fe; - si2157_config.if_port = 1; - - memset(&client_info, 0, sizeof(struct i2c_board_info)); - strlcpy(client_info.type, "si2157", I2C_NAME_SIZE); - client_info.addr = 0x60; - client_info.platform_data = &si2157_config; - - client_tuner = smi_add_i2c_client(tuner_i2c_adapter, &client_info); - if (!client_tuner) { - smi_del_i2c_client(port->i2c_client_demod); - port->i2c_client_demod = NULL; - ret = -ENODEV; - return ret; - } - port->i2c_client_tuner = client_tuner; - return ret; -} - -static int smi_fe_init(struct smi_port *port) -{ - int ret = 0; - struct smi_dev *dev = port->dev; - struct dvb_adapter *adap = &port->dvb_adapter; - u8 mac_ee[16]; - - dev_dbg(&port->dev->pci_dev->dev, - "%s: port %d, fe_type = %d\n", - __func__, port->idx, port->fe_type); - switch (port->fe_type) { - case DVBSKY_FE_M88DS3103: - ret = smi_dvbsky_m88ds3103_fe_attach(port); - break; - case DVBSKY_FE_M88RS6000: - ret = smi_dvbsky_m88rs6000_fe_attach(port); - break; - case DVBSKY_FE_SIT2: - ret = smi_dvbsky_sit2_fe_attach(port); - break; - } - if (ret < 0) - return ret; - - /* register dvb frontend */ - ret = dvb_register_frontend(adap, port->fe); - if (ret < 0) { - if (port->i2c_client_tuner) - smi_del_i2c_client(port->i2c_client_tuner); - if (port->i2c_client_demod) - smi_del_i2c_client(port->i2c_client_demod); - dvb_frontend_detach(port->fe); - return ret; - } - /* init MAC.*/ - ret = smi_read_eeprom(&dev->i2c_bus[0], 0xc0, mac_ee, 16); - dev_info(&port->dev->pci_dev->dev, - "DVBSky SMI PCIe MAC= %pM\n", mac_ee + (port->idx)*8); - memcpy(adap->proposed_mac, mac_ee + (port->idx)*8, 6); - return ret; -} - -static void smi_fe_exit(struct smi_port *port) -{ - dvb_unregister_frontend(port->fe); - /* remove I2C demod and tuner */ - if (port->i2c_client_tuner) - smi_del_i2c_client(port->i2c_client_tuner); - if (port->i2c_client_demod) - smi_del_i2c_client(port->i2c_client_demod); - dvb_frontend_detach(port->fe); -} - -static int my_dvb_dmx_ts_card_init(struct dvb_demux *dvbdemux, char *id, - int (*start_feed)(struct dvb_demux_feed *), - int (*stop_feed)(struct dvb_demux_feed *), - void *priv) -{ - dvbdemux->priv = priv; - - dvbdemux->filternum = 256; - dvbdemux->feednum = 256; - dvbdemux->start_feed = start_feed; - dvbdemux->stop_feed = stop_feed; - dvbdemux->write_to_decoder = NULL; - dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | - DMX_SECTION_FILTERING | - DMX_MEMORY_BASED_FILTERING); - return dvb_dmx_init(dvbdemux); -} - -static int my_dvb_dmxdev_ts_card_init(struct dmxdev *dmxdev, - struct dvb_demux *dvbdemux, - struct dmx_frontend *hw_frontend, - struct dmx_frontend *mem_frontend, - struct dvb_adapter *dvb_adapter) -{ - int ret; - - dmxdev->filternum = 256; - dmxdev->demux = &dvbdemux->dmx; - dmxdev->capabilities = 0; - ret = dvb_dmxdev_init(dmxdev, dvb_adapter); - if (ret < 0) - return ret; - - hw_frontend->source = DMX_FRONTEND_0; - dvbdemux->dmx.add_frontend(&dvbdemux->dmx, hw_frontend); - mem_frontend->source = DMX_MEMORY_FE; - dvbdemux->dmx.add_frontend(&dvbdemux->dmx, mem_frontend); - return dvbdemux->dmx.connect_frontend(&dvbdemux->dmx, hw_frontend); -} - -static u32 smi_config_DMA(struct smi_port *port) -{ - struct smi_dev *dev = port->dev; - u32 totalLength = 0, dmaMemPtrLow, dmaMemPtrHi, dmaCtlReg; - u8 chanLatencyTimer = 0, dmaChanEnable = 1, dmaTransStart = 1; - u32 dmaManagement = 0, tlpTransUnit = DMA_TRANS_UNIT_188; - u8 tlpTc = 0, tlpTd = 1, tlpEp = 0, tlpAttr = 0; - u64 mem; - - dmaManagement = smi_read(port->DMA_MANAGEMENT); - /* Setup Channel-0 */ - if (port->_dmaInterruptCH0) { - totalLength = SMI_TS_DMA_BUF_SIZE; - mem = port->dma_addr[0]; - dmaMemPtrLow = mem & 0xffffffff; - dmaMemPtrHi = mem >> 32; - dmaCtlReg = (totalLength) | (tlpTransUnit << 22) | (tlpTc << 25) - | (tlpTd << 28) | (tlpEp << 29) | (tlpAttr << 30); - dmaManagement |= dmaChanEnable | (dmaTransStart << 1) - | (chanLatencyTimer << 8); - /* write DMA register, start DMA engine */ - smi_write(port->DMA_CHAN0_ADDR_LOW, dmaMemPtrLow); - smi_write(port->DMA_CHAN0_ADDR_HI, dmaMemPtrHi); - smi_write(port->DMA_CHAN0_CONTROL, dmaCtlReg); - } - /* Setup Channel-1 */ - if (port->_dmaInterruptCH1) { - totalLength = SMI_TS_DMA_BUF_SIZE; - mem = port->dma_addr[1]; - dmaMemPtrLow = mem & 0xffffffff; - dmaMemPtrHi = mem >> 32; - dmaCtlReg = (totalLength) | (tlpTransUnit << 22) | (tlpTc << 25) - | (tlpTd << 28) | (tlpEp << 29) | (tlpAttr << 30); - dmaManagement |= (dmaChanEnable << 16) | (dmaTransStart << 17) - | (chanLatencyTimer << 24); - /* write DMA register, start DMA engine */ - smi_write(port->DMA_CHAN1_ADDR_LOW, dmaMemPtrLow); - smi_write(port->DMA_CHAN1_ADDR_HI, dmaMemPtrHi); - smi_write(port->DMA_CHAN1_CONTROL, dmaCtlReg); - } - return dmaManagement; -} - -static int smi_start_feed(struct dvb_demux_feed *dvbdmxfeed) -{ - struct dvb_demux *dvbdmx = dvbdmxfeed->demux; - struct smi_port *port = dvbdmx->priv; - struct smi_dev *dev = port->dev; - u32 dmaManagement; - - if (port->users++ == 0) { - dmaManagement = smi_config_DMA(port); - smi_port_clearInterrupt(port); - smi_port_enableInterrupt(port); - smi_write(port->DMA_MANAGEMENT, dmaManagement); - tasklet_enable(&port->tasklet); - } - return port->users; -} - -static int smi_stop_feed(struct dvb_demux_feed *dvbdmxfeed) -{ - struct dvb_demux *dvbdmx = dvbdmxfeed->demux; - struct smi_port *port = dvbdmx->priv; - struct smi_dev *dev = port->dev; - - if (--port->users) - return port->users; - - tasklet_disable(&port->tasklet); - smi_port_disableInterrupt(port); - smi_clear(port->DMA_MANAGEMENT, 0x30003); - return 0; -} - -static int smi_dvb_init(struct smi_port *port) -{ - int ret; - struct dvb_adapter *adap = &port->dvb_adapter; - struct dvb_demux *dvbdemux = &port->demux; - - dev_dbg(&port->dev->pci_dev->dev, - "%s, port %d\n", __func__, port->idx); - - ret = dvb_register_adapter(adap, "SMI_DVB", THIS_MODULE, - &port->dev->pci_dev->dev, - adapter_nr); - if (ret < 0) { - dev_err(&port->dev->pci_dev->dev, "Fail to register DVB adapter.\n"); - return ret; - } - ret = my_dvb_dmx_ts_card_init(dvbdemux, "SW demux", - smi_start_feed, - smi_stop_feed, port); - if (ret < 0) - goto err_del_dvb_register_adapter; - - ret = my_dvb_dmxdev_ts_card_init(&port->dmxdev, &port->demux, - &port->hw_frontend, - &port->mem_frontend, adap); - if (ret < 0) - goto err_del_dvb_dmx; - - ret = dvb_net_init(adap, &port->dvbnet, port->dmxdev.demux); - if (ret < 0) - goto err_del_dvb_dmxdev; - return 0; -err_del_dvb_dmxdev: - dvbdemux->dmx.close(&dvbdemux->dmx); - dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &port->hw_frontend); - dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &port->mem_frontend); - dvb_dmxdev_release(&port->dmxdev); -err_del_dvb_dmx: - dvb_dmx_release(&port->demux); -err_del_dvb_register_adapter: - dvb_unregister_adapter(&port->dvb_adapter); - return ret; -} - -static void smi_dvb_exit(struct smi_port *port) -{ - struct dvb_demux *dvbdemux = &port->demux; - - dvb_net_release(&port->dvbnet); - - dvbdemux->dmx.close(&dvbdemux->dmx); - dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &port->hw_frontend); - dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &port->mem_frontend); - dvb_dmxdev_release(&port->dmxdev); - dvb_dmx_release(&port->demux); - - dvb_unregister_adapter(&port->dvb_adapter); -} - -static int smi_port_attach(struct smi_dev *dev, - struct smi_port *port, int index) -{ - int ret, dmachs; - - port->dev = dev; - port->idx = index; - port->fe_type = (index == 0) ? dev->info->fe_0 : dev->info->fe_1; - dmachs = (index == 0) ? dev->info->ts_0 : dev->info->ts_1; - /* port init.*/ - ret = smi_port_init(port, dmachs); - if (ret < 0) - return ret; - /* dvb init.*/ - ret = smi_dvb_init(port); - if (ret < 0) - goto err_del_port_init; - /* fe init.*/ - ret = smi_fe_init(port); - if (ret < 0) - goto err_del_dvb_init; - return 0; -err_del_dvb_init: - smi_dvb_exit(port); -err_del_port_init: - smi_port_exit(port); - return ret; -} - -static void smi_port_detach(struct smi_port *port) -{ - smi_fe_exit(port); - smi_dvb_exit(port); - smi_port_exit(port); -} - -static int smi_probe(struct pci_dev *pdev, const struct pci_device_id *id) -{ - struct smi_dev *dev; - int ret = -ENOMEM; - - if (pci_enable_device(pdev) < 0) - return -ENODEV; - - dev = kzalloc(sizeof(struct smi_dev), GFP_KERNEL); - if (!dev) { - ret = -ENOMEM; - goto err_pci_disable_device; - } - - dev->pci_dev = pdev; - pci_set_drvdata(pdev, dev); - dev->info = (struct smi_cfg_info *) id->driver_data; - dev_info(&dev->pci_dev->dev, - "card detected: %s\n", dev->info->name); - - dev->nr = dev->info->type; - dev->lmmio = ioremap(pci_resource_start(dev->pci_dev, 0), - pci_resource_len(dev->pci_dev, 0)); - if (!dev->lmmio) { - ret = -ENOMEM; - goto err_kfree; - } - - /* should we set to 32bit DMA? */ - ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); - if (ret < 0) - goto err_pci_iounmap; - - pci_set_master(pdev); - - ret = smi_hw_init(dev); - if (ret < 0) - goto err_pci_iounmap; - - ret = smi_i2c_init(dev); - if (ret < 0) - goto err_pci_iounmap; - - if (dev->info->ts_0) { - ret = smi_port_attach(dev, &dev->ts_port[0], 0); - if (ret < 0) - goto err_del_i2c_adaptor; - } - - if (dev->info->ts_1) { - ret = smi_port_attach(dev, &dev->ts_port[1], 1); - if (ret < 0) - goto err_del_port0_attach; - } - -#ifdef CONFIG_PCI_MSI /* to do msi interrupt.???*/ - if (pci_msi_enabled()) - ret = pci_enable_msi(dev->pci_dev); - if (ret) - dev_info(&dev->pci_dev->dev, "MSI not available.\n"); -#endif - - ret = request_irq(dev->pci_dev->irq, smi_irq_handler, - IRQF_SHARED, "SMI_PCIE", dev); - if (ret < 0) - goto err_del_port1_attach; - - return 0; - -err_del_port1_attach: - if (dev->info->ts_1) - smi_port_detach(&dev->ts_port[1]); -err_del_port0_attach: - if (dev->info->ts_0) - smi_port_detach(&dev->ts_port[0]); -err_del_i2c_adaptor: - smi_i2c_exit(dev); -err_pci_iounmap: - iounmap(dev->lmmio); -err_kfree: - pci_set_drvdata(pdev, NULL); - kfree(dev); -err_pci_disable_device: - pci_disable_device(pdev); - return ret; -} - -static void smi_remove(struct pci_dev *pdev) -{ - struct smi_dev *dev = pci_get_drvdata(pdev); - - smi_write(MSI_INT_ENA_CLR, ALL_INT); - free_irq(dev->pci_dev->irq, dev); -#ifdef CONFIG_PCI_MSI - pci_disable_msi(dev->pci_dev); -#endif - if (dev->info->ts_1) - smi_port_detach(&dev->ts_port[1]); - if (dev->info->ts_0) - smi_port_detach(&dev->ts_port[0]); - - smi_i2c_exit(dev); - iounmap(dev->lmmio); - pci_set_drvdata(pdev, NULL); - pci_disable_device(pdev); - kfree(dev); -} - -/* DVBSky cards */ -static struct smi_cfg_info dvbsky_s950_cfg = { - .type = SMI_DVBSKY_S950, - .name = "DVBSky S950 V3", - .ts_0 = SMI_TS_NULL, - .ts_1 = SMI_TS_DMA_BOTH, - .fe_0 = DVBSKY_FE_NULL, - .fe_1 = DVBSKY_FE_M88DS3103, -}; - -static struct smi_cfg_info dvbsky_s952_cfg = { - .type = SMI_DVBSKY_S952, - .name = "DVBSky S952 V3", - .ts_0 = SMI_TS_DMA_BOTH, - .ts_1 = SMI_TS_DMA_BOTH, - .fe_0 = DVBSKY_FE_M88RS6000, - .fe_1 = DVBSKY_FE_M88RS6000, -}; - -static struct smi_cfg_info dvbsky_t9580_cfg = { - .type = SMI_DVBSKY_T9580, - .name = "DVBSky T9580 V3", - .ts_0 = SMI_TS_DMA_BOTH, - .ts_1 = SMI_TS_DMA_BOTH, - .fe_0 = DVBSKY_FE_SIT2, - .fe_1 = DVBSKY_FE_M88DS3103, -}; - -/* PCI IDs */ -#define SMI_ID(_subvend, _subdev, _driverdata) { \ - .vendor = SMI_VID, .device = SMI_PID, \ - .subvendor = _subvend, .subdevice = _subdev, \ - .driver_data = (unsigned long)&_driverdata } - -static const struct pci_device_id smi_id_table[] = { - SMI_ID(0x4254, 0x0550, dvbsky_s950_cfg), - SMI_ID(0x4254, 0x0552, dvbsky_s952_cfg), - SMI_ID(0x4254, 0x5580, dvbsky_t9580_cfg), - {0} -}; -MODULE_DEVICE_TABLE(pci, smi_id_table); - -static struct pci_driver smipcie_driver = { - .name = "SMI PCIe driver", - .id_table = smi_id_table, - .probe = smi_probe, - .remove = smi_remove, -}; - -module_pci_driver(smipcie_driver); - -MODULE_AUTHOR("Max nibble "); -MODULE_DESCRIPTION("SMI PCIe driver"); -MODULE_LICENSE("GPL"); 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_ */ -- cgit v1.2.3 From 41cc14ba11d7d8532ef4f615c39eb6ebdcdeef61 Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Wed, 10 Jun 2015 13:32:44 -0300 Subject: [media] ttusb-dec: use swap() in swap_bytes() Use kernel.h macro definition. Thanks to Julia Lawall for Coccinelle scripting support. Signed-off-by: Fabian Frederick Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/ttusb-dec/ttusb_dec.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/media/usb/ttusb-dec/ttusb_dec.c b/drivers/media/usb/ttusb-dec/ttusb_dec.c index 322b53a4f1dd..7c3a7c55d969 100644 --- a/drivers/media/usb/ttusb-dec/ttusb_dec.c +++ b/drivers/media/usb/ttusb-dec/ttusb_dec.c @@ -593,14 +593,9 @@ static void ttusb_dec_process_packet(struct ttusb_dec *dec) static void swap_bytes(u8 *b, int length) { - u8 c; - length -= length % 2; - for (; length; b += 2, length -= 2) { - c = *b; - *b = *(b + 1); - *(b + 1) = c; - } + for (; length; b += 2, length -= 2) + swap(*b, *(b + 1)); } static void ttusb_dec_process_urb_frame(struct ttusb_dec *dec, u8 *b, -- cgit v1.2.3 From fb8dfda980bdaf178a7addb0a4f68574e390a9d9 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 23 Jun 2015 06:20:23 -0300 Subject: [media] v4l2-event: v4l2_event_queue: do nothing if vdev == NULL If the vdev pointer == NULL, then just return. This makes it easier for subdev drivers to use this function without having to check if the sd->devnode pointer is NULL or not. Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-event.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/v4l2-core/v4l2-event.c b/drivers/media/v4l2-core/v4l2-event.c index 8761aab99de9..8d3171c6bee8 100644 --- a/drivers/media/v4l2-core/v4l2-event.c +++ b/drivers/media/v4l2-core/v4l2-event.c @@ -172,6 +172,9 @@ void v4l2_event_queue(struct video_device *vdev, const struct v4l2_event *ev) unsigned long flags; struct timespec timestamp; + if (vdev == NULL) + return; + ktime_get_ts(×tamp); spin_lock_irqsave(&vdev->fh_lock, flags); -- cgit v1.2.3 From 0975626d08b623ad42c89a6294b7c2f8391c69af Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 24 Jun 2015 13:50:27 -0300 Subject: [media] adv7604: Add support for control event notifications Allow userspace applications to subscribe to control change events. This can e.g. be used to monitor the 5V detect control to be notified when a source is connected or disconnected. Signed-off-by: Lars-Peter Clausen Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv7604.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index 21b549a8dc74..a7d47e079343 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -42,6 +42,7 @@ #include #include #include +#include #include #include @@ -2362,6 +2363,8 @@ static const struct v4l2_ctrl_ops adv76xx_ctrl_ops = { static const struct v4l2_subdev_core_ops adv76xx_core_ops = { .log_status = adv76xx_log_status, .interrupt_service_routine = adv76xx_isr, + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = adv76xx_g_register, .s_register = adv76xx_s_register, @@ -3047,7 +3050,7 @@ static int adv76xx_probe(struct i2c_client *client, snprintf(sd->name, sizeof(sd->name), "%s %d-%04x", id->name, i2c_adapter_id(client->adapter), client->addr); - sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; /* Configure IO Regmap region */ err = configure_regmap(state, ADV76XX_PAGE_IO); -- cgit v1.2.3 From aef5159ffc6bbf65d77af3a2e5b3d377c7c4f37a Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 24 Jun 2015 13:50:28 -0300 Subject: [media] adv7842: Add support for control event notifications Allow userspace applications to subscribe to control change events. This can e.g. be used to monitor the 5V detect control to be notified when a source is connected or disconnected. Signed-off-by: Lars-Peter Clausen Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv7842.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index aa0d1a04543b..0535463d2fed 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -3015,6 +3016,8 @@ static const struct v4l2_subdev_core_ops adv7842_core_ops = { .log_status = adv7842_log_status, .ioctl = adv7842_ioctl, .interrupt_service_routine = adv7842_isr, + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = adv7842_g_register, .s_register = adv7842_s_register, @@ -3210,7 +3213,7 @@ static int adv7842_probe(struct i2c_client *client, sd = &state->sd; v4l2_i2c_subdev_init(sd, client, &adv7842_ops); - sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; state->mode = pdata->mode; state->hdmi_port_a = pdata->input == ADV7842_SELECT_HDMI_PORT_A; -- cgit v1.2.3 From 8ae5640f1b4af2e903802f771ba6a8f0c3497cab Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 24 Jun 2015 13:50:29 -0300 Subject: [media] Add helper function for subdev event notifications Add a new helper function called v4l2_subdev_notify_event() which will deliver the specified event to both the v4l2 subdev event queue as well as to the notify callback. The former is typically used by userspace applications to listen to notification events while the later is used by bridge drivers. Combining both into the same function avoids boilerplate code in subdev drivers. Signed-off-by: Lars-Peter Clausen Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-subdev.c | 18 ++++++++++++++++++ include/media/v4l2-subdev.h | 4 ++++ 2 files changed, 22 insertions(+) diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 63596063b213..83615b8fb46a 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -588,3 +588,21 @@ void v4l2_subdev_init(struct v4l2_subdev *sd, const struct v4l2_subdev_ops *ops) #endif } EXPORT_SYMBOL(v4l2_subdev_init); + +/** + * v4l2_subdev_notify_event() - Delivers event notification for subdevice + * @sd: The subdev for which to deliver the event + * @ev: The event to deliver + * + * Will deliver the specified event to all userspace event listeners which are + * subscribed to the v42l subdev event queue as well as to the bridge driver + * using the notify callback. The notification type for the notify callback + * will be V4L2_DEVICE_NOTIFY_EVENT. + */ +void v4l2_subdev_notify_event(struct v4l2_subdev *sd, + const struct v4l2_event *ev) +{ + v4l2_event_queue(sd->devnode, ev); + v4l2_subdev_notify(sd, V4L2_DEVICE_NOTIFY_EVENT, (void *)ev); +} +EXPORT_SYMBOL_GPL(v4l2_subdev_notify_event); diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 4e18318eb425..370fc38c34f1 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -44,6 +44,7 @@ struct v4l2_device; struct v4l2_ctrl_handler; +struct v4l2_event; struct v4l2_event_subscription; struct v4l2_fh; struct v4l2_subdev; @@ -695,4 +696,7 @@ void v4l2_subdev_init(struct v4l2_subdev *sd, #define v4l2_subdev_has_op(sd, o, f) \ ((sd)->ops->o && (sd)->ops->o->f) +void v4l2_subdev_notify_event(struct v4l2_subdev *sd, + const struct v4l2_event *ev); + #endif -- cgit v1.2.3 From 6f5bcfc30e1918c6029ebfdc9ed5002d618ab3de Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 24 Jun 2015 13:50:30 -0300 Subject: [media] adv7604: Deliver resolution change events to userspace Use the new v4l2_subdev_notify_event() helper function to deliver the resolution change event to userspace via the v4l2 subdev event queue as well as to the bridge driver using the callback notify mechanism. This allows userspace applications to react to changes in resolution. This is useful and often necessary for video pipelines where there is no direct 1-to-1 relationship between the subdevice converter and the video capture device and hence it does not make sense to directly forward the event to the video capture device node. Signed-off-by: Lars-Peter Clausen [hans.verkuil@cisco.com: fix obvious mistake: v4l2_event_subdev_unsubscribe -> v4l2_ctrl_subdev_subscribe_event] Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv7604.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index a7d47e079343..bfb0b6a72bbc 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -1767,8 +1767,8 @@ static int adv76xx_s_routing(struct v4l2_subdev *sd, select_input(sd); enable_input(sd); - v4l2_subdev_notify(sd, V4L2_DEVICE_NOTIFY_EVENT, - (void *)&adv76xx_ev_fmt); + v4l2_subdev_notify_event(sd, &adv76xx_ev_fmt); + return 0; } @@ -1935,8 +1935,7 @@ static int adv76xx_isr(struct v4l2_subdev *sd, u32 status, bool *handled) "%s: fmt_change = 0x%x, fmt_change_digital = 0x%x\n", __func__, fmt_change, fmt_change_digital); - v4l2_subdev_notify(sd, V4L2_DEVICE_NOTIFY_EVENT, - (void *)&adv76xx_ev_fmt); + v4l2_subdev_notify_event(sd, &adv76xx_ev_fmt); if (handled) *handled = true; @@ -2354,6 +2353,20 @@ static int adv76xx_log_status(struct v4l2_subdev *sd) return 0; } +static int adv76xx_subscribe_event(struct v4l2_subdev *sd, + struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_SOURCE_CHANGE: + return v4l2_src_change_event_subdev_subscribe(sd, fh, sub); + case V4L2_EVENT_CTRL: + return v4l2_ctrl_subdev_subscribe_event(sd, fh, sub); + default: + return -EINVAL; + } +} + /* ----------------------------------------------------------------------- */ static const struct v4l2_ctrl_ops adv76xx_ctrl_ops = { @@ -2363,7 +2376,7 @@ static const struct v4l2_ctrl_ops adv76xx_ctrl_ops = { static const struct v4l2_subdev_core_ops adv76xx_core_ops = { .log_status = adv76xx_log_status, .interrupt_service_routine = adv76xx_isr, - .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .subscribe_event = adv76xx_subscribe_event, .unsubscribe_event = v4l2_event_subdev_unsubscribe, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = adv76xx_g_register, -- cgit v1.2.3 From 2cf4090fc8db8f9c2f3778a6a3768357a2b6aa6e Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 24 Jun 2015 13:50:31 -0300 Subject: [media] adv7842: Deliver resolution change events to userspace Use the new v4l2_subdev_notify_event() helper function to deliver the resolution change event to userspace via the v4l2 subdev event queue as well as to the bridge driver using the callback notify mechanism. This allows userspace applications to react to changes in resolution. This is useful and often necessary for video pipelines where there is no direct 1-to-1 relationship between the subdevice converter and the video capture device and hence it does not make sense to directly forward the event to the video capture device node. Signed-off-by: Lars-Peter Clausen [hans.verkuil@cisco.com: fix obvious mistake: v4l2_event_subdev_unsubscribe -> v4l2_ctrl_subdev_subscribe_event] Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv7842.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index 0535463d2fed..897d68c78b06 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -1981,8 +1981,7 @@ static int adv7842_s_routing(struct v4l2_subdev *sd, select_input(sd, state->vid_std_select); enable_input(sd); - v4l2_subdev_notify(sd, V4L2_DEVICE_NOTIFY_EVENT, - (void *)&adv7842_ev_fmt); + v4l2_subdev_notify_event(sd, &adv7842_ev_fmt); return 0; } @@ -2215,8 +2214,7 @@ static int adv7842_isr(struct v4l2_subdev *sd, u32 status, bool *handled) "%s: fmt_change_cp = 0x%x, fmt_change_digital = 0x%x, fmt_change_sdp = 0x%x\n", __func__, fmt_change_cp, fmt_change_digital, fmt_change_sdp); - v4l2_subdev_notify(sd, V4L2_DEVICE_NOTIFY_EVENT, - (void *)&adv7842_ev_fmt); + v4l2_subdev_notify_event(sd, &adv7842_ev_fmt); if (handled) *handled = true; } @@ -3006,6 +3004,20 @@ static long adv7842_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) return -ENOTTY; } +static int adv7842_subscribe_event(struct v4l2_subdev *sd, + struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_SOURCE_CHANGE: + return v4l2_src_change_event_subdev_subscribe(sd, fh, sub); + case V4L2_EVENT_CTRL: + return v4l2_ctrl_subdev_subscribe_event(sd, fh, sub); + default: + return -EINVAL; + } +} + /* ----------------------------------------------------------------------- */ static const struct v4l2_ctrl_ops adv7842_ctrl_ops = { @@ -3016,7 +3028,7 @@ static const struct v4l2_subdev_core_ops adv7842_core_ops = { .log_status = adv7842_log_status, .ioctl = adv7842_ioctl, .interrupt_service_routine = adv7842_isr, - .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .subscribe_event = adv7842_subscribe_event, .unsubscribe_event = v4l2_event_subdev_unsubscribe, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = adv7842_g_register, -- cgit v1.2.3 From 86e46aa80d7456663afeac51971d4234dbc59e5d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 2 Jul 2015 10:32:39 -0300 Subject: [media] DocBook: fix media-ioc-device-info.xml type The documentation had two media_version entries. The second one was a typo and it should be driver_version instead. Correct this. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/media-ioc-device-info.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/DocBook/media/v4l/media-ioc-device-info.xml b/Documentation/DocBook/media/v4l/media-ioc-device-info.xml index 2ce521419e67..b0a21ac300b8 100644 --- a/Documentation/DocBook/media/v4l/media-ioc-device-info.xml +++ b/Documentation/DocBook/media/v4l/media-ioc-device-info.xml @@ -102,7 +102,7 @@ __u32 - media_version + driver_version Media device driver version, formatted with the KERNEL_VERSION() macro. Together with the driver field this identifies a particular -- cgit v1.2.3 From ee5da769b3f238f818045e7630ed9ee3788690bc Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 8 Jul 2015 05:47:08 -0300 Subject: [media] DocBook media: fix typo in V4L2_CTRL_FLAG_EXECUTE_ON_WRITE Fix small typo (missing 'it') in the documentation for V4L2_CTRL_FLAG_EXECUTE_ON_WRITE. Signed-off-by: Hans Verkuil Acked-by: Ricardo Ribalda Delgado Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/vidioc-queryctrl.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/DocBook/media/v4l/vidioc-queryctrl.xml b/Documentation/DocBook/media/v4l/vidioc-queryctrl.xml index dc83ad70f8dc..6ec39c698baf 100644 --- a/Documentation/DocBook/media/v4l/vidioc-queryctrl.xml +++ b/Documentation/DocBook/media/v4l/vidioc-queryctrl.xml @@ -616,7 +616,7 @@ pointer to memory containing the payload of the control. V4L2_CTRL_FLAG_EXECUTE_ON_WRITE 0x0200 The value provided to the control will be propagated to the driver -even if remains constant. This is required when the control represents an action +even if it remains constant. This is required when the control represents an action on the hardware. For example: clearing an error flag or triggering the flash. All the controls of the type V4L2_CTRL_TYPE_BUTTON have this flag set. -- cgit v1.2.3 From 2f8e75d2762496bb2fcea7fa437a3339d2a6d9d4 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 29 Jun 2015 10:45:56 -0300 Subject: [media] adv7604/cobalt: Allow compile test if !GPIOLIB The GPIO subsystem provides dummy GPIO consumer functions if GPIOLIB is not enabled. Hence drivers that depend on GPIOLIB, but use GPIO consumer functionality only, can still be compiled if GPIOLIB is not enabled. Relax the dependency of VIDEO_ADV7604 and VIDEO_COBALT (the latter selects the former) on GPIOLIB if COMPILE_TEST is enabled. Signed-off-by: Geert Uytterhoeven Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/Kconfig | 3 ++- drivers/media/pci/cobalt/Kconfig | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 71ee8f586430..8d1268648fe0 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -196,7 +196,8 @@ config VIDEO_ADV7183 config VIDEO_ADV7604 tristate "Analog Devices ADV7604 decoder" - depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && GPIOLIB + depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API + depends on GPIOLIB || COMPILE_TEST select HDMI ---help--- Support for the Analog Devices ADV7604 video decoder. 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 -- cgit v1.2.3 From f47c9045643f91e76d8a9030828b9fe1cf4a6bcf Mon Sep 17 00:00:00 2001 From: Benoit Parrot Date: Mon, 29 Jun 2015 18:19:06 -0300 Subject: [media] media: am437x-vpfe: Requested frame size and fmt overwritten by current sensor setting Upon a S_FMT the input/requested frame size and pixel format is overwritten by the current sub-device settings. Fix this so application can actually set the frame size and format. Fixes: 417d2e507edc ("[media] media: platform: add VPFE capture driver support for AM437X") Cc: # v4.0+ Signed-off-by: Benoit Parrot Acked-by: Lad, Prabhakar Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/am437x/am437x-vpfe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/am437x/am437x-vpfe.c b/drivers/media/platform/am437x/am437x-vpfe.c index 1fba339cddc1..1fed7a56c8ae 100644 --- a/drivers/media/platform/am437x/am437x-vpfe.c +++ b/drivers/media/platform/am437x/am437x-vpfe.c @@ -1565,7 +1565,7 @@ static int vpfe_s_fmt(struct file *file, void *priv, return -EBUSY; } - ret = vpfe_try_fmt(file, priv, fmt); + ret = vpfe_try_fmt(file, priv, &format); if (ret) return ret; -- cgit v1.2.3 From 890024ad144902bfa637f23b94b396701a88ed88 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Fri, 3 Jul 2015 16:11:41 -0300 Subject: [media] stk1160: Reduce driver verbosity These messages are not really informational, and just makes the driver's output too verbose. This commit changes some messages to a debug level, removes a really useless "driver loaded" message and finally undefines the DEBUG macro. Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/stk1160/stk1160-core.c | 5 +---- drivers/media/usb/stk1160/stk1160-v4l.c | 16 ++++++++-------- drivers/media/usb/stk1160/stk1160.h | 1 - 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/drivers/media/usb/stk1160/stk1160-core.c b/drivers/media/usb/stk1160/stk1160-core.c index 03504dcf3c52..1b6836f15370 100644 --- a/drivers/media/usb/stk1160/stk1160-core.c +++ b/drivers/media/usb/stk1160/stk1160-core.c @@ -162,7 +162,7 @@ static void stk1160_release(struct v4l2_device *v4l2_dev) { struct stk1160 *dev = container_of(v4l2_dev, struct stk1160, v4l2_dev); - stk1160_info("releasing all resources\n"); + stk1160_dbg("releasing all resources\n"); stk1160_i2c_unregister(dev); @@ -363,9 +363,6 @@ static int stk1160_probe(struct usb_interface *interface, dev->sd_saa7115 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, "saa7115_auto", 0, saa7113_addrs); - stk1160_info("driver ver %s successfully loaded\n", - STK1160_VERSION); - /* i2c reset saa711x */ v4l2_device_call_all(&dev->v4l2_dev, 0, core, reset, 0); v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0); diff --git a/drivers/media/usb/stk1160/stk1160-v4l.c b/drivers/media/usb/stk1160/stk1160-v4l.c index 7291cca58704..b4b737b0b35a 100644 --- a/drivers/media/usb/stk1160/stk1160-v4l.c +++ b/drivers/media/usb/stk1160/stk1160-v4l.c @@ -136,7 +136,7 @@ static bool stk1160_set_alternate(struct stk1160 *dev) dev->alt = i; } - stk1160_info("setting alternate %d\n", dev->alt); + stk1160_dbg("setting alternate %d\n", dev->alt); if (dev->alt != prev_alt) { stk1160_dbg("minimum isoc packet size: %u (alt=%d)\n", @@ -226,7 +226,7 @@ static void stk1160_stop_hw(struct stk1160 *dev) /* set alternate 0 */ dev->alt = 0; - stk1160_info("setting alternate %d\n", dev->alt); + stk1160_dbg("setting alternate %d\n", dev->alt); usb_set_interface(dev->udev, 0, 0); /* Stop stk1160 */ @@ -540,8 +540,8 @@ static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *v4l_fmt, sizes[0] = size; - stk1160_info("%s: buffer count %d, each %ld bytes\n", - __func__, *nbuffers, size); + stk1160_dbg("%s: buffer count %d, each %ld bytes\n", + __func__, *nbuffers, size); return 0; } @@ -625,8 +625,8 @@ void stk1160_clear_queue(struct stk1160 *dev) struct stk1160_buffer, list); list_del(&buf->list); vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); - stk1160_info("buffer [%p/%d] aborted\n", - buf, buf->vb.v4l2_buf.index); + stk1160_dbg("buffer [%p/%d] aborted\n", + buf, buf->vb.v4l2_buf.index); } /* It's important to release the current buffer */ @@ -635,8 +635,8 @@ void stk1160_clear_queue(struct stk1160 *dev) dev->isoc_ctl.buf = NULL; vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); - stk1160_info("buffer [%p/%d] aborted\n", - buf, buf->vb.v4l2_buf.index); + stk1160_dbg("buffer [%p/%d] aborted\n", + buf, buf->vb.v4l2_buf.index); } spin_unlock_irqrestore(&dev->buf_lock, flags); } diff --git a/drivers/media/usb/stk1160/stk1160.h b/drivers/media/usb/stk1160/stk1160.h index 3922a6cabde2..72cc8e8cbef7 100644 --- a/drivers/media/usb/stk1160/stk1160.h +++ b/drivers/media/usb/stk1160/stk1160.h @@ -58,7 +58,6 @@ * new drivers should use. * */ -#define DEBUG #ifdef DEBUG #define stk1160_dbg(fmt, args...) \ printk(KERN_DEBUG "stk1160: " fmt, ## args) -- cgit v1.2.3 From d3194520e2790591b5fabeb913dd74af908ca160 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Fri, 3 Jul 2015 16:11:42 -0300 Subject: [media] stk1160: Add frame scaling support This commit implements frame decimation for stk1160, which allows to support format changes instead of a static frame size. The stk1160 supports independent row and column decimation, in two different modes: * set a number of rows/columns units to skip for each unit sent. * set a number of rows/columns units to send for each unit skipped. This effectively allows to achieve different frame scaling ratios. The unit number can be set to either two row/columns sent/skipped, or four row/columns sent/skipped. Since the video format (UYVY) has 4-bytes, using a unit number of two row/columns results in frame color 'shifting', so set to four row/columns sent/skipped. Signed-off-by: Michael Stegemann Signed-off-by: Dale Hamel Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/stk1160/stk1160-reg.h | 34 ++++++ drivers/media/usb/stk1160/stk1160-v4l.c | 201 +++++++++++++++++++++++++++----- 2 files changed, 207 insertions(+), 28 deletions(-) diff --git a/drivers/media/usb/stk1160/stk1160-reg.h b/drivers/media/usb/stk1160/stk1160-reg.h index 3e49da6e7edd..81ff3a15d96e 100644 --- a/drivers/media/usb/stk1160/stk1160-reg.h +++ b/drivers/media/usb/stk1160/stk1160-reg.h @@ -33,6 +33,40 @@ */ #define STK1160_DCTRL 0x100 +/* + * Decimation Control Register: + * Byte 104: Horizontal Decimation Line Unit Count + * Byte 105: Vertical Decimation Line Unit Count + * Byte 106: Decimation Control + * Bit 0 - Horizontal Decimation Control + * 0 Horizontal decimation is disabled. + * 1 Horizontal decimation is enabled. + * Bit 1 - Decimates Half or More Column + * 0 Decimates less than half from original column, + * send count unit (0x105) before each unit skipped. + * 1 Decimates half or more from original column, + * skip count unit (0x105) before each unit sent. + * Bit 2 - Vertical Decimation Control + * 0 Vertical decimation is disabled. + * 1 Vertical decimation is enabled. + * Bit 3 - Vertical Greater or Equal to Half + * 0 Decimates less than half from original row, + * send count unit (0x105) before each unit skipped. + * 1 Decimates half or more from original row, + * skip count unit (0x105) before each unit sent. + * Bit 4 - Decimation Unit + * 0 Decimation will work with 2 rows or columns per unit. + * 1 Decimation will work with 4 rows or columns per unit. + */ +#define STK1160_DMCTRL_H_UNITS 0x104 +#define STK1160_DMCTRL_V_UNITS 0x105 +#define STK1160_DMCTRL 0x106 +#define STK1160_H_DEC_EN BIT(0) +#define STK1160_H_DEC_MODE BIT(1) +#define STK1160_V_DEC_EN BIT(2) +#define STK1160_V_DEC_MODE BIT(3) +#define STK1160_DEC_UNIT_SIZE BIT(4) + /* Capture Frame Start Position */ #define STK116_CFSPO 0x110 #define STK116_CFSPO_STX_L 0x110 diff --git a/drivers/media/usb/stk1160/stk1160-v4l.c b/drivers/media/usb/stk1160/stk1160-v4l.c index b4b737b0b35a..e12b10352871 100644 --- a/drivers/media/usb/stk1160/stk1160-v4l.c +++ b/drivers/media/usb/stk1160/stk1160-v4l.c @@ -42,6 +42,17 @@ static bool keep_buffers; module_param(keep_buffers, bool, 0644); MODULE_PARM_DESC(keep_buffers, "don't release buffers upon stop streaming"); +enum stk1160_decimate_mode { + STK1160_DECIMATE_MORE_THAN_HALF, + STK1160_DECIMATE_LESS_THAN_HALF, +}; + +struct stk1160_decimate_ctrl { + bool col_en, row_en; + enum stk1160_decimate_mode col_mode, row_mode; + unsigned int col_n, row_n; +}; + /* supported video standards */ static struct stk1160_fmt format[] = { { @@ -51,6 +62,19 @@ static struct stk1160_fmt format[] = { } }; +/* + * Helper to find the next divisor that results in modulo being zero. + * This is required to guarantee valid decimation unit counts. + */ +static unsigned int +div_round_integer(unsigned int x, unsigned int y) +{ + for (;; y++) { + if (x % y == 0) + return x / y; + } +} + static void stk1160_set_std(struct stk1160 *dev) { int i; @@ -106,6 +130,41 @@ static void stk1160_set_std(struct stk1160 *dev) } +static void stk1160_set_fmt(struct stk1160 *dev, + struct stk1160_decimate_ctrl *ctrl) +{ + u32 val = 0; + + if (ctrl) { + /* + * Since the format is UYVY, the device must skip or send + * a number of rows/columns multiple of four. This way, the + * colour format is preserved. The STK1160_DEC_UNIT_SIZE bit + * does exactly this. + */ + val |= STK1160_DEC_UNIT_SIZE; + val |= ctrl->col_en ? STK1160_H_DEC_EN : 0; + val |= ctrl->row_en ? STK1160_V_DEC_EN : 0; + val |= ctrl->col_mode == + STK1160_DECIMATE_MORE_THAN_HALF ? + STK1160_H_DEC_MODE : 0; + val |= ctrl->row_mode == + STK1160_DECIMATE_MORE_THAN_HALF ? + STK1160_V_DEC_MODE : 0; + + /* Horizontal count units */ + stk1160_write_reg(dev, STK1160_DMCTRL_H_UNITS, ctrl->col_n); + /* Vertical count units */ + stk1160_write_reg(dev, STK1160_DMCTRL_V_UNITS, ctrl->row_n); + + stk1160_dbg("decimate 0x%x, column units %d, row units %d\n", + val, ctrl->col_n, ctrl->row_n); + } + + /* Decimation control */ + stk1160_write_reg(dev, STK1160_DMCTRL, val); +} + /* * Set a new alternate setting. * Returns true is dev->max_pkt_size has changed, false otherwise. @@ -323,41 +382,134 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, return 0; } -static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) +static int stk1160_try_fmt(struct stk1160 *dev, struct v4l2_format *f, + struct stk1160_decimate_ctrl *ctrl) { - struct stk1160 *dev = video_drvdata(file); + unsigned int width, height; + unsigned int base_width, base_height; + unsigned int col_n, row_n; + enum stk1160_decimate_mode col_mode, row_mode; + bool col_en, row_en; + + base_width = 720; + base_height = (dev->norm & V4L2_STD_525_60) ? 480 : 576; + + /* Minimum width and height is 5% the frame size */ + width = clamp_t(unsigned int, f->fmt.pix.width, + base_width / 20, base_width); + height = clamp_t(unsigned int, f->fmt.pix.height, + base_height / 20, base_height); + + /* Let's set default no decimation values */ + col_n = 0; + row_n = 0; + col_en = false; + row_en = false; + f->fmt.pix.width = base_width; + f->fmt.pix.height = base_height; + row_mode = STK1160_DECIMATE_LESS_THAN_HALF; + col_mode = STK1160_DECIMATE_LESS_THAN_HALF; + + if (width < base_width && width > base_width / 2) { + /* + * The device will send count units for each + * unit skipped. This means count unit is: + * + * n = width / (frame width - width) + * + * And the width is: + * + * width = (n / n + 1) * frame width + */ + col_n = div_round_integer(width, base_width - width); + if (col_n > 0 && col_n <= 255) { + col_en = true; + col_mode = STK1160_DECIMATE_LESS_THAN_HALF; + f->fmt.pix.width = (base_width * col_n) / (col_n + 1); + } - /* - * User can't choose size at his own will, - * so we just return him the current size chosen - * at standard selection. - * TODO: Implement frame scaling? - */ + } else if (width <= base_width / 2) { + + /* + * The device will skip count units for each + * unit sent. This means count is: + * + * n = (frame width / width) - 1 + * + * And the width is: + * + * width = frame width / (n + 1) + */ + col_n = div_round_integer(base_width, width) - 1; + if (col_n > 0 && col_n <= 255) { + col_en = true; + col_mode = STK1160_DECIMATE_MORE_THAN_HALF; + f->fmt.pix.width = base_width / (col_n + 1); + } + } + + if (height < base_height && height > base_height / 2) { + row_n = div_round_integer(height, base_height - height); + if (row_n > 0 && row_n <= 255) { + row_en = true; + row_mode = STK1160_DECIMATE_LESS_THAN_HALF; + f->fmt.pix.height = (base_height * row_n) / (row_n + 1); + } + + } else if (height <= base_height / 2) { + row_n = div_round_integer(base_height, height) - 1; + if (row_n > 0 && row_n <= 255) { + row_en = true; + row_mode = STK1160_DECIMATE_MORE_THAN_HALF; + f->fmt.pix.height = base_height / (row_n + 1); + } + } f->fmt.pix.pixelformat = dev->fmt->fourcc; - f->fmt.pix.width = dev->width; - f->fmt.pix.height = dev->height; f->fmt.pix.field = V4L2_FIELD_INTERLACED; - f->fmt.pix.bytesperline = dev->width * 2; - f->fmt.pix.sizeimage = dev->height * f->fmt.pix.bytesperline; + f->fmt.pix.bytesperline = f->fmt.pix.width * 2; + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + if (ctrl) { + ctrl->col_en = col_en; + ctrl->col_n = col_n; + ctrl->col_mode = col_mode; + ctrl->row_en = row_en; + ctrl->row_n = row_n; + ctrl->row_mode = row_mode; + } + + stk1160_dbg("width %d, height %d\n", + f->fmt.pix.width, f->fmt.pix.height); return 0; } +static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct stk1160 *dev = video_drvdata(file); + + return stk1160_try_fmt(dev, f, NULL); +} + static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct stk1160 *dev = video_drvdata(file); struct vb2_queue *q = &dev->vb_vidq; + struct stk1160_decimate_ctrl ctrl; + int rc; if (vb2_is_busy(q)) return -EBUSY; - vidioc_try_fmt_vid_cap(file, priv, f); - - /* We don't support any format changes */ + rc = stk1160_try_fmt(dev, f, &ctrl); + if (rc < 0) + return rc; + dev->width = f->fmt.pix.width; + dev->height = f->fmt.pix.height; + stk1160_set_fmt(dev, &ctrl); return 0; } @@ -393,22 +545,15 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm) return -ENODEV; /* We need to set this now, before we call stk1160_set_std */ + dev->width = 720; + dev->height = (norm & V4L2_STD_525_60) ? 480 : 576; dev->norm = norm; - /* This is taken from saa7115 video decoder */ - if (dev->norm & V4L2_STD_525_60) { - dev->width = 720; - dev->height = 480; - } else if (dev->norm & V4L2_STD_625_50) { - dev->width = 720; - dev->height = 576; - } else { - stk1160_err("invalid standard\n"); - return -EINVAL; - } - stk1160_set_std(dev); + /* Calling with NULL disables frame decimation */ + stk1160_set_fmt(dev, NULL); + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_std, dev->norm); -- cgit v1.2.3 From 3d8bffe88316c0c8b5edad633fc8a25cae644bb4 Mon Sep 17 00:00:00 2001 From: Fabien Dessenne Date: Fri, 10 Jul 2015 05:29:22 -0300 Subject: [media] bdisp: composing support Support the composing (at VIDEO_CAPTURE) with the _selection API. v4l2-compliance successfully run ("test Composing: OK") Signed-off-by: Fabien Dessenne Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sti/bdisp/bdisp-hw.c | 8 +-- drivers/media/platform/sti/bdisp/bdisp-v4l2.c | 76 ++++++++++++++++++--------- 2 files changed, 55 insertions(+), 29 deletions(-) diff --git a/drivers/media/platform/sti/bdisp/bdisp-hw.c b/drivers/media/platform/sti/bdisp/bdisp-hw.c index 465828e859e2..c83f9c2fbe3c 100644 --- a/drivers/media/platform/sti/bdisp/bdisp-hw.c +++ b/drivers/media/platform/sti/bdisp/bdisp-hw.c @@ -336,8 +336,8 @@ static int bdisp_hw_get_hv_inc(struct bdisp_ctx *ctx, u16 *h_inc, u16 *v_inc) src_w = ctx->src.crop.width; src_h = ctx->src.crop.height; - dst_w = ctx->dst.width; - dst_h = ctx->dst.height; + dst_w = ctx->dst.crop.width; + dst_h = ctx->dst.crop.height; if (bdisp_hw_get_inc(src_w, dst_w, h_inc) || bdisp_hw_get_inc(src_h, dst_h, v_inc)) { @@ -483,9 +483,9 @@ static void bdisp_hw_build_node(struct bdisp_ctx *ctx, src_rect.width -= src_x_offset; src_rect.width = min_t(__s32, MAX_SRC_WIDTH, src_rect.width); - dst_x_offset = (src_x_offset * dst->width) / ctx->src.crop.width; + dst_x_offset = (src_x_offset * dst_width) / ctx->src.crop.width; dst_rect.left += dst_x_offset; - dst_rect.width = (src_rect.width * dst->width) / ctx->src.crop.width; + dst_rect.width = (src_rect.width * dst_width) / ctx->src.crop.width; /* General */ src_fmt = src->fmt->pixelformat; diff --git a/drivers/media/platform/sti/bdisp/bdisp-v4l2.c b/drivers/media/platform/sti/bdisp/bdisp-v4l2.c index 9e782ebe18da..df61355b46f1 100644 --- a/drivers/media/platform/sti/bdisp/bdisp-v4l2.c +++ b/drivers/media/platform/sti/bdisp/bdisp-v4l2.c @@ -851,33 +851,56 @@ static int bdisp_g_selection(struct file *file, void *fh, struct bdisp_frame *frame; struct bdisp_ctx *ctx = fh_to_ctx(fh); - if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) { - /* Composing / capture is not supported */ - dev_dbg(ctx->bdisp_dev->dev, "Not supported for capture\n"); - return -EINVAL; - } - frame = ctx_get_frame(ctx, s->type); if (IS_ERR(frame)) { dev_err(ctx->bdisp_dev->dev, "Invalid frame (%p)\n", frame); return PTR_ERR(frame); } - switch (s->target) { - case V4L2_SEL_TGT_CROP: - /* cropped frame */ - s->r = frame->crop; + switch (s->type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + switch (s->target) { + case V4L2_SEL_TGT_CROP: + /* cropped frame */ + s->r = frame->crop; + break; + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + /* complete frame */ + s->r.left = 0; + s->r.top = 0; + s->r.width = frame->width; + s->r.height = frame->height; + break; + default: + dev_err(ctx->bdisp_dev->dev, "Invalid target\n"); + return -EINVAL; + } break; - case V4L2_SEL_TGT_CROP_DEFAULT: - case V4L2_SEL_TGT_CROP_BOUNDS: - /* complete frame */ - s->r.left = 0; - s->r.top = 0; - s->r.width = frame->width; - s->r.height = frame->height; + + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + switch (s->target) { + case V4L2_SEL_TGT_COMPOSE: + case V4L2_SEL_TGT_COMPOSE_PADDED: + /* composed (cropped) frame */ + s->r = frame->crop; + break; + case V4L2_SEL_TGT_COMPOSE_DEFAULT: + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + /* complete frame */ + s->r.left = 0; + s->r.top = 0; + s->r.width = frame->width; + s->r.height = frame->height; + break; + default: + dev_err(ctx->bdisp_dev->dev, "Invalid target\n"); + return -EINVAL; + } break; + default: - dev_dbg(ctx->bdisp_dev->dev, "Invalid target\n"); + dev_err(ctx->bdisp_dev->dev, "Invalid type\n"); return -EINVAL; } @@ -906,15 +929,18 @@ static int bdisp_s_selection(struct file *file, void *fh, struct bdisp_frame *frame; struct bdisp_ctx *ctx = fh_to_ctx(fh); struct v4l2_rect *in, out; + bool valid = false; - if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) { - /* Composing / capture is not supported */ - dev_dbg(ctx->bdisp_dev->dev, "Not supported for capture\n"); - return -EINVAL; - } + if ((s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) && + (s->target == V4L2_SEL_TGT_CROP)) + valid = true; + + if ((s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) && + (s->target == V4L2_SEL_TGT_COMPOSE)) + valid = true; - if (s->target != V4L2_SEL_TGT_CROP) { - dev_dbg(ctx->bdisp_dev->dev, "Invalid target\n"); + if (!valid) { + dev_err(ctx->bdisp_dev->dev, "Invalid type / target\n"); return -EINVAL; } -- cgit v1.2.3 From 24310279eb2851a2bdef9b267b43b98e252ce435 Mon Sep 17 00:00:00 2001 From: Fabien Dessenne Date: Fri, 10 Jul 2015 05:29:37 -0300 Subject: [media] bdisp: add debug info for RGB24 format Add this missing debug information Signed-off-by: Fabien Dessenne Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sti/bdisp/bdisp-debug.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/media/platform/sti/bdisp/bdisp-debug.c b/drivers/media/platform/sti/bdisp/bdisp-debug.c index 18282a0f80c9..79c56356a7c7 100644 --- a/drivers/media/platform/sti/bdisp/bdisp-debug.c +++ b/drivers/media/platform/sti/bdisp/bdisp-debug.c @@ -116,6 +116,9 @@ static void bdisp_dbg_dump_tty(struct seq_file *s, u32 val) case BDISP_RGB565: seq_puts(s, "RGB565 - "); break; + case BDISP_RGB888: + seq_puts(s, "RGB888 - "); + break; case BDISP_XRGB8888: seq_puts(s, "xRGB888 - "); break; @@ -185,6 +188,9 @@ static void bdisp_dbg_dump_sty(struct seq_file *s, case BDISP_RGB565: seq_puts(s, "RGB565 - "); break; + case BDISP_RGB888: + seq_puts(s, "RGB888 - "); + break; case BDISP_XRGB8888: seq_puts(s, "xRGB888 - "); break; @@ -420,6 +426,8 @@ static const char *bdisp_fmt_to_str(struct bdisp_frame frame) return "NV12"; case V4L2_PIX_FMT_RGB565: return "RGB16"; + case V4L2_PIX_FMT_RGB24: + return "RGB24"; case V4L2_PIX_FMT_XBGR32: return "XRGB"; case V4L2_PIX_FMT_ABGR32: -- cgit v1.2.3 From bde2b96d6dfb1c14e5d27a4e7e7d492e9be102cd Mon Sep 17 00:00:00 2001 From: Fabien Dessenne Date: Mon, 13 Jul 2015 06:54:11 -0300 Subject: [media] bdisp: fix debug info memory access bdisp_dev->dbg.copy_node shall be a copy of (and not point to) bdisp_ctx->node, since this resource is freed upon driver release. Signed-off-by: Fabien Dessenne Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sti/bdisp/bdisp-hw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/sti/bdisp/bdisp-hw.c b/drivers/media/platform/sti/bdisp/bdisp-hw.c index c83f9c2fbe3c..052c932ac942 100644 --- a/drivers/media/platform/sti/bdisp/bdisp-hw.c +++ b/drivers/media/platform/sti/bdisp/bdisp-hw.c @@ -768,12 +768,12 @@ static void bdisp_hw_save_request(struct bdisp_ctx *ctx) /* Allocate memory if not done yet */ if (!copy_node[i]) { copy_node[i] = devm_kzalloc(ctx->bdisp_dev->dev, - sizeof(*copy_node), + sizeof(*copy_node[i]), GFP_KERNEL); if (!copy_node[i]) return; } - copy_node[i] = node[i]; + *copy_node[i] = *node[i]; } } -- cgit v1.2.3 From d32d98642de66048f9534a05f3641558e811bbc9 Mon Sep 17 00:00:00 2001 From: Mats Randgaard Date: Thu, 9 Jul 2015 05:45:47 -0300 Subject: [media] Driver for Toshiba TC358743 HDMI to CSI-2 bridge The driver is tested on our hardware and all the implemented features works as expected. Missing features: - CEC support - HDCP repeater support - IR support Signed-off-by: Mats Randgaard [hans.verkuil@cisco.com: updated copyright year to 2015] [hans.verkuil@cisco.com: update confusing confctl_mutex comment] Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 7 + drivers/media/i2c/Kconfig | 9 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/tc358743.c | 1778 ++++++++++++++++++++++++++++++++++++ drivers/media/i2c/tc358743_regs.h | 681 ++++++++++++++ include/media/tc358743.h | 131 +++ include/uapi/linux/v4l2-controls.h | 4 + 7 files changed, 2611 insertions(+) create mode 100644 drivers/media/i2c/tc358743.c create mode 100644 drivers/media/i2c/tc358743_regs.h create mode 100644 include/media/tc358743.h diff --git a/MAINTAINERS b/MAINTAINERS index fd6078443083..2bb989be111d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10319,6 +10319,13 @@ F: drivers/char/toshiba.c F: include/linux/toshiba.h F: include/uapi/linux/toshiba.h +TOSHIBA TC358743 DRIVER +M: Mats Randgaard +L: linux-media@vger.kernel.org +S: Maintained +F: drivers/media/i2c/tc358743* +F: include/media/tc358743.h + TMIO MMC DRIVER M: Ian Molton L: linux-mmc@vger.kernel.org diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 8d1268648fe0..0e0490d60e7e 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -287,6 +287,15 @@ config VIDEO_SAA711X To compile this driver as a module, choose M here: the module will be called saa7115. +config VIDEO_TC358743 + tristate "Toshiba TC358743 decoder" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for the Toshiba TC358743 HDMI to MIPI CSI-2 bridge. + + To compile this driver as a module, choose M here: the + module will be called tc358743. + config VIDEO_TVP514X tristate "Texas Instruments TVP514x video decoder" depends on VIDEO_V4L2 && I2C diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index f165faea5b3f..07db257abfc1 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -78,3 +78,4 @@ obj-$(CONFIG_VIDEO_AK881X) += ak881x.o obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o obj-$(CONFIG_VIDEO_ML86V7667) += ml86v7667.o obj-$(CONFIG_VIDEO_OV2659) += ov2659.o +obj-$(CONFIG_VIDEO_TC358743) += tc358743.o diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c new file mode 100644 index 000000000000..4e8811c3e771 --- /dev/null +++ b/drivers/media/i2c/tc358743.c @@ -0,0 +1,1778 @@ +/* + * tc358743 - Toshiba HDMI to CSI-2 bridge + * + * Copyright 2015 Cisco Systems, Inc. and/or its affiliates. All rights + * reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +/* + * References (c = chapter, p = page): + * REF_01 - Toshiba, TC358743XBG (H2C), Functional Specification, Rev 0.60 + * REF_02 - Toshiba, TC358743XBG_HDMI-CSI_Tv11p_nm.xls + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tc358743_regs.h" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "debug level (0-3)"); + +MODULE_DESCRIPTION("Toshiba TC358743 HDMI to CSI-2 bridge driver"); +MODULE_AUTHOR("Ramakrishnan Muthukrishnan "); +MODULE_AUTHOR("Mikhail Khelik "); +MODULE_AUTHOR("Mats Randgaard "); +MODULE_LICENSE("GPL"); + +#define EDID_NUM_BLOCKS_MAX 8 +#define EDID_BLOCK_SIZE 128 + +static const struct v4l2_dv_timings_cap tc358743_timings_cap = { + .type = V4L2_DV_BT_656_1120, + /* keep this initialization for compatibility with GCC < 4.4.6 */ + .reserved = { 0 }, + /* Pixel clock from REF_01 p. 20. Min/max height/width are unknown */ + V4L2_INIT_BT_TIMINGS(1, 10000, 1, 10000, 0, 165000000, + V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | + V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT, + V4L2_DV_BT_CAP_PROGRESSIVE | + V4L2_DV_BT_CAP_REDUCED_BLANKING | + V4L2_DV_BT_CAP_CUSTOM) +}; + +struct tc358743_state { + struct tc358743_platform_data pdata; + struct v4l2_subdev sd; + struct media_pad pad; + struct v4l2_ctrl_handler hdl; + struct i2c_client *i2c_client; + /* CONFCTL is modified in ops and tc358743_hdmi_sys_int_handler */ + struct mutex confctl_mutex; + + /* controls */ + struct v4l2_ctrl *detect_tx_5v_ctrl; + struct v4l2_ctrl *audio_sampling_rate_ctrl; + struct v4l2_ctrl *audio_present_ctrl; + + /* work queues */ + struct workqueue_struct *work_queues; + struct delayed_work delayed_work_enable_hotplug; + + /* edid */ + u8 edid_blocks_written; + + struct v4l2_dv_timings timings; + u32 mbus_fmt_code; +}; + +static void tc358743_enable_interrupts(struct v4l2_subdev *sd, + bool cable_connected); +static int tc358743_s_ctrl_detect_tx_5v(struct v4l2_subdev *sd); + +static inline struct tc358743_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct tc358743_state, sd); +} + +/* --------------- I2C --------------- */ + +static void i2c_rd(struct v4l2_subdev *sd, u16 reg, u8 *values, u32 n) +{ + struct tc358743_state *state = to_state(sd); + struct i2c_client *client = state->i2c_client; + int err; + u8 buf[2] = { reg >> 8, reg & 0xff }; + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = 2, + .buf = buf, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = n, + .buf = values, + }, + }; + + err = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (err != ARRAY_SIZE(msgs)) { + v4l2_err(sd, "%s: reading register 0x%x from 0x%x failed\n", + __func__, reg, client->addr); + } +} + +static void i2c_wr(struct v4l2_subdev *sd, u16 reg, u8 *values, u32 n) +{ + struct tc358743_state *state = to_state(sd); + struct i2c_client *client = state->i2c_client; + int err, i; + struct i2c_msg msg; + u8 data[2 + n]; + + msg.addr = client->addr; + msg.buf = data; + msg.len = 2 + n; + msg.flags = 0; + + data[0] = reg >> 8; + data[1] = reg & 0xff; + + for (i = 0; i < n; i++) + data[2 + i] = values[i]; + + err = i2c_transfer(client->adapter, &msg, 1); + if (err != 1) { + v4l2_err(sd, "%s: writing register 0x%x from 0x%x failed\n", + __func__, reg, client->addr); + return; + } + + if (debug < 3) + return; + + switch (n) { + case 1: + v4l2_info(sd, "I2C write 0x%04x = 0x%02x", + reg, data[2]); + break; + case 2: + v4l2_info(sd, "I2C write 0x%04x = 0x%02x%02x", + reg, data[3], data[2]); + break; + case 4: + v4l2_info(sd, "I2C write 0x%04x = 0x%02x%02x%02x%02x", + reg, data[5], data[4], data[3], data[2]); + break; + default: + v4l2_info(sd, "I2C write %d bytes from address 0x%04x\n", + n, reg); + } +} + +static u8 i2c_rd8(struct v4l2_subdev *sd, u16 reg) +{ + u8 val; + + i2c_rd(sd, reg, &val, 1); + + return val; +} + +static void i2c_wr8(struct v4l2_subdev *sd, u16 reg, u8 val) +{ + i2c_wr(sd, reg, &val, 1); +} + +static void i2c_wr8_and_or(struct v4l2_subdev *sd, u16 reg, + u8 mask, u8 val) +{ + i2c_wr8(sd, reg, (i2c_rd8(sd, reg) & mask) | val); +} + +static u16 i2c_rd16(struct v4l2_subdev *sd, u16 reg) +{ + u16 val; + + i2c_rd(sd, reg, (u8 *)&val, 2); + + return val; +} + +static void i2c_wr16(struct v4l2_subdev *sd, u16 reg, u16 val) +{ + i2c_wr(sd, reg, (u8 *)&val, 2); +} + +static void i2c_wr16_and_or(struct v4l2_subdev *sd, u16 reg, u16 mask, u16 val) +{ + i2c_wr16(sd, reg, (i2c_rd16(sd, reg) & mask) | val); +} + +static u32 i2c_rd32(struct v4l2_subdev *sd, u16 reg) +{ + u32 val; + + i2c_rd(sd, reg, (u8 *)&val, 4); + + return val; +} + +static void i2c_wr32(struct v4l2_subdev *sd, u16 reg, u32 val) +{ + i2c_wr(sd, reg, (u8 *)&val, 4); +} + +/* --------------- STATUS --------------- */ + +static inline bool is_hdmi(struct v4l2_subdev *sd) +{ + return i2c_rd8(sd, SYS_STATUS) & MASK_S_HDMI; +} + +static inline bool tx_5v_power_present(struct v4l2_subdev *sd) +{ + return i2c_rd8(sd, SYS_STATUS) & MASK_S_DDC5V; +} + +static inline bool no_signal(struct v4l2_subdev *sd) +{ + return !(i2c_rd8(sd, SYS_STATUS) & MASK_S_TMDS); +} + +static inline bool no_sync(struct v4l2_subdev *sd) +{ + return !(i2c_rd8(sd, SYS_STATUS) & MASK_S_SYNC); +} + +static inline bool audio_present(struct v4l2_subdev *sd) +{ + return i2c_rd8(sd, AU_STATUS0) & MASK_S_A_SAMPLE; +} + +static int get_audio_sampling_rate(struct v4l2_subdev *sd) +{ + static const int code_to_rate[] = { + 44100, 0, 48000, 32000, 22050, 384000, 24000, 352800, + 88200, 768000, 96000, 705600, 176400, 0, 192000, 0 + }; + + /* Register FS_SET is not cleared when the cable is disconnected */ + if (no_signal(sd)) + return 0; + + return code_to_rate[i2c_rd8(sd, FS_SET) & MASK_FS]; +} + +static unsigned tc358743_num_csi_lanes_in_use(struct v4l2_subdev *sd) +{ + return ((i2c_rd32(sd, CSI_CONTROL) & MASK_NOL) >> 1) + 1; +} + +/* --------------- TIMINGS --------------- */ + +static inline unsigned fps(const struct v4l2_bt_timings *t) +{ + if (!V4L2_DV_BT_FRAME_HEIGHT(t) || !V4L2_DV_BT_FRAME_WIDTH(t)) + return 0; + + return DIV_ROUND_CLOSEST((unsigned)t->pixelclock, + V4L2_DV_BT_FRAME_HEIGHT(t) * V4L2_DV_BT_FRAME_WIDTH(t)); +} + +static int tc358743_get_detected_timings(struct v4l2_subdev *sd, + struct v4l2_dv_timings *timings) +{ + struct v4l2_bt_timings *bt = &timings->bt; + unsigned width, height, frame_width, frame_height, frame_interval, fps; + + memset(timings, 0, sizeof(struct v4l2_dv_timings)); + + if (no_signal(sd)) { + v4l2_dbg(1, debug, sd, "%s: no valid signal\n", __func__); + return -ENOLINK; + } + if (no_sync(sd)) { + v4l2_dbg(1, debug, sd, "%s: no sync on signal\n", __func__); + return -ENOLCK; + } + + timings->type = V4L2_DV_BT_656_1120; + bt->interlaced = i2c_rd8(sd, VI_STATUS1) & MASK_S_V_INTERLACE ? + V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE; + + width = ((i2c_rd8(sd, DE_WIDTH_H_HI) & 0x1f) << 8) + + i2c_rd8(sd, DE_WIDTH_H_LO); + height = ((i2c_rd8(sd, DE_WIDTH_V_HI) & 0x1f) << 8) + + i2c_rd8(sd, DE_WIDTH_V_LO); + frame_width = ((i2c_rd8(sd, H_SIZE_HI) & 0x1f) << 8) + + i2c_rd8(sd, H_SIZE_LO); + frame_height = (((i2c_rd8(sd, V_SIZE_HI) & 0x3f) << 8) + + i2c_rd8(sd, V_SIZE_LO)) / 2; + /* frame interval in milliseconds * 10 + * Require SYS_FREQ0 and SYS_FREQ1 are precisely set */ + frame_interval = ((i2c_rd8(sd, FV_CNT_HI) & 0x3) << 8) + + i2c_rd8(sd, FV_CNT_LO); + fps = (frame_interval > 0) ? + DIV_ROUND_CLOSEST(10000, frame_interval) : 0; + + bt->width = width; + bt->height = height; + bt->vsync = frame_height - height; + bt->hsync = frame_width - width; + bt->pixelclock = frame_width * frame_height * fps; + if (bt->interlaced == V4L2_DV_INTERLACED) { + bt->height *= 2; + bt->il_vsync = bt->vsync + 1; + bt->pixelclock /= 2; + } + + return 0; +} + +/* --------------- HOTPLUG / HDCP / EDID --------------- */ + +static void tc358743_delayed_work_enable_hotplug(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct tc358743_state *state = container_of(dwork, + struct tc358743_state, delayed_work_enable_hotplug); + struct v4l2_subdev *sd = &state->sd; + + v4l2_dbg(2, debug, sd, "%s:\n", __func__); + + i2c_wr8_and_or(sd, HPD_CTL, ~MASK_HPD_OUT0, MASK_HPD_OUT0); +} + +static void tc358743_set_hdmi_hdcp(struct v4l2_subdev *sd, bool enable) +{ + v4l2_dbg(2, debug, sd, "%s: %s\n", __func__, enable ? + "enable" : "disable"); + + i2c_wr8_and_or(sd, HDCP_REG1, + ~(MASK_AUTH_UNAUTH_SEL | MASK_AUTH_UNAUTH), + MASK_AUTH_UNAUTH_SEL_16_FRAMES | MASK_AUTH_UNAUTH_AUTO); + + i2c_wr8_and_or(sd, HDCP_REG2, ~MASK_AUTO_P3_RESET, + SET_AUTO_P3_RESET_FRAMES(0x0f)); + + /* HDCP is disabled by configuring the receiver as HDCP repeater. The + * repeater mode require software support to work, so HDCP + * authentication will fail. + */ + i2c_wr8_and_or(sd, HDCP_REG3, ~KEY_RD_CMD, enable ? KEY_RD_CMD : 0); + i2c_wr8_and_or(sd, HDCP_MODE, ~(MASK_AUTO_CLR | MASK_MODE_RST_TN), + enable ? (MASK_AUTO_CLR | MASK_MODE_RST_TN) : 0); + + /* Apple MacBook Pro gen.8 has a bug that makes it freeze every fifth + * second when HDCP is disabled, but the MAX_EXCED bit is handled + * correctly and HDCP is disabled on the HDMI output. + */ + i2c_wr8_and_or(sd, BSTATUS1, ~MASK_MAX_EXCED, + enable ? 0 : MASK_MAX_EXCED); + i2c_wr8_and_or(sd, BCAPS, ~(MASK_REPEATER | MASK_READY), + enable ? 0 : MASK_REPEATER | MASK_READY); +} + +static void tc358743_disable_edid(struct v4l2_subdev *sd) +{ + struct tc358743_state *state = to_state(sd); + + v4l2_dbg(2, debug, sd, "%s:\n", __func__); + + cancel_delayed_work_sync(&state->delayed_work_enable_hotplug); + + /* DDC access to EDID is also disabled when hotplug is disabled. See + * register DDC_CTL */ + i2c_wr8_and_or(sd, HPD_CTL, ~MASK_HPD_OUT0, 0x0); +} + +static void tc358743_enable_edid(struct v4l2_subdev *sd) +{ + struct tc358743_state *state = to_state(sd); + + if (state->edid_blocks_written == 0) { + v4l2_dbg(2, debug, sd, "%s: no EDID -> no hotplug\n", __func__); + return; + } + + v4l2_dbg(2, debug, sd, "%s:\n", __func__); + + /* Enable hotplug after 100 ms. DDC access to EDID is also enabled when + * hotplug is enabled. See register DDC_CTL */ + queue_delayed_work(state->work_queues, + &state->delayed_work_enable_hotplug, HZ / 10); + + tc358743_enable_interrupts(sd, true); + tc358743_s_ctrl_detect_tx_5v(sd); +} + +static void tc358743_erase_bksv(struct v4l2_subdev *sd) +{ + int i; + + for (i = 0; i < 5; i++) + i2c_wr8(sd, BKSV + i, 0); +} + +/* --------------- AVI infoframe --------------- */ + +static void print_avi_infoframe(struct v4l2_subdev *sd) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct device *dev = &client->dev; + union hdmi_infoframe frame; + u8 buffer[HDMI_INFOFRAME_SIZE(AVI)]; + + if (!is_hdmi(sd)) { + v4l2_info(sd, "DVI-D signal - AVI infoframe not supported\n"); + return; + } + + i2c_rd(sd, PK_AVI_0HEAD, buffer, HDMI_INFOFRAME_SIZE(AVI)); + + if (hdmi_infoframe_unpack(&frame, buffer) < 0) { + v4l2_err(sd, "%s: unpack of AVI infoframe failed\n", __func__); + return; + } + + hdmi_infoframe_log(KERN_INFO, dev, &frame); +} + +/* --------------- CTRLS --------------- */ + +static int tc358743_s_ctrl_detect_tx_5v(struct v4l2_subdev *sd) +{ + struct tc358743_state *state = to_state(sd); + + return v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl, + tx_5v_power_present(sd)); +} + +static int tc358743_s_ctrl_audio_sampling_rate(struct v4l2_subdev *sd) +{ + struct tc358743_state *state = to_state(sd); + + return v4l2_ctrl_s_ctrl(state->audio_sampling_rate_ctrl, + get_audio_sampling_rate(sd)); +} + +static int tc358743_s_ctrl_audio_present(struct v4l2_subdev *sd) +{ + struct tc358743_state *state = to_state(sd); + + return v4l2_ctrl_s_ctrl(state->audio_present_ctrl, + audio_present(sd)); +} + +static int tc358743_update_controls(struct v4l2_subdev *sd) +{ + int ret = 0; + + ret |= tc358743_s_ctrl_detect_tx_5v(sd); + ret |= tc358743_s_ctrl_audio_sampling_rate(sd); + ret |= tc358743_s_ctrl_audio_present(sd); + + return ret; +} + +/* --------------- INIT --------------- */ + +static void tc358743_reset_phy(struct v4l2_subdev *sd) +{ + v4l2_dbg(1, debug, sd, "%s:\n", __func__); + + i2c_wr8_and_or(sd, PHY_RST, ~MASK_RESET_CTRL, 0); + i2c_wr8_and_or(sd, PHY_RST, ~MASK_RESET_CTRL, MASK_RESET_CTRL); +} + +static void tc358743_reset(struct v4l2_subdev *sd, uint16_t mask) +{ + u16 sysctl = i2c_rd16(sd, SYSCTL); + + i2c_wr16(sd, SYSCTL, sysctl | mask); + i2c_wr16(sd, SYSCTL, sysctl & ~mask); +} + +static inline void tc358743_sleep_mode(struct v4l2_subdev *sd, bool enable) +{ + i2c_wr16_and_or(sd, SYSCTL, ~MASK_SLEEP, + enable ? MASK_SLEEP : 0); +} + +static inline void enable_stream(struct v4l2_subdev *sd, bool enable) +{ + struct tc358743_state *state = to_state(sd); + + v4l2_dbg(3, debug, sd, "%s: %sable\n", + __func__, enable ? "en" : "dis"); + + if (enable) { + /* It is critical for CSI receiver to see lane transition + * LP11->HS. Set to non-continuous mode to enable clock lane + * LP11 state. */ + i2c_wr32(sd, TXOPTIONCNTRL, 0); + /* Set to continuous mode to trigger LP11->HS transition */ + i2c_wr32(sd, TXOPTIONCNTRL, MASK_CONTCLKMODE); + /* Unmute video */ + i2c_wr8(sd, VI_MUTE, MASK_AUTO_MUTE); + } else { + /* Mute video so that all data lanes go to LSP11 state. + * No data is output to CSI Tx block. */ + i2c_wr8(sd, VI_MUTE, MASK_AUTO_MUTE | MASK_VI_MUTE); + } + + mutex_lock(&state->confctl_mutex); + i2c_wr16_and_or(sd, CONFCTL, ~(MASK_VBUFEN | MASK_ABUFEN), + enable ? (MASK_VBUFEN | MASK_ABUFEN) : 0x0); + mutex_unlock(&state->confctl_mutex); +} + +static void tc358743_set_pll(struct v4l2_subdev *sd) +{ + struct tc358743_state *state = to_state(sd); + struct tc358743_platform_data *pdata = &state->pdata; + u16 pllctl0 = i2c_rd16(sd, PLLCTL0); + u16 pllctl1 = i2c_rd16(sd, PLLCTL1); + u16 pllctl0_new = SET_PLL_PRD(pdata->pll_prd) | + SET_PLL_FBD(pdata->pll_fbd); + u32 hsck = (pdata->refclk_hz / pdata->pll_prd) * pdata->pll_fbd; + + v4l2_dbg(2, debug, sd, "%s:\n", __func__); + + /* Only rewrite when needed (new value or disabled), since rewriting + * triggers another format change event. */ + if ((pllctl0 != pllctl0_new) || ((pllctl1 & MASK_PLL_EN) == 0)) { + u16 pll_frs; + + if (hsck > 500000000) + pll_frs = 0x0; + else if (hsck > 250000000) + pll_frs = 0x1; + else if (hsck > 125000000) + pll_frs = 0x2; + else + pll_frs = 0x3; + + v4l2_dbg(1, debug, sd, "%s: updating PLL clock\n", __func__); + tc358743_sleep_mode(sd, true); + i2c_wr16(sd, PLLCTL0, pllctl0_new); + i2c_wr16_and_or(sd, PLLCTL1, + ~(MASK_PLL_FRS | MASK_RESETB | MASK_PLL_EN), + (SET_PLL_FRS(pll_frs) | MASK_RESETB | + MASK_PLL_EN)); + udelay(10); /* REF_02, Sheet "Source HDMI" */ + i2c_wr16_and_or(sd, PLLCTL1, ~MASK_CKEN, MASK_CKEN); + tc358743_sleep_mode(sd, false); + } +} + +static void tc358743_set_ref_clk(struct v4l2_subdev *sd) +{ + struct tc358743_state *state = to_state(sd); + struct tc358743_platform_data *pdata = &state->pdata; + u32 sys_freq; + u32 lockdet_ref; + u16 fh_min; + u16 fh_max; + + BUG_ON(!(pdata->refclk_hz == 26000000 || + pdata->refclk_hz == 27000000 || + pdata->refclk_hz == 42000000)); + + sys_freq = pdata->refclk_hz / 10000; + i2c_wr8(sd, SYS_FREQ0, sys_freq & 0x00ff); + i2c_wr8(sd, SYS_FREQ1, (sys_freq & 0xff00) >> 8); + + i2c_wr8_and_or(sd, PHY_CTL0, ~MASK_PHY_SYSCLK_IND, + (pdata->refclk_hz == 42000000) ? + MASK_PHY_SYSCLK_IND : 0x0); + + fh_min = pdata->refclk_hz / 100000; + i2c_wr8(sd, FH_MIN0, fh_min & 0x00ff); + i2c_wr8(sd, FH_MIN1, (fh_min & 0xff00) >> 8); + + fh_max = (fh_min * 66) / 10; + i2c_wr8(sd, FH_MAX0, fh_max & 0x00ff); + i2c_wr8(sd, FH_MAX1, (fh_max & 0xff00) >> 8); + + lockdet_ref = pdata->refclk_hz / 100; + i2c_wr8(sd, LOCKDET_REF0, lockdet_ref & 0x0000ff); + i2c_wr8(sd, LOCKDET_REF1, (lockdet_ref & 0x00ff00) >> 8); + i2c_wr8(sd, LOCKDET_REF2, (lockdet_ref & 0x0f0000) >> 16); + + i2c_wr8_and_or(sd, NCO_F0_MOD, ~MASK_NCO_F0_MOD, + (pdata->refclk_hz == 27000000) ? + MASK_NCO_F0_MOD_27MHZ : 0x0); +} + +static void tc358743_set_csi_color_space(struct v4l2_subdev *sd) +{ + struct tc358743_state *state = to_state(sd); + + switch (state->mbus_fmt_code) { + case MEDIA_BUS_FMT_UYVY8_1X16: + v4l2_dbg(2, debug, sd, "%s: YCbCr 422 16-bit\n", __func__); + i2c_wr8_and_or(sd, VOUT_SET2, + ~(MASK_SEL422 | MASK_VOUT_422FIL_100) & 0xff, + MASK_SEL422 | MASK_VOUT_422FIL_100); + i2c_wr8_and_or(sd, VI_REP, ~MASK_VOUT_COLOR_SEL & 0xff, + MASK_VOUT_COLOR_601_YCBCR_LIMITED); + mutex_lock(&state->confctl_mutex); + i2c_wr16_and_or(sd, CONFCTL, ~MASK_YCBCRFMT, + MASK_YCBCRFMT_422_8_BIT); + mutex_unlock(&state->confctl_mutex); + break; + case MEDIA_BUS_FMT_RGB888_1X24: + v4l2_dbg(2, debug, sd, "%s: RGB 888 24-bit\n", __func__); + i2c_wr8_and_or(sd, VOUT_SET2, + ~(MASK_SEL422 | MASK_VOUT_422FIL_100) & 0xff, + 0x00); + i2c_wr8_and_or(sd, VI_REP, ~MASK_VOUT_COLOR_SEL & 0xff, + MASK_VOUT_COLOR_RGB_FULL); + mutex_lock(&state->confctl_mutex); + i2c_wr16_and_or(sd, CONFCTL, ~MASK_YCBCRFMT, 0); + mutex_unlock(&state->confctl_mutex); + break; + default: + v4l2_dbg(2, debug, sd, "%s: Unsupported format code 0x%x\n", + __func__, state->mbus_fmt_code); + } +} + +static unsigned tc358743_num_csi_lanes_needed(struct v4l2_subdev *sd) +{ + struct tc358743_state *state = to_state(sd); + struct v4l2_bt_timings *bt = &state->timings.bt; + struct tc358743_platform_data *pdata = &state->pdata; + u32 bits_pr_pixel = + (state->mbus_fmt_code == MEDIA_BUS_FMT_UYVY8_1X16) ? 16 : 24; + u32 bps = bt->width * bt->height * fps(bt) * bits_pr_pixel; + u32 bps_pr_lane = (pdata->refclk_hz / pdata->pll_prd) * pdata->pll_fbd; + + return DIV_ROUND_UP(bps, bps_pr_lane); +} + +static void tc358743_set_csi(struct v4l2_subdev *sd) +{ + struct tc358743_state *state = to_state(sd); + struct tc358743_platform_data *pdata = &state->pdata; + unsigned lanes = tc358743_num_csi_lanes_needed(sd); + + v4l2_dbg(3, debug, sd, "%s:\n", __func__); + + tc358743_reset(sd, MASK_CTXRST); + + if (lanes < 1) + i2c_wr32(sd, CLW_CNTRL, MASK_CLW_LANEDISABLE); + if (lanes < 1) + i2c_wr32(sd, D0W_CNTRL, MASK_D0W_LANEDISABLE); + if (lanes < 2) + i2c_wr32(sd, D1W_CNTRL, MASK_D1W_LANEDISABLE); + if (lanes < 3) + i2c_wr32(sd, D2W_CNTRL, MASK_D2W_LANEDISABLE); + if (lanes < 4) + i2c_wr32(sd, D3W_CNTRL, MASK_D3W_LANEDISABLE); + + i2c_wr32(sd, LINEINITCNT, pdata->lineinitcnt); + i2c_wr32(sd, LPTXTIMECNT, pdata->lptxtimecnt); + i2c_wr32(sd, TCLK_HEADERCNT, pdata->tclk_headercnt); + i2c_wr32(sd, TCLK_TRAILCNT, pdata->tclk_trailcnt); + i2c_wr32(sd, THS_HEADERCNT, pdata->ths_headercnt); + i2c_wr32(sd, TWAKEUP, pdata->twakeup); + i2c_wr32(sd, TCLK_POSTCNT, pdata->tclk_postcnt); + i2c_wr32(sd, THS_TRAILCNT, pdata->ths_trailcnt); + i2c_wr32(sd, HSTXVREGCNT, pdata->hstxvregcnt); + + i2c_wr32(sd, HSTXVREGEN, + ((lanes > 0) ? MASK_CLM_HSTXVREGEN : 0x0) | + ((lanes > 0) ? MASK_D0M_HSTXVREGEN : 0x0) | + ((lanes > 1) ? MASK_D1M_HSTXVREGEN : 0x0) | + ((lanes > 2) ? MASK_D2M_HSTXVREGEN : 0x0) | + ((lanes > 3) ? MASK_D3M_HSTXVREGEN : 0x0)); + + i2c_wr32(sd, TXOPTIONCNTRL, MASK_CONTCLKMODE); + i2c_wr32(sd, STARTCNTRL, MASK_START); + i2c_wr32(sd, CSI_START, MASK_STRT); + + i2c_wr32(sd, CSI_CONFW, MASK_MODE_SET | + MASK_ADDRESS_CSI_CONTROL | + MASK_CSI_MODE | + MASK_TXHSMD | + ((lanes == 4) ? MASK_NOL_4 : + (lanes == 3) ? MASK_NOL_3 : + (lanes == 2) ? MASK_NOL_2 : MASK_NOL_1)); + + i2c_wr32(sd, CSI_CONFW, MASK_MODE_SET | + MASK_ADDRESS_CSI_ERR_INTENA | MASK_TXBRK | MASK_QUNK | + MASK_WCER | MASK_INER); + + i2c_wr32(sd, CSI_CONFW, MASK_MODE_CLEAR | + MASK_ADDRESS_CSI_ERR_HALT | MASK_TXBRK | MASK_QUNK); + + i2c_wr32(sd, CSI_CONFW, MASK_MODE_SET | + MASK_ADDRESS_CSI_INT_ENA | MASK_INTER); +} + +static void tc358743_set_hdmi_phy(struct v4l2_subdev *sd) +{ + struct tc358743_state *state = to_state(sd); + struct tc358743_platform_data *pdata = &state->pdata; + + /* Default settings from REF_02, sheet "Source HDMI" + * and custom settings as platform data */ + i2c_wr8_and_or(sd, PHY_EN, ~MASK_ENABLE_PHY, 0x0); + i2c_wr8(sd, PHY_CTL1, SET_PHY_AUTO_RST1_US(1600) | + SET_FREQ_RANGE_MODE_CYCLES(1)); + i2c_wr8_and_or(sd, PHY_CTL2, ~MASK_PHY_AUTO_RSTn, + (pdata->hdmi_phy_auto_reset_tmds_detected ? + MASK_PHY_AUTO_RST2 : 0) | + (pdata->hdmi_phy_auto_reset_tmds_in_range ? + MASK_PHY_AUTO_RST3 : 0) | + (pdata->hdmi_phy_auto_reset_tmds_valid ? + MASK_PHY_AUTO_RST4 : 0)); + i2c_wr8(sd, PHY_BIAS, 0x40); + i2c_wr8(sd, PHY_CSQ, SET_CSQ_CNT_LEVEL(0x0a)); + i2c_wr8(sd, AVM_CTL, 45); + i2c_wr8_and_or(sd, HDMI_DET, ~MASK_HDMI_DET_V, + pdata->hdmi_detection_delay << 4); + i2c_wr8_and_or(sd, HV_RST, ~(MASK_H_PI_RST | MASK_V_PI_RST), + (pdata->hdmi_phy_auto_reset_hsync_out_of_range ? + MASK_H_PI_RST : 0) | + (pdata->hdmi_phy_auto_reset_vsync_out_of_range ? + MASK_V_PI_RST : 0)); + i2c_wr8_and_or(sd, PHY_EN, ~MASK_ENABLE_PHY, MASK_ENABLE_PHY); +} + +static void tc358743_set_hdmi_audio(struct v4l2_subdev *sd) +{ + struct tc358743_state *state = to_state(sd); + + /* Default settings from REF_02, sheet "Source HDMI" */ + i2c_wr8(sd, FORCE_MUTE, 0x00); + i2c_wr8(sd, AUTO_CMD0, MASK_AUTO_MUTE7 | MASK_AUTO_MUTE6 | + MASK_AUTO_MUTE5 | MASK_AUTO_MUTE4 | + MASK_AUTO_MUTE1 | MASK_AUTO_MUTE0); + i2c_wr8(sd, AUTO_CMD1, MASK_AUTO_MUTE9); + i2c_wr8(sd, AUTO_CMD2, MASK_AUTO_PLAY3 | MASK_AUTO_PLAY2); + i2c_wr8(sd, BUFINIT_START, SET_BUFINIT_START_MS(500)); + i2c_wr8(sd, FS_MUTE, 0x00); + i2c_wr8(sd, FS_IMODE, MASK_NLPCM_SMODE | MASK_FS_SMODE); + i2c_wr8(sd, ACR_MODE, MASK_CTS_MODE); + i2c_wr8(sd, ACR_MDF0, MASK_ACR_L2MDF_1976_PPM | MASK_ACR_L1MDF_976_PPM); + i2c_wr8(sd, ACR_MDF1, MASK_ACR_L3MDF_3906_PPM); + i2c_wr8(sd, SDO_MODE1, MASK_SDO_FMT_I2S); + i2c_wr8(sd, DIV_MODE, SET_DIV_DLY_MS(100)); + + mutex_lock(&state->confctl_mutex); + i2c_wr16_and_or(sd, CONFCTL, 0xffff, MASK_AUDCHNUM_2 | + MASK_AUDOUTSEL_I2S | MASK_AUTOINDEX); + mutex_unlock(&state->confctl_mutex); +} + +static void tc358743_set_hdmi_info_frame_mode(struct v4l2_subdev *sd) +{ + /* Default settings from REF_02, sheet "Source HDMI" */ + i2c_wr8(sd, PK_INT_MODE, MASK_ISRC2_INT_MODE | MASK_ISRC_INT_MODE | + MASK_ACP_INT_MODE | MASK_VS_INT_MODE | + MASK_SPD_INT_MODE | MASK_MS_INT_MODE | + MASK_AUD_INT_MODE | MASK_AVI_INT_MODE); + i2c_wr8(sd, NO_PKT_LIMIT, 0x2c); + i2c_wr8(sd, NO_PKT_CLR, 0x53); + i2c_wr8(sd, ERR_PK_LIMIT, 0x01); + i2c_wr8(sd, NO_PKT_LIMIT2, 0x30); + i2c_wr8(sd, NO_GDB_LIMIT, 0x10); +} + +static void tc358743_initial_setup(struct v4l2_subdev *sd) +{ + struct tc358743_state *state = to_state(sd); + struct tc358743_platform_data *pdata = &state->pdata; + + /* CEC and IR are not supported by this driver */ + i2c_wr16_and_or(sd, SYSCTL, ~(MASK_CECRST | MASK_IRRST), + (MASK_CECRST | MASK_IRRST)); + + tc358743_reset(sd, MASK_CTXRST | MASK_HDMIRST); + tc358743_sleep_mode(sd, false); + + i2c_wr16(sd, FIFOCTL, pdata->fifo_level); + + tc358743_set_ref_clk(sd); + + i2c_wr8_and_or(sd, DDC_CTL, ~MASK_DDC5V_MODE, + pdata->ddc5v_delay & MASK_DDC5V_MODE); + i2c_wr8_and_or(sd, EDID_MODE, ~MASK_EDID_MODE, MASK_EDID_MODE_E_DDC); + + tc358743_set_hdmi_phy(sd); + tc358743_set_hdmi_hdcp(sd, pdata->enable_hdcp); + tc358743_set_hdmi_audio(sd); + tc358743_set_hdmi_info_frame_mode(sd); + + /* All CE and IT formats are detected as RGB full range in DVI mode */ + i2c_wr8_and_or(sd, VI_MODE, ~MASK_RGB_DVI, 0); + + i2c_wr8_and_or(sd, VOUT_SET2, ~MASK_VOUTCOLORMODE, + MASK_VOUTCOLORMODE_AUTO); + i2c_wr8(sd, VOUT_SET3, MASK_VOUT_EXTCNT); +} + +/* --------------- IRQ --------------- */ + +static void tc358743_format_change(struct v4l2_subdev *sd) +{ + struct tc358743_state *state = to_state(sd); + struct v4l2_dv_timings timings; + const struct v4l2_event tc358743_ev_fmt = { + .type = V4L2_EVENT_SOURCE_CHANGE, + .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, + }; + + if (tc358743_get_detected_timings(sd, &timings)) { + enable_stream(sd, false); + + v4l2_dbg(1, debug, sd, "%s: Format changed. No signal\n", + __func__); + } else { + if (!v4l2_match_dv_timings(&state->timings, &timings, 0)) + enable_stream(sd, false); + + v4l2_print_dv_timings(sd->name, + "tc358743_format_change: Format changed. New format: ", + &timings, false); + } + + v4l2_subdev_notify(sd, V4L2_DEVICE_NOTIFY_EVENT, + (void *)&tc358743_ev_fmt); +} + +static void tc358743_init_interrupts(struct v4l2_subdev *sd) +{ + u16 i; + + /* clear interrupt status registers */ + for (i = SYS_INT; i <= KEY_INT; i++) + i2c_wr8(sd, i, 0xff); + + i2c_wr16(sd, INTSTATUS, 0xffff); +} + +static void tc358743_enable_interrupts(struct v4l2_subdev *sd, + bool cable_connected) +{ + v4l2_dbg(2, debug, sd, "%s: cable connected = %d\n", __func__, + cable_connected); + + if (cable_connected) { + i2c_wr8(sd, SYS_INTM, ~(MASK_M_DDC | MASK_M_DVI_DET | + MASK_M_HDMI_DET) & 0xff); + i2c_wr8(sd, CLK_INTM, ~MASK_M_IN_DE_CHG); + i2c_wr8(sd, CBIT_INTM, ~(MASK_M_CBIT_FS | MASK_M_AF_LOCK | + MASK_M_AF_UNLOCK) & 0xff); + i2c_wr8(sd, AUDIO_INTM, ~MASK_M_BUFINIT_END); + i2c_wr8(sd, MISC_INTM, ~MASK_M_SYNC_CHG); + } else { + i2c_wr8(sd, SYS_INTM, ~MASK_M_DDC & 0xff); + i2c_wr8(sd, CLK_INTM, 0xff); + i2c_wr8(sd, CBIT_INTM, 0xff); + i2c_wr8(sd, AUDIO_INTM, 0xff); + i2c_wr8(sd, MISC_INTM, 0xff); + } +} + +static void tc358743_hdmi_audio_int_handler(struct v4l2_subdev *sd, + bool *handled) +{ + u8 audio_int_mask = i2c_rd8(sd, AUDIO_INTM); + u8 audio_int = i2c_rd8(sd, AUDIO_INT) & ~audio_int_mask; + + i2c_wr8(sd, AUDIO_INT, audio_int); + + v4l2_dbg(3, debug, sd, "%s: AUDIO_INT = 0x%02x\n", __func__, audio_int); + + tc358743_s_ctrl_audio_sampling_rate(sd); + tc358743_s_ctrl_audio_present(sd); +} + +static void tc358743_csi_err_int_handler(struct v4l2_subdev *sd, bool *handled) +{ + v4l2_err(sd, "%s: CSI_ERR = 0x%x\n", __func__, i2c_rd32(sd, CSI_ERR)); + + i2c_wr32(sd, CSI_INT_CLR, MASK_ICRER); +} + +static void tc358743_hdmi_misc_int_handler(struct v4l2_subdev *sd, + bool *handled) +{ + u8 misc_int_mask = i2c_rd8(sd, MISC_INTM); + u8 misc_int = i2c_rd8(sd, MISC_INT) & ~misc_int_mask; + + i2c_wr8(sd, MISC_INT, misc_int); + + v4l2_dbg(3, debug, sd, "%s: MISC_INT = 0x%02x\n", __func__, misc_int); + + if (misc_int & MASK_I_SYNC_CHG) { + /* Reset the HDMI PHY to try to trigger proper lock on the + * incoming video format. Erase BKSV to prevent that old keys + * are used when a new source is connected. */ + if (no_sync(sd) || no_signal(sd)) { + tc358743_reset_phy(sd); + tc358743_erase_bksv(sd); + } + + tc358743_format_change(sd); + + misc_int &= ~MASK_I_SYNC_CHG; + if (handled) + *handled = true; + } + + if (misc_int) { + v4l2_err(sd, "%s: Unhandled MISC_INT interrupts: 0x%02x\n", + __func__, misc_int); + } +} + +static void tc358743_hdmi_cbit_int_handler(struct v4l2_subdev *sd, + bool *handled) +{ + u8 cbit_int_mask = i2c_rd8(sd, CBIT_INTM); + u8 cbit_int = i2c_rd8(sd, CBIT_INT) & ~cbit_int_mask; + + i2c_wr8(sd, CBIT_INT, cbit_int); + + v4l2_dbg(3, debug, sd, "%s: CBIT_INT = 0x%02x\n", __func__, cbit_int); + + if (cbit_int & MASK_I_CBIT_FS) { + + v4l2_dbg(1, debug, sd, "%s: Audio sample rate changed\n", + __func__); + tc358743_s_ctrl_audio_sampling_rate(sd); + + cbit_int &= ~MASK_I_CBIT_FS; + if (handled) + *handled = true; + } + + if (cbit_int & (MASK_I_AF_LOCK | MASK_I_AF_UNLOCK)) { + + v4l2_dbg(1, debug, sd, "%s: Audio present changed\n", + __func__); + tc358743_s_ctrl_audio_present(sd); + + cbit_int &= ~(MASK_I_AF_LOCK | MASK_I_AF_UNLOCK); + if (handled) + *handled = true; + } + + if (cbit_int) { + v4l2_err(sd, "%s: Unhandled CBIT_INT interrupts: 0x%02x\n", + __func__, cbit_int); + } +} + +static void tc358743_hdmi_clk_int_handler(struct v4l2_subdev *sd, bool *handled) +{ + u8 clk_int_mask = i2c_rd8(sd, CLK_INTM); + u8 clk_int = i2c_rd8(sd, CLK_INT) & ~clk_int_mask; + + /* Bit 7 and bit 6 are set even when they are masked */ + i2c_wr8(sd, CLK_INT, clk_int | 0x80 | MASK_I_OUT_H_CHG); + + v4l2_dbg(3, debug, sd, "%s: CLK_INT = 0x%02x\n", __func__, clk_int); + + if (clk_int & (MASK_I_IN_DE_CHG)) { + + v4l2_dbg(1, debug, sd, "%s: DE size or position has changed\n", + __func__); + + /* If the source switch to a new resolution with the same pixel + * frequency as the existing (e.g. 1080p25 -> 720p50), the + * I_SYNC_CHG interrupt is not always triggered, while the + * I_IN_DE_CHG interrupt seems to work fine. Format change + * notifications are only sent when the signal is stable to + * reduce the number of notifications. */ + if (!no_signal(sd) && !no_sync(sd)) + tc358743_format_change(sd); + + clk_int &= ~(MASK_I_IN_DE_CHG); + if (handled) + *handled = true; + } + + if (clk_int) { + v4l2_err(sd, "%s: Unhandled CLK_INT interrupts: 0x%02x\n", + __func__, clk_int); + } +} + +static void tc358743_hdmi_sys_int_handler(struct v4l2_subdev *sd, bool *handled) +{ + struct tc358743_state *state = to_state(sd); + u8 sys_int_mask = i2c_rd8(sd, SYS_INTM); + u8 sys_int = i2c_rd8(sd, SYS_INT) & ~sys_int_mask; + + i2c_wr8(sd, SYS_INT, sys_int); + + v4l2_dbg(3, debug, sd, "%s: SYS_INT = 0x%02x\n", __func__, sys_int); + + if (sys_int & MASK_I_DDC) { + bool tx_5v = tx_5v_power_present(sd); + + v4l2_dbg(1, debug, sd, "%s: Tx 5V power present: %s\n", + __func__, tx_5v ? "yes" : "no"); + + if (tx_5v) { + tc358743_enable_edid(sd); + } else { + tc358743_enable_interrupts(sd, false); + tc358743_disable_edid(sd); + memset(&state->timings, 0, sizeof(state->timings)); + tc358743_erase_bksv(sd); + tc358743_update_controls(sd); + } + + sys_int &= ~MASK_I_DDC; + if (handled) + *handled = true; + } + + if (sys_int & MASK_I_DVI) { + v4l2_dbg(1, debug, sd, "%s: HDMI->DVI change detected\n", + __func__); + + /* Reset the HDMI PHY to try to trigger proper lock on the + * incoming video format. Erase BKSV to prevent that old keys + * are used when a new source is connected. */ + if (no_sync(sd) || no_signal(sd)) { + tc358743_reset_phy(sd); + tc358743_erase_bksv(sd); + } + + sys_int &= ~MASK_I_DVI; + if (handled) + *handled = true; + } + + if (sys_int & MASK_I_HDMI) { + v4l2_dbg(1, debug, sd, "%s: DVI->HDMI change detected\n", + __func__); + + /* Register is reset in DVI mode (REF_01, c. 6.6.41) */ + i2c_wr8(sd, ANA_CTL, MASK_APPL_PCSX_NORMAL | MASK_ANALOG_ON); + + sys_int &= ~MASK_I_HDMI; + if (handled) + *handled = true; + } + + if (sys_int) { + v4l2_err(sd, "%s: Unhandled SYS_INT interrupts: 0x%02x\n", + __func__, sys_int); + } +} + +/* --------------- CORE OPS --------------- */ + +static int tc358743_log_status(struct v4l2_subdev *sd) +{ + struct tc358743_state *state = to_state(sd); + struct v4l2_dv_timings timings; + uint8_t hdmi_sys_status = i2c_rd8(sd, SYS_STATUS); + uint16_t sysctl = i2c_rd16(sd, SYSCTL); + u8 vi_status3 = i2c_rd8(sd, VI_STATUS3); + const int deep_color_mode[4] = { 8, 10, 12, 16 }; + static const char * const input_color_space[] = { + "RGB", "YCbCr 601", "Adobe RGB", "YCbCr 709", "NA (4)", + "xvYCC 601", "NA(6)", "xvYCC 709", "NA(8)", "sYCC601", + "NA(10)", "NA(11)", "NA(12)", "Adobe YCC 601"}; + + v4l2_info(sd, "-----Chip status-----\n"); + v4l2_info(sd, "Chip ID: 0x%02x\n", + (i2c_rd16(sd, CHIPID) & MASK_CHIPID) >> 8); + v4l2_info(sd, "Chip revision: 0x%02x\n", + i2c_rd16(sd, CHIPID) & MASK_REVID); + v4l2_info(sd, "Reset: IR: %d, CEC: %d, CSI TX: %d, HDMI: %d\n", + !!(sysctl & MASK_IRRST), + !!(sysctl & MASK_CECRST), + !!(sysctl & MASK_CTXRST), + !!(sysctl & MASK_HDMIRST)); + v4l2_info(sd, "Sleep mode: %s\n", sysctl & MASK_SLEEP ? "on" : "off"); + v4l2_info(sd, "Cable detected (+5V power): %s\n", + hdmi_sys_status & MASK_S_DDC5V ? "yes" : "no"); + v4l2_info(sd, "DDC lines enabled: %s\n", + (i2c_rd8(sd, EDID_MODE) & MASK_EDID_MODE_E_DDC) ? + "yes" : "no"); + v4l2_info(sd, "Hotplug enabled: %s\n", + (i2c_rd8(sd, HPD_CTL) & MASK_HPD_OUT0) ? + "yes" : "no"); + v4l2_info(sd, "CEC enabled: %s\n", + (i2c_rd16(sd, CECEN) & MASK_CECEN) ? "yes" : "no"); + v4l2_info(sd, "-----Signal status-----\n"); + v4l2_info(sd, "TMDS signal detected: %s\n", + hdmi_sys_status & MASK_S_TMDS ? "yes" : "no"); + v4l2_info(sd, "Stable sync signal: %s\n", + hdmi_sys_status & MASK_S_SYNC ? "yes" : "no"); + v4l2_info(sd, "PHY PLL locked: %s\n", + hdmi_sys_status & MASK_S_PHY_PLL ? "yes" : "no"); + v4l2_info(sd, "PHY DE detected: %s\n", + hdmi_sys_status & MASK_S_PHY_SCDT ? "yes" : "no"); + + if (tc358743_get_detected_timings(sd, &timings)) { + v4l2_info(sd, "No video detected\n"); + } else { + v4l2_print_dv_timings(sd->name, "Detected format: ", &timings, + true); + } + v4l2_print_dv_timings(sd->name, "Configured format: ", &state->timings, + true); + + v4l2_info(sd, "-----CSI-TX status-----\n"); + v4l2_info(sd, "Lanes needed: %d\n", + tc358743_num_csi_lanes_needed(sd)); + v4l2_info(sd, "Lanes in use: %d\n", + tc358743_num_csi_lanes_in_use(sd)); + v4l2_info(sd, "Waiting for particular sync signal: %s\n", + (i2c_rd16(sd, CSI_STATUS) & MASK_S_WSYNC) ? + "yes" : "no"); + v4l2_info(sd, "Transmit mode: %s\n", + (i2c_rd16(sd, CSI_STATUS) & MASK_S_TXACT) ? + "yes" : "no"); + v4l2_info(sd, "Receive mode: %s\n", + (i2c_rd16(sd, CSI_STATUS) & MASK_S_RXACT) ? + "yes" : "no"); + v4l2_info(sd, "Stopped: %s\n", + (i2c_rd16(sd, CSI_STATUS) & MASK_S_HLT) ? + "yes" : "no"); + v4l2_info(sd, "Color space: %s\n", + state->mbus_fmt_code == MEDIA_BUS_FMT_UYVY8_1X16 ? + "YCbCr 422 16-bit" : + state->mbus_fmt_code == MEDIA_BUS_FMT_RGB888_1X24 ? + "RGB 888 24-bit" : "Unsupported"); + + v4l2_info(sd, "-----%s status-----\n", is_hdmi(sd) ? "HDMI" : "DVI-D"); + v4l2_info(sd, "HDCP encrypted content: %s\n", + hdmi_sys_status & MASK_S_HDCP ? "yes" : "no"); + v4l2_info(sd, "Input color space: %s %s range\n", + input_color_space[(vi_status3 & MASK_S_V_COLOR) >> 1], + (vi_status3 & MASK_LIMITED) ? "limited" : "full"); + if (!is_hdmi(sd)) + return 0; + v4l2_info(sd, "AV Mute: %s\n", hdmi_sys_status & MASK_S_AVMUTE ? "on" : + "off"); + v4l2_info(sd, "Deep color mode: %d-bits per channel\n", + deep_color_mode[(i2c_rd8(sd, VI_STATUS1) & + MASK_S_DEEPCOLOR) >> 2]); + print_avi_infoframe(sd); + + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static void tc358743_print_register_map(struct v4l2_subdev *sd) +{ + v4l2_info(sd, "0x0000–0x00FF: Global Control Register\n"); + v4l2_info(sd, "0x0100–0x01FF: CSI2-TX PHY Register\n"); + v4l2_info(sd, "0x0200–0x03FF: CSI2-TX PPI Register\n"); + v4l2_info(sd, "0x0400–0x05FF: Reserved\n"); + v4l2_info(sd, "0x0600–0x06FF: CEC Register\n"); + v4l2_info(sd, "0x0700–0x84FF: Reserved\n"); + v4l2_info(sd, "0x8500–0x85FF: HDMIRX System Control Register\n"); + v4l2_info(sd, "0x8600–0x86FF: HDMIRX Audio Control Register\n"); + v4l2_info(sd, "0x8700–0x87FF: HDMIRX InfoFrame packet data Register\n"); + v4l2_info(sd, "0x8800–0x88FF: HDMIRX HDCP Port Register\n"); + v4l2_info(sd, "0x8900–0x89FF: HDMIRX Video Output Port & 3D Register\n"); + v4l2_info(sd, "0x8A00–0x8BFF: Reserved\n"); + v4l2_info(sd, "0x8C00–0x8FFF: HDMIRX EDID-RAM (1024bytes)\n"); + v4l2_info(sd, "0x9000–0x90FF: HDMIRX GBD Extraction Control\n"); + v4l2_info(sd, "0x9100–0x92FF: HDMIRX GBD RAM read\n"); + v4l2_info(sd, "0x9300- : Reserved\n"); +} + +static int tc358743_get_reg_size(u16 address) +{ + /* REF_01 p. 66-72 */ + if (address <= 0x00ff) + return 2; + else if ((address >= 0x0100) && (address <= 0x06FF)) + return 4; + else if ((address >= 0x0700) && (address <= 0x84ff)) + return 2; + else + return 1; +} + +static int tc358743_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + if (reg->reg > 0xffff) { + tc358743_print_register_map(sd); + return -EINVAL; + } + + reg->size = tc358743_get_reg_size(reg->reg); + + i2c_rd(sd, reg->reg, (u8 *)®->val, reg->size); + + return 0; +} + +static int tc358743_s_register(struct v4l2_subdev *sd, + const struct v4l2_dbg_register *reg) +{ + if (reg->reg > 0xffff) { + tc358743_print_register_map(sd); + return -EINVAL; + } + + /* It should not be possible for the user to enable HDCP with a simple + * v4l2-dbg command. + * + * DO NOT REMOVE THIS unless all other issues with HDCP have been + * resolved. + */ + if (reg->reg == HDCP_MODE || + reg->reg == HDCP_REG1 || + reg->reg == HDCP_REG2 || + reg->reg == HDCP_REG3 || + reg->reg == BCAPS) + return 0; + + i2c_wr(sd, (u16)reg->reg, (u8 *)®->val, + tc358743_get_reg_size(reg->reg)); + + return 0; +} +#endif + +static int tc358743_isr(struct v4l2_subdev *sd, u32 status, bool *handled) +{ + u16 intstatus = i2c_rd16(sd, INTSTATUS); + + v4l2_dbg(1, debug, sd, "%s: IntStatus = 0x%04x\n", __func__, intstatus); + + if (intstatus & MASK_HDMI_INT) { + u8 hdmi_int0 = i2c_rd8(sd, HDMI_INT0); + u8 hdmi_int1 = i2c_rd8(sd, HDMI_INT1); + + if (hdmi_int0 & MASK_I_MISC) + tc358743_hdmi_misc_int_handler(sd, handled); + if (hdmi_int1 & MASK_I_CBIT) + tc358743_hdmi_cbit_int_handler(sd, handled); + if (hdmi_int1 & MASK_I_CLK) + tc358743_hdmi_clk_int_handler(sd, handled); + if (hdmi_int1 & MASK_I_SYS) + tc358743_hdmi_sys_int_handler(sd, handled); + if (hdmi_int1 & MASK_I_AUD) + tc358743_hdmi_audio_int_handler(sd, handled); + + i2c_wr16(sd, INTSTATUS, MASK_HDMI_INT); + intstatus &= ~MASK_HDMI_INT; + } + + if (intstatus & MASK_CSI_INT) { + u32 csi_int = i2c_rd32(sd, CSI_INT); + + if (csi_int & MASK_INTER) + tc358743_csi_err_int_handler(sd, handled); + + i2c_wr16(sd, INTSTATUS, MASK_CSI_INT); + intstatus &= ~MASK_CSI_INT; + } + + intstatus = i2c_rd16(sd, INTSTATUS); + if (intstatus) { + v4l2_dbg(1, debug, sd, + "%s: Unhandled IntStatus interrupts: 0x%02x\n", + __func__, intstatus); + } + + return 0; +} + +/* --------------- VIDEO OPS --------------- */ + +static int tc358743_g_input_status(struct v4l2_subdev *sd, u32 *status) +{ + *status = 0; + *status |= no_signal(sd) ? V4L2_IN_ST_NO_SIGNAL : 0; + *status |= no_sync(sd) ? V4L2_IN_ST_NO_SYNC : 0; + + v4l2_dbg(1, debug, sd, "%s: status = 0x%x\n", __func__, *status); + + return 0; +} + +static int tc358743_s_dv_timings(struct v4l2_subdev *sd, + struct v4l2_dv_timings *timings) +{ + struct tc358743_state *state = to_state(sd); + struct v4l2_bt_timings *bt; + + if (!timings) + return -EINVAL; + + if (debug) + v4l2_print_dv_timings(sd->name, "tc358743_s_dv_timings: ", + timings, false); + + if (v4l2_match_dv_timings(&state->timings, timings, 0)) { + v4l2_dbg(1, debug, sd, "%s: no change\n", __func__); + return 0; + } + + bt = &timings->bt; + + if (!v4l2_valid_dv_timings(timings, + &tc358743_timings_cap, NULL, NULL)) { + v4l2_dbg(1, debug, sd, "%s: timings out of range\n", __func__); + return -ERANGE; + } + + state->timings = *timings; + + enable_stream(sd, false); + tc358743_set_pll(sd); + tc358743_set_csi(sd); + + return 0; +} + +static int tc358743_g_dv_timings(struct v4l2_subdev *sd, + struct v4l2_dv_timings *timings) +{ + struct tc358743_state *state = to_state(sd); + + *timings = state->timings; + + return 0; +} + +static int tc358743_enum_dv_timings(struct v4l2_subdev *sd, + struct v4l2_enum_dv_timings *timings) +{ + if (timings->pad != 0) + return -EINVAL; + + return v4l2_enum_dv_timings_cap(timings, + &tc358743_timings_cap, NULL, NULL); +} + +static int tc358743_query_dv_timings(struct v4l2_subdev *sd, + struct v4l2_dv_timings *timings) +{ + int ret; + + ret = tc358743_get_detected_timings(sd, timings); + if (ret) + return ret; + + if (debug) + v4l2_print_dv_timings(sd->name, "tc358743_query_dv_timings: ", + timings, false); + + if (!v4l2_valid_dv_timings(timings, + &tc358743_timings_cap, NULL, NULL)) { + v4l2_dbg(1, debug, sd, "%s: timings out of range\n", __func__); + return -ERANGE; + } + + return 0; +} + +static int tc358743_dv_timings_cap(struct v4l2_subdev *sd, + struct v4l2_dv_timings_cap *cap) +{ + if (cap->pad != 0) + return -EINVAL; + + *cap = tc358743_timings_cap; + + return 0; +} + +static int tc358743_g_mbus_config(struct v4l2_subdev *sd, + struct v4l2_mbus_config *cfg) +{ + cfg->type = V4L2_MBUS_CSI2; + + /* Support for non-continuous CSI-2 clock is missing in the driver */ + cfg->flags = V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; + + switch (tc358743_num_csi_lanes_in_use(sd)) { + case 1: + cfg->flags |= V4L2_MBUS_CSI2_1_LANE; + break; + case 2: + cfg->flags |= V4L2_MBUS_CSI2_2_LANE; + break; + case 3: + cfg->flags |= V4L2_MBUS_CSI2_3_LANE; + break; + case 4: + cfg->flags |= V4L2_MBUS_CSI2_4_LANE; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int tc358743_s_stream(struct v4l2_subdev *sd, int enable) +{ + enable_stream(sd, enable); + + return 0; +} + +/* --------------- PAD OPS --------------- */ + +static int tc358743_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct tc358743_state *state = to_state(sd); + u8 vi_rep = i2c_rd8(sd, VI_REP); + + if (format->pad != 0) + return -EINVAL; + + format->format.code = state->mbus_fmt_code; + format->format.width = state->timings.bt.width; + format->format.height = state->timings.bt.height; + format->format.field = V4L2_FIELD_NONE; + + switch (vi_rep & MASK_VOUT_COLOR_SEL) { + case MASK_VOUT_COLOR_RGB_FULL: + case MASK_VOUT_COLOR_RGB_LIMITED: + format->format.colorspace = V4L2_COLORSPACE_SRGB; + break; + case MASK_VOUT_COLOR_601_YCBCR_LIMITED: + case MASK_VOUT_COLOR_601_YCBCR_FULL: + format->format.colorspace = V4L2_COLORSPACE_SMPTE170M; + break; + case MASK_VOUT_COLOR_709_YCBCR_FULL: + case MASK_VOUT_COLOR_709_YCBCR_LIMITED: + format->format.colorspace = V4L2_COLORSPACE_REC709; + break; + default: + format->format.colorspace = 0; + break; + } + + return 0; +} + +static int tc358743_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct tc358743_state *state = to_state(sd); + + u32 code = format->format.code; /* is overwritten by get_fmt */ + int ret = tc358743_get_fmt(sd, cfg, format); + + format->format.code = code; + + if (ret) + return ret; + + switch (code) { + case MEDIA_BUS_FMT_RGB888_1X24: + case MEDIA_BUS_FMT_UYVY8_1X16: + break; + default: + return -EINVAL; + } + + if (format->which == V4L2_SUBDEV_FORMAT_TRY) + return 0; + + state->mbus_fmt_code = format->format.code; + + enable_stream(sd, false); + tc358743_set_pll(sd); + tc358743_set_csi(sd); + tc358743_set_csi_color_space(sd); + + return 0; +} + +static int tc358743_g_edid(struct v4l2_subdev *sd, + struct v4l2_subdev_edid *edid) +{ + struct tc358743_state *state = to_state(sd); + + if (edid->pad != 0) + return -EINVAL; + + if (edid->start_block == 0 && edid->blocks == 0) { + edid->blocks = state->edid_blocks_written; + return 0; + } + + if (state->edid_blocks_written == 0) + return -ENODATA; + + if (edid->start_block >= state->edid_blocks_written || + edid->blocks == 0) + return -EINVAL; + + if (edid->start_block + edid->blocks > state->edid_blocks_written) + edid->blocks = state->edid_blocks_written - edid->start_block; + + i2c_rd(sd, EDID_RAM + (edid->start_block * EDID_BLOCK_SIZE), edid->edid, + edid->blocks * EDID_BLOCK_SIZE); + + return 0; +} + +static int tc358743_s_edid(struct v4l2_subdev *sd, + struct v4l2_subdev_edid *edid) +{ + struct tc358743_state *state = to_state(sd); + u16 edid_len = edid->blocks * EDID_BLOCK_SIZE; + + v4l2_dbg(2, debug, sd, "%s, pad %d, start block %d, blocks %d\n", + __func__, edid->pad, edid->start_block, edid->blocks); + + if (edid->pad != 0) + return -EINVAL; + + if (edid->start_block != 0) + return -EINVAL; + + if (edid->blocks > EDID_NUM_BLOCKS_MAX) { + edid->blocks = EDID_NUM_BLOCKS_MAX; + return -E2BIG; + } + + tc358743_disable_edid(sd); + + i2c_wr8(sd, EDID_LEN1, edid_len & 0xff); + i2c_wr8(sd, EDID_LEN2, edid_len >> 8); + + if (edid->blocks == 0) { + state->edid_blocks_written = 0; + return 0; + } + + i2c_wr(sd, EDID_RAM, edid->edid, edid_len); + + state->edid_blocks_written = edid->blocks; + + if (tx_5v_power_present(sd)) + tc358743_enable_edid(sd); + + return 0; +} + +/* -------------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops tc358743_core_ops = { + .log_status = tc358743_log_status, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = tc358743_g_register, + .s_register = tc358743_s_register, +#endif + .interrupt_service_routine = tc358743_isr, +}; + +static const struct v4l2_subdev_video_ops tc358743_video_ops = { + .g_input_status = tc358743_g_input_status, + .s_dv_timings = tc358743_s_dv_timings, + .g_dv_timings = tc358743_g_dv_timings, + .query_dv_timings = tc358743_query_dv_timings, + .g_mbus_config = tc358743_g_mbus_config, + .s_stream = tc358743_s_stream, +}; + +static const struct v4l2_subdev_pad_ops tc358743_pad_ops = { + .set_fmt = tc358743_set_fmt, + .get_fmt = tc358743_get_fmt, + .get_edid = tc358743_g_edid, + .set_edid = tc358743_s_edid, + .enum_dv_timings = tc358743_enum_dv_timings, + .dv_timings_cap = tc358743_dv_timings_cap, +}; + +static const struct v4l2_subdev_ops tc358743_ops = { + .core = &tc358743_core_ops, + .video = &tc358743_video_ops, + .pad = &tc358743_pad_ops, +}; + +/* --------------- CUSTOM CTRLS --------------- */ + +static const struct v4l2_ctrl_config tc358743_ctrl_audio_sampling_rate = { + .id = TC358743_CID_AUDIO_SAMPLING_RATE, + .name = "Audio sampling rate", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = 768000, + .step = 1, + .def = 0, + .flags = V4L2_CTRL_FLAG_READ_ONLY, +}; + +static const struct v4l2_ctrl_config tc358743_ctrl_audio_present = { + .id = TC358743_CID_AUDIO_PRESENT, + .name = "Audio present", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = 0, + .max = 1, + .step = 1, + .def = 0, + .flags = V4L2_CTRL_FLAG_READ_ONLY, +}; + +/* --------------- PROBE / REMOVE --------------- */ + +static int tc358743_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + static struct v4l2_dv_timings default_timing = + V4L2_DV_BT_CEA_640X480P59_94; + struct tc358743_state *state; + struct tc358743_platform_data *pdata = client->dev.platform_data; + struct v4l2_subdev *sd; + int err; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + v4l_dbg(1, debug, client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + + state = devm_kzalloc(&client->dev, sizeof(struct tc358743_state), + GFP_KERNEL); + if (!state) + return -ENOMEM; + + /* platform data */ + if (!pdata) { + v4l_err(client, "No platform data!\n"); + return -ENODEV; + } + state->pdata = *pdata; + + state->i2c_client = client; + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &tc358743_ops); + sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS; + + /* i2c access */ + if ((i2c_rd16(sd, CHIPID) & MASK_CHIPID) != 0) { + v4l2_info(sd, "not a TC358743 on address 0x%x\n", + client->addr << 1); + return -ENODEV; + } + + /* control handlers */ + v4l2_ctrl_handler_init(&state->hdl, 3); + + /* private controls */ + state->detect_tx_5v_ctrl = v4l2_ctrl_new_std(&state->hdl, NULL, + V4L2_CID_DV_RX_POWER_PRESENT, 0, 1, 0, 0); + + /* custom controls */ + state->audio_sampling_rate_ctrl = v4l2_ctrl_new_custom(&state->hdl, + &tc358743_ctrl_audio_sampling_rate, NULL); + + state->audio_present_ctrl = v4l2_ctrl_new_custom(&state->hdl, + &tc358743_ctrl_audio_present, NULL); + + sd->ctrl_handler = &state->hdl; + if (state->hdl.error) { + err = state->hdl.error; + goto err_hdl; + } + + if (tc358743_update_controls(sd)) { + err = -ENODEV; + goto err_hdl; + } + + /* work queues */ + state->work_queues = create_singlethread_workqueue(client->name); + if (!state->work_queues) { + v4l2_err(sd, "Could not create work queue\n"); + err = -ENOMEM; + goto err_hdl; + } + + mutex_init(&state->confctl_mutex); + + INIT_DELAYED_WORK(&state->delayed_work_enable_hotplug, + tc358743_delayed_work_enable_hotplug); + + tc358743_initial_setup(sd); + + tc358743_s_dv_timings(sd, &default_timing); + + state->mbus_fmt_code = MEDIA_BUS_FMT_RGB888_1X24; + tc358743_set_csi_color_space(sd); + + tc358743_init_interrupts(sd); + tc358743_enable_interrupts(sd, tx_5v_power_present(sd)); + i2c_wr16(sd, INTMASK, ~(MASK_HDMI_MSK | MASK_CSI_MSK) & 0xffff); + + err = v4l2_ctrl_handler_setup(sd->ctrl_handler); + if (err) + goto err_work_queues; + + v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name, + client->addr << 1, client->adapter->name); + + return 0; + +err_work_queues: + cancel_delayed_work(&state->delayed_work_enable_hotplug); + destroy_workqueue(state->work_queues); + mutex_destroy(&state->confctl_mutex); +err_hdl: + v4l2_ctrl_handler_free(&state->hdl); + return err; +} + +static int tc358743_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct tc358743_state *state = to_state(sd); + + cancel_delayed_work(&state->delayed_work_enable_hotplug); + destroy_workqueue(state->work_queues); + v4l2_device_unregister_subdev(sd); + mutex_destroy(&state->confctl_mutex); + v4l2_ctrl_handler_free(&state->hdl); + + return 0; +} + +static struct i2c_device_id tc358743_id[] = { + {"tc358743", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, tc358743_id); + +static struct i2c_driver tc358743_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "tc358743", + }, + .probe = tc358743_probe, + .remove = tc358743_remove, + .id_table = tc358743_id, +}; + +module_i2c_driver(tc358743_driver); diff --git a/drivers/media/i2c/tc358743_regs.h b/drivers/media/i2c/tc358743_regs.h new file mode 100644 index 000000000000..81f1db558e7c --- /dev/null +++ b/drivers/media/i2c/tc358743_regs.h @@ -0,0 +1,681 @@ +/* + * tc358743 - Toshiba HDMI to CSI-2 bridge - register names and bit masks + * + * Copyright 2015 Cisco Systems, Inc. and/or its affiliates. All rights + * reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +/* + * References (c = chapter, p = page): + * REF_01 - Toshiba, TC358743XBG (H2C), Functional Specification, Rev 0.60 + */ + +/* Bit masks has prefix 'MASK_' and options after '_'. */ + +#ifndef __TC358743_REGS_H +#define __TC358743_REGS_H + +#define CHIPID 0x0000 +#define MASK_CHIPID 0xff00 +#define MASK_REVID 0x00ff + +#define SYSCTL 0x0002 +#define MASK_IRRST 0x0800 +#define MASK_CECRST 0x0400 +#define MASK_CTXRST 0x0200 +#define MASK_HDMIRST 0x0100 +#define MASK_SLEEP 0x0001 + +#define CONFCTL 0x0004 +#define MASK_PWRISO 0x8000 +#define MASK_ACLKOPT 0x1000 +#define MASK_AUDCHNUM 0x0c00 +#define MASK_AUDCHNUM_8 0x0000 +#define MASK_AUDCHNUM_6 0x0400 +#define MASK_AUDCHNUM_4 0x0800 +#define MASK_AUDCHNUM_2 0x0c00 +#define MASK_AUDCHSEL 0x0200 +#define MASK_I2SDLYOPT 0x0100 +#define MASK_YCBCRFMT 0x00c0 +#define MASK_YCBCRFMT_444 0x0000 +#define MASK_YCBCRFMT_422_12_BIT 0x0040 +#define MASK_YCBCRFMT_COLORBAR 0x0080 +#define MASK_YCBCRFMT_422_8_BIT 0x00c0 +#define MASK_INFRMEN 0x0020 +#define MASK_AUDOUTSEL 0x0018 +#define MASK_AUDOUTSEL_CSI 0x0000 +#define MASK_AUDOUTSEL_I2S 0x0010 +#define MASK_AUDOUTSEL_TDM 0x0018 +#define MASK_AUTOINDEX 0x0004 +#define MASK_ABUFEN 0x0002 +#define MASK_VBUFEN 0x0001 + +#define FIFOCTL 0x0006 + +#define INTSTATUS 0x0014 +#define MASK_AMUTE_INT 0x0400 +#define MASK_HDMI_INT 0x0200 +#define MASK_CSI_INT 0x0100 +#define MASK_SYS_INT 0x0020 +#define MASK_CEC_EINT 0x0010 +#define MASK_CEC_TINT 0x0008 +#define MASK_CEC_RINT 0x0004 +#define MASK_IR_EINT 0x0002 +#define MASK_IR_DINT 0x0001 + +#define INTMASK 0x0016 +#define MASK_AMUTE_MSK 0x0400 +#define MASK_HDMI_MSK 0x0200 +#define MASK_CSI_MSK 0x0100 +#define MASK_SYS_MSK 0x0020 +#define MASK_CEC_EMSK 0x0010 +#define MASK_CEC_TMSK 0x0008 +#define MASK_CEC_RMSK 0x0004 +#define MASK_IR_EMSK 0x0002 +#define MASK_IR_DMSK 0x0001 + +#define INTFLAG 0x0018 +#define INTSYSSTATUS 0x001A + +#define PLLCTL0 0x0020 +#define MASK_PLL_PRD 0xf000 +#define SET_PLL_PRD(prd) ((((prd) - 1) << 12) &\ + MASK_PLL_PRD) +#define MASK_PLL_FBD 0x01ff +#define SET_PLL_FBD(fbd) (((fbd) - 1) & MASK_PLL_FBD) + +#define PLLCTL1 0x0022 +#define MASK_PLL_FRS 0x0c00 +#define SET_PLL_FRS(frs) (((frs) << 10) & MASK_PLL_FRS) +#define MASK_PLL_LBWS 0x0300 +#define MASK_LFBREN 0x0040 +#define MASK_BYPCKEN 0x0020 +#define MASK_CKEN 0x0010 +#define MASK_RESETB 0x0002 +#define MASK_PLL_EN 0x0001 + +#define CLW_CNTRL 0x0140 +#define MASK_CLW_LANEDISABLE 0x0001 + +#define D0W_CNTRL 0x0144 +#define MASK_D0W_LANEDISABLE 0x0001 + +#define D1W_CNTRL 0x0148 +#define MASK_D1W_LANEDISABLE 0x0001 + +#define D2W_CNTRL 0x014C +#define MASK_D2W_LANEDISABLE 0x0001 + +#define D3W_CNTRL 0x0150 +#define MASK_D3W_LANEDISABLE 0x0001 + +#define STARTCNTRL 0x0204 +#define MASK_START 0x00000001 + +#define LINEINITCNT 0x0210 +#define LPTXTIMECNT 0x0214 +#define TCLK_HEADERCNT 0x0218 +#define TCLK_TRAILCNT 0x021C +#define THS_HEADERCNT 0x0220 +#define TWAKEUP 0x0224 +#define TCLK_POSTCNT 0x0228 +#define THS_TRAILCNT 0x022C +#define HSTXVREGCNT 0x0230 + +#define HSTXVREGEN 0x0234 +#define MASK_D3M_HSTXVREGEN 0x0010 +#define MASK_D2M_HSTXVREGEN 0x0008 +#define MASK_D1M_HSTXVREGEN 0x0004 +#define MASK_D0M_HSTXVREGEN 0x0002 +#define MASK_CLM_HSTXVREGEN 0x0001 + + +#define TXOPTIONCNTRL 0x0238 +#define MASK_CONTCLKMODE 0x00000001 + +#define CSI_CONTROL 0x040C +#define MASK_CSI_MODE 0x8000 +#define MASK_HTXTOEN 0x0400 +#define MASK_TXHSMD 0x0080 +#define MASK_HSCKMD 0x0020 +#define MASK_NOL 0x0006 +#define MASK_NOL_1 0x0000 +#define MASK_NOL_2 0x0002 +#define MASK_NOL_3 0x0004 +#define MASK_NOL_4 0x0006 +#define MASK_EOTDIS 0x0001 + +#define CSI_INT 0x0414 +#define MASK_INTHLT 0x00000008 +#define MASK_INTER 0x00000004 + +#define CSI_INT_ENA 0x0418 +#define MASK_IENHLT 0x00000008 +#define MASK_IENER 0x00000004 + +#define CSI_ERR 0x044C +#define MASK_INER 0x00000200 +#define MASK_WCER 0x00000100 +#define MASK_QUNK 0x00000010 +#define MASK_TXBRK 0x00000002 + +#define CSI_ERR_INTENA 0x0450 +#define CSI_ERR_HALT 0x0454 + +#define CSI_CONFW 0x0500 +#define MASK_MODE 0xe0000000 +#define MASK_MODE_SET 0xa0000000 +#define MASK_MODE_CLEAR 0xc0000000 +#define MASK_ADDRESS 0x1f000000 +#define MASK_ADDRESS_CSI_CONTROL 0x03000000 +#define MASK_ADDRESS_CSI_INT_ENA 0x06000000 +#define MASK_ADDRESS_CSI_ERR_INTENA 0x14000000 +#define MASK_ADDRESS_CSI_ERR_HALT 0x15000000 +#define MASK_DATA 0x0000ffff + +#define CSI_INT_CLR 0x050C +#define MASK_ICRER 0x00000004 + +#define CSI_START 0x0518 +#define MASK_STRT 0x00000001 + +#define CECEN 0x0600 +#define MASK_CECEN 0x0001 + +#define HDMI_INT0 0x8500 +#define MASK_I_KEY 0x80 +#define MASK_I_MISC 0x02 +#define MASK_I_PHYERR 0x01 + +#define HDMI_INT1 0x8501 +#define MASK_I_GBD 0x80 +#define MASK_I_HDCP 0x40 +#define MASK_I_ERR 0x20 +#define MASK_I_AUD 0x10 +#define MASK_I_CBIT 0x08 +#define MASK_I_PACKET 0x04 +#define MASK_I_CLK 0x02 +#define MASK_I_SYS 0x01 + +#define SYS_INT 0x8502 +#define MASK_I_ACR_CTS 0x80 +#define MASK_I_ACRN 0x40 +#define MASK_I_DVI 0x20 +#define MASK_I_HDMI 0x10 +#define MASK_I_NOPMBDET 0x08 +#define MASK_I_DPMBDET 0x04 +#define MASK_I_TMDS 0x02 +#define MASK_I_DDC 0x01 + +#define CLK_INT 0x8503 +#define MASK_I_OUT_H_CHG 0x40 +#define MASK_I_IN_DE_CHG 0x20 +#define MASK_I_IN_HV_CHG 0x10 +#define MASK_I_DC_CHG 0x08 +#define MASK_I_PXCLK_CHG 0x04 +#define MASK_I_PHYCLK_CHG 0x02 +#define MASK_I_TMDSCLK_CHG 0x01 + +#define CBIT_INT 0x8505 +#define MASK_I_AF_LOCK 0x80 +#define MASK_I_AF_UNLOCK 0x40 +#define MASK_I_CBIT_FS 0x02 + +#define AUDIO_INT 0x8506 + +#define ERR_INT 0x8507 +#define MASK_I_EESS_ERR 0x80 + +#define HDCP_INT 0x8508 +#define MASK_I_AVM_SET 0x80 +#define MASK_I_AVM_CLR 0x40 +#define MASK_I_LINKERR 0x20 +#define MASK_I_SHA_END 0x10 +#define MASK_I_R0_END 0x08 +#define MASK_I_KM_END 0x04 +#define MASK_I_AKSV_END 0x02 +#define MASK_I_AN_END 0x01 + +#define MISC_INT 0x850B +#define MASK_I_AS_LAYOUT 0x10 +#define MASK_I_NO_SPD 0x08 +#define MASK_I_NO_VS 0x03 +#define MASK_I_SYNC_CHG 0x02 +#define MASK_I_AUDIO_MUTE 0x01 + +#define KEY_INT 0x850F + +#define SYS_INTM 0x8512 +#define MASK_M_ACR_CTS 0x80 +#define MASK_M_ACR_N 0x40 +#define MASK_M_DVI_DET 0x20 +#define MASK_M_HDMI_DET 0x10 +#define MASK_M_NOPMBDET 0x08 +#define MASK_M_BPMBDET 0x04 +#define MASK_M_TMDS 0x02 +#define MASK_M_DDC 0x01 + +#define CLK_INTM 0x8513 +#define MASK_M_OUT_H_CHG 0x40 +#define MASK_M_IN_DE_CHG 0x20 +#define MASK_M_IN_HV_CHG 0x10 +#define MASK_M_DC_CHG 0x08 +#define MASK_M_PXCLK_CHG 0x04 +#define MASK_M_PHYCLK_CHG 0x02 +#define MASK_M_TMDS_CHG 0x01 + +#define PACKET_INTM 0x8514 + +#define CBIT_INTM 0x8515 +#define MASK_M_AF_LOCK 0x80 +#define MASK_M_AF_UNLOCK 0x40 +#define MASK_M_CBIT_FS 0x02 + +#define AUDIO_INTM 0x8516 +#define MASK_M_BUFINIT_END 0x01 + +#define ERR_INTM 0x8517 +#define MASK_M_EESS_ERR 0x80 + +#define HDCP_INTM 0x8518 +#define MASK_M_AVM_SET 0x80 +#define MASK_M_AVM_CLR 0x40 +#define MASK_M_LINKERR 0x20 +#define MASK_M_SHA_END 0x10 +#define MASK_M_R0_END 0x08 +#define MASK_M_KM_END 0x04 +#define MASK_M_AKSV_END 0x02 +#define MASK_M_AN_END 0x01 + +#define MISC_INTM 0x851B +#define MASK_M_AS_LAYOUT 0x10 +#define MASK_M_NO_SPD 0x08 +#define MASK_M_NO_VS 0x03 +#define MASK_M_SYNC_CHG 0x02 +#define MASK_M_AUDIO_MUTE 0x01 + +#define KEY_INTM 0x851F + +#define SYS_STATUS 0x8520 +#define MASK_S_SYNC 0x80 +#define MASK_S_AVMUTE 0x40 +#define MASK_S_HDCP 0x20 +#define MASK_S_HDMI 0x10 +#define MASK_S_PHY_SCDT 0x08 +#define MASK_S_PHY_PLL 0x04 +#define MASK_S_TMDS 0x02 +#define MASK_S_DDC5V 0x01 + +#define CSI_STATUS 0x0410 +#define MASK_S_WSYNC 0x0400 +#define MASK_S_TXACT 0x0200 +#define MASK_S_RXACT 0x0100 +#define MASK_S_HLT 0x0001 + +#define VI_STATUS1 0x8522 +#define MASK_S_V_GBD 0x08 +#define MASK_S_DEEPCOLOR 0x0c +#define MASK_S_V_422 0x02 +#define MASK_S_V_INTERLACE 0x01 + +#define AU_STATUS0 0x8523 +#define MASK_S_A_SAMPLE 0x01 + +#define VI_STATUS3 0x8528 +#define MASK_S_V_COLOR 0x1e +#define MASK_LIMITED 0x01 + +#define PHY_CTL0 0x8531 +#define MASK_PHY_SYSCLK_IND 0x02 +#define MASK_PHY_CTL 0x01 + + +#define PHY_CTL1 0x8532 /* Not in REF_01 */ +#define MASK_PHY_AUTO_RST1 0xf0 +#define MASK_PHY_AUTO_RST1_OFF 0x00 +#define SET_PHY_AUTO_RST1_US(us) ((((us) / 200) << 4) & \ + MASK_PHY_AUTO_RST1) +#define MASK_FREQ_RANGE_MODE 0x0f +#define SET_FREQ_RANGE_MODE_CYCLES(cycles) (((cycles) - 1) & \ + MASK_FREQ_RANGE_MODE) + +#define PHY_CTL2 0x8533 /* Not in REF_01 */ +#define MASK_PHY_AUTO_RST4 0x04 +#define MASK_PHY_AUTO_RST3 0x02 +#define MASK_PHY_AUTO_RST2 0x01 +#define MASK_PHY_AUTO_RSTn (MASK_PHY_AUTO_RST4 | \ + MASK_PHY_AUTO_RST3 | \ + MASK_PHY_AUTO_RST2) + +#define PHY_EN 0x8534 +#define MASK_ENABLE_PHY 0x01 + +#define PHY_RST 0x8535 +#define MASK_RESET_CTRL 0x01 /* Reset active low */ + +#define PHY_BIAS 0x8536 /* Not in REF_01 */ + +#define PHY_CSQ 0x853F /* Not in REF_01 */ +#define MASK_CSQ_CNT 0x0f +#define SET_CSQ_CNT_LEVEL(n) (n & MASK_CSQ_CNT) + +#define SYS_FREQ0 0x8540 +#define SYS_FREQ1 0x8541 + +#define SYS_CLK 0x8542 /* Not in REF_01 */ +#define MASK_CLK_DIFF 0x0C +#define MASK_CLK_DIV 0x03 + +#define DDC_CTL 0x8543 +#define MASK_DDC_ACK_POL 0x08 +#define MASK_DDC_ACTION 0x04 +#define MASK_DDC5V_MODE 0x03 +#define MASK_DDC5V_MODE_0MS 0x00 +#define MASK_DDC5V_MODE_50MS 0x01 +#define MASK_DDC5V_MODE_100MS 0x02 +#define MASK_DDC5V_MODE_200MS 0x03 + +#define HPD_CTL 0x8544 +#define MASK_HPD_CTL0 0x10 +#define MASK_HPD_OUT0 0x01 + +#define ANA_CTL 0x8545 +#define MASK_APPL_PCSX 0x30 +#define MASK_APPL_PCSX_HIZ 0x00 +#define MASK_APPL_PCSX_L_FIX 0x10 +#define MASK_APPL_PCSX_H_FIX 0x20 +#define MASK_APPL_PCSX_NORMAL 0x30 +#define MASK_ANALOG_ON 0x01 + +#define AVM_CTL 0x8546 + +#define INIT_END 0x854A +#define MASK_INIT_END 0x01 + +#define HDMI_DET 0x8552 /* Not in REF_01 */ +#define MASK_HDMI_DET_MOD1 0x80 +#define MASK_HDMI_DET_MOD0 0x40 +#define MASK_HDMI_DET_V 0x30 +#define MASK_HDMI_DET_V_SYNC 0x00 +#define MASK_HDMI_DET_V_ASYNC_25MS 0x10 +#define MASK_HDMI_DET_V_ASYNC_50MS 0x20 +#define MASK_HDMI_DET_V_ASYNC_100MS 0x30 +#define MASK_HDMI_DET_NUM 0x0f + +#define HDCP_MODE 0x8560 +#define MASK_MODE_RST_TN 0x20 +#define MASK_LINE_REKEY 0x10 +#define MASK_AUTO_CLR 0x04 + +#define HDCP_REG1 0x8563 /* Not in REF_01 */ +#define MASK_AUTH_UNAUTH_SEL 0x70 +#define MASK_AUTH_UNAUTH_SEL_12_FRAMES 0x70 +#define MASK_AUTH_UNAUTH_SEL_8_FRAMES 0x60 +#define MASK_AUTH_UNAUTH_SEL_4_FRAMES 0x50 +#define MASK_AUTH_UNAUTH_SEL_2_FRAMES 0x40 +#define MASK_AUTH_UNAUTH_SEL_64_FRAMES 0x30 +#define MASK_AUTH_UNAUTH_SEL_32_FRAMES 0x20 +#define MASK_AUTH_UNAUTH_SEL_16_FRAMES 0x10 +#define MASK_AUTH_UNAUTH_SEL_ONCE 0x00 +#define MASK_AUTH_UNAUTH 0x01 +#define MASK_AUTH_UNAUTH_AUTO 0x01 + +#define HDCP_REG2 0x8564 /* Not in REF_01 */ +#define MASK_AUTO_P3_RESET 0x0F +#define SET_AUTO_P3_RESET_FRAMES(n) (n & MASK_AUTO_P3_RESET) +#define MASK_AUTO_P3_RESET_OFF 0x00 + +#define VI_MODE 0x8570 +#define MASK_RGB_DVI 0x08 /* Not in REF_01 */ + +#define VOUT_SET2 0x8573 +#define MASK_SEL422 0x80 +#define MASK_VOUT_422FIL_100 0x40 +#define MASK_VOUTCOLORMODE 0x03 +#define MASK_VOUTCOLORMODE_THROUGH 0x00 +#define MASK_VOUTCOLORMODE_AUTO 0x01 +#define MASK_VOUTCOLORMODE_MANUAL 0x03 + +#define VOUT_SET3 0x8574 +#define MASK_VOUT_EXTCNT 0x08 + +#define VI_REP 0x8576 +#define MASK_VOUT_COLOR_SEL 0xe0 +#define MASK_VOUT_COLOR_RGB_FULL 0x00 +#define MASK_VOUT_COLOR_RGB_LIMITED 0x20 +#define MASK_VOUT_COLOR_601_YCBCR_FULL 0x40 +#define MASK_VOUT_COLOR_601_YCBCR_LIMITED 0x60 +#define MASK_VOUT_COLOR_709_YCBCR_FULL 0x80 +#define MASK_VOUT_COLOR_709_YCBCR_LIMITED 0xa0 +#define MASK_VOUT_COLOR_FULL_TO_LIMITED 0xc0 +#define MASK_VOUT_COLOR_LIMITED_TO_FULL 0xe0 +#define MASK_IN_REP_HEN 0x10 +#define MASK_IN_REP 0x0f + +#define VI_MUTE 0x857F +#define MASK_AUTO_MUTE 0xc0 +#define MASK_VI_MUTE 0x10 + +#define DE_WIDTH_H_LO 0x8582 /* Not in REF_01 */ +#define DE_WIDTH_H_HI 0x8583 /* Not in REF_01 */ +#define DE_WIDTH_V_LO 0x8588 /* Not in REF_01 */ +#define DE_WIDTH_V_HI 0x8589 /* Not in REF_01 */ +#define H_SIZE_LO 0x858A /* Not in REF_01 */ +#define H_SIZE_HI 0x858B /* Not in REF_01 */ +#define V_SIZE_LO 0x858C /* Not in REF_01 */ +#define V_SIZE_HI 0x858D /* Not in REF_01 */ +#define FV_CNT_LO 0x85A1 /* Not in REF_01 */ +#define FV_CNT_HI 0x85A2 /* Not in REF_01 */ + +#define FH_MIN0 0x85AA /* Not in REF_01 */ +#define FH_MIN1 0x85AB /* Not in REF_01 */ +#define FH_MAX0 0x85AC /* Not in REF_01 */ +#define FH_MAX1 0x85AD /* Not in REF_01 */ + +#define HV_RST 0x85AF /* Not in REF_01 */ +#define MASK_H_PI_RST 0x20 +#define MASK_V_PI_RST 0x10 + +#define EDID_MODE 0x85C7 +#define MASK_EDID_SPEED 0x40 +#define MASK_EDID_MODE 0x03 +#define MASK_EDID_MODE_DISABLE 0x00 +#define MASK_EDID_MODE_DDC2B 0x01 +#define MASK_EDID_MODE_E_DDC 0x02 + +#define EDID_LEN1 0x85CA +#define EDID_LEN2 0x85CB + +#define HDCP_REG3 0x85D1 /* Not in REF_01 */ +#define KEY_RD_CMD 0x01 + +#define FORCE_MUTE 0x8600 +#define MASK_FORCE_AMUTE 0x10 +#define MASK_FORCE_DMUTE 0x01 + +#define CMD_AUD 0x8601 +#define MASK_CMD_BUFINIT 0x04 +#define MASK_CMD_LOCKDET 0x02 +#define MASK_CMD_MUTE 0x01 + +#define AUTO_CMD0 0x8602 +#define MASK_AUTO_MUTE7 0x80 +#define MASK_AUTO_MUTE6 0x40 +#define MASK_AUTO_MUTE5 0x20 +#define MASK_AUTO_MUTE4 0x10 +#define MASK_AUTO_MUTE3 0x08 +#define MASK_AUTO_MUTE2 0x04 +#define MASK_AUTO_MUTE1 0x02 +#define MASK_AUTO_MUTE0 0x01 + +#define AUTO_CMD1 0x8603 +#define MASK_AUTO_MUTE10 0x04 +#define MASK_AUTO_MUTE9 0x02 +#define MASK_AUTO_MUTE8 0x01 + +#define AUTO_CMD2 0x8604 +#define MASK_AUTO_PLAY3 0x08 +#define MASK_AUTO_PLAY2 0x04 + +#define BUFINIT_START 0x8606 +#define SET_BUFINIT_START_MS(milliseconds) ((milliseconds) / 100) + +#define FS_MUTE 0x8607 +#define MASK_FS_ELSE_MUTE 0x80 +#define MASK_FS22_MUTE 0x40 +#define MASK_FS24_MUTE 0x20 +#define MASK_FS88_MUTE 0x10 +#define MASK_FS96_MUTE 0x08 +#define MASK_FS176_MUTE 0x04 +#define MASK_FS192_MUTE 0x02 +#define MASK_FS_NO_MUTE 0x01 + +#define FS_IMODE 0x8620 +#define MASK_NLPCM_HMODE 0x40 +#define MASK_NLPCM_SMODE 0x20 +#define MASK_NLPCM_IMODE 0x10 +#define MASK_FS_HMODE 0x08 +#define MASK_FS_AMODE 0x04 +#define MASK_FS_SMODE 0x02 +#define MASK_FS_IMODE 0x01 + +#define FS_SET 0x8621 +#define MASK_FS 0x0f + +#define LOCKDET_REF0 0x8630 +#define LOCKDET_REF1 0x8631 +#define LOCKDET_REF2 0x8632 + +#define ACR_MODE 0x8640 +#define MASK_ACR_LOAD 0x10 +#define MASK_N_MODE 0x04 +#define MASK_CTS_MODE 0x01 + +#define ACR_MDF0 0x8641 +#define MASK_ACR_L2MDF 0x70 +#define MASK_ACR_L2MDF_0_PPM 0x00 +#define MASK_ACR_L2MDF_61_PPM 0x10 +#define MASK_ACR_L2MDF_122_PPM 0x20 +#define MASK_ACR_L2MDF_244_PPM 0x30 +#define MASK_ACR_L2MDF_488_PPM 0x40 +#define MASK_ACR_L2MDF_976_PPM 0x50 +#define MASK_ACR_L2MDF_1976_PPM 0x60 +#define MASK_ACR_L2MDF_3906_PPM 0x70 +#define MASK_ACR_L1MDF 0x07 +#define MASK_ACR_L1MDF_0_PPM 0x00 +#define MASK_ACR_L1MDF_61_PPM 0x01 +#define MASK_ACR_L1MDF_122_PPM 0x02 +#define MASK_ACR_L1MDF_244_PPM 0x03 +#define MASK_ACR_L1MDF_488_PPM 0x04 +#define MASK_ACR_L1MDF_976_PPM 0x05 +#define MASK_ACR_L1MDF_1976_PPM 0x06 +#define MASK_ACR_L1MDF_3906_PPM 0x07 + +#define ACR_MDF1 0x8642 +#define MASK_ACR_L3MDF 0x07 +#define MASK_ACR_L3MDF_0_PPM 0x00 +#define MASK_ACR_L3MDF_61_PPM 0x01 +#define MASK_ACR_L3MDF_122_PPM 0x02 +#define MASK_ACR_L3MDF_244_PPM 0x03 +#define MASK_ACR_L3MDF_488_PPM 0x04 +#define MASK_ACR_L3MDF_976_PPM 0x05 +#define MASK_ACR_L3MDF_1976_PPM 0x06 +#define MASK_ACR_L3MDF_3906_PPM 0x07 + +#define SDO_MODE1 0x8652 +#define MASK_SDO_BIT_LENG 0x70 +#define MASK_SDO_FMT 0x03 +#define MASK_SDO_FMT_RIGHT 0x00 +#define MASK_SDO_FMT_LEFT 0x01 +#define MASK_SDO_FMT_I2S 0x02 + +#define DIV_MODE 0x8665 /* Not in REF_01 */ +#define MASK_DIV_DLY 0xf0 +#define SET_DIV_DLY_MS(milliseconds) ((((milliseconds) / 100) << 4) & \ + MASK_DIV_DLY) +#define MASK_DIV_MODE 0x01 + +#define NCO_F0_MOD 0x8670 +#define MASK_NCO_F0_MOD 0x03 +#define MASK_NCO_F0_MOD_42MHZ 0x00 +#define MASK_NCO_F0_MOD_27MHZ 0x01 + +#define PK_INT_MODE 0x8709 +#define MASK_ISRC2_INT_MODE 0x80 +#define MASK_ISRC_INT_MODE 0x40 +#define MASK_ACP_INT_MODE 0x20 +#define MASK_VS_INT_MODE 0x10 +#define MASK_SPD_INT_MODE 0x08 +#define MASK_MS_INT_MODE 0x04 +#define MASK_AUD_INT_MODE 0x02 +#define MASK_AVI_INT_MODE 0x01 + +#define NO_PKT_LIMIT 0x870B +#define MASK_NO_ACP_LIMIT 0xf0 +#define SET_NO_ACP_LIMIT_MS(milliseconds) ((((milliseconds) / 80) << 4) & \ + MASK_NO_ACP_LIMIT) +#define MASK_NO_AVI_LIMIT 0x0f +#define SET_NO_AVI_LIMIT_MS(milliseconds) (((milliseconds) / 80) & \ + MASK_NO_AVI_LIMIT) + +#define NO_PKT_CLR 0x870C +#define MASK_NO_VS_CLR 0x40 +#define MASK_NO_SPD_CLR 0x20 +#define MASK_NO_ACP_CLR 0x10 +#define MASK_NO_AVI_CLR1 0x02 +#define MASK_NO_AVI_CLR0 0x01 + +#define ERR_PK_LIMIT 0x870D +#define NO_PKT_LIMIT2 0x870E +#define PK_AVI_0HEAD 0x8710 +#define PK_AVI_1HEAD 0x8711 +#define PK_AVI_2HEAD 0x8712 +#define PK_AVI_0BYTE 0x8713 +#define PK_AVI_1BYTE 0x8714 +#define PK_AVI_2BYTE 0x8715 +#define PK_AVI_3BYTE 0x8716 +#define PK_AVI_4BYTE 0x8717 +#define PK_AVI_5BYTE 0x8718 +#define PK_AVI_6BYTE 0x8719 +#define PK_AVI_7BYTE 0x871A +#define PK_AVI_8BYTE 0x871B +#define PK_AVI_9BYTE 0x871C +#define PK_AVI_10BYTE 0x871D +#define PK_AVI_11BYTE 0x871E +#define PK_AVI_12BYTE 0x871F +#define PK_AVI_13BYTE 0x8720 +#define PK_AVI_14BYTE 0x8721 +#define PK_AVI_15BYTE 0x8722 +#define PK_AVI_16BYTE 0x8723 + +#define BKSV 0x8800 + +#define BCAPS 0x8840 +#define MASK_HDMI_RSVD 0x80 +#define MASK_REPEATER 0x40 +#define MASK_READY 0x20 +#define MASK_FASTI2C 0x10 +#define MASK_1_1_FEA 0x02 +#define MASK_FAST_REAU 0x01 + +#define BSTATUS1 0x8842 +#define MASK_MAX_EXCED 0x08 + +#define EDID_RAM 0x8C00 +#define NO_GDB_LIMIT 0x9007 + +#endif diff --git a/include/media/tc358743.h b/include/media/tc358743.h new file mode 100644 index 000000000000..4513f2f9cfbc --- /dev/null +++ b/include/media/tc358743.h @@ -0,0 +1,131 @@ +/* + * tc358743 - Toshiba HDMI to CSI-2 bridge + * + * Copyright 2015 Cisco Systems, Inc. and/or its affiliates. All rights + * reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +/* + * References (c = chapter, p = page): + * REF_01 - Toshiba, TC358743XBG (H2C), Functional Specification, Rev 0.60 + * REF_02 - Toshiba, TC358743XBG_HDMI-CSI_Tv11p_nm.xls + */ + +#ifndef _TC358743_ +#define _TC358743_ + +enum tc358743_ddc5v_delays { + DDC5V_DELAY_0_MS, + DDC5V_DELAY_50_MS, + DDC5V_DELAY_100_MS, + DDC5V_DELAY_200_MS, +}; + +enum tc358743_hdmi_detection_delay { + HDMI_MODE_DELAY_0_MS, + HDMI_MODE_DELAY_25_MS, + HDMI_MODE_DELAY_50_MS, + HDMI_MODE_DELAY_100_MS, +}; + +struct tc358743_platform_data { + /* System clock connected to REFCLK (pin H5) */ + u32 refclk_hz; /* 26 MHz, 27 MHz or 42 MHz */ + + /* DDC +5V debounce delay to avoid spurious interrupts when the cable + * is connected. + * Sets DDC5V_MODE in register DDC_CTL. + * Default: DDC5V_DELAY_0_MS + */ + enum tc358743_ddc5v_delays ddc5v_delay; + + bool enable_hdcp; + + /* + * The FIFO size is 512x32, so Toshiba recommend to set the default FIFO + * level to somewhere in the middle (e.g. 300), so it can cover speed + * mismatches in input and output ports. + */ + u16 fifo_level; + + /* Bps pr lane is (refclk_hz / pll_prd) * pll_fbd */ + u16 pll_prd; + u16 pll_fbd; + + /* CSI + * Calculate CSI parameters with REF_02 for the highest resolution your + * CSI interface can handle. The driver will adjust the number of CSI + * lanes in use according to the pixel clock. + * + * The values in brackets are calculated with REF_02 when the number of + * bps pr lane is 823.5 MHz, and can serve as a starting point. + */ + u32 lineinitcnt; /* (0x00001770) */ + u32 lptxtimecnt; /* (0x00000005) */ + u32 tclk_headercnt; /* (0x00001d04) */ + u32 tclk_trailcnt; /* (0x00000000) */ + u32 ths_headercnt; /* (0x00000505) */ + u32 twakeup; /* (0x00004650) */ + u32 tclk_postcnt; /* (0x00000000) */ + u32 ths_trailcnt; /* (0x00000004) */ + u32 hstxvregcnt; /* (0x00000005) */ + + /* DVI->HDMI detection delay to avoid unnecessary switching between DVI + * and HDMI mode. + * Sets HDMI_DET_V in register HDMI_DET. + * Default: HDMI_MODE_DELAY_0_MS + */ + enum tc358743_hdmi_detection_delay hdmi_detection_delay; + + /* Reset PHY automatically when TMDS clock goes from DC to AC. + * Sets PHY_AUTO_RST2 in register PHY_CTL2. + * Default: false + */ + bool hdmi_phy_auto_reset_tmds_detected; + + /* Reset PHY automatically when TMDS clock passes 21 MHz. + * Sets PHY_AUTO_RST3 in register PHY_CTL2. + * Default: false + */ + bool hdmi_phy_auto_reset_tmds_in_range; + + /* Reset PHY automatically when TMDS clock is detected. + * Sets PHY_AUTO_RST4 in register PHY_CTL2. + * Default: false + */ + bool hdmi_phy_auto_reset_tmds_valid; + + /* Reset HDMI PHY automatically when hsync period is out of range. + * Sets H_PI_RST in register HV_RST. + * Default: false + */ + bool hdmi_phy_auto_reset_hsync_out_of_range; + + /* Reset HDMI PHY automatically when vsync period is out of range. + * Sets V_PI_RST in register HV_RST. + * Default: false + */ + bool hdmi_phy_auto_reset_vsync_out_of_range; +}; + +/* custom controls */ +/* Audio sample rate in Hz */ +#define TC358743_CID_AUDIO_SAMPLING_RATE (V4L2_CID_USER_TC358743_BASE + 0) +/* Audio present status */ +#define TC358743_CID_AUDIO_PRESENT (V4L2_CID_USER_TC358743_BASE + 1) + +#endif diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index 9f6e108ff4a0..d448c536b49d 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -174,6 +174,10 @@ enum v4l2_colorfx { * We reserve 16 controls for this driver. */ #define V4L2_CID_USER_ADV7180_BASE (V4L2_CID_USER_BASE + 0x1070) +/* The base for the tc358743 driver controls. + * We reserve 16 controls for this driver. */ +#define V4L2_CID_USER_TC358743_BASE (V4L2_CID_USER_BASE + 0x1080) + /* MPEG-class control IDs */ /* The MPEG controls are applicable to all codec controls * and the 'MPEG' part of the define is historical */ -- cgit v1.2.3 From e31f8f00bfc081ec1881d92a2dd192aeddf1d9d7 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 24 Jun 2015 11:28:31 -0300 Subject: [media] v4l: xilinx: missing error code We should set "ret" on this error path instead of returning success. Fixes: df3305156f98 ('[media] v4l: xilinx: Add Xilinx Video IP core') Signed-off-by: Dan Carpenter Acked-by: Hyun Kwon Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/xilinx/xilinx-dma.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/xilinx/xilinx-dma.c b/drivers/media/platform/xilinx/xilinx-dma.c index 98e50e446d57..e779c93cb015 100644 --- a/drivers/media/platform/xilinx/xilinx-dma.c +++ b/drivers/media/platform/xilinx/xilinx-dma.c @@ -699,8 +699,10 @@ int xvip_dma_init(struct xvip_composite_device *xdev, struct xvip_dma *dma, /* ... and the buffers queue... */ dma->alloc_ctx = vb2_dma_contig_init_ctx(dma->xdev->dev); - if (IS_ERR(dma->alloc_ctx)) + if (IS_ERR(dma->alloc_ctx)) { + ret = PTR_ERR(dma->alloc_ctx); goto error; + } /* Don't enable VB2_READ and VB2_WRITE, as using the read() and write() * V4L2 APIs would be inefficient. Testing on the command line with a -- cgit v1.2.3 From df5c3e7c8a87a4384ff7a0adba16baae9a40a566 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 19 Jun 2015 08:51:22 -0300 Subject: [media] v4l: vsp1: Fix plane stride and size checks The checks need to be performed on up to two planes, as the third plane, if present, must have the same stride and size as the second plane. The code incorrectly performs the checks on at least two planes instead of at most two planes, fix it. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_video.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index 770e08dc03f1..3c124c14ce14 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -245,7 +245,7 @@ static int __vsp1_video_try_format(struct vsp1_video *video, * the datasheet, strides not aligned to a multiple of 128 bytes result * in image corruption. */ - for (i = 0; i < max(info->planes, 2U); ++i) { + for (i = 0; i < min(info->planes, 2U); ++i) { unsigned int hsub = i > 0 ? info->hsub : 1; unsigned int vsub = i > 0 ? info->vsub : 1; unsigned int align = 128; -- cgit v1.2.3 From adb8963f27e00273c912a53f28f7af5d14cfd32e Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 13 Apr 2015 11:43:40 -0300 Subject: [media] v4l: vsp1: Don't sleep in atomic context The vsp1_entity_is_streaming() function is called in atomic context when queuing buffers, and sleeps due to a mutex. As the mutex just protects access to one structure field, fix this by replace the mutex with a spinlock. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_entity.c | 18 +++++++++--------- drivers/media/platform/vsp1/vsp1_entity.h | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c index a453bb4ddd37..fd95a75b04f4 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.c +++ b/drivers/media/platform/vsp1/vsp1_entity.c @@ -24,22 +24,24 @@ bool vsp1_entity_is_streaming(struct vsp1_entity *entity) { + unsigned long flags; bool streaming; - mutex_lock(&entity->lock); + spin_lock_irqsave(&entity->lock, flags); streaming = entity->streaming; - mutex_unlock(&entity->lock); + spin_unlock_irqrestore(&entity->lock, flags); return streaming; } int vsp1_entity_set_streaming(struct vsp1_entity *entity, bool streaming) { + unsigned long flags; int ret; - mutex_lock(&entity->lock); + spin_lock_irqsave(&entity->lock, flags); entity->streaming = streaming; - mutex_unlock(&entity->lock); + spin_unlock_irqrestore(&entity->lock, flags); if (!streaming) return 0; @@ -49,9 +51,9 @@ int vsp1_entity_set_streaming(struct vsp1_entity *entity, bool streaming) ret = v4l2_ctrl_handler_setup(entity->subdev.ctrl_handler); if (ret < 0) { - mutex_lock(&entity->lock); + spin_lock_irqsave(&entity->lock, flags); entity->streaming = false; - mutex_unlock(&entity->lock); + spin_unlock_irqrestore(&entity->lock, flags); } return ret; @@ -193,7 +195,7 @@ int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, if (i == ARRAY_SIZE(vsp1_routes)) return -EINVAL; - mutex_init(&entity->lock); + spin_lock_init(&entity->lock); entity->vsp1 = vsp1; entity->source_pad = num_pads - 1; @@ -228,6 +230,4 @@ void vsp1_entity_destroy(struct vsp1_entity *entity) if (entity->subdev.ctrl_handler) v4l2_ctrl_handler_free(entity->subdev.ctrl_handler); media_entity_cleanup(&entity->subdev.entity); - - mutex_destroy(&entity->lock); } diff --git a/drivers/media/platform/vsp1/vsp1_entity.h b/drivers/media/platform/vsp1/vsp1_entity.h index 62c768d1c6aa..8867a5787c28 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.h +++ b/drivers/media/platform/vsp1/vsp1_entity.h @@ -14,7 +14,7 @@ #define __VSP1_ENTITY_H__ #include -#include +#include #include @@ -73,7 +73,7 @@ struct vsp1_entity { struct vsp1_video *video; - struct mutex lock; /* Protects the streaming field */ + spinlock_t lock; /* Protects the streaming field */ bool streaming; }; -- cgit v1.2.3 From f964c409f7cc626335cf2370f55690660a273dad Mon Sep 17 00:00:00 2001 From: Lucas Stach Date: Thu, 9 Jul 2015 07:10:12 -0300 Subject: [media] coda: clamp frame sequence counters to 16 bit This is already done for one side of the comparison with the expectation that the HW counter rolls over at the 16 bit boundary. This is true when decoding a h.264 stream, but doesn't hold for at least MJPEG. As we don't know the exact wrap-around point for this format just clamp the HW counter to the same 16 bits. This should be enough to detect most of the errors and saves us from doing different comparisons based on the decoded format. Signed-off-by: Lucas Stach Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda/coda-bit.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c index 109797bb8fbb..9fbff248d7fa 100644 --- a/drivers/media/platform/coda/coda-bit.c +++ b/drivers/media/platform/coda/coda-bit.c @@ -1902,7 +1902,14 @@ static void coda_finish_decode(struct coda_ctx *ctx) meta = list_first_entry(&ctx->buffer_meta_list, struct coda_buffer_meta, list); list_del(&meta->list); - if (val != (meta->sequence & 0xffff)) { + /* + * Clamp counters to 16 bits for comparison, as the HW + * counter rolls over at this point for h.264. This + * may be different for other formats, but using 16 bits + * should be enough to detect most errors and saves us + * from doing different things based on the format. + */ + if ((val & 0xffff) != (meta->sequence & 0xffff)) { v4l2_err(&dev->v4l2_dev, "sequence number mismatch (%d(%d) != %d)\n", val, ctx->sequence_offset, -- cgit v1.2.3 From b05959c66e865fb9eb5b93e223da925572130d7f Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 9 Jul 2015 07:10:13 -0300 Subject: [media] coda: fix mvcol buffer for MPEG4 decoding The mvcol buffer is allocated at the end of the first internal buffer. This patch fixes an out of bounds array access. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda/coda-bit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c index 9fbff248d7fa..47fc2f19a4a9 100644 --- a/drivers/media/platform/coda/coda-bit.c +++ b/drivers/media/platform/coda/coda-bit.c @@ -384,7 +384,7 @@ static int coda_alloc_framebuffers(struct coda_ctx *ctx, /* mvcol buffer for mpeg4 */ if ((dev->devtype->product != CODA_DX6) && (ctx->codec->src_fourcc == V4L2_PIX_FMT_MPEG4)) - coda_parabuf_write(ctx, 97, ctx->internal_frames[i].paddr + + coda_parabuf_write(ctx, 97, ctx->internal_frames[0].paddr + ysize + ysize/4 + ysize/4); return 0; -- cgit v1.2.3 From 58bc7edf1d9a16073761031a03291af887ccdf66 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 9 Jul 2015 07:10:14 -0300 Subject: [media] coda: fix bitstream preloading for MPEG4 decoding All decoder instances using the BIT processor should preload buffers into the bitstream ring buffer, including MPEG4 decoding. Fix this by explicitly stating the above condition instead of listing all relevant input formats. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda/coda-common.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c index 58f65486de33..3259ea61cd9b 100644 --- a/drivers/media/platform/coda/coda-common.c +++ b/drivers/media/platform/coda/coda-common.c @@ -1244,9 +1244,7 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count) q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { - if (q_data_src->fourcc == V4L2_PIX_FMT_H264 || - (q_data_src->fourcc == V4L2_PIX_FMT_JPEG && - ctx->dev->devtype->product == CODA_7541)) { + if (ctx->inst_type == CODA_INST_DECODER && ctx->use_bit) { /* copy the buffers that were queued before streamon */ mutex_lock(&ctx->bitstream_mutex); coda_fill_bitstream(ctx, false); -- cgit v1.2.3 From 30a09579b2e238646bca4e7cc443db24d91436d6 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 9 Jul 2015 07:10:15 -0300 Subject: [media] coda: keep buffers on the queue in bitstream end mode In stream end mode the hardware will read the bitstream to its end, overshooting the write pointer. Do not write additional data into the bitstream in this mode. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda/coda-bit.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c index 47fc2f19a4a9..0f8dcea065af 100644 --- a/drivers/media/platform/coda/coda-bit.c +++ b/drivers/media/platform/coda/coda-bit.c @@ -228,6 +228,9 @@ void coda_fill_bitstream(struct coda_ctx *ctx, bool streaming) struct coda_buffer_meta *meta; u32 start; + if (ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG) + return; + while (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) > 0) { /* * Only queue a single JPEG into the bitstream buffer, except -- cgit v1.2.3 From 5c718bb323aef02ea580073a3640b072fb2f6838 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 9 Jul 2015 07:10:16 -0300 Subject: [media] coda: avoid calling SEQ_END twice Allow coda_seq_end_work to be called multiple times, move the setting of ctx->initialized from coda_start/stop_streaming() into coda_start_encoding/decoding and coda_seq_end_work, respectively, and skip the SEQ_END command in coda_seq_end_work if the context is already deinitialized before. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda/coda-bit.c | 8 ++++++++ drivers/media/platform/coda/coda-common.c | 4 +--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c index 0f8dcea065af..ac4dcb1f446a 100644 --- a/drivers/media/platform/coda/coda-bit.c +++ b/drivers/media/platform/coda/coda-bit.c @@ -999,6 +999,7 @@ static int coda_start_encoding(struct coda_ctx *ctx) ret = -EFAULT; goto out; } + ctx->initialized = 1; if (dst_fourcc != V4L2_PIX_FMT_JPEG) { if (dev->devtype->product == CODA_960) @@ -1329,6 +1330,9 @@ static void coda_seq_end_work(struct work_struct *work) mutex_lock(&ctx->buffer_mutex); mutex_lock(&dev->coda_mutex); + if (ctx->initialized == 0) + goto out; + v4l2_dbg(1, coda_debug, &dev->v4l2_dev, "%d: %s: sent command 'SEQ_END' to coda\n", ctx->idx, __func__); @@ -1342,6 +1346,9 @@ static void coda_seq_end_work(struct work_struct *work) coda_free_framebuffers(ctx); + ctx->initialized = 0; + +out: mutex_unlock(&dev->coda_mutex); mutex_unlock(&ctx->buffer_mutex); } @@ -1499,6 +1506,7 @@ static int __coda_start_decoding(struct coda_ctx *ctx) coda_write(dev, 0, CODA_REG_BIT_BIT_STREAM_PARAM); return -ETIMEDOUT; } + ctx->initialized = 1; /* Update kfifo out pointer from coda bitstream read pointer */ coda_kfifo_sync_from_device(ctx); diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c index 3259ea61cd9b..de0e24533727 100644 --- a/drivers/media/platform/coda/coda-common.c +++ b/drivers/media/platform/coda/coda-common.c @@ -1313,7 +1313,6 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count) goto err; } - ctx->initialized = 1; return ret; err: @@ -1376,7 +1375,6 @@ static void coda_stop_streaming(struct vb2_queue *q) mutex_unlock(&ctx->bitstream_mutex); kfifo_init(&ctx->bitstream_fifo, ctx->bitstream.vaddr, ctx->bitstream.size); - ctx->initialized = 0; ctx->runcounter = 0; ctx->aborting = 0; } @@ -1767,7 +1765,7 @@ static int coda_release(struct file *file) v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); /* In case the instance was not running, we still need to call SEQ_END */ - if (ctx->initialized && ctx->ops->seq_end_work) { + if (ctx->ops->seq_end_work) { queue_work(dev->workqueue, &ctx->seq_end_work); flush_work(&ctx->seq_end_work); } -- cgit v1.2.3 From c1ae0b283d13ad8b53bf3be379b1207085da4b22 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 9 Jul 2015 07:10:17 -0300 Subject: [media] coda: reset stream end in stop_streaming Otherwise a restarted stream won't queue buffers. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda/coda-common.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c index de0e24533727..8b91bda784a7 100644 --- a/drivers/media/platform/coda/coda-common.c +++ b/drivers/media/platform/coda/coda-common.c @@ -1378,6 +1378,9 @@ static void coda_stop_streaming(struct vb2_queue *q) ctx->runcounter = 0; ctx->aborting = 0; } + + if (!ctx->streamon_out && !ctx->streamon_cap) + ctx->bit_stream_param &= ~CODA_BIT_STREAM_END_FLAG; } static const struct vb2_ops coda_qops = { -- cgit v1.2.3 From 8076c7e3f68547ee1d8e07715329f26f7883152a Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 9 Jul 2015 07:10:18 -0300 Subject: [media] coda: drop custom list of pixel format descriptions Since commit ba3002045f80 ("[media] v4l2-ioctl: fill in the description for VIDIOC_ENUM_FMT"), all pixel formats are assigned their description in a central place. We can now drop the custom list. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda/coda-common.c | 75 +++---------------------------- 1 file changed, 7 insertions(+), 68 deletions(-) diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c index 8b91bda784a7..b265edd8d277 100644 --- a/drivers/media/platform/coda/coda-common.c +++ b/drivers/media/platform/coda/coda-common.c @@ -61,11 +61,6 @@ int coda_debug; module_param(coda_debug, int, 0644); MODULE_PARM_DESC(coda_debug, "Debug level (0-2)"); -struct coda_fmt { - char *name; - u32 fourcc; -}; - void coda_write(struct coda_dev *dev, u32 data, u32 reg) { v4l2_dbg(2, coda_debug, &dev->v4l2_dev, @@ -111,40 +106,6 @@ void coda_write_base(struct coda_ctx *ctx, struct coda_q_data *q_data, coda_write(ctx->dev, base_cr, reg_y + 8); } -/* - * Array of all formats supported by any version of Coda: - */ -static const struct coda_fmt coda_formats[] = { - { - .name = "YUV 4:2:0 Planar, YCbCr", - .fourcc = V4L2_PIX_FMT_YUV420, - }, - { - .name = "YUV 4:2:0 Planar, YCrCb", - .fourcc = V4L2_PIX_FMT_YVU420, - }, - { - .name = "YUV 4:2:0 Partial interleaved Y/CbCr", - .fourcc = V4L2_PIX_FMT_NV12, - }, - { - .name = "YUV 4:2:2 Planar, YCbCr", - .fourcc = V4L2_PIX_FMT_YUV422P, - }, - { - .name = "H264 Encoded Stream", - .fourcc = V4L2_PIX_FMT_H264, - }, - { - .name = "MPEG4 Encoded Stream", - .fourcc = V4L2_PIX_FMT_MPEG4, - }, - { - .name = "JPEG Encoded Images", - .fourcc = V4L2_PIX_FMT_JPEG, - }, -}; - #define CODA_CODEC(mode, src_fourcc, dst_fourcc, max_w, max_h) \ { mode, src_fourcc, dst_fourcc, max_w, max_h } @@ -261,40 +222,23 @@ static const struct coda_video_device *coda9_video_devices[] = { &coda_bit_decoder, }; -static bool coda_format_is_yuv(u32 fourcc) +/* + * Normalize all supported YUV 4:2:0 formats to the value used in the codec + * tables. + */ +static u32 coda_format_normalize_yuv(u32 fourcc) { switch (fourcc) { case V4L2_PIX_FMT_YUV420: case V4L2_PIX_FMT_YVU420: case V4L2_PIX_FMT_NV12: case V4L2_PIX_FMT_YUV422P: - return true; + return V4L2_PIX_FMT_YUV420; default: - return false; + return fourcc; } } -static const char *coda_format_name(u32 fourcc) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(coda_formats); i++) { - if (coda_formats[i].fourcc == fourcc) - return coda_formats[i].name; - } - - return NULL; -} - -/* - * Normalize all supported YUV 4:2:0 formats to the value used in the codec - * tables. - */ -static u32 coda_format_normalize_yuv(u32 fourcc) -{ - return coda_format_is_yuv(fourcc) ? V4L2_PIX_FMT_YUV420 : fourcc; -} - static const struct coda_codec *coda_find_codec(struct coda_dev *dev, int src_fourcc, int dst_fourcc) { @@ -396,7 +340,6 @@ static int coda_enum_fmt(struct file *file, void *priv, struct video_device *vdev = video_devdata(file); const struct coda_video_device *cvd = to_coda_video_device(vdev); const u32 *formats; - const char *name; if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) formats = cvd->src_formats; @@ -408,11 +351,7 @@ static int coda_enum_fmt(struct file *file, void *priv, if (f->index >= CODA_MAX_FORMATS || formats[f->index] == 0) return -EINVAL; - name = coda_format_name(formats[f->index]); - strlcpy(f->description, name, sizeof(f->description)); f->pixelformat = formats[f->index]; - if (!coda_format_is_yuv(formats[f->index])) - f->flags |= V4L2_FMT_FLAG_COMPRESSED; return 0; } -- cgit v1.2.3 From f0710815a0e17c63d3f17d365ae556ab15eccb03 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 9 Jul 2015 07:10:19 -0300 Subject: [media] coda: use event class to deduplicate v4l2 trace events Trace events with exactly the same parameters and trace output, such as coda_enc_pic_run and coda_enc_pic_done, are supposed to use the DECLARE_EVENT_CLASS and DEFINE_EVENT macros instead of duplicated TRACE_EVENT macro calls. This patch changes the order of parameters to coda_dec_rot_done and adds a timestamp so it can share an event class with coda_bit_queue. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda/coda-bit.c | 2 +- drivers/media/platform/coda/trace.h | 89 ++++++++++------------------------ 2 files changed, 26 insertions(+), 65 deletions(-) diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c index ac4dcb1f446a..226ce4a786ea 100644 --- a/drivers/media/platform/coda/coda-bit.c +++ b/drivers/media/platform/coda/coda-bit.c @@ -1978,7 +1978,7 @@ static void coda_finish_decode(struct coda_ctx *ctx) dst_buf->v4l2_buf.timecode = meta->timecode; dst_buf->v4l2_buf.timestamp = meta->timestamp; - trace_coda_dec_rot_done(ctx, meta, dst_buf); + trace_coda_dec_rot_done(ctx, dst_buf, meta); switch (q_data_dst->fourcc) { case V4L2_PIX_FMT_YUV420: diff --git a/drivers/media/platform/coda/trace.h b/drivers/media/platform/coda/trace.h index 781bf7286d53..d9099a0f7c32 100644 --- a/drivers/media/platform/coda/trace.h +++ b/drivers/media/platform/coda/trace.h @@ -48,7 +48,7 @@ TRACE_EVENT(coda_bit_done, TP_printk("minor = %d, ctx = %d", __entry->minor, __entry->ctx) ); -TRACE_EVENT(coda_enc_pic_run, +DECLARE_EVENT_CLASS(coda_buf_class, TP_PROTO(struct coda_ctx *ctx, struct vb2_buffer *buf), TP_ARGS(ctx, buf), @@ -69,28 +69,17 @@ TRACE_EVENT(coda_enc_pic_run, __entry->minor, __entry->index, __entry->ctx) ); -TRACE_EVENT(coda_enc_pic_done, +DEFINE_EVENT(coda_buf_class, coda_enc_pic_run, TP_PROTO(struct coda_ctx *ctx, struct vb2_buffer *buf), + TP_ARGS(ctx, buf) +); - TP_ARGS(ctx, buf), - - TP_STRUCT__entry( - __field(int, minor) - __field(int, index) - __field(int, ctx) - ), - - TP_fast_assign( - __entry->minor = ctx->fh.vdev->minor; - __entry->index = buf->v4l2_buf.index; - __entry->ctx = ctx->idx; - ), - - TP_printk("minor = %d, index = %d, ctx = %d", - __entry->minor, __entry->index, __entry->ctx) +DEFINE_EVENT(coda_buf_class, coda_enc_pic_done, + TP_PROTO(struct coda_ctx *ctx, struct vb2_buffer *buf), + TP_ARGS(ctx, buf) ); -TRACE_EVENT(coda_bit_queue, +DECLARE_EVENT_CLASS(coda_buf_meta_class, TP_PROTO(struct coda_ctx *ctx, struct vb2_buffer *buf, struct coda_buffer_meta *meta), @@ -117,7 +106,13 @@ TRACE_EVENT(coda_bit_queue, __entry->ctx) ); -TRACE_EVENT(coda_dec_pic_run, +DEFINE_EVENT(coda_buf_meta_class, coda_bit_queue, + TP_PROTO(struct coda_ctx *ctx, struct vb2_buffer *buf, + struct coda_buffer_meta *meta), + TP_ARGS(ctx, buf, meta) +); + +DECLARE_EVENT_CLASS(coda_meta_class, TP_PROTO(struct coda_ctx *ctx, struct coda_buffer_meta *meta), TP_ARGS(ctx, meta), @@ -140,54 +135,20 @@ TRACE_EVENT(coda_dec_pic_run, __entry->minor, __entry->start, __entry->end, __entry->ctx) ); -TRACE_EVENT(coda_dec_pic_done, +DEFINE_EVENT(coda_meta_class, coda_dec_pic_run, TP_PROTO(struct coda_ctx *ctx, struct coda_buffer_meta *meta), - - TP_ARGS(ctx, meta), - - TP_STRUCT__entry( - __field(int, minor) - __field(int, start) - __field(int, end) - __field(int, ctx) - ), - - TP_fast_assign( - __entry->minor = ctx->fh.vdev->minor; - __entry->start = meta->start; - __entry->end = meta->end; - __entry->ctx = ctx->idx; - ), - - TP_printk("minor = %d, start = 0x%x, end = 0x%x, ctx = %d", - __entry->minor, __entry->start, __entry->end, __entry->ctx) + TP_ARGS(ctx, meta) ); -TRACE_EVENT(coda_dec_rot_done, - TP_PROTO(struct coda_ctx *ctx, struct coda_buffer_meta *meta, - struct vb2_buffer *buf), - - TP_ARGS(ctx, meta, buf), - - TP_STRUCT__entry( - __field(int, minor) - __field(int, start) - __field(int, end) - __field(int, index) - __field(int, ctx) - ), - - TP_fast_assign( - __entry->minor = ctx->fh.vdev->minor; - __entry->start = meta->start; - __entry->end = meta->end; - __entry->index = buf->v4l2_buf.index; - __entry->ctx = ctx->idx; - ), +DEFINE_EVENT(coda_meta_class, coda_dec_pic_done, + TP_PROTO(struct coda_ctx *ctx, struct coda_buffer_meta *meta), + TP_ARGS(ctx, meta) +); - TP_printk("minor = %d, start = 0x%x, end = 0x%x, index = %d, ctx = %d", - __entry->minor, __entry->start, __entry->end, __entry->index, - __entry->ctx) +DEFINE_EVENT(coda_buf_meta_class, coda_dec_rot_done, + TP_PROTO(struct coda_ctx *ctx, struct vb2_buffer *buf, + struct coda_buffer_meta *meta), + TP_ARGS(ctx, buf, meta) ); #endif /* __CODA_TRACE_H__ */ -- cgit v1.2.3 From 2cf251c0c3961bd467e086033c6073ef62b29b02 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 9 Jul 2015 07:10:20 -0300 Subject: [media] coda: reuse src_bufs in coda_job_ready The v4l2_m2m_num_src_bufs_ready() function is called in multiple places in coda_cob_ready, and there already is a variable src_bufs that is assigned to its result. Move it to the beginning and use it everywhere. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda/coda-common.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c index b265edd8d277..267fda760b38 100644 --- a/drivers/media/platform/coda/coda-common.c +++ b/drivers/media/platform/coda/coda-common.c @@ -888,14 +888,14 @@ static void coda_pic_run_work(struct work_struct *work) static int coda_job_ready(void *m2m_priv) { struct coda_ctx *ctx = m2m_priv; + int src_bufs = v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx); /* * For both 'P' and 'key' frame cases 1 picture * and 1 frame are needed. In the decoder case, * the compressed frame can be in the bitstream. */ - if (!v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) && - ctx->inst_type != CODA_INST_DECODER) { + if (!src_bufs && ctx->inst_type != CODA_INST_DECODER) { v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, "not ready: not enough video buffers.\n"); return 0; @@ -911,9 +911,8 @@ static int coda_job_ready(void *m2m_priv) struct list_head *meta; bool stream_end; int num_metas; - int src_bufs; - if (ctx->hold && !v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx)) { + if (ctx->hold && !src_bufs) { v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, "%d: not ready: on hold for more buffers.\n", ctx->idx); @@ -927,8 +926,6 @@ static int coda_job_ready(void *m2m_priv) list_for_each(meta, &ctx->buffer_meta_list) num_metas++; - src_bufs = v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx); - if (!stream_end && (num_metas + src_bufs) < 2) { v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, "%d: not ready: need 2 buffers available (%d, %d)\n", @@ -937,8 +934,8 @@ static int coda_job_ready(void *m2m_priv) } - if (!v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) && - !stream_end && (coda_get_bitstream_payload(ctx) < 512)) { + if (!src_bufs && !stream_end && + (coda_get_bitstream_payload(ctx) < 512)) { v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, "%d: not ready: not enough bitstream data (%d).\n", ctx->idx, coda_get_bitstream_payload(ctx)); -- cgit v1.2.3 From 47f3fa63ee5c0e6bdf9c9d5ed73fc791981336e4 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 9 Jul 2015 07:10:21 -0300 Subject: [media] coda: rework meta counting and add separate lock Keep count of number of buffer meta structures in the list and use a separate spinlock for operations on this counted list instead of reusing the bitstream mutex in some places and none at all in others. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda/coda-bit.c | 16 ++++++++++++++-- drivers/media/platform/coda/coda-common.c | 21 +++++++++------------ drivers/media/platform/coda/coda.h | 2 ++ 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c index 226ce4a786ea..25910ccd2da0 100644 --- a/drivers/media/platform/coda/coda-bit.c +++ b/drivers/media/platform/coda/coda-bit.c @@ -226,6 +226,7 @@ void coda_fill_bitstream(struct coda_ctx *ctx, bool streaming) { struct vb2_buffer *src_buf; struct coda_buffer_meta *meta; + unsigned long flags; u32 start; if (ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG) @@ -274,8 +275,13 @@ void coda_fill_bitstream(struct coda_ctx *ctx, bool streaming) meta->start = start; meta->end = ctx->bitstream_fifo.kfifo.in & ctx->bitstream_fifo.kfifo.mask; + spin_lock_irqsave(&ctx->buffer_meta_lock, + flags); list_add_tail(&meta->list, &ctx->buffer_meta_list); + ctx->num_metas++; + spin_unlock_irqrestore(&ctx->buffer_meta_lock, + flags); trace_coda_bit_queue(ctx, src_buf, meta); } @@ -1665,6 +1671,7 @@ static int coda_prepare_decode(struct coda_ctx *ctx) struct coda_dev *dev = ctx->dev; struct coda_q_data *q_data_dst; struct coda_buffer_meta *meta; + unsigned long flags; u32 reg_addr, reg_stride; dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); @@ -1743,6 +1750,7 @@ static int coda_prepare_decode(struct coda_ctx *ctx) coda_write(dev, ctx->iram_info.axi_sram_use, CODA7_REG_BIT_AXI_SRAM_USE); + spin_lock_irqsave(&ctx->buffer_meta_lock, flags); meta = list_first_entry_or_null(&ctx->buffer_meta_list, struct coda_buffer_meta, list); @@ -1762,6 +1770,7 @@ static int coda_prepare_decode(struct coda_ctx *ctx) kfifo_in(&ctx->bitstream_fifo, buf, pad); } } + spin_unlock_irqrestore(&ctx->buffer_meta_lock, flags); coda_kfifo_sync_to_device_full(ctx); @@ -1783,6 +1792,7 @@ static void coda_finish_decode(struct coda_ctx *ctx) struct vb2_buffer *dst_buf; struct coda_buffer_meta *meta; unsigned long payload; + unsigned long flags; int width, height; int decoded_idx; int display_idx; @@ -1908,11 +1918,13 @@ static void coda_finish_decode(struct coda_ctx *ctx) } else { val = coda_read(dev, CODA_RET_DEC_PIC_FRAME_NUM) - 1; val -= ctx->sequence_offset; - mutex_lock(&ctx->bitstream_mutex); + spin_lock_irqsave(&ctx->buffer_meta_lock, flags); if (!list_empty(&ctx->buffer_meta_list)) { meta = list_first_entry(&ctx->buffer_meta_list, struct coda_buffer_meta, list); list_del(&meta->list); + ctx->num_metas--; + spin_unlock_irqrestore(&ctx->buffer_meta_lock, flags); /* * Clamp counters to 16 bits for comparison, as the HW * counter rolls over at this point for h.264. This @@ -1929,13 +1941,13 @@ static void coda_finish_decode(struct coda_ctx *ctx) ctx->frame_metas[decoded_idx] = *meta; kfree(meta); } else { + spin_unlock_irqrestore(&ctx->buffer_meta_lock, flags); v4l2_err(&dev->v4l2_dev, "empty timestamp list!\n"); memset(&ctx->frame_metas[decoded_idx], 0, sizeof(struct coda_buffer_meta)); ctx->frame_metas[decoded_idx].sequence = val; ctx->sequence_offset++; } - mutex_unlock(&ctx->bitstream_mutex); trace_coda_dec_pic_done(ctx, &ctx->frame_metas[decoded_idx]); diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c index 267fda760b38..367b6baa8f31 100644 --- a/drivers/media/platform/coda/coda-common.c +++ b/drivers/media/platform/coda/coda-common.c @@ -908,9 +908,9 @@ static int coda_job_ready(void *m2m_priv) } if (ctx->inst_type == CODA_INST_DECODER && ctx->use_bit) { - struct list_head *meta; - bool stream_end; - int num_metas; + bool stream_end = ctx->bit_stream_param & + CODA_BIT_STREAM_END_FLAG; + int num_metas = ctx->num_metas; if (ctx->hold && !src_bufs) { v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, @@ -919,13 +919,6 @@ static int coda_job_ready(void *m2m_priv) return 0; } - stream_end = ctx->bit_stream_param & - CODA_BIT_STREAM_END_FLAG; - - num_metas = 0; - list_for_each(meta, &ctx->buffer_meta_list) - num_metas++; - if (!stream_end && (num_metas + src_bufs) < 2) { v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, "%d: not ready: need 2 buffers available (%d, %d)\n", @@ -951,6 +944,7 @@ static int coda_job_ready(void *m2m_priv) v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, "job ready\n"); + return 1; } @@ -1267,6 +1261,7 @@ static void coda_stop_streaming(struct vb2_queue *q) struct coda_ctx *ctx = vb2_get_drv_priv(q); struct coda_dev *dev = ctx->dev; struct vb2_buffer *buf; + unsigned long flags; bool stop; stop = ctx->streamon_out && ctx->streamon_cap; @@ -1301,14 +1296,15 @@ static void coda_stop_streaming(struct vb2_queue *q) queue_work(dev->workqueue, &ctx->seq_end_work); flush_work(&ctx->seq_end_work); } - mutex_lock(&ctx->bitstream_mutex); + spin_lock_irqsave(&ctx->buffer_meta_lock, flags); while (!list_empty(&ctx->buffer_meta_list)) { meta = list_first_entry(&ctx->buffer_meta_list, struct coda_buffer_meta, list); list_del(&meta->list); kfree(meta); } - mutex_unlock(&ctx->bitstream_mutex); + ctx->num_metas = 0; + spin_unlock_irqrestore(&ctx->buffer_meta_lock, flags); kfifo_init(&ctx->bitstream_fifo, ctx->bitstream.vaddr, ctx->bitstream.size); ctx->runcounter = 0; @@ -1661,6 +1657,7 @@ static int coda_open(struct file *file) mutex_init(&ctx->bitstream_mutex); mutex_init(&ctx->buffer_mutex); INIT_LIST_HEAD(&ctx->buffer_meta_list); + spin_lock_init(&ctx->buffer_meta_lock); coda_lock(ctx); list_add(&ctx->list, &dev->instances); diff --git a/drivers/media/platform/coda/coda.h b/drivers/media/platform/coda/coda.h index 8e0af221b2e9..a3d70ccd1020 100644 --- a/drivers/media/platform/coda/coda.h +++ b/drivers/media/platform/coda/coda.h @@ -227,6 +227,8 @@ struct coda_ctx { struct coda_buffer_meta frame_metas[CODA_MAX_FRAMEBUFFERS]; u32 frame_errors[CODA_MAX_FRAMEBUFFERS]; struct list_head buffer_meta_list; + spinlock_t buffer_meta_lock; + int num_metas; struct coda_aux_buf workbuf; int num_internal_frames; int idx; -- cgit v1.2.3 From 68aa7ee15cd683006ec1ac91ad60c019bf62d978 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 10 Jul 2015 10:37:44 -0300 Subject: [media] coda: reset CODA960 hardware after sequence end On i.MX6, sometimes after decoding a stream, encoding will produce macroblock errors caused by missing 8-byte sequences in the output stream. Until the cause for this is found, reset the hardware after sequence end, which seems to help. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda/coda-bit.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c index 25910ccd2da0..bcb9911d5e3f 100644 --- a/drivers/media/platform/coda/coda-bit.c +++ b/drivers/media/platform/coda/coda-bit.c @@ -1347,6 +1347,14 @@ static void coda_seq_end_work(struct work_struct *work) "CODA_COMMAND_SEQ_END failed\n"); } + /* + * FIXME: Sometimes h.264 encoding fails with 8-byte sequences missing + * from the output stream after the h.264 decoder has run. Resetting the + * hardware after the decoder has finished seems to help. + */ + if (dev->devtype->product == CODA_960) + coda_hw_reset(ctx); + kfifo_init(&ctx->bitstream_fifo, ctx->bitstream.vaddr, ctx->bitstream.size); -- cgit v1.2.3 From da2b3b3e115d2793b5475039ca4d7f364135fcf4 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 10 Jul 2015 10:37:52 -0300 Subject: [media] coda: implement VBV delay and buffer size controls The encoder allows to specify the VBV model reference decoder's initial delay and buffer size. Export the corresponding V4L2 controls. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda/coda-bit.c | 5 ++++- drivers/media/platform/coda/coda-common.c | 14 ++++++++++++++ drivers/media/platform/coda/coda.h | 2 ++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c index bcb9911d5e3f..b14affcf8dc0 100644 --- a/drivers/media/platform/coda/coda-bit.c +++ b/drivers/media/platform/coda/coda-bit.c @@ -922,6 +922,9 @@ static int coda_start_encoding(struct coda_ctx *ctx) value = (ctx->params.bitrate & CODA_RATECONTROL_BITRATE_MASK) << CODA_RATECONTROL_BITRATE_OFFSET; value |= 1 & CODA_RATECONTROL_ENABLE_MASK; + value |= (ctx->params.vbv_delay & + CODA_RATECONTROL_INITIALDELAY_MASK) + << CODA_RATECONTROL_INITIALDELAY_OFFSET; if (dev->devtype->product == CODA_960) value |= BIT(31); /* disable autoskip */ } else { @@ -929,7 +932,7 @@ static int coda_start_encoding(struct coda_ctx *ctx) } coda_write(dev, value, CODA_CMD_ENC_SEQ_RC_PARA); - coda_write(dev, 0, CODA_CMD_ENC_SEQ_RC_BUF_SIZE); + coda_write(dev, ctx->params.vbv_size, CODA_CMD_ENC_SEQ_RC_BUF_SIZE); coda_write(dev, ctx->params.intra_refresh, CODA_CMD_ENC_SEQ_INTRA_REFRESH); diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c index 367b6baa8f31..24737f1a1a1b 100644 --- a/drivers/media/platform/coda/coda-common.c +++ b/drivers/media/platform/coda/coda-common.c @@ -1400,6 +1400,12 @@ static int coda_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_JPEG_RESTART_INTERVAL: ctx->params.jpeg_restart_interval = ctrl->val; break; + case V4L2_CID_MPEG_VIDEO_VBV_DELAY: + ctx->params.vbv_delay = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_VBV_SIZE: + ctx->params.vbv_size = min(ctrl->val * 8192, 0x7fffffff); + break; default: v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, "Invalid control, id=%d, val=%d\n", @@ -1459,6 +1465,14 @@ static void coda_encode_ctrls(struct coda_ctx *ctx) v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB, 0, 1920 * 1088 / 256, 1, 0); + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_VBV_DELAY, 0, 0x7fff, 1, 0); + /* + * The maximum VBV size value is 0x7fffffff bits, + * one bit less than 262144 KiB + */ + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_VBV_SIZE, 0, 262144, 1, 0); } static void coda_jpeg_encode_ctrls(struct coda_ctx *ctx) diff --git a/drivers/media/platform/coda/coda.h b/drivers/media/platform/coda/coda.h index a3d70ccd1020..26c9c4bb6e4a 100644 --- a/drivers/media/platform/coda/coda.h +++ b/drivers/media/platform/coda/coda.h @@ -128,6 +128,8 @@ struct coda_params { enum v4l2_mpeg_video_multi_slice_mode slice_mode; u32 framerate; u16 bitrate; + u16 vbv_delay; + u32 vbv_size; u32 slice_max_bits; u32 slice_max_mb; }; -- cgit v1.2.3 From cde29ef313de391ec9a1e461458274623ab1eb87 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 16 Jul 2015 13:13:24 -0300 Subject: [media] coda: Use S_PARM to set nominal framerate for h.264 encoder The encoder needs to know the nominal framerate for the constant bitrate control mechanism to work. Currently the only way to set the framerate is by using VIDIOC_S_PARM on the output queue. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda/coda-common.c | 102 ++++++++++++++++++++++++++++++ drivers/media/platform/coda/coda_regs.h | 4 ++ 2 files changed, 106 insertions(+) diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c index 24737f1a1a1b..a7cab14d10c9 100644 --- a/drivers/media/platform/coda/coda-common.c +++ b/drivers/media/platform/coda/coda-common.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -770,6 +771,104 @@ static int coda_decoder_cmd(struct file *file, void *fh, return 0; } +static int coda_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a) +{ + struct coda_ctx *ctx = fh_to_ctx(fh); + struct v4l2_fract *tpf; + + if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + a->parm.output.capability = V4L2_CAP_TIMEPERFRAME; + tpf = &a->parm.output.timeperframe; + tpf->denominator = ctx->params.framerate & CODA_FRATE_RES_MASK; + tpf->numerator = 1 + (ctx->params.framerate >> + CODA_FRATE_DIV_OFFSET); + + return 0; +} + +/* + * Approximate timeperframe v4l2_fract with values that can be written + * into the 16-bit CODA_FRATE_DIV and CODA_FRATE_RES fields. + */ +static void coda_approximate_timeperframe(struct v4l2_fract *timeperframe) +{ + struct v4l2_fract s = *timeperframe; + struct v4l2_fract f0; + struct v4l2_fract f1 = { 1, 0 }; + struct v4l2_fract f2 = { 0, 1 }; + unsigned int i, div, s_denominator; + + /* Lower bound is 1/65535 */ + if (s.numerator == 0 || s.denominator / s.numerator > 65535) { + timeperframe->numerator = 1; + timeperframe->denominator = 65535; + return; + } + + /* Upper bound is 65536/1, map everything above to infinity */ + if (s.denominator == 0 || s.numerator / s.denominator > 65536) { + timeperframe->numerator = 1; + timeperframe->denominator = 0; + return; + } + + /* Reduce fraction to lowest terms */ + div = gcd(s.numerator, s.denominator); + if (div > 1) { + s.numerator /= div; + s.denominator /= div; + } + + if (s.numerator <= 65536 && s.denominator < 65536) { + *timeperframe = s; + return; + } + + /* Find successive convergents from continued fraction expansion */ + while (f2.numerator <= 65536 && f2.denominator < 65536) { + f0 = f1; + f1 = f2; + + /* Stop when f2 exactly equals timeperframe */ + if (s.numerator == 0) + break; + + i = s.denominator / s.numerator; + + f2.numerator = f0.numerator + i * f1.numerator; + f2.denominator = f0.denominator + i * f2.denominator; + + s_denominator = s.numerator; + s.numerator = s.denominator % s.numerator; + s.denominator = s_denominator; + } + + *timeperframe = f1; +} + +static uint32_t coda_timeperframe_to_frate(struct v4l2_fract *timeperframe) +{ + return ((timeperframe->numerator - 1) << CODA_FRATE_DIV_OFFSET) | + timeperframe->denominator; +} + +static int coda_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a) +{ + struct coda_ctx *ctx = fh_to_ctx(fh); + struct v4l2_fract *tpf; + + if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + tpf = &a->parm.output.timeperframe; + coda_approximate_timeperframe(tpf); + ctx->params.framerate = coda_timeperframe_to_frate(tpf); + + return 0; +} + static int coda_subscribe_event(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub) { @@ -810,6 +909,9 @@ static const struct v4l2_ioctl_ops coda_ioctl_ops = { .vidioc_try_decoder_cmd = coda_try_decoder_cmd, .vidioc_decoder_cmd = coda_decoder_cmd, + .vidioc_g_parm = coda_g_parm, + .vidioc_s_parm = coda_s_parm, + .vidioc_subscribe_event = coda_subscribe_event, .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; diff --git a/drivers/media/platform/coda/coda_regs.h b/drivers/media/platform/coda/coda_regs.h index 7d026241171b..00e4f5160c1f 100644 --- a/drivers/media/platform/coda/coda_regs.h +++ b/drivers/media/platform/coda/coda_regs.h @@ -263,6 +263,10 @@ #define CODADX6_PICHEIGHT_MASK 0x3ff #define CODA7_PICHEIGHT_MASK 0xffff #define CODA_CMD_ENC_SEQ_SRC_F_RATE 0x194 +#define CODA_FRATE_RES_OFFSET 0 +#define CODA_FRATE_RES_MASK 0xffff +#define CODA_FRATE_DIV_OFFSET 16 +#define CODA_FRATE_DIV_MASK 0xffff #define CODA_CMD_ENC_SEQ_MP4_PARA 0x198 #define CODA_MP4PARAM_VERID_OFFSET 6 #define CODA_MP4PARAM_VERID_MASK 0x01 -- cgit v1.2.3 From 4e447ff199cfc4bc04ddb515d3d9ab46bb19530a Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 16 Jul 2015 13:19:37 -0300 Subject: [media] coda: move cache setup into coda9_set_frame_cache, also use it in start_encoding The frame cache should be set up correctly to encode NV12 source frames. This was not done before, so move the cache setup out of start_decoding into its own function and call it from both start_encoding and start_decoding. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda/coda-bit.c | 45 +++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c index b14affcf8dc0..46c70547fc12 100644 --- a/drivers/media/platform/coda/coda-bit.c +++ b/drivers/media/platform/coda/coda-bit.c @@ -721,6 +721,26 @@ err_clk_per: return ret; } +static void coda9_set_frame_cache(struct coda_ctx *ctx, u32 fourcc) +{ + u32 cache_size, cache_config; + + /* Luma 2x0 page, 2x6 cache, chroma 2x0 page, 2x4 cache size */ + cache_size = 0x20262024; + cache_config = 2 << CODA9_CACHE_PAGEMERGE_OFFSET; + coda_write(ctx->dev, cache_size, CODA9_CMD_SET_FRAME_CACHE_SIZE); + if (fourcc == V4L2_PIX_FMT_NV12) { + cache_config |= 32 << CODA9_CACHE_LUMA_BUFFER_SIZE_OFFSET | + 16 << CODA9_CACHE_CR_BUFFER_SIZE_OFFSET | + 0 << CODA9_CACHE_CB_BUFFER_SIZE_OFFSET; + } else { + cache_config |= 32 << CODA9_CACHE_LUMA_BUFFER_SIZE_OFFSET | + 8 << CODA9_CACHE_CR_BUFFER_SIZE_OFFSET | + 8 << CODA9_CACHE_CB_BUFFER_SIZE_OFFSET; + } + coda_write(ctx->dev, cache_config, CODA9_CMD_SET_FRAME_CACHE_CONFIG); +} + /* * Encoder context operations */ @@ -1049,6 +1069,8 @@ static int coda_start_encoding(struct coda_ctx *ctx) coda_write(dev, ctx->iram_info.buf_btp_use, CODA9_CMD_SET_FRAME_AXI_BTP_ADDR); + coda9_set_frame_cache(ctx, q_data_src->fourcc); + /* FIXME */ coda_write(dev, ctx->internal_frames[2].paddr, CODA9_CMD_SET_FRAME_SUBSAMP_A); @@ -1606,30 +1628,13 @@ static int __coda_start_decoding(struct coda_ctx *ctx) CODA7_CMD_SET_FRAME_AXI_DBKC_ADDR); coda_write(dev, ctx->iram_info.buf_ovl_use, CODA7_CMD_SET_FRAME_AXI_OVL_ADDR); - if (dev->devtype->product == CODA_960) + if (dev->devtype->product == CODA_960) { coda_write(dev, ctx->iram_info.buf_btp_use, CODA9_CMD_SET_FRAME_AXI_BTP_ADDR); - } - - if (dev->devtype->product == CODA_960) { - int cbb_size, crb_size; - - coda_write(dev, -1, CODA9_CMD_SET_FRAME_DELAY); - /* Luma 2x0 page, 2x6 cache, chroma 2x0 page, 2x4 cache size */ - coda_write(dev, 0x20262024, CODA9_CMD_SET_FRAME_CACHE_SIZE); - if (dst_fourcc == V4L2_PIX_FMT_NV12) { - cbb_size = 0; - crb_size = 16; - } else { - cbb_size = 8; - crb_size = 8; + coda_write(dev, -1, CODA9_CMD_SET_FRAME_DELAY); + coda9_set_frame_cache(ctx, dst_fourcc); } - coda_write(dev, 2 << CODA9_CACHE_PAGEMERGE_OFFSET | - 32 << CODA9_CACHE_LUMA_BUFFER_SIZE_OFFSET | - cbb_size << CODA9_CACHE_CB_BUFFER_SIZE_OFFSET | - crb_size << CODA9_CACHE_CR_BUFFER_SIZE_OFFSET, - CODA9_CMD_SET_FRAME_CACHE_CONFIG); } if (src_fourcc == V4L2_PIX_FMT_H264) { -- cgit v1.2.3 From a269e53b1aa87157311e53e5b69699ba8f3ba4d0 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 16 Jul 2015 13:19:38 -0300 Subject: [media] coda: add macroblock tiling support Storing internal frames in macroblock tiled order improves memory access patterns by allowing increased burst sizes when transferring the uncompressed macroblocks to or from main memory. The translation logic only supports a single chroma base address, so this is only supported for the chroma interleaved NV12 format. Since the rotator used to copy the decoder output into the v4l2 capture buffers does not seem to support the tiled format correctly, only enable it in the encoder for now. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda/Makefile | 2 +- drivers/media/platform/coda/coda-bit.c | 48 +++++++--- drivers/media/platform/coda/coda-common.c | 74 +++++---------- drivers/media/platform/coda/coda-gdi.c | 150 ++++++++++++++++++++++++++++++ drivers/media/platform/coda/coda.h | 11 +-- drivers/media/platform/coda/coda_regs.h | 6 ++ 6 files changed, 221 insertions(+), 70 deletions(-) create mode 100644 drivers/media/platform/coda/coda-gdi.c diff --git a/drivers/media/platform/coda/Makefile b/drivers/media/platform/coda/Makefile index 834e504bf085..9342ac57b230 100644 --- a/drivers/media/platform/coda/Makefile +++ b/drivers/media/platform/coda/Makefile @@ -1,5 +1,5 @@ ccflags-y += -I$(src) -coda-objs := coda-common.o coda-bit.o coda-h264.o coda-jpeg.o +coda-objs := coda-common.o coda-bit.o coda-gdi.o coda-h264.o coda-jpeg.o obj-$(CONFIG_VIDEO_CODA) += coda.o diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c index 46c70547fc12..3d434a4ed537 100644 --- a/drivers/media/platform/coda/coda-bit.c +++ b/drivers/media/platform/coda/coda-bit.c @@ -340,7 +340,6 @@ static int coda_alloc_framebuffers(struct coda_ctx *ctx, { struct coda_dev *dev = ctx->dev; int width, height; - dma_addr_t paddr; int ysize; int ret; int i; @@ -360,7 +359,10 @@ static int coda_alloc_framebuffers(struct coda_ctx *ctx, size_t size; char *name; - size = ysize + ysize / 2; + if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP) + size = round_up(ysize, 4096) + ysize / 2; + else + size = ysize + ysize / 2; if (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264 && dev->devtype->product != CODA_DX6) size += ysize / 4; @@ -376,11 +378,23 @@ static int coda_alloc_framebuffers(struct coda_ctx *ctx, /* Register frame buffers in the parameter buffer */ for (i = 0; i < ctx->num_internal_frames; i++) { - paddr = ctx->internal_frames[i].paddr; + u32 y, cb, cr; + /* Start addresses of Y, Cb, Cr planes */ - coda_parabuf_write(ctx, i * 3 + 0, paddr); - coda_parabuf_write(ctx, i * 3 + 1, paddr + ysize); - coda_parabuf_write(ctx, i * 3 + 2, paddr + ysize + ysize / 4); + y = ctx->internal_frames[i].paddr; + cb = y + ysize; + cr = y + ysize + ysize/4; + if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP) { + cb = round_up(cb, 4096); + cr = 0; + /* Packed 20-bit MSB of base addresses */ + /* YYYYYCCC, CCyyyyyc, cccc.... */ + y = (y & 0xfffff000) | cb >> 20; + cb = (cb & 0x000ff000) << 12; + } + coda_parabuf_write(ctx, i * 3 + 0, y); + coda_parabuf_write(ctx, i * 3 + 1, cb); + coda_parabuf_write(ctx, i * 3 + 2, cr); /* mvcol buffer for h.264 */ if (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264 && @@ -725,9 +739,15 @@ static void coda9_set_frame_cache(struct coda_ctx *ctx, u32 fourcc) { u32 cache_size, cache_config; - /* Luma 2x0 page, 2x6 cache, chroma 2x0 page, 2x4 cache size */ - cache_size = 0x20262024; - cache_config = 2 << CODA9_CACHE_PAGEMERGE_OFFSET; + if (ctx->tiled_map_type == GDI_LINEAR_FRAME_MAP) { + /* Luma 2x0 page, 2x6 cache, chroma 2x0 page, 2x4 cache size */ + cache_size = 0x20262024; + cache_config = 2 << CODA9_CACHE_PAGEMERGE_OFFSET; + } else { + /* Luma 0x2 page, 4x4 cache, chroma 0x2 page, 4x3 cache size */ + cache_size = 0x02440243; + cache_config = 1 << CODA9_CACHE_PAGEMERGE_OFFSET; + } coda_write(ctx->dev, cache_size, CODA9_CMD_SET_FRAME_CACHE_SIZE); if (fourcc == V4L2_PIX_FMT_NV12) { cache_config |= 32 << CODA9_CACHE_LUMA_BUFFER_SIZE_OFFSET | @@ -818,9 +838,12 @@ static int coda_start_encoding(struct coda_ctx *ctx) break; } - ctx->frame_mem_ctrl &= ~CODA_FRAME_CHROMA_INTERLEAVE; + ctx->frame_mem_ctrl &= ~(CODA_FRAME_CHROMA_INTERLEAVE | (0x3 << 9) | + CODA9_FRAME_TILED2LINEAR); if (q_data_src->fourcc == V4L2_PIX_FMT_NV12) ctx->frame_mem_ctrl |= CODA_FRAME_CHROMA_INTERLEAVE; + if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP) + ctx->frame_mem_ctrl |= (0x3 << 9) | CODA9_FRAME_TILED2LINEAR; coda_write(dev, ctx->frame_mem_ctrl, CODA_REG_BIT_FRAME_MEM_CTRL); if (dev->devtype->product == CODA_DX6) { @@ -1497,9 +1520,12 @@ static int __coda_start_decoding(struct coda_ctx *ctx) /* Update coda bitstream read and write pointers from kfifo */ coda_kfifo_sync_to_device_full(ctx); - ctx->frame_mem_ctrl &= ~CODA_FRAME_CHROMA_INTERLEAVE; + ctx->frame_mem_ctrl &= ~(CODA_FRAME_CHROMA_INTERLEAVE | (0x3 << 9) | + CODA9_FRAME_TILED2LINEAR); if (dst_fourcc == V4L2_PIX_FMT_NV12) ctx->frame_mem_ctrl |= CODA_FRAME_CHROMA_INTERLEAVE; + if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP) + ctx->frame_mem_ctrl |= (0x3 << 9) | CODA9_FRAME_TILED2LINEAR; coda_write(dev, ctx->frame_mem_ctrl, CODA_REG_BIT_FRAME_MEM_CTRL); ctx->display_idx = -1; diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c index a7cab14d10c9..d62b82846037 100644 --- a/drivers/media/platform/coda/coda-common.c +++ b/drivers/media/platform/coda/coda-common.c @@ -62,6 +62,10 @@ int coda_debug; module_param(coda_debug, int, 0644); MODULE_PARM_DESC(coda_debug, "Debug level (0-2)"); +static int disable_tiling; +module_param(disable_tiling, int, 0644); +MODULE_PARM_DESC(disable_tiling, "Disable tiled frame buffers"); + void coda_write(struct coda_dev *dev, u32 data, u32 reg) { v4l2_dbg(2, coda_debug, &dev->v4l2_dev, @@ -585,6 +589,22 @@ static int coda_s_fmt(struct coda_ctx *ctx, struct v4l2_format *f) q_data->rect.width = f->fmt.pix.width; q_data->rect.height = f->fmt.pix.height; + switch (f->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_NV12: + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + ctx->tiled_map_type = GDI_TILED_FRAME_MB_RASTER_MAP; + if (!disable_tiling) + break; + } + /* else fall through */ + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + ctx->tiled_map_type = GDI_LINEAR_FRAME_MAP; + break; + default: + break; + } + v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, "Setting format for type %d, wxh: %dx%d, fmt: %d\n", f->type, q_data->width, q_data->height, q_data->fourcc); @@ -916,27 +936,6 @@ static const struct v4l2_ioctl_ops coda_ioctl_ops = { .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; -void coda_set_gdi_regs(struct coda_ctx *ctx) -{ - struct gdi_tiled_map *tiled_map = &ctx->tiled_map; - struct coda_dev *dev = ctx->dev; - int i; - - for (i = 0; i < 16; i++) - coda_write(dev, tiled_map->xy2ca_map[i], - CODA9_GDI_XY2_CAS_0 + 4 * i); - for (i = 0; i < 4; i++) - coda_write(dev, tiled_map->xy2ba_map[i], - CODA9_GDI_XY2_BA_0 + 4 * i); - for (i = 0; i < 16; i++) - coda_write(dev, tiled_map->xy2ra_map[i], - CODA9_GDI_XY2_RAS_0 + 4 * i); - coda_write(dev, tiled_map->xy2rbc_config, CODA9_GDI_XY2_RBC_CONFIG); - for (i = 0; i < 32; i++) - coda_write(dev, tiled_map->rbc2axi_map[i], - CODA9_GDI_RBC2_AXI_0 + 4 * i); -} - /* * Mem-to-mem operations. */ @@ -1084,32 +1083,6 @@ static const struct v4l2_m2m_ops coda_m2m_ops = { .unlock = coda_unlock, }; -static void coda_set_tiled_map_type(struct coda_ctx *ctx, int tiled_map_type) -{ - struct gdi_tiled_map *tiled_map = &ctx->tiled_map; - int luma_map, chro_map, i; - - memset(tiled_map, 0, sizeof(*tiled_map)); - - luma_map = 64; - chro_map = 64; - tiled_map->map_type = tiled_map_type; - for (i = 0; i < 16; i++) - tiled_map->xy2ca_map[i] = luma_map << 8 | chro_map; - for (i = 0; i < 4; i++) - tiled_map->xy2ba_map[i] = luma_map << 8 | chro_map; - for (i = 0; i < 16; i++) - tiled_map->xy2ra_map[i] = luma_map << 8 | chro_map; - - if (tiled_map_type == GDI_LINEAR_FRAME_MAP) { - tiled_map->xy2rbc_config = 0; - } else { - dev_err(&ctx->dev->plat_dev->dev, "invalid map type: %d\n", - tiled_map_type); - return; - } -} - static void set_default_params(struct coda_ctx *ctx) { unsigned int max_w, max_h, usize, csize; @@ -1148,8 +1121,11 @@ static void set_default_params(struct coda_ctx *ctx) ctx->q_data[V4L2_M2M_DST].rect.width = max_w; ctx->q_data[V4L2_M2M_DST].rect.height = max_h; - if (ctx->dev->devtype->product == CODA_960) - coda_set_tiled_map_type(ctx, GDI_LINEAR_FRAME_MAP); + /* + * Since the RBC2AXI logic only supports a single chroma plane, + * macroblock tiling only works for to NV12 pixel format. + */ + ctx->tiled_map_type = GDI_LINEAR_FRAME_MAP; } /* diff --git a/drivers/media/platform/coda/coda-gdi.c b/drivers/media/platform/coda/coda-gdi.c new file mode 100644 index 000000000000..aaa7afc6870f --- /dev/null +++ b/drivers/media/platform/coda/coda-gdi.c @@ -0,0 +1,150 @@ +/* + * Coda multi-standard codec IP + * + * Copyright (C) 2014 Philipp Zabel, Pengutronix + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include "coda.h" + +#define XY2_INVERT BIT(7) +#define XY2_ZERO BIT(6) +#define XY2_TB_XOR BIT(5) +#define XY2_XYSEL BIT(4) +#define XY2_Y (1 << 4) +#define XY2_X (0 << 4) + +#define XY2(luma_sel, luma_bit, chroma_sel, chroma_bit) \ + (((XY2_##luma_sel) | (luma_bit)) << 8 | \ + (XY2_##chroma_sel) | (chroma_bit)) + +static const u16 xy2ca_zero_map[16] = { + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), +}; + +static const u16 xy2ca_tiled_map[16] = { + XY2(Y, 0, Y, 0), + XY2(Y, 1, Y, 1), + XY2(Y, 2, Y, 2), + XY2(Y, 3, X, 3), + XY2(X, 3, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), + XY2(ZERO, 0, ZERO, 0), +}; + +/* + * RA[15:0], CA[15:8] are hardwired to contain the 24-bit macroblock + * start offset (macroblock size is 16x16 for luma, 16x8 for chroma). + * Bits CA[4:0] are set using XY2CA above. BA[3:0] seems to be unused. + */ + +#define RBC_CA (0 << 4) +#define RBC_BA (1 << 4) +#define RBC_RA (2 << 4) +#define RBC_ZERO (3 << 4) + +#define RBC(luma_sel, luma_bit, chroma_sel, chroma_bit) \ + (((RBC_##luma_sel) | (luma_bit)) << 6 | \ + (RBC_##chroma_sel) | (chroma_bit)) + +static const u16 rbc2axi_tiled_map[32] = { + RBC(ZERO, 0, ZERO, 0), + RBC(ZERO, 0, ZERO, 0), + RBC(ZERO, 0, ZERO, 0), + RBC(CA, 0, CA, 0), + RBC(CA, 1, CA, 1), + RBC(CA, 2, CA, 2), + RBC(CA, 3, CA, 3), + RBC(CA, 4, CA, 8), + RBC(CA, 8, CA, 9), + RBC(CA, 9, CA, 10), + RBC(CA, 10, CA, 11), + RBC(CA, 11, CA, 12), + RBC(CA, 12, CA, 13), + RBC(CA, 13, CA, 14), + RBC(CA, 14, CA, 15), + RBC(CA, 15, RA, 0), + RBC(RA, 0, RA, 1), + RBC(RA, 1, RA, 2), + RBC(RA, 2, RA, 3), + RBC(RA, 3, RA, 4), + RBC(RA, 4, RA, 5), + RBC(RA, 5, RA, 6), + RBC(RA, 6, RA, 7), + RBC(RA, 7, RA, 8), + RBC(RA, 8, RA, 9), + RBC(RA, 9, RA, 10), + RBC(RA, 10, RA, 11), + RBC(RA, 11, RA, 12), + RBC(RA, 12, RA, 13), + RBC(RA, 13, RA, 14), + RBC(RA, 14, RA, 15), + RBC(RA, 15, ZERO, 0), +}; + +void coda_set_gdi_regs(struct coda_ctx *ctx) +{ + struct coda_dev *dev = ctx->dev; + const u16 *xy2ca_map; + u32 xy2rbc_config; + int i; + + switch (ctx->tiled_map_type) { + case GDI_LINEAR_FRAME_MAP: + default: + xy2ca_map = xy2ca_zero_map; + xy2rbc_config = 0; + break; + case GDI_TILED_FRAME_MB_RASTER_MAP: + xy2ca_map = xy2ca_tiled_map; + xy2rbc_config = CODA9_XY2RBC_TILED_MAP | + CODA9_XY2RBC_CA_INC_HOR | + (16 - 1) << 12 | (8 - 1) << 4; + break; + } + + for (i = 0; i < 16; i++) + coda_write(dev, xy2ca_map[i], + CODA9_GDI_XY2_CAS_0 + 4 * i); + for (i = 0; i < 4; i++) + coda_write(dev, XY2(ZERO, 0, ZERO, 0), + CODA9_GDI_XY2_BA_0 + 4 * i); + for (i = 0; i < 16; i++) + coda_write(dev, XY2(ZERO, 0, ZERO, 0), + CODA9_GDI_XY2_RAS_0 + 4 * i); + coda_write(dev, xy2rbc_config, CODA9_GDI_XY2_RBC_CONFIG); + if (xy2rbc_config) { + for (i = 0; i < 32; i++) + coda_write(dev, rbc2axi_tiled_map[i], + CODA9_GDI_RBC2_AXI_0 + 4 * i); + } +} diff --git a/drivers/media/platform/coda/coda.h b/drivers/media/platform/coda/coda.h index 26c9c4bb6e4a..59b2af9c7749 100644 --- a/drivers/media/platform/coda/coda.h +++ b/drivers/media/platform/coda/coda.h @@ -167,15 +167,8 @@ struct coda_iram_info { phys_addr_t next_paddr; }; -struct gdi_tiled_map { - int xy2ca_map[16]; - int xy2ba_map[16]; - int xy2ra_map[16]; - int rbc2axi_map[32]; - int xy2rbc_config; - int map_type; #define GDI_LINEAR_FRAME_MAP 0 -}; +#define GDI_TILED_FRAME_MB_RASTER_MAP 1 struct coda_ctx; @@ -236,7 +229,7 @@ struct coda_ctx { int idx; int reg_idx; struct coda_iram_info iram_info; - struct gdi_tiled_map tiled_map; + int tiled_map_type; u32 bit_stream_param; u32 frm_dis_flg; u32 frame_mem_ctrl; diff --git a/drivers/media/platform/coda/coda_regs.h b/drivers/media/platform/coda/coda_regs.h index 00e4f5160c1f..3490602fa6e1 100644 --- a/drivers/media/platform/coda/coda_regs.h +++ b/drivers/media/platform/coda/coda_regs.h @@ -51,6 +51,7 @@ #define CODA7_STREAM_SEL_64BITS_ENDIAN (1 << 1) #define CODA_STREAM_ENDIAN_SELECT (1 << 0) #define CODA_REG_BIT_FRAME_MEM_CTRL 0x110 +#define CODA9_FRAME_TILED2LINEAR (1 << 11) #define CODA_FRAME_CHROMA_INTERLEAVE (1 << 2) #define CODA_IMAGE_ENDIAN_SELECT (1 << 0) #define CODA_REG_BIT_BIT_STREAM_PARAM 0x114 @@ -452,7 +453,12 @@ #define CODA9_GDI_XY2_RAS_F (CODA9_GDMA_BASE + 0x88c) #define CODA9_GDI_XY2_RBC_CONFIG (CODA9_GDMA_BASE + 0x890) +#define CODA9_XY2RBC_SEPARATE_MAP BIT(19) +#define CODA9_XY2RBC_TOP_BOT_SPLIT BIT(18) +#define CODA9_XY2RBC_TILED_MAP BIT(17) +#define CODA9_XY2RBC_CA_INC_HOR BIT(16) #define CODA9_GDI_RBC2_AXI_0 (CODA9_GDMA_BASE + 0x8a0) #define CODA9_GDI_RBC2_AXI_1F (CODA9_GDMA_BASE + 0x91c) +#define CODA9_GDI_TILEDBUF_BASE (CODA9_GDMA_BASE + 0x920) #endif -- cgit v1.2.3 From 6727d4fce95586e60922bdaf57b8a0eb99482557 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 16 Jul 2015 13:19:39 -0300 Subject: [media] coda: make NV12 format default The chroma interleaved NV12 format has higher memory bandwidth efficiency because the chroma planes can be read/written with longer burst lengths. Use NV12 as default format if available and consistently sort it first. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda/coda-common.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c index d62b82846037..04310cd35bc1 100644 --- a/drivers/media/platform/coda/coda-common.c +++ b/drivers/media/platform/coda/coda-common.c @@ -90,17 +90,17 @@ void coda_write_base(struct coda_ctx *ctx, struct coda_q_data *q_data, u32 base_cb, base_cr; switch (q_data->fourcc) { - case V4L2_PIX_FMT_YVU420: - /* Switch Cb and Cr for YVU420 format */ - base_cr = base_y + q_data->bytesperline * q_data->height; - base_cb = base_cr + q_data->bytesperline * q_data->height / 4; - break; - case V4L2_PIX_FMT_YUV420: case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_YUV420: default: base_cb = base_y + q_data->bytesperline * q_data->height; base_cr = base_cb + q_data->bytesperline * q_data->height / 4; break; + case V4L2_PIX_FMT_YVU420: + /* Switch Cb and Cr for YVU420 format */ + base_cr = base_y + q_data->bytesperline * q_data->height; + base_cb = base_cr + q_data->bytesperline * q_data->height / 4; + break; case V4L2_PIX_FMT_YUV422P: base_cb = base_y + q_data->bytesperline * q_data->height; base_cr = base_cb + q_data->bytesperline * q_data->height / 2; @@ -156,9 +156,9 @@ static const struct coda_video_device coda_bit_encoder = { .type = CODA_INST_ENCODER, .ops = &coda_bit_encode_ops, .src_formats = { + V4L2_PIX_FMT_NV12, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_YVU420, - V4L2_PIX_FMT_NV12, }, .dst_formats = { V4L2_PIX_FMT_H264, @@ -171,9 +171,9 @@ static const struct coda_video_device coda_bit_jpeg_encoder = { .type = CODA_INST_ENCODER, .ops = &coda_bit_encode_ops, .src_formats = { + V4L2_PIX_FMT_NV12, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_YVU420, - V4L2_PIX_FMT_NV12, V4L2_PIX_FMT_YUV422P, }, .dst_formats = { @@ -190,9 +190,9 @@ static const struct coda_video_device coda_bit_decoder = { V4L2_PIX_FMT_MPEG4, }, .dst_formats = { + V4L2_PIX_FMT_NV12, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_YVU420, - V4L2_PIX_FMT_NV12, }, }; @@ -204,9 +204,9 @@ static const struct coda_video_device coda_bit_jpeg_decoder = { V4L2_PIX_FMT_JPEG, }, .dst_formats = { + V4L2_PIX_FMT_NV12, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_YVU420, - V4L2_PIX_FMT_NV12, V4L2_PIX_FMT_YUV422P, }, }; @@ -234,9 +234,9 @@ static const struct coda_video_device *coda9_video_devices[] = { static u32 coda_format_normalize_yuv(u32 fourcc) { switch (fourcc) { + case V4L2_PIX_FMT_NV12: case V4L2_PIX_FMT_YUV420: case V4L2_PIX_FMT_YVU420: - case V4L2_PIX_FMT_NV12: case V4L2_PIX_FMT_YUV422P: return V4L2_PIX_FMT_YUV420; default: @@ -448,9 +448,9 @@ static int coda_try_fmt(struct coda_ctx *ctx, const struct coda_codec *codec, S_ALIGN); switch (f->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_NV12: case V4L2_PIX_FMT_YUV420: case V4L2_PIX_FMT_YVU420: - case V4L2_PIX_FMT_NV12: /* * Frame stride must be at least multiple of 8, * but multiple of 16 for h.264 or JPEG 4:2:x @@ -1099,8 +1099,8 @@ static void set_default_params(struct coda_ctx *ctx) ctx->params.framerate = 30; /* Default formats for output and input queues */ - ctx->q_data[V4L2_M2M_SRC].fourcc = ctx->codec->src_fourcc; - ctx->q_data[V4L2_M2M_DST].fourcc = ctx->codec->dst_fourcc; + ctx->q_data[V4L2_M2M_SRC].fourcc = ctx->cvd->src_formats[0]; + ctx->q_data[V4L2_M2M_DST].fourcc = ctx->cvd->dst_formats[0]; ctx->q_data[V4L2_M2M_SRC].width = max_w; ctx->q_data[V4L2_M2M_SRC].height = max_h; ctx->q_data[V4L2_M2M_DST].width = max_w; -- cgit v1.2.3 From 85efe4e5082c381262f5303d20f808ac455b028e Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 10 Jul 2015 10:49:24 -0300 Subject: [media] v4l2-dev: use event class to deduplicate v4l2 trace events Trace events with exactly the same parameters and trace output, such as v4l2_qbuf and v4l2_dqbuf, are supposed to use the DECLARE_EVENT_CLASS and DEFINE_EVENT macros instead of duplicated TRACE_EVENT macro calls. Suggested-by: Steven Rostedt Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/trace/events/v4l2.h | 160 +++++++++++++++++++++----------------------- 1 file changed, 78 insertions(+), 82 deletions(-) diff --git a/include/trace/events/v4l2.h b/include/trace/events/v4l2.h index 89d0497c058a..4c88a3241c18 100644 --- a/include/trace/events/v4l2.h +++ b/include/trace/events/v4l2.h @@ -93,90 +93,86 @@ SHOW_FIELD { V4L2_TC_USERBITS_USERDEFINED, "USERBITS_USERDEFINED" }, \ { V4L2_TC_USERBITS_8BITCHARS, "USERBITS_8BITCHARS" }) -#define V4L2_TRACE_EVENT(event_name) \ - TRACE_EVENT(event_name, \ - TP_PROTO(int minor, struct v4l2_buffer *buf), \ - \ - TP_ARGS(minor, buf), \ - \ - TP_STRUCT__entry( \ - __field(int, minor) \ - __field(u32, index) \ - __field(u32, type) \ - __field(u32, bytesused) \ - __field(u32, flags) \ - __field(u32, field) \ - __field(s64, timestamp) \ - __field(u32, timecode_type) \ - __field(u32, timecode_flags) \ - __field(u8, timecode_frames) \ - __field(u8, timecode_seconds) \ - __field(u8, timecode_minutes) \ - __field(u8, timecode_hours) \ - __field(u8, timecode_userbits0) \ - __field(u8, timecode_userbits1) \ - __field(u8, timecode_userbits2) \ - __field(u8, timecode_userbits3) \ - __field(u32, sequence) \ - ), \ - \ - TP_fast_assign( \ - __entry->minor = minor; \ - __entry->index = buf->index; \ - __entry->type = buf->type; \ - __entry->bytesused = buf->bytesused; \ - __entry->flags = buf->flags; \ - __entry->field = buf->field; \ - __entry->timestamp = \ - timeval_to_ns(&buf->timestamp); \ - __entry->timecode_type = buf->timecode.type; \ - __entry->timecode_flags = buf->timecode.flags; \ - __entry->timecode_frames = \ - buf->timecode.frames; \ - __entry->timecode_seconds = \ - buf->timecode.seconds; \ - __entry->timecode_minutes = \ - buf->timecode.minutes; \ - __entry->timecode_hours = buf->timecode.hours; \ - __entry->timecode_userbits0 = \ - buf->timecode.userbits[0]; \ - __entry->timecode_userbits1 = \ - buf->timecode.userbits[1]; \ - __entry->timecode_userbits2 = \ - buf->timecode.userbits[2]; \ - __entry->timecode_userbits3 = \ - buf->timecode.userbits[3]; \ - __entry->sequence = buf->sequence; \ - ), \ - \ - TP_printk("minor = %d, index = %u, type = %s, " \ - "bytesused = %u, flags = %s, " \ - "field = %s, timestamp = %llu, timecode = { " \ - "type = %s, flags = %s, frames = %u, " \ - "seconds = %u, minutes = %u, hours = %u, " \ - "userbits = { %u %u %u %u } }, " \ - "sequence = %u", __entry->minor, \ - __entry->index, show_type(__entry->type), \ - __entry->bytesused, \ - show_flags(__entry->flags), \ - show_field(__entry->field), \ - __entry->timestamp, \ - show_timecode_type(__entry->timecode_type), \ - show_timecode_flags(__entry->timecode_flags), \ - __entry->timecode_frames, \ - __entry->timecode_seconds, \ - __entry->timecode_minutes, \ - __entry->timecode_hours, \ - __entry->timecode_userbits0, \ - __entry->timecode_userbits1, \ - __entry->timecode_userbits2, \ - __entry->timecode_userbits3, \ - __entry->sequence \ - ) \ +DECLARE_EVENT_CLASS(v4l2_event_class, + TP_PROTO(int minor, struct v4l2_buffer *buf), + + TP_ARGS(minor, buf), + + TP_STRUCT__entry( + __field(int, minor) + __field(u32, index) + __field(u32, type) + __field(u32, bytesused) + __field(u32, flags) + __field(u32, field) + __field(s64, timestamp) + __field(u32, timecode_type) + __field(u32, timecode_flags) + __field(u8, timecode_frames) + __field(u8, timecode_seconds) + __field(u8, timecode_minutes) + __field(u8, timecode_hours) + __field(u8, timecode_userbits0) + __field(u8, timecode_userbits1) + __field(u8, timecode_userbits2) + __field(u8, timecode_userbits3) + __field(u32, sequence) + ), + + TP_fast_assign( + __entry->minor = minor; + __entry->index = buf->index; + __entry->type = buf->type; + __entry->bytesused = buf->bytesused; + __entry->flags = buf->flags; + __entry->field = buf->field; + __entry->timestamp = timeval_to_ns(&buf->timestamp); + __entry->timecode_type = buf->timecode.type; + __entry->timecode_flags = buf->timecode.flags; + __entry->timecode_frames = buf->timecode.frames; + __entry->timecode_seconds = buf->timecode.seconds; + __entry->timecode_minutes = buf->timecode.minutes; + __entry->timecode_hours = buf->timecode.hours; + __entry->timecode_userbits0 = buf->timecode.userbits[0]; + __entry->timecode_userbits1 = buf->timecode.userbits[1]; + __entry->timecode_userbits2 = buf->timecode.userbits[2]; + __entry->timecode_userbits3 = buf->timecode.userbits[3]; + __entry->sequence = buf->sequence; + ), + + TP_printk("minor = %d, index = %u, type = %s, bytesused = %u, " + "flags = %s, field = %s, timestamp = %llu, " + "timecode = { type = %s, flags = %s, frames = %u, " + "seconds = %u, minutes = %u, hours = %u, " + "userbits = { %u %u %u %u } }, sequence = %u", __entry->minor, + __entry->index, show_type(__entry->type), + __entry->bytesused, + show_flags(__entry->flags), + show_field(__entry->field), + __entry->timestamp, + show_timecode_type(__entry->timecode_type), + show_timecode_flags(__entry->timecode_flags), + __entry->timecode_frames, + __entry->timecode_seconds, + __entry->timecode_minutes, + __entry->timecode_hours, + __entry->timecode_userbits0, + __entry->timecode_userbits1, + __entry->timecode_userbits2, + __entry->timecode_userbits3, + __entry->sequence ) +) -V4L2_TRACE_EVENT(v4l2_dqbuf); -V4L2_TRACE_EVENT(v4l2_qbuf); +DEFINE_EVENT(v4l2_event_class, v4l2_dqbuf, + TP_PROTO(int minor, struct v4l2_buffer *buf), + TP_ARGS(minor, buf) +); + +DEFINE_EVENT(v4l2_event_class, v4l2_qbuf, + TP_PROTO(int minor, struct v4l2_buffer *buf), + TP_ARGS(minor, buf) +); #endif /* if !defined(_TRACE_V4L2_H) || defined(TRACE_HEADER_MULTI_READ) */ -- cgit v1.2.3 From c13a5ccf5da86239213033214658b8a170eeab87 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 10 Jul 2015 10:49:25 -0300 Subject: [media] v4l2-mem2mem: set the queue owner field just as vb2_ioctl_reqbufs does The queue owner will be used by videobuf2 trace events to determine and record the device minor number. It is set in v4l2_m2m_reqbufs instead of v4l2_m2m_ioctl_reqbufs because several drivers implement their own vidioc_reqbufs handlers that still call v4l2_m2m_reqbufs directly. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-mem2mem.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index dc853e57f91f..af8d6b4aa807 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -357,9 +357,16 @@ int v4l2_m2m_reqbufs(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, struct v4l2_requestbuffers *reqbufs) { struct vb2_queue *vq; + int ret; vq = v4l2_m2m_get_vq(m2m_ctx, reqbufs->type); - return vb2_reqbufs(vq, reqbufs); + ret = vb2_reqbufs(vq, reqbufs); + /* If count == 0, then the owner has released all buffers and he + is no longer owner of the queue. Otherwise we have an owner. */ + if (ret == 0) + vq->owner = reqbufs->count ? file->private_data : NULL; + + return ret; } EXPORT_SYMBOL_GPL(v4l2_m2m_reqbufs); -- cgit v1.2.3 From 2091f5181c66b3617a977e79843aba10e087be6c Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 10 Jul 2015 10:49:26 -0300 Subject: [media] videobuf2: add trace events Add videobuf2 specific vb2_qbuf and vb2_dqbuf trace events that mirror the v4l2_qbuf and v4l2_dqbuf trace events, only they include additional information about queue fill state and are emitted right before the buffer is enqueued in the driver or userspace is woken up. This allows to make sense of the timeline of trace events in combination with others that might be triggered by __enqueue_in_driver. Also two new trace events vb2_buf_queue and vb2_buf_done are added, allowing to trace the handover between videobuf2 framework and driver. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/videobuf2-core.c | 11 ++++ include/trace/events/v4l2.h | 97 ++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+) diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index 93b315459098..b866a6becd99 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -30,6 +30,8 @@ #include #include +#include + static int debug; module_param(debug, int, 0644); @@ -1207,6 +1209,8 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state) atomic_dec(&q->owned_by_drv_count); spin_unlock_irqrestore(&q->done_lock, flags); + trace_vb2_buf_done(q, vb); + if (state == VB2_BUF_STATE_QUEUED) { if (q->start_streaming_called) __enqueue_in_driver(vb); @@ -1629,6 +1633,8 @@ static void __enqueue_in_driver(struct vb2_buffer *vb) vb->state = VB2_BUF_STATE_ACTIVE; atomic_inc(&q->owned_by_drv_count); + trace_vb2_buf_queue(q, vb); + /* sync buffers */ for (plane = 0; plane < vb->num_planes; ++plane) call_void_memop(vb, prepare, vb->planes[plane].mem_priv); @@ -1878,6 +1884,8 @@ static int vb2_internal_qbuf(struct vb2_queue *q, struct v4l2_buffer *b) vb->v4l2_buf.timecode = b->timecode; } + trace_vb2_qbuf(q, vb); + /* * If already streaming, give the buffer to driver for processing. * If not, the buffer will be given to driver on next streamon. @@ -2123,6 +2131,9 @@ static int vb2_internal_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool n /* Remove from videobuf queue */ list_del(&vb->queued_entry); q->queued_count--; + + trace_vb2_dqbuf(q, vb); + if (!V4L2_TYPE_IS_OUTPUT(q->type) && vb->v4l2_buf.flags & V4L2_BUF_FLAG_LAST) q->last_buffer_dequeued = true; diff --git a/include/trace/events/v4l2.h b/include/trace/events/v4l2.h index 4c88a3241c18..dbf017bfddd9 100644 --- a/include/trace/events/v4l2.h +++ b/include/trace/events/v4l2.h @@ -174,6 +174,103 @@ DEFINE_EVENT(v4l2_event_class, v4l2_qbuf, TP_ARGS(minor, buf) ); +DECLARE_EVENT_CLASS(vb2_event_class, + TP_PROTO(struct vb2_queue *q, struct vb2_buffer *vb), + TP_ARGS(q, vb), + + TP_STRUCT__entry( + __field(int, minor) + __field(u32, queued_count) + __field(int, owned_by_drv_count) + __field(u32, index) + __field(u32, type) + __field(u32, bytesused) + __field(u32, flags) + __field(u32, field) + __field(s64, timestamp) + __field(u32, timecode_type) + __field(u32, timecode_flags) + __field(u8, timecode_frames) + __field(u8, timecode_seconds) + __field(u8, timecode_minutes) + __field(u8, timecode_hours) + __field(u8, timecode_userbits0) + __field(u8, timecode_userbits1) + __field(u8, timecode_userbits2) + __field(u8, timecode_userbits3) + __field(u32, sequence) + ), + + TP_fast_assign( + __entry->minor = q->owner ? q->owner->vdev->minor : -1; + __entry->queued_count = q->queued_count; + __entry->owned_by_drv_count = + atomic_read(&q->owned_by_drv_count); + __entry->index = vb->v4l2_buf.index; + __entry->type = vb->v4l2_buf.type; + __entry->bytesused = vb->v4l2_planes[0].bytesused; + __entry->flags = vb->v4l2_buf.flags; + __entry->field = vb->v4l2_buf.field; + __entry->timestamp = timeval_to_ns(&vb->v4l2_buf.timestamp); + __entry->timecode_type = vb->v4l2_buf.timecode.type; + __entry->timecode_flags = vb->v4l2_buf.timecode.flags; + __entry->timecode_frames = vb->v4l2_buf.timecode.frames; + __entry->timecode_seconds = vb->v4l2_buf.timecode.seconds; + __entry->timecode_minutes = vb->v4l2_buf.timecode.minutes; + __entry->timecode_hours = vb->v4l2_buf.timecode.hours; + __entry->timecode_userbits0 = vb->v4l2_buf.timecode.userbits[0]; + __entry->timecode_userbits1 = vb->v4l2_buf.timecode.userbits[1]; + __entry->timecode_userbits2 = vb->v4l2_buf.timecode.userbits[2]; + __entry->timecode_userbits3 = vb->v4l2_buf.timecode.userbits[3]; + __entry->sequence = vb->v4l2_buf.sequence; + ), + + TP_printk("minor = %d, queued = %u, owned_by_drv = %d, index = %u, " + "type = %s, bytesused = %u, flags = %s, field = %s, " + "timestamp = %llu, timecode = { type = %s, flags = %s, " + "frames = %u, seconds = %u, minutes = %u, hours = %u, " + "userbits = { %u %u %u %u } }, sequence = %u", __entry->minor, + __entry->queued_count, + __entry->owned_by_drv_count, + __entry->index, show_type(__entry->type), + __entry->bytesused, + show_flags(__entry->flags), + show_field(__entry->field), + __entry->timestamp, + show_timecode_type(__entry->timecode_type), + show_timecode_flags(__entry->timecode_flags), + __entry->timecode_frames, + __entry->timecode_seconds, + __entry->timecode_minutes, + __entry->timecode_hours, + __entry->timecode_userbits0, + __entry->timecode_userbits1, + __entry->timecode_userbits2, + __entry->timecode_userbits3, + __entry->sequence + ) +) + +DEFINE_EVENT(vb2_event_class, vb2_buf_done, + TP_PROTO(struct vb2_queue *q, struct vb2_buffer *vb), + TP_ARGS(q, vb) +); + +DEFINE_EVENT(vb2_event_class, vb2_buf_queue, + TP_PROTO(struct vb2_queue *q, struct vb2_buffer *vb), + TP_ARGS(q, vb) +); + +DEFINE_EVENT(vb2_event_class, vb2_dqbuf, + TP_PROTO(struct vb2_queue *q, struct vb2_buffer *vb), + TP_ARGS(q, vb) +); + +DEFINE_EVENT(vb2_event_class, vb2_qbuf, + TP_PROTO(struct vb2_queue *q, struct vb2_buffer *vb), + TP_ARGS(q, vb) +); + #endif /* if !defined(_TRACE_V4L2_H) || defined(TRACE_HEADER_MULTI_READ) */ /* This part must be outside protection */ -- cgit v1.2.3 From c99235fa3ef833c3c23926085f2bb68851c8460a Mon Sep 17 00:00:00 2001 From: Benoit Parrot Date: Wed, 15 Jul 2015 18:00:06 -0300 Subject: [media] media: am437x-vpfe: Fix a race condition during release There was a race condition where during cleanup/release operation on-going streaming would cause a kernel panic because the hardware module was disabled prematurely with IRQ still pending. Fixes: 417d2e507edc ("[media] media: platform: add VPFE capture driver support for AM437X") Cc: # v4.0+ Signed-off-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/am437x/am437x-vpfe.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/am437x/am437x-vpfe.c b/drivers/media/platform/am437x/am437x-vpfe.c index 1fed7a56c8ae..c8447fa3fd91 100644 --- a/drivers/media/platform/am437x/am437x-vpfe.c +++ b/drivers/media/platform/am437x/am437x-vpfe.c @@ -1186,14 +1186,24 @@ static int vpfe_initialize_device(struct vpfe_device *vpfe) static int vpfe_release(struct file *file) { struct vpfe_device *vpfe = video_drvdata(file); + bool fh_singular; int ret; mutex_lock(&vpfe->lock); - if (v4l2_fh_is_singular_file(file)) - vpfe_ccdc_close(&vpfe->ccdc, vpfe->pdev); + /* Save the singular status before we call the clean-up helper */ + fh_singular = v4l2_fh_is_singular_file(file); + + /* the release helper will cleanup any on-going streaming */ ret = _vb2_fop_release(file, NULL); + /* + * If this was the last open file. + * Then de-initialize hw module. + */ + if (fh_singular) + vpfe_ccdc_close(&vpfe->ccdc, vpfe->pdev); + mutex_unlock(&vpfe->lock); return ret; -- cgit v1.2.3 From 810c168c233fc531ecd3e7a6d9e0b5a3ffe85da7 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 17 Jul 2015 08:29:40 -0300 Subject: [media] cobalt: accept unchanged timings when vb2_is_busy() When vb2_is_busy() it should still be possible to call S_DV_TIMINGS provided the new timings are the same as the current timings. For input 1 (test generator) the size is always 1080p, so just return that. Fixes a v4l2-compliance issue. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cobalt/cobalt-v4l2.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) 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 #include +#include #include #include @@ -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) { -- cgit v1.2.3 From 55b858b4e5f46e71f9e5089cb63602697a49a211 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 17 Jul 2015 08:45:22 -0300 Subject: [media] cobalt: allow fewer than 8 PCIe lanes Currently the cobalt driver refuses to load if fewer than 8 PCIe lanes are assigned. This patch changes this and just issues a warning. The only time it will refuse to load is if the number of assigned lanes is less than what the PCIe host is capable of since this suggests that the card isn't seated correctly in the slot. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cobalt/cobalt-driver.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) 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))) { -- cgit v1.2.3 From 4c5211a100399c3823563193dd881dcb3b7d24fc Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 17 Jul 2015 11:02:53 -0300 Subject: [media] tc358743: register v4l2 asynchronous subdevice Add support for registering the sensor subdevice using the v4l2-async API. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/tc358743.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index 4e8811c3e771..7278435048ed 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -1710,6 +1710,16 @@ static int tc358743_probe(struct i2c_client *client, goto err_hdl; } + state->pad.flags = MEDIA_PAD_FL_SOURCE; + err = media_entity_init(&sd->entity, 1, &state->pad, 0); + if (err < 0) + goto err_hdl; + + sd->dev = &client->dev; + err = v4l2_async_register_subdev(sd); + if (err < 0) + goto err_hdl; + mutex_init(&state->confctl_mutex); INIT_DELAYED_WORK(&state->delayed_work_enable_hotplug, @@ -1740,6 +1750,7 @@ err_work_queues: destroy_workqueue(state->work_queues); mutex_destroy(&state->confctl_mutex); err_hdl: + media_entity_cleanup(&sd->entity); v4l2_ctrl_handler_free(&state->hdl); return err; } @@ -1751,8 +1762,10 @@ static int tc358743_remove(struct i2c_client *client) cancel_delayed_work(&state->delayed_work_enable_hotplug); destroy_workqueue(state->work_queues); + v4l2_async_unregister_subdev(sd); v4l2_device_unregister_subdev(sd); mutex_destroy(&state->confctl_mutex); + media_entity_cleanup(&sd->entity); v4l2_ctrl_handler_free(&state->hdl); return 0; -- cgit v1.2.3 From 8ec23da73599848a5a5ecbab50f17a570b60c096 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 17 Jul 2015 11:02:54 -0300 Subject: [media] tc358743: enable v4l2 subdevice devnode Add V4L2_SUBDEV_FL_HAS_DEVNODE to subdev flags, in order to enable a subdev device node. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/tc358743.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index 7278435048ed..0ccae3308b68 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -1668,7 +1668,7 @@ static int tc358743_probe(struct i2c_client *client, state->i2c_client = client; sd = &state->sd; v4l2_i2c_subdev_init(sd, client, &tc358743_ops); - sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; /* i2c access */ if ((i2c_rd16(sd, CHIPID) & MASK_CHIPID) != 0) { -- cgit v1.2.3 From 25614824685247e00b786032a504f10bfab347b1 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 17 Jul 2015 11:02:55 -0300 Subject: [media] tc358743: support probe from device tree Add support for probing the TC358743 subdevice from device tree. The reference clock must be supplied using the common clock bindings. MIPI CSI-2 specific properties are parsed from the OF graph endpoint node and support for a non-continuous MIPI CSI-2 clock is added. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../devicetree/bindings/media/i2c/tc358743.txt | 48 +++++++ drivers/media/i2c/tc358743.c | 155 ++++++++++++++++++++- 2 files changed, 197 insertions(+), 6 deletions(-) create mode 100644 Documentation/devicetree/bindings/media/i2c/tc358743.txt diff --git a/Documentation/devicetree/bindings/media/i2c/tc358743.txt b/Documentation/devicetree/bindings/media/i2c/tc358743.txt new file mode 100644 index 000000000000..5218921629ed --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/tc358743.txt @@ -0,0 +1,48 @@ +* Toshiba TC358743 HDMI-RX to MIPI CSI2-TX Bridge + +The Toshiba TC358743 HDMI-RX to MIPI CSI2-TX (H2C) is a bridge that converts +a HDMI stream to MIPI CSI-2 TX. It is programmable through I2C. + +Required Properties: + +- compatible: value should be "toshiba,tc358743" +- clocks, clock-names: should contain a phandle link to the reference clock + source, the clock input is named "refclk". + +Optional Properties: + +- reset-gpios: gpio phandle GPIO connected to the reset pin +- interrupts, interrupt-parent: GPIO connected to the interrupt pin +- data-lanes: should be <1 2 3 4> for four-lane operation, + or <1 2> for two-lane operation +- clock-lanes: should be <0> +- clock-noncontinuous: Presence of this boolean property decides whether the + MIPI CSI-2 clock is continuous or non-continuous. +- link-frequencies: List of allowed link frequencies in Hz. Each frequency is + expressed as a 64-bit big-endian integer. The frequency + is half of the bps per lane due to DDR transmission. + +For further information on the MIPI CSI-2 endpoint node properties, see +Documentation/devicetree/bindings/media/video-interfaces.txt. + +Example: + + tc358743@0f { + compatible = "toshiba,tc358743"; + reg = <0x0f>; + clocks = <&hdmi_osc>; + clock-names = "refclk"; + reset-gpios = <&gpio6 9 GPIO_ACTIVE_LOW>; + interrupt-parent = <&gpio2>; + interrupts = <5 IRQ_TYPE_LEVEL_HIGH>; + + port { + tc358743_out: endpoint { + remote-endpoint = <&mipi_csi2_in>; + data-lanes = <1 2 3 4>; + clock-lanes = <0>; + clock-noncontinuous; + link-frequencies = /bits/ 64 <297000000>; + }; + }; + }; diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index 0ccae3308b68..76d0aaa19493 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -29,7 +29,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -37,6 +39,7 @@ #include #include #include +#include #include #include "tc358743_regs.h" @@ -69,6 +72,7 @@ static const struct v4l2_dv_timings_cap tc358743_timings_cap = { struct tc358743_state { struct tc358743_platform_data pdata; + struct v4l2_of_bus_mipi_csi2 bus; struct v4l2_subdev sd; struct media_pad pad; struct v4l2_ctrl_handler hdl; @@ -90,6 +94,8 @@ struct tc358743_state { struct v4l2_dv_timings timings; u32 mbus_fmt_code; + + struct gpio_desc *reset_gpio; }; static void tc358743_enable_interrupts(struct v4l2_subdev *sd, @@ -700,7 +706,8 @@ static void tc358743_set_csi(struct v4l2_subdev *sd) ((lanes > 2) ? MASK_D2M_HSTXVREGEN : 0x0) | ((lanes > 3) ? MASK_D3M_HSTXVREGEN : 0x0)); - i2c_wr32(sd, TXOPTIONCNTRL, MASK_CONTCLKMODE); + i2c_wr32(sd, TXOPTIONCNTRL, (state->bus.flags & + V4L2_MBUS_CSI2_CONTINUOUS_CLOCK) ? MASK_CONTCLKMODE : 0); i2c_wr32(sd, STARTCNTRL, MASK_START); i2c_wr32(sd, CSI_START, MASK_STRT); @@ -1638,6 +1645,136 @@ static const struct v4l2_ctrl_config tc358743_ctrl_audio_present = { /* --------------- PROBE / REMOVE --------------- */ +#ifdef CONFIG_OF +static void tc358743_gpio_reset(struct tc358743_state *state) +{ + gpiod_set_value(state->reset_gpio, 0); + usleep_range(5000, 10000); + gpiod_set_value(state->reset_gpio, 1); + usleep_range(1000, 2000); + gpiod_set_value(state->reset_gpio, 0); + msleep(20); +} + +static int tc358743_probe_of(struct tc358743_state *state) +{ + struct device *dev = &state->i2c_client->dev; + struct v4l2_of_endpoint *endpoint; + struct device_node *ep; + struct clk *refclk; + u32 bps_pr_lane; + int ret = -EINVAL; + + refclk = devm_clk_get(dev, "refclk"); + if (IS_ERR(refclk)) { + if (PTR_ERR(refclk) != -EPROBE_DEFER) + dev_err(dev, "failed to get refclk: %ld\n", + PTR_ERR(refclk)); + return PTR_ERR(refclk); + } + + ep = of_graph_get_next_endpoint(dev->of_node, NULL); + if (!ep) { + dev_err(dev, "missing endpoint node\n"); + return -EINVAL; + } + + endpoint = v4l2_of_alloc_parse_endpoint(ep); + if (IS_ERR(endpoint)) { + dev_err(dev, "failed to parse endpoint\n"); + return PTR_ERR(endpoint); + } + + if (endpoint->bus_type != V4L2_MBUS_CSI2 || + endpoint->bus.mipi_csi2.num_data_lanes == 0 || + endpoint->nr_of_link_frequencies == 0) { + dev_err(dev, "missing CSI-2 properties in endpoint\n"); + goto free_endpoint; + } + + state->bus = endpoint->bus.mipi_csi2; + + clk_prepare_enable(refclk); + + state->pdata.refclk_hz = clk_get_rate(refclk); + state->pdata.ddc5v_delay = DDC5V_DELAY_100_MS; + state->pdata.enable_hdcp = false; + /* A FIFO level of 16 should be enough for 2-lane 720p60 at 594 MHz. */ + state->pdata.fifo_level = 16; + /* + * The PLL input clock is obtained by dividing refclk by pll_prd. + * It must be between 6 MHz and 40 MHz, lower frequency is better. + */ + switch (state->pdata.refclk_hz) { + case 26000000: + case 27000000: + case 42000000: + state->pdata.pll_prd = state->pdata.refclk_hz / 6000000; + break; + default: + dev_err(dev, "unsupported refclk rate: %u Hz\n", + state->pdata.refclk_hz); + goto disable_clk; + } + + /* + * The CSI bps per lane must be between 62.5 Mbps and 1 Gbps. + * The default is 594 Mbps for 4-lane 1080p60 or 2-lane 720p60. + */ + bps_pr_lane = 2 * endpoint->link_frequencies[0]; + if (bps_pr_lane < 62500000U || bps_pr_lane > 1000000000U) { + dev_err(dev, "unsupported bps per lane: %u bps\n", bps_pr_lane); + goto disable_clk; + } + + /* The CSI speed per lane is refclk / pll_prd * pll_fbd */ + state->pdata.pll_fbd = bps_pr_lane / + state->pdata.refclk_hz * state->pdata.pll_prd; + + /* + * FIXME: These timings are from REF_02 for 594 Mbps per lane (297 MHz + * link frequency). In principle it should be possible to calculate + * them based on link frequency and resolution. + */ + if (bps_pr_lane != 594000000U) + dev_warn(dev, "untested bps per lane: %u bps\n", bps_pr_lane); + state->pdata.lineinitcnt = 0xe80; + state->pdata.lptxtimecnt = 0x003; + /* tclk-preparecnt: 3, tclk-zerocnt: 20 */ + state->pdata.tclk_headercnt = 0x1403; + state->pdata.tclk_trailcnt = 0x00; + /* ths-preparecnt: 3, ths-zerocnt: 1 */ + state->pdata.ths_headercnt = 0x0103; + state->pdata.twakeup = 0x4882; + state->pdata.tclk_postcnt = 0x008; + state->pdata.ths_trailcnt = 0x2; + state->pdata.hstxvregcnt = 0; + + state->reset_gpio = devm_gpiod_get(dev, "reset"); + if (IS_ERR(state->reset_gpio)) { + dev_err(dev, "failed to get reset gpio\n"); + ret = PTR_ERR(state->reset_gpio); + goto disable_clk; + } + + tc358743_gpio_reset(state); + + ret = 0; + goto free_endpoint; + +disable_clk: + clk_disable_unprepare(refclk); +free_endpoint: + v4l2_of_free_endpoint(endpoint); + return ret; +} +#else +static inline int tc358743_probe_of(struct tc358743_state *state) +{ + return -ENODEV; +} +#endif + static int tc358743_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -1658,14 +1795,20 @@ static int tc358743_probe(struct i2c_client *client, if (!state) return -ENOMEM; + state->i2c_client = client; + /* platform data */ - if (!pdata) { - v4l_err(client, "No platform data!\n"); - return -ENODEV; + if (pdata) { + state->pdata = *pdata; + state->bus.flags = V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; + } else { + err = tc358743_probe_of(state); + if (err == -ENODEV) + v4l_err(client, "No platform data!\n"); + if (err) + return err; } - state->pdata = *pdata; - state->i2c_client = client; sd = &state->sd; v4l2_i2c_subdev_init(sd, client, &tc358743_ops); sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; -- cgit v1.2.3 From d747b806abf41f037f7d2a076131adf8c7971a89 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 17 Jul 2015 11:02:56 -0300 Subject: [media] tc358743: add direct interrupt handling When probed from device tree, the i2c client driver can handle the interrupt on its own. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/tc358743.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index 76d0aaa19493..934f6e17e06d 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -1306,6 +1307,16 @@ static int tc358743_isr(struct v4l2_subdev *sd, u32 status, bool *handled) return 0; } +static irqreturn_t tc358743_irq_handler(int irq, void *dev_id) +{ + struct tc358743_state *state = dev_id; + bool handled; + + tc358743_isr(&state->sd, 0, &handled); + + return handled ? IRQ_HANDLED : IRQ_NONE; +} + /* --------------- VIDEO OPS --------------- */ static int tc358743_g_input_status(struct v4l2_subdev *sd, u32 *status) @@ -1876,6 +1887,17 @@ static int tc358743_probe(struct i2c_client *client, tc358743_set_csi_color_space(sd); tc358743_init_interrupts(sd); + + if (state->i2c_client->irq) { + err = devm_request_threaded_irq(&client->dev, + state->i2c_client->irq, + NULL, tc358743_irq_handler, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + "tc358743", state); + if (err) + goto err_work_queues; + } + tc358743_enable_interrupts(sd, tx_5v_power_present(sd)); i2c_wr16(sd, INTMASK, ~(MASK_HDMI_MSK | MASK_CSI_MSK) & 0xffff); -- cgit v1.2.3 From 1140f919f807b6d5a259ecfca88022da0e5340cb Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 17 Jul 2015 12:26:45 -0300 Subject: [media] tc358743: allow event subscription This is useful to subscribe to HDMI hotplug events via the V4L2_CID_DV_RX_POWER_PRESENT control. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/tc358743.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index 934f6e17e06d..d0dd83d7a38e 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include @@ -859,8 +860,7 @@ static void tc358743_format_change(struct v4l2_subdev *sd) &timings, false); } - v4l2_subdev_notify(sd, V4L2_DEVICE_NOTIFY_EVENT, - (void *)&tc358743_ev_fmt); + v4l2_subdev_notify_event(sd, &tc358743_ev_fmt); } static void tc358743_init_interrupts(struct v4l2_subdev *sd) @@ -1317,6 +1317,19 @@ static irqreturn_t tc358743_irq_handler(int irq, void *dev_id) return handled ? IRQ_HANDLED : IRQ_NONE; } +static int tc358743_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_SOURCE_CHANGE: + return v4l2_src_change_event_subdev_subscribe(sd, fh, sub); + case V4L2_EVENT_CTRL: + return v4l2_ctrl_subdev_subscribe_event(sd, fh, sub); + default: + return -EINVAL; + } +} + /* --------------- VIDEO OPS --------------- */ static int tc358743_g_input_status(struct v4l2_subdev *sd, u32 *status) @@ -1604,6 +1617,8 @@ static const struct v4l2_subdev_core_ops tc358743_core_ops = { .s_register = tc358743_s_register, #endif .interrupt_service_routine = tc358743_isr, + .subscribe_event = tc358743_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, }; static const struct v4l2_subdev_video_ops tc358743_video_ops = { -- cgit v1.2.3 From dd70b27eecdd4e13378a5e253dbd426058e164ef Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Sun, 14 Jun 2015 23:01:45 -0300 Subject: [media] media: ttpci: Use vsprintf %pM extension Format mac addresses with the normal kernel extension. Signed-off-by: Joe Perches Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/ttpci/ttpci-eeprom.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/media/pci/ttpci/ttpci-eeprom.c b/drivers/media/pci/ttpci/ttpci-eeprom.c index 32d43156c548..c6f31f2712bc 100644 --- a/drivers/media/pci/ttpci/ttpci-eeprom.c +++ b/drivers/media/pci/ttpci/ttpci-eeprom.c @@ -162,9 +162,7 @@ int ttpci_eeprom_parse_mac(struct i2c_adapter *adapter, u8 *proposed_mac) } 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; } -- cgit v1.2.3 From 17a705e4a99bc6d810f2d64efe9368e221302f7a Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 15 Jun 2015 08:33:29 -0300 Subject: [media] sh-veu: don't use COLORSPACE_JPEG COLORSPACE_JPEG should only be used for JPEGs. Use SMPTE170M instead, which is how YCbCr images are usually encoded. Signed-off-by: Hans Verkuil Acked-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sh_veu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/sh_veu.c b/drivers/media/platform/sh_veu.c index 2554f3719b9e..7bb249cd19b8 100644 --- a/drivers/media/platform/sh_veu.c +++ b/drivers/media/platform/sh_veu.c @@ -211,7 +211,7 @@ static enum v4l2_colorspace sh_veu_4cc2cspace(u32 fourcc) case V4L2_PIX_FMT_NV12: case V4L2_PIX_FMT_NV16: case V4L2_PIX_FMT_NV24: - return V4L2_COLORSPACE_JPEG; + return V4L2_COLORSPACE_SMPTE170M; case V4L2_PIX_FMT_RGB332: case V4L2_PIX_FMT_RGB444: case V4L2_PIX_FMT_RGB565: -- cgit v1.2.3 From a4c4f95eb64640ec31cf0661b1a863151c744827 Mon Sep 17 00:00:00 2001 From: Sunil Shahu Date: Sat, 20 Jun 2015 05:53:50 -0300 Subject: [media] staging: media: lirc: fix coding style error Fix code indentation error by replacing tab in place of spaces. Signed-off-by: Sunil Shahu Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/lirc/lirc_sasem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/lirc/lirc_sasem.c b/drivers/staging/media/lirc/lirc_sasem.c index 9e5674341abe..b247649a99eb 100644 --- a/drivers/staging/media/lirc/lirc_sasem.c +++ b/drivers/staging/media/lirc/lirc_sasem.c @@ -184,7 +184,7 @@ static void deregister_from_lirc(struct sasem_context *context) __func__, retval); else dev_info(&context->dev->dev, - "Deregistered Sasem driver (minor:%d)\n", minor); + "Deregistered Sasem driver (minor:%d)\n", minor); } -- cgit v1.2.3 From 4dc102b2f53d63207fa12a6ad49c7b6448bc3301 Mon Sep 17 00:00:00 2001 From: Vaishali Thakkar Date: Fri, 19 Jun 2015 23:58:17 -0300 Subject: [media] dvb_core: Replace memset with eth_zero_addr Use eth_zero_addr to assign the zero address to the given address array instead of memset when second argument is address of zero. The Coccinelle semantic patch that makes this change is as follows: // @eth_zero_addr@ expression e; @@ -memset(e,0x00,ETH_ALEN); +eth_zero_addr(e); // Signed-off-by: Vaishali Thakkar Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-core/dvb_net.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/dvb-core/dvb_net.c b/drivers/media/dvb-core/dvb_net.c index a694fb1ea228..b81e026edab3 100644 --- a/drivers/media/dvb-core/dvb_net.c +++ b/drivers/media/dvb-core/dvb_net.c @@ -709,7 +709,7 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len ) if (!priv->ule_dbit) { /* dest_addr buffer is only valid if priv->ule_dbit == 0 */ memcpy(ethh->h_dest, dest_addr, ETH_ALEN); - memset(ethh->h_source, 0, ETH_ALEN); + eth_zero_addr(ethh->h_source); } else /* zeroize source and dest */ memset( ethh, 0, ETH_ALEN*2 ); -- cgit v1.2.3 From 264d1cf86c9f801339d272c3067d98eef91611a0 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 10 Jul 2015 03:19:45 -0300 Subject: [media] s5p-tv: Drop owner assignment from i2c_driver i2c_driver does not need to set an owner because i2c_register_driver() will set it. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-tv/hdmiphy_drv.c | 1 - drivers/media/platform/s5p-tv/sii9234_drv.c | 1 - 2 files changed, 2 deletions(-) diff --git a/drivers/media/platform/s5p-tv/hdmiphy_drv.c b/drivers/media/platform/s5p-tv/hdmiphy_drv.c index c2f2e35642f2..aae652351aa8 100644 --- a/drivers/media/platform/s5p-tv/hdmiphy_drv.c +++ b/drivers/media/platform/s5p-tv/hdmiphy_drv.c @@ -315,7 +315,6 @@ MODULE_DEVICE_TABLE(i2c, hdmiphy_id); static struct i2c_driver hdmiphy_driver = { .driver = { .name = "s5p-hdmiphy", - .owner = THIS_MODULE, }, .probe = hdmiphy_probe, .remove = hdmiphy_remove, diff --git a/drivers/media/platform/s5p-tv/sii9234_drv.c b/drivers/media/platform/s5p-tv/sii9234_drv.c index db8c17bb4aaa..8d171310af8f 100644 --- a/drivers/media/platform/s5p-tv/sii9234_drv.c +++ b/drivers/media/platform/s5p-tv/sii9234_drv.c @@ -397,7 +397,6 @@ MODULE_DEVICE_TABLE(i2c, sii9234_id); static struct i2c_driver sii9234_driver = { .driver = { .name = "sii9234", - .owner = THIS_MODULE, .pm = &sii9234_pm_ops, }, .probe = sii9234_probe, -- cgit v1.2.3 From 4f50efadcb9710876c6d02f60931e5fcc896ddaf Mon Sep 17 00:00:00 2001 From: Nicholas Mc Guire Date: Wed, 10 Jun 2015 06:02:03 -0300 Subject: [media] s5p-tv: fix wait_event_timeout return handling event API conformance testing with coccinelle spatches are being used to locate API usage inconsistencies this triggert with: ./drivers/media/platform/s5p-tv/mixer_reg.c:364 incorrect check for negative return Return type of wait_event_timeout is signed long not int and the return type is >=0 always thus the negative check is unnecessary. An appropriately named variable of type long is inserted and the call fixed up aswell as the negative return check dropped. Signed-off-by: Nicholas Mc Guire Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-tv/mixer_reg.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/media/platform/s5p-tv/mixer_reg.c b/drivers/media/platform/s5p-tv/mixer_reg.c index b713403024ef..5127acb1e571 100644 --- a/drivers/media/platform/s5p-tv/mixer_reg.c +++ b/drivers/media/platform/s5p-tv/mixer_reg.c @@ -357,17 +357,15 @@ void mxr_reg_streamoff(struct mxr_device *mdev) int mxr_reg_wait4vsync(struct mxr_device *mdev) { - int ret; + long time_left; clear_bit(MXR_EVENT_VSYNC, &mdev->event_flags); /* TODO: consider adding interruptible */ - ret = wait_event_timeout(mdev->event_queue, - test_bit(MXR_EVENT_VSYNC, &mdev->event_flags), - msecs_to_jiffies(1000)); - if (ret > 0) + time_left = wait_event_timeout(mdev->event_queue, + test_bit(MXR_EVENT_VSYNC, &mdev->event_flags), + msecs_to_jiffies(1000)); + if (time_left > 0) return 0; - if (ret < 0) - return ret; mxr_warn(mdev, "no vsync detected - timeout\n"); return -ETIME; } -- cgit v1.2.3 From d44da04682033ad4c34d97861aef8ad5f7a8c2b5 Mon Sep 17 00:00:00 2001 From: Seung-Woo Kim Date: Wed, 13 May 2015 04:25:25 -0300 Subject: [media] s5p-mfc: fix state check from encoder queue_setup MFCINST_GOT_INST state is set to encoder context with set_format only for capture buffer. In queue_setup of encoder called during reqbufs, it is checked MFCINST_GOT_INST state for both capture and output buffer. So this patch fixes encoder to check MFCINST_GOT_INST state only for capture buffer from queue_setup. Signed-off-by: Seung-Woo Kim Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-mfc/s5p_mfc_enc.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c index e65993f4b901..2e57e9f45b85 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c @@ -1819,11 +1819,12 @@ static int s5p_mfc_queue_setup(struct vb2_queue *vq, struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv); struct s5p_mfc_dev *dev = ctx->dev; - if (ctx->state != MFCINST_GOT_INST) { - mfc_err("inavlid state: %d\n", ctx->state); - return -EINVAL; - } if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + if (ctx->state != MFCINST_GOT_INST) { + mfc_err("inavlid state: %d\n", ctx->state); + return -EINVAL; + } + if (ctx->dst_fmt) *plane_count = ctx->dst_fmt->num_planes; else -- cgit v1.2.3 From 490a977a1038a7f6a6edf30b2555a6890cf24cab Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Wed, 3 Jun 2015 07:36:22 -0300 Subject: [media] s5p-mfc: add return value check in mfc_sys_init_cmd alloc_dev_context_buffer method might fail, so add proper return value check. Signed-off-by: Marek Szyprowski Acked-by: Kamil Debski Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c b/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c index f17609669b96..b1b149151d2d 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c @@ -37,8 +37,12 @@ static int s5p_mfc_sys_init_cmd_v6(struct s5p_mfc_dev *dev) { struct s5p_mfc_cmd_args h2r_args; struct s5p_mfc_buf_size_v6 *buf_size = dev->variant->buf_size->priv; + int ret; + + ret = s5p_mfc_hw_call(dev->mfc_ops, alloc_dev_context_buffer, dev); + if (ret) + return ret; - s5p_mfc_hw_call(dev->mfc_ops, alloc_dev_context_buffer, dev); mfc_write(dev, dev->ctx_buf.dma, S5P_FIMV_CONTEXT_MEM_ADDR_V6); mfc_write(dev, buf_size->dev_ctx, S5P_FIMV_CONTEXT_MEM_SIZE_V6); return s5p_mfc_cmd_host2risc_v6(dev, S5P_FIMV_H2R_CMD_SYS_INIT_V6, -- cgit v1.2.3 From 1af21985473d72965807ef5e5cc02528aa8c01e4 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Wed, 3 Jun 2015 07:36:23 -0300 Subject: [media] s5p-mfc: add additional check for incorrect memory configuration MFC hardware is known to trash random memory if one tries to use a buffer which has lower DMA addresses than the configured DMA base address. This patch adds a check for this case and proper error handling. Signed-off-by: Marek Szyprowski Acked-by: Kamil Debski [s.nawrocki@samsung.com: fixed typo (addres -> address] Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-mfc/s5p_mfc_opr.c | 11 +++++++++-- drivers/media/platform/s5p-mfc/s5p_mfc_opr.h | 2 +- drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c | 12 +++++++----- drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c | 8 +++++--- 4 files changed, 22 insertions(+), 11 deletions(-) diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c index 00a1d8b2a8c2..1e7250260a9a 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c @@ -37,10 +37,9 @@ void s5p_mfc_init_regs(struct s5p_mfc_dev *dev) dev->mfc_regs = s5p_mfc_init_regs_v6_plus(dev); } -int s5p_mfc_alloc_priv_buf(struct device *dev, +int s5p_mfc_alloc_priv_buf(struct device *dev, dma_addr_t base, struct s5p_mfc_priv_buf *b) { - mfc_debug(3, "Allocating priv: %zu\n", b->size); b->virt = dma_alloc_coherent(dev, b->size, &b->dma, GFP_KERNEL); @@ -50,6 +49,14 @@ int s5p_mfc_alloc_priv_buf(struct device *dev, return -ENOMEM; } + if (b->dma < base) { + mfc_err("Invaling memory configuration!\n"); + mfc_err("Allocated buffer (%pad) is lower than memory base address (%pad)\n", + &b->dma, &base); + dma_free_coherent(dev, b->size, b->virt, b->dma); + return -ENOMEM; + } + mfc_debug(3, "Allocated addr %p %pad\n", b->virt, &b->dma); return 0; } diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h index 22dfb3effda8..77a08b19b46d 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h @@ -334,7 +334,7 @@ struct s5p_mfc_hw_ops { void s5p_mfc_init_hw_ops(struct s5p_mfc_dev *dev); void s5p_mfc_init_regs(struct s5p_mfc_dev *dev); -int s5p_mfc_alloc_priv_buf(struct device *dev, +int s5p_mfc_alloc_priv_buf(struct device *dev, dma_addr_t base, struct s5p_mfc_priv_buf *b); void s5p_mfc_release_priv_buf(struct device *dev, struct s5p_mfc_priv_buf *b); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c index 9a923b1a9bac..6402f76cc620 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c @@ -41,7 +41,7 @@ static int s5p_mfc_alloc_dec_temp_buffers_v5(struct s5p_mfc_ctx *ctx) int ret; ctx->dsc.size = buf_size->dsc; - ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, &ctx->dsc); + ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, dev->bank1, &ctx->dsc); if (ret) { mfc_err("Failed to allocate temporary buffer\n"); return ret; @@ -172,7 +172,8 @@ static int s5p_mfc_alloc_codec_buffers_v5(struct s5p_mfc_ctx *ctx) /* Allocate only if memory from bank 1 is necessary */ if (ctx->bank1.size > 0) { - ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, &ctx->bank1); + ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, dev->bank1, + &ctx->bank1); if (ret) { mfc_err("Failed to allocate Bank1 temporary buffer\n"); return ret; @@ -181,7 +182,8 @@ static int s5p_mfc_alloc_codec_buffers_v5(struct s5p_mfc_ctx *ctx) } /* Allocate only if memory from bank 2 is necessary */ if (ctx->bank2.size > 0) { - ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_r, &ctx->bank2); + ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_r, dev->bank2, + &ctx->bank2); if (ret) { mfc_err("Failed to allocate Bank2 temporary buffer\n"); s5p_mfc_release_priv_buf(ctx->dev->mem_dev_l, &ctx->bank1); @@ -212,7 +214,7 @@ static int s5p_mfc_alloc_instance_buffer_v5(struct s5p_mfc_ctx *ctx) else ctx->ctx.size = buf_size->non_h264_ctx; - ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, &ctx->ctx); + ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, dev->bank1, &ctx->ctx); if (ret) { mfc_err("Failed to allocate instance buffer\n"); return ret; @@ -225,7 +227,7 @@ static int s5p_mfc_alloc_instance_buffer_v5(struct s5p_mfc_ctx *ctx) /* Initialize shared memory */ ctx->shm.size = buf_size->shm; - ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, &ctx->shm); + ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, dev->bank1, &ctx->shm); if (ret) { mfc_err("Failed to allocate shared memory buffer\n"); s5p_mfc_release_priv_buf(dev->mem_dev_l, &ctx->ctx); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c index 12497f5ed8e9..2abf0473f095 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c @@ -239,7 +239,8 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) /* Allocate only if memory from bank 1 is necessary */ if (ctx->bank1.size > 0) { - ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, &ctx->bank1); + ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, dev->bank1, + &ctx->bank1); if (ret) { mfc_err("Failed to allocate Bank1 memory\n"); return ret; @@ -291,7 +292,7 @@ static int s5p_mfc_alloc_instance_buffer_v6(struct s5p_mfc_ctx *ctx) break; } - ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, &ctx->ctx); + ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, dev->bank1, &ctx->ctx); if (ret) { mfc_err("Failed to allocate instance buffer\n"); return ret; @@ -320,7 +321,8 @@ static int s5p_mfc_alloc_dev_context_buffer_v6(struct s5p_mfc_dev *dev) mfc_debug_enter(); dev->ctx_buf.size = buf_size->dev_ctx; - ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, &dev->ctx_buf); + ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, dev->bank1, + &dev->ctx_buf); if (ret) { mfc_err("Failed to allocate device context buffer\n"); return ret; -- cgit v1.2.3 From 7a1d4e7c064c0eddd90f0204cecd294d0dc5b36a Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Fri, 3 Jul 2015 07:04:38 -0300 Subject: [media] s5p-jpeg: Eliminate double kfree() video_unregister_device() calls device_unregister(), which calls put_device(), which calls kobject_put(), and if this is the last reference then kobject_release() is called, which calls kobject_cleanup(), which calls ktype's release method which happens to be device_release() in this case, which calls dev->release(), which happens to be v4l2_device_release() in this case, which calls vdev->release(), which happens to be video_device_release(). But video_device_release() is called explicitly both in error recovery path of s5p_jpeg_probe() and in s5p_jpeg_remove(). The pointers in question are not nullified between the two calls, so this is harmful. This patch fixes the driver so that video_device_release() is not called twice for the same object. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-jpeg/jpeg-core.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index bfbf1575677c..9690f9dcb0ca 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -2544,7 +2544,8 @@ static int s5p_jpeg_probe(struct platform_device *pdev) ret = video_register_device(jpeg->vfd_encoder, VFL_TYPE_GRABBER, -1); if (ret) { v4l2_err(&jpeg->v4l2_dev, "Failed to register video device\n"); - goto enc_vdev_alloc_rollback; + video_device_release(jpeg->vfd_encoder); + goto vb2_allocator_rollback; } video_set_drvdata(jpeg->vfd_encoder, jpeg); @@ -2572,7 +2573,8 @@ static int s5p_jpeg_probe(struct platform_device *pdev) ret = video_register_device(jpeg->vfd_decoder, VFL_TYPE_GRABBER, -1); if (ret) { v4l2_err(&jpeg->v4l2_dev, "Failed to register video device\n"); - goto dec_vdev_alloc_rollback; + video_device_release(jpeg->vfd_decoder); + goto enc_vdev_register_rollback; } video_set_drvdata(jpeg->vfd_decoder, jpeg); @@ -2589,15 +2591,9 @@ static int s5p_jpeg_probe(struct platform_device *pdev) return 0; -dec_vdev_alloc_rollback: - video_device_release(jpeg->vfd_decoder); - enc_vdev_register_rollback: video_unregister_device(jpeg->vfd_encoder); -enc_vdev_alloc_rollback: - video_device_release(jpeg->vfd_encoder); - vb2_allocator_rollback: vb2_dma_contig_cleanup_ctx(jpeg->alloc_ctx); @@ -2622,9 +2618,7 @@ static int s5p_jpeg_remove(struct platform_device *pdev) pm_runtime_disable(jpeg->dev); video_unregister_device(jpeg->vfd_decoder); - video_device_release(jpeg->vfd_decoder); video_unregister_device(jpeg->vfd_encoder); - video_device_release(jpeg->vfd_encoder); vb2_dma_contig_cleanup_ctx(jpeg->alloc_ctx); v4l2_m2m_release(jpeg->m2m_dev); v4l2_device_unregister(&jpeg->v4l2_dev); -- cgit v1.2.3 From e752577ed7bf55c81e10343fced8b378cda2b63b Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 20 Jul 2015 04:58:24 -0300 Subject: [media] v4l2-mem2mem: drop lock in v4l2_m2m_fop_mmap The v4l2_m2m_fop_mmap function takes the core mutex, but this will result in a potential circular locking dependency: [ 262.517164] ====================================================== [ 262.517166] [ INFO: possible circular locking dependency detected ] [ 262.517169] 4.2.0-rc2-koryphon #844 Not tainted [ 262.517171] ------------------------------------------------------- [ 262.517173] v4l2-compliance/1379 is trying to acquire lock: [ 262.517175] (&dev->dev_mutex){+.+.+.}, at: [] v4l2_m2m_fop_mmap+0x2b/0x90 [v4l2_mem2mem] [ 262.517187] but task is already holding lock: [ 262.517189] (&mm->mmap_sem){++++++}, at: [] vm_mmap_pgoff+0x69/0xc0 [ 262.517199] which lock already depends on the new lock. [ 262.517202] the existing dependency chain (in reverse order) is: [ 262.517204] -> #1 (&mm->mmap_sem){++++++}: [ 262.517209] [] __lock_acquire+0x62b/0xe80 [ 262.517215] [] lock_acquire+0x65/0x90 [ 262.517218] [] __might_fault+0x75/0xa0 [ 262.517222] [] video_usercopy+0x3e9/0x4e0 [videodev] [ 262.517231] [] video_ioctl2+0x10/0x20 [videodev] [ 262.517238] [] v4l2_ioctl+0xc3/0xe0 [videodev] [ 262.517243] [] do_vfs_ioctl+0x2fc/0x550 [ 262.517248] [] SyS_ioctl+0x74/0x80 [ 262.517252] [] entry_SYSCALL_64_fastpath+0x12/0x76 [ 262.517258] -> #0 (&dev->dev_mutex){+.+.+.}: [ 262.517262] [] validate_chain.isra.38+0xd04/0x1170 [ 262.517266] [] __lock_acquire+0x62b/0xe80 [ 262.517270] [] lock_acquire+0x65/0x90 [ 262.517273] [] mutex_lock_interruptible_nested+0x6c/0x4b0 [ 262.517279] [] v4l2_m2m_fop_mmap+0x2b/0x90 [v4l2_mem2mem] [ 262.517284] [] v4l2_mmap+0x4f/0x90 [videodev] [ 262.517288] [] mmap_region+0x38c/0x5b0 [ 262.517293] [] do_mmap_pgoff+0x2f5/0x3e0 [ 262.517297] [] vm_mmap_pgoff+0x8a/0xc0 [ 262.517300] [] SyS_mmap_pgoff+0x1cb/0x270 [ 262.517304] [] SyS_mmap+0x1d/0x20 [ 262.517309] [] entry_SYSCALL_64_fastpath+0x12/0x76 [ 262.517313] other info that might help us debug this: [ 262.517315] Possible unsafe locking scenario: [ 262.517318] CPU0 CPU1 [ 262.517319] ---- ---- [ 262.517321] lock(&mm->mmap_sem); [ 262.517324] lock(&dev->dev_mutex); [ 262.517327] lock(&mm->mmap_sem); [ 262.517329] lock(&dev->dev_mutex); [ 262.517332] *** DEADLOCK *** Since vb2_fop_mmap doesn't take the lock, neither should v4l2_m2m_fop_mmap. Signed-off-by: Hans Verkuil Tested-by: Mikhail Ulyanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-mem2mem.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index af8d6b4aa807..ec3ad4eb0c57 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -881,18 +881,8 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_streamoff); int v4l2_m2m_fop_mmap(struct file *file, struct vm_area_struct *vma) { struct v4l2_fh *fh = file->private_data; - struct v4l2_m2m_ctx *m2m_ctx = fh->m2m_ctx; - int ret; - - if (m2m_ctx->q_lock && mutex_lock_interruptible(m2m_ctx->q_lock)) - return -ERESTARTSYS; - - ret = v4l2_m2m_mmap(file, m2m_ctx, vma); - if (m2m_ctx->q_lock) - mutex_unlock(m2m_ctx->q_lock); - - return ret; + return v4l2_m2m_mmap(file, fh->m2m_ctx, vma); } EXPORT_SYMBOL_GPL(v4l2_m2m_fop_mmap); -- cgit v1.2.3 From b0527115af80970d65ae37c468e05e7bde82ce81 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 20 Jul 2015 06:47:23 -0300 Subject: [media] tc358743: remove unused variable The bt pointer was never used, remove it. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/tc358743.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index d0dd83d7a38e..2e926317d7e9 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -1347,7 +1347,6 @@ static int tc358743_s_dv_timings(struct v4l2_subdev *sd, struct v4l2_dv_timings *timings) { struct tc358743_state *state = to_state(sd); - struct v4l2_bt_timings *bt; if (!timings) return -EINVAL; @@ -1361,8 +1360,6 @@ static int tc358743_s_dv_timings(struct v4l2_subdev *sd, return 0; } - bt = &timings->bt; - if (!v4l2_valid_dv_timings(timings, &tc358743_timings_cap, NULL, NULL)) { v4l2_dbg(1, debug, sd, "%s: timings out of range\n", __func__); -- cgit v1.2.3 From 6d7570c42b963004ecfb42d9625c898eb3087b02 Mon Sep 17 00:00:00 2001 From: Laura Abbott Date: Thu, 23 Jul 2015 19:28:48 -0300 Subject: [media] v4l2-ioctl: Give more information when device_caps are missing Currently, the warning for missing device_caps gives a backtrace like so: [] dump_stack+0x45/0x57 [] warn_slowpath_common+0x8a/0xc0 [] warn_slowpath_null+0x1a/0x20 [] v4l_querycap+0x43/0x80 [videodev] [] __video_do_ioctl+0x2a4/0x320 [videodev] [] ? do_last+0x195/0x1210 [] video_usercopy+0x22e/0x5b0 [videodev] [] ? v4l_querycap+0x80/0x80 [videodev] [] video_ioctl2+0x15/0x20 [videodev] [] v4l2_ioctl+0x113/0x150 [videodev] [] do_vfs_ioctl+0x2f8/0x4f0 [] ? __audit_syscall_entry+0xb4/0x110 [] ? do_audit_syscall_entry+0x6c/0x70 [] SyS_ioctl+0x81/0xa0 [] ? __audit_syscall_exit+0x1f6/0x2a0 [] system_call_fastpath+0x12/0x17 This indicates that device_caps are missing but doesn't give much of a clue which driver is actually at fault. Improve the warning output by showing the capabilities and the responsible driver. Signed-off-by: Laura Abbott Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ioctl.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 85de4557f696..ad7e929179ba 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -1025,8 +1025,9 @@ static int v4l_querycap(const struct v4l2_ioctl_ops *ops, * Drivers MUST fill in device_caps, so check for this and * warn if it was forgotten. */ - WARN_ON(!(cap->capabilities & V4L2_CAP_DEVICE_CAPS) || - !cap->device_caps); + WARN(!(cap->capabilities & V4L2_CAP_DEVICE_CAPS) || + !cap->device_caps, "Bad caps for driver %s, %x %x", + cap->driver, cap->capabilities, cap->device_caps); cap->device_caps |= V4L2_CAP_EXT_PIX_FORMAT; return ret; -- cgit v1.2.3 From 6951813e66e07f18c9a425c3fbd966947ea401ab Mon Sep 17 00:00:00 2001 From: Mikhail Ulyanov Date: Wed, 22 Jul 2015 08:23:04 -0300 Subject: [media] devicetree: bindings: Document Renesas R-Car JPEG Processing Unit Add Renesas R-Car JPEG processing unit driver device tree bindings documentation. Signed-off-by: Mikhail Ulyanov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../devicetree/bindings/media/renesas,jpu.txt | 24 ++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/renesas,jpu.txt diff --git a/Documentation/devicetree/bindings/media/renesas,jpu.txt b/Documentation/devicetree/bindings/media/renesas,jpu.txt new file mode 100644 index 000000000000..0cb94201bf92 --- /dev/null +++ b/Documentation/devicetree/bindings/media/renesas,jpu.txt @@ -0,0 +1,24 @@ +* Renesas JPEG Processing Unit + +The JPEG processing unit (JPU) incorporates the JPEG codec with an encoding +and decoding function conforming to the JPEG baseline process, so that the JPU +can encode image data and decode JPEG data quickly. + +Required properties: + - compatible: should containg one of the following: + - "renesas,jpu-r8a7790" for R-Car H2 + - "renesas,jpu-r8a7791" for R-Car M2-W + - "renesas,jpu-r8a7792" for R-Car V2H + - "renesas,jpu-r8a7793" for R-Car M2-N + + - reg: Base address and length of the registers block for the JPU. + - interrupts: JPU interrupt specifier. + - clocks: A phandle + clock-specifier pair for the JPU functional clock. + +Example: R8A7790 (R-Car H2) JPU node + jpeg-codec@fe980000 { + compatible = "renesas,jpu-r8a7790"; + reg = <0 0xfe980000 0 0x10300>; + interrupts = <0 272 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&mstp1_clks R8A7790_CLK_JPU>; + }; -- cgit v1.2.3 From 2c42cdbaec56a9565a2717b450506150c9c55103 Mon Sep 17 00:00:00 2001 From: Mikhail Ulyanov Date: Wed, 22 Jul 2015 08:23:03 -0300 Subject: [media] V4L2: platform: Add Renesas R-Car JPEG codec driver Here's the driver for the Renesas R-Car JPEG processing unit. The driver is implemented within the V4L2 framework as a memory-to-memory device. It presents two video nodes to userspace, one for the encoding part, and one for the decoding part. It was found that the only working mode for encoding is no markers output, so we generate markers with software. In the current version of driver we also use software JPEG header parsing because with hardware parsing performance is lower than desired. >From a userspace point of view the process is typical (S_FMT, REQBUF, optionally QUERYBUF, QBUF, STREAMON, DQBUF) for both the source and destination queues. STREAMON can return -EINVAL in case of mismatch of output and capture queues format. Also during decoding driver can return buffers if queued buffer with JPEG image contains image with inappropriate subsampling (e.g. 4:2:0 in JPEG and 4:2:2 in capture). If JPEG image and queue format dimensions differ driver will return buffer on QBUF with VB2_BUF_STATE_ERROR flag. During encoding the available formats are: V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_NV12, V4L2_PIX_FMT_NV16, V4L2_PIX_FMT_NV16M for source and V4L2_PIX_FMT_JPEG for destination. During decoding the available formats are: V4L2_PIX_FMT_JPEG for source and V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_NV16M, V4L2_PIX_FMT_NV12, V4L2_PIX_FMT_NV16 for destination. Performance of current version: 1280x800 NV12 image encoding/decoding decoding ~122 FPS encoding ~191 FPS Signed-off-by: Mikhail Ulyanov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/Kconfig | 12 + drivers/media/platform/Makefile | 1 + drivers/media/platform/rcar_jpu.c | 1794 +++++++++++++++++++++++++++++++++++++ 3 files changed, 1807 insertions(+) create mode 100644 drivers/media/platform/rcar_jpu.c diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index f6bed197130c..ea1cb833191f 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -231,6 +231,18 @@ config VIDEO_SH_VEU Support for the Video Engine Unit (VEU) on SuperH and SH-Mobile SoCs. +config VIDEO_RENESAS_JPU + tristate "Renesas JPEG Processing Unit" + depends on VIDEO_DEV && VIDEO_V4L2 + depends on ARCH_SHMOBILE || COMPILE_TEST + select VIDEOBUF2_DMA_CONTIG + select V4L2_MEM2MEM_DEV + ---help--- + This is a V4L2 driver for the Renesas JPEG Processing Unit. + + To compile this driver as a module, choose M here: the module + will be called rcar_jpu. + config VIDEO_RENESAS_VSP1 tristate "Renesas VSP1 Video Processing Engine" depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && HAS_DMA diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index 114f9aba1c00..c2d17c3407e9 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -44,6 +44,7 @@ obj-$(CONFIG_VIDEO_SH_VOU) += sh_vou.o obj-$(CONFIG_SOC_CAMERA) += soc_camera/ +obj-$(CONFIG_VIDEO_RENESAS_JPU) += rcar_jpu.o obj-$(CONFIG_VIDEO_RENESAS_VSP1) += vsp1/ obj-y += omap/ diff --git a/drivers/media/platform/rcar_jpu.c b/drivers/media/platform/rcar_jpu.c new file mode 100644 index 000000000000..2973f070d328 --- /dev/null +++ b/drivers/media/platform/rcar_jpu.c @@ -0,0 +1,1794 @@ +/* + * Author: Mikhail Ulyanov + * Copyright (C) 2014-2015 Cogent Embedded, Inc. + * Copyright (C) 2014-2015 Renesas Electronics Corporation + * + * This is based on the drivers/media/platform/s5p-jpeg driver by + * Andrzej Pietrasiewicz and Jacek Anaszewski. + * Some portions of code inspired by VSP1 driver by Laurent Pinchart. + * + * TODO in order of priority: + * 1) Rotation + * 2) Cropping + * 3) V4L2_CID_JPEG_ACTIVE_MARKER + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define DRV_NAME "rcar_jpu" + +/* + * Align JPEG header end to cache line to make sure we will not have any issues + * with cache; additionally to requerment (33.3.27 R01UH0501EJ0100 Rev.1.00) + */ +#define JPU_JPEG_HDR_SIZE (ALIGN(0x258, L1_CACHE_BYTES)) +#define JPU_JPEG_MAX_BYTES_PER_PIXEL 2 /* 16 bit precision format */ +#define JPU_JPEG_MIN_SIZE 25 /* SOI + SOF + EOI */ +#define JPU_JPEG_QTBL_SIZE 0x40 +#define JPU_JPEG_HDCTBL_SIZE 0x1c +#define JPU_JPEG_HACTBL_SIZE 0xb2 +#define JPU_JPEG_HEIGHT_OFFSET 0x91 +#define JPU_JPEG_WIDTH_OFFSET 0x93 +#define JPU_JPEG_SUBS_OFFSET 0x97 +#define JPU_JPEG_QTBL_LUM_OFFSET 0x07 +#define JPU_JPEG_QTBL_CHR_OFFSET 0x4c +#define JPU_JPEG_HDCTBL_LUM_OFFSET 0xa4 +#define JPU_JPEG_HACTBL_LUM_OFFSET 0xc5 +#define JPU_JPEG_HDCTBL_CHR_OFFSET 0x17c +#define JPU_JPEG_HACTBL_CHR_OFFSET 0x19d +#define JPU_JPEG_PADDING_OFFSET 0x24f +#define JPU_JPEG_LUM 0x00 +#define JPU_JPEG_CHR 0x01 +#define JPU_JPEG_DC 0x00 +#define JPU_JPEG_AC 0x10 + +#define JPU_JPEG_422 0x21 +#define JPU_JPEG_420 0x22 + +#define JPU_JPEG_DEFAULT_422_PIX_FMT V4L2_PIX_FMT_NV16M +#define JPU_JPEG_DEFAULT_420_PIX_FMT V4L2_PIX_FMT_NV12M + +/* JPEG markers */ +#define TEM 0x01 +#define SOF0 0xc0 +#define RST 0xd0 +#define SOI 0xd8 +#define EOI 0xd9 +#define DHP 0xde +#define DHT 0xc4 +#define COM 0xfe +#define DQT 0xdb +#define DRI 0xdd +#define APP0 0xe0 + +#define JPU_RESET_TIMEOUT 100 /* ms */ +#define JPU_JOB_TIMEOUT 300 /* ms */ +#define JPU_MAX_QUALITY 4 +#define JPU_WIDTH_MIN 16 +#define JPU_HEIGHT_MIN 16 +#define JPU_WIDTH_MAX 4096 +#define JPU_HEIGHT_MAX 4096 +#define JPU_MEMALIGN 8 + +/* Flags that indicate a format can be used for capture/output */ +#define JPU_FMT_TYPE_OUTPUT 0 +#define JPU_FMT_TYPE_CAPTURE 1 +#define JPU_ENC_CAPTURE (1 << 0) +#define JPU_ENC_OUTPUT (1 << 1) +#define JPU_DEC_CAPTURE (1 << 2) +#define JPU_DEC_OUTPUT (1 << 3) + +/* + * JPEG registers and bits + */ + +/* JPEG code mode register */ +#define JCMOD 0x00 +#define JCMOD_PCTR (1 << 7) +#define JCMOD_MSKIP_ENABLE (1 << 5) +#define JCMOD_DSP_ENC (0 << 3) +#define JCMOD_DSP_DEC (1 << 3) +#define JCMOD_REDU (7 << 0) +#define JCMOD_REDU_422 (1 << 0) +#define JCMOD_REDU_420 (2 << 0) + +/* JPEG code command register */ +#define JCCMD 0x04 +#define JCCMD_SRST (1 << 12) +#define JCCMD_JEND (1 << 2) +#define JCCMD_JSRT (1 << 0) + +/* JPEG code quantanization table number register */ +#define JCQTN 0x0c +#define JCQTN_SHIFT(t) (((t) - 1) << 1) + +/* JPEG code Huffman table number register */ +#define JCHTN 0x10 +#define JCHTN_AC_SHIFT(t) (((t) << 1) - 1) +#define JCHTN_DC_SHIFT(t) (((t) - 1) << 1) + +#define JCVSZU 0x1c /* JPEG code vertical size upper register */ +#define JCVSZD 0x20 /* JPEG code vertical size lower register */ +#define JCHSZU 0x24 /* JPEG code horizontal size upper register */ +#define JCHSZD 0x28 /* JPEG code horizontal size lower register */ +#define JCSZ_MASK 0xff /* JPEG code h/v size register contains only 1 byte*/ + +#define JCDTCU 0x2c /* JPEG code data count upper register */ +#define JCDTCM 0x30 /* JPEG code data count middle register */ +#define JCDTCD 0x34 /* JPEG code data count lower register */ + +/* JPEG interrupt enable register */ +#define JINTE 0x38 +#define JINTE_ERR (7 << 5) /* INT5 + INT6 + INT7 */ +#define JINTE_TRANSF_COMPL (1 << 10) + +/* JPEG interrupt status register */ +#define JINTS 0x3c +#define JINTS_MASK 0x7c68 +#define JINTS_ERR (1 << 5) +#define JINTS_PROCESS_COMPL (1 << 6) +#define JINTS_TRANSF_COMPL (1 << 10) + +#define JCDERR 0x40 /* JPEG code decode error register */ +#define JCDERR_MASK 0xf /* JPEG code decode error register mask*/ + +/* JPEG interface encoding */ +#define JIFECNT 0x70 +#define JIFECNT_INFT_422 0 +#define JIFECNT_INFT_420 1 +#define JIFECNT_SWAP_WB (3 << 4) /* to JPU */ + +#define JIFESYA1 0x74 /* encode source Y address register 1 */ +#define JIFESCA1 0x78 /* encode source C address register 1 */ +#define JIFESYA2 0x7c /* encode source Y address register 2 */ +#define JIFESCA2 0x80 /* encode source C address register 2 */ +#define JIFESMW 0x84 /* encode source memory width register */ +#define JIFESVSZ 0x88 /* encode source vertical size register */ +#define JIFESHSZ 0x8c /* encode source horizontal size register */ +#define JIFEDA1 0x90 /* encode destination address register 1 */ +#define JIFEDA2 0x94 /* encode destination address register 2 */ + +/* JPEG decoding control register */ +#define JIFDCNT 0xa0 +#define JIFDCNT_SWAP_WB (3 << 1) /* from JPU */ + +#define JIFDSA1 0xa4 /* decode source address register 1 */ +#define JIFDDMW 0xb0 /* decode destination memory width register */ +#define JIFDDVSZ 0xb4 /* decode destination vert. size register */ +#define JIFDDHSZ 0xb8 /* decode destination horiz. size register */ +#define JIFDDYA1 0xbc /* decode destination Y address register 1 */ +#define JIFDDCA1 0xc0 /* decode destination C address register 1 */ + +#define JCQTBL(n) (0x10000 + (n) * 0x40) /* quantization tables regs */ +#define JCHTBD(n) (0x10100 + (n) * 0x100) /* Huffman table DC regs */ +#define JCHTBA(n) (0x10120 + (n) * 0x100) /* Huffman table AC regs */ + +/** + * struct jpu - JPEG IP abstraction + * @mutex: the mutex protecting this structure + * @lock: spinlock protecting the device contexts + * @v4l2_dev: v4l2 device for mem2mem mode + * @vfd_encoder: video device node for encoder mem2mem mode + * @vfd_decoder: video device node for decoder mem2mem mode + * @m2m_dev: v4l2 mem2mem device data + * @curr: pointer to current context + * @irq_queue: interrupt handler waitqueue + * @regs: JPEG IP registers mapping + * @irq: JPEG IP irq + * @clk: JPEG IP clock + * @dev: JPEG IP struct device + * @alloc_ctx: videobuf2 memory allocator's context + * @ref_count: reference counter + */ +struct jpu { + struct mutex mutex; + spinlock_t lock; + struct v4l2_device v4l2_dev; + struct video_device vfd_encoder; + struct video_device vfd_decoder; + struct v4l2_m2m_dev *m2m_dev; + struct jpu_ctx *curr; + wait_queue_head_t irq_queue; + + void __iomem *regs; + unsigned int irq; + struct clk *clk; + struct device *dev; + void *alloc_ctx; + int ref_count; +}; + +/** + * struct jpu_buffer - driver's specific video buffer + * @buf: m2m buffer + * @compr_quality: destination image quality in compression mode + * @subsampling: source image subsampling in decompression mode + */ +struct jpu_buffer { + struct v4l2_m2m_buffer buf; + unsigned short compr_quality; + unsigned char subsampling; +}; + +/** + * struct jpu_fmt - driver's internal format data + * @fourcc: the fourcc code, 0 if not applicable + * @colorspace: the colorspace specifier + * @bpp: number of bits per pixel per plane + * @h_align: horizontal alignment order (align to 2^h_align) + * @v_align: vertical alignment order (align to 2^v_align) + * @subsampling: (horizontal:4 | vertical:4) subsampling factor + * @num_planes: number of planes + * @types: types of queue this format is applicable to + */ +struct jpu_fmt { + u32 fourcc; + u32 colorspace; + u8 bpp[2]; + u8 h_align; + u8 v_align; + u8 subsampling; + u8 num_planes; + u16 types; +}; + +/** + * jpu_q_data - parameters of one queue + * @fmtinfo: driver-specific format of this queue + * @format: multiplanar format of this queue + * @sequence: sequence number + */ +struct jpu_q_data { + struct jpu_fmt *fmtinfo; + struct v4l2_pix_format_mplane format; + unsigned int sequence; +}; + +/** + * jpu_ctx - the device context data + * @jpu: JPEG IP device for this context + * @encoder: compression (encode) operation or decompression (decode) + * @compr_quality: destination image quality in compression (encode) mode + * @out_q: source (output) queue information + * @cap_q: destination (capture) queue information + * @fh: file handler + * @ctrl_handler: controls handler + */ +struct jpu_ctx { + struct jpu *jpu; + bool encoder; + unsigned short compr_quality; + struct jpu_q_data out_q; + struct jpu_q_data cap_q; + struct v4l2_fh fh; + struct v4l2_ctrl_handler ctrl_handler; +}; + + /** + * jpeg_buffer - description of memory containing input JPEG data + * @end: end position in the buffer + * @curr: current position in the buffer + */ +struct jpeg_buffer { + void *end; + void *curr; +}; + +static struct jpu_fmt jpu_formats[] = { + { V4L2_PIX_FMT_JPEG, V4L2_COLORSPACE_JPEG, + {0, 0}, 0, 0, 0, 1, JPU_ENC_CAPTURE | JPU_DEC_OUTPUT }, + { V4L2_PIX_FMT_NV16M, V4L2_COLORSPACE_SRGB, + {8, 8}, 2, 2, JPU_JPEG_422, 2, JPU_ENC_OUTPUT | JPU_DEC_CAPTURE }, + { V4L2_PIX_FMT_NV12M, V4L2_COLORSPACE_SRGB, + {8, 4}, 2, 2, JPU_JPEG_420, 2, JPU_ENC_OUTPUT | JPU_DEC_CAPTURE }, + { V4L2_PIX_FMT_NV16, V4L2_COLORSPACE_SRGB, + {16, 0}, 2, 2, JPU_JPEG_422, 1, JPU_ENC_OUTPUT | JPU_DEC_CAPTURE }, + { V4L2_PIX_FMT_NV12, V4L2_COLORSPACE_SRGB, + {12, 0}, 2, 2, JPU_JPEG_420, 1, JPU_ENC_OUTPUT | JPU_DEC_CAPTURE }, +}; + +static const u8 zigzag[] = { + 0x03, 0x02, 0x0b, 0x13, 0x0a, 0x01, 0x00, 0x09, + 0x12, 0x1b, 0x23, 0x1a, 0x11, 0x08, 0x07, 0x06, + 0x0f, 0x10, 0x19, 0x22, 0x2b, 0x33, 0x2a, 0x21, + 0x18, 0x17, 0x0e, 0x05, 0x04, 0x0d, 0x16, 0x1f, + 0x20, 0x29, 0x32, 0x3b, 0x3a, 0x31, 0x28, 0x27, + 0x1e, 0x15, 0x0e, 0x14, 0x10, 0x26, 0x2f, 0x30, + 0x39, 0x38, 0x37, 0x2e, 0x25, 0x1c, 0x24, 0x2b, + 0x36, 0x3f, 0x3e, 0x35, 0x2c, 0x34, 0x3d, 0x3c +}; + +#define QTBL_SIZE (ALIGN(JPU_JPEG_QTBL_SIZE, \ + sizeof(unsigned int)) / sizeof(unsigned int)) +#define HDCTBL_SIZE (ALIGN(JPU_JPEG_HDCTBL_SIZE, \ + sizeof(unsigned int)) / sizeof(unsigned int)) +#define HACTBL_SIZE (ALIGN(JPU_JPEG_HACTBL_SIZE, \ + sizeof(unsigned int)) / sizeof(unsigned int)) +/* + * Start of image; Quantization tables + * SOF0 (17 bytes payload) is Baseline DCT - Sample precision, height, width, + * Number of image components, (Ci:8 - Hi:4 - Vi:4 - Tq:8) * 3 - Y,Cb,Cr; + * Huffman tables; Padding with 0xff (33.3.27 R01UH0501EJ0100 Rev.1.00) + */ +#define JPU_JPEG_HDR_BLOB { \ + 0xff, SOI, 0xff, DQT, 0x00, JPU_JPEG_QTBL_SIZE + 0x3, JPU_JPEG_LUM, \ + [JPU_JPEG_QTBL_LUM_OFFSET ... \ + JPU_JPEG_QTBL_LUM_OFFSET + JPU_JPEG_QTBL_SIZE - 1] = 0x00, \ + 0xff, DQT, 0x00, JPU_JPEG_QTBL_SIZE + 0x3, JPU_JPEG_CHR, \ + [JPU_JPEG_QTBL_CHR_OFFSET ... JPU_JPEG_QTBL_CHR_OFFSET + \ + JPU_JPEG_QTBL_SIZE - 1] = 0x00, 0xff, SOF0, 0x00, 0x11, 0x08, \ + [JPU_JPEG_HEIGHT_OFFSET ... JPU_JPEG_HEIGHT_OFFSET + 1] = 0x00, \ + [JPU_JPEG_WIDTH_OFFSET ... JPU_JPEG_WIDTH_OFFSET + 1] = 0x00, \ + 0x03, 0x01, [JPU_JPEG_SUBS_OFFSET] = 0x00, JPU_JPEG_LUM, \ + 0x02, 0x11, JPU_JPEG_CHR, 0x03, 0x11, JPU_JPEG_CHR, \ + 0xff, DHT, 0x00, JPU_JPEG_HDCTBL_SIZE + 0x3, JPU_JPEG_LUM|JPU_JPEG_DC, \ + [JPU_JPEG_HDCTBL_LUM_OFFSET ... \ + JPU_JPEG_HDCTBL_LUM_OFFSET + JPU_JPEG_HDCTBL_SIZE - 1] = 0x00, \ + 0xff, DHT, 0x00, JPU_JPEG_HACTBL_SIZE + 0x3, JPU_JPEG_LUM|JPU_JPEG_AC, \ + [JPU_JPEG_HACTBL_LUM_OFFSET ... \ + JPU_JPEG_HACTBL_LUM_OFFSET + JPU_JPEG_HACTBL_SIZE - 1] = 0x00, \ + 0xff, DHT, 0x00, JPU_JPEG_HDCTBL_SIZE + 0x3, JPU_JPEG_CHR|JPU_JPEG_DC, \ + [JPU_JPEG_HDCTBL_CHR_OFFSET ... \ + JPU_JPEG_HDCTBL_CHR_OFFSET + JPU_JPEG_HDCTBL_SIZE - 1] = 0x00, \ + 0xff, DHT, 0x00, JPU_JPEG_HACTBL_SIZE + 0x3, JPU_JPEG_CHR|JPU_JPEG_AC, \ + [JPU_JPEG_HACTBL_CHR_OFFSET ... \ + JPU_JPEG_HACTBL_CHR_OFFSET + JPU_JPEG_HACTBL_SIZE - 1] = 0x00, \ + [JPU_JPEG_PADDING_OFFSET ... JPU_JPEG_HDR_SIZE - 1] = 0xff \ +} + +static unsigned char jpeg_hdrs[JPU_MAX_QUALITY][JPU_JPEG_HDR_SIZE] = { + [0 ... JPU_MAX_QUALITY - 1] = JPU_JPEG_HDR_BLOB +}; + +static const unsigned int qtbl_lum[JPU_MAX_QUALITY][QTBL_SIZE] = { + { + 0x14101927, 0x322e3e44, 0x10121726, 0x26354144, + 0x19171f26, 0x35414444, 0x27262635, 0x41444444, + 0x32263541, 0x44444444, 0x2e354144, 0x44444444, + 0x3e414444, 0x44444444, 0x44444444, 0x44444444 + }, + { + 0x100b0b10, 0x171b1f1e, 0x0b0c0c0f, 0x1417171e, + 0x0b0c0d10, 0x171a232f, 0x100f1017, 0x1a252f40, + 0x1714171a, 0x27334040, 0x1b171a25, 0x33404040, + 0x1f17232f, 0x40404040, 0x1e1e2f40, 0x40404040 + }, + { + 0x0c08080c, 0x11151817, 0x0809090b, 0x0f131217, + 0x08090a0c, 0x13141b24, 0x0c0b0c15, 0x141c2435, + 0x110f1314, 0x1e27333b, 0x1513141c, 0x27333b3b, + 0x18121b24, 0x333b3b3b, 0x17172435, 0x3b3b3b3b + }, + { + 0x08060608, 0x0c0e1011, 0x06060608, 0x0a0d0c0f, + 0x06060708, 0x0d0e1218, 0x0808080e, 0x0d131823, + 0x0c0a0d0d, 0x141a2227, 0x0e0d0e13, 0x1a222727, + 0x100c1318, 0x22272727, 0x110f1823, 0x27272727 + } +}; + +static const unsigned int qtbl_chr[JPU_MAX_QUALITY][QTBL_SIZE] = { + { + 0x15192026, 0x36444444, 0x191c1826, 0x36444444, + 0x2018202b, 0x42444444, 0x26262b35, 0x44444444, + 0x36424444, 0x44444444, 0x44444444, 0x44444444, + 0x44444444, 0x44444444, 0x44444444, 0x44444444 + }, + { + 0x110f1115, 0x141a2630, 0x0f131211, 0x141a232b, + 0x11121416, 0x1a1e2e35, 0x1511161c, 0x1e273540, + 0x14141a1e, 0x27304040, 0x1a1a1e27, 0x303f4040, + 0x26232e35, 0x40404040, 0x302b3540, 0x40404040 + }, + { + 0x0d0b0d10, 0x14141d25, 0x0b0e0e0e, 0x10141a20, + 0x0d0e0f11, 0x14172328, 0x100e1115, 0x171e2832, + 0x14101417, 0x1e25323b, 0x1414171e, 0x25303b3b, + 0x1d1a2328, 0x323b3b3b, 0x25202832, 0x3b3b3b3b + }, + { + 0x0908090b, 0x0e111318, 0x080a090b, 0x0e0d1116, + 0x09090d0e, 0x0d0f171a, 0x0b0b0e0e, 0x0f141a21, + 0x0e0e0d0f, 0x14182127, 0x110d0f14, 0x18202727, + 0x1311171a, 0x21272727, 0x18161a21, 0x27272727 + } +}; + +static const unsigned int hdctbl_lum[HDCTBL_SIZE] = { + 0x00010501, 0x01010101, 0x01000000, 0x00000000, + 0x00010203, 0x04050607, 0x08090a0b +}; + +static const unsigned int hdctbl_chr[HDCTBL_SIZE] = { + 0x00010501, 0x01010101, 0x01000000, 0x00000000, + 0x00010203, 0x04050607, 0x08090a0b +}; + +static const unsigned int hactbl_lum[HACTBL_SIZE] = { + 0x00020103, 0x03020403, 0x05050404, 0x0000017d, 0x01020300, 0x04110512, + 0x21314106, 0x13516107, 0x22711432, 0x8191a108, 0x2342b1c1, 0x1552d1f0, + 0x24336272, 0x82090a16, 0x1718191a, 0x25262728, 0x292a3435, 0x36373839, + 0x3a434445, 0x46474849, 0x4a535455, 0x56575859, 0x5a636465, 0x66676869, + 0x6a737475, 0x76777879, 0x7a838485, 0x86878889, 0x8a929394, 0x95969798, + 0x999aa2a3, 0xa4a5a6a7, 0xa8a9aab2, 0xb3b4b5b6, 0xb7b8b9ba, 0xc2c3c4c5, + 0xc6c7c8c9, 0xcad2d3d4, 0xd5d6d7d8, 0xd9dae1e2, 0xe3e4e5e6, 0xe7e8e9ea, + 0xf1f2f3f4, 0xf5f6f7f8, 0xf9fa0000 +}; + +static const unsigned int hactbl_chr[HACTBL_SIZE] = { + 0x00020103, 0x03020403, 0x05050404, 0x0000017d, 0x01020300, 0x04110512, + 0x21314106, 0x13516107, 0x22711432, 0x8191a108, 0x2342b1c1, 0x1552d1f0, + 0x24336272, 0x82090a16, 0x1718191a, 0x25262728, 0x292a3435, 0x36373839, + 0x3a434445, 0x46474849, 0x4a535455, 0x56575859, 0x5a636465, 0x66676869, + 0x6a737475, 0x76777879, 0x7a838485, 0x86878889, 0x8a929394, 0x95969798, + 0x999aa2a3, 0xa4a5a6a7, 0xa8a9aab2, 0xb3b4b5b6, 0xb7b8b9ba, 0xc2c3c4c5, + 0xc6c7c8c9, 0xcad2d3d4, 0xd5d6d7d8, 0xd9dae1e2, 0xe3e4e5e6, 0xe7e8e9ea, + 0xf1f2f3f4, 0xf5f6f7f8, 0xf9fa0000 +}; + +static const char *error_to_text[16] = { + "Normal", + "SOI not detected", + "SOF1 to SOFF detected", + "Subsampling not detected", + "SOF accuracy error", + "DQT accuracy error", + "Component error 1", + "Component error 2", + "SOF0, DQT, and DHT not detected when SOS detected", + "SOS not detected", + "EOI not detected", + "Restart interval data number error detected", + "Image size error", + "Last MCU data number error", + "Block data number error", + "Unknown" +}; + +static struct jpu_buffer *vb2_to_jpu_buffer(struct vb2_buffer *vb) +{ + struct v4l2_m2m_buffer *b = + container_of(vb, struct v4l2_m2m_buffer, vb); + + return container_of(b, struct jpu_buffer, buf); +} + +static u32 jpu_read(struct jpu *jpu, unsigned int reg) +{ + return ioread32(jpu->regs + reg); +} + +static void jpu_write(struct jpu *jpu, u32 val, unsigned int reg) +{ + iowrite32(val, jpu->regs + reg); +} + +static struct jpu_ctx *ctrl_to_ctx(struct v4l2_ctrl *c) +{ + return container_of(c->handler, struct jpu_ctx, ctrl_handler); +} + +static struct jpu_ctx *fh_to_ctx(struct v4l2_fh *fh) +{ + return container_of(fh, struct jpu_ctx, fh); +} + +static void jpu_set_tbl(struct jpu *jpu, u32 reg, const unsigned int *tbl, + unsigned int len) { + unsigned int i; + + for (i = 0; i < len; i++) + jpu_write(jpu, tbl[i], reg + (i << 2)); +} + +static void jpu_set_qtbl(struct jpu *jpu, unsigned short quality) +{ + jpu_set_tbl(jpu, JCQTBL(0), qtbl_lum[quality], QTBL_SIZE); + jpu_set_tbl(jpu, JCQTBL(1), qtbl_chr[quality], QTBL_SIZE); +} + +static void jpu_set_htbl(struct jpu *jpu) +{ + jpu_set_tbl(jpu, JCHTBD(0), hdctbl_lum, HDCTBL_SIZE); + jpu_set_tbl(jpu, JCHTBA(0), hactbl_lum, HACTBL_SIZE); + jpu_set_tbl(jpu, JCHTBD(1), hdctbl_chr, HDCTBL_SIZE); + jpu_set_tbl(jpu, JCHTBA(1), hactbl_chr, HACTBL_SIZE); +} + +static int jpu_wait_reset(struct jpu *jpu) +{ + unsigned long timeout; + + timeout = jiffies + msecs_to_jiffies(JPU_RESET_TIMEOUT); + + while (jpu_read(jpu, JCCMD) & JCCMD_SRST) { + if (time_after(jiffies, timeout)) { + dev_err(jpu->dev, "timed out in reset\n"); + return -ETIMEDOUT; + } + schedule(); + } + + return 0; +} + +static int jpu_reset(struct jpu *jpu) +{ + jpu_write(jpu, JCCMD_SRST, JCCMD); + return jpu_wait_reset(jpu); +} + +/* + * ============================================================================ + * video ioctl operations + * ============================================================================ + */ +static void put_qtbl(u8 *p, const u8 *qtbl) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(zigzag); i++) + p[i] = *(qtbl + zigzag[i]); +} + +static void put_htbl(u8 *p, const u8 *htbl, unsigned int len) +{ + unsigned int i, j; + + for (i = 0; i < len; i += 4) + for (j = 0; j < 4 && (i + j) < len; ++j) + p[i + j] = htbl[i + 3 - j]; +} + +static void jpu_generate_hdr(unsigned short quality, unsigned char *p) +{ + put_qtbl(p + JPU_JPEG_QTBL_LUM_OFFSET, (const u8 *)qtbl_lum[quality]); + put_qtbl(p + JPU_JPEG_QTBL_CHR_OFFSET, (const u8 *)qtbl_chr[quality]); + + put_htbl(p + JPU_JPEG_HDCTBL_LUM_OFFSET, (const u8 *)hdctbl_lum, + JPU_JPEG_HDCTBL_SIZE); + put_htbl(p + JPU_JPEG_HACTBL_LUM_OFFSET, (const u8 *)hactbl_lum, + JPU_JPEG_HACTBL_SIZE); + + put_htbl(p + JPU_JPEG_HDCTBL_CHR_OFFSET, (const u8 *)hdctbl_chr, + JPU_JPEG_HDCTBL_SIZE); + put_htbl(p + JPU_JPEG_HACTBL_CHR_OFFSET, (const u8 *)hactbl_chr, + JPU_JPEG_HACTBL_SIZE); +} + +static int get_byte(struct jpeg_buffer *buf) +{ + if (buf->curr >= buf->end) + return -1; + + return *(u8 *)buf->curr++; +} + +static int get_word_be(struct jpeg_buffer *buf, unsigned int *word) +{ + if (buf->end - buf->curr < 2) + return -1; + + *word = get_unaligned_be16(buf->curr); + buf->curr += 2; + + return 0; +} + +static void skip(struct jpeg_buffer *buf, unsigned long len) +{ + buf->curr += min((unsigned long)(buf->end - buf->curr), len); +} + +static u8 jpu_parse_hdr(void *buffer, unsigned long size, unsigned int *width, + unsigned int *height) +{ + struct jpeg_buffer jpeg_buffer; + unsigned int word; + bool soi = false; + + jpeg_buffer.end = buffer + size; + jpeg_buffer.curr = buffer; + + /* + * basic size check and EOI - we don't want to let JPU cross + * buffer bounds in any case. Hope it's stopping by EOI. + */ + if (size < JPU_JPEG_MIN_SIZE || *(u8 *)(buffer + size - 1) != EOI) + return 0; + + for (;;) { + int c; + + /* skip preceding filler bytes */ + do + c = get_byte(&jpeg_buffer); + while (c == 0xff || c == 0); + + if (!soi && c == SOI) { + soi = true; + continue; + } else if (soi != (c != SOI)) + return 0; + + switch (c) { + case SOF0: /* SOF0: baseline JPEG */ + skip(&jpeg_buffer, 3); /* segment length and bpp */ + if (get_word_be(&jpeg_buffer, height) || + get_word_be(&jpeg_buffer, width) || + get_byte(&jpeg_buffer) != 3) /* YCbCr only */ + return 0; + + skip(&jpeg_buffer, 1); + return get_byte(&jpeg_buffer); + case DHT: + case DQT: + case COM: + case DRI: + case APP0 ... APP0 + 0x0f: + if (get_word_be(&jpeg_buffer, &word)) + return 0; + skip(&jpeg_buffer, (long)word - 2); + case 0: + break; + default: + return 0; + } + } + + return 0; +} + +static int jpu_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct jpu_ctx *ctx = fh_to_ctx(priv); + + if (ctx->encoder) + strlcpy(cap->card, DRV_NAME " encoder", sizeof(cap->card)); + else + strlcpy(cap->card, DRV_NAME " decoder", sizeof(cap->card)); + + strlcpy(cap->driver, DRV_NAME, sizeof(cap->driver)); + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", + dev_name(ctx->jpu->dev)); + cap->device_caps |= V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE; + cap->capabilities = V4L2_CAP_DEVICE_CAPS | cap->device_caps; + memset(cap->reserved, 0, sizeof(cap->reserved)); + + return 0; +} + +static struct jpu_fmt *jpu_find_format(bool encoder, u32 pixelformat, + unsigned int fmt_type) +{ + unsigned int i, fmt_flag; + + if (encoder) + fmt_flag = fmt_type == JPU_FMT_TYPE_OUTPUT ? JPU_ENC_OUTPUT : + JPU_ENC_CAPTURE; + else + fmt_flag = fmt_type == JPU_FMT_TYPE_OUTPUT ? JPU_DEC_OUTPUT : + JPU_DEC_CAPTURE; + + for (i = 0; i < ARRAY_SIZE(jpu_formats); i++) { + struct jpu_fmt *fmt = &jpu_formats[i]; + + if (fmt->fourcc == pixelformat && fmt->types & fmt_flag) + return fmt; + } + + return NULL; +} + +static int jpu_enum_fmt(struct v4l2_fmtdesc *f, u32 type) +{ + unsigned int i, num = 0; + + for (i = 0; i < ARRAY_SIZE(jpu_formats); ++i) { + if (jpu_formats[i].types & type) { + if (num == f->index) + break; + ++num; + } + } + + if (i >= ARRAY_SIZE(jpu_formats)) + return -EINVAL; + + f->pixelformat = jpu_formats[i].fourcc; + + return 0; +} + +static int jpu_enum_fmt_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct jpu_ctx *ctx = fh_to_ctx(priv); + + return jpu_enum_fmt(f, ctx->encoder ? JPU_ENC_CAPTURE : + JPU_DEC_CAPTURE); +} + +static int jpu_enum_fmt_out(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct jpu_ctx *ctx = fh_to_ctx(priv); + + return jpu_enum_fmt(f, ctx->encoder ? JPU_ENC_OUTPUT : JPU_DEC_OUTPUT); +} + +static struct jpu_q_data *jpu_get_q_data(struct jpu_ctx *ctx, + enum v4l2_buf_type type) +{ + if (V4L2_TYPE_IS_OUTPUT(type)) + return &ctx->out_q; + else + return &ctx->cap_q; +} + +static void jpu_bound_align_image(u32 *w, unsigned int w_min, + unsigned int w_max, unsigned int w_align, + u32 *h, unsigned int h_min, + unsigned int h_max, unsigned int h_align) +{ + unsigned int width, height, w_step, h_step; + + width = *w; + height = *h; + + w_step = 1U << w_align; + h_step = 1U << h_align; + v4l_bound_align_image(w, w_min, w_max, w_align, h, h_min, h_max, + h_align, 3); + + if (*w < width && *w + w_step < w_max) + *w += w_step; + if (*h < height && *h + h_step < h_max) + *h += h_step; +} + +static int __jpu_try_fmt(struct jpu_ctx *ctx, struct jpu_fmt **fmtinfo, + struct v4l2_pix_format_mplane *pix, + enum v4l2_buf_type type) +{ + struct jpu_fmt *fmt; + unsigned int f_type, w, h; + + f_type = V4L2_TYPE_IS_OUTPUT(type) ? JPU_FMT_TYPE_OUTPUT : + JPU_FMT_TYPE_CAPTURE; + + fmt = jpu_find_format(ctx->encoder, pix->pixelformat, f_type); + if (!fmt) { + unsigned int pixelformat; + + dev_dbg(ctx->jpu->dev, "unknown format; set default format\n"); + if (ctx->encoder) + pixelformat = f_type == JPU_FMT_TYPE_OUTPUT ? + V4L2_PIX_FMT_NV16M : V4L2_PIX_FMT_JPEG; + else + pixelformat = f_type == JPU_FMT_TYPE_CAPTURE ? + V4L2_PIX_FMT_NV16M : V4L2_PIX_FMT_JPEG; + fmt = jpu_find_format(ctx->encoder, pixelformat, f_type); + } + + pix->pixelformat = fmt->fourcc; + pix->colorspace = fmt->colorspace; + pix->field = V4L2_FIELD_NONE; + pix->num_planes = fmt->num_planes; + memset(pix->reserved, 0, sizeof(pix->reserved)); + + jpu_bound_align_image(&pix->width, JPU_WIDTH_MIN, JPU_WIDTH_MAX, + fmt->h_align, &pix->height, JPU_HEIGHT_MIN, + JPU_HEIGHT_MAX, fmt->v_align); + + w = pix->width; + h = pix->height; + + if (fmt->fourcc == V4L2_PIX_FMT_JPEG) { + /* ignore userspaces's sizeimage for encoding */ + if (pix->plane_fmt[0].sizeimage <= 0 || ctx->encoder) + pix->plane_fmt[0].sizeimage = JPU_JPEG_HDR_SIZE + + (JPU_JPEG_MAX_BYTES_PER_PIXEL * w * h); + pix->plane_fmt[0].bytesperline = 0; + memset(pix->plane_fmt[0].reserved, 0, + sizeof(pix->plane_fmt[0].reserved)); + } else { + unsigned int i, bpl = 0; + + for (i = 0; i < pix->num_planes; ++i) + bpl = max(bpl, pix->plane_fmt[i].bytesperline); + + bpl = clamp_t(unsigned int, bpl, w, JPU_WIDTH_MAX); + bpl = round_up(bpl, JPU_MEMALIGN); + + for (i = 0; i < pix->num_planes; ++i) { + pix->plane_fmt[i].bytesperline = bpl; + pix->plane_fmt[i].sizeimage = bpl * h * fmt->bpp[i] / 8; + memset(pix->plane_fmt[i].reserved, 0, + sizeof(pix->plane_fmt[i].reserved)); + } + } + + if (fmtinfo) + *fmtinfo = fmt; + + return 0; +} + +static int jpu_try_fmt(struct file *file, void *priv, struct v4l2_format *f) +{ + struct jpu_ctx *ctx = fh_to_ctx(priv); + + if (!v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type)) + return -EINVAL; + + return __jpu_try_fmt(ctx, NULL, &f->fmt.pix_mp, f->type); +} + +static int jpu_s_fmt(struct file *file, void *priv, struct v4l2_format *f) +{ + struct vb2_queue *vq; + struct jpu_ctx *ctx = fh_to_ctx(priv); + struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx; + struct jpu_fmt *fmtinfo; + struct jpu_q_data *q_data; + int ret; + + vq = v4l2_m2m_get_vq(m2m_ctx, f->type); + if (!vq) + return -EINVAL; + + if (vb2_is_busy(vq)) { + v4l2_err(&ctx->jpu->v4l2_dev, "%s queue busy\n", __func__); + return -EBUSY; + } + + ret = __jpu_try_fmt(ctx, &fmtinfo, &f->fmt.pix_mp, f->type); + if (ret < 0) + return ret; + + q_data = jpu_get_q_data(ctx, f->type); + + q_data->format = f->fmt.pix_mp; + q_data->fmtinfo = fmtinfo; + + return 0; +} + +static int jpu_g_fmt(struct file *file, void *priv, struct v4l2_format *f) +{ + struct jpu_q_data *q_data; + struct jpu_ctx *ctx = fh_to_ctx(priv); + + if (!v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type)) + return -EINVAL; + + q_data = jpu_get_q_data(ctx, f->type); + f->fmt.pix_mp = q_data->format; + + return 0; +} + +/* + * V4L2 controls + */ +static int jpu_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct jpu_ctx *ctx = ctrl_to_ctx(ctrl); + unsigned long flags; + + spin_lock_irqsave(&ctx->jpu->lock, flags); + if (ctrl->id == V4L2_CID_JPEG_COMPRESSION_QUALITY) + ctx->compr_quality = ctrl->val; + spin_unlock_irqrestore(&ctx->jpu->lock, flags); + + return 0; +} + +static const struct v4l2_ctrl_ops jpu_ctrl_ops = { + .s_ctrl = jpu_s_ctrl, +}; + +static int jpu_streamon(struct file *file, void *priv, enum v4l2_buf_type type) +{ + struct jpu_ctx *ctx = fh_to_ctx(priv); + struct jpu_q_data *src_q_data, *dst_q_data, *orig, adj, *ref; + enum v4l2_buf_type adj_type; + + src_q_data = jpu_get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + dst_q_data = jpu_get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + + if (ctx->encoder) { + adj = *src_q_data; + orig = src_q_data; + ref = dst_q_data; + adj_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + } else { + adj = *dst_q_data; + orig = dst_q_data; + ref = src_q_data; + adj_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + } + + adj.format.width = ref->format.width; + adj.format.height = ref->format.height; + + __jpu_try_fmt(ctx, NULL, &adj.format, adj_type); + + if (adj.format.width != orig->format.width || + adj.format.height != orig->format.height) { + dev_err(ctx->jpu->dev, "src and dst formats do not match.\n"); + /* maybe we can return -EPIPE here? */ + return -EINVAL; + } + + return v4l2_m2m_streamon(file, ctx->fh.m2m_ctx, type); +} + +static const struct v4l2_ioctl_ops jpu_ioctl_ops = { + .vidioc_querycap = jpu_querycap, + + .vidioc_enum_fmt_vid_cap_mplane = jpu_enum_fmt_cap, + .vidioc_enum_fmt_vid_out_mplane = jpu_enum_fmt_out, + .vidioc_g_fmt_vid_cap_mplane = jpu_g_fmt, + .vidioc_g_fmt_vid_out_mplane = jpu_g_fmt, + .vidioc_try_fmt_vid_cap_mplane = jpu_try_fmt, + .vidioc_try_fmt_vid_out_mplane = jpu_try_fmt, + .vidioc_s_fmt_vid_cap_mplane = jpu_s_fmt, + .vidioc_s_fmt_vid_out_mplane = jpu_s_fmt, + + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, + + .vidioc_streamon = jpu_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, + + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe +}; + +static int jpu_controls_create(struct jpu_ctx *ctx) +{ + struct v4l2_ctrl *ctrl; + int ret; + + v4l2_ctrl_handler_init(&ctx->ctrl_handler, 1); + + ctrl = v4l2_ctrl_new_std(&ctx->ctrl_handler, &jpu_ctrl_ops, + V4L2_CID_JPEG_COMPRESSION_QUALITY, + 0, JPU_MAX_QUALITY - 1, 1, 0); + + if (ctx->ctrl_handler.error) { + ret = ctx->ctrl_handler.error; + goto error_free; + } + + if (!ctx->encoder) + ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE | + V4L2_CTRL_FLAG_READ_ONLY; + + ret = v4l2_ctrl_handler_setup(&ctx->ctrl_handler); + if (ret < 0) + goto error_free; + + return 0; + +error_free: + v4l2_ctrl_handler_free(&ctx->ctrl_handler); + return ret; +} + +/* + * ============================================================================ + * Queue operations + * ============================================================================ + */ +static int jpu_queue_setup(struct vb2_queue *vq, + const struct v4l2_format *fmt, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) +{ + struct jpu_ctx *ctx = vb2_get_drv_priv(vq); + struct jpu_q_data *q_data; + unsigned int i; + + q_data = jpu_get_q_data(ctx, vq->type); + + *nplanes = q_data->format.num_planes; + + for (i = 0; i < *nplanes; i++) { + unsigned int q_size = q_data->format.plane_fmt[i].sizeimage; + unsigned int f_size = fmt ? + fmt->fmt.pix_mp.plane_fmt[i].sizeimage : 0; + + if (fmt && f_size < q_size) + return -EINVAL; + + sizes[i] = fmt ? f_size : q_size; + alloc_ctxs[i] = ctx->jpu->alloc_ctx; + } + + return 0; +} + +static int jpu_buf_prepare(struct vb2_buffer *vb) +{ + struct jpu_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct jpu_q_data *q_data; + unsigned int i; + + q_data = jpu_get_q_data(ctx, vb->vb2_queue->type); + + if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) { + if (vb->v4l2_buf.field == V4L2_FIELD_ANY) + vb->v4l2_buf.field = V4L2_FIELD_NONE; + if (vb->v4l2_buf.field != V4L2_FIELD_NONE) { + dev_err(ctx->jpu->dev, "%s field isn't supported\n", + __func__); + return -EINVAL; + } + } + + for (i = 0; i < q_data->format.num_planes; i++) { + unsigned long size = q_data->format.plane_fmt[i].sizeimage; + + if (vb2_plane_size(vb, i) < size) { + dev_err(ctx->jpu->dev, + "%s: data will not fit into plane (%lu < %lu)\n", + __func__, vb2_plane_size(vb, i), size); + return -EINVAL; + } + + /* decoder capture queue */ + if (!ctx->encoder && !V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) + vb2_set_plane_payload(vb, i, size); + } + + return 0; +} + +static void jpu_buf_queue(struct vb2_buffer *vb) +{ + struct jpu_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + + if (!ctx->encoder && V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) { + struct jpu_buffer *jpu_buf = vb2_to_jpu_buffer(vb); + struct jpu_q_data *q_data, adjust; + void *buffer = vb2_plane_vaddr(vb, 0); + unsigned long buf_size = vb2_get_plane_payload(vb, 0); + unsigned int width, height; + + u8 subsampling = jpu_parse_hdr(buffer, buf_size, &width, + &height); + + /* check if JPEG data basic parsing was successful */ + if (subsampling != JPU_JPEG_422 && subsampling != JPU_JPEG_420) + goto format_error; + + q_data = &ctx->out_q; + + adjust = *q_data; + adjust.format.width = width; + adjust.format.height = height; + + __jpu_try_fmt(ctx, &adjust.fmtinfo, &adjust.format, + V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + + if (adjust.format.width != q_data->format.width || + adjust.format.height != q_data->format.height) + goto format_error; + + /* + * keep subsampling in buffer to check it + * for compatibility in device_run + */ + jpu_buf->subsampling = subsampling; + } + + if (ctx->fh.m2m_ctx) + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vb); + + return; + +format_error: + dev_err(ctx->jpu->dev, "incompatible or corrupted JPEG data\n"); + vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); +} + +static void jpu_buf_finish(struct vb2_buffer *vb) +{ + struct jpu_buffer *jpu_buf = vb2_to_jpu_buffer(vb); + struct jpu_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct jpu_q_data *q_data = &ctx->out_q; + enum v4l2_buf_type type = vb->vb2_queue->type; + u8 *buffer; + + if (vb->state == VB2_BUF_STATE_DONE) + vb->v4l2_buf.sequence = jpu_get_q_data(ctx, type)->sequence++; + + if (!ctx->encoder || vb->state != VB2_BUF_STATE_DONE || + V4L2_TYPE_IS_OUTPUT(type)) + return; + + buffer = vb2_plane_vaddr(vb, 0); + + memcpy(buffer, jpeg_hdrs[jpu_buf->compr_quality], JPU_JPEG_HDR_SIZE); + *(u16 *)(buffer + JPU_JPEG_HEIGHT_OFFSET) = + cpu_to_be16(q_data->format.height); + *(u16 *)(buffer + JPU_JPEG_WIDTH_OFFSET) = + cpu_to_be16(q_data->format.width); + *(buffer + JPU_JPEG_SUBS_OFFSET) = q_data->fmtinfo->subsampling; +} + +static int jpu_start_streaming(struct vb2_queue *vq, unsigned count) +{ + struct jpu_ctx *ctx = vb2_get_drv_priv(vq); + struct jpu_q_data *q_data = jpu_get_q_data(ctx, vq->type); + + q_data->sequence = 0; + return 0; +} + +static void jpu_stop_streaming(struct vb2_queue *vq) +{ + struct jpu_ctx *ctx = vb2_get_drv_priv(vq); + struct vb2_buffer *vb; + unsigned long flags; + + for (;;) { + if (V4L2_TYPE_IS_OUTPUT(vq->type)) + vb = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + else + vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + if (vb == NULL) + return; + spin_lock_irqsave(&ctx->jpu->lock, flags); + v4l2_m2m_buf_done(vb, VB2_BUF_STATE_ERROR); + spin_unlock_irqrestore(&ctx->jpu->lock, flags); + } +} + +static struct vb2_ops jpu_qops = { + .queue_setup = jpu_queue_setup, + .buf_prepare = jpu_buf_prepare, + .buf_queue = jpu_buf_queue, + .buf_finish = jpu_buf_finish, + .start_streaming = jpu_start_streaming, + .stop_streaming = jpu_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static int jpu_queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq) +{ + struct jpu_ctx *ctx = priv; + int ret; + + memset(src_vq, 0, sizeof(*src_vq)); + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + src_vq->io_modes = VB2_MMAP | VB2_DMABUF; + src_vq->drv_priv = ctx; + src_vq->buf_struct_size = sizeof(struct jpu_buffer); + src_vq->ops = &jpu_qops; + src_vq->mem_ops = &vb2_dma_contig_memops; + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->lock = &ctx->jpu->mutex; + + ret = vb2_queue_init(src_vq); + if (ret) + return ret; + + memset(dst_vq, 0, sizeof(*dst_vq)); + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; + dst_vq->drv_priv = ctx; + dst_vq->buf_struct_size = sizeof(struct jpu_buffer); + dst_vq->ops = &jpu_qops; + dst_vq->mem_ops = &vb2_dma_contig_memops; + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->lock = &ctx->jpu->mutex; + + return vb2_queue_init(dst_vq); +} + +/* + * ============================================================================ + * Device file operations + * ============================================================================ + */ +static int jpu_open(struct file *file) +{ + struct jpu *jpu = video_drvdata(file); + struct video_device *vfd = video_devdata(file); + struct jpu_ctx *ctx; + int ret; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + v4l2_fh_init(&ctx->fh, vfd); + ctx->fh.ctrl_handler = &ctx->ctrl_handler; + file->private_data = &ctx->fh; + v4l2_fh_add(&ctx->fh); + + ctx->jpu = jpu; + ctx->encoder = vfd == &jpu->vfd_encoder; + + __jpu_try_fmt(ctx, &ctx->out_q.fmtinfo, &ctx->out_q.format, + V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + __jpu_try_fmt(ctx, &ctx->cap_q.fmtinfo, &ctx->cap_q.format, + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + + ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(jpu->m2m_dev, ctx, jpu_queue_init); + if (IS_ERR(ctx->fh.m2m_ctx)) { + ret = PTR_ERR(ctx->fh.m2m_ctx); + goto v4l_prepare_rollback; + } + + ret = jpu_controls_create(ctx); + if (ret < 0) + goto v4l_prepare_rollback; + + if (mutex_lock_interruptible(&jpu->mutex)) { + ret = -ERESTARTSYS; + goto v4l_prepare_rollback; + } + + if (jpu->ref_count == 0) { + ret = clk_prepare_enable(jpu->clk); + if (ret < 0) + goto device_prepare_rollback; + /* ...issue software reset */ + ret = jpu_reset(jpu); + if (ret) + goto device_prepare_rollback; + } + + jpu->ref_count++; + + mutex_unlock(&jpu->mutex); + return 0; + +device_prepare_rollback: + mutex_unlock(&jpu->mutex); +v4l_prepare_rollback: + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); + kfree(ctx); + return ret; +} + +static int jpu_release(struct file *file) +{ + struct jpu *jpu = video_drvdata(file); + struct jpu_ctx *ctx = fh_to_ctx(file->private_data); + + mutex_lock(&jpu->mutex); + if (--jpu->ref_count == 0) + clk_disable_unprepare(jpu->clk); + mutex_unlock(&jpu->mutex); + + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); + v4l2_ctrl_handler_free(&ctx->ctrl_handler); + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); + kfree(ctx); + + return 0; +} + +static const struct v4l2_file_operations jpu_fops = { + .owner = THIS_MODULE, + .open = jpu_open, + .release = jpu_release, + .unlocked_ioctl = video_ioctl2, + .poll = v4l2_m2m_fop_poll, + .mmap = v4l2_m2m_fop_mmap, +}; + +/* + * ============================================================================ + * mem2mem callbacks + * ============================================================================ + */ +static void jpu_cleanup(struct jpu_ctx *ctx, bool reset) +{ + /* remove current buffers and finish job */ + struct vb2_buffer *src_buf, *dst_buf; + unsigned long flags; + + spin_lock_irqsave(&ctx->jpu->lock, flags); + + src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR); + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR); + + /* ...and give it a chance on next run */ + if (reset) + jpu_write(ctx->jpu, JCCMD_SRST, JCCMD); + + spin_unlock_irqrestore(&ctx->jpu->lock, flags); + + v4l2_m2m_job_finish(ctx->jpu->m2m_dev, ctx->fh.m2m_ctx); +} + +static void jpu_device_run(void *priv) +{ + struct jpu_ctx *ctx = priv; + struct jpu *jpu = ctx->jpu; + struct jpu_buffer *jpu_buf; + struct jpu_q_data *q_data; + struct vb2_buffer *src_buf, *dst_buf; + unsigned int w, h, bpl; + unsigned char num_planes, subsampling; + unsigned long flags; + + /* ...wait until module reset completes; we have mutex locked here */ + if (jpu_wait_reset(jpu)) { + jpu_cleanup(ctx, true); + return; + } + + spin_lock_irqsave(&ctx->jpu->lock, flags); + + jpu->curr = ctx; + + src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + + if (ctx->encoder) { + jpu_buf = vb2_to_jpu_buffer(dst_buf); + q_data = &ctx->out_q; + } else { + jpu_buf = vb2_to_jpu_buffer(src_buf); + q_data = &ctx->cap_q; + } + + w = q_data->format.width; + h = q_data->format.height; + bpl = q_data->format.plane_fmt[0].bytesperline; + num_planes = q_data->fmtinfo->num_planes; + subsampling = q_data->fmtinfo->subsampling; + + if (ctx->encoder) { + unsigned long src_1_addr, src_2_addr, dst_addr; + unsigned int redu, inft; + + dst_addr = vb2_dma_contig_plane_dma_addr(dst_buf, 0); + src_1_addr = vb2_dma_contig_plane_dma_addr(src_buf, 0); + if (num_planes > 1) + src_2_addr = vb2_dma_contig_plane_dma_addr(src_buf, 1); + else + src_2_addr = src_1_addr + w * h; + + jpu_buf->compr_quality = ctx->compr_quality; + + if (subsampling == JPU_JPEG_420) { + redu = JCMOD_REDU_420; + inft = JIFECNT_INFT_420; + } else { + redu = JCMOD_REDU_422; + inft = JIFECNT_INFT_422; + } + + /* only no marker mode works for encoding */ + jpu_write(jpu, JCMOD_DSP_ENC | JCMOD_PCTR | redu | + JCMOD_MSKIP_ENABLE, JCMOD); + + jpu_write(jpu, JIFECNT_SWAP_WB | inft, JIFECNT); + jpu_write(jpu, JIFDCNT_SWAP_WB, JIFDCNT); + jpu_write(jpu, JINTE_TRANSF_COMPL, JINTE); + + /* Y and C components source addresses */ + jpu_write(jpu, src_1_addr, JIFESYA1); + jpu_write(jpu, src_2_addr, JIFESCA1); + + /* memory width */ + jpu_write(jpu, bpl, JIFESMW); + + jpu_write(jpu, (w >> 8) & JCSZ_MASK, JCHSZU); + jpu_write(jpu, w & JCSZ_MASK, JCHSZD); + + jpu_write(jpu, (h >> 8) & JCSZ_MASK, JCVSZU); + jpu_write(jpu, h & JCSZ_MASK, JCVSZD); + + jpu_write(jpu, w, JIFESHSZ); + jpu_write(jpu, h, JIFESVSZ); + + jpu_write(jpu, dst_addr + JPU_JPEG_HDR_SIZE, JIFEDA1); + + jpu_write(jpu, 0 << JCQTN_SHIFT(1) | 1 << JCQTN_SHIFT(2) | + 1 << JCQTN_SHIFT(3), JCQTN); + + jpu_write(jpu, 0 << JCHTN_AC_SHIFT(1) | 0 << JCHTN_DC_SHIFT(1) | + 1 << JCHTN_AC_SHIFT(2) | 1 << JCHTN_DC_SHIFT(2) | + 1 << JCHTN_AC_SHIFT(3) | 1 << JCHTN_DC_SHIFT(3), + JCHTN); + + jpu_set_qtbl(jpu, ctx->compr_quality); + jpu_set_htbl(jpu); + } else { + unsigned long src_addr, dst_1_addr, dst_2_addr; + + if (jpu_buf->subsampling != subsampling) { + dev_err(ctx->jpu->dev, + "src and dst formats do not match.\n"); + spin_unlock_irqrestore(&ctx->jpu->lock, flags); + jpu_cleanup(ctx, false); + return; + } + + src_addr = vb2_dma_contig_plane_dma_addr(src_buf, 0); + dst_1_addr = vb2_dma_contig_plane_dma_addr(dst_buf, 0); + if (q_data->fmtinfo->num_planes > 1) + dst_2_addr = vb2_dma_contig_plane_dma_addr(dst_buf, 1); + else + dst_2_addr = dst_1_addr + w * h; + + /* ...set up decoder operation */ + jpu_write(jpu, JCMOD_DSP_DEC | JCMOD_PCTR, JCMOD); + jpu_write(jpu, JIFECNT_SWAP_WB, JIFECNT); + jpu_write(jpu, JIFDCNT_SWAP_WB, JIFDCNT); + + /* ...enable interrupts on transfer completion and d-g error */ + jpu_write(jpu, JINTE_TRANSF_COMPL | JINTE_ERR, JINTE); + + /* ...set source/destination addresses of encoded data */ + jpu_write(jpu, src_addr, JIFDSA1); + jpu_write(jpu, dst_1_addr, JIFDDYA1); + jpu_write(jpu, dst_2_addr, JIFDDCA1); + + jpu_write(jpu, bpl, JIFDDMW); + } + + /* ...start encoder/decoder operation */ + jpu_write(jpu, JCCMD_JSRT, JCCMD); + + spin_unlock_irqrestore(&ctx->jpu->lock, flags); +} + +static int jpu_job_ready(void *priv) +{ + return 1; +} + +static void jpu_job_abort(void *priv) +{ + struct jpu_ctx *ctx = priv; + + if (!wait_event_timeout(ctx->jpu->irq_queue, !ctx->jpu->curr, + msecs_to_jiffies(JPU_JOB_TIMEOUT))) + jpu_cleanup(ctx, true); +} + +static struct v4l2_m2m_ops jpu_m2m_ops = { + .device_run = jpu_device_run, + .job_ready = jpu_job_ready, + .job_abort = jpu_job_abort, +}; + +/* + * ============================================================================ + * IRQ handler + * ============================================================================ + */ +static irqreturn_t jpu_irq_handler(int irq, void *dev_id) +{ + struct jpu *jpu = dev_id; + struct jpu_ctx *curr_ctx; + struct vb2_buffer *src_buf, *dst_buf; + unsigned int int_status; + + int_status = jpu_read(jpu, JINTS); + + /* ...spurious interrupt */ + if (!((JINTS_TRANSF_COMPL | JINTS_PROCESS_COMPL | JINTS_ERR) & + int_status)) + return IRQ_NONE; + + /* ...clear interrupts */ + jpu_write(jpu, ~(int_status & JINTS_MASK), JINTS); + if (int_status & (JINTS_ERR | JINTS_PROCESS_COMPL)) + jpu_write(jpu, JCCMD_JEND, JCCMD); + + spin_lock(&jpu->lock); + + if ((int_status & JINTS_PROCESS_COMPL) && + !(int_status & JINTS_TRANSF_COMPL)) + goto handled; + + curr_ctx = v4l2_m2m_get_curr_priv(jpu->m2m_dev); + if (!curr_ctx) { + /* ...instance is not running */ + dev_err(jpu->dev, "no active context for m2m\n"); + goto handled; + } + + src_buf = v4l2_m2m_src_buf_remove(curr_ctx->fh.m2m_ctx); + dst_buf = v4l2_m2m_dst_buf_remove(curr_ctx->fh.m2m_ctx); + + if (int_status & JINTS_TRANSF_COMPL) { + if (curr_ctx->encoder) { + unsigned long payload_size = jpu_read(jpu, JCDTCU) << 16 + | jpu_read(jpu, JCDTCM) << 8 + | jpu_read(jpu, JCDTCD); + vb2_set_plane_payload(dst_buf, 0, + payload_size + JPU_JPEG_HDR_SIZE); + } + + dst_buf->v4l2_buf.field = src_buf->v4l2_buf.field; + dst_buf->v4l2_buf.timestamp = src_buf->v4l2_buf.timestamp; + if (src_buf->v4l2_buf.flags & V4L2_BUF_FLAG_TIMECODE) + dst_buf->v4l2_buf.timecode = src_buf->v4l2_buf.timecode; + dst_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; + dst_buf->v4l2_buf.flags |= src_buf->v4l2_buf.flags & + V4L2_BUF_FLAG_TSTAMP_SRC_MASK; + dst_buf->v4l2_buf.flags = src_buf->v4l2_buf.flags & + (V4L2_BUF_FLAG_TIMECODE | V4L2_BUF_FLAG_KEYFRAME | + V4L2_BUF_FLAG_PFRAME | V4L2_BUF_FLAG_BFRAME | + V4L2_BUF_FLAG_TSTAMP_SRC_MASK); + + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE); + } else if (int_status & JINTS_ERR) { + unsigned char error = jpu_read(jpu, JCDERR) & JCDERR_MASK; + + dev_dbg(jpu->dev, "processing error: %#X: %s\n", error, + error_to_text[error]); + + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR); + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR); + } + + jpu->curr = NULL; + + /* ...reset JPU after completion */ + jpu_write(jpu, JCCMD_SRST, JCCMD); + spin_unlock(&jpu->lock); + + v4l2_m2m_job_finish(jpu->m2m_dev, curr_ctx->fh.m2m_ctx); + + /* ...wakeup abort routine if needed */ + wake_up(&jpu->irq_queue); + + return IRQ_HANDLED; + +handled: + spin_unlock(&jpu->lock); + return IRQ_HANDLED; +} + +/* + * ============================================================================ + * Driver basic infrastructure + * ============================================================================ + */ +static const struct of_device_id jpu_dt_ids[] = { + { .compatible = "renesas,jpu-r8a7790" }, /* H2 */ + { .compatible = "renesas,jpu-r8a7791" }, /* M2-W */ + { .compatible = "renesas,jpu-r8a7792" }, /* V2H */ + { .compatible = "renesas,jpu-r8a7793" }, /* M2-N */ + { }, +}; +MODULE_DEVICE_TABLE(of, jpu_dt_ids); + +static int jpu_probe(struct platform_device *pdev) +{ + struct jpu *jpu; + struct resource *res; + int ret; + unsigned int i; + + jpu = devm_kzalloc(&pdev->dev, sizeof(*jpu), GFP_KERNEL); + if (!jpu) + return -ENOMEM; + + init_waitqueue_head(&jpu->irq_queue); + mutex_init(&jpu->mutex); + spin_lock_init(&jpu->lock); + jpu->dev = &pdev->dev; + + /* memory-mapped registers */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + jpu->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(jpu->regs)) + return PTR_ERR(jpu->regs); + + /* interrupt service routine registration */ + jpu->irq = ret = platform_get_irq(pdev, 0); + if (ret < 0) { + dev_err(&pdev->dev, "cannot find IRQ\n"); + return ret; + } + + ret = devm_request_irq(&pdev->dev, jpu->irq, jpu_irq_handler, 0, + dev_name(&pdev->dev), jpu); + if (ret) { + dev_err(&pdev->dev, "cannot claim IRQ %d\n", jpu->irq); + return ret; + } + + /* clocks */ + jpu->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(jpu->clk)) { + dev_err(&pdev->dev, "cannot get clock\n"); + return PTR_ERR(jpu->clk); + } + + /* v4l2 device */ + ret = v4l2_device_register(&pdev->dev, &jpu->v4l2_dev); + if (ret) { + dev_err(&pdev->dev, "Failed to register v4l2 device\n"); + return ret; + } + + /* mem2mem device */ + jpu->m2m_dev = v4l2_m2m_init(&jpu_m2m_ops); + if (IS_ERR(jpu->m2m_dev)) { + v4l2_err(&jpu->v4l2_dev, "Failed to init mem2mem device\n"); + ret = PTR_ERR(jpu->m2m_dev); + goto device_register_rollback; + } + + jpu->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); + if (IS_ERR(jpu->alloc_ctx)) { + v4l2_err(&jpu->v4l2_dev, "Failed to init memory allocator\n"); + ret = PTR_ERR(jpu->alloc_ctx); + goto m2m_init_rollback; + } + + /* fill in qantization and Huffman tables for encoder */ + for (i = 0; i < JPU_MAX_QUALITY; i++) + jpu_generate_hdr(i, (unsigned char *)jpeg_hdrs[i]); + + strlcpy(jpu->vfd_encoder.name, DRV_NAME, sizeof(jpu->vfd_encoder.name)); + jpu->vfd_encoder.fops = &jpu_fops; + jpu->vfd_encoder.ioctl_ops = &jpu_ioctl_ops; + jpu->vfd_encoder.minor = -1; + jpu->vfd_encoder.release = video_device_release_empty; + jpu->vfd_encoder.lock = &jpu->mutex; + jpu->vfd_encoder.v4l2_dev = &jpu->v4l2_dev; + jpu->vfd_encoder.vfl_dir = VFL_DIR_M2M; + + ret = video_register_device(&jpu->vfd_encoder, VFL_TYPE_GRABBER, -1); + if (ret) { + v4l2_err(&jpu->v4l2_dev, "Failed to register video device\n"); + goto vb2_allocator_rollback; + } + + video_set_drvdata(&jpu->vfd_encoder, jpu); + + strlcpy(jpu->vfd_decoder.name, DRV_NAME, sizeof(jpu->vfd_decoder.name)); + jpu->vfd_decoder.fops = &jpu_fops; + jpu->vfd_decoder.ioctl_ops = &jpu_ioctl_ops; + jpu->vfd_decoder.minor = -1; + jpu->vfd_decoder.release = video_device_release_empty; + jpu->vfd_decoder.lock = &jpu->mutex; + jpu->vfd_decoder.v4l2_dev = &jpu->v4l2_dev; + jpu->vfd_decoder.vfl_dir = VFL_DIR_M2M; + + ret = video_register_device(&jpu->vfd_decoder, VFL_TYPE_GRABBER, -1); + if (ret) { + v4l2_err(&jpu->v4l2_dev, "Failed to register video device\n"); + goto enc_vdev_register_rollback; + } + + video_set_drvdata(&jpu->vfd_decoder, jpu); + platform_set_drvdata(pdev, jpu); + + v4l2_info(&jpu->v4l2_dev, "encoder device registered as /dev/video%d\n", + jpu->vfd_encoder.num); + v4l2_info(&jpu->v4l2_dev, "decoder device registered as /dev/video%d\n", + jpu->vfd_decoder.num); + + return 0; + +enc_vdev_register_rollback: + video_unregister_device(&jpu->vfd_encoder); + +vb2_allocator_rollback: + vb2_dma_contig_cleanup_ctx(jpu->alloc_ctx); + +m2m_init_rollback: + v4l2_m2m_release(jpu->m2m_dev); + +device_register_rollback: + v4l2_device_unregister(&jpu->v4l2_dev); + + return ret; +} + +static int jpu_remove(struct platform_device *pdev) +{ + struct jpu *jpu = platform_get_drvdata(pdev); + + video_unregister_device(&jpu->vfd_decoder); + video_unregister_device(&jpu->vfd_encoder); + vb2_dma_contig_cleanup_ctx(jpu->alloc_ctx); + v4l2_m2m_release(jpu->m2m_dev); + v4l2_device_unregister(&jpu->v4l2_dev); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int jpu_suspend(struct device *dev) +{ + struct jpu *jpu = dev_get_drvdata(dev); + + if (jpu->ref_count == 0) + return 0; + + clk_disable_unprepare(jpu->clk); + + return 0; +} + +static int jpu_resume(struct device *dev) +{ + struct jpu *jpu = dev_get_drvdata(dev); + + if (jpu->ref_count == 0) + return 0; + + clk_prepare_enable(jpu->clk); + + return 0; +} +#endif + +static const struct dev_pm_ops jpu_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(jpu_suspend, jpu_resume) +}; + +static struct platform_driver jpu_driver = { + .probe = jpu_probe, + .remove = jpu_remove, + .driver = { + .of_match_table = jpu_dt_ids, + .name = DRV_NAME, + .pm = &jpu_pm_ops, + }, +}; + +module_platform_driver(jpu_driver); + +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_AUTHOR("Mikhail Ulianov "); +MODULE_DESCRIPTION("Renesas R-Car JPEG processing unit driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 207dab5ff84b7d8d9c6e580b6065465f061b76dd Mon Sep 17 00:00:00 2001 From: Mikhail Ulyanov Date: Wed, 22 Jul 2015 08:23:05 -0300 Subject: [media] MAINTAINERS: V4L2: PLATFORM: Add entry for Renesas JPEG Processing Unit driver Update RENESAS JPU driver maintainer in MAINTAINERS file. Signed-off-by: Mikhail Ulyanov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 2bb989be111d..c14c2315e822 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5765,6 +5765,12 @@ S: Maintained F: fs/jbd2/ F: include/linux/jbd2.h +JPU V4L2 MEM2MEM DRIVER FOR RENESAS +M: Mikhail Ulyanov +L: linux-media@vger.kernel.org +S: Maintained +F: drivers/media/platform/rcar_jpu.c + JSM Neo PCI based serial card M: Thadeu Lima de Souza Cascardo L: linux-serial@vger.kernel.org -- cgit v1.2.3 From ed099f66dadd8bac93571fb28b05bdae066b39a2 Mon Sep 17 00:00:00 2001 From: Masanari Iida Date: Mon, 13 Jul 2015 20:36:50 -0300 Subject: [media] DocBook: Fix typo in intro.xml This patch fix spelling typos in intro.xml. This xml file is not created from comments within source, I fix the xml file. Signed-off-by: Masanari Iida [hans.verkuil@cisco.com: removed mention of obsolete devfs] Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/dvb/intro.xml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Documentation/DocBook/media/dvb/intro.xml b/Documentation/DocBook/media/dvb/intro.xml index bcc72c216402..d30751338493 100644 --- a/Documentation/DocBook/media/dvb/intro.xml +++ b/Documentation/DocBook/media/dvb/intro.xml @@ -163,9 +163,8 @@ are called: where N enumerates the DVB PCI cards in a system starting from 0, and M enumerates the devices of each type within each adapter, starting from 0, too. We will omit the “ -/dev/dvb/adapterN/” in the further dicussion -of these devices. The naming scheme for the devices is the same wheter -devfs is used or not. +/dev/dvb/adapterN/” in the further discussion +of these devices. More details about the data structures and function calls of all the devices are described in the following chapters. -- cgit v1.2.3 From 62e259493d779b0e2c1a675ab733136511310821 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 20 Jul 2015 09:59:28 -0300 Subject: [media] usbvision: remove power_on_at_open and timed power off This causes lots of problems and is *very* slow as well. One of the main problems is that this prohibits the use of the control framework since subdevs will be unloaded on power off which is not allowed as long as they are used by a usb device. Apparently the reason for doing this is to turn off a noisy tuner. My hardware has no problem with that, and I wonder whether the hardware with that noisy tuner wasn't just functioning improperly as I have never heard of noisy tuners. Contact me if you have one of those devices and I can take a look whether the tuner can't be powered off if necessary by letting the tuner subdevice go into standby mode. Unloading the tuner module is just evil and is not the right approach. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/usbvision/usbvision-core.c | 49 ------------------ drivers/media/usb/usbvision/usbvision-video.c | 71 ++++----------------------- drivers/media/usb/usbvision/usbvision.h | 5 -- 3 files changed, 9 insertions(+), 116 deletions(-) diff --git a/drivers/media/usb/usbvision/usbvision-core.c b/drivers/media/usb/usbvision/usbvision-core.c index 7c04ef697fb6..ec509844fe32 100644 --- a/drivers/media/usb/usbvision/usbvision-core.c +++ b/drivers/media/usb/usbvision/usbvision-core.c @@ -2160,55 +2160,6 @@ int usbvision_power_on(struct usb_usbvision *usbvision) } -/* - * usbvision timer stuff - */ - -/* to call usbvision_power_off from task queue */ -static void call_usbvision_power_off(struct work_struct *work) -{ - struct usb_usbvision *usbvision = container_of(work, struct usb_usbvision, power_off_work); - - PDEBUG(DBG_FUNC, ""); - if (mutex_lock_interruptible(&usbvision->v4l2_lock)) - return; - - if (usbvision->user == 0) { - usbvision_i2c_unregister(usbvision); - - usbvision_power_off(usbvision); - usbvision->initialized = 0; - } - mutex_unlock(&usbvision->v4l2_lock); -} - -static void usbvision_power_off_timer(unsigned long data) -{ - struct usb_usbvision *usbvision = (void *)data; - - PDEBUG(DBG_FUNC, ""); - del_timer(&usbvision->power_off_timer); - INIT_WORK(&usbvision->power_off_work, call_usbvision_power_off); - (void) schedule_work(&usbvision->power_off_work); -} - -void usbvision_init_power_off_timer(struct usb_usbvision *usbvision) -{ - setup_timer(&usbvision->power_off_timer, usbvision_power_off_timer, - (unsigned long)usbvision); -} - -void usbvision_set_power_off_timer(struct usb_usbvision *usbvision) -{ - mod_timer(&usbvision->power_off_timer, jiffies + USBVISION_POWEROFF_TIME); -} - -void usbvision_reset_power_off_timer(struct usb_usbvision *usbvision) -{ - if (timer_pending(&usbvision->power_off_timer)) - del_timer(&usbvision->power_off_timer); -} - /* * usbvision_begin_streaming() * Sure you have to put bit 7 to 0, if not incoming frames are droped, but no diff --git a/drivers/media/usb/usbvision/usbvision-video.c b/drivers/media/usb/usbvision/usbvision-video.c index 1c6d31f7c1b9..1355b5d12f19 100644 --- a/drivers/media/usb/usbvision/usbvision-video.c +++ b/drivers/media/usb/usbvision/usbvision-video.c @@ -122,8 +122,6 @@ static void usbvision_release(struct usb_usbvision *usbvision); static int isoc_mode = ISOC_MODE_COMPRESS; /* Set the default Debug Mode of the device driver */ static int video_debug; -/* Set the default device to power on at startup */ -static int power_on_at_open = 1; /* Sequential Number of Video Device */ static int video_nr = -1; /* Sequential Number of Radio Device */ @@ -134,13 +132,11 @@ static int radio_nr = -1; /* Showing parameters under SYSFS */ module_param(isoc_mode, int, 0444); module_param(video_debug, int, 0444); -module_param(power_on_at_open, int, 0444); module_param(video_nr, int, 0444); module_param(radio_nr, int, 0444); MODULE_PARM_DESC(isoc_mode, " Set the default format for ISOC endpoint. Default: 0x60 (Compression On)"); MODULE_PARM_DESC(video_debug, " Set the default Debug Mode of the device driver. Default: 0 (Off)"); -MODULE_PARM_DESC(power_on_at_open, " Set the default device to power on when device is opened. Default: 1 (On)"); MODULE_PARM_DESC(video_nr, "Set video device number (/dev/videoX). Default: -1 (autodetect)"); MODULE_PARM_DESC(radio_nr, "Set radio device number (/dev/radioX). Default: -1 (autodetect)"); @@ -351,11 +347,10 @@ static int usbvision_v4l2_open(struct file *file) if (mutex_lock_interruptible(&usbvision->v4l2_lock)) return -ERESTARTSYS; - usbvision_reset_power_off_timer(usbvision); - if (usbvision->user) + if (usbvision->user) { err_code = -EBUSY; - else { + } else { /* Allocate memory for the scratch ring buffer */ err_code = usbvision_scratch_alloc(usbvision); if (isoc_mode == ISOC_MODE_COMPRESS) { @@ -372,11 +367,6 @@ static int usbvision_v4l2_open(struct file *file) /* If so far no errors then we shall start the camera */ if (!err_code) { - if (usbvision->power == 0) { - usbvision_power_on(usbvision); - usbvision_i2c_register(usbvision); - } - /* Send init sequence only once, it's large! */ if (!usbvision->initialized) { int setup_ok = 0; @@ -392,18 +382,13 @@ static int usbvision_v4l2_open(struct file *file) err_code = usbvision_init_isoc(usbvision); /* device must be initialized before isoc transfer */ usbvision_muxsel(usbvision, 0); + + /* prepare queues */ + usbvision_empty_framequeues(usbvision); usbvision->user++; - } else { - if (power_on_at_open) { - usbvision_i2c_unregister(usbvision); - usbvision_power_off(usbvision); - usbvision->initialized = 0; - } } } - /* prepare queues */ - usbvision_empty_framequeues(usbvision); mutex_unlock(&usbvision->v4l2_lock); PDEBUG(DBG_IO, "success"); @@ -436,13 +421,6 @@ static int usbvision_v4l2_close(struct file *file) usbvision->user--; - if (power_on_at_open) { - /* power off in a little while - to avoid off/on every close/open short sequences */ - usbvision_set_power_off_timer(usbvision); - usbvision->initialized = 0; - } - if (usbvision->remove_pending) { printk(KERN_INFO "%s: Final disconnect\n", __func__); usbvision_release(usbvision); @@ -1173,14 +1151,6 @@ static int usbvision_radio_open(struct file *file) __func__); err_code = -EBUSY; } else { - if (power_on_at_open) { - usbvision_reset_power_off_timer(usbvision); - if (usbvision->power == 0) { - usbvision_power_on(usbvision); - usbvision_i2c_register(usbvision); - } - } - /* Alternate interface 1 is is the biggest frame size */ err_code = usbvision_set_alternate(usbvision); if (err_code < 0) { @@ -1195,14 +1165,6 @@ static int usbvision_radio_open(struct file *file) usbvision_set_audio(usbvision, USBVISION_AUDIO_RADIO); usbvision->user++; } - - if (err_code) { - if (power_on_at_open) { - usbvision_i2c_unregister(usbvision); - usbvision_power_off(usbvision); - usbvision->initialized = 0; - } - } out: mutex_unlock(&usbvision->v4l2_lock); return err_code; @@ -1226,11 +1188,6 @@ static int usbvision_radio_close(struct file *file) usbvision->radio = 0; usbvision->user--; - if (power_on_at_open) { - usbvision_set_power_off_timer(usbvision); - usbvision->initialized = 0; - } - if (usbvision->remove_pending) { printk(KERN_INFO "%s: Final disconnect\n", __func__); usbvision_release(usbvision); @@ -1428,8 +1385,6 @@ static struct usb_usbvision *usbvision_alloc(struct usb_device *dev, goto err_unreg; init_waitqueue_head(&usbvision->ctrl_urb_wq); - usbvision_init_power_off_timer(usbvision); - return usbvision; err_unreg: @@ -1450,8 +1405,6 @@ static void usbvision_release(struct usb_usbvision *usbvision) { PDEBUG(DBG_PROBE, ""); - usbvision_reset_power_off_timer(usbvision); - usbvision->initialized = 0; usbvision_remove_sysfs(&usbvision->vdev); @@ -1495,11 +1448,9 @@ static void usbvision_configure_video(struct usb_usbvision *usbvision) /* first switch off audio */ if (usbvision_device_data[model].audio_channels > 0) usbvision_audio_off(usbvision); - if (!power_on_at_open) { - /* and then power up the noisy tuner */ - usbvision_power_on(usbvision); - usbvision_i2c_register(usbvision); - } + /* and then power up the tuner */ + usbvision_power_on(usbvision); + usbvision_i2c_register(usbvision); } /* @@ -1646,11 +1597,7 @@ static void usbvision_disconnect(struct usb_interface *intf) usbvision_stop_isoc(usbvision); v4l2_device_disconnect(&usbvision->v4l2_dev); - - if (usbvision->power) { - usbvision_i2c_unregister(usbvision); - usbvision_power_off(usbvision); - } + usbvision_i2c_unregister(usbvision); usbvision->remove_pending = 1; /* Now all ISO data will be ignored */ usb_put_dev(usbvision->dev); diff --git a/drivers/media/usb/usbvision/usbvision.h b/drivers/media/usb/usbvision/usbvision.h index 140a1f67566e..d39ab10ae9cd 100644 --- a/drivers/media/usb/usbvision/usbvision.h +++ b/drivers/media/usb/usbvision/usbvision.h @@ -391,8 +391,6 @@ struct usb_usbvision { unsigned char iface_alt; /* Alt settings */ unsigned char vin_reg2_preset; struct mutex v4l2_lock; - struct timer_list power_off_timer; - struct work_struct power_off_work; int power; /* is the device powered on? */ int user; /* user count for exclusive use */ int initialized; /* Had we already sent init sequence? */ @@ -510,9 +508,6 @@ int usbvision_muxsel(struct usb_usbvision *usbvision, int channel); int usbvision_set_input(struct usb_usbvision *usbvision); int usbvision_set_output(struct usb_usbvision *usbvision, int width, int height); -void usbvision_init_power_off_timer(struct usb_usbvision *usbvision); -void usbvision_set_power_off_timer(struct usb_usbvision *usbvision); -void usbvision_reset_power_off_timer(struct usb_usbvision *usbvision); int usbvision_power_off(struct usb_usbvision *usbvision); int usbvision_power_on(struct usb_usbvision *usbvision); -- cgit v1.2.3 From fd95870d1bd6d7da7e7d0c550bfaae6d76f0799f Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 20 Jul 2015 09:59:29 -0300 Subject: [media] usbvision: convert to the control framework Convert this driver to the control framework and struct v4l2_fh (needed for handling control events). Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/usbvision/usbvision-video.c | 69 ++++++++++----------------- drivers/media/usb/usbvision/usbvision.h | 2 + 2 files changed, 28 insertions(+), 43 deletions(-) diff --git a/drivers/media/usb/usbvision/usbvision-video.c b/drivers/media/usb/usbvision/usbvision-video.c index 1355b5d12f19..ea67c8c05bcd 100644 --- a/drivers/media/usb/usbvision/usbvision-video.c +++ b/drivers/media/usb/usbvision/usbvision-video.c @@ -62,6 +62,7 @@ #include #include #include +#include #include #include @@ -351,6 +352,10 @@ static int usbvision_v4l2_open(struct file *file) if (usbvision->user) { err_code = -EBUSY; } else { + err_code = v4l2_fh_open(file); + if (err_code) + goto unlock; + /* Allocate memory for the scratch ring buffer */ err_code = usbvision_scratch_alloc(usbvision); if (isoc_mode == ISOC_MODE_COMPRESS) { @@ -389,6 +394,7 @@ static int usbvision_v4l2_open(struct file *file) } } +unlock: mutex_unlock(&usbvision->v4l2_lock); PDEBUG(DBG_IO, "success"); @@ -429,7 +435,7 @@ static int usbvision_v4l2_close(struct file *file) mutex_unlock(&usbvision->v4l2_lock); PDEBUG(DBG_IO, "success"); - return 0; + return v4l2_fh_release(file); } @@ -675,37 +681,6 @@ static int vidioc_s_audio(struct file *file, void *fh, return 0; } -static int vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *ctrl) -{ - struct usb_usbvision *usbvision = video_drvdata(file); - - call_all(usbvision, core, queryctrl, ctrl); - - if (!ctrl->type) - return -EINVAL; - - return 0; -} - -static int vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct usb_usbvision *usbvision = video_drvdata(file); - - call_all(usbvision, core, g_ctrl, ctrl); - return 0; -} - -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct usb_usbvision *usbvision = video_drvdata(file); - - call_all(usbvision, core, s_ctrl, ctrl); - return 0; -} - static int vidioc_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *vr) { @@ -1145,6 +1120,9 @@ static int usbvision_radio_open(struct file *file) if (mutex_lock_interruptible(&usbvision->v4l2_lock)) return -ERESTARTSYS; + err_code = v4l2_fh_open(file); + if (err_code) + goto out; if (usbvision->user) { dev_err(&usbvision->rdev.dev, "%s: Someone tried to open an already opened USBVision Radio!\n", @@ -1174,14 +1152,13 @@ out: static int usbvision_radio_close(struct file *file) { struct usb_usbvision *usbvision = video_drvdata(file); - int err_code = 0; PDEBUG(DBG_IO, ""); mutex_lock(&usbvision->v4l2_lock); /* Set packet size to 0 */ usbvision->iface_alt = 0; - err_code = usb_set_interface(usbvision->dev, usbvision->iface, + usb_set_interface(usbvision->dev, usbvision->iface, usbvision->iface_alt); usbvision_audio_off(usbvision); @@ -1190,13 +1167,14 @@ static int usbvision_radio_close(struct file *file) if (usbvision->remove_pending) { printk(KERN_INFO "%s: Final disconnect\n", __func__); + v4l2_fh_release(file); usbvision_release(usbvision); - return err_code; + return 0; } mutex_unlock(&usbvision->v4l2_lock); PDEBUG(DBG_IO, "success"); - return err_code; + return v4l2_fh_release(file); } /* Video registration stuff */ @@ -1209,7 +1187,6 @@ static const struct v4l2_file_operations usbvision_fops = { .read = usbvision_v4l2_read, .mmap = usbvision_v4l2_mmap, .unlocked_ioctl = video_ioctl2, -/* .poll = video_poll, */ }; static const struct v4l2_ioctl_ops usbvision_ioctl_ops = { @@ -1227,17 +1204,17 @@ static const struct v4l2_ioctl_ops usbvision_ioctl_ops = { .vidioc_enum_input = vidioc_enum_input, .vidioc_g_input = vidioc_g_input, .vidioc_s_input = vidioc_s_input, - .vidioc_queryctrl = vidioc_queryctrl, .vidioc_g_audio = vidioc_g_audio, .vidioc_s_audio = vidioc_s_audio, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, .vidioc_streamon = vidioc_streamon, .vidioc_streamoff = vidioc_streamoff, .vidioc_g_tuner = vidioc_g_tuner, .vidioc_s_tuner = vidioc_s_tuner, .vidioc_g_frequency = vidioc_g_frequency, .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, #ifdef CONFIG_VIDEO_ADV_DEBUG .vidioc_g_register = vidioc_g_register, .vidioc_s_register = vidioc_s_register, @@ -1258,6 +1235,7 @@ static const struct v4l2_file_operations usbvision_radio_fops = { .owner = THIS_MODULE, .open = usbvision_radio_open, .release = usbvision_radio_close, + .poll = v4l2_ctrl_poll, .unlocked_ioctl = video_ioctl2, }; @@ -1266,15 +1244,15 @@ static const struct v4l2_ioctl_ops usbvision_radio_ioctl_ops = { .vidioc_enum_input = vidioc_enum_input, .vidioc_g_input = vidioc_g_input, .vidioc_s_input = vidioc_s_input, - .vidioc_queryctrl = vidioc_queryctrl, .vidioc_g_audio = vidioc_g_audio, .vidioc_s_audio = vidioc_s_audio, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, .vidioc_g_tuner = vidioc_g_tuner, .vidioc_s_tuner = vidioc_s_tuner, .vidioc_g_frequency = vidioc_g_frequency, .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; static struct video_device usbvision_radio_template = { @@ -1377,6 +1355,9 @@ static struct usb_usbvision *usbvision_alloc(struct usb_device *dev, if (v4l2_device_register(&intf->dev, &usbvision->v4l2_dev)) goto err_free; + if (v4l2_ctrl_handler_init(&usbvision->hdl, 4)) + goto err_unreg; + usbvision->v4l2_dev.ctrl_handler = &usbvision->hdl; mutex_init(&usbvision->v4l2_lock); /* prepare control urb for control messages during interrupts */ @@ -1388,6 +1369,7 @@ static struct usb_usbvision *usbvision_alloc(struct usb_device *dev, return usbvision; err_unreg: + v4l2_ctrl_handler_free(&usbvision->hdl); v4l2_device_unregister(&usbvision->v4l2_dev); err_free: kfree(usbvision); @@ -1413,6 +1395,7 @@ static void usbvision_release(struct usb_usbvision *usbvision) usb_free_urb(usbvision->ctrl_urb); + v4l2_ctrl_handler_free(&usbvision->hdl); v4l2_device_unregister(&usbvision->v4l2_dev); kfree(usbvision); diff --git a/drivers/media/usb/usbvision/usbvision.h b/drivers/media/usb/usbvision/usbvision.h index d39ab10ae9cd..4dbb4218b771 100644 --- a/drivers/media/usb/usbvision/usbvision.h +++ b/drivers/media/usb/usbvision/usbvision.h @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -357,6 +358,7 @@ extern struct usb_device_id usbvision_table[]; struct usb_usbvision { struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler hdl; struct video_device vdev; /* Video Device */ struct video_device rdev; /* Radio Device */ -- cgit v1.2.3 From 2b43665ffbca7fe38617442cf731332afc94ead2 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 20 Jul 2015 09:59:30 -0300 Subject: [media] usbvision: return valid error in usbvision_register_video() Don't return -1, return a proper error code. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/usbvision/usbvision-video.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/usb/usbvision/usbvision-video.c b/drivers/media/usb/usbvision/usbvision-video.c index ea67c8c05bcd..82a65a495c24 100644 --- a/drivers/media/usb/usbvision/usbvision-video.c +++ b/drivers/media/usb/usbvision/usbvision-video.c @@ -1304,6 +1304,8 @@ static void usbvision_unregister_video(struct usb_usbvision *usbvision) /* register video4linux devices */ static int usbvision_register_video(struct usb_usbvision *usbvision) { + int res = -ENOMEM; + /* Video Device: */ usbvision_vdev_init(usbvision, &usbvision->vdev, &usbvision_video_template, "USBVision Video"); @@ -1330,7 +1332,7 @@ static int usbvision_register_video(struct usb_usbvision *usbvision) "USBVision[%d]: video_register_device() failed\n", usbvision->nr); usbvision_unregister_video(usbvision); - return -1; + return res; } /* -- cgit v1.2.3 From 601ecab34bab60896aa135ebde0d2b629d491821 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 20 Jul 2015 09:59:31 -0300 Subject: [media] usbvision: remove g/s_audio and for radio remove enum/g/s_input The g/s_audio ioctls didn't do anything, so remove them all for both video and radio nodes and remove V4L2_CAP_AUDIO. The enum/g/s_input ioctls are invalid for radio nodes, so remove them from the radio ioctl_ops. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/usbvision/usbvision-video.c | 29 --------------------------- 1 file changed, 29 deletions(-) diff --git a/drivers/media/usb/usbvision/usbvision-video.c b/drivers/media/usb/usbvision/usbvision-video.c index 82a65a495c24..15a1ebf336e2 100644 --- a/drivers/media/usb/usbvision/usbvision-video.c +++ b/drivers/media/usb/usbvision/usbvision-video.c @@ -494,7 +494,6 @@ static int vidioc_querycap(struct file *file, void *priv, sizeof(vc->card)); usb_make_path(usbvision->dev, vc->bus_info, sizeof(vc->bus_info)); vc->device_caps = V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_AUDIO | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING | (usbvision->have_tuner ? V4L2_CAP_TUNER : 0); @@ -524,7 +523,6 @@ static int vidioc_enum_input(struct file *file, void *priv, } else { strcpy(vi->name, "Television"); vi->type = V4L2_INPUT_TYPE_TUNER; - vi->audioset = 1; vi->tuner = chan; vi->std = USBVISION_NORMS; } @@ -661,26 +659,6 @@ static int vidioc_s_frequency(struct file *file, void *priv, return 0; } -static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) -{ - struct usb_usbvision *usbvision = video_drvdata(file); - - if (usbvision->radio) - strcpy(a->name, "Radio"); - else - strcpy(a->name, "TV"); - - return 0; -} - -static int vidioc_s_audio(struct file *file, void *fh, - const struct v4l2_audio *a) -{ - if (a->index) - return -EINVAL; - return 0; -} - static int vidioc_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *vr) { @@ -1204,8 +1182,6 @@ static const struct v4l2_ioctl_ops usbvision_ioctl_ops = { .vidioc_enum_input = vidioc_enum_input, .vidioc_g_input = vidioc_g_input, .vidioc_s_input = vidioc_s_input, - .vidioc_g_audio = vidioc_g_audio, - .vidioc_s_audio = vidioc_s_audio, .vidioc_streamon = vidioc_streamon, .vidioc_streamoff = vidioc_streamoff, .vidioc_g_tuner = vidioc_g_tuner, @@ -1241,11 +1217,6 @@ static const struct v4l2_file_operations usbvision_radio_fops = { static const struct v4l2_ioctl_ops usbvision_radio_ioctl_ops = { .vidioc_querycap = vidioc_querycap, - .vidioc_enum_input = vidioc_enum_input, - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, - .vidioc_g_audio = vidioc_g_audio, - .vidioc_s_audio = vidioc_s_audio, .vidioc_g_tuner = vidioc_g_tuner, .vidioc_s_tuner = vidioc_s_tuner, .vidioc_g_frequency = vidioc_g_frequency, -- cgit v1.2.3 From cbe12cc66e9c03ebd62a5937aada4240924dbe75 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 20 Jul 2015 09:59:32 -0300 Subject: [media] usbvision: the radio device node has wrong caps The radio device node had the same caps as the video node. Fix this. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/usbvision/usbvision-video.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/media/usb/usbvision/usbvision-video.c b/drivers/media/usb/usbvision/usbvision-video.c index 15a1ebf336e2..f526712a6435 100644 --- a/drivers/media/usb/usbvision/usbvision-video.c +++ b/drivers/media/usb/usbvision/usbvision-video.c @@ -487,17 +487,24 @@ static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *vc) { struct usb_usbvision *usbvision = video_drvdata(file); + struct video_device *vdev = video_devdata(file); strlcpy(vc->driver, "USBVision", sizeof(vc->driver)); strlcpy(vc->card, usbvision_device_data[usbvision->dev_model].model_string, sizeof(vc->card)); usb_make_path(usbvision->dev, vc->bus_info, sizeof(vc->bus_info)); - vc->device_caps = V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING | - (usbvision->have_tuner ? V4L2_CAP_TUNER : 0); - vc->capabilities = vc->device_caps | V4L2_CAP_DEVICE_CAPS; + vc->device_caps = usbvision->have_tuner ? V4L2_CAP_TUNER : 0; + if (vdev->vfl_type == VFL_TYPE_GRABBER) + vc->device_caps |= V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; + else + vc->device_caps |= V4L2_CAP_RADIO; + + vc->capabilities = vc->device_caps | V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_READWRITE | V4L2_CAP_STREAMING | V4L2_CAP_DEVICE_CAPS; + if (usbvision_device_data[usbvision->dev_model].radio) + vc->capabilities |= V4L2_CAP_RADIO; return 0; } -- cgit v1.2.3 From 64d416ec965360734beb295fe43c24f36ab465ca Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 20 Jul 2015 09:59:33 -0300 Subject: [media] usbvision: frequency fixes - setup initial radio and tv frequencies. - set/get the correct frequency (radio vs tv). - disable tuner/freq ioctls if there is no tuner. - fix some tuner index checks. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/usbvision/usbvision-video.c | 44 +++++++++++++++++---------- drivers/media/usb/usbvision/usbvision.h | 3 +- 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/drivers/media/usb/usbvision/usbvision-video.c b/drivers/media/usb/usbvision/usbvision-video.c index f526712a6435..dc0e034fc676 100644 --- a/drivers/media/usb/usbvision/usbvision-video.c +++ b/drivers/media/usb/usbvision/usbvision-video.c @@ -608,14 +608,13 @@ static int vidioc_g_tuner(struct file *file, void *priv, { struct usb_usbvision *usbvision = video_drvdata(file); - if (!usbvision->have_tuner || vt->index) /* Only tuner 0 */ + if (vt->index) /* Only tuner 0 */ return -EINVAL; - if (usbvision->radio) { + if (vt->type == V4L2_TUNER_RADIO) strcpy(vt->name, "Radio"); - vt->type = V4L2_TUNER_RADIO; - } else { + else strcpy(vt->name, "Television"); - } + /* Let clients fill in the remainder of this struct */ call_all(usbvision, tuner, g_tuner, vt); @@ -627,8 +626,8 @@ static int vidioc_s_tuner(struct file *file, void *priv, { struct usb_usbvision *usbvision = video_drvdata(file); - /* Only no or one tuner for now */ - if (!usbvision->have_tuner || vt->index) + /* Only one tuner for now */ + if (vt->index) return -EINVAL; /* let clients handle this */ call_all(usbvision, tuner, s_tuner, vt); @@ -641,12 +640,13 @@ static int vidioc_g_frequency(struct file *file, void *priv, { struct usb_usbvision *usbvision = video_drvdata(file); - freq->tuner = 0; /* Only one tuner */ - if (usbvision->radio) - freq->type = V4L2_TUNER_RADIO; + /* Only one tuner */ + if (freq->tuner) + return -EINVAL; + if (freq->type == V4L2_TUNER_RADIO) + freq->frequency = usbvision->radio_freq; else - freq->type = V4L2_TUNER_ANALOG_TV; - freq->frequency = usbvision->freq; + freq->frequency = usbvision->tv_freq; return 0; } @@ -655,13 +655,18 @@ static int vidioc_s_frequency(struct file *file, void *priv, const struct v4l2_frequency *freq) { struct usb_usbvision *usbvision = video_drvdata(file); + struct v4l2_frequency new_freq = *freq; - /* Only no or one tuner for now */ - if (!usbvision->have_tuner || freq->tuner) + /* Only one tuner for now */ + if (freq->tuner) return -EINVAL; - usbvision->freq = freq->frequency; call_all(usbvision, tuner, s_frequency, freq); + call_all(usbvision, tuner, g_frequency, &new_freq); + if (freq->type == V4L2_TUNER_RADIO) + usbvision->radio_freq = new_freq.frequency; + else + usbvision->tv_freq = new_freq.frequency; return 0; } @@ -1287,6 +1292,12 @@ static int usbvision_register_video(struct usb_usbvision *usbvision) /* Video Device: */ usbvision_vdev_init(usbvision, &usbvision->vdev, &usbvision_video_template, "USBVision Video"); + if (!usbvision->have_tuner) { + v4l2_disable_ioctl(&usbvision->vdev, VIDIOC_G_FREQUENCY); + v4l2_disable_ioctl(&usbvision->vdev, VIDIOC_S_TUNER); + v4l2_disable_ioctl(&usbvision->vdev, VIDIOC_G_FREQUENCY); + v4l2_disable_ioctl(&usbvision->vdev, VIDIOC_S_TUNER); + } if (video_register_device(&usbvision->vdev, VFL_TYPE_GRABBER, video_nr) < 0) goto err_exit; printk(KERN_INFO "USBVision[%d]: registered USBVision Video device %s [v4l2]\n", @@ -1403,9 +1414,10 @@ static void usbvision_configure_video(struct usb_usbvision *usbvision) } usbvision->tvnorm_id = usbvision_device_data[model].video_norm; - usbvision->video_inputs = usbvision_device_data[model].video_channels; usbvision->ctl_input = 0; + usbvision->radio_freq = 87.5 * 16000; + usbvision->tv_freq = 400 * 16; /* This should be here to make i2c clients to be able to register */ /* first switch off audio */ diff --git a/drivers/media/usb/usbvision/usbvision.h b/drivers/media/usb/usbvision/usbvision.h index 4dbb4218b771..4f2e4fde38f2 100644 --- a/drivers/media/usb/usbvision/usbvision.h +++ b/drivers/media/usb/usbvision/usbvision.h @@ -378,7 +378,8 @@ struct usb_usbvision { int bridge_type; /* NT1003, NT1004, NT1005 */ int radio; int video_inputs; /* # of inputs */ - unsigned long freq; + unsigned long radio_freq; + unsigned long tv_freq; int audio_mute; int audio_channel; int isoc_mode; /* format of video data for the usb isoc-transfer */ -- cgit v1.2.3 From 4eeda6faaec4b11e3738ba3ad533d440f628faca Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 20 Jul 2015 09:59:34 -0300 Subject: [media] usbvision: set field and colorspace Set the colorspace and field in vidioc_try_fmt_vid_cap(). Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/usbvision/usbvision-video.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/usb/usbvision/usbvision-video.c b/drivers/media/usb/usbvision/usbvision-video.c index dc0e034fc676..4f425f3600f6 100644 --- a/drivers/media/usb/usbvision/usbvision-video.c +++ b/drivers/media/usb/usbvision/usbvision-video.c @@ -880,6 +880,8 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, vf->fmt.pix.bytesperline = vf->fmt.pix.width* usbvision->palette.bytes_per_pixel; vf->fmt.pix.sizeimage = vf->fmt.pix.bytesperline*vf->fmt.pix.height; + vf->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + vf->fmt.pix.field = V4L2_FIELD_NONE; /* Always progressive image */ return 0; } -- cgit v1.2.3 From e2c84ccb0fbe5e524d15bb09c042a6ca634adaed Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 20 Jul 2015 09:59:35 -0300 Subject: [media] usbvision: fix locking error If remove_pending is non-zero, then the v4l2_lock is never unlocked. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/usbvision/usbvision-video.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/usbvision/usbvision-video.c b/drivers/media/usb/usbvision/usbvision-video.c index 4f425f3600f6..a5e82c08f13f 100644 --- a/drivers/media/usb/usbvision/usbvision-video.c +++ b/drivers/media/usb/usbvision/usbvision-video.c @@ -426,13 +426,13 @@ static int usbvision_v4l2_close(struct file *file) usbvision_scratch_free(usbvision); usbvision->user--; + mutex_unlock(&usbvision->v4l2_lock); if (usbvision->remove_pending) { printk(KERN_INFO "%s: Final disconnect\n", __func__); usbvision_release(usbvision); return 0; } - mutex_unlock(&usbvision->v4l2_lock); PDEBUG(DBG_IO, "success"); return v4l2_fh_release(file); -- cgit v1.2.3 From 8926e8453476ef3c714d659803068b0f26dd656c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 20 Jul 2015 09:59:36 -0300 Subject: [media] usbvision: fix DMA from stack warnings In various places the stack was used to provide buffers for USB data, but this should be allocated memory. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/usbvision/usbvision-core.c | 18 ++++++++++-------- drivers/media/usb/usbvision/usbvision-i2c.c | 2 +- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/drivers/media/usb/usbvision/usbvision-core.c b/drivers/media/usb/usbvision/usbvision-core.c index ec509844fe32..9f4e63083e28 100644 --- a/drivers/media/usb/usbvision/usbvision-core.c +++ b/drivers/media/usb/usbvision/usbvision-core.c @@ -1367,7 +1367,7 @@ static void usbvision_isoc_irq(struct urb *urb) int usbvision_read_reg(struct usb_usbvision *usbvision, unsigned char reg) { int err_code = 0; - unsigned char buffer[1]; + unsigned char *buffer = usbvision->ctrl_urb_buffer; if (!USBVISION_IS_OPERATIONAL(usbvision)) return -1; @@ -1401,10 +1401,12 @@ int usbvision_write_reg(struct usb_usbvision *usbvision, unsigned char reg, if (!USBVISION_IS_OPERATIONAL(usbvision)) return 0; + usbvision->ctrl_urb_buffer[0] = value; err_code = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1), USBVISION_OP_CODE, USB_DIR_OUT | USB_TYPE_VENDOR | - USB_RECIP_ENDPOINT, 0, (__u16) reg, &value, 1, HZ); + USB_RECIP_ENDPOINT, 0, (__u16) reg, + usbvision->ctrl_urb_buffer, 1, HZ); if (err_code < 0) { dev_err(&usbvision->dev->dev, @@ -1596,7 +1598,7 @@ static int usbvision_init_webcam(struct usb_usbvision *usbvision) { 0x27, 0x00, 0x00 }, { 0x28, 0x00, 0x00 }, { 0x29, 0x00, 0x00 }, { 0x08, 0x80, 0x60 }, { 0x0f, 0x2d, 0x24 }, { 0x0c, 0x80, 0x80 } }; - char value[3]; + unsigned char *value = usbvision->ctrl_urb_buffer; /* the only difference between PAL and NTSC init_values */ if (usbvision_device_data[usbvision->dev_model].video_norm == V4L2_STD_NTSC) @@ -1635,8 +1637,8 @@ static int usbvision_init_webcam(struct usb_usbvision *usbvision) static int usbvision_set_video_format(struct usb_usbvision *usbvision, int format) { static const char proc[] = "usbvision_set_video_format"; + unsigned char *value = usbvision->ctrl_urb_buffer; int rc; - unsigned char value[2]; if (!USBVISION_IS_OPERATIONAL(usbvision)) return 0; @@ -1677,7 +1679,7 @@ int usbvision_set_output(struct usb_usbvision *usbvision, int width, int err_code = 0; int usb_width, usb_height; unsigned int frame_rate = 0, frame_drop = 0; - unsigned char value[4]; + unsigned char *value = usbvision->ctrl_urb_buffer; if (!USBVISION_IS_OPERATIONAL(usbvision)) return 0; @@ -1872,7 +1874,7 @@ static int usbvision_set_compress_params(struct usb_usbvision *usbvision) { static const char proc[] = "usbvision_set_compresion_params: "; int rc; - unsigned char value[6]; + unsigned char *value = usbvision->ctrl_urb_buffer; value[0] = 0x0F; /* Intra-Compression cycle */ value[1] = 0x01; /* Reg.45 one line per strip */ @@ -1946,7 +1948,7 @@ int usbvision_set_input(struct usb_usbvision *usbvision) { static const char proc[] = "usbvision_set_input: "; int rc; - unsigned char value[8]; + unsigned char *value = usbvision->ctrl_urb_buffer; unsigned char dvi_yuv_value; if (!USBVISION_IS_OPERATIONAL(usbvision)) @@ -2062,8 +2064,8 @@ int usbvision_set_input(struct usb_usbvision *usbvision) static int usbvision_set_dram_settings(struct usb_usbvision *usbvision) { + unsigned char *value = usbvision->ctrl_urb_buffer; int rc; - unsigned char value[8]; if (usbvision->isoc_mode == ISOC_MODE_COMPRESS) { value[0] = 0x42; diff --git a/drivers/media/usb/usbvision/usbvision-i2c.c b/drivers/media/usb/usbvision/usbvision-i2c.c index 26dbcb1146af..120de2e020e1 100644 --- a/drivers/media/usb/usbvision/usbvision-i2c.c +++ b/drivers/media/usb/usbvision/usbvision-i2c.c @@ -343,7 +343,7 @@ static int usbvision_i2c_write_max4(struct usb_usbvision *usbvision, { int rc, retries; int i; - unsigned char value[6]; + unsigned char *value = usbvision->ctrl_urb_buffer; unsigned char ser_cont; ser_cont = (len & 0x07) | 0x10; -- cgit v1.2.3 From 94384014f6ef8091bf31b68721761e68f77ad214 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 20 Jul 2015 09:59:37 -0300 Subject: [media] usbvision: fix standards for S-Video/Composite inputs The standards supported by S-Video and Composite inputs are not limited by PAL, so make it more generic. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/usbvision/usbvision-video.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/usb/usbvision/usbvision-video.c b/drivers/media/usb/usbvision/usbvision-video.c index a5e82c08f13f..6ad3d568d495 100644 --- a/drivers/media/usb/usbvision/usbvision-video.c +++ b/drivers/media/usb/usbvision/usbvision-video.c @@ -540,7 +540,7 @@ static int vidioc_enum_input(struct file *file, void *priv, strcpy(vi->name, "Green Video Input"); else strcpy(vi->name, "Composite Video Input"); - vi->std = V4L2_STD_PAL; + vi->std = USBVISION_NORMS; break; case 2: vi->type = V4L2_INPUT_TYPE_CAMERA; @@ -548,12 +548,12 @@ static int vidioc_enum_input(struct file *file, void *priv, strcpy(vi->name, "Yellow Video Input"); else strcpy(vi->name, "S-Video Input"); - vi->std = V4L2_STD_PAL; + vi->std = USBVISION_NORMS; break; case 3: vi->type = V4L2_INPUT_TYPE_CAMERA; strcpy(vi->name, "Red Video Input"); - vi->std = V4L2_STD_PAL; + vi->std = USBVISION_NORMS; break; } return 0; -- cgit v1.2.3 From df3cfa6d9265f10fabc3c44f852d6b6bbf26029e Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 20 Jul 2015 09:59:38 -0300 Subject: [media] usbvision: move init code to probe() These things are only initialized if you start streaming video, but they are also used in the disconnect function. So just init them always during probe time. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/usbvision/usbvision-core.c | 4 ---- drivers/media/usb/usbvision/usbvision-video.c | 4 ++++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/media/usb/usbvision/usbvision-core.c b/drivers/media/usb/usbvision/usbvision-core.c index 9f4e63083e28..dc3b4d5155c5 100644 --- a/drivers/media/usb/usbvision/usbvision-core.c +++ b/drivers/media/usb/usbvision/usbvision-core.c @@ -1791,10 +1791,6 @@ int usbvision_frames_alloc(struct usb_usbvision *usbvision, int number_of_frames usbvision->num_frames--; } - spin_lock_init(&usbvision->queue_lock); - init_waitqueue_head(&usbvision->wait_frame); - init_waitqueue_head(&usbvision->wait_stream); - /* Allocate all buffers */ for (i = 0; i < usbvision->num_frames; i++) { usbvision->frame[i].index = i; diff --git a/drivers/media/usb/usbvision/usbvision-video.c b/drivers/media/usb/usbvision/usbvision-video.c index 6ad3d568d495..b693206f66dd 100644 --- a/drivers/media/usb/usbvision/usbvision-video.c +++ b/drivers/media/usb/usbvision/usbvision-video.c @@ -1520,6 +1520,10 @@ static int usbvision_probe(struct usb_interface *intf, usbvision->nr = usbvision_nr++; + spin_lock_init(&usbvision->queue_lock); + init_waitqueue_head(&usbvision->wait_frame); + init_waitqueue_head(&usbvision->wait_stream); + usbvision->have_tuner = usbvision_device_data[model].tuner; if (usbvision->have_tuner) usbvision->tuner_type = usbvision_device_data[model].tuner_type; -- cgit v1.2.3 From 1fea3d6069ae446e084177430f66c11cd4fc8751 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 20 Jul 2015 10:09:28 -0300 Subject: [media] fsl-viu: convert to the control framework Interestingly enough, the existing control handling code basically did nothing. At least the new code will inherit the controls from the saa7115 driver. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/fsl-viu.c | 110 +++++---------------------------------- 1 file changed, 14 insertions(+), 96 deletions(-) diff --git a/drivers/media/platform/fsl-viu.c b/drivers/media/platform/fsl-viu.c index 5b76e3db6a92..f0ec55134b45 100644 --- a/drivers/media/platform/fsl-viu.c +++ b/drivers/media/platform/fsl-viu.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #define DRV_NAME "fsl_viu" @@ -40,49 +41,6 @@ /* I2C address of video decoder chip is 0x4A */ #define VIU_VIDEO_DECODER_ADDR 0x25 -/* supported controls */ -static struct v4l2_queryctrl viu_qctrl[] = { - { - .id = V4L2_CID_BRIGHTNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Brightness", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = 127, - .flags = 0, - }, { - .id = V4L2_CID_CONTRAST, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Contrast", - .minimum = 0, - .maximum = 255, - .step = 0x1, - .default_value = 0x10, - .flags = 0, - }, { - .id = V4L2_CID_SATURATION, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Saturation", - .minimum = 0, - .maximum = 255, - .step = 0x1, - .default_value = 127, - .flags = 0, - }, { - .id = V4L2_CID_HUE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Hue", - .minimum = -128, - .maximum = 127, - .step = 0x1, - .default_value = 0, - .flags = 0, - } -}; - -static int qctl_regs[ARRAY_SIZE(viu_qctrl)]; - static int info_level; #define dprintk(level, fmt, arg...) \ @@ -156,6 +114,7 @@ struct viu_reg { struct viu_dev { struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler hdl; struct mutex lock; spinlock_t slock; int users; @@ -1009,51 +968,6 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i) return 0; } -/* Controls */ -static int vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(viu_qctrl); i++) { - if (qc->id && qc->id == viu_qctrl[i].id) { - memcpy(qc, &(viu_qctrl[i]), sizeof(*qc)); - return 0; - } - } - return -EINVAL; -} - -static int vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(viu_qctrl); i++) { - if (ctrl->id == viu_qctrl[i].id) { - ctrl->value = qctl_regs[i]; - return 0; - } - } - return -EINVAL; -} -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(viu_qctrl); i++) { - if (ctrl->id == viu_qctrl[i].id) { - if (ctrl->value < viu_qctrl[i].minimum - || ctrl->value > viu_qctrl[i].maximum) - return -ERANGE; - qctl_regs[i] = ctrl->value; - return 0; - } - } - return -EINVAL; -} - inline void viu_activate_next_buf(struct viu_dev *dev, struct viu_dmaqueue *viuq) { @@ -1265,7 +1179,6 @@ static int viu_open(struct file *file) struct viu_reg *vr; int minor = vdev->minor; u32 status_cfg; - int i; dprintk(1, "viu: open (minor=%d)\n", minor); @@ -1303,10 +1216,6 @@ static int viu_open(struct file *file) dev->crop_current.width = fh->width; dev->crop_current.height = fh->height; - /* Put all controls at a sane state */ - for (i = 0; i < ARRAY_SIZE(viu_qctrl); i++) - qctl_regs[i] = viu_qctrl[i].default_value; - dprintk(1, "Open: fh=0x%08lx, dev=0x%08lx, dev->vidq=0x%08lx\n", (unsigned long)fh, (unsigned long)dev, (unsigned long)&dev->vidq); @@ -1463,9 +1372,6 @@ static const struct v4l2_ioctl_ops viu_ioctl_ops = { .vidioc_enum_input = vidioc_enum_input, .vidioc_g_input = vidioc_g_input, .vidioc_s_input = vidioc_s_input, - .vidioc_queryctrl = vidioc_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, .vidioc_streamon = vidioc_streamon, .vidioc_streamoff = vidioc_streamoff, }; @@ -1543,6 +1449,16 @@ static int viu_of_probe(struct platform_device *op) } ad = i2c_get_adapter(0); + + v4l2_ctrl_handler_init(&viu_dev->hdl, 5); + if (viu_dev->hdl.error) { + ret = viu_dev->hdl.error; + dev_err(&op->dev, "couldn't register control\n"); + goto err_vdev; + } + /* This control handler will inherit the control(s) from the + sub-device(s). */ + viu_dev->v4l2_dev.ctrl_handler = &viu_dev->hdl; viu_dev->decoder = v4l2_i2c_new_subdev(&viu_dev->v4l2_dev, ad, "saa7113", VIU_VIDEO_DECODER_ADDR, NULL); @@ -1614,6 +1530,7 @@ err_irq: err_clk: video_unregister_device(viu_dev->vdev); err_vdev: + v4l2_ctrl_handler_free(&viu_dev->hdl); mutex_unlock(&viu_dev->lock); i2c_put_adapter(ad); v4l2_device_unregister(&viu_dev->v4l2_dev); @@ -1635,6 +1552,7 @@ static int viu_of_remove(struct platform_device *op) clk_disable_unprepare(dev->clk); + v4l2_ctrl_handler_free(&dev->hdl); video_unregister_device(dev->vdev); i2c_put_adapter(client->adapter); v4l2_device_unregister(&dev->v4l2_dev); -- cgit v1.2.3 From 282b3fb831d64b38cac67a912d99ca3dd547aac9 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 20 Jul 2015 10:09:29 -0300 Subject: [media] fsl-viu: fill in bus_info in vidioc_querycap The bus_info field was never filled. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/fsl-viu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/platform/fsl-viu.c b/drivers/media/platform/fsl-viu.c index f0ec55134b45..ab8012c7635e 100644 --- a/drivers/media/platform/fsl-viu.c +++ b/drivers/media/platform/fsl-viu.c @@ -563,6 +563,7 @@ static int vidioc_querycap(struct file *file, void *priv, { strcpy(cap->driver, "viu"); strcpy(cap->card, "viu"); + strcpy(cap->bus_info, "platform:viu"); cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_OVERLAY | -- cgit v1.2.3 From e299bc99bfa7188d3b52c9aed28e0882f5abd137 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 20 Jul 2015 10:09:30 -0300 Subject: [media] fsl-viu: fill in colorspace, always set field to interlaced - fill in the missing colorspace value. - don't reject incorrect field values, always replace with a valid value. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/fsl-viu.c | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/drivers/media/platform/fsl-viu.c b/drivers/media/platform/fsl-viu.c index ab8012c7635e..7d0e360e58f1 100644 --- a/drivers/media/platform/fsl-viu.c +++ b/drivers/media/platform/fsl-viu.c @@ -597,6 +597,7 @@ static int vidioc_g_fmt_cap(struct file *file, void *priv, f->fmt.pix.bytesperline = (f->fmt.pix.width * fh->fmt->depth) >> 3; f->fmt.pix.sizeimage = fh->sizeimage; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; return 0; } @@ -604,7 +605,6 @@ static int vidioc_try_fmt_cap(struct file *file, void *priv, struct v4l2_format *f) { struct viu_fmt *fmt; - enum v4l2_field field; unsigned int maxw, maxh; fmt = format_by_fourcc(f->fmt.pix.pixelformat); @@ -614,19 +614,10 @@ static int vidioc_try_fmt_cap(struct file *file, void *priv, return -EINVAL; } - field = f->fmt.pix.field; - - if (field == V4L2_FIELD_ANY) { - field = V4L2_FIELD_INTERLACED; - } else if (field != V4L2_FIELD_INTERLACED) { - dprintk(1, "Field type invalid.\n"); - return -EINVAL; - } - maxw = norm_maxw(); maxh = norm_maxh(); - f->fmt.pix.field = field; + f->fmt.pix.field = V4L2_FIELD_INTERLACED; if (f->fmt.pix.height < 32) f->fmt.pix.height = 32; if (f->fmt.pix.height > maxh) @@ -638,6 +629,7 @@ static int vidioc_try_fmt_cap(struct file *file, void *priv, f->fmt.pix.width &= ~0x03; f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; return 0; } -- cgit v1.2.3 From 7fe0b3d7c721ddb5a37e868ad27c2476b4042a54 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 20 Jul 2015 10:09:31 -0300 Subject: [media] fsl-viu: add control event support Convert the driver to use v4l2_fh in order to support control events. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/fsl-viu.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/fsl-viu.c b/drivers/media/platform/fsl-viu.c index 7d0e360e58f1..906442ecd518 100644 --- a/drivers/media/platform/fsl-viu.c +++ b/drivers/media/platform/fsl-viu.c @@ -29,6 +29,8 @@ #include #include #include +#include +#include #include #define DRV_NAME "fsl_viu" @@ -154,6 +156,8 @@ struct viu_dev { }; struct viu_fh { + /* must remain the first field of this struct */ + struct v4l2_fh fh; struct viu_dev *dev; /* video capture */ @@ -1199,6 +1203,7 @@ static int viu_open(struct file *file) return -ENOMEM; } + v4l2_fh_init(&fh->fh, vdev); file->private_data = fh; fh->dev = dev; @@ -1234,6 +1239,7 @@ static int viu_open(struct file *file) fh->type, V4L2_FIELD_INTERLACED, sizeof(struct viu_buf), fh, &fh->dev->lock); + v4l2_fh_add(&fh->fh); mutex_unlock(&dev->lock); return 0; } @@ -1266,13 +1272,17 @@ static unsigned int viu_poll(struct file *file, struct poll_table_struct *wait) struct viu_fh *fh = file->private_data; struct videobuf_queue *q = &fh->vb_vidq; struct viu_dev *dev = fh->dev; - unsigned int res; + unsigned long req_events = poll_requested_events(wait); + unsigned int res = v4l2_ctrl_poll(file, wait); if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type) return POLLERR; + if (!(req_events & (POLLIN | POLLRDNORM))) + return res; + mutex_lock(&dev->lock); - res = videobuf_poll_stream(file, q, wait); + res |= videobuf_poll_stream(file, q, wait); mutex_unlock(&dev->lock); return res; } @@ -1287,6 +1297,8 @@ static int viu_release(struct file *file) viu_stop_dma(dev); videobuf_stop(&fh->vb_vidq); videobuf_mmap_free(&fh->vb_vidq); + v4l2_fh_del(&fh->fh); + v4l2_fh_exit(&fh->fh); mutex_unlock(&dev->lock); kfree(fh); @@ -1367,6 +1379,9 @@ static const struct v4l2_ioctl_ops viu_ioctl_ops = { .vidioc_s_input = vidioc_s_input, .vidioc_streamon = vidioc_streamon, .vidioc_streamoff = vidioc_streamoff, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; static struct video_device viu_template = { @@ -1468,7 +1483,7 @@ static int viu_of_probe(struct platform_device *op) goto err_vdev; } - memcpy(vdev, &viu_template, sizeof(viu_template)); + *vdev = viu_template; vdev->v4l2_dev = &viu_dev->v4l2_dev; -- cgit v1.2.3 From 9acc8093282456f920f7d3e34b0b1b58c3f07dbd Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 20 Jul 2015 10:09:32 -0300 Subject: [media] fsl-viu: small fixes - Fix an off-by-one index check in vidioc_enum_fmt() - Fill in the pix.sizeimage field in vidioc_try_fmt_cap() - Fix an off-by-one index check in vidioc_s_input() Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/fsl-viu.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/fsl-viu.c b/drivers/media/platform/fsl-viu.c index 906442ecd518..69ee2b6c2ffc 100644 --- a/drivers/media/platform/fsl-viu.c +++ b/drivers/media/platform/fsl-viu.c @@ -581,7 +581,7 @@ static int vidioc_enum_fmt(struct file *file, void *priv, { int index = f->index; - if (f->index > NUM_FORMATS) + if (f->index >= NUM_FORMATS) return -EINVAL; strlcpy(f->description, formats[index].name, sizeof(f->description)); @@ -633,6 +633,7 @@ static int vidioc_try_fmt_cap(struct file *file, void *priv, f->fmt.pix.width &= ~0x03; f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; return 0; @@ -958,7 +959,7 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i) { struct viu_fh *fh = priv; - if (i > 1) + if (i) return -EINVAL; decoder_call(fh->dev, video, s_routing, i, 0, 0); -- cgit v1.2.3 From 0a6b9b04dbe81d781720e077aea147805af0ee87 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 20 Jul 2015 10:09:33 -0300 Subject: [media] fsl-viu: drop format names The names of the pixelformats is set by the core. So there no longer is any need for drivers to fill it in. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/fsl-viu.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/media/platform/fsl-viu.c b/drivers/media/platform/fsl-viu.c index 69ee2b6c2ffc..ae8c6b35a357 100644 --- a/drivers/media/platform/fsl-viu.c +++ b/drivers/media/platform/fsl-viu.c @@ -55,7 +55,6 @@ static int info_level; * Basic structures */ struct viu_fmt { - char name[32]; u32 fourcc; /* v4l2 format id */ u32 pixelformat; int depth; @@ -63,12 +62,10 @@ struct viu_fmt { static struct viu_fmt formats[] = { { - .name = "RGB-16 (5/B-6/G-5/R)", .fourcc = V4L2_PIX_FMT_RGB565, .pixelformat = V4L2_PIX_FMT_RGB565, .depth = 16, }, { - .name = "RGB-32 (A-R-G-B)", .fourcc = V4L2_PIX_FMT_RGB32, .pixelformat = V4L2_PIX_FMT_RGB32, .depth = 32, @@ -584,7 +581,6 @@ static int vidioc_enum_fmt(struct file *file, void *priv, if (f->index >= NUM_FORMATS) return -EINVAL; - strlcpy(f->description, formats[index].name, sizeof(f->description)); f->pixelformat = formats[index].fourcc; return 0; } @@ -655,7 +651,6 @@ static int vidioc_s_fmt_cap(struct file *file, void *priv, fh->sizeimage = f->fmt.pix.sizeimage; fh->vb_vidq.field = f->fmt.pix.field; fh->type = f->type; - dprintk(1, "set to pixelformat '%4.6s'\n", (char *)&fh->fmt->name); return 0; } @@ -721,8 +716,8 @@ static int viu_setup_preview(struct viu_dev *dev, struct viu_fh *fh) { int bpp; - dprintk(1, "%s %dx%d %s\n", __func__, - fh->win.w.width, fh->win.w.height, dev->ovfmt->name); + dprintk(1, "%s %dx%d\n", __func__, + fh->win.w.width, fh->win.w.height); reg_val.status_cfg = 0; -- cgit v1.2.3 From 160d75724ae7db78112bbdef447f1a7b0f40bdf4 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 20 Jul 2015 10:18:18 -0300 Subject: [media] zoran: remove unnecessary memset There is no need to zero the v4l2_capability struct, the v4l2 core has done that already. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/zoran/zoran_driver.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/media/pci/zoran/zoran_driver.c b/drivers/media/pci/zoran/zoran_driver.c index 2b25d31c46f6..25cd18b15d31 100644 --- a/drivers/media/pci/zoran/zoran_driver.c +++ b/drivers/media/pci/zoran/zoran_driver.c @@ -1523,7 +1523,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", -- cgit v1.2.3 From 8148802f186bc09b7d61f9be5cb8d287928c8b50 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 20 Jul 2015 10:18:19 -0300 Subject: [media] zoran: remove unused read/write functions The zoran_read/write functions always return an error. Just remove them. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/zoran/zoran_driver.c | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/drivers/media/pci/zoran/zoran_driver.c b/drivers/media/pci/zoran/zoran_driver.c index 25cd18b15d31..3d3a0c0c72cf 100644 --- a/drivers/media/pci/zoran/zoran_driver.c +++ b/drivers/media/pci/zoran/zoran_driver.c @@ -1032,29 +1032,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, @@ -3052,8 +3029,6 @@ static const struct v4l2_file_operations zoran_fops = { .open = zoran_open, .release = zoran_close, .unlocked_ioctl = zoran_ioctl, - .read = zoran_read, - .write = zoran_write, .mmap = zoran_mmap, .poll = zoran_poll, }; -- cgit v1.2.3 From 7b962d43ef875f3a524e92ca944816b862470e21 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 20 Jul 2015 10:18:20 -0300 Subject: [media] zoran: use standard core lock Use the standard core lock to take care of serializing ioctl calls and to serialize file operations. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/zoran/zoran.h | 3 +- drivers/media/pci/zoran/zoran_card.c | 6 +- drivers/media/pci/zoran/zoran_driver.c | 249 ++++++++------------------------- 3 files changed, 60 insertions(+), 198 deletions(-) diff --git a/drivers/media/pci/zoran/zoran.h b/drivers/media/pci/zoran/zoran.h index 5e040085c2ff..4109775e4c74 100644 --- a/drivers/media/pci/zoran/zoran.h +++ b/drivers/media/pci/zoran/zoran.h @@ -280,8 +280,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..afeca42da174 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 @@ -1220,8 +1221,7 @@ static int zoran_probe(struct pci_dev *pdev, const struct pci_device_id *ent) zr->id = nr; snprintf(ZR_DEVNAME(zr), sizeof(ZR_DEVNAME(zr)), "MJPEG[%u]", zr->id); 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; diff --git a/drivers/media/pci/zoran/zoran_driver.c b/drivers/media/pci/zoran/zoran_driver.c index 3d3a0c0c72cf..cbcb0a02cdcc 100644 --- a/drivers/media/pci/zoran/zoran_driver.c +++ b/drivers/media/pci/zoran/zoran_driver.c @@ -592,10 +592,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 +787,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 +798,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 +807,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 +918,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", @@ -946,8 +953,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 +966,14 @@ static int zoran_open(struct file *file) file->private_data = fh; fh->zr = zr; zoran_open_init_session(fh); - mutex_unlock(&zr->other_lock); + 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 +992,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 +1026,8 @@ 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; kfree(fh->overlay_mask); kfree(fh); @@ -1559,9 +1563,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 / @@ -1577,7 +1578,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; } @@ -1590,7 +1590,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 * @@ -1602,7 +1601,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; } @@ -1612,8 +1610,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; @@ -1623,7 +1619,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; } @@ -1633,8 +1628,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) @@ -1644,7 +1637,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; } @@ -1659,7 +1651,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 */ @@ -1694,7 +1685,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; @@ -1710,8 +1701,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; } @@ -1726,23 +1715,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; } @@ -1750,7 +1733,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", @@ -1759,12 +1741,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; } @@ -1784,13 +1764,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; @@ -1827,7 +1805,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; @@ -1848,9 +1826,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; } @@ -1874,14 +1849,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; @@ -1893,7 +1866,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; @@ -1903,9 +1876,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; } @@ -1916,14 +1886,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; @@ -1949,10 +1917,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; } @@ -1960,12 +1926,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; } @@ -1989,14 +1952,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 && @@ -2013,7 +1975,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) { @@ -2030,7 +1992,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, @@ -2038,23 +2000,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; } @@ -2065,8 +2021,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) { @@ -2074,12 +2028,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; @@ -2099,12 +2053,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); @@ -2118,9 +2072,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; } @@ -2130,8 +2081,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) { @@ -2139,18 +2088,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; @@ -2170,7 +2119,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]; @@ -2178,12 +2127,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; } @@ -2195,9 +2144,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; } @@ -2207,14 +2153,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; @@ -2233,7 +2177,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; @@ -2252,9 +2196,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; } @@ -2265,17 +2206,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 */ @@ -2303,17 +2242,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 @@ -2322,9 +2261,6 @@ 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; } @@ -2354,9 +2290,7 @@ static int zoran_g_ctrl(struct file *file, void *__fh, struct v4l2_control *ctrl 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; } @@ -2371,9 +2305,7 @@ static int zoran_s_ctrl(struct file *file, void *__fh, struct v4l2_control *ctrl 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; } @@ -2383,9 +2315,7 @@ 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; } @@ -2395,14 +2325,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; } @@ -2421,9 +2348,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; } @@ -2432,9 +2357,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; } @@ -2445,15 +2368,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; } @@ -2496,8 +2416,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)) { @@ -2505,7 +2423,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; @@ -2514,8 +2432,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; } @@ -2528,8 +2444,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)) { @@ -2538,17 +2452,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; } @@ -2561,14 +2471,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 && @@ -2578,7 +2486,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 */ @@ -2590,13 +2498,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; } @@ -2604,11 +2509,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, @@ -2622,8 +2524,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; } @@ -2639,26 +2539,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; } @@ -2679,8 +2574,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); @@ -2735,8 +2628,6 @@ zoran_poll (struct file *file, res = POLLNVAL; } - mutex_unlock(&zr->resource_lock); - return res; } @@ -2768,9 +2659,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)); @@ -2783,7 +2671,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; } } @@ -2791,7 +2678,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; @@ -2811,8 +2697,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 = { @@ -2848,15 +2732,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; @@ -2872,7 +2754,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 */ @@ -2883,7 +2765,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; } } @@ -2891,7 +2773,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); @@ -2913,7 +2795,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; @@ -2945,7 +2827,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; @@ -2961,10 +2843,6 @@ zoran_mmap (struct file *file, } } - -mmap_unlock_and_return: - mutex_unlock(&zr->resource_lock); - return res; } @@ -3009,26 +2887,11 @@ static const struct v4l2_ioctl_ops zoran_ioctl_ops = { .vidioc_g_ctrl = zoran_g_ctrl, }; -/* 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, + .unlocked_ioctl = video_ioctl2, .mmap = zoran_mmap, .poll = zoran_poll, }; -- cgit v1.2.3 From e56c597b027ba95a9ce6719462793da4a91d27d2 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 20 Jul 2015 10:18:21 -0300 Subject: [media] zoran: convert to the control framework and to v4l2_fh Switch this driver to the control framework and to v4l2_fh for handling control events. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/zoran/zoran.h | 4 ++ drivers/media/pci/zoran/zoran_card.c | 5 +++ drivers/media/pci/zoran/zoran_driver.c | 69 +++++++--------------------------- 3 files changed, 23 insertions(+), 55 deletions(-) diff --git a/drivers/media/pci/zoran/zoran.h b/drivers/media/pci/zoran/zoran.h index 4109775e4c74..4e7db8939c2b 100644 --- a/drivers/media/pci/zoran/zoran.h +++ b/drivers/media/pci/zoran/zoran.h @@ -32,6 +32,8 @@ #define _BUZ_H_ #include +#include +#include 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; /* */ diff --git a/drivers/media/pci/zoran/zoran_card.c b/drivers/media/pci/zoran/zoran_card.c index afeca42da174..1136d92af642 100644 --- a/drivers/media/pci/zoran/zoran_card.c +++ b/drivers/media/pci/zoran/zoran_card.c @@ -1117,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); } @@ -1220,6 +1221,9 @@ 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->lock); if (pci_enable_device(pdev)) @@ -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_driver.c b/drivers/media/pci/zoran/zoran_driver.c index cbcb0a02cdcc..80caa70c6360 100644 --- a/drivers/media/pci/zoran/zoran_driver.c +++ b/drivers/media/pci/zoran/zoran_driver.c @@ -61,6 +61,7 @@ #include #include #include +#include #include "videocodec.h" #include @@ -937,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 = @@ -966,6 +969,7 @@ static int zoran_open(struct file *file) file->private_data = fh; fh->zr = zr; zoran_open_init_session(fh); + v4l2_fh_add(&fh->fh); mutex_unlock(&zr->lock); return 0; @@ -1028,6 +1032,8 @@ zoran_close(struct file *file) } mutex_unlock(&zr->lock); + v4l2_fh_del(&fh->fh); + v4l2_fh_exit(&fh->fh); kfree(fh->overlay_mask); kfree(fh); @@ -2263,53 +2269,6 @@ static int zoran_streamoff(struct file *file, void *__fh, enum v4l2_buf_type typ } 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; - - decoder_call(zr, core, g_ctrl, ctrl); - - 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; - - decoder_call(zr, core, s_ctrl, ctrl); - - return 0; -} - static int zoran_g_std(struct file *file, void *__fh, v4l2_std_id *std) { struct zoran_fh *fh = __fh; @@ -2563,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 @@ -2591,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; @@ -2612,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); @@ -2625,7 +2585,7 @@ 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; } return res; @@ -2882,9 +2842,8 @@ 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, }; static const struct v4l2_file_operations zoran_fops = { -- cgit v1.2.3 From 8d59604547c45332062bde1e25364a9820145fb1 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 20 Jul 2015 10:18:22 -0300 Subject: [media] bt819/saa7110/vpx3220: remove legacy control ops The zoran driver has now been converted to the control framework which means that these three subdevice drivers no longer need to support the legacy core control ops since the last bridge driver that needed that has now been converted. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/bt819.c | 11 ----------- drivers/media/i2c/saa7110.c | 11 ----------- drivers/media/i2c/vpx3220.c | 7 ------- 3 files changed, 29 deletions(-) diff --git a/drivers/media/i2c/bt819.c b/drivers/media/i2c/bt819.c index 76b334a6a56d..9b5187b56fe4 100644 --- a/drivers/media/i2c/bt819.c +++ b/drivers/media/i2c/bt819.c @@ -379,16 +379,6 @@ static const struct v4l2_ctrl_ops bt819_ctrl_ops = { .s_ctrl = bt819_s_ctrl, }; -static const struct v4l2_subdev_core_ops bt819_core_ops = { - .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_video_ops bt819_video_ops = { .s_std = bt819_s_std, .s_routing = bt819_s_routing, @@ -398,7 +388,6 @@ static const struct v4l2_subdev_video_ops bt819_video_ops = { }; static const struct v4l2_subdev_ops bt819_ops = { - .core = &bt819_core_ops, .video = &bt819_video_ops, }; diff --git a/drivers/media/i2c/saa7110.c b/drivers/media/i2c/saa7110.c index 99689ee57d7e..41fcaed8b326 100644 --- a/drivers/media/i2c/saa7110.c +++ b/drivers/media/i2c/saa7110.c @@ -357,16 +357,6 @@ static const struct v4l2_ctrl_ops saa7110_ctrl_ops = { .s_ctrl = saa7110_s_ctrl, }; -static const struct v4l2_subdev_core_ops saa7110_core_ops = { - .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_video_ops saa7110_video_ops = { .s_std = saa7110_s_std, .s_routing = saa7110_s_routing, @@ -376,7 +366,6 @@ static const struct v4l2_subdev_video_ops saa7110_video_ops = { }; static const struct v4l2_subdev_ops saa7110_ops = { - .core = &saa7110_core_ops, .video = &saa7110_video_ops, }; diff --git a/drivers/media/i2c/vpx3220.c b/drivers/media/i2c/vpx3220.c index 016e766e72ba..c060551a5b77 100644 --- a/drivers/media/i2c/vpx3220.c +++ b/drivers/media/i2c/vpx3220.c @@ -450,13 +450,6 @@ static const struct v4l2_ctrl_ops vpx3220_ctrl_ops = { static const struct v4l2_subdev_core_ops vpx3220_core_ops = { .init = vpx3220_init, - .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_video_ops vpx3220_video_ops = { -- cgit v1.2.3 From d029419dc6176ee860ef73baceec92de3efc4704 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Thu, 16 Apr 2015 22:55:31 -0300 Subject: [media] em28xx: remove unused a8293 SEC config Devices that were using a8293 SEC are converted to I2C platform data thus that old config structure is left unused. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-dvb.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index a38248360833..357be76c7a55 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -808,10 +808,6 @@ static struct tda18271_config em28xx_cxd2820r_tda18271_config = { .gate = TDA18271_GATE_DIGITAL, }; -static const struct a8293_config em28xx_a8293_config = { - .i2c_addr = 0x08, /* (0x10 >> 1) */ -}; - static struct zl10353_config em28xx_zl10353_no_i2c_gate_dev = { .demod_address = (0x1e >> 1), .disable_i2c_gate_ctrl = 1, -- cgit v1.2.3 From 55881b4fb50976a2956e6de3001bbe0a37e8be9a Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Mon, 20 Apr 2015 17:39:33 -0300 Subject: [media] a8293: remove legacy media attach Remove legacy media attach as all users are on I2C bindings now. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/a8293.c | 63 +------------------------------------ drivers/media/dvb-frontends/a8293.h | 18 ----------- 2 files changed, 1 insertion(+), 80 deletions(-) diff --git a/drivers/media/dvb-frontends/a8293.c b/drivers/media/dvb-frontends/a8293.c index 97ecbe01034c..522b0d125860 100644 --- a/drivers/media/dvb-frontends/a8293.c +++ b/drivers/media/dvb-frontends/a8293.c @@ -18,7 +18,6 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include "dvb_frontend.h" #include "a8293.h" struct a8293_priv { @@ -105,68 +104,8 @@ err: return ret; } -static void a8293_release_sec(struct dvb_frontend *fe) -{ - a8293_set_voltage(fe, SEC_VOLTAGE_OFF); - - kfree(fe->sec_priv); - fe->sec_priv = NULL; -} - -struct dvb_frontend *a8293_attach(struct dvb_frontend *fe, - struct i2c_adapter *i2c, const struct a8293_config *cfg) -{ - int ret; - struct a8293_priv *priv = NULL; - u8 buf[2]; - - /* allocate memory for the internal priv */ - priv = kzalloc(sizeof(struct a8293_priv), GFP_KERNEL); - if (priv == NULL) { - ret = -ENOMEM; - goto err; - } - - /* setup the priv */ - priv->i2c = i2c; - priv->i2c_addr = cfg->i2c_addr; - fe->sec_priv = priv; - - /* check if the SEC is there */ - ret = a8293_rd(priv, buf, 2); - if (ret) - goto err; - - /* ENB=0 */ - priv->reg[0] = 0x10; - ret = a8293_wr(priv, &priv->reg[0], 1); - if (ret) - goto err; - - /* TMODE=0, TGATE=1 */ - priv->reg[1] = 0x82; - ret = a8293_wr(priv, &priv->reg[1], 1); - if (ret) - goto err; - - fe->ops.release_sec = a8293_release_sec; - - /* override frontend ops */ - fe->ops.set_voltage = a8293_set_voltage; - - dev_info(&priv->i2c->dev, "%s: Allegro A8293 SEC attached\n", - KBUILD_MODNAME); - - return fe; -err: - dev_dbg(&i2c->dev, "%s: failed=%d\n", __func__, ret); - kfree(priv); - return NULL; -} -EXPORT_SYMBOL(a8293_attach); - static int a8293_probe(struct i2c_client *client, - const struct i2c_device_id *id) + const struct i2c_device_id *id) { struct a8293_priv *dev; struct a8293_platform_data *pdata = client->dev.platform_data; diff --git a/drivers/media/dvb-frontends/a8293.h b/drivers/media/dvb-frontends/a8293.h index aff36538f582..aa07b68d19e0 100644 --- a/drivers/media/dvb-frontends/a8293.h +++ b/drivers/media/dvb-frontends/a8293.h @@ -22,7 +22,6 @@ #define A8293_H #include "dvb_frontend.h" -#include /* * I2C address @@ -37,21 +36,4 @@ struct a8293_platform_data { struct dvb_frontend *dvb_frontend; }; - -struct a8293_config { - u8 i2c_addr; -}; - -#if IS_REACHABLE(CONFIG_DVB_A8293) -extern struct dvb_frontend *a8293_attach(struct dvb_frontend *fe, - struct i2c_adapter *i2c, const struct a8293_config *cfg); -#else -static inline struct dvb_frontend *a8293_attach(struct dvb_frontend *fe, - struct i2c_adapter *i2c, const struct a8293_config *cfg) -{ - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); - return NULL; -} -#endif - #endif /* A8293_H */ -- cgit v1.2.3 From 2c509b866bd299ab83cf1a4fb478055154845c8a Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Mon, 20 Apr 2015 18:16:19 -0300 Subject: [media] a8293: use i2c_master_send / i2c_master_recv for I2C I/O As driver is now proper I2C client driver, we could use correct functions for I2C I/O. Also rename state from priv to dev. Fix logging too. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/a8293.c | 80 +++++++++---------------------------- 1 file changed, 18 insertions(+), 62 deletions(-) diff --git a/drivers/media/dvb-frontends/a8293.c b/drivers/media/dvb-frontends/a8293.c index 522b0d125860..d99ea4d61315 100644 --- a/drivers/media/dvb-frontends/a8293.c +++ b/drivers/media/dvb-frontends/a8293.c @@ -20,94 +20,53 @@ #include "a8293.h" -struct a8293_priv { - u8 i2c_addr; - struct i2c_adapter *i2c; +struct a8293_dev { struct i2c_client *client; u8 reg[2]; }; -static int a8293_i2c(struct a8293_priv *priv, u8 *val, int len, bool rd) -{ - int ret; - struct i2c_msg msg[1] = { - { - .addr = priv->i2c_addr, - .len = len, - .buf = val, - } - }; - - if (rd) - msg[0].flags = I2C_M_RD; - else - msg[0].flags = 0; - - ret = i2c_transfer(priv->i2c, msg, 1); - if (ret == 1) { - ret = 0; - } else { - dev_warn(&priv->i2c->dev, "%s: i2c failed=%d rd=%d\n", - KBUILD_MODNAME, ret, rd); - ret = -EREMOTEIO; - } - - return ret; -} - -static int a8293_wr(struct a8293_priv *priv, u8 *val, int len) -{ - return a8293_i2c(priv, val, len, 0); -} - -static int a8293_rd(struct a8293_priv *priv, u8 *val, int len) -{ - return a8293_i2c(priv, val, len, 1); -} - static int a8293_set_voltage(struct dvb_frontend *fe, enum fe_sec_voltage fe_sec_voltage) { - struct a8293_priv *priv = fe->sec_priv; + struct a8293_dev *dev = fe->sec_priv; + struct i2c_client *client = dev->client; int ret; - dev_dbg(&priv->i2c->dev, "%s: fe_sec_voltage=%d\n", __func__, - fe_sec_voltage); + dev_dbg(&client->dev, "fe_sec_voltage=%d\n", fe_sec_voltage); switch (fe_sec_voltage) { case SEC_VOLTAGE_OFF: /* ENB=0 */ - priv->reg[0] = 0x10; + dev->reg[0] = 0x10; break; case SEC_VOLTAGE_13: /* VSEL0=1, VSEL1=0, VSEL2=0, VSEL3=0, ENB=1*/ - priv->reg[0] = 0x31; + dev->reg[0] = 0x31; break; case SEC_VOLTAGE_18: /* VSEL0=0, VSEL1=0, VSEL2=0, VSEL3=1, ENB=1*/ - priv->reg[0] = 0x38; + dev->reg[0] = 0x38; break; default: ret = -EINVAL; goto err; } - ret = a8293_wr(priv, &priv->reg[0], 1); - if (ret) + ret = i2c_master_send(client, &dev->reg[0], 1); + if (ret < 0) goto err; usleep_range(1500, 50000); - - return ret; + return 0; err: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } static int a8293_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct a8293_priv *dev; + struct a8293_dev *dev; struct a8293_platform_data *pdata = client->dev.platform_data; struct dvb_frontend *fe = pdata->dvb_frontend; int ret; @@ -120,29 +79,26 @@ static int a8293_probe(struct i2c_client *client, } dev->client = client; - dev->i2c = client->adapter; - dev->i2c_addr = client->addr; /* check if the SEC is there */ - ret = a8293_rd(dev, buf, 2); - if (ret) + ret = i2c_master_recv(client, buf, 2); + if (ret < 0) goto err_kfree; /* ENB=0 */ dev->reg[0] = 0x10; - ret = a8293_wr(dev, &dev->reg[0], 1); - if (ret) + ret = i2c_master_send(client, &dev->reg[0], 1); + if (ret < 0) goto err_kfree; /* TMODE=0, TGATE=1 */ dev->reg[1] = 0x82; - ret = a8293_wr(dev, &dev->reg[1], 1); - if (ret) + ret = i2c_master_send(client, &dev->reg[1], 1); + if (ret < 0) goto err_kfree; /* override frontend ops */ fe->ops.set_voltage = a8293_set_voltage; - fe->sec_priv = dev; i2c_set_clientdata(client, dev); -- cgit v1.2.3 From 4bef67e311d6f9908eeae054d5b8611138956abc Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Mon, 20 Apr 2015 18:47:44 -0300 Subject: [media] a8293: improve LNB register programming logic On power-on LNB power supply voltage is disabled, due to that no need to disable it during probe. Tone is supply is hard-coded as external tone coming from the demodulator. Program both voltage and tone on set_voltage(). Use register cache to prevent unneeded programming. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/a8293.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/drivers/media/dvb-frontends/a8293.c b/drivers/media/dvb-frontends/a8293.c index d99ea4d61315..ef2b47c4c17b 100644 --- a/drivers/media/dvb-frontends/a8293.c +++ b/drivers/media/dvb-frontends/a8293.c @@ -31,30 +31,42 @@ static int a8293_set_voltage(struct dvb_frontend *fe, struct a8293_dev *dev = fe->sec_priv; struct i2c_client *client = dev->client; int ret; + u8 reg0, reg1; dev_dbg(&client->dev, "fe_sec_voltage=%d\n", fe_sec_voltage); switch (fe_sec_voltage) { case SEC_VOLTAGE_OFF: /* ENB=0 */ - dev->reg[0] = 0x10; + reg0 = 0x10; break; case SEC_VOLTAGE_13: /* VSEL0=1, VSEL1=0, VSEL2=0, VSEL3=0, ENB=1*/ - dev->reg[0] = 0x31; + reg0 = 0x31; break; case SEC_VOLTAGE_18: /* VSEL0=0, VSEL1=0, VSEL2=0, VSEL3=1, ENB=1*/ - dev->reg[0] = 0x38; + reg0 = 0x38; break; default: ret = -EINVAL; goto err; } + if (reg0 != dev->reg[0]) { + ret = i2c_master_send(client, ®0, 1); + if (ret < 0) + goto err; + dev->reg[0] = reg0; + } - ret = i2c_master_send(client, &dev->reg[0], 1); - if (ret < 0) - goto err; + /* TMODE=0, TGATE=1 */ + reg1 = 0x82; + if (reg1 != dev->reg[1]) { + ret = i2c_master_send(client, ®1, 1); + if (ret < 0) + goto err; + dev->reg[1] = reg1; + } usleep_range(1500, 50000); return 0; @@ -85,18 +97,6 @@ static int a8293_probe(struct i2c_client *client, if (ret < 0) goto err_kfree; - /* ENB=0 */ - dev->reg[0] = 0x10; - ret = i2c_master_send(client, &dev->reg[0], 1); - if (ret < 0) - goto err_kfree; - - /* TMODE=0, TGATE=1 */ - dev->reg[1] = 0x82; - ret = i2c_master_send(client, &dev->reg[1], 1); - if (ret < 0) - goto err_kfree; - /* override frontend ops */ fe->ops.set_voltage = a8293_set_voltage; fe->sec_priv = dev; -- cgit v1.2.3 From 3250a550c48d075b01226f5561c21bc3a1e63dae Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Mon, 20 Apr 2015 18:57:03 -0300 Subject: [media] a8293: coding style issues Remove FSF address from license. Indent parameter correctly. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/a8293.c | 6 +----- drivers/media/dvb-frontends/a8293.h | 4 ---- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/drivers/media/dvb-frontends/a8293.c b/drivers/media/dvb-frontends/a8293.c index ef2b47c4c17b..192313391fa8 100644 --- a/drivers/media/dvb-frontends/a8293.c +++ b/drivers/media/dvb-frontends/a8293.c @@ -12,10 +12,6 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "a8293.h" @@ -26,7 +22,7 @@ struct a8293_dev { }; static int a8293_set_voltage(struct dvb_frontend *fe, - enum fe_sec_voltage fe_sec_voltage) + enum fe_sec_voltage fe_sec_voltage) { struct a8293_dev *dev = fe->sec_priv; struct i2c_client *client = dev->client; diff --git a/drivers/media/dvb-frontends/a8293.h b/drivers/media/dvb-frontends/a8293.h index aa07b68d19e0..7b90a03fcd0a 100644 --- a/drivers/media/dvb-frontends/a8293.h +++ b/drivers/media/dvb-frontends/a8293.h @@ -12,10 +12,6 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef A8293_H -- cgit v1.2.3 From 59ca2ce1d5c289e73173d91a139dd4c6cbeb75a2 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Mon, 20 Apr 2015 19:54:56 -0300 Subject: [media] tda10071: remove legacy media attach All users are now using I2C binding and old attach could be removed. Use I2C client for proper logging at the same. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/tda10071.c | 275 ++++++++++------------------ drivers/media/dvb-frontends/tda10071.h | 63 +------ drivers/media/dvb-frontends/tda10071_priv.h | 8 +- 3 files changed, 108 insertions(+), 238 deletions(-) diff --git a/drivers/media/dvb-frontends/tda10071.c b/drivers/media/dvb-frontends/tda10071.c index f6dc6307d35a..cc6528556dce 100644 --- a/drivers/media/dvb-frontends/tda10071.c +++ b/drivers/media/dvb-frontends/tda10071.c @@ -29,11 +29,12 @@ static struct dvb_frontend_ops tda10071_ops; static int tda10071_wr_regs(struct tda10071_priv *priv, u8 reg, u8 *val, int len) { + struct i2c_client *client = priv->client; int ret; u8 buf[MAX_XFER_SIZE]; struct i2c_msg msg[1] = { { - .addr = priv->cfg.demod_i2c_addr, + .addr = client->addr, .flags = 0, .len = 1 + len, .buf = buf, @@ -41,22 +42,20 @@ static int tda10071_wr_regs(struct tda10071_priv *priv, u8 reg, u8 *val, }; if (1 + len > sizeof(buf)) { - dev_warn(&priv->i2c->dev, - "%s: i2c wr reg=%04x: len=%d is too big!\n", - KBUILD_MODNAME, reg, len); + dev_warn(&client->dev, "i2c wr reg=%04x: len=%d is too big!\n", + reg, len); return -EINVAL; } buf[0] = reg; memcpy(&buf[1], val, len); - ret = i2c_transfer(priv->i2c, msg, 1); + ret = i2c_transfer(client->adapter, msg, 1); if (ret == 1) { ret = 0; } else { - dev_warn(&priv->i2c->dev, - "%s: i2c wr failed=%d reg=%02x len=%d\n", - KBUILD_MODNAME, ret, reg, len); + dev_warn(&client->dev, "i2c wr failed=%d reg=%02x len=%d\n", + ret, reg, len); ret = -EREMOTEIO; } return ret; @@ -66,16 +65,17 @@ static int tda10071_wr_regs(struct tda10071_priv *priv, u8 reg, u8 *val, static int tda10071_rd_regs(struct tda10071_priv *priv, u8 reg, u8 *val, int len) { + struct i2c_client *client = priv->client; int ret; u8 buf[MAX_XFER_SIZE]; struct i2c_msg msg[2] = { { - .addr = priv->cfg.demod_i2c_addr, + .addr = client->addr, .flags = 0, .len = 1, .buf = ®, }, { - .addr = priv->cfg.demod_i2c_addr, + .addr = client->addr, .flags = I2C_M_RD, .len = len, .buf = buf, @@ -83,20 +83,18 @@ static int tda10071_rd_regs(struct tda10071_priv *priv, u8 reg, u8 *val, }; if (len > sizeof(buf)) { - dev_warn(&priv->i2c->dev, - "%s: i2c wr reg=%04x: len=%d is too big!\n", - KBUILD_MODNAME, reg, len); + dev_warn(&client->dev, "i2c wr reg=%04x: len=%d is too big!\n", + reg, len); return -EINVAL; } - ret = i2c_transfer(priv->i2c, msg, 2); + ret = i2c_transfer(client->adapter, msg, 2); if (ret == 2) { memcpy(val, buf, len); ret = 0; } else { - dev_warn(&priv->i2c->dev, - "%s: i2c rd failed=%d reg=%02x len=%d\n", - KBUILD_MODNAME, ret, reg, len); + dev_warn(&client->dev, "i2c rd failed=%d reg=%02x len=%d\n", + ret, reg, len); ret = -EREMOTEIO; } return ret; @@ -162,6 +160,7 @@ static int tda10071_rd_reg_mask(struct tda10071_priv *priv, static int tda10071_cmd_execute(struct tda10071_priv *priv, struct tda10071_cmd *cmd) { + struct i2c_client *client = priv->client; int ret, i; u8 tmp; @@ -189,7 +188,7 @@ static int tda10071_cmd_execute(struct tda10071_priv *priv, usleep_range(200, 5000); } - dev_dbg(&priv->i2c->dev, "%s: loop=%d\n", __func__, i); + dev_dbg(&client->dev, "loop=%d\n", i); if (i == 0) { ret = -ETIMEDOUT; @@ -198,7 +197,7 @@ static int tda10071_cmd_execute(struct tda10071_priv *priv, return ret; error: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } @@ -206,6 +205,7 @@ static int tda10071_set_tone(struct dvb_frontend *fe, enum fe_sec_tone_mode fe_sec_tone_mode) { struct tda10071_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client; struct tda10071_cmd cmd; int ret; u8 tone; @@ -215,8 +215,7 @@ static int tda10071_set_tone(struct dvb_frontend *fe, goto error; } - dev_dbg(&priv->i2c->dev, "%s: tone_mode=%d\n", __func__, - fe_sec_tone_mode); + dev_dbg(&client->dev, "tone_mode=%d\n", fe_sec_tone_mode); switch (fe_sec_tone_mode) { case SEC_TONE_ON: @@ -226,8 +225,7 @@ static int tda10071_set_tone(struct dvb_frontend *fe, tone = 0; break; default: - dev_dbg(&priv->i2c->dev, "%s: invalid fe_sec_tone_mode\n", - __func__); + dev_dbg(&client->dev, "invalid fe_sec_tone_mode\n"); ret = -EINVAL; goto error; } @@ -244,7 +242,7 @@ static int tda10071_set_tone(struct dvb_frontend *fe, return ret; error: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } @@ -252,6 +250,7 @@ static int tda10071_set_voltage(struct dvb_frontend *fe, enum fe_sec_voltage fe_sec_voltage) { struct tda10071_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client; struct tda10071_cmd cmd; int ret; u8 voltage; @@ -261,7 +260,7 @@ static int tda10071_set_voltage(struct dvb_frontend *fe, goto error; } - dev_dbg(&priv->i2c->dev, "%s: voltage=%d\n", __func__, fe_sec_voltage); + dev_dbg(&client->dev, "voltage=%d\n", fe_sec_voltage); switch (fe_sec_voltage) { case SEC_VOLTAGE_13: @@ -274,8 +273,7 @@ static int tda10071_set_voltage(struct dvb_frontend *fe, voltage = 0; break; default: - dev_dbg(&priv->i2c->dev, "%s: invalid fe_sec_voltage\n", - __func__); + dev_dbg(&client->dev, "invalid fe_sec_voltage\n"); ret = -EINVAL; goto error; } @@ -290,7 +288,7 @@ static int tda10071_set_voltage(struct dvb_frontend *fe, return ret; error: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } @@ -298,6 +296,7 @@ static int tda10071_diseqc_send_master_cmd(struct dvb_frontend *fe, struct dvb_diseqc_master_cmd *diseqc_cmd) { struct tda10071_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client; struct tda10071_cmd cmd; int ret, i; u8 tmp; @@ -307,8 +306,7 @@ static int tda10071_diseqc_send_master_cmd(struct dvb_frontend *fe, goto error; } - dev_dbg(&priv->i2c->dev, "%s: msg_len=%d\n", __func__, - diseqc_cmd->msg_len); + dev_dbg(&client->dev, "msg_len=%d\n", diseqc_cmd->msg_len); if (diseqc_cmd->msg_len < 3 || diseqc_cmd->msg_len > 6) { ret = -EINVAL; @@ -324,7 +322,7 @@ static int tda10071_diseqc_send_master_cmd(struct dvb_frontend *fe, usleep_range(10000, 20000); } - dev_dbg(&priv->i2c->dev, "%s: loop=%d\n", __func__, i); + dev_dbg(&client->dev, "loop=%d\n", i); if (i == 0) { ret = -ETIMEDOUT; @@ -350,7 +348,7 @@ static int tda10071_diseqc_send_master_cmd(struct dvb_frontend *fe, return ret; error: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } @@ -358,6 +356,7 @@ static int tda10071_diseqc_recv_slave_reply(struct dvb_frontend *fe, struct dvb_diseqc_slave_reply *reply) { struct tda10071_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client; struct tda10071_cmd cmd; int ret, i; u8 tmp; @@ -367,7 +366,7 @@ static int tda10071_diseqc_recv_slave_reply(struct dvb_frontend *fe, goto error; } - dev_dbg(&priv->i2c->dev, "%s:\n", __func__); + dev_dbg(&client->dev, "\n"); /* wait LNB RX */ for (i = 500, tmp = 0; i && !tmp; i--) { @@ -378,7 +377,7 @@ static int tda10071_diseqc_recv_slave_reply(struct dvb_frontend *fe, usleep_range(10000, 20000); } - dev_dbg(&priv->i2c->dev, "%s: loop=%d\n", __func__, i); + dev_dbg(&client->dev, "loop=%d\n", i); if (i == 0) { ret = -ETIMEDOUT; @@ -408,7 +407,7 @@ static int tda10071_diseqc_recv_slave_reply(struct dvb_frontend *fe, return ret; error: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } @@ -416,6 +415,7 @@ static int tda10071_diseqc_send_burst(struct dvb_frontend *fe, enum fe_sec_mini_cmd fe_sec_mini_cmd) { struct tda10071_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client; struct tda10071_cmd cmd; int ret, i; u8 tmp, burst; @@ -425,8 +425,7 @@ static int tda10071_diseqc_send_burst(struct dvb_frontend *fe, goto error; } - dev_dbg(&priv->i2c->dev, "%s: fe_sec_mini_cmd=%d\n", __func__, - fe_sec_mini_cmd); + dev_dbg(&client->dev, "fe_sec_mini_cmd=%d\n", fe_sec_mini_cmd); switch (fe_sec_mini_cmd) { case SEC_MINI_A: @@ -436,8 +435,7 @@ static int tda10071_diseqc_send_burst(struct dvb_frontend *fe, burst = 1; break; default: - dev_dbg(&priv->i2c->dev, "%s: invalid fe_sec_mini_cmd\n", - __func__); + dev_dbg(&client->dev, "invalid fe_sec_mini_cmd\n"); ret = -EINVAL; goto error; } @@ -451,7 +449,7 @@ static int tda10071_diseqc_send_burst(struct dvb_frontend *fe, usleep_range(10000, 20000); } - dev_dbg(&priv->i2c->dev, "%s: loop=%d\n", __func__, i); + dev_dbg(&client->dev, "loop=%d\n", i); if (i == 0) { ret = -ETIMEDOUT; @@ -472,13 +470,14 @@ static int tda10071_diseqc_send_burst(struct dvb_frontend *fe, return ret; error: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } static int tda10071_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct tda10071_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client; int ret; u8 tmp; @@ -505,13 +504,14 @@ static int tda10071_read_status(struct dvb_frontend *fe, enum fe_status *status) return ret; error: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } static int tda10071_read_snr(struct dvb_frontend *fe, u16 *snr) { struct tda10071_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client; int ret; u8 buf[2]; @@ -530,13 +530,14 @@ static int tda10071_read_snr(struct dvb_frontend *fe, u16 *snr) return ret; error: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } static int tda10071_read_signal_strength(struct dvb_frontend *fe, u16 *strength) { struct tda10071_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client; struct tda10071_cmd cmd; int ret; u8 tmp; @@ -569,13 +570,14 @@ static int tda10071_read_signal_strength(struct dvb_frontend *fe, u16 *strength) return ret; error: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } static int tda10071_read_ber(struct dvb_frontend *fe, u32 *ber) { struct tda10071_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client; struct tda10071_cmd cmd; int ret, i, len; u8 tmp, reg, buf[8]; @@ -607,8 +609,7 @@ static int tda10071_read_ber(struct dvb_frontend *fe, u32 *ber) goto error; if (priv->meas_count[i] == tmp) { - dev_dbg(&priv->i2c->dev, "%s: meas not ready=%02x\n", __func__, - tmp); + dev_dbg(&client->dev, "meas not ready=%02x\n", tmp); *ber = priv->ber; return 0; } else { @@ -637,13 +638,14 @@ static int tda10071_read_ber(struct dvb_frontend *fe, u32 *ber) return ret; error: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } static int tda10071_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) { struct tda10071_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client; int ret = 0; if (!priv->warm || !(priv->fe_status & FE_HAS_LOCK)) { @@ -657,24 +659,24 @@ static int tda10071_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) return ret; error: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } static int tda10071_set_frontend(struct dvb_frontend *fe) { struct tda10071_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client; struct tda10071_cmd cmd; struct dtv_frontend_properties *c = &fe->dtv_property_cache; int ret, i; u8 mode, rolloff, pilot, inversion, div; enum fe_modulation modulation; - dev_dbg(&priv->i2c->dev, - "%s: delivery_system=%d modulation=%d frequency=%d symbol_rate=%d inversion=%d pilot=%d rolloff=%d\n", - __func__, c->delivery_system, c->modulation, - c->frequency, c->symbol_rate, c->inversion, c->pilot, - c->rolloff); + dev_dbg(&client->dev, + "delivery_system=%d modulation=%d frequency=%u symbol_rate=%d inversion=%d pilot=%d rolloff=%d\n", + c->delivery_system, c->modulation, c->frequency, c->symbol_rate, + c->inversion, c->pilot, c->rolloff); priv->delivery_system = SYS_UNDEFINED; @@ -696,7 +698,7 @@ static int tda10071_set_frontend(struct dvb_frontend *fe) inversion = 3; break; default: - dev_dbg(&priv->i2c->dev, "%s: invalid inversion\n", __func__); + dev_dbg(&client->dev, "invalid inversion\n"); ret = -EINVAL; goto error; } @@ -722,8 +724,7 @@ static int tda10071_set_frontend(struct dvb_frontend *fe) break; case ROLLOFF_AUTO: default: - dev_dbg(&priv->i2c->dev, "%s: invalid rolloff\n", - __func__); + dev_dbg(&client->dev, "invalid rolloff\n"); ret = -EINVAL; goto error; } @@ -739,15 +740,13 @@ static int tda10071_set_frontend(struct dvb_frontend *fe) pilot = 2; break; default: - dev_dbg(&priv->i2c->dev, "%s: invalid pilot\n", - __func__); + dev_dbg(&client->dev, "invalid pilot\n"); ret = -EINVAL; goto error; } break; default: - dev_dbg(&priv->i2c->dev, "%s: invalid delivery_system\n", - __func__); + dev_dbg(&client->dev, "invalid delivery_system\n"); ret = -EINVAL; goto error; } @@ -757,15 +756,13 @@ static int tda10071_set_frontend(struct dvb_frontend *fe) modulation == TDA10071_MODCOD[i].modulation && c->fec_inner == TDA10071_MODCOD[i].fec) { mode = TDA10071_MODCOD[i].val; - dev_dbg(&priv->i2c->dev, "%s: mode found=%02x\n", - __func__, mode); + dev_dbg(&client->dev, "mode found=%02x\n", mode); break; } } if (mode == 0xff) { - dev_dbg(&priv->i2c->dev, "%s: invalid parameter combination\n", - __func__); + dev_dbg(&client->dev, "invalid parameter combination\n"); ret = -EINVAL; goto error; } @@ -807,13 +804,14 @@ static int tda10071_set_frontend(struct dvb_frontend *fe) return ret; error: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } static int tda10071_get_frontend(struct dvb_frontend *fe) { struct tda10071_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client; struct dtv_frontend_properties *c = &fe->dtv_property_cache; int ret, i; u8 buf[5], tmp; @@ -864,13 +862,14 @@ static int tda10071_get_frontend(struct dvb_frontend *fe) return ret; error: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } static int tda10071_init(struct dvb_frontend *fe) { struct tda10071_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client; struct tda10071_cmd cmd; int ret, i, len, remaining, fw_size; const struct firmware *fw; @@ -890,7 +889,7 @@ static int tda10071_init(struct dvb_frontend *fe) }; struct tda10071_reg_val_mask tab2[] = { { 0xf1, 0x70, 0xff }, - { 0x88, priv->cfg.pll_multiplier, 0x3f }, + { 0x88, priv->pll_multiplier, 0x3f }, { 0x89, 0x00, 0x10 }, { 0x89, 0x10, 0x10 }, { 0xc0, 0x01, 0x01 }, @@ -955,11 +954,11 @@ static int tda10071_init(struct dvb_frontend *fe) /* cold state - try to download firmware */ /* request the firmware, this will block and timeout */ - ret = request_firmware(&fw, fw_file, priv->i2c->dev.parent); + ret = request_firmware(&fw, fw_file, &client->dev); if (ret) { - dev_err(&priv->i2c->dev, - "%s: did not find the firmware file. (%s) Please see linux/Documentation/dvb/ for more details on firmware-problems. (%d)\n", - KBUILD_MODNAME, fw_file, ret); + dev_err(&client->dev, + "did not find the firmware file. (%s) Please see linux/Documentation/dvb/ for more details on firmware-problems. (%d)\n", + fw_file, ret); goto error; } @@ -988,28 +987,26 @@ static int tda10071_init(struct dvb_frontend *fe) if (ret) goto error_release_firmware; - dev_info(&priv->i2c->dev, - "%s: found a '%s' in cold state, will try to load a firmware\n", - KBUILD_MODNAME, tda10071_ops.info.name); - dev_info(&priv->i2c->dev, - "%s: downloading firmware from file '%s'\n", - KBUILD_MODNAME, fw_file); + dev_info(&client->dev, + "found a '%s' in cold state, will try to load a firmware\n", + tda10071_ops.info.name); + dev_info(&client->dev, "downloading firmware from file '%s'\n", + fw_file); /* do not download last byte */ fw_size = fw->size - 1; for (remaining = fw_size; remaining > 0; - remaining -= (priv->cfg.i2c_wr_max - 1)) { + remaining -= (priv->i2c_wr_max - 1)) { len = remaining; - if (len > (priv->cfg.i2c_wr_max - 1)) - len = (priv->cfg.i2c_wr_max - 1); + if (len > (priv->i2c_wr_max - 1)) + len = (priv->i2c_wr_max - 1); ret = tda10071_wr_regs(priv, 0xfa, (u8 *) &fw->data[fw_size - remaining], len); if (ret) { - dev_err(&priv->i2c->dev, - "%s: firmware download failed=%d\n", - KBUILD_MODNAME, ret); + dev_err(&client->dev, + "firmware download failed=%d\n", ret); goto error_release_firmware; } } @@ -1032,8 +1029,7 @@ static int tda10071_init(struct dvb_frontend *fe) goto error; if (tmp) { - dev_info(&priv->i2c->dev, "%s: firmware did not run\n", - KBUILD_MODNAME); + dev_info(&client->dev, "firmware did not run\n"); ret = -EFAULT; goto error; } else { @@ -1050,30 +1046,30 @@ static int tda10071_init(struct dvb_frontend *fe) if (ret) goto error; - dev_info(&priv->i2c->dev, "%s: firmware version %d.%d.%d.%d\n", - KBUILD_MODNAME, buf[0], buf[1], buf[2], buf[3]); - dev_info(&priv->i2c->dev, "%s: found a '%s' in warm state\n", - KBUILD_MODNAME, tda10071_ops.info.name); + dev_info(&client->dev, "firmware version %d.%d.%d.%d\n", + buf[0], buf[1], buf[2], buf[3]); + dev_info(&client->dev, "found a '%s' in warm state\n", + tda10071_ops.info.name); ret = tda10071_rd_regs(priv, 0x81, buf, 2); if (ret) goto error; cmd.args[0] = CMD_DEMOD_INIT; - cmd.args[1] = ((priv->cfg.xtal / 1000) >> 8) & 0xff; - cmd.args[2] = ((priv->cfg.xtal / 1000) >> 0) & 0xff; + cmd.args[1] = ((priv->clk / 1000) >> 8) & 0xff; + cmd.args[2] = ((priv->clk / 1000) >> 0) & 0xff; cmd.args[3] = buf[0]; cmd.args[4] = buf[1]; - cmd.args[5] = priv->cfg.pll_multiplier; - cmd.args[6] = priv->cfg.spec_inv; + cmd.args[5] = priv->pll_multiplier; + cmd.args[6] = priv->spec_inv; cmd.args[7] = 0x00; cmd.len = 8; ret = tda10071_cmd_execute(priv, &cmd); if (ret) goto error; - if (priv->cfg.tuner_i2c_addr) - tmp = priv->cfg.tuner_i2c_addr; + if (priv->tuner_i2c_addr) + tmp = priv->tuner_i2c_addr; else tmp = 0x14; @@ -1099,7 +1095,7 @@ static int tda10071_init(struct dvb_frontend *fe) cmd.args[0] = CMD_MPEG_CONFIG; cmd.args[1] = 0; - cmd.args[2] = priv->cfg.ts_mode; + cmd.args[2] = priv->ts_mode; cmd.args[3] = 0x00; cmd.args[4] = 0x04; cmd.args[5] = 0x00; @@ -1142,13 +1138,14 @@ static int tda10071_init(struct dvb_frontend *fe) error_release_firmware: release_firmware(fw); error: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } static int tda10071_sleep(struct dvb_frontend *fe) { struct tda10071_priv *priv = fe->demodulator_priv; + struct i2c_client *client = priv->client; struct tda10071_cmd cmd; int ret, i; struct tda10071_reg_val_mask tab[] = { @@ -1186,7 +1183,7 @@ static int tda10071_sleep(struct dvb_frontend *fe) return ret; error: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } @@ -1200,71 +1197,6 @@ static int tda10071_get_tune_settings(struct dvb_frontend *fe, return 0; } -static void tda10071_release(struct dvb_frontend *fe) -{ - struct tda10071_priv *priv = fe->demodulator_priv; - kfree(priv); -} - -struct dvb_frontend *tda10071_attach(const struct tda10071_config *config, - struct i2c_adapter *i2c) -{ - int ret; - struct tda10071_priv *priv = NULL; - u8 tmp; - - /* allocate memory for the internal priv */ - priv = kzalloc(sizeof(struct tda10071_priv), GFP_KERNEL); - if (priv == NULL) { - ret = -ENOMEM; - goto error; - } - - /* make sure demod i2c address is specified */ - if (!config->demod_i2c_addr) { - dev_dbg(&i2c->dev, "%s: invalid demod i2c address\n", __func__); - ret = -EINVAL; - goto error; - } - - /* make sure tuner i2c address is specified */ - if (!config->tuner_i2c_addr) { - dev_dbg(&i2c->dev, "%s: invalid tuner i2c address\n", __func__); - ret = -EINVAL; - goto error; - } - - /* setup the priv */ - priv->i2c = i2c; - memcpy(&priv->cfg, config, sizeof(struct tda10071_config)); - - /* chip ID */ - ret = tda10071_rd_reg(priv, 0xff, &tmp); - if (ret || tmp != 0x0f) - goto error; - - /* chip type */ - ret = tda10071_rd_reg(priv, 0xdd, &tmp); - if (ret || tmp != 0x00) - goto error; - - /* chip version */ - ret = tda10071_rd_reg(priv, 0xfe, &tmp); - if (ret || tmp != 0x01) - goto error; - - /* create dvb_frontend */ - memcpy(&priv->fe.ops, &tda10071_ops, sizeof(struct dvb_frontend_ops)); - priv->fe.demodulator_priv = priv; - - return &priv->fe; -error: - dev_dbg(&i2c->dev, "%s: failed=%d\n", __func__, ret); - kfree(priv); - return NULL; -} -EXPORT_SYMBOL(tda10071_attach); - static struct dvb_frontend_ops tda10071_ops = { .delsys = { SYS_DVBS, SYS_DVBS2 }, .info = { @@ -1289,8 +1221,6 @@ static struct dvb_frontend_ops tda10071_ops = { FE_CAN_2G_MODULATION }, - .release = tda10071_release, - .get_tune_settings = tda10071_get_tune_settings, .init = tda10071_init, @@ -1337,14 +1267,12 @@ static int tda10071_probe(struct i2c_client *client, } dev->client = client; - dev->i2c = client->adapter; - dev->cfg.demod_i2c_addr = client->addr; - dev->cfg.i2c_wr_max = pdata->i2c_wr_max; - dev->cfg.ts_mode = pdata->ts_mode; - dev->cfg.spec_inv = pdata->spec_inv; - dev->cfg.xtal = pdata->clk; - dev->cfg.pll_multiplier = pdata->pll_multiplier; - dev->cfg.tuner_i2c_addr = pdata->tuner_i2c_addr; + dev->clk = pdata->clk; + dev->i2c_wr_max = pdata->i2c_wr_max; + dev->ts_mode = pdata->ts_mode; + dev->spec_inv = pdata->spec_inv; + dev->pll_multiplier = pdata->pll_multiplier; + dev->tuner_i2c_addr = pdata->tuner_i2c_addr; /* chip ID */ ret = tda10071_rd_reg(dev, 0xff, &u8tmp); @@ -1375,7 +1303,6 @@ static int tda10071_probe(struct i2c_client *client, /* create dvb_frontend */ memcpy(&dev->fe.ops, &tda10071_ops, sizeof(struct dvb_frontend_ops)); - dev->fe.ops.release = NULL; dev->fe.demodulator_priv = dev; i2c_set_clientdata(client, dev); diff --git a/drivers/media/dvb-frontends/tda10071.h b/drivers/media/dvb-frontends/tda10071.h index 0ffbfa5b2dfb..8f184026ee11 100644 --- a/drivers/media/dvb-frontends/tda10071.h +++ b/drivers/media/dvb-frontends/tda10071.h @@ -21,12 +21,11 @@ #ifndef TDA10071_H #define TDA10071_H -#include #include /* * I2C address - * 0x55, + * 0x05, 0x55, */ /** @@ -53,64 +52,4 @@ struct tda10071_platform_data { struct dvb_frontend* (*get_dvb_frontend)(struct i2c_client *); }; -struct tda10071_config { - /* Demodulator I2C address. - * Default: none, must set - * Values: 0x55, - */ - u8 demod_i2c_addr; - - /* Tuner I2C address. - * Default: none, must set - * Values: 0x14, 0x54, ... - */ - u8 tuner_i2c_addr; - - /* Max bytes I2C provider can write at once. - * Note: Buffer is taken from the stack currently! - * Default: none, must set - * Values: - */ - u16 i2c_wr_max; - - /* TS output mode. - * Default: TDA10071_TS_SERIAL - * Values: - */ -#define TDA10071_TS_SERIAL 0 -#define TDA10071_TS_PARALLEL 1 - u8 ts_mode; - - /* Input spectrum inversion. - * Default: 0 - * Values: 0, 1 - */ - bool spec_inv; - - /* Xtal frequency Hz - * Default: none, must set - * Values: - */ - u32 xtal; - - /* PLL multiplier. - * Default: none, must set - * Values: - */ - u8 pll_multiplier; -}; - - -#if IS_REACHABLE(CONFIG_DVB_TDA10071) -extern struct dvb_frontend *tda10071_attach( - const struct tda10071_config *config, struct i2c_adapter *i2c); -#else -static inline struct dvb_frontend *tda10071_attach( - const struct tda10071_config *config, struct i2c_adapter *i2c) -{ - dev_warn(&i2c->dev, "%s: driver disabled by Kconfig\n", __func__); - return NULL; -} -#endif - #endif /* TDA10071_H */ diff --git a/drivers/media/dvb-frontends/tda10071_priv.h b/drivers/media/dvb-frontends/tda10071_priv.h index 54d7c713eec8..a6d8f8cda652 100644 --- a/drivers/media/dvb-frontends/tda10071_priv.h +++ b/drivers/media/dvb-frontends/tda10071_priv.h @@ -26,10 +26,14 @@ #include struct tda10071_priv { - struct i2c_adapter *i2c; struct dvb_frontend fe; struct i2c_client *client; - struct tda10071_config cfg; + u32 clk; + u16 i2c_wr_max; + u8 ts_mode; + bool spec_inv; + u8 pll_multiplier; + u8 tuner_i2c_addr; u8 meas_count[2]; u32 ber; -- cgit v1.2.3 From fca3e00760edc18955483b25d835630ee1bf3e97 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Mon, 20 Apr 2015 20:04:00 -0300 Subject: [media] tda10071: rename device state struct to dev Rename device state struct from 'priv' to 'dev'. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/tda10071.c | 268 ++++++++++++++-------------- drivers/media/dvb-frontends/tda10071_priv.h | 2 +- 2 files changed, 135 insertions(+), 135 deletions(-) diff --git a/drivers/media/dvb-frontends/tda10071.c b/drivers/media/dvb-frontends/tda10071.c index cc6528556dce..39a4197a3453 100644 --- a/drivers/media/dvb-frontends/tda10071.c +++ b/drivers/media/dvb-frontends/tda10071.c @@ -26,10 +26,10 @@ static struct dvb_frontend_ops tda10071_ops; /* write multiple registers */ -static int tda10071_wr_regs(struct tda10071_priv *priv, u8 reg, u8 *val, +static int tda10071_wr_regs(struct tda10071_dev *dev, u8 reg, u8 *val, int len) { - struct i2c_client *client = priv->client; + struct i2c_client *client = dev->client; int ret; u8 buf[MAX_XFER_SIZE]; struct i2c_msg msg[1] = { @@ -62,10 +62,10 @@ static int tda10071_wr_regs(struct tda10071_priv *priv, u8 reg, u8 *val, } /* read multiple registers */ -static int tda10071_rd_regs(struct tda10071_priv *priv, u8 reg, u8 *val, +static int tda10071_rd_regs(struct tda10071_dev *dev, u8 reg, u8 *val, int len) { - struct i2c_client *client = priv->client; + struct i2c_client *client = dev->client; int ret; u8 buf[MAX_XFER_SIZE]; struct i2c_msg msg[2] = { @@ -101,19 +101,19 @@ static int tda10071_rd_regs(struct tda10071_priv *priv, u8 reg, u8 *val, } /* write single register */ -static int tda10071_wr_reg(struct tda10071_priv *priv, u8 reg, u8 val) +static int tda10071_wr_reg(struct tda10071_dev *dev, u8 reg, u8 val) { - return tda10071_wr_regs(priv, reg, &val, 1); + return tda10071_wr_regs(dev, reg, &val, 1); } /* read single register */ -static int tda10071_rd_reg(struct tda10071_priv *priv, u8 reg, u8 *val) +static int tda10071_rd_reg(struct tda10071_dev *dev, u8 reg, u8 *val) { - return tda10071_rd_regs(priv, reg, val, 1); + return tda10071_rd_regs(dev, reg, val, 1); } /* write single register with mask */ -static int tda10071_wr_reg_mask(struct tda10071_priv *priv, +static int tda10071_wr_reg_mask(struct tda10071_dev *dev, u8 reg, u8 val, u8 mask) { int ret; @@ -121,7 +121,7 @@ static int tda10071_wr_reg_mask(struct tda10071_priv *priv, /* no need for read if whole reg is written */ if (mask != 0xff) { - ret = tda10071_rd_regs(priv, reg, &tmp, 1); + ret = tda10071_rd_regs(dev, reg, &tmp, 1); if (ret) return ret; @@ -130,17 +130,17 @@ static int tda10071_wr_reg_mask(struct tda10071_priv *priv, val |= tmp; } - return tda10071_wr_regs(priv, reg, &val, 1); + return tda10071_wr_regs(dev, reg, &val, 1); } /* read single register with mask */ -static int tda10071_rd_reg_mask(struct tda10071_priv *priv, +static int tda10071_rd_reg_mask(struct tda10071_dev *dev, u8 reg, u8 *val, u8 mask) { int ret, i; u8 tmp; - ret = tda10071_rd_regs(priv, reg, &tmp, 1); + ret = tda10071_rd_regs(dev, reg, &tmp, 1); if (ret) return ret; @@ -157,31 +157,31 @@ static int tda10071_rd_reg_mask(struct tda10071_priv *priv, } /* execute firmware command */ -static int tda10071_cmd_execute(struct tda10071_priv *priv, +static int tda10071_cmd_execute(struct tda10071_dev *dev, struct tda10071_cmd *cmd) { - struct i2c_client *client = priv->client; + struct i2c_client *client = dev->client; int ret, i; u8 tmp; - if (!priv->warm) { + if (!dev->warm) { ret = -EFAULT; goto error; } /* write cmd and args for firmware */ - ret = tda10071_wr_regs(priv, 0x00, cmd->args, cmd->len); + ret = tda10071_wr_regs(dev, 0x00, cmd->args, cmd->len); if (ret) goto error; /* start cmd execution */ - ret = tda10071_wr_reg(priv, 0x1f, 1); + ret = tda10071_wr_reg(dev, 0x1f, 1); if (ret) goto error; /* wait cmd execution terminate */ for (i = 1000, tmp = 1; i && tmp; i--) { - ret = tda10071_rd_reg(priv, 0x1f, &tmp); + ret = tda10071_rd_reg(dev, 0x1f, &tmp); if (ret) goto error; @@ -204,13 +204,13 @@ error: static int tda10071_set_tone(struct dvb_frontend *fe, enum fe_sec_tone_mode fe_sec_tone_mode) { - struct tda10071_priv *priv = fe->demodulator_priv; - struct i2c_client *client = priv->client; + struct tda10071_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; struct tda10071_cmd cmd; int ret; u8 tone; - if (!priv->warm) { + if (!dev->warm) { ret = -EFAULT; goto error; } @@ -236,7 +236,7 @@ static int tda10071_set_tone(struct dvb_frontend *fe, cmd.args[3] = 0x00; cmd.args[4] = tone; cmd.len = 5; - ret = tda10071_cmd_execute(priv, &cmd); + ret = tda10071_cmd_execute(dev, &cmd); if (ret) goto error; @@ -249,13 +249,13 @@ error: static int tda10071_set_voltage(struct dvb_frontend *fe, enum fe_sec_voltage fe_sec_voltage) { - struct tda10071_priv *priv = fe->demodulator_priv; - struct i2c_client *client = priv->client; + struct tda10071_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; struct tda10071_cmd cmd; int ret; u8 voltage; - if (!priv->warm) { + if (!dev->warm) { ret = -EFAULT; goto error; } @@ -282,7 +282,7 @@ static int tda10071_set_voltage(struct dvb_frontend *fe, cmd.args[1] = 0; cmd.args[2] = voltage; cmd.len = 3; - ret = tda10071_cmd_execute(priv, &cmd); + ret = tda10071_cmd_execute(dev, &cmd); if (ret) goto error; @@ -295,13 +295,13 @@ error: static int tda10071_diseqc_send_master_cmd(struct dvb_frontend *fe, struct dvb_diseqc_master_cmd *diseqc_cmd) { - struct tda10071_priv *priv = fe->demodulator_priv; - struct i2c_client *client = priv->client; + struct tda10071_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; struct tda10071_cmd cmd; int ret, i; u8 tmp; - if (!priv->warm) { + if (!dev->warm) { ret = -EFAULT; goto error; } @@ -315,7 +315,7 @@ static int tda10071_diseqc_send_master_cmd(struct dvb_frontend *fe, /* wait LNB TX */ for (i = 500, tmp = 0; i && !tmp; i--) { - ret = tda10071_rd_reg_mask(priv, 0x47, &tmp, 0x01); + ret = tda10071_rd_reg_mask(dev, 0x47, &tmp, 0x01); if (ret) goto error; @@ -329,7 +329,7 @@ static int tda10071_diseqc_send_master_cmd(struct dvb_frontend *fe, goto error; } - ret = tda10071_wr_reg_mask(priv, 0x47, 0x00, 0x01); + ret = tda10071_wr_reg_mask(dev, 0x47, 0x00, 0x01); if (ret) goto error; @@ -342,7 +342,7 @@ static int tda10071_diseqc_send_master_cmd(struct dvb_frontend *fe, cmd.args[6] = diseqc_cmd->msg_len; memcpy(&cmd.args[7], diseqc_cmd->msg, diseqc_cmd->msg_len); cmd.len = 7 + diseqc_cmd->msg_len; - ret = tda10071_cmd_execute(priv, &cmd); + ret = tda10071_cmd_execute(dev, &cmd); if (ret) goto error; @@ -355,13 +355,13 @@ error: static int tda10071_diseqc_recv_slave_reply(struct dvb_frontend *fe, struct dvb_diseqc_slave_reply *reply) { - struct tda10071_priv *priv = fe->demodulator_priv; - struct i2c_client *client = priv->client; + struct tda10071_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; struct tda10071_cmd cmd; int ret, i; u8 tmp; - if (!priv->warm) { + if (!dev->warm) { ret = -EFAULT; goto error; } @@ -370,7 +370,7 @@ static int tda10071_diseqc_recv_slave_reply(struct dvb_frontend *fe, /* wait LNB RX */ for (i = 500, tmp = 0; i && !tmp; i--) { - ret = tda10071_rd_reg_mask(priv, 0x47, &tmp, 0x02); + ret = tda10071_rd_reg_mask(dev, 0x47, &tmp, 0x02); if (ret) goto error; @@ -385,7 +385,7 @@ static int tda10071_diseqc_recv_slave_reply(struct dvb_frontend *fe, } /* reply len */ - ret = tda10071_rd_reg(priv, 0x46, &tmp); + ret = tda10071_rd_reg(dev, 0x46, &tmp); if (ret) goto error; @@ -397,11 +397,11 @@ static int tda10071_diseqc_recv_slave_reply(struct dvb_frontend *fe, cmd.args[0] = CMD_LNB_UPDATE_REPLY; cmd.args[1] = 0; cmd.len = 2; - ret = tda10071_cmd_execute(priv, &cmd); + ret = tda10071_cmd_execute(dev, &cmd); if (ret) goto error; - ret = tda10071_rd_regs(priv, cmd.len, reply->msg, reply->msg_len); + ret = tda10071_rd_regs(dev, cmd.len, reply->msg, reply->msg_len); if (ret) goto error; @@ -414,13 +414,13 @@ error: static int tda10071_diseqc_send_burst(struct dvb_frontend *fe, enum fe_sec_mini_cmd fe_sec_mini_cmd) { - struct tda10071_priv *priv = fe->demodulator_priv; - struct i2c_client *client = priv->client; + struct tda10071_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; struct tda10071_cmd cmd; int ret, i; u8 tmp, burst; - if (!priv->warm) { + if (!dev->warm) { ret = -EFAULT; goto error; } @@ -442,7 +442,7 @@ static int tda10071_diseqc_send_burst(struct dvb_frontend *fe, /* wait LNB TX */ for (i = 500, tmp = 0; i && !tmp; i--) { - ret = tda10071_rd_reg_mask(priv, 0x47, &tmp, 0x01); + ret = tda10071_rd_reg_mask(dev, 0x47, &tmp, 0x01); if (ret) goto error; @@ -456,7 +456,7 @@ static int tda10071_diseqc_send_burst(struct dvb_frontend *fe, goto error; } - ret = tda10071_wr_reg_mask(priv, 0x47, 0x00, 0x01); + ret = tda10071_wr_reg_mask(dev, 0x47, 0x00, 0x01); if (ret) goto error; @@ -464,7 +464,7 @@ static int tda10071_diseqc_send_burst(struct dvb_frontend *fe, cmd.args[1] = 0; cmd.args[2] = burst; cmd.len = 3; - ret = tda10071_cmd_execute(priv, &cmd); + ret = tda10071_cmd_execute(dev, &cmd); if (ret) goto error; @@ -476,19 +476,19 @@ error: static int tda10071_read_status(struct dvb_frontend *fe, enum fe_status *status) { - struct tda10071_priv *priv = fe->demodulator_priv; - struct i2c_client *client = priv->client; + struct tda10071_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; int ret; u8 tmp; *status = 0; - if (!priv->warm) { + if (!dev->warm) { ret = 0; goto error; } - ret = tda10071_rd_reg(priv, 0x39, &tmp); + ret = tda10071_rd_reg(dev, 0x39, &tmp); if (ret) goto error; @@ -500,7 +500,7 @@ static int tda10071_read_status(struct dvb_frontend *fe, enum fe_status *status) if (tmp & 0x08) /* RS or BCH */ *status |= FE_HAS_SYNC | FE_HAS_LOCK; - priv->fe_status = *status; + dev->fe_status = *status; return ret; error: @@ -510,18 +510,18 @@ error: static int tda10071_read_snr(struct dvb_frontend *fe, u16 *snr) { - struct tda10071_priv *priv = fe->demodulator_priv; - struct i2c_client *client = priv->client; + struct tda10071_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; int ret; u8 buf[2]; - if (!priv->warm || !(priv->fe_status & FE_HAS_LOCK)) { + if (!dev->warm || !(dev->fe_status & FE_HAS_LOCK)) { *snr = 0; ret = 0; goto error; } - ret = tda10071_rd_regs(priv, 0x3a, buf, 2); + ret = tda10071_rd_regs(dev, 0x3a, buf, 2); if (ret) goto error; @@ -536,13 +536,13 @@ error: static int tda10071_read_signal_strength(struct dvb_frontend *fe, u16 *strength) { - struct tda10071_priv *priv = fe->demodulator_priv; - struct i2c_client *client = priv->client; + struct tda10071_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; struct tda10071_cmd cmd; int ret; u8 tmp; - if (!priv->warm || !(priv->fe_status & FE_HAS_LOCK)) { + if (!dev->warm || !(dev->fe_status & FE_HAS_LOCK)) { *strength = 0; ret = 0; goto error; @@ -551,12 +551,12 @@ static int tda10071_read_signal_strength(struct dvb_frontend *fe, u16 *strength) cmd.args[0] = CMD_GET_AGCACC; cmd.args[1] = 0; cmd.len = 2; - ret = tda10071_cmd_execute(priv, &cmd); + ret = tda10071_cmd_execute(dev, &cmd); if (ret) goto error; /* input power estimate dBm */ - ret = tda10071_rd_reg(priv, 0x50, &tmp); + ret = tda10071_rd_reg(dev, 0x50, &tmp); if (ret) goto error; @@ -576,19 +576,19 @@ error: static int tda10071_read_ber(struct dvb_frontend *fe, u32 *ber) { - struct tda10071_priv *priv = fe->demodulator_priv; - struct i2c_client *client = priv->client; + struct tda10071_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; struct tda10071_cmd cmd; int ret, i, len; u8 tmp, reg, buf[8]; - if (!priv->warm || !(priv->fe_status & FE_HAS_LOCK)) { - *ber = priv->ber = 0; + if (!dev->warm || !(dev->fe_status & FE_HAS_LOCK)) { + *ber = dev->ber = 0; ret = 0; goto error; } - switch (priv->delivery_system) { + switch (dev->delivery_system) { case SYS_DVBS: reg = 0x4c; len = 8; @@ -600,41 +600,41 @@ static int tda10071_read_ber(struct dvb_frontend *fe, u32 *ber) i = 0; break; default: - *ber = priv->ber = 0; + *ber = dev->ber = 0; return 0; } - ret = tda10071_rd_reg(priv, reg, &tmp); + ret = tda10071_rd_reg(dev, reg, &tmp); if (ret) goto error; - if (priv->meas_count[i] == tmp) { + if (dev->meas_count[i] == tmp) { dev_dbg(&client->dev, "meas not ready=%02x\n", tmp); - *ber = priv->ber; + *ber = dev->ber; return 0; } else { - priv->meas_count[i] = tmp; + dev->meas_count[i] = tmp; } cmd.args[0] = CMD_BER_UPDATE_COUNTERS; cmd.args[1] = 0; cmd.args[2] = i; cmd.len = 3; - ret = tda10071_cmd_execute(priv, &cmd); + ret = tda10071_cmd_execute(dev, &cmd); if (ret) goto error; - ret = tda10071_rd_regs(priv, cmd.len, buf, len); + ret = tda10071_rd_regs(dev, cmd.len, buf, len); if (ret) goto error; - if (priv->delivery_system == SYS_DVBS) { + if (dev->delivery_system == SYS_DVBS) { *ber = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; - priv->ucb += (buf[4] << 8) | buf[5]; + dev->ucb += (buf[4] << 8) | buf[5]; } else { *ber = (buf[0] << 8) | buf[1]; } - priv->ber = *ber; + dev->ber = *ber; return ret; error: @@ -644,18 +644,18 @@ error: static int tda10071_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) { - struct tda10071_priv *priv = fe->demodulator_priv; - struct i2c_client *client = priv->client; + struct tda10071_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; int ret = 0; - if (!priv->warm || !(priv->fe_status & FE_HAS_LOCK)) { + if (!dev->warm || !(dev->fe_status & FE_HAS_LOCK)) { *ucblocks = 0; goto error; } /* UCB is updated when BER is read. Assume BER is read anyway. */ - *ucblocks = priv->ucb; + *ucblocks = dev->ucb; return ret; error: @@ -665,8 +665,8 @@ error: static int tda10071_set_frontend(struct dvb_frontend *fe) { - struct tda10071_priv *priv = fe->demodulator_priv; - struct i2c_client *client = priv->client; + struct tda10071_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; struct tda10071_cmd cmd; struct dtv_frontend_properties *c = &fe->dtv_property_cache; int ret, i; @@ -678,9 +678,9 @@ static int tda10071_set_frontend(struct dvb_frontend *fe) c->delivery_system, c->modulation, c->frequency, c->symbol_rate, c->inversion, c->pilot, c->rolloff); - priv->delivery_system = SYS_UNDEFINED; + dev->delivery_system = SYS_UNDEFINED; - if (!priv->warm) { + if (!dev->warm) { ret = -EFAULT; goto error; } @@ -772,11 +772,11 @@ static int tda10071_set_frontend(struct dvb_frontend *fe) else div = 4; - ret = tda10071_wr_reg(priv, 0x81, div); + ret = tda10071_wr_reg(dev, 0x81, div); if (ret) goto error; - ret = tda10071_wr_reg(priv, 0xe3, div); + ret = tda10071_wr_reg(dev, 0xe3, div); if (ret) goto error; @@ -796,11 +796,11 @@ static int tda10071_set_frontend(struct dvb_frontend *fe) cmd.args[13] = 0x00; cmd.args[14] = 0x00; cmd.len = 15; - ret = tda10071_cmd_execute(priv, &cmd); + ret = tda10071_cmd_execute(dev, &cmd); if (ret) goto error; - priv->delivery_system = c->delivery_system; + dev->delivery_system = c->delivery_system; return ret; error: @@ -810,18 +810,18 @@ error: static int tda10071_get_frontend(struct dvb_frontend *fe) { - struct tda10071_priv *priv = fe->demodulator_priv; - struct i2c_client *client = priv->client; + struct tda10071_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; struct dtv_frontend_properties *c = &fe->dtv_property_cache; int ret, i; u8 buf[5], tmp; - if (!priv->warm || !(priv->fe_status & FE_HAS_LOCK)) { + if (!dev->warm || !(dev->fe_status & FE_HAS_LOCK)) { ret = -EFAULT; goto error; } - ret = tda10071_rd_regs(priv, 0x30, buf, 5); + ret = tda10071_rd_regs(dev, 0x30, buf, 5); if (ret) goto error; @@ -854,7 +854,7 @@ static int tda10071_get_frontend(struct dvb_frontend *fe) c->frequency = (buf[2] << 16) | (buf[3] << 8) | (buf[4] << 0); - ret = tda10071_rd_regs(priv, 0x52, buf, 3); + ret = tda10071_rd_regs(dev, 0x52, buf, 3); if (ret) goto error; @@ -868,8 +868,8 @@ error: static int tda10071_init(struct dvb_frontend *fe) { - struct tda10071_priv *priv = fe->demodulator_priv; - struct i2c_client *client = priv->client; + struct tda10071_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; struct tda10071_cmd cmd; int ret, i, len, remaining, fw_size; const struct firmware *fw; @@ -889,7 +889,7 @@ static int tda10071_init(struct dvb_frontend *fe) }; struct tda10071_reg_val_mask tab2[] = { { 0xf1, 0x70, 0xff }, - { 0x88, priv->pll_multiplier, 0x3f }, + { 0x88, dev->pll_multiplier, 0x3f }, { 0x89, 0x00, 0x10 }, { 0x89, 0x10, 0x10 }, { 0xc0, 0x01, 0x01 }, @@ -933,11 +933,11 @@ static int tda10071_init(struct dvb_frontend *fe) { 0xd5, 0x03, 0x03 }, }; - if (priv->warm) { + if (dev->warm) { /* warm state - wake up device from sleep */ for (i = 0; i < ARRAY_SIZE(tab); i++) { - ret = tda10071_wr_reg_mask(priv, tab[i].reg, + ret = tda10071_wr_reg_mask(dev, tab[i].reg, tab[i].val, tab[i].mask); if (ret) goto error; @@ -947,7 +947,7 @@ static int tda10071_init(struct dvb_frontend *fe) cmd.args[1] = 0; cmd.args[2] = 0; cmd.len = 3; - ret = tda10071_cmd_execute(priv, &cmd); + ret = tda10071_cmd_execute(dev, &cmd); if (ret) goto error; } else { @@ -964,26 +964,26 @@ static int tda10071_init(struct dvb_frontend *fe) /* init */ for (i = 0; i < ARRAY_SIZE(tab2); i++) { - ret = tda10071_wr_reg_mask(priv, tab2[i].reg, + ret = tda10071_wr_reg_mask(dev, tab2[i].reg, tab2[i].val, tab2[i].mask); if (ret) goto error_release_firmware; } /* download firmware */ - ret = tda10071_wr_reg(priv, 0xe0, 0x7f); + ret = tda10071_wr_reg(dev, 0xe0, 0x7f); if (ret) goto error_release_firmware; - ret = tda10071_wr_reg(priv, 0xf7, 0x81); + ret = tda10071_wr_reg(dev, 0xf7, 0x81); if (ret) goto error_release_firmware; - ret = tda10071_wr_reg(priv, 0xf8, 0x00); + ret = tda10071_wr_reg(dev, 0xf8, 0x00); if (ret) goto error_release_firmware; - ret = tda10071_wr_reg(priv, 0xf9, 0x00); + ret = tda10071_wr_reg(dev, 0xf9, 0x00); if (ret) goto error_release_firmware; @@ -997,12 +997,12 @@ static int tda10071_init(struct dvb_frontend *fe) fw_size = fw->size - 1; for (remaining = fw_size; remaining > 0; - remaining -= (priv->i2c_wr_max - 1)) { + remaining -= (dev->i2c_wr_max - 1)) { len = remaining; - if (len > (priv->i2c_wr_max - 1)) - len = (priv->i2c_wr_max - 1); + if (len > (dev->i2c_wr_max - 1)) + len = (dev->i2c_wr_max - 1); - ret = tda10071_wr_regs(priv, 0xfa, + ret = tda10071_wr_regs(dev, 0xfa, (u8 *) &fw->data[fw_size - remaining], len); if (ret) { dev_err(&client->dev, @@ -1012,11 +1012,11 @@ static int tda10071_init(struct dvb_frontend *fe) } release_firmware(fw); - ret = tda10071_wr_reg(priv, 0xf7, 0x0c); + ret = tda10071_wr_reg(dev, 0xf7, 0x0c); if (ret) goto error; - ret = tda10071_wr_reg(priv, 0xe0, 0x00); + ret = tda10071_wr_reg(dev, 0xe0, 0x00); if (ret) goto error; @@ -1024,7 +1024,7 @@ static int tda10071_init(struct dvb_frontend *fe) msleep(250); /* firmware status */ - ret = tda10071_rd_reg(priv, 0x51, &tmp); + ret = tda10071_rd_reg(dev, 0x51, &tmp); if (ret) goto error; @@ -1033,16 +1033,16 @@ static int tda10071_init(struct dvb_frontend *fe) ret = -EFAULT; goto error; } else { - priv->warm = true; + dev->warm = true; } cmd.args[0] = CMD_GET_FW_VERSION; cmd.len = 1; - ret = tda10071_cmd_execute(priv, &cmd); + ret = tda10071_cmd_execute(dev, &cmd); if (ret) goto error; - ret = tda10071_rd_regs(priv, cmd.len, buf, 4); + ret = tda10071_rd_regs(dev, cmd.len, buf, 4); if (ret) goto error; @@ -1051,25 +1051,25 @@ static int tda10071_init(struct dvb_frontend *fe) dev_info(&client->dev, "found a '%s' in warm state\n", tda10071_ops.info.name); - ret = tda10071_rd_regs(priv, 0x81, buf, 2); + ret = tda10071_rd_regs(dev, 0x81, buf, 2); if (ret) goto error; cmd.args[0] = CMD_DEMOD_INIT; - cmd.args[1] = ((priv->clk / 1000) >> 8) & 0xff; - cmd.args[2] = ((priv->clk / 1000) >> 0) & 0xff; + cmd.args[1] = ((dev->clk / 1000) >> 8) & 0xff; + cmd.args[2] = ((dev->clk / 1000) >> 0) & 0xff; cmd.args[3] = buf[0]; cmd.args[4] = buf[1]; - cmd.args[5] = priv->pll_multiplier; - cmd.args[6] = priv->spec_inv; + cmd.args[5] = dev->pll_multiplier; + cmd.args[6] = dev->spec_inv; cmd.args[7] = 0x00; cmd.len = 8; - ret = tda10071_cmd_execute(priv, &cmd); + ret = tda10071_cmd_execute(dev, &cmd); if (ret) goto error; - if (priv->tuner_i2c_addr) - tmp = priv->tuner_i2c_addr; + if (dev->tuner_i2c_addr) + tmp = dev->tuner_i2c_addr; else tmp = 0x14; @@ -1089,22 +1089,22 @@ static int tda10071_init(struct dvb_frontend *fe) cmd.args[13] = 0x00; cmd.args[14] = 0x00; cmd.len = 15; - ret = tda10071_cmd_execute(priv, &cmd); + ret = tda10071_cmd_execute(dev, &cmd); if (ret) goto error; cmd.args[0] = CMD_MPEG_CONFIG; cmd.args[1] = 0; - cmd.args[2] = priv->ts_mode; + cmd.args[2] = dev->ts_mode; cmd.args[3] = 0x00; cmd.args[4] = 0x04; cmd.args[5] = 0x00; cmd.len = 6; - ret = tda10071_cmd_execute(priv, &cmd); + ret = tda10071_cmd_execute(dev, &cmd); if (ret) goto error; - ret = tda10071_wr_reg_mask(priv, 0xf0, 0x01, 0x01); + ret = tda10071_wr_reg_mask(dev, 0xf0, 0x01, 0x01); if (ret) goto error; @@ -1120,7 +1120,7 @@ static int tda10071_init(struct dvb_frontend *fe) cmd.args[9] = 30; cmd.args[10] = 30; cmd.len = 11; - ret = tda10071_cmd_execute(priv, &cmd); + ret = tda10071_cmd_execute(dev, &cmd); if (ret) goto error; @@ -1129,7 +1129,7 @@ static int tda10071_init(struct dvb_frontend *fe) cmd.args[2] = 14; cmd.args[3] = 14; cmd.len = 4; - ret = tda10071_cmd_execute(priv, &cmd); + ret = tda10071_cmd_execute(dev, &cmd); if (ret) goto error; } @@ -1144,8 +1144,8 @@ error: static int tda10071_sleep(struct dvb_frontend *fe) { - struct tda10071_priv *priv = fe->demodulator_priv; - struct i2c_client *client = priv->client; + struct tda10071_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; struct tda10071_cmd cmd; int ret, i; struct tda10071_reg_val_mask tab[] = { @@ -1161,7 +1161,7 @@ static int tda10071_sleep(struct dvb_frontend *fe) { 0xce, 0x10, 0x10 }, }; - if (!priv->warm) { + if (!dev->warm) { ret = -EFAULT; goto error; } @@ -1170,12 +1170,12 @@ static int tda10071_sleep(struct dvb_frontend *fe) cmd.args[1] = 0; cmd.args[2] = 1; cmd.len = 3; - ret = tda10071_cmd_execute(priv, &cmd); + ret = tda10071_cmd_execute(dev, &cmd); if (ret) goto error; for (i = 0; i < ARRAY_SIZE(tab); i++) { - ret = tda10071_wr_reg_mask(priv, tab[i].reg, tab[i].val, + ret = tda10071_wr_reg_mask(dev, tab[i].reg, tab[i].val, tab[i].mask); if (ret) goto error; @@ -1245,7 +1245,7 @@ static struct dvb_frontend_ops tda10071_ops = { static struct dvb_frontend *tda10071_get_dvb_frontend(struct i2c_client *client) { - struct tda10071_priv *dev = i2c_get_clientdata(client); + struct tda10071_dev *dev = i2c_get_clientdata(client); dev_dbg(&client->dev, "\n"); @@ -1255,7 +1255,7 @@ static struct dvb_frontend *tda10071_get_dvb_frontend(struct i2c_client *client) static int tda10071_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct tda10071_priv *dev; + struct tda10071_dev *dev; struct tda10071_platform_data *pdata = client->dev.platform_data; int ret; u8 u8tmp; diff --git a/drivers/media/dvb-frontends/tda10071_priv.h b/drivers/media/dvb-frontends/tda10071_priv.h index a6d8f8cda652..98000282b5a6 100644 --- a/drivers/media/dvb-frontends/tda10071_priv.h +++ b/drivers/media/dvb-frontends/tda10071_priv.h @@ -25,7 +25,7 @@ #include "tda10071.h" #include -struct tda10071_priv { +struct tda10071_dev { struct dvb_frontend fe; struct i2c_client *client; u32 clk; -- cgit v1.2.3 From 54ab48ed5db69212b9b6abc88d21cbbd5c16e7c3 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Mon, 20 Apr 2015 22:21:44 -0300 Subject: [media] tda10071: convert to regmap I2C API Use regmap API for I2C operations. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/Kconfig | 1 + drivers/media/dvb-frontends/tda10071.c | 263 +++++++++------------------- drivers/media/dvb-frontends/tda10071_priv.h | 2 + 3 files changed, 87 insertions(+), 179 deletions(-) diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig index 5ab90f36a6a6..e717a05b71f6 100644 --- a/drivers/media/dvb-frontends/Kconfig +++ b/drivers/media/dvb-frontends/Kconfig @@ -264,6 +264,7 @@ config DVB_MB86A16 config DVB_TDA10071 tristate "NXP TDA10071" depends on DVB_CORE && I2C + select REGMAP default m if !MEDIA_SUBDRV_AUTOSELECT help Say Y when you want to support this frontend. diff --git a/drivers/media/dvb-frontends/tda10071.c b/drivers/media/dvb-frontends/tda10071.c index 39a4197a3453..6226b579a440 100644 --- a/drivers/media/dvb-frontends/tda10071.c +++ b/drivers/media/dvb-frontends/tda10071.c @@ -20,98 +20,13 @@ #include "tda10071_priv.h" -/* Max transfer size done by I2C transfer functions */ -#define MAX_XFER_SIZE 64 - static struct dvb_frontend_ops tda10071_ops; -/* write multiple registers */ -static int tda10071_wr_regs(struct tda10071_dev *dev, u8 reg, u8 *val, - int len) -{ - struct i2c_client *client = dev->client; - int ret; - u8 buf[MAX_XFER_SIZE]; - struct i2c_msg msg[1] = { - { - .addr = client->addr, - .flags = 0, - .len = 1 + len, - .buf = buf, - } - }; - - if (1 + len > sizeof(buf)) { - dev_warn(&client->dev, "i2c wr reg=%04x: len=%d is too big!\n", - reg, len); - return -EINVAL; - } - - buf[0] = reg; - memcpy(&buf[1], val, len); - - ret = i2c_transfer(client->adapter, msg, 1); - if (ret == 1) { - ret = 0; - } else { - dev_warn(&client->dev, "i2c wr failed=%d reg=%02x len=%d\n", - ret, reg, len); - ret = -EREMOTEIO; - } - return ret; -} - -/* read multiple registers */ -static int tda10071_rd_regs(struct tda10071_dev *dev, u8 reg, u8 *val, - int len) -{ - struct i2c_client *client = dev->client; - int ret; - u8 buf[MAX_XFER_SIZE]; - struct i2c_msg msg[2] = { - { - .addr = client->addr, - .flags = 0, - .len = 1, - .buf = ®, - }, { - .addr = client->addr, - .flags = I2C_M_RD, - .len = len, - .buf = buf, - } - }; - - if (len > sizeof(buf)) { - dev_warn(&client->dev, "i2c wr reg=%04x: len=%d is too big!\n", - reg, len); - return -EINVAL; - } - - ret = i2c_transfer(client->adapter, msg, 2); - if (ret == 2) { - memcpy(val, buf, len); - ret = 0; - } else { - dev_warn(&client->dev, "i2c rd failed=%d reg=%02x len=%d\n", - ret, reg, len); - ret = -EREMOTEIO; - } - return ret; -} - -/* write single register */ -static int tda10071_wr_reg(struct tda10071_dev *dev, u8 reg, u8 val) -{ - return tda10071_wr_regs(dev, reg, &val, 1); -} - -/* read single register */ -static int tda10071_rd_reg(struct tda10071_dev *dev, u8 reg, u8 *val) -{ - return tda10071_rd_regs(dev, reg, val, 1); -} - +/* + * XXX: regmap_update_bits() does not fit our needs as it does not support + * partially volatile registers. Also it performs register read even mask is as + * wide as register value. + */ /* write single register with mask */ static int tda10071_wr_reg_mask(struct tda10071_dev *dev, u8 reg, u8 val, u8 mask) @@ -121,7 +36,7 @@ static int tda10071_wr_reg_mask(struct tda10071_dev *dev, /* no need for read if whole reg is written */ if (mask != 0xff) { - ret = tda10071_rd_regs(dev, reg, &tmp, 1); + ret = regmap_bulk_read(dev->regmap, reg, &tmp, 1); if (ret) return ret; @@ -130,30 +45,7 @@ static int tda10071_wr_reg_mask(struct tda10071_dev *dev, val |= tmp; } - return tda10071_wr_regs(dev, reg, &val, 1); -} - -/* read single register with mask */ -static int tda10071_rd_reg_mask(struct tda10071_dev *dev, - u8 reg, u8 *val, u8 mask) -{ - int ret, i; - u8 tmp; - - ret = tda10071_rd_regs(dev, reg, &tmp, 1); - if (ret) - return ret; - - tmp &= mask; - - /* find position of the first bit */ - for (i = 0; i < 8; i++) { - if ((mask >> i) & 0x01) - break; - } - *val = tmp >> i; - - return 0; + return regmap_bulk_write(dev->regmap, reg, &val, 1); } /* execute firmware command */ @@ -162,7 +54,7 @@ static int tda10071_cmd_execute(struct tda10071_dev *dev, { struct i2c_client *client = dev->client; int ret, i; - u8 tmp; + unsigned int uitmp; if (!dev->warm) { ret = -EFAULT; @@ -170,18 +62,18 @@ static int tda10071_cmd_execute(struct tda10071_dev *dev, } /* write cmd and args for firmware */ - ret = tda10071_wr_regs(dev, 0x00, cmd->args, cmd->len); + ret = regmap_bulk_write(dev->regmap, 0x00, cmd->args, cmd->len); if (ret) goto error; /* start cmd execution */ - ret = tda10071_wr_reg(dev, 0x1f, 1); + ret = regmap_write(dev->regmap, 0x1f, 1); if (ret) goto error; /* wait cmd execution terminate */ - for (i = 1000, tmp = 1; i && tmp; i--) { - ret = tda10071_rd_reg(dev, 0x1f, &tmp); + for (i = 1000, uitmp = 1; i && uitmp; i--) { + ret = regmap_read(dev->regmap, 0x1f, &uitmp); if (ret) goto error; @@ -299,7 +191,7 @@ static int tda10071_diseqc_send_master_cmd(struct dvb_frontend *fe, struct i2c_client *client = dev->client; struct tda10071_cmd cmd; int ret, i; - u8 tmp; + unsigned int uitmp; if (!dev->warm) { ret = -EFAULT; @@ -314,11 +206,11 @@ static int tda10071_diseqc_send_master_cmd(struct dvb_frontend *fe, } /* wait LNB TX */ - for (i = 500, tmp = 0; i && !tmp; i--) { - ret = tda10071_rd_reg_mask(dev, 0x47, &tmp, 0x01); + for (i = 500, uitmp = 0; i && !uitmp; i--) { + ret = regmap_read(dev->regmap, 0x47, &uitmp); if (ret) goto error; - + uitmp = (uitmp >> 0) & 1; usleep_range(10000, 20000); } @@ -329,7 +221,7 @@ static int tda10071_diseqc_send_master_cmd(struct dvb_frontend *fe, goto error; } - ret = tda10071_wr_reg_mask(dev, 0x47, 0x00, 0x01); + ret = regmap_update_bits(dev->regmap, 0x47, 0x01, 0x00); if (ret) goto error; @@ -359,7 +251,7 @@ static int tda10071_diseqc_recv_slave_reply(struct dvb_frontend *fe, struct i2c_client *client = dev->client; struct tda10071_cmd cmd; int ret, i; - u8 tmp; + unsigned int uitmp; if (!dev->warm) { ret = -EFAULT; @@ -369,11 +261,11 @@ static int tda10071_diseqc_recv_slave_reply(struct dvb_frontend *fe, dev_dbg(&client->dev, "\n"); /* wait LNB RX */ - for (i = 500, tmp = 0; i && !tmp; i--) { - ret = tda10071_rd_reg_mask(dev, 0x47, &tmp, 0x02); + for (i = 500, uitmp = 0; i && !uitmp; i--) { + ret = regmap_read(dev->regmap, 0x47, &uitmp); if (ret) goto error; - + uitmp = (uitmp >> 1) & 1; usleep_range(10000, 20000); } @@ -385,11 +277,11 @@ static int tda10071_diseqc_recv_slave_reply(struct dvb_frontend *fe, } /* reply len */ - ret = tda10071_rd_reg(dev, 0x46, &tmp); + ret = regmap_read(dev->regmap, 0x46, &uitmp); if (ret) goto error; - reply->msg_len = tmp & 0x1f; /* [4:0] */ + reply->msg_len = uitmp & 0x1f; /* [4:0] */ if (reply->msg_len > sizeof(reply->msg)) reply->msg_len = sizeof(reply->msg); /* truncate API max */ @@ -401,7 +293,8 @@ static int tda10071_diseqc_recv_slave_reply(struct dvb_frontend *fe, if (ret) goto error; - ret = tda10071_rd_regs(dev, cmd.len, reply->msg, reply->msg_len); + ret = regmap_bulk_read(dev->regmap, cmd.len, reply->msg, + reply->msg_len); if (ret) goto error; @@ -418,7 +311,8 @@ static int tda10071_diseqc_send_burst(struct dvb_frontend *fe, struct i2c_client *client = dev->client; struct tda10071_cmd cmd; int ret, i; - u8 tmp, burst; + unsigned int uitmp; + u8 burst; if (!dev->warm) { ret = -EFAULT; @@ -441,11 +335,11 @@ static int tda10071_diseqc_send_burst(struct dvb_frontend *fe, } /* wait LNB TX */ - for (i = 500, tmp = 0; i && !tmp; i--) { - ret = tda10071_rd_reg_mask(dev, 0x47, &tmp, 0x01); + for (i = 500, uitmp = 0; i && !uitmp; i--) { + ret = regmap_read(dev->regmap, 0x47, &uitmp); if (ret) goto error; - + uitmp = (uitmp >> 0) & 1; usleep_range(10000, 20000); } @@ -456,7 +350,7 @@ static int tda10071_diseqc_send_burst(struct dvb_frontend *fe, goto error; } - ret = tda10071_wr_reg_mask(dev, 0x47, 0x00, 0x01); + ret = regmap_update_bits(dev->regmap, 0x47, 0x01, 0x00); if (ret) goto error; @@ -479,7 +373,7 @@ static int tda10071_read_status(struct dvb_frontend *fe, enum fe_status *status) struct tda10071_dev *dev = fe->demodulator_priv; struct i2c_client *client = dev->client; int ret; - u8 tmp; + unsigned int uitmp; *status = 0; @@ -488,16 +382,16 @@ static int tda10071_read_status(struct dvb_frontend *fe, enum fe_status *status) goto error; } - ret = tda10071_rd_reg(dev, 0x39, &tmp); + ret = regmap_read(dev->regmap, 0x39, &uitmp); if (ret) goto error; /* 0x39[0] tuner PLL */ - if (tmp & 0x02) /* demod PLL */ + if (uitmp & 0x02) /* demod PLL */ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER; - if (tmp & 0x04) /* viterbi or LDPC*/ + if (uitmp & 0x04) /* viterbi or LDPC*/ *status |= FE_HAS_VITERBI; - if (tmp & 0x08) /* RS or BCH */ + if (uitmp & 0x08) /* RS or BCH */ *status |= FE_HAS_SYNC | FE_HAS_LOCK; dev->fe_status = *status; @@ -521,7 +415,7 @@ static int tda10071_read_snr(struct dvb_frontend *fe, u16 *snr) goto error; } - ret = tda10071_rd_regs(dev, 0x3a, buf, 2); + ret = regmap_bulk_read(dev->regmap, 0x3a, buf, 2); if (ret) goto error; @@ -540,7 +434,7 @@ static int tda10071_read_signal_strength(struct dvb_frontend *fe, u16 *strength) struct i2c_client *client = dev->client; struct tda10071_cmd cmd; int ret; - u8 tmp; + unsigned int uitmp; if (!dev->warm || !(dev->fe_status & FE_HAS_LOCK)) { *strength = 0; @@ -556,17 +450,17 @@ static int tda10071_read_signal_strength(struct dvb_frontend *fe, u16 *strength) goto error; /* input power estimate dBm */ - ret = tda10071_rd_reg(dev, 0x50, &tmp); + ret = regmap_read(dev->regmap, 0x50, &uitmp); if (ret) goto error; - if (tmp < 181) - tmp = 181; /* -75 dBm */ - else if (tmp > 236) - tmp = 236; /* -20 dBm */ + if (uitmp < 181) + uitmp = 181; /* -75 dBm */ + else if (uitmp > 236) + uitmp = 236; /* -20 dBm */ /* scale value to 0x0000-0xffff */ - *strength = (tmp-181) * 0xffff / (236-181); + *strength = (uitmp-181) * 0xffff / (236-181); return ret; error: @@ -580,7 +474,8 @@ static int tda10071_read_ber(struct dvb_frontend *fe, u32 *ber) struct i2c_client *client = dev->client; struct tda10071_cmd cmd; int ret, i, len; - u8 tmp, reg, buf[8]; + unsigned int uitmp; + u8 reg, buf[8]; if (!dev->warm || !(dev->fe_status & FE_HAS_LOCK)) { *ber = dev->ber = 0; @@ -604,16 +499,16 @@ static int tda10071_read_ber(struct dvb_frontend *fe, u32 *ber) return 0; } - ret = tda10071_rd_reg(dev, reg, &tmp); + ret = regmap_read(dev->regmap, reg, &uitmp); if (ret) goto error; - if (dev->meas_count[i] == tmp) { - dev_dbg(&client->dev, "meas not ready=%02x\n", tmp); + if (dev->meas_count[i] == uitmp) { + dev_dbg(&client->dev, "meas not ready=%02x\n", uitmp); *ber = dev->ber; return 0; } else { - dev->meas_count[i] = tmp; + dev->meas_count[i] = uitmp; } cmd.args[0] = CMD_BER_UPDATE_COUNTERS; @@ -624,7 +519,7 @@ static int tda10071_read_ber(struct dvb_frontend *fe, u32 *ber) if (ret) goto error; - ret = tda10071_rd_regs(dev, cmd.len, buf, len); + ret = regmap_bulk_read(dev->regmap, cmd.len, buf, len); if (ret) goto error; @@ -772,11 +667,11 @@ static int tda10071_set_frontend(struct dvb_frontend *fe) else div = 4; - ret = tda10071_wr_reg(dev, 0x81, div); + ret = regmap_write(dev->regmap, 0x81, div); if (ret) goto error; - ret = tda10071_wr_reg(dev, 0xe3, div); + ret = regmap_write(dev->regmap, 0xe3, div); if (ret) goto error; @@ -821,7 +716,7 @@ static int tda10071_get_frontend(struct dvb_frontend *fe) goto error; } - ret = tda10071_rd_regs(dev, 0x30, buf, 5); + ret = regmap_bulk_read(dev->regmap, 0x30, buf, 5); if (ret) goto error; @@ -854,7 +749,7 @@ static int tda10071_get_frontend(struct dvb_frontend *fe) c->frequency = (buf[2] << 16) | (buf[3] << 8) | (buf[4] << 0); - ret = tda10071_rd_regs(dev, 0x52, buf, 3); + ret = regmap_bulk_read(dev->regmap, 0x52, buf, 3); if (ret) goto error; @@ -872,6 +767,7 @@ static int tda10071_init(struct dvb_frontend *fe) struct i2c_client *client = dev->client; struct tda10071_cmd cmd; int ret, i, len, remaining, fw_size; + unsigned int uitmp; const struct firmware *fw; u8 *fw_file = TDA10071_FIRMWARE; u8 tmp, buf[4]; @@ -971,19 +867,19 @@ static int tda10071_init(struct dvb_frontend *fe) } /* download firmware */ - ret = tda10071_wr_reg(dev, 0xe0, 0x7f); + ret = regmap_write(dev->regmap, 0xe0, 0x7f); if (ret) goto error_release_firmware; - ret = tda10071_wr_reg(dev, 0xf7, 0x81); + ret = regmap_write(dev->regmap, 0xf7, 0x81); if (ret) goto error_release_firmware; - ret = tda10071_wr_reg(dev, 0xf8, 0x00); + ret = regmap_write(dev->regmap, 0xf8, 0x00); if (ret) goto error_release_firmware; - ret = tda10071_wr_reg(dev, 0xf9, 0x00); + ret = regmap_write(dev->regmap, 0xf9, 0x00); if (ret) goto error_release_firmware; @@ -1002,7 +898,7 @@ static int tda10071_init(struct dvb_frontend *fe) if (len > (dev->i2c_wr_max - 1)) len = (dev->i2c_wr_max - 1); - ret = tda10071_wr_regs(dev, 0xfa, + ret = regmap_bulk_write(dev->regmap, 0xfa, (u8 *) &fw->data[fw_size - remaining], len); if (ret) { dev_err(&client->dev, @@ -1012,11 +908,11 @@ static int tda10071_init(struct dvb_frontend *fe) } release_firmware(fw); - ret = tda10071_wr_reg(dev, 0xf7, 0x0c); + ret = regmap_write(dev->regmap, 0xf7, 0x0c); if (ret) goto error; - ret = tda10071_wr_reg(dev, 0xe0, 0x00); + ret = regmap_write(dev->regmap, 0xe0, 0x00); if (ret) goto error; @@ -1024,11 +920,11 @@ static int tda10071_init(struct dvb_frontend *fe) msleep(250); /* firmware status */ - ret = tda10071_rd_reg(dev, 0x51, &tmp); + ret = regmap_read(dev->regmap, 0x51, &uitmp); if (ret) goto error; - if (tmp) { + if (uitmp) { dev_info(&client->dev, "firmware did not run\n"); ret = -EFAULT; goto error; @@ -1042,7 +938,7 @@ static int tda10071_init(struct dvb_frontend *fe) if (ret) goto error; - ret = tda10071_rd_regs(dev, cmd.len, buf, 4); + ret = regmap_bulk_read(dev->regmap, cmd.len, buf, 4); if (ret) goto error; @@ -1051,7 +947,7 @@ static int tda10071_init(struct dvb_frontend *fe) dev_info(&client->dev, "found a '%s' in warm state\n", tda10071_ops.info.name); - ret = tda10071_rd_regs(dev, 0x81, buf, 2); + ret = regmap_bulk_read(dev->regmap, 0x81, buf, 2); if (ret) goto error; @@ -1104,7 +1000,7 @@ static int tda10071_init(struct dvb_frontend *fe) if (ret) goto error; - ret = tda10071_wr_reg_mask(dev, 0xf0, 0x01, 0x01); + ret = regmap_update_bits(dev->regmap, 0xf0, 0x01, 0x01); if (ret) goto error; @@ -1258,7 +1154,11 @@ static int tda10071_probe(struct i2c_client *client, struct tda10071_dev *dev; struct tda10071_platform_data *pdata = client->dev.platform_data; int ret; - u8 u8tmp; + unsigned int uitmp; + static const struct regmap_config regmap_config = { + .reg_bits = 8, + .val_bits = 8, + }; dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) { @@ -1273,30 +1173,35 @@ static int tda10071_probe(struct i2c_client *client, dev->spec_inv = pdata->spec_inv; dev->pll_multiplier = pdata->pll_multiplier; dev->tuner_i2c_addr = pdata->tuner_i2c_addr; + dev->regmap = devm_regmap_init_i2c(client, ®map_config); + if (IS_ERR(dev->regmap)) { + ret = PTR_ERR(dev->regmap); + goto err_kfree; + } /* chip ID */ - ret = tda10071_rd_reg(dev, 0xff, &u8tmp); + ret = regmap_read(dev->regmap, 0xff, &uitmp); if (ret) goto err_kfree; - if (u8tmp != 0x0f) { + if (uitmp != 0x0f) { ret = -ENODEV; goto err_kfree; } /* chip type */ - ret = tda10071_rd_reg(dev, 0xdd, &u8tmp); + ret = regmap_read(dev->regmap, 0xdd, &uitmp); if (ret) goto err_kfree; - if (u8tmp != 0x00) { + if (uitmp != 0x00) { ret = -ENODEV; goto err_kfree; } /* chip version */ - ret = tda10071_rd_reg(dev, 0xfe, &u8tmp); + ret = regmap_read(dev->regmap, 0xfe, &uitmp); if (ret) goto err_kfree; - if (u8tmp != 0x01) { + if (uitmp != 0x01) { ret = -ENODEV; goto err_kfree; } diff --git a/drivers/media/dvb-frontends/tda10071_priv.h b/drivers/media/dvb-frontends/tda10071_priv.h index 98000282b5a6..30143c8f8bb8 100644 --- a/drivers/media/dvb-frontends/tda10071_priv.h +++ b/drivers/media/dvb-frontends/tda10071_priv.h @@ -24,10 +24,12 @@ #include "dvb_frontend.h" #include "tda10071.h" #include +#include struct tda10071_dev { struct dvb_frontend fe; struct i2c_client *client; + struct regmap *regmap; u32 clk; u16 i2c_wr_max; u8 ts_mode; -- cgit v1.2.3 From e14432a5b7302c1f5c6c094de176ae111697bbab Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Tue, 21 Apr 2015 09:58:15 -0300 Subject: [media] tda10071: protect firmware command exec with mutex There should be clearly some lock in order to make sure firmware command in execution is not disturbed by another command. It has worked as callbacks are serialized somehow pretty well and command execution happens usually without any delays. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/tda10071.c | 12 +++++++++--- drivers/media/dvb-frontends/tda10071_priv.h | 1 + 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/media/dvb-frontends/tda10071.c b/drivers/media/dvb-frontends/tda10071.c index 6226b579a440..84fb559e7b3d 100644 --- a/drivers/media/dvb-frontends/tda10071.c +++ b/drivers/media/dvb-frontends/tda10071.c @@ -61,25 +61,28 @@ static int tda10071_cmd_execute(struct tda10071_dev *dev, goto error; } + mutex_lock(&dev->cmd_execute_mutex); + /* write cmd and args for firmware */ ret = regmap_bulk_write(dev->regmap, 0x00, cmd->args, cmd->len); if (ret) - goto error; + goto error_mutex_unlock; /* start cmd execution */ ret = regmap_write(dev->regmap, 0x1f, 1); if (ret) - goto error; + goto error_mutex_unlock; /* wait cmd execution terminate */ for (i = 1000, uitmp = 1; i && uitmp; i--) { ret = regmap_read(dev->regmap, 0x1f, &uitmp); if (ret) - goto error; + goto error_mutex_unlock; usleep_range(200, 5000); } + mutex_unlock(&dev->cmd_execute_mutex); dev_dbg(&client->dev, "loop=%d\n", i); if (i == 0) { @@ -88,6 +91,8 @@ static int tda10071_cmd_execute(struct tda10071_dev *dev, } return ret; +error_mutex_unlock: + mutex_unlock(&dev->cmd_execute_mutex); error: dev_dbg(&client->dev, "failed=%d\n", ret); return ret; @@ -1167,6 +1172,7 @@ static int tda10071_probe(struct i2c_client *client, } dev->client = client; + mutex_init(&dev->cmd_execute_mutex); dev->clk = pdata->clk; dev->i2c_wr_max = pdata->i2c_wr_max; dev->ts_mode = pdata->ts_mode; diff --git a/drivers/media/dvb-frontends/tda10071_priv.h b/drivers/media/dvb-frontends/tda10071_priv.h index 30143c8f8bb8..cf5b43337136 100644 --- a/drivers/media/dvb-frontends/tda10071_priv.h +++ b/drivers/media/dvb-frontends/tda10071_priv.h @@ -30,6 +30,7 @@ struct tda10071_dev { struct dvb_frontend fe; struct i2c_client *client; struct regmap *regmap; + struct mutex cmd_execute_mutex; u32 clk; u16 i2c_wr_max; u8 ts_mode; -- cgit v1.2.3 From 4c4acb7a7e81e41901825ca2afb064ada672b39c Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Tue, 21 Apr 2015 11:16:44 -0300 Subject: [media] tda10071: do not get_frontend() when not ready This is a bit hack, but returning error when driver is not tuned yet causes DVBv5 zap stop polling DVBv5 statistics. Thus return 0 even callback is called during invalid device state. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/tda10071.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/dvb-frontends/tda10071.c b/drivers/media/dvb-frontends/tda10071.c index 84fb559e7b3d..5e949170d75d 100644 --- a/drivers/media/dvb-frontends/tda10071.c +++ b/drivers/media/dvb-frontends/tda10071.c @@ -717,7 +717,7 @@ static int tda10071_get_frontend(struct dvb_frontend *fe) u8 buf[5], tmp; if (!dev->warm || !(dev->fe_status & FE_HAS_LOCK)) { - ret = -EFAULT; + ret = 0; goto error; } -- cgit v1.2.3 From 267897a4708fd7a0592333f33a4a7c393c999ab7 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Tue, 21 Apr 2015 11:14:44 -0300 Subject: [media] tda10071: implement DVBv5 statistics Implement DVBv5 CNR, signal strength, BER and block errors. Wrap legacy DVBv3 statistics to DVBv5 internally. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/tda10071.c | 258 ++++++++++++++-------------- drivers/media/dvb-frontends/tda10071_priv.h | 7 +- 2 files changed, 135 insertions(+), 130 deletions(-) diff --git a/drivers/media/dvb-frontends/tda10071.c b/drivers/media/dvb-frontends/tda10071.c index 5e949170d75d..0b391adf1e41 100644 --- a/drivers/media/dvb-frontends/tda10071.c +++ b/drivers/media/dvb-frontends/tda10071.c @@ -377,8 +377,11 @@ static int tda10071_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct tda10071_dev *dev = fe->demodulator_priv; struct i2c_client *client = dev->client; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct tda10071_cmd cmd; int ret; unsigned int uitmp; + u8 buf[8]; *status = 0; @@ -401,71 +404,105 @@ static int tda10071_read_status(struct dvb_frontend *fe, enum fe_status *status) dev->fe_status = *status; - return ret; -error: - dev_dbg(&client->dev, "failed=%d\n", ret); - return ret; -} + /* signal strength */ + if (dev->fe_status & FE_HAS_SIGNAL) { + cmd.args[0] = CMD_GET_AGCACC; + cmd.args[1] = 0; + cmd.len = 2; + ret = tda10071_cmd_execute(dev, &cmd); + if (ret) + goto error; -static int tda10071_read_snr(struct dvb_frontend *fe, u16 *snr) -{ - struct tda10071_dev *dev = fe->demodulator_priv; - struct i2c_client *client = dev->client; - int ret; - u8 buf[2]; + /* input power estimate dBm */ + ret = regmap_read(dev->regmap, 0x50, &uitmp); + if (ret) + goto error; - if (!dev->warm || !(dev->fe_status & FE_HAS_LOCK)) { - *snr = 0; - ret = 0; - goto error; + c->strength.stat[0].scale = FE_SCALE_DECIBEL; + c->strength.stat[0].svalue = (int) (uitmp - 256) * 1000; + } else { + c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; } - ret = regmap_bulk_read(dev->regmap, 0x3a, buf, 2); - if (ret) - goto error; + /* CNR */ + if (dev->fe_status & FE_HAS_VITERBI) { + /* Es/No */ + ret = regmap_bulk_read(dev->regmap, 0x3a, buf, 2); + if (ret) + goto error; - /* Es/No dBx10 */ - *snr = buf[0] << 8 | buf[1]; + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].svalue = (buf[0] << 8 | buf[1] << 0) * 100; + } else { + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } - return ret; -error: - dev_dbg(&client->dev, "failed=%d\n", ret); - return ret; -} + /* UCB/PER/BER */ + if (dev->fe_status & FE_HAS_LOCK) { + /* TODO: report total bits/packets */ + u8 delivery_system, reg, len; -static int tda10071_read_signal_strength(struct dvb_frontend *fe, u16 *strength) -{ - struct tda10071_dev *dev = fe->demodulator_priv; - struct i2c_client *client = dev->client; - struct tda10071_cmd cmd; - int ret; - unsigned int uitmp; + switch (dev->delivery_system) { + case SYS_DVBS: + reg = 0x4c; + len = 8; + delivery_system = 1; + break; + case SYS_DVBS2: + reg = 0x4d; + len = 4; + delivery_system = 0; + break; + default: + ret = -EINVAL; + goto error; + } - if (!dev->warm || !(dev->fe_status & FE_HAS_LOCK)) { - *strength = 0; - ret = 0; - goto error; - } + ret = regmap_read(dev->regmap, reg, &uitmp); + if (ret) + goto error; - cmd.args[0] = CMD_GET_AGCACC; - cmd.args[1] = 0; - cmd.len = 2; - ret = tda10071_cmd_execute(dev, &cmd); - if (ret) - goto error; + if (dev->meas_count == uitmp) { + dev_dbg(&client->dev, "meas not ready=%02x\n", uitmp); + ret = 0; + goto error; + } else { + dev->meas_count = uitmp; + } - /* input power estimate dBm */ - ret = regmap_read(dev->regmap, 0x50, &uitmp); - if (ret) - goto error; + cmd.args[0] = CMD_BER_UPDATE_COUNTERS; + cmd.args[1] = 0; + cmd.args[2] = delivery_system; + cmd.len = 3; + ret = tda10071_cmd_execute(dev, &cmd); + if (ret) + goto error; - if (uitmp < 181) - uitmp = 181; /* -75 dBm */ - else if (uitmp > 236) - uitmp = 236; /* -20 dBm */ + ret = regmap_bulk_read(dev->regmap, cmd.len, buf, len); + if (ret) + goto error; - /* scale value to 0x0000-0xffff */ - *strength = (uitmp-181) * 0xffff / (236-181); + if (dev->delivery_system == SYS_DVBS) { + dev->dvbv3_ber = buf[0] << 24 | buf[1] << 16 | + buf[2] << 8 | buf[3] << 0; + dev->post_bit_error += buf[0] << 24 | buf[1] << 16 | + buf[2] << 8 | buf[3] << 0; + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[0].uvalue = dev->post_bit_error; + dev->block_error += buf[4] << 8 | buf[5] << 0; + c->block_error.stat[0].scale = FE_SCALE_COUNTER; + c->block_error.stat[0].uvalue = dev->block_error; + } else { + dev->dvbv3_ber = buf[0] << 8 | buf[1] << 0; + dev->post_bit_error += buf[0] << 8 | buf[1] << 0; + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[0].uvalue = dev->post_bit_error; + c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + } else { + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } return ret; error: @@ -473,94 +510,50 @@ error: return ret; } -static int tda10071_read_ber(struct dvb_frontend *fe, u32 *ber) +static int tda10071_read_snr(struct dvb_frontend *fe, u16 *snr) { - struct tda10071_dev *dev = fe->demodulator_priv; - struct i2c_client *client = dev->client; - struct tda10071_cmd cmd; - int ret, i, len; - unsigned int uitmp; - u8 reg, buf[8]; - - if (!dev->warm || !(dev->fe_status & FE_HAS_LOCK)) { - *ber = dev->ber = 0; - ret = 0; - goto error; - } + struct dtv_frontend_properties *c = &fe->dtv_property_cache; - switch (dev->delivery_system) { - case SYS_DVBS: - reg = 0x4c; - len = 8; - i = 1; - break; - case SYS_DVBS2: - reg = 0x4d; - len = 4; - i = 0; - break; - default: - *ber = dev->ber = 0; - return 0; - } + if (c->cnr.stat[0].scale == FE_SCALE_DECIBEL) + *snr = div_s64(c->cnr.stat[0].svalue, 100); + else + *snr = 0; + return 0; +} - ret = regmap_read(dev->regmap, reg, &uitmp); - if (ret) - goto error; +static int tda10071_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + unsigned int uitmp; - if (dev->meas_count[i] == uitmp) { - dev_dbg(&client->dev, "meas not ready=%02x\n", uitmp); - *ber = dev->ber; - return 0; + if (c->strength.stat[0].scale == FE_SCALE_DECIBEL) { + uitmp = c->strength.stat[0].svalue / 1000 + 256; + uitmp = clamp(uitmp, 181U, 236U); /* -75dBm - -20dBm */ + /* scale value to 0x0000-0xffff */ + *strength = (uitmp-181) * 0xffff / (236-181); } else { - dev->meas_count[i] = uitmp; + *strength = 0; } + return 0; +} - cmd.args[0] = CMD_BER_UPDATE_COUNTERS; - cmd.args[1] = 0; - cmd.args[2] = i; - cmd.len = 3; - ret = tda10071_cmd_execute(dev, &cmd); - if (ret) - goto error; - - ret = regmap_bulk_read(dev->regmap, cmd.len, buf, len); - if (ret) - goto error; - - if (dev->delivery_system == SYS_DVBS) { - *ber = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; - dev->ucb += (buf[4] << 8) | buf[5]; - } else { - *ber = (buf[0] << 8) | buf[1]; - } - dev->ber = *ber; +static int tda10071_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct tda10071_dev *dev = fe->demodulator_priv; - return ret; -error: - dev_dbg(&client->dev, "failed=%d\n", ret); - return ret; + *ber = dev->dvbv3_ber; + return 0; } static int tda10071_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) { - struct tda10071_dev *dev = fe->demodulator_priv; - struct i2c_client *client = dev->client; - int ret = 0; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; - if (!dev->warm || !(dev->fe_status & FE_HAS_LOCK)) { + if (c->block_error.stat[0].scale == FE_SCALE_COUNTER) + *ucblocks = c->block_error.stat[0].uvalue; + else *ucblocks = 0; - goto error; - } - - /* UCB is updated when BER is read. Assume BER is read anyway. */ - - *ucblocks = dev->ucb; - - return ret; -error: - dev_dbg(&client->dev, "failed=%d\n", ret); - return ret; + return 0; } static int tda10071_set_frontend(struct dvb_frontend *fe) @@ -770,6 +763,7 @@ static int tda10071_init(struct dvb_frontend *fe) { struct tda10071_dev *dev = fe->demodulator_priv; struct i2c_client *client = dev->client; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; struct tda10071_cmd cmd; int ret, i, len, remaining, fw_size; unsigned int uitmp; @@ -1035,6 +1029,16 @@ static int tda10071_init(struct dvb_frontend *fe) goto error; } + /* init stats here in order signal app which stats are supported */ + c->strength.len = 1; + c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_error.len = 1; + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_error.len = 1; + c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + return ret; error_release_firmware: release_firmware(fw); diff --git a/drivers/media/dvb-frontends/tda10071_priv.h b/drivers/media/dvb-frontends/tda10071_priv.h index cf5b43337136..b9c3601802ba 100644 --- a/drivers/media/dvb-frontends/tda10071_priv.h +++ b/drivers/media/dvb-frontends/tda10071_priv.h @@ -38,12 +38,13 @@ struct tda10071_dev { u8 pll_multiplier; u8 tuner_i2c_addr; - u8 meas_count[2]; - u32 ber; - u32 ucb; + u8 meas_count; + u32 dvbv3_ber; enum fe_status fe_status; enum fe_delivery_system delivery_system; bool warm; /* FW running */ + u64 post_bit_error; + u64 block_error; }; static struct tda10071_modcod { -- cgit v1.2.3 From 073b795efb5c3ed8ddfea4042d5d0d3f8e4409e8 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 15 Jun 2015 08:33:28 -0300 Subject: [media] sh-veu: initialize timestamp_flags and copy timestamp info This field wasn't set, causing WARN_ON's from the vb2 core. Signed-off-by: Hans Verkuil Acked-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sh_veu.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/media/platform/sh_veu.c b/drivers/media/platform/sh_veu.c index 7bb249cd19b8..f5e3eb3a20ff 100644 --- a/drivers/media/platform/sh_veu.c +++ b/drivers/media/platform/sh_veu.c @@ -958,6 +958,7 @@ static int sh_veu_queue_init(void *priv, struct vb2_queue *src_vq, src_vq->ops = &sh_veu_qops; src_vq->mem_ops = &vb2_dma_contig_memops; src_vq->lock = &veu->fop_lock; + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; ret = vb2_queue_init(src_vq); if (ret < 0) @@ -971,6 +972,7 @@ static int sh_veu_queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->ops = &sh_veu_qops; dst_vq->mem_ops = &vb2_dma_contig_memops; dst_vq->lock = &veu->fop_lock; + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; return vb2_queue_init(dst_vq); } @@ -1103,6 +1105,12 @@ static irqreturn_t sh_veu_isr(int irq, void *dev_id) if (!src || !dst) return IRQ_NONE; + dst->v4l2_buf.timestamp = src->v4l2_buf.timestamp; + dst->v4l2_buf.flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; + dst->v4l2_buf.flags |= + src->v4l2_buf.flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK; + dst->v4l2_buf.timecode = src->v4l2_buf.timecode; + spin_lock(&veu->lock); v4l2_m2m_buf_done(src, VB2_BUF_STATE_DONE); v4l2_m2m_buf_done(dst, VB2_BUF_STATE_DONE); -- cgit v1.2.3 From 9212e1d8722d28a06a763f46b783881dcde1fb34 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 15 Jun 2015 08:33:30 -0300 Subject: [media] tw9910: don't use COLORSPACE_JPEG This is an SDTV video receiver, so the colorspace should be SMPTE170M. Signed-off-by: Hans Verkuil Acked-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/soc_camera/tw9910.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/i2c/soc_camera/tw9910.c b/drivers/media/i2c/soc_camera/tw9910.c index 42bec9bf1892..df664173cc1b 100644 --- a/drivers/media/i2c/soc_camera/tw9910.c +++ b/drivers/media/i2c/soc_camera/tw9910.c @@ -711,7 +711,7 @@ static int tw9910_get_fmt(struct v4l2_subdev *sd, mf->width = priv->scale->width; mf->height = priv->scale->height; mf->code = MEDIA_BUS_FMT_UYVY8_2X8; - mf->colorspace = V4L2_COLORSPACE_JPEG; + mf->colorspace = V4L2_COLORSPACE_SMPTE170M; mf->field = V4L2_FIELD_INTERLACED_BT; return 0; @@ -732,7 +732,7 @@ static int tw9910_s_fmt(struct v4l2_subdev *sd, if (mf->code != MEDIA_BUS_FMT_UYVY8_2X8) return -EINVAL; - mf->colorspace = V4L2_COLORSPACE_JPEG; + mf->colorspace = V4L2_COLORSPACE_SMPTE170M; ret = tw9910_set_frame(sd, &width, &height); if (!ret) { @@ -762,7 +762,7 @@ static int tw9910_set_fmt(struct v4l2_subdev *sd, } mf->code = MEDIA_BUS_FMT_UYVY8_2X8; - mf->colorspace = V4L2_COLORSPACE_JPEG; + mf->colorspace = V4L2_COLORSPACE_SMPTE170M; /* * select suitable norm -- cgit v1.2.3 From 1f3375e0b257d086efe2b7d3d705e47c3e4a6c2d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 15 Jun 2015 08:33:31 -0300 Subject: [media] tw9910: init priv->scale and update standard When the standard changes the VACTIVE and VDELAY values need to be updated. Signed-off-by: Hans Verkuil Acked-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/soc_camera/tw9910.c | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/soc_camera/tw9910.c b/drivers/media/i2c/soc_camera/tw9910.c index df664173cc1b..e939c24bfd3c 100644 --- a/drivers/media/i2c/soc_camera/tw9910.c +++ b/drivers/media/i2c/soc_camera/tw9910.c @@ -510,13 +510,39 @@ static int tw9910_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct tw9910_priv *priv = to_tw9910(client); + const unsigned hact = 720; + const unsigned hdelay = 15; + unsigned vact; + unsigned vdelay; + int ret; if (!(norm & (V4L2_STD_NTSC | V4L2_STD_PAL))) return -EINVAL; priv->norm = norm; + if (norm & V4L2_STD_525_60) { + vact = 240; + vdelay = 18; + ret = tw9910_mask_set(client, VVBI, 0x10, 0x10); + } else { + vact = 288; + vdelay = 24; + ret = tw9910_mask_set(client, VVBI, 0x10, 0x00); + } + if (!ret) + ret = i2c_smbus_write_byte_data(client, CROP_HI, + ((vdelay >> 2) & 0xc0) | + ((vact >> 4) & 0x30) | + ((hdelay >> 6) & 0x0c) | + ((hact >> 8) & 0x03)); + if (!ret) + ret = i2c_smbus_write_byte_data(client, VDELAY_LO, + vdelay & 0xff); + if (!ret) + ret = i2c_smbus_write_byte_data(client, VACTIVE_LO, + vact & 0xff); - return 0; + return ret; } #ifdef CONFIG_VIDEO_ADV_DEBUG @@ -820,6 +846,7 @@ static int tw9910_video_probe(struct i2c_client *client) "tw9910 Product ID %0x:%0x\n", id, priv->revision); priv->norm = V4L2_STD_NTSC; + priv->scale = &tw9910_ntsc_scales[0]; done: tw9910_s_power(&priv->subdev, 0); -- cgit v1.2.3 From 517ef25521ad7fd61ec411871525ebc28b2170a6 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 15 Jun 2015 08:33:32 -0300 Subject: [media] ak881x: simplify standard checks Simplify confusing conditions. This also swaps the checks for NTSC and PAL: to be consistent with other drivers check for NTSC first. So if the user sets both NTSC and PAL bits, then NTSC wins. Signed-off-by: Hans Verkuil Acked-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ak881x.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/media/i2c/ak881x.c b/drivers/media/i2c/ak881x.c index 29846245aa3b..d3b965ec3bbc 100644 --- a/drivers/media/i2c/ak881x.c +++ b/drivers/media/i2c/ak881x.c @@ -156,12 +156,12 @@ static int ak881x_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) } else if (std == V4L2_STD_PAL_60) { vp1 = 7; ak881x->lines = 480; - } else if (std && !(std & ~V4L2_STD_PAL)) { - vp1 = 0xf; - ak881x->lines = 576; - } else if (std && !(std & ~V4L2_STD_NTSC)) { + } else if (std & V4L2_STD_NTSC) { vp1 = 0; ak881x->lines = 480; + } else if (std & V4L2_STD_PAL) { + vp1 = 0xf; + ak881x->lines = 576; } else { /* No SECAM or PAL_N/Nc supported */ return -EINVAL; -- cgit v1.2.3 From d9db01fbab33d7644934ca1c71b1d6908dd56a7f Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 15 Jun 2015 08:33:33 -0300 Subject: [media] mt9t112: JPEG -> SRGB The JPEG colorspace should only be used for JPEG encoded images. This is just a regular sRGB sensor. Signed-off-by: Hans Verkuil Acked-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/soc_camera/mt9t112.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/media/i2c/soc_camera/mt9t112.c b/drivers/media/i2c/soc_camera/mt9t112.c index de10a76ba6df..2f35d31ca58e 100644 --- a/drivers/media/i2c/soc_camera/mt9t112.c +++ b/drivers/media/i2c/soc_camera/mt9t112.c @@ -104,22 +104,22 @@ struct mt9t112_priv { static const struct mt9t112_format mt9t112_cfmts[] = { { .code = MEDIA_BUS_FMT_UYVY8_2X8, - .colorspace = V4L2_COLORSPACE_JPEG, + .colorspace = V4L2_COLORSPACE_SRGB, .fmt = 1, .order = 0, }, { .code = MEDIA_BUS_FMT_VYUY8_2X8, - .colorspace = V4L2_COLORSPACE_JPEG, + .colorspace = V4L2_COLORSPACE_SRGB, .fmt = 1, .order = 1, }, { .code = MEDIA_BUS_FMT_YUYV8_2X8, - .colorspace = V4L2_COLORSPACE_JPEG, + .colorspace = V4L2_COLORSPACE_SRGB, .fmt = 1, .order = 2, }, { .code = MEDIA_BUS_FMT_YVYU8_2X8, - .colorspace = V4L2_COLORSPACE_JPEG, + .colorspace = V4L2_COLORSPACE_SRGB, .fmt = 1, .order = 3, }, { -- cgit v1.2.3 From 31d38d33d79fe48258c54474a411b201833016f3 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 15 Jun 2015 08:33:34 -0300 Subject: [media] sh_mobile_ceu_camera: fix querycap Fill in the bus_info and driver fields. Found by v4l2-compliance. Signed-off-by: Hans Verkuil Acked-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c index c5c6c4e91f7b..8881efe7e000 100644 --- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c +++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c @@ -1665,6 +1665,8 @@ static int sh_mobile_ceu_querycap(struct soc_camera_host *ici, struct v4l2_capability *cap) { strlcpy(cap->card, "SuperH_Mobile_CEU", sizeof(cap->card)); + strlcpy(cap->driver, "sh_mobile_ceu", sizeof(cap->driver)); + strlcpy(cap->bus_info, "platform:sh_mobile_ceu", sizeof(cap->bus_info)); cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; -- cgit v1.2.3 From fd41c1af4ed14f9bc8591687c29d8a21d5a63d51 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 15 Jun 2015 08:33:35 -0300 Subject: [media] sh_mobile_ceu_camera: set field to FIELD_NONE Make sure that 'field' isn't FIELD_ANY when the driver is first loaded. Fixes a v4l2-compliance failure. Signed-off-by: Hans Verkuil Acked-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c index 8881efe7e000..efdeea4490e8 100644 --- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c +++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c @@ -1775,6 +1775,7 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev) pcdev->max_height = pcdev->pdata->max_height; pcdev->flags = pcdev->pdata->flags; } + pcdev->field = V4L2_FIELD_NONE; if (!pcdev->max_width) { unsigned int v; -- cgit v1.2.3 From d53c0f72757664d9011263cf655f49959d644028 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 15 Jun 2015 08:33:36 -0300 Subject: [media] soc_camera: fix enum_input Fill in the std field from the video_device tvnorms field. This fixes a v4l2-compliance failure. Signed-off-by: Hans Verkuil Signed-off-by: Rob Taylor Acked-by: Guennadi Liakhovetski Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/soc_camera.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c index d708df410f74..f24062ddd4cd 100644 --- a/drivers/media/platform/soc_camera/soc_camera.c +++ b/drivers/media/platform/soc_camera/soc_camera.c @@ -309,11 +309,14 @@ static int soc_camera_try_fmt_vid_cap(struct file *file, void *priv, static int soc_camera_enum_input(struct file *file, void *priv, struct v4l2_input *inp) { + struct soc_camera_device *icd = file->private_data; + if (inp->index != 0) return -EINVAL; /* default is camera */ inp->type = V4L2_INPUT_TYPE_CAMERA; + inp->std = icd->vdev->tvnorms; strcpy(inp->name, "Camera"); return 0; -- cgit v1.2.3 From eb01b1bca9ac7644f178384a594881f2b3433773 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 15 Jun 2015 08:33:37 -0300 Subject: [media] soc_camera: fix expbuf support - For vb1 drivers just return -ENOTTY. - For vb2 drivers allow vb2_expbuf without there being a stream owner: the vb2_expbuf function will return the correct error message in that case. Signed-off-by: Hans Verkuil Acked-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/soc_camera.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c index f24062ddd4cd..5f1e5a8cbab4 100644 --- a/drivers/media/platform/soc_camera/soc_camera.c +++ b/drivers/media/platform/soc_camera/soc_camera.c @@ -470,14 +470,13 @@ static int soc_camera_expbuf(struct file *file, void *priv, struct soc_camera_device *icd = file->private_data; struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - if (icd->streamer != file) - return -EBUSY; - /* videobuf2 only */ if (ici->ops->init_videobuf) - return -EINVAL; - else - return vb2_expbuf(&icd->vb2_vidq, p); + return -ENOTTY; + + if (icd->streamer && icd->streamer != file) + return -EBUSY; + return vb2_expbuf(&icd->vb2_vidq, p); } /* Always entered with .host_lock held */ -- cgit v1.2.3 From 9f317de424dfb57d8c3d3152ace24904bc402810 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 15 Jun 2015 08:33:38 -0300 Subject: [media] soc_camera: compliance fixes - REQBUFS(0) will stop streaming, free buffers and release the file ownership. - Return ENOTTY for create_bufs for a vb1 driver - Return EBUSY if there is a different streaming owner and set the new owner on success. Signed-off-by: Hans Verkuil Acked-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/soc_camera.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c index 5f1e5a8cbab4..6ce65762f556 100644 --- a/drivers/media/platform/soc_camera/soc_camera.c +++ b/drivers/media/platform/soc_camera/soc_camera.c @@ -384,9 +384,8 @@ static int soc_camera_reqbufs(struct file *file, void *priv, ret = vb2_reqbufs(&icd->vb2_vidq, p); } - if (!ret && !icd->streamer) - icd->streamer = file; - + if (!ret) + icd->streamer = p->count ? file : NULL; return ret; } @@ -443,12 +442,19 @@ static int soc_camera_create_bufs(struct file *file, void *priv, { struct soc_camera_device *icd = file->private_data; struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + int ret; /* videobuf2 only */ if (ici->ops->init_videobuf) - return -EINVAL; - else - return vb2_create_bufs(&icd->vb2_vidq, create); + return -ENOTTY; + + if (icd->streamer && icd->streamer != file) + return -EBUSY; + + ret = vb2_create_bufs(&icd->vb2_vidq, create); + if (!ret) + icd->streamer = file; + return ret; } static int soc_camera_prepare_buf(struct file *file, void *priv, -- cgit v1.2.3 From 2d703835b02c0d1adbea4b1eeac188be8e76710b Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 15 Jun 2015 08:33:39 -0300 Subject: [media] soc_camera: pass on streamoff error If streamoff returned an error, then pass that on to the caller. Signed-off-by: Hans Verkuil Acked-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/soc_camera.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c index 6ce65762f556..0b09281bdd7f 100644 --- a/drivers/media/platform/soc_camera/soc_camera.c +++ b/drivers/media/platform/soc_camera/soc_camera.c @@ -1000,6 +1000,7 @@ static int soc_camera_streamoff(struct file *file, void *priv, struct soc_camera_device *icd = file->private_data; struct v4l2_subdev *sd = soc_camera_to_subdev(icd); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + int ret; WARN_ON(priv != file->private_data); @@ -1014,13 +1015,13 @@ static int soc_camera_streamoff(struct file *file, void *priv, * remaining buffers. When the last buffer is freed, stop capture */ if (ici->ops->init_videobuf) - videobuf_streamoff(&icd->vb_vidq); + ret = videobuf_streamoff(&icd->vb_vidq); else - vb2_streamoff(&icd->vb2_vidq, i); + ret = vb2_streamoff(&icd->vb2_vidq, i); v4l2_subdev_call(sd, video, s_stream, 0); - return 0; + return ret; } static int soc_camera_cropcap(struct file *file, void *fh, -- cgit v1.2.3 From 16225ea7fa6843482c562f033b7ec270651636e9 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 15 Jun 2015 08:33:40 -0300 Subject: [media] soc_camera: always release queue for queue owner Always release the queue if the owner closes its filehandle and not when it is the last open filehandle. Signed-off-by: Hans Verkuil Acked-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/soc_camera.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c index 0b09281bdd7f..9087fed586fb 100644 --- a/drivers/media/platform/soc_camera/soc_camera.c +++ b/drivers/media/platform/soc_camera/soc_camera.c @@ -788,20 +788,21 @@ static int soc_camera_close(struct file *file) struct soc_camera_host *ici = to_soc_camera_host(icd->parent); mutex_lock(&ici->host_lock); + if (icd->streamer == file) { + if (ici->ops->init_videobuf2) + vb2_queue_release(&icd->vb2_vidq); + icd->streamer = NULL; + } icd->use_count--; if (!icd->use_count) { pm_runtime_suspend(&icd->vdev->dev); pm_runtime_disable(&icd->vdev->dev); - if (ici->ops->init_videobuf2) - vb2_queue_release(&icd->vb2_vidq); __soc_camera_power_off(icd); soc_camera_remove_device(icd); } - if (icd->streamer == file) - icd->streamer = NULL; mutex_unlock(&ici->host_lock); module_put(ici->ops->owner); -- cgit v1.2.3 From c1362384a85e429cd4e6f5fd65f130770224bcef Mon Sep 17 00:00:00 2001 From: William Towle Date: Thu, 23 Jul 2015 09:21:33 -0300 Subject: [media] media: adv7604: fix probe of ADV7611/7612 Prior to commit f862f57d ("[media] media: i2c: ADV7604: Migrate to regmap"), the local variable 'val' contained the combined register reads used in the chipset version ID test. Restore this expectation so that the comparison works as it used to. Signed-off-by: William Towle Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv7604.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index bfb0b6a72bbc..0587d2755809 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -3108,7 +3108,7 @@ static int adv76xx_probe(struct i2c_client *client, v4l2_err(sd, "Error %d reading IO Regmap\n", err); return -ENODEV; } - val2 |= val; + val |= val2; if ((state->info->type == ADV7611 && val != 0x2051) || (state->info->type == ADV7612 && val != 0x2041)) { v4l2_err(sd, "not an adv761x on address 0x%x\n", -- cgit v1.2.3 From 7111cddd518f7d51d72118b3cb42a8c6d3a00401 Mon Sep 17 00:00:00 2001 From: William Towle Date: Thu, 23 Jul 2015 09:21:34 -0300 Subject: [media] media: adv7604: reduce support to first (digital) input Using adv7611_read_cable_det() for ADV7612 means that full support for '.max_port = ADV7604_PAD_HDMI_PORT_B,' isn't available due to the need for multiple port reads to determine cable detection, and an agreed mechanism for communicating the separate statuses. This patch replaces adv7611_read_cable_det() with a functionally identical copy, commented appropriately. Earlier submissions [leading to commit 8331d30b] also set .cp_csc, which is used in a cp_read() call within adv76xx_log_status(). Signed-off-by: William Towle Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv7604.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index 0587d2755809..2524184f4987 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -877,6 +877,16 @@ static unsigned int adv7611_read_cable_det(struct v4l2_subdev *sd) return value & 1; } +static unsigned int adv7612_read_cable_det(struct v4l2_subdev *sd) +{ + /* Reads CABLE_DET_A_RAW. For input B support, need to + * account for bit 7 [MSB] of 0x6a (ie. CABLE_DET_B_RAW) + */ + u8 value = io_read(sd, 0x6f); + + return value & 1; +} + static int adv76xx_s_detect_tx_5v_ctrl(struct v4l2_subdev *sd) { struct adv76xx_state *state = to_state(sd); @@ -2728,20 +2738,21 @@ static const struct adv76xx_chip_info adv76xx_chip_info[] = { [ADV7612] = { .type = ADV7612, .has_afe = false, - .max_port = ADV7604_PAD_HDMI_PORT_B, - .num_dv_ports = 2, + .max_port = ADV76XX_PAD_HDMI_PORT_A, /* B not supported */ + .num_dv_ports = 1, /* normally 2 */ .edid_enable_reg = 0x74, .edid_status_reg = 0x76, .lcf_reg = 0xa3, .tdms_lock_mask = 0x43, .cable_det_mask = 0x01, .fmt_change_digital_mask = 0x03, + .cp_csc = 0xf4, .formats = adv7612_formats, .nformats = ARRAY_SIZE(adv7612_formats), .set_termination = adv7611_set_termination, .setup_irqs = adv7612_setup_irqs, .read_hdmi_pixelclock = adv7611_read_hdmi_pixelclock, - .read_cable_det = adv7611_read_cable_det, + .read_cable_det = adv7612_read_cable_det, .recommended_settings = { [1] = adv7612_recommended_settings_hdmi, }, -- cgit v1.2.3 From b1304f9bdc3df8f213550b15cdfd7262420ef328 Mon Sep 17 00:00:00 2001 From: Prashant Laddha Date: Mon, 3 Aug 2015 05:36:43 -0300 Subject: [media] vivid: support cvt, gtf timings for video out The generation of cvt, gtf timings is already supported by v4l2-ctl. This patch adds support for setting cvt,gtf timings for video out. While enabling cvt,gtf in vivid capture, the vivid video out was missed out. Adding it now. Signed-off-by: Prashant Laddha Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vivid/vivid-vid-out.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/vivid/vivid-vid-out.c b/drivers/media/platform/vivid/vivid-vid-out.c index 0862c1f24f57..c404e275eae0 100644 --- a/drivers/media/platform/vivid/vivid-vid-out.c +++ b/drivers/media/platform/vivid/vivid-vid-out.c @@ -1124,15 +1124,26 @@ int vivid_vid_out_s_std(struct file *file, void *priv, v4l2_std_id id) return 0; } +static bool valid_cvt_gtf_timings(struct v4l2_dv_timings *timings) +{ + struct v4l2_bt_timings *bt = &timings->bt; + + if ((bt->standards & (V4L2_DV_BT_STD_CVT | V4L2_DV_BT_STD_GTF)) && + v4l2_valid_dv_timings(timings, &vivid_dv_timings_cap, NULL, NULL)) + return true; + + return false; +} + int vivid_vid_out_s_dv_timings(struct file *file, void *_fh, struct v4l2_dv_timings *timings) { struct vivid_dev *dev = video_drvdata(file); - if (!vivid_is_hdmi_out(dev)) return -ENODATA; if (!v4l2_find_dv_timings_cap(timings, &vivid_dv_timings_cap, - 0, NULL, NULL)) + 0, NULL, NULL) && + !valid_cvt_gtf_timings(timings)) return -EINVAL; if (v4l2_match_dv_timings(timings, &dev->dv_timings_out, 0)) return 0; -- cgit v1.2.3 From 9deb6ad661c1656dc770d35336a84b0d885c3ff2 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 6 Aug 2015 09:38:02 -0300 Subject: [media] v4l2: move tracepoint generation into separate file To compile videobuf2-core as a module, the vb2_* tracepoints must be exported from the videodev module. Instead of exporting vb2 tracepoint symbols from v4l2-ioctl.c, move the tracepoint generation into a separate file. This patch fixes the following build error in the modpost stage, introduced by 2091f5181c66 ("[media] videobuf2: add trace events"): ERROR: "__tracepoint_vb2_buf_done" undefined! ERROR: "__tracepoint_vb2_dqbuf" undefined! ERROR: "__tracepoint_vb2_qbuf" undefined! ERROR: "__tracepoint_vb2_buf_queue" undefined! Suggested-by: Steven Rostedt Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/Makefile | 3 +++ drivers/media/v4l2-core/v4l2-ioctl.c | 1 - drivers/media/v4l2-core/v4l2-trace.c | 11 +++++++++++ 3 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 drivers/media/v4l2-core/v4l2-trace.c diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile index dc3de00d68b5..d1dd440d9d9b 100644 --- a/drivers/media/v4l2-core/Makefile +++ b/drivers/media/v4l2-core/Makefile @@ -13,6 +13,9 @@ endif ifeq ($(CONFIG_OF),y) videodev-objs += v4l2-of.o endif +ifeq ($(CONFIG_TRACEPOINTS),y) + videodev-objs += v4l2-trace.o +endif obj-$(CONFIG_VIDEO_V4L2) += videodev.o obj-$(CONFIG_VIDEO_V4L2) += v4l2-common.o diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index ad7e929179ba..4a384fc765b8 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -28,7 +28,6 @@ #include #include -#define CREATE_TRACE_POINTS #include /* Zero out the end of the struct pointed to by p. Everything after, but diff --git a/drivers/media/v4l2-core/v4l2-trace.c b/drivers/media/v4l2-core/v4l2-trace.c new file mode 100644 index 000000000000..ae10b0248c8e --- /dev/null +++ b/drivers/media/v4l2-core/v4l2-trace.c @@ -0,0 +1,11 @@ +#include +#include +#include + +#define CREATE_TRACE_POINTS +#include + +EXPORT_TRACEPOINT_SYMBOL_GPL(vb2_buf_done); +EXPORT_TRACEPOINT_SYMBOL_GPL(vb2_buf_queue); +EXPORT_TRACEPOINT_SYMBOL_GPL(vb2_dqbuf); +EXPORT_TRACEPOINT_SYMBOL_GPL(vb2_qbuf); -- cgit v1.2.3 From 8e8a6b23f115906678252190c8fcf4168cc60fd5 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 28 Jul 2015 05:33:55 -0300 Subject: [media] mt9v032: fix uninitialized variable warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drivers/media/i2c/mt9v032.c: In function ‘mt9v032_probe’: CC [M] drivers/media/i2c/s5k4ecgx.o drivers/media/i2c/mt9v032.c:996:20: warning: ‘pdata’ may be used uninitialized in this function [-Wmaybe-uninitialized] if (pdata && pdata->link_freqs) { ^ It can indeed be uninitialized in one corner case. Initialize to NULL. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/mt9v032.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/mt9v032.c b/drivers/media/i2c/mt9v032.c index 977f4006edbd..a68ce94ee097 100644 --- a/drivers/media/i2c/mt9v032.c +++ b/drivers/media/i2c/mt9v032.c @@ -882,7 +882,7 @@ static const struct regmap_config mt9v032_regmap_config = { static struct mt9v032_platform_data * mt9v032_get_pdata(struct i2c_client *client) { - struct mt9v032_platform_data *pdata; + struct mt9v032_platform_data *pdata = NULL; struct v4l2_of_endpoint endpoint; struct device_node *np; struct property *prop; -- cgit v1.2.3 From 919db5c3199cbd7cf503cdc42f3467a524d1f9ff Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 10 Aug 2015 14:11:41 -0300 Subject: [media] mantis: Fix error handling in mantis_dma_init() Current code assigns 0 to variable 'err', which makes mantis_dma_init() to return success even if mantis_alloc_buffers() fails. Fix it by checking the return value from mantis_alloc_buffers() and propagating it in the case of error. Reported-by: RUC_Soft_Sec Signed-off-by: Fabio Estevam Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/mantis/mantis_dma.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/media/pci/mantis/mantis_dma.c b/drivers/media/pci/mantis/mantis_dma.c index 1d59c7e039f7..87990ece5848 100644 --- a/drivers/media/pci/mantis/mantis_dma.c +++ b/drivers/media/pci/mantis/mantis_dma.c @@ -130,10 +130,11 @@ err: int mantis_dma_init(struct mantis_pci *mantis) { - int err = 0; + int err; dprintk(MANTIS_DEBUG, 1, "Mantis DMA init"); - if (mantis_alloc_buffers(mantis) < 0) { + err = mantis_alloc_buffers(mantis); + if (err < 0) { dprintk(MANTIS_ERROR, 1, "Error allocating DMA buffer"); /* Stop RISC Engine */ -- cgit v1.2.3 From b0c49e2acc6e0e1bf5ae80a3afd63bdcfce19883 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 11 Aug 2015 10:30:24 -0300 Subject: [media] mantis: remove an uneeded goto Gotos makes a little harder to check the code. In this particular case, the goto is doing nothing but jumping into a return. Instead, just replace the goto by the return, making it simpler. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/mantis/mantis_dma.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) 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); -- cgit v1.2.3 From e8e9055594e3d9177416aa4e7ff8619cda3745a9 Mon Sep 17 00:00:00 2001 From: Krzysztof Hałasa Date: Fri, 19 Jun 2015 07:20:40 -0300 Subject: [media] pci/Kconfig: don't use MEDIA_ANALOG_TV_SUPPORT for grabber cards I noticed certain cards are currently under MEDIA_ANALOG_TV_SUPPORT but it seems they are frame grabbers (with CVBS, Svideo etc. inputs) rather than TV receivers (with analog TV tuners). MEDIA_CAMERA_SUPPORT maybe isn't the best name (only "meye" driver seems to drive a real camera in a laptop) but it at least doesn't select the TUNERs. Perhaps the following patch would make sense. Signed-off-by: Krzysztof Ha?asa Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/Kconfig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/pci/Kconfig b/drivers/media/pci/Kconfig index f318ae9bb57a..e01a7688f22f 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 -- cgit v1.2.3 From 7ac5f8315e3daf277ae47620bffe412ef2bf3ab4 Mon Sep 17 00:00:00 2001 From: Antonio Borneo Date: Tue, 23 Jun 2015 11:53:19 -0300 Subject: [media] s5c73m3: Remove redundant spi driver bus initialization In ancient times it was necessary to manually initialize the bus field of an spi_driver to spi_bus_type. These days this is done in spi_register_driver(), so we can drop the manual assignment. Signed-off-by: Antonio Borneo Reviewed-by: Andrzej Hajda Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/s5c73m3/s5c73m3-spi.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-spi.c b/drivers/media/i2c/s5c73m3/s5c73m3-spi.c index 63eb19093381..fa4a5ebda6b2 100644 --- a/drivers/media/i2c/s5c73m3/s5c73m3-spi.c +++ b/drivers/media/i2c/s5c73m3/s5c73m3-spi.c @@ -149,7 +149,6 @@ int s5c73m3_register_spi_driver(struct s5c73m3 *state) spidrv->remove = s5c73m3_spi_remove; spidrv->probe = s5c73m3_spi_probe; spidrv->driver.name = S5C73M3_SPI_DRV_NAME; - spidrv->driver.bus = &spi_bus_type; spidrv->driver.owner = THIS_MODULE; spidrv->driver.of_match_table = s5c73m3_spi_ids; -- cgit v1.2.3 From 1e3f2987f3acbdc99b1f527392a40783323b266b Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Wed, 24 Jun 2015 14:23:20 -0300 Subject: [media] x86/mm/pat, drivers/media/ivtv: move pat warn and replace WARN() with pr_warn() On built-in kernels this warning will always splat as this is part of the module init. Fix that by shifting the PAT requirement check out under the code that does the "quasi-probe" for the device. This device driver relies on an existing driver to find its own devices, it looks for that device driver and its own found devices, then uses driver_for_each_device() to try to see if it can probe each of those devices as a frambuffer device with ivtvfb_init_card(). We tuck the PAT requiremenet check then on the ivtvfb_init_card() call making the check at least require an ivtv device present before complaining. Reported-by: Fengguang Wu [0-day test robot] Signed-off-by: Luis R. Rodriguez Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/ivtv/ivtvfb.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/media/pci/ivtv/ivtvfb.c b/drivers/media/pci/ivtv/ivtvfb.c index 4cb365d4ffdc..8b95eefb610b 100644 --- a/drivers/media/pci/ivtv/ivtvfb.c +++ b/drivers/media/pci/ivtv/ivtvfb.c @@ -38,6 +38,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -1171,6 +1173,13 @@ static int ivtvfb_init_card(struct ivtv *itv) { int rc; +#ifdef CONFIG_X86_64 + if (pat_enabled()) { + pr_warn("ivtvfb needs PAT disabled, boot with nopat kernel parameter\n"); + return -ENODEV; + } +#endif + if (itv->osd_info) { IVTVFB_ERR("Card %d already initialised\n", ivtvfb_card_id); return -EBUSY; @@ -1265,12 +1274,6 @@ static int __init ivtvfb_init(void) int registered = 0; int err; -#ifdef CONFIG_X86_64 - if (WARN(pat_enabled(), - "ivtvfb needs PAT disabled, boot with nopat kernel parameter\n")) { - return -ENODEV; - } -#endif if (ivtvfb_card_id < -1 || ivtvfb_card_id >= IVTV_MAX_CARDS) { printk(KERN_ERR "ivtvfb: ivtvfb_card_id parameter is out of range (valid range: -1 - %d)\n", -- cgit v1.2.3 From 6df34051d8bd8807c873e1bb92b905e370ff3f5b Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 29 Jun 2015 10:46:47 -0300 Subject: [media] dvb-frontends: Make all DVB Frontends visible if COMPILE_TEST=y Make the DVB Frontends menu visible when compile-testing, to allow selecting additional drivers on top of the drivers that are already automatically selected if MEDIA_SUBDRV_AUTOSELECT is enabled. Without this, many drivers stay disabled during e.g. allmodconfig. Signed-off-by: Geert Uytterhoeven Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig index e717a05b71f6..eb8ece64559d 100644 --- a/drivers/media/dvb-frontends/Kconfig +++ b/drivers/media/dvb-frontends/Kconfig @@ -1,5 +1,5 @@ menu "Customise DVB Frontends" - visible if !MEDIA_SUBDRV_AUTOSELECT + visible if !MEDIA_SUBDRV_AUTOSELECT || COMPILE_TEST comment "Multistandard (satellite) frontends" depends on DVB_CORE -- cgit v1.2.3 From 5cc596c66fe7c725ec049ae2093e7242034c97d6 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 29 Jun 2015 10:46:48 -0300 Subject: [media] i2c: Make all i2c devices visible if COMPILE_TEST=y Make the i2c devices menu visible when compile-testing, to allow selecting additional drivers on top of the drivers that are already automatically selected if MEDIA_SUBDRV_AUTOSELECT is enabled. Without this, many drivers stay disabled during e.g. allmodconfig. Signed-off-by: Geert Uytterhoeven Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 0e0490d60e7e..d669547604cd 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -22,7 +22,7 @@ config VIDEO_IR_I2C # menu "Encoders, decoders, sensors and other helper chips" - visible if !MEDIA_SUBDRV_AUTOSELECT + visible if !MEDIA_SUBDRV_AUTOSELECT || COMPILE_TEST comment "Audio decoders, processors and mixers" -- cgit v1.2.3 From a73df85892964a436f9c7f2f803e143b8c97d1ac Mon Sep 17 00:00:00 2001 From: Maninder Singh Date: Thu, 25 Jun 2015 06:58:58 -0300 Subject: [media] usb/airspy: removing unneeded goto This patch removes unneded goto, reported by coccinelle. Signed-off-by: Maninder Singh Reviewed-by: Akhilesh Kumar Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/airspy/airspy.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/media/usb/airspy/airspy.c b/drivers/media/usb/airspy/airspy.c index 4069234abed5..8f2e1c277c5f 100644 --- a/drivers/media/usb/airspy/airspy.c +++ b/drivers/media/usb/airspy/airspy.c @@ -936,9 +936,6 @@ static int airspy_set_if_gain(struct airspy *s) ret = airspy_ctrl_msg(s, CMD_SET_VGA_GAIN, 0, s->if_gain->val, &u8tmp, 1); - if (ret) - goto err; -err: if (ret) dev_dbg(s->dev, "failed=%d\n", ret); -- cgit v1.2.3 From fa9163b570d9cecedcfb45116f2a20dd03fb9cae Mon Sep 17 00:00:00 2001 From: Vaishali Thakkar Date: Fri, 26 Jun 2015 01:47:49 -0300 Subject: [media] ttpci: Replace memset with eth_zero_addr Use eth_zero_addr to assign the zero address to the given address array instead of memset when second argument is address of zero. Note that the 6 in the third argument of memset appears to represent an ethernet address size (ETH_ALEN). The Coccinelle semantic patch that makes this change is as follows: // @eth_zero_addr@ expression e; @@ -memset(e,0x00,6); +eth_zero_addr(e); // Signed-off-by: Vaishali Thakkar Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/ttpci/budget-av.c | 2 +- drivers/media/pci/ttpci/ttpci-eeprom.c | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) 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 c6f31f2712bc..079ee098b7e3 100644 --- a/drivers/media/pci/ttpci/ttpci-eeprom.c +++ b/drivers/media/pci/ttpci/ttpci-eeprom.c @@ -36,6 +36,7 @@ #include #include #include +#include #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,7 +158,7 @@ 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; } -- cgit v1.2.3 From 8278780e3d3effe8f1db495f7329dbedaf035c46 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 29 Jun 2015 10:46:49 -0300 Subject: [media] tuners: Make all TV tuners visible if COMPILE_TEST=y Make the TV tuners menu visible when compile-testing, to allow selecting additional drivers on top of the drivers that are already automatically selected if MEDIA_SUBDRV_AUTOSELECT is enabled. Without this, many drivers stay disabled during e.g. allmodconfig. Signed-off-by: Geert Uytterhoeven Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/tuners/Kconfig b/drivers/media/tuners/Kconfig index 8294af909174..05998f0254c6 100644 --- a/drivers/media/tuners/Kconfig +++ b/drivers/media/tuners/Kconfig @@ -15,7 +15,7 @@ config MEDIA_TUNER select MEDIA_TUNER_MC44S803 if MEDIA_SUBDRV_AUTOSELECT menu "Customize TV tuners" - visible if !MEDIA_SUBDRV_AUTOSELECT + visible if !MEDIA_SUBDRV_AUTOSELECT || COMPILE_TEST depends on MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT || MEDIA_RADIO_SUPPORT || MEDIA_SDR_SUPPORT config MEDIA_TUNER_SIMPLE -- cgit v1.2.3 From 38dcdca38739b95a29c6d03009c3f63b0d959398 Mon Sep 17 00:00:00 2001 From: Vaishali Thakkar Date: Fri, 26 Jun 2015 01:34:37 -0300 Subject: [media] pctv452e: Replace memset with eth_zero_addr Use eth_zero_addr to assign the zero address to the given address array instead of memset when second argument is address of zero. Note that the 6 in the third argument of memset appears to represent an ethernet address size (ETH_ALEN). The Coccinelle semantic patch that makes this change is as follows: // @eth_zero_addr@ expression e; @@ -memset(e,0x00,6); +eth_zero_addr(e); // Signed-off-by: Vaishali Thakkar Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb/pctv452e.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/dvb-usb/pctv452e.c b/drivers/media/usb/dvb-usb/pctv452e.c index d17618fe8f5c..ec397c4b7cc8 100644 --- a/drivers/media/usb/dvb-usb/pctv452e.c +++ b/drivers/media/usb/dvb-usb/pctv452e.c @@ -611,7 +611,7 @@ static int pctv452e_read_mac_address(struct dvb_usb_device *d, u8 mac[6]) return 0; failed: - memset(mac, 0, 6); + eth_zero_addr(mac); return ret; } -- cgit v1.2.3 From 04d8be053fbeac656f8db2acb768179be9997b0a Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 10 Jul 2015 03:19:42 -0300 Subject: [media] dvb-frontends: Drop owner assignment from i2c_driver i2c_driver does not need to set an owner because i2c_register_driver() will set it. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/a8293.c | 1 - drivers/media/dvb-frontends/af9033.c | 1 - drivers/media/dvb-frontends/au8522_decoder.c | 1 - drivers/media/dvb-frontends/m88ds3103.c | 1 - drivers/media/dvb-frontends/rtl2830.c | 1 - drivers/media/dvb-frontends/rtl2832.c | 1 - drivers/media/dvb-frontends/si2168.c | 1 - drivers/media/dvb-frontends/sp2.c | 1 - drivers/media/dvb-frontends/tda10071.c | 1 - drivers/media/dvb-frontends/ts2020.c | 1 - 10 files changed, 10 deletions(-) diff --git a/drivers/media/dvb-frontends/a8293.c b/drivers/media/dvb-frontends/a8293.c index 192313391fa8..e1e9bddcf516 100644 --- a/drivers/media/dvb-frontends/a8293.c +++ b/drivers/media/dvb-frontends/a8293.c @@ -125,7 +125,6 @@ MODULE_DEVICE_TABLE(i2c, a8293_id_table); static struct i2c_driver a8293_driver = { .driver = { - .owner = THIS_MODULE, .name = "a8293", .suppress_bind_attrs = true, }, diff --git a/drivers/media/dvb-frontends/af9033.c b/drivers/media/dvb-frontends/af9033.c index 59018afaa95f..bc35206a0821 100644 --- a/drivers/media/dvb-frontends/af9033.c +++ b/drivers/media/dvb-frontends/af9033.c @@ -1387,7 +1387,6 @@ MODULE_DEVICE_TABLE(i2c, af9033_id_table); static struct i2c_driver af9033_driver = { .driver = { - .owner = THIS_MODULE, .name = "af9033", }, .probe = af9033_probe, diff --git a/drivers/media/dvb-frontends/au8522_decoder.c b/drivers/media/dvb-frontends/au8522_decoder.c index 33aa9410b624..28d7dc2fee34 100644 --- a/drivers/media/dvb-frontends/au8522_decoder.c +++ b/drivers/media/dvb-frontends/au8522_decoder.c @@ -820,7 +820,6 @@ MODULE_DEVICE_TABLE(i2c, au8522_id); static struct i2c_driver au8522_driver = { .driver = { - .owner = THIS_MODULE, .name = "au8522", }, .probe = au8522_probe, diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c index e9b2d2b69b1d..ff31e7a01ca9 100644 --- a/drivers/media/dvb-frontends/m88ds3103.c +++ b/drivers/media/dvb-frontends/m88ds3103.c @@ -1495,7 +1495,6 @@ MODULE_DEVICE_TABLE(i2c, m88ds3103_id_table); static struct i2c_driver m88ds3103_driver = { .driver = { - .owner = THIS_MODULE, .name = "m88ds3103", .suppress_bind_attrs = true, }, diff --git a/drivers/media/dvb-frontends/rtl2830.c b/drivers/media/dvb-frontends/rtl2830.c index 3d01f4f22aca..b792f305cf15 100644 --- a/drivers/media/dvb-frontends/rtl2830.c +++ b/drivers/media/dvb-frontends/rtl2830.c @@ -915,7 +915,6 @@ MODULE_DEVICE_TABLE(i2c, rtl2830_id_table); static struct i2c_driver rtl2830_driver = { .driver = { - .owner = THIS_MODULE, .name = "rtl2830", }, .probe = rtl2830_probe, diff --git a/drivers/media/dvb-frontends/rtl2832.c b/drivers/media/dvb-frontends/rtl2832.c index 822ea4b7a7ff..78b87b260d74 100644 --- a/drivers/media/dvb-frontends/rtl2832.c +++ b/drivers/media/dvb-frontends/rtl2832.c @@ -1319,7 +1319,6 @@ MODULE_DEVICE_TABLE(i2c, rtl2832_id_table); static struct i2c_driver rtl2832_driver = { .driver = { - .owner = THIS_MODULE, .name = "rtl2832", }, .probe = rtl2832_probe, diff --git a/drivers/media/dvb-frontends/si2168.c b/drivers/media/dvb-frontends/si2168.c index 25e238c370e5..81788c5a44d8 100644 --- a/drivers/media/dvb-frontends/si2168.c +++ b/drivers/media/dvb-frontends/si2168.c @@ -757,7 +757,6 @@ MODULE_DEVICE_TABLE(i2c, si2168_id_table); static struct i2c_driver si2168_driver = { .driver = { - .owner = THIS_MODULE, .name = "si2168", }, .probe = si2168_probe, diff --git a/drivers/media/dvb-frontends/sp2.c b/drivers/media/dvb-frontends/sp2.c index 8fd42767e263..43d47dfcc7b8 100644 --- a/drivers/media/dvb-frontends/sp2.c +++ b/drivers/media/dvb-frontends/sp2.c @@ -426,7 +426,6 @@ MODULE_DEVICE_TABLE(i2c, sp2_id); static struct i2c_driver sp2_driver = { .driver = { - .owner = THIS_MODULE, .name = "sp2", }, .probe = sp2_probe, diff --git a/drivers/media/dvb-frontends/tda10071.c b/drivers/media/dvb-frontends/tda10071.c index 0b391adf1e41..ee6653124618 100644 --- a/drivers/media/dvb-frontends/tda10071.c +++ b/drivers/media/dvb-frontends/tda10071.c @@ -1251,7 +1251,6 @@ MODULE_DEVICE_TABLE(i2c, tda10071_id_table); static struct i2c_driver tda10071_driver = { .driver = { - .owner = THIS_MODULE, .name = "tda10071", .suppress_bind_attrs = true, }, diff --git a/drivers/media/dvb-frontends/ts2020.c b/drivers/media/dvb-frontends/ts2020.c index f61b143a0052..7979e5d6498b 100644 --- a/drivers/media/dvb-frontends/ts2020.c +++ b/drivers/media/dvb-frontends/ts2020.c @@ -726,7 +726,6 @@ MODULE_DEVICE_TABLE(i2c, ts2020_id_table); static struct i2c_driver ts2020_driver = { .driver = { - .owner = THIS_MODULE, .name = "ts2020", }, .probe = ts2020_probe, -- cgit v1.2.3 From f078818770e719b3c15b0ee26ace6b6088514152 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 10 Jul 2015 03:19:43 -0300 Subject: [media] dvb-frontends: Drop owner assignment from platform_driver platform_driver does not need to set an owner because platform_driver_register() will set it. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/rtl2832_sdr.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/media/dvb-frontends/rtl2832_sdr.c b/drivers/media/dvb-frontends/rtl2832_sdr.c index 7edb885ae9c8..d5b994f17612 100644 --- a/drivers/media/dvb-frontends/rtl2832_sdr.c +++ b/drivers/media/dvb-frontends/rtl2832_sdr.c @@ -1538,7 +1538,6 @@ static int rtl2832_sdr_remove(struct platform_device *pdev) static struct platform_driver rtl2832_sdr_driver = { .driver = { .name = "rtl2832_sdr", - .owner = THIS_MODULE, }, .probe = rtl2832_sdr_probe, .remove = rtl2832_sdr_remove, -- cgit v1.2.3 From dfadaccabf93362cda7232eb9684b2eae7f2abf9 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 10 Jul 2015 03:19:44 -0300 Subject: [media] i2c: Drop owner assignment from i2c_driver i2c_driver does not need to set an owner because i2c_register_driver() will set it. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv7170.c | 1 - drivers/media/i2c/adv7175.c | 1 - drivers/media/i2c/adv7180.c | 1 - drivers/media/i2c/adv7343.c | 1 - drivers/media/i2c/adv7511.c | 1 - drivers/media/i2c/adv7604.c | 1 - drivers/media/i2c/adv7842.c | 1 - drivers/media/i2c/bt819.c | 1 - drivers/media/i2c/bt856.c | 1 - drivers/media/i2c/bt866.c | 1 - drivers/media/i2c/cs5345.c | 1 - drivers/media/i2c/cs53l32a.c | 1 - drivers/media/i2c/cx25840/cx25840-core.c | 1 - drivers/media/i2c/ks0127.c | 1 - drivers/media/i2c/m52790.c | 1 - drivers/media/i2c/msp3400-driver.c | 1 - drivers/media/i2c/mt9v011.c | 1 - drivers/media/i2c/ov7640.c | 1 - drivers/media/i2c/ov7670.c | 1 - drivers/media/i2c/saa6588.c | 1 - drivers/media/i2c/saa6752hs.c | 1 - drivers/media/i2c/saa7110.c | 1 - drivers/media/i2c/saa7115.c | 1 - drivers/media/i2c/saa7127.c | 1 - drivers/media/i2c/saa717x.c | 1 - drivers/media/i2c/saa7185.c | 1 - drivers/media/i2c/sony-btf-mpx.c | 1 - drivers/media/i2c/tda7432.c | 1 - drivers/media/i2c/tda9840.c | 1 - drivers/media/i2c/tea6415c.c | 1 - drivers/media/i2c/tea6420.c | 1 - drivers/media/i2c/ths7303.c | 1 - drivers/media/i2c/tvaudio.c | 1 - drivers/media/i2c/tvp5150.c | 1 - drivers/media/i2c/tw9903.c | 1 - drivers/media/i2c/tw9906.c | 1 - drivers/media/i2c/upd64031a.c | 1 - drivers/media/i2c/upd64083.c | 1 - drivers/media/i2c/vp27smpx.c | 1 - drivers/media/i2c/vpx3220.c | 1 - drivers/media/i2c/wm8739.c | 1 - drivers/media/i2c/wm8775.c | 1 - 42 files changed, 42 deletions(-) diff --git a/drivers/media/i2c/adv7170.c b/drivers/media/i2c/adv7170.c index f0d3f5a2da46..05f1dc6c72af 100644 --- a/drivers/media/i2c/adv7170.c +++ b/drivers/media/i2c/adv7170.c @@ -401,7 +401,6 @@ MODULE_DEVICE_TABLE(i2c, adv7170_id); static struct i2c_driver adv7170_driver = { .driver = { - .owner = THIS_MODULE, .name = "adv7170", }, .probe = adv7170_probe, diff --git a/drivers/media/i2c/adv7175.c b/drivers/media/i2c/adv7175.c index 321834ba8f57..f554809a51e7 100644 --- a/drivers/media/i2c/adv7175.c +++ b/drivers/media/i2c/adv7175.c @@ -455,7 +455,6 @@ MODULE_DEVICE_TABLE(i2c, adv7175_id); static struct i2c_driver adv7175_driver = { .driver = { - .owner = THIS_MODULE, .name = "adv7175", }, .probe = adv7175_probe, diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c index 09a96df170d1..f82c8aa164fa 100644 --- a/drivers/media/i2c/adv7180.c +++ b/drivers/media/i2c/adv7180.c @@ -1336,7 +1336,6 @@ MODULE_DEVICE_TABLE(of, adv7180_of_id); static struct i2c_driver adv7180_driver = { .driver = { - .owner = THIS_MODULE, .name = KBUILD_MODNAME, .pm = ADV7180_PM_OPS, .of_match_table = of_match_ptr(adv7180_of_id), diff --git a/drivers/media/i2c/adv7343.c b/drivers/media/i2c/adv7343.c index d27283135490..f89d0afcd964 100644 --- a/drivers/media/i2c/adv7343.c +++ b/drivers/media/i2c/adv7343.c @@ -522,7 +522,6 @@ MODULE_DEVICE_TABLE(of, adv7343_of_match); static struct i2c_driver adv7343_driver = { .driver = { .of_match_table = of_match_ptr(adv7343_of_match), - .owner = THIS_MODULE, .name = "adv7343", }, .probe = adv7343_probe, diff --git a/drivers/media/i2c/adv7511.c b/drivers/media/i2c/adv7511.c index 95bcd4026451..ef198cee8969 100644 --- a/drivers/media/i2c/adv7511.c +++ b/drivers/media/i2c/adv7511.c @@ -1576,7 +1576,6 @@ MODULE_DEVICE_TABLE(i2c, adv7511_id); static struct i2c_driver adv7511_driver = { .driver = { - .owner = THIS_MODULE, .name = "adv7511", }, .probe = adv7511_probe, diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index 2524184f4987..5631ec004eed 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -3262,7 +3262,6 @@ static int adv76xx_remove(struct i2c_client *client) static struct i2c_driver adv76xx_driver = { .driver = { - .owner = THIS_MODULE, .name = "adv7604", .of_match_table = of_match_ptr(adv76xx_of_id), }, diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index 897d68c78b06..b7269b8f040d 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -3363,7 +3363,6 @@ MODULE_DEVICE_TABLE(i2c, adv7842_id); static struct i2c_driver adv7842_driver = { .driver = { - .owner = THIS_MODULE, .name = "adv7842", }, .probe = adv7842_probe, diff --git a/drivers/media/i2c/bt819.c b/drivers/media/i2c/bt819.c index 9b5187b56fe4..e00e3104d448 100644 --- a/drivers/media/i2c/bt819.c +++ b/drivers/media/i2c/bt819.c @@ -481,7 +481,6 @@ MODULE_DEVICE_TABLE(i2c, bt819_id); static struct i2c_driver bt819_driver = { .driver = { - .owner = THIS_MODULE, .name = "bt819", }, .probe = bt819_probe, diff --git a/drivers/media/i2c/bt856.c b/drivers/media/i2c/bt856.c index 7fc163d0253c..48176591a80d 100644 --- a/drivers/media/i2c/bt856.c +++ b/drivers/media/i2c/bt856.c @@ -252,7 +252,6 @@ MODULE_DEVICE_TABLE(i2c, bt856_id); static struct i2c_driver bt856_driver = { .driver = { - .owner = THIS_MODULE, .name = "bt856", }, .probe = bt856_probe, diff --git a/drivers/media/i2c/bt866.c b/drivers/media/i2c/bt866.c index a8bf10fc665d..bbec70c882a3 100644 --- a/drivers/media/i2c/bt866.c +++ b/drivers/media/i2c/bt866.c @@ -218,7 +218,6 @@ MODULE_DEVICE_TABLE(i2c, bt866_id); static struct i2c_driver bt866_driver = { .driver = { - .owner = THIS_MODULE, .name = "bt866", }, .probe = bt866_probe, diff --git a/drivers/media/i2c/cs5345.c b/drivers/media/i2c/cs5345.c index 8cebf9cc8007..c7de9790d4f3 100644 --- a/drivers/media/i2c/cs5345.c +++ b/drivers/media/i2c/cs5345.c @@ -211,7 +211,6 @@ MODULE_DEVICE_TABLE(i2c, cs5345_id); static struct i2c_driver cs5345_driver = { .driver = { - .owner = THIS_MODULE, .name = "cs5345", }, .probe = cs5345_probe, diff --git a/drivers/media/i2c/cs53l32a.c b/drivers/media/i2c/cs53l32a.c index 27400c16ef9a..b7e87e38642a 100644 --- a/drivers/media/i2c/cs53l32a.c +++ b/drivers/media/i2c/cs53l32a.c @@ -228,7 +228,6 @@ MODULE_DEVICE_TABLE(i2c, cs53l32a_id); static struct i2c_driver cs53l32a_driver = { .driver = { - .owner = THIS_MODULE, .name = "cs53l32a", }, .probe = cs53l32a_probe, diff --git a/drivers/media/i2c/cx25840/cx25840-core.c b/drivers/media/i2c/cx25840/cx25840-core.c index e15a789ad596..fe6eb78b6914 100644 --- a/drivers/media/i2c/cx25840/cx25840-core.c +++ b/drivers/media/i2c/cx25840/cx25840-core.c @@ -5348,7 +5348,6 @@ MODULE_DEVICE_TABLE(i2c, cx25840_id); static struct i2c_driver cx25840_driver = { .driver = { - .owner = THIS_MODULE, .name = "cx25840", }, .probe = cx25840_probe, diff --git a/drivers/media/i2c/ks0127.c b/drivers/media/i2c/ks0127.c index 25b81bc58c81..77551baab068 100644 --- a/drivers/media/i2c/ks0127.c +++ b/drivers/media/i2c/ks0127.c @@ -708,7 +708,6 @@ MODULE_DEVICE_TABLE(i2c, ks0127_id); static struct i2c_driver ks0127_driver = { .driver = { - .owner = THIS_MODULE, .name = "ks0127", }, .probe = ks0127_probe, diff --git a/drivers/media/i2c/m52790.c b/drivers/media/i2c/m52790.c index bf476358704d..77eb07eb667e 100644 --- a/drivers/media/i2c/m52790.c +++ b/drivers/media/i2c/m52790.c @@ -185,7 +185,6 @@ MODULE_DEVICE_TABLE(i2c, m52790_id); static struct i2c_driver m52790_driver = { .driver = { - .owner = THIS_MODULE, .name = "m52790", }, .probe = m52790_probe, diff --git a/drivers/media/i2c/msp3400-driver.c b/drivers/media/i2c/msp3400-driver.c index dcc68ec71732..bdb94000ba5a 100644 --- a/drivers/media/i2c/msp3400-driver.c +++ b/drivers/media/i2c/msp3400-driver.c @@ -894,7 +894,6 @@ MODULE_DEVICE_TABLE(i2c, msp_id); static struct i2c_driver msp_driver = { .driver = { - .owner = THIS_MODULE, .name = "msp3400", .pm = &msp3400_pm_ops, }, diff --git a/drivers/media/i2c/mt9v011.c b/drivers/media/i2c/mt9v011.c index 57132cdba5ea..a4a5c39b599b 100644 --- a/drivers/media/i2c/mt9v011.c +++ b/drivers/media/i2c/mt9v011.c @@ -583,7 +583,6 @@ MODULE_DEVICE_TABLE(i2c, mt9v011_id); static struct i2c_driver mt9v011_driver = { .driver = { - .owner = THIS_MODULE, .name = "mt9v011", }, .probe = mt9v011_probe, diff --git a/drivers/media/i2c/ov7640.c b/drivers/media/i2c/ov7640.c index faa64baf09e8..b8961df5af33 100644 --- a/drivers/media/i2c/ov7640.c +++ b/drivers/media/i2c/ov7640.c @@ -94,7 +94,6 @@ MODULE_DEVICE_TABLE(i2c, ov7640_id); static struct i2c_driver ov7640_driver = { .driver = { - .owner = THIS_MODULE, .name = "ov7640", }, .probe = ov7640_probe, diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index 2d1e25f10973..e1b5dc84c14e 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c @@ -1674,7 +1674,6 @@ MODULE_DEVICE_TABLE(i2c, ov7670_id); static struct i2c_driver ov7670_driver = { .driver = { - .owner = THIS_MODULE, .name = "ov7670", }, .probe = ov7670_probe, diff --git a/drivers/media/i2c/saa6588.c b/drivers/media/i2c/saa6588.c index 2240e0a67db1..37e65f661d7a 100644 --- a/drivers/media/i2c/saa6588.c +++ b/drivers/media/i2c/saa6588.c @@ -518,7 +518,6 @@ MODULE_DEVICE_TABLE(i2c, saa6588_id); static struct i2c_driver saa6588_driver = { .driver = { - .owner = THIS_MODULE, .name = "saa6588", }, .probe = saa6588_probe, diff --git a/drivers/media/i2c/saa6752hs.c b/drivers/media/i2c/saa6752hs.c index ba3c4156644d..7202d3a3219a 100644 --- a/drivers/media/i2c/saa6752hs.c +++ b/drivers/media/i2c/saa6752hs.c @@ -793,7 +793,6 @@ MODULE_DEVICE_TABLE(i2c, saa6752hs_id); static struct i2c_driver saa6752hs_driver = { .driver = { - .owner = THIS_MODULE, .name = "saa6752hs", }, .probe = saa6752hs_probe, diff --git a/drivers/media/i2c/saa7110.c b/drivers/media/i2c/saa7110.c index 41fcaed8b326..6f49886806ee 100644 --- a/drivers/media/i2c/saa7110.c +++ b/drivers/media/i2c/saa7110.c @@ -461,7 +461,6 @@ MODULE_DEVICE_TABLE(i2c, saa7110_id); static struct i2c_driver saa7110_driver = { .driver = { - .owner = THIS_MODULE, .name = "saa7110", }, .probe = saa7110_probe, diff --git a/drivers/media/i2c/saa7115.c b/drivers/media/i2c/saa7115.c index 0eae5f4471e2..91e75222c537 100644 --- a/drivers/media/i2c/saa7115.c +++ b/drivers/media/i2c/saa7115.c @@ -1929,7 +1929,6 @@ MODULE_DEVICE_TABLE(i2c, saa711x_id); static struct i2c_driver saa711x_driver = { .driver = { - .owner = THIS_MODULE, .name = "saa7115", }, .probe = saa711x_probe, diff --git a/drivers/media/i2c/saa7127.c b/drivers/media/i2c/saa7127.c index 264b755bedce..a43d96da1017 100644 --- a/drivers/media/i2c/saa7127.c +++ b/drivers/media/i2c/saa7127.c @@ -822,7 +822,6 @@ MODULE_DEVICE_TABLE(i2c, saa7127_id); static struct i2c_driver saa7127_driver = { .driver = { - .owner = THIS_MODULE, .name = "saa7127", }, .probe = saa7127_probe, diff --git a/drivers/media/i2c/saa717x.c b/drivers/media/i2c/saa717x.c index c6ba19cf1aa5..1baca37f3eb6 100644 --- a/drivers/media/i2c/saa717x.c +++ b/drivers/media/i2c/saa717x.c @@ -1356,7 +1356,6 @@ MODULE_DEVICE_TABLE(i2c, saa717x_id); static struct i2c_driver saa717x_driver = { .driver = { - .owner = THIS_MODULE, .name = "saa717x", }, .probe = saa717x_probe, diff --git a/drivers/media/i2c/saa7185.c b/drivers/media/i2c/saa7185.c index f56c1c88b27d..eecad2d1edce 100644 --- a/drivers/media/i2c/saa7185.c +++ b/drivers/media/i2c/saa7185.c @@ -356,7 +356,6 @@ MODULE_DEVICE_TABLE(i2c, saa7185_id); static struct i2c_driver saa7185_driver = { .driver = { - .owner = THIS_MODULE, .name = "saa7185", }, .probe = saa7185_probe, diff --git a/drivers/media/i2c/sony-btf-mpx.c b/drivers/media/i2c/sony-btf-mpx.c index 1da8004f5a8e..6b1a04ffad32 100644 --- a/drivers/media/i2c/sony-btf-mpx.c +++ b/drivers/media/i2c/sony-btf-mpx.c @@ -388,7 +388,6 @@ MODULE_DEVICE_TABLE(i2c, sony_btf_mpx_id); static struct i2c_driver sony_btf_mpx_driver = { .driver = { - .owner = THIS_MODULE, .name = "sony-btf-mpx", }, .probe = sony_btf_mpx_probe, diff --git a/drivers/media/i2c/tda7432.c b/drivers/media/i2c/tda7432.c index d3834a4c48da..d87168adee45 100644 --- a/drivers/media/i2c/tda7432.c +++ b/drivers/media/i2c/tda7432.c @@ -409,7 +409,6 @@ MODULE_DEVICE_TABLE(i2c, tda7432_id); static struct i2c_driver tda7432_driver = { .driver = { - .owner = THIS_MODULE, .name = "tda7432", }, .probe = tda7432_probe, diff --git a/drivers/media/i2c/tda9840.c b/drivers/media/i2c/tda9840.c index fbdff8b24eec..f31e659588ac 100644 --- a/drivers/media/i2c/tda9840.c +++ b/drivers/media/i2c/tda9840.c @@ -199,7 +199,6 @@ MODULE_DEVICE_TABLE(i2c, tda9840_id); static struct i2c_driver tda9840_driver = { .driver = { - .owner = THIS_MODULE, .name = "tda9840", }, .probe = tda9840_probe, diff --git a/drivers/media/i2c/tea6415c.c b/drivers/media/i2c/tea6415c.c index bbe1a99fda36..084bd75bb32c 100644 --- a/drivers/media/i2c/tea6415c.c +++ b/drivers/media/i2c/tea6415c.c @@ -162,7 +162,6 @@ MODULE_DEVICE_TABLE(i2c, tea6415c_id); static struct i2c_driver tea6415c_driver = { .driver = { - .owner = THIS_MODULE, .name = "tea6415c", }, .probe = tea6415c_probe, diff --git a/drivers/media/i2c/tea6420.c b/drivers/media/i2c/tea6420.c index 30a8d75771af..b7f4e58f3624 100644 --- a/drivers/media/i2c/tea6420.c +++ b/drivers/media/i2c/tea6420.c @@ -144,7 +144,6 @@ MODULE_DEVICE_TABLE(i2c, tea6420_id); static struct i2c_driver tea6420_driver = { .driver = { - .owner = THIS_MODULE, .name = "tea6420", }, .probe = tea6420_probe, diff --git a/drivers/media/i2c/ths7303.c b/drivers/media/i2c/ths7303.c index 9f7fdb6b61ca..bda3a6540a60 100644 --- a/drivers/media/i2c/ths7303.c +++ b/drivers/media/i2c/ths7303.c @@ -377,7 +377,6 @@ MODULE_DEVICE_TABLE(i2c, ths7303_id); static struct i2c_driver ths7303_driver = { .driver = { - .owner = THIS_MODULE, .name = "ths73x3", }, .probe = ths7303_probe, diff --git a/drivers/media/i2c/tvaudio.c b/drivers/media/i2c/tvaudio.c index 0c50e5285cf6..2a8114a676fd 100644 --- a/drivers/media/i2c/tvaudio.c +++ b/drivers/media/i2c/tvaudio.c @@ -2051,7 +2051,6 @@ MODULE_DEVICE_TABLE(i2c, tvaudio_id); static struct i2c_driver tvaudio_driver = { .driver = { - .owner = THIS_MODULE, .name = "tvaudio", }, .probe = tvaudio_probe, diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index e4fa0746f75e..522a865c5c60 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -1215,7 +1215,6 @@ MODULE_DEVICE_TABLE(i2c, tvp5150_id); static struct i2c_driver tvp5150_driver = { .driver = { - .owner = THIS_MODULE, .name = "tvp5150", }, .probe = tvp5150_probe, diff --git a/drivers/media/i2c/tw9903.c b/drivers/media/i2c/tw9903.c index 12c7d211a4a4..bef79cf74364 100644 --- a/drivers/media/i2c/tw9903.c +++ b/drivers/media/i2c/tw9903.c @@ -266,7 +266,6 @@ MODULE_DEVICE_TABLE(i2c, tw9903_id); static struct i2c_driver tw9903_driver = { .driver = { - .owner = THIS_MODULE, .name = "tw9903", }, .probe = tw9903_probe, diff --git a/drivers/media/i2c/tw9906.c b/drivers/media/i2c/tw9906.c index 2672d89265ff..316a3113ef27 100644 --- a/drivers/media/i2c/tw9906.c +++ b/drivers/media/i2c/tw9906.c @@ -234,7 +234,6 @@ MODULE_DEVICE_TABLE(i2c, tw9906_id); static struct i2c_driver tw9906_driver = { .driver = { - .owner = THIS_MODULE, .name = "tw9906", }, .probe = tw9906_probe, diff --git a/drivers/media/i2c/upd64031a.c b/drivers/media/i2c/upd64031a.c index d248e6a12b8e..2c0f955abc72 100644 --- a/drivers/media/i2c/upd64031a.c +++ b/drivers/media/i2c/upd64031a.c @@ -241,7 +241,6 @@ MODULE_DEVICE_TABLE(i2c, upd64031a_id); static struct i2c_driver upd64031a_driver = { .driver = { - .owner = THIS_MODULE, .name = "upd64031a", }, .probe = upd64031a_probe, diff --git a/drivers/media/i2c/upd64083.c b/drivers/media/i2c/upd64083.c index 3a152ce7258a..f2057a434060 100644 --- a/drivers/media/i2c/upd64083.c +++ b/drivers/media/i2c/upd64083.c @@ -213,7 +213,6 @@ MODULE_DEVICE_TABLE(i2c, upd64083_id); static struct i2c_driver upd64083_driver = { .driver = { - .owner = THIS_MODULE, .name = "upd64083", }, .probe = upd64083_probe, diff --git a/drivers/media/i2c/vp27smpx.c b/drivers/media/i2c/vp27smpx.c index 819ab6d12989..d6c23bdbcd4a 100644 --- a/drivers/media/i2c/vp27smpx.c +++ b/drivers/media/i2c/vp27smpx.c @@ -194,7 +194,6 @@ MODULE_DEVICE_TABLE(i2c, vp27smpx_id); static struct i2c_driver vp27smpx_driver = { .driver = { - .owner = THIS_MODULE, .name = "vp27smpx", }, .probe = vp27smpx_probe, diff --git a/drivers/media/i2c/vpx3220.c b/drivers/media/i2c/vpx3220.c index c060551a5b77..4b564f17f618 100644 --- a/drivers/media/i2c/vpx3220.c +++ b/drivers/media/i2c/vpx3220.c @@ -560,7 +560,6 @@ MODULE_DEVICE_TABLE(i2c, vpx3220_id); static struct i2c_driver vpx3220_driver = { .driver = { - .owner = THIS_MODULE, .name = "vpx3220", }, .probe = vpx3220_probe, diff --git a/drivers/media/i2c/wm8739.c b/drivers/media/i2c/wm8739.c index 534b0e560317..f086e5e6e844 100644 --- a/drivers/media/i2c/wm8739.c +++ b/drivers/media/i2c/wm8739.c @@ -265,7 +265,6 @@ MODULE_DEVICE_TABLE(i2c, wm8739_id); static struct i2c_driver wm8739_driver = { .driver = { - .owner = THIS_MODULE, .name = "wm8739", }, .probe = wm8739_probe, diff --git a/drivers/media/i2c/wm8775.c b/drivers/media/i2c/wm8775.c index bee7946faa7c..d33d2cd6d034 100644 --- a/drivers/media/i2c/wm8775.c +++ b/drivers/media/i2c/wm8775.c @@ -318,7 +318,6 @@ MODULE_DEVICE_TABLE(i2c, wm8775_id); static struct i2c_driver wm8775_driver = { .driver = { - .owner = THIS_MODULE, .name = "wm8775", }, .probe = wm8775_probe, -- cgit v1.2.3 From f77658bda96431801dcd078765ba92adcd02aaaa Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 10 Jul 2015 03:19:46 -0300 Subject: [media] radio: Drop owner assignment from i2c_driver i2c_driver does not need to set an owner because i2c_register_driver() will set it. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/radio-tea5764.c | 1 - drivers/media/radio/saa7706h.c | 1 - drivers/media/radio/tef6862.c | 1 - 3 files changed, 3 deletions(-) diff --git a/drivers/media/radio/radio-tea5764.c b/drivers/media/radio/radio-tea5764.c index cc3990111411..a1930b300c06 100644 --- a/drivers/media/radio/radio-tea5764.c +++ b/drivers/media/radio/radio-tea5764.c @@ -526,7 +526,6 @@ MODULE_DEVICE_TABLE(i2c, tea5764_id); static struct i2c_driver tea5764_i2c_driver = { .driver = { .name = "radio-tea5764", - .owner = THIS_MODULE, }, .probe = tea5764_i2c_probe, .remove = tea5764_i2c_remove, diff --git a/drivers/media/radio/saa7706h.c b/drivers/media/radio/saa7706h.c index 183e92719140..ba8e357ba0a2 100644 --- a/drivers/media/radio/saa7706h.c +++ b/drivers/media/radio/saa7706h.c @@ -417,7 +417,6 @@ MODULE_DEVICE_TABLE(i2c, saa7706h_id); static struct i2c_driver saa7706h_driver = { .driver = { - .owner = THIS_MODULE, .name = DRIVER_NAME, }, .probe = saa7706h_probe, diff --git a/drivers/media/radio/tef6862.c b/drivers/media/radio/tef6862.c index a9319a24c7ef..9f879f0ec0ef 100644 --- a/drivers/media/radio/tef6862.c +++ b/drivers/media/radio/tef6862.c @@ -195,7 +195,6 @@ MODULE_DEVICE_TABLE(i2c, tef6862_id); static struct i2c_driver tef6862_driver = { .driver = { - .owner = THIS_MODULE, .name = DRIVER_NAME, }, .probe = tef6862_probe, -- cgit v1.2.3 From 8972943c0e002541a7cede3e6612a3f775496fa2 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 10 Jul 2015 03:19:47 -0300 Subject: [media] tuners: Drop owner assignment from i2c_driver i2c_driver does not need to set an owner because i2c_register_driver() will set it. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/e4000.c | 1 - drivers/media/tuners/fc2580.c | 1 - drivers/media/tuners/it913x.c | 1 - drivers/media/tuners/m88rs6000t.c | 1 - drivers/media/tuners/si2157.c | 1 - drivers/media/tuners/tda18212.c | 1 - drivers/media/tuners/tua9001.c | 1 - 7 files changed, 7 deletions(-) diff --git a/drivers/media/tuners/e4000.c b/drivers/media/tuners/e4000.c index 03538f88f488..564a000f503e 100644 --- a/drivers/media/tuners/e4000.c +++ b/drivers/media/tuners/e4000.c @@ -752,7 +752,6 @@ MODULE_DEVICE_TABLE(i2c, e4000_id_table); static struct i2c_driver e4000_driver = { .driver = { - .owner = THIS_MODULE, .name = "e4000", .suppress_bind_attrs = true, }, diff --git a/drivers/media/tuners/fc2580.c b/drivers/media/tuners/fc2580.c index 12f916e53150..f4d4665de168 100644 --- a/drivers/media/tuners/fc2580.c +++ b/drivers/media/tuners/fc2580.c @@ -632,7 +632,6 @@ MODULE_DEVICE_TABLE(i2c, fc2580_id_table); static struct i2c_driver fc2580_driver = { .driver = { - .owner = THIS_MODULE, .name = "fc2580", .suppress_bind_attrs = true, }, diff --git a/drivers/media/tuners/it913x.c b/drivers/media/tuners/it913x.c index a076c87eda7a..5c96da693289 100644 --- a/drivers/media/tuners/it913x.c +++ b/drivers/media/tuners/it913x.c @@ -463,7 +463,6 @@ MODULE_DEVICE_TABLE(i2c, it913x_id_table); static struct i2c_driver it913x_driver = { .driver = { - .owner = THIS_MODULE, .name = "it913x", }, .probe = it913x_probe, diff --git a/drivers/media/tuners/m88rs6000t.c b/drivers/media/tuners/m88rs6000t.c index d4c13fe6e7b3..504bfbc4027a 100644 --- a/drivers/media/tuners/m88rs6000t.c +++ b/drivers/media/tuners/m88rs6000t.c @@ -729,7 +729,6 @@ MODULE_DEVICE_TABLE(i2c, m88rs6000t_id); static struct i2c_driver m88rs6000t_driver = { .driver = { - .owner = THIS_MODULE, .name = "m88rs6000t", }, .probe = m88rs6000t_probe, diff --git a/drivers/media/tuners/si2157.c b/drivers/media/tuners/si2157.c index a6245ef379c4..507382160e5e 100644 --- a/drivers/media/tuners/si2157.c +++ b/drivers/media/tuners/si2157.c @@ -469,7 +469,6 @@ MODULE_DEVICE_TABLE(i2c, si2157_id_table); static struct i2c_driver si2157_driver = { .driver = { - .owner = THIS_MODULE, .name = "si2157", }, .probe = si2157_probe, diff --git a/drivers/media/tuners/tda18212.c b/drivers/media/tuners/tda18212.c index d93e0667b46b..7b8068354fea 100644 --- a/drivers/media/tuners/tda18212.c +++ b/drivers/media/tuners/tda18212.c @@ -277,7 +277,6 @@ MODULE_DEVICE_TABLE(i2c, tda18212_id); static struct i2c_driver tda18212_driver = { .driver = { - .owner = THIS_MODULE, .name = "tda18212", }, .probe = tda18212_probe, diff --git a/drivers/media/tuners/tua9001.c b/drivers/media/tuners/tua9001.c index d4f6ca0c4d92..9d70378fe2d3 100644 --- a/drivers/media/tuners/tua9001.c +++ b/drivers/media/tuners/tua9001.c @@ -267,7 +267,6 @@ MODULE_DEVICE_TABLE(i2c, tua9001_id_table); static struct i2c_driver tua9001_driver = { .driver = { - .owner = THIS_MODULE, .name = "tua9001", .suppress_bind_attrs = true, }, -- cgit v1.2.3 From ebf8dce56a25f77545f43a15a8205a6ef4fd1ac0 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 10 Jul 2015 03:19:48 -0300 Subject: [media] Drop owner assignment from i2c_driver i2c_driver does not need to set an owner because i2c_register_driver() will set it. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/go7007/s2250-board.c | 1 - drivers/media/v4l2-core/tuner-core.c | 1 - 2 files changed, 2 deletions(-) diff --git a/drivers/media/usb/go7007/s2250-board.c b/drivers/media/usb/go7007/s2250-board.c index 5c2a49534d2b..1466db150d82 100644 --- a/drivers/media/usb/go7007/s2250-board.c +++ b/drivers/media/usb/go7007/s2250-board.c @@ -629,7 +629,6 @@ MODULE_DEVICE_TABLE(i2c, s2250_id); static struct i2c_driver s2250_driver = { .driver = { - .owner = THIS_MODULE, .name = "s2250", }, .probe = s2250_probe, diff --git a/drivers/media/v4l2-core/tuner-core.c b/drivers/media/v4l2-core/tuner-core.c index abdcffabcb59..581e21ad6801 100644 --- a/drivers/media/v4l2-core/tuner-core.c +++ b/drivers/media/v4l2-core/tuner-core.c @@ -1366,7 +1366,6 @@ MODULE_DEVICE_TABLE(i2c, tuner_id); static struct i2c_driver tuner_driver = { .driver = { - .owner = THIS_MODULE, .name = "tuner", .pm = &tuner_pm_ops, }, -- cgit v1.2.3 From acda7cf4463ae58e68cebeb0f72599fddfeeebb9 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 10 Jul 2015 03:34:27 -0300 Subject: [media] staging: media: Drop owner assignment from i2c_driver i2c_driver does not need to set an owner because i2c_register_driver() will set it. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/lirc/lirc_zilog.c | 1 - drivers/staging/media/mn88472/mn88472.c | 1 - drivers/staging/media/mn88473/mn88473.c | 1 - 3 files changed, 3 deletions(-) diff --git a/drivers/staging/media/lirc/lirc_zilog.c b/drivers/staging/media/lirc/lirc_zilog.c index 261e27d6b054..d032745081ee 100644 --- a/drivers/staging/media/lirc/lirc_zilog.c +++ b/drivers/staging/media/lirc/lirc_zilog.c @@ -1367,7 +1367,6 @@ static const struct i2c_device_id ir_transceiver_id[] = { static struct i2c_driver driver = { .driver = { - .owner = THIS_MODULE, .name = "Zilog/Hauppauge i2c IR", }, .probe = ir_probe, diff --git a/drivers/staging/media/mn88472/mn88472.c b/drivers/staging/media/mn88472/mn88472.c index a8d45f44765c..cf2e96bcf395 100644 --- a/drivers/staging/media/mn88472/mn88472.c +++ b/drivers/staging/media/mn88472/mn88472.c @@ -561,7 +561,6 @@ MODULE_DEVICE_TABLE(i2c, mn88472_id_table); static struct i2c_driver mn88472_driver = { .driver = { - .owner = THIS_MODULE, .name = "mn88472", }, .probe = mn88472_probe, diff --git a/drivers/staging/media/mn88473/mn88473.c b/drivers/staging/media/mn88473/mn88473.c index f9146a146d07..a222e99935d2 100644 --- a/drivers/staging/media/mn88473/mn88473.c +++ b/drivers/staging/media/mn88473/mn88473.c @@ -507,7 +507,6 @@ MODULE_DEVICE_TABLE(i2c, mn88473_id_table); static struct i2c_driver mn88473_driver = { .driver = { - .owner = THIS_MODULE, .name = "mn88473", }, .probe = mn88473_probe, -- cgit v1.2.3 From a53546751da4099d9802618313f57ee01c2efdc4 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 10 Jul 2015 03:34:26 -0300 Subject: [media] staging: iio: Drop owner assignment from i2c_driver i2c_driver does not need to set an owner because i2c_register_driver() will set it. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/iio/addac/adt7316-i2c.c | 1 - drivers/staging/iio/light/isl29018.c | 1 - drivers/staging/iio/light/isl29028.c | 1 - 3 files changed, 3 deletions(-) diff --git a/drivers/staging/iio/addac/adt7316-i2c.c b/drivers/staging/iio/addac/adt7316-i2c.c index 75ddd4f801a3..78fe0b557280 100644 --- a/drivers/staging/iio/addac/adt7316-i2c.c +++ b/drivers/staging/iio/addac/adt7316-i2c.c @@ -124,7 +124,6 @@ static struct i2c_driver adt7316_driver = { .driver = { .name = "adt7316", .pm = ADT7316_PM_OPS, - .owner = THIS_MODULE, }, .probe = adt7316_i2c_probe, .id_table = adt7316_i2c_id, diff --git a/drivers/staging/iio/light/isl29018.c b/drivers/staging/iio/light/isl29018.c index e646c5d24004..019ba5245c23 100644 --- a/drivers/staging/iio/light/isl29018.c +++ b/drivers/staging/iio/light/isl29018.c @@ -838,7 +838,6 @@ static struct i2c_driver isl29018_driver = { .name = "isl29018", .acpi_match_table = ACPI_PTR(isl29018_acpi_match), .pm = ISL29018_PM_OPS, - .owner = THIS_MODULE, .of_match_table = isl29018_of_match, }, .probe = isl29018_probe, diff --git a/drivers/staging/iio/light/isl29028.c b/drivers/staging/iio/light/isl29028.c index e5b2fdc2334b..cd6f2727aa58 100644 --- a/drivers/staging/iio/light/isl29028.c +++ b/drivers/staging/iio/light/isl29028.c @@ -547,7 +547,6 @@ static struct i2c_driver isl29028_driver = { .class = I2C_CLASS_HWMON, .driver = { .name = "isl29028", - .owner = THIS_MODULE, .of_match_table = isl29028_of_match, }, .probe = isl29028_probe, -- cgit v1.2.3 From f103a9c1a0ea1730fa3398366ffcc41c25ad8c9e Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 10 Jul 2015 03:34:28 -0300 Subject: [media] staging: Drop owner assignment from i2c_driver i2c_driver does not need to set an owner because i2c_register_driver() will set it. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/ste_rmi4/synaptics_i2c_rmi4.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/ste_rmi4/synaptics_i2c_rmi4.c b/drivers/staging/ste_rmi4/synaptics_i2c_rmi4.c index 0f524bb7b41d..1f9ba8beb061 100644 --- a/drivers/staging/ste_rmi4/synaptics_i2c_rmi4.c +++ b/drivers/staging/ste_rmi4/synaptics_i2c_rmi4.c @@ -1126,7 +1126,6 @@ MODULE_DEVICE_TABLE(i2c, synaptics_rmi4_id_table); static struct i2c_driver synaptics_rmi4_driver = { .driver = { .name = DRIVER_NAME, - .owner = THIS_MODULE, .pm = &synaptics_rmi4_dev_pm_ops, }, .probe = synaptics_rmi4_probe, -- cgit v1.2.3 From dd6ff6a05b3d467acff47131759e15d40933656c Mon Sep 17 00:00:00 2001 From: David Härdeman Date: Wed, 22 Jul 2015 17:55:24 -0300 Subject: [media] rc-core: improve the lirc protocol reporting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 275ddb40bcf686d210d86c6718e42425a6a0bc76 removed the lirc "protocol" but kept backwards compatibility by always listing the protocol as present and enabled. This patch further improves the logic by only listing the protocol if the lirc module is loaded (or if lirc is builtin). Signed-off-by: David Härdeman Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/rc-main.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index c80816561181..d08cedb03c74 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -828,6 +828,23 @@ struct rc_filter_attribute { .mask = (_mask), \ } +static bool lirc_is_present(void) +{ +#if defined(CONFIG_LIRC_MODULE) + struct module *lirc; + + mutex_lock(&module_mutex); + lirc = find_module("lirc_dev"); + mutex_unlock(&module_mutex); + + return lirc ? true : false; +#elif defined(CONFIG_LIRC) + return true; +#else + return false; +#endif +} + /** * show_protocols() - shows the current/wakeup IR protocol(s) * @device: the device descriptor @@ -884,7 +901,7 @@ static ssize_t show_protocols(struct device *device, allowed &= ~proto_names[i].type; } - if (dev->driver_type == RC_DRIVER_IR_RAW) + if (dev->driver_type == RC_DRIVER_IR_RAW && lirc_is_present()) tmp += sprintf(tmp, "[lirc] "); if (tmp != buf) -- cgit v1.2.3 From 3139f43f8eca4d3a8a0ffe3b0b9cd8ebe0e3be95 Mon Sep 17 00:00:00 2001 From: Christian Löpke Date: Sat, 18 Jul 2015 04:15:01 -0300 Subject: [media] Technisat SkyStar USB HD,(DVB-S/S2) too much URBs for arm devices Using 8 URBs results in a consecutive buffer allocation of too much memory for some arm devices. As we use isochronuous transfers the number of URBs can be reduced without risking data-loss. Signed-off-by: Christian Loepke Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb/technisat-usb2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/dvb-usb/technisat-usb2.c b/drivers/media/usb/dvb-usb/technisat-usb2.c index 03f334d3a8f4..6c3c47722955 100644 --- a/drivers/media/usb/dvb-usb/technisat-usb2.c +++ b/drivers/media/usb/dvb-usb/technisat-usb2.c @@ -707,7 +707,7 @@ static struct dvb_usb_device_properties technisat_usb2_devices = { .stream = { .type = USB_ISOC, - .count = 8, + .count = 4, .endpoint = 0x2, .u = { .isoc = { -- cgit v1.2.3 From a5d32b358254ff90f5dcd7ae2798b5d40503013a Mon Sep 17 00:00:00 2001 From: Kozlov Sergey Date: Tue, 28 Jul 2015 11:33:00 -0300 Subject: [media] horus3a: Sony Horus3A DVB-S/S2 tuner driver Add DVB-S/S2 frontend driver for Sony Horus3A (CXD2832AER) chip Signed-off-by: Kozlov Sergey Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 9 + drivers/media/dvb-frontends/Kconfig | 7 + drivers/media/dvb-frontends/Makefile | 1 + drivers/media/dvb-frontends/horus3a.c | 421 ++++++++++++++++++++++++++++++++++ drivers/media/dvb-frontends/horus3a.h | 58 +++++ 5 files changed, 496 insertions(+) create mode 100644 drivers/media/dvb-frontends/horus3a.c create mode 100644 drivers/media/dvb-frontends/horus3a.h diff --git a/MAINTAINERS b/MAINTAINERS index c14c2315e822..13508a5acdce 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6601,6 +6601,15 @@ S: Supported F: Documentation/devicetree/bindings/media/renesas,vsp1.txt F: drivers/media/platform/vsp1/ +MEDIA DRIVERS FOR HORUS3A +M: Sergey Kozlov +L: linux-media@vger.kernel.org +W: http://linuxtv.org/ +W: http://netup.tv/ +T: git git://linuxtv.org/media_tree.git +S: Supported +F: drivers/media/dvb-frontends/horus3a* + MEDIA INPUT INFRASTRUCTURE (V4L/DVB) M: Mauro Carvalho Chehab P: LinuxTV.org Project diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig index eb8ece64559d..522e7dd9af3d 100644 --- a/drivers/media/dvb-frontends/Kconfig +++ b/drivers/media/dvb-frontends/Kconfig @@ -816,6 +816,13 @@ config DVB_AF9033 depends on DVB_CORE && I2C default m if !MEDIA_SUBDRV_AUTOSELECT +config DVB_HORUS3A + tristate "Sony Horus3A tuner" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + Say Y when you want to support this frontend. + comment "Tools to develop new frontends" config DVB_DUMMY_FE diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile index ebab1b83e1fc..c1a820f15901 100644 --- a/drivers/media/dvb-frontends/Makefile +++ b/drivers/media/dvb-frontends/Makefile @@ -118,3 +118,4 @@ obj-$(CONFIG_DVB_M88RS2000) += m88rs2000.o obj-$(CONFIG_DVB_AF9033) += af9033.o obj-$(CONFIG_DVB_AS102_FE) += as102_fe.o obj-$(CONFIG_DVB_TC90522) += tc90522.o +obj-$(CONFIG_DVB_HORUS3A) += horus3a.o diff --git a/drivers/media/dvb-frontends/horus3a.c b/drivers/media/dvb-frontends/horus3a.c new file mode 100644 index 000000000000..46a82dc586d8 --- /dev/null +++ b/drivers/media/dvb-frontends/horus3a.c @@ -0,0 +1,421 @@ +/* + * horus3a.h + * + * Sony Horus3A DVB-S/S2 tuner driver + * + * Copyright 2012 Sony Corporation + * Copyright (C) 2014 NetUP Inc. + * Copyright (C) 2014 Sergey Kozlov + * Copyright (C) 2014 Abylay Ospan + * + * 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 +#include +#include +#include +#include "horus3a.h" +#include "dvb_frontend.h" + +enum horus3a_state { + STATE_UNKNOWN, + STATE_SLEEP, + STATE_ACTIVE +}; + +struct horus3a_priv { + u32 frequency; + u8 i2c_address; + struct i2c_adapter *i2c; + enum horus3a_state state; + void *set_tuner_data; + int (*set_tuner)(void *, int); +}; + +static void horus3a_i2c_debug(struct horus3a_priv *priv, + u8 reg, u8 write, const u8 *data, u32 len) +{ + dev_dbg(&priv->i2c->dev, "horus3a: I2C %s reg 0x%02x size %d\n", + (write == 0 ? "read" : "write"), reg, len); + print_hex_dump_bytes("horus3a: I2C data: ", + DUMP_PREFIX_OFFSET, data, len); +} + +static int horus3a_write_regs(struct horus3a_priv *priv, + u8 reg, const u8 *data, u32 len) +{ + int ret; + u8 buf[len+1]; + struct i2c_msg msg[1] = { + { + .addr = priv->i2c_address, + .flags = 0, + .len = sizeof(buf), + .buf = buf, + } + }; + + horus3a_i2c_debug(priv, reg, 1, data, len); + buf[0] = reg; + memcpy(&buf[1], data, len); + ret = i2c_transfer(priv->i2c, msg, 1); + if (ret >= 0 && ret != 1) + ret = -EREMOTEIO; + if (ret < 0) { + dev_warn(&priv->i2c->dev, + "%s: i2c wr failed=%d reg=%02x len=%d\n", + KBUILD_MODNAME, ret, reg, len); + return ret; + } + return 0; +} + +static int horus3a_write_reg(struct horus3a_priv *priv, u8 reg, u8 val) +{ + return horus3a_write_regs(priv, reg, &val, 1); +} + +static int horus3a_enter_power_save(struct horus3a_priv *priv) +{ + u8 data[2]; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state == STATE_SLEEP) + return 0; + /* IQ Generator disable */ + horus3a_write_reg(priv, 0x2a, 0x79); + /* MDIV_EN = 0 */ + horus3a_write_reg(priv, 0x29, 0x70); + /* VCO disable preparation */ + horus3a_write_reg(priv, 0x28, 0x3e); + /* VCO buffer disable */ + horus3a_write_reg(priv, 0x2a, 0x19); + /* VCO calibration disable */ + horus3a_write_reg(priv, 0x1c, 0x00); + /* Power save setting (xtal is not stopped) */ + data[0] = 0xC0; + /* LNA is Disabled */ + data[1] = 0xA7; + /* 0x11 - 0x12 */ + horus3a_write_regs(priv, 0x11, data, sizeof(data)); + priv->state = STATE_SLEEP; + return 0; +} + +static int horus3a_leave_power_save(struct horus3a_priv *priv) +{ + u8 data[2]; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state == STATE_ACTIVE) + return 0; + /* Leave power save */ + data[0] = 0x00; + /* LNA is Disabled */ + data[1] = 0xa7; + /* 0x11 - 0x12 */ + horus3a_write_regs(priv, 0x11, data, sizeof(data)); + /* VCO buffer enable */ + horus3a_write_reg(priv, 0x2a, 0x79); + /* VCO calibration enable */ + horus3a_write_reg(priv, 0x1c, 0xc0); + /* MDIV_EN = 1 */ + horus3a_write_reg(priv, 0x29, 0x71); + usleep_range(5000, 7000); + priv->state = STATE_ACTIVE; + return 0; +} + +static int horus3a_init(struct dvb_frontend *fe) +{ + struct horus3a_priv *priv = fe->tuner_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + return 0; +} + +static int horus3a_release(struct dvb_frontend *fe) +{ + struct horus3a_priv *priv = fe->tuner_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; + return 0; +} + +static int horus3a_sleep(struct dvb_frontend *fe) +{ + struct horus3a_priv *priv = fe->tuner_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + horus3a_enter_power_save(priv); + return 0; +} + +static int horus3a_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct horus3a_priv *priv = fe->tuner_priv; + u32 frequency = p->frequency; + u32 symbol_rate = p->symbol_rate/1000; + u8 mixdiv = 0; + u8 mdiv = 0; + u32 ms = 0; + u8 f_ctl = 0; + u8 g_ctl = 0; + u8 fc_lpf = 0; + u8 data[5]; + + dev_dbg(&priv->i2c->dev, "%s(): frequency %dkHz symbol_rate %dksps\n", + __func__, frequency, symbol_rate); + if (priv->set_tuner) + priv->set_tuner(priv->set_tuner_data, 0); + if (priv->state == STATE_SLEEP) + horus3a_leave_power_save(priv); + + /* frequency should be X MHz (X : integer) */ + frequency = DIV_ROUND_CLOSEST(frequency, 1000) * 1000; + if (frequency <= 1155000) { + mixdiv = 4; + mdiv = 1; + } else { + mixdiv = 2; + mdiv = 0; + } + /* Assumed that fREF == 1MHz (1000kHz) */ + ms = DIV_ROUND_CLOSEST((frequency * mixdiv) / 2, 1000); + if (ms > 0x7FFF) { /* 15 bit */ + dev_err(&priv->i2c->dev, "horus3a: invalid frequency %d\n", + frequency); + return -EINVAL; + } + if (frequency < 975000) { + /* F_CTL=11100 G_CTL=001 */ + f_ctl = 0x1C; + g_ctl = 0x01; + } else if (frequency < 1050000) { + /* F_CTL=11000 G_CTL=010 */ + f_ctl = 0x18; + g_ctl = 0x02; + } else if (frequency < 1150000) { + /* F_CTL=10100 G_CTL=010 */ + f_ctl = 0x14; + g_ctl = 0x02; + } else if (frequency < 1250000) { + /* F_CTL=10000 G_CTL=011 */ + f_ctl = 0x10; + g_ctl = 0x03; + } else if (frequency < 1350000) { + /* F_CTL=01100 G_CTL=100 */ + f_ctl = 0x0C; + g_ctl = 0x04; + } else if (frequency < 1450000) { + /* F_CTL=01010 G_CTL=100 */ + f_ctl = 0x0A; + g_ctl = 0x04; + } else if (frequency < 1600000) { + /* F_CTL=00111 G_CTL=101 */ + f_ctl = 0x07; + g_ctl = 0x05; + } else if (frequency < 1800000) { + /* F_CTL=00100 G_CTL=010 */ + f_ctl = 0x04; + g_ctl = 0x02; + } else if (frequency < 2000000) { + /* F_CTL=00010 G_CTL=001 */ + f_ctl = 0x02; + g_ctl = 0x01; + } else { + /* F_CTL=00000 G_CTL=000 */ + f_ctl = 0x00; + g_ctl = 0x00; + } + /* LPF cutoff frequency setting */ + if (p->delivery_system == SYS_DVBS) { + /* + * rolloff = 0.35 + * SR <= 4.3 + * fc_lpf = 5 + * 4.3 < SR <= 10 + * fc_lpf = SR * (1 + rolloff) / 2 + SR / 2 = + * SR * 1.175 = SR * (47/40) + * 10 < SR + * fc_lpf = SR * (1 + rolloff) / 2 + 5 = + * SR * 0.675 + 5 = SR * (27/40) + 5 + * NOTE: The result should be round up. + */ + if (symbol_rate <= 4300) + fc_lpf = 5; + else if (symbol_rate <= 10000) + fc_lpf = (u8)DIV_ROUND_UP(symbol_rate * 47, 40000); + else + fc_lpf = (u8)DIV_ROUND_UP(symbol_rate * 27, 40000) + 5; + /* 5 <= fc_lpf <= 36 */ + if (fc_lpf > 36) + fc_lpf = 36; + } else if (p->delivery_system == SYS_DVBS2) { + int rolloff; + + switch (p->rolloff) { + case ROLLOFF_35: + rolloff = 35; + break; + case ROLLOFF_25: + rolloff = 25; + break; + case ROLLOFF_20: + rolloff = 20; + break; + case ROLLOFF_AUTO: + dev_err(&priv->i2c->dev, + "horus3a: auto roll-off is not supported\n"); + return -EINVAL; + } + /* + * SR <= 4.5: + * fc_lpf = 5 + * 4.5 < SR <= 10: + * fc_lpf = SR * (1 + rolloff) / 2 + SR / 2 + * 10 < SR: + * fc_lpf = SR * (1 + rolloff) / 2 + 5 + * NOTE: The result should be round up. + */ + if (symbol_rate <= 4500) + fc_lpf = 5; + else if (symbol_rate <= 10000) + fc_lpf = (u8)DIV_ROUND_UP( + symbol_rate * (200 + rolloff), 200000); + else + fc_lpf = (u8)DIV_ROUND_UP( + symbol_rate * (100 + rolloff), 200000) + 5; + /* 5 <= fc_lpf <= 36 is valid */ + if (fc_lpf > 36) + fc_lpf = 36; + } else { + dev_err(&priv->i2c->dev, + "horus3a: invalid delivery system %d\n", + p->delivery_system); + return -EINVAL; + } + /* 0x00 - 0x04 */ + data[0] = (u8)((ms >> 7) & 0xFF); + data[1] = (u8)((ms << 1) & 0xFF); + data[2] = 0x00; + data[3] = 0x00; + data[4] = (u8)(mdiv << 7); + horus3a_write_regs(priv, 0x00, data, sizeof(data)); + /* Write G_CTL, F_CTL */ + horus3a_write_reg(priv, 0x09, (u8)((g_ctl << 5) | f_ctl)); + /* Write LPF cutoff frequency */ + horus3a_write_reg(priv, 0x37, (u8)(0x80 | (fc_lpf << 1))); + /* Start Calibration */ + horus3a_write_reg(priv, 0x05, 0x80); + /* IQ Generator enable */ + horus3a_write_reg(priv, 0x2a, 0x7b); + /* tuner stabilization time */ + msleep(60); + /* Store tuned frequency to the struct */ + priv->frequency = ms * 2 * 1000 / mixdiv; + return 0; +} + +static int horus3a_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct horus3a_priv *priv = fe->tuner_priv; + + *frequency = priv->frequency; + return 0; +} + +static struct dvb_tuner_ops horus3a_tuner_ops = { + .info = { + .name = "Sony Horus3a", + .frequency_min = 950000, + .frequency_max = 2150000, + .frequency_step = 1000, + }, + .init = horus3a_init, + .release = horus3a_release, + .sleep = horus3a_sleep, + .set_params = horus3a_set_params, + .get_frequency = horus3a_get_frequency, +}; + +struct dvb_frontend *horus3a_attach(struct dvb_frontend *fe, + const struct horus3a_config *config, + struct i2c_adapter *i2c) +{ + u8 buf[3], val; + struct horus3a_priv *priv = NULL; + + priv = kzalloc(sizeof(struct horus3a_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + priv->i2c_address = (config->i2c_address >> 1); + priv->i2c = i2c; + priv->set_tuner_data = config->set_tuner_priv; + priv->set_tuner = config->set_tuner_callback; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + /* wait 4ms after power on */ + usleep_range(4000, 6000); + /* IQ Generator disable */ + horus3a_write_reg(priv, 0x2a, 0x79); + /* REF_R = Xtal Frequency */ + buf[0] = config->xtal_freq_mhz; + buf[1] = config->xtal_freq_mhz; + buf[2] = 0; + /* 0x6 - 0x8 */ + horus3a_write_regs(priv, 0x6, buf, 3); + /* IQ Out = Single Ended */ + horus3a_write_reg(priv, 0x0a, 0x40); + switch (config->xtal_freq_mhz) { + case 27: + val = 0x1f; + break; + case 24: + val = 0x10; + break; + case 16: + val = 0xc; + break; + default: + val = 0; + dev_warn(&priv->i2c->dev, + "horus3a: invalid xtal frequency %dMHz\n", + config->xtal_freq_mhz); + break; + } + val <<= 2; + horus3a_write_reg(priv, 0x0e, val); + horus3a_enter_power_save(priv); + usleep_range(3000, 5000); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + memcpy(&fe->ops.tuner_ops, &horus3a_tuner_ops, + sizeof(struct dvb_tuner_ops)); + fe->tuner_priv = priv; + dev_info(&priv->i2c->dev, + "Sony HORUS3A attached on addr=%x at I2C adapter %p\n", + priv->i2c_address, priv->i2c); + return fe; +} +EXPORT_SYMBOL(horus3a_attach); + +MODULE_DESCRIPTION("Sony HORUS3A sattelite tuner driver"); +MODULE_AUTHOR("Sergey Kozlov "); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/horus3a.h b/drivers/media/dvb-frontends/horus3a.h new file mode 100644 index 000000000000..b055319d532e --- /dev/null +++ b/drivers/media/dvb-frontends/horus3a.h @@ -0,0 +1,58 @@ +/* + * horus3a.h + * + * Sony Horus3A DVB-S/S2 tuner driver + * + * Copyright 2012 Sony Corporation + * Copyright (C) 2014 NetUP Inc. + * Copyright (C) 2014 Sergey Kozlov + * Copyright (C) 2014 Abylay Ospan + * + * 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. + */ + +#ifndef __DVB_HORUS3A_H__ +#define __DVB_HORUS3A_H__ + +#include +#include +#include + +/** + * struct horus3a_config - the configuration of Horus3A tuner driver + * @i2c_address: I2C address of the tuner + * @xtal_freq_mhz: Oscillator frequency, MHz + * @set_tuner_priv: Callback function private context + * @set_tuner_callback: Callback function that notifies the parent driver + * which tuner is active now + */ +struct horus3a_config { + u8 i2c_address; + u8 xtal_freq_mhz; + void *set_tuner_priv; + int (*set_tuner_callback)(void *, int); +}; + +#if IS_REACHABLE(CONFIG_DVB_HORUS3A) +extern struct dvb_frontend *horus3a_attach(struct dvb_frontend *fe, + const struct horus3a_config *config, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *horus3a_attach( + const struct cxd2820r_config *config, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif -- cgit v1.2.3 From dacf9ce80b41667abb51a2a751a2dfe30e1f9a2b Mon Sep 17 00:00:00 2001 From: Kozlov Sergey Date: Tue, 28 Jul 2015 11:33:01 -0300 Subject: [media] ascot2e: Sony Ascot2e DVB-C/T/T2 tuner driver Add DVB-T/T2/C frontend driver for Sony Ascot2e (CXD2861ER) chip. Signed-off-by: Kozlov Sergey Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 9 + drivers/media/dvb-frontends/Kconfig | 7 + drivers/media/dvb-frontends/Makefile | 1 + drivers/media/dvb-frontends/ascot2e.c | 540 ++++++++++++++++++++++++++++++++++ drivers/media/dvb-frontends/ascot2e.h | 58 ++++ 5 files changed, 615 insertions(+) create mode 100644 drivers/media/dvb-frontends/ascot2e.c create mode 100644 drivers/media/dvb-frontends/ascot2e.h diff --git a/MAINTAINERS b/MAINTAINERS index 13508a5acdce..1735af359357 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6601,6 +6601,15 @@ S: Supported F: Documentation/devicetree/bindings/media/renesas,vsp1.txt F: drivers/media/platform/vsp1/ +MEDIA DRIVERS FOR ASCOT2E +M: Sergey Kozlov +L: linux-media@vger.kernel.org +W: http://linuxtv.org +W: http://netup.tv/ +T: git git://linuxtv.org/media_tree.git +S: Supported +F: drivers/media/dvb-frontends/ascot2e* + MEDIA DRIVERS FOR HORUS3A M: Sergey Kozlov L: linux-media@vger.kernel.org diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig index 522e7dd9af3d..ec628c482cdf 100644 --- a/drivers/media/dvb-frontends/Kconfig +++ b/drivers/media/dvb-frontends/Kconfig @@ -823,6 +823,13 @@ config DVB_HORUS3A help Say Y when you want to support this frontend. +config DVB_ASCOT2E + tristate "Sony Ascot2E tuner" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + Say Y when you want to support this frontend. + comment "Tools to develop new frontends" config DVB_DUMMY_FE diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile index c1a820f15901..0a1bf69cb79a 100644 --- a/drivers/media/dvb-frontends/Makefile +++ b/drivers/media/dvb-frontends/Makefile @@ -119,3 +119,4 @@ obj-$(CONFIG_DVB_AF9033) += af9033.o obj-$(CONFIG_DVB_AS102_FE) += as102_fe.o obj-$(CONFIG_DVB_TC90522) += tc90522.o obj-$(CONFIG_DVB_HORUS3A) += horus3a.o +obj-$(CONFIG_DVB_ASCOT2E) += ascot2e.o diff --git a/drivers/media/dvb-frontends/ascot2e.c b/drivers/media/dvb-frontends/ascot2e.c new file mode 100644 index 000000000000..ae7e463c2f9b --- /dev/null +++ b/drivers/media/dvb-frontends/ascot2e.c @@ -0,0 +1,540 @@ +/* + * ascot2e.c + * + * Sony Ascot3E DVB-T/T2/C/C2 tuner driver + * + * Copyright 2012 Sony Corporation + * Copyright (C) 2014 NetUP Inc. + * Copyright (C) 2014 Sergey Kozlov + * Copyright (C) 2014 Abylay Ospan + * + * 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 +#include +#include +#include +#include "ascot2e.h" +#include "dvb_frontend.h" + +enum ascot2e_state { + STATE_UNKNOWN, + STATE_SLEEP, + STATE_ACTIVE +}; + +struct ascot2e_priv { + u32 frequency; + u8 i2c_address; + struct i2c_adapter *i2c; + enum ascot2e_state state; + void *set_tuner_data; + int (*set_tuner)(void *, int); +}; + +enum ascot2e_tv_system_t { + ASCOT2E_DTV_DVBT_5, + ASCOT2E_DTV_DVBT_6, + ASCOT2E_DTV_DVBT_7, + ASCOT2E_DTV_DVBT_8, + ASCOT2E_DTV_DVBT2_1_7, + ASCOT2E_DTV_DVBT2_5, + ASCOT2E_DTV_DVBT2_6, + ASCOT2E_DTV_DVBT2_7, + ASCOT2E_DTV_DVBT2_8, + ASCOT2E_DTV_DVBC_6, + ASCOT2E_DTV_DVBC_8, + ASCOT2E_DTV_DVBC2_6, + ASCOT2E_DTV_DVBC2_8, + ASCOT2E_DTV_UNKNOWN +}; + +struct ascot2e_band_sett { + u8 if_out_sel; + u8 agc_sel; + u8 mix_oll; + u8 rf_gain; + u8 if_bpf_gc; + u8 fif_offset; + u8 bw_offset; + u8 bw; + u8 rf_oldet; + u8 if_bpf_f0; +}; + +#define ASCOT2E_AUTO 0xff +#define ASCOT2E_OFFSET(ofs) ((u8)(ofs) & 0x1F) +#define ASCOT2E_BW_6 0x00 +#define ASCOT2E_BW_7 0x01 +#define ASCOT2E_BW_8 0x02 +#define ASCOT2E_BW_1_7 0x03 + +static struct ascot2e_band_sett ascot2e_sett[] = { + { ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06, + ASCOT2E_OFFSET(-8), ASCOT2E_OFFSET(-6), ASCOT2E_BW_6, 0x0B, 0x00 }, + { ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06, + ASCOT2E_OFFSET(-8), ASCOT2E_OFFSET(-6), ASCOT2E_BW_6, 0x0B, 0x00 }, + { ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06, + ASCOT2E_OFFSET(-6), ASCOT2E_OFFSET(-4), ASCOT2E_BW_7, 0x0B, 0x00 }, + { ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06, + ASCOT2E_OFFSET(-4), ASCOT2E_OFFSET(-2), ASCOT2E_BW_8, 0x0B, 0x00 }, + { ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06, + ASCOT2E_OFFSET(-10), ASCOT2E_OFFSET(-16), ASCOT2E_BW_1_7, 0x0B, 0x00 }, + { ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06, + ASCOT2E_OFFSET(-8), ASCOT2E_OFFSET(-6), ASCOT2E_BW_6, 0x0B, 0x00 }, + { ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06, + ASCOT2E_OFFSET(-8), ASCOT2E_OFFSET(-6), ASCOT2E_BW_6, 0x0B, 0x00 }, + { ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06, + ASCOT2E_OFFSET(-6), ASCOT2E_OFFSET(-4), ASCOT2E_BW_7, 0x0B, 0x00 }, + { ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06, + ASCOT2E_OFFSET(-4), ASCOT2E_OFFSET(-2), ASCOT2E_BW_8, 0x0B, 0x00 }, + { ASCOT2E_AUTO, ASCOT2E_AUTO, 0x02, ASCOT2E_AUTO, 0x03, + ASCOT2E_OFFSET(-6), ASCOT2E_OFFSET(-8), ASCOT2E_BW_6, 0x09, 0x00 }, + { ASCOT2E_AUTO, ASCOT2E_AUTO, 0x02, ASCOT2E_AUTO, 0x03, + ASCOT2E_OFFSET(-2), ASCOT2E_OFFSET(-1), ASCOT2E_BW_8, 0x09, 0x00 }, + { ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x01, + ASCOT2E_OFFSET(-6), ASCOT2E_OFFSET(-4), ASCOT2E_BW_6, 0x09, 0x00 }, + { ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x01, + ASCOT2E_OFFSET(-2), ASCOT2E_OFFSET(2), ASCOT2E_BW_8, 0x09, 0x00 } +}; + +static void ascot2e_i2c_debug(struct ascot2e_priv *priv, + u8 reg, u8 write, const u8 *data, u32 len) +{ + dev_dbg(&priv->i2c->dev, "ascot2e: I2C %s reg 0x%02x size %d\n", + (write == 0 ? "read" : "write"), reg, len); + print_hex_dump_bytes("ascot2e: I2C data: ", + DUMP_PREFIX_OFFSET, data, len); +} + +static int ascot2e_write_regs(struct ascot2e_priv *priv, + u8 reg, const u8 *data, u32 len) +{ + int ret; + u8 buf[len+1]; + struct i2c_msg msg[1] = { + { + .addr = priv->i2c_address, + .flags = 0, + .len = sizeof(buf), + .buf = buf, + } + }; + + ascot2e_i2c_debug(priv, reg, 1, data, len); + buf[0] = reg; + memcpy(&buf[1], data, len); + ret = i2c_transfer(priv->i2c, msg, 1); + if (ret >= 0 && ret != 1) + ret = -EREMOTEIO; + if (ret < 0) { + dev_warn(&priv->i2c->dev, + "%s: i2c wr failed=%d reg=%02x len=%d\n", + KBUILD_MODNAME, ret, reg, len); + return ret; + } + return 0; +} + +static int ascot2e_write_reg(struct ascot2e_priv *priv, u8 reg, u8 val) +{ + return ascot2e_write_regs(priv, reg, &val, 1); +} + +static int ascot2e_read_regs(struct ascot2e_priv *priv, + u8 reg, u8 *val, u32 len) +{ + int ret; + struct i2c_msg msg[2] = { + { + .addr = priv->i2c_address, + .flags = 0, + .len = 1, + .buf = ®, + }, { + .addr = priv->i2c_address, + .flags = I2C_M_RD, + .len = len, + .buf = val, + } + }; + + ret = i2c_transfer(priv->i2c, &msg[0], 1); + if (ret >= 0 && ret != 1) + ret = -EREMOTEIO; + if (ret < 0) { + dev_warn(&priv->i2c->dev, + "%s: I2C rw failed=%d addr=%02x reg=%02x\n", + KBUILD_MODNAME, ret, priv->i2c_address, reg); + return ret; + } + ret = i2c_transfer(priv->i2c, &msg[1], 1); + if (ret >= 0 && ret != 1) + ret = -EREMOTEIO; + if (ret < 0) { + dev_warn(&priv->i2c->dev, + "%s: i2c rd failed=%d addr=%02x reg=%02x\n", + KBUILD_MODNAME, ret, priv->i2c_address, reg); + return ret; + } + ascot2e_i2c_debug(priv, reg, 0, val, len); + return 0; +} + +static int ascot2e_read_reg(struct ascot2e_priv *priv, u8 reg, u8 *val) +{ + return ascot2e_read_regs(priv, reg, val, 1); +} + +static int ascot2e_set_reg_bits(struct ascot2e_priv *priv, + u8 reg, u8 data, u8 mask) +{ + int res; + u8 rdata; + + if (mask != 0xff) { + res = ascot2e_read_reg(priv, reg, &rdata); + if (res != 0) + return res; + data = ((data & mask) | (rdata & (mask ^ 0xFF))); + } + return ascot2e_write_reg(priv, reg, data); +} + +static int ascot2e_enter_power_save(struct ascot2e_priv *priv) +{ + u8 data[2]; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state == STATE_SLEEP) + return 0; + data[0] = 0x00; + data[1] = 0x04; + ascot2e_write_regs(priv, 0x14, data, 2); + ascot2e_write_reg(priv, 0x50, 0x01); + priv->state = STATE_SLEEP; + return 0; +} + +static int ascot2e_leave_power_save(struct ascot2e_priv *priv) +{ + u8 data[2] = { 0xFB, 0x0F }; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state == STATE_ACTIVE) + return 0; + ascot2e_write_regs(priv, 0x14, data, 2); + ascot2e_write_reg(priv, 0x50, 0x00); + priv->state = STATE_ACTIVE; + return 0; +} + +static int ascot2e_init(struct dvb_frontend *fe) +{ + struct ascot2e_priv *priv = fe->tuner_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + return ascot2e_leave_power_save(priv); +} + +static int ascot2e_release(struct dvb_frontend *fe) +{ + struct ascot2e_priv *priv = fe->tuner_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; + return 0; +} + +static int ascot2e_sleep(struct dvb_frontend *fe) +{ + struct ascot2e_priv *priv = fe->tuner_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + ascot2e_enter_power_save(priv); + return 0; +} + +static enum ascot2e_tv_system_t ascot2e_get_tv_system(struct dvb_frontend *fe) +{ + enum ascot2e_tv_system_t system = ASCOT2E_DTV_UNKNOWN; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct ascot2e_priv *priv = fe->tuner_priv; + + if (p->delivery_system == SYS_DVBT) { + if (p->bandwidth_hz <= 5000000) + system = ASCOT2E_DTV_DVBT_5; + else if (p->bandwidth_hz <= 6000000) + system = ASCOT2E_DTV_DVBT_6; + else if (p->bandwidth_hz <= 7000000) + system = ASCOT2E_DTV_DVBT_7; + else if (p->bandwidth_hz <= 8000000) + system = ASCOT2E_DTV_DVBT_8; + else { + system = ASCOT2E_DTV_DVBT_8; + p->bandwidth_hz = 8000000; + } + } else if (p->delivery_system == SYS_DVBT2) { + if (p->bandwidth_hz <= 5000000) + system = ASCOT2E_DTV_DVBT2_5; + else if (p->bandwidth_hz <= 6000000) + system = ASCOT2E_DTV_DVBT2_6; + else if (p->bandwidth_hz <= 7000000) + system = ASCOT2E_DTV_DVBT2_7; + else if (p->bandwidth_hz <= 8000000) + system = ASCOT2E_DTV_DVBT2_8; + else { + system = ASCOT2E_DTV_DVBT2_8; + p->bandwidth_hz = 8000000; + } + } else if (p->delivery_system == SYS_DVBC_ANNEX_A) { + if (p->bandwidth_hz <= 6000000) + system = ASCOT2E_DTV_DVBC_6; + else if (p->bandwidth_hz <= 8000000) + system = ASCOT2E_DTV_DVBC_8; + } + dev_dbg(&priv->i2c->dev, + "%s(): ASCOT2E DTV system %d (delsys %d, bandwidth %d)\n", + __func__, (int)system, p->delivery_system, p->bandwidth_hz); + return system; +} + +static int ascot2e_set_params(struct dvb_frontend *fe) +{ + u8 data[10]; + u32 frequency; + enum ascot2e_tv_system_t tv_system; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct ascot2e_priv *priv = fe->tuner_priv; + + dev_dbg(&priv->i2c->dev, "%s(): tune frequency %dkHz\n", + __func__, p->frequency / 1000); + tv_system = ascot2e_get_tv_system(fe); + + if (tv_system == ASCOT2E_DTV_UNKNOWN) { + dev_dbg(&priv->i2c->dev, "%s(): unknown DTV system\n", + __func__); + return -EINVAL; + } + if (priv->set_tuner) + priv->set_tuner(priv->set_tuner_data, 1); + frequency = roundup(p->frequency / 1000, 25); + if (priv->state == STATE_SLEEP) + ascot2e_leave_power_save(priv); + + /* IF_OUT_SEL / AGC_SEL setting */ + data[0] = 0x00; + if (ascot2e_sett[tv_system].agc_sel != ASCOT2E_AUTO) { + /* AGC pin setting from parameter table */ + data[0] |= (u8)( + (ascot2e_sett[tv_system].agc_sel & 0x03) << 3); + } + if (ascot2e_sett[tv_system].if_out_sel != ASCOT2E_AUTO) { + /* IFOUT pin setting from parameter table */ + data[0] |= (u8)( + (ascot2e_sett[tv_system].if_out_sel & 0x01) << 2); + } + /* Set bit[4:2] only */ + ascot2e_set_reg_bits(priv, 0x05, data[0], 0x1c); + /* 0x06 - 0x0F */ + /* REF_R setting (0x06) */ + if (tv_system == ASCOT2E_DTV_DVBC_6 || + tv_system == ASCOT2E_DTV_DVBC_8) { + /* xtal, xtal*2 */ + data[0] = (frequency > 500000) ? 16 : 32; + } else { + /* xtal/8, xtal/4 */ + data[0] = (frequency > 500000) ? 2 : 4; + } + /* XOSC_SEL=100uA */ + data[1] = 0x04; + /* KBW setting (0x08), KC0 setting (0x09), KC1 setting (0x0A) */ + if (tv_system == ASCOT2E_DTV_DVBC_6 || + tv_system == ASCOT2E_DTV_DVBC_8) { + data[2] = 18; + data[3] = 120; + data[4] = 20; + } else { + data[2] = 48; + data[3] = 10; + data[4] = 30; + } + /* ORDER/R2_RANGE/R2_BANK/C2_BANK setting (0x0B) */ + if (tv_system == ASCOT2E_DTV_DVBC_6 || + tv_system == ASCOT2E_DTV_DVBC_8) + data[5] = (frequency > 500000) ? 0x08 : 0x0c; + else + data[5] = (frequency > 500000) ? 0x30 : 0x38; + /* Set MIX_OLL (0x0C) value from parameter table */ + data[6] = ascot2e_sett[tv_system].mix_oll; + /* Set RF_GAIN (0x0D) setting from parameter table */ + if (ascot2e_sett[tv_system].rf_gain == ASCOT2E_AUTO) { + /* RF_GAIN auto control enable */ + ascot2e_write_reg(priv, 0x4E, 0x01); + /* RF_GAIN Default value */ + data[7] = 0x00; + } else { + /* RF_GAIN auto control disable */ + ascot2e_write_reg(priv, 0x4E, 0x00); + data[7] = ascot2e_sett[tv_system].rf_gain; + } + /* Set IF_BPF_GC/FIF_OFFSET (0x0E) value from parameter table */ + data[8] = (u8)((ascot2e_sett[tv_system].fif_offset << 3) | + (ascot2e_sett[tv_system].if_bpf_gc & 0x07)); + /* Set BW_OFFSET (0x0F) value from parameter table */ + data[9] = ascot2e_sett[tv_system].bw_offset; + ascot2e_write_regs(priv, 0x06, data, 10); + /* + * 0x45 - 0x47 + * LNA optimization setting + * RF_LNA_DIST1-5, RF_LNA_CM + */ + if (tv_system == ASCOT2E_DTV_DVBC_6 || + tv_system == ASCOT2E_DTV_DVBC_8) { + data[0] = 0x0F; + data[1] = 0x00; + data[2] = 0x01; + } else { + data[0] = 0x0F; + data[1] = 0x00; + data[2] = 0x03; + } + ascot2e_write_regs(priv, 0x45, data, 3); + /* 0x49 - 0x4A + Set RF_OLDET_ENX/RF_OLDET_OLL value from parameter table */ + data[0] = ascot2e_sett[tv_system].rf_oldet; + /* Set IF_BPF_F0 value from parameter table */ + data[1] = ascot2e_sett[tv_system].if_bpf_f0; + ascot2e_write_regs(priv, 0x49, data, 2); + /* + * Tune now + * RFAGC fast mode / RFAGC auto control enable + * (set bit[7], bit[5:4] only) + * vco_cal = 1, set MIX_OL_CPU_EN + */ + ascot2e_set_reg_bits(priv, 0x0c, 0x90, 0xb0); + /* Logic wake up, CPU wake up */ + data[0] = 0xc4; + data[1] = 0x40; + ascot2e_write_regs(priv, 0x03, data, 2); + /* 0x10 - 0x14 */ + data[0] = (u8)(frequency & 0xFF); /* 0x10: FRF_L */ + data[1] = (u8)((frequency >> 8) & 0xFF); /* 0x11: FRF_M */ + data[2] = (u8)((frequency >> 16) & 0x0F); /* 0x12: FRF_H (bit[3:0]) */ + /* 0x12: BW (bit[5:4]) */ + data[2] |= (u8)(ascot2e_sett[tv_system].bw << 4); + data[3] = 0xFF; /* 0x13: VCO calibration enable */ + data[4] = 0xFF; /* 0x14: Analog block enable */ + /* Tune (Burst write) */ + ascot2e_write_regs(priv, 0x10, data, 5); + msleep(50); + /* CPU deep sleep */ + ascot2e_write_reg(priv, 0x04, 0x00); + /* Logic sleep */ + ascot2e_write_reg(priv, 0x03, 0xC0); + /* RFAGC normal mode (set bit[5:4] only) */ + ascot2e_set_reg_bits(priv, 0x0C, 0x00, 0x30); + priv->frequency = frequency; + return 0; +} + +static int ascot2e_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct ascot2e_priv *priv = fe->tuner_priv; + + *frequency = priv->frequency * 1000; + return 0; +} + +static struct dvb_tuner_ops ascot2e_tuner_ops = { + .info = { + .name = "Sony ASCOT2E", + .frequency_min = 1000000, + .frequency_max = 1200000000, + .frequency_step = 25000, + }, + .init = ascot2e_init, + .release = ascot2e_release, + .sleep = ascot2e_sleep, + .set_params = ascot2e_set_params, + .get_frequency = ascot2e_get_frequency, +}; + +struct dvb_frontend *ascot2e_attach(struct dvb_frontend *fe, + const struct ascot2e_config *config, + struct i2c_adapter *i2c) +{ + u8 data[4]; + struct ascot2e_priv *priv = NULL; + + priv = kzalloc(sizeof(struct ascot2e_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + priv->i2c_address = (config->i2c_address >> 1); + priv->i2c = i2c; + priv->set_tuner_data = config->set_tuner_priv; + priv->set_tuner = config->set_tuner_callback; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + /* 16 MHz xTal frequency */ + data[0] = 16; + /* VCO current setting */ + data[1] = 0x06; + /* Logic wake up, CPU boot */ + data[2] = 0xC4; + data[3] = 0x40; + ascot2e_write_regs(priv, 0x01, data, 4); + /* RFVGA optimization setting (RF_DIST0 - RF_DIST2) */ + data[0] = 0x10; + data[1] = 0x3F; + data[2] = 0x25; + ascot2e_write_regs(priv, 0x22, data, 3); + /* PLL mode setting */ + ascot2e_write_reg(priv, 0x28, 0x1e); + /* RSSI setting */ + ascot2e_write_reg(priv, 0x59, 0x04); + /* TODO check CPU HW error state here */ + msleep(80); + /* Xtal oscillator current control setting */ + ascot2e_write_reg(priv, 0x4c, 0x01); + /* XOSC_SEL=100uA */ + ascot2e_write_reg(priv, 0x07, 0x04); + /* CPU deep sleep */ + ascot2e_write_reg(priv, 0x04, 0x00); + /* Logic sleep */ + ascot2e_write_reg(priv, 0x03, 0xc0); + /* Power save setting */ + data[0] = 0x00; + data[1] = 0x04; + ascot2e_write_regs(priv, 0x14, data, 2); + ascot2e_write_reg(priv, 0x50, 0x01); + priv->state = STATE_SLEEP; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + memcpy(&fe->ops.tuner_ops, &ascot2e_tuner_ops, + sizeof(struct dvb_tuner_ops)); + fe->tuner_priv = priv; + dev_info(&priv->i2c->dev, + "Sony ASCOT2E attached on addr=%x at I2C adapter %p\n", + priv->i2c_address, priv->i2c); + return fe; +} +EXPORT_SYMBOL(ascot2e_attach); + +MODULE_DESCRIPTION("Sony ASCOT2E terr/cab tuner driver"); +MODULE_AUTHOR("info@netup.ru"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/ascot2e.h b/drivers/media/dvb-frontends/ascot2e.h new file mode 100644 index 000000000000..6da4ae6d6cc3 --- /dev/null +++ b/drivers/media/dvb-frontends/ascot2e.h @@ -0,0 +1,58 @@ +/* + * ascot2e.h + * + * Sony Ascot3E DVB-T/T2/C/C2 tuner driver + * + * Copyright 2012 Sony Corporation + * Copyright (C) 2014 NetUP Inc. + * Copyright (C) 2014 Sergey Kozlov + * Copyright (C) 2014 Abylay Ospan + * + * 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. + */ + +#ifndef __DVB_ASCOT2E_H__ +#define __DVB_ASCOT2E_H__ + +#include +#include +#include + +/** + * struct ascot2e_config - the configuration of Ascot2E tuner driver + * @i2c_address: I2C address of the tuner + * @xtal_freq_mhz: Oscillator frequency, MHz + * @set_tuner_priv: Callback function private context + * @set_tuner_callback: Callback function that notifies the parent driver + * which tuner is active now + */ +struct ascot2e_config { + u8 i2c_address; + u8 xtal_freq_mhz; + void *set_tuner_priv; + int (*set_tuner_callback)(void *, int); +}; + +#if IS_REACHABLE(CONFIG_DVB_ASCOT2E) +extern struct dvb_frontend *ascot2e_attach(struct dvb_frontend *fe, + const struct ascot2e_config *config, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *ascot2e_attach(struct dvb_frontend *fe, + const struct ascot2e_config *config, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif -- cgit v1.2.3 From e025273b86fb4a6440192b809e05332777c3faa5 Mon Sep 17 00:00:00 2001 From: Kozlov Sergey Date: Tue, 28 Jul 2015 11:33:02 -0300 Subject: [media] lnbh25: LNBH25 SEC controller driver Add DVB SEC frontend driver for STM LNBH25PQR chip. [mchehab@osg.samsung.com: fix merge conflict: fe_sec_voltage_t should not be used in kernelspace anymore. instead, it should use enum fe_sec_voltage] Signed-off-by: Kozlov Sergey Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 9 ++ drivers/media/dvb-frontends/Kconfig | 8 ++ drivers/media/dvb-frontends/Makefile | 1 + drivers/media/dvb-frontends/lnbh25.c | 189 +++++++++++++++++++++++++++++++++++ drivers/media/dvb-frontends/lnbh25.h | 56 +++++++++++ 5 files changed, 263 insertions(+) create mode 100644 drivers/media/dvb-frontends/lnbh25.c create mode 100644 drivers/media/dvb-frontends/lnbh25.h diff --git a/MAINTAINERS b/MAINTAINERS index 1735af359357..a8ef03c90105 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6619,6 +6619,15 @@ T: git git://linuxtv.org/media_tree.git S: Supported F: drivers/media/dvb-frontends/horus3a* +MEDIA DRIVERS FOR LNBH25 +M: Sergey Kozlov +L: linux-media@vger.kernel.org +W: http://linuxtv.org/ +W: http://netup.tv/ +T: git git://linuxtv.org/media_tree.git +S: Supported +F: drivers/media/dvb-frontends/lnbh25* + MEDIA INPUT INFRASTRUCTURE (V4L/DVB) M: Mauro Carvalho Chehab P: LinuxTV.org Project diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig index ec628c482cdf..82b21b92f33b 100644 --- a/drivers/media/dvb-frontends/Kconfig +++ b/drivers/media/dvb-frontends/Kconfig @@ -713,6 +713,14 @@ comment "SEC control devices for DVB-S" source "drivers/media/dvb-frontends/drx39xyj/Kconfig" +config DVB_LNBH25 + tristate "LNBH25 SEC controller" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + An SEC control chip. + Say Y when you want to support this chip. + config DVB_LNBP21 tristate "LNBP21/LNBH24 SEC controllers" depends on DVB_CORE && I2C diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile index 0a1bf69cb79a..07d962a29763 100644 --- a/drivers/media/dvb-frontends/Makefile +++ b/drivers/media/dvb-frontends/Makefile @@ -57,6 +57,7 @@ obj-$(CONFIG_DVB_LGDT3305) += lgdt3305.o obj-$(CONFIG_DVB_LGDT3306A) += lgdt3306a.o obj-$(CONFIG_DVB_LG2160) += lg2160.o obj-$(CONFIG_DVB_CX24123) += cx24123.o +obj-$(CONFIG_DVB_LNBH25) += lnbh25.o obj-$(CONFIG_DVB_LNBP21) += lnbp21.o obj-$(CONFIG_DVB_LNBP22) += lnbp22.o obj-$(CONFIG_DVB_ISL6405) += isl6405.o diff --git a/drivers/media/dvb-frontends/lnbh25.c b/drivers/media/dvb-frontends/lnbh25.c new file mode 100644 index 000000000000..ef3021e964be --- /dev/null +++ b/drivers/media/dvb-frontends/lnbh25.c @@ -0,0 +1,189 @@ +/* + * lnbh25.c + * + * Driver for LNB supply and control IC LNBH25 + * + * Copyright (C) 2014 NetUP Inc. + * Copyright (C) 2014 Sergey Kozlov + * Copyright (C) 2014 Abylay Ospan + * + * 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 +#include +#include +#include + +#include "dvb_frontend.h" +#include "lnbh25.h" + +/** + * struct lnbh25_priv - LNBH25 driver private data + * @i2c: pointer to the I2C adapter structure + * @i2c_address: I2C address of LNBH25 SEC chip + * @config: Registers configuration: + * offset 0: 1st register address, always 0x02 (DATA1) + * offset 1: DATA1 register value + * offset 2: DATA2 register value + */ +struct lnbh25_priv { + struct i2c_adapter *i2c; + u8 i2c_address; + u8 config[3]; +}; + +#define LNBH25_STATUS_OFL 0x1 +#define LNBH25_STATUS_VMON 0x4 +#define LNBH25_VSEL_13 0x03 +#define LNBH25_VSEL_18 0x0a + +static int lnbh25_read_vmon(struct lnbh25_priv *priv) +{ + int i, ret; + u8 addr = 0x00; + u8 status[6]; + struct i2c_msg msg[2] = { + { + .addr = priv->i2c_address, + .flags = 0, + .len = 1, + .buf = &addr + }, { + .addr = priv->i2c_address, + .flags = I2C_M_RD, + .len = sizeof(status), + .buf = status + } + }; + + for (i = 0; i < 2; i++) { + ret = i2c_transfer(priv->i2c, &msg[i], 1); + if (ret >= 0 && ret != 1) + ret = -EIO; + if (ret < 0) { + dev_dbg(&priv->i2c->dev, + "%s(): I2C transfer %d failed (%d)\n", + __func__, i, ret); + return ret; + } + } + print_hex_dump_bytes("lnbh25_read_vmon: ", + DUMP_PREFIX_OFFSET, status, sizeof(status)); + if ((status[0] & (LNBH25_STATUS_OFL | LNBH25_STATUS_VMON)) != 0) { + dev_err(&priv->i2c->dev, + "%s(): voltage in failure state, status reg 0x%x\n", + __func__, status[0]); + return -EIO; + } + return 0; +} + +static int lnbh25_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage voltage) +{ + int ret; + u8 data1_reg; + const char *vsel; + struct lnbh25_priv *priv = fe->sec_priv; + struct i2c_msg msg = { + .addr = priv->i2c_address, + .flags = 0, + .len = sizeof(priv->config), + .buf = priv->config + }; + + switch (voltage) { + case SEC_VOLTAGE_OFF: + data1_reg = 0x00; + vsel = "Off"; + break; + case SEC_VOLTAGE_13: + data1_reg = LNBH25_VSEL_13; + vsel = "13V"; + break; + case SEC_VOLTAGE_18: + data1_reg = LNBH25_VSEL_18; + vsel = "18V"; + break; + default: + return -EINVAL; + } + priv->config[1] = data1_reg; + dev_dbg(&priv->i2c->dev, + "%s(): %s, I2C 0x%x write [ %02x %02x %02x ]\n", + __func__, vsel, priv->i2c_address, + priv->config[0], priv->config[1], priv->config[2]); + ret = i2c_transfer(priv->i2c, &msg, 1); + if (ret >= 0 && ret != 1) + ret = -EIO; + if (ret < 0) { + dev_err(&priv->i2c->dev, "%s(): I2C transfer error (%d)\n", + __func__, ret); + return ret; + } + if (voltage != SEC_VOLTAGE_OFF) { + msleep(120); + ret = lnbh25_read_vmon(priv); + } else { + msleep(20); + ret = 0; + } + return ret; +} + +static void lnbh25_release(struct dvb_frontend *fe) +{ + struct lnbh25_priv *priv = fe->sec_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + lnbh25_set_voltage(fe, SEC_VOLTAGE_OFF); + kfree(fe->sec_priv); + fe->sec_priv = NULL; +} + +struct dvb_frontend *lnbh25_attach(struct dvb_frontend *fe, + struct lnbh25_config *cfg, + struct i2c_adapter *i2c) +{ + struct lnbh25_priv *priv; + + dev_dbg(&i2c->dev, "%s()\n", __func__); + priv = kzalloc(sizeof(struct lnbh25_priv), GFP_KERNEL); + if (!priv) + return NULL; + priv->i2c_address = (cfg->i2c_address >> 1); + priv->i2c = i2c; + priv->config[0] = 0x02; + priv->config[1] = 0x00; + priv->config[2] = cfg->data2_config; + fe->sec_priv = priv; + if (lnbh25_set_voltage(fe, SEC_VOLTAGE_OFF)) { + dev_err(&i2c->dev, + "%s(): no LNBH25 found at I2C addr 0x%02x\n", + __func__, priv->i2c_address); + kfree(priv); + fe->sec_priv = NULL; + return NULL; + } + + fe->ops.release_sec = lnbh25_release; + fe->ops.set_voltage = lnbh25_set_voltage; + + dev_err(&i2c->dev, "%s(): attached at I2C addr 0x%02x\n", + __func__, priv->i2c_address); + return fe; +} +EXPORT_SYMBOL(lnbh25_attach); + +MODULE_DESCRIPTION("ST LNBH25 driver"); +MODULE_AUTHOR("info@netup.ru"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/lnbh25.h b/drivers/media/dvb-frontends/lnbh25.h new file mode 100644 index 000000000000..69f30e21f6b3 --- /dev/null +++ b/drivers/media/dvb-frontends/lnbh25.h @@ -0,0 +1,56 @@ +/* + * lnbh25.c + * + * Driver for LNB supply and control IC LNBH25 + * + * Copyright (C) 2014 NetUP Inc. + * Copyright (C) 2014 Sergey Kozlov + * Copyright (C) 2014 Abylay Ospan + * + * 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. + */ + +#ifndef LNBH25_H +#define LNBH25_H + +#include +#include +#include + +/* 22 kHz tone enabled. Tone output controlled by DSQIN pin */ +#define LNBH25_TEN 0x01 +/* Low power mode activated (used only with 22 kHz tone output disabled) */ +#define LNBH25_LPM 0x02 +/* DSQIN input pin is set to receive external 22 kHz TTL signal source */ +#define LNBH25_EXTM 0x04 + +struct lnbh25_config { + u8 i2c_address; + u8 data2_config; +}; + +#if IS_REACHABLE(CONFIG_DVB_LNBH25) +struct dvb_frontend *lnbh25_attach( + struct dvb_frontend *fe, + struct lnbh25_config *cfg, + struct i2c_adapter *i2c); +#else +static inline dvb_frontend *lnbh25_attach( + struct dvb_frontend *fe, + struct lnbh25_config *cfg, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif -- cgit v1.2.3 From a6dc60ff1209df29ee4668024e93d31f31421932 Mon Sep 17 00:00:00 2001 From: Kozlov Sergey Date: Tue, 28 Jul 2015 11:33:03 -0300 Subject: [media] cxd2841er: Sony CXD2841ER DVB-S/S2/T/T2/C demodulator driver Add DVB-C/T/T2/S/S2 demodulator frontend driver Sony CXD2841ER chip. Signed-off-by: Kozlov Sergey Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 9 + drivers/media/dvb-frontends/Kconfig | 7 + drivers/media/dvb-frontends/Makefile | 1 + drivers/media/dvb-frontends/cxd2841er.c | 2719 ++++++++++++++++++++++++++ drivers/media/dvb-frontends/cxd2841er.h | 65 + drivers/media/dvb-frontends/cxd2841er_priv.h | 43 + 6 files changed, 2844 insertions(+) create mode 100644 drivers/media/dvb-frontends/cxd2841er.c create mode 100644 drivers/media/dvb-frontends/cxd2841er.h create mode 100644 drivers/media/dvb-frontends/cxd2841er_priv.h diff --git a/MAINTAINERS b/MAINTAINERS index a8ef03c90105..805a55d5e700 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6610,6 +6610,15 @@ T: git git://linuxtv.org/media_tree.git S: Supported F: drivers/media/dvb-frontends/ascot2e* +MEDIA DRIVERS FOR CXD2841ER +M: Sergey Kozlov +L: linux-media@vger.kernel.org +W: http://linuxtv.org/ +W: http://netup.tv/ +T: git git://linuxtv.org/media_tree.git +S: Supported +F: drivers/media/dvb-frontends/cxd2841er* + MEDIA DRIVERS FOR HORUS3A M: Sergey Kozlov L: linux-media@vger.kernel.org diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig index 82b21b92f33b..292c9479bb75 100644 --- a/drivers/media/dvb-frontends/Kconfig +++ b/drivers/media/dvb-frontends/Kconfig @@ -451,6 +451,13 @@ config DVB_CXD2820R help Say Y when you want to support this frontend. +config DVB_CXD2841ER + tristate "Sony CXD2841ER" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + Say Y when you want to support this frontend. + config DVB_RTL2830 tristate "Realtek RTL2830 DVB-T" depends on DVB_CORE && I2C && I2C_MUX diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile index 07d962a29763..37ef17b5b995 100644 --- a/drivers/media/dvb-frontends/Makefile +++ b/drivers/media/dvb-frontends/Makefile @@ -106,6 +106,7 @@ obj-$(CONFIG_DVB_MB86A20S) += mb86a20s.o obj-$(CONFIG_DVB_IX2505V) += ix2505v.o obj-$(CONFIG_DVB_STV0367) += stv0367.o obj-$(CONFIG_DVB_CXD2820R) += cxd2820r.o +obj-$(CONFIG_DVB_CXD2841ER) += cxd2841er.o obj-$(CONFIG_DVB_DRXK) += drxk.o obj-$(CONFIG_DVB_TDA18271C2DD) += tda18271c2dd.o obj-$(CONFIG_DVB_SI2165) += si2165.o diff --git a/drivers/media/dvb-frontends/cxd2841er.c b/drivers/media/dvb-frontends/cxd2841er.c new file mode 100644 index 000000000000..d3813cc22770 --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2841er.c @@ -0,0 +1,2719 @@ +/* + * cxd2841er.c + * + * Sony CXD2441ER digital demodulator driver + * + * Copyright 2012 Sony Corporation + * Copyright (C) 2014 NetUP Inc. + * Copyright (C) 2014 Sergey Kozlov + * Copyright (C) 2014 Abylay Ospan + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include "dvb_math.h" +#include "dvb_frontend.h" +#include "cxd2841er.h" +#include "cxd2841er_priv.h" + +enum cxd2841er_state { + STATE_SHUTDOWN = 0, + STATE_SLEEP_S, + STATE_ACTIVE_S, + STATE_SLEEP_TC, + STATE_ACTIVE_TC +}; + +struct cxd2841er_priv { + struct dvb_frontend frontend; + struct i2c_adapter *i2c; + u8 i2c_addr_slvx; + u8 i2c_addr_slvt; + const struct cxd2841er_config *config; + enum cxd2841er_state state; + u8 system; +}; + +static const struct cxd2841er_cnr_data s_cn_data[] = { + { 0x033e, 0 }, { 0x0339, 100 }, { 0x0333, 200 }, + { 0x032e, 300 }, { 0x0329, 400 }, { 0x0324, 500 }, + { 0x031e, 600 }, { 0x0319, 700 }, { 0x0314, 800 }, + { 0x030f, 900 }, { 0x030a, 1000 }, { 0x02ff, 1100 }, + { 0x02f4, 1200 }, { 0x02e9, 1300 }, { 0x02de, 1400 }, + { 0x02d4, 1500 }, { 0x02c9, 1600 }, { 0x02bf, 1700 }, + { 0x02b5, 1800 }, { 0x02ab, 1900 }, { 0x02a1, 2000 }, + { 0x029b, 2100 }, { 0x0295, 2200 }, { 0x0290, 2300 }, + { 0x028a, 2400 }, { 0x0284, 2500 }, { 0x027f, 2600 }, + { 0x0279, 2700 }, { 0x0274, 2800 }, { 0x026e, 2900 }, + { 0x0269, 3000 }, { 0x0262, 3100 }, { 0x025c, 3200 }, + { 0x0255, 3300 }, { 0x024f, 3400 }, { 0x0249, 3500 }, + { 0x0242, 3600 }, { 0x023c, 3700 }, { 0x0236, 3800 }, + { 0x0230, 3900 }, { 0x022a, 4000 }, { 0x0223, 4100 }, + { 0x021c, 4200 }, { 0x0215, 4300 }, { 0x020e, 4400 }, + { 0x0207, 4500 }, { 0x0201, 4600 }, { 0x01fa, 4700 }, + { 0x01f4, 4800 }, { 0x01ed, 4900 }, { 0x01e7, 5000 }, + { 0x01e0, 5100 }, { 0x01d9, 5200 }, { 0x01d2, 5300 }, + { 0x01cb, 5400 }, { 0x01c4, 5500 }, { 0x01be, 5600 }, + { 0x01b7, 5700 }, { 0x01b1, 5800 }, { 0x01aa, 5900 }, + { 0x01a4, 6000 }, { 0x019d, 6100 }, { 0x0196, 6200 }, + { 0x018f, 6300 }, { 0x0189, 6400 }, { 0x0182, 6500 }, + { 0x017c, 6600 }, { 0x0175, 6700 }, { 0x016f, 6800 }, + { 0x0169, 6900 }, { 0x0163, 7000 }, { 0x015c, 7100 }, + { 0x0156, 7200 }, { 0x0150, 7300 }, { 0x014a, 7400 }, + { 0x0144, 7500 }, { 0x013e, 7600 }, { 0x0138, 7700 }, + { 0x0132, 7800 }, { 0x012d, 7900 }, { 0x0127, 8000 }, + { 0x0121, 8100 }, { 0x011c, 8200 }, { 0x0116, 8300 }, + { 0x0111, 8400 }, { 0x010b, 8500 }, { 0x0106, 8600 }, + { 0x0101, 8700 }, { 0x00fc, 8800 }, { 0x00f7, 8900 }, + { 0x00f2, 9000 }, { 0x00ee, 9100 }, { 0x00ea, 9200 }, + { 0x00e6, 9300 }, { 0x00e2, 9400 }, { 0x00de, 9500 }, + { 0x00da, 9600 }, { 0x00d7, 9700 }, { 0x00d3, 9800 }, + { 0x00d0, 9900 }, { 0x00cc, 10000 }, { 0x00c7, 10100 }, + { 0x00c3, 10200 }, { 0x00bf, 10300 }, { 0x00ba, 10400 }, + { 0x00b6, 10500 }, { 0x00b2, 10600 }, { 0x00ae, 10700 }, + { 0x00aa, 10800 }, { 0x00a7, 10900 }, { 0x00a3, 11000 }, + { 0x009f, 11100 }, { 0x009c, 11200 }, { 0x0098, 11300 }, + { 0x0094, 11400 }, { 0x0091, 11500 }, { 0x008e, 11600 }, + { 0x008a, 11700 }, { 0x0087, 11800 }, { 0x0084, 11900 }, + { 0x0081, 12000 }, { 0x007e, 12100 }, { 0x007b, 12200 }, + { 0x0079, 12300 }, { 0x0076, 12400 }, { 0x0073, 12500 }, + { 0x0071, 12600 }, { 0x006e, 12700 }, { 0x006c, 12800 }, + { 0x0069, 12900 }, { 0x0067, 13000 }, { 0x0065, 13100 }, + { 0x0062, 13200 }, { 0x0060, 13300 }, { 0x005e, 13400 }, + { 0x005c, 13500 }, { 0x005a, 13600 }, { 0x0058, 13700 }, + { 0x0056, 13800 }, { 0x0054, 13900 }, { 0x0052, 14000 }, + { 0x0050, 14100 }, { 0x004e, 14200 }, { 0x004c, 14300 }, + { 0x004b, 14400 }, { 0x0049, 14500 }, { 0x0047, 14600 }, + { 0x0046, 14700 }, { 0x0044, 14800 }, { 0x0043, 14900 }, + { 0x0041, 15000 }, { 0x003f, 15100 }, { 0x003e, 15200 }, + { 0x003c, 15300 }, { 0x003b, 15400 }, { 0x003a, 15500 }, + { 0x0037, 15700 }, { 0x0036, 15800 }, { 0x0034, 15900 }, + { 0x0033, 16000 }, { 0x0032, 16100 }, { 0x0031, 16200 }, + { 0x0030, 16300 }, { 0x002f, 16400 }, { 0x002e, 16500 }, + { 0x002d, 16600 }, { 0x002c, 16700 }, { 0x002b, 16800 }, + { 0x002a, 16900 }, { 0x0029, 17000 }, { 0x0028, 17100 }, + { 0x0027, 17200 }, { 0x0026, 17300 }, { 0x0025, 17400 }, + { 0x0024, 17500 }, { 0x0023, 17600 }, { 0x0022, 17800 }, + { 0x0021, 17900 }, { 0x0020, 18000 }, { 0x001f, 18200 }, + { 0x001e, 18300 }, { 0x001d, 18500 }, { 0x001c, 18700 }, + { 0x001b, 18900 }, { 0x001a, 19000 }, { 0x0019, 19200 }, + { 0x0018, 19300 }, { 0x0017, 19500 }, { 0x0016, 19700 }, + { 0x0015, 19900 }, { 0x0014, 20000 }, +}; + +static const struct cxd2841er_cnr_data s2_cn_data[] = { + { 0x05af, 0 }, { 0x0597, 100 }, { 0x057e, 200 }, + { 0x0567, 300 }, { 0x0550, 400 }, { 0x0539, 500 }, + { 0x0522, 600 }, { 0x050c, 700 }, { 0x04f6, 800 }, + { 0x04e1, 900 }, { 0x04cc, 1000 }, { 0x04b6, 1100 }, + { 0x04a1, 1200 }, { 0x048c, 1300 }, { 0x0477, 1400 }, + { 0x0463, 1500 }, { 0x044f, 1600 }, { 0x043c, 1700 }, + { 0x0428, 1800 }, { 0x0416, 1900 }, { 0x0403, 2000 }, + { 0x03ef, 2100 }, { 0x03dc, 2200 }, { 0x03c9, 2300 }, + { 0x03b6, 2400 }, { 0x03a4, 2500 }, { 0x0392, 2600 }, + { 0x0381, 2700 }, { 0x036f, 2800 }, { 0x035f, 2900 }, + { 0x034e, 3000 }, { 0x033d, 3100 }, { 0x032d, 3200 }, + { 0x031d, 3300 }, { 0x030d, 3400 }, { 0x02fd, 3500 }, + { 0x02ee, 3600 }, { 0x02df, 3700 }, { 0x02d0, 3800 }, + { 0x02c2, 3900 }, { 0x02b4, 4000 }, { 0x02a6, 4100 }, + { 0x0299, 4200 }, { 0x028c, 4300 }, { 0x027f, 4400 }, + { 0x0272, 4500 }, { 0x0265, 4600 }, { 0x0259, 4700 }, + { 0x024d, 4800 }, { 0x0241, 4900 }, { 0x0236, 5000 }, + { 0x022b, 5100 }, { 0x0220, 5200 }, { 0x0215, 5300 }, + { 0x020a, 5400 }, { 0x0200, 5500 }, { 0x01f6, 5600 }, + { 0x01ec, 5700 }, { 0x01e2, 5800 }, { 0x01d8, 5900 }, + { 0x01cf, 6000 }, { 0x01c6, 6100 }, { 0x01bc, 6200 }, + { 0x01b3, 6300 }, { 0x01aa, 6400 }, { 0x01a2, 6500 }, + { 0x0199, 6600 }, { 0x0191, 6700 }, { 0x0189, 6800 }, + { 0x0181, 6900 }, { 0x0179, 7000 }, { 0x0171, 7100 }, + { 0x0169, 7200 }, { 0x0161, 7300 }, { 0x015a, 7400 }, + { 0x0153, 7500 }, { 0x014b, 7600 }, { 0x0144, 7700 }, + { 0x013d, 7800 }, { 0x0137, 7900 }, { 0x0130, 8000 }, + { 0x012a, 8100 }, { 0x0124, 8200 }, { 0x011e, 8300 }, + { 0x0118, 8400 }, { 0x0112, 8500 }, { 0x010c, 8600 }, + { 0x0107, 8700 }, { 0x0101, 8800 }, { 0x00fc, 8900 }, + { 0x00f7, 9000 }, { 0x00f2, 9100 }, { 0x00ec, 9200 }, + { 0x00e7, 9300 }, { 0x00e2, 9400 }, { 0x00dd, 9500 }, + { 0x00d8, 9600 }, { 0x00d4, 9700 }, { 0x00cf, 9800 }, + { 0x00ca, 9900 }, { 0x00c6, 10000 }, { 0x00c2, 10100 }, + { 0x00be, 10200 }, { 0x00b9, 10300 }, { 0x00b5, 10400 }, + { 0x00b1, 10500 }, { 0x00ae, 10600 }, { 0x00aa, 10700 }, + { 0x00a6, 10800 }, { 0x00a3, 10900 }, { 0x009f, 11000 }, + { 0x009b, 11100 }, { 0x0098, 11200 }, { 0x0095, 11300 }, + { 0x0091, 11400 }, { 0x008e, 11500 }, { 0x008b, 11600 }, + { 0x0088, 11700 }, { 0x0085, 11800 }, { 0x0082, 11900 }, + { 0x007f, 12000 }, { 0x007c, 12100 }, { 0x007a, 12200 }, + { 0x0077, 12300 }, { 0x0074, 12400 }, { 0x0072, 12500 }, + { 0x006f, 12600 }, { 0x006d, 12700 }, { 0x006b, 12800 }, + { 0x0068, 12900 }, { 0x0066, 13000 }, { 0x0064, 13100 }, + { 0x0061, 13200 }, { 0x005f, 13300 }, { 0x005d, 13400 }, + { 0x005b, 13500 }, { 0x0059, 13600 }, { 0x0057, 13700 }, + { 0x0055, 13800 }, { 0x0053, 13900 }, { 0x0051, 14000 }, + { 0x004f, 14100 }, { 0x004e, 14200 }, { 0x004c, 14300 }, + { 0x004a, 14400 }, { 0x0049, 14500 }, { 0x0047, 14600 }, + { 0x0045, 14700 }, { 0x0044, 14800 }, { 0x0042, 14900 }, + { 0x0041, 15000 }, { 0x003f, 15100 }, { 0x003e, 15200 }, + { 0x003c, 15300 }, { 0x003b, 15400 }, { 0x003a, 15500 }, + { 0x0038, 15600 }, { 0x0037, 15700 }, { 0x0036, 15800 }, + { 0x0034, 15900 }, { 0x0033, 16000 }, { 0x0032, 16100 }, + { 0x0031, 16200 }, { 0x0030, 16300 }, { 0x002f, 16400 }, + { 0x002e, 16500 }, { 0x002d, 16600 }, { 0x002c, 16700 }, + { 0x002b, 16800 }, { 0x002a, 16900 }, { 0x0029, 17000 }, + { 0x0028, 17100 }, { 0x0027, 17200 }, { 0x0026, 17300 }, + { 0x0025, 17400 }, { 0x0024, 17500 }, { 0x0023, 17600 }, + { 0x0022, 17800 }, { 0x0021, 17900 }, { 0x0020, 18000 }, + { 0x001f, 18200 }, { 0x001e, 18300 }, { 0x001d, 18500 }, + { 0x001c, 18700 }, { 0x001b, 18900 }, { 0x001a, 19000 }, + { 0x0019, 19200 }, { 0x0018, 19300 }, { 0x0017, 19500 }, + { 0x0016, 19700 }, { 0x0015, 19900 }, { 0x0014, 20000 }, +}; + +#define MAKE_IFFREQ_CONFIG(iffreq) ((u32)(((iffreq)/41.0)*16777216.0 + 0.5)) + +static void cxd2841er_i2c_debug(struct cxd2841er_priv *priv, + u8 addr, u8 reg, u8 write, + const u8 *data, u32 len) +{ + dev_dbg(&priv->i2c->dev, + "cxd2841er: I2C %s addr %02x reg 0x%02x size %d\n", + (write == 0 ? "read" : "write"), addr, reg, len); + print_hex_dump_bytes("cxd2841er: I2C data: ", + DUMP_PREFIX_OFFSET, data, len); +} + +static int cxd2841er_write_regs(struct cxd2841er_priv *priv, + u8 addr, u8 reg, const u8 *data, u32 len) +{ + int ret; + u8 buf[len+1]; + u8 i2c_addr = (addr == I2C_SLVX ? + priv->i2c_addr_slvx : priv->i2c_addr_slvt); + struct i2c_msg msg[1] = { + { + .addr = i2c_addr, + .flags = 0, + .len = sizeof(buf), + .buf = buf, + } + }; + + cxd2841er_i2c_debug(priv, i2c_addr, reg, 1, data, len); + buf[0] = reg; + memcpy(&buf[1], data, len); + + ret = i2c_transfer(priv->i2c, msg, 1); + if (ret >= 0 && ret != 1) + ret = -EIO; + if (ret < 0) { + dev_warn(&priv->i2c->dev, + "%s: i2c wr failed=%d addr=%02x reg=%02x len=%d\n", + KBUILD_MODNAME, ret, i2c_addr, reg, len); + return ret; + } + return 0; +} + +static int cxd2841er_write_reg(struct cxd2841er_priv *priv, + u8 addr, u8 reg, u8 val) +{ + return cxd2841er_write_regs(priv, addr, reg, &val, 1); +} + +static int cxd2841er_read_regs(struct cxd2841er_priv *priv, + u8 addr, u8 reg, u8 *val, u32 len) +{ + int ret; + u8 i2c_addr = (addr == I2C_SLVX ? + priv->i2c_addr_slvx : priv->i2c_addr_slvt); + struct i2c_msg msg[2] = { + { + .addr = i2c_addr, + .flags = 0, + .len = 1, + .buf = ®, + }, { + .addr = i2c_addr, + .flags = I2C_M_RD, + .len = len, + .buf = val, + } + }; + + ret = i2c_transfer(priv->i2c, &msg[0], 1); + if (ret >= 0 && ret != 1) + ret = -EIO; + if (ret < 0) { + dev_warn(&priv->i2c->dev, + "%s: i2c rw failed=%d addr=%02x reg=%02x\n", + KBUILD_MODNAME, ret, i2c_addr, reg); + return ret; + } + ret = i2c_transfer(priv->i2c, &msg[1], 1); + if (ret >= 0 && ret != 1) + ret = -EIO; + if (ret < 0) { + dev_warn(&priv->i2c->dev, + "%s: i2c rd failed=%d addr=%02x reg=%02x\n", + KBUILD_MODNAME, ret, i2c_addr, reg); + return ret; + } + return 0; +} + +static int cxd2841er_read_reg(struct cxd2841er_priv *priv, + u8 addr, u8 reg, u8 *val) +{ + return cxd2841er_read_regs(priv, addr, reg, val, 1); +} + +static int cxd2841er_set_reg_bits(struct cxd2841er_priv *priv, + u8 addr, u8 reg, u8 data, u8 mask) +{ + int res; + u8 rdata; + + if (mask != 0xff) { + res = cxd2841er_read_reg(priv, addr, reg, &rdata); + if (res) + return res; + data = ((data & mask) | (rdata & (mask ^ 0xFF))); + } + return cxd2841er_write_reg(priv, addr, reg, data); +} + +static int cxd2841er_dvbs2_set_symbol_rate(struct cxd2841er_priv *priv, + u32 symbol_rate) +{ + u32 reg_value = 0; + u8 data[3] = {0, 0, 0}; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + /* + * regValue = (symbolRateKSps * 2^14 / 1000) + 0.5 + * = ((symbolRateKSps * 2^14) + 500) / 1000 + * = ((symbolRateKSps * 16384) + 500) / 1000 + */ + reg_value = DIV_ROUND_CLOSEST(symbol_rate * 16384, 1000); + if ((reg_value == 0) || (reg_value > 0xFFFFF)) { + dev_err(&priv->i2c->dev, + "%s(): reg_value is out of range\n", __func__); + return -EINVAL; + } + data[0] = (u8)((reg_value >> 16) & 0x0F); + data[1] = (u8)((reg_value >> 8) & 0xFF); + data[2] = (u8)(reg_value & 0xFF); + /* Set SLV-T Bank : 0xAE */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xae); + cxd2841er_write_regs(priv, I2C_SLVT, 0x20, data, 3); + return 0; +} + +static void cxd2841er_set_ts_clock_mode(struct cxd2841er_priv *priv, + u8 system); + +static int cxd2841er_sleep_s_to_active_s(struct cxd2841er_priv *priv, + u8 system, u32 symbol_rate) +{ + int ret; + u8 data[4] = { 0, 0, 0, 0 }; + + if (priv->state != STATE_SLEEP_S) { + dev_err(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, (int)priv->state); + return -EINVAL; + } + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + cxd2841er_set_ts_clock_mode(priv, SYS_DVBS); + /* Set demod mode */ + if (system == SYS_DVBS) { + data[0] = 0x0A; + } else if (system == SYS_DVBS2) { + data[0] = 0x0B; + } else { + dev_err(&priv->i2c->dev, "%s(): invalid delsys %d\n", + __func__, system); + return -EINVAL; + } + /* Set SLV-X Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); + cxd2841er_write_reg(priv, I2C_SLVX, 0x17, data[0]); + /* DVB-S/S2 */ + data[0] = 0x00; + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* Enable S/S2 auto detection 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2d, data[0]); + /* Set SLV-T Bank : 0xAE */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xae); + /* Enable S/S2 auto detection 2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x30, data[0]); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* Enable demod clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x01); + /* Enable ADC clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x31, 0x01); + /* Enable ADC 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x63, 0x16); + /* Enable ADC 2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x65, 0x3f); + /* Set SLV-X Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); + /* Enable ADC 3 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x00); + /* Set SLV-T Bank : 0xA3 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa3); + cxd2841er_write_reg(priv, I2C_SLVT, 0xac, 0x00); + data[0] = 0x07; + data[1] = 0x3B; + data[2] = 0x08; + data[3] = 0xC5; + /* Set SLV-T Bank : 0xAB */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xab); + cxd2841er_write_regs(priv, I2C_SLVT, 0x98, data, 4); + data[0] = 0x05; + data[1] = 0x80; + data[2] = 0x0A; + data[3] = 0x80; + cxd2841er_write_regs(priv, I2C_SLVT, 0xa8, data, 4); + data[0] = 0x0C; + data[1] = 0xCC; + cxd2841er_write_regs(priv, I2C_SLVT, 0xc3, data, 2); + /* Set demod parameter */ + ret = cxd2841er_dvbs2_set_symbol_rate(priv, symbol_rate); + if (ret != 0) + return ret; + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* disable Hi-Z setting 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x10); + /* disable Hi-Z setting 2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0x00); + priv->state = STATE_ACTIVE_S; + return 0; +} + +static int cxd2841er_sleep_tc_to_active_t_band(struct cxd2841er_priv *priv, + u32 bandwidth); + +static int cxd2841er_sleep_tc_to_active_t2_band(struct cxd2841er_priv *priv, + u32 bandwidth); + +static int cxd2841er_sleep_tc_to_active_c_band(struct cxd2841er_priv *priv, + u32 bandwidth); + +static int cxd2841er_retune_active(struct cxd2841er_priv *priv, + struct dtv_frontend_properties *p) +{ + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_ACTIVE_S && + priv->state != STATE_ACTIVE_TC) { + dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* disable TS output */ + cxd2841er_write_reg(priv, I2C_SLVT, 0xc3, 0x01); + if (priv->state == STATE_ACTIVE_S) + return cxd2841er_dvbs2_set_symbol_rate( + priv, p->symbol_rate / 1000); + else if (priv->state == STATE_ACTIVE_TC) { + switch (priv->system) { + case SYS_DVBT: + return cxd2841er_sleep_tc_to_active_t_band( + priv, p->bandwidth_hz); + case SYS_DVBT2: + return cxd2841er_sleep_tc_to_active_t2_band( + priv, p->bandwidth_hz); + case SYS_DVBC_ANNEX_A: + return cxd2841er_sleep_tc_to_active_c_band( + priv, 8000000); + } + } + dev_dbg(&priv->i2c->dev, "%s(): invalid delivery system %d\n", + __func__, priv->system); + return -EINVAL; +} + +static int cxd2841er_active_s_to_sleep_s(struct cxd2841er_priv *priv) +{ + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_ACTIVE_S) { + dev_err(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* disable TS output */ + cxd2841er_write_reg(priv, I2C_SLVT, 0xc3, 0x01); + /* enable Hi-Z setting 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x1f); + /* enable Hi-Z setting 2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0xff); + /* Set SLV-X Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); + /* disable ADC 1 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x01); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* disable ADC clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x31, 0x00); + /* disable ADC 2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x63, 0x16); + /* disable ADC 3 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x65, 0x27); + /* SADC Bias ON */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x69, 0x06); + /* disable demod clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x00); + /* Set SLV-T Bank : 0xAE */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xae); + /* disable S/S2 auto detection1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* disable S/S2 auto detection2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2d, 0x00); + priv->state = STATE_SLEEP_S; + return 0; +} + +static int cxd2841er_sleep_s_to_shutdown(struct cxd2841er_priv *priv) +{ + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_SLEEP_S) { + dev_dbg(&priv->i2c->dev, "%s(): invalid demod state %d\n", + __func__, priv->state); + return -EINVAL; + } + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* Disable DSQOUT */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x3f); + /* Disable DSQIN */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x9c, 0x00); + /* Set SLV-X Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); + /* Disable oscillator */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x15, 0x01); + /* Set demod mode */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x17, 0x01); + priv->state = STATE_SHUTDOWN; + return 0; +} + +static int cxd2841er_sleep_tc_to_shutdown(struct cxd2841er_priv *priv) +{ + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_SLEEP_TC) { + dev_dbg(&priv->i2c->dev, "%s(): invalid demod state %d\n", + __func__, priv->state); + return -EINVAL; + } + /* Set SLV-X Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); + /* Disable oscillator */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x15, 0x01); + /* Set demod mode */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x17, 0x01); + priv->state = STATE_SHUTDOWN; + return 0; +} + +static int cxd2841er_active_t_to_sleep_tc(struct cxd2841er_priv *priv) +{ + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_ACTIVE_TC) { + dev_err(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* disable TS output */ + cxd2841er_write_reg(priv, I2C_SLVT, 0xc3, 0x01); + /* enable Hi-Z setting 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x3f); + /* enable Hi-Z setting 2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0xff); + /* Set SLV-X Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); + /* disable ADC 1 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x01); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* Disable ADC 2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x43, 0x0a); + /* Disable ADC 3 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x0a); + /* Disable ADC clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00); + /* Disable RF level monitor */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2f, 0x00); + /* Disable demod clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x00); + priv->state = STATE_SLEEP_TC; + return 0; +} + +static int cxd2841er_active_t2_to_sleep_tc(struct cxd2841er_priv *priv) +{ + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_ACTIVE_TC) { + dev_err(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* disable TS output */ + cxd2841er_write_reg(priv, I2C_SLVT, 0xc3, 0x01); + /* enable Hi-Z setting 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x3f); + /* enable Hi-Z setting 2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0xff); + /* Cancel DVB-T2 setting */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x13); + cxd2841er_write_reg(priv, I2C_SLVT, 0x83, 0x40); + cxd2841er_write_reg(priv, I2C_SLVT, 0x86, 0x21); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x9e, 0x09, 0x0f); + cxd2841er_write_reg(priv, I2C_SLVT, 0x9f, 0xfb); + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2a); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x38, 0x00, 0x0f); + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2b); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x11, 0x00, 0x3f); + /* Set SLV-X Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); + /* disable ADC 1 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x01); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* Disable ADC 2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x43, 0x0a); + /* Disable ADC 3 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x0a); + /* Disable ADC clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00); + /* Disable RF level monitor */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2f, 0x00); + /* Disable demod clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x00); + priv->state = STATE_SLEEP_TC; + return 0; +} + +static int cxd2841er_active_c_to_sleep_tc(struct cxd2841er_priv *priv) +{ + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_ACTIVE_TC) { + dev_err(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* disable TS output */ + cxd2841er_write_reg(priv, I2C_SLVT, 0xc3, 0x01); + /* enable Hi-Z setting 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x3f); + /* enable Hi-Z setting 2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0xff); + /* Cancel DVB-C setting */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x11); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xa3, 0x00, 0x1f); + /* Set SLV-X Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); + /* disable ADC 1 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x01); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* Disable ADC 2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x43, 0x0a); + /* Disable ADC 3 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x0a); + /* Disable ADC clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00); + /* Disable RF level monitor */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2f, 0x00); + /* Disable demod clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x00); + priv->state = STATE_SLEEP_TC; + return 0; +} + +static int cxd2841er_shutdown_to_sleep_s(struct cxd2841er_priv *priv) +{ + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_SHUTDOWN) { + dev_dbg(&priv->i2c->dev, "%s(): invalid demod state %d\n", + __func__, priv->state); + return -EINVAL; + } + /* Set SLV-X Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); + /* Clear all demodulator registers */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x02, 0x00); + usleep_range(3000, 5000); + /* Set SLV-X Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); + /* Set demod SW reset */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x10, 0x01); + /* Set X'tal clock to 20.5Mhz */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x14, 0x00); + /* Set demod mode */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x17, 0x0a); + /* Clear demod SW reset */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x10, 0x00); + usleep_range(1000, 2000); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* enable DSQOUT */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x1F); + /* enable DSQIN */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x9C, 0x40); + /* TADC Bias On */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x43, 0x0a); + cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x0a); + /* SADC Bias On */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x63, 0x16); + cxd2841er_write_reg(priv, I2C_SLVT, 0x65, 0x27); + cxd2841er_write_reg(priv, I2C_SLVT, 0x69, 0x06); + priv->state = STATE_SLEEP_S; + return 0; +} + +static int cxd2841er_shutdown_to_sleep_tc(struct cxd2841er_priv *priv) +{ + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_SHUTDOWN) { + dev_dbg(&priv->i2c->dev, "%s(): invalid demod state %d\n", + __func__, priv->state); + return -EINVAL; + } + /* Set SLV-X Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); + /* Clear all demodulator registers */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x02, 0x00); + usleep_range(3000, 5000); + /* Set SLV-X Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); + /* Set demod SW reset */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x10, 0x01); + /* Set X'tal clock to 20.5Mhz */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x13, 0x00); + cxd2841er_write_reg(priv, I2C_SLVX, 0x14, 0x00); + /* Clear demod SW reset */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x10, 0x00); + usleep_range(1000, 2000); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* TADC Bias On */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x43, 0x0a); + cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x0a); + /* SADC Bias On */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x63, 0x16); + cxd2841er_write_reg(priv, I2C_SLVT, 0x65, 0x27); + cxd2841er_write_reg(priv, I2C_SLVT, 0x69, 0x06); + priv->state = STATE_SLEEP_TC; + return 0; +} + +static int cxd2841er_tune_done(struct cxd2841er_priv *priv) +{ + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0, 0); + /* SW Reset */ + cxd2841er_write_reg(priv, I2C_SLVT, 0xfe, 0x01); + /* Enable TS output */ + cxd2841er_write_reg(priv, I2C_SLVT, 0xc3, 0x00); + return 0; +} + +/* Set TS parallel mode */ +static void cxd2841er_set_ts_clock_mode(struct cxd2841er_priv *priv, + u8 system) +{ + u8 serial_ts, ts_rate_ctrl_off, ts_in_off; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + cxd2841er_read_reg(priv, I2C_SLVT, 0xc4, &serial_ts); + cxd2841er_read_reg(priv, I2C_SLVT, 0xd3, &ts_rate_ctrl_off); + cxd2841er_read_reg(priv, I2C_SLVT, 0xde, &ts_in_off); + dev_dbg(&priv->i2c->dev, "%s(): ser_ts=0x%02x rate_ctrl_off=0x%02x in_off=0x%02x\n", + __func__, serial_ts, ts_rate_ctrl_off, ts_in_off); + + /* + * slave Bank Addr Bit default Name + * 00h D9h [7:0] 8'h08 OTSCKPERIOD + */ + cxd2841er_write_reg(priv, I2C_SLVT, 0xd9, 0x08); + /* + * Disable TS IF Clock + * slave Bank Addr Bit default Name + * 00h 32h [0] 1'b1 OREG_CK_TSIF_EN + */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x32, 0x00, 0x01); + /* + * slave Bank Addr Bit default Name + * 00h 33h [1:0] 2'b01 OREG_CKSEL_TSIF + */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x33, 0x00, 0x03); + /* + * Enable TS IF Clock + * slave Bank Addr Bit default Name + * 00h 32h [0] 1'b1 OREG_CK_TSIF_EN + */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x32, 0x01, 0x01); + + if (system == SYS_DVBT) { + /* Enable parity period for DVB-T */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x66, 0x01, 0x01); + } else if (system == SYS_DVBC_ANNEX_A) { + /* Enable parity period for DVB-C */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x40); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x66, 0x01, 0x01); + } +} + +static u8 cxd2841er_chip_id(struct cxd2841er_priv *priv) +{ + u8 chip_id; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + cxd2841er_write_reg(priv, I2C_SLVT, 0, 0); + cxd2841er_read_reg(priv, I2C_SLVT, 0xfd, &chip_id); + return chip_id; +} + +static int cxd2841er_read_status_s(struct dvb_frontend *fe, + enum fe_status *status) +{ + u8 reg = 0; + struct cxd2841er_priv *priv = fe->demodulator_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + *status = 0; + if (priv->state != STATE_ACTIVE_S) { + dev_err(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + /* Set SLV-T Bank : 0xA0 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa0); + /* + * slave Bank Addr Bit Signal name + * A0h 11h [2] ITSLOCK + */ + cxd2841er_read_reg(priv, I2C_SLVT, 0x11, ®); + if (reg & 0x04) { + *status = FE_HAS_SIGNAL + | FE_HAS_CARRIER + | FE_HAS_VITERBI + | FE_HAS_SYNC + | FE_HAS_LOCK; + } + dev_dbg(&priv->i2c->dev, "%s(): result 0x%x\n", __func__, *status); + return 0; +} + +static int cxd2841er_read_status_t_t2(struct cxd2841er_priv *priv, + u8 *sync, u8 *tslock, u8 *unlock) +{ + u8 data = 0; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_ACTIVE_TC) + return -EINVAL; + if (priv->system == SYS_DVBT) { + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + } else { + /* Set SLV-T Bank : 0x20 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x20); + } + cxd2841er_read_reg(priv, I2C_SLVT, 0x10, &data); + if ((data & 0x07) == 0x07) { + dev_dbg(&priv->i2c->dev, + "%s(): invalid hardware state detected\n", __func__); + *sync = 0; + *tslock = 0; + *unlock = 0; + } else { + *sync = ((data & 0x07) == 0x6 ? 1 : 0); + *tslock = ((data & 0x20) ? 1 : 0); + *unlock = ((data & 0x10) ? 1 : 0); + } + return 0; +} + +static int cxd2841er_read_status_c(struct cxd2841er_priv *priv, u8 *tslock) +{ + u8 data; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_ACTIVE_TC) + return -EINVAL; + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x40); + cxd2841er_read_reg(priv, I2C_SLVT, 0x88, &data); + if ((data & 0x01) == 0) { + *tslock = 0; + } else { + cxd2841er_read_reg(priv, I2C_SLVT, 0x10, &data); + *tslock = ((data & 0x20) ? 1 : 0); + } + return 0; +} + +static int cxd2841er_read_status_tc(struct dvb_frontend *fe, + enum fe_status *status) +{ + int ret = 0; + u8 sync = 0; + u8 tslock = 0; + u8 unlock = 0; + struct cxd2841er_priv *priv = fe->demodulator_priv; + + *status = 0; + if (priv->state == STATE_ACTIVE_TC) { + if (priv->system == SYS_DVBT || priv->system == SYS_DVBT2) { + ret = cxd2841er_read_status_t_t2( + priv, &sync, &tslock, &unlock); + if (ret) + goto done; + if (unlock) + goto done; + if (sync) + *status = FE_HAS_SIGNAL | + FE_HAS_CARRIER | + FE_HAS_VITERBI | + FE_HAS_SYNC; + if (tslock) + *status |= FE_HAS_LOCK; + } else if (priv->system == SYS_DVBC_ANNEX_A) { + ret = cxd2841er_read_status_c(priv, &tslock); + if (ret) + goto done; + if (tslock) + *status = FE_HAS_SIGNAL | + FE_HAS_CARRIER | + FE_HAS_VITERBI | + FE_HAS_SYNC | + FE_HAS_LOCK; + } + } +done: + dev_dbg(&priv->i2c->dev, "%s(): status 0x%x\n", __func__, *status); + return ret; +} + +static int cxd2841er_get_carrier_offset_s_s2(struct cxd2841er_priv *priv, + int *offset) +{ + u8 data[3]; + u8 is_hs_mode; + s32 cfrl_ctrlval; + s32 temp_div, temp_q, temp_r; + + if (priv->state != STATE_ACTIVE_S) { + dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + /* + * Get High Sampling Rate mode + * slave Bank Addr Bit Signal name + * A0h 10h [0] ITRL_LOCK + */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa0); + cxd2841er_read_reg(priv, I2C_SLVT, 0x10, &data[0]); + if (data[0] & 0x01) { + /* + * slave Bank Addr Bit Signal name + * A0h 50h [4] IHSMODE + */ + cxd2841er_read_reg(priv, I2C_SLVT, 0x50, &data[0]); + is_hs_mode = (data[0] & 0x10 ? 1 : 0); + } else { + dev_dbg(&priv->i2c->dev, + "%s(): unable to detect sampling rate mode\n", + __func__); + return -EINVAL; + } + /* + * slave Bank Addr Bit Signal name + * A0h 45h [4:0] ICFRL_CTRLVAL[20:16] + * A0h 46h [7:0] ICFRL_CTRLVAL[15:8] + * A0h 47h [7:0] ICFRL_CTRLVAL[7:0] + */ + cxd2841er_read_regs(priv, I2C_SLVT, 0x45, data, 3); + cfrl_ctrlval = sign_extend32((((u32)data[0] & 0x1F) << 16) | + (((u32)data[1] & 0xFF) << 8) | + ((u32)data[2] & 0xFF), 20); + temp_div = (is_hs_mode ? 1048576 : 1572864); + if (cfrl_ctrlval > 0) { + temp_q = div_s64_rem(97375LL * cfrl_ctrlval, + temp_div, &temp_r); + } else { + temp_q = div_s64_rem(-97375LL * cfrl_ctrlval, + temp_div, &temp_r); + } + if (temp_r >= temp_div / 2) + temp_q++; + if (cfrl_ctrlval > 0) + temp_q *= -1; + *offset = temp_q; + return 0; +} + +int cxd2841er_get_carrier_offset_t2( + struct cxd2841er_priv *priv, u32 bandwidth, int *offset) +{ + u8 data[4]; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_ACTIVE_TC) { + dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + if (priv->system != SYS_DVBT2) { + dev_dbg(&priv->i2c->dev, "%s(): invalid delivery system %d\n", + __func__, priv->system); + return -EINVAL; + } + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x20); + cxd2841er_read_regs(priv, I2C_SLVT, 0x4c, data, sizeof(data)); + *offset = -1 * sign_extend32( + ((u32)(data[0] & 0x0F) << 24) | ((u32)data[1] << 16) | + ((u32)data[2] << 8) | (u32)data[3], 27); + switch (bandwidth) { + case 1712000: + *offset /= 582; + break; + case 5000000: + case 6000000: + case 7000000: + case 8000000: + *offset *= (bandwidth / 1000000); + *offset /= 940; + break; + default: + dev_dbg(&priv->i2c->dev, "%s(): invalid bandwidth %d\n", + __func__, bandwidth); + return -EINVAL; + } + return 0; +} + +int cxd2841er_get_carrier_offset_c( + struct cxd2841er_priv *priv, int *offset) +{ + u8 data[2]; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state != STATE_ACTIVE_TC) { + dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + if (priv->system != SYS_DVBC_ANNEX_A) { + dev_dbg(&priv->i2c->dev, "%s(): invalid delivery system %d\n", + __func__, priv->system); + return -EINVAL; + } + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x40); + cxd2841er_read_regs(priv, I2C_SLVT, 0x15, data, sizeof(data)); + *offset = div_s64(41000LL * sign_extend32((((u32)data[0] & 0x3f) << 8) + | (u32)data[1], 13), 16384); + return 0; +} + +static int cxd2841er_read_packet_errors_t( + struct cxd2841er_priv *priv, u32 *penum) +{ + u8 data[3]; + + *penum = 0; + if (priv->state != STATE_ACTIVE_TC) { + dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + cxd2841er_read_regs(priv, I2C_SLVT, 0xea, data, sizeof(data)); + if (data[2] & 0x01) + *penum = ((u32)data[0] << 8) | (u32)data[1]; + return 0; +} + +static int cxd2841er_read_packet_errors_t2( + struct cxd2841er_priv *priv, u32 *penum) +{ + u8 data[3]; + + *penum = 0; + if (priv->state != STATE_ACTIVE_TC) { + dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x24); + cxd2841er_read_regs(priv, I2C_SLVT, 0xfd, data, sizeof(data)); + if (data[0] & 0x01) + *penum = ((u32)data[1] << 8) | (u32)data[2]; + return 0; +} + +static u32 cxd2841er_mon_read_ber_s(struct cxd2841er_priv *priv) +{ + u8 data[11]; + u32 bit_error, bit_count; + u32 temp_q, temp_r; + + /* Set SLV-T Bank : 0xA0 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa0); + /* + * slave Bank Addr Bit Signal name + * A0h 35h [0] IFVBER_VALID + * A0h 36h [5:0] IFVBER_BITERR[21:16] + * A0h 37h [7:0] IFVBER_BITERR[15:8] + * A0h 38h [7:0] IFVBER_BITERR[7:0] + * A0h 3Dh [5:0] IFVBER_BITNUM[21:16] + * A0h 3Eh [7:0] IFVBER_BITNUM[15:8] + * A0h 3Fh [7:0] IFVBER_BITNUM[7:0] + */ + cxd2841er_read_regs(priv, I2C_SLVT, 0x35, data, 11); + if (data[0] & 0x01) { + bit_error = ((u32)(data[1] & 0x3F) << 16) | + ((u32)(data[2] & 0xFF) << 8) | + (u32)(data[3] & 0xFF); + bit_count = ((u32)(data[8] & 0x3F) << 16) | + ((u32)(data[9] & 0xFF) << 8) | + (u32)(data[10] & 0xFF); + /* + * BER = bitError / bitCount + * = (bitError * 10^7) / bitCount + * = ((bitError * 625 * 125 * 128) / bitCount + */ + if ((bit_count == 0) || (bit_error > bit_count)) { + dev_dbg(&priv->i2c->dev, + "%s(): invalid bit_error %d, bit_count %d\n", + __func__, bit_error, bit_count); + return 0; + } + temp_q = div_u64_rem(10000000ULL * bit_error, + bit_count, &temp_r); + if (bit_count != 1 && temp_r >= bit_count / 2) + temp_q++; + return temp_q; + } + dev_dbg(&priv->i2c->dev, "%s(): no data available\n", __func__); + return 0; +} + + +static u32 cxd2841er_mon_read_ber_s2(struct cxd2841er_priv *priv) +{ + u8 data[5]; + u32 bit_error, period; + u32 temp_q, temp_r; + u32 result = 0; + + /* Set SLV-T Bank : 0xB2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xb2); + /* + * slave Bank Addr Bit Signal name + * B2h 30h [0] IFLBER_VALID + * B2h 31h [3:0] IFLBER_BITERR[27:24] + * B2h 32h [7:0] IFLBER_BITERR[23:16] + * B2h 33h [7:0] IFLBER_BITERR[15:8] + * B2h 34h [7:0] IFLBER_BITERR[7:0] + */ + cxd2841er_read_regs(priv, I2C_SLVT, 0x30, data, 5); + if (data[0] & 0x01) { + /* Bit error count */ + bit_error = ((u32)(data[1] & 0x0F) << 24) | + ((u32)(data[2] & 0xFF) << 16) | + ((u32)(data[3] & 0xFF) << 8) | + (u32)(data[4] & 0xFF); + + /* Set SLV-T Bank : 0xA0 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa0); + cxd2841er_read_reg(priv, I2C_SLVT, 0x7a, data); + /* Measurement period */ + period = (u32)(1 << (data[0] & 0x0F)); + if (period == 0) { + dev_dbg(&priv->i2c->dev, + "%s(): period is 0\n", __func__); + return 0; + } + if (bit_error > (period * 64800)) { + dev_dbg(&priv->i2c->dev, + "%s(): invalid bit_err 0x%x period 0x%x\n", + __func__, bit_error, period); + return 0; + } + /* + * BER = bitError / (period * 64800) + * = (bitError * 10^7) / (period * 64800) + * = (bitError * 10^5) / (period * 648) + * = (bitError * 12500) / (period * 81) + * = (bitError * 10) * 1250 / (period * 81) + */ + temp_q = div_u64_rem(12500ULL * bit_error, + period * 81, &temp_r); + if (temp_r >= period * 40) + temp_q++; + result = temp_q; + } else { + dev_dbg(&priv->i2c->dev, + "%s(): no data available\n", __func__); + } + return result; +} + +static int cxd2841er_read_ber_t2(struct cxd2841er_priv *priv, u32 *ber) +{ + u8 data[4]; + u32 div, q, r; + u32 bit_err, period_exp, n_ldpc; + + *ber = 0; + if (priv->state != STATE_ACTIVE_TC) { + dev_dbg(&priv->i2c->dev, + "%s(): invalid state %d\n", __func__, priv->state); + return -EINVAL; + } + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x20); + cxd2841er_read_regs(priv, I2C_SLVT, 0x39, data, sizeof(data)); + if (!(data[0] & 0x10)) { + dev_dbg(&priv->i2c->dev, + "%s(): no valid BER data\n", __func__); + return 0; + } + bit_err = ((u32)(data[0] & 0x0f) << 24) | + ((u32)data[1] << 16) | + ((u32)data[2] << 8) | + (u32)data[3]; + cxd2841er_read_reg(priv, I2C_SLVT, 0x6f, data); + period_exp = data[0] & 0x0f; + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x22); + cxd2841er_read_reg(priv, I2C_SLVT, 0x5e, data); + n_ldpc = ((data[0] & 0x03) == 0 ? 16200 : 64800); + if (bit_err > ((1U << period_exp) * n_ldpc)) { + dev_dbg(&priv->i2c->dev, + "%s(): invalid BER value\n", __func__); + return -EINVAL; + } + if (period_exp >= 4) { + div = (1U << (period_exp - 4)) * (n_ldpc / 200); + q = div_u64_rem(3125ULL * bit_err, div, &r); + } else { + div = (1U << period_exp) * (n_ldpc / 200); + q = div_u64_rem(50000ULL * bit_err, div, &r); + } + *ber = (r >= div / 2) ? q + 1 : q; + return 0; +} + +static int cxd2841er_read_ber_t(struct cxd2841er_priv *priv, u32 *ber) +{ + u8 data[2]; + u32 div, q, r; + u32 bit_err, period; + + *ber = 0; + if (priv->state != STATE_ACTIVE_TC) { + dev_dbg(&priv->i2c->dev, + "%s(): invalid state %d\n", __func__, priv->state); + return -EINVAL; + } + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + cxd2841er_read_reg(priv, I2C_SLVT, 0x39, data); + if (!(data[0] & 0x01)) { + dev_dbg(&priv->i2c->dev, + "%s(): no valid BER data\n", __func__); + return 0; + } + cxd2841er_read_regs(priv, I2C_SLVT, 0x22, data, sizeof(data)); + bit_err = ((u32)data[0] << 8) | (u32)data[1]; + cxd2841er_read_reg(priv, I2C_SLVT, 0x6f, data); + period = ((data[0] & 0x07) == 0) ? 256 : (4096 << (data[0] & 0x07)); + div = period / 128; + q = div_u64_rem(78125ULL * bit_err, div, &r); + *ber = (r >= div / 2) ? q + 1 : q; + return 0; +} + +static u32 cxd2841er_dvbs_read_snr(struct cxd2841er_priv *priv, u8 delsys) +{ + u8 data[3]; + u32 res = 0, value; + int min_index, max_index, index; + static const struct cxd2841er_cnr_data *cn_data; + + /* Set SLV-T Bank : 0xA1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa1); + /* + * slave Bank Addr Bit Signal name + * A1h 10h [0] ICPM_QUICKRDY + * A1h 11h [4:0] ICPM_QUICKCNDT[12:8] + * A1h 12h [7:0] ICPM_QUICKCNDT[7:0] + */ + cxd2841er_read_regs(priv, I2C_SLVT, 0x10, data, 3); + if (data[0] & 0x01) { + value = ((u32)(data[1] & 0x1F) << 8) | (u32)(data[2] & 0xFF); + min_index = 0; + if (delsys == SYS_DVBS) { + cn_data = s_cn_data; + max_index = sizeof(s_cn_data) / + sizeof(s_cn_data[0]) - 1; + } else { + cn_data = s2_cn_data; + max_index = sizeof(s2_cn_data) / + sizeof(s2_cn_data[0]) - 1; + } + if (value >= cn_data[min_index].value) { + res = cn_data[min_index].cnr_x1000; + goto done; + } + if (value <= cn_data[max_index].value) { + res = cn_data[max_index].cnr_x1000; + goto done; + } + while ((max_index - min_index) > 1) { + index = (max_index + min_index) / 2; + if (value == cn_data[index].value) { + res = cn_data[index].cnr_x1000; + goto done; + } else if (value > cn_data[index].value) + max_index = index; + else + min_index = index; + if ((max_index - min_index) <= 1) { + if (value == cn_data[max_index].value) { + res = cn_data[max_index].cnr_x1000; + goto done; + } else { + res = cn_data[min_index].cnr_x1000; + goto done; + } + } + } + } else { + dev_dbg(&priv->i2c->dev, + "%s(): no data available\n", __func__); + } +done: + return res; +} + +static int cxd2841er_read_snr_t(struct cxd2841er_priv *priv, u32 *snr) +{ + u32 reg; + u8 data[2]; + + *snr = 0; + if (priv->state != STATE_ACTIVE_TC) { + dev_dbg(&priv->i2c->dev, + "%s(): invalid state %d\n", __func__, priv->state); + return -EINVAL; + } + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + cxd2841er_read_regs(priv, I2C_SLVT, 0x28, data, sizeof(data)); + reg = ((u32)data[0] << 8) | (u32)data[1]; + if (reg == 0) { + dev_dbg(&priv->i2c->dev, + "%s(): reg value out of range\n", __func__); + return 0; + } + if (reg > 4996) + reg = 4996; + *snr = 10000 * ((intlog10(reg) - intlog10(5350 - reg)) >> 24) + 28500; + return 0; +} + +int cxd2841er_read_snr_t2(struct cxd2841er_priv *priv, u32 *snr) +{ + u32 reg; + u8 data[2]; + + *snr = 0; + if (priv->state != STATE_ACTIVE_TC) { + dev_dbg(&priv->i2c->dev, + "%s(): invalid state %d\n", __func__, priv->state); + return -EINVAL; + } + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x20); + cxd2841er_read_regs(priv, I2C_SLVT, 0x28, data, sizeof(data)); + reg = ((u32)data[0] << 8) | (u32)data[1]; + if (reg == 0) { + dev_dbg(&priv->i2c->dev, + "%s(): reg value out of range\n", __func__); + return 0; + } + if (reg > 10876) + reg = 10876; + *snr = 10000 * ((intlog10(reg) - + intlog10(12600 - reg)) >> 24) + 32000; + return 0; +} + +static u16 cxd2841er_read_agc_gain_t_t2(struct cxd2841er_priv *priv, + u8 delsys) +{ + u8 data[2]; + + cxd2841er_write_reg( + priv, I2C_SLVT, 0x00, (delsys == SYS_DVBT ? 0x10 : 0x20)); + cxd2841er_read_regs(priv, I2C_SLVT, 0x26, data, 2); + return ((((u16)data[0] & 0x0F) << 8) | (u16)(data[1] & 0xFF)) << 4; +} + +static u16 cxd2841er_read_agc_gain_s(struct cxd2841er_priv *priv) +{ + u8 data[2]; + + /* Set SLV-T Bank : 0xA0 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa0); + /* + * slave Bank Addr Bit Signal name + * A0h 1Fh [4:0] IRFAGC_GAIN[12:8] + * A0h 20h [7:0] IRFAGC_GAIN[7:0] + */ + cxd2841er_read_regs(priv, I2C_SLVT, 0x1f, data, 2); + return ((((u16)data[0] & 0x1F) << 8) | (u16)(data[1] & 0xFF)) << 3; +} + +static int cxd2841er_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct cxd2841er_priv *priv = fe->demodulator_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + *ber = 0; + switch (p->delivery_system) { + case SYS_DVBS: + *ber = cxd2841er_mon_read_ber_s(priv); + break; + case SYS_DVBS2: + *ber = cxd2841er_mon_read_ber_s2(priv); + break; + case SYS_DVBT: + return cxd2841er_read_ber_t(priv, ber); + case SYS_DVBT2: + return cxd2841er_read_ber_t2(priv, ber); + default: + *ber = 0; + break; + } + return 0; +} + +static int cxd2841er_read_signal_strength(struct dvb_frontend *fe, + u16 *strength) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct cxd2841er_priv *priv = fe->demodulator_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + switch (p->delivery_system) { + case SYS_DVBT: + case SYS_DVBT2: + *strength = 65535 - cxd2841er_read_agc_gain_t_t2( + priv, p->delivery_system); + break; + case SYS_DVBS: + case SYS_DVBS2: + *strength = 65535 - cxd2841er_read_agc_gain_s(priv); + break; + default: + *strength = 0; + break; + } + return 0; +} + +static int cxd2841er_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + u32 tmp = 0; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct cxd2841er_priv *priv = fe->demodulator_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + switch (p->delivery_system) { + case SYS_DVBT: + cxd2841er_read_snr_t(priv, &tmp); + break; + case SYS_DVBT2: + cxd2841er_read_snr_t2(priv, &tmp); + break; + case SYS_DVBS: + case SYS_DVBS2: + tmp = cxd2841er_dvbs_read_snr(priv, p->delivery_system); + break; + default: + dev_dbg(&priv->i2c->dev, "%s(): unknown delivery system %d\n", + __func__, p->delivery_system); + break; + } + *snr = tmp & 0xffff; + return 0; +} + +static int cxd2841er_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct cxd2841er_priv *priv = fe->demodulator_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + switch (p->delivery_system) { + case SYS_DVBT: + cxd2841er_read_packet_errors_t(priv, ucblocks); + break; + case SYS_DVBT2: + cxd2841er_read_packet_errors_t2(priv, ucblocks); + break; + default: + *ucblocks = 0; + break; + } + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + return 0; +} + +static int cxd2841er_dvbt2_set_profile( + struct cxd2841er_priv *priv, enum cxd2841er_dvbt2_profile_t profile) +{ + u8 tune_mode; + u8 seq_not2d_time; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + switch (profile) { + case DVBT2_PROFILE_BASE: + tune_mode = 0x01; + seq_not2d_time = 12; + break; + case DVBT2_PROFILE_LITE: + tune_mode = 0x05; + seq_not2d_time = 40; + break; + case DVBT2_PROFILE_ANY: + tune_mode = 0x00; + seq_not2d_time = 40; + break; + default: + return -EINVAL; + } + /* Set SLV-T Bank : 0x2E */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2e); + /* Set profile and tune mode */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x10, tune_mode, 0x07); + /* Set SLV-T Bank : 0x2B */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2b); + /* Set early unlock detection time */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x9d, seq_not2d_time); + return 0; +} + +static int cxd2841er_dvbt2_set_plp_config(struct cxd2841er_priv *priv, + u8 is_auto, u8 plp_id) +{ + if (is_auto) { + dev_dbg(&priv->i2c->dev, + "%s() using auto PLP selection\n", __func__); + } else { + dev_dbg(&priv->i2c->dev, + "%s() using manual PLP selection, ID %d\n", + __func__, plp_id); + } + /* Set SLV-T Bank : 0x23 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x23); + if (!is_auto) { + /* Manual PLP selection mode. Set the data PLP Id. */ + cxd2841er_write_reg(priv, I2C_SLVT, 0xaf, plp_id); + } + /* Auto PLP select (Scanning mode = 0x00). Data PLP select = 0x01. */ + cxd2841er_write_reg(priv, I2C_SLVT, 0xad, (is_auto ? 0x00 : 0x01)); + return 0; +} + +static int cxd2841er_sleep_tc_to_active_t2_band(struct cxd2841er_priv *priv, + u32 bandwidth) +{ + u32 iffreq; + u8 b20_9f[5]; + u8 b10_a6[14]; + u8 b10_b6[3]; + u8 b10_d7; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + switch (bandwidth) { + case 8000000: + /* bank 0x20, reg 0x9f */ + b20_9f[0] = 0x11; + b20_9f[1] = 0xf0; + b20_9f[2] = 0x00; + b20_9f[3] = 0x00; + b20_9f[4] = 0x00; + /* bank 0x10, reg 0xa6 */ + b10_a6[0] = 0x26; + b10_a6[1] = 0xaf; + b10_a6[2] = 0x06; + b10_a6[3] = 0xcd; + b10_a6[4] = 0x13; + b10_a6[5] = 0xbb; + b10_a6[6] = 0x28; + b10_a6[7] = 0xba; + b10_a6[8] = 0x23; + b10_a6[9] = 0xa9; + b10_a6[10] = 0x1f; + b10_a6[11] = 0xa8; + b10_a6[12] = 0x2c; + b10_a6[13] = 0xc8; + iffreq = MAKE_IFFREQ_CONFIG(4.80); + b10_d7 = 0x00; + break; + case 7000000: + /* bank 0x20, reg 0x9f */ + b20_9f[0] = 0x14; + b20_9f[1] = 0x80; + b20_9f[2] = 0x00; + b20_9f[3] = 0x00; + b20_9f[4] = 0x00; + /* bank 0x10, reg 0xa6 */ + b10_a6[0] = 0x2C; + b10_a6[1] = 0xBD; + b10_a6[2] = 0x02; + b10_a6[3] = 0xCF; + b10_a6[4] = 0x04; + b10_a6[5] = 0xF8; + b10_a6[6] = 0x23; + b10_a6[7] = 0xA6; + b10_a6[8] = 0x29; + b10_a6[9] = 0xB0; + b10_a6[10] = 0x26; + b10_a6[11] = 0xA9; + b10_a6[12] = 0x21; + b10_a6[13] = 0xA5; + iffreq = MAKE_IFFREQ_CONFIG(4.2); + b10_d7 = 0x02; + break; + case 6000000: + /* bank 0x20, reg 0x9f */ + b20_9f[0] = 0x17; + b20_9f[1] = 0xEA; + b20_9f[2] = 0xAA; + b20_9f[3] = 0xAA; + b20_9f[4] = 0xAA; + /* bank 0x10, reg 0xa6 */ + b10_a6[0] = 0x27; + b10_a6[1] = 0xA7; + b10_a6[2] = 0x28; + b10_a6[3] = 0xB3; + b10_a6[4] = 0x02; + b10_a6[5] = 0xF0; + b10_a6[6] = 0x01; + b10_a6[7] = 0xE8; + b10_a6[8] = 0x00; + b10_a6[9] = 0xCF; + b10_a6[10] = 0x00; + b10_a6[11] = 0xE6; + b10_a6[12] = 0x23; + b10_a6[13] = 0xA4; + iffreq = MAKE_IFFREQ_CONFIG(3.6); + b10_d7 = 0x04; + break; + case 5000000: + /* bank 0x20, reg 0x9f */ + b20_9f[0] = 0x1C; + b20_9f[1] = 0xB3; + b20_9f[2] = 0x33; + b20_9f[3] = 0x33; + b20_9f[4] = 0x33; + /* bank 0x10, reg 0xa6 */ + b10_a6[0] = 0x27; + b10_a6[1] = 0xA7; + b10_a6[2] = 0x28; + b10_a6[3] = 0xB3; + b10_a6[4] = 0x02; + b10_a6[5] = 0xF0; + b10_a6[6] = 0x01; + b10_a6[7] = 0xE8; + b10_a6[8] = 0x00; + b10_a6[9] = 0xCF; + b10_a6[10] = 0x00; + b10_a6[11] = 0xE6; + b10_a6[12] = 0x23; + b10_a6[13] = 0xA4; + iffreq = MAKE_IFFREQ_CONFIG(3.6); + b10_d7 = 0x06; + break; + case 1712000: + /* bank 0x20, reg 0x9f */ + b20_9f[0] = 0x58; + b20_9f[1] = 0xE2; + b20_9f[2] = 0xAF; + b20_9f[3] = 0xE0; + b20_9f[4] = 0xBC; + /* bank 0x10, reg 0xa6 */ + b10_a6[0] = 0x25; + b10_a6[1] = 0xA0; + b10_a6[2] = 0x36; + b10_a6[3] = 0x8D; + b10_a6[4] = 0x2E; + b10_a6[5] = 0x94; + b10_a6[6] = 0x28; + b10_a6[7] = 0x9B; + b10_a6[8] = 0x32; + b10_a6[9] = 0x90; + b10_a6[10] = 0x2C; + b10_a6[11] = 0x9D; + b10_a6[12] = 0x29; + b10_a6[13] = 0x99; + iffreq = MAKE_IFFREQ_CONFIG(3.5); + b10_d7 = 0x03; + break; + default: + return -EINVAL; + } + /* Set SLV-T Bank : 0x20 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x20); + cxd2841er_write_regs(priv, I2C_SLVT, 0x9f, b20_9f, sizeof(b20_9f)); + /* Set SLV-T Bank : 0x27 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x27); + cxd2841er_set_reg_bits( + priv, I2C_SLVT, 0x7a, + (bandwidth == 1712000 ? 0x03 : 0x00), 0x0f); + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + /* Group delay equaliser sett. for ASCOT2E */ + cxd2841er_write_regs(priv, I2C_SLVT, 0xa6, b10_a6, sizeof(b10_a6)); + /* */ + b10_b6[0] = (u8) ((iffreq >> 16) & 0xff); + b10_b6[1] = (u8)((iffreq >> 8) & 0xff); + b10_b6[2] = (u8)(iffreq & 0xff); + cxd2841er_write_regs(priv, I2C_SLVT, 0xb6, b10_b6, sizeof(b10_b6)); + /* System bandwidth setting */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xd7, b10_d7, 0x07); + return 0; +} + +static int cxd2841er_sleep_tc_to_active_t_band( + struct cxd2841er_priv *priv, u32 bandwidth) +{ + u8 b13_9c[2] = { 0x01, 0x14 }; + u8 bw8mhz_b10_9f[] = { 0x11, 0xF0, 0x00, 0x00, 0x00 }; + u8 bw8mhz_b10_a6[] = { 0x26, 0xAF, 0x06, 0xCD, 0x13, 0xBB, + 0x28, 0xBA, 0x23, 0xA9, 0x1F, 0xA8, 0x2C, 0xC8 }; + u8 bw8mhz_b10_d9[] = { 0x01, 0xE0 }; + u8 bw8mhz_b17_38[] = { 0x01, 0x02 }; + u8 bw7mhz_b10_9f[] = { 0x14, 0x80, 0x00, 0x00, 0x00 }; + u8 bw7mhz_b10_a6[] = { 0x2C, 0xBD, 0x02, 0xCF, 0x04, 0xF8, + 0x23, 0xA6, 0x29, 0xB0, 0x26, 0xA9, 0x21, 0xA5 }; + u8 bw7mhz_b10_d9[] = { 0x12, 0xF8 }; + u8 bw7mhz_b17_38[] = { 0x00, 0x03 }; + u8 bw6mhz_b10_9f[] = { 0x17, 0xEA, 0xAA, 0xAA, 0xAA }; + u8 bw6mhz_b10_a6[] = { 0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, + 0x01, 0xE8, 0x00, 0xCF, 0x00, 0xE6, 0x23, 0xA4 }; + u8 bw6mhz_b10_d9[] = { 0x1F, 0xDC }; + u8 bw6mhz_b17_38[] = { 0x00, 0x03 }; + u8 bw5mhz_b10_9f[] = { 0x1C, 0xB3, 0x33, 0x33, 0x33 }; + u8 bw5mhz_b10_a6[] = { 0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, + 0x01, 0xE8, 0x00, 0xCF, 0x00, 0xE6, 0x23, 0xA4 }; + u8 bw5mhz_b10_d9[] = { 0x26, 0x3C }; + u8 bw5mhz_b17_38[] = { 0x00, 0x03 }; + u8 b10_b6[3]; + u8 d7val; + u32 iffreq; + u8 *b10_9f; + u8 *b10_a6; + u8 *b10_d9; + u8 *b17_38; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x13); + /* Echo performance optimization setting */ + cxd2841er_write_regs(priv, I2C_SLVT, 0x9c, b13_9c, sizeof(b13_9c)); + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + + switch (bandwidth) { + case 8000000: + b10_9f = bw8mhz_b10_9f; + b10_a6 = bw8mhz_b10_a6; + b10_d9 = bw8mhz_b10_d9; + b17_38 = bw8mhz_b17_38; + d7val = 0; + iffreq = MAKE_IFFREQ_CONFIG(4.80); + break; + case 7000000: + b10_9f = bw7mhz_b10_9f; + b10_a6 = bw7mhz_b10_a6; + b10_d9 = bw7mhz_b10_d9; + b17_38 = bw7mhz_b17_38; + d7val = 2; + iffreq = MAKE_IFFREQ_CONFIG(4.20); + break; + case 6000000: + b10_9f = bw6mhz_b10_9f; + b10_a6 = bw6mhz_b10_a6; + b10_d9 = bw6mhz_b10_d9; + b17_38 = bw6mhz_b17_38; + d7val = 4; + iffreq = MAKE_IFFREQ_CONFIG(3.60); + break; + case 5000000: + b10_9f = bw5mhz_b10_9f; + b10_a6 = bw5mhz_b10_a6; + b10_d9 = bw5mhz_b10_d9; + b17_38 = bw5mhz_b17_38; + d7val = 6; + iffreq = MAKE_IFFREQ_CONFIG(3.60); + break; + default: + dev_dbg(&priv->i2c->dev, "%s(): invalid bandwidth %d\n", + __func__, bandwidth); + return -EINVAL; + } + /* */ + b10_b6[0] = (u8) ((iffreq >> 16) & 0xff); + b10_b6[1] = (u8)((iffreq >> 8) & 0xff); + b10_b6[2] = (u8)(iffreq & 0xff); + cxd2841er_write_regs( + priv, I2C_SLVT, 0x9f, b10_9f, sizeof(bw8mhz_b10_9f)); + cxd2841er_write_regs( + priv, I2C_SLVT, 0xa6, b10_a6, sizeof(bw8mhz_b10_a6)); + cxd2841er_write_regs(priv, I2C_SLVT, 0xb6, b10_b6, sizeof(b10_b6)); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xd7, d7val, 0x7); + cxd2841er_write_regs( + priv, I2C_SLVT, 0xd9, b10_d9, sizeof(bw8mhz_b10_d9)); + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x17); + cxd2841er_write_regs( + priv, I2C_SLVT, 0x38, b17_38, sizeof(bw8mhz_b17_38)); + return 0; +} + +static int cxd2841er_sleep_tc_to_active_c_band(struct cxd2841er_priv *priv, + u32 bandwidth) +{ + u8 bw7_8mhz_b10_a6[] = { + 0x2D, 0xC7, 0x04, 0xF4, 0x07, 0xC5, 0x2A, 0xB8, + 0x27, 0x9E, 0x27, 0xA4, 0x29, 0xAB }; + u8 bw6mhz_b10_a6[] = { + 0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8, + 0x00, 0xCF, 0x00, 0xE6, 0x23, 0xA4 }; + u8 b10_b6[3]; + u32 iffreq; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + switch (bandwidth) { + case 8000000: + case 7000000: + cxd2841er_write_regs( + priv, I2C_SLVT, 0xa6, + bw7_8mhz_b10_a6, sizeof(bw7_8mhz_b10_a6)); + iffreq = MAKE_IFFREQ_CONFIG(4.9); + break; + case 6000000: + cxd2841er_write_regs( + priv, I2C_SLVT, 0xa6, + bw6mhz_b10_a6, sizeof(bw6mhz_b10_a6)); + iffreq = MAKE_IFFREQ_CONFIG(3.7); + break; + default: + dev_dbg(&priv->i2c->dev, "%s(): unsupported bandwidth %d\n", + __func__, bandwidth); + return -EINVAL; + } + /* */ + b10_b6[0] = (u8) ((iffreq >> 16) & 0xff); + b10_b6[1] = (u8)((iffreq >> 8) & 0xff); + b10_b6[2] = (u8)(iffreq & 0xff); + cxd2841er_write_regs(priv, I2C_SLVT, 0xb6, b10_b6, sizeof(b10_b6)); + /* Set SLV-T Bank : 0x11 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x11); + switch (bandwidth) { + case 8000000: + case 7000000: + cxd2841er_set_reg_bits( + priv, I2C_SLVT, 0xa3, 0x00, 0x1f); + break; + case 6000000: + cxd2841er_set_reg_bits( + priv, I2C_SLVT, 0xa3, 0x14, 0x1f); + break; + } + /* Set SLV-T Bank : 0x40 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x40); + switch (bandwidth) { + case 8000000: + cxd2841er_set_reg_bits( + priv, I2C_SLVT, 0x26, 0x0b, 0x0f); + cxd2841er_write_reg(priv, I2C_SLVT, 0x27, 0x3e); + break; + case 7000000: + cxd2841er_set_reg_bits( + priv, I2C_SLVT, 0x26, 0x09, 0x0f); + cxd2841er_write_reg(priv, I2C_SLVT, 0x27, 0xd6); + break; + case 6000000: + cxd2841er_set_reg_bits( + priv, I2C_SLVT, 0x26, 0x08, 0x0f); + cxd2841er_write_reg(priv, I2C_SLVT, 0x27, 0x6e); + break; + } + return 0; +} + +static int cxd2841er_sleep_tc_to_active_t(struct cxd2841er_priv *priv, + u32 bandwidth) +{ + u8 data[2] = { 0x09, 0x54 }; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + cxd2841er_set_ts_clock_mode(priv, SYS_DVBT); + /* Set SLV-X Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); + /* Set demod mode */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x17, 0x01); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* Enable demod clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x01); + /* Disable RF level monitor */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2f, 0x00); + /* Enable ADC clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00); + /* Enable ADC 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x1a); + /* xtal freq 20.5MHz */ + cxd2841er_write_regs(priv, I2C_SLVT, 0x43, data, 2); + /* Enable ADC 4 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x00); + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + /* IFAGC gain settings */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xd2, 0x0c, 0x1f); + /* Set SLV-T Bank : 0x11 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x11); + /* BBAGC TARGET level setting */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x6a, 0x50); + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + /* ASCOT setting ON */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xa5, 0x01, 0x01); + /* Set SLV-T Bank : 0x18 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x18); + /* Pre-RS BER moniter setting */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x36, 0x40, 0x07); + /* FEC Auto Recovery setting */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x30, 0x01, 0x01); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x31, 0x01, 0x01); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* TSIF setting */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xce, 0x01, 0x01); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xcf, 0x01, 0x01); + cxd2841er_sleep_tc_to_active_t_band(priv, bandwidth); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* Disable HiZ Setting 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x28); + /* Disable HiZ Setting 2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0x00); + priv->state = STATE_ACTIVE_TC; + return 0; +} + +static int cxd2841er_sleep_tc_to_active_t2(struct cxd2841er_priv *priv, + u32 bandwidth) +{ + u8 data[2] = { 0x09, 0x54 }; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + cxd2841er_set_ts_clock_mode(priv, SYS_DVBT2); + /* Set SLV-X Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); + /* Set demod mode */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x17, 0x02); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* Enable demod clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x01); + /* Disable RF level monitor */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2f, 0x00); + /* Enable ADC clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00); + /* Enable ADC 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x1a); + /* xtal freq 20.5MHz */ + cxd2841er_write_regs(priv, I2C_SLVT, 0x43, data, 2); + /* Enable ADC 4 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x00); + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + /* IFAGC gain settings */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xd2, 0x0c, 0x1f); + /* Set SLV-T Bank : 0x11 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x11); + /* BBAGC TARGET level setting */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x6a, 0x50); + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + /* ASCOT setting ON */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xa5, 0x01, 0x01); + /* Set SLV-T Bank : 0x20 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x20); + /* Acquisition optimization setting */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x8b, 0x3c); + /* Set SLV-T Bank : 0x2b */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2b); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x76, 0x20, 0x70); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* TSIF setting */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xce, 0x01, 0x01); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xcf, 0x01, 0x01); + /* DVB-T2 initial setting */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x13); + cxd2841er_write_reg(priv, I2C_SLVT, 0x83, 0x10); + cxd2841er_write_reg(priv, I2C_SLVT, 0x86, 0x34); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x9e, 0x09, 0x0f); + cxd2841er_write_reg(priv, I2C_SLVT, 0x9f, 0xd8); + /* Set SLV-T Bank : 0x2a */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2a); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x38, 0x04, 0x0f); + /* Set SLV-T Bank : 0x2b */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2b); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x11, 0x20, 0x3f); + + cxd2841er_sleep_tc_to_active_t2_band(priv, bandwidth); + + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* Disable HiZ Setting 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x28); + /* Disable HiZ Setting 2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0x00); + priv->state = STATE_ACTIVE_TC; + return 0; +} + +static int cxd2841er_sleep_tc_to_active_c(struct cxd2841er_priv *priv, + u32 bandwidth) +{ + u8 data[2] = { 0x09, 0x54 }; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + cxd2841er_set_ts_clock_mode(priv, SYS_DVBC_ANNEX_A); + /* Set SLV-X Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00); + /* Set demod mode */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x17, 0x04); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* Enable demod clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x01); + /* Disable RF level monitor */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x2f, 0x00); + /* Enable ADC clock */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00); + /* Enable ADC 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x1a); + /* xtal freq 20.5MHz */ + cxd2841er_write_regs(priv, I2C_SLVT, 0x43, data, 2); + /* Enable ADC 4 */ + cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x00); + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + /* IFAGC gain settings */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xd2, 0x09, 0x1f); + /* Set SLV-T Bank : 0x11 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x11); + /* BBAGC TARGET level setting */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x6a, 0x48); + /* Set SLV-T Bank : 0x10 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + /* ASCOT setting ON */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xa5, 0x01, 0x01); + /* Set SLV-T Bank : 0x40 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x40); + /* Demod setting */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xc3, 0x00, 0x04); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* TSIF setting */ + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xce, 0x01, 0x01); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xcf, 0x01, 0x01); + + cxd2841er_sleep_tc_to_active_c_band(priv, 8000000); + /* Set SLV-T Bank : 0x00 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + /* Disable HiZ Setting 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x28); + /* Disable HiZ Setting 2 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0x00); + priv->state = STATE_ACTIVE_TC; + return 0; +} + +static int cxd2841er_get_frontend(struct dvb_frontend *fe) +{ + enum fe_status status = 0; + u16 strength = 0, snr = 0; + u32 errors = 0, ber = 0; + struct cxd2841er_priv *priv = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state == STATE_ACTIVE_S) + cxd2841er_read_status_s(fe, &status); + else if (priv->state == STATE_ACTIVE_TC) + cxd2841er_read_status_tc(fe, &status); + + if (status & FE_HAS_LOCK) { + cxd2841er_read_signal_strength(fe, &strength); + p->strength.len = 1; + p->strength.stat[0].scale = FE_SCALE_RELATIVE; + p->strength.stat[0].uvalue = strength; + cxd2841er_read_snr(fe, &snr); + p->cnr.len = 1; + p->cnr.stat[0].scale = FE_SCALE_DECIBEL; + p->cnr.stat[0].svalue = snr; + cxd2841er_read_ucblocks(fe, &errors); + p->block_error.len = 1; + p->block_error.stat[0].scale = FE_SCALE_COUNTER; + p->block_error.stat[0].uvalue = errors; + cxd2841er_read_ber(fe, &ber); + p->post_bit_error.len = 1; + p->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + p->post_bit_error.stat[0].uvalue = ber; + } else { + p->strength.len = 1; + p->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->cnr.len = 1; + p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->block_error.len = 1; + p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->post_bit_error.len = 1; + p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + return 0; +} + +static int cxd2841er_set_frontend_s(struct dvb_frontend *fe) +{ + int ret = 0, i, timeout, carr_offset; + enum fe_status status; + struct cxd2841er_priv *priv = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + u32 symbol_rate = p->symbol_rate/1000; + + dev_dbg(&priv->i2c->dev, "%s(): %s frequency=%d symbol_rate=%d\n", + __func__, + (p->delivery_system == SYS_DVBS ? "DVB-S" : "DVB-S2"), + p->frequency, symbol_rate); + switch (priv->state) { + case STATE_SLEEP_S: + ret = cxd2841er_sleep_s_to_active_s( + priv, p->delivery_system, symbol_rate); + break; + case STATE_ACTIVE_S: + ret = cxd2841er_retune_active(priv, p); + break; + default: + dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + ret = -EINVAL; + goto done; + } + if (ret) { + dev_dbg(&priv->i2c->dev, "%s(): tune failed\n", __func__); + goto done; + } + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (fe->ops.tuner_ops.set_params) + fe->ops.tuner_ops.set_params(fe); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + cxd2841er_tune_done(priv); + timeout = ((3000000 + (symbol_rate - 1)) / symbol_rate) + 150; + for (i = 0; i < timeout / CXD2841ER_DVBS_POLLING_INVL; i++) { + usleep_range(CXD2841ER_DVBS_POLLING_INVL*1000, + (CXD2841ER_DVBS_POLLING_INVL + 2) * 1000); + cxd2841er_read_status_s(fe, &status); + if (status & FE_HAS_LOCK) + break; + } + if (status & FE_HAS_LOCK) { + if (cxd2841er_get_carrier_offset_s_s2( + priv, &carr_offset)) { + ret = -EINVAL; + goto done; + } + dev_dbg(&priv->i2c->dev, "%s(): carrier_offset=%d\n", + __func__, carr_offset); + } +done: + return ret; +} + +static int cxd2841er_set_frontend_tc(struct dvb_frontend *fe) +{ + int ret = 0, timeout; + enum fe_status status; + struct cxd2841er_priv *priv = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (p->delivery_system == SYS_DVBT) { + priv->system = SYS_DVBT; + switch (priv->state) { + case STATE_SLEEP_TC: + ret = cxd2841er_sleep_tc_to_active_t( + priv, p->bandwidth_hz); + break; + case STATE_ACTIVE_TC: + ret = cxd2841er_retune_active(priv, p); + break; + default: + dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + ret = -EINVAL; + } + } else if (p->delivery_system == SYS_DVBT2) { + priv->system = SYS_DVBT2; + cxd2841er_dvbt2_set_plp_config(priv, + (int)(p->stream_id > 255), p->stream_id); + cxd2841er_dvbt2_set_profile(priv, DVBT2_PROFILE_BASE); + switch (priv->state) { + case STATE_SLEEP_TC: + ret = cxd2841er_sleep_tc_to_active_t2(priv, + p->bandwidth_hz); + break; + case STATE_ACTIVE_TC: + ret = cxd2841er_retune_active(priv, p); + break; + default: + dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + ret = -EINVAL; + } + } else if (p->delivery_system == SYS_DVBC_ANNEX_A || + p->delivery_system == SYS_DVBC_ANNEX_C) { + priv->system = SYS_DVBC_ANNEX_A; + switch (priv->state) { + case STATE_SLEEP_TC: + ret = cxd2841er_sleep_tc_to_active_c( + priv, p->bandwidth_hz); + break; + case STATE_ACTIVE_TC: + ret = cxd2841er_retune_active(priv, p); + break; + default: + dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + ret = -EINVAL; + } + } else { + dev_dbg(&priv->i2c->dev, + "%s(): invalid delivery system %d\n", + __func__, p->delivery_system); + ret = -EINVAL; + } + if (ret) + goto done; + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (fe->ops.tuner_ops.set_params) + fe->ops.tuner_ops.set_params(fe); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + cxd2841er_tune_done(priv); + timeout = 2500; + while (timeout > 0) { + ret = cxd2841er_read_status_tc(fe, &status); + if (ret) + goto done; + if (status & FE_HAS_LOCK) + break; + msleep(20); + timeout -= 20; + } + if (timeout < 0) + dev_dbg(&priv->i2c->dev, + "%s(): LOCK wait timeout\n", __func__); +done: + return ret; +} + +static int cxd2841er_tune_s(struct dvb_frontend *fe, + bool re_tune, + unsigned int mode_flags, + unsigned int *delay, + enum fe_status *status) +{ + int ret, carrier_offset; + struct cxd2841er_priv *priv = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + + dev_dbg(&priv->i2c->dev, "%s() re_tune=%d\n", __func__, re_tune); + if (re_tune) { + ret = cxd2841er_set_frontend_s(fe); + if (ret) + return ret; + cxd2841er_read_status_s(fe, status); + if (*status & FE_HAS_LOCK) { + if (cxd2841er_get_carrier_offset_s_s2( + priv, &carrier_offset)) + return -EINVAL; + p->frequency += carrier_offset; + ret = cxd2841er_set_frontend_s(fe); + if (ret) + return ret; + } + } + *delay = HZ / 5; + return cxd2841er_read_status_s(fe, status); +} + +static int cxd2841er_tune_tc(struct dvb_frontend *fe, + bool re_tune, + unsigned int mode_flags, + unsigned int *delay, + enum fe_status *status) +{ + int ret, carrier_offset; + struct cxd2841er_priv *priv = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + + dev_dbg(&priv->i2c->dev, "%s(): re_tune %d\n", __func__, re_tune); + if (re_tune) { + ret = cxd2841er_set_frontend_tc(fe); + if (ret) + return ret; + cxd2841er_read_status_tc(fe, status); + if (*status & FE_HAS_LOCK) { + switch (priv->system) { + case SYS_DVBT: + case SYS_DVBT2: + ret = cxd2841er_get_carrier_offset_t2( + priv, p->bandwidth_hz, + &carrier_offset); + break; + case SYS_DVBC_ANNEX_A: + ret = cxd2841er_get_carrier_offset_c( + priv, &carrier_offset); + break; + default: + dev_dbg(&priv->i2c->dev, + "%s(): invalid delivery system %d\n", + __func__, priv->system); + return -EINVAL; + } + if (ret) + return ret; + dev_dbg(&priv->i2c->dev, "%s(): carrier offset %d\n", + __func__, carrier_offset); + p->frequency += carrier_offset; + ret = cxd2841er_set_frontend_tc(fe); + if (ret) + return ret; + } + } + *delay = HZ / 5; + return cxd2841er_read_status_tc(fe, status); +} + +static int cxd2841er_sleep_s(struct dvb_frontend *fe) +{ + struct cxd2841er_priv *priv = fe->demodulator_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + cxd2841er_active_s_to_sleep_s(fe->demodulator_priv); + cxd2841er_sleep_s_to_shutdown(fe->demodulator_priv); + return 0; +} + +static int cxd2841er_sleep_tc(struct dvb_frontend *fe) +{ + struct cxd2841er_priv *priv = fe->demodulator_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + if (priv->state == STATE_ACTIVE_TC) { + switch (priv->system) { + case SYS_DVBT: + cxd2841er_active_t_to_sleep_tc(priv); + break; + case SYS_DVBT2: + cxd2841er_active_t2_to_sleep_tc(priv); + break; + case SYS_DVBC_ANNEX_A: + cxd2841er_active_c_to_sleep_tc(priv); + break; + default: + dev_warn(&priv->i2c->dev, + "%s(): unknown delivery system %d\n", + __func__, priv->system); + } + } + if (priv->state != STATE_SLEEP_TC) { + dev_err(&priv->i2c->dev, "%s(): invalid state %d\n", + __func__, priv->state); + return -EINVAL; + } + cxd2841er_sleep_tc_to_shutdown(priv); + return 0; +} + +static int cxd2841er_send_burst(struct dvb_frontend *fe, + enum fe_sec_mini_cmd burst) +{ + u8 data; + struct cxd2841er_priv *priv = fe->demodulator_priv; + + dev_dbg(&priv->i2c->dev, "%s(): burst mode %s\n", __func__, + (burst == SEC_MINI_A ? "A" : "B")); + if (priv->state != STATE_SLEEP_S && + priv->state != STATE_ACTIVE_S) { + dev_err(&priv->i2c->dev, "%s(): invalid demod state %d\n", + __func__, priv->state); + return -EINVAL; + } + data = (burst == SEC_MINI_A ? 0 : 1); + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xbb); + cxd2841er_write_reg(priv, I2C_SLVT, 0x34, 0x01); + cxd2841er_write_reg(priv, I2C_SLVT, 0x35, data); + return 0; +} + +static int cxd2841er_set_tone(struct dvb_frontend *fe, + enum fe_sec_tone_mode tone) +{ + u8 data; + struct cxd2841er_priv *priv = fe->demodulator_priv; + + dev_dbg(&priv->i2c->dev, "%s(): tone %s\n", __func__, + (tone == SEC_TONE_ON ? "On" : "Off")); + if (priv->state != STATE_SLEEP_S && + priv->state != STATE_ACTIVE_S) { + dev_err(&priv->i2c->dev, "%s(): invalid demod state %d\n", + __func__, priv->state); + return -EINVAL; + } + data = (tone == SEC_TONE_ON ? 1 : 0); + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xbb); + cxd2841er_write_reg(priv, I2C_SLVT, 0x36, data); + return 0; +} + +static int cxd2841er_send_diseqc_msg(struct dvb_frontend *fe, + struct dvb_diseqc_master_cmd *cmd) +{ + int i; + u8 data[12]; + struct cxd2841er_priv *priv = fe->demodulator_priv; + + if (priv->state != STATE_SLEEP_S && + priv->state != STATE_ACTIVE_S) { + dev_err(&priv->i2c->dev, "%s(): invalid demod state %d\n", + __func__, priv->state); + return -EINVAL; + } + dev_dbg(&priv->i2c->dev, + "%s(): cmd->len %d\n", __func__, cmd->msg_len); + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xbb); + /* DiDEqC enable */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x33, 0x01); + /* cmd1 length & data */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x3d, cmd->msg_len); + memset(data, 0, sizeof(data)); + for (i = 0; i < cmd->msg_len && i < sizeof(data); i++) + data[i] = cmd->msg[i]; + cxd2841er_write_regs(priv, I2C_SLVT, 0x3e, data, sizeof(data)); + /* repeat count for cmd1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x37, 1); + /* repeat count for cmd2: always 0 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x38, 0); + /* start transmit */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x32, 0x01); + /* wait for 1 sec timeout */ + for (i = 0; i < 50; i++) { + cxd2841er_read_reg(priv, I2C_SLVT, 0x10, data); + if (!data[0]) { + dev_dbg(&priv->i2c->dev, + "%s(): DiSEqC cmd has been sent\n", __func__); + return 0; + } + msleep(20); + } + dev_dbg(&priv->i2c->dev, + "%s(): DiSEqC cmd transmit timeout\n", __func__); + return -ETIMEDOUT; +} + +static void cxd2841er_release(struct dvb_frontend *fe) +{ + struct cxd2841er_priv *priv = fe->demodulator_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + kfree(priv); +} + +static int cxd2841er_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + struct cxd2841er_priv *priv = fe->demodulator_priv; + + dev_dbg(&priv->i2c->dev, "%s(): enable=%d\n", __func__, enable); + cxd2841er_set_reg_bits( + priv, I2C_SLVX, 0x8, (enable ? 0x01 : 0x00), 0x01); + return 0; +} + +static enum dvbfe_algo cxd2841er_get_algo(struct dvb_frontend *fe) +{ + struct cxd2841er_priv *priv = fe->demodulator_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + return DVBFE_ALGO_HW; +} + +static int cxd2841er_init_s(struct dvb_frontend *fe) +{ + struct cxd2841er_priv *priv = fe->demodulator_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + cxd2841er_shutdown_to_sleep_s(priv); + /* SONY_DEMOD_CONFIG_SAT_IFAGCNEG set to 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa0); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xb9, 0x01, 0x01); + return 0; +} + +static int cxd2841er_init_tc(struct dvb_frontend *fe) +{ + struct cxd2841er_priv *priv = fe->demodulator_priv; + + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); + cxd2841er_shutdown_to_sleep_tc(priv); + /* SONY_DEMOD_CONFIG_IFAGCNEG = 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xcb, 0x40, 0x40); + /* SONY_DEMOD_CONFIG_IFAGC_ADC_FS = 0 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0xcd, 0x50); + /* SONY_DEMOD_CONFIG_PARALLEL_SEL = 1 */ + cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00); + cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xc4, 0x00, 0x80); + return 0; +} + +static struct dvb_frontend_ops cxd2841er_dvbs_s2_ops; +static struct dvb_frontend_ops cxd2841er_dvbt_t2_ops; +static struct dvb_frontend_ops cxd2841er_dvbc_ops; + +static struct dvb_frontend *cxd2841er_attach(struct cxd2841er_config *cfg, + struct i2c_adapter *i2c, + u8 system) +{ + u8 chip_id = 0; + const char *type; + struct cxd2841er_priv *priv = NULL; + + /* allocate memory for the internal state */ + priv = kzalloc(sizeof(struct cxd2841er_priv), GFP_KERNEL); + if (!priv) + return NULL; + priv->i2c = i2c; + priv->config = cfg; + priv->i2c_addr_slvx = (cfg->i2c_addr + 4) >> 1; + priv->i2c_addr_slvt = (cfg->i2c_addr) >> 1; + /* create dvb_frontend */ + switch (system) { + case SYS_DVBS: + memcpy(&priv->frontend.ops, + &cxd2841er_dvbs_s2_ops, + sizeof(struct dvb_frontend_ops)); + type = "S/S2"; + break; + case SYS_DVBT: + memcpy(&priv->frontend.ops, + &cxd2841er_dvbt_t2_ops, + sizeof(struct dvb_frontend_ops)); + type = "T/T2"; + break; + case SYS_DVBC_ANNEX_A: + memcpy(&priv->frontend.ops, + &cxd2841er_dvbc_ops, + sizeof(struct dvb_frontend_ops)); + type = "C/C2"; + break; + default: + kfree(priv); + return NULL; + } + priv->frontend.demodulator_priv = priv; + dev_info(&priv->i2c->dev, + "%s(): attaching CXD2841ER DVB-%s frontend\n", + __func__, type); + dev_info(&priv->i2c->dev, + "%s(): I2C adapter %p SLVX addr %x SLVT addr %x\n", + __func__, priv->i2c, + priv->i2c_addr_slvx, priv->i2c_addr_slvt); + chip_id = cxd2841er_chip_id(priv); + if (chip_id != CXD2841ER_CHIP_ID) { + dev_err(&priv->i2c->dev, "%s(): invalid chip ID 0x%02x\n", + __func__, chip_id); + priv->frontend.demodulator_priv = NULL; + kfree(priv); + return NULL; + } + dev_info(&priv->i2c->dev, "%s(): chip ID 0x%02x OK.\n", + __func__, chip_id); + return &priv->frontend; +} + +struct dvb_frontend *cxd2841er_attach_s(struct cxd2841er_config *cfg, + struct i2c_adapter *i2c) +{ + return cxd2841er_attach(cfg, i2c, SYS_DVBS); +} +EXPORT_SYMBOL(cxd2841er_attach_s); + +struct dvb_frontend *cxd2841er_attach_t(struct cxd2841er_config *cfg, + struct i2c_adapter *i2c) +{ + return cxd2841er_attach(cfg, i2c, SYS_DVBT); +} +EXPORT_SYMBOL(cxd2841er_attach_t); + +struct dvb_frontend *cxd2841er_attach_c(struct cxd2841er_config *cfg, + struct i2c_adapter *i2c) +{ + return cxd2841er_attach(cfg, i2c, SYS_DVBC_ANNEX_A); +} +EXPORT_SYMBOL(cxd2841er_attach_c); + +static struct dvb_frontend_ops cxd2841er_dvbs_s2_ops = { + .delsys = { SYS_DVBS, SYS_DVBS2 }, + .info = { + .name = "Sony CXD2841ER DVB-S/S2 demodulator", + .frequency_min = 500000, + .frequency_max = 2500000, + .frequency_stepsize = 0, + .symbol_rate_min = 1000000, + .symbol_rate_max = 45000000, + .symbol_rate_tolerance = 500, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_AUTO | + FE_CAN_QPSK, + }, + .init = cxd2841er_init_s, + .sleep = cxd2841er_sleep_s, + .release = cxd2841er_release, + .set_frontend = cxd2841er_set_frontend_s, + .get_frontend = cxd2841er_get_frontend, + .read_status = cxd2841er_read_status_s, + .i2c_gate_ctrl = cxd2841er_i2c_gate_ctrl, + .get_frontend_algo = cxd2841er_get_algo, + .set_tone = cxd2841er_set_tone, + .diseqc_send_burst = cxd2841er_send_burst, + .diseqc_send_master_cmd = cxd2841er_send_diseqc_msg, + .tune = cxd2841er_tune_s +}; + +static struct dvb_frontend_ops cxd2841er_dvbt_t2_ops = { + .delsys = { SYS_DVBT, SYS_DVBT2 }, + .info = { + .name = "Sony CXD2841ER DVB-T/T2 demodulator", + .caps = FE_CAN_FEC_1_2 | + FE_CAN_FEC_2_3 | + FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | + FE_CAN_FEC_7_8 | + FE_CAN_FEC_AUTO | + FE_CAN_QPSK | + FE_CAN_QAM_16 | + FE_CAN_QAM_32 | + FE_CAN_QAM_64 | + FE_CAN_QAM_128 | + FE_CAN_QAM_256 | + FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_HIERARCHY_AUTO | + FE_CAN_MUTE_TS | + FE_CAN_2G_MODULATION, + .frequency_min = 42000000, + .frequency_max = 1002000000 + }, + .init = cxd2841er_init_tc, + .sleep = cxd2841er_sleep_tc, + .release = cxd2841er_release, + .set_frontend = cxd2841er_set_frontend_tc, + .get_frontend = cxd2841er_get_frontend, + .read_status = cxd2841er_read_status_tc, + .tune = cxd2841er_tune_tc, + .i2c_gate_ctrl = cxd2841er_i2c_gate_ctrl, + .get_frontend_algo = cxd2841er_get_algo +}; + +static struct dvb_frontend_ops cxd2841er_dvbc_ops = { + .delsys = { SYS_DVBC_ANNEX_A }, + .info = { + .name = "Sony CXD2841ER DVB-C demodulator", + .caps = FE_CAN_FEC_1_2 | + FE_CAN_FEC_2_3 | + FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | + FE_CAN_FEC_7_8 | + FE_CAN_FEC_AUTO | + FE_CAN_QAM_16 | + FE_CAN_QAM_32 | + FE_CAN_QAM_64 | + FE_CAN_QAM_128 | + FE_CAN_QAM_256 | + FE_CAN_QAM_AUTO | + FE_CAN_INVERSION_AUTO, + .frequency_min = 42000000, + .frequency_max = 1002000000 + }, + .init = cxd2841er_init_tc, + .sleep = cxd2841er_sleep_tc, + .release = cxd2841er_release, + .set_frontend = cxd2841er_set_frontend_tc, + .get_frontend = cxd2841er_get_frontend, + .read_status = cxd2841er_read_status_tc, + .tune = cxd2841er_tune_tc, + .i2c_gate_ctrl = cxd2841er_i2c_gate_ctrl, + .get_frontend_algo = cxd2841er_get_algo, +}; + +MODULE_DESCRIPTION("Sony CXD2841ER DVB-C/C2/T/T2/S/S2 demodulator driver"); +MODULE_AUTHOR("Sergey Kozlov "); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/cxd2841er.h b/drivers/media/dvb-frontends/cxd2841er.h new file mode 100644 index 000000000000..3472bdd58949 --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2841er.h @@ -0,0 +1,65 @@ +/* + * cxd2841er.h + * + * Sony CXD2441ER digital demodulator driver public definitions + * + * Copyright 2012 Sony Corporation + * Copyright (C) 2014 NetUP Inc. + * Copyright (C) 2014 Sergey Kozlov + * Copyright (C) 2014 Abylay Ospan + * + * 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. + */ + +#ifndef CXD2841ER_H +#define CXD2841ER_H + +#include +#include + +struct cxd2841er_config { + u8 i2c_addr; +}; + +#if IS_REACHABLE(CONFIG_DVB_CXD2841ER) +extern struct dvb_frontend *cxd2841er_attach_s(struct cxd2841er_config *cfg, + struct i2c_adapter *i2c); + +extern struct dvb_frontend *cxd2841er_attach_t(struct cxd2841er_config *cfg, + struct i2c_adapter *i2c); + +extern struct dvb_frontend *cxd2841er_attach_c(struct cxd2841er_config *cfg, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *cxd2841er_attach_s( + struct cxd2841er_config *cfg, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} + +static inline struct dvb_frontend *cxd2841er_attach_t( + struct cxd2841er_config *cfg, struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} + +static inline struct dvb_frontend *cxd2841er_attach_c( + struct cxd2841er_config *cfg, struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif diff --git a/drivers/media/dvb-frontends/cxd2841er_priv.h b/drivers/media/dvb-frontends/cxd2841er_priv.h new file mode 100644 index 000000000000..33e2f495277b --- /dev/null +++ b/drivers/media/dvb-frontends/cxd2841er_priv.h @@ -0,0 +1,43 @@ +/* + * cxd2841er_priv.h + * + * Sony CXD2441ER digital demodulator driver internal definitions + * + * Copyright 2012 Sony Corporation + * Copyright (C) 2014 NetUP Inc. + * Copyright (C) 2014 Sergey Kozlov + * Copyright (C) 2014 Abylay Ospan + * + * 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. + */ + +#ifndef CXD2841ER_PRIV_H +#define CXD2841ER_PRIV_H + +#define I2C_SLVX 0 +#define I2C_SLVT 1 + +#define CXD2841ER_CHIP_ID 0xa7 + +#define CXD2841ER_DVBS_POLLING_INVL 10 + +struct cxd2841er_cnr_data { + u32 value; + int cnr_x1000; +}; + +enum cxd2841er_dvbt2_profile_t { + DVBT2_PROFILE_ANY = 0, + DVBT2_PROFILE_BASE = 1, + DVBT2_PROFILE_LITE = 2 +}; + +#endif -- cgit v1.2.3 From c8946c8d5ab8725bd763fc98c0ec6e1e94e6f6a0 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 11 Aug 2015 15:08:47 -0300 Subject: [media] cxd2841er: declare static functions as such drivers/media/dvb-frontends/cxd2841er.c:992:5: warning: no previous prototype for 'cxd2841er_get_carrier_offset_t2' [-Wmissing-prototypes] int cxd2841er_get_carrier_offset_t2( ^ drivers/media/dvb-frontends/cxd2841er.c:1032:5: warning: no previous prototype for 'cxd2841er_get_carrier_offset_c' [-Wmissing-prototypes] int cxd2841er_get_carrier_offset_c( ^ drivers/media/dvb-frontends/cxd2841er.c:1360:5: warning: no previous prototype for 'cxd2841er_read_snr_t2' [-Wmissing-prototypes] int cxd2841er_read_snr_t2(struct cxd2841er_priv *priv, u32 *snr) ^ Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/cxd2841er.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/media/dvb-frontends/cxd2841er.c b/drivers/media/dvb-frontends/cxd2841er.c index d3813cc22770..0d1a15109d1e 100644 --- a/drivers/media/dvb-frontends/cxd2841er.c +++ b/drivers/media/dvb-frontends/cxd2841er.c @@ -989,8 +989,8 @@ static int cxd2841er_get_carrier_offset_s_s2(struct cxd2841er_priv *priv, return 0; } -int cxd2841er_get_carrier_offset_t2( - struct cxd2841er_priv *priv, u32 bandwidth, int *offset) +static int cxd2841er_get_carrier_offset_t2(struct cxd2841er_priv *priv, + u32 bandwidth, int *offset) { u8 data[4]; @@ -1029,8 +1029,8 @@ int cxd2841er_get_carrier_offset_t2( return 0; } -int cxd2841er_get_carrier_offset_c( - struct cxd2841er_priv *priv, int *offset) +static int cxd2841er_get_carrier_offset_c(struct cxd2841er_priv *priv, + int *offset) { u8 data[2]; @@ -1357,7 +1357,7 @@ static int cxd2841er_read_snr_t(struct cxd2841er_priv *priv, u32 *snr) return 0; } -int cxd2841er_read_snr_t2(struct cxd2841er_priv *priv, u32 *snr) +static int cxd2841er_read_snr_t2(struct cxd2841er_priv *priv, u32 *snr) { u32 reg; u8 data[2]; -- cgit v1.2.3 From 52b1eaf4c59a3bbd07afbb4ab4f43418a807d02e Mon Sep 17 00:00:00 2001 From: Kozlov Sergey Date: Tue, 28 Jul 2015 11:33:04 -0300 Subject: [media] netup_unidvb: NetUP Universal DVB-S/S2/T/T2/C PCI-E card driver Add NetUP Dual Universal CI PCIe board driver. The board has - two CI slots - two I2C adapters - SPI master bus for accessing flash memory containing FPGA firmware No changes required. Signed-off-by: Kozlov Sergey Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 9 + drivers/media/pci/Kconfig | 1 + drivers/media/pci/Makefile | 3 +- drivers/media/pci/netup_unidvb/Kconfig | 12 + drivers/media/pci/netup_unidvb/Makefile | 9 + drivers/media/pci/netup_unidvb/netup_unidvb.h | 133 +++ drivers/media/pci/netup_unidvb/netup_unidvb_ci.c | 248 +++++ drivers/media/pci/netup_unidvb/netup_unidvb_core.c | 1001 ++++++++++++++++++++ drivers/media/pci/netup_unidvb/netup_unidvb_i2c.c | 381 ++++++++ drivers/media/pci/netup_unidvb/netup_unidvb_spi.c | 252 +++++ 10 files changed, 2048 insertions(+), 1 deletion(-) create mode 100644 drivers/media/pci/netup_unidvb/Kconfig create mode 100644 drivers/media/pci/netup_unidvb/Makefile create mode 100644 drivers/media/pci/netup_unidvb/netup_unidvb.h create mode 100644 drivers/media/pci/netup_unidvb/netup_unidvb_ci.c create mode 100644 drivers/media/pci/netup_unidvb/netup_unidvb_core.c create mode 100644 drivers/media/pci/netup_unidvb/netup_unidvb_i2c.c create mode 100644 drivers/media/pci/netup_unidvb/netup_unidvb_spi.c diff --git a/MAINTAINERS b/MAINTAINERS index 805a55d5e700..00e92ff0d29f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6637,6 +6637,15 @@ T: git git://linuxtv.org/media_tree.git S: Supported F: drivers/media/dvb-frontends/lnbh25* +MEDIA DRIVERS FOR NETUP PCI UNIVERSAL DVB devices +M: Sergey Kozlov +L: linux-media@vger.kernel.org +W: http://linuxtv.org/ +W: http://netup.tv/ +T: git git://linuxtv.org/media_tree.git +S: Supported +F: drivers/media/pci/netup_unidvb/* + MEDIA INPUT INFRASTRUCTURE (V4L/DVB) M: Mauro Carvalho Chehab P: LinuxTV.org Project diff --git a/drivers/media/pci/Kconfig b/drivers/media/pci/Kconfig index e01a7688f22f..48a611bc3e18 100644 --- a/drivers/media/pci/Kconfig +++ b/drivers/media/pci/Kconfig @@ -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/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 + * Copyright (C) 2014 Abylay Ospan + * + * 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 +#include +#include +#include +#include +#include +#include + +#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 + * Copyright (C) 2014 Abylay Ospan + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#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 + * Copyright (C) 2014 Abylay Ospan + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + * Copyright (C) 2014 Abylay Ospan + * + * 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 +#include +#include +#include +#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 + * Copyright (C) 2014 Abylay Ospan + * + * 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 +#include +#include +#include + +#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; +} + + -- cgit v1.2.3 From d13a7b674af085eb67df73a20f5cc77310dd8602 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 11 Aug 2015 15:22:36 -0300 Subject: [media] cxd2841er: don't use variable length arrays The Linux stack is short; we need to be able to count the number of bytes used at stack on each function. So, we don't like to use variable-length arrays, as complained by smatch: drivers/media/dvb-frontends/cxd2841er.c:205:19: warning: Variable length array is used. The max usecase of the driver seems to be 15 bytes + 1 for the register. So, let's be safe and allocate 17 bytes for the write buffer. This should be enough to cover all cases. If not, let's print an error message. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/cxd2841er.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/media/dvb-frontends/cxd2841er.c b/drivers/media/dvb-frontends/cxd2841er.c index 0d1a15109d1e..fdffb2f0ded8 100644 --- a/drivers/media/dvb-frontends/cxd2841er.c +++ b/drivers/media/dvb-frontends/cxd2841er.c @@ -33,6 +33,8 @@ #include "cxd2841er.h" #include "cxd2841er_priv.h" +#define MAX_WRITE_REGSIZE 16 + enum cxd2841er_state { STATE_SHUTDOWN = 0, STATE_SLEEP_S, @@ -202,18 +204,24 @@ static int cxd2841er_write_regs(struct cxd2841er_priv *priv, u8 addr, u8 reg, const u8 *data, u32 len) { int ret; - u8 buf[len+1]; + u8 buf[MAX_WRITE_REGSIZE + 1]; u8 i2c_addr = (addr == I2C_SLVX ? priv->i2c_addr_slvx : priv->i2c_addr_slvt); struct i2c_msg msg[1] = { { .addr = i2c_addr, .flags = 0, - .len = sizeof(buf), + .len = len + 1, .buf = buf, } }; + if (len + 1 >= sizeof(buf)) { + dev_warn(&priv->i2c->dev,"wr reg=%04x: len=%d is too big!\n", + reg, len + 1); + return -E2BIG; + } + cxd2841er_i2c_debug(priv, i2c_addr, reg, 1, data, len); buf[0] = reg; memcpy(&buf[1], data, len); -- cgit v1.2.3 From 4aabd91625d0887210f68382f3fccb29a4586792 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 11 Aug 2015 15:29:54 -0300 Subject: [media] horus3a: don't use variable length arrays The Linux stack is short; we need to be able to count the number of bytes used at stack on each function. So, we don't like to use variable-length arrays, as complained by smatch: drivers/media/dvb-frontends/horus3a.c:57:19: warning: Variable length array is used. The max usecase of the driver seems to be 5 bytes + 1 for the register. So, let's be safe and allocate 6 bytes for the write buffer. This should be enough to cover all cases. If not, let's print an error message. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/horus3a.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/media/dvb-frontends/horus3a.c b/drivers/media/dvb-frontends/horus3a.c index 46a82dc586d8..5074305b289e 100644 --- a/drivers/media/dvb-frontends/horus3a.c +++ b/drivers/media/dvb-frontends/horus3a.c @@ -26,6 +26,8 @@ #include "horus3a.h" #include "dvb_frontend.h" +#define MAX_WRITE_REGSIZE 5 + enum horus3a_state { STATE_UNKNOWN, STATE_SLEEP, @@ -54,16 +56,22 @@ static int horus3a_write_regs(struct horus3a_priv *priv, u8 reg, const u8 *data, u32 len) { int ret; - u8 buf[len+1]; + u8 buf[MAX_WRITE_REGSIZE + 1]; struct i2c_msg msg[1] = { { .addr = priv->i2c_address, .flags = 0, - .len = sizeof(buf), + .len = len + 1, .buf = buf, } }; + if (len + 1 >= sizeof(buf)) { + dev_warn(&priv->i2c->dev,"wr reg=%04x: len=%d is too big!\n", + reg, len + 1); + return -E2BIG; + } + horus3a_i2c_debug(priv, reg, 1, data, len); buf[0] = reg; memcpy(&buf[1], data, len); -- cgit v1.2.3 From 8853780ec1e37bb91a1ccef9c05e412cb1cfd9e0 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 11 Aug 2015 15:36:15 -0300 Subject: [media] ascot2e: don't use variable length arrays The Linux stack is short; we need to be able to count the number of bytes used at stack on each function. So, we don't like to use variable-length arrays, as complained by smatch: drivers/media/dvb-frontends/horus3a.c:57:19: warning: Variable length array is used. The max usecase of the driver seems to be 10 bytes + 1 for the register. So, let's be safe and allocate 11 bytes for the write buffer. This should be enough to cover all cases. If not, let's print an error message. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/ascot2e.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/media/dvb-frontends/ascot2e.c b/drivers/media/dvb-frontends/ascot2e.c index ae7e463c2f9b..f770f6a2c987 100644 --- a/drivers/media/dvb-frontends/ascot2e.c +++ b/drivers/media/dvb-frontends/ascot2e.c @@ -26,6 +26,8 @@ #include "ascot2e.h" #include "dvb_frontend.h" +#define MAX_WRITE_REGSIZE 10 + enum ascot2e_state { STATE_UNKNOWN, STATE_SLEEP, @@ -120,16 +122,22 @@ static int ascot2e_write_regs(struct ascot2e_priv *priv, u8 reg, const u8 *data, u32 len) { int ret; - u8 buf[len+1]; + u8 buf[MAX_WRITE_REGSIZE + 1]; struct i2c_msg msg[1] = { { .addr = priv->i2c_address, .flags = 0, - .len = sizeof(buf), + .len = len + 1, .buf = buf, } }; + if (len + 1 >= sizeof(buf)) { + dev_warn(&priv->i2c->dev,"wr reg=%04x: len=%d is too big!\n", + reg, len + 1); + return -E2BIG; + } + ascot2e_i2c_debug(priv, reg, 1, data, len); buf[0] = reg; memcpy(&buf[1], data, len); -- cgit v1.2.3 From 86a10283e2d5e49b2e1827b0b0720734e7f69ff6 Mon Sep 17 00:00:00 2001 From: Peter Griffin Date: Thu, 30 Jul 2015 14:08:51 -0300 Subject: [media] stv0367: Refine i2c error trace to include i2c address When using stv0367 demodulator with STi STB platforms, we can have easily have four or more stv0367 demods running in the system at one time. As typically the b2120 reference design ships with a b2004a daughter board, which can accept two dvb NIM cards, and each b2100A NIM has 2x stv0367 demods and 2x NXPs tuner on it. In such circumstances it is useful to print the i2c address on error messages to know which one is failing due to I2C issues. Signed-off-by: Peter Griffin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/stv0367.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/media/dvb-frontends/stv0367.c b/drivers/media/dvb-frontends/stv0367.c index ec3e18e5ff50..9a49db1ae1ab 100644 --- a/drivers/media/dvb-frontends/stv0367.c +++ b/drivers/media/dvb-frontends/stv0367.c @@ -791,11 +791,13 @@ int stv0367_writeregs(struct stv0367_state *state, u16 reg, u8 *data, int len) memcpy(buf + 2, data, len); if (i2cdebug) - printk(KERN_DEBUG "%s: %02x: %02x\n", __func__, reg, buf[2]); + printk(KERN_DEBUG "%s: [%02x] %02x: %02x\n", __func__, + state->config->demod_address, reg, buf[2]); ret = i2c_transfer(state->i2c, &msg, 1); if (ret != 1) - printk(KERN_ERR "%s: i2c write error!\n", __func__); + printk(KERN_ERR "%s: i2c write error! ([%02x] %02x: %02x)\n", + __func__, state->config->demod_address, reg, buf[2]); return (ret != 1) ? -EREMOTEIO : 0; } @@ -829,10 +831,12 @@ static u8 stv0367_readreg(struct stv0367_state *state, u16 reg) ret = i2c_transfer(state->i2c, msg, 2); if (ret != 2) - printk(KERN_ERR "%s: i2c read error\n", __func__); + printk(KERN_ERR "%s: i2c read error ([%02x] %02x: %02x)\n", + __func__, state->config->demod_address, reg, b1[0]); if (i2cdebug) - printk(KERN_DEBUG "%s: %02x: %02x\n", __func__, reg, b1[0]); + printk(KERN_DEBUG "%s: [%02x] %02x: %02x\n", __func__, + state->config->demod_address, reg, b1[0]); return b1[0]; } -- cgit v1.2.3 From 6930f6696e73efe35e9e315588b1912c3d0d2df3 Mon Sep 17 00:00:00 2001 From: Peter Griffin Date: Thu, 30 Jul 2015 14:08:52 -0300 Subject: [media] stv0367: Add support for 16Mhz reference clock The B2100A dvb NIM card from ST has 2x stv0367 demodulators and 2x TDA18212 silicon tuners, with a 16Mhz crystal. To get this working properly with the upstream driver we need to add support for the 16Mhz reference clock. Signed-off-by: Peter Griffin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/stv0367.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/media/dvb-frontends/stv0367.c b/drivers/media/dvb-frontends/stv0367.c index 9a49db1ae1ab..44cb73f68af6 100644 --- a/drivers/media/dvb-frontends/stv0367.c +++ b/drivers/media/dvb-frontends/stv0367.c @@ -1554,6 +1554,11 @@ static int stv0367ter_init(struct dvb_frontend *fe) switch (state->config->xtal) { /*set internal freq to 53.125MHz */ + case 16000000: + stv0367_writereg(state, R367TER_PLLMDIV, 0x2); + stv0367_writereg(state, R367TER_PLLNDIV, 0x1b); + stv0367_writereg(state, R367TER_PLLSETUP, 0x18); + break; case 25000000: stv0367_writereg(state, R367TER_PLLMDIV, 0xa); stv0367_writereg(state, R367TER_PLLNDIV, 0x55); -- cgit v1.2.3 From 548146fbbc120404b716f548dc3183b41735aec5 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Thu, 30 Jul 2015 14:08:53 -0300 Subject: [media] dvb-pll: Convert struct dvb_pll_desc uses to const Convert the struct dvb_pll_desc uses to const and change the "entries" fixed array size from 12 to [] It saves a couple KB overall and remove ~5KB of data. $ size drivers/media/dvb-frontends/dvb-pll.o* text data bss dec hex filename 8520 1552 2120 12192 2fa0 drivers/media/dvb-frontends/dvb-pll.o.new 5624 6363 2120 14107 371b drivers/media/dvb-frontends/dvb-pll.o.old [PG] Patch taken from https://lkml.org/lkml/2015/6/24/721 with commit message updated. Signed-off-by: Joe Perches Signed-off-by: Peter Griffin Reviewed-by: Michael Ira Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/dvb-pll.c | 50 +++++++++++++++++------------------ 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/drivers/media/dvb-frontends/dvb-pll.c b/drivers/media/dvb-frontends/dvb-pll.c index 6d8fe8843237..53089e142715 100644 --- a/drivers/media/dvb-frontends/dvb-pll.c +++ b/drivers/media/dvb-frontends/dvb-pll.c @@ -34,7 +34,7 @@ struct dvb_pll_priv { struct i2c_adapter *i2c; /* the PLL descriptor */ - struct dvb_pll_desc *pll_desc; + const struct dvb_pll_desc *pll_desc; /* cached frequency/bandwidth */ u32 frequency; @@ -57,7 +57,7 @@ MODULE_PARM_DESC(id, "force pll id to use (DEBUG ONLY)"); /* ----------------------------------------------------------- */ struct dvb_pll_desc { - char *name; + const char *name; u32 min; u32 max; u32 iffreq; @@ -71,13 +71,13 @@ struct dvb_pll_desc { u32 stepsize; u8 config; u8 cb; - } entries[12]; + } entries[]; }; /* ----------------------------------------------------------- */ /* descriptions */ -static struct dvb_pll_desc dvb_pll_thomson_dtt7579 = { +static const struct dvb_pll_desc dvb_pll_thomson_dtt7579 = { .name = "Thomson dtt7579", .min = 177000000, .max = 858000000, @@ -99,7 +99,7 @@ static void thomson_dtt759x_bw(struct dvb_frontend *fe, u8 *buf) buf[3] |= 0x10; } -static struct dvb_pll_desc dvb_pll_thomson_dtt759x = { +static const struct dvb_pll_desc dvb_pll_thomson_dtt759x = { .name = "Thomson dtt759x", .min = 177000000, .max = 896000000, @@ -123,7 +123,7 @@ static void thomson_dtt7520x_bw(struct dvb_frontend *fe, u8 *buf) buf[3] ^= 0x10; } -static struct dvb_pll_desc dvb_pll_thomson_dtt7520x = { +static const struct dvb_pll_desc dvb_pll_thomson_dtt7520x = { .name = "Thomson dtt7520x", .min = 185000000, .max = 900000000, @@ -141,7 +141,7 @@ static struct dvb_pll_desc dvb_pll_thomson_dtt7520x = { }, }; -static struct dvb_pll_desc dvb_pll_lg_z201 = { +static const struct dvb_pll_desc dvb_pll_lg_z201 = { .name = "LG z201", .min = 174000000, .max = 862000000, @@ -157,7 +157,7 @@ static struct dvb_pll_desc dvb_pll_lg_z201 = { }, }; -static struct dvb_pll_desc dvb_pll_unknown_1 = { +static const struct dvb_pll_desc dvb_pll_unknown_1 = { .name = "unknown 1", /* used by dntv live dvb-t */ .min = 174000000, .max = 862000000, @@ -179,7 +179,7 @@ static struct dvb_pll_desc dvb_pll_unknown_1 = { /* Infineon TUA6010XS * used in Thomson Cable Tuner */ -static struct dvb_pll_desc dvb_pll_tua6010xs = { +static const struct dvb_pll_desc dvb_pll_tua6010xs = { .name = "Infineon TUA6010XS", .min = 44250000, .max = 858000000, @@ -193,7 +193,7 @@ static struct dvb_pll_desc dvb_pll_tua6010xs = { }; /* Panasonic env57h1xd5 (some Philips PLL ?) */ -static struct dvb_pll_desc dvb_pll_env57h1xd5 = { +static const struct dvb_pll_desc dvb_pll_env57h1xd5 = { .name = "Panasonic ENV57H1XD5", .min = 44250000, .max = 858000000, @@ -217,7 +217,7 @@ static void tda665x_bw(struct dvb_frontend *fe, u8 *buf) buf[3] |= 0x08; } -static struct dvb_pll_desc dvb_pll_tda665x = { +static const struct dvb_pll_desc dvb_pll_tda665x = { .name = "Philips TDA6650/TDA6651", .min = 44250000, .max = 858000000, @@ -251,7 +251,7 @@ static void tua6034_bw(struct dvb_frontend *fe, u8 *buf) buf[3] |= 0x08; } -static struct dvb_pll_desc dvb_pll_tua6034 = { +static const struct dvb_pll_desc dvb_pll_tua6034 = { .name = "Infineon TUA6034", .min = 44250000, .max = 858000000, @@ -275,7 +275,7 @@ static void tded4_bw(struct dvb_frontend *fe, u8 *buf) buf[3] |= 0x04; } -static struct dvb_pll_desc dvb_pll_tded4 = { +static const struct dvb_pll_desc dvb_pll_tded4 = { .name = "ALPS TDED4", .min = 47000000, .max = 863000000, @@ -293,7 +293,7 @@ static struct dvb_pll_desc dvb_pll_tded4 = { /* ALPS TDHU2 * used in AverTVHD MCE A180 */ -static struct dvb_pll_desc dvb_pll_tdhu2 = { +static const struct dvb_pll_desc dvb_pll_tdhu2 = { .name = "ALPS TDHU2", .min = 54000000, .max = 864000000, @@ -310,7 +310,7 @@ static struct dvb_pll_desc dvb_pll_tdhu2 = { /* Samsung TBMV30111IN / TBMV30712IN1 * used in Air2PC ATSC - 2nd generation (nxt2002) */ -static struct dvb_pll_desc dvb_pll_samsung_tbmv = { +static const struct dvb_pll_desc dvb_pll_samsung_tbmv = { .name = "Samsung TBMV30111IN / TBMV30712IN1", .min = 54000000, .max = 860000000, @@ -329,7 +329,7 @@ static struct dvb_pll_desc dvb_pll_samsung_tbmv = { /* * Philips SD1878 Tuner. */ -static struct dvb_pll_desc dvb_pll_philips_sd1878_tda8261 = { +static const struct dvb_pll_desc dvb_pll_philips_sd1878_tda8261 = { .name = "Philips SD1878", .min = 950000, .max = 2150000, @@ -395,7 +395,7 @@ static void opera1_bw(struct dvb_frontend *fe, u8 *buf) return; } -static struct dvb_pll_desc dvb_pll_opera1 = { +static const struct dvb_pll_desc dvb_pll_opera1 = { .name = "Opera Tuner", .min = 900000, .max = 2250000, @@ -442,7 +442,7 @@ static void samsung_dtos403ih102a_set(struct dvb_frontend *fe, u8 *buf) } /* unknown pll used in Samsung DTOS403IH102A DVB-C tuner */ -static struct dvb_pll_desc dvb_pll_samsung_dtos403ih102a = { +static const struct dvb_pll_desc dvb_pll_samsung_dtos403ih102a = { .name = "Samsung DTOS403IH102A", .min = 44250000, .max = 858000000, @@ -462,7 +462,7 @@ static struct dvb_pll_desc dvb_pll_samsung_dtos403ih102a = { }; /* Samsung TDTC9251DH0 DVB-T NIM, as used on AirStar 2 */ -static struct dvb_pll_desc dvb_pll_samsung_tdtc9251dh0 = { +static const struct dvb_pll_desc dvb_pll_samsung_tdtc9251dh0 = { .name = "Samsung TDTC9251DH0", .min = 48000000, .max = 863000000, @@ -476,7 +476,7 @@ static struct dvb_pll_desc dvb_pll_samsung_tdtc9251dh0 = { }; /* Samsung TBDU18132 DVB-S NIM with TSA5059 PLL, used in SkyStar2 DVB-S 2.3 */ -static struct dvb_pll_desc dvb_pll_samsung_tbdu18132 = { +static const struct dvb_pll_desc dvb_pll_samsung_tbdu18132 = { .name = "Samsung TBDU18132", .min = 950000, .max = 2150000, /* guesses */ @@ -497,7 +497,7 @@ static struct dvb_pll_desc dvb_pll_samsung_tbdu18132 = { }; /* Samsung TBMU24112 DVB-S NIM with SL1935 zero-IF tuner */ -static struct dvb_pll_desc dvb_pll_samsung_tbmu24112 = { +static const struct dvb_pll_desc dvb_pll_samsung_tbmu24112 = { .name = "Samsung TBMU24112", .min = 950000, .max = 2150000, /* guesses */ @@ -518,7 +518,7 @@ static struct dvb_pll_desc dvb_pll_samsung_tbmu24112 = { * 153 - 430 0 * 0 0 0 0 1 0 0x02 * 430 - 822 0 * 0 0 1 0 0 0 0x08 * 822 - 862 1 * 0 0 1 0 0 0 0x88 */ -static struct dvb_pll_desc dvb_pll_alps_tdee4 = { +static const struct dvb_pll_desc dvb_pll_alps_tdee4 = { .name = "ALPS TDEE4", .min = 47000000, .max = 862000000, @@ -534,7 +534,7 @@ static struct dvb_pll_desc dvb_pll_alps_tdee4 = { /* ----------------------------------------------------------- */ -static struct dvb_pll_desc *pll_list[] = { +static const struct dvb_pll_desc *pll_list[] = { [DVB_PLL_UNDEFINED] = NULL, [DVB_PLL_THOMSON_DTT7579] = &dvb_pll_thomson_dtt7579, [DVB_PLL_THOMSON_DTT759X] = &dvb_pll_thomson_dtt759x, @@ -564,7 +564,7 @@ static int dvb_pll_configure(struct dvb_frontend *fe, u8 *buf, const u32 frequency) { struct dvb_pll_priv *priv = fe->tuner_priv; - struct dvb_pll_desc *desc = priv->pll_desc; + const struct dvb_pll_desc *desc = priv->pll_desc; u32 div; int i; @@ -758,7 +758,7 @@ struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, int pll_addr, .buf = b1, .len = 1 }; struct dvb_pll_priv *priv = NULL; int ret; - struct dvb_pll_desc *desc; + const struct dvb_pll_desc *desc; if ((id[dvb_pll_devcount] > DVB_PLL_UNDEFINED) && (id[dvb_pll_devcount] < ARRAY_SIZE(pll_list))) -- cgit v1.2.3 From 179dd8c0348af75b02c7d72eaaf1cb179f1721ef Mon Sep 17 00:00:00 2001 From: Peter Griffin Date: Thu, 30 Jul 2015 14:08:54 -0300 Subject: [media] c8sectpfe: Add DT bindings documentation for c8sectpfe driver This patch adds the DT bindings documentation for the c8sectpfe LinuxDVB demux driver whose IP is in the STiH407 family silicon SoC's. Signed-off-by: Peter Griffin Signed-off-by: Mauro Carvalho Chehab --- .../bindings/media/stih407-c8sectpfe.txt | 89 ++++++++++++++++++++++ include/dt-bindings/media/c8sectpfe.h | 12 +++ 2 files changed, 101 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/stih407-c8sectpfe.txt create mode 100644 include/dt-bindings/media/c8sectpfe.h diff --git a/Documentation/devicetree/bindings/media/stih407-c8sectpfe.txt b/Documentation/devicetree/bindings/media/stih407-c8sectpfe.txt new file mode 100644 index 000000000000..d4def767bdfe --- /dev/null +++ b/Documentation/devicetree/bindings/media/stih407-c8sectpfe.txt @@ -0,0 +1,89 @@ +STMicroelectronics STi c8sectpfe binding +============================================ + +This document describes the c8sectpfe device bindings that is used to get transport +stream data into the SoC on the TS pins, and into DDR for further processing. + +It is typically used in conjunction with one or more demodulator and tuner devices +which converts from the RF to digital domain. Demodulators and tuners are usually +located on an external DVB frontend card connected to SoC TS input pins. + +Currently 7 TS input (tsin) channels are supported on the stih407 family SoC. + +Required properties (controller (parent) node): +- compatible : Should be "stih407-c8sectpfe" + +- reg : Address and length of register sets for each device in + "reg-names" + +- reg-names : The names of the register addresses corresponding to the + registers filled in "reg": + - c8sectpfe: c8sectpfe registers + - c8sectpfe-ram: c8sectpfe internal sram + +- clocks : phandle list of c8sectpfe clocks +- clock-names : should be "c8sectpfe" +See: Documentation/devicetree/bindings/clock/clock-bindings.txt + +- pinctrl-names : a pinctrl state named tsin%d-serial or tsin%d-parallel (where %d is tsin-num) + must be defined for each tsin child node. +- pinctrl-0 : phandle referencing pin configuration for this tsin configuration +See: Documentation/devicetree/bindings/pinctrl/pinctrl-binding.txt + + +Required properties (tsin (child) node): + +- tsin-num : tsin id of the InputBlock (must be between 0 to 6) +- i2c-bus : phandle to the I2C bus DT node which the demodulators & tuners on this tsin channel are connected. +- rst-gpio : reset gpio for this tsin channel. + +Optional properties (tsin (child) node): + +- invert-ts-clk : Bool property to control sense of ts input clock (data stored on falling edge of clk). +- serial-not-parallel : Bool property to configure input bus width (serial on ts_data<7>). +- async-not-sync : Bool property to control if data is received in asynchronous mode + (all bits/bytes with ts_valid or ts_packet asserted are valid). + +- dvb-card : Describes the NIM card connected to this tsin channel. + +Example: + +/* stih410 SoC b2120 + b2004a + stv0367-pll(NIMB) + stv0367-tda18212 (NIMA) DT example) */ + + c8sectpfe@08a20000 { + compatible = "st,stih407-c8sectpfe"; + status = "okay"; + reg = <0x08a20000 0x10000>, <0x08a00000 0x4000>; + reg-names = "stfe", "stfe-ram"; + interrupts = <0 34 0>, <0 35 0>; + interrupt-names = "stfe-error-irq", "stfe-idle-irq"; + + pinctrl-names = "tsin0-serial", "tsin0-parallel", "tsin3-serial", + "tsin4-serial", "tsin5-serial"; + + pinctrl-0 = <&pinctrl_tsin0_serial>; + pinctrl-1 = <&pinctrl_tsin0_parallel>; + pinctrl-2 = <&pinctrl_tsin3_serial>; + pinctrl-3 = <&pinctrl_tsin4_serial_alt3>; + pinctrl-4 = <&pinctrl_tsin5_serial_alt1>; + + clocks = <&clk_s_c0_flexgen CLK_PROC_STFE>; + clock-names = "stfe"; + + /* tsin0 is TSA on NIMA */ + tsin0: port@0 { + tsin-num = <0>; + serial-not-parallel; + i2c-bus = <&ssc2>; + rst-gpio = <&pio15 4 0>; + dvb-card = ; + }; + + tsin3: port@3 { + tsin-num = <3>; + serial-not-parallel; + i2c-bus = <&ssc3>; + rst-gpio = <&pio15 7 0>; + dvb-card = ; + }; + }; diff --git a/include/dt-bindings/media/c8sectpfe.h b/include/dt-bindings/media/c8sectpfe.h new file mode 100644 index 000000000000..a0b5c7be683c --- /dev/null +++ b/include/dt-bindings/media/c8sectpfe.h @@ -0,0 +1,12 @@ +#ifndef __DT_C8SECTPFE_H +#define __DT_C8SECTPFE_H + +#define STV0367_TDA18212_NIMA_1 0 +#define STV0367_TDA18212_NIMA_2 1 +#define STV0367_TDA18212_NIMB_1 2 +#define STV0367_TDA18212_NIMB_2 3 + +#define STV0903_6110_LNB24_NIMA 4 +#define STV0903_6110_LNB24_NIMB 5 + +#endif /* __DT_C8SECTPFE_H */ -- cgit v1.2.3 From 5d8877b6ae0d47897b821b8a11a2e8dee9a22686 Mon Sep 17 00:00:00 2001 From: Peter Griffin Date: Thu, 30 Jul 2015 14:08:55 -0300 Subject: [media] ARM: DT: STi: STiH407: Add c8sectpfe LinuxDVB DT node This patch adds in the required DT node for the c8sectpfe Linux DVB demux driver which allows the tsin channels to be used on an upstream kernel. Signed-off-by: Peter Griffin Signed-off-by: Mauro Carvalho Chehab --- arch/arm/boot/dts/stihxxx-b2120.dtsi | 38 ++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/arch/arm/boot/dts/stihxxx-b2120.dtsi b/arch/arm/boot/dts/stihxxx-b2120.dtsi index f589fe487f13..b5f31515ebf7 100644 --- a/arch/arm/boot/dts/stihxxx-b2120.dtsi +++ b/arch/arm/boot/dts/stihxxx-b2120.dtsi @@ -6,6 +6,10 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ + +#include +#include + / { soc { sbc_serial0: serial@9530000 { @@ -79,5 +83,39 @@ status = "okay"; }; + c8sectpfe@08a20000 { + compatible = "st,stih407-c8sectpfe"; + status = "okay"; + reg = <0x08a20000 0x10000>, <0x08a00000 0x4000>; + reg-names = "c8sectpfe", "c8sectpfe-ram"; + + interrupts = <0 34 0>, <0 35 0>; + interrupt-names = "c8sectpfe-error-irq", + "c8sectpfe-idle-irq"; + + pinctrl-names = "tsin0-serial", "tsin0-parallel", + "tsin3-serial", "tsin4-serial", + "tsin5-serial"; + + pinctrl-0 = <&pinctrl_tsin0_serial>; + pinctrl-1 = <&pinctrl_tsin0_parallel>; + pinctrl-2 = <&pinctrl_tsin3_serial>; + pinctrl-3 = <&pinctrl_tsin4_serial_alt3>; + pinctrl-4 = <&pinctrl_tsin5_serial_alt1>; + + clocks = <&clk_s_c0_flexgen CLK_PROC_STFE>; + clock-names = "c8sectpfe"; + + /* tsin0 is TSA on NIMA */ + tsin0: port@0 { + + tsin-num = <0>; + serial-not-parallel; + i2c-bus = <&ssc2>; + rst-gpio = <&pio15 4 0>; + + dvb-card = ; + }; + }; }; }; -- cgit v1.2.3 From c5f5d0f99794cfb675ecacfe37a1b33b352b9752 Mon Sep 17 00:00:00 2001 From: Peter Griffin Date: Thu, 30 Jul 2015 14:08:56 -0300 Subject: [media] c8sectpfe: STiH407/10 Linux DVB demux support This patch adds support for the c8sectpfe input HW found on STiH407/410 SoC's. It currently supports the TS input block, memdma engine and hw PID filtering blocks of the C8SECTPFE subsystem. The driver creates one LinuxDVB adapter, and a demux/dvr/frontend set of devices for each tsin channel which is specificed in the DT. It has been tested with multiple tsin channels tuned, locked, and grabbing TS simultaneously. Signed-off-by: Peter Griffin Signed-off-by: Mauro Carvalho Chehab --- .../media/platform/sti/c8sectpfe/c8sectpfe-core.c | 1235 ++++++++++++++++++++ .../media/platform/sti/c8sectpfe/c8sectpfe-core.h | 288 +++++ 2 files changed, 1523 insertions(+) create mode 100644 drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c create mode 100644 drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.h diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c new file mode 100644 index 000000000000..3a9109356e67 --- /dev/null +++ b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c @@ -0,0 +1,1235 @@ +/* + * c8sectpfe-core.c - C8SECTPFE STi DVB driver + * + * Copyright (c) STMicroelectronics 2015 + * + * Author:Peter Bennett + * Peter Griffin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "c8sectpfe-core.h" +#include "c8sectpfe-common.h" +#include "c8sectpfe-debugfs.h" +#include "dmxdev.h" +#include "dvb_demux.h" +#include "dvb_frontend.h" +#include "dvb_net.h" + +#define FIRMWARE_MEMDMA "pti_memdma_h407.elf" +MODULE_FIRMWARE(FIRMWARE_MEMDMA); + +#define PID_TABLE_SIZE 1024 +#define POLL_MSECS 50 + +static int load_c8sectpfe_fw_step1(struct c8sectpfei *fei); + +#define TS_PKT_SIZE 188 +#define HEADER_SIZE (4) +#define PACKET_SIZE (TS_PKT_SIZE+HEADER_SIZE) + +#define FEI_ALIGNMENT (32) +/* hw requires minimum of 8*PACKET_SIZE and padded to 8byte boundary */ +#define FEI_BUFFER_SIZE (8*PACKET_SIZE*340) + +#define FIFO_LEN 1024 + +static void c8sectpfe_timer_interrupt(unsigned long ac8sectpfei) +{ + struct c8sectpfei *fei = (struct c8sectpfei *)ac8sectpfei; + struct channel_info *channel; + int chan_num; + + /* iterate through input block channels */ + for (chan_num = 0; chan_num < fei->tsin_count; chan_num++) { + channel = fei->channel_data[chan_num]; + + /* is this descriptor initialised and TP enabled */ + if (channel->irec && readl(channel->irec + DMA_PRDS_TPENABLE)) + tasklet_schedule(&channel->tsklet); + } + + fei->timer.expires = jiffies + msecs_to_jiffies(POLL_MSECS); + add_timer(&fei->timer); +} + +static void channel_swdemux_tsklet(unsigned long data) +{ + struct channel_info *channel = (struct channel_info *)data; + struct c8sectpfei *fei = channel->fei; + unsigned long wp, rp; + int pos, num_packets, n, size; + u8 *buf; + + if (unlikely(!channel || !channel->irec)) + return; + + wp = readl(channel->irec + DMA_PRDS_BUSWP_TP(0)); + rp = readl(channel->irec + DMA_PRDS_BUSRP_TP(0)); + + pos = rp - channel->back_buffer_busaddr; + + /* has it wrapped */ + if (wp < rp) + wp = channel->back_buffer_busaddr + FEI_BUFFER_SIZE; + + size = wp - rp; + num_packets = size / PACKET_SIZE; + + /* manage cache so data is visible to CPU */ + dma_sync_single_for_cpu(fei->dev, + rp, + size, + DMA_FROM_DEVICE); + + buf = (u8 *) channel->back_buffer_aligned; + + dev_dbg(fei->dev, + "chan=%d channel=%p num_packets = %d, buf = %p, pos = 0x%x\n\t" + "rp=0x%lx, wp=0x%lx\n", + channel->tsin_id, channel, num_packets, buf, pos, rp, wp); + + for (n = 0; n < num_packets; n++) { + dvb_dmx_swfilter_packets( + &fei->c8sectpfe[0]-> + demux[channel->demux_mapping].dvb_demux, + &buf[pos], 1); + + pos += PACKET_SIZE; + } + + /* advance the read pointer */ + if (wp == (channel->back_buffer_busaddr + FEI_BUFFER_SIZE)) + writel(channel->back_buffer_busaddr, channel->irec + + DMA_PRDS_BUSRP_TP(0)); + else + writel(wp, channel->irec + DMA_PRDS_BUSWP_TP(0)); +} + +static int c8sectpfe_start_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + struct dvb_demux *demux = dvbdmxfeed->demux; + struct stdemux *stdemux = (struct stdemux *)demux->priv; + struct c8sectpfei *fei = stdemux->c8sectpfei; + struct channel_info *channel; + u32 tmp; + unsigned long *bitmap; + + switch (dvbdmxfeed->type) { + case DMX_TYPE_TS: + break; + case DMX_TYPE_SEC: + break; + default: + dev_err(fei->dev, "%s:%d Error bailing\n" + , __func__, __LINE__); + return -EINVAL; + } + + if (dvbdmxfeed->type == DMX_TYPE_TS) { + switch (dvbdmxfeed->pes_type) { + case DMX_PES_VIDEO: + case DMX_PES_AUDIO: + case DMX_PES_TELETEXT: + case DMX_PES_PCR: + case DMX_PES_OTHER: + break; + default: + dev_err(fei->dev, "%s:%d Error bailing\n" + , __func__, __LINE__); + return -EINVAL; + } + } + + if (!atomic_read(&fei->fw_loaded)) { + dev_err(fei->dev, "%s: c8sectpfe fw not loaded\n", __func__); + return -EINVAL; + } + + mutex_lock(&fei->lock); + + channel = fei->channel_data[stdemux->tsin_index]; + + bitmap = (unsigned long *) channel->pid_buffer_aligned; + + /* 8192 is a special PID */ + if (dvbdmxfeed->pid == 8192) { + tmp = readl(fei->io + C8SECTPFE_IB_PID_SET(channel->tsin_id)); + tmp &= ~C8SECTPFE_PID_ENABLE; + writel(tmp, fei->io + C8SECTPFE_IB_PID_SET(channel->tsin_id)); + + } else { + bitmap_set(bitmap, dvbdmxfeed->pid, 1); + } + + /* manage cache so PID bitmap is visible to HW */ + dma_sync_single_for_device(fei->dev, + channel->pid_buffer_busaddr, + PID_TABLE_SIZE, + DMA_TO_DEVICE); + + channel->active = 1; + + if (fei->global_feed_count == 0) { + fei->timer.expires = jiffies + + msecs_to_jiffies(msecs_to_jiffies(POLL_MSECS)); + + add_timer(&fei->timer); + } + + if (stdemux->running_feed_count == 0) { + + dev_dbg(fei->dev, "Starting channel=%p\n", channel); + + tasklet_init(&channel->tsklet, channel_swdemux_tsklet, + (unsigned long) channel); + + /* Reset the internal inputblock sram pointers */ + writel(channel->fifo, + fei->io + C8SECTPFE_IB_BUFF_STRT(channel->tsin_id)); + writel(channel->fifo + FIFO_LEN - 1, + fei->io + C8SECTPFE_IB_BUFF_END(channel->tsin_id)); + + writel(channel->fifo, + fei->io + C8SECTPFE_IB_READ_PNT(channel->tsin_id)); + writel(channel->fifo, + fei->io + C8SECTPFE_IB_WRT_PNT(channel->tsin_id)); + + + /* reset read / write memdma ptrs for this channel */ + writel(channel->back_buffer_busaddr, channel->irec + + DMA_PRDS_BUSBASE_TP(0)); + + tmp = channel->back_buffer_busaddr + FEI_BUFFER_SIZE - 1; + writel(tmp, channel->irec + DMA_PRDS_BUSTOP_TP(0)); + + writel(channel->back_buffer_busaddr, channel->irec + + DMA_PRDS_BUSWP_TP(0)); + + /* Issue a reset and enable InputBlock */ + writel(C8SECTPFE_SYS_ENABLE | C8SECTPFE_SYS_RESET + , fei->io + C8SECTPFE_IB_SYS(channel->tsin_id)); + + /* and enable the tp */ + writel(0x1, channel->irec + DMA_PRDS_TPENABLE); + + dev_dbg(fei->dev, "%s:%d Starting DMA feed on stdemux=%p\n" + , __func__, __LINE__, stdemux); + } + + stdemux->running_feed_count++; + fei->global_feed_count++; + + mutex_unlock(&fei->lock); + + return 0; +} + +static int c8sectpfe_stop_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + + struct dvb_demux *demux = dvbdmxfeed->demux; + struct stdemux *stdemux = (struct stdemux *)demux->priv; + struct c8sectpfei *fei = stdemux->c8sectpfei; + struct channel_info *channel; + int idlereq; + u32 tmp; + int ret; + unsigned long *bitmap; + + if (!atomic_read(&fei->fw_loaded)) { + dev_err(fei->dev, "%s: c8sectpfe fw not loaded\n", __func__); + return -EINVAL; + } + + mutex_lock(&fei->lock); + + channel = fei->channel_data[stdemux->tsin_index]; + + bitmap = (unsigned long *) channel->pid_buffer_aligned; + + if (dvbdmxfeed->pid == 8192) { + tmp = readl(fei->io + C8SECTPFE_IB_PID_SET(channel->tsin_id)); + tmp |= C8SECTPFE_PID_ENABLE; + writel(tmp, fei->io + C8SECTPFE_IB_PID_SET(channel->tsin_id)); + } else { + bitmap_clear(bitmap, dvbdmxfeed->pid, 1); + } + + /* manage cache so data is visible to HW */ + dma_sync_single_for_device(fei->dev, + channel->pid_buffer_busaddr, + PID_TABLE_SIZE, + DMA_TO_DEVICE); + + if (--stdemux->running_feed_count == 0) { + + channel = fei->channel_data[stdemux->tsin_index]; + + /* TP re-configuration on page 168 of functional spec */ + + /* disable IB (prevents more TS data going to memdma) */ + writel(0, fei->io + C8SECTPFE_IB_SYS(channel->tsin_id)); + + /* disable this channels descriptor */ + writel(0, channel->irec + DMA_PRDS_TPENABLE); + + tasklet_disable(&channel->tsklet); + + /* now request memdma channel goes idle */ + idlereq = (1 << channel->tsin_id) | IDLEREQ; + writel(idlereq, fei->io + DMA_IDLE_REQ); + + /* wait for idle irq handler to signal completion */ + ret = wait_for_completion_timeout(&channel->idle_completion, + msecs_to_jiffies(100)); + + if (ret == 0) + dev_warn(fei->dev, + "Timeout waiting for idle irq on tsin%d\n", + channel->tsin_id); + + reinit_completion(&channel->idle_completion); + + /* reset read / write ptrs for this channel */ + + writel(channel->back_buffer_busaddr, + channel->irec + DMA_PRDS_BUSBASE_TP(0)); + + tmp = channel->back_buffer_busaddr + FEI_BUFFER_SIZE - 1; + writel(tmp, channel->irec + DMA_PRDS_BUSTOP_TP(0)); + + writel(channel->back_buffer_busaddr, + channel->irec + DMA_PRDS_BUSWP_TP(0)); + + dev_dbg(fei->dev, + "%s:%d stopping DMA feed on stdemux=%p channel=%d\n", + __func__, __LINE__, stdemux, channel->tsin_id); + + /* turn off all PIDS in the bitmap */ + memset((void *)channel->pid_buffer_aligned + , 0x00, PID_TABLE_SIZE); + + /* manage cache so data is visible to HW */ + dma_sync_single_for_device(fei->dev, + channel->pid_buffer_busaddr, + PID_TABLE_SIZE, + DMA_TO_DEVICE); + + channel->active = 0; + } + + if (--fei->global_feed_count == 0) { + dev_dbg(fei->dev, "%s:%d global_feed_count=%d\n" + , __func__, __LINE__, fei->global_feed_count); + + del_timer(&fei->timer); + } + + mutex_unlock(&fei->lock); + + return 0; +} + +static struct channel_info *find_channel(struct c8sectpfei *fei, int tsin_num) +{ + int i; + + for (i = 0; i < C8SECTPFE_MAXCHANNEL; i++) { + if (!fei->channel_data[i]) + continue; + + if (fei->channel_data[i]->tsin_id == tsin_num) + return fei->channel_data[i]; + } + + return NULL; +} + +static void c8sectpfe_getconfig(struct c8sectpfei *fei) +{ + struct c8sectpfe_hw *hw = &fei->hw_stats; + + hw->num_ib = readl(fei->io + SYS_CFG_NUM_IB); + hw->num_mib = readl(fei->io + SYS_CFG_NUM_MIB); + hw->num_swts = readl(fei->io + SYS_CFG_NUM_SWTS); + hw->num_tsout = readl(fei->io + SYS_CFG_NUM_TSOUT); + hw->num_ccsc = readl(fei->io + SYS_CFG_NUM_CCSC); + hw->num_ram = readl(fei->io + SYS_CFG_NUM_RAM); + hw->num_tp = readl(fei->io + SYS_CFG_NUM_TP); + + dev_info(fei->dev, "C8SECTPFE hw supports the following:\n"); + dev_info(fei->dev, "Input Blocks: %d\n", hw->num_ib); + dev_info(fei->dev, "Merged Input Blocks: %d\n", hw->num_mib); + dev_info(fei->dev, "Software Transport Stream Inputs: %d\n" + , hw->num_swts); + dev_info(fei->dev, "Transport Stream Output: %d\n", hw->num_tsout); + dev_info(fei->dev, "Cable Card Converter: %d\n", hw->num_ccsc); + dev_info(fei->dev, "RAMs supported by C8SECTPFE: %d\n", hw->num_ram); + dev_info(fei->dev, "Tango TPs supported by C8SECTPFE: %d\n" + , hw->num_tp); +} + +static irqreturn_t c8sectpfe_idle_irq_handler(int irq, void *priv) +{ + struct c8sectpfei *fei = priv; + struct channel_info *chan; + int bit; + unsigned long tmp = readl(fei->io + DMA_IDLE_REQ); + + /* page 168 of functional spec: Clear the idle request + by writing 0 to the C8SECTPFE_DMA_IDLE_REQ register. */ + + /* signal idle completion */ + for_each_set_bit(bit, &tmp, fei->hw_stats.num_ib) { + + chan = find_channel(fei, bit); + + if (chan) + complete(&chan->idle_completion); + } + + writel(0, fei->io + DMA_IDLE_REQ); + + return IRQ_HANDLED; +} + + +static void free_input_block(struct c8sectpfei *fei, struct channel_info *tsin) +{ + if (!fei || !tsin) + return; + + if (tsin->back_buffer_busaddr) + if (!dma_mapping_error(fei->dev, tsin->back_buffer_busaddr)) + dma_unmap_single(fei->dev, tsin->back_buffer_busaddr, + FEI_BUFFER_SIZE, DMA_BIDIRECTIONAL); + + kfree(tsin->back_buffer_start); + + if (tsin->pid_buffer_busaddr) + if (!dma_mapping_error(fei->dev, tsin->pid_buffer_busaddr)) + dma_unmap_single(fei->dev, tsin->pid_buffer_busaddr, + PID_TABLE_SIZE, DMA_BIDIRECTIONAL); + + kfree(tsin->pid_buffer_start); +} + +#define MAX_NAME 20 + +static int configure_memdma_and_inputblock(struct c8sectpfei *fei, + struct channel_info *tsin) +{ + int ret; + u32 tmp; + char tsin_pin_name[MAX_NAME]; + + if (!fei || !tsin) + return -EINVAL; + + dev_dbg(fei->dev, "%s:%d Configuring channel=%p tsin=%d\n" + , __func__, __LINE__, tsin, tsin->tsin_id); + + init_completion(&tsin->idle_completion); + + tsin->back_buffer_start = kzalloc(FEI_BUFFER_SIZE + + FEI_ALIGNMENT, GFP_KERNEL); + + if (!tsin->back_buffer_start) { + ret = -ENOMEM; + goto err_unmap; + } + + /* Ensure backbuffer is 32byte aligned */ + tsin->back_buffer_aligned = tsin->back_buffer_start + + FEI_ALIGNMENT; + + tsin->back_buffer_aligned = (void *) + (((uintptr_t) tsin->back_buffer_aligned) & ~0x1F); + + tsin->back_buffer_busaddr = dma_map_single(fei->dev, + (void *)tsin->back_buffer_aligned, + FEI_BUFFER_SIZE, + DMA_BIDIRECTIONAL); + + if (dma_mapping_error(fei->dev, tsin->back_buffer_busaddr)) { + dev_err(fei->dev, "failed to map back_buffer\n"); + ret = -EFAULT; + goto err_unmap; + } + + /* + * The pid buffer can be configured (in hw) for byte or bit + * per pid. By powers of deduction we conclude stih407 family + * is configured (at SoC design stage) for bit per pid. + */ + tsin->pid_buffer_start = kzalloc(2048, GFP_KERNEL); + + if (!tsin->pid_buffer_start) { + ret = -ENOMEM; + goto err_unmap; + } + + /* + * PID buffer needs to be aligned to size of the pid table + * which at bit per pid is 1024 bytes (8192 pids / 8). + * PIDF_BASE register enforces this alignment when writing + * the register. + */ + + tsin->pid_buffer_aligned = tsin->pid_buffer_start + + PID_TABLE_SIZE; + + tsin->pid_buffer_aligned = (void *) + (((uintptr_t) tsin->pid_buffer_aligned) & ~0x3ff); + + tsin->pid_buffer_busaddr = dma_map_single(fei->dev, + tsin->pid_buffer_aligned, + PID_TABLE_SIZE, + DMA_BIDIRECTIONAL); + + if (dma_mapping_error(fei->dev, tsin->pid_buffer_busaddr)) { + dev_err(fei->dev, "failed to map pid_bitmap\n"); + ret = -EFAULT; + goto err_unmap; + } + + /* manage cache so pid bitmap is visible to HW */ + dma_sync_single_for_device(fei->dev, + tsin->pid_buffer_busaddr, + PID_TABLE_SIZE, + DMA_TO_DEVICE); + + snprintf(tsin_pin_name, MAX_NAME, "tsin%d-%s", tsin->tsin_id, + (tsin->serial_not_parallel ? "serial" : "parallel")); + + tsin->pstate = pinctrl_lookup_state(fei->pinctrl, tsin_pin_name); + if (IS_ERR(tsin->pstate)) { + dev_err(fei->dev, "%s: pinctrl_lookup_state couldn't find %s state\n" + , __func__, tsin_pin_name); + ret = PTR_ERR(tsin->pstate); + goto err_unmap; + } + + ret = pinctrl_select_state(fei->pinctrl, tsin->pstate); + + if (ret) { + dev_err(fei->dev, "%s: pinctrl_select_state failed\n" + , __func__); + goto err_unmap; + } + + /* Enable this input block */ + tmp = readl(fei->io + SYS_INPUT_CLKEN); + tmp |= BIT(tsin->tsin_id); + writel(tmp, fei->io + SYS_INPUT_CLKEN); + + if (tsin->serial_not_parallel) + tmp |= C8SECTPFE_SERIAL_NOT_PARALLEL; + + if (tsin->invert_ts_clk) + tmp |= C8SECTPFE_INVERT_TSCLK; + + if (tsin->async_not_sync) + tmp |= C8SECTPFE_ASYNC_NOT_SYNC; + + tmp |= C8SECTPFE_ALIGN_BYTE_SOP | C8SECTPFE_BYTE_ENDIANNESS_MSB; + + writel(tmp, fei->io + C8SECTPFE_IB_IP_FMT_CFG(tsin->tsin_id)); + + writel(C8SECTPFE_SYNC(0x9) | + C8SECTPFE_DROP(0x9) | + C8SECTPFE_TOKEN(0x47), + fei->io + C8SECTPFE_IB_SYNCLCKDRP_CFG(tsin->tsin_id)); + + writel(TS_PKT_SIZE, fei->io + C8SECTPFE_IB_PKT_LEN(tsin->tsin_id)); + + /* Place the FIFO's at the end of the irec descriptors */ + + tsin->fifo = (tsin->tsin_id * FIFO_LEN); + + writel(tsin->fifo, fei->io + C8SECTPFE_IB_BUFF_STRT(tsin->tsin_id)); + writel(tsin->fifo + FIFO_LEN - 1, + fei->io + C8SECTPFE_IB_BUFF_END(tsin->tsin_id)); + + writel(tsin->fifo, fei->io + C8SECTPFE_IB_READ_PNT(tsin->tsin_id)); + writel(tsin->fifo, fei->io + C8SECTPFE_IB_WRT_PNT(tsin->tsin_id)); + + writel(tsin->pid_buffer_busaddr, + fei->io + PIDF_BASE(tsin->tsin_id)); + + dev_info(fei->dev, "chan=%d PIDF_BASE=0x%x pid_bus_addr=0x%x\n", + tsin->tsin_id, readl(fei->io + PIDF_BASE(tsin->tsin_id)), + tsin->pid_buffer_busaddr); + + /* Configure and enable HW PID filtering */ + + /* + * The PID value is created by assembling the first 8 bytes of + * the TS packet into a 64-bit word in big-endian format. A + * slice of that 64-bit word is taken from + * (PID_OFFSET+PID_NUM_BITS-1) to PID_OFFSET. + */ + tmp = (C8SECTPFE_PID_ENABLE | C8SECTPFE_PID_NUMBITS(13) + | C8SECTPFE_PID_OFFSET(40)); + + writel(tmp, fei->io + C8SECTPFE_IB_PID_SET(tsin->tsin_id)); + + dev_dbg(fei->dev, "chan=%d setting wp: %d, rp: %d, buf: %d-%d\n", + tsin->tsin_id, + readl(fei->io + C8SECTPFE_IB_WRT_PNT(tsin->tsin_id)), + readl(fei->io + C8SECTPFE_IB_READ_PNT(tsin->tsin_id)), + readl(fei->io + C8SECTPFE_IB_BUFF_STRT(tsin->tsin_id)), + readl(fei->io + C8SECTPFE_IB_BUFF_END(tsin->tsin_id))); + + /* Get base addpress of pointer record block from DMEM */ + tsin->irec = fei->io + DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + + readl(fei->io + DMA_PTRREC_BASE); + + /* fill out pointer record data structure */ + + /* advance pointer record block to our channel */ + tsin->irec += (tsin->tsin_id * DMA_PRDS_SIZE); + + writel(tsin->fifo, tsin->irec + DMA_PRDS_MEMBASE); + + writel(tsin->fifo + FIFO_LEN - 1, tsin->irec + DMA_PRDS_MEMTOP); + + writel((188 + 7)&~7, tsin->irec + DMA_PRDS_PKTSIZE); + + writel(0x1, tsin->irec + DMA_PRDS_TPENABLE); + + /* read/write pointers with physical bus address */ + + writel(tsin->back_buffer_busaddr, tsin->irec + DMA_PRDS_BUSBASE_TP(0)); + + tmp = tsin->back_buffer_busaddr + FEI_BUFFER_SIZE - 1; + writel(tmp, tsin->irec + DMA_PRDS_BUSTOP_TP(0)); + + writel(tsin->back_buffer_busaddr, tsin->irec + DMA_PRDS_BUSWP_TP(0)); + writel(tsin->back_buffer_busaddr, tsin->irec + DMA_PRDS_BUSRP_TP(0)); + + /* initialize tasklet */ + tasklet_init(&tsin->tsklet, channel_swdemux_tsklet, + (unsigned long) tsin); + + return 0; + +err_unmap: + free_input_block(fei, tsin); + return ret; +} + +static irqreturn_t c8sectpfe_error_irq_handler(int irq, void *priv) +{ + struct c8sectpfei *fei = priv; + + dev_err(fei->dev, "%s: error handling not yet implemented\n" + , __func__); + + /* + * TODO FIXME we should detect some error conditions here + * and ideally so something about them! + */ + + return IRQ_HANDLED; +} + +static int c8sectpfe_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *child, *np = dev->of_node; + struct c8sectpfei *fei; + struct resource *res; + int ret, index = 0; + struct channel_info *tsin; + + /* Allocate the c8sectpfei structure */ + fei = devm_kzalloc(dev, sizeof(struct c8sectpfei), GFP_KERNEL); + if (!fei) + return -ENOMEM; + + fei->dev = dev; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "c8sectpfe"); + fei->io = devm_ioremap_resource(dev, res); + if (IS_ERR(fei->io)) + return PTR_ERR(fei->io); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "c8sectpfe-ram"); + fei->sram = devm_ioremap_resource(dev, res); + if (IS_ERR(fei->sram)) + return PTR_ERR(fei->sram); + + fei->sram_size = res->end - res->start; + + fei->idle_irq = platform_get_irq_byname(pdev, "c8sectpfe-idle-irq"); + if (fei->idle_irq < 0) { + dev_err(dev, "Can't get c8sectpfe-idle-irq\n"); + return fei->idle_irq; + } + + fei->error_irq = platform_get_irq_byname(pdev, "c8sectpfe-error-irq"); + if (fei->error_irq < 0) { + dev_err(dev, "Can't get c8sectpfe-error-irq\n"); + return fei->error_irq; + } + + platform_set_drvdata(pdev, fei); + + fei->c8sectpfeclk = devm_clk_get(dev, "c8sectpfe"); + if (IS_ERR(fei->c8sectpfeclk)) { + dev_err(dev, "c8sectpfe clk not found\n"); + return PTR_ERR(fei->c8sectpfeclk); + } + + ret = clk_prepare_enable(fei->c8sectpfeclk); + if (ret) { + dev_err(dev, "Failed to enable c8sectpfe clock\n"); + return ret; + } + + /* to save power disable all IP's (on by default) */ + writel(0, fei->io + SYS_INPUT_CLKEN); + + /* Enable memdma clock */ + writel(MEMDMAENABLE, fei->io + SYS_OTHER_CLKEN); + + /* clear internal sram */ + memset_io(fei->sram, 0x0, fei->sram_size); + + c8sectpfe_getconfig(fei); + + ret = devm_request_irq(dev, fei->idle_irq, c8sectpfe_idle_irq_handler, + 0, "c8sectpfe-idle-irq", fei); + if (ret) { + dev_err(dev, "Can't register c8sectpfe-idle-irq IRQ.\n"); + goto err_clk_disable; + } + + ret = devm_request_irq(dev, fei->error_irq, + c8sectpfe_error_irq_handler, 0, + "c8sectpfe-error-irq", fei); + if (ret) { + dev_err(dev, "Can't register c8sectpfe-error-irq IRQ.\n"); + goto err_clk_disable; + } + + fei->tsin_count = of_get_child_count(np); + + if (fei->tsin_count > C8SECTPFE_MAX_TSIN_CHAN || + fei->tsin_count > fei->hw_stats.num_ib) { + + dev_err(dev, "More tsin declared than exist on SoC!\n"); + ret = -EINVAL; + goto err_clk_disable; + } + + fei->pinctrl = devm_pinctrl_get(dev); + + if (IS_ERR(fei->pinctrl)) { + dev_err(dev, "Error getting tsin pins\n"); + ret = PTR_ERR(fei->pinctrl); + goto err_clk_disable; + } + + for_each_child_of_node(np, child) { + struct device_node *i2c_bus; + + fei->channel_data[index] = devm_kzalloc(dev, + sizeof(struct channel_info), + GFP_KERNEL); + + if (!fei->channel_data[index]) { + ret = -ENOMEM; + goto err_clk_disable; + } + + tsin = fei->channel_data[index]; + + tsin->fei = fei; + + ret = of_property_read_u32(child, "tsin-num", &tsin->tsin_id); + if (ret) { + dev_err(&pdev->dev, "No tsin_num found\n"); + goto err_clk_disable; + } + + /* sanity check value */ + if (tsin->tsin_id > fei->hw_stats.num_ib) { + dev_err(&pdev->dev, + "tsin-num %d specified greater than number\n\t" + "of input block hw in SoC! (%d)", + tsin->tsin_id, fei->hw_stats.num_ib); + ret = -EINVAL; + goto err_clk_disable; + } + + tsin->invert_ts_clk = of_property_read_bool(child, + "invert-ts-clk"); + + tsin->serial_not_parallel = of_property_read_bool(child, + "serial-not-parallel"); + + tsin->async_not_sync = of_property_read_bool(child, + "async-not-sync"); + + ret = of_property_read_u32(child, "dvb-card", + &tsin->dvb_card); + if (ret) { + dev_err(&pdev->dev, "No dvb-card found\n"); + goto err_clk_disable; + } + + i2c_bus = of_parse_phandle(child, "i2c-bus", 0); + if (!i2c_bus) { + dev_err(&pdev->dev, "No i2c-bus found\n"); + goto err_clk_disable; + } + tsin->i2c_adapter = + of_find_i2c_adapter_by_node(i2c_bus); + if (!tsin->i2c_adapter) { + dev_err(&pdev->dev, "No i2c adapter found\n"); + of_node_put(i2c_bus); + goto err_clk_disable; + } + of_node_put(i2c_bus); + + tsin->rst_gpio = of_get_named_gpio(child, "rst-gpio", 0); + + ret = gpio_is_valid(tsin->rst_gpio); + if (!ret) { + dev_err(dev, + "reset gpio for tsin%d not valid (gpio=%d)\n", + tsin->tsin_id, tsin->rst_gpio); + goto err_clk_disable; + } + + ret = devm_gpio_request_one(dev, tsin->rst_gpio, + GPIOF_OUT_INIT_LOW, "NIM reset"); + if (ret && ret != -EBUSY) { + dev_err(dev, "Can't request tsin%d reset gpio\n" + , fei->channel_data[index]->tsin_id); + goto err_clk_disable; + } + + if (!ret) { + /* toggle reset lines */ + gpio_direction_output(tsin->rst_gpio, 0); + usleep_range(3500, 5000); + gpio_direction_output(tsin->rst_gpio, 1); + usleep_range(3000, 5000); + } + + tsin->demux_mapping = index; + + dev_dbg(fei->dev, + "channel=%p n=%d tsin_num=%d, invert-ts-clk=%d\n\t" + "serial-not-parallel=%d pkt-clk-valid=%d dvb-card=%d\n", + fei->channel_data[index], index, + tsin->tsin_id, tsin->invert_ts_clk, + tsin->serial_not_parallel, tsin->async_not_sync, + tsin->dvb_card); + + index++; + } + + /* Setup timer interrupt */ + init_timer(&fei->timer); + fei->timer.function = c8sectpfe_timer_interrupt; + fei->timer.data = (unsigned long)fei; + + mutex_init(&fei->lock); + + /* Get the configuration information about the tuners */ + ret = c8sectpfe_tuner_register_frontend(&fei->c8sectpfe[0], + (void *)fei, + c8sectpfe_start_feed, + c8sectpfe_stop_feed); + if (ret) { + dev_err(dev, "c8sectpfe_tuner_register_frontend failed (%d)\n", + ret); + goto err_clk_disable; + } + + /* ensure all other init has been done before requesting firmware */ + ret = load_c8sectpfe_fw_step1(fei); + if (ret) { + dev_err(dev, "Couldn't load slim core firmware\n"); + goto err_clk_disable; + } + + c8sectpfe_debugfs_init(fei); + + return 0; + +err_clk_disable: + /* TODO uncomment when upstream has taken a reference on this clk */ + /*clk_disable_unprepare(fei->c8sectpfeclk);*/ + return ret; +} + +static int c8sectpfe_remove(struct platform_device *pdev) +{ + struct c8sectpfei *fei = platform_get_drvdata(pdev); + struct channel_info *channel; + int i; + + wait_for_completion(&fei->fw_ack); + + c8sectpfe_tuner_unregister_frontend(fei->c8sectpfe[0], fei); + + /* + * Now loop through and un-configure each of the InputBlock resources + */ + for (i = 0; i < fei->tsin_count; i++) { + channel = fei->channel_data[i]; + free_input_block(fei, channel); + } + + c8sectpfe_debugfs_exit(fei); + + dev_info(fei->dev, "Stopping memdma SLIM core\n"); + if (readl(fei->io + DMA_CPU_RUN)) + writel(0x0, fei->io + DMA_CPU_RUN); + + /* unclock all internal IP's */ + if (readl(fei->io + SYS_INPUT_CLKEN)) + writel(0, fei->io + SYS_INPUT_CLKEN); + + if (readl(fei->io + SYS_OTHER_CLKEN)) + writel(0, fei->io + SYS_OTHER_CLKEN); + + /* TODO uncomment when upstream has taken a reference on this clk */ + /* + if (fei->c8sectpfeclk) + clk_disable_unprepare(fei->c8sectpfeclk); + */ + + return 0; +} + + +static int configure_channels(struct c8sectpfei *fei) +{ + int index = 0, ret; + struct channel_info *tsin; + struct device_node *child, *np = fei->dev->of_node; + + /* iterate round each tsin and configure memdma descriptor and IB hw */ + for_each_child_of_node(np, child) { + + tsin = fei->channel_data[index]; + + ret = configure_memdma_and_inputblock(fei, + fei->channel_data[index]); + + if (ret) { + dev_err(fei->dev, + "configure_memdma_and_inputblock failed\n"); + goto err_unmap; + } + index++; + } + + return 0; + +err_unmap: + for (index = 0; index < fei->tsin_count; index++) { + tsin = fei->channel_data[index]; + free_input_block(fei, tsin); + } + return ret; +} + +static int +c8sectpfe_elf_sanity_check(struct c8sectpfei *fei, const struct firmware *fw) +{ + struct elf32_hdr *ehdr; + char class; + + if (!fw) { + dev_err(fei->dev, "failed to load %s\n", FIRMWARE_MEMDMA); + return -EINVAL; + } + + if (fw->size < sizeof(struct elf32_hdr)) { + dev_err(fei->dev, "Image is too small\n"); + return -EINVAL; + } + + ehdr = (struct elf32_hdr *)fw->data; + + /* We only support ELF32 at this point */ + class = ehdr->e_ident[EI_CLASS]; + if (class != ELFCLASS32) { + dev_err(fei->dev, "Unsupported class: %d\n", class); + return -EINVAL; + } + + if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB) { + dev_err(fei->dev, "Unsupported firmware endianness\n"); + return -EINVAL; + } + + if (fw->size < ehdr->e_shoff + sizeof(struct elf32_shdr)) { + dev_err(fei->dev, "Image is too small\n"); + return -EINVAL; + } + + if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) { + dev_err(fei->dev, "Image is corrupted (bad magic)\n"); + return -EINVAL; + } + + /* Check ELF magic */ + ehdr = (Elf32_Ehdr *)fw->data; + if (ehdr->e_ident[EI_MAG0] != ELFMAG0 || + ehdr->e_ident[EI_MAG1] != ELFMAG1 || + ehdr->e_ident[EI_MAG2] != ELFMAG2 || + ehdr->e_ident[EI_MAG3] != ELFMAG3) { + dev_err(fei->dev, "Invalid ELF magic\n"); + return -EINVAL; + } + + if (ehdr->e_type != ET_EXEC) { + dev_err(fei->dev, "Unsupported ELF header type\n"); + return -EINVAL; + } + + if (ehdr->e_phoff > fw->size) { + dev_err(fei->dev, "Firmware size is too small\n"); + return -EINVAL; + } + + return 0; +} + + +static void load_imem_segment(struct c8sectpfei *fei, Elf32_Phdr *phdr, + const struct firmware *fw, u8 __iomem *dest, + int seg_num) +{ + const u8 *imem_src = fw->data + phdr->p_offset; + int i; + + /* + * For IMEM segments, the segment contains 24-bit + * instructions which must be padded to 32-bit + * instructions before being written. The written + * segment is padded with NOP instructions. + */ + + dev_dbg(fei->dev, + "Loading IMEM segment %d 0x%08x\n\t" + " (0x%x bytes) -> 0x%p (0x%x bytes)\n", seg_num, + phdr->p_paddr, phdr->p_filesz, + dest, phdr->p_memsz + phdr->p_memsz / 3); + + for (i = 0; i < phdr->p_filesz; i++) { + + writeb(readb((void __iomem *)imem_src), (void __iomem *)dest); + + /* Every 3 bytes, add an additional + * padding zero in destination */ + if (i % 3 == 2) { + dest++; + writeb(0x00, (void __iomem *)dest); + } + + dest++; + imem_src++; + } +} + +static void load_dmem_segment(struct c8sectpfei *fei, Elf32_Phdr *phdr, + const struct firmware *fw, u8 __iomem *dst, int seg_num) +{ + /* + * For DMEM segments copy the segment data from the ELF + * file and pad segment with zeroes + */ + + dev_dbg(fei->dev, + "Loading DMEM segment %d 0x%08x\n\t" + "(0x%x bytes) -> 0x%p (0x%x bytes)\n", + seg_num, phdr->p_paddr, phdr->p_filesz, + dst, phdr->p_memsz); + + memcpy((void __iomem *)dst, (void *)fw->data + phdr->p_offset, + phdr->p_filesz); + + memset((void __iomem *)dst + phdr->p_filesz, 0, + phdr->p_memsz - phdr->p_filesz); +} + +static int load_slim_core_fw(const struct firmware *fw, void *context) +{ + struct c8sectpfei *fei = context; + Elf32_Ehdr *ehdr; + Elf32_Phdr *phdr; + u8 __iomem *dst; + int err, i; + + if (!fw || !context) + return -EINVAL; + + ehdr = (Elf32_Ehdr *)fw->data; + phdr = (Elf32_Phdr *)(fw->data + ehdr->e_phoff); + + /* go through the available ELF segments */ + for (i = 0; i < ehdr->e_phnum && !err; i++, phdr++) { + + /* Only consider LOAD segments */ + if (phdr->p_type != PT_LOAD) + continue; + + /* + * Check segment is contained within the fw->data buffer + */ + if (phdr->p_offset + phdr->p_filesz > fw->size) { + dev_err(fei->dev, + "Segment %d is outside of firmware file\n", i); + err = -EINVAL; + break; + } + + /* + * MEMDMA IMEM has executable flag set, otherwise load + * this segment into DMEM. + * + */ + + if (phdr->p_flags & PF_X) { + dst = (u8 __iomem *) fei->io + DMA_MEMDMA_IMEM; + /* + * The Slim ELF file uses 32-bit word addressing for + * load offsets. + */ + dst += (phdr->p_paddr & 0xFFFFF) * sizeof(unsigned int); + load_imem_segment(fei, phdr, fw, dst, i); + } else { + dst = (u8 __iomem *) fei->io + DMA_MEMDMA_DMEM; + /* + * The Slim ELF file uses 32-bit word addressing for + * load offsets. + */ + dst += (phdr->p_paddr & 0xFFFFF) * sizeof(unsigned int); + load_dmem_segment(fei, phdr, fw, dst, i); + } + } + + release_firmware(fw); + return err; +} + +static void load_c8sectpfe_fw_cb(const struct firmware *fw, void *context) +{ + struct c8sectpfei *fei = context; + int err; + + err = c8sectpfe_elf_sanity_check(fei, fw); + if (err) { + dev_err(fei->dev, "c8sectpfe_elf_sanity_check failed err=(%d)\n" + , err); + goto err; + } + + err = load_slim_core_fw(fw, context); + if (err) { + dev_err(fei->dev, "load_slim_core_fw failed err=(%d)\n", err); + goto err; + } + + /* now the firmware is loaded configure the input blocks */ + err = configure_channels(fei); + if (err) { + dev_err(fei->dev, "configure_channels failed err=(%d)\n", err); + goto err; + } + + /* + * STBus target port can access IMEM and DMEM ports + * without waiting for CPU + */ + writel(0x1, fei->io + DMA_PER_STBUS_SYNC); + + dev_info(fei->dev, "Boot the memdma SLIM core\n"); + writel(0x1, fei->io + DMA_CPU_RUN); + + atomic_set(&fei->fw_loaded, 1); +err: + complete_all(&fei->fw_ack); +} + +static int load_c8sectpfe_fw_step1(struct c8sectpfei *fei) +{ + int ret; + int err; + + dev_info(fei->dev, "Loading firmware: %s\n", FIRMWARE_MEMDMA); + + init_completion(&fei->fw_ack); + atomic_set(&fei->fw_loaded, 0); + + err = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + FIRMWARE_MEMDMA, fei->dev, GFP_KERNEL, fei, + load_c8sectpfe_fw_cb); + + if (err) { + dev_err(fei->dev, "request_firmware_nowait err: %d.\n", err); + complete_all(&fei->fw_ack); + return ret; + } + + return 0; +} + +static const struct of_device_id c8sectpfe_match[] = { + { .compatible = "st,stih407-c8sectpfe" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, c8sectpfe_match); + +static struct platform_driver c8sectpfe_driver = { + .driver = { + .name = "c8sectpfe", + .of_match_table = of_match_ptr(c8sectpfe_match), + }, + .probe = c8sectpfe_probe, + .remove = c8sectpfe_remove, +}; + +module_platform_driver(c8sectpfe_driver); + +MODULE_AUTHOR("Peter Bennett "); +MODULE_AUTHOR("Peter Griffin "); +MODULE_DESCRIPTION("C8SECTPFE STi DVB Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.h b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.h new file mode 100644 index 000000000000..39e7a221a941 --- /dev/null +++ b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.h @@ -0,0 +1,288 @@ +/* + * c8sectpfe-core.h - C8SECTPFE STi DVB driver + * + * Copyright (c) STMicroelectronics 2015 + * + * Author:Peter Bennett + * Peter Griffin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ +#ifndef _C8SECTPFE_CORE_H_ +#define _C8SECTPFE_CORE_H_ + +#define C8SECTPFEI_MAXCHANNEL 16 +#define C8SECTPFEI_MAXADAPTER 3 + +#define C8SECTPFE_MAX_TSIN_CHAN 8 + +struct channel_info { + + int tsin_id; + bool invert_ts_clk; + bool serial_not_parallel; + bool async_not_sync; + int i2c; + int dvb_card; + + int rst_gpio; + + struct i2c_adapter *i2c_adapter; + struct i2c_adapter *tuner_i2c; + struct i2c_adapter *lnb_i2c; + struct i2c_client *i2c_client; + struct dvb_frontend *frontend; + + struct pinctrl_state *pstate; + + int demux_mapping; + int active; + + void *back_buffer_start; + void *back_buffer_aligned; + dma_addr_t back_buffer_busaddr; + + void *pid_buffer_start; + void *pid_buffer_aligned; + dma_addr_t pid_buffer_busaddr; + + unsigned long fifo; + + struct completion idle_completion; + struct tasklet_struct tsklet; + + struct c8sectpfei *fei; + void __iomem *irec; + +}; + +struct c8sectpfe_hw { + int num_ib; + int num_mib; + int num_swts; + int num_tsout; + int num_ccsc; + int num_ram; + int num_tp; +}; + +struct c8sectpfei { + + struct device *dev; + struct pinctrl *pinctrl; + + struct dentry *root; + struct debugfs_regset32 *regset; + struct completion fw_ack; + atomic_t fw_loaded; + + int tsin_count; + + struct c8sectpfe_hw hw_stats; + + struct c8sectpfe *c8sectpfe[C8SECTPFEI_MAXADAPTER]; + + int mapping[C8SECTPFEI_MAXCHANNEL]; + + struct mutex lock; + + struct timer_list timer; /* timer interrupts for outputs */ + + void __iomem *io; + void __iomem *sram; + + unsigned long sram_size; + + struct channel_info *channel_data[C8SECTPFE_MAX_TSIN_CHAN]; + + struct clk *c8sectpfeclk; + int nima_rst_gpio; + int nimb_rst_gpio; + + int idle_irq; + int error_irq; + + int global_feed_count; +}; + +/* C8SECTPFE SYS Regs list */ + +#define SYS_INPUT_ERR_STATUS 0x0 +#define SYS_OTHER_ERR_STATUS 0x8 +#define SYS_INPUT_ERR_MASK 0x10 +#define SYS_OTHER_ERR_MASK 0x18 +#define SYS_DMA_ROUTE 0x20 +#define SYS_INPUT_CLKEN 0x30 +#define IBENABLE_MASK 0x7F + +#define SYS_OTHER_CLKEN 0x38 +#define TSDMAENABLE BIT(1) +#define MEMDMAENABLE BIT(0) + +#define SYS_CFG_NUM_IB 0x200 +#define SYS_CFG_NUM_MIB 0x204 +#define SYS_CFG_NUM_SWTS 0x208 +#define SYS_CFG_NUM_TSOUT 0x20C +#define SYS_CFG_NUM_CCSC 0x210 +#define SYS_CFG_NUM_RAM 0x214 +#define SYS_CFG_NUM_TP 0x218 + +/* Input Block Regs */ + +#define C8SECTPFE_INPUTBLK_OFFSET 0x1000 +#define C8SECTPFE_CHANNEL_OFFSET(x) ((x*0x40) + C8SECTPFE_INPUTBLK_OFFSET) + +#define C8SECTPFE_IB_IP_FMT_CFG(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x00) +#define C8SECTPFE_IGNORE_ERR_AT_SOP BIT(7) +#define C8SECTPFE_IGNORE_ERR_IN_PKT BIT(6) +#define C8SECTPFE_IGNORE_ERR_IN_BYTE BIT(5) +#define C8SECTPFE_INVERT_TSCLK BIT(4) +#define C8SECTPFE_ALIGN_BYTE_SOP BIT(3) +#define C8SECTPFE_ASYNC_NOT_SYNC BIT(2) +#define C8SECTPFE_BYTE_ENDIANNESS_MSB BIT(1) +#define C8SECTPFE_SERIAL_NOT_PARALLEL BIT(0) + +#define C8SECTPFE_IB_SYNCLCKDRP_CFG(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x04) +#define C8SECTPFE_SYNC(x) (x & 0xf) +#define C8SECTPFE_DROP(x) ((x<<4) & 0xf) +#define C8SECTPFE_TOKEN(x) ((x<<8) & 0xff00) +#define C8SECTPFE_SLDENDIANNESS BIT(16) + +#define C8SECTPFE_IB_TAGBYTES_CFG(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x08) +#define C8SECTPFE_TAG_HEADER(x) (x << 16) +#define C8SECTPFE_TAG_COUNTER(x) ((x<<1) & 0x7fff) +#define C8SECTPFE_TAG_ENABLE BIT(0) + +#define C8SECTPFE_IB_PID_SET(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x0C) +#define C8SECTPFE_PID_OFFSET(x) (x & 0x3f) +#define C8SECTPFE_PID_NUMBITS(x) ((x << 6) & 0xfff) +#define C8SECTPFE_PID_ENABLE BIT(31) + +#define C8SECTPFE_IB_PKT_LEN(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x10) + +#define C8SECTPFE_IB_BUFF_STRT(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x14) +#define C8SECTPFE_IB_BUFF_END(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x18) +#define C8SECTPFE_IB_READ_PNT(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x1C) +#define C8SECTPFE_IB_WRT_PNT(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x20) + +#define C8SECTPFE_IB_PRI_THRLD(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x24) +#define C8SECTPFE_PRI_VALUE(x) (x & 0x7fffff) +#define C8SECTPFE_PRI_LOWPRI(x) ((x & 0xf) << 24) +#define C8SECTPFE_PRI_HIGHPRI(x) ((x & 0xf) << 28) + +#define C8SECTPFE_IB_STAT(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x28) +#define C8SECTPFE_STAT_FIFO_OVERFLOW(x) (x & 0x1) +#define C8SECTPFE_STAT_BUFFER_OVERFLOW(x) (x & 0x2) +#define C8SECTPFE_STAT_OUTOFORDERRP(x) (x & 0x4) +#define C8SECTPFE_STAT_PID_OVERFLOW(x) (x & 0x8) +#define C8SECTPFE_STAT_PKT_OVERFLOW(x) (x & 0x10) +#define C8SECTPFE_STAT_ERROR_PACKETS(x) ((x >> 8) & 0xf) +#define C8SECTPFE_STAT_SHORT_PACKETS(x) ((x >> 12) & 0xf) + +#define C8SECTPFE_IB_MASK(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x2C) +#define C8SECTPFE_MASK_FIFO_OVERFLOW BIT(0) +#define C8SECTPFE_MASK_BUFFER_OVERFLOW BIT(1) +#define C8SECTPFE_MASK_OUTOFORDERRP(x) BIT(2) +#define C8SECTPFE_MASK_PID_OVERFLOW(x) BIT(3) +#define C8SECTPFE_MASK_PKT_OVERFLOW(x) BIT(4) +#define C8SECTPFE_MASK_ERROR_PACKETS(x) ((x & 0xf) << 8) +#define C8SECTPFE_MASK_SHORT_PACKETS(x) ((x & 0xf) >> 12) + +#define C8SECTPFE_IB_SYS(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x30) +#define C8SECTPFE_SYS_RESET BIT(1) +#define C8SECTPFE_SYS_ENABLE BIT(0) + +/* + * Ponter record data structure required for each input block + * see Table 82 on page 167 of functional specification. + */ + +#define DMA_PRDS_MEMBASE 0x0 /* Internal sram base address */ +#define DMA_PRDS_MEMTOP 0x4 /* Internal sram top address */ + +/* + * TS packet size, including tag bytes added by input block, + * rounded up to the next multiple of 8 bytes. The packet size, + * including any tagging bytes and rounded up to the nearest + * multiple of 8 bytes must be less than 255 bytes. + */ +#define DMA_PRDS_PKTSIZE 0x8 +#define DMA_PRDS_TPENABLE 0xc + +#define TP0_OFFSET 0x10 +#define DMA_PRDS_BUSBASE_TP(x) ((0x10*x) + TP0_OFFSET) +#define DMA_PRDS_BUSTOP_TP(x) ((0x10*x) + TP0_OFFSET + 0x4) +#define DMA_PRDS_BUSWP_TP(x) ((0x10*x) + TP0_OFFSET + 0x8) +#define DMA_PRDS_BUSRP_TP(x) ((0x10*x) + TP0_OFFSET + 0xc) + +#define DMA_PRDS_SIZE (0x20) + +#define DMA_MEMDMA_OFFSET 0x4000 +#define DMA_IMEM_OFFSET 0x0 +#define DMA_DMEM_OFFSET 0x4000 +#define DMA_CPU 0x8000 +#define DMA_PER_OFFSET 0xb000 + +#define DMA_MEMDMA_DMEM (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET) +#define DMA_MEMDMA_IMEM (DMA_MEMDMA_OFFSET + DMA_IMEM_OFFSET) + +/* XP70 Slim core regs */ +#define DMA_CPU_ID (DMA_MEMDMA_OFFSET + DMA_CPU + 0x0) +#define DMA_CPU_VCR (DMA_MEMDMA_OFFSET + DMA_CPU + 0x4) +#define DMA_CPU_RUN (DMA_MEMDMA_OFFSET + DMA_CPU + 0x8) +#define DMA_CPU_CLOCKGATE (DMA_MEMDMA_OFFSET + DMA_CPU + 0xc) +#define DMA_CPU_PC (DMA_MEMDMA_OFFSET + DMA_CPU + 0x20) + +/* Enable Interrupt for a IB */ +#define DMA_PER_TPn_DREQ_MASK (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xd00) +/* Ack interrupt by setting corresponding bit */ +#define DMA_PER_TPn_DACK_SET (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xd80) +#define DMA_PER_TPn_DREQ (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xe00) +#define DMA_PER_TPn_DACK (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xe80) +#define DMA_PER_DREQ_MODE (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xf80) +#define DMA_PER_STBUS_SYNC (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xf88) +#define DMA_PER_STBUS_ACCESS (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xf8c) +#define DMA_PER_STBUS_ADDRESS (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xf90) +#define DMA_PER_IDLE_INT (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfa8) +#define DMA_PER_PRIORITY (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfac) +#define DMA_PER_MAX_OPCODE (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfb0) +#define DMA_PER_MAX_CHUNK (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfb4) +#define DMA_PER_PAGE_SIZE (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfbc) +#define DMA_PER_MBOX_STATUS (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfc0) +#define DMA_PER_MBOX_SET (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfc8) +#define DMA_PER_MBOX_CLEAR (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfd0) +#define DMA_PER_MBOX_MASK (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfd8) +#define DMA_PER_INJECT_PKT_SRC (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfe0) +#define DMA_PER_INJECT_PKT_DEST (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfe4) +#define DMA_PER_INJECT_PKT_ADDR (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfe8) +#define DMA_PER_INJECT_PKT (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfec) +#define DMA_PER_PAT_PTR_INIT (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xff0) +#define DMA_PER_PAT_PTR (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xff4) +#define DMA_PER_SLEEP_MASK (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xff8) +#define DMA_PER_SLEEP_COUNTER (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xffc) +/* #define DMA_RF_CPUREGn DMA_RFBASEADDR n=0 to 15) slim regsa */ + +/* The following are from DMA_DMEM_BaseAddress */ +#define DMA_FIRMWARE_VERSION (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + 0x0) +#define DMA_PTRREC_BASE (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + 0x4) +#define DMA_PTRREC_INPUT_OFFSET (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + 0x8) +#define DMA_ERRREC_BASE (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + 0xc) +#define DMA_ERROR_RECORD(n) ((n*4) + DMA_ERRREC_BASE + 0x4) +#define DMA_IDLE_REQ (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + 0x10) +#define IDLEREQ BIT(31) + +#define DMA_FIRMWARE_CONFIG (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + 0x14) + +/* Regs for PID Filter */ + +#define PIDF_OFFSET 0x2800 +#define PIDF_BASE(n) ((n*4) + PIDF_OFFSET) +#define PIDF_LEAK_ENABLE (PIDF_OFFSET + 0x100) +#define PIDF_LEAK_STATUS (PIDF_OFFSET + 0x108) +#define PIDF_LEAK_COUNT_RESET (PIDF_OFFSET + 0x110) +#define PIDF_LEAK_COUNTER (PIDF_OFFSET + 0x114) + +#endif /* _C8SECTPFE_CORE_H_ */ -- cgit v1.2.3 From b5e208a6843225d90c297da273d4d2ebd027417b Mon Sep 17 00:00:00 2001 From: Peter Griffin Date: Thu, 30 Jul 2015 14:08:57 -0300 Subject: [media] c8sectpfe: Add LDVB helper functions These functions are used by the core code for creating the LDVB devices and adapter. Addtionally some older SoC's (and potentially newer ones) have different frontend HW which would allow those devices to be easily supported in the future by keeping the code specific to the IP separate from the more generic code. Signed-off-by: Peter Griffin Signed-off-by: Mauro Carvalho Chehab --- .../platform/sti/c8sectpfe/c8sectpfe-common.c | 265 +++++++++++++++++++++ .../platform/sti/c8sectpfe/c8sectpfe-common.h | 64 +++++ 2 files changed, 329 insertions(+) create mode 100644 drivers/media/platform/sti/c8sectpfe/c8sectpfe-common.c create mode 100644 drivers/media/platform/sti/c8sectpfe/c8sectpfe-common.h diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-common.c b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-common.c new file mode 100644 index 000000000000..95223ab71e19 --- /dev/null +++ b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-common.c @@ -0,0 +1,265 @@ +/* + * c8sectpfe-common.c - C8SECTPFE STi DVB driver + * + * Copyright (c) STMicroelectronics 2015 + * + * Author: Peter Griffin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dmxdev.h" +#include "dvbdev.h" +#include "dvb_demux.h" +#include "dvb_frontend.h" +#include "dvb_net.h" + +#include "c8sectpfe-common.h" +#include "c8sectpfe-core.h" +#include "c8sectpfe-dvb.h" + +static int register_dvb(struct stdemux *demux, struct dvb_adapter *adap, + void *start_feed, void *stop_feed, + struct c8sectpfei *fei) +{ + int result; + + demux->dvb_demux.dmx.capabilities = DMX_TS_FILTERING | + DMX_SECTION_FILTERING | + DMX_MEMORY_BASED_FILTERING; + + demux->dvb_demux.priv = demux; + demux->dvb_demux.filternum = C8SECTPFE_MAXCHANNEL; + demux->dvb_demux.feednum = C8SECTPFE_MAXCHANNEL; + + demux->dvb_demux.start_feed = start_feed; + demux->dvb_demux.stop_feed = stop_feed; + demux->dvb_demux.write_to_decoder = NULL; + + result = dvb_dmx_init(&demux->dvb_demux); + if (result < 0) { + dev_err(fei->dev, "dvb_dmx_init failed (errno = %d)\n", + result); + goto err_dmx; + } + + demux->dmxdev.filternum = demux->dvb_demux.filternum; + demux->dmxdev.demux = &demux->dvb_demux.dmx; + demux->dmxdev.capabilities = 0; + + result = dvb_dmxdev_init(&demux->dmxdev, adap); + if (result < 0) { + dev_err(fei->dev, "dvb_dmxdev_init failed (errno = %d)\n", + result); + + goto err_dmxdev; + } + + demux->hw_frontend.source = DMX_FRONTEND_0 + demux->tsin_index; + + result = demux->dvb_demux.dmx.add_frontend(&demux->dvb_demux.dmx, + &demux->hw_frontend); + if (result < 0) { + dev_err(fei->dev, "add_frontend failed (errno = %d)\n", result); + goto err_fe_hw; + } + + demux->mem_frontend.source = DMX_MEMORY_FE; + result = demux->dvb_demux.dmx.add_frontend(&demux->dvb_demux.dmx, + &demux->mem_frontend); + if (result < 0) { + dev_err(fei->dev, "add_frontend failed (%d)\n", result); + goto err_fe_mem; + } + + result = demux->dvb_demux.dmx.connect_frontend(&demux->dvb_demux.dmx, + &demux->hw_frontend); + if (result < 0) { + dev_err(fei->dev, "connect_frontend (%d)\n", result); + goto err_fe_con; + } + + return 0; + +err_fe_con: + demux->dvb_demux.dmx.remove_frontend(&demux->dvb_demux.dmx, + &demux->mem_frontend); +err_fe_mem: + demux->dvb_demux.dmx.remove_frontend(&demux->dvb_demux.dmx, + &demux->hw_frontend); +err_fe_hw: + dvb_dmxdev_release(&demux->dmxdev); +err_dmxdev: + dvb_dmx_release(&demux->dvb_demux); +err_dmx: + return result; + +} + +static void unregister_dvb(struct stdemux *demux) +{ + + demux->dvb_demux.dmx.remove_frontend(&demux->dvb_demux.dmx, + &demux->mem_frontend); + + demux->dvb_demux.dmx.remove_frontend(&demux->dvb_demux.dmx, + &demux->hw_frontend); + + dvb_dmxdev_release(&demux->dmxdev); + + dvb_dmx_release(&demux->dvb_demux); +} + +static struct c8sectpfe *c8sectpfe_create(struct c8sectpfei *fei, + void *start_feed, + void *stop_feed) +{ + struct c8sectpfe *c8sectpfe; + int result; + int i, j; + + short int ids[] = { -1 }; + + c8sectpfe = kzalloc(sizeof(struct c8sectpfe), GFP_KERNEL); + if (!c8sectpfe) + goto err1; + + mutex_init(&c8sectpfe->lock); + + c8sectpfe->device = fei->dev; + + result = dvb_register_adapter(&c8sectpfe->adapter, "STi c8sectpfe", + THIS_MODULE, fei->dev, ids); + if (result < 0) { + dev_err(fei->dev, "dvb_register_adapter failed (errno = %d)\n", + result); + goto err2; + } + + c8sectpfe->adapter.priv = fei; + + for (i = 0; i < fei->tsin_count; i++) { + + c8sectpfe->demux[i].tsin_index = i; + c8sectpfe->demux[i].c8sectpfei = fei; + + result = register_dvb(&c8sectpfe->demux[i], &c8sectpfe->adapter, + start_feed, stop_feed, fei); + if (result < 0) { + dev_err(fei->dev, + "register_dvb feed=%d failed (errno = %d)\n", + result, i); + + /* we take a all or nothing approach */ + for (j = 0; j < i; j++) + unregister_dvb(&c8sectpfe->demux[j]); + goto err3; + } + } + + c8sectpfe->num_feeds = fei->tsin_count; + + return c8sectpfe; +err3: + dvb_unregister_adapter(&c8sectpfe->adapter); +err2: + kfree(c8sectpfe); +err1: + return NULL; +}; + +static void c8sectpfe_delete(struct c8sectpfe *c8sectpfe) +{ + int i; + + if (!c8sectpfe) + return; + + for (i = 0; i < c8sectpfe->num_feeds; i++) + unregister_dvb(&c8sectpfe->demux[i]); + + dvb_unregister_adapter(&c8sectpfe->adapter); + + kfree(c8sectpfe); +}; + +void c8sectpfe_tuner_unregister_frontend(struct c8sectpfe *c8sectpfe, + struct c8sectpfei *fei) +{ + int n; + struct channel_info *tsin; + + for (n = 0; n < fei->tsin_count; n++) { + + tsin = fei->channel_data[n]; + + if (tsin && tsin->frontend) { + dvb_unregister_frontend(tsin->frontend); + dvb_frontend_detach(tsin->frontend); + } + + if (tsin && tsin->i2c_adapter) + i2c_put_adapter(tsin->i2c_adapter); + + if (tsin && tsin->i2c_client) { + if (tsin->i2c_client->dev.driver->owner) + module_put(tsin->i2c_client->dev.driver->owner); + i2c_unregister_device(tsin->i2c_client); + } + } + + c8sectpfe_delete(c8sectpfe); +}; + +int c8sectpfe_tuner_register_frontend(struct c8sectpfe **c8sectpfe, + struct c8sectpfei *fei, + void *start_feed, + void *stop_feed) +{ + struct channel_info *tsin; + struct dvb_frontend *frontend; + int n, res; + + *c8sectpfe = c8sectpfe_create(fei, start_feed, stop_feed); + if (!*c8sectpfe) + return -ENOMEM; + + for (n = 0; n < fei->tsin_count; n++) { + tsin = fei->channel_data[n]; + + res = c8sectpfe_frontend_attach(&frontend, *c8sectpfe, tsin, n); + if (res) + goto err; + + res = dvb_register_frontend(&c8sectpfe[0]->adapter, frontend); + if (res < 0) { + dev_err(fei->dev, "dvb_register_frontend failed (%d)\n", + res); + goto err; + } + + tsin->frontend = frontend; + } + + return 0; + +err: + c8sectpfe_tuner_unregister_frontend(*c8sectpfe, fei); + return res; +} diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-common.h b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-common.h new file mode 100644 index 000000000000..da21c0ac0fc1 --- /dev/null +++ b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-common.h @@ -0,0 +1,64 @@ +/* + * c8sectpfe-common.h - C8SECTPFE STi DVB driver + * + * Copyright (c) STMicroelectronics 2015 + * + * Author: Peter Griffin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ +#ifndef _C8SECTPFE_COMMON_H_ +#define _C8SECTPFE_COMMON_H_ + +#include +#include +#include +#include + +#include "dmxdev.h" +#include "dvb_demux.h" +#include "dvb_frontend.h" +#include "dvb_net.h" + +/* Maximum number of channels */ +#define C8SECTPFE_MAXADAPTER (4) +#define C8SECTPFE_MAXCHANNEL 64 +#define STPTI_MAXCHANNEL 64 + +#define MAX_INPUTBLOCKS 7 + +struct c8sectpfe; +struct stdemux; + +struct stdemux { + struct dvb_demux dvb_demux; + struct dmxdev dmxdev; + struct dmx_frontend hw_frontend; + struct dmx_frontend mem_frontend; + int tsin_index; + int running_feed_count; + struct c8sectpfei *c8sectpfei; +}; + +struct c8sectpfe { + struct stdemux demux[MAX_INPUTBLOCKS]; + struct mutex lock; + struct dvb_adapter adapter; + struct device *device; + int mapping; + int num_feeds; +}; + +/* Channel registration */ +int c8sectpfe_tuner_register_frontend(struct c8sectpfe **c8sectpfe, + struct c8sectpfei *fei, + void *start_feed, + void *stop_feed); + +void c8sectpfe_tuner_unregister_frontend(struct c8sectpfe *c8sectpfe, + struct c8sectpfei *fei); + +#endif -- cgit v1.2.3 From e77a0cbe6c087d3f181d9058879c45f9b19cce90 Mon Sep 17 00:00:00 2001 From: Peter Griffin Date: Thu, 30 Jul 2015 14:08:58 -0300 Subject: [media] c8sectpfe: Add support for various ST NIM cards This patch adds support for the following 2 NIM cards: - 1) B2100A (2x stv0367 demods & 2x NXP tda18212 tuners) 2) STV0903-6110NIM (stv0903 demod + 6110 tuner, lnb24) A NIM card is a cold plugable expansion card which usually features a demodulator / tuner combination. Signed-off-by: Peter Griffin Signed-off-by: Mauro Carvalho Chehab --- .../media/platform/sti/c8sectpfe/c8sectpfe-dvb.c | 244 +++++++++++++++++++++ .../media/platform/sti/c8sectpfe/c8sectpfe-dvb.h | 20 ++ 2 files changed, 264 insertions(+) create mode 100644 drivers/media/platform/sti/c8sectpfe/c8sectpfe-dvb.c create mode 100644 drivers/media/platform/sti/c8sectpfe/c8sectpfe-dvb.h diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-dvb.c b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-dvb.c new file mode 100644 index 000000000000..69d7fe4471c2 --- /dev/null +++ b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-dvb.c @@ -0,0 +1,244 @@ +/* + * c8sectpfe-dvb.c - C8SECTPFE STi DVB driver + * + * Copyright (c) STMicroelectronics 2015 + * + * Author Peter Griffin + * + * 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 +#include +#include +#include +#include + +#include + +#include "c8sectpfe-common.h" +#include "c8sectpfe-core.h" +#include "c8sectpfe-dvb.h" + +#include "dvb-pll.h" +#include "lnbh24.h" +#include "stv0367.h" +#include "stv0367_priv.h" +#include "stv6110x.h" +#include "stv090x.h" +#include "tda18212.h" + +static inline const char *dvb_card_str(unsigned int c) +{ + switch (c) { + case STV0367_TDA18212_NIMA_1: return "STV0367_TDA18212_NIMA_1"; + case STV0367_TDA18212_NIMA_2: return "STV0367_TDA18212_NIMA_2"; + case STV0367_TDA18212_NIMB_1: return "STV0367_TDA18212_NIMB_1"; + case STV0367_TDA18212_NIMB_2: return "STV0367_TDA18212_NIMB_2"; + case STV0903_6110_LNB24_NIMA: return "STV0903_6110_LNB24_NIMA"; + case STV0903_6110_LNB24_NIMB: return "STV0903_6110_LNB24_NIMB"; + default: return "unknown dvb frontend card"; + } +} + +static struct stv090x_config stv090x_config = { + .device = STV0903, + .demod_mode = STV090x_SINGLE, + .clk_mode = STV090x_CLK_EXT, + .xtal = 16000000, + .address = 0x69, + + .ts1_mode = STV090x_TSMODE_SERIAL_CONTINUOUS, + .ts2_mode = STV090x_TSMODE_SERIAL_CONTINUOUS, + + .repeater_level = STV090x_RPTLEVEL_64, + + .tuner_init = NULL, + .tuner_set_mode = NULL, + .tuner_set_frequency = NULL, + .tuner_get_frequency = NULL, + .tuner_set_bandwidth = NULL, + .tuner_get_bandwidth = NULL, + .tuner_set_bbgain = NULL, + .tuner_get_bbgain = NULL, + .tuner_set_refclk = NULL, + .tuner_get_status = NULL, +}; + +static struct stv6110x_config stv6110x_config = { + .addr = 0x60, + .refclk = 16000000, +}; + +#define NIMA 0 +#define NIMB 1 + +static struct stv0367_config stv0367_tda18212_config[] = { + { + .demod_address = 0x1c, + .xtal = 16000000, + .if_khz = 4500, + .if_iq_mode = FE_TER_NORMAL_IF_TUNER, + .ts_mode = STV0367_SERIAL_PUNCT_CLOCK, + .clk_pol = STV0367_CLOCKPOLARITY_DEFAULT, + }, { + .demod_address = 0x1d, + .xtal = 16000000, + .if_khz = 4500, + .if_iq_mode = FE_TER_NORMAL_IF_TUNER, + .ts_mode = STV0367_SERIAL_PUNCT_CLOCK, + .clk_pol = STV0367_CLOCKPOLARITY_DEFAULT, + }, { + .demod_address = 0x1e, + .xtal = 16000000, + .if_khz = 4500, + .if_iq_mode = FE_TER_NORMAL_IF_TUNER, + .ts_mode = STV0367_SERIAL_PUNCT_CLOCK, + .clk_pol = STV0367_CLOCKPOLARITY_DEFAULT, + }, +}; + +static struct tda18212_config tda18212_conf = { + .if_dvbt_6 = 4150, + .if_dvbt_7 = 4150, + .if_dvbt_8 = 4500, + .if_dvbc = 5000, +}; + +int c8sectpfe_frontend_attach(struct dvb_frontend **fe, + struct c8sectpfe *c8sectpfe, + struct channel_info *tsin, int chan_num) +{ + struct tda18212_config *tda18212; + struct stv6110x_devctl *fe2; + struct i2c_client *client; + struct i2c_board_info tda18212_info = { + .type = "tda18212", + .addr = 0x60, + }; + + if (!tsin) + return -EINVAL; + + switch (tsin->dvb_card) { + + case STV0367_TDA18212_NIMA_1: + case STV0367_TDA18212_NIMA_2: + case STV0367_TDA18212_NIMB_1: + case STV0367_TDA18212_NIMB_2: + if (tsin->dvb_card == STV0367_TDA18212_NIMA_1) + *fe = dvb_attach(stv0367ter_attach, + &stv0367_tda18212_config[0], + tsin->i2c_adapter); + else if (tsin->dvb_card == STV0367_TDA18212_NIMB_1) + *fe = dvb_attach(stv0367ter_attach, + &stv0367_tda18212_config[1], + tsin->i2c_adapter); + else + *fe = dvb_attach(stv0367ter_attach, + &stv0367_tda18212_config[2], + tsin->i2c_adapter); + + if (!*fe) { + dev_err(c8sectpfe->device, + "%s: stv0367ter_attach failed for NIM card %s\n" + , __func__, dvb_card_str(tsin->dvb_card)); + return -ENODEV; + }; + + /* + * init the demod so that i2c gate_ctrl + * to the tuner works correctly + */ + (*fe)->ops.init(*fe); + + /* Allocate the tda18212 structure */ + tda18212 = devm_kzalloc(c8sectpfe->device, + sizeof(struct tda18212_config), + GFP_KERNEL); + if (!tda18212) { + dev_err(c8sectpfe->device, + "%s: devm_kzalloc failed\n", __func__); + return -ENOMEM; + } + + memcpy(tda18212, &tda18212_conf, + sizeof(struct tda18212_config)); + + tda18212->fe = (*fe); + + tda18212_info.platform_data = tda18212; + + /* attach tuner */ + request_module("tda18212"); + client = i2c_new_device(tsin->i2c_adapter, &tda18212_info); + if (!client || !client->dev.driver) { + dvb_frontend_detach(*fe); + return -ENODEV; + } + + if (!try_module_get(client->dev.driver->owner)) { + i2c_unregister_device(client); + dvb_frontend_detach(*fe); + return -ENODEV; + } + + tsin->i2c_client = client; + + break; + + case STV0903_6110_LNB24_NIMA: + *fe = dvb_attach(stv090x_attach, &stv090x_config, + tsin->i2c_adapter, STV090x_DEMODULATOR_0); + if (!*fe) { + dev_err(c8sectpfe->device, "%s: stv090x_attach failed\n" + "\tfor NIM card %s\n", + __func__, dvb_card_str(tsin->dvb_card)); + return -ENODEV; + } + + fe2 = dvb_attach(stv6110x_attach, *fe, + &stv6110x_config, tsin->i2c_adapter); + if (!fe2) { + dev_err(c8sectpfe->device, + "%s: stv6110x_attach failed for NIM card %s\n" + , __func__, dvb_card_str(tsin->dvb_card)); + return -ENODEV; + }; + + stv090x_config.tuner_init = fe2->tuner_init; + stv090x_config.tuner_set_mode = fe2->tuner_set_mode; + stv090x_config.tuner_set_frequency = fe2->tuner_set_frequency; + stv090x_config.tuner_get_frequency = fe2->tuner_get_frequency; + stv090x_config.tuner_set_bandwidth = fe2->tuner_set_bandwidth; + stv090x_config.tuner_get_bandwidth = fe2->tuner_get_bandwidth; + stv090x_config.tuner_set_bbgain = fe2->tuner_set_bbgain; + stv090x_config.tuner_get_bbgain = fe2->tuner_get_bbgain; + stv090x_config.tuner_set_refclk = fe2->tuner_set_refclk; + stv090x_config.tuner_get_status = fe2->tuner_get_status; + + dvb_attach(lnbh24_attach, *fe, tsin->i2c_adapter, 0, 0, 0x9); + break; + + default: + dev_err(c8sectpfe->device, + "%s: DVB frontend card %s not yet supported\n", + __func__, dvb_card_str(tsin->dvb_card)); + return -ENODEV; + } + + (*fe)->id = chan_num; + + dev_info(c8sectpfe->device, + "DVB frontend card %s successfully attached", + dvb_card_str(tsin->dvb_card)); + return 0; +} diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-dvb.h b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-dvb.h new file mode 100644 index 000000000000..bd366dbc82b3 --- /dev/null +++ b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-dvb.h @@ -0,0 +1,20 @@ +/* + * c8sectpfe-common.h - C8SECTPFE STi DVB driver + * + * Copyright (c) STMicroelectronics 2015 + * + * Author: Peter Griffin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ +#ifndef _C8SECTPFE_DVB_H_ +#define _C8SECTPFE_DVB_H_ + +int c8sectpfe_frontend_attach(struct dvb_frontend **fe, + struct c8sectpfe *c8sectpfe, struct channel_info *tsin, + int chan_num); + +#endif -- cgit v1.2.3 From 03ad477615c98239cfa41a28c1c38a9a6d6ba291 Mon Sep 17 00:00:00 2001 From: Peter Griffin Date: Thu, 30 Jul 2015 14:08:59 -0300 Subject: [media] c8sectpfe: Add c8sectpfe debugfs support Some basic debugfs support to dump the IP registers. Further statistics could easily be added in the future for example for each enabled tsin channel we could expose number of corrupt packets received etc. Signed-off-by: Peter Griffin Signed-off-by: Mauro Carvalho Chehab --- .../platform/sti/c8sectpfe/c8sectpfe-debugfs.c | 271 +++++++++++++++++++++ .../platform/sti/c8sectpfe/c8sectpfe-debugfs.h | 26 ++ 2 files changed, 297 insertions(+) create mode 100644 drivers/media/platform/sti/c8sectpfe/c8sectpfe-debugfs.c create mode 100644 drivers/media/platform/sti/c8sectpfe/c8sectpfe-debugfs.h diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-debugfs.c b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-debugfs.c new file mode 100644 index 000000000000..e9ba13db49cd --- /dev/null +++ b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-debugfs.c @@ -0,0 +1,271 @@ +/* + * c8sectpfe-debugfs.c - C8SECTPFE STi DVB driver + * + * Copyright (c) STMicroelectronics 2015 + * + * Author: Peter Griffin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "c8sectpfe-debugfs.h" + +#define dump_register(nm ...) \ +{ \ + .name = #nm, \ + .offset = nm, \ +} + +static const struct debugfs_reg32 fei_sys_regs[] = { + dump_register(SYS_INPUT_ERR_STATUS), + dump_register(SYS_OTHER_ERR_STATUS), + dump_register(SYS_INPUT_ERR_MASK), + dump_register(SYS_DMA_ROUTE), + dump_register(SYS_INPUT_CLKEN), + dump_register(IBENABLE_MASK), + dump_register(SYS_OTHER_CLKEN), + dump_register(SYS_CFG_NUM_IB), + dump_register(SYS_CFG_NUM_MIB), + dump_register(SYS_CFG_NUM_SWTS), + dump_register(SYS_CFG_NUM_TSOUT), + dump_register(SYS_CFG_NUM_CCSC), + dump_register(SYS_CFG_NUM_RAM), + dump_register(SYS_CFG_NUM_TP), + + dump_register(C8SECTPFE_IB_IP_FMT_CFG(0)), + dump_register(C8SECTPFE_IB_TAGBYTES_CFG(0)), + dump_register(C8SECTPFE_IB_PID_SET(0)), + dump_register(C8SECTPFE_IB_PKT_LEN(0)), + dump_register(C8SECTPFE_IB_BUFF_STRT(0)), + dump_register(C8SECTPFE_IB_BUFF_END(0)), + dump_register(C8SECTPFE_IB_READ_PNT(0)), + dump_register(C8SECTPFE_IB_WRT_PNT(0)), + dump_register(C8SECTPFE_IB_PRI_THRLD(0)), + dump_register(C8SECTPFE_IB_STAT(0)), + dump_register(C8SECTPFE_IB_MASK(0)), + dump_register(C8SECTPFE_IB_SYS(0)), + + dump_register(C8SECTPFE_IB_IP_FMT_CFG(1)), + dump_register(C8SECTPFE_IB_TAGBYTES_CFG(1)), + dump_register(C8SECTPFE_IB_PID_SET(1)), + dump_register(C8SECTPFE_IB_PKT_LEN(1)), + dump_register(C8SECTPFE_IB_BUFF_STRT(1)), + dump_register(C8SECTPFE_IB_BUFF_END(1)), + dump_register(C8SECTPFE_IB_READ_PNT(1)), + dump_register(C8SECTPFE_IB_WRT_PNT(1)), + dump_register(C8SECTPFE_IB_PRI_THRLD(1)), + dump_register(C8SECTPFE_IB_STAT(1)), + dump_register(C8SECTPFE_IB_MASK(1)), + dump_register(C8SECTPFE_IB_SYS(1)), + + dump_register(C8SECTPFE_IB_IP_FMT_CFG(2)), + dump_register(C8SECTPFE_IB_TAGBYTES_CFG(2)), + dump_register(C8SECTPFE_IB_PID_SET(2)), + dump_register(C8SECTPFE_IB_PKT_LEN(2)), + dump_register(C8SECTPFE_IB_BUFF_STRT(2)), + dump_register(C8SECTPFE_IB_BUFF_END(2)), + dump_register(C8SECTPFE_IB_READ_PNT(2)), + dump_register(C8SECTPFE_IB_WRT_PNT(2)), + dump_register(C8SECTPFE_IB_PRI_THRLD(2)), + dump_register(C8SECTPFE_IB_STAT(2)), + dump_register(C8SECTPFE_IB_MASK(2)), + dump_register(C8SECTPFE_IB_SYS(2)), + + dump_register(C8SECTPFE_IB_IP_FMT_CFG(3)), + dump_register(C8SECTPFE_IB_TAGBYTES_CFG(3)), + dump_register(C8SECTPFE_IB_PID_SET(3)), + dump_register(C8SECTPFE_IB_PKT_LEN(3)), + dump_register(C8SECTPFE_IB_BUFF_STRT(3)), + dump_register(C8SECTPFE_IB_BUFF_END(3)), + dump_register(C8SECTPFE_IB_READ_PNT(3)), + dump_register(C8SECTPFE_IB_WRT_PNT(3)), + dump_register(C8SECTPFE_IB_PRI_THRLD(3)), + dump_register(C8SECTPFE_IB_STAT(3)), + dump_register(C8SECTPFE_IB_MASK(3)), + dump_register(C8SECTPFE_IB_SYS(3)), + + dump_register(C8SECTPFE_IB_IP_FMT_CFG(4)), + dump_register(C8SECTPFE_IB_TAGBYTES_CFG(4)), + dump_register(C8SECTPFE_IB_PID_SET(4)), + dump_register(C8SECTPFE_IB_PKT_LEN(4)), + dump_register(C8SECTPFE_IB_BUFF_STRT(4)), + dump_register(C8SECTPFE_IB_BUFF_END(4)), + dump_register(C8SECTPFE_IB_READ_PNT(4)), + dump_register(C8SECTPFE_IB_WRT_PNT(4)), + dump_register(C8SECTPFE_IB_PRI_THRLD(4)), + dump_register(C8SECTPFE_IB_STAT(4)), + dump_register(C8SECTPFE_IB_MASK(4)), + dump_register(C8SECTPFE_IB_SYS(4)), + + dump_register(C8SECTPFE_IB_IP_FMT_CFG(5)), + dump_register(C8SECTPFE_IB_TAGBYTES_CFG(5)), + dump_register(C8SECTPFE_IB_PID_SET(5)), + dump_register(C8SECTPFE_IB_PKT_LEN(5)), + dump_register(C8SECTPFE_IB_BUFF_STRT(5)), + dump_register(C8SECTPFE_IB_BUFF_END(5)), + dump_register(C8SECTPFE_IB_READ_PNT(5)), + dump_register(C8SECTPFE_IB_WRT_PNT(5)), + dump_register(C8SECTPFE_IB_PRI_THRLD(5)), + dump_register(C8SECTPFE_IB_STAT(5)), + dump_register(C8SECTPFE_IB_MASK(5)), + dump_register(C8SECTPFE_IB_SYS(5)), + + dump_register(C8SECTPFE_IB_IP_FMT_CFG(6)), + dump_register(C8SECTPFE_IB_TAGBYTES_CFG(6)), + dump_register(C8SECTPFE_IB_PID_SET(6)), + dump_register(C8SECTPFE_IB_PKT_LEN(6)), + dump_register(C8SECTPFE_IB_BUFF_STRT(6)), + dump_register(C8SECTPFE_IB_BUFF_END(6)), + dump_register(C8SECTPFE_IB_READ_PNT(6)), + dump_register(C8SECTPFE_IB_WRT_PNT(6)), + dump_register(C8SECTPFE_IB_PRI_THRLD(6)), + dump_register(C8SECTPFE_IB_STAT(6)), + dump_register(C8SECTPFE_IB_MASK(6)), + dump_register(C8SECTPFE_IB_SYS(6)), + + dump_register(DMA_CPU_ID), + dump_register(DMA_CPU_VCR), + dump_register(DMA_CPU_RUN), + dump_register(DMA_CPU_PC), + + dump_register(DMA_PER_TPn_DREQ_MASK), + dump_register(DMA_PER_TPn_DACK_SET), + dump_register(DMA_PER_TPn_DREQ), + dump_register(DMA_PER_TPn_DACK), + dump_register(DMA_PER_DREQ_MODE), + dump_register(DMA_PER_STBUS_SYNC), + dump_register(DMA_PER_STBUS_ACCESS), + dump_register(DMA_PER_STBUS_ADDRESS), + dump_register(DMA_PER_IDLE_INT), + dump_register(DMA_PER_PRIORITY), + dump_register(DMA_PER_MAX_OPCODE), + dump_register(DMA_PER_MAX_CHUNK), + dump_register(DMA_PER_PAGE_SIZE), + dump_register(DMA_PER_MBOX_STATUS), + dump_register(DMA_PER_MBOX_SET), + dump_register(DMA_PER_MBOX_CLEAR), + dump_register(DMA_PER_MBOX_MASK), + dump_register(DMA_PER_INJECT_PKT_SRC), + dump_register(DMA_PER_INJECT_PKT_DEST), + dump_register(DMA_PER_INJECT_PKT_ADDR), + dump_register(DMA_PER_INJECT_PKT), + dump_register(DMA_PER_PAT_PTR_INIT), + dump_register(DMA_PER_PAT_PTR), + dump_register(DMA_PER_SLEEP_MASK), + dump_register(DMA_PER_SLEEP_COUNTER), + + dump_register(DMA_FIRMWARE_VERSION), + dump_register(DMA_PTRREC_BASE), + dump_register(DMA_PTRREC_INPUT_OFFSET), + dump_register(DMA_ERRREC_BASE), + + dump_register(DMA_ERROR_RECORD(0)), + dump_register(DMA_ERROR_RECORD(1)), + dump_register(DMA_ERROR_RECORD(2)), + dump_register(DMA_ERROR_RECORD(3)), + dump_register(DMA_ERROR_RECORD(4)), + dump_register(DMA_ERROR_RECORD(5)), + dump_register(DMA_ERROR_RECORD(6)), + dump_register(DMA_ERROR_RECORD(7)), + dump_register(DMA_ERROR_RECORD(8)), + dump_register(DMA_ERROR_RECORD(9)), + dump_register(DMA_ERROR_RECORD(10)), + dump_register(DMA_ERROR_RECORD(11)), + dump_register(DMA_ERROR_RECORD(12)), + dump_register(DMA_ERROR_RECORD(13)), + dump_register(DMA_ERROR_RECORD(14)), + dump_register(DMA_ERROR_RECORD(15)), + dump_register(DMA_ERROR_RECORD(16)), + dump_register(DMA_ERROR_RECORD(17)), + dump_register(DMA_ERROR_RECORD(18)), + dump_register(DMA_ERROR_RECORD(19)), + dump_register(DMA_ERROR_RECORD(20)), + dump_register(DMA_ERROR_RECORD(21)), + dump_register(DMA_ERROR_RECORD(22)), + + dump_register(DMA_IDLE_REQ), + dump_register(DMA_FIRMWARE_CONFIG), + + dump_register(PIDF_BASE(0)), + dump_register(PIDF_BASE(1)), + dump_register(PIDF_BASE(2)), + dump_register(PIDF_BASE(3)), + dump_register(PIDF_BASE(4)), + dump_register(PIDF_BASE(5)), + dump_register(PIDF_BASE(6)), + dump_register(PIDF_BASE(7)), + dump_register(PIDF_BASE(8)), + dump_register(PIDF_BASE(9)), + dump_register(PIDF_BASE(10)), + dump_register(PIDF_BASE(11)), + dump_register(PIDF_BASE(12)), + dump_register(PIDF_BASE(13)), + dump_register(PIDF_BASE(14)), + dump_register(PIDF_BASE(15)), + dump_register(PIDF_BASE(16)), + dump_register(PIDF_BASE(17)), + dump_register(PIDF_BASE(18)), + dump_register(PIDF_BASE(19)), + dump_register(PIDF_BASE(20)), + dump_register(PIDF_BASE(21)), + dump_register(PIDF_BASE(22)), + dump_register(PIDF_LEAK_ENABLE), + dump_register(PIDF_LEAK_STATUS), + dump_register(PIDF_LEAK_COUNT_RESET), + dump_register(PIDF_LEAK_COUNTER), +}; + +void c8sectpfe_debugfs_init(struct c8sectpfei *fei) +{ + struct dentry *root; + struct dentry *file; + + root = debugfs_create_dir("c8sectpfe", NULL); + if (!root) + goto err; + + fei->root = root; + + fei->regset = devm_kzalloc(fei->dev, sizeof(*fei->regset), GFP_KERNEL); + if (!fei->regset) + goto err; + + fei->regset->regs = fei_sys_regs; + fei->regset->nregs = ARRAY_SIZE(fei_sys_regs); + fei->regset->base = fei->io; + + file = debugfs_create_regset32("registers", S_IRUGO, root, + fei->regset); + if (!file) { + dev_err(fei->dev, + "%s not able to create 'registers' debugfs\n" + , __func__); + goto err; + } + + return; + +err: + debugfs_remove_recursive(root); +} + +void c8sectpfe_debugfs_exit(struct c8sectpfei *fei) +{ + debugfs_remove_recursive(fei->root); + fei->root = NULL; +} diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-debugfs.h b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-debugfs.h new file mode 100644 index 000000000000..8af1ac1378c8 --- /dev/null +++ b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-debugfs.h @@ -0,0 +1,26 @@ +/** + * c8sectpfe-debugfs.h - C8SECTPFE STi DVB driver debugfs header + * + * Copyright (c) STMicroelectronics 2015 + * + * Authors: Peter Griffin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __C8SECTPFE_DEBUG_H +#define __C8SECTPFE_DEBUG_H + +#include "c8sectpfe-core.h" + +void c8sectpfe_debugfs_init(struct c8sectpfei *); +void c8sectpfe_debugfs_exit(struct c8sectpfei *); + +#endif /* __C8SECTPFE_DEBUG_H */ -- cgit v1.2.3 From 850a3f7d5911bbbdf3eedf2db9083546d49ea806 Mon Sep 17 00:00:00 2001 From: Peter Griffin Date: Thu, 30 Jul 2015 14:09:00 -0300 Subject: [media] c8sectpfe: Add Kconfig and Makefile for the driver This patch adds the Kconfig and Makefile for the c8sectpfe driver so it will be built. It also selects additional demodulator and tuners which are required by the supported NIM cards. Signed-off-by: Peter Griffin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/Kconfig | 4 +++- drivers/media/platform/Makefile | 1 + drivers/media/platform/sti/c8sectpfe/Kconfig | 28 +++++++++++++++++++++++++++ drivers/media/platform/sti/c8sectpfe/Makefile | 9 +++++++++ 4 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 drivers/media/platform/sti/c8sectpfe/Kconfig create mode 100644 drivers/media/platform/sti/c8sectpfe/Makefile diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index ea1cb833191f..ce3eaf050ba7 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -1,6 +1,6 @@ # # Platform drivers -# All drivers here are currently for webcam support +# Most drivers here are currently for webcam support menuconfig V4L_PLATFORM_DRIVERS bool "V4L platform devices" @@ -292,3 +292,5 @@ config VIDEO_VIM2M This is a virtual test device for the memory-to-memory driver framework. endif #V4L_TEST_DRIVERS + +source "drivers/media/platform/sti/c8sectpfe/Kconfig" diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index c2d17c3407e9..efa0295af87b 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -35,6 +35,7 @@ obj-$(CONFIG_VIDEO_SAMSUNG_S5P_G2D) += s5p-g2d/ obj-$(CONFIG_VIDEO_SAMSUNG_EXYNOS_GSC) += exynos-gsc/ obj-$(CONFIG_VIDEO_STI_BDISP) += sti/bdisp/ +obj-$(CONFIG_DVB_C8SECTPFE) += sti/c8sectpfe/ obj-$(CONFIG_BLACKFIN) += blackfin/ diff --git a/drivers/media/platform/sti/c8sectpfe/Kconfig b/drivers/media/platform/sti/c8sectpfe/Kconfig new file mode 100644 index 000000000000..d1bfd4c646ae --- /dev/null +++ b/drivers/media/platform/sti/c8sectpfe/Kconfig @@ -0,0 +1,28 @@ +config DVB_C8SECTPFE + tristate "STMicroelectronics C8SECTPFE DVB support" + depends on DVB_CORE && I2C && (ARCH_STI || ARCH_MULTIPLATFORM) + select LIBELF_32 + select FW_LOADER + select FW_LOADER_USER_HELPER_FALLBACK + select DEBUG_FS + select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV090x if MEDIA_SUBDRV_AUTOSELECT + select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV6110 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0900 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0367 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TDA18212 if MEDIA_SUBDRV_AUTOSELECT + + ---help--- + This adds support for DVB front-end cards connected + to TS inputs of STiH407/410 SoC. + + The driver currently supports C8SECTPFE's TS input block, + memdma engine, and HW PID filtering. + + Supported DVB front-end cards are: + - STMicroelectronics DVB-T B2100A (STV0367 + TDA18212) + - STMicroelectronics DVB-S/S2 STV0903 + STV6110 + LNBP24 board + + To compile this driver as a module, choose M here: the + module will be called c8sectpfe. diff --git a/drivers/media/platform/sti/c8sectpfe/Makefile b/drivers/media/platform/sti/c8sectpfe/Makefile new file mode 100644 index 000000000000..b578c7cb4c34 --- /dev/null +++ b/drivers/media/platform/sti/c8sectpfe/Makefile @@ -0,0 +1,9 @@ +c8sectpfe-y += c8sectpfe-core.o c8sectpfe-common.o c8sectpfe-dvb.o \ + c8sectpfe-debugfs.o + +obj-$(CONFIG_DVB_C8SECTPFE) += c8sectpfe.o + +ccflags-y += -Idrivers/media/i2c +ccflags-y += -Idrivers/media/common +ccflags-y += -Idrivers/media/dvb-core/ -Idrivers/media/dvb-frontends/ \ + -Idrivers/media/tuners/ -- cgit v1.2.3 From 95d66b161630c9fd26324262732b3854e497a802 Mon Sep 17 00:00:00 2001 From: Peter Griffin Date: Thu, 30 Jul 2015 14:09:01 -0300 Subject: [media] MAINTAINERS: Add c8sectpfe driver directory to STi section Add the new c8sectpfe demux driver to the STi section of the MAINTAINERS file. Signed-off-by: Peter Griffin Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 00e92ff0d29f..e0946a0ec620 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1505,6 +1505,7 @@ F: arch/arm/boot/dts/sti* F: drivers/clocksource/arm_global_timer.c F: drivers/i2c/busses/i2c-st.c F: drivers/media/rc/st_rc.c +F: drivers/media/platform/sti/c8sectpfe/ F: drivers/mmc/host/sdhci-st.c F: drivers/phy/phy-miphy28lp.c F: drivers/phy/phy-miphy365x.c -- cgit v1.2.3 From 409e9eff727295b93a5dde51988a6f8646e5aa6b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 11 Aug 2015 16:09:21 -0300 Subject: [media] c8sectpfe: Allow compiling it with COMPILE_TEST While it won't work, it is good to allow it to build with COMPILE_TEST, as we can check if other patches would break compilation for this driver. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sti/c8sectpfe/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/sti/c8sectpfe/Kconfig b/drivers/media/platform/sti/c8sectpfe/Kconfig index d1bfd4c646ae..a7227d2ab6cc 100644 --- a/drivers/media/platform/sti/c8sectpfe/Kconfig +++ b/drivers/media/platform/sti/c8sectpfe/Kconfig @@ -1,6 +1,6 @@ config DVB_C8SECTPFE tristate "STMicroelectronics C8SECTPFE DVB support" - depends on DVB_CORE && I2C && (ARCH_STI || ARCH_MULTIPLATFORM) + depends on DVB_CORE && I2C && (ARCH_STI || ARCH_MULTIPLATFORM || COMPILE_TEST) select LIBELF_32 select FW_LOADER select FW_LOADER_USER_HELPER_FALLBACK -- cgit v1.2.3 From 7612cf97ec8533b361529fc864508d1caaf47a87 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 11 Aug 2015 16:13:54 -0300 Subject: [media] c8sectpfe: don't go past channel_data array As reported by smatch: drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c:365 find_channel() error: buffer overflow 'fei->channel_data' 8 <= 63 It seems that a cut-and-paste type of error occurred here: the channel_data array size is C8SECTPFE_MAX_TSIN_CHAN, and not C8SECTPFE_MAXCHANNEL. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c index 3a9109356e67..955d8daf055f 100644 --- a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c +++ b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c @@ -361,7 +361,7 @@ static struct channel_info *find_channel(struct c8sectpfei *fei, int tsin_num) { int i; - for (i = 0; i < C8SECTPFE_MAXCHANNEL; i++) { + for (i = 0; i < C8SECTPFE_MAX_TSIN_CHAN; i++) { if (!fei->channel_data[i]) continue; -- cgit v1.2.3 From ed8d1cf07cb16dacc5414cb46a5a48517c9b98f9 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Thu, 30 Jul 2015 13:18:29 -0300 Subject: [media] Export I2C module alias information in missing drivers The I2C core always reports the MODALIAS uevent as "i2c: Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ir-kbd-i2c.c | 1 + drivers/media/i2c/s5k6a3.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/media/i2c/ir-kbd-i2c.c b/drivers/media/i2c/ir-kbd-i2c.c index 175a76114953..728d2cc8a3e7 100644 --- a/drivers/media/i2c/ir-kbd-i2c.c +++ b/drivers/media/i2c/ir-kbd-i2c.c @@ -478,6 +478,7 @@ static const struct i2c_device_id ir_kbd_id[] = { { "ir_rx_z8f0811_hdpvr", 0 }, { } }; +MODULE_DEVICE_TABLE(i2c, ir_kbd_id); static struct i2c_driver ir_kbd_driver = { .driver = { diff --git a/drivers/media/i2c/s5k6a3.c b/drivers/media/i2c/s5k6a3.c index bc389d5e42ae..b1b1574dfb95 100644 --- a/drivers/media/i2c/s5k6a3.c +++ b/drivers/media/i2c/s5k6a3.c @@ -363,6 +363,7 @@ static int s5k6a3_remove(struct i2c_client *client) static const struct i2c_device_id s5k6a3_ids[] = { { } }; +MODULE_DEVICE_TABLE(i2c, s5k6a3_ids); #ifdef CONFIG_OF static const struct of_device_id s5k6a3_of_match[] = { -- cgit v1.2.3 From efb4b8b60d79f52a72e3698aa14e6ad2ad25db34 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Thu, 30 Jul 2015 13:18:36 -0300 Subject: [media] staging: media: lirc: Export I2C module alias information The I2C core always reports the MODALIAS uevent as "i2c: Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/lirc/lirc_zilog.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/media/lirc/lirc_zilog.c b/drivers/staging/media/lirc/lirc_zilog.c index d032745081ee..ce3b5f230e2e 100644 --- a/drivers/staging/media/lirc/lirc_zilog.c +++ b/drivers/staging/media/lirc/lirc_zilog.c @@ -1364,6 +1364,7 @@ static const struct i2c_device_id ir_transceiver_id[] = { { "ir_rx_z8f0811_hdpvr", ID_FLAG_HDPVR }, { } }; +MODULE_DEVICE_TABLE(i2c, ir_transceiver_id); static struct i2c_driver driver = { .driver = { -- cgit v1.2.3 From 5473387b9382a35967d1fcd550482efa3a31d44f Mon Sep 17 00:00:00 2001 From: Zahari Doychev Date: Mon, 3 Aug 2015 08:57:19 -0300 Subject: [media] coda: drop zero payload bitstream buffers The buffers with zero payload are now dumped in coda_fill_bitstream and not passed to coda_bitstream_queue. This avoids unnecessary fifo addition and buffer sequence counter increment. Signed-off-by: Zahari Doychev Acked-by: Philipp Zabel Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda/coda-bit.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c index 3d434a4ed537..fd7819d8922d 100644 --- a/drivers/media/platform/coda/coda-bit.c +++ b/drivers/media/platform/coda/coda-bit.c @@ -256,6 +256,13 @@ void coda_fill_bitstream(struct coda_ctx *ctx, bool streaming) continue; } + /* Dump empty buffers */ + if (!vb2_get_plane_payload(src_buf, 0)) { + src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); + continue; + } + /* Buffer start position */ start = ctx->bitstream_fifo.kfifo.in & ctx->bitstream_fifo.kfifo.mask; -- cgit v1.2.3 From 7229f8c143dd932719530fb2fe4883f011917554 Mon Sep 17 00:00:00 2001 From: pradheep Date: Mon, 3 Aug 2015 04:56:31 -0300 Subject: [media] staging:media:lirc Remove the extra braces in if statement of lirc_imon This patche removes the extra braces found in drivers/staging/media/lirc/lirc_imon.c to fix the warning thrown by checkpatch.pl Signed-off-by: Pradheep Shrinivasan Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/lirc/lirc_imon.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/media/lirc/lirc_imon.c b/drivers/staging/media/lirc/lirc_imon.c index 62ec9f70dae4..05d47dc8ffb8 100644 --- a/drivers/staging/media/lirc/lirc_imon.c +++ b/drivers/staging/media/lirc/lirc_imon.c @@ -785,13 +785,13 @@ static int imon_probe(struct usb_interface *interface, } driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL); - if (!driver) { + if (!driver) goto free_context; - } + rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL); - if (!rbuf) { + if (!rbuf) goto free_driver; - } + if (lirc_buffer_init(rbuf, BUF_CHUNK_SIZE, BUF_SIZE)) { dev_err(dev, "%s: lirc_buffer_init failed\n", __func__); goto free_rbuf; -- cgit v1.2.3 From 2696f495bdc046d84da6c909a1e7f535138a2a62 Mon Sep 17 00:00:00 2001 From: Shraddha Barke Date: Thu, 6 Aug 2015 06:54:22 -0300 Subject: [media] Staging: media: lirc: use USB API functions rather than constants This patch introduces the use of the function usb_endpoint_type. The Coccinelle semantic patch that makes these changes is as follows: @@ struct usb_endpoint_descriptor *epd; @@ - (epd->bmAttributes & \(USB_ENDPOINT_XFERTYPE_MASK\|3\)) + usb_endpoint_type(epd) Signed-off-by: Shraddha Barke Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/lirc/lirc_imon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/lirc/lirc_imon.c b/drivers/staging/media/lirc/lirc_imon.c index 05d47dc8ffb8..534b8103ae80 100644 --- a/drivers/staging/media/lirc/lirc_imon.c +++ b/drivers/staging/media/lirc/lirc_imon.c @@ -739,7 +739,7 @@ static int imon_probe(struct usb_interface *interface, ep = &iface_desc->endpoint[i].desc; ep_dir = ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK; - ep_type = ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + ep_type = usb_endpoint_type(ep); if (!ir_ep_found && ep_dir == USB_DIR_IN && -- cgit v1.2.3 From 53cc7c9043f0a68a66e53623b114c86051a7250c Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 11 Aug 2015 18:58:31 -0300 Subject: [media] c8sectpfe: fix pinctrl dependencies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit compiling on some archs fail with: drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c:540:8: error: implicit declaration of function ‘pinctrl_select_state’ [-Werror=implicit-function-declaration] ret = pinctrl_select_state(fei->pinctrl, tsin->pstate); That's due the need of including pinctrl.h header and because CONFIG_PINCTRL needs to be true. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sti/c8sectpfe/Kconfig | 3 ++- drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/sti/c8sectpfe/Kconfig b/drivers/media/platform/sti/c8sectpfe/Kconfig index a7227d2ab6cc..1b1110d7374f 100644 --- a/drivers/media/platform/sti/c8sectpfe/Kconfig +++ b/drivers/media/platform/sti/c8sectpfe/Kconfig @@ -1,6 +1,7 @@ config DVB_C8SECTPFE tristate "STMicroelectronics C8SECTPFE DVB support" - depends on DVB_CORE && I2C && (ARCH_STI || ARCH_MULTIPLATFORM || COMPILE_TEST) + depends on PINCTRL && DVB_CORE && I2C + depends on ARCH_STI || ARCH_MULTIPLATFORM || COMPILE_TEST select LIBELF_32 select FW_LOADER select FW_LOADER_USER_HELPER_FALLBACK diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c index 955d8daf055f..1586a1e6836d 100644 --- a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c +++ b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c @@ -33,6 +33,7 @@ #include #include #include +#include #include "c8sectpfe-core.h" #include "c8sectpfe-common.h" -- cgit v1.2.3 From 7d0ddc91c854f1f42fd7165e259b3573f53c1d73 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 11 Aug 2015 19:08:59 -0300 Subject: [media] tda10071: use div_s64() when dividing a s64 integer Otherwise, it will break on 32 bits archs. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/tda10071.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/dvb-frontends/tda10071.c b/drivers/media/dvb-frontends/tda10071.c index ee6653124618..119d47596ac8 100644 --- a/drivers/media/dvb-frontends/tda10071.c +++ b/drivers/media/dvb-frontends/tda10071.c @@ -527,7 +527,7 @@ static int tda10071_read_signal_strength(struct dvb_frontend *fe, u16 *strength) unsigned int uitmp; if (c->strength.stat[0].scale == FE_SCALE_DECIBEL) { - uitmp = c->strength.stat[0].svalue / 1000 + 256; + uitmp = div_s64(c->strength.stat[0].svalue, 1000) + 256; uitmp = clamp(uitmp, 181U, 236U); /* -75dBm - -20dBm */ /* scale value to 0x0000-0xffff */ *strength = (uitmp-181) * 0xffff / (236-181); -- cgit v1.2.3 From ca05189716c2ce02c60303e9c1228a61d1cb9542 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 11 Aug 2015 19:24:05 -0300 Subject: [media] c8sectpfe: use a new Kconfig menu for DVB platform drivers While this is the first DVB platform drivers, let's keep the Kconfig options well organized, adding it on its own DVB menu. Of course, it should depend on MEDIA_DIGITAL_TV_SUPPORT, as this enables all DVB-related menus. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/Kconfig | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index ce3eaf050ba7..3adf686e005d 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -293,4 +293,13 @@ config VIDEO_VIM2M framework. endif #V4L_TEST_DRIVERS +menuconfig DVB_PLATFORM_DRIVERS + bool "DVB platform devices" + depends on MEDIA_DIGITAL_TV_SUPPORT + default n + ---help--- + Say Y here to enable support for platform-specific Digital TV drivers. + +if DVB_PLATFORM_DRIVERS source "drivers/media/platform/sti/c8sectpfe/Kconfig" +endif #DVB_PLATFORM_DRIVERS -- cgit v1.2.3 From 1d88f831d20c10b5633cd71117917cd04a0735a8 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 11 Aug 2015 12:18:30 -0300 Subject: [media] tc358743: don't use variable length array for I2C writes drivers/media/i2c/tc358743.c:148:19: warning: Variable length array is used. As the maximum size is 1026, we can't use dynamic var, as it would otherwise spend 1056 bytes of the stack at i2c_wr() function. So, allocate a buffer with the allowed maximum size together with the state var. Signed-off-by: Mauro Carvalho Chehab Acked-by: Mats Randgaard Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/tc358743.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index 2e926317d7e9..fe42c9a1cb78 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -59,6 +59,9 @@ MODULE_LICENSE("GPL"); #define EDID_NUM_BLOCKS_MAX 8 #define EDID_BLOCK_SIZE 128 +/* Max transfer size done by I2C transfer functions */ +#define MAX_XFER_SIZE (EDID_NUM_BLOCKS_MAX * EDID_BLOCK_SIZE + 2) + static const struct v4l2_dv_timings_cap tc358743_timings_cap = { .type = V4L2_DV_BT_656_1120, /* keep this initialization for compatibility with GCC < 4.4.6 */ @@ -94,6 +97,9 @@ struct tc358743_state { /* edid */ u8 edid_blocks_written; + /* used by i2c_wr() */ + u8 wr_data[MAX_XFER_SIZE]; + struct v4l2_dv_timings timings; u32 mbus_fmt_code; @@ -143,9 +149,13 @@ static void i2c_wr(struct v4l2_subdev *sd, u16 reg, u8 *values, u32 n) { struct tc358743_state *state = to_state(sd); struct i2c_client *client = state->i2c_client; + u8 *data = state->wr_data; int err, i; struct i2c_msg msg; - u8 data[2 + n]; + + if ((2 + n) > sizeof(state->wr_data)) + v4l2_warn(sd, "i2c wr reg=%04x: len=%d is too big!\n", + reg, 2 + n); msg.addr = client->addr; msg.buf = data; -- cgit v1.2.3 From 12976516558c81f20757f6b0947823119ad87046 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 11 Aug 2015 12:18:31 -0300 Subject: [media] ov9650: remove an extra space drivers/media/i2c/ov9650.c:1439 ov965x_detect_sensor() warn: inconsistent indenting Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov9650.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/ov9650.c b/drivers/media/i2c/ov9650.c index 2bc473385c91..e691bba1945b 100644 --- a/drivers/media/i2c/ov9650.c +++ b/drivers/media/i2c/ov9650.c @@ -1436,7 +1436,7 @@ static int ov965x_detect_sensor(struct v4l2_subdev *sd) int ret; mutex_lock(&ov965x->lock); - __ov965x_set_power(ov965x, 1); + __ov965x_set_power(ov965x, 1); usleep_range(25000, 26000); /* Check sensor revision */ -- cgit v1.2.3 From ab9a953b9f58ae695bbbe04a8540830bbae5d246 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 11 Aug 2015 12:18:32 -0300 Subject: [media] ov2659: get rid of unused values Why to store the chosed values for prediv, postdiv and mult if those won't be used? drivers/media/i2c/ov2659.c: In function 'ov2659_pll_calc_params': drivers/media/i2c/ov2659.c:912:35: warning: variable 's_mult' set but not used [-Wunused-but-set-variable] u32 s_prediv = 1, s_postdiv = 1, s_mult = 1; ^ drivers/media/i2c/ov2659.c:912:20: warning: variable 's_postdiv' set but not used [-Wunused-but-set-variable] u32 s_prediv = 1, s_postdiv = 1, s_mult = 1; ^ drivers/media/i2c/ov2659.c:912:6: warning: variable 's_prediv' set but not used [-Wunused-but-set-variable] u32 s_prediv = 1, s_postdiv = 1, s_mult = 1; ^ This is likely some leftover from some past change. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov2659.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/media/i2c/ov2659.c b/drivers/media/i2c/ov2659.c index 6edffc7b74e3..49109f4f5bb4 100644 --- a/drivers/media/i2c/ov2659.c +++ b/drivers/media/i2c/ov2659.c @@ -909,7 +909,6 @@ static void ov2659_pll_calc_params(struct ov2659 *ov2659) u8 ctrl1_reg = 0, ctrl2_reg = 0, ctrl3_reg = 0; struct i2c_client *client = ov2659->client; unsigned int desired = pdata->link_frequency; - u32 s_prediv = 1, s_postdiv = 1, s_mult = 1; u32 prediv, postdiv, mult; u32 bestdelta = -1; u32 delta, actual; @@ -929,9 +928,6 @@ static void ov2659_pll_calc_params(struct ov2659 *ov2659) if ((delta < bestdelta) || (bestdelta == -1)) { bestdelta = delta; - s_mult = mult; - s_prediv = prediv; - s_postdiv = postdiv; ctrl1_reg = ctrl1[i].reg; ctrl2_reg = mult; ctrl3_reg = ctrl3[j].reg; -- cgit v1.2.3 From 27c039750c8ff1297632e424a4674732cc4c3c70 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 11 Aug 2015 12:18:33 -0300 Subject: [media] sr030pc30: don't read a new pointer sr030pc30_get_fmt() can only succeed if both info->curr_win and info->curr_fmt are not NULL. If one of those vars are null, the curent code would call: ret = sr030pc30_set_params(sd); If the curr_win is null, it will return -EINVAL, as it would be expected. However, if curr_fmt is NULL, the function won't set it. The code will then try to read from it: mf->code = info->curr_fmt->code; mf->colorspace = info->curr_fmt->colorspace; with obviouly won't work. This got reported by smatch: drivers/media/i2c/sr030pc30.c:505 sr030pc30_get_fmt() error: we previously assumed 'info->curr_win' could be null (see line 499) drivers/media/i2c/sr030pc30.c:507 sr030pc30_get_fmt() error: we previously assumed 'info->curr_fmt' could be null (see line 499) Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/sr030pc30.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/media/i2c/sr030pc30.c b/drivers/media/i2c/sr030pc30.c index 229dc76c44a5..b04c09dd4bfb 100644 --- a/drivers/media/i2c/sr030pc30.c +++ b/drivers/media/i2c/sr030pc30.c @@ -489,18 +489,14 @@ static int sr030pc30_get_fmt(struct v4l2_subdev *sd, { struct v4l2_mbus_framefmt *mf; struct sr030pc30_info *info = to_sr030pc30(sd); - int ret; if (!format || format->pad) return -EINVAL; mf = &format->format; - if (!info->curr_win || !info->curr_fmt) { - ret = sr030pc30_set_params(sd); - if (ret) - return ret; - } + if (!info->curr_win || !info->curr_fmt) + return -EINVAL; mf->width = info->curr_win->width; mf->height = info->curr_win->height; -- cgit v1.2.3 From 78c66fbcec717b22fd9db4ddc95e543cfcf544af Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 20 May 2015 04:08:30 -0300 Subject: [media] v4l: omap3isp: Drop platform data support Platforms using the OMAP3 ISP have all switched to DT, drop platform data support. Signed-off-by: Laurent Pinchart Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/Kconfig | 2 +- drivers/media/platform/omap3isp/isp.c | 133 ++++------------------- drivers/media/platform/omap3isp/isp.h | 7 +- drivers/media/platform/omap3isp/ispcsiphy.h | 2 +- drivers/media/platform/omap3isp/ispvideo.c | 9 +- drivers/media/platform/omap3isp/omap3isp.h | 132 +++++++++++++++++++++++ include/media/omap3isp.h | 158 ---------------------------- 7 files changed, 158 insertions(+), 285 deletions(-) create mode 100644 drivers/media/platform/omap3isp/omap3isp.h delete mode 100644 include/media/omap3isp.h diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 3adf686e005d..dc75694ac12d 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -86,7 +86,7 @@ config VIDEO_M32R_AR_M64278 config VIDEO_OMAP3 tristate "OMAP 3 Camera support" depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && ARCH_OMAP3 - depends on HAS_DMA + depends on HAS_DMA && OF depends on OMAP_IOMMU select ARM_DMA_USE_IOMMU select VIDEOBUF2_DMA_CONTIG diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c index 12be830d704f..56e683b19a73 100644 --- a/drivers/media/platform/omap3isp/isp.c +++ b/drivers/media/platform/omap3isp/isp.c @@ -101,7 +101,6 @@ static const struct isp_res_mapping isp_res_maps[] = { 0x0000, /* csi2a, len 0x0170 */ 0x0170, /* csiphy2, len 0x000c */ }, - .syscon_offset = 0xdc, .phy_type = ISP_PHY_TYPE_3430, }, { @@ -124,7 +123,6 @@ static const struct isp_res_mapping isp_res_maps[] = { 0x0570, /* csiphy1, len 0x000c */ 0x05c0, /* csi2c, len 0x0040 (2nd area) */ }, - .syscon_offset = 0x2f0, .phy_type = ISP_PHY_TYPE_3630, }, }; @@ -1796,47 +1794,6 @@ static void isp_unregister_entities(struct isp_device *isp) media_device_unregister(&isp->media_dev); } -/* - * isp_register_subdev - Register a sub-device - * @isp: OMAP3 ISP device - * @isp_subdev: platform data related to a sub-device - * - * Register an I2C sub-device which has not been registered by other - * means (such as the Device Tree). - * - * Return a pointer to the sub-device if it has been successfully - * registered, or NULL otherwise. - */ -static struct v4l2_subdev * -isp_register_subdev(struct isp_device *isp, - struct isp_platform_subdev *isp_subdev) -{ - struct i2c_adapter *adapter; - struct v4l2_subdev *sd; - - if (isp_subdev->board_info == NULL) - return NULL; - - adapter = i2c_get_adapter(isp_subdev->i2c_adapter_id); - if (adapter == NULL) { - dev_err(isp->dev, - "%s: Unable to get I2C adapter %d for device %s\n", - __func__, isp_subdev->i2c_adapter_id, - isp_subdev->board_info->type); - return NULL; - } - - sd = v4l2_i2c_new_subdev_board(&isp->v4l2_dev, adapter, - isp_subdev->board_info, NULL); - if (sd == NULL) { - dev_err(isp->dev, "%s: Unable to register subdev %s\n", - __func__, isp_subdev->board_info->type); - return NULL; - } - - return sd; -} - static int isp_link_entity( struct isp_device *isp, struct media_entity *entity, enum isp_interface_type interface) @@ -1910,8 +1867,6 @@ static int isp_link_entity( static int isp_register_entities(struct isp_device *isp) { - struct isp_platform_data *pdata = isp->pdata; - struct isp_platform_subdev *isp_subdev; int ret; isp->media_dev.dev = isp->dev; @@ -1968,37 +1923,6 @@ static int isp_register_entities(struct isp_device *isp) if (ret < 0) goto done; - /* - * Device Tree --- the external sub-devices will be registered - * later. The same goes for the sub-device node registration. - */ - if (isp->dev->of_node) - return 0; - - /* Register external entities */ - for (isp_subdev = pdata ? pdata->subdevs : NULL; - isp_subdev && isp_subdev->board_info; isp_subdev++) { - struct v4l2_subdev *sd; - - sd = isp_register_subdev(isp, isp_subdev); - - /* - * No bus information --- this is either a flash or a - * lens subdev. - */ - if (!sd || !isp_subdev->bus) - continue; - - sd->host_priv = isp_subdev->bus; - - ret = isp_link_entity(isp, &sd->entity, - isp_subdev->bus->interface); - if (ret < 0) - goto done; - } - - ret = v4l2_device_register_subdev_nodes(&isp->v4l2_dev); - done: if (ret < 0) isp_unregister_entities(isp); @@ -2402,33 +2326,24 @@ static int isp_probe(struct platform_device *pdev) return -ENOMEM; } - if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) { - ret = of_property_read_u32(pdev->dev.of_node, "ti,phy-type", - &isp->phy_type); - if (ret) - return ret; + ret = of_property_read_u32(pdev->dev.of_node, "ti,phy-type", + &isp->phy_type); + if (ret) + return ret; - isp->syscon = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, - "syscon"); - if (IS_ERR(isp->syscon)) - return PTR_ERR(isp->syscon); + isp->syscon = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, + "syscon"); + if (IS_ERR(isp->syscon)) + return PTR_ERR(isp->syscon); - ret = of_property_read_u32_index(pdev->dev.of_node, "syscon", 1, - &isp->syscon_offset); - if (ret) - return ret; + ret = of_property_read_u32_index(pdev->dev.of_node, "syscon", 1, + &isp->syscon_offset); + if (ret) + return ret; - ret = isp_of_parse_nodes(&pdev->dev, &isp->notifier); - if (ret < 0) - return ret; - } else { - isp->pdata = pdev->dev.platform_data; - isp->syscon = syscon_regmap_lookup_by_pdevname("syscon.0"); - if (IS_ERR(isp->syscon)) - return PTR_ERR(isp->syscon); - dev_warn(&pdev->dev, - "Platform data support is deprecated! Please move to DT now!\n"); - } + ret = isp_of_parse_nodes(&pdev->dev, &isp->notifier); + if (ret < 0) + return ret; isp->autoidle = autoidle; @@ -2507,11 +2422,6 @@ static int isp_probe(struct platform_device *pdev) goto error_isp; } - if (!IS_ENABLED(CONFIG_OF) || !pdev->dev.of_node) { - isp->syscon_offset = isp_res_maps[m].syscon_offset; - isp->phy_type = isp_res_maps[m].phy_type; - } - for (i = 1; i < OMAP3_ISP_IOMEM_CSI2A_REGS1; i++) isp->mmio_base[i] = isp->mmio_base[0] + isp_res_maps[m].offset[i]; @@ -2555,15 +2465,12 @@ static int isp_probe(struct platform_device *pdev) if (ret < 0) goto error_modules; - if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) { - isp->notifier.bound = isp_subdev_notifier_bound; - isp->notifier.complete = isp_subdev_notifier_complete; + isp->notifier.bound = isp_subdev_notifier_bound; + isp->notifier.complete = isp_subdev_notifier_complete; - ret = v4l2_async_notifier_register(&isp->v4l2_dev, - &isp->notifier); - if (ret) - goto error_register_entities; - } + ret = v4l2_async_notifier_register(&isp->v4l2_dev, &isp->notifier); + if (ret) + goto error_register_entities; isp_core_init(isp, 1); omap3isp_put(isp); diff --git a/drivers/media/platform/omap3isp/isp.h b/drivers/media/platform/omap3isp/isp.h index e579943175c4..5acc2e6511a5 100644 --- a/drivers/media/platform/omap3isp/isp.h +++ b/drivers/media/platform/omap3isp/isp.h @@ -17,7 +17,6 @@ #ifndef OMAP3_ISP_CORE_H #define OMAP3_ISP_CORE_H -#include #include #include #include @@ -27,6 +26,7 @@ #include #include +#include "omap3isp.h" #include "ispstat.h" #include "ispccdc.h" #include "ispreg.h" @@ -101,15 +101,11 @@ struct regmap; * struct isp_res_mapping - Map ISP io resources to ISP revision. * @isp_rev: ISP_REVISION_x_x * @offset: register offsets of various ISP sub-blocks - * @syscon_offset: offset of the syscon register for 343x / 3630 - * (CONTROL_CSIRXFE / CONTROL_CAMERA_PHY_CTRL, respectively) - * from the syscon base address * @phy_type: ISP_PHY_TYPE_{3430,3630} */ struct isp_res_mapping { u32 isp_rev; u32 offset[OMAP3_ISP_IOMEM_LAST]; - u32 syscon_offset; u32 phy_type; }; @@ -184,7 +180,6 @@ struct isp_device { u32 revision; /* platform HW resources */ - struct isp_platform_data *pdata; unsigned int irq_num; void __iomem *mmio_base[OMAP3_ISP_IOMEM_LAST]; diff --git a/drivers/media/platform/omap3isp/ispcsiphy.h b/drivers/media/platform/omap3isp/ispcsiphy.h index e17c88beab92..28b63b28f9f7 100644 --- a/drivers/media/platform/omap3isp/ispcsiphy.h +++ b/drivers/media/platform/omap3isp/ispcsiphy.h @@ -17,7 +17,7 @@ #ifndef OMAP3_ISP_CSI_PHY_H #define OMAP3_ISP_CSI_PHY_H -#include +#include "omap3isp.h" struct isp_csi2_device; struct regulator; diff --git a/drivers/media/platform/omap3isp/ispvideo.c b/drivers/media/platform/omap3isp/ispvideo.c index d285af18df7f..41bb8df91f72 100644 --- a/drivers/media/platform/omap3isp/ispvideo.c +++ b/drivers/media/platform/omap3isp/ispvideo.c @@ -1018,8 +1018,7 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) pipe->entities = 0; - if (video->isp->pdata && video->isp->pdata->set_constraints) - video->isp->pdata->set_constraints(video->isp, true); + /* TODO: Implement PM QoS */ pipe->l3_ick = clk_get_rate(video->isp->clock[ISP_CLK_L3_ICK]); pipe->max_rate = pipe->l3_ick; @@ -1100,8 +1099,7 @@ err_set_stream: err_check_format: media_entity_pipeline_stop(&video->video.entity); err_pipeline_start: - if (video->isp->pdata && video->isp->pdata->set_constraints) - video->isp->pdata->set_constraints(video->isp, false); + /* TODO: Implement PM QoS */ /* The DMA queue must be emptied here, otherwise CCDC interrupts that * will get triggered the next time the CCDC is powered up will try to * access buffers that might have been freed but still present in the @@ -1161,8 +1159,7 @@ isp_video_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) video->queue = NULL; video->error = false; - if (video->isp->pdata && video->isp->pdata->set_constraints) - video->isp->pdata->set_constraints(video->isp, false); + /* TODO: Implement PM QoS */ media_entity_pipeline_stop(&video->video.entity); done: diff --git a/drivers/media/platform/omap3isp/omap3isp.h b/drivers/media/platform/omap3isp/omap3isp.h new file mode 100644 index 000000000000..190e259a6a2d --- /dev/null +++ b/drivers/media/platform/omap3isp/omap3isp.h @@ -0,0 +1,132 @@ +/* + * omap3isp.h + * + * TI OMAP3 ISP - Bus Configuration + * + * Copyright (C) 2011 Nokia Corporation + * + * Contacts: Laurent Pinchart + * Sakari Ailus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __OMAP3ISP_H__ +#define __OMAP3ISP_H__ + +enum isp_interface_type { + ISP_INTERFACE_PARALLEL, + ISP_INTERFACE_CSI2A_PHY2, + ISP_INTERFACE_CCP2B_PHY1, + ISP_INTERFACE_CCP2B_PHY2, + ISP_INTERFACE_CSI2C_PHY1, +}; + +/** + * struct isp_parallel_cfg - Parallel interface configuration + * @data_lane_shift: Data lane shifter + * 0 - CAMEXT[13:0] -> CAM[13:0] + * 1 - CAMEXT[13:2] -> CAM[11:0] + * 2 - CAMEXT[13:4] -> CAM[9:0] + * 3 - CAMEXT[13:6] -> CAM[7:0] + * @clk_pol: Pixel clock polarity + * 0 - Sample on rising edge, 1 - Sample on falling edge + * @hs_pol: Horizontal synchronization polarity + * 0 - Active high, 1 - Active low + * @vs_pol: Vertical synchronization polarity + * 0 - Active high, 1 - Active low + * @fld_pol: Field signal polarity + * 0 - Positive, 1 - Negative + * @data_pol: Data polarity + * 0 - Normal, 1 - One's complement + */ +struct isp_parallel_cfg { + unsigned int data_lane_shift:2; + unsigned int clk_pol:1; + unsigned int hs_pol:1; + unsigned int vs_pol:1; + unsigned int fld_pol:1; + unsigned int data_pol:1; +}; + +enum { + ISP_CCP2_PHY_DATA_CLOCK = 0, + ISP_CCP2_PHY_DATA_STROBE = 1, +}; + +enum { + ISP_CCP2_MODE_MIPI = 0, + ISP_CCP2_MODE_CCP2 = 1, +}; + +/** + * struct isp_csiphy_lane: CCP2/CSI2 lane position and polarity + * @pos: position of the lane + * @pol: polarity of the lane + */ +struct isp_csiphy_lane { + u8 pos; + u8 pol; +}; + +#define ISP_CSIPHY1_NUM_DATA_LANES 1 +#define ISP_CSIPHY2_NUM_DATA_LANES 2 + +/** + * struct isp_csiphy_lanes_cfg - CCP2/CSI2 lane configuration + * @data: Configuration of one or two data lanes + * @clk: Clock lane configuration + */ +struct isp_csiphy_lanes_cfg { + struct isp_csiphy_lane data[ISP_CSIPHY2_NUM_DATA_LANES]; + struct isp_csiphy_lane clk; +}; + +/** + * struct isp_ccp2_cfg - CCP2 interface configuration + * @strobe_clk_pol: Strobe/clock polarity + * 0 - Non Inverted, 1 - Inverted + * @crc: Enable the cyclic redundancy check + * @ccp2_mode: Enable CCP2 compatibility mode + * ISP_CCP2_MODE_MIPI - MIPI-CSI1 mode + * ISP_CCP2_MODE_CCP2 - CCP2 mode + * @phy_layer: Physical layer selection + * ISP_CCP2_PHY_DATA_CLOCK - Data/clock physical layer + * ISP_CCP2_PHY_DATA_STROBE - Data/strobe physical layer + * @vpclk_div: Video port output clock control + */ +struct isp_ccp2_cfg { + unsigned int strobe_clk_pol:1; + unsigned int crc:1; + unsigned int ccp2_mode:1; + unsigned int phy_layer:1; + unsigned int vpclk_div:2; + struct isp_csiphy_lanes_cfg lanecfg; +}; + +/** + * struct isp_csi2_cfg - CSI2 interface configuration + * @crc: Enable the cyclic redundancy check + */ +struct isp_csi2_cfg { + unsigned crc:1; + struct isp_csiphy_lanes_cfg lanecfg; +}; + +struct isp_bus_cfg { + enum isp_interface_type interface; + union { + struct isp_parallel_cfg parallel; + struct isp_ccp2_cfg ccp2; + struct isp_csi2_cfg csi2; + } bus; /* gcc < 4.6.0 chokes on anonymous union initializers */ +}; + +#endif /* __OMAP3ISP_H__ */ diff --git a/include/media/omap3isp.h b/include/media/omap3isp.h deleted file mode 100644 index 048f8f9117ef..000000000000 --- a/include/media/omap3isp.h +++ /dev/null @@ -1,158 +0,0 @@ -/* - * omap3isp.h - * - * TI OMAP3 ISP - Platform data - * - * Copyright (C) 2011 Nokia Corporation - * - * Contacts: Laurent Pinchart - * Sakari Ailus - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - */ - -#ifndef __MEDIA_OMAP3ISP_H__ -#define __MEDIA_OMAP3ISP_H__ - -struct i2c_board_info; -struct isp_device; - -enum isp_interface_type { - ISP_INTERFACE_PARALLEL, - ISP_INTERFACE_CSI2A_PHY2, - ISP_INTERFACE_CCP2B_PHY1, - ISP_INTERFACE_CCP2B_PHY2, - ISP_INTERFACE_CSI2C_PHY1, -}; - -enum { - ISP_LANE_SHIFT_0 = 0, - ISP_LANE_SHIFT_2 = 1, - ISP_LANE_SHIFT_4 = 2, - ISP_LANE_SHIFT_6 = 3, -}; - -/** - * struct isp_parallel_cfg - Parallel interface configuration - * @data_lane_shift: Data lane shifter - * ISP_LANE_SHIFT_0 - CAMEXT[13:0] -> CAM[13:0] - * ISP_LANE_SHIFT_2 - CAMEXT[13:2] -> CAM[11:0] - * ISP_LANE_SHIFT_4 - CAMEXT[13:4] -> CAM[9:0] - * ISP_LANE_SHIFT_6 - CAMEXT[13:6] -> CAM[7:0] - * @clk_pol: Pixel clock polarity - * 0 - Sample on rising edge, 1 - Sample on falling edge - * @hs_pol: Horizontal synchronization polarity - * 0 - Active high, 1 - Active low - * @vs_pol: Vertical synchronization polarity - * 0 - Active high, 1 - Active low - * @fld_pol: Field signal polarity - * 0 - Positive, 1 - Negative - * @data_pol: Data polarity - * 0 - Normal, 1 - One's complement - */ -struct isp_parallel_cfg { - unsigned int data_lane_shift:2; - unsigned int clk_pol:1; - unsigned int hs_pol:1; - unsigned int vs_pol:1; - unsigned int fld_pol:1; - unsigned int data_pol:1; -}; - -enum { - ISP_CCP2_PHY_DATA_CLOCK = 0, - ISP_CCP2_PHY_DATA_STROBE = 1, -}; - -enum { - ISP_CCP2_MODE_MIPI = 0, - ISP_CCP2_MODE_CCP2 = 1, -}; - -/** - * struct isp_csiphy_lane: CCP2/CSI2 lane position and polarity - * @pos: position of the lane - * @pol: polarity of the lane - */ -struct isp_csiphy_lane { - u8 pos; - u8 pol; -}; - -#define ISP_CSIPHY1_NUM_DATA_LANES 1 -#define ISP_CSIPHY2_NUM_DATA_LANES 2 - -/** - * struct isp_csiphy_lanes_cfg - CCP2/CSI2 lane configuration - * @data: Configuration of one or two data lanes - * @clk: Clock lane configuration - */ -struct isp_csiphy_lanes_cfg { - struct isp_csiphy_lane data[ISP_CSIPHY2_NUM_DATA_LANES]; - struct isp_csiphy_lane clk; -}; - -/** - * struct isp_ccp2_cfg - CCP2 interface configuration - * @strobe_clk_pol: Strobe/clock polarity - * 0 - Non Inverted, 1 - Inverted - * @crc: Enable the cyclic redundancy check - * @ccp2_mode: Enable CCP2 compatibility mode - * ISP_CCP2_MODE_MIPI - MIPI-CSI1 mode - * ISP_CCP2_MODE_CCP2 - CCP2 mode - * @phy_layer: Physical layer selection - * ISP_CCP2_PHY_DATA_CLOCK - Data/clock physical layer - * ISP_CCP2_PHY_DATA_STROBE - Data/strobe physical layer - * @vpclk_div: Video port output clock control - */ -struct isp_ccp2_cfg { - unsigned int strobe_clk_pol:1; - unsigned int crc:1; - unsigned int ccp2_mode:1; - unsigned int phy_layer:1; - unsigned int vpclk_div:2; - struct isp_csiphy_lanes_cfg lanecfg; -}; - -/** - * struct isp_csi2_cfg - CSI2 interface configuration - * @crc: Enable the cyclic redundancy check - */ -struct isp_csi2_cfg { - unsigned crc:1; - struct isp_csiphy_lanes_cfg lanecfg; -}; - -struct isp_bus_cfg { - enum isp_interface_type interface; - union { - struct isp_parallel_cfg parallel; - struct isp_ccp2_cfg ccp2; - struct isp_csi2_cfg csi2; - } bus; /* gcc < 4.6.0 chokes on anonymous union initializers */ -}; - -struct isp_platform_subdev { - struct i2c_board_info *board_info; - int i2c_adapter_id; - struct isp_bus_cfg *bus; -}; - -struct isp_platform_data { - struct isp_platform_subdev *subdevs; - void (*set_constraints)(struct isp_device *isp, bool enable); -}; - -#endif /* __MEDIA_OMAP3ISP_H__ */ -- cgit v1.2.3 From 823ea2a639611f79a3b4d3daff5ff722316342e7 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 12 Feb 2015 11:43:11 -0200 Subject: [media] media: Correctly notify about the failed pipeline validation On the place of the source entity name, the sink entity name was printed. Signed-off-by: Sakari Ailus Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/media-entity.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c index 4d8e01c7b1b2..153a46469814 100644 --- a/drivers/media/media-entity.c +++ b/drivers/media/media-entity.c @@ -282,9 +282,9 @@ __must_check int media_entity_pipeline_start(struct media_entity *entity, if (ret < 0 && ret != -ENOIOCTLCMD) { dev_dbg(entity->parent->dev, "link validation failed for \"%s\":%u -> \"%s\":%u, error %d\n", - entity->name, link->source->index, - link->sink->entity->name, - link->sink->index, ret); + link->source->entity->name, + link->source->index, + entity->name, link->sink->index, ret); goto error; } } -- cgit v1.2.3 From ab149b88eb23482cd9c203527bf4e612ee95b50f Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 21 Jul 2015 10:09:10 -0300 Subject: [media] tw68: Move PCI vendor and device IDs to pci_ids.h This commits moves the Intersil/Techwell PCI vendor ID, and the device IDs for the TW68 PCI video capture cards. This will allow to support future Intersil/Techwell devices without duplicating the IDs. Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/tw68/tw68-core.c | 21 +++++++++++---------- drivers/media/pci/tw68/tw68.h | 16 ---------------- include/linux/pci_ids.h | 9 +++++++++ 3 files changed, 20 insertions(+), 26 deletions(-) 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 #include #include +#include #include #include @@ -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/include/linux/pci_ids.h b/include/linux/pci_ids.h index fcff8f865341..d9ba49cedc5d 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2332,6 +2332,15 @@ #define PCI_VENDOR_ID_CAVIUM 0x177d +#define PCI_VENDOR_ID_TECHWELL 0x1797 +#define PCI_DEVICE_ID_TECHWELL_6800 0x6800 +#define PCI_DEVICE_ID_TECHWELL_6801 0x6801 +#define PCI_DEVICE_ID_TECHWELL_6804 0x6804 +#define PCI_DEVICE_ID_TECHWELL_6816_1 0x6810 +#define PCI_DEVICE_ID_TECHWELL_6816_2 0x6811 +#define PCI_DEVICE_ID_TECHWELL_6816_3 0x6812 +#define PCI_DEVICE_ID_TECHWELL_6816_4 0x6813 + #define PCI_VENDOR_ID_BELKIN 0x1799 #define PCI_DEVICE_ID_BELKIN_F5D7010V7 0x701f -- cgit v1.2.3 From 920a1bf30a361fc2c6d713a26deb3a488639def3 Mon Sep 17 00:00:00 2001 From: William Towle Date: Thu, 23 Jul 2015 09:21:36 -0300 Subject: [media] media: soc_camera: rcar_vin: Add BT.709 24-bit RGB888 input support This adds V4L2_MBUS_FMT_RGB888_1X24 input format support which is used by the ADV7612 chip. Modified to use MEDIA_BUS_FMT_* constants Signed-off-by: Koji Matsuoka Signed-off-by: Simon Horman Signed-off-by: Yoshihiro Kaneko Signed-off-by: William Towle Reviewed-by: Rob Taylor Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/rcar_vin.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/soc_camera/rcar_vin.c b/drivers/media/platform/soc_camera/rcar_vin.c index db7700b0af7c..16352a8c2ef1 100644 --- a/drivers/media/platform/soc_camera/rcar_vin.c +++ b/drivers/media/platform/soc_camera/rcar_vin.c @@ -98,6 +98,7 @@ #define VNMC_INF_YUV10_BT656 (2 << 16) #define VNMC_INF_YUV10_BT601 (3 << 16) #define VNMC_INF_YUV16 (5 << 16) +#define VNMC_INF_RGB888 (6 << 16) #define VNMC_VUP (1 << 10) #define VNMC_IM_ODD (0 << 3) #define VNMC_IM_ODD_EVEN (1 << 3) @@ -589,7 +590,7 @@ static int rcar_vin_setup(struct rcar_vin_priv *priv) struct soc_camera_device *icd = priv->ici.icd; struct rcar_vin_cam *cam = icd->host_priv; u32 vnmc, dmr, interrupts; - bool progressive = false, output_is_yuv = false; + bool progressive = false, output_is_yuv = false, input_is_yuv = false; switch (priv->field) { case V4L2_FIELD_TOP: @@ -623,16 +624,22 @@ static int rcar_vin_setup(struct rcar_vin_priv *priv) case MEDIA_BUS_FMT_YUYV8_1X16: /* BT.601/BT.1358 16bit YCbCr422 */ vnmc |= VNMC_INF_YUV16; + input_is_yuv = true; break; case MEDIA_BUS_FMT_YUYV8_2X8: /* BT.656 8bit YCbCr422 or BT.601 8bit YCbCr422 */ vnmc |= priv->pdata_flags & RCAR_VIN_BT656 ? VNMC_INF_YUV8_BT656 : VNMC_INF_YUV8_BT601; + input_is_yuv = true; + break; + case MEDIA_BUS_FMT_RGB888_1X24: + vnmc |= VNMC_INF_RGB888; break; case MEDIA_BUS_FMT_YUYV10_2X10: /* BT.656 10bit YCbCr422 or BT.601 10bit YCbCr422 */ vnmc |= priv->pdata_flags & RCAR_VIN_BT656 ? VNMC_INF_YUV10_BT656 : VNMC_INF_YUV10_BT601; + input_is_yuv = true; break; default: break; @@ -676,7 +683,7 @@ static int rcar_vin_setup(struct rcar_vin_priv *priv) vnmc |= VNMC_VUP; /* If input and output use the same colorspace, use bypass mode */ - if (output_is_yuv) + if (input_is_yuv == output_is_yuv) vnmc |= VNMC_BPS; /* progressive or interlaced mode */ @@ -1423,6 +1430,7 @@ static int rcar_vin_get_formats(struct soc_camera_device *icd, unsigned int idx, case MEDIA_BUS_FMT_YUYV8_1X16: case MEDIA_BUS_FMT_YUYV8_2X8: case MEDIA_BUS_FMT_YUYV10_2X10: + case MEDIA_BUS_FMT_RGB888_1X24: if (cam->extra_fmt) break; -- cgit v1.2.3 From 734f3f238587ffb6938700c4495dd5b5a54c4125 Mon Sep 17 00:00:00 2001 From: Rob Taylor Date: Thu, 23 Jul 2015 09:21:42 -0300 Subject: [media] media: rcar_vin: fill in bus_info field Adapt rcar_vin_querycap() so that cap->bus_info is populated with something meaningful/unique. Signed-off-by: Rob Taylor Signed-off-by: William Towle Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/rcar_vin.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/platform/soc_camera/rcar_vin.c b/drivers/media/platform/soc_camera/rcar_vin.c index 16352a8c2ef1..6abef82b204f 100644 --- a/drivers/media/platform/soc_camera/rcar_vin.c +++ b/drivers/media/platform/soc_camera/rcar_vin.c @@ -1791,6 +1791,7 @@ static int rcar_vin_querycap(struct soc_camera_host *ici, strlcpy(cap->card, "R_Car_VIN", sizeof(cap->card)); cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s%d", DRV_NAME, ici->nr); return 0; } -- cgit v1.2.3 From 4284118058b2b4f217a050908a555d0e1ed58641 Mon Sep 17 00:00:00 2001 From: Rob Taylor Date: Thu, 23 Jul 2015 09:21:43 -0300 Subject: [media] media: rcar_vin: Reject videobufs that are too small for current format In videobuf_setup reject buffers that are too small for the configured format. Fixes v4l2-compliance issue. Signed-off-by: Rob Taylor Reviewed-by: William Towle Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/rcar_vin.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/platform/soc_camera/rcar_vin.c b/drivers/media/platform/soc_camera/rcar_vin.c index 6abef82b204f..71dd71c0bd1f 100644 --- a/drivers/media/platform/soc_camera/rcar_vin.c +++ b/drivers/media/platform/soc_camera/rcar_vin.c @@ -541,6 +541,9 @@ static int rcar_vin_videobuf_setup(struct vb2_queue *vq, unsigned int bytes_per_line; int ret; + if (fmt->fmt.pix.sizeimage < icd->sizeimage) + return -EINVAL; + xlate = soc_camera_xlate_by_fourcc(icd, fmt->fmt.pix.pixelformat); if (!xlate) -- cgit v1.2.3 From 8f7a5f462a255b4395e5f0f08c045dc8af6efd55 Mon Sep 17 00:00:00 2001 From: Mike Looijmans Date: Tue, 11 Aug 2015 09:21:07 -0300 Subject: [media] i2c/adv7511: Fix license, set to GPL v2 Header claims GPL v2, so make the MODULE_LICENSE reflect that properly. Signed-off-by: Mike Looijmans Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv7511.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/adv7511.c b/drivers/media/i2c/adv7511.c index ef198cee8969..e4900df1140b 100644 --- a/drivers/media/i2c/adv7511.c +++ b/drivers/media/i2c/adv7511.c @@ -40,7 +40,7 @@ MODULE_PARM_DESC(debug, "debug level (0-2)"); MODULE_DESCRIPTION("Analog Devices ADV7511 HDMI Transmitter Device Driver"); MODULE_AUTHOR("Hans Verkuil"); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); #define MASK_ADV7511_EDID_RDY_INT 0x04 #define MASK_ADV7511_MSEN_INT 0x40 -- cgit v1.2.3 From e4d45dd81e67f9fe0e1af44bddd53c3d4eb061ea Mon Sep 17 00:00:00 2001 From: Abhilash Jindal Date: Tue, 11 Aug 2015 12:09:49 -0300 Subject: [media] zoran: Use monotonic time Wall time obtained from do_gettimeofday is susceptible to sudden jumps due to user setting the time or due to NTP. Monotonic time is constantly increasing time better suited for comparing two timestamps. Signed-off-by: Abhilash Jindal Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/zoran/zoran_device.c | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) 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 #include #include +#include #include #include @@ -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; -- cgit v1.2.3 From 82fde1a98eb9866347a1d3543fac4ab9a69aeb7d Mon Sep 17 00:00:00 2001 From: Abhilash Jindal Date: Tue, 11 Aug 2015 12:22:57 -0300 Subject: [media] bt8xxx: Use monotonic time Wall time obtained from do_gettimeofday is susceptible to sudden jumps due to user setting the time or due to NTP. Monotonic time is constantly increasing time better suited for comparing two timestamps. Signed-off-by: Abhilash Jindal Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/bt8xx/bttv-input.c | 21 ++++++++------------- drivers/media/pci/bt8xx/bttvp.h | 2 +- 2 files changed, 9 insertions(+), 14 deletions(-) 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 */ }; -- cgit v1.2.3 From 38e6a417f6205e98bef53c5530f5fddfea08e1c6 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 12 Aug 2015 04:14:28 -0300 Subject: [media] horus3a: fix compiler warning Shut up this compiler warning that I get during the daily build: horus3a.c: In function 'horus3a_set_params': horus3a.c:308:24: warning: 'rolloff' may be used uninitialized in this function [-Wmaybe-uninitialized] symbol_rate * (100 + rolloff), 200000) + 5; ^ Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/horus3a.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/dvb-frontends/horus3a.c b/drivers/media/dvb-frontends/horus3a.c index 5074305b289e..000606af70f7 100644 --- a/drivers/media/dvb-frontends/horus3a.c +++ b/drivers/media/dvb-frontends/horus3a.c @@ -285,6 +285,7 @@ static int horus3a_set_params(struct dvb_frontend *fe) rolloff = 20; break; case ROLLOFF_AUTO: + default: dev_err(&priv->i2c->dev, "horus3a: auto roll-off is not supported\n"); return -EINVAL; -- cgit v1.2.3 From 3a6b0605c73d1d695f6d4e49289deaa3fa3e73ee Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 17 Aug 2015 11:51:35 -0300 Subject: Revert "[media] ARM: DT: STi: STiH407: Add c8sectpfe LinuxDVB DT node" There are some missing dependencies at the DT, as it looks for an I2C bus named "ssc2", but this is not defined. Probably, it misses some dependencies from some other tree. For now, revert this patch, to avoid build breakages. This reverts commit 5d8877b6ae0d47897b821b8a11a2e8dee9a22686. Signed-off-by: Mauro Carvalho Chehab --- arch/arm/boot/dts/stihxxx-b2120.dtsi | 38 ------------------------------------ 1 file changed, 38 deletions(-) diff --git a/arch/arm/boot/dts/stihxxx-b2120.dtsi b/arch/arm/boot/dts/stihxxx-b2120.dtsi index b5f31515ebf7..f589fe487f13 100644 --- a/arch/arm/boot/dts/stihxxx-b2120.dtsi +++ b/arch/arm/boot/dts/stihxxx-b2120.dtsi @@ -6,10 +6,6 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ - -#include -#include - / { soc { sbc_serial0: serial@9530000 { @@ -83,39 +79,5 @@ status = "okay"; }; - c8sectpfe@08a20000 { - compatible = "st,stih407-c8sectpfe"; - status = "okay"; - reg = <0x08a20000 0x10000>, <0x08a00000 0x4000>; - reg-names = "c8sectpfe", "c8sectpfe-ram"; - - interrupts = <0 34 0>, <0 35 0>; - interrupt-names = "c8sectpfe-error-irq", - "c8sectpfe-idle-irq"; - - pinctrl-names = "tsin0-serial", "tsin0-parallel", - "tsin3-serial", "tsin4-serial", - "tsin5-serial"; - - pinctrl-0 = <&pinctrl_tsin0_serial>; - pinctrl-1 = <&pinctrl_tsin0_parallel>; - pinctrl-2 = <&pinctrl_tsin3_serial>; - pinctrl-3 = <&pinctrl_tsin4_serial_alt3>; - pinctrl-4 = <&pinctrl_tsin5_serial_alt1>; - - clocks = <&clk_s_c0_flexgen CLK_PROC_STFE>; - clock-names = "c8sectpfe"; - - /* tsin0 is TSA on NIMA */ - tsin0: port@0 { - - tsin-num = <0>; - serial-not-parallel; - i2c-bus = <&ssc2>; - rst-gpio = <&pio15 4 0>; - - dvb-card = ; - }; - }; }; }; -- cgit v1.2.3 From 88c25dcb1185f2c7041550976430e27fa9d8dcca Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 21 Aug 2015 13:37:54 -0300 Subject: [media] DocBook: fix an unbalanced tag The got lost on some change at the DVB docbook, making the tag unbalanced and causing a warning. Fix it. Signed-off-by: Mauro Carvalho Chehab Acked-by: Jonathan Corbet --- Documentation/DocBook/media/dvb/intro.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/DocBook/media/dvb/intro.xml b/Documentation/DocBook/media/dvb/intro.xml index d30751338493..51db15648099 100644 --- a/Documentation/DocBook/media/dvb/intro.xml +++ b/Documentation/DocBook/media/dvb/intro.xml @@ -164,7 +164,7 @@ are called: from 0, and M enumerates the devices of each type within each adapter, starting from 0, too. We will omit the “ /dev/dvb/adapterN/” in the further discussion -of these devices. +of these devices. More details about the data structures and function calls of all the devices are described in the following chapters. -- cgit v1.2.3 From dc2c8bd3c9a44ed38d9af6c7243fdddc42ec391a Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 21 Aug 2015 14:17:13 -0300 Subject: [media] DocBook/device-drivers: Add drivers/media core stuff There are lots of docbook marks at the media subsystem, but those aren't used. Add the core headers/code in order to start generating docs. Signed-off-by: Mauro Carvalho Chehab Acked-by: Jonathan Corbet --- Documentation/DocBook/device-drivers.tmpl | 30 ++++++++++++++++++++++++++++++ drivers/media/dvb-core/dvb_math.h | 10 ++++++---- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/Documentation/DocBook/device-drivers.tmpl b/Documentation/DocBook/device-drivers.tmpl index faf09d4a0ea8..e3e0f4880770 100644 --- a/Documentation/DocBook/device-drivers.tmpl +++ b/Documentation/DocBook/device-drivers.tmpl @@ -216,6 +216,36 @@ X!Isound/sound_firmware.c --> + + Media Devices +!Iinclude/media/media-device.h +!Iinclude/media/media-devnode.h +!Iinclude/media/media-entity.h +!Iinclude/media/v4l2-async.h +!Iinclude/media/v4l2-flash-led-class.h +!Iinclude/media/v4l2-mem2mem.h +!Iinclude/media/v4l2-of.h +!Iinclude/media/v4l2-subdev.h +!Iinclude/media/rc-core.h + + + + 16x50 UART Driver !Edrivers/tty/serial/serial_core.c diff --git a/drivers/media/dvb-core/dvb_math.h b/drivers/media/dvb-core/dvb_math.h index aecc867e9404..f586aa001ede 100644 --- a/drivers/media/dvb-core/dvb_math.h +++ b/drivers/media/dvb-core/dvb_math.h @@ -30,9 +30,10 @@ * to use rational values you can use the following method: * intlog2(value) = intlog2(value * 2^x) - x * 2^24 * - * example: intlog2(8) will give 3 << 24 = 3 * 2^24 - * example: intlog2(9) will give 3 << 24 + ... = 3.16... * 2^24 - * example: intlog2(1.5) = intlog2(3) - 2^24 = 0.584... * 2^24 + * Some usecase examples: + * intlog2(8) will give 3 << 24 = 3 * 2^24 + * intlog2(9) will give 3 << 24 + ... = 3.16... * 2^24 + * intlog2(1.5) = intlog2(3) - 2^24 = 0.584... * 2^24 * * @param value The value (must be != 0) * @return log2(value) * 2^24 @@ -45,7 +46,8 @@ extern unsigned int intlog2(u32 value); * to use rational values you can use the following method: * intlog10(value) = intlog10(value * 10^x) - x * 2^24 * - * example: intlog10(1000) will give 3 << 24 = 3 * 2^24 + * An usecase example: + * intlog10(1000) will give 3 << 24 = 3 * 2^24 * due to the implementation intlog10(1000) might be not exactly 3 * 2^24 * * look at intlog2 for similar examples -- cgit v1.2.3 From ec0255cad2dace5be54cc52010bb07fdb5628e1e Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 22 Aug 2015 04:45:03 -0300 Subject: [media] Docbook: Fix description of struct media_devnode Warning(.//include/media/media-devnode.h:80): No description found for parameter 'fops' Warning(.//include/media/media-devnode.h:80): No description found for parameter 'dev' Warning(.//include/media/media-devnode.h:80): No description found for parameter 'cdev' Warning(.//include/media/media-devnode.h:80): No description found for parameter 'release' Signed-off-by: Mauro Carvalho Chehab Acked-by: Jonathan Corbet --- include/media/media-devnode.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/media/media-devnode.h b/include/media/media-devnode.h index 0dc7060f9625..17ddae32060d 100644 --- a/include/media/media-devnode.h +++ b/include/media/media-devnode.h @@ -53,9 +53,13 @@ struct media_file_operations { /** * struct media_devnode - Media device node + * @fops: pointer to struct media_file_operations with media device ops + * @dev: struct device pointer for the media controller device + * @cdev: struct cdev pointer character device * @parent: parent device * @minor: device node minor number * @flags: flags, combination of the MEDIA_FLAG_* constants + * @release: release callback called at the end of media_devnode_release() * * This structure represents a media-related device node. * -- cgit v1.2.3 From 5240f4e68d4209588194ea1db60f7c822e2a1c6f Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 22 Aug 2015 05:02:21 -0300 Subject: [media] DocBook/media/Makefile: Avoid make htmldocs to fail If make is called twice like that: make V=1 DOCBOOKS=device-drivers.xml htmldocs Make will fail with: make -f ./scripts/Makefile.build obj=scripts/basic rm -f .tmp_quiet_recordmcount make -f ./scripts/Makefile.build obj=scripts build_docproc make -f ./scripts/Makefile.build obj=Documentation/DocBook htmldocs rm -rf Documentation/DocBook/index.html; echo '

Linux Kernel HTML Documentation

' >> Documentation/DocBook/index.html && echo '

Kernel Version: 4.2.0-rc2

' >> Documentation/DocBook/index.html && cat Documentation/DocBook/device-drivers.html >> Documentation/DocBook/index.html cp ./Documentation/DocBook//bayer.png ./Documentation/DocBook//constraints.png ./Documentation/DocBook//crop.gif ./Documentation/DocBook//dvbstb.png ./Documentation/DocBook//fieldseq_bt.gif ./Documentation/DocBook//fieldseq_tb.gif ./Documentation/DocBook//nv12mt.gif ./Documentation/DocBook//nv12mt_example.gif ./Documentation/DocBook//pipeline.png ./Documentation/DocBook//selection.png ./Documentation/DocBook//vbi_525.gif ./Documentation/DocBook//vbi_625.gif ./Documentation/DocBook//vbi_hsync.gif ./Documentation/DocBook/media/*.svg ./Documentation/DocBook/media/v4l/*.svg ./Documentation/DocBook//media_api cp: target './Documentation/DocBook//media_api' is not a directory Documentation/DocBook/Makefile:53: recipe for target 'htmldocs' failed Signed-off-by: Mauro Carvalho Chehab Acked-by: Jonathan Corbet --- Documentation/DocBook/media/Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/DocBook/media/Makefile b/Documentation/DocBook/media/Makefile index 23996f88cd58..08527e7ea4d0 100644 --- a/Documentation/DocBook/media/Makefile +++ b/Documentation/DocBook/media/Makefile @@ -199,7 +199,8 @@ DVB_DOCUMENTED = \ # install_media_images = \ - $(Q)-cp $(OBJIMGFILES) $(MEDIA_SRC_DIR)/*.svg $(MEDIA_SRC_DIR)/v4l/*.svg $(MEDIA_OBJ_DIR)/media_api + $(Q)-mkdir $(MEDIA_OBJ_DIR)/media_api; \ + cp $(OBJIMGFILES) $(MEDIA_SRC_DIR)/*.svg $(MEDIA_SRC_DIR)/v4l/*.svg $(MEDIA_OBJ_DIR)/media_api $(MEDIA_OBJ_DIR)/%: $(MEDIA_SRC_DIR)/%.b64 $(Q)base64 -d $< >$@ -- cgit v1.2.3 From f8b27377076e4405a25bdb40ee32ef7a0b261573 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 22 Aug 2015 05:16:24 -0300 Subject: [media] Docbook: Fix comments at v4l2-async.h Warning(.//include/media/v4l2-async.h:62): No description found for parameter 'match_type' Warning(.//include/media/v4l2-async.h:62): Excess struct/union/enum/typedef member 'bus_type' description in 'v4l2_async_subdev' Warning(.//include/media/v4l2-async.h:76): cannot understand function prototype: 'struct v4l2_async_notifier ' Signed-off-by: Mauro Carvalho Chehab Acked-by: Jonathan Corbet --- include/media/v4l2-async.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h index 768356917bea..1d6d7da4c45d 100644 --- a/include/media/v4l2-async.h +++ b/include/media/v4l2-async.h @@ -32,7 +32,8 @@ enum v4l2_async_match_type { /** * struct v4l2_async_subdev - sub-device descriptor, as known to a bridge - * @bus_type: subdevice bus type to select the appropriate matching method + * + * @match_type: type of match that will be used * @match: union of per-bus type matching data sets * @list: used to link struct v4l2_async_subdev objects, waiting to be * probed, to a notifier->waiting list @@ -62,8 +63,9 @@ struct v4l2_async_subdev { }; /** - * v4l2_async_notifier - v4l2_device notifier data - * @num_subdevs:number of subdevices + * struct v4l2_async_notifier - v4l2_device notifier data + * + * @num_subdevs: number of subdevices * @subdevs: array of pointers to subdevice descriptors * @v4l2_dev: pointer to struct v4l2_device * @waiting: list of struct v4l2_async_subdev, waiting for their drivers -- cgit v1.2.3 From fa810845efc4d3c0f159a3e7a35f009e0249c857 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 22 Aug 2015 05:25:45 -0300 Subject: [media] Docbook: Fix s_rx_carrier_range parameter description Warning(.//include/media/rc-core.h:178): No description found for parameter 's_rx_carrier_range' Warning(.//include/media/rc-core.h:178): Excess struct/union/enum/typedef member 's_rx_carrier' description in 'rc_dev' Signed-off-by: Mauro Carvalho Chehab Acked-by: Jonathan Corbet --- include/media/rc-core.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/media/rc-core.h b/include/media/rc-core.h index 5642fbea886e..63eb5217edd5 100644 --- a/include/media/rc-core.h +++ b/include/media/rc-core.h @@ -112,7 +112,7 @@ enum rc_filter_type { * @s_tx_mask: set transmitter mask (for devices with multiple tx outputs) * @s_tx_carrier: set transmit carrier frequency * @s_tx_duty_cycle: set transmit duty cycle (0% - 100%) - * @s_rx_carrier: inform driver about carrier it is expected to handle + * @s_rx_carrier_range: inform driver about carrier it is expected to handle * @tx_ir: transmit IR * @s_idle: enable/disable hardware idle mode, upon which, * device doesn't interrupt host until it sees IR pulses -- cgit v1.2.3 From 62ba6b22f4bb99dbf53c5a0ac43b24c00c0fc86a Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 22 Aug 2015 05:28:44 -0300 Subject: [media] Docbook: fix comments at v4l2-flash-led-class.h Warning(.//include/media/v4l2-flash-led-class.h:51): bad line: unique in the system Warning(.//include/media/v4l2-flash-led-class.h:56): bad line: definitions are available in the header file Warning(.//include/media/v4l2-flash-led-class.h:57): bad line: Warning(.//include/media/v4l2-flash-led-class.h:122): No description found for parameter 'ops' Warning(.//include/media/v4l2-flash-led-class.h:122): Excess function parameter 'flash_ops' description in 'v4l2_flash_init' Warning(.//include/media/v4l2-flash-led-class.h:130): No description found for parameter 'v4l2_flash' Warning(.//include/media/v4l2-flash-led-class.h:130): Excess function parameter 'flash' description in 'v4l2_flash_release' Signed-off-by: Mauro Carvalho Chehab Acked-by: Jonathan Corbet --- include/media/v4l2-flash-led-class.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/media/v4l2-flash-led-class.h b/include/media/v4l2-flash-led-class.h index 098236c083b8..3d184ab52274 100644 --- a/include/media/v4l2-flash-led-class.h +++ b/include/media/v4l2-flash-led-class.h @@ -48,13 +48,13 @@ struct v4l2_flash_ops { /** * struct v4l2_flash_config - V4L2 Flash sub-device initialization data * @dev_name: the name of the media entity, - unique in the system + * unique in the system * @torch_intensity: constraints for the LED in torch mode * @indicator_intensity: constraints for the indicator LED * @flash_faults: bitmask of flash faults that the LED flash class - device can report; corresponding LED_FAULT* bit - definitions are available in the header file - + * device can report; corresponding LED_FAULT* bit + * definitions are available in the header file + * * @has_external_strobe: external strobe capability */ struct v4l2_flash_config { @@ -105,7 +105,7 @@ static inline struct v4l2_flash *v4l2_ctrl_to_v4l2_flash(struct v4l2_ctrl *c) * @fled_cdev: LED flash class device to wrap * @iled_cdev: LED flash class device representing indicator LED associated * with fled_cdev, may be NULL - * @flash_ops: V4L2 Flash device ops + * @ops: V4L2 Flash device ops * @config: initialization data for V4L2 Flash sub-device * * Create V4L2 Flash sub-device wrapping given LED subsystem device. @@ -123,7 +123,7 @@ struct v4l2_flash *v4l2_flash_init( /** * v4l2_flash_release - release V4L2 Flash sub-device - * @flash: the V4L2 Flash sub-device to release + * @v4l2_flash: the V4L2 Flash sub-device to release * * Release V4L2 Flash sub-device. */ -- cgit v1.2.3 From 62c0d016dab0ffd3cd28f10236186ef341ddece8 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 22 Aug 2015 05:34:40 -0300 Subject: [media] Docbook: Fix comments at v4l2-mem2mem.h Warning(.//include/media/v4l2-mem2mem.h:50): No description found for parameter 'lock' Warning(.//include/media/v4l2-mem2mem.h:50): No description found for parameter 'unlock' Warning(.//include/media/v4l2-mem2mem.h:167): No description found for parameter 'm2m_ctx' Warning(.//include/media/v4l2-mem2mem.h:177): No description found for parameter 'm2m_ctx' Warning(.//include/media/v4l2-mem2mem.h:188): No description found for parameter 'm2m_ctx' Warning(.//include/media/v4l2-mem2mem.h:197): No description found for parameter 'm2m_ctx' Warning(.//include/media/v4l2-mem2mem.h:206): No description found for parameter 'm2m_ctx' Warning(.//include/media/v4l2-mem2mem.h:215): No description found for parameter 'm2m_ctx' Warning(.//include/media/v4l2-mem2mem.h:226): No description found for parameter 'm2m_ctx' Warning(.//include/media/v4l2-mem2mem.h:235): No description found for parameter 'm2m_ctx' Signed-off-by: Mauro Carvalho Chehab Acked-by: Jonathan Corbet --- include/media/v4l2-mem2mem.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/include/media/v4l2-mem2mem.h b/include/media/v4l2-mem2mem.h index 3bbd96da25c9..8849aaba6aa5 100644 --- a/include/media/v4l2-mem2mem.h +++ b/include/media/v4l2-mem2mem.h @@ -40,6 +40,10 @@ * v4l2_m2m_job_finish() (as if the transaction ended normally). * This function does not have to (and will usually not) wait * until the device enters a state when it can be stopped. + * @lock: optional. Define a driver's own lock callback, instead of using + * m2m_ctx->q_lock. + * @unlock: optional. Define a driver's own unlock callback, instead of + * using m2m_ctx->q_lock. */ struct v4l2_m2m_ops { void (*device_run)(void *priv); @@ -161,6 +165,8 @@ void v4l2_m2m_buf_queue(struct v4l2_m2m_ctx *m2m_ctx, struct vb2_buffer *vb); /** * v4l2_m2m_num_src_bufs_ready() - return the number of source buffers ready for * use + * + * @m2m_ctx: pointer to struct v4l2_m2m_ctx */ static inline unsigned int v4l2_m2m_num_src_bufs_ready(struct v4l2_m2m_ctx *m2m_ctx) @@ -171,6 +177,8 @@ unsigned int v4l2_m2m_num_src_bufs_ready(struct v4l2_m2m_ctx *m2m_ctx) /** * v4l2_m2m_num_src_bufs_ready() - return the number of destination buffers * ready for use + * + * @m2m_ctx: pointer to struct v4l2_m2m_ctx */ static inline unsigned int v4l2_m2m_num_dst_bufs_ready(struct v4l2_m2m_ctx *m2m_ctx) @@ -183,6 +191,8 @@ void *v4l2_m2m_next_buf(struct v4l2_m2m_queue_ctx *q_ctx); /** * v4l2_m2m_next_src_buf() - return next source buffer from the list of ready * buffers + * + * @m2m_ctx: pointer to struct v4l2_m2m_ctx */ static inline void *v4l2_m2m_next_src_buf(struct v4l2_m2m_ctx *m2m_ctx) { @@ -192,6 +202,8 @@ static inline void *v4l2_m2m_next_src_buf(struct v4l2_m2m_ctx *m2m_ctx) /** * v4l2_m2m_next_dst_buf() - return next destination buffer from the list of * ready buffers + * + * @m2m_ctx: pointer to struct v4l2_m2m_ctx */ static inline void *v4l2_m2m_next_dst_buf(struct v4l2_m2m_ctx *m2m_ctx) { @@ -200,6 +212,8 @@ static inline void *v4l2_m2m_next_dst_buf(struct v4l2_m2m_ctx *m2m_ctx) /** * v4l2_m2m_get_src_vq() - return vb2_queue for source buffers + * + * @m2m_ctx: pointer to struct v4l2_m2m_ctx */ static inline struct vb2_queue *v4l2_m2m_get_src_vq(struct v4l2_m2m_ctx *m2m_ctx) @@ -209,6 +223,8 @@ struct vb2_queue *v4l2_m2m_get_src_vq(struct v4l2_m2m_ctx *m2m_ctx) /** * v4l2_m2m_get_dst_vq() - return vb2_queue for destination buffers + * + * @m2m_ctx: pointer to struct v4l2_m2m_ctx */ static inline struct vb2_queue *v4l2_m2m_get_dst_vq(struct v4l2_m2m_ctx *m2m_ctx) @@ -221,6 +237,8 @@ void *v4l2_m2m_buf_remove(struct v4l2_m2m_queue_ctx *q_ctx); /** * v4l2_m2m_src_buf_remove() - take off a source buffer from the list of ready * buffers and return it + * + * @m2m_ctx: pointer to struct v4l2_m2m_ctx */ static inline void *v4l2_m2m_src_buf_remove(struct v4l2_m2m_ctx *m2m_ctx) { @@ -230,6 +248,8 @@ static inline void *v4l2_m2m_src_buf_remove(struct v4l2_m2m_ctx *m2m_ctx) /** * v4l2_m2m_dst_buf_remove() - take off a destination buffer from the list of * ready buffers and return it + * + * @m2m_ctx: pointer to struct v4l2_m2m_ctx */ static inline void *v4l2_m2m_dst_buf_remove(struct v4l2_m2m_ctx *m2m_ctx) { -- cgit v1.2.3 From 5c6629847cf6641984b1808707b43772db4e4403 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 22 Aug 2015 05:40:29 -0300 Subject: [media] v4l2-subdev: convert documentation to the right format struct v4l2_subdev_core_ops has some kernel-doc-nano documentation using a wrong format, with affects the number of stuff reported at the DocBook device-drivers.xml. Properly mark the such comment blocks using the right notation. Signed-off-by: Mauro Carvalho Chehab Acked-by: Jonathan Corbet --- include/media/v4l2-subdev.h | 261 +++++++++++++++++++++++--------------------- 1 file changed, 135 insertions(+), 126 deletions(-) diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 370fc38c34f1..7b15eccaceb3 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -118,34 +118,36 @@ struct v4l2_subdev_io_pin_config { u8 strength; /* Pin drive strength */ }; -/* - s_io_pin_config: configure one or more chip I/O pins for chips that - multiplex different internal signal pads out to IO pins. This function - takes a pointer to an array of 'n' pin configuration entries, one for - each pin being configured. This function could be called at times - other than just subdevice initialization. - - init: initialize the sensor registers to some sort of reasonable default - values. Do not use for new drivers and should be removed in existing - drivers. - - load_fw: load firmware. - - reset: generic reset command. The argument selects which subsystems to - reset. Passing 0 will always reset the whole chip. Do not use for new - drivers without discussing this first on the linux-media mailinglist. - There should be no reason normally to reset a device. - - s_gpio: set GPIO pins. Very simple right now, might need to be extended with - a direction argument if needed. - - s_power: puts subdevice in power saving mode (on == 0) or normal operation - mode (on == 1). - - interrupt_service_routine: Called by the bridge chip's interrupt service - handler, when an interrupt status has be raised due to this subdev, - so that this subdev can handle the details. It may schedule work to be - performed later. It must not sleep. *Called from an IRQ context*. +/** + * struct v4l2_subdev_core_ops - Define ops callbacks for subdevs + * + * @s_io_pin_config: configure one or more chip I/O pins for chips that + * multiplex different internal signal pads out to IO pins. This function + * takes a pointer to an array of 'n' pin configuration entries, one for + * each pin being configured. This function could be called at times + * other than just subdevice initialization. + * + * @init: initialize the sensor registers to some sort of reasonable default + * values. Do not use for new drivers and should be removed in existing + * drivers. + * + * @load_fw: load firmware. + * + * @reset: generic reset command. The argument selects which subsystems to + * reset. Passing 0 will always reset the whole chip. Do not use for new + * drivers without discussing this first on the linux-media mailinglist. + * There should be no reason normally to reset a device. + * + * @s_gpio: set GPIO pins. Very simple right now, might need to be extended with + * a direction argument if needed. + * + * @s_power: puts subdevice in power saving mode (on == 0) or normal operation + * mode (on == 1). + * + * @interrupt_service_routine: Called by the bridge chip's interrupt service + * handler, when an interrupt status has be raised due to this subdev, + * so that this subdev can handle the details. It may schedule work to be + * performed later. It must not sleep. *Called from an IRQ context*. */ struct v4l2_subdev_core_ops { int (*log_status)(struct v4l2_subdev *sd); @@ -180,18 +182,17 @@ struct v4l2_subdev_core_ops { struct v4l2_event_subscription *sub); }; -/* s_radio: v4l device was opened in radio mode. - - g_frequency: freq->type must be filled in. Normally done by video_ioctl2 - or the bridge driver. - - g_tuner: - s_tuner: vt->type must be filled in. Normally done by video_ioctl2 or the - bridge driver. - - s_type_addr: sets tuner type and its I2C addr. - - s_config: sets tda9887 specific stuff, like port1, port2 and qss +/** + * struct s_radio - Callbacks used when v4l device was opened in radio mode. + * + * @g_frequency: freq->type must be filled in. Normally done by video_ioctl2 + * or the bridge driver. + * @g_tuner: + * @s_tuner: vt->type must be filled in. Normally done by video_ioctl2 or the + * bridge driver. + * + * @s_type_addr: sets tuner type and its I2C addr. + * @s_config: sets tda9887 specific stuff, like port1, port2 and qss */ struct v4l2_subdev_tuner_ops { int (*s_radio)(struct v4l2_subdev *sd); @@ -206,25 +207,28 @@ struct v4l2_subdev_tuner_ops { int (*s_config)(struct v4l2_subdev *sd, const struct v4l2_priv_tun_config *config); }; -/* s_clock_freq: set the frequency (in Hz) of the audio clock output. - Used to slave an audio processor to the video decoder, ensuring that - audio and video remain synchronized. Usual values for the frequency - are 48000, 44100 or 32000 Hz. If the frequency is not supported, then - -EINVAL is returned. - - s_i2s_clock_freq: sets I2S speed in bps. This is used to provide a standard - way to select I2S clock used by driving digital audio streams at some - board designs. Usual values for the frequency are 1024000 and 2048000. - If the frequency is not supported, then -EINVAL is returned. - - s_routing: used to define the input and/or output pins of an audio chip, - and any additional configuration data. - Never attempt to use user-level input IDs (e.g. Composite, S-Video, - Tuner) at this level. An i2c device shouldn't know about whether an - input pin is connected to a Composite connector, become on another - board or platform it might be connected to something else entirely. - The calling driver is responsible for mapping a user-level input to - the right pins on the i2c device. +/** + * struct v4l2_subdev_audio_ops - Callbacks used for audio-related settings + * + * @s_clock_freq: set the frequency (in Hz) of the audio clock output. + * Used to slave an audio processor to the video decoder, ensuring that + * audio and video remain synchronized. Usual values for the frequency + * are 48000, 44100 or 32000 Hz. If the frequency is not supported, then + * -EINVAL is returned. + * + * @s_i2s_clock_freq: sets I2S speed in bps. This is used to provide a standard + * way to select I2S clock used by driving digital audio streams at some + * board designs. Usual values for the frequency are 1024000 and 2048000. + * If the frequency is not supported, then -EINVAL is returned. + * + * @s_routing: used to define the input and/or output pins of an audio chip, + * and any additional configuration data. + * Never attempt to use user-level input IDs (e.g. Composite, S-Video, + * Tuner) at this level. An i2c device shouldn't know about whether an + * input pin is connected to a Composite connector, become on another + * board or platform it might be connected to something else entirely. + * The calling driver is responsible for mapping a user-level input to + * the right pins on the i2c device. */ struct v4l2_subdev_audio_ops { int (*s_clock_freq)(struct v4l2_subdev *sd, u32 freq); @@ -243,6 +247,7 @@ struct v4l2_subdev_audio_ops { /** * struct v4l2_mbus_frame_desc_entry - media bus frame description structure + * * @flags: V4L2_MBUS_FRAME_DESC_FL_* flags * @pixelcode: media bus pixel code, valid if FRAME_DESC_FL_BLOB is not set * @length: number of octets per frame, valid if V4L2_MBUS_FRAME_DESC_FL_BLOB @@ -266,45 +271,46 @@ struct v4l2_mbus_frame_desc { unsigned short num_entries; }; -/* - s_std_output: set v4l2_std_id for video OUTPUT devices. This is ignored by - video input devices. - - g_std_output: get current standard for video OUTPUT devices. This is ignored - by video input devices. - - g_tvnorms: get v4l2_std_id with all standards supported by the video - CAPTURE device. This is ignored by video output devices. - - g_tvnorms_output: get v4l2_std_id with all standards supported by the video - OUTPUT device. This is ignored by video capture devices. - - s_crystal_freq: sets the frequency of the crystal used to generate the - clocks in Hz. An extra flags field allows device specific configuration - regarding clock frequency dividers, etc. If not used, then set flags - to 0. If the frequency is not supported, then -EINVAL is returned. - - g_input_status: get input status. Same as the status field in the v4l2_input - struct. - - s_routing: see s_routing in audio_ops, except this version is for video - devices. - - s_dv_timings(): Set custom dv timings in the sub device. This is used - when sub device is capable of setting detailed timing information - in the hardware to generate/detect the video signal. - - g_dv_timings(): Get custom dv timings in the sub device. - - g_mbus_config: get supported mediabus configurations - - s_mbus_config: set a certain mediabus configuration. This operation is added - for compatibility with soc-camera drivers and should not be used by new - software. - - s_rx_buffer: set a host allocated memory buffer for the subdev. The subdev - can adjust @size to a lower value and must not write more data to the - buffer starting at @data than the original value of @size. +/** + * struct v4l2_subdev_video_ops - Callbacks used when v4l device was opened + * in video mode. + * @s_std_output: set v4l2_std_id for video OUTPUT devices. This is ignored by + * video input devices. + * + * @g_std_output: get current standard for video OUTPUT devices. This is ignored + * by video input devices. + * + * @g_tvnorms: get v4l2_std_id with all standards supported by the video + * CAPTURE device. This is ignored by video output devices. + * + * @g_tvnorms_output: get v4l2_std_id with all standards supported by the video + * OUTPUT device. This is ignored by video capture devices. + * + * @s_crystal_freq: sets the frequency of the crystal used to generate the + * clocks in Hz. An extra flags field allows device specific configuration + * regarding clock frequency dividers, etc. If not used, then set flags + * to 0. If the frequency is not supported, then -EINVAL is returned. + * + * @g_input_status: get input status. Same as the status field in the v4l2_input + * struct. + * + * @s_routing: see s_routing in audio_ops, except this version is for video + * devices. + * + * @s_dv_timings(): Set custom dv timings in the sub device. This is used + * when sub device is capable of setting detailed timing information + * in the hardware to generate/detect the video signal. + * + * @g_dv_timings(): Get custom dv timings in the sub device. + * @g_mbus_config: get supported mediabus configurations + * + * @s_mbus_config: set a certain mediabus configuration. This operation is added + * for compatibility with soc-camera drivers and should not be used by new + * software. + * + * @s_rx_buffer: set a host allocated memory buffer for the subdev. The subdev + * can adjust @size to a lower value and must not write more data to the + * buffer starting at @data than the original value of @size. */ struct v4l2_subdev_video_ops { int (*s_routing)(struct v4l2_subdev *sd, u32 input, u32 output, u32 config); @@ -341,34 +347,37 @@ struct v4l2_subdev_video_ops { unsigned int *size); }; -/* - decode_vbi_line: video decoders that support sliced VBI need to implement - this ioctl. Field p of the v4l2_sliced_vbi_line struct is set to the - start of the VBI data that was generated by the decoder. The driver - then parses the sliced VBI data and sets the other fields in the - struct accordingly. The pointer p is updated to point to the start of - the payload which can be copied verbatim into the data field of the - v4l2_sliced_vbi_data struct. If no valid VBI data was found, then the - type field is set to 0 on return. - - s_vbi_data: used to generate VBI signals on a video signal. - v4l2_sliced_vbi_data is filled with the data packets that should be - output. Note that if you set the line field to 0, then that VBI signal - is disabled. If no valid VBI data was found, then the type field is - set to 0 on return. - - g_vbi_data: used to obtain the sliced VBI packet from a readback register. - Not all video decoders support this. If no data is available because - the readback register contains invalid or erroneous data -EIO is - returned. Note that you must fill in the 'id' member and the 'field' - member (to determine whether CC data from the first or second field - should be obtained). - - s_raw_fmt: setup the video encoder/decoder for raw VBI. - - g_sliced_fmt: retrieve the current sliced VBI settings. - - s_sliced_fmt: setup the sliced VBI settings. +/** + * struct v4l2_subdev_vbi_ops - Callbacks used when v4l device was opened + * in video mode via the vbi device node. + * + * @decode_vbi_line: video decoders that support sliced VBI need to implement + * this ioctl. Field p of the v4l2_sliced_vbi_line struct is set to the + * start of the VBI data that was generated by the decoder. The driver + * then parses the sliced VBI data and sets the other fields in the + * struct accordingly. The pointer p is updated to point to the start of + * the payload which can be copied verbatim into the data field of the + * v4l2_sliced_vbi_data struct. If no valid VBI data was found, then the + * type field is set to 0 on return. + * + * @s_vbi_data: used to generate VBI signals on a video signal. + * v4l2_sliced_vbi_data is filled with the data packets that should be + * output. Note that if you set the line field to 0, then that VBI signal + * is disabled. If no valid VBI data was found, then the type field is + * set to 0 on return. + * + * @g_vbi_data: used to obtain the sliced VBI packet from a readback register. + * Not all video decoders support this. If no data is available because + * the readback register contains invalid or erroneous data -EIO is + * returned. Note that you must fill in the 'id' member and the 'field' + * member (to determine whether CC data from the first or second field + * should be obtained). + * + * @s_raw_fmt: setup the video encoder/decoder for raw VBI. + * + * @g_sliced_fmt: retrieve the current sliced VBI settings. + * + * @s_sliced_fmt: setup the sliced VBI settings. */ struct v4l2_subdev_vbi_ops { int (*decode_vbi_line)(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi_line); -- cgit v1.2.3 From 783001adf99b432a31acb2a4c198da8846d8dcc3 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 22 Aug 2015 06:00:22 -0300 Subject: [media] v4l2-subdev: Add description for core ioctl handlers Warning(.//include/media/v4l2-subdev.h:183): No description found for parameter 'queryctrl' Warning(.//include/media/v4l2-subdev.h:183): No description found for parameter 'g_ctrl' Warning(.//include/media/v4l2-subdev.h:183): No description found for parameter 's_ctrl' Warning(.//include/media/v4l2-subdev.h:183): No description found for parameter 'g_ext_ctrls' Warning(.//include/media/v4l2-subdev.h:183): No description found for parameter 's_ext_ctrls' Warning(.//include/media/v4l2-subdev.h:183): No description found for parameter 'try_ext_ctrls' Warning(.//include/media/v4l2-subdev.h:183): No description found for parameter 'querymenu' Warning(.//include/media/v4l2-subdev.h:183): No description found for parameter 'g_register' Warning(.//include/media/v4l2-subdev.h:183): No description found for parameter 's_register' Signed-off-by: Mauro Carvalho Chehab Acked-by: Jonathan Corbet --- include/media/v4l2-subdev.h | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 7b15eccaceb3..78d2f0ac1848 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -119,7 +119,9 @@ struct v4l2_subdev_io_pin_config { }; /** - * struct v4l2_subdev_core_ops - Define ops callbacks for subdevs + * struct v4l2_subdev_core_ops - Define core ops callbacks for subdevs + * + * @log_status: callback for VIDIOC_LOG_STATUS ioctl handler code. * * @s_io_pin_config: configure one or more chip I/O pins for chips that * multiplex different internal signal pads out to IO pins. This function @@ -141,6 +143,24 @@ struct v4l2_subdev_io_pin_config { * @s_gpio: set GPIO pins. Very simple right now, might need to be extended with * a direction argument if needed. * + * @queryctrl: callback for VIDIOC_QUERYCTL ioctl handler code. + * + * @g_ctrl: callback for VIDIOC_G_CTRL ioctl handler code. + * + * @s_ctrl: callback for VIDIOC_S_CTRL ioctl handler code. + * + * @g_ext_ctrls: callback for VIDIOC_G_EXT_CTRLS ioctl handler code. + * + * @s_ext_ctrls: callback for VIDIOC_S_EXT_CTRLS ioctl handler code. + * + * @try_ext_ctrls: callback for VIDIOC_TRY_EXT_CTRLS ioctl handler code. + * + * @querymenu: callback for VIDIOC_QUERYMENU ioctl handler code. + * + * @g_register: callback for VIDIOC_G_REGISTER ioctl handler code. + * + * @s_register: callback for VIDIOC_G_REGISTER ioctl handler code. + * * @s_power: puts subdevice in power saving mode (on == 0) or normal operation * mode (on == 1). * -- cgit v1.2.3 From 91963aae9a65286eea30ca810027ddc09b26e779 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 22 Aug 2015 06:07:29 -0300 Subject: [media] v4l2-subdev: Add description for radio ioctl handlers Warning(.//include/media/v4l2-subdev.h:224): No description found for parameter 's_radio' Warning(.//include/media/v4l2-subdev.h:224): No description found for parameter 's_frequency' Warning(.//include/media/v4l2-subdev.h:224): No description found for parameter 'enum_freq_bands' Warning(.//include/media/v4l2-subdev.h:224): No description found for parameter 'g_tuner' Warning(.//include/media/v4l2-subdev.h:224): No description found for parameter 'g_modulator' Warning(.//include/media/v4l2-subdev.h:224): No description found for parameter 's_modulator' Signed-off-by: Mauro Carvalho Chehab Acked-by: Jonathan Corbet --- include/media/v4l2-subdev.h | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 78d2f0ac1848..d9315e5e8957 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -205,13 +205,28 @@ struct v4l2_subdev_core_ops { /** * struct s_radio - Callbacks used when v4l device was opened in radio mode. * - * @g_frequency: freq->type must be filled in. Normally done by video_ioctl2 - * or the bridge driver. - * @g_tuner: - * @s_tuner: vt->type must be filled in. Normally done by video_ioctl2 or the + * @s_radio: callback for VIDIOC_S_RADIO ioctl handler code. + * + * @s_frequency: callback for VIDIOC_S_FREQUENCY ioctl handler code. + * + * @g_frequency: callback for VIDIOC_G_FREQUENCY ioctl handler code. + * freq->type must be filled in. Normally done by video_ioctl2 + * or the bridge driver. + * + * @enum_freq_bands: callback for VIDIOC_ENUM_FREQ_BANDS ioctl handler code. + * + * @g_tuner: callback for VIDIOC_G_TUNER ioctl handler code. + * + * @s_tuner: callback for VIDIOC_S_TUNER ioctl handler code. vt->type must be + * filled in. Normally done by video_ioctl2 or the * bridge driver. * + * @g_modulator: callback for VIDIOC_G_MODULATOR ioctl handler code. + * + * @s_modulator: callback for VIDIOC_S_MODULATOR ioctl handler code. + * * @s_type_addr: sets tuner type and its I2C addr. + * * @s_config: sets tda9887 specific stuff, like port1, port2 and qss */ struct v4l2_subdev_tuner_ops { -- cgit v1.2.3 From b84aeb03a886beef39f80e30b76d30a833dd8161 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 22 Aug 2015 06:12:35 -0300 Subject: [media] v4l2-subdev: reorder the v4l2_subdev_video_ops comments The comments for struct v4l2_subdev_video_ops are out of the order as the parameter would appear at struct. This is not a problem for DocBook, but humans find harder to mentally reorder ;) So, put them at the right order. That makes easier to check what's missing, and to put the comments in the right place. Signed-off-by: Mauro Carvalho Chehab Acked-by: Jonathan Corbet --- include/media/v4l2-subdev.h | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index d9315e5e8957..884a8d7c1368 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -309,6 +309,15 @@ struct v4l2_mbus_frame_desc { /** * struct v4l2_subdev_video_ops - Callbacks used when v4l device was opened * in video mode. + * + * @s_routing: see s_routing in audio_ops, except this version is for video + * devices. + * + * @s_crystal_freq: sets the frequency of the crystal used to generate the + * clocks in Hz. An extra flags field allows device specific configuration + * regarding clock frequency dividers, etc. If not used, then set flags + * to 0. If the frequency is not supported, then -EINVAL is returned. + * * @s_std_output: set v4l2_std_id for video OUTPUT devices. This is ignored by * video input devices. * @@ -321,22 +330,15 @@ struct v4l2_mbus_frame_desc { * @g_tvnorms_output: get v4l2_std_id with all standards supported by the video * OUTPUT device. This is ignored by video capture devices. * - * @s_crystal_freq: sets the frequency of the crystal used to generate the - * clocks in Hz. An extra flags field allows device specific configuration - * regarding clock frequency dividers, etc. If not used, then set flags - * to 0. If the frequency is not supported, then -EINVAL is returned. - * * @g_input_status: get input status. Same as the status field in the v4l2_input * struct. * - * @s_routing: see s_routing in audio_ops, except this version is for video - * devices. - * * @s_dv_timings(): Set custom dv timings in the sub device. This is used * when sub device is capable of setting detailed timing information * in the hardware to generate/detect the video signal. * * @g_dv_timings(): Get custom dv timings in the sub device. + * * @g_mbus_config: get supported mediabus configurations * * @s_mbus_config: set a certain mediabus configuration. This operation is added -- cgit v1.2.3 From 914728ab609f7160a1b6d2a3be1edd8f6b2cf88e Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 22 Aug 2015 06:18:13 -0300 Subject: [media] v4l2_subdev: describe ioctl parms at v4l2_subdev_video_ops Warning(.//include/media/v4l2-subdev.h:381): No description found for parameter 'g_std' Warning(.//include/media/v4l2-subdev.h:381): No description found for parameter 's_std' Warning(.//include/media/v4l2-subdev.h:381): No description found for parameter 'querystd' Warning(.//include/media/v4l2-subdev.h:381): No description found for parameter 'cropcap' Warning(.//include/media/v4l2-subdev.h:381): No description found for parameter 'g_crop' Warning(.//include/media/v4l2-subdev.h:381): No description found for parameter 's_crop' Warning(.//include/media/v4l2-subdev.h:381): No description found for parameter 'g_parm' Warning(.//include/media/v4l2-subdev.h:381): No description found for parameter 's_parm' Warning(.//include/media/v4l2-subdev.h:381): No description found for parameter 'g_frame_interval' Warning(.//include/media/v4l2-subdev.h:381): No description found for parameter 's_frame_interval' Warning(.//include/media/v4l2-subdev.h:381): No description found for parameter 's_dv_timings' Warning(.//include/media/v4l2-subdev.h:381): No description found for parameter 'g_dv_timings' Warning(.//include/media/v4l2-subdev.h:381): No description found for parameter 'query_dv_timings' Signed-off-by: Mauro Carvalho Chehab Acked-by: Jonathan Corbet --- include/media/v4l2-subdev.h | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 884a8d7c1368..c33c24360b86 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -318,12 +318,18 @@ struct v4l2_mbus_frame_desc { * regarding clock frequency dividers, etc. If not used, then set flags * to 0. If the frequency is not supported, then -EINVAL is returned. * + * @g_std: callback for VIDIOC_G_STD ioctl handler code. + * + * @s_std: callback for VIDIOC_S_STD ioctl handler code. + * * @s_std_output: set v4l2_std_id for video OUTPUT devices. This is ignored by * video input devices. * * @g_std_output: get current standard for video OUTPUT devices. This is ignored * by video input devices. * + * @querystd: callback for VIDIOC_QUERYSTD ioctl handler code. + * * @g_tvnorms: get v4l2_std_id with all standards supported by the video * CAPTURE device. This is ignored by video output devices. * @@ -333,11 +339,27 @@ struct v4l2_mbus_frame_desc { * @g_input_status: get input status. Same as the status field in the v4l2_input * struct. * - * @s_dv_timings(): Set custom dv timings in the sub device. This is used + * @cropcap: callback for VIDIOC_CROPCAP ioctl handler code. + * + * @g_crop: callback for VIDIOC_G_CROP ioctl handler code. + * + * @s_crop: callback for VIDIOC_S_CROP ioctl handler code. + * + * @g_parm: callback for VIDIOC_G_PARM ioctl handler code. + * + * @s_parm: callback for VIDIOC_S_PARM ioctl handler code. + * + * @g_frame_interval: callback for VIDIOC_G_FRAMEINTERVAL ioctl handler code. + * + * @s_frame_interval: callback for VIDIOC_S_FRAMEINTERVAL ioctl handler code. + * + * @s_dv_timings: Set custom dv timings in the sub device. This is used * when sub device is capable of setting detailed timing information * in the hardware to generate/detect the video signal. * - * @g_dv_timings(): Get custom dv timings in the sub device. + * @g_dv_timings: Get custom dv timings in the sub device. + * + * @query_dv_timings: callback for VIDIOC_QUERY_DV_TIMINGS ioctl handler code. * * @g_mbus_config: get supported mediabus configurations * -- cgit v1.2.3 From e44cc20a1d0952835e2fc01706b609ee8a3d9d92 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 22 Aug 2015 06:34:06 -0300 Subject: [media] v4l2_subdev: describe ioctl parms at the remaining structs Fixes the following warnings: Warning(.//include/media/v4l2-subdev.h:445): No description found for parameter 'g_sliced_vbi_cap' Warning(.//include/media/v4l2-subdev.h:589): No description found for parameter 'enum_mbus_code' Warning(.//include/media/v4l2-subdev.h:589): No description found for parameter 'enum_frame_size' Warning(.//include/media/v4l2-subdev.h:589): No description found for parameter 'enum_frame_interval' Warning(.//include/media/v4l2-subdev.h:589): No description found for parameter 'get_fmt' Warning(.//include/media/v4l2-subdev.h:589): No description found for parameter 'set_fmt' Warning(.//include/media/v4l2-subdev.h:589): No description found for parameter 'get_selection' Warning(.//include/media/v4l2-subdev.h:589): No description found for parameter 'set_selection' Warning(.//include/media/v4l2-subdev.h:589): No description found for parameter 'get_edid' Warning(.//include/media/v4l2-subdev.h:589): No description found for parameter 'set_edid' Warning(.//include/media/v4l2-subdev.h:589): No description found for parameter 'dv_timings_cap' Warning(.//include/media/v4l2-subdev.h:589): No description found for parameter 'enum_dv_timings' Signed-off-by: Mauro Carvalho Chehab Acked-by: Jonathan Corbet --- include/media/v4l2-subdev.h | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index c33c24360b86..c6205c038b06 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -432,6 +432,8 @@ struct v4l2_subdev_video_ops { * member (to determine whether CC data from the first or second field * should be obtained). * + * @g_sliced_vbi_cap: callback for VIDIOC_SLICED_VBI_CAP ioctl handler code. + * * @s_raw_fmt: setup the video encoder/decoder for raw VBI. * * @g_sliced_fmt: retrieve the current sliced VBI settings. @@ -549,7 +551,35 @@ struct v4l2_subdev_pad_config { /** * struct v4l2_subdev_pad_ops - v4l2-subdev pad level operations + * + * @enum_mbus_code: callback for VIDIOC_SUBDEV_ENUM_MBUS_CODE ioctl handler + * code. + * @enum_frame_size: callback for VIDIOC_SUBDEV_ENUM_FRAME_SIZE ioctl handler + * code. + * + * @enum_frame_interval: callback for VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL ioctl + * handler code. + * + * @get_fmt: callback for VIDIOC_SUBDEV_G_FMT ioctl handler code. + * + * @set_fmt: callback for VIDIOC_SUBDEV_S_FMT ioctl handler code. + * + * @get_selection: callback for VIDIOC_SUBDEV_G_SELECTION ioctl handler code. + * + * @set_selection: callback for VIDIOC_SUBDEV_S_SELECTION ioctl handler code. + * + * @get_edid: callback for VIDIOC_SUBDEV_G_EDID ioctl handler code. + * + * @set_edid: callback for VIDIOC_SUBDEV_S_EDID ioctl handler code. + * + * @dv_timings_cap: callback for VIDIOC_SUBDEV_DV_TIMINGS_CAP ioctl handler + * code. + * + * @enum_dv_timings: callback for VIDIOC_SUBDEV_ENUM_DV_TIMINGS ioctl handler + * code. + * * @get_frame_desc: get the current low level media bus frame parameters. + * * @get_frame_desc: set the low level media bus frame parameters, @fd array * may be adjusted by the subdev driver to device capabilities. */ -- cgit v1.2.3 From 1d8955b2c3fa476a1222b020349937c93a8e4762 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 22 Aug 2015 06:59:26 -0300 Subject: [media] v4l2-subdev: add remaining argument descriptions Warning(.//include/media/v4l2-subdev.h:203): No description found for parameter 'ioctl' Warning(.//include/media/v4l2-subdev.h:203): No description found for parameter 'compat_ioctl32' Warning(.//include/media/v4l2-subdev.h:203): No description found for parameter 'subscribe_event' Warning(.//include/media/v4l2-subdev.h:203): No description found for parameter 'unsubscribe_event' Warning(.//include/media/v4l2-subdev.h:273): No description found for parameter 's_stream' Warning(.//include/media/v4l2-subdev.h:407): No description found for parameter 's_stream' Warning(.//include/media/v4l2-subdev.h:623): No description found for parameter 'link_validate' Warning(.//include/media/v4l2-subdev.h:623): No description found for parameter 'set_frame_desc' Signed-off-by: Mauro Carvalho Chehab Acked-by: Jonathan Corbet --- include/media/v4l2-subdev.h | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index c6205c038b06..b273cf9ac047 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -157,6 +157,12 @@ struct v4l2_subdev_io_pin_config { * * @querymenu: callback for VIDIOC_QUERYMENU ioctl handler code. * + * @ioctl: called at the end of ioctl() syscall handler at the V4L2 core. + * used to provide support for private ioctls used on the driver. + * + * @compat_ioctl32: called when a 32 bits application uses a 64 bits Kernel, + * in order to fix data passed from/to userspace. + * * @g_register: callback for VIDIOC_G_REGISTER ioctl handler code. * * @s_register: callback for VIDIOC_G_REGISTER ioctl handler code. @@ -168,6 +174,11 @@ struct v4l2_subdev_io_pin_config { * handler, when an interrupt status has be raised due to this subdev, * so that this subdev can handle the details. It may schedule work to be * performed later. It must not sleep. *Called from an IRQ context*. + * + * @subscribe_event: used by the drivers to request the control framework that + * for it to be warned when the value of a control changes. + * + * @unsubscribe_event: remove event subscription from the control framework. */ struct v4l2_subdev_core_ops { int (*log_status)(struct v4l2_subdev *sd); @@ -264,6 +275,9 @@ struct v4l2_subdev_tuner_ops { * board or platform it might be connected to something else entirely. * The calling driver is responsible for mapping a user-level input to * the right pins on the i2c device. + * + * @s_stream: used to notify the audio code that stream will start or has + * stopped. */ struct v4l2_subdev_audio_ops { int (*s_clock_freq)(struct v4l2_subdev *sd, u32 freq); @@ -339,6 +353,9 @@ struct v4l2_mbus_frame_desc { * @g_input_status: get input status. Same as the status field in the v4l2_input * struct. * + * @s_stream: used to notify the driver that a video stream will start or has + * stopped. + * * @cropcap: callback for VIDIOC_CROPCAP ioctl handler code. * * @g_crop: callback for VIDIOC_G_CROP ioctl handler code. @@ -578,9 +595,12 @@ struct v4l2_subdev_pad_config { * @enum_dv_timings: callback for VIDIOC_SUBDEV_ENUM_DV_TIMINGS ioctl handler * code. * + * @link_validate: used by the media controller code to check if the links + * that belongs to a pipeline can be used for stream. + * * @get_frame_desc: get the current low level media bus frame parameters. * - * @get_frame_desc: set the low level media bus frame parameters, @fd array + * @set_frame_desc: set the low level media bus frame parameters, @fd array * may be adjusted by the subdev driver to device capabilities. */ struct v4l2_subdev_pad_ops { -- cgit v1.2.3 From fbefb1a87c7e6e24df6ca5b42b42985e2680c2ea Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 22 Aug 2015 07:09:29 -0300 Subject: [media] DocBook: add dvb_ca_en50221.h to documentation There are already some tags at dvb_ca_en50221.h, but using a different format. Convert them, fix a few entries and add to the device-drivers DocBook. Signed-off-by: Mauro Carvalho Chehab Acked-by: Jonathan Corbet --- Documentation/DocBook/device-drivers.tmpl | 2 +- drivers/media/dvb-core/dvb_ca_en50221.c | 167 +++++++++++++++--------------- drivers/media/dvb-core/dvb_ca_en50221.h | 34 +++--- 3 files changed, 102 insertions(+), 101 deletions(-) diff --git a/Documentation/DocBook/device-drivers.tmpl b/Documentation/DocBook/device-drivers.tmpl index e3e0f4880770..e0c358760344 100644 --- a/Documentation/DocBook/device-drivers.tmpl +++ b/Documentation/DocBook/device-drivers.tmpl @@ -227,6 +227,7 @@ X!Isound/sound_firmware.c !Iinclude/media/v4l2-of.h !Iinclude/media/v4l2-subdev.h !Iinclude/media/rc-core.h +!Idrivers/media/dvb-core/dvb_ca_en50221.h diff --git a/drivers/media/dvb-core/dvb_ca_en50221.c b/drivers/media/dvb-core/dvb_ca_en50221.c index 72937756f60c..fb66184dc9b6 100644 --- a/drivers/media/dvb-core/dvb_ca_en50221.c +++ b/drivers/media/dvb-core/dvb_ca_en50221.c @@ -169,10 +169,10 @@ static int dvb_ca_en50221_write_data(struct dvb_ca_private *ca, int slot, u8 * e /** * Safely find needle in haystack. * - * @param haystack Buffer to look in. - * @param hlen Number of bytes in haystack. - * @param needle Buffer to find. - * @param nlen Number of bytes in needle. + * @haystack: Buffer to look in. + * @hlen: Number of bytes in haystack. + * @needle: Buffer to find. + * @nlen: Number of bytes in needle. * @return Pointer into haystack needle was found at, or NULL if not found. */ static char *findstr(char * haystack, int hlen, char * needle, int nlen) @@ -197,7 +197,7 @@ static char *findstr(char * haystack, int hlen, char * needle, int nlen) /** - * Check CAM status. + * dvb_ca_en50221_check_camstatus - Check CAM status. */ static int dvb_ca_en50221_check_camstatus(struct dvb_ca_private *ca, int slot) { @@ -240,13 +240,13 @@ static int dvb_ca_en50221_check_camstatus(struct dvb_ca_private *ca, int slot) /** - * Wait for flags to become set on the STATUS register on a CAM interface, - * checking for errors and timeout. + * dvb_ca_en50221_wait_if_status - Wait for flags to become set on the STATUS + * register on a CAM interface, checking for errors and timeout. * - * @param ca CA instance. - * @param slot Slot on interface. - * @param waitfor Flags to wait for. - * @param timeout_ms Timeout in milliseconds. + * @ca: CA instance. + * @slot: Slot on interface. + * @waitfor: Flags to wait for. + * @timeout_ms: Timeout in milliseconds. * * @return 0 on success, nonzero on error. */ @@ -290,10 +290,10 @@ static int dvb_ca_en50221_wait_if_status(struct dvb_ca_private *ca, int slot, /** - * Initialise the link layer connection to a CAM. + * dvb_ca_en50221_link_init - Initialise the link layer connection to a CAM. * - * @param ca CA instance. - * @param slot Slot id. + * @ca: CA instance. + * @slot: Slot id. * * @return 0 on success, nonzero on failure. */ @@ -346,14 +346,14 @@ static int dvb_ca_en50221_link_init(struct dvb_ca_private *ca, int slot) } /** - * Read a tuple from attribute memory. + * dvb_ca_en50221_read_tuple - Read a tuple from attribute memory. * - * @param ca CA instance. - * @param slot Slot id. - * @param address Address to read from. Updated. - * @param tupleType Tuple id byte. Updated. - * @param tupleLength Tuple length. Updated. - * @param tuple Dest buffer for tuple (must be 256 bytes). Updated. + * @ca: CA instance. + * @slot: Slot id. + * @address: Address to read from. Updated. + * @tupleType: Tuple id byte. Updated. + * @tupleLength: Tuple length. Updated. + * @tuple: Dest buffer for tuple (must be 256 bytes). Updated. * * @return 0 on success, nonzero on error. */ @@ -399,11 +399,11 @@ static int dvb_ca_en50221_read_tuple(struct dvb_ca_private *ca, int slot, /** - * Parse attribute memory of a CAM module, extracting Config register, and checking - * it is a DVB CAM module. + * dvb_ca_en50221_parse_attributes - Parse attribute memory of a CAM module, + * extracting Config register, and checking it is a DVB CAM module. * - * @param ca CA instance. - * @param slot Slot id. + * @ca: CA instance. + * @slot: Slot id. * * @return 0 on success, <0 on failure. */ @@ -546,10 +546,10 @@ static int dvb_ca_en50221_parse_attributes(struct dvb_ca_private *ca, int slot) /** - * Set CAM's configoption correctly. + * dvb_ca_en50221_set_configoption - Set CAM's configoption correctly. * - * @param ca CA instance. - * @param slot Slot containing the CAM. + * @ca: CA instance. + * @slot: Slot containing the CAM. */ static int dvb_ca_en50221_set_configoption(struct dvb_ca_private *ca, int slot) { @@ -574,15 +574,16 @@ static int dvb_ca_en50221_set_configoption(struct dvb_ca_private *ca, int slot) /** - * This function talks to an EN50221 CAM control interface. It reads a buffer of - * data from the CAM. The data can either be stored in a supplied buffer, or - * automatically be added to the slot's rx_buffer. + * dvb_ca_en50221_read_data - This function talks to an EN50221 CAM control + * interface. It reads a buffer of data from the CAM. The data can either + * be stored in a supplied buffer, or automatically be added to the slot's + * rx_buffer. * - * @param ca CA instance. - * @param slot Slot to read from. - * @param ebuf If non-NULL, the data will be written to this buffer. If NULL, + * @ca: CA instance. + * @slot: Slot to read from. + * @ebuf: If non-NULL, the data will be written to this buffer. If NULL, * the data will be added into the buffering system as a normal fragment. - * @param ecount Size of ebuf. Ignored if ebuf is NULL. + * @ecount: Size of ebuf. Ignored if ebuf is NULL. * * @return Number of bytes read, or < 0 on error */ @@ -698,14 +699,14 @@ exit: /** - * This function talks to an EN50221 CAM control interface. It writes a buffer of data - * to a CAM. + * dvb_ca_en50221_write_data - This function talks to an EN50221 CAM control + * interface. It writes a buffer of data to a CAM. * - * @param ca CA instance. - * @param slot Slot to write to. - * @param ebuf The data in this buffer is treated as a complete link-level packet to + * @ca: CA instance. + * @slot: Slot to write to. + * @ebuf: The data in this buffer is treated as a complete link-level packet to * be written. - * @param count Size of ebuf. + * @count: Size of ebuf. * * @return Number of bytes written, or < 0 on error. */ @@ -790,10 +791,10 @@ EXPORT_SYMBOL(dvb_ca_en50221_camchange_irq); /** - * A CAM has been removed => shut it down. + * dvb_ca_en50221_camready_irq - A CAM has been removed => shut it down. * - * @param ca CA instance. - * @param slot Slot to shut down. + * @ca: CA instance. + * @slot: Slot to shut down. */ static int dvb_ca_en50221_slot_shutdown(struct dvb_ca_private *ca, int slot) { @@ -815,11 +816,11 @@ EXPORT_SYMBOL(dvb_ca_en50221_camready_irq); /** - * A CAMCHANGE IRQ has occurred. + * dvb_ca_en50221_camready_irq - A CAMCHANGE IRQ has occurred. * - * @param ca CA instance. - * @param slot Slot concerned. - * @param change_type One of the DVB_CA_CAMCHANGE_* values. + * @ca: CA instance. + * @slot: Slot concerned. + * @change_type: One of the DVB_CA_CAMCHANGE_* values. */ void dvb_ca_en50221_camchange_irq(struct dvb_ca_en50221 *pubca, int slot, int change_type) { @@ -844,10 +845,10 @@ EXPORT_SYMBOL(dvb_ca_en50221_frda_irq); /** - * A CAMREADY IRQ has occurred. + * dvb_ca_en50221_camready_irq - A CAMREADY IRQ has occurred. * - * @param ca CA instance. - * @param slot Slot concerned. + * @ca: CA instance. + * @slot: Slot concerned. */ void dvb_ca_en50221_camready_irq(struct dvb_ca_en50221 *pubca, int slot) { @@ -865,8 +866,8 @@ void dvb_ca_en50221_camready_irq(struct dvb_ca_en50221 *pubca, int slot) /** * An FR or DA IRQ has occurred. * - * @param ca CA instance. - * @param slot Slot concerned. + * @ca: CA instance. + * @slot: Slot concerned. */ void dvb_ca_en50221_frda_irq(struct dvb_ca_en50221 *pubca, int slot) { @@ -899,7 +900,7 @@ void dvb_ca_en50221_frda_irq(struct dvb_ca_en50221 *pubca, int slot) /** * Wake up the DVB CA thread * - * @param ca CA instance. + * @ca: CA instance. */ static void dvb_ca_en50221_thread_wakeup(struct dvb_ca_private *ca) { @@ -914,7 +915,7 @@ static void dvb_ca_en50221_thread_wakeup(struct dvb_ca_private *ca) /** * Update the delay used by the thread. * - * @param ca CA instance. + * @ca: CA instance. */ static void dvb_ca_en50221_thread_update_delay(struct dvb_ca_private *ca) { @@ -1177,10 +1178,10 @@ static int dvb_ca_en50221_thread(void *data) * Real ioctl implementation. * NOTE: CA_SEND_MSG/CA_GET_MSG ioctls have userspace buffers passed to them. * - * @param inode Inode concerned. - * @param file File concerned. - * @param cmd IOCTL command. - * @param arg Associated argument. + * @inode: Inode concerned. + * @file: File concerned. + * @cmd: IOCTL command. + * @arg: Associated argument. * * @return 0 on success, <0 on error. */ @@ -1258,10 +1259,10 @@ out_unlock: /** * Wrapper for ioctl implementation. * - * @param inode Inode concerned. - * @param file File concerned. - * @param cmd IOCTL command. - * @param arg Associated argument. + * @inode: Inode concerned. + * @file: File concerned. + * @cmd: IOCTL command. + * @arg: Associated argument. * * @return 0 on success, <0 on error. */ @@ -1275,10 +1276,10 @@ static long dvb_ca_en50221_io_ioctl(struct file *file, /** * Implementation of write() syscall. * - * @param file File structure. - * @param buf Source buffer. - * @param count Size of source buffer. - * @param ppos Position in file (ignored). + * @file: File structure. + * @buf: Source buffer. + * @count: Size of source buffer. + * @ppos: Position in file (ignored). * * @return Number of bytes read, or <0 on error. */ @@ -1416,10 +1417,10 @@ nextslot: /** * Implementation of read() syscall. * - * @param file File structure. - * @param buf Destination buffer. - * @param count Size of destination buffer. - * @param ppos Position in file (ignored). + * @file: File structure. + * @buf: Destination buffer. + * @count: Size of destination buffer. + * @ppos: Position in file (ignored). * * @return Number of bytes read, or <0 on error. */ @@ -1519,8 +1520,8 @@ exit: /** * Implementation of file open syscall. * - * @param inode Inode concerned. - * @param file File concerned. + * @inode: Inode concerned. + * @file: File concerned. * * @return 0 on success, <0 on failure. */ @@ -1564,8 +1565,8 @@ static int dvb_ca_en50221_io_open(struct inode *inode, struct file *file) /** * Implementation of file close syscall. * - * @param inode Inode concerned. - * @param file File concerned. + * @inode: Inode concerned. + * @file: File concerned. * * @return 0 on success, <0 on failure. */ @@ -1592,8 +1593,8 @@ static int dvb_ca_en50221_io_release(struct inode *inode, struct file *file) /** * Implementation of poll() syscall. * - * @param file File concerned. - * @param wait poll wait table. + * @file: File concerned. + * @wait: poll wait table. * * @return Standard poll mask. */ @@ -1656,10 +1657,10 @@ static const struct dvb_device dvbdev_ca = { /** * Initialise a new DVB CA EN50221 interface device. * - * @param dvb_adapter DVB adapter to attach the new CA device to. - * @param ca The dvb_ca instance. - * @param flags Flags describing the CA device (DVB_CA_FLAG_*). - * @param slot_count Number of slots supported. + * @dvb_adapter: DVB adapter to attach the new CA device to. + * @ca: The dvb_ca instance. + * @flags: Flags describing the CA device (DVB_CA_FLAG_*). + * @slot_count: Number of slots supported. * * @return 0 on success, nonzero on failure */ @@ -1743,8 +1744,8 @@ EXPORT_SYMBOL(dvb_ca_en50221_release); /** * Release a DVB CA EN50221 interface device. * - * @param ca_dev The dvb_device_t instance for the CA device. - * @param ca The associated dvb_ca instance. + * @ca_dev: The dvb_device_t instance for the CA device. + * @ca: The associated dvb_ca instance. */ void dvb_ca_en50221_release(struct dvb_ca_en50221 *pubca) { diff --git a/drivers/media/dvb-core/dvb_ca_en50221.h b/drivers/media/dvb-core/dvb_ca_en50221.h index 7df2e141187a..aba3b4fbd704 100644 --- a/drivers/media/dvb-core/dvb_ca_en50221.h +++ b/drivers/media/dvb-core/dvb_ca_en50221.h @@ -83,27 +83,27 @@ struct dvb_ca_en50221 { /* Functions for reporting IRQ events */ /** - * A CAMCHANGE IRQ has occurred. + * dvb_ca_en50221_camchange_irq - A CAMCHANGE IRQ has occurred. * - * @param ca CA instance. - * @param slot Slot concerned. - * @param change_type One of the DVB_CA_CAMCHANGE_* values + * @pubca: CA instance. + * @slot: Slot concerned. + * @change_type: One of the DVB_CA_CAMCHANGE_* values */ void dvb_ca_en50221_camchange_irq(struct dvb_ca_en50221* pubca, int slot, int change_type); /** - * A CAMREADY IRQ has occurred. + * dvb_ca_en50221_camready_irq - A CAMREADY IRQ has occurred. * - * @param ca CA instance. - * @param slot Slot concerned. + * @pubca: CA instance. + * @slot: Slot concerned. */ void dvb_ca_en50221_camready_irq(struct dvb_ca_en50221* pubca, int slot); /** - * An FR or a DA IRQ has occurred. + * dvb_ca_en50221_frda_irq - An FR or a DA IRQ has occurred. * - * @param ca CA instance. - * @param slot Slot concerned. + * @ca: CA instance. + * @slot: Slot concerned. */ void dvb_ca_en50221_frda_irq(struct dvb_ca_en50221* ca, int slot); @@ -113,21 +113,21 @@ void dvb_ca_en50221_frda_irq(struct dvb_ca_en50221* ca, int slot); /* Initialisation/shutdown functions */ /** - * Initialise a new DVB CA device. + * dvb_ca_en50221_init - Initialise a new DVB CA device. * - * @param dvb_adapter DVB adapter to attach the new CA device to. - * @param ca The dvb_ca instance. - * @param flags Flags describing the CA device (DVB_CA_EN50221_FLAG_*). - * @param slot_count Number of slots supported. + * @dvb_adapter: DVB adapter to attach the new CA device to. + * @ca: The dvb_ca instance. + * @flags: Flags describing the CA device (DVB_CA_EN50221_FLAG_*). + * @slot_count: Number of slots supported. * * @return 0 on success, nonzero on failure */ extern int dvb_ca_en50221_init(struct dvb_adapter *dvb_adapter, struct dvb_ca_en50221* ca, int flags, int slot_count); /** - * Release a DVB CA device. + * dvb_ca_en50221_release - Release a DVB CA device. * - * @param ca The associated dvb_ca instance. + * @ca: The associated dvb_ca instance. */ extern void dvb_ca_en50221_release(struct dvb_ca_en50221* ca); -- cgit v1.2.3 From 4f1c1868e8d4e493d66b8e11eebbc95abdbaf444 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 22 Aug 2015 07:19:20 -0300 Subject: [media] DocBook: add dvb_frontend.h to documentation There are already some comments at dvb_frontend.h that are ready for DocBook, although not properly formatted. Convert them, and add this file to the device-drivers DocBook. Signed-off-by: Mauro Carvalho Chehab Acked-by: Jonathan Corbet --- Documentation/DocBook/device-drivers.tmpl | 2 +- drivers/media/dvb-core/dvb_frontend.h | 68 +++++++++++++++---------------- 2 files changed, 34 insertions(+), 36 deletions(-) diff --git a/Documentation/DocBook/device-drivers.tmpl b/Documentation/DocBook/device-drivers.tmpl index e0c358760344..fb5c16a24e4b 100644 --- a/Documentation/DocBook/device-drivers.tmpl +++ b/Documentation/DocBook/device-drivers.tmpl @@ -228,6 +228,7 @@ X!Isound/sound_firmware.c !Iinclude/media/v4l2-subdev.h !Iinclude/media/rc-core.h !Idrivers/media/dvb-core/dvb_ca_en50221.h +!Idrivers/media/dvb-core/dvb_frontend.h
diff --git a/drivers/media/dvb-core/dvb_math.h b/drivers/media/dvb-core/dvb_math.h index f586aa001ede..34dc1df03cab 100644 --- a/drivers/media/dvb-core/dvb_math.h +++ b/drivers/media/dvb-core/dvb_math.h @@ -25,7 +25,9 @@ #include /** - * computes log2 of a value; the result is shifted left by 24 bits + * cintlog2 - computes log2 of a value; the result is shifted left by 24 bits + * + * @value: The value (must be != 0) * * to use rational values you can use the following method: * intlog2(value) = intlog2(value * 2^x) - x * 2^24 @@ -35,13 +37,15 @@ * intlog2(9) will give 3 << 24 + ... = 3.16... * 2^24 * intlog2(1.5) = intlog2(3) - 2^24 = 0.584... * 2^24 * - * @param value The value (must be != 0) - * @return log2(value) * 2^24 + * + * return: log2(value) * 2^24 */ extern unsigned int intlog2(u32 value); /** - * computes log10 of a value; the result is shifted left by 24 bits + * intlog10 - computes log10 of a value; the result is shifted left by 24 bits + * + * @value: The value (must be != 0) * * to use rational values you can use the following method: * intlog10(value) = intlog10(value * 10^x) - x * 2^24 @@ -52,8 +56,7 @@ extern unsigned int intlog2(u32 value); * * look at intlog2 for similar examples * - * @param value The value (must be != 0) - * @return log10(value) * 2^24 + * return: log10(value) * 2^24 */ extern unsigned int intlog10(u32 value); -- cgit v1.2.3 From 2a86e373e0cf926f5cb41725f1b0ce3874e7b1cf Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 22 Aug 2015 07:38:51 -0300 Subject: [media] DocBook: add dvb_ringbuffer.h to documentation There are already some comments at dvb_ringbuffer.h that are ready for DocBook, although not properly formatted. Convert them, fix some issues and add this file to the device-drivers DocBook. While here, put multi-line comments on the right format. Signed-off-by: Mauro Carvalho Chehab Acked-by: Jonathan Corbet --- Documentation/DocBook/device-drivers.tmpl | 2 +- drivers/media/dvb-core/dvb_ringbuffer.h | 135 +++++++++++++++++------------- 2 files changed, 76 insertions(+), 61 deletions(-) diff --git a/Documentation/DocBook/device-drivers.tmpl b/Documentation/DocBook/device-drivers.tmpl index 21fc7684d706..030b3803cc68 100644 --- a/Documentation/DocBook/device-drivers.tmpl +++ b/Documentation/DocBook/device-drivers.tmpl @@ -230,6 +230,7 @@ X!Isound/sound_firmware.c !Idrivers/media/dvb-core/dvb_ca_en50221.h !Idrivers/media/dvb-core/dvb_frontend.h !Idrivers/media/dvb-core/dvb_math.h +!Idrivers/media/dvb-core/dvb_ringbuffer.h diff --git a/drivers/media/dvb-core/dvb_ringbuffer.h b/drivers/media/dvb-core/dvb_ringbuffer.h index 9e1e11b7c39c..3ebc2d34b4a2 100644 --- a/drivers/media/dvb-core/dvb_ringbuffer.h +++ b/drivers/media/dvb-core/dvb_ringbuffer.h @@ -45,33 +45,33 @@ struct dvb_ringbuffer { /* -** Notes: -** ------ -** (1) For performance reasons read and write routines don't check buffer sizes -** and/or number of bytes free/available. This has to be done before these -** routines are called. For example: -** -** *** write bytes *** -** free = dvb_ringbuffer_free(rbuf); -** if (free >= buflen) -** count = dvb_ringbuffer_write(rbuf, buffer, buflen); -** else -** ... -** -** *** read min. 1000, max. bytes *** -** avail = dvb_ringbuffer_avail(rbuf); -** if (avail >= 1000) -** count = dvb_ringbuffer_read(rbuf, buffer, min(avail, bufsize)); -** else -** ... -** -** (2) If there is exactly one reader and one writer, there is no need -** to lock read or write operations. -** Two or more readers must be locked against each other. -** Flushing the buffer counts as a read operation. -** Resetting the buffer counts as a read and write operation. -** Two or more writers must be locked against each other. -*/ + * Notes: + * ------ + * (1) For performance reasons read and write routines don't check buffer sizes + * and/or number of bytes free/available. This has to be done before these + * routines are called. For example: + * + * *** write @buflen: bytes *** + * free = dvb_ringbuffer_free(rbuf); + * if (free >= buflen) + * count = dvb_ringbuffer_write(rbuf, buffer, buflen); + * else + * ... + * + * *** read min. 1000, max. @bufsize: bytes *** + * avail = dvb_ringbuffer_avail(rbuf); + * if (avail >= 1000) + * count = dvb_ringbuffer_read(rbuf, buffer, min(avail, bufsize)); + * else + * ... + * + * (2) If there is exactly one reader and one writer, there is no need + * to lock read or write operations. + * Two or more readers must be locked against each other. + * Flushing the buffer counts as a read operation. + * Resetting the buffer counts as a read and write operation. + * Two or more writers must be locked against each other. + */ /* initialize ring buffer, lock and queue */ extern void dvb_ringbuffer_init(struct dvb_ringbuffer *rbuf, void *data, size_t len); @@ -87,9 +87,9 @@ extern ssize_t dvb_ringbuffer_avail(struct dvb_ringbuffer *rbuf); /* -** Reset the read and write pointers to zero and flush the buffer -** This counts as a read and write operation -*/ + * Reset the read and write pointers to zero and flush the buffer + * This counts as a read and write operation + */ extern void dvb_ringbuffer_reset(struct dvb_ringbuffer *rbuf); @@ -101,19 +101,19 @@ extern void dvb_ringbuffer_flush(struct dvb_ringbuffer *rbuf); /* flush buffer protected by spinlock and wake-up waiting task(s) */ extern void dvb_ringbuffer_flush_spinlock_wakeup(struct dvb_ringbuffer *rbuf); -/* peek at byte in the buffer */ +/* peek at byte @offs: in the buffer */ #define DVB_RINGBUFFER_PEEK(rbuf,offs) \ (rbuf)->data[((rbuf)->pread+(offs))%(rbuf)->size] -/* advance read ptr by bytes */ +/* advance read ptr by @num: bytes */ #define DVB_RINGBUFFER_SKIP(rbuf,num) \ (rbuf)->pread=((rbuf)->pread+(num))%(rbuf)->size /* -** read bytes from ring buffer into -** specifies whether resides in user space -** returns number of bytes transferred or -EFAULT -*/ + * read @len: bytes from ring buffer into @buf: + * @usermem: specifies whether @buf: resides in user space + * returns number of bytes transferred or -EFAULT + */ extern ssize_t dvb_ringbuffer_read_user(struct dvb_ringbuffer *rbuf, u8 __user *buf, size_t len); extern void dvb_ringbuffer_read(struct dvb_ringbuffer *rbuf, @@ -127,9 +127,9 @@ extern void dvb_ringbuffer_read(struct dvb_ringbuffer *rbuf, { (rbuf)->data[(rbuf)->pwrite]=(byte); \ (rbuf)->pwrite=((rbuf)->pwrite+1)%(rbuf)->size; } /* -** write bytes to ring buffer -** specifies whether resides in user space -** returns number of bytes transferred or -EFAULT + * write @len: bytes to ring buffer + * @usermem: specifies whether @buf: resides in user space + * returns number of bytes transferred or -EFAULT */ extern ssize_t dvb_ringbuffer_write(struct dvb_ringbuffer *rbuf, const u8 *buf, size_t len); @@ -138,48 +138,63 @@ extern ssize_t dvb_ringbuffer_write_user(struct dvb_ringbuffer *rbuf, /** - * Write a packet into the ringbuffer. + * dvb_ringbuffer_pkt_write - Write a packet into the ringbuffer. * - * Ringbuffer to write to. - * Buffer to write. - * Length of buffer (currently limited to 65535 bytes max). + * @rbuf: Ringbuffer to write to. + * @buf: Buffer to write. + * @len: Length of buffer (currently limited to 65535 bytes max). * returns Number of bytes written, or -EFAULT, -ENOMEM, -EVINAL. */ extern ssize_t dvb_ringbuffer_pkt_write(struct dvb_ringbuffer *rbuf, u8* buf, size_t len); /** - * Read from a packet in the ringbuffer. Note: unlike dvb_ringbuffer_read(), this - * does NOT update the read pointer in the ringbuffer. You must use - * dvb_ringbuffer_pkt_dispose() to mark a packet as no longer required. - * - * Ringbuffer concerned. - * Packet index as returned by dvb_ringbuffer_pkt_next(). - * Offset into packet to read from. - * Destination buffer for data. - * Size of destination buffer. - * Set to 1 if is in userspace. + * dvb_ringbuffer_pkt_read_user - Read from a packet in the ringbuffer. + * Note: unlike dvb_ringbuffer_read(), this does NOT update the read pointer + * in the ringbuffer. You must use dvb_ringbuffer_pkt_dispose() to mark a + * packet as no longer required. + * + * @rbuf: Ringbuffer concerned. + * @idx: Packet index as returned by dvb_ringbuffer_pkt_next(). + * @offset: Offset into packet to read from. + * @buf: Destination buffer for data. + * @len: Size of destination buffer. + * * returns Number of bytes read, or -EFAULT. */ extern ssize_t dvb_ringbuffer_pkt_read_user(struct dvb_ringbuffer *rbuf, size_t idx, int offset, u8 __user *buf, size_t len); + +/** + * dvb_ringbuffer_pkt_read - Read from a packet in the ringbuffer. + * Note: unlike dvb_ringbuffer_read_user(), this DOES update the read pointer + * in the ringbuffer. + * + * @rbuf: Ringbuffer concerned. + * @idx: Packet index as returned by dvb_ringbuffer_pkt_next(). + * @offset: Offset into packet to read from. + * @buf: Destination buffer for data. + * @len: Size of destination buffer. + * + * returns Number of bytes read, or -EFAULT. + */ extern ssize_t dvb_ringbuffer_pkt_read(struct dvb_ringbuffer *rbuf, size_t idx, int offset, u8 *buf, size_t len); /** - * Dispose of a packet in the ring buffer. + * dvb_ringbuffer_pkt_dispose - Dispose of a packet in the ring buffer. * - * Ring buffer concerned. - * Packet index as returned by dvb_ringbuffer_pkt_next(). + * @rbuf: Ring buffer concerned. + * @idx: Packet index as returned by dvb_ringbuffer_pkt_next(). */ extern void dvb_ringbuffer_pkt_dispose(struct dvb_ringbuffer *rbuf, size_t idx); /** - * Get the index of the next packet in a ringbuffer. + * dvb_ringbuffer_pkt_next - Get the index of the next packet in a ringbuffer. * - * Ringbuffer concerned. - * Previous packet index, or -1 to return the first packet index. - * On success, will be updated to contain the length of the packet in bytes. + * @rbuf: Ringbuffer concerned. + * @idx: Previous packet index, or -1 to return the first packet index. + * @pktlen: On success, will be updated to contain the length of the packet in bytes. * returns Packet index (if >=0), or -1 if no packets available. */ extern ssize_t dvb_ringbuffer_pkt_next(struct dvb_ringbuffer *rbuf, size_t idx, size_t* pktlen); -- cgit v1.2.3 From 8c2721d57a4bac055ae7bb1874a13a928277d5ff Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 22 Aug 2015 08:03:49 -0300 Subject: [media] v4l2-ctrls.h: add to device-drivers DocBook The comments there are using a wrong format. Due to that, DocBook were unable to parse it. Fix the tags format, and add it to device-drivers.xml. Signed-off-by: Mauro Carvalho Chehab Acked-by: Jonathan Corbet --- Documentation/DocBook/device-drivers.tmpl | 6 +- include/media/v4l2-ctrls.h | 1011 ++++++++++++++++------------- 2 files changed, 552 insertions(+), 465 deletions(-) diff --git a/Documentation/DocBook/device-drivers.tmpl b/Documentation/DocBook/device-drivers.tmpl index 030b3803cc68..5f01f7ad15dc 100644 --- a/Documentation/DocBook/device-drivers.tmpl +++ b/Documentation/DocBook/device-drivers.tmpl @@ -231,17 +231,13 @@ X!Isound/sound_firmware.c !Idrivers/media/dvb-core/dvb_frontend.h !Idrivers/media/dvb-core/dvb_math.h !Idrivers/media/dvb-core/dvb_ringbuffer.h +!Iinclude/media/v4l2-ctrls.h diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index 911f3e542834..88f736661c06 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -36,7 +36,8 @@ struct v4l2_subscribed_event; struct v4l2_fh; struct poll_table_struct; -/** union v4l2_ctrl_ptr - A pointer to a control value. +/** + * union v4l2_ctrl_ptr - A pointer to a control value. * @p_s32: Pointer to a 32-bit signed value. * @p_s64: Pointer to a 64-bit signed value. * @p_u8: Pointer to a 8-bit unsigned value. @@ -55,30 +56,34 @@ union v4l2_ctrl_ptr { void *p; }; -/** struct v4l2_ctrl_ops - The control operations that the driver has to provide. - * @g_volatile_ctrl: Get a new value for this control. Generally only relevant - * for volatile (and usually read-only) controls such as a control - * that returns the current signal strength which changes - * continuously. - * If not set, then the currently cached value will be returned. - * @try_ctrl: Test whether the control's value is valid. Only relevant when - * the usual min/max/step checks are not sufficient. - * @s_ctrl: Actually set the new control value. s_ctrl is compulsory. The - * ctrl->handler->lock is held when these ops are called, so no - * one else can access controls owned by that handler. - */ +/** + * struct v4l2_ctrl_ops - The control operations that the driver has to provide. + * @g_volatile_ctrl: Get a new value for this control. Generally only relevant + * for volatile (and usually read-only) controls such as a control + * that returns the current signal strength which changes + * continuously. + * If not set, then the currently cached value will be returned. + * @try_ctrl: Test whether the control's value is valid. Only relevant when + * the usual min/max/step checks are not sufficient. + * @s_ctrl: Actually set the new control value. s_ctrl is compulsory. The + * ctrl->handler->lock is held when these ops are called, so no + * one else can access controls owned by that handler. + */ struct v4l2_ctrl_ops { int (*g_volatile_ctrl)(struct v4l2_ctrl *ctrl); int (*try_ctrl)(struct v4l2_ctrl *ctrl); int (*s_ctrl)(struct v4l2_ctrl *ctrl); }; -/** struct v4l2_ctrl_type_ops - The control type operations that the driver has to provide. - * @equal: return true if both values are equal. - * @init: initialize the value. - * @log: log the value. - * @validate: validate the value. Return 0 on success and a negative value otherwise. - */ +/** + * struct v4l2_ctrl_type_ops - The control type operations that the driver + * has to provide. + * + * @equal: return true if both values are equal. + * @init: initialize the value. + * @log: log the value. + * @validate: validate the value. Return 0 on success and a negative value otherwise. + */ struct v4l2_ctrl_type_ops { bool (*equal)(const struct v4l2_ctrl *ctrl, u32 idx, union v4l2_ctrl_ptr ptr1, @@ -92,74 +97,75 @@ struct v4l2_ctrl_type_ops { typedef void (*v4l2_ctrl_notify_fnc)(struct v4l2_ctrl *ctrl, void *priv); -/** struct v4l2_ctrl - The control structure. - * @node: The list node. - * @ev_subs: The list of control event subscriptions. - * @handler: The handler that owns the control. - * @cluster: Point to start of cluster array. - * @ncontrols: Number of controls in cluster array. - * @done: Internal flag: set for each processed control. - * @is_new: Set when the user specified a new value for this control. It - * is also set when called from v4l2_ctrl_handler_setup. Drivers - * should never set this flag. - * @has_changed: Set when the current value differs from the new value. Drivers - * should never use this flag. - * @is_private: If set, then this control is private to its handler and it - * will not be added to any other handlers. Drivers can set - * this flag. - * @is_auto: If set, then this control selects whether the other cluster - * members are in 'automatic' mode or 'manual' mode. This is - * used for autogain/gain type clusters. Drivers should never - * set this flag directly. - * @is_int: If set, then this control has a simple integer value (i.e. it - * uses ctrl->val). - * @is_string: If set, then this control has type V4L2_CTRL_TYPE_STRING. - * @is_ptr: If set, then this control is an array and/or has type >= V4L2_CTRL_COMPOUND_TYPES - * and/or has type V4L2_CTRL_TYPE_STRING. In other words, struct - * v4l2_ext_control uses field p to point to the data. - * @is_array: If set, then this control contains an N-dimensional array. - * @has_volatiles: If set, then one or more members of the cluster are volatile. - * Drivers should never touch this flag. - * @call_notify: If set, then call the handler's notify function whenever the - * control's value changes. - * @manual_mode_value: If the is_auto flag is set, then this is the value - * of the auto control that determines if that control is in - * manual mode. So if the value of the auto control equals this - * value, then the whole cluster is in manual mode. Drivers should - * never set this flag directly. - * @ops: The control ops. - * @type_ops: The control type ops. - * @id: The control ID. - * @name: The control name. - * @type: The control type. - * @minimum: The control's minimum value. - * @maximum: The control's maximum value. - * @default_value: The control's default value. - * @step: The control's step value for non-menu controls. - * @elems: The number of elements in the N-dimensional array. - * @elem_size: The size in bytes of the control. - * @dims: The size of each dimension. - * @nr_of_dims:The number of dimensions in @dims. - * @menu_skip_mask: The control's skip mask for menu controls. This makes it - * easy to skip menu items that are not valid. If bit X is set, - * then menu item X is skipped. Of course, this only works for - * menus with <= 32 menu items. There are no menus that come - * close to that number, so this is OK. Should we ever need more, - * then this will have to be extended to a u64 or a bit array. - * @qmenu: A const char * array for all menu items. Array entries that are - * empty strings ("") correspond to non-existing menu items (this - * is in addition to the menu_skip_mask above). The last entry - * must be NULL. - * @flags: The control's flags. - * @cur: The control's current value. - * @val: The control's new s32 value. - * @val64: The control's new s64 value. - * @priv: The control's private pointer. For use by the driver. It is - * untouched by the control framework. Note that this pointer is - * not freed when the control is deleted. Should this be needed - * then a new internal bitfield can be added to tell the framework - * to free this pointer. - */ +/** + * struct v4l2_ctrl - The control structure. + * @node: The list node. + * @ev_subs: The list of control event subscriptions. + * @handler: The handler that owns the control. + * @cluster: Point to start of cluster array. + * @ncontrols: Number of controls in cluster array. + * @done: Internal flag: set for each processed control. + * @is_new: Set when the user specified a new value for this control. It + * is also set when called from v4l2_ctrl_handler_setup. Drivers + * should never set this flag. + * @has_changed: Set when the current value differs from the new value. Drivers + * should never use this flag. + * @is_private: If set, then this control is private to its handler and it + * will not be added to any other handlers. Drivers can set + * this flag. + * @is_auto: If set, then this control selects whether the other cluster + * members are in 'automatic' mode or 'manual' mode. This is + * used for autogain/gain type clusters. Drivers should never + * set this flag directly. + * @is_int: If set, then this control has a simple integer value (i.e. it + * uses ctrl->val). + * @is_string: If set, then this control has type V4L2_CTRL_TYPE_STRING. + * @is_ptr: If set, then this control is an array and/or has type >= V4L2_CTRL_COMPOUND_TYPES + * and/or has type V4L2_CTRL_TYPE_STRING. In other words, struct + * v4l2_ext_control uses field p to point to the data. + * @is_array: If set, then this control contains an N-dimensional array. + * @has_volatiles: If set, then one or more members of the cluster are volatile. + * Drivers should never touch this flag. + * @call_notify: If set, then call the handler's notify function whenever the + * control's value changes. + * @manual_mode_value: If the is_auto flag is set, then this is the value + * of the auto control that determines if that control is in + * manual mode. So if the value of the auto control equals this + * value, then the whole cluster is in manual mode. Drivers should + * never set this flag directly. + * @ops: The control ops. + * @type_ops: The control type ops. + * @id: The control ID. + * @name: The control name. + * @type: The control type. + * @minimum: The control's minimum value. + * @maximum: The control's maximum value. + * @default_value: The control's default value. + * @step: The control's step value for non-menu controls. + * @elems: The number of elements in the N-dimensional array. + * @elem_size: The size in bytes of the control. + * @dims: The size of each dimension. + * @nr_of_dims:The number of dimensions in @dims. + * @menu_skip_mask: The control's skip mask for menu controls. This makes it + * easy to skip menu items that are not valid. If bit X is set, + * then menu item X is skipped. Of course, this only works for + * menus with <= 32 menu items. There are no menus that come + * close to that number, so this is OK. Should we ever need more, + * then this will have to be extended to a u64 or a bit array. + * @qmenu: A const char * array for all menu items. Array entries that are + * empty strings ("") correspond to non-existing menu items (this + * is in addition to the menu_skip_mask above). The last entry + * must be NULL. + * @flags: The control's flags. + * @cur: The control's current value. + * @val: The control's new s32 value. + * @val64: The control's new s64 value. + * @priv: The control's private pointer. For use by the driver. It is + * untouched by the control framework. Note that this pointer is + * not freed when the control is deleted. Should this be needed + * then a new internal bitfield can be added to tell the framework + * to free this pointer. + */ struct v4l2_ctrl { /* Administrative fields */ struct list_head node; @@ -210,16 +216,17 @@ struct v4l2_ctrl { union v4l2_ctrl_ptr p_cur; }; -/** struct v4l2_ctrl_ref - The control reference. - * @node: List node for the sorted list. - * @next: Single-link list node for the hash. - * @ctrl: The actual control information. - * @helper: Pointer to helper struct. Used internally in prepare_ext_ctrls(). - * - * Each control handler has a list of these refs. The list_head is used to - * keep a sorted-by-control-ID list of all controls, while the next pointer - * is used to link the control in the hash's bucket. - */ +/** + * struct v4l2_ctrl_ref - The control reference. + * @node: List node for the sorted list. + * @next: Single-link list node for the hash. + * @ctrl: The actual control information. + * @helper: Pointer to helper struct. Used internally in prepare_ext_ctrls(). + * + * Each control handler has a list of these refs. The list_head is used to + * keep a sorted-by-control-ID list of all controls, while the next pointer + * is used to link the control in the hash's bucket. + */ struct v4l2_ctrl_ref { struct list_head node; struct v4l2_ctrl_ref *next; @@ -227,25 +234,26 @@ struct v4l2_ctrl_ref { struct v4l2_ctrl_helper *helper; }; -/** struct v4l2_ctrl_handler - The control handler keeps track of all the - * controls: both the controls owned by the handler and those inherited - * from other handlers. - * @_lock: Default for "lock". - * @lock: Lock to control access to this handler and its controls. - * May be replaced by the user right after init. - * @ctrls: The list of controls owned by this handler. - * @ctrl_refs: The list of control references. - * @cached: The last found control reference. It is common that the same - * control is needed multiple times, so this is a simple - * optimization. - * @buckets: Buckets for the hashing. Allows for quick control lookup. - * @notify: A notify callback that is called whenever the control changes value. - * Note that the handler's lock is held when the notify function - * is called! - * @notify_priv: Passed as argument to the v4l2_ctrl notify callback. - * @nr_of_buckets: Total number of buckets in the array. - * @error: The error code of the first failed control addition. - */ +/** + * struct v4l2_ctrl_handler - The control handler keeps track of all the + * controls: both the controls owned by the handler and those inherited + * from other handlers. + * @_lock: Default for "lock". + * @lock: Lock to control access to this handler and its controls. + * May be replaced by the user right after init. + * @ctrls: The list of controls owned by this handler. + * @ctrl_refs: The list of control references. + * @cached: The last found control reference. It is common that the same + * control is needed multiple times, so this is a simple + * optimization. + * @buckets: Buckets for the hashing. Allows for quick control lookup. + * @notify: A notify callback that is called whenever the control changes value. + * Note that the handler's lock is held when the notify function + * is called! + * @notify_priv: Passed as argument to the v4l2_ctrl notify callback. + * @nr_of_buckets: Total number of buckets in the array. + * @error: The error code of the first failed control addition. + */ struct v4l2_ctrl_handler { struct mutex _lock; struct mutex *lock; @@ -259,32 +267,33 @@ struct v4l2_ctrl_handler { int error; }; -/** struct v4l2_ctrl_config - Control configuration structure. - * @ops: The control ops. - * @type_ops: The control type ops. Only needed for compound controls. - * @id: The control ID. - * @name: The control name. - * @type: The control type. - * @min: The control's minimum value. - * @max: The control's maximum value. - * @step: The control's step value for non-menu controls. - * @def: The control's default value. - * @dims: The size of each dimension. - * @elem_size: The size in bytes of the control. - * @flags: The control's flags. - * @menu_skip_mask: The control's skip mask for menu controls. This makes it - * easy to skip menu items that are not valid. If bit X is set, - * then menu item X is skipped. Of course, this only works for - * menus with <= 64 menu items. There are no menus that come - * close to that number, so this is OK. Should we ever need more, - * then this will have to be extended to a bit array. - * @qmenu: A const char * array for all menu items. Array entries that are - * empty strings ("") correspond to non-existing menu items (this - * is in addition to the menu_skip_mask above). The last entry - * must be NULL. - * @is_private: If set, then this control is private to its handler and it - * will not be added to any other handlers. - */ +/** + * struct v4l2_ctrl_config - Control configuration structure. + * @ops: The control ops. + * @type_ops: The control type ops. Only needed for compound controls. + * @id: The control ID. + * @name: The control name. + * @type: The control type. + * @min: The control's minimum value. + * @max: The control's maximum value. + * @step: The control's step value for non-menu controls. + * @def: The control's default value. + * @dims: The size of each dimension. + * @elem_size: The size in bytes of the control. + * @flags: The control's flags. + * @menu_skip_mask: The control's skip mask for menu controls. This makes it + * easy to skip menu items that are not valid. If bit X is set, + * then menu item X is skipped. Of course, this only works for + * menus with <= 64 menu items. There are no menus that come + * close to that number, so this is OK. Should we ever need more, + * then this will have to be extended to a bit array. + * @qmenu: A const char * array for all menu items. Array entries that are + * empty strings ("") correspond to non-existing menu items (this + * is in addition to the menu_skip_mask above). The last entry + * must be NULL. + * @is_private: If set, then this control is private to its handler and it + * will not be added to any other handlers. + */ struct v4l2_ctrl_config { const struct v4l2_ctrl_ops *ops; const struct v4l2_ctrl_type_ops *type_ops; @@ -304,42 +313,44 @@ struct v4l2_ctrl_config { unsigned int is_private:1; }; -/** v4l2_ctrl_fill() - Fill in the control fields based on the control ID. - * - * This works for all standard V4L2 controls. - * For non-standard controls it will only fill in the given arguments - * and @name will be NULL. - * - * This function will overwrite the contents of @name, @type and @flags. - * The contents of @min, @max, @step and @def may be modified depending on - * the type. - * - * Do not use in drivers! It is used internally for backwards compatibility - * control handling only. Once all drivers are converted to use the new - * control framework this function will no longer be exported. - */ +/** + * v4l2_ctrl_fill() - Fill in the control fields based on the control ID. + * + * This works for all standard V4L2 controls. + * For non-standard controls it will only fill in the given arguments + * and @name will be NULL. + * + * This function will overwrite the contents of @name, @type and @flags. + * The contents of @min, @max, @step and @def may be modified depending on + * the type. + * + * Do not use in drivers! It is used internally for backwards compatibility + * control handling only. Once all drivers are converted to use the new + * control framework this function will no longer be exported. + */ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, s64 *min, s64 *max, u64 *step, s64 *def, u32 *flags); -/** v4l2_ctrl_handler_init_class() - Initialize the control handler. - * @hdl: The control handler. - * @nr_of_controls_hint: A hint of how many controls this handler is - * expected to refer to. This is the total number, so including - * any inherited controls. It doesn't have to be precise, but if - * it is way off, then you either waste memory (too many buckets - * are allocated) or the control lookup becomes slower (not enough - * buckets are allocated, so there are more slow list lookups). - * It will always work, though. - * @key: Used by the lock validator if CONFIG_LOCKDEP is set. - * @name: Used by the lock validator if CONFIG_LOCKDEP is set. - * - * Returns an error if the buckets could not be allocated. This error will - * also be stored in @hdl->error. - * - * Never use this call directly, always use the v4l2_ctrl_handler_init - * macro that hides the @key and @name arguments. - */ +/** + * v4l2_ctrl_handler_init_class() - Initialize the control handler. + * @hdl: The control handler. + * @nr_of_controls_hint: A hint of how many controls this handler is + * expected to refer to. This is the total number, so including + * any inherited controls. It doesn't have to be precise, but if + * it is way off, then you either waste memory (too many buckets + * are allocated) or the control lookup becomes slower (not enough + * buckets are allocated, so there are more slow list lookups). + * It will always work, though. + * @key: Used by the lock validator if CONFIG_LOCKDEP is set. + * @name: Used by the lock validator if CONFIG_LOCKDEP is set. + * + * Returns an error if the buckets could not be allocated. This error will + * also be stored in @hdl->error. + * + * Never use this call directly, always use the v4l2_ctrl_handler_init + * macro that hides the @key and @name arguments. + */ int v4l2_ctrl_handler_init_class(struct v4l2_ctrl_handler *hdl, unsigned nr_of_controls_hint, struct lock_class_key *key, const char *name); @@ -361,289 +372,326 @@ int v4l2_ctrl_handler_init_class(struct v4l2_ctrl_handler *hdl, v4l2_ctrl_handler_init_class(hdl, nr_of_controls_hint, NULL, NULL) #endif -/** v4l2_ctrl_handler_free() - Free all controls owned by the handler and free - * the control list. - * @hdl: The control handler. - * - * Does nothing if @hdl == NULL. - */ +/** + * v4l2_ctrl_handler_free() - Free all controls owned by the handler and free + * the control list. + * @hdl: The control handler. + * + * Does nothing if @hdl == NULL. + */ void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl); -/** v4l2_ctrl_lock() - Helper function to lock the handler - * associated with the control. - * @ctrl: The control to lock. - */ +/** + * v4l2_ctrl_lock() - Helper function to lock the handler + * associated with the control. + * @ctrl: The control to lock. + */ static inline void v4l2_ctrl_lock(struct v4l2_ctrl *ctrl) { mutex_lock(ctrl->handler->lock); } -/** v4l2_ctrl_unlock() - Helper function to unlock the handler - * associated with the control. - * @ctrl: The control to unlock. - */ +/** + * v4l2_ctrl_unlock() - Helper function to unlock the handler + * associated with the control. + * @ctrl: The control to unlock. + */ static inline void v4l2_ctrl_unlock(struct v4l2_ctrl *ctrl) { mutex_unlock(ctrl->handler->lock); } -/** v4l2_ctrl_handler_setup() - Call the s_ctrl op for all controls belonging - * to the handler to initialize the hardware to the current control values. - * @hdl: The control handler. - * - * Button controls will be skipped, as are read-only controls. - * - * If @hdl == NULL, then this just returns 0. - */ +/** + * v4l2_ctrl_handler_setup() - Call the s_ctrl op for all controls belonging + * to the handler to initialize the hardware to the current control values. + * @hdl: The control handler. + * + * Button controls will be skipped, as are read-only controls. + * + * If @hdl == NULL, then this just returns 0. + */ int v4l2_ctrl_handler_setup(struct v4l2_ctrl_handler *hdl); -/** v4l2_ctrl_handler_log_status() - Log all controls owned by the handler. - * @hdl: The control handler. - * @prefix: The prefix to use when logging the control values. If the - * prefix does not end with a space, then ": " will be added - * after the prefix. If @prefix == NULL, then no prefix will be - * used. - * - * For use with VIDIOC_LOG_STATUS. - * - * Does nothing if @hdl == NULL. - */ +/** + * v4l2_ctrl_handler_log_status() - Log all controls owned by the handler. + * @hdl: The control handler. + * @prefix: The prefix to use when logging the control values. If the + * prefix does not end with a space, then ": " will be added + * after the prefix. If @prefix == NULL, then no prefix will be + * used. + * + * For use with VIDIOC_LOG_STATUS. + * + * Does nothing if @hdl == NULL. + */ void v4l2_ctrl_handler_log_status(struct v4l2_ctrl_handler *hdl, const char *prefix); -/** v4l2_ctrl_new_custom() - Allocate and initialize a new custom V4L2 - * control. - * @hdl: The control handler. - * @cfg: The control's configuration data. - * @priv: The control's driver-specific private data. - * - * If the &v4l2_ctrl struct could not be allocated then NULL is returned - * and @hdl->error is set to the error code (if it wasn't set already). - */ +/** + * v4l2_ctrl_new_custom() - Allocate and initialize a new custom V4L2 + * control. + * @hdl: The control handler. + * @cfg: The control's configuration data. + * @priv: The control's driver-specific private data. + * + * If the &v4l2_ctrl struct could not be allocated then NULL is returned + * and @hdl->error is set to the error code (if it wasn't set already). + */ struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_config *cfg, void *priv); -/** v4l2_ctrl_new_std() - Allocate and initialize a new standard V4L2 non-menu control. - * @hdl: The control handler. - * @ops: The control ops. - * @id: The control ID. - * @min: The control's minimum value. - * @max: The control's maximum value. - * @step: The control's step value - * @def: The control's default value. - * - * If the &v4l2_ctrl struct could not be allocated, or the control - * ID is not known, then NULL is returned and @hdl->error is set to the - * appropriate error code (if it wasn't set already). - * - * If @id refers to a menu control, then this function will return NULL. - * - * Use v4l2_ctrl_new_std_menu() when adding menu controls. - */ +/** + * v4l2_ctrl_new_std() - Allocate and initialize a new standard V4L2 non-menu control. + * @hdl: The control handler. + * @ops: The control ops. + * @id: The control ID. + * @min: The control's minimum value. + * @max: The control's maximum value. + * @step: The control's step value + * @def: The control's default value. + * + * If the &v4l2_ctrl struct could not be allocated, or the control + * ID is not known, then NULL is returned and @hdl->error is set to the + * appropriate error code (if it wasn't set already). + * + * If @id refers to a menu control, then this function will return NULL. + * + * Use v4l2_ctrl_new_std_menu() when adding menu controls. + */ struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_ops *ops, u32 id, s64 min, s64 max, u64 step, s64 def); -/** v4l2_ctrl_new_std_menu() - Allocate and initialize a new standard V4L2 menu control. - * @hdl: The control handler. - * @ops: The control ops. - * @id: The control ID. - * @max: The control's maximum value. - * @mask: The control's skip mask for menu controls. This makes it - * easy to skip menu items that are not valid. If bit X is set, - * then menu item X is skipped. Of course, this only works for - * menus with <= 64 menu items. There are no menus that come - * close to that number, so this is OK. Should we ever need more, - * then this will have to be extended to a bit array. - * @def: The control's default value. - * - * Same as v4l2_ctrl_new_std(), but @min is set to 0 and the @mask value - * determines which menu items are to be skipped. - * - * If @id refers to a non-menu control, then this function will return NULL. - */ +/** + * v4l2_ctrl_new_std_menu() - Allocate and initialize a new standard V4L2 menu control. + * @hdl: The control handler. + * @ops: The control ops. + * @id: The control ID. + * @max: The control's maximum value. + * @mask: The control's skip mask for menu controls. This makes it + * easy to skip menu items that are not valid. If bit X is set, + * then menu item X is skipped. Of course, this only works for + * menus with <= 64 menu items. There are no menus that come + * close to that number, so this is OK. Should we ever need more, + * then this will have to be extended to a bit array. + * @def: The control's default value. + * + * Same as v4l2_ctrl_new_std(), but @min is set to 0 and the @mask value + * determines which menu items are to be skipped. + * + * If @id refers to a non-menu control, then this function will return NULL. + */ struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_ops *ops, u32 id, u8 max, u64 mask, u8 def); -/** v4l2_ctrl_new_std_menu_items() - Create a new standard V4L2 menu control - * with driver specific menu. - * @hdl: The control handler. - * @ops: The control ops. - * @id: The control ID. - * @max: The control's maximum value. - * @mask: The control's skip mask for menu controls. This makes it - * easy to skip menu items that are not valid. If bit X is set, - * then menu item X is skipped. Of course, this only works for - * menus with <= 64 menu items. There are no menus that come - * close to that number, so this is OK. Should we ever need more, - * then this will have to be extended to a bit array. - * @def: The control's default value. - * @qmenu: The new menu. - * - * Same as v4l2_ctrl_new_std_menu(), but @qmenu will be the driver specific - * menu of this control. - * - */ +/** + * v4l2_ctrl_new_std_menu_items() - Create a new standard V4L2 menu control + * with driver specific menu. + * @hdl: The control handler. + * @ops: The control ops. + * @id: The control ID. + * @max: The control's maximum value. + * @mask: The control's skip mask for menu controls. This makes it + * easy to skip menu items that are not valid. If bit X is set, + * then menu item X is skipped. Of course, this only works for + * menus with <= 64 menu items. There are no menus that come + * close to that number, so this is OK. Should we ever need more, + * then this will have to be extended to a bit array. + * @def: The control's default value. + * @qmenu: The new menu. + * + * Same as v4l2_ctrl_new_std_menu(), but @qmenu will be the driver specific + * menu of this control. + * + */ struct v4l2_ctrl *v4l2_ctrl_new_std_menu_items(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_ops *ops, u32 id, u8 max, u64 mask, u8 def, const char * const *qmenu); -/** v4l2_ctrl_new_int_menu() - Create a new standard V4L2 integer menu control. - * @hdl: The control handler. - * @ops: The control ops. - * @id: The control ID. - * @max: The control's maximum value. - * @def: The control's default value. - * @qmenu_int: The control's menu entries. - * - * Same as v4l2_ctrl_new_std_menu(), but @mask is set to 0 and it additionaly - * takes as an argument an array of integers determining the menu items. - * - * If @id refers to a non-integer-menu control, then this function will return NULL. - */ +/** + * v4l2_ctrl_new_int_menu() - Create a new standard V4L2 integer menu control. + * @hdl: The control handler. + * @ops: The control ops. + * @id: The control ID. + * @max: The control's maximum value. + * @def: The control's default value. + * @qmenu_int: The control's menu entries. + * + * Same as v4l2_ctrl_new_std_menu(), but @mask is set to 0 and it additionaly + * takes as an argument an array of integers determining the menu items. + * + * If @id refers to a non-integer-menu control, then this function will return NULL. + */ struct v4l2_ctrl *v4l2_ctrl_new_int_menu(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_ops *ops, u32 id, u8 max, u8 def, const s64 *qmenu_int); -/** v4l2_ctrl_add_ctrl() - Add a control from another handler to this handler. - * @hdl: The control handler. - * @ctrl: The control to add. - * - * It will return NULL if it was unable to add the control reference. - * If the control already belonged to the handler, then it will do - * nothing and just return @ctrl. - */ +/** + * v4l2_ctrl_add_ctrl() - Add a control from another handler to this handler. + * @hdl: The control handler. + * @ctrl: The control to add. + * + * It will return NULL if it was unable to add the control reference. + * If the control already belonged to the handler, then it will do + * nothing and just return @ctrl. + */ struct v4l2_ctrl *v4l2_ctrl_add_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_ctrl *ctrl); -/** v4l2_ctrl_add_handler() - Add all controls from handler @add to - * handler @hdl. - * @hdl: The control handler. - * @add: The control handler whose controls you want to add to - * the @hdl control handler. - * @filter: This function will filter which controls should be added. - * - * Does nothing if either of the two handlers is a NULL pointer. - * If @filter is NULL, then all controls are added. Otherwise only those - * controls for which @filter returns true will be added. - * In case of an error @hdl->error will be set to the error code (if it - * wasn't set already). - */ +/** + * v4l2_ctrl_add_handler() - Add all controls from handler @add to + * handler @hdl. + * @hdl: The control handler. + * @add: The control handler whose controls you want to add to + * the @hdl control handler. + * @filter: This function will filter which controls should be added. + * + * Does nothing if either of the two handlers is a NULL pointer. + * If @filter is NULL, then all controls are added. Otherwise only those + * controls for which @filter returns true will be added. + * In case of an error @hdl->error will be set to the error code (if it + * wasn't set already). + */ int v4l2_ctrl_add_handler(struct v4l2_ctrl_handler *hdl, struct v4l2_ctrl_handler *add, bool (*filter)(const struct v4l2_ctrl *ctrl)); -/** v4l2_ctrl_radio_filter() - Standard filter for radio controls. - * @ctrl: The control that is filtered. - * - * This will return true for any controls that are valid for radio device - * nodes. Those are all of the V4L2_CID_AUDIO_* user controls and all FM - * transmitter class controls. - * - * This function is to be used with v4l2_ctrl_add_handler(). - */ +/** + * v4l2_ctrl_radio_filter() - Standard filter for radio controls. + * @ctrl: The control that is filtered. + * + * This will return true for any controls that are valid for radio device + * nodes. Those are all of the V4L2_CID_AUDIO_* user controls and all FM + * transmitter class controls. + * + * This function is to be used with v4l2_ctrl_add_handler(). + */ bool v4l2_ctrl_radio_filter(const struct v4l2_ctrl *ctrl); -/** v4l2_ctrl_cluster() - Mark all controls in the cluster as belonging to that cluster. - * @ncontrols: The number of controls in this cluster. - * @controls: The cluster control array of size @ncontrols. - */ +/** + * v4l2_ctrl_cluster() - Mark all controls in the cluster as belonging to that cluster. + * @ncontrols: The number of controls in this cluster. + * @controls: The cluster control array of size @ncontrols. + */ void v4l2_ctrl_cluster(unsigned ncontrols, struct v4l2_ctrl **controls); -/** v4l2_ctrl_auto_cluster() - Mark all controls in the cluster as belonging to - * that cluster and set it up for autofoo/foo-type handling. - * @ncontrols: The number of controls in this cluster. - * @controls: The cluster control array of size @ncontrols. The first control - * must be the 'auto' control (e.g. autogain, autoexposure, etc.) - * @manual_val: The value for the first control in the cluster that equals the - * manual setting. - * @set_volatile: If true, then all controls except the first auto control will - * be volatile. - * - * Use for control groups where one control selects some automatic feature and - * the other controls are only active whenever the automatic feature is turned - * off (manual mode). Typical examples: autogain vs gain, auto-whitebalance vs - * red and blue balance, etc. - * - * The behavior of such controls is as follows: - * - * When the autofoo control is set to automatic, then any manual controls - * are set to inactive and any reads will call g_volatile_ctrl (if the control - * was marked volatile). - * - * When the autofoo control is set to manual, then any manual controls will - * be marked active, and any reads will just return the current value without - * going through g_volatile_ctrl. - * - * In addition, this function will set the V4L2_CTRL_FLAG_UPDATE flag - * on the autofoo control and V4L2_CTRL_FLAG_INACTIVE on the foo control(s) - * if autofoo is in auto mode. - */ +/** + * v4l2_ctrl_auto_cluster() - Mark all controls in the cluster as belonging to + * that cluster and set it up for autofoo/foo-type handling. + * @ncontrols: The number of controls in this cluster. + * @controls: The cluster control array of size @ncontrols. The first control + * must be the 'auto' control (e.g. autogain, autoexposure, etc.) + * @manual_val: The value for the first control in the cluster that equals the + * manual setting. + * @set_volatile: If true, then all controls except the first auto control will + * be volatile. + * + * Use for control groups where one control selects some automatic feature and + * the other controls are only active whenever the automatic feature is turned + * off (manual mode). Typical examples: autogain vs gain, auto-whitebalance vs + * red and blue balance, etc. + * + * The behavior of such controls is as follows: + * + * When the autofoo control is set to automatic, then any manual controls + * are set to inactive and any reads will call g_volatile_ctrl (if the control + * was marked volatile). + * + * When the autofoo control is set to manual, then any manual controls will + * be marked active, and any reads will just return the current value without + * going through g_volatile_ctrl. + * + * In addition, this function will set the V4L2_CTRL_FLAG_UPDATE flag + * on the autofoo control and V4L2_CTRL_FLAG_INACTIVE on the foo control(s) + * if autofoo is in auto mode. + */ void v4l2_ctrl_auto_cluster(unsigned ncontrols, struct v4l2_ctrl **controls, u8 manual_val, bool set_volatile); -/** v4l2_ctrl_find() - Find a control with the given ID. - * @hdl: The control handler. - * @id: The control ID to find. - * - * If @hdl == NULL this will return NULL as well. Will lock the handler so - * do not use from inside &v4l2_ctrl_ops. - */ +/** + * v4l2_ctrl_find() - Find a control with the given ID. + * @hdl: The control handler. + * @id: The control ID to find. + * + * If @hdl == NULL this will return NULL as well. Will lock the handler so + * do not use from inside &v4l2_ctrl_ops. + */ struct v4l2_ctrl *v4l2_ctrl_find(struct v4l2_ctrl_handler *hdl, u32 id); -/** v4l2_ctrl_activate() - Make the control active or inactive. - * @ctrl: The control to (de)activate. - * @active: True if the control should become active. - * - * This sets or clears the V4L2_CTRL_FLAG_INACTIVE flag atomically. - * Does nothing if @ctrl == NULL. - * This will usually be called from within the s_ctrl op. - * The V4L2_EVENT_CTRL event will be generated afterwards. - * - * This function assumes that the control handler is locked. - */ +/** + * v4l2_ctrl_activate() - Make the control active or inactive. + * @ctrl: The control to (de)activate. + * @active: True if the control should become active. + * + * This sets or clears the V4L2_CTRL_FLAG_INACTIVE flag atomically. + * Does nothing if @ctrl == NULL. + * This will usually be called from within the s_ctrl op. + * The V4L2_EVENT_CTRL event will be generated afterwards. + * + * This function assumes that the control handler is locked. + */ void v4l2_ctrl_activate(struct v4l2_ctrl *ctrl, bool active); -/** v4l2_ctrl_grab() - Mark the control as grabbed or not grabbed. - * @ctrl: The control to (de)activate. - * @grabbed: True if the control should become grabbed. - * - * This sets or clears the V4L2_CTRL_FLAG_GRABBED flag atomically. - * Does nothing if @ctrl == NULL. - * The V4L2_EVENT_CTRL event will be generated afterwards. - * This will usually be called when starting or stopping streaming in the - * driver. - * - * This function assumes that the control handler is not locked and will - * take the lock itself. - */ +/** + * v4l2_ctrl_grab() - Mark the control as grabbed or not grabbed. + * @ctrl: The control to (de)activate. + * @grabbed: True if the control should become grabbed. + * + * This sets or clears the V4L2_CTRL_FLAG_GRABBED flag atomically. + * Does nothing if @ctrl == NULL. + * The V4L2_EVENT_CTRL event will be generated afterwards. + * This will usually be called when starting or stopping streaming in the + * driver. + * + * This function assumes that the control handler is not locked and will + * take the lock itself. + */ void v4l2_ctrl_grab(struct v4l2_ctrl *ctrl, bool grabbed); -/** __v4l2_ctrl_modify_range() - Unlocked variant of v4l2_ctrl_modify_range() */ +/** + *__v4l2_ctrl_modify_range() - Unlocked variant of v4l2_ctrl_modify_range() + * + * @ctrl: The control to update. + * @min: The control's minimum value. + * @max: The control's maximum value. + * @step: The control's step value + * @def: The control's default value. + * + * Update the range of a control on the fly. This works for control types + * INTEGER, BOOLEAN, MENU, INTEGER MENU and BITMASK. For menu controls the + * @step value is interpreted as a menu_skip_mask. + * + * An error is returned if one of the range arguments is invalid for this + * control type. + * + * This function assumes that the control handler is not locked and will + * take the lock itself. + */ int __v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl, s64 min, s64 max, u64 step, s64 def); -/** v4l2_ctrl_modify_range() - Update the range of a control. - * @ctrl: The control to update. - * @min: The control's minimum value. - * @max: The control's maximum value. - * @step: The control's step value - * @def: The control's default value. - * - * Update the range of a control on the fly. This works for control types - * INTEGER, BOOLEAN, MENU, INTEGER MENU and BITMASK. For menu controls the - * @step value is interpreted as a menu_skip_mask. - * - * An error is returned if one of the range arguments is invalid for this - * control type. - * - * This function assumes that the control handler is not locked and will - * take the lock itself. - */ +/** + * v4l2_ctrl_modify_range() - Update the range of a control. + * @ctrl: The control to update. + * @min: The control's minimum value. + * @max: The control's maximum value. + * @step: The control's step value + * @def: The control's default value. + * + * Update the range of a control on the fly. This works for control types + * INTEGER, BOOLEAN, MENU, INTEGER MENU and BITMASK. For menu controls the + * @step value is interpreted as a menu_skip_mask. + * + * An error is returned if one of the range arguments is invalid for this + * control type. + * + * This function assumes that the control handler is not locked and will + * take the lock itself. + */ static inline int v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl, s64 min, s64 max, u64 step, s64 def) { @@ -656,21 +704,23 @@ static inline int v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl, return rval; } -/** v4l2_ctrl_notify() - Function to set a notify callback for a control. - * @ctrl: The control. - * @notify: The callback function. - * @priv: The callback private handle, passed as argument to the callback. - * - * This function sets a callback function for the control. If @ctrl is NULL, - * then it will do nothing. If @notify is NULL, then the notify callback will - * be removed. - * - * There can be only one notify. If another already exists, then a WARN_ON - * will be issued and the function will do nothing. - */ +/** + * v4l2_ctrl_notify() - Function to set a notify callback for a control. + * @ctrl: The control. + * @notify: The callback function. + * @priv: The callback private handle, passed as argument to the callback. + * + * This function sets a callback function for the control. If @ctrl is NULL, + * then it will do nothing. If @notify is NULL, then the notify callback will + * be removed. + * + * There can be only one notify. If another already exists, then a WARN_ON + * will be issued and the function will do nothing. + */ void v4l2_ctrl_notify(struct v4l2_ctrl *ctrl, v4l2_ctrl_notify_fnc notify, void *priv); -/** v4l2_ctrl_get_name() - Get the name of the control +/** + * v4l2_ctrl_get_name() - Get the name of the control * @id: The control ID. * * This function returns the name of the given control ID or NULL if it isn't @@ -678,7 +728,8 @@ void v4l2_ctrl_notify(struct v4l2_ctrl *ctrl, v4l2_ctrl_notify_fnc notify, void */ const char *v4l2_ctrl_get_name(u32 id); -/** v4l2_ctrl_get_menu() - Get the menu string array of the control +/** + * v4l2_ctrl_get_menu() - Get the menu string array of the control * @id: The control ID. * * This function returns the NULL-terminated menu string array name of the @@ -686,7 +737,8 @@ const char *v4l2_ctrl_get_name(u32 id); */ const char * const *v4l2_ctrl_get_menu(u32 id); -/** v4l2_ctrl_get_int_menu() - Get the integer menu array of the control +/** + * v4l2_ctrl_get_int_menu() - Get the integer menu array of the control * @id: The control ID. * @len: The size of the integer array. * @@ -695,29 +747,41 @@ const char * const *v4l2_ctrl_get_menu(u32 id); */ const s64 *v4l2_ctrl_get_int_menu(u32 id, u32 *len); -/** v4l2_ctrl_g_ctrl() - Helper function to get the control's value from within a driver. - * @ctrl: The control. - * - * This returns the control's value safely by going through the control - * framework. This function will lock the control's handler, so it cannot be - * used from within the &v4l2_ctrl_ops functions. - * - * This function is for integer type controls only. - */ +/** + * v4l2_ctrl_g_ctrl() - Helper function to get the control's value from within a driver. + * @ctrl: The control. + * + * This returns the control's value safely by going through the control + * framework. This function will lock the control's handler, so it cannot be + * used from within the &v4l2_ctrl_ops functions. + * + * This function is for integer type controls only. + */ s32 v4l2_ctrl_g_ctrl(struct v4l2_ctrl *ctrl); -/** __v4l2_ctrl_s_ctrl() - Unlocked variant of v4l2_ctrl_s_ctrl(). */ +/** + * __v4l2_ctrl_s_ctrl() - Unlocked variant of v4l2_ctrl_s_ctrl(). + * @ctrl: The control. + * @val: The new value. + * + * This set the control's new value safely by going through the control + * framework. This function will lock the control's handler, so it cannot be + * used from within the &v4l2_ctrl_ops functions. + * + * This function is for integer type controls only. + */ int __v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val); + /** v4l2_ctrl_s_ctrl() - Helper function to set the control's value from within a driver. - * @ctrl: The control. - * @val: The new value. - * - * This set the control's new value safely by going through the control - * framework. This function will lock the control's handler, so it cannot be - * used from within the &v4l2_ctrl_ops functions. - * - * This function is for integer type controls only. - */ + * @ctrl: The control. + * @val: The new value. + * + * This set the control's new value safely by going through the control + * framework. This function will lock the control's handler, so it cannot be + * used from within the &v4l2_ctrl_ops functions. + * + * This function is for integer type controls only. + */ static inline int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val) { int rval; @@ -729,30 +793,45 @@ static inline int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val) return rval; } -/** v4l2_ctrl_g_ctrl_int64() - Helper function to get a 64-bit control's value from within a driver. - * @ctrl: The control. - * - * This returns the control's value safely by going through the control - * framework. This function will lock the control's handler, so it cannot be - * used from within the &v4l2_ctrl_ops functions. - * - * This function is for 64-bit integer type controls only. - */ +/** + * v4l2_ctrl_g_ctrl_int64() - Helper function to get a 64-bit control's value + * from within a driver. + * @ctrl: The control. + * + * This returns the control's value safely by going through the control + * framework. This function will lock the control's handler, so it cannot be + * used from within the &v4l2_ctrl_ops functions. + * + * This function is for 64-bit integer type controls only. + */ s64 v4l2_ctrl_g_ctrl_int64(struct v4l2_ctrl *ctrl); -/** __v4l2_ctrl_s_ctrl_int64() - Unlocked variant of v4l2_ctrl_s_ctrl_int64(). */ +/** + * __v4l2_ctrl_s_ctrl_int64() - Unlocked variant of v4l2_ctrl_s_ctrl_int64(). + * + * @ctrl: The control. + * @val: The new value. + * + * This set the control's new value safely by going through the control + * framework. This function will lock the control's handler, so it cannot be + * used from within the &v4l2_ctrl_ops functions. + * + * This function is for 64-bit integer type controls only. + */ int __v4l2_ctrl_s_ctrl_int64(struct v4l2_ctrl *ctrl, s64 val); -/** v4l2_ctrl_s_ctrl_int64() - Helper function to set a 64-bit control's value from within a driver. - * @ctrl: The control. - * @val: The new value. - * - * This set the control's new value safely by going through the control - * framework. This function will lock the control's handler, so it cannot be - * used from within the &v4l2_ctrl_ops functions. - * - * This function is for 64-bit integer type controls only. - */ +/** v4l2_ctrl_s_ctrl_int64() - Helper function to set a 64-bit control's value + * from within a driver. + * + * @ctrl: The control. + * @val: The new value. + * + * This set the control's new value safely by going through the control + * framework. This function will lock the control's handler, so it cannot be + * used from within the &v4l2_ctrl_ops functions. + * + * This function is for 64-bit integer type controls only. + */ static inline int v4l2_ctrl_s_ctrl_int64(struct v4l2_ctrl *ctrl, s64 val) { int rval; @@ -764,19 +843,31 @@ static inline int v4l2_ctrl_s_ctrl_int64(struct v4l2_ctrl *ctrl, s64 val) return rval; } -/** __v4l2_ctrl_s_ctrl_string() - Unlocked variant of v4l2_ctrl_s_ctrl_string(). */ +/** __v4l2_ctrl_s_ctrl_string() - Unlocked variant of v4l2_ctrl_s_ctrl_string(). + * + * @ctrl: The control. + * @s: The new string. + * + * This set the control's new string safely by going through the control + * framework. This function will lock the control's handler, so it cannot be + * used from within the &v4l2_ctrl_ops functions. + * + * This function is for string type controls only. + */ int __v4l2_ctrl_s_ctrl_string(struct v4l2_ctrl *ctrl, const char *s); -/** v4l2_ctrl_s_ctrl_string() - Helper function to set a control's string value from within a driver. - * @ctrl: The control. - * @s: The new string. - * - * This set the control's new string safely by going through the control - * framework. This function will lock the control's handler, so it cannot be - * used from within the &v4l2_ctrl_ops functions. - * - * This function is for string type controls only. - */ +/** v4l2_ctrl_s_ctrl_string() - Helper function to set a control's string value + * from within a driver. + * + * @ctrl: The control. + * @s: The new string. + * + * This set the control's new string safely by going through the control + * framework. This function will lock the control's handler, so it cannot be + * used from within the &v4l2_ctrl_ops functions. + * + * This function is for string type controls only. + */ static inline int v4l2_ctrl_s_ctrl_string(struct v4l2_ctrl *ctrl, const char *s) { int rval; -- cgit v1.2.3 From 276ce3a860d3c119e472abcfa75351c703f8722f Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 22 Aug 2015 08:21:10 -0300 Subject: [media] v4l2-ctls: don't document v4l2_ctrl_fill() This is an obsolete function that has several missing arguments: Warning(.//include/media/v4l2-ctrls.h:340): No description found for parameter 'id' Warning(.//include/media/v4l2-ctrls.h:340): No description found for parameter 'name' Warning(.//include/media/v4l2-ctrls.h:340): No description found for parameter 'type' Warning(.//include/media/v4l2-ctrls.h:340): No description found for parameter 'min' Warning(.//include/media/v4l2-ctrls.h:340): No description found for parameter 'max' Warning(.//include/media/v4l2-ctrls.h:340): No description found for parameter 'step' Warning(.//include/media/v4l2-ctrls.h:340): No description found for parameter 'def' Warning(.//include/media/v4l2-ctrls.h:340): No description found for parameter 'flags' However, this is an obsolete function that should be removed soon. And are not meant to be used anymore. So, instead of documenting those stuff, let's just make DocBook to not handle it, by replacing "/**" by "/*". Signed-off-by: Mauro Carvalho Chehab Acked-by: Jonathan Corbet --- include/media/v4l2-ctrls.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index 88f736661c06..946d5d3d6ff7 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -313,7 +313,7 @@ struct v4l2_ctrl_config { unsigned int is_private:1; }; -/** +/* * v4l2_ctrl_fill() - Fill in the control fields based on the control ID. * * This works for all standard V4L2 controls. -- cgit v1.2.3 From 7dc879190f55f4ecb12c754fb0f11a596e478fd1 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 22 Aug 2015 08:22:03 -0300 Subject: [media] v4l2-ctrls.h: Document a few missing arguments Warning(.//include/media/v4l2-ctrls.h:217): No description found for parameter 'p_new' Warning(.//include/media/v4l2-ctrls.h:217): No description found for parameter 'p_cur' Warning(.//include/media/v4l2-ctrls.h:217): Excess struct/union/enum/typedef member 'val64' description in 'v4l2_ctrl' Warning(.//include/media/v4l2-ctrls.h:314): No description found for parameter 'qmenu_int' Signed-off-by: Mauro Carvalho Chehab Acked-by: Jonathan Corbet --- include/media/v4l2-ctrls.h | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index 946d5d3d6ff7..da6fe9802fee 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -159,12 +159,17 @@ typedef void (*v4l2_ctrl_notify_fnc)(struct v4l2_ctrl *ctrl, void *priv); * @flags: The control's flags. * @cur: The control's current value. * @val: The control's new s32 value. - * @val64: The control's new s64 value. * @priv: The control's private pointer. For use by the driver. It is * untouched by the control framework. Note that this pointer is * not freed when the control is deleted. Should this be needed * then a new internal bitfield can be added to tell the framework * to free this pointer. + * @p_cur: The control's current value represented via an union with + * provides a standard way of accessing control types + * through a pointer. + * @p_new: The control's new value represented via an union with provides + * a standard way of accessing control types + * through a pointer. */ struct v4l2_ctrl { /* Administrative fields */ @@ -291,6 +296,8 @@ struct v4l2_ctrl_handler { * empty strings ("") correspond to non-existing menu items (this * is in addition to the menu_skip_mask above). The last entry * must be NULL. + * @qmenu_int: A const s64 integer array for all menu items of the type + * V4L2_CTRL_TYPE_INTEGER_MENU. * @is_private: If set, then this control is private to its handler and it * will not be added to any other handlers. */ -- cgit v1.2.3 From b6fce850d9fa46eefdaa7ef28ac1f3fce7c803d2 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 22 Aug 2015 08:28:39 -0300 Subject: [media] v4l2-event.h: fix comments and add to DocBook The comments there are good enough for DocBook, however they're using a wrong format. Fix and add to device-drivers.xml. Signed-off-by: Mauro Carvalho Chehab Acked-by: Jonathan Corbet --- Documentation/DocBook/device-drivers.tmpl | 2 +- include/media/v4l2-event.h | 47 ++++++++++++++++--------------- 2 files changed, 26 insertions(+), 23 deletions(-) diff --git a/Documentation/DocBook/device-drivers.tmpl b/Documentation/DocBook/device-drivers.tmpl index 5f01f7ad15dc..46e6818b95ce 100644 --- a/Documentation/DocBook/device-drivers.tmpl +++ b/Documentation/DocBook/device-drivers.tmpl @@ -232,9 +232,9 @@ X!Isound/sound_firmware.c !Idrivers/media/dvb-core/dvb_math.h !Idrivers/media/dvb-core/dvb_ringbuffer.h !Iinclude/media/v4l2-ctrls.h +!Iinclude/media/v4l2-event.h diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index 22a44c2f5963..4f7f7aed9157 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -362,7 +362,9 @@ struct v4l2_fh; * start_streaming() can be called. Used when a DMA engine * cannot be started unless at least this number of buffers * have been queued into the driver. - * + */ +/* + * Private elements (won't appear at the DocBook): * @mmap_lock: private mutex used when buffers are allocated/freed/mmapped * @memory: current memory type used * @bufs: videobuf buffer structures @@ -405,7 +407,7 @@ struct vb2_queue { gfp_t gfp_flags; u32 min_buffers_needed; -/* private: internal use only */ + /* private: internal use only */ struct mutex mmap_lock; enum v4l2_memory memory; struct vb2_buffer *bufs[VIDEO_MAX_FRAME]; @@ -482,7 +484,8 @@ size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count, loff_t *ppos, int nonblock); size_t vb2_write(struct vb2_queue *q, const char __user *data, size_t count, loff_t *ppos, int nonblock); -/** + +/* * vb2_thread_fnc - callback function for use with vb2_thread * * This is called whenever a buffer is dequeued in the thread. @@ -575,7 +578,6 @@ static inline void vb2_set_plane_payload(struct vb2_buffer *vb, * vb2_get_plane_payload() - get bytesused for the plane plane_no * @vb: buffer for which plane payload should be set * @plane_no: plane number for which payload should be set - * @size: payload in bytes */ static inline unsigned long vb2_get_plane_payload(struct vb2_buffer *vb, unsigned int plane_no) -- cgit v1.2.3 From b6836a6fbc4b7e35679bf7bb8b54bad294ae0b10 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 22 Aug 2015 09:01:58 -0300 Subject: [media] videobuf2-memops.h: add to device-drivers DocBook The comment metadata was wrong: Warning(.//include/media/videobuf2-memops.h:25): cannot understand function prototype: 'struct vb2_vmarea_handler ' Warning(.//include/media/videobuf2-memops.h): no structured comments found Fix and add to DocBook. Signed-off-by: Mauro Carvalho Chehab Acked-by: Jonathan Corbet --- Documentation/DocBook/device-drivers.tmpl | 2 +- include/media/videobuf2-memops.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Documentation/DocBook/device-drivers.tmpl b/Documentation/DocBook/device-drivers.tmpl index e636bbd41933..8d1e04c94dce 100644 --- a/Documentation/DocBook/device-drivers.tmpl +++ b/Documentation/DocBook/device-drivers.tmpl @@ -235,9 +235,9 @@ X!Isound/sound_firmware.c !Iinclude/media/v4l2-event.h !Iinclude/media/v4l2-dv-timings.h !Iinclude/media/videobuf2-core.h +!Iinclude/media/videobuf2-memops.h diff --git a/include/media/videobuf2-memops.h b/include/media/videobuf2-memops.h index f05444ca8c0c..9f36641a6781 100644 --- a/include/media/videobuf2-memops.h +++ b/include/media/videobuf2-memops.h @@ -17,7 +17,8 @@ #include /** - * vb2_vmarea_handler - common vma refcount tracking handler + * struct vb2_vmarea_handler - common vma refcount tracking handler + * * @refcount: pointer to refcount entry in the buffer * @put: callback to function that decreases buffer refcount * @arg: argument for @put callback -- cgit v1.2.3 From 98d00bd7a2477ffce8010458938425095345f470 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 22 Aug 2015 09:04:46 -0300 Subject: [media] v4l2-mediabus: Add to DocBook Fix the format of the comments and add to DocBook. Signed-off-by: Mauro Carvalho Chehab Acked-by: Jonathan Corbet --- Documentation/DocBook/device-drivers.tmpl | 4 +--- include/media/v4l2-mediabus.h | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/Documentation/DocBook/device-drivers.tmpl b/Documentation/DocBook/device-drivers.tmpl index 8d1e04c94dce..53a7c5d8b996 100644 --- a/Documentation/DocBook/device-drivers.tmpl +++ b/Documentation/DocBook/device-drivers.tmpl @@ -236,9 +236,7 @@ X!Isound/sound_firmware.c !Iinclude/media/v4l2-dv-timings.h !Iinclude/media/videobuf2-core.h !Iinclude/media/videobuf2-memops.h - +!Iinclude/media/v4l2-mediabus.h diff --git a/include/media/v4l2-mediabus.h b/include/media/v4l2-mediabus.h index 73069e4c2796..34cc99e093ef 100644 --- a/include/media/v4l2-mediabus.h +++ b/include/media/v4l2-mediabus.h @@ -65,7 +65,7 @@ V4L2_MBUS_CSI2_CHANNEL_2 | V4L2_MBUS_CSI2_CHANNEL_3) /** - * v4l2_mbus_type - media bus type + * enum v4l2_mbus_type - media bus type * @V4L2_MBUS_PARALLEL: parallel interface with hsync and vsync * @V4L2_MBUS_BT656: parallel interface with embedded synchronisation, can * also be used for BT.1120 @@ -78,7 +78,7 @@ enum v4l2_mbus_type { }; /** - * v4l2_mbus_config - media bus configuration + * struct v4l2_mbus_config - media bus configuration * @type: in: interface type * @flags: in / out: configuration flags, depending on @type */ -- cgit v1.2.3 From 04ffb9c126d6d887b08b68bbe0c0842ec9def78d Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 22 Aug 2015 09:22:25 -0300 Subject: [media] DocBook: Better organize media devices Instead of putting all media devices on a flat structure, split them on 4 types, just like we do with the userspace API: Video2Linux devices Digital TV (DVB) devices Remote Controller devices Media Controller devices Signed-off-by: Mauro Carvalho Chehab Acked-by: Jonathan Corbet --- Documentation/DocBook/device-drivers.tmpl | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/Documentation/DocBook/device-drivers.tmpl b/Documentation/DocBook/device-drivers.tmpl index 53a7c5d8b996..5c9375b98303 100644 --- a/Documentation/DocBook/device-drivers.tmpl +++ b/Documentation/DocBook/device-drivers.tmpl @@ -218,25 +218,34 @@ X!Isound/sound_firmware.c Media Devices -!Iinclude/media/media-device.h -!Iinclude/media/media-devnode.h -!Iinclude/media/media-entity.h + + Video2Linux devices !Iinclude/media/v4l2-async.h +!Iinclude/media/v4l2-ctrls.h +!Iinclude/media/v4l2-dv-timings.h +!Iinclude/media/v4l2-event.h !Iinclude/media/v4l2-flash-led-class.h +!Iinclude/media/v4l2-mediabus.h !Iinclude/media/v4l2-mem2mem.h !Iinclude/media/v4l2-of.h !Iinclude/media/v4l2-subdev.h -!Iinclude/media/rc-core.h +!Iinclude/media/videobuf2-core.h +!Iinclude/media/videobuf2-memops.h + + Digital TV (DVB) devices !Idrivers/media/dvb-core/dvb_ca_en50221.h !Idrivers/media/dvb-core/dvb_frontend.h !Idrivers/media/dvb-core/dvb_math.h !Idrivers/media/dvb-core/dvb_ringbuffer.h -!Iinclude/media/v4l2-ctrls.h -!Iinclude/media/v4l2-event.h -!Iinclude/media/v4l2-dv-timings.h -!Iinclude/media/videobuf2-core.h -!Iinclude/media/videobuf2-memops.h -!Iinclude/media/v4l2-mediabus.h + + Remote Controller devices +!Iinclude/media/rc-core.h + + Media Controller devices +!Iinclude/media/media-device.h +!Iinclude/media/media-devnode.h +!Iinclude/media/media-entity.h + -- cgit v1.2.3 From b9de1726b52d0c1dc07d90fd6a63fca49ea8b5b9 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 22 Aug 2015 09:47:04 -0300 Subject: [media] dvb_frontend: document dvb_frontend_tune_settings Add Documentation for dvb_frontend_tune_settings struct. Signed-off-by: Mauro Carvalho Chehab Acked-by: Jonathan Corbet --- drivers/media/dvb-core/dvb_frontend.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/media/dvb-core/dvb_frontend.h b/drivers/media/dvb-core/dvb_frontend.h index d20d3da97201..ea68ba61f0de 100644 --- a/drivers/media/dvb-core/dvb_frontend.h +++ b/drivers/media/dvb-core/dvb_frontend.h @@ -48,6 +48,15 @@ */ #define MAX_DELSYS 8 +/** + * struct dvb_frontend_tune_settings - parameters to adjust frontend tuning + * + * @min_delay_ms: minimum delay for tuning, in ms + * @step_size: step size between two consecutive frequencies + * @max_drift: maximum drift + * + * NOTE: step_size is in Hz, for terrestrial/cable or kHz for satellite + */ struct dvb_frontend_tune_settings { int min_delay_ms; int step_size; -- cgit v1.2.3 From 8bcbc2f32e3388c6b899fd8c662be471ef609b2a Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 22 Aug 2015 09:48:05 -0300 Subject: [media] add documentation for struct dvb_tuner_info Despite being used everywhere at DVB frontends, the struct dvb_tuner_info were never documented. Signed-off-by: Mauro Carvalho Chehab Acked-by: Jonathan Corbet --- drivers/media/dvb-core/dvb_frontend.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/media/dvb-core/dvb_frontend.h b/drivers/media/dvb-core/dvb_frontend.h index ea68ba61f0de..09c3d23c2213 100644 --- a/drivers/media/dvb-core/dvb_frontend.h +++ b/drivers/media/dvb-core/dvb_frontend.h @@ -65,6 +65,20 @@ struct dvb_frontend_tune_settings { struct dvb_frontend; +/** + * struct dvb_tuner_info - Frontend name and min/max ranges/bandwidths + * + * @name: name of the Frontend + * @frequency_min: minimal frequency supported + * @frequency_max: maximum frequency supported + * @frequency_step: frequency step + * @bandwidth_min: minimal frontend bandwidth supported + * @bandwidth_max: maximum frontend bandwidth supported + * @bandwidth_step: frontend bandwidth step + * + * NOTE: frequency parameters are in Hz, for terrestrial/cable or kHz for + * satellite. + */ struct dvb_tuner_info { char name[128]; -- cgit v1.2.3 From 0c68b1ce850592fb5812e425cba6ab92951955a4 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 22 Aug 2015 09:48:57 -0300 Subject: [media] dvb_frontend.h: get rid of dvbfe_modcod This enum is not used anymore, as drivers use the modulation definitions from the public API. It is probably a left over from some DVB core cleanup. Signed-off-by: Mauro Carvalho Chehab Acked-by: Jonathan Corbet --- drivers/media/dvb-core/dvb_frontend.h | 36 ----------------------------------- 1 file changed, 36 deletions(-) diff --git a/drivers/media/dvb-core/dvb_frontend.h b/drivers/media/dvb-core/dvb_frontend.h index 09c3d23c2213..1ffa9a746fae 100644 --- a/drivers/media/dvb-core/dvb_frontend.h +++ b/drivers/media/dvb-core/dvb_frontend.h @@ -98,42 +98,6 @@ struct analog_parameters { u64 std; }; -enum dvbfe_modcod { - DVBFE_MODCOD_DUMMY_PLFRAME = 0, - DVBFE_MODCOD_QPSK_1_4, - DVBFE_MODCOD_QPSK_1_3, - DVBFE_MODCOD_QPSK_2_5, - DVBFE_MODCOD_QPSK_1_2, - DVBFE_MODCOD_QPSK_3_5, - DVBFE_MODCOD_QPSK_2_3, - DVBFE_MODCOD_QPSK_3_4, - DVBFE_MODCOD_QPSK_4_5, - DVBFE_MODCOD_QPSK_5_6, - DVBFE_MODCOD_QPSK_8_9, - DVBFE_MODCOD_QPSK_9_10, - DVBFE_MODCOD_8PSK_3_5, - DVBFE_MODCOD_8PSK_2_3, - DVBFE_MODCOD_8PSK_3_4, - DVBFE_MODCOD_8PSK_5_6, - DVBFE_MODCOD_8PSK_8_9, - DVBFE_MODCOD_8PSK_9_10, - DVBFE_MODCOD_16APSK_2_3, - DVBFE_MODCOD_16APSK_3_4, - DVBFE_MODCOD_16APSK_4_5, - DVBFE_MODCOD_16APSK_5_6, - DVBFE_MODCOD_16APSK_8_9, - DVBFE_MODCOD_16APSK_9_10, - DVBFE_MODCOD_32APSK_3_4, - DVBFE_MODCOD_32APSK_4_5, - DVBFE_MODCOD_32APSK_5_6, - DVBFE_MODCOD_32APSK_8_9, - DVBFE_MODCOD_32APSK_9_10, - DVBFE_MODCOD_RESERVED_1, - DVBFE_MODCOD_BPSK_1_3, - DVBFE_MODCOD_BPSK_1_4, - DVBFE_MODCOD_RESERVED_2 -}; - enum tuner_param { DVBFE_TUNER_FREQUENCY = (1 << 0), DVBFE_TUNER_TUNERSTEP = (1 << 1), -- cgit v1.2.3 From 1a77763428c7647e74347c1c5760440fe0cd0aea Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 22 Aug 2015 09:58:26 -0300 Subject: [media] Docbook: Document struct analog_parameters That struct inside dvb-frontend.h stores some parameters from V4L2 API (videodev2.h), in order to be used by the hybrid analog/digital TV tuners. Document it. Signed-off-by: Mauro Carvalho Chehab Acked-by: Jonathan Corbet --- drivers/media/dvb-core/dvb_frontend.h | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/drivers/media/dvb-core/dvb_frontend.h b/drivers/media/dvb-core/dvb_frontend.h index 1ffa9a746fae..8fc8d3a98382 100644 --- a/drivers/media/dvb-core/dvb_frontend.h +++ b/drivers/media/dvb-core/dvb_frontend.h @@ -91,6 +91,20 @@ struct dvb_tuner_info { u32 bandwidth_step; }; +/** + * struct analog_parameters - Parameters to tune into an analog/radio channel + * + * @frequency: Frequency used by analog TV tuner (either in 62.5 kHz step, + * for TV, or 62.5 Hz for radio) + * @mode: Tuner mode, as defined on enum v4l2_tuner_type + * @audmode: Audio mode as defined for the rxsubchans field at videodev2.h, + * e. g. V4L2_TUNER_MODE_* + * @std: TV standard bitmap as defined at videodev2.h, e. g. V4L2_STD_* + * + * Hybrid tuners should be supported by both V4L2 and DVB APIs. This + * struct contains the data that are used by the V4L2 side. To avoid + * dependencies from V4L2 headers, all enums here are declared as integers. + */ struct analog_parameters { unsigned int frequency; unsigned int mode; @@ -111,16 +125,16 @@ enum tuner_param { /** * enum dvbfe_algo - defines the algorithm used to tune into a channel * - * @DVBFE_ALGO_HW: (Hardware Algorithm) + * @DVBFE_ALGO_HW: Hardware Algorithm - * Devices that support this algorithm do everything in hardware * and no software support is needed to handle them. * Requesting these devices to LOCK is the only thing required, * device is supposed to do everything in the hardware. * - * @DVBFE_ALGO_SW: (Software Algorithm) + * @DVBFE_ALGO_SW: Software Algorithm - * These are dumb devices, that require software to do everything * - * @DVBFE_ALGO_CUSTOM: (Customizable Agorithm) + * @DVBFE_ALGO_CUSTOM: Customizable Agorithm - * Devices having this algorithm can be customized to have specific * algorithms in the frontend driver, rather than simply doing a * software zig-zag. In this case the zigzag maybe hardware assisted @@ -128,7 +142,7 @@ enum tuner_param { * this algorithm, in conjunction with the search and track * callbacks, utilizes the driver specific algorithm. * - * @DVBFE_ALGO_RECOVERY: (Recovery Algorithm) + * @DVBFE_ALGO_RECOVERY: Recovery Algorithm - * These devices have AUTO recovery capabilities from LOCK failure */ enum dvbfe_algo { -- cgit v1.2.3 From 465291a0eba9662a2ec597b96881dd92b034c30c Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 22 Aug 2015 11:13:37 -0300 Subject: [media] dvb_frontend.h: Document struct dvb_tuner_ops The struct dvb_tuner_ops contains lots of callbacks used by tuners. Document them. Signed-off-by: Mauro Carvalho Chehab Acked-by: Jonathan Corbet --- drivers/media/dvb-core/dvb_frontend.h | 48 ++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/drivers/media/dvb-core/dvb_frontend.h b/drivers/media/dvb-core/dvb_frontend.h index 8fc8d3a98382..991a6e0265d2 100644 --- a/drivers/media/dvb-core/dvb_frontend.h +++ b/drivers/media/dvb-core/dvb_frontend.h @@ -192,7 +192,53 @@ enum dvbfe_search { DVBFE_ALGO_SEARCH_ERROR = (1 << 31), }; - +/** + * struct dvb_tuner_ops - Tuner information and callbacks + * + * @info: embedded struct dvb_tuner_info with tuner properties + * @release: callback function called when frontend is dettached. + * drivers should free any allocated memory. + * @init: callback function used to initialize the tuner device. + * @sleep: callback function used to put the tuner to sleep. + * @suspend: callback function used to inform that the Kernel will + * suspend. + * @resume: callback function used to inform that the Kernel is + * resuming from suspend. + * @set_params: callback function used to inform the tuner to tune + * into a digital TV channel. The properties to be used + * are stored at @dvb_frontend.dtv_property_cache;. + * @set_analog_params: callback function used to tune into an analog TV + * channel on hybrid tuners. It passes @analog_parameters; + * to the driver. + * @calc_regs: callback function used to pass register data settings + * for simple tuners. + * @set_config: callback function used to send some tuner-specific + * parameters. + * @get_frequency: get the actual tuned frequency + * @get_bandwidth: get the bandwitdh used by the low pass filters + * @get_if_frequency: get the Intermediate Frequency, in Hz. For baseband, + * should return 0. + * @get_status: returns the frontend lock status + * @get_rf_strength: returns the RF signal strengh. Used mostly to support + * analog TV and radio. Digital TV should report, instead, + * via DVBv5 API (@dvb_frontend.dtv_property_cache;). + * @get_afc: Used only by analog TV core. Reports the frequency + * drift due to AFC. + * @set_frequency: Set a new frequency. Please notice that using + * set_params is preferred. + * @set_bandwidth: Set a new frequency. Please notice that using + * set_params is preferred. + * @set_state: callback function used on some legacy drivers that + * don't implement set_params in order to set properties. + * Shouldn't be used on new drivers. + * @get_state: callback function used to get properties by some + * legacy drivers that don't implement set_params. + * Shouldn't be used on new drivers. + * + * NOTE: frequencies used on get_frequency and set_frequency are in Hz for + * terrestrial/cable or kHz for satellite. + * + */ struct dvb_tuner_ops { struct dvb_tuner_info info; -- cgit v1.2.3 From 0bf6cd7bc5d6751386d5f092e8d3fd2537aac954 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 22 Aug 2015 11:35:50 -0300 Subject: [media] dvb_frontend.h: document struct analog_demod_ops Add documentation for struct analog_demod_info and struct analog_demod_ops. Signed-off-by: Mauro Carvalho Chehab Acked-by: Jonathan Corbet --- drivers/media/dvb-core/dvb_frontend.h | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/drivers/media/dvb-core/dvb_frontend.h b/drivers/media/dvb-core/dvb_frontend.h index 991a6e0265d2..c6c85e6e9874 100644 --- a/drivers/media/dvb-core/dvb_frontend.h +++ b/drivers/media/dvb-core/dvb_frontend.h @@ -282,10 +282,37 @@ struct dvb_tuner_ops { int (*get_state)(struct dvb_frontend *fe, enum tuner_param param, struct tuner_state *state); }; +/** + * struct analog_demod_info - Information struct for analog TV part of the demod + * + * @name: Name of the analog TV demodulator + */ struct analog_demod_info { char *name; }; +/** + * struct analog_demod_ops - Demodulation information and callbacks for + * analog TV and radio + * + * @info: pointer to struct analog_demod_info + * @set_params: callback function used to inform the demod to set the + * demodulator parameters needed to decode an analog or + * radio channel. The properties are passed via + * struct @analog_params;. + * @has_signal: returns 0xffff if has signal, or 0 if it doesn't. + * @get_afc: Used only by analog TV core. Reports the frequency + * drift due to AFC. + * @tuner_status: callback function that returns tuner status bits, e. g. + * TUNER_STATUS_LOCKED and TUNER_STATUS_STEREO. + * @standby: set the tuner to standby mode. + * @release: callback function called when frontend is dettached. + * drivers should free any allocated memory. + * @i2c_gate_ctrl: controls the I2C gate. Newer drivers should use I2C + * mux support instead. + * @set_config: callback function used to send some tuner-specific + * parameters. + */ struct analog_demod_ops { struct analog_demod_info info; -- cgit v1.2.3 From 27460adc07a3f84e671dec71ac553818d5988003 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 22 Aug 2015 12:48:09 -0300 Subject: [media] dvb: Use DVBFE_ALGO_HW where applicable The dvb_frontend.c core defines a FE_ALGO_HW symbol that it is never used. Also, both cx24123 returns 1 to get_algo() callback instead of using DVBFE_ALGO_HW. Probably, those are some left overs from some code cleanup. Let's stop returning magic numbers and use the proper macro value. Signed-off-by: Mauro Carvalho Chehab Acked-by: Jonathan Corbet --- drivers/media/dvb-core/dvb_frontend.c | 1 - drivers/media/dvb-frontends/cx24123.c | 2 +- drivers/media/dvb-frontends/s921.c | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/media/dvb-core/dvb_frontend.c b/drivers/media/dvb-core/dvb_frontend.c index 842b9c8f80c6..c38ef1a72b4a 100644 --- a/drivers/media/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb-core/dvb_frontend.c @@ -81,7 +81,6 @@ MODULE_PARM_DESC(dvb_mfe_wait_time, "Wait up to seconds on open( #define FESTATE_SEARCHING_SLOW (FESTATE_TUNING_SLOW | FESTATE_ZIGZAG_SLOW) #define FESTATE_LOSTLOCK (FESTATE_ZIGZAG_FAST | FESTATE_ZIGZAG_SLOW) -#define FE_ALGO_HW 1 /* * FESTATE_IDLE. No tuning parameters have been supplied and the loop is idling. * FESTATE_RETUNE. Parameters have been supplied, but we have not yet performed the first tune. diff --git a/drivers/media/dvb-frontends/cx24123.c b/drivers/media/dvb-frontends/cx24123.c index e18cf9e1185e..0fe7fb11124b 100644 --- a/drivers/media/dvb-frontends/cx24123.c +++ b/drivers/media/dvb-frontends/cx24123.c @@ -1011,7 +1011,7 @@ static int cx24123_tune(struct dvb_frontend *fe, static int cx24123_get_algo(struct dvb_frontend *fe) { - return 1; /* FE_ALGO_HW */ + return DVBFE_ALGO_HW; } static void cx24123_release(struct dvb_frontend *fe) diff --git a/drivers/media/dvb-frontends/s921.c b/drivers/media/dvb-frontends/s921.c index b2d9fe13e1a0..d6a8fa63040b 100644 --- a/drivers/media/dvb-frontends/s921.c +++ b/drivers/media/dvb-frontends/s921.c @@ -466,7 +466,7 @@ static int s921_tune(struct dvb_frontend *fe, static int s921_get_algo(struct dvb_frontend *fe) { - return 1; /* FE_ALGO_HW */ + return DVBFE_ALGO_HW; } static void s921_release(struct dvb_frontend *fe) -- cgit v1.2.3 From 0cfcc493a50ef6d8b2ceda3390be0306f319ed18 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 22 Aug 2015 13:28:02 -0300 Subject: [media] dvb-frontend.h: document struct dvb_frontend_ops This is one of the most important functions of the DVB frontend, containing the logic needed to set the parameters at the demux and to send commands via SEC. Document it. Signed-off-by: Mauro Carvalho Chehab Acked-by: Jonathan Corbet --- drivers/media/dvb-core/dvb_frontend.h | 86 ++++++++++++++++++++++++++++++++++- 1 file changed, 85 insertions(+), 1 deletion(-) diff --git a/drivers/media/dvb-core/dvb_frontend.h b/drivers/media/dvb-core/dvb_frontend.h index c6c85e6e9874..115995958da6 100644 --- a/drivers/media/dvb-core/dvb_frontend.h +++ b/drivers/media/dvb-core/dvb_frontend.h @@ -206,7 +206,10 @@ enum dvbfe_search { * resuming from suspend. * @set_params: callback function used to inform the tuner to tune * into a digital TV channel. The properties to be used - * are stored at @dvb_frontend.dtv_property_cache;. + * are stored at @dvb_frontend.dtv_property_cache;. The + * tuner demod can change the parameters to reflect the + * changes needed for the channel to be tuned, and + * update statistics. * @set_analog_params: callback function used to tune into an analog TV * channel on hybrid tuners. It passes @analog_parameters; * to the driver. @@ -332,6 +335,86 @@ struct analog_demod_ops { struct dtv_frontend_properties; + +/** + * struct dvb_frontend_ops + * + * @info: embedded struct dvb_tuner_info with tuner properties + * @delsys: Delivery systems supported by the frontend + * @release: callback function called when frontend is dettached. + * drivers should free any allocated memory. + * @release_sec: callback function requesting that the Satelite Equipment + * Control (SEC) driver to release and free any memory + * allocated by the driver. + * @init: callback function used to initialize the tuner device. + * @sleep: callback function used to put the tuner to sleep. + * @write: callback function used by some demod legacy drivers to + * allow other drivers to write data into their registers. + * Should not be used on new drivers. + * @tune: callback function used by demod drivers that use + * @DVBFE_ALGO_HW; to tune into a frequency. + * @get_frontend_algo: returns the desired hardware algorithm. + * @set_frontend: callback function used to inform the demod to set the + * parameters for demodulating a digital TV channel. + * The properties to be used are stored at + * @dvb_frontend.dtv_property_cache;. The demod can change + * the parameters to reflect the changes needed for the + * channel to be decoded, and update statistics. + * @get_tune_settings: callback function + * @get_frontend: callback function used to inform the parameters + * actuall in use. The properties to be used are stored at + * @dvb_frontend.dtv_property_cache; and update + * statistics. Please notice that it should not return + * an error code if the statistics are not available + * because the demog is not locked. + * @read_status: returns the locking status of the frontend. + * @read_ber: legacy callback function to return the bit error rate. + * Newer drivers should provide such info via DVBv5 API, + * e. g. @set_frontend;/@get_frontend;, implementing this + * callback only if DVBv3 API compatibility is wanted. + * @read_signal_strength: legacy callback function to return the signal + * strength. Newer drivers should provide such info via + * DVBv5 API, e. g. @set_frontend;/@get_frontend;, + * implementing this callback only if DVBv3 API + * compatibility is wanted. + * @read_snr: legacy callback function to return the Signal/Noise + * rate. Newer drivers should provide such info via + * DVBv5 API, e. g. @set_frontend;/@get_frontend;, + * implementing this callback only if DVBv3 API + * compatibility is wanted. + * @read_ucblocks: legacy callback function to return the Uncorrected Error + * Blocks. Newer drivers should provide such info via + * DVBv5 API, e. g. @set_frontend;/@get_frontend;, + * implementing this callback only if DVBv3 API + * compatibility is wanted. + * @diseqc_reset_overload: callback function to implement the + * FE_DISEQC_RESET_OVERLOAD ioctl. + * @diseqc_send_master_cmd: callback function to implement the + * FE_DISEQC_SEND_MASTER_CMD ioctl. + * @diseqc_recv_slave_reply: callback function to implement the + * FE_DISEQC_RECV_SLAVE_REPLY ioctl. + * @diseqc_send_burst: callback function to implement the + * FE_DISEQC_SEND_BURST ioctl. + * @set_tone: callback function to implement the + * FE_SET_TONE ioctl. + * @set_voltage: callback function to implement the + * FE_SET_VOLTAGE ioctl. + * @enable_high_lnb_voltage: callback function to implement the + * FE_ENABLE_HIGH_LNB_VOLTAGE ioctl. + * @dishnetwork_send_legacy_command: callback function to implement the + * FE_DISHNETWORK_SEND_LEGACY_CMD ioctl. + * @i2c_gate_ctrl: controls the I2C gate. Newer drivers should use I2C + * mux support instead. + * @ts_bus_ctrl: callback function used to take control of the TS bus. + * @set_lna: callback function to power on/off/auto the LNA. + * @search: callback function used on some custom algo search algos. + * @tuner_ops: pointer to struct dvb_tuner_ops + * @analog_ops: pointer to struct analog_demod_ops + * @set_property: callback function to allow the frontend to validade + * incoming properties. Should not be used on new drivers. + * @get_property: callback function to allow the frontend to override + * outcoming properties. Should not be used on new drivers. + */ struct dvb_frontend_ops { struct dvb_frontend_info info; @@ -352,6 +435,7 @@ struct dvb_frontend_ops { unsigned int mode_flags, unsigned int *delay, enum fe_status *status); + /* get frontend tuning algorithm from the module */ enum dvbfe_algo (*get_frontend_algo)(struct dvb_frontend *fe); -- cgit v1.2.3 From a3cccb2411a634b751771596bf21afc2d2bd5990 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 22 Aug 2015 14:10:31 -0300 Subject: [media] dvb-frontend.h: document struct dtv_frontend_properties Most of the parameters here are already well defined at the userspace documentation. Yet, it is good to add some documentation, for the developers to be sure that they are the same as the ones at userspace API. Signed-off-by: Mauro Carvalho Chehab Acked-by: Jonathan Corbet --- drivers/media/dvb-core/dvb_frontend.h | 105 +++++++++++++++++++++++++++++----- 1 file changed, 91 insertions(+), 14 deletions(-) diff --git a/drivers/media/dvb-core/dvb_frontend.h b/drivers/media/dvb-core/dvb_frontend.h index 115995958da6..796cc5dca819 100644 --- a/drivers/media/dvb-core/dvb_frontend.h +++ b/drivers/media/dvb-core/dvb_frontend.h @@ -337,7 +337,8 @@ struct dtv_frontend_properties; /** - * struct dvb_frontend_ops + * struct dvb_frontend_ops - Demodulation information and callbacks for + * ditialt TV * * @info: embedded struct dvb_tuner_info with tuner properties * @delsys: Delivery systems supported by the frontend @@ -388,21 +389,21 @@ struct dtv_frontend_properties; * implementing this callback only if DVBv3 API * compatibility is wanted. * @diseqc_reset_overload: callback function to implement the - * FE_DISEQC_RESET_OVERLOAD ioctl. + * FE_DISEQC_RESET_OVERLOAD ioctl (only Satellite) * @diseqc_send_master_cmd: callback function to implement the - * FE_DISEQC_SEND_MASTER_CMD ioctl. + * FE_DISEQC_SEND_MASTER_CMD ioctl (only Satellite). * @diseqc_recv_slave_reply: callback function to implement the - * FE_DISEQC_RECV_SLAVE_REPLY ioctl. + * FE_DISEQC_RECV_SLAVE_REPLY ioctl (only Satellite) * @diseqc_send_burst: callback function to implement the - * FE_DISEQC_SEND_BURST ioctl. + * FE_DISEQC_SEND_BURST ioctl (only Satellite). * @set_tone: callback function to implement the - * FE_SET_TONE ioctl. + * FE_SET_TONE ioctl (only Satellite). * @set_voltage: callback function to implement the - * FE_SET_VOLTAGE ioctl. + * FE_SET_VOLTAGE ioctl (only Satellite). * @enable_high_lnb_voltage: callback function to implement the - * FE_ENABLE_HIGH_LNB_VOLTAGE ioctl. + * FE_ENABLE_HIGH_LNB_VOLTAGE ioctl (only Satellite). * @dishnetwork_send_legacy_command: callback function to implement the - * FE_DISHNETWORK_SEND_LEGACY_CMD ioctl. + * FE_DISHNETWORK_SEND_LEGACY_CMD ioctl (only Satellite). * @i2c_gate_ctrl: controls the I2C gate. Newer drivers should use I2C * mux support instead. * @ts_bus_ctrl: callback function used to take control of the TS bus. @@ -480,6 +481,7 @@ struct dvb_frontend_ops { #ifdef __DVB_CORE__ #define MAX_EVENT 8 +/* Used only internally at dvb_frontend.c */ struct dvb_fe_events { struct dvb_frontend_event events[MAX_EVENT]; int eventw; @@ -490,13 +492,83 @@ struct dvb_fe_events { }; #endif +/** + * struct dtv_frontend_properties - contains a list of properties that are + * specific to a digital TV standard. + * + * @frequency: frequency in Hz for terrestrial/cable or in kHz for + * Satellite + * @modulation: Frontend modulation type + * @voltage: SEC voltage (only Satellite) + * @sectone: SEC tone mode (only Satellite) + * @inversion: Spectral inversion + * @fec_inner: Forward error correction inner Code Rate + * @transmission_mode: Transmission Mode + * @bandwidth_hz: Bandwidth, in Hz. A zero value means that userspace + * wants to autodetect. + * @guard_interval: Guard Interval + * @hierarchy: Hierarchy + * @symbol_rate: Symbol Rate + * @code_rate_HP: high priority stream code rate + * @code_rate_LP: low priority stream code rate + * @pilot: Enable/disable/autodetect pilot tones + * @rolloff: Rolloff factor (alpha) + * @delivery_system: FE delivery system (e. g. digital TV standard) + * @interleaving: interleaving + * @isdbt_partial_reception: ISDB-T partial reception (only ISDB standard) + * @isdbt_sb_mode: ISDB-T Sound Broadcast (SB) mode (only ISDB standard) + * @isdbt_sb_subchannel: ISDB-T SB subchannel (only ISDB standard) + * @isdbt_sb_segment_idx: ISDB-T SB segment index (only ISDB standard) + * @isdbt_sb_segment_count: ISDB-T SB segment count (only ISDB standard) + * @isdbt_layer_enabled: ISDB Layer enabled (only ISDB standard) + * @layer: ISDB per-layer data (only ISDB standard) + * @layer.segment_count: Segment Count; + * @layer.fec: per layer code rate; + * @layer.modulation: per layer modulation; + * @layer.interleaving: per layer interleaving. + * @stream_id: If different than zero, enable substream filtering, if + * hardware supports (DVB-S2 and DVB-T2). + * @atscmh_fic_ver: Version number of the FIC (Fast Information Channel) + * signaling data (only ATSC-M/H) + * @atscmh_parade_id: Parade identification number (only ATSC-M/H) + * @atscmh_nog: Number of MH groups per MH subframe for a designated + * parade (only ATSC-M/H) + * @atscmh_tnog: Total number of MH groups including all MH groups + * belonging to all MH parades in one MH subframe + * (only ATSC-M/H) + * @atscmh_sgn: Start group number (only ATSC-M/H) + * @atscmh_prc: Parade repetition cycle (only ATSC-M/H) + * @atscmh_rs_frame_mode: Reed Solomon (RS) frame mode (only ATSC-M/H) + * @atscmh_rs_frame_ensemble: RS frame ensemble (only ATSC-M/H) + * @atscmh_rs_code_mode_pri: RS code mode pri (only ATSC-M/H) + * @atscmh_rs_code_mode_sec: RS code mode sec (only ATSC-M/H) + * @atscmh_sccc_block_mode: Series Concatenated Convolutional Code (SCCC) + * Block Mode (only ATSC-M/H) + * @atscmh_sccc_code_mode_a: SCCC code mode A (only ATSC-M/H) + * @atscmh_sccc_code_mode_b: SCCC code mode B (only ATSC-M/H) + * @atscmh_sccc_code_mode_c: SCCC code mode C (only ATSC-M/H) + * @atscmh_sccc_code_mode_d: SCCC code mode D (only ATSC-M/H) + * @lna: Power ON/OFF/AUTO the Linear Now-noise Amplifier (LNA) + * @strength: DVBv5 API statistics: Signal Strength + * @cnr: DVBv5 API statistics: Signal to Noise ratio of the + * (main) carrier + * @pre_bit_error: DVBv5 API statistics: pre-Viterbi bit error count + * @pre_bit_count: DVBv5 API statistics: pre-Viterbi bit count + * @post_bit_error: DVBv5 API statistics: post-Viterbi bit error count + * @post_bit_count: DVBv5 API statistics: post-Viterbi bit count + * @block_error: DVBv5 API statistics: block error count + * @block_count: DVBv5 API statistics: block count + * + * NOTE: derivated statistics like Uncorrected Error blocks (UCE) are + * calculated on userspace. + * + * Only a subset of the properties are needed for a given delivery system. + * For more info, consult the media_api.html with the documentation of the + * Userspace API. + */ struct dtv_frontend_properties { - - /* Cache State */ - u32 state; - u32 frequency; - enum fe_modulation modulation; + enum fe_modulation modulation; enum fe_sec_voltage voltage; enum fe_sec_tone_mode sectone; @@ -563,6 +635,11 @@ struct dtv_frontend_properties { struct dtv_fe_stats post_bit_count; struct dtv_fe_stats block_error; struct dtv_fe_stats block_count; + + /* private: */ + /* Cache State */ + u32 state; + }; #define DVB_FE_NO_EXIT 0 -- cgit v1.2.3 From 35848bf0aa6ba677458adfbde49ef73d10c9f381 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 22 Aug 2015 14:20:25 -0300 Subject: [media] dvb_frontend.h: document the struct dvb_frontend That struct is used on every DVB Front End driver, as it contains what's needed to register/use a frontend at the Kernel. Document it. Signed-off-by: Mauro Carvalho Chehab Acked-by: Jonathan Corbet --- drivers/media/dvb-core/dvb_frontend.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/media/dvb-core/dvb_frontend.h b/drivers/media/dvb-core/dvb_frontend.h index 796cc5dca819..97661b2f247a 100644 --- a/drivers/media/dvb-core/dvb_frontend.h +++ b/drivers/media/dvb-core/dvb_frontend.h @@ -647,6 +647,25 @@ struct dtv_frontend_properties { #define DVB_FE_DEVICE_REMOVED 2 #define DVB_FE_DEVICE_RESUME 3 +/** + * struct dvb_frontend - Frontend structure to be used on drivers. + * + * @ops: embedded struct dvb_frontend_ops + * @dvb: pointer to struct dvb_adapter + * @demodulator_priv: demod private data + * @tuner_priv: tuner private data + * @frontend_priv: frontend private data + * @sec_priv: SEC private data + * @analog_demod_priv: Analog demod private data + * @dtv_property_cache: embedded struct dtv_frontend_properties + * @callback: callback function used on some drivers to call + * either the tuner or the demodulator. + * @id: Frontend ID + * @exit: Used to inform the DVB core that the frontend + * thread should exit (usually, means that the hardware + * got disconnected. + */ + struct dvb_frontend { struct dvb_frontend_ops ops; struct dvb_adapter *dvb; -- cgit v1.2.3 From d071c833a0d30e7aae0ea565d92ef83c79106d6f Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 22 Aug 2015 19:39:38 -0300 Subject: [media] dvbdev: document most of the functions/data structs Document the most relevant functions and data structs for developers that are working with the DVB subsystem. Signed-off-by: Mauro Carvalho Chehab Acked-by: Jonathan Corbet --- Documentation/DocBook/device-drivers.tmpl | 1 + drivers/media/dvb-core/dvbdev.h | 116 +++++++++++++++++++++++++----- 2 files changed, 100 insertions(+), 17 deletions(-) diff --git a/Documentation/DocBook/device-drivers.tmpl b/Documentation/DocBook/device-drivers.tmpl index 5c9375b98303..93b74884ae24 100644 --- a/Documentation/DocBook/device-drivers.tmpl +++ b/Documentation/DocBook/device-drivers.tmpl @@ -237,6 +237,7 @@ X!Isound/sound_firmware.c !Idrivers/media/dvb-core/dvb_frontend.h !Idrivers/media/dvb-core/dvb_math.h !Idrivers/media/dvb-core/dvb_ringbuffer.h +!Idrivers/media/dvb-core/dvbdev.h Remote Controller devices !Iinclude/media/rc-core.h diff --git a/drivers/media/dvb-core/dvbdev.h b/drivers/media/dvb-core/dvbdev.h index 12629b8ecb0c..c61a4f03a66f 100644 --- a/drivers/media/dvb-core/dvbdev.h +++ b/drivers/media/dvb-core/dvbdev.h @@ -57,6 +57,25 @@ struct dvb_frontend; +/** + * struct dvb_adapter - represents a Digital TV adapter using Linux DVB API + * + * @num: Number of the adapter + * @list_head: List with the DVB adapters + * @device_list: List with the DVB devices + * @name: Name of the adapter + * @proposed_mac: proposed MAC address for the adapter + * @priv: private data + * @device: pointer to struct device + * @module: pointer to struct module + * @mfe_shared: mfe shared: indicates mutually exclusive frontends + * Thie usage of this flag is currently deprecated + * @mfe_dvbdev: Frontend device in use, in the case of MFE + * @mfe_lock: Lock to prevent using the other frontends when MFE is + * used. + * @mdev: pointer to struct media_device, used when the media + * controller is used. + */ struct dvb_adapter { int num; struct list_head list_head; @@ -78,7 +97,34 @@ struct dvb_adapter { #endif }; - +/** + * struct dvb_device - represents a DVB device node + * + * @list_head: List head with all DVB devices + * @fops: pointer to struct file_operations + * @adapter: pointer to the adapter that holds this device node + * @type: type of the device: DVB_DEVICE_SEC, DVB_DEVICE_FRONTEND, + * DVB_DEVICE_DEMUX, DVB_DEVICE_DVR, DVB_DEVICE_CA, DVB_DEVICE_NET + * @minor: devnode minor number. Major number is always DVB_MAJOR. + * @id: device ID number, inside the adapter + * @readers: Initialized by the caller. Each call to open() in Read Only mode + * decreases this counter by one. + * @writers: Initialized by the caller. Each call to open() in Read/Write + * mode decreases this counter by one. + * @users: Initialized by the caller. Each call to open() in any mode + * decreases this counter by one. + * @wait_queue: wait queue, used to wait for certain events inside one of + * the DVB API callers + * @kernel_ioctl: callback function used to handle ioctl calls from userspace. + * @name: Name to be used for the device at the Media Controller + * @entity: pointer to struct media_entity associated with the device node + * @pads: pointer to struct media_pad associated with @entity; + * @priv: private data + * + * This structure is used by the DVB core (frontend, CA, net, demux) in + * order to create the device nodes. Usually, driver should not initialize + * this struct diretly. + */ struct dvb_device { struct list_head list_head; const struct file_operations *fops; @@ -109,19 +155,55 @@ struct dvb_device { void *priv; }; +/** + * dvb_register_adapter - Registers a new DVB adapter + * + * @adap: pointer to struct dvb_adapter + * @name: Adapter's name + * @module: initialized with THIS_MODULE at the caller + * @device: pointer to struct device that corresponds to the device driver + * @adapter_nums: Array with a list of the numbers for @dvb_register_adapter; + * to select among them. Typically, initialized with: + * DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nums) + */ +int dvb_register_adapter(struct dvb_adapter *adap, const char *name, + struct module *module, struct device *device, + short *adapter_nums); -extern int dvb_register_adapter(struct dvb_adapter *adap, const char *name, - struct module *module, struct device *device, - short *adapter_nums); -extern int dvb_unregister_adapter (struct dvb_adapter *adap); - -extern int dvb_register_device (struct dvb_adapter *adap, - struct dvb_device **pdvbdev, - const struct dvb_device *template, - void *priv, - int type); +/** + * dvb_unregister_adapter - Unregisters a DVB adapter + * + * @adap: pointer to struct dvb_adapter + */ +int dvb_unregister_adapter(struct dvb_adapter *adap); -extern void dvb_unregister_device (struct dvb_device *dvbdev); +/** + * dvb_register_device - Registers a new DVB device + * + * @adap: pointer to struct dvb_adapter + * @pdvbdev: pointer to the place where the new struct dvb_device will be + * stored + * @template: Template used to create &pdvbdev; + * @device: pointer to struct device that corresponds to the device driver + * @adapter_nums: Array with a list of the numbers for @dvb_register_adapter; + * to select among them. Typically, initialized with: + * DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nums) + * @priv: private data + * @type: type of the device: DVB_DEVICE_SEC, DVB_DEVICE_FRONTEND, + * DVB_DEVICE_DEMUX, DVB_DEVICE_DVR, DVB_DEVICE_CA, DVB_DEVICE_NET + */ +int dvb_register_device(struct dvb_adapter *adap, + struct dvb_device **pdvbdev, + const struct dvb_device *template, + void *priv, + int type); + +/** + * dvb_unregister_device - Unregisters a DVB device + * + * @dvbdev: pointer to struct dvb_device + */ +void dvb_unregister_device(struct dvb_device *dvbdev); #ifdef CONFIG_MEDIA_CONTROLLER_DVB void dvb_create_media_graph(struct dvb_adapter *adap); @@ -136,17 +218,17 @@ static inline void dvb_create_media_graph(struct dvb_adapter *adap) {} #define dvb_register_media_controller(a, b) {} #endif -extern int dvb_generic_open (struct inode *inode, struct file *file); -extern int dvb_generic_release (struct inode *inode, struct file *file); -extern long dvb_generic_ioctl (struct file *file, +int dvb_generic_open (struct inode *inode, struct file *file); +int dvb_generic_release (struct inode *inode, struct file *file); +long dvb_generic_ioctl (struct file *file, unsigned int cmd, unsigned long arg); /* we don't mess with video_usercopy() any more, we simply define out own dvb_usercopy(), which will hopefully become generic_usercopy() someday... */ -extern int dvb_usercopy(struct file *file, unsigned int cmd, unsigned long arg, - int (*func)(struct file *file, unsigned int cmd, void *arg)); +int dvb_usercopy(struct file *file, unsigned int cmd, unsigned long arg, + int (*func)(struct file *file, unsigned int cmd, void *arg)); /** generic DVB attach function. */ #ifdef CONFIG_MEDIA_ATTACH -- cgit v1.2.3 From 06d3f2e02024912d46e1fc8387c0284c9dfc36ad Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 18 Aug 2015 05:31:09 -0300 Subject: [media] tc358743: set direction of reset gpio using devm_gpiod_get MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 256148246852 ("[media] tc358743: support probe from device tree") failed to explicitly set the direction of the reset gpio. Use the optional flag of devm_gpiod_get to make up leeway. This is also necessary because the flag parameter will become mandatory soon. Signed-off-by: Uwe Kleine-König Reviewed-by: Linus Walleij Acked-by: Philipp Zabel Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/tc358743.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index fe42c9a1cb78..a4efb6e2e7a2 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -1681,7 +1681,6 @@ static const struct v4l2_ctrl_config tc358743_ctrl_audio_present = { #ifdef CONFIG_OF static void tc358743_gpio_reset(struct tc358743_state *state) { - gpiod_set_value(state->reset_gpio, 0); usleep_range(5000, 10000); gpiod_set_value(state->reset_gpio, 1); usleep_range(1000, 2000); @@ -1783,7 +1782,7 @@ static int tc358743_probe_of(struct tc358743_state *state) state->pdata.ths_trailcnt = 0x2; state->pdata.hstxvregcnt = 0; - state->reset_gpio = devm_gpiod_get(dev, "reset"); + state->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(state->reset_gpio)) { dev_err(dev, "failed to get reset gpio\n"); ret = PTR_ERR(state->reset_gpio); -- cgit v1.2.3 From 1e137d92c0c6f162d57ede1ba85c298acde26bdc Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 18 Aug 2015 05:31:10 -0300 Subject: [media] tc358743: make reset gpio optional MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 256148246852 ("[media] tc358743: support probe from device tree") specified in the device tree binding documentation that the reset gpio is optional. Make the implementation match accordingly. Signed-off-by: Uwe Kleine-König Reviewed-by: Linus Walleij Acked-by: Philipp Zabel Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/tc358743.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index a4efb6e2e7a2..7a6fdecc7215 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -1782,14 +1782,16 @@ static int tc358743_probe_of(struct tc358743_state *state) state->pdata.ths_trailcnt = 0x2; state->pdata.hstxvregcnt = 0; - state->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + state->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_LOW); if (IS_ERR(state->reset_gpio)) { dev_err(dev, "failed to get reset gpio\n"); ret = PTR_ERR(state->reset_gpio); goto disable_clk; } - tc358743_gpio_reset(state); + if (state->reset_gpio) + tc358743_gpio_reset(state); ret = 0; goto free_endpoint; -- cgit v1.2.3 From 062683901ad5c29ac375e6b7c7bca2737d41e11a Mon Sep 17 00:00:00 2001 From: Masanari Iida Date: Mon, 17 Aug 2015 08:20:56 -0300 Subject: [media] DocBook media: Fix typo "the the" in xml files This patch fix spelling typo "the the" found in controls.xml and vidioc-g-param.xml. These xml files are'nt generated from any source files, so I have to fix these xml files directly. Signed-off-by: Masanari Iida Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/controls.xml | 2 +- Documentation/DocBook/media/v4l/vidioc-g-parm.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/DocBook/media/v4l/controls.xml b/Documentation/DocBook/media/v4l/controls.xml index 6e1667b5f3eb..33aece541880 100644 --- a/Documentation/DocBook/media/v4l/controls.xml +++ b/Documentation/DocBook/media/v4l/controls.xml @@ -3414,7 +3414,7 @@ giving priority to the center of the metered area. V4L2_EXPOSURE_METERING_MATRIX  A multi-zone metering. The light intensity is measured -in several points of the frame and the the results are combined. The +in several points of the frame and the results are combined. The algorithm of the zones selection and their significance in calculating the final value is device dependent. diff --git a/Documentation/DocBook/media/v4l/vidioc-g-parm.xml b/Documentation/DocBook/media/v4l/vidioc-g-parm.xml index f4e28e7d4751..721728745407 100644 --- a/Documentation/DocBook/media/v4l/vidioc-g-parm.xml +++ b/Documentation/DocBook/media/v4l/vidioc-g-parm.xml @@ -267,7 +267,7 @@ is intended for still imaging applications. The idea is to get the best possible image quality that the hardware can deliver. It is not defined how the driver writer may achieve that; it will depend on the hardware and the ingenuity of the driver writer. High quality mode is -a different mode from the the regular motion video capture modes. In +a different mode from the regular motion video capture modes. In high quality mode: The driver may be able to capture higher -- cgit v1.2.3 From 62e5f051c110a4f8de66f4c33e690892089aaac4 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 18 Aug 2015 10:23:05 -0300 Subject: [media] c8sectpfe: Use %pad to print 'dma_addr_t' Use %pad to print 'dma_addr_t' in order to fix the following build warning: drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c:588:2: warning: format '%x' expects argument of type 'unsigned int', but argument 5 has type 'dma_addr_t' [-Wformat=] Reported-by: Olof's autobuilder Signed-off-by: Fabio Estevam Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c index 1586a1e6836d..486aef50d99b 100644 --- a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c +++ b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c @@ -585,9 +585,9 @@ static int configure_memdma_and_inputblock(struct c8sectpfei *fei, writel(tsin->pid_buffer_busaddr, fei->io + PIDF_BASE(tsin->tsin_id)); - dev_info(fei->dev, "chan=%d PIDF_BASE=0x%x pid_bus_addr=0x%x\n", + dev_info(fei->dev, "chan=%d PIDF_BASE=0x%x pid_bus_addr=%pad\n", tsin->tsin_id, readl(fei->io + PIDF_BASE(tsin->tsin_id)), - tsin->pid_buffer_busaddr); + &tsin->pid_buffer_busaddr); /* Configure and enable HW PID filtering */ -- cgit v1.2.3 From 22dbe35a3aa2c06cef9faa472c46e8decbcd64e9 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 19 Aug 2015 03:27:27 -0300 Subject: [media] tc358743: add missing Kconfig dependency/select As reported by Randy: > when CONFIG_MEDIA_CONTROLLER is not enabled: > > ../drivers/media/i2c/tc358743.c: In function 'tc358743_probe': > ../drivers/media/i2c/tc358743.c:1890:29: error: 'struct v4l2_subdev' has no member named 'entity' > err = media_entity_init(&sd->entity, 1, &state->pad, 0); > ^ > ../drivers/media/i2c/tc358743.c:1940:26: error: 'struct v4l2_subdev' has no member named 'entity' > media_entity_cleanup(&sd->entity); > ^ > ../drivers/media/i2c/tc358743.c: In function 'tc358743_remove': > ../drivers/media/i2c/tc358743.c:1955:26: error: 'struct v4l2_subdev' has no member named 'entity' > media_entity_cleanup(&sd->entity); > ^ This driver depends on VIDEO_V4L2_SUBDEV_API and needs to select HDMI. Signed-off-by: Hans Verkuil Reported-by: Randy Dunlap Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index d669547604cd..521bbf1b29bc 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -289,7 +289,8 @@ config VIDEO_SAA711X config VIDEO_TC358743 tristate "Toshiba TC358743 decoder" - depends on VIDEO_V4L2 && I2C + depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API + select HDMI ---help--- Support for the Toshiba TC358743 HDMI to MIPI CSI-2 bridge. -- cgit v1.2.3 From abeaca0ff5666a1cd7460304e619e4c6a486ea04 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 20 Aug 2015 04:35:43 -0300 Subject: [media] tc358743: only queue subdev notifications if devnode is set Hardware interrupts are enabled in the probe function, before the subdev is registered to its v4l2_device. Until v4l2_device_register_subdev_node is called, sd->devnode is NULL and v4l2_subdev_notify_event must not be called. Signed-off-by: Philipp Zabel Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/tc358743.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index 7a6fdecc7215..526d30745a19 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -870,7 +870,8 @@ static void tc358743_format_change(struct v4l2_subdev *sd) &timings, false); } - v4l2_subdev_notify_event(sd, &tc358743_ev_fmt); + if (sd->devnode) + v4l2_subdev_notify_event(sd, &tc358743_ev_fmt); } static void tc358743_init_interrupts(struct v4l2_subdev *sd) -- cgit v1.2.3 From 543409a2475de00d336468eee5bba8409092db16 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Thu, 20 Aug 2015 18:03:43 -0300 Subject: [media] cx231xx: Use wake_up_interruptible() instead of wake_up_interruptible_nr() While looking at use cases of the wake queues in order to add support for simple wait queues, I noticed that there was only a single user of wake_up_interruptible_nr(), and that use was doing a single task wake up. Have that user use the proper wake_up_interruptible() instead, and perhaps we can even remove the function wake_up_interruptible_nr(). Signed-off-by: Steven Rostedt Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/cx231xx/cx231xx-video.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/usb/cx231xx/cx231xx-video.c b/drivers/media/usb/cx231xx/cx231xx-video.c index c6ff8968286a..9798160698a3 100644 --- a/drivers/media/usb/cx231xx/cx231xx-video.c +++ b/drivers/media/usb/cx231xx/cx231xx-video.c @@ -1875,7 +1875,7 @@ static int cx231xx_close(struct file *filp) v4l2_fh_exit(&fh->fh); kfree(fh); dev->users--; - wake_up_interruptible_nr(&dev->open, 1); + wake_up_interruptible(&dev->open); return 0; } @@ -1908,7 +1908,7 @@ static int cx231xx_close(struct file *filp) } v4l2_fh_exit(&fh->fh); kfree(fh); - wake_up_interruptible_nr(&dev->open, 1); + wake_up_interruptible(&dev->open); return 0; } -- cgit v1.2.3 From cfd34ed8deede7cbb0ba46d61870f075ac9694e3 Mon Sep 17 00:00:00 2001 From: Fengguang Wu Date: Fri, 21 Aug 2015 17:12:20 -0300 Subject: [media] i2c: fix platform_no_drv_owner.cocci warnings drivers/media/i2c/tc358743.c:1960:3-8: No need to set .owner here. The core will do it. Remove .owner field if calls are used which set it automatically Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci CC: Geert Uytterhoeven Signed-off-by: Fengguang Wu Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/tc358743.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index 526d30745a19..9ef5baaf8646 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -1969,7 +1969,6 @@ MODULE_DEVICE_TABLE(i2c, tc358743_id); static struct i2c_driver tc358743_driver = { .driver = { - .owner = THIS_MODULE, .name = "tc358743", }, .probe = tc358743_probe, -- cgit v1.2.3 From 50ef28a6ac216fd8b796257a3768fef8f57b917d Mon Sep 17 00:00:00 2001 From: Peter Griffin Date: Tue, 1 Sep 2015 07:48:11 -0300 Subject: [media] c8sectpfe: Remove select on undefined LIBELF_32 LIBELF_32 is not defined in Kconfig, and is left over legacy which is not required in the upstream driver, so remove it. Suggested-by: Valentin Rothberg Signed-off-by: Peter Griffin Acked-by: Lee Jones Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sti/c8sectpfe/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/media/platform/sti/c8sectpfe/Kconfig b/drivers/media/platform/sti/c8sectpfe/Kconfig index 1b1110d7374f..641ad8f34956 100644 --- a/drivers/media/platform/sti/c8sectpfe/Kconfig +++ b/drivers/media/platform/sti/c8sectpfe/Kconfig @@ -2,7 +2,6 @@ config DVB_C8SECTPFE tristate "STMicroelectronics C8SECTPFE DVB support" depends on PINCTRL && DVB_CORE && I2C depends on ARCH_STI || ARCH_MULTIPLATFORM || COMPILE_TEST - select LIBELF_32 select FW_LOADER select FW_LOADER_USER_HELPER_FALLBACK select DEBUG_FS -- cgit v1.2.3