162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci */ 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci#include "cx88.h" 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/kernel.h> 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci#include <linux/init.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_cistatic unsigned int vbi_debug; 1262306a36Sopenharmony_cimodule_param(vbi_debug, int, 0644); 1362306a36Sopenharmony_ciMODULE_PARM_DESC(vbi_debug, "enable debug messages [vbi]"); 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#define dprintk(level, fmt, arg...) do { \ 1662306a36Sopenharmony_ci if (vbi_debug >= level) \ 1762306a36Sopenharmony_ci printk(KERN_DEBUG pr_fmt("%s: vbi:" fmt), \ 1862306a36Sopenharmony_ci __func__, ##arg); \ 1962306a36Sopenharmony_ci} while (0) 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci/* ------------------------------------------------------------------ */ 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ciint cx8800_vbi_fmt(struct file *file, void *priv, 2462306a36Sopenharmony_ci struct v4l2_format *f) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci struct cx8800_dev *dev = video_drvdata(file); 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci f->fmt.vbi.samples_per_line = VBI_LINE_LENGTH; 2962306a36Sopenharmony_ci f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; 3062306a36Sopenharmony_ci f->fmt.vbi.offset = 244; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci if (dev->core->tvnorm & V4L2_STD_525_60) { 3362306a36Sopenharmony_ci /* ntsc */ 3462306a36Sopenharmony_ci f->fmt.vbi.sampling_rate = 28636363; 3562306a36Sopenharmony_ci f->fmt.vbi.start[0] = 10; 3662306a36Sopenharmony_ci f->fmt.vbi.start[1] = 273; 3762306a36Sopenharmony_ci f->fmt.vbi.count[0] = VBI_LINE_NTSC_COUNT; 3862306a36Sopenharmony_ci f->fmt.vbi.count[1] = VBI_LINE_NTSC_COUNT; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci } else if (dev->core->tvnorm & V4L2_STD_625_50) { 4162306a36Sopenharmony_ci /* pal */ 4262306a36Sopenharmony_ci f->fmt.vbi.sampling_rate = 35468950; 4362306a36Sopenharmony_ci f->fmt.vbi.start[0] = V4L2_VBI_ITU_625_F1_START + 5; 4462306a36Sopenharmony_ci f->fmt.vbi.start[1] = V4L2_VBI_ITU_625_F2_START + 5; 4562306a36Sopenharmony_ci f->fmt.vbi.count[0] = VBI_LINE_PAL_COUNT; 4662306a36Sopenharmony_ci f->fmt.vbi.count[1] = VBI_LINE_PAL_COUNT; 4762306a36Sopenharmony_ci } 4862306a36Sopenharmony_ci return 0; 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic int cx8800_start_vbi_dma(struct cx8800_dev *dev, 5262306a36Sopenharmony_ci struct cx88_dmaqueue *q, 5362306a36Sopenharmony_ci struct cx88_buffer *buf) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci struct cx88_core *core = dev->core; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci /* setup fifo + format */ 5862306a36Sopenharmony_ci cx88_sram_channel_setup(dev->core, &cx88_sram_channels[SRAM_CH24], 5962306a36Sopenharmony_ci VBI_LINE_LENGTH, buf->risc.dma); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci cx_write(MO_VBOS_CONTROL, (1 << 18) | /* comb filter delay fixup */ 6262306a36Sopenharmony_ci (1 << 15) | /* enable vbi capture */ 6362306a36Sopenharmony_ci (1 << 11)); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci /* reset counter */ 6662306a36Sopenharmony_ci cx_write(MO_VBI_GPCNTRL, GP_COUNT_CONTROL_RESET); 6762306a36Sopenharmony_ci q->count = 0; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci /* enable irqs */ 7062306a36Sopenharmony_ci cx_set(MO_PCI_INTMSK, core->pci_irqmask | PCI_INT_VIDINT); 7162306a36Sopenharmony_ci cx_set(MO_VID_INTMSK, 0x0f0088); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci /* enable capture */ 7462306a36Sopenharmony_ci cx_set(VID_CAPTURE_CONTROL, 0x18); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci /* start dma */ 7762306a36Sopenharmony_ci cx_set(MO_DEV_CNTRL2, (1 << 5)); 7862306a36Sopenharmony_ci cx_set(MO_VID_DMACNTRL, 0x88); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci return 0; 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_civoid cx8800_stop_vbi_dma(struct cx8800_dev *dev) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci struct cx88_core *core = dev->core; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci /* stop dma */ 8862306a36Sopenharmony_ci cx_clear(MO_VID_DMACNTRL, 0x88); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci /* disable capture */ 9162306a36Sopenharmony_ci cx_clear(VID_CAPTURE_CONTROL, 0x18); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci /* disable irqs */ 9462306a36Sopenharmony_ci cx_clear(MO_PCI_INTMSK, PCI_INT_VIDINT); 9562306a36Sopenharmony_ci cx_clear(MO_VID_INTMSK, 0x0f0088); 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ciint cx8800_restart_vbi_queue(struct cx8800_dev *dev, 9962306a36Sopenharmony_ci struct cx88_dmaqueue *q) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci struct cx88_buffer *buf; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci if (list_empty(&q->active)) 10462306a36Sopenharmony_ci return 0; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci buf = list_entry(q->active.next, struct cx88_buffer, list); 10762306a36Sopenharmony_ci dprintk(2, "restart_queue [%p/%d]: restart dma\n", 10862306a36Sopenharmony_ci buf, buf->vb.vb2_buf.index); 10962306a36Sopenharmony_ci cx8800_start_vbi_dma(dev, q, buf); 11062306a36Sopenharmony_ci return 0; 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci/* ------------------------------------------------------------------ */ 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic int queue_setup(struct vb2_queue *q, 11662306a36Sopenharmony_ci unsigned int *num_buffers, unsigned int *num_planes, 11762306a36Sopenharmony_ci unsigned int sizes[], struct device *alloc_devs[]) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci struct cx8800_dev *dev = q->drv_priv; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci *num_planes = 1; 12262306a36Sopenharmony_ci if (dev->core->tvnorm & V4L2_STD_525_60) 12362306a36Sopenharmony_ci sizes[0] = VBI_LINE_NTSC_COUNT * VBI_LINE_LENGTH * 2; 12462306a36Sopenharmony_ci else 12562306a36Sopenharmony_ci sizes[0] = VBI_LINE_PAL_COUNT * VBI_LINE_LENGTH * 2; 12662306a36Sopenharmony_ci return 0; 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic int buffer_prepare(struct vb2_buffer *vb) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 13262306a36Sopenharmony_ci struct cx8800_dev *dev = vb->vb2_queue->drv_priv; 13362306a36Sopenharmony_ci struct cx88_buffer *buf = container_of(vbuf, struct cx88_buffer, vb); 13462306a36Sopenharmony_ci struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0); 13562306a36Sopenharmony_ci unsigned int lines; 13662306a36Sopenharmony_ci unsigned int size; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci if (dev->core->tvnorm & V4L2_STD_525_60) 13962306a36Sopenharmony_ci lines = VBI_LINE_NTSC_COUNT; 14062306a36Sopenharmony_ci else 14162306a36Sopenharmony_ci lines = VBI_LINE_PAL_COUNT; 14262306a36Sopenharmony_ci size = lines * VBI_LINE_LENGTH * 2; 14362306a36Sopenharmony_ci if (vb2_plane_size(vb, 0) < size) 14462306a36Sopenharmony_ci return -EINVAL; 14562306a36Sopenharmony_ci vb2_set_plane_payload(vb, 0, size); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci return cx88_risc_buffer(dev->pci, &buf->risc, sgt->sgl, 14862306a36Sopenharmony_ci 0, VBI_LINE_LENGTH * lines, 14962306a36Sopenharmony_ci VBI_LINE_LENGTH, 0, 15062306a36Sopenharmony_ci lines); 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic void buffer_finish(struct vb2_buffer *vb) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 15662306a36Sopenharmony_ci struct cx8800_dev *dev = vb->vb2_queue->drv_priv; 15762306a36Sopenharmony_ci struct cx88_buffer *buf = container_of(vbuf, struct cx88_buffer, vb); 15862306a36Sopenharmony_ci struct cx88_riscmem *risc = &buf->risc; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci if (risc->cpu) 16162306a36Sopenharmony_ci dma_free_coherent(&dev->pci->dev, risc->size, risc->cpu, 16262306a36Sopenharmony_ci risc->dma); 16362306a36Sopenharmony_ci memset(risc, 0, sizeof(*risc)); 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic void buffer_queue(struct vb2_buffer *vb) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 16962306a36Sopenharmony_ci struct cx8800_dev *dev = vb->vb2_queue->drv_priv; 17062306a36Sopenharmony_ci struct cx88_buffer *buf = container_of(vbuf, struct cx88_buffer, vb); 17162306a36Sopenharmony_ci struct cx88_buffer *prev; 17262306a36Sopenharmony_ci struct cx88_dmaqueue *q = &dev->vbiq; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci /* add jump to start */ 17562306a36Sopenharmony_ci buf->risc.cpu[1] = cpu_to_le32(buf->risc.dma + 8); 17662306a36Sopenharmony_ci buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_CNT_INC); 17762306a36Sopenharmony_ci buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma + 8); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci if (list_empty(&q->active)) { 18062306a36Sopenharmony_ci list_add_tail(&buf->list, &q->active); 18162306a36Sopenharmony_ci dprintk(2, "[%p/%d] vbi_queue - first active\n", 18262306a36Sopenharmony_ci buf, buf->vb.vb2_buf.index); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci } else { 18562306a36Sopenharmony_ci buf->risc.cpu[0] |= cpu_to_le32(RISC_IRQ1); 18662306a36Sopenharmony_ci prev = list_entry(q->active.prev, struct cx88_buffer, list); 18762306a36Sopenharmony_ci list_add_tail(&buf->list, &q->active); 18862306a36Sopenharmony_ci prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); 18962306a36Sopenharmony_ci dprintk(2, "[%p/%d] buffer_queue - append to active\n", 19062306a36Sopenharmony_ci buf, buf->vb.vb2_buf.index); 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistatic int start_streaming(struct vb2_queue *q, unsigned int count) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci struct cx8800_dev *dev = q->drv_priv; 19762306a36Sopenharmony_ci struct cx88_dmaqueue *dmaq = &dev->vbiq; 19862306a36Sopenharmony_ci struct cx88_buffer *buf = list_entry(dmaq->active.next, 19962306a36Sopenharmony_ci struct cx88_buffer, list); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci cx8800_start_vbi_dma(dev, dmaq, buf); 20262306a36Sopenharmony_ci return 0; 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistatic void stop_streaming(struct vb2_queue *q) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci struct cx8800_dev *dev = q->drv_priv; 20862306a36Sopenharmony_ci struct cx88_core *core = dev->core; 20962306a36Sopenharmony_ci struct cx88_dmaqueue *dmaq = &dev->vbiq; 21062306a36Sopenharmony_ci unsigned long flags; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci cx_clear(MO_VID_DMACNTRL, 0x11); 21362306a36Sopenharmony_ci cx_clear(VID_CAPTURE_CONTROL, 0x06); 21462306a36Sopenharmony_ci cx8800_stop_vbi_dma(dev); 21562306a36Sopenharmony_ci spin_lock_irqsave(&dev->slock, flags); 21662306a36Sopenharmony_ci while (!list_empty(&dmaq->active)) { 21762306a36Sopenharmony_ci struct cx88_buffer *buf = list_entry(dmaq->active.next, 21862306a36Sopenharmony_ci struct cx88_buffer, list); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci list_del(&buf->list); 22162306a36Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci spin_unlock_irqrestore(&dev->slock, flags); 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ciconst struct vb2_ops cx8800_vbi_qops = { 22762306a36Sopenharmony_ci .queue_setup = queue_setup, 22862306a36Sopenharmony_ci .buf_prepare = buffer_prepare, 22962306a36Sopenharmony_ci .buf_finish = buffer_finish, 23062306a36Sopenharmony_ci .buf_queue = buffer_queue, 23162306a36Sopenharmony_ci .wait_prepare = vb2_ops_wait_prepare, 23262306a36Sopenharmony_ci .wait_finish = vb2_ops_wait_finish, 23362306a36Sopenharmony_ci .start_streaming = start_streaming, 23462306a36Sopenharmony_ci .stop_streaming = stop_streaming, 23562306a36Sopenharmony_ci}; 236