// SPDX-License-Identifier: GPL-2.0
/*
*/
#include "cx88.h"
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
static unsigned int vbi_debug;
module_param(vbi_debug, int , 0644 );
MODULE_PARM_DESC(vbi_debug, "enable debug messages [vbi]" );
#define dprintk(level, fmt, arg...) do { \
if (vbi_debug >= level) \
printk(KERN_DEBUG pr_fmt("%s: vbi:" fmt), \
__func__, ## arg); \
} while (0 )
/* ------------------------------------------------------------------ */
int cx8800_vbi_fmt(struct file *file, void *priv,
struct v4l2_format *f)
{
struct cx8800_dev *dev = video_drvdata(file);
f->fmt.vbi.samples_per_line = VBI_LINE_LENGTH;
f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
f->fmt.vbi.offset = 244 ;
if (dev->core->tvnorm & V4L2_STD_525_60) {
/* ntsc */
f->fmt.vbi.sampling_rate = 28636363 ;
f->fmt.vbi.start[0 ] = 10 ;
f->fmt.vbi.start[1 ] = 273 ;
f->fmt.vbi.count[0 ] = VBI_LINE_NTSC_COUNT;
f->fmt.vbi.count[1 ] = VBI_LINE_NTSC_COUNT;
} else if (dev->core->tvnorm & V4L2_STD_625_50) {
/* pal */
f->fmt.vbi.sampling_rate = 35468950 ;
f->fmt.vbi.start[0 ] = V4L2_VBI_ITU_625_F1_START + 5 ;
f->fmt.vbi.start[1 ] = V4L2_VBI_ITU_625_F2_START + 5 ;
f->fmt.vbi.count[0 ] = VBI_LINE_PAL_COUNT;
f->fmt.vbi.count[1 ] = VBI_LINE_PAL_COUNT;
}
return 0 ;
}
static int cx8800_start_vbi_dma(struct cx8800_dev *dev,
struct cx88_dmaqueue *q,
struct cx88_buffer *buf)
{
struct cx88_core *core = dev->core;
/* setup fifo + format */
cx88_sram_channel_setup(dev->core, &cx88_sram_channels[SRAM_CH24],
VBI_LINE_LENGTH, buf->risc.dma);
cx_write(MO_VBOS_CONTROL, (1 << 18 ) | /* comb filter delay fixup */
(1 << 15 ) | /* enable vbi capture */
(1 << 11 ));
/* reset counter */
cx_write(MO_VBI_GPCNTRL, GP_COUNT_CONTROL_RESET);
q->count = 0 ;
/* enable irqs */
cx_set(MO_PCI_INTMSK, core->pci_irqmask | PCI_INT_VIDINT);
cx_set(MO_VID_INTMSK, 0 x0f0088);
/* enable capture */
cx_set(VID_CAPTURE_CONTROL, 0 x18);
/* start dma */
cx_set(MO_DEV_CNTRL2, (1 << 5 ));
cx_set(MO_VID_DMACNTRL, 0 x88);
return 0 ;
}
void cx8800_stop_vbi_dma(struct cx8800_dev *dev)
{
struct cx88_core *core = dev->core;
/* stop dma */
cx_clear(MO_VID_DMACNTRL, 0 x88);
/* disable capture */
cx_clear(VID_CAPTURE_CONTROL, 0 x18);
/* disable irqs */
cx_clear(MO_PCI_INTMSK, PCI_INT_VIDINT);
cx_clear(MO_VID_INTMSK, 0 x0f0088);
}
int cx8800_restart_vbi_queue(struct cx8800_dev *dev,
struct cx88_dmaqueue *q)
{
struct cx88_buffer *buf;
if (list_empty(&q->active))
return 0 ;
buf = list_entry(q->active.next, struct cx88_buffer, list);
dprintk(2 , "restart_queue [%p/%d]: restart dma\n" ,
buf, buf->vb.vb2_buf.index);
cx8800_start_vbi_dma(dev, q, buf);
return 0 ;
}
/* ------------------------------------------------------------------ */
static int queue_setup(struct vb2_queue *q,
unsigned int *num_buffers, unsigned int *num_planes,
unsigned int sizes[], struct device *alloc_devs[])
{
struct cx8800_dev *dev = q->drv_priv;
*num_planes = 1 ;
if (dev->core->tvnorm & V4L2_STD_525_60)
sizes[0 ] = VBI_LINE_NTSC_COUNT * VBI_LINE_LENGTH * 2 ;
else
sizes[0 ] = VBI_LINE_PAL_COUNT * VBI_LINE_LENGTH * 2 ;
return 0 ;
}
static int buffer_prepare(struct vb2_buffer *vb)
{
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
struct cx8800_dev *dev = vb->vb2_queue->drv_priv;
struct cx88_buffer *buf = container_of(vbuf, struct cx88_buffer, vb);
struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0 );
unsigned int lines;
unsigned int size;
if (dev->core->tvnorm & V4L2_STD_525_60)
lines = VBI_LINE_NTSC_COUNT;
else
lines = VBI_LINE_PAL_COUNT;
size = lines * VBI_LINE_LENGTH * 2 ;
if (vb2_plane_size(vb, 0 ) < size)
return -EINVAL;
vb2_set_plane_payload(vb, 0 , size);
return cx88_risc_buffer(dev->pci, &buf->risc, sgt->sgl,
0 , VBI_LINE_LENGTH * lines,
VBI_LINE_LENGTH, 0 ,
lines);
}
static void buffer_finish(struct vb2_buffer *vb)
{
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
struct cx8800_dev *dev = vb->vb2_queue->drv_priv;
struct cx88_buffer *buf = container_of(vbuf, struct cx88_buffer, vb);
struct cx88_riscmem *risc = &buf->risc;
if (risc->cpu)
dma_free_coherent(&dev->pci->dev, risc->size, risc->cpu,
risc->dma);
memset(risc, 0 , sizeof (*risc));
}
static void buffer_queue(struct vb2_buffer *vb)
{
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
struct cx8800_dev *dev = vb->vb2_queue->drv_priv;
struct cx88_buffer *buf = container_of(vbuf, struct cx88_buffer, vb);
struct cx88_buffer *prev;
struct cx88_dmaqueue *q = &dev->vbiq;
/* add jump to start */
buf->risc.cpu[1 ] = cpu_to_le32(buf->risc.dma + 8 );
buf->risc.jmp[0 ] = cpu_to_le32(RISC_JUMP | RISC_CNT_INC);
buf->risc.jmp[1 ] = cpu_to_le32(buf->risc.dma + 8 );
if (list_empty(&q->active)) {
list_add_tail(&buf->list, &q->active);
dprintk(2 , "[%p/%d] vbi_queue - first active\n" ,
buf, buf->vb.vb2_buf.index);
} else {
buf->risc.cpu[0 ] |= cpu_to_le32(RISC_IRQ1);
prev = list_entry(q->active.prev, struct cx88_buffer, list);
list_add_tail(&buf->list, &q->active);
prev->risc.jmp[1 ] = cpu_to_le32(buf->risc.dma);
dprintk(2 , "[%p/%d] buffer_queue - append to active\n" ,
buf, buf->vb.vb2_buf.index);
}
}
static int start_streaming(struct vb2_queue *q, unsigned int count)
{
struct cx8800_dev *dev = q->drv_priv;
struct cx88_dmaqueue *dmaq = &dev->vbiq;
struct cx88_buffer *buf = list_entry(dmaq->active.next,
struct cx88_buffer, list);
cx8800_start_vbi_dma(dev, dmaq, buf);
return 0 ;
}
static void stop_streaming(struct vb2_queue *q)
{
struct cx8800_dev *dev = q->drv_priv;
struct cx88_core *core = dev->core;
struct cx88_dmaqueue *dmaq = &dev->vbiq;
unsigned long flags;
cx_clear(MO_VID_DMACNTRL, 0 x11);
cx_clear(VID_CAPTURE_CONTROL, 0 x06);
cx8800_stop_vbi_dma(dev);
spin_lock_irqsave(&dev->slock, flags);
while (!list_empty(&dmaq->active)) {
struct cx88_buffer *buf = list_entry(dmaq->active.next,
struct cx88_buffer, list);
list_del(&buf->list);
vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
}
spin_unlock_irqrestore(&dev->slock, flags);
}
const struct vb2_ops cx8800_vbi_qops = {
.queue_setup = queue_setup,
.buf_prepare = buffer_prepare,
.buf_finish = buffer_finish,
.buf_queue = buffer_queue,
.start_streaming = start_streaming,
.stop_streaming = stop_streaming,
};
Messung V0.5 in Prozent C=96 H=94 G=94
¤ Dauer der Verarbeitung: 0.8 Sekunden
(vorverarbeitet am 2026-06-07)
¤
*© Formatika GbR, Deutschland