From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S933121AbcCNCDL (ORCPT ); Sun, 13 Mar 2016 22:03:11 -0400 Received: from new2-smtp.messagingengine.com ([66.111.4.224]:49789 "EHLO new2-smtp.messagingengine.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932747AbcCNCDD (ORCPT ); Sun, 13 Mar 2016 22:03:03 -0400 X-Sasl-enc: 4BuON5bxz2ExE60c+JDY2mwRz8wmmeTbbka7eBU5Vbuz 1457920538 From: Andrey Utkin To: Mauro Carvalho Chehab , Greg Kroah-Hartman , Bluecherry Maintainers , Bjorn Helgaas , Andrew Morton , "David S. Miller" , Kalle Valo , Joe Perches , Jiri Slaby Cc: linux-kernel@vger.kernel.org, linux-media@vger.kernel.org, devel@driverdev.osuosl.org, linux-pci@vger.kernel.org, kernel-mentors@selenic.com, Andrey Utkin Subject: [PATCH] Add tw5864 driver Date: Mon, 14 Mar 2016 03:55:14 +0200 Message-Id: <1457920514-20792-1-git-send-email-andrey_utkin@fastmail.com> X-Mailer: git-send-email 2.7.1.380.g0fea050.dirty In-Reply-To: <1457920461-20713-1-git-send-email-andrey_utkin@fastmail.com> References: <1457920461-20713-1-git-send-email-andrey_utkin@fastmail.com> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Andrey Utkin Support for boards based on Techwell TW5864 chip which provides multichannel video & audio grabbing and encoding (H.264, MJPEG, ADPCM G.726). Signed-off-by: Andrey Utkin Tested-by: Andrey Utkin --- MAINTAINERS | 7 + drivers/staging/media/Kconfig | 2 + drivers/staging/media/Makefile | 1 + drivers/staging/media/tw5864/Kconfig | 11 + drivers/staging/media/tw5864/Makefile | 3 + drivers/staging/media/tw5864/tw5864-bs.h | 154 ++ drivers/staging/media/tw5864/tw5864-config.c | 359 +++++ drivers/staging/media/tw5864/tw5864-core.c | 453 ++++++ drivers/staging/media/tw5864/tw5864-h264.c | 183 +++ drivers/staging/media/tw5864/tw5864-reg.h | 2200 ++++++++++++++++++++++++++ drivers/staging/media/tw5864/tw5864-tables.h | 237 +++ drivers/staging/media/tw5864/tw5864-video.c | 1364 ++++++++++++++++ drivers/staging/media/tw5864/tw5864.h | 280 ++++ include/linux/pci_ids.h | 1 + 14 files changed, 5255 insertions(+) create mode 100644 drivers/staging/media/tw5864/Kconfig create mode 100644 drivers/staging/media/tw5864/Makefile create mode 100644 drivers/staging/media/tw5864/tw5864-bs.h create mode 100644 drivers/staging/media/tw5864/tw5864-config.c create mode 100644 drivers/staging/media/tw5864/tw5864-core.c create mode 100644 drivers/staging/media/tw5864/tw5864-h264.c create mode 100644 drivers/staging/media/tw5864/tw5864-reg.h create mode 100644 drivers/staging/media/tw5864/tw5864-tables.h create mode 100644 drivers/staging/media/tw5864/tw5864-video.c create mode 100644 drivers/staging/media/tw5864/tw5864.h diff --git a/MAINTAINERS b/MAINTAINERS index 409509d..7bb1fa9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11195,6 +11195,13 @@ T: git git://linuxtv.org/media_tree.git S: Odd fixes F: drivers/media/usb/tm6000/ +TW5864 VIDEO4LINUX DRIVER +M: Bluecherry Maintainers +M: Andrey Utkin +L: linux-media@vger.kernel.org +S: Supported +F: drivers/staging/media/tw5864/ + TW68 VIDEO4LINUX DRIVER M: Hans Verkuil L: linux-media@vger.kernel.org diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index 0078b6a..15ac585 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -37,6 +37,8 @@ source "drivers/staging/media/omap4iss/Kconfig" source "drivers/staging/media/timb/Kconfig" +source "drivers/staging/media/tw5864/Kconfig" + # Keep LIRC at the end, as it has sub-menus source "drivers/staging/media/lirc/Kconfig" diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index 9149588..2f356ef 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -8,3 +8,4 @@ obj-$(CONFIG_VIDEO_OMAP1) += omap1/ obj-$(CONFIG_VIDEO_OMAP4) += omap4iss/ obj-$(CONFIG_DVB_MN88472) += mn88472/ obj-$(CONFIG_VIDEO_TIMBERDALE) += timb/ +obj-$(CONFIG_VIDEO_TW5864) += tw5864/ diff --git a/drivers/staging/media/tw5864/Kconfig b/drivers/staging/media/tw5864/Kconfig new file mode 100644 index 0000000..5d6871a --- /dev/null +++ b/drivers/staging/media/tw5864/Kconfig @@ -0,0 +1,11 @@ +config VIDEO_TW5864 + tristate "Techwell TW5864 video/audio grabber and encoder" + depends on VIDEO_DEV && PCI && VIDEO_V4L2 + select VIDEOBUF2_DMA_SG + ---help--- + Support for boards based on Techwell TW5864 chip which provides + multichannel video & audio grabbing and encoding (H.264, MJPEG, + ADPCM G.726). + + To compile this driver as a module, choose M here: the + module will be called tw5864. diff --git a/drivers/staging/media/tw5864/Makefile b/drivers/staging/media/tw5864/Makefile new file mode 100644 index 0000000..1da77d4 --- /dev/null +++ b/drivers/staging/media/tw5864/Makefile @@ -0,0 +1,3 @@ +tw5864-objs := tw5864-core.o tw5864-video.o tw5864-config.o tw5864-h264.o + +obj-$(CONFIG_VIDEO_TW5864) += tw5864.o diff --git a/drivers/staging/media/tw5864/tw5864-bs.h b/drivers/staging/media/tw5864/tw5864-bs.h new file mode 100644 index 0000000..8c1df7a --- /dev/null +++ b/drivers/staging/media/tw5864/tw5864-bs.h @@ -0,0 +1,154 @@ +/* + * TW5864 driver - Exp-Golomb code functions + * + * Copyright (C) 2015 Bluecherry, LLC + * Author: Andrey Utkin + * + * 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. + */ + +struct bs { + u8 *p_start; + u8 *p; + u8 *p_end; + int i_left; /* number of available bits */ +}; + +static inline void bs_init(struct bs *s, void *buf, int size) +{ + s->p_start = buf; + s->p = buf; + s->p_end = s->p + size; + s->i_left = 8; +} + +static inline int bs_pos(struct bs *s) +{ + return 8 * (s->p - s->p_start) + 8 - s->i_left; +} + +static inline bool bs_eof(struct bs *s) +{ + return s->p >= s->p_end; +} + +static inline int bs_len(struct bs *s) +{ + return s->p - s->p_start; +} + +static inline int bs_left(struct bs *s) +{ + return 8 - s->i_left; +} + +static inline void bs_direct_write(struct bs *s, u8 value) +{ + *s->p = value; + s->p++; + s->i_left = 8; +} + +static inline void bs_write(struct bs *s, int i_count, u32 i_bits) +{ + if (s->p >= s->p_end - 4) + return; + while (i_count > 0) { + if (i_count < 32) + i_bits &= (1 << i_count) - 1; + if (i_count < s->i_left) { + *s->p = (*s->p << i_count) | i_bits; + s->i_left -= i_count; + break; + } + *s->p = (*s->p << s->i_left) | (i_bits >> (i_count - + s->i_left)); + i_count -= s->i_left; + s->p++; + s->i_left = 8; + } +} + +static inline void bs_write1(struct bs *s, u32 i_bit) +{ + if (s->p < s->p_end) { + *s->p <<= 1; + *s->p |= i_bit; + s->i_left--; + if (s->i_left == 0) { + s->p++; + s->i_left = 8; + } + } +} + +static inline void bs_align_0(struct bs *s) +{ + if (s->i_left != 8) { + *s->p <<= s->i_left; + s->i_left = 8; + s->p++; + } +} + +static inline void bs_sh_align(struct bs *s) +{ + if (s->i_left != 8) { + *s->p <<= s->i_left; + s->i_left = 8; + } +} + +static inline void bs_align_1(struct bs *s) +{ + if (s->i_left != 8) { + *s->p <<= s->i_left; + *s->p |= (1 << s->i_left) - 1; + s->i_left = 8; + s->p++; + } +} + +static inline void bs_align(struct bs *s) +{ + bs_align_0(s); +} + +/* golomb functions */ +static inline void bs_write_ue(struct bs *s, u32 val) +{ + if (val == 0) { + bs_write1(s, 1); + } else { + val++; + bs_write(s, 2 * fls(val) - 1, val); + } +} + +static inline void bs_write_se(struct bs *s, int val) +{ + bs_write_ue(s, val <= 0 ? -val * 2 : val * 2 - 1); +} + +static inline void bs_write_te(struct bs *s, int x, int val) +{ + if (x == 1) + bs_write1(s, 1 & ~val); + else if (x > 1) + bs_write_ue(s, val); +} + +static inline void bs_rbsp_trailing(struct bs *s) +{ + bs_write1(s, 1); + if (s->i_left != 8) + bs_write(s, s->i_left, 0x00); +} diff --git a/drivers/staging/media/tw5864/tw5864-config.c b/drivers/staging/media/tw5864/tw5864-config.c new file mode 100644 index 0000000..ff3e308 --- /dev/null +++ b/drivers/staging/media/tw5864/tw5864-config.c @@ -0,0 +1,359 @@ +/* + * TW5864 driver - analog decoders configuration functions + * + * Copyright (C) 2015 Bluecherry, LLC + * Author: Andrey Utkin + * + * 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 "tw5864.h" +#include "tw5864-reg.h" + +#define TW5864_IIC_TIMEOUT (30000) + +static unsigned char tbl_pal_tw2864_common[] __used = { + 0x00, 0x00, 0x64, 0x11, + 0x80, 0x80, 0x00, 0x12, + 0x12, 0x20, 0x0a, 0xD0, + 0x00, 0x00, 0x07, 0x7F, +}; + +static unsigned char tbl_ntsc_tw2864_common[] __used = { + 0x00, 0x00, 0x64, 0x11, + 0x80, 0x80, 0x00, 0x02, + 0x12, 0xF0, 0x0C, 0xD0, + 0x00, 0x00, 0x07, 0x7F +}; + +static unsigned char tbl_pal_tw2864_common2[] __used = { + 0x00, 0x22, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x22, + 0x00, 0x00, 0x22, 0x00, +}; + +static unsigned char tbl_tw2864_other[] __used = { + 0xfb, 0x6f, 0xfc, 0xff, + 0xdb, 0xc1, 0xd2, 0x01, + 0xdd, 0x00, 0xde, 0x00, + 0xe1, 0xc0, 0xe2, 0xaa, + 0xe3, 0xaa, 0xf8, 0x64, + 0xf9, 0x11, 0xaa, 0x00, + 0x9e, 0x72, 0x9c, 0x20, + 0x94, 0x14, 0xca, 0xaa, + 0xcb, 0x00, 0x89, 0x02, + 0xfa, 0xc6, 0xcf, 0x83, + 0x9f, 0x00, 0xb1, 0x2a, + 0x9e, 0x7a, +}; + +static unsigned char tbl_pal_tw2865_common[] __used = { + 0x00, 0x00, 0x64, 0x11, + 0x80, 0x80, 0x00, 0x12, + 0x17, 0x20, 0x0C, 0xD0, + 0x00, 0x00, 0x07, 0x7F, +}; + +static unsigned char tbl_ntsc_tw2865_common[] __used = { + 0x00, 0x00, 0x64, 0x11, + 0x80, 0x80, 0x00, 0x02, + 0x12, 0xF0, 0x0C, 0xD0, + 0x00, 0x00, 0x07, 0x7F +}; + +static unsigned char tbl_tw2865_other1[] __used = { + 0xfa, 0x4a, 0xfb, 0x6f, + 0xfc, 0xff, 0x9c, 0x20, + 0x9e, 0x72, 0xca, 0x02, + 0xf9, 0x51, 0xaa, 0x00, + 0x41, 0xd4, 0x43, 0x08, + 0x6b, 0x0f, 0x6c, 0x0f, + 0x61, 0x02, 0x96, 0xe6, + 0x97, 0xc3, 0x9f, 0x03, + 0xb1, 0x2a, 0x9e, 0x7a, + 0x18, 0x19, 0x1a, 0x06, + 0x28, 0x19, 0x2a, 0x06, + 0x38, 0x19, 0x3a, 0x06, + 0x60, 0x15, +}; + +static unsigned char tbl_tw2866_other1[] __used = { + 0xfa, 0x4a, 0xfb, 0x6f, + 0xfc, 0xff, 0x9c, 0x20, + 0x9e, 0x72, 0xca, 0x02, + 0xf9, 0x51, 0xaa, 0x00, + 0x41, 0xd4, 0x43, 0x08, + 0x6b, 0x0f, 0x6c, 0x0f, + 0x61, 0x02, 0x96, 0xe6, + 0x97, 0xc3, 0x9f, 0x00, + 0xb1, 0x2a, 0x9e, 0x7a, + 0x5b, 0xff, 0x08, 0x19, + 0x0a, 0x06, 0x18, 0x19, + 0x1a, 0x06, 0x28, 0x19, + 0x2a, 0x06, 0x38, 0x19, + 0x3a, 0x06, 0x60, 0x15, +}; + +static unsigned char tbl_tw2865_other2[] __used = { + 0x73, 0x01, 0xf8, 0xc4, + 0xf9, 0x51, 0x70, 0x08, + 0x7f, 0x80, 0xcf, 0x80 +}; + +static unsigned char tbl_tw2865_other3[] __used = { + 0x89, 0x05, 0x7e, 0xc0, + 0xe0, 0x00 +}; + +static unsigned char audio_tw2865_common[] __used = { + 0x33, 0x33, 0x03, 0x31, + 0x75, 0xb9, 0xfd, 0x20, + 0x64, 0xa8, 0xec, 0xC1, + 0x00, 0x00, 0x00, 0x80, + 0x00, 0xC0, 0xAA, 0xAA +}; + +static unsigned char audio_tbl_pal_tw2865_8KHz[] __used = { + 0x83, 0xB5, 0x09, 0x00, + 0xA0, 0x00 +}; + +static unsigned char audio_tbl_pal_tw2865_16KHz[] __used = { + 0x07, 0x6B, 0x13, 0x00, 0x40, 0x01 +}; + +static unsigned char audio_tbl_ntsc_tw2865_8KHz[] __used = { + 0x83, 0xB5, 0x09, 0x78, 0x85, 0x00 +}; + +static unsigned char audio_tbl_ntsc_tw2865_16KHz[] __used = { + 0x07, 0x6B, 0x13, 0xEF, 0x0A, 0x01 +}; + +static int i2c_read(struct tw5864_dev *dev, u8 devid, u8 devfn, u8 *buf); + +static int __used i2c_multi_read(struct tw5864_dev *dev, u8 devid, u8 devfn, + u8 *buf, u32 count) +{ + int i = 0; + u32 val = 0; + int timeout = TW5864_IIC_TIMEOUT; + unsigned long flags; + + local_irq_save(flags); + for (i = 0; i < count; i++) { + val = (1 << 24) | ((devid | 0x01) << 16) | ((devfn + i) << 8); + + tw_writel(TW5864_IIC, val); + + do { + val = tw_readl(TW5864_IIC) & (0x01000000); + } while ((!val) && (--timeout)); + if (!timeout) { + local_irq_restore(flags); + dev_err(&dev->pci->dev, "dev 0x%x, fn 0x%x\n", devid, + devfn); + return -ETIMEDOUT; + } + buf[i] = (u8)tw_readl(TW5864_IIC); + } + local_irq_restore(flags); + + return 0; +} + +static int i2c_multi_write(struct tw5864_dev *dev, u8 devid, u8 devfn, u8 *buf, + u32 count) +{ + int i = 0; + u32 val = 0; + int timeout = TW5864_IIC_TIMEOUT; + unsigned long flags; + + local_irq_save(flags); + for (i = 0; i < count; i++) { + val = (1 << 24) | ((devid & 0xfe) << 16) | ((devfn + i) << 8) | + buf[i]; + tw_writel(TW5864_IIC, val); + do { + val = tw_readl(TW5864_IIC) & (0x01000000); + } while ((!val) && (--timeout)); + if (!timeout) { + local_irq_restore(flags); + dev_err(&dev->pci->dev, "dev 0x%x, fn 0x%x, 0x%x\n", + devid, devfn, buf[i]); + return -ETIMEDOUT; + } + } + local_irq_restore(flags); + + return 0; +} + +static int i2c_read(struct tw5864_dev *dev, u8 devid, u8 devfn, u8 *buf) +{ + u32 val = 0; + int timeout = TW5864_IIC_TIMEOUT; + unsigned long flags; + + local_irq_save(flags); + val = (1 << 24) | ((devid | 0x01) << 16) | (devfn << 8); + + tw_writel(TW5864_IIC, val); + do { + val = tw_readl(TW5864_IIC) & (0x01000000); + } while ((!val) && (--timeout)); + if (!timeout) { + local_irq_restore(flags); + dev_err(&dev->pci->dev, "dev 0x%x, fn 0x%x\n", devid, devfn); + return -ETIMEDOUT; + } + + *buf = (u8)tw_readl(TW5864_IIC); + local_irq_restore(flags); + + return 0; +} + +static int i2c_write(struct tw5864_dev *dev, u8 devid, u8 devfn, u8 buf) +{ + u32 val = 0; + int timeout = TW5864_IIC_TIMEOUT; + unsigned long flags; + + local_irq_save(flags); + val = (1 << 24) + ((devid & 0xfe) << 16) + (devfn << 8) + buf; + tw_writel(TW5864_IIC, val); + do { + val = tw_readl(TW5864_IIC) & (0x01000000); + } while ((!val) && (--timeout)); + local_irq_restore(flags); + if (!timeout) { + dev_err(&dev->pci->dev, "dev 0x%x, fn 0x%x, 0x%x\n", devid, + devfn, buf); + return -ETIMEDOUT; + } + + return 0; +} + +static int i2c_wscatter(struct tw5864_dev *dev, u8 devid, u8 *buf, u32 count) +{ + int i = 0; + u32 val = 0; + int timeout = TW5864_IIC_TIMEOUT; + unsigned long flags; + + local_irq_save(flags); + for (i = 0; i < count; i++) { + val = (1 << 24) + ((devid & 0xfe) << 16) + (buf[i * 2 + 0] << 8) + + buf[i * 2 + 1]; + tw_writel(TW5864_IIC, val); + do { + val = tw_readl(TW5864_IIC) & (0x01000000); + } while ((!val) && (--timeout)); + if (!timeout) { + local_irq_restore(flags); + dev_err(&dev->pci->dev, "dev 0x%x, fn 0x%x, 0x%x\n", + devid, buf[i * 2], buf[i * 2 + 1]); + return -ETIMEDOUT; + } + } + local_irq_restore(flags); + + return 0; +} + +static void init_tw2864(struct tw5864_dev *dev, u8 iic) +{ + u32 ch; + + for (ch = 0; ch < 4; ch++) + i2c_multi_write(dev, iic, ch * 0x10, tbl_pal_tw2864_common, 16); + + i2c_wscatter(dev, iic, tbl_tw2864_other, 23); + i2c_write(dev, iic, 0xcf, 0x83); + i2c_write(dev, iic, 0xe0, 0x00); +} + +static __used void init_tw2865(struct tw5864_dev *dev, u8 iic) +{ + u32 ch; + + for (ch = 0; ch < 4; ch++) + i2c_multi_write(dev, iic, ch * 0x10, tbl_pal_tw2865_common, 16); + + i2c_wscatter(dev, iic, tbl_tw2865_other1, + sizeof(tbl_tw2865_other1) >> 1); + i2c_multi_write(dev, iic, 0xd0, audio_tw2865_common, 20); + i2c_wscatter(dev, iic, tbl_tw2865_other2, 6); + i2c_multi_write(dev, iic, 0xf0, audio_tbl_pal_tw2865_8KHz, 6); + i2c_wscatter(dev, iic, tbl_tw2865_other3, 3); + i2c_write(dev, iic, 0xe0, 0x10); +} + +#define ISIL_PHY_VD_CHAN_NUMBER (16) + +/*auto detect CLKP_DEL delay*/ +static int tw28xx_clkp_delay(struct tw5864_dev *dev, u8 devid, u32 base_ch, + u32 limit) +{ + if (dev && (base_ch < ISIL_PHY_VD_CHAN_NUMBER) && + (limit <= (ISIL_PHY_VD_CHAN_NUMBER >> 2))) { + int delay; + u8 flags = 0; + + delay = -1; + i2c_read(dev, devid, 0x9f, &flags); + while ((++delay) < 0x10) { + i2c_write(dev, devid, 0x9f, delay); + /* only bus0 can detect colume and line */ + tw_writel(TW5864_H264EN_BUS0_MAP, base_ch); + /* clear error flags */ + tw_writel(TW5864_UNDEFINED_ERROR_FLAGS_0x9218, 0x1); + mdelay(100); + if (tw_readl(TW5864_UNDEFINED_ERROR_FLAGS_0x9218)) + continue; + dev_dbg(&dev->pci->dev, "auto detect CLKP_DEL = %02x\n", + delay); + break; + } + if (delay >= 0x10) { + dev_err(&dev->pci->dev, + "can't find suitable clkp_del for devid 0x%02x\n", + devid); + i2c_write(dev, devid, 0x9f, flags); + + return -EFAULT; + } + return 0; + } + + return 1; +} + +void tw5864_init_ad(struct tw5864_dev *dev) +{ + unsigned int val; + + val = tw_readl(TW5864_IIC_ENB); + val |= 0x01; + tw_writel(TW5864_IIC_ENB, val); + tw_writel(TW5864_I2C_PHASE_CFG, 0x01); + + init_tw2864(dev, 0x52); + tw28xx_clkp_delay(dev, 0x52, 4, 4); + init_tw2864(dev, 0x54); + tw28xx_clkp_delay(dev, 0x54, 8, 4); + init_tw2864(dev, 0x56); + tw28xx_clkp_delay(dev, 0x56, 12, 4); + init_tw2865(dev, 0x50); +} diff --git a/drivers/staging/media/tw5864/tw5864-core.c b/drivers/staging/media/tw5864/tw5864-core.c new file mode 100644 index 0000000..c41ba4c --- /dev/null +++ b/drivers/staging/media/tw5864/tw5864-core.c @@ -0,0 +1,453 @@ +/* + * TW5864 driver - core functions + * + * Copyright (C) 2015 Bluecherry, LLC + * Author: Andrey Utkin + * + * 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 +#include +#include +#include +#include + +#include "tw5864.h" +#include "tw5864-reg.h" + +MODULE_DESCRIPTION("V4L2 driver module for tw5864-based multimedia capture & encoding devices"); +MODULE_AUTHOR("Bluecherry Maintainers "); +MODULE_AUTHOR("Andrey Utkin "); +MODULE_LICENSE("GPL"); + +/* take first free /dev/videoX indexes by default */ +static unsigned int video_nr[] = {[0 ... (TW5864_INPUTS - 1)] = -1 }; + +module_param_array(video_nr, int, NULL, 0444); +MODULE_PARM_DESC(video_nr, "video devices numbers array"); + +/* + * Please add any new PCI IDs to: http://pci-ids.ucw.cz. This keeps + * the PCI ID database up to date. Note that the entries must be + * added under vendor 0x1797 (Techwell Inc.) as subsystem IDs. + */ +static const struct pci_device_id tw5864_pci_tbl[] = { + {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_TECHWELL_5864)}, + {0,} +}; + +void tw_indir_writeb(struct tw5864_dev *dev, u16 addr, u8 data) +{ + int retries = 30000; + + addr <<= 2; + + while ((tw_readl(TW5864_IND_CTL) >> 31) && (retries--)) + ; + if (!retries) + dev_err(&dev->pci->dev, + "tw_indir_writel() retries exhausted before writing\n"); + + tw_writel(TW5864_IND_DATA, data); + tw_writel(TW5864_IND_CTL, addr | TW5864_RW | TW5864_ENABLE); +} + +u8 tw_indir_readb(struct tw5864_dev *dev, u16 addr) +{ + int retries = 30000; + u32 data = 0; + + addr <<= 2; + + while ((tw_readl(TW5864_IND_CTL) >> 31) && (retries--)) + ; + if (!retries) + dev_err(&dev->pci->dev, + "tw_indir_readl() retries exhausted before reading\n"); + + tw_writel(TW5864_IND_CTL, addr | TW5864_ENABLE); + + retries = 30000; + while ((tw_readl(TW5864_IND_CTL) >> 31) && (retries--)) + ; + if (!retries) + dev_err(&dev->pci->dev, + "tw_indir_readl() retries exhausted at reading\n"); + + data = tw_readl(TW5864_IND_DATA); + return data & 0xff; +} + +void tw5864_irqmask_apply(struct tw5864_dev *dev) +{ + tw_writel(TW5864_INTR_ENABLE_L, dev->irqmask & 0xffff); + tw_writel(TW5864_INTR_ENABLE_H, (dev->irqmask >> 16)); +} + +static void tw5864_interrupts_disable(struct tw5864_dev *dev) +{ + unsigned long flags; + + spin_lock_irqsave(&dev->slock, flags); + dev->irqmask = 0; + tw5864_irqmask_apply(dev); + spin_unlock_irqrestore(&dev->slock, flags); +} + +static void tw5864_timer_isr(struct tw5864_dev *dev); +static void tw5864_h264_isr(struct tw5864_dev *dev); + +static irqreturn_t tw5864_isr(int irq, void *dev_id) +{ + struct tw5864_dev *dev = dev_id; + u32 status; + + status = tw_readl(TW5864_INTR_STATUS_L) + | (tw_readl(TW5864_INTR_STATUS_H) << 16); + if (!status) + return IRQ_NONE; + + tw_writel(TW5864_INTR_CLR_L, 0xffff); + tw_writel(TW5864_INTR_CLR_H, 0xffff); + + if (status & TW5864_INTR_VLC_DONE) { + tw5864_h264_isr(dev); + tw_writel(TW5864_VLC_DSP_INTR, 0x00000001); + tw_writel(TW5864_PCI_INTR_STATUS, TW5864_VLC_DONE_INTR); + } + + if (status & TW5864_INTR_TIMER) { + tw5864_timer_isr(dev); + tw_writel(TW5864_PCI_INTR_STATUS, TW5864_TIMER_INTR); + } + + if (!(status & (TW5864_INTR_TIMER | TW5864_INTR_VLC_DONE))) { + dev_dbg(&dev->pci->dev, "Unknown interrupt, status 0x%08X\n", + status); + } + + return IRQ_HANDLED; +} + +static void tw5864_h264_isr(struct tw5864_dev *dev) +{ + int channel = tw_readl(TW5864_DSP) & TW5864_DSP_ENC_CHN; + struct tw5864_input *input = &dev->inputs[channel]; + int cur_frame_index, next_frame_index; + struct tw5864_h264_frame *cur_frame, *next_frame; + unsigned long flags; + + spin_lock_irqsave(&dev->slock, flags); + + cur_frame_index = dev->h264_buf_w_index; + next_frame_index = (cur_frame_index + 1) % H264_BUF_CNT; + cur_frame = &dev->h264_buf[cur_frame_index]; + next_frame = &dev->h264_buf[next_frame_index]; + + dma_sync_single_for_cpu(&dev->pci->dev, cur_frame->vlc.dma_addr, + H264_VLC_BUF_SIZE, DMA_FROM_DEVICE); + dma_sync_single_for_cpu(&dev->pci->dev, cur_frame->mv.dma_addr, + H264_MV_BUF_SIZE, DMA_FROM_DEVICE); + + if (next_frame_index != dev->h264_buf_r_index) { + cur_frame->vlc_len = tw_readl(TW5864_VLC_LENGTH) << 2; + cur_frame->checksum = tw_readl(TW5864_VLC_CRC_REG); + cur_frame->input = input; + cur_frame->timestamp = ktime_get_ns(); + + dev->h264_buf_w_index = next_frame_index; + tasklet_schedule(&dev->tasklet); + + cur_frame = next_frame; + } else { + dev_err(&dev->pci->dev, + "Skipped frame on input %d because all buffers busy\n", + channel); + } + + dev->encoder_busy = 0; + + spin_unlock_irqrestore(&dev->slock, flags); + + input->frame_seqno++; + + dma_sync_single_for_device(&dev->pci->dev, + cur_frame->vlc.dma_addr, + H264_VLC_BUF_SIZE, DMA_FROM_DEVICE); + dma_sync_single_for_device(&dev->pci->dev, + cur_frame->mv.dma_addr, + H264_MV_BUF_SIZE, DMA_FROM_DEVICE); + + tw_writel(TW5864_VLC_STREAM_BASE_ADDR, cur_frame->vlc.dma_addr); + tw_writel(TW5864_MV_STREAM_BASE_ADDR, cur_frame->mv.dma_addr); +} + +static void tw5864_timer_isr(struct tw5864_dev *dev) +{ + unsigned long flags; + int i; + int encoder_busy; + + spin_lock_irqsave(&dev->slock, flags); + encoder_busy = dev->encoder_busy; + spin_unlock_irqrestore(&dev->slock, flags); + + if (encoder_busy) + return; + + /* + * Traversing inputs in round-robin fashion, starting from next to the + * last processed one + */ + for (i = 0; i < TW5864_INPUTS; i++) { + int next_input = (i + dev->next_i) % TW5864_INPUTS; + struct tw5864_input *input = &dev->inputs[next_input]; + int raw_buf_id; /* id of internal buf with last raw frame */ + + spin_lock_irqsave(&input->slock, flags); + if (!input->enabled) + goto next; + + raw_buf_id = tw_mask_shift_readl(TW5864_SENIF_ORG_FRM_PTR1, 0x3, + 2 * input->input_number); + + /* Check if new raw frame is available */ + if (input->buf_id == raw_buf_id) + goto next; + + input->buf_id = raw_buf_id; + spin_unlock_irqrestore(&input->slock, flags); + + spin_lock_irqsave(&dev->slock, flags); + dev->encoder_busy = 1; + spin_unlock_irqrestore(&dev->slock, flags); + tw5864_request_encoded_frame(input); + break; +next: + spin_unlock_irqrestore(&input->slock, flags); + continue; + } +} + +static size_t regs_dump(struct tw5864_dev *dev, char *buf, size_t size) +{ + size_t count = 0; + u32 reg_addr; + u32 value; + int i; + struct range { + int start; + int end; + } ranges[] = { + { 0x0000, 0x2FFC }, + { 0x4000, 0x4FFC }, + { 0x8000, 0x180DC }, + { 0x18100, 0x1817C }, + { 0x80000, 0x87FFF }, + }; + + /* + * Dumping direct registers space, + * except some spots which trigger hanging + */ + for (i = 0; i < ARRAY_SIZE(ranges); i++) + for (reg_addr = ranges[i].start; + (count < size) && (reg_addr <= ranges[i].end); + reg_addr += 4) { + value = tw_readl(reg_addr); + count += scnprintf(buf + count, size - count, + "[0x%05x] = 0x%08x\n", + reg_addr, value); + } + + /* Dumping indirect register space */ + for (reg_addr = 0x0; (count < size) && (reg_addr <= 0xEFE); + reg_addr += 1) { + value = tw_indir_readb(dev, reg_addr); + count += scnprintf(buf + count, size - count, + "indir[0x%03x] = 0x%02x\n", reg_addr, value); + } + + return count; +} + +#define DEBUGFS_BUF_SIZE (1024 * 1024) + +struct debugfs_buffer { + size_t count; + char data[DEBUGFS_BUF_SIZE]; +}; + +static int debugfs_regs_dump_open(struct inode *inode, struct file *file) +{ + struct tw5864_dev *dev = inode->i_private; + struct debugfs_buffer *buf; + + buf = kmalloc(sizeof(*buf), GFP_KERNEL); + if (!buf) + return -ENOMEM; + + buf->count = regs_dump(dev, buf->data, sizeof(buf->data)); + + file->private_data = buf; + return 0; +} + +static ssize_t debugfs_regs_dump_read(struct file *file, char __user *user_buf, + size_t nbytes, loff_t *ppos) +{ + struct debugfs_buffer *buf = file->private_data; + + return simple_read_from_buffer(user_buf, nbytes, ppos, buf->data, + buf->count); +} + +static int debugfs_regs_dump_release(struct inode *inode, struct file *file) +{ + kfree(file->private_data); + file->private_data = NULL; + + return 0; +} + +static const struct file_operations debugfs_regs_dump_fops = { + .owner = THIS_MODULE, + .open = debugfs_regs_dump_open, + .llseek = no_llseek, + .read = debugfs_regs_dump_read, + .release = debugfs_regs_dump_release, +}; + +static int tw5864_initdev(struct pci_dev *pci_dev, + const struct pci_device_id *pci_id) +{ + struct tw5864_dev *dev; + int err; + + dev = devm_kzalloc(&pci_dev->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + snprintf(dev->name, sizeof(dev->name), "tw5864:%s", pci_name(pci_dev)); + + err = v4l2_device_register(&pci_dev->dev, &dev->v4l2_dev); + if (err) + goto v4l2_reg_fail; + + /* pci init */ + dev->pci = pci_dev; + if (pci_enable_device(pci_dev)) { + err = -EIO; + goto pci_enable_fail; + } + + pci_set_master(pci_dev); + + err = pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32)); + if (err) { + dev_err(&dev->pci->dev, "32bit PCI DMA is not supported\n"); + goto req_mem_fail; + } + + /* get mmio */ + if (!request_mem_region(pci_resource_start(pci_dev, 0), + pci_resource_len(pci_dev, 0), dev->name)) { + err = -EBUSY; + dev_err(&dev->pci->dev, "can't get MMIO memory @ 0x%llx\n", + (unsigned long long)pci_resource_start(pci_dev, 0)); + goto req_mem_fail; + } + dev->mmio = ioremap_nocache(pci_resource_start(pci_dev, 0), + pci_resource_len(pci_dev, 0)); + if (!dev->mmio) { + err = -EIO; + dev_err(&dev->pci->dev, "can't ioremap() MMIO memory\n"); + goto ioremap_fail; + } + + spin_lock_init(&dev->slock); + + dev->debugfs_dir = debugfs_create_dir(dev->name, NULL); + err = tw5864_video_init(dev, video_nr); + if (err) + goto video_init_fail; + + /* get irq */ + err = devm_request_irq(&pci_dev->dev, pci_dev->irq, tw5864_isr, + IRQF_SHARED, "tw5864", dev); + if (err < 0) { + dev_err(&dev->pci->dev, "can't get IRQ %d\n", pci_dev->irq); + goto irq_req_fail; + } + + debugfs_create_file("regs_dump", S_IRUGO, dev->debugfs_dir, dev, + &debugfs_regs_dump_fops); + + return 0; + +irq_req_fail: + tw5864_video_fini(dev); +video_init_fail: + iounmap(dev->mmio); +ioremap_fail: + release_mem_region(pci_resource_start(pci_dev, 0), + pci_resource_len(pci_dev, 0)); +req_mem_fail: + pci_disable_device(pci_dev); +pci_enable_fail: + v4l2_device_unregister(&dev->v4l2_dev); +v4l2_reg_fail: + devm_kfree(&pci_dev->dev, dev); + return err; +} + +static void tw5864_finidev(struct pci_dev *pci_dev) +{ + struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); + struct tw5864_dev *dev = + container_of(v4l2_dev, struct tw5864_dev, v4l2_dev); + + /* shutdown subsystems */ + tw5864_interrupts_disable(dev); + + debugfs_remove_recursive(dev->debugfs_dir); + + /* unregister */ + tw5864_video_fini(dev); + + /* release resources */ + iounmap(dev->mmio); + release_mem_region(pci_resource_start(pci_dev, 0), + pci_resource_len(pci_dev, 0)); + + v4l2_device_unregister(&dev->v4l2_dev); + devm_kfree(&pci_dev->dev, dev); +} + +static struct pci_driver tw5864_pci_driver = { + .name = "tw5864", + .id_table = tw5864_pci_tbl, + .probe = tw5864_initdev, + .remove = tw5864_finidev, +}; + +module_pci_driver(tw5864_pci_driver); diff --git a/drivers/staging/media/tw5864/tw5864-h264.c b/drivers/staging/media/tw5864/tw5864-h264.c new file mode 100644 index 0000000..8203e3a --- /dev/null +++ b/drivers/staging/media/tw5864/tw5864-h264.c @@ -0,0 +1,183 @@ +/* + * TW5864 driver - H.264 headers generation functions + * + * Copyright (C) 2015 Bluecherry, LLC + * Author: Andrey Utkin + * + * 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 "tw5864.h" +#include "tw5864-bs.h" + +static u8 marker[] = { 0x00, 0x00, 0x00, 0x01 }; + +/* log2 of max GOP size, taken 8 as V4L2-advertised max GOP size is 255 */ +#define i_log2_max_frame_num 8 +#define i_log2_max_poc_lsb i_log2_max_frame_num + +static int tw5864_h264_gen_sps_rbsp(u8 *buf, size_t size, int width, int height) +{ + struct bs bs, *s; + const int i_mb_width = width / 16; + const int i_mb_height = height / 16; + + s = &bs; + bs_init(s, buf, size); + bs_write(s, 8, 0x42 /* profile == 66, baseline */); + bs_write(s, 8, 0 /* constraints */); + bs_write(s, 8, 0x1E /* level */); + bs_write_ue(s, 0 /* SPS id */); + bs_write_ue(s, i_log2_max_frame_num - 4); + bs_write_ue(s, 0 /* i_poc_type */); + bs_write_ue(s, i_log2_max_poc_lsb - 4); + + bs_write_ue(s, 1 /* i_num_ref_frames */); + bs_write(s, 1, 0 /* b_gaps_in_frame_num_value_allowed */); + bs_write_ue(s, i_mb_width - 1); + bs_write_ue(s, i_mb_height - 1); + bs_write(s, 1, 1 /* b_frame_mbs_only */); + bs_write(s, 1, 0 /* b_direct8x8_inference */); + bs_write(s, 1, 0); + bs_write(s, 1, 0); + bs_rbsp_trailing(s); + return bs_len(s); +} + +static int tw5864_h264_gen_pps_rbsp(u8 *buf, size_t size, int qp) +{ + struct bs bs, *s; + + s = &bs; + bs_init(s, buf, size); + bs_write_ue(s, 0 /* PPS id */); + bs_write_ue(s, 0 /* SPS id */); + bs_write(s, 1, 0 /* b_cabac */); + bs_write(s, 1, 0 /* b_pic_order */); + bs_write_ue(s, (1 /* i_num_slice_groups */) - 1); + bs_write_ue(s, (1 /* i_num_ref_idx_l0_active */) - 1); + bs_write_ue(s, (1 /* i_num_ref_idx_l1_active */) - 1); + bs_write(s, 1, 0 /* b_weighted_pred */); + bs_write(s, 2, 0 /* b_weighted_bipred */); + bs_write_se(s, qp - 26); + bs_write_se(s, qp - 26); + bs_write_se(s, 0 /* i_chroma_qp_index_offset */); + bs_write(s, 1, 0 /* b_deblocking_filter_control */); + bs_write(s, 1, 0 /* b_constrained_intra_pred */); + bs_write(s, 1, 0 /* b_redundant_pic_cnt */); + bs_rbsp_trailing(s); + return bs_len(s); +} + +static int tw5864_h264_gen_slice_head(u8 *buf, size_t size, + unsigned int idr_pic_id, + unsigned int frame_seqno_in_gop, + int *tail_nb_bits, u8 *tail) +{ + struct bs bs, *s; + int is_i_frame = frame_seqno_in_gop == 0; + int i_poc_lsb = frame_seqno_in_gop; + + s = &bs; + bs_init(s, buf, size); + bs_write_ue(s, 0 /* i_first_mb */); + bs_write_ue(s, is_i_frame ? 2 : 5 /* slice type - I or P */); + bs_write_ue(s, 0 /* PPS id */); + bs_write(s, i_log2_max_frame_num, frame_seqno_in_gop); + if (is_i_frame) + bs_write_ue(s, idr_pic_id); + + bs_write(s, i_log2_max_poc_lsb, i_poc_lsb); + + if (!is_i_frame) + bs_write1(s, 0 /*b_num_ref_idx_override */); + + /* ref pic list reordering */ + if (!is_i_frame) + bs_write1(s, 0 /* b_ref_pic_list_reordering_l0 */); + + if (is_i_frame) { + bs_write1(s, 0); /* no output of prior pics flag */ + bs_write1(s, 0); /* long term reference flag */ + } else { + bs_write1(s, 0); /* adaptive_ref_pic_marking_mode_flag */ + } + + bs_write_se(s, 0 /* i_qp_delta */); + + if (s->i_left != 8) { + *tail = ((s->p[0]) << s->i_left); + *tail_nb_bits = 8 - s->i_left; + } else { + *tail = 0; + *tail_nb_bits = 0; + } + + return bs_len(s); +} + +void tw5864_h264_put_stream_header(u8 **buf, size_t *space_left, int qp, + int width, int height) +{ + int nal_len; + + /* SPS */ + WARN_ON_ONCE(*space_left < 4); + memcpy(*buf, marker, sizeof(marker)); + *buf += 4; + *space_left -= 4; + + **buf = 0x67; /* SPS NAL header */ + *buf += 1; + *space_left -= 1; + + nal_len = tw5864_h264_gen_sps_rbsp(*buf, *space_left, width, height); + *buf += nal_len; + *space_left -= nal_len; + + /* PPS */ + WARN_ON_ONCE(*space_left < 4); + memcpy(*buf, marker, sizeof(marker)); + *buf += 4; + *space_left -= 4; + + **buf = 0x68; /* PPS NAL header */ + *buf += 1; + *space_left -= 1; + + nal_len = tw5864_h264_gen_pps_rbsp(*buf, *space_left, qp); + *buf += nal_len; + *space_left -= nal_len; +} + +void tw5864_h264_put_slice_header(u8 **buf, size_t *space_left, + unsigned int idr_pic_id, + unsigned int frame_seqno_in_gop, + int *tail_nb_bits, u8 *tail) +{ + int nal_len; + + WARN_ON_ONCE(*space_left < 4); + memcpy(*buf, marker, sizeof(marker)); + *buf += 4; + *space_left -= 4; + + /* Frame NAL header */ + **buf = (frame_seqno_in_gop == 0) ? 0x25 : 0x21; + *buf += 1; + *space_left -= 1; + + nal_len = tw5864_h264_gen_slice_head(*buf, *space_left, idr_pic_id, + frame_seqno_in_gop, tail_nb_bits, + tail); + *buf += nal_len; + *space_left -= nal_len; +} diff --git a/drivers/staging/media/tw5864/tw5864-reg.h b/drivers/staging/media/tw5864/tw5864-reg.h new file mode 100644 index 0000000..a6fdcc1 --- /dev/null +++ b/drivers/staging/media/tw5864/tw5864-reg.h @@ -0,0 +1,2200 @@ +/* + * TW5864 driver - registers description + * + * Copyright (C) 2015 Bluecherry, LLC + * Author: Andrey Utkin + * + * 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. + */ + +/* According to TW5864_datasheet_0.6d.pdf, tw5864b1-ds.pdf */ + +/* Register Description - Direct Map Space */ +/* 0x0000 ~ 0x1FFC - H264 Register Map */ +/* [15:0] The Version register for H264 core (Read Only) */ +#define TW5864_H264REV 0x0000 + +#define TW5864_EMU 0x0004 +/* Define controls in register TW5864_EMU */ +/* DDR controller enabled */ +#define TW5864_EMU_EN_DDR BIT(0) +/* Enable bit for Inter module */ +#define TW5864_EMU_EN_ME BIT(1) +/* Enable bit for Sensor Interface module */ +#define TW5864_EMU_EN_SEN BIT(2) +/* Enable bit for Host Burst Access */ +#define TW5864_EMU_EN_BHOST BIT(3) +/* Enable bit for Loop Filter module */ +#define TW5864_EMU_EN_LPF BIT(4) +/* Enable bit for PLBK module */ +#define TW5864_EMU_EN_PLBK BIT(5) +/* + * Video Frame mapping in DDR + * 00 CIF + * 01 D1 + * 10 Reserved + * 11 Reserved + * + */ +#define TW5864_DSP_FRAME_TYPE (3 << 6) +#define TW5864_DSP_FRAME_TYPE_D1 BIT(6) + +/* */ +#define TW5864_SLICE 0x000C +/* Define controls in register TW5864_SLICE */ +/* VLC Slice end flag */ +#define TW5864_VLC_SLICE_END BIT(0) +/* Master Slice End Flag */ +#define TW5864_MAS_SLICE_END BIT(4) +/* Host to start a new slice Address */ +#define TW5864_START_NSLICE BIT(15) + +/* + * [15:0] Two bit for each channel + * (channel 0 ~ 7). Each two bits are + * the buffer pointer for the last + * encoded frame of the corresponding + * channel. + */ +#define TW5864_ENC_BUF_PTR_REC1 0x0010 + +/* [5:0] DSP_MB_QP and [15:10] DSP_LPF_OFFSET */ +#define TW5864_DSP_QP 0x0018 +/* Define controls in register TW5864_DSP_QP */ +/* [5:0] H264 QP Value for codec */ +#define TW5864_DSP_MB_QP 0x003f +/* + * [15:10] H264 LPF_OFFSET Address + * (Default 0) + */ +#define TW5864_DSP_LPF_OFFSET 0xfc00 + +/* */ +#define TW5864_DSP_CODEC 0x001C +/* Define controls in register TW5864_DSP_CODEC */ +/* + * 0: Encode (TW5864 Default) + * 1: Decode + */ +#define TW5864_DSP_CODEC_MODE BIT(0) +/* + * 0->3 4 VLC data buffer in DDR (1M each) + * 0->7 8 VLC data buffer in DDR (512k each) + */ +#define TW5864_VLC_BUF_ID (7 << 2) +/* + * 0 4CIF in 1 MB + * 1 1CIF in 1 MB + */ +#define TW5864_CIF_MAP_MD BIT(6) +/* + * 0 2 falf D1 in 1 MB + * 1 1 half D1 in 1 MB + */ +#define TW5864_HD1_MAP_MD BIT(7) +/* VLC Stream valid */ +#define TW5864_VLC_VLD BIT(8) +/* MV Vector Valid */ +#define TW5864_MV_VECT_VLD BIT(9) +/* MV Flag Valid */ +#define TW5864_MV_FLAG_VLD BIT(10) + +/* */ +#define TW5864_DSP_SEN 0x0020 +/* Define controls in register TW5864_DSP_SEN */ +/* Org Buffer Base for Luma (default 0) */ +#define TW5864_DSP_SEN_PIC_LU 0x000f +/* Org Buffer Base for Chroma (default 4) */ +#define TW5864_DSP_SEN_PIC_CHM 0x00f0 +/* Maximum Number of Buffers (default 4) */ +#define TW5864_DSP_SEN_PIC_MAX 0x0700 +/* + * Original Frame D1 or HD1 switch + * (Default 0) + */ +#define TW5864_DSP_SEN_HFULL 0x1000 + +/* */ +#define TW5864_DSP_REF_PIC 0x0024 +/* Define controls in register TW5864_DSP_REF_PIC */ +/* Ref Buffer Base for Luma (default 0) */ +#define TW5864_DSP_REF_PIC_LU 0x000f +/* Ref Buffer Base for Chroma (default 4) */ +#define TW5864_DSP_REF_PIC_CHM 0x00f0 +/* Maximum Number of Buffers (default 4) */ +#define TW5864_DSP_REF_PIC_MAX 0x0700 + +/* + * [15:0] SEN_EN_CH[n] SENIF + * original frame capture enable for + * each channel + */ +#define TW5864_SEN_EN_CH 0x0028 + +/* */ +#define TW5864_DSP 0x002C +/* Define controls in register TW5864_DSP */ +/* + * The ID for channel selected for + * encoding operation + */ +#define TW5864_DSP_ENC_CHN 0x000f +/* See DSP_MB_DELAY below */ +#define TW5864_DSP_MB_WAIT 0x0010 +/* + * DSP Chroma Switch + * 0 DDRB + * 1 DDRA + */ +#define TW5864_DSP_CHROM_SW 0x0020 +/* VLC Flow Control: 1 for enable */ +#define TW5864_DSP_FLW_CNTL 0x0040 +/* + * If DSP_MB_WAIT == 0, MB delay is + * DSP_MB_DELAY * 16 + * If DSP_MB_DELAY == 1, MB delay is + * DSP_MB_DELAY * 128 + */ +#define TW5864_DSP_MB_DELAY 0x0f00 + +/* */ +#define TW5864_DDR 0x0030 +/* Define controls in register TW5864_DDR */ +/* DDR Single Access Page Number */ +#define TW5864_DDR_PAGE_CNTL 0x00ff +/* DDR-DPR Burst Read Enable */ +#define TW5864_DDR_BRST_EN BIT(13) +/* + * DDR A/B Select as HOST access + * 0 Select DDRA + * 1 Select DDRB + */ +#define TW5864_DDR_AB_SEL BIT(14) +/* + * DDR Access Mode Select + * 0 Single R/W Access (Host <-> DDR) + * 1 Burst R/W Access (Host <-> DPR) + */ +#define TW5864_DDR_MODE BIT(15) + +/* The original frame capture pointer. Two bits for each channel */ +/* SENIF_ORG_FRM_PTR [15:0] */ +#define TW5864_SENIF_ORG_FRM_PTR1 0x0038 +/* SENIF_ORG_FRM_PTR [31:16] */ +#define TW5864_SENIF_ORG_FRM_PTR2 0x003C + +#define TW5864_DSP_SEN_MODE 0x0040 +/* Define controls in register TW5864_DSP_SEN_MODE */ +#define TW5864_DSP_SEN_MODE_CH0 0x000f +#define TW5864_DSP_SEN_MODE_CH1 0x00f0 + +/* + * [15:0]: ENC_BUF_PTR_REC[31:16] + * Two bit for each channel (channel + * 8 ~ 15). Each two bits are the + * buffer pointer for the last + * encoded frame of a channel + */ +#define TW5864_ENC_BUF_PTR_REC2 0x004C + +/* Current MV Flag Status Pointer for Channel n. (Read only) */ +/* + * [1:0] CH0_MV_PTR, + * ..., [15:14] CH7_MV_PTR + */ +#define TW5864_CH_MV_PTR1 0x0060 +/* + * [1:0] CH8_MV_PTR, + * ..., [15:14] CH15_MV_PTR + */ +#define TW5864_CH_MV_PTR2 0x0064 + +/* + * [15:0] Reset Current MV Flag + * Status Pointer for Channel n + * (one bit each) + */ +#define TW5864_RST_MV_PTR 0x0068 +#define TW5864_INTERLACING 0x0200 +/* Define controls in register TW5864_INTERLACING */ +/* + * Inter_Mode Start. 2-nd bit? A + * guess. Missing in datasheet. + * Without this bit set, the output video is interlaced (stripy). + */ +#define TW5864_DSP_INTER_ST BIT(1) +/* Deinterlacer Enable */ +#define TW5864_DI_EN BIT(2) +/* + * De-interlacer Mode + * 1 Shuffled frame + * 0 Normal Un-Shuffled Frame + */ +#define TW5864_DI_MD BIT(3) +/* + * Down scale original frame in X direction + * 11: Un-used + * 10: down-sample to 1/4 + * 01: down-sample to 1/2 + * 00: down-sample disabled + */ +#define TW5864_DSP_DWN_X (3 << 4) +/* + * Down scale original frame in Y direction + * 11: Un-used + * 10: down-sample to 1/4 + * 01: down-sample to 1/2 + * 00: down-sample disabled + */ +#define TW5864_DSP_DWN_Y (3 << 6) +/* + * 1 Dual Stream + * 0 Single Stream + */ +#define TW5864_DUAL_STR BIT(8) + +#define TW5864_DSP_REF 0x0204 +/* Define controls in register TW5864_DSP_REF */ +/* Number of reference frame (Default 1 for TW5864B) */ +#define TW5864_DSP_REF_FRM 0x000f +/* Window size */ +#define TW5864_DSP_WIN_SIZE 0x02f0 + +#define TW5864_DSP_SKIP 0x0208 +/* Define controls in register TW5864_DSP_SKIP */ +/* + * Skip Offset Enable bit + * 0 DSP_SKIP_OFFSET value is not used (default 8) + * 1 DSP_SKIP_OFFSET value is used in HW + */ +#define TW5864_DSP_SKIP_OFEN 0x0080 +/* Skip mode cost offset (default 8) */ +#define TW5864_DSP_SKIP_OFFSET 0x007f + +#define TW5864_MOTION_SEARCH_ETC 0x020C +/* Define controls in register TW5864_MOTION_SEARCH_ETC */ +/* Enable quarter pel search mode */ +#define TW5864_QPEL_EN BIT(0) +/* Enable half pel search mode */ +#define TW5864_HPEL_EN BIT(1) +/* Enable motion search mode */ +#define TW5864_ME_EN BIT(2) +/* Enable Intra mode */ +#define TW5864_INTRA_EN BIT(3) +/* Enable Skip Mode */ +#define TW5864_SKIP_EN BIT(4) +/* Search Option (Default 2"b01) */ +#define TW5864_SRCH_OPT (3 << 5) + +#define TW5864_DSP_ENC_REC 0x0210 +/* Define controls in register TW5864_DSP_ENC_REC */ +/* Reference Buffer Pointer for encoding */ +#define TW5864_DSP_ENC_REF_PTR 0x0007 +/* Reconstruct Buffer pointer */ +#define TW5864_DSP_REC_BUF_PTR 0x7000 + +/* [15:0] Lambda Value for H264 */ +#define TW5864_DSP_REF_MVP_LAMBDA 0x0214 + +#define TW5864_DSP_PIC_MAX_MB 0x0218 +/* Define controls in register TW5864_DSP_PIC_MAX_MB */ +/* The MB number in Y direction for a frame */ +#define TW5864_DSP_PIC_MAX_MB_Y 0x007f +/* The MB number in X direction for a frame */ +#define TW5864_DSP_PIC_MAX_MB_X 0x7f00 + +/* The original frame pointer for encoding */ +#define TW5864_DSP_ENC_ORG_PTR_REG 0x021C +/* Mask to use with TW5864_DSP_ENC_ORG_PTR */ +#define TW5864_DSP_ENC_ORG_PTR_MASK 0x7000 +/* Number of bits to shift with TW5864_DSP_ENC_ORG_PTR */ +#define TW5864_DSP_ENC_ORG_PTR_SHIFT 12 + +/* DDR base address of OSD rectangle attribute data */ +#define TW5864_DSP_OSD_ATTRI_BASE 0x0220 +/* OSD enable bit for each channel */ +#define TW5864_DSP_OSD_ENABLE 0x0228 + +/* 0x0280 ~ 0x029C – Motion Vector for 1st 4x4 Block, e.g., 80 (X), 84 (Y) */ +#define TW5864_ME_MV_VEC1 0x0280 +/* 0x02A0 ~ 0x02BC – Motion Vector for 2nd 4x4 Block, e.g., A0 (X), A4 (Y) */ +#define TW5864_ME_MV_VEC2 0x02A0 +/* 0x02C0 ~ 0x02DC – Motion Vector for 3rd 4x4 Block, e.g., C0 (X), C4 (Y) */ +#define TW5864_ME_MV_VEC3 0x02C0 +/* 0x02E0 ~ 0x02FC – Motion Vector for 4th 4x4 Block, e.g., E0 (X), E4 (Y) */ +#define TW5864_ME_MV_VEC4 0x02E0 + +/* + * [5:0] + * if (intra16x16_cost < (intra4x4_cost+dsp_i4x4_offset)) + * Intra_mode = intra16x16_mode + * Else + * Intra_mode = intra4x4_mode + */ +#define TW5864_DSP_I4x4_OFFSET 0x040C + +/* + * [6:4] + * 0x5 Only 4x4 + * 0x6 Only 16x16 + * 0x7 16x16 & 4x4 + */ +#define TW5864_DSP_INTRA_MODE 0x0410 +#define TW5864_DSP_INTRA_MODE_SHIFT 4 +#define TW5864_DSP_INTRA_MODE_MASK (7 << TW5864_DSP_INTRA_MODE_SHIFT) +#define TW5864_DSP_INTRA_MODE_4x4 0x5 +#define TW5864_DSP_INTRA_MODE_16x16 0x6 +#define TW5864_DSP_INTRA_MODE_4x4_AND_16x16 0x7 +/* + * [5:0] + * WEIGHT Factor for I4x4 cost + * calculation (QP dependent) + */ +#define TW5864_DSP_I4x4_WEIGHT 0x0414 + +/* + * [7:0] + * Offset used to affect Intra/ME model decision + * If (me_cost < intra_cost + dsp_resid_mode_offset) + * Pred_Mode = me_mode + * Else + * Pred_mode = intra_mode + */ +#define TW5864_DSP_RESID_MODE_OFFSET 0x0604 + +/* 0x0800 ~ 0x09FF - Quantization TABLE Values */ +#define TW5864_QUAN_TAB 0x0800 + +/* Valid channel value [0; f], frame value [0; 3] */ +#define TW5864_RT_CNTR_CH_FRM(channel, frame) \ + (0x0C00 | (channel << 4) | (frame << 2)) + +/* */ +#define TW5864_FRAME_BUS1 0x0D00 +/* + * 1 Progressive in part A in bus n + * 0 Interlaced in part A in bus n + */ +#define TW5864_PROG_A BIT(0) +/* + * 1 Progressive in part B in bus n + * 0 Interlaced in part B in bus n + */ +#define TW5864_PROG_B BIT(1) +/* + * 1 Frame Mode in bus n + * 0 Field Mode in bus n + */ +#define TW5864_FRAME BIT(2) +/* + * 0 4CIF in bus n + * 1 1D1 + 4 CIF in bus n + * 2 2D1 in bus n + */ +#define TW5864_BUS_D1 (3 << 3) +/* Bus 1 goes in TW5864_FRAME_BUS1 in [4:0] */ +/* Bus 2 goes in TW5864_FRAME_BUS1 in [12:8] */ +/* */ +#define TW5864_FRAME_BUS2 0x0D04 +/* Bus 3 goes in TW5864_FRAME_BUS2 in [4:0] */ +/* Bus 4 goes in TW5864_FRAME_BUS2 in [12:8] */ + +/* [15:0] Horizontal Mirror for channel n */ +#define TW5864_SENIF_HOR_MIR 0x0D08 +/* [15:0] Vertical Mirror for channel n */ +#define TW5864_SENIF_VER_MIR 0x0D0C + +/* + * FRAME_WIDTH_BUSn_A 0x15F: 4 CIF + * 0x2CF: 1 D1 + 3 CIF + * 0x2CF: 2 D1 + * FRAME_WIDTH_BUSn_B 0x15F: 4 CIF + * 0x2CF: 1 D1 + 3 CIF + * 0x2CF: 2 D1 + * FRAME_HEIGHT_BUSn_A 0x11F: 4CIF (PAL) + * 0x23F: 1D1 + 3CIF (PAL) + * 0x23F: 2 D1 (PAL) + * 0x0EF: 4CIF (NTSC) + * 0x1DF: 1D1 + 3CIF (NTSC) + * 0x1DF: 2 D1 (NTSC) + * FRAME_HEIGHT_BUSn_B 0x11F: 4CIF (PAL) + * 0x23F: 1D1 + 3CIF (PAL) + * 0x23F: 2 D1 (PAL) + * 0x0EF: 4CIF (NTSC) + * 0x1DF: 1D1 + 3CIF (NTSC) + * 0x1DF: 2 D1 (NTSC) + */ +#define TW5864_FRAME_WIDTH_BUS_A(bus) (0x0D10 + 0x0010 * bus) +#define TW5864_FRAME_WIDTH_BUS_B(bus) (0x0D14 + 0x0010 * bus) +#define TW5864_FRAME_HEIGHT_BUS_A(bus) (0x0D18 + 0x0010 * bus) +#define TW5864_FRAME_HEIGHT_BUS_B(bus) (0x0D1C + 0x0010 * bus) + +/* + * 1: the bus mapped Channel n Full D1 + * 0: the bus mapped Channel n Half D1 + */ +#define TW5864_FULL_HALF_FLAG 0x0D50 + +/* + * 0 The bus mapped Channel select + * partA Mode + * 1 The bus mapped Channel select + * partB Mode + */ +#define TW5864_FULL_HALF_MODE_SEL 0x0D54 + +#define TW5864_VLC 0x1000 +/* Define controls in register TW5864_VLC */ +/* QP Value used by H264 CAVLC */ +#define TW5864_VLC_SLICE_QP 0x003f +/* + * Swap byte order of VLC stream in d-word. + * 1 Normal (VLC output= [31:0]) + * 0 Swap (VLC output={[23:16],[31:24],[7:0], [15:8]}) + */ +#define TW5864_VLC_BYTE_SWP BIT(6) +/* Enable Adding 03 circuit for VLC stream */ +#define TW5864_VLC_ADD03_EN BIT(7) +/* Number of bit for VLC bit Align */ +#define TW5864_VLC_BIT_ALIGN_SHIFT 8 +#define TW5864_VLC_BIT_ALIGN_MASK (0x1f << TW5864_VLC_BIT_ALIGN_SHIFT) +/* + * Synchronous Interface select for VLC Stream + * 1 CDC_VLCS_MAS read VLC stream + * 0 CPU read VLC stream + */ +#define TW5864_VLC_INF_SEL BIT(13) +/* Enable VLC overflow control */ +#define TW5864_VLC_OVFL_CNTL BIT(14) +/* + * 1 PCI Master Mode + * 0 Non PCI Master Mode + */ +#define TW5864_VLC_PCI_SEL BIT(15) +/* + * 0 Enable Adding 03 to VLC header and stream + * 1 Disable Adding 03 to VLC header of "00000001" + */ +#define TW5864_VLC_A03_DISAB BIT(16) +/* + * Status of VLC stream in DDR (one bit for each buffer) + * 1 VLC is ready in buffer n (HW set) + * 0 VLC is not ready in buffer n (SW clear) + */ +#define TW5864_VLC_BUF_RDY_SHIFT 24 +#define TW5864_VLC_BUF_RDY_MASK (0xff << TW5864_VLC_BUF_RDY_SHIFT) + +/* Total number of bit in the slice */ +#define TW5864_SLICE_TOTAL_BIT 0x1004 +/* Total number of bit in the residue */ +#define TW5864_RES_TOTAL_BIT 0x1008 + +#define TW5864_VLC_BUF 0x100C +/* Define controls in register TW5864_VLC_BUF */ +/* VLC BK0 full status, write ‘1’ to clear */ +#define TW5864_VLC_BK0_FULL BIT(0) +/* VLC BK1 full status, write ‘1’ to clear */ +#define TW5864_VLC_BK1_FULL BIT(1) +/* VLC end slice status, write ‘1’ to clear */ +#define TW5864_VLC_END_SLICE BIT(2) +/* VLC Buffer overflow status, write ‘1’ to clear */ +#define TW5864_DSP_RD_OF BIT(3) +/* VLC string length in either buffer 0 or 1 at end of frame */ +#define TW5864_VLC_STREAM_LEN_SHIFT 4 +#define TW5864_VLC_STREAM_LEN_MASK (0x1ff << TW5864_VLC_STREAM_LEN_SHIFT) + +/* [15:0] Total coefficient number in a frame */ +#define TW5864_TOTAL_COEF_NO 0x1010 +/* [0] VLC Encoder Interrupt. Write ‘1’ to clear */ +#define TW5864_VLC_DSP_INTR 0x1014 +/* [31:0] VLC stream CRC checksum */ +#define TW5864_VLC_STREAM_CRC 0x1018 + +#define TW5864_VLC_RD 0x101C +/* Define controls in register TW5864_VLC_RD */ +/* + * 1 Read VLC lookup Memory + * 0 Read VLC Stream Memory + */ +#define TW5864_VLC_RD_MEM BIT(0) +/* + * 1 Read VLC Stream Memory in burst mode + * 0 Read VLC Stream Memory in single mode + */ +#define TW5864_VLC_RD_BRST BIT(1) + +/* 0x2000 ~ 0x2FFC -- H264 Stream Memory Map */ +/* + * A word is 4 bytes. I.e., + * VLC_STREAM_MEM[0] address: 0x2000 + * VLC_STREAM_MEM[1] address: 0x2004 + * ... + * VLC_STREAM_MEM[3FF] address: 0x2FFC + */ +#define TW5864_VLC_STREAM_MEM_START 0x2000 +#define TW5864_VLC_STREAM_MEM_MAX_OFFSET 0x3ff +#define TW5864_VLC_STREAM_MEM(offset) (TW5864_VLC_STREAM_MEM_START + 4 * offset) + +/* 0x4000 ~ 0x4FFC -- Audio Register Map */ +/* [31:0] config 1ms cnt = Realtime clk/1000 */ +#define TW5864_CFG_1MS_CNT 0x4000 + +#define TW5864_ADPCM 0x4004 +/* Define controls in register TW5864_ADPCM */ +/* ADPCM decoder enable */ +#define TW5864_ADPCM_DEC BIT(0) +/* ADPCM input data enable */ +#define TW5864_ADPCM_IN_DATA BIT(1) +/* ADPCM encoder enable */ +#define TW5864_ADPCM_ENC BIT(2) + +#define TW5864_AUD 0x4008 +/* Define controls in register TW5864_AUD */ +/* + * Record path PCM Audio enable bit + * for each channel + */ +#define TW5864_AUD_ORG_CH_EN 0x00ff +/* Speaker path PCM Audio Enable */ +#define TW5864_SPK_ORG_EN BIT(16) +/* + * 0 16bit + * 1 8bit + */ +#define TW5864_AD_BIT_MODE BIT(17) +#define TW5864_AUD_TYPE_SHIFT 18 +/* + * 0 PCM + * 3 ADPCM + */ +#define TW5864_AUD_TYPE (0xf << TW5864_AUD_TYPE_SHIFT) +#define TW5864_AUD_SAMPLE_RATE_SHIFT 22 +/* + * 0 8K + * 1 16K + */ +#define TW5864_AUD_SAMPLE_RATE (3 << TW5864_AUD_SAMPLE_RATE_SHIFT) +/* Channel ID used to select audio channel (0 to 16) for loopback */ +#define TW5864_TESTLOOP_CHID_SHIFT 24 +#define TW5864_TESTLOOP_CHID (0x1f << TW5864_TESTLOOP_CHID_SHIFT) +/* Enable AD Loopback Test */ +#define TW5864_TEST_ADLOOP_EN BIT(30) +/* + * 0 Asynchronous Mode or PCI target mode + * 1 PCI Initiator Mode + */ +#define TW5864_AUD_MODE BIT(31) + +#define TW5864_AUD_ADPCM 0x400C +/* Define controls in register TW5864_AUD_ADPCM */ +/* + * Record path ADPCM audio channel + * enable, one bit for each + */ +#define TW5864_AUD_ADPCM_CH_EN 0x00ff +/* Speaker path ADPCM audio channel enable */ +#define TW5864_SPK_ADPCM_EN BIT(16) + +#define TW5864_PC_BLOCK_ADPCM_RD_NO 0x4018 +#define TW5864_PC_BLOCK_ADPCM_RD_NO_MASK 0x1f + +/* + * For ADPCM_ENC_WR_PTR, ADPCM_ENC_RD_PTR (see below): + * Bit[2:0] ch0 + * Bit[5:3] ch1 + * Bit[8:6] ch2 + * Bit[11:9] ch3 + * Bit[14:12] ch4 + * Bit[17:15] ch5 + * Bit[20:18] ch6 + * Bit[23:21] ch7 + * Bit[26:24] ch8 + * Bit[29:27] ch9 + * Bit[32:30] ch10 + * Bit[35:33] ch11 + * Bit[38:36] ch12 + * Bit[41:39] ch13 + * Bit[44:42] ch14 + * Bit[47:45] ch15 + * Bit[50:48] ch16 + */ +#define TW5864_ADPCM_ENC_XX_MASK 0x3fff +#define TW5864_ADPCM_ENC_XX_PTR2_SHIFT 30 +/* ADPCM_ENC_WR_PTR[29:0] */ +#define TW5864_ADPCM_ENC_WR_PTR1 0x401C +/* ADPCM_ENC_WR_PTR[50:30] */ +#define TW5864_ADPCM_ENC_WR_PTR2 0x4020 + +/* ADPCM_ENC_RD_PTR[29:0] */ +#define TW5864_ADPCM_ENC_RD_PTR1 0x4024 +/* ADPCM_ENC_RD_PTR[50:30] */ +#define TW5864_ADPCM_ENC_RD_PTR2 0x4028 + +/* + * [3:0] rd ch0, [7:4] rd ch1, + * [11:8] wr ch0, [15:12] wr ch1 + */ +#define TW5864_ADPCM_DEC_RD_WR_PTR 0x402C + +/* + * For TW5864_AD_ORIG_WR_PTR, TW5864_AD_ORIG_RD_PTR: + * Bit[3:0] ch0 + * Bit[7:4] ch1 + * Bit[11:8] ch2 + * Bit[15:12] ch3 + * Bit[19:16] ch4 + * Bit[23:20] ch5 + * Bit[27:24] ch6 + * Bit[31:28] ch7 + * Bit[35:32] ch8 + * Bit[39:36] ch9 + * Bit[43:40] ch10 + * Bit[47:44] ch11 + * Bit[51:48] ch12 + * Bit[55:52] ch13 + * Bit[59:56] ch14 + * Bit[63:60] ch15 + * Bit[67:64] ch16 + */ +/* AD_ORIG_WR_PTR[31:0] */ +#define TW5864_AD_ORIG_WR_PTR1 0x4030 +/* AD_ORIG_WR_PTR[63:32] */ +#define TW5864_AD_ORIG_WR_PTR2 0x4034 +/* AD_ORIG_WR_PTR[67:64] */ +#define TW5864_AD_ORIG_WR_PTR3 0x4038 + +/* AD_ORIG_RD_PTR[31:0] */ +#define TW5864_AD_ORIG_RD_PTR1 0x403C +/* AD_ORIG_RD_PTR[63:32] */ +#define TW5864_AD_ORIG_RD_PTR2 0x4040 +/* AD_ORIG_RD_PTR[67:64] */ +#define TW5864_AD_ORIG_RD_PTR3 0x4044 + +#define TW5864_PC_BLOCK_ORIG_RD_NO 0x4048 +#define TW5864_PC_BLOCK_ORIG_RD_NO_MASK 0x1f + +#define TW5864_PCI_AUD 0x404C +/* Define controls in register TW5864_PCI_AUD */ +/* + * The register is applicable to PCI + * initiator mode only. Used to + * select PCM(0) or ADPCM(1) audio + * data sent to PC. One bit for each + * channel + */ +#define TW5864_PCI_DATA_SEL 0xffff +/* + * Audio flow control mode selection bit. + * 0 Flow control disabled. TW5864 + * continuously sends audio frame to + * PC (initiator mode) + * 1 Flow control enabled + */ +#define TW5864_PCI_FLOW_EN BIT(16) +/* + * When PCI_FLOW_EN is set, PCI need + * to toggle this bit to send an + * audio frame to PC. One toggle to + * send one frame. + */ +#define TW5864_PCI_AUD_FRM_EN BIT(17) + +/* [1:0] CS valid to data valid CLK cycles when writing operation */ +#define TW5864_CS2DAT_CNT 0x8000 +/* [2:0] Data valid signal width by system clock cycles */ +#define TW5864_DATA_VLD_WIDTH 0x8004 + +#define TW5864_SYNC 0x8008 +/* Define controls in register TW5864_SYNC */ +/* + * 0 vlc stream to syncrous port + * 1 vlc stream to ddr buffers + */ +#define TW5864_SYNC_CFG BIT(7) +/* + * 0 SYNC Address sampled on Rising edge + * 1 SYNC Address sampled on Falling edge + */ +#define TW5864_SYNC_ADR_EDGE BIT(0) +#define TW5864_VLC_STR_DELAY_SHIFT 1 +/* + * 0 No system delay + * 1 One system clock delay + * 2 Two system clock delay + * 3 Three system clock delay + */ +#define TW5864_VLC_STR_DELAY (3 << TW5864_VLC_STR_DELAY_SHIFT) +/* + * 0 Rising edge output + * 1 Falling edge output + */ +#define TW5864_VLC_OUT_EDGE BIT(3) + +/* + * [1:0] + * 2’b00 phase set to 180 degree + * 2’b01 phase set to 270 degree + * 2’b10 phase set to 0 degree + * 2’b11 phase set to 90 degree + */ +#define TW5864_I2C_PHASE_CFG 0x800C + +/* + * The system / DDR clock (166 MHz) is generated with an on-chip system clock + * PLL (SYSPLL) using input crystal clock of 27 MHz. The system clock PLL + * frequency is controlled with the following equation. + * CLK_OUT = CLK_IN * (M+1) / ((N+1) * P) + * SYSPLL_M M parameter + * SYSPLL_N N parameter + * SYSPLL_P P parameter + */ +/* SYSPLL_M[7:0] */ +#define TW5864_SYSPLL1 0x8018 +/* Define controls in register TW5864_SYSPLL1 */ +#define TW5864_SYSPLL_M_LOW 0x00ff + +/* [2:0]: SYSPLL_M[10:8], [7:3]: SYSPLL_N[4:0] */ +#define TW5864_SYSPLL2 0x8019 +/* Define controls in register TW5864_SYSPLL2 */ +#define TW5864_SYSPLL_M_HI 0x07 +#define TW5864_SYSPLL_N_LOW_SHIFT 3 +#define TW5864_SYSPLL_N_LOW (0x1f << TW5864_SYSPLL_N_LOW_SHIFT) + +/* + * [1:0]: SYSPLL_N[6:5], [3:2]: SYSPLL_P, [4]: SYSPLL_IREF, + * [7:5]: SYSPLL_CP_SEL + */ +#define TW5864_SYSPLL3 0x8020 +/* Define controls in register TW5864_SYSPLL3 */ +#define TW5864_SYSPLL_N_HI 0x03 +#define TW5864_SYSPLL_P_SHIFT 2 +#define TW5864_SYSPLL_P (0x03 << TW5864_SYSPLL_P_SHIFT) +/* + * SYSPLL bias current control + * 0 Lower current (default) + * 1 30% higher current + */ +#define TW5864_SYSPLL_IREF BIT(4) +/* + * SYSPLL charge pump current selection + * 0 1,5 uA + * 1 4 uA + * 2 9 uA + * 3 19 uA + * 4 39 uA + * 5 79 uA + * 6 159 uA + * 7 319 uA + */ +#define TW5864_SYSPLL_CP_SEL_SHIFT 5 +#define TW5864_SYSPLL_CP_SEL (0x07 << TW5864_SYSPLL_CP_SEL_SHIFT) + +/* + * [1:0]: SYSPLL_VCO, [3:2]: SYSPLL_LP_X8, [5:4]: SYSPLL_ICP_SEL, [6]: + * SYSPLL_LPF_5PF, [7]: SYSPLL_ED_SEL + */ +#define TW5864_SYSPLL4 0x8021 +/* Define controls in register TW5864_SYSPLL4 */ +/* + * SYSPLL_VCO VCO Range selection + * 00 5 ~ 75 MHz + * 01 50 ~ 140 MHz + * 10 110 ~ 320 MHz + * 11 270 ~ 700 MHz + */ +#define TW5864_SYSPLL_VCO 0x03 +#define TW5864_SYSPLL_LP_X8_SHIFT 2 +/* + * Loop resister + * 0 38.5K ohms + * 1 6.6K ohms (default) + * 2 2.2K ohms + * 3 1.1K ohms + */ +#define TW5864_SYSPLL_LP_X8 (0x03 << TW5864_SYSPLL_LP_X8_SHIFT) +#define TW5864_SYSPLL_ICP_SEL_SHIFT 4 +/* + * PLL charge pump fine tune + * 00 x1 (default) + * 01 x1/2 + * 10 x1/7 + * 11 x1/8 + */ +#define TW5864_SYSPLL_ICP_SEL (0x03 << TW5864_SYSPLL_ICP_SEL_SHIFT) +/* + * PLL low pass filter phase margin adjustment + * 0 no 5pF (default) + * 1 5pF added + */ +#define TW5864_SYSPLL_LPF_5PF BIT(6) +/* + * PFD select edge for detection + * 0 Falling edge (default) + * 1 Rising edge + */ +#define TW5864_SYSPLL_ED_SEL BIT(7) + +/* [0]: SYSPLL_RST, [4]: SYSPLL_PD */ +#define TW5864_SYSPLL5 0x8024 +/* Define controls in register TW5864_SYSPLL5 */ +/* Reset SYSPLL */ +#define TW5864_SYSPLL_RST BIT(0) +/* Power down SYSPLL */ +#define TW5864_SYSPLL_PD BIT(4) + +#define TW5864_PLL_CFG 0x801C +/* Define controls in register TW5864_PLL_CFG */ +/* + * Issue Soft Reset from Async Host Interface / PCI Interface clock domain. + * Become valid after sync to the xtal clock domain. This bit is set only if + * LOAD register bit is also set to 1. + */ +#define TW5864_SRST BIT(0) +/* + * Issue SYSPLL (166 MHz) configuration latch from Async host interface / PCI + * Interface clock domain. The configuration setting becomes effective only if + * LOAD register bit is also set to 1. + */ +#define TW5864_SYSPLL_CFG BIT(2) +/* + * Issue SPLL (108 MHz) configuration load from Async host interface / PCI + * Interface clock domain. The configuration setting becomes effective only if + * the LOAD register bit is also set to 1. + */ +#define TW5864_SPLL_CFG BIT(4) +/* + * Set this bit to latch the SRST, SYSPLL_CFG, SPLL_CFG setting into the xtal + * clock domain to restart the PLL. This bit is self cleared. + */ +#define TW5864_LOAD BIT(3) + +/* SPLL_IREF, SPLL_LPX4, SPLL_CPX4, SPLL_PD, SPLL_DBG */ +#define TW5864_SPLL 0x8028 + +/* 0x8800 ~ 0x88FC -- Interrupt Register Map */ +/* + * Trigger mode of interrupt source 0 ~ 15 + * 1 Edge trigger mode + * 0 Level trigger mode + */ +#define TW5864_TRIGGER_MODE_L 0x8800 +/* Trigger mode of interrupt source 16 ~ 31 */ +#define TW5864_TRIGGER_MODE_H 0x8804 +/* Enable of interrupt source 0 ~ 15 */ +#define TW5864_INTR_ENABLE_L 0x8808 +/* Enable of interrupt source 16 ~ 31 */ +#define TW5864_INTR_ENABLE_H 0x880C +/* Clear interrupt command of interrupt source 0 ~ 15 */ +#define TW5864_INTR_CLR_L 0x8810 +/* Clear interrupt command of interrupt source 16 ~ 31 */ +#define TW5864_INTR_CLR_H 0x8814 +/* + * Assertion of interrupt source 0 ~ 15 + * 1 High level or pos-edge is assertion + * 0 Low level or neg-edge is assertion + */ +#define TW5864_INTR_ASSERT_L 0x8818 +/* Assertion of interrupt source 16 ~ 31 */ +#define TW5864_INTR_ASSERT_H 0x881C +/* + * Output level of interrupt + * 1 Interrupt output is high assertion + * 0 Interrupt output is low assertion + */ +#define TW5864_INTR_OUT_LEVEL 0x8820 +/* + * Status of interrupt source 0 ~ 15 + * Bit[0]: VLC 4k RAM interrupt + * Bit[1]: BURST DDR RAM interrupt + * Bit[2]: MV DSP interrupt + * Bit[3]: video lost interrupt + * Bit[4]: gpio 0 interrupt + * Bit[5]: gpio 1 interrupt + * Bit[6]: gpio 2 interrupt + * Bit[7]: gpio 3 interrupt + * Bit[8]: gpio 4 interrupt + * Bit[9]: gpio 5 interrupt + * Bit[10]: gpio 6 interrupt + * Bit[11]: gpio 7 interrupt + * Bit[12]: JPEG interrupt + * Bit[13:15]: Reserved + */ +#define TW5864_INTR_STATUS_L 0x8838 +/* + * Status of interrupt source 16 ~ 31 + * Bit[0]: Reserved + * Bit[1]: VLC done interrupt + * Bit[2]: Reserved + * Bit[3]: AD Vsync interrupt + * Bit[4]: Preview eof interrupt + * Bit[5]: Preview overflow interrupt + * Bit[6]: Timer interrupt + * Bit[7]: Reserved + * Bit[8]: Audio eof interrupt + * Bit[9]: I2C done interrupt + * Bit[10]: AD interrupt + * Bit[11:15]: Reserved + */ +#define TW5864_INTR_STATUS_H 0x883C + +/* Defines of interrupt bits, united for both low and high word registers */ +#define TW5864_INTR_VLC_RAM BIT(0) +#define TW5864_INTR_BURST BIT(1) +#define TW5864_INTR_MV_DSP BIT(2) +#define TW5864_INTR_VIN_LOST BIT(3) +/* n belongs to [0; 7] */ +#define TW5864_INTR_GPIO(n) (1 << (4 + n)) +#define TW5864_INTR_JPEG BIT(12) +#define TW5864_INTR_VLC_DONE BIT(17) +#define TW5864_INTR_AD_VSYNC BIT(19) +#define TW5864_INTR_PV_EOF BIT(20) +#define TW5864_INTR_PV_OVERFLOW BIT(21) +#define TW5864_INTR_TIMER BIT(22) +#define TW5864_INTR_AUD_EOF BIT(24) +#define TW5864_INTR_I2C_DONE BIT(25) +#define TW5864_INTR_AD BIT(26) + +/* 0x9000 ~ 0x920C -- Video Capture (VIF) Register Map */ +/* + * H264EN_CH_STATUS[n] Status of + * Vsync synchronized H264EN_CH_EN + * (Read Only) + * 1 Channel Enabled + * 0 Channel Disabled + */ +#define TW5864_H264EN_CH_STATUS 0x9000 +/* + * [15:0] H264EN_CH_EN[n] H264 + * Encoding Path Enable for channel + * 1 Channel Enabled + * 0 Channel Disabled + */ +#define TW5864_H264EN_CH_EN 0x9004 +/* + * H264EN_CH_DNS[n] H264 Encoding + * Path Downscale Video Decoder + * Input for channel n + * 1 Downscale Y to 1/2 + * 0 Does not downscale + */ +#define TW5864_H264EN_CH_DNS 0x9008 +/* + * H264EN_CH_PROG[n] H264 Encoding + * Path channel n is progressive + * 1 Progressive (Not valid for TW5864) + * 0 Interlaced (TW5864 default) + */ +#define TW5864_H264EN_CH_PROG 0x900C +/* + * [3:0] H264EN_BUS_MAX_CH[n] + * H264 Encoding Path maximum number + * of channel on BUS n + * 0 Max 4 channels + * 1 Max 2 channels + */ +#define TW5864_H264EN_BUS_MAX_CH 0x9010 + +/* + * H264EN_RATE_MAX_LINE_n H264 Encoding path Rate Mapping Maximum Line Number + * on Bus n + */ +#define TW5864_H264EN_RATE_MAX_LINE_EVEN 0x1f +#define TW5864_H264EN_RATE_MAX_LINE_ODD_SHIFT 5 +#define TW5864_H264EN_RATE_MAX_LINE_ODD \ + (0x1f << TW5864_H264EN_RATE_MAX_LINE_ODD_SHIFT) +/* + * [4:0] H264EN_RATE_MAX_LINE_0 + * [9:5] H264EN_RATE_MAX_LINE_1 + */ +#define TW5864_H264EN_RATE_MAX_LINE_REG1 0x9014 +/* + * [4:0] H264EN_RATE_MAX_LINE_2 + * [9:5] H264EN_RATE_MAX_LINE_3 + */ +#define TW5864_H264EN_RATE_MAX_LINE_REG2 0x9018 + +/* + * H264EN_CHn_FMT H264 Encoding Path Format configuration of Channel n + * 00 D1 (For D1 and hD1 frame) + * 01 (Reserved) + * 10 (Reserved) + * 11 D1 with 1/2 size in X (for CIF frame) + * Note: To be used with 0x9008 register to configure the frame size + */ +/* + * [1:0]: H264EN_CH0_FMT, + * ..., [15:14]: H264EN_CH7_FMT + */ +#define TW5864_H264EN_CH_FMT_REG1 0x9020 +/* + * [1:0]: H264EN_CH8_FMT (?), + * ..., [15:14]: H264EN_CH15_FMT (?) + */ +#define TW5864_H264EN_CH_FMT_REG2 0x9024 + +/* + * H264EN_RATE_CNTL_BUSm_CHn H264 Encoding Path BUS m Rate Control for Channel n + */ +#define TW5864_H264EN_RATE_CNTL_LO_WORD(bus, channel) \ + (0x9100 + bus * 0x20 + channel * 0x08) +#define TW5864_H264EN_RATE_CNTL_HI_WORD(bus, channel) \ + (0x9104 + bus * 0x20 + channel * 0x08) + +/* + * H264EN_BUSm_MAP_CHn The 16-to-1 MUX configuration register for each encoding + * channel (total of 16 channels). Four bits for each channel. + */ +#define TW5864_H264EN_BUS0_MAP 0x9200 +#define TW5864_H264EN_BUS1_MAP 0x9204 +#define TW5864_H264EN_BUS2_MAP 0x9208 +#define TW5864_H264EN_BUS3_MAP 0x920C + +/* This register is not defined in datasheet, but used in reference driver */ +#define TW5864_UNDEFINED_ERROR_FLAGS_0x9218 0x9218 + +#define TW5864_GPIO1 0x9800 +#define TW5864_GPIO2 0x9804 +/* Define controls in registers TW5864_GPIO1, TW5864_GPIO2 */ +/* GPIO DATA of Group n */ +#define TW5864_GPIO_DATA 0x00ff +#define TW5864_GPIO_OEN_SHIFT 8 +/* GPIO Output Enable of Group n */ +#define TW5864_GPIO_OEN (0xff << TW5864_GPIO_OEN_SHIFT) + +/* 0xA000 ~ 0xA8FF – DDR Controller Register Map */ +/* DDR Controller A */ +/* + * [2:0] Data valid counter after + * read command to DDR. This is the + * delay value to show how many + * cycles the data will be back from + * DDR after we issue a read + * command. + */ +#define TW5864_RD_ACK_VLD_MUX 0xA000 + +#define TW5864_DDR_PERIODS 0xA004 +/* Define controls in register TW5864_DDR_PERIODS */ +/* + * Tras value, the minimum cycle of + * active to precharge command + * period, default is 7 + */ +#define TW5864_TRAS_CNT_MAX 0x000f +/* Trfc value, the minimum cycle of refresh to active or refresh command + * period, default is 4"hf + */ +#define TW5864_RFC_CNT_MAX_SHIFT 8 +#define TW5864_RFC_CNT_MAX (0x0f << TW5864_RFC_CNT_MAX_SHIFT) +/* Trcd value, the minimum cycle of active to internal read/write command + * period, default is 4"h2 + */ +#define TW5864_TCD_CNT_MAX_SHIFT 4 +#define TW5864_TCD_CNT_MAX (0x0f << TW5864_TCD_CNT_MAX_SHIFT) +/* Twr value, write recovery time, default is 4"h3 */ +#define TW5864_TWR_CNT_MAX_SHIFT 12 +#define TW5864_TWR_CNT_MAX (0x0f << TW5864_TWR_CNT_MAX_SHIFT) + +/* + * [2:0] CAS latency, the delay + * cycle between internal read + * command and the availability of + * the first bit of output data, + * default is 3 + */ +#define TW5864_CAS_LATENCY 0xA008 +/* + * [15:0] Maximum average periodic + * refresh, the value is based on + * the current frequency to match + * 7.8mcs + */ +#define TW5864_DDR_REF_CNTR_MAX 0xA00C +/* + * DDR_ON_CHIP_MAP [1:0] 0x0 256M DDR on board + * 0x1 512M DDR on board + * 0x2 1G DDR on board + * DDR_ON_CHIP_MAP [2] 0x0 Only one DDR chip + * 0x1 Two DDR chips + */ +#define TW5864_DDR_ON_CHIP_MAP 0xA01C +#define TW5864_DDR_SELFTEST_MODE 0xA020 +/* Define controls in register TW5864_DDR_SELFTEST_MODE */ +/* + * 0 Common read/write mode + * 1 DDR self-test mode + */ +#define TW5864_MASTER_MODE BIT(0) +/* + * 0 DDR self-test single read/write + * 1 DDR self-test burst read/write + */ +#define TW5864_SINGLE_PROC BIT(1) +/* + * 0 DDR self-test write command + * 1 DDR self-test read command + */ +#define TW5864_WRITE_FLAG BIT(2) +#define TW5864_DATA_MODE_SHIFT 4 +/* + * 0x0 write 32'haaaa5555 to DDR + * 0x1 write 32'hffffffff to DDR + * 0x2 write 32'hha5a55a5a to DDR + * 0x3 write increasing data to DDR + */ +#define TW5864_DATA_MODE (0x3 << TW5864_DATA_MODE_SHIFT) + +/* [7:0] The maximum data of one burst in DDR self-test mode */ +#define TW5864_BURST_CNTR_MAX 0xA024 +/* [15:0] The maximum burst counter (bit 15~0) in DDR self-test mode */ +#define TW5864_DDR_PROC_CNTR_MAX_L 0xA028 +/* The maximum burst counter (bit 31~16) in DDR self-test mode */ +#define TW5864_DDR_PROC_CNTR_MAX_H 0xA02C +/* [0]: Start one DDR self-test */ +#define TW5864_DDR_SELF_TEST_CMD 0xA030 +/* The maximum error counter (bit 15 ~ 0) in DDR self-test */ +#define TW5864_ERR_CNTR_L 0xA034 + +#define TW5864_ERR_CNTR_H_AND_FLAG 0xA038 +/* Define controls in register TW5864_ERR_CNTR_H_AND_FLAG */ +/* The maximum error counter (bit 30 ~ 16) in DDR self-test */ +#define TW5864_ERR_CNTR_H_MASK 0x3fff +/* DDR self-test end flag */ +#define TW5864_END_FLAG 0x8000 + +/* + * DDR Controller B: same as 0xA000 ~ 0xA038, + * but add TW5864_DDR_B_OFFSET to all addresses + */ +#define TW5864_DDR_B_OFFSET 0x0800 + +/* 0xB004 ~ 0xB018 – HW version/ARB12 Register Map */ +/* [15:0] Default is C013 */ +#define TW5864_HW_VERSION 0xB004 + +#define TW5864_REQS_ENABLE 0xB010 +/* Define controls in register TW5864_REQS_ENABLE */ +/* Audio data in to DDR enable (default 1) */ +#define TW5864_AUD_DATA_IN_ENB BIT(0) +/* Audio encode request to DDR enable (default 1) */ +#define TW5864_AUD_ENC_REQ_ENB BIT(1) +/* Audio decode request0 to DDR enable (default 1) */ +#define TW5864_AUD_DEC_REQ0_ENB BIT(2) +/* Audio decode request1 to DDR enable (default 1) */ +#define TW5864_AUD_DEC_REQ1_ENB BIT(3) +/* VLC stream request to DDR enable (default 1) */ +#define TW5864_VLC_STRM_REQ_ENB BIT(4) +/* H264 MV request to DDR enable (default 1) */ +#define TW5864_DVM_MV_REQ_ENB BIT(5) +/* mux_core MVD request to DDR enable (default 1) */ +#define TW5864_MVD_REQ_ENB BIT(6) +/* mux_core MVD temp data request to DDR enable (default 1) */ +#define TW5864_MVD_TMP_REQ_ENB BIT(7) +/* JPEG request to DDR enable (default 1) */ +#define TW5864_JPEG_REQ_ENB BIT(8) +/* mv_flag request to DDR enable (default 1) */ +#define TW5864_MV_FLAG_REQ_ENB BIT(9) + +#define TW5864_ARB12 0xB018 +/* Define controls in register TW5864_ARB12 */ +/* ARB12 Enable (default 1) */ +#define TW5864_ARB12_ENB BIT(15) +/* ARB12 maximum value of time out counter (default 15"h1FF) */ +#define TW5864_ARB12_TIME_OUT_CNT 0x7fff + +/* 0xB800 ~ 0xB80C -- Indirect Access Register Map */ +/* + * In order to access the indirect register space, + * the following procedure is followed. + * + * Write Registers: + * (1) Write IND_DATA at 0xB804 ~ 0xB807 + * (2) Read BUSY flag from 0xB803. Wait until BUSY signal is 0. + * (3) Write IND_ADDR at 0xB800 ~ 0xB801. Set R/W to "1", ENABLE to "1" + * Read Registers: + * (1) Read BUSY flag from 0xB803. Wait until BUSY signal is 0. + * (2) Write IND_ADDR at 0xB800 ~ 0xB801. Set R/W to "0", ENABLE to "1" + * (3) Read BUSY flag from 0xB803. Wait until BUSY signal is 0. + * (4) Read IND_DATA from 0xB804 ~ 0xB807 + */ +#define TW5864_IND_CTL 0xB800 +/* Define controls in register TW5864_IND_CTL */ +/* Address used to access indirect register space */ +#define TW5864_IND_ADDR 0x0000ffff +/* Wait until this bit is "0" before using indirect access */ +#define TW5864_BUSY BIT(31) +/* Activate the indirect access. This bit is self cleared */ +#define TW5864_ENABLE BIT(25) +/* Read/Write command */ +#define TW5864_RW BIT(24) + +/* [31:0] Data used to read/write indirect register space */ +#define TW5864_IND_DATA 0xB804 + +/* 0xC000 ~ 0xC7FC -- Preview Register Map */ +/* Mostly skipped this section. */ +/* + * [15:0] Status of Vsync Synchronized PCI_PV_CH_EN (Read Only) + * 1 Channel Enabled + * 0 Channel Disabled + */ +#define TW5864_PCI_PV_CH_STATUS 0xC000 +/* + * [15:0] PCI Preview Path Enable for channel n + * 1 Channel Enable + * 0 Channel Disable + */ +#define TW5864_PCI_PV_CH_EN 0xC004 + +/* 0xC800 ~ 0xC804 -- JPEG Capture Register Map */ +/* Skipped. */ +/* 0xD000 ~ 0xD0FC -- JPEG Control Register Map */ +/* Skipped. */ + +/* 0xE000 ~ 0xFC04 – Motion Vector Register Map */ + +/* ME Motion Vector data (Four Byte Each) 0xE000 ~ 0xE7FC */ +#define TW5864_ME_MV_VEC_START 0xE000 +#define TW5864_ME_MV_VEC_MAX_OFFSET 0x1ff +#define TW5864_ME_MV_VEC(offset) (TW5864_ME_MV_VEC_START + 4 * offset) + +#define TW5864_MV 0xFC00 +/* Define controls in register TW5864_MV */ +/* mv bank0 full status , write "1" to clear */ +#define TW5864_MV_BK0_FULL BIT(0) +/* mv bank1 full status , write "1" to clear */ +#define TW5864_MV_BK1_FULL BIT(1) +/* slice end status; write "1" to clear */ +#define TW5864_MV_EOF BIT(2) +/* mv encode interrupt status; write "1" to clear */ +#define TW5864_MV_DSP_INTR BIT(3) +/* mv write memory overflow, write "1" to clear */ +#define TW5864_DSP_WR_OF BIT(4) +#define TW5864_MV_LEN_SHIFT 5 +/* mv stream length */ +#define TW5864_MV_LEN (0xff << TW5864_MV_LEN_SHIFT) +/* The configured status bit written into bit 15 of 0xFC04 */ +#define TW5864_MPI_DDR_SEL BIT(13) + +#define TW5864_MPI_DDR_SEL_REG 0xFC04 +/* Define controls in register TW5864_MPI_DDR_SEL_REG */ +/* + * SW configure register + * 0 MV is saved in internal DPR + * 1 MV is saved in DDR + */ +#define TW5864_MPI_DDR_SEL2 BIT(15) + +/* 0x18000 ~ 0x181FC – PCI Master/Slave Control Map */ +#define TW5864_PCI_INTR_STATUS 0x18000 +/* Define controls in register TW5864_PCI_INTR_STATUS */ +/* vlc done */ +#define TW5864_VLC_DONE_INTR BIT(1) +/* ad vsync */ +#define TW5864_AD_VSYNC_INTR BIT(3) +/* preview eof */ +#define TW5864_PREV_EOF_INTR BIT(4) +/* preview overflow interrupt */ +#define TW5864_PREV_OVERFLOW_INTR BIT(5) +/* timer interrupt */ +#define TW5864_TIMER_INTR BIT(6) +/* audio eof */ +#define TW5864_AUDIO_EOF_INTR BIT(8) +/* IIC done */ +#define TW5864_IIC_DONE_INTR BIT(24) +/* ad interrupt (e.g.: video lost, video format changed) */ +#define TW5864_AD_INTR_REG BIT(25) + +#define TW5864_PCI_INTR_CTL 0x18004 +/* Define controls in register TW5864_PCI_INTR_CTL */ +/* master enable */ +#define TW5864_PCI_MAST_ENB BIT(0) +/* mvd&vlc master enable */ +#define TW5864_MVD_VLC_MAST_ENB 0x06 +/* (Need to set 0 in TW5864A) */ +#define TW5864_AD_MAST_ENB BIT(3) +/* preview master enable */ +#define TW5864_PREV_MAST_ENB BIT(4) +/* preview overflow enable */ +#define TW5864_PREV_OVERFLOW_ENB BIT(5) +/* timer interrupt enable */ +#define TW5864_TIMER_INTR_ENB BIT(6) +/* JPEG master (push mode) enable */ +#define TW5864_JPEG_MAST_ENB BIT(7) +#define TW5864_AU_MAST_ENB_CHN_SHIFT 8 +/* audio master channel enable */ +#define TW5864_AU_MAST_ENB_CHN (0xffff << TW5864_AU_MAST_ENB_CHN_SHIFT) +/* IIC interrupt enable */ +#define TW5864_IIC_INTR_ENB BIT(24) +/* ad interrupt enable */ +#define TW5864_AD_INTR_ENB BIT(25) +/* target burst enable */ +#define TW5864_PCI_TAR_BURST_ENB BIT(26) +/* vlc stream burst enable */ +#define TW5864_PCI_VLC_BURST_ENB BIT(27) +/* ddr burst enable (1 enable, and must set DDR_BRST_EN) */ +#define TW5864_PCI_DDR_BURST_ENB BIT(28) + +/* + * Because preview and audio have 16 channels separately, so using this + * registers to indicate interrupt status for every channels. This is secondary + * interrupt status register. OR operating of the PREV_INTR_REG is + * PREV_EOF_INTR, OR operating of the AU_INTR_REG bits is AUDIO_EOF_INTR + */ +#define TW5864_PREV_AND_AU_INTR 0x18008 +/* Define controls in register TW5864_PREV_AND_AU_INTR */ +/* preview eof interrupt flag */ +#define TW5864_PREV_INTR_REG 0x0000ffff +#define TW5864_AU_INTR_REG_SHIFT 16 +/* audio eof interrupt flag */ +#define TW5864_AU_INTR_REG (0xffff << TW5864_AU_INTR_REG_SHIFT) + +#define TW5864_MASTER_ENB_REG 0x1800C +/* Define controls in register TW5864_MASTER_ENB_REG */ +/* master enable */ +#define TW5864_PCI_VLC_INTR_ENB BIT(1) +/* mvd and vlc master enable */ +#define TW5864_PCI_PREV_INTR_ENB BIT(4) +/* ad vsync master enable */ +#define TW5864_PCI_PREV_OF_INTR_ENB BIT(5) +/* jpeg master enable */ +#define TW5864_PCI_JPEG_INTR_ENB BIT(7) +/* preview master enable */ +#define TW5864_PCI_AUD_INTR_ENB BIT(8) + +/* + * Every channel of preview and audio have ping-pong buffers in system memory, + * this register is the buffer flag to notify software which buffer is been + * operated. + */ +#define TW5864_PREV_AND_AU_BUF_FLAG 0x18010 +/* Define controls in register TW5864_PREV_AND_AU_BUF_FLAG */ +/* preview buffer A/B flag */ +#define TW5864_PREV_BUF_FLAG 0xffff +#define TW5864_AUDIO_BUF_FLAG_SHIFT 16 +/* audio buffer A/B flag */ +#define TW5864_AUDIO_BUF_FLAG (0xffff << TW5864_AUDIO_BUF_FLAG_SHIFT) + +#define TW5864_IIC 0x18014 +/* Define controls in register TW5864_IIC */ +/* register data */ +#define TW5864_IIC_DATA 0x00ff +#define TW5864_IIC_REG_ADDR_SHIFT 8 +/* register addr */ +#define TW5864_IIC_REG_ADDR (0xff << TW5864_IIC_REG_ADDR_SHIFT) +/* rd/wr flag rd=1,wr=0 */ +#define TW5864_IIC_RW BIT(16) +#define TW5864_IIC_DEV_ADDR_SHIFT 17 +/* device addr */ +#define TW5864_IIC_DEV_ADDR (0x7f << TW5864_IIC_DEV_ADDR_SHIFT) +/* + * iic done, software kick off one time iic transaction through setting this + * bit to 1. Then poll this bit, value 1 indicate iic transaction have + * completed, if read, valid data have been stored in iic_data + */ +#define TW5864_IIC_DONE BIT(24) + +#define TW5864_RST_AND_IF_INFO 0x18018 +/* Define controls in register TW5864_RST_AND_IF_INFO */ +/* application software soft reset */ +#define TW5864_APP_SOFT_RST BIT(0) +#define TW5864_PCI_INF_VERSION_SHIFT 16 +/* PCI interface version, read only */ +#define TW5864_PCI_INF_VERSION (0xffff << TW5864_PCI_INF_VERSION_SHIFT) + +/* vlc stream crc value, it is calculated in pci module */ +#define TW5864_VLC_CRC_REG 0x1801C +/* + * vlc max length, it is defined by software based on software assign + * memory space for vlc + */ +#define TW5864_VLC_MAX_LENGTH 0x18020 +/* vlc length of one frame */ +#define TW5864_VLC_LENGTH 0x18024 +/* vlc original crc value */ +#define TW5864_VLC_INTRA_CRC_I_REG 0x18028 +/* vlc original crc value */ +#define TW5864_VLC_INTRA_CRC_O_REG 0x1802C +/* mv stream crc value, it is calculated in pci module */ +#define TW5864_VLC_PAR_CRC_REG 0x18030 +/* mv length */ +#define TW5864_VLC_PAR_LENGTH_REG 0x18034 +/* mv original crc value */ +#define TW5864_VLC_PAR_I_REG 0x18038 +/* mv original crc value */ +#define TW5864_VLC_PAR_O_REG 0x1803C + +/* + * Configuration register for 9[or 10] CIFs or 1D1+15QCIF Preview mode. + * PREV_PCI_ENB_CHN[0] Enable 9th preview channel (9CIF prev) + * or 1D1 channel in (1D1+15QCIF prev) + * PREV_PCI_ENB_CHN[1] Enable 10th preview channel + */ +#define TW5864_PREV_PCI_ENB_CHN 0x18040 +/* Description skipped. */ +#define TW5864_PREV_FRAME_FORMAT_IN 0x18044 +/* IIC enable */ +#define TW5864_IIC_ENB 0x18048 +/* + * Timer interrupt interval + * 0 1ms + * 1 2ms + * 2 4ms + * 3 8ms + */ +#define TW5864_PCI_INTTM_SCALE 0x1804C + +/* + * The above register is pci base address registers. Application software will + * initialize them to tell chip where the corresponding stream will be dumped + * to. Application software will select appropriate base address interval based + * on the stream length. + */ +/* VLC stream base address */ +#define TW5864_VLC_STREAM_BASE_ADDR 0x18080 +/* MV stream base address */ +#define TW5864_MV_STREAM_BASE_ADDR 0x18084 +/* 0x180a0 – 0x180bc: audio burst base address. Skipped. */ +/* 0x180C0 ~ 0x180DC – JPEG Push Mode Buffer Base Address. Skipped. */ +/* 0x18100 – 0x1817c: preview burst base address. Skipped. */ + +/* 0x80000 ~ 0x87FFF -- DDR Burst RW Register Map */ +#define TW5864_DDR_CTL 0x80000 +/* Define controls in register TW5864_DDR_CTL */ +#define TW5864_BRST_LENGTH_SHIFT 2 +/* Length of 32-bit data burst */ +#define TW5864_BRST_LENGTH (0x3fff << TW5864_BRST_LENGTH_SHIFT) +/* + * Burst Read/Write + * 0 Read Burst from DDR + * 1 Write Burst to DDR + */ +#define TW5864_BRST_RW BIT(16) +/* Begin a new DDR Burst. This bit is self cleared */ +#define TW5864_NEW_BRST_CMD BIT(17) +/* DDR Burst End Flag */ +#define TW5864_BRST_END BIT(24) +/* Enable Error Interrupt for Single DDR Access */ +#define TW5864_SING_ERR_INTR BIT(25) +/* Enable Error Interrupt for Burst DDR Access */ +#define TW5864_BRST_ERR_INTR BIT(26) +/* Enable Interrupt for End of DDR Burst Access */ +#define TW5864_BRST_END_INTR BIT(27) +/* DDR Single Access Error Flag */ +#define TW5864_SINGLE_ERR BIT(28) +/* DDR Single Access Busy Flag */ +#define TW5864_SINGLE_BUSY BIT(29) +/* DDR Burst Access Error Flag */ +#define TW5864_BRST_ERR BIT(30) +/* DDR Burst Access Busy Flag */ +#define TW5864_BRST_BUSY BIT(31) + +/* [27:0] DDR Access Address. Bit [1:0] has to be 0 */ +#define TW5864_DDR_ADDR 0x80004 +/* DDR Access Internal Buffer Address. Bit [1:0] has to be 0 */ +#define TW5864_DPR_BUF_ADDR 0x80008 +/* SRAM Buffer MPI Access Space. Totally 16 KB */ +#define TW5864_DPR_BUF_START 0x84000 +/* 0x84000 - 0x87FFC */ +#define TW5864_DPR_BUF_SIZE 0x4000 + +/* Indirect Map Space */ +/* + * The indirect space is accessed through 0xB800 ~ 0xB807 registers in direct + * access space + */ +/* Analog Video / Audio Decoder / Encoder */ +/* Allowed channel values: [0; 3] */ +/* Read-only register */ +#define TW5864_INDIR_VIN_0(channel) (0x000 + channel * 0x010) +/* Define controls in register TW5864_INDIR_VIN_0 */ +/* + * 1 Video not present. (sync is not detected in number of + * consecutive line periods specified by MISSCNT register) + * 0 Video detected. + */ +#define TW5864_INDIR_VIN_0_VDLOSS BIT(7) +/* + * 1 Horizontal sync PLL is locked to the incoming video + * source. + * 0 Horizontal sync PLL is not locked. + */ +#define TW5864_INDIR_VIN_0_HLOCK BIT(6) +/* + * 1 Sub-carrier PLL is locked to the incoming video source. + * 0 Sub-carrier PLL is not locked. + */ +#define TW5864_INDIR_VIN_0_SLOCK BIT(5) +/* + * 1 Even field is being decoded. + * 0 Odd field is being decoded. + */ +#define TW5864_INDIR_VIN_0_FLD BIT(4) +/* + * 1 Vertical logic is locked to the incoming video source. + * 0 Vertical logic is not locked. + */ +#define TW5864_INDIR_VIN_0_VLOCK BIT(3) +/* + * 1 No color burst signal detected. + * 0 Color burst signal detected. + */ +#define TW5864_INDIR_VIN_0_MONO BIT(1) +/* + * 0 60Hz source detected + * 1 50Hz source detected + * The actual vertical scanning frequency depends on the current + * standard invoked. + */ +#define TW5864_INDIR_VIN_0_DET50 BIT(0) + +#define TW5864_INDIR_VIN_1(channel) (0x001 + channel * 0x010) +/* VCR signal indicator. Read-only. */ +#define TW5864_INDIR_VIN_1_VCR BIT(7) +/* Weak signal indicator 2. Read-only. */ +#define TW5864_INDIR_VIN_1_WKAIR BIT(6) +/* Weak signal indicator controlled by WKTH. Read-only. */ +#define TW5864_INDIR_VIN_1_WKAIR1 BIT(5) +/* + * 1 = Standard signal + * 0 = Non-standard signal + * Read-only + */ +#define TW5864_INDIR_VIN_1_VSTD BIT(4) +/* + * 1 = Non-interlaced signal + * 0 = interlaced signal + * Read-only + */ +#define TW5864_INDIR_VIN_1_NINTL BIT(3) +/* + * Vertical Sharpness Control. Writable. + * 0 = None (default) + * 7 = Highest + * **Note: VSHP must be set to ‘0’ if COMB = 0 + */ +#define TW5864_INDIR_VIN_1_VSHP 0x07 + +/* HDELAY_XY[7:0] */ +#define TW5864_INDIR_VIN_2_HDELAY_XY_LO(channel) (0x002 + channel * 0x010) +/* HACTIVE_XY[7:0] */ +#define TW5864_INDIR_VIN_3_HACTIVE_XY_LO(channel) (0x003 + channel * 0x010) +/* VDELAY_XY[7:0] */ +#define TW5864_INDIR_VIN_4_VDELAY_XY_LO(channel) (0x004 + channel * 0x010) +/* VACTIVE_XY[7:0] */ +#define TW5864_INDIR_VIN_5_VACTIVE_XY_LO(channel) (0x005 + channel * 0x010) + +#define TW5864_INDIR_VIN_6(channel) (0x006 + channel * 0x010) +/* Define controls in register TW5864_INDIR_VIN_6 */ +#define TW5864_INDIR_VIN_6_HDELAY_XY_HI 0x03 +#define TW5864_INDIR_VIN_6_HACTIVE_XY_HI_SHIFT 2 +#define TW5864_INDIR_VIN_6_HACTIVE_XY_HI \ + (0x03 << TW5864_INDIR_VIN_6_HACTIVE_XY_HI_SHIFT) +#define TW5864_INDIR_VIN_6_VDELAY_XY_HI BIT(4) +#define TW5864_INDIR_VIN_6_VACTIVE_XY_HI BIT(5) + +/* + * HDELAY_XY This 10bit register defines the starting location of horizontal + * active pixel for display / record path. A unit is 1 pixel. The default value + * is 0x00F for NTSC and 0x00A for PAL. + * + * HACTIVE_XY This 10bit register defines the number of horizontal active pixel + * for display / record path. A unit is 1 pixel. The default value is decimal + * 720. + * + * VDELAY_XY This 9bit register defines the starting location of vertical + * active for display / record path. A unit is 1 line. The default value is + * decimal 6. + * + * VACTIVE_XY This 9bit register defines the number of vertical active lines + * for display / record path. A unit is 1 line. The default value is decimal + * 240. + */ + +/* HUE These bits control the color hue as 2's complement number. They have + * value from +36o (7Fh) to -36o (80h) with an increment of 2.8o. The 2 LSB has + * no effect. The positive value gives greenish tone and negative value gives + * purplish tone. The default value is 0o (00h). This is effective only on NTSC + * system. The default is 00h. + */ +#define TW5864_INDIR_VIN_7_HUE(channel) (0x007 + channel * 0x010) + +#define TW5864_INDIR_VIN_8(channel) (0x008 + channel * 0x010) +/* Define controls in register TW5864_INDIR_VIN_8 */ +/* + * This bit controls the center frequency of the peaking filter. + * The corresponding gain adjustment is HFLT. + * 0 Low + * 1 center + */ +#define TW5864_INDIR_VIN_8_SCURVE BIT(7) +/* CTI level selection. The default is 1. + * 0 None + * 3 Highest + */ +#define TW5864_INDIR_VIN_8_CTI_SHIFT 4 +#define TW5864_INDIR_VIN_8_CTI (0x03 << TW5864_INDIR_VIN_8_CTI_SHIFT) + +/* + * These bits control the amount of sharpness enhancement on the luminance + * signals. There are 16 levels of control with "0" having no effect on the + * output image. 1 through 15 provides sharpness enhancement with "F" being the + * strongest. The default is 1. + */ +#define TW5864_INDIR_VIN_8_SHARPNESS 0x0f + +/* + * These bits control the luminance contrast gain. A value of 100 (64h) has a + * gain of 1. The range adjustment is from 0% to 255% at 1% per step. The + * default is 64h. + */ +#define TW5864_INDIR_VIN_9_CNTRST(channel) (0x009 + channel * 0x010) + +/* + * These bits control the brightness. They have value of –128 to 127 in 2's + * complement form. Positive value increases brightness. A value 0 has no + * effect on the data. The default is 00h. + */ +#define TW5864_INDIR_VIN_A_BRIGHT(channel) (0x00A + channel * 0x010) + +/* + * These bits control the digital gain adjustment to the U (or Cb) component of + * the digital video signal. The color saturation can be adjusted by adjusting + * the U and V color gain components by the same amount in the normal + * situation. The U and V can also be adjusted independently to provide greater + * flexibility. The range of adjustment is 0 to 200%. A value of 128 (80h) has + * gain of 100%. The default is 80h. + */ +#define TW5864_INDIR_VIN_B_SAT_U(channel) (0x00B + channel * 0x010) + +/* + * These bits control the digital gain adjustment to the V (or Cr) component of + * the digital video signal. The color saturation can be adjusted by adjusting + * the U and V color gain components by the same amount in the normal + * situation. The U and V can also be adjusted independently to provide greater + * flexibility. The range of adjustment is 0 to 200%. A value of 128 (80h) has + * gain of 100%. The default is 80h. + */ +#define TW5864_INDIR_VIN_C_SAT_V(channel) (0x00C + channel * 0x010) + +/* Read-only */ +#define TW5864_INDIR_VIN_D(channel) (0x00D + channel * 0x010) +/* Define controls in register TW5864_INDIR_VIN_D */ +/* Macrovision color stripe detection may be un-reliable */ +#define TW5864_INDIR_VIN_D_CSBAD BIT(3) +/* Macrovision AGC pulse detected */ +#define TW5864_INDIR_VIN_D_MCVSN BIT(2) +/* Macrovision color stripe protection burst detected */ +#define TW5864_INDIR_VIN_D_CSTRIPE BIT(1) +/* + * This bit is valid only when color stripe protection is detected, i.e. + * if CSTRIPE=1, + * 1 Type 2 color stripe protection + * 0 Type 3 color stripe protection + */ +#define TW5864_INDIR_VIN_D_CTYPE2 BIT(0) + +/* Read-only */ +#define TW5864_INDIR_VIN_E(channel) (0x00E + channel * 0x010) +/* Define controls in register TW5864_INDIR_VIN_E */ +/* + * Read-only. + * 0 Idle + * 1 Detection in progress + */ +#define TW5864_INDIR_VIN_E_DETSTUS BIT(7) +/* + * STDNOW Current standard invoked + * 0 NTSC (M) + * 1 PAL (B, D, G, H, I) + * 2 SECAM + * 3 NTSC4.43 + * 4 PAL (M) + * 5 PAL (CN) + * 6 PAL 60 + * 7 Not valid + */ +#define TW5864_INDIR_VIN_E_STDNOW_SHIFT 4 +#define TW5864_INDIR_VIN_E_STDNOW (0x07 << TW5864_INDIR_VIN_E_STDNOW_SHIFT) + +/* + * 1 Disable the shadow registers + * 0 Enable VACTIVE and HDELAY shadow registers value + * depending on STANDARD. (Default) + */ +#define TW5864_INDIR_VIN_E_ATREG BIT(3) +/* + * STANDARD Standard selection + * 0 NTSC (M) + * 1 PAL (B, D, G, H, I) + * 2 SECAM + * 3 NTSC4.43 + * 4 PAL (M) + * 5 PAL (CN) + * 6 PAL 60 + * 7 Auto detection (Default) + */ +#define TW5864_INDIR_VIN_E_STANDARD 0x07 + +#define TW5864_INDIR_VIN_F(channel) (0x00F + channel * 0x010) +/* Define controls in register TW5864_INDIR_VIN_F */ +/* + * 1 Writing 1 to this bit will manually initiate the auto format + * detection process. This bit is a self-clearing bit + * 0 Manual initiation of auto format detection is done. + * (Default) + */ +#define TW5864_INDIR_VIN_F_ATSTART BIT(7) +/* Enable recognition of PAL60 (Default) */ +#define TW5864_INDIR_VIN_F_PAL60EN BIT(6) +/* Enable recognition of PAL (CN). (Default) */ +#define TW5864_INDIR_VIN_F_PALCNEN BIT(5) +/* Enable recognition of PAL (M). (Default) */ +#define TW5864_INDIR_VIN_F_PALMEN BIT(4) +/* Enable recognition of NTSC 4.43. (Default) */ +#define TW5864_INDIR_VIN_F_NTSC44EN BIT(3) +/* Enable recognition of SECAM. (Default) */ +#define TW5864_INDIR_VIN_F_SECAMEN BIT(2) +/* Enable recognition of PAL (B, D, G, H, I). (Default) */ +#define TW5864_INDIR_VIN_F_PALBEN BIT(1) +/* Enable recognition of NTSC (M). (Default) */ +#define TW5864_INDIR_VIN_F_NTSCEN BIT(0) + +/* Some registers skipped. */ + +/* Use falling edge to sample VD1-VD4 from 54 MHz to 108 MHz */ +#define TW5864_INDIR_VD_108_POL 0x041 +#define TW5864_INDIR_VD_108_POL_VD12 BIT(0) +#define TW5864_INDIR_VD_108_POL_VD34 BIT(1) +#define TW5864_INDIR_VD_108_POL_BOTH \ + (TW5864_INDIR_VD_108_POL_VD12 | TW5864_INDIR_VD_108_POL_VD34) + +/* Some registers skipped. */ + +/* + * Audio Input ADC gain control + * 0 0.25 + * 1 0.31 + * 2 0.38 + * 3 0.44 + * 4 0.50 + * 5 0.63 + * 6 0.75 + * 7 0.88 + * 8 1.00 (default) + * 9 1.25 + * 10 1.50 + * 11 1.75 + * 12 2.00 + * 13 2.25 + * 14 2.50 + * 15 2.75 + */ +/* [3:0] channel 0, [7:4] channel 1 */ +#define TW5864_INDIR_AIGAIN1 0x060 +/* [3:0] channel 2, [7:4] channel 3 */ +#define TW5864_INDIR_AIGAIN2 0x061 + +/* Some registers skipped */ + +#define TW5864_INDIR_AIN_0x06D 0x06D +/* Define controls in register TW5864_INDIR_AIN_0x06D */ +/* + * LAWMD Select u-Law/A-Law/PCM/SB data output format on ADATR and ADATM pin. + * 0 PCM output (default) + * 1 SB (Signed MSB bit in PCM data is inverted) output + * 2 u-Law output + * 3 A-Law output + */ +#define TW5864_INDIR_AIN_LAWMD_SHIFT 6 +#define TW5864_INDIR_AIN_LAWMD (0x03 << TW5864_INDIR_AIN_LAWMD_SHIFT) +/* + * Disable the mixing ratio value for all audio. + * 0 Apply individual mixing ratio value for each audio + * (default) + * 1 Apply nominal value for all audio commonly + */ +#define TW5864_INDIR_AIN_MIX_DERATIO BIT(5) +/* Enable the mute function for audio channel AINn when n is 0 to 3. It effects + * only for mixing. When n = 4, it enable the mute function of the playback + * audio input. It effects only for single chip or the last stage chip + * 0 Normal + * 1 Muted (default) + */ +#define TW5864_INDIR_AIN_MIX_MUTE 0x1f + +/* Some registers skipped */ + +#define TW5864_INDIR_AIN_0x0E3 0x0E3 +/* Define controls in register TW5864_INDIR_AIN_0x0E3 */ +/* + * ADATP signal is coming from external ADPCM decoder, instead + * of on-chip ADPCM decoder + */ +#define TW5864_INDIR_AIN_0x0E3_EXT_ADATP BIT(7) +/* ACLKP output signal polarity inverse */ +#define TW5864_INDIR_AIN_0x0E3_ACLKPPOLO BIT(6) +/* + * ACLKR input signal polarity inverse. + * 0 Not inversed (Default) + * 1 Inversed + */ +#define TW5864_INDIR_AIN_0x0E3_ACLKRPOL BIT(5) +/* + * ACLKP input signal polarity inverse. + * 0 Not inversed (Default) + * 1 Inversed + */ +#define TW5864_INDIR_AIN_0x0E3_ACLKPPOLI BIT(4) +/* + * ACKI [21:0] control automatic set up with AFMD registers + * This mode is only effective when ACLKRMASTER=1 + * 0 ACKI [21:0] registers set up ACKI control + * 1 ACKI control is automatically set up by AFMD register values + */ +#define TW5864_INDIR_AIN_0x0E3_AFAUTO BIT(3) +/* + * AFAUTO control mode + * 0 8kHz setting (Default) + * 1 16kHz setting + * 2 32kHz setting + * 3 44.1kHz setting + * 4 48kHz setting + */ +#define TW5864_INDIR_AIN_0x0E3_AFMD 0x07 + +#define TW5864_INDIR_AIN_0x0E4 0x0E4 +/* Define controls in register TW5864_INDIR_AIN_0x0ED */ +/* + * 8bit I2S Record output mode. + * 0 L/R half length separated output (Default). + * 1 One continuous packed output equal to DSP output + * format. + */ +#define TW5864_INDIR_AIN_0x0E4_I2S8MODE BIT(7) +/* + * Audio Clock Master ACLKR output wave format. + * 0 High periods is one 27MHz clock period (default). + * 1 Almost duty 50-50% clock output on ACLKR pin. If this + * mode is selected, two times bigger number value need + * to be set up on the ACKI register. If AFAUTO=1, ACKI + * control is automatically set up even if MASCKMD=1. + */ +#define TW5864_INDIR_AIN_0x0E4_MASCKMD BIT(6) +/* Playback ACLKP/ASYNP/ADATP input data MSB-LSB swapping */ +#define TW5864_INDIR_AIN_0x0E4_PBINSWAP BIT(5) +/* + * ASYNR input signal delay. + * 0 No delay + * 1 Add one 27MHz period delay in ASYNR signal input + */ +#define TW5864_INDIR_AIN_0x0E4_ASYNRDLY BIT(4) +/* + * ASYNP input signal delay. + * 0 no delay + * 1 add one 27MHz period delay in ASYNP signal input + */ +#define TW5864_INDIR_AIN_0x0E4_ASYNPDLY BIT(3) +/* + * ADATP input data delay by one ACLKP clock. + * 0 No delay (Default).This is for I2S type 1T delay input + * interface. + * 1 Add 1 ACLKP clock delay in ADATP input data. This is + * for left-justified type 0T delay input interface. + */ +#define TW5864_INDIR_AIN_0x0E4_ADATPDLY BIT(2) +/* + * Select u-Law/A-Law/PCM/SB data input format on ADATP pin. + * 0 PCM input (Default) + * 1 SB (Signed MSB bit in PCM data is inverted) input + * 2 u-Law input + * 3 A-Law input + */ +#define TW5864_INDIR_AIN_0x0E4_INLAWMD 0x03 + +/* + * Enable state register updating and interrupt request of audio AIN5 detection + * for each input + */ +#define TW5864_INDIR_AIN_A5DETENA 0x0E5 + +/* Some registers skipped */ + +/* + * [7:3]: DEV_ID The TW5864 product ID code is 01000 + * [2:0]: REV_ID The revision number is 0h + */ +#define TW5864_INDIR_ID 0x0FE + +#define TW5864_INDIR_IN_PIC_WIDTH(channel) (0x200 + 4 * channel) +#define TW5864_INDIR_IN_PIC_HEIGHT(channel) (0x201 + 4 * channel) +#define TW5864_INDIR_OUT_PIC_WIDTH(channel) (0x202 + 4 * channel) +#define TW5864_INDIR_OUT_PIC_HEIGHT(channel) (0x203 + 4 * channel) +/* + * Interrupt status register from the front-end. Write "1" to each bit to clear + * the interrupt + * 15:0 Motion detection interrupt for channel 0 ~ 15 + * 31:16 Night detection interrupt for channel 0 ~ 15 + * 47:32 Blind detection interrupt for channel 0 ~ 15 + * 63:48 No video interrupt for channel 0 ~ 15 + * 79:64 Line mode underflow interrupt for channel 0 ~ 15 + * 95:80 Line mode overflow interrupt for channel 0 ~ 15 + */ +/* 0x2D0~0x2D7: [63:0] bits */ +#define TW5864_INDIR_INTERRUPT1 0x2D0 +/* 0x2E0~0x2E3: [95:64] bits */ +#define TW5864_INDIR_INTERRUPT2 0x2E0 + +/* + * Interrupt mask register for interrupts in 0x2D0 ~ 0x2D7 + * 15:0 Motion detection interrupt for channel 0 ~ 15 + * 31:16 Night detection interrupt for channel 0 ~ 15 + * 47:32 Blind detection interrupt for channel 0 ~ 15 + * 63:48 No video interrupt for channel 0 ~ 15 + * 79:64 Line mode underflow interrupt for channel 0 ~ 15 + * 95:80 Line mode overflow interrupt for channel 0 ~ 15 + */ +/* 0x2D8~0x2DF: [63:0] bits */ +#define TW5864_INDIR_INTERRUPT_MASK1 0x2D8 +/* 0x2E8~0x2EB: [95:64] bits */ +#define TW5864_INDIR_INTERRUPT_MASK2 0x2E8 + +/* [11:0]: Interrupt summary register for interrupts & interrupt mask from in + * 0x2D0 ~ 0x2D7 and 0x2D8 ~ 0x2DF + * bit 0: interrupt occurs in 0x2D0 & 0x2D8 + * bit 1: interrupt occurs in 0x2D1 & 0x2D9 + * bit 2: interrupt occurs in 0x2D2 & 0x2DA + * bit 3: interrupt occurs in 0x2D3 & 0x2DB + * bit 4: interrupt occurs in 0x2D4 & 0x2DC + * bit 5: interrupt occurs in 0x2D5 & 0x2DD + * bit 6: interrupt occurs in 0x2D6 & 0x2DE + * bit 7: interrupt occurs in 0x2D7 & 0x2DF + * bit 8: interrupt occurs in 0x2E0 & 0x2E8 + * bit 9: interrupt occurs in 0x2E1 & 0x2E9 + * bit 10: interrupt occurs in 0x2E2 & 0x2EA + * bit 11: interrupt occurs in 0x2E3 & 0x2EB + */ +#define TW5864_INDIR_INTERRUPT_SUMMARY 0x2F0 + +/* Motion / Blind / Night Detection */ +/* valid value for channel is [0:15] */ +#define TW5864_INDIR_DETECTION_CTL0(channel) (0x300 + channel * 0x08) +/* Define controls in register TW5864_INDIR_DETECTION_CTL0 */ +/* + * Disable the motion and blind detection. + * 0 Enable motion and blind detection (default) + * 1 Disable motion and blind detection + */ +#define TW5864_INDIR_DETECTION_CTL0_MD_DIS BIT(5) +/* + * Request to start motion detection on manual trigger mode + * 0 None Operation (default) + * 1 Request to start motion detection + */ +#define TW5864_INDIR_DETECTION_CTL0_MD_STRB BIT(3) +/* + * Select the trigger mode of motion detection + * 0 Automatic trigger mode of motion detection (default) + * 1 Manual trigger mode for motion detection + */ +#define TW5864_INDIR_DETECTION_CTL0_MD_STRB_EN BIT(2) +/* + * Define the threshold of cell for blind detection. + * 0 Low threshold (More sensitive) (default) + * : : + * 3 High threshold (Less sensitive) + */ +#define TW5864_INDIR_DETECTION_CTL0_BD_CELSENS 0x03 + +#define TW5864_INDIR_DETECTION_CTL1(channel) (0x301 + channel * 0x08) +/* Define controls in register TW5864_INDIR_DETECTION_CTL1 */ +/* + * Control the temporal sensitivity of motion detector. + * 0 More Sensitive (default) + * : : + * 15 Less Sensitive + */ +#define TW5864_INDIR_DETECTION_CTL1_MD_TMPSENS_SHIFT 4 +#define TW5864_INDIR_DETECTION_CTL1_MD_TMPSENS \ + (0x0f << TW5864_INDIR_DETECTION_CTL1_MD_TMPSENS_SHIFT) +/* + * Adjust the horizontal starting position for motion detection + * 0 0 pixel (default) + * : : + * 15 15 pixels + */ +#define TW5864_INDIR_DETECTION_CTL1_MD_PIXEL_OS 0x0f + +#define TW5864_INDIR_DETECTION_CTL2(channel) (0x302 + channel * 0x08) +/* Define controls in register TW5864_INDIR_DETECTION_CTL2 */ +/* + * Control the updating time of reference field for motion detection. + * 0 Update reference field every field (default) + * 1 Update reference field according to MD_SPEED + */ +#define TW5864_INDIR_DETECTION_CTL2_MD_REFFLD BIT(7) +/* + * Select the field for motion detection. + * 0 Detecting motion for only odd field (default) + * 1 Detecting motion for only even field + * 2 Detecting motion for any field + * 3 Detecting motion for both odd and even field + */ +#define TW5864_INDIR_DETECTION_CTL2_MD_FIELD_SHIFT 5 +#define TW5864_INDIR_DETECTION_CTL2_MD_FIELD \ + (0x03 << TW5864_INDIR_DETECTION_CTL2_MD_FIELD_SHIFT) +/* + * Control the level sensitivity of motion detector. + * 0 More sensitive (default) + * : : + * 15 Less sensitive + */ +#define TW5864_INDIR_DETECTION_CTL2_MD_LVSENS 0x1f + +#define TW5864_INDIR_DETECTION_CTL3(channel) (0x303 + channel * 0x08) +/* Define controls in register TW5864_INDIR_DETECTION_CTL3 */ +/* + * Define the threshold of sub-cell number for motion detection. + * 0 Motion is detected if 1 sub-cell has motion (More sensitive) (default) + * 1 Motion is detected if 2 sub-cells have motion + * 2 Motion is detected if 3 sub-cells have motion + * 3 Motion is detected if 4 sub-cells have motion (Less sensitive) + */ +#define TW5864_INDIR_DETECTION_CTL3_MD_CELSENS_SHIFT 6 +#define TW5864_INDIR_DETECTION_CTL3_MD_CELSENS \ + (0x03 << TW5864_INDIR_DETECTION_CTL3_MD_CELSENS_SHIFT) +/* + * Control the velocity of motion detector. + * Large value is suitable for slow motion detection. + * In MD_DUAL_EN = 1, MD_SPEED should be limited to 0 ~ 31. + * 0 1 field intervals (default) + * 1 2 field intervals + * : : + * 61 62 field intervals + * 62 63 field intervals + * 63 Not supported + */ +#define TW5864_INDIR_DETECTION_CTL3_MD_SPEED 0x3f + +#define TW5864_INDIR_DETECTION_CTL4(channel) (0x304 + channel * 0x08) +/* Define controls in register TW5864_INDIR_DETECTION_CTL4 */ +/* + * Control the spatial sensitivity of motion detector. + * 0 More Sensitive (default) + * : : + * 15 Less Sensitive + */ +#define TW5864_INDIR_DETECTION_CTL4_MD_SPSENS_SHIFT 4 +#define TW5864_INDIR_DETECTION_CTL4_MD_SPSENS \ + (0x0f << TW5864_INDIR_DETECTION_CTL4_MD_SPSENS_SHIFT) +/* + * Define the threshold of level for blind detection. + * 0 Low threshold (More sensitive) (default) + * : : + * 15 High threshold (Less sensitive) + */ +#define TW5864_INDIR_DETECTION_CTL4_BD_LVSENS 0x0f + +#define TW5864_INDIR_DETECTION_CTL5(channel) (0x305 + channel * 0x08) +/* + * Define the threshold of temporal sensitivity for night detection. + * 0 Low threshold (More sensitive) (default) + * : : + * 15 High threshold (Less sensitive) + */ +#define TW5864_INDIR_DETECTION_CTL5_ND_TMPSENS_SHIFT 4 +#define TW5864_INDIR_DETECTION_CTL5_ND_TMPSENS \ + (0x0f << TW5864_INDIR_DETECTION_CTL5_ND_TMPSENS_SHIFT) +/* + * Define the threshold of level for night detection. + * 0 Low threshold (More sensitive) (default) + * : : + * 3 High threshold (Less sensitive) + */ +#define TW5864_INDIR_DETECTION_CTL5_ND_LVSENS 0x0f + +/* + * [11:0] The base address of the motion detection buffer. This address is + * in unit of 64K bytes. The generated DDR address will be + * {MD_BASE_ADDR, 16"h0000}. The default value should be + * 12"h000 + */ +#define TW5864_INDIR_MD_BASE_ADDR 0x380 + +/* This controls the channel of the motion detection result shown in register + * 0x3A0 ~ 0x3B7. Before reading back motion result, always set this first. + */ +#define TW5864_INDIR_RGR_MOTION_SEL 0x382 + +/* [15:0] MD strobe has been performed at channel n (read only) */ +#define TW5864_INDIR_MD_STRB 0x386 +/* NO_VIDEO Detected from channel n (read only) */ +#define TW5864_INDIR_NOVID_DET 0x388 +/* Motion Detected from channel n (read only) */ +#define TW5864_INDIR_MD_DET 0x38A +/* Blind Detected from channel n (read only) */ +#define TW5864_INDIR_BD_DET 0x38C +/* Night Detected from channel n (read only) */ +#define TW5864_INDIR_ND_DET 0x38E + +/* 192 bit motion flag of the channel specified by + * RGR_MOTION_SEL in 0x382 + */ +#define TW5864_INDIR_MOTION_FLAG 0x3A0 +#define TW5864_INDIR_MOTION_FLAG_BYTE_COUNT 24 + +/* + * [9:0] The motion cell count of a + * specific channel selected by 0x382. + * This is for DI purpose + */ +#define TW5864_INDIR_MD_DI_CNT 0x3B8 +/* The motion detection cell sensitivity for DI purpose */ +#define TW5864_INDIR_MD_DI_CELLSENS 0x3BA +/* The motion detection threshold level for DI purpose */ +#define TW5864_INDIR_MD_DI_LVSENS 0x3BB + +/* 192 bit motion mask of the channel specified by MASK_CH_SEL in 0x3FE */ +#define TW5864_INDIR_MOTION_MASK 0x3E0 +#define TW5864_INDIR_MOTION_MASK_BYTE_COUNT 24 + +/* [4:0] The channel selection to access masks in 0x3E0 ~ 0x3F7 */ +#define TW5864_INDIR_MASK_CH_SEL 0x3FE + +/* Clock PLL / Analog IP Control */ +/* Some registers skipped */ + +#define TW5864_INDIR_DDRA_DLL_DQS_SEL0 0xEE6 +#define TW5864_INDIR_DDRA_DLL_DQS_SEL1 0xEE7 +#define TW5864_INDIR_DDRA_DLL_CLK90_SEL 0xEE8 +#define TW5864_INDIR_DDRA_DLL_TEST_SEL_AND_TAP_S 0xEE9 + +#define TW5864_INDIR_DDRB_DLL_DQS_SEL0 0xEEB +#define TW5864_INDIR_DDRB_DLL_DQS_SEL1 0xEEC +#define TW5864_INDIR_DDRB_DLL_CLK90_SEL 0xEED +#define TW5864_INDIR_DDRB_DLL_TEST_SEL_AND_TAP_S 0xEEE + +#define TW5864_INDIR_RESET 0xEF0 +#define TW5864_INDIR_RESET_VD BIT(7) +#define TW5864_INDIR_RESET_DLL BIT(6) +#define TW5864_INDIR_RESET_MUX_CORE BIT(5) + +#define TW5864_INDIR_PV_VD_CK_POL 0xEFD +#define TW5864_INDIR_PV_VD_CK_POL_PV(channel) BIT(channel) +#define TW5864_INDIR_PV_VD_CK_POL_VD(channel) BIT(channel + 4) + +#define TW5864_INDIR_CLK0_SEL 0xEFE +#define TW5864_INDIR_CLK0_SEL_VD_SHIFT 0 +#define TW5864_INDIR_CLK0_SEL_VD_MASK (0x3 << TW5864_INDIR_CLK0_SEL_VD_SHIFT) +#define TW5864_INDIR_CLK0_SEL_PV_SHIFT 2 +#define TW5864_INDIR_CLK0_SEL_PV_MASK (0x3 << TW5864_INDIR_CLK0_SEL_PV_SHIFT) +#define TW5864_INDIR_CLK0_SEL_PV2_SHIFT 4 +#define TW5864_INDIR_CLK0_SEL_PV2_MASK (0x3 << TW5864_INDIR_CLK0_SEL_PV2_SHIFT) diff --git a/drivers/staging/media/tw5864/tw5864-tables.h b/drivers/staging/media/tw5864/tw5864-tables.h new file mode 100644 index 0000000..3a3e0ad --- /dev/null +++ b/drivers/staging/media/tw5864/tw5864-tables.h @@ -0,0 +1,237 @@ +/* + * TW5864 driver - precomputed tables + * + * Copyright (C) 2015 Bluecherry, LLC + * Author: Andrey Utkin + * + * 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. + */ + +#define QUANTIZATION_TABLE_LEN 96 +#define VLC_LOOKUP_TABLE_LEN 1024 + +static const u16 forward_quantization_table[QUANTIZATION_TABLE_LEN] = { + 0x3333, 0x1f82, 0x3333, 0x1F82, 0x1F82, 0x147B, 0x1F82, 0x147B, 0x3333, + 0x1F82, + 0x3333, 0x1F82, 0x1F82, 0x147B, 0x1F82, 0x147B, 0x2E8C, 0x1D42, 0x2E8C, + 0x1D42, + 0x1D42, 0x1234, 0x1D42, 0x1234, 0x2E8C, 0x1D42, 0x2E8C, 0x1D42, 0x1D42, + 0x1234, + 0x1D42, 0x1234, 0x2762, 0x199A, 0x2762, 0x199A, 0x199A, 0x1062, 0x199A, + 0x1062, + 0x2762, 0x199A, 0x2762, 0x199A, 0x199A, 0x1062, 0x199A, 0x1062, 0x2492, + 0x16C1, + 0x2492, 0x16C1, 0x16C1, 0x0E3F, 0x16C1, 0x0E3F, 0x2492, 0x16C1, 0x2492, + 0x16C1, + 0x16C1, 0x0E3F, 0x16C1, 0x0E3F, 0x2000, 0x147B, 0x2000, 0x147B, 0x147B, + 0x0D1B, + 0x147B, 0x0D1B, 0x2000, 0x147B, 0x2000, 0x147B, 0x147B, 0x0D1B, 0x147B, + 0x0D1B, + 0x1C72, 0x11CF, 0x1C72, 0x11CF, 0x11CF, 0x0B4D, 0x11CF, 0x0B4D, 0x1C72, + 0x11CF, + 0x1C72, 0x11CF, 0x11CF, 0x0B4D, 0x11CF, 0x0B4D +}; + +static const u16 inverse_quantization_table[QUANTIZATION_TABLE_LEN] = { + 0x800A, 0x800D, 0x800A, 0x800D, 0x800D, 0x8010, 0x800D, 0x8010, 0x800A, + 0x800D, + 0x800A, 0x800D, 0x800D, 0x8010, 0x800D, 0x8010, 0x800B, 0x800E, 0x800B, + 0x800E, + 0x800E, 0x8012, 0x800E, 0x8012, 0x800B, 0x800E, 0x800B, 0x800E, 0x800E, + 0x8012, + 0x800E, 0x8012, 0x800D, 0x8010, 0x800D, 0x8010, 0x8010, 0x8014, 0x8010, + 0x8014, + 0x800D, 0x8010, 0x800D, 0x8010, 0x8010, 0x8014, 0x8010, 0x8014, 0x800E, + 0x8012, + 0x800E, 0x8012, 0x8012, 0x8017, 0x8012, 0x8017, 0x800E, 0x8012, 0x800E, + 0x8012, + 0x8012, 0x8017, 0x8012, 0x8017, 0x8010, 0x8014, 0x8010, 0x8014, 0x8014, + 0x8019, + 0x8014, 0x8019, 0x8010, 0x8014, 0x8010, 0x8014, 0x8014, 0x8019, 0x8014, + 0x8019, + 0x8012, 0x8017, 0x8012, 0x8017, 0x8017, 0x801D, 0x8017, 0x801D, 0x8012, + 0x8017, + 0x8012, 0x8017, 0x8017, 0x801D, 0x8017, 0x801D +}; + +static const u16 encoder_vlc_lookup_table[VLC_LOOKUP_TABLE_LEN] = { + 0x011, 0x000, 0x000, 0x000, 0x065, 0x021, 0x000, 0x000, 0x087, 0x064, + 0x031, 0x000, + 0x097, 0x086, 0x075, 0x053, 0x0a7, 0x096, 0x085, 0x063, 0x0b7, 0x0a6, + 0x095, 0x074, + 0x0df, 0x0b6, 0x0a5, 0x084, 0x0db, 0x0de, 0x0b5, 0x094, 0x0d8, 0x0da, + 0x0dd, 0x0a4, + 0x0ef, 0x0ee, 0x0d9, 0x0b4, 0x0eb, 0x0ea, 0x0ed, 0x0dc, 0x0ff, 0x0fe, + 0x0e9, 0x0ec, + 0x0fb, 0x0fa, 0x0fd, 0x0e8, 0x10f, 0x0f1, 0x0f9, 0x0fc, 0x10b, 0x10e, + 0x10d, 0x0f8, + 0x107, 0x10a, 0x109, 0x10c, 0x104, 0x106, 0x105, 0x108, 0x023, 0x000, + 0x000, 0x000, + 0x06b, 0x022, 0x000, 0x000, 0x067, 0x057, 0x033, 0x000, 0x077, 0x06a, + 0x069, 0x045, + 0x087, 0x066, 0x065, 0x044, 0x084, 0x076, 0x075, 0x056, 0x097, 0x086, + 0x085, 0x068, + 0x0bf, 0x096, 0x095, 0x064, 0x0bb, 0x0be, 0x0bd, 0x074, 0x0cf, 0x0ba, + 0x0b9, 0x094, + 0x0cb, 0x0ce, 0x0cd, 0x0bc, 0x0c8, 0x0ca, 0x0c9, 0x0b8, 0x0df, 0x0de, + 0x0dd, 0x0cc, + 0x0db, 0x0da, 0x0d9, 0x0dc, 0x0d7, 0x0eb, 0x0d6, 0x0d8, 0x0e9, 0x0e8, + 0x0ea, 0x0d1, + 0x0e7, 0x0e6, 0x0e5, 0x0e4, 0x04f, 0x000, 0x000, 0x000, 0x06f, 0x04e, + 0x000, 0x000, + 0x06b, 0x05f, 0x04d, 0x000, 0x068, 0x05c, 0x05e, 0x04c, 0x07f, 0x05a, + 0x05b, 0x04b, + 0x07b, 0x058, 0x059, 0x04a, 0x079, 0x06e, 0x06d, 0x049, 0x078, 0x06a, + 0x069, 0x048, + 0x08f, 0x07e, 0x07d, 0x05d, 0x08b, 0x08e, 0x07a, 0x06c, 0x09f, 0x08a, + 0x08d, 0x07c, + 0x09b, 0x09e, 0x089, 0x08c, 0x098, 0x09a, 0x09d, 0x088, 0x0ad, 0x097, + 0x099, 0x09c, + 0x0a9, 0x0ac, 0x0ab, 0x0aa, 0x0a5, 0x0a8, 0x0a7, 0x0a6, 0x0a1, 0x0a4, + 0x0a3, 0x0a2, + 0x021, 0x000, 0x000, 0x000, 0x067, 0x011, 0x000, 0x000, 0x064, 0x066, + 0x031, 0x000, + 0x063, 0x073, 0x072, 0x065, 0x062, 0x083, 0x082, 0x070, 0x000, 0x000, + 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, + 0x011, 0x010, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x011, 0x021, 0x020, 0x000, 0x000, 0x000, + 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x023, 0x022, + 0x021, 0x020, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, + 0x023, 0x022, 0x021, 0x031, 0x030, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x023, 0x022, 0x033, 0x032, 0x031, 0x030, + 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x023, 0x030, + 0x031, 0x033, + 0x032, 0x035, 0x034, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, + 0x037, 0x036, 0x035, 0x034, 0x033, 0x032, 0x031, 0x041, 0x051, 0x061, + 0x071, 0x081, + 0x091, 0x0a1, 0x0b1, 0x000, 0x002, 0x000, 0x0e4, 0x011, 0x0f4, 0x002, + 0x024, 0x003, + 0x005, 0x012, 0x034, 0x013, 0x065, 0x024, 0x013, 0x063, 0x015, 0x022, + 0x075, 0x034, + 0x044, 0x023, 0x023, 0x073, 0x054, 0x033, 0x033, 0x004, 0x043, 0x014, + 0x011, 0x043, + 0x014, 0x001, 0x025, 0x015, 0x035, 0x025, 0x064, 0x055, 0x045, 0x035, + 0x074, 0x065, + 0x085, 0x0d5, 0x012, 0x095, 0x055, 0x045, 0x095, 0x0e5, 0x084, 0x075, + 0x022, 0x0a5, + 0x094, 0x085, 0x032, 0x0b5, 0x003, 0x0c5, 0x001, 0x044, 0x0a5, 0x032, + 0x0b5, 0x094, + 0x0c5, 0x0a4, 0x0a4, 0x054, 0x0d5, 0x0b4, 0x0b4, 0x064, 0x0f5, 0x0f5, + 0x053, 0x0d4, + 0x0e5, 0x0c4, 0x105, 0x105, 0x0c4, 0x074, 0x063, 0x0e4, 0x0d4, 0x084, + 0x073, 0x0f4, + 0x004, 0x005, 0x000, 0x053, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x011, 0x021, 0x031, 0x030, 0x011, 0x021, + 0x020, 0x000, + 0x011, 0x010, 0x000, 0x000, 0x011, 0x033, 0x032, 0x043, 0x042, 0x053, + 0x052, 0x063, + 0x062, 0x073, 0x072, 0x083, 0x082, 0x093, 0x092, 0x091, 0x037, 0x036, + 0x035, 0x034, + 0x033, 0x045, 0x044, 0x043, 0x042, 0x053, 0x052, 0x063, 0x062, 0x061, + 0x060, 0x000, + 0x045, 0x037, 0x036, 0x035, 0x044, 0x043, 0x034, 0x033, 0x042, 0x053, + 0x052, 0x061, + 0x051, 0x060, 0x000, 0x000, 0x053, 0x037, 0x045, 0x044, 0x036, 0x035, + 0x034, 0x043, + 0x033, 0x042, 0x052, 0x051, 0x050, 0x000, 0x000, 0x000, 0x045, 0x044, + 0x043, 0x037, + 0x036, 0x035, 0x034, 0x033, 0x042, 0x051, 0x041, 0x050, 0x000, 0x000, + 0x000, 0x000, + 0x061, 0x051, 0x037, 0x036, 0x035, 0x034, 0x033, 0x032, 0x041, 0x031, + 0x060, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x061, 0x051, 0x035, 0x034, 0x033, 0x023, + 0x032, 0x041, + 0x031, 0x060, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x061, 0x041, + 0x051, 0x033, + 0x023, 0x022, 0x032, 0x031, 0x060, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, + 0x061, 0x060, 0x041, 0x023, 0x022, 0x031, 0x021, 0x051, 0x000, 0x000, + 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x051, 0x050, 0x031, 0x023, 0x022, 0x021, + 0x041, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x040, 0x041, + 0x031, 0x032, + 0x011, 0x033, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, + 0x040, 0x041, 0x021, 0x011, 0x031, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x030, 0x031, 0x011, 0x021, 0x000, 0x000, + 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x020, 0x021, + 0x011, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, + 0x010, 0x011, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000 +}; diff --git a/drivers/staging/media/tw5864/tw5864-video.c b/drivers/staging/media/tw5864/tw5864-video.c new file mode 100644 index 0000000..71b79df --- /dev/null +++ b/drivers/staging/media/tw5864/tw5864-video.c @@ -0,0 +1,1364 @@ +/* + * TW5864 driver - video encoding functions + * + * Copyright (C) 2015 Bluecherry, LLC + * Author: Andrey Utkin + * + * 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 "tw5864.h" +#include "tw5864-reg.h" +#include "tw5864-tables.h" + +static void tw5864_handle_frame_task(unsigned long data); +static void tw5864_handle_frame(struct tw5864_h264_frame *frame); +static void tw5864_frame_interval_set(struct tw5864_input *input); + +static int tw5864_queue_setup(struct vb2_queue *q, + unsigned int *num_buffers, + unsigned int *num_planes, unsigned int sizes[], + void *alloc_ctxs[]) +{ + struct tw5864_input *dev = vb2_get_drv_priv(q); + + if (q->num_buffers + *num_buffers < 12) + *num_buffers = 12 - q->num_buffers; + + alloc_ctxs[0] = dev->alloc_ctx; + if (*num_planes) + return sizes[0] < H264_VLC_BUF_SIZE ? -EINVAL : 0; + + sizes[0] = H264_VLC_BUF_SIZE; + *num_planes = 1; + + return 0; +} + +static void tw5864_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct vb2_queue *vq = vb->vb2_queue; + struct tw5864_input *dev = vb2_get_drv_priv(vq); + struct tw5864_buf *buf = container_of(vbuf, struct tw5864_buf, vb); + unsigned long flags; + + spin_lock_irqsave(&dev->slock, flags); + list_add_tail(&buf->list, &dev->active); + spin_unlock_irqrestore(&dev->slock, flags); +} + +static int tw5864_input_std_get(struct tw5864_input *input, + enum tw5864_vid_std *std_arg) +{ + struct tw5864_dev *dev = input->root; + enum tw5864_vid_std std; + u8 indir_0x00e = tw_indir_readb(dev, + 0x00e + input->input_number * 0x010); + std = (indir_0x00e & 0x70) >> 4; + + if (indir_0x00e & 0x80) { + dev_err(&dev->pci->dev, + "Video format detection is in progress, please wait\n"); + return -EAGAIN; + } + + if (std == STD_INVALID) { + dev_err(&dev->pci->dev, "No valid video format detected\n"); + return -1; + } + + *std_arg = std; + return 0; +} + +static int tw5864_enable_input(struct tw5864_input *input) +{ + struct tw5864_dev *dev = input->root; + int input_number = input->input_number; + unsigned long flags; + int ret; + int d1_width = 720; + int d1_height; + int frame_width_bus_value = 0; + int frame_height_bus_value = 0; + int reg_frame_bus = 0x1c; + int fmt_reg_value = 0; + int downscale_enabled = 0; + + dev_dbg(&dev->pci->dev, "Enabling channel %d\n", input_number); + + ret = tw5864_input_std_get(input, &input->std); + if (ret) + return ret; + input->v4l2_std = tw5864_get_v4l2_std(input->std); + + input->frame_seqno = 0; + input->h264_idr_pic_id = 0; + input->h264_frame_seqno_in_gop = 0; + + input->reg_dsp_qp = input->qp; + input->reg_dsp_ref_mvp_lambda = lambda_lookup_table[input->qp]; + input->reg_dsp_i4x4_weight = intra4x4_lambda3[input->qp]; + input->reg_emu = TW5864_EMU_EN_LPF | TW5864_EMU_EN_BHOST + | TW5864_EMU_EN_SEN | TW5864_EMU_EN_ME | TW5864_EMU_EN_DDR; + input->reg_dsp = input_number /* channel id */ + | TW5864_DSP_CHROM_SW + | ((0xa << 8) & TW5864_DSP_MB_DELAY) + ; + + input->resolution = D1; + + d1_height = (input->std == STD_NTSC) ? 480 : 576; + + input->width = d1_width; + input->height = d1_height; + + input->reg_interlacing = 0x4; + + switch (input->resolution) { + case D1: + frame_width_bus_value = 0x2cf; + frame_height_bus_value = input->height - 1; + reg_frame_bus = 0x1c; + fmt_reg_value = 0; + downscale_enabled = 0; + input->reg_dsp_codec |= TW5864_CIF_MAP_MD | TW5864_HD1_MAP_MD; + input->reg_emu |= TW5864_DSP_FRAME_TYPE_D1; + input->reg_interlacing = TW5864_DI_EN | TW5864_DSP_INTER_ST; + + tw_setl(TW5864_FULL_HALF_FLAG, 1 << input_number); + break; + case HD1: + input->height /= 2; + input->width /= 2; + frame_width_bus_value = 0x2cf; + frame_height_bus_value = input->height * 2 - 1; + reg_frame_bus = 0x1c; + fmt_reg_value = 0; + downscale_enabled = 0; + input->reg_dsp_codec |= TW5864_HD1_MAP_MD; + input->reg_emu |= TW5864_DSP_FRAME_TYPE_D1; + + tw_clearl(TW5864_FULL_HALF_FLAG, 1 << input_number); + + break; + case CIF: + input->height /= 4; + input->width /= 2; + frame_width_bus_value = 0x15f; + frame_height_bus_value = input->height * 2 - 1; + reg_frame_bus = 0x07; + fmt_reg_value = 1; + downscale_enabled = 1; + input->reg_dsp_codec |= TW5864_CIF_MAP_MD; + + tw_clearl(TW5864_FULL_HALF_FLAG, 1 << input_number); + break; + case QCIF: + input->height /= 4; + input->width /= 4; + frame_width_bus_value = 0x15f; + frame_height_bus_value = input->height * 2 - 1; + reg_frame_bus = 0x07; + fmt_reg_value = 1; + downscale_enabled = 1; + input->reg_dsp_codec |= TW5864_CIF_MAP_MD; + + tw_clearl(TW5864_FULL_HALF_FLAG, 1 << input_number); + break; + } + + /* analog input width / 4 */ + tw_indir_writeb(dev, TW5864_INDIR_IN_PIC_WIDTH(input_number), + d1_width / 4); + tw_indir_writeb(dev, TW5864_INDIR_IN_PIC_HEIGHT(input_number), + d1_height / 4); + + /* output width / 4 */ + tw_indir_writeb(dev, TW5864_INDIR_OUT_PIC_WIDTH(input_number), + input->width / 4); + tw_indir_writeb(dev, TW5864_INDIR_OUT_PIC_HEIGHT(input_number), + input->height / 4); + + tw_writel(TW5864_DSP_PIC_MAX_MB, + ((input->width / 16) << 8) | (input->height / 16)); + + tw_writel(TW5864_FRAME_WIDTH_BUS_A(input_number), + frame_width_bus_value); + tw_writel(TW5864_FRAME_WIDTH_BUS_B(input_number), + frame_width_bus_value); + tw_writel(TW5864_FRAME_HEIGHT_BUS_A(input_number), + frame_height_bus_value); + tw_writel(TW5864_FRAME_HEIGHT_BUS_B(input_number), + (frame_height_bus_value + 1) / 2 - 1); + + tw5864_frame_interval_set(input); + + if (downscale_enabled) + tw_setl(TW5864_H264EN_CH_DNS, 1 << input_number); + + tw_mask_shift_writel(TW5864_H264EN_CH_FMT_REG1, 0x3, 2 * input_number, + fmt_reg_value); + + tw_mask_shift_writel( + (input_number < 2 + ? TW5864_H264EN_RATE_MAX_LINE_REG1 + : TW5864_H264EN_RATE_MAX_LINE_REG2), + 0x1f, 5 * (input_number % 2), + input->std == STD_NTSC ? 29 : 24); + + tw_mask_shift_writel((input_number < 2) ? TW5864_FRAME_BUS1 : + TW5864_FRAME_BUS2, 0xff, (input_number % 2) * 8, + reg_frame_bus); + + spin_lock_irqsave(&dev->slock, flags); + input->enabled = 1; + spin_unlock_irqrestore(&dev->slock, flags); + + return 0; +} + +void tw5864_request_encoded_frame(struct tw5864_input *input) +{ + struct tw5864_dev *dev = input->root; + u32 enc_buf_id_new; + + tw_setl(TW5864_DSP_CODEC, TW5864_CIF_MAP_MD | TW5864_HD1_MAP_MD); + tw_writel(TW5864_EMU, input->reg_emu); + tw_writel(TW5864_INTERLACING, input->reg_interlacing); + tw_writel(TW5864_DSP, input->reg_dsp); + + tw_writel(TW5864_DSP_QP, input->reg_dsp_qp); + tw_writel(TW5864_DSP_REF_MVP_LAMBDA, input->reg_dsp_ref_mvp_lambda); + tw_writel(TW5864_DSP_I4x4_WEIGHT, input->reg_dsp_i4x4_weight); + /* 16x16 */ + tw_mask_shift_writel(TW5864_DSP_INTRA_MODE, TW5864_DSP_INTRA_MODE_MASK, + TW5864_DSP_INTRA_MODE_SHIFT, + TW5864_DSP_INTRA_MODE_16x16); + + if (input->frame_seqno % input->gop == 0) { + /* Produce I-frame */ + tw_writel(TW5864_MOTION_SEARCH_ETC, TW5864_INTRA_EN); + input->h264_frame_seqno_in_gop = 0; + input->h264_idr_pic_id++; + input->h264_idr_pic_id &= TW5864_DSP_REF_FRM; + } else { + /* Produce P-frame */ + tw_writel(TW5864_MOTION_SEARCH_ETC, + TW5864_INTRA_EN + | TW5864_ME_EN + | BIT(5) /* SRCH_OPT default */ + ); + input->h264_frame_seqno_in_gop++; + } + tw5864_prepare_frame_headers(input); + tw_writel(TW5864_VLC, + TW5864_VLC_PCI_SEL | ((input->tail_nb_bits + 24) << + TW5864_VLC_BIT_ALIGN_SHIFT) | + input->reg_dsp_qp); + + enc_buf_id_new = tw_mask_shift_readl(TW5864_ENC_BUF_PTR_REC1, 0x3, + 2 * input->input_number); + tw_writel(TW5864_DSP_ENC_ORG_PTR_REG, + ((enc_buf_id_new + 1) % 4) << TW5864_DSP_ENC_ORG_PTR_SHIFT); + tw_writel(TW5864_DSP_ENC_REC, + (((enc_buf_id_new + 1) % 4) << 12) | (enc_buf_id_new & 0x3)); + + tw_writel(TW5864_SLICE, TW5864_START_NSLICE); + tw_writel(TW5864_SLICE, 0); +} + +static int tw5864_disable_input(struct tw5864_input *input) +{ + struct tw5864_dev *dev = input->root; + unsigned long flags; + + dev_dbg(&dev->pci->dev, "Disabling channel %d\n", input->input_number); + + spin_lock_irqsave(&dev->slock, flags); + input->enabled = 0; + spin_unlock_irqrestore(&dev->slock, flags); + return 0; +} + +static int tw5864_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct tw5864_input *input = vb2_get_drv_priv(q); + + tw5864_enable_input(input); + return 0; +} + +static void tw5864_stop_streaming(struct vb2_queue *q) +{ + unsigned long flags; + struct tw5864_input *input = vb2_get_drv_priv(q); + + tw5864_disable_input(input); + + spin_lock_irqsave(&input->slock, flags); + if (input->vb) { + vb2_buffer_done(&input->vb->vb.vb2_buf, VB2_BUF_STATE_ERROR); + input->vb = NULL; + } + while (!list_empty(&input->active)) { + struct tw5864_buf *buf = container_of(input->active.next, + struct tw5864_buf, list); + + list_del(&buf->list); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + } + spin_unlock_irqrestore(&input->slock, flags); +} + +static struct vb2_ops tw5864_video_qops = { + .queue_setup = tw5864_queue_setup, + .buf_queue = tw5864_buf_queue, + .start_streaming = tw5864_start_streaming, + .stop_streaming = tw5864_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static int tw5864_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct tw5864_input *input = + container_of(ctrl->handler, struct tw5864_input, hdl); + struct tw5864_dev *dev = input->root; + unsigned long flags; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + tw_indir_writeb(dev, + TW5864_INDIR_VIN_A_BRIGHT(input->input_number), + (u8)ctrl->val); + break; + case V4L2_CID_HUE: + tw_indir_writeb(dev, + TW5864_INDIR_VIN_7_HUE(input->input_number), + (u8)ctrl->val); + break; + case V4L2_CID_CONTRAST: + tw_indir_writeb(dev, + TW5864_INDIR_VIN_9_CNTRST(input->input_number), + (u8)ctrl->val); + break; + case V4L2_CID_SATURATION: + tw_indir_writeb(dev, + TW5864_INDIR_VIN_B_SAT_U(input->input_number), + (u8)ctrl->val); + tw_indir_writeb(dev, + TW5864_INDIR_VIN_C_SAT_V(input->input_number), + (u8)ctrl->val); + break; + case V4L2_CID_MPEG_VIDEO_GOP_SIZE: + input->gop = ctrl->val; + return 0; + case V4L2_CID_MPEG_VIDEO_H264_MIN_QP: + spin_lock_irqsave(&input->slock, flags); + input->qp = ctrl->val; + input->reg_dsp_qp = input->qp; + input->reg_dsp_ref_mvp_lambda = lambda_lookup_table[input->qp]; + input->reg_dsp_i4x4_weight = intra4x4_lambda3[input->qp]; + spin_unlock_irqrestore(&input->slock, flags); + return 0; + case V4L2_CID_DETECT_MD_GLOBAL_THRESHOLD: + memset(input->md_threshold_grid_values, ctrl->val, + sizeof(input->md_threshold_grid_values)); + return 0; + case V4L2_CID_DETECT_MD_MODE: + return 0; + case V4L2_CID_DETECT_MD_THRESHOLD_GRID: + /* input->md_threshold_grid_ctrl->p_new.p_u16 contains data */ + memcpy(input->md_threshold_grid_values, + input->md_threshold_grid_ctrl->p_new.p_u16, + sizeof(input->md_threshold_grid_values)); + return 0; + } + return 0; +} + +static int tw5864_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct tw5864_input *input = video_drvdata(file); + enum tw5864_vid_std std; + int ret; + + ret = tw5864_input_std_get(input, &std); + if (ret) + return ret; + + f->fmt.pix.width = 720; + switch (std) { + default: + WARN_ON_ONCE(1); + case STD_NTSC: + f->fmt.pix.height = 480; + break; + case STD_PAL: + case STD_SECAM: + f->fmt.pix.height = 576; + break; + } + f->fmt.pix.field = V4L2_FIELD_NONE; + f->fmt.pix.pixelformat = V4L2_PIX_FMT_H264; + f->fmt.pix.sizeimage = H264_VLC_BUF_SIZE; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + f->fmt.pix.priv = 0; + return 0; +} + +static int tw5864_enum_input(struct file *file, void *priv, + struct v4l2_input *i) +{ + struct tw5864_input *dev = video_drvdata(file); + + u8 indir_0x000 = tw_indir_readb(dev->root, + TW5864_INDIR_VIN_0(dev->input_number)); + u8 indir_0x00d = tw_indir_readb(dev->root, + TW5864_INDIR_VIN_D(dev->input_number)); + u8 v1 = indir_0x000; + u8 v2 = indir_0x00d; + + if (i->index) + return -EINVAL; + + i->type = V4L2_INPUT_TYPE_CAMERA; + snprintf(i->name, sizeof(i->name), "Encoder %d", dev->input_number); + i->std = TW5864_NORMS; + if (v1 & (1 << 7)) + i->status |= V4L2_IN_ST_NO_SYNC; + if (!(v1 & (1 << 6))) + i->status |= V4L2_IN_ST_NO_H_LOCK; + if (v1 & (1 << 2)) + i->status |= V4L2_IN_ST_NO_SIGNAL; + if (v1 & (1 << 1)) + i->status |= V4L2_IN_ST_NO_COLOR; + if (v2 & (1 << 2)) + i->status |= V4L2_IN_ST_MACROVISION; + + return 0; +} + +static int tw5864_g_input(struct file *file, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int tw5864_s_input(struct file *file, void *priv, unsigned int i) +{ + if (i) + return -EINVAL; + return 0; +} + +static int tw5864_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct tw5864_input *dev = video_drvdata(file); + + strcpy(cap->driver, "tw5864"); + snprintf(cap->card, sizeof(cap->card), "TW5864 Encoder %d", + dev->input_number); + sprintf(cap->bus_info, "PCI:%s", pci_name(dev->root->pci)); + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; + + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + return 0; +} + +static int tw5864_g_std(struct file *file, void *priv, v4l2_std_id *id) +{ + struct tw5864_input *input = video_drvdata(file); + enum tw5864_vid_std std; + int ret; + + ret = tw5864_input_std_get(input, &std); + if (ret) + return ret; + + *id = tw5864_get_v4l2_std(std); + return 0; +} + +static int tw5864_s_std(struct file *file, void *priv, v4l2_std_id id) +{ + struct tw5864_input *input = video_drvdata(file); + enum tw5864_vid_std std; + int ret; + + ret = tw5864_input_std_get(input, &std); + if (ret) + return ret; + + /* Allow only if matches with currently detected */ + if (id != tw5864_get_v4l2_std(std)) + return -EINVAL; + + return 0; +} + +static int tw5864_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + return tw5864_g_fmt_vid_cap(file, priv, f); +} + +static int tw5864_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + return tw5864_try_fmt_vid_cap(file, priv, f); +} + +static int tw5864_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + if (f->index) + return -EINVAL; + + f->pixelformat = V4L2_PIX_FMT_H264; + strcpy(f->description, "H.264"); + + return 0; +} + +static int tw5864_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_CTRL: + return v4l2_ctrl_subscribe_event(fh, sub); + case V4L2_EVENT_MOTION_DET: + /* + * Allow for up to 30 events (1 second for NTSC) to be stored. + */ + return v4l2_event_subscribe(fh, sub, 30, NULL); + } + return -EINVAL; +} + +static void tw5864_frame_interval_set(struct tw5864_input *input) +{ + /* + * This register value seems to follow such approach: In each second + * interval, when processing Nth frame, it checks Nth bit of register + * value and, if the bit is 1, it processes the frame, otherwise the + * frame is discarded. + * So unary representation would work, but more or less equal gaps + * between the frames should be preserved. + * For 1 FPS - 0x00000001 + * 00000000 00000000 00000000 00000001 + * + * For 2 FPS - 0x00010001. + * 00000000 00000001 00000000 00000001 + * + * For 4 FPS - 0x01010101. + * 00000001 00000001 00000001 00000001 + * + * For 8 FPS - 0x11111111. + * 00010001 00010001 00010001 00010001 + * + * For 16 FPS - 0x55555555. + * 01010101 01010101 01010101 01010101 + * + * For 32 FPS (not reached - capped by 25/30 limit) - 0xffffffff. + * 11111111 11111111 11111111 11111111 + * + * Et cetera. + */ + struct tw5864_dev *dev = input->root; + u32 unary_framerate = 0; + int shift = 0; + + for (shift = 0; shift <= 32; shift += input->frame_interval) + unary_framerate |= 0x00000001 << shift; + + tw_writel(TW5864_H264EN_RATE_CNTL_LO_WORD(input->input_number, 0), + unary_framerate >> 16); + tw_writel(TW5864_H264EN_RATE_CNTL_HI_WORD(input->input_number, 0), + unary_framerate & 0xffff); +} + +static int tw5864_frameinterval_get(struct tw5864_input *input, + struct v4l2_fract *frameinterval) +{ + int ret; + enum tw5864_vid_std std; + + ret = tw5864_input_std_get(input, &std); + if (ret) + return ret; + + frameinterval->numerator = 1; + + switch (std) { + case STD_NTSC: + case STD_SECAM: + frameinterval->denominator = 25; + break; + case STD_PAL: + frameinterval->denominator = 30; + break; + default: + WARN(1, "tw5864_frameinterval_get requested for unknown std %d\n", + std); + return 1; + } + + return 0; +} + +static int tw5864_enum_frameintervals(struct file *file, void *priv, + struct v4l2_frmivalenum *fintv) +{ + struct tw5864_input *input = video_drvdata(file); + + if (fintv->pixel_format != V4L2_PIX_FMT_H264) + return -EINVAL; + if (fintv->index) + return -EINVAL; + + fintv->type = V4L2_FRMIVAL_TYPE_DISCRETE; + + return tw5864_frameinterval_get(input, &fintv->discrete); +} + +static int tw5864_g_parm(struct file *file, void *priv, + struct v4l2_streamparm *sp) +{ + struct tw5864_input *input = video_drvdata(file); + struct v4l2_captureparm *cp = &sp->parm.capture; + int ret; + + cp->capability = V4L2_CAP_TIMEPERFRAME; + + ret = tw5864_frameinterval_get(input, &cp->timeperframe); + cp->timeperframe.numerator *= input->frame_interval; + cp->capturemode = 0; + cp->readbuffers = 2; + + return ret; +} + +static int tw5864_s_parm(struct file *file, void *priv, + struct v4l2_streamparm *sp) +{ + struct tw5864_input *input = video_drvdata(file); + struct v4l2_fract *t = &sp->parm.capture.timeperframe; + struct v4l2_fract time_base; + int ret; + + ret = tw5864_frameinterval_get(input, &time_base); + if (ret) + return ret; + + if (!t->numerator || !t->denominator) { + dev_err(&input->root->pci->dev, + "weird timeperframe %u/%u, using current %u/%u\n", + t->numerator, t->denominator, + input->frame_interval, time_base.denominator); + t->numerator = input->frame_interval; + t->denominator = time_base.denominator; + } else if (t->denominator != time_base.denominator) { + t->numerator = t->numerator * time_base.denominator / + t->denominator; + t->denominator = time_base.denominator; + } + + input->frame_interval = t->numerator; + tw5864_frame_interval_set(input); + return tw5864_g_parm(file, priv, sp); +} + +static const struct v4l2_ctrl_ops tw5864_ctrl_ops = { + .s_ctrl = tw5864_s_ctrl, +}; + +static const struct v4l2_file_operations video_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, + .unlocked_ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops video_ioctl_ops = { + .vidioc_querycap = tw5864_querycap, + .vidioc_enum_fmt_vid_cap = tw5864_enum_fmt_vid_cap, + .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_expbuf = vb2_ioctl_expbuf, + .vidioc_s_std = tw5864_s_std, + .vidioc_g_std = tw5864_g_std, + .vidioc_enum_input = tw5864_enum_input, + .vidioc_g_input = tw5864_g_input, + .vidioc_s_input = tw5864_s_input, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_try_fmt_vid_cap = tw5864_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = tw5864_s_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = tw5864_g_fmt_vid_cap, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = tw5864_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, + .vidioc_enum_frameintervals = tw5864_enum_frameintervals, + .vidioc_s_parm = tw5864_s_parm, + .vidioc_g_parm = tw5864_g_parm, +}; + +static struct video_device tw5864_video_template = { + .name = "tw5864_video", + .fops = &video_fops, + .ioctl_ops = &video_ioctl_ops, + .release = video_device_release_empty, + .tvnorms = TW5864_NORMS, +}; + +/* The TW5864 uses 192 (16x12) detection cells in full screen for motion + * detection. Each detection cell is composed of 44 pixels and 20 lines for + * NTSC and 24 lines for PAL. + */ +#define MD_CELLS_HOR 16 +#define MD_CELLS_VERT 12 + +/* Motion Detection Threshold matrix */ +static const struct v4l2_ctrl_config tw5864_md_thresholds = { + .ops = &tw5864_ctrl_ops, + .id = V4L2_CID_DETECT_MD_THRESHOLD_GRID, + .dims = {MD_CELLS_HOR, MD_CELLS_VERT}, + .def = 14, + /* See tw5864_md_metric_from_mvd() */ + .max = 2 * 0x0f, + .step = 1, +}; + +static int tw5864_video_input_init(struct tw5864_input *dev, int video_nr); +static void tw5864_video_input_fini(struct tw5864_input *dev); +static void tw5864_tables_upload(struct tw5864_dev *dev); + +int tw5864_video_init(struct tw5864_dev *dev, int *video_nr) +{ + int i; + int ret = -1; + + for (i = 0; i < H264_BUF_CNT; i++) { + dev->h264_buf[i].vlc.addr = + dma_alloc_coherent(&dev->pci->dev, H264_VLC_BUF_SIZE, + &dev->h264_buf[i].vlc.dma_addr, + GFP_KERNEL | GFP_DMA32); + dev->h264_buf[i].mv.addr = + dma_alloc_coherent(&dev->pci->dev, H264_MV_BUF_SIZE, + &dev->h264_buf[i].mv.dma_addr, + GFP_KERNEL | GFP_DMA32); + if (!dev->h264_buf[i].vlc.addr || !dev->h264_buf[i].mv.addr) { + dev_err(&dev->pci->dev, "dma alloc & map fail\n"); + ret = -ENOMEM; + goto dma_alloc_fail; + } + } + + tw5864_tables_upload(dev); + tw5864_init_ad(dev); + + /* Picture is distorted without this block */ + /* use falling edge to sample 54M to 108M */ + tw_indir_writeb(dev, TW5864_INDIR_VD_108_POL, + TW5864_INDIR_VD_108_POL_BOTH); + tw_indir_writeb(dev, TW5864_INDIR_CLK0_SEL, 0x00); + + tw_indir_writeb(dev, TW5864_INDIR_DDRA_DLL_DQS_SEL0, 0x02); + tw_indir_writeb(dev, TW5864_INDIR_DDRA_DLL_DQS_SEL1, 0x02); + tw_indir_writeb(dev, TW5864_INDIR_DDRA_DLL_CLK90_SEL, 0x02); + tw_indir_writeb(dev, TW5864_INDIR_DDRB_DLL_DQS_SEL0, 0x02); + tw_indir_writeb(dev, TW5864_INDIR_DDRB_DLL_DQS_SEL1, 0x02); + tw_indir_writeb(dev, TW5864_INDIR_DDRB_DLL_CLK90_SEL, 0x02); + + /* video input reset */ + tw_indir_writeb(dev, TW5864_INDIR_RESET, 0); + tw_indir_writeb(dev, TW5864_INDIR_RESET, TW5864_INDIR_RESET_VD | + TW5864_INDIR_RESET_DLL | TW5864_INDIR_RESET_MUX_CORE); + mdelay(10); + + /* + * Select Part A mode for all channels. + * tw_setl instead of tw_clearl for Part B mode. + * + * I guess "Part B" is primarily for downscaled version of same channel + * which goes in Part A of same bus + */ + tw_writel(TW5864_FULL_HALF_MODE_SEL, 0); + + tw_indir_writeb(dev, TW5864_INDIR_PV_VD_CK_POL, + TW5864_INDIR_PV_VD_CK_POL_VD(0) | + TW5864_INDIR_PV_VD_CK_POL_VD(1) | + TW5864_INDIR_PV_VD_CK_POL_VD(2) | + TW5864_INDIR_PV_VD_CK_POL_VD(3)); + + dev->h264_buf_r_index = 0; + dev->h264_buf_w_index = 0; + tw_writel(TW5864_VLC_STREAM_BASE_ADDR, + dev->h264_buf[dev->h264_buf_w_index].vlc.dma_addr); + tw_writel(TW5864_MV_STREAM_BASE_ADDR, + dev->h264_buf[dev->h264_buf_w_index].mv.dma_addr); + + for (i = 0; i < TW5864_INPUTS; i++) { + tw_indir_writeb(dev, TW5864_INDIR_VIN_E(i), 0x07); + /* to initiate auto format recognition */ + tw_indir_writeb(dev, TW5864_INDIR_VIN_F(i), 0xff); + } + + tw_writel(TW5864_SEN_EN_CH, 0x000f); + tw_writel(TW5864_H264EN_CH_EN, 0x000f); + + tw_writel(TW5864_H264EN_BUS0_MAP, 0x00000000); + tw_writel(TW5864_H264EN_BUS1_MAP, 0x00001111); + tw_writel(TW5864_H264EN_BUS2_MAP, 0x00002222); + tw_writel(TW5864_H264EN_BUS3_MAP, 0x00003333); + + /* + * Quote from Intersil (manufacturer): + * 0x0038 is managed by HW, and by default it won't pass the pointer set + * at 0x0010. So if you don't do encoding, 0x0038 should stay at '3' + * (with 4 frames in buffer). If you encode one frame and then move + * 0x0010 to '1' for example, HW will take one more frame and set it to + * buffer #0, and then you should see 0x0038 is set to '0'. There is + * only one HW encoder engine, so 4 channels cannot get encoded + * simultaneously. But each channel does have its own buffer (for + * original frames and reconstructed frames). So there is no problem to + * manage encoding for 4 channels at same time and no need to force + * I-frames in switching channels. + * End of quote. + * + * If we set 0x0010 (TW5864_ENC_BUF_PTR_REC1) to 0 (for any channel), we + * have no "rolling" (until we change this value). + * If we set 0x0010 (TW5864_ENC_BUF_PTR_REC1) to 0x3, it starts to roll + * continuously together with 0x0038. + */ + tw_writel(TW5864_ENC_BUF_PTR_REC1, 0x00ff); + tw_writel(TW5864_PCI_INTTM_SCALE, 3); + + tw_writel(TW5864_INTERLACING, TW5864_DI_EN); + tw_writel(TW5864_MASTER_ENB_REG, TW5864_PCI_VLC_INTR_ENB); + tw_writel(TW5864_PCI_INTR_CTL, + TW5864_TIMER_INTR_ENB | TW5864_PCI_MAST_ENB | + TW5864_MVD_VLC_MAST_ENB); + + dev->encoder_busy = 0; + + dev->irqmask |= TW5864_INTR_VLC_DONE | TW5864_INTR_TIMER; + tw5864_irqmask_apply(dev); + + tasklet_init(&dev->tasklet, tw5864_handle_frame_task, + (unsigned long)dev); + + for (i = 0; i < TW5864_INPUTS; i++) { + dev->inputs[i].root = dev; + dev->inputs[i].input_number = i; + ret = tw5864_video_input_init(&dev->inputs[i], video_nr[i]); + if (ret) + goto input_init_fail; + } + + return 0; + +dma_alloc_fail: + for (i = 0; i < H264_BUF_CNT; i++) { + dma_free_coherent(&dev->pci->dev, H264_VLC_BUF_SIZE, + dev->h264_buf[i].vlc.addr, + dev->h264_buf[i].vlc.dma_addr); + dma_free_coherent(&dev->pci->dev, H264_MV_BUF_SIZE, + dev->h264_buf[i].mv.addr, + dev->h264_buf[i].mv.dma_addr); + } + + i = TW5864_INPUTS; + +input_init_fail: + for (; i >= 0; i--) + tw5864_video_input_fini(&dev->inputs[i]); + + tasklet_kill(&dev->tasklet); + + return ret; +} + +static int tw5864_video_input_init(struct tw5864_input *input, int video_nr) +{ + int ret; + struct v4l2_ctrl_handler *hdl = &input->hdl; + + mutex_init(&input->lock); + spin_lock_init(&input->slock); + + /* setup video buffers queue */ + INIT_LIST_HEAD(&input->active); + input->vidq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + input->vidq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + input->vidq.io_modes = VB2_MMAP | VB2_READ | VB2_DMABUF; + input->vidq.ops = &tw5864_video_qops; + input->vidq.mem_ops = &vb2_dma_contig_memops; + input->vidq.drv_priv = input; + input->vidq.gfp_flags = __GFP_DMA32; + input->vidq.buf_struct_size = sizeof(struct tw5864_buf); + input->vidq.lock = &input->lock; + input->vidq.min_buffers_needed = 12; + ret = vb2_queue_init(&input->vidq); + if (ret) + goto vb2_q_init_fail; + + input->vdev = tw5864_video_template; + input->vdev.v4l2_dev = &input->root->v4l2_dev; + input->vdev.lock = &input->lock; + input->vdev.queue = &input->vidq; + video_set_drvdata(&input->vdev, input); + + /* Initialize the device control structures */ + input->alloc_ctx = vb2_dma_contig_init_ctx(&input->root->pci->dev); + if (IS_ERR(input->alloc_ctx)) { + ret = PTR_ERR(input->alloc_ctx); + goto vb2_dma_contig_init_ctx_fail; + } + + v4l2_ctrl_handler_init(hdl, 6); + v4l2_ctrl_new_std(hdl, &tw5864_ctrl_ops, + V4L2_CID_BRIGHTNESS, -128, 127, 1, 0); + v4l2_ctrl_new_std(hdl, &tw5864_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, 100); + v4l2_ctrl_new_std(hdl, &tw5864_ctrl_ops, + V4L2_CID_SATURATION, 0, 255, 1, 128); + /* NTSC only */ + v4l2_ctrl_new_std(hdl, &tw5864_ctrl_ops, V4L2_CID_HUE, -128, 127, 1, 0); + v4l2_ctrl_new_std(hdl, &tw5864_ctrl_ops, + V4L2_CID_MPEG_VIDEO_GOP_SIZE, 1, 255, 1, GOP_SIZE); + v4l2_ctrl_new_std(hdl, &tw5864_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_MIN_QP, 28, 51, 1, QP_VALUE); + v4l2_ctrl_new_std_menu(hdl, &tw5864_ctrl_ops, + V4L2_CID_DETECT_MD_MODE, + V4L2_DETECT_MD_MODE_THRESHOLD_GRID, 0, + V4L2_DETECT_MD_MODE_DISABLED); + v4l2_ctrl_new_std(hdl, &tw5864_ctrl_ops, + V4L2_CID_DETECT_MD_GLOBAL_THRESHOLD, + tw5864_md_thresholds.min, tw5864_md_thresholds.max, + tw5864_md_thresholds.step, tw5864_md_thresholds.def); + input->md_threshold_grid_ctrl = + v4l2_ctrl_new_custom(hdl, &tw5864_md_thresholds, NULL); + if (hdl->error) { + ret = hdl->error; + goto v4l2_ctrl_fail; + } + input->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_setup(hdl); + + input->qp = QP_VALUE; + input->gop = GOP_SIZE; + input->frame_interval = 1; + + ret = video_register_device(&input->vdev, VFL_TYPE_GRABBER, video_nr); + if (ret) + goto v4l2_ctrl_fail; + + dev_info(&input->root->pci->dev, "Registered video device %s\n", + video_device_node_name(&input->vdev)); + + return 0; + +v4l2_ctrl_fail: + v4l2_ctrl_handler_free(hdl); + vb2_dma_contig_cleanup_ctx(input->alloc_ctx); +vb2_dma_contig_init_ctx_fail: + vb2_queue_release(&input->vidq); +vb2_q_init_fail: + mutex_destroy(&input->lock); + + return ret; +} + +static void tw5864_video_input_fini(struct tw5864_input *dev) +{ + video_unregister_device(&dev->vdev); + v4l2_ctrl_handler_free(&dev->hdl); + vb2_dma_contig_cleanup_ctx(dev->alloc_ctx); + vb2_queue_release(&dev->vidq); +} + +void tw5864_video_fini(struct tw5864_dev *dev) +{ + int i; + + tasklet_kill(&dev->tasklet); + + for (i = 0; i < TW5864_INPUTS; i++) + tw5864_video_input_fini(&dev->inputs[i]); + + for (i = 0; i < H264_BUF_CNT; i++) { + dma_free_coherent(&dev->pci->dev, H264_VLC_BUF_SIZE, + dev->h264_buf[i].vlc.addr, + dev->h264_buf[i].vlc.dma_addr); + dma_free_coherent(&dev->pci->dev, H264_MV_BUF_SIZE, + dev->h264_buf[i].mv.addr, + dev->h264_buf[i].mv.dma_addr); + } +} + +void tw5864_prepare_frame_headers(struct tw5864_input *input) +{ + struct tw5864_buf *vb = input->vb; + u8 *dst; + unsigned long dst_size; + size_t dst_space; + unsigned long flags; + + u8 *sl_hdr; + unsigned long space_before_sl_hdr; + + if (!vb) { + spin_lock_irqsave(&input->slock, flags); + if (list_empty(&input->active)) { + spin_unlock_irqrestore(&input->slock, flags); + input->vb = NULL; + return; + } + vb = list_first_entry(&input->active, struct tw5864_buf, list); + list_del(&vb->list); + spin_unlock_irqrestore(&input->slock, flags); + } + + dst = vb2_plane_vaddr(&vb->vb.vb2_buf, 0); + dst_size = vb2_plane_size(&vb->vb.vb2_buf, 0); + dst_space = dst_size; + + /* + * Generate H264 headers: + * If this is first frame, put SPS and PPS + */ + if (input->frame_seqno == 0) + tw5864_h264_put_stream_header(&dst, &dst_space, input->qp, + input->width, input->height); + + /* Put slice header */ + sl_hdr = dst; + space_before_sl_hdr = dst_space; + tw5864_h264_put_slice_header(&dst, &dst_space, input->h264_idr_pic_id, + input->h264_frame_seqno_in_gop, + &input->tail_nb_bits, &input->tail); + input->vb = vb; + input->buf_cur_ptr = dst; + input->buf_cur_space_left = dst_space; +} + +/* + * Returns heuristic motion detection metric value from known components of + * hardware-provided Motion Vector Data. + */ +static unsigned int tw5864_md_metric_from_mvd(u32 mvd) +{ + /* + * Format of motion vector data exposed by tw5864, according to + * manufacturer: + * mv_x 10 bits + * mv_y 10 bits + * non_zero_members 8 bits + * mb_type 3 bits + * reserved 1 bit + * + * non_zero_members: number of non-zero residuals in each macro block + * after quantization + * + * unsigned int reserved = mvd >> 31; + * unsigned int mb_type = (mvd >> 28) & 0x7; + * unsigned int non_zero_members = (mvd >> 20) & 0xff; + */ + unsigned int mv_y = (mvd >> 10) & 0x3ff; + unsigned int mv_x = mvd & 0x3ff; + + /* heuristic: */ + mv_x &= 0x0f; + mv_y &= 0x0f; + + return mv_y + mv_x; +} + +static int tw5864_is_motion_triggered(struct tw5864_h264_frame *frame) +{ + struct tw5864_input *input = frame->input; + u32 *mv = (u32 *)frame->mv.addr; + int i; + int detected = 0; + unsigned int md_cells = MD_CELLS_HOR * MD_CELLS_VERT; + +#ifdef DEBUG + /* Stats */ + unsigned int max = 0; + unsigned int min = UINT_MAX; + unsigned int sum = 0; + unsigned int cnt_above_thresh = 0; +#endif + + for (i = 0; i < md_cells; i++) { + const u16 thresh = input->md_threshold_grid_values[i]; + const unsigned int metric = tw5864_md_metric_from_mvd(mv[i]); + + if (metric > thresh) + detected = 1; + +#ifdef DEBUG + if (metric > thresh) + cnt_above_thresh++; + if (metric > max) + max = metric; + if (metric < min) + min = metric; + sum += metric; +#else + if (detected) + break; +#endif + } +#ifdef DEBUG + dev_dbg(&input->root->pci->dev, + "input %d, frame md stats: min %u, max %u, avg %u, cells above threshold: %u\n", + input->input_number, min, max, sum / md_cells, + cnt_above_thresh); +#endif + return detected; +} + +#ifdef MD_DUMP +static void tw5864_md_dump(struct tw5864_input *input) +{ + struct tw5864_dev *dev = input->root; + u32 *mv = (u32 *)dev->h264_mv_buf[dev->h264_buf_index].addr; + int offset = 0; + int i; + + if (input->h264_frame_seqno_in_gop) { + offset = 0; + for (i = 0; i < MD_CELLS_VERT; i++) { + dev_dbg(&dev->pci->dev, + "MVD [%02d]: %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x\n", + i, mv[offset + 0], mv[offset + 1], + mv[offset + 2], mv[offset + 3], mv[offset + 4], + mv[offset + 5], mv[offset + 6], mv[offset + 7], + mv[offset + 8], mv[offset + 9], mv[offset + 10], + mv[offset + 11], mv[offset + 12], + mv[offset + 13], mv[offset + 14], + mv[offset + 15] + ); + offset += MD_CELLS_HOR; + } + offset = 0; + for (i = 0; i < MD_CELLS_VERT; i++) { + dev_dbg(&dev->pci->dev, + "MD heur [%02d]: % 2x % 2x % 2x % 2x % 2x % 2x % 2x % 2x % 2x % 2x % 2x % 2x % 2x % 2x % 2x % 2x\n", + i, tw5864_md_metric_from_mvd(mv[offset + 0]), + tw5864_md_metric_from_mvd(mv[offset + 1]), + tw5864_md_metric_from_mvd(mv[offset + 2]), + tw5864_md_metric_from_mvd(mv[offset + 3]), + tw5864_md_metric_from_mvd(mv[offset + 4]), + tw5864_md_metric_from_mvd(mv[offset + 5]), + tw5864_md_metric_from_mvd(mv[offset + 6]), + tw5864_md_metric_from_mvd(mv[offset + 7]), + tw5864_md_metric_from_mvd(mv[offset + 8]), + tw5864_md_metric_from_mvd(mv[offset + 9]), + tw5864_md_metric_from_mvd(mv[offset + 10]), + tw5864_md_metric_from_mvd(mv[offset + 11]), + tw5864_md_metric_from_mvd(mv[offset + 12]), + tw5864_md_metric_from_mvd(mv[offset + 13]), + tw5864_md_metric_from_mvd(mv[offset + 14]), + tw5864_md_metric_from_mvd(mv[offset + 15]) + ); + offset += MD_CELLS_HOR; + } + } +} +#endif + +static void tw5864_handle_frame_task(unsigned long data) +{ + struct tw5864_dev *dev = (struct tw5864_dev *)data; + unsigned long flags; + int batch_size = H264_BUF_CNT; + + spin_lock_irqsave(&dev->slock, flags); + while (dev->h264_buf_r_index != dev->h264_buf_w_index && batch_size--) { + spin_unlock_irqrestore(&dev->slock, flags); + tw5864_handle_frame(&dev->h264_buf[dev->h264_buf_r_index]); + spin_lock_irqsave(&dev->slock, flags); + + dev->h264_buf_r_index++; + dev->h264_buf_r_index %= H264_BUF_CNT; + } + spin_unlock_irqrestore(&dev->slock, flags); +} + +#ifdef DEBUG +static u32 checksum(u32 *data, int len) +{ + u32 val, count_len = len; + + val = *data++; + while (((count_len >> 2) - 1) > 0) { + val ^= *data++; + count_len -= 4; + } + val ^= htonl((len >> 2)); + return val; +} +#endif + +static void tw5864_handle_frame(struct tw5864_h264_frame *frame) +{ + struct tw5864_input *input = frame->input; + struct tw5864_dev *dev = input->root; + struct tw5864_buf *vb; + struct vb2_v4l2_buffer *v4l2_buf; + int frame_len = frame->vlc_len; + unsigned long dst_size; + unsigned long dst_space; + int skip_bytes = 3; + u8 *dst = input->buf_cur_ptr; + u8 tail_mask, vlc_mask = 0; + int i; + u8 vlc_first_byte = ((u8 *)(frame->vlc.addr + skip_bytes))[0]; + unsigned long flags; + +#ifdef DEBUG + if (frame->checksum != checksum((u32 *)frame->vlc.addr, frame_len)) + dev_err(&dev->pci->dev, + "Checksum of encoded frame doesn't match!\n"); +#endif + + spin_lock_irqsave(&input->slock, flags); + vb = input->vb; + input->vb = NULL; + spin_unlock_irqrestore(&input->slock, flags); + + v4l2_buf = to_vb2_v4l2_buffer(&vb->vb.vb2_buf); + + if (!vb) { /* Gone because of disabling */ + dev_dbg(&dev->pci->dev, "vb is empty, dropping frame\n"); + return; + } + + dst_size = vb2_plane_size(&vb->vb.vb2_buf, 0); + + dst_space = input->buf_cur_space_left; + frame_len -= skip_bytes; + if (WARN_ON_ONCE(dst_space < frame_len)) { + dev_err_once(&dev->pci->dev, + "Left space in vb2 buffer %lu is insufficient for frame length %d, writing truncated frame\n", + dst_space, frame_len); + frame_len = dst_space; + } + + for (i = 0; i < 8 - input->tail_nb_bits; i++) + vlc_mask |= 1 << i; + tail_mask = (~vlc_mask) & 0xff; + + dst[0] = (input->tail & tail_mask) | (vlc_first_byte & vlc_mask); + skip_bytes++; + frame_len--; + dst++; + dst_space--; + memcpy(dst, frame->vlc.addr + skip_bytes, frame_len); + dst_space -= frame_len; + vb2_set_plane_payload(&vb->vb.vb2_buf, 0, dst_size - dst_space); + + vb->vb.vb2_buf.timestamp = frame->timestamp; + v4l2_buf->field = V4L2_FIELD_NONE; + v4l2_buf->sequence = input->frame_seqno - 1; + + /* Check for motion flags */ + if (input->h264_frame_seqno_in_gop /* P-frame */ && + tw5864_is_motion_triggered(frame)) { + struct v4l2_event ev = { + .type = V4L2_EVENT_MOTION_DET, + .u.motion_det = { + .flags = + V4L2_EVENT_MD_FL_HAVE_FRAME_SEQ, + .frame_sequence = + v4l2_buf->sequence, + }, + }; + + v4l2_event_queue(&input->vdev, &ev); + } + + vb2_buffer_done(&vb->vb.vb2_buf, VB2_BUF_STATE_DONE); + +#ifdef MD_DUMP + tw5864_md_dump(input); +#endif +} + +v4l2_std_id tw5864_get_v4l2_std(enum tw5864_vid_std std) +{ + switch (std) { + case STD_NTSC: + return V4L2_STD_NTSC_M; + case STD_PAL: + return V4L2_STD_PAL_B; + case STD_SECAM: + return V4L2_STD_SECAM_B; + case STD_INVALID: + WARN_ON_ONCE(1); + return 0; + } + return 0; +} + +enum tw5864_vid_std tw5864_from_v4l2_std(v4l2_std_id v4l2_std) +{ + if (v4l2_std & V4L2_STD_NTSC) + return STD_NTSC; + if (v4l2_std & V4L2_STD_PAL) + return STD_PAL; + if (v4l2_std & V4L2_STD_SECAM) + return STD_SECAM; + WARN_ON_ONCE(1); + return STD_AUTO; +} + +static void tw5864_tables_upload(struct tw5864_dev *dev) +{ + int i; + + tw_writel(TW5864_VLC_RD, 0x1); + for (i = 0; i < VLC_LOOKUP_TABLE_LEN; i++) { + tw_writel((TW5864_VLC_STREAM_MEM_START + (i << 2)), + encoder_vlc_lookup_table[i]); + } + tw_writel(TW5864_VLC_RD, 0x0); + + for (i = 0; i < QUANTIZATION_TABLE_LEN; i++) { + tw_writel((TW5864_QUAN_TAB + (i << 2)), + forward_quantization_table[i]); + } + + for (i = 0; i < QUANTIZATION_TABLE_LEN; i++) { + tw_writel((TW5864_QUAN_TAB + (i << 2)), + inverse_quantization_table[i]); + } +} diff --git a/drivers/staging/media/tw5864/tw5864.h b/drivers/staging/media/tw5864/tw5864.h new file mode 100644 index 0000000..d140fee --- /dev/null +++ b/drivers/staging/media/tw5864/tw5864.h @@ -0,0 +1,280 @@ +/* + * TW5864 driver - common header file + * + * Copyright (C) 2015 Bluecherry, LLC + * Author: Andrey Utkin + * + * 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 +#include +#include + +#include "tw5864-reg.h" + +#define TW5864_NORMS ( \ + V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM | \ + V4L2_STD_PAL_M | V4L2_STD_PAL_Nc | V4L2_STD_PAL_60) + +/* ----------------------------------------------------------- */ +/* static data */ + +struct tw5864_tvnorm { + char *name; + v4l2_std_id id; + + /* video decoder */ + u32 sync_control; + u32 luma_control; + u32 chroma_ctrl1; + u32 chroma_gain; + u32 chroma_ctrl2; + u32 vgate_misc; + + /* video scaler */ + u32 h_delay; + u32 h_start; + u32 h_stop; + u32 v_delay; + u32 video_v_start; + u32 video_v_stop; + u32 vbi_v_start_0; + u32 vbi_v_stop_0; + u32 vbi_v_start_1; + + /* Techwell specific */ + u32 format; +}; + +struct tw5864_format { + char *name; + u32 fourcc; + u32 depth; + u32 twformat; +}; + +/* ----------------------------------------------------------- */ +/* card configuration */ + +#define TW5864_INPUTS 4 + +#define H264_VLC_BUF_SIZE 0x80000 +#define H264_MV_BUF_SIZE 0x40000 +#define QP_VALUE 28 +#define BITALIGN_VALUE_IN_TIMER 0 +#define BITALIGN_VALUE_IN_INIT 0 +#define GOP_SIZE 32 + +enum resolution { + D1 = 1, + HD1 = 2, /* half d1 - 360x(240|288) */ + CIF = 3, + QCIF = 4, +}; + +/* ----------------------------------------------------------- */ +/* device / file handle status */ + +struct tw5864_dev; /* forward delclaration */ + +/* buffer for one video/vbi/ts frame */ +struct tw5864_buf { + struct vb2_v4l2_buffer vb; + struct list_head list; + + unsigned int size; +}; + +struct tw5864_fmt { + char *name; + u32 fourcc; /* v4l2 format id */ + int depth; + int flags; + u32 twformat; +}; + +struct tw5864_dma_buf { + void *addr; + dma_addr_t dma_addr; +}; + +enum tw5864_vid_std { + STD_NTSC = 0, + STD_PAL = 1, + STD_SECAM = 2, + + STD_INVALID = 7, + STD_AUTO = 7, +}; + +v4l2_std_id tw5864_get_v4l2_std(enum tw5864_vid_std std); +enum tw5864_vid_std tw5864_from_v4l2_std(v4l2_std_id v4l2_std); + +struct tw5864_input { + int input_number; + struct tw5864_dev *root; + struct mutex lock; /* used for vidq and vdev */ + spinlock_t slock; /* used for sync between ISR, tasklet & V4L2 API */ + struct video_device vdev; + struct v4l2_ctrl_handler hdl; + const struct tw5864_tvnorm *tvnorm; + void *alloc_ctx; + struct vb2_queue vidq; + struct list_head active; + const struct tw5864_format *fmt; + enum resolution resolution; + unsigned int width, height; + unsigned int frame_seqno; + unsigned int h264_idr_pic_id; + unsigned int h264_frame_seqno_in_gop; + int enabled; + enum tw5864_vid_std std; + v4l2_std_id v4l2_std; + int tail_nb_bits; + u8 tail; + u8 *buf_cur_ptr; + int buf_cur_space_left; + + u32 reg_interlacing; + u32 reg_vlc; + u32 reg_dsp_codec; + u32 reg_dsp; + u32 reg_emu; + u32 reg_dsp_qp; + u32 reg_dsp_ref_mvp_lambda; + u32 reg_dsp_i4x4_weight; + u32 buf_id; + + struct tw5864_buf *vb; + + struct v4l2_ctrl *md_threshold_grid_ctrl; + u16 md_threshold_grid_values[12 * 16]; + int qp; + int gop; + + /* + * In (1/MAX_FPS) units. + * For max FPS (default), set to 1. + * For 1 FPS, set to e.g. 32. + */ + int frame_interval; +}; + +struct tw5864_h264_frame { + struct tw5864_dma_buf vlc; + struct tw5864_dma_buf mv; + + int vlc_len; + u32 checksum; + struct tw5864_input *input; + + u64 timestamp; +}; + +/* global device status */ +struct tw5864_dev { + spinlock_t slock; /* used for sync between ISR, tasklet & V4L2 API */ + struct v4l2_device v4l2_dev; + struct tw5864_input inputs[TW5864_INPUTS]; +#define H264_BUF_CNT 64 + struct tw5864_h264_frame h264_buf[H264_BUF_CNT]; + int h264_buf_r_index; + int h264_buf_w_index; + + struct tasklet_struct tasklet; + + int encoder_busy; + /* Input number to check next (in RR fashion) */ + int next_i; + + /* pci i/o */ + char name[64]; + struct pci_dev *pci; + void __iomem *mmio; + u32 irqmask; + u32 frame_seqno; + + u32 stored_len; + + struct dentry *debugfs_dir; +}; + +#define tw_readl(reg) readl(dev->mmio + reg) +#define tw_mask_readl(reg, mask) \ + (tw_readl(reg) & (mask)) +#define tw_mask_shift_readl(reg, mask, shift) \ + (tw_mask_readl((reg), ((mask) << (shift))) >> (shift)) + +#define tw_writel(reg, value) writel((value), dev->mmio + reg) +#define tw_mask_writel(reg, mask, value) \ + tw_writel(reg, (tw_readl(reg) & ~(mask)) | ((value) & (mask))) +#define tw_mask_shift_writel(reg, mask, shift, value) \ + tw_mask_writel((reg), ((mask) << (shift)), ((value) << (shift))) + +#define tw_setl(reg, bit) tw_writel((reg), tw_readl(reg) | (bit)) +#define tw_clearl(reg, bit) tw_writel((reg), tw_readl(reg) & ~(bit)) + +u8 tw_indir_readb(struct tw5864_dev *dev, u16 addr); +void tw_indir_writeb(struct tw5864_dev *dev, u16 addr, u8 data); + +void tw5864_set_tvnorm_hw(struct tw5864_dev *dev); + +void tw5864_irqmask_apply(struct tw5864_dev *dev); +void tw5864_init_ad(struct tw5864_dev *dev); +int tw5864_video_init(struct tw5864_dev *dev, int *video_nr); +void tw5864_video_fini(struct tw5864_dev *dev); +void tw5864_prepare_frame_headers(struct tw5864_input *input); +void tw5864_h264_put_stream_header(u8 **buf, size_t *space_left, int qp, + int width, int height); +void tw5864_h264_put_slice_header(u8 **buf, size_t *space_left, + unsigned int idr_pic_id, + unsigned int frame_seqno_in_gop, + int *tail_nb_bits, u8 *tail); +void tw5864_request_encoded_frame(struct tw5864_input *input); +void tw5864_push_to_make_it_roll(struct tw5864_input *input); + +static const unsigned int lambda_lookup_table[52] = { + 0x0020, 0x0020, 0x0020, 0x0020, + 0x0020, 0x0020, 0x0020, 0x0020, + 0x0020, 0x0020, 0x0020, 0x0020, + 0x0020, 0x0020, 0x0020, 0x0020, + 0x0040, 0x0040, 0x0040, 0x0040, + 0x0060, 0x0060, 0x0060, 0x0080, + 0x0080, 0x0080, 0x00a0, 0x00c0, + 0x00c0, 0x00e0, 0x0100, 0x0120, + 0x0140, 0x0160, 0x01a0, 0x01c0, + 0x0200, 0x0240, 0x0280, 0x02e0, + 0x0320, 0x03a0, 0x0400, 0x0480, + 0x0500, 0x05a0, 0x0660, 0x0720, + 0x0800, 0x0900, 0x0a20, 0x0b60 +}; + +static const unsigned int intra4x4_lambda3[52] = { + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 3, 3, 3, 4, + 4, 4, 5, 6, 6, 7, 8, 9, + 10, 11, 13, 14, 16, 18, 20, 23, + 25, 29, 32, 36, 40, 45, 51, 57, + 64, 72, 81, 91 +}; diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 37f05cb..231afead 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2333,6 +2333,7 @@ #define PCI_VENDOR_ID_CAVIUM 0x177d #define PCI_VENDOR_ID_TECHWELL 0x1797 +#define PCI_DEVICE_ID_TECHWELL_5864 0x5864 #define PCI_DEVICE_ID_TECHWELL_6800 0x6800 #define PCI_DEVICE_ID_TECHWELL_6801 0x6801 #define PCI_DEVICE_ID_TECHWELL_6804 0x6804 -- 2.7.1.380.g0fea050.dirty