18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci#include <media/drv-intf/saa7146_vv.h> 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_cistatic int vbi_pixel_to_capture = 720 * 2; 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_cistatic int vbi_workaround(struct saa7146_dev *dev) 78c2ecf20Sopenharmony_ci{ 88c2ecf20Sopenharmony_ci struct saa7146_vv *vv = dev->vv_data; 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci u32 *cpu; 118c2ecf20Sopenharmony_ci dma_addr_t dma_addr; 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci int count = 0; 148c2ecf20Sopenharmony_ci int i; 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci DECLARE_WAITQUEUE(wait, current); 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci DEB_VBI("dev:%p\n", dev); 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci /* once again, a bug in the saa7146: the brs acquisition 218c2ecf20Sopenharmony_ci is buggy and especially the BXO-counter does not work 228c2ecf20Sopenharmony_ci as specified. there is this workaround, but please 238c2ecf20Sopenharmony_ci don't let me explain it. ;-) */ 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci cpu = pci_alloc_consistent(dev->pci, 4096, &dma_addr); 268c2ecf20Sopenharmony_ci if (NULL == cpu) 278c2ecf20Sopenharmony_ci return -ENOMEM; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci /* setup some basic programming, just for the workaround */ 308c2ecf20Sopenharmony_ci saa7146_write(dev, BASE_EVEN3, dma_addr); 318c2ecf20Sopenharmony_ci saa7146_write(dev, BASE_ODD3, dma_addr+vbi_pixel_to_capture); 328c2ecf20Sopenharmony_ci saa7146_write(dev, PROT_ADDR3, dma_addr+4096); 338c2ecf20Sopenharmony_ci saa7146_write(dev, PITCH3, vbi_pixel_to_capture); 348c2ecf20Sopenharmony_ci saa7146_write(dev, BASE_PAGE3, 0x0); 358c2ecf20Sopenharmony_ci saa7146_write(dev, NUM_LINE_BYTE3, (2<<16)|((vbi_pixel_to_capture)<<0)); 368c2ecf20Sopenharmony_ci saa7146_write(dev, MC2, MASK_04|MASK_20); 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci /* load brs-control register */ 398c2ecf20Sopenharmony_ci WRITE_RPS1(CMD_WR_REG | (1 << 8) | (BRS_CTRL/4)); 408c2ecf20Sopenharmony_ci /* BXO = 1h, BRS to outbound */ 418c2ecf20Sopenharmony_ci WRITE_RPS1(0xc000008c); 428c2ecf20Sopenharmony_ci /* wait for vbi_a or vbi_b*/ 438c2ecf20Sopenharmony_ci if ( 0 != (SAA7146_USE_PORT_B_FOR_VBI & dev->ext_vv_data->flags)) { 448c2ecf20Sopenharmony_ci DEB_D("...using port b\n"); 458c2ecf20Sopenharmony_ci WRITE_RPS1(CMD_PAUSE | CMD_OAN | CMD_SIG1 | CMD_E_FID_B); 468c2ecf20Sopenharmony_ci WRITE_RPS1(CMD_PAUSE | CMD_OAN | CMD_SIG1 | CMD_O_FID_B); 478c2ecf20Sopenharmony_ci/* 488c2ecf20Sopenharmony_ci WRITE_RPS1(CMD_PAUSE | MASK_09); 498c2ecf20Sopenharmony_ci*/ 508c2ecf20Sopenharmony_ci } else { 518c2ecf20Sopenharmony_ci DEB_D("...using port a\n"); 528c2ecf20Sopenharmony_ci WRITE_RPS1(CMD_PAUSE | MASK_10); 538c2ecf20Sopenharmony_ci } 548c2ecf20Sopenharmony_ci /* upload brs */ 558c2ecf20Sopenharmony_ci WRITE_RPS1(CMD_UPLOAD | MASK_08); 568c2ecf20Sopenharmony_ci /* load brs-control register */ 578c2ecf20Sopenharmony_ci WRITE_RPS1(CMD_WR_REG | (1 << 8) | (BRS_CTRL/4)); 588c2ecf20Sopenharmony_ci /* BYO = 1, BXO = NQBIL (=1728 for PAL, for NTSC this is 858*2) - NumByte3 (=1440) = 288 */ 598c2ecf20Sopenharmony_ci WRITE_RPS1(((1728-(vbi_pixel_to_capture)) << 7) | MASK_19); 608c2ecf20Sopenharmony_ci /* wait for brs_done */ 618c2ecf20Sopenharmony_ci WRITE_RPS1(CMD_PAUSE | MASK_08); 628c2ecf20Sopenharmony_ci /* upload brs */ 638c2ecf20Sopenharmony_ci WRITE_RPS1(CMD_UPLOAD | MASK_08); 648c2ecf20Sopenharmony_ci /* load video-dma3 NumLines3 and NumBytes3 */ 658c2ecf20Sopenharmony_ci WRITE_RPS1(CMD_WR_REG | (1 << 8) | (NUM_LINE_BYTE3/4)); 668c2ecf20Sopenharmony_ci /* dev->vbi_count*2 lines, 720 pixel (= 1440 Bytes) */ 678c2ecf20Sopenharmony_ci WRITE_RPS1((2 << 16) | (vbi_pixel_to_capture)); 688c2ecf20Sopenharmony_ci /* load brs-control register */ 698c2ecf20Sopenharmony_ci WRITE_RPS1(CMD_WR_REG | (1 << 8) | (BRS_CTRL/4)); 708c2ecf20Sopenharmony_ci /* Set BRS right: note: this is an experimental value for BXO (=> PAL!) */ 718c2ecf20Sopenharmony_ci WRITE_RPS1((540 << 7) | (5 << 19)); // 5 == vbi_start 728c2ecf20Sopenharmony_ci /* wait for brs_done */ 738c2ecf20Sopenharmony_ci WRITE_RPS1(CMD_PAUSE | MASK_08); 748c2ecf20Sopenharmony_ci /* upload brs and video-dma3*/ 758c2ecf20Sopenharmony_ci WRITE_RPS1(CMD_UPLOAD | MASK_08 | MASK_04); 768c2ecf20Sopenharmony_ci /* load mc2 register: enable dma3 */ 778c2ecf20Sopenharmony_ci WRITE_RPS1(CMD_WR_REG | (1 << 8) | (MC1/4)); 788c2ecf20Sopenharmony_ci WRITE_RPS1(MASK_20 | MASK_04); 798c2ecf20Sopenharmony_ci /* generate interrupt */ 808c2ecf20Sopenharmony_ci WRITE_RPS1(CMD_INTERRUPT); 818c2ecf20Sopenharmony_ci /* stop rps1 */ 828c2ecf20Sopenharmony_ci WRITE_RPS1(CMD_STOP); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci /* we have to do the workaround twice to be sure that 858c2ecf20Sopenharmony_ci everything is ok */ 868c2ecf20Sopenharmony_ci for(i = 0; i < 2; i++) { 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci /* indicate to the irq handler that we do the workaround */ 898c2ecf20Sopenharmony_ci saa7146_write(dev, MC2, MASK_31|MASK_15); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci saa7146_write(dev, NUM_LINE_BYTE3, (1<<16)|(2<<0)); 928c2ecf20Sopenharmony_ci saa7146_write(dev, MC2, MASK_04|MASK_20); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci /* enable rps1 irqs */ 958c2ecf20Sopenharmony_ci SAA7146_IER_ENABLE(dev,MASK_28); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci /* prepare to wait to be woken up by the irq-handler */ 988c2ecf20Sopenharmony_ci add_wait_queue(&vv->vbi_wq, &wait); 998c2ecf20Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci /* start rps1 to enable workaround */ 1028c2ecf20Sopenharmony_ci saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle); 1038c2ecf20Sopenharmony_ci saa7146_write(dev, MC1, (MASK_13 | MASK_29)); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci schedule(); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci DEB_VBI("brs bug workaround %d/1\n", i); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci remove_wait_queue(&vv->vbi_wq, &wait); 1108c2ecf20Sopenharmony_ci __set_current_state(TASK_RUNNING); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci /* disable rps1 irqs */ 1138c2ecf20Sopenharmony_ci SAA7146_IER_DISABLE(dev,MASK_28); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci /* stop video-dma3 */ 1168c2ecf20Sopenharmony_ci saa7146_write(dev, MC1, MASK_20); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci if(signal_pending(current)) { 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci DEB_VBI("aborted (rps:0x%08x)\n", 1218c2ecf20Sopenharmony_ci saa7146_read(dev, RPS_ADDR1)); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci /* stop rps1 for sure */ 1248c2ecf20Sopenharmony_ci saa7146_write(dev, MC1, MASK_29); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci pci_free_consistent(dev->pci, 4096, cpu, dma_addr); 1278c2ecf20Sopenharmony_ci return -EINTR; 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci pci_free_consistent(dev->pci, 4096, cpu, dma_addr); 1328c2ecf20Sopenharmony_ci return 0; 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistatic void saa7146_set_vbi_capture(struct saa7146_dev *dev, struct saa7146_buf *buf, struct saa7146_buf *next) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci struct saa7146_vv *vv = dev->vv_data; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci struct saa7146_video_dma vdma3; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci int count = 0; 1428c2ecf20Sopenharmony_ci unsigned long e_wait = vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? CMD_E_FID_A : CMD_E_FID_B; 1438c2ecf20Sopenharmony_ci unsigned long o_wait = vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? CMD_O_FID_A : CMD_O_FID_B; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci/* 1468c2ecf20Sopenharmony_ci vdma3.base_even = 0xc8000000+2560*70; 1478c2ecf20Sopenharmony_ci vdma3.base_odd = 0xc8000000; 1488c2ecf20Sopenharmony_ci vdma3.prot_addr = 0xc8000000+2560*164; 1498c2ecf20Sopenharmony_ci vdma3.pitch = 2560; 1508c2ecf20Sopenharmony_ci vdma3.base_page = 0; 1518c2ecf20Sopenharmony_ci vdma3.num_line_byte = (64<<16)|((vbi_pixel_to_capture)<<0); // set above! 1528c2ecf20Sopenharmony_ci*/ 1538c2ecf20Sopenharmony_ci vdma3.base_even = buf->pt[2].offset; 1548c2ecf20Sopenharmony_ci vdma3.base_odd = buf->pt[2].offset + 16 * vbi_pixel_to_capture; 1558c2ecf20Sopenharmony_ci vdma3.prot_addr = buf->pt[2].offset + 16 * 2 * vbi_pixel_to_capture; 1568c2ecf20Sopenharmony_ci vdma3.pitch = vbi_pixel_to_capture; 1578c2ecf20Sopenharmony_ci vdma3.base_page = buf->pt[2].dma | ME1; 1588c2ecf20Sopenharmony_ci vdma3.num_line_byte = (16 << 16) | vbi_pixel_to_capture; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci saa7146_write_out_dma(dev, 3, &vdma3); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci /* write beginning of rps-program */ 1638c2ecf20Sopenharmony_ci count = 0; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci /* wait for o_fid_a/b / e_fid_a/b toggle only if bit 1 is not set */ 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci /* we don't wait here for the first field anymore. this is different from the video 1688c2ecf20Sopenharmony_ci capture and might cause that the first buffer is only half filled (with only 1698c2ecf20Sopenharmony_ci one field). but since this is some sort of streaming data, this is not that negative. 1708c2ecf20Sopenharmony_ci but by doing this, we can use the whole engine from videobuf-dma-sg.c... */ 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci/* 1738c2ecf20Sopenharmony_ci WRITE_RPS1(CMD_PAUSE | CMD_OAN | CMD_SIG1 | e_wait); 1748c2ecf20Sopenharmony_ci WRITE_RPS1(CMD_PAUSE | CMD_OAN | CMD_SIG1 | o_wait); 1758c2ecf20Sopenharmony_ci*/ 1768c2ecf20Sopenharmony_ci /* set bit 1 */ 1778c2ecf20Sopenharmony_ci WRITE_RPS1(CMD_WR_REG | (1 << 8) | (MC2/4)); 1788c2ecf20Sopenharmony_ci WRITE_RPS1(MASK_28 | MASK_12); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci /* turn on video-dma3 */ 1818c2ecf20Sopenharmony_ci WRITE_RPS1(CMD_WR_REG_MASK | (MC1/4)); 1828c2ecf20Sopenharmony_ci WRITE_RPS1(MASK_04 | MASK_20); /* => mask */ 1838c2ecf20Sopenharmony_ci WRITE_RPS1(MASK_04 | MASK_20); /* => values */ 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci /* wait for o_fid_a/b / e_fid_a/b toggle */ 1868c2ecf20Sopenharmony_ci WRITE_RPS1(CMD_PAUSE | o_wait); 1878c2ecf20Sopenharmony_ci WRITE_RPS1(CMD_PAUSE | e_wait); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci /* generate interrupt */ 1908c2ecf20Sopenharmony_ci WRITE_RPS1(CMD_INTERRUPT); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci /* stop */ 1938c2ecf20Sopenharmony_ci WRITE_RPS1(CMD_STOP); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci /* enable rps1 irqs */ 1968c2ecf20Sopenharmony_ci SAA7146_IER_ENABLE(dev, MASK_28); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci /* write the address of the rps-program */ 1998c2ecf20Sopenharmony_ci saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci /* turn on rps */ 2028c2ecf20Sopenharmony_ci saa7146_write(dev, MC1, (MASK_13 | MASK_29)); 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic int buffer_activate(struct saa7146_dev *dev, 2068c2ecf20Sopenharmony_ci struct saa7146_buf *buf, 2078c2ecf20Sopenharmony_ci struct saa7146_buf *next) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci struct saa7146_vv *vv = dev->vv_data; 2108c2ecf20Sopenharmony_ci buf->vb.state = VIDEOBUF_ACTIVE; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci DEB_VBI("dev:%p, buf:%p, next:%p\n", dev, buf, next); 2138c2ecf20Sopenharmony_ci saa7146_set_vbi_capture(dev,buf,next); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci mod_timer(&vv->vbi_dmaq.timeout, jiffies+BUFFER_TIMEOUT); 2168c2ecf20Sopenharmony_ci return 0; 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,enum v4l2_field field) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci struct file *file = q->priv_data; 2228c2ecf20Sopenharmony_ci struct saa7146_fh *fh = file->private_data; 2238c2ecf20Sopenharmony_ci struct saa7146_dev *dev = fh->dev; 2248c2ecf20Sopenharmony_ci struct saa7146_buf *buf = (struct saa7146_buf *)vb; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci int err = 0; 2278c2ecf20Sopenharmony_ci int lines, llength, size; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci lines = 16 * 2 ; /* 2 fields */ 2308c2ecf20Sopenharmony_ci llength = vbi_pixel_to_capture; 2318c2ecf20Sopenharmony_ci size = lines * llength; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci DEB_VBI("vb:%p\n", vb); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci if (0 != buf->vb.baddr && buf->vb.bsize < size) { 2368c2ecf20Sopenharmony_ci DEB_VBI("size mismatch\n"); 2378c2ecf20Sopenharmony_ci return -EINVAL; 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci if (buf->vb.size != size) 2418c2ecf20Sopenharmony_ci saa7146_dma_free(dev,q,buf); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { 2448c2ecf20Sopenharmony_ci struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci buf->vb.width = llength; 2478c2ecf20Sopenharmony_ci buf->vb.height = lines; 2488c2ecf20Sopenharmony_ci buf->vb.size = size; 2498c2ecf20Sopenharmony_ci buf->vb.field = field; // FIXME: check this 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci saa7146_pgtable_free(dev->pci, &buf->pt[2]); 2528c2ecf20Sopenharmony_ci saa7146_pgtable_alloc(dev->pci, &buf->pt[2]); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci err = videobuf_iolock(q,&buf->vb, NULL); 2558c2ecf20Sopenharmony_ci if (err) 2568c2ecf20Sopenharmony_ci goto oops; 2578c2ecf20Sopenharmony_ci err = saa7146_pgtable_build_single(dev->pci, &buf->pt[2], 2588c2ecf20Sopenharmony_ci dma->sglist, dma->sglen); 2598c2ecf20Sopenharmony_ci if (0 != err) 2608c2ecf20Sopenharmony_ci return err; 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci buf->vb.state = VIDEOBUF_PREPARED; 2638c2ecf20Sopenharmony_ci buf->activate = buffer_activate; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci return 0; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci oops: 2688c2ecf20Sopenharmony_ci DEB_VBI("error out\n"); 2698c2ecf20Sopenharmony_ci saa7146_dma_free(dev,q,buf); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci return err; 2728c2ecf20Sopenharmony_ci} 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_cistatic int buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci int llength,lines; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci lines = 16 * 2 ; /* 2 fields */ 2798c2ecf20Sopenharmony_ci llength = vbi_pixel_to_capture; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci *size = lines * llength; 2828c2ecf20Sopenharmony_ci *count = 2; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci DEB_VBI("count:%d, size:%d\n", *count, *size); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci return 0; 2878c2ecf20Sopenharmony_ci} 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_cistatic void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) 2908c2ecf20Sopenharmony_ci{ 2918c2ecf20Sopenharmony_ci struct file *file = q->priv_data; 2928c2ecf20Sopenharmony_ci struct saa7146_fh *fh = file->private_data; 2938c2ecf20Sopenharmony_ci struct saa7146_dev *dev = fh->dev; 2948c2ecf20Sopenharmony_ci struct saa7146_vv *vv = dev->vv_data; 2958c2ecf20Sopenharmony_ci struct saa7146_buf *buf = (struct saa7146_buf *)vb; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci DEB_VBI("vb:%p\n", vb); 2988c2ecf20Sopenharmony_ci saa7146_buffer_queue(dev, &vv->vbi_dmaq, buf); 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_cistatic void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) 3028c2ecf20Sopenharmony_ci{ 3038c2ecf20Sopenharmony_ci struct file *file = q->priv_data; 3048c2ecf20Sopenharmony_ci struct saa7146_fh *fh = file->private_data; 3058c2ecf20Sopenharmony_ci struct saa7146_dev *dev = fh->dev; 3068c2ecf20Sopenharmony_ci struct saa7146_buf *buf = (struct saa7146_buf *)vb; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci DEB_VBI("vb:%p\n", vb); 3098c2ecf20Sopenharmony_ci saa7146_dma_free(dev,q,buf); 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_cistatic const struct videobuf_queue_ops vbi_qops = { 3138c2ecf20Sopenharmony_ci .buf_setup = buffer_setup, 3148c2ecf20Sopenharmony_ci .buf_prepare = buffer_prepare, 3158c2ecf20Sopenharmony_ci .buf_queue = buffer_queue, 3168c2ecf20Sopenharmony_ci .buf_release = buffer_release, 3178c2ecf20Sopenharmony_ci}; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------ */ 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistatic void vbi_stop(struct saa7146_fh *fh, struct file *file) 3228c2ecf20Sopenharmony_ci{ 3238c2ecf20Sopenharmony_ci struct saa7146_dev *dev = fh->dev; 3248c2ecf20Sopenharmony_ci struct saa7146_vv *vv = dev->vv_data; 3258c2ecf20Sopenharmony_ci unsigned long flags; 3268c2ecf20Sopenharmony_ci DEB_VBI("dev:%p, fh:%p\n", dev, fh); 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->slock,flags); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci /* disable rps1 */ 3318c2ecf20Sopenharmony_ci saa7146_write(dev, MC1, MASK_29); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci /* disable rps1 irqs */ 3348c2ecf20Sopenharmony_ci SAA7146_IER_DISABLE(dev, MASK_28); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci /* shut down dma 3 transfers */ 3378c2ecf20Sopenharmony_ci saa7146_write(dev, MC1, MASK_20); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci if (vv->vbi_dmaq.curr) 3408c2ecf20Sopenharmony_ci saa7146_buffer_finish(dev, &vv->vbi_dmaq, VIDEOBUF_DONE); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci videobuf_queue_cancel(&fh->vbi_q); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci vv->vbi_streaming = NULL; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci del_timer(&vv->vbi_dmaq.timeout); 3478c2ecf20Sopenharmony_ci del_timer(&vv->vbi_read_timeout); 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->slock, flags); 3508c2ecf20Sopenharmony_ci} 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_cistatic void vbi_read_timeout(struct timer_list *t) 3538c2ecf20Sopenharmony_ci{ 3548c2ecf20Sopenharmony_ci struct saa7146_vv *vv = from_timer(vv, t, vbi_read_timeout); 3558c2ecf20Sopenharmony_ci struct file *file = vv->vbi_read_timeout_file; 3568c2ecf20Sopenharmony_ci struct saa7146_fh *fh = file->private_data; 3578c2ecf20Sopenharmony_ci struct saa7146_dev *dev = fh->dev; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci DEB_VBI("dev:%p, fh:%p\n", dev, fh); 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci vbi_stop(fh, file); 3628c2ecf20Sopenharmony_ci} 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_cistatic void vbi_init(struct saa7146_dev *dev, struct saa7146_vv *vv) 3658c2ecf20Sopenharmony_ci{ 3668c2ecf20Sopenharmony_ci DEB_VBI("dev:%p\n", dev); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&vv->vbi_dmaq.queue); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci timer_setup(&vv->vbi_dmaq.timeout, saa7146_buffer_timeout, 0); 3718c2ecf20Sopenharmony_ci vv->vbi_dmaq.dev = dev; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci init_waitqueue_head(&vv->vbi_wq); 3748c2ecf20Sopenharmony_ci} 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_cistatic int vbi_open(struct saa7146_dev *dev, struct file *file) 3778c2ecf20Sopenharmony_ci{ 3788c2ecf20Sopenharmony_ci struct saa7146_fh *fh = file->private_data; 3798c2ecf20Sopenharmony_ci struct saa7146_vv *vv = fh->dev->vv_data; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci u32 arbtr_ctrl = saa7146_read(dev, PCI_BT_V1); 3828c2ecf20Sopenharmony_ci int ret = 0; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci DEB_VBI("dev:%p, fh:%p\n", dev, fh); 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci ret = saa7146_res_get(fh, RESOURCE_DMA3_BRS); 3878c2ecf20Sopenharmony_ci if (0 == ret) { 3888c2ecf20Sopenharmony_ci DEB_S("cannot get vbi RESOURCE_DMA3_BRS resource\n"); 3898c2ecf20Sopenharmony_ci return -EBUSY; 3908c2ecf20Sopenharmony_ci } 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci /* adjust arbitrition control for video dma 3 */ 3938c2ecf20Sopenharmony_ci arbtr_ctrl &= ~0x1f0000; 3948c2ecf20Sopenharmony_ci arbtr_ctrl |= 0x1d0000; 3958c2ecf20Sopenharmony_ci saa7146_write(dev, PCI_BT_V1, arbtr_ctrl); 3968c2ecf20Sopenharmony_ci saa7146_write(dev, MC2, (MASK_04|MASK_20)); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci videobuf_queue_sg_init(&fh->vbi_q, &vbi_qops, 3998c2ecf20Sopenharmony_ci &dev->pci->dev, &dev->slock, 4008c2ecf20Sopenharmony_ci V4L2_BUF_TYPE_VBI_CAPTURE, 4018c2ecf20Sopenharmony_ci V4L2_FIELD_SEQ_TB, // FIXME: does this really work? 4028c2ecf20Sopenharmony_ci sizeof(struct saa7146_buf), 4038c2ecf20Sopenharmony_ci file, &dev->v4l2_lock); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci vv->vbi_read_timeout.function = vbi_read_timeout; 4068c2ecf20Sopenharmony_ci vv->vbi_read_timeout_file = file; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci /* initialize the brs */ 4098c2ecf20Sopenharmony_ci if ( 0 != (SAA7146_USE_PORT_B_FOR_VBI & dev->ext_vv_data->flags)) { 4108c2ecf20Sopenharmony_ci saa7146_write(dev, BRS_CTRL, MASK_30|MASK_29 | (7 << 19)); 4118c2ecf20Sopenharmony_ci } else { 4128c2ecf20Sopenharmony_ci saa7146_write(dev, BRS_CTRL, 0x00000001); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci if (0 != (ret = vbi_workaround(dev))) { 4158c2ecf20Sopenharmony_ci DEB_VBI("vbi workaround failed!\n"); 4168c2ecf20Sopenharmony_ci /* return ret;*/ 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci } 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci /* upload brs register */ 4218c2ecf20Sopenharmony_ci saa7146_write(dev, MC2, (MASK_08|MASK_24)); 4228c2ecf20Sopenharmony_ci return 0; 4238c2ecf20Sopenharmony_ci} 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_cistatic void vbi_close(struct saa7146_dev *dev, struct file *file) 4268c2ecf20Sopenharmony_ci{ 4278c2ecf20Sopenharmony_ci struct saa7146_fh *fh = file->private_data; 4288c2ecf20Sopenharmony_ci struct saa7146_vv *vv = dev->vv_data; 4298c2ecf20Sopenharmony_ci DEB_VBI("dev:%p, fh:%p\n", dev, fh); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci if( fh == vv->vbi_streaming ) { 4328c2ecf20Sopenharmony_ci vbi_stop(fh, file); 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci saa7146_res_free(fh, RESOURCE_DMA3_BRS); 4358c2ecf20Sopenharmony_ci} 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_cistatic void vbi_irq_done(struct saa7146_dev *dev, unsigned long status) 4388c2ecf20Sopenharmony_ci{ 4398c2ecf20Sopenharmony_ci struct saa7146_vv *vv = dev->vv_data; 4408c2ecf20Sopenharmony_ci spin_lock(&dev->slock); 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci if (vv->vbi_dmaq.curr) { 4438c2ecf20Sopenharmony_ci DEB_VBI("dev:%p, curr:%p\n", dev, vv->vbi_dmaq.curr); 4448c2ecf20Sopenharmony_ci /* this must be += 2, one count for each field */ 4458c2ecf20Sopenharmony_ci vv->vbi_fieldcount+=2; 4468c2ecf20Sopenharmony_ci vv->vbi_dmaq.curr->vb.field_count = vv->vbi_fieldcount; 4478c2ecf20Sopenharmony_ci saa7146_buffer_finish(dev, &vv->vbi_dmaq, VIDEOBUF_DONE); 4488c2ecf20Sopenharmony_ci } else { 4498c2ecf20Sopenharmony_ci DEB_VBI("dev:%p\n", dev); 4508c2ecf20Sopenharmony_ci } 4518c2ecf20Sopenharmony_ci saa7146_buffer_next(dev, &vv->vbi_dmaq, 1); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci spin_unlock(&dev->slock); 4548c2ecf20Sopenharmony_ci} 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_cistatic ssize_t vbi_read(struct file *file, char __user *data, size_t count, loff_t *ppos) 4578c2ecf20Sopenharmony_ci{ 4588c2ecf20Sopenharmony_ci struct saa7146_fh *fh = file->private_data; 4598c2ecf20Sopenharmony_ci struct saa7146_dev *dev = fh->dev; 4608c2ecf20Sopenharmony_ci struct saa7146_vv *vv = dev->vv_data; 4618c2ecf20Sopenharmony_ci ssize_t ret = 0; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci DEB_VBI("dev:%p, fh:%p\n", dev, fh); 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci if( NULL == vv->vbi_streaming ) { 4668c2ecf20Sopenharmony_ci // fixme: check if dma3 is available 4678c2ecf20Sopenharmony_ci // fixme: activate vbi engine here if necessary. (really?) 4688c2ecf20Sopenharmony_ci vv->vbi_streaming = fh; 4698c2ecf20Sopenharmony_ci } 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci if( fh != vv->vbi_streaming ) { 4728c2ecf20Sopenharmony_ci DEB_VBI("open %p is already using vbi capture\n", 4738c2ecf20Sopenharmony_ci vv->vbi_streaming); 4748c2ecf20Sopenharmony_ci return -EBUSY; 4758c2ecf20Sopenharmony_ci } 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci mod_timer(&vv->vbi_read_timeout, jiffies+BUFFER_TIMEOUT); 4788c2ecf20Sopenharmony_ci ret = videobuf_read_stream(&fh->vbi_q, data, count, ppos, 1, 4798c2ecf20Sopenharmony_ci file->f_flags & O_NONBLOCK); 4808c2ecf20Sopenharmony_ci/* 4818c2ecf20Sopenharmony_ci printk("BASE_ODD3: 0x%08x\n", saa7146_read(dev, BASE_ODD3)); 4828c2ecf20Sopenharmony_ci printk("BASE_EVEN3: 0x%08x\n", saa7146_read(dev, BASE_EVEN3)); 4838c2ecf20Sopenharmony_ci printk("PROT_ADDR3: 0x%08x\n", saa7146_read(dev, PROT_ADDR3)); 4848c2ecf20Sopenharmony_ci printk("PITCH3: 0x%08x\n", saa7146_read(dev, PITCH3)); 4858c2ecf20Sopenharmony_ci printk("BASE_PAGE3: 0x%08x\n", saa7146_read(dev, BASE_PAGE3)); 4868c2ecf20Sopenharmony_ci printk("NUM_LINE_BYTE3: 0x%08x\n", saa7146_read(dev, NUM_LINE_BYTE3)); 4878c2ecf20Sopenharmony_ci printk("BRS_CTRL: 0x%08x\n", saa7146_read(dev, BRS_CTRL)); 4888c2ecf20Sopenharmony_ci*/ 4898c2ecf20Sopenharmony_ci return ret; 4908c2ecf20Sopenharmony_ci} 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ciconst struct saa7146_use_ops saa7146_vbi_uops = { 4938c2ecf20Sopenharmony_ci .init = vbi_init, 4948c2ecf20Sopenharmony_ci .open = vbi_open, 4958c2ecf20Sopenharmony_ci .release = vbi_close, 4968c2ecf20Sopenharmony_ci .irq_done = vbi_irq_done, 4978c2ecf20Sopenharmony_ci .read = vbi_read, 4988c2ecf20Sopenharmony_ci}; 499