From 1b0e8e46297a214336d85c8e278a8a004f97889e Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Sat, 31 Jul 2010 16:01:00 -0300 Subject: [PATCH] [media] saa7164: allow DMA engine buffers to vary in size between analog and digital Signed-off-by: Steven Toth Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/saa7164/saa7164-buffer.c | 9 +- drivers/media/video/saa7164/saa7164-core.c | 85 ++++-- drivers/media/video/saa7164/saa7164-encoder.c | 245 ++++++++++-------- drivers/media/video/saa7164/saa7164.h | 3 + 4 files changed, 220 insertions(+), 122 deletions(-) diff --git a/drivers/media/video/saa7164/saa7164-buffer.c b/drivers/media/video/saa7164/saa7164-buffer.c index 5f45ea7f3d7c..187e3f60fa5a 100644 --- a/drivers/media/video/saa7164/saa7164-buffer.c +++ b/drivers/media/video/saa7164/saa7164-buffer.c @@ -134,18 +134,19 @@ struct saa7164_buffer *saa7164_buffer_alloc(struct saa7164_port *port, buf->crc = crc32(0, buf->cpu, buf->actual_size); memset_io(buf->pt_cpu, 0xff, buf->pt_size); - dprintk(DBGLVL_BUF, "%s() allocated buffer @ 0x%p\n", - __func__, buf); + dprintk(DBGLVL_BUF, "%s() allocated buffer @ 0x%p (%d pageptrs)\n", + __func__, buf, params->numpagetables); dprintk(DBGLVL_BUF, " pci_cpu @ 0x%p dma @ 0x%08lx len = 0x%x\n", buf->cpu, (long)buf->dma, buf->pci_size); dprintk(DBGLVL_BUF, " pt_cpu @ 0x%p pt_dma @ 0x%08lx len = 0x%x\n", buf->pt_cpu, (long)buf->pt_dma, buf->pt_size); /* Format the Page Table Entries to point into the data buffer */ -// for (i = 0 ; i < SAA7164_PT_ENTRIES; i++) { - for (i = 0 ; i < 10; i++) { + for (i = 0 ; i < params->numpagetables; i++) { *(buf->pt_cpu + i) = buf->dma + (i * 0x1000); /* TODO */ + dprintk(DBGLVL_BUF, " pt[%02d] = 0x%p -> 0x%llx\n", + i, buf->pt_cpu, (u64)*(buf->pt_cpu)); } diff --git a/drivers/media/video/saa7164/saa7164-core.c b/drivers/media/video/saa7164/saa7164-core.c index b54b032d56b6..8879517d974a 100644 --- a/drivers/media/video/saa7164/saa7164-core.c +++ b/drivers/media/video/saa7164/saa7164-core.c @@ -65,6 +65,14 @@ unsigned int print_histogram = 64; module_param(print_histogram, int, 0644); MODULE_PARM_DESC(print_histogram, "print histogram values once"); +unsigned int crc_checking = 1; +module_param(crc_checking, int, 0644); +MODULE_PARM_DESC(crc_checking, "enable crc sanity checking on buffers"); + +unsigned int guard_checking = 1; +module_param(guard_checking, int, 0644); +MODULE_PARM_DESC(guard_checking, "enable dma sanity checking for buffer overruns"); + static unsigned int saa7164_devcount; static DEFINE_MUTEX(devlist); @@ -101,17 +109,24 @@ static void saa7164_pack_verifier(struct saa7164_buffer *buf) for (i = 0; i < buf->actual_size; i += 2048) { - if ( (*(p + i + 0) != 0x00) || (*(p + i + 1) != 0x00) || (*(p + i + 2) != 0x01) || (*(p + i + 3) != 0xBA) ) + if ( (*(p + i + 0) != 0x00) || (*(p + i + 1) != 0x00) || + (*(p + i + 2) != 0x01) || (*(p + i + 3) != 0xBA) ) { printk(KERN_ERR "No pack at 0x%x\n", i); +// saa7164_dumphex16FF(buf->port->dev, (p + i), 32); + } } } +#define FIXED_VIDEO_PID 0xf1 +#define FIXED_AUDIO_PID 0xf2 + static void saa7164_ts_verifier(struct saa7164_buffer *buf) { struct saa7164_port *port = buf->port; u32 i; - u8 tmp, cc, a; - u8 *bufcpu = (u8 *)buf->cpu; + u8 cc, a; + u16 pid; + u8 __iomem *bufcpu = (u8 *)buf->cpu; port->sync_errors = 0; port->v_cc_errors = 0; @@ -121,23 +136,25 @@ static void saa7164_ts_verifier(struct saa7164_buffer *buf) if (*(bufcpu + i) != 0x47) port->sync_errors++; - /* Query pid lower 8 bits */ - tmp = *(bufcpu + i + 2); + /* TODO: Query pid lower 8 bits, ignoring upper bits intensionally */ + pid = ((*(bufcpu + i + 1) & 0x1f) << 8) | *(bufcpu + i + 2); cc = *(bufcpu + i + 3) & 0x0f; - if (tmp == 0xf1) { + if (pid == FIXED_VIDEO_PID) { a = ((port->last_v_cc + 1) & 0x0f); if (a != cc) { - printk(KERN_ERR "video cc last = %x current = %x i = %d\n", port->last_v_cc, cc, i); + printk(KERN_ERR "video cc last = %x current = %x i = %d\n", + port->last_v_cc, cc, i); port->v_cc_errors++; } port->last_v_cc = cc; } else - if (tmp == 0xf2) { + if (pid == FIXED_AUDIO_PID) { a = ((port->last_a_cc + 1) & 0x0f); if (a != cc) { - printk(KERN_ERR "audio cc last = %x current = %x i = %d\n", port->last_a_cc, cc, i); + printk(KERN_ERR "audio cc last = %x current = %x i = %d\n", + port->last_a_cc, cc, i); port->a_cc_errors++; } @@ -246,6 +263,7 @@ static void saa7164_work_enchandler_helper(struct saa7164_port *port, int bufnr) struct saa7164_user_buffer *ubuf = 0; struct list_head *c, *n; int i = 0; + u8 __iomem *p; mutex_lock(&port->dmaqueue_lock); list_for_each_safe(c, n, &port->dmaqueue.list) { @@ -260,12 +278,33 @@ static void saa7164_work_enchandler_helper(struct saa7164_port *port, int bufnr) if (buf->idx == bufnr) { /* Found the buffer, deal with it */ - dprintk(DBGLVL_IRQ, "%s() rp: %d\n", __func__, bufnr); + dprintk(DBGLVL_IRQ, "%s() bufnr: %d\n", __func__, bufnr); + + if (crc_checking) { + /* Throw a new checksum on the dma buffer */ + buf->crc = crc32(0, buf->cpu, buf->actual_size); + } + + if (guard_checking) { + p = (u8 *)buf->cpu; + if ( (*(p + buf->actual_size + 0) != 0xff) || + (*(p + buf->actual_size + 1) != 0xff) || + (*(p + buf->actual_size + 2) != 0xff) || + (*(p + buf->actual_size + 3) != 0xff) || + (*(p + buf->actual_size + 0x10) != 0xff) || + (*(p + buf->actual_size + 0x11) != 0xff) || + (*(p + buf->actual_size + 0x12) != 0xff) || + (*(p + buf->actual_size + 0x13) != 0xff) ) { + printk(KERN_ERR "%s() buf %p guard buffer breach\n", + __func__, buf); +// saa7164_dumphex16FF(dev, (p + buf->actual_size) - 32 , 64); + } + } /* Validate the incoming buffer content */ if (port->encoder_params.stream_type == V4L2_MPEG_STREAM_TYPE_MPEG2_TS) saa7164_ts_verifier(buf); - if (port->encoder_params.stream_type == V4L2_MPEG_STREAM_TYPE_MPEG2_PS) + else if (port->encoder_params.stream_type == V4L2_MPEG_STREAM_TYPE_MPEG2_PS) saa7164_pack_verifier(buf); /* find a free user buffer and clone to it */ @@ -280,8 +319,10 @@ static void saa7164_work_enchandler_helper(struct saa7164_port *port, int bufnr) memcpy_fromio(ubuf->data, buf->cpu, ubuf->actual_size); - /* Throw a new checksum on the read buffer */ - ubuf->crc = crc32(0, ubuf->data, ubuf->actual_size); + if (crc_checking) { + /* Throw a new checksum on the read buffer */ + ubuf->crc = crc32(0, ubuf->data, ubuf->actual_size); + } /* Requeue the buffer on the free list */ ubuf->pos = 0; @@ -304,6 +345,10 @@ static void saa7164_work_enchandler_helper(struct saa7164_port *port, int bufnr) * in time. */ saa7164_buffer_zero_offsets(port, bufnr); memset_io(buf->cpu, 0xff, buf->pci_size); + if (crc_checking) { + /* Throw yet aanother new checksum on the dma buffer */ + buf->crc = crc32(0, buf->cpu, buf->actual_size); + } break; } @@ -352,17 +397,22 @@ static void saa7164_work_enchandler(struct work_struct *w) /* Most current complete buffer */ if (wp == 0) - mcb = 7; + mcb = (port->hwcfg.buffercount - 1); else mcb = wp - 1; while (1) { - rp = (port->last_svc_rp + 1) % 8; + if (port->done_first_interrupt == 0) { + port->done_first_interrupt++; + rp = mcb; + } else + rp = (port->last_svc_rp + 1) % 8; - if ((rp < 0) || (rp > 7)) { + if ((rp < 0) || (rp > (port->hwcfg.buffercount - 1))) { printk(KERN_ERR "%s() illegal rp count %d\n", __func__, rp); break; } + saa7164_work_enchandler_helper(port, rp); port->last_svc_rp = rp; cnt++; @@ -371,6 +421,7 @@ static void saa7164_work_enchandler(struct work_struct *w) break; } + /* TODO: Convert this into a /proc/saa7164 style readable file */ if (print_histogram == port->nr) { saa7164_histogram_print(port, &port->irq_interval); saa7164_histogram_print(port, &port->svc_interval); @@ -438,7 +489,7 @@ static irqreturn_t saa7164_irq_ts(struct saa7164_port *port) /* Find the previous buffer to the current write point */ if (wp == 0) - rp = 7; + rp = (port->hwcfg.buffercount - 1); else rp = wp - 1; diff --git a/drivers/media/video/saa7164/saa7164-encoder.c b/drivers/media/video/saa7164/saa7164-encoder.c index 08b62e41c466..0859448bae05 100644 --- a/drivers/media/video/saa7164/saa7164-encoder.c +++ b/drivers/media/video/saa7164/saa7164-encoder.c @@ -76,15 +76,131 @@ static void saa7164_encoder_configure(struct saa7164_port *port) saa7164_api_set_audio_std(port); } -/* One time configuration at registration time */ -static int saa7164_encoder_initialize(struct saa7164_port *port) +static int saa7164_encoder_buffers_dealloc(struct saa7164_port *port) +{ + struct list_head *c, *n, *p, *q, *l, *v; + struct saa7164_dev *dev = port->dev; + struct saa7164_buffer *buf; + struct saa7164_user_buffer *ubuf; + + /* Remove any allocated buffers */ + mutex_lock(&port->dmaqueue_lock); + + dprintk(DBGLVL_ENC, "%s(port=%d) dmaqueue\n", __func__, port->nr); + list_for_each_safe(c, n, &port->dmaqueue.list) { + buf = list_entry(c, struct saa7164_buffer, list); + list_del(c); + saa7164_buffer_dealloc(buf); + } + + dprintk(DBGLVL_ENC, "%s(port=%d) used\n", __func__, port->nr); + list_for_each_safe(p, q, &port->list_buf_used.list) { + ubuf = list_entry(p, struct saa7164_user_buffer, list); + list_del(p); + saa7164_buffer_dealloc_user(ubuf); + } + + dprintk(DBGLVL_ENC, "%s(port=%d) free\n", __func__, port->nr); + list_for_each_safe(l, v, &port->list_buf_free.list) { + ubuf = list_entry(l, struct saa7164_user_buffer, list); + list_del(l); + saa7164_buffer_dealloc_user(ubuf); + } + + mutex_unlock(&port->dmaqueue_lock); + dprintk(DBGLVL_ENC, "%s(port=%d) done\n", __func__, port->nr); + + return 0; +} + +/* Dynamic buffer switch at encoder start time */ +static int saa7164_encoder_buffers_alloc(struct saa7164_port *port) { struct saa7164_dev *dev = port->dev; + struct saa7164_buffer *buf; + struct saa7164_user_buffer *ubuf; + tmHWStreamParameters_t *params = &port->hw_streamingparams; + int result = -ENODEV, i; + int len = 0; dprintk(DBGLVL_ENC, "%s()\n", __func__); - saa7164_encoder_configure(port); + if (port->encoder_params.stream_type == V4L2_MPEG_STREAM_TYPE_MPEG2_PS) { + dprintk(DBGLVL_ENC, "%s() type=V4L2_MPEG_STREAM_TYPE_MPEG2_PS\n", __func__); + params->samplesperline = 128; + params->numberoflines = 256; + params->pitch = 128; + params->numpagetables = 2 + + ((SAA7164_PS_NUMBER_OF_LINES * 128) / PAGE_SIZE); + } else + if (port->encoder_params.stream_type == V4L2_MPEG_STREAM_TYPE_MPEG2_TS) { + dprintk(DBGLVL_ENC, "%s() type=V4L2_MPEG_STREAM_TYPE_MPEG2_TS\n", __func__); + params->samplesperline = 188; + params->numberoflines = 312; + params->pitch = 188; + params->numpagetables = 2 + + ((SAA7164_TS_NUMBER_OF_LINES * 188) / PAGE_SIZE); + } else + BUG(); + + /* Init and establish defaults */ + params->bitspersample = 8; + params->linethreshold = 0; + params->pagetablelistvirt = 0; + params->pagetablelistphys = 0; + params->numpagetableentries = port->hwcfg.buffercount; + + /* Allocate the PCI resources, buffers (hard) */ + for (i = 0; i < port->hwcfg.buffercount; i++) { + buf = saa7164_buffer_alloc(port, + params->numberoflines * + params->pitch); + + if (!buf) { + printk(KERN_ERR "%s() failed " + "(errno = %d), unable to allocate buffer\n", + __func__, result); + result = -ENOMEM; + goto failed; + } else { + + mutex_lock(&port->dmaqueue_lock); + list_add_tail(&buf->list, &port->dmaqueue.list); + mutex_unlock(&port->dmaqueue_lock); + + } + } + + /* Allocate some kenrel kernel buffers for copying + * to userpsace. + */ + len = params->numberoflines * params->pitch; + + if (encoder_buffers < 16) + encoder_buffers = 16; + if (encoder_buffers > 512) + encoder_buffers = 512; + + for (i = 0; i < encoder_buffers; i++) { + + ubuf = saa7164_buffer_alloc_user(dev, len); + if (ubuf) { + mutex_lock(&port->dmaqueue_lock); + list_add_tail(&ubuf->list, &port->list_buf_free.list); + mutex_unlock(&port->dmaqueue_lock); + } + + } + + result = 0; +failed: + return result; +} + +static int saa7164_encoder_initialize(struct saa7164_port *port) +{ + saa7164_encoder_configure(port); return 0; } @@ -835,6 +951,7 @@ static int saa7164_encoder_stop_streaming(struct saa7164_port *port) dprintk(DBGLVL_ENC, "%s(port=%d) Hardware stopped\n", __func__, port->nr); + /* Reset the state of any allocated buffer resources */ mutex_lock(&port->dmaqueue_lock); /* Reset the hard and soft buffer state */ @@ -851,6 +968,10 @@ static int saa7164_encoder_stop_streaming(struct saa7164_port *port) } mutex_unlock(&port->dmaqueue_lock); + + /* Free any allocated resources */ + saa7164_encoder_buffers_dealloc(port); + dprintk(DBGLVL_ENC, "%s(port=%d) Released\n", __func__, port->nr); return ret; @@ -863,10 +984,19 @@ static int saa7164_encoder_start_streaming(struct saa7164_port *port) dprintk(DBGLVL_ENC, "%s(port=%d)\n", __func__, port->nr); + port->done_first_interrupt = 0; + + /* allocate all of the PCIe DMA buffer resources on the fly, + * allowing switching between TS and PS payloads without + * requiring a complete driver reload. + */ + saa7164_encoder_buffers_alloc(port); + /* Configure the encoder with any cache values */ saa7164_api_set_encoder(port); saa7164_api_get_encoder(port); + /* Place the empty buffers on the hardware */ saa7164_buffer_cfg_port(port); /* Acquire the hardware */ @@ -1005,27 +1135,29 @@ static int fops_release(struct file *file) struct saa7164_user_buffer *saa7164_enc_next_buf(struct saa7164_port *port) { - struct saa7164_user_buffer *buf = 0; + struct saa7164_user_buffer *ubuf = 0; struct saa7164_dev *dev = port->dev; u32 crc; mutex_lock(&port->dmaqueue_lock); if (!list_empty(&port->list_buf_used.list)) { - buf = list_first_entry(&port->list_buf_used.list, + ubuf = list_first_entry(&port->list_buf_used.list, struct saa7164_user_buffer, list); - crc = crc32(0, buf->data, buf->actual_size); - if (crc != buf->crc) { - printk(KERN_ERR "%s() buf %p crc became invalid, was 0x%x became 0x%x\n", __func__, - buf, buf->crc, crc); + if (crc_checking) { + crc = crc32(0, ubuf->data, ubuf->actual_size); + if (crc != ubuf->crc) { + printk(KERN_ERR "%s() ubuf %p crc became invalid, was 0x%x became 0x%x\n", __func__, + ubuf, ubuf->crc, crc); + } } } mutex_unlock(&port->dmaqueue_lock); - dprintk(DBGLVL_ENC, "%s() returns %p\n", __func__, buf); + dprintk(DBGLVL_ENC, "%s() returns %p\n", __func__, ubuf); - return buf; + return ubuf; } static ssize_t fops_read(struct file *file, char __user *buffer, @@ -1292,10 +1424,7 @@ static struct video_device *saa7164_encoder_alloc( int saa7164_encoder_register(struct saa7164_port *port) { struct saa7164_dev *dev = port->dev; - struct saa7164_buffer *buf; - struct saa7164_user_buffer *ubuf; - int result = -ENODEV, i; - int len = 0; + int result = -ENODEV; dprintk(DBGLVL_ENC, "%s()\n", __func__); @@ -1311,64 +1440,6 @@ int saa7164_encoder_register(struct saa7164_port *port) goto failed; } - /* Init and establish defaults */ - /* TODO: Check the umber of lines for PS */ - port->hw_streamingparams.bitspersample = 8; - port->hw_streamingparams.samplesperline = 128; - port->hw_streamingparams.numberoflines = 256; - - port->hw_streamingparams.pitch = 128; - port->hw_streamingparams.linethreshold = 0; - port->hw_streamingparams.pagetablelistvirt = 0; - port->hw_streamingparams.pagetablelistphys = 0; - port->hw_streamingparams.numpagetables = 2 + - ((SAA7164_PS_NUMBER_OF_LINES * 128) / PAGE_SIZE); - - port->hw_streamingparams.numpagetableentries = port->hwcfg.buffercount; - - /* Allocate the PCI resources, buffers (hard) */ - for (i = 0; i < port->hwcfg.buffercount; i++) { - buf = saa7164_buffer_alloc(port, - port->hw_streamingparams.numberoflines * - port->hw_streamingparams.pitch); - - if (!buf) { - printk(KERN_ERR "%s() failed " - "(errno = %d), unable to allocate buffer\n", - __func__, result); - result = -ENOMEM; - goto failed; - } else { - - mutex_lock(&port->dmaqueue_lock); - list_add_tail(&buf->list, &port->dmaqueue.list); - mutex_unlock(&port->dmaqueue_lock); - - } - } - - /* Allocate some kenrel kernel buffers for copying - * to userpsace. - */ - len = port->hw_streamingparams.numberoflines * - port->hw_streamingparams.pitch; - - if (encoder_buffers < 16) - encoder_buffers = 16; - if (encoder_buffers > 512) - encoder_buffers = 512; - - for (i = 0; i < encoder_buffers; i++) { - - ubuf = saa7164_buffer_alloc_user(dev, len); - if (ubuf) { - mutex_lock(&port->dmaqueue_lock); - list_add_tail(&ubuf->list, &port->list_buf_free.list); - mutex_unlock(&port->dmaqueue_lock); - } - - } - /* Establish encoder defaults here */ /* Set default TV standard */ port->encodernorm = saa7164_tvnorms[0]; @@ -1446,9 +1517,6 @@ failed: void saa7164_encoder_unregister(struct saa7164_port *port) { struct saa7164_dev *dev = port->dev; - struct saa7164_buffer *buf; - struct saa7164_user_buffer *ubuf; - struct list_head *c, *n, *p, *q, *l, *v; dprintk(DBGLVL_ENC, "%s(port=%d)\n", __func__, port->nr); @@ -1464,31 +1532,6 @@ void saa7164_encoder_unregister(struct saa7164_port *port) port->v4l_device = NULL; } - /* Remove any allocated buffers */ - mutex_lock(&port->dmaqueue_lock); - - dprintk(DBGLVL_ENC, "%s(port=%d) dmaqueue\n", __func__, port->nr); - list_for_each_safe(c, n, &port->dmaqueue.list) { - buf = list_entry(c, struct saa7164_buffer, list); - list_del(c); - saa7164_buffer_dealloc(buf); - } - - dprintk(DBGLVL_ENC, "%s(port=%d) used\n", __func__, port->nr); - list_for_each_safe(p, q, &port->list_buf_used.list) { - ubuf = list_entry(p, struct saa7164_user_buffer, list); - list_del(p); - saa7164_buffer_dealloc_user(ubuf); - } - - dprintk(DBGLVL_ENC, "%s(port=%d) free\n", __func__, port->nr); - list_for_each_safe(l, v, &port->list_buf_free.list) { - ubuf = list_entry(l, struct saa7164_user_buffer, list); - list_del(l); - saa7164_buffer_dealloc_user(ubuf); - } - - mutex_unlock(&port->dmaqueue_lock); dprintk(DBGLVL_ENC, "%s(port=%d) done\n", __func__, port->nr); } diff --git a/drivers/media/video/saa7164/saa7164.h b/drivers/media/video/saa7164/saa7164.h index 71e385328ce3..0cafe8a557e5 100644 --- a/drivers/media/video/saa7164/saa7164.h +++ b/drivers/media/video/saa7164/saa7164.h @@ -389,6 +389,7 @@ struct saa7164_port { u32 a_cc_errors; u8 last_v_cc; u8 last_a_cc; + u32 done_first_interrupt; }; struct saa7164_dev { @@ -546,6 +547,8 @@ void saa7164_encoder_unregister(struct saa7164_port *port); /* ----------------------------------------------------------- */ +extern unsigned int crc_checking; + extern unsigned int saa_debug; #define dprintk(level, fmt, arg...)\ do { if (saa_debug & level)\ -- 2.34.1