162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * cobalt V4L2 API 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Derived from ivtv-ioctl.c and cx18-fileops.c 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright 2012-2015 Cisco Systems, Inc. and/or its affiliates. 862306a36Sopenharmony_ci * All rights reserved. 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1262306a36Sopenharmony_ci#include <linux/delay.h> 1362306a36Sopenharmony_ci#include <linux/math64.h> 1462306a36Sopenharmony_ci#include <linux/pci.h> 1562306a36Sopenharmony_ci#include <linux/v4l2-dv-timings.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <media/v4l2-ctrls.h> 1862306a36Sopenharmony_ci#include <media/v4l2-event.h> 1962306a36Sopenharmony_ci#include <media/v4l2-dv-timings.h> 2062306a36Sopenharmony_ci#include <media/i2c/adv7604.h> 2162306a36Sopenharmony_ci#include <media/i2c/adv7842.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include "cobalt-alsa.h" 2462306a36Sopenharmony_ci#include "cobalt-cpld.h" 2562306a36Sopenharmony_ci#include "cobalt-driver.h" 2662306a36Sopenharmony_ci#include "cobalt-v4l2.h" 2762306a36Sopenharmony_ci#include "cobalt-irq.h" 2862306a36Sopenharmony_ci#include "cobalt-omnitek.h" 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic const struct v4l2_dv_timings cea1080p60 = V4L2_DV_BT_CEA_1920X1080P60; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/* vb2 DMA streaming ops */ 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic int cobalt_queue_setup(struct vb2_queue *q, 3562306a36Sopenharmony_ci unsigned int *num_buffers, unsigned int *num_planes, 3662306a36Sopenharmony_ci unsigned int sizes[], struct device *alloc_devs[]) 3762306a36Sopenharmony_ci{ 3862306a36Sopenharmony_ci struct cobalt_stream *s = q->drv_priv; 3962306a36Sopenharmony_ci unsigned size = s->stride * s->height; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci if (*num_buffers < 3) 4262306a36Sopenharmony_ci *num_buffers = 3; 4362306a36Sopenharmony_ci if (*num_buffers > NR_BUFS) 4462306a36Sopenharmony_ci *num_buffers = NR_BUFS; 4562306a36Sopenharmony_ci if (*num_planes) 4662306a36Sopenharmony_ci return sizes[0] < size ? -EINVAL : 0; 4762306a36Sopenharmony_ci *num_planes = 1; 4862306a36Sopenharmony_ci sizes[0] = size; 4962306a36Sopenharmony_ci return 0; 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic int cobalt_buf_init(struct vb2_buffer *vb) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci struct cobalt_stream *s = vb->vb2_queue->drv_priv; 5562306a36Sopenharmony_ci struct cobalt *cobalt = s->cobalt; 5662306a36Sopenharmony_ci const size_t max_pages_per_line = 5762306a36Sopenharmony_ci (COBALT_MAX_WIDTH * COBALT_MAX_BPP) / PAGE_SIZE + 2; 5862306a36Sopenharmony_ci const size_t bytes = 5962306a36Sopenharmony_ci COBALT_MAX_HEIGHT * max_pages_per_line * 0x20; 6062306a36Sopenharmony_ci const size_t audio_bytes = ((1920 * 4) / PAGE_SIZE + 1) * 0x20; 6162306a36Sopenharmony_ci struct sg_dma_desc_info *desc = &s->dma_desc_info[vb->index]; 6262306a36Sopenharmony_ci struct sg_table *sg_desc = vb2_dma_sg_plane_desc(vb, 0); 6362306a36Sopenharmony_ci unsigned size; 6462306a36Sopenharmony_ci int ret; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci size = s->stride * s->height; 6762306a36Sopenharmony_ci if (vb2_plane_size(vb, 0) < size) { 6862306a36Sopenharmony_ci cobalt_info("data will not fit into plane (%lu < %u)\n", 6962306a36Sopenharmony_ci vb2_plane_size(vb, 0), size); 7062306a36Sopenharmony_ci return -EINVAL; 7162306a36Sopenharmony_ci } 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci if (desc->virt == NULL) { 7462306a36Sopenharmony_ci desc->dev = &cobalt->pci_dev->dev; 7562306a36Sopenharmony_ci descriptor_list_allocate(desc, 7662306a36Sopenharmony_ci s->is_audio ? audio_bytes : bytes); 7762306a36Sopenharmony_ci if (desc->virt == NULL) 7862306a36Sopenharmony_ci return -ENOMEM; 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci ret = descriptor_list_create(cobalt, sg_desc->sgl, 8162306a36Sopenharmony_ci !s->is_output, sg_desc->nents, size, 8262306a36Sopenharmony_ci s->width * s->bpp, s->stride, desc); 8362306a36Sopenharmony_ci if (ret) 8462306a36Sopenharmony_ci descriptor_list_free(desc); 8562306a36Sopenharmony_ci return ret; 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic void cobalt_buf_cleanup(struct vb2_buffer *vb) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci struct cobalt_stream *s = vb->vb2_queue->drv_priv; 9162306a36Sopenharmony_ci struct sg_dma_desc_info *desc = &s->dma_desc_info[vb->index]; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci descriptor_list_free(desc); 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic int cobalt_buf_prepare(struct vb2_buffer *vb) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 9962306a36Sopenharmony_ci struct cobalt_stream *s = vb->vb2_queue->drv_priv; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci vb2_set_plane_payload(vb, 0, s->stride * s->height); 10262306a36Sopenharmony_ci vbuf->field = V4L2_FIELD_NONE; 10362306a36Sopenharmony_ci return 0; 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistatic void chain_all_buffers(struct cobalt_stream *s) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci struct sg_dma_desc_info *desc[NR_BUFS]; 10962306a36Sopenharmony_ci struct cobalt_buffer *cb; 11062306a36Sopenharmony_ci struct list_head *p; 11162306a36Sopenharmony_ci int i = 0; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci list_for_each(p, &s->bufs) { 11462306a36Sopenharmony_ci cb = list_entry(p, struct cobalt_buffer, list); 11562306a36Sopenharmony_ci desc[i] = &s->dma_desc_info[cb->vb.vb2_buf.index]; 11662306a36Sopenharmony_ci if (i > 0) 11762306a36Sopenharmony_ci descriptor_list_chain(desc[i-1], desc[i]); 11862306a36Sopenharmony_ci i++; 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic void cobalt_buf_queue(struct vb2_buffer *vb) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 12562306a36Sopenharmony_ci struct vb2_queue *q = vb->vb2_queue; 12662306a36Sopenharmony_ci struct cobalt_stream *s = q->drv_priv; 12762306a36Sopenharmony_ci struct cobalt_buffer *cb = to_cobalt_buffer(vbuf); 12862306a36Sopenharmony_ci struct sg_dma_desc_info *desc = &s->dma_desc_info[vb->index]; 12962306a36Sopenharmony_ci unsigned long flags; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci /* Prepare new buffer */ 13262306a36Sopenharmony_ci descriptor_list_loopback(desc); 13362306a36Sopenharmony_ci descriptor_list_interrupt_disable(desc); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci spin_lock_irqsave(&s->irqlock, flags); 13662306a36Sopenharmony_ci list_add_tail(&cb->list, &s->bufs); 13762306a36Sopenharmony_ci chain_all_buffers(s); 13862306a36Sopenharmony_ci spin_unlock_irqrestore(&s->irqlock, flags); 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic void cobalt_enable_output(struct cobalt_stream *s) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci struct cobalt *cobalt = s->cobalt; 14462306a36Sopenharmony_ci struct v4l2_bt_timings *bt = &s->timings.bt; 14562306a36Sopenharmony_ci struct m00514_syncgen_flow_evcnt_regmap __iomem *vo = 14662306a36Sopenharmony_ci COBALT_TX_BASE(cobalt); 14762306a36Sopenharmony_ci unsigned fmt = s->pixfmt != V4L2_PIX_FMT_BGR32 ? 14862306a36Sopenharmony_ci M00514_CONTROL_BITMAP_FORMAT_16_BPP_MSK : 0; 14962306a36Sopenharmony_ci struct v4l2_subdev_format sd_fmt = { 15062306a36Sopenharmony_ci .which = V4L2_SUBDEV_FORMAT_ACTIVE, 15162306a36Sopenharmony_ci }; 15262306a36Sopenharmony_ci u64 clk = bt->pixelclock; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci if (bt->flags & V4L2_DV_FL_REDUCED_FPS) 15562306a36Sopenharmony_ci clk = div_u64(clk * 1000ULL, 1001); 15662306a36Sopenharmony_ci if (!cobalt_cpld_set_freq(cobalt, clk)) { 15762306a36Sopenharmony_ci cobalt_err("pixelclock out of range\n"); 15862306a36Sopenharmony_ci return; 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci sd_fmt.format.colorspace = s->colorspace; 16262306a36Sopenharmony_ci sd_fmt.format.xfer_func = s->xfer_func; 16362306a36Sopenharmony_ci sd_fmt.format.ycbcr_enc = s->ycbcr_enc; 16462306a36Sopenharmony_ci sd_fmt.format.quantization = s->quantization; 16562306a36Sopenharmony_ci sd_fmt.format.width = bt->width; 16662306a36Sopenharmony_ci sd_fmt.format.height = bt->height; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci /* Set up FDMA packer */ 16962306a36Sopenharmony_ci switch (s->pixfmt) { 17062306a36Sopenharmony_ci case V4L2_PIX_FMT_YUYV: 17162306a36Sopenharmony_ci sd_fmt.format.code = MEDIA_BUS_FMT_UYVY8_1X16; 17262306a36Sopenharmony_ci break; 17362306a36Sopenharmony_ci case V4L2_PIX_FMT_BGR32: 17462306a36Sopenharmony_ci sd_fmt.format.code = MEDIA_BUS_FMT_RGB888_1X24; 17562306a36Sopenharmony_ci break; 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci v4l2_subdev_call(s->sd, pad, set_fmt, NULL, &sd_fmt); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci iowrite32(0, &vo->control); 18062306a36Sopenharmony_ci /* 1080p60 */ 18162306a36Sopenharmony_ci iowrite32(bt->hsync, &vo->sync_generator_h_sync_length); 18262306a36Sopenharmony_ci iowrite32(bt->hbackporch, &vo->sync_generator_h_backporch_length); 18362306a36Sopenharmony_ci iowrite32(bt->width, &vo->sync_generator_h_active_length); 18462306a36Sopenharmony_ci iowrite32(bt->hfrontporch, &vo->sync_generator_h_frontporch_length); 18562306a36Sopenharmony_ci iowrite32(bt->vsync, &vo->sync_generator_v_sync_length); 18662306a36Sopenharmony_ci iowrite32(bt->vbackporch, &vo->sync_generator_v_backporch_length); 18762306a36Sopenharmony_ci iowrite32(bt->height, &vo->sync_generator_v_active_length); 18862306a36Sopenharmony_ci iowrite32(bt->vfrontporch, &vo->sync_generator_v_frontporch_length); 18962306a36Sopenharmony_ci iowrite32(0x9900c1, &vo->error_color); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci iowrite32(M00514_CONTROL_BITMAP_SYNC_GENERATOR_LOAD_PARAM_MSK | fmt, 19262306a36Sopenharmony_ci &vo->control); 19362306a36Sopenharmony_ci iowrite32(M00514_CONTROL_BITMAP_EVCNT_CLEAR_MSK | fmt, &vo->control); 19462306a36Sopenharmony_ci iowrite32(M00514_CONTROL_BITMAP_SYNC_GENERATOR_ENABLE_MSK | 19562306a36Sopenharmony_ci M00514_CONTROL_BITMAP_FLOW_CTRL_OUTPUT_ENABLE_MSK | 19662306a36Sopenharmony_ci fmt, &vo->control); 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic void cobalt_enable_input(struct cobalt_stream *s) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci struct cobalt *cobalt = s->cobalt; 20262306a36Sopenharmony_ci int ch = (int)s->video_channel; 20362306a36Sopenharmony_ci struct m00235_fdma_packer_regmap __iomem *packer; 20462306a36Sopenharmony_ci struct v4l2_subdev_format sd_fmt_yuyv = { 20562306a36Sopenharmony_ci .pad = s->pad_source, 20662306a36Sopenharmony_ci .which = V4L2_SUBDEV_FORMAT_ACTIVE, 20762306a36Sopenharmony_ci .format.code = MEDIA_BUS_FMT_YUYV8_1X16, 20862306a36Sopenharmony_ci }; 20962306a36Sopenharmony_ci struct v4l2_subdev_format sd_fmt_rgb = { 21062306a36Sopenharmony_ci .pad = s->pad_source, 21162306a36Sopenharmony_ci .which = V4L2_SUBDEV_FORMAT_ACTIVE, 21262306a36Sopenharmony_ci .format.code = MEDIA_BUS_FMT_RGB888_1X24, 21362306a36Sopenharmony_ci }; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci cobalt_dbg(1, "video_channel %d (%s, %s)\n", 21662306a36Sopenharmony_ci s->video_channel, 21762306a36Sopenharmony_ci s->input == 0 ? "hdmi" : "generator", 21862306a36Sopenharmony_ci "YUYV"); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci packer = COBALT_CVI_PACKER(cobalt, ch); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci /* Set up FDMA packer */ 22362306a36Sopenharmony_ci switch (s->pixfmt) { 22462306a36Sopenharmony_ci case V4L2_PIX_FMT_YUYV: 22562306a36Sopenharmony_ci iowrite32(M00235_CONTROL_BITMAP_ENABLE_MSK | 22662306a36Sopenharmony_ci (1 << M00235_CONTROL_BITMAP_PACK_FORMAT_OFST), 22762306a36Sopenharmony_ci &packer->control); 22862306a36Sopenharmony_ci v4l2_subdev_call(s->sd, pad, set_fmt, NULL, 22962306a36Sopenharmony_ci &sd_fmt_yuyv); 23062306a36Sopenharmony_ci break; 23162306a36Sopenharmony_ci case V4L2_PIX_FMT_RGB24: 23262306a36Sopenharmony_ci iowrite32(M00235_CONTROL_BITMAP_ENABLE_MSK | 23362306a36Sopenharmony_ci (2 << M00235_CONTROL_BITMAP_PACK_FORMAT_OFST), 23462306a36Sopenharmony_ci &packer->control); 23562306a36Sopenharmony_ci v4l2_subdev_call(s->sd, pad, set_fmt, NULL, 23662306a36Sopenharmony_ci &sd_fmt_rgb); 23762306a36Sopenharmony_ci break; 23862306a36Sopenharmony_ci case V4L2_PIX_FMT_BGR32: 23962306a36Sopenharmony_ci iowrite32(M00235_CONTROL_BITMAP_ENABLE_MSK | 24062306a36Sopenharmony_ci M00235_CONTROL_BITMAP_ENDIAN_FORMAT_MSK | 24162306a36Sopenharmony_ci (3 << M00235_CONTROL_BITMAP_PACK_FORMAT_OFST), 24262306a36Sopenharmony_ci &packer->control); 24362306a36Sopenharmony_ci v4l2_subdev_call(s->sd, pad, set_fmt, NULL, 24462306a36Sopenharmony_ci &sd_fmt_rgb); 24562306a36Sopenharmony_ci break; 24662306a36Sopenharmony_ci } 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_cistatic void cobalt_dma_start_streaming(struct cobalt_stream *s) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci struct cobalt *cobalt = s->cobalt; 25262306a36Sopenharmony_ci int rx = s->video_channel; 25362306a36Sopenharmony_ci struct m00460_evcnt_regmap __iomem *evcnt = 25462306a36Sopenharmony_ci COBALT_CVI_EVCNT(cobalt, rx); 25562306a36Sopenharmony_ci struct cobalt_buffer *cb; 25662306a36Sopenharmony_ci unsigned long flags; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci spin_lock_irqsave(&s->irqlock, flags); 25962306a36Sopenharmony_ci if (!s->is_output) { 26062306a36Sopenharmony_ci iowrite32(M00460_CONTROL_BITMAP_CLEAR_MSK, &evcnt->control); 26162306a36Sopenharmony_ci iowrite32(M00460_CONTROL_BITMAP_ENABLE_MSK, &evcnt->control); 26262306a36Sopenharmony_ci } else { 26362306a36Sopenharmony_ci struct m00514_syncgen_flow_evcnt_regmap __iomem *vo = 26462306a36Sopenharmony_ci COBALT_TX_BASE(cobalt); 26562306a36Sopenharmony_ci u32 ctrl = ioread32(&vo->control); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci ctrl &= ~(M00514_CONTROL_BITMAP_EVCNT_ENABLE_MSK | 26862306a36Sopenharmony_ci M00514_CONTROL_BITMAP_EVCNT_CLEAR_MSK); 26962306a36Sopenharmony_ci iowrite32(ctrl | M00514_CONTROL_BITMAP_EVCNT_CLEAR_MSK, 27062306a36Sopenharmony_ci &vo->control); 27162306a36Sopenharmony_ci iowrite32(ctrl | M00514_CONTROL_BITMAP_EVCNT_ENABLE_MSK, 27262306a36Sopenharmony_ci &vo->control); 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci cb = list_first_entry(&s->bufs, struct cobalt_buffer, list); 27562306a36Sopenharmony_ci omni_sg_dma_start(s, &s->dma_desc_info[cb->vb.vb2_buf.index]); 27662306a36Sopenharmony_ci spin_unlock_irqrestore(&s->irqlock, flags); 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_cistatic int cobalt_start_streaming(struct vb2_queue *q, unsigned int count) 28062306a36Sopenharmony_ci{ 28162306a36Sopenharmony_ci struct cobalt_stream *s = q->drv_priv; 28262306a36Sopenharmony_ci struct cobalt *cobalt = s->cobalt; 28362306a36Sopenharmony_ci struct m00233_video_measure_regmap __iomem *vmr; 28462306a36Sopenharmony_ci struct m00473_freewheel_regmap __iomem *fw; 28562306a36Sopenharmony_ci struct m00479_clk_loss_detector_regmap __iomem *clkloss; 28662306a36Sopenharmony_ci int rx = s->video_channel; 28762306a36Sopenharmony_ci struct m00389_cvi_regmap __iomem *cvi = COBALT_CVI(cobalt, rx); 28862306a36Sopenharmony_ci struct m00460_evcnt_regmap __iomem *evcnt = COBALT_CVI_EVCNT(cobalt, rx); 28962306a36Sopenharmony_ci struct v4l2_bt_timings *bt = &s->timings.bt; 29062306a36Sopenharmony_ci u64 tot_size; 29162306a36Sopenharmony_ci u32 clk_freq; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci if (s->is_audio) 29462306a36Sopenharmony_ci goto done; 29562306a36Sopenharmony_ci if (s->is_output) { 29662306a36Sopenharmony_ci s->unstable_frame = false; 29762306a36Sopenharmony_ci cobalt_enable_output(s); 29862306a36Sopenharmony_ci goto done; 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci cobalt_enable_input(s); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci fw = COBALT_CVI_FREEWHEEL(cobalt, rx); 30462306a36Sopenharmony_ci vmr = COBALT_CVI_VMR(cobalt, rx); 30562306a36Sopenharmony_ci clkloss = COBALT_CVI_CLK_LOSS(cobalt, rx); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci iowrite32(M00460_CONTROL_BITMAP_CLEAR_MSK, &evcnt->control); 30862306a36Sopenharmony_ci iowrite32(M00460_CONTROL_BITMAP_ENABLE_MSK, &evcnt->control); 30962306a36Sopenharmony_ci iowrite32(bt->width, &cvi->frame_width); 31062306a36Sopenharmony_ci iowrite32(bt->height, &cvi->frame_height); 31162306a36Sopenharmony_ci tot_size = V4L2_DV_BT_FRAME_WIDTH(bt) * V4L2_DV_BT_FRAME_HEIGHT(bt); 31262306a36Sopenharmony_ci iowrite32(div_u64((u64)V4L2_DV_BT_FRAME_WIDTH(bt) * COBALT_CLK * 4, 31362306a36Sopenharmony_ci bt->pixelclock), &vmr->hsync_timeout_val); 31462306a36Sopenharmony_ci iowrite32(M00233_CONTROL_BITMAP_ENABLE_MEASURE_MSK, &vmr->control); 31562306a36Sopenharmony_ci clk_freq = ioread32(&fw->clk_freq); 31662306a36Sopenharmony_ci iowrite32(clk_freq / 1000000, &clkloss->ref_clk_cnt_val); 31762306a36Sopenharmony_ci /* The lower bound for the clock frequency is 0.5% lower as is 31862306a36Sopenharmony_ci * allowed by the spec */ 31962306a36Sopenharmony_ci iowrite32(div_u64(bt->pixelclock * 995, 1000000000), 32062306a36Sopenharmony_ci &clkloss->test_clk_cnt_val); 32162306a36Sopenharmony_ci /* will be enabled after the first frame has been received */ 32262306a36Sopenharmony_ci iowrite32(bt->width * bt->height, &fw->active_length); 32362306a36Sopenharmony_ci iowrite32(div_u64((u64)clk_freq * tot_size, bt->pixelclock), 32462306a36Sopenharmony_ci &fw->total_length); 32562306a36Sopenharmony_ci iowrite32(M00233_IRQ_TRIGGERS_BITMAP_VACTIVE_AREA_MSK | 32662306a36Sopenharmony_ci M00233_IRQ_TRIGGERS_BITMAP_HACTIVE_AREA_MSK, 32762306a36Sopenharmony_ci &vmr->irq_triggers); 32862306a36Sopenharmony_ci iowrite32(0, &cvi->control); 32962306a36Sopenharmony_ci iowrite32(M00233_CONTROL_BITMAP_ENABLE_MEASURE_MSK, &vmr->control); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci iowrite32(0xff, &fw->output_color); 33262306a36Sopenharmony_ci iowrite32(M00479_CTRL_BITMAP_ENABLE_MSK, &clkloss->ctrl); 33362306a36Sopenharmony_ci iowrite32(M00473_CTRL_BITMAP_ENABLE_MSK | 33462306a36Sopenharmony_ci M00473_CTRL_BITMAP_FORCE_FREEWHEEL_MODE_MSK, &fw->ctrl); 33562306a36Sopenharmony_ci s->unstable_frame = true; 33662306a36Sopenharmony_ci s->enable_freewheel = false; 33762306a36Sopenharmony_ci s->enable_cvi = false; 33862306a36Sopenharmony_ci s->skip_first_frames = 0; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_cidone: 34162306a36Sopenharmony_ci s->sequence = 0; 34262306a36Sopenharmony_ci cobalt_dma_start_streaming(s); 34362306a36Sopenharmony_ci return 0; 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_cistatic void cobalt_dma_stop_streaming(struct cobalt_stream *s) 34762306a36Sopenharmony_ci{ 34862306a36Sopenharmony_ci struct cobalt *cobalt = s->cobalt; 34962306a36Sopenharmony_ci struct sg_dma_desc_info *desc; 35062306a36Sopenharmony_ci struct cobalt_buffer *cb; 35162306a36Sopenharmony_ci struct list_head *p; 35262306a36Sopenharmony_ci unsigned long flags; 35362306a36Sopenharmony_ci int timeout_msec = 100; 35462306a36Sopenharmony_ci int rx = s->video_channel; 35562306a36Sopenharmony_ci struct m00460_evcnt_regmap __iomem *evcnt = 35662306a36Sopenharmony_ci COBALT_CVI_EVCNT(cobalt, rx); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci if (!s->is_output) { 35962306a36Sopenharmony_ci iowrite32(0, &evcnt->control); 36062306a36Sopenharmony_ci } else if (!s->is_audio) { 36162306a36Sopenharmony_ci struct m00514_syncgen_flow_evcnt_regmap __iomem *vo = 36262306a36Sopenharmony_ci COBALT_TX_BASE(cobalt); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci iowrite32(M00514_CONTROL_BITMAP_EVCNT_CLEAR_MSK, &vo->control); 36562306a36Sopenharmony_ci iowrite32(0, &vo->control); 36662306a36Sopenharmony_ci } 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci /* Try to stop the DMA engine gracefully */ 36962306a36Sopenharmony_ci spin_lock_irqsave(&s->irqlock, flags); 37062306a36Sopenharmony_ci list_for_each(p, &s->bufs) { 37162306a36Sopenharmony_ci cb = list_entry(p, struct cobalt_buffer, list); 37262306a36Sopenharmony_ci desc = &s->dma_desc_info[cb->vb.vb2_buf.index]; 37362306a36Sopenharmony_ci /* Stop DMA after this descriptor chain */ 37462306a36Sopenharmony_ci descriptor_list_end_of_chain(desc); 37562306a36Sopenharmony_ci } 37662306a36Sopenharmony_ci spin_unlock_irqrestore(&s->irqlock, flags); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci /* Wait 100 millisecond for DMA to finish, abort on timeout. */ 37962306a36Sopenharmony_ci if (!wait_event_timeout(s->q.done_wq, is_dma_done(s), 38062306a36Sopenharmony_ci msecs_to_jiffies(timeout_msec))) { 38162306a36Sopenharmony_ci omni_sg_dma_abort_channel(s); 38262306a36Sopenharmony_ci pr_warn("aborted\n"); 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci cobalt_write_bar0(cobalt, DMA_INTERRUPT_STATUS_REG, 38562306a36Sopenharmony_ci 1 << s->dma_channel); 38662306a36Sopenharmony_ci} 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_cistatic void cobalt_stop_streaming(struct vb2_queue *q) 38962306a36Sopenharmony_ci{ 39062306a36Sopenharmony_ci struct cobalt_stream *s = q->drv_priv; 39162306a36Sopenharmony_ci struct cobalt *cobalt = s->cobalt; 39262306a36Sopenharmony_ci int rx = s->video_channel; 39362306a36Sopenharmony_ci struct m00233_video_measure_regmap __iomem *vmr; 39462306a36Sopenharmony_ci struct m00473_freewheel_regmap __iomem *fw; 39562306a36Sopenharmony_ci struct m00479_clk_loss_detector_regmap __iomem *clkloss; 39662306a36Sopenharmony_ci struct cobalt_buffer *cb; 39762306a36Sopenharmony_ci struct list_head *p, *safe; 39862306a36Sopenharmony_ci unsigned long flags; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci cobalt_dma_stop_streaming(s); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci /* Return all buffers to user space */ 40362306a36Sopenharmony_ci spin_lock_irqsave(&s->irqlock, flags); 40462306a36Sopenharmony_ci list_for_each_safe(p, safe, &s->bufs) { 40562306a36Sopenharmony_ci cb = list_entry(p, struct cobalt_buffer, list); 40662306a36Sopenharmony_ci list_del(&cb->list); 40762306a36Sopenharmony_ci vb2_buffer_done(&cb->vb.vb2_buf, VB2_BUF_STATE_ERROR); 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci spin_unlock_irqrestore(&s->irqlock, flags); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci if (s->is_audio || s->is_output) 41262306a36Sopenharmony_ci return; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci fw = COBALT_CVI_FREEWHEEL(cobalt, rx); 41562306a36Sopenharmony_ci vmr = COBALT_CVI_VMR(cobalt, rx); 41662306a36Sopenharmony_ci clkloss = COBALT_CVI_CLK_LOSS(cobalt, rx); 41762306a36Sopenharmony_ci iowrite32(0, &vmr->control); 41862306a36Sopenharmony_ci iowrite32(M00233_CONTROL_BITMAP_ENABLE_MEASURE_MSK, &vmr->control); 41962306a36Sopenharmony_ci iowrite32(0, &fw->ctrl); 42062306a36Sopenharmony_ci iowrite32(0, &clkloss->ctrl); 42162306a36Sopenharmony_ci} 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_cistatic const struct vb2_ops cobalt_qops = { 42462306a36Sopenharmony_ci .queue_setup = cobalt_queue_setup, 42562306a36Sopenharmony_ci .buf_init = cobalt_buf_init, 42662306a36Sopenharmony_ci .buf_cleanup = cobalt_buf_cleanup, 42762306a36Sopenharmony_ci .buf_prepare = cobalt_buf_prepare, 42862306a36Sopenharmony_ci .buf_queue = cobalt_buf_queue, 42962306a36Sopenharmony_ci .start_streaming = cobalt_start_streaming, 43062306a36Sopenharmony_ci .stop_streaming = cobalt_stop_streaming, 43162306a36Sopenharmony_ci .wait_prepare = vb2_ops_wait_prepare, 43262306a36Sopenharmony_ci .wait_finish = vb2_ops_wait_finish, 43362306a36Sopenharmony_ci}; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci/* V4L2 ioctls */ 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG 43862306a36Sopenharmony_cistatic int cobalt_cobaltc(struct cobalt *cobalt, unsigned int cmd, void *arg) 43962306a36Sopenharmony_ci{ 44062306a36Sopenharmony_ci struct v4l2_dbg_register *regs = arg; 44162306a36Sopenharmony_ci void __iomem *adrs = cobalt->bar1 + regs->reg; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci cobalt_info("cobalt_cobaltc: adrs = %p\n", adrs); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 44662306a36Sopenharmony_ci return -EPERM; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci regs->size = 4; 44962306a36Sopenharmony_ci if (cmd == VIDIOC_DBG_S_REGISTER) 45062306a36Sopenharmony_ci iowrite32(regs->val, adrs); 45162306a36Sopenharmony_ci else 45262306a36Sopenharmony_ci regs->val = ioread32(adrs); 45362306a36Sopenharmony_ci return 0; 45462306a36Sopenharmony_ci} 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_cistatic int cobalt_g_register(struct file *file, void *priv_fh, 45762306a36Sopenharmony_ci struct v4l2_dbg_register *reg) 45862306a36Sopenharmony_ci{ 45962306a36Sopenharmony_ci struct cobalt_stream *s = video_drvdata(file); 46062306a36Sopenharmony_ci struct cobalt *cobalt = s->cobalt; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci return cobalt_cobaltc(cobalt, VIDIOC_DBG_G_REGISTER, reg); 46362306a36Sopenharmony_ci} 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_cistatic int cobalt_s_register(struct file *file, void *priv_fh, 46662306a36Sopenharmony_ci const struct v4l2_dbg_register *reg) 46762306a36Sopenharmony_ci{ 46862306a36Sopenharmony_ci struct cobalt_stream *s = video_drvdata(file); 46962306a36Sopenharmony_ci struct cobalt *cobalt = s->cobalt; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci return cobalt_cobaltc(cobalt, VIDIOC_DBG_S_REGISTER, 47262306a36Sopenharmony_ci (struct v4l2_dbg_register *)reg); 47362306a36Sopenharmony_ci} 47462306a36Sopenharmony_ci#endif 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_cistatic int cobalt_querycap(struct file *file, void *priv_fh, 47762306a36Sopenharmony_ci struct v4l2_capability *vcap) 47862306a36Sopenharmony_ci{ 47962306a36Sopenharmony_ci struct cobalt_stream *s = video_drvdata(file); 48062306a36Sopenharmony_ci struct cobalt *cobalt = s->cobalt; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci strscpy(vcap->driver, "cobalt", sizeof(vcap->driver)); 48362306a36Sopenharmony_ci strscpy(vcap->card, "cobalt", sizeof(vcap->card)); 48462306a36Sopenharmony_ci snprintf(vcap->bus_info, sizeof(vcap->bus_info), 48562306a36Sopenharmony_ci "PCIe:%s", pci_name(cobalt->pci_dev)); 48662306a36Sopenharmony_ci vcap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_READWRITE | 48762306a36Sopenharmony_ci V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_DEVICE_CAPS; 48862306a36Sopenharmony_ci if (cobalt->have_hsma_tx) 48962306a36Sopenharmony_ci vcap->capabilities |= V4L2_CAP_VIDEO_OUTPUT; 49062306a36Sopenharmony_ci return 0; 49162306a36Sopenharmony_ci} 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_cistatic void cobalt_video_input_status_show(struct cobalt_stream *s) 49462306a36Sopenharmony_ci{ 49562306a36Sopenharmony_ci struct m00389_cvi_regmap __iomem *cvi; 49662306a36Sopenharmony_ci struct m00233_video_measure_regmap __iomem *vmr; 49762306a36Sopenharmony_ci struct m00473_freewheel_regmap __iomem *fw; 49862306a36Sopenharmony_ci struct m00479_clk_loss_detector_regmap __iomem *clkloss; 49962306a36Sopenharmony_ci struct m00235_fdma_packer_regmap __iomem *packer; 50062306a36Sopenharmony_ci int rx = s->video_channel; 50162306a36Sopenharmony_ci struct cobalt *cobalt = s->cobalt; 50262306a36Sopenharmony_ci u32 cvi_ctrl, cvi_stat; 50362306a36Sopenharmony_ci u32 vmr_ctrl, vmr_stat; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci cvi = COBALT_CVI(cobalt, rx); 50662306a36Sopenharmony_ci vmr = COBALT_CVI_VMR(cobalt, rx); 50762306a36Sopenharmony_ci fw = COBALT_CVI_FREEWHEEL(cobalt, rx); 50862306a36Sopenharmony_ci clkloss = COBALT_CVI_CLK_LOSS(cobalt, rx); 50962306a36Sopenharmony_ci packer = COBALT_CVI_PACKER(cobalt, rx); 51062306a36Sopenharmony_ci cvi_ctrl = ioread32(&cvi->control); 51162306a36Sopenharmony_ci cvi_stat = ioread32(&cvi->status); 51262306a36Sopenharmony_ci vmr_ctrl = ioread32(&vmr->control); 51362306a36Sopenharmony_ci vmr_stat = ioread32(&vmr->status); 51462306a36Sopenharmony_ci cobalt_info("rx%d: cvi resolution: %dx%d\n", rx, 51562306a36Sopenharmony_ci ioread32(&cvi->frame_width), ioread32(&cvi->frame_height)); 51662306a36Sopenharmony_ci cobalt_info("rx%d: cvi control: %s%s%s\n", rx, 51762306a36Sopenharmony_ci (cvi_ctrl & M00389_CONTROL_BITMAP_ENABLE_MSK) ? 51862306a36Sopenharmony_ci "enable " : "disable ", 51962306a36Sopenharmony_ci (cvi_ctrl & M00389_CONTROL_BITMAP_HSYNC_POLARITY_LOW_MSK) ? 52062306a36Sopenharmony_ci "HSync- " : "HSync+ ", 52162306a36Sopenharmony_ci (cvi_ctrl & M00389_CONTROL_BITMAP_VSYNC_POLARITY_LOW_MSK) ? 52262306a36Sopenharmony_ci "VSync- " : "VSync+ "); 52362306a36Sopenharmony_ci cobalt_info("rx%d: cvi status: %s%s\n", rx, 52462306a36Sopenharmony_ci (cvi_stat & M00389_STATUS_BITMAP_LOCK_MSK) ? 52562306a36Sopenharmony_ci "lock " : "no-lock ", 52662306a36Sopenharmony_ci (cvi_stat & M00389_STATUS_BITMAP_ERROR_MSK) ? 52762306a36Sopenharmony_ci "error " : "no-error "); 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci cobalt_info("rx%d: Measurements: %s%s%s%s%s%s%s\n", rx, 53062306a36Sopenharmony_ci (vmr_ctrl & M00233_CONTROL_BITMAP_HSYNC_POLARITY_LOW_MSK) ? 53162306a36Sopenharmony_ci "HSync- " : "HSync+ ", 53262306a36Sopenharmony_ci (vmr_ctrl & M00233_CONTROL_BITMAP_VSYNC_POLARITY_LOW_MSK) ? 53362306a36Sopenharmony_ci "VSync- " : "VSync+ ", 53462306a36Sopenharmony_ci (vmr_ctrl & M00233_CONTROL_BITMAP_ENABLE_MEASURE_MSK) ? 53562306a36Sopenharmony_ci "enabled " : "disabled ", 53662306a36Sopenharmony_ci (vmr_ctrl & M00233_CONTROL_BITMAP_ENABLE_INTERRUPT_MSK) ? 53762306a36Sopenharmony_ci "irq-enabled " : "irq-disabled ", 53862306a36Sopenharmony_ci (vmr_ctrl & M00233_CONTROL_BITMAP_UPDATE_ON_HSYNC_MSK) ? 53962306a36Sopenharmony_ci "update-on-hsync " : "", 54062306a36Sopenharmony_ci (vmr_stat & M00233_STATUS_BITMAP_HSYNC_TIMEOUT_MSK) ? 54162306a36Sopenharmony_ci "hsync-timeout " : "", 54262306a36Sopenharmony_ci (vmr_stat & M00233_STATUS_BITMAP_INIT_DONE_MSK) ? 54362306a36Sopenharmony_ci "init-done" : ""); 54462306a36Sopenharmony_ci cobalt_info("rx%d: irq_status: 0x%02x irq_triggers: 0x%02x\n", rx, 54562306a36Sopenharmony_ci ioread32(&vmr->irq_status) & 0xff, 54662306a36Sopenharmony_ci ioread32(&vmr->irq_triggers) & 0xff); 54762306a36Sopenharmony_ci cobalt_info("rx%d: vsync: %d\n", rx, ioread32(&vmr->vsync_time)); 54862306a36Sopenharmony_ci cobalt_info("rx%d: vbp: %d\n", rx, ioread32(&vmr->vback_porch)); 54962306a36Sopenharmony_ci cobalt_info("rx%d: vact: %d\n", rx, ioread32(&vmr->vactive_area)); 55062306a36Sopenharmony_ci cobalt_info("rx%d: vfb: %d\n", rx, ioread32(&vmr->vfront_porch)); 55162306a36Sopenharmony_ci cobalt_info("rx%d: hsync: %d\n", rx, ioread32(&vmr->hsync_time)); 55262306a36Sopenharmony_ci cobalt_info("rx%d: hbp: %d\n", rx, ioread32(&vmr->hback_porch)); 55362306a36Sopenharmony_ci cobalt_info("rx%d: hact: %d\n", rx, ioread32(&vmr->hactive_area)); 55462306a36Sopenharmony_ci cobalt_info("rx%d: hfb: %d\n", rx, ioread32(&vmr->hfront_porch)); 55562306a36Sopenharmony_ci cobalt_info("rx%d: Freewheeling: %s%s%s\n", rx, 55662306a36Sopenharmony_ci (ioread32(&fw->ctrl) & M00473_CTRL_BITMAP_ENABLE_MSK) ? 55762306a36Sopenharmony_ci "enabled " : "disabled ", 55862306a36Sopenharmony_ci (ioread32(&fw->ctrl) & M00473_CTRL_BITMAP_FORCE_FREEWHEEL_MODE_MSK) ? 55962306a36Sopenharmony_ci "forced " : "", 56062306a36Sopenharmony_ci (ioread32(&fw->status) & M00473_STATUS_BITMAP_FREEWHEEL_MODE_MSK) ? 56162306a36Sopenharmony_ci "freewheeling " : "video-passthrough "); 56262306a36Sopenharmony_ci iowrite32(0xff, &vmr->irq_status); 56362306a36Sopenharmony_ci cobalt_info("rx%d: Clock Loss Detection: %s%s\n", rx, 56462306a36Sopenharmony_ci (ioread32(&clkloss->ctrl) & M00479_CTRL_BITMAP_ENABLE_MSK) ? 56562306a36Sopenharmony_ci "enabled " : "disabled ", 56662306a36Sopenharmony_ci (ioread32(&clkloss->status) & M00479_STATUS_BITMAP_CLOCK_MISSING_MSK) ? 56762306a36Sopenharmony_ci "clock-missing " : "found-clock "); 56862306a36Sopenharmony_ci cobalt_info("rx%d: Packer: %x\n", rx, ioread32(&packer->control)); 56962306a36Sopenharmony_ci} 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_cistatic int cobalt_log_status(struct file *file, void *priv_fh) 57262306a36Sopenharmony_ci{ 57362306a36Sopenharmony_ci struct cobalt_stream *s = video_drvdata(file); 57462306a36Sopenharmony_ci struct cobalt *cobalt = s->cobalt; 57562306a36Sopenharmony_ci struct m00514_syncgen_flow_evcnt_regmap __iomem *vo = 57662306a36Sopenharmony_ci COBALT_TX_BASE(cobalt); 57762306a36Sopenharmony_ci u8 stat; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci cobalt_info("%s", cobalt->hdl_info); 58062306a36Sopenharmony_ci cobalt_info("sysctrl: %08x, sysstat: %08x\n", 58162306a36Sopenharmony_ci cobalt_g_sysctrl(cobalt), 58262306a36Sopenharmony_ci cobalt_g_sysstat(cobalt)); 58362306a36Sopenharmony_ci cobalt_info("dma channel: %d, video channel: %d\n", 58462306a36Sopenharmony_ci s->dma_channel, s->video_channel); 58562306a36Sopenharmony_ci cobalt_pcie_status_show(cobalt); 58662306a36Sopenharmony_ci cobalt_cpld_status(cobalt); 58762306a36Sopenharmony_ci cobalt_irq_log_status(cobalt); 58862306a36Sopenharmony_ci v4l2_subdev_call(s->sd, core, log_status); 58962306a36Sopenharmony_ci if (!s->is_output) { 59062306a36Sopenharmony_ci cobalt_video_input_status_show(s); 59162306a36Sopenharmony_ci return 0; 59262306a36Sopenharmony_ci } 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci stat = ioread32(&vo->rd_status); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci cobalt_info("tx: status: %s%s\n", 59762306a36Sopenharmony_ci (stat & M00514_RD_STATUS_BITMAP_FLOW_CTRL_NO_DATA_ERROR_MSK) ? 59862306a36Sopenharmony_ci "no_data " : "", 59962306a36Sopenharmony_ci (stat & M00514_RD_STATUS_BITMAP_READY_BUFFER_FULL_MSK) ? 60062306a36Sopenharmony_ci "ready_buffer_full " : ""); 60162306a36Sopenharmony_ci cobalt_info("tx: evcnt: %d\n", ioread32(&vo->rd_evcnt_count)); 60262306a36Sopenharmony_ci return 0; 60362306a36Sopenharmony_ci} 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_cistatic int cobalt_enum_dv_timings(struct file *file, void *priv_fh, 60662306a36Sopenharmony_ci struct v4l2_enum_dv_timings *timings) 60762306a36Sopenharmony_ci{ 60862306a36Sopenharmony_ci struct cobalt_stream *s = video_drvdata(file); 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci if (s->input == 1) { 61162306a36Sopenharmony_ci if (timings->index) 61262306a36Sopenharmony_ci return -EINVAL; 61362306a36Sopenharmony_ci memset(timings->reserved, 0, sizeof(timings->reserved)); 61462306a36Sopenharmony_ci timings->timings = cea1080p60; 61562306a36Sopenharmony_ci return 0; 61662306a36Sopenharmony_ci } 61762306a36Sopenharmony_ci timings->pad = 0; 61862306a36Sopenharmony_ci return v4l2_subdev_call(s->sd, 61962306a36Sopenharmony_ci pad, enum_dv_timings, timings); 62062306a36Sopenharmony_ci} 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_cistatic int cobalt_s_dv_timings(struct file *file, void *priv_fh, 62362306a36Sopenharmony_ci struct v4l2_dv_timings *timings) 62462306a36Sopenharmony_ci{ 62562306a36Sopenharmony_ci struct cobalt_stream *s = video_drvdata(file); 62662306a36Sopenharmony_ci int err; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci if (s->input == 1) { 62962306a36Sopenharmony_ci *timings = cea1080p60; 63062306a36Sopenharmony_ci return 0; 63162306a36Sopenharmony_ci } 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci if (v4l2_match_dv_timings(timings, &s->timings, 0, true)) 63462306a36Sopenharmony_ci return 0; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci if (vb2_is_busy(&s->q)) 63762306a36Sopenharmony_ci return -EBUSY; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci err = v4l2_subdev_call(s->sd, 64062306a36Sopenharmony_ci video, s_dv_timings, timings); 64162306a36Sopenharmony_ci if (!err) { 64262306a36Sopenharmony_ci s->timings = *timings; 64362306a36Sopenharmony_ci s->width = timings->bt.width; 64462306a36Sopenharmony_ci s->height = timings->bt.height; 64562306a36Sopenharmony_ci s->stride = timings->bt.width * s->bpp; 64662306a36Sopenharmony_ci } 64762306a36Sopenharmony_ci return err; 64862306a36Sopenharmony_ci} 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_cistatic int cobalt_g_dv_timings(struct file *file, void *priv_fh, 65162306a36Sopenharmony_ci struct v4l2_dv_timings *timings) 65262306a36Sopenharmony_ci{ 65362306a36Sopenharmony_ci struct cobalt_stream *s = video_drvdata(file); 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci if (s->input == 1) { 65662306a36Sopenharmony_ci *timings = cea1080p60; 65762306a36Sopenharmony_ci return 0; 65862306a36Sopenharmony_ci } 65962306a36Sopenharmony_ci return v4l2_subdev_call(s->sd, 66062306a36Sopenharmony_ci video, g_dv_timings, timings); 66162306a36Sopenharmony_ci} 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_cistatic int cobalt_query_dv_timings(struct file *file, void *priv_fh, 66462306a36Sopenharmony_ci struct v4l2_dv_timings *timings) 66562306a36Sopenharmony_ci{ 66662306a36Sopenharmony_ci struct cobalt_stream *s = video_drvdata(file); 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci if (s->input == 1) { 66962306a36Sopenharmony_ci *timings = cea1080p60; 67062306a36Sopenharmony_ci return 0; 67162306a36Sopenharmony_ci } 67262306a36Sopenharmony_ci return v4l2_subdev_call(s->sd, 67362306a36Sopenharmony_ci video, query_dv_timings, timings); 67462306a36Sopenharmony_ci} 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_cistatic int cobalt_dv_timings_cap(struct file *file, void *priv_fh, 67762306a36Sopenharmony_ci struct v4l2_dv_timings_cap *cap) 67862306a36Sopenharmony_ci{ 67962306a36Sopenharmony_ci struct cobalt_stream *s = video_drvdata(file); 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci cap->pad = 0; 68262306a36Sopenharmony_ci return v4l2_subdev_call(s->sd, 68362306a36Sopenharmony_ci pad, dv_timings_cap, cap); 68462306a36Sopenharmony_ci} 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_cistatic int cobalt_enum_fmt_vid_cap(struct file *file, void *priv_fh, 68762306a36Sopenharmony_ci struct v4l2_fmtdesc *f) 68862306a36Sopenharmony_ci{ 68962306a36Sopenharmony_ci switch (f->index) { 69062306a36Sopenharmony_ci case 0: 69162306a36Sopenharmony_ci f->pixelformat = V4L2_PIX_FMT_YUYV; 69262306a36Sopenharmony_ci break; 69362306a36Sopenharmony_ci case 1: 69462306a36Sopenharmony_ci f->pixelformat = V4L2_PIX_FMT_RGB24; 69562306a36Sopenharmony_ci break; 69662306a36Sopenharmony_ci case 2: 69762306a36Sopenharmony_ci f->pixelformat = V4L2_PIX_FMT_BGR32; 69862306a36Sopenharmony_ci break; 69962306a36Sopenharmony_ci default: 70062306a36Sopenharmony_ci return -EINVAL; 70162306a36Sopenharmony_ci } 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci return 0; 70462306a36Sopenharmony_ci} 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_cistatic int cobalt_g_fmt_vid_cap(struct file *file, void *priv_fh, 70762306a36Sopenharmony_ci struct v4l2_format *f) 70862306a36Sopenharmony_ci{ 70962306a36Sopenharmony_ci struct cobalt_stream *s = video_drvdata(file); 71062306a36Sopenharmony_ci struct v4l2_pix_format *pix = &f->fmt.pix; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci pix->width = s->width; 71362306a36Sopenharmony_ci pix->height = s->height; 71462306a36Sopenharmony_ci pix->bytesperline = s->stride; 71562306a36Sopenharmony_ci pix->field = V4L2_FIELD_NONE; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci if (s->input == 1) { 71862306a36Sopenharmony_ci pix->colorspace = V4L2_COLORSPACE_SRGB; 71962306a36Sopenharmony_ci } else { 72062306a36Sopenharmony_ci struct v4l2_subdev_format sd_fmt = { 72162306a36Sopenharmony_ci .pad = s->pad_source, 72262306a36Sopenharmony_ci .which = V4L2_SUBDEV_FORMAT_ACTIVE, 72362306a36Sopenharmony_ci }; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci v4l2_subdev_call(s->sd, pad, get_fmt, NULL, &sd_fmt); 72662306a36Sopenharmony_ci v4l2_fill_pix_format(pix, &sd_fmt.format); 72762306a36Sopenharmony_ci } 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci pix->pixelformat = s->pixfmt; 73062306a36Sopenharmony_ci pix->sizeimage = pix->bytesperline * pix->height; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci return 0; 73362306a36Sopenharmony_ci} 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_cistatic int cobalt_try_fmt_vid_cap(struct file *file, void *priv_fh, 73662306a36Sopenharmony_ci struct v4l2_format *f) 73762306a36Sopenharmony_ci{ 73862306a36Sopenharmony_ci struct cobalt_stream *s = video_drvdata(file); 73962306a36Sopenharmony_ci struct v4l2_pix_format *pix = &f->fmt.pix; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci /* Check for min (QCIF) and max (Full HD) size */ 74262306a36Sopenharmony_ci if ((pix->width < 176) || (pix->height < 144)) { 74362306a36Sopenharmony_ci pix->width = 176; 74462306a36Sopenharmony_ci pix->height = 144; 74562306a36Sopenharmony_ci } 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci if ((pix->width > 1920) || (pix->height > 1080)) { 74862306a36Sopenharmony_ci pix->width = 1920; 74962306a36Sopenharmony_ci pix->height = 1080; 75062306a36Sopenharmony_ci } 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci /* Make width multiple of 4 */ 75362306a36Sopenharmony_ci pix->width &= ~0x3; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci /* Make height multiple of 2 */ 75662306a36Sopenharmony_ci pix->height &= ~0x1; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci if (s->input == 1) { 75962306a36Sopenharmony_ci /* Generator => fixed format only */ 76062306a36Sopenharmony_ci pix->width = 1920; 76162306a36Sopenharmony_ci pix->height = 1080; 76262306a36Sopenharmony_ci pix->colorspace = V4L2_COLORSPACE_SRGB; 76362306a36Sopenharmony_ci } else { 76462306a36Sopenharmony_ci struct v4l2_subdev_format sd_fmt = { 76562306a36Sopenharmony_ci .pad = s->pad_source, 76662306a36Sopenharmony_ci .which = V4L2_SUBDEV_FORMAT_ACTIVE, 76762306a36Sopenharmony_ci }; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci v4l2_subdev_call(s->sd, pad, get_fmt, NULL, &sd_fmt); 77062306a36Sopenharmony_ci v4l2_fill_pix_format(pix, &sd_fmt.format); 77162306a36Sopenharmony_ci } 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci switch (pix->pixelformat) { 77462306a36Sopenharmony_ci case V4L2_PIX_FMT_YUYV: 77562306a36Sopenharmony_ci default: 77662306a36Sopenharmony_ci pix->bytesperline = max(pix->bytesperline & ~0x3, 77762306a36Sopenharmony_ci pix->width * COBALT_BYTES_PER_PIXEL_YUYV); 77862306a36Sopenharmony_ci pix->pixelformat = V4L2_PIX_FMT_YUYV; 77962306a36Sopenharmony_ci break; 78062306a36Sopenharmony_ci case V4L2_PIX_FMT_RGB24: 78162306a36Sopenharmony_ci pix->bytesperline = max(pix->bytesperline & ~0x3, 78262306a36Sopenharmony_ci pix->width * COBALT_BYTES_PER_PIXEL_RGB24); 78362306a36Sopenharmony_ci break; 78462306a36Sopenharmony_ci case V4L2_PIX_FMT_BGR32: 78562306a36Sopenharmony_ci pix->bytesperline = max(pix->bytesperline & ~0x3, 78662306a36Sopenharmony_ci pix->width * COBALT_BYTES_PER_PIXEL_RGB32); 78762306a36Sopenharmony_ci break; 78862306a36Sopenharmony_ci } 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci pix->sizeimage = pix->bytesperline * pix->height; 79162306a36Sopenharmony_ci pix->field = V4L2_FIELD_NONE; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci return 0; 79462306a36Sopenharmony_ci} 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_cistatic int cobalt_s_fmt_vid_cap(struct file *file, void *priv_fh, 79762306a36Sopenharmony_ci struct v4l2_format *f) 79862306a36Sopenharmony_ci{ 79962306a36Sopenharmony_ci struct cobalt_stream *s = video_drvdata(file); 80062306a36Sopenharmony_ci struct v4l2_pix_format *pix = &f->fmt.pix; 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci if (vb2_is_busy(&s->q)) 80362306a36Sopenharmony_ci return -EBUSY; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci if (cobalt_try_fmt_vid_cap(file, priv_fh, f)) 80662306a36Sopenharmony_ci return -EINVAL; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci s->width = pix->width; 80962306a36Sopenharmony_ci s->height = pix->height; 81062306a36Sopenharmony_ci s->stride = pix->bytesperline; 81162306a36Sopenharmony_ci switch (pix->pixelformat) { 81262306a36Sopenharmony_ci case V4L2_PIX_FMT_YUYV: 81362306a36Sopenharmony_ci s->bpp = COBALT_BYTES_PER_PIXEL_YUYV; 81462306a36Sopenharmony_ci break; 81562306a36Sopenharmony_ci case V4L2_PIX_FMT_RGB24: 81662306a36Sopenharmony_ci s->bpp = COBALT_BYTES_PER_PIXEL_RGB24; 81762306a36Sopenharmony_ci break; 81862306a36Sopenharmony_ci case V4L2_PIX_FMT_BGR32: 81962306a36Sopenharmony_ci s->bpp = COBALT_BYTES_PER_PIXEL_RGB32; 82062306a36Sopenharmony_ci break; 82162306a36Sopenharmony_ci default: 82262306a36Sopenharmony_ci return -EINVAL; 82362306a36Sopenharmony_ci } 82462306a36Sopenharmony_ci s->pixfmt = pix->pixelformat; 82562306a36Sopenharmony_ci cobalt_enable_input(s); 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci return 0; 82862306a36Sopenharmony_ci} 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_cistatic int cobalt_try_fmt_vid_out(struct file *file, void *priv_fh, 83162306a36Sopenharmony_ci struct v4l2_format *f) 83262306a36Sopenharmony_ci{ 83362306a36Sopenharmony_ci struct v4l2_pix_format *pix = &f->fmt.pix; 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci /* Check for min (QCIF) and max (Full HD) size */ 83662306a36Sopenharmony_ci if ((pix->width < 176) || (pix->height < 144)) { 83762306a36Sopenharmony_ci pix->width = 176; 83862306a36Sopenharmony_ci pix->height = 144; 83962306a36Sopenharmony_ci } 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci if ((pix->width > 1920) || (pix->height > 1080)) { 84262306a36Sopenharmony_ci pix->width = 1920; 84362306a36Sopenharmony_ci pix->height = 1080; 84462306a36Sopenharmony_ci } 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci /* Make width multiple of 4 */ 84762306a36Sopenharmony_ci pix->width &= ~0x3; 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci /* Make height multiple of 2 */ 85062306a36Sopenharmony_ci pix->height &= ~0x1; 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci switch (pix->pixelformat) { 85362306a36Sopenharmony_ci case V4L2_PIX_FMT_YUYV: 85462306a36Sopenharmony_ci default: 85562306a36Sopenharmony_ci pix->bytesperline = max(pix->bytesperline & ~0x3, 85662306a36Sopenharmony_ci pix->width * COBALT_BYTES_PER_PIXEL_YUYV); 85762306a36Sopenharmony_ci pix->pixelformat = V4L2_PIX_FMT_YUYV; 85862306a36Sopenharmony_ci break; 85962306a36Sopenharmony_ci case V4L2_PIX_FMT_BGR32: 86062306a36Sopenharmony_ci pix->bytesperline = max(pix->bytesperline & ~0x3, 86162306a36Sopenharmony_ci pix->width * COBALT_BYTES_PER_PIXEL_RGB32); 86262306a36Sopenharmony_ci break; 86362306a36Sopenharmony_ci } 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci pix->sizeimage = pix->bytesperline * pix->height; 86662306a36Sopenharmony_ci pix->field = V4L2_FIELD_NONE; 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci return 0; 86962306a36Sopenharmony_ci} 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_cistatic int cobalt_g_fmt_vid_out(struct file *file, void *priv_fh, 87262306a36Sopenharmony_ci struct v4l2_format *f) 87362306a36Sopenharmony_ci{ 87462306a36Sopenharmony_ci struct cobalt_stream *s = video_drvdata(file); 87562306a36Sopenharmony_ci struct v4l2_pix_format *pix = &f->fmt.pix; 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci pix->width = s->width; 87862306a36Sopenharmony_ci pix->height = s->height; 87962306a36Sopenharmony_ci pix->bytesperline = s->stride; 88062306a36Sopenharmony_ci pix->field = V4L2_FIELD_NONE; 88162306a36Sopenharmony_ci pix->pixelformat = s->pixfmt; 88262306a36Sopenharmony_ci pix->colorspace = s->colorspace; 88362306a36Sopenharmony_ci pix->xfer_func = s->xfer_func; 88462306a36Sopenharmony_ci pix->ycbcr_enc = s->ycbcr_enc; 88562306a36Sopenharmony_ci pix->quantization = s->quantization; 88662306a36Sopenharmony_ci pix->sizeimage = pix->bytesperline * pix->height; 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci return 0; 88962306a36Sopenharmony_ci} 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_cistatic int cobalt_enum_fmt_vid_out(struct file *file, void *priv_fh, 89262306a36Sopenharmony_ci struct v4l2_fmtdesc *f) 89362306a36Sopenharmony_ci{ 89462306a36Sopenharmony_ci switch (f->index) { 89562306a36Sopenharmony_ci case 0: 89662306a36Sopenharmony_ci f->pixelformat = V4L2_PIX_FMT_YUYV; 89762306a36Sopenharmony_ci break; 89862306a36Sopenharmony_ci case 1: 89962306a36Sopenharmony_ci f->pixelformat = V4L2_PIX_FMT_BGR32; 90062306a36Sopenharmony_ci break; 90162306a36Sopenharmony_ci default: 90262306a36Sopenharmony_ci return -EINVAL; 90362306a36Sopenharmony_ci } 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci return 0; 90662306a36Sopenharmony_ci} 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_cistatic int cobalt_s_fmt_vid_out(struct file *file, void *priv_fh, 90962306a36Sopenharmony_ci struct v4l2_format *f) 91062306a36Sopenharmony_ci{ 91162306a36Sopenharmony_ci struct cobalt_stream *s = video_drvdata(file); 91262306a36Sopenharmony_ci struct v4l2_pix_format *pix = &f->fmt.pix; 91362306a36Sopenharmony_ci struct v4l2_subdev_format sd_fmt = { 91462306a36Sopenharmony_ci .which = V4L2_SUBDEV_FORMAT_ACTIVE, 91562306a36Sopenharmony_ci }; 91662306a36Sopenharmony_ci u32 code; 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci if (cobalt_try_fmt_vid_out(file, priv_fh, f)) 91962306a36Sopenharmony_ci return -EINVAL; 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci if (vb2_is_busy(&s->q) && (pix->pixelformat != s->pixfmt || 92262306a36Sopenharmony_ci pix->width != s->width || pix->height != s->height || 92362306a36Sopenharmony_ci pix->bytesperline != s->stride)) 92462306a36Sopenharmony_ci return -EBUSY; 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci switch (pix->pixelformat) { 92762306a36Sopenharmony_ci case V4L2_PIX_FMT_YUYV: 92862306a36Sopenharmony_ci s->bpp = COBALT_BYTES_PER_PIXEL_YUYV; 92962306a36Sopenharmony_ci code = MEDIA_BUS_FMT_UYVY8_1X16; 93062306a36Sopenharmony_ci break; 93162306a36Sopenharmony_ci case V4L2_PIX_FMT_BGR32: 93262306a36Sopenharmony_ci s->bpp = COBALT_BYTES_PER_PIXEL_RGB32; 93362306a36Sopenharmony_ci code = MEDIA_BUS_FMT_RGB888_1X24; 93462306a36Sopenharmony_ci break; 93562306a36Sopenharmony_ci default: 93662306a36Sopenharmony_ci return -EINVAL; 93762306a36Sopenharmony_ci } 93862306a36Sopenharmony_ci s->width = pix->width; 93962306a36Sopenharmony_ci s->height = pix->height; 94062306a36Sopenharmony_ci s->stride = pix->bytesperline; 94162306a36Sopenharmony_ci s->pixfmt = pix->pixelformat; 94262306a36Sopenharmony_ci s->colorspace = pix->colorspace; 94362306a36Sopenharmony_ci s->xfer_func = pix->xfer_func; 94462306a36Sopenharmony_ci s->ycbcr_enc = pix->ycbcr_enc; 94562306a36Sopenharmony_ci s->quantization = pix->quantization; 94662306a36Sopenharmony_ci v4l2_fill_mbus_format(&sd_fmt.format, pix, code); 94762306a36Sopenharmony_ci v4l2_subdev_call(s->sd, pad, set_fmt, NULL, &sd_fmt); 94862306a36Sopenharmony_ci return 0; 94962306a36Sopenharmony_ci} 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_cistatic int cobalt_enum_input(struct file *file, void *priv_fh, 95262306a36Sopenharmony_ci struct v4l2_input *inp) 95362306a36Sopenharmony_ci{ 95462306a36Sopenharmony_ci struct cobalt_stream *s = video_drvdata(file); 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci if (inp->index > 1) 95762306a36Sopenharmony_ci return -EINVAL; 95862306a36Sopenharmony_ci if (inp->index == 0) 95962306a36Sopenharmony_ci snprintf(inp->name, sizeof(inp->name), 96062306a36Sopenharmony_ci "HDMI-%d", s->video_channel); 96162306a36Sopenharmony_ci else 96262306a36Sopenharmony_ci snprintf(inp->name, sizeof(inp->name), 96362306a36Sopenharmony_ci "Generator-%d", s->video_channel); 96462306a36Sopenharmony_ci inp->type = V4L2_INPUT_TYPE_CAMERA; 96562306a36Sopenharmony_ci inp->capabilities = V4L2_IN_CAP_DV_TIMINGS; 96662306a36Sopenharmony_ci if (inp->index == 1) 96762306a36Sopenharmony_ci return 0; 96862306a36Sopenharmony_ci return v4l2_subdev_call(s->sd, 96962306a36Sopenharmony_ci video, g_input_status, &inp->status); 97062306a36Sopenharmony_ci} 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_cistatic int cobalt_g_input(struct file *file, void *priv_fh, unsigned int *i) 97362306a36Sopenharmony_ci{ 97462306a36Sopenharmony_ci struct cobalt_stream *s = video_drvdata(file); 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci *i = s->input; 97762306a36Sopenharmony_ci return 0; 97862306a36Sopenharmony_ci} 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_cistatic int cobalt_s_input(struct file *file, void *priv_fh, unsigned int i) 98162306a36Sopenharmony_ci{ 98262306a36Sopenharmony_ci struct cobalt_stream *s = video_drvdata(file); 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci if (i >= 2) 98562306a36Sopenharmony_ci return -EINVAL; 98662306a36Sopenharmony_ci if (vb2_is_busy(&s->q)) 98762306a36Sopenharmony_ci return -EBUSY; 98862306a36Sopenharmony_ci s->input = i; 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci cobalt_enable_input(s); 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci if (s->input == 1) /* Test Pattern Generator */ 99362306a36Sopenharmony_ci return 0; 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci return v4l2_subdev_call(s->sd, video, s_routing, 99662306a36Sopenharmony_ci ADV76XX_PAD_HDMI_PORT_A, 0, 0); 99762306a36Sopenharmony_ci} 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_cistatic int cobalt_enum_output(struct file *file, void *priv_fh, 100062306a36Sopenharmony_ci struct v4l2_output *out) 100162306a36Sopenharmony_ci{ 100262306a36Sopenharmony_ci if (out->index) 100362306a36Sopenharmony_ci return -EINVAL; 100462306a36Sopenharmony_ci snprintf(out->name, sizeof(out->name), "HDMI-%d", out->index); 100562306a36Sopenharmony_ci out->type = V4L2_OUTPUT_TYPE_ANALOG; 100662306a36Sopenharmony_ci out->capabilities = V4L2_OUT_CAP_DV_TIMINGS; 100762306a36Sopenharmony_ci return 0; 100862306a36Sopenharmony_ci} 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_cistatic int cobalt_g_output(struct file *file, void *priv_fh, unsigned int *i) 101162306a36Sopenharmony_ci{ 101262306a36Sopenharmony_ci *i = 0; 101362306a36Sopenharmony_ci return 0; 101462306a36Sopenharmony_ci} 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_cistatic int cobalt_s_output(struct file *file, void *priv_fh, unsigned int i) 101762306a36Sopenharmony_ci{ 101862306a36Sopenharmony_ci return i ? -EINVAL : 0; 101962306a36Sopenharmony_ci} 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_cistatic int cobalt_g_edid(struct file *file, void *fh, struct v4l2_edid *edid) 102262306a36Sopenharmony_ci{ 102362306a36Sopenharmony_ci struct cobalt_stream *s = video_drvdata(file); 102462306a36Sopenharmony_ci u32 pad = edid->pad; 102562306a36Sopenharmony_ci int ret; 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci if (edid->pad >= (s->is_output ? 1 : 2)) 102862306a36Sopenharmony_ci return -EINVAL; 102962306a36Sopenharmony_ci edid->pad = 0; 103062306a36Sopenharmony_ci ret = v4l2_subdev_call(s->sd, pad, get_edid, edid); 103162306a36Sopenharmony_ci edid->pad = pad; 103262306a36Sopenharmony_ci return ret; 103362306a36Sopenharmony_ci} 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_cistatic int cobalt_s_edid(struct file *file, void *fh, struct v4l2_edid *edid) 103662306a36Sopenharmony_ci{ 103762306a36Sopenharmony_ci struct cobalt_stream *s = video_drvdata(file); 103862306a36Sopenharmony_ci u32 pad = edid->pad; 103962306a36Sopenharmony_ci int ret; 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci if (edid->pad >= 2) 104262306a36Sopenharmony_ci return -EINVAL; 104362306a36Sopenharmony_ci edid->pad = 0; 104462306a36Sopenharmony_ci ret = v4l2_subdev_call(s->sd, pad, set_edid, edid); 104562306a36Sopenharmony_ci edid->pad = pad; 104662306a36Sopenharmony_ci return ret; 104762306a36Sopenharmony_ci} 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_cistatic int cobalt_subscribe_event(struct v4l2_fh *fh, 105062306a36Sopenharmony_ci const struct v4l2_event_subscription *sub) 105162306a36Sopenharmony_ci{ 105262306a36Sopenharmony_ci switch (sub->type) { 105362306a36Sopenharmony_ci case V4L2_EVENT_SOURCE_CHANGE: 105462306a36Sopenharmony_ci return v4l2_event_subscribe(fh, sub, 4, NULL); 105562306a36Sopenharmony_ci } 105662306a36Sopenharmony_ci return v4l2_ctrl_subscribe_event(fh, sub); 105762306a36Sopenharmony_ci} 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_cistatic int cobalt_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a) 106062306a36Sopenharmony_ci{ 106162306a36Sopenharmony_ci struct cobalt_stream *s = video_drvdata(file); 106262306a36Sopenharmony_ci struct v4l2_fract fps; 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) 106562306a36Sopenharmony_ci return -EINVAL; 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci fps = v4l2_calc_timeperframe(&s->timings); 106862306a36Sopenharmony_ci a->parm.capture.timeperframe.numerator = fps.numerator; 106962306a36Sopenharmony_ci a->parm.capture.timeperframe.denominator = fps.denominator; 107062306a36Sopenharmony_ci a->parm.capture.readbuffers = 3; 107162306a36Sopenharmony_ci return 0; 107262306a36Sopenharmony_ci} 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_cistatic int cobalt_g_pixelaspect(struct file *file, void *fh, 107562306a36Sopenharmony_ci int type, struct v4l2_fract *f) 107662306a36Sopenharmony_ci{ 107762306a36Sopenharmony_ci struct cobalt_stream *s = video_drvdata(file); 107862306a36Sopenharmony_ci struct v4l2_dv_timings timings; 107962306a36Sopenharmony_ci int err = 0; 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) 108262306a36Sopenharmony_ci return -EINVAL; 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci if (s->input == 1) 108562306a36Sopenharmony_ci timings = cea1080p60; 108662306a36Sopenharmony_ci else 108762306a36Sopenharmony_ci err = v4l2_subdev_call(s->sd, video, g_dv_timings, &timings); 108862306a36Sopenharmony_ci if (!err) 108962306a36Sopenharmony_ci *f = v4l2_dv_timings_aspect_ratio(&timings); 109062306a36Sopenharmony_ci return err; 109162306a36Sopenharmony_ci} 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_cistatic int cobalt_g_selection(struct file *file, void *fh, 109462306a36Sopenharmony_ci struct v4l2_selection *sel) 109562306a36Sopenharmony_ci{ 109662306a36Sopenharmony_ci struct cobalt_stream *s = video_drvdata(file); 109762306a36Sopenharmony_ci struct v4l2_dv_timings timings; 109862306a36Sopenharmony_ci int err = 0; 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) 110162306a36Sopenharmony_ci return -EINVAL; 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci if (s->input == 1) 110462306a36Sopenharmony_ci timings = cea1080p60; 110562306a36Sopenharmony_ci else 110662306a36Sopenharmony_ci err = v4l2_subdev_call(s->sd, video, g_dv_timings, &timings); 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci if (err) 110962306a36Sopenharmony_ci return err; 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci switch (sel->target) { 111262306a36Sopenharmony_ci case V4L2_SEL_TGT_CROP_BOUNDS: 111362306a36Sopenharmony_ci case V4L2_SEL_TGT_CROP_DEFAULT: 111462306a36Sopenharmony_ci sel->r.top = 0; 111562306a36Sopenharmony_ci sel->r.left = 0; 111662306a36Sopenharmony_ci sel->r.width = timings.bt.width; 111762306a36Sopenharmony_ci sel->r.height = timings.bt.height; 111862306a36Sopenharmony_ci break; 111962306a36Sopenharmony_ci default: 112062306a36Sopenharmony_ci return -EINVAL; 112162306a36Sopenharmony_ci } 112262306a36Sopenharmony_ci return 0; 112362306a36Sopenharmony_ci} 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops cobalt_ioctl_ops = { 112662306a36Sopenharmony_ci .vidioc_querycap = cobalt_querycap, 112762306a36Sopenharmony_ci .vidioc_g_parm = cobalt_g_parm, 112862306a36Sopenharmony_ci .vidioc_log_status = cobalt_log_status, 112962306a36Sopenharmony_ci .vidioc_streamon = vb2_ioctl_streamon, 113062306a36Sopenharmony_ci .vidioc_streamoff = vb2_ioctl_streamoff, 113162306a36Sopenharmony_ci .vidioc_g_pixelaspect = cobalt_g_pixelaspect, 113262306a36Sopenharmony_ci .vidioc_g_selection = cobalt_g_selection, 113362306a36Sopenharmony_ci .vidioc_enum_input = cobalt_enum_input, 113462306a36Sopenharmony_ci .vidioc_g_input = cobalt_g_input, 113562306a36Sopenharmony_ci .vidioc_s_input = cobalt_s_input, 113662306a36Sopenharmony_ci .vidioc_enum_fmt_vid_cap = cobalt_enum_fmt_vid_cap, 113762306a36Sopenharmony_ci .vidioc_g_fmt_vid_cap = cobalt_g_fmt_vid_cap, 113862306a36Sopenharmony_ci .vidioc_s_fmt_vid_cap = cobalt_s_fmt_vid_cap, 113962306a36Sopenharmony_ci .vidioc_try_fmt_vid_cap = cobalt_try_fmt_vid_cap, 114062306a36Sopenharmony_ci .vidioc_enum_output = cobalt_enum_output, 114162306a36Sopenharmony_ci .vidioc_g_output = cobalt_g_output, 114262306a36Sopenharmony_ci .vidioc_s_output = cobalt_s_output, 114362306a36Sopenharmony_ci .vidioc_enum_fmt_vid_out = cobalt_enum_fmt_vid_out, 114462306a36Sopenharmony_ci .vidioc_g_fmt_vid_out = cobalt_g_fmt_vid_out, 114562306a36Sopenharmony_ci .vidioc_s_fmt_vid_out = cobalt_s_fmt_vid_out, 114662306a36Sopenharmony_ci .vidioc_try_fmt_vid_out = cobalt_try_fmt_vid_out, 114762306a36Sopenharmony_ci .vidioc_s_dv_timings = cobalt_s_dv_timings, 114862306a36Sopenharmony_ci .vidioc_g_dv_timings = cobalt_g_dv_timings, 114962306a36Sopenharmony_ci .vidioc_query_dv_timings = cobalt_query_dv_timings, 115062306a36Sopenharmony_ci .vidioc_enum_dv_timings = cobalt_enum_dv_timings, 115162306a36Sopenharmony_ci .vidioc_dv_timings_cap = cobalt_dv_timings_cap, 115262306a36Sopenharmony_ci .vidioc_g_edid = cobalt_g_edid, 115362306a36Sopenharmony_ci .vidioc_s_edid = cobalt_s_edid, 115462306a36Sopenharmony_ci .vidioc_subscribe_event = cobalt_subscribe_event, 115562306a36Sopenharmony_ci .vidioc_unsubscribe_event = v4l2_event_unsubscribe, 115662306a36Sopenharmony_ci .vidioc_reqbufs = vb2_ioctl_reqbufs, 115762306a36Sopenharmony_ci .vidioc_create_bufs = vb2_ioctl_create_bufs, 115862306a36Sopenharmony_ci .vidioc_querybuf = vb2_ioctl_querybuf, 115962306a36Sopenharmony_ci .vidioc_qbuf = vb2_ioctl_qbuf, 116062306a36Sopenharmony_ci .vidioc_dqbuf = vb2_ioctl_dqbuf, 116162306a36Sopenharmony_ci .vidioc_expbuf = vb2_ioctl_expbuf, 116262306a36Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG 116362306a36Sopenharmony_ci .vidioc_g_register = cobalt_g_register, 116462306a36Sopenharmony_ci .vidioc_s_register = cobalt_s_register, 116562306a36Sopenharmony_ci#endif 116662306a36Sopenharmony_ci}; 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops cobalt_ioctl_empty_ops = { 116962306a36Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG 117062306a36Sopenharmony_ci .vidioc_g_register = cobalt_g_register, 117162306a36Sopenharmony_ci .vidioc_s_register = cobalt_s_register, 117262306a36Sopenharmony_ci#endif 117362306a36Sopenharmony_ci}; 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci/* Register device nodes */ 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_cistatic const struct v4l2_file_operations cobalt_fops = { 117862306a36Sopenharmony_ci .owner = THIS_MODULE, 117962306a36Sopenharmony_ci .open = v4l2_fh_open, 118062306a36Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 118162306a36Sopenharmony_ci .release = vb2_fop_release, 118262306a36Sopenharmony_ci .poll = vb2_fop_poll, 118362306a36Sopenharmony_ci .mmap = vb2_fop_mmap, 118462306a36Sopenharmony_ci .read = vb2_fop_read, 118562306a36Sopenharmony_ci}; 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_cistatic const struct v4l2_file_operations cobalt_out_fops = { 118862306a36Sopenharmony_ci .owner = THIS_MODULE, 118962306a36Sopenharmony_ci .open = v4l2_fh_open, 119062306a36Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 119162306a36Sopenharmony_ci .release = vb2_fop_release, 119262306a36Sopenharmony_ci .poll = vb2_fop_poll, 119362306a36Sopenharmony_ci .mmap = vb2_fop_mmap, 119462306a36Sopenharmony_ci .write = vb2_fop_write, 119562306a36Sopenharmony_ci}; 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_cistatic const struct v4l2_file_operations cobalt_empty_fops = { 119862306a36Sopenharmony_ci .owner = THIS_MODULE, 119962306a36Sopenharmony_ci .open = v4l2_fh_open, 120062306a36Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 120162306a36Sopenharmony_ci .release = v4l2_fh_release, 120262306a36Sopenharmony_ci}; 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_cistatic int cobalt_node_register(struct cobalt *cobalt, int node) 120562306a36Sopenharmony_ci{ 120662306a36Sopenharmony_ci static const struct v4l2_dv_timings dv1080p60 = 120762306a36Sopenharmony_ci V4L2_DV_BT_CEA_1920X1080P60; 120862306a36Sopenharmony_ci struct cobalt_stream *s = cobalt->streams + node; 120962306a36Sopenharmony_ci struct video_device *vdev = &s->vdev; 121062306a36Sopenharmony_ci struct vb2_queue *q = &s->q; 121162306a36Sopenharmony_ci int ret; 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci mutex_init(&s->lock); 121462306a36Sopenharmony_ci spin_lock_init(&s->irqlock); 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci snprintf(vdev->name, sizeof(vdev->name), 121762306a36Sopenharmony_ci "%s-%d", cobalt->v4l2_dev.name, node); 121862306a36Sopenharmony_ci s->width = 1920; 121962306a36Sopenharmony_ci /* Audio frames are just 4 lines of 1920 bytes */ 122062306a36Sopenharmony_ci s->height = s->is_audio ? 4 : 1080; 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci if (s->is_audio) { 122362306a36Sopenharmony_ci s->bpp = 1; 122462306a36Sopenharmony_ci s->pixfmt = V4L2_PIX_FMT_GREY; 122562306a36Sopenharmony_ci } else if (s->is_output) { 122662306a36Sopenharmony_ci s->bpp = COBALT_BYTES_PER_PIXEL_RGB32; 122762306a36Sopenharmony_ci s->pixfmt = V4L2_PIX_FMT_BGR32; 122862306a36Sopenharmony_ci } else { 122962306a36Sopenharmony_ci s->bpp = COBALT_BYTES_PER_PIXEL_YUYV; 123062306a36Sopenharmony_ci s->pixfmt = V4L2_PIX_FMT_YUYV; 123162306a36Sopenharmony_ci } 123262306a36Sopenharmony_ci s->colorspace = V4L2_COLORSPACE_SRGB; 123362306a36Sopenharmony_ci s->stride = s->width * s->bpp; 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci if (!s->is_audio) { 123662306a36Sopenharmony_ci if (s->is_dummy) 123762306a36Sopenharmony_ci cobalt_warn("Setting up dummy video node %d\n", node); 123862306a36Sopenharmony_ci vdev->v4l2_dev = &cobalt->v4l2_dev; 123962306a36Sopenharmony_ci if (s->is_dummy) 124062306a36Sopenharmony_ci vdev->fops = &cobalt_empty_fops; 124162306a36Sopenharmony_ci else 124262306a36Sopenharmony_ci vdev->fops = s->is_output ? &cobalt_out_fops : 124362306a36Sopenharmony_ci &cobalt_fops; 124462306a36Sopenharmony_ci vdev->release = video_device_release_empty; 124562306a36Sopenharmony_ci vdev->vfl_dir = s->is_output ? VFL_DIR_TX : VFL_DIR_RX; 124662306a36Sopenharmony_ci vdev->lock = &s->lock; 124762306a36Sopenharmony_ci if (s->sd) 124862306a36Sopenharmony_ci vdev->ctrl_handler = s->sd->ctrl_handler; 124962306a36Sopenharmony_ci s->timings = dv1080p60; 125062306a36Sopenharmony_ci v4l2_subdev_call(s->sd, video, s_dv_timings, &s->timings); 125162306a36Sopenharmony_ci if (!s->is_output && s->sd) 125262306a36Sopenharmony_ci cobalt_enable_input(s); 125362306a36Sopenharmony_ci vdev->ioctl_ops = s->is_dummy ? &cobalt_ioctl_empty_ops : 125462306a36Sopenharmony_ci &cobalt_ioctl_ops; 125562306a36Sopenharmony_ci } 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_ci INIT_LIST_HEAD(&s->bufs); 125862306a36Sopenharmony_ci q->type = s->is_output ? V4L2_BUF_TYPE_VIDEO_OUTPUT : 125962306a36Sopenharmony_ci V4L2_BUF_TYPE_VIDEO_CAPTURE; 126062306a36Sopenharmony_ci q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; 126162306a36Sopenharmony_ci q->io_modes |= s->is_output ? VB2_WRITE : VB2_READ; 126262306a36Sopenharmony_ci q->drv_priv = s; 126362306a36Sopenharmony_ci q->buf_struct_size = sizeof(struct cobalt_buffer); 126462306a36Sopenharmony_ci q->ops = &cobalt_qops; 126562306a36Sopenharmony_ci q->mem_ops = &vb2_dma_sg_memops; 126662306a36Sopenharmony_ci q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; 126762306a36Sopenharmony_ci q->min_buffers_needed = 2; 126862306a36Sopenharmony_ci q->lock = &s->lock; 126962306a36Sopenharmony_ci q->dev = &cobalt->pci_dev->dev; 127062306a36Sopenharmony_ci vdev->queue = q; 127162306a36Sopenharmony_ci vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; 127262306a36Sopenharmony_ci if (s->is_output) 127362306a36Sopenharmony_ci vdev->device_caps |= V4L2_CAP_VIDEO_OUTPUT; 127462306a36Sopenharmony_ci else 127562306a36Sopenharmony_ci vdev->device_caps |= V4L2_CAP_VIDEO_CAPTURE; 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_ci video_set_drvdata(vdev, s); 127862306a36Sopenharmony_ci ret = vb2_queue_init(q); 127962306a36Sopenharmony_ci if (!s->is_audio && ret == 0) 128062306a36Sopenharmony_ci ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); 128162306a36Sopenharmony_ci else if (!s->is_dummy) 128262306a36Sopenharmony_ci ret = cobalt_alsa_init(s); 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci if (ret < 0) { 128562306a36Sopenharmony_ci if (!s->is_audio) 128662306a36Sopenharmony_ci cobalt_err("couldn't register v4l2 device node %d\n", 128762306a36Sopenharmony_ci node); 128862306a36Sopenharmony_ci return ret; 128962306a36Sopenharmony_ci } 129062306a36Sopenharmony_ci cobalt_info("registered node %d\n", node); 129162306a36Sopenharmony_ci return 0; 129262306a36Sopenharmony_ci} 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci/* Initialize v4l2 variables and register v4l2 devices */ 129562306a36Sopenharmony_ciint cobalt_nodes_register(struct cobalt *cobalt) 129662306a36Sopenharmony_ci{ 129762306a36Sopenharmony_ci int node, ret; 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci /* Setup V4L2 Devices */ 130062306a36Sopenharmony_ci for (node = 0; node < COBALT_NUM_STREAMS; node++) { 130162306a36Sopenharmony_ci ret = cobalt_node_register(cobalt, node); 130262306a36Sopenharmony_ci if (ret) 130362306a36Sopenharmony_ci return ret; 130462306a36Sopenharmony_ci } 130562306a36Sopenharmony_ci return 0; 130662306a36Sopenharmony_ci} 130762306a36Sopenharmony_ci 130862306a36Sopenharmony_ci/* Unregister v4l2 devices */ 130962306a36Sopenharmony_civoid cobalt_nodes_unregister(struct cobalt *cobalt) 131062306a36Sopenharmony_ci{ 131162306a36Sopenharmony_ci int node; 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci /* Teardown all streams */ 131462306a36Sopenharmony_ci for (node = 0; node < COBALT_NUM_STREAMS; node++) { 131562306a36Sopenharmony_ci struct cobalt_stream *s = cobalt->streams + node; 131662306a36Sopenharmony_ci struct video_device *vdev = &s->vdev; 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_ci if (!s->is_audio) 131962306a36Sopenharmony_ci video_unregister_device(vdev); 132062306a36Sopenharmony_ci else if (!s->is_dummy) 132162306a36Sopenharmony_ci cobalt_alsa_exit(s); 132262306a36Sopenharmony_ci } 132362306a36Sopenharmony_ci} 1324