From patchwork Mon May 1 15:20:24 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Zigotzky X-Patchwork-Id: 678288 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 778ABC77B73 for ; Mon, 1 May 2023 15:23:34 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232758AbjEAPXd (ORCPT ); Mon, 1 May 2023 11:23:33 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49636 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232748AbjEAPXc (ORCPT ); Mon, 1 May 2023 11:23:32 -0400 X-Greylist: delayed 169 seconds by postgrey-1.37 at lindbergh.monkeyblade.net; Mon, 01 May 2023 08:23:27 PDT Received: from mo4-p00-ob.smtp.rzone.de (mo4-p00-ob.smtp.rzone.de [81.169.146.161]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 2F7191703 for ; Mon, 1 May 2023 08:23:26 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1682954425; cv=none; d=strato.com; s=strato-dkim-0002; b=cxgbdjO2j+WizYMld1ixOx6WXK8tkiF6Gq8oQzvmlCu4R4kLwGcAV6wf+3l1DlUzcp qH8Wa0hUrpUGOEV4064mANNdiR1etrvMsHeDBFa2x84yXebNT5I6v05eyx6A1MYCGwC2 UU8GtXixyzvQWicYeKf/o+3rEaiu8Slj7CCw48r5ugjWkpDu3MyyjEJ3zzvJF5LDKF7q xeErZagjGI2CFUMhLX2pSvsmz93vd1r7HpP8tU8Dbw+w1NDDWCT9Yq3phKFzgXpm0vRk LtpowAwrVGp2VPBa//YtHYZIQ4ANHx1YBx8m42KSBCt2qKT2KjuiAM5OZKxaP3AulByO gkwQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; t=1682954425; s=strato-dkim-0002; d=strato.com; h=In-Reply-To:References:Cc:To:From:Subject:Date:Message-ID:Cc:Date: From:Subject:Sender; bh=xURmSJKMwR3DXII6fR9To3fDvR+0JPcgTYoGVUVQYPU=; b=hgebarLQ6IKjZNhSmAnwLdE1sJlIvSFCEKkqxJZOJ2aNa37z8mdjvC/XlpC5U1RN4u qg+VYOV+91okG/ShBTV4o3oRE64941X9+KVjmNaYAXIp7xHDF08YbpOmYNW4Iax/huHL xQLLeaXcHhVAJ0I1Q1dowj6saDGicNKPSO9SlLo0a0hJstVvAE31xSc9TBMtDvNYYg/Z ZLzS7FpXbiXf3PaWfhHFQPh/bxl1jIegvOnmcBo5BcFb6LmObSKKfomMqPu51cL3AW6W YeTMbIxpnNML3rA71finNoQhi17WCZWI/Lm2p2caeQKIF866JUZ4xBjbDEdb8rDgGBFG y1ew== ARC-Authentication-Results: i=1; strato.com; arc=none; dkim=none X-RZG-CLASS-ID: mo00 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; t=1682954425; s=strato-dkim-0002; d=xenosoft.de; h=In-Reply-To:References:Cc:To:From:Subject:Date:Message-ID:Cc:Date: From:Subject:Sender; bh=xURmSJKMwR3DXII6fR9To3fDvR+0JPcgTYoGVUVQYPU=; b=Y3gv4CUWTFdbT1qF6RHinKJy1EUIqe9BPsgJQNIpA9KuQE+J3MsPyxaqzMED1KvzuY tuMLSrSLL5YFVA7InhHcC27voD67+KvCuRwA1AsYcsg9PaGB68qf04TDYHO6qEAK+CTR 3aJbny22xSiZ7yAy44j0tUN5ha9bwMHi1YspSCf0rMg8KElGM3tnil8rWBK91c94a5UN CHK6XinJ74Tg5iCJe6JpvAB6w0dm2ChyocMvRgcQUmR7qX02u7eKSu6LB/XtetbiOpC2 YIPijN+fS2HXbg89UiNZ1HKB1gGV+6oGrMf6IUUzS86llI7apATUzAShsiLpab/RxwSA XshQ== DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; t=1682954425; s=strato-dkim-0003; d=xenosoft.de; h=In-Reply-To:References:Cc:To:From:Subject:Date:Message-ID:Cc:Date: From:Subject:Sender; bh=xURmSJKMwR3DXII6fR9To3fDvR+0JPcgTYoGVUVQYPU=; b=iAQmGhcjQsuMEUhfixOMSsEVtVtto+YBDCRPU7LF6cmXt2pHeB87fCy35ERwKU5Bgk 7BqMZrGzKY6fd/eNflCg== X-RZG-AUTH: ":L2QefEenb+UdBJSdRCXu93KJ1bmSGnhMdmOod1DhGM4l4Hio94KKxRySfLxnHfJ+Dkjp5DdBfio0GngadwjXuh2mYDKPURdhgjt9UICyEgU4VQ==" Received: from [IPV6:2a02:8109:8980:4474:2c3a:a0ea:b083:2eb8] by smtp.strato.de (RZmta 49.4.0 AUTH) with ESMTPSA id w2b3aez41FKPFzq (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256 bits)) (Client did not present a certificate); Mon, 1 May 2023 17:20:25 +0200 (CEST) Message-ID: Date: Mon, 1 May 2023 17:20:24 +0200 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:102.0) Gecko/20100101 Thunderbird/102.10.0 Subject: [BTTV] [FSL P50x0] [PASEMI] TV Time doesn't work anymore after dropping the overlay support Content-Language: de-DE From: Christian Zigotzky To: linux-media@vger.kernel.org, hverkuil-cisco@xs4all.nl Cc: Darren Stevens , mad skateman , "R.T.Dickinson" References: <94a617be-00b3-6dc8-eb7d-ae13c6fffae5@xenosoft.de> In-Reply-To: <94a617be-00b3-6dc8-eb7d-ae13c6fffae5@xenosoft.de> Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org Hello, I created a patch for adding overlay support again. TV Time works without any problems again. [1] Please find attached the patch for adding the removed overlay support back for BTTV cards. Cheers, Christian [1] - https://i.ibb.co/6NJmj1y/Kernel-6-4-alpha1-Power-PC.png - https://i.ibb.co/7rx0MyD/Kernel-6-4-alpha2-Power-PC.png On 26 April 2023 at 04:09 pm, Christian Zigotzky wrote: > Hello, > > TV Time doesn't work anymore on my Cyrus+ board with a FSL P50x0 > PowerPC SoC [1] and on my P.A. Semi Nemo board [2] after dropping the > overlay support [3]. It starts and then the whole computer freezes. > > I use the following BTTV cards. > > - WinTV Express with a BT878A chip > - Typhoon TView RDS + FM Stereo (BT878 chip) > > It would be really nice if we could get the overlay support back, > because we love TV Time. [4] > > We use TV Time with connected TV receivers and game consoles. > > Thanks, > Christian > > [1] http://wiki.amiga.org/index.php?title=X5000 > [2] https://en.wikipedia.org/wiki/AmigaOne_X1000 > [3] > https://patchwork.kernel.org/project/linux-media/patch/20230302125731.1124945-4-hverkuil-cisco@xs4all.nl/ > [4] https://tvtime.sourceforge.net/ diff -rupN a/drivers/media/pci/bt8xx/btcx-risc.c b/drivers/media/pci/bt8xx/btcx-risc.c --- a/drivers/media/pci/bt8xx/btcx-risc.c 2023-04-27 04:46:15.574522003 +0200 +++ b/drivers/media/pci/bt8xx/btcx-risc.c 2023-04-27 04:45:43.011980514 +0200 @@ -75,3 +75,156 @@ int btcx_riscmem_alloc(struct pci_dev *p } return 0; } + +/* ---------------------------------------------------------- */ +/* screen overlay helpers */ + +int +btcx_screen_clips(int swidth, int sheight, struct v4l2_rect *win, + struct v4l2_clip *clips, unsigned int n) +{ + if (win->left < 0) { + /* left */ + clips[n].c.left = 0; + clips[n].c.top = 0; + clips[n].c.width = -win->left; + clips[n].c.height = win->height; + n++; + } + if (win->left + win->width > swidth) { + /* right */ + clips[n].c.left = swidth - win->left; + clips[n].c.top = 0; + clips[n].c.width = win->width - clips[n].c.left; + clips[n].c.height = win->height; + n++; + } + if (win->top < 0) { + /* top */ + clips[n].c.left = 0; + clips[n].c.top = 0; + clips[n].c.width = win->width; + clips[n].c.height = -win->top; + n++; + } + if (win->top + win->height > sheight) { + /* bottom */ + clips[n].c.left = 0; + clips[n].c.top = sheight - win->top; + clips[n].c.width = win->width; + clips[n].c.height = win->height - clips[n].c.top; + n++; + } + return n; +} + +int +btcx_align(struct v4l2_rect *win, struct v4l2_clip *clips, unsigned int n, int mask) +{ + s32 nx,nw,dx; + unsigned int i; + + /* fixup window */ + nx = (win->left + mask) & ~mask; + nw = (win->width) & ~mask; + if (nx + nw > win->left + win->width) + nw -= mask+1; + dx = nx - win->left; + win->left = nx; + win->width = nw; + dprintk("btcx: window align %dx%d+%d+%d [dx=%d]\n", + win->width, win->height, win->left, win->top, dx); + + /* fixup clips */ + for (i = 0; i < n; i++) { + nx = (clips[i].c.left-dx) & ~mask; + nw = (clips[i].c.width) & ~mask; + if (nx + nw < clips[i].c.left-dx + clips[i].c.width) + nw += mask+1; + clips[i].c.left = nx; + clips[i].c.width = nw; + dprintk("btcx: clip align %dx%d+%d+%d\n", + clips[i].c.width, clips[i].c.height, + clips[i].c.left, clips[i].c.top); + } + return 0; +} + +void +btcx_sort_clips(struct v4l2_clip *clips, unsigned int nclips) +{ + int i,j,n; + + if (nclips < 2) + return; + for (i = nclips-2; i >= 0; i--) { + for (n = 0, j = 0; j <= i; j++) { + if (clips[j].c.left > clips[j+1].c.left) { + swap(clips[j], clips[j + 1]); + n++; + } + } + if (0 == n) + break; + } +} + +void +btcx_calc_skips(int line, int width, int *maxy, + struct btcx_skiplist *skips, unsigned int *nskips, + const struct v4l2_clip *clips, unsigned int nclips) +{ + unsigned int clip,skip; + int end, maxline; + + skip=0; + maxline = 9999; + for (clip = 0; clip < nclips; clip++) { + + /* sanity checks */ + if (clips[clip].c.left + clips[clip].c.width <= 0) + continue; + if (clips[clip].c.left > (signed)width) + break; + + /* vertical range */ + if (line > clips[clip].c.top+clips[clip].c.height-1) + continue; + if (line < clips[clip].c.top) { + if (maxline > clips[clip].c.top-1) + maxline = clips[clip].c.top-1; + continue; + } + if (maxline > clips[clip].c.top+clips[clip].c.height-1) + maxline = clips[clip].c.top+clips[clip].c.height-1; + + /* horizontal range */ + if (0 == skip || clips[clip].c.left > skips[skip-1].end) { + /* new one */ + skips[skip].start = clips[clip].c.left; + if (skips[skip].start < 0) + skips[skip].start = 0; + skips[skip].end = clips[clip].c.left + clips[clip].c.width; + if (skips[skip].end > width) + skips[skip].end = width; + skip++; + } else { + /* overlaps -- expand last one */ + end = clips[clip].c.left + clips[clip].c.width; + if (skips[skip-1].end < end) + skips[skip-1].end = end; + if (skips[skip-1].end > width) + skips[skip-1].end = width; + } + } + *nskips = skip; + *maxy = maxline; + + if (btcx_debug) { + dprintk("btcx: skips line %d-%d:", line, maxline); + for (skip = 0; skip < *nskips; skip++) { + pr_cont(" %d-%d", skips[skip].start, skips[skip].end); + } + pr_cont("\n"); + } +} diff -rupN a/drivers/media/pci/bt8xx/btcx-risc.h b/drivers/media/pci/bt8xx/btcx-risc.h --- a/drivers/media/pci/bt8xx/btcx-risc.h 2023-04-27 04:46:15.574522003 +0200 +++ b/drivers/media/pci/bt8xx/btcx-risc.h 2023-04-27 04:45:11.061431009 +0200 @@ -16,3 +16,12 @@ int btcx_riscmem_alloc(struct pci_dev * unsigned int size); void btcx_riscmem_free(struct pci_dev *pci, struct btcx_riscmem *risc); + +int btcx_screen_clips(int swidth, int sheight, struct v4l2_rect *win, + struct v4l2_clip *clips, unsigned int n); +int btcx_align(struct v4l2_rect *win, struct v4l2_clip *clips, + unsigned int n, int mask); +void btcx_sort_clips(struct v4l2_clip *clips, unsigned int nclips); +void btcx_calc_skips(int line, int width, int *maxy, + struct btcx_skiplist *skips, unsigned int *nskips, + const struct v4l2_clip *clips, unsigned int nclips); diff -rupN a/drivers/media/pci/bt8xx/bttv-cards.c b/drivers/media/pci/bt8xx/bttv-cards.c --- a/drivers/media/pci/bt8xx/bttv-cards.c 2023-04-27 04:46:15.574522003 +0200 +++ b/drivers/media/pci/bt8xx/bttv-cards.c 2023-04-27 04:44:43.446820879 +0200 @@ -81,6 +81,7 @@ static int pvr_boot(struct bttv *btv); static unsigned int triton1; static unsigned int vsfx; static unsigned int latency = UNSET; +int no_overlay=-1; static unsigned int card[BTTV_MAX] = { [ 0 ... (BTTV_MAX-1) ] = UNSET }; static unsigned int pll[BTTV_MAX] = { [ 0 ... (BTTV_MAX-1) ] = UNSET }; @@ -98,6 +99,7 @@ static unsigned int audiomux[5] = { [ 0 /* insmod options */ module_param(triton1, int, 0444); module_param(vsfx, int, 0444); +module_param(no_overlay, int, 0444); module_param(latency, int, 0444); module_param(gpiomask, int, 0444); module_param(audioall, int, 0444); @@ -125,6 +127,7 @@ MODULE_PARM_DESC(audiodev, "specify audi "\t\t 2 = tda7432\n" "\t\t 3 = tvaudio"); MODULE_PARM_DESC(saa6588, "if 1, then load the saa6588 RDS module, default (0) is to use the card definition."); +MODULE_PARM_DESC(no_overlay, "allow override overlay default (0 disables, 1 enables) [some VIA/SIS chipsets are known to have problem with overlay]"); /* I2C addresses list */ @@ -4866,8 +4869,11 @@ static void gv800s_init(struct bttv *btv void __init bttv_check_chipset(void) { + int pcipci_fail = 0; struct pci_dev *dev = NULL; + if (pci_pci_problems & (PCIPCI_FAIL|PCIAGP_FAIL)) /* should check if target is AGP */ + pcipci_fail = 1; if (pci_pci_problems & (PCIPCI_TRITON|PCIPCI_NATOMA|PCIPCI_VIAETBF)) triton1 = 1; if (pci_pci_problems & PCIPCI_VSFX) @@ -4883,6 +4889,15 @@ void __init bttv_check_chipset(void) pr_info("Host bridge needs ETBF enabled\n"); if (vsfx) pr_info("Host bridge needs VSFX enabled\n"); + if (pcipci_fail) { + pr_info("bttv and your chipset may not work together\n"); + if (!no_overlay) { + pr_info("overlay will be disabled\n"); + no_overlay = 1; + } else { + pr_info("overlay forced. Use this option at your own risk.\n"); + } + } if (UNSET != latency) pr_info("pci latency fixup [%d]\n", latency); while ((dev = pci_get_device(PCI_VENDOR_ID_INTEL, diff -rupN a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c --- a/drivers/media/pci/bt8xx/bttv-driver.c 2023-04-27 04:46:15.574522003 +0200 +++ b/drivers/media/pci/bt8xx/bttv-driver.c 2023-04-27 04:44:23.737099449 +0200 @@ -49,6 +49,7 @@ #include #define BTTV_VERSION "0.9.19" +#define V4L2_FBUF_CAP_LIST_CLIPPING 0x0004 unsigned int bttv_num; /* number of Bt848s in use */ struct bttv *bttvs[BTTV_MAX]; @@ -624,14 +625,20 @@ static const unsigned int FORMATS = ARRA VIDIOC_QBUF 1) bttv_release VIDIOCMCAPTURE 1) + OVERLAY VIDIOCCAPTURE on VIDIOCCAPTURE off + VIDIOC_OVERLAY on VIDIOC_OVERLAY off + 3) bttv_release + VBI VIDIOC_STREAMON VIDIOC_STREAMOFF VIDIOC_QBUF 1) bttv_release - bttv_read, bttv_poll 1) 3) + bttv_read, bttv_poll 1) 4) 1) The resource must be allocated when we enter buffer prepare functions and remain allocated while buffers are in the DMA queue. 2) This is a single frame read. - 3) This is a continuous read, implies VIDIOC_STREAMON. + 3) VIDIOC_S_FBUF and VIDIOC_S_FMT (OVERLAY) still work when + RESOURCE_OVERLAY is allocated. + 4) This is a continuous read, implies VIDIOC_STREAMON. Note this driver permits video input and standard changes regardless if resources are allocated. @@ -639,7 +646,8 @@ static const unsigned int FORMATS = ARRA #define VBI_RESOURCES (RESOURCE_VBI) #define VIDEO_RESOURCES (RESOURCE_VIDEO_READ | \ - RESOURCE_VIDEO_STREAM) + RESOURCE_VIDEO_STREAM | \ + RESOURCE_OVERLAY) static int check_alloc_btres_lock(struct bttv *btv, struct bttv_fh *fh, int bit) @@ -1485,6 +1493,37 @@ format_by_fourcc(int fourcc) } /* ----------------------------------------------------------------------- */ +/* misc helpers */ + +static int +bttv_switch_overlay(struct bttv *btv, struct bttv_fh *fh, + struct bttv_buffer *new) +{ + struct bttv_buffer *old; + unsigned long flags; + + dprintk("switch_overlay: enter [new=%p]\n", new); + if (new) + new->vb.state = VIDEOBUF_DONE; + spin_lock_irqsave(&btv->s_lock,flags); + old = btv->screen; + btv->screen = new; + btv->loop_irq |= 1; + bttv_set_dma(btv, 0x03); + spin_unlock_irqrestore(&btv->s_lock,flags); + if (NULL != old) { + dprintk("switch_overlay: old=%p state is %d\n", + old, old->vb.state); + bttv_dma_free(&fh->cap,btv, old); + kfree(old); + } + if (NULL == new) + free_btres_lock(btv,fh,RESOURCE_OVERLAY); + dprintk("switch_overlay: done\n"); + return 0; +} + +/* ----------------------------------------------------------------------- */ /* video4linux (1) interface */ static int bttv_prepare_buffer(struct videobuf_queue *q,struct bttv *btv, @@ -2007,6 +2046,150 @@ limit_scaled_size_lock (struct btt return rc; } +/* Returns an error if the given overlay window dimensions are not + possible with the current cropping parameters. If adjust_size is + TRUE the function may adjust the window width and/or height + instead, however it always rounds the horizontal position and + width as btcx_align() does. If adjust_crop is TRUE the function + may also adjust the current cropping parameters to get closer + to the desired window size. */ +static int +verify_window_lock(struct bttv_fh *fh, struct v4l2_window *win, + int adjust_size, int adjust_crop) +{ + enum v4l2_field field; + unsigned int width_mask; + + if (win->w.width < 48) + win->w.width = 48; + if (win->w.height < 32) + win->w.height = 32; + if (win->clipcount > 2048) + win->clipcount = 2048; + + win->chromakey = 0; + win->global_alpha = 0; + field = win->field; + + switch (field) { + case V4L2_FIELD_TOP: + case V4L2_FIELD_BOTTOM: + case V4L2_FIELD_INTERLACED: + break; + default: + field = V4L2_FIELD_ANY; + break; + } + if (V4L2_FIELD_ANY == field) { + __s32 height2; + + height2 = fh->btv->crop[!!fh->do_crop].rect.height >> 1; + field = (win->w.height > height2) + ? V4L2_FIELD_INTERLACED + : V4L2_FIELD_TOP; + } + win->field = field; + + if (NULL == fh->ovfmt) + return -EINVAL; + /* 4-byte alignment. */ + width_mask = ~0; + switch (fh->ovfmt->depth) { + case 8: + case 24: + width_mask = ~3; + break; + case 16: + width_mask = ~1; + break; + case 32: + break; + default: + BUG(); + } + + win->w.width -= win->w.left & ~width_mask; + win->w.left = (win->w.left - width_mask - 1) & width_mask; + + return limit_scaled_size_lock(fh, &win->w.width, &win->w.height, + field, width_mask, + /* width_bias: round down */ 0, + adjust_size, adjust_crop); +} + +static int setup_window_lock(struct bttv_fh *fh, struct bttv *btv, + struct v4l2_window *win, int fixup) +{ + struct v4l2_clip *clips = NULL; + int n,size,retval = 0; + + if (NULL == fh->ovfmt) + return -EINVAL; + if (!(fh->ovfmt->flags & FORMAT_FLAGS_PACKED)) + return -EINVAL; + retval = verify_window_lock(fh, win, + /* adjust_size */ fixup, + /* adjust_crop */ fixup); + if (0 != retval) + return retval; + + /* copy clips -- luckily v4l1 + v4l2 are binary + compatible here ...*/ + n = win->clipcount; + size = sizeof(*clips)*(n+4); + clips = kmalloc(size,GFP_KERNEL); + if (NULL == clips) + return -ENOMEM; + if (n > 0) + memcpy(clips, win->clips, sizeof(struct v4l2_clip) * n); + + /* clip against screen */ + if (NULL != btv->fbuf.base) + n = btcx_screen_clips(btv->fbuf.fmt.width, btv->fbuf.fmt.height, + &win->w, clips, n); + btcx_sort_clips(clips,n); + + /* 4-byte alignments */ + switch (fh->ovfmt->depth) { + case 8: + case 24: + btcx_align(&win->w, clips, n, 3); + break; + case 16: + btcx_align(&win->w, clips, n, 1); + break; + case 32: + /* no alignment fixups needed */ + break; + default: + BUG(); + } + + kfree(fh->ov.clips); + fh->ov.clips = clips; + fh->ov.nclips = n; + + fh->ov.w = win->w; + fh->ov.field = win->field; + fh->ov.setup_ok = 1; + + btv->init.ov.w.width = win->w.width; + btv->init.ov.w.height = win->w.height; + btv->init.ov.field = win->field; + + /* update overlay if needed */ + retval = 0; + if (check_btres(fh, RESOURCE_OVERLAY)) { + struct bttv_buffer *new; + + new = videobuf_sg_alloc(sizeof(*new)); + new->crop = btv->crop[!!fh->do_crop].rect; + bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new); + retval = bttv_switch_overlay(btv,fh,new); + } + return retval; +} + /* ----------------------------------------------------------------------- */ static struct videobuf_queue* bttv_queue(struct bttv_fh *fh) @@ -2088,6 +2271,17 @@ static int bttv_g_fmt_vid_cap(struct fil return 0; } +static int bttv_g_fmt_vid_overlay(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct bttv_fh *fh = priv; + + f->fmt.win.w = fh->ov.w; + f->fmt.win.field = fh->ov.field; + + return 0; +} + static void bttv_get_width_mask_vid_cap(const struct bttv_format *fmt, unsigned int *width_mask, unsigned int *width_bias) @@ -2159,6 +2353,17 @@ static int bttv_try_fmt_vid_cap(struct f return 0; } +static int bttv_try_fmt_vid_overlay(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct bttv_fh *fh = priv; + + verify_window_lock(fh, &f->fmt.win, + /* adjust_size */ 1, + /* adjust_crop */ 0); + return 0; +} + static int bttv_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { @@ -2206,6 +2411,20 @@ static int bttv_s_fmt_vid_cap(struct fil return 0; } +static int bttv_s_fmt_vid_overlay(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct bttv_fh *fh = priv; + struct bttv *btv = fh->btv; + + if (no_overlay > 0) { + pr_err("V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n"); + return -EINVAL; + } + + return setup_window_lock(fh, btv, &f->fmt.win, 1); +} + static int bttv_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { @@ -2219,6 +2438,8 @@ static int bttv_querycap(struct file *fi strscpy(cap->card, btv->video_dev.name, sizeof(cap->card)); cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING | V4L2_CAP_DEVICE_CAPS; + if (no_overlay <= 0) + cap->capabilities |= V4L2_CAP_VIDEO_OVERLAY; if (video_is_registered(&btv->vbi_dev)) cap->capabilities |= V4L2_CAP_VBI_CAPTURE; if (video_is_registered(&btv->radio_dev)) { @@ -2238,8 +2459,7 @@ static int bttv_querycap(struct file *fi return 0; } -static int bttv_enum_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) +static int bttv_enum_fmt_cap_ovr(struct v4l2_fmtdesc *f) { int index = -1, i; @@ -2254,9 +2474,162 @@ static int bttv_enum_fmt_vid_cap(struct f->pixelformat = formats[i].fourcc; + return i; +} + +static int bttv_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + int rc = bttv_enum_fmt_cap_ovr(f); + + if (rc < 0) + return rc; + + return 0; +} + +static int bttv_enum_fmt_vid_overlay(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + int rc; + + if (no_overlay > 0) { + pr_err("V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n"); + return -EINVAL; + } + + rc = bttv_enum_fmt_cap_ovr(f); + + if (rc < 0) + return rc; + + if (!(formats[rc].flags & FORMAT_FLAGS_PACKED)) + return -EINVAL; + + return 0; +} + +static int bttv_g_fbuf(struct file *file, void *f, + struct v4l2_framebuffer *fb) +{ + struct bttv_fh *fh = f; + struct bttv *btv = fh->btv; + + *fb = btv->fbuf; + fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING; + fb->flags = V4L2_FBUF_FLAG_PRIMARY; + if (fh->ovfmt) + fb->fmt.pixelformat = fh->ovfmt->fourcc; return 0; } +static int bttv_overlay(struct file *file, void *f, unsigned int on) +{ + struct bttv_fh *fh = f; + struct bttv *btv = fh->btv; + struct bttv_buffer *new; + int retval = 0; + + if (on) { + /* verify args */ + if (unlikely(!btv->fbuf.base)) { + return -EINVAL; + } + if (unlikely(!fh->ov.setup_ok)) { + dprintk("%d: overlay: !setup_ok\n", btv->c.nr); + retval = -EINVAL; + } + if (retval) + return retval; + } + + if (!check_alloc_btres_lock(btv, fh, RESOURCE_OVERLAY)) + return -EBUSY; + + if (on) { + fh->ov.tvnorm = btv->tvnorm; + new = videobuf_sg_alloc(sizeof(*new)); + new->crop = btv->crop[!!fh->do_crop].rect; + bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new); + } else { + new = NULL; + } + + /* switch over */ + retval = bttv_switch_overlay(btv, fh, new); + return retval; +} + +static int bttv_s_fbuf(struct file *file, void *f, + const struct v4l2_framebuffer *fb) +{ + struct bttv_fh *fh = f; + struct bttv *btv = fh->btv; + const struct bttv_format *fmt; + int retval; + + if (!capable(CAP_SYS_ADMIN) && + !capable(CAP_SYS_RAWIO)) + return -EPERM; + + /* check args */ + fmt = format_by_fourcc(fb->fmt.pixelformat); + if (NULL == fmt) + return -EINVAL; + if (0 == (fmt->flags & FORMAT_FLAGS_PACKED)) + return -EINVAL; + + retval = -EINVAL; + if (fb->flags & V4L2_FBUF_FLAG_OVERLAY) { + __s32 width = fb->fmt.width; + __s32 height = fb->fmt.height; + + retval = limit_scaled_size_lock(fh, &width, &height, + V4L2_FIELD_INTERLACED, + /* width_mask */ ~3, + /* width_bias */ 2, + /* adjust_size */ 0, + /* adjust_crop */ 0); + if (0 != retval) + return retval; + } + + /* ok, accept it */ + btv->fbuf.base = fb->base; + btv->fbuf.fmt.width = fb->fmt.width; + btv->fbuf.fmt.height = fb->fmt.height; + if (0 != fb->fmt.bytesperline) + btv->fbuf.fmt.bytesperline = fb->fmt.bytesperline; + else + btv->fbuf.fmt.bytesperline = btv->fbuf.fmt.width*fmt->depth/8; + + retval = 0; + fh->ovfmt = fmt; + btv->init.ovfmt = fmt; + if (fb->flags & V4L2_FBUF_FLAG_OVERLAY) { + fh->ov.w.left = 0; + fh->ov.w.top = 0; + fh->ov.w.width = fb->fmt.width; + fh->ov.w.height = fb->fmt.height; + btv->init.ov.w.width = fb->fmt.width; + btv->init.ov.w.height = fb->fmt.height; + + kfree(fh->ov.clips); + fh->ov.clips = NULL; + fh->ov.nclips = 0; + + if (check_btres(fh, RESOURCE_OVERLAY)) { + struct bttv_buffer *new; + + new = videobuf_sg_alloc(sizeof(*new)); + new->crop = btv->crop[!!fh->do_crop].rect; + bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new); + retval = bttv_switch_overlay(btv, fh, new); + } + } + return retval; +} + static int bttv_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *p) { @@ -2376,7 +2749,8 @@ static int bttv_g_selection(struct file struct bttv_fh *fh = f; struct bttv *btv = fh->btv; - if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + sel->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) return -EINVAL; switch (sel->target) { @@ -2413,7 +2787,8 @@ static int bttv_s_selection(struct file __s32 b_right; __s32 b_bottom; - if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + sel->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) return -EINVAL; if (sel->target != V4L2_SEL_TGT_CROP) @@ -2603,6 +2978,7 @@ static int bttv_open(struct file *file) v4l2_fh_init(&fh->fh, vdev); fh->type = type; + fh->ov.setup_ok = 0; videobuf_queue_sg_init(&fh->cap, &bttv_video_qops, &btv->c.pci->dev, &btv->s_lock, @@ -2646,6 +3022,10 @@ static int bttv_release(struct file *fil struct bttv_fh *fh = file->private_data; struct bttv *btv = fh->btv; + /* turn off overlay */ + if (check_btres(fh, RESOURCE_OVERLAY)) + bttv_switch_overlay(btv,fh,NULL); + /* stop video capture */ if (check_btres(fh, RESOURCE_VIDEO_STREAM)) { videobuf_streamoff(&fh->cap); @@ -2711,6 +3091,10 @@ static const struct v4l2_ioctl_ops bttv_ .vidioc_g_fmt_vid_cap = bttv_g_fmt_vid_cap, .vidioc_try_fmt_vid_cap = bttv_try_fmt_vid_cap, .vidioc_s_fmt_vid_cap = bttv_s_fmt_vid_cap, + .vidioc_enum_fmt_vid_overlay = bttv_enum_fmt_vid_overlay, + .vidioc_g_fmt_vid_overlay = bttv_g_fmt_vid_overlay, + .vidioc_try_fmt_vid_overlay = bttv_try_fmt_vid_overlay, + .vidioc_s_fmt_vid_overlay = bttv_s_fmt_vid_overlay, .vidioc_g_fmt_vbi_cap = bttv_g_fmt_vbi_cap, .vidioc_try_fmt_vbi_cap = bttv_try_fmt_vbi_cap, .vidioc_s_fmt_vbi_cap = bttv_s_fmt_vbi_cap, @@ -2730,6 +3114,9 @@ static const struct v4l2_ioctl_ops bttv_ .vidioc_s_tuner = bttv_s_tuner, .vidioc_g_selection = bttv_g_selection, .vidioc_s_selection = bttv_s_selection, + .vidioc_g_fbuf = bttv_g_fbuf, + .vidioc_s_fbuf = bttv_s_fbuf, + .vidioc_overlay = bttv_overlay, .vidioc_g_parm = bttv_g_parm, .vidioc_g_frequency = bttv_g_frequency, .vidioc_s_frequency = bttv_s_frequency, @@ -2999,6 +3386,9 @@ static void bttv_print_riscaddr(struct b ? (unsigned long long)btv->curr.top->top.dma : 0, btv->curr.bottom ? (unsigned long long)btv->curr.bottom->bottom.dma : 0); + pr_info(" scr : o=%08llx e=%08llx\n", + btv->screen ? (unsigned long long)btv->screen->top.dma : 0, + btv->screen ? (unsigned long long)btv->screen->bottom.dma : 0); bttv_risc_disasm(btv, &btv->main); } @@ -3119,9 +3509,28 @@ bttv_irq_next_video(struct bttv *btv, st } } - dprintk("%d: next set: top=%p bottom=%p [irq=%d,%d]\n", + /* screen overlay ? */ + if (NULL != btv->screen) { + if (V4L2_FIELD_HAS_BOTH(btv->screen->vb.field)) { + if (NULL == set->top && NULL == set->bottom) { + set->top = btv->screen; + set->bottom = btv->screen; + } + } else { + if (V4L2_FIELD_TOP == btv->screen->vb.field && + NULL == set->top) { + set->top = btv->screen; + } + if (V4L2_FIELD_BOTTOM == btv->screen->vb.field && + NULL == set->bottom) { + set->bottom = btv->screen; + } + } + } + + dprintk("%d: next set: top=%p bottom=%p [screen=%p,irq=%d,%d]\n", btv->c.nr, set->top, set->bottom, - set->frame_irq, set->top_irq); + btv->screen, set->frame_irq, set->top_irq); return 0; } @@ -3475,12 +3884,17 @@ static void bttv_unregister_video(struct /* register video4linux devices */ static int bttv_register_video(struct bttv *btv) { + if (no_overlay > 0) + pr_notice("Overlay support disabled\n"); + /* video */ vdev_init(btv, &btv->video_dev, &bttv_video_template, "video"); btv->video_dev.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; if (btv->tuner_type != TUNER_ABSENT) btv->video_dev.device_caps |= V4L2_CAP_TUNER; + if (no_overlay <= 0) + btv->video_dev.device_caps |= V4L2_CAP_VIDEO_OVERLAY; if (video_register_device(&btv->video_dev, VFL_TYPE_VIDEO, video_nr[btv->c.nr]) < 0) @@ -3671,9 +4085,14 @@ static int bttv_probe(struct pci_dev *de /* fill struct bttv with some useful defaults */ btv->init.btv = btv; + btv->init.ov.w.width = 320; + btv->init.ov.w.height = 240; btv->init.fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24); btv->init.width = 320; btv->init.height = 240; + btv->init.ov.w.width = 320; + btv->init.ov.w.height = 240; + btv->init.ov.field = V4L2_FIELD_INTERLACED; btv->input = 0; v4l2_ctrl_new_std(hdl, &bttv_ctrl_ops, diff -rupN a/drivers/media/pci/bt8xx/bttv-risc.c b/drivers/media/pci/bt8xx/bttv-risc.c --- a/drivers/media/pci/bt8xx/bttv-risc.c 2023-04-27 04:46:15.574522003 +0200 +++ b/drivers/media/pci/bt8xx/bttv-risc.c 2023-04-27 04:43:56.240488522 +0200 @@ -231,6 +231,95 @@ bttv_risc_planar(struct bttv *btv, struc return 0; } +static int +bttv_risc_overlay(struct bttv *btv, struct btcx_riscmem *risc, + const struct bttv_format *fmt, struct bttv_overlay *ov, + int skip_even, int skip_odd) +{ + int dwords, rc, line, maxy, start, end; + unsigned skip, nskips; + struct btcx_skiplist *skips; + __le32 *rp; + u32 ri,ra; + u32 addr; + + /* skip list for window clipping */ + skips = kmalloc_array(ov->nclips, sizeof(*skips),GFP_KERNEL); + if (NULL == skips) + return -ENOMEM; + + /* estimate risc mem: worst case is (1.5*clip+1) * lines instructions + + sync + jump (all 2 dwords) */ + dwords = (3 * ov->nclips + 2) * + ((skip_even || skip_odd) ? (ov->w.height+1)>>1 : ov->w.height); + dwords += 4; + if ((rc = btcx_riscmem_alloc(btv->c.pci,risc,dwords*4)) < 0) { + kfree(skips); + return rc; + } + + /* sync instruction */ + rp = risc->cpu; + *(rp++) = cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1); + *(rp++) = cpu_to_le32(0); + + addr = (unsigned long)btv->fbuf.base; + addr += btv->fbuf.fmt.bytesperline * ov->w.top; + addr += (fmt->depth >> 3) * ov->w.left; + + /* scan lines */ + for (maxy = -1, line = 0; line < ov->w.height; + line++, addr += btv->fbuf.fmt.bytesperline) { + if ((btv->opt_vcr_hack) && + (line >= (ov->w.height - VCR_HACK_LINES))) + continue; + if ((line%2) == 0 && skip_even) + continue; + if ((line%2) == 1 && skip_odd) + continue; + + /* calculate clipping */ + if (line > maxy) + btcx_calc_skips(line, ov->w.width, &maxy, + skips, &nskips, ov->clips, ov->nclips); + + /* write out risc code */ + for (start = 0, skip = 0; start < ov->w.width; start = end) { + if (skip >= nskips) { + ri = BT848_RISC_WRITE; + end = ov->w.width; + } else if (start < skips[skip].start) { + ri = BT848_RISC_WRITE; + end = skips[skip].start; + } else { + ri = BT848_RISC_SKIP; + end = skips[skip].end; + skip++; + } + if (BT848_RISC_WRITE == ri) + ra = addr + (fmt->depth>>3)*start; + else + ra = 0; + + if (0 == start) + ri |= BT848_RISC_SOL; + if (ov->w.width == end) + ri |= BT848_RISC_EOL; + ri |= (fmt->depth>>3) * (end-start); + + *(rp++)=cpu_to_le32(ri); + if (0 != ra) + *(rp++)=cpu_to_le32(ra); + } + } + + /* save pointer to jmp instruction address */ + risc->jmp = rp; + BUG_ON((risc->jmp - risc->cpu + 2) * sizeof(*risc->cpu) > risc->size); + kfree(skips); + return 0; +} + /* ---------------------------------------------------------- */ static void @@ -759,3 +848,45 @@ bttv_buffer_risc(struct bttv *btv, struc buf->btswap = buf->fmt->btswap; return 0; } + +/* ---------------------------------------------------------- */ + +/* calculate geometry, build risc code */ +int +bttv_overlay_risc(struct bttv *btv, + struct bttv_overlay *ov, + const struct bttv_format *fmt, + struct bttv_buffer *buf) +{ + /* check interleave, bottom+top fields */ + dprintk("%d: overlay fields: %s format: 0x%08x size: %dx%d\n", + btv->c.nr, v4l2_field_names[buf->vb.field], + fmt->fourcc, ov->w.width, ov->w.height); + + /* calculate geometry */ + bttv_calc_geo(btv,&buf->geo,ov->w.width,ov->w.height, + V4L2_FIELD_HAS_BOTH(ov->field), + &bttv_tvnorms[ov->tvnorm],&buf->crop); + + /* build risc code */ + switch (ov->field) { + case V4L2_FIELD_TOP: + bttv_risc_overlay(btv, &buf->top, fmt, ov, 0, 0); + break; + case V4L2_FIELD_BOTTOM: + bttv_risc_overlay(btv, &buf->bottom, fmt, ov, 0, 0); + break; + case V4L2_FIELD_INTERLACED: + bttv_risc_overlay(btv, &buf->top, fmt, ov, 0, 1); + bttv_risc_overlay(btv, &buf->bottom, fmt, ov, 1, 0); + break; + default: + BUG(); + } + + /* copy format info */ + buf->btformat = fmt->btformat; + buf->btswap = fmt->btswap; + buf->vb.field = ov->field; + return 0; +} diff -rupN a/drivers/media/pci/bt8xx/bttvp.h b/drivers/media/pci/bt8xx/bttvp.h --- a/drivers/media/pci/bt8xx/bttvp.h 2023-04-27 04:46:15.574522003 +0200 +++ b/drivers/media/pci/bt8xx/bttvp.h 2023-04-27 04:43:39.128730924 +0200 @@ -50,6 +50,7 @@ #define RISC_SLOT_E_FIELD 12 #define RISC_SLOT_LOOP 14 +#define RESOURCE_OVERLAY 1 #define RESOURCE_VIDEO_STREAM 2 #define RESOURCE_VBI 4 #define RESOURCE_VIDEO_READ 8 @@ -164,6 +165,15 @@ struct bttv_buffer_set { unsigned int frame_irq; }; +struct bttv_overlay { + unsigned int tvnorm; + struct v4l2_rect w; + enum v4l2_field field; + struct v4l2_clip *clips; + int nclips; + int setup_ok; +}; + struct bttv_vbi_fmt { struct v4l2_vbi_format fmt; @@ -206,6 +216,10 @@ struct bttv_fh { int width; int height; + /* video overlay */ + const struct bttv_format *ovfmt; + struct bttv_overlay ov; + /* Application called VIDIOC_S_SELECTION. */ int do_crop; @@ -242,6 +256,12 @@ int bttv_buffer_activate_vbi(struct bttv void bttv_dma_free(struct videobuf_queue *q, struct bttv *btv, struct bttv_buffer *buf); +/* overlay handling */ +int bttv_overlay_risc(struct bttv *btv, struct bttv_overlay *ov, + const struct bttv_format *fmt, + struct bttv_buffer *buf); + + /* ---------------------------------------------------------- */ /* bttv-vbi.c */ @@ -259,6 +279,11 @@ int bttv_sub_add_device(struct bttv_core int bttv_sub_del_devices(struct bttv_core *core); /* ---------------------------------------------------------- */ +/* bttv-cards.c */ + +extern int no_overlay; + +/* ---------------------------------------------------------- */ /* bttv-input.c */ extern void init_bttv_i2c_ir(struct bttv *btv); @@ -429,6 +454,7 @@ struct bttv { - must acquire s_lock before changing these - only the irq handler is supported to touch top + bottom + vcurr */ struct btcx_riscmem main; + struct bttv_buffer *screen; /* overlay */ struct list_head capture; /* video capture queue */ struct list_head vcapture; /* vbi capture queue */ struct bttv_buffer_set curr; /* active buffers */ @@ -453,7 +479,7 @@ struct bttv { /* used to make dvb-bt8xx autoloadable */ struct work_struct request_module_wk; - /* Default (0) and current (1) video capturing + /* Default (0) and current (1) video capturing and overlay cropping parameters in bttv_tvnorm.cropcap units. Protected by bttv.lock. */ struct bttv_crop crop[2];