Commit f8afaa8d authored by Ezequiel Garcia's avatar Ezequiel Garcia Committed by Mauro Carvalho Chehab

[media] tw686x: Introduce an interface to support multiple DMA modes

Let's set the corner stone to support all the DMA modes
available on this device.

For stability reasons, the driver is currently setting DMA frame
mode, and using single DMA buffers to get the P and B buffers.
Each frame is then memcpy'ed into the user buffer.

However, other platforms might be interested in avoiding this
memcpy, or in taking advantage of the chip's DMA scatter-gather
capabilities.

To achieve this, this commit introduces a "dma_mode" module parameter,
and a tw686x_dma_ops struct. This will allow to define functions to
alloc/free DMA buffers, and to return the frames to userspace.

The memcpy-based method described above is named as dma_mode="memcpy".
Current alloc/free functions are renamed as tw686x_memcpy_xxx,
and are now used through a memcpy_dma_ops.
Tested-by: default avatarTim Harvey <tharvey@gateworks.com>
Signed-off-by: default avatarEzequiel Garcia <ezequiel@vanguardiasur.com.ar>
Signed-off-by: default avatarHans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@s-opensource.com>
parent 068adc45
...@@ -21,12 +21,14 @@ ...@@ -21,12 +21,14 @@
* under stress testings it has been found that the machine can * under stress testings it has been found that the machine can
* freeze completely if DMA registers are programmed while streaming * freeze completely if DMA registers are programmed while streaming
* is active. * is active.
* This driver tries to access hardware registers as infrequently *
* as possible by: * Therefore, driver implements a dma_mode called 'memcpy' which
* i. allocating fixed DMA buffers and memcpy'ing into * avoids cycling the DMA buffers, and insteads allocates extra DMA buffers
* vmalloc'ed buffers * and then copies into vmalloc'ed user buffers.
* ii. using a timer to mitigate the rate of DMA reset operations, *
* on DMA channels error. * In addition to this, when streaming is on, the driver tries to access
* hardware registers as infrequently as possible. This is done by using
* a timer to limit the rate at which DMA is reset on DMA channels error.
*/ */
#include <linux/init.h> #include <linux/init.h>
...@@ -55,6 +57,34 @@ static u32 dma_interval = 0x00098968; ...@@ -55,6 +57,34 @@ static u32 dma_interval = 0x00098968;
module_param(dma_interval, int, 0444); module_param(dma_interval, int, 0444);
MODULE_PARM_DESC(dma_interval, "Minimum time span for DMA interrupting host"); MODULE_PARM_DESC(dma_interval, "Minimum time span for DMA interrupting host");
static unsigned int dma_mode = TW686X_DMA_MODE_MEMCPY;
static const char *dma_mode_name(unsigned int mode)
{
switch (mode) {
case TW686X_DMA_MODE_MEMCPY:
return "memcpy";
default:
return "unknown";
}
}
static int tw686x_dma_mode_get(char *buffer, struct kernel_param *kp)
{
return sprintf(buffer, dma_mode_name(dma_mode));
}
static int tw686x_dma_mode_set(const char *val, struct kernel_param *kp)
{
if (!strcasecmp(val, dma_mode_name(TW686X_DMA_MODE_MEMCPY)))
dma_mode = TW686X_DMA_MODE_MEMCPY;
else
return -EINVAL;
return 0;
}
module_param_call(dma_mode, tw686x_dma_mode_set, tw686x_dma_mode_get,
&dma_mode, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(dma_mode, "DMA operation mode");
void tw686x_disable_channel(struct tw686x_dev *dev, unsigned int channel) void tw686x_disable_channel(struct tw686x_dev *dev, unsigned int channel)
{ {
u32 dma_en = reg_read(dev, DMA_CHANNEL_ENABLE); u32 dma_en = reg_read(dev, DMA_CHANNEL_ENABLE);
...@@ -212,6 +242,7 @@ static int tw686x_probe(struct pci_dev *pci_dev, ...@@ -212,6 +242,7 @@ static int tw686x_probe(struct pci_dev *pci_dev,
if (!dev) if (!dev)
return -ENOMEM; return -ENOMEM;
dev->type = pci_id->driver_data; dev->type = pci_id->driver_data;
dev->dma_mode = dma_mode;
sprintf(dev->name, "tw%04X", pci_dev->device); sprintf(dev->name, "tw%04X", pci_dev->device);
dev->video_channels = kcalloc(max_channels(dev), dev->video_channels = kcalloc(max_channels(dev),
...@@ -228,9 +259,10 @@ static int tw686x_probe(struct pci_dev *pci_dev, ...@@ -228,9 +259,10 @@ static int tw686x_probe(struct pci_dev *pci_dev,
goto free_video; goto free_video;
} }
pr_info("%s: PCI %s, IRQ %d, MMIO 0x%lx\n", dev->name, pr_info("%s: PCI %s, IRQ %d, MMIO 0x%lx (%s mode)\n", dev->name,
pci_name(pci_dev), pci_dev->irq, pci_name(pci_dev), pci_dev->irq,
(unsigned long)pci_resource_start(pci_dev, 0)); (unsigned long)pci_resource_start(pci_dev, 0),
dma_mode_name(dma_mode));
dev->pci_dev = pci_dev; dev->pci_dev = pci_dev;
if (pci_enable_device(pci_dev)) { if (pci_enable_device(pci_dev)) {
......
...@@ -119,4 +119,9 @@ ...@@ -119,4 +119,9 @@
#define TW686X_STD_PAL_CN 5 #define TW686X_STD_PAL_CN 5
#define TW686X_STD_PAL_60 6 #define TW686X_STD_PAL_60 6
#define TW686X_FIELD_MODE 0x3
#define TW686X_FRAME_MODE 0x2
/* 0x1 is reserved */
#define TW686X_SG_MODE 0x0
#define TW686X_FIFO_ERROR(x) (x & ~(0xff)) #define TW686X_FIFO_ERROR(x) (x & ~(0xff))
This diff is collapsed.
...@@ -27,16 +27,13 @@ ...@@ -27,16 +27,13 @@
#define TYPE_SECOND_GEN 0x10 #define TYPE_SECOND_GEN 0x10
#define TW686X_DEF_PHASE_REF 0x1518 #define TW686X_DEF_PHASE_REF 0x1518
#define TW686X_FIELD_MODE 0x3
#define TW686X_FRAME_MODE 0x2
/* 0x1 is reserved */
#define TW686X_SG_MODE 0x0
#define TW686X_AUDIO_PAGE_SZ 4096 #define TW686X_AUDIO_PAGE_SZ 4096
#define TW686X_AUDIO_PAGE_MAX 16 #define TW686X_AUDIO_PAGE_MAX 16
#define TW686X_AUDIO_PERIODS_MIN 2 #define TW686X_AUDIO_PERIODS_MIN 2
#define TW686X_AUDIO_PERIODS_MAX TW686X_AUDIO_PAGE_MAX #define TW686X_AUDIO_PERIODS_MAX TW686X_AUDIO_PAGE_MAX
#define TW686X_DMA_MODE_MEMCPY 0
struct tw686x_format { struct tw686x_format {
char *name; char *name;
unsigned int fourcc; unsigned int fourcc;
...@@ -99,6 +96,17 @@ struct tw686x_video_channel { ...@@ -99,6 +96,17 @@ struct tw686x_video_channel {
bool no_signal; bool no_signal;
}; };
struct tw686x_dma_ops {
int (*setup)(struct tw686x_dev *dev);
void (*cleanup)(struct tw686x_dev *dev);
int (*alloc)(struct tw686x_video_channel *vc, unsigned int pb);
void (*free)(struct tw686x_video_channel *vc, unsigned int pb);
void (*buf_refill)(struct tw686x_video_channel *vc, unsigned int pb);
const struct vb2_mem_ops *mem_ops;
enum v4l2_field field;
u32 hw_dma_mode;
};
/** /**
* struct tw686x_dev - global device status * struct tw686x_dev - global device status
* @lock: spinlock controlling access to the * @lock: spinlock controlling access to the
...@@ -112,11 +120,13 @@ struct tw686x_dev { ...@@ -112,11 +120,13 @@ struct tw686x_dev {
char name[32]; char name[32];
unsigned int type; unsigned int type;
unsigned int dma_mode;
struct pci_dev *pci_dev; struct pci_dev *pci_dev;
__u32 __iomem *mmio; __u32 __iomem *mmio;
void *alloc_ctx; void *alloc_ctx;
const struct tw686x_dma_ops *dma_ops;
struct tw686x_video_channel *video_channels; struct tw686x_video_channel *video_channels;
struct tw686x_audio_channel *audio_channels; struct tw686x_audio_channel *audio_channels;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment