From patchwork Fri Apr 1 22:38:24 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ezequiel Garcia X-Patchwork-Id: 8728281 Return-Path: X-Original-To: patchwork-linux-media@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 4402D9F36E for ; Fri, 1 Apr 2016 22:38:54 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id F30412039E for ; Fri, 1 Apr 2016 22:38:52 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id A3BA62038A for ; Fri, 1 Apr 2016 22:38:51 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754341AbcDAWiu (ORCPT ); Fri, 1 Apr 2016 18:38:50 -0400 Received: from mail-qg0-f53.google.com ([209.85.192.53]:33158 "EHLO mail-qg0-f53.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753797AbcDAWit (ORCPT ); Fri, 1 Apr 2016 18:38:49 -0400 Received: by mail-qg0-f53.google.com with SMTP id j35so108280284qge.0 for ; Fri, 01 Apr 2016 15:38:49 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=vanguardiasur-com-ar.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=zXUG4DoLFWWL2N+wjYg4oD7XLyNxm2PRjY5h4AOy5sE=; b=tCOQsusxSxbLHqo5ri/+x1AxlH0OJ69QU5Kmn2TbDkfq9t8F/NUJFyJOc0F6QZtkYk X6ar7U++xNIy9nII0uyo/uUZVjS1JPM4tPRF1/YPH5KPqKkyEQLyRdRe6TZko3jOnTNw c87uGGkui764CUByhVs3iKblvt494hhOnsLA+KnPQm0y0M+SAV/PXsLF29oTywOlFonY QSfyGNwudvpTaEm9iPM+Z/Rbax/zMvhMK2Vt+R63l7KCip3fvSjC29aOtKmQwOH7A6fk Ij3puaMtKjsqIOScVsAqBm/kEfyQhMVu0eWhfLnd6vQMnUKQZCAnw0Q0Z1TxlkbDKjMS qrCQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=zXUG4DoLFWWL2N+wjYg4oD7XLyNxm2PRjY5h4AOy5sE=; b=BrO0mUpJw1ZPSaUB42qmII67Hv6ho1HGeoBtH4IJwEO4v6xTPL5+rErOMlLG0LZYWu tP/Gl1EjL9JIUaE6kXCWPTmOP1KcpgMWNdUe0FSkGuaWMTLU7PgyuW8h+A0kNDSrDbeY SEJVkvQyTbQ65qzIPf7/q+4gsdjXiAsW2lxUKaTCAgUrxGdVTiklzwhkfp//2DPAjAhJ nzI7YD/iOOzOHdt/fJ8jFMfn6ilmHXwjBFbap46ITQLMImSdnHwwzoQbuB8RPcdRhGrH 4uCTsZiAaRg39aCAHDmiQ4ZkhaNGAblEaFGi5LAsCSeRyu2zUf99tB3vGV9vvSt/ip7G 1+2A== X-Gm-Message-State: AD7BkJICrLSQ5XGQX4trqILUTnhYGfJGGA+NneAHB1HP80hA6G0Rgawrrm0HGhsb7w+HUw== X-Received: by 10.140.150.66 with SMTP id 63mr10748981qhw.91.1459550328650; Fri, 01 Apr 2016 15:38:48 -0700 (PDT) Received: from localhost.localdomain ([190.2.108.156]) by smtp.gmail.com with ESMTPSA id k25sm7091105qkl.29.2016.04.01.15.38.46 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Fri, 01 Apr 2016 15:38:47 -0700 (PDT) From: Ezequiel Garcia To: Cc: Hans Verkuil , Ezequiel Garcia Subject: [PATCH 4/7] tw686x: Add support for DMA scatter-gather mode Date: Fri, 1 Apr 2016 19:38:24 -0300 Message-Id: <1459550307-688-5-git-send-email-ezequiel@vanguardiasur.com.ar> X-Mailer: git-send-email 2.7.0 In-Reply-To: <1459550307-688-1-git-send-email-ezequiel@vanguardiasur.com.ar> References: <1459550307-688-1-git-send-email-ezequiel@vanguardiasur.com.ar> Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org X-Spam-Status: No, score=-7.8 required=5.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_HI,RP_MATCHES_RCVD,T_DKIM_INVALID,UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Now that the driver has the infrastructure to support more DMA modes, let's add the DMA scatter-gather mode. In this mode, the device delivers sequential top-bottom frames. Signed-off-by: Ezequiel Garcia Tested-by: Tim Harvey --- drivers/media/pci/tw686x/Kconfig | 1 + drivers/media/pci/tw686x/tw686x-core.c | 4 + drivers/media/pci/tw686x/tw686x-video.c | 188 ++++++++++++++++++++++++++++++++ drivers/media/pci/tw686x/tw686x.h | 14 +++ 4 files changed, 207 insertions(+) diff --git a/drivers/media/pci/tw686x/Kconfig b/drivers/media/pci/tw686x/Kconfig index ef8ca85522f8..34ff37712313 100644 --- a/drivers/media/pci/tw686x/Kconfig +++ b/drivers/media/pci/tw686x/Kconfig @@ -4,6 +4,7 @@ config VIDEO_TW686X depends on HAS_DMA select VIDEOBUF2_VMALLOC select VIDEOBUF2_DMA_CONTIG + select VIDEOBUF2_DMA_SG select SND_PCM help Support for Intersil/Techwell TW686x-based frame grabber cards. diff --git a/drivers/media/pci/tw686x/tw686x-core.c b/drivers/media/pci/tw686x/tw686x-core.c index 9a7646c0f9f6..586bc6723c93 100644 --- a/drivers/media/pci/tw686x/tw686x-core.c +++ b/drivers/media/pci/tw686x/tw686x-core.c @@ -65,6 +65,8 @@ static const char *dma_mode_name(unsigned int mode) return "memcpy"; case TW686X_DMA_MODE_CONTIG: return "contig"; + case TW686X_DMA_MODE_SG: + return "sg"; default: return "unknown"; } @@ -81,6 +83,8 @@ static int tw686x_dma_mode_set(const char *val, struct kernel_param *kp) dma_mode = TW686X_DMA_MODE_MEMCPY; else if (!strcasecmp(val, dma_mode_name(TW686X_DMA_MODE_CONTIG))) dma_mode = TW686X_DMA_MODE_CONTIG; + else if (!strcasecmp(val, dma_mode_name(TW686X_DMA_MODE_SG))) + dma_mode = TW686X_DMA_MODE_SG; else return -EINVAL; return 0; diff --git a/drivers/media/pci/tw686x/tw686x-video.c b/drivers/media/pci/tw686x/tw686x-video.c index ed6abb4c41c2..16228c559f9a 100644 --- a/drivers/media/pci/tw686x/tw686x-video.c +++ b/drivers/media/pci/tw686x/tw686x-video.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include "tw686x.h" #include "tw686x-regs.h" @@ -28,6 +29,10 @@ #define TW686X_VIDEO_WIDTH 720 #define TW686X_VIDEO_HEIGHT(id) ((id & V4L2_STD_625_50) ? 576 : 480) +#define TW686X_MAX_SG_ENTRY_SIZE 4096 +#define TW686X_MAX_SG_DESC_COUNT 256 /* PAL 720x576 needs 203 4-KB pages */ +#define TW686X_SG_TABLE_SIZE (TW686X_MAX_SG_DESC_COUNT * sizeof(struct tw686x_sg_desc)) + static const struct tw686x_format formats[] = { { .fourcc = V4L2_PIX_FMT_UYVY, @@ -196,6 +201,174 @@ const struct tw686x_dma_ops contig_dma_ops = { .field = V4L2_FIELD_INTERLACED, }; +static int tw686x_sg_desc_fill(struct tw686x_sg_desc *descs, + struct tw686x_v4l2_buf *buf, + unsigned int buf_len) +{ + struct sg_table *vbuf = vb2_dma_sg_plane_desc(&buf->vb.vb2_buf, 0); + unsigned int len, entry_len; + struct scatterlist *sg; + int i, count; + + /* Clear the scatter-gather table */ + memset(descs, 0, TW686X_SG_TABLE_SIZE); + + count = 0; + for_each_sg(vbuf->sgl, sg, vbuf->nents, i) { + dma_addr_t phys = sg_dma_address(sg); + len = sg_dma_len(sg); + + while (len && buf_len) { + + if (count == TW686X_MAX_SG_DESC_COUNT) + return -ENOMEM; + + entry_len = min_t(unsigned int, len, + TW686X_MAX_SG_ENTRY_SIZE); + entry_len = min_t(unsigned int, entry_len, buf_len); + descs[count].phys = cpu_to_le32(phys); + descs[count++].flags_length = + cpu_to_le32(BIT(30) | entry_len); + phys += entry_len; + len -= entry_len; + buf_len -= entry_len; + } + + if (!buf_len) + return 0; + } + + return -ENOMEM; +} + +static void tw686x_sg_buf_refill(struct tw686x_video_channel *vc, + unsigned int pb) +{ + struct tw686x_dev *dev = vc->dev; + struct tw686x_v4l2_buf *buf; + + while (!list_empty(&vc->vidq_queued)) { + unsigned int buf_len; + + buf = list_first_entry(&vc->vidq_queued, + struct tw686x_v4l2_buf, list); + list_del(&buf->list); + + buf_len = (vc->width * vc->height * vc->format->depth) >> 3; + if (tw686x_sg_desc_fill(vc->sg_descs[pb], buf, buf_len)) { + v4l2_err(&dev->v4l2_dev, + "dma%d: unable to fill %s-buffer\n", + vc->ch, pb ? "B" : "P"); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + continue; + } + + buf->vb.vb2_buf.state = VB2_BUF_STATE_ACTIVE; + vc->curr_bufs[pb] = buf; + return; + } + + vc->curr_bufs[pb] = NULL; +} + +static void tw686x_sg_dma_free(struct tw686x_video_channel *vc, + unsigned int pb) +{ + struct tw686x_dma_desc *desc = &vc->dma_descs[pb]; + struct tw686x_dev *dev = vc->dev; + + if (desc->size) { + pci_free_consistent(dev->pci_dev, desc->size, + desc->virt, desc->phys); + desc->virt = NULL; + } + + vc->sg_descs[pb] = NULL; +} + +static int tw686x_sg_dma_alloc(struct tw686x_video_channel *vc, + unsigned int pb) +{ + struct tw686x_dma_desc *desc = &vc->dma_descs[pb]; + struct tw686x_dev *dev = vc->dev; + u32 reg = pb ? DMA_PAGE_TABLE1_ADDR[vc->ch] : + DMA_PAGE_TABLE0_ADDR[vc->ch]; + void *virt; + + if (desc->size) { + + virt = pci_alloc_consistent(dev->pci_dev, desc->size, + &desc->phys); + if (!virt) { + v4l2_err(&dev->v4l2_dev, + "dma%d: unable to allocate %s-buffer\n", + vc->ch, pb ? "B" : "P"); + return -ENOMEM; + } + desc->virt = virt; + reg_write(dev, reg, desc->phys); + } else { + virt = dev->video_channels[0].dma_descs[pb].virt + + vc->ch * TW686X_SG_TABLE_SIZE; + } + + vc->sg_descs[pb] = virt; + return 0; +} + +static void tw686x_sg_cleanup(struct tw686x_dev *dev) +{ + vb2_dma_sg_cleanup_ctx(dev->alloc_ctx); +} + +static int tw686x_sg_setup(struct tw686x_dev *dev) +{ + unsigned int sg_table_size, pb, ch, channels; + + dev->alloc_ctx = vb2_dma_sg_init_ctx(&dev->pci_dev->dev); + if (IS_ERR(dev->alloc_ctx)) { + dev_err(&dev->pci_dev->dev, "unable to init DMA context\n"); + return PTR_ERR(dev->alloc_ctx); + } + + if (is_second_gen(dev)) { + /* + * TW6865/TW6869: each channel needs a pair of + * P-B descriptor tables. + */ + channels = max_channels(dev); + sg_table_size = TW686X_SG_TABLE_SIZE; + } else { + /* + * TW6864/TW6868: we need to allocate a pair of + * P-B descriptor tables, common for all channels. + * Each table will be bigger than 4 KB. + */ + channels = 1; + sg_table_size = max_channels(dev) * TW686X_SG_TABLE_SIZE; + } + + for (ch = 0; ch < channels; ch++) { + struct tw686x_video_channel *vc = &dev->video_channels[ch]; + + for (pb = 0; pb < 2; pb++) + vc->dma_descs[pb].size = sg_table_size; + } + + return 0; +} + +const struct tw686x_dma_ops sg_dma_ops = { + .setup = tw686x_sg_setup, + .cleanup = tw686x_sg_cleanup, + .alloc = tw686x_sg_dma_alloc, + .free = tw686x_sg_dma_free, + .buf_refill = tw686x_sg_buf_refill, + .mem_ops = &vb2_dma_sg_memops, + .hw_dma_mode = TW686X_SG_MODE, + .field = V4L2_FIELD_SEQ_TB, +}; + static unsigned int tw686x_fields_map(v4l2_std_id std, unsigned int fps) { static const unsigned int map[15] = { @@ -545,6 +718,19 @@ static int tw686x_s_fmt_vid_cap(struct file *file, void *priv, else val &= ~BIT(24); + val &= ~0x7ffff; + + /* Program the DMA scatter-gather */ + if (dev->dma_mode == TW686X_DMA_MODE_SG) { + u32 start_idx, end_idx; + + start_idx = is_second_gen(dev) ? + 0 : vc->ch * TW686X_MAX_SG_DESC_COUNT; + end_idx = start_idx + TW686X_MAX_SG_DESC_COUNT - 1; + + val |= (end_idx << 10) | start_idx; + } + val &= ~(0x7 << 20); val |= vc->format->mode << 20; reg_write(vc->dev, VDMA_CHANNEL_CONFIG[vc->ch], val); @@ -882,6 +1068,8 @@ int tw686x_video_init(struct tw686x_dev *dev) dev->dma_ops = &memcpy_dma_ops; else if (dev->dma_mode == TW686X_DMA_MODE_CONTIG) dev->dma_ops = &contig_dma_ops; + else if (dev->dma_mode == TW686X_DMA_MODE_SG) + dev->dma_ops = &sg_dma_ops; else return -EINVAL; diff --git a/drivers/media/pci/tw686x/tw686x.h b/drivers/media/pci/tw686x/tw686x.h index 938f16b2449a..fe848a40f9d0 100644 --- a/drivers/media/pci/tw686x/tw686x.h +++ b/drivers/media/pci/tw686x/tw686x.h @@ -34,6 +34,7 @@ #define TW686X_DMA_MODE_MEMCPY 0 #define TW686X_DMA_MODE_CONTIG 1 +#define TW686X_DMA_MODE_SG 2 struct tw686x_format { char *name; @@ -48,6 +49,12 @@ struct tw686x_dma_desc { unsigned size; }; +struct tw686x_sg_desc { + /* 3 MSBits for flags, 13 LSBits for length */ + __le32 flags_length; + __le32 phys; +}; + struct tw686x_audio_buf { dma_addr_t dma; void *virt; @@ -80,6 +87,7 @@ struct tw686x_video_channel { struct video_device *device; struct tw686x_v4l2_buf *curr_bufs[2]; struct tw686x_dma_desc dma_descs[2]; + struct tw686x_sg_desc *sg_descs[2]; struct v4l2_ctrl_handler ctrl_handler; const struct tw686x_format *format; @@ -154,6 +162,12 @@ static inline unsigned max_channels(struct tw686x_dev *dev) return dev->type & TYPE_MAX_CHANNELS; /* 4 or 8 channels */ } +static inline unsigned is_second_gen(struct tw686x_dev *dev) +{ + /* each channel has its own DMA SG table */ + return dev->type & TYPE_SECOND_GEN; +} + void tw686x_enable_channel(struct tw686x_dev *dev, unsigned int channel); void tw686x_disable_channel(struct tw686x_dev *dev, unsigned int channel);