// SPDX-License-Identifier: GPL-2.0-or-later /* * tw68 functions to handle video data * * Much of this code is derived from the cx88 and sa7134 drivers, which * were in turn derived from the bt87x driver. The original work was by * Gerd Knorr; more recently the code was enhanced by Mauro Carvalho Chehab, * Hans Verkuil, Andy Walls and many others. Their work is gratefully * acknowledged. Full credit goes to them - any problems within this code * are mine. * * Copyright (C) 2009 William M. Brack * * Refactored and updated to the latest v4l core frameworks: * * Copyright (C) 2014 Hans Verkuil <[email protected]> */ #include <linux/module.h> #include <media/v4l2-common.h> #include <media/v4l2-event.h> #include <media/videobuf2-dma-sg.h> #include "tw68.h" #include "tw68-reg.h" /* ------------------------------------------------------------------ */ /* data structs for video */ /* * FIXME - * Note that the saa7134 has formats, e.g. YUV420, which are classified * as "planar". These affect overlay mode, and are flagged with a field * ".planar" in the format. Do we need to implement this in this driver? */ static const struct tw68_format formats[] = …; #define FORMATS … #define NORM_625_50 … #define NORM_525_60 … /* * The following table is searched by tw68_s_std, first for a specific * match, then for an entry which contains the desired id. The table * entries should therefore be ordered in ascending order of specificity. */ static const struct tw68_tvnorm tvnorms[] = …; #define TVNORMS … static const struct tw68_format *format_by_fourcc(unsigned int fourcc) { … } /* ------------------------------------------------------------------ */ /* * Note that the cropping rectangles are described in terms of a single * frame, i.e. line positions are only 1/2 the interlaced equivalent */ static void set_tvnorm(struct tw68_dev *dev, const struct tw68_tvnorm *norm) { … } /* * tw68_set_scale * * Scaling and Cropping for video decoding * * We are working with 3 values for horizontal and vertical - scale, * delay and active. * * HACTIVE represent the actual number of pixels in the "usable" image, * before scaling. HDELAY represents the number of pixels skipped * between the start of the horizontal sync and the start of the image. * HSCALE is calculated using the formula * HSCALE = (HACTIVE / (#pixels desired)) * 256 * * The vertical registers are similar, except based upon the total number * of lines in the image, and the first line of the image (i.e. ignoring * vertical sync and VBI). * * Note that the number of bytes reaching the FIFO (and hence needing * to be processed by the DMAP program) is completely dependent upon * these values, especially HSCALE. * * Parameters: * @dev pointer to the device structure, needed for * getting current norm (as well as debug print) * @width actual image width (from user buffer) * @height actual image height * @field indicates Top, Bottom or Interlaced */ static int tw68_set_scale(struct tw68_dev *dev, unsigned int width, unsigned int height, enum v4l2_field field) { … } /* ------------------------------------------------------------------ */ int tw68_video_start_dma(struct tw68_dev *dev, struct tw68_buf *buf) { … } /* ------------------------------------------------------------------ */ /* calc max # of buffers from size (must not exceed the 4MB virtual * address space per DMA channel) */ static int tw68_buffer_count(unsigned int size, unsigned int count) { … } /* ------------------------------------------------------------- */ /* vb2 queue operations */ static int tw68_queue_setup(struct vb2_queue *q, unsigned int *num_buffers, unsigned int *num_planes, unsigned int sizes[], struct device *alloc_devs[]) { … } /* * The risc program for each buffers works as follows: it starts with a simple * 'JUMP to addr + 8', which is effectively a NOP. Then the program to DMA the * buffer follows and at the end we have a JUMP back to the start + 8 (skipping * the initial JUMP). * * This is the program of the first buffer to be queued if the active list is * empty and it just keeps DMAing this buffer without generating any interrupts. * * If a new buffer is added then the initial JUMP in the program generates an * interrupt as well which signals that the previous buffer has been DMAed * successfully and that it can be returned to userspace. * * It also sets the final jump of the previous buffer to the start of the new * buffer, thus chaining the new buffer into the DMA chain. This is a single * atomic u32 write, so there is no race condition. * * The end-result of all this that you only get an interrupt when a buffer * is ready, so the control flow is very easy. */ static void tw68_buf_queue(struct vb2_buffer *vb) { … } /* * buffer_prepare * * Set the ancillary information into the buffer structure. This * includes generating the necessary risc program if it hasn't already * been done for the current buffer format. * The structure fh contains the details of the format requested by the * user - type, width, height and #fields. This is compared with the * last format set for the current buffer. If they differ, the risc * code (which controls the filling of the buffer) is (re-)generated. */ static int tw68_buf_prepare(struct vb2_buffer *vb) { … } static void tw68_buf_finish(struct vb2_buffer *vb) { … } static int tw68_start_streaming(struct vb2_queue *q, unsigned int count) { … } static void tw68_stop_streaming(struct vb2_queue *q) { … } static const struct vb2_ops tw68_video_qops = …; /* ------------------------------------------------------------------ */ static int tw68_s_ctrl(struct v4l2_ctrl *ctrl) { … } /* ------------------------------------------------------------------ */ /* * Note that this routine returns what is stored in the fh structure, and * does not interrogate any of the device registers. */ static int tw68_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { … } static int tw68_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { … } /* * Note that tw68_s_fmt_vid_cap sets the information into the fh structure, * and it will be used for all future new buffers. However, there could be * some number of buffers on the "active" chain which will be filled before * the change takes place. */ static int tw68_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { … } static int tw68_enum_input(struct file *file, void *priv, struct v4l2_input *i) { … } static int tw68_g_input(struct file *file, void *priv, unsigned int *i) { … } static int tw68_s_input(struct file *file, void *priv, unsigned int i) { … } static int tw68_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { … } static int tw68_s_std(struct file *file, void *priv, v4l2_std_id id) { … } static int tw68_g_std(struct file *file, void *priv, v4l2_std_id *id) { … } static int tw68_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { … } /* * Used strictly for internal development and debugging, this routine * prints out the current register contents for the tw68xx device. */ static void tw68_dump_regs(struct tw68_dev *dev) { … } static int vidioc_log_status(struct file *file, void *priv) { … } #ifdef CONFIG_VIDEO_ADV_DEBUG static int vidioc_g_register(struct file *file, void *priv, struct v4l2_dbg_register *reg) { … } static int vidioc_s_register(struct file *file, void *priv, const struct v4l2_dbg_register *reg) { … } #endif static const struct v4l2_ctrl_ops tw68_ctrl_ops = …; static const struct v4l2_file_operations video_fops = …; static const struct v4l2_ioctl_ops video_ioctl_ops = …; static const struct video_device tw68_video_template = …; /* ------------------------------------------------------------------ */ /* exported stuff */ void tw68_set_tvnorm_hw(struct tw68_dev *dev) { … } int tw68_video_init1(struct tw68_dev *dev) { … } int tw68_video_init2(struct tw68_dev *dev, int video_nr) { … } /* * tw68_irq_video_done */ void tw68_irq_video_done(struct tw68_dev *dev, unsigned long status) { … }