358 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * vivid-kthread-out.h - video/vbi output thread support functions.4 *5 * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.6 */7 8#include <linux/module.h>9#include <linux/errno.h>10#include <linux/kernel.h>11#include <linux/init.h>12#include <linux/sched.h>13#include <linux/slab.h>14#include <linux/font.h>15#include <linux/mutex.h>16#include <linux/videodev2.h>17#include <linux/kthread.h>18#include <linux/freezer.h>19#include <linux/random.h>20#include <linux/v4l2-dv-timings.h>21#include <linux/jiffies.h>22#include <asm/div64.h>23#include <media/videobuf2-vmalloc.h>24#include <media/v4l2-dv-timings.h>25#include <media/v4l2-ioctl.h>26#include <media/v4l2-fh.h>27#include <media/v4l2-event.h>28 29#include "vivid-core.h"30#include "vivid-vid-common.h"31#include "vivid-vid-cap.h"32#include "vivid-vid-out.h"33#include "vivid-radio-common.h"34#include "vivid-radio-rx.h"35#include "vivid-radio-tx.h"36#include "vivid-sdr-cap.h"37#include "vivid-vbi-cap.h"38#include "vivid-vbi-out.h"39#include "vivid-osd.h"40#include "vivid-ctrls.h"41#include "vivid-kthread-out.h"42#include "vivid-meta-out.h"43 44static void vivid_thread_vid_out_tick(struct vivid_dev *dev)45{46 struct vivid_buffer *vid_out_buf = NULL;47 struct vivid_buffer *vbi_out_buf = NULL;48 struct vivid_buffer *meta_out_buf = NULL;49 50 dprintk(dev, 1, "Video Output Thread Tick\n");51 52 /* Drop a certain percentage of buffers. */53 if (dev->perc_dropped_buffers &&54 get_random_u32_below(100) < dev->perc_dropped_buffers)55 return;56 57 spin_lock(&dev->slock);58 /*59 * Only dequeue buffer if there is at least one more pending.60 * This makes video loopback possible.61 */62 if (!list_empty(&dev->vid_out_active) &&63 !list_is_singular(&dev->vid_out_active)) {64 vid_out_buf = list_entry(dev->vid_out_active.next,65 struct vivid_buffer, list);66 list_del(&vid_out_buf->list);67 }68 if (!list_empty(&dev->vbi_out_active) &&69 (dev->field_out != V4L2_FIELD_ALTERNATE ||70 (dev->vbi_out_seq_count & 1))) {71 vbi_out_buf = list_entry(dev->vbi_out_active.next,72 struct vivid_buffer, list);73 list_del(&vbi_out_buf->list);74 }75 if (!list_empty(&dev->meta_out_active)) {76 meta_out_buf = list_entry(dev->meta_out_active.next,77 struct vivid_buffer, list);78 list_del(&meta_out_buf->list);79 }80 spin_unlock(&dev->slock);81 82 if (!vid_out_buf && !vbi_out_buf && !meta_out_buf)83 return;84 85 if (vid_out_buf) {86 v4l2_ctrl_request_setup(vid_out_buf->vb.vb2_buf.req_obj.req,87 &dev->ctrl_hdl_vid_out);88 v4l2_ctrl_request_complete(vid_out_buf->vb.vb2_buf.req_obj.req,89 &dev->ctrl_hdl_vid_out);90 vid_out_buf->vb.sequence = dev->vid_out_seq_count;91 if (dev->field_out == V4L2_FIELD_ALTERNATE) {92 /*93 * The sequence counter counts frames, not fields.94 * So divide by two.95 */96 vid_out_buf->vb.sequence /= 2;97 }98 vid_out_buf->vb.vb2_buf.timestamp =99 ktime_get_ns() + dev->time_wrap_offset;100 vb2_buffer_done(&vid_out_buf->vb.vb2_buf, dev->dqbuf_error ?101 VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);102 dprintk(dev, 2, "vid_out buffer %d done\n",103 vid_out_buf->vb.vb2_buf.index);104 }105 106 if (vbi_out_buf) {107 v4l2_ctrl_request_setup(vbi_out_buf->vb.vb2_buf.req_obj.req,108 &dev->ctrl_hdl_vbi_out);109 v4l2_ctrl_request_complete(vbi_out_buf->vb.vb2_buf.req_obj.req,110 &dev->ctrl_hdl_vbi_out);111 if (dev->stream_sliced_vbi_out)112 vivid_sliced_vbi_out_process(dev, vbi_out_buf);113 114 vbi_out_buf->vb.sequence = dev->vbi_out_seq_count;115 vbi_out_buf->vb.vb2_buf.timestamp =116 ktime_get_ns() + dev->time_wrap_offset;117 vb2_buffer_done(&vbi_out_buf->vb.vb2_buf, dev->dqbuf_error ?118 VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);119 dprintk(dev, 2, "vbi_out buffer %d done\n",120 vbi_out_buf->vb.vb2_buf.index);121 }122 if (meta_out_buf) {123 v4l2_ctrl_request_setup(meta_out_buf->vb.vb2_buf.req_obj.req,124 &dev->ctrl_hdl_meta_out);125 v4l2_ctrl_request_complete(meta_out_buf->vb.vb2_buf.req_obj.req,126 &dev->ctrl_hdl_meta_out);127 vivid_meta_out_process(dev, meta_out_buf);128 meta_out_buf->vb.sequence = dev->meta_out_seq_count;129 meta_out_buf->vb.vb2_buf.timestamp =130 ktime_get_ns() + dev->time_wrap_offset;131 vb2_buffer_done(&meta_out_buf->vb.vb2_buf, dev->dqbuf_error ?132 VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);133 dprintk(dev, 2, "meta_out buffer %d done\n",134 meta_out_buf->vb.vb2_buf.index);135 }136 137 dev->dqbuf_error = false;138}139 140static int vivid_thread_vid_out(void *data)141{142 struct vivid_dev *dev = data;143 u64 numerators_since_start;144 u64 buffers_since_start;145 u64 next_jiffies_since_start;146 unsigned long jiffies_since_start;147 unsigned long cur_jiffies;148 unsigned wait_jiffies;149 unsigned numerator;150 unsigned denominator;151 152 dprintk(dev, 1, "Video Output Thread Start\n");153 154 set_freezable();155 156 /* Resets frame counters */157 dev->out_seq_offset = 0;158 dev->out_seq_count = 0;159 dev->jiffies_vid_out = jiffies;160 dev->out_seq_resync = false;161 if (dev->time_wrap)162 dev->time_wrap_offset = dev->time_wrap - ktime_get_ns();163 else164 dev->time_wrap_offset = 0;165 166 for (;;) {167 try_to_freeze();168 if (kthread_should_stop())169 break;170 171 if (!mutex_trylock(&dev->mutex)) {172 schedule();173 continue;174 }175 176 cur_jiffies = jiffies;177 if (dev->out_seq_resync) {178 dev->jiffies_vid_out = cur_jiffies;179 dev->out_seq_offset = dev->out_seq_count + 1;180 dev->out_seq_count = 0;181 dev->out_seq_resync = false;182 }183 numerator = dev->timeperframe_vid_out.numerator;184 denominator = dev->timeperframe_vid_out.denominator;185 186 if (dev->field_out == V4L2_FIELD_ALTERNATE)187 denominator *= 2;188 189 /* Calculate the number of jiffies since we started streaming */190 jiffies_since_start = cur_jiffies - dev->jiffies_vid_out;191 /* Get the number of buffers streamed since the start */192 buffers_since_start = (u64)jiffies_since_start * denominator +193 (HZ * numerator) / 2;194 do_div(buffers_since_start, HZ * numerator);195 196 /*197 * After more than 0xf0000000 (rounded down to a multiple of198 * 'jiffies-per-day' to ease jiffies_to_msecs calculation)199 * jiffies have passed since we started streaming reset the200 * counters and keep track of the sequence offset.201 */202 if (jiffies_since_start > JIFFIES_RESYNC) {203 dev->jiffies_vid_out = cur_jiffies;204 dev->out_seq_offset = buffers_since_start;205 buffers_since_start = 0;206 }207 dev->out_seq_count = buffers_since_start + dev->out_seq_offset;208 dev->vid_out_seq_count = dev->out_seq_count - dev->vid_out_seq_start;209 dev->vbi_out_seq_count = dev->out_seq_count - dev->vbi_out_seq_start;210 dev->meta_out_seq_count = dev->out_seq_count - dev->meta_out_seq_start;211 212 vivid_thread_vid_out_tick(dev);213 mutex_unlock(&dev->mutex);214 215 /*216 * Calculate the number of 'numerators' streamed since we started,217 * not including the current buffer.218 */219 numerators_since_start = buffers_since_start * numerator;220 221 /* And the number of jiffies since we started */222 jiffies_since_start = jiffies - dev->jiffies_vid_out;223 224 /* Increase by the 'numerator' of one buffer */225 numerators_since_start += numerator;226 /*227 * Calculate when that next buffer is supposed to start228 * in jiffies since we started streaming.229 */230 next_jiffies_since_start = numerators_since_start * HZ +231 denominator / 2;232 do_div(next_jiffies_since_start, denominator);233 /* If it is in the past, then just schedule asap */234 if (next_jiffies_since_start < jiffies_since_start)235 next_jiffies_since_start = jiffies_since_start;236 237 wait_jiffies = next_jiffies_since_start - jiffies_since_start;238 while (time_is_after_jiffies(cur_jiffies + wait_jiffies) &&239 !kthread_should_stop())240 schedule();241 }242 dprintk(dev, 1, "Video Output Thread End\n");243 return 0;244}245 246static void vivid_grab_controls(struct vivid_dev *dev, bool grab)247{248 v4l2_ctrl_grab(dev->ctrl_has_crop_out, grab);249 v4l2_ctrl_grab(dev->ctrl_has_compose_out, grab);250 v4l2_ctrl_grab(dev->ctrl_has_scaler_out, grab);251 v4l2_ctrl_grab(dev->ctrl_tx_mode, grab);252 v4l2_ctrl_grab(dev->ctrl_tx_rgb_range, grab);253}254 255int vivid_start_generating_vid_out(struct vivid_dev *dev, bool *pstreaming)256{257 dprintk(dev, 1, "%s\n", __func__);258 259 if (dev->kthread_vid_out) {260 u32 seq_count = dev->out_seq_count + dev->seq_wrap * 128;261 262 if (pstreaming == &dev->vid_out_streaming)263 dev->vid_out_seq_start = seq_count;264 else if (pstreaming == &dev->vbi_out_streaming)265 dev->vbi_out_seq_start = seq_count;266 else267 dev->meta_out_seq_start = seq_count;268 *pstreaming = true;269 return 0;270 }271 272 /* Resets frame counters */273 dev->jiffies_vid_out = jiffies;274 dev->vid_out_seq_start = dev->seq_wrap * 128;275 dev->vbi_out_seq_start = dev->seq_wrap * 128;276 dev->meta_out_seq_start = dev->seq_wrap * 128;277 278 dev->kthread_vid_out = kthread_run(vivid_thread_vid_out, dev,279 "%s-vid-out", dev->v4l2_dev.name);280 281 if (IS_ERR(dev->kthread_vid_out)) {282 int err = PTR_ERR(dev->kthread_vid_out);283 284 dev->kthread_vid_out = NULL;285 v4l2_err(&dev->v4l2_dev, "kernel_thread() failed\n");286 return err;287 }288 *pstreaming = true;289 vivid_grab_controls(dev, true);290 291 dprintk(dev, 1, "returning from %s\n", __func__);292 return 0;293}294 295void vivid_stop_generating_vid_out(struct vivid_dev *dev, bool *pstreaming)296{297 dprintk(dev, 1, "%s\n", __func__);298 299 if (dev->kthread_vid_out == NULL)300 return;301 302 *pstreaming = false;303 if (pstreaming == &dev->vid_out_streaming) {304 /* Release all active buffers */305 while (!list_empty(&dev->vid_out_active)) {306 struct vivid_buffer *buf;307 308 buf = list_entry(dev->vid_out_active.next,309 struct vivid_buffer, list);310 list_del(&buf->list);311 v4l2_ctrl_request_complete(buf->vb.vb2_buf.req_obj.req,312 &dev->ctrl_hdl_vid_out);313 vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);314 dprintk(dev, 2, "vid_out buffer %d done\n",315 buf->vb.vb2_buf.index);316 }317 }318 319 if (pstreaming == &dev->vbi_out_streaming) {320 while (!list_empty(&dev->vbi_out_active)) {321 struct vivid_buffer *buf;322 323 buf = list_entry(dev->vbi_out_active.next,324 struct vivid_buffer, list);325 list_del(&buf->list);326 v4l2_ctrl_request_complete(buf->vb.vb2_buf.req_obj.req,327 &dev->ctrl_hdl_vbi_out);328 vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);329 dprintk(dev, 2, "vbi_out buffer %d done\n",330 buf->vb.vb2_buf.index);331 }332 }333 334 if (pstreaming == &dev->meta_out_streaming) {335 while (!list_empty(&dev->meta_out_active)) {336 struct vivid_buffer *buf;337 338 buf = list_entry(dev->meta_out_active.next,339 struct vivid_buffer, list);340 list_del(&buf->list);341 v4l2_ctrl_request_complete(buf->vb.vb2_buf.req_obj.req,342 &dev->ctrl_hdl_meta_out);343 vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);344 dprintk(dev, 2, "meta_out buffer %d done\n",345 buf->vb.vb2_buf.index);346 }347 }348 349 if (dev->vid_out_streaming || dev->vbi_out_streaming ||350 dev->meta_out_streaming)351 return;352 353 /* shutdown control thread */354 vivid_grab_controls(dev, false);355 kthread_stop(dev->kthread_vid_out);356 dev->kthread_vid_out = NULL;357}358