diff options
Diffstat (limited to 'drivers/media/pci/tw5864')
-rw-r--r-- | drivers/media/pci/tw5864/Kconfig | 12 | ||||
-rw-r--r-- | drivers/media/pci/tw5864/Makefile | 3 | ||||
-rw-r--r-- | drivers/media/pci/tw5864/tw5864-core.c | 359 | ||||
-rw-r--r-- | drivers/media/pci/tw5864/tw5864-h264.c | 259 | ||||
-rw-r--r-- | drivers/media/pci/tw5864/tw5864-reg.h | 2133 | ||||
-rw-r--r-- | drivers/media/pci/tw5864/tw5864-util.c | 37 | ||||
-rw-r--r-- | drivers/media/pci/tw5864/tw5864-video.c | 1510 | ||||
-rw-r--r-- | drivers/media/pci/tw5864/tw5864.h | 205 |
8 files changed, 4518 insertions, 0 deletions
diff --git a/drivers/media/pci/tw5864/Kconfig b/drivers/media/pci/tw5864/Kconfig new file mode 100644 index 000000000000..87c8f327e2d4 --- /dev/null +++ b/drivers/media/pci/tw5864/Kconfig @@ -0,0 +1,12 @@ +config VIDEO_TW5864 + tristate "Techwell TW5864 video/audio grabber and encoder" + depends on VIDEO_DEV && PCI && VIDEO_V4L2 + depends on HAS_DMA + select VIDEOBUF2_DMA_CONTIG + ---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/media/pci/tw5864/Makefile b/drivers/media/pci/tw5864/Makefile new file mode 100644 index 000000000000..4fc8b3b1a45a --- /dev/null +++ b/drivers/media/pci/tw5864/Makefile @@ -0,0 +1,3 @@ +tw5864-objs := tw5864-core.o tw5864-video.o tw5864-h264.o tw5864-util.o + +obj-$(CONFIG_VIDEO_TW5864) += tw5864.o diff --git a/drivers/media/pci/tw5864/tw5864-core.c b/drivers/media/pci/tw5864/tw5864-core.c new file mode 100644 index 000000000000..1d43b96958ea --- /dev/null +++ b/drivers/media/pci/tw5864/tw5864-core.c @@ -0,0 +1,359 @@ +/* + * TW5864 driver - core functions + * + * Copyright (C) 2016 Bluecherry, LLC <maintainers@bluecherrydvr.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/init.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/kmod.h> +#include <linux/sound.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/pm.h> +#include <linux/pci_ids.h> +#include <linux/jiffies.h> +#include <asm/dma.h> +#include <media/v4l2-dev.h> + +#include "tw5864.h" +#include "tw5864-reg.h" + +MODULE_DESCRIPTION("V4L2 driver module for tw5864-based multimedia capture & encoding devices"); +MODULE_AUTHOR("Bluecherry Maintainers <maintainers@bluecherrydvr.com>"); +MODULE_AUTHOR("Andrey Utkin <andrey.utkin@corp.bluecherry.net>"); +MODULE_LICENSE("GPL"); + +/* + * BEWARE OF KNOWN ISSUES WITH VIDEO QUALITY + * + * This driver was developed by Bluecherry LLC by deducing behaviour of + * original manufacturer's driver, from both source code and execution traces. + * It is known that there are some artifacts on output video with this driver: + * - on all known hardware samples: random pixels of wrong color (mostly + * white, red or blue) appearing and disappearing on sequences of P-frames; + * - on some hardware samples (known with H.264 core version e006:2800): + * total madness on P-frames: blocks of wrong luminance; blocks of wrong + * colors "creeping" across the picture. + * There is a workaround for both issues: avoid P-frames by setting GOP size + * to 1. To do that, run this command on device files created by this driver: + * + * v4l2-ctl --device /dev/videoX --set-ctrl=video_gop_size=1 + * + * These issues are not decoding errors; all produced H.264 streams are decoded + * properly. Streams without P-frames don't have these artifacts so it's not + * analog-to-digital conversion issues nor internal memory errors; we conclude + * it's internal H.264 encoder issues. + * We cannot even check the original driver's behaviour because it has never + * worked properly at all in our development environment. So these issues may + * be actually related to firmware or hardware. However it may be that there's + * just some more register settings missing in the driver which would please + * the hardware. + * Manufacturer didn't help much on our inquiries, but feel free to disturb + * again the support of Intersil (owner of former Techwell). + */ + +/* 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 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); + + if (status & TW5864_INTR_TIMER) + tw5864_timer_isr(dev); + + 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]; + + 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(); + cur_frame->seqno = input->frame_seqno; + cur_frame->gop_seqno = input->frame_gop_seqno; + + dev->h264_buf_w_index = next_frame_index; + tasklet_schedule(&dev->tasklet); + + cur_frame = next_frame; + + spin_lock(&input->slock); + input->frame_seqno++; + input->frame_gop_seqno++; + if (input->frame_gop_seqno >= input->gop) + input->frame_gop_seqno = 0; + spin_unlock(&input->slock); + } 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); + + tw_writel(TW5864_VLC_STREAM_BASE_ADDR, cur_frame->vlc.dma_addr); + tw_writel(TW5864_MV_STREAM_BASE_ADDR, cur_frame->mv.dma_addr); + + /* Additional ack for this interrupt */ + tw_writel(TW5864_VLC_DSP_INTR, 0x00000001); + tw_writel(TW5864_PCI_INTR_STATUS, TW5864_VLC_DONE_INTR); +} + +static void tw5864_input_deadline_update(struct tw5864_input *input) +{ + input->new_frame_deadline = jiffies + msecs_to_jiffies(1000); +} + +static void tw5864_timer_isr(struct tw5864_dev *dev) +{ + unsigned long flags; + int i; + int encoder_busy; + + /* Additional ack for this interrupt */ + tw_writel(TW5864_PCI_INTR_STATUS, TW5864_TIMER_INTR); + + 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_input) % 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; + + /* Check if new raw frame is available */ + raw_buf_id = tw_mask_shift_readl(TW5864_SENIF_ORG_FRM_PTR1, 0x3, + 2 * input->nr); + + if (input->buf_id != raw_buf_id) { + input->buf_id = raw_buf_id; + tw5864_input_deadline_update(input); + spin_unlock_irqrestore(&input->slock, flags); + + spin_lock_irqsave(&dev->slock, flags); + dev->encoder_busy = 1; + dev->next_input = (next_input + 1) % TW5864_INPUTS; + spin_unlock_irqrestore(&dev->slock, flags); + + tw5864_request_encoded_frame(input); + break; + } + + /* No new raw frame; check if channel is stuck */ + if (time_is_after_jiffies(input->new_frame_deadline)) { + /* If stuck, request new raw frames again */ + tw_mask_shift_writel(TW5864_ENC_BUF_PTR_REC1, 0x3, + 2 * input->nr, input->buf_id + 3); + tw5864_input_deadline_update(input); + } +next: + spin_unlock_irqrestore(&input->slock, flags); + } +} + +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) + return err; + + /* pci init */ + dev->pci = pci_dev; + err = pci_enable_device(pci_dev); + if (err) { + dev_err(&dev->pci->dev, "pci_enable_device() failed\n"); + goto unreg_v4l2; + } + + pci_set_master(pci_dev); + + err = pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32)); + if (err) { + dev_err(&dev->pci->dev, "32 bit PCI DMA is not supported\n"); + goto disable_pci; + } + + /* get mmio */ + err = pci_request_regions(pci_dev, dev->name); + if (err) { + dev_err(&dev->pci->dev, "Cannot request regions for MMIO\n"); + goto disable_pci; + } + dev->mmio = pci_ioremap_bar(pci_dev, 0); + if (!dev->mmio) { + err = -EIO; + dev_err(&dev->pci->dev, "can't ioremap() MMIO memory\n"); + goto release_mmio; + } + + spin_lock_init(&dev->slock); + + dev_info(&pci_dev->dev, "TW5864 hardware version: %04x\n", + tw_readl(TW5864_HW_VERSION)); + dev_info(&pci_dev->dev, "TW5864 H.264 core version: %04x:%04x\n", + tw_readl(TW5864_H264REV), + tw_readl(TW5864_UNDECLARED_H264REV_PART2)); + + err = tw5864_video_init(dev, video_nr); + if (err) + goto unmap_mmio; + + /* 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 fini_video; + } + + dev_info(&pci_dev->dev, "Note: there are known video quality issues. For details\n"); + dev_info(&pci_dev->dev, "see the comment in drivers/media/pci/tw5864/tw5864-core.c.\n"); + + return 0; + +fini_video: + tw5864_video_fini(dev); +unmap_mmio: + iounmap(dev->mmio); +release_mmio: + pci_release_regions(pci_dev); +disable_pci: + pci_disable_device(pci_dev); +unreg_v4l2: + v4l2_device_unregister(&dev->v4l2_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); + + /* 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/media/pci/tw5864/tw5864-h264.c b/drivers/media/pci/tw5864/tw5864-h264.c new file mode 100644 index 000000000000..330d200f52cd --- /dev/null +++ b/drivers/media/pci/tw5864/tw5864-h264.c @@ -0,0 +1,259 @@ +/* + * TW5864 driver - H.264 headers generation functions + * + * Copyright (C) 2016 Bluecherry, LLC <maintainers@bluecherrydvr.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/log2.h> + +#include "tw5864.h" + +static u8 marker[] = { 0x00, 0x00, 0x00, 0x01 }; + +/* + * Exponential-Golomb coding functions + * + * These functions are used for generation of H.264 bitstream headers. + * + * This code is derived from tw5864 reference driver by manufacturers, which + * itself apparently was derived from x264 project. + */ + +/* Bitstream writing context */ +struct bs { + u8 *buf; /* pointer to buffer beginning */ + u8 *buf_end; /* pointer to buffer end */ + u8 *ptr; /* pointer to current byte in buffer */ + unsigned int bits_left; /* number of available bits in current byte */ +}; + +static void bs_init(struct bs *s, void *buf, int size) +{ + s->buf = buf; + s->ptr = buf; + s->buf_end = s->ptr + size; + s->bits_left = 8; +} + +static int bs_len(struct bs *s) +{ + return s->ptr - s->buf; +} + +static void bs_write(struct bs *s, int count, u32 bits) +{ + if (s->ptr >= s->buf_end - 4) + return; + while (count > 0) { + if (count < 32) + bits &= (1 << count) - 1; + if (count < s->bits_left) { + *s->ptr = (*s->ptr << count) | bits; + s->bits_left -= count; + break; + } + *s->ptr = (*s->ptr << s->bits_left) | + (bits >> (count - s->bits_left)); + count -= s->bits_left; + s->ptr++; + s->bits_left = 8; + } +} + +static void bs_write1(struct bs *s, u32 bit) +{ + if (s->ptr < s->buf_end) { + *s->ptr <<= 1; + *s->ptr |= bit; + s->bits_left--; + if (s->bits_left == 0) { + s->ptr++; + s->bits_left = 8; + } + } +} + +static 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 void bs_write_se(struct bs *s, int val) +{ + bs_write_ue(s, val <= 0 ? -val * 2 : val * 2 - 1); +} + +static void bs_rbsp_trailing(struct bs *s) +{ + bs_write1(s, 1); + if (s->bits_left != 8) + bs_write(s, s->bits_left, 0x00); +} + +/* H.264 headers generation functions */ + +static int tw5864_h264_gen_sps_rbsp(u8 *buf, size_t size, int width, int height) +{ + struct bs bs, *s; + + s = &bs; + bs_init(s, buf, size); + bs_write(s, 8, 0x42); /* profile_idc, baseline */ + bs_write(s, 1, 1); /* constraint_set0_flag */ + bs_write(s, 1, 1); /* constraint_set1_flag */ + bs_write(s, 1, 0); /* constraint_set2_flag */ + bs_write(s, 5, 0); /* reserved_zero_5bits */ + bs_write(s, 8, 0x1e); /* level_idc */ + bs_write_ue(s, 0); /* seq_parameter_set_id */ + bs_write_ue(s, ilog2(MAX_GOP_SIZE) - 4); /* log2_max_frame_num_minus4 */ + bs_write_ue(s, 0); /* pic_order_cnt_type */ + /* log2_max_pic_order_cnt_lsb_minus4 */ + bs_write_ue(s, ilog2(MAX_GOP_SIZE) - 4); + bs_write_ue(s, 1); /* num_ref_frames */ + bs_write(s, 1, 0); /* gaps_in_frame_num_value_allowed_flag */ + bs_write_ue(s, width / 16 - 1); /* pic_width_in_mbs_minus1 */ + bs_write_ue(s, height / 16 - 1); /* pic_height_in_map_units_minus1 */ + bs_write(s, 1, 1); /* frame_mbs_only_flag */ + bs_write(s, 1, 0); /* direct_8x8_inference_flag */ + bs_write(s, 1, 0); /* frame_cropping_flag */ + bs_write(s, 1, 0); /* vui_parameters_present_flag */ + 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); /* pic_parameter_set_id */ + bs_write_ue(s, 0); /* seq_parameter_set_id */ + bs_write(s, 1, 0); /* entropy_coding_mode_flag */ + bs_write(s, 1, 0); /* pic_order_present_flag */ + bs_write_ue(s, 0); /* num_slice_groups_minus1 */ + bs_write_ue(s, 0); /* i_num_ref_idx_l0_active_minus1 */ + bs_write_ue(s, 0); /* i_num_ref_idx_l1_active_minus1 */ + bs_write(s, 1, 0); /* weighted_pred_flag */ + bs_write(s, 2, 0); /* weighted_bipred_idc */ + bs_write_se(s, qp - 26); /* pic_init_qp_minus26 */ + bs_write_se(s, qp - 26); /* pic_init_qs_minus26 */ + bs_write_se(s, 0); /* chroma_qp_index_offset */ + bs_write(s, 1, 0); /* deblocking_filter_control_present_flag */ + bs_write(s, 1, 0); /* constrained_intra_pred_flag */ + bs_write(s, 1, 0); /* redundant_pic_cnt_present_flag */ + 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_gop_seqno, + int *tail_nb_bits, u8 *tail) +{ + struct bs bs, *s; + int is_i_frame = frame_gop_seqno == 0; + + s = &bs; + bs_init(s, buf, size); + bs_write_ue(s, 0); /* first_mb_in_slice */ + bs_write_ue(s, is_i_frame ? 2 : 5); /* slice_type - I or P */ + bs_write_ue(s, 0); /* pic_parameter_set_id */ + bs_write(s, ilog2(MAX_GOP_SIZE), frame_gop_seqno); /* frame_num */ + if (is_i_frame) + bs_write_ue(s, idr_pic_id); + + /* pic_order_cnt_lsb */ + bs_write(s, ilog2(MAX_GOP_SIZE), frame_gop_seqno); + + 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); /* num_ref_idx_active_override_flag */ + bs_write1(s, 0); /* ref_pic_list_reordering_flag_l0 */ + bs_write1(s, 0); /* adaptive_ref_pic_marking_mode_flag */ + } + + bs_write_se(s, 0); /* slice_qp_delta */ + + if (s->bits_left != 8) { + *tail = ((s->ptr[0]) << s->bits_left); + *tail_nb_bits = 8 - s->bits_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 */ + 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 */ + 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_gop_seqno, + int *tail_nb_bits, u8 *tail) +{ + int nal_len; + + memcpy(*buf, marker, sizeof(marker)); + *buf += 4; + *space_left -= 4; + + /* Frame NAL header */ + **buf = (frame_gop_seqno == 0) ? 0x25 : 0x21; + *buf += 1; + *space_left -= 1; + + nal_len = tw5864_h264_gen_slice_head(*buf, *space_left, idr_pic_id, + frame_gop_seqno, tail_nb_bits, + tail); + *buf += nal_len; + *space_left -= nal_len; +} diff --git a/drivers/media/pci/tw5864/tw5864-reg.h b/drivers/media/pci/tw5864/tw5864-reg.h new file mode 100644 index 000000000000..92a1b077ef8a --- /dev/null +++ b/drivers/media/pci/tw5864/tw5864-reg.h @@ -0,0 +1,2133 @@ +/* + * TW5864 driver - registers description + * + * Copyright (C) 2016 Bluecherry, LLC <maintainers@bluecherrydvr.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* 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_UNDECLARED_H264REV_PART2 0x0008 + +#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 << 4) +#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 |