18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * vivid-sdr-cap.c - software defined radio 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/delay.h> 118c2ecf20Sopenharmony_ci#include <linux/kthread.h> 128c2ecf20Sopenharmony_ci#include <linux/freezer.h> 138c2ecf20Sopenharmony_ci#include <linux/math64.h> 148c2ecf20Sopenharmony_ci#include <linux/videodev2.h> 158c2ecf20Sopenharmony_ci#include <linux/v4l2-dv-timings.h> 168c2ecf20Sopenharmony_ci#include <media/v4l2-common.h> 178c2ecf20Sopenharmony_ci#include <media/v4l2-event.h> 188c2ecf20Sopenharmony_ci#include <media/v4l2-dv-timings.h> 198c2ecf20Sopenharmony_ci#include <linux/fixp-arith.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include "vivid-core.h" 228c2ecf20Sopenharmony_ci#include "vivid-ctrls.h" 238c2ecf20Sopenharmony_ci#include "vivid-sdr-cap.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* stream formats */ 268c2ecf20Sopenharmony_cistruct vivid_format { 278c2ecf20Sopenharmony_ci u32 pixelformat; 288c2ecf20Sopenharmony_ci u32 buffersize; 298c2ecf20Sopenharmony_ci}; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/* format descriptions for capture and preview */ 328c2ecf20Sopenharmony_cistatic const struct vivid_format formats[] = { 338c2ecf20Sopenharmony_ci { 348c2ecf20Sopenharmony_ci .pixelformat = V4L2_SDR_FMT_CU8, 358c2ecf20Sopenharmony_ci .buffersize = SDR_CAP_SAMPLES_PER_BUF * 2, 368c2ecf20Sopenharmony_ci }, { 378c2ecf20Sopenharmony_ci .pixelformat = V4L2_SDR_FMT_CS8, 388c2ecf20Sopenharmony_ci .buffersize = SDR_CAP_SAMPLES_PER_BUF * 2, 398c2ecf20Sopenharmony_ci }, 408c2ecf20Sopenharmony_ci}; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic const struct v4l2_frequency_band bands_adc[] = { 438c2ecf20Sopenharmony_ci { 448c2ecf20Sopenharmony_ci .tuner = 0, 458c2ecf20Sopenharmony_ci .type = V4L2_TUNER_ADC, 468c2ecf20Sopenharmony_ci .index = 0, 478c2ecf20Sopenharmony_ci .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS, 488c2ecf20Sopenharmony_ci .rangelow = 300000, 498c2ecf20Sopenharmony_ci .rangehigh = 300000, 508c2ecf20Sopenharmony_ci }, 518c2ecf20Sopenharmony_ci { 528c2ecf20Sopenharmony_ci .tuner = 0, 538c2ecf20Sopenharmony_ci .type = V4L2_TUNER_ADC, 548c2ecf20Sopenharmony_ci .index = 1, 558c2ecf20Sopenharmony_ci .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS, 568c2ecf20Sopenharmony_ci .rangelow = 900001, 578c2ecf20Sopenharmony_ci .rangehigh = 2800000, 588c2ecf20Sopenharmony_ci }, 598c2ecf20Sopenharmony_ci { 608c2ecf20Sopenharmony_ci .tuner = 0, 618c2ecf20Sopenharmony_ci .type = V4L2_TUNER_ADC, 628c2ecf20Sopenharmony_ci .index = 2, 638c2ecf20Sopenharmony_ci .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS, 648c2ecf20Sopenharmony_ci .rangelow = 3200000, 658c2ecf20Sopenharmony_ci .rangehigh = 3200000, 668c2ecf20Sopenharmony_ci }, 678c2ecf20Sopenharmony_ci}; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci/* ADC band midpoints */ 708c2ecf20Sopenharmony_ci#define BAND_ADC_0 ((bands_adc[0].rangehigh + bands_adc[1].rangelow) / 2) 718c2ecf20Sopenharmony_ci#define BAND_ADC_1 ((bands_adc[1].rangehigh + bands_adc[2].rangelow) / 2) 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic const struct v4l2_frequency_band bands_fm[] = { 748c2ecf20Sopenharmony_ci { 758c2ecf20Sopenharmony_ci .tuner = 1, 768c2ecf20Sopenharmony_ci .type = V4L2_TUNER_RF, 778c2ecf20Sopenharmony_ci .index = 0, 788c2ecf20Sopenharmony_ci .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS, 798c2ecf20Sopenharmony_ci .rangelow = 50000000, 808c2ecf20Sopenharmony_ci .rangehigh = 2000000000, 818c2ecf20Sopenharmony_ci }, 828c2ecf20Sopenharmony_ci}; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic void vivid_thread_sdr_cap_tick(struct vivid_dev *dev) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci struct vivid_buffer *sdr_cap_buf = NULL; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci dprintk(dev, 1, "SDR Capture Thread Tick\n"); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci /* Drop a certain percentage of buffers. */ 918c2ecf20Sopenharmony_ci if (dev->perc_dropped_buffers && 928c2ecf20Sopenharmony_ci prandom_u32_max(100) < dev->perc_dropped_buffers) 938c2ecf20Sopenharmony_ci return; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci spin_lock(&dev->slock); 968c2ecf20Sopenharmony_ci if (!list_empty(&dev->sdr_cap_active)) { 978c2ecf20Sopenharmony_ci sdr_cap_buf = list_entry(dev->sdr_cap_active.next, 988c2ecf20Sopenharmony_ci struct vivid_buffer, list); 998c2ecf20Sopenharmony_ci list_del(&sdr_cap_buf->list); 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci spin_unlock(&dev->slock); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci if (sdr_cap_buf) { 1048c2ecf20Sopenharmony_ci sdr_cap_buf->vb.sequence = dev->sdr_cap_seq_count; 1058c2ecf20Sopenharmony_ci v4l2_ctrl_request_setup(sdr_cap_buf->vb.vb2_buf.req_obj.req, 1068c2ecf20Sopenharmony_ci &dev->ctrl_hdl_sdr_cap); 1078c2ecf20Sopenharmony_ci v4l2_ctrl_request_complete(sdr_cap_buf->vb.vb2_buf.req_obj.req, 1088c2ecf20Sopenharmony_ci &dev->ctrl_hdl_sdr_cap); 1098c2ecf20Sopenharmony_ci vivid_sdr_cap_process(dev, sdr_cap_buf); 1108c2ecf20Sopenharmony_ci sdr_cap_buf->vb.vb2_buf.timestamp = 1118c2ecf20Sopenharmony_ci ktime_get_ns() + dev->time_wrap_offset; 1128c2ecf20Sopenharmony_ci vb2_buffer_done(&sdr_cap_buf->vb.vb2_buf, dev->dqbuf_error ? 1138c2ecf20Sopenharmony_ci VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); 1148c2ecf20Sopenharmony_ci dev->dqbuf_error = false; 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic int vivid_thread_sdr_cap(void *data) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci struct vivid_dev *dev = data; 1218c2ecf20Sopenharmony_ci u64 samples_since_start; 1228c2ecf20Sopenharmony_ci u64 buffers_since_start; 1238c2ecf20Sopenharmony_ci u64 next_jiffies_since_start; 1248c2ecf20Sopenharmony_ci unsigned long jiffies_since_start; 1258c2ecf20Sopenharmony_ci unsigned long cur_jiffies; 1268c2ecf20Sopenharmony_ci unsigned wait_jiffies; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci dprintk(dev, 1, "SDR Capture Thread Start\n"); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci set_freezable(); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci /* Resets frame counters */ 1338c2ecf20Sopenharmony_ci dev->sdr_cap_seq_offset = 0; 1348c2ecf20Sopenharmony_ci if (dev->seq_wrap) 1358c2ecf20Sopenharmony_ci dev->sdr_cap_seq_offset = 0xffffff80U; 1368c2ecf20Sopenharmony_ci dev->jiffies_sdr_cap = jiffies; 1378c2ecf20Sopenharmony_ci dev->sdr_cap_seq_resync = false; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci for (;;) { 1408c2ecf20Sopenharmony_ci try_to_freeze(); 1418c2ecf20Sopenharmony_ci if (kthread_should_stop()) 1428c2ecf20Sopenharmony_ci break; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci if (!mutex_trylock(&dev->mutex)) { 1458c2ecf20Sopenharmony_ci schedule_timeout_uninterruptible(1); 1468c2ecf20Sopenharmony_ci continue; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci cur_jiffies = jiffies; 1508c2ecf20Sopenharmony_ci if (dev->sdr_cap_seq_resync) { 1518c2ecf20Sopenharmony_ci dev->jiffies_sdr_cap = cur_jiffies; 1528c2ecf20Sopenharmony_ci dev->sdr_cap_seq_offset = dev->sdr_cap_seq_count + 1; 1538c2ecf20Sopenharmony_ci dev->sdr_cap_seq_count = 0; 1548c2ecf20Sopenharmony_ci dev->sdr_cap_seq_resync = false; 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci /* Calculate the number of jiffies since we started streaming */ 1578c2ecf20Sopenharmony_ci jiffies_since_start = cur_jiffies - dev->jiffies_sdr_cap; 1588c2ecf20Sopenharmony_ci /* Get the number of buffers streamed since the start */ 1598c2ecf20Sopenharmony_ci buffers_since_start = 1608c2ecf20Sopenharmony_ci (u64)jiffies_since_start * dev->sdr_adc_freq + 1618c2ecf20Sopenharmony_ci (HZ * SDR_CAP_SAMPLES_PER_BUF) / 2; 1628c2ecf20Sopenharmony_ci do_div(buffers_since_start, HZ * SDR_CAP_SAMPLES_PER_BUF); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci /* 1658c2ecf20Sopenharmony_ci * After more than 0xf0000000 (rounded down to a multiple of 1668c2ecf20Sopenharmony_ci * 'jiffies-per-day' to ease jiffies_to_msecs calculation) 1678c2ecf20Sopenharmony_ci * jiffies have passed since we started streaming reset the 1688c2ecf20Sopenharmony_ci * counters and keep track of the sequence offset. 1698c2ecf20Sopenharmony_ci */ 1708c2ecf20Sopenharmony_ci if (jiffies_since_start > JIFFIES_RESYNC) { 1718c2ecf20Sopenharmony_ci dev->jiffies_sdr_cap = cur_jiffies; 1728c2ecf20Sopenharmony_ci dev->sdr_cap_seq_offset = buffers_since_start; 1738c2ecf20Sopenharmony_ci buffers_since_start = 0; 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci dev->sdr_cap_seq_count = 1768c2ecf20Sopenharmony_ci buffers_since_start + dev->sdr_cap_seq_offset; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci vivid_thread_sdr_cap_tick(dev); 1798c2ecf20Sopenharmony_ci mutex_unlock(&dev->mutex); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci /* 1828c2ecf20Sopenharmony_ci * Calculate the number of samples streamed since we started, 1838c2ecf20Sopenharmony_ci * not including the current buffer. 1848c2ecf20Sopenharmony_ci */ 1858c2ecf20Sopenharmony_ci samples_since_start = buffers_since_start * SDR_CAP_SAMPLES_PER_BUF; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci /* And the number of jiffies since we started */ 1888c2ecf20Sopenharmony_ci jiffies_since_start = jiffies - dev->jiffies_sdr_cap; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci /* Increase by the number of samples in one buffer */ 1918c2ecf20Sopenharmony_ci samples_since_start += SDR_CAP_SAMPLES_PER_BUF; 1928c2ecf20Sopenharmony_ci /* 1938c2ecf20Sopenharmony_ci * Calculate when that next buffer is supposed to start 1948c2ecf20Sopenharmony_ci * in jiffies since we started streaming. 1958c2ecf20Sopenharmony_ci */ 1968c2ecf20Sopenharmony_ci next_jiffies_since_start = samples_since_start * HZ + 1978c2ecf20Sopenharmony_ci dev->sdr_adc_freq / 2; 1988c2ecf20Sopenharmony_ci do_div(next_jiffies_since_start, dev->sdr_adc_freq); 1998c2ecf20Sopenharmony_ci /* If it is in the past, then just schedule asap */ 2008c2ecf20Sopenharmony_ci if (next_jiffies_since_start < jiffies_since_start) 2018c2ecf20Sopenharmony_ci next_jiffies_since_start = jiffies_since_start; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci wait_jiffies = next_jiffies_since_start - jiffies_since_start; 2048c2ecf20Sopenharmony_ci schedule_timeout_interruptible(wait_jiffies ? wait_jiffies : 1); 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci dprintk(dev, 1, "SDR Capture Thread End\n"); 2078c2ecf20Sopenharmony_ci return 0; 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistatic int sdr_cap_queue_setup(struct vb2_queue *vq, 2118c2ecf20Sopenharmony_ci unsigned *nbuffers, unsigned *nplanes, 2128c2ecf20Sopenharmony_ci unsigned sizes[], struct device *alloc_devs[]) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci /* 2 = max 16-bit sample returned */ 2158c2ecf20Sopenharmony_ci sizes[0] = SDR_CAP_SAMPLES_PER_BUF * 2; 2168c2ecf20Sopenharmony_ci *nplanes = 1; 2178c2ecf20Sopenharmony_ci return 0; 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic int sdr_cap_buf_prepare(struct vb2_buffer *vb) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); 2238c2ecf20Sopenharmony_ci unsigned size = SDR_CAP_SAMPLES_PER_BUF * 2; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci dprintk(dev, 1, "%s\n", __func__); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci if (dev->buf_prepare_error) { 2288c2ecf20Sopenharmony_ci /* 2298c2ecf20Sopenharmony_ci * Error injection: test what happens if buf_prepare() returns 2308c2ecf20Sopenharmony_ci * an error. 2318c2ecf20Sopenharmony_ci */ 2328c2ecf20Sopenharmony_ci dev->buf_prepare_error = false; 2338c2ecf20Sopenharmony_ci return -EINVAL; 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci if (vb2_plane_size(vb, 0) < size) { 2368c2ecf20Sopenharmony_ci dprintk(dev, 1, "%s data will not fit into plane (%lu < %u)\n", 2378c2ecf20Sopenharmony_ci __func__, vb2_plane_size(vb, 0), size); 2388c2ecf20Sopenharmony_ci return -EINVAL; 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci vb2_set_plane_payload(vb, 0, size); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci return 0; 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cistatic void sdr_cap_buf_queue(struct vb2_buffer *vb) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 2488c2ecf20Sopenharmony_ci struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); 2498c2ecf20Sopenharmony_ci struct vivid_buffer *buf = container_of(vbuf, struct vivid_buffer, vb); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci dprintk(dev, 1, "%s\n", __func__); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci spin_lock(&dev->slock); 2548c2ecf20Sopenharmony_ci list_add_tail(&buf->list, &dev->sdr_cap_active); 2558c2ecf20Sopenharmony_ci spin_unlock(&dev->slock); 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_cistatic int sdr_cap_start_streaming(struct vb2_queue *vq, unsigned count) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci struct vivid_dev *dev = vb2_get_drv_priv(vq); 2618c2ecf20Sopenharmony_ci int err = 0; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci dprintk(dev, 1, "%s\n", __func__); 2648c2ecf20Sopenharmony_ci dev->sdr_cap_seq_count = 0; 2658c2ecf20Sopenharmony_ci if (dev->start_streaming_error) { 2668c2ecf20Sopenharmony_ci dev->start_streaming_error = false; 2678c2ecf20Sopenharmony_ci err = -EINVAL; 2688c2ecf20Sopenharmony_ci } else if (dev->kthread_sdr_cap == NULL) { 2698c2ecf20Sopenharmony_ci dev->kthread_sdr_cap = kthread_run(vivid_thread_sdr_cap, dev, 2708c2ecf20Sopenharmony_ci "%s-sdr-cap", dev->v4l2_dev.name); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci if (IS_ERR(dev->kthread_sdr_cap)) { 2738c2ecf20Sopenharmony_ci v4l2_err(&dev->v4l2_dev, "kernel_thread() failed\n"); 2748c2ecf20Sopenharmony_ci err = PTR_ERR(dev->kthread_sdr_cap); 2758c2ecf20Sopenharmony_ci dev->kthread_sdr_cap = NULL; 2768c2ecf20Sopenharmony_ci } 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci if (err) { 2798c2ecf20Sopenharmony_ci struct vivid_buffer *buf, *tmp; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci list_for_each_entry_safe(buf, tmp, &dev->sdr_cap_active, list) { 2828c2ecf20Sopenharmony_ci list_del(&buf->list); 2838c2ecf20Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, 2848c2ecf20Sopenharmony_ci VB2_BUF_STATE_QUEUED); 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci } 2878c2ecf20Sopenharmony_ci return err; 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci/* abort streaming and wait for last buffer */ 2918c2ecf20Sopenharmony_cistatic void sdr_cap_stop_streaming(struct vb2_queue *vq) 2928c2ecf20Sopenharmony_ci{ 2938c2ecf20Sopenharmony_ci struct vivid_dev *dev = vb2_get_drv_priv(vq); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci if (dev->kthread_sdr_cap == NULL) 2968c2ecf20Sopenharmony_ci return; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci while (!list_empty(&dev->sdr_cap_active)) { 2998c2ecf20Sopenharmony_ci struct vivid_buffer *buf; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci buf = list_entry(dev->sdr_cap_active.next, 3028c2ecf20Sopenharmony_ci struct vivid_buffer, list); 3038c2ecf20Sopenharmony_ci list_del(&buf->list); 3048c2ecf20Sopenharmony_ci v4l2_ctrl_request_complete(buf->vb.vb2_buf.req_obj.req, 3058c2ecf20Sopenharmony_ci &dev->ctrl_hdl_sdr_cap); 3068c2ecf20Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); 3078c2ecf20Sopenharmony_ci } 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci /* shutdown control thread */ 3108c2ecf20Sopenharmony_ci kthread_stop(dev->kthread_sdr_cap); 3118c2ecf20Sopenharmony_ci dev->kthread_sdr_cap = NULL; 3128c2ecf20Sopenharmony_ci} 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_cistatic void sdr_cap_buf_request_complete(struct vb2_buffer *vb) 3158c2ecf20Sopenharmony_ci{ 3168c2ecf20Sopenharmony_ci struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci v4l2_ctrl_request_complete(vb->req_obj.req, &dev->ctrl_hdl_sdr_cap); 3198c2ecf20Sopenharmony_ci} 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ciconst struct vb2_ops vivid_sdr_cap_qops = { 3228c2ecf20Sopenharmony_ci .queue_setup = sdr_cap_queue_setup, 3238c2ecf20Sopenharmony_ci .buf_prepare = sdr_cap_buf_prepare, 3248c2ecf20Sopenharmony_ci .buf_queue = sdr_cap_buf_queue, 3258c2ecf20Sopenharmony_ci .start_streaming = sdr_cap_start_streaming, 3268c2ecf20Sopenharmony_ci .stop_streaming = sdr_cap_stop_streaming, 3278c2ecf20Sopenharmony_ci .buf_request_complete = sdr_cap_buf_request_complete, 3288c2ecf20Sopenharmony_ci .wait_prepare = vb2_ops_wait_prepare, 3298c2ecf20Sopenharmony_ci .wait_finish = vb2_ops_wait_finish, 3308c2ecf20Sopenharmony_ci}; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ciint vivid_sdr_enum_freq_bands(struct file *file, void *fh, 3338c2ecf20Sopenharmony_ci struct v4l2_frequency_band *band) 3348c2ecf20Sopenharmony_ci{ 3358c2ecf20Sopenharmony_ci switch (band->tuner) { 3368c2ecf20Sopenharmony_ci case 0: 3378c2ecf20Sopenharmony_ci if (band->index >= ARRAY_SIZE(bands_adc)) 3388c2ecf20Sopenharmony_ci return -EINVAL; 3398c2ecf20Sopenharmony_ci *band = bands_adc[band->index]; 3408c2ecf20Sopenharmony_ci return 0; 3418c2ecf20Sopenharmony_ci case 1: 3428c2ecf20Sopenharmony_ci if (band->index >= ARRAY_SIZE(bands_fm)) 3438c2ecf20Sopenharmony_ci return -EINVAL; 3448c2ecf20Sopenharmony_ci *band = bands_fm[band->index]; 3458c2ecf20Sopenharmony_ci return 0; 3468c2ecf20Sopenharmony_ci default: 3478c2ecf20Sopenharmony_ci return -EINVAL; 3488c2ecf20Sopenharmony_ci } 3498c2ecf20Sopenharmony_ci} 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ciint vivid_sdr_g_frequency(struct file *file, void *fh, 3528c2ecf20Sopenharmony_ci struct v4l2_frequency *vf) 3538c2ecf20Sopenharmony_ci{ 3548c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci switch (vf->tuner) { 3578c2ecf20Sopenharmony_ci case 0: 3588c2ecf20Sopenharmony_ci vf->frequency = dev->sdr_adc_freq; 3598c2ecf20Sopenharmony_ci vf->type = V4L2_TUNER_ADC; 3608c2ecf20Sopenharmony_ci return 0; 3618c2ecf20Sopenharmony_ci case 1: 3628c2ecf20Sopenharmony_ci vf->frequency = dev->sdr_fm_freq; 3638c2ecf20Sopenharmony_ci vf->type = V4L2_TUNER_RF; 3648c2ecf20Sopenharmony_ci return 0; 3658c2ecf20Sopenharmony_ci default: 3668c2ecf20Sopenharmony_ci return -EINVAL; 3678c2ecf20Sopenharmony_ci } 3688c2ecf20Sopenharmony_ci} 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ciint vivid_sdr_s_frequency(struct file *file, void *fh, 3718c2ecf20Sopenharmony_ci const struct v4l2_frequency *vf) 3728c2ecf20Sopenharmony_ci{ 3738c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 3748c2ecf20Sopenharmony_ci unsigned freq = vf->frequency; 3758c2ecf20Sopenharmony_ci unsigned band; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci switch (vf->tuner) { 3788c2ecf20Sopenharmony_ci case 0: 3798c2ecf20Sopenharmony_ci if (vf->type != V4L2_TUNER_ADC) 3808c2ecf20Sopenharmony_ci return -EINVAL; 3818c2ecf20Sopenharmony_ci if (freq < BAND_ADC_0) 3828c2ecf20Sopenharmony_ci band = 0; 3838c2ecf20Sopenharmony_ci else if (freq < BAND_ADC_1) 3848c2ecf20Sopenharmony_ci band = 1; 3858c2ecf20Sopenharmony_ci else 3868c2ecf20Sopenharmony_ci band = 2; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci freq = clamp_t(unsigned, freq, 3898c2ecf20Sopenharmony_ci bands_adc[band].rangelow, 3908c2ecf20Sopenharmony_ci bands_adc[band].rangehigh); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci if (vb2_is_streaming(&dev->vb_sdr_cap_q) && 3938c2ecf20Sopenharmony_ci freq != dev->sdr_adc_freq) { 3948c2ecf20Sopenharmony_ci /* resync the thread's timings */ 3958c2ecf20Sopenharmony_ci dev->sdr_cap_seq_resync = true; 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci dev->sdr_adc_freq = freq; 3988c2ecf20Sopenharmony_ci return 0; 3998c2ecf20Sopenharmony_ci case 1: 4008c2ecf20Sopenharmony_ci if (vf->type != V4L2_TUNER_RF) 4018c2ecf20Sopenharmony_ci return -EINVAL; 4028c2ecf20Sopenharmony_ci dev->sdr_fm_freq = clamp_t(unsigned, freq, 4038c2ecf20Sopenharmony_ci bands_fm[0].rangelow, 4048c2ecf20Sopenharmony_ci bands_fm[0].rangehigh); 4058c2ecf20Sopenharmony_ci return 0; 4068c2ecf20Sopenharmony_ci default: 4078c2ecf20Sopenharmony_ci return -EINVAL; 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ci} 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ciint vivid_sdr_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt) 4128c2ecf20Sopenharmony_ci{ 4138c2ecf20Sopenharmony_ci switch (vt->index) { 4148c2ecf20Sopenharmony_ci case 0: 4158c2ecf20Sopenharmony_ci strscpy(vt->name, "ADC", sizeof(vt->name)); 4168c2ecf20Sopenharmony_ci vt->type = V4L2_TUNER_ADC; 4178c2ecf20Sopenharmony_ci vt->capability = 4188c2ecf20Sopenharmony_ci V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS; 4198c2ecf20Sopenharmony_ci vt->rangelow = bands_adc[0].rangelow; 4208c2ecf20Sopenharmony_ci vt->rangehigh = bands_adc[2].rangehigh; 4218c2ecf20Sopenharmony_ci return 0; 4228c2ecf20Sopenharmony_ci case 1: 4238c2ecf20Sopenharmony_ci strscpy(vt->name, "RF", sizeof(vt->name)); 4248c2ecf20Sopenharmony_ci vt->type = V4L2_TUNER_RF; 4258c2ecf20Sopenharmony_ci vt->capability = 4268c2ecf20Sopenharmony_ci V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS; 4278c2ecf20Sopenharmony_ci vt->rangelow = bands_fm[0].rangelow; 4288c2ecf20Sopenharmony_ci vt->rangehigh = bands_fm[0].rangehigh; 4298c2ecf20Sopenharmony_ci return 0; 4308c2ecf20Sopenharmony_ci default: 4318c2ecf20Sopenharmony_ci return -EINVAL; 4328c2ecf20Sopenharmony_ci } 4338c2ecf20Sopenharmony_ci} 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ciint vivid_sdr_s_tuner(struct file *file, void *fh, const struct v4l2_tuner *vt) 4368c2ecf20Sopenharmony_ci{ 4378c2ecf20Sopenharmony_ci if (vt->index > 1) 4388c2ecf20Sopenharmony_ci return -EINVAL; 4398c2ecf20Sopenharmony_ci return 0; 4408c2ecf20Sopenharmony_ci} 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ciint vidioc_enum_fmt_sdr_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f) 4438c2ecf20Sopenharmony_ci{ 4448c2ecf20Sopenharmony_ci if (f->index >= ARRAY_SIZE(formats)) 4458c2ecf20Sopenharmony_ci return -EINVAL; 4468c2ecf20Sopenharmony_ci f->pixelformat = formats[f->index].pixelformat; 4478c2ecf20Sopenharmony_ci return 0; 4488c2ecf20Sopenharmony_ci} 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ciint vidioc_g_fmt_sdr_cap(struct file *file, void *fh, struct v4l2_format *f) 4518c2ecf20Sopenharmony_ci{ 4528c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci f->fmt.sdr.pixelformat = dev->sdr_pixelformat; 4558c2ecf20Sopenharmony_ci f->fmt.sdr.buffersize = dev->sdr_buffersize; 4568c2ecf20Sopenharmony_ci memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); 4578c2ecf20Sopenharmony_ci return 0; 4588c2ecf20Sopenharmony_ci} 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ciint vidioc_s_fmt_sdr_cap(struct file *file, void *fh, struct v4l2_format *f) 4618c2ecf20Sopenharmony_ci{ 4628c2ecf20Sopenharmony_ci struct vivid_dev *dev = video_drvdata(file); 4638c2ecf20Sopenharmony_ci struct vb2_queue *q = &dev->vb_sdr_cap_q; 4648c2ecf20Sopenharmony_ci int i; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci if (vb2_is_busy(q)) 4678c2ecf20Sopenharmony_ci return -EBUSY; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); 4708c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(formats); i++) { 4718c2ecf20Sopenharmony_ci if (formats[i].pixelformat == f->fmt.sdr.pixelformat) { 4728c2ecf20Sopenharmony_ci dev->sdr_pixelformat = formats[i].pixelformat; 4738c2ecf20Sopenharmony_ci dev->sdr_buffersize = formats[i].buffersize; 4748c2ecf20Sopenharmony_ci f->fmt.sdr.buffersize = formats[i].buffersize; 4758c2ecf20Sopenharmony_ci return 0; 4768c2ecf20Sopenharmony_ci } 4778c2ecf20Sopenharmony_ci } 4788c2ecf20Sopenharmony_ci dev->sdr_pixelformat = formats[0].pixelformat; 4798c2ecf20Sopenharmony_ci dev->sdr_buffersize = formats[0].buffersize; 4808c2ecf20Sopenharmony_ci f->fmt.sdr.pixelformat = formats[0].pixelformat; 4818c2ecf20Sopenharmony_ci f->fmt.sdr.buffersize = formats[0].buffersize; 4828c2ecf20Sopenharmony_ci return 0; 4838c2ecf20Sopenharmony_ci} 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ciint vidioc_try_fmt_sdr_cap(struct file *file, void *fh, struct v4l2_format *f) 4868c2ecf20Sopenharmony_ci{ 4878c2ecf20Sopenharmony_ci int i; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); 4908c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(formats); i++) { 4918c2ecf20Sopenharmony_ci if (formats[i].pixelformat == f->fmt.sdr.pixelformat) { 4928c2ecf20Sopenharmony_ci f->fmt.sdr.buffersize = formats[i].buffersize; 4938c2ecf20Sopenharmony_ci return 0; 4948c2ecf20Sopenharmony_ci } 4958c2ecf20Sopenharmony_ci } 4968c2ecf20Sopenharmony_ci f->fmt.sdr.pixelformat = formats[0].pixelformat; 4978c2ecf20Sopenharmony_ci f->fmt.sdr.buffersize = formats[0].buffersize; 4988c2ecf20Sopenharmony_ci return 0; 4998c2ecf20Sopenharmony_ci} 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci#define FIXP_N (15) 5028c2ecf20Sopenharmony_ci#define FIXP_FRAC (1 << FIXP_N) 5038c2ecf20Sopenharmony_ci#define FIXP_2PI ((int)(2 * 3.141592653589 * FIXP_FRAC)) 5048c2ecf20Sopenharmony_ci#define M_100000PI (3.14159 * 100000) 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_civoid vivid_sdr_cap_process(struct vivid_dev *dev, struct vivid_buffer *buf) 5078c2ecf20Sopenharmony_ci{ 5088c2ecf20Sopenharmony_ci u8 *vbuf = vb2_plane_vaddr(&buf->vb.vb2_buf, 0); 5098c2ecf20Sopenharmony_ci unsigned long i; 5108c2ecf20Sopenharmony_ci unsigned long plane_size = vb2_plane_size(&buf->vb.vb2_buf, 0); 5118c2ecf20Sopenharmony_ci s64 s64tmp; 5128c2ecf20Sopenharmony_ci s32 src_phase_step; 5138c2ecf20Sopenharmony_ci s32 mod_phase_step; 5148c2ecf20Sopenharmony_ci s32 fixp_i; 5158c2ecf20Sopenharmony_ci s32 fixp_q; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci /* calculate phase step */ 5188c2ecf20Sopenharmony_ci #define BEEP_FREQ 1000 /* 1kHz beep */ 5198c2ecf20Sopenharmony_ci src_phase_step = DIV_ROUND_CLOSEST(FIXP_2PI * BEEP_FREQ, 5208c2ecf20Sopenharmony_ci dev->sdr_adc_freq); 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci for (i = 0; i < plane_size; i += 2) { 5238c2ecf20Sopenharmony_ci mod_phase_step = fixp_cos32_rad(dev->sdr_fixp_src_phase, 5248c2ecf20Sopenharmony_ci FIXP_2PI) >> (31 - FIXP_N); 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci dev->sdr_fixp_src_phase += src_phase_step; 5278c2ecf20Sopenharmony_ci s64tmp = (s64) mod_phase_step * dev->sdr_fm_deviation; 5288c2ecf20Sopenharmony_ci dev->sdr_fixp_mod_phase += div_s64(s64tmp, M_100000PI); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci /* 5318c2ecf20Sopenharmony_ci * Transfer phase angle to [0, 2xPI] in order to avoid variable 5328c2ecf20Sopenharmony_ci * overflow and make it suitable for cosine implementation 5338c2ecf20Sopenharmony_ci * used, which does not support negative angles. 5348c2ecf20Sopenharmony_ci */ 5358c2ecf20Sopenharmony_ci dev->sdr_fixp_src_phase %= FIXP_2PI; 5368c2ecf20Sopenharmony_ci dev->sdr_fixp_mod_phase %= FIXP_2PI; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci if (dev->sdr_fixp_mod_phase < 0) 5398c2ecf20Sopenharmony_ci dev->sdr_fixp_mod_phase += FIXP_2PI; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci fixp_i = fixp_cos32_rad(dev->sdr_fixp_mod_phase, FIXP_2PI); 5428c2ecf20Sopenharmony_ci fixp_q = fixp_sin32_rad(dev->sdr_fixp_mod_phase, FIXP_2PI); 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci /* Normalize fraction values represented with 32 bit precision 5458c2ecf20Sopenharmony_ci * to fixed point representation with FIXP_N bits */ 5468c2ecf20Sopenharmony_ci fixp_i >>= (31 - FIXP_N); 5478c2ecf20Sopenharmony_ci fixp_q >>= (31 - FIXP_N); 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci switch (dev->sdr_pixelformat) { 5508c2ecf20Sopenharmony_ci case V4L2_SDR_FMT_CU8: 5518c2ecf20Sopenharmony_ci /* convert 'fixp float' to u8 [0, +255] */ 5528c2ecf20Sopenharmony_ci /* u8 = X * 127.5 + 127.5; X is float [-1.0, +1.0] */ 5538c2ecf20Sopenharmony_ci fixp_i = fixp_i * 1275 + FIXP_FRAC * 1275; 5548c2ecf20Sopenharmony_ci fixp_q = fixp_q * 1275 + FIXP_FRAC * 1275; 5558c2ecf20Sopenharmony_ci *vbuf++ = DIV_ROUND_CLOSEST(fixp_i, FIXP_FRAC * 10); 5568c2ecf20Sopenharmony_ci *vbuf++ = DIV_ROUND_CLOSEST(fixp_q, FIXP_FRAC * 10); 5578c2ecf20Sopenharmony_ci break; 5588c2ecf20Sopenharmony_ci case V4L2_SDR_FMT_CS8: 5598c2ecf20Sopenharmony_ci /* convert 'fixp float' to s8 [-128, +127] */ 5608c2ecf20Sopenharmony_ci /* s8 = X * 127.5 - 0.5; X is float [-1.0, +1.0] */ 5618c2ecf20Sopenharmony_ci fixp_i = fixp_i * 1275 - FIXP_FRAC * 5; 5628c2ecf20Sopenharmony_ci fixp_q = fixp_q * 1275 - FIXP_FRAC * 5; 5638c2ecf20Sopenharmony_ci *vbuf++ = DIV_ROUND_CLOSEST(fixp_i, FIXP_FRAC * 10); 5648c2ecf20Sopenharmony_ci *vbuf++ = DIV_ROUND_CLOSEST(fixp_q, FIXP_FRAC * 10); 5658c2ecf20Sopenharmony_ci break; 5668c2ecf20Sopenharmony_ci default: 5678c2ecf20Sopenharmony_ci break; 5688c2ecf20Sopenharmony_ci } 5698c2ecf20Sopenharmony_ci } 5708c2ecf20Sopenharmony_ci} 571