18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * vivid-vbi-out.c - vbi output support functions. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/errno.h> 98c2ecf20Sopenharmony_ci#include <linux/kernel.h> 108c2ecf20Sopenharmony_ci#include <linux/videodev2.h> 118c2ecf20Sopenharmony_ci#include <media/v4l2-common.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include "vivid-core.h" 148c2ecf20Sopenharmony_ci#include "vivid-kthread-out.h" 158c2ecf20Sopenharmony_ci#include "vivid-vbi-out.h" 168c2ecf20Sopenharmony_ci#include "vivid-vbi-cap.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistatic int vbi_out_queue_setup(struct vb2_queue *vq, 198c2ecf20Sopenharmony_ci unsigned *nbuffers, unsigned *nplanes, 208c2ecf20Sopenharmony_ci unsigned sizes[], struct device *alloc_devs[]) 218c2ecf20Sopenharmony_ci{ 228c2ecf20Sopenharmony_ci struct vivid_dev *dev = vb2_get_drv_priv(vq); 238c2ecf20Sopenharmony_ci bool is_60hz = dev->std_out & V4L2_STD_525_60; 248c2ecf20Sopenharmony_ci unsigned size = vq->type == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT ? 258c2ecf20Sopenharmony_ci 36 * sizeof(struct v4l2_sliced_vbi_data) : 268c2ecf20Sopenharmony_ci 1440 * 2 * (is_60hz ? 12 : 18); 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci if (!vivid_is_svid_out(dev)) 298c2ecf20Sopenharmony_ci return -EINVAL; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci sizes[0] = size; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci if (vq->num_buffers + *nbuffers < 2) 348c2ecf20Sopenharmony_ci *nbuffers = 2 - vq->num_buffers; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci *nplanes = 1; 378c2ecf20Sopenharmony_ci return 0; 388c2ecf20Sopenharmony_ci} 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic int vbi_out_buf_prepare(struct vb2_buffer *vb) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); 438c2ecf20Sopenharmony_ci bool is_60hz = dev->std_out & V4L2_STD_525_60; 448c2ecf20Sopenharmony_ci unsigned size = vb->vb2_queue->type == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT ? 458c2ecf20Sopenharmony_ci 36 * sizeof(struct v4l2_sliced_vbi_data) : 468c2ecf20Sopenharmony_ci 1440 * 2 * (is_60hz ? 12 : 18); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci dprintk(dev, 1, "%s\n", __func__); 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci if (dev->buf_prepare_error) { 518c2ecf20Sopenharmony_ci /* 528c2ecf20Sopenharmony_ci * Error injection: test what happens if buf_prepare() returns 538c2ecf20Sopenharmony_ci * an error. 548c2ecf20Sopenharmony_ci */ 558c2ecf20Sopenharmony_ci dev->buf_prepare_error = false; 568c2ecf20Sopenharmony_ci return -EINVAL; 578c2ecf20Sopenharmony_ci } 588c2ecf20Sopenharmony_ci if (vb2_plane_size(vb, 0) < size) { 598c2ecf20Sopenharmony_ci dprintk(dev, 1, "%s data will not fit into plane (%lu < %u)\n", 608c2ecf20Sopenharmony_ci __func__, vb2_plane_size(vb, 0), size); 618c2ecf20Sopenharmony_ci return -EINVAL; 628c2ecf20Sopenharmony_ci } 638c2ecf20Sopenharmony_ci vb2_set_plane_payload(vb, 0, size); 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci return 0; 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic void vbi_out_buf_queue(struct vb2_buffer *vb) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 718c2ecf20Sopenharmony_ci struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); 728c2ecf20Sopenharmony_ci struct vivid_buffer *buf = container_of(vbuf, struct vivid_buffer, vb); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci dprintk(dev, 1, "%s\n", __func__); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci spin_lock(&dev->slock); 778c2ecf20Sopenharmony_ci list_add_tail(&buf->list, &dev->vbi_out_active); 788c2ecf20Sopenharmony_ci spin_unlock(&dev->slock); 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic int vbi_out_start_streaming(struct vb2_queue *vq, unsigned count) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci struct vivid_dev *dev = vb2_get_drv_priv(vq); 848c2ecf20Sopenharmony_ci int err; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci dprintk(dev, 1, "%s\n", __func__); 878c2ecf20Sopenharmony_ci dev->vbi_out_seq_count = 0; 888c2ecf20Sopenharmony_ci if (dev->start_streaming_error) { 898c2ecf20Sopenharmony_ci dev->start_streaming_error = false; 908c2ecf20Sopenharmony_ci err = -EINVAL; 918c2ecf20Sopenharmony_ci } else { 928c2ecf20Sopenharmony_ci err = vivid_start_generating_vid_out(dev, &dev->vbi_out_streaming); 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci if (err) { 958c2ecf20Sopenharmony_ci struct vivid_buffer *buf, *tmp; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci list_for_each_entry_safe(buf, tmp, &dev->vbi_out_active, list) { 988c2ecf20Sopenharmony_ci list_del(&buf->list); 998c2ecf20Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, 1008c2ecf20Sopenharmony_ci VB2_BUF_STATE_QUEUED); 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci return err; 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci/* abort streaming and wait for last buffer */ 1078c2ecf20Sopenharmony_cistatic void vbi_out_stop_streaming(struct vb2_queue *vq) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci struct vivid_dev *dev = vb2_get_drv_priv(vq); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci dprintk(dev, 1, "%s\n", __func__); 1128c2ecf20Sopenharmony_ci vivid_stop_generating_vid_out(dev, &dev->vbi_out_streaming); 1138c2ecf20Sopenharmony_ci dev->vbi_out_have_wss = false; 1148c2ecf20Sopenharmony_ci dev->vbi_out_have_cc[0] = false; 1158c2ecf20Sopenharmony_ci dev->vbi_out_have_cc[1] = false; 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic void vbi_out_buf_request_complete(struct vb2_buffer *vb) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci v4l2_ctrl_request_complete(vb->req_obj.req, &dev->ctrl_hdl_vbi_out); 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ciconst struct vb2_ops vivid_vbi_out_qops = { 1268c2ecf20Sopenharmony_ci .queue_setup = vbi_out_queue_setup, 1278c2ecf20Sopenharmony_ci .buf_prepare = vbi_out_buf_prepare, 1288c2ecf20Sopenharmony_ci .buf_queue = vbi_out_buf_queue, 1298c2ecf20Sopenharmony_ci .start_streaming = vbi_out_start_streaming, 1308c2ecf20Sopenharmony_ci .stop_streaming = vbi_out_stop_streaming, 1318c2ecf20Sopenharmony_ci .buf_request_complete = vbi_out_buf_request_complete, 1328c2ecf20Sopenharmony_ci .wait_prepare = vb2_ops_wait_prepare, 1338c2ecf20Sopenharmony_ci .wait_finish = vb2_ops_wait_finish, 1348c2ecf20Sopenharmony_ci}; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ciint vidioc_g_fmt_vbi_out(struct file *file, void *priv, 1378c2ecf20Sopenharmony_ci struct v4l2_format *f) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 1408c2ecf20Sopenharmony_ci struct v4l2_vbi_format *vbi = &f->fmt.vbi; 1418c2ecf20Sopenharmony_ci bool is_60hz = dev->std_out & V4L2_STD_525_60; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci if (!vivid_is_svid_out(dev) || !dev->has_raw_vbi_out) 1448c2ecf20Sopenharmony_ci return -EINVAL; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci vbi->sampling_rate = 25000000; 1478c2ecf20Sopenharmony_ci vbi->offset = 24; 1488c2ecf20Sopenharmony_ci vbi->samples_per_line = 1440; 1498c2ecf20Sopenharmony_ci vbi->sample_format = V4L2_PIX_FMT_GREY; 1508c2ecf20Sopenharmony_ci vbi->start[0] = is_60hz ? V4L2_VBI_ITU_525_F1_START + 9 : V4L2_VBI_ITU_625_F1_START + 5; 1518c2ecf20Sopenharmony_ci vbi->start[1] = is_60hz ? V4L2_VBI_ITU_525_F2_START + 9 : V4L2_VBI_ITU_625_F2_START + 5; 1528c2ecf20Sopenharmony_ci vbi->count[0] = vbi->count[1] = is_60hz ? 12 : 18; 1538c2ecf20Sopenharmony_ci vbi->flags = dev->vbi_cap_interlaced ? V4L2_VBI_INTERLACED : 0; 1548c2ecf20Sopenharmony_ci vbi->reserved[0] = 0; 1558c2ecf20Sopenharmony_ci vbi->reserved[1] = 0; 1568c2ecf20Sopenharmony_ci return 0; 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ciint vidioc_s_fmt_vbi_out(struct file *file, void *priv, 1608c2ecf20Sopenharmony_ci struct v4l2_format *f) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 1638c2ecf20Sopenharmony_ci int ret = vidioc_g_fmt_vbi_out(file, priv, f); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci if (ret) 1668c2ecf20Sopenharmony_ci return ret; 1678c2ecf20Sopenharmony_ci if (vb2_is_busy(&dev->vb_vbi_out_q)) 1688c2ecf20Sopenharmony_ci return -EBUSY; 1698c2ecf20Sopenharmony_ci dev->stream_sliced_vbi_out = false; 1708c2ecf20Sopenharmony_ci dev->vbi_out_dev.queue->type = V4L2_BUF_TYPE_VBI_OUTPUT; 1718c2ecf20Sopenharmony_ci return 0; 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ciint vidioc_g_fmt_sliced_vbi_out(struct file *file, void *fh, struct v4l2_format *fmt) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 1778c2ecf20Sopenharmony_ci struct v4l2_sliced_vbi_format *vbi = &fmt->fmt.sliced; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci if (!vivid_is_svid_out(dev) || !dev->has_sliced_vbi_out) 1808c2ecf20Sopenharmony_ci return -EINVAL; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci vivid_fill_service_lines(vbi, dev->service_set_out); 1838c2ecf20Sopenharmony_ci return 0; 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ciint vidioc_try_fmt_sliced_vbi_out(struct file *file, void *fh, struct v4l2_format *fmt) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 1898c2ecf20Sopenharmony_ci struct v4l2_sliced_vbi_format *vbi = &fmt->fmt.sliced; 1908c2ecf20Sopenharmony_ci bool is_60hz = dev->std_out & V4L2_STD_525_60; 1918c2ecf20Sopenharmony_ci u32 service_set = vbi->service_set; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci if (!vivid_is_svid_out(dev) || !dev->has_sliced_vbi_out) 1948c2ecf20Sopenharmony_ci return -EINVAL; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci service_set &= is_60hz ? V4L2_SLICED_CAPTION_525 : 1978c2ecf20Sopenharmony_ci V4L2_SLICED_WSS_625 | V4L2_SLICED_TELETEXT_B; 1988c2ecf20Sopenharmony_ci vivid_fill_service_lines(vbi, service_set); 1998c2ecf20Sopenharmony_ci return 0; 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ciint vidioc_s_fmt_sliced_vbi_out(struct file *file, void *fh, 2038c2ecf20Sopenharmony_ci struct v4l2_format *fmt) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 2068c2ecf20Sopenharmony_ci struct v4l2_sliced_vbi_format *vbi = &fmt->fmt.sliced; 2078c2ecf20Sopenharmony_ci int ret = vidioc_try_fmt_sliced_vbi_out(file, fh, fmt); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if (ret) 2108c2ecf20Sopenharmony_ci return ret; 2118c2ecf20Sopenharmony_ci if (vb2_is_busy(&dev->vb_vbi_out_q)) 2128c2ecf20Sopenharmony_ci return -EBUSY; 2138c2ecf20Sopenharmony_ci dev->service_set_out = vbi->service_set; 2148c2ecf20Sopenharmony_ci dev->stream_sliced_vbi_out = true; 2158c2ecf20Sopenharmony_ci dev->vbi_out_dev.queue->type = V4L2_BUF_TYPE_SLICED_VBI_OUTPUT; 2168c2ecf20Sopenharmony_ci return 0; 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_civoid vivid_sliced_vbi_out_process(struct vivid_dev *dev, 2208c2ecf20Sopenharmony_ci struct vivid_buffer *buf) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci struct v4l2_sliced_vbi_data *vbi = 2238c2ecf20Sopenharmony_ci vb2_plane_vaddr(&buf->vb.vb2_buf, 0); 2248c2ecf20Sopenharmony_ci unsigned elems = 2258c2ecf20Sopenharmony_ci vb2_get_plane_payload(&buf->vb.vb2_buf, 0) / sizeof(*vbi); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci dev->vbi_out_have_cc[0] = false; 2288c2ecf20Sopenharmony_ci dev->vbi_out_have_cc[1] = false; 2298c2ecf20Sopenharmony_ci dev->vbi_out_have_wss = false; 2308c2ecf20Sopenharmony_ci while (elems--) { 2318c2ecf20Sopenharmony_ci switch (vbi->id) { 2328c2ecf20Sopenharmony_ci case V4L2_SLICED_CAPTION_525: 2338c2ecf20Sopenharmony_ci if ((dev->std_out & V4L2_STD_525_60) && vbi->line == 21) { 2348c2ecf20Sopenharmony_ci dev->vbi_out_have_cc[!!vbi->field] = true; 2358c2ecf20Sopenharmony_ci dev->vbi_out_cc[!!vbi->field][0] = vbi->data[0]; 2368c2ecf20Sopenharmony_ci dev->vbi_out_cc[!!vbi->field][1] = vbi->data[1]; 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci break; 2398c2ecf20Sopenharmony_ci case V4L2_SLICED_WSS_625: 2408c2ecf20Sopenharmony_ci if ((dev->std_out & V4L2_STD_625_50) && 2418c2ecf20Sopenharmony_ci vbi->field == 0 && vbi->line == 23) { 2428c2ecf20Sopenharmony_ci dev->vbi_out_have_wss = true; 2438c2ecf20Sopenharmony_ci dev->vbi_out_wss[0] = vbi->data[0]; 2448c2ecf20Sopenharmony_ci dev->vbi_out_wss[1] = vbi->data[1]; 2458c2ecf20Sopenharmony_ci } 2468c2ecf20Sopenharmony_ci break; 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci vbi++; 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci} 251