diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2020-03-30 13:42:05 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2020-03-30 13:42:05 -0700 |
commit | 063d1942247668eb0bb800aef5afbbef337344be (patch) | |
tree | 3be8d6edaa586580d169da63c6c0c17ce1a86b25 /drivers/staging | |
parent | 47acac8cae28b36668bf89400c56b7fdebca3e75 (diff) | |
parent | 2632e7b618a7730969f9782593c29ca53553aa22 (diff) |
Merge tag 'media/v5.7-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media
Pull media updates from Mauro Carvalho Chehab:
- New sensor driver: imx219
- Support for some new pixelformats
- Support for Sun8i SoC
- Added more codecs to meson vdec driver
- Prepare for removing the legacy usbvision driver by moving it to
staging. This driver has issues and use legacy core APIs. If nobody
steps up to address those, it is time for its retirement.
- Several cleanups and improvements on drivers, with the addition of
new supported boards
* tag 'media/v5.7-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (236 commits)
media: venus: firmware: Ignore secure call error on first resume
media: mtk-vpu: load vpu firmware from the new location
media: i2c: video-i2c: fix build errors due to 'imply hwmon'
media: MAINTAINERS: add myself to co-maintain Hantro G1/G2 for i.MX8MQ
media: hantro: add initial i.MX8MQ support
media: dt-bindings: Document i.MX8MQ VPU bindings
media: vivid: fix incorrect PA assignment to HDMI outputs
media: hantro: Add linux-rockchip mailing list to MAINTAINERS
media: cedrus: h264: Fix 4K decoding on H6
media: siano: Use scnprintf() for avoiding potential buffer overflow
media: rc: Use scnprintf() for avoiding potential buffer overflow
media: allegro: create new struct for channel parameters
media: allegro: move mail definitions to separate file
media: allegro: pass buffers through firmware
media: allegro: verify source and destination buffer in VCU response
media: allegro: handle dependency of bitrate and bitrate_peak
media: allegro: read bitrate mode directly from control
media: allegro: make QP configurable
media: allegro: make frame rate configurable
media: allegro: skip filler data if possible
...
Diffstat (limited to 'drivers/staging')
69 files changed, 11520 insertions, 701 deletions
diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index c394abffea86..e59a846bc909 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -42,4 +42,8 @@ source "drivers/staging/media/phy-rockchip-dphy-rx0/Kconfig" source "drivers/staging/media/rkisp1/Kconfig" +if MEDIA_ANALOG_TV_SUPPORT +source "drivers/staging/media/usbvision/Kconfig" +endif + endif diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index ea9fce8014bb..23c682461b62 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -10,3 +10,4 @@ obj-$(CONFIG_VIDEO_IPU3_IMGU) += ipu3/ obj-$(CONFIG_SOC_CAMERA) += soc_camera/ obj-$(CONFIG_PHY_ROCKCHIP_DPHY_RX0) += phy-rockchip-dphy-rx0/ obj-$(CONFIG_VIDEO_ROCKCHIP_ISP1) += rkisp1/ +obj-$(CONFIG_VIDEO_USBVISION) += usbvision/ diff --git a/drivers/staging/media/allegro-dvt/Makefile b/drivers/staging/media/allegro-dvt/Makefile index 80817160815c..8e306dcdc55c 100644 --- a/drivers/staging/media/allegro-dvt/Makefile +++ b/drivers/staging/media/allegro-dvt/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 -allegro-objs := allegro-core.o nal-h264.o +allegro-objs := allegro-core.o nal-h264.o allegro-mail.o obj-$(CONFIG_VIDEO_ALLEGRO_DVT) += allegro.o diff --git a/drivers/staging/media/allegro-dvt/allegro-core.c b/drivers/staging/media/allegro-dvt/allegro-core.c index 3be41698df4c..34c3e55be902 100644 --- a/drivers/staging/media/allegro-dvt/allegro-core.c +++ b/drivers/staging/media/allegro-dvt/allegro-core.c @@ -5,7 +5,9 @@ * Allegro DVT video encoder driver */ +#include <linux/bits.h> #include <linux/firmware.h> +#include <linux/gcd.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/kernel.h> @@ -26,6 +28,7 @@ #include <media/videobuf2-dma-contig.h> #include <media/videobuf2-v4l2.h> +#include "allegro-mail.h" #include "nal-h264.h" /* @@ -40,6 +43,8 @@ #define ALLEGRO_HEIGHT_DEFAULT 1080 #define ALLEGRO_HEIGHT_MAX 2160 +#define ALLEGRO_FRAMERATE_DEFAULT ((struct v4l2_fract) { 30, 1 }) + #define ALLEGRO_GOP_SIZE_DEFAULT 25 #define ALLEGRO_GOP_SIZE_MAX 1000 @@ -176,6 +181,7 @@ struct allegro_channel { unsigned int width; unsigned int height; unsigned int stride; + struct v4l2_fract framerate; enum v4l2_colorspace colorspace; enum v4l2_ycbcr_encoding ycbcr_enc; @@ -192,7 +198,7 @@ struct allegro_channel { unsigned int sizeimage_encoded; unsigned int csequence; - enum v4l2_mpeg_video_bitrate_mode bitrate_mode; + bool frame_rc_enable; unsigned int bitrate; unsigned int bitrate_peak; unsigned int cpb_size; @@ -200,9 +206,17 @@ struct allegro_channel { struct v4l2_ctrl *mpeg_video_h264_profile; struct v4l2_ctrl *mpeg_video_h264_level; - struct v4l2_ctrl *mpeg_video_bitrate_mode; - struct v4l2_ctrl *mpeg_video_bitrate; - struct v4l2_ctrl *mpeg_video_bitrate_peak; + struct v4l2_ctrl *mpeg_video_h264_i_frame_qp; + struct v4l2_ctrl *mpeg_video_h264_max_qp; + struct v4l2_ctrl *mpeg_video_h264_min_qp; + struct v4l2_ctrl *mpeg_video_h264_p_frame_qp; + struct v4l2_ctrl *mpeg_video_h264_b_frame_qp; + struct v4l2_ctrl *mpeg_video_frame_rc_enable; + struct { /* video bitrate mode control cluster */ + struct v4l2_ctrl *mpeg_video_bitrate_mode; + struct v4l2_ctrl *mpeg_video_bitrate; + struct v4l2_ctrl *mpeg_video_bitrate_peak; + }; struct v4l2_ctrl *mpeg_video_cpb_size; struct v4l2_ctrl *mpeg_video_gop_size; @@ -215,6 +229,11 @@ struct allegro_channel { struct list_head buffers_reference; struct list_head buffers_intermediate; + struct list_head source_shadow_list; + struct list_head stream_shadow_list; + /* protect shadow lists of buffers passed to firmware */ + struct mutex shadow_list_lock; + struct list_head list; struct completion completion; @@ -236,6 +255,14 @@ allegro_get_state(struct allegro_channel *channel) return channel->state; } +struct allegro_m2m_buffer { + struct v4l2_m2m_buffer buf; + struct list_head head; +}; + +#define to_allegro_m2m_buffer(__buf) \ + container_of(__buf, struct allegro_m2m_buffer, buf) + struct fw_info { unsigned int id; unsigned int id_codec; @@ -258,276 +285,33 @@ static const struct fw_info supported_firmware[] = { }, }; -enum mcu_msg_type { - MCU_MSG_TYPE_INIT = 0x0000, - MCU_MSG_TYPE_CREATE_CHANNEL = 0x0005, - MCU_MSG_TYPE_DESTROY_CHANNEL = 0x0006, - MCU_MSG_TYPE_ENCODE_FRAME = 0x0007, - MCU_MSG_TYPE_PUT_STREAM_BUFFER = 0x0012, - MCU_MSG_TYPE_PUSH_BUFFER_INTERMEDIATE = 0x000e, - MCU_MSG_TYPE_PUSH_BUFFER_REFERENCE = 0x000f, -}; +static inline u32 to_mcu_addr(struct allegro_dev *dev, dma_addr_t phys) +{ + if (upper_32_bits(phys) || (lower_32_bits(phys) & MCU_CACHE_OFFSET)) + v4l2_warn(&dev->v4l2_dev, + "address %pad is outside mcu window\n", &phys); + + return lower_32_bits(phys) | MCU_CACHE_OFFSET; +} -static const char *msg_type_name(enum mcu_msg_type type) +static inline u32 to_mcu_size(struct allegro_dev *dev, size_t size) { - static char buf[9]; + return lower_32_bits(size); +} - switch (type) { - case MCU_MSG_TYPE_INIT: - return "INIT"; - case MCU_MSG_TYPE_CREATE_CHANNEL: - return "CREATE_CHANNEL"; - case MCU_MSG_TYPE_DESTROY_CHANNEL: - return "DESTROY_CHANNEL"; - case MCU_MSG_TYPE_ENCODE_FRAME: - return "ENCODE_FRAME"; - case MCU_MSG_TYPE_PUT_STREAM_BUFFER: - return "PUT_STREAM_BUFFER"; - case MCU_MSG_TYPE_PUSH_BUFFER_INTERMEDIATE: - return "PUSH_BUFFER_INTERMEDIATE"; - case MCU_MSG_TYPE_PUSH_BUFFER_REFERENCE: - return "PUSH_BUFFER_REFERENCE"; - default: - snprintf(buf, sizeof(buf), "(0x%04x)", type); - return buf; - } -} - -struct mcu_msg_header { - u16 length; /* length of the body in bytes */ - u16 type; -} __attribute__ ((__packed__)); - -struct mcu_msg_init_request { - struct mcu_msg_header header; - u32 reserved0; /* maybe a unused channel id */ - u32 suballoc_dma; - u32 suballoc_size; - s32 l2_cache[3]; -} __attribute__ ((__packed__)); - -struct mcu_msg_init_response { - struct mcu_msg_header header; - u32 reserved0; -} __attribute__ ((__packed__)); - -struct mcu_msg_create_channel { - struct mcu_msg_header header; - u32 user_id; - u16 width; - u16 height; - u32 format; - u32 colorspace; - u32 src_mode; - u8 profile; - u16 constraint_set_flags; - s8 codec; - u16 level; - u16 tier; - u32 sps_param; - u32 pps_param; - - u32 enc_option; -#define AL_OPT_WPP BIT(0) -#define AL_OPT_TILE BIT(1) -#define AL_OPT_LF BIT(2) -#define AL_OPT_LF_X_SLICE BIT(3) -#define AL_OPT_LF_X_TILE BIT(4) -#define AL_OPT_SCL_LST BIT(5) -#define AL_OPT_CONST_INTRA_PRED BIT(6) -#define AL_OPT_QP_TAB_RELATIVE BIT(7) -#define AL_OPT_FIX_PREDICTOR BIT(8) -#define AL_OPT_CUSTOM_LDA BIT(9) -#define AL_OPT_ENABLE_AUTO_QP BIT(10) -#define AL_OPT_ADAPT_AUTO_QP BIT(11) -#define AL_OPT_TRANSFO_SKIP BIT(13) -#define AL_OPT_FORCE_REC BIT(15) -#define AL_OPT_FORCE_MV_OUT BIT(16) -#define AL_OPT_FORCE_MV_CLIP BIT(17) -#define AL_OPT_LOWLAT_SYNC BIT(18) -#define AL_OPT_LOWLAT_INT BIT(19) -#define AL_OPT_RDO_COST_MODE BIT(20) - - s8 beta_offset; - s8 tc_offset; - u16 reserved10; - u32 unknown11; - u32 unknown12; - u16 num_slices; - u16 prefetch_auto; - u32 prefetch_mem_offset; - u32 prefetch_mem_size; - u16 clip_hrz_range; - u16 clip_vrt_range; - u16 me_range[4]; - u8 max_cu_size; - u8 min_cu_size; - u8 max_tu_size; - u8 min_tu_size; - u8 max_transfo_depth_inter; - u8 max_transfo_depth_intra; - u16 reserved20; - u32 entropy_mode; - u32 wp_mode; - - /* rate control param */ - u32 rate_control_mode; - u32 initial_rem_delay; - u32 cpb_size; - u16 framerate; - u16 clk_ratio; - u32 target_bitrate; - u32 max_bitrate; - u16 initial_qp; - u16 min_qp; - u16 max_qp; - s16 ip_delta; - s16 pb_delta; - u16 golden_ref; - u16 golden_delta; - u16 golden_ref_frequency; - u32 rate_control_option; - - /* gop param */ - u32 gop_ctrl_mode; - u32 freq_ird; - u32 freq_lt; - u32 gdr_mode; - u32 gop_length; - u32 unknown39; - - u32 subframe_latency; - u32 lda_control_mode; -} __attribute__ ((__packed__)); - -struct mcu_msg_create_channel_response { - struct mcu_msg_header header; - u32 channel_id; - u32 user_id; - u32 options; - u32 num_core; - u32 pps_param; - u32 int_buffers_count; - u32 int_buffers_size; - u32 rec_buffers_count; - u32 rec_buffers_size; - u32 reserved; - u32 error_code; -} __attribute__ ((__packed__)); - -struct mcu_msg_destroy_channel { - struct mcu_msg_header header; - u32 channel_id; -} __attribute__ ((__packed__)); - -struct mcu_msg_destroy_channel_response { - struct mcu_msg_header header; - u32 channel_id; -} __attribute__ ((__packed__)); - -struct mcu_msg_push_buffers_internal_buffer { - u32 dma_addr; - u32 mcu_addr; - u32 size; -} __attribute__ ((__packed__)); - -struct mcu_msg_push_buffers_internal { - struct mcu_msg_header header; - u32 channel_id; - struct mcu_msg_push_buffers_internal_buffer buffer[0]; -} __attribute__ ((__packed__)); - -struct mcu_msg_put_stream_buffer { - struct mcu_msg_header header; - u32 channel_id; - u32 dma_addr; - u32 mcu_addr; - u32 size; - u32 offset; - u64 stream_id; -} __attribute__ ((__packed__)); - -struct mcu_msg_encode_frame { - struct mcu_msg_header header; - u32 channel_id; - u32 reserved; - - u32 encoding_options; -#define AL_OPT_USE_QP_TABLE BIT(0) -#define AL_OPT_FORCE_LOAD BIT(1) -#define AL_OPT_USE_L2 BIT(2) -#define AL_OPT_DISABLE_INTRA BIT(3) -#define AL_OPT_DEPENDENT_SLICES BIT(4) - - s16 pps_qp; - u16 padding; - u64 user_param; - u64 src_handle; +static inline u32 to_codec_addr(struct allegro_dev *dev, dma_addr_t phys) +{ + if (upper_32_bits(phys)) + v4l2_warn(&dev->v4l2_dev, + "address %pad cannot be used by codec\n", &phys); - u32 request_options; -#define AL_OPT_SCENE_CHANGE BIT(0) -#define AL_OPT_RESTART_GOP BIT(1) -#define AL_OPT_USE_LONG_TERM BIT(2) -#define AL_OPT_UPDATE_PARAMS BIT(3) - - /* u32 scene_change_delay (optional) */ - /* rate control param (optional) */ - /* gop param (optional) */ - u32 src_y; - u32 src_uv; - u32 stride; - u32 ep2; - u64 ep2_v; -} __attribute__ ((__packed__)); - -struct mcu_msg_encode_frame_response { - struct mcu_msg_header header; - u32 channel_id; - u64 stream_id; /* see mcu_msg_put_stream_buffer */ - u64 user_param; /* see mcu_msg_encode_frame */ - u64 src_handle; /* see mcu_msg_encode_frame */ - u16 skip; - u16 is_ref; - u32 initial_removal_delay; - u32 dpb_output_delay; - u32 size; - u32 frame_tag_size; - s32 stuffing; - s32 filler; - u16 num_column; - u16 num_row; - u16 qp; - u8 num_ref_idx_l0; - u8 num_ref_idx_l1; - u32 partition_table_offset; - s32 partition_table_size; - u32 sum_complex; - s32 tile_width[4]; - s32 tile_height[22]; - u32 error_code; - - u32 slice_type; -#define AL_ENC_SLICE_TYPE_B 0 -#define AL_ENC_SLICE_TYPE_P 1 -#define AL_ENC_SLICE_TYPE_I 2 - - u32 pic_struct; - u8 is_idr; - u8 is_first_slice; - u8 is_last_slice; - u8 reserved; - u16 pps_qp; - u16 reserved1; - u32 reserved2; -} __attribute__ ((__packed__)); - -union mcu_msg_response { - struct mcu_msg_header header; - struct mcu_msg_init_response init; - struct mcu_msg_create_channel_response create_channel; - struct mcu_msg_destroy_channel_response destroy_channel; - struct mcu_msg_encode_frame_response encode_frame; -}; + return lower_32_bits(phys); +} + +static inline u64 ptr_to_u64(const void *ptr) +{ + return (uintptr_t)ptr; +} /* Helper functions for channel and user operations */ @@ -572,6 +356,56 @@ static inline bool channel_exists(struct allegro_channel *channel) return channel->mcu_channel_id != -1; } +#define AL_ERROR 0x80 +#define AL_ERR_INIT_FAILED 0x81 +#define AL_ERR_NO_FRAME_DECODED 0x82 +#define AL_ERR_RESOLUTION_CHANGE 0x85 +#define AL_ERR_NO_MEMORY 0x87 +#define AL_ERR_STREAM_OVERFLOW 0x88 +#define AL_ERR_TOO_MANY_SLICES 0x89 +#define AL_ERR_BUF_NOT_READY 0x8c +#define AL_ERR_NO_CHANNEL_AVAILABLE 0x8d +#define AL_ERR_RESOURCE_UNAVAILABLE 0x8e +#define AL_ERR_NOT_ENOUGH_CORES 0x8f +#define AL_ERR_REQUEST_MALFORMED 0x90 +#define AL_ERR_CMD_NOT_ALLOWED 0x91 +#define AL_ERR_INVALID_CMD_VALUE 0x92 + +static inline const char *allegro_err_to_string(unsigned int err) +{ + switch (err) { + case AL_ERR_INIT_FAILED: + return "initialization failed"; + case AL_ERR_NO_FRAME_DECODED: + return "no frame decoded"; + case AL_ERR_RESOLUTION_CHANGE: + return "resolution change"; + case AL_ERR_NO_MEMORY: + return "out of memory"; + case AL_ERR_STREAM_OVERFLOW: + return "stream buffer overflow"; + case AL_ERR_TOO_MANY_SLICES: + return "too many slices"; + case AL_ERR_BUF_NOT_READY: + return "buffer not ready"; + case AL_ERR_NO_CHANNEL_AVAILABLE: + return "no channel available"; + case AL_ERR_RESOURCE_UNAVAILABLE: + return "resource unavailable"; + case AL_ERR_NOT_ENOUGH_CORES: + return "not enough cores"; + case AL_ERR_REQUEST_MALFORMED: + return "request malformed"; + case AL_ERR_CMD_NOT_ALLOWED: + return "command not allowed"; + case AL_ERR_INVALID_CMD_VALUE: + return "invalid command value"; + case AL_ERROR: + default: + return "unknown error"; + } +} + static unsigned int estimate_stream_size(unsigned int width, unsigned int height) { @@ -781,7 +615,7 @@ static int allegro_mbox_write(struct allegro_dev *dev, if (size > mbox->size) { v4l2_err(&dev->v4l2_dev, - "message (%zu bytes) to large for mailbox (%zu bytes)\n", + "message (%zu bytes) too large for mailbox (%zu bytes)\n", size, mbox->size); return -EINVAL; } @@ -894,8 +728,8 @@ static void allegro_mcu_send_init(struct allegro_dev *dev, msg.header.type = MCU_MSG_TYPE_INIT; msg.header.length = sizeof(msg) - sizeof(msg.header); - msg.suballoc_dma = lower_32_bits(suballoc_dma) | MCU_CACHE_OFFSET; - msg.suballoc_size = suballoc_size; + msg.suballoc_dma = to_mcu_addr(dev, suballoc_dma); + msg.suballoc_size = to_mcu_size(dev, suballoc_size); /* disable L2 cache */ msg.l2_cache[0] = -1; @@ -1001,6 +835,103 @@ v4l2_bitrate_mode_to_mcu_mode(enum v4l2_mpeg_video_bitrate_mode mode) } } +static u32 v4l2_cpb_size_to_mcu(unsigned int cpb_size, unsigned int bitrate) +{ + unsigned int cpb_size_kbit; + unsigned int bitrate_kbps; + + /* + * The mcu expects the CPB size in units of a 90 kHz clock, but the + * channel follows the V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE and stores + * the CPB size in kilobytes. + */ + cpb_size_kbit = cpb_size * BITS_PER_BYTE; + bitrate_kbps = bitrate / 1000; + + return (cpb_size_kbit * 90000) / bitrate_kbps; +} + +static s16 get_qp_delta(int minuend, int subtrahend) +{ + if (minuend == subtrahend) + return -1; + else + return minuend - subtrahend; +} + +static int fill_create_channel_param(struct allegro_channel *channel, + struct create_channel_param *param) +{ + int i_frame_qp = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_i_frame_qp); + int p_frame_qp = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_p_frame_qp); + int b_frame_qp = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_b_frame_qp); + int bitrate_mode = v4l2_ctrl_g_ctrl(channel->mpeg_video_bitrate_mode); + + param->width = channel->width; + param->height = channel->height; + param->format = v4l2_pixelformat_to_mcu_format(channel->pixelformat); + param->colorspace = + v4l2_colorspace_to_mcu_colorspace(channel->colorspace); + param->src_mode = 0x0; + param->profile = v4l2_profile_to_mcu_profile(channel->profile); + param->constraint_set_flags = BIT(1); + param->codec = v4l2_pixelformat_to_mcu_codec(channel->codec); + param->level = v4l2_level_to_mcu_level(channel->level); + param->tier = 0; + param->sps_param = BIT(20) | 0x4a; + param->pps_param = BIT(2); + param->enc_option = AL_OPT_RDO_COST_MODE | AL_OPT_LF_X_TILE | + AL_OPT_LF_X_SLICE | AL_OPT_LF; + param->beta_offset = -1; + param->tc_offset = -1; + param->num_slices = 1; + param->me_range[0] = 8; + param->me_range[1] = 8; + param->me_range[2] = 16; + param->me_range[3] = 16; + param->max_cu_size = ilog2(SIZE_MACROBLOCK); + param->min_cu_size = ilog2(8); + param->max_tu_size = 2; + param->min_tu_size = 2; + param->max_transfo_depth_intra = 1; + param->max_transfo_depth_inter = 1; + + param->prefetch_auto = 0; + param->prefetch_mem_offset = 0; + param->prefetch_mem_size = 0; + + param->rate_control_mode = channel->frame_rc_enable ? + v4l2_bitrate_mode_to_mcu_mode(bitrate_mode) : 0; + + param->cpb_size = v4l2_cpb_size_to_mcu(channel->cpb_size, + channel->bitrate_peak); + /* Shall be ]0;cpb_size in 90 kHz units]. Use maximum value. */ + param->initial_rem_delay = param->cpb_size; + param->framerate = DIV_ROUND_UP(channel->framerate.numerator, + channel->framerate.denominator); + param->clk_ratio = channel->framerate.denominator == 1001 ? 1001 : 1000; + param->target_bitrate = channel->bitrate; + param->max_bitrate = channel->bitrate_peak; + param->initial_qp = i_frame_qp; + param->min_qp = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_min_qp); + param->max_qp = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_max_qp); + param->ip_delta = get_qp_delta(i_frame_qp, p_frame_qp); + param->pb_delta = get_qp_delta(p_frame_qp, b_frame_qp); + param->golden_ref = 0; + param->golden_delta = 2; + param->golden_ref_frequency = 10; + param->rate_control_option = 0x00000000; + + param->gop_ctrl_mode = 0x00000000; + param->freq_idr = channel->gop_size; + param->freq_lt = 0; + param->gdr_mode = 0x00000000; + param->gop_length = channel->gop_size; + param->subframe_latency = 0x00000000; + + return 0; +} + static int allegro_mcu_send_create_channel(struct allegro_dev *dev, struct allegro_channel *channel) { @@ -1012,63 +943,8 @@ static int allegro_mcu_send_create_channel(struct allegro_dev *dev, msg.header.length = sizeof(msg) - sizeof(msg.header); msg.user_id = channel->user_id; - msg.width = channel->width; - msg.height = channel->height; - msg.format = v4l2_pixelformat_to_mcu_format(channel->pixelformat); - msg.colorspace = v4l2_colorspace_to_mcu_colorspace(channel->colorspace); - msg.src_mode = 0x0; - msg.profile = v4l2_profile_to_mcu_profile(channel->profile); - msg.constraint_set_flags = BIT(1); - msg.codec = v4l2_pixelformat_to_mcu_codec(channel->codec); - msg.level = v4l2_level_to_mcu_level(channel->level); - msg.tier = 0; - msg.sps_param = BIT(20) | 0x4a; - msg.pps_param = BIT(2); - msg.enc_option = AL_OPT_RDO_COST_MODE | AL_OPT_LF_X_TILE | - AL_OPT_LF_X_SLICE | AL_OPT_LF; - msg.beta_offset = -1; - msg.tc_offset = -1; - msg.num_slices = 1; - msg.me_range[0] = 8; - msg.me_range[1] = 8; - msg.me_range[2] = 16; - msg.me_range[3] = 16; - msg.max_cu_size = ilog2(SIZE_MACROBLOCK); - msg.min_cu_size = ilog2(8); - msg.max_tu_size = 2; - msg.min_tu_size = 2; - msg.max_transfo_depth_intra = 1; - msg.max_transfo_depth_inter = 1; - - msg.rate_control_mode = - v4l2_bitrate_mode_to_mcu_mode(channel->bitrate_mode); - /* Shall be ]0;cpb_size in 90 kHz units]. Use maximum value. */ - msg.initial_rem_delay = - ((channel->cpb_size * 1000) / channel->bitrate_peak) * 90000; - /* Encoder expects cpb_size in units of a 90 kHz clock. */ - msg.cpb_size = - ((channel->cpb_size * 1000) / channel->bitrate_peak) * 90000; - msg.framerate = 25; - msg.clk_ratio = 1000; - msg.target_bitrate = channel->bitrate; - msg.max_bitrate = channel->bitrate_peak; - msg.initial_qp = 25; - msg.min_qp = 10; - msg.max_qp = 51; - msg.ip_delta = -1; - msg.pb_delta = -1; - msg.golden_ref = 0; - msg.golden_delta = 2; - msg.golden_ref_frequency = 10; - msg.rate_control_option = 0x00000000; - - msg.gop_ctrl_mode = 0x00000000; - msg.freq_ird = 0x7fffffff; - msg.freq_lt = 0; - msg.gdr_mode = 0x00000000; - msg.gop_length = channel->gop_size; - msg.subframe_latency = 0x00000000; - msg.lda_control_mode = 0x700d0000; + + fill_create_channel_param(channel, &msg.param); allegro_mbox_write(dev, &dev->mbox_command, &msg, sizeof(msg)); allegro_mcu_interrupt(dev); @@ -1097,7 +973,8 @@ static int allegro_mcu_send_destroy_channel(struct allegro_dev *dev, static int allegro_mcu_send_put_stream_buffer(struct allegro_dev *dev, struct allegro_channel *channel, dma_addr_t paddr, - unsigned long size) + unsigned long size, + u64 stream_id) { struct mcu_msg_put_stream_buffer msg; @@ -1107,11 +984,12 @@ static int allegro_mcu_send_put_stream_buffer(struct allegro_dev *dev, msg.header.length = sizeof(msg) - sizeof(msg.header); msg.channel_id = channel->mcu_channel_id; - msg.dma_addr = paddr; - msg.mcu_addr = paddr | MCU_CACHE_OFFSET; + msg.dma_addr = to_codec_addr(dev, paddr); + msg.mcu_addr = to_mcu_addr(dev, paddr); msg.size = size; msg.offset = ENCODER_STREAM_OFFSET; - msg.stream_id = 0; /* copied to mcu_msg_encode_frame_response */ + /* copied to mcu_msg_encode_frame_response */ + msg.stream_id = stream_id; allegro_mbox_write(dev, &dev->mbox_command, &msg, sizeof(msg)); allegro_mcu_interrupt(dev); @@ -1121,7 +999,8 @@ static int allegro_mcu_send_put_stream_buffer(struct allegro_dev *dev, static int allegro_mcu_send_encode_frame(struct allegro_dev *dev, struct allegro_channel *channel, - dma_addr_t src_y, dma_addr_t src_uv) + dma_addr_t src_y, dma_addr_t src_uv, + u64 src_handle) { struct mcu_msg_encode_frame msg; @@ -1134,12 +1013,13 @@ static int allegro_mcu_send_encode_frame(struct allegro_dev *dev, msg.encoding_options = AL_OPT_FORCE_LOAD; msg.pps_qp = 26; /* qp are relative to 26 */ msg.user_param = 0; /* copied to mcu_msg_encode_frame_response */ - msg.src_handle = 0; /* copied to mcu_msg_encode_frame_response */ - msg.src_y = src_y; - msg.src_uv = src_uv; + /* src_handle is copied to mcu_msg_encode_frame_response */ + msg.src_handle = src_handle; + msg.src_y = to_codec_addr(dev, src_y); + msg.src_uv = to_codec_addr(dev, src_uv); msg.stride = channel->stride; msg.ep2 = 0x0; - msg.ep2_v = msg.ep2 | MCU_CACHE_OFFSET; + msg.ep2_v = to_mcu_addr(dev, msg.ep2); allegro_mbox_write(dev, &dev->mbox_command, &msg, sizeof(msg)); allegro_mcu_interrupt(dev); @@ -1198,10 +1078,9 @@ static int allegro_mcu_push_buffer_internal(struct allegro_channel *channel, buffer = msg->buffer; list_for_each_entry(al_buffer, list, head) { - buffer->dma_addr = lower_32_bits(al_buffer->paddr); - buffer->mcu_addr = - lower_32_bits(al_buffer->paddr) | MCU_CACHE_OFFSET; - buffer->size = al_buffer->size; + buffer->dma_addr = to_codec_addr(dev, al_buffer->paddr); + buffer->mcu_addr = to_mcu_addr(dev, al_buffer->paddr); + buffer->size = to_mcu_size(dev, al_buffer->size); buffer++; } @@ -1360,9 +1239,11 @@ static ssize_t allegro_h264_write_sps(struct allegro_channel *channel, sps->vui.chroma_loc_info_present_flag = 1; sps->vui.chroma_sample_loc_type_top_field = 0; sps->vui.chroma_sample_loc_type_bottom_field = 0; + sps->vui.timing_info_present_flag = 1; - sps->vui.num_units_in_tick = 1; - sps->vui.time_scale = 50; + sps->vui.num_units_in_tick = channel->framerate.denominator; + sps->vui.time_scale = 2 * channel->framerate.numerator; + sps->vui.fixed_frame_rate_flag = 1; sps->vui.nal_hrd_parameters_present_flag = 0; sps->vui.vcl_hrd_parameters_present_flag = 1; @@ -1375,7 +1256,8 @@ static ssize_t allegro_h264_write_sps(struct allegro_channel *channel, /* See Rec. ITU-T H.264 (04/2017) p. 410 E-54 */ sps->vui.vcl_hrd_parameters.cpb_size_value_minus1[0] = (channel->cpb_size * 1000) / (1 << (4 + sps->vui.vcl_hrd_parameters.cpb_size_scale)) - 1; - sps->vui.vcl_hrd_parameters.cbr_flag[0] = 1; + sps->vui.vcl_hrd_parameters.cbr_flag[0] = + !v4l2_ctrl_g_ctrl(channel->mpeg_video_frame_rc_enable); sps->vui.vcl_hrd_parameters.initial_cpb_removal_delay_length_minus1 = 31; sps->vui.vcl_hrd_parameters.cpb_removal_delay_length_minus1 = 31; sps->vui.vcl_hrd_parameters.dpb_output_delay_length_minus1 = 31; @@ -1438,8 +1320,11 @@ static bool allegro_channel_is_at_eos(struct allegro_channel *channel) break; case ALLEGRO_STATE_DRAIN: case ALLEGRO_STATE_WAIT_FOR_BUFFER: - if (v4l2_m2m_num_src_bufs_ready(channel->fh.m2m_ctx) == 0) + mutex_lock(&channel->shadow_list_lock); + if (v4l2_m2m_num_src_bufs_ready(channel->fh.m2m_ctx) == 0 && + list_empty(&channel->source_shadow_list)) is_at_eos = true; + mutex_unlock(&channel->shadow_list_lock); break; default: break; @@ -1466,6 +1351,41 @@ static void allegro_channel_buf_done(struct allegro_channel *channel, v4l2_m2m_buf_done(buf, state); } +static u64 allegro_put_buffer(struct allegro_channel *channel, + struct list_head *list, + struct vb2_v4l2_buffer *buffer) +{ + struct v4l2_m2m_buffer *b = container_of(buffer, + struct v4l2_m2m_buffer, vb); + struct allegro_m2m_buffer *shadow = to_allegro_m2m_buffer(b); + + mutex_lock(&channel->shadow_list_lock); + list_add_tail(&shadow->head, list); + mutex_unlock(&channel->shadow_list_lock); + + return ptr_to_u64(buffer); +} + +static struct vb2_v4l2_buffer * +allegro_get_buffer(struct allegro_channel *channel, + struct list_head *list, u64 handle) +{ + struct allegro_m2m_buffer *shadow, *tmp; + struct vb2_v4l2_buffer *buffer = NULL; + + mutex_lock(&channel->shadow_list_lock); + list_for_each_entry_safe(shadow, tmp, list, head) { + if (handle == ptr_to_u64(&shadow->buf.vb)) { + buffer = &shadow->buf.vb; + list_del_init(&shadow->head); + break; + } + } + mutex_unlock(&channel->shadow_list_lock); + + return buffer; +} + static void allegro_channel_finish_frame(struct allegro_channel *channel, struct mcu_msg_encode_frame_response *msg) { @@ -1481,15 +1401,31 @@ static void allegro_channel_finish_frame(struct allegro_channel *channel, ssize_t len; ssize_t free; - src_buf = v4l2_m2m_src_buf_remove(channel->fh.m2m_ctx); + src_buf = allegro_get_buffer(channel, &channel->source_shadow_list, + msg->src_handle); + if (!src_buf) + v4l2_warn(&dev->v4l2_dev, + "channel %d: invalid source buffer\n", + channel->mcu_channel_id); + + dst_buf = allegro_get_buffer(channel, &channel->stream_shadow_list, + msg->stream_id); + if (!dst_buf) + v4l2_warn(&dev->v4l2_dev, + "channel %d: invalid stream buffer\n", + channel->mcu_channel_id); + + if (!src_buf || !dst_buf) + goto err; - dst_buf = v4l2_m2m_dst_buf_remove(channel->fh.m2m_ctx); dst_buf->sequence = channel->csequence++; - if (msg->error_code) { + if (msg->error_code & AL_ERROR) { v4l2_err(&dev->v4l2_dev, - "channel %d: error while encoding frame: %x\n", - channel->mcu_channel_id, msg->error_code); + "channel %d: failed to encode frame: %s (%x)\n", + channel->mcu_channel_id, + allegro_err_to_string(msg->error_code), + msg->error_code); goto err; } @@ -1562,17 +1498,22 @@ static void allegro_channel_finish_frame(struct allegro_channel *channel, channel->mcu_channel_id, len); } - len = nal_h264_write_filler(&dev->plat_dev->dev, curr, free); - if (len < 0) { - v4l2_err(&dev->v4l2_dev, - "failed to write %zd filler data\n", free); - goto err; + if (msg->slice_type != AL_ENC_SLICE_TYPE_I && !msg->is_idr) { + dst_buf->vb2_buf.planes[0].data_offset = free; + free = 0; + } else { + len = nal_h264_write_filler(&dev->plat_dev->dev, curr, free); + if (len < 0) { + v4l2_err(&dev->v4l2_dev, + "failed to write %zd filler data\n", free); + goto err; + } + curr += len; + free -= len; + v4l2_dbg(2, debug, &dev->v4l2_dev, + "channel %d: wrote %zd bytes filler nal unit\n", + channel->mcu_channel_id, len); } - curr += len; - free -= len; - v4l2_dbg(2, debug, &dev->v4l2_dev, - "channel %d: wrote %zd bytes filler nal unit\n", - channel->mcu_channel_id, len); if (free != 0) { v4l2_err(&dev->v4l2_dev, @@ -1590,20 +1531,20 @@ static void allegro_channel_finish_frame(struct allegro_channel *channel, dst_buf->flags |= V4L2_BUF_FLAG_PFRAME; v4l2_dbg(1, debug, &dev->v4l2_dev, - "channel %d: encoded frame #%03d (%s%s, %d bytes)\n", + "channel %d: encoded frame #%03d (%s%s, QP %d, %d bytes)\n", channel->mcu_channel_id, dst_buf->sequence, msg->is_idr ? "IDR, " : "", msg->slice_type == AL_ENC_SLICE_TYPE_I ? "I slice" : msg->slice_type == AL_ENC_SLICE_TYPE_P ? "P slice" : "unknown", - partition->size); + msg->qp, partition->size); err: - v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); - - allegro_channel_buf_done(channel, dst_buf, state); + if (src_buf) + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); - v4l2_m2m_job_finish(dev->m2m_dev, channel->fh.m2m_ctx); + if (dst_buf) + allegro_channel_buf_done(channel, dst_buf, state); } static int allegro_handle_init(struct allegro_dev *dev, @@ -1621,6 +1562,12 @@ allegro_handle_create_channel(struct allegro_dev *dev, struct allegro_channel *channel; int err = 0; + if (msg->header.length != sizeof(*msg) - sizeof(msg->header)) + v4l2_warn(&dev->v4l2_dev, + "received message has %d bytes, but expected %zu\n", + msg->header.length, + sizeof(*msg) - sizeof(msg->header)); + channel = allegro_find_channel_by_user_id(dev, msg->user_id); if (IS_ERR(channel)) { v4l2_warn(&dev->v4l2_dev, @@ -1632,8 +1579,10 @@ allegro_handle_create_channel(struct allegro_dev *dev, if (msg->error_code) { v4l2_err(&dev->v4l2_dev, - "user %d: mcu failed to create channel: error %x\n", - channel->user_id, msg->error_code); + "user %d: mcu failed to create channel: %s (%x)\n", + channel->user_id, + allegro_err_to_string(msg->error_code), + msg->error_code); err = -EIO; goto out; } @@ -1712,6 +1661,12 @@ allegro_handle_encode_frame(struct allegro_dev *dev, { struct allegro_channel *channel; + if (msg->header.length != sizeof(*msg) - sizeof(msg->header)) + v4l2_warn(&dev->v4l2_dev, + "received message has %d bytes, but expected %zu\n", + msg->header.length, + sizeof(*msg) - sizeof(msg->header)); + channel = allegro_find_channel_by_channel_id(dev, msg->channel_id); if (IS_ERR(channel)) { v4l2_err(&dev->v4l2_dev, @@ -1920,6 +1875,14 @@ static int allegro_mcu_reset(struct allegro_dev *dev) { int err; + /* + * Ensure that the AL5_MCU_WAKEUP bit is set to 0 otherwise the mcu + * does not go to sleep after the reset. + */ + err = regmap_write(dev->regmap, AL5_MCU_WAKEUP, 0); + if (err) + return err; + err = regmap_write(dev->regmap, AL5_MCU_RESET_MODE, AL5_MCU_RESET_MODE_SLEEP); if (err < 0) @@ -1955,6 +1918,12 @@ static void allegro_destroy_channel(struct allegro_channel *channel) v4l2_ctrl_grab(channel->mpeg_video_h264_profile, false); v4l2_ctrl_grab(channel->mpeg_video_h264_level, false); + v4l2_ctrl_grab(channel->mpeg_video_h264_i_frame_qp, false); + v4l2_ctrl_grab(channel->mpeg_video_h264_max_qp, false); + v4l2_ctrl_grab(channel->mpeg_video_h264_min_qp, false); + v4l2_ctrl_grab(channel->mpeg_video_h264_p_frame_qp, false); + v4l2_ctrl_grab(channel->mpeg_video_h264_b_frame_qp, false); + v4l2_ctrl_grab(channel->mpeg_video_frame_rc_enable, false); v4l2_ctrl_grab(channel->mpeg_video_bitrate_mode, false); v4l2_ctrl_grab(channel->mpeg_video_bitrate, false); v4l2_ctrl_grab(channel->mpeg_video_bitrate_peak, false); @@ -2000,7 +1969,9 @@ static int allegro_create_channel(struct allegro_channel *channel) v4l2_dbg(1, debug, &dev->v4l2_dev, "user %d: creating channel (%4.4s, %dx%d@%d)\n", channel->user_id, - (char *)&channel->codec, channel->width, channel->height, 25); + (char *)&channel->codec, channel->width, channel->height, + DIV_ROUND_UP(channel->framerate.numerator, + channel->framerate.denominator)); min_level = select_minimum_h264_level(channel->width, channel->height); if (channel->level < min_level) { @@ -2014,6 +1985,12 @@ static int allegro_create_channel(struct allegro_channel *channel) v4l2_ctrl_grab(channel->mpeg_video_h264_profile, true); v4l2_ctrl_grab(channel->mpeg_video_h264_level, true); + v4l2_ctrl_grab(channel->mpeg_video_h264_i_frame_qp, true); + v4l2_ctrl_grab(channel->mpeg_video_h264_max_qp, true); + v4l2_ctrl_grab(channel->mpeg_video_h264_min_qp, true); + v4l2_ctrl_grab(channel->mpeg_video_h264_p_frame_qp, true); + v4l2_ctrl_grab(channel->mpeg_video_h264_b_frame_qp, true); + v4l2_ctrl_grab(channel->mpeg_video_frame_rc_enable, true); v4l2_ctrl_grab(channel->mpeg_video_bitrate_mode, true); v4l2_ctrl_grab(channel->mpeg_video_bitrate, true); v4l2_ctrl_grab(channel->mpeg_video_bitrate_peak, true); @@ -2046,6 +2023,7 @@ static void allegro_set_default_params(struct allegro_channel *channel) channel->width = ALLEGRO_WIDTH_DEFAULT; channel->height = ALLEGRO_HEIGHT_DEFAULT; channel->stride = round_up(channel->width, 32); + channel->framerate = ALLEGRO_FRAMERATE_DEFAULT; channel->colorspace = V4L2_COLORSPACE_REC709; channel->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; @@ -2062,7 +2040,6 @@ static void allegro_set_default_params(struct allegro_channel *channel) channel->sizeimage_encoded = estimate_stream_size(channel->width, channel->height); - channel->bitrate_mode = V4L2_MPEG_VIDEO_BITRATE_MODE_CBR; channel->bitrate = maximum_bitrate(channel->level); channel->bitrate_peak = maximum_bitrate(channel->level); channel->cpb_size = maximum_cpb_size(channel->level); @@ -2163,16 +2140,33 @@ static void allegro_stop_streaming(struct vb2_queue *q) struct allegro_channel *channel = vb2_get_drv_priv(q); struct allegro_dev *dev = channel->dev; struct vb2_v4l2_buffer *buffer; + struct allegro_m2m_buffer *shadow, *tmp; v4l2_dbg(2, debug, &dev->v4l2_dev, "%s: stop streaming\n", V4L2_TYPE_IS_OUTPUT(q->type) ? "output" : "capture"); if (V4L2_TYPE_IS_OUTPUT(q->type)) { + mutex_lock(&channel->shadow_list_lock); + list_for_each_entry_safe(shadow, tmp, + &channel->source_shadow_list, head) { + list_del(&shadow->head); + v4l2_m2m_buf_done(&shadow->buf.vb, VB2_BUF_STATE_ERROR); + } + mutex_unlock(&channel->shadow_list_lock); + allegro_set_state(channel, ALLEGRO_STATE_STOPPED); while ((buffer = v4l2_m2m_src_buf_remove(channel->fh.m2m_ctx))) v4l2_m2m_buf_done(buffer, VB2_BUF_STATE_ERROR); } else if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + mutex_lock(&channel->shadow_list_lock); + list_for_each_entry_safe(shadow, tmp, + &channel->stream_shadow_list, head) { + list_del(&shadow->head); + v4l2_m2m_buf_done(&shadow->buf.vb, VB2_BUF_STATE_ERROR); + } + mutex_unlock(&channel->shadow_list_lock); + allegro_destroy_channel(channel); while ((buffer = v4l2_m2m_dst_buf_remove(channel->fh.m2m_ctx))) v4l2_m2m_buf_done(buffer, VB2_BUF_STATE_ERROR); @@ -2203,7 +2197,7 @@ static int allegro_queue_init(void *priv, src_vq->drv_priv = channel; src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; src_vq->ops = &allegro_queue_ops; - src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + src_vq->buf_struct_size = sizeof(struct allegro_m2m_buffer); src_vq->lock = &channel->dev->lock; err = vb2_queue_init(src_vq); if (err) @@ -2216,7 +2210,7 @@ static int allegro_queue_init(void *priv, dst_vq->drv_priv = channel; dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; dst_vq->ops = &allegro_queue_ops; - dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + dst_vq->buf_struct_size = sizeof(struct allegro_m2m_buffer); dst_vq->lock = &channel->dev->lock; err = vb2_queue_init(dst_vq); if (err) @@ -2225,6 +2219,52 @@ static int allegro_queue_init(void *priv, return 0; } +static int allegro_clamp_qp(struct allegro_channel *channel, + struct v4l2_ctrl *ctrl) +{ + struct v4l2_ctrl *next_ctrl; + + if (ctrl->id == V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP) + next_ctrl = channel->mpeg_video_h264_p_frame_qp; + else if (ctrl->id == V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP) + next_ctrl = channel->mpeg_video_h264_b_frame_qp; + else + return 0; + + /* Modify range automatically updates the value */ + __v4l2_ctrl_modify_range(next_ctrl, ctrl->val, 51, 1, ctrl->val); + + return allegro_clamp_qp(channel, next_ctrl); +} + +static int allegro_clamp_bitrate(struct allegro_channel *channel, + struct v4l2_ctrl *ctrl) +{ + struct v4l2_ctrl *ctrl_bitrate = channel->mpeg_video_bitrate; + struct v4l2_ctrl *ctrl_bitrate_peak = channel->mpeg_video_bitrate_peak; + + if (ctrl->val == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR && + ctrl_bitrate_peak->val < ctrl_bitrate->val) + ctrl_bitrate_peak->val = ctrl_bitrate->val; + + return 0; +} + +static int allegro_try_ctrl(struct v4l2_ctrl *ctrl) +{ + struct allegro_channel *channel = container_of(ctrl->handler, + struct allegro_channel, + ctrl_handler); + + switch (ctrl->id) { + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: + allegro_clamp_bitrate(channel, ctrl); + break; + } + + return 0; +} + static int allegro_s_ctrl(struct v4l2_ctrl *ctrl) { struct allegro_channel *channel = container_of(ctrl->handler, @@ -2239,14 +2279,14 @@ static int allegro_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_MPEG_VIDEO_H264_LEVEL: channel->level = ctrl->val; break; - case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: - channel->bitrate_mode = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_BITRATE: - channel->bitrate = ctrl->val; + case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE: + channel->frame_rc_enable = ctrl->val; break; - case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: - channel->bitrate_peak = ctrl->val; + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: + channel->bitrate = channel->mpeg_video_bitrate->val; + channel->bitrate_peak = channel->mpeg_video_bitrate_peak->val; + v4l2_ctrl_activate(channel->mpeg_video_bitrate_peak, + ctrl->val == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR); break; case V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE: channel->cpb_size = ctrl->val; @@ -2254,12 +2294,18 @@ static int allegro_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_MPEG_VIDEO_GOP_SIZE: channel->gop_size = ctrl->val; break; + case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP: + case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP: + case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP: + allegro_clamp_qp(channel, ctrl); + break; } return 0; } static const struct v4l2_ctrl_ops allegro_ctrl_ops = { + .try_ctrl = allegro_try_ctrl, .s_ctrl = allegro_s_ctrl, }; @@ -2270,16 +2316,18 @@ static int allegro_open(struct file *file) struct allegro_channel *channel = NULL; struct v4l2_ctrl_handler *handler; u64 mask; + int ret; channel = kzalloc(sizeof(*channel), GFP_KERNEL); if (!channel) return -ENOMEM; v4l2_fh_init(&channel->fh, vdev); - file->private_data = &channel->fh; - v4l2_fh_add(&channel->fh); init_completion(&channel->completion); + INIT_LIST_HEAD(&channel->source_shadow_list); + INIT_LIST_HEAD(&channel->stream_shadow_list); + mutex_init(&channel->shadow_list_lock); channel->dev = dev; @@ -2298,11 +2346,42 @@ static int allegro_open(struct file *file) V4L2_CID_MPEG_VIDEO_H264_LEVEL, V4L2_MPEG_VIDEO_H264_LEVEL_5_1, mask, V4L2_MPEG_VIDEO_H264_LEVEL_5_1); + channel->mpeg_video_h264_i_frame_qp = + v4l2_ctrl_new_std(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP, + 0, 51, 1, 30); + channel->mpeg_video_h264_max_qp = + v4l2_ctrl_new_std(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_MAX_QP, + 0, 51, 1, 51); + channel->mpeg_video_h264_min_qp = + v4l2_ctrl_new_std(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_MIN_QP, + 0, 51, 1, 0); + channel->mpeg_video_h264_p_frame_qp = + v4l2_ctrl_new_std(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP, + 0, 51, 1, 30); + channel->mpeg_video_h264_b_frame_qp = + v4l2_ctrl_new_std(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP, + 0, 51, 1, 30); + channel->mpeg_video_frame_rc_enable = + v4l2_ctrl_new_std(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE, + false, 0x1, + true, false); channel->mpeg_video_bitrate_mode = v4l2_ctrl_new_std_menu(handler, &allegro_ctrl_ops, V4L2_CID_MPEG_VIDEO_BITRATE_MODE, V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 0, - channel->bitrate_mode); + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR); channel->mpeg_video_bitrate = v4l2_ctrl_new_std(handler, &allegro_ctrl_ops, V4L2_CID_MPEG_VIDEO_BITRATE, @@ -2328,8 +2407,15 @@ static int allegro_open(struct file *file) V4L2_CID_MIN_BUFFERS_FOR_OUTPUT, 1, 32, 1, 1); + if (handler->error != 0) { + ret = handler->error; + goto error; + } + channel->fh.ctrl_handler = handler; + v4l2_ctrl_cluster(3, &channel->mpeg_video_bitrate_mode); + channel->mcu_channel_id = -1; channel->user_id = -1; @@ -2341,7 +2427,20 @@ static int allegro_open(struct file *file) channel->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, channel, allegro_queue_init); + if (IS_ERR(channel->fh.m2m_ctx)) { + ret = PTR_ERR(channel->fh.m2m_ctx); + goto error; + } + + file->private_data = &channel->fh; + v4l2_fh_add(&channel->fh); + return 0; + +error: + v4l2_ctrl_handler_free(handler); + kfree(channel); + return ret; } static int allegro_release(struct file *file) @@ -2636,6 +2735,46 @@ static int allegro_ioctl_streamon(struct file *file, void *priv, return v4l2_m2m_streamon(file, fh->m2m_ctx, type); } +static int allegro_g_parm(struct file *file, void *fh, + struct v4l2_streamparm *a) +{ + struct allegro_channel *channel = fh_to_channel(fh); + struct v4l2_fract *timeperframe; + + if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + a->parm.output.capability = V4L2_CAP_TIMEPERFRAME; + timeperframe = &a->parm.output.timeperframe; + timeperframe->numerator = channel->framerate.denominator; + timeperframe->denominator = channel->framerate.numerator; + + return 0; +} + +static int allegro_s_parm(struct file *file, void *fh, + struct v4l2_streamparm *a) +{ + struct allegro_channel *channel = fh_to_channel(fh); + struct v4l2_fract *timeperframe; + int div; + + if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + a->parm.output.capability = V4L2_CAP_TIMEPERFRAME; + timeperframe = &a->parm.output.timeperframe; + + if (timeperframe->numerator == 0 || timeperframe->denominator == 0) + return allegro_g_parm(file, fh, a); + + div = gcd(timeperframe->denominator, timeperframe->numerator); + channel->framerate.numerator = timeperframe->denominator / div; + channel->framerate.denominator = timeperframe->numerator / div; + + return 0; +} + static int allegro_subscribe_event(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub) { @@ -2674,6 +2813,9 @@ static const struct v4l2_ioctl_ops allegro_ioctl_ops = { .vidioc_encoder_cmd = allegro_encoder_cmd, .vidioc_enum_framesizes = allegro_enum_framesizes, + .vidioc_g_parm = allegro_g_parm, + .vidioc_s_parm = allegro_s_parm, + .vidioc_subscribe_event = allegro_subscribe_event, .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; @@ -2701,7 +2843,7 @@ static int allegro_register_device(struct allegro_dev *dev) video_dev->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING; video_set_drvdata(video_dev, dev); - return video_register_device(video_dev, VFL_TYPE_GRABBER, 0); + return video_register_device(video_dev, VFL_TYPE_VIDEO, 0); } static void allegro_device_run(void *priv) @@ -2714,18 +2856,26 @@ static void allegro_device_run(void *priv) dma_addr_t src_uv; dma_addr_t dst_addr; unsigned long dst_size; + u64 src_handle; + u64 dst_handle; - dst_buf = v4l2_m2m_next_dst_buf(channel->fh.m2m_ctx); + dst_buf = v4l2_m2m_dst_buf_remove(channel->fh.m2m_ctx); dst_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); dst_size = vb2_plane_size(&dst_buf->vb2_buf, 0); - allegro_mcu_send_put_stream_buffer(dev, channel, dst_addr, dst_size); + dst_handle = allegro_put_buffer(channel, &channel->stream_shadow_list, + dst_buf); + allegro_mcu_send_put_stream_buffer(dev, channel, dst_addr, dst_size, + dst_handle); - src_buf = v4l2_m2m_next_src_buf(channel->fh.m2m_ctx); + src_buf = v4l2_m2m_src_buf_remove(channel->fh.m2m_ctx); src_buf->sequence = channel->osequence++; - src_y = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0); src_uv = src_y + (channel->stride * channel->height); - allegro_mcu_send_encode_frame(dev, channel, src_y, src_uv); + src_handle = allegro_put_buffer(channel, &channel->source_shadow_list, + src_buf); + allegro_mcu_send_encode_frame(dev, channel, src_y, src_uv, src_handle); + + v4l2_m2m_job_finish(dev->m2m_dev, channel->fh.m2m_ctx); } static const struct v4l2_m2m_ops allegro_m2m_ops = { diff --git a/drivers/staging/media/allegro-dvt/allegro-mail.c b/drivers/staging/media/allegro-dvt/allegro-mail.c new file mode 100644 index 000000000000..df0d8d26a6fb --- /dev/null +++ b/drivers/staging/media/allegro-dvt/allegro-mail.c @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Pengutronix, Michael Tretter <kernel@pengutronix.de> + * + * Helper functions for handling messages that are send via mailbox to the + * Allegro VCU firmware. + */ + +#include <linux/export.h> + +#include "allegro-mail.h" + +const char *msg_type_name(enum mcu_msg_type type) +{ + static char buf[9]; + + switch (type) { + case MCU_MSG_TYPE_INIT: + return "INIT"; + case MCU_MSG_TYPE_CREATE_CHANNEL: + return "CREATE_CHANNEL"; + case MCU_MSG_TYPE_DESTROY_CHANNEL: + return "DESTROY_CHANNEL"; + case MCU_MSG_TYPE_ENCODE_FRAME: + return "ENCODE_FRAME"; + case MCU_MSG_TYPE_PUT_STREAM_BUFFER: + return "PUT_STREAM_BUFFER"; + case MCU_MSG_TYPE_PUSH_BUFFER_INTERMEDIATE: + return "PUSH_BUFFER_INTERMEDIATE"; + case MCU_MSG_TYPE_PUSH_BUFFER_REFERENCE: + return "PUSH_BUFFER_REFERENCE"; + default: + snprintf(buf, sizeof(buf), "(0x%04x)", type); + return buf; + } +} +EXPORT_SYMBOL(msg_type_name); diff --git a/drivers/staging/media/allegro-dvt/allegro-mail.h b/drivers/staging/media/allegro-dvt/allegro-mail.h new file mode 100644 index 000000000000..1fd36f65be78 --- /dev/null +++ b/drivers/staging/media/allegro-dvt/allegro-mail.h @@ -0,0 +1,267 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2019 Pengutronix, Michael Tretter <kernel@pengutronix.de> + * + * Allegro VCU firmware mailbox mail definitions + */ + +#ifndef ALLEGRO_MAIL_H +#define ALLEGRO_MAIL_H + +#include <linux/kernel.h> + +enum mcu_msg_type { + MCU_MSG_TYPE_INIT = 0x0000, + MCU_MSG_TYPE_CREATE_CHANNEL = 0x0005, + MCU_MSG_TYPE_DESTROY_CHANNEL = 0x0006, + MCU_MSG_TYPE_ENCODE_FRAME = 0x0007, + MCU_MSG_TYPE_PUT_STREAM_BUFFER = 0x0012, + MCU_MSG_TYPE_PUSH_BUFFER_INTERMEDIATE = 0x000e, + MCU_MSG_TYPE_PUSH_BUFFER_REFERENCE = 0x000f, +}; + +const char *msg_type_name(enum mcu_msg_type type); + +struct mcu_msg_header { + u16 length; /* length of the body in bytes */ + u16 type; +} __attribute__ ((__packed__)); + +struct mcu_msg_init_request { + struct mcu_msg_header header; + u32 reserved0; /* maybe a unused channel id */ + u32 suballoc_dma; + u32 suballoc_size; + s32 l2_cache[3]; +} __attribute__ ((__packed__)); + +struct mcu_msg_init_response { + struct mcu_msg_header header; + u32 reserved0; +} __attribute__ ((__packed__)); + +struct create_channel_param { + u16 width; + u16 height; + u32 format; + u32 colorspace; + u32 src_mode; + u8 profile; + u16 constraint_set_flags; + s8 codec; + u16 level; + u16 tier; + u32 sps_param; + u32 pps_param; + + u32 enc_option; +#define AL_OPT_WPP BIT(0) +#define AL_OPT_TILE BIT(1) +#define AL_OPT_LF BIT(2) +#define AL_OPT_LF_X_SLICE BIT(3) +#define AL_OPT_LF_X_TILE BIT(4) +#define AL_OPT_SCL_LST BIT(5) +#define AL_OPT_CONST_INTRA_PRED BIT(6) +#define AL_OPT_QP_TAB_RELATIVE BIT(7) +#define AL_OPT_FIX_PREDICTOR BIT(8) +#define AL_OPT_CUSTOM_LDA BIT(9) +#define AL_OPT_ENABLE_AUTO_QP BIT(10) +#define AL_OPT_ADAPT_AUTO_QP BIT(11) +#define AL_OPT_TRANSFO_SKIP BIT(13) +#define AL_OPT_FORCE_REC BIT(15) +#define AL_OPT_FORCE_MV_OUT BIT(16) +#define AL_OPT_FORCE_MV_CLIP BIT(17) +#define AL_OPT_LOWLAT_SYNC BIT(18) +#define AL_OPT_LOWLAT_INT BIT(19) +#define AL_OPT_RDO_COST_MODE BIT(20) + + s8 beta_offset; + s8 tc_offset; + u16 reserved10; + u32 unknown11; + u32 unknown12; + u16 num_slices; + u16 prefetch_auto; + u32 prefetch_mem_offset; + u32 prefetch_mem_size; + u16 clip_hrz_range; + u16 clip_vrt_range; + u16 me_range[4]; + u8 max_cu_size; + u8 min_cu_size; + u8 max_tu_size; + u8 min_tu_size; + u8 max_transfo_depth_inter; + u8 max_transfo_depth_intra; + u16 reserved20; + u32 entropy_mode; + u32 wp_mode; + + /* rate control param */ + u32 rate_control_mode; + u32 initial_rem_delay; + u32 cpb_size; + u16 framerate; + u16 clk_ratio; + u32 target_bitrate; + u32 max_bitrate; + u16 initial_qp; + u16 min_qp; + u16 max_qp; + s16 ip_delta; + s16 pb_delta; + u16 golden_ref; + u16 golden_delta; + u16 golden_ref_frequency; + u32 rate_control_option; + + /* gop param */ + u32 gop_ctrl_mode; + u32 freq_idr; + u32 freq_lt; + u32 gdr_mode; + u16 gop_length; + u8 num_b; + u8 freq_golden_ref; + + u32 subframe_latency; + u32 lda_control_mode; + u32 unknown41; +} __attribute__ ((__packed__)); + +struct mcu_msg_create_channel { + struct mcu_msg_header header; + u32 user_id; + struct create_channel_param param; +} __attribute__ ((__packed__)); + +struct mcu_msg_create_channel_response { + struct mcu_msg_header header; + u32 channel_id; + u32 user_id; + u32 options; + u32 num_core; + u32 pps_param; + u32 int_buffers_count; + u32 int_buffers_size; + u32 rec_buffers_count; + u32 rec_buffers_size; + u32 reserved; + u32 error_code; +} __attribute__ ((__packed__)); + +struct mcu_msg_destroy_channel { + struct mcu_msg_header header; + u32 channel_id; +} __attribute__ ((__packed__)); + +struct mcu_msg_destroy_channel_response { + struct mcu_msg_header header; + u32 channel_id; +} __attribute__ ((__packed__)); + +struct mcu_msg_push_buffers_internal_buffer { + u32 dma_addr; + u32 mcu_addr; + u32 size; +} __attribute__ ((__packed__)); + +struct mcu_msg_push_buffers_internal { + struct mcu_msg_header header; + u32 channel_id; + struct mcu_msg_push_buffers_internal_buffer buffer[0]; +} __attribute__ ((__packed__)); + +struct mcu_msg_put_stream_buffer { + struct mcu_msg_header header; + u32 channel_id; + u32 dma_addr; + u32 mcu_addr; + u32 size; + u32 offset; + u64 stream_id; +} __attribute__ ((__packed__)); + +struct mcu_msg_encode_frame { + struct mcu_msg_header header; + u32 channel_id; + u32 reserved; + + u32 encoding_options; +#define AL_OPT_USE_QP_TABLE BIT(0) +#define AL_OPT_FORCE_LOAD BIT(1) +#define AL_OPT_USE_L2 BIT(2) +#define AL_OPT_DISABLE_INTRA BIT(3) +#define AL_OPT_DEPENDENT_SLICES BIT(4) + + s16 pps_qp; + u16 padding; + u64 user_param; + u64 src_handle; + + u32 request_options; +#define AL_OPT_SCENE_CHANGE BIT(0) +#define AL_OPT_RESTART_GOP BIT(1) +#define AL_OPT_USE_LONG_TERM BIT(2) +#define AL_OPT_UPDATE_PARAMS BIT(3) + + /* u32 scene_change_delay (optional) */ + /* rate control param (optional) */ + /* gop param (optional) */ + u32 src_y; + u32 src_uv; + u32 stride; + u32 ep2; + u64 ep2_v; +} __attribute__ ((__packed__)); + +struct mcu_msg_encode_frame_response { + struct mcu_msg_header header; + u32 channel_id; + u64 stream_id; /* see mcu_msg_put_stream_buffer */ + u64 user_param; /* see mcu_msg_encode_frame */ + u64 src_handle; /* see mcu_msg_encode_frame */ + u16 skip; + u16 is_ref; + u32 initial_removal_delay; + u32 dpb_output_delay; + u32 size; + u32 frame_tag_size; + s32 stuffing; + s32 filler; + u16 num_column; + u16 num_row; + u16 qp; + u8 num_ref_idx_l0; + u8 num_ref_idx_l1; + u32 partition_table_offset; + s32 partition_table_size; + u32 sum_complex; + s32 tile_width[4]; + s32 tile_height[22]; + u32 error_code; + + u32 slice_type; +#define AL_ENC_SLICE_TYPE_B 0 +#define AL_ENC_SLICE_TYPE_P 1 +#define AL_ENC_SLICE_TYPE_I 2 + + u32 pic_struct; + u8 is_idr; + u8 is_first_slice; + u8 is_last_slice; + u8 reserved; + u16 pps_qp; + u16 reserved1; + u32 reserved2; +} __attribute__ ((__packed__)); + +union mcu_msg_response { + struct mcu_msg_header header; + struct mcu_msg_init_response init; + struct mcu_msg_create_channel_response create_channel; + struct mcu_msg_destroy_channel_response destroy_channel; + struct mcu_msg_encode_frame_response encode_frame; +}; + +#endif diff --git a/drivers/staging/media/hantro/Kconfig b/drivers/staging/media/hantro/Kconfig index de77fe6554e7..99aed9a5b0b9 100644 --- a/drivers/staging/media/hantro/Kconfig +++ b/drivers/staging/media/hantro/Kconfig @@ -1,19 +1,27 @@ # SPDX-License-Identifier: GPL-2.0 config VIDEO_HANTRO tristate "Hantro VPU driver" - depends on ARCH_ROCKCHIP || COMPILE_TEST + depends on ARCH_MXC || ARCH_ROCKCHIP || COMPILE_TEST depends on VIDEO_DEV && VIDEO_V4L2 && MEDIA_CONTROLLER depends on MEDIA_CONTROLLER_REQUEST_API select VIDEOBUF2_DMA_CONTIG select VIDEOBUF2_VMALLOC select V4L2_MEM2MEM_DEV help - Support for the Hantro IP based Video Processing Unit present on - Rockchip SoC, which accelerates video and image encoding and - decoding. + Support for the Hantro IP based Video Processing Units present on + Rockchip and NXP i.MX8M SoCs, which accelerate video and image + encoding and decoding. To compile this driver as a module, choose M here: the module will be called hantro-vpu. +config VIDEO_HANTRO_IMX8M + bool "Hantro VPU i.MX8M support" + depends on VIDEO_HANTRO + depends on ARCH_MXC || COMPILE_TEST + default y + help + Enable support for i.MX8M SoCs. + config VIDEO_HANTRO_ROCKCHIP bool "Hantro VPU Rockchip support" depends on VIDEO_HANTRO diff --git a/drivers/staging/media/hantro/Makefile b/drivers/staging/media/hantro/Makefile index 496b30c3c396..68c29a9c4946 100644 --- a/drivers/staging/media/hantro/Makefile +++ b/drivers/staging/media/hantro/Makefile @@ -16,6 +16,9 @@ hantro-vpu-y += \ hantro_mpeg2.o \ hantro_vp8.o +hantro-vpu-$(CONFIG_VIDEO_HANTRO_IMX8M) += \ + imx8m_vpu_hw.o + hantro-vpu-$(CONFIG_VIDEO_HANTRO_ROCKCHIP) += \ rk3288_vpu_hw.o \ rk3399_vpu_hw.o diff --git a/drivers/staging/media/hantro/hantro.h b/drivers/staging/media/hantro/hantro.h index b0faa43b3f79..327ddef45345 100644 --- a/drivers/staging/media/hantro/hantro.h +++ b/drivers/staging/media/hantro/hantro.h @@ -423,7 +423,7 @@ hantro_get_dst_buf(struct hantro_ctx *ctx) static inline bool hantro_needs_postproc(struct hantro_ctx *ctx, const struct hantro_fmt *fmt) { - return fmt->fourcc != V4L2_PIX_FMT_NV12; + return !hantro_is_encoder_ctx(ctx) && fmt->fourcc != V4L2_PIX_FMT_NV12; } static inline dma_addr_t diff --git a/drivers/staging/media/hantro/hantro_drv.c b/drivers/staging/media/hantro/hantro_drv.c index c98835326135..ace13973e2d0 100644 --- a/drivers/staging/media/hantro/hantro_drv.c +++ b/drivers/staging/media/hantro/hantro_drv.c @@ -362,6 +362,16 @@ static const struct hantro_ctrl controls[] = { .max = V4L2_MPEG_VIDEO_H264_START_CODE_ANNEX_B, }, }, { + .codec = HANTRO_H264_DECODER, + .cfg = { + .id = V4L2_CID_MPEG_VIDEO_H264_PROFILE, + .min = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE, + .max = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH, + .menu_skip_mask = + BIT(V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED), + .def = V4L2_MPEG_VIDEO_H264_PROFILE_MAIN, + } + }, { }, }; @@ -489,6 +499,9 @@ static const struct of_device_id of_hantro_match[] = { { .compatible = "rockchip,rk3328-vpu", .data = &rk3328_vpu_variant, }, { .compatible = "rockchip,rk3288-vpu", .data = &rk3288_vpu_variant, }, #endif +#ifdef CONFIG_VIDEO_HANTRO_IMX8M + { .compatible = "nxp,imx8mq-vpu", .data = &imx8mq_vpu_variant, }, +#endif { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, of_hantro_match); @@ -664,7 +677,7 @@ static int hantro_add_func(struct hantro_dev *vpu, unsigned int funcid) video_set_drvdata(vfd, vpu); - ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1); if (ret) { v4l2_err(&vpu->v4l2_dev, "Failed to register video device\n"); return ret; diff --git a/drivers/staging/media/hantro/hantro_h1_jpeg_enc.c b/drivers/staging/media/hantro/hantro_h1_jpeg_enc.c index 0d8afc3e5d71..b22418436823 100644 --- a/drivers/staging/media/hantro/hantro_h1_jpeg_enc.c +++ b/drivers/staging/media/hantro/hantro_h1_jpeg_enc.c @@ -67,12 +67,23 @@ hantro_h1_jpeg_enc_set_qtable(struct hantro_dev *vpu, unsigned char *chroma_qtable) { u32 reg, i; + __be32 *luma_qtable_p; + __be32 *chroma_qtable_p; + luma_qtable_p = (__be32 *)luma_qtable; + chroma_qtable_p = (__be32 *)chroma_qtable; + + /* + * Quantization table registers must be written in contiguous blocks. + * DO NOT collapse the below two "for" loops into one. + */ for (i = 0; i < H1_JPEG_QUANT_TABLE_COUNT; i++) { - reg = get_unaligned_be32(&luma_qtable[i]); + reg = get_unaligned_be32(&luma_qtable_p[i]); vepu_write_relaxed(vpu, reg, H1_REG_JPEG_LUMA_QUAT(i)); + } - reg = get_unaligned_be32(&chroma_qtable[i]); + for (i = 0; i < H1_JPEG_QUANT_TABLE_COUNT; i++) { + reg = get_unaligned_be32(&chroma_qtable_p[i]); vepu_write_relaxed(vpu, reg, H1_REG_JPEG_CHROMA_QUAT(i)); } } @@ -103,8 +114,8 @@ void hantro_h1_jpeg_enc_run(struct hantro_ctx *ctx) hantro_h1_set_src_img_ctrl(vpu, ctx); hantro_h1_jpeg_enc_set_buffers(vpu, ctx, &src_buf->vb2_buf); hantro_h1_jpeg_enc_set_qtable(vpu, - hantro_jpeg_get_qtable(&jpeg_ctx, 0), - hantro_jpeg_get_qtable(&jpeg_ctx, 1)); + hantro_jpeg_get_qtable(0), + hantro_jpeg_get_qtable(1)); reg = H1_REG_AXI_CTRL_OUTPUT_SWAP16 | H1_REG_AXI_CTRL_INPUT_SWAP16 diff --git a/drivers/staging/media/hantro/hantro_hw.h b/drivers/staging/media/hantro/hantro_hw.h index 2398d4c1f207..7dfc9bad7297 100644 --- a/drivers/staging/media/hantro/hantro_hw.h +++ b/drivers/staging/media/hantro/hantro_hw.h @@ -151,6 +151,7 @@ enum hantro_enc_fmt { extern const struct hantro_variant rk3399_vpu_variant; extern const struct hantro_variant rk3328_vpu_variant; extern const struct hantro_variant rk3288_vpu_variant; +extern const struct hantro_variant imx8mq_vpu_variant; extern const struct hantro_postproc_regs hantro_g1_postproc_regs; diff --git a/drivers/staging/media/hantro/hantro_jpeg.c b/drivers/staging/media/hantro/hantro_jpeg.c index 125eb41f2ede..36c140fc6a36 100644 --- a/drivers/staging/media/hantro/hantro_jpeg.c +++ b/drivers/staging/media/hantro/hantro_jpeg.c @@ -23,19 +23,21 @@ #define HUFF_CHROMA_AC_OFF 409 /* Default tables from JPEG ITU-T.81 - * (ISO/IEC 10918-1) Annex K.3, I + * (ISO/IEC 10918-1) Annex K, tables K.1 and K.2 */ static const unsigned char luma_q_table[] = { - 0x10, 0x0b, 0x0a, 0x10, 0x7c, 0x8c, 0x97, 0xa1, - 0x0c, 0x0c, 0x0e, 0x13, 0x7e, 0x9e, 0xa0, 0x9b, - 0x0e, 0x0d, 0x10, 0x18, 0x8c, 0x9d, 0xa9, 0x9c, - 0x0e, 0x11, 0x16, 0x1d, 0x97, 0xbb, 0xb4, 0xa2, - 0x12, 0x16, 0x25, 0x38, 0xa8, 0x6d, 0x67, 0xb1, - 0x18, 0x23, 0x37, 0x40, 0xb5, 0x68, 0x71, 0xc0, + 0x10, 0x0b, 0x0a, 0x10, 0x18, 0x28, 0x33, 0x3d, + 0x0c, 0x0c, 0x0e, 0x13, 0x1a, 0x3a, 0x3c, 0x37, + 0x0e, 0x0d, 0x10, 0x18, 0x28, 0x39, 0x45, 0x38, + 0x0e, 0x11, 0x16, 0x1d, 0x33, 0x57, 0x50, 0x3e, + 0x12, 0x16, 0x25, 0x38, 0x44, 0x6d, 0x67, 0x4d, + 0x18, 0x23, 0x37, 0x40, 0x51, 0x68, 0x71, 0x5c, 0x31, 0x40, 0x4e, 0x57, 0x67, 0x79, 0x78, 0x65, - 0x48, 0x5c, 0x5f, 0x62, 0x70, 0x64, 0x67, 0xc7, + 0x48, 0x5c, 0x5f, 0x62, 0x70, 0x64, 0x67, 0x63 }; +static unsigned char luma_q_table_reordered[ARRAY_SIZE(luma_q_table)]; + static const unsigned char chroma_q_table[] = { 0x11, 0x12, 0x18, 0x2f, 0x63, 0x63, 0x63, 0x63, 0x12, 0x15, 0x1a, 0x42, 0x63, 0x63, 0x63, 0x63, @@ -47,6 +49,30 @@ static const unsigned char chroma_q_table[] = { 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63 }; +static unsigned char chroma_q_table_reordered[ARRAY_SIZE(chroma_q_table)]; + +static const unsigned char zigzag[64] = { + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63 +}; + +static const u32 hw_reorder[64] = { + 0, 8, 16, 24, 1, 9, 17, 25, + 32, 40, 48, 56, 33, 41, 49, 57, + 2, 10, 18, 26, 3, 11, 19, 27, + 34, 42, 50, 58, 35, 43, 51, 59, + 4, 12, 20, 28, 5, 13, 21, 29, + 36, 44, 52, 60, 37, 45, 53, 61, + 6, 14, 22, 30, 7, 15, 23, 31, + 38, 46, 54, 62, 39, 47, 55, 63 +}; + /* Huffman tables are shared with CODA */ static const unsigned char luma_dc_table[] = { 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, @@ -225,20 +251,29 @@ static const unsigned char hantro_jpeg_header[JPEG_HEADER_SIZE] = { 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00, }; +static unsigned char jpeg_scale_qp(const unsigned char qp, int scale) +{ + unsigned int temp; + + temp = DIV_ROUND_CLOSEST((unsigned int)qp * scale, 100); + if (temp <= 0) + temp = 1; + if (temp > 255) + temp = 255; + + return (unsigned char)temp; +} + static void -jpeg_scale_quant_table(unsigned char *q_tab, +jpeg_scale_quant_table(unsigned char *file_q_tab, + unsigned char *reordered_q_tab, const unsigned char *tab, int scale) { - unsigned int temp; int i; for (i = 0; i < 64; i++) { - temp = DIV_ROUND_CLOSEST((unsigned int)tab[i] * scale, 100); - if (temp <= 0) - temp = 1; - if (temp > 255) - temp = 255; - q_tab[i] = (unsigned char)temp; + file_q_tab[i] = jpeg_scale_qp(tab[zigzag[i]], scale); + reordered_q_tab[i] = jpeg_scale_qp(tab[hw_reorder[i]], scale); } } @@ -256,17 +291,18 @@ static void jpeg_set_quality(unsigned char *buffer, int quality) scale = 200 - 2 * quality; jpeg_scale_quant_table(buffer + LUMA_QUANT_OFF, + luma_q_table_reordered, luma_q_table, scale); jpeg_scale_quant_table(buffer + CHROMA_QUANT_OFF, + chroma_q_table_reordered, chroma_q_table, scale); } -unsigned char * -hantro_jpeg_get_qtable(struct hantro_jpeg_ctx *ctx, int index) +unsigned char *hantro_jpeg_get_qtable(int index) { if (index == 0) - return ctx->buffer + LUMA_QUANT_OFF; - return ctx->buffer + CHROMA_QUANT_OFF; + return luma_q_table_reordered; + return chroma_q_table_reordered; } void hantro_jpeg_header_assemble(struct hantro_jpeg_ctx *ctx) diff --git a/drivers/staging/media/hantro/hantro_jpeg.h b/drivers/staging/media/hantro/hantro_jpeg.h index 9e8397c71388..9474a00277f8 100644 --- a/drivers/staging/media/hantro/hantro_jpeg.h +++ b/drivers/staging/media/hantro/hantro_jpeg.h @@ -9,5 +9,5 @@ struct hantro_jpeg_ctx { unsigned char *buffer; }; -unsigned char *hantro_jpeg_get_qtable(struct hantro_jpeg_ctx *ctx, int index); +unsigned char *hantro_jpeg_get_qtable(int index); void hantro_jpeg_header_assemble(struct hantro_jpeg_ctx *ctx); diff --git a/drivers/staging/media/hantro/hantro_v4l2.c b/drivers/staging/media/hantro/hantro_v4l2.c index 0198bcda26b7..f4ae2cee0f18 100644 --- a/drivers/staging/media/hantro/hantro_v4l2.c +++ b/drivers/staging/media/hantro/hantro_v4l2.c @@ -295,7 +295,7 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f, * +---------------------------+ */ if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_H264_SLICE && - !hantro_needs_postproc(ctx, ctx->vpu_dst_fmt)) + !hantro_needs_postproc(ctx, fmt)) pix_mp->plane_fmt[0].sizeimage += 64 * MB_WIDTH(pix_mp->width) * MB_WIDTH(pix_mp->height) + 32; diff --git a/drivers/staging/media/hantro/imx8m_vpu_hw.c b/drivers/staging/media/hantro/imx8m_vpu_hw.c new file mode 100644 index 000000000000..cb2420c5526e --- /dev/null +++ b/drivers/staging/media/hantro/imx8m_vpu_hw.c @@ -0,0 +1,220 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Hantro VPU codec driver + * + * Copyright (C) 2019 Pengutronix, Philipp Zabel <kernel@pengutronix.de> + */ + +#include <linux/clk.h> +#include <linux/delay.h> + +#include "hantro.h" +#include "hantro_jpeg.h" +#include "hantro_g1_regs.h" + +#define CTRL_SOFT_RESET 0x00 +#define RESET_G1 BIT(1) +#define RESET_G2 BIT(0) + +#define CTRL_CLOCK_ENABLE 0x04 +#define CLOCK_G1 BIT(1) +#define CLOCK_G2 BIT(0) + +#define CTRL_G1_DEC_FUSE 0x08 +#define CTRL_G1_PP_FUSE 0x0c +#define CTRL_G2_DEC_FUSE 0x10 + +static void imx8m_soft_reset(struct hantro_dev *vpu, u32 reset_bits) +{ + u32 val; + + /* Assert */ + val = readl(vpu->ctrl_base + CTRL_SOFT_RESET); + val &= ~reset_bits; + writel(val, vpu->ctrl_base + CTRL_SOFT_RESET); + + udelay(2); + + /* Release */ + val = readl(vpu->ctrl_base + CTRL_SOFT_RESET); + val |= reset_bits; + writel(val, vpu->ctrl_base + CTRL_SOFT_RESET); +} + +static void imx8m_clk_enable(struct hantro_dev *vpu, u32 clock_bits) +{ + u32 val; + + val = readl(vpu->ctrl_base + CTRL_CLOCK_ENABLE); + val |= clock_bits; + writel(val, vpu->ctrl_base + CTRL_CLOCK_ENABLE); +} + +static int imx8mq_runtime_resume(struct hantro_dev *vpu) +{ + int ret; + + ret = clk_bulk_prepare_enable(vpu->variant->num_clocks, vpu->clocks); + if (ret) { + dev_err(vpu->dev, "Failed to enable clocks\n"); + return ret; + } + + imx8m_soft_reset(vpu, RESET_G1 | RESET_G2); + imx8m_clk_enable(vpu, CLOCK_G1 | CLOCK_G2); + + /* Set values of the fuse registers */ + writel(0xffffffff, vpu->ctrl_base + CTRL_G1_DEC_FUSE); + writel(0xffffffff, vpu->ctrl_base + CTRL_G1_PP_FUSE); + writel(0xffffffff, vpu->ctrl_base + CTRL_G2_DEC_FUSE); + + clk_bulk_disable_unprepare(vpu->variant->num_clocks, vpu->clocks); + + return 0; +} + +/* + * Supported formats. + */ + +static const struct hantro_fmt imx8m_vpu_postproc_fmts[] = { + { + .fourcc = V4L2_PIX_FMT_YUYV, + .codec_mode = HANTRO_MODE_NONE, + }, +}; + +static const struct hantro_fmt imx8m_vpu_dec_fmts[] = { + { + .fourcc = V4L2_PIX_FMT_NV12, + .codec_mode = HANTRO_MODE_NONE, + }, + { + .fourcc = V4L2_PIX_FMT_MPEG2_SLICE, + .codec_mode = HANTRO_MODE_MPEG2_DEC, + .max_depth = 2, + .frmsize = { + .min_width = 48, + .max_width = 1920, + .step_width = MB_DIM, + .min_height = 48, + .max_height = 1088, + .step_height = MB_DIM, + }, + }, + { + .fourcc = V4L2_PIX_FMT_VP8_FRAME, + .codec_mode = HANTRO_MODE_VP8_DEC, + .max_depth = 2, + .frmsize = { + .min_width = 48, + .max_width = 3840, + .step_width = 16, + .min_height = 48, + .max_height = 2160, + .step_height = 16, + }, + }, + { + .fourcc = V4L2_PIX_FMT_H264_SLICE, + .codec_mode = HANTRO_MODE_H264_DEC, + .max_depth = 2, + .frmsize = { + .min_width = 48, + .max_width = 3840, + .step_width = MB_DIM, + .min_height = 48, + .max_height = 2160, + .step_height = MB_DIM, + }, + }, +}; + +static irqreturn_t imx8m_vpu_g1_irq(int irq, void *dev_id) +{ + struct hantro_dev *vpu = dev_id; + enum vb2_buffer_state state; + u32 status; + + status = vdpu_read(vpu, G1_REG_INTERRUPT); + state = (status & G1_REG_INTERRUPT_DEC_RDY_INT) ? + VB2_BUF_STATE_DONE : VB2_BUF_STATE_ERROR; + + vdpu_write(vpu, 0, G1_REG_INTERRUPT); + vdpu_write(vpu, G1_REG_CONFIG_DEC_CLK_GATE_E, G1_REG_CONFIG); + + hantro_irq_done(vpu, 0, state); + + return IRQ_HANDLED; +} + +static int imx8mq_vpu_hw_init(struct hantro_dev *vpu) +{ + vpu->dec_base = vpu->reg_bases[0]; + vpu->ctrl_base = vpu->reg_bases[vpu->variant->num_regs - 1]; + + return 0; +} + +static void imx8m_vpu_g1_reset(struct hantro_ctx *ctx) +{ + struct hantro_dev *vpu = ctx->dev; + + imx8m_soft_reset(vpu, RESET_G1); +} + +/* + * Supported codec ops. + */ + +static const struct hantro_codec_ops imx8mq_vpu_codec_ops[] = { + [HANTRO_MODE_MPEG2_DEC] = { + .run = hantro_g1_mpeg2_dec_run, + .reset = imx8m_vpu_g1_reset, + .init = hantro_mpeg2_dec_init, + .exit = hantro_mpeg2_dec_exit, + }, + [HANTRO_MODE_VP8_DEC] = { + .run = hantro_g1_vp8_dec_run, + .reset = imx8m_vpu_g1_reset, + .init = hantro_vp8_dec_init, + .exit = hantro_vp8_dec_exit, + }, + [HANTRO_MODE_H264_DEC] = { + .run = hantro_g1_h264_dec_run, + .reset = imx8m_vpu_g1_reset, + .init = hantro_h264_dec_init, + .exit = hantro_h264_dec_exit, + }, +}; + +/* + * VPU variants. + */ + +static const struct hantro_irq imx8mq_irqs[] = { + { "g1", imx8m_vpu_g1_irq }, + { "g2", NULL /* TODO: imx8m_vpu_g2_irq */ }, +}; + +static const char * const imx8mq_clk_names[] = { "g1", "g2", "bus" }; +static const char * const imx8mq_reg_names[] = { "g1", "g2", "ctrl" }; + +const struct hantro_variant imx8mq_vpu_variant = { + .dec_fmts = imx8m_vpu_dec_fmts, + .num_dec_fmts = ARRAY_SIZE(imx8m_vpu_dec_fmts), + .postproc_fmts = imx8m_vpu_postproc_fmts, + .num_postproc_fmts = ARRAY_SIZE(imx8m_vpu_postproc_fmts), + .postproc_regs = &hantro_g1_postproc_regs, + .codec = HANTRO_MPEG2_DECODER | HANTRO_VP8_DECODER | + HANTRO_H264_DECODER, + .codec_ops = imx8mq_vpu_codec_ops, + .init = imx8mq_vpu_hw_init, + .runtime_resume = imx8mq_runtime_resume, + .irqs = imx8mq_irqs, + .num_irqs = ARRAY_SIZE(imx8mq_irqs), + .clk_names = imx8mq_clk_names, + .num_clocks = ARRAY_SIZE(imx8mq_clk_names), + .reg_names = imx8mq_reg_names, + .num_regs = ARRAY_SIZE(imx8mq_reg_names) +}; diff --git a/drivers/staging/media/hantro/rk3399_vpu_hw_jpeg_enc.c b/drivers/staging/media/hantro/rk3399_vpu_hw_jpeg_enc.c index 4c2d43fb6fd1..3498e6124acd 100644 --- a/drivers/staging/media/hantro/rk3399_vpu_hw_jpeg_enc.c +++ b/drivers/staging/media/hantro/rk3399_vpu_hw_jpeg_enc.c @@ -18,9 +18,8 @@ * * Quantization luma table values are written to registers * VEPU_swreg_0-VEPU_swreg_15, and chroma table values to - * VEPU_swreg_16-VEPU_swreg_31. - * - * JPEG zigzag order is expected on the quantization tables. + * VEPU_swreg_16-VEPU_swreg_31. A special order is needed, neither + * zigzag, nor linear. */ #include <asm/unaligned.h> @@ -98,12 +97,23 @@ rk3399_vpu_jpeg_enc_set_qtable(struct hantro_dev *vpu, unsigned char *chroma_qtable) { u32 reg, i; + __be32 *luma_qtable_p; + __be32 *chroma_qtable_p; + + luma_qtable_p = (__be32 *)luma_qtable; + chroma_qtable_p = (__be32 *)chroma_qtable; + /* + * Quantization table registers must be written in contiguous blocks. + * DO NOT collapse the below two "for" loops into one. + */ for (i = 0; i < VEPU_JPEG_QUANT_TABLE_COUNT; i++) { - reg = get_unaligned_be32(&luma_qtable[i]); + reg = get_unaligned_be32(&luma_qtable_p[i]); vepu_write_relaxed(vpu, reg, VEPU_REG_JPEG_LUMA_QUAT(i)); + } - reg = get_unaligned_be32(&chroma_qtable[i]); + for (i = 0; i < VEPU_JPEG_QUANT_TABLE_COUNT; i++) { + reg = get_unaligned_be32(&chroma_qtable_p[i]); vepu_write_relaxed(vpu, reg, VEPU_REG_JPEG_CHROMA_QUAT(i)); } } @@ -134,8 +144,8 @@ void rk3399_vpu_jpeg_enc_run(struct hantro_ctx *ctx) rk3399_vpu_set_src_img_ctrl(vpu, ctx); rk3399_vpu_jpeg_enc_set_buffers(vpu, ctx, &src_buf->vb2_buf); rk3399_vpu_jpeg_enc_set_qtable(vpu, - hantro_jpeg_get_qtable(&jpeg_ctx, 0), - hantro_jpeg_get_qtable(&jpeg_ctx, 1)); + hantro_jpeg_get_qtable(0), + hantro_jpeg_get_qtable(1)); reg = VEPU_REG_OUTPUT_SWAP32 | VEPU_REG_OUTPUT_SWAP16 diff --git a/drivers/staging/media/imx/imx-media-capture.c b/drivers/staging/media/imx/imx-media-capture.c index 7712e7be8625..d37b776ff86d 100644 --- a/drivers/staging/media/imx/imx-media-capture.c +++ b/drivers/staging/media/imx/imx-media-capture.c @@ -643,7 +643,7 @@ static int capture_open(struct file *file) if (ret) v4l2_err(priv->src_sd, "v4l2_fh_open failed\n"); - ret = v4l2_pipeline_pm_use(&vfd->entity, 1); + ret = v4l2_pipeline_pm_get(&vfd->entity); if (ret) v4l2_fh_release(file); @@ -664,7 +664,7 @@ static int capture_release(struct file *file) vq->owner = NULL; } - v4l2_pipeline_pm_use(&vfd->entity, 0); + v4l2_pipeline_pm_put(&vfd->entity); v4l2_fh_release(file); mutex_unlock(&priv->mutex); @@ -742,7 +742,7 @@ int imx_media_capture_device_register(struct imx_media_video_dev *vdev) vfd->v4l2_dev = v4l2_dev; - ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1); if (ret) { v4l2_err(sd, "Failed to register video device\n"); return ret; @@ -778,7 +778,7 @@ int imx_media_capture_device_register(struct imx_media_video_dev *vdev) /* setup default format */ fmt_src.pad = priv->src_sd_pad; fmt_src.which = V4L2_SUBDEV_FORMAT_ACTIVE; - v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt_src); + ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt_src); if (ret) { v4l2_err(sd, "failed to get src_sd format\n"); goto unreg; diff --git a/drivers/staging/media/imx/imx-media-csc-scaler.c b/drivers/staging/media/imx/imx-media-csc-scaler.c index 2b635ebf62d6..2cc77f6e84b6 100644 --- a/drivers/staging/media/imx/imx-media-csc-scaler.c +++ b/drivers/staging/media/imx/imx-media-csc-scaler.c @@ -849,7 +849,7 @@ int imx_media_csc_scaler_device_register(struct imx_media_video_dev *vdev) vfd->v4l2_dev = &priv->md->v4l2_dev; - ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1); if (ret) { v4l2_err(vfd->v4l2_dev, "Failed to register video device\n"); return ret; diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index b60ed4f22f6d..e76a6a85baa3 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -457,7 +457,8 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) case V4L2_PIX_FMT_SGBRG16: case V4L2_PIX_FMT_SGRBG16: case V4L2_PIX_FMT_SRGGB16: - case V4L2_PIX_FMT_Y16: + case V4L2_PIX_FMT_Y10: + case V4L2_PIX_FMT_Y12: burst_size = 8; passthrough_bits = 16; break; @@ -1459,6 +1460,8 @@ static void csi_try_fmt(struct csi_priv *priv, /* propagate colorimetry from sink */ sdformat->format.colorspace = infmt->colorspace; sdformat->format.xfer_func = infmt->xfer_func; + sdformat->format.quantization = infmt->quantization; + sdformat->format.ycbcr_enc = infmt->ycbcr_enc; break; case CSI_SINK_PAD: diff --git a/drivers/staging/media/imx/imx-media-utils.c b/drivers/staging/media/imx/imx-media-utils.c index 0788a1874557..fae981698c49 100644 --- a/drivers/staging/media/imx/imx-media-utils.c +++ b/drivers/staging/media/imx/imx-media-utils.c @@ -161,17 +161,24 @@ static const struct imx_media_pixfmt rgb_formats[] = { .bayer = true, }, { .fourcc = V4L2_PIX_FMT_GREY, - .codes = {MEDIA_BUS_FMT_Y8_1X8}, - .cs = IPUV3_COLORSPACE_RGB, - .bpp = 8, - .bayer = true, - }, { - .fourcc = V4L2_PIX_FMT_Y16, .codes = { + MEDIA_BUS_FMT_Y8_1X8, MEDIA_BUS_FMT_Y10_1X10, MEDIA_BUS_FMT_Y12_1X12, }, .cs = IPUV3_COLORSPACE_RGB, + .bpp = 8, + .bayer = true, + }, { + .fourcc = V4L2_PIX_FMT_Y10, + .codes = {MEDIA_BUS_FMT_Y10_1X10}, + .cs = IPUV3_COLORSPACE_RGB, + .bpp = 16, + .bayer = true, + }, { + .fourcc = V4L2_PIX_FMT_Y12, + .codes = {MEDIA_BUS_FMT_Y12_1X12}, + .cs = IPUV3_COLORSPACE_RGB, .bpp = 16, .bayer = true, }, diff --git a/drivers/staging/media/imx/imx7-media-csi.c b/drivers/staging/media/imx/imx7-media-csi.c index db30e2c70f2f..acbdffb77668 100644 --- a/drivers/staging/media/imx/imx7-media-csi.c +++ b/drivers/staging/media/imx/imx7-media-csi.c @@ -292,7 +292,7 @@ static void imx7_csi_hw_disable(struct imx7_csi *csi) static void imx7_csi_dma_reflash(struct imx7_csi *csi) { - u32 cr3 = imx7_csi_reg_read(csi, CSI_CSICR18); + u32 cr3; cr3 = imx7_csi_reg_read(csi, CSI_CSICR3); cr3 |= BIT_DMA_REFLASH_RFF; @@ -804,6 +804,22 @@ static int imx7_csi_configure(struct imx7_csi *csi) case V4L2_PIX_FMT_YUYV: cr18 |= BIT_MIPI_DATA_FORMAT_YUV422_8B; break; + case V4L2_PIX_FMT_GREY: + if (in_code == MEDIA_BUS_FMT_Y8_1X8) + cr18 |= BIT_MIPI_DATA_FORMAT_RAW8; + else if (in_code == MEDIA_BUS_FMT_Y10_1X10) + cr18 |= BIT_MIPI_DATA_FORMAT_RAW10; + else + cr18 |= BIT_MIPI_DATA_FORMAT_RAW12; + break; + case V4L2_PIX_FMT_Y10: + cr18 |= BIT_MIPI_DATA_FORMAT_RAW10; + cr1 |= BIT_PIXEL_BIT; + break; + case V4L2_PIX_FMT_Y12: + cr18 |= BIT_MIPI_DATA_FORMAT_RAW12; + cr1 |= BIT_PIXEL_BIT; + break; case V4L2_PIX_FMT_SBGGR8: cr18 |= BIT_MIPI_DATA_FORMAT_RAW8; break; @@ -1009,10 +1025,13 @@ static int imx7_csi_try_fmt(struct imx7_csi *csi, sdformat->format.width = in_fmt->width; sdformat->format.height = in_fmt->height; sdformat->format.code = in_fmt->code; + sdformat->format.field = in_fmt->field; *cc = in_cc; sdformat->format.colorspace = in_fmt->colorspace; sdformat->format.xfer_func = in_fmt->xfer_func; + sdformat->format.quantization = in_fmt->quantization; + sdformat->format.ycbcr_enc = in_fmt->ycbcr_enc; break; case IMX7_CSI_PAD_SINK: *cc = imx_media_find_mbus_format(sdformat->format.code, @@ -1023,6 +1042,9 @@ static int imx7_csi_try_fmt(struct imx7_csi *csi, false); sdformat->format.code = (*cc)->codes[0]; } + + if (sdformat->format.field != V4L2_FIELD_INTERLACED) + sdformat->format.field = V4L2_FIELD_NONE; break; default: return -EINVAL; diff --git a/drivers/staging/media/imx/imx7-mipi-csis.c b/drivers/staging/media/imx/imx7-mipi-csis.c index 383abecb3bec..de17a1d22873 100644 --- a/drivers/staging/media/imx/imx7-mipi-csis.c +++ b/drivers/staging/media/imx/imx7-mipi-csis.c @@ -280,6 +280,18 @@ static const struct csis_pix_format mipi_csis_formats[] = { .code = MEDIA_BUS_FMT_YUYV8_2X8, .fmt_reg = MIPI_CSIS_ISPCFG_FMT_YCBCR422_8BIT, .data_alignment = 16, + }, { + .code = MEDIA_BUS_FMT_Y8_1X8, + .fmt_reg = MIPI_CSIS_ISPCFG_FMT_RAW8, + .data_alignment = 8, + }, { + .code = MEDIA_BUS_FMT_Y10_1X10, + .fmt_reg = MIPI_CSIS_ISPCFG_FMT_RAW10, + .data_alignment = 16, + }, { + .code = MEDIA_BUS_FMT_Y12_1X12, + .fmt_reg = MIPI_CSIS_ISPCFG_FMT_RAW12, + .data_alignment = 16, } }; @@ -301,6 +313,7 @@ static int mipi_csis_dump_regs(struct csi_state *state) { 0x20, "DPHYSTS" }, { 0x10, "INTMSK" }, { 0x40, "CONFIG_CH0" }, + { 0x44, "RESOL_CH0" }, { 0xC0, "DBG_CONFIG" }, { 0x38, "DPHYSLAVE_L" }, { 0x3C, "DPHYSLAVE_H" }, @@ -417,6 +430,7 @@ static void mipi_csis_set_params(struct csi_state *state) val = mipi_csis_read(state, MIPI_CSIS_CMN_CTRL); val &= ~MIPI_CSIS_CMN_CTRL_LANE_NR_MASK; val |= (lanes - 1) << MIPI_CSIS_CMN_CTRL_LANE_NR_OFFSET; + val |= MIPI_CSIS_CMN_CTRL_INTER_MODE; mipi_csis_write(state, MIPI_CSIS_CMN_CTRL, val); __mipi_csis_set_format(state); @@ -577,7 +591,7 @@ static int mipi_csis_s_stream(struct v4l2_subdev *mipi_sd, int enable) state->flags |= ST_STREAMING; } else { v4l2_subdev_call(state->src_sd, video, s_stream, 0); - ret = v4l2_subdev_call(state->src_sd, core, s_power, 1); + ret = v4l2_subdev_call(state->src_sd, core, s_power, 0); mipi_csis_stop_stream(state); state->flags &= ~ST_STREAMING; if (state->debug) diff --git a/drivers/staging/media/ipu3/TODO b/drivers/staging/media/ipu3/TODO index dc356d6f03c4..52063b651810 100644 --- a/drivers/staging/media/ipu3/TODO +++ b/drivers/staging/media/ipu3/TODO @@ -13,8 +13,6 @@ staging directory. - Elaborate the functionality of different selection rectangles in driver documentation. This may require driver changes as well. -- More detailed documentation on calculating BDS, GCD etc. sizes needed. - - Document different operation modes, and which buffer queues are relevant in each mode. To process an image, which queues require a buffer an in which ones is it optional? diff --git a/drivers/staging/media/ipu3/ipu3-css.c b/drivers/staging/media/ipu3/ipu3-css.c index f36de501edc6..4f04fe838b0c 100644 --- a/drivers/staging/media/ipu3/ipu3-css.c +++ b/drivers/staging/media/ipu3/ipu3-css.c @@ -210,12 +210,12 @@ static int imgu_hw_wait(void __iomem *base, int reg, u32 mask, u32 cmp) /* Initialize the IPU3 CSS hardware and associated h/w blocks */ -int imgu_css_set_powerup(struct device *dev, void __iomem *base) +int imgu_css_set_powerup(struct device *dev, void __iomem *base, + unsigned int freq) { - static const unsigned int freq = 450; u32 pm_ctrl, state, val; - dev_dbg(dev, "%s\n", __func__); + dev_dbg(dev, "%s with freq %u\n", __func__, freq); /* Clear the CSS busy signal */ readl(base + IMGU_REG_GP_BUSY); writel(0, base + IMGU_REG_GP_BUSY); diff --git a/drivers/staging/media/ipu3/ipu3-css.h b/drivers/staging/media/ipu3/ipu3-css.h index 6b8bab27ab1f..6108a068b228 100644 --- a/drivers/staging/media/ipu3/ipu3-css.h +++ b/drivers/staging/media/ipu3/ipu3-css.h @@ -187,7 +187,8 @@ bool imgu_css_is_streaming(struct imgu_css *css); bool imgu_css_pipe_queue_empty(struct imgu_css *css, unsigned int pipe); /******************* css hw *******************/ -int imgu_css_set_powerup(struct device *dev, void __iomem *base); +int imgu_css_set_powerup(struct device *dev, void __iomem *base, + unsigned int freq); void imgu_css_set_powerdown(struct device *dev, void __iomem *base); int imgu_css_irq_ack(struct imgu_css *css); diff --git a/drivers/staging/media/ipu3/ipu3-mmu.c b/drivers/staging/media/ipu3/ipu3-mmu.c index 3d969b0522ab..5f3ff964f3e7 100644 --- a/drivers/staging/media/ipu3/ipu3-mmu.c +++ b/drivers/staging/media/ipu3/ipu3-mmu.c @@ -130,7 +130,7 @@ static u32 *imgu_mmu_alloc_page_table(u32 pteval) for (pte = 0; pte < IPU3_PT_PTES; pte++) pt[pte] = pteval; - set_memory_uc((unsigned long int)pt, IPU3_PT_ORDER); + set_memory_uc((unsigned long)pt, IPU3_PT_ORDER); return pt; } @@ -141,7 +141,7 @@ static u32 *imgu_mmu_alloc_page_table(u32 pteval) */ static void imgu_mmu_free_page_table(u32 *pt) { - set_memory_wb((unsigned long int)pt, IPU3_PT_ORDER); + set_memory_wb((unsigned long)pt, IPU3_PT_ORDER); free_page((unsigned long)pt); } diff --git a/drivers/staging/media/ipu3/ipu3-v4l2.c b/drivers/staging/media/ipu3/ipu3-v4l2.c index 569e27b824c8..09c8ede1457c 100644 --- a/drivers/staging/media/ipu3/ipu3-v4l2.c +++ b/drivers/staging/media/ipu3/ipu3-v4l2.c @@ -1245,7 +1245,7 @@ static int imgu_v4l2_node_setup(struct imgu_device *imgu, unsigned int pipe, vdev->queue = &node->vbq; vdev->vfl_dir = node->output ? VFL_DIR_TX : VFL_DIR_RX; video_set_drvdata(vdev, imgu); - r = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + r = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (r) { dev_err(dev, "failed to register video device (%d)", r); media_entity_cleanup(&vdev->entity); diff --git a/drivers/staging/media/ipu3/ipu3.c b/drivers/staging/media/ipu3/ipu3.c index 06a61f31ca50..4d53aad31483 100644 --- a/drivers/staging/media/ipu3/ipu3.c +++ b/drivers/staging/media/ipu3/ipu3.c @@ -345,8 +345,20 @@ failed: static int imgu_powerup(struct imgu_device *imgu) { int r; + unsigned int pipe; + unsigned int freq = 200; + struct v4l2_mbus_framefmt *fmt; + + /* input larger than 2048*1152, ask imgu to work on high freq */ + for_each_set_bit(pipe, imgu->css.enabled_pipes, IMGU_MAX_PIPE_NUM) { + fmt = &imgu->imgu_pipe[pipe].nodes[IMGU_NODE_IN].pad_fmt; + dev_dbg(&imgu->pci_dev->dev, "pipe %u input format = %ux%u", + pipe, fmt->width, fmt->height); + if ((fmt->width * fmt->height) >= (2048 * 1152)) + freq = 450; + } - r = imgu_css_set_powerup(&imgu->pci_dev->dev, imgu->base); + r = imgu_css_set_powerup(&imgu->pci_dev->dev, imgu->base, freq); if (r) return r; @@ -666,7 +678,7 @@ static int imgu_pci_probe(struct pci_dev *pci_dev, atomic_set(&imgu->qbuf_barrier, 0); init_waitqueue_head(&imgu->buf_drain_wq); - r = imgu_css_set_powerup(&pci_dev->dev, imgu->base); + r = imgu_css_set_powerup(&pci_dev->dev, imgu->base, 200); if (r) { dev_err(&pci_dev->dev, "failed to power up CSS (%d)\n", r); diff --git a/drivers/staging/media/meson/vdec/Makefile b/drivers/staging/media/meson/vdec/Makefile index 6bea129084b7..6e726af84ac9 100644 --- a/drivers/staging/media/meson/vdec/Makefile +++ b/drivers/staging/media/meson/vdec/Makefile @@ -2,7 +2,7 @@ # Makefile for Amlogic meson video decoder driver meson-vdec-objs = esparser.o vdec.o vdec_helpers.o vdec_platform.o -meson-vdec-objs += vdec_1.o -meson-vdec-objs += codec_mpeg12.o +meson-vdec-objs += vdec_1.o vdec_hevc.o +meson-vdec-objs += codec_mpeg12.o codec_h264.o codec_hevc_common.o codec_vp9.o obj-$(CONFIG_VIDEO_MESON_VDEC) += meson-vdec.o diff --git a/drivers/staging/media/meson/vdec/codec_h264.c b/drivers/staging/media/meson/vdec/codec_h264.c new file mode 100644 index 000000000000..c61128fc4bb9 --- /dev/null +++ b/drivers/staging/media/meson/vdec/codec_h264.c @@ -0,0 +1,485 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019 BayLibre, SAS + * Author: Maxime Jourdan <mjourdan@baylibre.com> + */ + +#include <media/v4l2-mem2mem.h> +#include <media/videobuf2-dma-contig.h> + +#include "vdec_helpers.h" +#include "dos_regs.h" +#include "codec_h264.h" + +#define SIZE_EXT_FW (20 * SZ_1K) +#define SIZE_WORKSPACE 0x1ee000 +#define SIZE_SEI (8 * SZ_1K) + +/* + * Offset added by the firmware which must be substracted + * from the workspace phyaddr + */ +#define WORKSPACE_BUF_OFFSET 0x1000000 + +/* ISR status */ +#define CMD_MASK GENMASK(7, 0) +#define CMD_SRC_CHANGE 1 +#define CMD_FRAMES_READY 2 +#define CMD_FATAL_ERROR 6 +#define CMD_BAD_WIDTH 7 +#define CMD_BAD_HEIGHT 8 + +#define SEI_DATA_READY BIT(15) + +/* Picture type */ +#define PIC_TOP_BOT 5 +#define PIC_BOT_TOP 6 + +/* Size of Motion Vector per macroblock */ +#define MB_MV_SIZE 96 + +/* Frame status data */ +#define PIC_STRUCT_BIT 5 +#define PIC_STRUCT_MASK GENMASK(2, 0) +#define BUF_IDX_MASK GENMASK(4, 0) +#define ERROR_FLAG BIT(9) +#define OFFSET_BIT 16 +#define OFFSET_MASK GENMASK(15, 0) + +/* Bitstream parsed data */ +#define MB_TOTAL_BIT 8 +#define MB_TOTAL_MASK GENMASK(15, 0) +#define MB_WIDTH_MASK GENMASK(7, 0) +#define MAX_REF_BIT 24 +#define MAX_REF_MASK GENMASK(6, 0) +#define AR_IDC_BIT 16 +#define AR_IDC_MASK GENMASK(7, 0) +#define AR_PRESENT_FLAG BIT(0) +#define AR_EXTEND 0xff + +/* + * Buffer to send to the ESPARSER to signal End Of Stream for H.264. + * This is a 16x16 encoded picture that will trigger drain firmware-side. + * There is no known alternative. + */ +static const u8 eos_sequence[SZ_4K] = { + 0x00, 0x00, 0x00, 0x01, 0x06, 0x05, 0xff, 0xe4, 0xdc, 0x45, 0xe9, 0xbd, + 0xe6, 0xd9, 0x48, 0xb7, 0x96, 0x2c, 0xd8, 0x20, 0xd9, 0x23, 0xee, 0xef, + 0x78, 0x32, 0x36, 0x34, 0x20, 0x2d, 0x20, 0x63, 0x6f, 0x72, 0x65, 0x20, + 0x36, 0x37, 0x20, 0x72, 0x31, 0x31, 0x33, 0x30, 0x20, 0x38, 0x34, 0x37, + 0x35, 0x39, 0x37, 0x37, 0x20, 0x2d, 0x20, 0x48, 0x2e, 0x32, 0x36, 0x34, + 0x2f, 0x4d, 0x50, 0x45, 0x47, 0x2d, 0x34, 0x20, 0x41, 0x56, 0x43, 0x20, + 0x63, 0x6f, 0x64, 0x65, 0x63, 0x20, 0x2d, 0x20, 0x43, 0x6f, 0x70, 0x79, + 0x6c, 0x65, 0x66, 0x74, 0x20, 0x32, 0x30, 0x30, 0x33, 0x2d, 0x32, 0x30, + 0x30, 0x39, 0x20, 0x2d, 0x20, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, + 0x77, 0x77, 0x77, 0x2e, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x6c, 0x61, 0x6e, + 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x78, 0x32, 0x36, 0x34, 0x2e, 0x68, 0x74, + 0x6d, 0x6c, 0x20, 0x2d, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x3a, 0x20, 0x63, 0x61, 0x62, 0x61, 0x63, 0x3d, 0x31, 0x20, 0x72, 0x65, + 0x66, 0x3d, 0x31, 0x20, 0x64, 0x65, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x3d, + 0x31, 0x3a, 0x30, 0x3a, 0x30, 0x20, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x73, + 0x65, 0x3d, 0x30, 0x78, 0x31, 0x3a, 0x30, 0x78, 0x31, 0x31, 0x31, 0x20, + 0x6d, 0x65, 0x3d, 0x68, 0x65, 0x78, 0x20, 0x73, 0x75, 0x62, 0x6d, 0x65, + 0x3d, 0x36, 0x20, 0x70, 0x73, 0x79, 0x5f, 0x72, 0x64, 0x3d, 0x31, 0x2e, + 0x30, 0x3a, 0x30, 0x2e, 0x30, 0x20, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x5f, + 0x72, 0x65, 0x66, 0x3d, 0x30, 0x20, 0x6d, 0x65, 0x5f, 0x72, 0x61, 0x6e, + 0x67, 0x65, 0x3d, 0x31, 0x36, 0x20, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x61, + 0x5f, 0x6d, 0x65, 0x3d, 0x31, 0x20, 0x74, 0x72, 0x65, 0x6c, 0x6c, 0x69, + 0x73, 0x3d, 0x30, 0x20, 0x38, 0x78, 0x38, 0x64, 0x63, 0x74, 0x3d, 0x30, + 0x20, 0x63, 0x71, 0x6d, 0x3d, 0x30, 0x20, 0x64, 0x65, 0x61, 0x64, 0x7a, + 0x6f, 0x6e, 0x65, 0x3d, 0x32, 0x31, 0x2c, 0x31, 0x31, 0x20, 0x63, 0x68, + 0x72, 0x6f, 0x6d, 0x61, 0x5f, 0x71, 0x70, 0x5f, 0x6f, 0x66, 0x66, 0x73, + 0x65, 0x74, 0x3d, 0x2d, 0x32, 0x20, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, + 0x73, 0x3d, 0x31, 0x20, 0x6e, 0x72, 0x3d, 0x30, 0x20, 0x64, 0x65, 0x63, + 0x69, 0x6d, 0x61, 0x74, 0x65, 0x3d, 0x31, 0x20, 0x6d, 0x62, 0x61, 0x66, + 0x66, 0x3d, 0x30, 0x20, 0x62, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x3d, + 0x30, 0x20, 0x6b, 0x65, 0x79, 0x69, 0x6e, 0x74, 0x3d, 0x32, 0x35, 0x30, + 0x20, 0x6b, 0x65, 0x79, 0x69, 0x6e, 0x74, 0x5f, 0x6d, 0x69, 0x6e, 0x3d, + 0x32, 0x35, 0x20, 0x73, 0x63, 0x65, 0x6e, 0x65, 0x63, 0x75, 0x74, 0x3d, + 0x34, 0x30, 0x20, 0x72, 0x63, 0x3d, 0x61, 0x62, 0x72, 0x20, 0x62, 0x69, + 0x74, 0x72, 0x61, 0x74, 0x65, 0x3d, 0x31, 0x30, 0x20, 0x72, 0x61, 0x74, + 0x65, 0x74, 0x6f, 0x6c, 0x3d, 0x31, 0x2e, 0x30, 0x20, 0x71, 0x63, 0x6f, + 0x6d, 0x70, 0x3d, 0x30, 0x2e, 0x36, 0x30, 0x20, 0x71, 0x70, 0x6d, 0x69, + 0x6e, 0x3d, 0x31, 0x30, 0x20, 0x71, 0x70, 0x6d, 0x61, 0x78, 0x3d, 0x35, + 0x31, 0x20, 0x71, 0x70, 0x73, 0x74, 0x65, 0x70, 0x3d, 0x34, 0x20, 0x69, + 0x70, 0x5f, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x3d, 0x31, 0x2e, 0x34, 0x30, + 0x20, 0x61, 0x71, 0x3d, 0x31, 0x3a, 0x31, 0x2e, 0x30, 0x30, 0x00, 0x80, + 0x00, 0x00, 0x00, 0x01, 0x67, 0x4d, 0x40, 0x0a, 0x9a, 0x74, 0xf4, 0x20, + 0x00, 0x00, 0x03, 0x00, 0x20, 0x00, 0x00, 0x06, 0x51, 0xe2, 0x44, 0xd4, + 0x00, 0x00, 0x00, 0x01, 0x68, 0xee, 0x32, 0xc8, 0x00, 0x00, 0x00, 0x01, + 0x65, 0x88, 0x80, 0x20, 0x00, 0x08, 0x7f, 0xea, 0x6a, 0xe2, 0x99, 0xb6, + 0x57, 0xae, 0x49, 0x30, 0xf5, 0xfe, 0x5e, 0x46, 0x0b, 0x72, 0x44, 0xc4, + 0xe1, 0xfc, 0x62, 0xda, 0xf1, 0xfb, 0xa2, 0xdb, 0xd6, 0xbe, 0x5c, 0xd7, + 0x24, 0xa3, 0xf5, 0xb9, 0x2f, 0x57, 0x16, 0x49, 0x75, 0x47, 0x77, 0x09, + 0x5c, 0xa1, 0xb4, 0xc3, 0x4f, 0x60, 0x2b, 0xb0, 0x0c, 0xc8, 0xd6, 0x66, + 0xba, 0x9b, 0x82, 0x29, 0x33, 0x92, 0x26, 0x99, 0x31, 0x1c, 0x7f, 0x9b, + 0x00, 0x00, 0x01, 0x0ff, +}; + +static const u8 *codec_h264_eos_sequence(u32 *len) +{ + *len = ARRAY_SIZE(eos_sequence); + return eos_sequence; +} + +struct codec_h264 { + /* H.264 decoder requires an extended firmware */ + void *ext_fw_vaddr; + dma_addr_t ext_fw_paddr; + + /* Buffer for the H.264 Workspace */ + void *workspace_vaddr; + dma_addr_t workspace_paddr; + + /* Buffer for the H.264 references MV */ + void *ref_vaddr; + dma_addr_t ref_paddr; + u32 ref_size; + + /* Buffer for parsed SEI data */ + void *sei_vaddr; + dma_addr_t sei_paddr; + + u32 mb_width; + u32 mb_height; + u32 max_refs; +}; + +static int codec_h264_can_recycle(struct amvdec_core *core) +{ + return !amvdec_read_dos(core, AV_SCRATCH_7) || + !amvdec_read_dos(core, AV_SCRATCH_8); +} + +static void codec_h264_recycle(struct amvdec_core *core, u32 buf_idx) +{ + /* + * Tell the firmware it can recycle this buffer. + * AV_SCRATCH_8 serves the same purpose. + */ + if (!amvdec_read_dos(core, AV_SCRATCH_7)) + amvdec_write_dos(core, AV_SCRATCH_7, buf_idx + 1); + else + amvdec_write_dos(core, AV_SCRATCH_8, buf_idx + 1); +} + +static int codec_h264_start(struct amvdec_session *sess) +{ + u32 workspace_offset; + struct amvdec_core *core = sess->core; + struct codec_h264 *h264 = sess->priv; + + /* Allocate some memory for the H.264 decoder's state */ + h264->workspace_vaddr = + dma_alloc_coherent(core->dev, SIZE_WORKSPACE, + &h264->workspace_paddr, GFP_KERNEL); + if (!h264->workspace_vaddr) + return -ENOMEM; + + /* Allocate some memory for the H.264 SEI dump */ + h264->sei_vaddr = dma_alloc_coherent(core->dev, SIZE_SEI, + &h264->sei_paddr, GFP_KERNEL); + if (!h264->sei_vaddr) + return -ENOMEM; + + amvdec_write_dos_bits(core, POWER_CTL_VLD, BIT(9) | BIT(6)); + + workspace_offset = h264->workspace_paddr - WORKSPACE_BUF_OFFSET; + amvdec_write_dos(core, AV_SCRATCH_1, workspace_offset); + amvdec_write_dos(core, AV_SCRATCH_G, h264->ext_fw_paddr); + amvdec_write_dos(core, AV_SCRATCH_I, h264->sei_paddr - + workspace_offset); + + /* Enable "error correction" */ + amvdec_write_dos(core, AV_SCRATCH_F, + (amvdec_read_dos(core, AV_SCRATCH_F) & 0xffffffc3) | + BIT(4) | BIT(7)); + + amvdec_write_dos(core, MDEC_PIC_DC_THRESH, 0x404038aa); + + return 0; +} + +static int codec_h264_stop(struct amvdec_session *sess) +{ + struct codec_h264 *h264 = sess->priv; + struct amvdec_core *core = sess->core; + + if (h264->ext_fw_vaddr) + dma_free_coherent(core->dev, SIZE_EXT_FW, + h264->ext_fw_vaddr, h264->ext_fw_paddr); + + if (h264->workspace_vaddr) + dma_free_coherent(core->dev, SIZE_WORKSPACE, + h264->workspace_vaddr, h264->workspace_paddr); + + if (h264->ref_vaddr) + dma_free_coherent(core->dev, h264->ref_size, + h264->ref_vaddr, h264->ref_paddr); + + if (h264->sei_vaddr) + dma_free_coherent(core->dev, SIZE_SEI, + h264->sei_vaddr, h264->sei_paddr); + + return 0; +} + +static int codec_h264_load_extended_firmware(struct amvdec_session *sess, + const u8 *data, u32 len) +{ + struct codec_h264 *h264; + struct amvdec_core *core = sess->core; + + if (len < SIZE_EXT_FW) + return -EINVAL; + + h264 = kzalloc(sizeof(*h264), GFP_KERNEL); + if (!h264) + return -ENOMEM; + + h264->ext_fw_vaddr = dma_alloc_coherent(core->dev, SIZE_EXT_FW, + &h264->ext_fw_paddr, + GFP_KERNEL); + if (!h264->ext_fw_vaddr) { + kfree(h264); + return -ENOMEM; + } + + memcpy(h264->ext_fw_vaddr, data, SIZE_EXT_FW); + sess->priv = h264; + + return 0; +} + +static const struct v4l2_fract par_table[] = { + { 1, 1 }, { 1, 1 }, { 12, 11 }, { 10, 11 }, + { 16, 11 }, { 40, 33 }, { 24, 11 }, { 20, 11 }, + { 32, 11 }, { 80, 33 }, { 18, 11 }, { 15, 11 }, + { 64, 33 }, { 160, 99 }, { 4, 3 }, { 3, 2 }, + { 2, 1 } +}; + +static void codec_h264_set_par(struct amvdec_session *sess) +{ + struct amvdec_core *core = sess->core; + u32 seq_info = amvdec_read_dos(core, AV_SCRATCH_2); + u32 ar_idc = (seq_info >> AR_IDC_BIT) & AR_IDC_MASK; + + if (!(seq_info & AR_PRESENT_FLAG)) + return; + + if (ar_idc == AR_EXTEND) { + u32 ar_info = amvdec_read_dos(core, AV_SCRATCH_3); + + sess->pixelaspect.numerator = ar_info & 0xffff; + sess->pixelaspect.denominator = (ar_info >> 16) & 0xffff; + return; + } + + if (ar_idc >= ARRAY_SIZE(par_table)) + return; + + sess->pixelaspect = par_table[ar_idc]; +} + +static void codec_h264_resume(struct amvdec_session *sess) +{ + struct amvdec_core *core = sess->core; + struct codec_h264 *h264 = sess->priv; + u32 mb_width, mb_height, mb_total; + + amvdec_set_canvases(sess, + (u32[]){ ANC0_CANVAS_ADDR, 0 }, + (u32[]){ 24, 0 }); + + dev_dbg(core->dev, "max_refs = %u; actual_dpb_size = %u\n", + h264->max_refs, sess->num_dst_bufs); + + /* Align to a multiple of 4 macroblocks */ + mb_width = ALIGN(h264->mb_width, 4); + mb_height = ALIGN(h264->mb_height, 4); + mb_total = mb_width * mb_height; + + h264->ref_size = mb_total * MB_MV_SIZE * h264->max_refs; + h264->ref_vaddr = dma_alloc_coherent(core->dev, h264->ref_size, + &h264->ref_paddr, GFP_KERNEL); + if (!h264->ref_vaddr) { + amvdec_abort(sess); + return; + } + + /* Address to store the references' MVs */ + amvdec_write_dos(core, AV_SCRATCH_1, h264->ref_paddr); + /* End of ref MV */ + amvdec_write_dos(core, AV_SCRATCH_4, h264->ref_paddr + h264->ref_size); + + amvdec_write_dos(core, AV_SCRATCH_0, (h264->max_refs << 24) | + (sess->num_dst_bufs << 16) | + ((h264->max_refs - 1) << 8)); +} + +/* + * Configure the H.264 decoder when the parser detected a parameter set change + */ +static void codec_h264_src_change(struct amvdec_session *sess) +{ + struct amvdec_core *core = sess->core; + struct codec_h264 *h264 = sess->priv; + u32 parsed_info, mb_total; + u32 crop_infor, crop_bottom, crop_right; + u32 frame_width, frame_height; + + sess->keyframe_found = 1; + + parsed_info = amvdec_read_dos(core, AV_SCRATCH_1); + + /* Total number of 16x16 macroblocks */ + mb_total = (parsed_info >> MB_TOTAL_BIT) & MB_TOTAL_MASK; + /* Number of macroblocks per line */ + h264->mb_width = parsed_info & MB_WIDTH_MASK; + /* Number of macroblock lines */ + h264->mb_height = mb_total / h264->mb_width; + + h264->max_refs = ((parsed_info >> MAX_REF_BIT) & MAX_REF_MASK) + 1; + + crop_infor = amvdec_read_dos(core, AV_SCRATCH_6); + crop_bottom = (crop_infor & 0xff); + crop_right = (crop_infor >> 16) & 0xff; + + frame_width = h264->mb_width * 16 - crop_right; + frame_height = h264->mb_height * 16 - crop_bottom; + + dev_dbg(core->dev, "frame: %ux%u; crop: %u %u\n", + frame_width, frame_height, crop_right, crop_bottom); + + codec_h264_set_par(sess); + amvdec_src_change(sess, frame_width, frame_height, h264->max_refs + 5); +} + +/* + * The bitstream offset is split in half in 2 different registers. + * Fetch its MSB here, which location depends on the frame number. + */ +static u32 get_offset_msb(struct amvdec_core *core, int frame_num) +{ + int take_msb = frame_num % 2; + int reg_offset = (frame_num / 2) * 4; + u32 offset_msb = amvdec_read_dos(core, AV_SCRATCH_A + reg_offset); + + if (take_msb) + return offset_msb & 0xffff0000; + + return (offset_msb & 0x0000ffff) << 16; +} + +static void codec_h264_frames_ready(struct amvdec_session *sess, u32 status) +{ + struct amvdec_core *core = sess->core; + int error_count; + int num_frames; + int i; + + error_count = amvdec_read_dos(core, AV_SCRATCH_D); + num_frames = (status >> 8) & 0xff; + if (error_count) { + dev_warn(core->dev, + "decoder error(s) happened, count %d\n", error_count); + amvdec_write_dos(core, AV_SCRATCH_D, 0); + } + + for (i = 0; i < num_frames; i++) { + u32 frame_status = amvdec_read_dos(core, AV_SCRATCH_1 + i * 4); + u32 buffer_index = frame_status & BUF_IDX_MASK; + u32 pic_struct = (frame_status >> PIC_STRUCT_BIT) & + PIC_STRUCT_MASK; + u32 offset = (frame_status >> OFFSET_BIT) & OFFSET_MASK; + u32 field = V4L2_FIELD_NONE; + + /* + * A buffer decode error means it was decoded, + * but part of the picture will have artifacts. + * Typical reason is a temporarily corrupted bitstream + */ + if (frame_status & ERROR_FLAG) + dev_dbg(core->dev, "Buffer %d decode error\n", + buffer_index); + + if (pic_struct == PIC_TOP_BOT) + field = V4L2_FIELD_INTERLACED_TB; + else if (pic_struct == PIC_BOT_TOP) + field = V4L2_FIELD_INTERLACED_BT; + + offset |= get_offset_msb(core, i); + amvdec_dst_buf_done_idx(sess, buffer_index, offset, field); + } +} + +static irqreturn_t codec_h264_threaded_isr(struct amvdec_session *sess) +{ + struct amvdec_core *core = sess->core; + u32 status; + u32 size; + u8 cmd; + + status = amvdec_read_dos(core, AV_SCRATCH_0); + cmd = status & CMD_MASK; + + switch (cmd) { + case CMD_SRC_CHANGE: + codec_h264_src_change(sess); + break; + case CMD_FRAMES_READY: + codec_h264_frames_ready(sess, status); + break; + case CMD_FATAL_ERROR: + dev_err(core->dev, "H.264 decoder fatal error\n"); + goto abort; + case CMD_BAD_WIDTH: + size = (amvdec_read_dos(core, AV_SCRATCH_1) + 1) * 16; + dev_err(core->dev, "Unsupported video width: %u\n", size); + goto abort; + case CMD_BAD_HEIGHT: + size = (amvdec_read_dos(core, AV_SCRATCH_1) + 1) * 16; + dev_err(core->dev, "Unsupported video height: %u\n", size); + goto abort; + case 0: /* Unused but not worth printing for */ + case 9: + break; + default: + dev_info(core->dev, "Unexpected H264 ISR: %08X\n", cmd); + break; + } + + if (cmd && cmd != CMD_SRC_CHANGE) + amvdec_write_dos(core, AV_SCRATCH_0, 0); + + /* Decoder has some SEI data for us ; ignore */ + if (amvdec_read_dos(core, AV_SCRATCH_J) & SEI_DATA_READY) + amvdec_write_dos(core, AV_SCRATCH_J, 0); + + return IRQ_HANDLED; +abort: + amvdec_abort(sess); + return IRQ_HANDLED; +} + +static irqreturn_t codec_h264_isr(struct amvdec_session *sess) +{ + struct amvdec_core *core = sess->core; + + amvdec_write_dos(core, ASSIST_MBOX1_CLR_REG, 1); + + return IRQ_WAKE_THREAD; +} + +struct amvdec_codec_ops codec_h264_ops = { + .start = codec_h264_start, + .stop = codec_h264_stop, + .load_extended_firmware = codec_h264_load_extended_firmware, + .isr = codec_h264_isr, + .threaded_isr = codec_h264_threaded_isr, + .can_recycle = codec_h264_can_recycle, + .recycle = codec_h264_recycle, + .eos_sequence = codec_h264_eos_sequence, + .resume = codec_h264_resume, +}; diff --git a/drivers/staging/media/meson/vdec/codec_h264.h b/drivers/staging/media/meson/vdec/codec_h264.h new file mode 100644 index 000000000000..7cb4fb86ff36 --- /dev/null +++ b/drivers/staging/media/meson/vdec/codec_h264.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2019 BayLibre, SAS + * Author: Maxime Jourdan <mjourdan@baylibre.com> + */ + +#ifndef __MESON_VDEC_CODEC_H264_H_ +#define __MESON_VDEC_CODEC_H264_H_ + +#include "vdec.h" + +extern struct amvdec_codec_ops codec_h264_ops; + +#endif diff --git a/drivers/staging/media/meson/vdec/codec_hevc_common.c b/drivers/staging/media/meson/vdec/codec_hevc_common.c new file mode 100644 index 000000000000..0315cc0911cd --- /dev/null +++ b/drivers/staging/media/meson/vdec/codec_hevc_common.c @@ -0,0 +1,297 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2018 Maxime Jourdan <mjourdan@baylibre.com> + */ + +#include <media/v4l2-mem2mem.h> +#include <media/videobuf2-dma-contig.h> + +#include "codec_hevc_common.h" +#include "vdec_helpers.h" +#include "hevc_regs.h" + +#define MMU_COMPRESS_HEADER_SIZE 0x48000 +#define MMU_MAP_SIZE 0x4800 + +const u16 vdec_hevc_parser_cmd[] = { + 0x0401, 0x8401, 0x0800, 0x0402, + 0x9002, 0x1423, 0x8CC3, 0x1423, + 0x8804, 0x9825, 0x0800, 0x04FE, + 0x8406, 0x8411, 0x1800, 0x8408, + 0x8409, 0x8C2A, 0x9C2B, 0x1C00, + 0x840F, 0x8407, 0x8000, 0x8408, + 0x2000, 0xA800, 0x8410, 0x04DE, + 0x840C, 0x840D, 0xAC00, 0xA000, + 0x08C0, 0x08E0, 0xA40E, 0xFC00, + 0x7C00 +}; + +/* Configure decode head read mode */ +void codec_hevc_setup_decode_head(struct amvdec_session *sess, int is_10bit) +{ + struct amvdec_core *core = sess->core; + u32 body_size = amvdec_am21c_body_size(sess->width, sess->height); + u32 head_size = amvdec_am21c_head_size(sess->width, sess->height); + + if (!codec_hevc_use_fbc(sess->pixfmt_cap, is_10bit)) { + /* Enable 2-plane reference read mode */ + amvdec_write_dos(core, HEVCD_MPP_DECOMP_CTL1, BIT(31)); + return; + } + + if (codec_hevc_use_mmu(core->platform->revision, + sess->pixfmt_cap, is_10bit)) + amvdec_write_dos(core, HEVCD_MPP_DECOMP_CTL1, BIT(4)); + else + amvdec_write_dos(core, HEVCD_MPP_DECOMP_CTL1, 0); + + if (core->platform->revision < VDEC_REVISION_SM1) + amvdec_write_dos(core, HEVCD_MPP_DECOMP_CTL2, body_size / 32); + amvdec_write_dos(core, HEVC_CM_BODY_LENGTH, body_size); + amvdec_write_dos(core, HEVC_CM_HEADER_OFFSET, body_size); + amvdec_write_dos(core, HEVC_CM_HEADER_LENGTH, head_size); +} +EXPORT_SYMBOL_GPL(codec_hevc_setup_decode_head); + +static void codec_hevc_setup_buffers_gxbb(struct amvdec_session *sess, + struct codec_hevc_common *comm, + int is_10bit) +{ + struct amvdec_core *core = sess->core; + struct v4l2_m2m_buffer *buf; + u32 buf_num = v4l2_m2m_num_dst_bufs_ready(sess->m2m_ctx); + dma_addr_t buf_y_paddr = 0; + dma_addr_t buf_uv_paddr = 0; + u32 idx = 0; + u32 val; + int i; + + amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_CONF_ADDR, 0); + + v4l2_m2m_for_each_dst_buf(sess->m2m_ctx, buf) { + struct vb2_buffer *vb = &buf->vb.vb2_buf; + + idx = vb->index; + + if (codec_hevc_use_downsample(sess->pixfmt_cap, is_10bit)) + buf_y_paddr = comm->fbc_buffer_paddr[idx]; + else + buf_y_paddr = vb2_dma_contig_plane_dma_addr(vb, 0); + + if (codec_hevc_use_fbc(sess->pixfmt_cap, is_10bit)) { + val = buf_y_paddr | (idx << 8) | 1; + amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_CMD_ADDR, + val); + } else { + buf_uv_paddr = vb2_dma_contig_plane_dma_addr(vb, 1); + val = buf_y_paddr | ((idx * 2) << 8) | 1; + amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_CMD_ADDR, + val); + val = buf_uv_paddr | ((idx * 2 + 1) << 8) | 1; + amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_CMD_ADDR, + val); + } + } + + if (codec_hevc_use_fbc(sess->pixfmt_cap, is_10bit)) + val = buf_y_paddr | (idx << 8) | 1; + else + val = buf_y_paddr | ((idx * 2) << 8) | 1; + + /* Fill the remaining unused slots with the last buffer's Y addr */ + for (i = buf_num; i < MAX_REF_PIC_NUM; ++i) + amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_CMD_ADDR, val); + + amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_CONF_ADDR, 1); + amvdec_write_dos(core, HEVCD_MPP_ANC_CANVAS_ACCCONFIG_ADDR, 1); + for (i = 0; i < 32; ++i) + amvdec_write_dos(core, HEVCD_MPP_ANC_CANVAS_DATA_ADDR, 0); +} + +static void codec_hevc_setup_buffers_gxl(struct amvdec_session *sess, + struct codec_hevc_common *comm, + int is_10bit) +{ + struct amvdec_core *core = sess->core; + struct v4l2_m2m_buffer *buf; + u32 revision = core->platform->revision; + u32 pixfmt_cap = sess->pixfmt_cap; + int i; + + amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_CONF_ADDR, + BIT(2) | BIT(1)); + + v4l2_m2m_for_each_dst_buf(sess->m2m_ctx, buf) { + struct vb2_buffer *vb = &buf->vb.vb2_buf; + dma_addr_t buf_y_paddr = 0; + dma_addr_t buf_uv_paddr = 0; + u32 idx = vb->index; + + if (codec_hevc_use_mmu(revision, pixfmt_cap, is_10bit)) + buf_y_paddr = comm->mmu_header_paddr[idx]; + else if (codec_hevc_use_downsample(pixfmt_cap, is_10bit)) + buf_y_paddr = comm->fbc_buffer_paddr[idx]; + else + buf_y_paddr = vb2_dma_contig_plane_dma_addr(vb, 0); + + amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_DATA, + buf_y_paddr >> 5); + + if (!codec_hevc_use_fbc(pixfmt_cap, is_10bit)) { + buf_uv_paddr = vb2_dma_contig_plane_dma_addr(vb, 1); + amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_DATA, + buf_uv_paddr >> 5); + } + } + + amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_CONF_ADDR, 1); + amvdec_write_dos(core, HEVCD_MPP_ANC_CANVAS_ACCCONFIG_ADDR, 1); + for (i = 0; i < 32; ++i) + amvdec_write_dos(core, HEVCD_MPP_ANC_CANVAS_DATA_ADDR, 0); +} + +void codec_hevc_free_fbc_buffers(struct amvdec_session *sess, + struct codec_hevc_common *comm) +{ + struct device *dev = sess->core->dev; + u32 am21_size = amvdec_am21c_size(sess->width, sess->height); + int i; + + for (i = 0; i < MAX_REF_PIC_NUM; ++i) { + if (comm->fbc_buffer_vaddr[i]) { + dma_free_coherent(dev, am21_size, + comm->fbc_buffer_vaddr[i], + comm->fbc_buffer_paddr[i]); + comm->fbc_buffer_vaddr[i] = NULL; + } + } +} +EXPORT_SYMBOL_GPL(codec_hevc_free_fbc_buffers); + +static int codec_hevc_alloc_fbc_buffers(struct amvdec_session *sess, + struct codec_hevc_common *comm) +{ + struct device *dev = sess->core->dev; + struct v4l2_m2m_buffer *buf; + u32 am21_size = amvdec_am21c_size(sess->width, sess->height); + + v4l2_m2m_for_each_dst_buf(sess->m2m_ctx, buf) { + u32 idx = buf->vb.vb2_buf.index; + dma_addr_t paddr; + void *vaddr = dma_alloc_coherent(dev, am21_size, &paddr, + GFP_KERNEL); + if (!vaddr) { + codec_hevc_free_fbc_buffers(sess, comm); + return -ENOMEM; + } + + comm->fbc_buffer_vaddr[idx] = vaddr; + comm->fbc_buffer_paddr[idx] = paddr; + } + + return 0; +} + +void codec_hevc_free_mmu_headers(struct amvdec_session *sess, + struct codec_hevc_common *comm) +{ + struct device *dev = sess->core->dev; + int i; + + for (i = 0; i < MAX_REF_PIC_NUM; ++i) { + if (comm->mmu_header_vaddr[i]) { + dma_free_coherent(dev, MMU_COMPRESS_HEADER_SIZE, + comm->mmu_header_vaddr[i], + comm->mmu_header_paddr[i]); + comm->mmu_header_vaddr[i] = NULL; + } + } + + if (comm->mmu_map_vaddr) { + dma_free_coherent(dev, MMU_MAP_SIZE, + comm->mmu_map_vaddr, + comm->mmu_map_paddr); + comm->mmu_map_vaddr = NULL; + } +} +EXPORT_SYMBOL_GPL(codec_hevc_free_mmu_headers); + +static int codec_hevc_alloc_mmu_headers(struct amvdec_session *sess, + struct codec_hevc_common *comm) +{ + struct device *dev = sess->core->dev; + struct v4l2_m2m_buffer *buf; + + comm->mmu_map_vaddr = dma_alloc_coherent(dev, MMU_MAP_SIZE, + &comm->mmu_map_paddr, + GFP_KERNEL); + if (!comm->mmu_map_vaddr) + return -ENOMEM; + + v4l2_m2m_for_each_dst_buf(sess->m2m_ctx, buf) { + u32 idx = buf->vb.vb2_buf.index; + dma_addr_t paddr; + void *vaddr = dma_alloc_coherent(dev, MMU_COMPRESS_HEADER_SIZE, + &paddr, GFP_KERNEL); + if (!vaddr) { + codec_hevc_free_mmu_headers(sess, comm); + return -ENOMEM; + } + + comm->mmu_header_vaddr[idx] = vaddr; + comm->mmu_header_paddr[idx] = paddr; + } + + return 0; +} + +int codec_hevc_setup_buffers(struct amvdec_session *sess, + struct codec_hevc_common *comm, + int is_10bit) +{ + struct amvdec_core *core = sess->core; + int ret; + + if (codec_hevc_use_downsample(sess->pixfmt_cap, is_10bit)) { + ret = codec_hevc_alloc_fbc_buffers(sess, comm); + if (ret) + return ret; + } + + if (codec_hevc_use_mmu(core->platform->revision, + sess->pixfmt_cap, is_10bit)) { + ret = codec_hevc_alloc_mmu_headers(sess, comm); + if (ret) { + codec_hevc_free_fbc_buffers(sess, comm); + return ret; + } + } + + if (core->platform->revision == VDEC_REVISION_GXBB) + codec_hevc_setup_buffers_gxbb(sess, comm, is_10bit); + else + codec_hevc_setup_buffers_gxl(sess, comm, is_10bit); + + return 0; +} +EXPORT_SYMBOL_GPL(codec_hevc_setup_buffers); + +void codec_hevc_fill_mmu_map(struct amvdec_session *sess, + struct codec_hevc_common *comm, + struct vb2_buffer *vb) +{ + u32 size = amvdec_am21c_size(sess->width, sess->height); + u32 nb_pages = size / PAGE_SIZE; + u32 *mmu_map = comm->mmu_map_vaddr; + u32 first_page; + u32 i; + + if (sess->pixfmt_cap == V4L2_PIX_FMT_NV12M) + first_page = comm->fbc_buffer_paddr[vb->index] >> PAGE_SHIFT; + else + first_page = vb2_dma_contig_plane_dma_addr(vb, 0) >> PAGE_SHIFT; + + for (i = 0; i < nb_pages; ++i) + mmu_map[i] = first_page + i; +} +EXPORT_SYMBOL_GPL(codec_hevc_fill_mmu_map); diff --git a/drivers/staging/media/meson/vdec/codec_hevc_common.h b/drivers/staging/media/meson/vdec/codec_hevc_common.h new file mode 100644 index 000000000000..88e4379ba1ee --- /dev/null +++ b/drivers/staging/media/meson/vdec/codec_hevc_common.h @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2018 BayLibre, SAS + * Author: Maxime Jourdan <mjourdan@baylibre.com> + */ + +#ifndef __MESON_VDEC_HEVC_COMMON_H_ +#define __MESON_VDEC_HEVC_COMMON_H_ + +#include "vdec.h" + +#define PARSER_CMD_SKIP_CFG_0 0x0000090b +#define PARSER_CMD_SKIP_CFG_1 0x1b14140f +#define PARSER_CMD_SKIP_CFG_2 0x001b1910 + +#define VDEC_HEVC_PARSER_CMD_LEN 37 +extern const u16 vdec_hevc_parser_cmd[VDEC_HEVC_PARSER_CMD_LEN]; + +#define MAX_REF_PIC_NUM 24 + +struct codec_hevc_common { + void *fbc_buffer_vaddr[MAX_REF_PIC_NUM]; + dma_addr_t fbc_buffer_paddr[MAX_REF_PIC_NUM]; + + void *mmu_header_vaddr[MAX_REF_PIC_NUM]; + dma_addr_t mmu_header_paddr[MAX_REF_PIC_NUM]; + + void *mmu_map_vaddr; + dma_addr_t mmu_map_paddr; +}; + +/* Returns 1 if we must use framebuffer compression */ +static inline int codec_hevc_use_fbc(u32 pixfmt, int is_10bit) +{ + /* TOFIX: Handle Amlogic Compressed buffer for 8bit also */ + return is_10bit; +} + +/* Returns 1 if we are decoding 10-bit but outputting 8-bit NV12 */ +static inline int codec_hevc_use_downsample(u32 pixfmt, int is_10bit) +{ + return is_10bit; +} + +/* Returns 1 if we are decoding using the IOMMU */ +static inline int codec_hevc_use_mmu(u32 revision, u32 pixfmt, int is_10bit) +{ + return revision >= VDEC_REVISION_G12A && + codec_hevc_use_fbc(pixfmt, is_10bit); +} + +/** + * Configure decode head read mode + */ +void codec_hevc_setup_decode_head(struct amvdec_session *sess, int is_10bit); + +void codec_hevc_free_fbc_buffers(struct amvdec_session *sess, + struct codec_hevc_common *comm); + +void codec_hevc_free_mmu_headers(struct amvdec_session *sess, + struct codec_hevc_common *comm); + +int codec_hevc_setup_buffers(struct amvdec_session *sess, + struct codec_hevc_common *comm, + int is_10bit); + +void codec_hevc_fill_mmu_map(struct amvdec_session *sess, + struct codec_hevc_common *comm, + struct vb2_buffer *vb); + +#endif diff --git a/drivers/staging/media/meson/vdec/codec_vp9.c b/drivers/staging/media/meson/vdec/codec_vp9.c new file mode 100644 index 000000000000..60e4fc0052b3 --- /dev/null +++ b/drivers/staging/media/meson/vdec/codec_vp9.c @@ -0,0 +1,2141 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2018 Maxime Jourdan <mjourdan@baylibre.com> + * Copyright (C) 2015 Amlogic, Inc. All rights reserved. + */ + +#include <media/v4l2-mem2mem.h> +#include <media/videobuf2-dma-contig.h> + +#include "dos_regs.h" +#include "hevc_regs.h" +#include "codec_vp9.h" +#include "vdec_helpers.h" +#include "codec_hevc_common.h" + +/* HEVC reg mapping */ +#define VP9_DEC_STATUS_REG HEVC_ASSIST_SCRATCH_0 + #define VP9_10B_DECODE_SLICE 5 + #define VP9_HEAD_PARSER_DONE 0xf0 +#define VP9_RPM_BUFFER HEVC_ASSIST_SCRATCH_1 +#define VP9_SHORT_TERM_RPS HEVC_ASSIST_SCRATCH_2 +#define VP9_ADAPT_PROB_REG HEVC_ASSIST_SCRATCH_3 +#define VP9_MMU_MAP_BUFFER HEVC_ASSIST_SCRATCH_4 +#define VP9_PPS_BUFFER HEVC_ASSIST_SCRATCH_5 +#define VP9_SAO_UP HEVC_ASSIST_SCRATCH_6 +#define VP9_STREAM_SWAP_BUFFER HEVC_ASSIST_SCRATCH_7 +#define VP9_STREAM_SWAP_BUFFER2 HEVC_ASSIST_SCRATCH_8 +#define VP9_PROB_SWAP_BUFFER HEVC_ASSIST_SCRATCH_9 +#define VP9_COUNT_SWAP_BUFFER HEVC_ASSIST_SCRATCH_A +#define VP9_SEG_MAP_BUFFER HEVC_ASSIST_SCRATCH_B +#define VP9_SCALELUT HEVC_ASSIST_SCRATCH_D +#define VP9_WAIT_FLAG HEVC_ASSIST_SCRATCH_E +#define LMEM_DUMP_ADR HEVC_ASSIST_SCRATCH_F +#define NAL_SEARCH_CTL HEVC_ASSIST_SCRATCH_I +#define VP9_DECODE_MODE HEVC_ASSIST_SCRATCH_J + #define DECODE_MODE_SINGLE 0 +#define DECODE_STOP_POS HEVC_ASSIST_SCRATCH_K +#define HEVC_DECODE_COUNT HEVC_ASSIST_SCRATCH_M +#define HEVC_DECODE_SIZE HEVC_ASSIST_SCRATCH_N + +/* VP9 Constants */ +#define LCU_SIZE 64 +#define MAX_REF_PIC_NUM 24 +#define REFS_PER_FRAME 3 +#define REF_FRAMES 8 +#define MV_MEM_UNIT 0x240 +#define ADAPT_PROB_SIZE 0xf80 + +enum FRAME_TYPE { + KEY_FRAME = 0, + INTER_FRAME = 1, + FRAME_TYPES, +}; + +/* VP9 Workspace layout */ +#define MPRED_MV_BUF_SIZE 0x120000 + +#define IPP_SIZE 0x4000 +#define SAO_ABV_SIZE 0x30000 +#define SAO_VB_SIZE 0x30000 +#define SH_TM_RPS_SIZE 0x800 +#define VPS_SIZE 0x800 +#define SPS_SIZE 0x800 +#define PPS_SIZE 0x2000 +#define SAO_UP_SIZE 0x2800 +#define SWAP_BUF_SIZE 0x800 +#define SWAP_BUF2_SIZE 0x800 +#define SCALELUT_SIZE 0x8000 +#define DBLK_PARA_SIZE 0x80000 +#define DBLK_DATA_SIZE 0x80000 +#define SEG_MAP_SIZE 0xd800 +#define PROB_SIZE 0x5000 +#define COUNT_SIZE 0x3000 +#define MMU_VBH_SIZE 0x5000 +#define MPRED_ABV_SIZE 0x10000 +#define MPRED_MV_SIZE (MPRED_MV_BUF_SIZE * MAX_REF_PIC_NUM) +#define RPM_BUF_SIZE 0x100 +#define LMEM_SIZE 0x800 + +#define IPP_OFFSET 0x00 +#define SAO_ABV_OFFSET (IPP_OFFSET + IPP_SIZE) +#define SAO_VB_OFFSET (SAO_ABV_OFFSET + SAO_ABV_SIZE) +#define SH_TM_RPS_OFFSET (SAO_VB_OFFSET + SAO_VB_SIZE) +#define VPS_OFFSET (SH_TM_RPS_OFFSET + SH_TM_RPS_SIZE) +#define SPS_OFFSET (VPS_OFFSET + VPS_SIZE) +#define PPS_OFFSET (SPS_OFFSET + SPS_SIZE) +#define SAO_UP_OFFSET (PPS_OFFSET + PPS_SIZE) +#define SWAP_BUF_OFFSET (SAO_UP_OFFSET + SAO_UP_SIZE) +#define SWAP_BUF2_OFFSET (SWAP_BUF_OFFSET + SWAP_BUF_SIZE) +#define SCALELUT_OFFSET (SWAP_BUF2_OFFSET + SWAP_BUF2_SIZE) +#define DBLK_PARA_OFFSET (SCALELUT_OFFSET + SCALELUT_SIZE) +#define DBLK_DATA_OFFSET (DBLK_PARA_OFFSET + DBLK_PARA_SIZE) +#define SEG_MAP_OFFSET (DBLK_DATA_OFFSET + DBLK_DATA_SIZE) +#define PROB_OFFSET (SEG_MAP_OFFSET + SEG_MAP_SIZE) +#define COUNT_OFFSET (PROB_OFFSET + PROB_SIZE) +#define MMU_VBH_OFFSET (COUNT_OFFSET + COUNT_SIZE) +#define MPRED_ABV_OFFSET (MMU_VBH_OFFSET + MMU_VBH_SIZE) +#define MPRED_MV_OFFSET (MPRED_ABV_OFFSET + MPRED_ABV_SIZE) +#define RPM_OFFSET (MPRED_MV_OFFSET + MPRED_MV_SIZE) +#define LMEM_OFFSET (RPM_OFFSET + RPM_BUF_SIZE) + +#define SIZE_WORKSPACE ALIGN(LMEM_OFFSET + LMEM_SIZE, 64 * SZ_1K) + +#define NONE -1 +#define INTRA_FRAME 0 +#define LAST_FRAME 1 +#define GOLDEN_FRAME 2 +#define ALTREF_FRAME 3 +#define MAX_REF_FRAMES 4 + +/* + * Defines, declarations, sub-functions for vp9 de-block loop + filter Thr/Lvl table update + * - struct segmentation is for loop filter only (removed something) + * - function "vp9_loop_filter_init" and "vp9_loop_filter_frame_init" will + be instantiated in C_Entry + * - vp9_loop_filter_init run once before decoding start + * - vp9_loop_filter_frame_init run before every frame decoding start + * - set video format to VP9 is in vp9_loop_filter_init + */ +#define MAX_LOOP_FILTER 63 +#define MAX_REF_LF_DELTAS 4 +#define MAX_MODE_LF_DELTAS 2 +#define SEGMENT_DELTADATA 0 +#define SEGMENT_ABSDATA 1 +#define MAX_SEGMENTS 8 + +/* VP9 PROB processing defines */ +#define VP9_PARTITION_START 0 +#define VP9_PARTITION_SIZE_STEP (3 * 4) +#define VP9_PARTITION_ONE_SIZE (4 * VP9_PARTITION_SIZE_STEP) +#define VP9_PARTITION_KEY_START 0 +#define VP9_PARTITION_P_START VP9_PARTITION_ONE_SIZE +#define VP9_PARTITION_SIZE (2 * VP9_PARTITION_ONE_SIZE) +#define VP9_SKIP_START (VP9_PARTITION_START + VP9_PARTITION_SIZE) +#define VP9_SKIP_SIZE 4 /* only use 3*/ +#define VP9_TX_MODE_START (VP9_SKIP_START + VP9_SKIP_SIZE) +#define VP9_TX_MODE_8_0_OFFSET 0 +#define VP9_TX_MODE_8_1_OFFSET 1 +#define VP9_TX_MODE_16_0_OFFSET 2 +#define VP9_TX_MODE_16_1_OFFSET 4 +#define VP9_TX_MODE_32_0_OFFSET 6 +#define VP9_TX_MODE_32_1_OFFSET 9 +#define VP9_TX_MODE_SIZE 12 +#define VP9_COEF_START (VP9_TX_MODE_START + VP9_TX_MODE_SIZE) +#define VP9_COEF_BAND_0_OFFSET 0 +#define VP9_COEF_BAND_1_OFFSET (VP9_COEF_BAND_0_OFFSET + 3 * 3 + 1) +#define VP9_COEF_BAND_2_OFFSET (VP9_COEF_BAND_1_OFFSET + 6 * 3) +#define VP9_COEF_BAND_3_OFFSET (VP9_COEF_BAND_2_OFFSET + 6 * 3) +#define VP9_COEF_BAND_4_OFFSET (VP9_COEF_BAND_3_OFFSET + 6 * 3) +#define VP9_COEF_BAND_5_OFFSET (VP9_COEF_BAND_4_OFFSET + 6 * 3) +#define VP9_COEF_SIZE_ONE_SET 100 /* ((3 + 5 * 6) * 3 + 1 padding)*/ +#define VP9_COEF_4X4_START (VP9_COEF_START + 0 * VP9_COEF_SIZE_ONE_SET) +#define VP9_COEF_8X8_START (VP9_COEF_START + 4 * VP9_COEF_SIZE_ONE_SET) +#define VP9_COEF_16X16_START (VP9_COEF_START + 8 * VP9_COEF_SIZE_ONE_SET) +#define VP9_COEF_32X32_START (VP9_COEF_START + 12 * VP9_COEF_SIZE_ONE_SET) +#define VP9_COEF_SIZE_PLANE (2 * VP9_COEF_SIZE_ONE_SET) +#define VP9_COEF_SIZE (4 * 2 * 2 * VP9_COEF_SIZE_ONE_SET) +#define VP9_INTER_MODE_START (VP9_COEF_START + VP9_COEF_SIZE) +#define VP9_INTER_MODE_SIZE 24 /* only use 21 (# * 7)*/ +#define VP9_INTERP_START (VP9_INTER_MODE_START + VP9_INTER_MODE_SIZE) +#define VP9_INTERP_SIZE 8 +#define VP9_INTRA_INTER_START (VP9_INTERP_START + VP9_INTERP_SIZE) +#define VP9_INTRA_INTER_SIZE 4 +#define VP9_INTERP_INTRA_INTER_START VP9_INTERP_START +#define VP9_INTERP_INTRA_INTER_SIZE (VP9_INTERP_SIZE + VP9_INTRA_INTER_SIZE) +#define VP9_COMP_INTER_START \ + (VP9_INTERP_INTRA_INTER_START + VP9_INTERP_INTRA_INTER_SIZE) +#define VP9_COMP_INTER_SIZE 5 +#define VP9_COMP_REF_START (VP9_COMP_INTER_START + VP9_COMP_INTER_SIZE) +#define VP9_COMP_REF_SIZE 5 +#define VP9_SINGLE_REF_START (VP9_COMP_REF_START + VP9_COMP_REF_SIZE) +#define VP9_SINGLE_REF_SIZE 10 +#define VP9_REF_MODE_START VP9_COMP_INTER_START +#define VP9_REF_MODE_SIZE \ + (VP9_COMP_INTER_SIZE + VP9_COMP_REF_SIZE + VP9_SINGLE_REF_SIZE) +#define VP9_IF_Y_MODE_START (VP9_REF_MODE_START + VP9_REF_MODE_SIZE) +#define VP9_IF_Y_MODE_SIZE 36 +#define VP9_IF_UV_MODE_START (VP9_IF_Y_MODE_START + VP9_IF_Y_MODE_SIZE) +#define VP9_IF_UV_MODE_SIZE 92 /* only use 90*/ +#define VP9_MV_JOINTS_START (VP9_IF_UV_MODE_START + VP9_IF_UV_MODE_SIZE) +#define VP9_MV_JOINTS_SIZE 3 +#define VP9_MV_SIGN_0_START (VP9_MV_JOINTS_START + VP9_MV_JOINTS_SIZE) +#define VP9_MV_SIGN_0_SIZE 1 +#define VP9_MV_CLASSES_0_START (VP9_MV_SIGN_0_START + VP9_MV_SIGN_0_SIZE) +#define VP9_MV_CLASSES_0_SIZE 10 +#define VP9_MV_CLASS0_0_START \ + (VP9_MV_CLASSES_0_START + VP9_MV_CLASSES_0_SIZE) +#define VP9_MV_CLASS0_0_SIZE 1 +#define VP9_MV_BITS_0_START (VP9_MV_CLASS0_0_START + VP9_MV_CLASS0_0_SIZE) +#define VP9_MV_BITS_0_SIZE 10 +#define VP9_MV_SIGN_1_START (VP9_MV_BITS_0_START + VP9_MV_BITS_0_SIZE) +#define VP9_MV_SIGN_1_SIZE 1 +#define VP9_MV_CLASSES_1_START \ + (VP9_MV_SIGN_1_START + VP9_MV_SIGN_1_SIZE) +#define VP9_MV_CLASSES_1_SIZE 10 +#define VP9_MV_CLASS0_1_START \ + (VP9_MV_CLASSES_1_START + VP9_MV_CLASSES_1_SIZE) +#define VP9_MV_CLASS0_1_SIZE 1 +#define VP9_MV_BITS_1_START \ + (VP9_MV_CLASS0_1_START + VP9_MV_CLASS0_1_SIZE) +#define VP9_MV_BITS_1_SIZE 10 +#define VP9_MV_CLASS0_FP_0_START \ + (VP9_MV_BITS_1_START + VP9_MV_BITS_1_SIZE) +#define VP9_MV_CLASS0_FP_0_SIZE 9 +#define VP9_MV_CLASS0_FP_1_START \ + (VP9_MV_CLASS0_FP_0_START + VP9_MV_CLASS0_FP_0_SIZE) +#define VP9_MV_CLASS0_FP_1_SIZE 9 +#define VP9_MV_CLASS0_HP_0_START \ + (VP9_MV_CLASS0_FP_1_START + VP9_MV_CLASS0_FP_1_SIZE) +#define VP9_MV_CLASS0_HP_0_SIZE 2 +#define VP9_MV_CLASS0_HP_1_START \ + (VP9_MV_CLASS0_HP_0_START + VP9_MV_CLASS0_HP_0_SIZE) +#define VP9_MV_CLASS0_HP_1_SIZE 2 +#define VP9_MV_START VP9_MV_JOINTS_START +#define VP9_MV_SIZE 72 /*only use 69*/ + +#define VP9_TOTAL_SIZE (VP9_MV_START + VP9_MV_SIZE) + +/* VP9 COUNT mem processing defines */ +#define VP9_COEF_COUNT_START 0 +#define VP9_COEF_COUNT_BAND_0_OFFSET 0 +#define VP9_COEF_COUNT_BAND_1_OFFSET \ + (VP9_COEF_COUNT_BAND_0_OFFSET + 3 * 5) +#define VP9_COEF_COUNT_BAND_2_OFFSET \ + (VP9_COEF_COUNT_BAND_1_OFFSET + 6 * 5) +#define VP9_COEF_COUNT_BAND_3_OFFSET \ + (VP9_COEF_COUNT_BAND_2_OFFSET + 6 * 5) +#define VP9_COEF_COUNT_BAND_4_OFFSET \ + (VP9_COEF_COUNT_BAND_3_OFFSET + 6 * 5) +#define VP9_COEF_COUNT_BAND_5_OFFSET \ + (VP9_COEF_COUNT_BAND_4_OFFSET + 6 * 5) +#define VP9_COEF_COUNT_SIZE_ONE_SET 165 /* ((3 + 5 * 6) * 5 */ +#define VP9_COEF_COUNT_4X4_START \ + (VP9_COEF_COUNT_START + 0 * VP9_COEF_COUNT_SIZE_ONE_SET) +#define VP9_COEF_COUNT_8X8_START \ + (VP9_COEF_COUNT_START + 4 * VP9_COEF_COUNT_SIZE_ONE_SET) +#define VP9_COEF_COUNT_16X16_START \ + (VP9_COEF_COUNT_START + 8 * VP9_COEF_COUNT_SIZE_ONE_SET) +#define VP9_COEF_COUNT_32X32_START \ + (VP9_COEF_COUNT_START + 12 * VP9_COEF_COUNT_SIZE_ONE_SET) +#define VP9_COEF_COUNT_SIZE_PLANE (2 * VP9_COEF_COUNT_SIZE_ONE_SET) +#define VP9_COEF_COUNT_SIZE (4 * 2 * 2 * VP9_COEF_COUNT_SIZE_ONE_SET) + +#define VP9_INTRA_INTER_COUNT_START \ + (VP9_COEF_COUNT_START + VP9_COEF_COUNT_SIZE) +#define VP9_INTRA_INTER_COUNT_SIZE (4 * 2) +#define VP9_COMP_INTER_COUNT_START \ + (VP9_INTRA_INTER_COUNT_START + VP9_INTRA_INTER_COUNT_SIZE) +#define VP9_COMP_INTER_COUNT_SIZE (5 * 2) +#define VP9_COMP_REF_COUNT_START \ + (VP9_COMP_INTER_COUNT_START + VP9_COMP_INTER_COUNT_SIZE) +#define VP9_COMP_REF_COUNT_SIZE (5 * 2) +#define VP9_SINGLE_REF_COUNT_START \ + (VP9_COMP_REF_COUNT_START + VP9_COMP_REF_COUNT_SIZE) +#define VP9_SINGLE_REF_COUNT_SIZE (10 * 2) +#define VP9_TX_MODE_COUNT_START \ + (VP9_SINGLE_REF_COUNT_START + VP9_SINGLE_REF_COUNT_SIZE) +#define VP9_TX_MODE_COUNT_SIZE (12 * 2) +#define VP9_SKIP_COUNT_START \ + (VP9_TX_MODE_COUNT_START + VP9_TX_MODE_COUNT_SIZE) +#define VP9_SKIP_COUNT_SIZE (3 * 2) +#define VP9_MV_SIGN_0_COUNT_START \ + (VP9_SKIP_COUNT_START + VP9_SKIP_COUNT_SIZE) +#define VP9_MV_SIGN_0_COUNT_SIZE (1 * 2) +#define VP9_MV_SIGN_1_COUNT_START \ + (VP9_MV_SIGN_0_COUNT_START + VP9_MV_SIGN_0_COUNT_SIZE) +#define VP9_MV_SIGN_1_COUNT_SIZE (1 * 2) +#define VP9_MV_BITS_0_COUNT_START \ + (VP9_MV_SIGN_1_COUNT_START + VP9_MV_SIGN_1_COUNT_SIZE) +#define VP9_MV_BITS_0_COUNT_SIZE (10 * 2) +#define VP9_MV_BITS_1_COUNT_START \ + (VP9_MV_BITS_0_COUNT_START + VP9_MV_BITS_0_COUNT_SIZE) +#define VP9_MV_BITS_1_COUNT_SIZE (10 * 2) +#define VP9_MV_CLASS0_HP_0_COUNT_START \ + (VP9_MV_BITS_1_COUNT_START + VP9_MV_BITS_1_COUNT_SIZE) +#define VP9_MV_CLASS0_HP_0_COUNT_SIZE (2 * 2) +#define VP9_MV_CLASS0_HP_1_COUNT_START \ + (VP9_MV_CLASS0_HP_0_COUNT_START + VP9_MV_CLASS0_HP_0_COUNT_SIZE) +#define VP9_MV_CLASS0_HP_1_COUNT_SIZE (2 * 2) + +/* Start merge_tree */ +#define VP9_INTER_MODE_COUNT_START \ + (VP9_MV_CLASS0_HP_1_COUNT_START + VP9_MV_CLASS0_HP_1_COUNT_SIZE) +#define VP9_INTER_MODE_COUNT_SIZE (7 * 4) +#define VP9_IF_Y_MODE_COUNT_START \ + (VP9_INTER_MODE_COUNT_START + VP9_INTER_MODE_COUNT_SIZE) +#define VP9_IF_Y_MODE_COUNT_SIZE (10 * 4) +#define VP9_IF_UV_MODE_COUNT_START \ + (VP9_IF_Y_MODE_COUNT_START + VP9_IF_Y_MODE_COUNT_SIZE) +#define VP9_IF_UV_MODE_COUNT_SIZE (10 * 10) +#define VP9_PARTITION_P_COUNT_START \ + (VP9_IF_UV_MODE_COUNT_START + VP9_IF_UV_MODE_COUNT_SIZE) +#define VP9_PARTITION_P_COUNT_SIZE (4 * 4 * 4) +#define VP9_INTERP_COUNT_START \ + (VP9_PARTITION_P_COUNT_START + VP9_PARTITION_P_COUNT_SIZE) +#define VP9_INTERP_COUNT_SIZE (4 * 3) +#define VP9_MV_JOINTS_COUNT_START \ + (VP9_INTERP_COUNT_START + VP9_INTERP_COUNT_SIZE) +#define VP9_MV_JOINTS_COUNT_SIZE (1 * 4) +#define VP9_MV_CLASSES_0_COUNT_START \ + (VP9_MV_JOINTS_COUNT_START + VP9_MV_JOINTS_COUNT_SIZE) +#define VP9_MV_CLASSES_0_COUNT_SIZE (1 * 11) +#define VP9_MV_CLASS0_0_COUNT_START \ + (VP9_MV_CLASSES_0_COUNT_START + VP9_MV_CLASSES_0_COUNT_SIZE) +#define VP9_MV_CLASS0_0_COUNT_SIZE (1 * 2) +#define VP9_MV_CLASSES_1_COUNT_START \ + (VP9_MV_CLASS0_0_COUNT_START + VP9_MV_CLASS0_0_COUNT_SIZE) +#define VP9_MV_CLASSES_1_COUNT_SIZE (1 * 11) +#define VP9_MV_CLASS0_1_COUNT_START \ + (VP9_MV_CLASSES_1_COUNT_START + VP9_MV_CLASSES_1_COUNT_SIZE) +#define VP9_MV_CLASS0_1_COUNT_SIZE (1 * 2) +#define VP9_MV_CLASS0_FP_0_COUNT_START \ + (VP9_MV_CLASS0_1_COUNT_START + VP9_MV_CLASS0_1_COUNT_SIZE) +#define VP9_MV_CLASS0_FP_0_COUNT_SIZE (3 * 4) +#define VP9_MV_CLASS0_FP_1_COUNT_START \ + (VP9_MV_CLASS0_FP_0_COUNT_START + VP9_MV_CLASS0_FP_0_COUNT_SIZE) +#define VP9_MV_CLASS0_FP_1_COUNT_SIZE (3 * 4) + +#define DC_PRED 0 /* Average of above and left pixels */ +#define V_PRED 1 /* Vertical */ +#define H_PRED 2 /* Horizontal */ +#define D45_PRED 3 /* Directional 45 deg = round(arctan(1/1) * 180/pi) */ +#define D135_PRED 4 /* Directional 135 deg = 180 - 45 */ +#define D117_PRED 5 /* Directional 117 deg = 180 - 63 */ +#define D153_PRED 6 /* Directional 153 deg = 180 - 27 */ +#define D207_PRED 7 /* Directional 207 deg = 180 + 27 */ +#define D63_PRED 8 /* Directional 63 deg = round(arctan(2/1) * 180/pi) */ +#define TM_PRED 9 /* True-motion */ + +/* Use a static inline to avoid possible side effect from num being reused */ +static inline int round_power_of_two(int value, int num) +{ + return (value + (1 << (num - 1))) >> num; +} + +#define MODE_MV_COUNT_SAT 20 +static const int count_to_update_factor[MODE_MV_COUNT_SAT + 1] = { + 0, 6, 12, 19, 25, 32, 38, 44, 51, 57, 64, + 70, 76, 83, 89, 96, 102, 108, 115, 121, 128 +}; + +union rpm_param { + struct { + u16 data[RPM_BUF_SIZE]; + } l; + struct { + u16 profile; + u16 show_existing_frame; + u16 frame_to_show_idx; + u16 frame_type; /*1 bit*/ + u16 show_frame; /*1 bit*/ + u16 error_resilient_mode; /*1 bit*/ + u16 intra_only; /*1 bit*/ + u16 display_size_present; /*1 bit*/ + u16 reset_frame_context; + u16 refresh_frame_flags; + u16 width; + u16 height; + u16 display_width; + u16 display_height; + u16 ref_info; + u16 same_frame_size; + u16 mode_ref_delta_enabled; + u16 ref_deltas[4]; + u16 mode_deltas[2]; + u16 filter_level; + u16 sharpness_level; + u16 bit_depth; + u16 seg_quant_info[8]; + u16 seg_enabled; + u16 seg_abs_delta; + /* bit 15: feature enabled; bit 8, sign; bit[5:0], data */ + u16 seg_lf_info[8]; + } p; +}; + +enum SEG_LVL_FEATURES { + SEG_LVL_ALT_Q = 0, /* Use alternate Quantizer */ + SEG_LVL_ALT_LF = 1, /* Use alternate loop filter value */ + SEG_LVL_REF_FRAME = 2, /* Optional Segment reference frame */ + SEG_LVL_SKIP = 3, /* Optional Segment (0,0) + skip mode */ + SEG_LVL_MAX = 4 /* Number of features supported */ +}; + +struct segmentation { + u8 enabled; + u8 update_map; + u8 update_data; + u8 abs_delta; + u8 temporal_update; + s16 feature_data[MAX_SEGMENTS][SEG_LVL_MAX]; + unsigned int feature_mask[MAX_SEGMENTS]; +}; + +struct loop_filter_thresh { + u8 mblim; + u8 lim; + u8 hev_thr; +}; + +struct loop_filter_info_n { + struct loop_filter_thresh lfthr[MAX_LOOP_FILTER + 1]; + u8 lvl[MAX_SEGMENTS][MAX_REF_FRAMES][MAX_MODE_LF_DELTAS]; +}; + +struct loopfilter { + int filter_level; + + int sharpness_level; + int last_sharpness_level; + + u8 mode_ref_delta_enabled; + u8 mode_ref_delta_update; + + /*0 = Intra, Last, GF, ARF*/ + signed char ref_deltas[MAX_REF_LF_DELTAS]; + signed char last_ref_deltas[MAX_REF_LF_DELTAS]; + + /*0 = ZERO_MV, MV*/ + signed char mode_deltas[MAX_MODE_LF_DELTAS]; + signed char last_mode_deltas[MAX_MODE_LF_DELTAS]; +}; + +struct vp9_frame { + struct list_head list; + struct vb2_v4l2_buffer *vbuf; + int index; + int intra_only; + int show; + int type; + int done; + unsigned int width; + unsigned int height; +}; + +struct codec_vp9 { + /* VP9 context lock */ + struct mutex lock; + + /* Common part with the HEVC decoder */ + struct codec_hevc_common common; + + /* Buffer for the VP9 Workspace */ + void *workspace_vaddr; + dma_addr_t workspace_paddr; + + /* Contains many information parsed from the bitstream */ + union rpm_param rpm_param; + + /* Whether we detected the bitstream as 10-bit */ + int is_10bit; + + /* Coded resolution reported by the hardware */ + u32 width, height; + + /* All ref frames used by the HW at a given time */ + struct list_head ref_frames_list; + u32 frames_num; + + /* In case of downsampling (decoding with FBC but outputting in NV12M), + * we need to allocate additional buffers for FBC. + */ + void *fbc_buffer_vaddr[MAX_REF_PIC_NUM]; + dma_addr_t fbc_buffer_paddr[MAX_REF_PIC_NUM]; + + int ref_frame_map[REF_FRAMES]; + int next_ref_frame_map[REF_FRAMES]; + struct vp9_frame *frame_refs[REFS_PER_FRAME]; + + u32 lcu_total; + + /* loop filter */ + int default_filt_lvl; + struct loop_filter_info_n lfi; + struct loopfilter lf; + struct segmentation seg_4lf; + + struct vp9_frame *cur_frame; + struct vp9_frame *prev_frame; +}; + +static int div_r32(s64 m, int n) +{ + s64 qu = div_s64(m, n); + + return (int)qu; +} + +static int clip_prob(int p) +{ + return clamp_val(p, 1, 255); +} + +static int segfeature_active(struct segmentation *seg, int segment_id, + enum SEG_LVL_FEATURES feature_id) +{ + return seg->enabled && + (seg->feature_mask[segment_id] & (1 << feature_id)); +} + +static int get_segdata(struct segmentation *seg, int segment_id, + enum SEG_LVL_FEATURES feature_id) +{ + return seg->feature_data[segment_id][feature_id]; +} + +static void vp9_update_sharpness(struct loop_filter_info_n *lfi, + int sharpness_lvl) +{ + int lvl; + + /* For each possible value for the loop filter fill out limits*/ + for (lvl = 0; lvl <= MAX_LOOP_FILTER; lvl++) { + /* Set loop filter parameters that control sharpness.*/ + int block_inside_limit = lvl >> ((sharpness_lvl > 0) + + (sharpness_lvl > 4)); + + if (sharpness_lvl > 0) { + if (block_inside_limit > (9 - sharpness_lvl)) + block_inside_limit = (9 - sharpness_lvl); + } + + if (block_inside_limit < 1) + block_inside_limit = 1; + + lfi->lfthr[lvl].lim = (u8)block_inside_limit; + lfi->lfthr[lvl].mblim = (u8)(2 * (lvl + 2) + + block_inside_limit); + } +} + +/* Instantiate this function once when decode is started */ +static void +vp9_loop_filter_init(struct amvdec_core *core, struct codec_vp9 *vp9) +{ + struct loop_filter_info_n *lfi = &vp9->lfi; + struct loopfilter *lf = &vp9->lf; + struct segmentation *seg_4lf = &vp9->seg_4lf; + int i; + + memset(lfi, 0, sizeof(struct loop_filter_info_n)); + memset(lf, 0, sizeof(struct loopfilter)); + memset(seg_4lf, 0, sizeof(struct segmentation)); + lf->sharpness_level = 0; + vp9_update_sharpness(lfi, lf->sharpness_level); + lf->last_sharpness_level = lf->sharpness_level; + + for (i = 0; i < 32; i++) { + unsigned int thr; + + thr = ((lfi->lfthr[i * 2 + 1].lim & 0x3f) << 8) | + (lfi->lfthr[i * 2 + 1].mblim & 0xff); + thr = (thr << 16) | ((lfi->lfthr[i * 2].lim & 0x3f) << 8) | + (lfi->lfthr[i * 2].mblim & 0xff); + + amvdec_write_dos(core, HEVC_DBLK_CFG9, thr); + } + + if (core->platform->revision >= VDEC_REVISION_SM1) + amvdec_write_dos(core, HEVC_DBLK_CFGB, + (0x3 << 14) | /* dw fifo thres r and b */ + (0x3 << 12) | /* dw fifo thres r or b */ + (0x3 << 10) | /* dw fifo thres not r/b */ + BIT(0)); /* VP9 video format */ + else if (core->platform->revision >= VDEC_REVISION_G12A) + /* VP9 video format */ + amvdec_write_dos(core, HEVC_DBLK_CFGB, (0x54 << 8) | BIT(0)); + else + amvdec_write_dos(core, HEVC_DBLK_CFGB, 0x40400001); +} + +static void +vp9_loop_filter_frame_init(struct amvdec_core *core, struct segmentation *seg, + struct loop_filter_info_n *lfi, + struct loopfilter *lf, int default_filt_lvl) +{ + int i; + int seg_id; + + /* + * n_shift is the multiplier for lf_deltas + * the multiplier is: + * - 1 for when filter_lvl is between 0 and 31 + * - 2 when filter_lvl is between 32 and 63 + */ + const int scale = 1 << (default_filt_lvl >> 5); + + /* update limits if sharpness has changed */ + if (lf->last_sharpness_level != lf->sharpness_level) { + vp9_update_sharpness(lfi, lf->sharpness_level); + lf->last_sharpness_level = lf->sharpness_level; + + /* Write to register */ + for (i = 0; i < 32; i++) { + unsigned int thr; + + thr = ((lfi->lfthr[i * 2 + 1].lim & 0x3f) << 8) | + (lfi->lfthr[i * 2 + 1].mblim & 0xff); + thr = (thr << 16) | + ((lfi->lfthr[i * 2].lim & 0x3f) << 8) | + (lfi->lfthr[i * 2].mblim & 0xff); + + amvdec_write_dos(core, HEVC_DBLK_CFG9, thr); + } + } + + for (seg_id = 0; seg_id < MAX_SEGMENTS; seg_id++) { + int lvl_seg = default_filt_lvl; + + if (segfeature_active(seg, seg_id, SEG_LVL_ALT_LF)) { + const int data = get_segdata(seg, seg_id, + SEG_LVL_ALT_LF); + lvl_seg = clamp_t(int, + seg->abs_delta == SEGMENT_ABSDATA ? + data : default_filt_lvl + data, + 0, MAX_LOOP_FILTER); + } + + if (!lf->mode_ref_delta_enabled) { + /* + * We could get rid of this if we assume that deltas + * are set to zero when not in use. + * encoder always uses deltas + */ + memset(lfi->lvl[seg_id], lvl_seg, + sizeof(lfi->lvl[seg_id])); + } else { + int ref, mode; + const int intra_lvl = + lvl_seg + lf->ref_deltas[INTRA_FRAME] * scale; + lfi->lvl[seg_id][INTRA_FRAME][0] = + clamp_val(intra_lvl, 0, MAX_LOOP_FILTER); + + for (ref = LAST_FRAME; ref < MAX_REF_FRAMES; ++ref) { + for (mode = 0; mode < MAX_MODE_LF_DELTAS; + ++mode) { + const int inter_lvl = + lvl_seg + + lf->ref_deltas[ref] * scale + + lf->mode_deltas[mode] * scale; + lfi->lvl[seg_id][ref][mode] = + clamp_val(inter_lvl, 0, + MAX_LOOP_FILTER); + } + } + } + } + + for (i = 0; i < 16; i++) { + unsigned int level; + + level = ((lfi->lvl[i >> 1][3][i & 1] & 0x3f) << 24) | + ((lfi->lvl[i >> 1][2][i & 1] & 0x3f) << 16) | + ((lfi->lvl[i >> 1][1][i & 1] & 0x3f) << 8) | + (lfi->lvl[i >> 1][0][i & 1] & 0x3f); + if (!default_filt_lvl) + level = 0; + + amvdec_write_dos(core, HEVC_DBLK_CFGA, level); + } +} + +static void codec_vp9_flush_output(struct amvdec_session *sess) +{ + struct codec_vp9 *vp9 = sess->priv; + struct vp9_frame *tmp, *n; + + mutex_lock(&vp9->lock); + list_for_each_entry_safe(tmp, n, &vp9->ref_frames_list, list) { + if (!tmp->done) { + if (tmp->show) + amvdec_dst_buf_done(sess, tmp->vbuf, + V4L2_FIELD_NONE); + else + v4l2_m2m_buf_queue(sess->m2m_ctx, tmp->vbuf); + + vp9->frames_num--; + } + + list_del(&tmp->list); + kfree(tmp); + } + mutex_unlock(&vp9->lock); +} + +static u32 codec_vp9_num_pending_bufs(struct amvdec_session *sess) +{ + struct codec_vp9 *vp9 = sess->priv; + + if (!vp9) + return 0; + + return vp9->frames_num; +} + +static int codec_vp9_alloc_workspace(struct amvdec_core *core, + struct codec_vp9 *vp9) +{ + /* Allocate some memory for the VP9 decoder's state */ + vp9->workspace_vaddr = dma_alloc_coherent(core->dev, SIZE_WORKSPACE, + &vp9->workspace_paddr, + GFP_KERNEL); + if (!vp9->workspace_vaddr) { + dev_err(core->dev, "Failed to allocate VP9 Workspace\n"); + return -ENOMEM; + } + + return 0; +} + +static void codec_vp9_setup_workspace(struct amvdec_session *sess, + struct codec_vp9 *vp9) +{ + struct amvdec_core *core = sess->core; + u32 revision = core->platform->revision; + dma_addr_t wkaddr = vp9->workspace_paddr; + + amvdec_write_dos(core, HEVCD_IPP_LINEBUFF_BASE, wkaddr + IPP_OFFSET); + amvdec_write_dos(core, VP9_RPM_BUFFER, wkaddr + RPM_OFFSET); + amvdec_write_dos(core, VP9_SHORT_TERM_RPS, wkaddr + SH_TM_RPS_OFFSET); + amvdec_write_dos(core, VP9_PPS_BUFFER, wkaddr + PPS_OFFSET); + amvdec_write_dos(core, VP9_SAO_UP, wkaddr + SAO_UP_OFFSET); + + amvdec_write_dos(core, VP9_STREAM_SWAP_BUFFER, + wkaddr + SWAP_BUF_OFFSET); + amvdec_write_dos(core, VP9_STREAM_SWAP_BUFFER2, + wkaddr + SWAP_BUF2_OFFSET); + amvdec_write_dos(core, VP9_SCALELUT, wkaddr + SCALELUT_OFFSET); + + if (core->platform->revision >= VDEC_REVISION_G12A) + amvdec_write_dos(core, HEVC_DBLK_CFGE, + wkaddr + DBLK_PARA_OFFSET); + + amvdec_write_dos(core, HEVC_DBLK_CFG4, wkaddr + DBLK_PARA_OFFSET); + amvdec_write_dos(core, HEVC_DBLK_CFG5, wkaddr + DBLK_DATA_OFFSET); + amvdec_write_dos(core, VP9_SEG_MAP_BUFFER, wkaddr + SEG_MAP_OFFSET); + amvdec_write_dos(core, VP9_PROB_SWAP_BUFFER, wkaddr + PROB_OFFSET); + amvdec_write_dos(core, VP9_COUNT_SWAP_BUFFER, wkaddr + COUNT_OFFSET); + amvdec_write_dos(core, LMEM_DUMP_ADR, wkaddr + LMEM_OFFSET); + + if (codec_hevc_use_mmu(revision, sess->pixfmt_cap, vp9->is_10bit)) { + amvdec_write_dos(core, HEVC_SAO_MMU_VH0_ADDR, + wkaddr + MMU_VBH_OFFSET); + amvdec_write_dos(core, HEVC_SAO_MMU_VH1_ADDR, + wkaddr + MMU_VBH_OFFSET + (MMU_VBH_SIZE / 2)); + + if (revision >= VDEC_REVISION_G12A) + amvdec_write_dos(core, HEVC_ASSIST_MMU_MAP_ADDR, + vp9->common.mmu_map_paddr); + else + amvdec_write_dos(core, VP9_MMU_MAP_BUFFER, + vp9->common.mmu_map_paddr); + } +} + +static int codec_vp9_start(struct amvdec_session *sess) +{ + struct amvdec_core *core = sess->core; + struct codec_vp9 *vp9; + u32 val; + int i; + int ret; + + vp9 = kzalloc(sizeof(*vp9), GFP_KERNEL); + if (!vp9) + return -ENOMEM; + + ret = codec_vp9_alloc_workspace(core, vp9); + if (ret) + goto free_vp9; + + codec_vp9_setup_workspace(sess, vp9); + amvdec_write_dos_bits(core, HEVC_STREAM_CONTROL, BIT(0)); + /* stream_fifo_hole */ + if (core->platform->revision >= VDEC_REVISION_G12A) + amvdec_write_dos_bits(core, HEVC_STREAM_FIFO_CTL, BIT(29)); + + val = amvdec_read_dos(core, HEVC_PARSER_INT_CONTROL) & 0x7fffffff; + val |= (3 << 29) | BIT(24) | BIT(22) | BIT(7) | BIT(4) | BIT(0); + amvdec_write_dos(core, HEVC_PARSER_INT_CONTROL, val); + amvdec_write_dos_bits(core, HEVC_SHIFT_STATUS, BIT(0)); + amvdec_write_dos(core, HEVC_SHIFT_CONTROL, BIT(10) | BIT(9) | + (3 << 6) | BIT(5) | BIT(2) | BIT(1) | BIT(0)); + amvdec_write_dos(core, HEVC_CABAC_CONTROL, BIT(0)); + amvdec_write_dos(core, HEVC_PARSER_CORE_CONTROL, BIT(0)); + amvdec_write_dos(core, HEVC_SHIFT_STARTCODE, 0x00000001); + + amvdec_write_dos(core, VP9_DEC_STATUS_REG, 0); + + amvdec_write_dos(core, HEVC_PARSER_CMD_WRITE, BIT(16)); + for (i = 0; i < ARRAY_SIZE(vdec_hevc_parser_cmd); ++i) + amvdec_write_dos(core, HEVC_PARSER_CMD_WRITE, + vdec_hevc_parser_cmd[i]); + + amvdec_write_dos(core, HEVC_PARSER_CMD_SKIP_0, PARSER_CMD_SKIP_CFG_0); + amvdec_write_dos(core, HEVC_PARSER_CMD_SKIP_1, PARSER_CMD_SKIP_CFG_1); + amvdec_write_dos(core, HEVC_PARSER_CMD_SKIP_2, PARSER_CMD_SKIP_CFG_2); + amvdec_write_dos(core, HEVC_PARSER_IF_CONTROL, + BIT(5) | BIT(2) | BIT(0)); + + amvdec_write_dos(core, HEVCD_IPP_TOP_CNTL, BIT(0)); + amvdec_write_dos(core, HEVCD_IPP_TOP_CNTL, BIT(1)); + + amvdec_write_dos(core, VP9_WAIT_FLAG, 1); + + /* clear mailbox interrupt */ + amvdec_write_dos(core, HEVC_ASSIST_MBOX1_CLR_REG, 1); + /* enable mailbox interrupt */ + amvdec_write_dos(core, HEVC_ASSIST_MBOX1_MASK, 1); + /* disable PSCALE for hardware sharing */ + amvdec_write_dos(core, HEVC_PSCALE_CTRL, 0); + /* Let the uCode do all the parsing */ + amvdec_write_dos(core, NAL_SEARCH_CTL, 0x8); + + amvdec_write_dos(core, DECODE_STOP_POS, 0); + amvdec_write_dos(core, VP9_DECODE_MODE, DECODE_MODE_SINGLE); + + pr_debug("decode_count: %u; decode_size: %u\n", + amvdec_read_dos(core, HEVC_DECODE_COUNT), + amvdec_read_dos(core, HEVC_DECODE_SIZE)); + + vp9_loop_filter_init(core, vp9); + + INIT_LIST_HEAD(&vp9->ref_frames_list); + mutex_init(&vp9->lock); + memset(&vp9->ref_frame_map, -1, sizeof(vp9->ref_frame_map)); + memset(&vp9->next_ref_frame_map, -1, sizeof(vp9->next_ref_frame_map)); + for (i = 0; i < REFS_PER_FRAME; ++i) + vp9->frame_refs[i] = NULL; + sess->priv = vp9; + + return 0; + +free_vp9: + kfree(vp9); + return ret; +} + +static int codec_vp9_stop(struct amvdec_session *sess) +{ + struct amvdec_core *core = sess->core; + struct codec_vp9 *vp9 = sess->priv; + + mutex_lock(&vp9->lock); + if (vp9->workspace_vaddr) + dma_free_coherent(core->dev, SIZE_WORKSPACE, + vp9->workspace_vaddr, + vp9->workspace_paddr); + + codec_hevc_free_fbc_buffers(sess, &vp9->common); + mutex_unlock(&vp9->lock); + + return 0; +} + +static void codec_vp9_set_sao(struct amvdec_session *sess, + struct vb2_buffer *vb) +{ + struct amvdec_core *core = sess->core; + struct codec_vp9 *vp9 = sess->priv; + + dma_addr_t buf_y_paddr; + dma_addr_t buf_u_v_paddr; + u32 val; + + if (codec_hevc_use_downsample(sess->pixfmt_cap, vp9->is_10bit)) + buf_y_paddr = + vp9->common.fbc_buffer_paddr[vb->index]; + else + buf_y_paddr = + vb2_dma_contig_plane_dma_addr(vb, 0); + + if (codec_hevc_use_fbc(sess->pixfmt_cap, vp9->is_10bit)) { + val = amvdec_read_dos(core, HEVC_SAO_CTRL5) & ~0xff0200; + amvdec_write_dos(core, HEVC_SAO_CTRL5, val); + amvdec_write_dos(core, HEVC_CM_BODY_START_ADDR, buf_y_paddr); + } + + if (sess->pixfmt_cap == V4L2_PIX_FMT_NV12M) { + buf_y_paddr = + vb2_dma_contig_plane_dma_addr(vb, 0); + buf_u_v_paddr = + vb2_dma_contig_plane_dma_addr(vb, 1); + amvdec_write_dos(core, HEVC_SAO_Y_START_ADDR, buf_y_paddr); + amvdec_write_dos(core, HEVC_SAO_C_START_ADDR, buf_u_v_paddr); + amvdec_write_dos(core, HEVC_SAO_Y_WPTR, buf_y_paddr); + amvdec_write_dos(core, HEVC_SAO_C_WPTR, buf_u_v_paddr); + } + + if (codec_hevc_use_mmu(core->platform->revision, sess->pixfmt_cap, + vp9->is_10bit)) { + amvdec_write_dos(core, HEVC_CM_HEADER_START_ADDR, + vp9->common.mmu_header_paddr[vb->index]); + /* use HEVC_CM_HEADER_START_ADDR */ + amvdec_write_dos_bits(core, HEVC_SAO_CTRL5, BIT(10)); + } + + amvdec_write_dos(core, HEVC_SAO_Y_LENGTH, + amvdec_get_output_size(sess)); + amvdec_write_dos(core, HEVC_SAO_C_LENGTH, + (amvdec_get_output_size(sess) / 2)); + + if (core->platform->revision >= VDEC_REVISION_G12A) { + amvdec_clear_dos_bits(core, HEVC_DBLK_CFGB, + BIT(4) | BIT(5) | BIT(8) | BIT(9)); + /* enable first, compressed write */ + if (codec_hevc_use_fbc(sess->pixfmt_cap, vp9->is_10bit)) + amvdec_write_dos_bits(core, HEVC_DBLK_CFGB, BIT(8)); + + /* enable second, uncompressed write */ + if (sess->pixfmt_cap == V4L2_PIX_FMT_NV12M) + amvdec_write_dos_bits(core, HEVC_DBLK_CFGB, BIT(9)); + + /* dblk pipeline mode=1 for performance */ + if (sess->width >= 1280) + amvdec_write_dos_bits(core, HEVC_DBLK_CFGB, BIT(4)); + + pr_debug("HEVC_DBLK_CFGB: %08X\n", + amvdec_read_dos(core, HEVC_DBLK_CFGB)); + } + + val = amvdec_read_dos(core, HEVC_SAO_CTRL1) & ~0x3ff0; + val |= 0xff0; /* Set endianness for 2-bytes swaps (nv12) */ + if (core->platform->revision < VDEC_REVISION_G12A) { + val &= ~0x3; + if (!codec_hevc_use_fbc(sess->pixfmt_cap, vp9->is_10bit)) + val |= BIT(0); /* disable cm compression */ + /* TOFIX: Handle Amlogic Framebuffer compression */ + } + + amvdec_write_dos(core, HEVC_SAO_CTRL1, val); + pr_debug("HEVC_SAO_CTRL1: %08X\n", val); + + /* no downscale for NV12 */ + val = amvdec_read_dos(core, HEVC_SAO_CTRL5) & ~0xff0000; + amvdec_write_dos(core, HEVC_SAO_CTRL5, val); + + val = amvdec_read_dos(core, HEVCD_IPP_AXIIF_CONFIG) & ~0x30; + val |= 0xf; + val &= ~BIT(12); /* NV12 */ + amvdec_write_dos(core, HEVCD_IPP_AXIIF_CONFIG, val); +} + +static dma_addr_t codec_vp9_get_frame_mv_paddr(struct codec_vp9 *vp9, + struct vp9_frame *frame) +{ + return vp9->workspace_paddr + MPRED_MV_OFFSET + + (frame->index * MPRED_MV_BUF_SIZE); +} + +static void codec_vp9_set_mpred_mv(struct amvdec_core *core, + struct codec_vp9 *vp9) +{ + int mpred_mv_rd_end_addr; + int use_prev_frame_mvs = vp9->prev_frame->width == + vp9->cur_frame->width && + vp9->prev_frame->height == + vp9->cur_frame->height && + !vp9->prev_frame->intra_only && + vp9->prev_frame->show && + vp9->prev_frame->type != KEY_FRAME; + + amvdec_write_dos(core, HEVC_MPRED_CTRL3, 0x24122412); + amvdec_write_dos(core, HEVC_MPRED_ABV_START_ADDR, + vp9->workspace_paddr + MPRED_ABV_OFFSET); + + amvdec_clear_dos_bits(core, HEVC_MPRED_CTRL4, BIT(6)); + if (use_prev_frame_mvs) + amvdec_write_dos_bits(core, HEVC_MPRED_CTRL4, BIT(6)); + + amvdec_write_dos(core, HEVC_MPRED_MV_WR_START_ADDR, + codec_vp9_get_frame_mv_paddr(vp9, vp9->cur_frame)); + amvdec_write_dos(core, HEVC_MPRED_MV_WPTR, + codec_vp9_get_frame_mv_paddr(vp9, vp9->cur_frame)); + + amvdec_write_dos(core, HEVC_MPRED_MV_RD_START_ADDR, + codec_vp9_get_frame_mv_paddr(vp9, vp9->prev_frame)); + amvdec_write_dos(core, HEVC_MPRED_MV_RPTR, + codec_vp9_get_frame_mv_paddr(vp9, vp9->prev_frame)); + + mpred_mv_rd_end_addr = + codec_vp9_get_frame_mv_paddr(vp9, vp9->prev_frame) + + (vp9->lcu_total * MV_MEM_UNIT); + amvdec_write_dos(core, HEVC_MPRED_MV_RD_END_ADDR, mpred_mv_rd_end_addr); +} + +static void codec_vp9_update_next_ref(struct codec_vp9 *vp9) +{ + union rpm_param *param = &vp9->rpm_param; + u32 buf_idx = vp9->cur_frame->index; + int ref_index = 0; + int refresh_frame_flags; + int mask; + + refresh_frame_flags = vp9->cur_frame->type == KEY_FRAME ? + 0xff : param->p.refresh_frame_flags; + + for (mask = refresh_frame_flags; mask; mask >>= 1) { + pr_debug("mask=%08X; ref_index=%d\n", mask, ref_index); + if (mask & 1) + vp9->next_ref_frame_map[ref_index] = buf_idx; + else + vp9->next_ref_frame_map[ref_index] = + vp9->ref_frame_map[ref_index]; + + ++ref_index; + } + + for (; ref_index < REF_FRAMES; ++ref_index) + vp9->next_ref_frame_map[ref_index] = + vp9->ref_frame_map[ref_index]; +} + +static void codec_vp9_save_refs(struct codec_vp9 *vp9) +{ + union rpm_param *param = &vp9->rpm_param; + int i; + + for (i = 0; i < REFS_PER_FRAME; ++i) { + const int ref = (param->p.ref_info >> + (((REFS_PER_FRAME - i - 1) * 4) + 1)) & 0x7; + + if (vp9->ref_frame_map[ref] < 0) + continue; + + pr_warn("%s: FIXME, would need to save ref %d\n", + __func__, vp9->ref_frame_map[ref]); + } +} + +static void codec_vp9_update_ref(struct codec_vp9 *vp9) +{ + union rpm_param *param = &vp9->rpm_param; + int ref_index = 0; + int mask; + int refresh_frame_flags; + + if (!vp9->cur_frame) + return; + + refresh_frame_flags = vp9->cur_frame->type == KEY_FRAME ? + 0xff : param->p.refresh_frame_flags; + + for (mask = refresh_frame_flags; mask; mask >>= 1) { + vp9->ref_frame_map[ref_index] = + vp9->next_ref_frame_map[ref_index]; + ++ref_index; + } + + if (param->p.show_existing_frame) + return; + + for (; ref_index < REF_FRAMES; ++ref_index) + vp9->ref_frame_map[ref_index] = + vp9->next_ref_frame_map[ref_index]; +} + +static struct vp9_frame *codec_vp9_get_frame_by_idx(struct codec_vp9 *vp9, + int idx) +{ + struct vp9_frame *frame; + + list_for_each_entry(frame, &vp9->ref_frames_list, list) { + if (frame->index == idx) + return frame; + } + + return NULL; +} + +static void codec_vp9_sync_ref(struct codec_vp9 *vp9) +{ + union rpm_param *param = &vp9->rpm_param; + int i; + + for (i = 0; i < REFS_PER_FRAME; ++i) { + const int ref = (param->p.ref_info >> + (((REFS_PER_FRAME - i - 1) * 4) + 1)) & 0x7; + const int idx = vp9->ref_frame_map[ref]; + + vp9->frame_refs[i] = codec_vp9_get_frame_by_idx(vp9, idx); + if (!vp9->frame_refs[i]) + pr_warn("%s: couldn't find VP9 ref %d\n", __func__, + idx); + } +} + +static void codec_vp9_set_refs(struct amvdec_session *sess, + struct codec_vp9 *vp9) +{ + struct amvdec_core *core = sess->core; + int i; + + for (i = 0; i < REFS_PER_FRAME; ++i) { + struct vp9_frame *frame = vp9->frame_refs[i]; + int id_y; + int id_u_v; + + if (!frame) + continue; + + if (codec_hevc_use_fbc(sess->pixfmt_cap, vp9->is_10bit)) { + id_y = frame->index; + id_u_v = id_y; + } else { + id_y = frame->index * 2; + id_u_v = id_y + 1; + } + + amvdec_write_dos(core, HEVCD_MPP_ANC_CANVAS_DATA_ADDR, + (id_u_v << 16) | (id_u_v << 8) | id_y); + } +} + +static void codec_vp9_set_mc(struct amvdec_session *sess, + struct codec_vp9 *vp9) +{ + struct amvdec_core *core = sess->core; + u32 scale = 0; + u32 sz; + int i; + + amvdec_write_dos(core, HEVCD_MPP_ANC_CANVAS_ACCCONFIG_ADDR, 1); + codec_vp9_set_refs(sess, vp9); + amvdec_write_dos(core, HEVCD_MPP_ANC_CANVAS_ACCCONFIG_ADDR, + (16 << 8) | 1); + codec_vp9_set_refs(sess, vp9); + + amvdec_write_dos(core, VP9D_MPP_REFINFO_TBL_ACCCONFIG, BIT(2)); + for (i = 0; i < REFS_PER_FRAME; ++i) { + if (!vp9->frame_refs[i]) + continue; + + if (vp9->frame_refs[i]->width != vp9->width || + vp9->frame_refs[i]->height != vp9->height) + scale = 1; + + sz = amvdec_am21c_body_size(vp9->frame_refs[i]->width, + vp9->frame_refs[i]->height); + + amvdec_write_dos(core, VP9D_MPP_REFINFO_DATA, + vp9->frame_refs[i]->width); + amvdec_write_dos(core, VP9D_MPP_REFINFO_DATA, + vp9->frame_refs[i]->height); + amvdec_write_dos(core, VP9D_MPP_REFINFO_DATA, + (vp9->frame_refs[i]->width << 14) / + vp9->width); + amvdec_write_dos(core, VP9D_MPP_REFINFO_DATA, + (vp9->frame_refs[i]->height << 14) / + vp9->height); + amvdec_write_dos(core, VP9D_MPP_REFINFO_DATA, sz >> 5); + } + + amvdec_write_dos(core, VP9D_MPP_REF_SCALE_ENBL, scale); +} + +static struct vp9_frame *codec_vp9_get_new_frame(struct amvdec_session *sess) +{ + struct codec_vp9 *vp9 = sess->priv; + union rpm_param *param = &vp9->rpm_param; + struct vb2_v4l2_buffer *vbuf; + struct vp9_frame *new_frame; + + new_frame = kzalloc(sizeof(*new_frame), GFP_KERNEL); + if (!new_frame) + return NULL; + + vbuf = v4l2_m2m_dst_buf_remove(sess->m2m_ctx); + if (!vbuf) { + dev_err(sess->core->dev, "No dst buffer available\n"); + kfree(new_frame); + return NULL; + } + + while (codec_vp9_get_frame_by_idx(vp9, vbuf->vb2_buf.index)) { + struct vb2_v4l2_buffer *old_vbuf = vbuf; + + vbuf = v4l2_m2m_dst_buf_remove(sess->m2m_ctx); + v4l2_m2m_buf_queue(sess->m2m_ctx, old_vbuf); + if (!vbuf) { + dev_err(sess->core->dev, "No dst buffer available\n"); + kfree(new_frame); + return NULL; + } + } + + new_frame->vbuf = vbuf; + new_frame->index = vbuf->vb2_buf.index; + new_frame->intra_only = param->p.intra_only; + new_frame->show = param->p.show_frame; + new_frame->type = param->p.frame_type; + new_frame->width = vp9->width; + new_frame->height = vp9->height; + list_add_tail(&new_frame->list, &vp9->ref_frames_list); + vp9->frames_num++; + + return new_frame; +} + +static void codec_vp9_show_existing_frame(struct codec_vp9 *vp9) +{ + union rpm_param *param = &vp9->rpm_param; + + if (!param->p.show_existing_frame) + return; + + pr_debug("showing frame %u\n", param->p.frame_to_show_idx); +} + +static void codec_vp9_rm_noshow_frame(struct amvdec_session *sess) +{ + struct codec_vp9 *vp9 = sess->priv; + struct vp9_frame *tmp; + + list_for_each_entry(tmp, &vp9->ref_frames_list, list) { + if (tmp->show) + continue; + + pr_debug("rm noshow: %u\n", tmp->index); + v4l2_m2m_buf_queue(sess->m2m_ctx, tmp->vbuf); + list_del(&tmp->list); + kfree(tmp); + vp9->frames_num--; + return; + } +} + +static void codec_vp9_process_frame(struct amvdec_session *sess) +{ + struct amvdec_core *core = sess->core; + struct codec_vp9 *vp9 = sess->priv; + union rpm_param *param = &vp9->rpm_param; + int intra_only; + + if (!param->p.show_frame) + codec_vp9_rm_noshow_frame(sess); + + vp9->cur_frame = codec_vp9_get_new_frame(sess); + if (!vp9->cur_frame) + return; + + pr_debug("frame %d: type: %08X; show_exist: %u; show: %u, intra_only: %u\n", + vp9->cur_frame->index, + param->p.frame_type, param->p.show_existing_frame, + param->p.show_frame, param->p.intra_only); + + if (param->p.frame_type != KEY_FRAME) + codec_vp9_sync_ref(vp9); + codec_vp9_update_next_ref(vp9); + codec_vp9_show_existing_frame(vp9); + + if (codec_hevc_use_mmu(core->platform->revision, sess->pixfmt_cap, + vp9->is_10bit)) + codec_hevc_fill_mmu_map(sess, &vp9->common, + &vp9->cur_frame->vbuf->vb2_buf); + + intra_only = param->p.show_frame ? 0 : param->p.intra_only; + + /* clear mpred (for keyframe only) */ + if (param->p.frame_type != KEY_FRAME && !intra_only) { + codec_vp9_set_mc(sess, vp9); + codec_vp9_set_mpred_mv(core, vp9); + } else { + amvdec_clear_dos_bits(core, HEVC_MPRED_CTRL4, BIT(6)); + } + + amvdec_write_dos(core, HEVC_PARSER_PICTURE_SIZE, + (vp9->height << 16) | vp9->width); + codec_vp9_set_sao(sess, &vp9->cur_frame->vbuf->vb2_buf); + + vp9_loop_filter_frame_init(core, &vp9->seg_4lf, + &vp9->lfi, &vp9->lf, + vp9->default_filt_lvl); + + /* ask uCode to start decoding */ + amvdec_write_dos(core, VP9_DEC_STATUS_REG, VP9_10B_DECODE_SLICE); +} + +static void codec_vp9_process_lf(struct codec_vp9 *vp9) +{ + union rpm_param *param = &vp9->rpm_param; + int i; + + vp9->lf.mode_ref_delta_enabled = param->p.mode_ref_delta_enabled; + vp9->lf.sharpness_level = param->p.sharpness_level; + vp9->default_filt_lvl = param->p.filter_level; + vp9->seg_4lf.enabled = param->p.seg_enabled; + vp9->seg_4lf.abs_delta = param->p.seg_abs_delta; + + for (i = 0; i < 4; i++) + vp9->lf.ref_deltas[i] = param->p.ref_deltas[i]; + + for (i = 0; i < 2; i++) + vp9->lf.mode_deltas[i] = param->p.mode_deltas[i]; + + for (i = 0; i < MAX_SEGMENTS; i++) + vp9->seg_4lf.feature_mask[i] = + (param->p.seg_lf_info[i] & 0x8000) ? + (1 << SEG_LVL_ALT_LF) : 0; + + for (i = 0; i < MAX_SEGMENTS; i++) + vp9->seg_4lf.feature_data[i][SEG_LVL_ALT_LF] = + (param->p.seg_lf_info[i] & 0x100) ? + -(param->p.seg_lf_info[i] & 0x3f) + : (param->p.seg_lf_info[i] & 0x3f); +} + +static void codec_vp9_resume(struct amvdec_session *sess) +{ + struct codec_vp9 *vp9 = sess->priv; + + mutex_lock(&vp9->lock); + if (codec_hevc_setup_buffers(sess, &vp9->common, vp9->is_10bit)) { + mutex_unlock(&vp9->lock); + amvdec_abort(sess); + return; + } + + codec_vp9_setup_workspace(sess, vp9); + codec_hevc_setup_decode_head(sess, vp9->is_10bit); + codec_vp9_process_lf(vp9); + codec_vp9_process_frame(sess); + + mutex_unlock(&vp9->lock); +} + +/* + * The RPM section within the workspace contains + * many information regarding the parsed bitstream + */ +static void codec_vp9_fetch_rpm(struct amvdec_session *sess) +{ + struct codec_vp9 *vp9 = sess->priv; + u16 *rpm_vaddr = vp9->workspace_vaddr + RPM_OFFSET; + int i, j; + + for (i = 0; i < RPM_BUF_SIZE; i += 4) + for (j = 0; j < 4; j++) + vp9->rpm_param.l.data[i + j] = rpm_vaddr[i + 3 - j]; +} + +static int codec_vp9_process_rpm(struct codec_vp9 *vp9) +{ + union rpm_param *param = &vp9->rpm_param; + int src_changed = 0; + int is_10bit = 0; + int pic_width_64 = ALIGN(param->p.width, 64); + int pic_height_32 = ALIGN(param->p.height, 32); + int pic_width_lcu = (pic_width_64 % LCU_SIZE) ? + pic_width_64 / LCU_SIZE + 1 + : pic_width_64 / LCU_SIZE; + int pic_height_lcu = (pic_height_32 % LCU_SIZE) ? + pic_height_32 / LCU_SIZE + 1 + : pic_height_32 / LCU_SIZE; + vp9->lcu_total = pic_width_lcu * pic_height_lcu; + + if (param->p.bit_depth == 10) + is_10bit = 1; + + if (vp9->width != param->p.width || vp9->height != param->p.height || + vp9->is_10bit != is_10bit) + src_changed = 1; + + vp9->width = param->p.width; + vp9->height = param->p.height; + vp9->is_10bit = is_10bit; + + pr_debug("width: %u; height: %u; is_10bit: %d; src_changed: %d\n", + vp9->width, vp9->height, is_10bit, src_changed); + + return src_changed; +} + +static bool codec_vp9_is_ref(struct codec_vp9 *vp9, struct vp9_frame *frame) +{ + int i; + + for (i = 0; i < REF_FRAMES; ++i) + if (vp9->ref_frame_map[i] == frame->index) + return true; + + return false; +} + +static void codec_vp9_show_frame(struct amvdec_session *sess) +{ + struct codec_vp9 *vp9 = sess->priv; + struct vp9_frame *tmp, *n; + + list_for_each_entry_safe(tmp, n, &vp9->ref_frames_list, list) { + if (!tmp->show || tmp == vp9->cur_frame) + continue; + + if (!tmp->done) { + pr_debug("Doning %u\n", tmp->index); + amvdec_dst_buf_done(sess, tmp->vbuf, V4L2_FIELD_NONE); + tmp->done = 1; + vp9->frames_num--; + } + + if (codec_vp9_is_ref(vp9, tmp) || tmp == vp9->prev_frame) + continue; + + pr_debug("deleting %d\n", tmp->index); + list_del(&tmp->list); + kfree(tmp); + } +} + +static void vp9_tree_merge_probs(unsigned int *prev_prob, + unsigned int *cur_prob, + int coef_node_start, int tree_left, + int tree_right, + int tree_i, int node) +{ + int prob_32, prob_res, prob_shift; + int pre_prob, new_prob; + int den, m_count, get_prob, factor; + + prob_32 = prev_prob[coef_node_start / 4 * 2]; + prob_res = coef_node_start & 3; + prob_shift = prob_res * 8; + pre_prob = (prob_32 >> prob_shift) & 0xff; + + den = tree_left + tree_right; + + if (den == 0) { + new_prob = pre_prob; + } else { + m_count = den < MODE_MV_COUNT_SAT ? den : MODE_MV_COUNT_SAT; + get_prob = + clip_prob(div_r32(((int64_t)tree_left * 256 + + (den >> 1)), + den)); + + /* weighted_prob */ + factor = count_to_update_factor[m_count]; + new_prob = round_power_of_two(pre_prob * (256 - factor) + + get_prob * factor, 8); + } + + cur_prob[coef_node_start / 4 * 2] = + (cur_prob[coef_node_start / 4 * 2] & (~(0xff << prob_shift))) | + (new_prob << prob_shift); +} + +static void adapt_coef_probs_cxt(unsigned int *prev_prob, + unsigned int *cur_prob, + unsigned int *count, + int update_factor, + int cxt_num, + int coef_cxt_start, + int coef_count_cxt_start) +{ + int prob_32, prob_res, prob_shift; + int pre_prob, new_prob; + int num, den, m_count, get_prob, factor; + int node, coef_node_start; + int count_sat = 24; + int cxt; + + for (cxt = 0; cxt < cxt_num; cxt++) { + const int n0 = count[coef_count_cxt_start]; + const int n1 = count[coef_count_cxt_start + 1]; + const int n2 = count[coef_count_cxt_start + 2]; + const int neob = count[coef_count_cxt_start + 3]; + const int nneob = count[coef_count_cxt_start + 4]; + const unsigned int branch_ct[3][2] = { + { neob, nneob }, + { n0, n1 + n2 }, + { n1, n2 } + }; + + coef_node_start = coef_cxt_start; + for (node = 0 ; node < 3 ; node++) { + prob_32 = prev_prob[coef_node_start / 4 * 2]; + prob_res = coef_node_start & 3; + prob_shift = prob_res * 8; + pre_prob = (prob_32 >> prob_shift) & 0xff; + + /* get binary prob */ + num = branch_ct[node][0]; + den = branch_ct[node][0] + branch_ct[node][1]; + m_count = den < count_sat ? den : count_sat; + + get_prob = (den == 0) ? + 128u : + clip_prob(div_r32(((int64_t)num * 256 + + (den >> 1)), den)); + + factor = update_factor * m_count / count_sat; + new_prob = + round_power_of_two(pre_prob * (256 - factor) + + get_prob * factor, 8); + + cur_prob[coef_node_start / 4 * 2] = + (cur_prob[coef_node_start / 4 * 2] & + (~(0xff << prob_shift))) | + (new_prob << prob_shift); + + coef_node_start += 1; + } + + coef_cxt_start = coef_cxt_start + 3; + coef_count_cxt_start = coef_count_cxt_start + 5; + } +} + +static void adapt_coef_probs(int prev_kf, int cur_kf, int pre_fc, + unsigned int *prev_prob, unsigned int *cur_prob, + unsigned int *count) +{ + int tx_size, coef_tx_size_start, coef_count_tx_size_start; + int plane, coef_plane_start, coef_count_plane_start; + int type, coef_type_start, coef_count_type_start; + int band, coef_band_start, coef_count_band_start; + int cxt_num; + int coef_cxt_start, coef_count_cxt_start; + int node, coef_node_start, coef_count_node_start; + + int tree_i, tree_left, tree_right; + int mvd_i; + + int update_factor = cur_kf ? 112 : (prev_kf ? 128 : 112); + + int prob_32; + int prob_res; + int prob_shift; + int pre_prob; + + int den; + int get_prob; + int m_count; + int factor; + + int new_prob; + + for (tx_size = 0 ; tx_size < 4 ; tx_size++) { + coef_tx_size_start = VP9_COEF_START + + tx_size * 4 * VP9_COEF_SIZE_ONE_SET; + coef_count_tx_size_start = VP9_COEF_COUNT_START + + tx_size * 4 * VP9_COEF_COUNT_SIZE_ONE_SET; + coef_plane_start = coef_tx_size_start; + coef_count_plane_start = coef_count_tx_size_start; + + for (plane = 0 ; plane < 2 ; plane++) { + coef_type_start = coef_plane_start; + coef_count_type_start = coef_count_plane_start; + + for (type = 0 ; type < 2 ; type++) { + coef_band_start = coef_type_start; + coef_count_band_start = coef_count_type_start; + + for (band = 0 ; band < 6 ; band++) { + if (band == 0) + cxt_num = 3; + else + cxt_num = 6; + coef_cxt_start = coef_band_start; + coef_count_cxt_start = + coef_count_band_start; + + adapt_coef_probs_cxt(prev_prob, + cur_prob, + count, + update_factor, + cxt_num, + coef_cxt_start, + coef_count_cxt_start); + + if (band == 0) { + coef_band_start += 10; + coef_count_band_start += 15; + } else { + coef_band_start += 18; + coef_count_band_start += 30; + } + } + coef_type_start += VP9_COEF_SIZE_ONE_SET; + coef_count_type_start += + VP9_COEF_COUNT_SIZE_ONE_SET; + } + + coef_plane_start += 2 * VP9_COEF_SIZE_ONE_SET; + coef_count_plane_start += + 2 * VP9_COEF_COUNT_SIZE_ONE_SET; + } + } + + if (cur_kf == 0) { + /* mode_mv_merge_probs - merge_intra_inter_prob */ + for (coef_count_node_start = VP9_INTRA_INTER_COUNT_START; + coef_count_node_start < (VP9_MV_CLASS0_HP_1_COUNT_START + + VP9_MV_CLASS0_HP_1_COUNT_SIZE); + coef_count_node_start += 2) { + if (coef_count_node_start == + VP9_INTRA_INTER_COUNT_START) + coef_node_start = VP9_INTRA_INTER_START; + else if (coef_count_node_start == + VP9_COMP_INTER_COUNT_START) + coef_node_start = VP9_COMP_INTER_START; + else if (coef_count_node_start == + VP9_TX_MODE_COUNT_START) + coef_node_start = VP9_TX_MODE_START; + else if (coef_count_node_start == + VP9_SKIP_COUNT_START) + coef_node_start = VP9_SKIP_START; + else if (coef_count_node_start == + VP9_MV_SIGN_0_COUNT_START) + coef_node_start = VP9_MV_SIGN_0_START; + else if (coef_count_node_start == + VP9_MV_SIGN_1_COUNT_START) + coef_node_start = VP9_MV_SIGN_1_START; + else if (coef_count_node_start == + VP9_MV_BITS_0_COUNT_START) + coef_node_start = VP9_MV_BITS_0_START; + else if (coef_count_node_start == + VP9_MV_BITS_1_COUNT_START) + coef_node_start = VP9_MV_BITS_1_START; + else if (coef_count_node_start == + VP9_MV_CLASS0_HP_0_COUNT_START) + coef_node_start = VP9_MV_CLASS0_HP_0_START; + + den = count[coef_count_node_start] + + count[coef_count_node_start + 1]; + + prob_32 = prev_prob[coef_node_start / 4 * 2]; + prob_res = coef_node_start & 3; + prob_shift = prob_res * 8; + pre_prob = (prob_32 >> prob_shift) & 0xff; + + if (den == 0) { + new_prob = pre_prob; + } else { + m_count = den < MODE_MV_COUNT_SAT ? + den : MODE_MV_COUNT_SAT; + get_prob = + clip_prob(div_r32(((int64_t) + count[coef_count_node_start] * 256 + + (den >> 1)), + den)); + + /* weighted prob */ + factor = count_to_update_factor[m_count]; + new_prob = + round_power_of_two(pre_prob * + (256 - factor) + + get_prob * factor, + 8); + } + + cur_prob[coef_node_start / 4 * 2] = + (cur_prob[coef_node_start / 4 * 2] & + (~(0xff << prob_shift))) | + (new_prob << prob_shift); + + coef_node_start = coef_node_start + 1; + } + + coef_node_start = VP9_INTER_MODE_START; + coef_count_node_start = VP9_INTER_MODE_COUNT_START; + for (tree_i = 0 ; tree_i < 7 ; tree_i++) { + for (node = 0 ; node < 3 ; node++) { + unsigned int start = coef_count_node_start; + + switch (node) { + case 2: + tree_left = count[start + 1]; + tree_right = count[start + 3]; + break; + case 1: + tree_left = count[start + 0]; + tree_right = count[start + 1] + + count[start + 3]; + break; + default: + tree_left = count[start + 2]; + tree_right = count[start + 0] + + count[start + 1] + + count[start + 3]; + break; + } + + vp9_tree_merge_probs(prev_prob, cur_prob, + coef_node_start, + tree_left, tree_right, + tree_i, node); + + coef_node_start = coef_node_start + 1; + } + + coef_count_node_start = coef_count_node_start + 4; + } + + coef_node_start = VP9_IF_Y_MODE_START; + coef_count_node_start = VP9_IF_Y_MODE_COUNT_START; + for (tree_i = 0 ; tree_i < 14 ; tree_i++) { + for (node = 0 ; node < 9 ; node++) { + unsigned int start = coef_count_node_start; + + switch (node) { + case 8: + tree_left = + count[start + D153_PRED]; + tree_right = + count[start + D207_PRED]; + break; + case 7: + tree_left = + count[start + D63_PRED]; + tree_right = + count[start + D207_PRED] + + count[start + D153_PRED]; + break; + case 6: + tree_left = + count[start + D45_PRED]; + tree_right = + count[start + D207_PRED] + + count[start + D153_PRED] + + count[start + D63_PRED]; + break; + case 5: + tree_left = + count[start + D135_PRED]; + tree_right = + count[start + D117_PRED]; + break; + case 4: + tree_left = + count[start + H_PRED]; + tree_right = + count[start + D117_PRED] + + count[start + D135_PRED]; + break; + case 3: + tree_left = + count[start + H_PRED] + + count[start + D117_PRED] + + count[start + D135_PRED]; + tree_right = + count[start + D45_PRED] + + count[start + D207_PRED] + + count[start + D153_PRED] + + count[start + D63_PRED]; + break; + case 2: + tree_left = + count[start + V_PRED]; + tree_right = + count[start + H_PRED] + + count[start + D117_PRED] + + count[start + D135_PRED] + + count[start + D45_PRED] + + count[start + D207_PRED] + + count[start + D153_PRED] + + count[start + D63_PRED]; + break; + case 1: + tree_left = + count[start + TM_PRED]; + tree_right = + count[start + V_PRED] + + count[start + H_PRED] + + count[start + D117_PRED] + + count[start + D135_PRED] + + count[start + D45_PRED] + + count[start + D207_PRED] + + count[start + D153_PRED] + + count[start + D63_PRED]; + break; + default: + tree_left = + count[start + DC_PRED]; + tree_right = + count[start + TM_PRED] + + count[start + V_PRED] + + count[start + H_PRED] + + count[start + D117_PRED] + + count[start + D135_PRED] + + count[start + D45_PRED] + + count[start + D207_PRED] + + count[start + D153_PRED] + + count[start + D63_PRED]; + break; + } + + vp9_tree_merge_probs(prev_prob, cur_prob, + coef_node_start, + tree_left, tree_right, + tree_i, node); + + coef_node_start = coef_node_start + 1; + } + coef_count_node_start = coef_count_node_start + 10; + } + + coef_node_start = VP9_PARTITION_P_START; + coef_count_node_start = VP9_PARTITION_P_COUNT_START; + for (tree_i = 0 ; tree_i < 16 ; tree_i++) { + for (node = 0 ; node < 3 ; node++) { + unsigned int start = coef_count_node_start; + + switch (node) { + case 2: + tree_left = count[start + 2]; + tree_right = count[start + 3]; + break; + case 1: + tree_left = count[start + 1]; + tree_right = count[start + 2] + + count[start + 3]; + break; + default: + tree_left = count[start + 0]; + tree_right = count[start + 1] + + count[start + 2] + + count[start + 3]; + break; + } + + vp9_tree_merge_probs(prev_prob, cur_prob, + coef_node_start, + tree_left, tree_right, + tree_i, node); + + coef_node_start = coef_node_start + 1; + } + + coef_count_node_start = coef_count_node_start + 4; + } + + coef_node_start = VP9_INTERP_START; + coef_count_node_start = VP9_INTERP_COUNT_START; + for (tree_i = 0 ; tree_i < 4 ; tree_i++) { + for (node = 0 ; node < 2 ; node++) { + unsigned int start = coef_count_node_start; + + switch (node) { + case 1: + tree_left = count[start + 1]; + tree_right = count[start + 2]; + break; + default: + tree_left = count[start + 0]; + tree_right = count[start + 1] + + count[start + 2]; + break; + } + + vp9_tree_merge_probs(prev_prob, cur_prob, + coef_node_start, + tree_left, tree_right, + tree_i, node); + + coef_node_start = coef_node_start + 1; + } + coef_count_node_start = coef_count_node_start + 3; + } + + coef_node_start = VP9_MV_JOINTS_START; + coef_count_node_start = VP9_MV_JOINTS_COUNT_START; + for (tree_i = 0 ; tree_i < 1 ; tree_i++) { + for (node = 0 ; node < 3 ; node++) { + unsigned int start = coef_count_node_start; + + switch (node) { + case 2: + tree_left = count[start + 2]; + tree_right = count[start + 3]; + break; + case 1: + tree_left = count[start + 1]; + tree_right = count[start + 2] + + count[start + 3]; + break; + default: + tree_left = count[start + 0]; + tree_right = count[start + 1] + + count[start + 2] + + count[start + 3]; + break; + } + + vp9_tree_merge_probs(prev_prob, cur_prob, + coef_node_start, + tree_left, tree_right, + tree_i, node); + + coef_node_start = coef_node_start + 1; + } + coef_count_node_start = coef_count_node_start + 4; + } + + for (mvd_i = 0 ; mvd_i < 2 ; mvd_i++) { + coef_node_start = mvd_i ? VP9_MV_CLASSES_1_START : + VP9_MV_CLASSES_0_START; + coef_count_node_start = mvd_i ? + VP9_MV_CLASSES_1_COUNT_START : + VP9_MV_CLASSES_0_COUNT_START; + tree_i = 0; + for (node = 0; node < 10; node++) { + unsigned int start = coef_count_node_start; + + switch (node) { + case 9: + tree_left = count[start + 9]; + tree_right = count[start + 10]; + break; + case 8: + tree_left = count[start + 7]; + tree_right = count[start + 8]; + break; + case 7: + tree_left = count[start + 7] + + count[start + 8]; + tree_right = count[start + 9] + + count[start + 10]; + break; + case 6: + tree_left = count[start + 6]; + tree_right = count[start + 7] + + count[start + 8] + + count[start + 9] + + count[start + 10]; + break; + case 5: + tree_left = count[start + 4]; + tree_right = count[start + 5]; + break; + case 4: + tree_left = count[start + 4] + + count[start + 5]; + tree_right = count[start + 6] + + count[start + 7] + + count[start + 8] + + count[start + 9] + + count[start + 10]; + break; + case 3: + tree_left = count[start + 2]; + tree_right = count[start + 3]; + break; + case 2: + tree_left = count[start + 2] + + count[start + 3]; + tree_right = count[start + 4] + + count[start + 5] + + count[start + 6] + + count[start + 7] + + count[start + 8] + + count[start + 9] + + count[start + 10]; + break; + case 1: + tree_left = count[start + 1]; + tree_right = count[start + 2] + + count[start + 3] + + count[start + 4] + + count[start + 5] + + count[start + 6] + + count[start + 7] + + count[start + 8] + + count[start + 9] + + count[start + 10]; + break; + default: + tree_left = count[start + 0]; + tree_right = count[start + 1] + + count[start + 2] + + count[start + 3] + + count[start + 4] + + count[start + 5] + + count[start + 6] + + count[start + 7] + + count[start + 8] + + count[start + 9] + + count[start + 10]; + break; + } + + vp9_tree_merge_probs(prev_prob, cur_prob, + coef_node_start, + tree_left, tree_right, + tree_i, node); + + coef_node_start = coef_node_start + 1; + } + + coef_node_start = mvd_i ? VP9_MV_CLASS0_1_START : + VP9_MV_CLASS0_0_START; + coef_count_node_start = mvd_i ? + VP9_MV_CLASS0_1_COUNT_START : + VP9_MV_CLASS0_0_COUNT_START; + tree_i = 0; + node = 0; + tree_left = count[coef_count_node_start + 0]; + tree_right = count[coef_count_node_start + 1]; + + vp9_tree_merge_probs(prev_prob, cur_prob, + coef_node_start, + tree_left, tree_right, + tree_i, node); + coef_node_start = mvd_i ? VP9_MV_CLASS0_FP_1_START : + VP9_MV_CLASS0_FP_0_START; + coef_count_node_start = mvd_i ? + VP9_MV_CLASS0_FP_1_COUNT_START : + VP9_MV_CLASS0_FP_0_COUNT_START; + + for (tree_i = 0; tree_i < 3; tree_i++) { + for (node = 0; node < 3; node++) { + unsigned int start = + coef_count_node_start; + switch (node) { + case 2: + tree_left = count[start + 2]; + tree_right = count[start + 3]; + break; + case 1: + tree_left = count[start + 1]; + tree_right = count[start + 2] + + count[start + 3]; + break; + default: + tree_left = count[start + 0]; + tree_right = count[start + 1] + + count[start + 2] + + count[start + 3]; + break; + } + + vp9_tree_merge_probs(prev_prob, + cur_prob, + coef_node_start, + tree_left, + tree_right, + tree_i, node); + + coef_node_start = coef_node_start + 1; + } + coef_count_node_start = + coef_count_node_start + 4; + } + } + } +} + +static irqreturn_t codec_vp9_threaded_isr(struct amvdec_session *sess) +{ + struct amvdec_core *core = sess->core; + struct codec_vp9 *vp9 = sess->priv; + u32 dec_status = amvdec_read_dos(core, VP9_DEC_STATUS_REG); + u32 prob_status = amvdec_read_dos(core, VP9_ADAPT_PROB_REG); + int i; + + if (!vp9) + return IRQ_HANDLED; + + mutex_lock(&vp9->lock); + if (dec_status != VP9_HEAD_PARSER_DONE) { + dev_err(core->dev_dec, "Unrecognized dec_status: %08X\n", + dec_status); + amvdec_abort(sess); + goto unlock; + } + + pr_debug("ISR: %08X;%08X\n", dec_status, prob_status); + sess->keyframe_found = 1; + + if ((prob_status & 0xff) == 0xfd && vp9->cur_frame) { + /* VP9_REQ_ADAPT_PROB */ + u8 *prev_prob_b = ((u8 *)vp9->workspace_vaddr + + PROB_OFFSET) + + ((prob_status >> 8) * 0x1000); + u8 *cur_prob_b = ((u8 *)vp9->workspace_vaddr + + PROB_OFFSET) + 0x4000; + u8 *count_b = (u8 *)vp9->workspace_vaddr + + COUNT_OFFSET; + int last_frame_type = vp9->prev_frame ? + vp9->prev_frame->type : + KEY_FRAME; + + adapt_coef_probs(last_frame_type == KEY_FRAME, + vp9->cur_frame->type == KEY_FRAME ? 1 : 0, + prob_status >> 8, + (unsigned int *)prev_prob_b, + (unsigned int *)cur_prob_b, + (unsigned int *)count_b); + + memcpy(prev_prob_b, cur_prob_b, ADAPT_PROB_SIZE); + amvdec_write_dos(core, VP9_ADAPT_PROB_REG, 0); + } + + /* Invalidate first 3 refs */ + for (i = 0; i < REFS_PER_FRAME ; ++i) + vp9->frame_refs[i] = NULL; + + vp9->prev_frame = vp9->cur_frame; + codec_vp9_update_ref(vp9); + + codec_vp9_fetch_rpm(sess); + if (codec_vp9_process_rpm(vp9)) { + amvdec_src_change(sess, vp9->width, vp9->height, 16); + + /* No frame is actually processed */ + vp9->cur_frame = NULL; + + /* Show the remaining frame */ + codec_vp9_show_frame(sess); + + /* FIXME: Save refs for resized frame */ + if (vp9->frames_num) + codec_vp9_save_refs(vp9); + + goto unlock; + } + + codec_vp9_process_lf(vp9); + codec_vp9_process_frame(sess); + codec_vp9_show_frame(sess); + +unlock: + mutex_unlock(&vp9->lock); + return IRQ_HANDLED; +} + +static irqreturn_t codec_vp9_isr(struct amvdec_session *sess) +{ + return IRQ_WAKE_THREAD; +} + +struct amvdec_codec_ops codec_vp9_ops = { + .start = codec_vp9_start, + .stop = codec_vp9_stop, + .isr = codec_vp9_isr, + .threaded_isr = codec_vp9_threaded_isr, + .num_pending_bufs = codec_vp9_num_pending_bufs, + .drain = codec_vp9_flush_output, + .resume = codec_vp9_resume, +}; diff --git a/drivers/staging/media/meson/vdec/codec_vp9.h b/drivers/staging/media/meson/vdec/codec_vp9.h new file mode 100644 index 000000000000..62db65a2b939 --- /dev/null +++ b/drivers/staging/media/meson/vdec/codec_vp9.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2018 Maxime Jourdan <maxi.jourdan@wanadoo.fr> + */ + +#ifndef __MESON_VDEC_CODEC_VP9_H_ +#define __MESON_VDEC_CODEC_VP9_H_ + +#include "vdec.h" + +extern struct amvdec_codec_ops codec_vp9_ops; + +#endif diff --git a/drivers/staging/media/meson/vdec/esparser.c b/drivers/staging/media/meson/vdec/esparser.c index 95102a4bdc62..db7022707ff8 100644 --- a/drivers/staging/media/meson/vdec/esparser.c +++ b/drivers/staging/media/meson/vdec/esparser.c @@ -52,6 +52,7 @@ #define PARSER_VIDEO_HOLE 0x90 #define SEARCH_PATTERN_LEN 512 +#define VP9_HEADER_SIZE 16 static DECLARE_WAIT_QUEUE_HEAD(wq); static int search_done; @@ -74,27 +75,140 @@ static irqreturn_t esparser_isr(int irq, void *dev) return IRQ_HANDLED; } +/* + * VP9 frame headers need to be appended by a 16-byte long + * Amlogic custom header + */ +static int vp9_update_header(struct amvdec_core *core, struct vb2_buffer *buf) +{ + u8 *dp; + u8 marker; + int dsize; + int num_frames, cur_frame; + int cur_mag, mag, mag_ptr; + int frame_size[8], tot_frame_size[8]; + int total_datasize = 0; + int new_frame_size; + unsigned char *old_header = NULL; + + dp = (uint8_t *)vb2_plane_vaddr(buf, 0); + dsize = vb2_get_plane_payload(buf, 0); + + if (dsize == vb2_plane_size(buf, 0)) { + dev_warn(core->dev, "%s: unable to update header\n", __func__); + return 0; + } + + marker = dp[dsize - 1]; + if ((marker & 0xe0) == 0xc0) { + num_frames = (marker & 0x7) + 1; + mag = ((marker >> 3) & 0x3) + 1; + mag_ptr = dsize - mag * num_frames - 2; + if (dp[mag_ptr] != marker) + return 0; + + mag_ptr++; + for (cur_frame = 0; cur_frame < num_frames; cur_frame++) { + frame_size[cur_frame] = 0; + for (cur_mag = 0; cur_mag < mag; cur_mag++) { + frame_size[cur_frame] |= + (dp[mag_ptr] << (cur_mag * 8)); + mag_ptr++; + } + if (cur_frame == 0) + tot_frame_size[cur_frame] = + frame_size[cur_frame]; + else + tot_frame_size[cur_frame] = + tot_frame_size[cur_frame - 1] + + frame_size[cur_frame]; + total_datasize += frame_size[cur_frame]; + } + } else { + num_frames = 1; + frame_size[0] = dsize; + tot_frame_size[0] = dsize; + total_datasize = dsize; + } + + new_frame_size = total_datasize + num_frames * VP9_HEADER_SIZE; + + if (new_frame_size >= vb2_plane_size(buf, 0)) { + dev_warn(core->dev, "%s: unable to update header\n", __func__); + return 0; + } + + for (cur_frame = num_frames - 1; cur_frame >= 0; cur_frame--) { + int framesize = frame_size[cur_frame]; + int framesize_header = framesize + 4; + int oldframeoff = tot_frame_size[cur_frame] - framesize; + int outheaderoff = oldframeoff + cur_frame * VP9_HEADER_SIZE; + u8 *fdata = dp + outheaderoff; + u8 *old_framedata = dp + oldframeoff; + + memmove(fdata + VP9_HEADER_SIZE, old_framedata, framesize); + + fdata[0] = (framesize_header >> 24) & 0xff; + fdata[1] = (framesize_header >> 16) & 0xff; + fdata[2] = (framesize_header >> 8) & 0xff; + fdata[3] = (framesize_header >> 0) & 0xff; + fdata[4] = ((framesize_header >> 24) & 0xff) ^ 0xff; + fdata[5] = ((framesize_header >> 16) & 0xff) ^ 0xff; + fdata[6] = ((framesize_header >> 8) & 0xff) ^ 0xff; + fdata[7] = ((framesize_header >> 0) & 0xff) ^ 0xff; + fdata[8] = 0; + fdata[9] = 0; + fdata[10] = 0; + fdata[11] = 1; + fdata[12] = 'A'; + fdata[13] = 'M'; + fdata[14] = 'L'; + fdata[15] = 'V'; + + if (!old_header) { + /* nothing */ + } else if (old_header > fdata + 16 + framesize) { + dev_dbg(core->dev, "%s: data has gaps, setting to 0\n", + __func__); + memset(fdata + 16 + framesize, 0, + (old_header - fdata + 16 + framesize)); + } else if (old_header < fdata + 16 + framesize) { + dev_err(core->dev, "%s: data overwritten\n", __func__); + } + old_header = fdata; + } + + return new_frame_size; +} + /* Pad the packet to at least 4KiB bytes otherwise the VDEC unit won't trigger * ISRs. * Also append a start code 000001ff at the end to trigger * the ESPARSER interrupt. */ -static u32 esparser_pad_start_code(struct vb2_buffer *vb) +static u32 esparser_pad_start_code(struct amvdec_core *core, + struct vb2_buffer *vb, + u32 payload_size) { - u32 payload_size = vb2_get_plane_payload(vb, 0); u32 pad_size = 0; - u8 *vaddr = vb2_plane_vaddr(vb, 0) + payload_size; + u8 *vaddr = vb2_plane_vaddr(vb, 0); if (payload_size < ESPARSER_MIN_PACKET_SIZE) { pad_size = ESPARSER_MIN_PACKET_SIZE - payload_size; - memset(vaddr, 0, pad_size); + memset(vaddr + payload_size, 0, pad_size); + } + + if ((payload_size + pad_size + SEARCH_PATTERN_LEN) > + vb2_plane_size(vb, 0)) { + dev_warn(core->dev, "%s: unable to pad start code\n", __func__); + return pad_size; } - memset(vaddr + pad_size, 0, SEARCH_PATTERN_LEN); - vaddr[pad_size] = 0x00; - vaddr[pad_size + 1] = 0x00; - vaddr[pad_size + 2] = 0x01; - vaddr[pad_size + 3] = 0xff; + memset(vaddr + payload_size + pad_size, 0, SEARCH_PATTERN_LEN); + vaddr[payload_size + pad_size] = 0x00; + vaddr[payload_size + pad_size + 1] = 0x00; + vaddr[payload_size + pad_size + 2] = 0x01; + vaddr[payload_size + pad_size + 3] = 0xff; return pad_size; } @@ -181,30 +295,60 @@ esparser_queue(struct amvdec_session *sess, struct vb2_v4l2_buffer *vbuf) struct vb2_buffer *vb = &vbuf->vb2_buf; struct amvdec_core *core = sess->core; struct amvdec_codec_ops *codec_ops = sess->fmt_out->codec_ops; - u32 num_dst_bufs = 0; u32 payload_size = vb2_get_plane_payload(vb, 0); dma_addr_t phy = vb2_dma_contig_plane_dma_addr(vb, 0); + u32 num_dst_bufs = 0; u32 offset; u32 pad_size; - if (codec_ops->num_pending_bufs) - num_dst_bufs = codec_ops->num_pending_bufs(sess); - - num_dst_bufs += v4l2_m2m_num_dst_bufs_ready(sess->m2m_ctx); - - if (esparser_vififo_get_free_space(sess) < payload_size || - atomic_read(&sess->esparser_queued_bufs) >= num_dst_bufs) + /* + * When max ref frame is held by VP9, this should be -= 3 to prevent a + * shortage of CAPTURE buffers on the decoder side. + * For the future, a good enhancement of the way this is handled could + * be to notify new capture buffers to the decoding modules, so that + * they could pause when there is no capture buffer available and + * resume on this notification. + */ + if (sess->fmt_out->pixfmt == V4L2_PIX_FMT_VP9) { + if (codec_ops->num_pending_bufs) + num_dst_bufs = codec_ops->num_pending_bufs(sess); + + num_dst_bufs += v4l2_m2m_num_dst_bufs_ready(sess->m2m_ctx); + if (sess->fmt_out->pixfmt == V4L2_PIX_FMT_VP9) + num_dst_bufs -= 3; + + if (esparser_vififo_get_free_space(sess) < payload_size || + atomic_read(&sess->esparser_queued_bufs) >= num_dst_bufs) + return -EAGAIN; + } else if (esparser_vififo_get_free_space(sess) < payload_size) { return -EAGAIN; + } v4l2_m2m_src_buf_remove_by_buf(sess->m2m_ctx, vbuf); offset = esparser_get_offset(sess); - amvdec_add_ts_reorder(sess, vb->timestamp, offset); - dev_dbg(core->dev, "esparser: ts = %llu pld_size = %u offset = %08X\n", - vb->timestamp, payload_size, offset); + amvdec_add_ts(sess, vb->timestamp, vbuf->timecode, offset, vbuf->flags); + dev_dbg(core->dev, "esparser: ts = %llu pld_size = %u offset = %08X flags = %08X\n", + vb->timestamp, payload_size, offset, vbuf->flags); + + vbuf->flags = 0; + vbuf->field = V4L2_FIELD_NONE; + vbuf->sequence = sess->sequence_out++; + + if (sess->fmt_out->pixfmt == V4L2_PIX_FMT_VP9) { + payload_size = vp9_update_header(core, vb); - pad_size = esparser_pad_start_code(vb); + /* If unable to alter buffer to add headers */ + if (payload_size == 0) { + amvdec_remove_ts(sess, vb->timestamp); + v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR); + + return 0; + } + } + + pad_size = esparser_pad_start_code(core, vb, payload_size); ret = esparser_write_data(core, phy, payload_size + pad_size); if (ret <= 0) { @@ -216,19 +360,7 @@ esparser_queue(struct amvdec_session *sess, struct vb2_v4l2_buffer *vbuf) return 0; } - /* We need to wait until we parse the first keyframe. - * All buffers prior to the first keyframe must be dropped. - */ - if (!sess->keyframe_found) - usleep_range(1000, 2000); - - if (sess->keyframe_found) - atomic_inc(&sess->esparser_queued_bufs); - else - amvdec_remove_ts(sess, vb->timestamp); - - vbuf->flags = 0; - vbuf->field = V4L2_FIELD_NONE; + atomic_inc(&sess->esparser_queued_bufs); v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE); return 0; diff --git a/drivers/staging/media/meson/vdec/hevc_regs.h b/drivers/staging/media/meson/vdec/hevc_regs.h new file mode 100644 index 000000000000..0392f41a1eed --- /dev/null +++ b/drivers/staging/media/meson/vdec/hevc_regs.h @@ -0,0 +1,218 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2015 Amlogic, Inc. All rights reserved. + */ + +#ifndef __MESON_VDEC_HEVC_REGS_H_ +#define __MESON_VDEC_HEVC_REGS_H_ + +#define HEVC_ASSIST_MMU_MAP_ADDR 0xc024 + +#define HEVC_ASSIST_MBOX1_CLR_REG 0xc1d4 +#define HEVC_ASSIST_MBOX1_MASK 0xc1d8 + +#define HEVC_ASSIST_SCRATCH_0 0xc300 +#define HEVC_ASSIST_SCRATCH_1 0xc304 +#define HEVC_ASSIST_SCRATCH_2 0xc308 +#define HEVC_ASSIST_SCRATCH_3 0xc30c +#define HEVC_ASSIST_SCRATCH_4 0xc310 +#define HEVC_ASSIST_SCRATCH_5 0xc314 +#define HEVC_ASSIST_SCRATCH_6 0xc318 +#define HEVC_ASSIST_SCRATCH_7 0xc31c +#define HEVC_ASSIST_SCRATCH_8 0xc320 +#define HEVC_ASSIST_SCRATCH_9 0xc324 +#define HEVC_ASSIST_SCRATCH_A 0xc328 +#define HEVC_ASSIST_SCRATCH_B 0xc32c +#define HEVC_ASSIST_SCRATCH_C 0xc330 +#define HEVC_ASSIST_SCRATCH_D 0xc334 +#define HEVC_ASSIST_SCRATCH_E 0xc338 +#define HEVC_ASSIST_SCRATCH_F 0xc33c +#define HEVC_ASSIST_SCRATCH_G 0xc340 +#define HEVC_ASSIST_SCRATCH_H 0xc344 +#define HEVC_ASSIST_SCRATCH_I 0xc348 +#define HEVC_ASSIST_SCRATCH_J 0xc34c +#define HEVC_ASSIST_SCRATCH_K 0xc350 +#define HEVC_ASSIST_SCRATCH_L 0xc354 +#define HEVC_ASSIST_SCRATCH_M 0xc358 +#define HEVC_ASSIST_SCRATCH_N 0xc35c + +#define HEVC_PARSER_VERSION 0xc400 +#define HEVC_STREAM_CONTROL 0xc404 +#define HEVC_STREAM_START_ADDR 0xc408 +#define HEVC_STREAM_END_ADDR 0xc40c +#define HEVC_STREAM_WR_PTR 0xc410 +#define HEVC_STREAM_RD_PTR 0xc414 +#define HEVC_STREAM_LEVEL 0xc418 +#define HEVC_STREAM_FIFO_CTL 0xc41c +#define HEVC_SHIFT_CONTROL 0xc420 +#define HEVC_SHIFT_STARTCODE 0xc424 +#define HEVC_SHIFT_EMULATECODE 0xc428 +#define HEVC_SHIFT_STATUS 0xc42c +#define HEVC_SHIFTED_DATA 0xc430 +#define HEVC_SHIFT_BYTE_COUNT 0xc434 +#define HEVC_SHIFT_COMMAND 0xc438 +#define HEVC_ELEMENT_RESULT 0xc43c +#define HEVC_CABAC_CONTROL 0xc440 +#define HEVC_PARSER_SLICE_INFO 0xc444 +#define HEVC_PARSER_CMD_WRITE 0xc448 +#define HEVC_PARSER_CORE_CONTROL 0xc44c +#define HEVC_PARSER_CMD_FETCH 0xc450 +#define HEVC_PARSER_CMD_STATUS 0xc454 +#define HEVC_PARSER_LCU_INFO 0xc458 +#define HEVC_PARSER_HEADER_INFO 0xc45c +#define HEVC_PARSER_INT_CONTROL 0xc480 +#define HEVC_PARSER_INT_STATUS 0xc484 +#define HEVC_PARSER_IF_CONTROL 0xc488 +#define HEVC_PARSER_PICTURE_SIZE 0xc48c +#define HEVC_PARSER_LCU_START 0xc490 +#define HEVC_PARSER_HEADER_INFO2 0xc494 +#define HEVC_PARSER_QUANT_READ 0xc498 +#define HEVC_PARSER_RESERVED_27 0xc49c +#define HEVC_PARSER_CMD_SKIP_0 0xc4a0 +#define HEVC_PARSER_CMD_SKIP_1 0xc4a4 +#define HEVC_PARSER_CMD_SKIP_2 0xc4a8 +#define HEVC_SAO_IF_STATUS 0xc4c0 +#define HEVC_SAO_IF_DATA_Y 0xc4c4 +#define HEVC_SAO_IF_DATA_U 0xc4c8 +#define HEVC_SAO_IF_DATA_V 0xc4cc +#define HEVC_STREAM_SWAP_ADDR 0xc4d0 +#define HEVC_STREAM_SWAP_CTRL 0xc4d4 +#define HEVC_IQIT_IF_WAIT_CNT 0xc4d8 +#define HEVC_MPRED_IF_WAIT_CNT 0xc4dc +#define HEVC_SAO_IF_WAIT_CNT 0xc4e0 + +#define HEVC_MPRED_VERSION 0xc800 +#define HEVC_MPRED_CTRL0 0xc804 + #define MPRED_CTRL0_NEW_PIC BIT(2) + #define MPRED_CTRL0_NEW_TILE BIT(3) + #define MPRED_CTRL0_NEW_SLI_SEG BIT(4) + #define MPRED_CTRL0_TMVP BIT(5) + #define MPRED_CTRL0_LDC BIT(6) + #define MPRED_CTRL0_COL_FROM_L0 BIT(7) + #define MPRED_CTRL0_ABOVE_EN BIT(9) + #define MPRED_CTRL0_MV_WR_EN BIT(10) + #define MPRED_CTRL0_MV_RD_EN BIT(11) + #define MPRED_CTRL0_BUF_LINEAR BIT(13) +#define HEVC_MPRED_CTRL1 0xc808 +#define HEVC_MPRED_INT_EN 0xc80c +#define HEVC_MPRED_INT_STATUS 0xc810 +#define HEVC_MPRED_PIC_SIZE 0xc814 +#define HEVC_MPRED_PIC_SIZE_LCU 0xc818 +#define HEVC_MPRED_TILE_START 0xc81c +#define HEVC_MPRED_TILE_SIZE_LCU 0xc820 +#define HEVC_MPRED_REF_NUM 0xc824 +#define HEVC_MPRED_REF_EN_L0 0xc830 +#define HEVC_MPRED_REF_EN_L1 0xc834 +#define HEVC_MPRED_COLREF_EN_L0 0xc838 +#define HEVC_MPRED_COLREF_EN_L1 0xc83c +#define HEVC_MPRED_AXI_WCTRL 0xc840 +#define HEVC_MPRED_AXI_RCTRL 0xc844 +#define HEVC_MPRED_ABV_START_ADDR 0xc848 +#define HEVC_MPRED_MV_WR_START_ADDR 0xc84c +#define HEVC_MPRED_MV_RD_START_ADDR 0xc850 +#define HEVC_MPRED_MV_WPTR 0xc854 +#define HEVC_MPRED_MV_RPTR 0xc858 +#define HEVC_MPRED_MV_WR_ROW_JUMP 0xc85c +#define HEVC_MPRED_MV_RD_ROW_JUMP 0xc860 +#define HEVC_MPRED_CURR_LCU 0xc864 +#define HEVC_MPRED_ABV_WPTR 0xc868 +#define HEVC_MPRED_ABV_RPTR 0xc86c +#define HEVC_MPRED_CTRL2 0xc870 +#define HEVC_MPRED_CTRL3 0xc874 +#define HEVC_MPRED_L0_REF00_POC 0xc880 +#define HEVC_MPRED_L1_REF00_POC 0xc8c0 + +#define HEVC_MPRED_CTRL4 0xc930 + +#define HEVC_MPRED_CUR_POC 0xc980 +#define HEVC_MPRED_COL_POC 0xc984 +#define HEVC_MPRED_MV_RD_END_ADDR 0xc988 + +#define HEVC_MSP 0xcc00 +#define HEVC_MPSR 0xcc04 +#define HEVC_MCPU_INTR_MSK 0xcc10 +#define HEVC_MCPU_INTR_REQ 0xcc14 +#define HEVC_CPSR 0xcc84 + +#define HEVC_IMEM_DMA_CTRL 0xcd00 +#define HEVC_IMEM_DMA_ADR 0xcd04 +#define HEVC_IMEM_DMA_COUNT 0xcd08 + +#define HEVCD_IPP_TOP_CNTL 0xd000 +#define HEVCD_IPP_LINEBUFF_BASE 0xd024 +#define HEVCD_IPP_AXIIF_CONFIG 0xd02c + +#define VP9D_MPP_REF_SCALE_ENBL 0xd104 +#define VP9D_MPP_REFINFO_TBL_ACCCONFIG 0xd108 +#define VP9D_MPP_REFINFO_DATA 0xd10c + +#define HEVCD_MPP_ANC2AXI_TBL_CONF_ADDR 0xd180 +#define HEVCD_MPP_ANC2AXI_TBL_CMD_ADDR 0xd184 +#define HEVCD_MPP_ANC2AXI_TBL_DATA 0xd190 + +#define HEVCD_MPP_ANC_CANVAS_ACCCONFIG_ADDR 0xd300 +#define HEVCD_MPP_ANC_CANVAS_DATA_ADDR 0xd304 +#define HEVCD_MPP_DECOMP_CTL1 0xd308 +#define HEVCD_MPP_DECOMP_CTL2 0xd30c +#define HEVCD_MCRCC_CTL1 0xd3c0 +#define HEVCD_MCRCC_CTL2 0xd3c4 +#define HEVCD_MCRCC_CTL3 0xd3c8 + +#define HEVC_DBLK_CFG0 0xd400 +#define HEVC_DBLK_CFG1 0xd404 +#define HEVC_DBLK_CFG2 0xd408 +#define HEVC_DBLK_CFG3 0xd40c +#define HEVC_DBLK_CFG4 0xd410 +#define HEVC_DBLK_CFG5 0xd414 +#define HEVC_DBLK_CFG6 0xd418 +#define HEVC_DBLK_CFG7 0xd41c +#define HEVC_DBLK_CFG8 0xd420 +#define HEVC_DBLK_CFG9 0xd424 +#define HEVC_DBLK_CFGA 0xd428 +#define HEVC_DBLK_STS0 0xd42c +#define HEVC_DBLK_CFGB 0xd42c +#define HEVC_DBLK_STS1 0xd430 +#define HEVC_DBLK_CFGE 0xd438 + +#define HEVC_SAO_VERSION 0xd800 +#define HEVC_SAO_CTRL0 0xd804 +#define HEVC_SAO_CTRL1 0xd808 +#define HEVC_SAO_PIC_SIZE 0xd814 +#define HEVC_SAO_PIC_SIZE_LCU 0xd818 +#define HEVC_SAO_TILE_START 0xd81c +#define HEVC_SAO_TILE_SIZE_LCU 0xd820 +#define HEVC_SAO_Y_START_ADDR 0xd82c +#define HEVC_SAO_Y_LENGTH 0xd830 +#define HEVC_SAO_C_START_ADDR 0xd834 +#define HEVC_SAO_C_LENGTH 0xd838 +#define HEVC_SAO_Y_WPTR 0xd83c +#define HEVC_SAO_C_WPTR 0xd840 +#define HEVC_SAO_ABV_START_ADDR 0xd844 +#define HEVC_SAO_VB_WR_START_ADDR 0xd848 +#define HEVC_SAO_VB_RD_START_ADDR 0xd84c +#define HEVC_SAO_ABV_WPTR 0xd850 +#define HEVC_SAO_ABV_RPTR 0xd854 +#define HEVC_SAO_VB_WPTR 0xd858 +#define HEVC_SAO_VB_RPTR 0xd85c +#define HEVC_SAO_CTRL2 0xd880 +#define HEVC_SAO_CTRL3 0xd884 +#define HEVC_SAO_CTRL4 0xd888 +#define HEVC_SAO_CTRL5 0xd88c +#define HEVC_SAO_CTRL6 0xd890 +#define HEVC_SAO_CTRL7 0xd894 +#define HEVC_CM_BODY_START_ADDR 0xd898 +#define HEVC_CM_BODY_LENGTH 0xd89c +#define HEVC_CM_HEADER_START_ADDR 0xd8a0 +#define HEVC_CM_HEADER_LENGTH 0xd8a4 +#define HEVC_CM_HEADER_OFFSET 0xd8ac +#define HEVC_SAO_MMU_VH0_ADDR 0xd8e8 +#define HEVC_SAO_MMU_VH1_ADDR 0xd8ec + +#define HEVC_IQIT_CLK_RST_CTRL 0xdc00 +#define HEVC_IQIT_SCALELUT_WR_ADDR 0xdc08 +#define HEVC_IQIT_SCALELUT_RD_ADDR 0xdc0c +#define HEVC_IQIT_SCALELUT_DATA 0xdc10 + +#define HEVC_PSCALE_CTRL 0xe444 + +#endif diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c index 5c5dabed2f09..3040136ceb77 100644 --- a/drivers/staging/media/meson/vdec/vdec.c +++ b/drivers/staging/media/meson/vdec/vdec.c @@ -168,7 +168,10 @@ static void process_num_buffers(struct vb2_queue *q, { const struct amvdec_format *fmt_out = sess->fmt_out; unsigned int buffers_total = q->num_buffers + *num_buffers; + u32 min_buf_capture = v4l2_ctrl_g_ctrl(sess->ctrl_min_buf_capture); + if (q->num_buffers + *num_buffers < min_buf_capture) + *num_buffers = min_buf_capture - q->num_buffers; if (is_reqbufs && buffers_total < fmt_out->min_buffers) *num_buffers = fmt_out->min_buffers - q->num_buffers; if (buffers_total > fmt_out->max_buffers) @@ -193,7 +196,8 @@ static int vdec_queue_setup(struct vb2_queue *q, unsigned int *num_buffers, if (*num_planes) { switch (q->type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - if (*num_planes != 1 || sizes[0] < output_size) + if (*num_planes != 1 || + sizes[0] < sess->src_buffer_size) return -EINVAL; break; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: @@ -224,7 +228,7 @@ static int vdec_queue_setup(struct vb2_queue *q, unsigned int *num_buffers, switch (q->type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - sizes[0] = amvdec_get_output_size(sess); + sizes[0] = sess->src_buffer_size; *num_planes = 1; break; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: @@ -250,6 +254,7 @@ static int vdec_queue_setup(struct vb2_queue *q, unsigned int *num_buffers, return -EINVAL; } + sess->changed_format = 1; return 0; } @@ -261,10 +266,11 @@ static void vdec_vb2_buf_queue(struct vb2_buffer *vb) v4l2_m2m_buf_queue(m2m_ctx, vbuf); - if (!sess->streamon_out || !sess->streamon_cap) + if (!sess->streamon_out) return; - if (vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && + if (sess->streamon_cap && + vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && vdec_codec_needs_recycle(sess)) vdec_queue_recycle(sess, vb); @@ -289,16 +295,22 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count) else sess->streamon_cap = 1; - if (!sess->streamon_out || !sess->streamon_cap) + if (!sess->streamon_out) return 0; if (sess->status == STATUS_NEEDS_RESUME && - q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && + sess->changed_format) { codec_ops->resume(sess); sess->status = STATUS_RUNNING; return 0; } + if (sess->status == STATUS_RUNNING || + sess->status == STATUS_NEEDS_RESUME || + sess->status == STATUS_INIT) + return 0; + sess->vififo_size = SIZE_VIFIFO; sess->vififo_vaddr = dma_alloc_coherent(sess->core->dev, sess->vififo_size, @@ -323,13 +335,14 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count) goto vififo_free; sess->sequence_cap = 0; + sess->sequence_out = 0; if (vdec_codec_needs_recycle(sess)) sess->recycle_thread = kthread_run(vdec_recycle_thread, sess, "vdec_recycle"); - sess->status = STATUS_RUNNING; + sess->status = STATUS_INIT; core->cur_sess = sess; - + schedule_work(&sess->esparser_queue_work); return 0; vififo_free: @@ -382,10 +395,12 @@ static void vdec_reset_bufs_recycle(struct amvdec_session *sess) static void vdec_stop_streaming(struct vb2_queue *q) { struct amvdec_session *sess = vb2_get_drv_priv(q); + struct amvdec_codec_ops *codec_ops = sess->fmt_out->codec_ops; struct amvdec_core *core = sess->core; struct vb2_v4l2_buffer *buf; if (sess->status == STATUS_RUNNING || + sess->status == STATUS_INIT || (sess->status == STATUS_NEEDS_RESUME && (!sess->streamon_out || !sess->streamon_cap))) { if (vdec_codec_needs_recycle(sess)) @@ -409,6 +424,10 @@ static void vdec_stop_streaming(struct vb2_queue *q) sess->streamon_out = 0; } else { + /* Drain remaining refs if was still running */ + if (sess->status >= STATUS_RUNNING && codec_ops->drain) + codec_ops->drain(sess); + while ((buf = v4l2_m2m_dst_buf_remove(sess->m2m_ctx))) v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR); @@ -476,20 +495,34 @@ vdec_try_fmt_common(struct amvdec_session *sess, u32 size, struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp; struct v4l2_plane_pix_format *pfmt = pixmp->plane_fmt; const struct amvdec_format *fmts = sess->core->platform->formats; - const struct amvdec_format *fmt_out; + const struct amvdec_format *fmt_out = NULL; + u32 output_size = 0; memset(pfmt[0].reserved, 0, sizeof(pfmt[0].reserved)); memset(pixmp->reserved, 0, sizeof(pixmp->reserved)); - if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: fmt_out = find_format(fmts, size, pixmp->pixelformat); if (!fmt_out) { pixmp->pixelformat = V4L2_PIX_FMT_MPEG2; fmt_out = find_format(fmts, size, pixmp->pixelformat); } + break; + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + fmt_out = sess->fmt_out; + break; + default: + return NULL; + } + + pixmp->width = clamp(pixmp->width, (u32)256, fmt_out->max_width); + pixmp->height = clamp(pixmp->height, (u32)144, fmt_out->max_height); + output_size = get_output_size(pixmp->width, pixmp->height); - pfmt[0].sizeimage = - get_output_size(pixmp->width, pixmp->height); + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + if (!pfmt[0].sizeimage) + pfmt[0].sizeimage = sess->src_buffer_size; pfmt[0].bytesperline = 0; pixmp->num_planes = 1; } else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { @@ -499,35 +532,25 @@ vdec_try_fmt_common(struct amvdec_session *sess, u32 size, memset(pfmt[1].reserved, 0, sizeof(pfmt[1].reserved)); if (pixmp->pixelformat == V4L2_PIX_FMT_NV12M) { - pfmt[0].sizeimage = - get_output_size(pixmp->width, pixmp->height); - pfmt[0].bytesperline = ALIGN(pixmp->width, 64); + pfmt[0].sizeimage = output_size; + pfmt[0].bytesperline = ALIGN(pixmp->width, 32); - pfmt[1].sizeimage = - get_output_size(pixmp->width, pixmp->height) / 2; - pfmt[1].bytesperline = ALIGN(pixmp->width, 64); + pfmt[1].sizeimage = output_size / 2; + pfmt[1].bytesperline = ALIGN(pixmp->width, 32); pixmp->num_planes = 2; } else if (pixmp->pixelformat == V4L2_PIX_FMT_YUV420M) { - pfmt[0].sizeimage = - get_output_size(pixmp->width, pixmp->height); - pfmt[0].bytesperline = ALIGN(pixmp->width, 64); + pfmt[0].sizeimage = output_size; + pfmt[0].bytesperline = ALIGN(pixmp->width, 32); - pfmt[1].sizeimage = - get_output_size(pixmp->width, pixmp->height) / 4; - pfmt[1].bytesperline = ALIGN(pixmp->width, 64) / 2; + pfmt[1].sizeimage = output_size / 4; + pfmt[1].bytesperline = ALIGN(pixmp->width, 32) / 2; - pfmt[2].sizeimage = - get_output_size(pixmp->width, pixmp->height) / 4; - pfmt[2].bytesperline = ALIGN(pixmp->width, 64) / 2; + pfmt[2].sizeimage = output_size / 2; + pfmt[2].bytesperline = ALIGN(pixmp->width, 32) / 2; pixmp->num_planes = 3; } - } else { - return NULL; } - pixmp->width = clamp(pixmp->width, (u32)256, fmt_out->max_width); - pixmp->height = clamp(pixmp->height, (u32)144, fmt_out->max_height); - if (pixmp->field == V4L2_FIELD_ANY) pixmp->field = V4L2_FIELD_NONE; @@ -586,6 +609,8 @@ static int vdec_s_fmt(struct file *file, void *fh, struct v4l2_format *f) orig_pixmp = *pixmp; fmt_out = vdec_try_fmt_common(sess, num_formats, f); + if (!fmt_out) + return -EINVAL; if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { pixfmt_out = pixmp->pixelformat; @@ -610,6 +635,7 @@ static int vdec_s_fmt(struct file *file, void *fh, struct v4l2_format *f) sess->ycbcr_enc = pixmp->ycbcr_enc; sess->quantization = pixmp->quantization; sess->xfer_func = pixmp->xfer_func; + sess->src_buffer_size = pixmp->plane_fmt[0].sizeimage; } memset(&format, 0, sizeof(format)); @@ -701,29 +727,31 @@ vdec_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *cmd) if (!(sess->streamon_out & sess->streamon_cap)) return 0; - /* Currently not handled since we do not support dynamic resolution - * for MPEG2. We consider both queues streaming to mean that the - * decoding session is started - */ - if (cmd->cmd == V4L2_DEC_CMD_START) + if (cmd->cmd == V4L2_DEC_CMD_START) { + v4l2_m2m_clear_state(sess->m2m_ctx); + sess->should_stop = 0; return 0; + } /* Should not happen */ if (cmd->cmd != V4L2_DEC_CMD_STOP) return -EINVAL; dev_dbg(dev, "Received V4L2_DEC_CMD_STOP\n"); + sess->should_stop = 1; - vdec_wait_inactive(sess); + v4l2_m2m_mark_stopped(sess->m2m_ctx); if (codec_ops->drain) { + vdec_wait_inactive(sess); codec_ops->drain(sess); } else if (codec_ops->eos_sequence) { u32 len; const u8 *data = codec_ops->eos_sequence(&len); esparser_queue_eos(sess->core, data, len); + vdec_wait_inactive(sess); } return ret; @@ -883,6 +911,7 @@ static int vdec_open(struct file *file) sess->height = 720; sess->pixelaspect.numerator = 1; sess->pixelaspect.denominator = 1; + sess->src_buffer_size = SZ_1M; INIT_LIST_HEAD(&sess->timestamps); INIT_LIST_HEAD(&sess->bufs_recycle); @@ -1076,7 +1105,7 @@ static int vdec_probe(struct platform_device *pdev) video_set_drvdata(vdev, core); - ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret) { dev_err(dev, "Failed registering video device\n"); goto err_vdev_release; diff --git a/drivers/staging/media/meson/vdec/vdec.h b/drivers/staging/media/meson/vdec/vdec.h index 0faa1ec4858e..f95445ac0658 100644 --- a/drivers/staging/media/meson/vdec/vdec.h +++ b/drivers/staging/media/meson/vdec/vdec.h @@ -29,13 +29,19 @@ struct amvdec_buffer { * struct amvdec_timestamp - stores a src timestamp along with a VIFIFO offset * * @list: used to make lists out of this struct - * @ts: timestamp + * @tc: timecode from the v4l2 buffer + * @ts: timestamp from the VB2 buffer * @offset: offset in the VIFIFO where the associated packet was written + * @flags: flags from the v4l2 buffer + * @used_count: times this timestamp was checked for a match with a dst buffer */ struct amvdec_timestamp { struct list_head list; + struct v4l2_timecode tc; u64 ts; u32 offset; + u32 flags; + u32 used_count; }; struct amvdec_session; @@ -165,6 +171,7 @@ struct amvdec_format { enum amvdec_status { STATUS_STOPPED, + STATUS_INIT, STATUS_RUNNING, STATUS_NEEDS_RESUME, }; @@ -180,6 +187,7 @@ enum amvdec_status { * @ctrl_min_buf_capture: V4L2 control V4L2_CID_MIN_BUFFERS_FOR_CAPTURE * @fmt_out: vdec pixel format for the OUTPUT queue * @pixfmt_cap: V4L2 pixel format for the CAPTURE queue + * @src_buffer_size: size in bytes of the OUTPUT buffers' only plane * @width: current picture width * @height: current picture height * @colorspace: current colorspace @@ -221,6 +229,7 @@ struct amvdec_session { const struct amvdec_format *fmt_out; u32 pixfmt_cap; + u32 src_buffer_size; u32 width; u32 height; @@ -235,10 +244,11 @@ struct amvdec_session { struct work_struct esparser_queue_work; unsigned int streamon_cap, streamon_out; - unsigned int sequence_cap; + unsigned int sequence_cap, sequence_out; unsigned int should_stop; unsigned int keyframe_found; unsigned int num_dst_bufs; + unsigned int changed_format; u8 canvas_alloc[MAX_CANVAS]; u32 canvas_num; diff --git a/drivers/staging/media/meson/vdec/vdec_helpers.c b/drivers/staging/media/meson/vdec/vdec_helpers.c index f16948bdbf2f..7f07a9175815 100644 --- a/drivers/staging/media/meson/vdec/vdec_helpers.c +++ b/drivers/staging/media/meson/vdec/vdec_helpers.c @@ -50,6 +50,33 @@ void amvdec_write_parser(struct amvdec_core *core, u32 reg, u32 val) } EXPORT_SYMBOL_GPL(amvdec_write_parser); +/* 4 KiB per 64x32 block */ +u32 amvdec_am21c_body_size(u32 width, u32 height) +{ + u32 width_64 = ALIGN(width, 64) / 64; + u32 height_32 = ALIGN(height, 32) / 32; + + return SZ_4K * width_64 * height_32; +} +EXPORT_SYMBOL_GPL(amvdec_am21c_body_size); + +/* 32 bytes per 128x64 block */ +u32 amvdec_am21c_head_size(u32 width, u32 height) +{ + u32 width_128 = ALIGN(width, 128) / 128; + u32 height_64 = ALIGN(height, 64) / 64; + + return 32 * width_128 * height_64; +} +EXPORT_SYMBOL_GPL(amvdec_am21c_head_size); + +u32 amvdec_am21c_size(u32 width, u32 height) +{ + return ALIGN(amvdec_am21c_body_size(width, height) + + amvdec_am21c_head_size(width, height), SZ_64K); +} +EXPORT_SYMBOL_GPL(amvdec_am21c_size); + static int canvas_alloc(struct amvdec_session *sess, u8 *canvas_id) { int ret; @@ -154,8 +181,8 @@ int amvdec_set_canvases(struct amvdec_session *sess, { struct v4l2_m2m_buffer *buf; u32 pixfmt = sess->pixfmt_cap; - u32 width = ALIGN(sess->width, 64); - u32 height = ALIGN(sess->height, 64); + u32 width = ALIGN(sess->width, 32); + u32 height = ALIGN(sess->height, 32); u32 reg_cur = reg_base[0]; u32 reg_num_cur = 0; u32 reg_base_cur = 0; @@ -200,33 +227,23 @@ int amvdec_set_canvases(struct amvdec_session *sess, } EXPORT_SYMBOL_GPL(amvdec_set_canvases); -void amvdec_add_ts_reorder(struct amvdec_session *sess, u64 ts, u32 offset) +void amvdec_add_ts(struct amvdec_session *sess, u64 ts, + struct v4l2_timecode tc, u32 offset, u32 vbuf_flags) { - struct amvdec_timestamp *new_ts, *tmp; + struct amvdec_timestamp *new_ts; unsigned long flags; - new_ts = kmalloc(sizeof(*new_ts), GFP_KERNEL); + new_ts = kzalloc(sizeof(*new_ts), GFP_KERNEL); new_ts->ts = ts; + new_ts->tc = tc; new_ts->offset = offset; + new_ts->flags = vbuf_flags; spin_lock_irqsave(&sess->ts_spinlock, flags); - - if (list_empty(&sess->timestamps)) - goto add_tail; - - list_for_each_entry(tmp, &sess->timestamps, list) { - if (ts <= tmp->ts) { - list_add_tail(&new_ts->list, &tmp->list); - goto unlock; - } - } - -add_tail: list_add_tail(&new_ts->list, &sess->timestamps); -unlock: spin_unlock_irqrestore(&sess->ts_spinlock, flags); } -EXPORT_SYMBOL_GPL(amvdec_add_ts_reorder); +EXPORT_SYMBOL_GPL(amvdec_add_ts); void amvdec_remove_ts(struct amvdec_session *sess, u64 ts) { @@ -251,8 +268,8 @@ EXPORT_SYMBOL_GPL(amvdec_remove_ts); static void dst_buf_done(struct amvdec_session *sess, struct vb2_v4l2_buffer *vbuf, - u32 field, - u64 timestamp) + u32 field, u64 timestamp, + struct v4l2_timecode timecode, u32 flags) { struct device *dev = sess->core->dev_dec; u32 output_size = amvdec_get_output_size(sess); @@ -271,19 +288,27 @@ static void dst_buf_done(struct amvdec_session *sess, vbuf->vb2_buf.timestamp = timestamp; vbuf->sequence = sess->sequence_cap++; + vbuf->flags = flags; + vbuf->timecode = timecode; if (sess->should_stop && - atomic_read(&sess->esparser_queued_bufs) <= 2) { + atomic_read(&sess->esparser_queued_bufs) <= 1) { const struct v4l2_event ev = { .type = V4L2_EVENT_EOS }; - dev_dbg(dev, "Signaling EOS\n"); + dev_dbg(dev, "Signaling EOS, sequence_cap = %u\n", + sess->sequence_cap - 1); v4l2_event_queue_fh(&sess->fh, &ev); vbuf->flags |= V4L2_BUF_FLAG_LAST; + } else if (sess->status == STATUS_NEEDS_RESUME) { + /* Mark LAST for drained show frames during a source change */ + vbuf->flags |= V4L2_BUF_FLAG_LAST; + sess->sequence_cap = 0; } else if (sess->should_stop) dev_dbg(dev, "should_stop, %u bufs remain\n", atomic_read(&sess->esparser_queued_bufs)); - dev_dbg(dev, "Buffer %u done\n", vbuf->vb2_buf.index); + dev_dbg(dev, "Buffer %u done, ts = %llu, flags = %08X\n", + vbuf->vb2_buf.index, timestamp, flags); vbuf->field = field; v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE); @@ -297,7 +322,9 @@ void amvdec_dst_buf_done(struct amvdec_session *sess, struct device *dev = sess->core->dev_dec; struct amvdec_timestamp *tmp; struct list_head *timestamps = &sess->timestamps; + struct v4l2_timecode timecode; u64 timestamp; + u32 vbuf_flags; unsigned long flags; spin_lock_irqsave(&sess->ts_spinlock, flags); @@ -312,11 +339,13 @@ void amvdec_dst_buf_done(struct amvdec_session *sess, tmp = list_first_entry(timestamps, struct amvdec_timestamp, list); timestamp = tmp->ts; + timecode = tmp->tc; + vbuf_flags = tmp->flags; list_del(&tmp->list); kfree(tmp); spin_unlock_irqrestore(&sess->ts_spinlock, flags); - dst_buf_done(sess, vbuf, field, timestamp); + dst_buf_done(sess, vbuf, field, timestamp, timecode, vbuf_flags); atomic_dec(&sess->esparser_queued_bufs); } EXPORT_SYMBOL_GPL(amvdec_dst_buf_done); @@ -328,48 +357,43 @@ void amvdec_dst_buf_done_offset(struct amvdec_session *sess, struct device *dev = sess->core->dev_dec; struct amvdec_timestamp *match = NULL; struct amvdec_timestamp *tmp, *n; + struct v4l2_timecode timecode = { 0 }; u64 timestamp = 0; + u32 vbuf_flags = 0; unsigned long flags; spin_lock_irqsave(&sess->ts_spinlock, flags); /* Look for our vififo offset to get the corresponding timestamp. */ list_for_each_entry_safe(tmp, n, &sess->timestamps, list) { - s64 delta = (s64)offset - tmp->offset; - - /* Offsets reported by codecs usually differ slightly, - * so we need some wiggle room. - * 4KiB being the minimum packet size, there is no risk here. - */ - if (delta > (-1 * (s32)SZ_4K) && delta < SZ_4K) { - match = tmp; + if (tmp->offset > offset) { + /* + * Delete any record that remained unused for 32 match + * checks + */ + if (tmp->used_count++ >= 32) { + list_del(&tmp->list); + kfree(tmp); + } break; } - if (!allow_drop) - continue; - - /* Delete any timestamp entry that appears before our target - * (not all src packets/timestamps lead to a frame) - */ - if (delta > 0 || delta < -1 * (s32)sess->vififo_size) { - atomic_dec(&sess->esparser_queued_bufs); - list_del(&tmp->list); - kfree(tmp); - } + match = tmp; } if (!match) { - dev_dbg(dev, "Buffer %u done but can't match offset (%08X)\n", + dev_err(dev, "Buffer %u done but can't match offset (%08X)\n", vbuf->vb2_buf.index, offset); } else { timestamp = match->ts; + timecode = match->tc; + vbuf_flags = match->flags; list_del(&match->list); kfree(match); } spin_unlock_irqrestore(&sess->ts_spinlock, flags); - dst_buf_done(sess, vbuf, field, timestamp); + dst_buf_done(sess, vbuf, field, timestamp, timecode, vbuf_flags); if (match) atomic_dec(&sess->esparser_queued_bufs); } @@ -420,16 +444,19 @@ void amvdec_src_change(struct amvdec_session *sess, u32 width, v4l2_ctrl_s_ctrl(sess->ctrl_min_buf_capture, dpb_size); - /* Check if the capture queue is already configured well for our + /* + * Check if the capture queue is already configured well for our * usecase. If so, keep decoding with it and do not send the event */ - if (sess->width == width && + if (sess->streamon_cap && + sess->width == width && sess->height == height && dpb_size <= sess->num_dst_bufs) { sess->fmt_out->codec_ops->resume(sess); return; } + sess->changed_format = 0; sess->width = width; sess->height = height; sess->status = STATUS_NEEDS_RESUME; diff --git a/drivers/staging/media/meson/vdec/vdec_helpers.h b/drivers/staging/media/meson/vdec/vdec_helpers.h index a455a9ee1cc2..cfaed52ab526 100644 --- a/drivers/staging/media/meson/vdec/vdec_helpers.h +++ b/drivers/staging/media/meson/vdec/vdec_helpers.h @@ -27,6 +27,10 @@ void amvdec_clear_dos_bits(struct amvdec_core *core, u32 reg, u32 val); u32 amvdec_read_parser(struct amvdec_core *core, u32 reg); void amvdec_write_parser(struct amvdec_core *core, u32 reg, u32 val); +u32 amvdec_am21c_body_size(u32 width, u32 height); +u32 amvdec_am21c_head_size(u32 width, u32 height); +u32 amvdec_am21c_size(u32 width, u32 height); + /** * amvdec_dst_buf_done_idx() - Signal that a buffer is done decoding * @@ -44,13 +48,15 @@ void amvdec_dst_buf_done_offset(struct amvdec_session *sess, u32 offset, u32 field, bool allow_drop); /** - * amvdec_add_ts_reorder() - Add a timestamp to the list in chronological order + * amvdec_add_ts() - Add a timestamp to the list * * @sess: current session * @ts: timestamp to add * @offset: offset in the VIFIFO where the associated packet was written + * @flags the vb2_v4l2_buffer flags */ -void amvdec_add_ts_reorder(struct amvdec_session *sess, u64 ts, u32 offset); +void amvdec_add_ts(struct amvdec_session *sess, u64 ts, + struct v4l2_timecode tc, u32 offset, u32 flags); void amvdec_remove_ts(struct amvdec_session *sess, u64 ts); /** diff --git a/drivers/staging/media/meson/vdec/vdec_hevc.c b/drivers/staging/media/meson/vdec/vdec_hevc.c new file mode 100644 index 000000000000..9530e580e57a --- /dev/null +++ b/drivers/staging/media/meson/vdec/vdec_hevc.c @@ -0,0 +1,231 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2018 Maxime Jourdan <maxi.jourdan@wanadoo.fr> + * + * VDEC_HEVC is a video decoding block that allows decoding of + * HEVC, VP9 + */ + +#include <linux/firmware.h> +#include <linux/clk.h> + +#include "vdec_1.h" +#include "vdec_helpers.h" +#include "vdec_hevc.h" +#include "hevc_regs.h" +#include "dos_regs.h" + +/* AO Registers */ +#define AO_RTI_GEN_PWR_SLEEP0 0xe8 +#define AO_RTI_GEN_PWR_ISO0 0xec + #define GEN_PWR_VDEC_HEVC (BIT(7) | BIT(6)) + #define GEN_PWR_VDEC_HEVC_SM1 (BIT(2)) + +#define MC_SIZE (4096 * 4) + +static int vdec_hevc_load_firmware(struct amvdec_session *sess, + const char *fwname) +{ + struct amvdec_core *core = sess->core; + struct device *dev = core->dev_dec; + const struct firmware *fw; + static void *mc_addr; + static dma_addr_t mc_addr_map; + int ret; + u32 i = 100; + + ret = request_firmware(&fw, fwname, dev); + if (ret < 0) { + dev_err(dev, "Unable to request firmware %s\n", fwname); + return ret; + } + + if (fw->size < MC_SIZE) { + dev_err(dev, "Firmware size %zu is too small. Expected %u.\n", + fw->size, MC_SIZE); + ret = -EINVAL; + goto release_firmware; + } + + mc_addr = dma_alloc_coherent(core->dev, MC_SIZE, &mc_addr_map, + GFP_KERNEL); + if (!mc_addr) { + ret = -ENOMEM; + goto release_firmware; + } + + memcpy(mc_addr, fw->data, MC_SIZE); + + amvdec_write_dos(core, HEVC_MPSR, 0); + amvdec_write_dos(core, HEVC_CPSR, 0); + + amvdec_write_dos(core, HEVC_IMEM_DMA_ADR, mc_addr_map); + amvdec_write_dos(core, HEVC_IMEM_DMA_COUNT, MC_SIZE / 4); + amvdec_write_dos(core, HEVC_IMEM_DMA_CTRL, (0x8000 | (7 << 16))); + + while (i && (readl(core->dos_base + HEVC_IMEM_DMA_CTRL) & 0x8000)) + i--; + + if (i == 0) { + dev_err(dev, "Firmware load fail (DMA hang?)\n"); + ret = -ENODEV; + } + + dma_free_coherent(core->dev, MC_SIZE, mc_addr, mc_addr_map); +release_firmware: + release_firmware(fw); + return ret; +} + +static void vdec_hevc_stbuf_init(struct amvdec_session *sess) +{ + struct amvdec_core *core = sess->core; + + amvdec_write_dos(core, HEVC_STREAM_CONTROL, + amvdec_read_dos(core, HEVC_STREAM_CONTROL) & ~1); + amvdec_write_dos(core, HEVC_STREAM_START_ADDR, sess->vififo_paddr); + amvdec_write_dos(core, HEVC_STREAM_END_ADDR, + sess->vififo_paddr + sess->vififo_size); + amvdec_write_dos(core, HEVC_STREAM_RD_PTR, sess->vififo_paddr); + amvdec_write_dos(core, HEVC_STREAM_WR_PTR, sess->vififo_paddr); +} + +/* VDEC_HEVC specific ESPARSER configuration */ +static void vdec_hevc_conf_esparser(struct amvdec_session *sess) +{ + struct amvdec_core *core = sess->core; + + /* set vififo_vbuf_rp_sel=>vdec_hevc */ + amvdec_write_dos(core, DOS_GEN_CTRL0, 3 << 1); + amvdec_write_dos(core, HEVC_STREAM_CONTROL, + amvdec_read_dos(core, HEVC_STREAM_CONTROL) | BIT(3)); + amvdec_write_dos(core, HEVC_STREAM_CONTROL, + amvdec_read_dos(core, HEVC_STREAM_CONTROL) | 1); + amvdec_write_dos(core, HEVC_STREAM_FIFO_CTL, + amvdec_read_dos(core, HEVC_STREAM_FIFO_CTL) | BIT(29)); +} + +static u32 vdec_hevc_vififo_level(struct amvdec_session *sess) +{ + return readl_relaxed(sess->core->dos_base + HEVC_STREAM_LEVEL); +} + +static int vdec_hevc_stop(struct amvdec_session *sess) +{ + struct amvdec_core *core = sess->core; + struct amvdec_codec_ops *codec_ops = sess->fmt_out->codec_ops; + + /* Disable interrupt */ + amvdec_write_dos(core, HEVC_ASSIST_MBOX1_MASK, 0); + /* Disable firmware processor */ + amvdec_write_dos(core, HEVC_MPSR, 0); + + if (sess->priv) + codec_ops->stop(sess); + + /* Enable VDEC_HEVC Isolation */ + if (core->platform->revision == VDEC_REVISION_SM1) + regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_ISO0, + GEN_PWR_VDEC_HEVC_SM1, + GEN_PWR_VDEC_HEVC_SM1); + else + regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_ISO0, + 0xc00, 0xc00); + + /* VDEC_HEVC Memories */ + amvdec_write_dos(core, DOS_MEM_PD_HEVC, 0xffffffffUL); + + if (core->platform->revision == VDEC_REVISION_SM1) + regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, + GEN_PWR_VDEC_HEVC_SM1, + GEN_PWR_VDEC_HEVC_SM1); + else + regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, + GEN_PWR_VDEC_HEVC, GEN_PWR_VDEC_HEVC); + + clk_disable_unprepare(core->vdec_hevc_clk); + if (core->platform->revision == VDEC_REVISION_G12A || + core->platform->revision == VDEC_REVISION_SM1) + clk_disable_unprepare(core->vdec_hevcf_clk); + + return 0; +} + +static int vdec_hevc_start(struct amvdec_session *sess) +{ + int ret; + struct amvdec_core *core = sess->core; + struct amvdec_codec_ops *codec_ops = sess->fmt_out->codec_ops; + + if (core->platform->revision == VDEC_REVISION_G12A || + core->platform->revision == VDEC_REVISION_SM1) { + clk_set_rate(core->vdec_hevcf_clk, 666666666); + ret = clk_prepare_enable(core->vdec_hevcf_clk); + if (ret) + return ret; + } + + clk_set_rate(core->vdec_hevc_clk, 666666666); + ret = clk_prepare_enable(core->vdec_hevc_clk); + if (ret) + return ret; + + if (core->platform->revision == VDEC_REVISION_SM1) + regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, + GEN_PWR_VDEC_HEVC_SM1, 0); + else + regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, + GEN_PWR_VDEC_HEVC, 0); + usleep_range(10, 20); + + /* Reset VDEC_HEVC*/ + amvdec_write_dos(core, DOS_SW_RESET3, 0xffffffff); + amvdec_write_dos(core, DOS_SW_RESET3, 0x00000000); + + amvdec_write_dos(core, DOS_GCLK_EN3, 0xffffffff); + + /* VDEC_HEVC Memories */ + amvdec_write_dos(core, DOS_MEM_PD_HEVC, 0x00000000); + + /* Remove VDEC_HEVC Isolation */ + if (core->platform->revision == VDEC_REVISION_SM1) + regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_ISO0, + GEN_PWR_VDEC_HEVC_SM1, 0); + else + regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_ISO0, + 0xc00, 0); + + amvdec_write_dos(core, DOS_SW_RESET3, 0xffffffff); + amvdec_write_dos(core, DOS_SW_RESET3, 0x00000000); + + vdec_hevc_stbuf_init(sess); + + ret = vdec_hevc_load_firmware(sess, sess->fmt_out->firmware_path); + if (ret) + goto stop; + + ret = codec_ops->start(sess); + if (ret) + goto stop; + + amvdec_write_dos(core, DOS_SW_RESET3, BIT(12) | BIT(11)); + amvdec_write_dos(core, DOS_SW_RESET3, 0); + amvdec_read_dos(core, DOS_SW_RESET3); + + amvdec_write_dos(core, HEVC_MPSR, 1); + /* Let the firmware settle */ + usleep_range(10, 20); + + return 0; + +stop: + vdec_hevc_stop(sess); + return ret; +} + +struct amvdec_ops vdec_hevc_ops = { + .start = vdec_hevc_start, + .stop = vdec_hevc_stop, + .conf_esparser = vdec_hevc_conf_esparser, + .vififo_level = vdec_hevc_vififo_level, +}; diff --git a/drivers/staging/media/meson/vdec/vdec_hevc.h b/drivers/staging/media/meson/vdec/vdec_hevc.h new file mode 100644 index 000000000000..cd576a73a966 --- /dev/null +++ b/drivers/staging/media/meson/vdec/vdec_hevc.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2018 Maxime Jourdan <maxi.jourdan@wanadoo.fr> + */ + +#ifndef __MESON_VDEC_VDEC_HEVC_H_ +#define __MESON_VDEC_VDEC_HEVC_H_ + +#include "vdec.h" + +extern struct amvdec_ops vdec_hevc_ops; + +#endif diff --git a/drivers/staging/media/meson/vdec/vdec_platform.c b/drivers/staging/media/meson/vdec/vdec_platform.c index ea39f8209ec7..eabbebab2da2 100644 --- a/drivers/staging/media/meson/vdec/vdec_platform.c +++ b/drivers/staging/media/meson/vdec/vdec_platform.c @@ -8,10 +8,25 @@ #include "vdec.h" #include "vdec_1.h" +#include "vdec_hevc.h" #include "codec_mpeg12.h" +#include "codec_h264.h" +#include "codec_vp9.h" static const struct amvdec_format vdec_formats_gxbb[] = { { + .pixfmt = V4L2_PIX_FMT_H264, + .min_buffers = 2, + .max_buffers = 24, + .max_width = 1920, + .max_height = 1080, + .vdec_ops = &vdec_1_ops, + .codec_ops = &codec_h264_ops, + .firmware_path = "meson/vdec/gxbb_h264.bin", + .pixfmts_cap = { V4L2_PIX_FMT_NV12M, 0 }, + .flags = V4L2_FMT_FLAG_COMPRESSED | + V4L2_FMT_FLAG_DYN_RESOLUTION, + }, { .pixfmt = V4L2_PIX_FMT_MPEG1, .min_buffers = 8, .max_buffers = 8, @@ -21,6 +36,7 @@ static const struct amvdec_format vdec_formats_gxbb[] = { .codec_ops = &codec_mpeg12_ops, .firmware_path = "meson/vdec/gxl_mpeg12.bin", .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 }, + .flags = V4L2_FMT_FLAG_COMPRESSED, }, { .pixfmt = V4L2_PIX_FMT_MPEG2, .min_buffers = 8, @@ -31,11 +47,36 @@ static const struct amvdec_format vdec_formats_gxbb[] = { .codec_ops = &codec_mpeg12_ops, .firmware_path = "meson/vdec/gxl_mpeg12.bin", .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 }, + .flags = V4L2_FMT_FLAG_COMPRESSED, }, }; static const struct amvdec_format vdec_formats_gxl[] = { { + .pixfmt = V4L2_PIX_FMT_VP9, + .min_buffers = 16, + .max_buffers = 24, + .max_width = 3840, + .max_height = 2160, + .vdec_ops = &vdec_hevc_ops, + .codec_ops = &codec_vp9_ops, + .firmware_path = "meson/vdec/gxl_vp9.bin", + .pixfmts_cap = { V4L2_PIX_FMT_NV12M, 0 }, + .flags = V4L2_FMT_FLAG_COMPRESSED | + V4L2_FMT_FLAG_DYN_RESOLUTION, + }, { + .pixfmt = V4L2_PIX_FMT_H264, + .min_buffers = 2, + .max_buffers = 24, + .max_width = 3840, + .max_height = 2160, + .vdec_ops = &vdec_1_ops, + .codec_ops = &codec_h264_ops, + .firmware_path = "meson/vdec/gxl_h264.bin", + .pixfmts_cap = { V4L2_PIX_FMT_NV12M, 0 }, + .flags = V4L2_FMT_FLAG_COMPRESSED | + V4L2_FMT_FLAG_DYN_RESOLUTION, + }, { .pixfmt = V4L2_PIX_FMT_MPEG1, .min_buffers = 8, .max_buffers = 8, @@ -45,6 +86,7 @@ static const struct amvdec_format vdec_formats_gxl[] = { .codec_ops = &codec_mpeg12_ops, .firmware_path = "meson/vdec/gxl_mpeg12.bin", .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 }, + .flags = V4L2_FMT_FLAG_COMPRESSED, }, { .pixfmt = V4L2_PIX_FMT_MPEG2, .min_buffers = 8, @@ -55,11 +97,24 @@ static const struct amvdec_format vdec_formats_gxl[] = { .codec_ops = &codec_mpeg12_ops, .firmware_path = "meson/vdec/gxl_mpeg12.bin", .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 }, + .flags = V4L2_FMT_FLAG_COMPRESSED, }, }; static const struct amvdec_format vdec_formats_gxm[] = { { + .pixfmt = V4L2_PIX_FMT_H264, + .min_buffers = 2, + .max_buffers = 24, + .max_width = 3840, + .max_height = 2160, + .vdec_ops = &vdec_1_ops, + .codec_ops = &codec_h264_ops, + .firmware_path = "meson/vdec/gxm_h264.bin", + .pixfmts_cap = { V4L2_PIX_FMT_NV12M, 0 }, + .flags = V4L2_FMT_FLAG_COMPRESSED | + V4L2_FMT_FLAG_DYN_RESOLUTION, + }, { .pixfmt = V4L2_PIX_FMT_MPEG1, .min_buffers = 8, .max_buffers = 8, @@ -69,6 +124,7 @@ static const struct amvdec_format vdec_formats_gxm[] = { .codec_ops = &codec_mpeg12_ops, .firmware_path = "meson/vdec/gxl_mpeg12.bin", .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 }, + .flags = V4L2_FMT_FLAG_COMPRESSED, }, { .pixfmt = V4L2_PIX_FMT_MPEG2, .min_buffers = 8, @@ -79,11 +135,36 @@ static const struct amvdec_format vdec_formats_gxm[] = { .codec_ops = &codec_mpeg12_ops, .firmware_path = "meson/vdec/gxl_mpeg12.bin", .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 }, + .flags = V4L2_FMT_FLAG_COMPRESSED, }, }; static const struct amvdec_format vdec_formats_g12a[] = { { + .pixfmt = V4L2_PIX_FMT_VP9, + .min_buffers = 16, + .max_buffers = 24, + .max_width = 3840, + .max_height = 2160, + .vdec_ops = &vdec_hevc_ops, + .codec_ops = &codec_vp9_ops, + .firmware_path = "meson/vdec/g12a_vp9.bin", + .pixfmts_cap = { V4L2_PIX_FMT_NV12M, 0 }, + .flags = V4L2_FMT_FLAG_COMPRESSED | + V4L2_FMT_FLAG_DYN_RESOLUTION, + }, { + .pixfmt = V4L2_PIX_FMT_H264, + .min_buffers = 2, + .max_buffers = 24, + .max_width = 3840, + .max_height = 2160, + .vdec_ops = &vdec_1_ops, + .codec_ops = &codec_h264_ops, + .firmware_path = "meson/vdec/g12a_h264.bin", + .pixfmts_cap = { V4L2_PIX_FMT_NV12M, 0 }, + .flags = V4L2_FMT_FLAG_COMPRESSED | + V4L2_FMT_FLAG_DYN_RESOLUTION, + }, { .pixfmt = V4L2_PIX_FMT_MPEG1, .min_buffers = 8, .max_buffers = 8, @@ -93,6 +174,7 @@ static const struct amvdec_format vdec_formats_g12a[] = { .codec_ops = &codec_mpeg12_ops, .firmware_path = "meson/vdec/gxl_mpeg12.bin", .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 }, + .flags = V4L2_FMT_FLAG_COMPRESSED, }, { .pixfmt = V4L2_PIX_FMT_MPEG2, .min_buffers = 8, @@ -103,11 +185,36 @@ static const struct amvdec_format vdec_formats_g12a[] = { .codec_ops = &codec_mpeg12_ops, .firmware_path = "meson/vdec/gxl_mpeg12.bin", .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 }, + .flags = V4L2_FMT_FLAG_COMPRESSED, }, }; static const struct amvdec_format vdec_formats_sm1[] = { { + .pixfmt = V4L2_PIX_FMT_VP9, + .min_buffers = 16, + .max_buffers = 24, + .max_width = 3840, + .max_height = 2160, + .vdec_ops = &vdec_hevc_ops, + .codec_ops = &codec_vp9_ops, + .firmware_path = "meson/vdec/sm1_vp9_mmu.bin", + .pixfmts_cap = { V4L2_PIX_FMT_NV12M, 0 }, + .flags = V4L2_FMT_FLAG_COMPRESSED | + V4L2_FMT_FLAG_DYN_RESOLUTION, + }, { + .pixfmt = V4L2_PIX_FMT_H264, + .min_buffers = 2, + .max_buffers = 24, + .max_width = 3840, + .max_height = 2160, + .vdec_ops = &vdec_1_ops, + .codec_ops = &codec_h264_ops, + .firmware_path = "meson/vdec/g12a_h264.bin", + .pixfmts_cap = { V4L2_PIX_FMT_NV12M, 0 }, + .flags = V4L2_FMT_FLAG_COMPRESSED | + V4L2_FMT_FLAG_DYN_RESOLUTION, + }, { .pixfmt = V4L2_PIX_FMT_MPEG1, .min_buffers = 8, .max_buffers = 8, @@ -117,6 +224,7 @@ static const struct amvdec_format vdec_formats_sm1[] = { .codec_ops = &codec_mpeg12_ops, .firmware_path = "meson/vdec/gxl_mpeg12.bin", .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 }, + .flags = V4L2_FMT_FLAG_COMPRESSED, }, { .pixfmt = V4L2_PIX_FMT_MPEG2, .min_buffers = 8, @@ -127,6 +235,7 @@ static const struct amvdec_format vdec_formats_sm1[] = { .codec_ops = &codec_mpeg12_ops, .firmware_path = "meson/vdec/gxl_mpeg12.bin", .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 }, + .flags = V4L2_FMT_FLAG_COMPRESSED, }, }; diff --git a/drivers/staging/media/omap4iss/iss_video.c b/drivers/staging/media/omap4iss/iss_video.c index 673aa3a5f2bd..66975a37dc85 100644 --- a/drivers/staging/media/omap4iss/iss_video.c +++ b/drivers/staging/media/omap4iss/iss_video.c @@ -1111,7 +1111,7 @@ static int iss_video_open(struct file *file) goto done; } - ret = v4l2_pipeline_pm_use(&video->video.entity, 1); + ret = v4l2_pipeline_pm_get(&video->video.entity); if (ret < 0) { omap4iss_put(video->iss); goto done; @@ -1160,7 +1160,7 @@ static int iss_video_release(struct file *file) /* Disable streaming and free the buffers queue resources. */ iss_video_streamoff(file, vfh, video->type); - v4l2_pipeline_pm_use(&video->video.entity, 0); + v4l2_pipeline_pm_put(&video->video.entity); /* Release the videobuf2 queue */ vb2_queue_release(&handle->queue); @@ -1242,7 +1242,7 @@ int omap4iss_video_init(struct iss_video *video, const char *name) video->video.fops = &iss_video_fops; snprintf(video->video.name, sizeof(video->video.name), "OMAP4 ISS %s %s", name, direction); - video->video.vfl_type = VFL_TYPE_GRABBER; + video->video.vfl_type = VFL_TYPE_VIDEO; video->video.release = video_device_release_empty; video->video.ioctl_ops = &iss_video_ioctl_ops; video->pipe.stream_state = ISS_PIPELINE_STREAM_STOPPED; @@ -1270,7 +1270,7 @@ int omap4iss_video_register(struct iss_video *video, struct v4l2_device *vdev) video->video.device_caps = V4L2_CAP_VIDEO_OUTPUT; video->video.device_caps |= V4L2_CAP_STREAMING; - ret = video_register_device(&video->video, VFL_TYPE_GRABBER, -1); + ret = video_register_device(&video->video, VFL_TYPE_VIDEO, -1); if (ret < 0) dev_err(video->iss->dev, "could not register video device (%d)\n", ret); diff --git a/drivers/staging/media/rkisp1/TODO b/drivers/staging/media/rkisp1/TODO index 03cd9a4e70f7..0aa9877dd64a 100644 --- a/drivers/staging/media/rkisp1/TODO +++ b/drivers/staging/media/rkisp1/TODO @@ -1,4 +1,3 @@ -* Fix serialization on subdev ops. * Don't use v4l2_async_notifier_parse_fwnode_endpoints_by_port(). e.g. isp_parse_of_endpoints in drivers/media/platform/omap3isp/isp.c cio2_parse_firmware in drivers/media/pci/intel/ipu3/ipu3-cio2.c. diff --git a/drivers/staging/media/rkisp1/rkisp1-capture.c b/drivers/staging/media/rkisp1/rkisp1-capture.c index 524e0dd38c1b..24fe6a7888aa 100644 --- a/drivers/staging/media/rkisp1/rkisp1-capture.c +++ b/drivers/staging/media/rkisp1/rkisp1-capture.c @@ -937,10 +937,7 @@ static void rkisp1_vb2_stop_streaming(struct vb2_queue *queue) rkisp1_return_all_buffers(cap, VB2_BUF_STATE_ERROR); - ret = v4l2_pipeline_pm_use(&node->vdev.entity, 0); - if (ret) - dev_err(rkisp1->dev, "pipeline close failed error:%d\n", ret); - + v4l2_pipeline_pm_put(&node->vdev.entity); ret = pm_runtime_put(rkisp1->dev); if (ret) dev_err(rkisp1->dev, "power down failed error:%d\n", ret); @@ -999,7 +996,7 @@ rkisp1_vb2_start_streaming(struct vb2_queue *queue, unsigned int count) dev_err(cap->rkisp1->dev, "power up failed %d\n", ret); goto err_destroy_dummy; } - ret = v4l2_pipeline_pm_use(entity, 1); + ret = v4l2_pipeline_pm_get(entity); if (ret) { dev_err(cap->rkisp1->dev, "open cif pipeline failed %d\n", ret); goto err_pipe_pm_put; @@ -1025,7 +1022,7 @@ err_pipe_disable: rkisp1_pipeline_sink_walk(entity, NULL, rkisp1_pipeline_disable_cb); err_stop_stream: rkisp1_stream_stop(cap); - v4l2_pipeline_pm_use(entity, 0); + v4l2_pipeline_pm_put(entity); err_pipe_pm_put: pm_runtime_put(cap->rkisp1->dev); err_destroy_dummy: @@ -1344,7 +1341,7 @@ static int rkisp1_register_capture(struct rkisp1_capture *cap) q = &node->buf_queue; q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; - q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_USERPTR; + q->io_modes = VB2_MMAP | VB2_DMABUF; q->drv_priv = cap; q->ops = &rkisp1_vb2_ops; q->mem_ops = &vb2_dma_contig_memops; @@ -1362,7 +1359,7 @@ static int rkisp1_register_capture(struct rkisp1_capture *cap) vdev->queue = q; - ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret) { dev_err(cap->rkisp1->dev, "failed to register %s, ret=%d\n", vdev->name, ret); diff --git a/drivers/staging/media/rkisp1/rkisp1-common.h b/drivers/staging/media/rkisp1/rkisp1-common.h index 369a401b098a..b291cc60de8e 100644 --- a/drivers/staging/media/rkisp1/rkisp1-common.h +++ b/drivers/staging/media/rkisp1/rkisp1-common.h @@ -96,6 +96,7 @@ struct rkisp1_sensor_async { * @sink_crop: crop for sink pad * @src_fmt: output format * @src_crop: output size + * @ops_lock: ops serialization * * @is_dphy_errctrl_disabled : if dphy errctrl is disabled (avoid endless interrupt) * @frame_sequence: used to synchronize frame_id between video devices. @@ -107,6 +108,7 @@ struct rkisp1_isp { struct v4l2_subdev_pad_config pad_cfg[RKISP1_ISP_PAD_MAX]; const struct rkisp1_isp_mbus_info *sink_fmt; const struct rkisp1_isp_mbus_info *src_fmt; + struct mutex ops_lock; bool is_dphy_errctrl_disabled; atomic_t frame_sequence; }; @@ -224,6 +226,7 @@ struct rkisp1_resizer { struct v4l2_subdev_pad_config pad_cfg[RKISP1_ISP_PAD_MAX]; const struct rkisp1_rsz_config *config; enum rkisp1_fmt_pix_type fmt_type; + struct mutex ops_lock; }; struct rkisp1_debug { diff --git a/drivers/staging/media/rkisp1/rkisp1-dev.c b/drivers/staging/media/rkisp1/rkisp1-dev.c index 558126e66465..b1b3c058e957 100644 --- a/drivers/staging/media/rkisp1/rkisp1-dev.c +++ b/drivers/staging/media/rkisp1/rkisp1-dev.c @@ -128,7 +128,7 @@ static int rkisp1_create_links(struct rkisp1_device *rkisp1) ret = media_entity_get_fwnode_pad(&sd->entity, sd->fwnode, MEDIA_PAD_FL_SOURCE); - if (ret) { + if (ret < 0) { dev_err(sd->dev, "failed to find src pad for %s\n", sd->name); return ret; @@ -145,14 +145,15 @@ static int rkisp1_create_links(struct rkisp1_device *rkisp1) flags = 0; } - flags = MEDIA_LNK_FL_ENABLED; + flags = MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE; /* create ISP->RSZ->CAP links */ for (i = 0; i < 2; i++) { source = &rkisp1->isp.sd.entity; sink = &rkisp1->resizer_devs[i].sd.entity; ret = media_create_pad_link(source, RKISP1_ISP_PAD_SOURCE_VIDEO, - sink, RKISP1_RSZ_PAD_SINK, flags); + sink, RKISP1_RSZ_PAD_SINK, + MEDIA_LNK_FL_ENABLED); if (ret) return ret; @@ -219,19 +220,17 @@ static int rkisp1_subdev_notifier_complete(struct v4l2_async_notifier *notifier) container_of(notifier, struct rkisp1_device, notifier); int ret; - mutex_lock(&rkisp1->media_dev.graph_mutex); ret = rkisp1_create_links(rkisp1); if (ret) - goto unlock; + return ret; + ret = v4l2_device_register_subdev_nodes(&rkisp1->v4l2_dev); if (ret) - goto unlock; + return ret; dev_dbg(rkisp1->dev, "Async subdev notifier completed\n"); -unlock: - mutex_unlock(&rkisp1->media_dev.graph_mutex); - return ret; + return 0; } static int rkisp1_fwnode_parse(struct device *dev, @@ -502,8 +501,7 @@ static int rkisp1_probe(struct platform_device *pdev) strscpy(rkisp1->media_dev.model, RKISP1_DRIVER_NAME, sizeof(rkisp1->media_dev.model)); rkisp1->media_dev.dev = &pdev->dev; - strscpy(rkisp1->media_dev.bus_info, - "platform: " RKISP1_DRIVER_NAME, + strscpy(rkisp1->media_dev.bus_info, RKISP1_BUS_INFO, sizeof(rkisp1->media_dev.bus_info)); media_device_init(&rkisp1->media_dev); diff --git a/drivers/staging/media/rkisp1/rkisp1-isp.c b/drivers/staging/media/rkisp1/rkisp1-isp.c index 328c7ea60971..fa53f05e37d8 100644 --- a/drivers/staging/media/rkisp1/rkisp1-isp.c +++ b/drivers/staging/media/rkisp1/rkisp1-isp.c @@ -28,9 +28,9 @@ #define RKISP1_DIR_SINK_SRC (RKISP1_DIR_SINK | RKISP1_DIR_SRC) /* - * NOTE: MIPI controller and input MUX are also configured in this file, - * because ISP Subdev is not only describe ISP submodule(input size,format, - * output size, format), but also a virtual route device. + * NOTE: MIPI controller and input MUX are also configured in this file. + * This is because ISP Subdev describes not only ISP submodule (input size, + * format, output size, format), but also a virtual route device. */ /* @@ -504,7 +504,7 @@ static int rkisp1_config_cif(struct rkisp1_device *rkisp1) return 0; } -static int rkisp1_isp_stop(struct rkisp1_device *rkisp1) +static void rkisp1_isp_stop(struct rkisp1_device *rkisp1) { u32 val; @@ -540,8 +540,6 @@ static int rkisp1_isp_stop(struct rkisp1_device *rkisp1) RKISP1_CIF_IRCL_MIPI_SW_RST | RKISP1_CIF_IRCL_ISP_SW_RST, RKISP1_CIF_IRCL); rkisp1_write(rkisp1, 0x0, RKISP1_CIF_IRCL); - - return 0; } static void rkisp1_config_clk(struct rkisp1_device *rkisp1) @@ -555,7 +553,7 @@ static void rkisp1_config_clk(struct rkisp1_device *rkisp1) rkisp1_write(rkisp1, val, RKISP1_CIF_ICCL); } -static int rkisp1_isp_start(struct rkisp1_device *rkisp1) +static void rkisp1_isp_start(struct rkisp1_device *rkisp1) { struct rkisp1_sensor_async *sensor = rkisp1->active_sensor; u32 val; @@ -580,8 +578,6 @@ static int rkisp1_isp_start(struct rkisp1_device *rkisp1) * the MIPI interface and before starting the sensor output. */ usleep_range(1000, 1200); - - return 0; } /* ---------------------------------------------------------------------------- @@ -683,7 +679,7 @@ static void rkisp1_isp_set_src_fmt(struct rkisp1_isp *isp, src_fmt->code = format->code; mbus_info = rkisp1_isp_mbus_info_get(src_fmt->code); - if (!mbus_info) { + if (!mbus_info || !(mbus_info->direction & RKISP1_DIR_SRC)) { src_fmt->code = RKISP1_DEF_SRC_PAD_FMT; mbus_info = rkisp1_isp_mbus_info_get(src_fmt->code); } @@ -767,7 +763,7 @@ static void rkisp1_isp_set_sink_fmt(struct rkisp1_isp *isp, which); sink_fmt->code = format->code; mbus_info = rkisp1_isp_mbus_info_get(sink_fmt->code); - if (!mbus_info) { + if (!mbus_info || !(mbus_info->direction & RKISP1_DIR_SINK)) { sink_fmt->code = RKISP1_DEF_SINK_PAD_FMT; mbus_info = rkisp1_isp_mbus_info_get(sink_fmt->code); } @@ -795,7 +791,9 @@ static int rkisp1_isp_get_fmt(struct v4l2_subdev *sd, { struct rkisp1_isp *isp = container_of(sd, struct rkisp1_isp, sd); + mutex_lock(&isp->ops_lock); fmt->format = *rkisp1_isp_get_pad_fmt(isp, cfg, fmt->pad, fmt->which); + mutex_unlock(&isp->ops_lock); return 0; } @@ -805,6 +803,7 @@ static int rkisp1_isp_set_fmt(struct v4l2_subdev *sd, { struct rkisp1_isp *isp = container_of(sd, struct rkisp1_isp, sd); + mutex_lock(&isp->ops_lock); if (fmt->pad == RKISP1_ISP_PAD_SINK_VIDEO) rkisp1_isp_set_sink_fmt(isp, cfg, &fmt->format, fmt->which); else if (fmt->pad == RKISP1_ISP_PAD_SOURCE_VIDEO) @@ -813,6 +812,7 @@ static int rkisp1_isp_set_fmt(struct v4l2_subdev *sd, fmt->format = *rkisp1_isp_get_pad_fmt(isp, cfg, fmt->pad, fmt->which); + mutex_unlock(&isp->ops_lock); return 0; } @@ -821,11 +821,13 @@ static int rkisp1_isp_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_selection *sel) { struct rkisp1_isp *isp = container_of(sd, struct rkisp1_isp, sd); + int ret = 0; if (sel->pad != RKISP1_ISP_PAD_SOURCE_VIDEO && sel->pad != RKISP1_ISP_PAD_SINK_VIDEO) return -EINVAL; + mutex_lock(&isp->ops_lock); switch (sel->target) { case V4L2_SEL_TGT_CROP_BOUNDS: if (sel->pad == RKISP1_ISP_PAD_SINK_VIDEO) { @@ -848,10 +850,10 @@ static int rkisp1_isp_get_selection(struct v4l2_subdev *sd, sel->which); break; default: - return -EINVAL; + ret = -EINVAL; } - - return 0; + mutex_unlock(&isp->ops_lock); + return ret; } static int rkisp1_isp_set_selection(struct v4l2_subdev *sd, @@ -861,21 +863,23 @@ static int rkisp1_isp_set_selection(struct v4l2_subdev *sd, struct rkisp1_device *rkisp1 = container_of(sd->v4l2_dev, struct rkisp1_device, v4l2_dev); struct rkisp1_isp *isp = container_of(sd, struct rkisp1_isp, sd); + int ret = 0; if (sel->target != V4L2_SEL_TGT_CROP) return -EINVAL; dev_dbg(rkisp1->dev, "%s: pad: %d sel(%d,%d)/%dx%d\n", __func__, sel->pad, sel->r.left, sel->r.top, sel->r.width, sel->r.height); - + mutex_lock(&isp->ops_lock); if (sel->pad == RKISP1_ISP_PAD_SINK_VIDEO) rkisp1_isp_set_sink_crop(isp, cfg, &sel->r, sel->which); else if (sel->pad == RKISP1_ISP_PAD_SOURCE_VIDEO) rkisp1_isp_set_src_crop(isp, cfg, &sel->r, sel->which); else - return -EINVAL; + ret = -EINVAL; - return 0; + mutex_unlock(&isp->ops_lock); + return ret; } static int rkisp1_subdev_link_validate(struct media_link *link) @@ -936,13 +940,12 @@ static int rkisp1_isp_s_stream(struct v4l2_subdev *sd, int enable) { struct rkisp1_device *rkisp1 = container_of(sd->v4l2_dev, struct rkisp1_device, v4l2_dev); + struct rkisp1_isp *isp = &rkisp1->isp; struct v4l2_subdev *sensor_sd; int ret = 0; if (!enable) { - ret = rkisp1_isp_stop(rkisp1); - if (ret) - return ret; + rkisp1_isp_stop(rkisp1); rkisp1_mipi_csi2_stop(rkisp1->active_sensor); return 0; } @@ -953,22 +956,23 @@ static int rkisp1_isp_s_stream(struct v4l2_subdev *sd, int enable) rkisp1->active_sensor = container_of(sensor_sd->asd, struct rkisp1_sensor_async, asd); + if (rkisp1->active_sensor->mbus.type != V4L2_MBUS_CSI2_DPHY) + return -EINVAL; + atomic_set(&rkisp1->isp.frame_sequence, -1); + mutex_lock(&isp->ops_lock); ret = rkisp1_config_cif(rkisp1); if (ret) - return ret; - - if (rkisp1->active_sensor->mbus.type != V4L2_MBUS_CSI2_DPHY) - return -EINVAL; + goto mutex_unlock; ret = rkisp1_mipi_csi2_start(&rkisp1->isp, rkisp1->active_sensor); if (ret) - return ret; + goto mutex_unlock; - ret = rkisp1_isp_start(rkisp1); - if (ret) - rkisp1_mipi_csi2_stop(rkisp1->active_sensor); + rkisp1_isp_start(rkisp1); +mutex_unlock: + mutex_unlock(&isp->ops_lock); return ret; } @@ -1028,6 +1032,7 @@ int rkisp1_isp_register(struct rkisp1_device *rkisp1, isp->sink_fmt = rkisp1_isp_mbus_info_get(RKISP1_DEF_SINK_PAD_FMT); isp->src_fmt = rkisp1_isp_mbus_info_get(RKISP1_DEF_SRC_PAD_FMT); + mutex_init(&isp->ops_lock); ret = media_entity_pads_init(&sd->entity, RKISP1_ISP_PAD_MAX, pads); if (ret) return ret; diff --git a/drivers/staging/media/rkisp1/rkisp1-params.c b/drivers/staging/media/rkisp1/rkisp1-params.c index 781f0ca85af1..44d542caf32b 100644 --- a/drivers/staging/media/rkisp1/rkisp1-params.c +++ b/drivers/staging/media/rkisp1/rkisp1-params.c @@ -1605,7 +1605,7 @@ int rkisp1_params_register(struct rkisp1_params *params, ret = media_entity_pads_init(&vdev->entity, 1, &node->pad); if (ret) goto err_release_queue; - ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret) { dev_err(&vdev->dev, "failed to register %s, ret=%d\n", vdev->name, ret); diff --git a/drivers/staging/media/rkisp1/rkisp1-resizer.c b/drivers/staging/media/rkisp1/rkisp1-resizer.c index 8cdc29c1a178..87799fbf0363 100644 --- a/drivers/staging/media/rkisp1/rkisp1-resizer.c +++ b/drivers/staging/media/rkisp1/rkisp1-resizer.c @@ -503,6 +503,8 @@ static void rkisp1_rsz_set_sink_crop(struct rkisp1_resizer *rsz, sink_crop->top = 0; sink_crop->width = sink_fmt->width; sink_crop->height = sink_fmt->height; + + *r = *sink_crop; return; } @@ -537,15 +539,6 @@ static void rkisp1_rsz_set_sink_fmt(struct rkisp1_resizer *rsz, if (which == V4L2_SUBDEV_FORMAT_ACTIVE) rsz->fmt_type = mbus_info->fmt_type; - if (rsz->id == RKISP1_MAINPATH && - mbus_info->fmt_type == RKISP1_FMT_BAYER) { - sink_crop->left = 0; - sink_crop->top = 0; - sink_crop->width = sink_fmt->width; - sink_crop->height = sink_fmt->height; - return; - } - /* Propagete to source pad */ src_fmt->code = sink_fmt->code; @@ -569,7 +562,9 @@ static int rkisp1_rsz_get_fmt(struct v4l2_subdev *sd, struct rkisp1_resizer *rsz = container_of(sd, struct rkisp1_resizer, sd); + mutex_lock(&rsz->ops_lock); fmt->format = *rkisp1_rsz_get_pad_fmt(rsz, cfg, fmt->pad, fmt->which); + mutex_unlock(&rsz->ops_lock); return 0; } @@ -580,11 +575,13 @@ static int rkisp1_rsz_set_fmt(struct v4l2_subdev *sd, struct rkisp1_resizer *rsz = container_of(sd, struct rkisp1_resizer, sd); + mutex_lock(&rsz->ops_lock); if (fmt->pad == RKISP1_RSZ_PAD_SINK) rkisp1_rsz_set_sink_fmt(rsz, cfg, &fmt->format, fmt->which); else rkisp1_rsz_set_src_fmt(rsz, cfg, &fmt->format, fmt->which); + mutex_unlock(&rsz->ops_lock); return 0; } @@ -595,10 +592,12 @@ static int rkisp1_rsz_get_selection(struct v4l2_subdev *sd, struct rkisp1_resizer *rsz = container_of(sd, struct rkisp1_resizer, sd); struct v4l2_mbus_framefmt *mf_sink; + int ret = 0; if (sel->pad == RKISP1_RSZ_PAD_SRC) return -EINVAL; + mutex_lock(&rsz->ops_lock); switch (sel->target) { case V4L2_SEL_TGT_CROP_BOUNDS: mf_sink = rkisp1_rsz_get_pad_fmt(rsz, cfg, RKISP1_RSZ_PAD_SINK, @@ -613,10 +612,11 @@ static int rkisp1_rsz_get_selection(struct v4l2_subdev *sd, sel->which); break; default: - return -EINVAL; + ret = -EINVAL; } - return 0; + mutex_unlock(&rsz->ops_lock); + return ret; } static int rkisp1_rsz_set_selection(struct v4l2_subdev *sd, @@ -632,7 +632,9 @@ static int rkisp1_rsz_set_selection(struct v4l2_subdev *sd, dev_dbg(sd->dev, "%s: pad: %d sel(%d,%d)/%dx%d\n", __func__, sel->pad, sel->r.left, sel->r.top, sel->r.width, sel->r.height); + mutex_lock(&rsz->ops_lock); rkisp1_rsz_set_sink_crop(rsz, cfg, &sel->r, sel->which); + mutex_unlock(&rsz->ops_lock); return 0; } @@ -672,9 +674,11 @@ static int rkisp1_rsz_s_stream(struct v4l2_subdev *sd, int enable) if (other->is_streaming) when = RKISP1_SHADOW_REGS_ASYNC; + mutex_lock(&rsz->ops_lock); rkisp1_rsz_config(rsz, when); rkisp1_dcrop_config(rsz); + mutex_unlock(&rsz->ops_lock); return 0; } @@ -720,6 +724,7 @@ static int rkisp1_rsz_register(struct rkisp1_resizer *rsz) rsz->fmt_type = RKISP1_DEF_FMT_TYPE; + mutex_init(&rsz->ops_lock); ret = media_entity_pads_init(&sd->entity, 2, pads); if (ret) return ret; diff --git a/drivers/staging/media/rkisp1/rkisp1-stats.c b/drivers/staging/media/rkisp1/rkisp1-stats.c index d98ea15837de..6dfcbdc3deb8 100644 --- a/drivers/staging/media/rkisp1/rkisp1-stats.c +++ b/drivers/staging/media/rkisp1/rkisp1-stats.c @@ -70,8 +70,7 @@ static int rkisp1_stats_querycap(struct file *file, strscpy(cap->driver, RKISP1_DRIVER_NAME, sizeof(cap->driver)); strscpy(cap->card, vdev->name, sizeof(cap->card)); - strscpy(cap->bus_info, "platform: " RKISP1_DRIVER_NAME, - sizeof(cap->bus_info)); + strscpy(cap->bus_info, RKISP1_BUS_INFO, sizeof(cap->bus_info)); return 0; } @@ -487,7 +486,7 @@ int rkisp1_stats_register(struct rkisp1_stats *stats, if (ret) goto err_release_queue; - ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret) { dev_err(&vdev->dev, "failed to register %s, ret=%d\n", vdev->name, ret); diff --git a/drivers/staging/media/soc_camera/soc_camera.c b/drivers/staging/media/soc_camera/soc_camera.c index 7b9448e3c9ba..39f513f69b89 100644 --- a/drivers/staging/media/soc_camera/soc_camera.c +++ b/drivers/staging/media/soc_camera/soc_camera.c @@ -2068,7 +2068,7 @@ static int soc_camera_video_start(struct soc_camera_device *icd) v4l2_disable_ioctl(icd->vdev, VIDIOC_S_STD); v4l2_disable_ioctl(icd->vdev, VIDIOC_ENUMSTD); } - ret = video_register_device(icd->vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(icd->vdev, VFL_TYPE_VIDEO, -1); if (ret < 0) { dev_err(icd->pdev, "video_register_device failed: %d\n", ret); return ret; diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.c b/drivers/staging/media/sunxi/cedrus/cedrus.c index c6ddd46eff82..05a85517ff60 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus.c @@ -414,7 +414,7 @@ static int cedrus_probe(struct platform_device *pdev) dev->mdev.ops = &cedrus_m2m_media_ops; dev->v4l2_dev.mdev = &dev->mdev; - ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); + ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); if (ret) { v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); goto err_m2m; diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_h264.c b/drivers/staging/media/sunxi/cedrus/cedrus_h264.c index bfb4a4820a67..54ee2aa423e2 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_h264.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_h264.c @@ -610,8 +610,12 @@ static int cedrus_h264_start(struct cedrus_ctx *ctx) goto err_mv_col_buf; } + /* + * NOTE: Multiplying by two deviates from CedarX logic, but it + * is for some unknown reason needed for H264 4K decoding on H6. + */ ctx->codec.h264.intra_pred_buf_size = - ALIGN(ctx->src_fmt.width, 64) * 5; + ALIGN(ctx->src_fmt.width, 64) * 5 * 2; ctx->codec.h264.intra_pred_buf = dma_alloc_coherent(dev->dev, ctx->codec.h264.intra_pred_buf_size, diff --git a/drivers/staging/media/tegra-vde/vde.c b/drivers/staging/media/tegra-vde/vde.c index e18fd48981da..d3e63512a765 100644 --- a/drivers/staging/media/tegra-vde/vde.c +++ b/drivers/staging/media/tegra-vde/vde.c @@ -949,7 +949,6 @@ static int tegra_vde_runtime_resume(struct device *dev) static int tegra_vde_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct resource *regs; struct tegra_vde *vde; int irq, err; @@ -959,75 +958,39 @@ static int tegra_vde_probe(struct platform_device *pdev) platform_set_drvdata(pdev, vde); - regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sxe"); - if (!regs) - return -ENODEV; - - vde->sxe = devm_ioremap_resource(dev, regs); + vde->sxe = devm_platform_ioremap_resource_byname(pdev, "sxe"); if (IS_ERR(vde->sxe)) return PTR_ERR(vde->sxe); - regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "bsev"); - if (!regs) - return -ENODEV; - - vde->bsev = devm_ioremap_resource(dev, regs); + vde->bsev = devm_platform_ioremap_resource_byname(pdev, "bsev"); if (IS_ERR(vde->bsev)) return PTR_ERR(vde->bsev); - regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mbe"); - if (!regs) - return -ENODEV; - - vde->mbe = devm_ioremap_resource(dev, regs); + vde->mbe = devm_platform_ioremap_resource_byname(pdev, "mbe"); if (IS_ERR(vde->mbe)) return PTR_ERR(vde->mbe); - regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ppe"); - if (!regs) - return -ENODEV; - - vde->ppe = devm_ioremap_resource(dev, regs); + vde->ppe = devm_platform_ioremap_resource_byname(pdev, "ppe"); if (IS_ERR(vde->ppe)) return PTR_ERR(vde->ppe); - regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mce"); - if (!regs) - return -ENODEV; - - vde->mce = devm_ioremap_resource(dev, regs); + vde->mce = devm_platform_ioremap_resource_byname(pdev, "mce"); if (IS_ERR(vde->mce)) return PTR_ERR(vde->mce); - regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tfe"); - if (!regs) - return -ENODEV; - - vde->tfe = devm_ioremap_resource(dev, regs); + vde->tfe = devm_platform_ioremap_resource_byname(pdev, "tfe"); if (IS_ERR(vde->tfe)) return PTR_ERR(vde->tfe); - regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ppb"); - if (!regs) - return -ENODEV; - - vde->ppb = devm_ioremap_resource(dev, regs); + vde->ppb = devm_platform_ioremap_resource_byname(pdev, "ppb"); if (IS_ERR(vde->ppb)) return PTR_ERR(vde->ppb); - regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vdma"); - if (!regs) - return -ENODEV; - - vde->vdma = devm_ioremap_resource(dev, regs); + vde->vdma = devm_platform_ioremap_resource_byname(pdev, "vdma"); if (IS_ERR(vde->vdma)) return PTR_ERR(vde->vdma); - regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "frameid"); - if (!regs) - return -ENODEV; - - vde->frameid = devm_ioremap_resource(dev, regs); + vde->frameid = devm_platform_ioremap_resource_byname(pdev, "frameid"); if (IS_ERR(vde->frameid)) return PTR_ERR(vde->frameid); diff --git a/drivers/staging/media/usbvision/Kconfig b/drivers/staging/media/usbvision/Kconfig new file mode 100644 index 000000000000..c6e1afb5ac48 --- /dev/null +++ b/drivers/staging/media/usbvision/Kconfig @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0-only +config VIDEO_USBVISION + tristate "USB video devices based on Nogatech NT1003/1004/1005 (Deprecated)" + depends on MEDIA_USB_SUPPORT && I2C && VIDEO_V4L2 + select VIDEO_TUNER + select VIDEO_SAA711X if MEDIA_SUBDRV_AUTOSELECT + help + There are more than 50 different USB video devices based on + NT1003/1004/1005 USB Bridges. This driver enables using those + devices. + + This driver is deprecated and scheduled for removal by the + end of 2020. See the TODO file in drivers/staging/media/usbvision + for a list of actions that have to be done in order to prevent + removal of this driver. + + To compile this driver as a module, choose M here: the + module will be called usbvision. diff --git a/drivers/staging/media/usbvision/Makefile b/drivers/staging/media/usbvision/Makefile new file mode 100644 index 000000000000..4d8541b9d4f8 --- /dev/null +++ b/drivers/staging/media/usbvision/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only +usbvision-objs := usbvision-core.o usbvision-video.o usbvision-i2c.o usbvision-cards.o + +obj-$(CONFIG_VIDEO_USBVISION) += usbvision.o diff --git a/drivers/staging/media/usbvision/TODO b/drivers/staging/media/usbvision/TODO new file mode 100644 index 000000000000..e9fb4d125581 --- /dev/null +++ b/drivers/staging/media/usbvision/TODO @@ -0,0 +1,11 @@ +The driver is deprecated and scheduled for removal by the end +of 2020. + +In order to prevent removal the following actions would have to +be taken: + +- clean up the code +- convert to the vb2 framework +- fix the disconnect and free-on-last-user handling (i.e., add + a release callback for struct v4l2_device and rework the code + to use that correctly). diff --git a/drivers/staging/media/usbvision/usbvision-cards.c b/drivers/staging/media/usbvision/usbvision-cards.c new file mode 100644 index 000000000000..5e0cbbfe7c86 --- /dev/null +++ b/drivers/staging/media/usbvision/usbvision-cards.c @@ -0,0 +1,1120 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * usbvision-cards.c + * usbvision cards definition file + * + * Copyright (c) 1999-2005 Joerg Heckenbach <joerg@heckenbach-aw.de> + * + * This module is part of usbvision driver project. + * Updates to driver completed by Dwaine P. Garden + */ + + +#include <linux/list.h> +#include <linux/module.h> +#include <media/v4l2-dev.h> +#include <media/tuner.h> +#include "usbvision.h" +#include "usbvision-cards.h" + +/* Supported Devices: A table for usbvision.c*/ +struct usbvision_device_data_st usbvision_device_data[] = { + [XANBOO] = { + .interface = -1, + .codec = CODEC_SAA7113, + .video_channels = 4, + .video_norm = V4L2_STD_NTSC, + .audio_channels = 1, + .radio = 0, + .vbi = 1, + .tuner = 0, + .tuner_type = 0, + .x_offset = -1, + .y_offset = -1, + .model_string = "Xanboo", + }, + [BELKIN_VIDEOBUS_II] = { + .interface = -1, + .codec = CODEC_SAA7113, + .video_channels = 2, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 0, + .vbi = 1, + .tuner = 0, + .tuner_type = 0, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Belkin USB VideoBus II Adapter", + }, + [BELKIN_VIDEOBUS] = { + .interface = -1, + .codec = CODEC_SAA7111, + .video_channels = 2, + .video_norm = V4L2_STD_NTSC, + .audio_channels = 1, + .radio = 0, + .vbi = 1, + .tuner = 0, + .tuner_type = 0, + .x_offset = -1, + .y_offset = -1, + .model_string = "Belkin Components USB VideoBus", + }, + [BELKIN_USB_VIDEOBUS_II] = { + .interface = -1, + .codec = CODEC_SAA7113, + .video_channels = 2, + .video_norm = V4L2_STD_NTSC, + .audio_channels = 1, + .radio = 0, + .vbi = 1, + .tuner = 0, + .tuner_type = 0, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Belkin USB VideoBus II", + }, + [ECHOFX_INTERVIEW_LITE] = { + .interface = 0, + .codec = CODEC_SAA7111, + .video_channels = 2, + .video_norm = V4L2_STD_PAL, + .audio_channels = 0, + .radio = 0, + .vbi = 1, + .tuner = 0, + .tuner_type = 0, + .x_offset = -1, + .y_offset = -1, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "echoFX InterView Lite", + }, + [USBGEAR_USBG_V1] = { + .interface = -1, + .codec = CODEC_SAA7111, + .video_channels = 2, + .video_norm = V4L2_STD_NTSC, + .audio_channels = 1, + .radio = 0, + .vbi = 1, + .tuner = 0, + .tuner_type = 0, + .x_offset = -1, + .y_offset = -1, + .model_string = "USBGear USBG-V1 resp. HAMA USB", + }, + [D_LINK_V100] = { + .interface = -1, + .codec = CODEC_SAA7113, + .video_channels = 4, + .video_norm = V4L2_STD_NTSC, + .audio_channels = 0, + .radio = 0, + .vbi = 1, + .tuner = 0, + .tuner_type = 0, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "D-Link V100", + }, + [X10_USB_CAMERA] = { + .interface = -1, + .codec = CODEC_SAA7111, + .video_channels = 2, + .video_norm = V4L2_STD_NTSC, + .audio_channels = 1, + .radio = 0, + .vbi = 1, + .tuner = 0, + .tuner_type = 0, + .x_offset = -1, + .y_offset = -1, + .model_string = "X10 USB Camera", + }, + [HPG_WINTV_LIVE_PAL_BG] = { + .interface = -1, + .codec = CODEC_SAA7111, + .video_channels = 2, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 0, + .vbi = 1, + .tuner = 0, + .tuner_type = 0, + .x_offset = -1, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Hauppauge WinTV USB Live (PAL B/G)", + }, + [HPG_WINTV_LIVE_PRO_NTSC_MN] = { + .interface = -1, + .codec = CODEC_SAA7113, + .video_channels = 2, + .video_norm = V4L2_STD_NTSC, + .audio_channels = 0, + .radio = 0, + .vbi = 1, + .tuner = 0, + .tuner_type = 0, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Hauppauge WinTV USB Live Pro (NTSC M/N)", + }, + [ZORAN_PMD_NOGATECH] = { + .interface = -1, + .codec = CODEC_SAA7113, + .video_channels = 2, + .video_norm = V4L2_STD_PAL, + .audio_channels = 2, + .radio = 0, + .vbi = 1, + .tuner = 0, + .tuner_type = 0, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Zoran Co. PMD (Nogatech) AV-grabber Manhattan", + }, + [NOGATECH_USB_TV_NTSC_FM] = { + .interface = -1, + .codec = CODEC_SAA7111, + .video_channels = 3, + .video_norm = V4L2_STD_NTSC, + .audio_channels = 1, + .radio = 1, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_NTSC_M, + .x_offset = -1, + .y_offset = 20, + .model_string = "Nogatech USB-TV (NTSC) FM", + }, + [PNY_USB_TV_NTSC_FM] = { + .interface = -1, + .codec = CODEC_SAA7111, + .video_channels = 3, + .video_norm = V4L2_STD_NTSC, + .audio_channels = 1, + .radio = 1, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_NTSC_M, + .x_offset = -1, + .y_offset = 20, + .model_string = "PNY USB-TV (NTSC) FM", + }, + [PV_PLAYTV_USB_PRO_PAL_FM] = { + .interface = 0, + .codec = CODEC_SAA7113, + .video_channels = 3, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 1, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_PAL, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "PixelView PlayTv-USB PRO (PAL) FM", + }, + [ZT_721] = { + .interface = 0, + .codec = CODEC_SAA7113, + .video_channels = 3, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 1, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_PAL, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "ZTV ZT-721 2.4GHz USB A/V Receiver", + }, + [HPG_WINTV_NTSC_MN] = { + .interface = -1, + .codec = CODEC_SAA7111, + .video_channels = 3, + .video_norm = V4L2_STD_NTSC, + .audio_channels = 1, + .radio = 0, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_NTSC_M, + .x_offset = -1, + .y_offset = 20, + .model_string = "Hauppauge WinTV USB (NTSC M/N)", + }, + [HPG_WINTV_PAL_BG] = { + .interface = -1, + .codec = CODEC_SAA7111, + .video_channels = 3, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 0, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_PAL, + .x_offset = -1, + .y_offset = -1, + .model_string = "Hauppauge WinTV USB (PAL B/G)", + }, + [HPG_WINTV_PAL_I] = { + .interface = -1, + .codec = CODEC_SAA7111, + .video_channels = 3, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 0, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_PAL, + .x_offset = -1, + .y_offset = -1, + .model_string = "Hauppauge WinTV USB (PAL I)", + }, + [HPG_WINTV_PAL_SECAM_L] = { + .interface = -1, + .codec = CODEC_SAA7111, + .video_channels = 3, + .video_norm = V4L2_STD_SECAM, + .audio_channels = 1, + .radio = 0, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_SECAM, + .x_offset = 0x80, + .y_offset = 0x16, + .model_string = "Hauppauge WinTV USB (PAL/SECAM L)", + }, + [HPG_WINTV_PAL_D_K] = { + .interface = -1, + .codec = CODEC_SAA7111, + .video_channels = 3, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 0, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_PAL, + .x_offset = -1, + .y_offset = -1, + .model_string = "Hauppauge WinTV USB (PAL D/K)", + }, + [HPG_WINTV_NTSC_FM] = { + .interface = -1, + .codec = CODEC_SAA7111, + .video_channels = 3, + .video_norm = V4L2_STD_NTSC, + .audio_channels = 1, + .radio = 1, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_NTSC_M, + .x_offset = -1, + .y_offset = -1, + .model_string = "Hauppauge WinTV USB (NTSC FM)", + }, + [HPG_WINTV_PAL_BG_FM] = { + .interface = -1, + .codec = CODEC_SAA7111, + .video_channels = 3, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 1, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_PAL, + .x_offset = -1, + .y_offset = -1, + .model_string = "Hauppauge WinTV USB (PAL B/G FM)", + }, + [HPG_WINTV_PAL_I_FM] = { + .interface = -1, + .codec = CODEC_SAA7111, + .video_channels = 3, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 1, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_PAL, + .x_offset = -1, + .y_offset = -1, + .model_string = "Hauppauge WinTV USB (PAL I FM)", + }, + [HPG_WINTV_PAL_D_K_FM] = { + .interface = -1, + .codec = CODEC_SAA7111, + .video_channels = 3, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 1, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_PAL, + .x_offset = -1, + .y_offset = -1, + .model_string = "Hauppauge WinTV USB (PAL D/K FM)", + }, + [HPG_WINTV_PRO_NTSC_MN] = { + .interface = 0, + .codec = CODEC_SAA7113, + .video_channels = 3, + .video_norm = V4L2_STD_NTSC, + .audio_channels = 1, + .radio = 1, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_MICROTUNE_4049FM5, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Hauppauge WinTV USB Pro (NTSC M/N)", + }, + [HPG_WINTV_PRO_NTSC_MN_V2] = { + .interface = 0, + .codec = CODEC_SAA7113, + .video_channels = 3, + .video_norm = V4L2_STD_NTSC, + .audio_channels = 1, + .radio = 1, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_MICROTUNE_4049FM5, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Hauppauge WinTV USB Pro (NTSC M/N) V2", + }, + [HPG_WINTV_PRO_PAL] = { + .interface = 0, + .codec = CODEC_SAA7113, + .video_channels = 3, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 0, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Hauppauge WinTV USB Pro (PAL/SECAM B/G/I/D/K/L)", + }, + [HPG_WINTV_PRO_NTSC_MN_V3] = { + .interface = 0, + .codec = CODEC_SAA7113, + .video_channels = 3, + .video_norm = V4L2_STD_NTSC, + .audio_channels = 1, + .radio = 1, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_NTSC_M, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Hauppauge WinTV USB Pro (NTSC M/N) V3", + }, + [HPG_WINTV_PRO_PAL_BG] = { + .interface = 0, + .codec = CODEC_SAA7113, + .video_channels = 3, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 0, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_PAL, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Hauppauge WinTV USB Pro (PAL B/G)", + }, + [HPG_WINTV_PRO_PAL_I] = { + .interface = 0, + .codec = CODEC_SAA7113, + .video_channels = 3, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 0, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_PAL, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Hauppauge WinTV USB Pro (PAL I)", + }, + [HPG_WINTV_PRO_PAL_SECAM_L] = { + .interface = -1, + .codec = CODEC_SAA7113, + .video_channels = 3, + .video_norm = V4L2_STD_SECAM, + .audio_channels = 1, + .radio = 0, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_SECAM, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Hauppauge WinTV USB Pro (PAL/SECAM L)", + }, + [HPG_WINTV_PRO_PAL_D_K] = { + .interface = -1, + .codec = CODEC_SAA7113, + .video_channels = 3, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 0, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_PAL, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Hauppauge WinTV USB Pro (PAL D/K)", + }, + [HPG_WINTV_PRO_PAL_SECAM] = { + .interface = -1, + .codec = CODEC_SAA7113, + .video_channels = 3, + .video_norm = V4L2_STD_SECAM, + .audio_channels = 1, + .radio = 0, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_SECAM, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Hauppauge WinTV USB Pro (PAL/SECAM BGDK/I/L)", + }, + [HPG_WINTV_PRO_PAL_SECAM_V2] = { + .interface = -1, + .codec = CODEC_SAA7113, + .video_channels = 3, + .video_norm = V4L2_STD_SECAM, + .audio_channels = 1, + .radio = 0, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_SECAM, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Hauppauge WinTV USB Pro (PAL/SECAM BGDK/I/L) V2", + }, + [HPG_WINTV_PRO_PAL_BG_V2] = { + .interface = -1, + .codec = CODEC_SAA7113, + .video_channels = 3, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 0, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_ALPS_TSBE1_PAL, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Hauppauge WinTV USB Pro (PAL B/G) V2", + }, + [HPG_WINTV_PRO_PAL_BG_D_K] = { + .interface = -1, + .codec = CODEC_SAA7113, + .video_channels = 3, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 0, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_ALPS_TSBE1_PAL, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Hauppauge WinTV USB Pro (PAL B/G,D/K)", + }, + [HPG_WINTV_PRO_PAL_I_D_K] = { + .interface = -1, + .codec = CODEC_SAA7113, + .video_channels = 3, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 0, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_LG_PAL_NEW_TAPC, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Hauppauge WinTV USB Pro (PAL I,D/K)", + }, + [HPG_WINTV_PRO_NTSC_MN_FM] = { + .interface = -1, + .codec = CODEC_SAA7113, + .video_channels = 3, + .video_norm = V4L2_STD_NTSC, + .audio_channels = 1, + .radio = 1, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_NTSC_M, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Hauppauge WinTV USB Pro (NTSC M/N FM)", + }, + [HPG_WINTV_PRO_PAL_BG_FM] = { + .interface = 0, + .codec = CODEC_SAA7113, + .video_channels = 3, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 1, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_PAL, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Hauppauge WinTV USB Pro (PAL B/G FM)", + }, + [HPG_WINTV_PRO_PAL_I_FM] = { + .interface = 0, + .codec = CODEC_SAA7113, + .video_channels = 3, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 1, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_PAL, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Hauppauge WinTV USB Pro (PAL I FM)", + }, + [HPG_WINTV_PRO_PAL_D_K_FM] = { + .interface = 0, + .codec = CODEC_SAA7113, + .video_channels = 3, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 1, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_PAL, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Hauppauge WinTV USB Pro (PAL D/K FM)", + }, + [HPG_WINTV_PRO_TEMIC_PAL_FM] = { + .interface = 0, + .codec = CODEC_SAA7113, + .video_channels = 3, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 1, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_MICROTUNE_4049FM5, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Hauppauge WinTV USB Pro (Temic PAL/SECAM B/G/I/D/K/L FM)", + }, + [HPG_WINTV_PRO_TEMIC_PAL_BG_FM] = { + .interface = 0, + .codec = CODEC_SAA7113, + .video_channels = 3, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 1, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_MICROTUNE_4049FM5, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Hauppauge WinTV USB Pro (Temic PAL B/G FM)", + }, + [HPG_WINTV_PRO_PAL_FM] = { + .interface = 0, + .codec = CODEC_SAA7113, + .video_channels = 3, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 1, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Hauppauge WinTV USB Pro (PAL/SECAM B/G/I/D/K/L FM)", + }, + [HPG_WINTV_PRO_NTSC_MN_FM_V2] = { + .interface = 0, + .codec = CODEC_SAA7113, + .video_channels = 3, + .video_norm = V4L2_STD_NTSC, + .audio_channels = 1, + .radio = 1, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_NTSC_M, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Hauppauge WinTV USB Pro (NTSC M/N FM) V2", + }, + [CAMTEL_TVB330] = { + .interface = -1, + .codec = CODEC_SAA7113, + .video_channels = 3, + .video_norm = V4L2_STD_NTSC, + .audio_channels = 1, + .radio = 1, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_NTSC_M, + .x_offset = 5, + .y_offset = 5, + .model_string = "Camtel Technology USB TV Genie Pro FM Model TVB330", + }, + [DIGITAL_VIDEO_CREATOR_I] = { + .interface = -1, + .codec = CODEC_SAA7113, + .video_channels = 2, + .video_norm = V4L2_STD_PAL, + .audio_channels = 0, + .radio = 0, + .vbi = 1, + .tuner = 0, + .tuner_type = 0, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Digital Video Creator I", + }, + [GLOBAL_VILLAGE_GV_007_NTSC] = { + .interface = -1, + .codec = CODEC_SAA7111, + .video_channels = 2, + .video_norm = V4L2_STD_NTSC, + .audio_channels = 0, + .radio = 0, + .vbi = 1, + .tuner = 0, + .tuner_type = 0, + .x_offset = 82, + .y_offset = 20, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Global Village GV-007 (NTSC)", + }, + [DAZZLE_DVC_50_REV_1_NTSC] = { + .interface = 0, + .codec = CODEC_SAA7113, + .video_channels = 2, + .video_norm = V4L2_STD_NTSC, + .audio_channels = 0, + .radio = 0, + .vbi = 1, + .tuner = 0, + .tuner_type = 0, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Dazzle Fusion Model DVC-50 Rev 1 (NTSC)", + }, + [DAZZLE_DVC_80_REV_1_PAL] = { + .interface = 0, + .codec = CODEC_SAA7113, + .video_channels = 2, + .video_norm = V4L2_STD_PAL, + .audio_channels = 0, + .radio = 0, + .vbi = 1, + .tuner = 0, + .tuner_type = 0, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Dazzle Fusion Model DVC-80 Rev 1 (PAL)", + }, + [DAZZLE_DVC_90_REV_1_SECAM] = { + .interface = 0, + .codec = CODEC_SAA7113, + .video_channels = 2, + .video_norm = V4L2_STD_SECAM, + .audio_channels = 0, + .radio = 0, + .vbi = 1, + .tuner = 0, + .tuner_type = 0, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Dazzle Fusion Model DVC-90 Rev 1 (SECAM)", + }, + [ESKAPE_LABS_MYTV2GO] = { + .interface = 0, + .codec = CODEC_SAA7113, + .video_channels = 2, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 1, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Eskape Labs MyTV2Go", + }, + [PINNA_PCTV_USB_PAL] = { + .interface = -1, + .codec = CODEC_SAA7111, + .video_channels = 3, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 0, + .vbi = 0, + .tuner = 1, + .tuner_type = TUNER_TEMIC_4066FY5_PAL_I, + .x_offset = -1, + .y_offset = -1, + .model_string = "Pinnacle Studio PCTV USB (PAL)", + }, + [PINNA_PCTV_USB_SECAM] = { + .interface = -1, + .codec = CODEC_SAA7111, + .video_channels = 3, + .video_norm = V4L2_STD_SECAM, + .audio_channels = 1, + .radio = 0, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_SECAM, + .x_offset = -1, + .y_offset = -1, + .model_string = "Pinnacle Studio PCTV USB (SECAM)", + }, + [PINNA_PCTV_USB_PAL_FM] = { + .interface = -1, + .codec = CODEC_SAA7111, + .video_channels = 3, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 1, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_PAL, + .x_offset = 128, + .y_offset = 23, + .model_string = "Pinnacle Studio PCTV USB (PAL) FM", + }, + [MIRO_PCTV_USB] = { + .interface = -1, + .codec = CODEC_SAA7111, + .video_channels = 3, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 0, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_PAL, + .x_offset = -1, + .y_offset = -1, + .model_string = "Miro PCTV USB", + }, + [PINNA_PCTV_USB_NTSC_FM] = { + .interface = -1, + .codec = CODEC_SAA7111, + .video_channels = 3, + .video_norm = V4L2_STD_NTSC, + .audio_channels = 1, + .radio = 1, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_NTSC_M, + .x_offset = -1, + .y_offset = -1, + .model_string = "Pinnacle Studio PCTV USB (NTSC) FM", + }, + [PINNA_PCTV_USB_NTSC_FM_V3] = { + .interface = -1, + .codec = CODEC_SAA7111, + .video_channels = 3, + .video_norm = V4L2_STD_NTSC, + .audio_channels = 1, + .radio = 1, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_NTSC_M, + .x_offset = -1, + .y_offset = -1, + .model_string = "Pinnacle Studio PCTV USB (NTSC) FM V3", + }, + [PINNA_PCTV_USB_PAL_FM_V2] = { + .interface = -1, + .codec = CODEC_SAA7113, + .video_channels = 3, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 1, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_TEMIC_4009FR5_PAL, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Pinnacle Studio PCTV USB (PAL) FM V2", + }, + [PINNA_PCTV_USB_NTSC_FM_V2] = { + .interface = -1, + .codec = CODEC_SAA7111, + .video_channels = 3, + .video_norm = V4L2_STD_NTSC, + .audio_channels = 1, + .radio = 1, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_TEMIC_4039FR5_NTSC, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Pinnacle Studio PCTV USB (NTSC) FM V2", + }, + [PINNA_PCTV_USB_PAL_FM_V3] = { + .interface = -1, + .codec = CODEC_SAA7113, + .video_channels = 3, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 1, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_TEMIC_4009FR5_PAL, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Pinnacle Studio PCTV USB (PAL) FM V3", + }, + [PINNA_LINX_VD_IN_CAB_NTSC] = { + .interface = -1, + .codec = CODEC_SAA7113, + .video_channels = 2, + .video_norm = V4L2_STD_NTSC, + .audio_channels = 1, + .radio = 0, + .vbi = 1, + .tuner = 0, + .tuner_type = 0, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Pinnacle Studio Linx Video input cable (NTSC)", + }, + [PINNA_LINX_VD_IN_CAB_PAL] = { + .interface = -1, + .codec = CODEC_SAA7113, + .video_channels = 2, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 0, + .vbi = 1, + .tuner = 0, + .tuner_type = 0, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Pinnacle Studio Linx Video input cable (PAL)", + }, + [PINNA_PCTV_BUNGEE_PAL_FM] = { + .interface = -1, + .codec = CODEC_SAA7113, + .video_channels = 3, + .video_norm = V4L2_STD_PAL, + .audio_channels = 1, + .radio = 1, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_TEMIC_4009FR5_PAL, + .x_offset = 0, + .y_offset = 3, + .dvi_yuv_override = 1, + .dvi_yuv = 7, + .model_string = "Pinnacle PCTV Bungee USB (PAL) FM", + }, + [HPG_WINTV] = { + .interface = -1, + .codec = CODEC_SAA7111, + .video_channels = 3, + .video_norm = V4L2_STD_NTSC, + .audio_channels = 1, + .radio = 0, + .vbi = 1, + .tuner = 1, + .tuner_type = TUNER_PHILIPS_NTSC_M, + .x_offset = -1, + .y_offset = -1, + .model_string = "Hauppauge WinTv-USB", + }, + [MICROCAM_NTSC] = { + .interface = -1, + .codec = CODEC_WEBCAM, + .video_channels = 1, + .video_norm = V4L2_STD_NTSC, + .audio_channels = 0, + .radio = 0, + .vbi = 0, + .tuner = 0, + .tuner_type = 0, + .x_offset = 71, + .y_offset = 15, + .model_string = "Nogatech USB MicroCam NTSC (NV3000N)", + }, + [MICROCAM_PAL] = { + .interface = -1, + .codec = CODEC_WEBCAM, + .video_channels = 1, + .video_norm = V4L2_STD_PAL, + .audio_channels = 0, + .radio = 0, + .vbi = 0, + .tuner = 0, + .tuner_type = 0, + .x_offset = 71, + .y_offset = 18, + .model_string = "Nogatech USB MicroCam PAL (NV3001P)", + }, +}; +const int usbvision_device_data_size = ARRAY_SIZE(usbvision_device_data); + +/* Supported Devices */ + +struct usb_device_id usbvision_table[] = { + { USB_DEVICE(0x0a6f, 0x0400), .driver_info = XANBOO }, + { USB_DEVICE(0x050d, 0x0106), .driver_info = BELKIN_VIDEOBUS_II }, + { USB_DEVICE(0x050d, 0x0207), .driver_info = BELKIN_VIDEOBUS }, + { USB_DEVICE(0x050d, 0x0208), .driver_info = BELKIN_USB_VIDEOBUS_II }, + { USB_DEVICE(0x0571, 0x0002), .driver_info = ECHOFX_INTERVIEW_LITE }, + { USB_DEVICE(0x0573, 0x0003), .driver_info = USBGEAR_USBG_V1 }, + { USB_DEVICE(0x0573, 0x0400), .driver_info = D_LINK_V100 }, + { USB_DEVICE(0x0573, 0x2000), .driver_info = X10_USB_CAMERA }, + { USB_DEVICE(0x0573, 0x2d00), .driver_info = HPG_WINTV_LIVE_PAL_BG }, + { USB_DEVICE(0x0573, 0x2d01), .driver_info = HPG_WINTV_LIVE_PRO_NTSC_MN }, + { USB_DEVICE(0x0573, 0x2101), .driver_info = ZORAN_PMD_NOGATECH }, + { USB_DEVICE(0x0573, 0x3000), .driver_info = MICROCAM_NTSC }, + { USB_DEVICE(0x0573, 0x3001), .driver_info = MICROCAM_PAL }, + { USB_DEVICE(0x0573, 0x4100), .driver_info = NOGATECH_USB_TV_NTSC_FM }, + { USB_DEVICE(0x0573, 0x4110), .driver_info = PNY_USB_TV_NTSC_FM }, + { USB_DEVICE(0x0573, 0x4450), .driver_info = PV_PLAYTV_USB_PRO_PAL_FM }, + { USB_DEVICE(0x0573, 0x4550), .driver_info = ZT_721 }, + { USB_DEVICE(0x0573, 0x4d00), .driver_info = HPG_WINTV_NTSC_MN }, + { USB_DEVICE(0x0573, 0x4d01), .driver_info = HPG_WINTV_PAL_BG }, + { USB_DEVICE(0x0573, 0x4d02), .driver_info = HPG_WINTV_PAL_I }, + { USB_DEVICE(0x0573, 0x4d03), .driver_info = HPG_WINTV_PAL_SECAM_L }, + { USB_DEVICE(0x0573, 0x4d04), .driver_info = HPG_WINTV_PAL_D_K }, + { USB_DEVICE(0x0573, 0x4d10), .driver_info = HPG_WINTV_NTSC_FM }, + { USB_DEVICE(0x0573, 0x4d11), .driver_info = HPG_WINTV_PAL_BG_FM }, + { USB_DEVICE(0x0573, 0x4d12), .driver_info = HPG_WINTV_PAL_I_FM }, + { USB_DEVICE(0x0573, 0x4d14), .driver_info = HPG_WINTV_PAL_D_K_FM }, + { USB_DEVICE(0x0573, 0x4d2a), .driver_info = HPG_WINTV_PRO_NTSC_MN }, + { USB_DEVICE(0x0573, 0x4d2b), .driver_info = HPG_WINTV_PRO_NTSC_MN_V2 }, + { USB_DEVICE(0x0573, 0x4d2c), .driver_info = HPG_WINTV_PRO_PAL }, + { USB_DEVICE(0x0573, 0x4d20), .driver_info = HPG_WINTV_PRO_NTSC_MN_V3 }, + { USB_DEVICE(0x0573, 0x4d21), .driver_info = HPG_WINTV_PRO_PAL_BG }, + { USB_DEVICE(0x0573, 0x4d22), .driver_info = HPG_WINTV_PRO_PAL_I }, + { USB_DEVICE(0x0573, 0x4d23), .driver_info = HPG_WINTV_PRO_PAL_SECAM_L }, + { USB_DEVICE(0x0573, 0x4d24), .driver_info = HPG_WINTV_PRO_PAL_D_K }, + { USB_DEVICE(0x0573, 0x4d25), .driver_info = HPG_WINTV_PRO_PAL_SECAM }, + { USB_DEVICE(0x0573, 0x4d26), .driver_info = HPG_WINTV_PRO_PAL_SECAM_V2 }, + { USB_DEVICE(0x0573, 0x4d27), .driver_info = HPG_WINTV_PRO_PAL_BG_V2 }, + { USB_DEVICE(0x0573, 0x4d28), .driver_info = HPG_WINTV_PRO_PAL_BG_D_K }, + { USB_DEVICE(0x0573, 0x4d29), .driver_info = HPG_WINTV_PRO_PAL_I_D_K }, + { USB_DEVICE(0x0573, 0x4d30), .driver_info = HPG_WINTV_PRO_NTSC_MN_FM }, + { USB_DEVICE(0x0573, 0x4d31), .driver_info = HPG_WINTV_PRO_PAL_BG_FM }, + { USB_DEVICE(0x0573, 0x4d32), .driver_info = HPG_WINTV_PRO_PAL_I_FM }, + { USB_DEVICE(0x0573, 0x4d34), .driver_info = HPG_WINTV_PRO_PAL_D_K_FM }, + { USB_DEVICE(0x0573, 0x4d35), .driver_info = HPG_WINTV_PRO_TEMIC_PAL_FM }, + { USB_DEVICE(0x0573, 0x4d36), .driver_info = HPG_WINTV_PRO_TEMIC_PAL_BG_FM }, + { USB_DEVICE(0x0573, 0x4d37), .driver_info = HPG_WINTV_PRO_PAL_FM }, + { USB_DEVICE(0x0573, 0x4d38), .driver_info = HPG_WINTV_PRO_NTSC_MN_FM_V2 }, + { USB_DEVICE(0x0768, 0x0006), .driver_info = CAMTEL_TVB330 }, + { USB_DEVICE(0x07d0, 0x0001), .driver_info = DIGITAL_VIDEO_CREATOR_I }, + { USB_DEVICE(0x07d0, 0x0002), .driver_info = GLOBAL_VILLAGE_GV_007_NTSC }, + { USB_DEVICE(0x07d0, 0x0003), .driver_info = DAZZLE_DVC_50_REV_1_NTSC }, + { USB_DEVICE(0x07d0, 0x0004), .driver_info = DAZZLE_DVC_80_REV_1_PAL }, + { USB_DEVICE(0x07d0, 0x0005), .driver_info = DAZZLE_DVC_90_REV_1_SECAM }, + { USB_DEVICE(0x07f8, 0x9104), .driver_info = ESKAPE_LABS_MYTV2GO }, + { USB_DEVICE(0x2304, 0x010d), .driver_info = PINNA_PCTV_USB_PAL }, + { USB_DEVICE(0x2304, 0x0109), .driver_info = PINNA_PCTV_USB_SECAM }, + { USB_DEVICE(0x2304, 0x0110), .driver_info = PINNA_PCTV_USB_PAL_FM }, + { USB_DEVICE(0x2304, 0x0111), .driver_info = MIRO_PCTV_USB }, + { USB_DEVICE(0x2304, 0x0112), .driver_info = PINNA_PCTV_USB_NTSC_FM }, + { USB_DEVICE(0x2304, 0x0113), .driver_info = PINNA_PCTV_USB_NTSC_FM_V3 }, + { USB_DEVICE(0x2304, 0x0210), .driver_info = PINNA_PCTV_USB_PAL_FM_V2 }, + { USB_DEVICE(0x2304, 0x0212), .driver_info = PINNA_PCTV_USB_NTSC_FM_V2 }, + { USB_DEVICE(0x2304, 0x0214), .driver_info = PINNA_PCTV_USB_PAL_FM_V3 }, + { USB_DEVICE(0x2304, 0x0300), .driver_info = PINNA_LINX_VD_IN_CAB_NTSC }, + { USB_DEVICE(0x2304, 0x0301), .driver_info = PINNA_LINX_VD_IN_CAB_PAL }, + { USB_DEVICE(0x2304, 0x0419), .driver_info = PINNA_PCTV_BUNGEE_PAL_FM }, + { USB_DEVICE(0x2400, 0x4200), .driver_info = HPG_WINTV }, + { }, /* terminate list */ +}; + +MODULE_DEVICE_TABLE(usb, usbvision_table); diff --git a/drivers/staging/media/usbvision/usbvision-cards.h b/drivers/staging/media/usbvision/usbvision-cards.h new file mode 100644 index 000000000000..07ec83512743 --- /dev/null +++ b/drivers/staging/media/usbvision/usbvision-cards.h @@ -0,0 +1,70 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#define XANBOO 0 +#define BELKIN_VIDEOBUS_II 1 +#define BELKIN_VIDEOBUS 2 +#define BELKIN_USB_VIDEOBUS_II 3 +#define ECHOFX_INTERVIEW_LITE 4 +#define USBGEAR_USBG_V1 5 +#define D_LINK_V100 6 +#define X10_USB_CAMERA 7 +#define HPG_WINTV_LIVE_PAL_BG 8 +#define HPG_WINTV_LIVE_PRO_NTSC_MN 9 +#define ZORAN_PMD_NOGATECH 10 +#define NOGATECH_USB_TV_NTSC_FM 11 +#define PNY_USB_TV_NTSC_FM 12 +#define PV_PLAYTV_USB_PRO_PAL_FM 13 +#define ZT_721 14 +#define HPG_WINTV_NTSC_MN 15 +#define HPG_WINTV_PAL_BG 16 +#define HPG_WINTV_PAL_I 17 +#define HPG_WINTV_PAL_SECAM_L 18 +#define HPG_WINTV_PAL_D_K 19 +#define HPG_WINTV_NTSC_FM 20 +#define HPG_WINTV_PAL_BG_FM 21 +#define HPG_WINTV_PAL_I_FM 22 +#define HPG_WINTV_PAL_D_K_FM 23 +#define HPG_WINTV_PRO_NTSC_MN 24 +#define HPG_WINTV_PRO_NTSC_MN_V2 25 +#define HPG_WINTV_PRO_PAL 26 +#define HPG_WINTV_PRO_NTSC_MN_V3 27 +#define HPG_WINTV_PRO_PAL_BG 28 +#define HPG_WINTV_PRO_PAL_I 29 +#define HPG_WINTV_PRO_PAL_SECAM_L 30 +#define HPG_WINTV_PRO_PAL_D_K 31 +#define HPG_WINTV_PRO_PAL_SECAM 32 +#define HPG_WINTV_PRO_PAL_SECAM_V2 33 +#define HPG_WINTV_PRO_PAL_BG_V2 34 +#define HPG_WINTV_PRO_PAL_BG_D_K 35 +#define HPG_WINTV_PRO_PAL_I_D_K 36 +#define HPG_WINTV_PRO_NTSC_MN_FM 37 +#define HPG_WINTV_PRO_PAL_BG_FM 38 +#define HPG_WINTV_PRO_PAL_I_FM 39 +#define HPG_WINTV_PRO_PAL_D_K_FM 40 +#define HPG_WINTV_PRO_TEMIC_PAL_FM 41 +#define HPG_WINTV_PRO_TEMIC_PAL_BG_FM 42 +#define HPG_WINTV_PRO_PAL_FM 43 +#define HPG_WINTV_PRO_NTSC_MN_FM_V2 44 +#define CAMTEL_TVB330 45 +#define DIGITAL_VIDEO_CREATOR_I 46 +#define GLOBAL_VILLAGE_GV_007_NTSC 47 +#define DAZZLE_DVC_50_REV_1_NTSC 48 +#define DAZZLE_DVC_80_REV_1_PAL 49 +#define DAZZLE_DVC_90_REV_1_SECAM 50 +#define ESKAPE_LABS_MYTV2GO 51 +#define PINNA_PCTV_USB_PAL 52 +#define PINNA_PCTV_USB_SECAM 53 +#define PINNA_PCTV_USB_PAL_FM 54 +#define MIRO_PCTV_USB 55 +#define PINNA_PCTV_USB_NTSC_FM 56 +#define PINNA_PCTV_USB_PAL_FM_V2 57 +#define PINNA_PCTV_USB_NTSC_FM_V2 58 +#define PINNA_PCTV_USB_PAL_FM_V3 59 +#define PINNA_LINX_VD_IN_CAB_NTSC 60 +#define PINNA_LINX_VD_IN_CAB_PAL 61 +#define PINNA_PCTV_BUNGEE_PAL_FM 62 +#define HPG_WINTV 63 +#define PINNA_PCTV_USB_NTSC_FM_V3 64 +#define MICROCAM_NTSC 65 +#define MICROCAM_PAL 66 + +extern const int usbvision_device_data_size; diff --git a/drivers/staging/media/usbvision/usbvision-core.c b/drivers/staging/media/usbvision/usbvision-core.c new file mode 100644 index 000000000000..f05a5c84dc18 --- /dev/null +++ b/drivers/staging/media/usbvision/usbvision-core.c @@ -0,0 +1,2428 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * usbvision-core.c - driver for NT100x USB video capture devices + * + * Copyright (c) 1999-2005 Joerg Heckenbach <joerg@heckenbach-aw.de> + * Dwaine Garden <dwainegarden@rogers.com> + * + * This module is part of usbvision driver project. + * Updates to driver completed by Dwaine P. Garden + */ + +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/timer.h> +#include <linux/gfp.h> +#include <linux/mm.h> +#include <linux/highmem.h> +#include <linux/vmalloc.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/spinlock.h> +#include <linux/io.h> +#include <linux/videodev2.h> +#include <linux/i2c.h> + +#include <media/i2c/saa7115.h> +#include <media/v4l2-common.h> +#include <media/tuner.h> + +#include <linux/workqueue.h> + +#include "usbvision.h" + +static unsigned int core_debug; +module_param(core_debug, int, 0644); +MODULE_PARM_DESC(core_debug, "enable debug messages [core]"); + +static int adjust_compression = 1; /* Set the compression to be adaptive */ +module_param(adjust_compression, int, 0444); +MODULE_PARM_DESC(adjust_compression, " Set the ADPCM compression for the device. Default: 1 (On)"); + +/* To help people with Black and White output with using s-video input. + * Some cables and input device are wired differently. */ +static int switch_svideo_input; +module_param(switch_svideo_input, int, 0444); +MODULE_PARM_DESC(switch_svideo_input, " Set the S-Video input. Some cables and input device are wired differently. Default: 0 (Off)"); + +static unsigned int adjust_x_offset = -1; +module_param(adjust_x_offset, int, 0644); +MODULE_PARM_DESC(adjust_x_offset, "adjust X offset display [core]"); + +static unsigned int adjust_y_offset = -1; +module_param(adjust_y_offset, int, 0644); +MODULE_PARM_DESC(adjust_y_offset, "adjust Y offset display [core]"); + + +#define ENABLE_HEXDUMP 0 /* Enable if you need it */ + + +#ifdef USBVISION_DEBUG + #define PDEBUG(level, fmt, args...) { \ + if (core_debug & (level)) \ + printk(KERN_INFO KBUILD_MODNAME ":[%s:%d] " fmt, \ + __func__, __LINE__ , ## args); \ + } +#else + #define PDEBUG(level, fmt, args...) do {} while (0) +#endif + +#define DBG_HEADER (1 << 0) +#define DBG_IRQ (1 << 1) +#define DBG_ISOC (1 << 2) +#define DBG_PARSE (1 << 3) +#define DBG_SCRATCH (1 << 4) +#define DBG_FUNC (1 << 5) + +/* The value of 'scratch_buf_size' affects quality of the picture + * in many ways. Shorter buffers may cause loss of data when client + * is too slow. Larger buffers are memory-consuming and take longer + * to work with. This setting can be adjusted, but the default value + * should be OK for most desktop users. + */ +#define DEFAULT_SCRATCH_BUF_SIZE (0x20000) /* 128kB memory scratch buffer */ +static const int scratch_buf_size = DEFAULT_SCRATCH_BUF_SIZE; + +/* Function prototypes */ +static int usbvision_request_intra(struct usb_usbvision *usbvision); +static int usbvision_unrequest_intra(struct usb_usbvision *usbvision); +static int usbvision_adjust_compression(struct usb_usbvision *usbvision); +static int usbvision_measure_bandwidth(struct usb_usbvision *usbvision); + +/*******************************/ +/* Memory management functions */ +/*******************************/ + +/* + * Here we want the physical address of the memory. + * This is used when initializing the contents of the area. + */ + +static void *usbvision_rvmalloc(unsigned long size) +{ + void *mem; + unsigned long adr; + + size = PAGE_ALIGN(size); + mem = vmalloc_32(size); + if (!mem) + return NULL; + + memset(mem, 0, size); /* Clear the ram out, no junk to the user */ + adr = (unsigned long) mem; + while (size > 0) { + SetPageReserved(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + + return mem; +} + +static void usbvision_rvfree(void *mem, unsigned long size) +{ + unsigned long adr; + + if (!mem) + return; + + size = PAGE_ALIGN(size); + + adr = (unsigned long) mem; + while ((long) size > 0) { + ClearPageReserved(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + + vfree(mem); +} + + +#if ENABLE_HEXDUMP +static void usbvision_hexdump(const unsigned char *data, int len) +{ + char tmp[80]; + int i, k; + + for (i = k = 0; len > 0; i++, len--) { + if (i > 0 && (i % 16 == 0)) { + printk("%s\n", tmp); + k = 0; + } + k += sprintf(&tmp[k], "%02x ", data[i]); + } + if (k > 0) + printk(KERN_CONT "%s\n", tmp); +} +#endif + +/******************************** + * scratch ring buffer handling + ********************************/ +static int scratch_len(struct usb_usbvision *usbvision) /* This returns the amount of data actually in the buffer */ +{ + int len = usbvision->scratch_write_ptr - usbvision->scratch_read_ptr; + + if (len < 0) + len += scratch_buf_size; + PDEBUG(DBG_SCRATCH, "scratch_len() = %d\n", len); + + return len; +} + + +/* This returns the free space left in the buffer */ +static int scratch_free(struct usb_usbvision *usbvision) +{ + int free = usbvision->scratch_read_ptr - usbvision->scratch_write_ptr; + if (free <= 0) + free += scratch_buf_size; + if (free) { + free -= 1; /* at least one byte in the buffer must */ + /* left blank, otherwise there is no chance to differ between full and empty */ + } + PDEBUG(DBG_SCRATCH, "return %d\n", free); + + return free; +} + + +/* This puts data into the buffer */ +static int scratch_put(struct usb_usbvision *usbvision, unsigned char *data, + int len) +{ + int len_part; + + if (usbvision->scratch_write_ptr + len < scratch_buf_size) { + memcpy(usbvision->scratch + usbvision->scratch_write_ptr, data, len); + usbvision->scratch_write_ptr += len; + } else { + len_part = scratch_buf_size - usbvision->scratch_write_ptr; + memcpy(usbvision->scratch + usbvision->scratch_write_ptr, data, len_part); + if (len == len_part) { + usbvision->scratch_write_ptr = 0; /* just set write_ptr to zero */ + } else { + memcpy(usbvision->scratch, data + len_part, len - len_part); + usbvision->scratch_write_ptr = len - len_part; + } + } + + PDEBUG(DBG_SCRATCH, "len=%d, new write_ptr=%d\n", len, usbvision->scratch_write_ptr); + + return len; +} + +/* This marks the write_ptr as position of new frame header */ +static void scratch_mark_header(struct usb_usbvision *usbvision) +{ + PDEBUG(DBG_SCRATCH, "header at write_ptr=%d\n", usbvision->scratch_headermarker_write_ptr); + + usbvision->scratch_headermarker[usbvision->scratch_headermarker_write_ptr] = + usbvision->scratch_write_ptr; + usbvision->scratch_headermarker_write_ptr += 1; + usbvision->scratch_headermarker_write_ptr %= USBVISION_NUM_HEADERMARKER; +} + +/* This gets data from the buffer at the given "ptr" position */ +static int scratch_get_extra(struct usb_usbvision *usbvision, + unsigned char *data, int *ptr, int len) +{ + int len_part; + + if (*ptr + len < scratch_buf_size) { + memcpy(data, usbvision->scratch + *ptr, len); + *ptr += len; + } else { + len_part = scratch_buf_size - *ptr; + memcpy(data, usbvision->scratch + *ptr, len_part); + if (len == len_part) { + *ptr = 0; /* just set the y_ptr to zero */ + } else { + memcpy(data + len_part, usbvision->scratch, len - len_part); + *ptr = len - len_part; + } + } + + PDEBUG(DBG_SCRATCH, "len=%d, new ptr=%d\n", len, *ptr); + + return len; +} + + +/* This sets the scratch extra read pointer */ +static void scratch_set_extra_ptr(struct usb_usbvision *usbvision, int *ptr, + int len) +{ + *ptr = (usbvision->scratch_read_ptr + len) % scratch_buf_size; + + PDEBUG(DBG_SCRATCH, "ptr=%d\n", *ptr); +} + + +/* This increments the scratch extra read pointer */ +static void scratch_inc_extra_ptr(int *ptr, int len) +{ + *ptr = (*ptr + len) % scratch_buf_size; + + PDEBUG(DBG_SCRATCH, "ptr=%d\n", *ptr); +} + + +/* This gets data from the buffer */ +static int scratch_get(struct usb_usbvision *usbvision, unsigned char *data, + int len) +{ + int len_part; + + if (usbvision->scratch_read_ptr + len < scratch_buf_size) { + memcpy(data, usbvision->scratch + usbvision->scratch_read_ptr, len); + usbvision->scratch_read_ptr += len; + } else { + len_part = scratch_buf_size - usbvision->scratch_read_ptr; + memcpy(data, usbvision->scratch + usbvision->scratch_read_ptr, len_part); + if (len == len_part) { + usbvision->scratch_read_ptr = 0; /* just set the read_ptr to zero */ + } else { + memcpy(data + len_part, usbvision->scratch, len - len_part); + usbvision->scratch_read_ptr = len - len_part; + } + } + + PDEBUG(DBG_SCRATCH, "len=%d, new read_ptr=%d\n", len, usbvision->scratch_read_ptr); + + return len; +} + + +/* This sets read pointer to next header and returns it */ +static int scratch_get_header(struct usb_usbvision *usbvision, + struct usbvision_frame_header *header) +{ + int err_code = 0; + + PDEBUG(DBG_SCRATCH, "from read_ptr=%d", usbvision->scratch_headermarker_read_ptr); + + while (usbvision->scratch_headermarker_write_ptr - + usbvision->scratch_headermarker_read_ptr != 0) { + usbvision->scratch_read_ptr = + usbvision->scratch_headermarker[usbvision->scratch_headermarker_read_ptr]; + usbvision->scratch_headermarker_read_ptr += 1; + usbvision->scratch_headermarker_read_ptr %= USBVISION_NUM_HEADERMARKER; + scratch_get(usbvision, (unsigned char *)header, USBVISION_HEADER_LENGTH); + if ((header->magic_1 == USBVISION_MAGIC_1) + && (header->magic_2 == USBVISION_MAGIC_2) + && (header->header_length == USBVISION_HEADER_LENGTH)) { + err_code = USBVISION_HEADER_LENGTH; + header->frame_width = header->frame_width_lo + (header->frame_width_hi << 8); + header->frame_height = header->frame_height_lo + (header->frame_height_hi << 8); + break; + } + } + + return err_code; +} + + +/* This removes len bytes of old data from the buffer */ +static void scratch_rm_old(struct usb_usbvision *usbvision, int len) +{ + usbvision->scratch_read_ptr += len; + usbvision->scratch_read_ptr %= scratch_buf_size; + PDEBUG(DBG_SCRATCH, "read_ptr is now %d\n", usbvision->scratch_read_ptr); +} + + +/* This resets the buffer - kills all data in it too */ +static void scratch_reset(struct usb_usbvision *usbvision) +{ + PDEBUG(DBG_SCRATCH, "\n"); + + usbvision->scratch_read_ptr = 0; + usbvision->scratch_write_ptr = 0; + usbvision->scratch_headermarker_read_ptr = 0; + usbvision->scratch_headermarker_write_ptr = 0; + usbvision->isocstate = isoc_state_no_frame; +} + +int usbvision_scratch_alloc(struct usb_usbvision *usbvision) +{ + usbvision->scratch = vmalloc_32(scratch_buf_size); + scratch_reset(usbvision); + if (usbvision->scratch == NULL) { + dev_err(&usbvision->dev->dev, + "%s: unable to allocate %d bytes for scratch\n", + __func__, scratch_buf_size); + return -ENOMEM; + } + return 0; +} + +void usbvision_scratch_free(struct usb_usbvision *usbvision) +{ + vfree(usbvision->scratch); + usbvision->scratch = NULL; +} + +/* + * usbvision_decompress_alloc() + * + * allocates intermediate buffer for decompression + */ +int usbvision_decompress_alloc(struct usb_usbvision *usbvision) +{ + int IFB_size = MAX_FRAME_WIDTH * MAX_FRAME_HEIGHT * 3 / 2; + + usbvision->intra_frame_buffer = vmalloc_32(IFB_size); + if (usbvision->intra_frame_buffer == NULL) { + dev_err(&usbvision->dev->dev, + "%s: unable to allocate %d for compr. frame buffer\n", + __func__, IFB_size); + return -ENOMEM; + } + return 0; +} + +/* + * usbvision_decompress_free() + * + * frees intermediate buffer for decompression + */ +void usbvision_decompress_free(struct usb_usbvision *usbvision) +{ + vfree(usbvision->intra_frame_buffer); + usbvision->intra_frame_buffer = NULL; + +} + +/************************************************************ + * Here comes the data parsing stuff that is run as interrupt + ************************************************************/ +/* + * usbvision_find_header() + * + * Locate one of supported header markers in the scratch buffer. + */ +static enum parse_state usbvision_find_header(struct usb_usbvision *usbvision) +{ + struct usbvision_frame *frame; + int found_header = 0; + + frame = usbvision->cur_frame; + + while (scratch_get_header(usbvision, &frame->isoc_header) == USBVISION_HEADER_LENGTH) { + /* found header in scratch */ + PDEBUG(DBG_HEADER, "found header: 0x%02x%02x %d %d %d %d %#x 0x%02x %u %u", + frame->isoc_header.magic_2, + frame->isoc_header.magic_1, + frame->isoc_header.header_length, + frame->isoc_header.frame_num, + frame->isoc_header.frame_phase, + frame->isoc_header.frame_latency, + frame->isoc_header.data_format, + frame->isoc_header.format_param, + frame->isoc_header.frame_width, + frame->isoc_header.frame_height); + + if (usbvision->request_intra) { + if (frame->isoc_header.format_param & 0x80) { + found_header = 1; + usbvision->last_isoc_frame_num = -1; /* do not check for lost frames this time */ + usbvision_unrequest_intra(usbvision); + break; + } + } else { + found_header = 1; + break; + } + } + + if (found_header) { + frame->frmwidth = frame->isoc_header.frame_width * usbvision->stretch_width; + frame->frmheight = frame->isoc_header.frame_height * usbvision->stretch_height; + frame->v4l2_linesize = (frame->frmwidth * frame->v4l2_format.depth) >> 3; + } else { /* no header found */ + PDEBUG(DBG_HEADER, "skipping scratch data, no header"); + scratch_reset(usbvision); + return parse_state_end_parse; + } + + /* found header */ + if (frame->isoc_header.data_format == ISOC_MODE_COMPRESS) { + /* check isoc_header.frame_num for lost frames */ + if (usbvision->last_isoc_frame_num >= 0) { + if (((usbvision->last_isoc_frame_num + 1) % 32) != frame->isoc_header.frame_num) { + /* unexpected frame drop: need to request new intra frame */ + PDEBUG(DBG_HEADER, "Lost frame before %d on USB", frame->isoc_header.frame_num); + usbvision_request_intra(usbvision); + return parse_state_next_frame; + } + } + usbvision->last_isoc_frame_num = frame->isoc_header.frame_num; + } + usbvision->header_count++; + frame->scanstate = scan_state_lines; + frame->curline = 0; + + return parse_state_continue; +} + +static enum parse_state usbvision_parse_lines_422(struct usb_usbvision *usbvision, + long *pcopylen) +{ + volatile struct usbvision_frame *frame; + unsigned char *f; + int len; + int i; + unsigned char yuyv[4] = { 180, 128, 10, 128 }; /* YUV components */ + unsigned char rv, gv, bv; /* RGB components */ + int clipmask_index, bytes_per_pixel; + int stretch_bytes, clipmask_add; + + frame = usbvision->cur_frame; + f = frame->data + (frame->v4l2_linesize * frame->curline); + + /* Make sure there's enough data for the entire line */ + len = (frame->isoc_header.frame_width * 2) + 5; + if (scratch_len(usbvision) < len) { + PDEBUG(DBG_PARSE, "out of data in line %d, need %u.\n", frame->curline, len); + return parse_state_out; + } + + if ((frame->curline + 1) >= frame->frmheight) + return parse_state_next_frame; + + bytes_per_pixel = frame->v4l2_format.bytes_per_pixel; + stretch_bytes = (usbvision->stretch_width - 1) * bytes_per_pixel; + clipmask_index = frame->curline * MAX_FRAME_WIDTH; + clipmask_add = usbvision->stretch_width; + + for (i = 0; i < frame->frmwidth; i += (2 * usbvision->stretch_width)) { + scratch_get(usbvision, &yuyv[0], 4); + + if (frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) { + *f++ = yuyv[0]; /* Y */ + *f++ = yuyv[3]; /* U */ + } else { + YUV_TO_RGB_BY_THE_BOOK(yuyv[0], yuyv[1], yuyv[3], rv, gv, bv); + switch (frame->v4l2_format.format) { + case V4L2_PIX_FMT_RGB565: + *f++ = (0x1F & rv) | + (0xE0 & (gv << 5)); + *f++ = (0x07 & (gv >> 3)) | + (0xF8 & bv); + break; + case V4L2_PIX_FMT_RGB24: + *f++ = rv; + *f++ = gv; + *f++ = bv; + break; + case V4L2_PIX_FMT_RGB32: + *f++ = rv; + *f++ = gv; + *f++ = bv; + f++; + break; + case V4L2_PIX_FMT_RGB555: + *f++ = (0x1F & rv) | + (0xE0 & (gv << 5)); + *f++ = (0x03 & (gv >> 3)) | + (0x7C & (bv << 2)); + break; + } + } + clipmask_index += clipmask_add; + f += stretch_bytes; + + if (frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) { + *f++ = yuyv[2]; /* Y */ + *f++ = yuyv[1]; /* V */ + } else { + YUV_TO_RGB_BY_THE_BOOK(yuyv[2], yuyv[1], yuyv[3], rv, gv, bv); + switch (frame->v4l2_format.format) { + case V4L2_PIX_FMT_RGB565: + *f++ = (0x1F & rv) | + (0xE0 & (gv << 5)); + *f++ = (0x07 & (gv >> 3)) | + (0xF8 & bv); + break; + case V4L2_PIX_FMT_RGB24: + *f++ = rv; + *f++ = gv; + *f++ = bv; + break; + case V4L2_PIX_FMT_RGB32: + *f++ = rv; + *f++ = gv; + *f++ = bv; + f++; + break; + case V4L2_PIX_FMT_RGB555: + *f++ = (0x1F & rv) | + (0xE0 & (gv << 5)); + *f++ = (0x03 & (gv >> 3)) | + (0x7C & (bv << 2)); + break; + } + } + clipmask_index += clipmask_add; + f += stretch_bytes; + } + + frame->curline += usbvision->stretch_height; + *pcopylen += frame->v4l2_linesize * usbvision->stretch_height; + + if (frame->curline >= frame->frmheight) + return parse_state_next_frame; + return parse_state_continue; +} + +/* The decompression routine */ +static int usbvision_decompress(struct usb_usbvision *usbvision, unsigned char *compressed, + unsigned char *decompressed, int *start_pos, + int *block_typestart_pos, int len) +{ + int rest_pixel, idx, pos, extra_pos, block_len, block_type_pos, block_type_len; + unsigned char block_byte, block_code, block_type, block_type_byte, integrator; + + integrator = 0; + pos = *start_pos; + block_type_pos = *block_typestart_pos; + extra_pos = pos; + block_len = 0; + block_byte = 0; + block_code = 0; + block_type = 0; + block_type_byte = 0; + block_type_len = 0; + rest_pixel = len; + + for (idx = 0; idx < len; idx++) { + if (block_len == 0) { + if (block_type_len == 0) { + block_type_byte = compressed[block_type_pos]; + block_type_pos++; + block_type_len = 4; + } + block_type = (block_type_byte & 0xC0) >> 6; + + /* statistic: */ + usbvision->compr_block_types[block_type]++; + + pos = extra_pos; + if (block_type == 0) { + if (rest_pixel >= 24) { + idx += 23; + rest_pixel -= 24; + integrator = decompressed[idx]; + } else { + idx += rest_pixel - 1; + rest_pixel = 0; + } + } else { + block_code = compressed[pos]; + pos++; + if (rest_pixel >= 24) + block_len = 24; + else + block_len = rest_pixel; + rest_pixel -= block_len; + extra_pos = pos + (block_len / 4); + } + block_type_byte <<= 2; + block_type_len -= 1; + } + if (block_len > 0) { + if ((block_len % 4) == 0) { + block_byte = compressed[pos]; + pos++; + } + if (block_type == 1) /* inter Block */ + integrator = decompressed[idx]; + switch (block_byte & 0xC0) { + case 0x03 << 6: + integrator += compressed[extra_pos]; + extra_pos++; + break; + case 0x02 << 6: + integrator += block_code; + break; + case 0x00: + integrator -= block_code; + break; + } + decompressed[idx] = integrator; + block_byte <<= 2; + block_len -= 1; + } + } + *start_pos = extra_pos; + *block_typestart_pos = block_type_pos; + return idx; +} + + +/* + * usbvision_parse_compress() + * + * Parse compressed frame from the scratch buffer, put + * decoded RGB value into the current frame buffer and add the written + * number of bytes (RGB) to the *pcopylen. + * + */ +static enum parse_state usbvision_parse_compress(struct usb_usbvision *usbvision, + long *pcopylen) +{ +#define USBVISION_STRIP_MAGIC 0x5A +#define USBVISION_STRIP_LEN_MAX 400 +#define USBVISION_STRIP_HEADER_LEN 3 + + struct usbvision_frame *frame; + unsigned char *f, *u = NULL, *v = NULL; + unsigned char strip_data[USBVISION_STRIP_LEN_MAX]; + unsigned char strip_header[USBVISION_STRIP_HEADER_LEN]; + int idx, idx_end, strip_len, strip_ptr, startblock_pos, block_pos, block_type_pos; + int clipmask_index; + int image_size; + unsigned char rv, gv, bv; + static unsigned char *Y, *U, *V; + + frame = usbvision->cur_frame; + image_size = frame->frmwidth * frame->frmheight; + if ((frame->v4l2_format.format == V4L2_PIX_FMT_YUV422P) || + (frame->v4l2_format.format == V4L2_PIX_FMT_YVU420)) { /* this is a planar format */ + /* ... v4l2_linesize not used here. */ + f = frame->data + (frame->width * frame->curline); + } else + f = frame->data + (frame->v4l2_linesize * frame->curline); + + if (frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) { /* initialise u and v pointers */ + /* get base of u and b planes add halfoffset */ + u = frame->data + + image_size + + (frame->frmwidth >> 1) * frame->curline; + v = u + (image_size >> 1); + } else if (frame->v4l2_format.format == V4L2_PIX_FMT_YVU420) { + v = frame->data + image_size + ((frame->curline * (frame->width)) >> 2); + u = v + (image_size >> 2); + } + + if (frame->curline == 0) + usbvision_adjust_compression(usbvision); + + if (scratch_len(usbvision) < USBVISION_STRIP_HEADER_LEN) + return parse_state_out; + + /* get strip header without changing the scratch_read_ptr */ + scratch_set_extra_ptr(usbvision, &strip_ptr, 0); + scratch_get_extra(usbvision, &strip_header[0], &strip_ptr, + USBVISION_STRIP_HEADER_LEN); + + if (strip_header[0] != USBVISION_STRIP_MAGIC) { + /* wrong strip magic */ + usbvision->strip_magic_errors++; + return parse_state_next_frame; + } + + if (frame->curline != (int)strip_header[2]) { + /* line number mismatch error */ + usbvision->strip_line_number_errors++; + } + + strip_len = 2 * (unsigned int)strip_header[1]; + if (strip_len > USBVISION_STRIP_LEN_MAX) { + /* strip overrun */ + /* I think this never happens */ + usbvision_request_intra(usbvision); + } + + if (scratch_len(usbvision) < strip_len) { + /* there is not enough data for the strip */ + return parse_state_out; + } + + if (usbvision->intra_frame_buffer) { + Y = usbvision->intra_frame_buffer + frame->frmwidth * frame->curline; + U = usbvision->intra_frame_buffer + image_size + (frame->frmwidth / 2) * (frame->curline / 2); + V = usbvision->intra_frame_buffer + image_size / 4 * 5 + (frame->frmwidth / 2) * (frame->curline / 2); + } else { + return parse_state_next_frame; + } + + clipmask_index = frame->curline * MAX_FRAME_WIDTH; + + scratch_get(usbvision, strip_data, strip_len); + + idx_end = frame->frmwidth; + block_type_pos = USBVISION_STRIP_HEADER_LEN; + startblock_pos = block_type_pos + (idx_end - 1) / 96 + (idx_end / 2 - 1) / 96 + 2; + block_pos = startblock_pos; + + usbvision->block_pos = block_pos; + + usbvision_decompress(usbvision, strip_data, Y, &block_pos, &block_type_pos, idx_end); + if (strip_len > usbvision->max_strip_len) + usbvision->max_strip_len = strip_len; + + if (frame->curline % 2) + usbvision_decompress(usbvision, strip_data, V, &block_pos, &block_type_pos, idx_end / 2); + else + usbvision_decompress(usbvision, strip_data, U, &block_pos, &block_type_pos, idx_end / 2); + + if (block_pos > usbvision->comprblock_pos) + usbvision->comprblock_pos = block_pos; + if (block_pos > strip_len) + usbvision->strip_len_errors++; + + for (idx = 0; idx < idx_end; idx++) { + if (frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) { + *f++ = Y[idx]; + *f++ = idx & 0x01 ? U[idx / 2] : V[idx / 2]; + } else if (frame->v4l2_format.format == V4L2_PIX_FMT_YUV422P) { + *f++ = Y[idx]; + if (idx & 0x01) + *u++ = U[idx >> 1]; + else + *v++ = V[idx >> 1]; + } else if (frame->v4l2_format.format == V4L2_PIX_FMT_YVU420) { + *f++ = Y[idx]; + if (!((idx & 0x01) | (frame->curline & 0x01))) { + /* only need do this for 1 in 4 pixels */ + /* intraframe buffer is YUV420 format */ + *u++ = U[idx >> 1]; + *v++ = V[idx >> 1]; + } + } else { + YUV_TO_RGB_BY_THE_BOOK(Y[idx], U[idx / 2], V[idx / 2], rv, gv, bv); + switch (frame->v4l2_format.format) { + case V4L2_PIX_FMT_GREY: + *f++ = Y[idx]; + break; + case V4L2_PIX_FMT_RGB555: + *f++ = (0x1F & rv) | + (0xE0 & (gv << 5)); + *f++ = (0x03 & (gv >> 3)) | + (0x7C & (bv << 2)); + break; + case V4L2_PIX_FMT_RGB565: + *f++ = (0x1F & rv) | + (0xE0 & (gv << 5)); + *f++ = (0x07 & (gv >> 3)) | + (0xF8 & bv); + break; + case V4L2_PIX_FMT_RGB24: + *f++ = rv; + *f++ = gv; + *f++ = bv; + break; + case V4L2_PIX_FMT_RGB32: + *f++ = rv; + *f++ = gv; + *f++ = bv; + f++; + break; + } + } + clipmask_index++; + } + /* Deal with non-integer no. of bytes for YUV420P */ + if (frame->v4l2_format.format != V4L2_PIX_FMT_YVU420) + *pcopylen += frame->v4l2_linesize; + else + *pcopylen += frame->curline & 0x01 ? frame->v4l2_linesize : frame->v4l2_linesize << 1; + + frame->curline += 1; + + if (frame->curline >= frame->frmheight) + return parse_state_next_frame; + return parse_state_continue; + +} + + +/* + * usbvision_parse_lines_420() + * + * Parse two lines from the scratch buffer, put + * decoded RGB value into the current frame buffer and add the written + * number of bytes (RGB) to the *pcopylen. + * + */ +static enum parse_state usbvision_parse_lines_420(struct usb_usbvision *usbvision, + long *pcopylen) +{ + struct usbvision_frame *frame; + unsigned char *f_even = NULL, *f_odd = NULL; + unsigned int pixel_per_line, block; + int pixel, block_split; + int y_ptr, u_ptr, v_ptr, y_odd_offset; + const int y_block_size = 128; + const int uv_block_size = 64; + const int sub_block_size = 32; + const int y_step[] = { 0, 0, 0, 2 }, y_step_size = 4; + const int uv_step[] = { 0, 0, 0, 4 }, uv_step_size = 4; + unsigned char y[2], u, v; /* YUV components */ + int y_, u_, v_, vb, uvg, ur; + int r_, g_, b_; /* RGB components */ + unsigned char g; + int clipmask_even_index, clipmask_odd_index, bytes_per_pixel; + int clipmask_add, stretch_bytes; + + frame = usbvision->cur_frame; + f_even = frame->data + (frame->v4l2_linesize * frame->curline); + f_odd = f_even + frame->v4l2_linesize * usbvision->stretch_height; + + /* Make sure there's enough data for the entire line */ + /* In this mode usbvision transfer 3 bytes for every 2 pixels */ + /* I need two lines to decode the color */ + bytes_per_pixel = frame->v4l2_format.bytes_per_pixel; + stretch_bytes = (usbvision->stretch_width - 1) * bytes_per_pixel; + clipmask_even_index = frame->curline * MAX_FRAME_WIDTH; + clipmask_odd_index = clipmask_even_index + MAX_FRAME_WIDTH; + clipmask_add = usbvision->stretch_width; + pixel_per_line = frame->isoc_header.frame_width; + + if (scratch_len(usbvision) < (int)pixel_per_line * 3) { + /* printk(KERN_DEBUG "out of data, need %d\n", len); */ + return parse_state_out; + } + + if ((frame->curline + 1) >= frame->frmheight) + return parse_state_next_frame; + + block_split = (pixel_per_line%y_block_size) ? 1 : 0; /* are some blocks split into different lines? */ + + y_odd_offset = (pixel_per_line / y_block_size) * (y_block_size + uv_block_size) + + block_split * uv_block_size; + + scratch_set_extra_ptr(usbvision, &y_ptr, y_odd_offset); + scratch_set_extra_ptr(usbvision, &u_ptr, y_block_size); + scratch_set_extra_ptr(usbvision, &v_ptr, y_odd_offset + + (4 - block_split) * sub_block_size); + + for (block = 0; block < (pixel_per_line / sub_block_size); block++) { + for (pixel = 0; pixel < sub_block_size; pixel += 2) { + scratch_get(usbvision, &y[0], 2); + scratch_get_extra(usbvision, &u, &u_ptr, 1); + scratch_get_extra(usbvision, &v, &v_ptr, 1); + + /* I don't use the YUV_TO_RGB macro for better performance */ + v_ = v - 128; + u_ = u - 128; + vb = 132252 * v_; + uvg = -53281 * u_ - 25625 * v_; + ur = 104595 * u_; + + if (frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) { + *f_even++ = y[0]; + *f_even++ = v; + } else { + y_ = 76284 * (y[0] - 16); + + b_ = (y_ + vb) >> 16; + g_ = (y_ + uvg) >> 16; + r_ = (y_ + ur) >> 16; + + switch (frame->v4l2_format.format) { + case V4L2_PIX_FMT_RGB565: + g = LIMIT_RGB(g_); + *f_even++ = + (0x1F & LIMIT_RGB(r_)) | + (0xE0 & (g << 5)); + *f_even++ = + (0x07 & (g >> 3)) | + (0xF8 & LIMIT_RGB(b_)); + break; + case V4L2_PIX_FMT_RGB24: + *f_even++ = LIMIT_RGB(r_); + *f_even++ = LIMIT_RGB(g_); + *f_even++ = LIMIT_RGB(b_); + break; + case V4L2_PIX_FMT_RGB32: + *f_even++ = LIMIT_RGB(r_); + *f_even++ = LIMIT_RGB(g_); + *f_even++ = LIMIT_RGB(b_); + f_even++; + break; + case V4L2_PIX_FMT_RGB555: + g = LIMIT_RGB(g_); + *f_even++ = (0x1F & LIMIT_RGB(r_)) | + (0xE0 & (g << 5)); + *f_even++ = (0x03 & (g >> 3)) | + (0x7C & (LIMIT_RGB(b_) << 2)); + break; + } + } + clipmask_even_index += clipmask_add; + f_even += stretch_bytes; + + if (frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) { + *f_even++ = y[1]; + *f_even++ = u; + } else { + y_ = 76284 * (y[1] - 16); + + b_ = (y_ + vb) >> 16; + g_ = (y_ + uvg) >> 16; + r_ = (y_ + ur) >> 16; + + switch (frame->v4l2_format.format) { + case V4L2_PIX_FMT_RGB565: + g = LIMIT_RGB(g_); + *f_even++ = + (0x1F & LIMIT_RGB(r_)) | + (0xE0 & (g << 5)); + *f_even++ = + (0x07 & (g >> 3)) | + (0xF8 & LIMIT_RGB(b_)); + break; + case V4L2_PIX_FMT_RGB24: + *f_even++ = LIMIT_RGB(r_); + *f_even++ = LIMIT_RGB(g_); + *f_even++ = LIMIT_RGB(b_); + break; + case V4L2_PIX_FMT_RGB32: + *f_even++ = LIMIT_RGB(r_); + *f_even++ = LIMIT_RGB(g_); + *f_even++ = LIMIT_RGB(b_); + f_even++; + break; + case V4L2_PIX_FMT_RGB555: + g = LIMIT_RGB(g_); + *f_even++ = (0x1F & LIMIT_RGB(r_)) | + (0xE0 & (g << 5)); + *f_even++ = (0x03 & (g >> 3)) | + (0x7C & (LIMIT_RGB(b_) << 2)); + break; + } + } + clipmask_even_index += clipmask_add; + f_even += stretch_bytes; + + scratch_get_extra(usbvision, &y[0], &y_ptr, 2); + + if (frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) { + *f_odd++ = y[0]; + *f_odd++ = v; + } else { + y_ = 76284 * (y[0] - 16); + + b_ = (y_ + vb) >> 16; + g_ = (y_ + uvg) >> 16; + r_ = (y_ + ur) >> 16; + + switch (frame->v4l2_format.format) { + case V4L2_PIX_FMT_RGB565: + g = LIMIT_RGB(g_); + *f_odd++ = + (0x1F & LIMIT_RGB(r_)) | + (0xE0 & (g << 5)); + *f_odd++ = + (0x07 & (g >> 3)) | + (0xF8 & LIMIT_RGB(b_)); + break; + case V4L2_PIX_FMT_RGB24: + *f_odd++ = LIMIT_RGB(r_); + *f_odd++ = LIMIT_RGB(g_); + *f_odd++ = LIMIT_RGB(b_); + break; + case V4L2_PIX_FMT_RGB32: + *f_odd++ = LIMIT_RGB(r_); + *f_odd++ = LIMIT_RGB(g_); + *f_odd++ = LIMIT_RGB(b_); + f_odd++; + break; + case V4L2_PIX_FMT_RGB555: + g = LIMIT_RGB(g_); + *f_odd++ = (0x1F & LIMIT_RGB(r_)) | + (0xE0 & (g << 5)); + *f_odd++ = (0x03 & (g >> 3)) | + (0x7C & (LIMIT_RGB(b_) << 2)); + break; + } + } + clipmask_odd_index += clipmask_add; + f_odd += stretch_bytes; + + if (frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) { + *f_odd++ = y[1]; + *f_odd++ = u; + } else { + y_ = 76284 * (y[1] - 16); + + b_ = (y_ + vb) >> 16; + g_ = (y_ + uvg) >> 16; + r_ = (y_ + ur) >> 16; + + switch (frame->v4l2_format.format) { + case V4L2_PIX_FMT_RGB565: + g = LIMIT_RGB(g_); + *f_odd++ = + (0x1F & LIMIT_RGB(r_)) | + (0xE0 & (g << 5)); + *f_odd++ = + (0x07 & (g >> 3)) | + (0xF8 & LIMIT_RGB(b_)); + break; + case V4L2_PIX_FMT_RGB24: + *f_odd++ = LIMIT_RGB(r_); + *f_odd++ = LIMIT_RGB(g_); + *f_odd++ = LIMIT_RGB(b_); + break; + case V4L2_PIX_FMT_RGB32: + *f_odd++ = LIMIT_RGB(r_); + *f_odd++ = LIMIT_RGB(g_); + *f_odd++ = LIMIT_RGB(b_); + f_odd++; + break; + case V4L2_PIX_FMT_RGB555: + g = LIMIT_RGB(g_); + *f_odd++ = (0x1F & LIMIT_RGB(r_)) | + (0xE0 & (g << 5)); + *f_odd++ = (0x03 & (g >> 3)) | + (0x7C & (LIMIT_RGB(b_) << 2)); + break; + } + } + clipmask_odd_index += clipmask_add; + f_odd += stretch_bytes; + } + + scratch_rm_old(usbvision, y_step[block % y_step_size] * sub_block_size); + scratch_inc_extra_ptr(&y_ptr, y_step[(block + 2 * block_split) % y_step_size] + * sub_block_size); + scratch_inc_extra_ptr(&u_ptr, uv_step[block % uv_step_size] + * sub_block_size); + scratch_inc_extra_ptr(&v_ptr, uv_step[(block + 2 * block_split) % uv_step_size] + * sub_block_size); + } + + scratch_rm_old(usbvision, pixel_per_line * 3 / 2 + + block_split * sub_block_size); + + frame->curline += 2 * usbvision->stretch_height; + *pcopylen += frame->v4l2_linesize * 2 * usbvision->stretch_height; + + if (frame->curline >= frame->frmheight) + return parse_state_next_frame; + return parse_state_continue; +} + +/* + * usbvision_parse_data() + * + * Generic routine to parse the scratch buffer. It employs either + * usbvision_find_header() or usbvision_parse_lines() to do most + * of work. + * + */ +static void usbvision_parse_data(struct usb_usbvision *usbvision) +{ + struct usbvision_frame *frame; + enum parse_state newstate; + long copylen = 0; + unsigned long lock_flags; + + frame = usbvision->cur_frame; + + PDEBUG(DBG_PARSE, "parsing len=%d\n", scratch_len(usbvision)); + + while (1) { + newstate = parse_state_out; + if (scratch_len(usbvision)) { + if (frame->scanstate == scan_state_scanning) { + newstate = usbvision_find_header(usbvision); + } else if (frame->scanstate == scan_state_lines) { + if (usbvision->isoc_mode == ISOC_MODE_YUV420) + newstate = usbvision_parse_lines_420(usbvision, ©len); + else if (usbvision->isoc_mode == ISOC_MODE_YUV422) + newstate = usbvision_parse_lines_422(usbvision, ©len); + else if (usbvision->isoc_mode == ISOC_MODE_COMPRESS) + newstate = usbvision_parse_compress(usbvision, ©len); + } + } + if (newstate == parse_state_continue) + continue; + if ((newstate == parse_state_next_frame) || (newstate == parse_state_out)) + break; + return; /* parse_state_end_parse */ + } + + if (newstate == parse_state_next_frame) { + frame->grabstate = frame_state_done; + frame->ts = ktime_get_ns(); + frame->sequence = usbvision->frame_num; + + spin_lock_irqsave(&usbvision->queue_lock, lock_flags); + list_move_tail(&(frame->frame), &usbvision->outqueue); + usbvision->cur_frame = NULL; + spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags); + + usbvision->frame_num++; + + /* This will cause the process to request another frame. */ + if (waitqueue_active(&usbvision->wait_frame)) { + PDEBUG(DBG_PARSE, "Wake up !"); + wake_up_interruptible(&usbvision->wait_frame); + } + } else { + frame->grabstate = frame_state_grabbing; + } + + /* Update the frame's uncompressed length. */ + frame->scanlength += copylen; +} + + +/* + * Make all of the blocks of data contiguous + */ +static int usbvision_compress_isochronous(struct usb_usbvision *usbvision, + struct urb *urb) +{ + unsigned char *packet_data; + int i, totlen = 0; + + for (i = 0; i < urb->number_of_packets; i++) { + int packet_len = urb->iso_frame_desc[i].actual_length; + int packet_stat = urb->iso_frame_desc[i].status; + + packet_data = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + + /* Detect and ignore errored packets */ + if (packet_stat) { /* packet_stat != 0 ????????????? */ + PDEBUG(DBG_ISOC, "data error: [%d] len=%d, status=%X", i, packet_len, packet_stat); + usbvision->isoc_err_count++; + continue; + } + + /* Detect and ignore empty packets */ + if (packet_len < 0) { + PDEBUG(DBG_ISOC, "error packet [%d]", i); + usbvision->isoc_skip_count++; + continue; + } else if (packet_len == 0) { /* Frame end ????? */ + PDEBUG(DBG_ISOC, "null packet [%d]", i); + usbvision->isocstate = isoc_state_no_frame; + usbvision->isoc_skip_count++; + continue; + } else if (packet_len > usbvision->isoc_packet_size) { + PDEBUG(DBG_ISOC, "packet[%d] > isoc_packet_size", i); + usbvision->isoc_skip_count++; + continue; + } + + PDEBUG(DBG_ISOC, "packet ok [%d] len=%d", i, packet_len); + + if (usbvision->isocstate == isoc_state_no_frame) { /* new frame begins */ + usbvision->isocstate = isoc_state_in_frame; + scratch_mark_header(usbvision); + usbvision_measure_bandwidth(usbvision); + PDEBUG(DBG_ISOC, "packet with header"); + } + + /* + * If usbvision continues to feed us with data but there is no + * consumption (if, for example, V4L client fell asleep) we + * may overflow the buffer. We have to move old data over to + * free room for new data. This is bad for old data. If we + * just drop new data then it's bad for new data... choose + * your favorite evil here. + */ + if (scratch_free(usbvision) < packet_len) { + usbvision->scratch_ovf_count++; + PDEBUG(DBG_ISOC, "scratch buf overflow! scr_len: %d, n: %d", + scratch_len(usbvision), packet_len); + scratch_rm_old(usbvision, packet_len - scratch_free(usbvision)); + } + + /* Now we know that there is enough room in scratch buffer */ + scratch_put(usbvision, packet_data, packet_len); + totlen += packet_len; + usbvision->isoc_data_count += packet_len; + usbvision->isoc_packet_count++; + } +#if ENABLE_HEXDUMP + if (totlen > 0) { + static int foo; + + if (foo < 1) { + printk(KERN_DEBUG "+%d.\n", usbvision->scratchlen); + usbvision_hexdump(data0, (totlen > 64) ? 64 : totlen); + ++foo; + } + } +#endif + return totlen; +} + +static void usbvision_isoc_irq(struct urb *urb) +{ + int err_code = 0; + int len; + struct usb_usbvision *usbvision = urb->context; + int i; + struct usbvision_frame **f; + + /* We don't want to do anything if we are about to be removed! */ + if (!USBVISION_IS_OPERATIONAL(usbvision)) + return; + + /* any urb with wrong status is ignored without acknowledgement */ + if (urb->status == -ENOENT) + return; + + f = &usbvision->cur_frame; + + /* Manage streaming interruption */ + if (usbvision->streaming == stream_interrupt) { + usbvision->streaming = stream_idle; + if ((*f)) { + (*f)->grabstate = frame_state_ready; + (*f)->scanstate = scan_state_scanning; + } + PDEBUG(DBG_IRQ, "stream interrupted"); + wake_up_interruptible(&usbvision->wait_stream); + } + + /* Copy the data received into our scratch buffer */ + len = usbvision_compress_isochronous(usbvision, urb); + + usbvision->isoc_urb_count++; + usbvision->urb_length = len; + + if (usbvision->streaming == stream_on) { + /* If we collected enough data let's parse! */ + if (scratch_len(usbvision) > USBVISION_HEADER_LENGTH && + !list_empty(&(usbvision->inqueue))) { + if (!(*f)) { + (*f) = list_entry(usbvision->inqueue.next, + struct usbvision_frame, + frame); + } + usbvision_parse_data(usbvision); + } else { + /* If we don't have a frame + we're current working on, complain */ + PDEBUG(DBG_IRQ, + "received data, but no one needs it"); + scratch_reset(usbvision); + } + } else { + PDEBUG(DBG_IRQ, "received data, but no one needs it"); + scratch_reset(usbvision); + } + + for (i = 0; i < USBVISION_URB_FRAMES; i++) { + urb->iso_frame_desc[i].status = 0; + urb->iso_frame_desc[i].actual_length = 0; + } + + urb->status = 0; + urb->dev = usbvision->dev; + err_code = usb_submit_urb(urb, GFP_ATOMIC); + + if (err_code) { + dev_err(&usbvision->dev->dev, + "%s: usb_submit_urb failed: error %d\n", + __func__, err_code); + } + + return; +} + +/*************************************/ +/* Low level usbvision access functions */ +/*************************************/ + +/* + * usbvision_read_reg() + * + * return < 0 -> Error + * >= 0 -> Data + */ + +int usbvision_read_reg(struct usb_usbvision *usbvision, unsigned char reg) +{ + int err_code = 0; + unsigned char *buffer = usbvision->ctrl_urb_buffer; + + if (!USBVISION_IS_OPERATIONAL(usbvision)) + return -1; + + err_code = usb_control_msg(usbvision->dev, usb_rcvctrlpipe(usbvision->dev, 1), + USBVISION_OP_CODE, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT, + 0, (__u16) reg, buffer, 1, HZ); + + if (err_code < 0) { + dev_err(&usbvision->dev->dev, + "%s: failed: error %d\n", __func__, err_code); + return err_code; + } + return buffer[0]; +} + +/* + * usbvision_write_reg() + * + * return 1 -> Reg written + * 0 -> usbvision is not yet ready + * -1 -> Something went wrong + */ + +int usbvision_write_reg(struct usb_usbvision *usbvision, unsigned char reg, + unsigned char value) +{ + int err_code = 0; + + if (!USBVISION_IS_OPERATIONAL(usbvision)) + return 0; + + usbvision->ctrl_urb_buffer[0] = value; + err_code = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1), + USBVISION_OP_CODE, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_ENDPOINT, 0, (__u16) reg, + usbvision->ctrl_urb_buffer, 1, HZ); + + if (err_code < 0) { + dev_err(&usbvision->dev->dev, + "%s: failed: error %d\n", __func__, err_code); + } + return err_code; +} + + +static void usbvision_ctrl_urb_complete(struct urb *urb) +{ + struct usb_usbvision *usbvision = (struct usb_usbvision *)urb->context; + + PDEBUG(DBG_IRQ, ""); + usbvision->ctrl_urb_busy = 0; +} + + +static int usbvision_write_reg_irq(struct usb_usbvision *usbvision, int address, + unsigned char *data, int len) +{ + int err_code = 0; + + PDEBUG(DBG_IRQ, ""); + if (len > 8) + return -EFAULT; + if (usbvision->ctrl_urb_busy) + return -EBUSY; + usbvision->ctrl_urb_busy = 1; + + usbvision->ctrl_urb_setup.bRequestType = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT; + usbvision->ctrl_urb_setup.bRequest = USBVISION_OP_CODE; + usbvision->ctrl_urb_setup.wValue = 0; + usbvision->ctrl_urb_setup.wIndex = cpu_to_le16(address); + usbvision->ctrl_urb_setup.wLength = cpu_to_le16(len); + usb_fill_control_urb(usbvision->ctrl_urb, usbvision->dev, + usb_sndctrlpipe(usbvision->dev, 1), + (unsigned char *)&usbvision->ctrl_urb_setup, + (void *)usbvision->ctrl_urb_buffer, len, + usbvision_ctrl_urb_complete, + (void *)usbvision); + + memcpy(usbvision->ctrl_urb_buffer, data, len); + + err_code = usb_submit_urb(usbvision->ctrl_urb, GFP_ATOMIC); + if (err_code < 0) { + /* error in usb_submit_urb() */ + usbvision->ctrl_urb_busy = 0; + } + PDEBUG(DBG_IRQ, "submit %d byte: error %d", len, err_code); + return err_code; +} + + +static int usbvision_init_compression(struct usb_usbvision *usbvision) +{ + usbvision->last_isoc_frame_num = -1; + usbvision->isoc_data_count = 0; + usbvision->isoc_packet_count = 0; + usbvision->isoc_skip_count = 0; + usbvision->compr_level = 50; + usbvision->last_compr_level = -1; + usbvision->isoc_urb_count = 0; + usbvision->request_intra = 1; + usbvision->isoc_measure_bandwidth_count = 0; + + return 0; +} + +/* this function measures the used bandwidth since last call + * return: 0 : no error + * sets used_bandwidth to 1-100 : 1-100% of full bandwidth resp. to isoc_packet_size + */ +static int usbvision_measure_bandwidth(struct usb_usbvision *usbvision) +{ + if (usbvision->isoc_measure_bandwidth_count < 2) { /* this gives an average bandwidth of 3 frames */ + usbvision->isoc_measure_bandwidth_count++; + return 0; + } + if ((usbvision->isoc_packet_size > 0) && (usbvision->isoc_packet_count > 0)) { + usbvision->used_bandwidth = usbvision->isoc_data_count / + (usbvision->isoc_packet_count + usbvision->isoc_skip_count) * + 100 / usbvision->isoc_packet_size; + } + usbvision->isoc_measure_bandwidth_count = 0; + usbvision->isoc_data_count = 0; + usbvision->isoc_packet_count = 0; + usbvision->isoc_skip_count = 0; + return 0; +} + +static int usbvision_adjust_compression(struct usb_usbvision *usbvision) +{ + int err_code = 0; + unsigned char buffer[6]; + + PDEBUG(DBG_IRQ, ""); + if ((adjust_compression) && (usbvision->used_bandwidth > 0)) { + usbvision->compr_level += (usbvision->used_bandwidth - 90) / 2; + RESTRICT_TO_RANGE(usbvision->compr_level, 0, 100); + if (usbvision->compr_level != usbvision->last_compr_level) { + int distortion; + + if (usbvision->bridge_type == BRIDGE_NT1004 || usbvision->bridge_type == BRIDGE_NT1005) { + buffer[0] = (unsigned char)(4 + 16 * usbvision->compr_level / 100); /* PCM Threshold 1 */ + buffer[1] = (unsigned char)(4 + 8 * usbvision->compr_level / 100); /* PCM Threshold 2 */ + distortion = 7 + 248 * usbvision->compr_level / 100; + buffer[2] = (unsigned char)(distortion & 0xFF); /* Average distortion Threshold (inter) */ + buffer[3] = (unsigned char)(distortion & 0xFF); /* Average distortion Threshold (intra) */ + distortion = 1 + 42 * usbvision->compr_level / 100; + buffer[4] = (unsigned char)(distortion & 0xFF); /* Maximum distortion Threshold (inter) */ + buffer[5] = (unsigned char)(distortion & 0xFF); /* Maximum distortion Threshold (intra) */ + } else { /* BRIDGE_NT1003 */ + buffer[0] = (unsigned char)(4 + 16 * usbvision->compr_level / 100); /* PCM threshold 1 */ + buffer[1] = (unsigned char)(4 + 8 * usbvision->compr_level / 100); /* PCM threshold 2 */ + distortion = 2 + 253 * usbvision->compr_level / 100; + buffer[2] = (unsigned char)(distortion & 0xFF); /* distortion threshold bit0-7 */ + buffer[3] = 0; /* (unsigned char)((distortion >> 8) & 0x0F); distortion threshold bit 8-11 */ + distortion = 0 + 43 * usbvision->compr_level / 100; + buffer[4] = (unsigned char)(distortion & 0xFF); /* maximum distortion bit0-7 */ + buffer[5] = 0; /* (unsigned char)((distortion >> 8) & 0x01); maximum distortion bit 8 */ + } + err_code = usbvision_write_reg_irq(usbvision, USBVISION_PCM_THR1, buffer, 6); + if (err_code == 0) { + PDEBUG(DBG_IRQ, "new compr params %#02x %#02x %#02x %#02x %#02x %#02x", buffer[0], + buffer[1], buffer[2], buffer[3], buffer[4], buffer[5]); + usbvision->last_compr_level = usbvision->compr_level; + } + } + } + return err_code; +} + +static int usbvision_request_intra(struct usb_usbvision *usbvision) +{ + unsigned char buffer[1]; + + PDEBUG(DBG_IRQ, ""); + usbvision->request_intra = 1; + buffer[0] = 1; + usbvision_write_reg_irq(usbvision, USBVISION_FORCE_INTRA, buffer, 1); + return 0; +} + +static int usbvision_unrequest_intra(struct usb_usbvision *usbvision) +{ + unsigned char buffer[1]; + + PDEBUG(DBG_IRQ, ""); + usbvision->request_intra = 0; + buffer[0] = 0; + usbvision_write_reg_irq(usbvision, USBVISION_FORCE_INTRA, buffer, 1); + return 0; +} + +/******************************* + * usbvision utility functions + *******************************/ + +int usbvision_power_off(struct usb_usbvision *usbvision) +{ + int err_code = 0; + + PDEBUG(DBG_FUNC, ""); + + err_code = usbvision_write_reg(usbvision, USBVISION_PWR_REG, USBVISION_SSPND_EN); + if (err_code == 1) + usbvision->power = 0; + PDEBUG(DBG_FUNC, "%s: err_code %d", (err_code != 1) ? "ERROR" : "power is off", err_code); + return err_code; +} + +/* configure webcam image sensor using the serial port */ +static int usbvision_init_webcam(struct usb_usbvision *usbvision) +{ + int rc; + int i; + static char init_values[38][3] = { + { 0x04, 0x12, 0x08 }, { 0x05, 0xff, 0xc8 }, { 0x06, 0x18, 0x07 }, { 0x07, 0x90, 0x00 }, + { 0x09, 0x00, 0x00 }, { 0x0a, 0x00, 0x00 }, { 0x0b, 0x08, 0x00 }, { 0x0d, 0xcc, 0xcc }, + { 0x0e, 0x13, 0x14 }, { 0x10, 0x9b, 0x83 }, { 0x11, 0x5a, 0x3f }, { 0x12, 0xe4, 0x73 }, + { 0x13, 0x88, 0x84 }, { 0x14, 0x89, 0x80 }, { 0x15, 0x00, 0x20 }, { 0x16, 0x00, 0x00 }, + { 0x17, 0xff, 0xa0 }, { 0x18, 0x6b, 0x20 }, { 0x19, 0x22, 0x40 }, { 0x1a, 0x10, 0x07 }, + { 0x1b, 0x00, 0x47 }, { 0x1c, 0x03, 0xe0 }, { 0x1d, 0x00, 0x00 }, { 0x1e, 0x00, 0x00 }, + { 0x1f, 0x00, 0x00 }, { 0x20, 0x00, 0x00 }, { 0x21, 0x00, 0x00 }, { 0x22, 0x00, 0x00 }, + { 0x23, 0x00, 0x00 }, { 0x24, 0x00, 0x00 }, { 0x25, 0x00, 0x00 }, { 0x26, 0x00, 0x00 }, + { 0x27, 0x00, 0x00 }, { 0x28, 0x00, 0x00 }, { 0x29, 0x00, 0x00 }, { 0x08, 0x80, 0x60 }, + { 0x0f, 0x2d, 0x24 }, { 0x0c, 0x80, 0x80 } + }; + unsigned char *value = usbvision->ctrl_urb_buffer; + + /* the only difference between PAL and NTSC init_values */ + if (usbvision_device_data[usbvision->dev_model].video_norm == V4L2_STD_NTSC) + init_values[4][1] = 0x34; + + for (i = 0; i < sizeof(init_values) / 3; i++) { + usbvision_write_reg(usbvision, USBVISION_SER_MODE, USBVISION_SER_MODE_SOFT); + memcpy(value, init_values[i], 3); + rc = usb_control_msg(usbvision->dev, + usb_sndctrlpipe(usbvision->dev, 1), + USBVISION_OP_CODE, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_ENDPOINT, 0, + (__u16) USBVISION_SER_DAT1, value, + 3, HZ); + if (rc < 0) + return rc; + usbvision_write_reg(usbvision, USBVISION_SER_MODE, USBVISION_SER_MODE_SIO); + /* write 3 bytes to the serial port using SIO mode */ + usbvision_write_reg(usbvision, USBVISION_SER_CONT, 3 | 0x10); + usbvision_write_reg(usbvision, USBVISION_IOPIN_REG, 0); + usbvision_write_reg(usbvision, USBVISION_SER_MODE, USBVISION_SER_MODE_SOFT); + usbvision_write_reg(usbvision, USBVISION_IOPIN_REG, USBVISION_IO_2); + usbvision_write_reg(usbvision, USBVISION_SER_MODE, USBVISION_SER_MODE_SOFT | USBVISION_CLK_OUT); + usbvision_write_reg(usbvision, USBVISION_SER_MODE, USBVISION_SER_MODE_SOFT | USBVISION_DAT_IO); + usbvision_write_reg(usbvision, USBVISION_SER_MODE, USBVISION_SER_MODE_SOFT | USBVISION_CLK_OUT | USBVISION_DAT_IO); + } + + return 0; +} + +/* + * usbvision_set_video_format() + * + */ +static int usbvision_set_video_format(struct usb_usbvision *usbvision, int format) +{ + static const char proc[] = "usbvision_set_video_format"; + unsigned char *value = usbvision->ctrl_urb_buffer; + int rc; + + if (!USBVISION_IS_OPERATIONAL(usbvision)) + return 0; + + PDEBUG(DBG_FUNC, "isoc_mode %#02x", format); + + if ((format != ISOC_MODE_YUV422) + && (format != ISOC_MODE_YUV420) + && (format != ISOC_MODE_COMPRESS)) { + printk(KERN_ERR "usbvision: unknown video format %02x, using default YUV420", + format); + format = ISOC_MODE_YUV420; + } + value[0] = 0x0A; /* TODO: See the effect of the filter */ + value[1] = format; /* Sets the VO_MODE register which follows FILT_CONT */ + rc = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1), + USBVISION_OP_CODE, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_ENDPOINT, 0, + (__u16) USBVISION_FILT_CONT, value, 2, HZ); + + if (rc < 0) { + printk(KERN_ERR "%s: ERROR=%d. USBVISION stopped - reconnect or reload driver.\n", + proc, rc); + } + usbvision->isoc_mode = format; + return rc; +} + +/* + * usbvision_set_output() + * + */ + +int usbvision_set_output(struct usb_usbvision *usbvision, int width, + int height) +{ + int err_code = 0; + int usb_width, usb_height; + unsigned int frame_rate = 0, frame_drop = 0; + unsigned char *value = usbvision->ctrl_urb_buffer; + + if (!USBVISION_IS_OPERATIONAL(usbvision)) + return 0; + + if (width > MAX_USB_WIDTH) { + usb_width = width / 2; + usbvision->stretch_width = 2; + } else { + usb_width = width; + usbvision->stretch_width = 1; + } + + if (height > MAX_USB_HEIGHT) { + usb_height = height / 2; + usbvision->stretch_height = 2; + } else { + usb_height = height; + usbvision->stretch_height = 1; + } + + RESTRICT_TO_RANGE(usb_width, MIN_FRAME_WIDTH, MAX_USB_WIDTH); + usb_width &= ~(MIN_FRAME_WIDTH-1); + RESTRICT_TO_RANGE(usb_height, MIN_FRAME_HEIGHT, MAX_USB_HEIGHT); + usb_height &= ~(1); + + PDEBUG(DBG_FUNC, "usb %dx%d; screen %dx%d; stretch %dx%d", + usb_width, usb_height, width, height, + usbvision->stretch_width, usbvision->stretch_height); + + /* I'll not rewrite the same values */ + if ((usb_width != usbvision->curwidth) || (usb_height != usbvision->curheight)) { + value[0] = usb_width & 0xff; /* LSB */ + value[1] = (usb_width >> 8) & 0x03; /* MSB */ + value[2] = usb_height & 0xff; /* LSB */ + value[3] = (usb_height >> 8) & 0x03; /* MSB */ + + err_code = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1), + USBVISION_OP_CODE, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT, + 0, (__u16) USBVISION_LXSIZE_O, value, 4, HZ); + + if (err_code < 0) { + dev_err(&usbvision->dev->dev, + "%s failed: error %d\n", __func__, err_code); + return err_code; + } + usbvision->curwidth = usbvision->stretch_width * usb_width; + usbvision->curheight = usbvision->stretch_height * usb_height; + } + + if (usbvision->isoc_mode == ISOC_MODE_YUV422) + frame_rate = (usbvision->isoc_packet_size * 1000) / (usb_width * usb_height * 2); + else if (usbvision->isoc_mode == ISOC_MODE_YUV420) + frame_rate = (usbvision->isoc_packet_size * 1000) / ((usb_width * usb_height * 12) / 8); + else + frame_rate = FRAMERATE_MAX; + + if (usbvision->tvnorm_id & V4L2_STD_625_50) + frame_drop = frame_rate * 32 / 25 - 1; + else if (usbvision->tvnorm_id & V4L2_STD_525_60) + frame_drop = frame_rate * 32 / 30 - 1; + + RESTRICT_TO_RANGE(frame_drop, FRAMERATE_MIN, FRAMERATE_MAX); + + PDEBUG(DBG_FUNC, "frame_rate %d fps, frame_drop %d", frame_rate, frame_drop); + + frame_drop = FRAMERATE_MAX; /* We can allow the maximum here, because dropping is controlled */ + + if (usbvision_device_data[usbvision->dev_model].codec == CODEC_WEBCAM) { + if (usbvision_device_data[usbvision->dev_model].video_norm == V4L2_STD_PAL) + frame_drop = 25; + else + frame_drop = 30; + } + + /* frame_drop = 7; => frame_phase = 1, 5, 9, 13, 17, 21, 25, 0, 4, 8, ... + => frame_skip = 4; + => frame_rate = (7 + 1) * 25 / 32 = 200 / 32 = 6.25; + + frame_drop = 9; => frame_phase = 1, 5, 8, 11, 14, 17, 21, 24, 27, 1, 4, 8, ... + => frame_skip = 4, 3, 3, 3, 3, 4, 3, 3, 3, 3, 4, ... + => frame_rate = (9 + 1) * 25 / 32 = 250 / 32 = 7.8125; + */ + err_code = usbvision_write_reg(usbvision, USBVISION_FRM_RATE, frame_drop); + return err_code; +} + + +/* + * usbvision_frames_alloc + * allocate the required frames + */ +int usbvision_frames_alloc(struct usb_usbvision *usbvision, int number_of_frames) +{ + int i; + + /* needs to be page aligned cause the buffers can be mapped individually! */ + usbvision->max_frame_size = PAGE_ALIGN(usbvision->curwidth * + usbvision->curheight * + usbvision->palette.bytes_per_pixel); + + /* Try to do my best to allocate the frames the user want in the remaining memory */ + usbvision->num_frames = number_of_frames; + while (usbvision->num_frames > 0) { + usbvision->fbuf_size = usbvision->num_frames * usbvision->max_frame_size; + usbvision->fbuf = usbvision_rvmalloc(usbvision->fbuf_size); + if (usbvision->fbuf) + break; + usbvision->num_frames--; + } + + /* Allocate all buffers */ + for (i = 0; i < usbvision->num_frames; i++) { + usbvision->frame[i].index = i; + usbvision->frame[i].grabstate = frame_state_unused; + usbvision->frame[i].data = usbvision->fbuf + + i * usbvision->max_frame_size; + /* + * Set default sizes for read operation. + */ + usbvision->stretch_width = 1; + usbvision->stretch_height = 1; + usbvision->frame[i].width = usbvision->curwidth; + usbvision->frame[i].height = usbvision->curheight; + usbvision->frame[i].bytes_read = 0; + } + PDEBUG(DBG_FUNC, "allocated %d frames (%d bytes per frame)", + usbvision->num_frames, usbvision->max_frame_size); + return usbvision->num_frames; +} + +/* + * usbvision_frames_free + * frees memory allocated for the frames + */ +void usbvision_frames_free(struct usb_usbvision *usbvision) +{ + /* Have to free all that memory */ + PDEBUG(DBG_FUNC, "free %d frames", usbvision->num_frames); + + if (usbvision->fbuf != NULL) { + usbvision_rvfree(usbvision->fbuf, usbvision->fbuf_size); + usbvision->fbuf = NULL; + + usbvision->num_frames = 0; + } +} +/* + * usbvision_empty_framequeues() + * prepare queues for incoming and outgoing frames + */ +void usbvision_empty_framequeues(struct usb_usbvision *usbvision) +{ + u32 i; + + INIT_LIST_HEAD(&(usbvision->inqueue)); + INIT_LIST_HEAD(&(usbvision->outqueue)); + + for (i = 0; i < USBVISION_NUMFRAMES; i++) { + usbvision->frame[i].grabstate = frame_state_unused; + usbvision->frame[i].bytes_read = 0; + } +} + +/* + * usbvision_stream_interrupt() + * stops streaming + */ +int usbvision_stream_interrupt(struct usb_usbvision *usbvision) +{ + int ret = 0; + + /* stop reading from the device */ + + usbvision->streaming = stream_interrupt; + ret = wait_event_timeout(usbvision->wait_stream, + (usbvision->streaming == stream_idle), + msecs_to_jiffies(USBVISION_NUMSBUF*USBVISION_URB_FRAMES)); + return ret; +} + +/* + * usbvision_set_compress_params() + * + */ + +static int usbvision_set_compress_params(struct usb_usbvision *usbvision) +{ + static const char proc[] = "usbvision_set_compression_params: "; + int rc; + unsigned char *value = usbvision->ctrl_urb_buffer; + + value[0] = 0x0F; /* Intra-Compression cycle */ + value[1] = 0x01; /* Reg.45 one line per strip */ + value[2] = 0x00; /* Reg.46 Force intra mode on all new frames */ + value[3] = 0x00; /* Reg.47 FORCE_UP <- 0 normal operation (not force) */ + value[4] = 0xA2; /* Reg.48 BUF_THR I'm not sure if this does something in not compressed mode. */ + value[5] = 0x00; /* Reg.49 DVI_YUV This has nothing to do with compression */ + + /* caught values for NT1004 */ + /* value[0] = 0xFF; Never apply intra mode automatically */ + /* value[1] = 0xF1; Use full frame height for virtual strip width; One line per strip */ + /* value[2] = 0x01; Force intra mode on all new frames */ + /* value[3] = 0x00; Strip size 400 Bytes; do not force up */ + /* value[4] = 0xA2; */ + if (!USBVISION_IS_OPERATIONAL(usbvision)) + return 0; + + rc = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1), + USBVISION_OP_CODE, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_ENDPOINT, 0, + (__u16) USBVISION_INTRA_CYC, value, 5, HZ); + + if (rc < 0) { + printk(KERN_ERR "%sERROR=%d. USBVISION stopped - reconnect or reload driver.\n", + proc, rc); + return rc; + } + + if (usbvision->bridge_type == BRIDGE_NT1004) { + value[0] = 20; /* PCM Threshold 1 */ + value[1] = 12; /* PCM Threshold 2 */ + value[2] = 255; /* Distortion Threshold inter */ + value[3] = 255; /* Distortion Threshold intra */ + value[4] = 43; /* Max Distortion inter */ + value[5] = 43; /* Max Distortion intra */ + } else { + value[0] = 20; /* PCM Threshold 1 */ + value[1] = 12; /* PCM Threshold 2 */ + value[2] = 255; /* Distortion Threshold d7-d0 */ + value[3] = 0; /* Distortion Threshold d11-d8 */ + value[4] = 43; /* Max Distortion d7-d0 */ + value[5] = 0; /* Max Distortion d8 */ + } + + if (!USBVISION_IS_OPERATIONAL(usbvision)) + return 0; + + rc = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1), + USBVISION_OP_CODE, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_ENDPOINT, 0, + (__u16) USBVISION_PCM_THR1, value, 6, HZ); + + if (rc < 0) { + printk(KERN_ERR "%sERROR=%d. USBVISION stopped - reconnect or reload driver.\n", + proc, rc); + } + return rc; +} + + +/* + * usbvision_set_input() + * + * Set the input (saa711x, ...) size x y and other misc input params + * I've no idea if this parameters are right + * + */ +int usbvision_set_input(struct usb_usbvision *usbvision) +{ + static const char proc[] = "usbvision_set_input: "; + int rc; + unsigned char *value = usbvision->ctrl_urb_buffer; + unsigned char dvi_yuv_value; + + if (!USBVISION_IS_OPERATIONAL(usbvision)) + return 0; + + /* Set input format expected from decoder*/ + if (usbvision_device_data[usbvision->dev_model].vin_reg1_override) { + value[0] = usbvision_device_data[usbvision->dev_model].vin_reg1; + } else if (usbvision_device_data[usbvision->dev_model].codec == CODEC_SAA7113) { + /* SAA7113 uses 8 bit output */ + value[0] = USBVISION_8_422_SYNC; + } else { + /* I'm sure only about d2-d0 [010] 16 bit 4:2:2 using sync pulses + * as that is how saa7111 is configured */ + value[0] = USBVISION_16_422_SYNC; + /* | USBVISION_VSNC_POL | USBVISION_VCLK_POL);*/ + } + + rc = usbvision_write_reg(usbvision, USBVISION_VIN_REG1, value[0]); + if (rc < 0) { + printk(KERN_ERR "%sERROR=%d. USBVISION stopped - reconnect or reload driver.\n", + proc, rc); + return rc; + } + + + if (usbvision->tvnorm_id & V4L2_STD_PAL) { + value[0] = 0xC0; + value[1] = 0x02; /* 0x02C0 -> 704 Input video line length */ + value[2] = 0x20; + value[3] = 0x01; /* 0x0120 -> 288 Input video n. of lines */ + value[4] = 0x60; + value[5] = 0x00; /* 0x0060 -> 96 Input video h offset */ + value[6] = 0x16; + value[7] = 0x00; /* 0x0016 -> 22 Input video v offset */ + } else if (usbvision->tvnorm_id & V4L2_STD_SECAM) { + value[0] = 0xC0; + value[1] = 0x02; /* 0x02C0 -> 704 Input video line length */ + value[2] = 0x20; + value[3] = 0x01; /* 0x0120 -> 288 Input video n. of lines */ + value[4] = 0x01; + value[5] = 0x00; /* 0x0001 -> 01 Input video h offset */ + value[6] = 0x01; + value[7] = 0x00; /* 0x0001 -> 01 Input video v offset */ + } else { /* V4L2_STD_NTSC */ + value[0] = 0xD0; + value[1] = 0x02; /* 0x02D0 -> 720 Input video line length */ + value[2] = 0xF0; + value[3] = 0x00; /* 0x00F0 -> 240 Input video number of lines */ + value[4] = 0x50; + value[5] = 0x00; /* 0x0050 -> 80 Input video h offset */ + value[6] = 0x10; + value[7] = 0x00; /* 0x0010 -> 16 Input video v offset */ + } + + /* webcam is only 480 pixels wide, both PAL and NTSC version */ + if (usbvision_device_data[usbvision->dev_model].codec == CODEC_WEBCAM) { + value[0] = 0xe0; + value[1] = 0x01; /* 0x01E0 -> 480 Input video line length */ + } + + if (usbvision_device_data[usbvision->dev_model].x_offset >= 0) { + value[4] = usbvision_device_data[usbvision->dev_model].x_offset & 0xff; + value[5] = (usbvision_device_data[usbvision->dev_model].x_offset & 0x0300) >> 8; + } + + if (adjust_x_offset != -1) { + value[4] = adjust_x_offset & 0xff; + value[5] = (adjust_x_offset & 0x0300) >> 8; + } + + if (usbvision_device_data[usbvision->dev_model].y_offset >= 0) { + value[6] = usbvision_device_data[usbvision->dev_model].y_offset & 0xff; + value[7] = (usbvision_device_data[usbvision->dev_model].y_offset & 0x0300) >> 8; + } + + if (adjust_y_offset != -1) { + value[6] = adjust_y_offset & 0xff; + value[7] = (adjust_y_offset & 0x0300) >> 8; + } + + rc = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1), + USBVISION_OP_CODE, /* USBVISION specific code */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT, 0, + (__u16) USBVISION_LXSIZE_I, value, 8, HZ); + if (rc < 0) { + printk(KERN_ERR "%sERROR=%d. USBVISION stopped - reconnect or reload driver.\n", + proc, rc); + return rc; + } + + + dvi_yuv_value = 0x00; /* U comes after V, Ya comes after U/V, Yb comes after Yb */ + + if (usbvision_device_data[usbvision->dev_model].dvi_yuv_override) { + dvi_yuv_value = usbvision_device_data[usbvision->dev_model].dvi_yuv; + } else if (usbvision_device_data[usbvision->dev_model].codec == CODEC_SAA7113) { + /* This changes as the fine sync control changes. Further investigation necessary */ + dvi_yuv_value = 0x06; + } + + return usbvision_write_reg(usbvision, USBVISION_DVI_YUV, dvi_yuv_value); +} + + +/* + * usbvision_set_dram_settings() + * + * Set the buffer address needed by the usbvision dram to operate + * This values has been taken with usbsnoop. + * + */ + +static int usbvision_set_dram_settings(struct usb_usbvision *usbvision) +{ + unsigned char *value = usbvision->ctrl_urb_buffer; + int rc; + + if (usbvision->isoc_mode == ISOC_MODE_COMPRESS) { + value[0] = 0x42; + value[1] = 0x71; + value[2] = 0xff; + value[3] = 0x00; + value[4] = 0x98; + value[5] = 0xe0; + value[6] = 0x71; + value[7] = 0xff; + /* UR: 0x0E200-0x3FFFF = 204288 Words (1 Word = 2 Byte) */ + /* FDL: 0x00000-0x0E099 = 57498 Words */ + /* VDW: 0x0E3FF-0x3FFFF */ + } else { + value[0] = 0x42; + value[1] = 0x00; + value[2] = 0xff; + value[3] = 0x00; + value[4] = 0x00; + value[5] = 0x00; + value[6] = 0x00; + value[7] = 0xff; + } + /* These are the values of the address of the video buffer, + * they have to be loaded into the USBVISION_DRM_PRM1-8 + * + * Start address of video output buffer for read: drm_prm1-2 -> 0x00000 + * End address of video output buffer for read: drm_prm1-3 -> 0x1ffff + * Start address of video frame delay buffer: drm_prm1-4 -> 0x20000 + * Only used in compressed mode + * End address of video frame delay buffer: drm_prm1-5-6 -> 0x3ffff + * Only used in compressed mode + * Start address of video output buffer for write: drm_prm1-7 -> 0x00000 + * End address of video output buffer for write: drm_prm1-8 -> 0x1ffff + */ + + if (!USBVISION_IS_OPERATIONAL(usbvision)) + return 0; + + rc = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1), + USBVISION_OP_CODE, /* USBVISION specific code */ + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_ENDPOINT, 0, + (__u16) USBVISION_DRM_PRM1, value, 8, HZ); + + if (rc < 0) { + dev_err(&usbvision->dev->dev, "%s: ERROR=%d\n", __func__, rc); + return rc; + } + + /* Restart the video buffer logic */ + rc = usbvision_write_reg(usbvision, USBVISION_DRM_CONT, USBVISION_RES_UR | + USBVISION_RES_FDL | USBVISION_RES_VDW); + if (rc < 0) + return rc; + rc = usbvision_write_reg(usbvision, USBVISION_DRM_CONT, 0x00); + + return rc; +} + +/* + * () + * + * Power on the device, enables suspend-resume logic + * & reset the isoc End-Point + * + */ + +int usbvision_power_on(struct usb_usbvision *usbvision) +{ + int err_code = 0; + + PDEBUG(DBG_FUNC, ""); + + usbvision_write_reg(usbvision, USBVISION_PWR_REG, USBVISION_SSPND_EN); + usbvision_write_reg(usbvision, USBVISION_PWR_REG, + USBVISION_SSPND_EN | USBVISION_RES2); + + if (usbvision_device_data[usbvision->dev_model].codec == CODEC_WEBCAM) { + usbvision_write_reg(usbvision, USBVISION_VIN_REG1, + USBVISION_16_422_SYNC | USBVISION_HVALID_PO); + usbvision_write_reg(usbvision, USBVISION_VIN_REG2, + USBVISION_NOHVALID | USBVISION_KEEP_BLANK); + } + usbvision_write_reg(usbvision, USBVISION_PWR_REG, + USBVISION_SSPND_EN | USBVISION_PWR_VID); + mdelay(10); + err_code = usbvision_write_reg(usbvision, USBVISION_PWR_REG, + USBVISION_SSPND_EN | USBVISION_PWR_VID | USBVISION_RES2); + if (err_code == 1) + usbvision->power = 1; + PDEBUG(DBG_FUNC, "%s: err_code %d", (err_code < 0) ? "ERROR" : "power is on", err_code); + return err_code; +} + + +/* + * usbvision_begin_streaming() + * Sure you have to put bit 7 to 0, if not incoming frames are dropped, but no + * idea about the rest + */ +int usbvision_begin_streaming(struct usb_usbvision *usbvision) +{ + if (usbvision->isoc_mode == ISOC_MODE_COMPRESS) + usbvision_init_compression(usbvision); + return usbvision_write_reg(usbvision, USBVISION_VIN_REG2, + USBVISION_NOHVALID | usbvision->vin_reg2_preset); +} + +/* + * usbvision_restart_isoc() + * Not sure yet if touching here PWR_REG make loose the config + */ + +int usbvision_restart_isoc(struct usb_usbvision *usbvision) +{ + int ret; + + ret = usbvision_write_reg(usbvision, USBVISION_PWR_REG, + USBVISION_SSPND_EN | USBVISION_PWR_VID); + if (ret < 0) + return ret; + ret = usbvision_write_reg(usbvision, USBVISION_PWR_REG, + USBVISION_SSPND_EN | USBVISION_PWR_VID | + USBVISION_RES2); + if (ret < 0) + return ret; + ret = usbvision_write_reg(usbvision, USBVISION_VIN_REG2, + USBVISION_KEEP_BLANK | USBVISION_NOHVALID | + usbvision->vin_reg2_preset); + if (ret < 0) + return ret; + + /* TODO: schedule timeout */ + while ((usbvision_read_reg(usbvision, USBVISION_STATUS_REG) & 0x01) != 1) + ; + + return 0; +} + +int usbvision_audio_off(struct usb_usbvision *usbvision) +{ + if (usbvision_write_reg(usbvision, USBVISION_IOPIN_REG, USBVISION_AUDIO_MUTE) < 0) { + printk(KERN_ERR "usbvision_audio_off: can't write reg\n"); + return -1; + } + usbvision->audio_mute = 0; + usbvision->audio_channel = USBVISION_AUDIO_MUTE; + return 0; +} + +int usbvision_set_audio(struct usb_usbvision *usbvision, int audio_channel) +{ + if (!usbvision->audio_mute) { + if (usbvision_write_reg(usbvision, USBVISION_IOPIN_REG, audio_channel) < 0) { + printk(KERN_ERR "usbvision_set_audio: can't write iopin register for audio switching\n"); + return -1; + } + } + usbvision->audio_channel = audio_channel; + return 0; +} + +int usbvision_setup(struct usb_usbvision *usbvision, int format) +{ + if (usbvision_device_data[usbvision->dev_model].codec == CODEC_WEBCAM) + usbvision_init_webcam(usbvision); + usbvision_set_video_format(usbvision, format); + usbvision_set_dram_settings(usbvision); + usbvision_set_compress_params(usbvision); + usbvision_set_input(usbvision); + usbvision_set_output(usbvision, MAX_USB_WIDTH, MAX_USB_HEIGHT); + usbvision_restart_isoc(usbvision); + + /* cosas del PCM */ + return USBVISION_IS_OPERATIONAL(usbvision); +} + +int usbvision_set_alternate(struct usb_usbvision *dev) +{ + int err_code, prev_alt = dev->iface_alt; + int i; + + dev->iface_alt = 0; + for (i = 0; i < dev->num_alt; i++) + if (dev->alt_max_pkt_size[i] > dev->alt_max_pkt_size[dev->iface_alt]) + dev->iface_alt = i; + + if (dev->iface_alt != prev_alt) { + dev->isoc_packet_size = dev->alt_max_pkt_size[dev->iface_alt]; + PDEBUG(DBG_FUNC, "setting alternate %d with max_packet_size=%u", + dev->iface_alt, dev->isoc_packet_size); + err_code = usb_set_interface(dev->dev, dev->iface, dev->iface_alt); + if (err_code < 0) { + dev_err(&dev->dev->dev, + "cannot change alternate number to %d (error=%i)\n", + dev->iface_alt, err_code); + return err_code; + } + } + + PDEBUG(DBG_ISOC, "ISO Packet Length:%d", dev->isoc_packet_size); + + return 0; +} + +/* + * usbvision_init_isoc() + * + */ +int usbvision_init_isoc(struct usb_usbvision *usbvision) +{ + struct usb_device *dev = usbvision->dev; + int buf_idx, err_code, reg_value; + int sb_size; + + if (!USBVISION_IS_OPERATIONAL(usbvision)) + return -EFAULT; + + usbvision->cur_frame = NULL; + scratch_reset(usbvision); + + /* Alternate interface 1 is is the biggest frame size */ + err_code = usbvision_set_alternate(usbvision); + if (err_code < 0) { + usbvision->last_error = err_code; + return -EBUSY; + } + sb_size = USBVISION_URB_FRAMES * usbvision->isoc_packet_size; + + reg_value = (16 - usbvision_read_reg(usbvision, + USBVISION_ALTER_REG)) & 0x0F; + + usbvision->usb_bandwidth = reg_value >> 1; + PDEBUG(DBG_ISOC, "USB Bandwidth Usage: %dMbit/Sec", + usbvision->usb_bandwidth); + + + + /* We double buffer the Iso lists */ + + for (buf_idx = 0; buf_idx < USBVISION_NUMSBUF; buf_idx++) { + int j, k; + struct urb *urb; + + urb = usb_alloc_urb(USBVISION_URB_FRAMES, GFP_KERNEL); + if (urb == NULL) + return -ENOMEM; + usbvision->sbuf[buf_idx].urb = urb; + usbvision->sbuf[buf_idx].data = + usb_alloc_coherent(usbvision->dev, + sb_size, + GFP_KERNEL, + &urb->transfer_dma); + if (!usbvision->sbuf[buf_idx].data) + return -ENOMEM; + + urb->dev = dev; + urb->context = usbvision; + urb->pipe = usb_rcvisocpipe(dev, usbvision->video_endp); + urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; + urb->interval = 1; + urb->transfer_buffer = usbvision->sbuf[buf_idx].data; + urb->complete = usbvision_isoc_irq; + urb->number_of_packets = USBVISION_URB_FRAMES; + urb->transfer_buffer_length = + usbvision->isoc_packet_size * USBVISION_URB_FRAMES; + for (j = k = 0; j < USBVISION_URB_FRAMES; j++, + k += usbvision->isoc_packet_size) { + urb->iso_frame_desc[j].offset = k; + urb->iso_frame_desc[j].length = + usbvision->isoc_packet_size; + } + } + + /* Submit all URBs */ + for (buf_idx = 0; buf_idx < USBVISION_NUMSBUF; buf_idx++) { + err_code = usb_submit_urb(usbvision->sbuf[buf_idx].urb, + GFP_KERNEL); + if (err_code) { + dev_err(&usbvision->dev->dev, + "%s: usb_submit_urb(%d) failed: error %d\n", + __func__, buf_idx, err_code); + } + } + + usbvision->streaming = stream_idle; + PDEBUG(DBG_ISOC, "%s: streaming=1 usbvision->video_endp=$%02x", + __func__, + usbvision->video_endp); + return 0; +} + +/* + * usbvision_stop_isoc() + * + * This procedure stops streaming and deallocates URBs. Then it + * activates zero-bandwidth alt. setting of the video interface. + * + */ +void usbvision_stop_isoc(struct usb_usbvision *usbvision) +{ + int buf_idx, err_code, reg_value; + int sb_size = USBVISION_URB_FRAMES * usbvision->isoc_packet_size; + + if ((usbvision->streaming == stream_off) || (usbvision->dev == NULL)) + return; + + /* Unschedule all of the iso td's */ + for (buf_idx = 0; buf_idx < USBVISION_NUMSBUF; buf_idx++) { + usb_kill_urb(usbvision->sbuf[buf_idx].urb); + if (usbvision->sbuf[buf_idx].data) { + usb_free_coherent(usbvision->dev, + sb_size, + usbvision->sbuf[buf_idx].data, + usbvision->sbuf[buf_idx].urb->transfer_dma); + } + usb_free_urb(usbvision->sbuf[buf_idx].urb); + usbvision->sbuf[buf_idx].urb = NULL; + } + + PDEBUG(DBG_ISOC, "%s: streaming=stream_off\n", __func__); + usbvision->streaming = stream_off; + + if (!usbvision->remove_pending) { + /* Set packet size to 0 */ + usbvision->iface_alt = 0; + err_code = usb_set_interface(usbvision->dev, usbvision->iface, + usbvision->iface_alt); + if (err_code < 0) { + dev_err(&usbvision->dev->dev, + "%s: usb_set_interface() failed: error %d\n", + __func__, err_code); + usbvision->last_error = err_code; + } + reg_value = (16-usbvision_read_reg(usbvision, USBVISION_ALTER_REG)) & 0x0F; + usbvision->isoc_packet_size = + (reg_value == 0) ? 0 : (reg_value * 64) - 1; + PDEBUG(DBG_ISOC, "ISO Packet Length:%d", + usbvision->isoc_packet_size); + + usbvision->usb_bandwidth = reg_value >> 1; + PDEBUG(DBG_ISOC, "USB Bandwidth Usage: %dMbit/Sec", + usbvision->usb_bandwidth); + } +} + +int usbvision_muxsel(struct usb_usbvision *usbvision, int channel) +{ + /* inputs #0 and #3 are constant for every SAA711x. */ + /* inputs #1 and #2 are variable for SAA7111 and SAA7113 */ + int mode[4] = { SAA7115_COMPOSITE0, 0, 0, SAA7115_COMPOSITE3 }; + int audio[] = { 1, 0, 0, 0 }; + /* channel 0 is TV with audiochannel 1 (tuner mono) */ + /* channel 1 is Composite with audio channel 0 (line in) */ + /* channel 2 is S-Video with audio channel 0 (line in) */ + /* channel 3 is additional video inputs to the device with audio channel 0 (line in) */ + + RESTRICT_TO_RANGE(channel, 0, usbvision->video_inputs); + usbvision->ctl_input = channel; + + /* set the new channel */ + /* Regular USB TV Tuners -> channel: 0 = Television, 1 = Composite, 2 = S-Video */ + /* Four video input devices -> channel: 0 = Chan White, 1 = Chan Green, 2 = Chan Yellow, 3 = Chan Red */ + + switch (usbvision_device_data[usbvision->dev_model].codec) { + case CODEC_SAA7113: + mode[1] = SAA7115_COMPOSITE2; + if (switch_svideo_input) { + /* To handle problems with S-Video Input for + * some devices. Use switch_svideo_input + * parameter when loading the module.*/ + mode[2] = SAA7115_COMPOSITE1; + } else { + mode[2] = SAA7115_SVIDEO1; + } + break; + case CODEC_SAA7111: + default: + /* modes for saa7111 */ + mode[1] = SAA7115_COMPOSITE1; + mode[2] = SAA7115_SVIDEO1; + break; + } + call_all(usbvision, video, s_routing, mode[channel], 0, 0); + usbvision_set_audio(usbvision, audio[channel]); + return 0; +} diff --git a/drivers/staging/media/usbvision/usbvision-i2c.c b/drivers/staging/media/usbvision/usbvision-i2c.c new file mode 100644 index 000000000000..6e4df3335b1b --- /dev/null +++ b/drivers/staging/media/usbvision/usbvision-i2c.c @@ -0,0 +1,438 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * usbvision_i2c.c + * i2c algorithm for USB-I2C Bridges + * + * Copyright (c) 1999-2007 Joerg Heckenbach <joerg@heckenbach-aw.de> + * Dwaine Garden <dwainegarden@rogers.com> + * + * This module is part of usbvision driver project. + * Updates to driver completed by Dwaine P. Garden + */ + + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/uaccess.h> +#include <linux/ioport.h> +#include <linux/errno.h> +#include <linux/usb.h> +#include <linux/i2c.h> +#include "usbvision.h" + +#define DBG_I2C (1 << 0) + +static int i2c_debug; + +module_param(i2c_debug, int, 0644); /* debug_i2c_usb mode of the device driver */ +MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]"); + +#define PDEBUG(level, fmt, args...) { \ + if (i2c_debug & (level)) \ + printk(KERN_INFO KBUILD_MODNAME ":[%s:%d] " fmt, \ + __func__, __LINE__ , ## args); \ + } + +static int usbvision_i2c_write(struct usb_usbvision *usbvision, unsigned char addr, char *buf, + short len); +static int usbvision_i2c_read(struct usb_usbvision *usbvision, unsigned char addr, char *buf, + short len); + +static inline int try_write_address(struct i2c_adapter *i2c_adap, + unsigned char addr, int retries) +{ + struct usb_usbvision *usbvision; + int i, ret = -1; + char buf[4]; + + usbvision = (struct usb_usbvision *)i2c_get_adapdata(i2c_adap); + buf[0] = 0x00; + for (i = 0; i <= retries; i++) { + ret = (usbvision_i2c_write(usbvision, addr, buf, 1)); + if (ret == 1) + break; /* success! */ + udelay(5); + if (i == retries) /* no success */ + break; + udelay(10); + } + if (i) { + PDEBUG(DBG_I2C, "Needed %d retries for address %#2x", i, addr); + PDEBUG(DBG_I2C, "Maybe there's no device at this address"); + } + return ret; +} + +static inline int try_read_address(struct i2c_adapter *i2c_adap, + unsigned char addr, int retries) +{ + struct usb_usbvision *usbvision; + int i, ret = -1; + char buf[4]; + + usbvision = (struct usb_usbvision *)i2c_get_adapdata(i2c_adap); + for (i = 0; i <= retries; i++) { + ret = (usbvision_i2c_read(usbvision, addr, buf, 1)); + if (ret == 1) + break; /* success! */ + udelay(5); + if (i == retries) /* no success */ + break; + udelay(10); + } + if (i) { + PDEBUG(DBG_I2C, "Needed %d retries for address %#2x", i, addr); + PDEBUG(DBG_I2C, "Maybe there's no device at this address"); + } + return ret; +} + +static inline int usb_find_address(struct i2c_adapter *i2c_adap, + struct i2c_msg *msg, int retries, + unsigned char *add) +{ + unsigned short flags = msg->flags; + + unsigned char addr; + int ret; + + addr = (msg->addr << 1); + if (flags & I2C_M_RD) + addr |= 1; + + add[0] = addr; + if (flags & I2C_M_RD) + ret = try_read_address(i2c_adap, addr, retries); + else + ret = try_write_address(i2c_adap, addr, retries); + + if (ret != 1) + return -EREMOTEIO; + + return 0; +} + +static int +usbvision_i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num) +{ + struct i2c_msg *pmsg; + struct usb_usbvision *usbvision; + int i, ret; + unsigned char addr = 0; + + usbvision = (struct usb_usbvision *)i2c_get_adapdata(i2c_adap); + + for (i = 0; i < num; i++) { + pmsg = &msgs[i]; + ret = usb_find_address(i2c_adap, pmsg, i2c_adap->retries, &addr); + if (ret != 0) { + PDEBUG(DBG_I2C, "got NAK from device, message #%d", i); + return (ret < 0) ? ret : -EREMOTEIO; + } + + if (pmsg->flags & I2C_M_RD) { + /* read bytes into buffer */ + ret = (usbvision_i2c_read(usbvision, addr, pmsg->buf, pmsg->len)); + if (ret < pmsg->len) + return (ret < 0) ? ret : -EREMOTEIO; + } else { + /* write bytes from buffer */ + ret = (usbvision_i2c_write(usbvision, addr, pmsg->buf, pmsg->len)); + if (ret < pmsg->len) + return (ret < 0) ? ret : -EREMOTEIO; + } + } + return num; +} + +static u32 functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +/* -----exported algorithm data: ------------------------------------- */ + +static const struct i2c_algorithm usbvision_algo = { + .master_xfer = usbvision_i2c_xfer, + .smbus_xfer = NULL, + .functionality = functionality, +}; + + +/* ----------------------------------------------------------------------- */ +/* usbvision specific I2C functions */ +/* ----------------------------------------------------------------------- */ +static const struct i2c_adapter i2c_adap_template; + +int usbvision_i2c_register(struct usb_usbvision *usbvision) +{ + static unsigned short saa711x_addrs[] = { + 0x4a >> 1, 0x48 >> 1, /* SAA7111, SAA7111A and SAA7113 */ + 0x42 >> 1, 0x40 >> 1, /* SAA7114, SAA7115 and SAA7118 */ + I2C_CLIENT_END }; + + if (usbvision->registered_i2c) + return 0; + + usbvision->i2c_adap = i2c_adap_template; + + snprintf(usbvision->i2c_adap.name, sizeof(usbvision->i2c_adap.name), + "usbvision-%d-%s", + usbvision->dev->bus->busnum, usbvision->dev->devpath); + PDEBUG(DBG_I2C, "Adaptername: %s", usbvision->i2c_adap.name); + usbvision->i2c_adap.dev.parent = &usbvision->dev->dev; + + i2c_set_adapdata(&usbvision->i2c_adap, &usbvision->v4l2_dev); + + if (usbvision_write_reg(usbvision, USBVISION_SER_MODE, USBVISION_IIC_LRNACK) < 0) { + printk(KERN_ERR "usbvision_i2c_register: can't write reg\n"); + return -EBUSY; + } + + PDEBUG(DBG_I2C, "I2C debugging is enabled [i2c]"); + PDEBUG(DBG_I2C, "ALGO debugging is enabled [i2c]"); + + /* register new adapter to i2c module... */ + + usbvision->i2c_adap.algo = &usbvision_algo; + + usbvision->i2c_adap.timeout = 100; /* default values, should */ + usbvision->i2c_adap.retries = 3; /* be replaced by defines */ + + i2c_add_adapter(&usbvision->i2c_adap); + + PDEBUG(DBG_I2C, "i2c bus for %s registered", usbvision->i2c_adap.name); + + /* Request the load of the i2c modules we need */ + switch (usbvision_device_data[usbvision->dev_model].codec) { + case CODEC_SAA7113: + case CODEC_SAA7111: + /* Without this delay the detection of the saa711x is + hit-and-miss. */ + mdelay(10); + v4l2_i2c_new_subdev(&usbvision->v4l2_dev, + &usbvision->i2c_adap, + "saa7115_auto", 0, saa711x_addrs); + break; + } + if (usbvision_device_data[usbvision->dev_model].tuner == 1) { + struct v4l2_subdev *sd; + enum v4l2_i2c_tuner_type type; + struct tuner_setup tun_setup; + + sd = v4l2_i2c_new_subdev(&usbvision->v4l2_dev, + &usbvision->i2c_adap, + "tuner", 0, v4l2_i2c_tuner_addrs(ADDRS_DEMOD)); + /* depending on whether we found a demod or not, select + the tuner type. */ + type = sd ? ADDRS_TV_WITH_DEMOD : ADDRS_TV; + + sd = v4l2_i2c_new_subdev(&usbvision->v4l2_dev, + &usbvision->i2c_adap, + "tuner", 0, v4l2_i2c_tuner_addrs(type)); + + if (sd == NULL) + return -ENODEV; + if (usbvision->tuner_type != -1) { + tun_setup.mode_mask = T_ANALOG_TV | T_RADIO; + tun_setup.type = usbvision->tuner_type; + tun_setup.addr = v4l2_i2c_subdev_addr(sd); + call_all(usbvision, tuner, s_type_addr, &tun_setup); + } + } + usbvision->registered_i2c = 1; + + return 0; +} + +int usbvision_i2c_unregister(struct usb_usbvision *usbvision) +{ + if (!usbvision->registered_i2c) + return 0; + + i2c_del_adapter(&(usbvision->i2c_adap)); + usbvision->registered_i2c = 0; + + PDEBUG(DBG_I2C, "i2c bus for %s unregistered", usbvision->i2c_adap.name); + + return 0; +} + +static int +usbvision_i2c_read_max4(struct usb_usbvision *usbvision, unsigned char addr, + char *buf, short len) +{ + int rc, retries; + + for (retries = 5;;) { + rc = usbvision_write_reg(usbvision, USBVISION_SER_ADRS, addr); + if (rc < 0) + return rc; + + /* Initiate byte read cycle */ + /* USBVISION_SER_CONT <- d0-d2 n. of bytes to r/w */ + /* d3 0=Wr 1=Rd */ + rc = usbvision_write_reg(usbvision, USBVISION_SER_CONT, + (len & 0x07) | 0x18); + if (rc < 0) + return rc; + + /* Test for Busy and ACK */ + do { + /* USBVISION_SER_CONT -> d4 == 0 busy */ + rc = usbvision_read_reg(usbvision, USBVISION_SER_CONT); + } while (rc > 0 && ((rc & 0x10) != 0)); /* Retry while busy */ + if (rc < 0) + return rc; + + /* USBVISION_SER_CONT -> d5 == 1 Not ack */ + if ((rc & 0x20) == 0) /* Ack? */ + break; + + /* I2C abort */ + rc = usbvision_write_reg(usbvision, USBVISION_SER_CONT, 0x00); + if (rc < 0) + return rc; + + if (--retries < 0) + return -1; + } + + switch (len) { + case 4: + buf[3] = usbvision_read_reg(usbvision, USBVISION_SER_DAT4); + /* fall through */ + case 3: + buf[2] = usbvision_read_reg(usbvision, USBVISION_SER_DAT3); + /* fall through */ + case 2: + buf[1] = usbvision_read_reg(usbvision, USBVISION_SER_DAT2); + /* fall through */ + case 1: + buf[0] = usbvision_read_reg(usbvision, USBVISION_SER_DAT1); + break; + default: + printk(KERN_ERR + "usbvision_i2c_read_max4: buffer length > 4\n"); + } + + if (i2c_debug & DBG_I2C) { + int idx; + + for (idx = 0; idx < len; idx++) + PDEBUG(DBG_I2C, "read %x from address %x", (unsigned char)buf[idx], addr); + } + return len; +} + + +static int usbvision_i2c_write_max4(struct usb_usbvision *usbvision, + unsigned char addr, const char *buf, + short len) +{ + int rc, retries; + int i; + unsigned char *value = usbvision->ctrl_urb_buffer; + unsigned char ser_cont; + + ser_cont = (len & 0x07) | 0x10; + + value[0] = addr; + value[1] = ser_cont; + for (i = 0; i < len; i++) + value[i + 2] = buf[i]; + + for (retries = 5;;) { + rc = usb_control_msg(usbvision->dev, + usb_sndctrlpipe(usbvision->dev, 1), + USBVISION_OP_CODE, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_ENDPOINT, 0, + (__u16) USBVISION_SER_ADRS, value, + len + 2, HZ); + + if (rc < 0) + return rc; + + rc = usbvision_write_reg(usbvision, USBVISION_SER_CONT, + (len & 0x07) | 0x10); + if (rc < 0) + return rc; + + /* Test for Busy and ACK */ + do { + rc = usbvision_read_reg(usbvision, USBVISION_SER_CONT); + } while (rc > 0 && ((rc & 0x10) != 0)); /* Retry while busy */ + if (rc < 0) + return rc; + + if ((rc & 0x20) == 0) /* Ack? */ + break; + + /* I2C abort */ + usbvision_write_reg(usbvision, USBVISION_SER_CONT, 0x00); + + if (--retries < 0) + return -1; + + } + + if (i2c_debug & DBG_I2C) { + int idx; + + for (idx = 0; idx < len; idx++) + PDEBUG(DBG_I2C, "wrote %x at address %x", (unsigned char)buf[idx], addr); + } + return len; +} + +static int usbvision_i2c_write(struct usb_usbvision *usbvision, unsigned char addr, char *buf, + short len) +{ + char *buf_ptr = buf; + int retval; + int wrcount = 0; + int count; + int max_len = 4; + + while (len > 0) { + count = (len > max_len) ? max_len : len; + retval = usbvision_i2c_write_max4(usbvision, addr, buf_ptr, count); + if (retval > 0) { + len -= count; + buf_ptr += count; + wrcount += count; + } else + return (retval < 0) ? retval : -EFAULT; + } + return wrcount; +} + +static int usbvision_i2c_read(struct usb_usbvision *usbvision, unsigned char addr, char *buf, + short len) +{ + char temp[4]; + int retval, i; + int rdcount = 0; + int count; + + while (len > 0) { + count = (len > 3) ? 4 : len; + retval = usbvision_i2c_read_max4(usbvision, addr, temp, count); + if (retval > 0) { + for (i = 0; i < len; i++) + buf[rdcount + i] = temp[i]; + len -= count; + rdcount += count; + } else + return (retval < 0) ? retval : -EFAULT; + } + return rdcount; +} + +static const struct i2c_adapter i2c_adap_template = { + .owner = THIS_MODULE, + .name = "usbvision", +}; diff --git a/drivers/staging/media/usbvision/usbvision-video.c b/drivers/staging/media/usbvision/usbvision-video.c new file mode 100644 index 000000000000..3ea25fdcf767 --- /dev/null +++ b/drivers/staging/media/usbvision/usbvision-video.c @@ -0,0 +1,1643 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * USB USBVISION Video device driver 0.9.10 + * + * Copyright (c) 1999-2005 Joerg Heckenbach <joerg@heckenbach-aw.de> + * + * This module is part of usbvision driver project. + * + * Let's call the version 0.... until compression decoding is completely + * implemented. + * + * This driver is written by Jose Ignacio Gijon and Joerg Heckenbach. + * It was based on USB CPiA driver written by Peter Pregler, + * Scott J. Bertin and Johannes Erdfelt + * Ideas are taken from bttv driver by Ralph Metzler, Marcus Metzler & + * Gerd Knorr and zoran 36120/36125 driver by Pauline Middelink + * Updates to driver completed by Dwaine P. Garden + * + * TODO: + * - use submit_urb for all setup packets + * - Fix memory settings for nt1004. It is 4 times as big as the + * nt1003 memory. + * - Add audio on endpoint 3 for nt1004 chip. + * Seems impossible, needs a codec interface. Which one? + * - Clean up the driver. + * - optimization for performance. + * - Add Videotext capability (VBI). Working on it..... + * - Check audio for other devices + */ + +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/timer.h> +#include <linux/slab.h> +#include <linux/mm.h> +#include <linux/highmem.h> +#include <linux/vmalloc.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/spinlock.h> +#include <linux/io.h> +#include <linux/videodev2.h> +#include <linux/i2c.h> + +#include <media/i2c/saa7115.h> +#include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-event.h> +#include <media/tuner.h> + +#include <linux/workqueue.h> + +#include "usbvision.h" +#include "usbvision-cards.h" + +#define DRIVER_AUTHOR \ + "Joerg Heckenbach <joerg@heckenbach-aw.de>, " \ + "Dwaine Garden <DwaineGarden@rogers.com>" +#define DRIVER_NAME "usbvision" +#define DRIVER_ALIAS "USBVision" +#define DRIVER_DESC "USBVision USB Video Device Driver for Linux" +#define USBVISION_VERSION_STRING "0.9.11" + +#define ENABLE_HEXDUMP 0 /* Enable if you need it */ + + +#ifdef USBVISION_DEBUG + #define PDEBUG(level, fmt, args...) { \ + if (video_debug & (level)) \ + printk(KERN_INFO KBUILD_MODNAME ":[%s:%d] " fmt, \ + __func__, __LINE__ , ## args); \ + } +#else + #define PDEBUG(level, fmt, args...) do {} while (0) +#endif + +#define DBG_IO (1 << 1) +#define DBG_PROBE (1 << 2) +#define DBG_MMAP (1 << 3) + +/* String operations */ +#define rmspace(str) while (*str == ' ') str++; +#define goto2next(str) while (*str != ' ') str++; while (*str == ' ') str++; + + +/* sequential number of usbvision device */ +static int usbvision_nr; + +static struct usbvision_v4l2_format_st usbvision_v4l2_format[] = { + { 1, 1, 8, V4L2_PIX_FMT_GREY }, + { 1, 2, 16, V4L2_PIX_FMT_RGB565 }, + { 1, 3, 24, V4L2_PIX_FMT_RGB24 }, + { 1, 4, 32, V4L2_PIX_FMT_RGB32 }, + { 1, 2, 16, V4L2_PIX_FMT_RGB555 }, + { 1, 2, 16, V4L2_PIX_FMT_YUYV }, + { 1, 2, 12, V4L2_PIX_FMT_YVU420 }, /* 1.5 ! */ + { 1, 2, 16, V4L2_PIX_FMT_YUV422P } +}; + +/* Function prototypes */ +static void usbvision_release(struct usb_usbvision *usbvision); + +/* Default initialization of device driver parameters */ +/* Set the default format for ISOC endpoint */ +static int isoc_mode = ISOC_MODE_COMPRESS; +/* Set the default Debug Mode of the device driver */ +static int video_debug; +/* Sequential Number of Video Device */ +static int video_nr = -1; +/* Sequential Number of Radio Device */ +static int radio_nr = -1; + +/* Grab parameters for the device driver */ + +/* Showing parameters under SYSFS */ +module_param(isoc_mode, int, 0444); +module_param(video_debug, int, 0444); +module_param(video_nr, int, 0444); +module_param(radio_nr, int, 0444); + +MODULE_PARM_DESC(isoc_mode, " Set the default format for ISOC endpoint. Default: 0x60 (Compression On)"); +MODULE_PARM_DESC(video_debug, " Set the default Debug Mode of the device driver. Default: 0 (Off)"); +MODULE_PARM_DESC(video_nr, "Set video device number (/dev/videoX). Default: -1 (autodetect)"); +MODULE_PARM_DESC(radio_nr, "Set radio device number (/dev/radioX). Default: -1 (autodetect)"); + + +/* Misc stuff */ +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_VERSION(USBVISION_VERSION_STRING); +MODULE_ALIAS(DRIVER_ALIAS); + + +/*****************************************************************************/ +/* SYSFS Code - Copied from the stv680.c usb module. */ +/* Device information is located at /sys/class/video4linux/video0 */ +/* Device parameters information is located at /sys/module/usbvision */ +/* Device USB Information is located at */ +/* /sys/bus/usb/drivers/USBVision Video Grabber */ +/*****************************************************************************/ + +#define YES_NO(x) ((x) ? "Yes" : "No") + +static inline struct usb_usbvision *cd_to_usbvision(struct device *cd) +{ + struct video_device *vdev = to_video_device(cd); + return video_get_drvdata(vdev); +} + +static ssize_t show_version(struct device *cd, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s\n", USBVISION_VERSION_STRING); +} +static DEVICE_ATTR(version, S_IRUGO, show_version, NULL); + +static ssize_t show_model(struct device *cd, + struct device_attribute *attr, char *buf) +{ + struct video_device *vdev = to_video_device(cd); + struct usb_usbvision *usbvision = video_get_drvdata(vdev); + return sprintf(buf, "%s\n", + usbvision_device_data[usbvision->dev_model].model_string); +} +static DEVICE_ATTR(model, S_IRUGO, show_model, NULL); + +static ssize_t show_hue(struct device *cd, + struct device_attribute *attr, char *buf) +{ + struct video_device *vdev = to_video_device(cd); + struct usb_usbvision *usbvision = video_get_drvdata(vdev); + s32 val = v4l2_ctrl_g_ctrl(v4l2_ctrl_find(&usbvision->hdl, + V4L2_CID_HUE)); + + return sprintf(buf, "%d\n", val); +} +static DEVICE_ATTR(hue, S_IRUGO, show_hue, NULL); + +static ssize_t show_contrast(struct device *cd, + struct device_attribute *attr, char *buf) +{ + struct video_device *vdev = to_video_device(cd); + struct usb_usbvision *usbvision = video_get_drvdata(vdev); + s32 val = v4l2_ctrl_g_ctrl(v4l2_ctrl_find(&usbvision->hdl, + V4L2_CID_CONTRAST)); + + return sprintf(buf, "%d\n", val); +} +static DEVICE_ATTR(contrast, S_IRUGO, show_contrast, NULL); + +static ssize_t show_brightness(struct device *cd, + struct device_attribute *attr, char *buf) +{ + struct video_device *vdev = to_video_device(cd); + struct usb_usbvision *usbvision = video_get_drvdata(vdev); + s32 val = v4l2_ctrl_g_ctrl(v4l2_ctrl_find(&usbvision->hdl, + V4L2_CID_BRIGHTNESS)); + + return sprintf(buf, "%d\n", val); +} +static DEVICE_ATTR(brightness, S_IRUGO, show_brightness, NULL); + +static ssize_t show_saturation(struct device *cd, + struct device_attribute *attr, char *buf) +{ + struct video_device *vdev = to_video_device(cd); + struct usb_usbvision *usbvision = video_get_drvdata(vdev); + s32 val = v4l2_ctrl_g_ctrl(v4l2_ctrl_find(&usbvision->hdl, + V4L2_CID_SATURATION)); + + return sprintf(buf, "%d\n", val); +} +static DEVICE_ATTR(saturation, S_IRUGO, show_saturation, NULL); + +static ssize_t show_streaming(struct device *cd, + struct device_attribute *attr, char *buf) +{ + struct video_device *vdev = to_video_device(cd); + struct usb_usbvision *usbvision = video_get_drvdata(vdev); + return sprintf(buf, "%s\n", + YES_NO(usbvision->streaming == stream_on ? 1 : 0)); +} +static DEVICE_ATTR(streaming, S_IRUGO, show_streaming, NULL); + +static ssize_t show_compression(struct device *cd, + struct device_attribute *attr, char *buf) +{ + struct video_device *vdev = to_video_device(cd); + struct usb_usbvision *usbvision = video_get_drvdata(vdev); + return sprintf(buf, "%s\n", + YES_NO(usbvision->isoc_mode == ISOC_MODE_COMPRESS)); +} +static DEVICE_ATTR(compression, S_IRUGO, show_compression, NULL); + +static ssize_t show_device_bridge(struct device *cd, + struct device_attribute *attr, char *buf) +{ + struct video_device *vdev = to_video_device(cd); + struct usb_usbvision *usbvision = video_get_drvdata(vdev); + return sprintf(buf, "%d\n", usbvision->bridge_type); +} +static DEVICE_ATTR(bridge, S_IRUGO, show_device_bridge, NULL); + +static void usbvision_create_sysfs(struct video_device *vdev) +{ + int res; + + if (!vdev) + return; + do { + res = device_create_file(&vdev->dev, &dev_attr_version); + if (res < 0) + break; + res = device_create_file(&vdev->dev, &dev_attr_model); + if (res < 0) + break; + res = device_create_file(&vdev->dev, &dev_attr_hue); + if (res < 0) + break; + res = device_create_file(&vdev->dev, &dev_attr_contrast); + if (res < 0) + break; + res = device_create_file(&vdev->dev, &dev_attr_brightness); + if (res < 0) + break; + res = device_create_file(&vdev->dev, &dev_attr_saturation); + if (res < 0) + break; + res = device_create_file(&vdev->dev, &dev_attr_streaming); + if (res < 0) + break; + res = device_create_file(&vdev->dev, &dev_attr_compression); + if (res < 0) + break; + res = device_create_file(&vdev->dev, &dev_attr_bridge); + if (res >= 0) + return; + } while (0); + + dev_err(&vdev->dev, "%s error: %d\n", __func__, res); +} + +static void usbvision_remove_sysfs(struct video_device *vdev) +{ + if (vdev) { + device_remove_file(&vdev->dev, &dev_attr_version); + device_remove_file(&vdev->dev, &dev_attr_model); + device_remove_file(&vdev->dev, &dev_attr_hue); + device_remove_file(&vdev->dev, &dev_attr_contrast); + device_remove_file(&vdev->dev, &dev_attr_brightness); + device_remove_file(&vdev->dev, &dev_attr_saturation); + device_remove_file(&vdev->dev, &dev_attr_streaming); + device_remove_file(&vdev->dev, &dev_attr_compression); + device_remove_file(&vdev->dev, &dev_attr_bridge); + } +} + +/* + * usbvision_open() + * + * This is part of Video 4 Linux API. The driver can be opened by one + * client only (checks internal counter 'usbvision->user'). The procedure + * then allocates buffers needed for video processing. + * + */ +static int usbvision_v4l2_open(struct file *file) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + int err_code = 0; + + PDEBUG(DBG_IO, "open"); + + if (mutex_lock_interruptible(&usbvision->v4l2_lock)) + return -ERESTARTSYS; + + if (usbvision->remove_pending) { + err_code = -ENODEV; + goto unlock; + } + if (usbvision->user) { + err_code = -EBUSY; + } else { + err_code = v4l2_fh_open(file); + if (err_code) + goto unlock; + + /* Allocate memory for the scratch ring buffer */ + err_code = usbvision_scratch_alloc(usbvision); + if (isoc_mode == ISOC_MODE_COMPRESS) { + /* Allocate intermediate decompression buffers + only if needed */ + err_code = usbvision_decompress_alloc(usbvision); + } + if (err_code) { + /* Deallocate all buffers if trouble */ + usbvision_scratch_free(usbvision); + usbvision_decompress_free(usbvision); + } + } + + /* If so far no errors then we shall start the camera */ + if (!err_code) { + /* Send init sequence only once, it's large! */ + if (!usbvision->initialized) { + int setup_ok = 0; + setup_ok = usbvision_setup(usbvision, isoc_mode); + if (setup_ok) + usbvision->initialized = 1; + else + err_code = -EBUSY; + } + + if (!err_code) { + usbvision_begin_streaming(usbvision); + err_code = usbvision_init_isoc(usbvision); + /* device must be initialized before isoc transfer */ + usbvision_muxsel(usbvision, 0); + + /* prepare queues */ + usbvision_empty_framequeues(usbvision); + usbvision->user++; + } + } + +unlock: + mutex_unlock(&usbvision->v4l2_lock); + + PDEBUG(DBG_IO, "success"); + return err_code; +} + +/* + * usbvision_v4l2_close() + * + * This is part of Video 4 Linux API. The procedure + * stops streaming and deallocates all buffers that were earlier + * allocated in usbvision_v4l2_open(). + * + */ +static int usbvision_v4l2_close(struct file *file) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + int r; + + PDEBUG(DBG_IO, "close"); + + mutex_lock(&usbvision->v4l2_lock); + usbvision_audio_off(usbvision); + usbvision_restart_isoc(usbvision); + usbvision_stop_isoc(usbvision); + + usbvision_decompress_free(usbvision); + usbvision_frames_free(usbvision); + usbvision_empty_framequeues(usbvision); + usbvision_scratch_free(usbvision); + + usbvision->user--; + r = usbvision->remove_pending; + mutex_unlock(&usbvision->v4l2_lock); + + if (r) { + printk(KERN_INFO "%s: Final disconnect\n", __func__); + usbvision_release(usbvision); + return 0; + } + + PDEBUG(DBG_IO, "success"); + return v4l2_fh_release(file); +} + + +/* + * usbvision_ioctl() + * + * This is part of Video 4 Linux API. The procedure handles ioctl() calls. + * + */ +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int vidioc_g_register(struct file *file, void *priv, + struct v4l2_dbg_register *reg) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + int err_code; + + /* NT100x has a 8-bit register space */ + err_code = usbvision_read_reg(usbvision, reg->reg&0xff); + if (err_code < 0) { + dev_err(&usbvision->vdev.dev, + "%s: VIDIOC_DBG_G_REGISTER failed: error %d\n", + __func__, err_code); + return err_code; + } + reg->val = err_code; + reg->size = 1; + return 0; +} + +static int vidioc_s_register(struct file *file, void *priv, + const struct v4l2_dbg_register *reg) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + int err_code; + + /* NT100x has a 8-bit register space */ + err_code = usbvision_write_reg(usbvision, reg->reg & 0xff, reg->val); + if (err_code < 0) { + dev_err(&usbvision->vdev.dev, + "%s: VIDIOC_DBG_S_REGISTER failed: error %d\n", + __func__, err_code); + return err_code; + } + return 0; +} +#endif + +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *vc) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + + if (!usbvision->dev) + return -ENODEV; + + strscpy(vc->driver, "USBVision", sizeof(vc->driver)); + strscpy(vc->card, + usbvision_device_data[usbvision->dev_model].model_string, + sizeof(vc->card)); + usb_make_path(usbvision->dev, vc->bus_info, sizeof(vc->bus_info)); + vc->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING | V4L2_CAP_DEVICE_CAPS; + if (usbvision_device_data[usbvision->dev_model].radio) + vc->capabilities |= V4L2_CAP_RADIO; + if (usbvision->have_tuner) + vc->capabilities |= V4L2_CAP_TUNER; + return 0; +} + +static int vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *vi) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + int chan; + + if (vi->index >= usbvision->video_inputs) + return -EINVAL; + if (usbvision->have_tuner) + chan = vi->index; + else + chan = vi->index + 1; /* skip Television string*/ + + /* Determine the requested input characteristics + specific for each usbvision card model */ + switch (chan) { + case 0: + if (usbvision_device_data[usbvision->dev_model].video_channels == 4) { + strscpy(vi->name, "White Video Input", sizeof(vi->name)); + } else { + strscpy(vi->name, "Television", sizeof(vi->name)); + vi->type = V4L2_INPUT_TYPE_TUNER; + vi->tuner = chan; + vi->std = USBVISION_NORMS; + } + break; + case 1: + vi->type = V4L2_INPUT_TYPE_CAMERA; + if (usbvision_device_data[usbvision->dev_model].video_channels == 4) + strscpy(vi->name, "Green Video Input", sizeof(vi->name)); + else + strscpy(vi->name, "Composite Video Input", + sizeof(vi->name)); + vi->std = USBVISION_NORMS; + break; + case 2: + vi->type = V4L2_INPUT_TYPE_CAMERA; + if (usbvision_device_data[usbvision->dev_model].video_channels == 4) + strscpy(vi->name, "Yellow Video Input", sizeof(vi->name)); + else + strscpy(vi->name, "S-Video Input", sizeof(vi->name)); + vi->std = USBVISION_NORMS; + break; + case 3: + vi->type = V4L2_INPUT_TYPE_CAMERA; + strscpy(vi->name, "Red Video Input", sizeof(vi->name)); + vi->std = USBVISION_NORMS; + break; + } + return 0; +} + +static int vidioc_g_input(struct file *file, void *priv, unsigned int *input) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + + *input = usbvision->ctl_input; + return 0; +} + +static int vidioc_s_input(struct file *file, void *priv, unsigned int input) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + + if (input >= usbvision->video_inputs) + return -EINVAL; + + usbvision_muxsel(usbvision, input); + usbvision_set_input(usbvision); + usbvision_set_output(usbvision, + usbvision->curwidth, + usbvision->curheight); + return 0; +} + +static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id id) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + + usbvision->tvnorm_id = id; + + call_all(usbvision, video, s_std, usbvision->tvnorm_id); + /* propagate the change to the decoder */ + usbvision_muxsel(usbvision, usbvision->ctl_input); + + return 0; +} + +static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + + *id = usbvision->tvnorm_id; + return 0; +} + +static int vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *vt) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + + if (vt->index) /* Only tuner 0 */ + return -EINVAL; + if (vt->type == V4L2_TUNER_RADIO) + strscpy(vt->name, "Radio", sizeof(vt->name)); + else + strscpy(vt->name, "Television", sizeof(vt->name)); + + /* Let clients fill in the remainder of this struct */ + call_all(usbvision, tuner, g_tuner, vt); + + return 0; +} + +static int vidioc_s_tuner(struct file *file, void *priv, + const struct v4l2_tuner *vt) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + + /* Only one tuner for now */ + if (vt->index) + return -EINVAL; + /* let clients handle this */ + call_all(usbvision, tuner, s_tuner, vt); + + return 0; +} + +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *freq) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + + /* Only one tuner */ + if (freq->tuner) + return -EINVAL; + if (freq->type == V4L2_TUNER_RADIO) + freq->frequency = usbvision->radio_freq; + else + freq->frequency = usbvision->tv_freq; + + return 0; +} + +static int vidioc_s_frequency(struct file *file, void *priv, + const struct v4l2_frequency *freq) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + struct v4l2_frequency new_freq = *freq; + + /* Only one tuner for now */ + if (freq->tuner) + return -EINVAL; + + call_all(usbvision, tuner, s_frequency, freq); + call_all(usbvision, tuner, g_frequency, &new_freq); + if (freq->type == V4L2_TUNER_RADIO) + usbvision->radio_freq = new_freq.frequency; + else + usbvision->tv_freq = new_freq.frequency; + + return 0; +} + +static int vidioc_reqbufs(struct file *file, + void *priv, struct v4l2_requestbuffers *vr) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + int ret; + + RESTRICT_TO_RANGE(vr->count, 1, USBVISION_NUMFRAMES); + + /* Check input validity: + the user must do a VIDEO CAPTURE and MMAP method. */ + if (vr->memory != V4L2_MEMORY_MMAP) + return -EINVAL; + + if (usbvision->streaming == stream_on) { + ret = usbvision_stream_interrupt(usbvision); + if (ret) + return ret; + } + + usbvision_frames_free(usbvision); + usbvision_empty_framequeues(usbvision); + vr->count = usbvision_frames_alloc(usbvision, vr->count); + + usbvision->cur_frame = NULL; + + return 0; +} + +static int vidioc_querybuf(struct file *file, + void *priv, struct v4l2_buffer *vb) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + struct usbvision_frame *frame; + + /* FIXME : must control + that buffers are mapped (VIDIOC_REQBUFS has been called) */ + if (vb->index >= usbvision->num_frames) + return -EINVAL; + /* Updating the corresponding frame state */ + vb->flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + frame = &usbvision->frame[vb->index]; + if (frame->grabstate >= frame_state_ready) + vb->flags |= V4L2_BUF_FLAG_QUEUED; + if (frame->grabstate >= frame_state_done) + vb->flags |= V4L2_BUF_FLAG_DONE; + if (frame->grabstate == frame_state_unused) + vb->flags |= V4L2_BUF_FLAG_MAPPED; + vb->memory = V4L2_MEMORY_MMAP; + + vb->m.offset = vb->index * PAGE_ALIGN(usbvision->max_frame_size); + + vb->memory = V4L2_MEMORY_MMAP; + vb->field = V4L2_FIELD_NONE; + vb->length = usbvision->curwidth * + usbvision->curheight * + usbvision->palette.bytes_per_pixel; + v4l2_buffer_set_timestamp(vb, usbvision->frame[vb->index].ts); + vb->sequence = usbvision->frame[vb->index].sequence; + return 0; +} + +static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *vb) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + struct usbvision_frame *frame; + unsigned long lock_flags; + + /* FIXME : works only on VIDEO_CAPTURE MODE, MMAP. */ + if (vb->index >= usbvision->num_frames) + return -EINVAL; + + frame = &usbvision->frame[vb->index]; + + if (frame->grabstate != frame_state_unused) + return -EAGAIN; + + /* Mark it as ready and enqueue frame */ + frame->grabstate = frame_state_ready; + frame->scanstate = scan_state_scanning; + frame->scanlength = 0; /* Accumulated in usbvision_parse_data() */ + + vb->flags &= ~V4L2_BUF_FLAG_DONE; + + /* set v4l2_format index */ + frame->v4l2_format = usbvision->palette; + + spin_lock_irqsave(&usbvision->queue_lock, lock_flags); + list_add_tail(&usbvision->frame[vb->index].frame, &usbvision->inqueue); + spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags); + + return 0; +} + +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *vb) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + int ret; + struct usbvision_frame *f; + unsigned long lock_flags; + + if (list_empty(&(usbvision->outqueue))) { + if (usbvision->streaming == stream_idle) + return -EINVAL; + ret = wait_event_interruptible + (usbvision->wait_frame, + !list_empty(&(usbvision->outqueue))); + if (ret) + return ret; + } + + spin_lock_irqsave(&usbvision->queue_lock, lock_flags); + f = list_entry(usbvision->outqueue.next, + struct usbvision_frame, frame); + list_del(usbvision->outqueue.next); + spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags); + + f->grabstate = frame_state_unused; + + vb->memory = V4L2_MEMORY_MMAP; + vb->flags = V4L2_BUF_FLAG_MAPPED | + V4L2_BUF_FLAG_QUEUED | + V4L2_BUF_FLAG_DONE | + V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + vb->index = f->index; + vb->sequence = f->sequence; + v4l2_buffer_set_timestamp(vb, f->ts); + vb->field = V4L2_FIELD_NONE; + vb->bytesused = f->scanlength; + + return 0; +} + +static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + + usbvision->streaming = stream_on; + call_all(usbvision, video, s_stream, 1); + + return 0; +} + +static int vidioc_streamoff(struct file *file, + void *priv, enum v4l2_buf_type type) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + + if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (usbvision->streaming == stream_on) { + usbvision_stream_interrupt(usbvision); + /* Stop all video streamings */ + call_all(usbvision, video, s_stream, 0); + } + usbvision_empty_framequeues(usbvision); + + return 0; +} + +static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *vfd) +{ + if (vfd->index >= USBVISION_SUPPORTED_PALETTES - 1) + return -EINVAL; + vfd->pixelformat = usbvision_v4l2_format[vfd->index].format; + return 0; +} + +static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *vf) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + vf->fmt.pix.width = usbvision->curwidth; + vf->fmt.pix.height = usbvision->curheight; + vf->fmt.pix.pixelformat = usbvision->palette.format; + vf->fmt.pix.bytesperline = + usbvision->curwidth * usbvision->palette.bytes_per_pixel; + vf->fmt.pix.sizeimage = vf->fmt.pix.bytesperline * usbvision->curheight; + vf->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + vf->fmt.pix.field = V4L2_FIELD_NONE; /* Always progressive image */ + + return 0; +} + +static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *vf) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + int format_idx; + + /* Find requested format in available ones */ + for (format_idx = 0; format_idx < USBVISION_SUPPORTED_PALETTES; format_idx++) { + if (vf->fmt.pix.pixelformat == + usbvision_v4l2_format[format_idx].format) { + usbvision->palette = usbvision_v4l2_format[format_idx]; + break; + } + } + /* robustness */ + if (format_idx == USBVISION_SUPPORTED_PALETTES) + return -EINVAL; + RESTRICT_TO_RANGE(vf->fmt.pix.width, MIN_FRAME_WIDTH, MAX_FRAME_WIDTH); + RESTRICT_TO_RANGE(vf->fmt.pix.height, MIN_FRAME_HEIGHT, MAX_FRAME_HEIGHT); + + vf->fmt.pix.bytesperline = vf->fmt.pix.width* + usbvision->palette.bytes_per_pixel; + vf->fmt.pix.sizeimage = vf->fmt.pix.bytesperline*vf->fmt.pix.height; + vf->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + vf->fmt.pix.field = V4L2_FIELD_NONE; /* Always progressive image */ + + return 0; +} + +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *vf) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + int ret; + + ret = vidioc_try_fmt_vid_cap(file, priv, vf); + if (ret) + return ret; + + /* stop io in case it is already in progress */ + if (usbvision->streaming == stream_on) { + ret = usbvision_stream_interrupt(usbvision); + if (ret) + return ret; + } + usbvision_frames_free(usbvision); + usbvision_empty_framequeues(usbvision); + + usbvision->cur_frame = NULL; + + /* by now we are committed to the new data... */ + usbvision_set_output(usbvision, vf->fmt.pix.width, vf->fmt.pix.height); + + return 0; +} + +static ssize_t usbvision_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + int noblock = file->f_flags & O_NONBLOCK; + unsigned long lock_flags; + int ret, i; + struct usbvision_frame *frame; + + PDEBUG(DBG_IO, "%s: %ld bytes, noblock=%d", __func__, + (unsigned long)count, noblock); + + if (!USBVISION_IS_OPERATIONAL(usbvision) || !buf) + return -EFAULT; + + /* This entry point is compatible with the mmap routines + so that a user can do either VIDIOC_QBUF/VIDIOC_DQBUF + to get frames or call read on the device. */ + if (!usbvision->num_frames) { + /* First, allocate some frames to work with + if this has not been done with VIDIOC_REQBUF */ + usbvision_frames_free(usbvision); + usbvision_empty_framequeues(usbvision); + usbvision_frames_alloc(usbvision, USBVISION_NUMFRAMES); + } + + if (usbvision->streaming != stream_on) { + /* no stream is running, make it running ! */ + usbvision->streaming = stream_on; + call_all(usbvision, video, s_stream, 1); + } + + /* Then, enqueue as many frames as possible + (like a user of VIDIOC_QBUF would do) */ + for (i = 0; i < usbvision->num_frames; i++) { + frame = &usbvision->frame[i]; + if (frame->grabstate == frame_state_unused) { + /* Mark it as ready and enqueue frame */ + frame->grabstate = frame_state_ready; + frame->scanstate = scan_state_scanning; + /* Accumulated in usbvision_parse_data() */ + frame->scanlength = 0; + + /* set v4l2_format index */ + frame->v4l2_format = usbvision->palette; + + spin_lock_irqsave(&usbvision->queue_lock, lock_flags); + list_add_tail(&frame->frame, &usbvision->inqueue); + spin_unlock_irqrestore(&usbvision->queue_lock, + lock_flags); + } + } + + /* Then try to steal a frame (like a VIDIOC_DQBUF would do) */ + if (list_empty(&(usbvision->outqueue))) { + if (noblock) + return -EAGAIN; + + ret = wait_event_interruptible + (usbvision->wait_frame, + !list_empty(&(usbvision->outqueue))); + if (ret) + return ret; + } + + spin_lock_irqsave(&usbvision->queue_lock, lock_flags); + frame = list_entry(usbvision->outqueue.next, + struct usbvision_frame, frame); + list_del(usbvision->outqueue.next); + spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags); + + /* An error returns an empty frame */ + if (frame->grabstate == frame_state_error) { + frame->bytes_read = 0; + return 0; + } + + PDEBUG(DBG_IO, "%s: frmx=%d, bytes_read=%ld, scanlength=%ld", + __func__, + frame->index, frame->bytes_read, frame->scanlength); + + /* copy bytes to user space; we allow for partials reads */ + if ((count + frame->bytes_read) > (unsigned long)frame->scanlength) + count = frame->scanlength - frame->bytes_read; + + if (copy_to_user(buf, frame->data + frame->bytes_read, count)) + return -EFAULT; + + frame->bytes_read += count; + PDEBUG(DBG_IO, "%s: {copy} count used=%ld, new bytes_read=%ld", + __func__, + (unsigned long)count, frame->bytes_read); + + /* + * FIXME: + * For now, forget the frame if it has not been read in one shot. + */ + frame->bytes_read = 0; + + /* Mark it as available to be used again. */ + frame->grabstate = frame_state_unused; + + return count; +} + +static ssize_t usbvision_v4l2_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + int res; + + if (mutex_lock_interruptible(&usbvision->v4l2_lock)) + return -ERESTARTSYS; + res = usbvision_read(file, buf, count, ppos); + mutex_unlock(&usbvision->v4l2_lock); + return res; +} + +static int usbvision_mmap(struct file *file, struct vm_area_struct *vma) +{ + unsigned long size = vma->vm_end - vma->vm_start, + start = vma->vm_start; + void *pos; + u32 i; + struct usb_usbvision *usbvision = video_drvdata(file); + + PDEBUG(DBG_MMAP, "mmap"); + + if (!USBVISION_IS_OPERATIONAL(usbvision)) + return -EFAULT; + + if (!(vma->vm_flags & VM_WRITE) || + size != PAGE_ALIGN(usbvision->max_frame_size)) { + return -EINVAL; + } + + for (i = 0; i < usbvision->num_frames; i++) { + if (((PAGE_ALIGN(usbvision->max_frame_size)*i) >> PAGE_SHIFT) == + vma->vm_pgoff) + break; + } + if (i == usbvision->num_frames) { + PDEBUG(DBG_MMAP, + "mmap: user supplied mapping address is out of range"); + return -EINVAL; + } + + /* VM_IO is eventually going to replace PageReserved altogether */ + vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP; + + pos = usbvision->frame[i].data; + while (size > 0) { + if (vm_insert_page(vma, start, vmalloc_to_page(pos))) { + PDEBUG(DBG_MMAP, "mmap: vm_insert_page failed"); + return -EAGAIN; + } + start += PAGE_SIZE; + pos += PAGE_SIZE; + size -= PAGE_SIZE; + } + + return 0; +} + +static int usbvision_v4l2_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + int res; + + if (mutex_lock_interruptible(&usbvision->v4l2_lock)) + return -ERESTARTSYS; + res = usbvision_mmap(file, vma); + mutex_unlock(&usbvision->v4l2_lock); + return res; +} + +/* + * Here comes the stuff for radio on usbvision based devices + * + */ +static int usbvision_radio_open(struct file *file) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + int err_code = 0; + + PDEBUG(DBG_IO, "%s:", __func__); + + if (mutex_lock_interruptible(&usbvision->v4l2_lock)) + return -ERESTARTSYS; + + if (usbvision->remove_pending) { + err_code = -ENODEV; + goto out; + } + err_code = v4l2_fh_open(file); + if (err_code) + goto out; + if (usbvision->user) { + dev_err(&usbvision->rdev.dev, + "%s: Someone tried to open an already opened USBVision Radio!\n", + __func__); + err_code = -EBUSY; + } else { + /* Alternate interface 1 is is the biggest frame size */ + err_code = usbvision_set_alternate(usbvision); + if (err_code < 0) { + usbvision->last_error = err_code; + err_code = -EBUSY; + goto out; + } + + /* If so far no errors then we shall start the radio */ + usbvision->radio = 1; + call_all(usbvision, tuner, s_radio); + usbvision_set_audio(usbvision, USBVISION_AUDIO_RADIO); + usbvision->user++; + } +out: + mutex_unlock(&usbvision->v4l2_lock); + return err_code; +} + + +static int usbvision_radio_close(struct file *file) +{ + struct usb_usbvision *usbvision = video_drvdata(file); + int r; + + PDEBUG(DBG_IO, ""); + + mutex_lock(&usbvision->v4l2_lock); + /* Set packet size to 0 */ + usbvision->iface_alt = 0; + if (usbvision->dev) + usb_set_interface(usbvision->dev, usbvision->iface, + usbvision->iface_alt); + + usbvision_audio_off(usbvision); + usbvision->radio = 0; + usbvision->user--; + r = usbvision->remove_pending; + mutex_unlock(&usbvision->v4l2_lock); + + if (r) { + printk(KERN_INFO "%s: Final disconnect\n", __func__); + v4l2_fh_release(file); + usbvision_release(usbvision); + return 0; + } + + PDEBUG(DBG_IO, "success"); + return v4l2_fh_release(file); +} + +/* Video registration stuff */ + +/* Video template */ +static const struct v4l2_file_operations usbvision_fops = { + .owner = THIS_MODULE, + .open = usbvision_v4l2_open, + .release = usbvision_v4l2_close, + .read = usbvision_v4l2_read, + .mmap = usbvision_v4l2_mmap, + .unlocked_ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops usbvision_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_s_std = vidioc_s_std, + .vidioc_g_std = vidioc_g_std, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = vidioc_g_register, + .vidioc_s_register = vidioc_s_register, +#endif +}; + +static struct video_device usbvision_video_template = { + .fops = &usbvision_fops, + .ioctl_ops = &usbvision_ioctl_ops, + .name = "usbvision-video", + .release = video_device_release_empty, + .tvnorms = USBVISION_NORMS, +}; + + +/* Radio template */ +static const struct v4l2_file_operations usbvision_radio_fops = { + .owner = THIS_MODULE, + .open = usbvision_radio_open, + .release = usbvision_radio_close, + .poll = v4l2_ctrl_poll, + .unlocked_ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops usbvision_radio_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static struct video_device usbvision_radio_template = { + .fops = &usbvision_radio_fops, + .name = "usbvision-radio", + .release = video_device_release_empty, + .ioctl_ops = &usbvision_radio_ioctl_ops, +}; + + +static void usbvision_vdev_init(struct usb_usbvision *usbvision, + struct video_device *vdev, + const struct video_device *vdev_template, + const char *name) +{ + struct usb_device *usb_dev = usbvision->dev; + + if (!usb_dev) { + dev_err(&usbvision->dev->dev, + "%s: usbvision->dev is not set\n", __func__); + return; + } + + *vdev = *vdev_template; + vdev->lock = &usbvision->v4l2_lock; + vdev->v4l2_dev = &usbvision->v4l2_dev; + snprintf(vdev->name, sizeof(vdev->name), "%s", name); + video_set_drvdata(vdev, usbvision); +} + +/* unregister video4linux devices */ +static void usbvision_unregister_video(struct usb_usbvision *usbvision) +{ + /* Radio Device: */ + if (video_is_registered(&usbvision->rdev)) { + PDEBUG(DBG_PROBE, "unregister %s [v4l2]", + video_device_node_name(&usbvision->rdev)); + video_unregister_device(&usbvision->rdev); + } + + /* Video Device: */ + if (video_is_registered(&usbvision->vdev)) { + PDEBUG(DBG_PROBE, "unregister %s [v4l2]", + video_device_node_name(&usbvision->vdev)); + video_unregister_device(&usbvision->vdev); + } +} + +/* register video4linux devices */ +static int usbvision_register_video(struct usb_usbvision *usbvision) +{ + int res = -ENOMEM; + + /* Video Device: */ + usbvision_vdev_init(usbvision, &usbvision->vdev, + &usbvision_video_template, "USBVision Video"); + if (!usbvision->have_tuner) { + v4l2_disable_ioctl(&usbvision->vdev, VIDIOC_G_FREQUENCY); + v4l2_disable_ioctl(&usbvision->vdev, VIDIOC_S_TUNER); + v4l2_disable_ioctl(&usbvision->vdev, VIDIOC_G_FREQUENCY); + v4l2_disable_ioctl(&usbvision->vdev, VIDIOC_S_TUNER); + } + usbvision->vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; + if (usbvision->have_tuner) + usbvision->vdev.device_caps |= V4L2_CAP_TUNER; + + if (video_register_device(&usbvision->vdev, VFL_TYPE_VIDEO, video_nr) < 0) + goto err_exit; + printk(KERN_INFO "USBVision[%d]: registered USBVision Video device %s [v4l2]\n", + usbvision->nr, video_device_node_name(&usbvision->vdev)); + + /* Radio Device: */ + if (usbvision_device_data[usbvision->dev_model].radio) { + /* usbvision has radio */ + usbvision_vdev_init(usbvision, &usbvision->rdev, + &usbvision_radio_template, "USBVision Radio"); + usbvision->rdev.device_caps = V4L2_CAP_RADIO | V4L2_CAP_TUNER; + if (video_register_device(&usbvision->rdev, VFL_TYPE_RADIO, radio_nr) < 0) + goto err_exit; + printk(KERN_INFO "USBVision[%d]: registered USBVision Radio device %s [v4l2]\n", + usbvision->nr, video_device_node_name(&usbvision->rdev)); + } + /* all done */ + return 0; + + err_exit: + dev_err(&usbvision->dev->dev, + "USBVision[%d]: video_register_device() failed\n", + usbvision->nr); + usbvision_unregister_video(usbvision); + return res; +} + +/* + * usbvision_alloc() + * + * This code allocates the struct usb_usbvision. + * It is filled with default values. + * + * Returns NULL on error, a pointer to usb_usbvision else. + * + */ +static struct usb_usbvision *usbvision_alloc(struct usb_device *dev, + struct usb_interface *intf) +{ + struct usb_usbvision *usbvision; + + usbvision = kzalloc(sizeof(*usbvision), GFP_KERNEL); + if (!usbvision) + return NULL; + + usbvision->dev = dev; + if (v4l2_device_register(&intf->dev, &usbvision->v4l2_dev)) + goto err_free; + + if (v4l2_ctrl_handler_init(&usbvision->hdl, 4)) + goto err_unreg; + usbvision->v4l2_dev.ctrl_handler = &usbvision->hdl; + mutex_init(&usbvision->v4l2_lock); + + /* prepare control urb for control messages during interrupts */ + usbvision->ctrl_urb = usb_alloc_urb(USBVISION_URB_FRAMES, GFP_KERNEL); + if (!usbvision->ctrl_urb) + goto err_unreg; + + return usbvision; + +err_unreg: + v4l2_ctrl_handler_free(&usbvision->hdl); + v4l2_device_unregister(&usbvision->v4l2_dev); +err_free: + kfree(usbvision); + return NULL; +} + +/* + * usbvision_release() + * + * This code does final release of struct usb_usbvision. This happens + * after the device is disconnected -and- all clients closed their files. + * + */ +static void usbvision_release(struct usb_usbvision *usbvision) +{ + PDEBUG(DBG_PROBE, ""); + + usbvision->initialized = 0; + + usbvision_remove_sysfs(&usbvision->vdev); + usbvision_unregister_video(usbvision); + kfree(usbvision->alt_max_pkt_size); + + usb_free_urb(usbvision->ctrl_urb); + + v4l2_ctrl_handler_free(&usbvision->hdl); + v4l2_device_unregister(&usbvision->v4l2_dev); + kfree(usbvision); + + PDEBUG(DBG_PROBE, "success"); +} + + +/*********************** usb interface **********************************/ + +static void usbvision_configure_video(struct usb_usbvision *usbvision) +{ + int model; + + if (!usbvision) + return; + + model = usbvision->dev_model; + usbvision->palette = usbvision_v4l2_format[2]; /* V4L2_PIX_FMT_RGB24; */ + + if (usbvision_device_data[usbvision->dev_model].vin_reg2_override) { + usbvision->vin_reg2_preset = + usbvision_device_data[usbvision->dev_model].vin_reg2; + } else { + usbvision->vin_reg2_preset = 0; + } + + usbvision->tvnorm_id = usbvision_device_data[model].video_norm; + usbvision->video_inputs = usbvision_device_data[model].video_channels; + usbvision->ctl_input = 0; + usbvision->radio_freq = 87.5 * 16000; + usbvision->tv_freq = 400 * 16; + + /* This should be here to make i2c clients to be able to register */ + /* first switch off audio */ + if (usbvision_device_data[model].audio_channels > 0) + usbvision_audio_off(usbvision); + /* and then power up the tuner */ + usbvision_power_on(usbvision); + usbvision_i2c_register(usbvision); +} + +/* + * usbvision_probe() + * + * This procedure queries device descriptor and accepts the interface + * if it looks like USBVISION video device + * + */ +static int usbvision_probe(struct usb_interface *intf, + const struct usb_device_id *devid) +{ + struct usb_device *dev = usb_get_dev(interface_to_usbdev(intf)); + struct usb_interface *uif; + __u8 ifnum = intf->altsetting->desc.bInterfaceNumber; + const struct usb_host_interface *interface; + struct usb_usbvision *usbvision = NULL; + const struct usb_endpoint_descriptor *endpoint; + int model, i, ret; + + PDEBUG(DBG_PROBE, "VID=%#04x, PID=%#04x, ifnum=%u", + le16_to_cpu(dev->descriptor.idVendor), + le16_to_cpu(dev->descriptor.idProduct), ifnum); + + model = devid->driver_info; + if (model < 0 || model >= usbvision_device_data_size) { + PDEBUG(DBG_PROBE, "model out of bounds %d", model); + ret = -ENODEV; + goto err_usb; + } + printk(KERN_INFO "%s: %s found\n", __func__, + usbvision_device_data[model].model_string); + + if (usbvision_device_data[model].interface >= 0) + interface = &dev->actconfig->interface[usbvision_device_data[model].interface]->altsetting[0]; + else if (ifnum < dev->actconfig->desc.bNumInterfaces) + interface = &dev->actconfig->interface[ifnum]->altsetting[0]; + else { + dev_err(&intf->dev, "interface %d is invalid, max is %d\n", + ifnum, dev->actconfig->desc.bNumInterfaces - 1); + ret = -ENODEV; + goto err_usb; + } + + if (interface->desc.bNumEndpoints < 2) { + dev_err(&intf->dev, "interface %d has %d endpoints, but must have minimum 2\n", + ifnum, interface->desc.bNumEndpoints); + ret = -ENODEV; + goto err_usb; + } + endpoint = &interface->endpoint[1].desc; + + if (!usb_endpoint_xfer_isoc(endpoint)) { + dev_err(&intf->dev, "%s: interface %d. has non-ISO endpoint!\n", + __func__, ifnum); + dev_err(&intf->dev, "%s: Endpoint attributes %d", + __func__, endpoint->bmAttributes); + ret = -ENODEV; + goto err_usb; + } + if (usb_endpoint_dir_out(endpoint)) { + dev_err(&intf->dev, "%s: interface %d. has ISO OUT endpoint!\n", + __func__, ifnum); + ret = -ENODEV; + goto err_usb; + } + + usbvision = usbvision_alloc(dev, intf); + if (!usbvision) { + dev_err(&intf->dev, "%s: couldn't allocate USBVision struct\n", __func__); + ret = -ENOMEM; + goto err_usb; + } + + if (dev->descriptor.bNumConfigurations > 1) + usbvision->bridge_type = BRIDGE_NT1004; + else if (model == DAZZLE_DVC_90_REV_1_SECAM) + usbvision->bridge_type = BRIDGE_NT1005; + else + usbvision->bridge_type = BRIDGE_NT1003; + PDEBUG(DBG_PROBE, "bridge_type %d", usbvision->bridge_type); + + /* compute alternate max packet sizes */ + uif = dev->actconfig->interface[0]; + + usbvision->num_alt = uif->num_altsetting; + PDEBUG(DBG_PROBE, "Alternate settings: %i", usbvision->num_alt); + usbvision->alt_max_pkt_size = kmalloc_array(32, usbvision->num_alt, + GFP_KERNEL); + if (!usbvision->alt_max_pkt_size) { + ret = -ENOMEM; + goto err_pkt; + } + + for (i = 0; i < usbvision->num_alt; i++) { + u16 tmp; + + if (uif->altsetting[i].desc.bNumEndpoints < 2) { + ret = -ENODEV; + goto err_pkt; + } + + tmp = le16_to_cpu(uif->altsetting[i].endpoint[1].desc. + wMaxPacketSize); + usbvision->alt_max_pkt_size[i] = + (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1); + PDEBUG(DBG_PROBE, "Alternate setting %i, max size= %i", i, + usbvision->alt_max_pkt_size[i]); + } + + + usbvision->nr = usbvision_nr++; + + spin_lock_init(&usbvision->queue_lock); + init_waitqueue_head(&usbvision->wait_frame); + init_waitqueue_head(&usbvision->wait_stream); + + usbvision->have_tuner = usbvision_device_data[model].tuner; + if (usbvision->have_tuner) + usbvision->tuner_type = usbvision_device_data[model].tuner_type; + + usbvision->dev_model = model; + usbvision->remove_pending = 0; + usbvision->iface = ifnum; + usbvision->iface_alt = 0; + usbvision->video_endp = endpoint->bEndpointAddress; + usbvision->isoc_packet_size = 0; + usbvision->usb_bandwidth = 0; + usbvision->user = 0; + usbvision->streaming = stream_off; + usbvision_configure_video(usbvision); + usbvision_register_video(usbvision); + + usbvision_create_sysfs(&usbvision->vdev); + + PDEBUG(DBG_PROBE, "success"); + return 0; + +err_pkt: + usbvision_release(usbvision); +err_usb: + usb_put_dev(dev); + return ret; +} + + +/* + * usbvision_disconnect() + * + * This procedure stops all driver activity, deallocates interface-private + * structure (pointed by 'ptr') and after that driver should be removable + * with no ill consequences. + * + */ +static void usbvision_disconnect(struct usb_interface *intf) +{ + struct usb_usbvision *usbvision = to_usbvision(usb_get_intfdata(intf)); + int u; + + PDEBUG(DBG_PROBE, ""); + + if (!usbvision) { + pr_err("%s: usb_get_intfdata() failed\n", __func__); + return; + } + + mutex_lock(&usbvision->v4l2_lock); + + /* At this time we ask to cancel outstanding URBs */ + usbvision_stop_isoc(usbvision); + + v4l2_device_disconnect(&usbvision->v4l2_dev); + usbvision_i2c_unregister(usbvision); + usbvision->remove_pending = 1; /* Now all ISO data will be ignored */ + u = usbvision->user; + + usb_put_dev(usbvision->dev); + usbvision->dev = NULL; /* USB device is no more */ + + mutex_unlock(&usbvision->v4l2_lock); + + if (u) { + printk(KERN_INFO "%s: In use, disconnect pending\n", + __func__); + wake_up_interruptible(&usbvision->wait_frame); + wake_up_interruptible(&usbvision->wait_stream); + } else { + usbvision_release(usbvision); + } + + PDEBUG(DBG_PROBE, "success"); +} + +static struct usb_driver usbvision_driver = { + .name = "usbvision", + .id_table = usbvision_table, + .probe = usbvision_probe, + .disconnect = usbvision_disconnect, +}; + +/* + * usbvision_init() + * + * This code is run to initialize the driver. + * + */ +static int __init usbvision_init(void) +{ + int err_code; + + PDEBUG(DBG_PROBE, ""); + + PDEBUG(DBG_IO, "IO debugging is enabled [video]"); + PDEBUG(DBG_PROBE, "PROBE debugging is enabled [video]"); + PDEBUG(DBG_MMAP, "MMAP debugging is enabled [video]"); + + /* disable planar mode support unless compression enabled */ + if (isoc_mode != ISOC_MODE_COMPRESS) { + /* FIXME : not the right way to set supported flag */ + usbvision_v4l2_format[6].supported = 0; /* V4L2_PIX_FMT_YVU420 */ + usbvision_v4l2_format[7].supported = 0; /* V4L2_PIX_FMT_YUV422P */ + } + + err_code = usb_register(&usbvision_driver); + + if (err_code == 0) { + printk(KERN_INFO DRIVER_DESC " : " USBVISION_VERSION_STRING "\n"); + PDEBUG(DBG_PROBE, "success"); + } + return err_code; +} + +static void __exit usbvision_exit(void) +{ + PDEBUG(DBG_PROBE, ""); + + usb_deregister(&usbvision_driver); + PDEBUG(DBG_PROBE, "success"); +} + +module_init(usbvision_init); +module_exit(usbvision_exit); diff --git a/drivers/staging/media/usbvision/usbvision.h b/drivers/staging/media/usbvision/usbvision.h new file mode 100644 index 000000000000..11539578e8d2 --- /dev/null +++ b/drivers/staging/media/usbvision/usbvision.h @@ -0,0 +1,500 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * USBVISION.H + * usbvision header file + * + * Copyright (c) 1999-2005 Joerg Heckenbach <joerg@heckenbach-aw.de> + * Dwaine Garden <dwainegarden@rogers.com> + * + * Report problems to v4l MailingList: linux-media@vger.kernel.org + * + * This module is part of usbvision driver project. + * Updates to driver completed by Dwaine P. Garden + * v4l2 conversion by Thierry Merle <thierry.merle@free.fr> + */ + + +#ifndef __LINUX_USBVISION_H +#define __LINUX_USBVISION_H + +#include <linux/list.h> +#include <linux/usb.h> +#include <linux/i2c.h> +#include <linux/mutex.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ctrls.h> +#include <media/tuner.h> +#include <linux/videodev2.h> + +#define USBVISION_DEBUG /* Turn on debug messages */ + +#define USBVISION_PWR_REG 0x00 + #define USBVISION_SSPND_EN (1 << 1) + #define USBVISION_RES2 (1 << 2) + #define USBVISION_PWR_VID (1 << 5) + #define USBVISION_E2_EN (1 << 7) +#define USBVISION_CONFIG_REG 0x01 +#define USBVISION_ADRS_REG 0x02 +#define USBVISION_ALTER_REG 0x03 +#define USBVISION_FORCE_ALTER_REG 0x04 +#define USBVISION_STATUS_REG 0x05 +#define USBVISION_IOPIN_REG 0x06 + #define USBVISION_IO_1 (1 << 0) + #define USBVISION_IO_2 (1 << 1) + #define USBVISION_AUDIO_IN 0 + #define USBVISION_AUDIO_TV 1 + #define USBVISION_AUDIO_RADIO 2 + #define USBVISION_AUDIO_MUTE 3 +#define USBVISION_SER_MODE 0x07 + #define USBVISION_CLK_OUT (1 << 0) + #define USBVISION_DAT_IO (1 << 1) + #define USBVISION_SENS_OUT (1 << 2) + #define USBVISION_SER_MODE_SOFT (0 << 4) + #define USBVISION_SER_MODE_SIO (1 << 4) +#define USBVISION_SER_ADRS 0x08 +#define USBVISION_SER_CONT 0x09 +#define USBVISION_SER_DAT1 0x0A +#define USBVISION_SER_DAT2 0x0B +#define USBVISION_SER_DAT3 0x0C +#define USBVISION_SER_DAT4 0x0D +#define USBVISION_EE_DATA 0x0E +#define USBVISION_EE_LSBAD 0x0F +#define USBVISION_EE_CONT 0x10 +#define USBVISION_DRM_CONT 0x12 + #define USBVISION_REF (1 << 0) + #define USBVISION_RES_UR (1 << 2) + #define USBVISION_RES_FDL (1 << 3) + #define USBVISION_RES_VDW (1 << 4) +#define USBVISION_DRM_PRM1 0x13 +#define USBVISION_DRM_PRM2 0x14 +#define USBVISION_DRM_PRM3 0x15 +#define USBVISION_DRM_PRM4 0x16 +#define USBVISION_DRM_PRM5 0x17 +#define USBVISION_DRM_PRM6 0x18 +#define USBVISION_DRM_PRM7 0x19 +#define USBVISION_DRM_PRM8 0x1A +#define USBVISION_VIN_REG1 0x1B + #define USBVISION_8_422_SYNC 0x01 + #define USBVISION_16_422_SYNC 0x02 + #define USBVISION_VSNC_POL (1 << 3) + #define USBVISION_HSNC_POL (1 << 4) + #define USBVISION_FID_POL (1 << 5) + #define USBVISION_HVALID_PO (1 << 6) + #define USBVISION_VCLK_POL (1 << 7) +#define USBVISION_VIN_REG2 0x1C + #define USBVISION_AUTO_FID (1 << 0) + #define USBVISION_NONE_INTER (1 << 1) + #define USBVISION_NOHVALID (1 << 2) + #define USBVISION_UV_ID (1 << 3) + #define USBVISION_FIX_2C (1 << 4) + #define USBVISION_SEND_FID (1 << 5) + #define USBVISION_KEEP_BLANK (1 << 7) +#define USBVISION_LXSIZE_I 0x1D +#define USBVISION_MXSIZE_I 0x1E +#define USBVISION_LYSIZE_I 0x1F +#define USBVISION_MYSIZE_I 0x20 +#define USBVISION_LX_OFFST 0x21 +#define USBVISION_MX_OFFST 0x22 +#define USBVISION_LY_OFFST 0x23 +#define USBVISION_MY_OFFST 0x24 +#define USBVISION_FRM_RATE 0x25 +#define USBVISION_LXSIZE_O 0x26 +#define USBVISION_MXSIZE_O 0x27 +#define USBVISION_LYSIZE_O 0x28 +#define USBVISION_MYSIZE_O 0x29 +#define USBVISION_FILT_CONT 0x2A +#define USBVISION_VO_MODE 0x2B +#define USBVISION_INTRA_CYC 0x2C +#define USBVISION_STRIP_SZ 0x2D +#define USBVISION_FORCE_INTRA 0x2E +#define USBVISION_FORCE_UP 0x2F +#define USBVISION_BUF_THR 0x30 +#define USBVISION_DVI_YUV 0x31 +#define USBVISION_AUDIO_CONT 0x32 +#define USBVISION_AUD_PK_LEN 0x33 +#define USBVISION_BLK_PK_LEN 0x34 +#define USBVISION_PCM_THR1 0x38 +#define USBVISION_PCM_THR2 0x39 +#define USBVISION_DIST_THR_L 0x3A +#define USBVISION_DIST_THR_H 0x3B +#define USBVISION_MAX_DIST_L 0x3C +#define USBVISION_MAX_DIST_H 0x3D +#define USBVISION_OP_CODE 0x33 + +#define MAX_BYTES_PER_PIXEL 4 + +#define MIN_FRAME_WIDTH 64 +#define MAX_USB_WIDTH 320 /* 384 */ +#define MAX_FRAME_WIDTH 320 /* 384 */ /* stretching sometimes causes crashes*/ + +#define MIN_FRAME_HEIGHT 48 +#define MAX_USB_HEIGHT 240 /* 288 */ +#define MAX_FRAME_HEIGHT 240 /* 288 */ /* Stretching sometimes causes crashes*/ + +#define MAX_FRAME_SIZE (MAX_FRAME_WIDTH * MAX_FRAME_HEIGHT * MAX_BYTES_PER_PIXEL) +#define USBVISION_CLIPMASK_SIZE (MAX_FRAME_WIDTH * MAX_FRAME_HEIGHT / 8) /* bytesize of clipmask */ + +#define USBVISION_URB_FRAMES 32 + +#define USBVISION_NUM_HEADERMARKER 20 +#define USBVISION_NUMFRAMES 3 /* Maximum number of frames an application can get */ +#define USBVISION_NUMSBUF 2 /* Dimensioning the USB S buffering */ + +#define USBVISION_POWEROFF_TIME (3 * HZ) /* 3 seconds */ + + +#define FRAMERATE_MIN 0 +#define FRAMERATE_MAX 31 + +enum { + ISOC_MODE_YUV422 = 0x03, + ISOC_MODE_YUV420 = 0x14, + ISOC_MODE_COMPRESS = 0x60, +}; + +/* This macro restricts an int variable to an inclusive range */ +#define RESTRICT_TO_RANGE(v, mi, ma) \ + { if (((int)v) < (mi)) (v) = (mi); else if ((v) > (ma)) (v) = (ma); } + +/* + * We use macros to do YUV -> RGB conversion because this is + * very important for speed and totally unimportant for size. + * + * YUV -> RGB Conversion + * --------------------- + * + * B = 1.164*(Y-16) + 2.018*(V-128) + * G = 1.164*(Y-16) - 0.813*(U-128) - 0.391*(V-128) + * R = 1.164*(Y-16) + 1.596*(U-128) + * + * If you fancy integer arithmetic (as you should), hear this: + * + * 65536*B = 76284*(Y-16) + 132252*(V-128) + * 65536*G = 76284*(Y-16) - 53281*(U-128) - 25625*(V-128) + * 65536*R = 76284*(Y-16) + 104595*(U-128) + * + * Make sure the output values are within [0..255] range. + */ +#define LIMIT_RGB(x) (((x) < 0) ? 0 : (((x) > 255) ? 255 : (x))) +#define YUV_TO_RGB_BY_THE_BOOK(my, mu, mv, mr, mg, mb) { \ + int mm_y, mm_yc, mm_u, mm_v, mm_r, mm_g, mm_b; \ + mm_y = (my) - 16; \ + mm_u = (mu) - 128; \ + mm_v = (mv) - 128; \ + mm_yc = mm_y * 76284; \ + mm_b = (mm_yc + 132252 * mm_v) >> 16; \ + mm_g = (mm_yc - 53281 * mm_u - 25625 * mm_v) >> 16; \ + mm_r = (mm_yc + 104595 * mm_u) >> 16; \ + mb = LIMIT_RGB(mm_b); \ + mg = LIMIT_RGB(mm_g); \ + mr = LIMIT_RGB(mm_r); \ +} + +/* + * This macro checks if usbvision is still operational. The 'usbvision' + * pointer must be valid, usbvision->dev must be valid, we are not + * removing the device and the device has not erred on us. + */ +#define USBVISION_IS_OPERATIONAL(udevice) (\ + (udevice != NULL) && \ + ((udevice)->dev != NULL) && \ + ((udevice)->last_error == 0) && \ + (!(udevice)->remove_pending)) + +#define I2C_USB_ADAP_MAX 16 + +#define USBVISION_NORMS (V4L2_STD_PAL | V4L2_STD_NTSC | V4L2_STD_SECAM | V4L2_STD_PAL_M) + +/* ----------------------------------------------------------------- */ +/* usbvision video structures */ +/* ----------------------------------------------------------------- */ +enum scan_state { + scan_state_scanning, /* Scanning for header */ + scan_state_lines /* Parsing lines */ +}; + +/* Completion states of the data parser */ +enum parse_state { + parse_state_continue, /* Just parse next item */ + parse_state_next_frame, /* Frame done, send it to V4L */ + parse_state_out, /* Not enough data for frame */ + parse_state_end_parse /* End parsing */ +}; + +enum frame_state { + frame_state_unused, /* Unused (no MCAPTURE) */ + frame_state_ready, /* Ready to start grabbing */ + frame_state_grabbing, /* In the process of being grabbed into */ + frame_state_done, /* Finished grabbing, but not been synced yet */ + frame_state_done_hold, /* Are syncing or reading */ + frame_state_error, /* Something bad happened while processing */ +}; + +/* stream states */ +enum stream_state { + stream_off, /* Driver streaming is completely OFF */ + stream_idle, /* Driver streaming is ready to be put ON by the application */ + stream_interrupt, /* Driver streaming must be interrupted */ + stream_on, /* Driver streaming is put ON by the application */ +}; + +enum isoc_state { + isoc_state_in_frame, /* Isoc packet is member of frame */ + isoc_state_no_frame, /* Isoc packet is not member of any frame */ +}; + +struct usb_device; + +struct usbvision_sbuf { + char *data; + struct urb *urb; +}; + +#define USBVISION_MAGIC_1 0x55 +#define USBVISION_MAGIC_2 0xAA +#define USBVISION_HEADER_LENGTH 0x0c +#define USBVISION_SAA7111_ADDR 0x48 +#define USBVISION_SAA7113_ADDR 0x4a +#define USBVISION_IIC_LRACK 0x20 +#define USBVISION_IIC_LRNACK 0x30 +#define USBVISION_FRAME_FORMAT_PARAM_INTRA (1<<7) + +struct usbvision_v4l2_format_st { + int supported; + int bytes_per_pixel; + int depth; + int format; +}; +#define USBVISION_SUPPORTED_PALETTES ARRAY_SIZE(usbvision_v4l2_format) + +struct usbvision_frame_header { + unsigned char magic_1; /* 0 magic */ + unsigned char magic_2; /* 1 magic */ + unsigned char header_length; /* 2 */ + unsigned char frame_num; /* 3 */ + unsigned char frame_phase; /* 4 */ + unsigned char frame_latency; /* 5 */ + unsigned char data_format; /* 6 */ + unsigned char format_param; /* 7 */ + unsigned char frame_width_lo; /* 8 */ + unsigned char frame_width_hi; /* 9 */ + unsigned char frame_height_lo; /* 10 */ + unsigned char frame_height_hi; /* 11 */ + __u16 frame_width; /* 8 - 9 after endian correction*/ + __u16 frame_height; /* 10 - 11 after endian correction*/ +}; + +struct usbvision_frame { + char *data; /* Frame buffer */ + struct usbvision_frame_header isoc_header; /* Header from stream */ + + int width; /* Width application is expecting */ + int height; /* Height */ + int index; /* Frame index */ + int frmwidth; /* Width the frame actually is */ + int frmheight; /* Height */ + + volatile int grabstate; /* State of grabbing */ + int scanstate; /* State of scanning */ + + struct list_head frame; + + int curline; /* Line of frame we're working on */ + + long scanlength; /* uncompressed, raw data length of frame */ + long bytes_read; /* amount of scanlength that has been read from data */ + struct usbvision_v4l2_format_st v4l2_format; /* format the user needs*/ + int v4l2_linesize; /* bytes for one videoline*/ + u64 ts; + int sequence; /* How many video frames we send to user */ +}; + +#define CODEC_SAA7113 7113 +#define CODEC_SAA7111 7111 +#define CODEC_WEBCAM 3000 +#define BRIDGE_NT1003 1003 +#define BRIDGE_NT1004 1004 +#define BRIDGE_NT1005 1005 + +struct usbvision_device_data_st { + __u64 video_norm; + const char *model_string; + int interface; /* to handle special interface number like BELKIN and Hauppauge WinTV-USB II */ + __u16 codec; + unsigned video_channels:3; + unsigned audio_channels:2; + unsigned radio:1; + unsigned vbi:1; + unsigned tuner:1; + unsigned vin_reg1_override:1; /* Override default value with */ + unsigned vin_reg2_override:1; /* vin_reg1, vin_reg2, etc. */ + unsigned dvi_yuv_override:1; + __u8 vin_reg1; + __u8 vin_reg2; + __u8 dvi_yuv; + __u8 tuner_type; + __s16 x_offset; + __s16 y_offset; +}; + +/* Declared on usbvision-cards.c */ +extern struct usbvision_device_data_st usbvision_device_data[]; +extern struct usb_device_id usbvision_table[]; + +struct usb_usbvision { + struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler hdl; + struct video_device vdev; /* Video Device */ + struct video_device rdev; /* Radio Device */ + + /* i2c Declaration Section*/ + struct i2c_adapter i2c_adap; + int registered_i2c; + + struct urb *ctrl_urb; + unsigned char ctrl_urb_buffer[8]; + int ctrl_urb_busy; + struct usb_ctrlrequest ctrl_urb_setup; + + /* configuration part */ + int have_tuner; + int tuner_type; + int bridge_type; /* NT1003, NT1004, NT1005 */ + int radio; + int video_inputs; /* # of inputs */ + unsigned long radio_freq; + unsigned long tv_freq; + int audio_mute; + int audio_channel; + int isoc_mode; /* format of video data for the usb isoc-transfer */ + unsigned int nr; /* Number of the device */ + + /* Device structure */ + struct usb_device *dev; + /* usb transfer */ + int num_alt; /* Number of alternative settings */ + unsigned int *alt_max_pkt_size; /* array of max_packet_size */ + unsigned char iface; /* Video interface number */ + unsigned char iface_alt; /* Alt settings */ + unsigned char vin_reg2_preset; + struct mutex v4l2_lock; + int power; /* is the device powered on? */ + int user; /* user count for exclusive use */ + int initialized; /* Had we already sent init sequence? */ + int dev_model; /* What type of USBVISION device we got? */ + enum stream_state streaming; /* Are we streaming Isochronous? */ + int last_error; /* What calamity struck us? */ + int curwidth; /* width of the frame the device is currently set to*/ + int curheight; /* height of the frame the device is currently set to*/ + int stretch_width; /* stretch-factor for frame width (from usb to screen)*/ + int stretch_height; /* stretch-factor for frame height (from usb to screen)*/ + char *fbuf; /* Videodev buffer area for mmap*/ + int max_frame_size; /* Bytes in one video frame */ + int fbuf_size; /* Videodev buffer size */ + spinlock_t queue_lock; /* spinlock for protecting mods on inqueue and outqueue */ + struct list_head inqueue, outqueue; /* queued frame list and ready to dequeue frame list */ + wait_queue_head_t wait_frame; /* Processes waiting */ + wait_queue_head_t wait_stream; /* Processes waiting */ + struct usbvision_frame *cur_frame; /* pointer to current frame, set by usbvision_find_header */ + struct usbvision_frame frame[USBVISION_NUMFRAMES]; /* frame buffer */ + int num_frames; /* number of frames allocated */ + struct usbvision_sbuf sbuf[USBVISION_NUMSBUF]; /* S buffering */ + volatile int remove_pending; /* If set then about to exit */ + + /* Scratch space from the Isochronous Pipe.*/ + unsigned char *scratch; + int scratch_read_ptr; + int scratch_write_ptr; + int scratch_headermarker[USBVISION_NUM_HEADERMARKER]; + int scratch_headermarker_read_ptr; + int scratch_headermarker_write_ptr; + enum isoc_state isocstate; + struct usbvision_v4l2_format_st palette; + + struct v4l2_capability vcap; /* Video capabilities */ + unsigned int ctl_input; /* selected input */ + v4l2_std_id tvnorm_id; /* selected tv norm */ + unsigned char video_endp; /* 0x82 for USBVISION devices based */ + + /* Decompression stuff: */ + unsigned char *intra_frame_buffer; /* Buffer for reference frame */ + int block_pos; /* for test only */ + int request_intra; /* 0 = normal; 1 = intra frame is requested; */ + int last_isoc_frame_num; /* check for lost isoc frames */ + int isoc_packet_size; /* need to calculate used_bandwidth */ + int used_bandwidth; /* used bandwidth 0-100%, need to set compr_level */ + int compr_level; /* How strong (100) or weak (0) is compression */ + int last_compr_level; /* How strong (100) or weak (0) was compression */ + int usb_bandwidth; /* Mbit/s */ + + /* Statistics that can be overlaid on the screen */ + unsigned long isoc_urb_count; /* How many URBs we received so far */ + unsigned long urb_length; /* Length of last URB */ + unsigned long isoc_data_count; /* How many bytes we received */ + unsigned long header_count; /* How many frame headers we found */ + unsigned long scratch_ovf_count; /* How many times we overflowed scratch */ + unsigned long isoc_skip_count; /* How many empty ISO packets received */ + unsigned long isoc_err_count; /* How many bad ISO packets received */ + unsigned long isoc_packet_count; /* How many packets we totally got */ + int isoc_measure_bandwidth_count; + int frame_num; /* How many video frames we send to user */ + int max_strip_len; /* How big is the biggest strip */ + int comprblock_pos; + int strip_len_errors; /* How many times was block_pos greater than strip_len */ + int strip_magic_errors; + int strip_line_number_errors; + int compr_block_types[4]; +}; + +static inline struct usb_usbvision *to_usbvision(struct v4l2_device *v4l2_dev) +{ + return container_of(v4l2_dev, struct usb_usbvision, v4l2_dev); +} + +#define call_all(usbvision, o, f, args...) \ + v4l2_device_call_all(&usbvision->v4l2_dev, 0, o, f, ##args) + +/* --------------------------------------------------------------- */ +/* defined in usbvision-i2c.c */ +/* i2c-algo-usb declaration */ +/* --------------------------------------------------------------- */ + +/* ----------------------------------------------------------------------- */ +/* usbvision specific I2C functions */ +/* ----------------------------------------------------------------------- */ +int usbvision_i2c_register(struct usb_usbvision *usbvision); +int usbvision_i2c_unregister(struct usb_usbvision *usbvision); + +/* defined in usbvision-core.c */ +int usbvision_read_reg(struct usb_usbvision *usbvision, unsigned char reg); +int usbvision_write_reg(struct usb_usbvision *usbvision, unsigned char reg, + unsigned char value); + +int usbvision_frames_alloc(struct usb_usbvision *usbvision, int number_of_frames); +void usbvision_frames_free(struct usb_usbvision *usbvision); +int usbvision_scratch_alloc(struct usb_usbvision *usbvision); +void usbvision_scratch_free(struct usb_usbvision *usbvision); +int usbvision_decompress_alloc(struct usb_usbvision *usbvision); +void usbvision_decompress_free(struct usb_usbvision *usbvision); + +int usbvision_setup(struct usb_usbvision *usbvision, int format); +int usbvision_init_isoc(struct usb_usbvision *usbvision); +int usbvision_restart_isoc(struct usb_usbvision *usbvision); +void usbvision_stop_isoc(struct usb_usbvision *usbvision); +int usbvision_set_alternate(struct usb_usbvision *dev); + +int usbvision_set_audio(struct usb_usbvision *usbvision, int audio_channel); +int usbvision_audio_off(struct usb_usbvision *usbvision); + +int usbvision_begin_streaming(struct usb_usbvision *usbvision); +void usbvision_empty_framequeues(struct usb_usbvision *dev); +int usbvision_stream_interrupt(struct usb_usbvision *dev); + +int usbvision_muxsel(struct usb_usbvision *usbvision, int channel); +int usbvision_set_input(struct usb_usbvision *usbvision); +int usbvision_set_output(struct usb_usbvision *usbvision, int width, int height); + +int usbvision_power_off(struct usb_usbvision *usbvision); +int usbvision_power_on(struct usb_usbvision *usbvision); + +#endif /* __LINUX_USBVISION_H */ diff --git a/drivers/staging/most/video/video.c b/drivers/staging/most/video/video.c index d32ae49d617b..4cffc8fd62bd 100644 --- a/drivers/staging/most/video/video.c +++ b/drivers/staging/most/video/video.c @@ -74,7 +74,7 @@ static int comp_vdev_open(struct file *filp) struct comp_fh *fh; switch (vdev->vfl_type) { - case VFL_TYPE_GRABBER: + case VFL_TYPE_VIDEO: break; default: return -EINVAL; @@ -424,7 +424,7 @@ static int comp_register_videodev(struct most_video_dev *mdev) /* Register the v4l2 device */ video_set_drvdata(mdev->vdev, mdev); - ret = video_register_device(mdev->vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(mdev->vdev, VFL_TYPE_VIDEO, -1); if (ret) { v4l2_err(&mdev->v4l2_dev, "video_register_device failed (%d)\n", ret); diff --git a/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c b/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c index 1ef31a984741..597acef35d0b 100644 --- a/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c +++ b/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c @@ -1776,7 +1776,7 @@ static int bm2835_mmal_init_device(struct bm2835_mmal_dev *dev, video_set_drvdata(vfd, dev); ret = video_register_device(vfd, - VFL_TYPE_GRABBER, + VFL_TYPE_VIDEO, video_nr[dev->camera_num]); if (ret < 0) return ret; |