18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2015 VanguardiaSur - www.vanguardiasur.com.ar 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Based on original driver by Krzysztof Ha?asa: 68c2ecf20Sopenharmony_ci * Copyright (C) 2015 Industrial Research Institute for Automation 78c2ecf20Sopenharmony_ci * and Measurements PIAP 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/init.h> 118c2ecf20Sopenharmony_ci#include <linux/delay.h> 128c2ecf20Sopenharmony_ci#include <linux/list.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/kernel.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci#include <media/v4l2-common.h> 178c2ecf20Sopenharmony_ci#include <media/v4l2-event.h> 188c2ecf20Sopenharmony_ci#include <media/videobuf2-dma-contig.h> 198c2ecf20Sopenharmony_ci#include <media/videobuf2-dma-sg.h> 208c2ecf20Sopenharmony_ci#include <media/videobuf2-vmalloc.h> 218c2ecf20Sopenharmony_ci#include "tw686x.h" 228c2ecf20Sopenharmony_ci#include "tw686x-regs.h" 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define TW686X_INPUTS_PER_CH 4 258c2ecf20Sopenharmony_ci#define TW686X_VIDEO_WIDTH 720 268c2ecf20Sopenharmony_ci#define TW686X_VIDEO_HEIGHT(id) ((id & V4L2_STD_525_60) ? 480 : 576) 278c2ecf20Sopenharmony_ci#define TW686X_MAX_FPS(id) ((id & V4L2_STD_525_60) ? 30 : 25) 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define TW686X_MAX_SG_ENTRY_SIZE 4096 308c2ecf20Sopenharmony_ci#define TW686X_MAX_SG_DESC_COUNT 256 /* PAL 720x576 needs 203 4-KB pages */ 318c2ecf20Sopenharmony_ci#define TW686X_SG_TABLE_SIZE (TW686X_MAX_SG_DESC_COUNT * sizeof(struct tw686x_sg_desc)) 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic const struct tw686x_format formats[] = { 348c2ecf20Sopenharmony_ci { 358c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_UYVY, 368c2ecf20Sopenharmony_ci .mode = 0, 378c2ecf20Sopenharmony_ci .depth = 16, 388c2ecf20Sopenharmony_ci }, { 398c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_RGB565, 408c2ecf20Sopenharmony_ci .mode = 5, 418c2ecf20Sopenharmony_ci .depth = 16, 428c2ecf20Sopenharmony_ci }, { 438c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_YUYV, 448c2ecf20Sopenharmony_ci .mode = 6, 458c2ecf20Sopenharmony_ci .depth = 16, 468c2ecf20Sopenharmony_ci } 478c2ecf20Sopenharmony_ci}; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic void tw686x_buf_done(struct tw686x_video_channel *vc, 508c2ecf20Sopenharmony_ci unsigned int pb) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci struct tw686x_dma_desc *desc = &vc->dma_descs[pb]; 538c2ecf20Sopenharmony_ci struct tw686x_dev *dev = vc->dev; 548c2ecf20Sopenharmony_ci struct vb2_v4l2_buffer *vb; 558c2ecf20Sopenharmony_ci struct vb2_buffer *vb2_buf; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci if (vc->curr_bufs[pb]) { 588c2ecf20Sopenharmony_ci vb = &vc->curr_bufs[pb]->vb; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci vb->field = dev->dma_ops->field; 618c2ecf20Sopenharmony_ci vb->sequence = vc->sequence++; 628c2ecf20Sopenharmony_ci vb2_buf = &vb->vb2_buf; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci if (dev->dma_mode == TW686X_DMA_MODE_MEMCPY) 658c2ecf20Sopenharmony_ci memcpy(vb2_plane_vaddr(vb2_buf, 0), desc->virt, 668c2ecf20Sopenharmony_ci desc->size); 678c2ecf20Sopenharmony_ci vb2_buf->timestamp = ktime_get_ns(); 688c2ecf20Sopenharmony_ci vb2_buffer_done(vb2_buf, VB2_BUF_STATE_DONE); 698c2ecf20Sopenharmony_ci } 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci vc->pb = !pb; 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci/* 758c2ecf20Sopenharmony_ci * We can call this even when alloc_dma failed for the given channel 768c2ecf20Sopenharmony_ci */ 778c2ecf20Sopenharmony_cistatic void tw686x_memcpy_dma_free(struct tw686x_video_channel *vc, 788c2ecf20Sopenharmony_ci unsigned int pb) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci struct tw686x_dma_desc *desc = &vc->dma_descs[pb]; 818c2ecf20Sopenharmony_ci struct tw686x_dev *dev = vc->dev; 828c2ecf20Sopenharmony_ci struct pci_dev *pci_dev; 838c2ecf20Sopenharmony_ci unsigned long flags; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci /* Check device presence. Shouldn't really happen! */ 868c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->lock, flags); 878c2ecf20Sopenharmony_ci pci_dev = dev->pci_dev; 888c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->lock, flags); 898c2ecf20Sopenharmony_ci if (!pci_dev) { 908c2ecf20Sopenharmony_ci WARN(1, "trying to deallocate on missing device\n"); 918c2ecf20Sopenharmony_ci return; 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci if (desc->virt) { 958c2ecf20Sopenharmony_ci pci_free_consistent(dev->pci_dev, desc->size, 968c2ecf20Sopenharmony_ci desc->virt, desc->phys); 978c2ecf20Sopenharmony_ci desc->virt = NULL; 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic int tw686x_memcpy_dma_alloc(struct tw686x_video_channel *vc, 1028c2ecf20Sopenharmony_ci unsigned int pb) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci struct tw686x_dev *dev = vc->dev; 1058c2ecf20Sopenharmony_ci u32 reg = pb ? VDMA_B_ADDR[vc->ch] : VDMA_P_ADDR[vc->ch]; 1068c2ecf20Sopenharmony_ci unsigned int len; 1078c2ecf20Sopenharmony_ci void *virt; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci WARN(vc->dma_descs[pb].virt, 1108c2ecf20Sopenharmony_ci "Allocating buffer but previous still here\n"); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci len = (vc->width * vc->height * vc->format->depth) >> 3; 1138c2ecf20Sopenharmony_ci virt = pci_alloc_consistent(dev->pci_dev, len, 1148c2ecf20Sopenharmony_ci &vc->dma_descs[pb].phys); 1158c2ecf20Sopenharmony_ci if (!virt) { 1168c2ecf20Sopenharmony_ci v4l2_err(&dev->v4l2_dev, 1178c2ecf20Sopenharmony_ci "dma%d: unable to allocate %s-buffer\n", 1188c2ecf20Sopenharmony_ci vc->ch, pb ? "B" : "P"); 1198c2ecf20Sopenharmony_ci return -ENOMEM; 1208c2ecf20Sopenharmony_ci } 1218c2ecf20Sopenharmony_ci vc->dma_descs[pb].size = len; 1228c2ecf20Sopenharmony_ci vc->dma_descs[pb].virt = virt; 1238c2ecf20Sopenharmony_ci reg_write(dev, reg, vc->dma_descs[pb].phys); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci return 0; 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic void tw686x_memcpy_buf_refill(struct tw686x_video_channel *vc, 1298c2ecf20Sopenharmony_ci unsigned int pb) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci struct tw686x_v4l2_buf *buf; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci while (!list_empty(&vc->vidq_queued)) { 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci buf = list_first_entry(&vc->vidq_queued, 1368c2ecf20Sopenharmony_ci struct tw686x_v4l2_buf, list); 1378c2ecf20Sopenharmony_ci list_del(&buf->list); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci vc->curr_bufs[pb] = buf; 1408c2ecf20Sopenharmony_ci return; 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci vc->curr_bufs[pb] = NULL; 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic const struct tw686x_dma_ops memcpy_dma_ops = { 1468c2ecf20Sopenharmony_ci .alloc = tw686x_memcpy_dma_alloc, 1478c2ecf20Sopenharmony_ci .free = tw686x_memcpy_dma_free, 1488c2ecf20Sopenharmony_ci .buf_refill = tw686x_memcpy_buf_refill, 1498c2ecf20Sopenharmony_ci .mem_ops = &vb2_vmalloc_memops, 1508c2ecf20Sopenharmony_ci .hw_dma_mode = TW686X_FRAME_MODE, 1518c2ecf20Sopenharmony_ci .field = V4L2_FIELD_INTERLACED, 1528c2ecf20Sopenharmony_ci}; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic void tw686x_contig_buf_refill(struct tw686x_video_channel *vc, 1558c2ecf20Sopenharmony_ci unsigned int pb) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci struct tw686x_v4l2_buf *buf; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci while (!list_empty(&vc->vidq_queued)) { 1608c2ecf20Sopenharmony_ci u32 reg = pb ? VDMA_B_ADDR[vc->ch] : VDMA_P_ADDR[vc->ch]; 1618c2ecf20Sopenharmony_ci dma_addr_t phys; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci buf = list_first_entry(&vc->vidq_queued, 1648c2ecf20Sopenharmony_ci struct tw686x_v4l2_buf, list); 1658c2ecf20Sopenharmony_ci list_del(&buf->list); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci phys = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); 1688c2ecf20Sopenharmony_ci reg_write(vc->dev, reg, phys); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci buf->vb.vb2_buf.state = VB2_BUF_STATE_ACTIVE; 1718c2ecf20Sopenharmony_ci vc->curr_bufs[pb] = buf; 1728c2ecf20Sopenharmony_ci return; 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci vc->curr_bufs[pb] = NULL; 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic const struct tw686x_dma_ops contig_dma_ops = { 1788c2ecf20Sopenharmony_ci .buf_refill = tw686x_contig_buf_refill, 1798c2ecf20Sopenharmony_ci .mem_ops = &vb2_dma_contig_memops, 1808c2ecf20Sopenharmony_ci .hw_dma_mode = TW686X_FRAME_MODE, 1818c2ecf20Sopenharmony_ci .field = V4L2_FIELD_INTERLACED, 1828c2ecf20Sopenharmony_ci}; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic int tw686x_sg_desc_fill(struct tw686x_sg_desc *descs, 1858c2ecf20Sopenharmony_ci struct tw686x_v4l2_buf *buf, 1868c2ecf20Sopenharmony_ci unsigned int buf_len) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci struct sg_table *vbuf = vb2_dma_sg_plane_desc(&buf->vb.vb2_buf, 0); 1898c2ecf20Sopenharmony_ci unsigned int len, entry_len; 1908c2ecf20Sopenharmony_ci struct scatterlist *sg; 1918c2ecf20Sopenharmony_ci int i, count; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci /* Clear the scatter-gather table */ 1948c2ecf20Sopenharmony_ci memset(descs, 0, TW686X_SG_TABLE_SIZE); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci count = 0; 1978c2ecf20Sopenharmony_ci for_each_sg(vbuf->sgl, sg, vbuf->nents, i) { 1988c2ecf20Sopenharmony_ci dma_addr_t phys = sg_dma_address(sg); 1998c2ecf20Sopenharmony_ci len = sg_dma_len(sg); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci while (len && buf_len) { 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci if (count == TW686X_MAX_SG_DESC_COUNT) 2048c2ecf20Sopenharmony_ci return -ENOMEM; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci entry_len = min_t(unsigned int, len, 2078c2ecf20Sopenharmony_ci TW686X_MAX_SG_ENTRY_SIZE); 2088c2ecf20Sopenharmony_ci entry_len = min_t(unsigned int, entry_len, buf_len); 2098c2ecf20Sopenharmony_ci descs[count].phys = cpu_to_le32(phys); 2108c2ecf20Sopenharmony_ci descs[count++].flags_length = 2118c2ecf20Sopenharmony_ci cpu_to_le32(BIT(30) | entry_len); 2128c2ecf20Sopenharmony_ci phys += entry_len; 2138c2ecf20Sopenharmony_ci len -= entry_len; 2148c2ecf20Sopenharmony_ci buf_len -= entry_len; 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci if (!buf_len) 2188c2ecf20Sopenharmony_ci return 0; 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci return -ENOMEM; 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic void tw686x_sg_buf_refill(struct tw686x_video_channel *vc, 2258c2ecf20Sopenharmony_ci unsigned int pb) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci struct tw686x_dev *dev = vc->dev; 2288c2ecf20Sopenharmony_ci struct tw686x_v4l2_buf *buf; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci while (!list_empty(&vc->vidq_queued)) { 2318c2ecf20Sopenharmony_ci unsigned int buf_len; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci buf = list_first_entry(&vc->vidq_queued, 2348c2ecf20Sopenharmony_ci struct tw686x_v4l2_buf, list); 2358c2ecf20Sopenharmony_ci list_del(&buf->list); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci buf_len = (vc->width * vc->height * vc->format->depth) >> 3; 2388c2ecf20Sopenharmony_ci if (tw686x_sg_desc_fill(vc->sg_descs[pb], buf, buf_len)) { 2398c2ecf20Sopenharmony_ci v4l2_err(&dev->v4l2_dev, 2408c2ecf20Sopenharmony_ci "dma%d: unable to fill %s-buffer\n", 2418c2ecf20Sopenharmony_ci vc->ch, pb ? "B" : "P"); 2428c2ecf20Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); 2438c2ecf20Sopenharmony_ci continue; 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci buf->vb.vb2_buf.state = VB2_BUF_STATE_ACTIVE; 2478c2ecf20Sopenharmony_ci vc->curr_bufs[pb] = buf; 2488c2ecf20Sopenharmony_ci return; 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci vc->curr_bufs[pb] = NULL; 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_cistatic void tw686x_sg_dma_free(struct tw686x_video_channel *vc, 2558c2ecf20Sopenharmony_ci unsigned int pb) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci struct tw686x_dma_desc *desc = &vc->dma_descs[pb]; 2588c2ecf20Sopenharmony_ci struct tw686x_dev *dev = vc->dev; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci if (desc->size) { 2618c2ecf20Sopenharmony_ci pci_free_consistent(dev->pci_dev, desc->size, 2628c2ecf20Sopenharmony_ci desc->virt, desc->phys); 2638c2ecf20Sopenharmony_ci desc->virt = NULL; 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci vc->sg_descs[pb] = NULL; 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cistatic int tw686x_sg_dma_alloc(struct tw686x_video_channel *vc, 2708c2ecf20Sopenharmony_ci unsigned int pb) 2718c2ecf20Sopenharmony_ci{ 2728c2ecf20Sopenharmony_ci struct tw686x_dma_desc *desc = &vc->dma_descs[pb]; 2738c2ecf20Sopenharmony_ci struct tw686x_dev *dev = vc->dev; 2748c2ecf20Sopenharmony_ci u32 reg = pb ? DMA_PAGE_TABLE1_ADDR[vc->ch] : 2758c2ecf20Sopenharmony_ci DMA_PAGE_TABLE0_ADDR[vc->ch]; 2768c2ecf20Sopenharmony_ci void *virt; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci if (desc->size) { 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci virt = pci_alloc_consistent(dev->pci_dev, desc->size, 2818c2ecf20Sopenharmony_ci &desc->phys); 2828c2ecf20Sopenharmony_ci if (!virt) { 2838c2ecf20Sopenharmony_ci v4l2_err(&dev->v4l2_dev, 2848c2ecf20Sopenharmony_ci "dma%d: unable to allocate %s-buffer\n", 2858c2ecf20Sopenharmony_ci vc->ch, pb ? "B" : "P"); 2868c2ecf20Sopenharmony_ci return -ENOMEM; 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci desc->virt = virt; 2898c2ecf20Sopenharmony_ci reg_write(dev, reg, desc->phys); 2908c2ecf20Sopenharmony_ci } else { 2918c2ecf20Sopenharmony_ci virt = dev->video_channels[0].dma_descs[pb].virt + 2928c2ecf20Sopenharmony_ci vc->ch * TW686X_SG_TABLE_SIZE; 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci vc->sg_descs[pb] = virt; 2968c2ecf20Sopenharmony_ci return 0; 2978c2ecf20Sopenharmony_ci} 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_cistatic int tw686x_sg_setup(struct tw686x_dev *dev) 3008c2ecf20Sopenharmony_ci{ 3018c2ecf20Sopenharmony_ci unsigned int sg_table_size, pb, ch, channels; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci if (is_second_gen(dev)) { 3048c2ecf20Sopenharmony_ci /* 3058c2ecf20Sopenharmony_ci * TW6865/TW6869: each channel needs a pair of 3068c2ecf20Sopenharmony_ci * P-B descriptor tables. 3078c2ecf20Sopenharmony_ci */ 3088c2ecf20Sopenharmony_ci channels = max_channels(dev); 3098c2ecf20Sopenharmony_ci sg_table_size = TW686X_SG_TABLE_SIZE; 3108c2ecf20Sopenharmony_ci } else { 3118c2ecf20Sopenharmony_ci /* 3128c2ecf20Sopenharmony_ci * TW6864/TW6868: we need to allocate a pair of 3138c2ecf20Sopenharmony_ci * P-B descriptor tables, common for all channels. 3148c2ecf20Sopenharmony_ci * Each table will be bigger than 4 KB. 3158c2ecf20Sopenharmony_ci */ 3168c2ecf20Sopenharmony_ci channels = 1; 3178c2ecf20Sopenharmony_ci sg_table_size = max_channels(dev) * TW686X_SG_TABLE_SIZE; 3188c2ecf20Sopenharmony_ci } 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci for (ch = 0; ch < channels; ch++) { 3218c2ecf20Sopenharmony_ci struct tw686x_video_channel *vc = &dev->video_channels[ch]; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci for (pb = 0; pb < 2; pb++) 3248c2ecf20Sopenharmony_ci vc->dma_descs[pb].size = sg_table_size; 3258c2ecf20Sopenharmony_ci } 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci return 0; 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_cistatic const struct tw686x_dma_ops sg_dma_ops = { 3318c2ecf20Sopenharmony_ci .setup = tw686x_sg_setup, 3328c2ecf20Sopenharmony_ci .alloc = tw686x_sg_dma_alloc, 3338c2ecf20Sopenharmony_ci .free = tw686x_sg_dma_free, 3348c2ecf20Sopenharmony_ci .buf_refill = tw686x_sg_buf_refill, 3358c2ecf20Sopenharmony_ci .mem_ops = &vb2_dma_sg_memops, 3368c2ecf20Sopenharmony_ci .hw_dma_mode = TW686X_SG_MODE, 3378c2ecf20Sopenharmony_ci .field = V4L2_FIELD_SEQ_TB, 3388c2ecf20Sopenharmony_ci}; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_cistatic const unsigned int fps_map[15] = { 3418c2ecf20Sopenharmony_ci /* 3428c2ecf20Sopenharmony_ci * bit 31 enables selecting the field control register 3438c2ecf20Sopenharmony_ci * bits 0-29 are a bitmask with fields that will be output. 3448c2ecf20Sopenharmony_ci * For NTSC (and PAL-M, PAL-60), all 30 bits are used. 3458c2ecf20Sopenharmony_ci * For other PAL standards, only the first 25 bits are used. 3468c2ecf20Sopenharmony_ci */ 3478c2ecf20Sopenharmony_ci 0x00000000, /* output all fields */ 3488c2ecf20Sopenharmony_ci 0x80000006, /* 2 fps (60Hz), 2 fps (50Hz) */ 3498c2ecf20Sopenharmony_ci 0x80018006, /* 4 fps (60Hz), 4 fps (50Hz) */ 3508c2ecf20Sopenharmony_ci 0x80618006, /* 6 fps (60Hz), 6 fps (50Hz) */ 3518c2ecf20Sopenharmony_ci 0x81818186, /* 8 fps (60Hz), 8 fps (50Hz) */ 3528c2ecf20Sopenharmony_ci 0x86186186, /* 10 fps (60Hz), 8 fps (50Hz) */ 3538c2ecf20Sopenharmony_ci 0x86619866, /* 12 fps (60Hz), 10 fps (50Hz) */ 3548c2ecf20Sopenharmony_ci 0x86666666, /* 14 fps (60Hz), 12 fps (50Hz) */ 3558c2ecf20Sopenharmony_ci 0x9999999e, /* 16 fps (60Hz), 14 fps (50Hz) */ 3568c2ecf20Sopenharmony_ci 0x99e6799e, /* 18 fps (60Hz), 16 fps (50Hz) */ 3578c2ecf20Sopenharmony_ci 0x9e79e79e, /* 20 fps (60Hz), 16 fps (50Hz) */ 3588c2ecf20Sopenharmony_ci 0x9e7e7e7e, /* 22 fps (60Hz), 18 fps (50Hz) */ 3598c2ecf20Sopenharmony_ci 0x9fe7f9fe, /* 24 fps (60Hz), 20 fps (50Hz) */ 3608c2ecf20Sopenharmony_ci 0x9ffe7ffe, /* 26 fps (60Hz), 22 fps (50Hz) */ 3618c2ecf20Sopenharmony_ci 0x9ffffffe, /* 28 fps (60Hz), 24 fps (50Hz) */ 3628c2ecf20Sopenharmony_ci}; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_cistatic unsigned int tw686x_real_fps(unsigned int index, unsigned int max_fps) 3658c2ecf20Sopenharmony_ci{ 3668c2ecf20Sopenharmony_ci unsigned long mask; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci if (!index || index >= ARRAY_SIZE(fps_map)) 3698c2ecf20Sopenharmony_ci return max_fps; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci mask = GENMASK(max_fps - 1, 0); 3728c2ecf20Sopenharmony_ci return hweight_long(fps_map[index] & mask); 3738c2ecf20Sopenharmony_ci} 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_cistatic unsigned int tw686x_fps_idx(unsigned int fps, unsigned int max_fps) 3768c2ecf20Sopenharmony_ci{ 3778c2ecf20Sopenharmony_ci unsigned int idx, real_fps; 3788c2ecf20Sopenharmony_ci int delta; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci /* First guess */ 3818c2ecf20Sopenharmony_ci idx = (12 + 15 * fps) / max_fps; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci /* Minimal possible framerate is 2 frames per second */ 3848c2ecf20Sopenharmony_ci if (!idx) 3858c2ecf20Sopenharmony_ci return 1; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci /* Check if the difference is bigger than abs(1) and adjust */ 3888c2ecf20Sopenharmony_ci real_fps = tw686x_real_fps(idx, max_fps); 3898c2ecf20Sopenharmony_ci delta = real_fps - fps; 3908c2ecf20Sopenharmony_ci if (delta < -1) 3918c2ecf20Sopenharmony_ci idx++; 3928c2ecf20Sopenharmony_ci else if (delta > 1) 3938c2ecf20Sopenharmony_ci idx--; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci /* Max framerate */ 3968c2ecf20Sopenharmony_ci if (idx >= 15) 3978c2ecf20Sopenharmony_ci return 0; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci return idx; 4008c2ecf20Sopenharmony_ci} 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_cistatic void tw686x_set_framerate(struct tw686x_video_channel *vc, 4038c2ecf20Sopenharmony_ci unsigned int fps) 4048c2ecf20Sopenharmony_ci{ 4058c2ecf20Sopenharmony_ci unsigned int i; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci i = tw686x_fps_idx(fps, TW686X_MAX_FPS(vc->video_standard)); 4088c2ecf20Sopenharmony_ci reg_write(vc->dev, VIDEO_FIELD_CTRL[vc->ch], fps_map[i]); 4098c2ecf20Sopenharmony_ci vc->fps = tw686x_real_fps(i, TW686X_MAX_FPS(vc->video_standard)); 4108c2ecf20Sopenharmony_ci} 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_cistatic const struct tw686x_format *format_by_fourcc(unsigned int fourcc) 4138c2ecf20Sopenharmony_ci{ 4148c2ecf20Sopenharmony_ci unsigned int cnt; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci for (cnt = 0; cnt < ARRAY_SIZE(formats); cnt++) 4178c2ecf20Sopenharmony_ci if (formats[cnt].fourcc == fourcc) 4188c2ecf20Sopenharmony_ci return &formats[cnt]; 4198c2ecf20Sopenharmony_ci return NULL; 4208c2ecf20Sopenharmony_ci} 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_cistatic int tw686x_queue_setup(struct vb2_queue *vq, 4238c2ecf20Sopenharmony_ci unsigned int *nbuffers, unsigned int *nplanes, 4248c2ecf20Sopenharmony_ci unsigned int sizes[], struct device *alloc_devs[]) 4258c2ecf20Sopenharmony_ci{ 4268c2ecf20Sopenharmony_ci struct tw686x_video_channel *vc = vb2_get_drv_priv(vq); 4278c2ecf20Sopenharmony_ci unsigned int szimage = 4288c2ecf20Sopenharmony_ci (vc->width * vc->height * vc->format->depth) >> 3; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci /* 4318c2ecf20Sopenharmony_ci * Let's request at least three buffers: two for the 4328c2ecf20Sopenharmony_ci * DMA engine and one for userspace. 4338c2ecf20Sopenharmony_ci */ 4348c2ecf20Sopenharmony_ci if (vq->num_buffers + *nbuffers < 3) 4358c2ecf20Sopenharmony_ci *nbuffers = 3 - vq->num_buffers; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci if (*nplanes) { 4388c2ecf20Sopenharmony_ci if (*nplanes != 1 || sizes[0] < szimage) 4398c2ecf20Sopenharmony_ci return -EINVAL; 4408c2ecf20Sopenharmony_ci return 0; 4418c2ecf20Sopenharmony_ci } 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci sizes[0] = szimage; 4448c2ecf20Sopenharmony_ci *nplanes = 1; 4458c2ecf20Sopenharmony_ci return 0; 4468c2ecf20Sopenharmony_ci} 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_cistatic void tw686x_buf_queue(struct vb2_buffer *vb) 4498c2ecf20Sopenharmony_ci{ 4508c2ecf20Sopenharmony_ci struct tw686x_video_channel *vc = vb2_get_drv_priv(vb->vb2_queue); 4518c2ecf20Sopenharmony_ci struct tw686x_dev *dev = vc->dev; 4528c2ecf20Sopenharmony_ci struct pci_dev *pci_dev; 4538c2ecf20Sopenharmony_ci unsigned long flags; 4548c2ecf20Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 4558c2ecf20Sopenharmony_ci struct tw686x_v4l2_buf *buf = 4568c2ecf20Sopenharmony_ci container_of(vbuf, struct tw686x_v4l2_buf, vb); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci /* Check device presence */ 4598c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->lock, flags); 4608c2ecf20Sopenharmony_ci pci_dev = dev->pci_dev; 4618c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->lock, flags); 4628c2ecf20Sopenharmony_ci if (!pci_dev) { 4638c2ecf20Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); 4648c2ecf20Sopenharmony_ci return; 4658c2ecf20Sopenharmony_ci } 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci spin_lock_irqsave(&vc->qlock, flags); 4688c2ecf20Sopenharmony_ci list_add_tail(&buf->list, &vc->vidq_queued); 4698c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vc->qlock, flags); 4708c2ecf20Sopenharmony_ci} 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_cistatic void tw686x_clear_queue(struct tw686x_video_channel *vc, 4738c2ecf20Sopenharmony_ci enum vb2_buffer_state state) 4748c2ecf20Sopenharmony_ci{ 4758c2ecf20Sopenharmony_ci unsigned int pb; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci while (!list_empty(&vc->vidq_queued)) { 4788c2ecf20Sopenharmony_ci struct tw686x_v4l2_buf *buf; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci buf = list_first_entry(&vc->vidq_queued, 4818c2ecf20Sopenharmony_ci struct tw686x_v4l2_buf, list); 4828c2ecf20Sopenharmony_ci list_del(&buf->list); 4838c2ecf20Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, state); 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci for (pb = 0; pb < 2; pb++) { 4878c2ecf20Sopenharmony_ci if (vc->curr_bufs[pb]) 4888c2ecf20Sopenharmony_ci vb2_buffer_done(&vc->curr_bufs[pb]->vb.vb2_buf, state); 4898c2ecf20Sopenharmony_ci vc->curr_bufs[pb] = NULL; 4908c2ecf20Sopenharmony_ci } 4918c2ecf20Sopenharmony_ci} 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_cistatic int tw686x_start_streaming(struct vb2_queue *vq, unsigned int count) 4948c2ecf20Sopenharmony_ci{ 4958c2ecf20Sopenharmony_ci struct tw686x_video_channel *vc = vb2_get_drv_priv(vq); 4968c2ecf20Sopenharmony_ci struct tw686x_dev *dev = vc->dev; 4978c2ecf20Sopenharmony_ci struct pci_dev *pci_dev; 4988c2ecf20Sopenharmony_ci unsigned long flags; 4998c2ecf20Sopenharmony_ci int pb, err; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci /* Check device presence */ 5028c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->lock, flags); 5038c2ecf20Sopenharmony_ci pci_dev = dev->pci_dev; 5048c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->lock, flags); 5058c2ecf20Sopenharmony_ci if (!pci_dev) { 5068c2ecf20Sopenharmony_ci err = -ENODEV; 5078c2ecf20Sopenharmony_ci goto err_clear_queue; 5088c2ecf20Sopenharmony_ci } 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci spin_lock_irqsave(&vc->qlock, flags); 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci /* Sanity check */ 5138c2ecf20Sopenharmony_ci if (dev->dma_mode == TW686X_DMA_MODE_MEMCPY && 5148c2ecf20Sopenharmony_ci (!vc->dma_descs[0].virt || !vc->dma_descs[1].virt)) { 5158c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vc->qlock, flags); 5168c2ecf20Sopenharmony_ci v4l2_err(&dev->v4l2_dev, 5178c2ecf20Sopenharmony_ci "video%d: refusing to start without DMA buffers\n", 5188c2ecf20Sopenharmony_ci vc->num); 5198c2ecf20Sopenharmony_ci err = -ENOMEM; 5208c2ecf20Sopenharmony_ci goto err_clear_queue; 5218c2ecf20Sopenharmony_ci } 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci for (pb = 0; pb < 2; pb++) 5248c2ecf20Sopenharmony_ci dev->dma_ops->buf_refill(vc, pb); 5258c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vc->qlock, flags); 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci vc->sequence = 0; 5288c2ecf20Sopenharmony_ci vc->pb = 0; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->lock, flags); 5318c2ecf20Sopenharmony_ci tw686x_enable_channel(dev, vc->ch); 5328c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->lock, flags); 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci mod_timer(&dev->dma_delay_timer, jiffies + msecs_to_jiffies(100)); 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci return 0; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_cierr_clear_queue: 5398c2ecf20Sopenharmony_ci spin_lock_irqsave(&vc->qlock, flags); 5408c2ecf20Sopenharmony_ci tw686x_clear_queue(vc, VB2_BUF_STATE_QUEUED); 5418c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vc->qlock, flags); 5428c2ecf20Sopenharmony_ci return err; 5438c2ecf20Sopenharmony_ci} 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_cistatic void tw686x_stop_streaming(struct vb2_queue *vq) 5468c2ecf20Sopenharmony_ci{ 5478c2ecf20Sopenharmony_ci struct tw686x_video_channel *vc = vb2_get_drv_priv(vq); 5488c2ecf20Sopenharmony_ci struct tw686x_dev *dev = vc->dev; 5498c2ecf20Sopenharmony_ci struct pci_dev *pci_dev; 5508c2ecf20Sopenharmony_ci unsigned long flags; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci /* Check device presence */ 5538c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->lock, flags); 5548c2ecf20Sopenharmony_ci pci_dev = dev->pci_dev; 5558c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->lock, flags); 5568c2ecf20Sopenharmony_ci if (pci_dev) 5578c2ecf20Sopenharmony_ci tw686x_disable_channel(dev, vc->ch); 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci spin_lock_irqsave(&vc->qlock, flags); 5608c2ecf20Sopenharmony_ci tw686x_clear_queue(vc, VB2_BUF_STATE_ERROR); 5618c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vc->qlock, flags); 5628c2ecf20Sopenharmony_ci} 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_cistatic int tw686x_buf_prepare(struct vb2_buffer *vb) 5658c2ecf20Sopenharmony_ci{ 5668c2ecf20Sopenharmony_ci struct tw686x_video_channel *vc = vb2_get_drv_priv(vb->vb2_queue); 5678c2ecf20Sopenharmony_ci unsigned int size = 5688c2ecf20Sopenharmony_ci (vc->width * vc->height * vc->format->depth) >> 3; 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci if (vb2_plane_size(vb, 0) < size) 5718c2ecf20Sopenharmony_ci return -EINVAL; 5728c2ecf20Sopenharmony_ci vb2_set_plane_payload(vb, 0, size); 5738c2ecf20Sopenharmony_ci return 0; 5748c2ecf20Sopenharmony_ci} 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_cistatic const struct vb2_ops tw686x_video_qops = { 5778c2ecf20Sopenharmony_ci .queue_setup = tw686x_queue_setup, 5788c2ecf20Sopenharmony_ci .buf_queue = tw686x_buf_queue, 5798c2ecf20Sopenharmony_ci .buf_prepare = tw686x_buf_prepare, 5808c2ecf20Sopenharmony_ci .start_streaming = tw686x_start_streaming, 5818c2ecf20Sopenharmony_ci .stop_streaming = tw686x_stop_streaming, 5828c2ecf20Sopenharmony_ci .wait_prepare = vb2_ops_wait_prepare, 5838c2ecf20Sopenharmony_ci .wait_finish = vb2_ops_wait_finish, 5848c2ecf20Sopenharmony_ci}; 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_cistatic int tw686x_s_ctrl(struct v4l2_ctrl *ctrl) 5878c2ecf20Sopenharmony_ci{ 5888c2ecf20Sopenharmony_ci struct tw686x_video_channel *vc; 5898c2ecf20Sopenharmony_ci struct tw686x_dev *dev; 5908c2ecf20Sopenharmony_ci unsigned int ch; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci vc = container_of(ctrl->handler, struct tw686x_video_channel, 5938c2ecf20Sopenharmony_ci ctrl_handler); 5948c2ecf20Sopenharmony_ci dev = vc->dev; 5958c2ecf20Sopenharmony_ci ch = vc->ch; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci switch (ctrl->id) { 5988c2ecf20Sopenharmony_ci case V4L2_CID_BRIGHTNESS: 5998c2ecf20Sopenharmony_ci reg_write(dev, BRIGHT[ch], ctrl->val & 0xff); 6008c2ecf20Sopenharmony_ci return 0; 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci case V4L2_CID_CONTRAST: 6038c2ecf20Sopenharmony_ci reg_write(dev, CONTRAST[ch], ctrl->val); 6048c2ecf20Sopenharmony_ci return 0; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci case V4L2_CID_SATURATION: 6078c2ecf20Sopenharmony_ci reg_write(dev, SAT_U[ch], ctrl->val); 6088c2ecf20Sopenharmony_ci reg_write(dev, SAT_V[ch], ctrl->val); 6098c2ecf20Sopenharmony_ci return 0; 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci case V4L2_CID_HUE: 6128c2ecf20Sopenharmony_ci reg_write(dev, HUE[ch], ctrl->val & 0xff); 6138c2ecf20Sopenharmony_ci return 0; 6148c2ecf20Sopenharmony_ci } 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci return -EINVAL; 6178c2ecf20Sopenharmony_ci} 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops ctrl_ops = { 6208c2ecf20Sopenharmony_ci .s_ctrl = tw686x_s_ctrl, 6218c2ecf20Sopenharmony_ci}; 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_cistatic int tw686x_g_fmt_vid_cap(struct file *file, void *priv, 6248c2ecf20Sopenharmony_ci struct v4l2_format *f) 6258c2ecf20Sopenharmony_ci{ 6268c2ecf20Sopenharmony_ci struct tw686x_video_channel *vc = video_drvdata(file); 6278c2ecf20Sopenharmony_ci struct tw686x_dev *dev = vc->dev; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci f->fmt.pix.width = vc->width; 6308c2ecf20Sopenharmony_ci f->fmt.pix.height = vc->height; 6318c2ecf20Sopenharmony_ci f->fmt.pix.field = dev->dma_ops->field; 6328c2ecf20Sopenharmony_ci f->fmt.pix.pixelformat = vc->format->fourcc; 6338c2ecf20Sopenharmony_ci f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; 6348c2ecf20Sopenharmony_ci f->fmt.pix.bytesperline = (f->fmt.pix.width * vc->format->depth) / 8; 6358c2ecf20Sopenharmony_ci f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; 6368c2ecf20Sopenharmony_ci return 0; 6378c2ecf20Sopenharmony_ci} 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_cistatic int tw686x_try_fmt_vid_cap(struct file *file, void *priv, 6408c2ecf20Sopenharmony_ci struct v4l2_format *f) 6418c2ecf20Sopenharmony_ci{ 6428c2ecf20Sopenharmony_ci struct tw686x_video_channel *vc = video_drvdata(file); 6438c2ecf20Sopenharmony_ci struct tw686x_dev *dev = vc->dev; 6448c2ecf20Sopenharmony_ci unsigned int video_height = TW686X_VIDEO_HEIGHT(vc->video_standard); 6458c2ecf20Sopenharmony_ci const struct tw686x_format *format; 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci format = format_by_fourcc(f->fmt.pix.pixelformat); 6488c2ecf20Sopenharmony_ci if (!format) { 6498c2ecf20Sopenharmony_ci format = &formats[0]; 6508c2ecf20Sopenharmony_ci f->fmt.pix.pixelformat = format->fourcc; 6518c2ecf20Sopenharmony_ci } 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci if (f->fmt.pix.width <= TW686X_VIDEO_WIDTH / 2) 6548c2ecf20Sopenharmony_ci f->fmt.pix.width = TW686X_VIDEO_WIDTH / 2; 6558c2ecf20Sopenharmony_ci else 6568c2ecf20Sopenharmony_ci f->fmt.pix.width = TW686X_VIDEO_WIDTH; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci if (f->fmt.pix.height <= video_height / 2) 6598c2ecf20Sopenharmony_ci f->fmt.pix.height = video_height / 2; 6608c2ecf20Sopenharmony_ci else 6618c2ecf20Sopenharmony_ci f->fmt.pix.height = video_height; 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci f->fmt.pix.bytesperline = (f->fmt.pix.width * format->depth) / 8; 6648c2ecf20Sopenharmony_ci f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; 6658c2ecf20Sopenharmony_ci f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; 6668c2ecf20Sopenharmony_ci f->fmt.pix.field = dev->dma_ops->field; 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci return 0; 6698c2ecf20Sopenharmony_ci} 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_cistatic int tw686x_set_format(struct tw686x_video_channel *vc, 6728c2ecf20Sopenharmony_ci unsigned int pixelformat, unsigned int width, 6738c2ecf20Sopenharmony_ci unsigned int height, bool realloc) 6748c2ecf20Sopenharmony_ci{ 6758c2ecf20Sopenharmony_ci struct tw686x_dev *dev = vc->dev; 6768c2ecf20Sopenharmony_ci u32 val, dma_width, dma_height, dma_line_width; 6778c2ecf20Sopenharmony_ci int err, pb; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci vc->format = format_by_fourcc(pixelformat); 6808c2ecf20Sopenharmony_ci vc->width = width; 6818c2ecf20Sopenharmony_ci vc->height = height; 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci /* We need new DMA buffers if the framesize has changed */ 6848c2ecf20Sopenharmony_ci if (dev->dma_ops->alloc && realloc) { 6858c2ecf20Sopenharmony_ci for (pb = 0; pb < 2; pb++) 6868c2ecf20Sopenharmony_ci dev->dma_ops->free(vc, pb); 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci for (pb = 0; pb < 2; pb++) { 6898c2ecf20Sopenharmony_ci err = dev->dma_ops->alloc(vc, pb); 6908c2ecf20Sopenharmony_ci if (err) { 6918c2ecf20Sopenharmony_ci if (pb > 0) 6928c2ecf20Sopenharmony_ci dev->dma_ops->free(vc, 0); 6938c2ecf20Sopenharmony_ci return err; 6948c2ecf20Sopenharmony_ci } 6958c2ecf20Sopenharmony_ci } 6968c2ecf20Sopenharmony_ci } 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci val = reg_read(vc->dev, VDMA_CHANNEL_CONFIG[vc->ch]); 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci if (vc->width <= TW686X_VIDEO_WIDTH / 2) 7018c2ecf20Sopenharmony_ci val |= BIT(23); 7028c2ecf20Sopenharmony_ci else 7038c2ecf20Sopenharmony_ci val &= ~BIT(23); 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci if (vc->height <= TW686X_VIDEO_HEIGHT(vc->video_standard) / 2) 7068c2ecf20Sopenharmony_ci val |= BIT(24); 7078c2ecf20Sopenharmony_ci else 7088c2ecf20Sopenharmony_ci val &= ~BIT(24); 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci val &= ~0x7ffff; 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci /* Program the DMA scatter-gather */ 7138c2ecf20Sopenharmony_ci if (dev->dma_mode == TW686X_DMA_MODE_SG) { 7148c2ecf20Sopenharmony_ci u32 start_idx, end_idx; 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci start_idx = is_second_gen(dev) ? 7178c2ecf20Sopenharmony_ci 0 : vc->ch * TW686X_MAX_SG_DESC_COUNT; 7188c2ecf20Sopenharmony_ci end_idx = start_idx + TW686X_MAX_SG_DESC_COUNT - 1; 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci val |= (end_idx << 10) | start_idx; 7218c2ecf20Sopenharmony_ci } 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci val &= ~(0x7 << 20); 7248c2ecf20Sopenharmony_ci val |= vc->format->mode << 20; 7258c2ecf20Sopenharmony_ci reg_write(vc->dev, VDMA_CHANNEL_CONFIG[vc->ch], val); 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci /* Program the DMA frame size */ 7288c2ecf20Sopenharmony_ci dma_width = (vc->width * 2) & 0x7ff; 7298c2ecf20Sopenharmony_ci dma_height = vc->height / 2; 7308c2ecf20Sopenharmony_ci dma_line_width = (vc->width * 2) & 0x7ff; 7318c2ecf20Sopenharmony_ci val = (dma_height << 22) | (dma_line_width << 11) | dma_width; 7328c2ecf20Sopenharmony_ci reg_write(vc->dev, VDMA_WHP[vc->ch], val); 7338c2ecf20Sopenharmony_ci return 0; 7348c2ecf20Sopenharmony_ci} 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_cistatic int tw686x_s_fmt_vid_cap(struct file *file, void *priv, 7378c2ecf20Sopenharmony_ci struct v4l2_format *f) 7388c2ecf20Sopenharmony_ci{ 7398c2ecf20Sopenharmony_ci struct tw686x_video_channel *vc = video_drvdata(file); 7408c2ecf20Sopenharmony_ci unsigned long area; 7418c2ecf20Sopenharmony_ci bool realloc; 7428c2ecf20Sopenharmony_ci int err; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci if (vb2_is_busy(&vc->vidq)) 7458c2ecf20Sopenharmony_ci return -EBUSY; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci area = vc->width * vc->height; 7488c2ecf20Sopenharmony_ci err = tw686x_try_fmt_vid_cap(file, priv, f); 7498c2ecf20Sopenharmony_ci if (err) 7508c2ecf20Sopenharmony_ci return err; 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci realloc = area != (f->fmt.pix.width * f->fmt.pix.height); 7538c2ecf20Sopenharmony_ci return tw686x_set_format(vc, f->fmt.pix.pixelformat, 7548c2ecf20Sopenharmony_ci f->fmt.pix.width, f->fmt.pix.height, 7558c2ecf20Sopenharmony_ci realloc); 7568c2ecf20Sopenharmony_ci} 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_cistatic int tw686x_querycap(struct file *file, void *priv, 7598c2ecf20Sopenharmony_ci struct v4l2_capability *cap) 7608c2ecf20Sopenharmony_ci{ 7618c2ecf20Sopenharmony_ci struct tw686x_video_channel *vc = video_drvdata(file); 7628c2ecf20Sopenharmony_ci struct tw686x_dev *dev = vc->dev; 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci strscpy(cap->driver, "tw686x", sizeof(cap->driver)); 7658c2ecf20Sopenharmony_ci strscpy(cap->card, dev->name, sizeof(cap->card)); 7668c2ecf20Sopenharmony_ci snprintf(cap->bus_info, sizeof(cap->bus_info), 7678c2ecf20Sopenharmony_ci "PCI:%s", pci_name(dev->pci_dev)); 7688c2ecf20Sopenharmony_ci return 0; 7698c2ecf20Sopenharmony_ci} 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_cistatic int tw686x_set_standard(struct tw686x_video_channel *vc, v4l2_std_id id) 7728c2ecf20Sopenharmony_ci{ 7738c2ecf20Sopenharmony_ci u32 val; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci if (id & V4L2_STD_NTSC) 7768c2ecf20Sopenharmony_ci val = 0; 7778c2ecf20Sopenharmony_ci else if (id & V4L2_STD_PAL) 7788c2ecf20Sopenharmony_ci val = 1; 7798c2ecf20Sopenharmony_ci else if (id & V4L2_STD_SECAM) 7808c2ecf20Sopenharmony_ci val = 2; 7818c2ecf20Sopenharmony_ci else if (id & V4L2_STD_NTSC_443) 7828c2ecf20Sopenharmony_ci val = 3; 7838c2ecf20Sopenharmony_ci else if (id & V4L2_STD_PAL_M) 7848c2ecf20Sopenharmony_ci val = 4; 7858c2ecf20Sopenharmony_ci else if (id & V4L2_STD_PAL_Nc) 7868c2ecf20Sopenharmony_ci val = 5; 7878c2ecf20Sopenharmony_ci else if (id & V4L2_STD_PAL_60) 7888c2ecf20Sopenharmony_ci val = 6; 7898c2ecf20Sopenharmony_ci else 7908c2ecf20Sopenharmony_ci return -EINVAL; 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci vc->video_standard = id; 7938c2ecf20Sopenharmony_ci reg_write(vc->dev, SDT[vc->ch], val); 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci val = reg_read(vc->dev, VIDEO_CONTROL1); 7968c2ecf20Sopenharmony_ci if (id & V4L2_STD_525_60) 7978c2ecf20Sopenharmony_ci val &= ~(1 << (SYS_MODE_DMA_SHIFT + vc->ch)); 7988c2ecf20Sopenharmony_ci else 7998c2ecf20Sopenharmony_ci val |= (1 << (SYS_MODE_DMA_SHIFT + vc->ch)); 8008c2ecf20Sopenharmony_ci reg_write(vc->dev, VIDEO_CONTROL1, val); 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci return 0; 8038c2ecf20Sopenharmony_ci} 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_cistatic int tw686x_s_std(struct file *file, void *priv, v4l2_std_id id) 8068c2ecf20Sopenharmony_ci{ 8078c2ecf20Sopenharmony_ci struct tw686x_video_channel *vc = video_drvdata(file); 8088c2ecf20Sopenharmony_ci struct v4l2_format f; 8098c2ecf20Sopenharmony_ci int ret; 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci if (vc->video_standard == id) 8128c2ecf20Sopenharmony_ci return 0; 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci if (vb2_is_busy(&vc->vidq)) 8158c2ecf20Sopenharmony_ci return -EBUSY; 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci ret = tw686x_set_standard(vc, id); 8188c2ecf20Sopenharmony_ci if (ret) 8198c2ecf20Sopenharmony_ci return ret; 8208c2ecf20Sopenharmony_ci /* 8218c2ecf20Sopenharmony_ci * Adjust format after V4L2_STD_525_60/V4L2_STD_625_50 change, 8228c2ecf20Sopenharmony_ci * calling g_fmt and s_fmt will sanitize the height 8238c2ecf20Sopenharmony_ci * according to the standard. 8248c2ecf20Sopenharmony_ci */ 8258c2ecf20Sopenharmony_ci tw686x_g_fmt_vid_cap(file, priv, &f); 8268c2ecf20Sopenharmony_ci tw686x_s_fmt_vid_cap(file, priv, &f); 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci /* 8298c2ecf20Sopenharmony_ci * Frame decimation depends on the chosen standard, 8308c2ecf20Sopenharmony_ci * so reset it to the current value. 8318c2ecf20Sopenharmony_ci */ 8328c2ecf20Sopenharmony_ci tw686x_set_framerate(vc, vc->fps); 8338c2ecf20Sopenharmony_ci return 0; 8348c2ecf20Sopenharmony_ci} 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_cistatic int tw686x_querystd(struct file *file, void *priv, v4l2_std_id *std) 8378c2ecf20Sopenharmony_ci{ 8388c2ecf20Sopenharmony_ci struct tw686x_video_channel *vc = video_drvdata(file); 8398c2ecf20Sopenharmony_ci struct tw686x_dev *dev = vc->dev; 8408c2ecf20Sopenharmony_ci unsigned int old_std, detected_std = 0; 8418c2ecf20Sopenharmony_ci unsigned long end; 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci if (vb2_is_streaming(&vc->vidq)) 8448c2ecf20Sopenharmony_ci return -EBUSY; 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci /* Enable and start standard detection */ 8478c2ecf20Sopenharmony_ci old_std = reg_read(dev, SDT[vc->ch]); 8488c2ecf20Sopenharmony_ci reg_write(dev, SDT[vc->ch], 0x7); 8498c2ecf20Sopenharmony_ci reg_write(dev, SDT_EN[vc->ch], 0xff); 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci end = jiffies + msecs_to_jiffies(500); 8528c2ecf20Sopenharmony_ci while (time_is_after_jiffies(end)) { 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci detected_std = reg_read(dev, SDT[vc->ch]); 8558c2ecf20Sopenharmony_ci if (!(detected_std & BIT(7))) 8568c2ecf20Sopenharmony_ci break; 8578c2ecf20Sopenharmony_ci msleep(100); 8588c2ecf20Sopenharmony_ci } 8598c2ecf20Sopenharmony_ci reg_write(dev, SDT[vc->ch], old_std); 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci /* Exit if still busy */ 8628c2ecf20Sopenharmony_ci if (detected_std & BIT(7)) 8638c2ecf20Sopenharmony_ci return 0; 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci detected_std = (detected_std >> 4) & 0x7; 8668c2ecf20Sopenharmony_ci switch (detected_std) { 8678c2ecf20Sopenharmony_ci case TW686X_STD_NTSC_M: 8688c2ecf20Sopenharmony_ci *std &= V4L2_STD_NTSC; 8698c2ecf20Sopenharmony_ci break; 8708c2ecf20Sopenharmony_ci case TW686X_STD_NTSC_443: 8718c2ecf20Sopenharmony_ci *std &= V4L2_STD_NTSC_443; 8728c2ecf20Sopenharmony_ci break; 8738c2ecf20Sopenharmony_ci case TW686X_STD_PAL_M: 8748c2ecf20Sopenharmony_ci *std &= V4L2_STD_PAL_M; 8758c2ecf20Sopenharmony_ci break; 8768c2ecf20Sopenharmony_ci case TW686X_STD_PAL_60: 8778c2ecf20Sopenharmony_ci *std &= V4L2_STD_PAL_60; 8788c2ecf20Sopenharmony_ci break; 8798c2ecf20Sopenharmony_ci case TW686X_STD_PAL: 8808c2ecf20Sopenharmony_ci *std &= V4L2_STD_PAL; 8818c2ecf20Sopenharmony_ci break; 8828c2ecf20Sopenharmony_ci case TW686X_STD_PAL_CN: 8838c2ecf20Sopenharmony_ci *std &= V4L2_STD_PAL_Nc; 8848c2ecf20Sopenharmony_ci break; 8858c2ecf20Sopenharmony_ci case TW686X_STD_SECAM: 8868c2ecf20Sopenharmony_ci *std &= V4L2_STD_SECAM; 8878c2ecf20Sopenharmony_ci break; 8888c2ecf20Sopenharmony_ci default: 8898c2ecf20Sopenharmony_ci *std = 0; 8908c2ecf20Sopenharmony_ci } 8918c2ecf20Sopenharmony_ci return 0; 8928c2ecf20Sopenharmony_ci} 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_cistatic int tw686x_g_std(struct file *file, void *priv, v4l2_std_id *id) 8958c2ecf20Sopenharmony_ci{ 8968c2ecf20Sopenharmony_ci struct tw686x_video_channel *vc = video_drvdata(file); 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci *id = vc->video_standard; 8998c2ecf20Sopenharmony_ci return 0; 9008c2ecf20Sopenharmony_ci} 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_cistatic int tw686x_enum_framesizes(struct file *file, void *priv, 9038c2ecf20Sopenharmony_ci struct v4l2_frmsizeenum *fsize) 9048c2ecf20Sopenharmony_ci{ 9058c2ecf20Sopenharmony_ci struct tw686x_video_channel *vc = video_drvdata(file); 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci if (fsize->index) 9088c2ecf20Sopenharmony_ci return -EINVAL; 9098c2ecf20Sopenharmony_ci fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; 9108c2ecf20Sopenharmony_ci fsize->stepwise.max_width = TW686X_VIDEO_WIDTH; 9118c2ecf20Sopenharmony_ci fsize->stepwise.min_width = fsize->stepwise.max_width / 2; 9128c2ecf20Sopenharmony_ci fsize->stepwise.step_width = fsize->stepwise.min_width; 9138c2ecf20Sopenharmony_ci fsize->stepwise.max_height = TW686X_VIDEO_HEIGHT(vc->video_standard); 9148c2ecf20Sopenharmony_ci fsize->stepwise.min_height = fsize->stepwise.max_height / 2; 9158c2ecf20Sopenharmony_ci fsize->stepwise.step_height = fsize->stepwise.min_height; 9168c2ecf20Sopenharmony_ci return 0; 9178c2ecf20Sopenharmony_ci} 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_cistatic int tw686x_enum_frameintervals(struct file *file, void *priv, 9208c2ecf20Sopenharmony_ci struct v4l2_frmivalenum *ival) 9218c2ecf20Sopenharmony_ci{ 9228c2ecf20Sopenharmony_ci struct tw686x_video_channel *vc = video_drvdata(file); 9238c2ecf20Sopenharmony_ci int max_fps = TW686X_MAX_FPS(vc->video_standard); 9248c2ecf20Sopenharmony_ci int max_rates = DIV_ROUND_UP(max_fps, 2); 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci if (ival->index >= max_rates) 9278c2ecf20Sopenharmony_ci return -EINVAL; 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci ival->type = V4L2_FRMIVAL_TYPE_DISCRETE; 9308c2ecf20Sopenharmony_ci ival->discrete.numerator = 1; 9318c2ecf20Sopenharmony_ci if (ival->index < (max_rates - 1)) 9328c2ecf20Sopenharmony_ci ival->discrete.denominator = (ival->index + 1) * 2; 9338c2ecf20Sopenharmony_ci else 9348c2ecf20Sopenharmony_ci ival->discrete.denominator = max_fps; 9358c2ecf20Sopenharmony_ci return 0; 9368c2ecf20Sopenharmony_ci} 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_cistatic int tw686x_g_parm(struct file *file, void *priv, 9398c2ecf20Sopenharmony_ci struct v4l2_streamparm *sp) 9408c2ecf20Sopenharmony_ci{ 9418c2ecf20Sopenharmony_ci struct tw686x_video_channel *vc = video_drvdata(file); 9428c2ecf20Sopenharmony_ci struct v4l2_captureparm *cp = &sp->parm.capture; 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) 9458c2ecf20Sopenharmony_ci return -EINVAL; 9468c2ecf20Sopenharmony_ci sp->parm.capture.readbuffers = 3; 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci cp->capability = V4L2_CAP_TIMEPERFRAME; 9498c2ecf20Sopenharmony_ci cp->timeperframe.numerator = 1; 9508c2ecf20Sopenharmony_ci cp->timeperframe.denominator = vc->fps; 9518c2ecf20Sopenharmony_ci return 0; 9528c2ecf20Sopenharmony_ci} 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_cistatic int tw686x_s_parm(struct file *file, void *priv, 9558c2ecf20Sopenharmony_ci struct v4l2_streamparm *sp) 9568c2ecf20Sopenharmony_ci{ 9578c2ecf20Sopenharmony_ci struct tw686x_video_channel *vc = video_drvdata(file); 9588c2ecf20Sopenharmony_ci struct v4l2_captureparm *cp = &sp->parm.capture; 9598c2ecf20Sopenharmony_ci unsigned int denominator = cp->timeperframe.denominator; 9608c2ecf20Sopenharmony_ci unsigned int numerator = cp->timeperframe.numerator; 9618c2ecf20Sopenharmony_ci unsigned int fps; 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci if (vb2_is_busy(&vc->vidq)) 9648c2ecf20Sopenharmony_ci return -EBUSY; 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci fps = (!numerator || !denominator) ? 0 : denominator / numerator; 9678c2ecf20Sopenharmony_ci if (vc->fps != fps) 9688c2ecf20Sopenharmony_ci tw686x_set_framerate(vc, fps); 9698c2ecf20Sopenharmony_ci return tw686x_g_parm(file, priv, sp); 9708c2ecf20Sopenharmony_ci} 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_cistatic int tw686x_enum_fmt_vid_cap(struct file *file, void *priv, 9738c2ecf20Sopenharmony_ci struct v4l2_fmtdesc *f) 9748c2ecf20Sopenharmony_ci{ 9758c2ecf20Sopenharmony_ci if (f->index >= ARRAY_SIZE(formats)) 9768c2ecf20Sopenharmony_ci return -EINVAL; 9778c2ecf20Sopenharmony_ci f->pixelformat = formats[f->index].fourcc; 9788c2ecf20Sopenharmony_ci return 0; 9798c2ecf20Sopenharmony_ci} 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_cistatic void tw686x_set_input(struct tw686x_video_channel *vc, unsigned int i) 9828c2ecf20Sopenharmony_ci{ 9838c2ecf20Sopenharmony_ci u32 val; 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci vc->input = i; 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci val = reg_read(vc->dev, VDMA_CHANNEL_CONFIG[vc->ch]); 9888c2ecf20Sopenharmony_ci val &= ~(0x3 << 30); 9898c2ecf20Sopenharmony_ci val |= i << 30; 9908c2ecf20Sopenharmony_ci reg_write(vc->dev, VDMA_CHANNEL_CONFIG[vc->ch], val); 9918c2ecf20Sopenharmony_ci} 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_cistatic int tw686x_s_input(struct file *file, void *priv, unsigned int i) 9948c2ecf20Sopenharmony_ci{ 9958c2ecf20Sopenharmony_ci struct tw686x_video_channel *vc = video_drvdata(file); 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci if (i >= TW686X_INPUTS_PER_CH) 9988c2ecf20Sopenharmony_ci return -EINVAL; 9998c2ecf20Sopenharmony_ci if (i == vc->input) 10008c2ecf20Sopenharmony_ci return 0; 10018c2ecf20Sopenharmony_ci /* 10028c2ecf20Sopenharmony_ci * Not sure we are able to support on the fly input change 10038c2ecf20Sopenharmony_ci */ 10048c2ecf20Sopenharmony_ci if (vb2_is_busy(&vc->vidq)) 10058c2ecf20Sopenharmony_ci return -EBUSY; 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci tw686x_set_input(vc, i); 10088c2ecf20Sopenharmony_ci return 0; 10098c2ecf20Sopenharmony_ci} 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_cistatic int tw686x_g_input(struct file *file, void *priv, unsigned int *i) 10128c2ecf20Sopenharmony_ci{ 10138c2ecf20Sopenharmony_ci struct tw686x_video_channel *vc = video_drvdata(file); 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci *i = vc->input; 10168c2ecf20Sopenharmony_ci return 0; 10178c2ecf20Sopenharmony_ci} 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_cistatic int tw686x_enum_input(struct file *file, void *priv, 10208c2ecf20Sopenharmony_ci struct v4l2_input *i) 10218c2ecf20Sopenharmony_ci{ 10228c2ecf20Sopenharmony_ci struct tw686x_video_channel *vc = video_drvdata(file); 10238c2ecf20Sopenharmony_ci unsigned int vidstat; 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci if (i->index >= TW686X_INPUTS_PER_CH) 10268c2ecf20Sopenharmony_ci return -EINVAL; 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci snprintf(i->name, sizeof(i->name), "Composite%d", i->index); 10298c2ecf20Sopenharmony_ci i->type = V4L2_INPUT_TYPE_CAMERA; 10308c2ecf20Sopenharmony_ci i->std = vc->device->tvnorms; 10318c2ecf20Sopenharmony_ci i->capabilities = V4L2_IN_CAP_STD; 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci vidstat = reg_read(vc->dev, VIDSTAT[vc->ch]); 10348c2ecf20Sopenharmony_ci i->status = 0; 10358c2ecf20Sopenharmony_ci if (vidstat & TW686X_VIDSTAT_VDLOSS) 10368c2ecf20Sopenharmony_ci i->status |= V4L2_IN_ST_NO_SIGNAL; 10378c2ecf20Sopenharmony_ci if (!(vidstat & TW686X_VIDSTAT_HLOCK)) 10388c2ecf20Sopenharmony_ci i->status |= V4L2_IN_ST_NO_H_LOCK; 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci return 0; 10418c2ecf20Sopenharmony_ci} 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_cistatic const struct v4l2_file_operations tw686x_video_fops = { 10448c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 10458c2ecf20Sopenharmony_ci .open = v4l2_fh_open, 10468c2ecf20Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 10478c2ecf20Sopenharmony_ci .release = vb2_fop_release, 10488c2ecf20Sopenharmony_ci .poll = vb2_fop_poll, 10498c2ecf20Sopenharmony_ci .read = vb2_fop_read, 10508c2ecf20Sopenharmony_ci .mmap = vb2_fop_mmap, 10518c2ecf20Sopenharmony_ci}; 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_cistatic const struct v4l2_ioctl_ops tw686x_video_ioctl_ops = { 10548c2ecf20Sopenharmony_ci .vidioc_querycap = tw686x_querycap, 10558c2ecf20Sopenharmony_ci .vidioc_g_fmt_vid_cap = tw686x_g_fmt_vid_cap, 10568c2ecf20Sopenharmony_ci .vidioc_s_fmt_vid_cap = tw686x_s_fmt_vid_cap, 10578c2ecf20Sopenharmony_ci .vidioc_enum_fmt_vid_cap = tw686x_enum_fmt_vid_cap, 10588c2ecf20Sopenharmony_ci .vidioc_try_fmt_vid_cap = tw686x_try_fmt_vid_cap, 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci .vidioc_querystd = tw686x_querystd, 10618c2ecf20Sopenharmony_ci .vidioc_g_std = tw686x_g_std, 10628c2ecf20Sopenharmony_ci .vidioc_s_std = tw686x_s_std, 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci .vidioc_g_parm = tw686x_g_parm, 10658c2ecf20Sopenharmony_ci .vidioc_s_parm = tw686x_s_parm, 10668c2ecf20Sopenharmony_ci .vidioc_enum_framesizes = tw686x_enum_framesizes, 10678c2ecf20Sopenharmony_ci .vidioc_enum_frameintervals = tw686x_enum_frameintervals, 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci .vidioc_enum_input = tw686x_enum_input, 10708c2ecf20Sopenharmony_ci .vidioc_g_input = tw686x_g_input, 10718c2ecf20Sopenharmony_ci .vidioc_s_input = tw686x_s_input, 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci .vidioc_reqbufs = vb2_ioctl_reqbufs, 10748c2ecf20Sopenharmony_ci .vidioc_querybuf = vb2_ioctl_querybuf, 10758c2ecf20Sopenharmony_ci .vidioc_qbuf = vb2_ioctl_qbuf, 10768c2ecf20Sopenharmony_ci .vidioc_dqbuf = vb2_ioctl_dqbuf, 10778c2ecf20Sopenharmony_ci .vidioc_create_bufs = vb2_ioctl_create_bufs, 10788c2ecf20Sopenharmony_ci .vidioc_streamon = vb2_ioctl_streamon, 10798c2ecf20Sopenharmony_ci .vidioc_streamoff = vb2_ioctl_streamoff, 10808c2ecf20Sopenharmony_ci .vidioc_prepare_buf = vb2_ioctl_prepare_buf, 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci .vidioc_log_status = v4l2_ctrl_log_status, 10838c2ecf20Sopenharmony_ci .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, 10848c2ecf20Sopenharmony_ci .vidioc_unsubscribe_event = v4l2_event_unsubscribe, 10858c2ecf20Sopenharmony_ci}; 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_civoid tw686x_video_irq(struct tw686x_dev *dev, unsigned long requests, 10888c2ecf20Sopenharmony_ci unsigned int pb_status, unsigned int fifo_status, 10898c2ecf20Sopenharmony_ci unsigned int *reset_ch) 10908c2ecf20Sopenharmony_ci{ 10918c2ecf20Sopenharmony_ci struct tw686x_video_channel *vc; 10928c2ecf20Sopenharmony_ci unsigned long flags; 10938c2ecf20Sopenharmony_ci unsigned int ch, pb; 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci for_each_set_bit(ch, &requests, max_channels(dev)) { 10968c2ecf20Sopenharmony_ci vc = &dev->video_channels[ch]; 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci /* 10998c2ecf20Sopenharmony_ci * This can either be a blue frame (with signal-lost bit set) 11008c2ecf20Sopenharmony_ci * or a good frame (with signal-lost bit clear). If we have just 11018c2ecf20Sopenharmony_ci * got signal, then this channel needs resetting. 11028c2ecf20Sopenharmony_ci */ 11038c2ecf20Sopenharmony_ci if (vc->no_signal && !(fifo_status & BIT(ch))) { 11048c2ecf20Sopenharmony_ci v4l2_printk(KERN_DEBUG, &dev->v4l2_dev, 11058c2ecf20Sopenharmony_ci "video%d: signal recovered\n", vc->num); 11068c2ecf20Sopenharmony_ci vc->no_signal = false; 11078c2ecf20Sopenharmony_ci *reset_ch |= BIT(ch); 11088c2ecf20Sopenharmony_ci vc->pb = 0; 11098c2ecf20Sopenharmony_ci continue; 11108c2ecf20Sopenharmony_ci } 11118c2ecf20Sopenharmony_ci vc->no_signal = !!(fifo_status & BIT(ch)); 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_ci /* Check FIFO errors only if there's signal */ 11148c2ecf20Sopenharmony_ci if (!vc->no_signal) { 11158c2ecf20Sopenharmony_ci u32 fifo_ov, fifo_bad; 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci fifo_ov = (fifo_status >> 24) & BIT(ch); 11188c2ecf20Sopenharmony_ci fifo_bad = (fifo_status >> 16) & BIT(ch); 11198c2ecf20Sopenharmony_ci if (fifo_ov || fifo_bad) { 11208c2ecf20Sopenharmony_ci /* Mark this channel for reset */ 11218c2ecf20Sopenharmony_ci v4l2_printk(KERN_DEBUG, &dev->v4l2_dev, 11228c2ecf20Sopenharmony_ci "video%d: FIFO error\n", vc->num); 11238c2ecf20Sopenharmony_ci *reset_ch |= BIT(ch); 11248c2ecf20Sopenharmony_ci vc->pb = 0; 11258c2ecf20Sopenharmony_ci continue; 11268c2ecf20Sopenharmony_ci } 11278c2ecf20Sopenharmony_ci } 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci pb = !!(pb_status & BIT(ch)); 11308c2ecf20Sopenharmony_ci if (vc->pb != pb) { 11318c2ecf20Sopenharmony_ci /* Mark this channel for reset */ 11328c2ecf20Sopenharmony_ci v4l2_printk(KERN_DEBUG, &dev->v4l2_dev, 11338c2ecf20Sopenharmony_ci "video%d: unexpected p-b buffer!\n", 11348c2ecf20Sopenharmony_ci vc->num); 11358c2ecf20Sopenharmony_ci *reset_ch |= BIT(ch); 11368c2ecf20Sopenharmony_ci vc->pb = 0; 11378c2ecf20Sopenharmony_ci continue; 11388c2ecf20Sopenharmony_ci } 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci spin_lock_irqsave(&vc->qlock, flags); 11418c2ecf20Sopenharmony_ci tw686x_buf_done(vc, pb); 11428c2ecf20Sopenharmony_ci dev->dma_ops->buf_refill(vc, pb); 11438c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vc->qlock, flags); 11448c2ecf20Sopenharmony_ci } 11458c2ecf20Sopenharmony_ci} 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_civoid tw686x_video_free(struct tw686x_dev *dev) 11488c2ecf20Sopenharmony_ci{ 11498c2ecf20Sopenharmony_ci unsigned int ch, pb; 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci for (ch = 0; ch < max_channels(dev); ch++) { 11528c2ecf20Sopenharmony_ci struct tw686x_video_channel *vc = &dev->video_channels[ch]; 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci video_unregister_device(vc->device); 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_ci if (dev->dma_ops->free) 11578c2ecf20Sopenharmony_ci for (pb = 0; pb < 2; pb++) 11588c2ecf20Sopenharmony_ci dev->dma_ops->free(vc, pb); 11598c2ecf20Sopenharmony_ci } 11608c2ecf20Sopenharmony_ci} 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ciint tw686x_video_init(struct tw686x_dev *dev) 11638c2ecf20Sopenharmony_ci{ 11648c2ecf20Sopenharmony_ci unsigned int ch, val; 11658c2ecf20Sopenharmony_ci int err; 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci if (dev->dma_mode == TW686X_DMA_MODE_MEMCPY) 11688c2ecf20Sopenharmony_ci dev->dma_ops = &memcpy_dma_ops; 11698c2ecf20Sopenharmony_ci else if (dev->dma_mode == TW686X_DMA_MODE_CONTIG) 11708c2ecf20Sopenharmony_ci dev->dma_ops = &contig_dma_ops; 11718c2ecf20Sopenharmony_ci else if (dev->dma_mode == TW686X_DMA_MODE_SG) 11728c2ecf20Sopenharmony_ci dev->dma_ops = &sg_dma_ops; 11738c2ecf20Sopenharmony_ci else 11748c2ecf20Sopenharmony_ci return -EINVAL; 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_ci err = v4l2_device_register(&dev->pci_dev->dev, &dev->v4l2_dev); 11778c2ecf20Sopenharmony_ci if (err) 11788c2ecf20Sopenharmony_ci return err; 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci if (dev->dma_ops->setup) { 11818c2ecf20Sopenharmony_ci err = dev->dma_ops->setup(dev); 11828c2ecf20Sopenharmony_ci if (err) 11838c2ecf20Sopenharmony_ci return err; 11848c2ecf20Sopenharmony_ci } 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci /* Initialize vc->dev and vc->ch for the error path */ 11878c2ecf20Sopenharmony_ci for (ch = 0; ch < max_channels(dev); ch++) { 11888c2ecf20Sopenharmony_ci struct tw686x_video_channel *vc = &dev->video_channels[ch]; 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci vc->dev = dev; 11918c2ecf20Sopenharmony_ci vc->ch = ch; 11928c2ecf20Sopenharmony_ci } 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_ci for (ch = 0; ch < max_channels(dev); ch++) { 11958c2ecf20Sopenharmony_ci struct tw686x_video_channel *vc = &dev->video_channels[ch]; 11968c2ecf20Sopenharmony_ci struct video_device *vdev; 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_ci mutex_init(&vc->vb_mutex); 11998c2ecf20Sopenharmony_ci spin_lock_init(&vc->qlock); 12008c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&vc->vidq_queued); 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_ci /* default settings */ 12038c2ecf20Sopenharmony_ci err = tw686x_set_standard(vc, V4L2_STD_NTSC); 12048c2ecf20Sopenharmony_ci if (err) 12058c2ecf20Sopenharmony_ci goto error; 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_ci err = tw686x_set_format(vc, formats[0].fourcc, 12088c2ecf20Sopenharmony_ci TW686X_VIDEO_WIDTH, 12098c2ecf20Sopenharmony_ci TW686X_VIDEO_HEIGHT(vc->video_standard), 12108c2ecf20Sopenharmony_ci true); 12118c2ecf20Sopenharmony_ci if (err) 12128c2ecf20Sopenharmony_ci goto error; 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_ci tw686x_set_input(vc, 0); 12158c2ecf20Sopenharmony_ci tw686x_set_framerate(vc, 30); 12168c2ecf20Sopenharmony_ci reg_write(dev, VDELAY_LO[ch], 0x14); 12178c2ecf20Sopenharmony_ci reg_write(dev, HACTIVE_LO[ch], 0xd0); 12188c2ecf20Sopenharmony_ci reg_write(dev, VIDEO_SIZE[ch], 0); 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci vc->vidq.io_modes = VB2_READ | VB2_MMAP | VB2_DMABUF; 12218c2ecf20Sopenharmony_ci vc->vidq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 12228c2ecf20Sopenharmony_ci vc->vidq.drv_priv = vc; 12238c2ecf20Sopenharmony_ci vc->vidq.buf_struct_size = sizeof(struct tw686x_v4l2_buf); 12248c2ecf20Sopenharmony_ci vc->vidq.ops = &tw686x_video_qops; 12258c2ecf20Sopenharmony_ci vc->vidq.mem_ops = dev->dma_ops->mem_ops; 12268c2ecf20Sopenharmony_ci vc->vidq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; 12278c2ecf20Sopenharmony_ci vc->vidq.min_buffers_needed = 2; 12288c2ecf20Sopenharmony_ci vc->vidq.lock = &vc->vb_mutex; 12298c2ecf20Sopenharmony_ci vc->vidq.gfp_flags = dev->dma_mode != TW686X_DMA_MODE_MEMCPY ? 12308c2ecf20Sopenharmony_ci GFP_DMA32 : 0; 12318c2ecf20Sopenharmony_ci vc->vidq.dev = &dev->pci_dev->dev; 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci err = vb2_queue_init(&vc->vidq); 12348c2ecf20Sopenharmony_ci if (err) { 12358c2ecf20Sopenharmony_ci v4l2_err(&dev->v4l2_dev, 12368c2ecf20Sopenharmony_ci "dma%d: cannot init vb2 queue\n", ch); 12378c2ecf20Sopenharmony_ci goto error; 12388c2ecf20Sopenharmony_ci } 12398c2ecf20Sopenharmony_ci 12408c2ecf20Sopenharmony_ci err = v4l2_ctrl_handler_init(&vc->ctrl_handler, 4); 12418c2ecf20Sopenharmony_ci if (err) { 12428c2ecf20Sopenharmony_ci v4l2_err(&dev->v4l2_dev, 12438c2ecf20Sopenharmony_ci "dma%d: cannot init ctrl handler\n", ch); 12448c2ecf20Sopenharmony_ci goto error; 12458c2ecf20Sopenharmony_ci } 12468c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(&vc->ctrl_handler, &ctrl_ops, 12478c2ecf20Sopenharmony_ci V4L2_CID_BRIGHTNESS, -128, 127, 1, 0); 12488c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(&vc->ctrl_handler, &ctrl_ops, 12498c2ecf20Sopenharmony_ci V4L2_CID_CONTRAST, 0, 255, 1, 100); 12508c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(&vc->ctrl_handler, &ctrl_ops, 12518c2ecf20Sopenharmony_ci V4L2_CID_SATURATION, 0, 255, 1, 128); 12528c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(&vc->ctrl_handler, &ctrl_ops, 12538c2ecf20Sopenharmony_ci V4L2_CID_HUE, -128, 127, 1, 0); 12548c2ecf20Sopenharmony_ci err = vc->ctrl_handler.error; 12558c2ecf20Sopenharmony_ci if (err) 12568c2ecf20Sopenharmony_ci goto error; 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_ci err = v4l2_ctrl_handler_setup(&vc->ctrl_handler); 12598c2ecf20Sopenharmony_ci if (err) 12608c2ecf20Sopenharmony_ci goto error; 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci vdev = video_device_alloc(); 12638c2ecf20Sopenharmony_ci if (!vdev) { 12648c2ecf20Sopenharmony_ci v4l2_err(&dev->v4l2_dev, 12658c2ecf20Sopenharmony_ci "dma%d: unable to allocate device\n", ch); 12668c2ecf20Sopenharmony_ci err = -ENOMEM; 12678c2ecf20Sopenharmony_ci goto error; 12688c2ecf20Sopenharmony_ci } 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci snprintf(vdev->name, sizeof(vdev->name), "%s video", dev->name); 12718c2ecf20Sopenharmony_ci vdev->fops = &tw686x_video_fops; 12728c2ecf20Sopenharmony_ci vdev->ioctl_ops = &tw686x_video_ioctl_ops; 12738c2ecf20Sopenharmony_ci vdev->release = video_device_release; 12748c2ecf20Sopenharmony_ci vdev->v4l2_dev = &dev->v4l2_dev; 12758c2ecf20Sopenharmony_ci vdev->queue = &vc->vidq; 12768c2ecf20Sopenharmony_ci vdev->tvnorms = V4L2_STD_525_60 | V4L2_STD_625_50; 12778c2ecf20Sopenharmony_ci vdev->minor = -1; 12788c2ecf20Sopenharmony_ci vdev->lock = &vc->vb_mutex; 12798c2ecf20Sopenharmony_ci vdev->ctrl_handler = &vc->ctrl_handler; 12808c2ecf20Sopenharmony_ci vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | 12818c2ecf20Sopenharmony_ci V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; 12828c2ecf20Sopenharmony_ci vc->device = vdev; 12838c2ecf20Sopenharmony_ci video_set_drvdata(vdev, vc); 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_ci err = video_register_device(vdev, VFL_TYPE_VIDEO, -1); 12868c2ecf20Sopenharmony_ci if (err < 0) { 12878c2ecf20Sopenharmony_ci video_device_release(vdev); 12888c2ecf20Sopenharmony_ci goto error; 12898c2ecf20Sopenharmony_ci } 12908c2ecf20Sopenharmony_ci vc->num = vdev->num; 12918c2ecf20Sopenharmony_ci } 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci val = TW686X_DEF_PHASE_REF; 12948c2ecf20Sopenharmony_ci for (ch = 0; ch < max_channels(dev); ch++) 12958c2ecf20Sopenharmony_ci val |= dev->dma_ops->hw_dma_mode << (16 + ch * 2); 12968c2ecf20Sopenharmony_ci reg_write(dev, PHASE_REF, val); 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci reg_write(dev, MISC2[0], 0xe7); 12998c2ecf20Sopenharmony_ci reg_write(dev, VCTRL1[0], 0xcc); 13008c2ecf20Sopenharmony_ci reg_write(dev, LOOP[0], 0xa5); 13018c2ecf20Sopenharmony_ci if (max_channels(dev) > 4) { 13028c2ecf20Sopenharmony_ci reg_write(dev, VCTRL1[1], 0xcc); 13038c2ecf20Sopenharmony_ci reg_write(dev, LOOP[1], 0xa5); 13048c2ecf20Sopenharmony_ci reg_write(dev, MISC2[1], 0xe7); 13058c2ecf20Sopenharmony_ci } 13068c2ecf20Sopenharmony_ci return 0; 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_cierror: 13098c2ecf20Sopenharmony_ci tw686x_video_free(dev); 13108c2ecf20Sopenharmony_ci return err; 13118c2ecf20Sopenharmony_ci} 1312