162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2015 VanguardiaSur - www.vanguardiasur.com.ar 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Based on original driver by Krzysztof Ha?asa: 662306a36Sopenharmony_ci * Copyright (C) 2015 Industrial Research Institute for Automation 762306a36Sopenharmony_ci * and Measurements PIAP 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/init.h> 1162306a36Sopenharmony_ci#include <linux/delay.h> 1262306a36Sopenharmony_ci#include <linux/list.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/kernel.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci#include <media/v4l2-common.h> 1762306a36Sopenharmony_ci#include <media/v4l2-event.h> 1862306a36Sopenharmony_ci#include <media/videobuf2-dma-contig.h> 1962306a36Sopenharmony_ci#include <media/videobuf2-dma-sg.h> 2062306a36Sopenharmony_ci#include <media/videobuf2-vmalloc.h> 2162306a36Sopenharmony_ci#include "tw686x.h" 2262306a36Sopenharmony_ci#include "tw686x-regs.h" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define TW686X_INPUTS_PER_CH 4 2562306a36Sopenharmony_ci#define TW686X_VIDEO_WIDTH 720 2662306a36Sopenharmony_ci#define TW686X_VIDEO_HEIGHT(id) ((id & V4L2_STD_525_60) ? 480 : 576) 2762306a36Sopenharmony_ci#define TW686X_MAX_FPS(id) ((id & V4L2_STD_525_60) ? 30 : 25) 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define TW686X_MAX_SG_ENTRY_SIZE 4096 3062306a36Sopenharmony_ci#define TW686X_MAX_SG_DESC_COUNT 256 /* PAL 720x576 needs 203 4-KB pages */ 3162306a36Sopenharmony_ci#define TW686X_SG_TABLE_SIZE (TW686X_MAX_SG_DESC_COUNT * sizeof(struct tw686x_sg_desc)) 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic const struct tw686x_format formats[] = { 3462306a36Sopenharmony_ci { 3562306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_UYVY, 3662306a36Sopenharmony_ci .mode = 0, 3762306a36Sopenharmony_ci .depth = 16, 3862306a36Sopenharmony_ci }, { 3962306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_RGB565, 4062306a36Sopenharmony_ci .mode = 5, 4162306a36Sopenharmony_ci .depth = 16, 4262306a36Sopenharmony_ci }, { 4362306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_YUYV, 4462306a36Sopenharmony_ci .mode = 6, 4562306a36Sopenharmony_ci .depth = 16, 4662306a36Sopenharmony_ci } 4762306a36Sopenharmony_ci}; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic void tw686x_buf_done(struct tw686x_video_channel *vc, 5062306a36Sopenharmony_ci unsigned int pb) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci struct tw686x_dma_desc *desc = &vc->dma_descs[pb]; 5362306a36Sopenharmony_ci struct tw686x_dev *dev = vc->dev; 5462306a36Sopenharmony_ci struct vb2_v4l2_buffer *vb; 5562306a36Sopenharmony_ci struct vb2_buffer *vb2_buf; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci if (vc->curr_bufs[pb]) { 5862306a36Sopenharmony_ci vb = &vc->curr_bufs[pb]->vb; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci vb->field = dev->dma_ops->field; 6162306a36Sopenharmony_ci vb->sequence = vc->sequence++; 6262306a36Sopenharmony_ci vb2_buf = &vb->vb2_buf; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci if (dev->dma_mode == TW686X_DMA_MODE_MEMCPY) 6562306a36Sopenharmony_ci memcpy(vb2_plane_vaddr(vb2_buf, 0), desc->virt, 6662306a36Sopenharmony_ci desc->size); 6762306a36Sopenharmony_ci vb2_buf->timestamp = ktime_get_ns(); 6862306a36Sopenharmony_ci vb2_buffer_done(vb2_buf, VB2_BUF_STATE_DONE); 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci vc->pb = !pb; 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci/* 7562306a36Sopenharmony_ci * We can call this even when alloc_dma failed for the given channel 7662306a36Sopenharmony_ci */ 7762306a36Sopenharmony_cistatic void tw686x_memcpy_dma_free(struct tw686x_video_channel *vc, 7862306a36Sopenharmony_ci unsigned int pb) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci struct tw686x_dma_desc *desc = &vc->dma_descs[pb]; 8162306a36Sopenharmony_ci struct tw686x_dev *dev = vc->dev; 8262306a36Sopenharmony_ci struct pci_dev *pci_dev; 8362306a36Sopenharmony_ci unsigned long flags; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci /* Check device presence. Shouldn't really happen! */ 8662306a36Sopenharmony_ci spin_lock_irqsave(&dev->lock, flags); 8762306a36Sopenharmony_ci pci_dev = dev->pci_dev; 8862306a36Sopenharmony_ci spin_unlock_irqrestore(&dev->lock, flags); 8962306a36Sopenharmony_ci if (!pci_dev) { 9062306a36Sopenharmony_ci WARN(1, "trying to deallocate on missing device\n"); 9162306a36Sopenharmony_ci return; 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci if (desc->virt) { 9562306a36Sopenharmony_ci dma_free_coherent(&dev->pci_dev->dev, desc->size, desc->virt, 9662306a36Sopenharmony_ci desc->phys); 9762306a36Sopenharmony_ci desc->virt = NULL; 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistatic int tw686x_memcpy_dma_alloc(struct tw686x_video_channel *vc, 10262306a36Sopenharmony_ci unsigned int pb) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci struct tw686x_dev *dev = vc->dev; 10562306a36Sopenharmony_ci u32 reg = pb ? VDMA_B_ADDR[vc->ch] : VDMA_P_ADDR[vc->ch]; 10662306a36Sopenharmony_ci unsigned int len; 10762306a36Sopenharmony_ci void *virt; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci WARN(vc->dma_descs[pb].virt, 11062306a36Sopenharmony_ci "Allocating buffer but previous still here\n"); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci len = (vc->width * vc->height * vc->format->depth) >> 3; 11362306a36Sopenharmony_ci virt = dma_alloc_coherent(&dev->pci_dev->dev, len, 11462306a36Sopenharmony_ci &vc->dma_descs[pb].phys, GFP_KERNEL); 11562306a36Sopenharmony_ci if (!virt) { 11662306a36Sopenharmony_ci v4l2_err(&dev->v4l2_dev, 11762306a36Sopenharmony_ci "dma%d: unable to allocate %s-buffer\n", 11862306a36Sopenharmony_ci vc->ch, pb ? "B" : "P"); 11962306a36Sopenharmony_ci return -ENOMEM; 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci vc->dma_descs[pb].size = len; 12262306a36Sopenharmony_ci vc->dma_descs[pb].virt = virt; 12362306a36Sopenharmony_ci reg_write(dev, reg, vc->dma_descs[pb].phys); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci return 0; 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic void tw686x_memcpy_buf_refill(struct tw686x_video_channel *vc, 12962306a36Sopenharmony_ci unsigned int pb) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci struct tw686x_v4l2_buf *buf; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci while (!list_empty(&vc->vidq_queued)) { 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci buf = list_first_entry(&vc->vidq_queued, 13662306a36Sopenharmony_ci struct tw686x_v4l2_buf, list); 13762306a36Sopenharmony_ci list_del(&buf->list); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci vc->curr_bufs[pb] = buf; 14062306a36Sopenharmony_ci return; 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci vc->curr_bufs[pb] = NULL; 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic const struct tw686x_dma_ops memcpy_dma_ops = { 14662306a36Sopenharmony_ci .alloc = tw686x_memcpy_dma_alloc, 14762306a36Sopenharmony_ci .free = tw686x_memcpy_dma_free, 14862306a36Sopenharmony_ci .buf_refill = tw686x_memcpy_buf_refill, 14962306a36Sopenharmony_ci .mem_ops = &vb2_vmalloc_memops, 15062306a36Sopenharmony_ci .hw_dma_mode = TW686X_FRAME_MODE, 15162306a36Sopenharmony_ci .field = V4L2_FIELD_INTERLACED, 15262306a36Sopenharmony_ci}; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic void tw686x_contig_buf_refill(struct tw686x_video_channel *vc, 15562306a36Sopenharmony_ci unsigned int pb) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci struct tw686x_v4l2_buf *buf; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci while (!list_empty(&vc->vidq_queued)) { 16062306a36Sopenharmony_ci u32 reg = pb ? VDMA_B_ADDR[vc->ch] : VDMA_P_ADDR[vc->ch]; 16162306a36Sopenharmony_ci dma_addr_t phys; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci buf = list_first_entry(&vc->vidq_queued, 16462306a36Sopenharmony_ci struct tw686x_v4l2_buf, list); 16562306a36Sopenharmony_ci list_del(&buf->list); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci phys = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); 16862306a36Sopenharmony_ci reg_write(vc->dev, reg, phys); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci buf->vb.vb2_buf.state = VB2_BUF_STATE_ACTIVE; 17162306a36Sopenharmony_ci vc->curr_bufs[pb] = buf; 17262306a36Sopenharmony_ci return; 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci vc->curr_bufs[pb] = NULL; 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic const struct tw686x_dma_ops contig_dma_ops = { 17862306a36Sopenharmony_ci .buf_refill = tw686x_contig_buf_refill, 17962306a36Sopenharmony_ci .mem_ops = &vb2_dma_contig_memops, 18062306a36Sopenharmony_ci .hw_dma_mode = TW686X_FRAME_MODE, 18162306a36Sopenharmony_ci .field = V4L2_FIELD_INTERLACED, 18262306a36Sopenharmony_ci}; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic int tw686x_sg_desc_fill(struct tw686x_sg_desc *descs, 18562306a36Sopenharmony_ci struct tw686x_v4l2_buf *buf, 18662306a36Sopenharmony_ci unsigned int buf_len) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci struct sg_table *vbuf = vb2_dma_sg_plane_desc(&buf->vb.vb2_buf, 0); 18962306a36Sopenharmony_ci unsigned int len, entry_len; 19062306a36Sopenharmony_ci struct scatterlist *sg; 19162306a36Sopenharmony_ci int i, count; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci /* Clear the scatter-gather table */ 19462306a36Sopenharmony_ci memset(descs, 0, TW686X_SG_TABLE_SIZE); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci count = 0; 19762306a36Sopenharmony_ci for_each_sg(vbuf->sgl, sg, vbuf->nents, i) { 19862306a36Sopenharmony_ci dma_addr_t phys = sg_dma_address(sg); 19962306a36Sopenharmony_ci len = sg_dma_len(sg); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci while (len && buf_len) { 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci if (count == TW686X_MAX_SG_DESC_COUNT) 20462306a36Sopenharmony_ci return -ENOMEM; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci entry_len = min_t(unsigned int, len, 20762306a36Sopenharmony_ci TW686X_MAX_SG_ENTRY_SIZE); 20862306a36Sopenharmony_ci entry_len = min_t(unsigned int, entry_len, buf_len); 20962306a36Sopenharmony_ci descs[count].phys = cpu_to_le32(phys); 21062306a36Sopenharmony_ci descs[count++].flags_length = 21162306a36Sopenharmony_ci cpu_to_le32(BIT(30) | entry_len); 21262306a36Sopenharmony_ci phys += entry_len; 21362306a36Sopenharmony_ci len -= entry_len; 21462306a36Sopenharmony_ci buf_len -= entry_len; 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci if (!buf_len) 21862306a36Sopenharmony_ci return 0; 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci return -ENOMEM; 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cistatic void tw686x_sg_buf_refill(struct tw686x_video_channel *vc, 22562306a36Sopenharmony_ci unsigned int pb) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci struct tw686x_dev *dev = vc->dev; 22862306a36Sopenharmony_ci struct tw686x_v4l2_buf *buf; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci while (!list_empty(&vc->vidq_queued)) { 23162306a36Sopenharmony_ci unsigned int buf_len; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci buf = list_first_entry(&vc->vidq_queued, 23462306a36Sopenharmony_ci struct tw686x_v4l2_buf, list); 23562306a36Sopenharmony_ci list_del(&buf->list); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci buf_len = (vc->width * vc->height * vc->format->depth) >> 3; 23862306a36Sopenharmony_ci if (tw686x_sg_desc_fill(vc->sg_descs[pb], buf, buf_len)) { 23962306a36Sopenharmony_ci v4l2_err(&dev->v4l2_dev, 24062306a36Sopenharmony_ci "dma%d: unable to fill %s-buffer\n", 24162306a36Sopenharmony_ci vc->ch, pb ? "B" : "P"); 24262306a36Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); 24362306a36Sopenharmony_ci continue; 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci buf->vb.vb2_buf.state = VB2_BUF_STATE_ACTIVE; 24762306a36Sopenharmony_ci vc->curr_bufs[pb] = buf; 24862306a36Sopenharmony_ci return; 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci vc->curr_bufs[pb] = NULL; 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic void tw686x_sg_dma_free(struct tw686x_video_channel *vc, 25562306a36Sopenharmony_ci unsigned int pb) 25662306a36Sopenharmony_ci{ 25762306a36Sopenharmony_ci struct tw686x_dma_desc *desc = &vc->dma_descs[pb]; 25862306a36Sopenharmony_ci struct tw686x_dev *dev = vc->dev; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci if (desc->size) { 26162306a36Sopenharmony_ci dma_free_coherent(&dev->pci_dev->dev, desc->size, desc->virt, 26262306a36Sopenharmony_ci desc->phys); 26362306a36Sopenharmony_ci desc->virt = NULL; 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci vc->sg_descs[pb] = NULL; 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic int tw686x_sg_dma_alloc(struct tw686x_video_channel *vc, 27062306a36Sopenharmony_ci unsigned int pb) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci struct tw686x_dma_desc *desc = &vc->dma_descs[pb]; 27362306a36Sopenharmony_ci struct tw686x_dev *dev = vc->dev; 27462306a36Sopenharmony_ci u32 reg = pb ? DMA_PAGE_TABLE1_ADDR[vc->ch] : 27562306a36Sopenharmony_ci DMA_PAGE_TABLE0_ADDR[vc->ch]; 27662306a36Sopenharmony_ci void *virt; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci if (desc->size) { 27962306a36Sopenharmony_ci virt = dma_alloc_coherent(&dev->pci_dev->dev, desc->size, 28062306a36Sopenharmony_ci &desc->phys, GFP_KERNEL); 28162306a36Sopenharmony_ci if (!virt) { 28262306a36Sopenharmony_ci v4l2_err(&dev->v4l2_dev, 28362306a36Sopenharmony_ci "dma%d: unable to allocate %s-buffer\n", 28462306a36Sopenharmony_ci vc->ch, pb ? "B" : "P"); 28562306a36Sopenharmony_ci return -ENOMEM; 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci desc->virt = virt; 28862306a36Sopenharmony_ci reg_write(dev, reg, desc->phys); 28962306a36Sopenharmony_ci } else { 29062306a36Sopenharmony_ci virt = dev->video_channels[0].dma_descs[pb].virt + 29162306a36Sopenharmony_ci vc->ch * TW686X_SG_TABLE_SIZE; 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci vc->sg_descs[pb] = virt; 29562306a36Sopenharmony_ci return 0; 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistatic int tw686x_sg_setup(struct tw686x_dev *dev) 29962306a36Sopenharmony_ci{ 30062306a36Sopenharmony_ci unsigned int sg_table_size, pb, ch, channels; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci if (is_second_gen(dev)) { 30362306a36Sopenharmony_ci /* 30462306a36Sopenharmony_ci * TW6865/TW6869: each channel needs a pair of 30562306a36Sopenharmony_ci * P-B descriptor tables. 30662306a36Sopenharmony_ci */ 30762306a36Sopenharmony_ci channels = max_channels(dev); 30862306a36Sopenharmony_ci sg_table_size = TW686X_SG_TABLE_SIZE; 30962306a36Sopenharmony_ci } else { 31062306a36Sopenharmony_ci /* 31162306a36Sopenharmony_ci * TW6864/TW6868: we need to allocate a pair of 31262306a36Sopenharmony_ci * P-B descriptor tables, common for all channels. 31362306a36Sopenharmony_ci * Each table will be bigger than 4 KB. 31462306a36Sopenharmony_ci */ 31562306a36Sopenharmony_ci channels = 1; 31662306a36Sopenharmony_ci sg_table_size = max_channels(dev) * TW686X_SG_TABLE_SIZE; 31762306a36Sopenharmony_ci } 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci for (ch = 0; ch < channels; ch++) { 32062306a36Sopenharmony_ci struct tw686x_video_channel *vc = &dev->video_channels[ch]; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci for (pb = 0; pb < 2; pb++) 32362306a36Sopenharmony_ci vc->dma_descs[pb].size = sg_table_size; 32462306a36Sopenharmony_ci } 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci return 0; 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_cistatic const struct tw686x_dma_ops sg_dma_ops = { 33062306a36Sopenharmony_ci .setup = tw686x_sg_setup, 33162306a36Sopenharmony_ci .alloc = tw686x_sg_dma_alloc, 33262306a36Sopenharmony_ci .free = tw686x_sg_dma_free, 33362306a36Sopenharmony_ci .buf_refill = tw686x_sg_buf_refill, 33462306a36Sopenharmony_ci .mem_ops = &vb2_dma_sg_memops, 33562306a36Sopenharmony_ci .hw_dma_mode = TW686X_SG_MODE, 33662306a36Sopenharmony_ci .field = V4L2_FIELD_SEQ_TB, 33762306a36Sopenharmony_ci}; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_cistatic const unsigned int fps_map[15] = { 34062306a36Sopenharmony_ci /* 34162306a36Sopenharmony_ci * bit 31 enables selecting the field control register 34262306a36Sopenharmony_ci * bits 0-29 are a bitmask with fields that will be output. 34362306a36Sopenharmony_ci * For NTSC (and PAL-M, PAL-60), all 30 bits are used. 34462306a36Sopenharmony_ci * For other PAL standards, only the first 25 bits are used. 34562306a36Sopenharmony_ci */ 34662306a36Sopenharmony_ci 0x00000000, /* output all fields */ 34762306a36Sopenharmony_ci 0x80000006, /* 2 fps (60Hz), 2 fps (50Hz) */ 34862306a36Sopenharmony_ci 0x80018006, /* 4 fps (60Hz), 4 fps (50Hz) */ 34962306a36Sopenharmony_ci 0x80618006, /* 6 fps (60Hz), 6 fps (50Hz) */ 35062306a36Sopenharmony_ci 0x81818186, /* 8 fps (60Hz), 8 fps (50Hz) */ 35162306a36Sopenharmony_ci 0x86186186, /* 10 fps (60Hz), 8 fps (50Hz) */ 35262306a36Sopenharmony_ci 0x86619866, /* 12 fps (60Hz), 10 fps (50Hz) */ 35362306a36Sopenharmony_ci 0x86666666, /* 14 fps (60Hz), 12 fps (50Hz) */ 35462306a36Sopenharmony_ci 0x9999999e, /* 16 fps (60Hz), 14 fps (50Hz) */ 35562306a36Sopenharmony_ci 0x99e6799e, /* 18 fps (60Hz), 16 fps (50Hz) */ 35662306a36Sopenharmony_ci 0x9e79e79e, /* 20 fps (60Hz), 16 fps (50Hz) */ 35762306a36Sopenharmony_ci 0x9e7e7e7e, /* 22 fps (60Hz), 18 fps (50Hz) */ 35862306a36Sopenharmony_ci 0x9fe7f9fe, /* 24 fps (60Hz), 20 fps (50Hz) */ 35962306a36Sopenharmony_ci 0x9ffe7ffe, /* 26 fps (60Hz), 22 fps (50Hz) */ 36062306a36Sopenharmony_ci 0x9ffffffe, /* 28 fps (60Hz), 24 fps (50Hz) */ 36162306a36Sopenharmony_ci}; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_cistatic unsigned int tw686x_real_fps(unsigned int index, unsigned int max_fps) 36462306a36Sopenharmony_ci{ 36562306a36Sopenharmony_ci unsigned long mask; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci if (!index || index >= ARRAY_SIZE(fps_map)) 36862306a36Sopenharmony_ci return max_fps; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci mask = GENMASK(max_fps - 1, 0); 37162306a36Sopenharmony_ci return hweight_long(fps_map[index] & mask); 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_cistatic unsigned int tw686x_fps_idx(unsigned int fps, unsigned int max_fps) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci unsigned int idx, real_fps; 37762306a36Sopenharmony_ci int delta; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci /* First guess */ 38062306a36Sopenharmony_ci idx = (12 + 15 * fps) / max_fps; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci /* Minimal possible framerate is 2 frames per second */ 38362306a36Sopenharmony_ci if (!idx) 38462306a36Sopenharmony_ci return 1; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci /* Check if the difference is bigger than abs(1) and adjust */ 38762306a36Sopenharmony_ci real_fps = tw686x_real_fps(idx, max_fps); 38862306a36Sopenharmony_ci delta = real_fps - fps; 38962306a36Sopenharmony_ci if (delta < -1) 39062306a36Sopenharmony_ci idx++; 39162306a36Sopenharmony_ci else if (delta > 1) 39262306a36Sopenharmony_ci idx--; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci /* Max framerate */ 39562306a36Sopenharmony_ci if (idx >= 15) 39662306a36Sopenharmony_ci return 0; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci return idx; 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_cistatic void tw686x_set_framerate(struct tw686x_video_channel *vc, 40262306a36Sopenharmony_ci unsigned int fps) 40362306a36Sopenharmony_ci{ 40462306a36Sopenharmony_ci unsigned int i; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci i = tw686x_fps_idx(fps, TW686X_MAX_FPS(vc->video_standard)); 40762306a36Sopenharmony_ci reg_write(vc->dev, VIDEO_FIELD_CTRL[vc->ch], fps_map[i]); 40862306a36Sopenharmony_ci vc->fps = tw686x_real_fps(i, TW686X_MAX_FPS(vc->video_standard)); 40962306a36Sopenharmony_ci} 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_cistatic const struct tw686x_format *format_by_fourcc(unsigned int fourcc) 41262306a36Sopenharmony_ci{ 41362306a36Sopenharmony_ci unsigned int cnt; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci for (cnt = 0; cnt < ARRAY_SIZE(formats); cnt++) 41662306a36Sopenharmony_ci if (formats[cnt].fourcc == fourcc) 41762306a36Sopenharmony_ci return &formats[cnt]; 41862306a36Sopenharmony_ci return NULL; 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_cistatic int tw686x_queue_setup(struct vb2_queue *vq, 42262306a36Sopenharmony_ci unsigned int *nbuffers, unsigned int *nplanes, 42362306a36Sopenharmony_ci unsigned int sizes[], struct device *alloc_devs[]) 42462306a36Sopenharmony_ci{ 42562306a36Sopenharmony_ci struct tw686x_video_channel *vc = vb2_get_drv_priv(vq); 42662306a36Sopenharmony_ci unsigned int szimage = 42762306a36Sopenharmony_ci (vc->width * vc->height * vc->format->depth) >> 3; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci /* 43062306a36Sopenharmony_ci * Let's request at least three buffers: two for the 43162306a36Sopenharmony_ci * DMA engine and one for userspace. 43262306a36Sopenharmony_ci */ 43362306a36Sopenharmony_ci if (vq->num_buffers + *nbuffers < 3) 43462306a36Sopenharmony_ci *nbuffers = 3 - vq->num_buffers; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci if (*nplanes) { 43762306a36Sopenharmony_ci if (*nplanes != 1 || sizes[0] < szimage) 43862306a36Sopenharmony_ci return -EINVAL; 43962306a36Sopenharmony_ci return 0; 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci sizes[0] = szimage; 44362306a36Sopenharmony_ci *nplanes = 1; 44462306a36Sopenharmony_ci return 0; 44562306a36Sopenharmony_ci} 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_cistatic void tw686x_buf_queue(struct vb2_buffer *vb) 44862306a36Sopenharmony_ci{ 44962306a36Sopenharmony_ci struct tw686x_video_channel *vc = vb2_get_drv_priv(vb->vb2_queue); 45062306a36Sopenharmony_ci struct tw686x_dev *dev = vc->dev; 45162306a36Sopenharmony_ci struct pci_dev *pci_dev; 45262306a36Sopenharmony_ci unsigned long flags; 45362306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 45462306a36Sopenharmony_ci struct tw686x_v4l2_buf *buf = 45562306a36Sopenharmony_ci container_of(vbuf, struct tw686x_v4l2_buf, vb); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci /* Check device presence */ 45862306a36Sopenharmony_ci spin_lock_irqsave(&dev->lock, flags); 45962306a36Sopenharmony_ci pci_dev = dev->pci_dev; 46062306a36Sopenharmony_ci spin_unlock_irqrestore(&dev->lock, flags); 46162306a36Sopenharmony_ci if (!pci_dev) { 46262306a36Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); 46362306a36Sopenharmony_ci return; 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci spin_lock_irqsave(&vc->qlock, flags); 46762306a36Sopenharmony_ci list_add_tail(&buf->list, &vc->vidq_queued); 46862306a36Sopenharmony_ci spin_unlock_irqrestore(&vc->qlock, flags); 46962306a36Sopenharmony_ci} 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_cistatic void tw686x_clear_queue(struct tw686x_video_channel *vc, 47262306a36Sopenharmony_ci enum vb2_buffer_state state) 47362306a36Sopenharmony_ci{ 47462306a36Sopenharmony_ci unsigned int pb; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci while (!list_empty(&vc->vidq_queued)) { 47762306a36Sopenharmony_ci struct tw686x_v4l2_buf *buf; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci buf = list_first_entry(&vc->vidq_queued, 48062306a36Sopenharmony_ci struct tw686x_v4l2_buf, list); 48162306a36Sopenharmony_ci list_del(&buf->list); 48262306a36Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, state); 48362306a36Sopenharmony_ci } 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci for (pb = 0; pb < 2; pb++) { 48662306a36Sopenharmony_ci if (vc->curr_bufs[pb]) 48762306a36Sopenharmony_ci vb2_buffer_done(&vc->curr_bufs[pb]->vb.vb2_buf, state); 48862306a36Sopenharmony_ci vc->curr_bufs[pb] = NULL; 48962306a36Sopenharmony_ci } 49062306a36Sopenharmony_ci} 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_cistatic int tw686x_start_streaming(struct vb2_queue *vq, unsigned int count) 49362306a36Sopenharmony_ci{ 49462306a36Sopenharmony_ci struct tw686x_video_channel *vc = vb2_get_drv_priv(vq); 49562306a36Sopenharmony_ci struct tw686x_dev *dev = vc->dev; 49662306a36Sopenharmony_ci struct pci_dev *pci_dev; 49762306a36Sopenharmony_ci unsigned long flags; 49862306a36Sopenharmony_ci int pb, err; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci /* Check device presence */ 50162306a36Sopenharmony_ci spin_lock_irqsave(&dev->lock, flags); 50262306a36Sopenharmony_ci pci_dev = dev->pci_dev; 50362306a36Sopenharmony_ci spin_unlock_irqrestore(&dev->lock, flags); 50462306a36Sopenharmony_ci if (!pci_dev) { 50562306a36Sopenharmony_ci err = -ENODEV; 50662306a36Sopenharmony_ci goto err_clear_queue; 50762306a36Sopenharmony_ci } 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci spin_lock_irqsave(&vc->qlock, flags); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci /* Sanity check */ 51262306a36Sopenharmony_ci if (dev->dma_mode == TW686X_DMA_MODE_MEMCPY && 51362306a36Sopenharmony_ci (!vc->dma_descs[0].virt || !vc->dma_descs[1].virt)) { 51462306a36Sopenharmony_ci spin_unlock_irqrestore(&vc->qlock, flags); 51562306a36Sopenharmony_ci v4l2_err(&dev->v4l2_dev, 51662306a36Sopenharmony_ci "video%d: refusing to start without DMA buffers\n", 51762306a36Sopenharmony_ci vc->num); 51862306a36Sopenharmony_ci err = -ENOMEM; 51962306a36Sopenharmony_ci goto err_clear_queue; 52062306a36Sopenharmony_ci } 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci for (pb = 0; pb < 2; pb++) 52362306a36Sopenharmony_ci dev->dma_ops->buf_refill(vc, pb); 52462306a36Sopenharmony_ci spin_unlock_irqrestore(&vc->qlock, flags); 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci vc->sequence = 0; 52762306a36Sopenharmony_ci vc->pb = 0; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci spin_lock_irqsave(&dev->lock, flags); 53062306a36Sopenharmony_ci tw686x_enable_channel(dev, vc->ch); 53162306a36Sopenharmony_ci spin_unlock_irqrestore(&dev->lock, flags); 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci mod_timer(&dev->dma_delay_timer, jiffies + msecs_to_jiffies(100)); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci return 0; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_cierr_clear_queue: 53862306a36Sopenharmony_ci spin_lock_irqsave(&vc->qlock, flags); 53962306a36Sopenharmony_ci tw686x_clear_queue(vc, VB2_BUF_STATE_QUEUED); 54062306a36Sopenharmony_ci spin_unlock_irqrestore(&vc->qlock, flags); 54162306a36Sopenharmony_ci return err; 54262306a36Sopenharmony_ci} 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_cistatic void tw686x_stop_streaming(struct vb2_queue *vq) 54562306a36Sopenharmony_ci{ 54662306a36Sopenharmony_ci struct tw686x_video_channel *vc = vb2_get_drv_priv(vq); 54762306a36Sopenharmony_ci struct tw686x_dev *dev = vc->dev; 54862306a36Sopenharmony_ci struct pci_dev *pci_dev; 54962306a36Sopenharmony_ci unsigned long flags; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci /* Check device presence */ 55262306a36Sopenharmony_ci spin_lock_irqsave(&dev->lock, flags); 55362306a36Sopenharmony_ci pci_dev = dev->pci_dev; 55462306a36Sopenharmony_ci spin_unlock_irqrestore(&dev->lock, flags); 55562306a36Sopenharmony_ci if (pci_dev) 55662306a36Sopenharmony_ci tw686x_disable_channel(dev, vc->ch); 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci spin_lock_irqsave(&vc->qlock, flags); 55962306a36Sopenharmony_ci tw686x_clear_queue(vc, VB2_BUF_STATE_ERROR); 56062306a36Sopenharmony_ci spin_unlock_irqrestore(&vc->qlock, flags); 56162306a36Sopenharmony_ci} 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_cistatic int tw686x_buf_prepare(struct vb2_buffer *vb) 56462306a36Sopenharmony_ci{ 56562306a36Sopenharmony_ci struct tw686x_video_channel *vc = vb2_get_drv_priv(vb->vb2_queue); 56662306a36Sopenharmony_ci unsigned int size = 56762306a36Sopenharmony_ci (vc->width * vc->height * vc->format->depth) >> 3; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci if (vb2_plane_size(vb, 0) < size) 57062306a36Sopenharmony_ci return -EINVAL; 57162306a36Sopenharmony_ci vb2_set_plane_payload(vb, 0, size); 57262306a36Sopenharmony_ci return 0; 57362306a36Sopenharmony_ci} 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_cistatic const struct vb2_ops tw686x_video_qops = { 57662306a36Sopenharmony_ci .queue_setup = tw686x_queue_setup, 57762306a36Sopenharmony_ci .buf_queue = tw686x_buf_queue, 57862306a36Sopenharmony_ci .buf_prepare = tw686x_buf_prepare, 57962306a36Sopenharmony_ci .start_streaming = tw686x_start_streaming, 58062306a36Sopenharmony_ci .stop_streaming = tw686x_stop_streaming, 58162306a36Sopenharmony_ci .wait_prepare = vb2_ops_wait_prepare, 58262306a36Sopenharmony_ci .wait_finish = vb2_ops_wait_finish, 58362306a36Sopenharmony_ci}; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_cistatic int tw686x_s_ctrl(struct v4l2_ctrl *ctrl) 58662306a36Sopenharmony_ci{ 58762306a36Sopenharmony_ci struct tw686x_video_channel *vc; 58862306a36Sopenharmony_ci struct tw686x_dev *dev; 58962306a36Sopenharmony_ci unsigned int ch; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci vc = container_of(ctrl->handler, struct tw686x_video_channel, 59262306a36Sopenharmony_ci ctrl_handler); 59362306a36Sopenharmony_ci dev = vc->dev; 59462306a36Sopenharmony_ci ch = vc->ch; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci switch (ctrl->id) { 59762306a36Sopenharmony_ci case V4L2_CID_BRIGHTNESS: 59862306a36Sopenharmony_ci reg_write(dev, BRIGHT[ch], ctrl->val & 0xff); 59962306a36Sopenharmony_ci return 0; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci case V4L2_CID_CONTRAST: 60262306a36Sopenharmony_ci reg_write(dev, CONTRAST[ch], ctrl->val); 60362306a36Sopenharmony_ci return 0; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci case V4L2_CID_SATURATION: 60662306a36Sopenharmony_ci reg_write(dev, SAT_U[ch], ctrl->val); 60762306a36Sopenharmony_ci reg_write(dev, SAT_V[ch], ctrl->val); 60862306a36Sopenharmony_ci return 0; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci case V4L2_CID_HUE: 61162306a36Sopenharmony_ci reg_write(dev, HUE[ch], ctrl->val & 0xff); 61262306a36Sopenharmony_ci return 0; 61362306a36Sopenharmony_ci } 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci return -EINVAL; 61662306a36Sopenharmony_ci} 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_cistatic const struct v4l2_ctrl_ops ctrl_ops = { 61962306a36Sopenharmony_ci .s_ctrl = tw686x_s_ctrl, 62062306a36Sopenharmony_ci}; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_cistatic int tw686x_g_fmt_vid_cap(struct file *file, void *priv, 62362306a36Sopenharmony_ci struct v4l2_format *f) 62462306a36Sopenharmony_ci{ 62562306a36Sopenharmony_ci struct tw686x_video_channel *vc = video_drvdata(file); 62662306a36Sopenharmony_ci struct tw686x_dev *dev = vc->dev; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci f->fmt.pix.width = vc->width; 62962306a36Sopenharmony_ci f->fmt.pix.height = vc->height; 63062306a36Sopenharmony_ci f->fmt.pix.field = dev->dma_ops->field; 63162306a36Sopenharmony_ci f->fmt.pix.pixelformat = vc->format->fourcc; 63262306a36Sopenharmony_ci f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; 63362306a36Sopenharmony_ci f->fmt.pix.bytesperline = (f->fmt.pix.width * vc->format->depth) / 8; 63462306a36Sopenharmony_ci f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; 63562306a36Sopenharmony_ci return 0; 63662306a36Sopenharmony_ci} 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_cistatic int tw686x_try_fmt_vid_cap(struct file *file, void *priv, 63962306a36Sopenharmony_ci struct v4l2_format *f) 64062306a36Sopenharmony_ci{ 64162306a36Sopenharmony_ci struct tw686x_video_channel *vc = video_drvdata(file); 64262306a36Sopenharmony_ci struct tw686x_dev *dev = vc->dev; 64362306a36Sopenharmony_ci unsigned int video_height = TW686X_VIDEO_HEIGHT(vc->video_standard); 64462306a36Sopenharmony_ci const struct tw686x_format *format; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci format = format_by_fourcc(f->fmt.pix.pixelformat); 64762306a36Sopenharmony_ci if (!format) { 64862306a36Sopenharmony_ci format = &formats[0]; 64962306a36Sopenharmony_ci f->fmt.pix.pixelformat = format->fourcc; 65062306a36Sopenharmony_ci } 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci if (f->fmt.pix.width <= TW686X_VIDEO_WIDTH / 2) 65362306a36Sopenharmony_ci f->fmt.pix.width = TW686X_VIDEO_WIDTH / 2; 65462306a36Sopenharmony_ci else 65562306a36Sopenharmony_ci f->fmt.pix.width = TW686X_VIDEO_WIDTH; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci if (f->fmt.pix.height <= video_height / 2) 65862306a36Sopenharmony_ci f->fmt.pix.height = video_height / 2; 65962306a36Sopenharmony_ci else 66062306a36Sopenharmony_ci f->fmt.pix.height = video_height; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci f->fmt.pix.bytesperline = (f->fmt.pix.width * format->depth) / 8; 66362306a36Sopenharmony_ci f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; 66462306a36Sopenharmony_ci f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; 66562306a36Sopenharmony_ci f->fmt.pix.field = dev->dma_ops->field; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci return 0; 66862306a36Sopenharmony_ci} 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_cistatic int tw686x_set_format(struct tw686x_video_channel *vc, 67162306a36Sopenharmony_ci unsigned int pixelformat, unsigned int width, 67262306a36Sopenharmony_ci unsigned int height, bool realloc) 67362306a36Sopenharmony_ci{ 67462306a36Sopenharmony_ci struct tw686x_dev *dev = vc->dev; 67562306a36Sopenharmony_ci u32 val, dma_width, dma_height, dma_line_width; 67662306a36Sopenharmony_ci int err, pb; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci vc->format = format_by_fourcc(pixelformat); 67962306a36Sopenharmony_ci vc->width = width; 68062306a36Sopenharmony_ci vc->height = height; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci /* We need new DMA buffers if the framesize has changed */ 68362306a36Sopenharmony_ci if (dev->dma_ops->alloc && realloc) { 68462306a36Sopenharmony_ci for (pb = 0; pb < 2; pb++) 68562306a36Sopenharmony_ci dev->dma_ops->free(vc, pb); 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci for (pb = 0; pb < 2; pb++) { 68862306a36Sopenharmony_ci err = dev->dma_ops->alloc(vc, pb); 68962306a36Sopenharmony_ci if (err) { 69062306a36Sopenharmony_ci if (pb > 0) 69162306a36Sopenharmony_ci dev->dma_ops->free(vc, 0); 69262306a36Sopenharmony_ci return err; 69362306a36Sopenharmony_ci } 69462306a36Sopenharmony_ci } 69562306a36Sopenharmony_ci } 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci val = reg_read(vc->dev, VDMA_CHANNEL_CONFIG[vc->ch]); 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci if (vc->width <= TW686X_VIDEO_WIDTH / 2) 70062306a36Sopenharmony_ci val |= BIT(23); 70162306a36Sopenharmony_ci else 70262306a36Sopenharmony_ci val &= ~BIT(23); 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci if (vc->height <= TW686X_VIDEO_HEIGHT(vc->video_standard) / 2) 70562306a36Sopenharmony_ci val |= BIT(24); 70662306a36Sopenharmony_ci else 70762306a36Sopenharmony_ci val &= ~BIT(24); 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci val &= ~0x7ffff; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci /* Program the DMA scatter-gather */ 71262306a36Sopenharmony_ci if (dev->dma_mode == TW686X_DMA_MODE_SG) { 71362306a36Sopenharmony_ci u32 start_idx, end_idx; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci start_idx = is_second_gen(dev) ? 71662306a36Sopenharmony_ci 0 : vc->ch * TW686X_MAX_SG_DESC_COUNT; 71762306a36Sopenharmony_ci end_idx = start_idx + TW686X_MAX_SG_DESC_COUNT - 1; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci val |= (end_idx << 10) | start_idx; 72062306a36Sopenharmony_ci } 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci val &= ~(0x7 << 20); 72362306a36Sopenharmony_ci val |= vc->format->mode << 20; 72462306a36Sopenharmony_ci reg_write(vc->dev, VDMA_CHANNEL_CONFIG[vc->ch], val); 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci /* Program the DMA frame size */ 72762306a36Sopenharmony_ci dma_width = (vc->width * 2) & 0x7ff; 72862306a36Sopenharmony_ci dma_height = vc->height / 2; 72962306a36Sopenharmony_ci dma_line_width = (vc->width * 2) & 0x7ff; 73062306a36Sopenharmony_ci val = (dma_height << 22) | (dma_line_width << 11) | dma_width; 73162306a36Sopenharmony_ci reg_write(vc->dev, VDMA_WHP[vc->ch], val); 73262306a36Sopenharmony_ci return 0; 73362306a36Sopenharmony_ci} 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_cistatic int tw686x_s_fmt_vid_cap(struct file *file, void *priv, 73662306a36Sopenharmony_ci struct v4l2_format *f) 73762306a36Sopenharmony_ci{ 73862306a36Sopenharmony_ci struct tw686x_video_channel *vc = video_drvdata(file); 73962306a36Sopenharmony_ci unsigned long area; 74062306a36Sopenharmony_ci bool realloc; 74162306a36Sopenharmony_ci int err; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci if (vb2_is_busy(&vc->vidq)) 74462306a36Sopenharmony_ci return -EBUSY; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci area = vc->width * vc->height; 74762306a36Sopenharmony_ci err = tw686x_try_fmt_vid_cap(file, priv, f); 74862306a36Sopenharmony_ci if (err) 74962306a36Sopenharmony_ci return err; 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci realloc = area != (f->fmt.pix.width * f->fmt.pix.height); 75262306a36Sopenharmony_ci return tw686x_set_format(vc, f->fmt.pix.pixelformat, 75362306a36Sopenharmony_ci f->fmt.pix.width, f->fmt.pix.height, 75462306a36Sopenharmony_ci realloc); 75562306a36Sopenharmony_ci} 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_cistatic int tw686x_querycap(struct file *file, void *priv, 75862306a36Sopenharmony_ci struct v4l2_capability *cap) 75962306a36Sopenharmony_ci{ 76062306a36Sopenharmony_ci struct tw686x_video_channel *vc = video_drvdata(file); 76162306a36Sopenharmony_ci struct tw686x_dev *dev = vc->dev; 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci strscpy(cap->driver, "tw686x", sizeof(cap->driver)); 76462306a36Sopenharmony_ci strscpy(cap->card, dev->name, sizeof(cap->card)); 76562306a36Sopenharmony_ci return 0; 76662306a36Sopenharmony_ci} 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_cistatic int tw686x_set_standard(struct tw686x_video_channel *vc, v4l2_std_id id) 76962306a36Sopenharmony_ci{ 77062306a36Sopenharmony_ci u32 val; 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci if (id & V4L2_STD_NTSC) 77362306a36Sopenharmony_ci val = 0; 77462306a36Sopenharmony_ci else if (id & V4L2_STD_PAL) 77562306a36Sopenharmony_ci val = 1; 77662306a36Sopenharmony_ci else if (id & V4L2_STD_SECAM) 77762306a36Sopenharmony_ci val = 2; 77862306a36Sopenharmony_ci else if (id & V4L2_STD_NTSC_443) 77962306a36Sopenharmony_ci val = 3; 78062306a36Sopenharmony_ci else if (id & V4L2_STD_PAL_M) 78162306a36Sopenharmony_ci val = 4; 78262306a36Sopenharmony_ci else if (id & V4L2_STD_PAL_Nc) 78362306a36Sopenharmony_ci val = 5; 78462306a36Sopenharmony_ci else if (id & V4L2_STD_PAL_60) 78562306a36Sopenharmony_ci val = 6; 78662306a36Sopenharmony_ci else 78762306a36Sopenharmony_ci return -EINVAL; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci vc->video_standard = id; 79062306a36Sopenharmony_ci reg_write(vc->dev, SDT[vc->ch], val); 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci val = reg_read(vc->dev, VIDEO_CONTROL1); 79362306a36Sopenharmony_ci if (id & V4L2_STD_525_60) 79462306a36Sopenharmony_ci val &= ~(1 << (SYS_MODE_DMA_SHIFT + vc->ch)); 79562306a36Sopenharmony_ci else 79662306a36Sopenharmony_ci val |= (1 << (SYS_MODE_DMA_SHIFT + vc->ch)); 79762306a36Sopenharmony_ci reg_write(vc->dev, VIDEO_CONTROL1, val); 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci return 0; 80062306a36Sopenharmony_ci} 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_cistatic int tw686x_s_std(struct file *file, void *priv, v4l2_std_id id) 80362306a36Sopenharmony_ci{ 80462306a36Sopenharmony_ci struct tw686x_video_channel *vc = video_drvdata(file); 80562306a36Sopenharmony_ci struct v4l2_format f; 80662306a36Sopenharmony_ci int ret; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci if (vc->video_standard == id) 80962306a36Sopenharmony_ci return 0; 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci if (vb2_is_busy(&vc->vidq)) 81262306a36Sopenharmony_ci return -EBUSY; 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci ret = tw686x_set_standard(vc, id); 81562306a36Sopenharmony_ci if (ret) 81662306a36Sopenharmony_ci return ret; 81762306a36Sopenharmony_ci /* 81862306a36Sopenharmony_ci * Adjust format after V4L2_STD_525_60/V4L2_STD_625_50 change, 81962306a36Sopenharmony_ci * calling g_fmt and s_fmt will sanitize the height 82062306a36Sopenharmony_ci * according to the standard. 82162306a36Sopenharmony_ci */ 82262306a36Sopenharmony_ci tw686x_g_fmt_vid_cap(file, priv, &f); 82362306a36Sopenharmony_ci tw686x_s_fmt_vid_cap(file, priv, &f); 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci /* 82662306a36Sopenharmony_ci * Frame decimation depends on the chosen standard, 82762306a36Sopenharmony_ci * so reset it to the current value. 82862306a36Sopenharmony_ci */ 82962306a36Sopenharmony_ci tw686x_set_framerate(vc, vc->fps); 83062306a36Sopenharmony_ci return 0; 83162306a36Sopenharmony_ci} 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_cistatic int tw686x_querystd(struct file *file, void *priv, v4l2_std_id *std) 83462306a36Sopenharmony_ci{ 83562306a36Sopenharmony_ci struct tw686x_video_channel *vc = video_drvdata(file); 83662306a36Sopenharmony_ci struct tw686x_dev *dev = vc->dev; 83762306a36Sopenharmony_ci unsigned int old_std, detected_std = 0; 83862306a36Sopenharmony_ci unsigned long end; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci if (vb2_is_streaming(&vc->vidq)) 84162306a36Sopenharmony_ci return -EBUSY; 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci /* Enable and start standard detection */ 84462306a36Sopenharmony_ci old_std = reg_read(dev, SDT[vc->ch]); 84562306a36Sopenharmony_ci reg_write(dev, SDT[vc->ch], 0x7); 84662306a36Sopenharmony_ci reg_write(dev, SDT_EN[vc->ch], 0xff); 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci end = jiffies + msecs_to_jiffies(500); 84962306a36Sopenharmony_ci while (time_is_after_jiffies(end)) { 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci detected_std = reg_read(dev, SDT[vc->ch]); 85262306a36Sopenharmony_ci if (!(detected_std & BIT(7))) 85362306a36Sopenharmony_ci break; 85462306a36Sopenharmony_ci msleep(100); 85562306a36Sopenharmony_ci } 85662306a36Sopenharmony_ci reg_write(dev, SDT[vc->ch], old_std); 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci /* Exit if still busy */ 85962306a36Sopenharmony_ci if (detected_std & BIT(7)) 86062306a36Sopenharmony_ci return 0; 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci detected_std = (detected_std >> 4) & 0x7; 86362306a36Sopenharmony_ci switch (detected_std) { 86462306a36Sopenharmony_ci case TW686X_STD_NTSC_M: 86562306a36Sopenharmony_ci *std &= V4L2_STD_NTSC; 86662306a36Sopenharmony_ci break; 86762306a36Sopenharmony_ci case TW686X_STD_NTSC_443: 86862306a36Sopenharmony_ci *std &= V4L2_STD_NTSC_443; 86962306a36Sopenharmony_ci break; 87062306a36Sopenharmony_ci case TW686X_STD_PAL_M: 87162306a36Sopenharmony_ci *std &= V4L2_STD_PAL_M; 87262306a36Sopenharmony_ci break; 87362306a36Sopenharmony_ci case TW686X_STD_PAL_60: 87462306a36Sopenharmony_ci *std &= V4L2_STD_PAL_60; 87562306a36Sopenharmony_ci break; 87662306a36Sopenharmony_ci case TW686X_STD_PAL: 87762306a36Sopenharmony_ci *std &= V4L2_STD_PAL; 87862306a36Sopenharmony_ci break; 87962306a36Sopenharmony_ci case TW686X_STD_PAL_CN: 88062306a36Sopenharmony_ci *std &= V4L2_STD_PAL_Nc; 88162306a36Sopenharmony_ci break; 88262306a36Sopenharmony_ci case TW686X_STD_SECAM: 88362306a36Sopenharmony_ci *std &= V4L2_STD_SECAM; 88462306a36Sopenharmony_ci break; 88562306a36Sopenharmony_ci default: 88662306a36Sopenharmony_ci *std = 0; 88762306a36Sopenharmony_ci } 88862306a36Sopenharmony_ci return 0; 88962306a36Sopenharmony_ci} 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_cistatic int tw686x_g_std(struct file *file, void *priv, v4l2_std_id *id) 89262306a36Sopenharmony_ci{ 89362306a36Sopenharmony_ci struct tw686x_video_channel *vc = video_drvdata(file); 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci *id = vc->video_standard; 89662306a36Sopenharmony_ci return 0; 89762306a36Sopenharmony_ci} 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_cistatic int tw686x_enum_framesizes(struct file *file, void *priv, 90062306a36Sopenharmony_ci struct v4l2_frmsizeenum *fsize) 90162306a36Sopenharmony_ci{ 90262306a36Sopenharmony_ci struct tw686x_video_channel *vc = video_drvdata(file); 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci if (fsize->index) 90562306a36Sopenharmony_ci return -EINVAL; 90662306a36Sopenharmony_ci fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; 90762306a36Sopenharmony_ci fsize->stepwise.max_width = TW686X_VIDEO_WIDTH; 90862306a36Sopenharmony_ci fsize->stepwise.min_width = fsize->stepwise.max_width / 2; 90962306a36Sopenharmony_ci fsize->stepwise.step_width = fsize->stepwise.min_width; 91062306a36Sopenharmony_ci fsize->stepwise.max_height = TW686X_VIDEO_HEIGHT(vc->video_standard); 91162306a36Sopenharmony_ci fsize->stepwise.min_height = fsize->stepwise.max_height / 2; 91262306a36Sopenharmony_ci fsize->stepwise.step_height = fsize->stepwise.min_height; 91362306a36Sopenharmony_ci return 0; 91462306a36Sopenharmony_ci} 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_cistatic int tw686x_enum_frameintervals(struct file *file, void *priv, 91762306a36Sopenharmony_ci struct v4l2_frmivalenum *ival) 91862306a36Sopenharmony_ci{ 91962306a36Sopenharmony_ci struct tw686x_video_channel *vc = video_drvdata(file); 92062306a36Sopenharmony_ci int max_fps = TW686X_MAX_FPS(vc->video_standard); 92162306a36Sopenharmony_ci int max_rates = DIV_ROUND_UP(max_fps, 2); 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci if (ival->index >= max_rates) 92462306a36Sopenharmony_ci return -EINVAL; 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci ival->type = V4L2_FRMIVAL_TYPE_DISCRETE; 92762306a36Sopenharmony_ci ival->discrete.numerator = 1; 92862306a36Sopenharmony_ci if (ival->index < (max_rates - 1)) 92962306a36Sopenharmony_ci ival->discrete.denominator = (ival->index + 1) * 2; 93062306a36Sopenharmony_ci else 93162306a36Sopenharmony_ci ival->discrete.denominator = max_fps; 93262306a36Sopenharmony_ci return 0; 93362306a36Sopenharmony_ci} 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_cistatic int tw686x_g_parm(struct file *file, void *priv, 93662306a36Sopenharmony_ci struct v4l2_streamparm *sp) 93762306a36Sopenharmony_ci{ 93862306a36Sopenharmony_ci struct tw686x_video_channel *vc = video_drvdata(file); 93962306a36Sopenharmony_ci struct v4l2_captureparm *cp = &sp->parm.capture; 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) 94262306a36Sopenharmony_ci return -EINVAL; 94362306a36Sopenharmony_ci sp->parm.capture.readbuffers = 3; 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci cp->capability = V4L2_CAP_TIMEPERFRAME; 94662306a36Sopenharmony_ci cp->timeperframe.numerator = 1; 94762306a36Sopenharmony_ci cp->timeperframe.denominator = vc->fps; 94862306a36Sopenharmony_ci return 0; 94962306a36Sopenharmony_ci} 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_cistatic int tw686x_s_parm(struct file *file, void *priv, 95262306a36Sopenharmony_ci struct v4l2_streamparm *sp) 95362306a36Sopenharmony_ci{ 95462306a36Sopenharmony_ci struct tw686x_video_channel *vc = video_drvdata(file); 95562306a36Sopenharmony_ci struct v4l2_captureparm *cp = &sp->parm.capture; 95662306a36Sopenharmony_ci unsigned int denominator = cp->timeperframe.denominator; 95762306a36Sopenharmony_ci unsigned int numerator = cp->timeperframe.numerator; 95862306a36Sopenharmony_ci unsigned int fps; 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci if (vb2_is_busy(&vc->vidq)) 96162306a36Sopenharmony_ci return -EBUSY; 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci fps = (!numerator || !denominator) ? 0 : denominator / numerator; 96462306a36Sopenharmony_ci if (vc->fps != fps) 96562306a36Sopenharmony_ci tw686x_set_framerate(vc, fps); 96662306a36Sopenharmony_ci return tw686x_g_parm(file, priv, sp); 96762306a36Sopenharmony_ci} 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_cistatic int tw686x_enum_fmt_vid_cap(struct file *file, void *priv, 97062306a36Sopenharmony_ci struct v4l2_fmtdesc *f) 97162306a36Sopenharmony_ci{ 97262306a36Sopenharmony_ci if (f->index >= ARRAY_SIZE(formats)) 97362306a36Sopenharmony_ci return -EINVAL; 97462306a36Sopenharmony_ci f->pixelformat = formats[f->index].fourcc; 97562306a36Sopenharmony_ci return 0; 97662306a36Sopenharmony_ci} 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_cistatic void tw686x_set_input(struct tw686x_video_channel *vc, unsigned int i) 97962306a36Sopenharmony_ci{ 98062306a36Sopenharmony_ci u32 val; 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci vc->input = i; 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci val = reg_read(vc->dev, VDMA_CHANNEL_CONFIG[vc->ch]); 98562306a36Sopenharmony_ci val &= ~(0x3 << 30); 98662306a36Sopenharmony_ci val |= i << 30; 98762306a36Sopenharmony_ci reg_write(vc->dev, VDMA_CHANNEL_CONFIG[vc->ch], val); 98862306a36Sopenharmony_ci} 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_cistatic int tw686x_s_input(struct file *file, void *priv, unsigned int i) 99162306a36Sopenharmony_ci{ 99262306a36Sopenharmony_ci struct tw686x_video_channel *vc = video_drvdata(file); 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci if (i >= TW686X_INPUTS_PER_CH) 99562306a36Sopenharmony_ci return -EINVAL; 99662306a36Sopenharmony_ci if (i == vc->input) 99762306a36Sopenharmony_ci return 0; 99862306a36Sopenharmony_ci /* 99962306a36Sopenharmony_ci * Not sure we are able to support on the fly input change 100062306a36Sopenharmony_ci */ 100162306a36Sopenharmony_ci if (vb2_is_busy(&vc->vidq)) 100262306a36Sopenharmony_ci return -EBUSY; 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci tw686x_set_input(vc, i); 100562306a36Sopenharmony_ci return 0; 100662306a36Sopenharmony_ci} 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_cistatic int tw686x_g_input(struct file *file, void *priv, unsigned int *i) 100962306a36Sopenharmony_ci{ 101062306a36Sopenharmony_ci struct tw686x_video_channel *vc = video_drvdata(file); 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci *i = vc->input; 101362306a36Sopenharmony_ci return 0; 101462306a36Sopenharmony_ci} 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_cistatic int tw686x_enum_input(struct file *file, void *priv, 101762306a36Sopenharmony_ci struct v4l2_input *i) 101862306a36Sopenharmony_ci{ 101962306a36Sopenharmony_ci struct tw686x_video_channel *vc = video_drvdata(file); 102062306a36Sopenharmony_ci unsigned int vidstat; 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci if (i->index >= TW686X_INPUTS_PER_CH) 102362306a36Sopenharmony_ci return -EINVAL; 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci snprintf(i->name, sizeof(i->name), "Composite%d", i->index); 102662306a36Sopenharmony_ci i->type = V4L2_INPUT_TYPE_CAMERA; 102762306a36Sopenharmony_ci i->std = vc->device->tvnorms; 102862306a36Sopenharmony_ci i->capabilities = V4L2_IN_CAP_STD; 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci vidstat = reg_read(vc->dev, VIDSTAT[vc->ch]); 103162306a36Sopenharmony_ci i->status = 0; 103262306a36Sopenharmony_ci if (vidstat & TW686X_VIDSTAT_VDLOSS) 103362306a36Sopenharmony_ci i->status |= V4L2_IN_ST_NO_SIGNAL; 103462306a36Sopenharmony_ci if (!(vidstat & TW686X_VIDSTAT_HLOCK)) 103562306a36Sopenharmony_ci i->status |= V4L2_IN_ST_NO_H_LOCK; 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci return 0; 103862306a36Sopenharmony_ci} 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_cistatic const struct v4l2_file_operations tw686x_video_fops = { 104162306a36Sopenharmony_ci .owner = THIS_MODULE, 104262306a36Sopenharmony_ci .open = v4l2_fh_open, 104362306a36Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 104462306a36Sopenharmony_ci .release = vb2_fop_release, 104562306a36Sopenharmony_ci .poll = vb2_fop_poll, 104662306a36Sopenharmony_ci .read = vb2_fop_read, 104762306a36Sopenharmony_ci .mmap = vb2_fop_mmap, 104862306a36Sopenharmony_ci}; 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops tw686x_video_ioctl_ops = { 105162306a36Sopenharmony_ci .vidioc_querycap = tw686x_querycap, 105262306a36Sopenharmony_ci .vidioc_g_fmt_vid_cap = tw686x_g_fmt_vid_cap, 105362306a36Sopenharmony_ci .vidioc_s_fmt_vid_cap = tw686x_s_fmt_vid_cap, 105462306a36Sopenharmony_ci .vidioc_enum_fmt_vid_cap = tw686x_enum_fmt_vid_cap, 105562306a36Sopenharmony_ci .vidioc_try_fmt_vid_cap = tw686x_try_fmt_vid_cap, 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci .vidioc_querystd = tw686x_querystd, 105862306a36Sopenharmony_ci .vidioc_g_std = tw686x_g_std, 105962306a36Sopenharmony_ci .vidioc_s_std = tw686x_s_std, 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci .vidioc_g_parm = tw686x_g_parm, 106262306a36Sopenharmony_ci .vidioc_s_parm = tw686x_s_parm, 106362306a36Sopenharmony_ci .vidioc_enum_framesizes = tw686x_enum_framesizes, 106462306a36Sopenharmony_ci .vidioc_enum_frameintervals = tw686x_enum_frameintervals, 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci .vidioc_enum_input = tw686x_enum_input, 106762306a36Sopenharmony_ci .vidioc_g_input = tw686x_g_input, 106862306a36Sopenharmony_ci .vidioc_s_input = tw686x_s_input, 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci .vidioc_reqbufs = vb2_ioctl_reqbufs, 107162306a36Sopenharmony_ci .vidioc_querybuf = vb2_ioctl_querybuf, 107262306a36Sopenharmony_ci .vidioc_qbuf = vb2_ioctl_qbuf, 107362306a36Sopenharmony_ci .vidioc_dqbuf = vb2_ioctl_dqbuf, 107462306a36Sopenharmony_ci .vidioc_create_bufs = vb2_ioctl_create_bufs, 107562306a36Sopenharmony_ci .vidioc_streamon = vb2_ioctl_streamon, 107662306a36Sopenharmony_ci .vidioc_streamoff = vb2_ioctl_streamoff, 107762306a36Sopenharmony_ci .vidioc_prepare_buf = vb2_ioctl_prepare_buf, 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci .vidioc_log_status = v4l2_ctrl_log_status, 108062306a36Sopenharmony_ci .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, 108162306a36Sopenharmony_ci .vidioc_unsubscribe_event = v4l2_event_unsubscribe, 108262306a36Sopenharmony_ci}; 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_civoid tw686x_video_irq(struct tw686x_dev *dev, unsigned long requests, 108562306a36Sopenharmony_ci unsigned int pb_status, unsigned int fifo_status, 108662306a36Sopenharmony_ci unsigned int *reset_ch) 108762306a36Sopenharmony_ci{ 108862306a36Sopenharmony_ci struct tw686x_video_channel *vc; 108962306a36Sopenharmony_ci unsigned long flags; 109062306a36Sopenharmony_ci unsigned int ch, pb; 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci for_each_set_bit(ch, &requests, max_channels(dev)) { 109362306a36Sopenharmony_ci vc = &dev->video_channels[ch]; 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci /* 109662306a36Sopenharmony_ci * This can either be a blue frame (with signal-lost bit set) 109762306a36Sopenharmony_ci * or a good frame (with signal-lost bit clear). If we have just 109862306a36Sopenharmony_ci * got signal, then this channel needs resetting. 109962306a36Sopenharmony_ci */ 110062306a36Sopenharmony_ci if (vc->no_signal && !(fifo_status & BIT(ch))) { 110162306a36Sopenharmony_ci v4l2_printk(KERN_DEBUG, &dev->v4l2_dev, 110262306a36Sopenharmony_ci "video%d: signal recovered\n", vc->num); 110362306a36Sopenharmony_ci vc->no_signal = false; 110462306a36Sopenharmony_ci *reset_ch |= BIT(ch); 110562306a36Sopenharmony_ci vc->pb = 0; 110662306a36Sopenharmony_ci continue; 110762306a36Sopenharmony_ci } 110862306a36Sopenharmony_ci vc->no_signal = !!(fifo_status & BIT(ch)); 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci /* Check FIFO errors only if there's signal */ 111162306a36Sopenharmony_ci if (!vc->no_signal) { 111262306a36Sopenharmony_ci u32 fifo_ov, fifo_bad; 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci fifo_ov = (fifo_status >> 24) & BIT(ch); 111562306a36Sopenharmony_ci fifo_bad = (fifo_status >> 16) & BIT(ch); 111662306a36Sopenharmony_ci if (fifo_ov || fifo_bad) { 111762306a36Sopenharmony_ci /* Mark this channel for reset */ 111862306a36Sopenharmony_ci v4l2_printk(KERN_DEBUG, &dev->v4l2_dev, 111962306a36Sopenharmony_ci "video%d: FIFO error\n", vc->num); 112062306a36Sopenharmony_ci *reset_ch |= BIT(ch); 112162306a36Sopenharmony_ci vc->pb = 0; 112262306a36Sopenharmony_ci continue; 112362306a36Sopenharmony_ci } 112462306a36Sopenharmony_ci } 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci pb = !!(pb_status & BIT(ch)); 112762306a36Sopenharmony_ci if (vc->pb != pb) { 112862306a36Sopenharmony_ci /* Mark this channel for reset */ 112962306a36Sopenharmony_ci v4l2_printk(KERN_DEBUG, &dev->v4l2_dev, 113062306a36Sopenharmony_ci "video%d: unexpected p-b buffer!\n", 113162306a36Sopenharmony_ci vc->num); 113262306a36Sopenharmony_ci *reset_ch |= BIT(ch); 113362306a36Sopenharmony_ci vc->pb = 0; 113462306a36Sopenharmony_ci continue; 113562306a36Sopenharmony_ci } 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci spin_lock_irqsave(&vc->qlock, flags); 113862306a36Sopenharmony_ci tw686x_buf_done(vc, pb); 113962306a36Sopenharmony_ci dev->dma_ops->buf_refill(vc, pb); 114062306a36Sopenharmony_ci spin_unlock_irqrestore(&vc->qlock, flags); 114162306a36Sopenharmony_ci } 114262306a36Sopenharmony_ci} 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_civoid tw686x_video_free(struct tw686x_dev *dev) 114562306a36Sopenharmony_ci{ 114662306a36Sopenharmony_ci unsigned int ch, pb; 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_ci for (ch = 0; ch < max_channels(dev); ch++) { 114962306a36Sopenharmony_ci struct tw686x_video_channel *vc = &dev->video_channels[ch]; 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci video_unregister_device(vc->device); 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci if (dev->dma_ops->free) 115462306a36Sopenharmony_ci for (pb = 0; pb < 2; pb++) 115562306a36Sopenharmony_ci dev->dma_ops->free(vc, pb); 115662306a36Sopenharmony_ci } 115762306a36Sopenharmony_ci} 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ciint tw686x_video_init(struct tw686x_dev *dev) 116062306a36Sopenharmony_ci{ 116162306a36Sopenharmony_ci unsigned int ch, val; 116262306a36Sopenharmony_ci int err; 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci if (dev->dma_mode == TW686X_DMA_MODE_MEMCPY) 116562306a36Sopenharmony_ci dev->dma_ops = &memcpy_dma_ops; 116662306a36Sopenharmony_ci else if (dev->dma_mode == TW686X_DMA_MODE_CONTIG) 116762306a36Sopenharmony_ci dev->dma_ops = &contig_dma_ops; 116862306a36Sopenharmony_ci else if (dev->dma_mode == TW686X_DMA_MODE_SG) 116962306a36Sopenharmony_ci dev->dma_ops = &sg_dma_ops; 117062306a36Sopenharmony_ci else 117162306a36Sopenharmony_ci return -EINVAL; 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci err = v4l2_device_register(&dev->pci_dev->dev, &dev->v4l2_dev); 117462306a36Sopenharmony_ci if (err) 117562306a36Sopenharmony_ci return err; 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci if (dev->dma_ops->setup) { 117862306a36Sopenharmony_ci err = dev->dma_ops->setup(dev); 117962306a36Sopenharmony_ci if (err) 118062306a36Sopenharmony_ci return err; 118162306a36Sopenharmony_ci } 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci /* Initialize vc->dev and vc->ch for the error path */ 118462306a36Sopenharmony_ci for (ch = 0; ch < max_channels(dev); ch++) { 118562306a36Sopenharmony_ci struct tw686x_video_channel *vc = &dev->video_channels[ch]; 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci vc->dev = dev; 118862306a36Sopenharmony_ci vc->ch = ch; 118962306a36Sopenharmony_ci } 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci for (ch = 0; ch < max_channels(dev); ch++) { 119262306a36Sopenharmony_ci struct tw686x_video_channel *vc = &dev->video_channels[ch]; 119362306a36Sopenharmony_ci struct video_device *vdev; 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci mutex_init(&vc->vb_mutex); 119662306a36Sopenharmony_ci spin_lock_init(&vc->qlock); 119762306a36Sopenharmony_ci INIT_LIST_HEAD(&vc->vidq_queued); 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci /* default settings */ 120062306a36Sopenharmony_ci err = tw686x_set_standard(vc, V4L2_STD_NTSC); 120162306a36Sopenharmony_ci if (err) 120262306a36Sopenharmony_ci goto error; 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci err = tw686x_set_format(vc, formats[0].fourcc, 120562306a36Sopenharmony_ci TW686X_VIDEO_WIDTH, 120662306a36Sopenharmony_ci TW686X_VIDEO_HEIGHT(vc->video_standard), 120762306a36Sopenharmony_ci true); 120862306a36Sopenharmony_ci if (err) 120962306a36Sopenharmony_ci goto error; 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci tw686x_set_input(vc, 0); 121262306a36Sopenharmony_ci tw686x_set_framerate(vc, 30); 121362306a36Sopenharmony_ci reg_write(dev, VDELAY_LO[ch], 0x14); 121462306a36Sopenharmony_ci reg_write(dev, HACTIVE_LO[ch], 0xd0); 121562306a36Sopenharmony_ci reg_write(dev, VIDEO_SIZE[ch], 0); 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci vc->vidq.io_modes = VB2_READ | VB2_MMAP | VB2_DMABUF; 121862306a36Sopenharmony_ci vc->vidq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 121962306a36Sopenharmony_ci vc->vidq.drv_priv = vc; 122062306a36Sopenharmony_ci vc->vidq.buf_struct_size = sizeof(struct tw686x_v4l2_buf); 122162306a36Sopenharmony_ci vc->vidq.ops = &tw686x_video_qops; 122262306a36Sopenharmony_ci vc->vidq.mem_ops = dev->dma_ops->mem_ops; 122362306a36Sopenharmony_ci vc->vidq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; 122462306a36Sopenharmony_ci vc->vidq.min_buffers_needed = 2; 122562306a36Sopenharmony_ci vc->vidq.lock = &vc->vb_mutex; 122662306a36Sopenharmony_ci vc->vidq.gfp_flags = dev->dma_mode != TW686X_DMA_MODE_MEMCPY ? 122762306a36Sopenharmony_ci GFP_DMA32 : 0; 122862306a36Sopenharmony_ci vc->vidq.dev = &dev->pci_dev->dev; 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci err = vb2_queue_init(&vc->vidq); 123162306a36Sopenharmony_ci if (err) { 123262306a36Sopenharmony_ci v4l2_err(&dev->v4l2_dev, 123362306a36Sopenharmony_ci "dma%d: cannot init vb2 queue\n", ch); 123462306a36Sopenharmony_ci goto error; 123562306a36Sopenharmony_ci } 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci err = v4l2_ctrl_handler_init(&vc->ctrl_handler, 4); 123862306a36Sopenharmony_ci if (err) { 123962306a36Sopenharmony_ci v4l2_err(&dev->v4l2_dev, 124062306a36Sopenharmony_ci "dma%d: cannot init ctrl handler\n", ch); 124162306a36Sopenharmony_ci goto error; 124262306a36Sopenharmony_ci } 124362306a36Sopenharmony_ci v4l2_ctrl_new_std(&vc->ctrl_handler, &ctrl_ops, 124462306a36Sopenharmony_ci V4L2_CID_BRIGHTNESS, -128, 127, 1, 0); 124562306a36Sopenharmony_ci v4l2_ctrl_new_std(&vc->ctrl_handler, &ctrl_ops, 124662306a36Sopenharmony_ci V4L2_CID_CONTRAST, 0, 255, 1, 100); 124762306a36Sopenharmony_ci v4l2_ctrl_new_std(&vc->ctrl_handler, &ctrl_ops, 124862306a36Sopenharmony_ci V4L2_CID_SATURATION, 0, 255, 1, 128); 124962306a36Sopenharmony_ci v4l2_ctrl_new_std(&vc->ctrl_handler, &ctrl_ops, 125062306a36Sopenharmony_ci V4L2_CID_HUE, -128, 127, 1, 0); 125162306a36Sopenharmony_ci err = vc->ctrl_handler.error; 125262306a36Sopenharmony_ci if (err) 125362306a36Sopenharmony_ci goto error; 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_ci err = v4l2_ctrl_handler_setup(&vc->ctrl_handler); 125662306a36Sopenharmony_ci if (err) 125762306a36Sopenharmony_ci goto error; 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci vdev = video_device_alloc(); 126062306a36Sopenharmony_ci if (!vdev) { 126162306a36Sopenharmony_ci v4l2_err(&dev->v4l2_dev, 126262306a36Sopenharmony_ci "dma%d: unable to allocate device\n", ch); 126362306a36Sopenharmony_ci err = -ENOMEM; 126462306a36Sopenharmony_ci goto error; 126562306a36Sopenharmony_ci } 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_ci snprintf(vdev->name, sizeof(vdev->name), "%s video", dev->name); 126862306a36Sopenharmony_ci vdev->fops = &tw686x_video_fops; 126962306a36Sopenharmony_ci vdev->ioctl_ops = &tw686x_video_ioctl_ops; 127062306a36Sopenharmony_ci vdev->release = video_device_release; 127162306a36Sopenharmony_ci vdev->v4l2_dev = &dev->v4l2_dev; 127262306a36Sopenharmony_ci vdev->queue = &vc->vidq; 127362306a36Sopenharmony_ci vdev->tvnorms = V4L2_STD_525_60 | V4L2_STD_625_50; 127462306a36Sopenharmony_ci vdev->minor = -1; 127562306a36Sopenharmony_ci vdev->lock = &vc->vb_mutex; 127662306a36Sopenharmony_ci vdev->ctrl_handler = &vc->ctrl_handler; 127762306a36Sopenharmony_ci vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | 127862306a36Sopenharmony_ci V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; 127962306a36Sopenharmony_ci vc->device = vdev; 128062306a36Sopenharmony_ci video_set_drvdata(vdev, vc); 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_ci err = video_register_device(vdev, VFL_TYPE_VIDEO, -1); 128362306a36Sopenharmony_ci if (err < 0) { 128462306a36Sopenharmony_ci video_device_release(vdev); 128562306a36Sopenharmony_ci goto error; 128662306a36Sopenharmony_ci } 128762306a36Sopenharmony_ci vc->num = vdev->num; 128862306a36Sopenharmony_ci } 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ci val = TW686X_DEF_PHASE_REF; 129162306a36Sopenharmony_ci for (ch = 0; ch < max_channels(dev); ch++) 129262306a36Sopenharmony_ci val |= dev->dma_ops->hw_dma_mode << (16 + ch * 2); 129362306a36Sopenharmony_ci reg_write(dev, PHASE_REF, val); 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci reg_write(dev, MISC2[0], 0xe7); 129662306a36Sopenharmony_ci reg_write(dev, VCTRL1[0], 0xcc); 129762306a36Sopenharmony_ci reg_write(dev, LOOP[0], 0xa5); 129862306a36Sopenharmony_ci if (max_channels(dev) > 4) { 129962306a36Sopenharmony_ci reg_write(dev, VCTRL1[1], 0xcc); 130062306a36Sopenharmony_ci reg_write(dev, LOOP[1], 0xa5); 130162306a36Sopenharmony_ci reg_write(dev, MISC2[1], 0xe7); 130262306a36Sopenharmony_ci } 130362306a36Sopenharmony_ci return 0; 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_cierror: 130662306a36Sopenharmony_ci tw686x_video_free(dev); 130762306a36Sopenharmony_ci return err; 130862306a36Sopenharmony_ci} 1309